js/src/jit/BaselineDebugModeOSR.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "jit/BaselineDebugModeOSR.h"
     9 #include "mozilla/DebugOnly.h"
    11 #include "jit/IonLinker.h"
    12 #include "jit/PerfSpewer.h"
    14 #include "jit/IonFrames-inl.h"
    15 #include "vm/Stack-inl.h"
    17 using namespace mozilla;
    18 using namespace js;
    19 using namespace js::jit;
    21 struct DebugModeOSREntry
    22 {
    23     JSScript *script;
    24     BaselineScript *oldBaselineScript;
    25     BaselineDebugModeOSRInfo *recompInfo;
    26     uint32_t pcOffset;
    27     ICEntry::Kind frameKind;
    29     // Used for sanity asserts in debug builds.
    30     DebugOnly<ICStub *> stub;
    32     DebugModeOSREntry(JSScript *script)
    33       : script(script),
    34         oldBaselineScript(script->baselineScript()),
    35         recompInfo(nullptr),
    36         pcOffset(uint32_t(-1)),
    37         frameKind(ICEntry::Kind_NonOp),
    38         stub(nullptr)
    39     { }
    41     DebugModeOSREntry(JSScript *script, const ICEntry &icEntry)
    42       : script(script),
    43         oldBaselineScript(script->baselineScript()),
    44         recompInfo(nullptr),
    45         pcOffset(icEntry.pcOffset()),
    46         frameKind(icEntry.kind()),
    47         stub(nullptr)
    48     {
    49 #ifdef DEBUG
    50         MOZ_ASSERT(pcOffset == icEntry.pcOffset());
    51         MOZ_ASSERT(frameKind == icEntry.kind());
    53         // Assert that if we have a NonOp ICEntry, that there are no unsynced
    54         // slots, since such a recompile could have only been triggered from
    55         // either an interrupt check or a debug trap handler.
    56         //
    57         // If triggered from an interrupt check, the stack should be fully
    58         // synced.
    59         //
    60         // If triggered from a debug trap handler, we must be recompiling for
    61         // toggling debug mode on->off, in which case the old baseline script
    62         // should have fully synced stack at every bytecode.
    63         if (frameKind == ICEntry::Kind_NonOp) {
    64             PCMappingSlotInfo slotInfo;
    65             jsbytecode *pc = script->offsetToPC(pcOffset);
    66             oldBaselineScript->nativeCodeForPC(script, pc, &slotInfo);
    67             MOZ_ASSERT(slotInfo.numUnsynced() == 0);
    68         }
    69 #endif
    70     }
    72     DebugModeOSREntry(DebugModeOSREntry &&other)
    73       : script(other.script),
    74         oldBaselineScript(other.oldBaselineScript),
    75         recompInfo(other.recompInfo ? other.takeRecompInfo() : nullptr),
    76         pcOffset(other.pcOffset),
    77         frameKind(other.frameKind),
    78         stub(other.stub)
    79     { }
    81     ~DebugModeOSREntry() {
    82         // Note that this is nulled out when the recompInfo is taken by the
    83         // frame. The frame then has the responsibility of freeing the
    84         // recompInfo.
    85         js_delete(recompInfo);
    86     }
    88     bool needsRecompileInfo() const {
    89         return (frameKind == ICEntry::Kind_CallVM ||
    90                 frameKind == ICEntry::Kind_DebugTrap ||
    91                 frameKind == ICEntry::Kind_DebugPrologue ||
    92                 frameKind == ICEntry::Kind_DebugEpilogue);
    93     }
    95     BaselineDebugModeOSRInfo *takeRecompInfo() {
    96         MOZ_ASSERT(recompInfo);
    97         BaselineDebugModeOSRInfo *tmp = recompInfo;
    98         recompInfo = nullptr;
    99         return tmp;
   100     }
   102     bool allocateRecompileInfo(JSContext *cx) {
   103         MOZ_ASSERT(needsRecompileInfo());
   105         // If we are returning to a frame which needs a continuation fixer,
   106         // allocate the recompile info up front so that the patching function
   107         // is infallible.
   108         jsbytecode *pc = script->offsetToPC(pcOffset);
   110         // XXX: Work around compiler error disallowing using bitfields
   111         // with the template magic of new_.
   112         ICEntry::Kind kind = frameKind;
   113         recompInfo = cx->new_<BaselineDebugModeOSRInfo>(pc, kind);
   114         return !!recompInfo;
   115     }
   116 };
   118 typedef js::Vector<DebugModeOSREntry> DebugModeOSREntryVector;
   120 static bool
   121 CollectOnStackScripts(JSContext *cx, const JitActivationIterator &activation,
   122                       DebugModeOSREntryVector &entries)
   123 {
   124     DebugOnly<ICStub *> prevFrameStubPtr = nullptr;
   125     bool needsRecompileHandler = false;
   126     for (JitFrameIterator iter(activation); !iter.done(); ++iter) {
   127         switch (iter.type()) {
   128           case JitFrame_BaselineJS: {
   129             JSScript *script = iter.script();
   130             uint8_t *retAddr = iter.returnAddressToFp();
   131             ICEntry &entry = script->baselineScript()->icEntryFromReturnAddress(retAddr);
   133             if (!entries.append(DebugModeOSREntry(script, entry)))
   134                 return false;
   136             if (entries.back().needsRecompileInfo()) {
   137                 if (!entries.back().allocateRecompileInfo(cx))
   138                     return false;
   140                 needsRecompileHandler |= true;
   141             }
   143             entries.back().stub = prevFrameStubPtr;
   144             prevFrameStubPtr = nullptr;
   145             break;
   146           }
   148           case JitFrame_BaselineStub:
   149             prevFrameStubPtr =
   150                 reinterpret_cast<IonBaselineStubFrameLayout *>(iter.fp())->maybeStubPtr();
   151             break;
   153           case JitFrame_IonJS: {
   154             JSScript *script = iter.script();
   155             if (!entries.append(DebugModeOSREntry(script)))
   156                 return false;
   157             for (InlineFrameIterator inlineIter(cx, &iter); inlineIter.more(); ++inlineIter) {
   158                 if (!entries.append(DebugModeOSREntry(inlineIter.script())))
   159                     return false;
   160             }
   161             break;
   162           }
   164           default:;
   165         }
   166     }
   168     // Initialize the on-stack recompile handler, which may fail, so that
   169     // patching the stack is infallible.
   170     if (needsRecompileHandler) {
   171         JitRuntime *rt = cx->runtime()->jitRuntime();
   172         if (!rt->getBaselineDebugModeOSRHandlerAddress(cx, true))
   173             return false;
   174     }
   176     return true;
   177 }
   179 static inline uint8_t *
   180 GetStubReturnFromStubAddress(JSContext *cx, jsbytecode *pc)
   181 {
   182     JitCompartment *comp = cx->compartment()->jitCompartment();
   183     void *addr;
   184     if (IsGetPropPC(pc)) {
   185         addr = comp->baselineGetPropReturnFromStubAddr();
   186     } else if (IsSetPropPC(pc)) {
   187         addr = comp->baselineSetPropReturnFromStubAddr();
   188     } else {
   189         JS_ASSERT(IsCallPC(pc));
   190         addr = comp->baselineCallReturnFromStubAddr();
   191     }
   192     return reinterpret_cast<uint8_t *>(addr);
   193 }
   195 static const char *
   196 ICEntryKindToString(ICEntry::Kind kind)
   197 {
   198     switch (kind) {
   199       case ICEntry::Kind_Op:
   200         return "IC";
   201       case ICEntry::Kind_NonOp:
   202         return "non-op IC";
   203       case ICEntry::Kind_CallVM:
   204         return "callVM";
   205       case ICEntry::Kind_DebugTrap:
   206         return "debug trap";
   207       case ICEntry::Kind_DebugPrologue:
   208         return "debug prologue";
   209       case ICEntry::Kind_DebugEpilogue:
   210         return "debug epilogue";
   211       default:
   212         MOZ_ASSUME_UNREACHABLE("bad ICEntry kind");
   213     }
   214 }
   216 static void
   217 SpewPatchBaselineFrame(uint8_t *oldReturnAddress, uint8_t *newReturnAddress,
   218                        JSScript *script, ICEntry::Kind frameKind, jsbytecode *pc)
   219 {
   220     IonSpew(IonSpew_BaselineDebugModeOSR,
   221             "Patch return %#016llx -> %#016llx to BaselineJS (%s:%d) from %s at %s",
   222             uintptr_t(oldReturnAddress), uintptr_t(newReturnAddress),
   223             script->filename(), script->lineno(),
   224             ICEntryKindToString(frameKind), js_CodeName[(JSOp)*pc]);
   225 }
   227 static void
   228 SpewPatchStubFrame(uint8_t *oldReturnAddress, uint8_t *newReturnAddress,
   229                    ICStub *oldStub, ICStub *newStub)
   230 {
   231     IonSpew(IonSpew_BaselineDebugModeOSR,
   232             "Patch return %#016llx -> %#016llx",
   233             uintptr_t(oldReturnAddress), uintptr_t(newReturnAddress));
   234     IonSpew(IonSpew_BaselineDebugModeOSR,
   235             "Patch   stub %#016llx -> %#016llx to BaselineStub",
   236             uintptr_t(oldStub), uintptr_t(newStub));
   237 }
   239 static void
   240 PatchBaselineFramesForDebugMode(JSContext *cx, const JitActivationIterator &activation,
   241                                 DebugModeOSREntryVector &entries, size_t *start)
   242 {
   243     //
   244     // Recompile Patching Overview
   245     //
   246     // When toggling debug mode with live baseline scripts on the stack, we
   247     // could have entered the VM via the following ways from the baseline
   248     // script.
   249     //
   250     // Off to On:
   251     //  A. From a "can call" stub.
   252     //  B. From a VM call (interrupt handler, debugger statement handler).
   253     //
   254     // On to Off:
   255     //  - All the ways above.
   256     //  C. From the debug trap handler.
   257     //  D. From the debug prologue.
   258     //  E. From the debug epilogue.
   259     //
   260     // In general, we patch the return address from the VM call to return to a
   261     // "continuation fixer" to fix up machine state (registers and stack
   262     // state). Specifics on what need to be done are documented below.
   263     //
   265     IonCommonFrameLayout *prev = nullptr;
   266     size_t entryIndex = *start;
   267     DebugOnly<bool> expectedDebugMode = cx->compartment()->debugMode();
   269     for (JitFrameIterator iter(activation); !iter.done(); ++iter) {
   270         switch (iter.type()) {
   271           case JitFrame_BaselineJS: {
   272             JSScript *script = entries[entryIndex].script;
   273             uint32_t pcOffset = entries[entryIndex].pcOffset;
   274             jsbytecode *pc = script->offsetToPC(pcOffset);
   276             MOZ_ASSERT(script == iter.script());
   277             MOZ_ASSERT(pcOffset < script->length());
   278             MOZ_ASSERT(script->baselineScript()->debugMode() == expectedDebugMode);
   280             BaselineScript *bl = script->baselineScript();
   281             ICEntry::Kind kind = entries[entryIndex].frameKind;
   283             if (kind == ICEntry::Kind_Op) {
   284                 // Case A above.
   285                 //
   286                 // Patching this case needs to patch both the stub frame and
   287                 // the baseline frame. The stub frame is patched below. For
   288                 // the baseline frame here, we resume right after the IC
   289                 // returns.
   290                 //
   291                 // Since we're using the IC-specific k-fixer, we can resume
   292                 // directly to the IC resume address.
   293                 uint8_t *retAddr = bl->returnAddressForIC(bl->icEntryFromPCOffset(pcOffset));
   294                 SpewPatchBaselineFrame(prev->returnAddress(), retAddr, script, kind, pc);
   295                 prev->setReturnAddress(retAddr);
   296                 entryIndex++;
   297                 break;
   298             }
   300             bool popFrameReg;
   302             // The RecompileInfo must already be allocated so that this
   303             // function may be infallible.
   304             BaselineDebugModeOSRInfo *recompInfo = entries[entryIndex].takeRecompInfo();
   306             switch (kind) {
   307               case ICEntry::Kind_CallVM:
   308                 // Case B above.
   309                 //
   310                 // Patching returns from an interrupt handler or the debugger
   311                 // statement handler is similar in that we can resume at the
   312                 // next op.
   313                 pc += GetBytecodeLength(pc);
   314                 recompInfo->resumeAddr = bl->nativeCodeForPC(script, pc, &recompInfo->slotInfo);
   315                 popFrameReg = true;
   316                 break;
   318               case ICEntry::Kind_DebugTrap:
   319                 // Case C above.
   320                 //
   321                 // Debug traps are emitted before each op, so we resume at the
   322                 // same op. Calling debug trap handlers is done via a toggled
   323                 // call to a thunk (DebugTrapHandler) that takes care tearing
   324                 // down its own stub frame so we don't need to worry about
   325                 // popping the frame reg.
   326                 recompInfo->resumeAddr = bl->nativeCodeForPC(script, pc, &recompInfo->slotInfo);
   327                 popFrameReg = false;
   328                 break;
   330               case ICEntry::Kind_DebugPrologue:
   331                 // Case D above.
   332                 //
   333                 // We patch a jump directly to the right place in the prologue
   334                 // after popping the frame reg and checking for forced return.
   335                 recompInfo->resumeAddr = bl->postDebugPrologueAddr();
   336                 popFrameReg = true;
   337                 break;
   339               default:
   340                 // Case E above.
   341                 //
   342                 // We patch a jump directly to the epilogue after popping the
   343                 // frame reg and checking for forced return.
   344                 MOZ_ASSERT(kind == ICEntry::Kind_DebugEpilogue);
   345                 recompInfo->resumeAddr = bl->epilogueEntryAddr();
   346                 popFrameReg = true;
   347                 break;
   348             }
   350             SpewPatchBaselineFrame(prev->returnAddress(), recompInfo->resumeAddr,
   351                                    script, kind, recompInfo->pc);
   353             // The recompile handler must already be created so that this
   354             // function may be infallible.
   355             JitRuntime *rt = cx->runtime()->jitRuntime();
   356             void *handlerAddr = rt->getBaselineDebugModeOSRHandlerAddress(cx, popFrameReg);
   357             MOZ_ASSERT(handlerAddr);
   359             prev->setReturnAddress(reinterpret_cast<uint8_t *>(handlerAddr));
   360             iter.baselineFrame()->setDebugModeOSRInfo(recompInfo);
   362             entryIndex++;
   363             break;
   364           }
   366           case JitFrame_BaselineStub: {
   367             JSScript *script = entries[entryIndex].script;
   368             IonBaselineStubFrameLayout *layout =
   369                 reinterpret_cast<IonBaselineStubFrameLayout *>(iter.fp());
   370             MOZ_ASSERT(script->baselineScript()->debugMode() == expectedDebugMode);
   371             MOZ_ASSERT(layout->maybeStubPtr() == entries[entryIndex].stub);
   373             // Patch baseline stub frames for case A above.
   374             //
   375             // We need to patch the stub frame return address to go to the
   376             // k-fixer that is at the end of fallback stubs of all such
   377             // can-call ICs. These k-fixers share code with bailout-from-Ion
   378             // fixers, but in this case we are returning from VM and not
   379             // Ion. See e.g., JitCompartment::baselineCallReturnFromStubAddr()
   380             //
   381             // Subtlety here: the debug trap handler of case C above pushes a
   382             // stub frame with a null stub pointer. This handler will exist
   383             // across recompiling the script, so we don't patch anything for
   384             // such stub frames. We will return to that handler, which takes
   385             // care of cleaning up the stub frame.
   386             //
   387             // Note that for stub pointers that are already on the C stack
   388             // (i.e. fallback calls), we need to check for recompilation using
   389             // DebugModeOSRVolatileStub.
   390             if (layout->maybeStubPtr()) {
   391                 MOZ_ASSERT(layout->maybeStubPtr() == entries[entryIndex].stub);
   392                 uint32_t pcOffset = entries[entryIndex].pcOffset;
   393                 uint8_t *retAddr = GetStubReturnFromStubAddress(cx, script->offsetToPC(pcOffset));
   395                 // Get the fallback stub for the IC in the recompiled
   396                 // script. The fallback stub is guaranteed to exist.
   397                 ICEntry &entry = script->baselineScript()->icEntryFromPCOffset(pcOffset);
   398                 ICStub *newStub = entry.fallbackStub();
   399                 SpewPatchStubFrame(prev->returnAddress(), retAddr, layout->maybeStubPtr(), newStub);
   400                 prev->setReturnAddress(retAddr);
   401                 layout->setStubPtr(newStub);
   402             }
   404             break;
   405           }
   407           case JitFrame_IonJS:
   408             // Nothing to patch.
   409             entryIndex++;
   410             for (InlineFrameIterator inlineIter(cx, &iter); inlineIter.more(); ++inlineIter)
   411                 entryIndex++;
   412             break;
   414           default:;
   415         }
   417         prev = iter.current();
   418     }
   420     *start = entryIndex;
   421 }
   423 static bool
   424 RecompileBaselineScriptForDebugMode(JSContext *cx, JSScript *script)
   425 {
   426     BaselineScript *oldBaselineScript = script->baselineScript();
   428     // If a script is on the stack multiple times, it may have already
   429     // been recompiled.
   430     bool expectedDebugMode = cx->compartment()->debugMode();
   431     if (oldBaselineScript->debugMode() == expectedDebugMode)
   432         return true;
   434     IonSpew(IonSpew_BaselineDebugModeOSR, "Recompiling (%s:%d) for debug mode %s",
   435             script->filename(), script->lineno(), expectedDebugMode ? "ON" : "OFF");
   437     if (script->hasIonScript())
   438         Invalidate(cx, script, /* resetUses = */ false);
   440     script->setBaselineScript(cx, nullptr);
   442     MethodStatus status = BaselineCompile(cx, script);
   443     if (status != Method_Compiled) {
   444         // We will only fail to recompile for debug mode due to OOM. Restore
   445         // the old baseline script in case something doesn't properly
   446         // propagate OOM.
   447         MOZ_ASSERT(status == Method_Error);
   448         script->setBaselineScript(cx, oldBaselineScript);
   449         return false;
   450     }
   452     // Don't destroy the old baseline script yet, since if we fail any of the
   453     // recompiles we need to rollback all the old baseline scripts.
   454     MOZ_ASSERT(script->baselineScript()->debugMode() == expectedDebugMode);
   455     return true;
   456 }
   458 static void
   459 UndoRecompileBaselineScriptsForDebugMode(JSContext *cx,
   460                                          const DebugModeOSREntryVector &entries)
   461 {
   462     // In case of failure, roll back the entire set of active scripts so that
   463     // we don't have to patch return addresses on the stack.
   464     for (size_t i = 0; i < entries.length(); i++) {
   465         JSScript *script = entries[i].script;
   466         BaselineScript *baselineScript = script->baselineScript();
   467         if (baselineScript != entries[i].oldBaselineScript) {
   468             script->setBaselineScript(cx, entries[i].oldBaselineScript);
   469             BaselineScript::Destroy(cx->runtime()->defaultFreeOp(), baselineScript);
   470         }
   471     }
   472 }
   474 bool
   475 jit::RecompileOnStackBaselineScriptsForDebugMode(JSContext *cx, JSCompartment *comp)
   476 {
   477     AutoCompartment ac(cx, comp);
   479     // First recompile the active scripts on the stack and patch the live
   480     // frames.
   481     Vector<DebugModeOSREntry> entries(cx);
   483     for (JitActivationIterator iter(cx->runtime()); !iter.done(); ++iter) {
   484         if (iter.activation()->compartment() == comp) {
   485             if (!CollectOnStackScripts(cx, iter, entries))
   486                 return false;
   487         }
   488     }
   490 #ifdef JSGC_GENERATIONAL
   491     // Scripts can entrain nursery things. See note in js::ReleaseAllJITCode.
   492     if (!entries.empty())
   493         MinorGC(cx->runtime(), JS::gcreason::EVICT_NURSERY);
   494 #endif
   496     // Try to recompile all the scripts. If we encounter an error, we need to
   497     // roll back as if none of the compilations happened, so that we don't
   498     // crash.
   499     for (size_t i = 0; i < entries.length(); i++) {
   500         JSScript *script = entries[i].script;
   501         if (!RecompileBaselineScriptForDebugMode(cx, script)) {
   502             UndoRecompileBaselineScriptsForDebugMode(cx, entries);
   503             return false;
   504         }
   505     }
   507     // If all recompiles succeeded, destroy the old baseline scripts and patch
   508     // the live frames.
   509     //
   510     // After this point the function must be infallible.
   512     for (size_t i = 0; i < entries.length(); i++)
   513         BaselineScript::Destroy(cx->runtime()->defaultFreeOp(), entries[i].oldBaselineScript);
   515     size_t processed = 0;
   516     for (JitActivationIterator iter(cx->runtime()); !iter.done(); ++iter) {
   517         if (iter.activation()->compartment() == comp)
   518             PatchBaselineFramesForDebugMode(cx, iter, entries, &processed);
   519     }
   520     MOZ_ASSERT(processed == entries.length());
   522     return true;
   523 }
   525 void
   526 BaselineDebugModeOSRInfo::popValueInto(PCMappingSlotInfo::SlotLocation loc, Value *vp)
   527 {
   528     switch (loc) {
   529       case PCMappingSlotInfo::SlotInR0:
   530         valueR0 = vp[stackAdjust];
   531         break;
   532       case PCMappingSlotInfo::SlotInR1:
   533         valueR1 = vp[stackAdjust];
   534         break;
   535       case PCMappingSlotInfo::SlotIgnore:
   536         break;
   537       default:
   538         MOZ_ASSUME_UNREACHABLE("Bad slot location");
   539     }
   541     stackAdjust++;
   542 }
   544 static inline bool
   545 HasForcedReturn(BaselineDebugModeOSRInfo *info, bool rv)
   546 {
   547     ICEntry::Kind kind = info->frameKind;
   549     // The debug epilogue always checks its resumption value, so we don't need
   550     // to check rv.
   551     if (kind == ICEntry::Kind_DebugEpilogue)
   552         return true;
   554     // |rv| is the value in ReturnReg. If true, in the case of the prologue,
   555     // debug trap, and debugger statement handler, it means a forced return.
   556     if (kind == ICEntry::Kind_DebugPrologue ||
   557         (kind == ICEntry::Kind_CallVM && JSOp(*info->pc) == JSOP_DEBUGGER))
   558     {
   559         return rv;
   560     }
   562     // N.B. The debug trap handler handles its own forced return, so no
   563     // need to deal with it here.
   564     return false;
   565 }
   567 static void
   568 SyncBaselineDebugModeOSRInfo(BaselineFrame *frame, Value *vp, bool rv)
   569 {
   570     BaselineDebugModeOSRInfo *info = frame->debugModeOSRInfo();
   571     MOZ_ASSERT(info);
   572     MOZ_ASSERT(frame->script()->baselineScript()->containsCodeAddress(info->resumeAddr));
   574     if (HasForcedReturn(info, rv)) {
   575         // Load the frame's rval and overwrite the resume address to go to the
   576         // epilogue.
   577         MOZ_ASSERT(R0 == JSReturnOperand);
   578         info->valueR0 = frame->returnValue();
   579         info->resumeAddr = frame->script()->baselineScript()->epilogueEntryAddr();
   580         return;
   581     }
   583     // Read stack values and make sure R0 and R1 have the right values.
   584     unsigned numUnsynced = info->slotInfo.numUnsynced();
   585     MOZ_ASSERT(numUnsynced <= 2);
   586     if (numUnsynced > 0)
   587         info->popValueInto(info->slotInfo.topSlotLocation(), vp);
   588     if (numUnsynced > 1)
   589         info->popValueInto(info->slotInfo.nextSlotLocation(), vp);
   591     // Scale stackAdjust.
   592     info->stackAdjust *= sizeof(Value);
   593 }
   595 static void
   596 FinishBaselineDebugModeOSR(BaselineFrame *frame)
   597 {
   598     frame->deleteDebugModeOSRInfo();
   599 }
   601 void
   602 BaselineFrame::deleteDebugModeOSRInfo()
   603 {
   604     js_delete(getDebugModeOSRInfo());
   605     flags_ &= ~HAS_DEBUG_MODE_OSR_INFO;
   606 }
   608 JitCode *
   609 JitRuntime::getBaselineDebugModeOSRHandler(JSContext *cx)
   610 {
   611     if (!baselineDebugModeOSRHandler_) {
   612         AutoLockForExclusiveAccess lock(cx);
   613         AutoCompartment ac(cx, cx->runtime()->atomsCompartment());
   614         uint32_t offset;
   615         if (JitCode *code = generateBaselineDebugModeOSRHandler(cx, &offset)) {
   616             baselineDebugModeOSRHandler_ = code;
   617             baselineDebugModeOSRHandlerNoFrameRegPopAddr_ = code->raw() + offset;
   618         }
   619     }
   621     return baselineDebugModeOSRHandler_;
   622 }
   624 void *
   625 JitRuntime::getBaselineDebugModeOSRHandlerAddress(JSContext *cx, bool popFrameReg)
   626 {
   627     if (!getBaselineDebugModeOSRHandler(cx))
   628         return nullptr;
   629     return (popFrameReg
   630             ? baselineDebugModeOSRHandler_->raw()
   631             : baselineDebugModeOSRHandlerNoFrameRegPopAddr_);
   632 }
   634 JitCode *
   635 JitRuntime::generateBaselineDebugModeOSRHandler(JSContext *cx, uint32_t *noFrameRegPopOffsetOut)
   636 {
   637     MacroAssembler masm(cx);
   639     GeneralRegisterSet regs(GeneralRegisterSet::All());
   640     regs.take(BaselineFrameReg);
   641     regs.take(ReturnReg);
   642     Register temp = regs.takeAny();
   643     Register syncedStackStart = regs.takeAny();
   645     // Pop the frame reg.
   646     masm.pop(BaselineFrameReg);
   648     // Not all patched baseline frames are returning from a situation where
   649     // the frame reg is already fixed up.
   650     CodeOffsetLabel noFrameRegPopOffset = masm.currentOffset();
   652     // Record the stack pointer for syncing.
   653     masm.movePtr(StackPointer, syncedStackStart);
   654     masm.push(BaselineFrameReg);
   656     // Call a stub to fully initialize the info.
   657     masm.setupUnalignedABICall(3, temp);
   658     masm.loadBaselineFramePtr(BaselineFrameReg, temp);
   659     masm.passABIArg(temp);
   660     masm.passABIArg(syncedStackStart);
   661     masm.passABIArg(ReturnReg);
   662     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, SyncBaselineDebugModeOSRInfo));
   664     // Discard stack values depending on how many were unsynced, as we always
   665     // have a fully synced stack in the recompile handler. See assert in
   666     // DebugModeOSREntry constructor.
   667     masm.pop(BaselineFrameReg);
   668     masm.loadPtr(Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfScratchValue()), temp);
   669     masm.addPtr(Address(temp, offsetof(BaselineDebugModeOSRInfo, stackAdjust)), StackPointer);
   671     // Save real return address on the stack temporarily.
   672     masm.pushValue(Address(temp, offsetof(BaselineDebugModeOSRInfo, valueR0)));
   673     masm.pushValue(Address(temp, offsetof(BaselineDebugModeOSRInfo, valueR1)));
   674     masm.push(BaselineFrameReg);
   675     masm.push(Address(temp, offsetof(BaselineDebugModeOSRInfo, resumeAddr)));
   677     // Call a stub to free the allocated info.
   678     masm.setupUnalignedABICall(1, temp);
   679     masm.loadBaselineFramePtr(BaselineFrameReg, temp);
   680     masm.passABIArg(temp);
   681     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, FinishBaselineDebugModeOSR));
   683     // Restore saved values.
   684     GeneralRegisterSet jumpRegs(GeneralRegisterSet::All());
   685     jumpRegs.take(R0);
   686     jumpRegs.take(R1);
   687     jumpRegs.take(BaselineFrameReg);
   688     Register target = jumpRegs.takeAny();
   690     masm.pop(target);
   691     masm.pop(BaselineFrameReg);
   692     masm.popValue(R1);
   693     masm.popValue(R0);
   695     masm.jump(target);
   697     Linker linker(masm);
   698     AutoFlushICache afc("BaselineDebugModeOSRHandler");
   699     JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
   700     if (!code)
   701         return nullptr;
   703     noFrameRegPopOffset.fixup(&masm);
   704     *noFrameRegPopOffsetOut = noFrameRegPopOffset.offset();
   706 #ifdef JS_ION_PERF
   707     writePerfSpewerJitCodeProfile(code, "BaselineDebugModeOSRHandler");
   708 #endif
   710     return code;
   711 }

mercurial