diff options
| -rw-r--r-- | compiler/optimizing/code_generator_arm64.cc | 106 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_arm64.h | 4 | ||||
| -rw-r--r-- | compiler/optimizing/common_arm64.h | 26 | ||||
| -rw-r--r-- | test/679-checker-minmax/src/Main.java | 204 |
4 files changed, 237 insertions, 103 deletions
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 5f0533cbe9..8aa790db34 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -2459,6 +2459,9 @@ void InstructionCodeGeneratorARM64::HandleBinaryOp(HBinaryOperation* instr) { // all & reg_bits - 1. __ Ror(dst, lhs, RegisterFrom(instr->GetLocations()->InAt(1), type)); } + } else if (instr->IsMin() || instr->IsMax()) { + __ Cmp(lhs, rhs); + __ Csel(dst, lhs, rhs, instr->IsMin() ? lt : gt); } else { DCHECK(instr->IsXor()); __ Eor(dst, lhs, rhs); @@ -2474,6 +2477,10 @@ void InstructionCodeGeneratorARM64::HandleBinaryOp(HBinaryOperation* instr) { __ Fadd(dst, lhs, rhs); } else if (instr->IsSub()) { __ Fsub(dst, lhs, rhs); + } else if (instr->IsMin()) { + __ Fmin(dst, lhs, rhs); + } else if (instr->IsMax()) { + __ Fmax(dst, lhs, rhs); } else { LOG(FATAL) << "Unexpected floating-point binary operation"; } @@ -5671,111 +5678,20 @@ void InstructionCodeGeneratorARM64::VisitRem(HRem* rem) { } } -// TODO: integrate with HandleBinaryOp? -static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) { - LocationSummary* locations = new (allocator) LocationSummary(minmax); - switch (minmax->GetResultType()) { - case DataType::Type::kInt32: - case DataType::Type::kInt64: - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); - break; - case DataType::Type::kFloat32: - case DataType::Type::kFloat64: - locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetInAt(1, Location::RequiresFpuRegister()); - locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); - break; - default: - LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType(); - } -} - -void InstructionCodeGeneratorARM64::GenerateMinMaxInt(LocationSummary* locations, - bool is_min, - DataType::Type type) { - Location op1 = locations->InAt(0); - Location op2 = locations->InAt(1); - Location out = locations->Out(); - - Register op1_reg; - Register op2_reg; - Register out_reg; - if (type == DataType::Type::kInt64) { - op1_reg = XRegisterFrom(op1); - op2_reg = XRegisterFrom(op2); - out_reg = XRegisterFrom(out); - } else { - DCHECK_EQ(type, DataType::Type::kInt32); - op1_reg = WRegisterFrom(op1); - op2_reg = WRegisterFrom(op2); - out_reg = WRegisterFrom(out); - } - - __ Cmp(op1_reg, op2_reg); - __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt); -} - -void InstructionCodeGeneratorARM64::GenerateMinMaxFP(LocationSummary* locations, - bool is_min, - DataType::Type type) { - Location op1 = locations->InAt(0); - Location op2 = locations->InAt(1); - Location out = locations->Out(); - - FPRegister op1_reg; - FPRegister op2_reg; - FPRegister out_reg; - if (type == DataType::Type::kFloat64) { - op1_reg = DRegisterFrom(op1); - op2_reg = DRegisterFrom(op2); - out_reg = DRegisterFrom(out); - } else { - DCHECK_EQ(type, DataType::Type::kFloat32); - op1_reg = SRegisterFrom(op1); - op2_reg = SRegisterFrom(op2); - out_reg = SRegisterFrom(out); - } - - if (is_min) { - __ Fmin(out_reg, op1_reg, op2_reg); - } else { - __ Fmax(out_reg, op1_reg, op2_reg); - } -} - -// TODO: integrate with HandleBinaryOp? -void InstructionCodeGeneratorARM64::GenerateMinMax(HBinaryOperation* minmax, bool is_min) { - DataType::Type type = minmax->GetResultType(); - switch (type) { - case DataType::Type::kInt32: - case DataType::Type::kInt64: - GenerateMinMaxInt(minmax->GetLocations(), is_min, type); - break; - case DataType::Type::kFloat32: - case DataType::Type::kFloat64: - GenerateMinMaxFP(minmax->GetLocations(), is_min, type); - break; - default: - LOG(FATAL) << "Unexpected type for HMinMax " << type; - } -} - void LocationsBuilderARM64::VisitMin(HMin* min) { - CreateMinMaxLocations(GetGraph()->GetAllocator(), min); + HandleBinaryOp(min); } void InstructionCodeGeneratorARM64::VisitMin(HMin* min) { - GenerateMinMax(min, /*is_min*/ true); + HandleBinaryOp(min); } void LocationsBuilderARM64::VisitMax(HMax* max) { - CreateMinMaxLocations(GetGraph()->GetAllocator(), max); + HandleBinaryOp(max); } void InstructionCodeGeneratorARM64::VisitMax(HMax* max) { - GenerateMinMax(max, /*is_min*/ false); + HandleBinaryOp(max); } void LocationsBuilderARM64::VisitAbs(HAbs* abs) { diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index e7fe5b71b7..5afb712d17 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -280,10 +280,6 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator { void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info); void HandleCondition(HCondition* instruction); - void GenerateMinMaxInt(LocationSummary* locations, bool is_min, DataType::Type type); - void GenerateMinMaxFP(LocationSummary* locations, bool is_min, DataType::Type type); - void GenerateMinMax(HBinaryOperation* minmax, bool is_min); - // Generate a heap reference load using one register `out`: // // out <- *(out + offset) diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h index ed2f8e995d..5191ee2b1e 100644 --- a/compiler/optimizing/common_arm64.h +++ b/compiler/optimizing/common_arm64.h @@ -234,6 +234,13 @@ inline vixl::aarch64::Operand OperandFromMemOperand( } } +inline bool AddSubCanEncodeAsImmediate(int64_t value) { + // If `value` does not fit but `-value` does, VIXL will automatically use + // the 'opposite' instruction. + return vixl::aarch64::Assembler::IsImmAddSub(value) + || vixl::aarch64::Assembler::IsImmAddSub(-value); +} + inline bool Arm64CanEncodeConstantAsImmediate(HConstant* constant, HInstruction* instr) { int64_t value = CodeGenerator::GetInt64ValueOf(constant); @@ -249,6 +256,20 @@ inline bool Arm64CanEncodeConstantAsImmediate(HConstant* constant, HInstruction* return IsUint<8>(value); } + // Code generation for Min/Max: + // Cmp left_op, right_op + // Csel dst, left_op, right_op, cond + if (instr->IsMin() || instr->IsMax()) { + if (constant->GetUses().HasExactlyOneElement()) { + // If value can be encoded as immediate for the Cmp, then let VIXL handle + // the constant generation for the Csel. + return AddSubCanEncodeAsImmediate(value); + } + // These values are encodable as immediates for Cmp and VIXL will use csinc and csinv + // with the zr register as right_op, hence no constant generation is required. + return constant->IsZeroBitPattern() || constant->IsOne() || constant->IsMinusOne(); + } + // For single uses we let VIXL handle the constant generation since it will // use registers that are not managed by the register allocator (wip0, wip1). if (constant->GetUses().HasExactlyOneElement()) { @@ -275,10 +296,7 @@ inline bool Arm64CanEncodeConstantAsImmediate(HConstant* constant, HInstruction* instr->IsSub()) << instr->DebugName(); // Uses aliases of ADD/SUB instructions. - // If `value` does not fit but `-value` does, VIXL will automatically use - // the 'opposite' instruction. - return vixl::aarch64::Assembler::IsImmAddSub(value) - || vixl::aarch64::Assembler::IsImmAddSub(-value); + return AddSubCanEncodeAsImmediate(value); } } diff --git a/test/679-checker-minmax/src/Main.java b/test/679-checker-minmax/src/Main.java index abf8c279da..4b7265642a 100644 --- a/test/679-checker-minmax/src/Main.java +++ b/test/679-checker-minmax/src/Main.java @@ -37,6 +37,13 @@ public class Main { // /// CHECK-START: int Main.minI(int) instruction_simplifier (after) /// CHECK-NOT: InvokeStaticOrDirect + // + /// CHECK-START-ARM64: int Main.minI(int) disassembly (after) + /// CHECK-NOT: mov {{w\d+}}, #0x14 + /// CHECK: cmp {{w\d+}}, #0x14 + // Check that the constant generation was handled by VIXL. + /// CHECK: mov w16, #0x14 + /// CHECK: csel {{w\d+}}, {{w\d+}}, w16, lt public static int minI(int a) { return Math.min(a, 20); } @@ -55,6 +62,13 @@ public class Main { // /// CHECK-START: long Main.minL(long) instruction_simplifier (after) /// CHECK-NOT: InvokeStaticOrDirect + // + /// CHECK-START-ARM64: long Main.minL(long) disassembly (after) + /// CHECK-NOT: mov {{x\d+}}, #0x14 + /// CHECK: cmp {{x\d+}}, #0x14 + // Check that the constant generation was handled by VIXL. + /// CHECK: mov x16, #0x14 + /// CHECK: csel {{x\d+}}, {{x\d+}}, x16, lt public static long minL(long a) { return Math.min(a, 20L); } @@ -73,6 +87,13 @@ public class Main { // /// CHECK-START: int Main.maxI(int) instruction_simplifier (after) /// CHECK-NOT: InvokeStaticOrDirect + // + /// CHECK-START-ARM64: int Main.maxI(int) disassembly (after) + /// CHECK-NOT: mov {{w\d+}}, #0x14 + /// CHECK: cmp {{w\d+}}, #0x14 + // Check that the constant generation was handled by VIXL. + /// CHECK: mov w16, #0x14 + /// CHECK: csel {{w\d+}}, {{w\d+}}, w16, gt public static int maxI(int a) { return Math.max(a, 20); } @@ -91,11 +112,166 @@ public class Main { // /// CHECK-START: long Main.maxL(long) instruction_simplifier (after) /// CHECK-NOT: InvokeStaticOrDirect + // + /// CHECK-START-ARM64: long Main.maxL(long) disassembly (after) + /// CHECK-NOT: mov {{x\d+}}, #0x14 + /// CHECK: cmp {{x\d+}}, #0x14 + // Check that the constant generation was handled by VIXL. + /// CHECK: mov x16, #0x14 + /// CHECK: csel {{x\d+}}, {{x\d+}}, x16, gt public static long maxL(long a) { return Math.max(a, 20L); } // + // Special Cases + // + + /// CHECK-START-ARM64: int Main.minIntConstantZero(int) disassembly (after) + /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: mov {{w\d+}}, #0x0 + /// CHECK: cmp {{w\d+}}, #0x0 (0) + /// CHECK: csel {{w\d+}}, {{w\d+}}, wzr, lt + /// CHECK: ret + public static int minIntConstantZero(int a) { + return Math.min(a, 0); + } + + /// CHECK-START-ARM64: int Main.minIntConstantOne(int) disassembly (after) + /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: mov {{w\d+}}, #0x1 + /// CHECK: cmp {{w\d+}}, #0x1 (1) + /// CHECK: csinc {{w\d+}}, {{w\d+}}, wzr, lt + /// CHECK: ret + public static int minIntConstantOne(int a) { + return Math.min(a, 1); + } + + /// CHECK-START-ARM64: int Main.minIntConstantMinusOne(int) disassembly (after) + /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: mov {{w\d+}}, #0xffffffff + /// CHECK: cmn {{w\d+}}, #0x1 (1) + /// CHECK: csinv {{w\d+}}, {{w\d+}}, wzr, lt + /// CHECK: ret + public static int minIntConstantMinusOne(int a) { + return Math.min(a, -1); + } + + /// CHECK-START-ARM64: long Main.minLongConstantZero(long) disassembly (after) + /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: mov {{x\d+}}, #0x0 + /// CHECK: cmp {{x\d+}}, #0x0 (0) + /// CHECK: csel {{x\d+}}, {{x\d+}}, xzr, lt + /// CHECK: ret + public static long minLongConstantZero(long a) { + return Math.min(a, 0L); + } + + /// CHECK-START-ARM64: long Main.minLongConstantOne(long) disassembly (after) + /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: mov {{x\d+}}, #0x1 + /// CHECK: cmp {{x\d+}}, #0x1 (1) + /// CHECK: csinc {{x\d+}}, {{x\d+}}, xzr, lt + /// CHECK: ret + public static long minLongConstantOne(long a) { + return Math.min(a, 1L); + } + + /// CHECK-START-ARM64: long Main.minLongConstantMinusOne(long) disassembly (after) + /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: mov {{x\d+}}, #0xffffffffffffffff + /// CHECK: cmn {{x\d+}}, #0x1 (1) + /// CHECK: csinv {{x\d+}}, {{x\d+}}, xzr, lt + /// CHECK: ret + public static long minLongConstantMinusOne(long a) { + return Math.min(a, -1L); + } + + /// CHECK-START-ARM64: int Main.maxIntConstantZero(int) disassembly (after) + /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: mov {{w\d+}}, #0x0 + /// CHECK: cmp {{w\d+}}, #0x0 (0) + /// CHECK: csel {{w\d+}}, {{w\d+}}, wzr, gt + /// CHECK: ret + public static int maxIntConstantZero(int a) { + return Math.max(a, 0); + } + + /// CHECK-START-ARM64: int Main.maxIntConstantOne(int) disassembly (after) + /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: mov {{w\d+}}, #0x1 + /// CHECK: cmp {{w\d+}}, #0x1 (1) + /// CHECK: csinc {{w\d+}}, {{w\d+}}, wzr, gt + /// CHECK: ret + public static int maxIntConstantOne(int a) { + return Math.max(a, 1); + } + + /// CHECK-START-ARM64: int Main.maxIntConstantMinusOne(int) disassembly (after) + /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: mov {{w\d+}}, #0xffffffff + /// CHECK: cmn {{w\d+}}, #0x1 (1) + /// CHECK: csinv {{w\d+}}, {{w\d+}}, wzr, gt + /// CHECK: ret + public static int maxIntConstantMinusOne(int a) { + return Math.max(a, -1); + } + + /// CHECK-START-ARM64: int Main.maxIntLargeConstant(int) disassembly (after) + /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK: mov {{w\d+}}, #0x2001 + /// CHECK: cmp {{w\d+}}, {{w\d+}} + // Check that constant generation was not handled by VIXL. + /// CHECK-NOT: mov {{w\d+}}, #0x2001 + /// CHECK: csel {{w\d+}}, {{w\d+}}, {{w\d+}}, gt + /// CHECK: ret + public static int maxIntLargeConstant(int a) { + return Math.max(a, 8193); + } + + /// CHECK-START-ARM64: long Main.maxLongConstantZero(long) disassembly (after) + /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: mov {{x\d+}}, #0x0 + /// CHECK: cmp {{x\d+}}, #0x0 (0) + /// CHECK: csel {{x\d+}}, {{x\d+}}, xzr, gt + /// CHECK: ret + public static long maxLongConstantZero(long a) { + return Math.max(a, 0L); + } + + /// CHECK-START-ARM64: long Main.maxLongConstantOne(long) disassembly (after) + /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: mov {{x\d+}}, #0x1 + /// CHECK: cmp {{x\d+}}, #0x1 (1) + /// CHECK: csinc {{x\d+}}, {{x\d+}}, xzr, gt + /// CHECK: ret + public static long maxLongConstantOne(long a) { + return Math.max(a, 1L); + } + + /// CHECK-START-ARM64: long Main.maxLongConstantMinusOne(long) disassembly (after) + /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: mov {{x\d+}}, #0xffffffffffffffff + /// CHECK: cmn {{x\d+}}, #0x1 (1) + /// CHECK: csinv {{x\d+}}, {{x\d+}}, xzr, gt + /// CHECK: ret + public static long maxLongConstantMinusOne(long a) { + return Math.max(a, -1L); + } + + /// CHECK-START-ARM64: long Main.maxLongLargeConstant(long) disassembly (after) + /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK: mov {{x\d+}}, #0x2001 + /// CHECK: cmp {{x\d+}}, {{x\d+}} + // Check that constant generation was not handled by VIXL. + /// CHECK-NOT: mov {{x\d+}}, #0x2001 + /// CHECK: csel {{x\d+}}, {{x\d+}}, {{x\d+}}, gt + /// CHECK: ret + public static long maxLongLargeConstant(long a) { + return Math.max(a, 8193L); + } + + // // Different types. // @@ -538,12 +714,40 @@ public class Main { // Intrinsics. expectEquals(10, minI(10)); expectEquals(20, minI(25)); + expectEquals(-1, minIntConstantZero(-1)); + expectEquals(0, minIntConstantZero(1)); + expectEquals(0, minIntConstantOne(0)); + expectEquals(1, minIntConstantOne(2)); + expectEquals(-2, minIntConstantMinusOne(-2)); + expectEquals(-1, minIntConstantMinusOne(0)); expectEquals(10L, minL(10L)); expectEquals(20L, minL(25L)); + expectEquals(-1L, minLongConstantZero(-1L)); + expectEquals(0L, minLongConstantZero(1L)); + expectEquals(0L, minLongConstantOne(0L)); + expectEquals(1L, minLongConstantOne(2L)); + expectEquals(-2L, minLongConstantMinusOne(-2L)); + expectEquals(-1L, minLongConstantMinusOne(0L)); expectEquals(20, maxI(10)); expectEquals(25, maxI(25)); + expectEquals(0, maxIntConstantZero(-1)); + expectEquals(1, maxIntConstantZero(1)); + expectEquals(1, maxIntConstantOne(0)); + expectEquals(2, maxIntConstantOne(2)); + expectEquals(-1, maxIntConstantMinusOne(-2)); + expectEquals(0, maxIntConstantMinusOne(0)); + expectEquals(8193, maxIntLargeConstant(8192)); + expectEquals(9000, maxIntLargeConstant(9000)); expectEquals(20L, maxL(10L)); expectEquals(25L, maxL(25L)); + expectEquals(0L, maxLongConstantZero(-1L)); + expectEquals(1L, maxLongConstantZero(1L)); + expectEquals(1L, maxLongConstantOne(0L)); + expectEquals(2L, maxLongConstantOne(2L)); + expectEquals(-1L, maxLongConstantMinusOne(-2L)); + expectEquals(0L, maxLongConstantMinusOne(0L)); + expectEquals(8193L, maxLongLargeConstant(8192L)); + expectEquals(9000L, maxLongLargeConstant(9000L)); // Types. expectEquals(10, min1(10, 20)); expectEquals(10, min2(10, 20)); |