js/src/jit/BaselineDebugModeOSR.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:f6dc20900164
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/. */
6
7 #include "jit/BaselineDebugModeOSR.h"
8
9 #include "mozilla/DebugOnly.h"
10
11 #include "jit/IonLinker.h"
12 #include "jit/PerfSpewer.h"
13
14 #include "jit/IonFrames-inl.h"
15 #include "vm/Stack-inl.h"
16
17 using namespace mozilla;
18 using namespace js;
19 using namespace js::jit;
20
21 struct DebugModeOSREntry
22 {
23 JSScript *script;
24 BaselineScript *oldBaselineScript;
25 BaselineDebugModeOSRInfo *recompInfo;
26 uint32_t pcOffset;
27 ICEntry::Kind frameKind;
28
29 // Used for sanity asserts in debug builds.
30 DebugOnly<ICStub *> stub;
31
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 { }
40
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());
52
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 }
71
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 { }
80
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 }
87
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 }
94
95 BaselineDebugModeOSRInfo *takeRecompInfo() {
96 MOZ_ASSERT(recompInfo);
97 BaselineDebugModeOSRInfo *tmp = recompInfo;
98 recompInfo = nullptr;
99 return tmp;
100 }
101
102 bool allocateRecompileInfo(JSContext *cx) {
103 MOZ_ASSERT(needsRecompileInfo());
104
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);
109
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 };
117
118 typedef js::Vector<DebugModeOSREntry> DebugModeOSREntryVector;
119
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);
132
133 if (!entries.append(DebugModeOSREntry(script, entry)))
134 return false;
135
136 if (entries.back().needsRecompileInfo()) {
137 if (!entries.back().allocateRecompileInfo(cx))
138 return false;
139
140 needsRecompileHandler |= true;
141 }
142
143 entries.back().stub = prevFrameStubPtr;
144 prevFrameStubPtr = nullptr;
145 break;
146 }
147
148 case JitFrame_BaselineStub:
149 prevFrameStubPtr =
150 reinterpret_cast<IonBaselineStubFrameLayout *>(iter.fp())->maybeStubPtr();
151 break;
152
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 }
163
164 default:;
165 }
166 }
167
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 }
175
176 return true;
177 }
178
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 }
194
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 }
215
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 }
226
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 }
238
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 //
264
265 IonCommonFrameLayout *prev = nullptr;
266 size_t entryIndex = *start;
267 DebugOnly<bool> expectedDebugMode = cx->compartment()->debugMode();
268
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);
275
276 MOZ_ASSERT(script == iter.script());
277 MOZ_ASSERT(pcOffset < script->length());
278 MOZ_ASSERT(script->baselineScript()->debugMode() == expectedDebugMode);
279
280 BaselineScript *bl = script->baselineScript();
281 ICEntry::Kind kind = entries[entryIndex].frameKind;
282
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 }
299
300 bool popFrameReg;
301
302 // The RecompileInfo must already be allocated so that this
303 // function may be infallible.
304 BaselineDebugModeOSRInfo *recompInfo = entries[entryIndex].takeRecompInfo();
305
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;
317
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;
329
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;
338
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 }
349
350 SpewPatchBaselineFrame(prev->returnAddress(), recompInfo->resumeAddr,
351 script, kind, recompInfo->pc);
352
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);
358
359 prev->setReturnAddress(reinterpret_cast<uint8_t *>(handlerAddr));
360 iter.baselineFrame()->setDebugModeOSRInfo(recompInfo);
361
362 entryIndex++;
363 break;
364 }
365
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);
372
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));
394
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 }
403
404 break;
405 }
406
407 case JitFrame_IonJS:
408 // Nothing to patch.
409 entryIndex++;
410 for (InlineFrameIterator inlineIter(cx, &iter); inlineIter.more(); ++inlineIter)
411 entryIndex++;
412 break;
413
414 default:;
415 }
416
417 prev = iter.current();
418 }
419
420 *start = entryIndex;
421 }
422
423 static bool
424 RecompileBaselineScriptForDebugMode(JSContext *cx, JSScript *script)
425 {
426 BaselineScript *oldBaselineScript = script->baselineScript();
427
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;
433
434 IonSpew(IonSpew_BaselineDebugModeOSR, "Recompiling (%s:%d) for debug mode %s",
435 script->filename(), script->lineno(), expectedDebugMode ? "ON" : "OFF");
436
437 if (script->hasIonScript())
438 Invalidate(cx, script, /* resetUses = */ false);
439
440 script->setBaselineScript(cx, nullptr);
441
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 }
451
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 }
457
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 }
473
474 bool
475 jit::RecompileOnStackBaselineScriptsForDebugMode(JSContext *cx, JSCompartment *comp)
476 {
477 AutoCompartment ac(cx, comp);
478
479 // First recompile the active scripts on the stack and patch the live
480 // frames.
481 Vector<DebugModeOSREntry> entries(cx);
482
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 }
489
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
495
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 }
506
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.
511
512 for (size_t i = 0; i < entries.length(); i++)
513 BaselineScript::Destroy(cx->runtime()->defaultFreeOp(), entries[i].oldBaselineScript);
514
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());
521
522 return true;
523 }
524
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 }
540
541 stackAdjust++;
542 }
543
544 static inline bool
545 HasForcedReturn(BaselineDebugModeOSRInfo *info, bool rv)
546 {
547 ICEntry::Kind kind = info->frameKind;
548
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;
553
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 }
561
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 }
566
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));
573
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 }
582
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);
590
591 // Scale stackAdjust.
592 info->stackAdjust *= sizeof(Value);
593 }
594
595 static void
596 FinishBaselineDebugModeOSR(BaselineFrame *frame)
597 {
598 frame->deleteDebugModeOSRInfo();
599 }
600
601 void
602 BaselineFrame::deleteDebugModeOSRInfo()
603 {
604 js_delete(getDebugModeOSRInfo());
605 flags_ &= ~HAS_DEBUG_MODE_OSR_INFO;
606 }
607
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 }
620
621 return baselineDebugModeOSRHandler_;
622 }
623
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 }
633
634 JitCode *
635 JitRuntime::generateBaselineDebugModeOSRHandler(JSContext *cx, uint32_t *noFrameRegPopOffsetOut)
636 {
637 MacroAssembler masm(cx);
638
639 GeneralRegisterSet regs(GeneralRegisterSet::All());
640 regs.take(BaselineFrameReg);
641 regs.take(ReturnReg);
642 Register temp = regs.takeAny();
643 Register syncedStackStart = regs.takeAny();
644
645 // Pop the frame reg.
646 masm.pop(BaselineFrameReg);
647
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();
651
652 // Record the stack pointer for syncing.
653 masm.movePtr(StackPointer, syncedStackStart);
654 masm.push(BaselineFrameReg);
655
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));
663
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);
670
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)));
676
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));
682
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();
689
690 masm.pop(target);
691 masm.pop(BaselineFrameReg);
692 masm.popValue(R1);
693 masm.popValue(R0);
694
695 masm.jump(target);
696
697 Linker linker(masm);
698 AutoFlushICache afc("BaselineDebugModeOSRHandler");
699 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
700 if (!code)
701 return nullptr;
702
703 noFrameRegPopOffset.fixup(&masm);
704 *noFrameRegPopOffsetOut = noFrameRegPopOffset.offset();
705
706 #ifdef JS_ION_PERF
707 writePerfSpewerJitCodeProfile(code, "BaselineDebugModeOSRHandler");
708 #endif
709
710 return code;
711 }

mercurial