Bug 1393723 - Fix handling of wasm truncate-to-uint32 on mips. r=lth
authorDragan Mladjenovic <dragan.mladjenovic@rt-rk.com>
Fri, 25 Aug 2017 02:44:00 -0400
changeset 377556 68604930b47b
parent 377555 2a7f4271c027
child 377557 4fd740b71f29
push id94292
push userryanvm@gmail.com
push dateTue, 29 Aug 2017 18:45:26 +0000
treeherdermozilla-inbound@81cdf89896a3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslth
bugs1393723
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1393723 - Fix handling of wasm truncate-to-uint32 on mips. r=lth The trunc.l.d/s cannot be used on mips32 requiring a different algorithm for fp -> uint32 conversion.
js/src/jit/MacroAssembler.h
js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp
js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
js/src/jit/mips32/MacroAssembler-mips32.cpp
js/src/jit/mips64/MacroAssembler-mips64.cpp
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -1453,22 +1453,22 @@ class MacroAssembler : public MacroAssem
 
     // `ptr` will always be updated.
     void wasmUnalignedStoreI64(const wasm::MemoryAccessDesc& access, Register64 value,
                                Register memoryBase, Register ptr, Register ptrScratch,
                                Register tmp)
         DEFINED_ON(arm);
 
     // wasm specific methods, used in both the wasm baseline compiler and ion.
-    void wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86, x64, arm);
-    void wasmTruncateDoubleToInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86_shared, arm);
+    void wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86, x64, arm, mips32, mips64);
+    void wasmTruncateDoubleToInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86_shared, arm, mips_shared);
     void outOfLineWasmTruncateDoubleToInt32(FloatRegister input, bool isUnsigned, wasm::BytecodeOffset off, Label* rejoin) DEFINED_ON(x86_shared);
 
-    void wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86, x64, arm);
-    void wasmTruncateFloat32ToInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86_shared, arm);
+    void wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86, x64, arm, mips32, mips64);
+    void wasmTruncateFloat32ToInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86_shared, arm, mips_shared);
     void outOfLineWasmTruncateFloat32ToInt32(FloatRegister input, bool isUnsigned, wasm::BytecodeOffset off, Label* rejoin) DEFINED_ON(x86_shared);
 
     void outOfLineWasmTruncateDoubleToInt64(FloatRegister input, bool isUnsigned, wasm::BytecodeOffset off, Label* rejoin) DEFINED_ON(x86_shared);
     void outOfLineWasmTruncateFloat32ToInt64(FloatRegister input, bool isUnsigned, wasm::BytecodeOffset off, Label* rejoin) DEFINED_ON(x86_shared);
 
     // This function takes care of loading the callee's TLS and pinned regs but
     // it is the caller's responsibility to save/restore TLS or pinned regs.
     void wasmCallImport(const wasm::CallSiteDesc& desc, const wasm::CalleeDesc& callee);
