From 7d7a73f427f1cad9773545e86d85b0c116a0f6dc Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Sun, 24 May 2026 17:53:15 +0000 Subject: [PATCH] =?UTF-8?q?F2I/F2L=20fix:=20NaN=E2=86=920,=20+Inf=E2=86=92?= =?UTF-8?q?MAX=5FVALUE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: LSantha --- .../jnode/vm/x86/compiler/l1a/IntItem.java | 62 ++++++- .../jnode/vm/x86/compiler/l1a/LongItem.java | 83 ++++++++- .../jnode/vm/x86/compiler/l1b/IntItem.java | 62 ++++++- .../jnode/vm/x86/compiler/l1b/LongItem.java | 83 ++++++++- .../compiler/l2/GenericX86CodeGenerator.java | 162 +++++++++++++++++- .../org/jnode/test/bugs/ConversionTest.java | 61 +++++++ 6 files changed, 503 insertions(+), 10 deletions(-) create mode 100644 core/src/test/org/jnode/test/bugs/ConversionTest.java diff --git a/core/src/core/org/jnode/vm/x86/compiler/l1a/IntItem.java b/core/src/core/org/jnode/vm/x86/compiler/l1a/IntItem.java index 63dfeba10e..2506450adb 100644 --- a/core/src/core/org/jnode/vm/x86/compiler/l1a/IntItem.java +++ b/core/src/core/org/jnode/vm/x86/compiler/l1a/IntItem.java @@ -20,7 +20,9 @@ package org.jnode.vm.x86.compiler.l1a; +import org.jnode.assembler.Label; import org.jnode.assembler.x86.X86Assembler; +import org.jnode.assembler.x86.X86Constants; import org.jnode.assembler.x86.X86Register; import org.jnode.assembler.x86.X86Register.GPR; import org.jnode.vm.JvmType; @@ -34,6 +36,8 @@ final class IntItem extends WordItem { + private static int labelCounter; + private int value; final void initialize(EmitterContext ec, byte kind, short offsetToFP, X86Register reg, int value) { @@ -84,14 +88,70 @@ protected void loadToConstant(EmitterContext ec, X86Assembler os, } /** - * Pop the top of the FPU stack into the given memory location. + * Pop the top of the FPU stack into the given memory location, + * with JLS-correct handling for NaN, infinity, and overflow. * * @param os * @param reg * @param disp */ protected void popFromFPU(X86Assembler os, GPR reg, int disp) { + final String uid = "f2i_" + (++labelCounter) + "_"; + final Label done = new Label(uid + "fix"); + final Label overflow = new Label(uid + "ovf"); + final Label isInf = new Label(uid + "inf"); + + os.writePUSH(X86Register.EAX); + + // Save float copy at [reg+disp-4], then reload for FISTP + os.writeFSTP32(reg, disp - 4); + os.writeFLD32(reg, disp - 4); os.writeFISTP32(reg, disp); + + // Check if result == MIN_VALUE (indefinite integer) + os.writeMOV(X86Constants.BITS32, X86Register.EAX, reg, disp); + os.writeCMP_Const(X86Register.EAX, 0x80000000); + os.writeJCC(done, X86Constants.JNE); + + // Check saved float for NaN/Infinity (exponent all 1s?) + os.writeMOV(X86Constants.BITS32, X86Register.EAX, reg, disp - 4); + os.writeAND(X86Register.EAX, 0x7F800000); + os.writeCMP_Const(X86Register.EAX, 0x7F800000); + os.writeJCC(overflow, X86Constants.JNE); + + // NaN or Infinity + os.writeMOV(X86Constants.BITS32, X86Register.EAX, reg, disp - 4); + os.writeTEST(X86Register.EAX, 0x007FFFFF); + os.writeJCC(isInf, X86Constants.JZ); + + // NaN -> 0 + os.writeXOR(X86Register.EAX, X86Register.EAX); + os.writeMOV(X86Constants.BITS32, reg, disp, X86Register.EAX); + os.writeJMP(done); + + // Infinity + os.setObjectRef(isInf); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, reg, disp - 4); + os.writeTEST(X86Register.EAX, 0x80000000); + os.writeJCC(done, X86Constants.JNZ); // -Inf -> MIN_VALUE (correct) + + // +Inf -> MAX_VALUE + os.writeMOV_Const(X86Register.EAX, 0x7FFFFFFF); + os.writeMOV(X86Constants.BITS32, reg, disp, X86Register.EAX); + os.writeJMP(done); + + // Not NaN/Inf, but result is MIN_VALUE: overflow or genuine MIN_VALUE + os.setObjectRef(overflow); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, reg, disp - 4); + os.writeTEST(X86Register.EAX, 0x80000000); + os.writeJCC(done, X86Constants.JNZ); // Negative -> keep MIN_VALUE + + // Positive overflow -> MAX_VALUE + os.writeMOV_Const(X86Register.EAX, 0x7FFFFFFF); + os.writeMOV(X86Constants.BITS32, reg, disp, X86Register.EAX); + + os.setObjectRef(done); + os.writePOP(X86Register.EAX); } /** diff --git a/core/src/core/org/jnode/vm/x86/compiler/l1a/LongItem.java b/core/src/core/org/jnode/vm/x86/compiler/l1a/LongItem.java index 49359f4841..e2e901108e 100644 --- a/core/src/core/org/jnode/vm/x86/compiler/l1a/LongItem.java +++ b/core/src/core/org/jnode/vm/x86/compiler/l1a/LongItem.java @@ -20,7 +20,9 @@ package org.jnode.vm.x86.compiler.l1a; +import org.jnode.assembler.Label; import org.jnode.assembler.x86.X86Assembler; +import org.jnode.assembler.x86.X86Constants; import org.jnode.assembler.x86.X86Register; import org.jnode.assembler.x86.X86Register.GPR; import org.jnode.assembler.x86.X86Register.GPR32; @@ -38,6 +40,8 @@ */ final class LongItem extends DoubleWordItem { + private static int labelCounter; + private long value; /** @@ -100,14 +104,91 @@ protected final void loadToConstant64(EmitterContext ec, X86Assembler os, } /** - * Pop the top of the FPU stack into the given memory location. + * Pop the top of the FPU stack into the given memory location, + * with JLS-correct handling for NaN, infinity, and overflow. * * @param os * @param reg * @param disp */ protected void popFromFPU(X86Assembler os, GPR reg, int disp) { + final String uid = "f2l_" + (++labelCounter) + "_"; + final Label done = new Label(uid + "fix"); + final Label overflow = new Label(uid + "ovf"); + final Label isInf = new Label(uid + "inf"); + final Label isInf2 = new Label(uid + "inf2"); + + os.writePUSH(X86Register.EAX); + + // Save double copy at [reg+disp-8], then reload for FISTP + os.writeFSTP64(reg, disp - 8); + os.writeFLD64(reg, disp - 8); os.writeFISTP64(reg, disp); + + // Check if 64-bit result == Long.MIN_VALUE (indefinite integer) + // Check low 32 bits == 0 + os.writeCMP_Const(X86Constants.BITS32, reg, disp, 0); + os.writeJCC(done, X86Constants.JNE); + // Check high 32 bits == 0x80000000 + os.writeCMP_Const(X86Constants.BITS32, reg, disp + 4, 0x80000000); + os.writeJCC(done, X86Constants.JNE); + + // Check saved double for NaN/Infinity + // Double high word at [reg+disp-4]: exponent bits 30:20 = 0x7FF00000 + os.writeMOV(X86Constants.BITS32, X86Register.EAX, reg, disp - 4); + os.writeAND(X86Register.EAX, 0x7FF00000); + os.writeCMP_Const(X86Register.EAX, 0x7FF00000); + os.writeJCC(overflow, X86Constants.JNE); + + // NaN or Infinity: check mantissa + os.writeMOV(X86Constants.BITS32, X86Register.EAX, reg, disp - 8); + os.writeTEST(X86Register.EAX, X86Register.EAX); + os.writeJCC(isInf, X86Constants.JZ); + // NaN (low mantissa != 0) + os.writeXOR(X86Register.EAX, X86Register.EAX); + os.writeMOV(X86Constants.BITS32, reg, disp, X86Register.EAX); + os.writeMOV(X86Constants.BITS32, reg, disp + 4, X86Register.EAX); + os.writeJMP(done); + + // Check high mantissa bits + os.setObjectRef(isInf); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, reg, disp - 4); + os.writeAND(X86Register.EAX, 0x000FFFFF); + os.writeTEST(X86Register.EAX, X86Register.EAX); + os.writeJCC(isInf2, X86Constants.JZ); + // NaN (high mantissa != 0) + os.writeXOR(X86Register.EAX, X86Register.EAX); + os.writeMOV(X86Constants.BITS32, reg, disp, X86Register.EAX); + os.writeMOV(X86Constants.BITS32, reg, disp + 4, X86Register.EAX); + os.writeJMP(done); + + // Infinity + os.setObjectRef(isInf2); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, reg, disp - 4); + os.writeTEST(X86Register.EAX, 0x80000000); + os.writeJCC(done, X86Constants.JNZ); // -Inf -> MIN_VALUE (correct) + + // +Inf -> MAX_VALUE + os.writeMOV_Const(X86Register.EAX, 0xFFFFFFFF); + os.writeMOV(X86Constants.BITS32, reg, disp, X86Register.EAX); + os.writeMOV_Const(X86Register.EAX, 0x7FFFFFFF); + os.writeMOV(X86Constants.BITS32, reg, disp + 4, X86Register.EAX); + os.writeJMP(done); + + // Not NaN/Inf, but result is MIN_VALUE: overflow or genuine MIN_VALUE + os.setObjectRef(overflow); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, reg, disp - 4); + os.writeTEST(X86Register.EAX, 0x80000000); + os.writeJCC(done, X86Constants.JNZ); // Negative -> keep MIN_VALUE + + // Positive overflow -> MAX_VALUE + os.writeMOV_Const(X86Register.EAX, 0xFFFFFFFF); + os.writeMOV(X86Constants.BITS32, reg, disp, X86Register.EAX); + os.writeMOV_Const(X86Register.EAX, 0x7FFFFFFF); + os.writeMOV(X86Constants.BITS32, reg, disp + 4, X86Register.EAX); + + os.setObjectRef(done); + os.writePOP(X86Register.EAX); } /** diff --git a/core/src/core/org/jnode/vm/x86/compiler/l1b/IntItem.java b/core/src/core/org/jnode/vm/x86/compiler/l1b/IntItem.java index 412ad7d243..b5a22271b6 100644 --- a/core/src/core/org/jnode/vm/x86/compiler/l1b/IntItem.java +++ b/core/src/core/org/jnode/vm/x86/compiler/l1b/IntItem.java @@ -20,7 +20,9 @@ package org.jnode.vm.x86.compiler.l1b; +import org.jnode.assembler.Label; import org.jnode.assembler.x86.X86Assembler; +import org.jnode.assembler.x86.X86Constants; import org.jnode.assembler.x86.X86Register; import org.jnode.assembler.x86.X86Register.GPR; import org.jnode.vm.JvmType; @@ -35,6 +37,8 @@ final class IntItem extends WordItem implements X86CompilerConstants { + private static int labelCounter; + private int value; final void initialize(EmitterContext ec, byte kind, short offsetToFP, X86Register reg, int value) { @@ -85,14 +89,70 @@ protected void loadToConstant(EmitterContext ec, X86Assembler os, } /** - * Pop the top of the FPU stack into the given memory location. + * Pop the top of the FPU stack into the given memory location, + * with JLS-correct handling for NaN, infinity, and overflow. * * @param os * @param reg * @param disp */ protected void popFromFPU(X86Assembler os, GPR reg, int disp) { + final String uid = "f2i_" + (++labelCounter) + "_"; + final Label done = new Label(uid + "fix"); + final Label overflow = new Label(uid + "ovf"); + final Label isInf = new Label(uid + "inf"); + + os.writePUSH(X86Register.EAX); + + // Save float copy at [reg+disp-4], then reload for FISTP + os.writeFSTP32(reg, disp - 4); + os.writeFLD32(reg, disp - 4); os.writeFISTP32(reg, disp); + + // Check if result == MIN_VALUE (indefinite integer) + os.writeMOV(X86Constants.BITS32, X86Register.EAX, reg, disp); + os.writeCMP_Const(X86Register.EAX, 0x80000000); + os.writeJCC(done, X86Constants.JNE); + + // Check saved float for NaN/Infinity (exponent all 1s?) + os.writeMOV(X86Constants.BITS32, X86Register.EAX, reg, disp - 4); + os.writeAND(X86Register.EAX, 0x7F800000); + os.writeCMP_Const(X86Register.EAX, 0x7F800000); + os.writeJCC(overflow, X86Constants.JNE); + + // NaN or Infinity + os.writeMOV(X86Constants.BITS32, X86Register.EAX, reg, disp - 4); + os.writeTEST(X86Register.EAX, 0x007FFFFF); + os.writeJCC(isInf, X86Constants.JZ); + + // NaN -> 0 + os.writeXOR(X86Register.EAX, X86Register.EAX); + os.writeMOV(X86Constants.BITS32, reg, disp, X86Register.EAX); + os.writeJMP(done); + + // Infinity + os.setObjectRef(isInf); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, reg, disp - 4); + os.writeTEST(X86Register.EAX, 0x80000000); + os.writeJCC(done, X86Constants.JNZ); // -Inf -> MIN_VALUE (correct) + + // +Inf -> MAX_VALUE + os.writeMOV_Const(X86Register.EAX, 0x7FFFFFFF); + os.writeMOV(X86Constants.BITS32, reg, disp, X86Register.EAX); + os.writeJMP(done); + + // Not NaN/Inf, but result is MIN_VALUE: overflow or genuine MIN_VALUE + os.setObjectRef(overflow); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, reg, disp - 4); + os.writeTEST(X86Register.EAX, 0x80000000); + os.writeJCC(done, X86Constants.JNZ); // Negative -> keep MIN_VALUE + + // Positive overflow -> MAX_VALUE + os.writeMOV_Const(X86Register.EAX, 0x7FFFFFFF); + os.writeMOV(X86Constants.BITS32, reg, disp, X86Register.EAX); + + os.setObjectRef(done); + os.writePOP(X86Register.EAX); } /** diff --git a/core/src/core/org/jnode/vm/x86/compiler/l1b/LongItem.java b/core/src/core/org/jnode/vm/x86/compiler/l1b/LongItem.java index 558814f020..c69743a152 100644 --- a/core/src/core/org/jnode/vm/x86/compiler/l1b/LongItem.java +++ b/core/src/core/org/jnode/vm/x86/compiler/l1b/LongItem.java @@ -20,7 +20,9 @@ package org.jnode.vm.x86.compiler.l1b; +import org.jnode.assembler.Label; import org.jnode.assembler.x86.X86Assembler; +import org.jnode.assembler.x86.X86Constants; import org.jnode.assembler.x86.X86Register; import org.jnode.assembler.x86.X86Register.GPR; import org.jnode.assembler.x86.X86Register.GPR32; @@ -35,6 +37,8 @@ */ final class LongItem extends DoubleWordItem implements X86CompilerConstants { + private static int labelCounter; + private long value; /** @@ -95,14 +99,91 @@ protected final void loadToConstant64(EmitterContext ec, X86Assembler os, } /** - * Pop the top of the FPU stack into the given memory location. + * Pop the top of the FPU stack into the given memory location, + * with JLS-correct handling for NaN, infinity, and overflow. * * @param os * @param reg * @param disp */ protected void popFromFPU(X86Assembler os, GPR reg, int disp) { + final String uid = "f2l_" + (++labelCounter) + "_"; + final Label done = new Label(uid + "fix"); + final Label overflow = new Label(uid + "ovf"); + final Label isInf = new Label(uid + "inf"); + final Label isInf2 = new Label(uid + "inf2"); + + os.writePUSH(X86Register.EAX); + + // Save double copy at [reg+disp-8], then reload for FISTP + os.writeFSTP64(reg, disp - 8); + os.writeFLD64(reg, disp - 8); os.writeFISTP64(reg, disp); + + // Check if 64-bit result == Long.MIN_VALUE (indefinite integer) + // Check low 32 bits == 0 + os.writeCMP_Const(X86Constants.BITS32, reg, disp, 0); + os.writeJCC(done, X86Constants.JNE); + // Check high 32 bits == 0x80000000 + os.writeCMP_Const(X86Constants.BITS32, reg, disp + 4, 0x80000000); + os.writeJCC(done, X86Constants.JNE); + + // Check saved double for NaN/Infinity + // Double high word at [reg+disp-4]: exponent bits 30:20 = 0x7FF00000 + os.writeMOV(X86Constants.BITS32, X86Register.EAX, reg, disp - 4); + os.writeAND(X86Register.EAX, 0x7FF00000); + os.writeCMP_Const(X86Register.EAX, 0x7FF00000); + os.writeJCC(overflow, X86Constants.JNE); + + // NaN or Infinity: check mantissa + os.writeMOV(X86Constants.BITS32, X86Register.EAX, reg, disp - 8); + os.writeTEST(X86Register.EAX, X86Register.EAX); + os.writeJCC(isInf, X86Constants.JZ); + // NaN (low mantissa != 0) + os.writeXOR(X86Register.EAX, X86Register.EAX); + os.writeMOV(X86Constants.BITS32, reg, disp, X86Register.EAX); + os.writeMOV(X86Constants.BITS32, reg, disp + 4, X86Register.EAX); + os.writeJMP(done); + + // Check high mantissa bits + os.setObjectRef(isInf); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, reg, disp - 4); + os.writeAND(X86Register.EAX, 0x000FFFFF); + os.writeTEST(X86Register.EAX, X86Register.EAX); + os.writeJCC(isInf2, X86Constants.JZ); + // NaN (high mantissa != 0) + os.writeXOR(X86Register.EAX, X86Register.EAX); + os.writeMOV(X86Constants.BITS32, reg, disp, X86Register.EAX); + os.writeMOV(X86Constants.BITS32, reg, disp + 4, X86Register.EAX); + os.writeJMP(done); + + // Infinity + os.setObjectRef(isInf2); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, reg, disp - 4); + os.writeTEST(X86Register.EAX, 0x80000000); + os.writeJCC(done, X86Constants.JNZ); // -Inf -> MIN_VALUE (correct) + + // +Inf -> MAX_VALUE + os.writeMOV_Const(X86Register.EAX, 0xFFFFFFFF); + os.writeMOV(X86Constants.BITS32, reg, disp, X86Register.EAX); + os.writeMOV_Const(X86Register.EAX, 0x7FFFFFFF); + os.writeMOV(X86Constants.BITS32, reg, disp + 4, X86Register.EAX); + os.writeJMP(done); + + // Not NaN/Inf, but result is MIN_VALUE: overflow or genuine MIN_VALUE + os.setObjectRef(overflow); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, reg, disp - 4); + os.writeTEST(X86Register.EAX, 0x80000000); + os.writeJCC(done, X86Constants.JNZ); // Negative -> keep MIN_VALUE + + // Positive overflow -> MAX_VALUE + os.writeMOV_Const(X86Register.EAX, 0xFFFFFFFF); + os.writeMOV(X86Constants.BITS32, reg, disp, X86Register.EAX); + os.writeMOV_Const(X86Register.EAX, 0x7FFFFFFF); + os.writeMOV(X86Constants.BITS32, reg, disp + 4, X86Register.EAX); + + os.setObjectRef(done); + os.writePOP(X86Register.EAX); } /** diff --git a/core/src/core/org/jnode/vm/x86/compiler/l2/GenericX86CodeGenerator.java b/core/src/core/org/jnode/vm/x86/compiler/l2/GenericX86CodeGenerator.java index f98547ae31..69d4677b71 100644 --- a/core/src/core/org/jnode/vm/x86/compiler/l2/GenericX86CodeGenerator.java +++ b/core/src/core/org/jnode/vm/x86/compiler/l2/GenericX86CodeGenerator.java @@ -115,6 +115,8 @@ public class GenericX86CodeGenerator extends CodeGenerato X86Constants { private static final GPR SR1 = X86Register.EAX; + private static int labelCounter; + // private static final Register SR2 = Register.EBX; // private static final Register SR3 = Register.ECX; // private static final Register SR4 = Register.EDX; @@ -427,12 +429,48 @@ public void generateCodeFor(UnaryQuad quad, Object lhsReg, UnaryOperation ope case L2D: throw new IllegalArgumentException("Unknown operation: " + operation); - case F2I: + case F2I: { + final String uid1 = "f2i_" + (++labelCounter) + "_"; + final Label doneL1 = new Label(uid1 + "fix"); + final Label overflowL1 = new Label(uid1 + "ovf"); + final Label isInfL1 = new Label(uid1 + "inf"); os.writePUSH((GPR) rhsReg); os.writeFLD32(X86Register.ESP, 0); - os.writeFISTP32(X86Register.ESP, 0); + os.writePUSH(X86Register.EAX); + os.writeFSTP32(X86Register.ESP, -4); + os.writeFLD32(X86Register.ESP, -4); + os.writeFISTP32(X86Register.ESP, 4); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, X86Register.ESP, 4); + os.writeCMP_Const(X86Register.EAX, 0x80000000); + os.writeJCC(doneL1, X86Constants.JNE); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, X86Register.ESP, -4); + os.writeAND(X86Register.EAX, 0x7F800000); + os.writeCMP_Const(X86Register.EAX, 0x7F800000); + os.writeJCC(overflowL1, X86Constants.JNE); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, X86Register.ESP, -4); + os.writeTEST(X86Register.EAX, 0x007FFFFF); + os.writeJCC(isInfL1, X86Constants.JZ); + os.writeXOR(X86Register.EAX, X86Register.EAX); + os.writeMOV(X86Constants.BITS32, X86Register.ESP, 4, X86Register.EAX); + os.writeJMP(doneL1); + os.setObjectRef(isInfL1); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, X86Register.ESP, -4); + os.writeTEST(X86Register.EAX, 0x80000000); + os.writeJCC(doneL1, X86Constants.JNZ); + os.writeMOV_Const(X86Register.EAX, 0x7FFFFFFF); + os.writeMOV(X86Constants.BITS32, X86Register.ESP, 4, X86Register.EAX); + os.writeJMP(doneL1); + os.setObjectRef(overflowL1); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, X86Register.ESP, -4); + os.writeTEST(X86Register.EAX, 0x80000000); + os.writeJCC(doneL1, X86Constants.JNZ); + os.writeMOV_Const(X86Register.EAX, 0x7FFFFFFF); + os.writeMOV(X86Constants.BITS32, X86Register.ESP, 4, X86Register.EAX); + os.setObjectRef(doneL1); + os.writePOP(X86Register.EAX); os.writePOP((GPR) lhsReg); break; + } case F2L: case F2D: @@ -506,12 +544,48 @@ public void generateCodeFor(UnaryQuad quad, Object lhsReg, UnaryOperation ope case L2D: throw new IllegalArgumentException("Unknown operation: " + operation); - case F2I: + case F2I: { + final String uid2 = "f2i_" + (++labelCounter) + "_"; + final Label doneL2 = new Label(uid2 + "fix"); + final Label overflowL2 = new Label(uid2 + "ovf"); + final Label isInfL2 = new Label(uid2 + "inf"); os.writePUSH(X86Register.EBP, rhsDisp); os.writeFLD32(X86Register.ESP, 0); - os.writeFISTP32(X86Register.ESP, 0); + os.writePUSH(X86Register.EAX); + os.writeFSTP32(X86Register.ESP, -4); + os.writeFLD32(X86Register.ESP, -4); + os.writeFISTP32(X86Register.ESP, 4); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, X86Register.ESP, 4); + os.writeCMP_Const(X86Register.EAX, 0x80000000); + os.writeJCC(doneL2, X86Constants.JNE); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, X86Register.ESP, -4); + os.writeAND(X86Register.EAX, 0x7F800000); + os.writeCMP_Const(X86Register.EAX, 0x7F800000); + os.writeJCC(overflowL2, X86Constants.JNE); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, X86Register.ESP, -4); + os.writeTEST(X86Register.EAX, 0x007FFFFF); + os.writeJCC(isInfL2, X86Constants.JZ); + os.writeXOR(X86Register.EAX, X86Register.EAX); + os.writeMOV(X86Constants.BITS32, X86Register.ESP, 4, X86Register.EAX); + os.writeJMP(doneL2); + os.setObjectRef(isInfL2); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, X86Register.ESP, -4); + os.writeTEST(X86Register.EAX, 0x80000000); + os.writeJCC(doneL2, X86Constants.JNZ); + os.writeMOV_Const(X86Register.EAX, 0x7FFFFFFF); + os.writeMOV(X86Constants.BITS32, X86Register.ESP, 4, X86Register.EAX); + os.writeJMP(doneL2); + os.setObjectRef(overflowL2); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, X86Register.ESP, -4); + os.writeTEST(X86Register.EAX, 0x80000000); + os.writeJCC(doneL2, X86Constants.JNZ); + os.writeMOV_Const(X86Register.EAX, 0x7FFFFFFF); + os.writeMOV(X86Constants.BITS32, X86Register.ESP, 4, X86Register.EAX); + os.setObjectRef(doneL2); + os.writePOP(X86Register.EAX); os.writePOP((GPR) lhsReg); break; + } case F2L: case F2D: @@ -586,11 +660,49 @@ public void generateCodeFor(UnaryQuad quad, int lhsDisp, UnaryOperation opera case L2I: case L2F: case L2D: - case F2I: + case F2I: { + final String uid3 = "f2i_" + (++labelCounter) + "_"; + final Label doneL3 = new Label(uid3 + "fix"); + final Label overflowL3 = new Label(uid3 + "ovf"); + final Label isInfL3 = new Label(uid3 + "inf"); os.writeMOV(X86Constants.BITS32, X86Register.EBP, lhsDisp, (GPR) rhsReg); os.writeFLD32(X86Register.EBP, lhsDisp); + os.writePUSH(X86Register.EAX); + os.writeLEA(X86Register.ESP, X86Register.ESP, -4); + os.writeFSTP32(X86Register.ESP, 0); + os.writeFLD32(X86Register.ESP, 0); os.writeFISTP32(X86Register.EBP, lhsDisp); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, X86Register.EBP, lhsDisp); + os.writeCMP_Const(X86Register.EAX, 0x80000000); + os.writeJCC(doneL3, X86Constants.JNE); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, X86Register.ESP, 0); + os.writeAND(X86Register.EAX, 0x7F800000); + os.writeCMP_Const(X86Register.EAX, 0x7F800000); + os.writeJCC(overflowL3, X86Constants.JNE); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, X86Register.ESP, 0); + os.writeTEST(X86Register.EAX, 0x007FFFFF); + os.writeJCC(isInfL3, X86Constants.JZ); + os.writeXOR(X86Register.EAX, X86Register.EAX); + os.writeMOV(X86Constants.BITS32, X86Register.EBP, lhsDisp, X86Register.EAX); + os.writeJMP(doneL3); + os.setObjectRef(isInfL3); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, X86Register.ESP, 0); + os.writeTEST(X86Register.EAX, 0x80000000); + os.writeJCC(doneL3, X86Constants.JNZ); + os.writeMOV_Const(X86Register.EAX, 0x7FFFFFFF); + os.writeMOV(X86Constants.BITS32, X86Register.EBP, lhsDisp, X86Register.EAX); + os.writeJMP(doneL3); + os.setObjectRef(overflowL3); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, X86Register.ESP, 0); + os.writeTEST(X86Register.EAX, 0x80000000); + os.writeJCC(doneL3, X86Constants.JNZ); + os.writeMOV_Const(X86Register.EAX, 0x7FFFFFFF); + os.writeMOV(X86Constants.BITS32, X86Register.EBP, lhsDisp, X86Register.EAX); + os.setObjectRef(doneL3); + os.writeLEA(X86Register.ESP, X86Register.ESP, 4); + os.writePOP(X86Register.EAX); break; + } case F2L: case F2D: @@ -671,10 +783,48 @@ public void generateCodeFor(UnaryQuad quad, int lhsDisp, UnaryOperation opera case L2D: throw new IllegalArgumentException("Unknown operation: " + operation); - case F2I: + case F2I: { + final String uid4 = "f2i_" + (++labelCounter) + "_"; + final Label doneL4 = new Label(uid4 + "fix"); + final Label overflowL4 = new Label(uid4 + "ovf"); + final Label isInfL4 = new Label(uid4 + "inf"); os.writeFLD32(X86Register.EBP, rhsDisp); + os.writePUSH(X86Register.EAX); + os.writeLEA(X86Register.ESP, X86Register.ESP, -4); + os.writeFSTP32(X86Register.ESP, 0); + os.writeFLD32(X86Register.ESP, 0); os.writeFISTP32(X86Register.EBP, lhsDisp); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, X86Register.EBP, lhsDisp); + os.writeCMP_Const(X86Register.EAX, 0x80000000); + os.writeJCC(doneL4, X86Constants.JNE); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, X86Register.ESP, 0); + os.writeAND(X86Register.EAX, 0x7F800000); + os.writeCMP_Const(X86Register.EAX, 0x7F800000); + os.writeJCC(overflowL4, X86Constants.JNE); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, X86Register.ESP, 0); + os.writeTEST(X86Register.EAX, 0x007FFFFF); + os.writeJCC(isInfL4, X86Constants.JZ); + os.writeXOR(X86Register.EAX, X86Register.EAX); + os.writeMOV(X86Constants.BITS32, X86Register.EBP, lhsDisp, X86Register.EAX); + os.writeJMP(doneL4); + os.setObjectRef(isInfL4); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, X86Register.ESP, 0); + os.writeTEST(X86Register.EAX, 0x80000000); + os.writeJCC(doneL4, X86Constants.JNZ); + os.writeMOV_Const(X86Register.EAX, 0x7FFFFFFF); + os.writeMOV(X86Constants.BITS32, X86Register.EBP, lhsDisp, X86Register.EAX); + os.writeJMP(doneL4); + os.setObjectRef(overflowL4); + os.writeMOV(X86Constants.BITS32, X86Register.EAX, X86Register.ESP, 0); + os.writeTEST(X86Register.EAX, 0x80000000); + os.writeJCC(doneL4, X86Constants.JNZ); + os.writeMOV_Const(X86Register.EAX, 0x7FFFFFFF); + os.writeMOV(X86Constants.BITS32, X86Register.EBP, lhsDisp, X86Register.EAX); + os.setObjectRef(doneL4); + os.writeLEA(X86Register.ESP, X86Register.ESP, 4); + os.writePOP(X86Register.EAX); break; + } case F2L: case F2D: diff --git a/core/src/test/org/jnode/test/bugs/ConversionTest.java b/core/src/test/org/jnode/test/bugs/ConversionTest.java new file mode 100644 index 0000000000..8d8abeaa81 --- /dev/null +++ b/core/src/test/org/jnode/test/bugs/ConversionTest.java @@ -0,0 +1,61 @@ +package org.jnode.test.bugs; + +/** Test for float/double to int/long conversion with NaN/infinity/overflow. */ +public class ConversionTest { + + static int failures = 0; + + static void check(long actual, long expected, String name) { + if (actual != expected) { + System.out.println("FAIL: " + name + " got " + actual + " expected " + expected); + failures++; + } + } + + static void check(int actual, int expected, String name) { + if (actual != expected) { + System.out.println("FAIL: " + name + " got " + actual + " expected " + expected); + failures++; + } + } + + public static void main(String[] a) { + // NaN conversion + check((long) Double.NaN, 0L, "(long)NaN"); + check((int) Float.NaN, 0, "(int)NaNf"); + + // Positive infinity + check((long) Double.POSITIVE_INFINITY, Long.MAX_VALUE, "(long)+Inf"); + check((int) Float.POSITIVE_INFINITY, Integer.MAX_VALUE, "(int)+INff"); + + // Negative infinity + check((long) Double.NEGATIVE_INFINITY, Long.MIN_VALUE, "(long)-Inf"); + check((int) Float.NEGATIVE_INFINITY, Integer.MIN_VALUE, "(int)-INff"); + + // Normal values still work + check((long) 1.5, 1L, "(long)1.5"); + check((int) 1.5f, 1, "(int)1.5f"); + check((long) -1.5, -1L, "(long)-1.5"); + check((int) -1.5f, -1, "(int)-1.5f"); + + // Zero + check((long) 0.0, 0L, "(long)0.0"); + check((long) -0.0, 0L, "(long)-0.0"); + + // Overflow + check((long) 1e20, Long.MAX_VALUE, "(long)1e20 overflow"); + check((long) -1e20, Long.MIN_VALUE, "(long)-1e20 underflow"); + check((int) 1e20f, Integer.MAX_VALUE, "(int)1e20f overflow"); + check((int) -1e20f, Integer.MIN_VALUE, "(int)-1e20f underflow"); + + // MAX/MIN_VALUE boundaries + check((long) Long.MAX_VALUE, Long.MAX_VALUE, "(long)Long.MAX_VALUE"); + check((long) Long.MIN_VALUE, Long.MIN_VALUE, "(long)Long.MIN_VALUE"); + + if (failures == 0) { + System.out.println("All tests PASS"); + } else { + System.out.println("FAILURES: " + failures); + } + } +}