Bug 1260737: wasm: Implement Reinterpret opcodes; r=luke
authorBenjamin Bouvier <benj@benj.me>
Wed, 30 Mar 2016 14:10:06 +0200
changeset 291247 ef04d22a4de586d67193e7011bd37aaecdf90164
parent 291246 bf8dc00cc76fad3ef265e391d84b41d6b3e86abc
child 291248 f53dbc1c638a131bff1b5b49ce08d71a8f0a56c9
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1260737
milestone48.0a1
Bug 1260737: wasm: Implement Reinterpret opcodes; r=luke MozReview-Commit-ID: 8n5BHkVhwNp
js/src/asmjs/Wasm.cpp
js/src/asmjs/WasmBinaryToText.cpp
js/src/asmjs/WasmIonCompile.cpp
js/src/asmjs/WasmTextToBinary.cpp
js/src/jit-test/tests/wasm/basic-conversion.js
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/arm/CodeGenerator-arm.cpp
js/src/jit/arm/CodeGenerator-arm.h
js/src/jit/shared/LIR-shared.h
js/src/jit/shared/LOpcodes-shared.h
js/src/jit/x64/CodeGenerator-x64.cpp
js/src/jit/x64/CodeGenerator-x64.h
js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
js/src/jit/x86-shared/CodeGenerator-x86-shared.h
--- a/js/src/asmjs/Wasm.cpp
+++ b/js/src/asmjs/Wasm.cpp
@@ -794,56 +794,52 @@ DecodeExpr(FunctionDecoder& f, ExprType*
       case Expr::F64Gt:
       case Expr::F64Ge:
         return DecodeComparisonOperator(f, ValType::F64, type);
       case Expr::I32WrapI64:
         return f.checkI64Support() &&
                DecodeConversionOperator(f, ValType::I32, ValType::I64, type);
       case Expr::I32TruncSF32:
       case Expr::I32TruncUF32:
+      case Expr::I32ReinterpretF32:
         return DecodeConversionOperator(f, ValType::I32, ValType::F32, type);
-      case Expr::I32ReinterpretF32:
-        return f.fail("NYI: reinterpret");
       case Expr::I32TruncSF64:
       case Expr::I32TruncUF64:
         return DecodeConversionOperator(f, ValType::I32, ValType::F64, type);
       case Expr::I64ExtendSI32:
       case Expr::I64ExtendUI32:
         return f.checkI64Support() &&
                DecodeConversionOperator(f, ValType::I64, ValType::I32, type);
       case Expr::I64TruncSF32:
       case Expr::I64TruncUF32:
         return f.checkI64Support() &&
                DecodeConversionOperator(f, ValType::I64, ValType::F32, type);
       case Expr::I64TruncSF64:
       case Expr::I64TruncUF64:
+      case Expr::I64ReinterpretF64:
         return f.checkI64Support() &&
                DecodeConversionOperator(f, ValType::I64, ValType::F64, type);
-      case Expr::I64ReinterpretF64:
-        return f.fail("NYI: i64");
       case Expr::F32ConvertSI32:
       case Expr::F32ConvertUI32:
+      case Expr::F32ReinterpretI32:
         return DecodeConversionOperator(f, ValType::F32, ValType::I32, type);
-      case Expr::F32ReinterpretI32:
-        return f.fail("NYI: reinterpret");
       case Expr::F32ConvertSI64:
       case Expr::F32ConvertUI64:
         return f.checkI64Support() &&
                DecodeConversionOperator(f, ValType::F32, ValType::I64, type);
       case Expr::F32DemoteF64:
         return DecodeConversionOperator(f, ValType::F32, ValType::F64, type);
       case Expr::F64ConvertSI32:
       case Expr::F64ConvertUI32:
         return DecodeConversionOperator(f, ValType::F64, ValType::I32, type);
       case Expr::F64ConvertSI64:
       case Expr::F64ConvertUI64:
+      case Expr::F64ReinterpretI64:
         return f.checkI64Support() &&
                DecodeConversionOperator(f, ValType::F64, ValType::I64, type);
-      case Expr::F64ReinterpretI64:
-        return f.fail("NYI: i64");
       case Expr::F64PromoteF32:
         return DecodeConversionOperator(f, ValType::F64, ValType::F32, type);
       case Expr::I32Load8S:
       case Expr::I32Load8U:
         return DecodeLoad(f, 1, ValType::I32, type);
       case Expr::I32Load16S:
       case Expr::I32Load16U:
         return DecodeLoad(f, 2, ValType::I32, type);
--- a/js/src/asmjs/WasmBinaryToText.cpp
+++ b/js/src/asmjs/WasmBinaryToText.cpp
@@ -1006,17 +1006,17 @@ RenderExpr(WasmRenderContext& c)
       case Expr::I32Ctz:
       case Expr::I32Popcnt:
       case Expr::I32Eqz:
         return RenderUnaryOperator(c, expr, ValType::I32);
       case Expr::I64Clz:
       case Expr::I64Ctz:
       case Expr::I64Popcnt:
         return RenderFail(c, "NYI: i64") &&
-            RenderUnaryOperator(c, expr, ValType::I64);
+               RenderUnaryOperator(c, expr, ValType::I64);
       case Expr::F32Abs:
       case Expr::F32Neg:
       case Expr::F32Ceil:
       case Expr::F32Floor:
       case Expr::F32Sqrt:
         return RenderUnaryOperator(c, expr, ValType::F32);
       case Expr::F32Trunc:
         return RenderFail(c, "NYI: trunc");
@@ -1113,38 +1113,35 @@ RenderExpr(WasmRenderContext& c)
       case Expr::F64Le:
       case Expr::F64Gt:
       case Expr::F64Ge:
         return RenderComparisonOperator(c, expr, ValType::F64);
       case Expr::I32WrapI64:
         return RenderConversionOperator(c, expr, ValType::I32, ValType::I64);
       case Expr::I32TruncSF32:
       case Expr::I32TruncUF32:
+      case Expr::I32ReinterpretF32:
         return RenderConversionOperator(c, expr, ValType::I32, ValType::F32);
-      case Expr::I32ReinterpretF32:
-        return RenderFail(c, "NYI: reinterpret");
       case Expr::I32TruncSF64:
       case Expr::I32TruncUF64:
         return RenderConversionOperator(c, expr, ValType::I32, ValType::F64);
       case Expr::I64ExtendSI32:
       case Expr::I64ExtendUI32:
         return RenderConversionOperator(c, expr, ValType::I64, ValType::I32);
       case Expr::I64TruncSF32:
       case Expr::I64TruncUF32:
         return RenderConversionOperator(c, expr, ValType::I64, ValType::F32);
       case Expr::I64TruncSF64:
       case Expr::I64TruncUF64:
+      case Expr::I64ReinterpretF64:
         return RenderConversionOperator(c, expr, ValType::I64, ValType::F64);
-      case Expr::I64ReinterpretF64:
-        return RenderFail(c, "NYI: i64");
       case Expr::F32ConvertSI32:
       case Expr::F32ConvertUI32:
+      case Expr::F32ReinterpretI32:
         return RenderConversionOperator(c, expr, ValType::F32, ValType::I32);
-      case Expr::F32ReinterpretI32:
-        return RenderFail(c, "NYI: reinterpret");
       case Expr::F32ConvertSI64:
       case Expr::F32ConvertUI64:
         return RenderConversionOperator(c, expr, ValType::F32, ValType::I64);
       case Expr::F32DemoteF64:
         return RenderConversionOperator(c, expr, ValType::F32, ValType::I64);
       case Expr::F64ConvertSI32:
       case Expr::F64ConvertUI32:
         return RenderConversionOperator(c, expr, ValType::F64, ValType::I32);
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -491,16 +491,25 @@ class FunctionCompiler
     {
         if (inDeadCode())
             return nullptr;
         MInt64ToFloatingPoint* ins = MInt64ToFloatingPoint::NewAsmJS(alloc(), op, type, isUnsigned);
         curBlock_->add(ins);
         return ins;
     }
 
+    MDefinition* reinterpret(MDefinition* op, MIRType to)
+    {
+        if (inDeadCode())
+            return nullptr;
+        auto* ins = MAsmReinterpret::New(alloc(), op, to);
+        curBlock_->add(ins);
+        return ins;
+    }
+
     template <class T>
     MDefinition* truncate(MDefinition* op, bool isUnsigned)
     {
         if (inDeadCode())
             return nullptr;
         T* ins = T::NewAsmJS(alloc(), op, isUnsigned);
         curBlock_->add(ins);
         return ins;
@@ -2435,16 +2444,26 @@ EmitConvertI64ToFloatingPoint(FunctionCo
     MDefinition* in;
     if (!EmitExpr(f, &in))
         return false;
     *def = f.convertI64ToFloatingPoint(in, ToMIRType(type), isUnsigned);
     return true;
 }
 
 static bool
+EmitReinterpret(FunctionCompiler& f, ValType to, MDefinition** def)
+{
+    MDefinition* in;
+    if (!EmitExpr(f, &in))
+        return false;
+    *def = f.reinterpret(in, ToMIRType(to));
+    return true;
+}
+
+static bool
 EmitSimdOp(FunctionCompiler& f, ValType type, SimdOperation op, SimdSign sign, MDefinition** def)
 {
     switch (op) {
       case SimdOperation::Constructor:
         return EmitSimdCtor(f, type, def);
       case SimdOperation::Fn_extractLane:
         return EmitExtractLane(f, type, sign, def);
       case SimdOperation::Fn_replaceLane:
@@ -2851,16 +2870,18 @@ EmitExpr(FunctionCompiler& f, MDefinitio
       case Expr::F32Ge:
       case Expr::F64Eq:
       case Expr::F64Ne:
       case Expr::F64Lt:
       case Expr::F64Le:
       case Expr::F64Gt:
       case Expr::F64Ge:
         return EmitComparison(f, op, def);
+      case Expr::I32ReinterpretF32:
+        return EmitReinterpret(f, ValType::I32, def);
 
       // I64
       case Expr::I64Const:
         return EmitLiteral(f, ValType::I64, def);
       case Expr::I64ExtendSI32:
       case Expr::I64ExtendUI32:
         return EmitExtendI32(f, IsUnsigned(op == Expr::I64ExtendUI32), def);
       case Expr::I64TruncSF32:
@@ -2888,16 +2909,18 @@ EmitExpr(FunctionCompiler& f, MDefinitio
       case Expr::I64Mul:
         return EmitMultiply(f, ValType::I64, def);
       case Expr::I64DivS:
       case Expr::I64DivU:
         return EmitDivOrMod(f, ValType::I64, IsDiv(true), IsUnsigned(op == Expr::I64DivU), def);
       case Expr::I64RemS:
       case Expr::I64RemU:
         return EmitDivOrMod(f, ValType::I64, IsDiv(false), IsUnsigned(op == Expr::I64RemU), def);
+      case Expr::I64ReinterpretF64:
+        return EmitReinterpret(f, ValType::I64, def);
 
       // F32
       case Expr::F32Const:
         return EmitLiteral(f, ValType::F32, def);
       case Expr::F32Add:
         return EmitAddOrSub(f, ValType::F32, IsAdd(true), def);
       case Expr::F32Sub:
         return EmitAddOrSub(f, ValType::F32, IsAdd(false), def);
@@ -2923,22 +2946,25 @@ EmitExpr(FunctionCompiler& f, MDefinitio
       case Expr::F32ConvertSI32:
         return EmitUnary<MToFloat32>(f, def);
       case Expr::F32ConvertUI32:
         return EmitUnary<MAsmJSUnsignedToFloat32>(f, def);
       case Expr::F32ConvertSI64:
       case Expr::F32ConvertUI64:
         return EmitConvertI64ToFloatingPoint(f, ValType::F32,
                                              IsUnsigned(op == Expr::F32ConvertUI64), def);
+
       case Expr::F32Load:
         return EmitLoad(f, Scalar::Float32, def);
       case Expr::F32Store:
         return EmitStore(f, Scalar::Float32, def);
       case Expr::F32StoreF64:
         return EmitStoreWithCoercion(f, Scalar::Float32, Scalar::Float64, def);
+      case Expr::F32ReinterpretI32:
+        return EmitReinterpret(f, ValType::F32, def);
 
       // F64
       case Expr::F64Const:
         return EmitLiteral(f, ValType::F64, def);
       case Expr::F64Add:
         return EmitAddOrSub(f, ValType::F64, IsAdd(true), def);
       case Expr::F64Sub:
         return EmitAddOrSub(f, ValType::F64, IsAdd(false), def);
@@ -2982,16 +3008,18 @@ EmitExpr(FunctionCompiler& f, MDefinitio
         return EmitConvertI64ToFloatingPoint(f, ValType::F64,
                                              IsUnsigned(op == Expr::F64ConvertUI64), def);
       case Expr::F64Load:
         return EmitLoad(f, Scalar::Float64, def);
       case Expr::F64Store:
         return EmitStore(f, Scalar::Float64, def);
       case Expr::F64StoreF32:
         return EmitStoreWithCoercion(f, Scalar::Float64, Scalar::Float32, def);
+      case Expr::F64ReinterpretI64:
+        return EmitReinterpret(f, ValType::F64, def);
 
       // SIMD
 #define CASE(TYPE, OP, SIGN)                                                    \
       case Expr::TYPE##OP:                                                      \
         return EmitSimdOp(f, ValType::TYPE, SimdOperation::Fn_##OP, SIGN, def);
 #define I32CASE(OP) CASE(I32x4, OP, SimdSign::Signed)
 #define F32CASE(OP) CASE(F32x4, OP, SimdSign::NotApplicable)
 #define B32CASE(OP) CASE(B32x4, OP, SimdSign::NotApplicable)
@@ -3050,20 +3078,16 @@ EmitExpr(FunctionCompiler& f, MDefinitio
 
       // Future opcodes
       case Expr::F32CopySign:
       case Expr::F32Trunc:
       case Expr::F32Nearest:
       case Expr::F64CopySign:
       case Expr::F64Nearest:
       case Expr::F64Trunc:
-      case Expr::I64ReinterpretF64:
-      case Expr::F64ReinterpretI64:
-      case Expr::I32ReinterpretF32:
-      case Expr::F32ReinterpretI32:
       case Expr::I64Load8S:
       case Expr::I64Load16S:
       case Expr::I64Load32S:
       case Expr::I64Load8U:
       case Expr::I64Load16U:
       case Expr::I64Load32U:
       case Expr::I64Load:
       case Expr::I64Store8:
--- a/js/src/asmjs/WasmTextToBinary.cpp
+++ b/js/src/asmjs/WasmTextToBinary.cpp
@@ -1620,16 +1620,21 @@ WasmTokenStream::next()
                 if (consume(MOZ_UTF16("ne")))
                     return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Ne, begin, cur_);
                 break;
               case 'p':
                 if (consume(MOZ_UTF16("promote/f32")))
                     return WasmToken(WasmToken::ConversionOpcode, Expr::F64PromoteF32,
                                      begin, cur_);
                 break;
+              case 'r':
+                if (consume(MOZ_UTF16("reinterpret/i64")))
+                    return WasmToken(WasmToken::UnaryOpcode, Expr::F64ReinterpretI64,
+                                     begin, cur_);
+                break;
               case 's':
                 if (consume(MOZ_UTF16("sqrt")))
                     return WasmToken(WasmToken::UnaryOpcode, Expr::F64Sqrt, begin, cur_);
                 if (consume(MOZ_UTF16("sub")))
                     return WasmToken(WasmToken::BinaryOpcode, Expr::F64Sub, begin, cur_);
                 if (consume(MOZ_UTF16("store")))
                     return WasmToken(WasmToken::Store, Expr::F64Store, begin, cur_);
                 break;
--- a/js/src/jit-test/tests/wasm/basic-conversion.js
+++ b/js/src/jit-test/tests/wasm/basic-conversion.js
@@ -173,35 +173,35 @@ if (getBuildConfiguration().x64) {
     testConversion('i64', 'trunc_s', 'f32', "infinity", "0x8000000000000000");
     testConversion('i64', 'trunc_s', 'f32', "-infinity", "0x8000000000000000");
 
     testConversion('i64', 'trunc_u', 'f32', 18446744073709551616.0, "0x8000000000000000");
     testConversion('i64', 'trunc_u', 'f32', -1, "0x8000000000000000");
     testConversion('i64', 'trunc_u', 'f32', "nan", "0x8000000000000000");
     testConversion('i64', 'trunc_u', 'f32', "infinity", "0x8000000000000000");
     testConversion('i64', 'trunc_u', 'f32', "-infinity", "0x8000000000000000");
+
+    testConversion('i64', 'reinterpret', 'f64', 40.09999999999968, 4630840390592548000);
+    testConversion('f64', 'reinterpret', 'i64', 4630840390592548000, 40.09999999999968);
 } else {
     // Sleeper test: once i64 works on more platforms, remove this if-else.
     try {
         testConversion('i32', 'wrap', 'i64', 4294967336, 40);
         assertEq(0, 1);
     } catch(e) {
         assertEq(e.toString().indexOf("NYI on this platform") >= 0, true);
     }
 }
 
 testConversion('i32', 'trunc_s', 'f32', 40.1, 40);
 testConversion('i32', 'trunc_u', 'f32', 40.1, 40);
 testConversion('i32', 'trunc_s', 'f64', 40.1, 40);
 testConversion('i32', 'trunc_u', 'f64', 40.1, 40);
-//testConversion('i32', 'reinterpret', 'f32', 40.1, 1109419622); // TODO: NYI
-
-//testConversion('i64', 'reinterpret', 'f64', 40.1, 1109419622); // TODO: NYI
+testConversion('i32', 'reinterpret', 'f32', 40.1, 1109419622);
 
 testConversion('f32', 'convert_s', 'i32', 40, 40);
 testConversion('f32', 'convert_u', 'i32', 40, 40);
 testConversion('f32', 'demote', 'f64', 40.1, 40.099998474121094);
-//testConversion('f32', 'reinterpret', 'i32', 40, 5.605193857299268e-44); // TODO: NYI
+testConversion('f32', 'reinterpret', 'i32', 40, 5.605193857299268e-44);
 
 testConversion('f64', 'convert_s', 'i32', 40, 40);
 testConversion('f64', 'convert_u', 'i32', 40, 40);
 testConversion('f64', 'promote', 'f32', 40.1, 40.099998474121094);
-//testConversion('f64', 'reinterpret', 'i64', 40.1, 4630840390592548045); // TODO: NYI
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2468,16 +2468,27 @@ LIRGenerator::visitAsmJSInterruptCheck(M
 
 void
 LIRGenerator::visitAsmThrowUnreachable(MAsmThrowUnreachable* ins)
 {
     add(new(alloc()) LAsmThrowUnreachable, ins);
 }
 
 void
+LIRGenerator::visitAsmReinterpret(MAsmReinterpret* ins)
+{
+    if (ins->type() == MIRType_Int64)
+        defineInt64(new(alloc()) LAsmReinterpretToI64(useRegisterAtStart(ins->input())), ins);
+    else if (ins->input()->type() == MIRType_Int64)
+        define(new(alloc()) LAsmReinterpretFromI64(useInt64RegisterAtStart(ins->input())), ins);
+    else
+        define(new(alloc()) LAsmReinterpret(useRegisterAtStart(ins->input())), ins);
+}
+
+void
 LIRGenerator::visitStoreSlot(MStoreSlot* ins)
 {
     LInstruction* lir;
 
     switch (ins->value()->type()) {
       case MIRType_Value:
         lir = new(alloc()) LStoreSlotV(useRegister(ins->slots()), useBox(ins->value()));
         add(lir, ins);
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -179,16 +179,17 @@ class LIRGenerator : public LIRGenerator
     void visitMaybeToDoubleElement(MMaybeToDoubleElement* ins);
     void visitMaybeCopyElementsForWrite(MMaybeCopyElementsForWrite* ins);
     void visitLoadSlot(MLoadSlot* ins);
     void visitLoadFixedSlotAndUnbox(MLoadFixedSlotAndUnbox* ins);
     void visitFunctionEnvironment(MFunctionEnvironment* ins);
     void visitInterruptCheck(MInterruptCheck* ins);
     void visitAsmJSInterruptCheck(MAsmJSInterruptCheck* ins);
     void visitAsmThrowUnreachable(MAsmThrowUnreachable* ins);
+    void visitAsmReinterpret(MAsmReinterpret* ins);
     void visitStoreSlot(MStoreSlot* ins);
     void visitFilterTypeSet(MFilterTypeSet* ins);
     void visitTypeBarrier(MTypeBarrier* ins);
     void visitMonitorTypes(MMonitorTypes* ins);
     void visitPostWriteBarrier(MPostWriteBarrier* ins);
     void visitPostWriteElementBarrier(MPostWriteElementBarrier* ins);
     void visitArrayLength(MArrayLength* ins);
     void visitSetArrayLength(MSetArrayLength* ins);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -14545,16 +14545,52 @@ class MAsmSelect
 
     bool congruentTo(const MDefinition* ins) const override {
         return congruentIfOperandsEqual(ins);
     }
 
     ALLOW_CLONE(MAsmSelect)
 };
 
+class MAsmReinterpret
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
+{
+    MAsmReinterpret(MDefinition* val, MIRType toType)
+      : MUnaryInstruction(val)
+    {
+        switch (val->type()) {
+          case MIRType_Int32:   MOZ_ASSERT(toType == MIRType_Float32); break;
+          case MIRType_Float32: MOZ_ASSERT(toType == MIRType_Int32);   break;
+          case MIRType_Double:  MOZ_ASSERT(toType == MIRType_Int64);   break;
+          case MIRType_Int64:   MOZ_ASSERT(toType == MIRType_Double);  break;
+          default:              MOZ_CRASH("unexpected reinterpret conversion");
+        }
+        setMovable();
+        setResultType(toType);
+    }
+
+  public:
+    INSTRUCTION_HEADER(AsmReinterpret)
+
+    static MAsmReinterpret* New(TempAllocator& alloc, MDefinition* val, MIRType toType)
+    {
+        return new(alloc) MAsmReinterpret(val, toType);
+    }
+
+    AliasSet getAliasSet() const override {
+        return AliasSet::None();
+    }
+    bool congruentTo(const MDefinition* ins) const override {
+        return congruentIfOperandsEqual(ins);
+    }
+
+    ALLOW_CLONE(MAsmReinterpret)
+};
+
 class MUnknownValue : public MNullaryInstruction
 {
   protected:
     MUnknownValue() {
         setResultType(MIRType_Value);
     }
 
   public:
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -272,16 +272,17 @@ namespace jit {
     _(AsmJSLoadFuncPtr)                                                     \
     _(AsmJSLoadFFIFunc)                                                     \
     _(AsmJSReturn)                                                          \
     _(AsmJSParameter)                                                       \
     _(AsmJSVoidReturn)                                                      \
     _(AsmJSPassStackArg)                                                    \
     _(AsmJSCall)                                                            \
     _(AsmSelect)                                                            \
+    _(AsmReinterpret)                                                       \
     _(NewDerivedTypedObject)                                                \
     _(RecompileCheck)                                                       \
     _(MemoryBarrier)                                                        \
     _(AsmJSCompareExchangeHeap)                                             \
     _(AsmJSAtomicExchangeHeap)                                              \
     _(AsmJSAtomicBinopHeap)                                                 \
     _(UnknownValue)                                                         \
     _(LexicalCheck)                                                         \
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -2135,16 +2135,42 @@ CodeGeneratorARM::visitAsmSelect(LAsmSel
         masm.moveDouble(falseExpr, out, Assembler::Zero);
     else if (mirType == MIRType_Float32)
         masm.moveFloat32(falseExpr, out, Assembler::Zero);
     else
         MOZ_CRASH("unhandled type in visitAsmSelect!");
 }
 
 void
+CodeGeneratorARM::visitAsmReinterpret(LAsmReinterpret* lir)
+{
+    MOZ_ASSERT(gen->compilingAsmJS());
+    MAsmReinterpret* ins = lir->mir();
+
+    MIRType to = ins->type();
+    DebugOnly<MIRType> from = ins->input()->type();
+
+    switch (to) {
+      case MIRType_Int32:
+        MOZ_ASSERT(from == MIRType_Float32);
+        masm.ma_vxfer(ToFloatRegister(lir->input()), ToRegister(lir->output()));
+        break;
+      case MIRType_Float32:
+        MOZ_ASSERT(from == MIRType_Int32);
+        masm.ma_vxfer(ToRegister(lir->input()), ToFloatRegister(lir->output()));
+        break;
+      case MIRType_Double:
+      case MIRType_Int64:
+        MOZ_CRASH("not handled by this LIR opcode");
+      default:
+        MOZ_CRASH("unexpected AsmReinterpret");
+    }
+}
+
+void
 CodeGeneratorARM::visitAsmJSCall(LAsmJSCall* ins)
 {
     MAsmJSCall* mir = ins->mir();
 
     if (UseHardFpABI() || mir->callee().which() != MAsmJSCall::Callee::Builtin) {
         emitAsmJSCall(ins);
         return;
     }
--- a/js/src/jit/arm/CodeGenerator-arm.h
+++ b/js/src/jit/arm/CodeGenerator-arm.h
@@ -196,16 +196,17 @@ class CodeGeneratorARM : public CodeGene
     void visitNegF(LNegF* lir);
     void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins);
     void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
     void visitAtomicTypedArrayElementBinop(LAtomicTypedArrayElementBinop* lir);
     void visitAtomicTypedArrayElementBinopForEffect(LAtomicTypedArrayElementBinopForEffect* lir);
     void visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayElement* lir);
     void visitAtomicExchangeTypedArrayElement(LAtomicExchangeTypedArrayElement* lir);
     void visitAsmSelect(LAsmSelect* ins);
+    void visitAsmReinterpret(LAsmReinterpret* ins);
     void visitAsmJSCall(LAsmJSCall* ins);
     void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins);
     void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins);
     void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins);
     void visitAsmJSCompareExchangeCallout(LAsmJSCompareExchangeCallout* ins);
     void visitAsmJSAtomicExchangeHeap(LAsmJSAtomicExchangeHeap* ins);
     void visitAsmJSAtomicExchangeCallout(LAsmJSAtomicExchangeCallout* ins);
     void visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins);
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -1251,16 +1251,57 @@ class LAsmThrowUnreachable : public LIns
 {
   public:
     LIR_HEADER(AsmThrowUnreachable);
 
     LAsmThrowUnreachable()
     { }
 };
 
+template<size_t Defs, size_t Ops>
+class LAsmReinterpretBase : public LInstructionHelper<Defs, Ops, 0>
+{
+    typedef LInstructionHelper<Defs, Ops, 0> Base;
+
+  public:
+    const LAllocation* input() {
+        return Base::getOperand(0);
+    }
+    MAsmReinterpret* mir() const {
+        return Base::mir_->toAsmReinterpret();
+    }
+};
+
+class LAsmReinterpret : public LAsmReinterpretBase<1, 1>
+{
+  public:
+    LIR_HEADER(AsmReinterpret);
+    explicit LAsmReinterpret(const LAllocation& input) {
+        setOperand(0, input);
+    }
+};
+
+class LAsmReinterpretFromI64 : public LAsmReinterpretBase<1, INT64_PIECES>
+{
+  public:
+    LIR_HEADER(AsmReinterpretFromI64);
+    explicit LAsmReinterpretFromI64(const LInt64Allocation& input) {
+        setInt64Operand(0, input);
+    }
+};
+
+class LAsmReinterpretToI64 : public LAsmReinterpretBase<INT64_PIECES, 1>
+{
+  public:
+    LIR_HEADER(AsmReinterpretToI64);
+    explicit LAsmReinterpretToI64(const LAllocation& input) {
+        setOperand(0, input);
+    }
+};
+
 class LInterruptCheck : public LInstructionHelper<0, 0, 0>
 {
     Label* oolEntry_;
 
     // Whether this is an implicit interrupt check. Implicit interrupt checks
     // use a patchable backedge and signal handlers instead of an explicit
     // rt->interrupt check.
     bool implicit_;
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -341,16 +341,19 @@
     _(In)                           \
     _(InArray)                      \
     _(InstanceOfO)                  \
     _(InstanceOfV)                  \
     _(CallInstanceOf)               \
     _(InterruptCheck)               \
     _(AsmJSInterruptCheck)          \
     _(AsmThrowUnreachable)          \
+    _(AsmReinterpret)               \
+    _(AsmReinterpretToI64)          \
+    _(AsmReinterpretFromI64)        \
     _(GetDOMProperty)               \
     _(GetDOMMemberV)                \
     _(GetDOMMemberT)                \
     _(SetDOMProperty)               \
     _(CallDOMNative)                \
     _(IsCallable)                   \
     _(IsObject)                     \
     _(IsObjectAndBranch)            \
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -488,16 +488,32 @@ CodeGeneratorX64::visitAsmSelectI64(LAsm
     Register out = ToRegister(lir->output());
     MOZ_ASSERT(ToRegister(lir->trueExpr()) == out, "true expr is reused for input");
 
     masm.test32(cond, cond);
     masm.cmovzq(falseExpr, out);
 }
 
 void
+CodeGeneratorX64::visitAsmReinterpretFromI64(LAsmReinterpretFromI64* lir)
+{
+    MOZ_ASSERT(lir->mir()->type() == MIRType_Double);
+    MOZ_ASSERT(lir->mir()->input()->type() == MIRType_Int64);
+    masm.vmovq(ToRegister(lir->input()), ToFloatRegister(lir->output()));
+}
+
+void
+CodeGeneratorX64::visitAsmReinterpretToI64(LAsmReinterpretToI64* lir)
+{
+    MOZ_ASSERT(lir->mir()->type() == MIRType_Int64);
+    MOZ_ASSERT(lir->mir()->input()->type() == MIRType_Double);
+    masm.vmovq(ToFloatRegister(lir->input()), ToRegister(lir->output()));
+}
+
+void
 CodeGeneratorX64::visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir)
 {
     masm.convertUInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output()));
 }
 
 void
 CodeGeneratorX64::visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32* lir)
 {
--- a/js/src/jit/x64/CodeGenerator-x64.h
+++ b/js/src/jit/x64/CodeGenerator-x64.h
@@ -68,16 +68,18 @@ class CodeGeneratorX64 : public CodeGene
     void visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins);
     void visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEffect* ins);
     void visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar* ins);
     void visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins);
     void visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins);
     void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins);
     void visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir);
     void visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32* lir);
