js/src/jit/BaselineDebugModeOSR.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/jit/BaselineDebugModeOSR.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,711 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99:
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "jit/BaselineDebugModeOSR.h"
    1.11 +
    1.12 +#include "mozilla/DebugOnly.h"
    1.13 +
    1.14 +#include "jit/IonLinker.h"
    1.15 +#include "jit/PerfSpewer.h"
    1.16 +
    1.17 +#include "jit/IonFrames-inl.h"
    1.18 +#include "vm/Stack-inl.h"
    1.19 +
    1.20 +using namespace mozilla;
    1.21 +using namespace js;
    1.22 +using namespace js::jit;
    1.23 +
    1.24 +struct DebugModeOSREntry
    1.25 +{
    1.26 +    JSScript *script;
    1.27 +    BaselineScript *oldBaselineScript;
    1.28 +    BaselineDebugModeOSRInfo *recompInfo;
    1.29 +    uint32_t pcOffset;
    1.30 +    ICEntry::Kind frameKind;
    1.31 +
    1.32 +    // Used for sanity asserts in debug builds.
    1.33 +    DebugOnly<ICStub *> stub;
    1.34 +
    1.35 +    DebugModeOSREntry(JSScript *script)
    1.36 +      : script(script),
    1.37 +        oldBaselineScript(script->baselineScript()),
    1.38 +        recompInfo(nullptr),
    1.39 +        pcOffset(uint32_t(-1)),
    1.40 +        frameKind(ICEntry::Kind_NonOp),
    1.41 +        stub(nullptr)
    1.42 +    { }
    1.43 +
    1.44 +    DebugModeOSREntry(JSScript *script, const ICEntry &icEntry)
    1.45 +      : script(script),
    1.46 +        oldBaselineScript(script->baselineScript()),
    1.47 +        recompInfo(nullptr),
    1.48 +        pcOffset(icEntry.pcOffset()),
    1.49 +        frameKind(icEntry.kind()),
    1.50 +        stub(nullptr)
    1.51 +    {
    1.52 +#ifdef DEBUG
    1.53 +        MOZ_ASSERT(pcOffset == icEntry.pcOffset());
    1.54 +        MOZ_ASSERT(frameKind == icEntry.kind());
    1.55 +
    1.56 +        // Assert that if we have a NonOp ICEntry, that there are no unsynced
    1.57 +        // slots, since such a recompile could have only been triggered from
    1.58 +        // either an interrupt check or a debug trap handler.
    1.59 +        //
    1.60 +        // If triggered from an interrupt check, the stack should be fully
    1.61 +        // synced.
    1.62 +        //
    1.63 +        // If triggered from a debug trap handler, we must be recompiling for
    1.64 +        // toggling debug mode on->off, in which case the old baseline script
    1.65 +        // should have fully synced stack at every bytecode.
    1.66 +        if (frameKind == ICEntry::Kind_NonOp) {
    1.67 +            PCMappingSlotInfo slotInfo;
    1.68 +            jsbytecode *pc = script->offsetToPC(pcOffset);
    1.69 +            oldBaselineScript->nativeCodeForPC(script, pc, &slotInfo);
    1.70 +            MOZ_ASSERT(slotInfo.numUnsynced() == 0);
    1.71 +        }
    1.72 +#endif
    1.73 +    }
    1.74 +
    1.75 +    DebugModeOSREntry(DebugModeOSREntry &&other)
    1.76 +      : script(other.script),
    1.77 +        oldBaselineScript(other.oldBaselineScript),
    1.78 +        recompInfo(other.recompInfo ? other.takeRecompInfo() : nullptr),
    1.79 +        pcOffset(other.pcOffset),
    1.80 +        frameKind(other.frameKind),
    1.81 +        stub(other.stub)
    1.82 +    { }
    1.83 +
    1.84 +    ~DebugModeOSREntry() {
    1.85 +        // Note that this is nulled out when the recompInfo is taken by the
    1.86 +        // frame. The frame then has the responsibility of freeing the
    1.87 +        // recompInfo.
    1.88 +        js_delete(recompInfo);
    1.89 +    }
    1.90 +
    1.91 +    bool needsRecompileInfo() const {
    1.92 +        return (frameKind == ICEntry::Kind_CallVM ||
    1.93 +                frameKind == ICEntry::Kind_DebugTrap ||
    1.94 +                frameKind == ICEntry::Kind_DebugPrologue ||
    1.95 +                frameKind == ICEntry::Kind_DebugEpilogue);
    1.96 +    }
    1.97 +
    1.98 +    BaselineDebugModeOSRInfo *takeRecompInfo() {
    1.99 +        MOZ_ASSERT(recompInfo);
   1.100 +        BaselineDebugModeOSRInfo *tmp = recompInfo;
   1.101 +        recompInfo = nullptr;
   1.102 +        return tmp;
   1.103 +    }
   1.104 +
   1.105 +    bool allocateRecompileInfo(JSContext *cx) {
   1.106 +        MOZ_ASSERT(needsRecompileInfo());
   1.107 +
   1.108 +        // If we are returning to a frame which needs a continuation fixer,
   1.109 +        // allocate the recompile info up front so that the patching function
   1.110 +        // is infallible.
   1.111 +        jsbytecode *pc = script->offsetToPC(pcOffset);
   1.112 +
   1.113 +        // XXX: Work around compiler error disallowing using bitfields
   1.114 +        // with the template magic of new_.
   1.115 +        ICEntry::Kind kind = frameKind;
   1.116 +        recompInfo = cx->new_<BaselineDebugModeOSRInfo>(pc, kind);
   1.117 +        return !!recompInfo;
   1.118 +    }
   1.119 +};
   1.120 +
   1.121 +typedef js::Vector<DebugModeOSREntry> DebugModeOSREntryVector;
   1.122 +
   1.123 +static bool
   1.124 +CollectOnStackScripts(JSContext *cx, const JitActivationIterator &activation,
   1.125 +                      DebugModeOSREntryVector &entries)
   1.126 +{
   1.127 +    DebugOnly<ICStub *> prevFrameStubPtr = nullptr;
   1.128 +    bool needsRecompileHandler = false;
   1.129 +    for (JitFrameIterator iter(activation); !iter.done(); ++iter) {
   1.130 +        switch (iter.type()) {
   1.131 +          case JitFrame_BaselineJS: {
   1.132 +            JSScript *script = iter.script();
   1.133 +            uint8_t *retAddr = iter.returnAddressToFp();
   1.134 +            ICEntry &entry = script->baselineScript()->icEntryFromReturnAddress(retAddr);
   1.135 +
   1.136 +            if (!entries.append(DebugModeOSREntry(script, entry)))
   1.137 +                return false;
   1.138 +
   1.139 +            if (entries.back().needsRecompileInfo()) {
   1.140 +                if (!entries.back().allocateRecompileInfo(cx))
   1.141 +                    return false;
   1.142 +
   1.143 +                needsRecompileHandler |= true;
   1.144 +            }
   1.145 +
   1.146 +            entries.back().stub = prevFrameStubPtr;
   1.147 +            prevFrameStubPtr = nullptr;
   1.148 +            break;
   1.149 +          }
   1.150 +
   1.151 +          case JitFrame_BaselineStub:
   1.152 +            prevFrameStubPtr =
   1.153 +                reinterpret_cast<IonBaselineStubFrameLayout *>(iter.fp())->maybeStubPtr();
   1.154 +            break;
   1.155 +
   1.156 +          case JitFrame_IonJS: {
   1.157 +            JSScript *script = iter.script();
   1.158 +            if (!entries.append(DebugModeOSREntry(script)))
   1.159 +                return false;
   1.160 +            for (InlineFrameIterator inlineIter(cx, &iter); inlineIter.more(); ++inlineIter) {
   1.161 +                if (!entries.append(DebugModeOSREntry(inlineIter.script())))
   1.162 +                    return false;
   1.163 +            }
   1.164 +            break;
   1.165 +          }
   1.166 +
   1.167 +          default:;
   1.168 +        }
   1.169 +    }
   1.170 +
   1.171 +    // Initialize the on-stack recompile handler, which may fail, so that
   1.172 +    // patching the stack is infallible.
   1.173 +    if (needsRecompileHandler) {
   1.174 +        JitRuntime *rt = cx->runtime()->jitRuntime();
   1.175 +        if (!rt->getBaselineDebugModeOSRHandlerAddress(cx, true))
   1.176 +            return false;
   1.177 +    }
   1.178 +
   1.179 +    return true;
   1.180 +}
   1.181 +
   1.182 +static inline uint8_t *
   1.183 +GetStubReturnFromStubAddress(JSContext *cx, jsbytecode *pc)
   1.184 +{
   1.185 +    JitCompartment *comp = cx->compartment()->jitCompartment();
   1.186 +    void *addr;
   1.187 +    if (IsGetPropPC(pc)) {
   1.188 +        addr = comp->baselineGetPropReturnFromStubAddr();
   1.189 +    } else if (IsSetPropPC(pc)) {
   1.190 +        addr = comp->baselineSetPropReturnFromStubAddr();
   1.191 +    } else {
   1.192 +        JS_ASSERT(IsCallPC(pc));
   1.193 +        addr = comp->baselineCallReturnFromStubAddr();
   1.194 +    }
   1.195 +    return reinterpret_cast<uint8_t *>(addr);
   1.196 +}
   1.197 +
   1.198 +static const char *
   1.199 +ICEntryKindToString(ICEntry::Kind kind)
   1.200 +{
   1.201 +    switch (kind) {
   1.202 +      case ICEntry::Kind_Op:
   1.203 +        return "IC";
   1.204 +      case ICEntry::Kind_NonOp:
   1.205 +        return "non-op IC";
   1.206 +      case ICEntry::Kind_CallVM:
   1.207 +        return "callVM";
   1.208 +      case ICEntry::Kind_DebugTrap:
   1.209 +        return "debug trap";
   1.210 +      case ICEntry::Kind_DebugPrologue:
   1.211 +        return "debug prologue";
   1.212 +      case ICEntry::Kind_DebugEpilogue:
   1.213 +        return "debug epilogue";
   1.214 +      default:
   1.215 +        MOZ_ASSUME_UNREACHABLE("bad ICEntry kind");
   1.216 +    }
   1.217 +}
   1.218 +
   1.219 +static void
   1.220 +SpewPatchBaselineFrame(uint8_t *oldReturnAddress, uint8_t *newReturnAddress,
   1.221 +                       JSScript *script, ICEntry::Kind frameKind, jsbytecode *pc)
   1.222 +{
   1.223 +    IonSpew(IonSpew_BaselineDebugModeOSR,
   1.224 +            "Patch return %#016llx -> %#016llx to BaselineJS (%s:%d) from %s at %s",
   1.225 +            uintptr_t(oldReturnAddress), uintptr_t(newReturnAddress),
   1.226 +            script->filename(), script->lineno(),
   1.227 +            ICEntryKindToString(frameKind), js_CodeName[(JSOp)*pc]);
   1.228 +}
   1.229 +
   1.230 +static void
   1.231 +SpewPatchStubFrame(uint8_t *oldReturnAddress, uint8_t *newReturnAddress,
   1.232 +                   ICStub *oldStub, ICStub *newStub)
   1.233 +{
   1.234 +    IonSpew(IonSpew_BaselineDebugModeOSR,
   1.235 +            "Patch return %#016llx -> %#016llx",
   1.236 +            uintptr_t(oldReturnAddress), uintptr_t(newReturnAddress));
   1.237 +    IonSpew(IonSpew_BaselineDebugModeOSR,
   1.238 +            "Patch   stub %#016llx -> %#016llx to BaselineStub",
   1.239 +            uintptr_t(oldStub), uintptr_t(newStub));
   1.240 +}
   1.241 +
   1.242 +static void
   1.243 +PatchBaselineFramesForDebugMode(JSContext *cx, const JitActivationIterator &activation,
   1.244 +                                DebugModeOSREntryVector &entries, size_t *start)
   1.245 +{
   1.246 +    //
   1.247 +    // Recompile Patching Overview
   1.248 +    //
   1.249 +    // When toggling debug mode with live baseline scripts on the stack, we
   1.250 +    // could have entered the VM via the following ways from the baseline
   1.251 +    // script.
   1.252 +    //
   1.253 +    // Off to On:
   1.254 +    //  A. From a "can call" stub.
   1.255 +    //  B. From a VM call (interrupt handler, debugger statement handler).
   1.256 +    //
   1.257 +    // On to Off:
   1.258 +    //  - All the ways above.
   1.259 +    //  C. From the debug trap handler.
   1.260 +    //  D. From the debug prologue.
   1.261 +    //  E. From the debug epilogue.
   1.262 +    //
   1.263 +    // In general, we patch the return address from the VM call to return to a
   1.264 +    // "continuation fixer" to fix up machine state (registers and stack
   1.265 +    // state). Specifics on what need to be done are documented below.
   1.266 +    //
   1.267 +
   1.268 +    IonCommonFrameLayout *prev = nullptr;
   1.269 +    size_t entryIndex = *start;
   1.270 +    DebugOnly<bool> expectedDebugMode = cx->compartment()->debugMode();
   1.271 +
   1.272 +    for (JitFrameIterator iter(activation); !iter.done(); ++iter) {
   1.273 +        switch (iter.type()) {
   1.274 +          case JitFrame_BaselineJS: {
   1.275 +            JSScript *script = entries[entryIndex].script;
   1.276 +            uint32_t pcOffset = entries[entryIndex].pcOffset;
   1.277 +            jsbytecode *pc = script->offsetToPC(pcOffset);
   1.278 +
   1.279 +            MOZ_ASSERT(script == iter.script());
   1.280 +            MOZ_ASSERT(pcOffset < script->length());
   1.281 +            MOZ_ASSERT(script->baselineScript()->debugMode() == expectedDebugMode);
   1.282 +
   1.283 +            BaselineScript *bl = script->baselineScript();
   1.284 +            ICEntry::Kind kind = entries[entryIndex].frameKind;
   1.285 +
   1.286 +            if (kind == ICEntry::Kind_Op) {
   1.287 +                // Case A above.
   1.288 +                //
   1.289 +                // Patching this case needs to patch both the stub frame and
   1.290 +                // the baseline frame. The stub frame is patched below. For
   1.291 +                // the baseline frame here, we resume right after the IC
   1.292 +                // returns.
   1.293 +                //
   1.294 +                // Since we're using the IC-specific k-fixer, we can resume
   1.295 +                // directly to the IC resume address.
   1.296 +                uint8_t *retAddr = bl->returnAddressForIC(bl->icEntryFromPCOffset(pcOffset));
   1.297 +                SpewPatchBaselineFrame(prev->returnAddress(), retAddr, script, kind, pc);
   1.298 +                prev->setReturnAddress(retAddr);
   1.299 +                entryIndex++;
   1.300 +                break;
   1.301 +            }
   1.302 +
   1.303 +            bool popFrameReg;
   1.304 +
   1.305 +            // The RecompileInfo must already be allocated so that this
   1.306 +            // function may be infallible.
   1.307 +            BaselineDebugModeOSRInfo *recompInfo = entries[entryIndex].takeRecompInfo();
   1.308 +
   1.309 +            switch (kind) {
   1.310 +              case ICEntry::Kind_CallVM:
   1.311 +                // Case B above.
   1.312 +                //
   1.313 +                // Patching returns from an interrupt handler or the debugger
   1.314 +                // statement handler is similar in that we can resume at the
   1.315 +                // next op.
   1.316 +                pc += GetBytecodeLength(pc);
   1.317 +                recompInfo->resumeAddr = bl->nativeCodeForPC(script, pc, &recompInfo->slotInfo);
   1.318 +                popFrameReg = true;
   1.319 +                break;
   1.320 +
   1.321 +              case ICEntry::Kind_DebugTrap:
   1.322 +                // Case C above.
   1.323 +                //
   1.324 +                // Debug traps are emitted before each op, so we resume at the
   1.325 +                // same op. Calling debug trap handlers is done via a toggled
   1.326 +                // call to a thunk (DebugTrapHandler) that takes care tearing
   1.327 +                // down its own stub frame so we don't need to worry about
   1.328 +                // popping the frame reg.
   1.329 +                recompInfo->resumeAddr = bl->nativeCodeForPC(script, pc, &recompInfo->slotInfo);
   1.330 +                popFrameReg = false;
   1.331 +                break;
   1.332 +
   1.333 +              case ICEntry::Kind_DebugPrologue:
   1.334 +                // Case D above.
   1.335 +                //
   1.336 +                // We patch a jump directly to the right place in the prologue
   1.337 +                // after popping the frame reg and checking for forced return.
   1.338 +                recompInfo->resumeAddr = bl->postDebugPrologueAddr();
   1.339 +                popFrameReg = true;
   1.340 +                break;
   1.341 +
   1.342 +              default:
   1.343 +                // Case E above.
   1.344 +                //
   1.345 +                // We patch a jump directly to the epilogue after popping the
   1.346 +                // frame reg and checking for forced return.
   1.347 +                MOZ_ASSERT(kind == ICEntry::Kind_DebugEpilogue);
   1.348 +                recompInfo->resumeAddr = bl->epilogueEntryAddr();
   1.349 +                popFrameReg = true;
   1.350 +                break;
   1.351 +            }
   1.352 +
   1.353 +            SpewPatchBaselineFrame(prev->returnAddress(), recompInfo->resumeAddr,
   1.354 +                                   script, kind, recompInfo->pc);
   1.355 +
   1.356 +            // The recompile handler must already be created so that this
   1.357 +            // function may be infallible.
   1.358 +            JitRuntime *rt = cx->runtime()->jitRuntime();
   1.359 +            void *handlerAddr = rt->getBaselineDebugModeOSRHandlerAddress(cx, popFrameReg);
   1.360 +            MOZ_ASSERT(handlerAddr);
   1.361 +
   1.362 +            prev->setReturnAddress(reinterpret_cast<uint8_t *>(handlerAddr));
   1.363 +            iter.baselineFrame()->setDebugModeOSRInfo(recompInfo);
   1.364 +
   1.365 +            entryIndex++;
   1.366 +            break;
   1.367 +          }
   1.368 +
   1.369 +          case JitFrame_BaselineStub: {
   1.370 +            JSScript *script = entries[entryIndex].script;
   1.371 +            IonBaselineStubFrameLayout *layout =
   1.372 +                reinterpret_cast<IonBaselineStubFrameLayout *>(iter.fp());
   1.373 +            MOZ_ASSERT(script->baselineScript()->debugMode() == expectedDebugMode);
   1.374 +            MOZ_ASSERT(layout->maybeStubPtr() == entries[entryIndex].stub);
   1.375 +
   1.376 +            // Patch baseline stub frames for case A above.
   1.377 +            //
   1.378 +            // We need to patch the stub frame return address to go to the
   1.379 +            // k-fixer that is at the end of fallback stubs of all such
   1.380 +            // can-call ICs. These k-fixers share code with bailout-from-Ion
   1.381 +            // fixers, but in this case we are returning from VM and not
   1.382 +            // Ion. See e.g., JitCompartment::baselineCallReturnFromStubAddr()
   1.383 +            //
   1.384 +            // Subtlety here: the debug trap handler of case C above pushes a
   1.385 +            // stub frame with a null stub pointer. This handler will exist
   1.386 +            // across recompiling the script, so we don't patch anything for
   1.387 +            // such stub frames. We will return to that handler, which takes
   1.388 +            // care of cleaning up the stub frame.
   1.389 +            //
   1.390 +            // Note that for stub pointers that are already on the C stack
   1.391 +            // (i.e. fallback calls), we need to check for recompilation using
   1.392 +            // DebugModeOSRVolatileStub.
   1.393 +            if (layout->maybeStubPtr()) {
   1.394 +                MOZ_ASSERT(layout->maybeStubPtr() == entries[entryIndex].stub);
   1.395 +                uint32_t pcOffset = entries[entryIndex].pcOffset;
   1.396 +                uint8_t *retAddr = GetStubReturnFromStubAddress(cx, script->offsetToPC(pcOffset));
   1.397 +
   1.398 +                // Get the fallback stub for the IC in the recompiled
   1.399 +                // script. The fallback stub is guaranteed to exist.
   1.400 +                ICEntry &entry = script->baselineScript()->icEntryFromPCOffset(pcOffset);
   1.401 +                ICStub *newStub = entry.fallbackStub();
   1.402 +                SpewPatchStubFrame(prev->returnAddress(), retAddr, layout->maybeStubPtr(), newStub);
   1.403 +                prev->setReturnAddress(retAddr);
   1.404 +                layout->setStubPtr(newStub);
   1.405 +            }
   1.406 +
   1.407 +            break;
   1.408 +          }
   1.409 +
   1.410 +          case JitFrame_IonJS:
   1.411 +            // Nothing to patch.
   1.412 +            entryIndex++;
   1.413 +            for (InlineFrameIterator inlineIter(cx, &iter); inlineIter.more(); ++inlineIter)
   1.414 +                entryIndex++;
   1.415 +            break;
   1.416 +
   1.417 +          default:;
   1.418 +        }
   1.419 +
   1.420 +        prev = iter.current();
   1.421 +    }
   1.422 +
   1.423 +    *start = entryIndex;
   1.424 +}
   1.425 +
   1.426 +static bool
   1.427 +RecompileBaselineScriptForDebugMode(JSContext *cx, JSScript *script)
   1.428 +{
   1.429 +    BaselineScript *oldBaselineScript = script->baselineScript();
   1.430 +
   1.431 +    // If a script is on the stack multiple times, it may have already
   1.432 +    // been recompiled.
   1.433 +    bool expectedDebugMode = cx->compartment()->debugMode();
   1.434 +    if (oldBaselineScript->debugMode() == expectedDebugMode)
   1.435 +        return true;
   1.436 +
   1.437 +    IonSpew(IonSpew_BaselineDebugModeOSR, "Recompiling (%s:%d) for debug mode %s",
   1.438 +            script->filename(), script->lineno(), expectedDebugMode ? "ON" : "OFF");
   1.439 +
   1.440 +    if (script->hasIonScript())
   1.441 +        Invalidate(cx, script, /* resetUses = */ false);
   1.442 +
   1.443 +    script->setBaselineScript(cx, nullptr);
   1.444 +
   1.445 +    MethodStatus status = BaselineCompile(cx, script);
   1.446 +    if (status != Method_Compiled) {
   1.447 +        // We will only fail to recompile for debug mode due to OOM. Restore
   1.448 +        // the old baseline script in case something doesn't properly
   1.449 +        // propagate OOM.
   1.450 +        MOZ_ASSERT(status == Method_Error);
   1.451 +        script->setBaselineScript(cx, oldBaselineScript);
   1.452 +        return false;
   1.453 +    }
   1.454 +
   1.455 +    // Don't destroy the old baseline script yet, since if we fail any of the
   1.456 +    // recompiles we need to rollback all the old baseline scripts.
   1.457 +    MOZ_ASSERT(script->baselineScript()->debugMode() == expectedDebugMode);
   1.458 +    return true;
   1.459 +}
   1.460 +
   1.461 +static void
   1.462 +UndoRecompileBaselineScriptsForDebugMode(JSContext *cx,
   1.463 +                                         const DebugModeOSREntryVector &entries)
   1.464 +{
   1.465 +    // In case of failure, roll back the entire set of active scripts so that
   1.466 +    // we don't have to patch return addresses on the stack.
   1.467 +    for (size_t i = 0; i < entries.length(); i++) {
   1.468 +        JSScript *script = entries[i].script;
   1.469 +        BaselineScript *baselineScript = script->baselineScript();
   1.470 +        if (baselineScript != entries[i].oldBaselineScript) {
   1.471 +            script->setBaselineScript(cx, entries[i].oldBaselineScript);
   1.472 +            BaselineScript::Destroy(cx->runtime()->defaultFreeOp(), baselineScript);
   1.473 +        }
   1.474 +    }
   1.475 +}
   1.476 +
   1.477 +bool
   1.478 +jit::RecompileOnStackBaselineScriptsForDebugMode(JSContext *cx, JSCompartment *comp)
   1.479 +{
   1.480 +    AutoCompartment ac(cx, comp);
   1.481 +
   1.482 +    // First recompile the active scripts on the stack and patch the live
   1.483 +    // frames.
   1.484 +    Vector<DebugModeOSREntry> entries(cx);
   1.485 +
   1.486 +    for (JitActivationIterator iter(cx->runtime()); !iter.done(); ++iter) {
   1.487 +        if (iter.activation()->compartment() == comp) {
   1.488 +            if (!CollectOnStackScripts(cx, iter, entries))
   1.489 +                return false;
   1.490 +        }
   1.491 +    }
   1.492 +
   1.493 +#ifdef JSGC_GENERATIONAL
   1.494 +    // Scripts can entrain nursery things. See note in js::ReleaseAllJITCode.
   1.495 +    if (!entries.empty())
   1.496 +        MinorGC(cx->runtime(), JS::gcreason::EVICT_NURSERY);
   1.497 +#endif
   1.498 +
   1.499 +    // Try to recompile all the scripts. If we encounter an error, we need to
   1.500 +    // roll back as if none of the compilations happened, so that we don't
   1.501 +    // crash.
   1.502 +    for (size_t i = 0; i < entries.length(); i++) {
   1.503 +        JSScript *script = entries[i].script;
   1.504 +        if (!RecompileBaselineScriptForDebugMode(cx, script)) {
   1.505 +            UndoRecompileBaselineScriptsForDebugMode(cx, entries);
   1.506 +            return false;
   1.507 +        }
   1.508 +    }
   1.509 +
   1.510 +    // If all recompiles succeeded, destroy the old baseline scripts and patch
   1.511 +    // the live frames.
   1.512 +    //
   1.513 +    // After this point the function must be infallible.
   1.514 +
   1.515 +    for (size_t i = 0; i < entries.length(); i++)
   1.516 +        BaselineScript::Destroy(cx->runtime()->defaultFreeOp(), entries[i].oldBaselineScript);
   1.517 +
   1.518 +    size_t processed = 0;
   1.519 +    for (JitActivationIterator iter(cx->runtime()); !iter.done(); ++iter) {
   1.520 +        if (iter.activation()->compartment() == comp)
   1.521 +            PatchBaselineFramesForDebugMode(cx, iter, entries, &processed);
   1.522 +    }
   1.523 +    MOZ_ASSERT(processed == entries.length());
   1.524 +
   1.525 +    return true;
   1.526 +}
   1.527 +
   1.528 +void
   1.529 +BaselineDebugModeOSRInfo::popValueInto(PCMappingSlotInfo::SlotLocation loc, Value *vp)
   1.530 +{
   1.531 +    switch (loc) {
   1.532 +      case PCMappingSlotInfo::SlotInR0:
   1.533 +        valueR0 = vp[stackAdjust];
   1.534 +        break;
   1.535 +      case PCMappingSlotInfo::SlotInR1:
   1.536 +        valueR1 = vp[stackAdjust];
   1.537 +        break;
   1.538 +      case PCMappingSlotInfo::SlotIgnore:
   1.539 +        break;
   1.540 +      default:
   1.541 +        MOZ_ASSUME_UNREACHABLE("Bad slot location");
   1.542 +    }
   1.543 +
   1.544 +    stackAdjust++;
   1.545 +}
   1.546 +
   1.547 +static inline bool
   1.548 +HasForcedReturn(BaselineDebugModeOSRInfo *info, bool rv)
   1.549 +{
   1.550 +    ICEntry::Kind kind = info->frameKind;
   1.551 +
   1.552 +    // The debug epilogue always checks its resumption value, so we don't need
   1.553 +    // to check rv.
   1.554 +    if (kind == ICEntry::Kind_DebugEpilogue)
   1.555 +        return true;
   1.556 +
   1.557 +    // |rv| is the value in ReturnReg. If true, in the case of the prologue,
   1.558 +    // debug trap, and debugger statement handler, it means a forced return.
   1.559 +    if (kind == ICEntry::Kind_DebugPrologue ||
   1.560 +        (kind == ICEntry::Kind_CallVM && JSOp(*info->pc) == JSOP_DEBUGGER))
   1.561 +    {
   1.562 +        return rv;
   1.563 +    }
   1.564 +
   1.565 +    // N.B. The debug trap handler handles its own forced return, so no
   1.566 +    // need to deal with it here.
   1.567 +    return false;
   1.568 +}
   1.569 +
   1.570 +static void
   1.571 +SyncBaselineDebugModeOSRInfo(BaselineFrame *frame, Value *vp, bool rv)
   1.572 +{
   1.573 +    BaselineDebugModeOSRInfo *info = frame->debugModeOSRInfo();
   1.574 +    MOZ_ASSERT(info);
   1.575 +    MOZ_ASSERT(frame->script()->baselineScript()->containsCodeAddress(info->resumeAddr));
   1.576 +
   1.577 +    if (HasForcedReturn(info, rv)) {
   1.578 +        // Load the frame's rval and overwrite the resume address to go to the
   1.579 +        // epilogue.
   1.580 +        MOZ_ASSERT(R0 == JSReturnOperand);
   1.581 +        info->valueR0 = frame->returnValue();
   1.582 +        info->resumeAddr = frame->script()->baselineScript()->epilogueEntryAddr();
   1.583 +        return;
   1.584 +    }
   1.585 +
   1.586 +    // Read stack values and make sure R0 and R1 have the right values.
   1.587 +    unsigned numUnsynced = info->slotInfo.numUnsynced();
   1.588 +    MOZ_ASSERT(numUnsynced <= 2);
   1.589 +    if (numUnsynced > 0)
   1.590 +        info->popValueInto(info->slotInfo.topSlotLocation(), vp);
   1.591 +    if (numUnsynced > 1)
   1.592 +        info->popValueInto(info->slotInfo.nextSlotLocation(), vp);
   1.593 +
   1.594 +    // Scale stackAdjust.
   1.595 +    info->stackAdjust *= sizeof(Value);
   1.596 +}
   1.597 +
   1.598 +static void
   1.599 +FinishBaselineDebugModeOSR(BaselineFrame *frame)
   1.600 +{
   1.601 +    frame->deleteDebugModeOSRInfo();
   1.602 +}
   1.603 +
   1.604 +void
   1.605 +BaselineFrame::deleteDebugModeOSRInfo()
   1.606 +{
   1.607 +    js_delete(getDebugModeOSRInfo());
   1.608 +    flags_ &= ~HAS_DEBUG_MODE_OSR_INFO;
   1.609 +}
   1.610 +
   1.611 +JitCode *
   1.612 +JitRuntime::getBaselineDebugModeOSRHandler(JSContext *cx)
   1.613 +{
   1.614 +    if (!baselineDebugModeOSRHandler_) {
   1.615 +        AutoLockForExclusiveAccess lock(cx);
   1.616 +        AutoCompartment ac(cx, cx->runtime()->atomsCompartment());
   1.617 +        uint32_t offset;
   1.618 +        if (JitCode *code = generateBaselineDebugModeOSRHandler(cx, &offset)) {
   1.619 +            baselineDebugModeOSRHandler_ = code;
   1.620 +            baselineDebugModeOSRHandlerNoFrameRegPopAddr_ = code->raw() + offset;
   1.621 +        }
   1.622 +    }
   1.623 +
   1.624 +    return baselineDebugModeOSRHandler_;
   1.625 +}
   1.626 +
   1.627 +void *
   1.628 +JitRuntime::getBaselineDebugModeOSRHandlerAddress(JSContext *cx, bool popFrameReg)
   1.629 +{
   1.630 +    if (!getBaselineDebugModeOSRHandler(cx))
   1.631 +        return nullptr;
   1.632 +    return (popFrameReg
   1.633 +            ? baselineDebugModeOSRHandler_->raw()
   1.634 +            : baselineDebugModeOSRHandlerNoFrameRegPopAddr_);
   1.635 +}
   1.636 +
   1.637 +JitCode *
   1.638 +JitRuntime::generateBaselineDebugModeOSRHandler(JSContext *cx, uint32_t *noFrameRegPopOffsetOut)
   1.639 +{
   1.640 +    MacroAssembler masm(cx);
   1.641 +
   1.642 +    GeneralRegisterSet regs(GeneralRegisterSet::All());
   1.643 +    regs.take(BaselineFrameReg);
   1.644 +    regs.take(ReturnReg);
   1.645 +    Register temp = regs.takeAny();
   1.646 +    Register syncedStackStart = regs.takeAny();
   1.647 +
   1.648 +    // Pop the frame reg.
   1.649 +    masm.pop(BaselineFrameReg);
   1.650 +
   1.651 +    // Not all patched baseline frames are returning from a situation where
   1.652 +    // the frame reg is already fixed up.
   1.653 +    CodeOffsetLabel noFrameRegPopOffset = masm.currentOffset();
   1.654 +
   1.655 +    // Record the stack pointer for syncing.
   1.656 +    masm.movePtr(StackPointer, syncedStackStart);
   1.657 +    masm.push(BaselineFrameReg);
   1.658 +
   1.659 +    // Call a stub to fully initialize the info.
   1.660 +    masm.setupUnalignedABICall(3, temp);
   1.661 +    masm.loadBaselineFramePtr(BaselineFrameReg, temp);
   1.662 +    masm.passABIArg(temp);
   1.663 +    masm.passABIArg(syncedStackStart);
   1.664 +    masm.passABIArg(ReturnReg);
   1.665 +    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, SyncBaselineDebugModeOSRInfo));
   1.666 +
   1.667 +    // Discard stack values depending on how many were unsynced, as we always
   1.668 +    // have a fully synced stack in the recompile handler. See assert in
   1.669 +    // DebugModeOSREntry constructor.
   1.670 +    masm.pop(BaselineFrameReg);
   1.671 +    masm.loadPtr(Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfScratchValue()), temp);
   1.672 +    masm.addPtr(Address(temp, offsetof(BaselineDebugModeOSRInfo, stackAdjust)), StackPointer);
   1.673 +
   1.674 +    // Save real return address on the stack temporarily.
   1.675 +    masm.pushValue(Address(temp, offsetof(BaselineDebugModeOSRInfo, valueR0)));
   1.676 +    masm.pushValue(Address(temp, offsetof(BaselineDebugModeOSRInfo, valueR1)));
   1.677 +    masm.push(BaselineFrameReg);
   1.678 +    masm.push(Address(temp, offsetof(BaselineDebugModeOSRInfo, resumeAddr)));
   1.679 +
   1.680 +    // Call a stub to free the allocated info.
   1.681 +    masm.setupUnalignedABICall(1, temp);
   1.682 +    masm.loadBaselineFramePtr(BaselineFrameReg, temp);
   1.683 +    masm.passABIArg(temp);
   1.684 +    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, FinishBaselineDebugModeOSR));
   1.685 +
   1.686 +    // Restore saved values.
   1.687 +    GeneralRegisterSet jumpRegs(GeneralRegisterSet::All());
   1.688 +    jumpRegs.take(R0);
   1.689 +    jumpRegs.take(R1);
   1.690 +    jumpRegs.take(BaselineFrameReg);
   1.691 +    Register target = jumpRegs.takeAny();
   1.692 +
   1.693 +    masm.pop(target);
   1.694 +    masm.pop(BaselineFrameReg);
   1.695 +    masm.popValue(R1);
   1.696 +    masm.popValue(R0);
   1.697 +
   1.698 +    masm.jump(target);
   1.699 +
   1.700 +    Linker linker(masm);
   1.701 +    AutoFlushICache afc("BaselineDebugModeOSRHandler");
   1.702 +    JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
   1.703 +    if (!code)
   1.704 +        return nullptr;
   1.705 +
   1.706 +    noFrameRegPopOffset.fixup(&masm);
   1.707 +    *noFrameRegPopOffsetOut = noFrameRegPopOffset.offset();
   1.708 +
   1.709 +#ifdef JS_ION_PERF
   1.710 +    writePerfSpewerJitCodeProfile(code, "BaselineDebugModeOSRHandler");
   1.711 +#endif
   1.712 +
   1.713 +    return code;
   1.714 +}

mercurial