Bug 1311433 - Wasm baseline: clean up function calling code. r=h4writer
authorLars T Hansen <lhansen@mozilla.com>
Tue, 25 Oct 2016 15:31:13 +0200
changeset 362244 37dfd653f3a5ee6bc76b6d41e32621870ca95d7e
parent 362243 a5f3e15356a89e450eaaa66fbafda36ed3b48a37
child 362245 70934a19312e01a66f2087fde2ed51cd552a523c
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-beta@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersh4writer
bugs1311433
milestone52.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 1311433 - Wasm baseline: clean up function calling code. r=h4writer
js/src/asmjs/WasmBaselineCompile.cpp
--- a/js/src/asmjs/WasmBaselineCompile.cpp
+++ b/js/src/asmjs/WasmBaselineCompile.cpp
@@ -140,18 +140,34 @@ struct BaseCompilePolicy : ExprIterPolic
 };
 
 typedef ExprIter<BaseCompilePolicy> BaseExprIter;
 
 typedef bool IsUnsigned;
 typedef bool ZeroOnOverflow;
 typedef bool IsKnownNotZero;
 typedef bool HandleNaNSpecially;
-typedef bool EscapesSandbox;
-typedef bool IsBuiltinCall;
+
+// UseABI::Wasm implies that the Tls/Heap/Global registers are nonvolatile,
+// except when InterModule::True is also set, when they are volatile.
+//
+// UseABI::System implies that the Tls/Heap/Global registers are volatile.
+// Additionally, the parameter passing mechanism may be slightly different from
+// the UseABI::Wasm convention.
+//
+// When the Tls/Heap/Global registers are not volatile, the baseline compiler
+// will restore the Tls register from its save slot before the call, since the
+// baseline compiler uses the Tls register for other things.
+//
+// When those registers are volatile, the baseline compiler will reload them
+// after the call (it will restore the Tls register from the save slot and load
+// the other two from the Tls data).
+
+enum class UseABI { Wasm, System };
+enum class InterModule { False = false, True = true };
 
 #ifdef JS_CODEGEN_ARM64
 // FIXME: This is not correct, indeed for ARM64 there is no reliable
 // StackPointer and we'll need to change the abstractions that use
 // SP-relative addressing.  There's a guard in emitFunction() below to
 // prevent this workaround from having any consequence.  This hack
 // exists only as a stopgap; there is no ARM64 JIT support yet.
 static const Register StackPointer = RealStackPointer;
@@ -161,17 +177,16 @@ static const Register StackPointer = Rea
 // The selection of EBX here steps gingerly around: the need for EDX
 // to be allocatable for multiply/divide; ECX to be allocatable for
 // shift/rotate; EAX (= ReturnReg) to be allocatable as the joinreg;
 // EBX not being one of the WasmTableCall registers; and needing a
 // temp register for load/store that has a single-byte persona.
 static const Register ScratchRegX86 = ebx;
 
 # define QUOT_REM_I64_CALLOUT
