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/JitFrames-inl.h" 8 9 #include "mozilla/SizePrintfMacros.h" 10 11 #include "jsfun.h" 12 #include "jsobj.h" 13 #include "jsscript.h" 14 #include "jsutil.h" 15 16 #include "gc/Marking.h" 17 #include "jit/BaselineDebugModeOSR.h" 18 #include "jit/BaselineFrame.h" 19 #include "jit/BaselineIC.h" 20 #include "jit/BaselineJIT.h" 21 #include "jit/Ion.h" 22 #include "jit/JitcodeMap.h" 23 #include "jit/JitCompartment.h" 24 #include "jit/JitSpewer.h" 25 #include "jit/MacroAssembler.h" 26 #include "jit/PcScriptCache.h" 27 #include "jit/Recover.h" 28 #include "jit/Safepoints.h" 29 #include "jit/Snapshots.h" 30 #include "jit/VMFunctions.h" 31 #include "vm/ArgumentsObject.h" 32 #include "vm/Debugger.h" 33 #include "vm/Interpreter.h" 34 #include "vm/SPSProfiler.h" 35 #include "vm/TraceLogging.h" 36 #include "vm/TypeInference.h" 37 38 #include "jsscriptinlines.h" 39 #include "gc/Nursery-inl.h" 40 #include "jit/JitFrameIterator-inl.h" 41 #include "vm/Debugger-inl.h" 42 #include "vm/Probes-inl.h" 43 #include "vm/TypeInference-inl.h" 44 45 namespace js { 46 namespace jit { 47 48 // Given a slot index, returns the offset, in bytes, of that slot from an 49 // JitFrameLayout. Slot distances are uniform across architectures, however, 50 // the distance does depend on the size of the frame header. 51 static inline int32_t 52 OffsetOfFrameSlot(int32_t slot) 53 { 54 return -slot; 55 } 56 57 static inline uint8_t* 58 AddressOfFrameSlot(JitFrameLayout* fp, int32_t slot) 59 { 60 return (uint8_t*) fp + OffsetOfFrameSlot(slot); 61 } 62 63 static inline uintptr_t 64 ReadFrameSlot(JitFrameLayout* fp, int32_t slot) 65 { 66 return *(uintptr_t*) AddressOfFrameSlot(fp, slot); 67 } 68 69 static inline void 70 WriteFrameSlot(JitFrameLayout* fp, int32_t slot, uintptr_t value) 71 { 72 *(uintptr_t*) AddressOfFrameSlot(fp, slot) = value; 73 } 74 75 static inline double 76 ReadFrameDoubleSlot(JitFrameLayout* fp, int32_t slot) 77 { 78 return *(double*) AddressOfFrameSlot(fp, slot); 79 } 80 81 static inline float 82 ReadFrameFloat32Slot(JitFrameLayout* fp, int32_t slot) 83 { 84 return *(float*) AddressOfFrameSlot(fp, slot); 85 } 86 87 static inline int32_t 88 ReadFrameInt32Slot(JitFrameLayout* fp, int32_t slot) 89 { 90 return *(int32_t*) AddressOfFrameSlot(fp, slot); 91 } 92 93 static inline bool 94 ReadFrameBooleanSlot(JitFrameLayout* fp, int32_t slot) 95 { 96 return *(bool*) AddressOfFrameSlot(fp, slot); 97 } 98 99 JitFrameIterator::JitFrameIterator() 100 : current_(nullptr), 101 type_(JitFrame_Exit), 102 returnAddressToFp_(nullptr), 103 frameSize_(0), 104 cachedSafepointIndex_(nullptr), 105 activation_(nullptr) 106 { 107 } 108 109 JitFrameIterator::JitFrameIterator(JSContext* cx) 110 : current_(cx->runtime()->jitTop), 111 type_(JitFrame_Exit), 112 returnAddressToFp_(nullptr), 113 frameSize_(0), 114 cachedSafepointIndex_(nullptr), 115 activation_(cx->runtime()->activation()->asJit()) 116 { 117 if (activation_->bailoutData()) { 118 current_ = activation_->bailoutData()->fp(); 119 frameSize_ = activation_->bailoutData()->topFrameSize(); 120 type_ = JitFrame_Bailout; 121 } 122 } 123 124 JitFrameIterator::JitFrameIterator(const ActivationIterator& activations) 125 : current_(activations.jitTop()), 126 type_(JitFrame_Exit), 127 returnAddressToFp_(nullptr), 128 frameSize_(0), 129 cachedSafepointIndex_(nullptr), 130 activation_(activations->asJit()) 131 { 132 if (activation_->bailoutData()) { 133 current_ = activation_->bailoutData()->fp(); 134 frameSize_ = activation_->bailoutData()->topFrameSize(); 135 type_ = JitFrame_Bailout; 136 } 137 } 138 139 bool 140 JitFrameIterator::checkInvalidation() const 141 { 142 IonScript* dummy; 143 return checkInvalidation(&dummy); 144 } 145 146 bool 147 JitFrameIterator::checkInvalidation(IonScript** ionScriptOut) const 148 { 149 JSScript* script = this->script(); 150 if (isBailoutJS()) { 151 *ionScriptOut = activation_->bailoutData()->ionScript(); 152 return !script->hasIonScript() || script->ionScript() != *ionScriptOut; 153 } 154 155 uint8_t* returnAddr = returnAddressToFp(); 156 // N.B. the current IonScript is not the same as the frame's 157 // IonScript if the frame has since been invalidated. 158 bool invalidated = !script->hasIonScript() || 159 !script->ionScript()->containsReturnAddress(returnAddr); 160 if (!invalidated) 161 return false; 162 163 int32_t invalidationDataOffset = ((int32_t*) returnAddr)[-1]; 164 uint8_t* ionScriptDataOffset = returnAddr + invalidationDataOffset; 165 IonScript* ionScript = (IonScript*) Assembler::GetPointer(ionScriptDataOffset); 166 MOZ_ASSERT(ionScript->containsReturnAddress(returnAddr)); 167 *ionScriptOut = ionScript; 168 return true; 169 } 170 171 CalleeToken 172 JitFrameIterator::calleeToken() const 173 { 174 return ((JitFrameLayout*) current_)->calleeToken(); 175 } 176 177 JSFunction* 178 JitFrameIterator::callee() const 179 { 180 MOZ_ASSERT(isScripted()); 181 MOZ_ASSERT(isFunctionFrame()); 182 return CalleeTokenToFunction(calleeToken()); 183 } 184 185 JSFunction* 186 JitFrameIterator::maybeCallee() const 187 { 188 if (isScripted() && (isFunctionFrame())) 189 return callee(); 190 return nullptr; 191 } 192 193 bool 194 JitFrameIterator::isBareExit() const 195 { 196 if (type_ != JitFrame_Exit) 197 return false; 198 return exitFrame()->isBareExit(); 199 } 200 201 bool 202 JitFrameIterator::isFunctionFrame() const 203 { 204 return CalleeTokenIsFunction(calleeToken()); 205 } 206 207 JSScript* 208 JitFrameIterator::script() const 209 { 210 MOZ_ASSERT(isScripted()); 211 if (isBaselineJS()) 212 return baselineFrame()->script(); 213 JSScript* script = ScriptFromCalleeToken(calleeToken()); 214 MOZ_ASSERT(script); 215 return script; 216 } 217 218 void 219 JitFrameIterator::baselineScriptAndPc(JSScript** scriptRes, jsbytecode** pcRes) const 220 { 221 MOZ_ASSERT(isBaselineJS()); 222 JSScript* script = this->script(); 223 if (scriptRes) 224 *scriptRes = script; 225 226 MOZ_ASSERT(pcRes); 227 228 // Use the frame's override pc, if we have one. This should only happen 229 // when we're in FinishBailoutToBaseline, handling an exception or toggling 230 // debug mode. 231 if (jsbytecode* overridePc = baselineFrame()->maybeOverridePc()) { 232 *pcRes = overridePc; 233 return; 234 } 235 236 // Else, there must be an ICEntry for the current return address. 237 uint8_t* retAddr = returnAddressToFp(); 238 ICEntry& icEntry = script->baselineScript()->icEntryFromReturnAddress(retAddr); 239 *pcRes = icEntry.pc(script); 240 } 241 242 Value* 243 JitFrameIterator::actualArgs() const 244 { 245 return jsFrame()->argv() + 1; 246 } 247 248 uint8_t* 249 JitFrameIterator::prevFp() const 250 { 251 return current_ + current()->prevFrameLocalSize() + current()->headerSize(); 252 } 253 254 JitFrameIterator& 255 JitFrameIterator::operator++() 256 { 257 MOZ_ASSERT(type_ != JitFrame_Entry); 258 259 frameSize_ = prevFrameLocalSize(); 260 cachedSafepointIndex_ = nullptr; 261 262 // If the next frame is the entry frame, just exit. Don't update current_, 263 // since the entry and first frames overlap. 264 if (current()->prevType() == JitFrame_Entry) { 265 type_ = JitFrame_Entry; 266 return *this; 267 } 268 269 type_ = current()->prevType(); 270 returnAddressToFp_ = current()->returnAddress(); 271 current_ = prevFp(); 272 273 return *this; 274 } 275 276 uintptr_t* 277 JitFrameIterator::spillBase() const 278 { 279 MOZ_ASSERT(isIonJS()); 280 281 // Get the base address to where safepoint registers are spilled. 282 // Out-of-line calls do not unwind the extra padding space used to 283 // aggregate bailout tables, so we use frameSize instead of frameLocals, 284 // which would only account for local stack slots. 285 return reinterpret_cast<uintptr_t*>(fp() - ionScript()->frameSize()); 286 } 287 288 MachineState 289 JitFrameIterator::machineState() const 290 { 291 MOZ_ASSERT(isIonScripted()); 292 293 // The MachineState is used by GCs for marking call-sites. 294 if (MOZ_UNLIKELY(isBailoutJS())) 295 return *activation_->bailoutData()->machineState(); 296 297 SafepointReader reader(ionScript(), safepoint()); 298 uintptr_t* spill = spillBase(); 299 MachineState machine; 300 301 for (GeneralRegisterBackwardIterator iter(reader.allGprSpills()); iter.more(); ++iter) 302 machine.setRegisterLocation(*iter, --spill); 303 304 uint8_t* spillAlign = alignDoubleSpillWithOffset(reinterpret_cast<uint8_t*>(spill), 0); 305 306 char* floatSpill = reinterpret_cast<char*>(spillAlign); 307 FloatRegisterSet fregs = reader.allFloatSpills().set(); 308 fregs = fregs.reduceSetForPush(); 309 for (FloatRegisterBackwardIterator iter(fregs); iter.more(); ++iter) { 310 floatSpill -= (*iter).size(); 311 for (uint32_t a = 0; a < (*iter).numAlignedAliased(); a++) { 312 // Only say that registers that actually start here start here. 313 // e.g. d0 should not start at s1, only at s0. 314 FloatRegister ftmp; 315 (*iter).alignedAliased(a, &ftmp); 316 machine.setRegisterLocation(ftmp, (double*)floatSpill); 317 } 318 } 319 320 return machine; 321 } 322 323 static uint32_t 324 NumArgAndLocalSlots(const InlineFrameIterator& frame) 325 { 326 JSScript* script = frame.script(); 327 return CountArgSlots(script, frame.maybeCalleeTemplate()) + script->nfixed(); 328 } 329 330 static void 331 CloseLiveIteratorIon(JSContext* cx, const InlineFrameIterator& frame, uint32_t stackSlot) 332 { 333 SnapshotIterator si = frame.snapshotIterator(); 334 335 // Skip stack slots until we reach the iterator object. 336 uint32_t skipSlots = NumArgAndLocalSlots(frame) + stackSlot - 1; 337 338 for (unsigned i = 0; i < skipSlots; i++) 339 si.skip(); 340 341 Value v = si.read(); 342 RootedObject obj(cx, &v.toObject()); 343 344 if (cx->isExceptionPending()) 345 UnwindIteratorForException(cx, obj); 346 else 347 UnwindIteratorForUncatchableException(cx, obj); 348 } 349 350 class IonFrameStackDepthOp 351 { 352 uint32_t depth_; 353 354 public: 355 explicit IonFrameStackDepthOp(const InlineFrameIterator& frame) { 356 uint32_t base = NumArgAndLocalSlots(frame); 357 SnapshotIterator si = frame.snapshotIterator(); 358 MOZ_ASSERT(si.numAllocations() >= base); 359 depth_ = si.numAllocations() - base; 360 } 361 362 uint32_t operator()() { return depth_; } 363 }; 364 365 class TryNoteIterIon : public TryNoteIter<IonFrameStackDepthOp> 366 { 367 public: 368 TryNoteIterIon(JSContext* cx, const InlineFrameIterator& frame) 369 : TryNoteIter(cx, frame.script(), frame.pc(), IonFrameStackDepthOp(frame)) 370 { } 371 }; 372 373 static void 374 HandleExceptionIon(JSContext* cx, const InlineFrameIterator& frame, ResumeFromException* rfe, 375 bool* overrecursed) 376 { 377 if (cx->compartment()->isDebuggee()) { 378 // We need to bail when there is a catchable exception, and we are the 379 // debuggee of a Debugger with a live onExceptionUnwind hook, or if a 380 // Debugger has observed this frame (e.g., for onPop). 381 bool shouldBail = Debugger::hasLiveHook(cx->global(), Debugger::OnExceptionUnwind); 382 RematerializedFrame* rematFrame = nullptr; 383 if (!shouldBail) { 384 JitActivation* act = cx->runtime()->activation()->asJit(); 385 rematFrame = act->lookupRematerializedFrame(frame.frame().fp(), frame.frameNo()); 386 shouldBail = rematFrame && rematFrame->isDebuggee(); 387 } 388 389 if (shouldBail) { 390 // If we have an exception from within Ion and the debugger is active, 391 // we do the following: 392 // 393 // 1. Bailout to baseline to reconstruct a baseline frame. 394 // 2. Resume immediately into the exception tail afterwards, and 395 // handle the exception again with the top frame now a baseline 396 // frame. 397 // 398 // An empty exception info denotes that we're propagating an Ion 399 // exception due to debug mode, which BailoutIonToBaseline needs to 400 // know. This is because we might not be able to fully reconstruct up 401 // to the stack depth at the snapshot, as we could've thrown in the 402 // middle of a call. 403 ExceptionBailoutInfo propagateInfo; 404 uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, propagateInfo, overrecursed); 405 if (retval == BAILOUT_RETURN_OK) 406 return; 407 } 408 409 MOZ_ASSERT_IF(rematFrame, !Debugger::inFrameMaps(rematFrame)); 410 } 411 412 RootedScript script(cx, frame.script()); 413 if (!script->hasTrynotes()) 414 return; 415 416 for (TryNoteIterIon tni(cx, frame); !tni.done(); ++tni) { 417 JSTryNote* tn = *tni; 418 419 switch (tn->kind) { 420 case JSTRY_FOR_IN: { 421 MOZ_ASSERT(JSOp(*(script->main() + tn->start + tn->length)) == JSOP_ENDITER); 422 MOZ_ASSERT(tn->stackDepth > 0); 423 424 uint32_t localSlot = tn->stackDepth; 425 CloseLiveIteratorIon(cx, frame, localSlot); 426 break; 427 } 428 429 case JSTRY_FOR_OF: 430 case JSTRY_LOOP: 431 break; 432 433 case JSTRY_CATCH: 434 if (cx->isExceptionPending()) { 435 // Ion can compile try-catch, but bailing out to catch 436 // exceptions is slow. Reset the warm-up counter so that if we 437 // catch many exceptions we won't Ion-compile the script. 438 script->resetWarmUpCounter(); 439 440 // Bailout at the start of the catch block. 441 jsbytecode* catchPC = script->main() + tn->start + tn->length; 442 ExceptionBailoutInfo excInfo(frame.frameNo(), catchPC, tn->stackDepth); 443 uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, excInfo, overrecursed); 444 if (retval == BAILOUT_RETURN_OK) 445 return; 446 447 // Error on bailout clears pending exception. 448 MOZ_ASSERT(!cx->isExceptionPending()); 449 } 450 break; 451 452 default: 453 MOZ_CRASH("Unexpected try note"); 454 } 455 } 456 } 457 458 static void 459 OnLeaveBaselineFrame(JSContext* cx, const JitFrameIterator& frame, jsbytecode* pc, 460 ResumeFromException* rfe, bool frameOk) 461 { 462 BaselineFrame* baselineFrame = frame.baselineFrame(); 463 if (jit::DebugEpilogue(cx, baselineFrame, pc, frameOk)) { 464 rfe->kind = ResumeFromException::RESUME_FORCED_RETURN; 465 rfe->framePointer = frame.fp() - BaselineFrame::FramePointerOffset; 466 rfe->stackPointer = reinterpret_cast<uint8_t*>(baselineFrame); 467 } 468 } 469 470 static inline void 471 ForcedReturn(JSContext* cx, const JitFrameIterator& frame, jsbytecode* pc, 472 ResumeFromException* rfe) 473 { 474 OnLeaveBaselineFrame(cx, frame, pc, rfe, true); 475 } 476 477 static inline void 478 BaselineFrameAndStackPointersFromTryNote(JSTryNote* tn, const JitFrameIterator& frame, 479 uint8_t** framePointer, uint8_t** stackPointer) 480 { 481 JSScript* script = frame.baselineFrame()->script(); 482 *framePointer = frame.fp() - BaselineFrame::FramePointerOffset; 483 *stackPointer = *framePointer - BaselineFrame::Size() - 484 (script->nfixed() + tn->stackDepth) * sizeof(Value); 485 } 486 487 static void 488 SettleOnTryNote(JSContext* cx, JSTryNote* tn, const JitFrameIterator& frame, 489 EnvironmentIter& ei, ResumeFromException* rfe, jsbytecode** pc) 490 { 491 RootedScript script(cx, frame.baselineFrame()->script()); 492 493 // Unwind environment chain (pop block objects). 494 if (cx->isExceptionPending()) 495 UnwindEnvironment(cx, ei, UnwindEnvironmentToTryPc(script, tn)); 496 497 // Compute base pointer and stack pointer. 498 BaselineFrameAndStackPointersFromTryNote(tn, frame, &rfe->framePointer, &rfe->stackPointer); 499 500 // Compute the pc. 501 *pc = script->main() + tn->start + tn->length; 502 } 503 504 struct AutoBaselineHandlingException 505 { 506 BaselineFrame* frame; 507 AutoBaselineHandlingException(BaselineFrame* frame, jsbytecode* pc) 508 : frame(frame) 509 { 510 frame->setIsHandlingException(); 511 frame->setOverridePc(pc); 512 } 513 ~AutoBaselineHandlingException() { 514 frame->unsetIsHandlingException(); 515 frame->clearOverridePc(); 516 } 517 }; 518 519 class BaselineFrameStackDepthOp 520 { 521 BaselineFrame* frame_; 522 public: 523 explicit BaselineFrameStackDepthOp(BaselineFrame* frame) 524 : frame_(frame) 525 { } 526 uint32_t operator()() { 527 MOZ_ASSERT(frame_->numValueSlots() >= frame_->script()->nfixed()); 528 return frame_->numValueSlots() - frame_->script()->nfixed(); 529 } 530 }; 531 532 class TryNoteIterBaseline : public TryNoteIter<BaselineFrameStackDepthOp> 533 { 534 public: 535 TryNoteIterBaseline(JSContext* cx, BaselineFrame* frame, jsbytecode* pc) 536 : TryNoteIter(cx, frame->script(), pc, BaselineFrameStackDepthOp(frame)) 537 { } 538 }; 539 540 // Close all live iterators on a BaselineFrame due to exception unwinding. The 541 // pc parameter is updated to where the envs have been unwound to. 542 static void 543 CloseLiveIteratorsBaselineForUncatchableException(JSContext* cx, const JitFrameIterator& frame, 544 jsbytecode* pc) 545 { 546 for (TryNoteIterBaseline tni(cx, frame.baselineFrame(), pc); !tni.done(); ++tni) { 547 JSTryNote* tn = *tni; 548 549 if (tn->kind == JSTRY_FOR_IN) { 550 uint8_t* framePointer; 551 uint8_t* stackPointer; 552 BaselineFrameAndStackPointersFromTryNote(tn, frame, &framePointer, &stackPointer); 553 Value iterValue(*(Value*) stackPointer); 554 RootedObject iterObject(cx, &iterValue.toObject()); 555 UnwindIteratorForUncatchableException(cx, iterObject); 556 } 557 } 558 } 559 560 static bool 561 ProcessTryNotesBaseline(JSContext* cx, const JitFrameIterator& frame, EnvironmentIter& ei, 562 ResumeFromException* rfe, jsbytecode** pc) 563 { 564 RootedScript script(cx, frame.baselineFrame()->script()); 565 566 for (TryNoteIterBaseline tni(cx, frame.baselineFrame(), *pc); !tni.done(); ++tni) { 567 JSTryNote* tn = *tni; 568 569 MOZ_ASSERT(cx->isExceptionPending()); 570 switch (tn->kind) { 571 case JSTRY_CATCH: { 572 // If we're closing a legacy generator, we have to skip catch 573 // blocks. 574 if (cx->isClosingGenerator()) 575 continue; 576 577 SettleOnTryNote(cx, tn, frame, ei, rfe, pc); 578 579 // Ion can compile try-catch, but bailing out to catch 580 // exceptions is slow. Reset the warm-up counter so that if we 581 // catch many exceptions we won't Ion-compile the script. 582 script->resetWarmUpCounter(); 583 584 // Resume at the start of the catch block. 585 rfe->kind = ResumeFromException::RESUME_CATCH; 586 rfe->target = script->baselineScript()->nativeCodeForPC(script, *pc); 587 return true; 588 } 589 590 case JSTRY_FINALLY: { 591 SettleOnTryNote(cx, tn, frame, ei, rfe, pc); 592 rfe->kind = ResumeFromException::RESUME_FINALLY; 593 rfe->target = script->baselineScript()->nativeCodeForPC(script, *pc); 594 // Drop the exception instead of leaking cross compartment data. 595 if (!cx->getPendingException(MutableHandleValue::fromMarkedLocation(&rfe->exception))) 596 rfe->exception = UndefinedValue(); 597 cx->clearPendingException(); 598 return true; 599 } 600 601 case JSTRY_FOR_IN: { 602 uint8_t* framePointer; 603 uint8_t* stackPointer; 604 BaselineFrameAndStackPointersFromTryNote(tn, frame, &framePointer, &stackPointer); 605 Value iterValue(*(Value*) stackPointer); 606 RootedObject iterObject(cx, &iterValue.toObject()); 607 if (!UnwindIteratorForException(cx, iterObject)) { 608 // See comment in the JSTRY_FOR_IN case in Interpreter.cpp's 609 // ProcessTryNotes. 610 SettleOnTryNote(cx, tn, frame, ei, rfe, pc); 611 MOZ_ASSERT(**pc == JSOP_ENDITER); 612 return false; 613 } 614 break; 615 } 616 617 case JSTRY_FOR_OF: 618 case JSTRY_LOOP: 619 break; 620 621 default: 622 MOZ_CRASH("Invalid try note"); 623 } 624 } 625 return true; 626 } 627 628 static void 629 HandleExceptionBaseline(JSContext* cx, const JitFrameIterator& frame, ResumeFromException* rfe, 630 jsbytecode* pc) 631 { 632 MOZ_ASSERT(frame.isBaselineJS()); 633 634 bool frameOk = false; 635 RootedScript script(cx, frame.baselineFrame()->script()); 636 637 if (script->hasScriptCounts()) { 638 PCCounts* counts = script->getThrowCounts(pc); 639 // If we failed to allocate, then skip the increment and continue to 640 // handle the exception. 641 if (counts) 642 counts->numExec()++; 643 } 644 645 // We may be propagating a forced return from the interrupt 646 // callback, which cannot easily force a return. 647 if (cx->isPropagatingForcedReturn()) { 648 cx->clearPropagatingForcedReturn(); 649 ForcedReturn(cx, frame, pc, rfe); 650 return; 651 } 652 653 again: 654 if (cx->isExceptionPending()) { 655 if (!cx->isClosingGenerator()) { 656 switch (Debugger::onExceptionUnwind(cx, frame.baselineFrame())) { 657 case JSTRAP_ERROR: 658 // Uncatchable exception. 659 MOZ_ASSERT(!cx->isExceptionPending()); 660 goto again; 661 662 case JSTRAP_CONTINUE: 663 case JSTRAP_THROW: 664 MOZ_ASSERT(cx->isExceptionPending()); 665 break; 666 667 case JSTRAP_RETURN: 668 if (script->hasTrynotes()) 669 CloseLiveIteratorsBaselineForUncatchableException(cx, frame, pc); 670 ForcedReturn(cx, frame, pc, rfe); 671 return; 672 673 default: 674 MOZ_CRASH("Invalid trap status"); 675 } 676 } 677 678 if (script->hasTrynotes()) { 679 EnvironmentIter ei(cx, frame.baselineFrame(), pc); 680 if (!ProcessTryNotesBaseline(cx, frame, ei, rfe, &pc)) 681 goto again; 682 if (rfe->kind != ResumeFromException::RESUME_ENTRY_FRAME) { 683 // No need to increment the PCCounts number of execution here, 684 // as the interpreter increments any PCCounts if present. 685 MOZ_ASSERT_IF(script->hasScriptCounts(), script->maybeGetPCCounts(pc)); 686 return; 687 } 688 } 689 690 frameOk = HandleClosingGeneratorReturn(cx, frame.baselineFrame(), frameOk); 691 frameOk = Debugger::onLeaveFrame(cx, frame.baselineFrame(), pc, frameOk); 692 } else if (script->hasTrynotes()) { 693 CloseLiveIteratorsBaselineForUncatchableException(cx, frame, pc); 694 } 695 696 OnLeaveBaselineFrame(cx, frame, pc, rfe, frameOk); 697 } 698 699 struct AutoDeleteDebugModeOSRInfo 700 { 701 BaselineFrame* frame; 702 explicit AutoDeleteDebugModeOSRInfo(BaselineFrame* frame) : frame(frame) { MOZ_ASSERT(frame); } 703 ~AutoDeleteDebugModeOSRInfo() { frame->deleteDebugModeOSRInfo(); } 704 }; 705 706 struct AutoResetLastProfilerFrameOnReturnFromException 707 { 708 JSContext* cx; 709 ResumeFromException* rfe; 710 711 AutoResetLastProfilerFrameOnReturnFromException(JSContext* cx, ResumeFromException* rfe) 712 : cx(cx), rfe(rfe) {} 713 714 ~AutoResetLastProfilerFrameOnReturnFromException() { 715 if (!cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime())) 716 return; 717 718 MOZ_ASSERT(cx->runtime()->jitActivation == cx->runtime()->profilingActivation()); 719 720 void* lastProfilingFrame = getLastProfilingFrame(); 721 cx->runtime()->jitActivation->setLastProfilingFrame(lastProfilingFrame); 722 } 723 724 void* getLastProfilingFrame() { 725 switch (rfe->kind) { 726 case ResumeFromException::RESUME_ENTRY_FRAME: 727 return nullptr; 728 729 // The following all return into baseline frames. 730 case ResumeFromException::RESUME_CATCH: 731 case ResumeFromException::RESUME_FINALLY: 732 case ResumeFromException::RESUME_FORCED_RETURN: 733 return rfe->framePointer + BaselineFrame::FramePointerOffset; 734 735 // When resuming into a bailed-out ion frame, use the bailout info to 736 // find the frame we are resuming into. 737 case ResumeFromException::RESUME_BAILOUT: 738 return rfe->bailoutInfo->incomingStack; 739 } 740 741 MOZ_CRASH("Invalid ResumeFromException type!"); 742 return nullptr; 743 } 744 }; 745 746 void 747 HandleException(ResumeFromException* rfe) 748 { 749 JSContext* cx = GetJSContextFromMainThread(); 750 TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime()); 751 752 AutoResetLastProfilerFrameOnReturnFromException profFrameReset(cx, rfe); 753 754 rfe->kind = ResumeFromException::RESUME_ENTRY_FRAME; 755 756 JitSpew(JitSpew_IonInvalidate, "handling exception"); 757 758 // Clear any Ion return override that's been set. 759 // This may happen if a callVM function causes an invalidation (setting the 760 // override), and then fails, bypassing the bailout handlers that would 761 // otherwise clear the return override. 762 if (cx->runtime()->jitRuntime()->hasIonReturnOverride()) 763 cx->runtime()->jitRuntime()->takeIonReturnOverride(); 764 765 JitActivation* activation = cx->runtime()->activation()->asJit(); 766 767 #ifdef CHECK_OSIPOINT_REGISTERS 768 if (JitOptions.checkOsiPointRegisters) 769 activation->setCheckRegs(false); 770 #endif 771 772 // The Debugger onExceptionUnwind hook (reachable via 773 // HandleExceptionBaseline below) may cause on-stack recompilation of 774 // baseline scripts, which may patch return addresses on the stack. Since 775 // JitFrameIterators cache the previous frame's return address when 776 // iterating, we need a variant here that is automatically updated should 777 // on-stack recompilation occur. 778 DebugModeOSRVolatileJitFrameIterator iter(cx); 779 while (!iter.isEntry()) { 780 bool overrecursed = false; 781 if (iter.isIonJS()) { 782 // Search each inlined frame for live iterator objects, and close 783 // them. 784 InlineFrameIterator frames(cx, &iter); 785 786 // Invalidation state will be the same for all inlined scripts in the frame. 787 IonScript* ionScript = nullptr; 788 bool invalidated = iter.checkInvalidation(&ionScript); 789 790 #ifdef JS_TRACE_LOGGING 791 if (logger && cx->compartment()->isDebuggee() && logger->enabled()) { 792 logger->disable(/* force = */ true, 793 "Forcefully disabled tracelogger, due to " 794 "throwing an exception with an active Debugger " 795 "in IonMonkey."); 796 } 797 #endif 798 799 for (;;) { 800 HandleExceptionIon(cx, frames, rfe, &overrecursed); 801 802 if (rfe->kind == ResumeFromException::RESUME_BAILOUT) { 803 if (invalidated) 804 ionScript->decrementInvalidationCount(cx->runtime()->defaultFreeOp()); 805 return; 806 } 807 808 MOZ_ASSERT(rfe->kind == ResumeFromException::RESUME_ENTRY_FRAME); 809 810 // When profiling, each frame popped needs a notification that 811 // the function has exited, so invoke the probe that a function 812 // is exiting. 813 814 JSScript* script = frames.script(); 815 probes::ExitScript(cx, script, script->functionNonDelazifying(), 816 /* popSPSFrame = */ false); 817 if (!frames.more()) { 818 TraceLogStopEvent(logger, TraceLogger_IonMonkey); 819 TraceLogStopEvent(logger, TraceLogger_Scripts); 820 break; 821 } 822 ++frames; 823 } 824 825 activation->removeIonFrameRecovery(iter.jsFrame()); 826 if (invalidated) 827 ionScript->decrementInvalidationCount(cx->runtime()->defaultFreeOp()); 828 829 } else if (iter.isBaselineJS()) { 830 // Set a flag on the frame to signal to DebugModeOSR that we're 831 // handling an exception. Also ensure the frame has an override 832 // pc. We clear the frame's override pc when we leave this block, 833 // this is fine because we're either: 834 // 835 // (1) Going to enter a catch or finally block. We don't want to 836 // keep the old pc when we're executing JIT code. 837 // (2) Going to pop the frame, either here or a forced return. 838 // In this case nothing will observe the frame's pc. 839 // (3) Performing an exception bailout. In this case 840 // FinishBailoutToBaseline will set the pc to the resume pc 841 // and clear it before it returns to JIT code. 842 jsbytecode* pc; 843 iter.baselineScriptAndPc(nullptr, &pc); 844 AutoBaselineHandlingException handlingException(iter.baselineFrame(), pc); 845 846 HandleExceptionBaseline(cx, iter, rfe, pc); 847 848 // If we are propagating an exception through a frame with 849 // on-stack recompile info, we should free the allocated 850 // RecompileInfo struct before we leave this block, as we will not 851 // be returning to the recompile handler. 852 AutoDeleteDebugModeOSRInfo deleteDebugModeOSRInfo(iter.baselineFrame()); 853 854 if (rfe->kind != ResumeFromException::RESUME_ENTRY_FRAME && 855 rfe->kind != ResumeFromException::RESUME_FORCED_RETURN) 856 { 857 return; 858 } 859 860 TraceLogStopEvent(logger, TraceLogger_Baseline); 861 TraceLogStopEvent(logger, TraceLogger_Scripts); 862 863 // Unwind profiler pseudo-stack 864 JSScript* script = iter.script(); 865 probes::ExitScript(cx, script, script->functionNonDelazifying(), 866 /* popSPSFrame = */ false); 867 868 if (rfe->kind == ResumeFromException::RESUME_FORCED_RETURN) 869 return; 870 } 871 872 JitFrameLayout* current = iter.isScripted() ? iter.jsFrame() : nullptr; 873 874 ++iter; 875 876 if (current) { 877 // Unwind the frame by updating jitTop. This is necessary so that 878 // (1) debugger exception unwind and leave frame hooks don't see this 879 // frame when they use ScriptFrameIter, and (2) ScriptFrameIter does 880 // not crash when accessing an IonScript that's destroyed by the 881 // ionScript->decref call. 882 EnsureBareExitFrame(cx, current); 883 } 884 885 if (overrecursed) { 886 // We hit an overrecursion error during bailout. Report it now. 887 ReportOverRecursed(cx); 888 } 889 } 890 891 rfe->stackPointer = iter.fp(); 892 } 893 894 // Turns a JitFrameLayout into an ExitFrameLayout. Note that it has to be a 895 // bare exit frame so it's ignored by MarkJitExitFrame. 896 void 897 EnsureBareExitFrame(JSContext* cx, JitFrameLayout* frame) 898 { 899 ExitFrameLayout* exitFrame = reinterpret_cast<ExitFrameLayout*>(frame); 900 901 if (cx->runtime()->jitTop == (uint8_t*)frame) { 902 // If we already called this function for the current frame, do 903 // nothing. 904 MOZ_ASSERT(exitFrame->isBareExit()); 905 return; 906 } 907 908 #ifdef DEBUG 909 JitFrameIterator iter(cx); 910 while (!iter.isScripted()) 911 ++iter; 912 MOZ_ASSERT(iter.current() == frame, "|frame| must be the top JS frame"); 913 914 MOZ_ASSERT((uint8_t*)exitFrame->footer() >= cx->runtime()->jitTop, 915 "Must have space for ExitFooterFrame before jitTop"); 916 #endif 917 918 cx->runtime()->jitTop = (uint8_t*)frame; 919 *exitFrame->footer()->addressOfJitCode() = ExitFrameLayout::BareToken(); 920 MOZ_ASSERT(exitFrame->isBareExit()); 921 } 922 923 CalleeToken 924 MarkCalleeToken(JSTracer* trc, CalleeToken token) 925 { 926 switch (CalleeTokenTag tag = GetCalleeTokenTag(token)) { 927 case CalleeToken_Function: 928 case CalleeToken_FunctionConstructing: 929 { 930 JSFunction* fun = CalleeTokenToFunction(token); 931 TraceRoot(trc, &fun, "jit-callee"); 932 return CalleeToToken(fun, tag == CalleeToken_FunctionConstructing); 933 } 934 case CalleeToken_Script: 935 { 936 JSScript* script = CalleeTokenToScript(token); 937 TraceRoot(trc, &script, "jit-script"); 938 return CalleeToToken(script); 939 } 940 default: 941 MOZ_CRASH("unknown callee token type"); 942 } 943 } 944 945 uintptr_t* 946 JitFrameLayout::slotRef(SafepointSlotEntry where) 947 { 948 if (where.stack) 949 return (uintptr_t*)((uint8_t*)this - where.slot); 950 return (uintptr_t*)((uint8_t*)argv() + where.slot); 951 } 952 953 #ifdef JS_NUNBOX32 954 static inline uintptr_t 955 ReadAllocation(const JitFrameIterator& frame, const LAllocation* a) 956 { 957 if (a->isGeneralReg()) { 958 Register reg = a->toGeneralReg()->reg(); 959 return frame.machineState().read(reg); 960 } 961 return *frame.jsFrame()->slotRef(SafepointSlotEntry(a)); 962 } 963 #endif 964 965 static void 966 MarkThisAndArguments(JSTracer* trc, const JitFrameIterator& frame) 967 { 968 // Mark |this| and any extra actual arguments for an Ion frame. Marking of 969 // formal arguments is taken care of by the frame's safepoint/snapshot, 970 // except when the script might have lazy arguments or rest, in which case 971 // we mark them as well. We also have to mark formals if we have a LazyLink 972 // frame. 973 974 JitFrameLayout* layout = frame.isExitFrameLayout<LazyLinkExitFrameLayout>() 975 ? frame.exitFrame()->as<LazyLinkExitFrameLayout>()->jsFrame() 976 : frame.jsFrame(); 977 978 if (!CalleeTokenIsFunction(layout->calleeToken())) 979 return; 980 981 size_t nargs = layout->numActualArgs(); 982 size_t nformals = 0; 983 984 JSFunction* fun = CalleeTokenToFunction(layout->calleeToken()); 985 if (!frame.isExitFrameLayout<LazyLinkExitFrameLayout>() && 986 !fun->nonLazyScript()->mayReadFrameArgsDirectly()) 987 { 988 nformals = fun->nargs(); 989 } 990 991 size_t newTargetOffset = Max(nargs, fun->nargs()); 992 993 Value* argv = layout->argv(); 994 995 // Trace |this|. 996 TraceRoot(trc, argv, "ion-thisv"); 997 998 // Trace actual arguments beyond the formals. Note + 1 for thisv. 999 for (size_t i = nformals + 1; i < nargs + 1; i++) 1000 TraceRoot(trc, &argv[i], "ion-argv"); 1001 1002 // Always mark the new.target from the frame. It's not in the snapshots. 1003 // +1 to pass |this| 1004 if (CalleeTokenIsConstructing(layout->calleeToken())) 1005 TraceRoot(trc, &argv[1 + newTargetOffset], "ion-newTarget"); 1006 } 1007 1008 #ifdef JS_NUNBOX32 1009 static inline void 1010 WriteAllocation(const JitFrameIterator& frame, const LAllocation* a, uintptr_t value) 1011 { 1012 if (a->isGeneralReg()) { 1013 Register reg = a->toGeneralReg()->reg(); 1014 frame.machineState().write(reg, value); 1015 } else { 1016 *frame.jsFrame()->slotRef(SafepointSlotEntry(a)) = value; 1017 } 1018 } 1019 #endif 1020 1021 static void 1022 MarkIonJSFrame(JSTracer* trc, const JitFrameIterator& frame) 1023 { 1024 JitFrameLayout* layout = (JitFrameLayout*)frame.fp(); 1025 1026 layout->replaceCalleeToken(MarkCalleeToken(trc, layout->calleeToken())); 1027 1028 IonScript* ionScript = nullptr; 1029 if (frame.checkInvalidation(&ionScript)) { 1030 // This frame has been invalidated, meaning that its IonScript is no 1031 // longer reachable through the callee token (JSFunction/JSScript->ion 1032 // is now nullptr or recompiled). Manually trace it here. 1033 IonScript::Trace(trc, ionScript); 1034 } else { 1035 ionScript = frame.ionScriptFromCalleeToken(); 1036 } 1037 1038 MarkThisAndArguments(trc, frame); 1039 1040 const SafepointIndex* si = ionScript->getSafepointIndex(frame.returnAddressToFp()); 1041 1042 SafepointReader safepoint(ionScript, si); 1043 1044 // Scan through slots which contain pointers (or on punboxing systems, 1045 // actual values). 1046 SafepointSlotEntry entry; 1047 1048 while (safepoint.getGcSlot(&entry)) { 1049 uintptr_t* ref = layout->slotRef(entry); 1050 TraceGenericPointerRoot(trc, reinterpret_cast<gc::Cell**>(ref), "ion-gc-slot"); 1051 } 1052 1053 while (safepoint.getValueSlot(&entry)) { 1054 Value* v = (Value*)layout->slotRef(entry); 1055 TraceRoot(trc, v, "ion-gc-slot"); 1056 } 1057 1058 uintptr_t* spill = frame.spillBase(); 1059 LiveGeneralRegisterSet gcRegs = safepoint.gcSpills(); 1060 LiveGeneralRegisterSet valueRegs = safepoint.valueSpills(); 1061 for (GeneralRegisterBackwardIterator iter(safepoint.allGprSpills()); iter.more(); ++iter) { 1062 --spill; 1063 if (gcRegs.has(*iter)) 1064 TraceGenericPointerRoot(trc, reinterpret_cast<gc::Cell**>(spill), "ion-gc-spill"); 1065 else if (valueRegs.has(*iter)) 1066 TraceRoot(trc, reinterpret_cast<Value*>(spill), "ion-value-spill"); 1067 } 1068 1069 #ifdef JS_NUNBOX32 1070 LAllocation type, payload; 1071 while (safepoint.getNunboxSlot(&type, &payload)) { 1072 JSValueTag tag = JSValueTag(ReadAllocation(frame, &type)); 1073 uintptr_t rawPayload = ReadAllocation(frame, &payload); 1074 1075 Value v = Value::fromTagAndPayload(tag, rawPayload); 1076 TraceRoot(trc, &v, "ion-torn-value"); 1077 1078 if (v != Value::fromTagAndPayload(tag, rawPayload)) { 1079 // GC moved the value, replace the stored payload. 1080 rawPayload = *v.payloadUIntPtr(); 1081 WriteAllocation(frame, &payload, rawPayload); 1082 } 1083 } 1084 #endif 1085 } 1086 1087 static void 1088 MarkBailoutFrame(JSTracer* trc, const JitFrameIterator& frame) 1089 { 1090 JitFrameLayout* layout = (JitFrameLayout*)frame.fp(); 1091 1092 layout->replaceCalleeToken(MarkCalleeToken(trc, layout->calleeToken())); 1093 1094 // We have to mark the list of actual arguments, as only formal arguments 1095 // are represented in the Snapshot. 1096 MarkThisAndArguments(trc, frame); 1097 1098 // Under a bailout, do not have a Safepoint to only iterate over GC-things. 1099 // Thus we use a SnapshotIterator to trace all the locations which would be 1100 // used to reconstruct the Baseline frame. 1101 // 1102 // Note that at the time where this function is called, we have not yet 1103 // started to reconstruct baseline frames. 1104 1105 // The vector of recover instructions is already traced as part of the 1106 // JitActivation. 1107 SnapshotIterator snapIter(frame, frame.activation()->bailoutData()->machineState()); 1108 1109 // For each instruction, we read the allocations without evaluating the 1110 // recover instruction, nor reconstructing the frame. We are only looking at 1111 // tracing readable allocations. 1112 while (true) { 1113 while (snapIter.moreAllocations()) 1114 snapIter.traceAllocation(trc); 1115 1116 if (!snapIter.moreInstructions()) 1117 break; 1118 snapIter.nextInstruction(); 1119 } 1120 1121 } 1122 1123 void 1124 UpdateIonJSFrameForMinorGC(JSTracer* trc, const JitFrameIterator& frame) 1125 { 1126 // Minor GCs may move slots/elements allocated in the nursery. Update 1127 // any slots/elements pointers stored in this frame. 1128 1129 JitFrameLayout* layout = (JitFrameLayout*)frame.fp(); 1130 1131 IonScript* ionScript = nullptr; 1132 if (frame.checkInvalidation(&ionScript)) { 1133 // This frame has been invalidated, meaning that its IonScript is no 1134 // longer reachable through the callee token (JSFunction/JSScript->ion 1135 // is now nullptr or recompiled). 1136 } else { 1137 ionScript = frame.ionScriptFromCalleeToken(); 1138 } 1139 1140 Nursery& nursery = trc->runtime()->gc.nursery; 1141 1142 const SafepointIndex* si = ionScript->getSafepointIndex(frame.returnAddressToFp()); 1143 SafepointReader safepoint(ionScript, si); 1144 1145 LiveGeneralRegisterSet slotsRegs = safepoint.slotsOrElementsSpills(); 1146 uintptr_t* spill = frame.spillBase(); 1147 for (GeneralRegisterBackwardIterator iter(safepoint.allGprSpills()); iter.more(); ++iter) { 1148 --spill; 1149 if (slotsRegs.has(*iter)) 1150 nursery.forwardBufferPointer(reinterpret_cast<HeapSlot**>(spill)); 1151 } 1152 1153 // Skip to the right place in the safepoint 1154 SafepointSlotEntry entry; 1155 while (safepoint.getGcSlot(&entry)); 1156 while (safepoint.getValueSlot(&entry)); 1157 #ifdef JS_NUNBOX32 1158 LAllocation type, payload; 1159 while (safepoint.getNunboxSlot(&type, &payload)); 1160 #endif 1161 1162 while (safepoint.getSlotsOrElementsSlot(&entry)) { 1163 HeapSlot** slots = reinterpret_cast<HeapSlot**>(layout->slotRef(entry)); 1164 nursery.forwardBufferPointer(slots); 1165 } 1166 } 1167 1168 static void 1169 MarkJitStubFrame(JSTracer* trc, const JitFrameIterator& frame) 1170 { 1171 // Mark the ICStub pointer stored in the stub frame. This is necessary 1172 // so that we don't destroy the stub code after unlinking the stub. 1173 1174 MOZ_ASSERT(frame.type() == JitFrame_IonStub || frame.type() == JitFrame_BaselineStub); 1175 JitStubFrameLayout* layout = (JitStubFrameLayout*)frame.fp(); 1176 1177 if (ICStub* stub = layout->maybeStubPtr()) { 1178 MOZ_ASSERT(ICStub::CanMakeCalls(stub->kind())); 1179 stub->trace(trc); 1180 } 1181 } 1182 1183 static void 1184 MarkIonAccessorICFrame(JSTracer* trc, const JitFrameIterator& frame) 1185 { 1186 MOZ_ASSERT(frame.type() == JitFrame_IonAccessorIC); 1187 IonAccessorICFrameLayout* layout = (IonAccessorICFrameLayout*)frame.fp(); 1188 TraceRoot(trc, layout->stubCode(), "ion-ic-accessor-code"); 1189 } 1190 1191 #ifdef JS_CODEGEN_MIPS32 1192 uint8_t* 1193 alignDoubleSpillWithOffset(uint8_t* pointer, int32_t offset) 1194 { 1195 uint32_t address = reinterpret_cast<uint32_t>(pointer); 1196 address = (address - offset) & ~(ABIStackAlignment - 1); 1197 return reinterpret_cast<uint8_t*>(address); 1198 } 1199 1200 static void 1201 MarkJitExitFrameCopiedArguments(JSTracer* trc, const VMFunction* f, ExitFooterFrame* footer) 1202 { 1203 uint8_t* doubleArgs = reinterpret_cast<uint8_t*>(footer); 1204 doubleArgs = alignDoubleSpillWithOffset(doubleArgs, sizeof(intptr_t)); 1205 if (f->outParam == Type_Handle) 1206 doubleArgs -= sizeof(Value); 1207 doubleArgs -= f->doubleByRefArgs() * sizeof(double); 1208 1209 for (uint32_t explicitArg = 0; explicitArg < f->explicitArgs; explicitArg++) { 1210 if (f->argProperties(explicitArg) == VMFunction::DoubleByRef) { 1211 // Arguments with double size can only have RootValue type. 1212 if (f->argRootType(explicitArg) == VMFunction::RootValue) 1213 TraceRoot(trc, reinterpret_cast<Value*>(doubleArgs), "ion-vm-args"); 1214 else 1215 MOZ_ASSERT(f->argRootType(explicitArg) == VMFunction::RootNone); 1216 doubleArgs += sizeof(double); 1217 } 1218 } 1219 } 1220 #else 1221 static void 1222 MarkJitExitFrameCopiedArguments(JSTracer* trc, const VMFunction* f, ExitFooterFrame* footer) 1223 { 1224 // This is NO-OP on other platforms. 1225 } 1226 #endif 1227 1228 static void 1229 MarkJitExitFrame(JSTracer* trc, const JitFrameIterator& frame) 1230 { 1231 ExitFooterFrame* footer = frame.exitFrame()->footer(); 1232 1233 // Mark the code of the code handling the exit path. This is needed because 1234 // invalidated script are no longer marked because data are erased by the 1235 // invalidation and relocation data are no longer reliable. So the VM 1236 // wrapper or the invalidation code may be GC if no JitCode keep reference 1237 // on them. 1238 MOZ_ASSERT(uintptr_t(footer->jitCode()) != uintptr_t(-1)); 1239 1240 // This corresponds to the case where we have build a fake exit frame which 1241 // handles the case of a native function call. We need to mark the argument 1242 // vector of the function call, and also new.target if it was a constructing 1243 // call. 1244 if (frame.isExitFrameLayout<NativeExitFrameLayout>()) { 1245 NativeExitFrameLayout* native = frame.exitFrame()->as<NativeExitFrameLayout>(); 1246 size_t len = native->argc() + 2; 1247 Value* vp = native->vp(); 1248 TraceRootRange(trc, len, vp, "ion-native-args"); 1249 if (frame.isExitFrameLayout<ConstructNativeExitFrameLayout>()) 1250 TraceRoot(trc, vp + len, "ion-native-new-target"); 1251 return; 1252 } 1253 1254 if (frame.isExitFrameLayout<IonOOLNativeExitFrameLayout>()) { 1255 IonOOLNativeExitFrameLayout* oolnative = 1256 frame.exitFrame()->as<IonOOLNativeExitFrameLayout>(); 1257 TraceRoot(trc, oolnative->stubCode(), "ion-ool-native-code"); 1258 TraceRoot(trc, oolnative->vp(), "iol-ool-native-vp"); 1259 size_t len = oolnative->argc() + 1; 1260 TraceRootRange(trc, len, oolnative->thisp(), "ion-ool-native-thisargs"); 1261 return; 1262 } 1263 1264 if (frame.isExitFrameLayout<IonOOLPropertyOpExitFrameLayout>() || 1265 frame.isExitFrameLayout<IonOOLSetterOpExitFrameLayout>()) 1266 { 1267 // A SetterOp frame is a different size, but that's the only relevant 1268 // difference between the two. The fields that need marking are all in 1269 // the common base class. 1270 IonOOLPropertyOpExitFrameLayout* oolgetter = 1271 frame.isExitFrameLayout<IonOOLPropertyOpExitFrameLayout>() 1272 ? frame.exitFrame()->as<IonOOLPropertyOpExitFrameLayout>() 1273 : frame.exitFrame()->as<IonOOLSetterOpExitFrameLayout>(); 1274 TraceRoot(trc, oolgetter->stubCode(), "ion-ool-property-op-code"); 1275 TraceRoot(trc, oolgetter->vp(), "ion-ool-property-op-vp"); 1276 TraceRoot(trc, oolgetter->id(), "ion-ool-property-op-id"); 1277 TraceRoot(trc, oolgetter->obj(), "ion-ool-property-op-obj"); 1278 return; 1279 } 1280 1281 if (frame.isExitFrameLayout<IonOOLProxyExitFrameLayout>()) { 1282 IonOOLProxyExitFrameLayout* oolproxy = frame.exitFrame()->as<IonOOLProxyExitFrameLayout>(); 1283 TraceRoot(trc, oolproxy->stubCode(), "ion-ool-proxy-code"); 1284 TraceRoot(trc, oolproxy->vp(), "ion-ool-proxy-vp"); 1285 TraceRoot(trc, oolproxy->id(), "ion-ool-proxy-id"); 1286 TraceRoot(trc, oolproxy->proxy(), "ion-ool-proxy-proxy"); 1287 return; 1288 } 1289 1290 if (frame.isExitFrameLayout<IonDOMExitFrameLayout>()) { 1291 IonDOMExitFrameLayout* dom = frame.exitFrame()->as<IonDOMExitFrameLayout>(); 1292 TraceRoot(trc, dom->thisObjAddress(), "ion-dom-args"); 1293 if (dom->isMethodFrame()) { 1294 IonDOMMethodExitFrameLayout* method = 1295 reinterpret_cast<IonDOMMethodExitFrameLayout*>(dom); 1296 size_t len = method->argc() + 2; 1297 Value* vp = method->vp(); 1298 TraceRootRange(trc, len, vp, "ion-dom-args"); 1299 } else { 1300 TraceRoot(trc, dom->vp(), "ion-dom-args"); 1301 } 1302 return; 1303 } 1304 1305 if (frame.isExitFrameLayout<LazyLinkExitFrameLayout>()) { 1306 LazyLinkExitFrameLayout* ll = frame.exitFrame()->as<LazyLinkExitFrameLayout>(); 1307 JitFrameLayout* layout = ll->jsFrame(); 1308 1309 TraceRoot(trc, ll->stubCode(), "lazy-link-code"); 1310 layout->replaceCalleeToken(MarkCalleeToken(trc, layout->calleeToken())); 1311 MarkThisAndArguments(trc, frame); 1312 return; 1313 } 1314 1315 if (frame.isBareExit()) { 1316 // Nothing to mark. Fake exit frame pushed for VM functions with 1317 // nothing to mark on the stack. 1318 return; 1319 } 1320 1321 TraceRoot(trc, footer->addressOfJitCode(), "ion-exit-code"); 1322 1323 const VMFunction* f = footer->function(); 1324 if (f == nullptr) 1325 return; 1326 1327 // Mark arguments of the VM wrapper. 1328 uint8_t* argBase = frame.exitFrame()->argBase(); 1329 for (uint32_t explicitArg = 0; explicitArg < f->explicitArgs; explicitArg++) { 1330 switch (f->argRootType(explicitArg)) { 1331 case VMFunction::RootNone: 1332 break; 1333 case VMFunction::RootObject: { 1334 // Sometimes we can bake in HandleObjects to nullptr. 1335 JSObject** pobj = reinterpret_cast<JSObject**>(argBase); 1336 if (*pobj) 1337 TraceRoot(trc, pobj, "ion-vm-args"); 1338 break; 1339 } 1340 case VMFunction::RootString: 1341 case VMFunction::RootPropertyName: 1342 TraceRoot(trc, reinterpret_cast<JSString**>(argBase), "ion-vm-args"); 1343 break; 1344 case VMFunction::RootFunction: 1345 TraceRoot(trc, reinterpret_cast<JSFunction**>(argBase), "ion-vm-args"); 1346 break; 1347 case VMFunction::RootValue: 1348 TraceRoot(trc, reinterpret_cast<Value*>(argBase), "ion-vm-args"); 1349 break; 1350 case VMFunction::RootCell: 1351 TraceGenericPointerRoot(trc, reinterpret_cast<gc::Cell**>(argBase), "ion-vm-args"); 1352 break; 1353 } 1354 1355 switch (f->argProperties(explicitArg)) { 1356 case VMFunction::WordByValue: 1357 case VMFunction::WordByRef: 1358 argBase += sizeof(void*); 1359 break; 1360 case VMFunction::DoubleByValue: 1361 case VMFunction::DoubleByRef: 1362 argBase += 2 * sizeof(void*); 1363 break; 1364 } 1365 } 1366 1367 if (f->outParam == Type_Handle) { 1368 switch (f->outParamRootType) { 1369 case VMFunction::RootNone: 1370 MOZ_CRASH("Handle outparam must have root type"); 1371 case VMFunction::RootObject: 1372 TraceRoot(trc, footer->outParam<JSObject*>(), "ion-vm-out"); 1373 break; 1374 case VMFunction::RootString: 1375 case VMFunction::RootPropertyName: 1376 TraceRoot(trc, footer->outParam<JSString*>(), "ion-vm-out"); 1377 break; 1378 case VMFunction::RootFunction: 1379 TraceRoot(trc, footer->outParam<JSFunction*>(), "ion-vm-out"); 1380 break; 1381 case VMFunction::RootValue: 1382 TraceRoot(trc, footer->outParam<Value>(), "ion-vm-outvp"); 1383 break; 1384 case VMFunction::RootCell: 1385 TraceGenericPointerRoot(trc, footer->outParam<gc::Cell*>(), "ion-vm-out"); 1386 break; 1387 } 1388 } 1389 1390 MarkJitExitFrameCopiedArguments(trc, f, footer); 1391 } 1392 1393 static void 1394 MarkRectifierFrame(JSTracer* trc, const JitFrameIterator& frame) 1395 { 1396 // Mark thisv. 1397 // 1398 // Baseline JIT code generated as part of the ICCall_Fallback stub may use 1399 // it if we're calling a constructor that returns a primitive value. 1400 RectifierFrameLayout* layout = (RectifierFrameLayout*)frame.fp(); 1401 TraceRoot(trc, &layout->argv()[0], "ion-thisv"); 1402 } 1403 1404 static void 1405 MarkJitActivation(JSTracer* trc, const JitActivationIterator& activations) 1406 { 1407 JitActivation* activation = activations->asJit(); 1408 1409 #ifdef CHECK_OSIPOINT_REGISTERS 1410 if (JitOptions.checkOsiPointRegisters) { 1411 // GC can modify spilled registers, breaking our register checks. 1412 // To handle this, we disable these checks for the current VM call 1413 // when a GC happens. 1414 activation->setCheckRegs(false); 1415 } 1416 #endif 1417 1418 activation->markRematerializedFrames(trc); 1419 activation->markIonRecovery(trc); 1420 1421 for (JitFrameIterator frames(activations); !frames.done(); ++frames) { 1422 switch (frames.type()) { 1423 case JitFrame_Exit: 1424 MarkJitExitFrame(trc, frames); 1425 break; 1426 case JitFrame_BaselineJS: 1427 frames.baselineFrame()->trace(trc, frames); 1428 break; 1429 case JitFrame_IonJS: 1430 MarkIonJSFrame(trc, frames); 1431 break; 1432 case JitFrame_BaselineStub: 1433 case JitFrame_IonStub: 1434 MarkJitStubFrame(trc, frames); 1435 break; 1436 case JitFrame_Bailout: 1437 MarkBailoutFrame(trc, frames); 1438 break; 1439 case JitFrame_Rectifier: 1440 MarkRectifierFrame(trc, frames); 1441 break; 1442 case JitFrame_IonAccessorIC: 1443 MarkIonAccessorICFrame(trc, frames); 1444 break; 1445 default: 1446 MOZ_CRASH("unexpected frame type"); 1447 } 1448 } 1449 } 1450 1451 void 1452 MarkJitActivations(JSRuntime* rt, JSTracer* trc) 1453 { 1454 for (JitActivationIterator activations(rt); !activations.done(); ++activations) 1455 MarkJitActivation(trc, activations); 1456 } 1457 1458 JSCompartment* 1459 TopmostIonActivationCompartment(JSRuntime* rt) 1460 { 1461 for (JitActivationIterator activations(rt); !activations.done(); ++activations) { 1462 for (JitFrameIterator frames(activations); !frames.done(); ++frames) { 1463 if (frames.type() == JitFrame_IonJS) 1464 return activations.activation()->compartment(); 1465 } 1466 } 1467 return nullptr; 1468 } 1469 1470 void UpdateJitActivationsForMinorGC(JSRuntime* rt, JSTracer* trc) 1471 { 1472 MOZ_ASSERT(trc->runtime()->isHeapMinorCollecting()); 1473 for (JitActivationIterator activations(rt); !activations.done(); ++activations) { 1474 for (JitFrameIterator frames(activations); !frames.done(); ++frames) { 1475 if (frames.type() == JitFrame_IonJS) 1476 UpdateIonJSFrameForMinorGC(trc, frames); 1477 } 1478 } 1479 } 1480 1481 void 1482 GetPcScript(JSContext* cx, JSScript** scriptRes, jsbytecode** pcRes) 1483 { 1484 JitSpew(JitSpew_IonSnapshots, "Recover PC & Script from the last frame."); 1485 1486 // Recover the return address so that we can look it up in the 1487 // PcScriptCache, as script/pc computation is expensive. 1488 JSRuntime* rt = cx->runtime(); 1489 JitActivationIterator iter(rt); 1490 JitFrameIterator it(iter); 1491 uint8_t* retAddr; 1492 if (it.isExitFrame()) { 1493 ++it; 1494 1495 // Skip rectifier frames. 1496 if (it.isRectifier()) { 1497 ++it; 1498 MOZ_ASSERT(it.isBaselineStub() || it.isBaselineJS() || it.isIonJS()); 1499 } 1500 1501 // Skip Baseline/Ion stub and accessor IC frames. 1502 if (it.isBaselineStub()) { 1503 ++it; 1504 MOZ_ASSERT(it.isBaselineJS()); 1505 } else if (it.isIonStub() || it.isIonAccessorIC()) { 1506 ++it; 1507 MOZ_ASSERT(it.isIonJS()); 1508 } 1509 1510 MOZ_ASSERT(it.isBaselineJS() || it.isIonJS()); 1511 1512 // Don't use the return address if the BaselineFrame has an override pc. 1513 // The override pc is cheap to get, so we won't benefit from the cache, 1514 // and the override pc could change without the return address changing. 1515 // Moreover, sometimes when an override pc is present during exception 1516 // handling, the return address is set to nullptr as a sanity check, 1517 // since we do not return to the frame that threw the exception. 1518 if (!it.isBaselineJS() || !it.baselineFrame()->hasOverridePc()) { 1519 retAddr = it.returnAddressToFp(); 1520 MOZ_ASSERT(retAddr); 1521 } else { 1522 retAddr = nullptr; 1523 } 1524 } else { 1525 MOZ_ASSERT(it.isBailoutJS()); 1526 retAddr = it.returnAddress(); 1527 } 1528 1529 uint32_t hash; 1530 if (retAddr) { 1531 hash = PcScriptCache::Hash(retAddr); 1532 1533 // Lazily initialize the cache. The allocation may safely fail and will not GC. 1534 if (MOZ_UNLIKELY(rt->ionPcScriptCache == nullptr)) { 1535 rt->ionPcScriptCache = (PcScriptCache*)js_malloc(sizeof(struct PcScriptCache)); 1536 if (rt->ionPcScriptCache) 1537 rt->ionPcScriptCache->clear(rt->gc.gcNumber()); 1538 } 1539 1540 if (rt->ionPcScriptCache && rt->ionPcScriptCache->get(rt, hash, retAddr, scriptRes, pcRes)) 1541 return; 1542 } 1543 1544 // Lookup failed: undertake expensive process to recover the innermost inlined frame. 1545 jsbytecode* pc = nullptr; 1546 if (it.isIonJS() || it.isBailoutJS()) { 1547 InlineFrameIterator ifi(cx, &it); 1548 *scriptRes = ifi.script(); 1549 pc = ifi.pc(); 1550 } else { 1551 MOZ_ASSERT(it.isBaselineJS()); 1552 it.baselineScriptAndPc(scriptRes, &pc); 1553 } 1554 1555 if (pcRes) 1556 *pcRes = pc; 1557 1558 // Add entry to cache. 1559 if (retAddr && rt->ionPcScriptCache) 1560 rt->ionPcScriptCache->add(hash, retAddr, pc, *scriptRes); 1561 } 1562 1563 uint32_t 1564 OsiIndex::returnPointDisplacement() const 1565 { 1566 // In general, pointer arithmetic on code is bad, but in this case, 1567 // getting the return address from a call instruction, stepping over pools 1568 // would be wrong. 1569 return callPointDisplacement_ + Assembler::PatchWrite_NearCallSize(); 1570 } 1571 1572 RInstructionResults::RInstructionResults(JitFrameLayout* fp) 1573 : results_(nullptr), 1574 fp_(fp), 1575 initialized_(false) 1576 { 1577 } 1578 1579 RInstructionResults::RInstructionResults(RInstructionResults&& src) 1580 : results_(mozilla::Move(src.results_)), 1581 fp_(src.fp_), 1582 initialized_(src.initialized_) 1583 { 1584 src.initialized_ = false; 1585 } 1586 1587 RInstructionResults& 1588 RInstructionResults::operator=(RInstructionResults&& rhs) 1589 { 1590 MOZ_ASSERT(&rhs != this, "self-moves are prohibited"); 1591 this->~RInstructionResults(); 1592 new(this) RInstructionResults(mozilla::Move(rhs)); 1593 return *this; 1594 } 1595 1596 RInstructionResults::~RInstructionResults() 1597 { 1598 // results_ is freed by the UniquePtr. 1599 } 1600 1601 bool 1602 RInstructionResults::init(JSContext* cx, uint32_t numResults) 1603 { 1604 if (numResults) { 1605 results_ = cx->make_unique<Values>(); 1606 if (!results_ || !results_->growBy(numResults)) 1607 return false; 1608 1609 Value guard = MagicValue(JS_ION_BAILOUT); 1610 for (size_t i = 0; i < numResults; i++) 1611 (*results_)[i].init(guard); 1612 } 1613 1614 initialized_ = true; 1615 return true; 1616 } 1617 1618 bool 1619 RInstructionResults::isInitialized() const 1620 { 1621 return initialized_; 1622 } 1623 1624 #ifdef DEBUG 1625 size_t 1626 RInstructionResults::length() const 1627 { 1628 return results_->length(); 1629 } 1630 #endif 1631 1632 JitFrameLayout* 1633 RInstructionResults::frame() const 1634 { 1635 MOZ_ASSERT(fp_); 1636 return fp_; 1637 } 1638 1639 HeapPtr<Value>& 1640 RInstructionResults::operator [](size_t index) 1641 { 1642 return (*results_)[index]; 1643 } 1644 1645 void 1646 RInstructionResults::trace(JSTracer* trc) 1647 { 1648 // Note: The vector necessary exists, otherwise this object would not have 1649 // been stored on the activation from where the trace function is called. 1650 TraceRange(trc, results_->length(), results_->begin(), "ion-recover-results"); 1651 } 1652 1653 1654 SnapshotIterator::SnapshotIterator(const JitFrameIterator& iter, const MachineState* machineState) 1655 : snapshot_(iter.ionScript()->snapshots(), 1656 iter.snapshotOffset(), 1657 iter.ionScript()->snapshotsRVATableSize(), 1658 iter.ionScript()->snapshotsListSize()), 1659 recover_(snapshot_, 1660 iter.ionScript()->recovers(), 1661 iter.ionScript()->recoversSize()), 1662 fp_(iter.jsFrame()), 1663 machine_(machineState), 1664 ionScript_(iter.ionScript()), 1665 instructionResults_(nullptr) 1666 { 1667 } 1668 1669 SnapshotIterator::SnapshotIterator() 1670 : snapshot_(nullptr, 0, 0, 0), 1671 recover_(snapshot_, nullptr, 0), 1672 fp_(nullptr), 1673 ionScript_(nullptr), 1674 instructionResults_(nullptr) 1675 { 1676 } 1677 1678 int32_t 1679 SnapshotIterator::readOuterNumActualArgs() const 1680 { 1681 return fp_->numActualArgs(); 1682 } 1683 1684 uintptr_t 1685 SnapshotIterator::fromStack(int32_t offset) const 1686 { 1687 return ReadFrameSlot(fp_, offset); 1688 } 1689 1690 static Value 1691 FromObjectPayload(uintptr_t payload) 1692 { 1693 // Note: Both MIRType::Object and MIRType::ObjectOrNull are encoded in 1694 // snapshots using JSVAL_TYPE_OBJECT. 1695 return ObjectOrNullValue(reinterpret_cast<JSObject*>(payload)); 1696 } 1697 1698 static Value 1699 FromStringPayload(uintptr_t payload) 1700 { 1701 return StringValue(reinterpret_cast<JSString*>(payload)); 1702 } 1703 1704 static Value 1705 FromSymbolPayload(uintptr_t payload) 1706 { 1707 return SymbolValue(reinterpret_cast<JS::Symbol*>(payload)); 1708 } 1709 1710 static Value 1711 FromTypedPayload(JSValueType type, uintptr_t payload) 1712 { 1713 switch (type) { 1714 case JSVAL_TYPE_INT32: 1715 return Int32Value(payload); 1716 case JSVAL_TYPE_BOOLEAN: 1717 return BooleanValue(!!payload); 1718 case JSVAL_TYPE_STRING: 1719 return FromStringPayload(payload); 1720 case JSVAL_TYPE_SYMBOL: 1721 return FromSymbolPayload(payload); 1722 case JSVAL_TYPE_OBJECT: 1723 return FromObjectPayload(payload); 1724 default: 1725 MOZ_CRASH("unexpected type - needs payload"); 1726 } 1727 } 1728 1729 bool 1730 SnapshotIterator::allocationReadable(const RValueAllocation& alloc, ReadMethod rm) 1731 { 1732 // If we have to recover stores, and if we are not interested in the 1733 // default value of the instruction, then we have to check if the recover 1734 // instruction results are available. 1735 if (alloc.needSideEffect() && !(rm & RM_AlwaysDefault)) { 1736 if (!hasInstructionResults()) 1737 return false; 1738 } 1739 1740 switch (alloc.mode()) { 1741 case RValueAllocation::DOUBLE_REG: 1742 return hasRegister(alloc.fpuReg()); 1743 1744 case RValueAllocation::TYPED_REG: 1745 return hasRegister(alloc.reg2()); 1746 1747 #if defined(JS_NUNBOX32) 1748 case RValueAllocation::UNTYPED_REG_REG: 1749 return hasRegister(alloc.reg()) && hasRegister(alloc.reg2()); 1750 case RValueAllocation::UNTYPED_REG_STACK: 1751 return hasRegister(alloc.reg()) && hasStack(alloc.stackOffset2()); 1752 case RValueAllocation::UNTYPED_STACK_REG: 1753 return hasStack(alloc.stackOffset()) && hasRegister(alloc.reg2()); 1754 case RValueAllocation::UNTYPED_STACK_STACK: 1755 return hasStack(alloc.stackOffset()) && hasStack(alloc.stackOffset2()); 1756 #elif defined(JS_PUNBOX64) 1757 case RValueAllocation::UNTYPED_REG: 1758 return hasRegister(alloc.reg()); 1759 case RValueAllocation::UNTYPED_STACK: 1760 return hasStack(alloc.stackOffset()); 1761 #endif 1762 1763 case RValueAllocation::RECOVER_INSTRUCTION: 1764 return hasInstructionResult(alloc.index()); 1765 case RValueAllocation::RI_WITH_DEFAULT_CST: 1766 return rm & RM_AlwaysDefault || hasInstructionResult(alloc.index()); 1767 1768 default: 1769 return true; 1770 } 1771 } 1772 1773 Value 1774 SnapshotIterator::allocationValue(const RValueAllocation& alloc, ReadMethod rm) 1775 { 1776 switch (alloc.mode()) { 1777 case RValueAllocation::CONSTANT: 1778 return ionScript_->getConstant(alloc.index()); 1779 1780 case RValueAllocation::CST_UNDEFINED: 1781 return UndefinedValue(); 1782 1783 case RValueAllocation::CST_NULL: 1784 return NullValue(); 1785 1786 case RValueAllocation::DOUBLE_REG: 1787 return DoubleValue(fromRegister(alloc.fpuReg())); 1788 1789 case RValueAllocation::ANY_FLOAT_REG: 1790 { 1791 union { 1792 double d; 1793 float f; 1794 } pun; 1795 MOZ_ASSERT(alloc.fpuReg().isSingle()); 1796 pun.d = fromRegister(alloc.fpuReg()); 1797 // The register contains the encoding of a float32. We just read 1798 // the bits without making any conversion. 1799 return Float32Value(pun.f); 1800 } 1801 1802 case RValueAllocation::ANY_FLOAT_STACK: 1803 return Float32Value(ReadFrameFloat32Slot(fp_, alloc.stackOffset())); 1804 1805 case RValueAllocation::TYPED_REG: 1806 return FromTypedPayload(alloc.knownType(), fromRegister(alloc.reg2())); 1807 1808 case RValueAllocation::TYPED_STACK: 1809 { 1810 switch (alloc.knownType()) { 1811 case JSVAL_TYPE_DOUBLE: 1812 return DoubleValue(ReadFrameDoubleSlot(fp_, alloc.stackOffset2())); 1813 case JSVAL_TYPE_INT32: 1814 return Int32Value(ReadFrameInt32Slot(fp_, alloc.stackOffset2())); 1815 case JSVAL_TYPE_BOOLEAN: 1816 return BooleanValue(ReadFrameBooleanSlot(fp_, alloc.stackOffset2())); 1817 case JSVAL_TYPE_STRING: 1818 return FromStringPayload(fromStack(alloc.stackOffset2())); 1819 case JSVAL_TYPE_SYMBOL: 1820 return FromSymbolPayload(fromStack(alloc.stackOffset2())); 1821 case JSVAL_TYPE_OBJECT: 1822 return FromObjectPayload(fromStack(alloc.stackOffset2())); 1823 default: 1824 MOZ_CRASH("Unexpected type"); 1825 } 1826 } 1827 1828 #if defined(JS_NUNBOX32) 1829 case RValueAllocation::UNTYPED_REG_REG: 1830 { 1831 return Value::fromTagAndPayload(JSValueTag(fromRegister(alloc.reg())), 1832 fromRegister(alloc.reg2())); 1833 } 1834 1835 case RValueAllocation::UNTYPED_REG_STACK: 1836 { 1837 return Value::fromTagAndPayload(JSValueTag(fromRegister(alloc.reg())), 1838 fromStack(alloc.stackOffset2())); 1839 } 1840 1841 case RValueAllocation::UNTYPED_STACK_REG: 1842 { 1843 return Value::fromTagAndPayload(JSValueTag(fromStack(alloc.stackOffset())), 1844 fromRegister(alloc.reg2())); 1845 } 1846 1847 case RValueAllocation::UNTYPED_STACK_STACK: 1848 { 1849 return Value::fromTagAndPayload(JSValueTag(fromStack(alloc.stackOffset())), 1850 fromStack(alloc.stackOffset2())); 1851 } 1852 #elif defined(JS_PUNBOX64) 1853 case RValueAllocation::UNTYPED_REG: 1854 { 1855 return Value::fromRawBits(fromRegister(alloc.reg())); 1856 } 1857 1858 case RValueAllocation::UNTYPED_STACK: 1859 { 1860 return Value::fromRawBits(fromStack(alloc.stackOffset())); 1861 } 1862 #endif 1863 1864 case RValueAllocation::RECOVER_INSTRUCTION: 1865 return fromInstructionResult(alloc.index()); 1866 1867 case RValueAllocation::RI_WITH_DEFAULT_CST: 1868 if (rm & RM_Normal && hasInstructionResult(alloc.index())) 1869 return fromInstructionResult(alloc.index()); 1870 MOZ_ASSERT(rm & RM_AlwaysDefault); 1871 return ionScript_->getConstant(alloc.index2()); 1872 1873 default: 1874 MOZ_CRASH("huh?"); 1875 } 1876 } 1877 1878 const FloatRegisters::RegisterContent* 1879 SnapshotIterator::floatAllocationPointer(const RValueAllocation& alloc) const 1880 { 1881 switch (alloc.mode()) { 1882 case RValueAllocation::ANY_FLOAT_REG: 1883 return machine_->address(alloc.fpuReg()); 1884 1885 case RValueAllocation::ANY_FLOAT_STACK: 1886 return (FloatRegisters::RegisterContent*) AddressOfFrameSlot(fp_, alloc.stackOffset()); 1887 1888 default: 1889 MOZ_CRASH("Not a float allocation."); 1890 } 1891 } 1892 1893 Value 1894 SnapshotIterator::maybeRead(const RValueAllocation& a, MaybeReadFallback& fallback) 1895 { 1896 if (allocationReadable(a)) 1897 return allocationValue(a); 1898 1899 if (fallback.canRecoverResults()) { 1900 // Code paths which are calling maybeRead are not always capable of 1901 // returning an error code, as these code paths used to be infallible. 1902 AutoEnterOOMUnsafeRegion oomUnsafe; 1903 if (!initInstructionResults(fallback)) 1904 oomUnsafe.crash("js::jit::SnapshotIterator::maybeRead"); 1905 1906 if (allocationReadable(a)) 1907 return allocationValue(a); 1908 1909 MOZ_ASSERT_UNREACHABLE("All allocations should be readable."); 1910 } 1911 1912 return fallback.unreadablePlaceholder(); 1913 } 1914 1915 void 1916 SnapshotIterator::writeAllocationValuePayload(const RValueAllocation& alloc, const Value& v) 1917 { 1918 uintptr_t payload = *v.payloadUIntPtr(); 1919 #if defined(JS_PUNBOX64) 1920 // Do not write back the tag, as this will trigger an assertion when we will 1921 // reconstruct the JS Value while marking again or when bailing out. 1922 payload &= JSVAL_PAYLOAD_MASK; 1923 #endif 1924 1925 switch (alloc.mode()) { 1926 case RValueAllocation::CONSTANT: 1927 ionScript_->getConstant(alloc.index()) = v; 1928 break; 1929 1930 case RValueAllocation::CST_UNDEFINED: 1931 case RValueAllocation::CST_NULL: 1932 case RValueAllocation::DOUBLE_REG: 1933 case RValueAllocation::ANY_FLOAT_REG: 1934 case RValueAllocation::ANY_FLOAT_STACK: 1935 MOZ_CRASH("Not a GC thing: Unexpected write"); 1936 break; 1937 1938 case RValueAllocation::TYPED_REG: 1939 machine_->write(alloc.reg2(), payload); 1940 break; 1941 1942 case RValueAllocation::TYPED_STACK: 1943 switch (alloc.knownType()) { 1944 default: 1945 MOZ_CRASH("Not a GC thing: Unexpected write"); 1946 break; 1947 case JSVAL_TYPE_STRING: 1948 case JSVAL_TYPE_SYMBOL: 1949 case JSVAL_TYPE_OBJECT: 1950 WriteFrameSlot(fp_, alloc.stackOffset2(), payload); 1951 break; 1952 } 1953 break; 1954 1955 #if defined(JS_NUNBOX32) 1956 case RValueAllocation::UNTYPED_REG_REG: 1957 case RValueAllocation::UNTYPED_STACK_REG: 1958 machine_->write(alloc.reg2(), payload); 1959 break; 1960 1961 case RValueAllocation::UNTYPED_REG_STACK: 1962 case RValueAllocation::UNTYPED_STACK_STACK: 1963 WriteFrameSlot(fp_, alloc.stackOffset2(), payload); 1964 break; 1965 #elif defined(JS_PUNBOX64) 1966 case RValueAllocation::UNTYPED_REG: 1967 machine_->write(alloc.reg(), v.asRawBits()); 1968 break; 1969 1970 case RValueAllocation::UNTYPED_STACK: 1971 WriteFrameSlot(fp_, alloc.stackOffset(), v.asRawBits()); 1972 break; 1973 #endif 1974 1975 case RValueAllocation::RECOVER_INSTRUCTION: 1976 MOZ_CRASH("Recover instructions are handled by the JitActivation."); 1977 break; 1978 1979 case RValueAllocation::RI_WITH_DEFAULT_CST: 1980 // Assume that we are always going to be writing on the default value 1981 // while tracing. 1982 ionScript_->getConstant(alloc.index2()) = v; 1983 break; 1984 1985 default: 1986 MOZ_CRASH("huh?"); 1987 } 1988 } 1989 1990 void 1991 SnapshotIterator::traceAllocation(JSTracer* trc) 1992 { 1993 RValueAllocation alloc = readAllocation(); 1994 if (!allocationReadable(alloc, RM_AlwaysDefault)) 1995 return; 1996 1997 Value v = allocationValue(alloc, RM_AlwaysDefault); 1998 if (!v.isMarkable()) 1999 return; 2000 2001 Value copy = v; 2002 TraceRoot(trc, &v, "ion-typed-reg"); 2003 if (v != copy) { 2004 MOZ_ASSERT(SameType(v, copy)); 2005 writeAllocationValuePayload(alloc, v); 2006 } 2007 } 2008 2009 const RResumePoint* 2010 SnapshotIterator::resumePoint() const 2011 { 2012 return instruction()->toResumePoint(); 2013 } 2014 2015 uint32_t 2016 SnapshotIterator::numAllocations() const 2017 { 2018 return instruction()->numOperands(); 2019 } 2020 2021 uint32_t 2022 SnapshotIterator::pcOffset() const 2023 { 2024 return resumePoint()->pcOffset(); 2025 } 2026 2027 void 2028 SnapshotIterator::skipInstruction() 2029 { 2030 MOZ_ASSERT(snapshot_.numAllocationsRead() == 0); 2031 size_t numOperands = instruction()->numOperands(); 2032 for (size_t i = 0; i < numOperands; i++) 2033 skip(); 2034 nextInstruction(); 2035 } 2036 2037 bool 2038 SnapshotIterator::initInstructionResults(MaybeReadFallback& fallback) 2039 { 2040 MOZ_ASSERT(fallback.canRecoverResults()); 2041 JSContext* cx = fallback.maybeCx; 2042 2043 // If there is only one resume point in the list of instructions, then there 2044 // is no instruction to recover, and thus no need to register any results. 2045 if (recover_.numInstructions() == 1) 2046 return true; 2047 2048 JitFrameLayout* fp = fallback.frame->jsFrame(); 2049 RInstructionResults* results = fallback.activation->maybeIonFrameRecovery(fp); 2050 if (!results) { 2051 AutoCompartment ac(cx, fallback.frame->script()->compartment()); 2052 2053 // We do not have the result yet, which means that an observable stack 2054 // slot is requested. As we do not want to bailout every time for the 2055 // same reason, we need to recompile without optimizing away the 2056 // observable stack slots. The script would later be recompiled to have 2057 // support for Argument objects. 2058 if (fallback.consequence == MaybeReadFallback::Fallback_Invalidate) 2059 ionScript_->invalidate(cx, /* resetUses = */ false, "Observe recovered instruction."); 2060 2061 // Register the list of result on the activation. We need to do that 2062 // before we initialize the list such as if any recover instruction 2063 // cause a GC, we can ensure that the results are properly traced by the 2064 // activation. 2065 RInstructionResults tmp(fallback.frame->jsFrame()); 2066 if (!fallback.activation->registerIonFrameRecovery(mozilla::Move(tmp))) 2067 return false; 2068 2069 results = fallback.activation->maybeIonFrameRecovery(fp); 2070 2071 // Start a new snapshot at the beginning of the JitFrameIterator. This 2072 // SnapshotIterator is used for evaluating the content of all recover 2073 // instructions. The result is then saved on the JitActivation. 2074 MachineState machine = fallback.frame->machineState(); 2075 SnapshotIterator s(*fallback.frame, &machine); 2076 if (!s.computeInstructionResults(cx, results)) { 2077 2078 // If the evaluation failed because of OOMs, then we discard the 2079 // current set of result that we collected so far. 2080 fallback.activation->removeIonFrameRecovery(fp); 2081 return false; 2082 } 2083 } 2084 2085 MOZ_ASSERT(results->isInitialized()); 2086 MOZ_ASSERT(results->length() == recover_.numInstructions() - 1); 2087 instructionResults_ = results; 2088 return true; 2089 } 2090 2091 bool 2092 SnapshotIterator::computeInstructionResults(JSContext* cx, RInstructionResults* results) const 2093 { 2094 MOZ_ASSERT(!results->isInitialized()); 2095 MOZ_ASSERT(recover_.numInstructionsRead() == 1); 2096 2097 // The last instruction will always be a resume point. 2098 size_t numResults = recover_.numInstructions() - 1; 2099 if (!results->isInitialized()) { 2100 if (!results->init(cx, numResults)) 2101 return false; 2102 2103 // No need to iterate over the only resume point. 2104 if (!numResults) { 2105 MOZ_ASSERT(results->isInitialized()); 2106 return true; 2107 } 2108 2109 // Use AutoEnterAnalysis to avoid invoking the object metadata callback, 2110 // which could try to walk the stack while bailing out. 2111 AutoEnterAnalysis enter(cx); 2112 2113 // Fill with the results of recover instructions. 2114 SnapshotIterator s(*this); 2115 s.instructionResults_ = results; 2116 while (s.moreInstructions()) { 2117 // Skip resume point and only interpret recover instructions. 2118 if (s.instruction()->isResumePoint()) { 2119 s.skipInstruction(); 2120 continue; 2121 } 2122 2123 if (!s.instruction()->recover(cx, s)) 2124 return false; 2125 s.nextInstruction(); 2126 } 2127 } 2128 2129 MOZ_ASSERT(results->isInitialized()); 2130 return true; 2131 } 2132 2133 void 2134 SnapshotIterator::storeInstructionResult(const Value& v) 2135 { 2136 uint32_t currIns = recover_.numInstructionsRead() - 1; 2137 MOZ_ASSERT((*instructionResults_)[currIns].isMagic(JS_ION_BAILOUT)); 2138 (*instructionResults_)[currIns] = v; 2139 } 2140 2141 Value 2142 SnapshotIterator::fromInstructionResult(uint32_t index) const 2143 { 2144 MOZ_ASSERT(!(*instructionResults_)[index].isMagic(JS_ION_BAILOUT)); 2145 return (*instructionResults_)[index]; 2146 } 2147 2148 void 2149 SnapshotIterator::settleOnFrame() 2150 { 2151 // Check that the current instruction can still be use. 2152 MOZ_ASSERT(snapshot_.numAllocationsRead() == 0); 2153 while (!instruction()->isResumePoint()) 2154 skipInstruction(); 2155 } 2156 2157 void 2158 SnapshotIterator::nextFrame() 2159 { 2160 nextInstruction(); 2161 settleOnFrame(); 2162 } 2163 2164 Value 2165 SnapshotIterator::maybeReadAllocByIndex(size_t index) 2166 { 2167 while (index--) { 2168 MOZ_ASSERT(moreAllocations()); 2169 skip(); 2170 } 2171 2172 Value s; 2173 { 2174 // This MaybeReadFallback method cannot GC. 2175 JS::AutoSuppressGCAnalysis nogc; 2176 MaybeReadFallback fallback(UndefinedValue()); 2177 s = maybeRead(fallback); 2178 } 2179 2180 while (moreAllocations()) 2181 skip(); 2182 2183 return s; 2184 } 2185 2186 JitFrameLayout* 2187 JitFrameIterator::jsFrame() const 2188 { 2189 MOZ_ASSERT(isScripted()); 2190 if (isBailoutJS()) 2191 return (JitFrameLayout*) activation_->bailoutData()->fp(); 2192 2193 return (JitFrameLayout*) fp(); 2194 } 2195 2196 IonScript* 2197 JitFrameIterator::ionScript() const 2198 { 2199 MOZ_ASSERT(isIonScripted()); 2200 if (isBailoutJS()) 2201 return activation_->bailoutData()->ionScript(); 2202 2203 IonScript* ionScript = nullptr; 2204 if (checkInvalidation(&ionScript)) 2205 return ionScript; 2206 return ionScriptFromCalleeToken(); 2207 } 2208 2209 IonScript* 2210 JitFrameIterator::ionScriptFromCalleeToken() const 2211 { 2212 MOZ_ASSERT(isIonJS()); 2213 MOZ_ASSERT(!checkInvalidation()); 2214 return script()->ionScript(); 2215 } 2216 2217 const SafepointIndex* 2218 JitFrameIterator::safepoint() const 2219 { 2220 MOZ_ASSERT(isIonJS()); 2221 if (!cachedSafepointIndex_) 2222 cachedSafepointIndex_ = ionScript()->getSafepointIndex(returnAddressToFp()); 2223 return cachedSafepointIndex_; 2224 } 2225 2226 SnapshotOffset 2227 JitFrameIterator::snapshotOffset() const 2228 { 2229 MOZ_ASSERT(isIonScripted()); 2230 if (isBailoutJS()) 2231 return activation_->bailoutData()->snapshotOffset(); 2232 return osiIndex()->snapshotOffset(); 2233 } 2234 2235 const OsiIndex* 2236 JitFrameIterator::osiIndex() const 2237 { 2238 MOZ_ASSERT(isIonJS()); 2239 SafepointReader reader(ionScript(), safepoint()); 2240 return ionScript()->getOsiIndex(reader.osiReturnPointOffset()); 2241 } 2242 2243 InlineFrameIterator::InlineFrameIterator(JSContext* cx, const JitFrameIterator* iter) 2244 : calleeTemplate_(cx), 2245 calleeRVA_(), 2246 script_(cx) 2247 { 2248 resetOn(iter); 2249 } 2250 2251 InlineFrameIterator::InlineFrameIterator(JSRuntime* rt, const JitFrameIterator* iter) 2252 : calleeTemplate_(rt->contextFromMainThread()), 2253 calleeRVA_(), 2254 script_(rt->contextFromMainThread()) 2255 { 2256 resetOn(iter); 2257 } 2258 2259 InlineFrameIterator::InlineFrameIterator(JSContext* cx, const InlineFrameIterator* iter) 2260 : frame_(iter ? iter->frame_ : nullptr), 2261 framesRead_(0), 2262 frameCount_(iter ? iter->frameCount_ : UINT32_MAX), 2263 calleeTemplate_(cx), 2264 calleeRVA_(), 2265 script_(cx) 2266 { 2267 if (frame_) { 2268 machine_ = iter->machine_; 2269 start_ = SnapshotIterator(*frame_, &machine_); 2270 2271 // findNextFrame will iterate to the next frame and init. everything. 2272 // Therefore to settle on the same frame, we report one frame less readed. 2273 framesRead_ = iter->framesRead_ - 1; 2274 findNextFrame(); 2275 } 2276 } 2277 2278 void 2279 InlineFrameIterator::resetOn(const JitFrameIterator* iter) 2280 { 2281 frame_ = iter; 2282 framesRead_ = 0; 2283 frameCount_ = UINT32_MAX; 2284 2285 if (iter) { 2286 machine_ = iter->machineState(); 2287 start_ = SnapshotIterator(*iter, &machine_); 2288 findNextFrame(); 2289 } 2290 } 2291 2292 void 2293 InlineFrameIterator::findNextFrame() 2294 { 2295 MOZ_ASSERT(more()); 2296 2297 si_ = start_; 2298 2299 // Read the initial frame out of the C stack. 2300 calleeTemplate_ = frame_->maybeCallee(); 2301 calleeRVA_ = RValueAllocation(); 2302 script_ = frame_->script(); 2303 MOZ_ASSERT(script_->hasBaselineScript()); 2304 2305 // Settle on the outermost frame without evaluating any instructions before 2306 // looking for a pc. 2307 si_.settleOnFrame(); 2308 2309 pc_ = script_->offsetToPC(si_.pcOffset()); 2310 numActualArgs_ = 0xbadbad; 2311 2312 // This unfortunately is O(n*m), because we must skip over outer frames 2313 // before reading inner ones. 2314 2315 // The first time (frameCount_ == UINT32_MAX) we do not know the number of 2316 // frames that we are going to inspect. So we are iterating until there is 2317 // no more frames, to settle on the inner most frame and to count the number 2318 // of frames. 2319 size_t remaining = (frameCount_ != UINT32_MAX) ? frameNo() - 1 : SIZE_MAX; 2320 2321 size_t i = 1; 2322 for (; i <= remaining && si_.moreFrames(); i++) { 2323 MOZ_ASSERT(IsIonInlinablePC(pc_)); 2324 2325 // Recover the number of actual arguments from the script. 2326 if (JSOp(*pc_) != JSOP_FUNAPPLY) 2327 numActualArgs_ = GET_ARGC(pc_); 2328 if (JSOp(*pc_) == JSOP_FUNCALL) { 2329 MOZ_ASSERT(GET_ARGC(pc_) > 0); 2330 numActualArgs_ = GET_ARGC(pc_) - 1; 2331 } else if (IsGetPropPC(pc_)) { 2332 numActualArgs_ = 0; 2333 } else if (IsSetPropPC(pc_)) { 2334 numActualArgs_ = 1; 2335 } 2336 2337 if (numActualArgs_ == 0xbadbad) 2338 MOZ_CRASH("Couldn't deduce the number of arguments of an ionmonkey frame"); 2339 2340 // Skip over non-argument slots, as well as |this|. 2341 bool skipNewTarget = JSOp(*pc_) == JSOP_NEW; 2342 unsigned skipCount = (si_.numAllocations() - 1) - numActualArgs_ - 1 - skipNewTarget; 2343 for (unsigned j = 0; j < skipCount; j++) 2344 si_.skip(); 2345 2346 // This value should correspond to the function which is being inlined. 2347 // The value must be readable to iterate over the inline frame. Most of 2348 // the time, these functions are stored as JSFunction constants, 2349 // register which are holding the JSFunction pointer, or recover 2350 // instruction with Default value. 2351 Value funval = si_.readWithDefault(&calleeRVA_); 2352 2353 // Skip extra value allocations. 2354 while (si_.moreAllocations()) 2355 si_.skip(); 2356 2357 si_.nextFrame(); 2358 2359 calleeTemplate_ = &funval.toObject().as<JSFunction>(); 2360 2361 // Inlined functions may be clones that still point to the lazy script 2362 // for the executed script, if they are clones. The actual script 2363 // exists though, just make sure the function points to it. 2364 script_ = calleeTemplate_->existingScript(); 2365 MOZ_ASSERT(script_->hasBaselineScript()); 2366 2367 pc_ = script_->offsetToPC(si_.pcOffset()); 2368 } 2369 2370 // The first time we do not know the number of frames, we only settle on the 2371 // last frame, and update the number of frames based on the number of 2372 // iteration that we have done. 2373 if (frameCount_ == UINT32_MAX) { 2374 MOZ_ASSERT(!si_.moreFrames()); 2375 frameCount_ = i; 2376 } 2377 2378 framesRead_++; 2379 } 2380 2381 JSFunction* 2382 InlineFrameIterator::callee(MaybeReadFallback& fallback) const 2383 { 2384 MOZ_ASSERT(isFunctionFrame()); 2385 if (calleeRVA_.mode() == RValueAllocation::INVALID || !fallback.canRecoverResults()) 2386 return calleeTemplate_; 2387 2388 SnapshotIterator s(si_); 2389 // :TODO: Handle allocation failures from recover instruction. 2390 Value funval = s.maybeRead(calleeRVA_, fallback); 2391 return &funval.toObject().as<JSFunction>(); 2392 } 2393 2394 JSObject* 2395 InlineFrameIterator::computeEnvironmentChain(const Value& envChainValue, 2396 MaybeReadFallback& fallback, 2397 bool* hasInitialEnv) const 2398 { 2399 if (envChainValue.isObject()) { 2400 if (hasInitialEnv) { 2401 if (fallback.canRecoverResults()) { 2402 RootedObject obj(fallback.maybeCx, &envChainValue.toObject()); 2403 *hasInitialEnv = isFunctionFrame() && 2404 callee(fallback)->needsFunctionEnvironmentObjects(); 2405 return obj; 2406 } else { 2407 JS::AutoSuppressGCAnalysis nogc; // If we cannot recover then we cannot GC. 2408 *hasInitialEnv = isFunctionFrame() && 2409 callee(fallback)->needsFunctionEnvironmentObjects(); 2410 } 2411 } 2412 2413 return &envChainValue.toObject(); 2414 } 2415 2416 // Note we can hit this case even for functions with a CallObject, in case 2417 // we are walking the frame during the function prologue, before the env 2418 // chain has been initialized. 2419 if (isFunctionFrame()) 2420 return callee(fallback)->environment(); 2421 2422 // Ion does not handle non-function scripts that have anything other than 2423 // the global on their env chain. 2424 MOZ_ASSERT(!script()->isForEval()); 2425 MOZ_ASSERT(!script()->hasNonSyntacticScope()); 2426 return &script()->global().lexicalEnvironment(); 2427 } 2428 2429 bool 2430 InlineFrameIterator::isFunctionFrame() const 2431 { 2432 return !!calleeTemplate_; 2433 } 2434 2435 MachineState 2436 MachineState::FromBailout(RegisterDump::GPRArray& regs, RegisterDump::FPUArray& fpregs) 2437 { 2438 MachineState machine; 2439 2440 for (unsigned i = 0; i < Registers::Total; i++) 2441 machine.setRegisterLocation(Register::FromCode(i), ®s[i].r); 2442 #ifdef JS_CODEGEN_ARM 2443 float* fbase = (float*)&fpregs[0]; 2444 for (unsigned i = 0; i < FloatRegisters::TotalDouble; i++) 2445 machine.setRegisterLocation(FloatRegister(i, FloatRegister::Double), &fpregs[i].d); 2446 for (unsigned i = 0; i < FloatRegisters::TotalSingle; i++) 2447 machine.setRegisterLocation(FloatRegister(i, FloatRegister::Single), (double*)&fbase[i]); 2448 #elif defined(JS_CODEGEN_MIPS32) 2449 float* fbase = (float*)&fpregs[0]; 2450 for (unsigned i = 0; i < FloatRegisters::TotalDouble; i++) { 2451 machine.setRegisterLocation(FloatRegister::FromIndex(i, FloatRegister::Double), 2452 &fpregs[i].d); 2453 } 2454 for (unsigned i = 0; i < FloatRegisters::TotalSingle; i++) { 2455 machine.setRegisterLocation(FloatRegister::FromIndex(i, FloatRegister::Single), 2456 (double*)&fbase[i]); 2457 } 2458 #elif defined(JS_CODEGEN_MIPS64) 2459 for (unsigned i = 0; i < FloatRegisters::TotalPhys; i++) { 2460 machine.setRegisterLocation(FloatRegister(i, FloatRegisters::Double), &fpregs[i]); 2461 machine.setRegisterLocation(FloatRegister(i, FloatRegisters::Single), &fpregs[i]); 2462 } 2463 #elif defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 2464 for (unsigned i = 0; i < FloatRegisters::TotalPhys; i++) { 2465 machine.setRegisterLocation(FloatRegister(i, FloatRegisters::Single), &fpregs[i]); 2466 machine.setRegisterLocation(FloatRegister(i, FloatRegisters::Double), &fpregs[i]); 2467 machine.setRegisterLocation(FloatRegister(i, FloatRegisters::Simd128), &fpregs[i]); 2468 } 2469 #elif defined(JS_CODEGEN_ARM64) 2470 for (unsigned i = 0; i < FloatRegisters::TotalPhys; i++) { 2471 machine.setRegisterLocation(FloatRegister(i, FloatRegisters::Single), &fpregs[i]); 2472 machine.setRegisterLocation(FloatRegister(i, FloatRegisters::Double), &fpregs[i]); 2473 } 2474 2475 #elif defined(JS_CODEGEN_NONE) 2476 MOZ_CRASH(); 2477 #else 2478 # error "Unknown architecture!" 2479 #endif 2480 return machine; 2481 } 2482 2483 bool 2484 InlineFrameIterator::isConstructing() const 2485 { 2486 // Skip the current frame and look at the caller's. 2487 if (more()) { 2488 InlineFrameIterator parent(GetJSContextFromMainThread(), this); 2489 ++parent; 2490 2491 // Inlined Getters and Setters are never constructing. 2492 if (IsGetPropPC(parent.pc()) || IsSetPropPC(parent.pc())) 2493 return false; 2494 2495 // In the case of a JS frame, look up the pc from the snapshot. 2496 MOZ_ASSERT(IsCallPC(parent.pc())); 2497 2498 return (JSOp)*parent.pc() == JSOP_NEW; 2499 } 2500 2501 return frame_->isConstructing(); 2502 } 2503 2504 bool 2505 JitFrameIterator::isConstructing() const 2506 { 2507 return CalleeTokenIsConstructing(calleeToken()); 2508 } 2509 2510 unsigned 2511 JitFrameIterator::numActualArgs() const 2512 { 2513 if (isScripted()) 2514 return jsFrame()->numActualArgs(); 2515 2516 MOZ_ASSERT(isExitFrameLayout<NativeExitFrameLayout>()); 2517 return exitFrame()->as<NativeExitFrameLayout>()->argc(); 2518 } 2519 2520 void 2521 SnapshotIterator::warnUnreadableAllocation() 2522 { 2523 fprintf(stderr, "Warning! Tried to access unreadable value allocation (possible f.arguments).\n"); 2524 } 2525 2526 struct DumpOp { 2527 explicit DumpOp(unsigned int i) : i_(i) {} 2528 2529 unsigned int i_; 2530 void operator()(const Value& v) { 2531 fprintf(stderr, " actual (arg %d): ", i_); 2532 #ifdef DEBUG 2533 DumpValue(v); 2534 #else 2535 fprintf(stderr, "?\n"); 2536 #endif 2537 i_++; 2538 } 2539 }; 2540 2541 void 2542 JitFrameIterator::dumpBaseline() const 2543 { 2544 MOZ_ASSERT(isBaselineJS()); 2545 2546 fprintf(stderr, " JS Baseline frame\n"); 2547 if (isFunctionFrame()) { 2548 fprintf(stderr, " callee fun: "); 2549 #ifdef DEBUG 2550 DumpObject(callee()); 2551 #else 2552 fprintf(stderr, "?\n"); 2553 #endif 2554 } else { 2555 fprintf(stderr, " global frame, no callee\n"); 2556 } 2557 2558 fprintf(stderr, " file %s line %" PRIuSIZE "\n", 2559 script()->filename(), script()->lineno()); 2560 2561 JSContext* cx = GetJSContextFromMainThread(); 2562 RootedScript script(cx); 2563 jsbytecode* pc; 2564 baselineScriptAndPc(script.address(), &pc); 2565 2566 fprintf(stderr, " script = %p, pc = %p (offset %u)\n", (void*)script, pc, uint32_t(script->pcToOffset(pc))); 2567 fprintf(stderr, " current op: %s\n", CodeName[*pc]); 2568 2569 fprintf(stderr, " actual args: %d\n", numActualArgs()); 2570 2571 BaselineFrame* frame = baselineFrame(); 2572 2573 for (unsigned i = 0; i < frame->numValueSlots(); i++) { 2574 fprintf(stderr, " slot %u: ", i); 2575 #ifdef DEBUG 2576 Value* v = frame->valueSlot(i); 2577 DumpValue(*v); 2578 #else 2579 fprintf(stderr, "?\n"); 2580 #endif 2581 } 2582 } 2583 2584 void 2585 InlineFrameIterator::dump() const 2586 { 2587 MaybeReadFallback fallback(UndefinedValue()); 2588 2589 if (more()) 2590 fprintf(stderr, " JS frame (inlined)\n"); 2591 else 2592 fprintf(stderr, " JS frame\n"); 2593 2594 bool isFunction = false; 2595 if (isFunctionFrame()) { 2596 isFunction = true; 2597 fprintf(stderr, " callee fun: "); 2598 #ifdef DEBUG 2599 DumpObject(callee(fallback)); 2600 #else 2601 fprintf(stderr, "?\n"); 2602 #endif 2603 } else { 2604 fprintf(stderr, " global frame, no callee\n"); 2605 } 2606 2607 fprintf(stderr, " file %s line %" PRIuSIZE "\n", 2608 script()->filename(), script()->lineno()); 2609 2610 fprintf(stderr, " script = %p, pc = %p\n", (void*) script(), pc()); 2611 fprintf(stderr, " current op: %s\n", CodeName[*pc()]); 2612 2613 if (!more()) { 2614 numActualArgs(); 2615 } 2616 2617 SnapshotIterator si = snapshotIterator(); 2618 fprintf(stderr, " slots: %u\n", si.numAllocations() - 1); 2619 for (unsigned i = 0; i < si.numAllocations() - 1; i++) { 2620 if (isFunction) { 2621 if (i == 0) 2622 fprintf(stderr, " env chain: "); 2623 else if (i == 1) 2624 fprintf(stderr, " this: "); 2625 else if (i - 2 < calleeTemplate()->nargs()) 2626 fprintf(stderr, " formal (arg %d): ", i - 2); 2627 else { 2628 if (i - 2 == calleeTemplate()->nargs() && numActualArgs() > calleeTemplate()->nargs()) { 2629 DumpOp d(calleeTemplate()->nargs()); 2630 unaliasedForEachActual(GetJSContextFromMainThread(), d, ReadFrame_Overflown, fallback); 2631 } 2632 2633 fprintf(stderr, " slot %d: ", int(i - 2 - calleeTemplate()->nargs())); 2634 } 2635 } else 2636 fprintf(stderr, " slot %u: ", i); 2637 #ifdef DEBUG 2638 DumpValue(si.maybeRead(fallback)); 2639 #else 2640 fprintf(stderr, "?\n"); 2641 #endif 2642 } 2643 2644 fputc('\n', stderr); 2645 } 2646 2647 void 2648 JitFrameIterator::dump() const 2649 { 2650 switch (type_) { 2651 case JitFrame_Entry: 2652 fprintf(stderr, " Entry frame\n"); 2653 fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize())); 2654 break; 2655 case JitFrame_BaselineJS: 2656 dumpBaseline(); 2657 break; 2658 case JitFrame_BaselineStub: 2659 fprintf(stderr, " Baseline stub frame\n"); 2660 fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize())); 2661 break; 2662 case JitFrame_Bailout: 2663 case JitFrame_IonJS: 2664 { 2665 InlineFrameIterator frames(GetJSContextFromMainThread(), this); 2666 for (;;) { 2667 frames.dump(); 2668 if (!frames.more()) 2669 break; 2670 ++frames; 2671 } 2672 break; 2673 } 2674 case JitFrame_IonStub: 2675 fprintf(stderr, " Ion stub frame\n"); 2676 fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize())); 2677 break; 2678 case JitFrame_Rectifier: 2679 fprintf(stderr, " Rectifier frame\n"); 2680 fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize())); 2681 break; 2682 case JitFrame_IonAccessorIC: 2683 fprintf(stderr, " Ion scripted accessor IC\n"); 2684 fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize())); 2685 break; 2686 case JitFrame_Exit: 2687 fprintf(stderr, " Exit frame\n"); 2688 break; 2689 }; 2690 fputc('\n', stderr); 2691 } 2692 2693 #ifdef DEBUG 2694 bool 2695 JitFrameIterator::verifyReturnAddressUsingNativeToBytecodeMap() 2696 { 2697 MOZ_ASSERT(returnAddressToFp_ != nullptr); 2698 2699 // Only handle Ion frames for now. 2700 if (type_ != JitFrame_IonJS && type_ != JitFrame_BaselineJS) 2701 return true; 2702 2703 JSRuntime* rt = js::TlsPerThreadData.get()->runtimeIfOnOwnerThread(); 2704 2705 // Don't verify on non-main-thread. 2706 if (!rt) 2707 return true; 2708 2709 // Don't verify if sampling is being suppressed. 2710 if (!rt->isProfilerSamplingEnabled()) 2711 return true; 2712 2713 if (rt->isHeapMinorCollecting()) 2714 return true; 2715 2716 JitRuntime* jitrt = rt->jitRuntime(); 2717 2718 // Look up and print bytecode info for the native address. 2719 const JitcodeGlobalEntry* entry = jitrt->getJitcodeGlobalTable()->lookup(returnAddressToFp_); 2720 if (!entry) 2721 return true; 2722 2723 JitSpew(JitSpew_Profiling, "Found nativeToBytecode entry for %p: %p - %p", 2724 returnAddressToFp_, entry->nativeStartAddr(), entry->nativeEndAddr()); 2725 2726 JitcodeGlobalEntry::BytecodeLocationVector location; 2727 uint32_t depth = UINT32_MAX; 2728 if (!entry->callStackAtAddr(rt, returnAddressToFp_, location, &depth)) 2729 return false; 2730 MOZ_ASSERT(depth > 0 && depth != UINT32_MAX); 2731 MOZ_ASSERT(location.length() == depth); 2732 2733 JitSpew(JitSpew_Profiling, "Found bytecode location of depth %d:", depth); 2734 for (size_t i = 0; i < location.length(); i++) { 2735 JitSpew(JitSpew_Profiling, " %s:%" PRIuSIZE " - %" PRIuSIZE, 2736 location[i].script->filename(), location[i].script->lineno(), 2737 size_t(location[i].pc - location[i].script->code())); 2738 } 2739 2740 if (type_ == JitFrame_IonJS) { 2741 // Create an InlineFrameIterator here and verify the mapped info against the iterator info. 2742 InlineFrameIterator inlineFrames(GetJSContextFromMainThread(), this); 2743 for (size_t idx = 0; idx < location.length(); idx++) { 2744 MOZ_ASSERT(idx < location.length()); 2745 MOZ_ASSERT_IF(idx < location.length() - 1, inlineFrames.more()); 2746 2747 JitSpew(JitSpew_Profiling, 2748 "Match %d: ION %s:%" PRIuSIZE "(%" PRIuSIZE ") vs N2B %s:%" PRIuSIZE "(%" PRIuSIZE ")", 2749 (int)idx, 2750 inlineFrames.script()->filename(), 2751 inlineFrames.script()->lineno(), 2752 size_t(inlineFrames.pc() - inlineFrames.script()->code()), 2753 location[idx].script->filename(), 2754 location[idx].script->lineno(), 2755 size_t(location[idx].pc - location[idx].script->code())); 2756 2757 MOZ_ASSERT(inlineFrames.script() == location[idx].script); 2758 2759 if (inlineFrames.more()) 2760 ++inlineFrames; 2761 } 2762 } 2763 2764 return true; 2765 } 2766 #endif // DEBUG 2767 2768 JitProfilingFrameIterator::JitProfilingFrameIterator( 2769 JSRuntime* rt, const JS::ProfilingFrameIterator::RegisterState& state) 2770 { 2771 // If no profilingActivation is live, initialize directly to 2772 // end-of-iteration state. 2773 if (!rt->profilingActivation()) { 2774 type_ = JitFrame_Entry; 2775 fp_ = nullptr; 2776 returnAddressToFp_ = nullptr; 2777 return; 2778 } 2779 2780 MOZ_ASSERT(rt->profilingActivation()->isJit()); 2781 2782 JitActivation* act = rt->profilingActivation()->asJit(); 2783 2784 // If the top JitActivation has a null lastProfilingFrame, assume that 2785 // it's a trivially empty activation, and initialize directly 2786 // to end-of-iteration state. 2787 if (!act->lastProfilingFrame()) { 2788 type_ = JitFrame_Entry; 2789 fp_ = nullptr; 2790 returnAddressToFp_ = nullptr; 2791 return; 2792 } 2793 2794 // Get the fp from the current profilingActivation 2795 fp_ = (uint8_t*) act->lastProfilingFrame(); 2796 void* lastCallSite = act->lastProfilingCallSite(); 2797 2798 JitcodeGlobalTable* table = rt->jitRuntime()->getJitcodeGlobalTable(); 2799 2800 // Profiler sampling must NOT be suppressed if we are here. 2801 MOZ_ASSERT(rt->isProfilerSamplingEnabled()); 2802 2803 // Try initializing with sampler pc 2804 if (tryInitWithPC(state.pc)) 2805 return; 2806 2807 // Try initializing with sampler pc using native=>bytecode table. 2808 if (tryInitWithTable(table, state.pc, rt, /* forLastCallSite = */ false)) 2809 return; 2810 2811 // Try initializing with lastProfilingCallSite pc 2812 if (lastCallSite) { 2813 if (tryInitWithPC(lastCallSite)) 2814 return; 2815 2816 // Try initializing with lastProfilingCallSite pc using native=>bytecode table. 2817 if (tryInitWithTable(table, lastCallSite, rt, /* forLastCallSite = */ true)) 2818 return; 2819 } 2820 2821 MOZ_ASSERT(frameScript()->hasBaselineScript()); 2822 2823 // If nothing matches, for now just assume we are at the start of the last frame's 2824 // baseline jit code. 2825 type_ = JitFrame_BaselineJS; 2826 returnAddressToFp_ = frameScript()->baselineScript()->method()->raw(); 2827 } 2828 2829 template <typename ReturnType = CommonFrameLayout*> 2830 inline ReturnType 2831 GetPreviousRawFrame(CommonFrameLayout* frame) 2832 { 2833 size_t prevSize = frame->prevFrameLocalSize() + frame->headerSize(); 2834 return ReturnType((uint8_t*)frame + prevSize); 2835 } 2836 2837 JitProfilingFrameIterator::JitProfilingFrameIterator(void* exitFrame) 2838 { 2839 // Skip the exit frame. 2840 ExitFrameLayout* frame = (ExitFrameLayout*) exitFrame; 2841 moveToNextFrame(frame); 2842 } 2843 2844 bool 2845 JitProfilingFrameIterator::tryInitWithPC(void* pc) 2846 { 2847 JSScript* callee = frameScript(); 2848 2849 // Check for Ion first, since it's more likely for hot code. 2850 if (callee->hasIonScript() && callee->ionScript()->method()->containsNativePC(pc)) { 2851 type_ = JitFrame_IonJS; 2852 returnAddressToFp_ = pc; 2853 return true; 2854 } 2855 2856 // Check for containment in Baseline jitcode second. 2857 if (callee->hasBaselineScript() && callee->baselineScript()->method()->containsNativePC(pc)) { 2858 type_ = JitFrame_BaselineJS; 2859 returnAddressToFp_ = pc; 2860 return true; 2861 } 2862 2863 return false; 2864 } 2865 2866 bool 2867 JitProfilingFrameIterator::tryInitWithTable(JitcodeGlobalTable* table, void* pc, JSRuntime* rt, 2868 bool forLastCallSite) 2869 { 2870 if (!pc) 2871 return false; 2872 2873 const JitcodeGlobalEntry* entry = table->lookup(pc); 2874 if (!entry) 2875 return false; 2876 2877 JSScript* callee = frameScript(); 2878 2879 MOZ_ASSERT(entry->isIon() || entry->isBaseline() || entry->isIonCache() || entry->isDummy()); 2880 2881 // Treat dummy lookups as an empty frame sequence. 2882 if (entry->isDummy()) { 2883 type_ = JitFrame_Entry; 2884 fp_ = nullptr; 2885 returnAddressToFp_ = nullptr; 2886 return true; 2887 } 2888 2889 if (entry->isIon()) { 2890 // If looked-up callee doesn't match frame callee, don't accept lastProfilingCallSite 2891 if (entry->ionEntry().getScript(0) != callee) 2892 return false; 2893 2894 type_ = JitFrame_IonJS; 2895 returnAddressToFp_ = pc; 2896 return true; 2897 } 2898 2899 if (entry->isBaseline()) { 2900 // If looked-up callee doesn't match frame callee, don't accept lastProfilingCallSite 2901 if (forLastCallSite && entry->baselineEntry().script() != callee) 2902 return false; 2903 2904 type_ = JitFrame_BaselineJS; 2905 returnAddressToFp_ = pc; 2906 return true; 2907 } 2908 2909 if (entry->isIonCache()) { 2910 void* ptr = entry->ionCacheEntry().rejoinAddr(); 2911 const JitcodeGlobalEntry& ionEntry = table->lookupInfallible(ptr); 2912 MOZ_ASSERT(ionEntry.isIon()); 2913 2914 if (ionEntry.ionEntry().getScript(0) != callee) 2915 return false; 2916 2917 type_ = JitFrame_IonJS; 2918 returnAddressToFp_ = pc; 2919 return true; 2920 } 2921 2922 return false; 2923 } 2924 2925 void 2926 JitProfilingFrameIterator::fixBaselineReturnAddress() 2927 { 2928 MOZ_ASSERT(type_ == JitFrame_BaselineJS); 2929 BaselineFrame* bl = (BaselineFrame*)(fp_ - BaselineFrame::FramePointerOffset - 2930 BaselineFrame::Size()); 2931 2932 // Debug mode OSR for Baseline uses a "continuation fixer" and stashes the 2933 // actual return address in an auxiliary structure. 2934 if (BaselineDebugModeOSRInfo* info = bl->getDebugModeOSRInfo()) { 2935 returnAddressToFp_ = info->resumeAddr; 2936 return; 2937 } 2938 2939 // Resuming a generator via .throw() pushes a bogus return address onto 2940 // the stack. We have the actual jsbytecode* stashed on the frame itself; 2941 // translate that into the Baseline code address. 2942 if (jsbytecode* override = bl->maybeOverridePc()) { 2943 JSScript* script = bl->script(); 2944 returnAddressToFp_ = script->baselineScript()->nativeCodeForPC(script, override); 2945 return; 2946 } 2947 } 2948 2949 void 2950 JitProfilingFrameIterator::operator++() 2951 { 2952 JitFrameLayout* frame = framePtr(); 2953 moveToNextFrame(frame); 2954 } 2955 2956 void 2957 JitProfilingFrameIterator::moveToNextFrame(CommonFrameLayout* frame) 2958 { 2959 /* 2960 * fp_ points to a Baseline or Ion frame. The possible call-stacks 2961 * patterns occurring between this frame and a previous Ion or Baseline 2962 * frame are as follows: 2963 * 2964 * <Baseline-Or-Ion> 2965 * ^ 2966 * | 2967 * ^--- Ion 2968 * | 2969 * ^--- Baseline Stub <---- Baseline 2970 * | 2971 * ^--- Argument Rectifier 2972 * | ^ 2973 * | | 2974 * | ^--- Ion 2975 * | | 2976 * | ^--- Baseline Stub <---- Baseline 2977 * | 2978 * ^--- Entry Frame (From C++) 2979 * Exit Frame (From previous JitActivation) 2980 * ^ 2981 * | 2982 * ^--- Ion 2983 * | 2984 * ^--- Baseline 2985 * | 2986 * ^--- Baseline Stub <---- Baseline 2987 */ 2988 FrameType prevType = frame->prevType(); 2989 2990 if (prevType == JitFrame_IonJS) { 2991 returnAddressToFp_ = frame->returnAddress(); 2992 fp_ = GetPreviousRawFrame<uint8_t*>(frame); 2993 type_ = JitFrame_IonJS; 2994 return; 2995 } 2996 2997 if (prevType == JitFrame_BaselineJS) { 2998 returnAddressToFp_ = frame->returnAddress(); 2999 fp_ = GetPreviousRawFrame<uint8_t*>(frame); 3000 type_ = JitFrame_BaselineJS; 3001 fixBaselineReturnAddress(); 3002 return; 3003 } 3004 3005 if (prevType == JitFrame_BaselineStub) { 3006 BaselineStubFrameLayout* stubFrame = GetPreviousRawFrame<BaselineStubFrameLayout*>(frame); 3007 MOZ_ASSERT(stubFrame->prevType() == JitFrame_BaselineJS); 3008 3009 returnAddressToFp_ = stubFrame->returnAddress(); 3010 fp_ = ((uint8_t*) stubFrame->reverseSavedFramePtr()) 3011 + jit::BaselineFrame::FramePointerOffset; 3012 type_ = JitFrame_BaselineJS; 3013 return; 3014 } 3015 3016 if (prevType == JitFrame_Rectifier) { 3017 RectifierFrameLayout* rectFrame = GetPreviousRawFrame<RectifierFrameLayout*>(frame); 3018 FrameType rectPrevType = rectFrame->prevType(); 3019 3020 if (rectPrevType == JitFrame_IonJS) { 3021 returnAddressToFp_ = rectFrame->returnAddress(); 3022 fp_ = GetPreviousRawFrame<uint8_t*>(rectFrame); 3023 type_ = JitFrame_IonJS; 3024 return; 3025 } 3026 3027 if (rectPrevType == JitFrame_BaselineStub) { 3028 BaselineStubFrameLayout* stubFrame = 3029 GetPreviousRawFrame<BaselineStubFrameLayout*>(rectFrame); 3030 returnAddressToFp_ = stubFrame->returnAddress(); 3031 fp_ = ((uint8_t*) stubFrame->reverseSavedFramePtr()) 3032 + jit::BaselineFrame::FramePointerOffset; 3033 type_ = JitFrame_BaselineJS; 3034 return; 3035 } 3036 3037 MOZ_CRASH("Bad frame type prior to rectifier frame."); 3038 } 3039 3040 if (prevType == JitFrame_IonAccessorIC) { 3041 IonAccessorICFrameLayout* accessorFrame = 3042 GetPreviousRawFrame<IonAccessorICFrameLayout*>(frame); 3043 3044 MOZ_ASSERT(accessorFrame->prevType() == JitFrame_IonJS); 3045 3046 returnAddressToFp_ = accessorFrame->returnAddress(); 3047 fp_ = GetPreviousRawFrame<uint8_t*>(accessorFrame); 3048 type_ = JitFrame_IonJS; 3049 return; 3050 } 3051 3052 if (prevType == JitFrame_Entry) { 3053 // No previous frame, set to null to indicate that JitFrameIterator is done() 3054 returnAddressToFp_ = nullptr; 3055 fp_ = nullptr; 3056 type_ = JitFrame_Entry; 3057 return; 3058 } 3059 3060 MOZ_CRASH("Bad frame type."); 3061 } 3062 3063 JitFrameLayout* 3064 InvalidationBailoutStack::fp() const 3065 { 3066 return (JitFrameLayout*) (sp() + ionScript_->frameSize()); 3067 } 3068 3069 void 3070 InvalidationBailoutStack::checkInvariants() const 3071 { 3072 #ifdef DEBUG 3073 JitFrameLayout* frame = fp(); 3074 CalleeToken token = frame->calleeToken(); 3075 MOZ_ASSERT(token); 3076 3077 uint8_t* rawBase = ionScript()->method()->raw(); 3078 uint8_t* rawLimit = rawBase + ionScript()->method()->instructionsSize(); 3079 uint8_t* osiPoint = osiPointReturnAddress(); 3080 MOZ_ASSERT(rawBase <= osiPoint && osiPoint <= rawLimit); 3081 #endif 3082 } 3083 3084 void 3085 AssertJitStackInvariants(JSContext* cx) 3086 { 3087 for (JitActivationIterator activations(cx->runtime()); !activations.done(); ++activations) { 3088 JitFrameIterator frames(activations); 3089 size_t prevFrameSize = 0; 3090 size_t frameSize = 0; 3091 bool isScriptedCallee = false; 3092 for (; !frames.done(); ++frames) { 3093 size_t calleeFp = reinterpret_cast<size_t>(frames.fp()); 3094 size_t callerFp = reinterpret_cast<size_t>(frames.prevFp()); 3095 MOZ_ASSERT(callerFp >= calleeFp); 3096 prevFrameSize = frameSize; 3097 frameSize = callerFp - calleeFp; 3098 3099 if (frames.prevType() == JitFrame_Rectifier) { 3100 MOZ_RELEASE_ASSERT(frameSize % JitStackAlignment == 0, 3101 "The rectifier frame should keep the alignment"); 3102 3103 size_t expectedFrameSize = 0 3104 #if defined(JS_CODEGEN_X86) 3105 + sizeof(void*) /* frame pointer */ 3106 #endif 3107 + sizeof(Value) * (frames.callee()->nargs() + 3108 1 /* |this| argument */ + 3109 frames.isConstructing() /* new.target */) 3110 + sizeof(JitFrameLayout); 3111 MOZ_RELEASE_ASSERT(frameSize >= expectedFrameSize, 3112 "The frame is large enough to hold all arguments"); 3113 MOZ_RELEASE_ASSERT(expectedFrameSize + JitStackAlignment > frameSize, 3114 "The frame size is optimal"); 3115 } 3116 3117 if (frames.isExitFrame()) { 3118 // For the moment, we do not keep the JitStackAlignment 3119 // alignment for exit frames. 3120 frameSize -= ExitFrameLayout::Size(); 3121 } 3122 3123 if (frames.isIonJS()) { 3124 // Ideally, we should not have such requirement, but keep the 3125 // alignment-delta as part of the Safepoint such that we can pad 3126 // accordingly when making out-of-line calls. In the mean time, 3127 // let us have check-points where we can garantee that 3128 // everything can properly be aligned before adding complexity. 3129 MOZ_RELEASE_ASSERT(frames.ionScript()->frameSize() % JitStackAlignment == 0, 3130 "Ensure that if the Ion frame is aligned, then the spill base is also aligned"); 3131 3132 if (isScriptedCallee) { 3133 MOZ_RELEASE_ASSERT(prevFrameSize % JitStackAlignment == 0, 3134 "The ion frame should keep the alignment"); 3135 } 3136 } 3137 3138 // The stack is dynamically aligned by baseline stubs before calling 3139 // any jitted code. 3140 if (frames.prevType() == JitFrame_BaselineStub && isScriptedCallee) { 3141 MOZ_RELEASE_ASSERT(calleeFp % JitStackAlignment == 0, 3142 "The baseline stub restores the stack alignment"); 3143 } 3144 3145 isScriptedCallee = false 3146 || frames.isScripted() 3147 || frames.type() == JitFrame_Rectifier; 3148 } 3149 3150 MOZ_RELEASE_ASSERT(frames.type() == JitFrame_Entry, 3151 "The first frame of a Jit activation should be an entry frame"); 3152 MOZ_RELEASE_ASSERT(reinterpret_cast<size_t>(frames.fp()) % JitStackAlignment == 0, 3153 "The entry frame should be properly aligned"); 3154 } 3155 } 3156 3157 } // namespace jit 3158 } // namespace js 3159