Bug 1554080 part 4 - Store JitScript* instead of BaselineScript* in FuncImportTls. r=bbouvier
authorJan de Mooij <jdemooij@mozilla.com>
Mon, 27 May 2019 14:08:23 +0000
changeset 475738 81d0d9e9990d250792aa42969236c9e248c04c45
parent 475737 e8484107b29e114c3d55338aae70e182a9441416
child 475739 b32580fad0489689aa78fd4f43e58ee56ae33580
push id113228
push userdluca@mozilla.com
push dateTue, 28 May 2019 09:48:00 +0000
treeherdermozilla-inbound@900ff508219c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1554080
milestone69.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 1554080 part 4 - Store JitScript* instead of BaselineScript* in FuncImportTls. r=bbouvier This has a few benefits: * Less toggling when we discard/reallocate the BaselineScript without discarding the JitScript. * Wasm's JIT exit optimization will work with the Baseline Interpreter in the future. * The next part will use this to eliminate a load. Differential Revision: https://phabricator.services.mozilla.com/D32444
js/src/jit/BaselineJIT.cpp
js/src/jit/BaselineJIT.h
js/src/jit/JitScript.cpp
js/src/jit/JitScript.h
js/src/wasm/WasmInstance.cpp
js/src/wasm/WasmStubs.cpp
js/src/wasm/WasmTypes.h
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -16,17 +16,16 @@
 #include "jit/CompileInfo.h"
 #include "jit/IonControlFlow.h"
 #include "jit/JitCommon.h"
 #include "jit/JitSpewer.h"
 #include "util/StructuredSpewer.h"
 #include "vm/Debugger.h"
 #include "vm/Interpreter.h"
 #include "vm/TraceLogging.h"
-#include "wasm/WasmInstance.h"
 
 #include "gc/PrivateIterators-inl.h"
 #include "jit/JitFrames-inl.h"
 #include "jit/MacroAssembler-inl.h"
 #include "vm/BytecodeUtil-inl.h"
 #include "vm/GeckoProfiler-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/JSScript-inl.h"