-
 #endif
 
 class BaseCompiler
 {
     // We define our own ScratchRegister abstractions, deferring to
     // the platform's when possible.
 
 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
@@ -1985,18 +2000,17 @@ class BaseCompiler
         // stack so that, when the trap exit stub executes, it is a safe
         // distance away from the end of the native stack.
         if (localSize_)
             masm.addToStackPtr(Imm32(localSize_));
         masm.jump(TrapDesc(prologueTrapOffset_, Trap::StackOverflow, /* framePushed = */ 0));
 
         masm.bind(&returnLabel_);
 
-        // The return value was set up before jumping here, but we also need to
-        // preserve the TLS register.
+        // Restore the TLS register in case it was overwritten by the function.
         loadFromFramePtr(WasmTlsReg, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
 
         GenerateFunctionEpilogue(masm, localSize_, &compileResults_.offsets());
 
 #if defined(JS_ION_PERF)
         // FIXME - profiling code missing
 
         // Note the end of the inline code and start of the OOL code.
@@ -2021,112 +2035,81 @@ class BaseCompiler
 
     //////////////////////////////////////////////////////////////////////
     //
     // Calls.
 
     struct FunctionCall
     {
         explicit FunctionCall(uint32_t lineOrBytecode)
-          : lineOrBytecode_(lineOrBytecode),
-            callSavesMachineState_(false),
-            builtinCall_(false),
-            machineStateAreaSize_(0),
-            frameAlignAdjustment_(0),
-            stackArgAreaSize_(0),
-            calleePopsArgs_(false)
+          : lineOrBytecode(lineOrBytecode),
+            reloadMachineStateAfter(false),
+            usesSystemAbi(false),
+            loadTlsBefore(false),
+            frameAlignAdjustment(0),
+            stackArgAreaSize(0)
         {}
 
-        uint32_t lineOrBytecode_;
+        uint32_t lineOrBytecode;
         ABIArgGenerator abi_;
-        bool callSavesMachineState_;
-        bool builtinCall_;
-        size_t machineStateAreaSize_;
-        size_t frameAlignAdjustment_;
-        size_t stackArgAreaSize_;
-
-        // TODO / INVESTIGATE: calleePopsArgs_ is unused on x86, x64,
-        // always false at present, certainly not tested.
-
-        bool calleePopsArgs_;
+        bool reloadMachineStateAfter;
+        bool usesSystemAbi;
+        bool loadTlsBefore;
+        size_t frameAlignAdjustment;
+        size_t stackArgAreaSize;
     };
 
-    void beginCall(FunctionCall& call, bool escapesSandbox, bool builtinCall)
+    void beginCall(FunctionCall& call, UseABI useABI, InterModule interModule)
     {
-        call.callSavesMachineState_ = escapesSandbox;
-        if (escapesSandbox) {
-#if defined(JS_CODEGEN_X64)
-            call.machineStateAreaSize_ = 16; // Save HeapReg
-#elif defined(JS_CODEGEN_X86)
-            // Nothing
-#else
-            MOZ_CRASH("BaseCompiler platform hook: beginCall");
-#endif
-        }
-
-        call.builtinCall_ = builtinCall;
-        if (builtinCall) {
+        call.reloadMachineStateAfter = interModule == InterModule::True || useABI == UseABI::System;
+        call.usesSystemAbi = useABI == UseABI::System;
+        call.loadTlsBefore = useABI == UseABI::Wasm;
+
+        if (call.usesSystemAbi) {
             // Call-outs need to use the appropriate system ABI.
             // ARM will have something here.
         }
 
-        call.frameAlignAdjustment_ = ComputeByteAlignment(masm.framePushed() + sizeof(AsmJSFrame),
-                                                          JitStackAlignment);
+        call.frameAlignAdjustment = ComputeByteAlignment(masm.framePushed() + sizeof(AsmJSFrame),
+                                                         JitStackAlignment);
     }
 
     void endCall(FunctionCall& call)
     {
-        if (call.machineStateAreaSize_ || call.frameAlignAdjustment_) {
-            int size = call.calleePopsArgs_ ? 0 : call.stackArgAreaSize_;
-            if (call.callSavesMachineState_) {
-#if defined(JS_CODEGEN_X64)
-                masm.loadPtr(Address(StackPointer, size + 8), HeapReg);
-#elif defined(JS_CODEGEN_X86)
-                // Nothing
-#else
-                MOZ_CRASH("BaseCompiler platform hook: endCall");
-#endif
-            }
-            masm.freeStack(size + call.machineStateAreaSize_ + call.frameAlignAdjustment_);
-        } else if (!call.calleePopsArgs_) {
-            masm.freeStack(call.stackArgAreaSize_);
+        size_t adjustment = call.stackArgAreaSize + call.frameAlignAdjustment;
+        if (adjustment)
+            masm.freeStack(adjustment);
+
+        if (call.reloadMachineStateAfter) {
+            loadFromFramePtr(WasmTlsReg, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
+            masm.loadWasmPinnedRegsFromTls();
         }
     }
 
     // TODO / OPTIMIZE: This is expensive; let's roll the iterator
     // walking into the walking done for passArg.  See comments in
     // passArg.
 
     size_t stackArgAreaSize(const ValTypeVector& args) {
         ABIArgIter<const ValTypeVector> i(args);
         while (!i.done())
             i++;
         return AlignBytes(i.stackBytesConsumedSoFar(), 16u);
     }
 
     void startCallArgs(FunctionCall& call, size_t stackArgAreaSize)
     {
-        call.stackArgAreaSize_ = stackArgAreaSize;
-        if (call.machineStateAreaSize_ || call.frameAlignAdjustment_) {
-            masm.reserveStack(stackArgAreaSize + call.machineStateAreaSize_ + call.frameAlignAdjustment_);
-            if (call.callSavesMachineState_) {
-#if defined(JS_CODEGEN_X64)
-                masm.storePtr(HeapReg, Address(StackPointer, stackArgAreaSize + 8));
-#elif defined(JS_CODEGEN_X86)
-                // Nothing
-#else
-                MOZ_CRASH("BaseCompiler platform hook: startCallArgs");
-#endif
-            }
-        } else if (stackArgAreaSize > 0) {
-            masm.reserveStack(stackArgAreaSize);
-        }
-    }
-
-    const ABIArg reserveArgument(FunctionCall& call) {
+        call.stackArgAreaSize = stackArgAreaSize;
+
+        size_t adjustment = call.stackArgAreaSize + call.frameAlignAdjustment;
+        if (adjustment)
+            masm.reserveStack(adjustment);
+    }
+
+    const ABIArg reservePointerArgument(FunctionCall& call) {
         return call.abi_.next(MIRType::Pointer);
     }
 
     // TODO / OPTIMIZE: Note passArg is used only in one place.  I'm
     // not saying we should manually inline it, but we could hoist the
     // dispatch into the caller and have type-specific implementations
     // of passArg: passArgI32(), etc.  Then those might be inlined, at
     // least in PGO builds.
@@ -2231,22 +2214,22 @@ class BaseCompiler
           }
           default:
             MOZ_CRASH("Function argument type");
         }
     }
 
     void callDefinition(uint32_t funcDefIndex, const FunctionCall& call)
     {
-        CallSiteDesc desc(call.lineOrBytecode_, CallSiteDesc::FuncDef);
+        CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::FuncDef);
         masm.call(desc, funcDefIndex);
     }
 
     void callSymbolic(SymbolicAddress callee, const FunctionCall& call) {
-        CallSiteDesc desc(call.lineOrBytecode_, CallSiteDesc::Symbolic);
+        CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::Symbolic);
         masm.call(callee);
     }
 
     // Precondition: sync()
 
     void callIndirect(uint32_t sigIndex, Stk& indexVal, const FunctionCall& call)
     {
         loadI32(WasmTableCallIndexReg, indexVal);
@@ -2265,52 +2248,41 @@ class BaseCompiler
         } else {
             MOZ_ASSERT(sig.id.kind() != SigIdDesc::Kind::None);
             MOZ_ASSERT(mg_.tables.length() == 1);
             const TableDesc& table = mg_.tables[0];
 
             callee = CalleeDesc::wasmTable(table, sig.id);
         }
 
