diff options
| -rw-r--r-- | compiler/optimizing/code_generator_arm.cc | 18 | ||||
| -rw-r--r-- | compiler/utils/arm/assembler_arm.h | 16 | ||||
| -rw-r--r-- | compiler/utils/arm/assembler_arm32.h | 4 | ||||
| -rw-r--r-- | compiler/utils/arm/assembler_thumb2.cc | 33 | ||||
| -rw-r--r-- | compiler/utils/arm/assembler_thumb2.h | 29 | ||||
| -rw-r--r-- | compiler/utils/assembler_test_base.h | 4 | ||||
| -rw-r--r-- | compiler/utils/assembler_thumb_test.cc | 18 | ||||
| -rw-r--r-- | compiler/utils/assembler_thumb_test_expected.cc.inc | 11 |
8 files changed, 110 insertions, 23 deletions
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 1c76630efe..09ed9c700e 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -2883,7 +2883,7 @@ void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) { Location left = locations->InAt(0); Location right = locations->InAt(1); - Label less, greater, done; + NearLabel less, greater, done; Primitive::Type type = compare->InputAt(0)->GetType(); switch (type) { case Primitive::kPrimLong: { @@ -2979,7 +2979,7 @@ void InstructionCodeGeneratorARM::GenerateWideAtomicStore(Register addr, Register temp1, Register temp2, HInstruction* instruction) { - Label fail; + NearLabel fail; if (offset != 0) { __ LoadImmediate(temp1, offset); __ add(IP, addr, ShifterOperand(temp1)); @@ -3659,7 +3659,7 @@ void CodeGeneratorARM::MarkGCCard(Register temp, Register object, Register value, bool can_be_null) { - Label is_null; + NearLabel is_null; if (can_be_null) { __ CompareAndBranchIfZero(value, &is_null); } @@ -4081,14 +4081,13 @@ void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { Register cls = locations->InAt(1).AsRegister<Register>(); Register out = locations->Out().AsRegister<Register>(); uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); - Label done, zero; + NearLabel done, zero; SlowPathCodeARM* slow_path = nullptr; // Return 0 if `obj` is null. // avoid null check if we know obj is not null. if (instruction->MustDoNullCheck()) { - __ cmp(obj, ShifterOperand(0)); - __ b(&zero, EQ); + __ CompareAndBranchIfZero(obj, &zero); } // Compare the class of `obj` with `cls`. __ LoadFromOffset(kLoadWord, out, obj, class_offset); @@ -4139,16 +4138,19 @@ void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { instruction, locations->InAt(1), locations->GetTemp(0), instruction->GetDexPc()); codegen_->AddSlowPath(slow_path); + NearLabel done; // avoid null check if we know obj is not null. if (instruction->MustDoNullCheck()) { - __ cmp(obj, ShifterOperand(0)); - __ b(slow_path->GetExitLabel(), EQ); + __ CompareAndBranchIfZero(obj, &done); } // Compare the class of `obj` with `cls`. __ LoadFromOffset(kLoadWord, temp, obj, class_offset); __ cmp(temp, ShifterOperand(cls)); __ b(slow_path->GetEntryLabel(), NE); __ Bind(slow_path->GetExitLabel()); + if (instruction->MustDoNullCheck()) { + __ Bind(&done); + } } void LocationsBuilderARM::VisitMonitorOperation(HMonitorOperation* instruction) { diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h index dee8287e67..52a69ca487 100644 --- a/compiler/utils/arm/assembler_arm.h +++ b/compiler/utils/arm/assembler_arm.h @@ -33,6 +33,16 @@ namespace arm { class Arm32Assembler; class Thumb2Assembler; +// This class indicates that the label and its uses +// will fall into a range that is encodable in 16bits on thumb2. +class NearLabel : public Label { + public: + NearLabel() {} + + private: + DISALLOW_COPY_AND_ASSIGN(NearLabel); +}; + class ShifterOperand { public: ShifterOperand() : type_(kUnknown), rm_(kNoRegister), rs_(kNoRegister), @@ -519,6 +529,9 @@ class ArmAssembler : public Assembler { // Branch instructions. virtual void b(Label* label, Condition cond = AL) = 0; + virtual void b(NearLabel* label, Condition cond = AL) { + b(static_cast<Label*>(label), cond); + } virtual void bl(Label* label, Condition cond = AL) = 0; virtual void blx(Register rm, Condition cond = AL) = 0; virtual void bx(Register rm, Condition cond = AL) = 0; @@ -654,6 +667,9 @@ class ArmAssembler : public Assembler { virtual void Bind(Label* label) = 0; virtual void CompareAndBranchIfZero(Register r, Label* label) = 0; + virtual void CompareAndBranchIfZero(Register r, NearLabel* label) { + CompareAndBranchIfZero(r, static_cast<Label*>(label)); + } virtual void CompareAndBranchIfNonZero(Register r, Label* label) = 0; // diff --git a/compiler/utils/arm/assembler_arm32.h b/compiler/utils/arm/assembler_arm32.h index 55ec7b46d8..45647675e8 100644 --- a/compiler/utils/arm/assembler_arm32.h +++ b/compiler/utils/arm/assembler_arm32.h @@ -201,8 +201,8 @@ class Arm32Assembler FINAL : public ArmAssembler { void vpopd(DRegister reg, int nregs, Condition cond = AL) OVERRIDE; // Branch instructions. - void b(Label* label, Condition cond = AL); - void bl(Label* label, Condition cond = AL); + void b(Label* label, Condition cond = AL) OVERRIDE; + void bl(Label* label, Condition cond = AL) OVERRIDE; void blx(Register rm, Condition cond = AL) OVERRIDE; void bx(Register rm, Condition cond = AL) OVERRIDE; void Lsl(Register rd, Register rm, uint32_t shift_imm, bool setcc = false, diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc index 75f2b77c96..4ff3aeb45b 100644 --- a/compiler/utils/arm/assembler_thumb2.cc +++ b/compiler/utils/arm/assembler_thumb2.cc @@ -671,11 +671,17 @@ void Thumb2Assembler::vcmpdz(DRegister dd, Condition cond) { EmitVFPddd(cond, B23 | B21 | B20 | B18 | B16 | B6, dd, D0, D0); } + void Thumb2Assembler::b(Label* label, Condition cond) { EmitBranch(cond, label, false, false); } +void Thumb2Assembler::b(NearLabel* label, Condition cond) { + EmitBranch(cond, label, false, false, /* is_near */ true); +} + + void Thumb2Assembler::bl(Label* label, Condition cond) { CheckCondition(cond); EmitBranch(cond, label, true, false); @@ -1369,6 +1375,7 @@ void Thumb2Assembler::Branch::Emit(AssemblerBuffer* buffer) const { uint16_t Thumb2Assembler::EmitCompareAndBranch(Register rn, uint16_t prev, bool n) { + CHECK(IsLowRegister(rn)); uint32_t location = buffer_.Size(); // This is always unresolved as it must be a forward branch. @@ -1613,7 +1620,7 @@ void Thumb2Assembler::EmitMultiMemOp(Condition cond, } -void Thumb2Assembler::EmitBranch(Condition cond, Label* label, bool link, bool x) { +void Thumb2Assembler::EmitBranch(Condition cond, Label* label, bool link, bool x, bool is_near) { uint32_t pc = buffer_.Size(); Branch::Type branch_type; if (cond == AL) { @@ -1644,8 +1651,8 @@ void Thumb2Assembler::EmitBranch(Condition cond, Label* label, bool link, bool x } } else { // Branch is to an unbound label. Emit space for it. - uint16_t branch_id = AddBranch(branch_type, pc, cond); // Unresolved branch. - if (!CanRelocateBranches() || force_32bit_) { + uint16_t branch_id = AddBranch(branch_type, pc, cond, is_near); // Unresolved branch. + if (force_32bit_ || (!CanRelocateBranches() && !is_near)) { Emit16(static_cast<uint16_t>(label->position_)); // Emit current label link. Emit16(0); // another 16 bits. } else { @@ -2199,6 +2206,9 @@ void Thumb2Assembler::cbz(Register rn, Label* label) { if (label->IsBound()) { LOG(FATAL) << "cbz can only be used to branch forwards"; UNREACHABLE(); + } else if (IsHighRegister(rn)) { + LOG(FATAL) << "cbz can only be used with low registers"; + UNREACHABLE(); } else { uint16_t branchid = EmitCompareAndBranch(rn, static_cast<uint16_t>(label->position_), false); label->LinkTo(branchid); @@ -2211,6 +2221,9 @@ void Thumb2Assembler::cbnz(Register rn, Label* label) { if (label->IsBound()) { LOG(FATAL) << "cbnz can only be used to branch forwards"; UNREACHABLE(); + } else if (IsHighRegister(rn)) { + LOG(FATAL) << "cbnz can only be used with low registers"; + UNREACHABLE(); } else { uint16_t branchid = EmitCompareAndBranch(rn, static_cast<uint16_t>(label->position_), true); label->LinkTo(branchid); @@ -2741,7 +2754,17 @@ void Thumb2Assembler::dmb(DmbOptions flavor) { void Thumb2Assembler::CompareAndBranchIfZero(Register r, Label* label) { - if (CanRelocateBranches()) { + if (CanRelocateBranches() && IsLowRegister(r)) { + cbz(r, label); + } else { + cmp(r, ShifterOperand(0)); + b(label, EQ); + } +} + + +void Thumb2Assembler::CompareAndBranchIfZero(Register r, NearLabel* label) { + if (IsLowRegister(r)) { cbz(r, label); } else { cmp(r, ShifterOperand(0)); @@ -2751,7 +2774,7 @@ void Thumb2Assembler::CompareAndBranchIfZero(Register r, Label* label) { void Thumb2Assembler::CompareAndBranchIfNonZero(Register r, Label* label) { - if (CanRelocateBranches()) { + if (CanRelocateBranches() && IsLowRegister(r)) { cbnz(r, label); } else { cmp(r, ShifterOperand(0)); diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h index 90d489fb02..9f02e56a74 100644 --- a/compiler/utils/arm/assembler_thumb2.h +++ b/compiler/utils/arm/assembler_thumb2.h @@ -239,6 +239,7 @@ class Thumb2Assembler FINAL : public ArmAssembler { // Branch instructions. void b(Label* label, Condition cond = AL); + void b(NearLabel* label, Condition cond = AL); void bl(Label* label, Condition cond = AL); void blx(Label* label); void blx(Register rm, Condition cond = AL) OVERRIDE; @@ -273,6 +274,7 @@ class Thumb2Assembler FINAL : public ArmAssembler { void Mov(Register rd, Register rm, Condition cond = AL) OVERRIDE; void CompareAndBranchIfZero(Register r, Label* label) OVERRIDE; + void CompareAndBranchIfZero(Register r, NearLabel* label) OVERRIDE; void CompareAndBranchIfNonZero(Register r, Label* label) OVERRIDE; // Memory barriers. @@ -431,7 +433,7 @@ class Thumb2Assembler FINAL : public ArmAssembler { void EmitVPushPop(uint32_t reg, int nregs, bool push, bool dbl, Condition cond); - void EmitBranch(Condition cond, Label* label, bool link, bool x); + void EmitBranch(Condition cond, Label* label, bool link, bool x, bool is_near = false); static int32_t EncodeBranchOffset(int32_t offset, int32_t inst); static int DecodeBranchOffset(int32_t inst); int32_t EncodeTstOffset(int offset, int32_t inst); @@ -559,6 +561,7 @@ class Thumb2Assembler FINAL : public ArmAssembler { // Resolve a branch when the target is known. If this causes the // size of the branch to change return true. Otherwise return false. bool Resolve(uint32_t target) { + uint32_t old_target = target_; target_ = target; if (assembler_->CanRelocateBranches()) { Size new_size = CalculateSize(); @@ -569,9 +572,12 @@ class Thumb2Assembler FINAL : public ArmAssembler { return false; } else { if (kIsDebugBuild) { - Size new_size = CalculateSize(); - // Check that the size has not increased. - DCHECK(!(new_size == k32Bit && size_ == k16Bit)); + if (old_target == kUnresolved) { + // Check that the size has not increased. + DCHECK(!(CalculateSize() == k32Bit && size_ == k16Bit)); + } else { + DCHECK(CalculateSize() == size_); + } } return false; } @@ -651,6 +657,10 @@ class Thumb2Assembler FINAL : public ArmAssembler { if (assembler_->IsForced32Bit() && (type_ == kUnconditional || type_ == kConditional)) { return k32Bit; } + if (IsCompareAndBranch()) { + // Compare and branch instructions can only be encoded on 16 bits. + return k16Bit; + } return assembler_->CanRelocateBranches() ? k16Bit : k32Bit; } // When the target is resolved, we know the best encoding for it. @@ -714,8 +724,15 @@ class Thumb2Assembler FINAL : public ArmAssembler { } // Add an unresolved branch and return its id. - uint16_t AddBranch(Branch::Type type, uint32_t location, Condition cond = AL) { - branches_.push_back(new Branch(this, type, location, cond)); + uint16_t AddBranch(Branch::Type type, + uint32_t location, + Condition cond = AL, + bool is_near = false) { + Branch* branch = new Branch(this, type, location, cond); + if (is_near) { + branch->ResetSize(Branch::k16Bit); + } + branches_.push_back(branch); return branches_.size() - 1; } diff --git a/compiler/utils/assembler_test_base.h b/compiler/utils/assembler_test_base.h index 3341151c8b..40eb15bbdc 100644 --- a/compiler/utils/assembler_test_base.h +++ b/compiler/utils/assembler_test_base.h @@ -215,9 +215,9 @@ class AssemblerTestInfrastructure { bool success = Exec(args, error_msg); if (!success) { - LOG(INFO) << "Assembler command line:"; + LOG(ERROR) << "Assembler command line:"; for (std::string arg : args) { - LOG(INFO) << arg; + LOG(ERROR) << arg; } } return success; diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc index 773862710d..1a2c9a9000 100644 --- a/compiler/utils/assembler_thumb_test.cc +++ b/compiler/utils/assembler_thumb_test.cc @@ -1338,6 +1338,24 @@ TEST(Thumb2AssemblerTest, LoadStoreLimits) { delete assembler; } +TEST(Thumb2AssemblerTest, CompareAndBranch) { + arm::Thumb2Assembler* assembler = static_cast<arm::Thumb2Assembler*>(Assembler::Create(kThumb2)); + + arm::NearLabel label; + __ CompareAndBranchIfZero(arm::R0, &label); + __ CompareAndBranchIfZero(arm::R11, &label); + __ CompareAndBranchIfNonZero(arm::R0, &label); + __ CompareAndBranchIfNonZero(arm::R11, &label); + __ Bind(&label); + + size_t cs = __ CodeSize(); + std::vector<uint8_t> managed_code(cs); + MemoryRegion code(&managed_code[0], managed_code.size()); + __ FinalizeInstructions(code); + dump(managed_code, "CompareAndBranch"); + delete assembler; +} + #undef __ } // namespace arm } // namespace art diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc index 3d03234e04..841d6a00c0 100644 --- a/compiler/utils/assembler_thumb_test_expected.cc.inc +++ b/compiler/utils/assembler_thumb_test_expected.cc.inc @@ -4822,6 +4822,16 @@ const char* LoadStoreLimitsResults[] = { " 30: f8a4 0040 strh.w r0, [r4, #64] ; 0x40\n", nullptr }; +const char* CompareAndBranchResults[] = { + " 0: b130 cbz r0, 10 <CompareAndBranch+0x10>\n", + " 2: f1bb 0f00 cmp.w fp, #0\n", + " 6: d003 beq.n 10 <CompareAndBranch+0x10>\n", + " 8: b910 cbnz r0, 10 <CompareAndBranch+0x10>\n", + " a: f1bb 0f00 cmp.w fp, #0\n", + " e: d1ff bne.n 10 <CompareAndBranch+0x10>\n", + nullptr +}; + std::map<std::string, const char**> test_results; void setup_results() { test_results["SimpleMov"] = SimpleMovResults; @@ -4869,4 +4879,5 @@ void setup_results() { test_results["LoadStoreRegOffset"] = LoadStoreRegOffsetResults; test_results["LoadStoreLiteral"] = LoadStoreLiteralResults; test_results["LoadStoreLimits"] = LoadStoreLimitsResults; + test_results["CompareAndBranch"] = CompareAndBranchResults; } |