From 4c0b61f506644bb6b647be05d02c5fb45b9ceb48 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Fri, 5 Dec 2014 12:06:01 +0000 Subject: [PATCH] Add support for double-to-int & double-to-long in optimizing. - Add support for the double-to-int and double-to-long Dex instructions in the optimizing compiler. - Add S1 to the list of ARM FPU parameter registers so that a double value can be passed as parameter during a call to the runtime through D0. - Have art::x86_64::X86_64Assembler::cvttsd2si work with 64-bit operands. - Generate x86, x86-64 and ARM (but not ARM64) code for double to int and double to long HTypeConversion nodes. - Add related tests to test/422-type-conversion. Change-Id: Ic93b9ec6630c26e940f7966a3346ad3fd5a2ab3a --- compiler/optimizing/builder.cc | 10 ++++ compiler/optimizing/code_generator_arm.cc | 43 +++++++++++------ compiler/optimizing/code_generator_x86.cc | 64 +++++++++++++++++++------ compiler/optimizing/code_generator_x86_64.cc | 68 ++++++++++++++++++++++----- compiler/utils/x86_64/assembler_x86_64.cc | 12 ++++- compiler/utils/x86_64/assembler_x86_64.h | 1 + test/422-type-conversion/src/Main.java | 70 ++++++++++++++++++++++++++-- 7 files changed, 224 insertions(+), 44 deletions(-) diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 76efef09f..fe32da05e 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -1260,6 +1260,16 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::DOUBLE_TO_INT: { + Conversion_12x(instruction, Primitive::kPrimDouble, Primitive::kPrimInt, dex_pc); + break; + } + + case Instruction::DOUBLE_TO_LONG: { + Conversion_12x(instruction, Primitive::kPrimDouble, Primitive::kPrimLong, dex_pc); + break; + } + case Instruction::DOUBLE_TO_FLOAT: { Conversion_12x(instruction, Primitive::kPrimDouble, Primitive::kPrimFloat, dex_pc); break; diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 5076c8588..36af393e3 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -44,7 +44,7 @@ static constexpr int kCurrentMethodStackOffset = 0; static constexpr Register kRuntimeParameterCoreRegisters[] = { R0, R1, R2, R3 }; static constexpr size_t kRuntimeParameterCoreRegistersLength = arraysize(kRuntimeParameterCoreRegisters); -static constexpr SRegister kRuntimeParameterFpuRegisters[] = { S0 }; +static constexpr SRegister kRuntimeParameterFpuRegisters[] = { S0, S1 }; static constexpr size_t kRuntimeParameterFpuRegistersLength = arraysize(kRuntimeParameterFpuRegisters); @@ -1365,9 +1365,11 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) { Primitive::Type input_type = conversion->GetInputType(); DCHECK_NE(result_type, input_type); - // Float-to-long conversions invoke the runtime. + // The float-to-long and double-to-long type conversions rely on a + // call to the runtime. LocationSummary::CallKind call_kind = - (input_type == Primitive::kPrimFloat && result_type == Primitive::kPrimLong) + ((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble) + && result_type == Primitive::kPrimLong) ? LocationSummary::kCall : LocationSummary::kNoCall; LocationSummary* locations = @@ -1422,8 +1424,10 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) { break; case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type - << " to " << result_type << " not yet implemented"; + // Processing a Dex `double-to-int' instruction. + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresFpuRegister()); break; default: @@ -1452,10 +1456,15 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) { break; } - case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type << " to " - << result_type << " not yet implemented"; + case Primitive::kPrimDouble: { + // Processing a Dex `double-to-long' instruction. + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::FpuRegisterPairLocation( + calling_convention.GetFpuRegisterAt(0), + calling_convention.GetFpuRegisterAt(1))); + locations->SetOut(Location::RegisterPairLocation(R0, R1)); break; + } default: LOG(FATAL) << "Unexpected type conversion from " << input_type @@ -1614,10 +1623,15 @@ void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversio break; } - case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type - << " to " << result_type << " not yet implemented"; + case Primitive::kPrimDouble: { + // Processing a Dex `double-to-int' instruction. + SRegister temp_s = locations->GetTemp(0).AsFpuRegisterPairLow(); + DRegister temp_d = FromLowSToD(temp_s); + __ vmovd(temp_d, FromLowSToD(in.AsFpuRegisterPairLow())); + __ vcvtid(temp_s, temp_d); + __ vmovrs(out.AsRegister(), temp_s); break; + } default: LOG(FATAL) << "Unexpected type conversion from " << input_type @@ -1643,15 +1657,16 @@ void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversio case Primitive::kPrimFloat: // Processing a Dex `float-to-long' instruction. - // This call does not actually record PC information. codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pF2l), conversion, conversion->GetDexPc()); break; case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type << " to " - << result_type << " not yet implemented"; + // Processing a Dex `double-to-long' instruction. + codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pD2l), + conversion, + conversion->GetDexPc()); break; default: diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 2aa121d04..2fd712f7e 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -1330,9 +1330,11 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) { Primitive::Type input_type = conversion->GetInputType(); DCHECK_NE(result_type, input_type); - // Float-to-long conversions invoke the runtime. + // The float-to-long and double-to-long type conversions rely on a + // call to the runtime. LocationSummary::CallKind call_kind = - (input_type == Primitive::kPrimFloat && result_type == Primitive::kPrimLong) + ((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble) + && result_type == Primitive::kPrimLong) ? LocationSummary::kCall : LocationSummary::kNoCall; LocationSummary* locations = @@ -1387,8 +1389,10 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) { break; case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type - << " to " << result_type << " not yet implemented"; + // Processing a Dex `double-to-int' instruction. + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresFpuRegister()); break; default: @@ -1411,15 +1415,27 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) { case Primitive::kPrimFloat: { // Processing a Dex `float-to-long' instruction. InvokeRuntimeCallingConvention calling_convention; - locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + // Note that on x86 floating-point parameters are passed + // through core registers (here, EAX). + locations->SetInAt(0, Location::RegisterLocation( + calling_convention.GetRegisterAt(0))); // The runtime helper puts the result in EAX, EDX. locations->SetOut(Location::RegisterPairLocation(EAX, EDX)); break; } - case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type << " to " - << result_type << " not yet implemented"; + case Primitive::kPrimDouble: { + // Processing a Dex `double-to-long' instruction. + InvokeRuntimeCallingConvention calling_convention; + // Note that on x86 floating-point parameters are passed + // through core registers (here, EAX and ECX). + locations->SetInAt(0, Location::RegisterPairLocation( + calling_convention.GetRegisterAt(0), + calling_convention.GetRegisterAt(1))); + // The runtime helper puts the result in EAX, EDX. + locations->SetOut(Location::RegisterPairLocation(EAX, EDX)); + break; + } break; default: @@ -1607,10 +1623,30 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio break; } - case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type - << " to " << result_type << " not yet implemented"; + case Primitive::kPrimDouble: { + // Processing a Dex `double-to-int' instruction. + XmmRegister input = in.AsFpuRegister(); + Register output = out.AsRegister(); + XmmRegister temp = locations->GetTemp(0).AsFpuRegister(); + Label done, nan; + + __ movl(output, Immediate(kPrimIntMax)); + // temp = int-to-double(output) + __ cvtsi2sd(temp, output); + // if input >= temp goto done + __ comisd(input, temp); + __ j(kAboveEqual, &done); + // if input == NaN goto nan + __ j(kUnordered, &nan); + // output = double-to-int-truncate(input) + __ cvttsd2si(output, input); + __ jmp(&done); + __ Bind(&nan); + // output = 0 + __ xorl(output, output); + __ Bind(&done); break; + } default: LOG(FATAL) << "Unexpected type conversion from " << input_type @@ -1634,13 +1670,13 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio case Primitive::kPrimFloat: // Processing a Dex `float-to-long' instruction. __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pF2l))); - // This call does not actually record PC information. codegen_->RecordPcInfo(conversion, conversion->GetDexPc()); break; case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type << " to " - << result_type << " not yet implemented"; + // Processing a Dex `double-to-long' instruction. + __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pD2l))); + codegen_->RecordPcInfo(conversion, conversion->GetDexPc()); break; default: diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 5761fb1bd..39a97661c 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -1370,8 +1370,10 @@ void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) { break; case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type - << " to " << result_type << " not yet implemented"; + // Processing a Dex `double-to-int' instruction. + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresFpuRegister()); break; default: @@ -1401,8 +1403,10 @@ void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) { break; case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type << " to " - << result_type << " not yet implemented"; + // Processing a Dex `double-to-long' instruction. + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresFpuRegister()); break; default: @@ -1589,10 +1593,30 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver break; } - case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type - << " to " << result_type << " not yet implemented"; + case Primitive::kPrimDouble: { + // Processing a Dex `double-to-int' instruction. + XmmRegister input = in.AsFpuRegister(); + CpuRegister output = out.AsRegister(); + XmmRegister temp = locations->GetTemp(0).AsFpuRegister(); + Label done, nan; + + __ movl(output, Immediate(kPrimIntMax)); + // temp = int-to-double(output) + __ cvtsi2sd(temp, output); + // if input >= temp goto done + __ comisd(input, temp); + __ j(kAboveEqual, &done); + // if input == NaN goto nan + __ j(kUnordered, &nan); + // output = double-to-int-truncate(input) + __ cvttsd2si(output, input); + __ jmp(&done); + __ Bind(&nan); + // output = 0 + __ xorl(output, output); + __ Bind(&done); break; + } default: LOG(FATAL) << "Unexpected type conversion from " << input_type @@ -1620,14 +1644,14 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver Label done, nan; __ movq(output, Immediate(kPrimLongMax)); - // temp = int-to-float(output) + // temp = long-to-float(output) __ cvtsi2ss(temp, output, true); // if input >= temp goto done __ comiss(input, temp); __ j(kAboveEqual, &done); // if input == NaN goto nan __ j(kUnordered, &nan); - // output = float-to-int-truncate(input) + // output = float-to-long-truncate(input) __ cvttss2si(output, input, true); __ jmp(&done); __ Bind(&nan); @@ -1637,10 +1661,30 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver break; } - case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type << " to " - << result_type << " not yet implemented"; + case Primitive::kPrimDouble: { + // Processing a Dex `double-to-long' instruction. + XmmRegister input = in.AsFpuRegister(); + CpuRegister output = out.AsRegister(); + XmmRegister temp = locations->GetTemp(0).AsFpuRegister(); + Label done, nan; + + __ movq(output, Immediate(kPrimLongMax)); + // temp = long-to-double(output) + __ cvtsi2sd(temp, output, true); + // if input >= temp goto done + __ comisd(input, temp); + __ j(kAboveEqual, &done); + // if input == NaN goto nan + __ j(kUnordered, &nan); + // output = double-to-long-truncate(input) + __ cvttsd2si(output, input, true); + __ jmp(&done); + __ Bind(&nan); + // output = 0 + __ xorq(output, output); + __ Bind(&done); break; + } default: LOG(FATAL) << "Unexpected type conversion from " << input_type diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index 3c21236a8..2a6c58e12 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -683,9 +683,19 @@ void X86_64Assembler::cvttss2si(CpuRegister dst, XmmRegister src, bool is64bit) void X86_64Assembler::cvttsd2si(CpuRegister dst, XmmRegister src) { + cvttsd2si(dst, src, false); +} + + +void X86_64Assembler::cvttsd2si(CpuRegister dst, XmmRegister src, bool is64bit) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xF2); - EmitOptionalRex32(dst, src); + if (is64bit) { + // Emit a REX.W prefix if the operand size is 64 bits. + EmitRex64(dst, src); + } else { + EmitOptionalRex32(dst, src); + } EmitUint8(0x0F); EmitUint8(0x2C); EmitXmmRegisterOperand(dst.LowBits(), src); diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index 4c2836665..51d1de2c0 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -342,6 +342,7 @@ class X86_64Assembler FINAL : public Assembler { void cvttss2si(CpuRegister dst, XmmRegister src); // Note: this is the r32 version. void cvttss2si(CpuRegister dst, XmmRegister src, bool is64bit); void cvttsd2si(CpuRegister dst, XmmRegister src); // Note: this is the r32 version. + void cvttsd2si(CpuRegister dst, XmmRegister src, bool is64bit); void cvtdq2pd(XmmRegister dst, XmmRegister src); diff --git a/test/422-type-conversion/src/Main.java b/test/422-type-conversion/src/Main.java index 91618fc34..7ce286828 100644 --- a/test/422-type-conversion/src/Main.java +++ b/test/422-type-conversion/src/Main.java @@ -112,6 +112,12 @@ public class Main { // Generate, compile and check float-to-double Dex instructions. floatToDouble(); + // Generate, compile and check double-to-int Dex instructions. + doubleToInt(); + + // Generate, compile and check double-to-long Dex instructions. + doubleToLong(); + // Generate, compile and check double-to-float Dex instructions. doubleToFloat(); @@ -415,6 +421,58 @@ public class Main { assertDoubleEquals(Double.NEGATIVE_INFINITY, $opt$FloatToDouble(Float.NEGATIVE_INFINITY)); } + private static void doubleToInt() { + assertIntEquals(1, $opt$DoubleToInt(1D)); + assertIntEquals(0, $opt$DoubleToInt(0D)); + assertIntEquals(0, $opt$DoubleToInt(-0D)); + assertIntEquals(-1, $opt$DoubleToInt(-1D)); + assertIntEquals(51, $opt$DoubleToInt(51D)); + assertIntEquals(-51, $opt$DoubleToInt(-51D)); + assertIntEquals(0, $opt$DoubleToInt(0.5D)); + assertIntEquals(0, $opt$DoubleToInt(0.4999999D)); + assertIntEquals(0, $opt$DoubleToInt(-0.4999999D)); + assertIntEquals(0, $opt$DoubleToInt(-0.5D)); + assertIntEquals(42, $opt$DoubleToInt(42.199D)); + assertIntEquals(-42, $opt$DoubleToInt(-42.199D)); + assertIntEquals(2147483647, $opt$DoubleToInt(2147483647D)); // 2^31 - 1 + assertIntEquals(-2147483647, $opt$DoubleToInt(-2147483647D)); // -(2^31 - 1) + assertIntEquals(-2147483648, $opt$DoubleToInt(-2147483648D)); // -(2^31) + assertIntEquals(2147483647, $opt$DoubleToInt(2147483648D)); // (2^31) + assertIntEquals(-2147483648, $opt$DoubleToInt(-2147483649D)); // -(2^31 + 1) + assertIntEquals(2147483647, $opt$DoubleToInt(9223372036854775807D)); // 2^63 - 1 + assertIntEquals(-2147483648, $opt$DoubleToInt(-9223372036854775807D)); // -(2^63 - 1) + assertIntEquals(-2147483648, $opt$DoubleToInt(-9223372036854775808D)); // -(2^63) + assertIntEquals(0, $opt$DoubleToInt(Double.NaN)); + assertIntEquals(2147483647, $opt$DoubleToInt(Double.POSITIVE_INFINITY)); + assertIntEquals(-2147483648, $opt$DoubleToInt(Double.NEGATIVE_INFINITY)); + } + + private static void doubleToLong() { + assertLongEquals(1L, $opt$DoubleToLong(1D)); + assertLongEquals(0L, $opt$DoubleToLong(0D)); + assertLongEquals(0L, $opt$DoubleToLong(-0D)); + assertLongEquals(-1L, $opt$DoubleToLong(-1D)); + assertLongEquals(51L, $opt$DoubleToLong(51D)); + assertLongEquals(-51L, $opt$DoubleToLong(-51D)); + assertLongEquals(0L, $opt$DoubleToLong(0.5D)); + assertLongEquals(0L, $opt$DoubleToLong(0.4999999D)); + assertLongEquals(0L, $opt$DoubleToLong(-0.4999999D)); + assertLongEquals(0L, $opt$DoubleToLong(-0.5D)); + assertLongEquals(42L, $opt$DoubleToLong(42.199D)); + assertLongEquals(-42L, $opt$DoubleToLong(-42.199D)); + assertLongEquals(2147483647L, $opt$DoubleToLong(2147483647D)); // 2^31 - 1 + assertLongEquals(-2147483647L, $opt$DoubleToLong(-2147483647D)); // -(2^31 - 1) + assertLongEquals(-2147483648L, $opt$DoubleToLong(-2147483648D)); // -(2^31) + assertLongEquals(2147483648L, $opt$DoubleToLong(2147483648D)); // (2^31) + assertLongEquals(-2147483649L, $opt$DoubleToLong(-2147483649D)); // -(2^31 + 1) + assertLongEquals(9223372036854775807L, $opt$DoubleToLong(9223372036854775807D)); // 2^63 - 1 + assertLongEquals(-9223372036854775808L, $opt$DoubleToLong(-9223372036854775807D)); // -(2^63 - 1) + assertLongEquals(-9223372036854775808L, $opt$DoubleToLong(-9223372036854775808D)); // -(2^63) + assertLongEquals(0L, $opt$DoubleToLong(Double.NaN)); + assertLongEquals(9223372036854775807L, $opt$DoubleToLong(Double.POSITIVE_INFINITY)); + assertLongEquals(-9223372036854775808L, $opt$DoubleToLong(Double.NEGATIVE_INFINITY)); + } + private static void doubleToFloat() { assertFloatEquals(1F, $opt$DoubleToFloat(1D)); assertFloatEquals(0F, $opt$DoubleToFloat(0D)); @@ -599,15 +657,21 @@ public class Main { // This method produces a float-to-int Dex instruction. static int $opt$FloatToInt(float a) { return (int)a; } + // This method produces a float-to-long Dex instruction. + static long $opt$FloatToLong(float a){ return (long)a; } + // This method produces a float-to-double Dex instruction. static double $opt$FloatToDouble(float a) { return (double)a; } + // This method produces a double-to-int Dex instruction. + static int $opt$DoubleToInt(double a){ return (int)a; } + + // This method produces a double-to-long Dex instruction. + static long $opt$DoubleToLong(double a){ return (long)a; } + // This method produces a double-to-float Dex instruction. static float $opt$DoubleToFloat(double a) { return (float)a; } - // This method produces a float-to-long Dex instruction. - static long $opt$FloatToLong(float a){ return (long)a; } - // These methods produce int-to-byte Dex instructions. static byte $opt$ShortToByte(short a) { return (byte)a; } static byte $opt$IntToByte(int a) { return (byte)a; } -- 2.11.0