--- a/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp
+++ b/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp
@@ -1454,117 +1454,112 @@ void
 CodeGeneratorMIPSShared::visitWasmTruncateToInt32(LWasmTruncateToInt32* lir)
 {
     auto input = ToFloatRegister(lir->input());
     auto output = ToRegister(lir->output());
 
     MWasmTruncateToInt32* mir = lir->mir();
     MIRType fromType = mir->input()->type();
 
+    MOZ_ASSERT(fromType == MIRType::Double || fromType == MIRType::Float32);
+
     auto* ool = new (alloc()) OutOfLineWasmTruncateCheck(mir, input);
     addOutOfLineCode(ool, mir);
 
+    Label* oolEntry = ool->entry();
     if (mir->isUnsigned()) {
-        // When the input value is Infinity, NaN, or rounds to an integer outside the
-        // range [INT64_MIN; INT64_MAX + 1[, the Invalid Operation flag is set in the FCSR.
         if (fromType == MIRType::Double)
-            masm.as_truncld(ScratchDoubleReg, input);
+            masm.wasmTruncateDoubleToUInt32(input, output, oolEntry);
         else if (fromType == MIRType::Float32)
-            masm.as_truncls(ScratchDoubleReg, input);
+            masm.wasmTruncateFloat32ToUInt32(input, output, oolEntry);
         else
-            MOZ_CRASH("unexpected type in visitWasmTruncateToInt32");
-
-        // Check that the result is in the uint32_t range.
-        masm.moveFromDoubleHi(ScratchDoubleReg, output);
-        masm.as_cfc1(ScratchRegister, Assembler::FCSR);
-        masm.as_ext(ScratchRegister, ScratchRegister, 16, 1);
-        masm.ma_or(output, ScratchRegister);
-        masm.ma_b(output, Imm32(0), ool->entry(), Assembler::NotEqual);
-
-        masm.moveFromFloat32(ScratchDoubleReg, output);
+            MOZ_CRASH("unexpected type");
         return;
     }
 
-    // When the input value is Infinity, NaN, or rounds to an integer outside the
-    // range [INT32_MIN; INT32_MAX + 1[, the Invalid Operation flag is set in the FCSR.
     if (fromType == MIRType::Double)
-        masm.as_truncwd(ScratchFloat32Reg, input);
+        masm.wasmTruncateDoubleToInt32(input, output, oolEntry);
     else if (fromType == MIRType::Float32)
-        masm.as_truncws(ScratchFloat32Reg, input);
+        masm.wasmTruncateFloat32ToInt32(input, output, oolEntry);
     else
-        MOZ_CRASH("unexpected type in visitWasmTruncateToInt32");
-
-    // Check that the result is in the int32_t range.
-    masm.as_cfc1(output, Assembler::FCSR);
-    masm.as_ext(output, output, 16, 1);
-    masm.ma_b(output, Imm32(0), ool->entry(), Assembler::NotEqual);
+        MOZ_CRASH("unexpected type");
 
     masm.bind(ool->rejoin());
-    masm.moveFromFloat32(ScratchFloat32Reg, output);
 }
 
 void
 CodeGeneratorMIPSShared::visitOutOfLineWasmTruncateCheck(OutOfLineWasmTruncateCheck* ool)
 {
     FloatRegister input = ool->input();
     MIRType fromType = ool->fromType();
     MIRType toType = ool->toType();
-
+    bool isUnsigned = ool->isUnsigned();
     // Eagerly take care of NaNs.
     Label inputIsNaN;
     if (fromType == MIRType::Double)
         masm.branchDouble(Assembler::DoubleUnordered, input, input, &inputIsNaN);
     else if (fromType == MIRType::Float32)
         masm.branchFloat(Assembler::DoubleUnordered, input, input, &inputIsNaN);
     else
         MOZ_CRASH("unexpected type in visitOutOfLineWasmTruncateCheck");
 
-    Label fail;
-
-    // Handle special values (not needed for unsigned values).
-    if (!ool->isUnsigned()) {
-        if (toType == MIRType::Int32) {
-            // MWasmTruncateToInt32
-            if (fromType == MIRType::Double) {
-                // we've used truncwd. the only valid double values that can
-                // truncate to INT32_MIN are in ]INT32_MIN - 1; INT32_MIN].
-                masm.loadConstantDouble(double(INT32_MIN) - 1.0, ScratchDoubleReg);
-                masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, ScratchDoubleReg, &fail);
-
-                masm.loadConstantDouble(double(INT32_MIN), ScratchDoubleReg);
-                masm.branchDouble(Assembler::DoubleGreaterThan, input, ScratchDoubleReg, &fail);
-
-                masm.as_truncwd(ScratchFloat32Reg, ScratchDoubleReg);
-                masm.jump(ool->rejoin());
-            }
-        } else if (toType == MIRType::Int64) {
-            if (fromType == MIRType::Double) {
-                masm.loadConstantDouble(double(INT64_MIN), ScratchDoubleReg);
-                masm.branchDouble(Assembler::DoubleLessThan, input, ScratchDoubleReg, &fail);
-
-                masm.loadConstantDouble(double(INT64_MAX) + 1.0, ScratchDoubleReg);
-                masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input,
-                                  ScratchDoubleReg, &fail);
-                masm.jump(ool->rejoin());
-            }
+    // By default test for the following inputs and bail:
+    // signed:   ] -Inf, INTXX_MIN - 1.0 ] and [ INTXX_MAX + 1.0 : +Inf [
+    // unsigned: ] -Inf, -1.0 ] and [ UINTXX_MAX + 1.0 : +Inf [
+    // Note: we cannot always represent those exact values. As a result
+    // this changes the actual comparison a bit.
+    double minValue, maxValue;
+    Assembler::DoubleCondition minCond = Assembler::DoubleLessThanOrEqual;
+    Assembler::DoubleCondition maxCond = Assembler::DoubleGreaterThanOrEqual;
+    if (toType == MIRType::Int64) {
+        if (isUnsigned) {
+            minValue = -1;
+            maxValue = double(UINT64_MAX) + 1.0;
+        } else {
+            // In the float32/double range there exists no value between
+            // INT64_MIN and INT64_MIN - 1.0. Making INT64_MIN the lower-bound.
+            minValue = double(INT64_MIN);
+            minCond = Assembler::DoubleLessThan;
+            maxValue = double(INT64_MAX) + 1.0;
         }
     } else {
-        if (toType == MIRType::Int64) {
-            if (fromType == MIRType::Double) {
-                masm.loadConstantDouble(double(-1), ScratchDoubleReg);
-                masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, ScratchDoubleReg, &fail);
-
-                masm.loadConstantDouble(double(UINT64_MAX) + 1.0, ScratchDoubleReg);
-                masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input,
-                                  ScratchDoubleReg, &fail);
-                masm.jump(ool->rejoin());
+        if (isUnsigned) {
+            minValue = -1;
+            maxValue = double(UINT32_MAX) + 1.0;
+        } else {
+            if (fromType == MIRType::Float32) {
+                // In the float32 range there exists no value between
+                // INT32_MIN and INT32_MIN - 1.0. Making INT32_MIN the lower-bound.
+                minValue = double(INT32_MIN);
+                minCond = Assembler::DoubleLessThan;
+            } else {
+                minValue = double(INT32_MIN) - 1.0;
             }
-       }
+            maxValue = double(INT32_MAX) + 1.0;
+        }
     }
 