-        CallSiteDesc desc(call.lineOrBytecode_, CallSiteDesc::Dynamic);
+        CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::Dynamic);
         masm.wasmCallIndirect(desc, callee);
-
-        // After return, restore the caller's TLS and pinned registers.
-        loadFromFramePtr(WasmTlsReg, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
-        masm.loadWasmPinnedRegsFromTls();
     }
 
     // Precondition: sync()
 
     void callImport(unsigned globalDataOffset, const FunctionCall& call)
     {
-        // There is no need to preserve WasmTlsReg since it has already been
-        // spilt to a local slot.
-
-        CallSiteDesc desc(call.lineOrBytecode_, CallSiteDesc::Dynamic);
+        CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::Dynamic);
         CalleeDesc callee = CalleeDesc::import(globalDataOffset);
         masm.wasmCallImport(desc, callee);
-
-        // After return, restore the caller's TLS and pinned registers.
-        loadFromFramePtr(WasmTlsReg, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
-        masm.loadWasmPinnedRegsFromTls();
     }
 
     void builtinCall(SymbolicAddress builtin, const FunctionCall& call)
     {
         callSymbolic(builtin, call);
     }
 
     void builtinInstanceMethodCall(SymbolicAddress builtin, const ABIArg& instanceArg,
                                    const FunctionCall& call)
     {
-        // Builtin method calls assumed the TLS register has been set.
+        // Builtin method calls assume the TLS register has been set.
         loadFromFramePtr(WasmTlsReg, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
 
-        CallSiteDesc desc(call.lineOrBytecode_, CallSiteDesc::Symbolic);
+        CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::Symbolic);
         masm.wasmCallBuiltinInstanceMethod(instanceArg, builtin);
     }
 
     //////////////////////////////////////////////////////////////////////
     //
     // Sundry low-level code generators.
 
     void addInterruptCheck()
