Optimizing: Fix long-to-fp conversion on x86.

long-to-fp conversion implemented using SSE loses the precision.
The test is included. CL uses FPU to provide the correct result.

Change-Id: I8eaf3c46819a8cb52642a7e7d7c4e3e0edbc88db
Signed-off-by: Serguei Katkov <serguei.i.katkov@intel.com>
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 92b62e2..ef896aa 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -1525,10 +1525,8 @@
 
         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());
+          locations->SetInAt(0, Location::Any());
+          locations->SetOut(Location::Any());
           break;
 
         case Primitive::kPrimDouble:
@@ -1558,10 +1556,8 @@
 
         case Primitive::kPrimLong:
           // Processing a Dex `long-to-double' instruction.
-          locations->SetInAt(0, Location::RequiresRegister());
-          locations->SetOut(Location::RequiresFpuRegister());
-          locations->AddTemp(Location::RequiresFpuRegister());
-          locations->AddTemp(Location::RequiresFpuRegister());
+          locations->SetInAt(0, Location::Any());
+          locations->SetOut(Location::Any());
           break;
 
         case Primitive::kPrimFloat:
@@ -1782,37 +1778,31 @@
 
         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>();
+          size_t adjustment = 0;
 
-          // 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.
+          // Create stack space for the call to
+          // InstructionCodeGeneratorX86::PushOntoFPStack and/or X86Assembler::fstps below.
+          // TODO: enhance register allocator to ask for stack temporaries.
+          if (!in.IsDoubleStackSlot() || !out.IsStackSlot()) {
+            adjustment = Primitive::ComponentSize(Primitive::kPrimLong);
+            __ subl(ESP, Immediate(adjustment));
+          }
 
-          // 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);
-          // Restore low.
-          __ addl(low, Immediate(0x80000000));
+          // Load the value to the FP stack, using temporaries if needed.
+          PushOntoFPStack(in, 0, adjustment, false, true);
+
+          if (out.IsStackSlot()) {
+            __ fstps(Address(ESP, out.GetStackIndex() + adjustment));
+          } else {
+            __ fstps(Address(ESP, 0));
+            Location stack_temp = Location::StackSlot(0);
+            codegen_->Move32(out, stack_temp);
+          }
+
+          // Remove the temporary stack space we allocated.
+          if (adjustment != 0) {
+            __ addl(ESP, Immediate(adjustment));
+          }
           break;
         }
 
@@ -1841,29 +1831,31 @@
 
         case Primitive::kPrimLong: {
           // Processing a Dex `long-to-double' 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>();
+          size_t adjustment = 0;
 
-          // 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);
-          // Restore low.
-          __ addl(low, Immediate(0x80000000));
+          // Create stack space for the call to
+          // InstructionCodeGeneratorX86::PushOntoFPStack and/or X86Assembler::fstpl below.
+          // TODO: enhance register allocator to ask for stack temporaries.
+          if (!in.IsDoubleStackSlot() || !out.IsDoubleStackSlot()) {
+            adjustment = Primitive::ComponentSize(Primitive::kPrimLong);
+            __ subl(ESP, Immediate(adjustment));
+          }
+
+          // Load the value to the FP stack, using temporaries if needed.
+          PushOntoFPStack(in, 0, adjustment, false, true);
+
+          if (out.IsDoubleStackSlot()) {
+            __ fstpl(Address(ESP, out.GetStackIndex() + adjustment));
+          } else {
+            __ fstpl(Address(ESP, 0));
+            Location stack_temp = Location::DoubleStackSlot(0);
+            codegen_->Move64(out, stack_temp);
+          }
+
+          // Remove the temporary stack space we allocated.
+          if (adjustment != 0) {
+            __ addl(ESP, Immediate(adjustment));
+          }
           break;
         }
 
@@ -2203,24 +2195,43 @@
   }
 }
 
