LCOV - code coverage report
Current view: top level - js/src/jit - BaselineBailouts.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 899 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 71 0.0 %
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 "mozilla/ScopeExit.h"
       8             : #include "mozilla/SizePrintfMacros.h"
       9             : 
      10             : #include "jsprf.h"
      11             : #include "jsutil.h"
      12             : #include "jit/arm/Simulator-arm.h"
      13             : #include "jit/BaselineIC.h"
      14             : #include "jit/BaselineJIT.h"
      15             : #include "jit/CompileInfo.h"
      16             : #include "jit/JitSpewer.h"
      17             : #include "jit/mips32/Simulator-mips32.h"
      18             : #include "jit/mips64/Simulator-mips64.h"
      19             : #include "jit/Recover.h"
      20             : #include "jit/RematerializedFrame.h"
      21             : 
      22             : #include "vm/ArgumentsObject.h"
      23             : #include "vm/Debugger.h"
      24             : #include "vm/TraceLogging.h"
      25             : 
      26             : #include "jsscriptinlines.h"
      27             : 
      28             : #include "jit/JitFrames-inl.h"
      29             : 
      30             : using namespace js;
      31             : using namespace js::jit;
      32             : 
      33             : // BaselineStackBuilder may reallocate its buffer if the current one is too
      34             : // small. To avoid dangling pointers, BufferPointer represents a pointer into
      35             : // this buffer as a pointer to the header and a fixed offset.
      36             : template <typename T>
      37             : class BufferPointer
      38             : {
      39             :     BaselineBailoutInfo** header_;
      40             :     size_t offset_;
      41             :     bool heap_;
      42             : 
      43             :   public:
      44           0 :     BufferPointer(BaselineBailoutInfo** header, size_t offset, bool heap)
      45           0 :       : header_(header), offset_(offset), heap_(heap)
      46           0 :     { }
      47             : 
      48           0 :     T* get() const {
      49           0 :         BaselineBailoutInfo* header = *header_;
      50           0 :         if (!heap_)
      51           0 :             return (T*)(header->incomingStack + offset_);
      52             : 
      53           0 :         uint8_t* p = header->copyStackTop - offset_;
      54           0 :         MOZ_ASSERT(p >= header->copyStackBottom && p < header->copyStackTop);
      55           0 :         return (T*)p;
      56             :     }
      57             : 
      58           0 :     void set(const T& value) {
      59           0 :         *get() = value;
      60           0 :     }
      61             : 
      62             :     // Note: we return a copy instead of a reference, to avoid potential memory
      63             :     // safety hazards when the underlying buffer gets resized.
      64           0 :     const T operator*() const { return *get(); }
      65           0 :     T* operator->() const { return get(); }
      66             : };
      67             : 
      68             : /**
      69             :  * BaselineStackBuilder helps abstract the process of rebuilding the C stack on the heap.
      70             :  * It takes a bailout iterator and keeps track of the point on the C stack from which
      71             :  * the reconstructed frames will be written.
      72             :  *
      73             :  * It exposes methods to write data into the heap memory storing the reconstructed
      74             :  * stack.  It also exposes method to easily calculate addresses.  This includes both the
      75             :  * virtual address that a particular value will be at when it's eventually copied onto
      76             :  * the stack, as well as the current actual address of that value (whether on the heap
      77             :  * allocated portion being constructed or the existing stack).
      78             :  *
      79             :  * The abstraction handles transparent re-allocation of the heap memory when it
      80             :  * needs to be enlarged to accommodate new data.  Similarly to the C stack, the
      81             :  * data that's written to the reconstructed stack grows from high to low in memory.
      82             :  *
      83             :  * The lowest region of the allocated memory contains a BaselineBailoutInfo structure that
      84             :  * points to the start and end of the written data.
      85             :  */
      86             : struct BaselineStackBuilder
      87             : {
      88             :     JitFrameIterator& iter_;
      89             :     JitFrameLayout* frame_;
      90             : 
      91           0 :     static size_t HeaderSize() {
      92           0 :         return AlignBytes(sizeof(BaselineBailoutInfo), sizeof(void*));
      93             :     }
      94             :     size_t bufferTotal_;
      95             :     size_t bufferAvail_;
      96             :     size_t bufferUsed_;
      97             :     uint8_t* buffer_;
      98             :     BaselineBailoutInfo* header_;
      99             : 
     100             :     size_t framePushed_;
     101             : 
     102           0 :     BaselineStackBuilder(JitFrameIterator& iter, size_t initialSize)
     103           0 :       : iter_(iter),
     104           0 :         frame_(static_cast<JitFrameLayout*>(iter.current())),
     105             :         bufferTotal_(initialSize),
     106             :         bufferAvail_(0),
     107             :         bufferUsed_(0),
     108             :         buffer_(nullptr),
     109             :         header_(nullptr),
     110           0 :         framePushed_(0)
     111             :     {
     112           0 :         MOZ_ASSERT(bufferTotal_ >= HeaderSize());
     113           0 :         MOZ_ASSERT(iter.isBailoutJS());
     114           0 :     }
     115             : 
     116           0 :     ~BaselineStackBuilder() {
     117           0 :         js_free(buffer_);
     118           0 :     }
     119             : 
     120           0 :     MOZ_MUST_USE bool init() {
     121           0 :         MOZ_ASSERT(!buffer_);
     122           0 :         MOZ_ASSERT(bufferUsed_ == 0);
     123           0 :         buffer_ = reinterpret_cast<uint8_t*>(js_calloc(bufferTotal_));
     124           0 :         if (!buffer_)
     125           0 :             return false;
     126           0 :         bufferAvail_ = bufferTotal_ - HeaderSize();
     127           0 :         bufferUsed_ = 0;
     128             : 
     129           0 :         header_ = reinterpret_cast<BaselineBailoutInfo*>(buffer_);
     130           0 :         header_->incomingStack = reinterpret_cast<uint8_t*>(frame_);
     131           0 :         header_->copyStackTop = buffer_ + bufferTotal_;
     132           0 :         header_->copyStackBottom = header_->copyStackTop;
     133           0 :         header_->setR0 = 0;
     134           0 :         header_->valueR0 = UndefinedValue();
     135           0 :         header_->setR1 = 0;
     136           0 :         header_->valueR1 = UndefinedValue();
     137           0 :         header_->resumeFramePtr = nullptr;
     138           0 :         header_->resumeAddr = nullptr;
     139           0 :         header_->resumePC = nullptr;
     140           0 :         header_->tryPC = nullptr;
     141           0 :         header_->faultPC = nullptr;
     142           0 :         header_->monitorStub = nullptr;
     143           0 :         header_->numFrames = 0;
     144           0 :         header_->checkGlobalDeclarationConflicts = false;
     145           0 :         return true;
     146             :     }
     147             : 
     148           0 :     MOZ_MUST_USE bool enlarge() {
     149           0 :         MOZ_ASSERT(buffer_ != nullptr);
     150           0 :         if (bufferTotal_ & mozilla::tl::MulOverflowMask<2>::value)
     151           0 :             return false;
     152           0 :         size_t newSize = bufferTotal_ * 2;
     153           0 :         uint8_t* newBuffer = reinterpret_cast<uint8_t*>(js_calloc(newSize));
     154           0 :         if (!newBuffer)
     155           0 :             return false;
     156           0 :         memcpy((newBuffer + newSize) - bufferUsed_, header_->copyStackBottom, bufferUsed_);
     157           0 :         memcpy(newBuffer, header_, sizeof(BaselineBailoutInfo));
     158           0 :         js_free(buffer_);
     159           0 :         buffer_ = newBuffer;
     160           0 :         bufferTotal_ = newSize;
     161           0 :         bufferAvail_ = newSize - (HeaderSize() + bufferUsed_);
     162             : 
     163           0 :         header_ = reinterpret_cast<BaselineBailoutInfo*>(buffer_);
     164           0 :         header_->copyStackTop = buffer_ + bufferTotal_;
     165           0 :         header_->copyStackBottom = header_->copyStackTop - bufferUsed_;
     166           0 :         return true;
     167             :     }
     168             : 
     169           0 :     BaselineBailoutInfo* info() {
     170           0 :         MOZ_ASSERT(header_ == reinterpret_cast<BaselineBailoutInfo*>(buffer_));
     171           0 :         return header_;
     172             :     }
     173             : 
     174           0 :     BaselineBailoutInfo* takeBuffer() {
     175           0 :         MOZ_ASSERT(header_ == reinterpret_cast<BaselineBailoutInfo*>(buffer_));
     176           0 :         buffer_ = nullptr;
     177           0 :         return header_;
     178             :     }
     179             : 
     180           0 :     void resetFramePushed() {
     181           0 :         framePushed_ = 0;
     182           0 :     }
     183             : 
     184           0 :     size_t framePushed() const {
     185           0 :         return framePushed_;
     186             :     }
     187             : 
     188           0 :     MOZ_MUST_USE bool subtract(size_t size, const char* info = nullptr) {
     189             :         // enlarge the buffer if need be.
     190           0 :         while (size > bufferAvail_) {
     191           0 :             if (!enlarge())
     192           0 :                 return false;
     193             :         }
     194             : 
     195             :         // write out element.
     196           0 :         header_->copyStackBottom -= size;
     197           0 :         bufferAvail_ -= size;
     198           0 :         bufferUsed_ += size;
     199           0 :         framePushed_ += size;
     200           0 :         if (info) {
     201           0 :             JitSpew(JitSpew_BaselineBailouts,
     202             :                     "      SUB_%03d   %p/%p %-15s",
     203           0 :                     (int) size, header_->copyStackBottom, virtualPointerAtStackOffset(0), info);
     204             :         }
     205           0 :         return true;
     206             :     }
     207             : 
     208             :     template <typename T>
     209           0 :     MOZ_MUST_USE bool write(const T& t) {
     210           0 :         MOZ_ASSERT(!(uintptr_t(&t) >= uintptr_t(header_->copyStackBottom) &&
     211             :                      uintptr_t(&t) < uintptr_t(header_->copyStackTop)),
     212             :                    "Should not reference memory that can be freed");
     213           0 :         if (!subtract(sizeof(T)))
     214           0 :             return false;
     215           0 :         memcpy(header_->copyStackBottom, &t, sizeof(T));
     216           0 :         return true;
     217             :     }
     218             : 
     219             :     template <typename T>
     220           0 :     MOZ_MUST_USE bool writePtr(T* t, const char* info) {
     221           0 :         if (!write<T*>(t))
     222           0 :             return false;
     223           0 :         if (info)
     224           0 :             JitSpew(JitSpew_BaselineBailouts,
     225             :                     "      WRITE_PTR %p/%p %-15s %p",
     226           0 :                     header_->copyStackBottom, virtualPointerAtStackOffset(0), info, t);
     227           0 :         return true;
     228             :     }
     229             : 
     230           0 :     MOZ_MUST_USE bool writeWord(size_t w, const char* info) {
     231           0 :         if (!write<size_t>(w))
     232           0 :             return false;
     233           0 :         if (info) {
     234             :             if (sizeof(size_t) == 4) {
     235             :                 JitSpew(JitSpew_BaselineBailouts,
     236             :                         "      WRITE_WRD %p/%p %-15s %08" PRIxSIZE,
     237             :                         header_->copyStackBottom, virtualPointerAtStackOffset(0), info, w);
     238             :             } else {
     239           0 :                 JitSpew(JitSpew_BaselineBailouts,
     240             :                         "      WRITE_WRD %p/%p %-15s %016" PRIxSIZE,
     241           0 :                         header_->copyStackBottom, virtualPointerAtStackOffset(0), info, w);
     242             :             }
     243             :         }
     244           0 :         return true;
     245             :     }
     246             : 
     247           0 :     MOZ_MUST_USE bool writeValue(const Value& val, const char* info) {
     248           0 :         if (!write<Value>(val))
     249           0 :             return false;
     250           0 :         if (info) {
     251           0 :             JitSpew(JitSpew_BaselineBailouts,
     252             :                     "      WRITE_VAL %p/%p %-15s %016" PRIx64,
     253           0 :                     header_->copyStackBottom, virtualPointerAtStackOffset(0), info,
     254           0 :                     *((uint64_t*) &val));
     255             :         }
     256           0 :         return true;
     257             :     }
     258             : 
     259           0 :     MOZ_MUST_USE bool maybeWritePadding(size_t alignment, size_t after, const char* info) {
     260           0 :         MOZ_ASSERT(framePushed_ % sizeof(Value) == 0);
     261           0 :         MOZ_ASSERT(after % sizeof(Value) == 0);
     262           0 :         size_t offset = ComputeByteAlignment(after, alignment);
     263           0 :         while (framePushed_ % alignment != offset) {
     264           0 :             if (!writeValue(MagicValue(JS_ARG_POISON), info))
     265           0 :                 return false;
     266             :         }
     267             : 
     268           0 :         return true;
     269             :     }
     270             : 
     271           0 :     Value popValue() {
     272           0 :         MOZ_ASSERT(bufferUsed_ >= sizeof(Value));
     273           0 :         MOZ_ASSERT(framePushed_ >= sizeof(Value));
     274           0 :         bufferAvail_ += sizeof(Value);
     275           0 :         bufferUsed_ -= sizeof(Value);
     276           0 :         framePushed_ -= sizeof(Value);
     277           0 :         Value result = *((Value*) header_->copyStackBottom);
     278           0 :         header_->copyStackBottom += sizeof(Value);
     279           0 :         return result;
     280             :     }
     281             : 
     282           0 :     void popValueInto(PCMappingSlotInfo::SlotLocation loc) {
     283           0 :         MOZ_ASSERT(PCMappingSlotInfo::ValidSlotLocation(loc));
     284           0 :         switch(loc) {
     285             :           case PCMappingSlotInfo::SlotInR0:
     286           0 :             header_->setR0 = 1;
     287           0 :             header_->valueR0 = popValue();
     288           0 :             break;
     289             :           case PCMappingSlotInfo::SlotInR1:
     290           0 :             header_->setR1 = 1;
     291           0 :             header_->valueR1 = popValue();
     292           0 :             break;
     293             :           default:
     294           0 :             MOZ_ASSERT(loc == PCMappingSlotInfo::SlotIgnore);
     295           0 :             popValue();
     296           0 :             break;
     297             :         }
     298           0 :     }
     299             : 
     300           0 :     void setResumeFramePtr(void* resumeFramePtr) {
     301           0 :         header_->resumeFramePtr = resumeFramePtr;
     302           0 :     }
     303             : 
     304           0 :     void setResumeAddr(void* resumeAddr) {
     305           0 :         header_->resumeAddr = resumeAddr;
     306           0 :     }
     307             : 
     308           0 :     void setResumePC(jsbytecode* pc) {
     309           0 :         header_->resumePC = pc;
     310           0 :     }
     311             : 
     312           0 :     void setMonitorStub(ICStub* stub) {
     313           0 :         header_->monitorStub = stub;
     314           0 :     }
     315             : 
     316             :     template <typename T>
     317           0 :     BufferPointer<T> pointerAtStackOffset(size_t offset) {
     318           0 :         if (offset < bufferUsed_) {
     319             :             // Calculate offset from copyStackTop.
     320           0 :             offset = header_->copyStackTop - (header_->copyStackBottom + offset);
     321           0 :             return BufferPointer<T>(&header_, offset, /* heap = */ true);
     322             :         }
     323             : 
     324           0 :         return BufferPointer<T>(&header_, offset - bufferUsed_, /* heap = */ false);
     325             :     }
     326             : 
     327           0 :     BufferPointer<Value> valuePointerAtStackOffset(size_t offset) {
     328           0 :         return pointerAtStackOffset<Value>(offset);
     329             :     }
     330             : 
     331           0 :     inline uint8_t* virtualPointerAtStackOffset(size_t offset) {
     332           0 :         if (offset < bufferUsed_)
     333           0 :             return reinterpret_cast<uint8_t*>(frame_) - (bufferUsed_ - offset);
     334           0 :         return reinterpret_cast<uint8_t*>(frame_) + (offset - bufferUsed_);
     335             :     }
     336             : 
     337           0 :     inline JitFrameLayout* startFrame() {
     338           0 :         return frame_;
     339             :     }
     340             : 
     341           0 :     BufferPointer<JitFrameLayout> topFrameAddress() {
     342           0 :         return pointerAtStackOffset<JitFrameLayout>(0);
     343             :     }
     344             : 
     345             :     //
     346             :     // This method should only be called when the builder is in a state where it is
     347             :     // starting to construct the stack frame for the next callee.  This means that
     348             :     // the lowest value on the constructed stack is the return address for the previous
     349             :     // caller frame.
     350             :     //
     351             :     // This method is used to compute the value of the frame pointer (e.g. ebp on x86)
     352             :     // that would have been saved by the baseline jitcode when it was entered.  In some
     353             :     // cases, this value can be bogus since we can ensure that the caller would have saved
     354             :     // it anyway.
     355             :     //
     356           0 :     void* calculatePrevFramePtr() {
     357             :         // Get the incoming frame.
     358           0 :         BufferPointer<JitFrameLayout> topFrame = topFrameAddress();
     359           0 :         FrameType type = topFrame->prevType();
     360             : 
     361             :         // For IonJS, IonICCall and Entry frames, the "saved" frame pointer
     362             :         // in the baseline frame is meaningless, since Ion saves all registers
     363             :         // before calling other ion frames, and the entry frame saves all
     364             :         // registers too.
     365           0 :         if (type == JitFrame_IonJS || type == JitFrame_Entry || type == JitFrame_IonICCall)
     366           0 :             return nullptr;
     367             : 
     368             :         // BaselineStub - Baseline calling into Ion.
     369             :         //  PrevFramePtr needs to point to the BaselineStubFrame's saved frame pointer.
     370             :         //      STACK_START_ADDR + JitFrameLayout::Size() + PREV_FRAME_SIZE
     371             :         //                      - BaselineStubFrameLayout::reverseOffsetOfSavedFramePtr()
     372           0 :         if (type == JitFrame_BaselineStub) {
     373           0 :             size_t offset = JitFrameLayout::Size() + topFrame->prevFrameLocalSize() +
     374           0 :                             BaselineStubFrameLayout::reverseOffsetOfSavedFramePtr();
     375           0 :             return virtualPointerAtStackOffset(offset);
     376             :         }
     377             : 
     378           0 :         MOZ_ASSERT(type == JitFrame_Rectifier);
     379             :         // Rectifier - behaviour depends on the frame preceding the rectifier frame, and
     380             :         // whether the arch is x86 or not.  The x86 rectifier frame saves the frame pointer,
     381             :         // so we can calculate it directly.  For other archs, the previous frame pointer
     382             :         // is stored on the stack in the frame that precedes the rectifier frame.
     383           0 :         size_t priorOffset = JitFrameLayout::Size() + topFrame->prevFrameLocalSize();
     384             : #if defined(JS_CODEGEN_X86)
     385             :         // On X86, the FramePointer is pushed as the first value in the Rectifier frame.
     386             :         MOZ_ASSERT(BaselineFrameReg == FramePointer);
     387             :         priorOffset -= sizeof(void*);
     388             :         return virtualPointerAtStackOffset(priorOffset);
     389             : #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
     390             :       defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \
     391             :       defined(JS_CODEGEN_X64)
     392             :         // On X64, ARM, ARM64, and MIPS, the frame pointer save location depends on
     393             :         // the caller of the rectifier frame.
     394             :         BufferPointer<RectifierFrameLayout> priorFrame =
     395           0 :             pointerAtStackOffset<RectifierFrameLayout>(priorOffset);
     396           0 :         FrameType priorType = priorFrame->prevType();
     397           0 :         MOZ_ASSERT(priorType == JitFrame_IonJS || priorType == JitFrame_BaselineStub);
     398             : 
     399             :         // If the frame preceding the rectifier is an IonJS frame, then once again
     400             :         // the frame pointer does not matter.
     401           0 :         if (priorType == JitFrame_IonJS)
     402           0 :             return nullptr;
     403             : 
     404             :         // Otherwise, the frame preceding the rectifier is a BaselineStub frame.
     405             :         //  let X = STACK_START_ADDR + JitFrameLayout::Size() + PREV_FRAME_SIZE
     406             :         //      X + RectifierFrameLayout::Size()
     407             :         //        + ((RectifierFrameLayout*) X)->prevFrameLocalSize()
     408             :         //        - BaselineStubFrameLayout::reverseOffsetOfSavedFramePtr()
     409           0 :         size_t extraOffset = RectifierFrameLayout::Size() + priorFrame->prevFrameLocalSize() +
     410           0 :                              BaselineStubFrameLayout::reverseOffsetOfSavedFramePtr();
     411           0 :         return virtualPointerAtStackOffset(priorOffset + extraOffset);
     412             : #elif defined(JS_CODEGEN_NONE)
     413             :         MOZ_CRASH();
     414             : #else
     415             : #  error "Bad architecture!"
     416             : #endif
     417             :     }
     418             : 
     419           0 :     void setCheckGlobalDeclarationConflicts() {
     420           0 :         header_->checkGlobalDeclarationConflicts = true;
     421           0 :     }
     422             : };
     423             : 
     424             : // Ensure that all value locations are readable from the SnapshotIterator.
     425             : // Remove RInstructionResults from the JitActivation if the frame got recovered
     426             : // ahead of the bailout.
     427             : class SnapshotIteratorForBailout : public SnapshotIterator
     428             : {
     429             :     JitActivation* activation_;
     430             :     JitFrameIterator& iter_;
     431             : 
     432             :   public:
     433           0 :     SnapshotIteratorForBailout(JitActivation* activation, JitFrameIterator& iter)
     434           0 :       : SnapshotIterator(iter, activation->bailoutData()->machineState()),
     435             :         activation_(activation),
     436           0 :         iter_(iter)
     437             :     {
     438           0 :         MOZ_ASSERT(iter.isBailoutJS());
     439           0 :     }
     440             : 
     441           0 :     ~SnapshotIteratorForBailout() {
     442             :         // The bailout is complete, we no longer need the recover instruction
     443             :         // results.
     444           0 :         activation_->removeIonFrameRecovery(fp_);
     445           0 :     }
     446             : 
     447             :     // Take previously computed result out of the activation, or compute the
     448             :     // results of all recover instructions contained in the snapshot.
     449           0 :     MOZ_MUST_USE bool init(JSContext* cx) {
     450             : 
     451             :         // Under a bailout, there is no need to invalidate the frame after
     452             :         // evaluating the recover instruction, as the invalidation is only
     453             :         // needed to cause of the frame which has been introspected.
     454           0 :         MaybeReadFallback recoverBailout(cx, activation_, &iter_, MaybeReadFallback::Fallback_DoNothing);
     455           0 :         return initInstructionResults(recoverBailout);
     456             :     }
     457             : };
     458             : 
     459             : #ifdef DEBUG
     460             : static inline bool
     461           0 : IsInlinableFallback(ICFallbackStub* icEntry)
     462             : {
     463           0 :     return icEntry->isCall_Fallback() || icEntry->isGetProp_Fallback() ||
     464           0 :            icEntry->isSetProp_Fallback();
     465             : }
     466             : #endif
     467             : 
     468             : static inline void*
     469           0 : GetStubReturnAddress(JSContext* cx, jsbytecode* pc)
     470             : {
     471           0 :     JitCompartment* jitComp = cx->compartment()->jitCompartment();
     472             : 
     473           0 :     if (IsGetPropPC(pc))
     474           0 :         return jitComp->bailoutReturnAddr(BailoutReturnStub::GetProp);
     475           0 :     if (IsSetPropPC(pc))
     476           0 :         return jitComp->bailoutReturnAddr(BailoutReturnStub::SetProp);
     477             : 
     478             :     // This should be a call op of some kind, now.
     479           0 :     MOZ_ASSERT(IsCallPC(pc) && !IsSpreadCallPC(pc));
     480           0 :     if (IsConstructorCallPC(pc))
     481           0 :         return jitComp->bailoutReturnAddr(BailoutReturnStub::New);
     482           0 :     return jitComp->bailoutReturnAddr(BailoutReturnStub::Call);
     483             : }
     484             : 
     485             : static inline jsbytecode*
     486           0 : GetNextNonLoopEntryPc(jsbytecode* pc)
     487             : {
     488           0 :     JSOp op = JSOp(*pc);
     489           0 :     if (op == JSOP_GOTO)
     490           0 :         return pc + GET_JUMP_OFFSET(pc);
     491           0 :     if (op == JSOP_LOOPENTRY || op == JSOP_NOP || op == JSOP_LOOPHEAD)
     492           0 :         return GetNextPc(pc);
     493           0 :     return pc;
     494             : }
     495             : 
     496             : static bool
     497           0 : HasLiveStackValueAtDepth(JSScript* script, jsbytecode* pc, uint32_t stackDepth)
     498             : {
     499           0 :     if (!script->hasTrynotes())
     500           0 :         return false;
     501             : 
     502           0 :     JSTryNote* tn = script->trynotes()->vector;
     503           0 :     JSTryNote* tnEnd = tn + script->trynotes()->length;
     504           0 :     uint32_t pcOffset = uint32_t(pc - script->main());
     505           0 :     for (; tn != tnEnd; ++tn) {
     506           0 :         if (pcOffset < tn->start)
     507           0 :             continue;
     508           0 :         if (pcOffset >= tn->start + tn->length)
     509           0 :             continue;
     510             : 
     511           0 :         switch (tn->kind) {
     512             :           case JSTRY_FOR_IN:
     513             :             // For-in loops have only the iterator on stack.
     514           0 :             if (stackDepth == tn->stackDepth)
     515           0 :                 return true;
     516           0 :             break;
     517             : 
     518             :           case JSTRY_FOR_OF:
     519             :             // For-of loops have the iterator and the result.value on stack.
     520             :             // The iterator is below the result.value.
     521           0 :             if (stackDepth == tn->stackDepth - 1)
     522           0 :                 return true;
     523           0 :             break;
     524             : 
     525             :           case JSTRY_DESTRUCTURING_ITERCLOSE:
     526             :             // Destructuring code that need to call IteratorClose have both
     527             :             // the iterator and the "done" value on the stack.
     528           0 :             if (stackDepth == tn->stackDepth || stackDepth == tn->stackDepth - 1)
     529           0 :                 return true;
     530           0 :             break;
     531             : 
     532             :           default:
     533           0 :             break;
     534             :         }
     535             :     }
     536             : 
     537           0 :     return false;
     538             : }
     539             : 
     540             : static bool
     541           0 : IsPrologueBailout(const SnapshotIterator& iter, const ExceptionBailoutInfo* excInfo)
     542             : {
     543             :     // If we are propagating an exception for debug mode, we will not resume
     544             :     // into baseline code, but instead into HandleExceptionBaseline (i.e.,
     545             :     // never before the prologue).
     546           0 :     return iter.pcOffset() == 0 && !iter.resumeAfter() &&
     547           0 :            (!excInfo || !excInfo->propagatingIonExceptionForDebugMode());
     548             : }
     549             : 
     550             : // For every inline frame, we write out the following data:
     551             : //
     552             : //                      |      ...      |
     553             : //                      +---------------+
     554             : //                      |  Descr(???)   |  --- Descr size here is (PREV_FRAME_SIZE)
     555             : //                      +---------------+
     556             : //                      |  ReturnAddr   |
     557             : //             --       +===============+  --- OVERWRITE STARTS HERE  (START_STACK_ADDR)
     558             : //             |        | PrevFramePtr  |
     559             : //             |    +-> +---------------+
     560             : //             |    |   |   Baseline    |
     561             : //             |    |   |    Frame      |
     562             : //             |    |   +---------------+
     563             : //             |    |   |    Fixed0     |
     564             : //             |    |   +---------------+
     565             : //         +--<     |   |     ...       |
     566             : //         |   |    |   +---------------+
     567             : //         |   |    |   |    FixedF     |
     568             : //         |   |    |   +---------------+
     569             : //         |   |    |   |    Stack0     |
     570             : //         |   |    |   +---------------+
     571             : //         |   |    |   |     ...       |
     572             : //         |   |    |   +---------------+
     573             : //         |   |    |   |    StackS     |
     574             : //         |   --   |   +---------------+  --- IF NOT LAST INLINE FRAME,
     575             : //         +------------|  Descr(BLJS)  |  --- CALLING INFO STARTS HERE
     576             : //                  |   +---------------+
     577             : //                  |   |  ReturnAddr   | <-- return into main jitcode after IC
     578             : //             --   |   +===============+
     579             : //             |    |   |    StubPtr    |
     580             : //             |    |   +---------------+
     581             : //             |    +---|   FramePtr    |
     582             : //             |        +---------------+  --- The inlined frame might OSR in Ion
     583             : //             |        |   Padding?    |  --- Thus the return address should be aligned.
     584             : //             |        +---------------+
     585             : //         +--<         |     ArgA      |
     586             : //         |   |        +---------------+
     587             : //         |   |        |     ...       |
     588             : //         |   |        +---------------+
     589             : //         |   |        |     Arg0      |
     590             : //         |   |        +---------------+
     591             : //         |   |        |     ThisV     |
     592             : //         |   --       +---------------+
     593             : //         |            |  ActualArgC   |
     594             : //         |            +---------------+
     595             : //         |            |  CalleeToken  |
     596             : //         |            +---------------+
     597             : //         +------------| Descr(BLStub) |
     598             : //                      +---------------+
     599             : //                      |  ReturnAddr   | <-- return into ICCall_Scripted IC
     600             : //             --       +===============+ --- IF CALLEE FORMAL ARGS > ActualArgC
     601             : //             |        |   Padding?    |
     602             : //             |        +---------------+
     603             : //             |        |  UndefinedU   |
     604             : //             |        +---------------+
     605             : //             |        |     ...       |
     606             : //             |        +---------------+
     607             : //             |        |  Undefined0   |
     608             : //         +--<         +---------------+
     609             : //         |   |        |     ArgA      |
     610             : //         |   |        +---------------+
     611             : //         |   |        |     ...       |
     612             : //         |   |        +---------------+
     613             : //         |   |        |     Arg0      |
     614             : //         |   |        +---------------+
     615             : //         |   |        |     ThisV     |
     616             : //         |   --       +---------------+
     617             : //         |            |  ActualArgC   |
     618             : //         |            +---------------+
     619             : //         |            |  CalleeToken  |
     620             : //         |            +---------------+
     621             : //         +------------|  Descr(Rect)  |
     622             : //                      +---------------+
     623             : //                      |  ReturnAddr   | <-- return into ArgumentsRectifier after call
     624             : //                      +===============+
     625             : //
     626             : static bool
     627           0 : InitFromBailout(JSContext* cx, HandleScript caller, jsbytecode* callerPC,
     628             :                 HandleFunction fun, HandleScript script, IonScript* ionScript,
     629             :                 SnapshotIterator& iter, bool invalidate, BaselineStackBuilder& builder,
     630             :                 MutableHandle<GCVector<Value>> startFrameFormals, MutableHandleFunction nextCallee,
     631             :                 jsbytecode** callPC, const ExceptionBailoutInfo* excInfo)
     632             : {
     633             :     // The Baseline frames we will reconstruct on the heap are not rooted, so GC
     634             :     // must be suppressed here.
     635           0 :     MOZ_ASSERT(cx->suppressGC);
     636             : 
     637           0 :     MOZ_ASSERT(script->hasBaselineScript());
     638             : 
     639             :     // Are we catching an exception?
     640           0 :     bool catchingException = excInfo && excInfo->catchingException();
     641             : 
     642             :     // If we are catching an exception, we are bailing out to a catch or
     643             :     // finally block and this is the frame where we will resume. Usually the
     644             :     // expression stack should be empty in this case but there can be
     645             :     // iterators on the stack.
     646             :     uint32_t exprStackSlots;
     647           0 :     if (catchingException)
     648           0 :         exprStackSlots = excInfo->numExprSlots();
     649             :     else
     650           0 :         exprStackSlots = iter.numAllocations() - (script->nfixed() + CountArgSlots(script, fun));
     651             : 
     652           0 :     builder.resetFramePushed();
     653             : 
     654             :     // Build first baseline frame:
     655             :     // +===============+
     656             :     // | PrevFramePtr  |
     657             :     // +---------------+
     658             :     // |   Baseline    |
     659             :     // |    Frame      |
     660             :     // +---------------+
     661             :     // |    Fixed0     |
     662             :     // +---------------+
     663             :     // |     ...       |
     664             :     // +---------------+
     665             :     // |    FixedF     |
     666             :     // +---------------+
     667             :     // |    Stack0     |
     668             :     // +---------------+
     669             :     // |     ...       |
     670             :     // +---------------+
     671             :     // |    StackS     |
     672             :     // +---------------+  --- IF NOT LAST INLINE FRAME,
     673             :     // |  Descr(BLJS)  |  --- CALLING INFO STARTS HERE
     674             :     // +---------------+
     675             :     // |  ReturnAddr   | <-- return into main jitcode after IC
     676             :     // +===============+
     677             : 
     678           0 :     JitSpew(JitSpew_BaselineBailouts, "      Unpacking %s:%" PRIuSIZE, script->filename(), script->lineno());
     679           0 :     JitSpew(JitSpew_BaselineBailouts, "      [BASELINE-JS FRAME]");
     680             : 
     681             :     // Calculate and write the previous frame pointer value.
     682             :     // Record the virtual stack offset at this location.  Later on, if we end up
     683             :     // writing out a BaselineStub frame for the next callee, we'll need to save the
     684             :     // address.
     685           0 :     void* prevFramePtr = builder.calculatePrevFramePtr();
     686           0 :     if (!builder.writePtr(prevFramePtr, "PrevFramePtr"))
     687           0 :         return false;
     688           0 :     prevFramePtr = builder.virtualPointerAtStackOffset(0);
     689             : 
     690             :     // Write struct BaselineFrame.
     691           0 :     if (!builder.subtract(BaselineFrame::Size(), "BaselineFrame"))
     692           0 :         return false;
     693           0 :     BufferPointer<BaselineFrame> blFrame = builder.pointerAtStackOffset<BaselineFrame>(0);
     694             : 
     695           0 :     uint32_t flags = 0;
     696             : 
     697             :     // If we are bailing to a script whose execution is observed, mark the
     698             :     // baseline frame as a debuggee frame. This is to cover the case where we
     699             :     // don't rematerialize the Ion frame via the Debugger.
     700           0 :     if (script->isDebuggee())
     701           0 :         flags |= BaselineFrame::DEBUGGEE;
     702             : 
     703             :     // Initialize BaselineFrame's envChain and argsObj
     704           0 :     JSObject* envChain = nullptr;
     705           0 :     Value returnValue;
     706           0 :     ArgumentsObject* argsObj = nullptr;
     707           0 :     BailoutKind bailoutKind = iter.bailoutKind();
     708           0 :     if (bailoutKind == Bailout_ArgumentCheck) {
     709             :         // Temporary hack -- skip the (unused) envChain, because it could be
     710             :         // bogus (we can fail before the env chain slot is set). Strip the
     711             :         // hasEnvironmentChain flag and this will be fixed up later in
     712             :         // |FinishBailoutToBaseline|, which calls
     713             :         // |EnsureHasEnvironmentObjects|.
     714           0 :         JitSpew(JitSpew_BaselineBailouts, "      Bailout_ArgumentCheck! (no valid envChain)");
     715           0 :         iter.skip();
     716             : 
     717             :         // skip |return value|
     718           0 :         iter.skip();
     719           0 :         returnValue = UndefinedValue();
     720             : 
     721             :         // Scripts with |argumentsHasVarBinding| have an extra slot.
     722           0 :         if (script->argumentsHasVarBinding()) {
     723             :             JitSpew(JitSpew_BaselineBailouts,
     724             :                     "      Bailout_ArgumentCheck for script with argumentsHasVarBinding!"
     725           0 :                     "Using empty arguments object");
     726           0 :             iter.skip();
     727             :         }
     728             :     } else {
     729           0 :         Value v = iter.read();
     730           0 :         if (v.isObject()) {
     731           0 :             envChain = &v.toObject();
     732             : 
     733             :             // If Ion has updated env slot from UndefinedValue, it will be the
     734             :             // complete initial environment, so we can set the HAS_INITIAL_ENV
     735             :             // flag if needed.
     736           0 :             if (fun && fun->needsFunctionEnvironmentObjects()) {
     737           0 :                 MOZ_ASSERT(fun->nonLazyScript()->initialEnvironmentShape());
     738           0 :                 MOZ_ASSERT(!fun->needsExtraBodyVarEnvironment());
     739           0 :                 flags |= BaselineFrame::HAS_INITIAL_ENV;
     740             :             }
     741             :         } else {
     742           0 :             MOZ_ASSERT(v.isUndefined() || v.isMagic(JS_OPTIMIZED_OUT));
     743             : 
     744             : #ifdef DEBUG
     745             :             // The |envChain| slot must not be optimized out if the currently
     746             :             // active scope requires any EnvironmentObjects beyond what is
     747             :             // available at body scope. This checks that scope chain does not
     748             :             // require any such EnvironmentObjects.
     749             :             // See also: |CompileInfo::isObservableFrameSlot|
     750           0 :             jsbytecode* pc = script->offsetToPC(iter.pcOffset());
     751           0 :             Scope* scopeIter = script->innermostScope(pc);
     752           0 :             while (scopeIter != script->bodyScope()) {
     753           0 :                 MOZ_ASSERT(scopeIter);
     754           0 :                 MOZ_ASSERT(!scopeIter->hasEnvironment());
     755           0 :                 scopeIter = scopeIter->enclosing();
     756             :             }
     757             : #endif
     758             : 
     759             :             // Get env chain from function or script.
     760           0 :             if (fun) {
     761             :                 // If pcOffset == 0, we may have to push a new call object, so
     762             :                 // we leave envChain nullptr and enter baseline code before
     763             :                 // the prologue.
     764           0 :                 if (!IsPrologueBailout(iter, excInfo))
     765           0 :                     envChain = fun->environment();
     766           0 :             } else if (script->module()) {
     767           0 :                 envChain = script->module()->environment();
     768             :             } else {
     769             :                 // For global scripts without a non-syntactic env the env
     770             :                 // chain is the script's global lexical environment (Ion does
     771             :                 // not compile scripts with a non-syntactic global scope).
     772             :                 // Also note that it's invalid to resume into the prologue in
     773             :                 // this case because the prologue expects the env chain in R1
     774             :                 // for eval and global scripts.
     775           0 :                 MOZ_ASSERT(!script->isForEval());
     776           0 :                 MOZ_ASSERT(!script->hasNonSyntacticScope());
     777           0 :                 envChain = &(script->global().lexicalEnvironment());
     778             : 
     779             :                 // We have possibly bailed out before Ion could do the global
     780             :                 // declaration conflicts check. Since it's invalid to resume
     781             :                 // into the prologue, set a flag so FinishBailoutToBaseline
     782             :                 // can do the conflict check.
     783           0 :                 if (IsPrologueBailout(iter, excInfo))
     784           0 :                     builder.setCheckGlobalDeclarationConflicts();
     785             :             }
     786             :         }
     787             : 
     788             :         // Make sure to add HAS_RVAL to flags here because setFlags() below
     789             :         // will clobber it.
     790           0 :         returnValue = iter.read();
     791           0 :         flags |= BaselineFrame::HAS_RVAL;
     792             : 
     793             :         // If script maybe has an arguments object, the third slot will hold it.
     794           0 :         if (script->argumentsHasVarBinding()) {
     795           0 :             v = iter.read();
     796           0 :             MOZ_ASSERT(v.isObject() || v.isUndefined() || v.isMagic(JS_OPTIMIZED_OUT));
     797           0 :             if (v.isObject())
     798           0 :                 argsObj = &v.toObject().as<ArgumentsObject>();
     799             :         }
     800             :     }
     801           0 :     JitSpew(JitSpew_BaselineBailouts, "      EnvChain=%p", envChain);
     802           0 :     blFrame->setEnvironmentChain(envChain);
     803           0 :     JitSpew(JitSpew_BaselineBailouts, "      ReturnValue=%016" PRIx64, *((uint64_t*) &returnValue));
     804           0 :     blFrame->setReturnValue(returnValue);
     805             : 
     806             :     // Do not need to initialize scratchValue field in BaselineFrame.
     807           0 :     blFrame->setFlags(flags);
     808             : 
     809             :     // initArgsObjUnchecked modifies the frame's flags, so call it after setFlags.
     810           0 :     if (argsObj)
     811           0 :         blFrame->initArgsObjUnchecked(*argsObj);
     812             : 
     813           0 :     if (fun) {
     814             :         // The unpacked thisv and arguments should overwrite the pushed args present
     815             :         // in the calling frame.
     816           0 :         Value thisv = iter.read();
     817           0 :         JitSpew(JitSpew_BaselineBailouts, "      Is function!");
     818           0 :         JitSpew(JitSpew_BaselineBailouts, "      thisv=%016" PRIx64, *((uint64_t*) &thisv));
     819             : 
     820           0 :         size_t thisvOffset = builder.framePushed() + JitFrameLayout::offsetOfThis();
     821           0 :         builder.valuePointerAtStackOffset(thisvOffset).set(thisv);
     822             : 
     823           0 :         MOZ_ASSERT(iter.numAllocations() >= CountArgSlots(script, fun));
     824           0 :         JitSpew(JitSpew_BaselineBailouts, "      frame slots %u, nargs %" PRIuSIZE ", nfixed %" PRIuSIZE,
     825           0 :                 iter.numAllocations(), fun->nargs(), script->nfixed());
     826             : 
     827           0 :         if (!callerPC) {
     828             :             // This is the first frame. Store the formals in a Vector until we
     829             :             // are done. Due to UCE and phi elimination, we could store an
     830             :             // UndefinedValue() here for formals we think are unused, but
     831             :             // locals may still reference the original argument slot
     832             :             // (MParameter/LArgument) and expect the original Value.
     833           0 :             MOZ_ASSERT(startFrameFormals.empty());
     834           0 :             if (!startFrameFormals.resize(fun->nargs()))
     835           0 :                 return false;
     836             :         }
     837             : 
     838           0 :         for (uint32_t i = 0; i < fun->nargs(); i++) {
     839           0 :             Value arg = iter.read();
     840           0 :             JitSpew(JitSpew_BaselineBailouts, "      arg %d = %016" PRIx64,
     841           0 :                         (int) i, *((uint64_t*) &arg));
     842           0 :             if (callerPC) {
     843           0 :                 size_t argOffset = builder.framePushed() + JitFrameLayout::offsetOfActualArg(i);
     844           0 :                 builder.valuePointerAtStackOffset(argOffset).set(arg);
     845             :             } else {
     846           0 :                 startFrameFormals[i].set(arg);
     847             :             }
     848             :         }
     849             :     }
     850             : 
     851           0 :     for (uint32_t i = 0; i < script->nfixed(); i++) {
     852           0 :         Value slot = iter.read();
     853           0 :         if (!builder.writeValue(slot, "FixedValue"))
     854           0 :             return false;
     855             :     }
     856             : 
     857             :     // Get the pc. If we are handling an exception, resume at the pc of the
     858             :     // catch or finally block.
     859           0 :     jsbytecode* pc = catchingException ? excInfo->resumePC() : script->offsetToPC(iter.pcOffset());
     860           0 :     bool resumeAfter = catchingException ? false : iter.resumeAfter();
     861             : 
     862             :     // When pgo is enabled, increment the counter of the block in which we
     863             :     // resume, as Ion does not keep track of the code coverage.
     864             :     //
     865             :     // We need to do that when pgo is enabled, as after a specific number of
     866             :     // FirstExecution bailouts, we invalidate and recompile the script with
     867             :     // IonMonkey. Failing to increment the counter of the current basic block
     868             :     // might lead to repeated bailouts and invalidations.
     869           0 :     if (!JitOptions.disablePgo && script->hasScriptCounts())
     870           0 :         script->incHitCount(pc);
     871             : 
     872           0 :     JSOp op = JSOp(*pc);
     873             : 
     874             :     // Inlining of SPREADCALL-like frames not currently supported.
     875           0 :     MOZ_ASSERT_IF(IsSpreadCallPC(pc), !iter.moreFrames());
     876             : 
     877             :     // Fixup inlined JSOP_FUNCALL, JSOP_FUNAPPLY, and accessors on the caller side.
     878             :     // On the caller side this must represent like the function wasn't inlined.
     879           0 :     uint32_t pushedSlots = 0;
     880           0 :     AutoValueVector savedCallerArgs(cx);
     881           0 :     bool needToSaveArgs = op == JSOP_FUNAPPLY || IsGetPropPC(pc) || IsSetPropPC(pc);
     882           0 :     if (iter.moreFrames() && (op == JSOP_FUNCALL || needToSaveArgs))
     883             :     {
     884           0 :         uint32_t inlined_args = 0;
     885           0 :         if (op == JSOP_FUNCALL) {
     886           0 :             inlined_args = 2 + GET_ARGC(pc) - 1;
     887           0 :         } else if (op == JSOP_FUNAPPLY) {
     888           0 :             inlined_args = 2 + blFrame->numActualArgs();
     889             :         } else {
     890           0 :             MOZ_ASSERT(IsGetPropPC(pc) || IsSetPropPC(pc));
     891           0 :             inlined_args = 2 + IsSetPropPC(pc);
     892             :         }
     893             : 
     894           0 :         MOZ_ASSERT(exprStackSlots >= inlined_args);
     895           0 :         pushedSlots = exprStackSlots - inlined_args;
     896             : 
     897             :         JitSpew(JitSpew_BaselineBailouts,
     898             :                 "      pushing %u expression stack slots before fixup",
     899           0 :                 pushedSlots);
     900           0 :         for (uint32_t i = 0; i < pushedSlots; i++) {
     901           0 :             Value v = iter.read();
     902           0 :             if (!builder.writeValue(v, "StackValue"))
     903           0 :                 return false;
     904             :         }
     905             : 
     906           0 :         if (op == JSOP_FUNCALL) {
     907             :             // When funcall got inlined and the native js_fun_call was bypassed,
     908             :             // the stack state is incorrect. To restore correctly it must look like
     909             :             // js_fun_call was actually called. This means transforming the stack
     910             :             // from |target, this, args| to |js_fun_call, target, this, args|
     911             :             // The js_fun_call is never read, so just pushing undefined now.
     912           0 :             JitSpew(JitSpew_BaselineBailouts, "      pushing undefined to fixup funcall");
     913           0 :             if (!builder.writeValue(UndefinedValue(), "StackValue"))
     914           0 :                 return false;
     915             :         }
     916             : 
     917           0 :         if (needToSaveArgs) {
     918             :             // When an accessor is inlined, the whole thing is a lie. There
     919             :             // should never have been a call there. Fix the caller's stack to
     920             :             // forget it ever happened.
     921             : 
     922             :             // When funapply gets inlined we take all arguments out of the
     923             :             // arguments array. So the stack state is incorrect. To restore
     924             :             // correctly it must look like js_fun_apply was actually called.
     925             :             // This means transforming the stack from |target, this, arg1, ...|
     926             :             // to |js_fun_apply, target, this, argObject|.
     927             :             // Since the information is never read, we can just push undefined
     928             :             // for all values.
     929           0 :             if (op == JSOP_FUNAPPLY) {
     930           0 :                 JitSpew(JitSpew_BaselineBailouts, "      pushing 4x undefined to fixup funapply");
     931           0 :                 if (!builder.writeValue(UndefinedValue(), "StackValue"))
     932           0 :                     return false;
     933           0 :                 if (!builder.writeValue(UndefinedValue(), "StackValue"))
     934           0 :                     return false;
     935           0 :                 if (!builder.writeValue(UndefinedValue(), "StackValue"))
     936           0 :                     return false;
     937           0 :                 if (!builder.writeValue(UndefinedValue(), "StackValue"))
     938           0 :                     return false;
     939             :             }
     940             :             // Save the actual arguments. They are needed on the callee side
     941             :             // as the arguments. Else we can't recover them.
     942           0 :             if (!savedCallerArgs.resize(inlined_args))
     943           0 :                 return false;
     944           0 :             for (uint32_t i = 0; i < inlined_args; i++)
     945           0 :                 savedCallerArgs[i].set(iter.read());
     946             : 
     947           0 :             if (IsSetPropPC(pc)) {
     948             :                 // We would love to just save all the arguments and leave them
     949             :                 // in the stub frame pushed below, but we will lose the inital
     950             :                 // argument which the function was called with, which we must
     951             :                 // leave on the stack. It's pushed as the result of the SETPROP.
     952           0 :                 Value initialArg = savedCallerArgs[inlined_args - 1];
     953           0 :                 JitSpew(JitSpew_BaselineBailouts, "     pushing setter's initial argument");
     954           0 :                 if (!builder.writeValue(initialArg, "StackValue"))
     955           0 :                     return false;
     956             :             }
     957           0 :             pushedSlots = exprStackSlots;
     958             :         }
     959             :     }
     960             : 
     961           0 :     JitSpew(JitSpew_BaselineBailouts, "      pushing %u expression stack slots",
     962           0 :                                       exprStackSlots - pushedSlots);
     963           0 :     for (uint32_t i = pushedSlots; i < exprStackSlots; i++) {
     964           0 :         Value v;
     965             : 
     966           0 :         if (!iter.moreFrames() && i == exprStackSlots - 1 &&
     967           0 :             cx->hasIonReturnOverride())
     968             :         {
     969             :             // If coming from an invalidation bailout, and this is the topmost
     970             :             // value, and a value override has been specified, don't read from the
     971             :             // iterator. Otherwise, we risk using a garbage value.
     972           0 :             MOZ_ASSERT(invalidate);
     973           0 :             iter.skip();
     974           0 :             JitSpew(JitSpew_BaselineBailouts, "      [Return Override]");
     975           0 :             v = cx->takeIonReturnOverride();
     976           0 :         } else if (excInfo && excInfo->propagatingIonExceptionForDebugMode()) {
     977             :             // If we are in the middle of propagating an exception from Ion by
     978             :             // bailing to baseline due to debug mode, we might not have all
     979             :             // the stack if we are at the newest frame.
     980             :             //
     981             :             // For instance, if calling |f()| pushed an Ion frame which threw,
     982             :             // the snapshot expects the return value to be pushed, but it's
     983             :             // possible nothing was pushed before we threw. We can't drop
     984             :             // iterators, however, so read them out. They will be closed by
     985             :             // HandleExceptionBaseline.
     986           0 :             MOZ_ASSERT(cx->compartment()->isDebuggee());
     987           0 :             if (iter.moreFrames() || HasLiveStackValueAtDepth(script, pc, i + 1)) {
     988           0 :                 v = iter.read();
     989             :             } else {
     990           0 :                 iter.skip();
     991           0 :                 v = MagicValue(JS_OPTIMIZED_OUT);
     992             :             }
     993             :         } else {
     994           0 :             v = iter.read();
     995             :         }
     996           0 :         if (!builder.writeValue(v, "StackValue"))
     997           0 :             return false;
     998             :     }
     999             : 
    1000             :     // BaselineFrame::frameSize is the size of everything pushed since
    1001             :     // the builder.resetFramePushed() call.
    1002           0 :     uint32_t frameSize = builder.framePushed();
    1003           0 :     blFrame->setFrameSize(frameSize);
    1004           0 :     JitSpew(JitSpew_BaselineBailouts, "      FrameSize=%u", frameSize);
    1005             : 
    1006             :     // numValueSlots() is based on the frame size, do some sanity checks.
    1007           0 :     MOZ_ASSERT(blFrame->numValueSlots() >= script->nfixed());
    1008           0 :     MOZ_ASSERT(blFrame->numValueSlots() <= script->nslots());
    1009             : 
    1010             :     // If we are resuming at a LOOPENTRY op, resume at the next op to avoid
    1011             :     // a bailout -> enter Ion -> bailout loop with --ion-eager. See also
    1012             :     // ThunkToInterpreter.
    1013             :     //
    1014             :     // The algorithm below is the "tortoise and the hare" algorithm. See bug
    1015             :     // 994444 for more explanation.
    1016           0 :     if (!resumeAfter) {
    1017           0 :         jsbytecode* fasterPc = pc;
    1018             :         while (true) {
    1019           0 :             pc = GetNextNonLoopEntryPc(pc);
    1020           0 :             fasterPc = GetNextNonLoopEntryPc(GetNextNonLoopEntryPc(fasterPc));
    1021           0 :             if (fasterPc == pc)
    1022           0 :                 break;
    1023             :         }
    1024           0 :         op = JSOp(*pc);
    1025             :     }
    1026             : 
    1027           0 :     uint32_t pcOff = script->pcToOffset(pc);
    1028           0 :     BaselineScript* baselineScript = script->baselineScript();
    1029             : 
    1030             : #ifdef DEBUG
    1031             :     uint32_t expectedDepth;
    1032             :     bool reachablePC;
    1033           0 :     if (!ReconstructStackDepth(cx, script, resumeAfter ? GetNextPc(pc) : pc, &expectedDepth, &reachablePC))
    1034           0 :         return false;
    1035             : 
    1036           0 :     if (reachablePC) {
    1037           0 :         if (op != JSOP_FUNAPPLY || !iter.moreFrames() || resumeAfter) {
    1038           0 :             if (op == JSOP_FUNCALL) {
    1039             :                 // For fun.call(this, ...); the reconstructStackDepth will
    1040             :                 // include the this. When inlining that is not included.
    1041             :                 // So the exprStackSlots will be one less.
    1042           0 :                 MOZ_ASSERT(expectedDepth - exprStackSlots <= 1);
    1043           0 :             } else if (iter.moreFrames() && (IsGetPropPC(pc) || IsSetPropPC(pc))) {
    1044             :                 // Accessors coming out of ion are inlined via a complete
    1045             :                 // lie perpetrated by the compiler internally. Ion just rearranges
    1046             :                 // the stack, and pretends that it looked like a call all along.
    1047             :                 // This means that the depth is actually one *more* than expected
    1048             :                 // by the interpreter, as there is now a JSFunction, |this| and [arg],
    1049             :                 // rather than the expected |this| and [arg]
    1050             :                 // Note that none of that was pushed, but it's still reflected
    1051             :                 // in exprStackSlots.
    1052           0 :                 MOZ_ASSERT(exprStackSlots - expectedDepth == 1);
    1053             :             } else {
    1054             :                 // For fun.apply({}, arguments) the reconstructStackDepth will
    1055             :                 // have stackdepth 4, but it could be that we inlined the
    1056             :                 // funapply. In that case exprStackSlots, will have the real
    1057             :                 // arguments in the slots and not be 4.
    1058           0 :                 MOZ_ASSERT(exprStackSlots == expectedDepth);
    1059             :             }
    1060             :         }
    1061             :     }
    1062             : #endif
    1063             : 
    1064             : #ifdef JS_JITSPEW
    1065           0 :     JitSpew(JitSpew_BaselineBailouts, "      Resuming %s pc offset %d (op %s) (line %d) of %s:%" PRIuSIZE,
    1066           0 :                 resumeAfter ? "after" : "at", (int) pcOff, CodeName[op],
    1067           0 :                 PCToLineNumber(script, pc), script->filename(), script->lineno());
    1068           0 :     JitSpew(JitSpew_BaselineBailouts, "      Bailout kind: %s",
    1069           0 :             BailoutKindString(bailoutKind));
    1070             : #endif
    1071             : 
    1072           0 :     bool pushedNewTarget = IsConstructorCallPC(pc);
    1073             : 
    1074             :     // If this was the last inline frame, or we are bailing out to a catch or
    1075             :     // finally block in this frame, then unpacking is almost done.
    1076           0 :     if (!iter.moreFrames() || catchingException) {
    1077             :         // Last frame, so PC for call to next frame is set to nullptr.
    1078           0 :         *callPC = nullptr;
    1079             : 
    1080             :         // If the bailout was a resumeAfter, and the opcode is monitored,
    1081             :         // then the bailed out state should be in a position to enter
    1082             :         // into the ICTypeMonitor chain for the op.
    1083           0 :         bool enterMonitorChain = false;
    1084           0 :         if (resumeAfter && (CodeSpec[op].format & JOF_TYPESET)) {
    1085             :             // Not every monitored op has a monitored fallback stub, e.g.
    1086             :             // JSOP_NEWOBJECT, which always returns the same type for a
    1087             :             // particular script/pc location.
    1088           0 :             BaselineICEntry& icEntry = baselineScript->icEntryFromPCOffset(pcOff);
    1089           0 :             ICFallbackStub* fallbackStub = icEntry.firstStub()->getChainFallback();
    1090           0 :             if (fallbackStub->isMonitoredFallback())
    1091           0 :                 enterMonitorChain = true;
    1092             :         }
    1093             : 
    1094           0 :         uint32_t numUses = js::StackUses(script, pc);
    1095             : 
    1096           0 :         if (resumeAfter && !enterMonitorChain)
    1097           0 :             pc = GetNextPc(pc);
    1098             : 
    1099           0 :         builder.setResumePC(pc);
    1100           0 :         builder.setResumeFramePtr(prevFramePtr);
    1101             : 
    1102           0 :         if (enterMonitorChain) {
    1103           0 :             BaselineICEntry& icEntry = baselineScript->icEntryFromPCOffset(pcOff);
    1104           0 :             ICFallbackStub* fallbackStub = icEntry.firstStub()->getChainFallback();
    1105           0 :             MOZ_ASSERT(fallbackStub->isMonitoredFallback());
    1106           0 :             JitSpew(JitSpew_BaselineBailouts, "      [TYPE-MONITOR CHAIN]");
    1107           0 :             ICMonitoredFallbackStub* monFallbackStub = fallbackStub->toMonitoredFallbackStub();
    1108           0 :             ICStub* firstMonStub = monFallbackStub->fallbackMonitorStub()->firstMonitorStub();
    1109             : 
    1110             :             // To enter a monitoring chain, we load the top stack value into R0
    1111           0 :             JitSpew(JitSpew_BaselineBailouts, "      Popping top stack value into R0.");
    1112           0 :             builder.popValueInto(PCMappingSlotInfo::SlotInR0);
    1113             : 
    1114             :             // Need to adjust the frameSize for the frame to match the values popped
    1115             :             // into registers.
    1116           0 :             frameSize -= sizeof(Value);
    1117           0 :             blFrame->setFrameSize(frameSize);
    1118           0 :             JitSpew(JitSpew_BaselineBailouts, "      Adjusted framesize -= %d: %d",
    1119           0 :                             (int) sizeof(Value), (int) frameSize);
    1120             : 
    1121             :             // If resuming into a JSOP_CALL, baseline keeps the arguments on the
    1122             :             // stack and pops them only after returning from the call IC.
    1123             :             // Push undefs onto the stack in anticipation of the popping of the
    1124             :             // callee, thisv, and actual arguments passed from the caller's frame.
    1125           0 :             if (IsCallPC(pc)) {
    1126           0 :                 uint32_t numCallArgs = numUses - 2 - uint32_t(pushedNewTarget);
    1127           0 :                 if (!builder.writeValue(UndefinedValue(), "CallOp FillerCallee"))
    1128           0 :                     return false;
    1129           0 :                 if (!builder.writeValue(UndefinedValue(), "CallOp FillerThis"))
    1130           0 :                     return false;
    1131           0 :                 for (uint32_t i = 0; i < numCallArgs; i++) {
    1132           0 :                     if (!builder.writeValue(UndefinedValue(), "CallOp FillerArg"))
    1133           0 :                         return false;
    1134             :                 }
    1135           0 :                 if (pushedNewTarget) {
    1136           0 :                     if (!builder.writeValue(UndefinedValue(), "CallOp FillerNewTarget"))
    1137           0 :                         return false;
    1138             :                 }
    1139             : 
    1140           0 :                 frameSize += numUses * sizeof(Value);
    1141           0 :                 blFrame->setFrameSize(frameSize);
    1142           0 :                 JitSpew(JitSpew_BaselineBailouts, "      Adjusted framesize += %d: %d",
    1143             :                                 (int) (numUses * sizeof(Value)),
    1144           0 :                                 (int) frameSize);
    1145             :             }
    1146             : 
    1147             :             // Set the resume address to the return point from the IC, and set
    1148             :             // the monitor stub addr.
    1149           0 :             builder.setResumeAddr(baselineScript->returnAddressForIC(icEntry));
    1150           0 :             builder.setMonitorStub(firstMonStub);
    1151           0 :             JitSpew(JitSpew_BaselineBailouts, "      Set resumeAddr=%p monitorStub=%p",
    1152           0 :                     baselineScript->returnAddressForIC(icEntry), firstMonStub);
    1153             : 
    1154             :         } else {
    1155             :             // If needed, initialize BaselineBailoutInfo's valueR0 and/or valueR1 with the
    1156             :             // top stack values.
    1157             :             //
    1158             :             // Note that we use the 'maybe' variant of nativeCodeForPC because
    1159             :             // of exception propagation for debug mode. See note below.
    1160           0 :             PCMappingSlotInfo slotInfo;
    1161             :             uint8_t* nativeCodeForPC;
    1162             : 
    1163           0 :             if (excInfo && excInfo->propagatingIonExceptionForDebugMode()) {
    1164             :                 // When propagating an exception for debug mode, set the
    1165             :                 // resume pc to the throwing pc, so that Debugger hooks report
    1166             :                 // the correct pc offset of the throwing op instead of its
    1167             :                 // successor (this pc will be used as the BaselineFrame's
    1168             :                 // override pc).
    1169             :                 //
    1170             :                 // Note that we never resume at this pc, it is set for the sake
    1171             :                 // of frame iterators giving the correct answer.
    1172           0 :                 jsbytecode* throwPC = script->offsetToPC(iter.pcOffset());
    1173           0 :                 builder.setResumePC(throwPC);
    1174           0 :                 nativeCodeForPC = baselineScript->nativeCodeForPC(script, throwPC);
    1175             :             } else {
    1176           0 :                 nativeCodeForPC = baselineScript->nativeCodeForPC(script, pc, &slotInfo);
    1177             :             }
    1178           0 :             MOZ_ASSERT(nativeCodeForPC);
    1179             : 
    1180           0 :             unsigned numUnsynced = slotInfo.numUnsynced();
    1181             : 
    1182           0 :             MOZ_ASSERT(numUnsynced <= 2);
    1183             :             PCMappingSlotInfo::SlotLocation loc1, loc2;
    1184           0 :             if (numUnsynced > 0) {
    1185           0 :                 loc1 = slotInfo.topSlotLocation();
    1186           0 :                 JitSpew(JitSpew_BaselineBailouts, "      Popping top stack value into %d.",
    1187           0 :                         (int) loc1);
    1188           0 :                 builder.popValueInto(loc1);
    1189             :             }
    1190           0 :             if (numUnsynced > 1) {
    1191           0 :                 loc2 = slotInfo.nextSlotLocation();
    1192           0 :                 JitSpew(JitSpew_BaselineBailouts, "      Popping next stack value into %d.",
    1193           0 :                         (int) loc2);
    1194           0 :                 MOZ_ASSERT_IF(loc1 != PCMappingSlotInfo::SlotIgnore, loc1 != loc2);
    1195           0 :                 builder.popValueInto(loc2);
    1196             :             }
    1197             : 
    1198             :             // Need to adjust the frameSize for the frame to match the values popped
    1199             :             // into registers.
    1200           0 :             frameSize -= sizeof(Value) * numUnsynced;
    1201           0 :             blFrame->setFrameSize(frameSize);
    1202           0 :             JitSpew(JitSpew_BaselineBailouts, "      Adjusted framesize -= %d: %d",
    1203           0 :                     int(sizeof(Value) * numUnsynced), int(frameSize));
    1204             : 
    1205             :             // If envChain is nullptr, then bailout is occurring during argument check.
    1206             :             // In this case, resume into the prologue.
    1207             :             uint8_t* opReturnAddr;
    1208           0 :             if (envChain == nullptr) {
    1209             :                 // Global and eval scripts expect the env chain in R1, so only
    1210             :                 // resume into the prologue for function scripts.
    1211           0 :                 MOZ_ASSERT(fun);
    1212           0 :                 MOZ_ASSERT(numUnsynced == 0);
    1213           0 :                 opReturnAddr = baselineScript->prologueEntryAddr();
    1214           0 :                 JitSpew(JitSpew_BaselineBailouts, "      Resuming into prologue.");
    1215             : 
    1216             :             } else {
    1217           0 :                 opReturnAddr = nativeCodeForPC;
    1218             :             }
    1219           0 :             builder.setResumeAddr(opReturnAddr);
    1220           0 :             JitSpew(JitSpew_BaselineBailouts, "      Set resumeAddr=%p", opReturnAddr);
    1221             :         }
    1222             : 
    1223           0 :         if (cx->runtime()->geckoProfiler().enabled()) {
    1224             :             // Register bailout with profiler.
    1225           0 :             const char* filename = script->filename();
    1226           0 :             if (filename == nullptr)
    1227           0 :                 filename = "<unknown>";
    1228           0 :             unsigned len = strlen(filename) + 200;
    1229           0 :             char* buf = js_pod_malloc<char>(len);
    1230           0 :             if (buf == nullptr) {
    1231           0 :                 ReportOutOfMemory(cx);
    1232           0 :                 return false;
    1233             :             }
    1234           0 :             snprintf(buf, len, "%s %s %s on line %u of %s:%" PRIuSIZE,
    1235             :                      BailoutKindString(bailoutKind),
    1236             :                      resumeAfter ? "after" : "at",
    1237           0 :                      CodeName[op],
    1238           0 :                      PCToLineNumber(script, pc),
    1239             :                      filename,
    1240           0 :                      script->lineno());
    1241           0 :             cx->runtime()->geckoProfiler().markEvent(buf);
    1242           0 :             js_free(buf);
    1243             :         }
    1244             : 
    1245           0 :         return true;
    1246             :     }
    1247             : 
    1248           0 :     *callPC = pc;
    1249             : 
    1250             :     // Write out descriptor of BaselineJS frame.
    1251           0 :     size_t baselineFrameDescr = MakeFrameDescriptor((uint32_t) builder.framePushed(),
    1252             :                                                     JitFrame_BaselineJS,
    1253           0 :                                                     BaselineStubFrameLayout::Size());
    1254           0 :     if (!builder.writeWord(baselineFrameDescr, "Descriptor"))
    1255           0 :         return false;
    1256             : 
    1257             :     // Calculate and write out return address.
    1258             :     // The icEntry in question MUST have an inlinable fallback stub.
    1259           0 :     BaselineICEntry& icEntry = baselineScript->icEntryFromPCOffset(pcOff);
    1260           0 :     MOZ_ASSERT(IsInlinableFallback(icEntry.firstStub()->getChainFallback()));
    1261           0 :     if (!builder.writePtr(baselineScript->returnAddressForIC(icEntry), "ReturnAddr"))
    1262           0 :         return false;
    1263             : 
    1264             :     // Build baseline stub frame:
    1265             :     // +===============+
    1266             :     // |    StubPtr    |
    1267             :     // +---------------+
    1268             :     // |   FramePtr    |
    1269             :     // +---------------+
    1270             :     // |   Padding?    |
    1271             :     // +---------------+
    1272             :     // |     ArgA      |
    1273             :     // +---------------+
    1274             :     // |     ...       |
    1275             :     // +---------------+
    1276             :     // |     Arg0      |
    1277             :     // +---------------+
    1278             :     // |     ThisV     |
    1279             :     // +---------------+
    1280             :     // |  ActualArgC   |
    1281             :     // +---------------+
    1282             :     // |  CalleeToken  |
    1283             :     // +---------------+
    1284             :     // | Descr(BLStub) |
    1285             :     // +---------------+
    1286             :     // |  ReturnAddr   |
    1287             :     // +===============+
    1288             : 
    1289           0 :     JitSpew(JitSpew_BaselineBailouts, "      [BASELINE-STUB FRAME]");
    1290             : 
    1291           0 :     size_t startOfBaselineStubFrame = builder.framePushed();
    1292             : 
    1293             :     // Write stub pointer.
    1294           0 :     MOZ_ASSERT(IsInlinableFallback(icEntry.fallbackStub()));
    1295           0 :     if (!builder.writePtr(icEntry.fallbackStub(), "StubPtr"))
    1296           0 :         return false;
    1297             : 
    1298             :     // Write previous frame pointer (saved earlier).
    1299           0 :     if (!builder.writePtr(prevFramePtr, "PrevFramePtr"))
    1300           0 :         return false;
    1301           0 :     prevFramePtr = builder.virtualPointerAtStackOffset(0);
    1302             : 
    1303             :     // Write out actual arguments (and thisv), copied from unpacked stack of BaselineJS frame.
    1304             :     // Arguments are reversed on the BaselineJS frame's stack values.
    1305           0 :     MOZ_ASSERT(IsIonInlinablePC(pc));
    1306             :     unsigned actualArgc;
    1307           0 :     Value callee;
    1308           0 :     if (needToSaveArgs) {
    1309             :         // For FUNAPPLY or an accessor, the arguments are not on the stack anymore,
    1310             :         // but they are copied in a vector and are written here.
    1311           0 :         if (op == JSOP_FUNAPPLY)
    1312           0 :             actualArgc = blFrame->numActualArgs();
    1313             :         else
    1314           0 :             actualArgc = IsSetPropPC(pc);
    1315           0 :         callee = savedCallerArgs[0];
    1316             : 
    1317             :         // Align the stack based on the number of arguments.
    1318           0 :         size_t afterFrameSize = (actualArgc + 1) * sizeof(Value) + JitFrameLayout::Size();
    1319           0 :         if (!builder.maybeWritePadding(JitStackAlignment, afterFrameSize, "Padding"))
    1320           0 :             return false;
    1321             : 
    1322             :         // Push arguments.
    1323           0 :         MOZ_ASSERT(actualArgc + 2 <= exprStackSlots);
    1324           0 :         MOZ_ASSERT(savedCallerArgs.length() == actualArgc + 2);
    1325           0 :         for (unsigned i = 0; i < actualArgc + 1; i++) {
    1326           0 :             size_t arg = savedCallerArgs.length() - (i + 1);
    1327           0 :             if (!builder.writeValue(savedCallerArgs[arg], "ArgVal"))
    1328           0 :                 return false;
    1329             :         }
    1330             :     } else {
    1331           0 :         actualArgc = GET_ARGC(pc);
    1332           0 :         if (op == JSOP_FUNCALL) {
    1333           0 :             MOZ_ASSERT(actualArgc > 0);
    1334           0 :             actualArgc--;
    1335             :         }
    1336             : 
    1337             :         // Align the stack based on the number of arguments.
    1338           0 :         size_t afterFrameSize = (actualArgc + 1 + pushedNewTarget) * sizeof(Value) +
    1339           0 :                                 JitFrameLayout::Size();
    1340           0 :         if (!builder.maybeWritePadding(JitStackAlignment, afterFrameSize, "Padding"))
    1341           0 :             return false;
    1342             : 
    1343             :         // Copy the arguments and |this| from the BaselineFrame, in reverse order.
    1344           0 :         size_t valueSlot = blFrame->numValueSlots() - 1;
    1345           0 :         size_t calleeSlot = valueSlot - actualArgc - 1 - pushedNewTarget;
    1346             : 
    1347           0 :         for (size_t i = valueSlot; i > calleeSlot; i--) {
    1348           0 :             Value v = *blFrame->valueSlot(i);
    1349           0 :             if (!builder.writeValue(v, "ArgVal"))
    1350           0 :                 return false;
    1351             :         }
    1352             : 
    1353           0 :         callee = *blFrame->valueSlot(calleeSlot);
    1354             :     }
    1355             : 
    1356             :     // In case these arguments need to be copied on the stack again for a rectifier frame,
    1357             :     // save the framePushed values here for later use.
    1358           0 :     size_t endOfBaselineStubArgs = builder.framePushed();
    1359             : 
    1360             :     // Calculate frame size for descriptor.
    1361           0 :     size_t baselineStubFrameSize = builder.framePushed() - startOfBaselineStubFrame;
    1362           0 :     size_t baselineStubFrameDescr = MakeFrameDescriptor((uint32_t) baselineStubFrameSize,
    1363             :                                                         JitFrame_BaselineStub,
    1364           0 :                                                         JitFrameLayout::Size());
    1365             : 
    1366             :     // Push actual argc
    1367           0 :     if (!builder.writeWord(actualArgc, "ActualArgc"))
    1368           0 :         return false;
    1369             : 
    1370             :     // Push callee token (must be a JS Function)
    1371           0 :     JitSpew(JitSpew_BaselineBailouts, "      Callee = %016" PRIx64, callee.asRawBits());
    1372             : 
    1373           0 :     JSFunction* calleeFun = &callee.toObject().as<JSFunction>();
    1374           0 :     if (!builder.writePtr(CalleeToToken(calleeFun, pushedNewTarget), "CalleeToken"))
    1375           0 :         return false;
    1376           0 :     nextCallee.set(calleeFun);
    1377             : 
    1378             :     // Push BaselineStub frame descriptor
    1379           0 :     if (!builder.writeWord(baselineStubFrameDescr, "Descriptor"))
    1380           0 :         return false;
    1381             : 
    1382             :     // Push return address into ICCall_Scripted stub, immediately after the call.
    1383           0 :     void* baselineCallReturnAddr = GetStubReturnAddress(cx, pc);
    1384           0 :     MOZ_ASSERT(baselineCallReturnAddr);
    1385           0 :     if (!builder.writePtr(baselineCallReturnAddr, "ReturnAddr"))
    1386           0 :         return false;
    1387           0 :     MOZ_ASSERT(builder.framePushed() % JitStackAlignment == 0);
    1388             : 
    1389             :     // If actualArgc >= fun->nargs, then we are done.  Otherwise, we need to push on
    1390             :     // a reconstructed rectifier frame.
    1391           0 :     if (actualArgc >= calleeFun->nargs())
    1392           0 :         return true;
    1393             : 
    1394             :     // Push a reconstructed rectifier frame.
    1395             :     // +===============+
    1396             :     // |   Padding?    |
    1397             :     // +---------------+
    1398             :     // |  UndefinedU   |
    1399             :     // +---------------+
    1400             :     // |     ...       |
    1401             :     // +---------------+
    1402             :     // |  Undefined0   |
    1403             :     // +---------------+
    1404             :     // |     ArgA      |
    1405             :     // +---------------+
    1406             :     // |     ...       |
    1407             :     // +---------------+
    1408             :     // |     Arg0      |
    1409             :     // +---------------+
    1410             :     // |     ThisV     |
    1411             :     // +---------------+
    1412             :     // |  ActualArgC   |
    1413             :     // +---------------+
    1414             :     // |  CalleeToken  |
    1415             :     // +---------------+
    1416             :     // |  Descr(Rect)  |
    1417             :     // +---------------+
    1418             :     // |  ReturnAddr   |
    1419             :     // +===============+
    1420             : 
    1421           0 :     JitSpew(JitSpew_BaselineBailouts, "      [RECTIFIER FRAME]");
    1422             : 
    1423           0 :     size_t startOfRectifierFrame = builder.framePushed();
    1424             : 
    1425             :     // On x86-only, the frame pointer is saved again in the rectifier frame.
    1426             : #if defined(JS_CODEGEN_X86)
    1427             :     if (!builder.writePtr(prevFramePtr, "PrevFramePtr-X86Only"))
    1428             :         return false;
    1429             :     // Follow the same logic as in JitRuntime::generateArgumentsRectifier.
    1430             :     prevFramePtr = builder.virtualPointerAtStackOffset(0);
    1431             :     if (!builder.writePtr(prevFramePtr, "Padding-X86Only"))
    1432             :         return false;
    1433             : #endif
    1434             : 
    1435             :     // Align the stack based on the number of arguments.
    1436           0 :     size_t afterFrameSize = (calleeFun->nargs() + 1 + pushedNewTarget) * sizeof(Value) +
    1437           0 :                             RectifierFrameLayout::Size();
    1438           0 :     if (!builder.maybeWritePadding(JitStackAlignment, afterFrameSize, "Padding"))
    1439           0 :         return false;
    1440             : 
    1441             :     // Copy new.target, if necessary.
    1442           0 :     if (pushedNewTarget) {
    1443           0 :         size_t newTargetOffset = (builder.framePushed() - endOfBaselineStubArgs) +
    1444           0 :                                  (actualArgc + 1) * sizeof(Value);
    1445           0 :         Value newTargetValue = *builder.valuePointerAtStackOffset(newTargetOffset);
    1446           0 :         if (!builder.writeValue(newTargetValue, "CopiedNewTarget"))
    1447           0 :             return false;
    1448             :     }
    1449             : 
    1450             :     // Push undefined for missing arguments.
    1451           0 :     for (unsigned i = 0; i < (calleeFun->nargs() - actualArgc); i++) {
    1452           0 :         if (!builder.writeValue(UndefinedValue(), "FillerVal"))
    1453           0 :             return false;
    1454             :     }
    1455             : 
    1456             :     // Copy arguments + thisv from BaselineStub frame.
    1457           0 :     if (!builder.subtract((actualArgc + 1) * sizeof(Value), "CopiedArgs"))
    1458           0 :         return false;
    1459             :     BufferPointer<uint8_t> stubArgsEnd =
    1460           0 :         builder.pointerAtStackOffset<uint8_t>(builder.framePushed() - endOfBaselineStubArgs);
    1461           0 :     JitSpew(JitSpew_BaselineBailouts, "      MemCpy from %p", stubArgsEnd.get());
    1462           0 :     memcpy(builder.pointerAtStackOffset<uint8_t>(0).get(), stubArgsEnd.get(),
    1463           0 :            (actualArgc + 1) * sizeof(Value));
    1464             : 
    1465             :     // Calculate frame size for descriptor.
    1466           0 :     size_t rectifierFrameSize = builder.framePushed() - startOfRectifierFrame;
    1467           0 :     size_t rectifierFrameDescr = MakeFrameDescriptor((uint32_t) rectifierFrameSize,
    1468             :                                                      JitFrame_Rectifier,
    1469           0 :                                                      JitFrameLayout::Size());
    1470             : 
    1471             :     // Push actualArgc
    1472           0 :     if (!builder.writeWord(actualArgc, "ActualArgc"))
    1473           0 :         return false;
    1474             : 
    1475             :     // Push calleeToken again.
    1476           0 :     if (!builder.writePtr(CalleeToToken(calleeFun, pushedNewTarget), "CalleeToken"))
    1477           0 :         return false;
    1478             : 
    1479             :     // Push rectifier frame descriptor
    1480           0 :     if (!builder.writeWord(rectifierFrameDescr, "Descriptor"))
    1481           0 :         return false;
    1482             : 
    1483             :     // Push return address into the ArgumentsRectifier code, immediately after the ioncode
    1484             :     // call.
    1485           0 :     void* rectReturnAddr = cx->runtime()->jitRuntime()->getArgumentsRectifierReturnAddr();
    1486           0 :     MOZ_ASSERT(rectReturnAddr);
    1487           0 :     if (!builder.writePtr(rectReturnAddr, "ReturnAddr"))
    1488           0 :         return false;
    1489           0 :     MOZ_ASSERT(builder.framePushed() % JitStackAlignment == 0);
    1490             : 
    1491           0 :     return true;
    1492             : }
    1493             : 
    1494             : uint32_t
    1495           0 : jit::BailoutIonToBaseline(JSContext* cx, JitActivation* activation, JitFrameIterator& iter,
    1496             :                           bool invalidate, BaselineBailoutInfo** bailoutInfo,
    1497             :                           const ExceptionBailoutInfo* excInfo)
    1498             : {
    1499           0 :     MOZ_ASSERT(bailoutInfo != nullptr);
    1500           0 :     MOZ_ASSERT(*bailoutInfo == nullptr);
    1501             : 
    1502           0 :     TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
    1503           0 :     TraceLogStopEvent(logger, TraceLogger_IonMonkey);
    1504           0 :     TraceLogStartEvent(logger, TraceLogger_Baseline);
    1505             : 
    1506             :     // Ion bailout can fail due to overrecursion and OOM. In such cases we
    1507             :     // cannot honor any further Debugger hooks on the frame, and need to
    1508             :     // ensure that its Debugger.Frame entry is cleaned up.
    1509           0 :     auto guardRemoveRematerializedFramesFromDebugger = mozilla::MakeScopeExit([&] {
    1510           0 :         activation->removeRematerializedFramesFromDebugger(cx, iter.fp());
    1511           0 :     });
    1512             : 
    1513             :     // The caller of the top frame must be one of the following:
    1514             :     //      IonJS - Ion calling into Ion.
    1515             :     //      BaselineStub - Baseline calling into Ion.
    1516             :     //      Entry - Interpreter or other calling into Ion.
    1517             :     //      Rectifier - Arguments rectifier calling into Ion.
    1518           0 :     MOZ_ASSERT(iter.isBailoutJS());
    1519             : #if defined(DEBUG) || defined(JS_JITSPEW)
    1520           0 :     FrameType prevFrameType = iter.prevType();
    1521           0 :     MOZ_ASSERT(prevFrameType == JitFrame_IonJS ||
    1522             :                prevFrameType == JitFrame_BaselineStub ||
    1523             :                prevFrameType == JitFrame_Entry ||
    1524             :                prevFrameType == JitFrame_Rectifier ||
    1525             :                prevFrameType == JitFrame_IonICCall);
    1526             : #endif
    1527             : 
    1528             :     // All incoming frames are going to look like this:
    1529             :     //
    1530             :     //      +---------------+
    1531             :     //      |     ...       |
    1532             :     //      +---------------+
    1533             :     //      |     Args      |
    1534             :     //      |     ...       |
    1535             :     //      +---------------+
    1536             :     //      |    ThisV      |
    1537             :     //      +---------------+
    1538             :     //      |  ActualArgC   |
    1539             :     //      +---------------+
    1540             :     //      |  CalleeToken  |
    1541             :     //      +---------------+
    1542             :     //      |  Descriptor   |
    1543             :     //      +---------------+
    1544             :     //      |  ReturnAddr   |
    1545             :     //      +---------------+
    1546             :     //      |    |||||      | <---- Overwrite starting here.
    1547             :     //      |    |||||      |
    1548             :     //      |    |||||      |
    1549             :     //      +---------------+
    1550             : 
    1551           0 :     JitSpew(JitSpew_BaselineBailouts, "Bailing to baseline %s:%" PRIuSIZE " (IonScript=%p) (FrameType=%d)",
    1552           0 :             iter.script()->filename(), iter.script()->lineno(), (void*) iter.ionScript(),
    1553           0 :             (int) prevFrameType);
    1554             : 
    1555             :     bool catchingException;
    1556             :     bool propagatingExceptionForDebugMode;
    1557           0 :     if (excInfo) {
    1558           0 :         catchingException = excInfo->catchingException();
    1559           0 :         propagatingExceptionForDebugMode = excInfo->propagatingIonExceptionForDebugMode();
    1560             : 
    1561           0 :         if (catchingException)
    1562           0 :             JitSpew(JitSpew_BaselineBailouts, "Resuming in catch or finally block");
    1563             : 
    1564           0 :         if (propagatingExceptionForDebugMode)
    1565           0 :             JitSpew(JitSpew_BaselineBailouts, "Resuming in-place for debug mode");
    1566             :     } else {
    1567           0 :         catchingException = false;
    1568           0 :         propagatingExceptionForDebugMode = false;
    1569             :     }
    1570             : 
    1571           0 :     JitSpew(JitSpew_BaselineBailouts, "  Reading from snapshot offset %u size %" PRIuSIZE,
    1572           0 :             iter.snapshotOffset(), iter.ionScript()->snapshotsListSize());
    1573             : 
    1574           0 :     if (!excInfo)
    1575           0 :         iter.ionScript()->incNumBailouts();
    1576           0 :     iter.script()->updateBaselineOrIonRaw(cx->runtime());
    1577             : 
    1578             :     // Allocate buffer to hold stack replacement data.
    1579           0 :     BaselineStackBuilder builder(iter, 1024);
    1580           0 :     if (!builder.init()) {
    1581           0 :         ReportOutOfMemory(cx);
    1582           0 :         return BAILOUT_RETURN_FATAL_ERROR;
    1583             :     }
    1584           0 :     JitSpew(JitSpew_BaselineBailouts, "  Incoming frame ptr = %p", builder.startFrame());
    1585             : 
    1586           0 :     SnapshotIteratorForBailout snapIter(activation, iter);
    1587           0 :     if (!snapIter.init(cx))
    1588           0 :         return BAILOUT_RETURN_FATAL_ERROR;
    1589             : 
    1590             : #ifdef TRACK_SNAPSHOTS
    1591           0 :     snapIter.spewBailingFrom();
    1592             : #endif
    1593             : 
    1594           0 :     RootedFunction callee(cx, iter.maybeCallee());
    1595           0 :     RootedScript scr(cx, iter.script());
    1596           0 :     if (callee) {
    1597           0 :         JitSpew(JitSpew_BaselineBailouts, "  Callee function (%s:%" PRIuSIZE ")",
    1598           0 :                 scr->filename(), scr->lineno());
    1599             :     } else {
    1600           0 :         JitSpew(JitSpew_BaselineBailouts, "  No callee!");
    1601             :     }
    1602             : 
    1603           0 :     if (iter.isConstructing())
    1604           0 :         JitSpew(JitSpew_BaselineBailouts, "  Constructing!");
    1605             :     else
    1606           0 :         JitSpew(JitSpew_BaselineBailouts, "  Not constructing!");
    1607             : 
    1608           0 :     JitSpew(JitSpew_BaselineBailouts, "  Restoring frames:");
    1609           0 :     size_t frameNo = 0;
    1610             : 
    1611             :     // Reconstruct baseline frames using the builder.
    1612           0 :     RootedScript caller(cx);
    1613           0 :     jsbytecode* callerPC = nullptr;
    1614           0 :     RootedFunction fun(cx, callee);
    1615           0 :     Rooted<GCVector<Value>> startFrameFormals(cx, GCVector<Value>(cx));
    1616             : 
    1617           0 :     gc::AutoSuppressGC suppress(cx);
    1618             : 
    1619             :     while (true) {
    1620             :         // Skip recover instructions as they are already recovered by |initInstructionResults|.
    1621           0 :         snapIter.settleOnFrame();
    1622             : 
    1623           0 :         if (frameNo > 0) {
    1624             :             // TraceLogger doesn't create entries for inlined frames. But we
    1625             :             // see them in Baseline. Here we create the start events of those
    1626             :             // entries. So they correspond to what we will see in Baseline.
    1627           0 :             TraceLoggerEvent scriptEvent(TraceLogger_Scripts, scr);
    1628           0 :             TraceLogStartEvent(logger, scriptEvent);
    1629           0 :             TraceLogStartEvent(logger, TraceLogger_Baseline);
    1630             :         }
    1631             : 
    1632           0 :         JitSpew(JitSpew_BaselineBailouts, "    FrameNo %" PRIuSIZE, frameNo);
    1633             : 
    1634             :         // If we are bailing out to a catch or finally block in this frame,
    1635             :         // pass excInfo to InitFromBailout and don't unpack any other frames.
    1636           0 :         bool handleException = (catchingException && excInfo->frameNo() == frameNo);
    1637             : 
    1638             :         // We also need to pass excInfo if we're bailing out in place for
    1639             :         // debug mode.
    1640           0 :         bool passExcInfo = handleException || propagatingExceptionForDebugMode;
    1641             : 
    1642           0 :         jsbytecode* callPC = nullptr;
    1643           0 :         RootedFunction nextCallee(cx, nullptr);
    1644           0 :         if (!InitFromBailout(cx, caller, callerPC, fun, scr, iter.ionScript(),
    1645             :                              snapIter, invalidate, builder, &startFrameFormals,
    1646             :                              &nextCallee, &callPC, passExcInfo ? excInfo : nullptr))
    1647             :         {
    1648           0 :             return BAILOUT_RETURN_FATAL_ERROR;
    1649             :         }
    1650             : 
    1651           0 :         if (!snapIter.moreFrames()) {
    1652           0 :             MOZ_ASSERT(!callPC);
    1653           0 :             break;
    1654             :         }
    1655             : 
    1656           0 :         if (handleException)
    1657           0 :             break;
    1658             : 
    1659           0 :         MOZ_ASSERT(nextCallee);
    1660           0 :         MOZ_ASSERT(callPC);
    1661           0 :         caller = scr;
    1662           0 :         callerPC = callPC;
    1663           0 :         fun = nextCallee;
    1664           0 :         scr = fun->existingScript();
    1665             : 
    1666           0 :         frameNo++;
    1667             : 
    1668           0 :         snapIter.nextInstruction();
    1669           0 :     }
    1670           0 :     JitSpew(JitSpew_BaselineBailouts, "  Done restoring frames");
    1671             : 
    1672           0 :     BailoutKind bailoutKind = snapIter.bailoutKind();
    1673             : 
    1674           0 :     if (!startFrameFormals.empty()) {
    1675             :         // Set the first frame's formals, see the comment in InitFromBailout.
    1676           0 :         Value* argv = builder.startFrame()->argv() + 1; // +1 to skip |this|.
    1677           0 :         mozilla::PodCopy(argv, startFrameFormals.begin(), startFrameFormals.length());
    1678             :     }
    1679             : 
    1680             :     // Do stack check.
    1681           0 :     bool overRecursed = false;
    1682           0 :     BaselineBailoutInfo *info = builder.info();
    1683           0 :     uint8_t* newsp = info->incomingStack - (info->copyStackTop - info->copyStackBottom);
    1684             : #ifdef JS_SIMULATOR
    1685             :     if (Simulator::Current()->overRecursed(uintptr_t(newsp)))
    1686             :         overRecursed = true;
    1687             : #else
    1688           0 :     if (!CheckRecursionLimitWithStackPointerDontReport(cx, newsp))
    1689           0 :         overRecursed = true;
    1690             : #endif
    1691           0 :     if (overRecursed) {
    1692           0 :         JitSpew(JitSpew_BaselineBailouts, "  Overrecursion check failed!");
    1693           0 :         return BAILOUT_RETURN_OVERRECURSED;
    1694             :     }
    1695             : 
    1696             :     // Take the reconstructed baseline stack so it doesn't get freed when builder destructs.
    1697           0 :     info = builder.takeBuffer();
    1698           0 :     info->numFrames = frameNo + 1;
    1699           0 :     info->bailoutKind = bailoutKind;
    1700           0 :     *bailoutInfo = info;
    1701           0 :     guardRemoveRematerializedFramesFromDebugger.release();
    1702           0 :     return BAILOUT_RETURN_OK;
    1703             : }
    1704             : 
    1705             : static void
    1706           0 : InvalidateAfterBailout(JSContext* cx, HandleScript outerScript, const char* reason)
    1707             : {
    1708             :     // In some cases, the computation of recover instruction can invalidate the
    1709             :     // Ion script before we reach the end of the bailout. Thus, if the outer
    1710             :     // script no longer have any Ion script attached, then we just skip the
    1711             :     // invalidation.
    1712             :     //
    1713             :     // For example, such case can happen if the template object for an unboxed
    1714             :     // objects no longer match the content of its properties (see Bug 1174547)
    1715           0 :     if (!outerScript->hasIonScript()) {
    1716           0 :         JitSpew(JitSpew_BaselineBailouts, "Ion script is already invalidated");
    1717           0 :         return;
    1718             :     }
    1719             : 
    1720           0 :     MOZ_ASSERT(!outerScript->ionScript()->invalidated());
    1721             : 
    1722           0 :     JitSpew(JitSpew_BaselineBailouts, "Invalidating due to %s", reason);
    1723           0 :     Invalidate(cx, outerScript);
    1724             : }
    1725             : 
    1726             : static void
    1727           0 : HandleBoundsCheckFailure(JSContext* cx, HandleScript outerScript, HandleScript innerScript)
    1728             : {
    1729           0 :     JitSpew(JitSpew_IonBailouts, "Bounds check failure %s:%" PRIuSIZE ", inlined into %s:%" PRIuSIZE,
    1730           0 :             innerScript->filename(), innerScript->lineno(),
    1731           0 :             outerScript->filename(), outerScript->lineno());
    1732             : 
    1733           0 :     if (!innerScript->failedBoundsCheck())
    1734           0 :         innerScript->setFailedBoundsCheck();
    1735             : 
    1736           0 :     InvalidateAfterBailout(cx, outerScript, "bounds check failure");
    1737           0 :     if (innerScript->hasIonScript())
    1738           0 :         Invalidate(cx, innerScript);
    1739           0 : }
    1740             : 
    1741             : static void
    1742           0 : HandleShapeGuardFailure(JSContext* cx, HandleScript outerScript, HandleScript innerScript)
    1743             : {
    1744           0 :     JitSpew(JitSpew_IonBailouts, "Shape guard failure %s:%" PRIuSIZE ", inlined into %s:%" PRIuSIZE,
    1745           0 :             innerScript->filename(), innerScript->lineno(),
    1746           0 :             outerScript->filename(), outerScript->lineno());
    1747             : 
    1748             :     // TODO: Currently this mimic's Ion's handling of this case.  Investigate setting
    1749             :     // the flag on innerScript as opposed to outerScript, and maybe invalidating both
    1750             :     // inner and outer scripts, instead of just the outer one.
    1751           0 :     outerScript->setFailedShapeGuard();
    1752             : 
    1753           0 :     InvalidateAfterBailout(cx, outerScript, "shape guard failure");
    1754           0 : }
    1755             : 
    1756             : static void
    1757           0 : HandleBaselineInfoBailout(JSContext* cx, HandleScript outerScript, HandleScript innerScript)
    1758             : {
    1759           0 :     JitSpew(JitSpew_IonBailouts, "Baseline info failure %s:%" PRIuSIZE ", inlined into %s:%" PRIuSIZE,
    1760           0 :             innerScript->filename(), innerScript->lineno(),
    1761           0 :             outerScript->filename(), outerScript->lineno());
    1762             : 
    1763           0 :     InvalidateAfterBailout(cx, outerScript, "invalid baseline info");
    1764           0 : }
    1765             : 
    1766             : static void
    1767           0 : HandleLexicalCheckFailure(JSContext* cx, HandleScript outerScript, HandleScript innerScript)
    1768             : {
    1769           0 :     JitSpew(JitSpew_IonBailouts, "Lexical check failure %s:%" PRIuSIZE ", inlined into %s:%" PRIuSIZE,
    1770           0 :             innerScript->filename(), innerScript->lineno(),
    1771           0 :             outerScript->filename(), outerScript->lineno());
    1772             : 
    1773           0 :     if (!innerScript->failedLexicalCheck())
    1774           0 :         innerScript->setFailedLexicalCheck();
    1775             : 
    1776           0 :     InvalidateAfterBailout(cx, outerScript, "lexical check failure");
    1777           0 :     if (innerScript->hasIonScript())
    1778           0 :         Invalidate(cx, innerScript);
    1779           0 : }
    1780             : 
    1781             : static bool
    1782           0 : CopyFromRematerializedFrame(JSContext* cx, JitActivation* act, uint8_t* fp, size_t inlineDepth,
    1783             :                             BaselineFrame* frame)
    1784             : {
    1785           0 :     RematerializedFrame* rematFrame = act->lookupRematerializedFrame(fp, inlineDepth);
    1786             : 
    1787             :     // We might not have rematerialized a frame if the user never requested a
    1788             :     // Debugger.Frame for it.
    1789           0 :     if (!rematFrame)
    1790           0 :         return true;
    1791             : 
    1792           0 :     MOZ_ASSERT(rematFrame->script() == frame->script());
    1793           0 :     MOZ_ASSERT(rematFrame->numActualArgs() == frame->numActualArgs());
    1794             : 
    1795           0 :     frame->setEnvironmentChain(rematFrame->environmentChain());
    1796             : 
    1797           0 :     if (frame->isFunctionFrame())
    1798           0 :         frame->thisArgument() = rematFrame->thisArgument();
    1799             : 
    1800           0 :     for (unsigned i = 0; i < frame->numActualArgs(); i++)
    1801           0 :         frame->argv()[i] = rematFrame->argv()[i];
    1802             : 
    1803           0 :     for (size_t i = 0; i < frame->script()->nfixed(); i++)
    1804           0 :         *frame->valueSlot(i) = rematFrame->locals()[i];
    1805             : 
    1806           0 :     frame->setReturnValue(rematFrame->returnValue());
    1807             : 
    1808           0 :     if (rematFrame->hasCachedSavedFrame())
    1809           0 :         frame->setHasCachedSavedFrame();
    1810             : 
    1811             :     JitSpew(JitSpew_BaselineBailouts,
    1812             :             "  Copied from rematerialized frame at (%p,%" PRIuSIZE ")",
    1813           0 :             fp, inlineDepth);
    1814             : 
    1815             :     // Propagate the debuggee frame flag. For the case where the Debugger did
    1816             :     // not rematerialize an Ion frame, the baseline frame has its debuggee
    1817             :     // flag set iff its script is considered a debuggee. See the debuggee case
    1818             :     // in InitFromBailout.
    1819           0 :     if (rematFrame->isDebuggee()) {
    1820           0 :         frame->setIsDebuggee();
    1821           0 :         return Debugger::handleIonBailout(cx, rematFrame, frame);
    1822             :     }
    1823             : 
    1824           0 :     return true;
    1825             : }
    1826             : 
    1827             : uint32_t
    1828           0 : jit::FinishBailoutToBaseline(BaselineBailoutInfo* bailoutInfo)
    1829             : {
    1830             :     // The caller pushes R0 and R1 on the stack without rooting them.
    1831             :     // Since GC here is very unlikely just suppress it.
    1832           0 :     JSContext* cx = TlsContext.get();
    1833           0 :     js::gc::AutoSuppressGC suppressGC(cx);
    1834             : 
    1835           0 :     JitSpew(JitSpew_BaselineBailouts, "  Done restoring frames");
    1836             : 
    1837             :     // The current native code pc may not have a corresponding ICEntry, so we
    1838             :     // store the bytecode pc in the frame for frame iterators. This pc is
    1839             :     // cleared at the end of this function. If we return false, we don't clear
    1840             :     // it: the exception handler also needs it and will clear it for us.
    1841           0 :     BaselineFrame* topFrame = GetTopBaselineFrame(cx);
    1842           0 :     topFrame->setOverridePc(bailoutInfo->resumePC);
    1843             : 
    1844           0 :     jsbytecode* faultPC = bailoutInfo->faultPC;
    1845           0 :     jsbytecode* tryPC = bailoutInfo->tryPC;
    1846           0 :     uint32_t numFrames = bailoutInfo->numFrames;
    1847           0 :     MOZ_ASSERT(numFrames > 0);
    1848           0 :     BailoutKind bailoutKind = bailoutInfo->bailoutKind;
    1849           0 :     bool checkGlobalDeclarationConflicts = bailoutInfo->checkGlobalDeclarationConflicts;
    1850             : 
    1851             :     // Free the bailout buffer.
    1852           0 :     js_free(bailoutInfo);
    1853           0 :     bailoutInfo = nullptr;
    1854             : 
    1855           0 :     if (topFrame->environmentChain()) {
    1856             :         // Ensure the frame has a call object if it needs one. If the env chain
    1857             :         // is nullptr, we will enter baseline code at the prologue so no need to do
    1858             :         // anything in that case.
    1859           0 :         if (!EnsureHasEnvironmentObjects(cx, topFrame))
    1860           0 :             return false;
    1861             : 
    1862             :         // If we bailed out before Ion could do the global declaration
    1863             :         // conflicts check, because we resume in the body instead of the
    1864             :         // prologue for global frames.
    1865           0 :         if (checkGlobalDeclarationConflicts) {
    1866           0 :             Rooted<LexicalEnvironmentObject*> lexicalEnv(cx, &cx->global()->lexicalEnvironment());
    1867           0 :             RootedScript script(cx, topFrame->script());
    1868           0 :             if (!CheckGlobalDeclarationConflicts(cx, script, lexicalEnv, cx->global()))
    1869           0 :                 return false;
    1870             :         }
    1871             :     }
    1872             : 
    1873             :     // Create arguments objects for bailed out frames, to maintain the invariant
    1874             :     // that script->needsArgsObj() implies frame->hasArgsObj().
    1875           0 :     RootedScript innerScript(cx, nullptr);
    1876           0 :     RootedScript outerScript(cx, nullptr);
    1877             : 
    1878           0 :     MOZ_ASSERT(cx->currentlyRunningInJit());
    1879           0 :     JitFrameIterator iter(cx);
    1880           0 :     uint8_t* outerFp = nullptr;
    1881             : 
    1882             :     // Iter currently points at the exit frame.  Get the previous frame
    1883             :     // (which must be a baseline frame), and set it as the last profiling
    1884             :     // frame.
    1885           0 :     if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime()))
    1886           0 :         cx->jitActivation->setLastProfilingFrame(iter.prevFp());
    1887             : 
    1888           0 :     uint32_t frameno = 0;
    1889           0 :     while (frameno < numFrames) {
    1890           0 :         MOZ_ASSERT(!iter.isIonJS());
    1891             : 
    1892           0 :         if (iter.isBaselineJS()) {
    1893           0 :             BaselineFrame* frame = iter.baselineFrame();
    1894           0 :             MOZ_ASSERT(frame->script()->hasBaselineScript());
    1895             : 
    1896             :             // If the frame doesn't even have a env chain set yet, then it's resuming
    1897             :             // into the the prologue before the env chain is initialized.  Any
    1898             :             // necessary args object will also be initialized there.
    1899           0 :             if (frame->environmentChain() && frame->script()->needsArgsObj()) {
    1900             :                 ArgumentsObject* argsObj;
    1901           0 :                 if (frame->hasArgsObj()) {
    1902           0 :                     argsObj = &frame->argsObj();
    1903             :                 } else {
    1904           0 :                     argsObj = ArgumentsObject::createExpected(cx, frame);
    1905           0 :                     if (!argsObj)
    1906           0 :                         return false;
    1907             :                 }
    1908             : 
    1909             :                 // The arguments is a local binding and needsArgsObj does not
    1910             :                 // check if it is clobbered. Ensure that the local binding
    1911             :                 // restored during bailout before storing the arguments object
    1912             :                 // to the slot.
    1913           0 :                 RootedScript script(cx, frame->script());
    1914           0 :                 SetFrameArgumentsObject(cx, frame, script, argsObj);
    1915             :             }
    1916             : 
    1917           0 :             if (frameno == 0)
    1918           0 :                 innerScript = frame->script();
    1919             : 
    1920           0 :             if (frameno == numFrames - 1) {
    1921           0 :                 outerScript = frame->script();
    1922           0 :                 outerFp = iter.fp();
    1923             :             }
    1924             : 
    1925           0 :             frameno++;
    1926             :         }
    1927             : 
    1928           0 :         ++iter;
    1929             :     }
    1930             : 
    1931           0 :     MOZ_ASSERT(innerScript);
    1932           0 :     MOZ_ASSERT(outerScript);
    1933           0 :     MOZ_ASSERT(outerFp);
    1934             : 
    1935             :     // If we rematerialized Ion frames due to debug mode toggling, copy their
    1936             :     // values into the baseline frame. We need to do this even when debug mode
    1937             :     // is off, as we should respect the mutations made while debug mode was
    1938             :     // on.
    1939           0 :     JitActivation* act = cx->activation()->asJit();
    1940           0 :     if (act->hasRematerializedFrame(outerFp)) {
    1941           0 :         JitFrameIterator iter(cx);
    1942           0 :         size_t inlineDepth = numFrames;
    1943           0 :         bool ok = true;
    1944           0 :         while (inlineDepth > 0) {
    1945           0 :             if (iter.isBaselineJS()) {
    1946             :                 // We must attempt to copy all rematerialized frames over,
    1947             :                 // even if earlier ones failed, to invoke the proper frame
    1948             :                 // cleanup in the Debugger.
    1949           0 :                 ok = CopyFromRematerializedFrame(cx, act, outerFp, --inlineDepth,
    1950           0 :                                                  iter.baselineFrame());
    1951             :             }
    1952           0 :             ++iter;
    1953             :         }
    1954             : 
    1955             :         // After copying from all the rematerialized frames, remove them from
    1956             :         // the table to keep the table up to date.
    1957           0 :         act->removeRematerializedFrame(outerFp);
    1958             : 
    1959           0 :         if (!ok)
    1960           0 :             return false;
    1961             :     }
    1962             : 
    1963             :     // If we are catching an exception, we need to unwind scopes.
    1964             :     // See |SettleOnTryNote|
    1965           0 :     if (cx->isExceptionPending() && faultPC) {
    1966           0 :         EnvironmentIter ei(cx, topFrame, faultPC);
    1967           0 :         UnwindEnvironment(cx, ei, tryPC);
    1968             :     }
    1969             : 
    1970           0 :     JitSpew(JitSpew_BaselineBailouts,
    1971             :             "  Restored outerScript=(%s:%" PRIuSIZE ",%u) innerScript=(%s:%" PRIuSIZE ",%u) (bailoutKind=%u)",
    1972           0 :             outerScript->filename(), outerScript->lineno(), outerScript->getWarmUpCount(),
    1973           0 :             innerScript->filename(), innerScript->lineno(), innerScript->getWarmUpCount(),
    1974           0 :             (unsigned) bailoutKind);
    1975             : 
    1976           0 :     switch (bailoutKind) {
    1977             :       // Normal bailouts.
    1978             :       case Bailout_Inevitable:
    1979             :       case Bailout_DuringVMCall:
    1980             :       case Bailout_NonJSFunctionCallee:
    1981             :       case Bailout_DynamicNameNotFound:
    1982             :       case Bailout_StringArgumentsEval:
    1983             :       case Bailout_Overflow:
    1984             :       case Bailout_Round:
    1985             :       case Bailout_NonPrimitiveInput:
    1986             :       case Bailout_PrecisionLoss:
    1987             :       case Bailout_TypeBarrierO:
    1988             :       case Bailout_TypeBarrierV:
    1989             :       case Bailout_MonitorTypes:
    1990             :       case Bailout_Hole:
    1991             :       case Bailout_NegativeIndex:
    1992             :       case Bailout_NonInt32Input:
    1993             :       case Bailout_NonNumericInput:
    1994             :       case Bailout_NonBooleanInput:
    1995             :       case Bailout_NonObjectInput:
    1996             :       case Bailout_NonStringInput:
    1997             :       case Bailout_NonSymbolInput:
    1998             :       case Bailout_UnexpectedSimdInput:
    1999             :       case Bailout_NonSharedTypedArrayInput:
    2000             :       case Bailout_Debugger:
    2001             :       case Bailout_UninitializedThis:
    2002             :       case Bailout_BadDerivedConstructorReturn:
    2003             :         // Do nothing.
    2004           0 :         break;
    2005             : 
    2006             :       case Bailout_FirstExecution:
    2007             :         // Do not return directly, as this was not frequent in the first place,
    2008             :         // thus rely on the check for frequent bailouts to recompile the current
    2009             :         // script.
    2010           0 :         break;
    2011             : 
    2012             :       // Invalid assumption based on baseline code.
    2013             :       case Bailout_OverflowInvalidate:
    2014           0 :         outerScript->setHadOverflowBailout();
    2015             :         MOZ_FALLTHROUGH;
    2016             :       case Bailout_NonStringInputInvalidate:
    2017             :       case Bailout_DoubleOutput:
    2018             :       case Bailout_ObjectIdentityOrTypeGuard:
    2019           0 :         HandleBaselineInfoBailout(cx, outerScript, innerScript);
    2020           0 :         break;
    2021             : 
    2022             :       case Bailout_ArgumentCheck:
    2023             :         // Do nothing, bailout will resume before the argument monitor ICs.
    2024           0 :         break;
    2025             :       case Bailout_BoundsCheck:
    2026             :       case Bailout_Detached:
    2027           0 :         HandleBoundsCheckFailure(cx, outerScript, innerScript);
    2028           0 :         break;
    2029             :       case Bailout_ShapeGuard:
    2030           0 :         HandleShapeGuardFailure(cx, outerScript, innerScript);
    2031           0 :         break;
    2032             :       case Bailout_UninitializedLexical:
    2033           0 :         HandleLexicalCheckFailure(cx, outerScript, innerScript);
    2034           0 :         break;
    2035             :       case Bailout_IonExceptionDebugMode:
    2036             :         // Return false to resume in HandleException with reconstructed
    2037             :         // baseline frame.
    2038           0 :         return false;
    2039             :       default:
    2040           0 :         MOZ_CRASH("Unknown bailout kind!");
    2041             :     }
    2042             : 
    2043           0 :     CheckFrequentBailouts(cx, outerScript, bailoutKind);
    2044             : 
    2045             :     // We're returning to JIT code, so we should clear the override pc.
    2046           0 :     topFrame->clearOverridePc();
    2047           0 :     return true;
    2048             : }

Generated by: LCOV version 1.13