@@ -2361,34 +2333,34 @@ class BaseCompiler
         needI64(rv);
         return rv;
     }
 
     RegF32 captureReturnedF32(const FunctionCall& call) {
         RegF32 rv = RegF32(ReturnFloat32Reg);
         MOZ_ASSERT(isAvailable(rv.reg));
         needF32(rv);
-#ifdef JS_CODEGEN_X86
-        if (call.builtinCall_) {
+#if defined(JS_CODEGEN_X86)
+        if (call.usesSystemAbi) {
             masm.reserveStack(sizeof(float));
             Operand op(esp, 0);
             masm.fstp32(op);
             masm.loadFloat32(op, rv.reg);
             masm.freeStack(sizeof(float));
         }
 #endif
         return rv;
     }
 
     RegF64 captureReturnedF64(const FunctionCall& call) {
         RegF64 rv = RegF64(ReturnDoubleReg);
         MOZ_ASSERT(isAvailable(rv.reg));
         needF64(rv);
-#ifdef JS_CODEGEN_X86
-        if (call.builtinCall_) {
+#if defined(JS_CODEGEN_X86)
+        if (call.usesSystemAbi) {
             masm.reserveStack(sizeof(double));
             Operand op(esp, 0);
             masm.fstp(op);
             masm.loadDouble(op, rv.reg);
             masm.freeStack(sizeof(double));
         }
 #endif
         return rv;
@@ -2429,17 +2401,17 @@ class BaseCompiler
             masm.bind(&notDivByZero);
         } else {
             masm.branchTest32(Assembler::Zero, rhs.reg, rhs.reg, trap(Trap::IntegerDivideByZero));
         }
     }
 
     void checkDivideByZeroI64(RegI64 rhs, RegI64 srcDest, Label* done) {
         MOZ_ASSERT(!isCompilingAsmJS());
-#ifdef JS_CODEGEN_X64
+#if defined(JS_CODEGEN_X64)
         masm.testq(rhs.reg.reg, rhs.reg.reg);
         masm.j(Assembler::Zero, trap(Trap::IntegerDivideByZero));
 #elif defined(JS_CODEGEN_X86)
         Label nonZero;
         masm.branchTest32(Assembler::NonZero, rhs.reg.low, rhs.reg.low, &nonZero);
         masm.branchTest32(Assembler::Zero, rhs.reg.high, rhs.reg.high, trap(Trap::IntegerDivideByZero));
         masm.bind(&nonZero);
 #else
@@ -3308,16 +3280,19 @@ class BaseCompiler
     bool emitCallImportCommon(uint32_t lineOrBytecode, uint32_t funcImportIndex);
     MOZ_MUST_USE
     bool emitCall();
     MOZ_MUST_USE
     bool emitOldCallImport();
     MOZ_MUST_USE
     bool emitCallIndirect(bool oldStyle);
     MOZ_MUST_USE
+    bool emitCommonMathCall(uint32_t lineOrBytecode, SymbolicAddress callee,
+                            ValTypeVector& signature, ExprType retType);
+    MOZ_MUST_USE
     bool emitUnaryMathBuiltinCall(SymbolicAddress callee, ValType operandType);
     MOZ_MUST_USE
     bool emitBinaryMathBuiltinCall(SymbolicAddress callee, ValType operandType);
 #ifdef QUOT_REM_I64_CALLOUT
     MOZ_MUST_USE
     bool emitDivOrModI64BuiltinCall(SymbolicAddress callee, ValType operandType);
 #endif
     MOZ_MUST_USE
@@ -3438,18 +3413,30 @@ class BaseCompiler
     void emitConvertU64ToF32();
     void emitConvertF32ToF64();
     void emitConvertI32ToF64();
     void emitConvertU32ToF64();
     void emitConvertI64ToF64();
     void emitConvertU64ToF64();
     void emitReinterpretI32AsF32();
     void emitReinterpretI64AsF64();
-    MOZ_MUST_USE bool emitGrowMemory();
-    MOZ_MUST_USE bool emitCurrentMemory();
+    MOZ_MUST_USE
+    bool emitGrowMemory();
+    MOZ_MUST_USE
+    bool emitCurrentMemory();
+#ifdef I64_TO_FLOAT_CALLOUT
+    MOZ_MUST_USE
+    bool emitConvertInt64ToFloatingCallout(SymbolicAddress callee, ValType operandType,
+                                           ValType resultType);
+#endif
+#ifdef FLOAT_TO_I64_CALLOUT
+    MOZ_MUST_USE
+    bool emitConvertFloatingToInt64Callout(SymbolicAddress callee, ValType operandType,
+                                           ValType resultType);
+#endif
 };
 
 void
 BaseCompiler::emitAddI32()
 {
     int32_t c;
     if (popConstI32(c)) {
         RegI32 r = popI32();
@@ -5119,20 +5106,21 @@ BaseCompiler::emitCallArgs(const ValType
         ValType argType = args[i];
         Nothing arg_;
         if (!iter_.readCallArg(argType, numArgs, i, &arg_))
             return false;
         Stk& arg = peek(numArgs - 1 - i);
         passArg(baselineCall, argType, arg);
     }
 
-    // Always pass the TLS pointer as a hidden argument in WasmTlsReg.
-    // Load it directly out if its stack slot so we don't interfere with the
-    // stk_.
-    loadFromFramePtr(WasmTlsReg, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
+    // Pass the TLS pointer as a hidden argument in WasmTlsReg.  Load
+    // it directly out if its stack slot so we don't interfere with
+    // the stk_.
+    if (baselineCall.loadTlsBefore)
+        loadFromFramePtr(WasmTlsReg, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
 
     if (!iter_.readCallArgsEnd(numArgs))
         return false;
 
     return true;
 }
 
 void
@@ -5191,17 +5179,17 @@ BaseCompiler::emitCallImportCommon(uint3
         return true;
 
     sync();
 
     uint32_t numArgs = sig.args().length();
     size_t stackSpace = stackConsumed(numArgs);
 
     FunctionCall baselineCall(lineOrBytecode);
-    beginCall(baselineCall, EscapesSandbox(true), IsBuiltinCall(false));
+    beginCall(baselineCall, UseABI::Wasm, InterModule::True);
 
     if (!emitCallArgs(sig.args(), baselineCall))
         return false;
 
     if (!iter_.readCallReturn(sig.ret()))
         return false;
 
     callImport(funcImport.globalDataOffset, baselineCall);
@@ -5241,17 +5229,17 @@ BaseCompiler::emitCall()
     sync();
 
     uint32_t funcDefIndex = calleeIndex - mg_.firstFuncDefIndex;
     const Sig& sig = *mg_.funcDefSigs[funcDefIndex];
     uint32_t numArgs = sig.args().length();
     size_t stackSpace = stackConsumed(numArgs);
 
     FunctionCall baselineCall(lineOrBytecode);
-    beginCall(baselineCall, EscapesSandbox(false), IsBuiltinCall(false));
+    beginCall(baselineCall, UseABI::Wasm, InterModule::False);
 
     if (!emitCallArgs(sig.args(), baselineCall))
         return false;
 
     if (!iter_.readCallReturn(sig.ret()))
         return false;
 
     callDefinition(funcDefIndex, baselineCall);
@@ -5314,17 +5302,17 @@ BaseCompiler::emitCallIndirect(bool oldS
 
     // The arguments must be at the stack top for emitCallArgs, so pop the
     // callee if it is on top.  Note this only pops the compiler's stack,
     // not the CPU stack.
 
     Stk callee = oldStyle ? peek(numArgs) : stk_.popCopy();
 
     FunctionCall baselineCall(lineOrBytecode);
-    beginCall(baselineCall, EscapesSandbox(false), IsBuiltinCall(false));
+    beginCall(baselineCall, UseABI::Wasm, InterModule::True);
 
     if (!emitCallArgs(sig.args(), baselineCall))
         return false;
 
     if (oldStyle) {
         if (!iter_.readOldCallIndirectCallee(&callee_))
             return false;
     }
@@ -5347,48 +5335,30 @@ BaseCompiler::emitCallIndirect(bool oldS
     masm.freeStack(stackSpace);
 
     if (!IsVoid(sig.ret()))
         pushReturned(baselineCall, sig.ret());
 
     return true;
 }
 
-
 bool
-BaseCompiler::emitUnaryMathBuiltinCall(SymbolicAddress callee, ValType operandType)
+BaseCompiler::emitCommonMathCall(uint32_t lineOrBytecode, SymbolicAddress callee,
+                                 ValTypeVector& signature, ExprType retType)
 {
-    if (deadCode_)
-        return true;
-
-    uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
-
     sync();
 
-    uint32_t numArgs = 1;
+    uint32_t numArgs = signature.length();
     size_t stackSpace = stackConsumed(numArgs);
 
     FunctionCall baselineCall(lineOrBytecode);
-    beginCall(baselineCall, EscapesSandbox(false), IsBuiltinCall(true));
-
-    ExprType retType;
-    switch (operandType) {
-      case ValType::F64:
-        if (!emitCallArgs(SigD_, baselineCall))
-            return false;
-        retType = ExprType::F64;
-        break;
-      case ValType::F32:
-        if (!emitCallArgs(SigF_, baselineCall))
-            return false;
-        retType = ExprType::F32;
-        break;
-      default:
-        MOZ_CRASH("Compiler bug: not a float type");
-    }
+    beginCall(baselineCall, UseABI::System, InterModule::False);
+
+    if (!emitCallArgs(signature, baselineCall))
+        return false;
 
     if (!iter_.readCallReturn(retType))
       return false;
 
     builtinCall(callee, baselineCall);
 
     endCall(baselineCall);
 
@@ -5399,58 +5369,44 @@ BaseCompiler::emitUnaryMathBuiltinCall(S
     masm.freeStack(stackSpace);
 
     pushReturned(baselineCall, retType);
 
     return true;
 }
 
 bool
+BaseCompiler::emitUnaryMathBuiltinCall(SymbolicAddress callee, ValType operandType)
+{
+    if (deadCode_)
+        return true;
+
+    uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
+
+    return emitCommonMathCall(lineOrBytecode, callee,
+                              operandType == ValType::F32 ? SigF_ : SigD_,
+                              operandType == ValType::F32 ? ExprType::F32 : ExprType::F64);
+}
+
+bool
 BaseCompiler::emitBinaryMathBuiltinCall(SymbolicAddress callee, ValType operandType)
 {
     MOZ_ASSERT(operandType == ValType::F64);
 
     if (deadCode_)
         return true;
 
     uint32_t lineOrBytecode = 0;
     if (callee == SymbolicAddress::ModD) {
         // Not actually a call in the binary representation
     } else {
-        readCallSiteLineOrBytecode();
-    }
-
-    sync();
-
-    uint32_t numArgs = 2;
-    size_t stackSpace = stackConsumed(numArgs);
-
-    FunctionCall baselineCall(lineOrBytecode);
-    beginCall(baselineCall, EscapesSandbox(false), IsBuiltinCall(true));
-
-    ExprType retType = ExprType::F64;
-    if (!emitCallArgs(SigDD_, baselineCall))
-        return false;
-
-    if (!iter_.readCallReturn(retType))
-        return false;
-
-    builtinCall(callee, baselineCall);
-
-    endCall(baselineCall);
-
-    // TODO / OPTIMIZE: It would be better to merge this freeStack()
-    // into the one in endCall, if we can.
-
-    popValueStackBy(numArgs);
-    masm.freeStack(stackSpace);
-
-    pushReturned(baselineCall, retType);
-
-    return true;
+        lineOrBytecode = readCallSiteLineOrBytecode();
+    }
+
+    return emitCommonMathCall(lineOrBytecode, callee, SigDD_, ExprType::F64);
 }
 
 #ifdef QUOT_REM_I64_CALLOUT
 bool
 BaseCompiler::emitDivOrModI64BuiltinCall(SymbolicAddress callee, ValType operandType)
 {
     MOZ_ASSERT(operandType == ValType::I64);
 
@@ -6237,26 +6193,23 @@ BaseCompiler::emitGrowMemory()
         return false;
 
     sync();
 
     uint32_t numArgs = 1;
     size_t stackSpace = stackConsumed(numArgs);
 
     FunctionCall baselineCall(readCallSiteLineOrBytecode());
-    beginCall(baselineCall, EscapesSandbox(true), IsBuiltinCall(true));
-
-    ABIArg instanceArg = reserveArgument(baselineCall);
+    beginCall(baselineCall, UseABI::System, InterModule::True);
+
+    ABIArg instanceArg = reservePointerArgument(baselineCall);
 
     startCallArgs(baselineCall, stackArgAreaSize(SigI_));
-
     passArg(baselineCall, ValType::I32, peek(0));
-
     builtinInstanceMethodCall(SymbolicAddress::GrowMemory, instanceArg, baselineCall);
-
     endCall(baselineCall);
 
     popValueStackBy(numArgs);
     masm.freeStack(stackSpace);
 
     pushReturned(baselineCall, ExprType::I32);
 
     return true;
@@ -6269,24 +6222,22 @@ BaseCompiler::emitCurrentMemory()
         return true;
 
     if (!iter_.readCurrentMemory())
         return false;
 
     sync();
 
     FunctionCall baselineCall(readCallSiteLineOrBytecode());
-    beginCall(baselineCall, EscapesSandbox(true), IsBuiltinCall(true));
-
-    ABIArg instanceArg = reserveArgument(baselineCall);
+    beginCall(baselineCall, UseABI::System, InterModule::False);
+
+    ABIArg instanceArg = reservePointerArgument(baselineCall);
 
     startCallArgs(baselineCall, stackArgAreaSize(Sig_));
-
     builtinInstanceMethodCall(SymbolicAddress::CurrentMemory, instanceArg, baselineCall);
-
     endCall(baselineCall);
 
     pushReturned(baselineCall, ExprType::I32);
 
     return true;
 }
 
 bool
@@ -7069,17 +7020,17 @@ BaseCompiler::init()
             else
                 l.init(MIRType::Float32, -(i->offsetFromArgBase() + sizeof(AsmJSFrame)));
             break;
           default:
             MOZ_CRASH("Argument type");
         }
     }
 
-    // Reserve a stack slot for the TLS pointer before the varLow - varHigh
+    // Reserve a stack slot for the TLS pointer outside the varLow..varHigh
     // range so it isn't zero-filled like the normal locals.
     localInfo_[tlsSlot_].init(MIRType::Pointer, pushLocal(sizeof(void*)));
 
     varLow_ = localSize_;
 
     for (size_t i = args.length(); i < locals_.length(); i++) {
         Local& l = localInfo_[i];
         switch (locals_[i]) {