Bug 1639153 - Part 3: Implement the algorithm for obtaining tls and use it for wasm signal handling. r=lth
☠☠ backed out by 1fc282e54b7a ☠ ☠
authorDmitry Bezhetskov <dbezhetskov@igalia.com>
Thu, 27 Aug 2020 10:26:17 +0000
changeset 548287 b79c89e6ac82e947d6805ce7fb4ee52f28c55a5a
parent 548286 ab5825b43bb56cdb7b50e536e4cab3734c35088f
child 548288 f2e486f1be1760e1751cf50acea1ab50cb3cfa90
push id37776
push userbtara@mozilla.com
push dateFri, 11 Sep 2020 15:10:42 +0000
treeherdermozilla-central@b133e2d673e8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslth
bugs1639153
milestone82.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 1639153 - Part 3: Implement the algorithm for obtaining tls and use it for wasm signal handling. r=lth This is the third part of series of patches to Frame without tls pointer. Here we preserve initial tls in all entry stubs and then use it to find a proper tls instance for a given frame. To find the TlsData* for specific frame we start from a entry stub's tls and then track tls through all possible cross-instance calls. This logic is implemented in GetNearestEffectiveTls procedure. Then, we use this new procedure to make singal handling free from Frame::tls. Differential Revision: https://phabricator.services.mozilla.com/D83044 Depends on D82888
js/src/jit/MacroAssembler.cpp
js/src/jit/MacroAssembler.h
js/src/wasm/WasmFrameIter.cpp
js/src/wasm/WasmFrameIter.h
js/src/wasm/WasmSignalHandlers.cpp
js/src/wasm/WasmStubs.cpp
js/src/wasm/WasmTypes.h
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -3721,16 +3721,17 @@ CodeOffset MacroAssembler::wasmCallBuilt
     const wasm::CallSiteDesc& desc, const ABIArg& instanceArg,
     wasm::SymbolicAddress builtin, wasm::FailureMode failureMode) {
   MOZ_ASSERT(instanceArg != ABIArg());
 
   storePtr(WasmTlsReg,
            Address(getStackPointer(), WasmCallerTLSOffsetBeforeCall));
   storePtr(WasmTlsReg,
            Address(getStackPointer(), WasmCalleeTLSOffsetBeforeCall));
+
   if (instanceArg.kind() == ABIArg::GPR) {
     loadPtr(Address(WasmTlsReg, offsetof(wasm::TlsData, instance)),
             instanceArg.gpr());
   } else if (instanceArg.kind() == ABIArg::Stack) {
     // Safe to use ABINonArgReg0 since it's the last thing before the call.
     Register scratch = ABINonArgReg0;
     loadPtr(Address(WasmTlsReg, offsetof(wasm::TlsData, instance)), scratch);
     storePtr(scratch,
@@ -4526,12 +4527,24 @@ void AutoGenericRegisterScope<RegisterTy
   released_ = false;
   const RegisterType& reg = *dynamic_cast<RegisterType*>(this);
   masm_.debugTrackedRegisters_.add(reg);
 }
 
 template void AutoGenericRegisterScope<Register>::reacquire();
 template void AutoGenericRegisterScope<FloatRegister>::reacquire();
 
+wasm::TlsData* ExtractCallerTlsFromFrameWithTls(wasm::Frame* fp) {
+  return *reinterpret_cast<wasm::TlsData**>(
+      reinterpret_cast<uint8_t*>(fp) + sizeof(wasm::Frame) + ShadowStackSpace +
+      wasm::FrameWithTls::callerTLSOffset());
+}
+
+wasm::TlsData* ExtractCalleeTlsFromFrameWithTls(wasm::Frame* fp) {
+  return *reinterpret_cast<wasm::TlsData**>(
+      reinterpret_cast<uint8_t*>(fp) + sizeof(wasm::Frame) + ShadowStackSpace +
+      wasm::FrameWithTls::calleeTLSOffset());
+}
+
 #endif  // DEBUG
 
 }  // namespace jit
 }  // namespace js
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -4284,12 +4284,15 @@ class WasmABIArgGenerator : public ABIAr
 
 template <class VecT>
 class WasmABIArgIter : public ABIArgIterBase<VecT, WasmABIArgGenerator> {
  public:
   explicit WasmABIArgIter(const VecT& types)
       : ABIArgIterBase<VecT, WasmABIArgGenerator>(types) {}
 };
 
