Bug 1289054 - Part 18: Implement the 64bit variant of WasmTruncate on arm, r=bbouvier
authorHannes Verschore <hv1989@gmail.com>
Fri, 29 Jul 2016 16:53:49 +0200
changeset 349437 48d273fefe73d63673d7a0497864e5ac0701a8f7
parent 349436 2f7ac340ea3c3fa7c3c1d1b37340f6d2ed56f4a5
child 349438 ce45ee774edb69d4e94d3f834b558776782bfac0
push id1230
push userjlund@mozilla.com
push dateMon, 31 Oct 2016 18:13:35 +0000
treeherdermozilla-release@5e06e3766db2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1289054
milestone50.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 1289054 - Part 18: Implement the 64bit variant of WasmTruncate on arm, r=bbouvier
js/src/asmjs/WasmTypes.cpp
js/src/asmjs/WasmTypes.h
js/src/jit/IonTypes.h
js/src/jit/arm/CodeGenerator-arm.cpp
js/src/jit/arm/CodeGenerator-arm.h
js/src/jit/arm/LIR-arm.h
js/src/jit/arm/LOpcodes-arm.h
js/src/jit/arm/Lowering-arm.cpp
js/src/jit/arm/Simulator-arm.cpp
--- a/js/src/asmjs/WasmTypes.cpp
+++ b/js/src/asmjs/WasmTypes.cpp
@@ -195,16 +195,40 @@ static int64_t
 UModI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
 {
     uint64_t x = ((uint64_t)x_hi << 32) + x_lo;
     uint64_t y = ((uint64_t)y_hi << 32) + y_lo;
     MOZ_ASSERT(y != 0);
     return x % y;
 }
 