+    Label fail;
+
+    if (fromType == MIRType::Double) {
+        masm.loadConstantDouble(minValue, ScratchDoubleReg);
+        masm.branchDouble(minCond, input, ScratchDoubleReg, &fail);
+
+        masm.loadConstantDouble(maxValue, ScratchDoubleReg);
+        masm.branchDouble(maxCond, input, ScratchDoubleReg, &fail);
+    } else {
+        masm.loadConstantFloat32(float(minValue), ScratchFloat32Reg);
+        masm.branchFloat(minCond, input, ScratchFloat32Reg, &fail);
+
+        masm.loadConstantFloat32(float(maxValue), ScratchFloat32Reg);
+        masm.branchFloat(maxCond, input, ScratchFloat32Reg, &fail);
+    }
+
+    masm.jump(ool->rejoin());
+
     // Handle errors.
     masm.bind(&fail);
     masm.jump(trap(ool, wasm::Trap::IntegerOverflow));
 
     masm.bind(&inputIsNaN);
     masm.jump(trap(ool, wasm::Trap::InvalidConversionToInteger));
 }
 
--- a/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
+++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
@@ -1756,9 +1756,31 @@ MacroAssembler::branchPtrInNurseryChunk(
 }
 
 void
 MacroAssembler::comment(const char* msg)
 {
     Assembler::comment(msg);
 }
 
+
+void
+MacroAssembler::wasmTruncateDoubleToInt32(FloatRegister input, Register output, Label* oolEntry)
+{
+    as_truncwd(ScratchFloat32Reg, input);
+    as_cfc1(ScratchRegister, Assembler::FCSR);
+    moveFromFloat32(ScratchFloat32Reg, output);
+    as_ext(ScratchRegister, ScratchRegister, 6, 1);
+    ma_b(ScratchRegister, Imm32(0), oolEntry, Assembler::NotEqual);
+}
+
+
+void
+MacroAssembler::wasmTruncateFloat32ToInt32(FloatRegister input, Register output, Label* oolEntry)
+{
+    as_truncws(ScratchFloat32Reg, input);
+    as_cfc1(ScratchRegister, Assembler::FCSR);
+    moveFromFloat32(ScratchFloat32Reg, output);
+    as_ext(ScratchRegister, ScratchRegister, 6, 1);
+    ma_b(ScratchRegister, Imm32(0), oolEntry, Assembler::NotEqual);
+}
+
 //}}} check_macroassembler_style
