Bug 1334239 - Baldr: fix and optimize call_indirect. r=bbouvier, a=lizzard
authorLuke Wagner <luke@mozilla.com>
Thu, 02 Feb 2017 23:27:00 -0500
changeset 480353 e69d59dae7b2f34b56e7805974841953b04493bd
parent 480352 51523138a95eff972a01953746d86341ed1f402a
child 480354 b8b67afeb8f18cffa2ae678ee9a6c2f572323441
push id44524
push usermartin.thomson@gmail.com
push dateWed, 08 Feb 2017 05:10:11 +0000
reviewersbbouvier, lizzard
bugs1334239
milestone52.0
Bug 1334239 - Baldr: fix and optimize call_indirect. r=bbouvier, a=lizzard
js/src/jit-test/tests/wasm/tables.js
js/src/jit/MacroAssembler.h
js/src/jit/arm/MacroAssembler-arm-inl.h
js/src/jit/arm64/MacroAssembler-arm64-inl.h
js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h
js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h
js/src/wasm/WasmFrameIterator.cpp
js/src/wasm/WasmStubs.cpp
js/src/wasm/WasmTypes.cpp
--- a/js/src/jit-test/tests/wasm/tables.js
+++ b/js/src/jit-test/tests/wasm/tables.js
@@ -58,16 +58,22 @@ assertErrorMessage(() => call(0), Runtim
 assertEq(call(1), 0);
 assertEq(call(2), 1);
 assertErrorMessage(() => call(3), RuntimeError, /indirect call to null/);
 assertEq(call(4), 0);
 assertEq(call(5), 2);
 assertErrorMessage(() => call(6), RuntimeError, /indirect call to null/);
 assertErrorMessage(() => call(10), RuntimeError, /index out of bounds/);
 
+var imports = {a:{b:()=>42}};
+var call = wasmEvalText(`(module (table 10 anyfunc) (elem (i32.const 0) $f0 $f1 $f2) ${callee(0)} (import "a" "b" (func $f1)) (import "a" "b" (func $f2 (result i32))) ${caller})`, imports).exports.call;
+assertEq(call(0), 0);
+assertErrorMessage(() => call(1), RuntimeError, /indirect call signature mismatch/);
+assertEq(call(2), 42);
+
 var tbl = new Table({initial:3, element:"anyfunc"});
 var call = wasmEvalText(`(module (import "a" "b" (table 3 anyfunc)) (export "tbl" table) (elem (i32.const 0) $f0 $f1) ${callee(0)} ${callee(1)} ${caller})`, {a:{b:tbl}}).exports.call;
 assertEq(call(0), 0);
 assertEq(call(1), 1);
 assertEq(tbl.get(0)(), 0);
 assertEq(tbl.get(1)(), 1);
 assertErrorMessage(() => call(2), RuntimeError, /indirect call to null/);
 assertEq(tbl.get(2), null);
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -983,17 +983,18 @@ class MacroAssembler : public MacroAssem
     // with Address as lhs. On others only the NotEqual condition.
     inline void branch64(Condition cond, const Address& lhs, Imm64 val, Label* label) PER_ARCH;
 
     // Compare the value at |lhs| with the value at |rhs|.  The scratch
     // register *must not* be the base of |lhs| or |rhs|.
     inline void branch64(Condition cond, const Address& lhs, const Address& rhs, Register scratch,
                          Label* label) PER_ARCH;
 
-    inline void branchPtr(Condition cond, Register lhs, Register rhs, Label* label) PER_SHARED_ARCH;
+    template <class L>
+    inline void branchPtr(Condition cond, Register lhs, Register rhs, L label) PER_SHARED_ARCH;
     inline void branchPtr(Condition cond, Register lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH;
     inline void branchPtr(Condition cond, Register lhs, ImmPtr rhs, Label* label) PER_SHARED_ARCH;
     inline void branchPtr(Condition cond, Register lhs, ImmGCPtr rhs, Label* label) PER_SHARED_ARCH;
     inline void branchPtr(Condition cond, Register lhs, ImmWord rhs, Label* label) PER_SHARED_ARCH;
 
     template <class L>
     inline void branchPtr(Condition cond, const Address& lhs, Register rhs, L label) PER_SHARED_ARCH;
     inline void branchPtr(Condition cond, const Address& lhs, ImmPtr rhs, Label* label) PER_SHARED_ARCH;
--- a/js/src/jit/arm/MacroAssembler-arm-inl.h
+++ b/js/src/jit/arm/MacroAssembler-arm-inl.h
@@ -1340,18 +1340,19 @@ MacroAssembler::branch64(Condition cond,
         MOZ_CRASH("Condition code not supported");
         break;
     }
 
     if (fallthrough)
         bind(fail);
 }
 
+template <class L>
 void
-MacroAssembler::branchPtr(Condition cond, Register lhs, Register rhs, Label* label)
+MacroAssembler::branchPtr(Condition cond, Register lhs, Register rhs, L label)
 {
     branch32(cond, lhs, rhs, label);
 }
 
 void
 MacroAssembler::branchPtr(Condition cond, Register lhs, Imm32 rhs, Label* label)
 {
     branch32(cond, lhs, rhs, label);
--- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h
@@ -870,18 +870,20 @@ MacroAssembler::branch64(Condition cond,
     MOZ_ASSERT(cond == Assembler::NotEqual,
                "other condition codes not supported");
     MOZ_ASSERT(lhs.base != scratch);
     MOZ_ASSERT(rhs.base != scratch);
 
     loadPtr(rhs, scratch);
     branchPtr(cond, lhs, scratch, label);
 }
+
+template <class L>
 void
-MacroAssembler::branchPtr(Condition cond, Register lhs, Register rhs, Label* label)
+MacroAssembler::branchPtr(Condition cond, Register lhs, Register rhs, L label)
 {
     Cmp(ARMRegister(lhs, 64), ARMRegister(rhs, 64));
     B(label, cond);
 }
 
 void
 MacroAssembler::branchPtr(Condition cond, Register lhs, Imm32 rhs, Label* label)
 {
--- a/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h
+++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h
@@ -485,18 +485,19 @@ MacroAssembler::branch32(Condition cond,
 
 void
 MacroAssembler::branch32(Condition cond, wasm::SymbolicAddress addr, Imm32 imm, Label* label)
 {
     load32(addr, SecondScratchReg);
     ma_b(SecondScratchReg, imm, label, cond);
 }
 
+template <class L>
 void
-MacroAssembler::branchPtr(Condition cond, Register lhs, Register rhs, Label* label)
+MacroAssembler::branchPtr(Condition cond, Register lhs, Register rhs, L label)
 {
     ma_b(lhs, rhs, label, cond);
 }
 
 void
 MacroAssembler::branchPtr(Condition cond, Register lhs, Imm32 rhs, Label* label)
 {
     ma_b(lhs, rhs, label, cond);
--- a/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h
+++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h
@@ -520,18 +520,19 @@ MacroAssembler::branch32(Condition cond,
 
 void
 MacroAssembler::branch32(Condition cond, const Operand& lhs, Imm32 rhs, Label* label)
 {
     cmp32(lhs, rhs);
     j(cond, label);
 }
 
+template <class L>
 void
-MacroAssembler::branchPtr(Condition cond, Register lhs, Register rhs, Label* label)
+MacroAssembler::branchPtr(Condition cond, Register lhs, Register rhs, L label)
 {
     cmpPtr(lhs, rhs);
     j(cond, label);
 }
 
 void
 MacroAssembler::branchPtr(Condition cond, Register lhs, Imm32 rhs, Label* label)
 {
--- a/js/src/wasm/WasmFrameIterator.cpp
+++ b/js/src/wasm/WasmFrameIterator.cpp
@@ -382,17 +382,17 @@ wasm::GenerateFunctionPrologue(MacroAsse
     masm.haltingAlign(CodeAlignment);
     offsets->tableEntry = masm.currentOffset();
     TrapOffset trapOffset(0);  // ignored by masm.wasmEmitTrapOutOfLineCode
     TrapDesc trap(trapOffset, Trap::IndirectCallBadSig, masm.framePushed());
     switch (sigId.kind()) {
       case SigIdDesc::Kind::Global: {
         Register scratch = WasmTableCallScratchReg;
         masm.loadWasmGlobalPtr(sigId.globalDataOffset(), scratch);
-        masm.branch32(Assembler::Condition::NotEqual, WasmTableCallSigReg, scratch, trap);
+        masm.branchPtr(Assembler::Condition::NotEqual, WasmTableCallSigReg, scratch, trap);
         break;
       }
       case SigIdDesc::Kind::Immediate:
         masm.branch32(Assembler::Condition::NotEqual, WasmTableCallSigReg, Imm32(sigId.immediate()), trap);
         break;
       case SigIdDesc::Kind::None:
         break;
     }
--- a/js/src/wasm/WasmStubs.cpp
+++ b/js/src/wasm/WasmStubs.cpp
@@ -482,16 +482,19 @@ wasm::GenerateImportFunction(jit::MacroA
     CallSiteDesc desc(CallSiteDesc::Dynamic);
     masm.wasmCallImport(desc, CalleeDesc::import(fi.tlsDataOffset()));
 
     // Restore the TLS register and pinned regs, per wasm function ABI.
     masm.loadPtr(Address(masm.getStackPointer(), tlsStackOffset), WasmTlsReg);
     masm.loadWasmPinnedRegsFromTls();
 
     GenerateFunctionEpilogue(masm, framePushed, &offsets);
+
+    masm.wasmEmitTrapOutOfLineCode();
+
     offsets.end = masm.currentOffset();
     return offsets;
 }
 
 // Generate a stub that is called via the internal ABI derived from the
 // signature of the import and calls into an appropriate callImport C++
 // function, having boxed all the ABI arguments into a homogeneous Value array.
 ProfilingOffsets
--- a/js/src/wasm/WasmTypes.cpp
+++ b/js/src/wasm/WasmTypes.cpp
@@ -453,35 +453,68 @@ static const unsigned sTagBits = 1;
 static const unsigned sReturnBit = 1;
 static const unsigned sLengthBits = 4;
 static const unsigned sTypeBits = 2;
 static const unsigned sMaxTypes = (sTotalBits - sTagBits - sReturnBit - sLengthBits) / sTypeBits;
 
 static bool
 IsImmediateType(ValType vt)
 {
-    MOZ_ASSERT(uint32_t(vt) > 0);
-    return (uint32_t(vt) - 1) < (1 << sTypeBits);
+    switch (vt) {
+      case ValType::I32:
+      case ValType::I64:
+      case ValType::F32:
+      case ValType::F64:
+        return true;
+      case ValType::I8x16:
+      case ValType::I16x8:
+      case ValType::I32x4:
+      case ValType::F32x4:
+      case ValType::B8x16:
+      case ValType::B16x8:
+      case ValType::B32x4:
+        return false;
+    }
+    MOZ_CRASH("bad ValType");
 }
 
-static bool
-IsImmediateType(ExprType et)
+static unsigned
+EncodeImmediateType(ValType vt)
 {
-    return et == ExprType::Void || IsImmediateType(NonVoidToValType(et));
+    static_assert(3 < (1 << sTypeBits), "fits");
+    switch (vt) {
+      case ValType::I32:
+        return 0;
+      case ValType::I64:
+        return 1;
+      case ValType::F32:
+        return 2;
+      case ValType::F64:
+        return 3;
+      case ValType::I8x16:
+      case ValType::I16x8:
+      case ValType::I32x4:
+      case ValType::F32x4:
+      case ValType::B8x16:
+      case ValType::B16x8:
+      case ValType::B32x4:
+        break;
+    }
+    MOZ_CRASH("bad ValType");
 }
 
 /* static */ bool
 SigIdDesc::isGlobal(const Sig& sig)
 {
     unsigned numTypes = (sig.ret() == ExprType::Void ? 0 : 1) +
                         (sig.args().length());
     if (numTypes > sMaxTypes)
         return true;
 
-    if (!IsImmediateType(sig.ret()))
+    if (sig.ret() != ExprType::Void && !IsImmediateType(NonVoidToValType(sig.ret())))
         return true;
 
     for (ValType v : sig.args()) {
         if (!IsImmediateType(v))
             return true;
     }
 
     return false;
@@ -497,45 +530,37 @@ SigIdDesc::global(const Sig& sig, uint32
 static ImmediateType
 LengthToBits(uint32_t length)
 {
     static_assert(sMaxTypes <= ((1 << sLengthBits) - 1), "fits");
     MOZ_ASSERT(length <= sMaxTypes);
     return length;
 }
 
-static ImmediateType
-TypeToBits(ValType type)
-{
-    static_assert(3 <= ((1 << sTypeBits) - 1), "fits");
-    MOZ_ASSERT(uint32_t(type) >= 1 && uint32_t(type) <= 4);
-    return uint32_t(type) - 1;
-}
-
 /* static */ SigIdDesc
 SigIdDesc::immediate(const Sig& sig)
 {
     ImmediateType immediate = ImmediateBit;
     uint32_t shift = sTagBits;
 
     if (sig.ret() != ExprType::Void) {
         immediate |= (1 << shift);
         shift += sReturnBit;
 
-        immediate |= TypeToBits(NonVoidToValType(sig.ret())) << shift;
+        immediate |= EncodeImmediateType(NonVoidToValType(sig.ret())) << shift;
         shift += sTypeBits;
     } else {
         shift += sReturnBit;
     }
 
     immediate |= LengthToBits(sig.args().length()) << shift;
     shift += sLengthBits;
 
     for (ValType argType : sig.args()) {
-        immediate |= TypeToBits(argType) << shift;
+        immediate |= EncodeImmediateType(argType) << shift;
         shift += sTypeBits;
     }
 
     MOZ_ASSERT(shift <= sTotalBits);
     return SigIdDesc(Kind::Immediate, immediate);
 }
 
 size_t