+static int64_t
+TruncateDoubleToInt64(double input)
+{
+    // Note: INT64_MAX is not representable in double. It is actually INT64_MAX + 1.
+    // Therefore also sending the failure value.
+    if (input >= double(INT64_MAX))
+        return 0x8000000000000000;
+    if (input < double(INT64_MIN))
+        return 0x8000000000000000;
+    return int64_t(input);
+}
+
+static uint64_t
+TruncateDoubleToUint64(double input)
+{
+    // Note: UINT64_MAX is not representable in double. It is actually UINT64_MAX + 1.
+    // Therefore also sending the failure value.
+    if (input >= double(UINT64_MAX))
+        return 0x8000000000000000;
+    if (input <= -1.0)
+        return 0x8000000000000000;
+    return uint64_t(input);
+}
+
 template <class F>
 static inline void*
 FuncCast(F* pf, ABIFunctionType type)
 {
     void *pv = JS_FUNC_TO_DATA_PTR(void*, pf);
 #ifdef JS_SIMULATOR
     pv = Simulator::RedirectNativeFunction(pv, type);
 #endif
@@ -242,16 +266,20 @@ wasm::AddressOf(SymbolicAddress imm, Exc
       case SymbolicAddress::DivI64:
         return FuncCast(DivI64, Args_General4);
       case SymbolicAddress::UDivI64:
         return FuncCast(UDivI64, Args_General4);
       case SymbolicAddress::ModI64:
         return FuncCast(ModI64, Args_General4);
       case SymbolicAddress::UModI64:
         return FuncCast(UModI64, Args_General4);
+      case SymbolicAddress::TruncateDoubleToUint64:
+        return FuncCast(TruncateDoubleToUint64, Args_Int64_Double);
+      case SymbolicAddress::TruncateDoubleToInt64:
+        return FuncCast(TruncateDoubleToInt64, Args_Int64_Double);
 #if defined(JS_CODEGEN_ARM)
       case SymbolicAddress::aeabi_idivmod:
         return FuncCast(__aeabi_idivmod, Args_General2);
       case SymbolicAddress::aeabi_uidivmod:
         return FuncCast(__aeabi_uidivmod, Args_General2);
       case SymbolicAddress::AtomicCmpXchg:
         return FuncCast<int32_t (int32_t, int32_t, int32_t, int32_t)>(js::atomics_cmpxchg_asm_callout, Args_General4);
       case SymbolicAddress::AtomicXchg:
--- a/js/src/asmjs/WasmTypes.h
+++ b/js/src/asmjs/WasmTypes.h
@@ -896,16 +896,18 @@ enum class SymbolicAddress
     CallImport_I64,
     CallImport_F64,
     CoerceInPlace_ToInt32,
     CoerceInPlace_ToNumber,
     DivI64,
     UDivI64,
     ModI64,
     UModI64,
+    TruncateDoubleToInt64,
+    TruncateDoubleToUint64,
     Limit
 };
 
 void*
 AddressOf(SymbolicAddress imm, ExclusiveContext* cx);
 
 // A wasm::Trap is a reason for why we reached a trap in executed code. Each
 // different trap is mapped to a different error message.
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -757,36 +757,40 @@ ScalarTypeToLength(Scalar::Type type)
 #define CHECK_OSIPOINT_REGISTERS 1
 
 #endif // DEBUG
 
 enum {
     ArgType_General = 0x1,
     ArgType_Double  = 0x2,
     ArgType_Float32 = 0x3,
+    ArgType_Int64 = 0x4,
 
     RetType_Shift   = 0x0,
-    ArgType_Shift   = 0x2,
-    ArgType_Mask    = 0x3
+    ArgType_Shift   = 0x3,
+    ArgType_Mask    = 0x7
 };
 
 enum ABIFunctionType
 {
     // VM functions that take 0-9 non-double arguments
     // and return a non-double value.
     Args_General0 = ArgType_General << RetType_Shift,
     Args_General1 = Args_General0 | (ArgType_General << (ArgType_Shift * 1)),
     Args_General2 = Args_General1 | (ArgType_General << (ArgType_Shift * 2)),
     Args_General3 = Args_General2 | (ArgType_General << (ArgType_Shift * 3)),
     Args_General4 = Args_General3 | (ArgType_General << (ArgType_Shift * 4)),
     Args_General5 = Args_General4 | (ArgType_General << (ArgType_Shift * 5)),
     Args_General6 = Args_General5 | (ArgType_General << (ArgType_Shift * 6)),
     Args_General7 = Args_General6 | (ArgType_General << (ArgType_Shift * 7)),
     Args_General8 = Args_General7 | (ArgType_General << (ArgType_Shift * 8)),
 
+    // int64 f(double)
+    Args_Int64_Double = (ArgType_Int64 << RetType_Shift) | (ArgType_Double << ArgType_Shift),
+
     // double f()
     Args_Double_None = ArgType_Double << RetType_Shift,
 
     // int f(double)
     Args_Int_Double = Args_General0 | (ArgType_Double << ArgType_Shift),
 
     // float f(float)
     Args_Float32_Float32 = (ArgType_Float32 << RetType_Shift) | (ArgType_Float32 << ArgType_Shift),
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -2964,16 +2964,55 @@ CodeGeneratorARM::visitWasmTruncateToInt
     masm.ma_cmp(output, Imm32(INT32_MAX));
     masm.ma_cmp(output, Imm32(INT32_MIN), Assembler::NotEqual);
     masm.ma_b(ool->entry(), Assembler::Equal);
 
     masm.bind(ool->rejoin());
 }
 
 void
+CodeGeneratorARM::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir)
+{
+    FloatRegister input = ToFloatRegister(lir->input());
+    FloatRegister inputDouble = input;
+    Register64 output = ToOutRegister64(lir);
+
+    MWasmTruncateToInt64* mir = lir->mir();
+    MIRType fromType = mir->input()->type();
+
+    auto* ool = new(alloc()) OutOfLineWasmTruncateCheck(mir, input);
+    addOutOfLineCode(ool, mir);
+
+    ScratchDoubleScope scratchScope(masm);
+    if (fromType == MIRType::Float32) {
+        inputDouble = ScratchDoubleReg;
+        masm.convertFloat32ToDouble(input, inputDouble);
+    }
+
+    masm.Push(input);
+
+    masm.setupUnalignedABICall(output.high);
+    masm.passABIArg(inputDouble, MoveOp::DOUBLE);
+    if (lir->mir()->isUnsigned())
+        masm.callWithABI(wasm::SymbolicAddress::TruncateDoubleToUint64);
+    else
+        masm.callWithABI(wasm::SymbolicAddress::TruncateDoubleToInt64);
+
+    masm.Pop(input);
+
+    masm.ma_cmp(output.high, Imm32(0x80000000));
+    masm.ma_cmp(output.low, Imm32(0x00000000), Assembler::Equal);
+    masm.ma_b(ool->entry(), Assembler::Equal);
+
+    masm.bind(ool->rejoin());
+
+    MOZ_ASSERT(ReturnReg64 == output);
+}
+
+void
 CodeGeneratorARM::visitOutOfLineWasmTruncateCheck(OutOfLineWasmTruncateCheck* ool)
 {
     MIRType fromType = ool->fromType();
     FloatRegister input = ool->input();
 
     ScratchDoubleScope scratchScope(masm);
     FloatRegister scratch;
 
@@ -2984,49 +3023,67 @@ CodeGeneratorARM::visitOutOfLineWasmTrun
     else if (fromType == MIRType::Float32)
         masm.branchFloat(Assembler::DoubleUnordered, input, input, &inputIsNaN);
     else
         MOZ_CRASH("unexpected type in visitOutOfLineWasmTruncateCheck");
 
     // Handle special values.
     Label fail;
 
+    // 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;
-    if (ool->isUnsigned()) {
-        minValue = -1;
-        maxValue = double(UINT32_MAX) + 1.0;
+    Assembler::DoubleCondition minCond = Assembler::DoubleLessThanOrEqual;
+    Assembler::DoubleCondition maxCond = Assembler::DoubleGreaterThanOrEqual;
+    if (ool->toType() == MIRType::Int64) {
+        if (ool->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 {
-        minValue = double(INT32_MIN) - 1.0;
-        maxValue = double(INT32_MAX) + 1.0;
+        if (ool->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;
+        }
     }
 
     if (fromType == MIRType::Double) {
         scratch = scratchScope.doubleOverlay();
         masm.loadConstantDouble(minValue, scratch);
-        masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, scratch, &fail);
+        masm.branchDouble(minCond, input, scratch, &fail);
 
         masm.loadConstantDouble(maxValue, scratch);
-        masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, scratch, &fail);
+        masm.branchDouble(maxCond, input, scratch, &fail);
     } else {
         MOZ_ASSERT(fromType == MIRType::Float32);
         scratch = scratchScope.singleOverlay();
-
-        // For int32, float(minValue) rounds to INT32_MIN, we want to fail when
-        // input < float(minValue).
-        // For uint32, float(minValue) == -1, we want to fail when input <= -1.
-        auto condition = minValue == -1.0
-                         ? Assembler::DoubleLessThanOrEqual
-                         : Assembler::DoubleLessThan;
-
         masm.loadConstantFloat32(float(minValue), scratch);
-        masm.branchFloat(condition, input, scratch, &fail);
-
-        // maxValue is exactly represented in both cases.
+        masm.branchFloat(minCond, input, scratch, &fail);
+
         masm.loadConstantFloat32(float(maxValue), scratch);
-        masm.branchFloat(Assembler::DoubleGreaterThanOrEqual, input, scratch, &fail);
+        masm.branchFloat(maxCond, input, scratch, &fail);
     }
 
     // We had an actual correct value, get back to where we were.
     masm.ma_b(ool->rejoin());
 
     // Handle errors.
     masm.bind(&fail);
     masm.jump(wasm::JumpTarget::IntegerOverflow);
--- a/js/src/jit/arm/CodeGenerator-arm.h
+++ b/js/src/jit/arm/CodeGenerator-arm.h
@@ -172,16 +172,17 @@ class CodeGeneratorARM : public CodeGene
     virtual void visitAsmJSPassStackArgI64(LAsmJSPassStackArgI64* lir);
     virtual void visitAsmSelectI64(LAsmSelectI64* lir);
     virtual void visitAsmReinterpretFromI64(LAsmReinterpretFromI64* lir);
     virtual void visitAsmReinterpretToI64(LAsmReinterpretToI64* lir);
     virtual void visitPopcntI64(LPopcntI64* ins);
     virtual void visitClzI64(LClzI64* ins);
     virtual void visitCtzI64(LCtzI64* ins);
     virtual void visitNotI64(LNotI64* ins);
+    virtual void visitWasmTruncateToInt64(LWasmTruncateToInt64* ins);
 
     // Out of line visitors.
     void visitOutOfLineBailout(OutOfLineBailout* ool);
     void visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool);
 
   protected:
     ValueOperand ToValue(LInstruction* ins, size_t pos);
     ValueOperand ToOutValue(LInstruction* ins);
--- a/js/src/jit/arm/LIR-arm.h
+++ b/js/src/jit/arm/LIR-arm.h
@@ -536,12 +536,27 @@ class LAsmJSAtomicBinopCallout : public 
         return getOperand(1);
     }
 
     const MAsmJSAtomicBinopHeap* mir() const {
         return mir_->toAsmJSAtomicBinopHeap();
     }
 };
 