+    void visitAsmReinterpretFromI64(LAsmReinterpretFromI64* lir);
+    void visitAsmReinterpretToI64(LAsmReinterpretToI64* lir);
 };
 
 typedef CodeGeneratorX64 CodeGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x64_CodeGenerator_x64_h */
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
@@ -19,16 +19,17 @@
 
 #include "jit/MacroAssembler-inl.h"
 #include "jit/shared/CodeGenerator-shared-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::Abs;
+using mozilla::DebugOnly;
 using mozilla::FloatingPoint;
 using mozilla::FloorLog2;
 using mozilla::NegativeInfinity;
 using mozilla::SpecificNaN;
 
 using JS::GenericNaN;
 
 namespace js {
@@ -337,16 +338,42 @@ CodeGeneratorX86Shared::visitAsmSelect(L
         MOZ_CRASH("unhandled type in visitAsmSelect!");
     }
 
     masm.bind(&done);
     return;
 }
 
 void
+CodeGeneratorX86Shared::visitAsmReinterpret(LAsmReinterpret* lir)
+{
+    MOZ_ASSERT(gen->compilingAsmJS());
+    MAsmReinterpret* ins = lir->mir();
+
+    MIRType to = ins->type();
+    DebugOnly<MIRType> from = ins->input()->type();
+
+    switch (to) {
+      case MIRType_Int32:
+        MOZ_ASSERT(from == MIRType_Float32);
+        masm.vmovd(ToFloatRegister(lir->input()), ToRegister(lir->output()));
+        break;
+      case MIRType_Float32:
+        MOZ_ASSERT(from == MIRType_Int32);
+        masm.vmovd(ToRegister(lir->input()), ToFloatRegister(lir->output()));
+        break;
+      case MIRType_Double:
+      case MIRType_Int64:
+        MOZ_CRASH("not handled by this LIR opcode");
+      default:
+        MOZ_CRASH("unexpected AsmReinterpret");
+    }
+}
+
+void
 CodeGeneratorX86Shared::visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds* ool)
 {
     switch (ool->viewType()) {
       case Scalar::Float32x4:
       case Scalar::Int32x4:
       case Scalar::MaxTypedArrayViewType:
         MOZ_CRASH("unexpected array type");
       case Scalar::Float32:
@@ -927,17 +954,17 @@ CodeGeneratorX86Shared::visitSubI(LSubI*
 }
 
 void
 CodeGeneratorX86Shared::visitOutOfLineUndoALUOperation(OutOfLineUndoALUOperation* ool)
 {
     LInstruction* ins = ool->ins();
     Register reg = ToRegister(ins->getDef(0));
 
-    mozilla::DebugOnly<LAllocation*> lhs = ins->getOperand(0);
+    DebugOnly<LAllocation*> lhs = ins->getOperand(0);
     LAllocation* rhs = ins->getOperand(1);
 
     MOZ_ASSERT(reg == ToRegister(lhs));
     MOZ_ASSERT_IF(rhs->isGeneralReg(), reg != ToRegister(rhs));
 
     // Undo the effect of the ALU operation, which was performed on the output
     // register and overflowed. Writing to the output register clobbered an
     // input reg, and the original value of the input needs to be recovered
@@ -1211,17 +1238,17 @@ CodeGeneratorX86Shared::visitMulNegative
     masm.mov(ImmWord(0), result);
     masm.jmp(ool->rejoin());
 }
 
 void
 CodeGeneratorX86Shared::visitDivPowTwoI(LDivPowTwoI* ins)
 {
     Register lhs = ToRegister(ins->numerator());
-    mozilla::DebugOnly<Register> output = ToRegister(ins->output());
+    DebugOnly<Register> output = ToRegister(ins->output());
 
     int32_t shift = ins->shift();
     bool negativeDivisor = ins->negativeDivisor();
     MDiv* mir = ins->mir();
 
     // We use defineReuseInput so these should always be the same, which is
     // convenient since all of our instructions here are two-address.
     MOZ_ASSERT(lhs == output);
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
@@ -261,16 +261,17 @@ class CodeGeneratorX86Shared : public Co
     virtual void visitGuardShape(LGuardShape* guard);
     virtual void visitGuardObjectGroup(LGuardObjectGroup* guard);
     virtual void visitGuardClass(LGuardClass* guard);
     virtual void visitEffectiveAddress(LEffectiveAddress* ins);
     virtual void visitUDivOrMod(LUDivOrMod* ins);
     virtual void visitUDivOrModConstant(LUDivOrModConstant *ins);
     virtual void visitAsmJSPassStackArg(LAsmJSPassStackArg* ins);
     virtual void visitAsmSelect(LAsmSelect* ins);
+    virtual void visitAsmReinterpret(LAsmReinterpret* lir);
     virtual void visitMemoryBarrier(LMemoryBarrier* ins);
     virtual void visitAtomicTypedArrayElementBinop(LAtomicTypedArrayElementBinop* lir);
     virtual void visitAtomicTypedArrayElementBinopForEffect(LAtomicTypedArrayElementBinopForEffect* lir);
     virtual void visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayElement* lir);
     virtual void visitAtomicExchangeTypedArrayElement(LAtomicExchangeTypedArrayElement* lir);
 
     void visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds* ool);
     void visitOffsetBoundsCheck(OffsetBoundsCheck* oolCheck);