From 4fa13f65ece3b68fe3d8722d679ebab8656bbf99 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Mon, 6 Jul 2015 18:11:54 +0100 Subject: [PATCH] Fuse long and FP compare & condition on ARM in Optimizing. Also: - Stylistic changes in corresponding parts on the x86 and x86-64 code generators. - Update and improve the documentation of art::arm::Condition. Bug: 21120453 Change-Id: If144772046e7d21362c3c2086246cb7d011d49ce --- compiler/optimizing/builder.cc | 10 +- compiler/optimizing/code_generator_arm.cc | 295 +++++++++++++++++++++----- compiler/optimizing/code_generator_arm.h | 8 + compiler/optimizing/code_generator_x86.cc | 118 ++++------- compiler/optimizing/code_generator_x86_64.cc | 90 ++++---- compiler/optimizing/instruction_simplifier.cc | 6 +- compiler/optimizing/instruction_simplifier.h | 2 +- compiler/optimizing/nodes.h | 20 +- compiler/utils/arm/constants_arm.h | 45 ++-- 9 files changed, 382 insertions(+), 212 deletions(-) diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 6b245b3ff..a7eb36c2f 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -2303,27 +2303,27 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 } case Instruction::CMP_LONG: { - Binop_23x_cmp(instruction, Primitive::kPrimLong, kNoBias, dex_pc); + Binop_23x_cmp(instruction, Primitive::kPrimLong, ComparisonBias::kNoBias, dex_pc); break; } case Instruction::CMPG_FLOAT: { - Binop_23x_cmp(instruction, Primitive::kPrimFloat, kGtBias, dex_pc); + Binop_23x_cmp(instruction, Primitive::kPrimFloat, ComparisonBias::kGtBias, dex_pc); break; } case Instruction::CMPG_DOUBLE: { - Binop_23x_cmp(instruction, Primitive::kPrimDouble, kGtBias, dex_pc); + Binop_23x_cmp(instruction, Primitive::kPrimDouble, ComparisonBias::kGtBias, dex_pc); break; } case Instruction::CMPL_FLOAT: { - Binop_23x_cmp(instruction, Primitive::kPrimFloat, kLtBias, dex_pc); + Binop_23x_cmp(instruction, Primitive::kPrimFloat, ComparisonBias::kLtBias, dex_pc); break; } case Instruction::CMPL_DOUBLE: { - Binop_23x_cmp(instruction, Primitive::kPrimDouble, kLtBias, dex_pc); + Binop_23x_cmp(instruction, Primitive::kPrimDouble, ComparisonBias::kLtBias, dex_pc); break; } diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index e3683ef0d..ff1232902 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -334,7 +334,7 @@ class DeoptimizationSlowPathARM : public SlowPathCodeARM { #undef __ #define __ down_cast(GetAssembler())-> -inline Condition ARMCondition(IfCondition cond) { +inline Condition ARMSignedOrFPCondition(IfCondition cond) { switch (cond) { case kCondEQ: return EQ; case kCondNE: return NE; @@ -342,24 +342,22 @@ inline Condition ARMCondition(IfCondition cond) { case kCondLE: return LE; case kCondGT: return GT; case kCondGE: return GE; - default: - LOG(FATAL) << "Unknown if condition"; } - return EQ; // Unreachable. + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); } -inline Condition ARMOppositeCondition(IfCondition cond) { +inline Condition ARMUnsignedCondition(IfCondition cond) { switch (cond) { - case kCondEQ: return NE; - case kCondNE: return EQ; - case kCondLT: return GE; - case kCondLE: return GT; - case kCondGT: return LE; - case kCondGE: return LT; - default: - LOG(FATAL) << "Unknown if condition"; + case kCondEQ: return EQ; + case kCondNE: return NE; + case kCondLT: return LO; + case kCondLE: return LS; + case kCondGT: return HI; + case kCondGE: return HS; } - return EQ; // Unreachable. + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); } void CodeGeneratorARM::DumpCoreRegister(std::ostream& stream, int reg) const { @@ -1008,6 +1006,142 @@ void InstructionCodeGeneratorARM::VisitExit(HExit* exit) { UNUSED(exit); } +void InstructionCodeGeneratorARM::GenerateCompareWithImmediate(Register left, int32_t right) { + ShifterOperand operand; + if (GetAssembler()->ShifterOperandCanHold(R0, left, CMP, right, &operand)) { + __ cmp(left, operand); + } else { + Register temp = IP; + __ LoadImmediate(temp, right); + __ cmp(left, ShifterOperand(temp)); + } +} + +void InstructionCodeGeneratorARM::GenerateFPJumps(HCondition* cond, + Label* true_label, + Label* false_label) { + __ vmstat(); // transfer FP status register to ARM APSR. + if (cond->IsFPConditionTrueIfNaN()) { + __ b(true_label, VS); // VS for unordered. + } else if (cond->IsFPConditionFalseIfNaN()) { + __ b(false_label, VS); // VS for unordered. + } + __ b(true_label, ARMSignedOrFPCondition(cond->GetCondition())); +} + +void InstructionCodeGeneratorARM::GenerateLongComparesAndJumps(HCondition* cond, + Label* true_label, + Label* false_label) { + LocationSummary* locations = cond->GetLocations(); + Location left = locations->InAt(0); + Location right = locations->InAt(1); + IfCondition if_cond = cond->GetCondition(); + + Register left_high = left.AsRegisterPairHigh(); + Register left_low = left.AsRegisterPairLow(); + IfCondition true_high_cond = if_cond; + IfCondition false_high_cond = cond->GetOppositeCondition(); + Condition final_condition = ARMUnsignedCondition(if_cond); + + // Set the conditions for the test, remembering that == needs to be + // decided using the low words. + switch (if_cond) { + case kCondEQ: + case kCondNE: + // Nothing to do. + break; + case kCondLT: + false_high_cond = kCondGT; + break; + case kCondLE: + true_high_cond = kCondLT; + break; + case kCondGT: + false_high_cond = kCondLT; + break; + case kCondGE: + true_high_cond = kCondGT; + break; + } + if (right.IsConstant()) { + int64_t value = right.GetConstant()->AsLongConstant()->GetValue(); + int32_t val_low = Low32Bits(value); + int32_t val_high = High32Bits(value); + + GenerateCompareWithImmediate(left_high, val_high); + if (if_cond == kCondNE) { + __ b(true_label, ARMSignedOrFPCondition(true_high_cond)); + } else if (if_cond == kCondEQ) { + __ b(false_label, ARMSignedOrFPCondition(false_high_cond)); + } else { + __ b(true_label, ARMSignedOrFPCondition(true_high_cond)); + __ b(false_label, ARMSignedOrFPCondition(false_high_cond)); + } + // Must be equal high, so compare the lows. + GenerateCompareWithImmediate(left_low, val_low); + } else { + Register right_high = right.AsRegisterPairHigh(); + Register right_low = right.AsRegisterPairLow(); + + __ cmp(left_high, ShifterOperand(right_high)); + if (if_cond == kCondNE) { + __ b(true_label, ARMSignedOrFPCondition(true_high_cond)); + } else if (if_cond == kCondEQ) { + __ b(false_label, ARMSignedOrFPCondition(false_high_cond)); + } else { + __ b(true_label, ARMSignedOrFPCondition(true_high_cond)); + __ b(false_label, ARMSignedOrFPCondition(false_high_cond)); + } + // Must be equal high, so compare the lows. + __ cmp(left_low, ShifterOperand(right_low)); + } + // The last comparison might be unsigned. + __ b(true_label, final_condition); +} + +void InstructionCodeGeneratorARM::GenerateCompareTestAndBranch(HIf* if_instr, + HCondition* condition, + Label* true_target, + Label* false_target, + Label* always_true_target) { + LocationSummary* locations = condition->GetLocations(); + Location left = locations->InAt(0); + Location right = locations->InAt(1); + + // We don't want true_target as a nullptr. + if (true_target == nullptr) { + true_target = always_true_target; + } + bool falls_through = (false_target == nullptr); + + // FP compares don't like null false_targets. + if (false_target == nullptr) { + false_target = codegen_->GetLabelOf(if_instr->IfFalseSuccessor()); + } + + Primitive::Type type = condition->InputAt(0)->GetType(); + switch (type) { + case Primitive::kPrimLong: + GenerateLongComparesAndJumps(condition, true_target, false_target); + break; + case Primitive::kPrimFloat: + __ vcmps(left.AsFpuRegister(), right.AsFpuRegister()); + GenerateFPJumps(condition, true_target, false_target); + break; + case Primitive::kPrimDouble: + __ vcmpd(FromLowSToD(left.AsFpuRegisterPairLow()), + FromLowSToD(right.AsFpuRegisterPairLow())); + GenerateFPJumps(condition, true_target, false_target); + break; + default: + LOG(FATAL) << "Unexpected compare type " << type; + } + + if (!falls_through) { + __ b(false_target); + } +} + void InstructionCodeGeneratorARM::GenerateTestAndBranch(HInstruction* instruction, Label* true_target, Label* false_target, @@ -1033,25 +1167,27 @@ void InstructionCodeGeneratorARM::GenerateTestAndBranch(HInstruction* instructio } else { // Condition has not been materialized, use its inputs as the // comparison and its condition as the branch condition. + Primitive::Type type = + cond->IsCondition() ? cond->InputAt(0)->GetType() : Primitive::kPrimInt; + // Is this a long or FP comparison that has been folded into the HCondition? + if (type == Primitive::kPrimLong || Primitive::IsFloatingPointType(type)) { + // Generate the comparison directly. + GenerateCompareTestAndBranch(instruction->AsIf(), cond->AsCondition(), + true_target, false_target, always_true_target); + return; + } + LocationSummary* locations = cond->GetLocations(); DCHECK(locations->InAt(0).IsRegister()) << locations->InAt(0); Register left = locations->InAt(0).AsRegister(); - if (locations->InAt(1).IsRegister()) { - __ cmp(left, ShifterOperand(locations->InAt(1).AsRegister())); + Location right = locations->InAt(1); + if (right.IsRegister()) { + __ cmp(left, ShifterOperand(right.AsRegister())); } else { - DCHECK(locations->InAt(1).IsConstant()); - HConstant* constant = locations->InAt(1).GetConstant(); - int32_t value = CodeGenerator::GetInt32ValueOf(constant); - ShifterOperand operand; - if (GetAssembler()->ShifterOperandCanHold(R0, left, CMP, value, &operand)) { - __ cmp(left, operand); - } else { - Register temp = IP; - __ LoadImmediate(temp, value); - __ cmp(left, ShifterOperand(temp)); - } + DCHECK(right.IsConstant()); + GenerateCompareWithImmediate(left, CodeGenerator::GetInt32ValueOf(right.GetConstant())); } - __ b(true_target, ARMCondition(cond->AsCondition()->GetCondition())); + __ b(true_target, ARMSignedOrFPCondition(cond->AsCondition()->GetCondition())); } } if (false_target != nullptr) { @@ -1104,37 +1240,88 @@ void InstructionCodeGeneratorARM::VisitDeoptimize(HDeoptimize* deoptimize) { void LocationsBuilderARM::VisitCondition(HCondition* cond) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cond, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1))); - if (cond->NeedsMaterialization()) { - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + // Handle the long/FP comparisons made in instruction simplification. + switch (cond->InputAt(0)->GetType()) { + case Primitive::kPrimLong: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1))); + if (cond->NeedsMaterialization()) { + locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); + } + break; + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + if (cond->NeedsMaterialization()) { + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + } + break; + + default: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1))); + if (cond->NeedsMaterialization()) { + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + } } } void InstructionCodeGeneratorARM::VisitCondition(HCondition* cond) { - if (!cond->NeedsMaterialization()) return; + if (!cond->NeedsMaterialization()) { + return; + } + LocationSummary* locations = cond->GetLocations(); - Register left = locations->InAt(0).AsRegister(); + Location left = locations->InAt(0); + Location right = locations->InAt(1); + Register out = locations->Out().AsRegister(); + Label true_label, false_label; - if (locations->InAt(1).IsRegister()) { - __ cmp(left, ShifterOperand(locations->InAt(1).AsRegister())); - } else { - DCHECK(locations->InAt(1).IsConstant()); - int32_t value = CodeGenerator::GetInt32ValueOf(locations->InAt(1).GetConstant()); - ShifterOperand operand; - if (GetAssembler()->ShifterOperandCanHold(R0, left, CMP, value, &operand)) { - __ cmp(left, operand); - } else { - Register temp = IP; - __ LoadImmediate(temp, value); - __ cmp(left, ShifterOperand(temp)); + switch (cond->InputAt(0)->GetType()) { + default: { + // Integer case. + if (right.IsRegister()) { + __ cmp(left.AsRegister(), ShifterOperand(right.AsRegister())); + } else { + DCHECK(right.IsConstant()); + GenerateCompareWithImmediate(left.AsRegister(), + CodeGenerator::GetInt32ValueOf(right.GetConstant())); + } + __ it(ARMSignedOrFPCondition(cond->GetCondition()), kItElse); + __ mov(locations->Out().AsRegister(), ShifterOperand(1), + ARMSignedOrFPCondition(cond->GetCondition())); + __ mov(locations->Out().AsRegister(), ShifterOperand(0), + ARMSignedOrFPCondition(cond->GetOppositeCondition())); + return; } + case Primitive::kPrimLong: + GenerateLongComparesAndJumps(cond, &true_label, &false_label); + break; + case Primitive::kPrimFloat: + __ vcmps(left.AsFpuRegister(), right.AsFpuRegister()); + GenerateFPJumps(cond, &true_label, &false_label); + break; + case Primitive::kPrimDouble: + __ vcmpd(FromLowSToD(left.AsFpuRegisterPairLow()), + FromLowSToD(right.AsFpuRegisterPairLow())); + GenerateFPJumps(cond, &true_label, &false_label); + break; } - __ it(ARMCondition(cond->GetCondition()), kItElse); - __ mov(locations->Out().AsRegister(), ShifterOperand(1), - ARMCondition(cond->GetCondition())); - __ mov(locations->Out().AsRegister(), ShifterOperand(0), - ARMOppositeCondition(cond->GetCondition())); + + // Convert the jumps into the result. + Label done_label; + + // False case: result = 0. + __ Bind(&false_label); + __ LoadImmediate(out, 0); + __ b(&done_label); + + // True case: result = 1. + __ Bind(&true_label); + __ LoadImmediate(out, 1); + __ Bind(&done_label); } void LocationsBuilderARM::VisitEqual(HEqual* comp) { @@ -2913,7 +3100,7 @@ void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) { ShifterOperand(right.AsRegisterPairHigh())); // Signed compare. __ b(&less, LT); __ b(&greater, GT); - // Do LoadImmediate before any `cmp`, as LoadImmediate might affect the status flags. + // Do LoadImmediate before the last `cmp`, as LoadImmediate might affect the status flags. __ LoadImmediate(out, 0); __ cmp(left.AsRegisterPairLow(), ShifterOperand(right.AsRegisterPairLow())); // Unsigned compare. @@ -2936,7 +3123,7 @@ void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) { LOG(FATAL) << "Unexpected compare type " << type; } __ b(&done, EQ); - __ b(&less, CC); // CC is for both: unsigned compare for longs and 'less than' for floats. + __ b(&less, LO); // LO is for both: unsigned compare for longs and 'less than' for floats. __ Bind(&greater); __ LoadImmediate(out, 1); @@ -3710,7 +3897,7 @@ void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) { Register length = locations->InAt(1).AsRegister(); __ cmp(index, ShifterOperand(length)); - __ b(slow_path->GetEntryLabel(), CS); + __ b(slow_path->GetEntryLabel(), HS); } void CodeGeneratorARM::MarkGCCard(Register temp, diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 1d10293b5..53bd766dd 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -207,6 +207,14 @@ class InstructionCodeGeneratorARM : public HGraphVisitor { Label* true_target, Label* false_target, Label* always_true_target); + void GenerateCompareWithImmediate(Register left, int32_t right); + void GenerateCompareTestAndBranch(HIf* if_instr, + HCondition* condition, + Label* true_target, + Label* false_target, + Label* always_true_target); + void GenerateFPJumps(HCondition* cond, Label* true_label, Label* false_label); + void GenerateLongComparesAndJumps(HCondition* cond, Label* true_label, Label* false_label); void DivRemOneOrMinusOne(HBinaryOperation* instruction); void DivRemByPowerOfTwo(HBinaryOperation* instruction); void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index be71443b1..fd4bd1803 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -344,7 +344,7 @@ class DeoptimizationSlowPathX86 : public SlowPathCodeX86 { #undef __ #define __ down_cast(GetAssembler())-> -inline Condition X86Condition(IfCondition cond) { +inline Condition X86SignedCondition(IfCondition cond) { switch (cond) { case kCondEQ: return kEqual; case kCondNE: return kNotEqual; @@ -352,10 +352,22 @@ inline Condition X86Condition(IfCondition cond) { case kCondLE: return kLessEqual; case kCondGT: return kGreater; case kCondGE: return kGreaterEqual; - default: - LOG(FATAL) << "Unknown if condition"; } - return kEqual; + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); +} + +inline Condition X86UnsignedOrFPCondition(IfCondition cond) { + switch (cond) { + case kCondEQ: return kEqual; + case kCondNE: return kNotEqual; + case kCondLT: return kBelow; + case kCondLE: return kBelowEqual; + case kCondGT: return kAbove; + case kCondGE: return kAboveEqual; + } + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); } void CodeGeneratorX86::DumpCoreRegister(std::ostream& stream, int reg) const { @@ -892,46 +904,12 @@ void InstructionCodeGeneratorX86::VisitExit(HExit* exit) { void InstructionCodeGeneratorX86::GenerateFPJumps(HCondition* cond, Label* true_label, Label* false_label) { - bool gt_bias = cond->IsGtBias(); - IfCondition if_cond = cond->GetCondition(); - Condition ccode = X86Condition(if_cond); - switch (if_cond) { - case kCondEQ: - if (!gt_bias) { - __ j(kParityEven, false_label); - } - break; - case kCondNE: - if (!gt_bias) { - __ j(kParityEven, true_label); - } - break; - case kCondLT: - if (gt_bias) { - __ j(kParityEven, false_label); - } - ccode = kBelow; - break; - case kCondLE: - if (gt_bias) { - __ j(kParityEven, false_label); - } - ccode = kBelowEqual; - break; - case kCondGT: - if (gt_bias) { - __ j(kParityEven, true_label); - } - ccode = kAbove; - break; - case kCondGE: - if (gt_bias) { - __ j(kParityEven, true_label); - } - ccode = kAboveEqual; - break; + if (cond->IsFPConditionTrueIfNaN()) { + __ j(kUnordered, true_label); + } else if (cond->IsFPConditionFalseIfNaN()) { + __ j(kUnordered, false_label); } - __ j(ccode, true_label); + __ j(X86UnsignedOrFPCondition(cond->GetCondition()), true_label); } void InstructionCodeGeneratorX86::GenerateLongComparesAndJumps(HCondition* cond, @@ -942,43 +920,37 @@ void InstructionCodeGeneratorX86::GenerateLongComparesAndJumps(HCondition* cond, Location right = locations->InAt(1); IfCondition if_cond = cond->GetCondition(); - Register left_low = left.AsRegisterPairLow(); Register left_high = left.AsRegisterPairHigh(); + Register left_low = left.AsRegisterPairLow(); IfCondition true_high_cond = if_cond; IfCondition false_high_cond = cond->GetOppositeCondition(); - Condition final_condition = X86Condition(if_cond); + Condition final_condition = X86UnsignedOrFPCondition(if_cond); // Set the conditions for the test, remembering that == needs to be // decided using the low words. switch (if_cond) { case kCondEQ: - false_high_cond = kCondNE; - break; case kCondNE: - false_high_cond = kCondEQ; + // Nothing to do. break; case kCondLT: false_high_cond = kCondGT; - final_condition = kBelow; break; case kCondLE: true_high_cond = kCondLT; - final_condition = kBelowEqual; break; case kCondGT: false_high_cond = kCondLT; - final_condition = kAbove; break; case kCondGE: true_high_cond = kCondGT; - final_condition = kAboveEqual; break; } if (right.IsConstant()) { int64_t value = right.GetConstant()->AsLongConstant()->GetValue(); - int32_t val_low = Low32Bits(value); int32_t val_high = High32Bits(value); + int32_t val_low = Low32Bits(value); if (val_high == 0) { __ testl(left_high, left_high); @@ -986,12 +958,12 @@ void InstructionCodeGeneratorX86::GenerateLongComparesAndJumps(HCondition* cond, __ cmpl(left_high, Immediate(val_high)); } if (if_cond == kCondNE) { - __ j(X86Condition(true_high_cond), true_label); + __ j(X86SignedCondition(true_high_cond), true_label); } else if (if_cond == kCondEQ) { - __ j(X86Condition(false_high_cond), false_label); + __ j(X86SignedCondition(false_high_cond), false_label); } else { - __ j(X86Condition(true_high_cond), true_label); - __ j(X86Condition(false_high_cond), false_label); + __ j(X86SignedCondition(true_high_cond), true_label); + __ j(X86SignedCondition(false_high_cond), false_label); } // Must be equal high, so compare the lows. if (val_low == 0) { @@ -1000,17 +972,17 @@ void InstructionCodeGeneratorX86::GenerateLongComparesAndJumps(HCondition* cond, __ cmpl(left_low, Immediate(val_low)); } } else { - Register right_low = right.AsRegisterPairLow(); Register right_high = right.AsRegisterPairHigh(); + Register right_low = right.AsRegisterPairLow(); __ cmpl(left_high, right_high); if (if_cond == kCondNE) { - __ j(X86Condition(true_high_cond), true_label); + __ j(X86SignedCondition(true_high_cond), true_label); } else if (if_cond == kCondEQ) { - __ j(X86Condition(false_high_cond), false_label); + __ j(X86SignedCondition(false_high_cond), false_label); } else { - __ j(X86Condition(true_high_cond), true_label); - __ j(X86Condition(false_high_cond), false_label); + __ j(X86SignedCondition(true_high_cond), true_label); + __ j(X86SignedCondition(false_high_cond), false_label); } // Must be equal high, so compare the lows. __ cmpl(left_low, right_low); @@ -1045,12 +1017,10 @@ void InstructionCodeGeneratorX86::GenerateCompareTestAndBranch(HIf* if_instr, GenerateLongComparesAndJumps(condition, true_target, false_target); break; case Primitive::kPrimFloat: - DCHECK(right.IsFpuRegister()); __ ucomiss(left.AsFpuRegister(), right.AsFpuRegister()); GenerateFPJumps(condition, true_target, false_target); break; case Primitive::kPrimDouble: - DCHECK(right.IsFpuRegister()); __ ucomisd(left.AsFpuRegister(), right.AsFpuRegister()); GenerateFPJumps(condition, true_target, false_target); break; @@ -1080,7 +1050,7 @@ void InstructionCodeGeneratorX86::GenerateTestAndBranch(HInstruction* instructio DCHECK_EQ(cond_value, 0); } } else { - bool materialized = + bool is_materialized = !cond->IsCondition() || cond->AsCondition()->NeedsMaterialization(); // Moves do not affect the eflags register, so if the condition is // evaluated just before the if, we don't need to evaluate it @@ -1089,8 +1059,8 @@ void InstructionCodeGeneratorX86::GenerateTestAndBranch(HInstruction* instructio Primitive::Type type = cond->IsCondition() ? cond->InputAt(0)->GetType() : Primitive::kPrimInt; bool eflags_set = cond->IsCondition() && cond->AsCondition()->IsBeforeWhenDisregardMoves(instruction) - && type == Primitive::kPrimInt; - if (materialized) { + && (type != Primitive::kPrimLong && !Primitive::IsFloatingPointType(type)); + if (is_materialized) { if (!eflags_set) { // Materialized condition, compare against 0. Location lhs = instruction->GetLocations()->InAt(0); @@ -1101,9 +1071,12 @@ void InstructionCodeGeneratorX86::GenerateTestAndBranch(HInstruction* instructio } __ j(kNotEqual, true_target); } else { - __ j(X86Condition(cond->AsCondition()->GetCondition()), true_target); + __ j(X86SignedCondition(cond->AsCondition()->GetCondition()), true_target); } } else { + // Condition has not been materialized, use its inputs as the + // comparison and its condition as the branch condition. + // Is this a long or FP comparison that has been folded into the HCondition? if (type == Primitive::kPrimLong || Primitive::IsFloatingPointType(type)) { // Generate the comparison directly. @@ -1114,6 +1087,7 @@ void InstructionCodeGeneratorX86::GenerateTestAndBranch(HInstruction* instructio always_true_target); return; } + Location lhs = cond->GetLocations()->InAt(0); Location rhs = cond->GetLocations()->InAt(1); // LHS is guaranteed to be in a register (see @@ -1130,7 +1104,7 @@ void InstructionCodeGeneratorX86::GenerateTestAndBranch(HInstruction* instructio } else { __ cmpl(lhs.AsRegister(), Address(ESP, rhs.GetStackIndex())); } - __ j(X86Condition(cond->AsCondition()->GetCondition()), true_target); + __ j(X86SignedCondition(cond->AsCondition()->GetCondition()), true_target); } } if (false_target != nullptr) { @@ -1288,7 +1262,7 @@ void InstructionCodeGeneratorX86::VisitCondition(HCondition* cond) { } else { __ cmpl(lhs.AsRegister(), Address(ESP, rhs.GetStackIndex())); } - __ setb(X86Condition(cond->GetCondition()), reg); + __ setb(X86SignedCondition(cond->GetCondition()), reg); return; } case Primitive::kPrimLong: @@ -1307,12 +1281,12 @@ void InstructionCodeGeneratorX86::VisitCondition(HCondition* cond) { // Convert the jumps into the result. Label done_label; - // false case: result = 0; + // False case: result = 0. __ Bind(&false_label); __ xorl(reg, reg); __ jmp(&done_label); - // True case: result = 1 + // True case: result = 1. __ Bind(&true_label); __ movl(reg, Immediate(1)); __ Bind(&done_label); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index ddaa60db5..ae7bcc8f0 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -363,7 +363,7 @@ class DeoptimizationSlowPathX86_64 : public SlowPathCodeX86_64 { #undef __ #define __ down_cast(GetAssembler())-> -inline Condition X86_64Condition(IfCondition cond) { +inline Condition X86_64IntegerCondition(IfCondition cond) { switch (cond) { case kCondEQ: return kEqual; case kCondNE: return kNotEqual; @@ -371,10 +371,22 @@ inline Condition X86_64Condition(IfCondition cond) { case kCondLE: return kLessEqual; case kCondGT: return kGreater; case kCondGE: return kGreaterEqual; - default: - LOG(FATAL) << "Unknown if condition"; } - return kEqual; + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); +} + +inline Condition X86_64FPCondition(IfCondition cond) { + switch (cond) { + case kCondEQ: return kEqual; + case kCondNE: return kNotEqual; + case kCondLT: return kBelow; + case kCondLE: return kBelowEqual; + case kCondGT: return kAbove; + case kCondGE: return kAboveEqual; + }; + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); } void CodeGeneratorX86_64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, @@ -836,46 +848,12 @@ void InstructionCodeGeneratorX86_64::VisitExit(HExit* exit) { void InstructionCodeGeneratorX86_64::GenerateFPJumps(HCondition* cond, Label* true_label, Label* false_label) { - bool gt_bias = cond->IsGtBias(); - IfCondition if_cond = cond->GetCondition(); - Condition ccode = X86_64Condition(if_cond); - switch (if_cond) { - case kCondEQ: - if (!gt_bias) { - __ j(kParityEven, false_label); - } - break; - case kCondNE: - if (!gt_bias) { - __ j(kParityEven, true_label); - } - break; - case kCondLT: - if (gt_bias) { - __ j(kParityEven, false_label); - } - ccode = kBelow; - break; - case kCondLE: - if (gt_bias) { - __ j(kParityEven, false_label); - } - ccode = kBelowEqual; - break; - case kCondGT: - if (gt_bias) { - __ j(kParityEven, true_label); - } - ccode = kAbove; - break; - case kCondGE: - if (gt_bias) { - __ j(kParityEven, true_label); - } - ccode = kAboveEqual; - break; + if (cond->IsFPConditionTrueIfNaN()) { + __ j(kUnordered, true_label); + } else if (cond->IsFPConditionFalseIfNaN()) { + __ j(kUnordered, false_label); } - __ j(ccode, true_label); + __ j(X86_64FPCondition(cond->GetCondition()), true_label); } void InstructionCodeGeneratorX86_64::GenerateCompareTestAndBranch(HIf* if_instr, @@ -911,7 +889,7 @@ void InstructionCodeGeneratorX86_64::GenerateCompareTestAndBranch(HIf* if_instr, __ cmpq(left_reg, Immediate(static_cast(value))); } } else { - // Value won't fit in an 32-bit integer. + // Value won't fit in a 32-bit integer. __ cmpq(left_reg, codegen_->LiteralInt64Address(value)); } } else if (right.IsDoubleStackSlot()) { @@ -919,7 +897,7 @@ void InstructionCodeGeneratorX86_64::GenerateCompareTestAndBranch(HIf* if_instr, } else { __ cmpq(left_reg, right.AsRegister()); } - __ j(X86_64Condition(condition->GetCondition()), true_target); + __ j(X86_64IntegerCondition(condition->GetCondition()), true_target); break; } case Primitive::kPrimFloat: { @@ -978,7 +956,7 @@ void InstructionCodeGeneratorX86_64::GenerateTestAndBranch(HInstruction* instruc DCHECK_EQ(cond_value, 0); } } else { - bool materialized = + bool is_materialized = !cond->IsCondition() || cond->AsCondition()->NeedsMaterialization(); // Moves do not affect the eflags register, so if the condition is // evaluated just before the if, we don't need to evaluate it @@ -989,7 +967,7 @@ void InstructionCodeGeneratorX86_64::GenerateTestAndBranch(HInstruction* instruc && cond->AsCondition()->IsBeforeWhenDisregardMoves(instruction) && !Primitive::IsFloatingPointType(type); - if (materialized) { + if (is_materialized) { if (!eflags_set) { // Materialized condition, compare against 0. Location lhs = instruction->GetLocations()->InAt(0); @@ -1001,16 +979,20 @@ void InstructionCodeGeneratorX86_64::GenerateTestAndBranch(HInstruction* instruc } __ j(kNotEqual, true_target); } else { - __ j(X86_64Condition(cond->AsCondition()->GetCondition()), true_target); + __ j(X86_64IntegerCondition(cond->AsCondition()->GetCondition()), true_target); } } else { + // Condition has not been materialized, use its inputs as the + // comparison and its condition as the branch condition. + // Is this a long or FP comparison that has been folded into the HCondition? if (type == Primitive::kPrimLong || Primitive::IsFloatingPointType(type)) { - // Generate the comparison directly + // Generate the comparison directly. GenerateCompareTestAndBranch(instruction->AsIf(), cond->AsCondition(), true_target, false_target, always_true_target); return; } + Location lhs = cond->GetLocations()->InAt(0); Location rhs = cond->GetLocations()->InAt(1); if (rhs.IsRegister()) { @@ -1026,7 +1008,7 @@ void InstructionCodeGeneratorX86_64::GenerateTestAndBranch(HInstruction* instruc __ cmpl(lhs.AsRegister(), Address(CpuRegister(RSP), rhs.GetStackIndex())); } - __ j(X86_64Condition(cond->AsCondition()->GetCondition()), true_target); + __ j(X86_64IntegerCondition(cond->AsCondition()->GetCondition()), true_target); } } if (false_target != nullptr) { @@ -1175,7 +1157,7 @@ void InstructionCodeGeneratorX86_64::VisitCondition(HCondition* cond) { } else { __ cmpl(lhs.AsRegister(), Address(CpuRegister(RSP), rhs.GetStackIndex())); } - __ setcc(X86_64Condition(cond->GetCondition()), reg); + __ setcc(X86_64IntegerCondition(cond->GetCondition()), reg); return; case Primitive::kPrimLong: // Clear output register: setcc only sets the low byte. @@ -1198,7 +1180,7 @@ void InstructionCodeGeneratorX86_64::VisitCondition(HCondition* cond) { } else { __ cmpq(lhs.AsRegister(), Address(CpuRegister(RSP), rhs.GetStackIndex())); } - __ setcc(X86_64Condition(cond->GetCondition()), reg); + __ setcc(X86_64IntegerCondition(cond->GetCondition()), reg); return; case Primitive::kPrimFloat: { XmmRegister lhs_reg = lhs.AsFpuRegister(); @@ -1231,12 +1213,12 @@ void InstructionCodeGeneratorX86_64::VisitCondition(HCondition* cond) { // Convert the jumps into the result. Label done_label; - // false case: result = 0; + // False case: result = 0. __ Bind(&false_label); __ xorl(reg, reg); __ jmp(&done_label); - // True case: result = 1 + // True case: result = 1. __ Bind(&true_label); __ movl(reg, Immediate(1)); __ Bind(&done_label); diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 337cf5b52..017b6781a 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -517,10 +517,10 @@ void InstructionSimplifierVisitor::VisitLessThanOrEqual(HLessThanOrEqual* condit void InstructionSimplifierVisitor::VisitCondition(HCondition* condition) { // Try to fold an HCompare into this HCondition. - // This simplification is currently only supported on x86 and x86_64. - // TODO: Implement it for ARM, ARM64 and MIPS64. + // This simplification is currently only supported on x86, x86_64 and ARM. + // TODO: Implement it for ARM64 and MIPS64. InstructionSet instruction_set = GetGraph()->GetInstructionSet(); - if (instruction_set != kX86 && instruction_set != kX86_64) { + if (instruction_set != kX86 && instruction_set != kX86_64 && instruction_set != kThumb2) { return; } diff --git a/compiler/optimizing/instruction_simplifier.h b/compiler/optimizing/instruction_simplifier.h index faee2dd91..cc4b6f6ad 100644 --- a/compiler/optimizing/instruction_simplifier.h +++ b/compiler/optimizing/instruction_simplifier.h @@ -31,7 +31,7 @@ class InstructionSimplifier : public HOptimization { InstructionSimplifier(HGraph* graph, OptimizingCompilerStats* stats = nullptr, const char* name = kInstructionSimplifierPassName) - : HOptimization(graph, name, stats) {} + : HOptimization(graph, name, stats) {} static constexpr const char* kInstructionSimplifierPassName = "instruction_simplifier"; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 0db1ac32b..d19170195 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -2149,7 +2149,7 @@ class HBinaryOperation : public HExpression<2> { // The comparison bias applies for floating point operations and indicates how NaN // comparisons are treated: -enum ComparisonBias { +enum class ComparisonBias { kNoBias, // bias is not applicable (i.e. for long operation) kGtBias, // return 1 for NaN comparisons kLtBias, // return -1 for NaN comparisons @@ -2160,7 +2160,7 @@ class HCondition : public HBinaryOperation { HCondition(HInstruction* first, HInstruction* second) : HBinaryOperation(Primitive::kPrimBoolean, first, second), needs_materialization_(true), - bias_(kNoBias) {} + bias_(ComparisonBias::kNoBias) {} bool NeedsMaterialization() const { return needs_materialization_; } void ClearNeedsMaterialization() { needs_materialization_ = false; } @@ -2175,7 +2175,7 @@ class HCondition : public HBinaryOperation { virtual IfCondition GetOppositeCondition() const = 0; - bool IsGtBias() { return bias_ == kGtBias; } + bool IsGtBias() const { return bias_ == ComparisonBias::kGtBias; } void SetBias(ComparisonBias bias) { bias_ = bias; } @@ -2183,6 +2183,18 @@ class HCondition : public HBinaryOperation { return bias_ == other->AsCondition()->bias_; } + bool IsFPConditionTrueIfNaN() const { + DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())); + IfCondition if_cond = GetCondition(); + return IsGtBias() ? ((if_cond == kCondGT) || (if_cond == kCondGE)) : (if_cond == kCondNE); + } + + bool IsFPConditionFalseIfNaN() const { + DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())); + IfCondition if_cond = GetCondition(); + return IsGtBias() ? ((if_cond == kCondLT) || (if_cond == kCondLE)) : (if_cond == kCondEQ); + } + private: // For register allocation purposes, returns whether this instruction needs to be // materialized (that is, not just be in the processor flags). @@ -2390,7 +2402,7 @@ class HCompare : public HBinaryOperation { ComparisonBias GetBias() const { return bias_; } - bool IsGtBias() { return bias_ == kGtBias; } + bool IsGtBias() { return bias_ == ComparisonBias::kGtBias; } uint32_t GetDexPc() const { return dex_pc_; } diff --git a/compiler/utils/arm/constants_arm.h b/compiler/utils/arm/constants_arm.h index 1513296c2..6b4daed90 100644 --- a/compiler/utils/arm/constants_arm.h +++ b/compiler/utils/arm/constants_arm.h @@ -32,8 +32,9 @@ namespace arm { // Defines constants and accessor classes to assemble, disassemble and // simulate ARM instructions. // -// Section references in the code refer to the "ARM Architecture Reference -// Manual" from July 2005 (available at http://www.arm.com/miscPDFs/14128.pdf) +// Section references in the code refer to the "ARM Architecture +// Reference Manual ARMv7-A and ARMv7-R edition", issue C.b (24 July +// 2012). // // Constants for specific fields are defined in their respective named enums. // General constants are in an anonymous enum in class Instr. @@ -97,26 +98,32 @@ enum DRegister { // private marker to avoid generate-operator-out.py from proce std::ostream& operator<<(std::ostream& os, const DRegister& rhs); -// Values for the condition field as defined in section A3.2. +// Values for the condition field as defined in Table A8-1 "Condition +// codes" (refer to Section A8.3 "Conditional execution"). enum Condition { // private marker to avoid generate-operator-out.py from processing. kNoCondition = -1, - EQ = 0, // equal - NE = 1, // not equal - CS = 2, // carry set/unsigned higher or same - CC = 3, // carry clear/unsigned lower - MI = 4, // minus/negative - PL = 5, // plus/positive or zero - VS = 6, // overflow - VC = 7, // no overflow - HI = 8, // unsigned higher - LS = 9, // unsigned lower or same - GE = 10, // signed greater than or equal - LT = 11, // signed less than - GT = 12, // signed greater than - LE = 13, // signed less than or equal - AL = 14, // always (unconditional) - kSpecialCondition = 15, // special condition (refer to section A3.2.1) + // Meaning (integer) | Meaning (floating-point) + // ---------------------------------------+----------------------------------------- + EQ = 0, // Equal | Equal + NE = 1, // Not equal | Not equal, or unordered + CS = 2, // Carry set | Greater than, equal, or unordered + CC = 3, // Carry clear | Less than + MI = 4, // Minus, negative | Less than + PL = 5, // Plus, positive or zero | Greater than, equal, or unordered + VS = 6, // Overflow | Unordered (i.e. at least one NaN operand) + VC = 7, // No overflow | Not unordered + HI = 8, // Unsigned higher | Greater than, or unordered + LS = 9, // Unsigned lower or same | Less than or equal + GE = 10, // Signed greater than or equal | Greater than or equal + LT = 11, // Signed less than | Less than, or unordered + GT = 12, // Signed greater than | Greater than + LE = 13, // Signed less than or equal | Less than, equal, or unordered + AL = 14, // Always (unconditional) | Always (unconditional) + kSpecialCondition = 15, // Special condition (refer to Section A8.3 "Conditional execution"). kMaxCondition = 16, + + HS = CS, // HS (unsigned higher or same) is a synonym for CS. + LO = CC // LO (unsigned lower) is a synonym for CC. }; std::ostream& operator<<(std::ostream& os, const Condition& rhs); -- 2.11.0