diff options
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/dex/quick/arm/int_arm.cc | 8 | ||||
| -rw-r--r-- | compiler/dex/quick/arm64/int_arm64.cc | 8 | ||||
| -rwxr-xr-x | compiler/dex/quick/x86/target_x86.cc | 8 | ||||
| -rw-r--r-- | compiler/image_writer.cc | 31 | ||||
| -rw-r--r-- | compiler/llvm/ir_builder.h | 2 | ||||
| -rw-r--r-- | compiler/utils/arm/assembler_arm.cc | 17 | ||||
| -rw-r--r-- | compiler/utils/arm/assembler_arm32.cc | 2 | ||||
| -rw-r--r-- | compiler/utils/arm/assembler_arm32_test.cc | 705 | ||||
| -rw-r--r-- | compiler/utils/arm/assembler_arm_test.h | 545 | ||||
| -rw-r--r-- | compiler/utils/arm/assembler_thumb2.cc | 2 | ||||
| -rw-r--r-- | compiler/utils/arm64/assembler_arm64.cc | 2 | ||||
| -rw-r--r-- | compiler/utils/assembler_test.h | 79 | ||||
| -rw-r--r-- | compiler/utils/x86/assembler_x86.cc | 2 | ||||
| -rw-r--r-- | compiler/utils/x86_64/assembler_x86_64.cc | 2 |
14 files changed, 1261 insertions, 152 deletions
diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index 57544b5187..cab039bfd4 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -16,6 +16,7 @@ /* This file contains codegen for the Thumb2 ISA. */ +#include "arch/instruction_set_features.h" #include "arm_lir.h" #include "codegen_arm.h" #include "dex/quick/mir_to_lir-inl.h" @@ -1119,7 +1120,9 @@ LIR* ArmMir2Lir::OpDecAndBranch(ConditionCode c_code, RegStorage reg, LIR* targe } bool ArmMir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) { -#if ANDROID_SMP != 0 + if (!cu_->GetInstructionSetFeatures()->IsSmp()) { + return false; + } // Start off with using the last LIR as the barrier. If it is not enough, then we will generate one. LIR* barrier = last_lir_insn_; @@ -1149,9 +1152,6 @@ bool ArmMir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) { DCHECK(!barrier->flags.use_def_invalid); barrier->u.m.def_mask = &kEncodeAll; return ret; -#else - return false; -#endif } void ArmMir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) { diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc index dfdb76bbab..0e00698388 100644 --- a/compiler/dex/quick/arm64/int_arm64.cc +++ b/compiler/dex/quick/arm64/int_arm64.cc @@ -16,6 +16,7 @@ /* This file contains codegen for the Thumb2 ISA. */ +#include "arch/instruction_set_features.h" #include "arm64_lir.h" #include "codegen_arm64.h" #include "dex/quick/mir_to_lir-inl.h" @@ -978,7 +979,9 @@ LIR* Arm64Mir2Lir::OpDecAndBranch(ConditionCode c_code, RegStorage reg, LIR* tar } bool Arm64Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) { -#if ANDROID_SMP != 0 + if (!cu_->GetInstructionSetFeatures()->IsSmp()) { + return false; + } // Start off with using the last LIR as the barrier. If it is not enough, then we will generate one. LIR* barrier = last_lir_insn_; @@ -1014,9 +1017,6 @@ bool Arm64Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) { DCHECK(!barrier->flags.use_def_invalid); barrier->u.m.def_mask = &kEncodeAll; return ret; -#else - return false; -#endif } void Arm64Mir2Lir::GenIntToLong(RegLocation rl_dest, RegLocation rl_src) { diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc index f5f71132e4..ead31b37b6 100755 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -18,6 +18,7 @@ #include <inttypes.h> #include <string> +#include "arch/instruction_set_features.h" #include "backend_x86.h" #include "codegen_x86.h" #include "dex/compiler_internals.h" @@ -594,7 +595,9 @@ bool X86Mir2Lir::ProvidesFullMemoryBarrier(X86OpCode opcode) { } bool X86Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) { -#if ANDROID_SMP != 0 + if (!cu_->GetInstructionSetFeatures()->IsSmp()) { + return false; + } // Start off with using the last LIR as the barrier. If it is not enough, then we will update it. LIR* mem_barrier = last_lir_insn_; @@ -630,9 +633,6 @@ bool X86Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) { mem_barrier->u.m.def_mask = &kEncodeAll; } return ret; -#else - return false; -#endif } void X86Mir2Lir::CompilerInitializeRegAlloc() { diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index ef1bf81faa..b03727b068 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -327,23 +327,16 @@ class StringLengthComparator { Handle<mirror::ObjectArray<mirror::String>> strings_; }; -// If string a is a prefix of b or b is a prefix of a then they are considered equal. This -// enables us to find prefixes instead of exact matches. Otherwise we do a normal string -// comparison. The strings compared of the form <position, length> inside of the chars_ array. +// Normal string < comparison through the chars_ array. class SubstringComparator { public: explicit SubstringComparator(const std::vector<uint16_t>* const chars) : chars_(chars) { } bool operator()(const std::pair<size_t, size_t>& a, const std::pair<size_t, size_t>& b) { - size_t compare_length = std::min(a.second, b.second); - const uint16_t* ptr_a = &chars_->at(a.first); - const uint16_t* ptr_b = &chars_->at(b.first); - for (size_t i = 0; i < compare_length; ++i) { - if (ptr_a[i] != ptr_b[i]) { - return ptr_a[i] < ptr_b[i]; - } - } - return false; + return std::lexicographical_compare(chars_->begin() + a.first, + chars_->begin() + a.first + a.second, + chars_->begin() + b.first, + chars_->begin() + b.first + b.second); } private: @@ -399,11 +392,15 @@ void ImageWriter::ProcessStrings() { // Try to see if the string exists as a prefix of an existing string. size_t new_offset = 0; std::pair<size_t, size_t> new_string(num_chars - length, length); - auto it = existing_strings.find(new_string); + auto it = existing_strings.lower_bound(new_string); + bool is_prefix = false; if (it != existing_strings.end()) { - for (size_t j = 0; j < length; ++j) { - DCHECK_EQ(combined_chars[it->first + j], s->CharAt(j)); - } + CHECK_LE(length, it->second); + is_prefix = std::equal(combined_chars.begin() + it->first, + combined_chars.begin() + it->first + it->second, + combined_chars.begin() + new_string.first); + } + if (is_prefix) { // Shares a prefix, set the offset to where the new offset will be. new_offset = it->first; // Remove the added chars. @@ -425,7 +422,7 @@ void ImageWriter::ProcessStrings() { for (size_t i = 0; i < total_strings; ++i) { strings->GetWithoutChecks(i)->SetArray(array); } - VLOG(compiler) << "Total # image strings=" << total_strings << " combined length=" + LOG(INFO) << "Total # image strings=" << total_strings << " combined length=" << total_length << " prefix saved chars=" << prefix_saved_chars; ComputeEagerResolvedStrings(); } diff --git a/compiler/llvm/ir_builder.h b/compiler/llvm/ir_builder.h index 03498efcbb..990ba02d3d 100644 --- a/compiler/llvm/ir_builder.h +++ b/compiler/llvm/ir_builder.h @@ -101,10 +101,8 @@ class IRBuilder : public LLVMIRBuilder { // Extend memory barrier //-------------------------------------------------------------------------- void CreateMemoryBarrier(MemBarrierKind barrier_kind) { -#if ANDROID_SMP // TODO: select atomic ordering according to given barrier kind. CreateFence(::llvm::SequentiallyConsistent); -#endif } //-------------------------------------------------------------------------- diff --git a/compiler/utils/arm/assembler_arm.cc b/compiler/utils/arm/assembler_arm.cc index 591d461244..9c84bc1e37 100644 --- a/compiler/utils/arm/assembler_arm.cc +++ b/compiler/utils/arm/assembler_arm.cc @@ -92,16 +92,29 @@ uint32_t ShifterOperand::encodingArm() const { break; case kRegister: if (is_shift_) { + uint32_t shift_type; + switch (shift_) { + case arm::Shift::ROR: + shift_type = static_cast<uint32_t>(shift_); + CHECK_NE(immed_, 0U); + break; + case arm::Shift::RRX: + shift_type = static_cast<uint32_t>(arm::Shift::ROR); // Same encoding as ROR. + CHECK_EQ(immed_, 0U); + break; + default: + shift_type = static_cast<uint32_t>(shift_); + } // Shifted immediate or register. if (rs_ == kNoRegister) { // Immediate shift. return immed_ << kShiftImmShift | - static_cast<uint32_t>(shift_) << kShiftShift | + shift_type << kShiftShift | static_cast<uint32_t>(rm_); } else { // Register shift. return static_cast<uint32_t>(rs_) << kShiftRegisterShift | - static_cast<uint32_t>(shift_) << kShiftShift | (1 << 4) | + shift_type << kShiftShift | (1 << 4) | static_cast<uint32_t>(rm_); } } else { diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc index 39ebf6803c..a1594b02ac 100644 --- a/compiler/utils/arm/assembler_arm32.cc +++ b/compiler/utils/arm/assembler_arm32.cc @@ -1513,10 +1513,8 @@ void Arm32Assembler::MemoryBarrier(ManagedRegister mscratch) { void Arm32Assembler::dmb(DmbOptions flavor) { -#if ANDROID_SMP != 0 int32_t encoding = 0xf57ff05f; // dmb Emit(encoding | flavor); -#endif } diff --git a/compiler/utils/arm/assembler_arm32_test.cc b/compiler/utils/arm/assembler_arm32_test.cc index 277a9eb0fa..fe12a226c8 100644 --- a/compiler/utils/arm/assembler_arm32_test.cc +++ b/compiler/utils/arm/assembler_arm32_test.cc @@ -16,49 +16,208 @@ #include "assembler_arm32.h" +#include <functional> +#include <type_traits> + +#include "base/macros.h" #include "base/stl_util.h" -#include "utils/assembler_test.h" +#include "utils/arm/assembler_arm_test.h" namespace art { -class AssemblerArm32Test : public AssemblerTest<arm::Arm32Assembler, - arm::Register, arm::SRegister, - uint32_t> { +using std::placeholders::_1; +using std::placeholders::_2; +using std::placeholders::_3; +using std::placeholders::_4; +using std::placeholders::_5; + +// To speed up tests, don't use all register combinations. +static constexpr bool kUseSparseRegisterList = true; + +// To speed up tests, don't use all condition codes. +static constexpr bool kUseSparseConditionList = true; + +// To speed up tests, don't use all shift immediates. +static constexpr bool kUseSparseShiftImmediates = true; + +class AssemblerArm32Test : public AssemblerArmTest<arm::Arm32Assembler, + arm::Register, arm::SRegister, + uint32_t, arm::ShifterOperand, arm::Condition> { protected: std::string GetArchitectureString() OVERRIDE { return "arm"; } + std::string GetAssemblerParameters() OVERRIDE { + return " -march=armv7ve"; // Arm-v7a with virtualization extension (means we have sdiv). + } + + const char* GetAssemblyHeader() OVERRIDE { + return kArm32AssemblyHeader; + } + std::string GetDisassembleParameters() OVERRIDE { return " -D -bbinary -marm --no-show-raw-insn"; } void SetUpHelpers() OVERRIDE { if (registers_.size() == 0) { - registers_.insert(end(registers_), - { // NOLINT(whitespace/braces) - new arm::Register(arm::R0), - new arm::Register(arm::R1), - new arm::Register(arm::R2), - new arm::Register(arm::R3), - new arm::Register(arm::R4), - new arm::Register(arm::R5), - new arm::Register(arm::R6), - new arm::Register(arm::R7), - new arm::Register(arm::R8), - new arm::Register(arm::R9), - new arm::Register(arm::R10), - new arm::Register(arm::R11), - new arm::Register(arm::R12), - new arm::Register(arm::R13), - new arm::Register(arm::R14), - new arm::Register(arm::R15) - }); + if (kUseSparseRegisterList) { + registers_.insert(end(registers_), + { // NOLINT(whitespace/braces) + new arm::Register(arm::R0), + new arm::Register(arm::R1), + new arm::Register(arm::R4), + new arm::Register(arm::R8), + new arm::Register(arm::R11), + new arm::Register(arm::R12), + new arm::Register(arm::R13), + new arm::Register(arm::R14), + new arm::Register(arm::R15) + }); + } else { + registers_.insert(end(registers_), + { // NOLINT(whitespace/braces) + new arm::Register(arm::R0), + new arm::Register(arm::R1), + new arm::Register(arm::R2), + new arm::Register(arm::R3), + new arm::Register(arm::R4), + new arm::Register(arm::R5), + new arm::Register(arm::R6), + new arm::Register(arm::R7), + new arm::Register(arm::R8), + new arm::Register(arm::R9), + new arm::Register(arm::R10), + new arm::Register(arm::R11), + new arm::Register(arm::R12), + new arm::Register(arm::R13), + new arm::Register(arm::R14), + new arm::Register(arm::R15) + }); + } + } + + if (!kUseSparseConditionList) { + conditions_.push_back(arm::Condition::EQ); + conditions_.push_back(arm::Condition::NE); + conditions_.push_back(arm::Condition::CS); + conditions_.push_back(arm::Condition::CC); + conditions_.push_back(arm::Condition::MI); + conditions_.push_back(arm::Condition::PL); + conditions_.push_back(arm::Condition::VS); + conditions_.push_back(arm::Condition::VC); + conditions_.push_back(arm::Condition::HI); + conditions_.push_back(arm::Condition::LS); + conditions_.push_back(arm::Condition::GE); + conditions_.push_back(arm::Condition::LT); + conditions_.push_back(arm::Condition::GT); + conditions_.push_back(arm::Condition::LE); + conditions_.push_back(arm::Condition::AL); + } else { + conditions_.push_back(arm::Condition::EQ); + conditions_.push_back(arm::Condition::NE); + conditions_.push_back(arm::Condition::CC); + conditions_.push_back(arm::Condition::VC); + conditions_.push_back(arm::Condition::HI); + conditions_.push_back(arm::Condition::LT); + conditions_.push_back(arm::Condition::AL); + } + + shifter_operands_.push_back(arm::ShifterOperand(0)); + shifter_operands_.push_back(arm::ShifterOperand(1)); + shifter_operands_.push_back(arm::ShifterOperand(2)); + shifter_operands_.push_back(arm::ShifterOperand(3)); + shifter_operands_.push_back(arm::ShifterOperand(4)); + shifter_operands_.push_back(arm::ShifterOperand(5)); + shifter_operands_.push_back(arm::ShifterOperand(127)); + shifter_operands_.push_back(arm::ShifterOperand(128)); + shifter_operands_.push_back(arm::ShifterOperand(254)); + shifter_operands_.push_back(arm::ShifterOperand(255)); + + if (!kUseSparseRegisterList) { + shifter_operands_.push_back(arm::ShifterOperand(arm::R0)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R1)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R2)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R3)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R4)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R5)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R6)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R7)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R8)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R9)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R10)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R11)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R12)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R13)); + } else { + shifter_operands_.push_back(arm::ShifterOperand(arm::R0)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R1)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R4)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R8)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R11)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R12)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R13)); + } + + std::vector<arm::Shift> shifts { + arm::Shift::LSL, arm::Shift::LSR, arm::Shift::ASR, arm::Shift::ROR, arm::Shift::RRX + }; + + // ShifterOperands of form "reg shift-type imm." + for (arm::Shift shift : shifts) { + for (arm::Register* reg : registers_) { // Note: this will pick up the sparse set. + if (*reg == arm::R15) { // Skip PC. + continue; + } + if (shift != arm::Shift::RRX) { + if (!kUseSparseShiftImmediates) { + for (uint32_t imm = 1; imm < 32; ++imm) { + shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, imm)); + } + } else { + shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 1)); + shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 2)); + shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 3)); + shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 7)); + shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 15)); + shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 16)); + shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 30)); + shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 31)); + } + } else { + // RRX doesn't have an immediate. + shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 0)); + } + } + } + } + + std::vector<arm::ShifterOperand> CreateRegisterShifts(std::vector<arm::Register*>& base_regs, + int32_t shift_min, int32_t shift_max) { + std::vector<arm::ShifterOperand> res; + static constexpr arm::Shift kShifts[] = { arm::Shift::LSL, arm::Shift::LSR, arm::Shift::ASR, + arm::Shift::ROR }; + + for (arm::Shift shift : kShifts) { + for (arm::Register* reg : base_regs) { + // Take the min, the max, and three values in between. + res.push_back(arm::ShifterOperand(*reg, shift, shift_min)); + if (shift_min != shift_max) { + res.push_back(arm::ShifterOperand(*reg, shift, shift_max)); + int32_t middle = (shift_min + shift_max) / 2; + res.push_back(arm::ShifterOperand(*reg, shift, middle)); + res.push_back(arm::ShifterOperand(*reg, shift, middle - 1)); + res.push_back(arm::ShifterOperand(*reg, shift, middle + 1)); + } + } } + + return res; } void TearDown() OVERRIDE { - AssemblerTest::TearDown(); + AssemblerArmTest::TearDown(); STLDeleteElements(®isters_); } @@ -70,8 +229,280 @@ class AssemblerArm32Test : public AssemblerTest<arm::Arm32Assembler, return imm_value; } + std::vector<arm::Condition>& GetConditions() OVERRIDE { + return conditions_; + } + + std::string GetConditionString(arm::Condition c) OVERRIDE { + std::ostringstream oss; + oss << c; + return oss.str(); + } + + arm::Register GetPCRegister() OVERRIDE { + return arm::R15; + } + + std::vector<arm::ShifterOperand>& GetShiftOperands() OVERRIDE { + return shifter_operands_; + } + + std::string GetShiftString(arm::ShifterOperand sop) OVERRIDE { + std::ostringstream oss; + if (sop.IsShift()) { + // Not a rotate... + if (sop.GetShift() == arm::Shift::RRX) { + oss << sop.GetRegister() << ", " << sop.GetShift(); + } else { + oss << sop.GetRegister() << ", " << sop.GetShift() << " #" << sop.GetImmediate(); + } + } else if (sop.IsRegister()) { + oss << sop.GetRegister(); + } else { + CHECK(sop.IsImmediate()); + oss << "#" << sop.GetImmediate(); + } + return oss.str(); + } + + static const char* GetRegTokenFromDepth(int depth) { + switch (depth) { + case 0: + return Base::REG1_TOKEN; + case 1: + return Base::REG2_TOKEN; + case 2: + return REG3_TOKEN; + case 3: + return REG4_TOKEN; + default: + LOG(FATAL) << "Depth problem."; + UNREACHABLE(); + } + } + + void ExecuteAndPrint(std::function<void()> f, std::string fmt, std::ostringstream& oss) { + if (first_) { + first_ = false; + } else { + oss << "\n"; + } + oss << fmt; + + f(); + } + + void TemplateHelper(std::function<void(arm::Register)> f, int depth ATTRIBUTE_UNUSED, + bool without_pc, + std::string fmt, std::ostringstream& oss) { + std::vector<arm::Register*> registers = without_pc ? GetRegistersWithoutPC() : GetRegisters(); + for (auto reg : registers) { + std::string after_reg = fmt; + + std::string reg_string = GetRegName<RegisterView::kUsePrimaryName>(*reg); + size_t reg_index; + const char* reg_token = GetRegTokenFromDepth(depth); + + while ((reg_index = after_reg.find(reg_token)) != std::string::npos) { + after_reg.replace(reg_index, strlen(reg_token), reg_string); + } + + ExecuteAndPrint([&] () { f(*reg); }, after_reg, oss); + } + } + + void TemplateHelper(std::function<void(const arm::ShifterOperand&)> f, int depth ATTRIBUTE_UNUSED, + bool without_pc ATTRIBUTE_UNUSED, std::string fmt, std::ostringstream& oss) { + for (const arm::ShifterOperand& shift : GetShiftOperands()) { + std::string after_shift = fmt; + + std::string shift_string = GetShiftString(shift); + size_t shift_index; + while ((shift_index = after_shift.find(SHIFT_TOKEN)) != std::string::npos) { + after_shift.replace(shift_index, ConstexprStrLen(SHIFT_TOKEN), shift_string); + } + + ExecuteAndPrint([&] () { f(shift); }, after_shift, oss); + } + } + + void TemplateHelper(std::function<void(arm::Condition)> f, int depth ATTRIBUTE_UNUSED, + bool without_pc ATTRIBUTE_UNUSED, std::string fmt, std::ostringstream& oss) { + for (arm::Condition c : GetConditions()) { + std::string after_cond = fmt; + + size_t cond_index = after_cond.find(COND_TOKEN); + if (cond_index != std::string::npos) { + after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c)); + } + + ExecuteAndPrint([&] () { f(c); }, after_cond, oss); + } + } + + template <typename... Args> + void TemplateHelper(std::function<void(arm::Register, Args...)> f, int depth, bool without_pc, + std::string fmt, std::ostringstream& oss) { + std::vector<arm::Register*> registers = without_pc ? GetRegistersWithoutPC() : GetRegisters(); + for (auto reg : registers) { + std::string after_reg = fmt; + + std::string reg_string = GetRegName<RegisterView::kUsePrimaryName>(*reg); + size_t reg_index; + const char* reg_token = GetRegTokenFromDepth(depth); + + while ((reg_index = after_reg.find(reg_token)) != std::string::npos) { + after_reg.replace(reg_index, strlen(reg_token), reg_string); + } + + auto lambda = [&] (Args... args) { f(*reg, args...); }; // NOLINT [readability/braces] [4] + TemplateHelper(std::function<void(Args...)>(lambda), depth + 1, without_pc, + after_reg, oss); + } + } + + template <typename... Args> + void TemplateHelper(std::function<void(const arm::ShifterOperand&, Args...)> f, int depth, + bool without_pc, std::string fmt, std::ostringstream& oss) { + for (const arm::ShifterOperand& shift : GetShiftOperands()) { + std::string after_shift = fmt; + + std::string shift_string = GetShiftString(shift); + size_t shift_index; + while ((shift_index = after_shift.find(SHIFT_TOKEN)) != std::string::npos) { + after_shift.replace(shift_index, ConstexprStrLen(SHIFT_TOKEN), shift_string); + } + + auto lambda = [&] (Args... args) { f(shift, args...); }; // NOLINT [readability/braces] [4] + TemplateHelper(std::function<void(Args...)>(lambda), depth, without_pc, + after_shift, oss); + } + } + + template <typename... Args> + void TemplateHelper(std::function<void(arm::Condition, Args...)> f, int depth, bool without_pc, + std::string fmt, std::ostringstream& oss) { + for (arm::Condition c : GetConditions()) { + std::string after_cond = fmt; + + size_t cond_index = after_cond.find(COND_TOKEN); + if (cond_index != std::string::npos) { + after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c)); + } + + auto lambda = [&] (Args... args) { f(c, args...); }; // NOLINT [readability/braces] [4] + TemplateHelper(std::function<void(Args...)>(lambda), depth, without_pc, + after_cond, oss); + } + } + + template <typename T1, typename T2> + std::function<void(T1, T2)> GetBoundFunction2(void (arm::Arm32Assembler::*f)(T1, T2)) { + return std::bind(f, GetAssembler(), _1, _2); + } + + template <typename T1, typename T2, typename T3> + std::function<void(T1, T2, T3)> GetBoundFunction3(void (arm::Arm32Assembler::*f)(T1, T2, T3)) { + return std::bind(f, GetAssembler(), _1, _2, _3); + } + + template <typename T1, typename T2, typename T3, typename T4> + std::function<void(T1, T2, T3, T4)> GetBoundFunction4( + void (arm::Arm32Assembler::*f)(T1, T2, T3, T4)) { + return std::bind(f, GetAssembler(), _1, _2, _3, _4); + } + + template <typename T1, typename T2, typename T3, typename T4, typename T5> + std::function<void(T1, T2, T3, T4, T5)> GetBoundFunction5( + void (arm::Arm32Assembler::*f)(T1, T2, T3, T4, T5)) { + return std::bind(f, GetAssembler(), _1, _2, _3, _4, _5); + } + + template <typename... Args> + void GenericTemplateHelper(std::function<void(Args...)> f, bool without_pc, + std::string fmt, std::string test_name) { + WarnOnCombinations(CountHelper<Args...>(without_pc)); + + std::ostringstream oss; + + TemplateHelper(f, 0, without_pc, fmt, oss); + + oss << "\n"; // Trailing newline. + + DriverStr(oss.str(), test_name); + } + + template <typename... Args> + void T2Helper(void (arm::Arm32Assembler::*f)(Args...), bool without_pc, std::string fmt, + std::string test_name) { + GenericTemplateHelper(GetBoundFunction2(f), without_pc, fmt, test_name); + } + + template <typename... Args> + void T3Helper(void (arm::Arm32Assembler::*f)(Args...), bool without_pc, std::string fmt, + std::string test_name) { + GenericTemplateHelper(GetBoundFunction3(f), without_pc, fmt, test_name); + } + + template <typename... Args> + void T4Helper(void (arm::Arm32Assembler::*f)(Args...), bool without_pc, std::string fmt, + std::string test_name) { + GenericTemplateHelper(GetBoundFunction4(f), without_pc, fmt, test_name); + } + + template <typename... Args> + void T5Helper(void (arm::Arm32Assembler::*f)(Args...), bool without_pc, std::string fmt, + std::string test_name) { + GenericTemplateHelper(GetBoundFunction5(f), without_pc, fmt, test_name); + } + private: + template <typename T> + size_t CountHelper(bool without_pc) { + size_t tmp; + if (std::is_same<T, arm::Register>::value) { + tmp = GetRegisters().size(); + if (without_pc) { + tmp--;; // Approximation... + } + return tmp; + } else if (std::is_same<T, const arm::ShifterOperand&>::value) { + return GetShiftOperands().size(); + } else if (std::is_same<T, arm::Condition>::value) { + return GetConditions().size(); + } else { + LOG(WARNING) << "Unknown type while counting."; + return 1; + } + } + + template <typename T1, typename T2, typename... Args> + size_t CountHelper(bool without_pc) { + size_t tmp; + if (std::is_same<T1, arm::Register>::value) { + tmp = GetRegisters().size(); + if (without_pc) { + tmp--;; // Approximation... + } + } else if (std::is_same<T1, const arm::ShifterOperand&>::value) { + tmp = GetShiftOperands().size(); + } else if (std::is_same<T1, arm::Condition>::value) { + tmp = GetConditions().size(); + } else { + LOG(WARNING) << "Unknown type while counting."; + tmp = 1; + } + size_t rec = CountHelper<T2, Args...>(without_pc); + return rec * tmp; + } + + bool first_; + + static constexpr const char* kArm32AssemblyHeader = ".arm\n"; + std::vector<arm::Register*> registers_; + std::vector<arm::Condition> conditions_; + std::vector<arm::ShifterOperand> shifter_operands_; }; @@ -79,77 +510,181 @@ TEST_F(AssemblerArm32Test, Toolchain) { EXPECT_TRUE(CheckTools()); } - TEST_F(AssemblerArm32Test, Sbfx) { - GetAssembler()->sbfx(arm::R0, arm::R1, 0, 1); - GetAssembler()->sbfx(arm::R0, arm::R1, 0, 8); - GetAssembler()->sbfx(arm::R0, arm::R1, 0, 16); - GetAssembler()->sbfx(arm::R0, arm::R1, 0, 32); + std::vector<std::pair<uint32_t, uint32_t>> immediates; + immediates.push_back({0, 1}); + immediates.push_back({0, 8}); + immediates.push_back({0, 15}); + immediates.push_back({0, 16}); + immediates.push_back({0, 31}); + immediates.push_back({0, 32}); + + immediates.push_back({1, 1}); + immediates.push_back({1, 15}); + immediates.push_back({1, 31}); + + immediates.push_back({8, 1}); + immediates.push_back({8, 15}); + immediates.push_back({8, 16}); + immediates.push_back({8, 24}); - GetAssembler()->sbfx(arm::R0, arm::R1, 8, 1); - GetAssembler()->sbfx(arm::R0, arm::R1, 8, 8); - GetAssembler()->sbfx(arm::R0, arm::R1, 8, 16); - GetAssembler()->sbfx(arm::R0, arm::R1, 8, 24); + immediates.push_back({31, 1}); - GetAssembler()->sbfx(arm::R0, arm::R1, 16, 1); - GetAssembler()->sbfx(arm::R0, arm::R1, 16, 8); - GetAssembler()->sbfx(arm::R0, arm::R1, 16, 16); + DriverStr(RepeatRRiiC(&arm::Arm32Assembler::sbfx, immediates, + "sbfx{cond} {reg1}, {reg2}, #{imm1}, #{imm2}"), "sbfx"); +} - GetAssembler()->sbfx(arm::R0, arm::R1, 31, 1); +TEST_F(AssemblerArm32Test, Ubfx) { + std::vector<std::pair<uint32_t, uint32_t>> immediates; + immediates.push_back({0, 1}); + immediates.push_back({0, 8}); + immediates.push_back({0, 15}); + immediates.push_back({0, 16}); + immediates.push_back({0, 31}); + immediates.push_back({0, 32}); - const char* expected = - "sbfx r0, r1, #0, #1\n" - "sbfx r0, r1, #0, #8\n" - "sbfx r0, r1, #0, #16\n" - "sbfx r0, r1, #0, #32\n" + immediates.push_back({1, 1}); + immediates.push_back({1, 15}); + immediates.push_back({1, 31}); - "sbfx r0, r1, #8, #1\n" - "sbfx r0, r1, #8, #8\n" - "sbfx r0, r1, #8, #16\n" - "sbfx r0, r1, #8, #24\n" + immediates.push_back({8, 1}); + immediates.push_back({8, 15}); + immediates.push_back({8, 16}); + immediates.push_back({8, 24}); - "sbfx r0, r1, #16, #1\n" - "sbfx r0, r1, #16, #8\n" - "sbfx r0, r1, #16, #16\n" + immediates.push_back({31, 1}); - "sbfx r0, r1, #31, #1\n"; - DriverStr(expected, "sbfx"); + DriverStr(RepeatRRiiC(&arm::Arm32Assembler::ubfx, immediates, + "ubfx{cond} {reg1}, {reg2}, #{imm1}, #{imm2}"), "ubfx"); } -TEST_F(AssemblerArm32Test, Ubfx) { - GetAssembler()->ubfx(arm::R0, arm::R1, 0, 1); - GetAssembler()->ubfx(arm::R0, arm::R1, 0, 8); - GetAssembler()->ubfx(arm::R0, arm::R1, 0, 16); - GetAssembler()->ubfx(arm::R0, arm::R1, 0, 32); - - GetAssembler()->ubfx(arm::R0, arm::R1, 8, 1); - GetAssembler()->ubfx(arm::R0, arm::R1, 8, 8); - GetAssembler()->ubfx(arm::R0, arm::R1, 8, 16); - GetAssembler()->ubfx(arm::R0, arm::R1, 8, 24); - - GetAssembler()->ubfx(arm::R0, arm::R1, 16, 1); - GetAssembler()->ubfx(arm::R0, arm::R1, 16, 8); - GetAssembler()->ubfx(arm::R0, arm::R1, 16, 16); - - GetAssembler()->ubfx(arm::R0, arm::R1, 31, 1); - - const char* expected = - "ubfx r0, r1, #0, #1\n" - "ubfx r0, r1, #0, #8\n" - "ubfx r0, r1, #0, #16\n" - "ubfx r0, r1, #0, #32\n" - - "ubfx r0, r1, #8, #1\n" - "ubfx r0, r1, #8, #8\n" - "ubfx r0, r1, #8, #16\n" - "ubfx r0, r1, #8, #24\n" - - "ubfx r0, r1, #16, #1\n" - "ubfx r0, r1, #16, #8\n" - "ubfx r0, r1, #16, #16\n" - - "ubfx r0, r1, #31, #1\n"; - DriverStr(expected, "ubfx"); +TEST_F(AssemblerArm32Test, Mul) { + T4Helper(&arm::Arm32Assembler::mul, true, "mul{cond} {reg1}, {reg2}, {reg3}", "mul"); +} + +TEST_F(AssemblerArm32Test, Mla) { + T5Helper(&arm::Arm32Assembler::mla, true, "mla{cond} {reg1}, {reg2}, {reg3}, {reg4}", "mul"); +} + +/* TODO: Needs support to filter out register combinations, as rdhi must not be equal to rdlo. +TEST_F(AssemblerArm32Test, Umull) { + T5Helper(&arm::Arm32Assembler::umull, true, "umull{cond} {reg1}, {reg2}, {reg3}, {reg4}", + "umull"); +} +*/ + +TEST_F(AssemblerArm32Test, Sdiv) { + T4Helper(&arm::Arm32Assembler::sdiv, true, "sdiv{cond} {reg1}, {reg2}, {reg3}", "sdiv"); +} + +TEST_F(AssemblerArm32Test, Udiv) { + T4Helper(&arm::Arm32Assembler::udiv, true, "udiv{cond} {reg1}, {reg2}, {reg3}", "udiv"); +} + +TEST_F(AssemblerArm32Test, And) { + T4Helper(&arm::Arm32Assembler::and_, true, "and{cond} {reg1}, {reg2}, {shift}", "and"); +} + +TEST_F(AssemblerArm32Test, Eor) { + T4Helper(&arm::Arm32Assembler::eor, true, "eor{cond} {reg1}, {reg2}, {shift}", "eor"); +} + +TEST_F(AssemblerArm32Test, Orr) { + T4Helper(&arm::Arm32Assembler::orr, true, "orr{cond} {reg1}, {reg2}, {shift}", "orr"); +} + +TEST_F(AssemblerArm32Test, Orrs) { + T4Helper(&arm::Arm32Assembler::orrs, true, "orr{cond}s {reg1}, {reg2}, {shift}", "orrs"); +} + +TEST_F(AssemblerArm32Test, Bic) { + T4Helper(&arm::Arm32Assembler::bic, true, "bic{cond} {reg1}, {reg2}, {shift}", "bic"); +} + +TEST_F(AssemblerArm32Test, Mov) { + T3Helper(&arm::Arm32Assembler::mov, true, "mov{cond} {reg1}, {shift}", "mov"); +} + +TEST_F(AssemblerArm32Test, Movs) { + T3Helper(&arm::Arm32Assembler::movs, true, "mov{cond}s {reg1}, {shift}", "movs"); +} + +TEST_F(AssemblerArm32Test, Mvn) { + T3Helper(&arm::Arm32Assembler::mvn, true, "mvn{cond} {reg1}, {shift}", "mvn"); +} + +TEST_F(AssemblerArm32Test, Mvns) { + T3Helper(&arm::Arm32Assembler::mvns, true, "mvn{cond}s {reg1}, {shift}", "mvns"); +} + +TEST_F(AssemblerArm32Test, Add) { + T4Helper(&arm::Arm32Assembler::add, false, "add{cond} {reg1}, {reg2}, {shift}", "add"); +} + +TEST_F(AssemblerArm32Test, Adds) { + T4Helper(&arm::Arm32Assembler::adds, false, "add{cond}s {reg1}, {reg2}, {shift}", "adds"); +} + +TEST_F(AssemblerArm32Test, Adc) { + T4Helper(&arm::Arm32Assembler::adc, false, "adc{cond} {reg1}, {reg2}, {shift}", "adc"); +} + +TEST_F(AssemblerArm32Test, Sub) { + T4Helper(&arm::Arm32Assembler::sub, false, "sub{cond} {reg1}, {reg2}, {shift}", "sub"); +} + +TEST_F(AssemblerArm32Test, Subs) { + T4Helper(&arm::Arm32Assembler::subs, false, "sub{cond}s {reg1}, {reg2}, {shift}", "subs"); +} + +TEST_F(AssemblerArm32Test, Sbc) { + T4Helper(&arm::Arm32Assembler::sbc, false, "sbc{cond} {reg1}, {reg2}, {shift}", "sbc"); +} + +TEST_F(AssemblerArm32Test, Rsb) { + T4Helper(&arm::Arm32Assembler::rsb, true, "rsb{cond} {reg1}, {reg2}, {shift}", "rsb"); +} + +TEST_F(AssemblerArm32Test, Rsbs) { + T4Helper(&arm::Arm32Assembler::rsbs, true, "rsb{cond}s {reg1}, {reg2}, {shift}", "rsbs"); +} + +TEST_F(AssemblerArm32Test, Rsc) { + T4Helper(&arm::Arm32Assembler::rsc, true, "rsc{cond} {reg1}, {reg2}, {shift}", "rsc"); +} + +/* TODO: Needs support to filter out register combinations, as reg1 must not be equal to reg3. +TEST_F(AssemblerArm32Test, Strex) { + RRRCWithoutPCHelper(&arm::Arm32Assembler::strex, "strex{cond} {reg1}, {reg2}, [{reg3}]", "strex"); +} +*/ + +TEST_F(AssemblerArm32Test, Clz) { + T3Helper(&arm::Arm32Assembler::clz, true, "clz{cond} {reg1}, {reg2}", "clz"); +} + +TEST_F(AssemblerArm32Test, Tst) { + T3Helper(&arm::Arm32Assembler::tst, true, "tst{cond} {reg1}, {shift}", "tst"); +} + +TEST_F(AssemblerArm32Test, Teq) { + T3Helper(&arm::Arm32Assembler::teq, true, "teq{cond} {reg1}, {shift}", "teq"); +} + +TEST_F(AssemblerArm32Test, Cmp) { + T3Helper(&arm::Arm32Assembler::cmp, true, "cmp{cond} {reg1}, {shift}", "cmp"); +} + +TEST_F(AssemblerArm32Test, Cmn) { + T3Helper(&arm::Arm32Assembler::cmn, true, "cmn{cond} {reg1}, {shift}", "cmn"); +} + +TEST_F(AssemblerArm32Test, Blx) { + T2Helper(&arm::Arm32Assembler::blx, true, "blx{cond} {reg1}", "blx"); +} + +TEST_F(AssemblerArm32Test, Bx) { + T2Helper(&arm::Arm32Assembler::bx, true, "bx{cond} {reg1}", "bx"); } } // namespace art diff --git a/compiler/utils/arm/assembler_arm_test.h b/compiler/utils/arm/assembler_arm_test.h new file mode 100644 index 0000000000..34475b2bfd --- /dev/null +++ b/compiler/utils/arm/assembler_arm_test.h @@ -0,0 +1,545 @@ +/* + * 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_ARM_ASSEMBLER_ARM_TEST_H_ +#define ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_TEST_H_ + +#include "utils/assembler_test.h" + +namespace art { + +template<typename Ass, typename Reg, typename FPReg, typename Imm, typename SOp, typename Cond> +class AssemblerArmTest : public AssemblerTest<Ass, Reg, FPReg, Imm> { + public: + typedef AssemblerTest<Ass, Reg, FPReg, Imm> Base; + + using Base::GetRegisters; + using Base::GetRegName; + using Base::CreateImmediate; + using Base::WarnOnCombinations; + + static constexpr int64_t kFullImmRangeThreshold = 32; + + virtual void FillImmediates(std::vector<Imm>& immediates, int64_t imm_min, int64_t imm_max) { + // Small range: do completely. + if (imm_max - imm_min <= kFullImmRangeThreshold) { + for (int64_t i = imm_min; i <= imm_max; ++i) { + immediates.push_back(CreateImmediate(i)); + } + } else { + immediates.push_back(CreateImmediate(imm_min)); + immediates.push_back(CreateImmediate(imm_max)); + if (imm_min < imm_max - 1) { + immediates.push_back(CreateImmediate(imm_min + 1)); + } + if (imm_min < imm_max - 2) { + immediates.push_back(CreateImmediate(imm_min + 2)); + } + if (imm_min < imm_max - 3) { + immediates.push_back(CreateImmediate(imm_max - 1)); + } + if (imm_min < imm_max - 4) { + immediates.push_back(CreateImmediate((imm_min + imm_max) / 2)); + } + } + } + + std::string RepeatRRIIC(void (Ass::*f)(Reg, Reg, Imm, Imm, Cond), + int64_t imm1_min, int64_t imm1_max, + int64_t imm2_min, int64_t imm2_max, + std::string fmt) { + return RepeatTemplatedRRIIC(f, GetRegisters(), GetRegisters(), + &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>, + &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>, + imm1_min, imm1_max, imm2_min, imm2_max, + fmt); + } + + template <typename Reg1, typename Reg2> + std::string RepeatTemplatedRRIIC(void (Ass::*f)(Reg1, Reg2, Imm, Imm, Cond), + const std::vector<Reg1*> reg1_registers, + const std::vector<Reg2*> reg2_registers, + std::string (AssemblerArmTest::*GetName1)(const Reg1&), + std::string (AssemblerArmTest::*GetName2)(const Reg2&), + int64_t imm1_min, int64_t imm1_max, + int64_t imm2_min, int64_t imm2_max, + std::string fmt) { + std::vector<Imm> immediates1; + FillImmediates(immediates1, imm1_min, imm1_max); + std::vector<Imm> immediates2; + FillImmediates(immediates2, imm2_min, imm2_max); + + std::vector<Cond>& cond = GetConditions(); + + WarnOnCombinations(cond.size() * immediates1.size() * immediates2.size() * + reg1_registers.size() * reg2_registers.size()); + + std::ostringstream oss; + bool first = true; + for (Cond& c : cond) { + std::string after_cond = fmt; + + size_t cond_index = after_cond.find(COND_TOKEN); + if (cond_index != std::string::npos) { + after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c)); + } + + for (Imm i : immediates1) { + std::string base = after_cond; + + size_t imm1_index = base.find(IMM1_TOKEN); + if (imm1_index != std::string::npos) { + std::ostringstream sreg; + sreg << i; + std::string imm_string = sreg.str(); + base.replace(imm1_index, ConstexprStrLen(IMM1_TOKEN), imm_string); + } + + for (Imm j : immediates2) { + std::string base2 = base; + + size_t imm2_index = base2.find(IMM2_TOKEN); + if (imm2_index != std::string::npos) { + std::ostringstream sreg; + sreg << j; + std::string imm_string = sreg.str(); + base2.replace(imm2_index, ConstexprStrLen(IMM2_TOKEN), imm_string); + } + + for (auto reg1 : reg1_registers) { + std::string base3 = base2; + + std::string reg1_string = (this->*GetName1)(*reg1); + size_t reg1_index; + while ((reg1_index = base3.find(Base::REG1_TOKEN)) != std::string::npos) { + base3.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string); + } + + for (auto reg2 : reg2_registers) { + std::string base4 = base3; + + std::string reg2_string = (this->*GetName2)(*reg2); + size_t reg2_index; + while ((reg2_index = base4.find(Base::REG2_TOKEN)) != std::string::npos) { + base4.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string); + } + + if (first) { + first = false; + } else { + oss << "\n"; + } + oss << base4; + + (Base::GetAssembler()->*f)(*reg1, *reg2, i, j, c); + } + } + } + } + } + // Add a newline at the end. + oss << "\n"; + + return oss.str(); + } + + std::string RepeatRRiiC(void (Ass::*f)(Reg, Reg, Imm, Imm, Cond), + std::vector<std::pair<Imm, Imm>>& immediates, + std::string fmt) { + return RepeatTemplatedRRiiC(f, GetRegisters(), GetRegisters(), + &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>, + &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>, + immediates, fmt); + } + + template <typename Reg1, typename Reg2> + std::string RepeatTemplatedRRiiC(void (Ass::*f)(Reg1, Reg2, Imm, Imm, Cond), + const std::vector<Reg1*> reg1_registers, + const std::vector<Reg2*> reg2_registers, + std::string (AssemblerArmTest::*GetName1)(const Reg1&), + std::string (AssemblerArmTest::*GetName2)(const Reg2&), + std::vector<std::pair<Imm, Imm>>& immediates, + std::string fmt) { + std::vector<Cond>& cond = GetConditions(); + + WarnOnCombinations(cond.size() * immediates.size() * reg1_registers.size() * + reg2_registers.size()); + + std::ostringstream oss; + bool first = true; + for (Cond& c : cond) { + std::string after_cond = fmt; + + size_t cond_index = after_cond.find(COND_TOKEN); + if (cond_index != std::string::npos) { + after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c)); + } + + for (std::pair<Imm, Imm>& pair : immediates) { + Imm i = pair.first; + Imm j = pair.second; + std::string after_imm1 = after_cond; + + size_t imm1_index = after_imm1.find(IMM1_TOKEN); + if (imm1_index != std::string::npos) { + std::ostringstream sreg; + sreg << i; + std::string imm_string = sreg.str(); + after_imm1.replace(imm1_index, ConstexprStrLen(IMM1_TOKEN), imm_string); + } + + std::string after_imm2 = after_imm1; + + size_t imm2_index = after_imm2.find(IMM2_TOKEN); + if (imm2_index != std::string::npos) { + std::ostringstream sreg; + sreg << j; + std::string imm_string = sreg.str(); + after_imm2.replace(imm2_index, ConstexprStrLen(IMM2_TOKEN), imm_string); + } + + for (auto reg1 : reg1_registers) { + std::string after_reg1 = after_imm2; + + std::string reg1_string = (this->*GetName1)(*reg1); + size_t reg1_index; + while ((reg1_index = after_reg1.find(Base::REG1_TOKEN)) != std::string::npos) { + after_reg1.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string); + } + + for (auto reg2 : reg2_registers) { + std::string after_reg2 = after_reg1; + + std::string reg2_string = (this->*GetName2)(*reg2); + size_t reg2_index; + while ((reg2_index = after_reg2.find(Base::REG2_TOKEN)) != std::string::npos) { + after_reg2.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string); + } + + if (first) { + first = false; + } else { + oss << "\n"; + } + oss << after_reg2; + + (Base::GetAssembler()->*f)(*reg1, *reg2, i, j, c); + } + } + } + } + // Add a newline at the end. + oss << "\n"; + + return oss.str(); + } + + std::string RepeatRRC(void (Ass::*f)(Reg, Reg, Cond), std::string fmt) { + return RepeatTemplatedRRC(f, GetRegisters(), GetRegisters(), GetConditions(), + &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>, + &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>, + fmt); + } + + template <typename Reg1, typename Reg2> + std::string RepeatTemplatedRRC(void (Ass::*f)(Reg1, Reg2, Cond), + const std::vector<Reg1*>& reg1_registers, + const std::vector<Reg2*>& reg2_registers, + const std::vector<Cond>& cond, + std::string (AssemblerArmTest::*GetName1)(const Reg1&), + std::string (AssemblerArmTest::*GetName2)(const Reg2&), + std::string fmt) { + WarnOnCombinations(cond.size() * reg1_registers.size() * reg2_registers.size()); + + std::ostringstream oss; + bool first = true; + for (const Cond& c : cond) { + std::string after_cond = fmt; + + size_t cond_index = after_cond.find(COND_TOKEN); + if (cond_index != std::string::npos) { + after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c)); + } + + for (auto reg1 : reg1_registers) { + std::string after_reg1 = after_cond; + + std::string reg1_string = (this->*GetName1)(*reg1); + size_t reg1_index; + while ((reg1_index = after_reg1.find(Base::REG1_TOKEN)) != std::string::npos) { + after_reg1.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string); + } + + for (auto reg2 : reg2_registers) { + std::string after_reg2 = after_reg1; + + std::string reg2_string = (this->*GetName2)(*reg2); + size_t reg2_index; + while ((reg2_index = after_reg2.find(Base::REG2_TOKEN)) != std::string::npos) { + after_reg2.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string); + } + + if (first) { + first = false; + } else { + oss << "\n"; + } + oss << after_reg2; + + (Base::GetAssembler()->*f)(*reg1, *reg2, c); + } + } + } + // Add a newline at the end. + oss << "\n"; + + return oss.str(); + } + + std::string RepeatRRRC(void (Ass::*f)(Reg, Reg, Reg, Cond), std::string fmt) { + return RepeatTemplatedRRRC(f, GetRegisters(), GetRegisters(), GetRegisters(), GetConditions(), + &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>, + &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>, + &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>, + fmt); + } + + template <typename Reg1, typename Reg2, typename Reg3> + std::string RepeatTemplatedRRRC(void (Ass::*f)(Reg1, Reg2, Reg3, Cond), + const std::vector<Reg1*>& reg1_registers, + const std::vector<Reg2*>& reg2_registers, + const std::vector<Reg3*>& reg3_registers, + const std::vector<Cond>& cond, + std::string (AssemblerArmTest::*GetName1)(const Reg1&), + std::string (AssemblerArmTest::*GetName2)(const Reg2&), + std::string (AssemblerArmTest::*GetName3)(const Reg3&), + std::string fmt) { + WarnOnCombinations(cond.size() * reg1_registers.size() * reg2_registers.size() * + reg3_registers.size()); + + std::ostringstream oss; + bool first = true; + for (const Cond& c : cond) { + std::string after_cond = fmt; + + size_t cond_index = after_cond.find(COND_TOKEN); + if (cond_index != std::string::npos) { + after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c)); + } + + for (auto reg1 : reg1_registers) { + std::string after_reg1 = after_cond; + + std::string reg1_string = (this->*GetName1)(*reg1); + size_t reg1_index; + while ((reg1_index = after_reg1.find(Base::REG1_TOKEN)) != std::string::npos) { + after_reg1.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string); + } + + for (auto reg2 : reg2_registers) { + std::string after_reg2 = after_reg1; + + std::string reg2_string = (this->*GetName2)(*reg2); + size_t reg2_index; + while ((reg2_index = after_reg2.find(Base::REG2_TOKEN)) != std::string::npos) { + after_reg2.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string); + } + + for (auto reg3 : reg3_registers) { + std::string after_reg3 = after_reg2; + + std::string reg3_string = (this->*GetName3)(*reg3); + size_t reg3_index; + while ((reg3_index = after_reg3.find(REG3_TOKEN)) != std::string::npos) { + after_reg3.replace(reg3_index, ConstexprStrLen(REG3_TOKEN), reg3_string); + } + + if (first) { + first = false; + } else { + oss << "\n"; + } + oss << after_reg3; + + (Base::GetAssembler()->*f)(*reg1, *reg2, *reg3, c); + } + } + } + } + // Add a newline at the end. + oss << "\n"; + + return oss.str(); + } + + template <typename RegT> + std::string RepeatTemplatedRSC(void (Ass::*f)(RegT, SOp, Cond), + const std::vector<RegT*>& registers, + const std::vector<SOp>& shifts, + const std::vector<Cond>& cond, + std::string (AssemblerArmTest::*GetName)(const RegT&), + std::string fmt) { + WarnOnCombinations(cond.size() * registers.size() * shifts.size()); + + std::ostringstream oss; + bool first = true; + for (const Cond& c : cond) { + std::string after_cond = fmt; + + size_t cond_index = after_cond.find(COND_TOKEN); + if (cond_index != std::string::npos) { + after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c)); + } + + for (const SOp& shift : shifts) { + std::string after_shift = after_cond; + + std::string shift_string = GetShiftString(shift); + size_t shift_index; + while ((shift_index = after_shift.find(Base::SHIFT_TOKEN)) != std::string::npos) { + after_shift.replace(shift_index, ConstexprStrLen(Base::SHIFT_TOKEN), shift_string); + } + + for (auto reg : registers) { + std::string after_reg = after_shift; + + std::string reg_string = (this->*GetName)(*reg); + size_t reg_index; + while ((reg_index = after_reg.find(Base::REG_TOKEN)) != std::string::npos) { + after_reg.replace(reg_index, ConstexprStrLen(Base::REG_TOKEN), reg_string); + } + + if (first) { + first = false; + } else { + oss << "\n"; + } + oss << after_reg; + + (Base::GetAssembler()->*f)(*reg, shift, c); + } + } + } + // Add a newline at the end. + oss << "\n"; + + return oss.str(); + } + + template <typename Reg1, typename Reg2> + std::string RepeatTemplatedRRSC(void (Ass::*f)(Reg1, Reg2, const SOp&, Cond), + const std::vector<Reg1*>& reg1_registers, + const std::vector<Reg2*>& reg2_registers, + const std::vector<SOp>& shifts, + const std::vector<Cond>& cond, + std::string (AssemblerArmTest::*GetName1)(const Reg1&), + std::string (AssemblerArmTest::*GetName2)(const Reg2&), + std::string fmt) { + WarnOnCombinations(cond.size() * reg1_registers.size() * reg2_registers.size() * shifts.size()); + + std::ostringstream oss; + bool first = true; + for (const Cond& c : cond) { + std::string after_cond = fmt; + + size_t cond_index = after_cond.find(COND_TOKEN); + if (cond_index != std::string::npos) { + after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c)); + } + + for (const SOp& shift : shifts) { + std::string after_shift = after_cond; + + std::string shift_string = GetShiftString(shift); + size_t shift_index; + while ((shift_index = after_shift.find(SHIFT_TOKEN)) != std::string::npos) { + after_shift.replace(shift_index, ConstexprStrLen(SHIFT_TOKEN), shift_string); + } + + for (auto reg1 : reg1_registers) { + std::string after_reg1 = after_shift; + + std::string reg1_string = (this->*GetName1)(*reg1); + size_t reg1_index; + while ((reg1_index = after_reg1.find(Base::REG1_TOKEN)) != std::string::npos) { + after_reg1.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string); + } + + for (auto reg2 : reg2_registers) { + std::string after_reg2 = after_reg1; + + std::string reg2_string = (this->*GetName2)(*reg2); + size_t reg2_index; + while ((reg2_index = after_reg2.find(Base::REG2_TOKEN)) != std::string::npos) { + after_reg2.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string); + } + + if (first) { + first = false; + } else { + oss << "\n"; + } + oss << after_reg2; + + (Base::GetAssembler()->*f)(*reg1, *reg2, shift, c); + } + } + } + } + // Add a newline at the end. + oss << "\n"; + + return oss.str(); + } + + protected: + explicit AssemblerArmTest() {} + + virtual std::vector<Cond>& GetConditions() = 0; + virtual std::string GetConditionString(Cond c) = 0; + + virtual std::vector<SOp>& GetShiftOperands() = 0; + virtual std::string GetShiftString(SOp sop) = 0; + + virtual Reg GetPCRegister() = 0; + virtual std::vector<Reg*> GetRegistersWithoutPC() { + std::vector<Reg*> without_pc = GetRegisters(); + Reg pc_reg = GetPCRegister(); + + for (auto it = without_pc.begin(); it != without_pc.end(); ++it) { + if (**it == pc_reg) { + without_pc.erase(it); + break; + } + } + + return without_pc; + } + + static constexpr const char* IMM1_TOKEN = "{imm1}"; + static constexpr const char* IMM2_TOKEN = "{imm2}"; + static constexpr const char* REG3_TOKEN = "{reg3}"; + static constexpr const char* REG4_TOKEN = "{reg4}"; + static constexpr const char* COND_TOKEN = "{cond}"; + static constexpr const char* SHIFT_TOKEN = "{shift}"; + + private: + DISALLOW_COPY_AND_ASSIGN(AssemblerArmTest); +}; + +} // namespace art + +#endif // ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_TEST_H_ diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc index 3ab9b2ba03..a34920999e 100644 --- a/compiler/utils/arm/assembler_thumb2.cc +++ b/compiler/utils/arm/assembler_thumb2.cc @@ -2599,10 +2599,8 @@ void Thumb2Assembler::MemoryBarrier(ManagedRegister mscratch) { void Thumb2Assembler::dmb(DmbOptions flavor) { -#if ANDROID_SMP != 0 int32_t encoding = 0xf3bf8f50; // dmb in T1 encoding. Emit32(encoding | flavor); -#endif } diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc index 02011b87a0..390f2ea449 100644 --- a/compiler/utils/arm64/assembler_arm64.cc +++ b/compiler/utils/arm64/assembler_arm64.cc @@ -476,9 +476,7 @@ void Arm64Assembler::Copy(FrameOffset /*dst*/, Offset /*dest_offset*/, void Arm64Assembler::MemoryBarrier(ManagedRegister m_scratch ATTRIBUTE_UNUSED) { // TODO: Should we check that m_scratch is IP? - see arm. -#if ANDROID_SMP != 0 ___ Dmb(vixl::InnerShareable, vixl::BarrierAll); -#endif } void Arm64Assembler::SignExtend(ManagedRegister mreg, size_t size) { diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h index 9d3fa01a9d..1fadb916fc 100644 --- a/compiler/utils/assembler_test.h +++ b/compiler/utils/assembler_test.h @@ -38,14 +38,14 @@ constexpr size_t ConstexprStrLen(char const* str, size_t count = 0) { // temp directory. static std::string tmpnam_; +enum class RegisterView { // private + kUsePrimaryName, + kUseSecondaryName +}; + 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(); } @@ -159,6 +159,9 @@ class AssemblerTest : public testing::Test { 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); (assembler_.get()->*f)(new_imm); @@ -184,12 +187,12 @@ class AssemblerTest : public testing::Test { // This is intended to be run as a test. bool CheckTools() { - if (!FileExists(GetAssemblerCommand())) { + if (!FileExists(FindTool(GetAssemblerCmdName()))) { return false; } LOG(INFO) << "Chosen assembler command: " << GetAssemblerCommand(); - if (!FileExists(GetObjdumpCommand())) { + if (!FileExists(FindTool(GetObjdumpCmdName()))) { return false; } LOG(INFO) << "Chosen objdump command: " << GetObjdumpCommand(); @@ -197,7 +200,7 @@ class AssemblerTest : public testing::Test { // Disassembly is optional. std::string disassembler = GetDisassembleCommand(); if (disassembler.length() != 0) { - if (!FileExists(disassembler)) { + if (!FileExists(FindTool(GetDisassembleCmdName()))) { return false; } LOG(INFO) << "Chosen disassemble command: " << GetDisassembleCommand(); @@ -271,7 +274,7 @@ class AssemblerTest : public testing::Test { resolved_assembler_cmd_ = line + GetAssemblerParameters(); - return line; + return resolved_assembler_cmd_; } // Get the name of the objdump, e.g., "objdump" by default. @@ -298,7 +301,7 @@ class AssemblerTest : public testing::Test { resolved_objdump_cmd_ = line + GetObjdumpParameters(); - return line; + return resolved_objdump_cmd_; } // Get the name of the objdump, e.g., "objdump" by default. @@ -324,7 +327,7 @@ class AssemblerTest : public testing::Test { resolved_disassemble_cmd_ = line + GetDisassembleParameters(); - return line; + return resolved_disassemble_cmd_; } // Create a couple of immediate values up to the number of bytes given. @@ -406,6 +409,8 @@ class AssemblerTest : public testing::Test { std::string (AssemblerTest::*GetName1)(const Reg1&), std::string (AssemblerTest::*GetName2)(const Reg2&), std::string fmt) { + WarnOnCombinations(reg1_registers.size() * reg2_registers.size()); + std::string str; for (auto reg1 : reg1_registers) { for (auto reg2 : reg2_registers) { @@ -435,7 +440,6 @@ class AssemblerTest : public testing::Test { return str; } - private: template <RegisterView kRegView> std::string GetRegName(const Reg& reg) { std::ostringstream sreg; @@ -457,12 +461,32 @@ class AssemblerTest : public testing::Test { return sreg.str(); } + // If the assembly file needs a header, return it in a sub-class. + virtual const char* GetAssemblyHeader() { + return nullptr; + } + + void WarnOnCombinations(size_t count) { + if (count > kWarnManyCombinationsThreshold) { + GTEST_LOG_(WARNING) << "Many combinations (" << count << "), test generation might be slow."; + } + } + + 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}"; + + private: 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); + + WarnOnCombinations(registers.size() * imms.size()); + for (auto reg : registers) { for (int64_t imm : imms) { Imm new_imm = CreateImmediate(imm); @@ -547,7 +571,7 @@ class AssemblerTest : public testing::Test { // Compile the assembly file from_file to a binary file to_file. Returns true on success. bool Assemble(const char* from_file, const char* to_file, std::string* error_msg) { - bool have_assembler = FileExists(GetAssemblerCommand()); + bool have_assembler = FileExists(FindTool(GetAssemblerCmdName())); EXPECT_TRUE(have_assembler) << "Cannot find assembler:" << GetAssemblerCommand(); if (!have_assembler) { return false; @@ -569,13 +593,20 @@ class AssemblerTest : public testing::Test { args.push_back("-c"); args.push_back(cmd); - return Exec(args, error_msg); + bool success = Exec(args, error_msg); + if (!success) { + LOG(INFO) << "Assembler command line:"; + for (std::string arg : args) { + LOG(INFO) << arg; + } + } + return success; } // Runs objdump -h on the binary file and extracts the first line with .text. // Returns "" on failure. std::string Objdump(std::string file) { - bool have_objdump = FileExists(GetObjdumpCommand()); + bool have_objdump = FileExists(FindTool(GetObjdumpCmdName())); EXPECT_TRUE(have_objdump) << "Cannot find objdump: " << GetObjdumpCommand(); if (!have_objdump) { return ""; @@ -652,10 +683,10 @@ class AssemblerTest : public testing::Test { // 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()); +// 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; } @@ -714,6 +745,10 @@ class AssemblerTest : public testing::Test { // TODO: Lots of error checking. std::ofstream s_out(res->base_name + ".S"); + const char* header = GetAssemblyHeader(); + if (header != nullptr) { + s_out << header; + } s_out << assembly_code; s_out.close(); @@ -862,13 +897,9 @@ class AssemblerTest : public testing::Test { return tmpnam_; } + static constexpr size_t kWarnManyCombinationsThreshold = 500; 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_; diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc index 8ebb40e338..afa4a3b958 100644 --- a/compiler/utils/x86/assembler_x86.cc +++ b/compiler/utils/x86/assembler_x86.cc @@ -1830,9 +1830,7 @@ void X86Assembler::Copy(FrameOffset dest, Offset dest_offset, FrameOffset src, O } void X86Assembler::MemoryBarrier(ManagedRegister) { -#if ANDROID_SMP != 0 mfence(); -#endif } void X86Assembler::CreateHandleScopeEntry(ManagedRegister mout_reg, diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index 2bb2ed8c9c..8c428f455e 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -2371,9 +2371,7 @@ void X86_64Assembler::Copy(FrameOffset dest, Offset dest_offset, FrameOffset src } void X86_64Assembler::MemoryBarrier(ManagedRegister) { -#if ANDROID_SMP != 0 mfence(); -#endif } void X86_64Assembler::CreateHandleScopeEntry(ManagedRegister mout_reg, |