--- a/js/src/jit/mips32/MacroAssembler-mips32.cpp
+++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp
@@ -2422,9 +2422,57 @@ MacroAssembler::storeUnboxedValue(const 
 
 template void
 MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
                                   const Address& dest, MIRType slotType);
 template void
 MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
                                   const BaseIndex& dest, MIRType slotType);
 
+
+void
+MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry)
+{
+
+    loadConstantDouble(double(-1.0), ScratchDoubleReg);
+    branchDouble(Assembler::DoubleLessThanOrEqual, input, ScratchDoubleReg, oolEntry);
+
+    loadConstantDouble(double(UINT32_MAX) + 1.0, ScratchDoubleReg);
+    branchDouble(Assembler::DoubleGreaterThanOrEqualOrUnordered, input, ScratchDoubleReg, oolEntry);
+    Label done, simple;
+    loadConstantDouble(double(0x80000000UL), ScratchDoubleReg);
+    branchDouble(Assembler::DoubleLessThan, input, ScratchDoubleReg, &simple);
+    as_subd(ScratchDoubleReg, input, ScratchDoubleReg);
+    as_truncwd(ScratchDoubleReg, ScratchDoubleReg);
+    moveFromFloat32(ScratchDoubleReg, output);
+    ma_li(ScratchRegister, Imm32(0x80000000UL));
+    ma_or(output, ScratchRegister);
+    ma_b(&done);
+    bind(&simple);
+    as_truncwd(ScratchDoubleReg, input);
+    moveFromFloat32(ScratchDoubleReg, output);
+    bind(&done);
+}
+
+void
+MacroAssembler::wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, Label* oolEntry)
+{
+    loadConstantFloat32(double(-1.0), ScratchDoubleReg);
+    branchFloat(Assembler::DoubleLessThanOrEqualOrUnordered, input, ScratchDoubleReg, oolEntry);
+
+    loadConstantFloat32(double(UINT32_MAX) + 1.0, ScratchDoubleReg);
+    branchFloat(Assembler::DoubleGreaterThanOrEqualOrUnordered, input, ScratchDoubleReg, oolEntry);
+    Label done, simple;
+    loadConstantFloat32(double(0x80000000UL), ScratchDoubleReg);
+    branchFloat(Assembler::DoubleLessThan, input, ScratchDoubleReg, &simple);
+    as_subs(ScratchDoubleReg, input, ScratchDoubleReg);
+    as_truncws(ScratchDoubleReg, ScratchDoubleReg);
+    moveFromFloat32(ScratchDoubleReg, output);
+    ma_li(ScratchRegister, Imm32(0x80000000UL));
+    ma_or(output, ScratchRegister);
+    ma_b(&done);
+    bind(&simple);
+    as_truncws(ScratchDoubleReg, input);
+    moveFromFloat32(ScratchDoubleReg, output);
+    bind(&done);
+}
+
 //}}} check_macroassembler_style
--- a/js/src/jit/mips64/MacroAssembler-mips64.cpp
+++ b/js/src/jit/mips64/MacroAssembler-mips64.cpp
@@ -2566,9 +2566,37 @@ MacroAssembler::storeUnboxedValue(const 
 
 template void
 MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
                                   const Address& dest, MIRType slotType);
 template void
 MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
                                   const BaseIndex& dest, MIRType slotType);
 
+
+void
+MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry)
+{
+    as_truncld(ScratchDoubleReg, input);
+    moveFromDoubleHi(ScratchDoubleReg, output);
+    as_cfc1(ScratchRegister, Assembler::FCSR);
+    as_ext(ScratchRegister, ScratchRegister, 6, 1);
+    ma_or(ScratchRegister, output);
+    moveFromFloat32(ScratchDoubleReg, output);
+    ma_b(ScratchRegister, Imm32(0), oolEntry, Assembler::NotEqual);
+
+
+}
+
+void
+MacroAssembler::wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, Label* oolEntry)
+{
+    as_truncls(ScratchDoubleReg, input);
+    moveFromDoubleHi(ScratchDoubleReg, output);
+    as_cfc1(ScratchRegister, Assembler::FCSR);
+    as_ext(ScratchRegister, ScratchRegister, 6, 1);
+    ma_or(ScratchRegister, output);
+    moveFromFloat32(ScratchDoubleReg, output);
+    ma_b(ScratchRegister, Imm32(0), oolEntry, Assembler::NotEqual);
+
+}
+
 //}}} check_macroassembler_style