Bug 1279312 - Pass a TLS pointer hidden argument to WebAssembly functions. r=luke
authorJakob Stoklund Olesen <jolesen@mozilla.com>
Mon, 25 Jul 2016 07:57:35 -0700
changeset 348663 29f104c4c3ce6d8b89d6cead50a2bdab8de42731
parent 348662 b1bc27d8f8a583ccdbbb05e1dc5829a11118d8af
child 348664 866d7307db91fd01cd26b50aba25b8396d716d93
push id1230
push userjlund@mozilla.com
push dateMon, 31 Oct 2016 18:13:35 +0000
treeherdermozilla-release@5e06e3766db2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1279312
milestone50.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 1279312 - Pass a TLS pointer hidden argument to WebAssembly functions. r=luke WebAssembly functions now expect to be passed a hidden argument in WasmTlsReg which is a pointer to a TlsData struct. Temporarily allocate a TlsData instance in the wasm::Instance itself. When wasm supports multithreading, we will need to allocate a TlsData instance per thread per module instance. This patch generates code to pass the TLS pointer to WebAssembly functions, preserving it through intra-module calls. The pointer is not used for anything yet, and the the TLS pointer register is not currently preserved across function calls.
js/src/asmjs/WasmInstance.cpp
js/src/asmjs/WasmInstance.h
js/src/asmjs/WasmIonCompile.cpp
js/src/asmjs/WasmModule.cpp
js/src/asmjs/WasmStubs.cpp
js/src/asmjs/WasmTypes.h
js/src/jit/JitCommon.h
js/src/jit/arm/Assembler-arm.h
js/src/jit/arm64/Assembler-arm64.h
js/src/jit/mips-shared/Assembler-mips-shared.h
js/src/jit/x64/Assembler-x64.h
js/src/jit/x86/Assembler-x86.h
--- a/js/src/asmjs/WasmInstance.cpp
+++ b/js/src/asmjs/WasmInstance.cpp
@@ -412,17 +412,18 @@ Instance::callImport_f64(int32_t funcImp
 
     RootedValue rval(cx);
     if (!activation->instance().callImport(cx, funcImportIndex, argc, argv, &rval))
         return false;
 
     return ToNumber(cx, rval, (double*)argv);
 }
 
