| /* | |
| * Copyright (C) 2014 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| #ifndef ART_COMPILER_UTILS_ASSEMBLER_TEST_H_ | |
| #define ART_COMPILER_UTILS_ASSEMBLER_TEST_H_ | |
| #include "assembler.h" | |
| #include <sys/stat.h> | |
| #include <cstdio> | |
| #include <cstdlib> | |
| #include <fstream> | |
| #include <iterator> | |
| #include "base/array_ref.h" | |
| #include "base/macros.h" | |
| #include "base/malloc_arena_pool.h" | |
| #include "assembler_test_base.h" | |
| #include "common_runtime_test.h" // For ScratchFile | |
| namespace art HIDDEN { | |
| // Helper for a constexpr string length. | |
| constexpr size_t ConstexprStrLen(char const* str, size_t count = 0) { | |
| return ('\0' == str[0]) ? count : ConstexprStrLen(str+1, count+1); | |
| } | |
| enum class RegisterView { // private | |
| kUsePrimaryName, | |
| kUseSecondaryName, | |
| kUseTertiaryName, | |
| kUseQuaternaryName, | |
| }; | |
| // For use in the template as the default type to get a nonvector registers version. | |
| struct NoVectorRegs {}; | |
| template<typename Ass, | |
| typename Addr, | |
| typename Reg, | |
| typename FPReg, | |
| typename Imm, | |
| typename VecReg = NoVectorRegs> | |
| class AssemblerTest : public AssemblerTestBase { | |
| public: | |
| Ass* GetAssembler() { | |
| return assembler_.get(); | |
| } | |
| using TestFn = std::string (*)(AssemblerTest *, Ass *); | |
| void DriverFn(TestFn f, const std::string& test_name) { | |
| DriverWrapper(f(this, assembler_.get()), test_name); | |
| } | |
| // This driver assumes the assembler has already been called. | |
| void DriverStr(const std::string& assembly_string, const std::string& test_name) { | |
| DriverWrapper(assembly_string, test_name); | |
| } | |
| // | |
| // Register repeats. | |
| // | |
| std::string RepeatR(void (Ass::*f)(Reg), const std::string& fmt) { | |
| return RepeatTemplatedRegister<Reg>(f, | |
| GetRegisters(), | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| fmt); | |
| } | |
| std::string Repeatr(void (Ass::*f)(Reg), const std::string& fmt) { | |
| return RepeatTemplatedRegister<Reg>(f, | |
| GetRegisters(), | |
| &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, | |
| fmt); | |
| } | |
| std::string RepeatRR(void (Ass::*f)(Reg, Reg), | |
| const std::string& fmt, | |
| const std::vector<std::pair<Reg, Reg>>* except = nullptr) { | |
| return RepeatTemplatedRegisters<Reg, Reg>(f, | |
| GetRegisters(), | |
| GetRegisters(), | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| fmt, | |
| except); | |
| } | |
| std::string RepeatRRNoDupes(void (Ass::*f)(Reg, Reg), const std::string& fmt) { | |
| return RepeatTemplatedRegistersNoDupes<Reg, Reg>(f, | |
| GetRegisters(), | |
| GetRegisters(), | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| fmt); | |
| } | |
| std::string Repeatrr(void (Ass::*f)(Reg, Reg), | |
| const std::string& fmt, | |
| const std::vector<std::pair<Reg, Reg>>* except = nullptr) { | |
| return RepeatTemplatedRegisters<Reg, Reg>(f, | |
| GetRegisters(), | |
| GetRegisters(), | |
| &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, | |
| &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, | |
| fmt, | |
| except); | |
| } | |
| std::string Repeatww(void (Ass::*f)(Reg, Reg), | |
| const std::string& fmt, | |
| const std::vector<std::pair<Reg, Reg>>* except = nullptr) { | |
| return RepeatTemplatedRegisters<Reg, Reg>(f, | |
| GetRegisters(), | |
| GetRegisters(), | |
| &AssemblerTest::GetRegName<RegisterView::kUseTertiaryName>, | |
| &AssemblerTest::GetRegName<RegisterView::kUseTertiaryName>, | |
| fmt, | |
| except); | |
| } | |
| std::string Repeatbb(void (Ass::*f)(Reg, Reg), | |
| const std::string& fmt, | |
| const std::vector<std::pair<Reg, Reg>>* except = nullptr) { | |
| return RepeatTemplatedRegisters<Reg, Reg>(f, | |
| GetRegisters(), | |
| GetRegisters(), | |
| &AssemblerTest::GetRegName<RegisterView::kUseQuaternaryName>, | |
| &AssemblerTest::GetRegName<RegisterView::kUseQuaternaryName>, | |
| fmt, | |
| except); | |
| } | |
| std::string RepeatRRR(void (Ass::*f)(Reg, Reg, Reg), const std::string& fmt) { | |
| return RepeatTemplatedRegisters<Reg, Reg, Reg>(f, | |
| GetRegisters(), | |
| GetRegisters(), | |
| GetRegisters(), | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| fmt); | |
| } | |
| std::string Repeatrb(void (Ass::*f)(Reg, Reg), | |
| const std::string& fmt, | |
| const std::vector<std::pair<Reg, Reg>>* except = nullptr) { | |
| return RepeatTemplatedRegisters<Reg, Reg>(f, | |
| GetRegisters(), | |
| GetRegisters(), | |
| &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, | |
| &AssemblerTest::GetRegName<RegisterView::kUseQuaternaryName>, | |
| fmt, | |
| except); | |
| } | |
| std::string RepeatRr(void (Ass::*f)(Reg, Reg), | |
| const std::string& fmt, | |
| const std::vector<std::pair<Reg, Reg>>* except = nullptr) { | |
| return RepeatTemplatedRegisters<Reg, Reg>(f, | |
| GetRegisters(), | |
| GetRegisters(), | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, | |
| fmt, | |
| except); | |
| } | |
| std::string RepeatRI(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, const std::string& fmt) { | |
| return RepeatRegisterImm<RegisterView::kUsePrimaryName>(f, imm_bytes, fmt); | |
| } | |
| std::string RepeatrI(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, const std::string& fmt) { | |
| return RepeatRegisterImm<RegisterView::kUseSecondaryName>(f, imm_bytes, fmt); | |
| } | |
| std::string RepeatwI(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, const std::string& fmt) { | |
| return RepeatRegisterImm<RegisterView::kUseTertiaryName>(f, imm_bytes, fmt); | |
| } | |
| std::string RepeatbI(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, const std::string& fmt) { | |
| return RepeatRegisterImm<RegisterView::kUseQuaternaryName>(f, imm_bytes, fmt); | |
| } | |
| template <typename Reg1, typename Reg2, typename ImmType> | |
| std::string RepeatTemplatedRegistersImmBits(void (Ass::*f)(Reg1, Reg2, ImmType), | |
| int imm_bits, | |
| ArrayRef<const Reg1> reg1_registers, | |
| ArrayRef<const Reg2> reg2_registers, | |
| std::string (AssemblerTest::*GetName1)(const Reg1&), | |
| std::string (AssemblerTest::*GetName2)(const Reg2&), | |
| const std::string& fmt, | |
| int bias = 0, | |
| int multiplier = 1) { | |
| std::string str; | |
| std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0)); | |
| for (auto reg1 : reg1_registers) { | |
| for (auto reg2 : reg2_registers) { | |
| for (int64_t imm : imms) { | |
| ImmType new_imm = CreateImmediate(imm); | |
| if (f != nullptr) { | |
| (assembler_.get()->*f)(reg1, reg2, new_imm * multiplier + bias); | |
| } | |
| std::string base = fmt; | |
| ReplaceReg(REG1_TOKEN, (this->*GetName1)(reg1), &base); | |
| ReplaceReg(REG2_TOKEN, (this->*GetName2)(reg2), &base); | |
| ReplaceImm(imm, bias, multiplier, &base); | |
| str += base; | |
| str += "\n"; | |
| } | |
| } | |
| } | |
| return str; | |
| } | |
| template <typename Reg1, typename Reg2, typename Reg3, typename ImmType> | |
| std::string RepeatTemplatedRegistersImmBits(void (Ass::*f)(Reg1, Reg2, Reg3, ImmType), | |
| int imm_bits, | |
| ArrayRef<const Reg1> reg1_registers, | |
| ArrayRef<const Reg2> reg2_registers, | |
| ArrayRef<const Reg3> reg3_registers, | |
| std::string (AssemblerTest::*GetName1)(const Reg1&), | |
| std::string (AssemblerTest::*GetName2)(const Reg2&), | |
| std::string (AssemblerTest::*GetName3)(const Reg3&), | |
| const std::string& fmt, | |
| int bias) { | |
| std::string str; | |
| std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0)); | |
| for (auto reg1 : reg1_registers) { | |
| for (auto reg2 : reg2_registers) { | |
| for (auto reg3 : reg3_registers) { | |
| for (int64_t imm : imms) { | |
| ImmType new_imm = CreateImmediate(imm); | |
| if (f != nullptr) { | |
| (assembler_.get()->*f)(reg1, reg2, reg3, new_imm + bias); | |
| } | |
| std::string base = fmt; | |
| ReplaceReg(REG1_TOKEN, (this->*GetName1)(reg1), &base); | |
| ReplaceReg(REG2_TOKEN, (this->*GetName2)(reg2), &base); | |
| ReplaceReg(REG3_TOKEN, (this->*GetName3)(reg3), &base); | |
| ReplaceImm(imm, bias, /*multiplier=*/ 1, &base); | |
| str += base; | |
| str += "\n"; | |
| } | |
| } | |
| } | |
| } | |
| return str; | |
| } | |
| template <typename ImmType, typename Reg1, typename Reg2> | |
| std::string RepeatTemplatedImmBitsRegisters(void (Ass::*f)(ImmType, Reg1, Reg2), | |
| ArrayRef<const Reg1> reg1_registers, | |
| ArrayRef<const Reg2> reg2_registers, | |
| std::string (AssemblerTest::*GetName1)(const Reg1&), | |
| std::string (AssemblerTest::*GetName2)(const Reg2&), | |
| int imm_bits, | |
| const std::string& fmt) { | |
| std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0)); | |
| WarnOnCombinations(reg1_registers.size() * reg2_registers.size() * imms.size()); | |
| std::string str; | |
| for (auto reg1 : reg1_registers) { | |
| for (auto reg2 : reg2_registers) { | |
| for (int64_t imm : imms) { | |
| ImmType new_imm = CreateImmediate(imm); | |
| if (f != nullptr) { | |
| (assembler_.get()->*f)(new_imm, reg1, reg2); | |
| } | |
| std::string base = fmt; | |
| ReplaceReg(REG1_TOKEN, (this->*GetName1)(reg1), &base); | |
| ReplaceReg(REG2_TOKEN, (this->*GetName2)(reg2), &base); | |
| ReplaceImm(imm, /*bias=*/ 0, /*multiplier=*/ 1, &base); | |
| str += base; | |
| str += "\n"; | |
| } | |
| } | |
| } | |
| return str; | |
| } | |
| template <typename RegType, typename ImmType> | |
| std::string RepeatTemplatedRegisterImmBits(void (Ass::*f)(RegType, ImmType), | |
| int imm_bits, | |
| ArrayRef<const RegType> registers, | |
| std::string (AssemblerTest::*GetName)(const RegType&), | |
| const std::string& fmt, | |
| int bias) { | |
| std::string str; | |
| std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0)); | |
| for (auto reg : registers) { | |
| for (int64_t imm : imms) { | |
| if (f != nullptr) { | |
| (assembler_.get()->*f)(reg, CreateImmediate(imm + bias)); | |
| } | |
| std::string base = fmt; | |
| ReplaceReg(REG_TOKEN, (this->*GetName)(reg), &base); | |
| ReplaceImm(imm, bias, /*multiplier=*/ 1, &base); | |
| str += base; | |
| str += "\n"; | |
| } | |
| } | |
| return str; | |
| } | |
| template <typename RegType, typename ImmType> | |
| std::string RepeatTemplatedRegisterImmBitsShift( | |
| void (Ass::*f)(RegType, ImmType), | |
| int imm_bits, | |
| int shift, | |
| ArrayRef<const RegType> registers, | |
| std::string (AssemblerTest::*GetName)(const RegType&), | |
| const std::string& fmt, | |
| int bias) { | |
| std::string str; | |
| std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0), shift); | |
| for (auto reg : registers) { | |
| for (int64_t imm : imms) { | |
| ImmType new_imm = CreateImmediate(imm); | |
| if (f != nullptr) { | |
| (assembler_.get()->*f)(reg, new_imm + bias); | |
| } | |
| std::string base = fmt; | |
| ReplaceReg(REG_TOKEN, (this->*GetName)(reg), &base); | |
| ReplaceImm(imm, bias, /*multiplier=*/ 1, &base); | |
| str += base; | |
| str += "\n"; | |
| } | |
| } | |
| return str; | |
| } | |
| template <typename ImmType> | |
| std::string RepeatTemplatedImmBitsShift( | |
| void (Ass::*f)(ImmType), int imm_bits, int shift, const std::string& fmt, int bias = 0) { | |
| std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0), shift); | |
| WarnOnCombinations(imms.size()); | |
| std::string str; | |
| for (int64_t imm : imms) { | |
| ImmType new_imm = CreateImmediate(imm); | |
| if (f != nullptr) { | |
| (assembler_.get()->*f)(new_imm + bias); | |
| } | |
| std::string base = fmt; | |
| ReplaceImm(imm, bias, /*multiplier=*/ 1, &base); | |
| str += base; | |
| str += "\n"; | |
| } | |
| return str; | |
| } | |
| template <typename Reg1, typename Reg2, typename ImmType> | |
| std::string RepeatTemplatedRegistersImmBitsShift( | |
| void (Ass::*f)(Reg1, Reg2, ImmType), | |
| int imm_bits, | |
| int shift, | |
| ArrayRef<const Reg1> reg1_registers, | |
| ArrayRef<const Reg2> reg2_registers, | |
| std::string (AssemblerTest::*GetName1)(const Reg1&), | |
| std::string (AssemblerTest::*GetName2)(const Reg2&), | |
| const std::string& fmt, | |
| int bias = 0, | |
| int multiplier = 1) { | |
| std::string str; | |
| std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0), shift); | |
| for (auto reg1 : reg1_registers) { | |
| for (auto reg2 : reg2_registers) { | |
| for (int64_t imm : imms) { | |
| ImmType new_imm = CreateImmediate(imm); | |
| if (f != nullptr) { | |
| (assembler_.get()->*f)(reg1, reg2, new_imm * multiplier + bias); | |
| } | |
| std::string base = fmt; | |
| ReplaceReg(REG1_TOKEN, (this->*GetName1)(reg1), &base); | |
| ReplaceReg(REG2_TOKEN, (this->*GetName2)(reg2), &base); | |
| ReplaceImm(imm, bias, multiplier, &base); | |
| str += base; | |
| str += "\n"; | |
| } | |
| } | |
| } | |
| return str; | |
| } | |
| template <typename ImmType> | |
| std::string RepeatIbS( | |
| void (Ass::*f)(ImmType), int imm_bits, int shift, const std::string& fmt, int bias = 0) { | |
| return RepeatTemplatedImmBitsShift<ImmType>(f, imm_bits, shift, fmt, bias); | |
| } | |
| template <typename ImmType> | |
| std::string RepeatRIbS( | |
| void (Ass::*f)(Reg, ImmType), int imm_bits, int shift, const std::string& fmt, int bias = 0) { | |
| return RepeatTemplatedRegisterImmBitsShift<Reg, ImmType>( | |
| f, | |
| imm_bits, | |
| shift, | |
| GetRegisters(), | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| fmt, | |
| bias); | |
| } | |
| template <typename ImmType> | |
| std::string RepeatRRIbS(void (Ass::*f)(Reg, Reg, ImmType), | |
| int imm_bits, | |
| int shift, | |
| const std::string& fmt, | |
| int bias = 0) { | |
| return RepeatTemplatedRegistersImmBitsShift<Reg, Reg, ImmType>( | |
| f, | |
| imm_bits, | |
| shift, | |
| GetRegisters(), | |
| GetRegisters(), | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| fmt, | |
| bias); | |
| } | |
| template <typename ImmType> | |
| std::string RepeatRRIb(void (Ass::*f)(Reg, Reg, ImmType), | |
| int imm_bits, | |
| const std::string& fmt, | |
| int bias = 0) { | |
| return RepeatTemplatedRegistersImmBits<Reg, Reg, ImmType>(f, | |
| imm_bits, | |
| GetRegisters(), | |
| GetRegisters(), | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| fmt, | |
| bias); | |
| } | |
| template <typename ImmType> | |
| std::string RepeatRRRIb(void (Ass::*f)(Reg, Reg, Reg, ImmType), | |
| int imm_bits, | |
| const std::string& fmt, | |
| int bias = 0) { | |
| return RepeatTemplatedRegistersImmBits<Reg, Reg, Reg, ImmType>(f, | |
| imm_bits, | |
| GetRegisters(), | |
| GetRegisters(), | |
| GetRegisters(), | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| fmt, | |
| bias); | |
| } | |
| template <typename ImmType> | |
| std::string RepeatRIb(void (Ass::*f)(Reg, ImmType), int imm_bits, std::string fmt, int bias = 0) { | |
| return RepeatTemplatedRegisterImmBits<Reg, ImmType>(f, | |
| imm_bits, | |
| GetRegisters(), | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| fmt, | |
| bias); | |
| } | |
| template <typename ImmType> | |
| std::string RepeatFRIb(void (Ass::*f)(FPReg, Reg, ImmType), | |
| int imm_bits, | |
| const std::string& fmt, | |
| int bias = 0) { | |
| return RepeatTemplatedRegistersImmBits<FPReg, Reg, ImmType>(f, | |
| imm_bits, | |
| GetFPRegisters(), | |
| GetRegisters(), | |
| &AssemblerTest::GetFPRegName, | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| fmt, | |
| bias); | |
| } | |
| template <typename ImmType> | |
| std::string RepeatFI(void (Ass::*f)(FPReg, ImmType), | |
| size_t imm_bits, | |
| const std::string& fmt) { | |
| return RepeatTemplatedRegisterImmBits<FPReg, ImmType>(f, | |
| imm_bits, | |
| GetFPRegisters(), | |
| &AssemblerTest::GetFPRegName, | |
| fmt, | |
| /*bias=*/ 0); | |
| } | |
| std::string RepeatFF(void (Ass::*f)(FPReg, FPReg), const std::string& fmt) { | |
| return RepeatTemplatedRegisters<FPReg, FPReg>(f, | |
| GetFPRegisters(), | |
| GetFPRegisters(), | |
| &AssemblerTest::GetFPRegName, | |
| &AssemblerTest::GetFPRegName, | |
| fmt); | |
| } | |
| std::string RepeatFFF(void (Ass::*f)(FPReg, FPReg, FPReg), const std::string& fmt) { | |
| return RepeatTemplatedRegisters<FPReg, FPReg, FPReg>(f, | |
| GetFPRegisters(), | |
| GetFPRegisters(), | |
| GetFPRegisters(), | |
| &AssemblerTest::GetFPRegName, | |
| &AssemblerTest::GetFPRegName, | |
| &AssemblerTest::GetFPRegName, | |
| fmt); | |
| } | |
| std::string RepeatFFFF(void (Ass::*f)(FPReg, FPReg, FPReg, FPReg), const std::string& fmt) { | |
| return RepeatTemplatedRegisters<FPReg, FPReg, FPReg, FPReg>(f, | |
| GetFPRegisters(), | |
| GetFPRegisters(), | |
| GetFPRegisters(), | |
| GetFPRegisters(), | |
| &AssemblerTest::GetFPRegName, | |
| &AssemblerTest::GetFPRegName, | |
| &AssemblerTest::GetFPRegName, | |
| &AssemblerTest::GetFPRegName, | |
| fmt); | |
| } | |
| std::string RepeatFFR(void (Ass::*f)(FPReg, FPReg, Reg), const std::string& fmt) { | |
| return RepeatTemplatedRegisters<FPReg, FPReg, Reg>( | |
| f, | |
| GetFPRegisters(), | |
| GetFPRegisters(), | |
| GetRegisters(), | |
| &AssemblerTest::GetFPRegName, | |
| &AssemblerTest::GetFPRegName, | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| fmt); | |
| } | |
| std::string RepeatFFI(void (Ass::*f)(FPReg, FPReg, const Imm&), | |
| size_t imm_bytes, | |
| const std::string& fmt) { | |
| return RepeatTemplatedRegistersImm<FPReg, FPReg>(f, | |
| GetFPRegisters(), | |
| GetFPRegisters(), | |
| &AssemblerTest::GetFPRegName, | |
| &AssemblerTest::GetFPRegName, | |
| imm_bytes, | |
| fmt); | |
| } | |
| template <typename ImmType> | |
| std::string RepeatFFIb(void (Ass::*f)(FPReg, FPReg, ImmType), | |
| int imm_bits, | |
| const std::string& fmt) { | |
| return RepeatTemplatedRegistersImmBits<FPReg, FPReg, ImmType>(f, | |
| imm_bits, | |
| GetFPRegisters(), | |
| GetFPRegisters(), | |
| &AssemblerTest::GetFPRegName, | |
| &AssemblerTest::GetFPRegName, | |
| fmt); | |
| } | |
| template <typename ImmType> | |
| std::string RepeatIbFF(void (Ass::*f)(ImmType, FPReg, FPReg), | |
| int imm_bits, | |
| const std::string& fmt) { | |
| return RepeatTemplatedImmBitsRegisters<ImmType, FPReg, FPReg>(f, | |
| GetFPRegisters(), | |
| GetFPRegisters(), | |
| &AssemblerTest::GetFPRegName, | |
| &AssemblerTest::GetFPRegName, | |
| imm_bits, | |
| fmt); | |
| } | |
| std::string RepeatRFF(void (Ass::*f)(Reg, FPReg, FPReg), const std::string& fmt) { | |
| return RepeatTemplatedRegisters<Reg, FPReg, FPReg>( | |
| f, | |
| GetRegisters(), | |
| GetFPRegisters(), | |
| GetFPRegisters(), | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| &AssemblerTest::GetFPRegName, | |
| &AssemblerTest::GetFPRegName, | |
| fmt); | |
| } | |
| template <typename ImmType> | |
| std::string RepeatRFIb(void (Ass::*f)(Reg, FPReg, ImmType), | |
| int imm_bits, | |
| const std::string& fmt) { | |
| return RepeatTemplatedRegistersImmBits<Reg, FPReg, ImmType>( | |
| f, | |
| imm_bits, | |
| GetRegisters(), | |
| GetFPRegisters(), | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| &AssemblerTest::GetFPRegName, | |
| fmt); | |
| } | |
| std::string RepeatFR(void (Ass::*f)(FPReg, Reg), const std::string& fmt) { | |
| return RepeatTemplatedRegisters<FPReg, Reg>(f, | |
| GetFPRegisters(), | |
| GetRegisters(), | |
| &AssemblerTest::GetFPRegName, | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| fmt); | |
| } | |
| std::string RepeatFr(void (Ass::*f)(FPReg, Reg), const std::string& fmt) { | |
| return RepeatTemplatedRegisters<FPReg, Reg>(f, | |
| GetFPRegisters(), | |
| GetRegisters(), | |
| &AssemblerTest::GetFPRegName, | |
| &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, | |
| fmt); | |
| } | |
| std::string RepeatRF(void (Ass::*f)(Reg, FPReg), const std::string& fmt) { | |
| return RepeatTemplatedRegisters<Reg, FPReg>(f, | |
| GetRegisters(), | |
| GetFPRegisters(), | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| &AssemblerTest::GetFPRegName, | |
| fmt); | |
| } | |
| std::string RepeatrF(void (Ass::*f)(Reg, FPReg), const std::string& fmt) { | |
| return RepeatTemplatedRegisters<Reg, FPReg>(f, | |
| GetRegisters(), | |
| GetFPRegisters(), | |
| &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, | |
| &AssemblerTest::GetFPRegName, | |
| fmt); | |
| } | |
| std::string RepeatI(void (Ass::*f)(const Imm&), | |
| size_t imm_bytes, | |
| const std::string& fmt, | |
| bool as_uint = false) { | |
| std::string str; | |
| std::vector<int64_t> imms = CreateImmediateValues(imm_bytes, as_uint); | |
| WarnOnCombinations(imms.size()); | |
| for (int64_t imm : imms) { | |
| Imm new_imm = CreateImmediate(imm); | |
| if (f != nullptr) { | |
| (assembler_.get()->*f)(new_imm); | |
| } | |
| std::string base = fmt; | |
| ReplaceImm(imm, /*bias=*/ 0, /*multiplier=*/ 1, &base); | |
| str += base; | |
| str += "\n"; | |
| } | |
| return str; | |
| } | |
| std::string RepeatV(void (Ass::*f)(VecReg), const std::string& fmt) { | |
| return RepeatTemplatedRegister<VecReg>( | |
| f, GetVectorRegisters(), &AssemblerTest::GetVecRegName, fmt); | |
| } | |
| std::string RepeatVV(void (Ass::*f)(VecReg, VecReg), const std::string& fmt) { | |
| return RepeatTemplatedRegisters<VecReg, VecReg>(f, | |
| GetVectorRegisters(), | |
| GetVectorRegisters(), | |
| &AssemblerTest::GetVecRegName, | |
| &AssemblerTest::GetVecRegName, | |
| fmt); | |
| } | |
| std::string RepeatVVV(void (Ass::*f)(VecReg, VecReg, VecReg), const std::string& fmt) { | |
| return RepeatTemplatedRegisters<VecReg, VecReg, VecReg>(f, | |
| GetVectorRegisters(), | |
| GetVectorRegisters(), | |
| GetVectorRegisters(), | |
| &AssemblerTest::GetVecRegName, | |
| &AssemblerTest::GetVecRegName, | |
| &AssemblerTest::GetVecRegName, | |
| fmt); | |
| } | |
| std::string RepeatVVR(void (Ass::*f)(VecReg, VecReg, Reg), const std::string& fmt) { | |
| return RepeatTemplatedRegisters<VecReg, VecReg, Reg>( | |
| f, | |
| GetVectorRegisters(), | |
| GetVectorRegisters(), | |
| GetRegisters(), | |
| &AssemblerTest::GetVecRegName, | |
| &AssemblerTest::GetVecRegName, | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| fmt); | |
| } | |
| std::string RepeatVR(void (Ass::*f)(VecReg, Reg), const std::string& fmt) { | |
| return RepeatTemplatedRegisters<VecReg, Reg>( | |
| f, | |
| GetVectorRegisters(), | |
| GetRegisters(), | |
| &AssemblerTest::GetVecRegName, | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| fmt); | |
| } | |
| std::string RepeatVF(void (Ass::*f)(VecReg, FPReg), const std::string& fmt) { | |
| return RepeatTemplatedRegisters<VecReg, FPReg>(f, | |
| GetVectorRegisters(), | |
| GetFPRegisters(), | |
| &AssemblerTest::GetVecRegName, | |
| &AssemblerTest::GetFPRegName, | |
| fmt); | |
| } | |
| std::string RepeatFV(void (Ass::*f)(FPReg, VecReg), const std::string& fmt) { | |
| return RepeatTemplatedRegisters<FPReg, VecReg>(f, | |
| GetFPRegisters(), | |
| GetVectorRegisters(), | |
| &AssemblerTest::GetFPRegName, | |
| &AssemblerTest::GetVecRegName, | |
| fmt); | |
| } | |
| std::string RepeatRV(void (Ass::*f)(Reg, VecReg), const std::string& fmt) { | |
| return RepeatTemplatedRegisters<Reg, VecReg>( | |
| f, | |
| GetRegisters(), | |
| GetVectorRegisters(), | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| &AssemblerTest::GetVecRegName, | |
| fmt); | |
| } | |
| template <typename ImmType> | |
| std::string RepeatVIb(void (Ass::*f)(VecReg, ImmType), | |
| int imm_bits, | |
| std::string fmt, | |
| int bias = 0) { | |
| return RepeatTemplatedRegisterImmBits<VecReg, ImmType>(f, | |
| imm_bits, | |
| GetVectorRegisters(), | |
| &AssemblerTest::GetVecRegName, | |
| fmt, | |
| bias); | |
| } | |
| template <typename ImmType> | |
| std::string RepeatVRIb(void (Ass::*f)(VecReg, Reg, ImmType), | |
| int imm_bits, | |
| const std::string& fmt, | |
| int bias = 0, | |
| int multiplier = 1) { | |
| return RepeatTemplatedRegistersImmBits<VecReg, Reg, ImmType>( | |
| f, | |
| imm_bits, | |
| GetVectorRegisters(), | |
| GetRegisters(), | |
| &AssemblerTest::GetVecRegName, | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| fmt, | |
| bias, | |
| multiplier); | |
| } | |
| template <typename ImmType> | |
| std::string RepeatRVIb(void (Ass::*f)(Reg, VecReg, ImmType), | |
| int imm_bits, | |
| const std::string& fmt, | |
| int bias = 0, | |
| int multiplier = 1) { | |
| return RepeatTemplatedRegistersImmBits<Reg, VecReg, ImmType>( | |
| f, | |
| imm_bits, | |
| GetRegisters(), | |
| GetVectorRegisters(), | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| &AssemblerTest::GetVecRegName, | |
| fmt, | |
| bias, | |
| multiplier); | |
| } | |
| template <typename ImmType> | |
| std::string RepeatVVIb(void (Ass::*f)(VecReg, VecReg, ImmType), | |
| int imm_bits, | |
| const std::string& fmt, | |
| int bias = 0) { | |
| return RepeatTemplatedRegistersImmBits<VecReg, VecReg, ImmType>(f, | |
| imm_bits, | |
| GetVectorRegisters(), | |
| GetVectorRegisters(), | |
| &AssemblerTest::GetVecRegName, | |
| &AssemblerTest::GetVecRegName, | |
| fmt, | |
| bias); | |
| } | |
| // The following functions are public so that TestFn can use them... | |
| // Returns a vector of address used by any of the repeat methods | |
| // involving an "A" (e.g. RepeatA). | |
| virtual std::vector<Addr> GetAddresses() = 0; | |
| // Returns a vector of registers used by any of the repeat methods | |
| // involving an "R" (e.g. RepeatR). | |
| virtual ArrayRef<const Reg> GetRegisters() = 0; | |
| // Returns a vector of fp-registers used by any of the repeat methods | |
| // involving an "F" (e.g. RepeatFF). | |
| virtual ArrayRef<const FPReg> GetFPRegisters() { | |
| UNIMPLEMENTED(FATAL) << "Architecture does not support floating-point registers"; | |
| UNREACHABLE(); | |
| } | |
| // Returns a vector of dedicated simd-registers used by any of the repeat | |
| // methods involving an "V" (e.g. RepeatVV). | |
| virtual ArrayRef<const VecReg> GetVectorRegisters() { | |
| UNIMPLEMENTED(FATAL) << "Architecture does not support vector registers"; | |
| UNREACHABLE(); | |
| } | |
| // Secondary register names are the secondary view on registers, e.g., 32b on 64b systems. | |
| virtual std::string GetSecondaryRegisterName([[maybe_unused]] const Reg& reg) { | |
| UNIMPLEMENTED(FATAL) << "Architecture does not support secondary registers"; | |
| UNREACHABLE(); | |
| } | |
| // Tertiary register names are the tertiary view on registers, e.g., 16b on 64b systems. | |
| virtual std::string GetTertiaryRegisterName([[maybe_unused]] const Reg& reg) { | |
| UNIMPLEMENTED(FATAL) << "Architecture does not support tertiary registers"; | |
| UNREACHABLE(); | |
| } | |
| // Quaternary register names are the quaternary view on registers, e.g., 8b on 64b systems. | |
| virtual std::string GetQuaternaryRegisterName([[maybe_unused]] const Reg& reg) { | |
| UNIMPLEMENTED(FATAL) << "Architecture does not support quaternary registers"; | |
| UNREACHABLE(); | |
| } | |
| std::string GetRegisterName(const Reg& reg) { | |
| return GetRegName<RegisterView::kUsePrimaryName>(reg); | |
| } | |
| protected: | |
| AssemblerTest() {} | |
| void SetUp() override { | |
| AssemblerTestBase::SetUp(); | |
| allocator_.reset(new ArenaAllocator(&pool_)); | |
| assembler_.reset(CreateAssembler(allocator_.get())); | |
| SetUpHelpers(); | |
| } | |
| void TearDown() override { | |
| AssemblerTestBase::TearDown(); | |
| assembler_.reset(); | |
| allocator_.reset(); | |
| } | |
| // Override this to set up any architecture-specific things, e.g., CPU revision. | |
| virtual Ass* CreateAssembler(ArenaAllocator* allocator) { | |
| return new (allocator) Ass(allocator); | |
| } | |
| // Override this to set up any architecture-specific things, e.g., register vectors. | |
| virtual void SetUpHelpers() {} | |
| // Create a couple of immediate values up to the number of bytes given. | |
| virtual std::vector<int64_t> CreateImmediateValues(size_t imm_bytes, bool as_uint = false) { | |
| std::vector<int64_t> res; | |
| res.push_back(0); | |
| if (!as_uint) { | |
| res.push_back(-1); | |
| } else { | |
| res.push_back(0xFF); | |
| } | |
| res.push_back(0x12); | |
| if (imm_bytes >= 2) { | |
| res.push_back(0x1234); | |
| if (!as_uint) { | |
| res.push_back(-0x1234); | |
| } else { | |
| res.push_back(0xFFFF); | |
| } | |
| if (imm_bytes >= 4) { | |
| res.push_back(0x12345678); | |
| if (!as_uint) { | |
| res.push_back(-0x12345678); | |
| } else { | |
| res.push_back(0xFFFFFFFF); | |
| } | |
| if (imm_bytes >= 6) { | |
| res.push_back(0x123456789ABC); | |
| if (!as_uint) { | |
| res.push_back(-0x123456789ABC); | |
| } | |
| if (imm_bytes >= 8) { | |
| res.push_back(0x123456789ABCDEF0); | |
| if (!as_uint) { | |
| res.push_back(-0x123456789ABCDEF0); | |
| } else { | |
| res.push_back(0xFFFFFFFFFFFFFFFF); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return res; | |
| } | |
| const int kMaxBitsExhaustiveTest = 8; | |
| // Create a couple of immediate values up to the number of bits given. | |
| virtual std::vector<int64_t> CreateImmediateValuesBits(const int imm_bits, | |
| bool as_uint = false, | |
| int shift = 0) { | |
| CHECK_GT(imm_bits, 0); | |
| CHECK_LE(imm_bits, 64); | |
| std::vector<int64_t> res; | |
| if (imm_bits <= kMaxBitsExhaustiveTest) { | |
| if (as_uint) { | |
| for (uint64_t i = MinInt<uint64_t>(imm_bits); i <= MaxInt<uint64_t>(imm_bits); i++) { | |
| res.push_back(static_cast<int64_t>(i << shift)); | |
| } | |
| } else { | |
| for (int64_t i = MinInt<int64_t>(imm_bits); i <= MaxInt<int64_t>(imm_bits); i++) { | |
| res.push_back(i << shift); | |
| } | |
| } | |
| } else { | |
| if (as_uint) { | |
| for (uint64_t i = MinInt<uint64_t>(kMaxBitsExhaustiveTest); | |
| i <= MaxInt<uint64_t>(kMaxBitsExhaustiveTest); | |
| i++) { | |
| res.push_back(static_cast<int64_t>(i << shift)); | |
| } | |
| for (int i = 0; i <= imm_bits; i++) { | |
| uint64_t j = (MaxInt<uint64_t>(kMaxBitsExhaustiveTest) + 1) + | |
| ((MaxInt<uint64_t>(imm_bits) - | |
| (MaxInt<uint64_t>(kMaxBitsExhaustiveTest) + 1)) | |
| * i / imm_bits); | |
| res.push_back(static_cast<int64_t>(j << shift)); | |
| } | |
| } else { | |
| for (int i = 0; i <= imm_bits; i++) { | |
| int64_t j = MinInt<int64_t>(imm_bits) + | |
| ((((MinInt<int64_t>(kMaxBitsExhaustiveTest) - 1) - | |
| MinInt<int64_t>(imm_bits)) | |
| * i) / imm_bits); | |
| res.push_back(static_cast<int64_t>(j << shift)); | |
| } | |
| for (int64_t i = MinInt<int64_t>(kMaxBitsExhaustiveTest); | |
| i <= MaxInt<int64_t>(kMaxBitsExhaustiveTest); | |
| i++) { | |
| res.push_back(static_cast<int64_t>(i << shift)); | |
| } | |
| for (int i = 0; i <= imm_bits; i++) { | |
| int64_t j = (MaxInt<int64_t>(kMaxBitsExhaustiveTest) + 1) + | |
| ((MaxInt<int64_t>(imm_bits) - (MaxInt<int64_t>(kMaxBitsExhaustiveTest) + 1)) | |
| * i / imm_bits); | |
| res.push_back(static_cast<int64_t>(j << shift)); | |
| } | |
| } | |
| } | |
| return res; | |
| } | |
| // Create an immediate from the specific value. | |
| virtual Imm CreateImmediate(int64_t imm_value) = 0; | |
| // | |
| // Addresses repeats. | |
| // | |
| // Repeats over addresses provided by fixture. | |
| std::string RepeatA(void (Ass::*f)(const Addr&), const std::string& fmt) { | |
| return RepeatA(f, GetAddresses(), fmt); | |
| } | |
| // Variant that takes explicit vector of addresss | |
| // (to test restricted addressing modes set). | |
| std::string RepeatA(void (Ass::*f)(const Addr&), | |
| const std::vector<Addr>& a, | |
| const std::string& fmt) { | |
| return RepeatTemplatedMem<Addr>(f, a, &AssemblerTest::GetAddrName, fmt); | |
| } | |
| // Repeats over addresses and immediates provided by fixture. | |
| std::string RepeatAI(void (Ass::*f)(const Addr&, const Imm&), | |
| size_t imm_bytes, | |
| const std::string& fmt) { | |
| return RepeatAI(f, imm_bytes, GetAddresses(), fmt); | |
| } | |
| // Variant that takes explicit vector of addresss | |
| // (to test restricted addressing modes set). | |
| std::string RepeatAI(void (Ass::*f)(const Addr&, const Imm&), | |
| size_t imm_bytes, | |
| const std::vector<Addr>& a, | |
| const std::string& fmt) { | |
| return RepeatTemplatedMemImm<Addr>(f, imm_bytes, a, &AssemblerTest::GetAddrName, fmt); | |
| } | |
| // Repeats over registers and addresses provided by fixture. | |
| std::string RepeatRA(void (Ass::*f)(Reg, const Addr&), const std::string& fmt) { | |
| return RepeatRA(f, GetAddresses(), fmt); | |
| } | |
| // Variant that takes explicit vector of addresss | |
| // (to test restricted addressing modes set). | |
| std::string RepeatRA(void (Ass::*f)(Reg, const Addr&), | |
| const std::vector<Addr>& a, | |
| const std::string& fmt) { | |
| return RepeatTemplatedRegMem<Reg, Addr>( | |
| f, | |
| GetRegisters(), | |
| a, | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| &AssemblerTest::GetAddrName, | |
| fmt); | |
| } | |
| // Repeats over secondary registers and addresses provided by fixture. | |
| std::string RepeatrA(void (Ass::*f)(Reg, const Addr&), const std::string& fmt) { | |
| return RepeatrA(f, GetAddresses(), fmt); | |
| } | |
| // Variant that takes explicit vector of addresss | |
| // (to test restricted addressing modes set). | |
| std::string RepeatrA(void (Ass::*f)(Reg, const Addr&), | |
| const std::vector<Addr>& a, | |
| const std::string& fmt) { | |
| return RepeatTemplatedRegMem<Reg, Addr>( | |
| f, | |
| GetRegisters(), | |
| a, | |
| &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, | |
| &AssemblerTest::GetAddrName, | |
| fmt); | |
| } | |
| // Repeats over tertiary registers and addresses provided by fixture. | |
| std::string RepeatwA(void (Ass::*f)(Reg, const Addr&), const std::string& fmt) { | |
| return RepeatwA(f, GetAddresses(), fmt); | |
| } | |
| // Variant that takes explicit vector of addresss | |
| // (to test restricted addressing modes set). | |
| std::string RepeatwA(void (Ass::*f)(Reg, const Addr&), | |
| const std::vector<Addr>& a, | |
| const std::string& fmt) { | |
| return RepeatTemplatedRegMem<Reg, Addr>( | |
| f, | |
| GetRegisters(), | |
| a, | |
| &AssemblerTest::GetRegName<RegisterView::kUseTertiaryName>, | |
| &AssemblerTest::GetAddrName, | |
| fmt); | |
| } | |
| // Repeats over quaternary registers and addresses provided by fixture. | |
| std::string RepeatbA(void (Ass::*f)(Reg, const Addr&), const std::string& fmt) { | |
| return RepeatbA(f, GetAddresses(), fmt); | |
| } | |
| // Variant that takes explicit vector of addresss | |
| // (to test restricted addressing modes set). | |
| std::string RepeatbA(void (Ass::*f)(Reg, const Addr&), | |
| const std::vector<Addr>& a, | |
| const std::string& fmt) { | |
| return RepeatTemplatedRegMem<Reg, Addr>( | |
| f, | |
| GetRegisters(), | |
| a, | |
| &AssemblerTest::GetRegName<RegisterView::kUseQuaternaryName>, | |
| &AssemblerTest::GetAddrName, | |
| fmt); | |
| } | |
| // Repeats over fp-registers and addresses provided by fixture. | |
| std::string RepeatFA(void (Ass::*f)(FPReg, const Addr&), const std::string& fmt) { | |
| return RepeatFA(f, GetAddresses(), fmt); | |
| } | |
| // Variant that takes explicit vector of addresss | |
| // (to test restricted addressing modes set). | |
| std::string RepeatFA(void (Ass::*f)(FPReg, const Addr&), | |
| const std::vector<Addr>& a, | |
| const std::string& fmt) { | |
| return RepeatTemplatedRegMem<FPReg, Addr>( | |
| f, | |
| GetFPRegisters(), | |
| a, | |
| &AssemblerTest::GetFPRegName, | |
| &AssemblerTest::GetAddrName, | |
| fmt); | |
| } | |
| // Repeats over addresses and registers provided by fixture. | |
| std::string RepeatAR(void (Ass::*f)(const Addr&, Reg), const std::string& fmt) { | |
| return RepeatAR(f, GetAddresses(), fmt); | |
| } | |
| // Variant that takes explicit vector of addresss | |
| // (to test restricted addressing modes set). | |
| std::string RepeatAR(void (Ass::*f)(const Addr&, Reg), | |
| const std::vector<Addr>& a, | |
| const std::string& fmt) { | |
| return RepeatTemplatedMemReg<Addr, Reg>( | |
| f, | |
| a, | |
| GetRegisters(), | |
| &AssemblerTest::GetAddrName, | |
| &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | |
| fmt); | |
| } | |
| // Repeats over addresses and secondary registers provided by fixture. | |
| std::string RepeatAr(void (Ass::*f)(const Addr&, Reg), const std::string& fmt) { | |
| return RepeatAr(f, GetAddresses(), fmt); | |
| } | |
| // Variant that takes explicit vector of addresss | |
| // (to test restricted addressing modes set). | |
| std::string RepeatAr(void (Ass::*f)(const Addr&, Reg), | |
| const std::vector<Addr>& a, | |
| const std::string& fmt) { | |
| return RepeatTemplatedMemReg<Addr, Reg>( | |
| f, | |
| a, | |
| GetRegisters(), | |
| &AssemblerTest::GetAddrName, | |
| &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, | |
| fmt); | |
| } | |
| // Repeats over addresses and tertiary registers provided by fixture. | |
| std::string RepeatAw(void (Ass::*f)(const Addr&, Reg), const std::string& fmt) { | |
| return RepeatAw(f, GetAddresses(), fmt); | |
| } | |
| // Variant that takes explicit vector of addresss | |
| // (to test restricted addressing modes set). | |
| std::string RepeatAw(void (Ass::*f)(const Addr&, Reg), | |
| const std::vector<Addr>& a, | |
| const std::string& fmt) { | |
| return RepeatTemplatedMemReg<Addr, Reg>( | |
| f, | |
| a, | |
| GetRegisters(), | |
| &AssemblerTest::GetAddrName, | |
| &AssemblerTest::GetRegName<RegisterView::kUseTertiaryName>, | |
| fmt); | |
| } | |
| // Repeats over addresses and quaternary registers provided by fixture. | |
| std::string RepeatAb(void (Ass::*f)(const Addr&, Reg), const std::string& fmt) { | |
| return RepeatAb(f, GetAddresses(), fmt); | |
| } | |
| // Variant that takes explicit vector of addresss | |
| // (to test restricted addressing modes set). | |
| std::string RepeatAb(void (Ass::*f)(const Addr&, Reg), | |
| const std::vector<Addr>& a, | |
| const std::string& fmt) { | |
| return RepeatTemplatedMemReg<Addr, Reg>( | |
| f, | |
| a, | |
| GetRegisters(), | |
| &AssemblerTest::GetAddrName, | |
| &AssemblerTest::GetRegName<RegisterView::kUseQuaternaryName>, | |
| fmt); | |
| } | |
| // Repeats over addresses and fp-registers provided by fixture. | |
| std::string RepeatAF(void (Ass::*f)(const Addr&, FPReg), const std::string& fmt) { | |
| return RepeatAF(f, GetAddresses(), fmt); | |
| } | |
| // Variant that takes explicit vector of addresss | |
| // (to test restricted addressing modes set). | |
| std::string RepeatAF(void (Ass::*f)(const Addr&, FPReg), | |
| const std::vector<Addr>& a, | |
| const std::string& fmt) { | |
| return RepeatTemplatedMemReg<Addr, FPReg>( | |
| f, | |
| a, | |
| GetFPRegisters(), | |
| &AssemblerTest::GetAddrName, | |
| &AssemblerTest::GetFPRegName, | |
| fmt); | |
| } | |
| template <typename AddrType> | |
| std::string RepeatTemplatedMem(void (Ass::*f)(const AddrType&), | |
| const std::vector<AddrType> addresses, | |
| std::string (AssemblerTest::*GetAName)(const AddrType&), | |
| const std::string& fmt) { | |
| WarnOnCombinations(addresses.size()); | |
| std::string str; | |
| for (auto addr : addresses) { | |
| if (f != nullptr) { | |
| (assembler_.get()->*f)(addr); | |
| } | |
| std::string base = fmt; | |
| ReplaceAddr((this->*GetAName)(addr), &base); | |
| str += base; | |
| str += "\n"; | |
| } | |
| return str; | |
| } | |
| template <typename AddrType> | |
| std::string RepeatTemplatedMemImm(void (Ass::*f)(const AddrType&, const Imm&), | |
| size_t imm_bytes, | |
| const std::vector<AddrType> addresses, | |
| std::string (AssemblerTest::*GetAName)(const AddrType&), | |
| const std::string& fmt) { | |
| std::vector<int64_t> imms = CreateImmediateValues(imm_bytes); | |
| WarnOnCombinations(addresses.size() * imms.size()); | |
| std::string str; | |
| for (auto addr : addresses) { | |
| for (int64_t imm : imms) { | |
| Imm new_imm = CreateImmediate(imm); | |
| if (f != nullptr) { | |
| (assembler_.get()->*f)(addr, new_imm); | |
| } | |
| std::string base = fmt; | |
| ReplaceAddr((this->*GetAName)(addr), &base); | |
| ReplaceImm(imm, /*bias=*/ 0, /*multiplier=*/ 1, &base); | |
| str += base; | |
| str += "\n"; | |
| } | |
| } | |
| return str; | |
| } | |
| template <typename RegType, typename AddrType> | |
| std::string RepeatTemplatedRegMem(void (Ass::*f)(RegType, const AddrType&), | |
| ArrayRef<const RegType> registers, | |
| const std::vector<AddrType> addresses, | |
| std::string (AssemblerTest::*GetRName)(const RegType&), | |
| std::string (AssemblerTest::*GetAName)(const AddrType&), | |
| const std::string& fmt) { | |
| WarnOnCombinations(addresses.size() * registers.size()); | |
| std::string str; | |
| for (auto reg : registers) { | |
| for (auto addr : addresses) { | |
| if (f != nullptr) { | |
| (assembler_.get()->*f)(reg, addr); | |
| } | |
| std::string base = fmt; | |
| ReplaceReg(REG_TOKEN, (this->*GetRName)(reg), &base); | |
| ReplaceAddr((this->*GetAName)(addr), &base); | |
| str += base; | |
| str += "\n"; | |
| } | |
| } | |
| return str; | |
| } | |
| template <typename AddrType, typename RegType> | |
| std::string RepeatTemplatedMemReg(void (Ass::*f)(const AddrType&, RegType), | |
| const std::vector<AddrType> addresses, | |
| ArrayRef<const RegType> registers, | |
| std::string (AssemblerTest::*GetAName)(const AddrType&), | |
| std::string (AssemblerTest::*GetRName)(const RegType&), | |
| const std::string& fmt) { | |
| WarnOnCombinations(addresses.size() * registers.size()); | |
| std::string str; | |
| for (auto addr : addresses) { | |
| for (auto reg : registers) { | |
| if (f != nullptr) { | |
| (assembler_.get()->*f)(addr, reg); | |
| } | |
| std::string base = fmt; | |
| ReplaceAddr((this->*GetAName)(addr), &base); | |
| ReplaceReg(REG_TOKEN, (this->*GetRName)(reg), &base); | |
| str += base; | |
| str += "\n"; | |
| } | |
| } | |
| return str; | |
| } | |
| // | |
| // Register repeats. | |
| // | |
| template <typename RegType> | |
| std::string RepeatTemplatedRegister(void (Ass::*f)(RegType), | |
| ArrayRef<const RegType> registers, | |
| std::string (AssemblerTest::*GetName)(const RegType&), | |
| const std::string& fmt) { | |
| std::string str; | |
| for (auto reg : registers) { | |
| if (f != nullptr) { | |
| (assembler_.get()->*f)(reg); | |
| } | |
| std::string base = fmt; | |
| ReplaceReg(REG_TOKEN, (this->*GetName)(reg), &base); | |
| str += base; | |
| str += "\n"; | |
| } | |
| return str; | |
| } | |
| template <typename Reg1, typename Reg2> | |
| std::string RepeatTemplatedRegisters(void (Ass::*f)(Reg1, Reg2), | |
| ArrayRef<const Reg1> reg1_registers, | |
| ArrayRef<const Reg2> reg2_registers, | |
| std::string (AssemblerTest::*GetName1)(const Reg1&), | |
| std::string (AssemblerTest::*GetName2)(const Reg2&), | |
| const std::string& fmt, | |
| const std::vector<std::pair<Reg1, Reg2>>* except = nullptr) { | |
| WarnOnCombinations(reg1_registers.size() * reg2_registers.size()); | |
| std::string str; | |
| for (auto reg1 : reg1_registers) { | |
| for (auto reg2 : reg2_registers) { | |
| // Check if this register pair is on the exception list. If so, skip it. | |
| if (except != nullptr) { | |
| const auto& pair = std::make_pair(reg1, reg2); | |
| if (std::find(except->begin(), except->end(), pair) != except->end()) { | |
| continue; | |
| } | |
| } | |
| if (f != nullptr) { | |
| (assembler_.get()->*f)(reg1, reg2); | |
| } | |
| std::string base = fmt; | |
| ReplaceReg(REG1_TOKEN, (this->*GetName1)(reg1), &base); | |
| ReplaceReg(REG2_TOKEN, (this->*GetName2)(reg2), &base); | |
| str += base; | |
| str += "\n"; | |
| } | |
| } | |
| return str; | |
| } | |
| template <typename Reg1, typename Reg2> | |
| std::string RepeatTemplatedRegistersNoDupes(void (Ass::*f)(Reg1, Reg2), | |
| ArrayRef<const Reg1> reg1_registers, | |
| ArrayRef<const Reg2> reg2_registers, | |
| std::string (AssemblerTest::*GetName1)(const Reg1&), | |
| std::string (AssemblerTest::*GetName2)(const Reg2&), | |
| const std::string& fmt) { | |
| WarnOnCombinations(reg1_registers.size() * reg2_registers.size()); | |
| std::string str; | |
| for (auto reg1 : reg1_registers) { | |
| for (auto reg2 : reg2_registers) { | |
| if (reg1 == reg2) continue; | |
| if (f != nullptr) { | |
| (assembler_.get()->*f)(reg1, reg2); | |
| } | |
| std::string base = fmt; | |
| ReplaceReg(REG1_TOKEN, (this->*GetName1)(reg1), &base); | |
| ReplaceReg(REG2_TOKEN, (this->*GetName2)(reg2), &base); | |
| str += base; | |
| str += "\n"; | |
| } | |
| } | |
| return str; | |
| } | |
| template <typename Reg1, typename Reg2, typename Reg3> | |
| std::string RepeatTemplatedRegisters(void (Ass::*f)(Reg1, Reg2, Reg3), | |
| ArrayRef<const Reg1> reg1_registers, | |
| ArrayRef<const Reg2> reg2_registers, | |
| ArrayRef<const Reg3> reg3_registers, | |
| std::string (AssemblerTest::*GetName1)(const Reg1&), | |
| std::string (AssemblerTest::*GetName2)(const Reg2&), | |
| std::string (AssemblerTest::*GetName3)(const Reg3&), | |
| const std::string& fmt) { | |
| std::string str; | |
| for (auto reg1 : reg1_registers) { | |
| for (auto reg2 : reg2_registers) { | |
| for (auto reg3 : reg3_registers) { | |
| if (f != nullptr) { | |
| (assembler_.get()->*f)(reg1, reg2, reg3); | |
| } | |
| std::string base = fmt; | |
| ReplaceReg(REG1_TOKEN, (this->*GetName1)(reg1), &base); | |
| ReplaceReg(REG2_TOKEN, (this->*GetName2)(reg2), &base); | |
| ReplaceReg(REG3_TOKEN, (this->*GetName3)(reg3), &base); | |
| str += base; | |
| str += "\n"; | |
| } | |
| } | |
| } | |
| return str; | |
| } | |
| template <typename Reg1, typename Reg2, typename Reg3, typename Reg4> | |
| std::string RepeatTemplatedRegisters(void (Ass::*f)(Reg1, Reg2, Reg3, Reg4), | |
| ArrayRef<const Reg1> reg1_registers, | |
| ArrayRef<const Reg2> reg2_registers, | |
| ArrayRef<const Reg3> reg3_registers, | |
| ArrayRef<const Reg4> reg4_registers, | |
| std::string (AssemblerTest::*GetName1)(const Reg1&), | |
| std::string (AssemblerTest::*GetName2)(const Reg2&), | |
| std::string (AssemblerTest::*GetName3)(const Reg3&), | |
| std::string (AssemblerTest::*GetName4)(const Reg4&), | |
| const std::string& fmt) { | |
| std::string str; | |
| for (auto reg1 : reg1_registers) { | |
| for (auto reg2 : reg2_registers) { | |
| for (auto reg3 : reg3_registers) { | |
| for (auto reg4 : reg4_registers) { | |
| if (f != nullptr) { | |
| (assembler_.get()->*f)(reg1, reg2, reg3, reg4); | |
| } | |
| std::string base = fmt; | |
| ReplaceReg(REG1_TOKEN, (this->*GetName1)(reg1), &base); | |
| ReplaceReg(REG2_TOKEN, (this->*GetName2)(reg2), &base); | |
| ReplaceReg(REG3_TOKEN, (this->*GetName3)(reg3), &base); | |
| ReplaceReg(REG4_TOKEN, (this->*GetName4)(reg4), &base); | |
| str += base; | |
| str += "\n"; | |
| } | |
| } | |
| } | |
| } | |
| return str; | |
| } | |
| template <typename Reg1, typename Reg2> | |
| std::string RepeatTemplatedRegistersImm(void (Ass::*f)(Reg1, Reg2, const Imm&), | |
| ArrayRef<const Reg1> reg1_registers, | |
| ArrayRef<const Reg2> reg2_registers, | |
| std::string (AssemblerTest::*GetName1)(const Reg1&), | |
| std::string (AssemblerTest::*GetName2)(const Reg2&), | |
| size_t imm_bytes, | |
| const std::string& fmt) { | |
| std::vector<int64_t> imms = CreateImmediateValues(imm_bytes); | |
| WarnOnCombinations(reg1_registers.size() * reg2_registers.size() * imms.size()); | |
| std::string str; | |
| for (auto reg1 : reg1_registers) { | |
| for (auto reg2 : reg2_registers) { | |
| for (int64_t imm : imms) { | |
| Imm new_imm = CreateImmediate(imm); | |
| if (f != nullptr) { | |
| (assembler_.get()->*f)(reg1, reg2, new_imm); | |
| } | |
| std::string base = fmt; | |
| ReplaceReg(REG1_TOKEN, (this->*GetName1)(reg1), &base); | |
| ReplaceReg(REG2_TOKEN, (this->*GetName2)(reg2), &base); | |
| ReplaceImm(imm, /*bias=*/ 0, /*multiplier=*/ 1, &base); | |
| str += base; | |
| str += "\n"; | |
| } | |
| } | |
| } | |
| return str; | |
| } | |
| std::string GetAddrName(const Addr& addr) { | |
| std::ostringstream saddr; | |
| saddr << addr; | |
| return saddr.str(); | |
| } | |
| template <RegisterView kRegView> | |
| std::string GetRegName(const Reg& reg) { | |
| std::ostringstream sreg; | |
| switch (kRegView) { | |
| case RegisterView::kUsePrimaryName: | |
| sreg << reg; | |
| break; | |
| case RegisterView::kUseSecondaryName: | |
| sreg << GetSecondaryRegisterName(reg); | |
| break; | |
| case RegisterView::kUseTertiaryName: | |
| sreg << GetTertiaryRegisterName(reg); | |
| break; | |
| case RegisterView::kUseQuaternaryName: | |
| sreg << GetQuaternaryRegisterName(reg); | |
| break; | |
| } | |
| return sreg.str(); | |
| } | |
| std::string GetFPRegName(const FPReg& reg) { | |
| std::ostringstream sreg; | |
| sreg << reg; | |
| return sreg.str(); | |
| } | |
| std::string GetVecRegName(const VecReg& reg) { | |
| std::ostringstream sreg; | |
| sreg << reg; | |
| return sreg.str(); | |
| } | |
| void WarnOnCombinations(size_t count) { | |
| if (count > kWarnManyCombinationsThreshold) { | |
| GTEST_LOG_(WARNING) << "Many combinations (" << count << "), test generation might be slow."; | |
| } | |
| } | |
| static void ReplaceReg(const std::string& reg_token, | |
| const std::string& replacement, | |
| /*inout*/ std::string* str) { | |
| size_t reg_index; | |
| while ((reg_index = str->find(reg_token)) != std::string::npos) { | |
| str->replace(reg_index, reg_token.length(), replacement); | |
| } | |
| } | |
| static void ReplaceImm(int64_t imm, | |
| int64_t bias, | |
| int64_t multiplier, | |
| /*inout*/ std::string* str) { | |
| size_t imm_index = str->find(IMM_TOKEN); | |
| if (imm_index != std::string::npos) { | |
| std::ostringstream sreg; | |
| sreg << imm * multiplier + bias; | |
| std::string imm_string = sreg.str(); | |
| str->replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string); | |
| } | |
| } | |
| static void ReplaceAddr(const std::string& replacement, /*inout*/ std::string* str) { | |
| size_t addr_index; | |
| if ((addr_index = str->find(ADDRESS_TOKEN)) != std::string::npos) { | |
| str->replace(addr_index, ConstexprStrLen(ADDRESS_TOKEN), replacement); | |
| } | |
| } | |
| static constexpr const char* ADDRESS_TOKEN = "{mem}"; | |
| static constexpr const char* REG_TOKEN = "{reg}"; | |
| static constexpr const char* REG1_TOKEN = "{reg1}"; | |
| static constexpr const char* REG2_TOKEN = "{reg2}"; | |
| static constexpr const char* REG3_TOKEN = "{reg3}"; | |
| static constexpr const char* REG4_TOKEN = "{reg4}"; | |
| static constexpr const char* IMM_TOKEN = "{imm}"; | |
| private: | |
| template <RegisterView kRegView> | |
| std::string RepeatRegisterImm(void (Ass::*f)(Reg, const Imm&), | |
| size_t imm_bytes, | |
| const std::string& fmt) { | |
| ArrayRef<const Reg> registers = GetRegisters(); | |
| std::string str; | |
| std::vector<int64_t> imms = CreateImmediateValues(imm_bytes); | |
| WarnOnCombinations(registers.size() * imms.size()); | |
| for (auto reg : registers) { | |
| for (int64_t imm : imms) { | |
| Imm new_imm = CreateImmediate(imm); | |
| if (f != nullptr) { | |
| (assembler_.get()->*f)(reg, new_imm); | |
| } | |
| std::string base = fmt; | |
| ReplaceReg(REG_TOKEN, GetRegName<kRegView>(reg), &base); | |
| ReplaceImm(imm, /*bias=*/ 0, /*multiplier=*/ 1, &base); | |
| str += base; | |
| str += "\n"; | |
| } | |
| } | |
| return str; | |
| } | |
| // Override this to pad the code with NOPs to a certain size if needed. | |
| virtual void Pad([[maybe_unused]] std::vector<uint8_t>& data) {} | |
| void DriverWrapper(const std::string& assembly_text, const std::string& test_name) { | |
| assembler_->FinalizeCode(); | |
| size_t cs = assembler_->CodeSize(); | |
| std::unique_ptr<std::vector<uint8_t>> data(new std::vector<uint8_t>(cs)); | |
| MemoryRegion code(&(*data)[0], data->size()); | |
| assembler_->CopyInstructions(code); | |
| Pad(*data); | |
| Driver(*data, assembly_text, test_name); | |
| } | |
| static constexpr size_t kWarnManyCombinationsThreshold = 500; | |
| MallocArenaPool pool_; | |
| std::unique_ptr<ArenaAllocator> allocator_; | |
| std::unique_ptr<Ass> assembler_; | |
| DISALLOW_COPY_AND_ASSIGN(AssemblerTest); | |
| }; | |
| } // namespace art | |
| #endif // ART_COMPILER_UTILS_ASSEMBLER_TEST_H_ |