ARM(64): Improve the code generated for HSelect
Test: m test-art-target-run-test-566-checker-codegen-select
Test: m test-art-target-run-test-570-checker-select
Change-Id: If0140892303490701782df9a818e6d8346bf3d6c
Signed-off-by: Anton Kirilov <anton.kirilov@linaro.org>
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 7b84ef8..e916fce 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -1375,10 +1375,332 @@
}
}
+static void GenerateVcmp(HInstruction* instruction, CodeGeneratorARM* codegen) {
+ Primitive::Type type = instruction->InputAt(0)->GetType();
+ Location lhs_loc = instruction->GetLocations()->InAt(0);
+ Location rhs_loc = instruction->GetLocations()->InAt(1);
+ if (rhs_loc.IsConstant()) {
+ // 0.0 is the only immediate that can be encoded directly in
+ // a VCMP instruction.
+ //
+ // Both the JLS (section 15.20.1) and the JVMS (section 6.5)
+ // specify that in a floating-point comparison, positive zero
+ // and negative zero are considered equal, so we can use the
+ // literal 0.0 for both cases here.
+ //
+ // Note however that some methods (Float.equal, Float.compare,
+ // Float.compareTo, Double.equal, Double.compare,
+ // Double.compareTo, Math.max, Math.min, StrictMath.max,
+ // StrictMath.min) consider 0.0 to be (strictly) greater than
+ // -0.0. So if we ever translate calls to these methods into a
+ // HCompare instruction, we must handle the -0.0 case with
+ // care here.
+ DCHECK(rhs_loc.GetConstant()->IsArithmeticZero());
+ if (type == Primitive::kPrimFloat) {
+ __ vcmpsz(lhs_loc.AsFpuRegister<SRegister>());
+ } else {
+ DCHECK_EQ(type, Primitive::kPrimDouble);
+ __ vcmpdz(FromLowSToD(lhs_loc.AsFpuRegisterPairLow<SRegister>()));
+ }
+ } else {
+ if (type == Primitive::kPrimFloat) {
+ __ vcmps(lhs_loc.AsFpuRegister<SRegister>(), rhs_loc.AsFpuRegister<SRegister>());
+ } else {
+ DCHECK_EQ(type, Primitive::kPrimDouble);
+ __ vcmpd(FromLowSToD(lhs_loc.AsFpuRegisterPairLow<SRegister>()),
+ FromLowSToD(rhs_loc.AsFpuRegisterPairLow<SRegister>()));
+ }
+ }
+}
+
+static Condition GenerateLongTestConstant(HCondition* condition,
+ bool invert,
+ CodeGeneratorARM* codegen) {
+ DCHECK_EQ(condition->GetLeft()->GetType(), Primitive::kPrimLong);
+
+ const LocationSummary* const locations = condition->GetLocations();
+ IfCondition cond = invert ? condition->GetOppositeCondition() : condition->GetCondition();
+ Condition ret = EQ;
+ const Location left = locations->InAt(0);
+ const Location right = locations->InAt(1);
+
+ DCHECK(right.IsConstant());
+
+ const Register left_high = left.AsRegisterPairHigh<Register>();
+ const Register left_low = left.AsRegisterPairLow<Register>();
+ int64_t value = right.GetConstant()->AsLongConstant()->GetValue();
+
+ switch (cond) {
+ case kCondEQ:
+ case kCondNE:
+ case kCondB:
+ case kCondBE:
+ case kCondA:
+ case kCondAE:
+ __ CmpConstant(left_high, High32Bits(value));
+ __ it(EQ);
+ __ cmp(left_low, ShifterOperand(Low32Bits(value)), EQ);
+ ret = ARMUnsignedCondition(cond);
+ break;
+ case kCondLE:
+ case kCondGT:
+ // Trivially true or false.
+ if (value == std::numeric_limits<int64_t>::max()) {
+ __ cmp(left_low, ShifterOperand(left_low));
+ ret = cond == kCondLE ? EQ : NE;
+ break;
+ }
+
+ if (cond == kCondLE) {
+ cond = kCondLT;
+ } else {
+ DCHECK_EQ(cond, kCondGT);
+ cond = kCondGE;
+ }
+
+ value++;
+ FALLTHROUGH_INTENDED;
+ case kCondGE:
+ case kCondLT:
+ __ CmpConstant(left_low, Low32Bits(value));
+ __ sbcs(IP, left_high, ShifterOperand(High32Bits(value)));
+ ret = ARMCondition(cond);
+ break;
+ default:
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
+ }
+
+ return ret;
+}
+
+static Condition GenerateLongTest(HCondition* condition,
+ bool invert,
+ CodeGeneratorARM* codegen) {
+ DCHECK_EQ(condition->GetLeft()->GetType(), Primitive::kPrimLong);
+
+ const LocationSummary* const locations = condition->GetLocations();
+ IfCondition cond = invert ? condition->GetOppositeCondition() : condition->GetCondition();
+ Condition ret = EQ;
+ Location left = locations->InAt(0);
+ Location right = locations->InAt(1);
+
+ DCHECK(right.IsRegisterPair());
+
+ switch (cond) {
+ case kCondEQ:
+ case kCondNE:
+ case kCondB:
+ case kCondBE:
+ case kCondA:
+ case kCondAE:
+ __ cmp(left.AsRegisterPairHigh<Register>(),
+ ShifterOperand(right.AsRegisterPairHigh<Register>()));
+ __ it(EQ);
+ __ cmp(left.AsRegisterPairLow<Register>(),
+ ShifterOperand(right.AsRegisterPairLow<Register>()),
+ EQ);
+ ret = ARMUnsignedCondition(cond);
+ break;
+ case kCondLE:
+ case kCondGT:
+ if (cond == kCondLE) {
+ cond = kCondGE;
+ } else {
+ DCHECK_EQ(cond, kCondGT);
+ cond = kCondLT;
+ }
+
+ std::swap(left, right);
+ FALLTHROUGH_INTENDED;
+ case kCondGE:
+ case kCondLT:
+ __ cmp(left.AsRegisterPairLow<Register>(),
+ ShifterOperand(right.AsRegisterPairLow<Register>()));
+ __ sbcs(IP,
+ left.AsRegisterPairHigh<Register>(),
+ ShifterOperand(right.AsRegisterPairHigh<Register>()));
+ ret = ARMCondition(cond);
+ break;
+ default:
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
+ }
+
+ return ret;
+}
+
+static Condition GenerateTest(HInstruction* instruction,
+ Location loc,
+ bool invert,
+ CodeGeneratorARM* codegen) {
+ DCHECK(!instruction->IsConstant());
+
+ Condition ret = invert ? EQ : NE;
+
+ if (IsBooleanValueOrMaterializedCondition(instruction)) {
+ __ CmpConstant(loc.AsRegister<Register>(), 0);
+ } else {
+ HCondition* const condition = instruction->AsCondition();
+ const LocationSummary* const locations = condition->GetLocations();
+ const Primitive::Type type = condition->GetLeft()->GetType();
+ const IfCondition cond = invert ? condition->GetOppositeCondition() : condition->GetCondition();
+ const Location right = locations->InAt(1);
+
+ if (type == Primitive::kPrimLong) {
+ ret = condition->GetLocations()->InAt(1).IsConstant()
+ ? GenerateLongTestConstant(condition, invert, codegen)
+ : GenerateLongTest(condition, invert, codegen);
+ } else if (Primitive::IsFloatingPointType(type)) {
+ GenerateVcmp(condition, codegen);
+ __ vmstat();
+ ret = ARMFPCondition(cond, condition->IsGtBias());
+ } else {
+ DCHECK(Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) << type;
+
+ const Register left = locations->InAt(0).AsRegister<Register>();
+
+ if (right.IsRegister()) {
+ __ cmp(left, ShifterOperand(right.AsRegister<Register>()));
+ } else {
+ DCHECK(right.IsConstant());
+ __ CmpConstant(left, CodeGenerator::GetInt32ValueOf(right.GetConstant()));
+ }
+
+ ret = ARMCondition(cond);
+ }
+ }
+
+ return ret;
+}
+
+static bool CanGenerateTest(HInstruction* condition, ArmAssembler* assembler) {
+ if (!IsBooleanValueOrMaterializedCondition(condition)) {
+ const HCondition* const cond = condition->AsCondition();
+
+ if (cond->GetLeft()->GetType() == Primitive::kPrimLong) {
+ const LocationSummary* const locations = cond->GetLocations();
+ const IfCondition c = cond->GetCondition();
+
+ if (locations->InAt(1).IsConstant()) {
+ const int64_t value = locations->InAt(1).GetConstant()->AsLongConstant()->GetValue();
+ ShifterOperand so;
+
+ if (c < kCondLT || c > kCondGE) {
+ // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
+ // we check that the least significant half of the first input to be compared
+ // is in a low register (the other half is read outside an IT block), and
+ // the constant fits in an 8-bit unsigned integer, so that a 16-bit CMP
+ // encoding can be used.
+ if (!ArmAssembler::IsLowRegister(locations->InAt(0).AsRegisterPairLow<Register>()) ||
+ !IsUint<8>(Low32Bits(value))) {
+ return false;
+ }
+ } else if (c == kCondLE || c == kCondGT) {
+ if (value < std::numeric_limits<int64_t>::max() &&
+ !assembler->ShifterOperandCanHold(kNoRegister,
+ kNoRegister,
+ SBC,
+ High32Bits(value + 1),
+ kCcSet,
+ &so)) {
+ return false;
+ }
+ } else if (!assembler->ShifterOperandCanHold(kNoRegister,
+ kNoRegister,
+ SBC,
+ High32Bits(value),
+ kCcSet,
+ &so)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool CanEncodeConstantAs8BitImmediate(HConstant* constant) {
+ const Primitive::Type type = constant->GetType();
+ bool ret = false;
+
+ DCHECK(Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) << type;
+
+ if (type == Primitive::kPrimLong) {
+ const uint64_t value = constant->AsLongConstant()->GetValueAsUint64();
+
+ ret = IsUint<8>(Low32Bits(value)) && IsUint<8>(High32Bits(value));
+ } else {
+ ret = IsUint<8>(CodeGenerator::GetInt32ValueOf(constant));
+ }
+
+ return ret;
+}
+
+static Location Arm8BitEncodableConstantOrRegister(HInstruction* constant) {
+ DCHECK(!Primitive::IsFloatingPointType(constant->GetType()));
+
+ if (constant->IsConstant() && CanEncodeConstantAs8BitImmediate(constant->AsConstant())) {
+ return Location::ConstantLocation(constant->AsConstant());
+ }
+
+ return Location::RequiresRegister();
+}
+
+static bool CanGenerateConditionalMove(const Location& out, const Location& src) {
+ // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
+ // we check that we are not dealing with floating-point output (there is no
+ // 16-bit VMOV encoding).
+ if (!out.IsRegister() && !out.IsRegisterPair()) {
+ return false;
+ }
+
+ // For constants, we also check that the output is in one or two low registers,
+ // and that the constants fit in an 8-bit unsigned integer, so that a 16-bit
+ // MOV encoding can be used.
+ if (src.IsConstant()) {
+ if (!CanEncodeConstantAs8BitImmediate(src.GetConstant())) {
+ return false;
+ }
+
+ if (out.IsRegister()) {
+ if (!ArmAssembler::IsLowRegister(out.AsRegister<Register>())) {
+ return false;
+ }
+ } else {
+ DCHECK(out.IsRegisterPair());
+
+ if (!ArmAssembler::IsLowRegister(out.AsRegisterPairHigh<Register>())) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
#undef __
// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
#define __ down_cast<ArmAssembler*>(GetAssembler())-> // NOLINT
+Label* CodeGeneratorARM::GetFinalLabel(HInstruction* instruction, Label* final_label) {
+ DCHECK(!instruction->IsControlFlow() && !instruction->IsSuspendCheck());
+
+ const HBasicBlock* const block = instruction->GetBlock();
+ const HLoopInformation* const info = block->GetLoopInformation();
+ HInstruction* const next = instruction->GetNext();
+
+ // Avoid a branch to a branch.
+ if (next->IsGoto() && (info == nullptr ||
+ !info->IsBackEdge(*block) ||
+ !info->HasSuspendCheck())) {
+ final_label = GetLabelOf(next->AsGoto()->GetSuccessor());
+ }
+
+ return final_label;
+}
+
void CodeGeneratorARM::DumpCoreRegister(std::ostream& stream, int reg) const {
stream << Register(reg);
}
@@ -1905,44 +2227,6 @@
void InstructionCodeGeneratorARM::VisitExit(HExit* exit ATTRIBUTE_UNUSED) {
}
-void InstructionCodeGeneratorARM::GenerateVcmp(HInstruction* instruction) {
- Primitive::Type type = instruction->InputAt(0)->GetType();
- Location lhs_loc = instruction->GetLocations()->InAt(0);
- Location rhs_loc = instruction->GetLocations()->InAt(1);
- if (rhs_loc.IsConstant()) {
- // 0.0 is the only immediate that can be encoded directly in
- // a VCMP instruction.
- //
- // Both the JLS (section 15.20.1) and the JVMS (section 6.5)
- // specify that in a floating-point comparison, positive zero
- // and negative zero are considered equal, so we can use the
- // literal 0.0 for both cases here.
- //
- // Note however that some methods (Float.equal, Float.compare,
- // Float.compareTo, Double.equal, Double.compare,
- // Double.compareTo, Math.max, Math.min, StrictMath.max,
- // StrictMath.min) consider 0.0 to be (strictly) greater than
- // -0.0. So if we ever translate calls to these methods into a
- // HCompare instruction, we must handle the -0.0 case with
- // care here.
- DCHECK(rhs_loc.GetConstant()->IsArithmeticZero());
- if (type == Primitive::kPrimFloat) {
- __ vcmpsz(lhs_loc.AsFpuRegister<SRegister>());
- } else {
- DCHECK_EQ(type, Primitive::kPrimDouble);
- __ vcmpdz(FromLowSToD(lhs_loc.AsFpuRegisterPairLow<SRegister>()));
- }
- } else {
- if (type == Primitive::kPrimFloat) {
- __ vcmps(lhs_loc.AsFpuRegister<SRegister>(), rhs_loc.AsFpuRegister<SRegister>());
- } else {
- DCHECK_EQ(type, Primitive::kPrimDouble);
- __ vcmpd(FromLowSToD(lhs_loc.AsFpuRegisterPairLow<SRegister>()),
- FromLowSToD(rhs_loc.AsFpuRegisterPairLow<SRegister>()));
- }
- }
-}
-
void InstructionCodeGeneratorARM::GenerateFPJumps(HCondition* cond,
Label* true_label,
Label* false_label ATTRIBUTE_UNUSED) {
@@ -2050,7 +2334,7 @@
break;
case Primitive::kPrimFloat:
case Primitive::kPrimDouble:
- GenerateVcmp(condition);
+ GenerateVcmp(condition, codegen_);
GenerateFPJumps(condition, true_target, false_target);
break;
default:
@@ -2120,20 +2404,38 @@
return;
}
+ Label* non_fallthrough_target;
+ Condition arm_cond;
LocationSummary* locations = cond->GetLocations();
DCHECK(locations->InAt(0).IsRegister());
Register left = locations->InAt(0).AsRegister<Register>();
Location right = locations->InAt(1);
- if (right.IsRegister()) {
- __ cmp(left, ShifterOperand(right.AsRegister<Register>()));
- } else {
- DCHECK(right.IsConstant());
- __ CmpConstant(left, CodeGenerator::GetInt32ValueOf(right.GetConstant()));
- }
+
if (true_target == nullptr) {
- __ b(false_target, ARMCondition(condition->GetOppositeCondition()));
+ arm_cond = ARMCondition(condition->GetOppositeCondition());
+ non_fallthrough_target = false_target;
} else {
- __ b(true_target, ARMCondition(condition->GetCondition()));
+ arm_cond = ARMCondition(condition->GetCondition());
+ non_fallthrough_target = true_target;
+ }
+
+ if (right.IsConstant() && (arm_cond == NE || arm_cond == EQ) &&
+ CodeGenerator::GetInt32ValueOf(right.GetConstant()) == 0) {
+ if (arm_cond == EQ) {
+ __ CompareAndBranchIfZero(left, non_fallthrough_target);
+ } else {
+ DCHECK_EQ(arm_cond, NE);
+ __ CompareAndBranchIfNonZero(left, non_fallthrough_target);
+ }
+ } else {
+ if (right.IsRegister()) {
+ __ cmp(left, ShifterOperand(right.AsRegister<Register>()));
+ } else {
+ DCHECK(right.IsConstant());
+ __ CmpConstant(left, CodeGenerator::GetInt32ValueOf(right.GetConstant()));
+ }
+
+ __ b(non_fallthrough_target, arm_cond);
}
}
@@ -2193,28 +2495,140 @@
void LocationsBuilderARM::VisitSelect(HSelect* select) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
- if (Primitive::IsFloatingPointType(select->GetType())) {
+ const bool is_floating_point = Primitive::IsFloatingPointType(select->GetType());
+
+ if (is_floating_point) {
locations->SetInAt(0, Location::RequiresFpuRegister());
- locations->SetInAt(1, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::FpuRegisterOrConstant(select->GetTrueValue()));
} else {
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(1, Arm8BitEncodableConstantOrRegister(select->GetTrueValue()));
}
+
if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
- locations->SetInAt(2, Location::RequiresRegister());
+ locations->SetInAt(2, Location::RegisterOrConstant(select->GetCondition()));
+ // The code generator handles overlap with the values, but not with the condition.
+ locations->SetOut(Location::SameAsFirstInput());
+ } else if (is_floating_point) {
+ locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+ } else {
+ if (!locations->InAt(1).IsConstant()) {
+ locations->SetInAt(0, Arm8BitEncodableConstantOrRegister(select->GetFalseValue()));
+ }
+
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}
- locations->SetOut(Location::SameAsFirstInput());
}
void InstructionCodeGeneratorARM::VisitSelect(HSelect* select) {
- LocationSummary* locations = select->GetLocations();
- Label false_target;
- GenerateTestAndBranch(select,
- /* condition_input_index */ 2,
- /* true_target */ nullptr,
- &false_target);
- codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
- __ Bind(&false_target);
+ HInstruction* const condition = select->GetCondition();
+ const LocationSummary* const locations = select->GetLocations();
+ const Primitive::Type type = select->GetType();
+ const Location first = locations->InAt(0);
+ const Location out = locations->Out();
+ const Location second = locations->InAt(1);
+ Location src;
+
+ if (condition->IsIntConstant()) {
+ if (condition->AsIntConstant()->IsFalse()) {
+ src = first;
+ } else {
+ src = second;
+ }
+
+ codegen_->MoveLocation(out, src, type);
+ return;
+ }
+
+ if (!Primitive::IsFloatingPointType(type) &&
+ CanGenerateTest(condition, codegen_->GetAssembler())) {
+ bool invert = false;
+
+ if (out.Equals(second)) {
+ src = first;
+ invert = true;
+ } else if (out.Equals(first)) {
+ src = second;
+ } else if (second.IsConstant()) {
+ DCHECK(CanEncodeConstantAs8BitImmediate(second.GetConstant()));
+ src = second;
+ } else if (first.IsConstant()) {
+ DCHECK(CanEncodeConstantAs8BitImmediate(first.GetConstant()));
+ src = first;
+ invert = true;
+ } else {
+ src = second;
+ }
+
+ if (CanGenerateConditionalMove(out, src)) {
+ if (!out.Equals(first) && !out.Equals(second)) {
+ codegen_->MoveLocation(out, src.Equals(first) ? second : first, type);
+ }
+
+ const Condition cond = GenerateTest(condition, locations->InAt(2), invert, codegen_);
+
+ if (out.IsRegister()) {
+ ShifterOperand operand;
+
+ if (src.IsConstant()) {
+ operand = ShifterOperand(CodeGenerator::GetInt32ValueOf(src.GetConstant()));
+ } else {
+ DCHECK(src.IsRegister());
+ operand = ShifterOperand(src.AsRegister<Register>());
+ }
+
+ __ it(cond);
+ __ mov(out.AsRegister<Register>(), operand, cond);
+ } else {
+ DCHECK(out.IsRegisterPair());
+
+ ShifterOperand operand_high;
+ ShifterOperand operand_low;
+
+ if (src.IsConstant()) {
+ const int64_t value = src.GetConstant()->AsLongConstant()->GetValue();
+
+ operand_high = ShifterOperand(High32Bits(value));
+ operand_low = ShifterOperand(Low32Bits(value));
+ } else {
+ DCHECK(src.IsRegisterPair());
+ operand_high = ShifterOperand(src.AsRegisterPairHigh<Register>());
+ operand_low = ShifterOperand(src.AsRegisterPairLow<Register>());
+ }
+
+ __ it(cond);
+ __ mov(out.AsRegisterPairLow<Register>(), operand_low, cond);
+ __ it(cond);
+ __ mov(out.AsRegisterPairHigh<Register>(), operand_high, cond);
+ }
+
+ return;
+ }
+ }
+
+ Label* false_target = nullptr;
+ Label* true_target = nullptr;
+ Label select_end;
+ Label* target = codegen_->GetFinalLabel(select, &select_end);
+
+ if (out.Equals(second)) {
+ true_target = target;
+ src = first;
+ } else {
+ false_target = target;
+ src = second;
+
+ if (!out.Equals(first)) {
+ codegen_->MoveLocation(out, first, type);
+ }
+ }
+
+ GenerateTestAndBranch(select, 2, true_target, false_target);
+ codegen_->MoveLocation(out, src, type);
+
+ if (select_end.IsLinked()) {
+ __ Bind(&select_end);
+ }
}
void LocationsBuilderARM::VisitNativeDebugInfo(HNativeDebugInfo* info) {
@@ -2293,7 +2707,7 @@
break;
case Primitive::kPrimFloat:
case Primitive::kPrimDouble:
- GenerateVcmp(cond);
+ GenerateVcmp(cond, codegen_);
GenerateFPJumps(cond, &true_label, &false_label);
break;
}
@@ -4347,7 +4761,7 @@
case Primitive::kPrimFloat:
case Primitive::kPrimDouble: {
__ LoadImmediate(out, 0);
- GenerateVcmp(compare);
+ GenerateVcmp(compare, codegen_);
__ vmstat(); // transfer FP status register to ARM APSR.
less_cond = ARMFPCondition(kCondLT, compare->IsGtBias());
break;
@@ -4710,6 +5124,7 @@
case ADC: neg_opcode = SBC; value = ~value; break;
case SUB: neg_opcode = ADD; value = -value; break;
case SBC: neg_opcode = ADC; value = ~value; break;
+ case MOV: neg_opcode = MVN; value = ~value; break;
default:
return false;
}
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index df2dbc7..36475bc 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -299,7 +299,6 @@
void GenerateCompareTestAndBranch(HCondition* condition,
Label* true_target,
Label* false_target);
- void GenerateVcmp(HInstruction* instruction);
void GenerateFPJumps(HCondition* cond, Label* true_label, Label* false_label);
void GenerateLongComparesAndJumps(HCondition* cond, Label* true_label, Label* false_label);
void DivRemOneOrMinusOne(HBinaryOperation* instruction);
@@ -422,6 +421,8 @@
return CommonGetLabelOf<Label>(block_labels_, block);
}
+ Label* GetFinalLabel(HInstruction* instruction, Label* final_label);
+
void Initialize() OVERRIDE {
block_labels_ = CommonInitializeLabels<Label>();
}
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 18c95b3..9b780e4 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -3419,7 +3419,7 @@
if (Primitive::IsFloatingPointType(select->GetType())) {
locations->SetInAt(0, Location::RequiresFpuRegister());
locations->SetInAt(1, Location::RequiresFpuRegister());
- locations->SetOut(Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
} else {
HConstant* cst_true_value = select->GetTrueValue()->AsConstant();
HConstant* cst_false_value = select->GetFalseValue()->AsConstant();
@@ -3442,7 +3442,7 @@
: Location::ConstantLocation(cst_true_value));
locations->SetInAt(0, false_value_in_register ? Location::RequiresRegister()
: Location::ConstantLocation(cst_false_value));
- locations->SetOut(Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}
if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 6bfbe4a..4b6be32 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -42,6 +42,7 @@
using helpers::DWARFReg;
using helpers::HighDRegisterFrom;
using helpers::HighRegisterFrom;
+using helpers::InputDRegisterAt;
using helpers::InputOperandAt;
using helpers::InputRegister;
using helpers::InputRegisterAt;
@@ -53,6 +54,7 @@
using helpers::LocationFrom;
using helpers::LowRegisterFrom;
using helpers::LowSRegisterFrom;
+using helpers::OperandFrom;
using helpers::OutputRegister;
using helpers::OutputSRegister;
using helpers::OutputVRegister;
@@ -1450,8 +1452,317 @@
}
}
+static void GenerateVcmp(HInstruction* instruction, CodeGeneratorARMVIXL* codegen) {
+ const Location rhs_loc = instruction->GetLocations()->InAt(1);
+ if (rhs_loc.IsConstant()) {
+ // 0.0 is the only immediate that can be encoded directly in
+ // a VCMP instruction.
+ //
+ // Both the JLS (section 15.20.1) and the JVMS (section 6.5)
+ // specify that in a floating-point comparison, positive zero
+ // and negative zero are considered equal, so we can use the
+ // literal 0.0 for both cases here.
+ //
+ // Note however that some methods (Float.equal, Float.compare,
+ // Float.compareTo, Double.equal, Double.compare,
+ // Double.compareTo, Math.max, Math.min, StrictMath.max,
+ // StrictMath.min) consider 0.0 to be (strictly) greater than
+ // -0.0. So if we ever translate calls to these methods into a
+ // HCompare instruction, we must handle the -0.0 case with
+ // care here.
+ DCHECK(rhs_loc.GetConstant()->IsArithmeticZero());
+
+ const Primitive::Type type = instruction->InputAt(0)->GetType();
+
+ if (type == Primitive::kPrimFloat) {
+ __ Vcmp(F32, InputSRegisterAt(instruction, 0), 0.0);
+ } else {
+ DCHECK_EQ(type, Primitive::kPrimDouble);
+ __ Vcmp(F64, InputDRegisterAt(instruction, 0), 0.0);
+ }
+ } else {
+ __ Vcmp(InputVRegisterAt(instruction, 0), InputVRegisterAt(instruction, 1));
+ }
+}
+
+static vixl32::Condition GenerateLongTestConstant(HCondition* condition,
+ bool invert,
+ CodeGeneratorARMVIXL* codegen) {
+ DCHECK_EQ(condition->GetLeft()->GetType(), Primitive::kPrimLong);
+
+ const LocationSummary* const locations = condition->GetLocations();
+ IfCondition cond = invert ? condition->GetOppositeCondition() : condition->GetCondition();
+ vixl32::Condition ret = eq;
+ const Location left = locations->InAt(0);
+ const Location right = locations->InAt(1);
+
+ DCHECK(right.IsConstant());
+
+ const vixl32::Register left_high = HighRegisterFrom(left);
+ const vixl32::Register left_low = LowRegisterFrom(left);
+ int64_t value = Int64ConstantFrom(right);
+
+ switch (cond) {
+ case kCondEQ:
+ case kCondNE:
+ case kCondB:
+ case kCondBE:
+ case kCondA:
+ case kCondAE: {
+ __ Cmp(left_high, High32Bits(value));
+
+ ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
+ 2 * vixl32::k16BitT32InstructionSizeInBytes,
+ CodeBufferCheckScope::kExactSize);
+
+ __ it(eq);
+ __ cmp(eq, left_low, Low32Bits(value));
+ ret = ARMUnsignedCondition(cond);
+ break;
+ }
+ case kCondLE:
+ case kCondGT:
+ // Trivially true or false.
+ if (value == std::numeric_limits<int64_t>::max()) {
+ __ Cmp(left_low, left_low);
+ ret = cond == kCondLE ? eq : ne;
+ break;
+ }
+
+ if (cond == kCondLE) {
+ cond = kCondLT;
+ } else {
+ DCHECK_EQ(cond, kCondGT);
+ cond = kCondGE;
+ }
+
+ value++;
+ FALLTHROUGH_INTENDED;
+ case kCondGE:
+ case kCondLT: {
+ UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
+
+ __ Cmp(left_low, Low32Bits(value));
+ __ Sbcs(temps.Acquire(), left_high, High32Bits(value));
+ ret = ARMCondition(cond);
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
+ }
+
+ return ret;
+}
+
+static vixl32::Condition GenerateLongTest(HCondition* condition,
+ bool invert,
+ CodeGeneratorARMVIXL* codegen) {
+ DCHECK_EQ(condition->GetLeft()->GetType(), Primitive::kPrimLong);
+
+ const LocationSummary* const locations = condition->GetLocations();
+ IfCondition cond = invert ? condition->GetOppositeCondition() : condition->GetCondition();
+ vixl32::Condition ret = eq;
+ Location left = locations->InAt(0);
+ Location right = locations->InAt(1);
+
+ DCHECK(right.IsRegisterPair());
+
+ switch (cond) {
+ case kCondEQ:
+ case kCondNE:
+ case kCondB:
+ case kCondBE:
+ case kCondA:
+ case kCondAE: {
+ __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right));
+
+ ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
+ 2 * vixl32::k16BitT32InstructionSizeInBytes,
+ CodeBufferCheckScope::kExactSize);
+
+ __ it(eq);
+ __ cmp(eq, LowRegisterFrom(left), LowRegisterFrom(right));
+ ret = ARMUnsignedCondition(cond);
+ break;
+ }
+ case kCondLE:
+ case kCondGT:
+ if (cond == kCondLE) {
+ cond = kCondGE;
+ } else {
+ DCHECK_EQ(cond, kCondGT);
+ cond = kCondLT;
+ }
+
+ std::swap(left, right);
+ FALLTHROUGH_INTENDED;
+ case kCondGE:
+ case kCondLT: {
+ UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
+
+ __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right));
+ __ Sbcs(temps.Acquire(), HighRegisterFrom(left), HighRegisterFrom(right));
+ ret = ARMCondition(cond);
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
+ }
+
+ return ret;
+}
+
+static vixl32::Condition GenerateTest(HInstruction* instruction,
+ Location loc,
+ bool invert,
+ CodeGeneratorARMVIXL* codegen) {
+ DCHECK(!instruction->IsConstant());
+
+ vixl32::Condition ret = invert ? eq : ne;
+
+ if (IsBooleanValueOrMaterializedCondition(instruction)) {
+ __ Cmp(RegisterFrom(loc), 0);
+ } else {
+ HCondition* const condition = instruction->AsCondition();
+ const Primitive::Type type = condition->GetLeft()->GetType();
+ const IfCondition cond = invert ? condition->GetOppositeCondition() : condition->GetCondition();
+
+ if (type == Primitive::kPrimLong) {
+ ret = condition->GetLocations()->InAt(1).IsConstant()
+ ? GenerateLongTestConstant(condition, invert, codegen)
+ : GenerateLongTest(condition, invert, codegen);
+ } else if (Primitive::IsFloatingPointType(type)) {
+ GenerateVcmp(condition, codegen);
+ __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
+ ret = ARMFPCondition(cond, condition->IsGtBias());
+ } else {
+ DCHECK(Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) << type;
+ __ Cmp(InputRegisterAt(condition, 0), InputOperandAt(condition, 1));
+ ret = ARMCondition(cond);
+ }
+ }
+
+ return ret;
+}
+
+static bool CanGenerateTest(HInstruction* condition, ArmVIXLAssembler* assembler) {
+ if (!IsBooleanValueOrMaterializedCondition(condition)) {
+ const HCondition* const cond = condition->AsCondition();
+
+ if (cond->GetLeft()->GetType() == Primitive::kPrimLong) {
+ const LocationSummary* const locations = cond->GetLocations();
+ const IfCondition c = cond->GetCondition();
+
+ if (locations->InAt(1).IsConstant()) {
+ const int64_t value = Int64ConstantFrom(locations->InAt(1));
+
+ if (c < kCondLT || c > kCondGE) {
+ // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
+ // we check that the least significant half of the first input to be compared
+ // is in a low register (the other half is read outside an IT block), and
+ // the constant fits in an 8-bit unsigned integer, so that a 16-bit CMP
+ // encoding can be used.
+ if (!LowRegisterFrom(locations->InAt(0)).IsLow() || !IsUint<8>(Low32Bits(value))) {
+ return false;
+ }
+ // TODO(VIXL): The rest of the checks are there to keep the backend in sync with
+ // the previous one, but are not strictly necessary.
+ } else if (c == kCondLE || c == kCondGT) {
+ if (value < std::numeric_limits<int64_t>::max() &&
+ !assembler->ShifterOperandCanHold(SBC, High32Bits(value + 1), kCcSet)) {
+ return false;
+ }
+ } else if (!assembler->ShifterOperandCanHold(SBC, High32Bits(value), kCcSet)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool CanEncodeConstantAs8BitImmediate(HConstant* constant) {
+ const Primitive::Type type = constant->GetType();
+ bool ret = false;
+
+ DCHECK(Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) << type;
+
+ if (type == Primitive::kPrimLong) {
+ const uint64_t value = Uint64ConstantFrom(constant);
+
+ ret = IsUint<8>(Low32Bits(value)) && IsUint<8>(High32Bits(value));
+ } else {
+ ret = IsUint<8>(Int32ConstantFrom(constant));
+ }
+
+ return ret;
+}
+
+static Location Arm8BitEncodableConstantOrRegister(HInstruction* constant) {
+ DCHECK(!Primitive::IsFloatingPointType(constant->GetType()));
+
+ if (constant->IsConstant() && CanEncodeConstantAs8BitImmediate(constant->AsConstant())) {
+ return Location::ConstantLocation(constant->AsConstant());
+ }
+
+ return Location::RequiresRegister();
+}
+
+static bool CanGenerateConditionalMove(const Location& out, const Location& src) {
+ // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
+ // we check that we are not dealing with floating-point output (there is no
+ // 16-bit VMOV encoding).
+ if (!out.IsRegister() && !out.IsRegisterPair()) {
+ return false;
+ }
+
+ // For constants, we also check that the output is in one or two low registers,
+ // and that the constants fit in an 8-bit unsigned integer, so that a 16-bit
+ // MOV encoding can be used.
+ if (src.IsConstant()) {
+ if (!CanEncodeConstantAs8BitImmediate(src.GetConstant())) {
+ return false;
+ }
+
+ if (out.IsRegister()) {
+ if (!RegisterFrom(out).IsLow()) {
+ return false;
+ }
+ } else {
+ DCHECK(out.IsRegisterPair());
+
+ if (!HighRegisterFrom(out).IsLow()) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
#undef __
+vixl32::Label* CodeGeneratorARMVIXL::GetFinalLabel(HInstruction* instruction,
+ vixl32::Label* final_label) {
+ DCHECK(!instruction->IsControlFlow() && !instruction->IsSuspendCheck());
+
+ const HBasicBlock* const block = instruction->GetBlock();
+ const HLoopInformation* const info = block->GetLoopInformation();
+ HInstruction* const next = instruction->GetNext();
+
+ // Avoid a branch to a branch.
+ if (next->IsGoto() && (info == nullptr ||
+ !info->IsBackEdge(*block) ||
+ !info->HasSuspendCheck())) {
+ final_label = GetLabelOf(next->AsGoto()->GetSuccessor());
+ }
+
+ return final_label;
+}
+
CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph,
const ArmInstructionSetFeatures& isa_features,
const CompilerOptions& compiler_options,
@@ -1945,43 +2256,6 @@
void InstructionCodeGeneratorARMVIXL::VisitExit(HExit* exit ATTRIBUTE_UNUSED) {
}
-void InstructionCodeGeneratorARMVIXL::GenerateVcmp(HInstruction* instruction) {
- Primitive::Type type = instruction->InputAt(0)->GetType();
- Location lhs_loc = instruction->GetLocations()->InAt(0);
- Location rhs_loc = instruction->GetLocations()->InAt(1);
- if (rhs_loc.IsConstant()) {
- // 0.0 is the only immediate that can be encoded directly in
- // a VCMP instruction.
- //
- // Both the JLS (section 15.20.1) and the JVMS (section 6.5)
- // specify that in a floating-point comparison, positive zero
- // and negative zero are considered equal, so we can use the
- // literal 0.0 for both cases here.
- //
- // Note however that some methods (Float.equal, Float.compare,
- // Float.compareTo, Double.equal, Double.compare,
- // Double.compareTo, Math.max, Math.min, StrictMath.max,
- // StrictMath.min) consider 0.0 to be (strictly) greater than
- // -0.0. So if we ever translate calls to these methods into a
- // HCompare instruction, we must handle the -0.0 case with
- // care here.
- DCHECK(rhs_loc.GetConstant()->IsArithmeticZero());
- if (type == Primitive::kPrimFloat) {
- __ Vcmp(F32, InputSRegisterAt(instruction, 0), 0.0);
- } else {
- DCHECK_EQ(type, Primitive::kPrimDouble);
- __ Vcmp(F64, DRegisterFrom(lhs_loc), 0.0);
- }
- } else {
- if (type == Primitive::kPrimFloat) {
- __ Vcmp(InputSRegisterAt(instruction, 0), InputSRegisterAt(instruction, 1));
- } else {
- DCHECK_EQ(type, Primitive::kPrimDouble);
- __ Vcmp(DRegisterFrom(lhs_loc), DRegisterFrom(rhs_loc));
- }
- }
-}
-
void InstructionCodeGeneratorARMVIXL::GenerateFPJumps(HCondition* cond,
vixl32::Label* true_label,
vixl32::Label* false_label ATTRIBUTE_UNUSED) {
@@ -2090,7 +2364,7 @@
break;
case Primitive::kPrimFloat:
case Primitive::kPrimDouble:
- GenerateVcmp(condition);
+ GenerateVcmp(condition, codegen_);
GenerateFPJumps(condition, true_target, false_target);
break;
default:
@@ -2167,20 +2441,29 @@
return;
}
- LocationSummary* locations = cond->GetLocations();
- DCHECK(locations->InAt(0).IsRegister());
- vixl32::Register left = InputRegisterAt(cond, 0);
- Location right = locations->InAt(1);
- if (right.IsRegister()) {
- __ Cmp(left, InputRegisterAt(cond, 1));
- } else {
- DCHECK(right.IsConstant());
- __ Cmp(left, CodeGenerator::GetInt32ValueOf(right.GetConstant()));
- }
+ vixl32::Label* non_fallthrough_target;
+ vixl32::Condition arm_cond = vixl32::Condition::None();
+ const vixl32::Register left = InputRegisterAt(cond, 0);
+ const Operand right = InputOperandAt(cond, 1);
+
if (true_target == nullptr) {
- __ B(ARMCondition(condition->GetOppositeCondition()), false_target);
+ arm_cond = ARMCondition(condition->GetOppositeCondition());
+ non_fallthrough_target = false_target;
} else {
- __ B(ARMCondition(condition->GetCondition()), true_target);
+ arm_cond = ARMCondition(condition->GetCondition());
+ non_fallthrough_target = true_target;
+ }
+
+ if (right.IsImmediate() && right.GetImmediate() == 0 && (arm_cond.Is(ne) || arm_cond.Is(eq))) {
+ if (arm_cond.Is(eq)) {
+ __ CompareAndBranchIfZero(left, non_fallthrough_target);
+ } else {
+ DCHECK(arm_cond.Is(ne));
+ __ CompareAndBranchIfNonZero(left, non_fallthrough_target);
+ }
+ } else {
+ __ Cmp(left, right);
+ __ B(arm_cond, non_fallthrough_target);
}
}
@@ -2241,29 +2524,135 @@
void LocationsBuilderARMVIXL::VisitSelect(HSelect* select) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
- if (Primitive::IsFloatingPointType(select->GetType())) {
+ const bool is_floating_point = Primitive::IsFloatingPointType(select->GetType());
+
+ if (is_floating_point) {
locations->SetInAt(0, Location::RequiresFpuRegister());
- locations->SetInAt(1, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::FpuRegisterOrConstant(select->GetTrueValue()));
} else {
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(1, Arm8BitEncodableConstantOrRegister(select->GetTrueValue()));
}
+
if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
- locations->SetInAt(2, Location::RequiresRegister());
+ locations->SetInAt(2, Location::RegisterOrConstant(select->GetCondition()));
+ // The code generator handles overlap with the values, but not with the condition.
+ locations->SetOut(Location::SameAsFirstInput());
+ } else if (is_floating_point) {
+ locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+ } else {
+ if (!locations->InAt(1).IsConstant()) {
+ locations->SetInAt(0, Arm8BitEncodableConstantOrRegister(select->GetFalseValue()));
+ }
+
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}
- locations->SetOut(Location::SameAsFirstInput());
}
void InstructionCodeGeneratorARMVIXL::VisitSelect(HSelect* select) {
- LocationSummary* locations = select->GetLocations();
- vixl32::Label false_target;
- GenerateTestAndBranch(select,
- /* condition_input_index */ 2,
- /* true_target */ nullptr,
- &false_target,
- /* far_target */ false);
- codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
- __ Bind(&false_target);
+ HInstruction* const condition = select->GetCondition();
+ const LocationSummary* const locations = select->GetLocations();
+ const Primitive::Type type = select->GetType();
+ const Location first = locations->InAt(0);
+ const Location out = locations->Out();
+ const Location second = locations->InAt(1);
+ Location src;
+
+ if (condition->IsIntConstant()) {
+ if (condition->AsIntConstant()->IsFalse()) {
+ src = first;
+ } else {
+ src = second;
+ }
+
+ codegen_->MoveLocation(out, src, type);
+ return;
+ }
+
+ if (!Primitive::IsFloatingPointType(type) &&
+ CanGenerateTest(condition, codegen_->GetAssembler())) {
+ bool invert = false;
+
+ if (out.Equals(second)) {
+ src = first;
+ invert = true;
+ } else if (out.Equals(first)) {
+ src = second;
+ } else if (second.IsConstant()) {
+ DCHECK(CanEncodeConstantAs8BitImmediate(second.GetConstant()));
+ src = second;
+ } else if (first.IsConstant()) {
+ DCHECK(CanEncodeConstantAs8BitImmediate(first.GetConstant()));
+ src = first;
+ invert = true;
+ } else {
+ src = second;
+ }
+
+ if (CanGenerateConditionalMove(out, src)) {
+ if (!out.Equals(first) && !out.Equals(second)) {
+ codegen_->MoveLocation(out, src.Equals(first) ? second : first, type);
+ }
+
+ const vixl32::Condition cond = GenerateTest(condition, locations->InAt(2), invert, codegen_);
+ const size_t instr_count = out.IsRegisterPair() ? 4 : 2;
+ ExactAssemblyScope guard(GetVIXLAssembler(),
+ instr_count * vixl32::k16BitT32InstructionSizeInBytes,
+ CodeBufferCheckScope::kExactSize);
+
+ if (out.IsRegister()) {
+ __ it(cond);
+ __ mov(cond, RegisterFrom(out), OperandFrom(src, type));
+ } else {
+ DCHECK(out.IsRegisterPair());
+
+ Operand operand_high(0);
+ Operand operand_low(0);
+
+ if (src.IsConstant()) {
+ const int64_t value = Int64ConstantFrom(src);
+
+ operand_high = High32Bits(value);
+ operand_low = Low32Bits(value);
+ } else {
+ DCHECK(src.IsRegisterPair());
+ operand_high = HighRegisterFrom(src);
+ operand_low = LowRegisterFrom(src);
+ }
+
+ __ it(cond);
+ __ mov(cond, LowRegisterFrom(out), operand_low);
+ __ it(cond);
+ __ mov(cond, HighRegisterFrom(out), operand_high);
+ }
+
+ return;
+ }
+ }
+
+ vixl32::Label* false_target = nullptr;
+ vixl32::Label* true_target = nullptr;
+ vixl32::Label select_end;
+ vixl32::Label* const target = codegen_->GetFinalLabel(select, &select_end);
+
+ if (out.Equals(second)) {
+ true_target = target;
+ src = first;
+ } else {
+ false_target = target;
+ src = second;
+
+ if (!out.Equals(first)) {
+ codegen_->MoveLocation(out, first, type);
+ }
+ }
+
+ GenerateTestAndBranch(select, 2, true_target, false_target, /* far_target */ false);
+ codegen_->MoveLocation(out, src, type);
+
+ if (select_end.IsReferenced()) {
+ __ Bind(&select_end);
+ }
}
void LocationsBuilderARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo* info) {
@@ -2341,7 +2730,7 @@
break;
case Primitive::kPrimFloat:
case Primitive::kPrimDouble:
- GenerateVcmp(cond);
+ GenerateVcmp(cond, codegen_);
GenerateFPJumps(cond, &true_label, &false_label);
break;
}
@@ -4356,7 +4745,7 @@
case Primitive::kPrimFloat:
case Primitive::kPrimDouble: {
__ Mov(out, 0);
- GenerateVcmp(compare);
+ GenerateVcmp(compare, codegen_);
// To branch on the FP compare result we transfer FPSCR to APSR (encoded as PC in VMRS).
__ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
less_cond = ARMFPCondition(kCondLT, compare->IsGtBias());
@@ -4733,6 +5122,7 @@
case ADC: neg_opcode = SBC; value = ~value; break;
case SUB: neg_opcode = ADD; value = -value; break;
case SBC: neg_opcode = ADC; value = ~value; break;
+ case MOV: neg_opcode = MVN; value = ~value; break;
default:
return false;
}
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index 3f52c72..b4964e4 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -396,7 +396,6 @@
void GenerateCompareTestAndBranch(HCondition* condition,
vixl::aarch32::Label* true_target,
vixl::aarch32::Label* false_target);
- void GenerateVcmp(HInstruction* instruction);
void GenerateFPJumps(HCondition* cond,
vixl::aarch32::Label* true_label,
vixl::aarch32::Label* false_label);
@@ -505,6 +504,8 @@
return &(block_labels_[block->GetBlockId()]);
}
+ vixl32::Label* GetFinalLabel(HInstruction* instruction, vixl32::Label* final_label);
+
void Initialize() OVERRIDE {
block_labels_.resize(GetGraph()->GetBlocks().size());
}
diff --git a/compiler/optimizing/register_allocator_linear_scan.cc b/compiler/optimizing/register_allocator_linear_scan.cc
index 1a391ce..6354e76 100644
--- a/compiler/optimizing/register_allocator_linear_scan.cc
+++ b/compiler/optimizing/register_allocator_linear_scan.cc
@@ -629,21 +629,21 @@
if (!locations->OutputCanOverlapWithInputs() && locations->Out().IsUnallocated()) {
HInputsRef inputs = defined_by->GetInputs();
for (size_t i = 0; i < inputs.size(); ++i) {
- // Take the last interval of the input. It is the location of that interval
- // that will be used at `defined_by`.
- LiveInterval* interval = inputs[i]->GetLiveInterval()->GetLastSibling();
- // Note that interval may have not been processed yet.
- // TODO: Handle non-split intervals last in the work list.
- if (locations->InAt(i).IsValid()
- && interval->HasRegister()
- && interval->SameRegisterKind(*current)) {
- // The input must be live until the end of `defined_by`, to comply to
- // the linear scan algorithm. So we use `defined_by`'s end lifetime
- // position to check whether the input is dead or is inactive after
- // `defined_by`.
- DCHECK(interval->CoversSlow(defined_by->GetLifetimePosition()));
- size_t position = defined_by->GetLifetimePosition() + 1;
- FreeIfNotCoverAt(interval, position, free_until);
+ if (locations->InAt(i).IsValid()) {
+ // Take the last interval of the input. It is the location of that interval
+ // that will be used at `defined_by`.
+ LiveInterval* interval = inputs[i]->GetLiveInterval()->GetLastSibling();
+ // Note that interval may have not been processed yet.
+ // TODO: Handle non-split intervals last in the work list.
+ if (interval->HasRegister() && interval->SameRegisterKind(*current)) {
+ // The input must be live until the end of `defined_by`, to comply to
+ // the linear scan algorithm. So we use `defined_by`'s end lifetime
+ // position to check whether the input is dead or is inactive after
+ // `defined_by`.
+ DCHECK(interval->CoversSlow(defined_by->GetLifetimePosition()));
+ size_t position = defined_by->GetLifetimePosition() + 1;
+ FreeIfNotCoverAt(interval, position, free_until);
+ }
}
}
}
diff --git a/test/570-checker-select/src/Main.java b/test/570-checker-select/src/Main.java
index e0a76ca..3ac6f89 100644
--- a/test/570-checker-select/src/Main.java
+++ b/test/570-checker-select/src/Main.java
@@ -371,6 +371,49 @@
return a > b ? x : y;
}
+ /// CHECK-START-ARM: long Main.$noinline$LongEqNonmatCond_LongVarVar(long, long, long, long) disassembly (after)
+ /// CHECK: Select
+ /// CHECK-NEXT: cmp {{r\d+}}, {{r\d+}}
+ /// CHECK-NEXT: it eq
+ /// CHECK-NEXT: cmpeq {{r\d+}}, {{r\d+}}
+ /// CHECK-NEXT: it eq
+
+ public static long $noinline$LongEqNonmatCond_LongVarVar(long a, long b, long x, long y) {
+ return a == b ? x : y;
+ }
+
+ /// CHECK-START-ARM: long Main.$noinline$LongNonmatCondCst_LongVarVar(long, long, long) disassembly (after)
+ /// CHECK: Select
+ /// CHECK-NEXT: mov ip, #52720
+ /// CHECK-NEXT: movt ip, #35243
+ /// CHECK-NEXT: cmp {{r\d+}}, ip
+ /// CHECK-NEXT: sbcs ip, {{r\d+}}, #{{\d+}}
+ /// CHECK-NEXT: it ge
+
+ public static long $noinline$LongNonmatCondCst_LongVarVar(long a, long x, long y) {
+ return a > 0x89ABCDEFL ? x : y;
+ }
+
+ /// CHECK-START-ARM: long Main.$noinline$LongNonmatCondCst_LongVarVar2(long, long, long) disassembly (after)
+ /// CHECK: Select
+ /// CHECK-NEXT: mov ip, #{{\d+}}
+ /// CHECK-NEXT: movt ip, #{{\d+}}
+ /// CHECK-NEXT: cmp {{r\d+}}, ip
+
+ public static long $noinline$LongNonmatCondCst_LongVarVar2(long a, long x, long y) {
+ return a > 0x0123456789ABCDEFL ? x : y;
+ }
+
+ /// CHECK-START-ARM: long Main.$noinline$LongNonmatCondCst_LongVarVar3(long, long, long) disassembly (after)
+ /// CHECK: Select
+ /// CHECK-NEXT: cmp {{r\d+}}, {{r\d+}}
+ /// CHECK-NOT: sbcs
+ /// CHECK-NOT: cmp
+
+ public static long $noinline$LongNonmatCondCst_LongVarVar3(long a, long x, long y) {
+ return a > 0x7FFFFFFFFFFFFFFFL ? x : y;
+ }
+
/// CHECK-START: long Main.LongMatCond_LongVarVar(long, long, long, long) register (after)
/// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{j\d+}},{{j\d+}}]
/// CHECK: <<Sel1:j\d+>> Select [{{j\d+}},{{j\d+}},<<Cond>>]
@@ -612,6 +655,39 @@
assertEqual(5, IntMatCond_IntVarVar(3, 2, 5, 7));
assertEqual(8, IntMatCond_IntVarVar(2, 3, 5, 7));
+ assertEqual(0xAAAAAAAA55555555L,
+ LongNonmatCond_LongVarVar(3L, 2L, 0xAAAAAAAA55555555L, 0x8888888877777777L));
+ assertEqual(0x8888888877777777L,
+ LongNonmatCond_LongVarVar(2L, 2L, 0xAAAAAAAA55555555L, 0x8888888877777777L));
+ assertEqual(0x8888888877777777L,
+ LongNonmatCond_LongVarVar(2L, 3L, 0xAAAAAAAA55555555L, 0x8888888877777777L));
+ assertEqual(0xAAAAAAAA55555555L, LongNonmatCond_LongVarVar(0x0000000100000000L,
+ 0x00000000FFFFFFFFL,
+ 0xAAAAAAAA55555555L,
+ 0x8888888877777777L));
+ assertEqual(0x8888888877777777L, LongNonmatCond_LongVarVar(0x00000000FFFFFFFFL,
+ 0x0000000100000000L,
+ 0xAAAAAAAA55555555L,
+ 0x8888888877777777L));
+
+ assertEqual(0x8888888877777777L, $noinline$LongEqNonmatCond_LongVarVar(2L,
+ 3L,
+ 0xAAAAAAAA55555555L,
+ 0x8888888877777777L));
+ assertEqual(0xAAAAAAAA55555555L, $noinline$LongEqNonmatCond_LongVarVar(2L,
+ 2L,
+ 0xAAAAAAAA55555555L,
+ 0x8888888877777777L));
+ assertEqual(0x8888888877777777L, $noinline$LongEqNonmatCond_LongVarVar(0x10000000000L,
+ 0L,
+ 0xAAAAAAAA55555555L,
+ 0x8888888877777777L));
+
+ assertEqual(5L, $noinline$LongNonmatCondCst_LongVarVar2(0x7FFFFFFFFFFFFFFFL, 5L, 7L));
+ assertEqual(7L, $noinline$LongNonmatCondCst_LongVarVar2(2L, 5L, 7L));
+
+ assertEqual(7L, $noinline$LongNonmatCondCst_LongVarVar3(2L, 5L, 7L));
+
assertEqual(5, FloatLtNonmatCond_IntVarVar(3, 2, 5, 7));
assertEqual(7, FloatLtNonmatCond_IntVarVar(2, 3, 5, 7));
assertEqual(7, FloatLtNonmatCond_IntVarVar(Float.NaN, 2, 5, 7));
diff --git a/test/626-checker-arm64-scratch-register/src/Main.java b/test/626-checker-arm64-scratch-register/src/Main.java
index 6dd4374..1394917 100644
--- a/test/626-checker-arm64-scratch-register/src/Main.java
+++ b/test/626-checker-arm64-scratch-register/src/Main.java
@@ -70,7 +70,7 @@
/// CHECK: end_block
/// CHECK: begin_block
/// CHECK: name "<<ElseBlock>>"
- /// CHECK: ParallelMove moves:[#100->d17,32(sp)->d1,36(sp)->d2,d17->d3,d3->d4,d4->d5,d5->d6,d6->d7,d7->d18,d18->d19,d19->d20,d20->d21,d21->d22,d22->d23,d23->d10,d10->d11,d11->d12,24(sp)->d13,28(sp)->d14,d14->16(sp),d12->20(sp),d13->24(sp),d1->28(sp),d2->32(sp),16(sp)->36(sp),20(sp)->40(sp)]
+ /// CHECK: ParallelMove moves:[40(sp)->d0,24(sp)->32(sp),28(sp)->36(sp),d0->d3,d3->d4,d2->d5,d4->d6,d5->d7,d6->d18,d7->d19,d18->d20,d19->d21,d20->d22,d21->d23,d22->d10,d23->d11,16(sp)->24(sp),20(sp)->28(sp),d10->d14,d11->d12,d12->d13,d13->d1,d14->d2,32(sp)->16(sp),36(sp)->20(sp)]
/// CHECK: end_block
/// CHECK-START-ARM64: void Main.test() disassembly (after)
@@ -85,7 +85,7 @@
/// CHECK: end_block
/// CHECK: begin_block
/// CHECK: name "<<ElseBlock>>"
- /// CHECK: ParallelMove moves:[invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid]
+ /// CHECK: ParallelMove moves:[invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid]
/// CHECK: fmov d31, d2
/// CHECK: ldr s2, [sp, #36]
/// CHECK: ldr w16, [sp, #16]
@@ -111,11 +111,10 @@
/// CHECK: fmov d6, d5
/// CHECK: fmov d5, d4
/// CHECK: fmov d4, d3
- /// CHECK: fmov d3, d17
- /// CHECK: fmov d17, d13
+ /// CHECK: fmov d3, d13
/// CHECK: ldr s13, [sp, #24]
- /// CHECK: str s17, [sp, #24]
- /// CHECK: ldr s17, pc+{{\d+}} (addr {{0x[0-9a-f]+}}) (100)
+ /// CHECK: str s3, [sp, #24]
+ /// CHECK: ldr s3, pc+{{\d+}} (addr {{0x[0-9a-f]+}}) (100)
/// CHECK: end_block
public void test() {