From 05d4f7c4a9e0604a247954220363e28cc92c382d Mon Sep 17 00:00:00 2001 From: Udayan Banerji Date: Mon, 1 Apr 2013 14:27:09 -0700 Subject: [PATCH] [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. Change-Id: I2f306d7c228ad3da2b84faf9f08326769a9417af Signed-off-by: Udayan Banerji --- tests/302-float-conversion/expected.txt | 1 + tests/302-float-conversion/info.txt | 4 +++ tests/302-float-conversion/src/Main.java | 43 +++++++++++++++++++++++++++ vm/compiler/codegen/x86/LowerAlu.cpp | 50 ++++++++++++++++++++++---------- 4 files changed, 82 insertions(+), 16 deletions(-) create mode 100644 tests/302-float-conversion/expected.txt create mode 100644 tests/302-float-conversion/info.txt create mode 100644 tests/302-float-conversion/src/Main.java diff --git a/tests/302-float-conversion/expected.txt b/tests/302-float-conversion/expected.txt new file mode 100644 index 000000000..6939a5ccc --- /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 000000000..2b8bc2174 --- /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 000000000..dc512c5dc --- /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 2231baca1..c8c4d66e8 100644 --- a/vm/compiler/codegen/x86/LowerAlu.cpp +++ b/vm/compiler/codegen/x86/LowerAlu.cpp @@ -291,56 +291,74 @@ int common_fp_to_long(bool isDouble, u2 vA, u2 vB) { 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); - + //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; } -- 2.11.0