-void InstructionCodeGeneratorX86::PushOntoFPStack(Location source, uint32_t temp_offset,
-                                                  uint32_t stack_adjustment, bool is_float) {
+void InstructionCodeGeneratorX86::PushOntoFPStack(Location source,
+                                                  uint32_t temp_offset,
+                                                  uint32_t stack_adjustment,
+                                                  bool is_fp,
+                                                  bool is_wide) {
   if (source.IsStackSlot()) {
-    DCHECK(is_float);
-    __ flds(Address(ESP, source.GetStackIndex() + stack_adjustment));
+    DCHECK(!is_wide);
+    if (is_fp) {
+      __ flds(Address(ESP, source.GetStackIndex() + stack_adjustment));
+    } else {
+      __ filds(Address(ESP, source.GetStackIndex() + stack_adjustment));
+    }
   } else if (source.IsDoubleStackSlot()) {
-    DCHECK(!is_float);
-    __ fldl(Address(ESP, source.GetStackIndex() + stack_adjustment));
+    DCHECK(is_wide);
+    if (is_fp) {
+      __ fldl(Address(ESP, source.GetStackIndex() + stack_adjustment));
+    } else {
+      __ fildl(Address(ESP, source.GetStackIndex() + stack_adjustment));
+    }
   } else {
     // Write the value to the temporary location on the stack and load to FP stack.
-    if (is_float) {
+    if (!is_wide) {
       Location stack_temp = Location::StackSlot(temp_offset);
       codegen_->Move32(stack_temp, source);
-      __ flds(Address(ESP, temp_offset));
+      if (is_fp) {
+        __ flds(Address(ESP, temp_offset));
+      } else {
+        __ filds(Address(ESP, temp_offset));
+      }
     } else {
       Location stack_temp = Location::DoubleStackSlot(temp_offset);
       codegen_->Move64(stack_temp, source);
-      __ fldl(Address(ESP, temp_offset));
+      if (is_fp) {
+        __ fldl(Address(ESP, temp_offset));
+      } else {
+        __ fildl(Address(ESP, temp_offset));
+      }
     }
   }
 }
@@ -2239,8 +2250,9 @@
   __ subl(ESP, Immediate(2 * elem_size));
 
   // Load the values to the FP stack in reverse order, using temporaries if needed.
-  PushOntoFPStack(second, elem_size, 2 * elem_size, is_float);
-  PushOntoFPStack(first, 0, 2 * elem_size, is_float);
+  const bool is_wide = !is_float;
+  PushOntoFPStack(second, elem_size, 2 * elem_size, /* is_fp */ true, is_wide);
+  PushOntoFPStack(first, 0, 2 * elem_size, /* is_fp */ true, is_wide);
 
   // Loop doing FPREM until we stabilize.
   Label retry;
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 0cc3c65..1cb9e32 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -171,8 +171,10 @@
   void GenerateMemoryBarrier(MemBarrierKind kind);
   void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
   void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
+  // Push value to FPU stack. `is_fp` specifies whether the value is floating point or not.
+  // `is_wide` specifies whether it is long/double or not.
   void PushOntoFPStack(Location source, uint32_t temp_offset,
-                       uint32_t stack_adjustment, bool is_float);
+                       uint32_t stack_adjustment, bool is_fp, bool is_wide);
 
   void GenerateImplicitNullCheck(HNullCheck* instruction);
   void GenerateExplicitNullCheck(HNullCheck* instruction);
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index 4cca529..7b206a3 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -883,6 +883,13 @@
 }
 
 
+void X86Assembler::filds(const Address& src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0xDB);
+  EmitOperand(0, src);
+}
+
+
 void X86Assembler::fincstp() {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0xD9);
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index f3675ae..a933474 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -349,6 +349,7 @@
   void fistpl(const Address& dst);
   void fistps(const Address& dst);
   void fildl(const Address& src);
+  void filds(const Address& src);
 
   void fincstp();
   void ffree(const Immediate& index);
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index 2e0d9e1..699b0b2 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -984,6 +984,13 @@
 }
 
 
+void X86_64Assembler::filds(const Address& src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0xDB);
+  EmitOperand(0, src);
+}
+
+
 void X86_64Assembler::fincstp() {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0xD9);
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index a786a6c..3df0950 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -389,6 +389,7 @@
   void fistpl(const Address& dst);
   void fistps(const Address& dst);
   void fildl(const Address& src);
+  void filds(const Address& src);
 
   void fincstp();
   void ffree(const Immediate& index);
diff --git a/test/422-type-conversion/src/Main.java b/test/422-type-conversion/src/Main.java
index 7ce2868..da5bd76 100644
--- a/test/422-type-conversion/src/Main.java
+++ b/test/422-type-conversion/src/Main.java
@@ -321,6 +321,7 @@
     assertFloatEquals(9223372036854775807F, $opt$LongToFloat(9223372036854775807L));  // 2^63 - 1
     assertFloatEquals(-9223372036854775807F, $opt$LongToFloat(-9223372036854775807L));  // -(2^63 - 1)
     assertFloatEquals(-9223372036854775808F, $opt$LongToFloat(-9223372036854775808L));  // -(2^63)
+    assertFloatEquals(Float.intBitsToFloat(-555858671), $opt$LongToFloat(-8008112895877447681L));
   }
 
   private static void longToDouble() {