Revert "Revert "Introduce a NearLabel in thumb2.""
This reverts commit 1f277e3cef6c33cd35e91123978491d83338d2ad.
- Fix CompareAndBranch to not use cbz/cbnz with high registers.
- Add a test for CompareAndBranch with the *inc file, as the
other assembler test infrastructure does not handle labels.
Change-Id: If552bf1112b96caa3b9bb6c73c4b40bb90a33db7
diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h
index dee8287..52a69ca 100644
--- a/compiler/utils/arm/assembler_arm.h
+++ b/compiler/utils/arm/assembler_arm.h
@@ -33,6 +33,16 @@
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 @@
// 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 @@
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 55ec7b4..4564767 100644
--- a/compiler/utils/arm/assembler_arm32.h
+++ b/compiler/utils/arm/assembler_arm32.h
@@ -201,8 +201,8 @@
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 75f2b77..4ff3aeb 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -671,11 +671,17 @@
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 @@
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::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 @@
}
} 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 @@
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 @@
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::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::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 90d489f..9f02e56 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -239,6 +239,7 @@
// 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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
}
// 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 3341151..40eb15b 100644
--- a/compiler/utils/assembler_test_base.h
+++ b/compiler/utils/assembler_test_base.h
@@ -215,9 +215,9 @@
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 7738627..1a2c9a9 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -1338,6 +1338,24 @@
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 3d03234..841d6a0 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -4822,6 +4822,16 @@
" 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 @@
test_results["LoadStoreRegOffset"] = LoadStoreRegOffsetResults;
test_results["LoadStoreLiteral"] = LoadStoreLiteralResults;
test_results["LoadStoreLimits"] = LoadStoreLimitsResults;
+ test_results["CompareAndBranch"] = CompareAndBranchResults;
}