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 +}