+class LWasmTruncateToInt64 : public LCallInstructionHelper<INT64_PIECES, 1, 0>
+{
+  public:
+    LIR_HEADER(WasmTruncateToInt64);
+
+    LWasmTruncateToInt64(const LAllocation& in)
+    {
+        setOperand(0, in);
+    }
+
+    MWasmTruncateToInt64* mir() const {
+        return mir_->toWasmTruncateToInt64();
+    }
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm_LIR_arm_h */
--- a/js/src/jit/arm/LOpcodes-arm.h
+++ b/js/src/jit/arm/LOpcodes-arm.h
@@ -16,11 +16,12 @@
     _(ModMaskI)                 \
     _(UDiv)                     \
     _(UMod)                     \
     _(SoftUDivOrMod)            \
     _(AsmJSCompareExchangeCallout) \
     _(AsmJSAtomicExchangeCallout) \
     _(AsmJSAtomicBinopCallout)  \
     _(DivOrModI64)              \
-    _(UDivOrModI64)
+    _(UDivOrModI64)             \
+    _(WasmTruncateToInt64)
 
 #endif /* jit_arm_LOpcodes_arm_h */
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -910,17 +910,20 @@ LIRGeneratorARM::visitRandom(MRandom* in
                                         temp(),
                                         temp());
     defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
 }
 
 void
 LIRGeneratorARM::visitWasmTruncateToInt64(MWasmTruncateToInt64* ins)
 {
-    MOZ_CRASH("NY");
+    MDefinition* opd = ins->input();
+    MOZ_ASSERT(opd->type() == MIRType::Double || opd->type() == MIRType::Float32);
+
+    defineReturn(new(alloc()) LWasmTruncateToInt64(useRegisterAtStart(opd)), ins);
 }
 
 void
 LIRGeneratorARM::visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins)
 {
     MOZ_CRASH("NY");
 }
 
