[x86] FPU stack needs to be reset after double conversion

The x86 codegen uses the FPU stack for double/float to long conversions. We
need to clear out the FPU stack after done, to prevent an eventual stack
overflow.

Signed-off-by: Udayan Banerji <udayan.banerji@intel.com>

(cherry picked from commit 05d4f7c4a9e0604a247954220363e28cc92c382d)

Change-Id: I2342de4b4efab74cb88471ded8dcc94899768d7c
diff --git a/tests/302-float-conversion/expected.txt b/tests/302-float-conversion/expected.txt
new file mode 100644
index 0000000..6939a5c
--- /dev/null
+++ b/tests/302-float-conversion/expected.txt
@@ -0,0 +1 @@
+Result is as expected
diff --git a/tests/302-float-conversion/info.txt b/tests/302-float-conversion/info.txt
new file mode 100644
index 0000000..2b8bc21
--- /dev/null
+++ b/tests/302-float-conversion/info.txt
@@ -0,0 +1,4 @@
+Tests whether constant conversions of double values to long values are
+properly handled by the VM. For example, x86 systems using the x87 stack
+ should not overflow under constant conversions.
+
diff --git a/tests/302-float-conversion/src/Main.java b/tests/302-float-conversion/src/Main.java
new file mode 100644
index 0000000..dc512c5
--- /dev/null
+++ b/tests/302-float-conversion/src/Main.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+    static final long NUM_ITERATIONS = 50000;
+    static volatile double negInfinity = Double.NEGATIVE_INFINITY;
+
+    public static void main(String args[]) {
+
+        long sumInf = 0;
+        long sumRes = 0;
+
+        for (long i = 0 ; i < NUM_ITERATIONS ; i++) {
+            //Every second iteration, sumInf becomes 0
+            sumInf += (long) negInfinity;
+
+            //Some extra work for compilers to make this
+            //loop seem important
+            if (sumInf == Long.MIN_VALUE) {
+                sumRes++;
+            }
+        }
+
+        if (sumRes == NUM_ITERATIONS / 2) {
+            System.out.println("Result is as expected");
+        } else {
+            System.out.println("Conversions failed over " + NUM_ITERATIONS + " iterations");
+        }
+    }
+}
diff --git a/vm/compiler/codegen/x86/LowerAlu.cpp b/vm/compiler/codegen/x86/LowerAlu.cpp
index 2231bac..c8c4d66 100644
--- a/vm/compiler/codegen/x86/LowerAlu.cpp
+++ b/vm/compiler/codegen/x86/LowerAlu.cpp
@@ -291,56 +291,74 @@
         load_fp_stack_VR(OpndSize_32, vB); //flds
     }
 
-    load_fp_stack_global_data_API("valuePosInfLong", OpndSize_64);
+    //Check if it is the special Negative Infinity value
     load_fp_stack_global_data_API("valueNegInfLong", OpndSize_64);
-
-    //ST(0) ST(1) ST(2) --> LintMin LintMax value
-    compare_fp_stack(true, 2, false/*isDouble*/); //ST(2)
-    //ST(0) ST(1) --> LintMax value
+    //Stack status: ST(0) ST(1) --> LlongMin value
+    compare_fp_stack(true, 1, false/*isDouble*/); // Pops ST(1)
     conditional_jump(Condition_AE, ".float_to_long_negInf", true);
     rememberState(1);
-    compare_fp_stack(true, 1, false/*isDouble*/); //ST(1)
+
+    //Check if it is the special Positive Infinity value
+    load_fp_stack_global_data_API("valuePosInfLong", OpndSize_64);
+    //Stack status: ST(0) ST(1) --> LlongMax value
+    compare_fp_stack(true, 1, false/*isDouble*/); // Pops ST(1)
     rememberState(2);
-    //ST(0) --> value
     conditional_jump(Condition_C, ".float_to_long_nanInf", true);
-    //fnstcw, orw, fldcw, xorw
+
+    //Normal Case
+    //We want to truncate to 0 for conversion. That will be rounding mode 0x11
     load_effective_addr(-2, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
     store_fpu_cw(false/*checkException*/, 0, PhysicalReg_ESP, true);
+    //Change control word to rounding mode 11:
     alu_binary_imm_mem(OpndSize_16, or_opc, 0xc00, 0, PhysicalReg_ESP, true);
+    //Load the control word
     load_fpu_cw(0, PhysicalReg_ESP, true);
+    //Reset the control word
     alu_binary_imm_mem(OpndSize_16, xor_opc, 0xc00, 0, PhysicalReg_ESP, true);
+    //Perform the actual conversion
     store_int_fp_stack_VR(true/*pop*/, OpndSize_64, vA); //fistpll
-    //fldcw
+    // Restore the original control word
     load_fpu_cw(0, PhysicalReg_ESP, true);
     load_effective_addr(2, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
     rememberState(3);
+    /* NOTE: We do not need to pop out the original value we pushed
+     * since load_fpu_cw above already clears the stack for
+     * normal values.
+     */
     unconditional_jump(".float_to_long_okay", true);
+
+    //We can be here for positive infinity or NaN. Check parity bit
     insertLabel(".float_to_long_nanInf", true);
     conditional_jump(Condition_NP, ".float_to_long_posInf", true);
-    //fstpl??
     goToState(2);
-
+    //Save corresponding Long NaN value
     load_global_data_API("valueNanLong", OpndSize_64, 1, false);
-
     set_virtual_reg(vA, OpndSize_64, 1, false);
     transferToState(3);
+    //Pop out the original value we pushed
+    compare_fp_stack(true, 0, false/*isDouble*/); //ST(0)
     unconditional_jump(".float_to_long_okay", true);
-    insertLabel(".float_to_long_posInf", true);
-    //fstpl
-    goToState(2);
 
+    insertLabel(".float_to_long_posInf", true);
+    goToState(2);
+    //Save corresponding Long Positive Infinity value
     load_global_data_API("valuePosInfLong", OpndSize_64, 2, false);
     set_virtual_reg(vA, OpndSize_64, 2, false);
     transferToState(3);
+    //Pop out the original value we pushed
+    compare_fp_stack(true, 0, false/*isDouble*/); //ST(0)
     unconditional_jump(".float_to_long_okay", true);
+
     insertLabel(".float_to_long_negInf", true);
     //fstpl
-    //fstpl
     goToState(1);
-
+    //Load corresponding Long Negative Infinity value
     load_global_data_API("valueNegInfLong", OpndSize_64, 3, false);
     set_virtual_reg(vA, OpndSize_64, 3, false);
     transferToState(3);
+    //Pop out the original value we pushed
+    compare_fp_stack(true, 0, false/*isDouble*/); //ST(0)
+
     insertLabel(".float_to_long_okay", true);
     return 0;
 }