ARM: VIXL32: Improve codegen on CBZ/CBNZ
This patch improves codegen on CBZ/CBNZ.
CompareAndBranchIfZero/CompareAndBranchIfNonZero are introduced.
These two functions can generate CMP+Bcc or Cbz/Cbnz.
CMP+Bcc are generated by default.
If a hint is given (is_far_target = false) and rn and label
can all fit into Cbz/Cbnz, then Cbz/Cbnz is generated.
Prefer these two interfaces to using vixl32::MacroAssembler::Cbz/Cbnz.
In T32, Cbz/Cbnz instructions have following limitations:
- Far targets, which are over 126 bytes away, are not supported.
- Only low registers can be encoded.
- Backward branches are not supported.
Test: ART_USE_VIXL_ARM_BACKEND=true m test-art-host
Test: ART_USE_VIXL_ARM_BACKEND=true m test-art-target
Change-Id: I5d2ada19ea4f83dab78baf0cf78c72e99e58d946
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index b8d9a91..7aea616 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -1152,7 +1152,8 @@
void InstructionCodeGeneratorARMVIXL::GenerateTestAndBranch(HInstruction* instruction,
size_t condition_input_index,
vixl32::Label* true_target,
- vixl32::Label* false_target) {
+ vixl32::Label* false_target,
+ bool far_target) {
HInstruction* cond = instruction->InputAt(condition_input_index);
if (true_target == nullptr && false_target == nullptr) {
@@ -1188,9 +1189,13 @@
DCHECK(cond_val.IsRegister());
}
if (true_target == nullptr) {
- __ Cbz(InputRegisterAt(instruction, condition_input_index), false_target);
+ __ CompareAndBranchIfZero(InputRegisterAt(instruction, condition_input_index),
+ false_target,
+ far_target);
} else {
- __ Cbnz(InputRegisterAt(instruction, condition_input_index), true_target);
+ __ CompareAndBranchIfNonZero(InputRegisterAt(instruction, condition_input_index),
+ true_target,
+ far_target);
}
} else {
// Condition has not been materialized. Use its inputs as the comparison and
@@ -1285,7 +1290,8 @@
GenerateTestAndBranch(select,
/* condition_input_index */ 2,
/* true_target */ nullptr,
- &false_target);
+ &false_target,
+ /* far_target */ false);
codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
__ Bind(&false_target);
}
@@ -2775,7 +2781,7 @@
case Primitive::kPrimShort:
case Primitive::kPrimInt: {
if (value.IsRegister()) {
- __ Cbz(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
+ __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
} else {
DCHECK(value.IsConstant()) << value;
if (value.GetConstant()->AsIntConstant()->GetValue() == 0) {
@@ -3441,7 +3447,7 @@
__ Ldrexd(temp1, temp2, addr);
codegen_->MaybeRecordImplicitNullCheck(instruction);
__ Strexd(temp1, value_lo, value_hi, addr);
- __ Cbnz(temp1, &fail);
+ __ CompareAndBranchIfNonZero(temp1, &fail);
}
void LocationsBuilderARMVIXL::HandleFieldSet(
@@ -3957,7 +3963,7 @@
NullCheckSlowPathARMVIXL* slow_path =
new (GetGraph()->GetArena()) NullCheckSlowPathARMVIXL(instruction);
AddSlowPath(slow_path);
- __ Cbz(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
+ __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
}
void InstructionCodeGeneratorARMVIXL::VisitNullCheck(HNullCheck* instruction) {
@@ -4413,7 +4419,7 @@
codegen_->AddSlowPath(slow_path);
if (instruction->GetValueCanBeNull()) {
vixl32::Label non_zero;
- __ Cbnz(value, &non_zero);
+ __ CompareAndBranchIfNonZero(value, &non_zero);
if (index.IsConstant()) {
size_t offset =
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
@@ -4461,7 +4467,7 @@
GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
// If heap poisoning is enabled, no need to unpoison
// `temp1`, as we are comparing against null below.
- __ Cbnz(temp1, slow_path->GetEntryLabel());
+ __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel());
__ Bind(&do_put);
} else {
__ B(ne, slow_path->GetEntryLabel());
@@ -4638,7 +4644,7 @@
bool can_be_null) {
vixl32::Label is_null;
if (can_be_null) {
- __ Cbz(value, &is_null);
+ __ CompareAndBranchIfZero(value, &is_null);
}
GetAssembler()->LoadFromOffset(
kLoadWord, card, tr, Thread::CardTableOffset<kArmPointerSize>().Int32Value());
@@ -4697,10 +4703,10 @@
GetAssembler()->LoadFromOffset(
kLoadUnsignedHalfword, temp, tr, Thread::ThreadFlagsOffset<kArmPointerSize>().Int32Value());
if (successor == nullptr) {
- __ Cbnz(temp, slow_path->GetEntryLabel());
+ __ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel());
__ Bind(slow_path->GetReturnLabel());
} else {
- __ Cbz(temp, codegen_->GetLabelOf(successor));
+ __ CompareAndBranchIfZero(temp, codegen_->GetLabelOf(successor));
__ B(slow_path->GetEntryLabel());
}
}
@@ -5015,7 +5021,7 @@
cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
codegen_->AddSlowPath(slow_path);
if (generate_null_check) {
- __ Cbz(out, slow_path->GetEntryLabel());
+ __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
}
if (cls->MustGenerateClinitCheck()) {
GenerateClassInitializationCheck(slow_path, out);
@@ -5206,7 +5212,7 @@
// Return 0 if `obj` is null.
// avoid null check if we know obj is not null.
if (instruction->MustDoNullCheck()) {
- __ Cbz(obj, &zero);
+ __ CompareAndBranchIfZero(obj, &zero, /* far_target */ false);
}
switch (type_check_kind) {
@@ -5239,7 +5245,7 @@
// /* HeapReference<Class> */ out = out->super_class_
GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc);
// If `out` is null, we use it for the result, and jump to `done`.
- __ Cbz(out, &done);
+ __ CompareAndBranchIfZero(out, &done, /* far_target */ false);
__ Cmp(out, cls);
__ B(ne, &loop);
__ Mov(out, 1);
@@ -5263,7 +5269,7 @@
__ B(eq, &success);
// /* HeapReference<Class> */ out = out->super_class_
GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc);
- __ Cbnz(out, &loop);
+ __ CompareAndBranchIfNonZero(out, &loop);
// If `out` is null, we use it for the result, and jump to `done`.
__ B(&done);
__ Bind(&success);
@@ -5289,10 +5295,10 @@
// /* HeapReference<Class> */ out = out->component_type_
GenerateReferenceLoadOneRegister(instruction, out_loc, component_offset, maybe_temp_loc);
// If `out` is null, we use it for the result, and jump to `done`.
- __ Cbz(out, &done);
+ __ CompareAndBranchIfZero(out, &done, /* far_target */ false);
GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
- __ Cbnz(out, &zero);
+ __ CompareAndBranchIfNonZero(out, &zero, /* far_target */ false);
__ Bind(&exact_check);
__ Mov(out, 1);
__ B(&done);
@@ -5428,7 +5434,7 @@
vixl32::Label done;
// Avoid null check if we know obj is not null.
if (instruction->MustDoNullCheck()) {
- __ Cbz(obj, &done);
+ __ CompareAndBranchIfZero(obj, &done, /* far_target */ false);
}
// /* HeapReference<Class> */ temp = obj->klass_
@@ -5454,7 +5460,7 @@
// If the class reference currently in `temp` is null, jump to the slow path to throw the
// exception.
- __ Cbz(temp, type_check_slow_path->GetEntryLabel());
+ __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
// Otherwise, compare the classes.
__ Cmp(temp, cls);
@@ -5474,7 +5480,7 @@
// If the class reference currently in `temp` is null, jump to the slow path to throw the
// exception.
- __ Cbz(temp, type_check_slow_path->GetEntryLabel());
+ __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
// Otherwise, jump to the beginning of the loop.
__ B(&loop);
break;
@@ -5489,12 +5495,12 @@
// /* HeapReference<Class> */ temp = temp->component_type_
GenerateReferenceLoadOneRegister(instruction, temp_loc, component_offset, maybe_temp2_loc);
// If the component type is null, jump to the slow path to throw the exception.
- __ Cbz(temp, type_check_slow_path->GetEntryLabel());
+ __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
// Otherwise,the object is indeed an array, jump to label `check_non_primitive_component_type`
// to further check that this component type is not a primitive type.
GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset);
static_assert(Primitive::kPrimNot == 0, "Expected 0 for art::Primitive::kPrimNot");
- __ Cbnz(temp, type_check_slow_path->GetEntryLabel());
+ __ CompareAndBranchIfNonZero(temp, type_check_slow_path->GetEntryLabel());
break;
}