--- a/js/src/jit/arm/Simulator-arm.cpp
+++ b/js/src/jit/arm/Simulator-arm.cpp
@@ -2282,16 +2282,17 @@ typedef int64_t (*Prototype_General7)(in
                                       int32_t arg4, int32_t arg5, int32_t arg6);
 typedef int64_t (*Prototype_General8)(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3,
                                       int32_t arg4, int32_t arg5, int32_t arg6, int32_t arg7);
 
 typedef double (*Prototype_Double_None)();
 typedef double (*Prototype_Double_Double)(double arg0);
 typedef double (*Prototype_Double_Int)(int32_t arg0);
 typedef int32_t (*Prototype_Int_Double)(double arg0);
+typedef int64_t (*Prototype_Int64_Double)(double arg0);
 typedef int32_t (*Prototype_Int_DoubleIntInt)(double arg0, int32_t arg1, int32_t arg2);
 typedef int32_t (*Prototype_Int_IntDoubleIntInt)(int32_t arg0, double arg1, int32_t arg2,
                                                  int32_t arg3);
 typedef float (*Prototype_Float32_Float32)(float arg0);
 
 typedef double (*Prototype_DoubleInt)(double arg0, int32_t arg1);
 typedef double (*Prototype_Double_IntDouble)(int32_t arg0, double arg1);
 typedef double (*Prototype_Double_DoubleDouble)(double arg0, double arg1);
@@ -2424,16 +2425,26 @@ Simulator::softwareInterrupt(SimInstruct
             Prototype_General8 target = reinterpret_cast<Prototype_General8>(external);
             int32_t arg6 = stack_pointer[2];
             int32_t arg7 = stack_pointer[3];
             int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
             scratchVolatileRegisters(/* scratchFloat = true */);
             setCallResult(result);
             break;
           }
+          case Args_Int64_Double: {
+            double dval0, dval1;
+            int32_t ival;
+            getFpArgs(&dval0, &dval1, &ival);
+            Prototype_Int64_Double target = reinterpret_cast<Prototype_Int64_Double>(external);
+            int64_t result = target(dval0);
+            scratchVolatileRegisters(/* scratchFloat = true */);
+            setCallResult(result);
+            break;
+          }
           case Args_Double_None: {
             Prototype_Double_None target = reinterpret_cast<Prototype_Double_None>(external);
             double dresult = target();
             scratchVolatileRegisters(/* scratchFloat = true */);
             setCallResultDouble(dresult);
             break;
           }
           case Args_Int_Double: {