Delegate long-to-float type conversions to the runtime on ARM.
On ARM, translate long-to-float type conversions (from both
Quick and Optimizing) as calls to the runtime routine
art_l2f, instead of generating ad hoc code, so as to improve
the precision of the conversions.
Bug: 20413424
Change-Id: I8c414ee1c6f4ff1f32ee78f75734cfd3cf579f71
diff --git a/compiler/dex/quick/arm/fp_arm.cc b/compiler/dex/quick/arm/fp_arm.cc
index 94fc474..1a5c108 100644
--- a/compiler/dex/quick/arm/fp_arm.cc
+++ b/compiler/dex/quick/arm/fp_arm.cc
@@ -191,30 +191,8 @@
GenConversionCall(kQuickF2l, rl_dest, rl_src, kCoreReg);
return;
case Instruction::LONG_TO_FLOAT: {
- rl_src = LoadValueWide(rl_src, kFPReg);
- RegisterInfo* info = GetRegInfo(rl_src.reg);
- RegStorage src_low = info->FindMatchingView(RegisterInfo::kLowSingleStorageMask)->GetReg();
- DCHECK(src_low.Valid());
- RegStorage src_high = info->FindMatchingView(RegisterInfo::kHighSingleStorageMask)->GetReg();
- DCHECK(src_high.Valid());
- rl_result = EvalLoc(rl_dest, kFPReg, true);
- // Allocate temp registers.
- RegStorage high_val = AllocTempDouble();
- RegStorage low_val = AllocTempDouble();
- RegStorage const_val = AllocTempDouble();
- // Long to double.
- NewLIR2(kThumb2VcvtF64S32, high_val.GetReg(), src_high.GetReg());
- NewLIR2(kThumb2VcvtF64U32, low_val.GetReg(), src_low.GetReg());
- LoadConstantWide(const_val, INT64_C(0x41f0000000000000));
- NewLIR3(kThumb2VmlaF64, low_val.GetReg(), high_val.GetReg(), const_val.GetReg());
- // Double to float.
- NewLIR2(kThumb2VcvtDF, rl_result.reg.GetReg(), low_val.GetReg());
- // Free temp registers.
- FreeTemp(high_val);
- FreeTemp(low_val);
- FreeTemp(const_val);
- // Store result.
- StoreValue(rl_dest, rl_result);
+ CheckEntrypointTypes<kQuickL2f, float, int64_t>(); // float -> kFPReg
+ GenConversionCall(kQuickL2f, rl_dest, rl_src, kFPReg);
return;
}
case Instruction::DOUBLE_TO_LONG:
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 987a6c4..1c68654 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -1456,11 +1456,12 @@
Primitive::Type input_type = conversion->GetInputType();
DCHECK_NE(result_type, input_type);
- // The float-to-long and double-to-long type conversions rely on a
- // call to the runtime.
+ // The float-to-long, double-to-long and long-to-float type conversions
+ // rely on a call to the runtime.
LocationSummary::CallKind call_kind =
- ((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble)
- && result_type == Primitive::kPrimLong)
+ (((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble)
+ && result_type == Primitive::kPrimLong)
+ || (input_type == Primitive::kPrimLong && result_type == Primitive::kPrimFloat))
? LocationSummary::kCall
: LocationSummary::kNoCall;
LocationSummary* locations =
@@ -1603,15 +1604,14 @@
locations->SetOut(Location::RequiresFpuRegister());
break;
- case Primitive::kPrimLong:
+ 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());
+ InvokeRuntimeCallingConvention calling_convention;
+ locations->SetInAt(0, Location::RegisterPairLocation(
+ calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
+ locations->SetOut(Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
break;
+ }
case Primitive::kPrimDouble:
// Processing a Dex `double-to-float' instruction.
@@ -1820,47 +1820,13 @@
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);
+ codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pL2f),
+ conversion,
+ conversion->GetDexPc(),
+ nullptr);
break;
- }
case Primitive::kPrimDouble:
// Processing a Dex `double-to-float' instruction.