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 332401 48d273fefe73d63673d7a0497864e5ac0701a8f7
parent 332400 2f7ac340ea3c3fa7c3c1d1b37340f6d2ed56f4a5
child 332402 ce45ee774edb69d4e94d3f834b558776782bfac0
push id9858
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 14:37:10 +0000
treeherdermozilla-aurora@203106ef6cb6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1289054
milestone50.0a1
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: {