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), &regs[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