@@ -526,64 +525,25 @@ void BaselineScript::writeBarrierPre(Zon
 
 void BaselineScript::Trace(JSTracer* trc, BaselineScript* script) {
   script->trace(trc);
 }
 
 void BaselineScript::Destroy(FreeOp* fop, BaselineScript* script) {
   MOZ_ASSERT(!script->hasPendingIonBuilder());
 
-  script->unlinkDependentWasmImports(fop);
-
   fop->delete_(script);
 }
 
 void JS::DeletePolicy<js::jit::BaselineScript>::operator()(
     const js::jit::BaselineScript* script) {
   BaselineScript::Destroy(rt_->defaultFreeOp(),
                           const_cast<BaselineScript*>(script));
 }
 
-void BaselineScript::unlinkDependentWasmImports(FreeOp* fop) {
-  // Remove any links from wasm::Instances that contain optimized FFI calls into
-  // this BaselineScript.
-  if (dependentWasmImports_) {
-    for (DependentWasmImport& dep : *dependentWasmImports_) {
-      dep.instance->deoptimizeImportExit(dep.importIndex);
-    }
-    dependentWasmImports_.reset();
-  }
-}
-
-bool BaselineScript::addDependentWasmImport(JSContext* cx,
-                                            wasm::Instance& instance,
-                                            uint32_t idx) {
-  if (!dependentWasmImports_) {
-    dependentWasmImports_ = cx->make_unique<Vector<DependentWasmImport>>(cx);
-    if (!dependentWasmImports_) {
-      return false;
-    }
-  }
-  return dependentWasmImports_->emplaceBack(instance, idx);
-}
-
-void BaselineScript::removeDependentWasmImport(wasm::Instance& instance,
-                                               uint32_t idx) {
-  if (!dependentWasmImports_) {
-    return;
-  }
-
-  for (DependentWasmImport& dep : *dependentWasmImports_) {
-    if (dep.instance == &instance && dep.importIndex == idx) {
-      dependentWasmImports_->erase(&dep);
-      break;
-    }
-  }
-}
-
 RetAddrEntry& BaselineScript::retAddrEntry(size_t index) {
   MOZ_ASSERT(index < numRetAddrEntries());
   return retAddrEntryList()[index];
 }
 
 PCMappingIndexEntry& BaselineScript::pcMappingIndexEntry(size_t index) {
   MOZ_ASSERT(index < numPCMappingIndexEntries());
   return pcMappingIndexEntryList()[index];
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -84,26 +84,16 @@ struct PCMappingIndexEntry {
 
   // Native code offset.
   uint32_t nativeOffset;
 
   // Offset in the CompactBuffer where data for pcOffset starts.
   uint32_t bufferOffset;
 };
 
-// Describes a single wasm::ImportExit which jumps (via an import with
-// the given index) directly to a BaselineScript or IonScript.
-struct DependentWasmImport {
-  wasm::Instance* instance;
-  size_t importIndex;
-
-  DependentWasmImport(wasm::Instance& instance, size_t importIndex)
-      : instance(&instance), importIndex(importIndex) {}
-};
-
 // Largest script that the baseline compiler will attempt to compile.
 #if defined(JS_CODEGEN_ARM)
 // ARM branches can only reach 32MB, and the macroassembler doesn't mitigate
 // that limitation. Use a stricter limit on the acceptable script size to
 // avoid crashing when branches go out of range.
 static constexpr uint32_t BaselineMaxScriptLength = 1000000u;
 #else
 static constexpr uint32_t BaselineMaxScriptLength = 0x0fffffffu;
@@ -210,20 +200,16 @@ struct BaselineScript final {
   // Code pointer containing the actual method.
   HeapPtr<JitCode*> method_ = nullptr;
 
   // For functions with a call object, template objects to use for the call
   // object and decl env object (linked via the call object's enclosing
   // scope).
   HeapPtr<EnvironmentObject*> templateEnv_ = nullptr;
 
-  // If non-null, the list of wasm::Modules that contain an optimized call
-  // directly to this script.
-  js::UniquePtr<Vector<DependentWasmImport>> dependentWasmImports_;
-
   // Early Ion bailouts will enter at this address. This is after frame
   // construction and before environment chain is initialized.
   uint32_t bailoutPrologueOffset_;
 
   // Baseline Interpreter can enter Baseline Compiler code at this address. This
   // is right after the warm-up counter check in the prologue.
   uint32_t warmUpCheckPrologueOffset_;
 
@@ -448,22 +434,16 @@ struct BaselineScript final {
   }
 
   // Return the bytecode offset for a given native code address. Be careful
   // when using this method: we don't emit code for some bytecode ops, so
   // the result may not be accurate.
   jsbytecode* approximatePcForNativeAddress(JSScript* script,
                                             uint8_t* nativeAddress);
 
-  MOZ_MUST_USE bool addDependentWasmImport(JSContext* cx,
-                                           wasm::Instance& instance,
-                                           uint32_t idx);
-  void removeDependentWasmImport(wasm::Instance& instance, uint32_t idx);
-  void unlinkDependentWasmImports(FreeOp* fop);
-
   // Toggle debug traps (used for breakpoints and step mode) in the script.
   // If |pc| is nullptr, toggle traps for all ops in the script. Else, only
   // toggle traps at |pc|.
   void toggleDebugTraps(JSScript* script, jsbytecode* pc);
 
   void toggleProfilerInstrumentation(bool enable);
   bool isProfilerInstrumentationOn() const {
     return flags_ & PROFILER_INSTRUMENTATION_ON;
--- a/js/src/jit/JitScript.cpp
+++ b/js/src/jit/JitScript.cpp
@@ -10,16 +10,17 @@
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Move.h"
 #include "mozilla/ScopeExit.h"
 
 #include "jit/BaselineIC.h"
 #include "vm/JSScript.h"
 #include "vm/Stack.h"
 #include "vm/TypeInference.h"
+#include "wasm/WasmInstance.h"
 
 #include "jit/JSJitFrameIter-inl.h"
 #include "vm/JSScript-inl.h"
 #include "vm/TypeInference-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
@@ -220,17 +221,19 @@ void JitScript::printTypes(JSContext* cx
   }
 
   fprintf(stderr, "\n");
 }
 #endif /* DEBUG */
 
 /* static */
 void JitScript::Destroy(Zone* zone, JitScript* script) {
+  script->unlinkDependentWasmImports();
   script->prepareForDestruction(zone);
+
   js_delete(script);
 }
 
 struct ICEntries {
   JitScript* const jitScript_;
 
   explicit ICEntries(JitScript* jitScript) : jitScript_(jitScript) {}
 
@@ -418,16 +421,52 @@ void JitScript::noteHasDenseAdd(uint32_t
   ICEntry& entry = icEntryFromPCOffset(pcOffset);
   ICFallbackStub* stub = entry.fallbackStub();
 
   if (stub->isSetElem_Fallback()) {
     stub->toSetElem_Fallback()->noteHasDenseAdd();
   }
 }
 
+void JitScript::unlinkDependentWasmImports() {
+  // Remove any links from wasm::Instances that contain optimized FFI calls into
+  // this JitScript.
+  if (dependentWasmImports_) {
+    for (DependentWasmImport& dep : *dependentWasmImports_) {
+      dep.instance->deoptimizeImportExit(dep.importIndex);
+    }
+    dependentWasmImports_.reset();
+  }
+}
+
+bool JitScript::addDependentWasmImport(JSContext* cx, wasm::Instance& instance,
+                                       uint32_t idx) {
+  if (!dependentWasmImports_) {
+    dependentWasmImports_ = cx->make_unique<Vector<DependentWasmImport>>(cx);
+    if (!dependentWasmImports_) {
+      return false;
+    }
+  }
+  return dependentWasmImports_->emplaceBack(instance, idx);
+}
+
+void JitScript::removeDependentWasmImport(wasm::Instance& instance,
+                                          uint32_t idx) {
+  if (!dependentWasmImports_) {
+    return;
+  }
+
+  for (DependentWasmImport& dep : *dependentWasmImports_) {
+    if (dep.instance == &instance && dep.importIndex == idx) {
+      dependentWasmImports_->erase(&dep);
+      break;
+    }
+  }
+}
+
 #ifdef JS_STRUCTURED_SPEW
 static bool GetStubEnteredCount(ICStub* stub, uint32_t* count) {
   switch (stub->kind()) {
     case ICStub::CacheIR_Regular:
       *count = stub->toCacheIR_Regular()->enteredCount();
       return true;
     case ICStub::CacheIR_Updated:
       *count = stub->toCacheIR_Updated()->enteredCount();
--- a/js/src/jit/JitScript.h
+++ b/js/src/jit/JitScript.h
@@ -11,16 +11,26 @@
 #include "js/UniquePtr.h"
 #include "vm/TypeInference.h"
 
 class JSScript;
 
 namespace js {
 namespace jit {
 
+// Describes a single wasm::ImportExit which jumps (via an import with
+// the given index) directly to a BaselineScript or IonScript.
+struct DependentWasmImport {
+  wasm::Instance* instance;
+  size_t importIndex;
+
+  DependentWasmImport(wasm::Instance& instance, size_t importIndex)
+      : instance(&instance), importIndex(importIndex) {}
+};
+
 // [SMDOC] JitScript
 //
 // JitScript stores type inference data, Baseline ICs and other JIT-related data
 // for a script. Scripts with a JitScript can run in the Baseline Interpreter.
 //
 // IC Data
 // =======
 // All IC data for Baseline (Interpreter and JIT) is stored in JitScript. Ion
@@ -86,16 +96,20 @@ class alignas(uintptr_t) JitScript final
   // contains compilations that inlined this script, so we can invalidate
   // them as well.
   RecompileInfoVector inlinedCompilations_;
 
   // Like JSScript::jitCodeRaw_ but when the script has an IonScript this can
   // point to a separate entry point that skips the argument type checks.
   uint8_t* jitCodeSkipArgCheck_ = nullptr;
 
+  // If non-null, the list of wasm::Modules that contain an optimized call
+  // directly to this script.
+  js::UniquePtr<Vector<DependentWasmImport>> dependentWasmImports_;
+
   // Offset of the StackTypeSet array.
   uint32_t typeSetOffset_ = 0;
 
   // Offset of the bytecode type map.
   uint32_t bytecodeTypeMapOffset_ = 0;
 
   // This field is used to avoid binary searches for the sought entry when
   // bytecode map queries are in linear order.
@@ -302,16 +316,22 @@ class alignas(uintptr_t) JitScript final
   ICEntry* interpreterICEntryFromPCOffset(uint32_t pcOffset);
 
   ICEntry* maybeICEntryFromPCOffset(uint32_t pcOffset);
   ICEntry* maybeICEntryFromPCOffset(uint32_t pcOffset,
                                     ICEntry* prevLookedUpEntry);
 
   ICEntry& icEntryFromPCOffset(uint32_t pcOffset);
   ICEntry& icEntryFromPCOffset(uint32_t pcOffset, ICEntry* prevLookedUpEntry);
+
+  MOZ_MUST_USE bool addDependentWasmImport(JSContext* cx,
+                                           wasm::Instance& instance,
+                                           uint32_t idx);
+  void removeDependentWasmImport(wasm::Instance& instance, uint32_t idx);
+  void unlinkDependentWasmImports();
 };
 
 // Ensures no JitScripts are purged in the current zone.
 class MOZ_RAII AutoKeepJitScripts {
   TypeZone& zone_;
   bool prev_;
 
   AutoKeepJitScripts(const AutoKeepJitScripts&) = delete;
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -14,20 +14,20 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "wasm/WasmInstance.h"
 
 #include "jit/AtomicOperations.h"
-#include "jit/BaselineJIT.h"
 #include "jit/InlinableNatives.h"
 #include "jit/JitCommon.h"
 #include "jit/JitRealm.h"
+#include "jit/JitScript.h"
 #include "util/StringBuffer.h"
 #include "util/Text.h"
 #include "wasm/WasmBuiltins.h"
 #include "wasm/WasmModule.h"
 #include "wasm/WasmStubs.h"
 
 #include "gc/StoreBuffer-inl.h"
 #include "vm/ArrayBufferObject-inl.h"
@@ -170,29 +170,24 @@ bool Instance::callImport(JSContext* cx,
   void* jitExitCode = codeBase(tier) + fi.jitExitCodeOffset();
 
   // Test if the function is JIT compiled.
   if (!importFun->hasScript()) {
     return true;
   }
 
   JSScript* script = importFun->nonLazyScript();
-  if (!script->hasBaselineScript()) {
-    MOZ_ASSERT(!script->hasIonScript());
+  if (!script->hasJitScript()) {
     return true;
   }
 
   // Ensure the argument types are included in the argument TypeSets stored in
   // the JitScript. This is necessary for Ion, because the import will use
-  // the skip-arg-checks entry point.
-  //
-  // Note that the JitScript is never discarded while the script has a
-  // BaselineScript, so if those checks hold now they must hold at least until
-  // the BaselineScript is discarded and when that happens the import is
-  // patched back.
+  // the skip-arg-checks entry point. When the JitScript is discarded the import
+  // is patched back.
   AutoSweepJitScript sweep(script);
   JitScript* jitScript = script->jitScript();
 
   StackTypeSet* thisTypes = jitScript->thisTypes(sweep, script);
   if (!thisTypes->hasType(TypeSet::UndefinedType())) {
     return true;
   }
 
@@ -238,23 +233,22 @@ bool Instance::callImport(JSContext* cx,
   for (uint32_t i = importArgs.length(); i < importFun->nargs(); i++) {
     StackTypeSet* argTypes = jitScript->argTypes(sweep, script, i);
     if (!argTypes->hasType(TypeSet::UndefinedType())) {
       return true;
     }
   }
 
   // Let's optimize it!
-  if (!script->baselineScript()->addDependentWasmImport(cx, *this,
-                                                        funcImportIndex)) {
+  if (!jitScript->addDependentWasmImport(cx, *this, funcImportIndex)) {
     return false;
   }
 
   import.code = jitExitCode;
-  import.baselineScript = script->baselineScript();
+  import.jitScript = jitScript;
   return true;
 }
 
 /* static */ int32_t /* 0 to signal trap; 1 to signal OK */
 Instance::callImport_void(Instance* instance, int32_t funcImportIndex,
                           int32_t argc, uint64_t* argv) {
   JSContext* cx = TlsContext.get();
   RootedValue rval(cx);
@@ -1202,27 +1196,27 @@ Instance::Instance(JSContext* cx, Handle
       Instance& calleeInstance = calleeInstanceObj->instance();
       Tier calleeTier = calleeInstance.code().bestTier();
       const CodeRange& codeRange =
           calleeInstanceObj->getExportedFunctionCodeRange(f, calleeTier);
       import.tls = calleeInstance.tlsData();
       import.realm = f->realm();
       import.code =
           calleeInstance.codeBase(calleeTier) + codeRange.funcNormalEntry();
-      import.baselineScript = nullptr;
+      import.jitScript = nullptr;
     } else if (void* thunk = MaybeGetBuiltinThunk(f, fi.funcType())) {
       import.tls = tlsData();
       import.realm = f->realm();
       import.code = thunk;
-      import.baselineScript = nullptr;
+      import.jitScript = nullptr;
     } else {
       import.tls = tlsData();
       import.realm = f->realm();
       import.code = codeBase(callerTier) + fi.interpExitCodeOffset();
-      import.baselineScript = nullptr;
+      import.jitScript = nullptr;
     }
   }
 
   for (size_t i = 0; i < tables_.length(); i++) {
     const TableDesc& td = metadata().tables[i];
     TableTls& table = tableTls(td);
     table.length = tables_[i]->length();
     table.functionBase = tables_[i]->functionBase();
@@ -1336,18 +1330,18 @@ bool Instance::init(JSContext* cx, const
 Instance::~Instance() {
   realm_->wasm.unregisterInstance(*this);
 
   const FuncImportVector& funcImports =
       metadata(code().stableTier()).funcImports;
 
   for (unsigned i = 0; i < funcImports.length(); i++) {
     FuncImportTls& import = funcImportTls(funcImports[i]);
-    if (import.baselineScript) {
-      import.baselineScript->removeDependentWasmImport(*this, i);
+    if (import.jitScript) {
+      import.jitScript->removeDependentWasmImport(*this, i);
     }
   }
 
   if (!metadata().funcTypeIds.empty()) {
     ExclusiveData<FuncTypeIdSet>::Guard lockedFuncTypeIdSet =
         funcTypeIdSet.lock();
 
     for (const FuncTypeWithId& funcType : metadata().funcTypeIds) {
@@ -1880,17 +1874,17 @@ void Instance::onMovingGrowTable(const T
   }
 }
 
 void Instance::deoptimizeImportExit(uint32_t funcImportIndex) {
   Tier t = code().bestTier();
   const FuncImport& fi = metadata(t).funcImports[funcImportIndex];
   FuncImportTls& import = funcImportTls(fi);
   import.code = codeBase(t) + fi.interpExitCodeOffset();
-  import.baselineScript = nullptr;
+  import.jitScript = nullptr;
 }
 
 JSString* Instance::createDisplayURL(JSContext* cx) {
   // In the best case, we simply have a URL, from a streaming compilation of a
   // fetched Response.
 
   if (metadata().filenameIsURL) {
     return NewStringCopyZ<CanGC>(cx, metadata().filename.get());
--- a/js/src/wasm/WasmStubs.cpp
+++ b/js/src/wasm/WasmStubs.cpp
@@ -15,16 +15,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "wasm/WasmStubs.h"
 
 #include "mozilla/ArrayUtils.h"
 
+#include "jit/JitScript.h"
 #include "jit/RegisterAllocator.h"
 #include "js/Printf.h"
 #include "wasm/WasmCode.h"
 #include "wasm/WasmGenerator.h"
 #include "wasm/WasmInstance.h"
 
 #include "jit/MacroAssembler-inl.h"
 
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -38,17 +38,17 @@
 #include "js/Utility.h"
 #include "js/Vector.h"
 #include "vm/MallocProvider.h"
 #include "wasm/WasmConstants.h"
 
 namespace js {
 
 namespace jit {
-struct BaselineScript;
+class JitScript;
 enum class RoundingMode;
 }  // namespace jit
 
 // This is a widespread header, so lets keep out the core wasm impl types.
 
 typedef GCVector<JSFunction*, 0, SystemAllocPolicy> JSFunctionVector;
 
 class WasmMemoryObject;
@@ -2087,19 +2087,19 @@ struct FuncImportTls {
 
   // The callee's TlsData pointer, which must be loaded to WasmTlsReg (along
   // with any pinned registers) before calling 'code'.
   TlsData* tls;
 
   // The callee function's realm.
   JS::Realm* realm;
 
-  // If 'code' points into a JIT code thunk, the BaselineScript of the callee,
-  // for bidirectional registration purposes.
-  jit::BaselineScript* baselineScript;
+  // If 'code' points into a JIT code thunk, the JitScript of the callee, for
+  // bidirectional registration purposes.
+  jit::JitScript* jitScript;
 
   // A GC pointer which keeps the callee alive and is used to recover import
   // values for lazy table initialization.
   GCPtrFunction fun;
   static_assert(sizeof(GCPtrFunction) == sizeof(void*), "for JIT access");
 };
 
 // TableTls describes the region of wasm global memory allocated in the