ARM: Use 64-bit literals for LoadDImmediate().
And rewrite the medium-range long/fp literal to use
MOVW+ADD+LDRD/VLDR because the old instruction sequence
was broken if the "ADD ip, pc" was not 4-byte aligned.
Test: assembler_thumb2_test has been updated. Standard
ART test suite has been run on host and Nexus 5.
Change-Id: I37c6a62aa6e77c6a9701b5a1fb4db2e666c1eae9
diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc
index 6f7119d..c95dfa8 100644
--- a/compiler/utils/arm/assembler_arm32.cc
+++ b/compiler/utils/arm/assembler_arm32.cc
@@ -1486,6 +1486,34 @@
}
}
+void Arm32Assembler::LoadDImmediate(DRegister dd, double value, Condition cond) {
+ if (!vmovd(dd, value, cond)) {
+ uint64_t int_value = bit_cast<uint64_t, double>(value);
+ if (int_value == bit_cast<uint64_t, double>(0.0)) {
+ // 0.0 is quite common, so we special case it by loading
+ // 2.0 in `dd` and then subtracting it.
+ bool success = vmovd(dd, 2.0, cond);
+ CHECK(success);
+ vsubd(dd, dd, dd, cond);
+ } else {
+ if (dd < 16) {
+ // Note: Depending on the particular CPU, this may cause register
+ // forwarding hazard, negatively impacting the performance.
+ SRegister low = static_cast<SRegister>(dd << 1);
+ SRegister high = static_cast<SRegister>(low + 1);
+ LoadSImmediate(low, bit_cast<float, uint32_t>(Low32Bits(int_value)), cond);
+ if (High32Bits(int_value) == Low32Bits(int_value)) {
+ vmovs(high, low);
+ } else {
+ LoadSImmediate(high, bit_cast<float, uint32_t>(High32Bits(int_value)), cond);
+ }
+ } else {
+ LOG(FATAL) << "Unimplemented loading of double into a D register "
+ << "that cannot be split into two S registers";
+ }
+ }
+ }
+}
// Implementation note: this method must emit at most one instruction when
// Address::CanHoldLoadOffsetArm.