Bug 1552154 part 2 - Stop using pc-to-native map for OSR into Baseline JIT. r=tcampbell
authorJan de Mooij <jdemooij@mozilla.com>
Sat, 10 Aug 2019 10:20:27 +0000
changeset 487323 77497544ca03361d243b6d08bc9f1ec4d4cb550f
parent 487322 235c854c3233076a9a4a25cc70849b16a0adca4b
child 487324 941b29575f6ab24bca3c8adb87448cf0238260f1
push id113869
push userncsoregi@mozilla.com
push dateSat, 10 Aug 2019 21:42:30 +0000
treeherdermozilla-inbound@5cd7526ffbc3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstcampbell
bugs1552154
milestone70.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 1552154 part 2 - Stop using pc-to-native map for OSR into Baseline JIT. r=tcampbell Differential Revision: https://phabricator.services.mozilla.com/D40945
js/src/jit/BaselineCodeGen.cpp
js/src/jit/BaselineCodeGen.h
js/src/jit/BaselineJIT.cpp
js/src/jit/BaselineJIT.h
--- a/js/src/jit/BaselineCodeGen.cpp
+++ b/js/src/jit/BaselineCodeGen.cpp
@@ -287,23 +287,23 @@ MethodStatus BaselineCompiler::compile()
   }
 
   if (pcEntries.oom()) {
     ReportOutOfMemory(cx);
     return Method_Error;
   }
 
   UniquePtr<BaselineScript> baselineScript(
-      BaselineScript::New(script, warmUpCheckPrologueOffset_.offset(),
-                          profilerEnterFrameToggleOffset_.offset(),
-                          profilerExitFrameToggleOffset_.offset(),
-                          handler.retAddrEntries().length(),
-                          pcMappingIndexEntries.length(), pcEntries.length(),
-                          script->resumeOffsets().size(),
-                          traceLoggerToggleOffsets_.length()),
+      BaselineScript::New(
+          script, warmUpCheckPrologueOffset_.offset(),
+          profilerEnterFrameToggleOffset_.offset(),
+          profilerExitFrameToggleOffset_.offset(),
+          handler.retAddrEntries().length(), handler.osrEntries().length(),
+          pcMappingIndexEntries.length(), pcEntries.length(),
+          script->resumeOffsets().size(), traceLoggerToggleOffsets_.length()),
       JS::DeletePolicy<BaselineScript>(cx->runtime()));
   if (!baselineScript) {
     ReportOutOfMemory(cx);
     return Method_Error;
   }
 
   baselineScript->setMethod(code);
 
@@ -314,16 +314,17 @@ MethodStatus BaselineCompiler::compile()
 
   MOZ_ASSERT(pcMappingIndexEntries.length() > 0);
   baselineScript->copyPCMappingIndexEntries(&pcMappingIndexEntries[0]);
 
   MOZ_ASSERT(pcEntries.length() > 0);
   baselineScript->copyPCMappingEntries(pcEntries);
 
   baselineScript->copyRetAddrEntries(handler.retAddrEntries().begin());
+  baselineScript->copyOSREntries(handler.osrEntries().begin());
 
   // If profiler instrumentation is enabled, toggle instrumentation on.
   if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(
           cx->runtime())) {
     baselineScript->toggleProfilerInstrumentation(true);
   }
 
 #ifdef JS_TRACE_LOGGING
