Bug 1252432 part 1 - Implement wasm i32.wrap. r=luke
authorJan de Mooij <jdemooij@mozilla.com>
Fri, 04 Mar 2016 13:57:44 +0100
changeset 323121 fa16519da919a07c029abac60d328564ee8e73d3
parent 323120 92ce3f765f055e650df3cf21412ce92f6605ea8e
child 323122 3231cc3fd19e28e9718716a8da85041c262a3be8
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1252432
milestone47.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 1252432 part 1 - Implement wasm i32.wrap. r=luke
js/src/asmjs/Wasm.cpp
js/src/asmjs/WasmIonCompile.cpp
js/src/jit-test/tests/wasm/basic-conversion.js
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/shared/LIR-shared.h
js/src/jit/shared/LOpcodes-shared.h
js/src/jit/shared/Lowering-shared-inl.h
js/src/jit/shared/Lowering-shared.h
js/src/jit/x64/CodeGenerator-x64.cpp
js/src/jit/x64/CodeGenerator-x64.h
--- a/js/src/asmjs/Wasm.cpp
+++ b/js/src/asmjs/Wasm.cpp
@@ -591,18 +591,17 @@ DecodeExpr(FunctionDecoder& f, ExprType 
       case Expr::F64Eq:
       case Expr::F64Ne:
       case Expr::F64Lt:
       case Expr::F64Le:
       case Expr::F64Gt:
       case Expr::F64Ge:
         return DecodeComparisonOperator(f, expected, ExprType::F64);
       case Expr::I32WrapI64:
-        return f.fail("NYI: i64") &&
-               DecodeConversionOperator(f, expected, ExprType::I32, ExprType::I64);
+        return DecodeConversionOperator(f, expected, ExprType::I32, ExprType::I64);
       case Expr::I32TruncSF32:
       case Expr::I32TruncUF32:
         return DecodeConversionOperator(f, expected, ExprType::I32, ExprType::F32);
       case Expr::I32ReinterpretF32:
         return f.fail("NYI: reinterpret");
       case Expr::I32TruncSF64:
       case Expr::I32TruncUF64:
         return DecodeConversionOperator(f, expected, ExprType::I32, ExprType::F64);
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -2637,16 +2637,18 @@ EmitExpr(FunctionCompiler& f, ExprType t
       case Expr::I32Not:
         return EmitUnary<MNot>(f, ExprType::I32, def);
       case Expr::I32TruncSF32:
       case Expr::I32TruncUF32:
         return EmitUnary<MTruncateToInt32>(f, ExprType::F32, def);
       case Expr::I32TruncSF64:
       case Expr::I32TruncUF64:
         return EmitUnary<MTruncateToInt32>(f, ExprType::F64, def);
+      case Expr::I32WrapI64:
+        return EmitUnary<MWrapInt64ToInt32>(f, ExprType::I64, def);
       case Expr::I32Clz:
         return EmitUnary<MClz>(f, ExprType::I32, def);
       case Expr::I32Ctz:
         return EmitUnary<MCtz>(f, ExprType::I32, def);
       case Expr::I32Popcnt:
         return EmitUnary<MPopcnt>(f, ExprType::I32, def);
       case Expr::I32Abs:
         return EmitUnaryMir<MAbs>(f, ExprType::I32, def);
@@ -2882,17 +2884,16 @@ EmitExpr(FunctionCompiler& f, ExprType t
       // Future opcodes
       case Expr::Select:
       case Expr::F32CopySign:
       case Expr::F32Trunc:
       case Expr::F32Nearest:
       case Expr::F64CopySign:
       case Expr::F64Nearest:
       case Expr::F64Trunc:
-      case Expr::I32WrapI64:
       case Expr::I64ExtendSI32:
       case Expr::I64ExtendUI32:
       case Expr::I64TruncSF32:
       case Expr::I64TruncSF64:
       case Expr::I64TruncUF32:
       case Expr::I64TruncUF64:
       case Expr::F32ConvertSI64:
       case Expr::F32ConvertUI64:
--- a/js/src/jit-test/tests/wasm/basic-conversion.js
+++ b/js/src/jit-test/tests/wasm/basic-conversion.js
@@ -1,32 +1,61 @@
 load(libdir + "wasm.js");
 
 if (!wasmIsSupported())
     quit();
 
 function testConversion(resultType, opcode, paramType, op, expect) {
-  assertEq(wasmEvalText('(module (func (param ' + paramType + ') (result ' + resultType + ') (' + resultType + '.' + opcode + '/' + paramType + ' (get_local 0))) (export "" 0))')(op), expect);
+  if (paramType === 'i64') {
+    // i64 cannot be imported, so we use a wrapper function.
+    assertEq(wasmEvalText(`(module
+                            (func (param i64) (result ${resultType}) (${resultType}.${opcode}/i64 (get_local 0)))
+                            (func (result ${resultType}) (call 0 (i64.const ${op})))
+                            (export "" 1))`)(), expect);
+    // The same, but now the input is a constant.
+    assertEq(wasmEvalText(`(module
+                            (func (result ${resultType}) (${resultType}.${opcode}/i64 (i64.const ${op})))
+                            (export "" 0))`)(), expect);
+  } else {
+    assertEq(wasmEvalText('(module (func (param ' + paramType + ') (result ' + resultType + ') (' + resultType + '.' + opcode + '/' + paramType + ' (get_local 0))) (export "" 0))')(op), expect);
+  }
 
   // TODO: i64 NYI
   for (var bad of ['i32', 'f32', 'f64']) {
     if (bad != resultType)
       assertErrorMessage(() => wasmEvalText('(module (func (param ' + paramType + ') (result ' + bad + ') (' + resultType + '.' + opcode + '/' + paramType + ' (get_local 0))))'),
                          TypeError,
                          mismatchError(resultType, bad)
                         );
     if (bad != paramType)
       assertErrorMessage(() => wasmEvalText('(module (func (param ' + bad + ') (result ' + resultType + ') (' + resultType + '.' + opcode + '/' + paramType + ' (get_local 0))))'),
                          TypeError,
                          mismatchError(bad, paramType)
                         );
   }
 }
 
-//testConversion('i32', 'wrap', 'i64', 4294967336, 40); // TODO: NYI
+if (getBuildConfiguration().x64) {
+    testConversion('i32', 'wrap', 'i64', 4294967336, 40);
+    testConversion('i32', 'wrap', 'i64', -10, -10);
+    testConversion('i32', 'wrap', 'i64', "0xffffffff7fffffff", 0x7fffffff);
+    testConversion('i32', 'wrap', 'i64', "0xffffffff00000000", 0);
+    testConversion('i32', 'wrap', 'i64', "0xfffffffeffffffff", -1);
+    testConversion('i32', 'wrap', 'i64', "0x1234567801abcdef", 0x01abcdef);
+    testConversion('i32', 'wrap', 'i64', "0x8000000000000002", 2);
+} 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', 'extend_s', 'i32', -2, -2); / TODO: NYI
 //testConversion('i64', 'extend_u', 'i32', -2, 0xfffffffffffffffc); / TODO: NYI
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2090,16 +2090,22 @@ LIRGenerator::visitTruncateToInt32(MTrun
       default:
         // Objects might be effectful. Symbols throw.
         // Strings are complicated - we don't handle them yet.
         MOZ_CRASH("unexpected type");
     }
 }
 
 void
+LIRGenerator::visitWrapInt64ToInt32(MWrapInt64ToInt32* ins)
+{
+    define(new(alloc()) LWrapInt64ToInt32(useInt64AtStart(ins->input())), ins);
+}
+
+void
 LIRGenerator::visitToString(MToString* ins)
 {
     MDefinition* opd = ins->input();
 
     switch (opd->type()) {
       case MIRType_Null: {
         const JSAtomState& names = GetJitContext()->runtime->names();
         LPointer* lir = new(alloc()) LPointer(names.null);
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -153,16 +153,17 @@ class LIRGenerator : public LIRGenerator
     void visitOsrValue(MOsrValue* value);
     void visitOsrScopeChain(MOsrScopeChain* object);
     void visitOsrReturnValue(MOsrReturnValue* value);
     void visitOsrArgumentsObject(MOsrArgumentsObject* object);
     void visitToDouble(MToDouble* convert);
     void visitToFloat32(MToFloat32* convert);
     void visitToInt32(MToInt32* convert);
     void visitTruncateToInt32(MTruncateToInt32* truncate);
+    void visitWrapInt64ToInt32(MWrapInt64ToInt32* ins);
     void visitToString(MToString* convert);
     void visitToObjectOrNull(MToObjectOrNull* convert);
     void visitRegExp(MRegExp* ins);
     void visitRegExpMatcher(MRegExpMatcher* ins);
     void visitRegExpTester(MRegExpTester* ins);
     void visitRegExpReplace(MRegExpReplace* ins);
     void visitStringReplace(MStringReplace* ins);
     void visitBinarySharedStub(MBinarySharedStub* ins);
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -3721,16 +3721,28 @@ MTruncateToInt32::foldsTo(TempAllocator&
         int32_t ret = ToInt32(input->toConstant()->toDouble());
         return MConstant::New(alloc, Int32Value(ret));
     }
 
     return this;
 }
 
 MDefinition*
+MWrapInt64ToInt32::foldsTo(TempAllocator& alloc)
+{
+    MDefinition* input = this->input();
+    if (input->isConstant()) {
+        int64_t c = input->toConstant()->toInt64();
+        return MConstant::New(alloc, Int32Value(int32_t(c)));
+    }
+
+    return this;
+}
+
+MDefinition*
 MToDouble::foldsTo(TempAllocator& alloc)
 {
     MDefinition* input = getOperand(0);
     if (input->isBox())
         input = input->getOperand(0);
 
     if (input->type() == MIRType_Double)
         return input;
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -5242,16 +5242,42 @@ class MAsmJSUnsignedToFloat32
     }
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
 
     bool canProduceFloat32() const override { return true; }
 };
 
+class MWrapInt64ToInt32
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
+{
+    explicit MWrapInt64ToInt32(MDefinition* def)
+      : MUnaryInstruction(def)
+    {
+        setResultType(MIRType_Int32);
+        setMovable();
+    }
+
+  public:
+    INSTRUCTION_HEADER(WrapInt64ToInt32)
+    static MWrapInt64ToInt32* NewAsmJS(TempAllocator& alloc, MDefinition* def) {
+        return new(alloc) MWrapInt64ToInt32(def);
+    }
+
+    MDefinition* foldsTo(TempAllocator& alloc) override;
+    bool congruentTo(const MDefinition* ins) const override {
+        return congruentIfOperandsEqual(ins);
+    }
+    AliasSet getAliasSet() const override {
+        return AliasSet::None();
+    }
+};
+
 // Converts a primitive (either typed or untyped) to an int32. If the input is
 // not primitive at runtime, a bailout occurs. If the input cannot be converted
 // to an int32 without loss (i.e. "5.5" or undefined) then a bailout occurs.
 class MToInt32
   : public MUnaryInstruction,
     public ToInt32Policy::Data
 {
     bool canBeNegativeZero_;
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -113,16 +113,17 @@ namespace jit {
     _(GuardObject)                                                          \
     _(GuardString)                                                          \
     _(PolyInlineGuard)                                                      \
     _(AssertRange)                                                          \
     _(ToDouble)                                                             \
     _(ToFloat32)                                                            \
     _(ToInt32)                                                              \
     _(TruncateToInt32)                                                      \
+    _(WrapInt64ToInt32)                                                     \
     _(ToString)                                                             \
     _(ToObjectOrNull)                                                       \
     _(NewArray)                                                             \
     _(NewArrayCopyOnWrite)                                                  \
     _(NewArrayDynamicLength)                                                \
     _(NewObject)                                                            \
     _(NewTypedObject)                                                       \
     _(NewDeclEnvObject)                                                     \
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -3958,16 +3958,28 @@ class LTruncateFToInt32 : public LInstru
         return getTemp(0);
     }
 
     MTruncateToInt32* mir() const {
         return mir_->toTruncateToInt32();
     }
 };
 
+class LWrapInt64ToInt32 : public LInstructionHelper<1, INT64_PIECES, 0>
+{
+  public:
+    LIR_HEADER(WrapInt64ToInt32)
+
+    static const size_t Input = 0;
+
+    explicit LWrapInt64ToInt32(const LInt64Allocation& input) {
+        setInt64Operand(Input, input);
+    }
+};
+
 // Convert a boolean value to a string.
 class LBooleanToString : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(BooleanToString)
 
     explicit LBooleanToString(const LAllocation& input) {
         setOperand(0, input);
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -184,16 +184,17 @@
     _(Int32ToFloat32)               \
     _(ValueToDouble)                \
     _(ValueToInt32)                 \
     _(ValueToFloat32)               \
     _(DoubleToInt32)                \
     _(Float32ToInt32)               \
     _(TruncateDToInt32)             \
     _(TruncateFToInt32)             \
+    _(WrapInt64ToInt32)             \
     _(BooleanToString)              \
     _(IntToString)                  \
     _(DoubleToString)               \
     _(ValueToString)                \
     _(ValueToObjectOrNull)          \
     _(Int32x4ToFloat32x4)           \
     _(Float32x4ToInt32x4)           \
     _(Float32x4ToUint32x4)          \
--- a/js/src/jit/shared/Lowering-shared-inl.h
+++ b/js/src/jit/shared/Lowering-shared-inl.h
@@ -748,16 +748,22 @@ LIRGeneratorShared::useInt64(MDefinition
 #if JS_BITS_PER_WORD == 32
     return useInt64(mir, LUse::REGISTER, useAtStart);
 #else
     return useInt64(mir, LUse::ANY, useAtStart);
 #endif
 }
 
 LInt64Allocation
+LIRGeneratorShared::useInt64AtStart(MDefinition* mir)
+{
+    return useInt64(mir, /* useAtStart = */ true);
+}
+
+LInt64Allocation
 LIRGeneratorShared::useInt64Register(MDefinition* mir, bool useAtStart)
 {
     return useInt64(mir, LUse::REGISTER, useAtStart);
 }
 
 LInt64Allocation
 LIRGeneratorShared::useInt64OrConstant(MDefinition* mir, bool useAtStart)
 {
--- a/js/src/jit/shared/Lowering-shared.h
+++ b/js/src/jit/shared/Lowering-shared.h
@@ -177,16 +177,17 @@ class LIRGeneratorShared : public MDefin
 
     // Returns a box allocation. The use is either typed, a Value, or
     // a constant (if useConstant is true).
     inline LBoxAllocation useBoxOrTypedOrConstant(MDefinition* mir, bool useConstant);
 
     // Returns an int64 allocation for an Int64-typed instruction.
     inline LInt64Allocation useInt64(MDefinition* mir, LUse::Policy policy, bool useAtStart);
     inline LInt64Allocation useInt64(MDefinition* mir, bool useAtStart = false);
+    inline LInt64Allocation useInt64AtStart(MDefinition* mir);
     inline LInt64Allocation useInt64OrConstant(MDefinition* mir, bool useAtStart = false);
     inline LInt64Allocation useInt64Register(MDefinition* mir, bool useAtStart = false);
 
     LInt64Allocation useInt64RegisterAtStart(MDefinition* mir) {
         return useInt64Register(mir, /* useAtStart = */ true);
     }
     LInt64Allocation useInt64OrConstantAtStart(MDefinition* mir) {
         return useInt64OrConstant(mir, /* useAtStart = */ true);
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -1086,8 +1086,17 @@ CodeGeneratorX64::visitTruncateFToInt32(
     FloatRegister input = ToFloatRegister(ins->input());
     Register output = ToRegister(ins->output());
 
     // On x64, branchTruncateFloat32 uses vcvttss2sq. Unlike the x86
     // implementation, this should handle most floats and we can just
     // call a stub if it fails.
     emitTruncateFloat32(input, output, ins->mir());
 }
+
+void
+CodeGeneratorX64::visitWrapInt64ToInt32(LWrapInt64ToInt32* lir)
+{
+    const LAllocation* input = lir->getOperand(0);
+    Register output = ToRegister(lir->output());
+
+    masm.movl(ToOperand(input), output);
+}
--- a/js/src/jit/x64/CodeGenerator-x64.h
+++ b/js/src/jit/x64/CodeGenerator-x64.h
@@ -48,16 +48,17 @@ class CodeGeneratorX64 : public CodeGene
     void visitShiftI64(LShiftI64* lir);
     void visitAddI64(LAddI64* lir);
     void visitSubI64(LSubI64* lir);
     void visitMulI64(LMulI64* lir);
     void visitDivOrModI64(LDivOrModI64* lir);
     void visitUDivOrMod64(LUDivOrMod64* lir);
     void visitTruncateDToInt32(LTruncateDToInt32* ins);
     void visitTruncateFToInt32(LTruncateFToInt32* ins);
+    void visitWrapInt64ToInt32(LWrapInt64ToInt32* lir);
     void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins);
     void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
     void visitAsmJSCall(LAsmJSCall* ins);
     void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins);
     void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins);
     void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins);
     void visitAsmJSAtomicExchangeHeap(LAsmJSAtomicExchangeHeap* ins);
     void visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins);