diff options
| author | 2016-02-06 13:58:35 -0500 | |
|---|---|---|
| committer | 2016-02-16 10:10:44 -0500 | |
| commit | 0c5b18edd1308975804ccf29a02a130a7b6f7fa7 (patch) | |
| tree | 58f60a2f93973460f1602f007ffdb2c6d7c577bf | |
| parent | 7eca244e79480f2ecea341598524a53273959c2b (diff) | |
Support CMOV for x86 Select
If possible, generate CMOV to implement HSelect. Tricky cases are a
long or FP condition (no single CC generated), FP inputs (no FP CMOV)
and when the condition is a boolean or not emitted at the use site.
In these cases, keep using the existing HSelect code.
Change-Id: I4ff1e152b8ef126fbbabeb3316e9e2b6a6b74aeb
Signed-off-by: Mark Mendell <mark.p.mendell@intel.com>
| -rw-r--r-- | compiler/Android.mk | 2 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_x86.cc | 146 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_x86.h | 1 | ||||
| -rw-r--r-- | compiler/optimizing/prepare_for_register_allocation.cc | 10 | ||||
| -rw-r--r-- | test/566-checker-codegen-select/src/Main.java | 7 | ||||
| -rw-r--r-- | test/570-checker-select/src/Main.java | 58 |
6 files changed, 176 insertions, 48 deletions
diff --git a/compiler/Android.mk b/compiler/Android.mk index b16494248f..159e9cfb5e 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -90,7 +90,6 @@ LIBART_COMPILER_SRC_FILES := \ optimizing/optimization.cc \ optimizing/optimizing_compiler.cc \ optimizing/parallel_move_resolver.cc \ - optimizing/pc_relative_fixups_x86.cc \ optimizing/prepare_for_register_allocation.cc \ optimizing/reference_type_propagation.cc \ optimizing/register_allocator.cc \ @@ -182,6 +181,7 @@ LIBART_COMPILER_SRC_FILES_x86 := \ linker/x86/relative_patcher_x86_base.cc \ optimizing/code_generator_x86.cc \ optimizing/intrinsics_x86.cc \ + optimizing/pc_relative_fixups_x86.cc \ utils/x86/assembler_x86.cc \ utils/x86/managed_register_x86.cc \ diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 07edd97c1f..ae5679eaf5 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -26,7 +26,6 @@ #include "intrinsics_x86.h" #include "mirror/array-inl.h" #include "mirror/class-inl.h" -#include "pc_relative_fixups_x86.h" #include "thread.h" #include "utils/assembler.h" #include "utils/stack_checks.h" @@ -1505,30 +1504,131 @@ void InstructionCodeGeneratorX86::VisitDeoptimize(HDeoptimize* deoptimize) { /* false_target */ nullptr); } +static bool SelectCanUseCMOV(HSelect* select) { + // There are no conditional move instructions for XMMs. + if (Primitive::IsFloatingPointType(select->GetType())) { + return false; + } + + // A FP condition doesn't generate the single CC that we need. + // In 32 bit mode, a long condition doesn't generate a single CC either. + HInstruction* condition = select->GetCondition(); + if (condition->IsCondition()) { + Primitive::Type compare_type = condition->InputAt(0)->GetType(); + if (compare_type == Primitive::kPrimLong || + Primitive::IsFloatingPointType(compare_type)) { + return false; + } + } + + // We can generate a CMOV for this Select. + return true; +} + void LocationsBuilderX86::VisitSelect(HSelect* select) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select); - Primitive::Type select_type = select->GetType(); - HInstruction* cond = select->GetCondition(); - - if (Primitive::IsFloatingPointType(select_type)) { + if (Primitive::IsFloatingPointType(select->GetType())) { locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::Any()); } else { locations->SetInAt(0, Location::RequiresRegister()); + if (SelectCanUseCMOV(select)) { + if (select->InputAt(1)->IsConstant()) { + // Cmov can't handle a constant value. + locations->SetInAt(1, Location::RequiresRegister()); + } else { + locations->SetInAt(1, Location::Any()); + } + } else { + locations->SetInAt(1, Location::Any()); + } } - locations->SetInAt(1, Location::Any()); - if (IsBooleanValueOrMaterializedCondition(cond)) { - locations->SetInAt(2, Location::Any()); + if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) { + locations->SetInAt(2, Location::RequiresRegister()); } locations->SetOut(Location::SameAsFirstInput()); } +void InstructionCodeGeneratorX86::GenerateIntCompare(Location lhs, Location rhs) { + Register lhs_reg = lhs.AsRegister<Register>(); + if (rhs.IsConstant()) { + int32_t value = CodeGenerator::GetInt32ValueOf(rhs.GetConstant()); + codegen_->Compare32BitValue(lhs_reg, value); + } else if (rhs.IsStackSlot()) { + __ cmpl(lhs_reg, Address(ESP, rhs.GetStackIndex())); + } else { + __ cmpl(lhs_reg, rhs.AsRegister<Register>()); + } +} + void InstructionCodeGeneratorX86::VisitSelect(HSelect* select) { LocationSummary* locations = select->GetLocations(); - NearLabel false_target; - GenerateTestAndBranch<NearLabel>( - select, /* condition_input_index */ 2, /* true_target */ nullptr, &false_target); - codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType()); - __ Bind(&false_target); + DCHECK(locations->InAt(0).Equals(locations->Out())); + if (SelectCanUseCMOV(select)) { + // If both the condition and the source types are integer, we can generate + // a CMOV to implement Select. + + HInstruction* select_condition = select->GetCondition(); + Condition cond = kNotEqual; + + // Figure out how to test the 'condition'. + if (select_condition->IsCondition()) { + HCondition* condition = select_condition->AsCondition(); + if (!condition->IsEmittedAtUseSite()) { + // This was a previously materialized condition. + // Can we use the existing condition code? + if (AreEflagsSetFrom(condition, select)) { + // Materialization was the previous instruction. Condition codes are right. + cond = X86Condition(condition->GetCondition()); + } else { + // No, we have to recreate the condition code. + Register cond_reg = locations->InAt(2).AsRegister<Register>(); + __ testl(cond_reg, cond_reg); + } + } else { + // We can't handle FP or long here. + DCHECK_NE(condition->InputAt(0)->GetType(), Primitive::kPrimLong); + DCHECK(!Primitive::IsFloatingPointType(condition->InputAt(0)->GetType())); + LocationSummary* cond_locations = condition->GetLocations(); + GenerateIntCompare(cond_locations->InAt(0), cond_locations->InAt(1)); + cond = X86Condition(condition->GetCondition()); + } + } else { + // Must be a boolean condition, which needs to be compared to 0. + Register cond_reg = locations->InAt(2).AsRegister<Register>(); + __ testl(cond_reg, cond_reg); + } + + // If the condition is true, overwrite the output, which already contains false. + Location false_loc = locations->InAt(0); + Location true_loc = locations->InAt(1); + if (select->GetType() == Primitive::kPrimLong) { + // 64 bit conditional move. + Register false_high = false_loc.AsRegisterPairHigh<Register>(); + Register false_low = false_loc.AsRegisterPairLow<Register>(); + if (true_loc.IsRegisterPair()) { + __ cmovl(cond, false_high, true_loc.AsRegisterPairHigh<Register>()); + __ cmovl(cond, false_low, true_loc.AsRegisterPairLow<Register>()); + } else { + __ cmovl(cond, false_high, Address(ESP, true_loc.GetHighStackIndex(kX86WordSize))); + __ cmovl(cond, false_low, Address(ESP, true_loc.GetStackIndex())); + } + } else { + // 32 bit conditional move. + Register false_reg = false_loc.AsRegister<Register>(); + if (true_loc.IsRegister()) { + __ cmovl(cond, false_reg, true_loc.AsRegister<Register>()); + } else { + __ cmovl(cond, false_reg, Address(ESP, true_loc.GetStackIndex())); + } + } + } else { + NearLabel false_target; + GenerateTestAndBranch<NearLabel>( + select, /* condition_input_index */ 2, /* true_target */ nullptr, &false_target); + codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType()); + __ Bind(&false_target); + } } void LocationsBuilderX86::VisitNativeDebugInfo(HNativeDebugInfo* info) { @@ -1642,15 +1742,7 @@ void InstructionCodeGeneratorX86::HandleCondition(HCondition* cond) { // Clear output register: setb only sets the low byte. __ xorl(reg, reg); - - if (rhs.IsRegister()) { - __ cmpl(lhs.AsRegister<Register>(), rhs.AsRegister<Register>()); - } else if (rhs.IsConstant()) { - int32_t constant = CodeGenerator::GetInt32ValueOf(rhs.GetConstant()); - codegen_->Compare32BitValue(lhs.AsRegister<Register>(), constant); - } else { - __ cmpl(lhs.AsRegister<Register>(), Address(ESP, rhs.GetStackIndex())); - } + GenerateIntCompare(lhs, rhs); __ setb(X86Condition(cond->GetCondition()), reg); return; } @@ -4128,15 +4220,7 @@ void InstructionCodeGeneratorX86::VisitCompare(HCompare* compare) { switch (compare->InputAt(0)->GetType()) { case Primitive::kPrimInt: { - Register left_reg = left.AsRegister<Register>(); - if (right.IsConstant()) { - int32_t value = right.GetConstant()->AsIntConstant()->GetValue(); - codegen_->Compare32BitValue(left_reg, value); - } else if (right.IsStackSlot()) { - __ cmpl(left_reg, Address(ESP, right.GetStackIndex())); - } else { - __ cmpl(left_reg, right.AsRegister<Register>()); - } + GenerateIntCompare(left, right); break; } case Primitive::kPrimLong: { diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 2fb6d60ad5..63e9b2fc9c 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -297,6 +297,7 @@ class InstructionCodeGeneratorX86 : public InstructionCodeGenerator { HBasicBlock* default_block); void GenerateFPCompare(Location lhs, Location rhs, HInstruction* insn, bool is_double); + void GenerateIntCompare(Location lhs, Location rhs); X86Assembler* const assembler_; CodeGeneratorX86* const codegen_; diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index 324d84f3db..0ad104eaa7 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -138,15 +138,7 @@ bool PrepareForRegisterAllocation::CanEmitConditionAt(HCondition* condition, } if (user->IsSelect() && user->AsSelect()->GetCondition() == condition) { - if (GetGraph()->GetInstructionSet() == kX86) { - // Long values and long condition inputs result in 8 required core registers. - // We don't have that many on x86. Materialize the condition in such case. - return user->GetType() != Primitive::kPrimLong || - condition->InputAt(1)->GetType() != Primitive::kPrimLong || - condition->InputAt(1)->IsConstant(); - } else { - return true; - } + return true; } return false; diff --git a/test/566-checker-codegen-select/src/Main.java b/test/566-checker-codegen-select/src/Main.java index 3a1b3fcf85..e215ab0309 100644 --- a/test/566-checker-codegen-select/src/Main.java +++ b/test/566-checker-codegen-select/src/Main.java @@ -20,13 +20,6 @@ public class Main { /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{j\d+}},{{j\d+}}] /// CHECK-NEXT: Select [{{j\d+}},{{j\d+}},<<Cond>>] - // Condition must be materialized on X86 because it would need too many - // registers otherwise. - /// CHECK-START-X86: long Main.$noinline$longSelect(long) disassembly (after) - /// CHECK: LessThanOrEqual - /// CHECK-NEXT: cmp - /// CHECK: Select - public long $noinline$longSelect(long param) { if (doThrow) { throw new Error(); } long val_true = longB; diff --git a/test/570-checker-select/src/Main.java b/test/570-checker-select/src/Main.java index 8a4cf603af..59741d6d05 100644 --- a/test/570-checker-select/src/Main.java +++ b/test/570-checker-select/src/Main.java @@ -29,6 +29,11 @@ public class Main { /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>] /// CHECK: cmovnz/ne + /// CHECK-START-X86: int Main.BoolCond_IntVarVar(boolean, int, int) disassembly (after) + /// CHECK: <<Cond:z\d+>> ParameterValue + /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>] + /// CHECK: cmovnz/ne + public static int BoolCond_IntVarVar(boolean cond, int x, int y) { return cond ? x : y; } @@ -46,6 +51,11 @@ public class Main { /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>] /// CHECK: cmovnz/ne + /// CHECK-START-X86: int Main.BoolCond_IntVarCst(boolean, int) disassembly (after) + /// CHECK: <<Cond:z\d+>> ParameterValue + /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>] + /// CHECK: cmovnz/ne + public static int BoolCond_IntVarCst(boolean cond, int x) { return cond ? x : 1; } @@ -63,6 +73,11 @@ public class Main { /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>] /// CHECK: cmovnz/ne + /// CHECK-START-X86: int Main.BoolCond_IntCstVar(boolean, int) disassembly (after) + /// CHECK: <<Cond:z\d+>> ParameterValue + /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>] + /// CHECK: cmovnz/ne + public static int BoolCond_IntCstVar(boolean cond, int y) { return cond ? 1 : y; } @@ -80,6 +95,12 @@ public class Main { /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] /// CHECK: cmovnz/neq + /// CHECK-START-X86: long Main.BoolCond_LongVarVar(boolean, long, long) disassembly (after) + /// CHECK: <<Cond:z\d+>> ParameterValue + /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] + /// CHECK: cmovnz/ne + /// CHECK-NEXT: cmovnz/ne + public static long BoolCond_LongVarVar(boolean cond, long x, long y) { return cond ? x : y; } @@ -97,6 +118,12 @@ public class Main { /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] /// CHECK: cmovnz/neq + /// CHECK-START-X86: long Main.BoolCond_LongVarCst(boolean, long) disassembly (after) + /// CHECK: <<Cond:z\d+>> ParameterValue + /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] + /// CHECK: cmovnz/ne + /// CHECK-NEXT: cmovnz/ne + public static long BoolCond_LongVarCst(boolean cond, long x) { return cond ? x : 1L; } @@ -114,6 +141,12 @@ public class Main { /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] /// CHECK: cmovnz/neq + /// CHECK-START-X86: long Main.BoolCond_LongCstVar(boolean, long) disassembly (after) + /// CHECK: <<Cond:z\d+>> ParameterValue + /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] + /// CHECK: cmovnz/ne + /// CHECK-NEXT: cmovnz/ne + public static long BoolCond_LongCstVar(boolean cond, long y) { return cond ? 1L : y; } @@ -168,6 +201,11 @@ public class Main { /// CHECK-NEXT: Select [{{i\d+}},{{i\d+}},<<Cond>>] /// CHECK: cmovle/ng + /// CHECK-START-X86: int Main.IntNonmatCond_IntVarVar(int, int, int, int) disassembly (after) + /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{i\d+}},{{i\d+}}] + /// CHECK-NEXT: Select [{{i\d+}},{{i\d+}},<<Cond>>] + /// CHECK: cmovle/ng + public static int IntNonmatCond_IntVarVar(int a, int b, int x, int y) { return a > b ? x : y; } @@ -189,6 +227,11 @@ public class Main { /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>] /// CHECK: cmovle/ng + /// CHECK-START-X86: int Main.IntMatCond_IntVarVar(int, int, int, int) disassembly (after) + /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{i\d+}},{{i\d+}}] + /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>] + /// CHECK: cmovle/ng + public static int IntMatCond_IntVarVar(int a, int b, int x, int y) { int result = (a > b ? x : y); return result + (a > b ? 0 : 1); @@ -208,6 +251,12 @@ public class Main { /// CHECK-NEXT: Select [{{j\d+}},{{j\d+}},<<Cond>>] /// CHECK: cmovle/ngq + /// CHECK-START-X86: long Main.IntNonmatCond_LongVarVar(int, int, long, long) disassembly (after) + /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{i\d+}},{{i\d+}}] + /// CHECK-NEXT: Select [{{j\d+}},{{j\d+}},<<Cond>>] + /// CHECK: cmovle/ng + /// CHECK-NEXT: cmovle/ng + public static long IntNonmatCond_LongVarVar(int a, int b, long x, long y) { return a > b ? x : y; } @@ -232,6 +281,15 @@ public class Main { /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] /// CHECK: cmovnz/neq + /// CHECK-START-X86: long Main.IntMatCond_LongVarVar(int, int, long, long) disassembly (after) + /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{i\d+}},{{i\d+}}] + /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] + /// CHECK-NEXT: cmovle/ng + /// CHECK-NEXT: cmovle/ng + /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] + /// CHECK: cmovnz/ne + /// CHECK-NEXT: cmovnz/ne + public static long IntMatCond_LongVarVar(int a, int b, long x, long y) { long result = (a > b ? x : y); return result + (a > b ? 0L : 1L); |