summaryrefslogtreecommitdiff
path: root/compiler/utils/assembler_test.h
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/utils/assembler_test.h')
-rw-r--r--compiler/utils/assembler_test.h376
1 files changed, 272 insertions, 104 deletions
diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h
index 0378176afd..9d3fa01a9d 100644
--- a/compiler/utils/assembler_test.h
+++ b/compiler/utils/assembler_test.h
@@ -29,21 +29,31 @@
namespace art {
+// 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);
+}
+
// Use a glocal static variable to keep the same name for all test data. Else we'll just spam the
// temp directory.
static std::string tmpnam_;
-template<typename Ass, typename Reg, typename Imm>
+template<typename Ass, typename Reg, typename FPReg, typename Imm>
class AssemblerTest : public testing::Test {
public:
+ enum class RegisterView { // private
+ kUsePrimaryName,
+ kUseSecondaryName
+ };
+
Ass* GetAssembler() {
return assembler_.get();
}
- typedef std::string (*TestFn)(Ass* assembler);
+ typedef std::string (*TestFn)(AssemblerTest* assembler_test, Ass* assembler);
void DriverFn(TestFn f, std::string test_name) {
- Driver(f(assembler_.get()), test_name);
+ Driver(f(this, assembler_.get()), test_name);
}
// This driver assumes the assembler has already been called.
@@ -52,116 +62,114 @@ class AssemblerTest : public testing::Test {
}
std::string RepeatR(void (Ass::*f)(Reg), std::string fmt) {
- const std::vector<Reg*> registers = GetRegisters();
- std::string str;
- for (auto reg : registers) {
- (assembler_.get()->*f)(*reg);
- std::string base = fmt;
-
- size_t reg_index = base.find("{reg}");
- if (reg_index != std::string::npos) {
- std::ostringstream sreg;
- sreg << *reg;
- std::string reg_string = sreg.str();
- base.replace(reg_index, 5, reg_string);
- }
+ return RepeatTemplatedRegister<Reg>(f,
+ GetRegisters(),
+ &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+ fmt);
+ }
- if (str.size() > 0) {
- str += "\n";
- }
- str += base;
- }
- // Add a newline at the end.
- str += "\n";
- return str;
+ std::string Repeatr(void (Ass::*f)(Reg), std::string fmt) {
+ return RepeatTemplatedRegister<Reg>(f,
+ GetRegisters(),
+ &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
+ fmt);
}
std::string RepeatRR(void (Ass::*f)(Reg, Reg), std::string fmt) {
- const std::vector<Reg*> registers = GetRegisters();
- std::string str;
- for (auto reg1 : registers) {
- for (auto reg2 : registers) {
- (assembler_.get()->*f)(*reg1, *reg2);
- std::string base = fmt;
-
- size_t reg1_index = base.find("{reg1}");
- if (reg1_index != std::string::npos) {
- std::ostringstream sreg;
- sreg << *reg1;
- std::string reg_string = sreg.str();
- base.replace(reg1_index, 6, reg_string);
- }
+ return RepeatTemplatedRegisters<Reg, Reg>(f,
+ GetRegisters(),
+ GetRegisters(),
+ &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+ &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+ fmt);
+ }
- size_t reg2_index = base.find("{reg2}");
- if (reg2_index != std::string::npos) {
- std::ostringstream sreg;
- sreg << *reg2;
- std::string reg_string = sreg.str();
- base.replace(reg2_index, 6, reg_string);
- }
+ std::string Repeatrr(void (Ass::*f)(Reg, Reg), std::string fmt) {
+ return RepeatTemplatedRegisters<Reg, Reg>(f,
+ GetRegisters(),
+ GetRegisters(),
+ &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
+ &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
+ fmt);
+ }
- if (str.size() > 0) {
- str += "\n";
- }
- str += base;
- }
- }
- // Add a newline at the end.
- str += "\n";
- return str;
+ std::string RepeatRr(void (Ass::*f)(Reg, Reg), std::string fmt) {
+ return RepeatTemplatedRegisters<Reg, Reg>(f,
+ GetRegisters(),
+ GetRegisters(),
+ &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+ &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
+ fmt);
}
std::string RepeatRI(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, std::string fmt) {
- const std::vector<Reg*> registers = GetRegisters();
- std::string str;
- std::vector<int64_t> imms = CreateImmediateValues(imm_bytes);
- for (auto reg : registers) {
- for (int64_t imm : imms) {
- Imm new_imm = CreateImmediate(imm);
- (assembler_.get()->*f)(*reg, new_imm);
- std::string base = fmt;
+ return RepeatRegisterImm<RegisterView::kUsePrimaryName>(f, imm_bytes, fmt);
+ }
- size_t reg_index = base.find("{reg}");
- if (reg_index != std::string::npos) {
- std::ostringstream sreg;
- sreg << *reg;
- std::string reg_string = sreg.str();
- base.replace(reg_index, 5, reg_string);
- }
+ std::string Repeatri(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, std::string fmt) {
+ return RepeatRegisterImm<RegisterView::kUseSecondaryName>(f, imm_bytes, fmt);
+ }
- size_t imm_index = base.find("{imm}");
- if (imm_index != std::string::npos) {
- std::ostringstream sreg;
- sreg << imm;
- std::string imm_string = sreg.str();
- base.replace(imm_index, 5, imm_string);
- }
+ std::string RepeatFF(void (Ass::*f)(FPReg, FPReg), std::string fmt) {
+ return RepeatTemplatedRegisters<FPReg, FPReg>(f,
+ GetFPRegisters(),
+ GetFPRegisters(),
+ &AssemblerTest::GetFPRegName,
+ &AssemblerTest::GetFPRegName,
+ fmt);
+ }
- if (str.size() > 0) {
- str += "\n";
- }
- str += base;
- }
- }
- // Add a newline at the end.
- str += "\n";
- return str;
+ std::string RepeatFR(void (Ass::*f)(FPReg, Reg), 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), 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), std::string fmt) {
+ return RepeatTemplatedRegisters<Reg, FPReg>(f,
+ GetRegisters(),
+ GetFPRegisters(),
+ &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+ &AssemblerTest::GetFPRegName,
+ fmt);
}
- std::string RepeatI(void (Ass::*f)(const Imm&), size_t imm_bytes, std::string fmt) {
+ std::string RepeatrF(void (Ass::*f)(Reg, FPReg), 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, std::string fmt,
+ bool as_uint = false) {
std::string str;
- std::vector<int64_t> imms = CreateImmediateValues(imm_bytes);
+ std::vector<int64_t> imms = CreateImmediateValues(imm_bytes, as_uint);
for (int64_t imm : imms) {
Imm new_imm = CreateImmediate(imm);
(assembler_.get()->*f)(new_imm);
std::string base = fmt;
- size_t imm_index = base.find("{imm}");
+ size_t imm_index = base.find(IMM_TOKEN);
if (imm_index != std::string::npos) {
std::ostringstream sreg;
sreg << imm;
std::string imm_string = sreg.str();
- base.replace(imm_index, 5, imm_string);
+ base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
}
if (str.size() > 0) {
@@ -200,7 +208,24 @@ class AssemblerTest : public testing::Test {
return true;
}
+ // The following functions are public so that TestFn can use them...
+
+ virtual std::vector<Reg*> GetRegisters() = 0;
+
+ virtual std::vector<FPReg*> GetFPRegisters() {
+ UNIMPLEMENTED(FATAL) << "Architecture does not support floating-point registers";
+ UNREACHABLE();
+ }
+
+ // Secondary register names are the secondary view on registers, e.g., 32b on 64b systems.
+ virtual std::string GetSecondaryRegisterName(const Reg& reg ATTRIBUTE_UNUSED) {
+ UNIMPLEMENTED(FATAL) << "Architecture does not support secondary registers";
+ UNREACHABLE();
+ }
+
protected:
+ explicit AssemblerTest() {}
+
void SetUp() OVERRIDE {
assembler_.reset(new Ass());
@@ -219,8 +244,6 @@ class AssemblerTest : public testing::Test {
// Override this to set up any architecture-specific things, e.g., register vectors.
virtual void SetUpHelpers() {}
- virtual std::vector<Reg*> GetRegisters() = 0;
-
// Get the typically used name for this architecture, e.g., aarch64, x86_64, ...
virtual std::string GetArchitectureString() = 0;
@@ -305,23 +328,41 @@ class AssemblerTest : public testing::Test {
}
// Create a couple of immediate values up to the number of bytes given.
- virtual std::vector<int64_t> CreateImmediateValues(size_t imm_bytes) {
+ virtual std::vector<int64_t> CreateImmediateValues(size_t imm_bytes, bool as_uint = false) {
std::vector<int64_t> res;
res.push_back(0);
- res.push_back(-1);
+ if (!as_uint) {
+ res.push_back(-1);
+ } else {
+ res.push_back(0xFF);
+ }
res.push_back(0x12);
if (imm_bytes >= 2) {
res.push_back(0x1234);
- res.push_back(-0x1234);
+ if (!as_uint) {
+ res.push_back(-0x1234);
+ } else {
+ res.push_back(0xFFFF);
+ }
if (imm_bytes >= 4) {
res.push_back(0x12345678);
- res.push_back(-0x12345678);
+ if (!as_uint) {
+ res.push_back(-0x12345678);
+ } else {
+ res.push_back(0xFFFFFFFF);
+ }
if (imm_bytes >= 6) {
res.push_back(0x123456789ABC);
- res.push_back(-0x123456789ABC);
+ if (!as_uint) {
+ res.push_back(-0x123456789ABC);
+ }
if (imm_bytes >= 8) {
res.push_back(0x123456789ABCDEF0);
- res.push_back(-0x123456789ABCDEF0);
+ if (!as_uint) {
+ res.push_back(-0x123456789ABCDEF0);
+ } else {
+ res.push_back(0xFFFFFFFFFFFFFFFF);
+ }
}
}
}
@@ -332,7 +373,127 @@ class AssemblerTest : public testing::Test {
// Create an immediate from the specific value.
virtual Imm CreateImmediate(int64_t imm_value) = 0;
+ template <typename RegType>
+ std::string RepeatTemplatedRegister(void (Ass::*f)(RegType),
+ const std::vector<RegType*> registers,
+ std::string (AssemblerTest::*GetName)(const RegType&),
+ std::string fmt) {
+ std::string str;
+ for (auto reg : registers) {
+ (assembler_.get()->*f)(*reg);
+ std::string base = fmt;
+
+ std::string reg_string = (this->*GetName)(*reg);
+ size_t reg_index;
+ if ((reg_index = base.find(REG_TOKEN)) != std::string::npos) {
+ base.replace(reg_index, ConstexprStrLen(REG_TOKEN), reg_string);
+ }
+
+ if (str.size() > 0) {
+ str += "\n";
+ }
+ str += base;
+ }
+ // Add a newline at the end.
+ str += "\n";
+ return str;
+ }
+
+ template <typename Reg1, typename Reg2>
+ std::string RepeatTemplatedRegisters(void (Ass::*f)(Reg1, Reg2),
+ const std::vector<Reg1*> reg1_registers,
+ const std::vector<Reg2*> reg2_registers,
+ std::string (AssemblerTest::*GetName1)(const Reg1&),
+ std::string (AssemblerTest::*GetName2)(const Reg2&),
+ std::string fmt) {
+ std::string str;
+ for (auto reg1 : reg1_registers) {
+ for (auto reg2 : reg2_registers) {
+ (assembler_.get()->*f)(*reg1, *reg2);
+ std::string base = fmt;
+
+ std::string reg1_string = (this->*GetName1)(*reg1);
+ size_t reg1_index;
+ while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
+ base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
+ }
+
+ std::string reg2_string = (this->*GetName2)(*reg2);
+ size_t reg2_index;
+ while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
+ base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
+ }
+
+ if (str.size() > 0) {
+ str += "\n";
+ }
+ str += base;
+ }
+ }
+ // Add a newline at the end.
+ str += "\n";
+ return str;
+ }
+
private:
+ 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;
+ }
+ return sreg.str();
+ }
+
+ std::string GetFPRegName(const FPReg& reg) {
+ std::ostringstream sreg;
+ sreg << reg;
+ return sreg.str();
+ }
+
+ template <RegisterView kRegView>
+ std::string RepeatRegisterImm(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes,
+ std::string fmt) {
+ const std::vector<Reg*> registers = GetRegisters();
+ std::string str;
+ std::vector<int64_t> imms = CreateImmediateValues(imm_bytes);
+ for (auto reg : registers) {
+ for (int64_t imm : imms) {
+ Imm new_imm = CreateImmediate(imm);
+ (assembler_.get()->*f)(*reg, new_imm);
+ std::string base = fmt;
+
+ std::string reg_string = GetRegName<kRegView>(*reg);
+ size_t reg_index;
+ while ((reg_index = base.find(REG_TOKEN)) != std::string::npos) {
+ base.replace(reg_index, ConstexprStrLen(REG_TOKEN), reg_string);
+ }
+
+ size_t imm_index = base.find(IMM_TOKEN);
+ if (imm_index != std::string::npos) {
+ std::ostringstream sreg;
+ sreg << imm;
+ std::string imm_string = sreg.str();
+ base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
+ }
+
+ if (str.size() > 0) {
+ str += "\n";
+ }
+ str += base;
+ }
+ }
+ // Add a newline at the end.
+ str += "\n";
+ return str;
+ }
+
// Driver() assembles and compares the results. If the results are not equal and we have a
// disassembler, disassemble both and check whether they have the same mnemonics (in which case
// we just warn).
@@ -489,12 +650,12 @@ class AssemblerTest : public testing::Test {
bool result = CompareFiles(data_name + ".dis", as_name + ".dis");
- if (result) {
- std::remove(data_name.c_str());
- std::remove(as_name.c_str());
- std::remove((data_name + ".dis").c_str());
- std::remove((as_name + ".dis").c_str());
- }
+ // If you want to take a look at the differences between the ART assembler and GCC, comment
+ // out the removal code.
+ std::remove(data_name.c_str());
+ std::remove(as_name.c_str());
+ std::remove((data_name + ".dis").c_str());
+ std::remove((as_name + ".dis").c_str());
return result;
}
@@ -701,6 +862,13 @@ class AssemblerTest : public testing::Test {
return tmpnam_;
}
+ static constexpr size_t OBJDUMP_SECTION_LINE_MIN_TOKENS = 6;
+
+ 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* IMM_TOKEN = "{imm}";
+
std::unique_ptr<Ass> assembler_;
std::string resolved_assembler_cmd_;
@@ -709,7 +877,7 @@ class AssemblerTest : public testing::Test {
std::string android_data_;
- static constexpr size_t OBJDUMP_SECTION_LINE_MIN_TOKENS = 6;
+ DISALLOW_COPY_AND_ASSIGN(AssemblerTest);
};
} // namespace art