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.

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

mercurial