+wasm::TlsData* ExtractCalleeTlsFromFrameWithTls(wasm::Frame* fp);
+wasm::TlsData* ExtractCallerTlsFromFrameWithTls(wasm::Frame* fp);
+
 }  // namespace jit
 }  // namespace js
 
 #endif /* jit_MacroAssembler_h */
--- a/js/src/wasm/WasmFrameIter.cpp
+++ b/js/src/wasm/WasmFrameIter.cpp
@@ -913,16 +913,44 @@ static bool isSignatureCheckFail(uint32_
   //                        4. jump 7
   // unchecked call entry:  5. push Frame
   //                        6. set FP
   //                        7. function's code
   return offsetInCode < codeRange->funcUncheckedCallEntry() &&
          (offsetInCode - codeRange->funcCheckedCallEntry()) > SetFP;
 }
 
+TlsData* js::wasm::GetNearestEffectiveTls(Frame* fp) {
+  while (true) {
+    if (fp->callerIsExitOrJitEntryFP()) {
+      // It is a direct call from JIT.
+      MOZ_ASSERT(!LookupCode(fp->returnAddress()));
+      return ExtractCalleeTlsFromFrameWithTls(fp);
+    }
+
+    uint8_t* returnAddress = fp->returnAddress();
+    const CodeRange* codeRange = nullptr;
+    const Code* code = LookupCode(returnAddress, &codeRange);
+    MOZ_ASSERT(codeRange);
+
+    if (codeRange->isEntry()) {
+      return ExtractCalleeTlsFromFrameWithTls(fp);
+    }
+
+    MOZ_ASSERT(codeRange->kind() == CodeRange::Function);
+    MOZ_ASSERT(code);
+    const CallSite* callsite = code->lookupCallSite(returnAddress);
+    if (callsite->mightBeCrossInstance()) {
+      return ExtractCalleeTlsFromFrameWithTls(fp);
+    }
+
+    fp = fp->wasmCaller();
+  }
+}
+
 bool js::wasm::StartUnwinding(const RegisterState& registers,
                               UnwindState* unwindState, bool* unwoundCaller) {
   // Shorthands.
   uint8_t* const pc = (uint8_t*)registers.pc;
   void** const sp = (void**)registers.sp;
 
   // The frame pointer might be:
   // - in the process of tagging/untagging when calling into the JITs;
--- a/js/src/wasm/WasmFrameIter.h
+++ b/js/src/wasm/WasmFrameIter.h
@@ -228,16 +228,20 @@ void GenerateJitEntryPrologue(jit::Macro
 
 void GenerateFunctionPrologue(jit::MacroAssembler& masm,
                               const FuncTypeIdDesc& funcTypeId,
                               const mozilla::Maybe<uint32_t>& tier1FuncIndex,
                               FuncOffsets* offsets);
 void GenerateFunctionEpilogue(jit::MacroAssembler& masm, unsigned framePushed,
                               FuncOffsets* offsets);
 
+// Iterates through frames for either possible cross-instance call or an entry
+// stub to obtain tls that corresponds to the passed fp.
+TlsData* GetNearestEffectiveTls(Frame* fp);
+
 // Describes register state and associated code at a given call frame.
 
 struct UnwindState {
   uint8_t* fp;
   void* pc;
   const Code* code;
   const CodeRange* codeRange;
   UnwindState() : fp(nullptr), pc(nullptr), code(nullptr), codeRange(nullptr) {}
--- a/js/src/wasm/WasmSignalHandlers.cpp
+++ b/js/src/wasm/WasmSignalHandlers.cpp
@@ -712,17 +712,19 @@ static MOZ_MUST_USE bool HandleTrap(CONT
     return false;
   }
 
   // We have a safe, expected wasm trap, so fp is well-defined to be a Frame*.
   // For the first sanity check, the Trap::IndirectCallBadSig special case is
   // due to this trap occurring in the indirect call prologue, while fp points
   // to the caller's Frame which can be in a different Module. In any case,
   // though, the containing JSContext is the same.
-  Instance* instance = ((Frame*)ContextToFP(context))->instance();
+
+  auto* frame = reinterpret_cast<Frame*>(ContextToFP(context));
+  Instance* instance = GetNearestEffectiveTls(frame)->instance;
   MOZ_RELEASE_ASSERT(&instance->code() == &segment.code() ||
                      trap == Trap::IndirectCallBadSig);
 
   if (isUnalignedSignal) {
     if (trap != Trap::OutOfBounds) {
       return false;
     }
     if (HandleUnalignedTrap(context, pc, instance)) {
--- a/js/src/wasm/WasmStubs.cpp
+++ b/js/src/wasm/WasmStubs.cpp
@@ -779,16 +779,19 @@ static bool GenerateInterpEntry(MacroAss
   // Copy parameters out of argv and into the wasm ABI registers/stack-slots.
   SetupABIArguments(masm, fe, argv, scratch);
 
   // Setup wasm register state. The nullness of the frame pointer is used to
   // determine whether the call ended in success or failure.
   masm.movePtr(ImmWord(0), FramePointer);
   masm.loadWasmPinnedRegsFromTls();
 
+  masm.storePtr(WasmTlsReg,
+                Address(masm.getStackPointer(), WasmCalleeTLSOffsetBeforeCall));
+
   // Call into the real function. Note that, due to the throw stub, fp, tls
   // and pinned registers may be clobbered.
   masm.assertStackAlignment(WasmStackAlignment);
   CallFuncExport(masm, fe, funcPtr);
   masm.assertStackAlignment(WasmStackAlignment);
 
   // Pop the arguments pushed after the dynamic alignment.
   masm.freeStack(argDecrement);
@@ -1236,16 +1239,19 @@ static bool GenerateJitEntry(MacroAssemb
     }
   }
 
   GenPrintf(DebugChannel::Function, masm, "\n");
 
   // Setup wasm register state.
   masm.loadWasmPinnedRegsFromTls();
 
+  masm.storePtr(WasmTlsReg,
+                Address(masm.getStackPointer(), WasmCalleeTLSOffsetBeforeCall));
+
   // Call into the real function. Note that, due to the throw stub, fp, tls
   // and pinned registers may be clobbered.
   masm.assertStackAlignment(WasmStackAlignment);
   CallFuncExport(masm, fe, funcPtr);
   masm.assertStackAlignment(WasmStackAlignment);
 
   // If fp is equal to the FailFP magic value (set by the throw stub), then
   // report the exception to the JIT caller by jumping into the exception
@@ -1539,16 +1545,18 @@ void wasm::GenerateDirectCallFromJit(Mac
       }
     }
   }
 
   GenPrintf(DebugChannel::Function, masm, "\n");
 
   // Load tls; from now on, WasmTlsReg is live.
   masm.movePtr(ImmPtr(inst.tlsData()), WasmTlsReg);
+  masm.storePtr(WasmTlsReg,
+                Address(masm.getStackPointer(), WasmCalleeTLSOffsetBeforeCall));
   masm.loadWasmPinnedRegsFromTls();
 
   // Actual call.
   const CodeTier& codeTier = inst.code().codeTier(inst.code().bestTier());
   const MetadataTier& metadata = codeTier.metadata();
   const CodeRange& codeRange = metadata.codeRange(fe);
   void* callee = codeTier.segment().base() + codeRange.funcUncheckedCallEntry();
 
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -2571,16 +2571,17 @@ class CallSiteDesc {
   }
   CallSiteDesc(uint32_t lineOrBytecode, Kind kind)
       : lineOrBytecode_(lineOrBytecode), kind_(kind) {
     MOZ_ASSERT(kind == Kind(kind_));
     MOZ_ASSERT(lineOrBytecode == lineOrBytecode_);
   }
   uint32_t lineOrBytecode() const { return lineOrBytecode_; }
   Kind kind() const { return Kind(kind_); }
+  bool mightBeCrossInstance() const { return kind() == CallSiteDesc::Dynamic; }
 };
 
 class CallSite : public CallSiteDesc {
   uint32_t returnAddressOffset_;
 
  public:
   CallSite() : returnAddressOffset_(0) {}
 
@@ -3290,20 +3291,23 @@ class Frame {
     return reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(fp) |
                                       ExitOrJitEntryFPTag);
   }
 };
 
 static_assert(!std::is_polymorphic_v<Frame>, "Frame doesn't need a vtable.");
 
 class FrameWithTls : public Frame {
- public:
   TlsData* calleeTls_;
   TlsData* callerTls_;
 
+ public:
+  TlsData* calleeTls() { return calleeTls_; }
+  TlsData* callerTls() { return callerTls_; }
+
   constexpr static uint32_t sizeWithoutFrame() {
     return sizeof(wasm::FrameWithTls) - sizeof(wasm::Frame);
   }
 
   constexpr static uint32_t calleeTLSOffset() {
     return offsetof(FrameWithTls, calleeTls_) - sizeof(wasm::Frame);
   }