diff options
author | Roland Levillain <rpl@google.com> | 2014-11-27 18:31:21 +0000 |
---|---|---|
committer | Roland Levillain <rpl@google.com> | 2014-11-27 18:36:14 +0000 |
commit | 6d0e483dd2e0b63e952de060738c10e2abd12ff7 (patch) | |
tree | b396377926d2645f0df982f0b03c41149632a3de | |
parent | 7c97e855ceb9b45a1cc738fb144bd3312c4e09a8 (diff) | |
download | art-6d0e483dd2e0b63e952de060738c10e2abd12ff7.zip art-6d0e483dd2e0b63e952de060738c10e2abd12ff7.tar.gz art-6d0e483dd2e0b63e952de060738c10e2abd12ff7.tar.bz2 |
Add support for long-to-float in the optimizing compiler.
- Add support for the long-to-float Dex instruction in the
optimizing compiler.
- Have art::x86_64::X86_64Assembler::cvtsi2ss work with
64-bit operands.
- Generate x86, x86-64 and ARM (but not ARM64) code for
long to float HTypeConversion nodes.
- Add related tests to test/422-type-conversion.
Change-Id: Ic983cbeb1ae2051add40bc519a8f00a6196166c9
-rw-r--r-- | compiler/optimizing/builder.cc | 5 | ||||
-rw-r--r-- | compiler/optimizing/code_generator.h | 5 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm.cc | 69 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86.cc | 55 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86_64.cc | 15 | ||||
-rw-r--r-- | compiler/utils/x86_64/assembler_x86_64.cc | 12 | ||||
-rw-r--r-- | compiler/utils/x86_64/assembler_x86_64.h | 1 | ||||
-rw-r--r-- | test/422-type-conversion/src/Main.java | 26 |
8 files changed, 163 insertions, 25 deletions
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 51814ea..777a117 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -1043,6 +1043,11 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::LONG_TO_FLOAT: { + Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimFloat); + break; + } + case Instruction::LONG_TO_DOUBLE: { Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimDouble); break; diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index f906eb8..321a31f 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -30,6 +30,11 @@ namespace art { static size_t constexpr kVRegSize = 4; static size_t constexpr kUninitializedFrameSize = 0; +// Binary encoding of 2^32 for type double. +static int64_t constexpr k2Pow32EncodingForDouble = INT64_C(0x41F0000000000000); +// Binary encoding of 2^31 for type double. +static int64_t constexpr k2Pow31EncodingForDouble = INT64_C(0x41E0000000000000); + class Assembler; class CodeGenerator; class DexCompilationUnit; diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 0967a01..7d6d827 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -1468,6 +1468,15 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) { break; case Primitive::kPrimLong: + // Processing a Dex `long-to-float' instruction. + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresFpuRegister()); + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresFpuRegister()); + locations->AddTemp(Location::RequiresFpuRegister()); + break; + case Primitive::kPrimDouble: LOG(FATAL) << "Type conversion from " << input_type << " to " << result_type << " not yet implemented"; @@ -1638,7 +1647,48 @@ void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversio break; } - case Primitive::kPrimLong: + case Primitive::kPrimLong: { + // Processing a Dex `long-to-float' instruction. + Register low = in.AsRegisterPairLow<Register>(); + Register high = in.AsRegisterPairHigh<Register>(); + SRegister output = out.AsFpuRegister<SRegister>(); + Register constant_low = locations->GetTemp(0).AsRegister<Register>(); + Register constant_high = locations->GetTemp(1).AsRegister<Register>(); + SRegister temp1_s = locations->GetTemp(2).AsFpuRegisterPairLow<SRegister>(); + DRegister temp1_d = FromLowSToD(temp1_s); + SRegister temp2_s = locations->GetTemp(3).AsFpuRegisterPairLow<SRegister>(); + DRegister temp2_d = FromLowSToD(temp2_s); + + // Operations use doubles for precision reasons (each 32-bit + // half of a long fits in the 53-bit mantissa of a double, + // but not in the 24-bit mantissa of a float). This is + // especially important for the low bits. The result is + // eventually converted to float. + + // temp1_d = int-to-double(high) + __ vmovsr(temp1_s, high); + __ vcvtdi(temp1_d, temp1_s); + // Using vmovd to load the `k2Pow32EncodingForDouble` constant + // as an immediate value into `temp2_d` does not work, as + // this instruction only transfers 8 significant bits of its + // immediate operand. Instead, use two 32-bit core + // registers to load `k2Pow32EncodingForDouble` into + // `temp2_d`. + __ LoadImmediate(constant_low, Low32Bits(k2Pow32EncodingForDouble)); + __ LoadImmediate(constant_high, High32Bits(k2Pow32EncodingForDouble)); + __ vmovdrr(temp2_d, constant_low, constant_high); + // temp1_d = temp1_d * 2^32 + __ vmuld(temp1_d, temp1_d, temp2_d); + // temp2_d = unsigned-to-double(low) + __ vmovsr(temp2_s, low); + __ vcvtdu(temp2_d, temp2_s); + // temp1_d = temp1_d + temp2_d + __ vaddd(temp1_d, temp1_d, temp2_d); + // output = double-to-float(temp1_d); + __ vcvtsd(output, temp1_d); + break; + } + case Primitive::kPrimDouble: LOG(FATAL) << "Type conversion from " << input_type << " to " << result_type << " not yet implemented"; @@ -1674,19 +1724,16 @@ void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversio SRegister temp_s = locations->GetTemp(2).AsFpuRegisterPairLow<SRegister>(); DRegister temp_d = FromLowSToD(temp_s); - // Binary encoding of 2^32 for type double. - const uint64_t c = UINT64_C(0x41F0000000000000); - // out_d = int-to-double(high) __ vmovsr(out_s, high); __ vcvtdi(out_d, out_s); - // Using vmovd to load the `c` constant as an immediate - // value into `temp_d` does not work, as this instruction - // only transfers 8 significant bits of its immediate - // operand. Instead, use two 32-bit core registers to - // load `c` into `temp_d`. - __ LoadImmediate(constant_low, Low32Bits(c)); - __ LoadImmediate(constant_high, High32Bits(c)); + // Using vmovd to load the `k2Pow32EncodingForDouble` constant + // as an immediate value into `temp_d` does not work, as + // this instruction only transfers 8 significant bits of its + // immediate operand. Instead, use two 32-bit core + // registers to load `k2Pow32EncodingForDouble` into `temp_d`. + __ LoadImmediate(constant_low, Low32Bits(k2Pow32EncodingForDouble)); + __ LoadImmediate(constant_high, High32Bits(k2Pow32EncodingForDouble)); __ vmovdrr(temp_d, constant_low, constant_high); // out_d = out_d * 2^32 __ vmuld(out_d, out_d, temp_d); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 705065d..b21abad 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -1435,6 +1435,13 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) { break; case Primitive::kPrimLong: + // Processing a Dex `long-to-float' instruction. + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresFpuRegister()); + locations->AddTemp(Location::RequiresFpuRegister()); + locations->AddTemp(Location::RequiresFpuRegister()); + break; + case Primitive::kPrimDouble: LOG(FATAL) << "Type conversion from " << input_type << " to " << result_type << " not yet implemented"; @@ -1614,15 +1621,48 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio case Primitive::kPrimFloat: switch (input_type) { - // Processing a Dex `int-to-float' instruction. case Primitive::kPrimByte: case Primitive::kPrimShort: case Primitive::kPrimInt: case Primitive::kPrimChar: + // Processing a Dex `int-to-float' instruction. __ cvtsi2ss(out.AsFpuRegister<XmmRegister>(), in.AsRegister<Register>()); break; - case Primitive::kPrimLong: + case Primitive::kPrimLong: { + // Processing a Dex `long-to-float' instruction. + Register low = in.AsRegisterPairLow<Register>(); + Register high = in.AsRegisterPairHigh<Register>(); + XmmRegister result = out.AsFpuRegister<XmmRegister>(); + XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); + XmmRegister constant = locations->GetTemp(1).AsFpuRegister<XmmRegister>(); + + // Operations use doubles for precision reasons (each 32-bit + // half of a long fits in the 53-bit mantissa of a double, + // but not in the 24-bit mantissa of a float). This is + // especially important for the low bits. The result is + // eventually converted to float. + + // low = low - 2^31 (to prevent bit 31 of `low` to be + // interpreted as a sign bit) + __ subl(low, Immediate(0x80000000)); + // temp = int-to-double(high) + __ cvtsi2sd(temp, high); + // temp = temp * 2^32 + __ LoadLongConstant(constant, k2Pow32EncodingForDouble); + __ mulsd(temp, constant); + // result = int-to-double(low) + __ cvtsi2sd(result, low); + // result = result + 2^31 (restore the original value of `low`) + __ LoadLongConstant(constant, k2Pow31EncodingForDouble); + __ addsd(result, constant); + // result = result + temp + __ addsd(result, temp); + // result = double-to-float(result) + __ cvtsd2ss(result, result); + break; + } + case Primitive::kPrimDouble: LOG(FATAL) << "Type conversion from " << input_type << " to " << result_type << " not yet implemented"; @@ -1636,11 +1676,11 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio case Primitive::kPrimDouble: switch (input_type) { - // Processing a Dex `int-to-double' instruction. case Primitive::kPrimByte: case Primitive::kPrimShort: case Primitive::kPrimInt: case Primitive::kPrimChar: + // Processing a Dex `int-to-double' instruction. __ cvtsi2sd(out.AsFpuRegister<XmmRegister>(), in.AsRegister<Register>()); break; @@ -1652,23 +1692,18 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); XmmRegister constant = locations->GetTemp(1).AsFpuRegister<XmmRegister>(); - // Binary encoding of 2^32 for type double. - const int64_t c1 = INT64_C(0x41F0000000000000); - // Binary encoding of 2^31 for type double. - const int64_t c2 = INT64_C(0x41E0000000000000); - // low = low - 2^31 (to prevent bit 31 of `low` to be // interpreted as a sign bit) __ subl(low, Immediate(0x80000000)); // temp = int-to-double(high) __ cvtsi2sd(temp, high); // temp = temp * 2^32 - __ LoadLongConstant(constant, c1); + __ LoadLongConstant(constant, k2Pow32EncodingForDouble); __ mulsd(temp, constant); // result = int-to-double(low) __ cvtsi2sd(result, low); // result = result + 2^31 (restore the original value of `low`) - __ LoadLongConstant(constant, c2); + __ LoadLongConstant(constant, k2Pow31EncodingForDouble); __ addsd(result, constant); // result = result + temp __ addsd(result, temp); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index f9e8685..5d3e809 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -1427,6 +1427,11 @@ void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) { break; case Primitive::kPrimLong: + // Processing a Dex `long-to-float' instruction. + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresFpuRegister()); + break; + case Primitive::kPrimDouble: LOG(FATAL) << "Type conversion from " << input_type << " to " << result_type << " not yet implemented"; @@ -1607,15 +1612,19 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver case Primitive::kPrimFloat: switch (input_type) { - // Processing a Dex `int-to-float' instruction. case Primitive::kPrimByte: case Primitive::kPrimShort: case Primitive::kPrimInt: case Primitive::kPrimChar: - __ cvtsi2ss(out.AsFpuRegister<XmmRegister>(), in.AsRegister<CpuRegister>()); + // Processing a Dex `int-to-float' instruction. + __ cvtsi2ss(out.AsFpuRegister<XmmRegister>(), in.AsRegister<CpuRegister>(), false); break; case Primitive::kPrimLong: + // Processing a Dex `long-to-float' instruction. + __ cvtsi2ss(out.AsFpuRegister<XmmRegister>(), in.AsRegister<CpuRegister>(), true); + break; + case Primitive::kPrimDouble: LOG(FATAL) << "Type conversion from " << input_type << " to " << result_type << " not yet implemented"; @@ -1629,11 +1638,11 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver case Primitive::kPrimDouble: switch (input_type) { - // Processing a Dex `int-to-double' instruction. case Primitive::kPrimByte: case Primitive::kPrimShort: case Primitive::kPrimInt: case Primitive::kPrimChar: + // Processing a Dex `int-to-double' instruction. __ cvtsi2sd(out.AsFpuRegister<XmmRegister>(), in.AsRegister<CpuRegister>(), false); break; diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index b5f56fc..474d8a9 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -593,9 +593,19 @@ void X86_64Assembler::divsd(XmmRegister dst, const Address& src) { void X86_64Assembler::cvtsi2ss(XmmRegister dst, CpuRegister src) { + cvtsi2ss(dst, src, false); +} + + +void X86_64Assembler::cvtsi2ss(XmmRegister dst, CpuRegister src, bool is64bit) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xF3); - 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(0x2A); EmitOperand(dst.LowBits(), Operand(src)); diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index 12b49ea..6e71e4a 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -329,6 +329,7 @@ class X86_64Assembler FINAL : public Assembler { void divsd(XmmRegister dst, const Address& src); void cvtsi2ss(XmmRegister dst, CpuRegister src); // Note: this is the r/m32 version. + void cvtsi2ss(XmmRegister dst, CpuRegister src, bool is64bit); void cvtsi2sd(XmmRegister dst, CpuRegister src); // Note: this is the r/m32 version. void cvtsi2sd(XmmRegister dst, CpuRegister src, bool is64bit); diff --git a/test/422-type-conversion/src/Main.java b/test/422-type-conversion/src/Main.java index 4461863..c434db3 100644 --- a/test/422-type-conversion/src/Main.java +++ b/test/422-type-conversion/src/Main.java @@ -85,6 +85,9 @@ public class Main { // Generate, compile and check long-to-int Dex instructions. longToInt(); + // Generate, compile and check long-to-float Dex instructions. + longToFloat(); + // Generate, compile and check long-to-double Dex instructions. longToDouble(); @@ -270,6 +273,26 @@ public class Main { assertLongEquals(-1, $opt$IntToLong($opt$LongToInt(-4294967297L))); // -(2^32 + 1) } + private static void longToFloat() { + assertFloatEquals(1F, $opt$LongToFloat(1L)); + assertFloatEquals(0F, $opt$LongToFloat(0L)); + assertFloatEquals(-1F, $opt$LongToFloat(-1L)); + assertFloatEquals(51F, $opt$LongToFloat(51L)); + assertFloatEquals(-51F, $opt$LongToFloat(-51L)); + assertFloatEquals(2147483647F, $opt$LongToFloat(2147483647L)); // 2^31 - 1 + assertFloatEquals(-2147483647F, $opt$LongToFloat(-2147483647L)); // -(2^31 - 1) + assertFloatEquals(-2147483648F, $opt$LongToFloat(-2147483648L)); // -(2^31) + assertFloatEquals(2147483648F, $opt$LongToFloat(2147483648L)); // (2^31) + assertFloatEquals(-2147483649F, $opt$LongToFloat(-2147483649L)); // -(2^31 + 1) + assertFloatEquals(4294967296F, $opt$LongToFloat(4294967296L)); // (2^32) + assertFloatEquals(-4294967296F, $opt$LongToFloat(-4294967296L)); // -(2^32) + assertFloatEquals(140739635871745F, $opt$LongToFloat(140739635871745L)); // 1 + 2^15 + 2^31 + 2^47 + assertFloatEquals(-140739635871745F, $opt$LongToFloat(-140739635871745L)); // -(1 + 2^15 + 2^31 + 2^47) + assertFloatEquals(9223372036854775807F, $opt$LongToFloat(9223372036854775807L)); // 2^63 - 1 + assertFloatEquals(-9223372036854775807F, $opt$LongToFloat(-9223372036854775807L)); // -(2^63 - 1) + assertFloatEquals(-9223372036854775808F, $opt$LongToFloat(-9223372036854775808L)); // -(2^63) + } + private static void longToDouble() { assertDoubleEquals(1D, $opt$LongToDouble(1L)); assertDoubleEquals(0D, $opt$LongToDouble(0L)); @@ -439,6 +462,9 @@ public class Main { static int $opt$LongToInt(long a){ return (int)a; } static int $opt$LongLiteralToInt(){ return (int)42L; } + // This method produces a long-to-float Dex instruction. + static float $opt$LongToFloat(long a){ return (float)a; } + // This method produces a long-to-double Dex instruction. static double $opt$LongToDouble(long a){ return (double)a; } |