-Instance::Instance(UniqueCodeSegment codeSegment,
+Instance::Instance(JSContext* cx,
+                   UniqueCodeSegment codeSegment,
                    const Metadata& metadata,
                    const ShareableBytes* maybeBytecode,
                    HandleWasmMemoryObject memory,
                    SharedTableVector&& tables,
                    Handle<FunctionVector> funcImports)
   : codeSegment_(Move(codeSegment)),
     metadata_(&metadata),
     maybeBytecode_(maybeBytecode),
@@ -441,16 +442,18 @@ Instance::Instance(UniqueCodeSegment cod
         exit.baselineScript = nullptr;
     }
 
     if (memory)
         *addressOfMemoryBase() = memory->buffer().dataPointerEither().unwrap();
 
     for (size_t i = 0; i < tables_.length(); i++)
         *addressOfTableBase(i) = tables_[i]->array();
+
+   updateStackLimit(cx);
 }
 
 bool
 Instance::init(JSContext* cx)
 {
     if (!metadata_->sigIds.empty()) {
         ExclusiveData<SigIdSet>::Guard lockedSigIdSet = sigIdSet.lock();
 
@@ -504,16 +507,23 @@ Instance::memoryBase() const
 }
 
 size_t
 Instance::memoryLength() const
 {
     return memory_->buffer().byteLength();
 }
 
+void
+Instance::updateStackLimit(JSContext* cx)
+{
+    // Capture the stack limit for cx's thread.
+    tlsData_.stackLimit = *(void**)cx->stackLimitAddressForJitCode(StackForUntrustedScript);
+}
+
 bool
 Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args)
 {
     const FuncExport& func = metadata_->lookupFuncExport(funcIndex);
 
     // Enable/disable profiling in the Module to match the current global
     // profiling state. Don't do this if the Module is already active on the
     // stack since this would leave the Module in a state where profiling is
@@ -619,17 +629,17 @@ Instance::callExport(JSContext* cx, uint
         // the optimized wasm-to-Ion FFI call path (which we want to be very
         // fast) can avoid doing so. The JitActivation is marked as inactive so
         // stack iteration will skip over it.
         WasmActivation activation(cx, *this);
         JitActivation jitActivation(cx, /* active */ false);
 
         // Call the per-exported-function trampoline created by GenerateEntry.
         auto funcPtr = JS_DATA_TO_FUNC_PTR(ExportFuncPtr, codeSegment_->code() + func.entryOffset());
-        if (!CALL_GENERATED_2(funcPtr, exportArgs.begin(), codeSegment_->globalData()))
+        if (!CALL_GENERATED_3(funcPtr, exportArgs.begin(), codeSegment_->globalData(), tlsData()))
             return false;
     }
 
     if (args.isConstructing()) {
         // By spec, when a function is called as a constructor and this function
         // returns a primary type, which is the case for all wasm exported
         // functions, the returned value is discarded and an empty object is
         // returned instead.
--- a/js/src/asmjs/WasmInstance.h
+++ b/js/src/asmjs/WasmInstance.h
@@ -45,41 +45,51 @@ class Instance
     const SharedMetadata                 metadata_;
     const SharedBytes                    maybeBytecode_;
     GCPtrWasmMemoryObject                memory_;
     SharedTableVector                    tables_;
 
     bool                                 profilingEnabled_;
     CacheableCharsVector                 funcLabels_;
 
+
     UniquePtr<GeneratedSourceMap>        maybeSourceMap_;
 
+    // Thread-local data for code running in this instance.
+    // When threading is supported, we need a TlsData object per thread per
+    // instance.
+    TlsData                              tlsData_;
+
     // Internal helpers:
     uint8_t** addressOfMemoryBase() const;
     void** addressOfTableBase(size_t tableIndex) const;
     const void** addressOfSigId(const SigIdDesc& sigId) const;
     FuncImportExit& funcImportToExit(const FuncImport& fi);
     MOZ_MUST_USE bool toggleProfiling(JSContext* cx);
 
+    // Get this instance's TLS data pointer for the current thread.
+    TlsData* tlsData() { return &tlsData_; }
+
     // An instance keeps track of its innermost WasmActivation. A WasmActivation
     // is pushed for the duration of each call of an export.
     friend class js::WasmActivation;
     WasmActivation*& activation();
 
     // Import call slow paths which are called directly from wasm code.
     friend void* AddressOf(SymbolicAddress, ExclusiveContext*);
     bool callImport(JSContext* cx, uint32_t funcImportIndex, unsigned argc, const uint64_t* argv,
                     MutableHandleValue rval);
     static int32_t callImport_void(int32_t importIndex, int32_t argc, uint64_t* argv);
     static int32_t callImport_i32(int32_t importIndex, int32_t argc, uint64_t* argv);
     static int32_t callImport_i64(int32_t importIndex, int32_t argc, uint64_t* argv);
     static int32_t callImport_f64(int32_t importIndex, int32_t argc, uint64_t* argv);
 
   public:
-    Instance(UniqueCodeSegment codeSegment,
+    Instance(JSContext* cx,
+             UniqueCodeSegment codeSegment,
              const Metadata& metadata,
              const ShareableBytes* maybeBytecode,
              HandleWasmMemoryObject memory,
              SharedTableVector&& tables,
              Handle<FunctionVector> funcImports);
     ~Instance();
     bool init(JSContext* cx);
     void trace(JSTracer* trc);
@@ -131,16 +141,19 @@ class Instance
     // Stack frame iterator support:
 
     const CallSite* lookupCallSite(void* returnAddress) const;
     const CodeRange* lookupCodeRange(void* pc) const;
 #ifdef ASMJS_MAY_USE_SIGNAL_HANDLERS
     const MemoryAccess* lookupMemoryAccess(void* pc) const;
 #endif
 
+    // Update the instance's copy of the stack limit.
+    void updateStackLimit(JSContext*);
+
     // about:memory reporting:
 
     void addSizeOfMisc(MallocSizeOf mallocSizeOf,
                        Metadata::SeenSet* seenMetadata,
                        ShareableBytes::SeenSet* seenBytes,
                        Table::SeenSet* seenTables,
                        size_t* code,
                        size_t* data) const;
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -91,16 +91,19 @@ class FunctionCompiler
     uint32_t                   maxStackArgBytes_;
 
     uint32_t                   loopDepth_;
     uint32_t                   blockDepth_;
     ControlFlowPatchsVector    blockPatches_;
 
     FuncCompileResults&        compileResults_;
 
+    // TLS pointer argument to the current function.
+    MAsmJSParameter*           tlsPointer_;
+
   public:
     FunctionCompiler(const ModuleGeneratorData& mg,
                      Decoder& decoder,
                      const FuncBytes& func,
                      const ValTypeVector& locals,
                      MIRGenerator& mirGen,
                      FuncCompileResults& compileResults)
       : mg_(mg),
@@ -112,17 +115,18 @@ class FunctionCompiler
         graph_(mirGen.graph()),
         info_(mirGen.info()),
         mirGen_(mirGen),
         dummyIns_(nullptr),
         curBlock_(nullptr),
         maxStackArgBytes_(0),
         loopDepth_(0),
         blockDepth_(0),
-        compileResults_(compileResults)
+        compileResults_(compileResults),
+        tlsPointer_(nullptr)
     {}
 
     const ModuleGeneratorData& mg() const    { return mg_; }
     IonExprIter&               iter()        { return iter_; }
     TempAllocator&             alloc() const { return alloc_; }
     MacroAssembler&            masm() const  { return compileResults_.masm(); }
     const Sig&                 sig() const   { return func_.sig(); }
 
@@ -140,16 +144,22 @@ class FunctionCompiler
         for (ABIArgValTypeIter i(args); !i.done(); i++) {
             MAsmJSParameter* ins = MAsmJSParameter::New(alloc(), *i, i.mirType());
             curBlock_->add(ins);
             curBlock_->initSlot(info().localSlot(i.index()), ins);
             if (!mirGen_.ensureBallast())
                 return false;
         }
 
+        // Set up a parameter that receives the hidden TLS pointer argument.
+        tlsPointer_ = MAsmJSParameter::New(alloc(), ABIArg(WasmTlsReg), MIRType::Pointer);
+        curBlock_->add(tlsPointer_);
+        if (!mirGen_.ensureBallast())
+            return false;
+
         for (size_t i = args.length(); i < locals_.length(); i++) {
             MInstruction* ins = nullptr;
             switch (locals_[i]) {
               case ValType::I32:
                 ins = MConstant::NewAsmJS(alloc(), Int32Value(0), MIRType::Int32);
                 break;
               case ValType::I64:
                 ins = MConstant::NewInt64(alloc(), 0);
@@ -808,16 +818,24 @@ class FunctionCompiler
         if (arg.kind() != ABIArg::Stack)
             return args->regArgs_.append(MAsmJSCall::Arg(arg.reg(), argDef));
 
         auto* mir = MAsmJSPassStackArg::New(alloc(), arg.offsetFromArgBase(), argDef);
         curBlock_->add(mir);
         return args->stackArgs_.append(mir);
     }
 
+    // Add the hidden TLS pointer argument to CallArgs.
+    bool passTlsPointer(CallArgs* args)
+    {
+        if (inDeadCode())
+            return true;
+        return args->regArgs_.append(MAsmJSCall::Arg(AnyRegister(WasmTlsReg), tlsPointer_));
+    }
+
     void propagateMaxStackArgBytes(uint32_t stackBytes)
     {
         if (callStack_.empty()) {
             // Outermost call
             maxStackArgBytes_ = Max(maxStackArgBytes_, stackBytes);
             return;
         }
 
@@ -1651,18 +1669,26 @@ EmitReturn(FunctionCompiler& f)
         f.returnVoid();
         return true;
     }
 
     f.returnExpr(value);
     return true;
 }
 
+// Is a callee within the same module instance?
+enum class IntraModule
+{
+    False,
+    True
+};
+
 static bool
-EmitCallArgs(FunctionCompiler& f, const Sig& sig, FunctionCompiler::CallArgs* args)
+EmitCallArgs(FunctionCompiler& f, const Sig& sig, IntraModule intraModule,
+             FunctionCompiler::CallArgs* args)
 {
     if (!f.startCallArgs(args))
         return false;
 
     MDefinition* arg;
     const ValTypeVector& argTypes = sig.args();
     uint32_t numArgs = argTypes.length();
     for (size_t i = 0; i < numArgs; ++i) {
@@ -1671,16 +1697,21 @@ EmitCallArgs(FunctionCompiler& f, const 
             return false;
         if (!f.passArg(arg, argType, args))
             return false;
     }
 
     if (!f.iter().readCallArgsEnd(numArgs))
         return false;
 
+    // Calls within the module pass the module's TLS pointer.
+    // Calls to other modules go through stubs that set up their TLS pointers.
+    if (intraModule == IntraModule::True)
+        f.passTlsPointer(args);
+
     f.finishCallArgs(args);
     return true;
 }
 
 static bool
 EmitCall(FunctionCompiler& f, uint32_t callOffset)
 {
     uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(callOffset);
@@ -1688,17 +1719,17 @@ EmitCall(FunctionCompiler& f, uint32_t c
     uint32_t calleeIndex;
     uint32_t arity;
     if (!f.iter().readCall(&calleeIndex, &arity))
         return false;
 
     const Sig& sig = *f.mg().funcSigs[calleeIndex];
 
     FunctionCompiler::CallArgs args(f, lineOrBytecode);
-    if (!EmitCallArgs(f, sig, &args))
+    if (!EmitCallArgs(f, sig, IntraModule::True, &args))
         return false;
 
     if (!f.iter().readCallReturn(sig.ret()))
         return false;
 
     MDefinition* def;
     if (!f.internalCall(sig, calleeIndex, args, &def))
         return false;
@@ -1718,17 +1749,17 @@ EmitCallIndirect(FunctionCompiler& f, ui
     uint32_t sigIndex;
     uint32_t arity;
     if (!f.iter().readCallIndirect(&sigIndex, &arity))
         return false;
 
     const Sig& sig = f.mg().sigs[sigIndex];
 
     FunctionCompiler::CallArgs args(f, lineOrBytecode);
-    if (!EmitCallArgs(f, sig, &args))
+    if (!EmitCallArgs(f, sig, IntraModule::True, &args))
         return false;
 
     MDefinition* callee;
     if (!f.iter().readCallIndirectCallee(&callee))
         return false;
 
     if (!f.iter().readCallReturn(sig.ret()))
         return false;
@@ -1757,17 +1788,17 @@ EmitCallImport(FunctionCompiler& f, uint
     uint32_t arity;
     if (!f.iter().readCallImport(&funcImportIndex, &arity))
         return false;
 
     const FuncImportGenDesc& funcImport = f.mg().funcImports[funcImportIndex];
     const Sig& sig = *funcImport.sig;
 
     FunctionCompiler::CallArgs args(f, lineOrBytecode);
-    if (!EmitCallArgs(f, sig, &args))
+    if (!EmitCallArgs(f, sig, IntraModule::False, &args))
         return false;
 
     if (!f.iter().readCallReturn(sig.ret()))
         return false;
 
     MDefinition* def;
     if (!f.ffiCall(funcImport.globalDataOffset, args, sig.ret(), &def))
         return false;
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -569,17 +569,18 @@ Module::instantiate(JSContext* cx,
     // before any GC can occur and invalidate the pointers stored in global
     // memory.
 
     {
         instanceObj.set(WasmInstanceObject::create(cx, instanceProto));
         if (!instanceObj)
             return false;
 
-        auto instance = cx->make_unique<Instance>(Move(codeSegment),
+        auto instance = cx->make_unique<Instance>(cx,
+                                                  Move(codeSegment),
                                                   *metadata_,
                                                   maybeBytecode,
                                                   memory,
                                                   Move(tables),
                                                   funcImports);
         if (!instance)
             return false;
 
--- a/js/src/asmjs/WasmStubs.cpp
+++ b/js/src/asmjs/WasmStubs.cpp
@@ -86,20 +86,20 @@ static const unsigned FramePushedAfterSa
 #elif defined(JS_CODEGEN_NONE)
 static const unsigned FramePushedAfterSave = 0;
 #else
 static const unsigned FramePushedAfterSave = NonVolatileRegs.gprs().size() * sizeof(intptr_t)
                                            + NonVolatileRegs.fpus().getPushSizeInBytes();
 #endif
 static const unsigned FramePushedForEntrySP = FramePushedAfterSave + sizeof(void*);
 
-// Generate a stub that enters wasm from a C++ caller via the native ABI.
-// The signature of the entry point is Module::CodePtr. The exported wasm
+// Generate a stub that enters wasm from a C++ caller via the native ABI. The
+// signature of the entry point is Module::ExportFuncPtr. The exported wasm
 // function has an ABI derived from its specific signature, so this function
-// must map from the ABI of CodePtr to the export's signature's ABI.
+// must map from the ABI of ExportFuncPtr to the export's signature's ABI.
 Offsets
 wasm::GenerateEntry(MacroAssembler& masm, const FuncExport& fe, bool usesHeap)
 {
     masm.haltingAlign(CodeAlignment);
 
     Offsets offsets;
     offsets.begin = masm.currentOffset();
 
@@ -127,16 +127,29 @@ wasm::GenerateEntry(MacroAssembler& masm
 #endif
 
     // ARM, MIPS/MIPS64 and x64 have a globally-pinned HeapReg (x86 uses immediates in
     // effective addresses). Loading the heap register depends on the global
     // register already having been loaded.
     if (usesHeap)
         masm.loadAsmJSHeapRegisterFromGlobalData();
 
+    // Put the per-thread, per-module TLS pointer into WasmTlsReg.
+    // This is the third argument in the ExportFuncPtr prototype.
+#if defined(JS_CODEGEN_X86)
+    masm.loadPtr(
+      Address(masm.getStackPointer(), EntryFrameSize + masm.framePushed() + 2 * sizeof(void*)),
+      WasmTlsReg);
+#else
+    masm.movePtr(IntArgReg2, WasmTlsReg);
+#endif
+    // Make sure the TLS pointer is not clobbered by the following code.
+    MOZ_ASSERT(WasmTlsReg != ABINonArgReg0, "TLS pointer can't be scratch reg");
+    MOZ_ASSERT(WasmTlsReg != ABINonArgReg1, "TLS pointer can't be scratch reg");
+
     // Put the 'argv' argument into a non-argument/return register so that we
     // can use 'argv' while we fill in the arguments for the asm.js callee.
     // Also, save 'argv' on the stack so that we can recover it after the call.
     // Use a second non-argument/return register as temporary scratch.
     Register argv = ABINonArgReturnReg0;
     Register scratch = ABINonArgReturnReg1;
     Register64 scratch64(scratch);
 
--- a/js/src/asmjs/WasmTypes.h
+++ b/js/src/asmjs/WasmTypes.h
@@ -895,17 +895,37 @@ struct FuncImportExit
 // be called through an ExportFuncPtr.
 
 struct ExportArg
 {
     uint64_t lo;
     uint64_t hi;
 };
 
-typedef int32_t (*ExportFuncPtr)(ExportArg* args, uint8_t* global);
+// TLS data for a single module instance.
+//
+// Every WebAssembly function expects to be passed a hidden TLS pointer argument
+// in WasmTlsReg. The TLS pointer argument points to a TlsData struct.
+// Compiled functions expect that the TLS pointer does not change for the
+// lifetime of the thread.
+//
+// There is a TlsData per module instance per thread, so inter-module calls need
+// to pass the TLS pointer appropriate for the callee module.
+//
+// After the TlsData struct follows the module's declared TLS variables.
+//
+struct TlsData
+{
+    // Stack limit for the current thread. This limit is checked against the
+    // stack pointer in the prologue of functions that allocate stack space. See
+    // `CodeGenerator::generateWasm`.
+    void* stackLimit;
+};
+
+typedef int32_t (*ExportFuncPtr)(ExportArg* args, uint8_t* global, TlsData* tls);
 
 // Constants:
 
 // The WebAssembly spec hard-codes the virtual page size to be 64KiB and
 // requires linear memory to always be a multiple of 64KiB.
 static const unsigned PageSize = 64 * 1024;
 
 #ifdef ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB
--- a/js/src/jit/JitCommon.h
+++ b/js/src/jit/JitCommon.h
@@ -28,20 +28,25 @@
 #define CALL_GENERATED_1(entry, p0)                     \
     (js::jit::Simulator::Current()->call(               \
         JS_FUNC_TO_DATA_PTR(uint8_t*, entry), 1, p0))
 
 #define CALL_GENERATED_2(entry, p0, p1)                                 \
     (js::jit::Simulator::Current()->call(                               \
         JS_FUNC_TO_DATA_PTR(uint8_t*, entry), 2, p0, p1))
 
+#define CALL_GENERATED_3(entry, p0, p1, p2)                             \
+    (js::jit::Simulator::Current()->call(                               \
+        JS_FUNC_TO_DATA_PTR(uint8_t*, entry), 3, p0, p1, p2))
+
 #else
 
 // Call into jitted code by following the ABI of the native architecture.
 #define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4, p5, p6, p7)   \
   entry(p0, p1, p2, p3, p4, p5, p6, p7)
 
-#define CALL_GENERATED_1(entry, p0)      entry(p0)
-#define CALL_GENERATED_2(entry, p0, p1)  entry(p0, p1)
+#define CALL_GENERATED_1(entry, p0)         entry(p0)
+#define CALL_GENERATED_2(entry, p0, p1)     entry(p0, p1)
+#define CALL_GENERATED_3(entry, p0, p1, p2) entry(p0, p1, p2)
 
 #endif
 
 #endif // jit_JitCommon_h
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -69,16 +69,21 @@ static constexpr Register IntArgReg1 = r
 static constexpr Register IntArgReg2 = r2;
 static constexpr Register IntArgReg3 = r3;
 static constexpr Register GlobalReg = r10;
 static constexpr Register HeapReg = r11;
 static constexpr Register CallTempNonArgRegs[] = { r5, r6, r7, r8 };
 static const uint32_t NumCallTempNonArgRegs =
     mozilla::ArrayLength(CallTempNonArgRegs);
 
+// TLS pointer argument register for WebAssembly functions. This must not alias
+// any other register used for passing function arguments or return values.
+// Preserved by WebAssembly functions.
+static constexpr Register WasmTlsReg = r9;
+
 class ABIArgGenerator
 {
     unsigned intRegIndex_;
     unsigned floatRegIndex_;
     uint32_t stackOffset_;
     ABIArg current_;
 
     // ARM can either use HardFp (use float registers for float arguments), or
--- a/js/src/jit/arm64/Assembler-arm64.h
+++ b/js/src/jit/arm64/Assembler-arm64.h
@@ -83,16 +83,21 @@ static constexpr Register IntArgReg3 = {
 static constexpr Register IntArgReg4 = { Registers::x4 };
 static constexpr Register IntArgReg5 = { Registers::x5 };
 static constexpr Register IntArgReg6 = { Registers::x6 };
 static constexpr Register IntArgReg7 = { Registers::x7 };
 static constexpr Register GlobalReg =  { Registers::x20 };
 static constexpr Register HeapReg = { Registers::x21 };
 static constexpr Register HeapLenReg = { Registers::x22 };
 
+// TLS pointer argument register for WebAssembly functions. This must not alias
+// any other register used for passing function arguments or return values.
+// Preserved by WebAssembly functions.
+static constexpr Register WasmTlsReg = { Registers::x17 };
+
 // Define unsized Registers.
 #define DEFINE_UNSIZED_REGISTERS(N)  \
 static constexpr Register r##N = { Registers::x##N };
 REGISTER_CODE_LIST(DEFINE_UNSIZED_REGISTERS)
 #undef DEFINE_UNSIZED_REGISTERS
 static constexpr Register ip0 = { Registers::x16 };
 static constexpr Register ip1 = { Registers::x16 };
 static constexpr Register fp  = { Registers::x30 };
--- a/js/src/jit/mips-shared/Assembler-mips-shared.h
+++ b/js/src/jit/mips-shared/Assembler-mips-shared.h
@@ -102,16 +102,21 @@ static constexpr Register InvalidReg = {
 static constexpr FloatRegister InvalidFloatReg;
 
 static constexpr Register StackPointer = sp;
 static constexpr Register FramePointer = InvalidReg;
 static constexpr Register ReturnReg = v0;
 static constexpr FloatRegister ReturnSimd128Reg = InvalidFloatReg;
 static constexpr FloatRegister ScratchSimd128Reg = InvalidFloatReg;
 
+// TLS pointer argument register for WebAssembly functions. This must not alias
+// any other register used for passing function arguments or return values.
+// Preserved by WebAssembly functions.
+static constexpr Register WasmTlsReg = s5;
+
 // A bias applied to the GlobalReg to allow the use of instructions with small
 // negative immediate offsets which doubles the range of global data that can be
 // accessed with a single instruction.
 static const int32_t AsmJSGlobalRegBias = 32768;
 
 // Registers used in the GenerateFFIIonExit Enable Activation block.
 static constexpr Register AsmJSIonExitRegCallee = t0;
 static constexpr Register AsmJSIonExitRegE0 = a0;
--- a/js/src/jit/x64/Assembler-x64.h
+++ b/js/src/jit/x64/Assembler-x64.h
@@ -144,16 +144,21 @@ static constexpr FloatRegister FloatArgR
 static constexpr FloatRegister FloatArgReg4 = xmm4;
 static constexpr FloatRegister FloatArgReg5 = xmm5;
 static constexpr FloatRegister FloatArgReg6 = xmm6;
 static constexpr FloatRegister FloatArgReg7 = xmm7;
 static constexpr uint32_t NumFloatArgRegs = 8;
 static constexpr FloatRegister FloatArgRegs[NumFloatArgRegs] = { xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7 };
 #endif
 
+// TLS pointer argument register for WebAssembly functions. This must not alias
+// any other register used for passing function arguments or return values.
+// Preserved by WebAssembly functions.
+static constexpr Register WasmTlsReg = r14;
+
 // Registers used in the GenerateFFIIonExit Enable Activation block.
 static constexpr Register AsmJSIonExitRegCallee = r10;
 static constexpr Register AsmJSIonExitRegE0 = rax;
 static constexpr Register AsmJSIonExitRegE1 = rdi;
 static constexpr Register AsmJSIonExitRegE2 = rbx;
 
 // Registers used in the GenerateFFIIonExit Disable Activation block.
 static constexpr Register AsmJSIonExitRegReturnData = ecx;
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -47,16 +47,21 @@ static constexpr Register ReturnReg = ea
 static constexpr Register64 ReturnReg64(InvalidReg, InvalidReg);
 static constexpr FloatRegister ReturnFloat32Reg = FloatRegister(X86Encoding::xmm0, FloatRegisters::Single);
 static constexpr FloatRegister ReturnDoubleReg = FloatRegister(X86Encoding::xmm0, FloatRegisters::Double);
 static constexpr FloatRegister ReturnSimd128Reg = FloatRegister(X86Encoding::xmm0, FloatRegisters::Simd128);
 static constexpr FloatRegister ScratchFloat32Reg = FloatRegister(X86Encoding::xmm7, FloatRegisters::Single);
 static constexpr FloatRegister ScratchDoubleReg = FloatRegister(X86Encoding::xmm7, FloatRegisters::Double);
 static constexpr FloatRegister ScratchSimd128Reg = FloatRegister(X86Encoding::xmm7, FloatRegisters::Simd128);
 
+// TLS pointer argument register for WebAssembly functions. This must not alias
+// any other register used for passing function arguments or return values.
+// Preserved by WebAssembly functions.
+static constexpr Register WasmTlsReg = esi;
+
 // Avoid ebp, which is the FramePointer, which is unavailable in some modes.
 static constexpr Register ArgumentsRectifierReg = esi;
 static constexpr Register CallTempReg0 = edi;
 static constexpr Register CallTempReg1 = eax;
 static constexpr Register CallTempReg2 = ebx;
 static constexpr Register CallTempReg3 = ecx;
 static constexpr Register CallTempReg4 = esi;
 static constexpr Register CallTempReg5 = edx;