@@ -1316,36 +1317,48 @@ bool BaselineCodeGen<Handler>::emitInter
   }
 
   masm.bind(&done);
   return true;
 }
 
 template <>
 bool BaselineCompilerCodeGen::emitWarmUpCounterIncrement() {
-  // Emit no warm-up counter increments or bailouts if Ion is not
-  // enabled, or if the script will never be Ion-compileable
-
+  frame.assertSyncedStack();
+
+  // Record native code offset for OSR from Baseline Interpreter into Baseline
+  // JIT code. This is right before the warm-up check in the Baseline JIT code,
+  // to make sure we can immediately enter Ion if the script is warm enough or
+  // if --ion-eager is used.
+  JSScript* script = handler.script();
+  jsbytecode* pc = handler.pc();
+  if (JSOp(*pc) == JSOP_LOOPENTRY) {
+    uint32_t pcOffset = script->pcToOffset(pc);
+    uint32_t nativeOffset = masm.currentOffset();
+    if (!handler.osrEntries().emplaceBack(pcOffset, nativeOffset)) {
+      ReportOutOfMemory(cx);
+      return false;
+    }
+  }
+
+  // Emit no warm-up counter increments if Ion is not enabled or if the script
+  // will never be Ion-compileable.
   if (!handler.maybeIonCompileable()) {
     return true;
   }
 
-  frame.assertSyncedStack();
-
   Register scriptReg = R2.scratchReg();
   Register countReg = R0.scratchReg();
   Address warmUpCounterAddr(scriptReg, JSScript::offsetOfWarmUpCounter());
 
-  JSScript* script = handler.script();
   masm.movePtr(ImmGCPtr(script), scriptReg);
   masm.load32(warmUpCounterAddr, countReg);
   masm.add32(Imm32(1), countReg);
   masm.store32(countReg, warmUpCounterAddr);
 
-  jsbytecode* pc = handler.pc();
   if (JSOp(*pc) == JSOP_LOOPENTRY) {
     // If this is a loop inside a catch or finally block, increment the warmup
     // counter but don't attempt OSR (Ion only compiles the try block).
     if (handler.analysis().info(pc).loopEntryInCatchOrFinally) {
       return true;
     }
 
     if (!LoopEntryCanIonOsr(pc)) {
--- a/js/src/jit/BaselineCodeGen.h
+++ b/js/src/jit/BaselineCodeGen.h
@@ -530,16 +530,22 @@ class BaselineCompilerHandler {
   CompilerFrameInfo frame_;
   TempAllocator& alloc_;
   BytecodeAnalysis analysis_;
 #ifdef DEBUG
   const MacroAssembler& masm_;
 #endif
   FixedList<Label> labels_;
   RetAddrEntryVector retAddrEntries_;
+
+  // Native code offsets for OSR at JSOP_LOOPENTRY ops.
+  using OSREntryVector =
+      Vector<BaselineScript::OSREntry, 16, SystemAllocPolicy>;
+  OSREntryVector osrEntries_;
+
   JSScript* script_;
   jsbytecode* pc_;
 
   // Index of the current ICEntry in the script's JitScript.
   uint32_t icEntryIndex_;
 
   bool compileDebugInstrumentation_;
   bool ionCompileable_;
@@ -589,16 +595,17 @@ class BaselineCompilerHandler {
   bool maybeIonCompileable() const { return ionCompileable_; }
 
   uint32_t icEntryIndex() const { return icEntryIndex_; }
   void moveToNextICEntry() { icEntryIndex_++; }
 
   BytecodeAnalysis& analysis() { return analysis_; }
 
   RetAddrEntryVector& retAddrEntries() { return retAddrEntries_; }
+  OSREntryVector& osrEntries() { return osrEntries_; }
 
   MOZ_MUST_USE bool recordCallRetAddr(JSContext* cx, RetAddrEntry::Kind kind,
                                       uint32_t retOffset);
 
   // If a script has more |nslots| than this the stack check must account
   // for these slots explicitly.
   bool mustIncludeSlotsInStackCheck() const {
     static constexpr size_t NumSlotsLimit = 128;
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -413,59 +413,57 @@ bool jit::BaselineCompileFromBaselineInt
 
     case Method_CantCompile:
     case Method_Skipped:
       *res = nullptr;
       return true;
 
     case Method_Compiled: {
       if (*pc == JSOP_LOOPENTRY) {
-        PCMappingSlotInfo slotInfo;
         BaselineScript* baselineScript = script->baselineScript();
-        *res = baselineScript->nativeCodeForPC(script, pc, &slotInfo);
-        MOZ_ASSERT(slotInfo.isStackSynced());
-        if (frame->isDebuggee()) {
-          // Skip the debug trap emitted by emitInterpreterLoop because the
-          // Baseline Interpreter already handled it for the current op.
-          MOZ_RELEASE_ASSERT(baselineScript->hasDebugInstrumentation());
-          *res += MacroAssembler::ToggledCallSize(*res);
-        }
+        uint32_t pcOffset = script->pcToOffset(pc);
+        *res = baselineScript->nativeCodeForOSREntry(pcOffset);
       } else {
         *res = script->baselineScript()->warmUpCheckPrologueAddr();
       }
       frame->prepareForBaselineInterpreterToJitOSR();
       return true;
     }
   }
 
   MOZ_CRASH("Unexpected status");
 }
 
-BaselineScript* BaselineScript::New(
-    JSScript* jsscript, uint32_t warmUpCheckPrologueOffset,
-    uint32_t profilerEnterToggleOffset, uint32_t profilerExitToggleOffset,
-    size_t retAddrEntries, size_t pcMappingIndexEntries, size_t pcMappingSize,
-    size_t resumeEntries, size_t traceLoggerToggleOffsetEntries) {
+BaselineScript* BaselineScript::New(JSScript* jsscript,
+                                    uint32_t warmUpCheckPrologueOffset,
+                                    uint32_t profilerEnterToggleOffset,
+                                    uint32_t profilerExitToggleOffset,
+                                    size_t retAddrEntries, size_t osrEntries,
+                                    size_t pcMappingIndexEntries,
+                                    size_t pcMappingSize, size_t resumeEntries,
+                                    size_t traceLoggerToggleOffsetEntries) {
   static const unsigned DataAlignment = sizeof(uintptr_t);
 
   size_t retAddrEntriesSize = retAddrEntries * sizeof(RetAddrEntry);
+  size_t osrEntriesSize = osrEntries * sizeof(BaselineScript::OSREntry);
   size_t pcMappingIndexEntriesSize =
       pcMappingIndexEntries * sizeof(PCMappingIndexEntry);
   size_t resumeEntriesSize = resumeEntries * sizeof(uintptr_t);
   size_t tlEntriesSize = traceLoggerToggleOffsetEntries * sizeof(uint32_t);
 
   size_t paddedRetAddrEntriesSize =
       AlignBytes(retAddrEntriesSize, DataAlignment);
+  size_t paddedOSREntriesSize = AlignBytes(osrEntriesSize, DataAlignment);
   size_t paddedPCMappingIndexEntriesSize =
       AlignBytes(pcMappingIndexEntriesSize, DataAlignment);
   size_t paddedPCMappingSize = AlignBytes(pcMappingSize, DataAlignment);
   size_t paddedResumeEntriesSize = AlignBytes(resumeEntriesSize, DataAlignment);
   size_t paddedTLEntriesSize = AlignBytes(tlEntriesSize, DataAlignment);
 
-  size_t allocBytes = paddedRetAddrEntriesSize +
+  size_t allocBytes = paddedRetAddrEntriesSize + paddedOSREntriesSize +
                       paddedPCMappingIndexEntriesSize + paddedPCMappingSize +
                       paddedResumeEntriesSize + paddedTLEntriesSize;
 
   BaselineScript* script =
       jsscript->zone()->pod_malloc_with_extra<BaselineScript, uint8_t>(
           allocBytes);
   if (!script) {
     return nullptr;
@@ -476,16 +474,20 @@ BaselineScript* BaselineScript::New(
 
   size_t offsetCursor = sizeof(BaselineScript);
   MOZ_ASSERT(offsetCursor == AlignBytes(sizeof(BaselineScript), DataAlignment));
 
   script->retAddrEntriesOffset_ = offsetCursor;
   script->retAddrEntries_ = retAddrEntries;
   offsetCursor += paddedRetAddrEntriesSize;
 
+  script->osrEntriesOffset_ = offsetCursor;
+  script->osrEntries_ = osrEntries;
+  offsetCursor += paddedOSREntriesSize;
+
   script->pcMappingIndexOffset_ = offsetCursor;
   script->pcMappingIndexEntries_ = pcMappingIndexEntries;
   offsetCursor += paddedPCMappingIndexEntriesSize;
 
   script->pcMappingOffset_ = offsetCursor;
   script->pcMappingSize_ = pcMappingSize;
   offsetCursor += paddedPCMappingSize;
 
@@ -570,21 +572,22 @@ const RetAddrEntry& BaselineScript::retA
           },
           &loc);
 
   MOZ_ASSERT(found);
   MOZ_ASSERT(entries[loc].returnOffset().offset() == returnOffset.offset());
   return entries[loc];
 }
 
-static bool ComputeBinarySearchMid(mozilla::Span<RetAddrEntry> entries,
+template <typename Entry>
+static bool ComputeBinarySearchMid(mozilla::Span<Entry> entries,
                                    uint32_t pcOffset, size_t* loc) {
   return BinarySearchIf(
       entries.data(), 0, entries.size(),
-      [pcOffset](const RetAddrEntry& entry) {
+      [pcOffset](const Entry& entry) {
         uint32_t entryOffset = entry.pcOffset();
         if (pcOffset < entryOffset) {
           return -1;
         }
         if (entryOffset < pcOffset) {
           return 1;
         }
         return 0;
@@ -661,16 +664,27 @@ const RetAddrEntry& BaselineScript::prol
 const RetAddrEntry& BaselineScript::retAddrEntryFromReturnAddress(
     uint8_t* returnAddr) {
   MOZ_ASSERT(returnAddr > method_->raw());
   MOZ_ASSERT(returnAddr < method_->raw() + method_->instructionsSize());
   CodeOffset offset(returnAddr - method_->raw());
   return retAddrEntryFromReturnOffset(offset);
 }
 
+uint8_t* BaselineScript::nativeCodeForOSREntry(uint32_t pcOffset) {
+  mozilla::Span<OSREntry> entries = osrEntries();
+  size_t mid;
+  if (!ComputeBinarySearchMid(entries, pcOffset, &mid)) {
+    return nullptr;
+  }
+
+  uint32_t nativeOffset = entries[mid].nativeOffset();
+  return method_->raw() + nativeOffset;
+}
+
 void BaselineScript::computeResumeNativeOffsets(JSScript* script) {
   // Translate pcOffset to BaselineScript native address. This may return
   // nullptr if compiler decided code was unreachable.
   auto computeNative = [this, script](uint32_t pcOffset) {
     PCMappingSlotInfo slotInfo;
     uint8_t* nativeCode =
         maybeNativeCodeForPC(script, script->offsetToPC(pcOffset), &slotInfo);
     MOZ_ASSERT(slotInfo.isStackSynced());
@@ -683,16 +697,20 @@ void BaselineScript::computeResumeNative
   std::transform(pcOffsets.begin(), pcOffsets.end(), nativeOffsets,
                  computeNative);
 }
 
 void BaselineScript::copyRetAddrEntries(const RetAddrEntry* entries) {
   std::copy_n(entries, retAddrEntries().size(), retAddrEntries().data());
 }
 
+void BaselineScript::copyOSREntries(const OSREntry* entries) {
+  std::copy_n(entries, osrEntries().size(), osrEntries().data());
+}
+
 void BaselineScript::copyPCMappingEntries(const CompactBufferWriter& entries) {
   MOZ_ASSERT(entries.length() > 0);
   MOZ_ASSERT(entries.length() == pcMappingSize_);
 
   memcpy(pcMappingData(), entries.buffer(), entries.length());
 }
 
 void BaselineScript::copyPCMappingIndexEntries(
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -221,16 +221,19 @@ struct BaselineScript final {
 #endif
 
  private:
   void trace(JSTracer* trc);
 
   uint32_t retAddrEntriesOffset_ = 0;
   uint32_t retAddrEntries_ = 0;
 
+  uint32_t osrEntriesOffset_ = 0;
+  uint32_t osrEntries_ = 0;
+
   uint32_t pcMappingIndexOffset_ = 0;
   uint32_t pcMappingIndexEntries_ = 0;
 
   uint32_t pcMappingOffset_ = 0;
   uint32_t pcMappingSize_ = 0;
 
   // We store the native code address corresponding to each bytecode offset in
   // the script's resumeOffsets list.
@@ -249,16 +252,29 @@ struct BaselineScript final {
     // Flag set when compiled for use with Debugger. Handles various
     // Debugger hooks and compiles toggled calls for traps.
     HAS_DEBUG_INSTRUMENTATION = 1 << 0,
 
     // Flag is set if this script has profiling instrumentation turned on.
     PROFILER_INSTRUMENTATION_ON = 1 << 1,
   };
 
+  // Native code offset for OSR from Baseline Interpreter into Baseline JIT at
+  // JSOP_LOOPENTRY ops.
+  class OSREntry {
+    uint32_t pcOffset_;
+    uint32_t nativeOffset_;
+
+   public:
+    OSREntry(uint32_t pcOffset, uint32_t nativeOffset)
+        : pcOffset_(pcOffset), nativeOffset_(nativeOffset) {}
+    uint32_t pcOffset() const { return pcOffset_; }
+    uint32_t nativeOffset() const { return nativeOffset_; }
+  };
+
  private:
   uint8_t flags_ = 0;
 
   // An ion compilation that is ready, but isn't linked yet.
   IonBuilder* pendingBuilder_ = nullptr;
 
   // Use BaselineScript::New to create new instances. It will properly
   // allocate trailing objects.
@@ -276,16 +292,20 @@ struct BaselineScript final {
     uintptr_t elem = base + offset;
     return reinterpret_cast<T*>(elem);
   }
 
   mozilla::Span<RetAddrEntry> retAddrEntries() const {
     return mozilla::MakeSpan(
         offsetToPointer<RetAddrEntry>(retAddrEntriesOffset_), retAddrEntries_);
   }
+  mozilla::Span<OSREntry> osrEntries() const {
+    return mozilla::MakeSpan(offsetToPointer<OSREntry>(osrEntriesOffset_),
+                             osrEntries_);
+  }
 
 #ifdef JS_TRACE_LOGGING
   mozilla::Span<uint32_t> traceLoggerToggleOffsets() const {
     MOZ_ASSERT(traceLoggerToggleOffsetsOffset_);
     return mozilla::MakeSpan(
         offsetToPointer<uint32_t>(traceLoggerToggleOffsetsOffset_),
         numTraceLoggerToggleOffsets_);
   }
@@ -293,21 +313,24 @@ struct BaselineScript final {
 
   // Note: this doesn't return a Span<> because BaselineScript does not store
   // the number of entries.
   uint8_t** resumeEntryList() const {
     return offsetToPointer<uint8_t*>(resumeEntriesOffset_);
   }
 
  public:
-  static BaselineScript* New(
-      JSScript* jsscript, uint32_t warmUpCheckPrologueOffset,
-      uint32_t profilerEnterToggleOffset, uint32_t profilerExitToggleOffset,
-      size_t retAddrEntries, size_t pcMappingIndexEntries, size_t pcMappingSize,
-      size_t resumeEntries, size_t traceLoggerToggleOffsetEntries);
+  static BaselineScript* New(JSScript* jsscript,
+                             uint32_t warmUpCheckPrologueOffset,
+                             uint32_t profilerEnterToggleOffset,
+                             uint32_t profilerExitToggleOffset,
+                             size_t retAddrEntries, size_t osrEntries,
+                             size_t pcMappingIndexEntries, size_t pcMappingSize,
+                             size_t resumeEntries,
+                             size_t traceLoggerToggleOffsetEntries);
 
   static void Trace(JSTracer* trc, BaselineScript* script);
   static void Destroy(FreeOp* fop, BaselineScript* script);
 
   static inline size_t offsetOfMethod() {
     return offsetof(BaselineScript, method_);
   }
 
@@ -347,17 +370,20 @@ struct BaselineScript final {
   uint8_t* returnAddressForEntry(const RetAddrEntry& ent);
 
   const RetAddrEntry& retAddrEntryFromPCOffset(uint32_t pcOffset,
                                                RetAddrEntry::Kind kind);
   const RetAddrEntry& prologueRetAddrEntry(RetAddrEntry::Kind kind);
   const RetAddrEntry& retAddrEntryFromReturnOffset(CodeOffset returnOffset);
   const RetAddrEntry& retAddrEntryFromReturnAddress(uint8_t* returnAddr);
 
+  uint8_t* nativeCodeForOSREntry(uint32_t pcOffset);
+
   void copyRetAddrEntries(const RetAddrEntry* entries);
+  void copyOSREntries(const OSREntry* entries);
 
   // Copy resumeOffsets list from |script| and convert the pcOffsets
   // to native addresses in the Baseline code.
   void computeResumeNativeOffsets(JSScript* script);
 
   PCMappingIndexEntry& pcMappingIndexEntry(size_t index);
   CompactBufferReader pcMappingReader(size_t indexEntry);