LCOV - code coverage report
Current view: top level - js/src/jit - JitFrames.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 412 1446 28.5 %
Date: 2017-07-14 16:53:18 Functions: 61 144 42.4 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13