js/src/jit/BaselineBailouts.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/jit/BaselineBailouts.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1722 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99:
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "jsprf.h"
    1.11 +#include "jit/arm/Simulator-arm.h"
    1.12 +#include "jit/BaselineIC.h"
    1.13 +#include "jit/BaselineJIT.h"
    1.14 +#include "jit/CompileInfo.h"
    1.15 +#include "jit/IonSpewer.h"
    1.16 +#include "jit/Recover.h"
    1.17 +#include "jit/RematerializedFrame.h"
    1.18 +
    1.19 +#include "vm/ArgumentsObject.h"
    1.20 +#include "vm/Debugger.h"
    1.21 +#include "vm/TraceLogging.h"
    1.22 +
    1.23 +#include "jsscriptinlines.h"
    1.24 +
    1.25 +#include "jit/IonFrames-inl.h"
    1.26 +
    1.27 +using namespace js;
    1.28 +using namespace js::jit;
    1.29 +
    1.30 +// BaselineStackBuilder may reallocate its buffer if the current one is too
    1.31 +// small. To avoid dangling pointers, BufferPointer represents a pointer into
    1.32 +// this buffer as a pointer to the header and a fixed offset.
    1.33 +template <typename T>
    1.34 +class BufferPointer
    1.35 +{
    1.36 +    BaselineBailoutInfo **header_;
    1.37 +    size_t offset_;
    1.38 +    bool heap_;
    1.39 +
    1.40 +  public:
    1.41 +    BufferPointer(BaselineBailoutInfo **header, size_t offset, bool heap)
    1.42 +      : header_(header), offset_(offset), heap_(heap)
    1.43 +    { }
    1.44 +
    1.45 +    T *get() const {
    1.46 +        BaselineBailoutInfo *header = *header_;
    1.47 +        if (!heap_)
    1.48 +            return (T*)(header->incomingStack + offset_);
    1.49 +
    1.50 +        uint8_t *p = header->copyStackTop - offset_;
    1.51 +        JS_ASSERT(p >= header->copyStackBottom && p < header->copyStackTop);
    1.52 +        return (T*)p;
    1.53 +    }
    1.54 +
    1.55 +    T &operator*() const { return *get(); }
    1.56 +    T *operator->() const { return get(); }
    1.57 +};
    1.58 +
    1.59 +/**
    1.60 + * BaselineStackBuilder helps abstract the process of rebuilding the C stack on the heap.
    1.61 + * It takes a bailout iterator and keeps track of the point on the C stack from which
    1.62 + * the reconstructed frames will be written.
    1.63 + *
    1.64 + * It exposes methods to write data into the heap memory storing the reconstructed
    1.65 + * stack.  It also exposes method to easily calculate addresses.  This includes both the
    1.66 + * virtual address that a particular value will be at when it's eventually copied onto
    1.67 + * the stack, as well as the current actual address of that value (whether on the heap
    1.68 + * allocated portion being constructed or the existing stack).
    1.69 + *
    1.70 + * The abstraction handles transparent re-allocation of the heap memory when it
    1.71 + * needs to be enlarged to accomodate new data.  Similarly to the C stack, the
    1.72 + * data that's written to the reconstructed stack grows from high to low in memory.
    1.73 + *
    1.74 + * The lowest region of the allocated memory contains a BaselineBailoutInfo structure that
    1.75 + * points to the start and end of the written data.
    1.76 + */
    1.77 +struct BaselineStackBuilder
    1.78 +{
    1.79 +    IonBailoutIterator &iter_;
    1.80 +    IonJSFrameLayout *frame_;
    1.81 +
    1.82 +    static size_t HeaderSize() {
    1.83 +        return AlignBytes(sizeof(BaselineBailoutInfo), sizeof(void *));
    1.84 +    };
    1.85 +    size_t bufferTotal_;
    1.86 +    size_t bufferAvail_;
    1.87 +    size_t bufferUsed_;
    1.88 +    uint8_t *buffer_;
    1.89 +    BaselineBailoutInfo *header_;
    1.90 +
    1.91 +    size_t framePushed_;
    1.92 +
    1.93 +    BaselineStackBuilder(IonBailoutIterator &iter, size_t initialSize)
    1.94 +      : iter_(iter),
    1.95 +        frame_(static_cast<IonJSFrameLayout*>(iter.current())),
    1.96 +        bufferTotal_(initialSize),
    1.97 +        bufferAvail_(0),
    1.98 +        bufferUsed_(0),
    1.99 +        buffer_(nullptr),
   1.100 +        header_(nullptr),
   1.101 +        framePushed_(0)
   1.102 +    {
   1.103 +        JS_ASSERT(bufferTotal_ >= HeaderSize());
   1.104 +    }
   1.105 +
   1.106 +    ~BaselineStackBuilder() {
   1.107 +        js_free(buffer_);
   1.108 +    }
   1.109 +
   1.110 +    bool init() {
   1.111 +        JS_ASSERT(!buffer_);
   1.112 +        JS_ASSERT(bufferUsed_ == 0);
   1.113 +        buffer_ = reinterpret_cast<uint8_t *>(js_calloc(bufferTotal_));
   1.114 +        if (!buffer_)
   1.115 +            return false;
   1.116 +        bufferAvail_ = bufferTotal_ - HeaderSize();
   1.117 +        bufferUsed_ = 0;
   1.118 +
   1.119 +        header_ = reinterpret_cast<BaselineBailoutInfo *>(buffer_);
   1.120 +        header_->incomingStack = reinterpret_cast<uint8_t *>(frame_);
   1.121 +        header_->copyStackTop = buffer_ + bufferTotal_;
   1.122 +        header_->copyStackBottom = header_->copyStackTop;
   1.123 +        header_->setR0 = 0;
   1.124 +        header_->valueR0 = UndefinedValue();
   1.125 +        header_->setR1 = 0;
   1.126 +        header_->valueR1 = UndefinedValue();
   1.127 +        header_->resumeFramePtr = nullptr;
   1.128 +        header_->resumeAddr = nullptr;
   1.129 +        header_->monitorStub = nullptr;
   1.130 +        header_->numFrames = 0;
   1.131 +        return true;
   1.132 +    }
   1.133 +
   1.134 +    bool enlarge() {
   1.135 +        JS_ASSERT(buffer_ != nullptr);
   1.136 +        if (bufferTotal_ & mozilla::tl::MulOverflowMask<2>::value)
   1.137 +            return false;
   1.138 +        size_t newSize = bufferTotal_ * 2;
   1.139 +        uint8_t *newBuffer = reinterpret_cast<uint8_t *>(js_calloc(newSize));
   1.140 +        if (!newBuffer)
   1.141 +            return false;
   1.142 +        memcpy((newBuffer + newSize) - bufferUsed_, header_->copyStackBottom, bufferUsed_);
   1.143 +        memcpy(newBuffer, header_, sizeof(BaselineBailoutInfo));
   1.144 +        js_free(buffer_);
   1.145 +        buffer_ = newBuffer;
   1.146 +        bufferTotal_ = newSize;
   1.147 +        bufferAvail_ = newSize - (HeaderSize() + bufferUsed_);
   1.148 +
   1.149 +        header_ = reinterpret_cast<BaselineBailoutInfo *>(buffer_);
   1.150 +        header_->copyStackTop = buffer_ + bufferTotal_;
   1.151 +        header_->copyStackBottom = header_->copyStackTop - bufferUsed_;
   1.152 +        return true;
   1.153 +    }
   1.154 +
   1.155 +    BaselineBailoutInfo *info() {
   1.156 +        JS_ASSERT(header_ == reinterpret_cast<BaselineBailoutInfo *>(buffer_));
   1.157 +        return header_;
   1.158 +    }
   1.159 +
   1.160 +    BaselineBailoutInfo *takeBuffer() {
   1.161 +        JS_ASSERT(header_ == reinterpret_cast<BaselineBailoutInfo *>(buffer_));
   1.162 +        buffer_ = nullptr;
   1.163 +        return header_;
   1.164 +    }
   1.165 +
   1.166 +    void resetFramePushed() {
   1.167 +        framePushed_ = 0;
   1.168 +    }
   1.169 +
   1.170 +    size_t framePushed() const {
   1.171 +        return framePushed_;
   1.172 +    }
   1.173 +
   1.174 +    bool subtract(size_t size, const char *info = nullptr) {
   1.175 +        // enlarge the buffer if need be.
   1.176 +        while (size > bufferAvail_) {
   1.177 +            if (!enlarge())
   1.178 +                return false;
   1.179 +        }
   1.180 +
   1.181 +        // write out element.
   1.182 +        header_->copyStackBottom -= size;
   1.183 +        bufferAvail_ -= size;
   1.184 +        bufferUsed_ += size;
   1.185 +        framePushed_ += size;
   1.186 +        if (info) {
   1.187 +            IonSpew(IonSpew_BaselineBailouts,
   1.188 +                    "      SUB_%03d   %p/%p %-15s",
   1.189 +                    (int) size, header_->copyStackBottom, virtualPointerAtStackOffset(0), info);
   1.190 +        }
   1.191 +        return true;
   1.192 +    }
   1.193 +
   1.194 +    template <typename T>
   1.195 +    bool write(const T &t) {
   1.196 +        if (!subtract(sizeof(T)))
   1.197 +            return false;
   1.198 +        memcpy(header_->copyStackBottom, &t, sizeof(T));
   1.199 +        return true;
   1.200 +    }
   1.201 +
   1.202 +    template <typename T>
   1.203 +    bool writePtr(T *t, const char *info) {
   1.204 +        if (!write<T *>(t))
   1.205 +            return false;
   1.206 +        if (info)
   1.207 +            IonSpew(IonSpew_BaselineBailouts,
   1.208 +                    "      WRITE_PTR %p/%p %-15s %p",
   1.209 +                    header_->copyStackBottom, virtualPointerAtStackOffset(0), info, t);
   1.210 +        return true;
   1.211 +    }
   1.212 +
   1.213 +    bool writeWord(size_t w, const char *info) {
   1.214 +        if (!write<size_t>(w))
   1.215 +            return false;
   1.216 +        if (info) {
   1.217 +            if (sizeof(size_t) == 4) {
   1.218 +                IonSpew(IonSpew_BaselineBailouts,
   1.219 +                        "      WRITE_WRD %p/%p %-15s %08x",
   1.220 +                        header_->copyStackBottom, virtualPointerAtStackOffset(0), info, w);
   1.221 +            } else {
   1.222 +                IonSpew(IonSpew_BaselineBailouts,
   1.223 +                        "      WRITE_WRD %p/%p %-15s %016llx",
   1.224 +                        header_->copyStackBottom, virtualPointerAtStackOffset(0), info, w);
   1.225 +            }
   1.226 +        }
   1.227 +        return true;
   1.228 +    }
   1.229 +
   1.230 +    bool writeValue(Value val, const char *info) {
   1.231 +        if (!write<Value>(val))
   1.232 +            return false;
   1.233 +        if (info) {
   1.234 +            IonSpew(IonSpew_BaselineBailouts,
   1.235 +                    "      WRITE_VAL %p/%p %-15s %016llx",
   1.236 +                    header_->copyStackBottom, virtualPointerAtStackOffset(0), info,
   1.237 +                    *((uint64_t *) &val));
   1.238 +        }
   1.239 +        return true;
   1.240 +    }
   1.241 +
   1.242 +    Value popValue() {
   1.243 +        JS_ASSERT(bufferUsed_ >= sizeof(Value));
   1.244 +        JS_ASSERT(framePushed_ >= sizeof(Value));
   1.245 +        bufferAvail_ += sizeof(Value);
   1.246 +        bufferUsed_ -= sizeof(Value);
   1.247 +        framePushed_ -= sizeof(Value);
   1.248 +        Value result = *((Value *) header_->copyStackBottom);
   1.249 +        header_->copyStackBottom += sizeof(Value);
   1.250 +        return result;
   1.251 +    }
   1.252 +
   1.253 +    void popValueInto(PCMappingSlotInfo::SlotLocation loc) {
   1.254 +        JS_ASSERT(PCMappingSlotInfo::ValidSlotLocation(loc));
   1.255 +        switch(loc) {
   1.256 +          case PCMappingSlotInfo::SlotInR0:
   1.257 +            header_->setR0 = 1;
   1.258 +            header_->valueR0 = popValue();
   1.259 +            break;
   1.260 +          case PCMappingSlotInfo::SlotInR1:
   1.261 +            header_->setR1 = 1;
   1.262 +            header_->valueR1 = popValue();
   1.263 +            break;
   1.264 +          default:
   1.265 +            JS_ASSERT(loc == PCMappingSlotInfo::SlotIgnore);
   1.266 +            popValue();
   1.267 +            break;
   1.268 +        }
   1.269 +    }
   1.270 +
   1.271 +    void setResumeFramePtr(void *resumeFramePtr) {
   1.272 +        header_->resumeFramePtr = resumeFramePtr;
   1.273 +    }
   1.274 +
   1.275 +    void setResumeAddr(void *resumeAddr) {
   1.276 +        header_->resumeAddr = resumeAddr;
   1.277 +    }
   1.278 +
   1.279 +    void setMonitorStub(ICStub *stub) {
   1.280 +        header_->monitorStub = stub;
   1.281 +    }
   1.282 +
   1.283 +    template <typename T>
   1.284 +    BufferPointer<T> pointerAtStackOffset(size_t offset) {
   1.285 +        if (offset < bufferUsed_) {
   1.286 +            // Calculate offset from copyStackTop.
   1.287 +            offset = header_->copyStackTop - (header_->copyStackBottom + offset);
   1.288 +            return BufferPointer<T>(&header_, offset, /* heap = */ true);
   1.289 +        }
   1.290 +
   1.291 +        return BufferPointer<T>(&header_, offset - bufferUsed_, /* heap = */ false);
   1.292 +    }
   1.293 +
   1.294 +    BufferPointer<Value> valuePointerAtStackOffset(size_t offset) {
   1.295 +        return pointerAtStackOffset<Value>(offset);
   1.296 +    }
   1.297 +
   1.298 +    inline uint8_t *virtualPointerAtStackOffset(size_t offset) {
   1.299 +        if (offset < bufferUsed_)
   1.300 +            return reinterpret_cast<uint8_t *>(frame_) - (bufferUsed_ - offset);
   1.301 +        return reinterpret_cast<uint8_t *>(frame_) + (offset - bufferUsed_);
   1.302 +    }
   1.303 +
   1.304 +    inline IonJSFrameLayout *startFrame() {
   1.305 +        return frame_;
   1.306 +    }
   1.307 +
   1.308 +    BufferPointer<IonJSFrameLayout> topFrameAddress() {
   1.309 +        return pointerAtStackOffset<IonJSFrameLayout>(0);
   1.310 +    }
   1.311 +
   1.312 +    //
   1.313 +    // This method should only be called when the builder is in a state where it is
   1.314 +    // starting to construct the stack frame for the next callee.  This means that
   1.315 +    // the lowest value on the constructed stack is the return address for the previous
   1.316 +    // caller frame.
   1.317 +    //
   1.318 +    // This method is used to compute the value of the frame pointer (e.g. ebp on x86)
   1.319 +    // that would have been saved by the baseline jitcode when it was entered.  In some
   1.320 +    // cases, this value can be bogus since we can ensure that the caller would have saved
   1.321 +    // it anyway.
   1.322 +    //
   1.323 +    void *calculatePrevFramePtr() {
   1.324 +        // Get the incoming frame.
   1.325 +        BufferPointer<IonJSFrameLayout> topFrame = topFrameAddress();
   1.326 +        FrameType type = topFrame->prevType();
   1.327 +
   1.328 +        // For IonJS and Entry frames, the "saved" frame pointer in the baseline
   1.329 +        // frame is meaningless, since Ion saves all registers before calling other ion
   1.330 +        // frames, and the entry frame saves all registers too.
   1.331 +        if (type == JitFrame_IonJS || type == JitFrame_Entry)
   1.332 +            return nullptr;
   1.333 +
   1.334 +        // BaselineStub - Baseline calling into Ion.
   1.335 +        //  PrevFramePtr needs to point to the BaselineStubFrame's saved frame pointer.
   1.336 +        //      STACK_START_ADDR + IonJSFrameLayout::Size() + PREV_FRAME_SIZE
   1.337 +        //                      - IonBaselineStubFrameLayout::reverseOffsetOfSavedFramePtr()
   1.338 +        if (type == JitFrame_BaselineStub) {
   1.339 +            size_t offset = IonJSFrameLayout::Size() + topFrame->prevFrameLocalSize() +
   1.340 +                            IonBaselineStubFrameLayout::reverseOffsetOfSavedFramePtr();
   1.341 +            return virtualPointerAtStackOffset(offset);
   1.342 +        }
   1.343 +
   1.344 +        JS_ASSERT(type == JitFrame_Rectifier);
   1.345 +        // Rectifier - behaviour depends on the frame preceding the rectifier frame, and
   1.346 +        // whether the arch is x86 or not.  The x86 rectifier frame saves the frame pointer,
   1.347 +        // so we can calculate it directly.  For other archs, the previous frame pointer
   1.348 +        // is stored on the stack in the frame that precedes the rectifier frame.
   1.349 +        size_t priorOffset = IonJSFrameLayout::Size() + topFrame->prevFrameLocalSize();
   1.350 +#if defined(JS_CODEGEN_X86)
   1.351 +        // On X86, the FramePointer is pushed as the first value in the Rectifier frame.
   1.352 +        JS_ASSERT(BaselineFrameReg == FramePointer);
   1.353 +        priorOffset -= sizeof(void *);
   1.354 +        return virtualPointerAtStackOffset(priorOffset);
   1.355 +#elif defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
   1.356 +        // On X64, ARM and MIPS, the frame pointer save location depends on
   1.357 +        // the caller of the rectifier frame.
   1.358 +        BufferPointer<IonRectifierFrameLayout> priorFrame =
   1.359 +            pointerAtStackOffset<IonRectifierFrameLayout>(priorOffset);
   1.360 +        FrameType priorType = priorFrame->prevType();
   1.361 +        JS_ASSERT(priorType == JitFrame_IonJS || priorType == JitFrame_BaselineStub);
   1.362 +
   1.363 +        // If the frame preceding the rectifier is an IonJS frame, then once again
   1.364 +        // the frame pointer does not matter.
   1.365 +        if (priorType == JitFrame_IonJS)
   1.366 +            return nullptr;
   1.367 +
   1.368 +        // Otherwise, the frame preceding the rectifier is a BaselineStub frame.
   1.369 +        //  let X = STACK_START_ADDR + IonJSFrameLayout::Size() + PREV_FRAME_SIZE
   1.370 +        //      X + IonRectifierFrameLayout::Size()
   1.371 +        //        + ((IonRectifierFrameLayout *) X)->prevFrameLocalSize()
   1.372 +        //        - BaselineStubFrameLayout::reverseOffsetOfSavedFramePtr()
   1.373 +        size_t extraOffset = IonRectifierFrameLayout::Size() + priorFrame->prevFrameLocalSize() +
   1.374 +                             IonBaselineStubFrameLayout::reverseOffsetOfSavedFramePtr();
   1.375 +        return virtualPointerAtStackOffset(priorOffset + extraOffset);
   1.376 +#else
   1.377 +#  error "Bad architecture!"
   1.378 +#endif
   1.379 +    }
   1.380 +};
   1.381 +
   1.382 +static inline bool
   1.383 +IsInlinableFallback(ICFallbackStub *icEntry)
   1.384 +{
   1.385 +    return icEntry->isCall_Fallback() || icEntry->isGetProp_Fallback() ||
   1.386 +           icEntry->isSetProp_Fallback();
   1.387 +}
   1.388 +
   1.389 +static inline void*
   1.390 +GetStubReturnAddress(JSContext *cx, jsbytecode *pc)
   1.391 +{
   1.392 +    if (IsGetPropPC(pc))
   1.393 +        return cx->compartment()->jitCompartment()->baselineGetPropReturnFromIonAddr();
   1.394 +    if (IsSetPropPC(pc))
   1.395 +        return cx->compartment()->jitCompartment()->baselineSetPropReturnFromIonAddr();
   1.396 +    // This should be a call op of some kind, now.
   1.397 +    JS_ASSERT(IsCallPC(pc));
   1.398 +    return cx->compartment()->jitCompartment()->baselineCallReturnFromIonAddr();
   1.399 +}
   1.400 +
   1.401 +static inline jsbytecode *
   1.402 +GetNextNonLoopEntryPc(jsbytecode *pc)
   1.403 +{
   1.404 +    JSOp op = JSOp(*pc);
   1.405 +    if (op == JSOP_GOTO)
   1.406 +        return pc + GET_JUMP_OFFSET(pc);
   1.407 +    if (op == JSOP_LOOPENTRY || op == JSOP_NOP || op == JSOP_LOOPHEAD)
   1.408 +        return GetNextPc(pc);
   1.409 +    return pc;
   1.410 +}
   1.411 +
   1.412 +// For every inline frame, we write out the following data:
   1.413 +//
   1.414 +//                      |      ...      |
   1.415 +//                      +---------------+
   1.416 +//                      |  Descr(???)   |  --- Descr size here is (PREV_FRAME_SIZE)
   1.417 +//                      +---------------+
   1.418 +//                      |  ReturnAddr   |
   1.419 +//             --       +===============+  --- OVERWRITE STARTS HERE  (START_STACK_ADDR)
   1.420 +//             |        | PrevFramePtr  |
   1.421 +//             |    +-> +---------------+
   1.422 +//             |    |   |   Baseline    |
   1.423 +//             |    |   |    Frame      |
   1.424 +//             |    |   +---------------+
   1.425 +//             |    |   |    Fixed0     |
   1.426 +//             |    |   +---------------+
   1.427 +//         +--<     |   |     ...       |
   1.428 +//         |   |    |   +---------------+
   1.429 +//         |   |    |   |    FixedF     |
   1.430 +//         |   |    |   +---------------+
   1.431 +//         |   |    |   |    Stack0     |
   1.432 +//         |   |    |   +---------------+
   1.433 +//         |   |    |   |     ...       |
   1.434 +//         |   |    |   +---------------+
   1.435 +//         |   |    |   |    StackS     |
   1.436 +//         |   --   |   +---------------+  --- IF NOT LAST INLINE FRAME,
   1.437 +//         +------------|  Descr(BLJS)  |  --- CALLING INFO STARTS HERE
   1.438 +//                  |   +---------------+
   1.439 +//                  |   |  ReturnAddr   | <-- return into main jitcode after IC
   1.440 +//             --   |   +===============+
   1.441 +//             |    |   |    StubPtr    |
   1.442 +//             |    |   +---------------+
   1.443 +//             |    +---|   FramePtr    |
   1.444 +//             |        +---------------+
   1.445 +//             |        |     ArgA      |
   1.446 +//             |        +---------------+
   1.447 +//             |        |     ...       |
   1.448 +//         +--<         +---------------+
   1.449 +//         |   |        |     Arg0      |
   1.450 +//         |   |        +---------------+
   1.451 +//         |   |        |     ThisV     |
   1.452 +//         |   --       +---------------+
   1.453 +//         |            |  ActualArgC   |
   1.454 +//         |            +---------------+
   1.455 +//         |            |  CalleeToken  |
   1.456 +//         |            +---------------+
   1.457 +//         +------------| Descr(BLStub) |
   1.458 +//                      +---------------+
   1.459 +//                      |  ReturnAddr   | <-- return into ICCall_Scripted IC
   1.460 +//             --       +===============+ --- IF CALLEE FORMAL ARGS > ActualArgC
   1.461 +//             |        |  UndefinedU   |
   1.462 +//             |        +---------------+
   1.463 +//             |        |     ...       |
   1.464 +//             |        +---------------+
   1.465 +//             |        |  Undefined0   |
   1.466 +//             |        +---------------+
   1.467 +//         +--<         |     ArgA      |
   1.468 +//         |   |        +---------------+
   1.469 +//         |   |        |     ...       |
   1.470 +//         |   |        +---------------+
   1.471 +//         |   |        |     Arg0      |
   1.472 +//         |   |        +---------------+
   1.473 +//         |   |        |     ThisV     |
   1.474 +//         |   --       +---------------+
   1.475 +//         |            |  ActualArgC   |
   1.476 +//         |            +---------------+
   1.477 +//         |            |  CalleeToken  |
   1.478 +//         |            +---------------+
   1.479 +//         +------------|  Descr(Rect)  |
   1.480 +//                      +---------------+
   1.481 +//                      |  ReturnAddr   | <-- return into ArgumentsRectifier after call
   1.482 +//                      +===============+
   1.483 +//
   1.484 +static bool
   1.485 +InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
   1.486 +                HandleFunction fun, HandleScript script, IonScript *ionScript,
   1.487 +                SnapshotIterator &iter, bool invalidate, BaselineStackBuilder &builder,
   1.488 +                AutoValueVector &startFrameFormals, MutableHandleFunction nextCallee,
   1.489 +                jsbytecode **callPC, const ExceptionBailoutInfo *excInfo)
   1.490 +{
   1.491 +    MOZ_ASSERT(script->hasBaselineScript());
   1.492 +
   1.493 +    // Are we catching an exception?
   1.494 +    bool catchingException = excInfo && excInfo->catchingException();
   1.495 +
   1.496 +    // If we are catching an exception, we are bailing out to a catch or
   1.497 +    // finally block and this is the frame where we will resume. Usually the
   1.498 +    // expression stack should be empty in this case but there can be
   1.499 +    // iterators on the stack.
   1.500 +    uint32_t exprStackSlots;
   1.501 +    if (catchingException)
   1.502 +        exprStackSlots = excInfo->numExprSlots();
   1.503 +    else
   1.504 +        exprStackSlots = iter.numAllocations() - (script->nfixed() + CountArgSlots(script, fun));
   1.505 +
   1.506 +    builder.resetFramePushed();
   1.507 +
   1.508 +    // Build first baseline frame:
   1.509 +    // +===============+
   1.510 +    // | PrevFramePtr  |
   1.511 +    // +---------------+
   1.512 +    // |   Baseline    |
   1.513 +    // |    Frame      |
   1.514 +    // +---------------+
   1.515 +    // |    Fixed0     |
   1.516 +    // +---------------+
   1.517 +    // |     ...       |
   1.518 +    // +---------------+
   1.519 +    // |    FixedF     |
   1.520 +    // +---------------+
   1.521 +    // |    Stack0     |
   1.522 +    // +---------------+
   1.523 +    // |     ...       |
   1.524 +    // +---------------+
   1.525 +    // |    StackS     |
   1.526 +    // +---------------+  --- IF NOT LAST INLINE FRAME,
   1.527 +    // |  Descr(BLJS)  |  --- CALLING INFO STARTS HERE
   1.528 +    // +---------------+
   1.529 +    // |  ReturnAddr   | <-- return into main jitcode after IC
   1.530 +    // +===============+
   1.531 +
   1.532 +    IonSpew(IonSpew_BaselineBailouts, "      Unpacking %s:%d", script->filename(), script->lineno());
   1.533 +    IonSpew(IonSpew_BaselineBailouts, "      [BASELINE-JS FRAME]");
   1.534 +
   1.535 +    // Calculate and write the previous frame pointer value.
   1.536 +    // Record the virtual stack offset at this location.  Later on, if we end up
   1.537 +    // writing out a BaselineStub frame for the next callee, we'll need to save the
   1.538 +    // address.
   1.539 +    void *prevFramePtr = builder.calculatePrevFramePtr();
   1.540 +    if (!builder.writePtr(prevFramePtr, "PrevFramePtr"))
   1.541 +        return false;
   1.542 +    prevFramePtr = builder.virtualPointerAtStackOffset(0);
   1.543 +
   1.544 +    // Write struct BaselineFrame.
   1.545 +    if (!builder.subtract(BaselineFrame::Size(), "BaselineFrame"))
   1.546 +        return false;
   1.547 +    BufferPointer<BaselineFrame> blFrame = builder.pointerAtStackOffset<BaselineFrame>(0);
   1.548 +
   1.549 +    // Initialize BaselineFrame::frameSize
   1.550 +    uint32_t frameSize = BaselineFrame::Size() + BaselineFrame::FramePointerOffset +
   1.551 +                         (sizeof(Value) * (script->nfixed() + exprStackSlots));
   1.552 +    IonSpew(IonSpew_BaselineBailouts, "      FrameSize=%d", (int) frameSize);
   1.553 +    blFrame->setFrameSize(frameSize);
   1.554 +
   1.555 +    uint32_t flags = 0;
   1.556 +
   1.557 +    // If SPS Profiler is enabled, mark the frame as having pushed an SPS entry.
   1.558 +    // This may be wrong for the last frame of ArgumentCheck bailout, but
   1.559 +    // that will be fixed later.
   1.560 +    if (ionScript->hasSPSInstrumentation()) {
   1.561 +        if (callerPC == nullptr) {
   1.562 +            IonSpew(IonSpew_BaselineBailouts, "      Setting SPS flag on top frame!");
   1.563 +            flags |= BaselineFrame::HAS_PUSHED_SPS_FRAME;
   1.564 +        } else if (js_JitOptions.profileInlineFrames) {
   1.565 +            IonSpew(IonSpew_BaselineBailouts, "      Setting SPS flag on inline frame!");
   1.566 +            flags |= BaselineFrame::HAS_PUSHED_SPS_FRAME;
   1.567 +        }
   1.568 +    }
   1.569 +
   1.570 +    // Initialize BaselineFrame's scopeChain and argsObj
   1.571 +    JSObject *scopeChain = nullptr;
   1.572 +    Value returnValue;
   1.573 +    ArgumentsObject *argsObj = nullptr;
   1.574 +    BailoutKind bailoutKind = iter.bailoutKind();
   1.575 +    if (bailoutKind == Bailout_ArgumentCheck) {
   1.576 +        // Temporary hack -- skip the (unused) scopeChain, because it could be
   1.577 +        // bogus (we can fail before the scope chain slot is set). Strip the
   1.578 +        // hasScopeChain flag and this will be fixed up later in |FinishBailoutToBaseline|,
   1.579 +        // which calls |EnsureHasScopeObjects|.
   1.580 +        IonSpew(IonSpew_BaselineBailouts, "      Bailout_ArgumentCheck! (no valid scopeChain)");
   1.581 +        iter.skip();
   1.582 +
   1.583 +        // skip |return value|
   1.584 +        iter.skip();
   1.585 +        returnValue = UndefinedValue();
   1.586 +
   1.587 +        // Scripts with |argumentsHasVarBinding| have an extra slot.
   1.588 +        if (script->argumentsHasVarBinding()) {
   1.589 +            IonSpew(IonSpew_BaselineBailouts,
   1.590 +                    "      Bailout_ArgumentCheck for script with argumentsHasVarBinding!"
   1.591 +                    "Using empty arguments object");
   1.592 +            iter.skip();
   1.593 +        }
   1.594 +    } else {
   1.595 +        Value v = iter.read();
   1.596 +        if (v.isObject()) {
   1.597 +            scopeChain = &v.toObject();
   1.598 +            if (fun && fun->isHeavyweight())
   1.599 +                flags |= BaselineFrame::HAS_CALL_OBJ;
   1.600 +        } else {
   1.601 +            JS_ASSERT(v.isUndefined() || v.isMagic(JS_OPTIMIZED_OUT));
   1.602 +
   1.603 +            // Get scope chain from function or script.
   1.604 +            if (fun) {
   1.605 +                // If pcOffset == 0, we may have to push a new call object, so
   1.606 +                // we leave scopeChain nullptr and enter baseline code before
   1.607 +                // the prologue.
   1.608 +                if (iter.pcOffset() != 0 || iter.resumeAfter())
   1.609 +                    scopeChain = fun->environment();
   1.610 +            } else {
   1.611 +                // For global, compile-and-go scripts the scope chain is the
   1.612 +                // script's global (Ion does not compile non-compile-and-go
   1.613 +                // scripts). Also note that it's invalid to resume into the
   1.614 +                // prologue in this case because the prologue expects the scope
   1.615 +                // chain in R1 for eval and global scripts.
   1.616 +                JS_ASSERT(!script->isForEval());
   1.617 +                JS_ASSERT(script->compileAndGo());
   1.618 +                scopeChain = &(script->global());
   1.619 +            }
   1.620 +        }
   1.621 +
   1.622 +        // Make sure to add HAS_RVAL to flags here because setFlags() below
   1.623 +        // will clobber it.
   1.624 +        returnValue = iter.read();
   1.625 +        flags |= BaselineFrame::HAS_RVAL;
   1.626 +
   1.627 +        // If script maybe has an arguments object, the third slot will hold it.
   1.628 +        if (script->argumentsHasVarBinding()) {
   1.629 +            v = iter.read();
   1.630 +            JS_ASSERT(v.isObject() || v.isUndefined() || v.isMagic(JS_OPTIMIZED_OUT));
   1.631 +            if (v.isObject())
   1.632 +                argsObj = &v.toObject().as<ArgumentsObject>();
   1.633 +        }
   1.634 +    }
   1.635 +    IonSpew(IonSpew_BaselineBailouts, "      ScopeChain=%p", scopeChain);
   1.636 +    blFrame->setScopeChain(scopeChain);
   1.637 +    IonSpew(IonSpew_BaselineBailouts, "      ReturnValue=%016llx", *((uint64_t *) &returnValue));
   1.638 +    blFrame->setReturnValue(returnValue);
   1.639 +
   1.640 +    // Do not need to initialize scratchValue field in BaselineFrame.
   1.641 +    blFrame->setFlags(flags);
   1.642 +
   1.643 +    // initArgsObjUnchecked modifies the frame's flags, so call it after setFlags.
   1.644 +    if (argsObj)
   1.645 +        blFrame->initArgsObjUnchecked(*argsObj);
   1.646 +
   1.647 +    if (fun) {
   1.648 +        // The unpacked thisv and arguments should overwrite the pushed args present
   1.649 +        // in the calling frame.
   1.650 +        Value thisv = iter.read();
   1.651 +        IonSpew(IonSpew_BaselineBailouts, "      Is function!");
   1.652 +        IonSpew(IonSpew_BaselineBailouts, "      thisv=%016llx", *((uint64_t *) &thisv));
   1.653 +
   1.654 +        size_t thisvOffset = builder.framePushed() + IonJSFrameLayout::offsetOfThis();
   1.655 +        *builder.valuePointerAtStackOffset(thisvOffset) = thisv;
   1.656 +
   1.657 +        JS_ASSERT(iter.numAllocations() >= CountArgSlots(script, fun));
   1.658 +        IonSpew(IonSpew_BaselineBailouts, "      frame slots %u, nargs %u, nfixed %u",
   1.659 +                iter.numAllocations(), fun->nargs(), script->nfixed());
   1.660 +
   1.661 +        if (!callerPC) {
   1.662 +            // This is the first frame. Store the formals in a Vector until we
   1.663 +            // are done. Due to UCE and phi elimination, we could store an
   1.664 +            // UndefinedValue() here for formals we think are unused, but
   1.665 +            // locals may still reference the original argument slot
   1.666 +            // (MParameter/LArgument) and expect the original Value.
   1.667 +            JS_ASSERT(startFrameFormals.empty());
   1.668 +            if (!startFrameFormals.resize(fun->nargs()))
   1.669 +                return false;
   1.670 +        }
   1.671 +
   1.672 +        for (uint32_t i = 0; i < fun->nargs(); i++) {
   1.673 +            Value arg = iter.read();
   1.674 +            IonSpew(IonSpew_BaselineBailouts, "      arg %d = %016llx",
   1.675 +                        (int) i, *((uint64_t *) &arg));
   1.676 +            if (callerPC) {
   1.677 +                size_t argOffset = builder.framePushed() + IonJSFrameLayout::offsetOfActualArg(i);
   1.678 +                *builder.valuePointerAtStackOffset(argOffset) = arg;
   1.679 +            } else {
   1.680 +                startFrameFormals[i] = arg;
   1.681 +            }
   1.682 +        }
   1.683 +    }
   1.684 +
   1.685 +    for (uint32_t i = 0; i < script->nfixed(); i++) {
   1.686 +        Value slot = iter.read();
   1.687 +        if (!builder.writeValue(slot, "FixedValue"))
   1.688 +            return false;
   1.689 +    }
   1.690 +
   1.691 +    // Get the pc. If we are handling an exception, resume at the pc of the
   1.692 +    // catch or finally block.
   1.693 +    jsbytecode *pc = catchingException ? excInfo->resumePC() : script->offsetToPC(iter.pcOffset());
   1.694 +    bool resumeAfter = catchingException ? false : iter.resumeAfter();
   1.695 +
   1.696 +    JSOp op = JSOp(*pc);
   1.697 +
   1.698 +    // Fixup inlined JSOP_FUNCALL, JSOP_FUNAPPLY, and accessors on the caller side.
   1.699 +    // On the caller side this must represent like the function wasn't inlined.
   1.700 +    uint32_t pushedSlots = 0;
   1.701 +    AutoValueVector savedCallerArgs(cx);
   1.702 +    bool needToSaveArgs = op == JSOP_FUNAPPLY || IsGetPropPC(pc) || IsSetPropPC(pc);
   1.703 +    if (iter.moreFrames() && (op == JSOP_FUNCALL || needToSaveArgs))
   1.704 +    {
   1.705 +        uint32_t inlined_args = 0;
   1.706 +        if (op == JSOP_FUNCALL)
   1.707 +            inlined_args = 2 + GET_ARGC(pc) - 1;
   1.708 +        else if (op == JSOP_FUNAPPLY)
   1.709 +            inlined_args = 2 + blFrame->numActualArgs();
   1.710 +        else
   1.711 +            inlined_args = 2 + IsSetPropPC(pc);
   1.712 +
   1.713 +        JS_ASSERT(exprStackSlots >= inlined_args);
   1.714 +        pushedSlots = exprStackSlots - inlined_args;
   1.715 +
   1.716 +        IonSpew(IonSpew_BaselineBailouts,
   1.717 +                "      pushing %u expression stack slots before fixup",
   1.718 +                pushedSlots);
   1.719 +        for (uint32_t i = 0; i < pushedSlots; i++) {
   1.720 +            Value v = iter.read();
   1.721 +            if (!builder.writeValue(v, "StackValue"))
   1.722 +                return false;
   1.723 +        }
   1.724 +
   1.725 +        if (op == JSOP_FUNCALL) {
   1.726 +            // When funcall got inlined and the native js_fun_call was bypassed,
   1.727 +            // the stack state is incorrect. To restore correctly it must look like
   1.728 +            // js_fun_call was actually called. This means transforming the stack
   1.729 +            // from |target, this, args| to |js_fun_call, target, this, args|
   1.730 +            // The js_fun_call is never read, so just pushing undefined now.
   1.731 +            IonSpew(IonSpew_BaselineBailouts, "      pushing undefined to fixup funcall");
   1.732 +            if (!builder.writeValue(UndefinedValue(), "StackValue"))
   1.733 +                return false;
   1.734 +        }
   1.735 +
   1.736 +        if (needToSaveArgs) {
   1.737 +            // When an accessor is inlined, the whole thing is a lie. There
   1.738 +            // should never have been a call there. Fix the caller's stack to
   1.739 +            // forget it ever happened.
   1.740 +
   1.741 +            // When funapply gets inlined we take all arguments out of the
   1.742 +            // arguments array. So the stack state is incorrect. To restore
   1.743 +            // correctly it must look like js_fun_apply was actually called.
   1.744 +            // This means transforming the stack from |target, this, arg1, ...|
   1.745 +            // to |js_fun_apply, target, this, argObject|.
   1.746 +            // Since the information is never read, we can just push undefined
   1.747 +            // for all values.
   1.748 +            if (op == JSOP_FUNAPPLY) {
   1.749 +                IonSpew(IonSpew_BaselineBailouts, "      pushing 4x undefined to fixup funapply");
   1.750 +                if (!builder.writeValue(UndefinedValue(), "StackValue"))
   1.751 +                    return false;
   1.752 +                if (!builder.writeValue(UndefinedValue(), "StackValue"))
   1.753 +                    return false;
   1.754 +                if (!builder.writeValue(UndefinedValue(), "StackValue"))
   1.755 +                    return false;
   1.756 +                if (!builder.writeValue(UndefinedValue(), "StackValue"))
   1.757 +                    return false;
   1.758 +            }
   1.759 +            // Save the actual arguments. They are needed on the callee side
   1.760 +            // as the arguments. Else we can't recover them.
   1.761 +            if (!savedCallerArgs.resize(inlined_args))
   1.762 +                return false;
   1.763 +            for (uint32_t i = 0; i < inlined_args; i++)
   1.764 +                savedCallerArgs[i] = iter.read();
   1.765 +
   1.766 +            if (IsSetPropPC(pc)) {
   1.767 +                // We would love to just save all the arguments and leave them
   1.768 +                // in the stub frame pushed below, but we will lose the inital
   1.769 +                // argument which the function was called with, which we must
   1.770 +                // return to the caller, even if the setter internally modifies
   1.771 +                // its arguments. Stash the initial argument on the stack, to be
   1.772 +                // later retrieved by the SetProp_Fallback stub.
   1.773 +                Value initialArg = savedCallerArgs[inlined_args - 1];
   1.774 +                IonSpew(IonSpew_BaselineBailouts, "     pushing setter's initial argument");
   1.775 +                if (!builder.writeValue(initialArg, "StackValue"))
   1.776 +                    return false;
   1.777 +            }
   1.778 +            pushedSlots = exprStackSlots;
   1.779 +        }
   1.780 +    }
   1.781 +
   1.782 +    IonSpew(IonSpew_BaselineBailouts, "      pushing %u expression stack slots",
   1.783 +                                      exprStackSlots - pushedSlots);
   1.784 +    for (uint32_t i = pushedSlots; i < exprStackSlots; i++) {
   1.785 +        Value v;
   1.786 +
   1.787 +        if (!iter.moreFrames() && i == exprStackSlots - 1 &&
   1.788 +            cx->runtime()->hasIonReturnOverride())
   1.789 +        {
   1.790 +            // If coming from an invalidation bailout, and this is the topmost
   1.791 +            // value, and a value override has been specified, don't read from the
   1.792 +            // iterator. Otherwise, we risk using a garbage value.
   1.793 +            JS_ASSERT(invalidate);
   1.794 +            iter.skip();
   1.795 +            IonSpew(IonSpew_BaselineBailouts, "      [Return Override]");
   1.796 +            v = cx->runtime()->takeIonReturnOverride();
   1.797 +        } else if (excInfo && excInfo->propagatingIonExceptionForDebugMode()) {
   1.798 +            // If we are in the middle of propagating an exception from Ion by
   1.799 +            // bailing to baseline due to debug mode, we might not have all
   1.800 +            // the stack if we are at the newest frame.
   1.801 +            //
   1.802 +            // For instance, if calling |f()| pushed an Ion frame which threw,
   1.803 +            // the snapshot expects the return value to be pushed, but it's
   1.804 +            // possible nothing was pushed before we threw. Iterators might
   1.805 +            // still be on the stack, so we can't just drop the stack.
   1.806 +            MOZ_ASSERT(cx->compartment()->debugMode());
   1.807 +            if (iter.moreFrames())
   1.808 +                v = iter.read();
   1.809 +            else
   1.810 +                v = MagicValue(JS_OPTIMIZED_OUT);
   1.811 +        } else {
   1.812 +            v = iter.read();
   1.813 +        }
   1.814 +        if (!builder.writeValue(v, "StackValue"))
   1.815 +            return false;
   1.816 +    }
   1.817 +
   1.818 +    size_t endOfBaselineJSFrameStack = builder.framePushed();
   1.819 +
   1.820 +    // If we are resuming at a LOOPENTRY op, resume at the next op to avoid
   1.821 +    // a bailout -> enter Ion -> bailout loop with --ion-eager. See also
   1.822 +    // ThunkToInterpreter.
   1.823 +    //
   1.824 +    // The algorithm below is the "tortoise and the hare" algorithm. See bug
   1.825 +    // 994444 for more explanation.
   1.826 +    if (!resumeAfter) {
   1.827 +        jsbytecode *fasterPc = pc;
   1.828 +        while (true) {
   1.829 +            pc = GetNextNonLoopEntryPc(pc);
   1.830 +            fasterPc = GetNextNonLoopEntryPc(GetNextNonLoopEntryPc(fasterPc));
   1.831 +            if (fasterPc == pc)
   1.832 +                break;
   1.833 +        }
   1.834 +        op = JSOp(*pc);
   1.835 +    }
   1.836 +
   1.837 +    uint32_t pcOff = script->pcToOffset(pc);
   1.838 +    bool isCall = IsCallPC(pc);
   1.839 +    BaselineScript *baselineScript = script->baselineScript();
   1.840 +
   1.841 +#ifdef DEBUG
   1.842 +    uint32_t expectedDepth;
   1.843 +    bool reachablePC;
   1.844 +    if (!ReconstructStackDepth(cx, script, resumeAfter ? GetNextPc(pc) : pc, &expectedDepth, &reachablePC))
   1.845 +        return false;
   1.846 +
   1.847 +    if (reachablePC) {
   1.848 +        if (op != JSOP_FUNAPPLY || !iter.moreFrames() || resumeAfter) {
   1.849 +            if (op == JSOP_FUNCALL) {
   1.850 +                // For fun.call(this, ...); the reconstructStackDepth will
   1.851 +                // include the this. When inlining that is not included.
   1.852 +                // So the exprStackSlots will be one less.
   1.853 +                JS_ASSERT(expectedDepth - exprStackSlots <= 1);
   1.854 +            } else if (iter.moreFrames() && (IsGetPropPC(pc) || IsSetPropPC(pc))) {
   1.855 +                // Accessors coming out of ion are inlined via a complete
   1.856 +                // lie perpetrated by the compiler internally. Ion just rearranges
   1.857 +                // the stack, and pretends that it looked like a call all along.
   1.858 +                // This means that the depth is actually one *more* than expected
   1.859 +                // by the interpreter, as there is now a JSFunction, |this| and [arg],
   1.860 +                // rather than the expected |this| and [arg]
   1.861 +                // Note that none of that was pushed, but it's still reflected
   1.862 +                // in exprStackSlots.
   1.863 +                JS_ASSERT(exprStackSlots - expectedDepth == 1);
   1.864 +            } else {
   1.865 +                // For fun.apply({}, arguments) the reconstructStackDepth will
   1.866 +                // have stackdepth 4, but it could be that we inlined the
   1.867 +                // funapply. In that case exprStackSlots, will have the real
   1.868 +                // arguments in the slots and not be 4.
   1.869 +                JS_ASSERT(exprStackSlots == expectedDepth);
   1.870 +            }
   1.871 +        }
   1.872 +    }
   1.873 +
   1.874 +    IonSpew(IonSpew_BaselineBailouts, "      Resuming %s pc offset %d (op %s) (line %d) of %s:%d",
   1.875 +                resumeAfter ? "after" : "at", (int) pcOff, js_CodeName[op],
   1.876 +                PCToLineNumber(script, pc), script->filename(), (int) script->lineno());
   1.877 +    IonSpew(IonSpew_BaselineBailouts, "      Bailout kind: %s",
   1.878 +            BailoutKindString(bailoutKind));
   1.879 +#endif
   1.880 +
   1.881 +    // If this was the last inline frame, or we are bailing out to a catch or
   1.882 +    // finally block in this frame, then unpacking is almost done.
   1.883 +    if (!iter.moreFrames() || catchingException) {
   1.884 +        // Last frame, so PC for call to next frame is set to nullptr.
   1.885 +        *callPC = nullptr;
   1.886 +
   1.887 +        // If the bailout was a resumeAfter, and the opcode is monitored,
   1.888 +        // then the bailed out state should be in a position to enter
   1.889 +        // into the ICTypeMonitor chain for the op.
   1.890 +        bool enterMonitorChain = false;
   1.891 +        if (resumeAfter && (js_CodeSpec[op].format & JOF_TYPESET)) {
   1.892 +            // Not every monitored op has a monitored fallback stub, e.g.
   1.893 +            // JSOP_NEWOBJECT, which always returns the same type for a
   1.894 +            // particular script/pc location.
   1.895 +            ICEntry &icEntry = baselineScript->icEntryFromPCOffset(pcOff);
   1.896 +            ICFallbackStub *fallbackStub = icEntry.firstStub()->getChainFallback();
   1.897 +            if (fallbackStub->isMonitoredFallback())
   1.898 +                enterMonitorChain = true;
   1.899 +        }
   1.900 +
   1.901 +        uint32_t numCallArgs = isCall ? GET_ARGC(pc) : 0;
   1.902 +
   1.903 +        if (resumeAfter && !enterMonitorChain)
   1.904 +            pc = GetNextPc(pc);
   1.905 +
   1.906 +        builder.setResumeFramePtr(prevFramePtr);
   1.907 +
   1.908 +        if (enterMonitorChain) {
   1.909 +            ICEntry &icEntry = baselineScript->icEntryFromPCOffset(pcOff);
   1.910 +            ICFallbackStub *fallbackStub = icEntry.firstStub()->getChainFallback();
   1.911 +            JS_ASSERT(fallbackStub->isMonitoredFallback());
   1.912 +            IonSpew(IonSpew_BaselineBailouts, "      [TYPE-MONITOR CHAIN]");
   1.913 +            ICMonitoredFallbackStub *monFallbackStub = fallbackStub->toMonitoredFallbackStub();
   1.914 +            ICStub *firstMonStub = monFallbackStub->fallbackMonitorStub()->firstMonitorStub();
   1.915 +
   1.916 +            // To enter a monitoring chain, we load the top stack value into R0
   1.917 +            IonSpew(IonSpew_BaselineBailouts, "      Popping top stack value into R0.");
   1.918 +            builder.popValueInto(PCMappingSlotInfo::SlotInR0);
   1.919 +
   1.920 +            // Need to adjust the frameSize for the frame to match the values popped
   1.921 +            // into registers.
   1.922 +            frameSize -= sizeof(Value);
   1.923 +            blFrame->setFrameSize(frameSize);
   1.924 +            IonSpew(IonSpew_BaselineBailouts, "      Adjusted framesize -= %d: %d",
   1.925 +                            (int) sizeof(Value), (int) frameSize);
   1.926 +
   1.927 +            // If resuming into a JSOP_CALL, baseline keeps the arguments on the
   1.928 +            // stack and pops them only after returning from the call IC.
   1.929 +            // Push undefs onto the stack in anticipation of the popping of the
   1.930 +            // callee, thisv, and actual arguments passed from the caller's frame.
   1.931 +            if (isCall) {
   1.932 +                builder.writeValue(UndefinedValue(), "CallOp FillerCallee");
   1.933 +                builder.writeValue(UndefinedValue(), "CallOp FillerThis");
   1.934 +                for (uint32_t i = 0; i < numCallArgs; i++)
   1.935 +                    builder.writeValue(UndefinedValue(), "CallOp FillerArg");
   1.936 +
   1.937 +                frameSize += (numCallArgs + 2) * sizeof(Value);
   1.938 +                blFrame->setFrameSize(frameSize);
   1.939 +                IonSpew(IonSpew_BaselineBailouts, "      Adjusted framesize += %d: %d",
   1.940 +                                (int) ((numCallArgs + 2) * sizeof(Value)), (int) frameSize);
   1.941 +            }
   1.942 +
   1.943 +            // Set the resume address to the return point from the IC, and set
   1.944 +            // the monitor stub addr.
   1.945 +            builder.setResumeAddr(baselineScript->returnAddressForIC(icEntry));
   1.946 +            builder.setMonitorStub(firstMonStub);
   1.947 +            IonSpew(IonSpew_BaselineBailouts, "      Set resumeAddr=%p monitorStub=%p",
   1.948 +                    baselineScript->returnAddressForIC(icEntry), firstMonStub);
   1.949 +
   1.950 +        } else {
   1.951 +            // If needed, initialize BaselineBailoutInfo's valueR0 and/or valueR1 with the
   1.952 +            // top stack values.
   1.953 +            PCMappingSlotInfo slotInfo;
   1.954 +            uint8_t *nativeCodeForPC = baselineScript->nativeCodeForPC(script, pc, &slotInfo);
   1.955 +            unsigned numUnsynced = slotInfo.numUnsynced();
   1.956 +            JS_ASSERT(numUnsynced <= 2);
   1.957 +            PCMappingSlotInfo::SlotLocation loc1, loc2;
   1.958 +            if (numUnsynced > 0) {
   1.959 +                loc1 = slotInfo.topSlotLocation();
   1.960 +                IonSpew(IonSpew_BaselineBailouts, "      Popping top stack value into %d.",
   1.961 +                            (int) loc1);
   1.962 +                builder.popValueInto(loc1);
   1.963 +            }
   1.964 +            if (numUnsynced > 1) {
   1.965 +                loc2 = slotInfo.nextSlotLocation();
   1.966 +                IonSpew(IonSpew_BaselineBailouts, "      Popping next stack value into %d.",
   1.967 +                            (int) loc2);
   1.968 +                JS_ASSERT_IF(loc1 != PCMappingSlotInfo::SlotIgnore, loc1 != loc2);
   1.969 +                builder.popValueInto(loc2);
   1.970 +            }
   1.971 +
   1.972 +            // Need to adjust the frameSize for the frame to match the values popped
   1.973 +            // into registers.
   1.974 +            frameSize -= sizeof(Value) * numUnsynced;
   1.975 +            blFrame->setFrameSize(frameSize);
   1.976 +            IonSpew(IonSpew_BaselineBailouts, "      Adjusted framesize -= %d: %d",
   1.977 +                            int(sizeof(Value) * numUnsynced), int(frameSize));
   1.978 +
   1.979 +            // If scopeChain is nullptr, then bailout is occurring during argument check.
   1.980 +            // In this case, resume into the prologue.
   1.981 +            uint8_t *opReturnAddr;
   1.982 +            if (scopeChain == nullptr) {
   1.983 +                // Global and eval scripts expect the scope chain in R1, so only
   1.984 +                // resume into the prologue for function scripts.
   1.985 +                JS_ASSERT(fun);
   1.986 +                JS_ASSERT(numUnsynced == 0);
   1.987 +                opReturnAddr = baselineScript->prologueEntryAddr();
   1.988 +                IonSpew(IonSpew_BaselineBailouts, "      Resuming into prologue.");
   1.989 +
   1.990 +                // If bailing into prologue, HAS_PUSHED_SPS_FRAME should not be set on frame.
   1.991 +                blFrame->unsetPushedSPSFrame();
   1.992 +
   1.993 +                if (cx->runtime()->spsProfiler.enabled()) {
   1.994 +                    if (js_JitOptions.profileInlineFrames) {
   1.995 +                        // If SPS is enabled, there are two corner cases to handle:
   1.996 +                        //  1. If resuming into the prologue, and innermost frame is an inlined
   1.997 +                        //     frame, and bailout is because of argument check failure, then:
   1.998 +                        //          Top SPS profiler entry would be for caller frame.
   1.999 +                        //          Ion would not have set the PC index field on that frame
  1.1000 +                        //              (since this bailout happens before MFunctionBoundary).
  1.1001 +                        //          Make sure that's done now.
  1.1002 +                        //  2. If resuming into the prologue, and the bailout is NOT because of an
  1.1003 +                        //     argument check, then:
  1.1004 +                        //          Top SPS profiler entry would be for callee frame.
  1.1005 +                        //          Ion would already have pushed an SPS entry for this frame.
  1.1006 +                        //          The pc for this entry would be set to nullptr.
  1.1007 +                        //          Make sure it's set to script->pc.
  1.1008 +                        if (caller && bailoutKind == Bailout_ArgumentCheck) {
  1.1009 +                            IonSpew(IonSpew_BaselineBailouts, "      Setting PCidx on innermost "
  1.1010 +                                    "inlined frame's parent's SPS entry (%s:%d) (pcIdx=%d)!",
  1.1011 +                                    caller->filename(), caller->lineno(),
  1.1012 +                                    caller->pcToOffset(callerPC));
  1.1013 +                            cx->runtime()->spsProfiler.updatePC(caller, callerPC);
  1.1014 +
  1.1015 +                        } else if (bailoutKind != Bailout_ArgumentCheck) {
  1.1016 +                            IonSpew(IonSpew_BaselineBailouts,
  1.1017 +                                    "      Popping SPS entry for innermost inlined frame");
  1.1018 +                            cx->runtime()->spsProfiler.exit(script, fun);
  1.1019 +                        }
  1.1020 +
  1.1021 +                    } else {
  1.1022 +                        // If not profiling inline frames, then this is logically simpler.
  1.1023 +                        //
  1.1024 +                        // 1. If resuming into inline code, then the top SPS entry will be
  1.1025 +                        // for the outermost caller, and will have an uninitialized PC.
  1.1026 +                        // This will be fixed up later in BailoutIonToBaseline.
  1.1027 +                        //
  1.1028 +                        // 2. If resuming into top-level code prologue, with ArgumentCheck,
  1.1029 +                        // no SPS entry will have been pushed.  Can be left alone.
  1.1030 +                        //
  1.1031 +                        // 3. If resuming into top-level code prologue, without ArgumentCheck,
  1.1032 +                        // an SPS entry will have been pushed, and needs to be popped.
  1.1033 +                        //
  1.1034 +                        // 4. If resuming into top-level code main body, an SPS entry will
  1.1035 +                        // have been pushed, and can be left alone.
  1.1036 +                        //
  1.1037 +                        // Only need to handle case 3 here.
  1.1038 +                        if (!caller && bailoutKind != Bailout_ArgumentCheck) {
  1.1039 +                            IonSpew(IonSpew_BaselineBailouts,
  1.1040 +                                    "      Popping SPS entry for outermost frame");
  1.1041 +                            cx->runtime()->spsProfiler.exit(script, fun);
  1.1042 +                        }
  1.1043 +                    }
  1.1044 +                }
  1.1045 +            } else {
  1.1046 +                opReturnAddr = nativeCodeForPC;
  1.1047 +            }
  1.1048 +            builder.setResumeAddr(opReturnAddr);
  1.1049 +            IonSpew(IonSpew_BaselineBailouts, "      Set resumeAddr=%p", opReturnAddr);
  1.1050 +        }
  1.1051 +
  1.1052 +        if (cx->runtime()->spsProfiler.enabled()) {
  1.1053 +            if (blFrame->hasPushedSPSFrame()) {
  1.1054 +                // Set PC index to 0 for the innermost frame to match what the
  1.1055 +                // interpreter and Baseline do: they update the SPS pc for
  1.1056 +                // JSOP_CALL ops but set it to 0 when running other ops. Ion code
  1.1057 +                // can set the pc to NullPCIndex and this will confuse SPS when
  1.1058 +                // Baseline calls into the VM at non-CALL ops and re-enters JS.
  1.1059 +                IonSpew(IonSpew_BaselineBailouts, "      Setting PCidx for last frame to 0");
  1.1060 +                cx->runtime()->spsProfiler.updatePC(script, script->code());
  1.1061 +            }
  1.1062 +
  1.1063 +            // Register bailout with profiler.
  1.1064 +            const char *filename = script->filename();
  1.1065 +            if (filename == nullptr)
  1.1066 +                filename = "<unknown>";
  1.1067 +            unsigned len = strlen(filename) + 200;
  1.1068 +            char *buf = js_pod_malloc<char>(len);
  1.1069 +            if (buf == nullptr)
  1.1070 +                return false;
  1.1071 +            JS_snprintf(buf, len, "%s %s %s on line %d of %s:%d",
  1.1072 +                                  BailoutKindString(bailoutKind),
  1.1073 +                                  resumeAfter ? "after" : "at",
  1.1074 +                                  js_CodeName[op],
  1.1075 +                                  int(PCToLineNumber(script, pc)),
  1.1076 +                                  filename,
  1.1077 +                                  int(script->lineno()));
  1.1078 +            cx->runtime()->spsProfiler.markEvent(buf);
  1.1079 +            js_free(buf);
  1.1080 +        }
  1.1081 +
  1.1082 +        return true;
  1.1083 +    }
  1.1084 +
  1.1085 +    *callPC = pc;
  1.1086 +
  1.1087 +    // Write out descriptor of BaselineJS frame.
  1.1088 +    size_t baselineFrameDescr = MakeFrameDescriptor((uint32_t) builder.framePushed(),
  1.1089 +                                                    JitFrame_BaselineJS);
  1.1090 +    if (!builder.writeWord(baselineFrameDescr, "Descriptor"))
  1.1091 +        return false;
  1.1092 +
  1.1093 +    // Calculate and write out return address.
  1.1094 +    // The icEntry in question MUST have an inlinable fallback stub.
  1.1095 +    ICEntry &icEntry = baselineScript->icEntryFromPCOffset(pcOff);
  1.1096 +    JS_ASSERT(IsInlinableFallback(icEntry.firstStub()->getChainFallback()));
  1.1097 +    if (!builder.writePtr(baselineScript->returnAddressForIC(icEntry), "ReturnAddr"))
  1.1098 +        return false;
  1.1099 +
  1.1100 +    // Build baseline stub frame:
  1.1101 +    // +===============+
  1.1102 +    // |    StubPtr    |
  1.1103 +    // +---------------+
  1.1104 +    // |   FramePtr    |
  1.1105 +    // +---------------+
  1.1106 +    // |     ArgA      |
  1.1107 +    // +---------------+
  1.1108 +    // |     ...       |
  1.1109 +    // +---------------+
  1.1110 +    // |     Arg0      |
  1.1111 +    // +---------------+
  1.1112 +    // |     ThisV     |
  1.1113 +    // +---------------+
  1.1114 +    // |  ActualArgC   |
  1.1115 +    // +---------------+
  1.1116 +    // |  CalleeToken  |
  1.1117 +    // +---------------+
  1.1118 +    // | Descr(BLStub) |
  1.1119 +    // +---------------+
  1.1120 +    // |  ReturnAddr   |
  1.1121 +    // +===============+
  1.1122 +
  1.1123 +    IonSpew(IonSpew_BaselineBailouts, "      [BASELINE-STUB FRAME]");
  1.1124 +
  1.1125 +    size_t startOfBaselineStubFrame = builder.framePushed();
  1.1126 +
  1.1127 +    // Write stub pointer.
  1.1128 +    JS_ASSERT(IsInlinableFallback(icEntry.fallbackStub()));
  1.1129 +    if (!builder.writePtr(icEntry.fallbackStub(), "StubPtr"))
  1.1130 +        return false;
  1.1131 +
  1.1132 +    // Write previous frame pointer (saved earlier).
  1.1133 +    if (!builder.writePtr(prevFramePtr, "PrevFramePtr"))
  1.1134 +        return false;
  1.1135 +    prevFramePtr = builder.virtualPointerAtStackOffset(0);
  1.1136 +
  1.1137 +    // Write out actual arguments (and thisv), copied from unpacked stack of BaselineJS frame.
  1.1138 +    // Arguments are reversed on the BaselineJS frame's stack values.
  1.1139 +    JS_ASSERT(IsIonInlinablePC(pc));
  1.1140 +    unsigned actualArgc;
  1.1141 +    if (needToSaveArgs) {
  1.1142 +        // For FUNAPPLY or an accessor, the arguments are not on the stack anymore,
  1.1143 +        // but they are copied in a vector and are written here.
  1.1144 +        if (op == JSOP_FUNAPPLY)
  1.1145 +            actualArgc = blFrame->numActualArgs();
  1.1146 +        else
  1.1147 +            actualArgc = IsSetPropPC(pc);
  1.1148 +
  1.1149 +        JS_ASSERT(actualArgc + 2 <= exprStackSlots);
  1.1150 +        JS_ASSERT(savedCallerArgs.length() == actualArgc + 2);
  1.1151 +        for (unsigned i = 0; i < actualArgc + 1; i++) {
  1.1152 +            size_t arg = savedCallerArgs.length() - (i + 1);
  1.1153 +            if (!builder.writeValue(savedCallerArgs[arg], "ArgVal"))
  1.1154 +                return false;
  1.1155 +        }
  1.1156 +    } else {
  1.1157 +        actualArgc = GET_ARGC(pc);
  1.1158 +        if (op == JSOP_FUNCALL) {
  1.1159 +            JS_ASSERT(actualArgc > 0);
  1.1160 +            actualArgc--;
  1.1161 +        }
  1.1162 +
  1.1163 +        JS_ASSERT(actualArgc + 2 <= exprStackSlots);
  1.1164 +        for (unsigned i = 0; i < actualArgc + 1; i++) {
  1.1165 +            size_t argSlot = (script->nfixed() + exprStackSlots) - (i + 1);
  1.1166 +            if (!builder.writeValue(*blFrame->valueSlot(argSlot), "ArgVal"))
  1.1167 +                return false;
  1.1168 +        }
  1.1169 +    }
  1.1170 +
  1.1171 +    // In case these arguments need to be copied on the stack again for a rectifier frame,
  1.1172 +    // save the framePushed values here for later use.
  1.1173 +    size_t endOfBaselineStubArgs = builder.framePushed();
  1.1174 +
  1.1175 +    // Calculate frame size for descriptor.
  1.1176 +    size_t baselineStubFrameSize = builder.framePushed() - startOfBaselineStubFrame;
  1.1177 +    size_t baselineStubFrameDescr = MakeFrameDescriptor((uint32_t) baselineStubFrameSize,
  1.1178 +                                                        JitFrame_BaselineStub);
  1.1179 +
  1.1180 +    // Push actual argc
  1.1181 +    if (!builder.writeWord(actualArgc, "ActualArgc"))
  1.1182 +        return false;
  1.1183 +
  1.1184 +    // Push callee token (must be a JS Function)
  1.1185 +    Value callee;
  1.1186 +    if (needToSaveArgs) {
  1.1187 +        // The arguments of FUNAPPLY or inlined accessors are not writen to the stack.
  1.1188 +        // So get the callee from the specially saved vector.
  1.1189 +        callee = savedCallerArgs[0];
  1.1190 +    } else {
  1.1191 +        uint32_t calleeStackSlot = exprStackSlots - uint32_t(actualArgc + 2);
  1.1192 +        size_t calleeOffset = (builder.framePushed() - endOfBaselineJSFrameStack)
  1.1193 +            + ((exprStackSlots - (calleeStackSlot + 1)) * sizeof(Value));
  1.1194 +        callee = *builder.valuePointerAtStackOffset(calleeOffset);
  1.1195 +        IonSpew(IonSpew_BaselineBailouts, "      CalleeStackSlot=%d", (int) calleeStackSlot);
  1.1196 +    }
  1.1197 +    IonSpew(IonSpew_BaselineBailouts, "      Callee = %016llx", *((uint64_t *) &callee));
  1.1198 +    JS_ASSERT(callee.isObject() && callee.toObject().is<JSFunction>());
  1.1199 +    JSFunction *calleeFun = &callee.toObject().as<JSFunction>();
  1.1200 +    if (!builder.writePtr(CalleeToToken(calleeFun), "CalleeToken"))
  1.1201 +        return false;
  1.1202 +    nextCallee.set(calleeFun);
  1.1203 +
  1.1204 +    // Push BaselineStub frame descriptor
  1.1205 +    if (!builder.writeWord(baselineStubFrameDescr, "Descriptor"))
  1.1206 +        return false;
  1.1207 +
  1.1208 +    // Push return address into ICCall_Scripted stub, immediately after the call.
  1.1209 +    void *baselineCallReturnAddr = GetStubReturnAddress(cx, pc);
  1.1210 +    JS_ASSERT(baselineCallReturnAddr);
  1.1211 +    if (!builder.writePtr(baselineCallReturnAddr, "ReturnAddr"))
  1.1212 +        return false;
  1.1213 +
  1.1214 +    // If actualArgc >= fun->nargs, then we are done.  Otherwise, we need to push on
  1.1215 +    // a reconstructed rectifier frame.
  1.1216 +    if (actualArgc >= calleeFun->nargs())
  1.1217 +        return true;
  1.1218 +
  1.1219 +    // Push a reconstructed rectifier frame.
  1.1220 +    // +===============+
  1.1221 +    // |  UndefinedU   |
  1.1222 +    // +---------------+
  1.1223 +    // |     ...       |
  1.1224 +    // +---------------+
  1.1225 +    // |  Undefined0   |
  1.1226 +    // +---------------+
  1.1227 +    // |     ArgA      |
  1.1228 +    // +---------------+
  1.1229 +    // |     ...       |
  1.1230 +    // +---------------+
  1.1231 +    // |     Arg0      |
  1.1232 +    // +---------------+
  1.1233 +    // |     ThisV     |
  1.1234 +    // +---------------+
  1.1235 +    // |  ActualArgC   |
  1.1236 +    // +---------------+
  1.1237 +    // |  CalleeToken  |
  1.1238 +    // +---------------+
  1.1239 +    // |  Descr(Rect)  |
  1.1240 +    // +---------------+
  1.1241 +    // |  ReturnAddr   |
  1.1242 +    // +===============+
  1.1243 +
  1.1244 +    IonSpew(IonSpew_BaselineBailouts, "      [RECTIFIER FRAME]");
  1.1245 +
  1.1246 +    size_t startOfRectifierFrame = builder.framePushed();
  1.1247 +
  1.1248 +    // On x86-only, the frame pointer is saved again in the rectifier frame.
  1.1249 +#if defined(JS_CODEGEN_X86)
  1.1250 +    if (!builder.writePtr(prevFramePtr, "PrevFramePtr-X86Only"))
  1.1251 +        return false;
  1.1252 +#endif
  1.1253 +
  1.1254 +    // Push undefined for missing arguments.
  1.1255 +    for (unsigned i = 0; i < (calleeFun->nargs() - actualArgc); i++) {
  1.1256 +        if (!builder.writeValue(UndefinedValue(), "FillerVal"))
  1.1257 +            return false;
  1.1258 +    }
  1.1259 +
  1.1260 +    // Copy arguments + thisv from BaselineStub frame.
  1.1261 +    if (!builder.subtract((actualArgc + 1) * sizeof(Value), "CopiedArgs"))
  1.1262 +        return false;
  1.1263 +    BufferPointer<uint8_t> stubArgsEnd =
  1.1264 +        builder.pointerAtStackOffset<uint8_t>(builder.framePushed() - endOfBaselineStubArgs);
  1.1265 +    IonSpew(IonSpew_BaselineBailouts, "      MemCpy from %p", stubArgsEnd.get());
  1.1266 +    memcpy(builder.pointerAtStackOffset<uint8_t>(0).get(), stubArgsEnd.get(),
  1.1267 +           (actualArgc + 1) * sizeof(Value));
  1.1268 +
  1.1269 +    // Calculate frame size for descriptor.
  1.1270 +    size_t rectifierFrameSize = builder.framePushed() - startOfRectifierFrame;
  1.1271 +    size_t rectifierFrameDescr = MakeFrameDescriptor((uint32_t) rectifierFrameSize,
  1.1272 +                                                     JitFrame_Rectifier);
  1.1273 +
  1.1274 +    // Push actualArgc
  1.1275 +    if (!builder.writeWord(actualArgc, "ActualArgc"))
  1.1276 +        return false;
  1.1277 +
  1.1278 +    // Push calleeToken again.
  1.1279 +    if (!builder.writePtr(CalleeToToken(calleeFun), "CalleeToken"))
  1.1280 +        return false;
  1.1281 +
  1.1282 +    // Push rectifier frame descriptor
  1.1283 +    if (!builder.writeWord(rectifierFrameDescr, "Descriptor"))
  1.1284 +        return false;
  1.1285 +
  1.1286 +    // Push return address into the ArgumentsRectifier code, immediately after the ioncode
  1.1287 +    // call.
  1.1288 +    void *rectReturnAddr = cx->runtime()->jitRuntime()->getArgumentsRectifierReturnAddr();
  1.1289 +    JS_ASSERT(rectReturnAddr);
  1.1290 +    if (!builder.writePtr(rectReturnAddr, "ReturnAddr"))
  1.1291 +        return false;
  1.1292 +
  1.1293 +    return true;
  1.1294 +}
  1.1295 +
  1.1296 +uint32_t
  1.1297 +jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIterator &iter,
  1.1298 +                          bool invalidate, BaselineBailoutInfo **bailoutInfo,
  1.1299 +                          const ExceptionBailoutInfo *excInfo)
  1.1300 +{
  1.1301 +    JS_ASSERT(bailoutInfo != nullptr);
  1.1302 +    JS_ASSERT(*bailoutInfo == nullptr);
  1.1303 +
  1.1304 +    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
  1.1305 +    TraceLogStopEvent(logger, TraceLogger::IonMonkey);
  1.1306 +    TraceLogStartEvent(logger, TraceLogger::Baseline);
  1.1307 +
  1.1308 +    // The caller of the top frame must be one of the following:
  1.1309 +    //      IonJS - Ion calling into Ion.
  1.1310 +    //      BaselineStub - Baseline calling into Ion.
  1.1311 +    //      Entry - Interpreter or other calling into Ion.
  1.1312 +    //      Rectifier - Arguments rectifier calling into Ion.
  1.1313 +    JS_ASSERT(iter.isIonJS());
  1.1314 +    FrameType prevFrameType = iter.prevType();
  1.1315 +    JS_ASSERT(prevFrameType == JitFrame_IonJS ||
  1.1316 +              prevFrameType == JitFrame_BaselineStub ||
  1.1317 +              prevFrameType == JitFrame_Entry ||
  1.1318 +              prevFrameType == JitFrame_Rectifier);
  1.1319 +
  1.1320 +    // All incoming frames are going to look like this:
  1.1321 +    //
  1.1322 +    //      +---------------+
  1.1323 +    //      |     ...       |
  1.1324 +    //      +---------------+
  1.1325 +    //      |     Args      |
  1.1326 +    //      |     ...       |
  1.1327 +    //      +---------------+
  1.1328 +    //      |    ThisV      |
  1.1329 +    //      +---------------+
  1.1330 +    //      |  ActualArgC   |
  1.1331 +    //      +---------------+
  1.1332 +    //      |  CalleeToken  |
  1.1333 +    //      +---------------+
  1.1334 +    //      |  Descriptor   |
  1.1335 +    //      +---------------+
  1.1336 +    //      |  ReturnAddr   |
  1.1337 +    //      +---------------+
  1.1338 +    //      |    |||||      | <---- Overwrite starting here.
  1.1339 +    //      |    |||||      |
  1.1340 +    //      |    |||||      |
  1.1341 +    //      +---------------+
  1.1342 +
  1.1343 +    IonSpew(IonSpew_BaselineBailouts, "Bailing to baseline %s:%u (IonScript=%p) (FrameType=%d)",
  1.1344 +            iter.script()->filename(), iter.script()->lineno(), (void *) iter.ionScript(),
  1.1345 +            (int) prevFrameType);
  1.1346 +
  1.1347 +    bool catchingException;
  1.1348 +    bool propagatingExceptionForDebugMode;
  1.1349 +    if (excInfo) {
  1.1350 +        catchingException = excInfo->catchingException();
  1.1351 +        propagatingExceptionForDebugMode = excInfo->propagatingIonExceptionForDebugMode();
  1.1352 +
  1.1353 +        if (catchingException)
  1.1354 +            IonSpew(IonSpew_BaselineBailouts, "Resuming in catch or finally block");
  1.1355 +
  1.1356 +        if (propagatingExceptionForDebugMode)
  1.1357 +            IonSpew(IonSpew_BaselineBailouts, "Resuming in-place for debug mode");
  1.1358 +    } else {
  1.1359 +        catchingException = false;
  1.1360 +        propagatingExceptionForDebugMode = false;
  1.1361 +    }
  1.1362 +
  1.1363 +    IonSpew(IonSpew_BaselineBailouts, "  Reading from snapshot offset %u size %u",
  1.1364 +            iter.snapshotOffset(), iter.ionScript()->snapshotsListSize());
  1.1365 +
  1.1366 +    if (!excInfo)
  1.1367 +        iter.ionScript()->incNumBailouts();
  1.1368 +    iter.script()->updateBaselineOrIonRaw();
  1.1369 +
  1.1370 +    // Allocate buffer to hold stack replacement data.
  1.1371 +    BaselineStackBuilder builder(iter, 1024);
  1.1372 +    if (!builder.init())
  1.1373 +        return BAILOUT_RETURN_FATAL_ERROR;
  1.1374 +    IonSpew(IonSpew_BaselineBailouts, "  Incoming frame ptr = %p", builder.startFrame());
  1.1375 +
  1.1376 +    SnapshotIterator snapIter(iter);
  1.1377 +
  1.1378 +    RootedFunction callee(cx, iter.maybeCallee());
  1.1379 +    RootedScript scr(cx, iter.script());
  1.1380 +    if (callee) {
  1.1381 +        IonSpew(IonSpew_BaselineBailouts, "  Callee function (%s:%u)",
  1.1382 +                scr->filename(), scr->lineno());
  1.1383 +    } else {
  1.1384 +        IonSpew(IonSpew_BaselineBailouts, "  No callee!");
  1.1385 +    }
  1.1386 +
  1.1387 +    if (iter.isConstructing())
  1.1388 +        IonSpew(IonSpew_BaselineBailouts, "  Constructing!");
  1.1389 +    else
  1.1390 +        IonSpew(IonSpew_BaselineBailouts, "  Not constructing!");
  1.1391 +
  1.1392 +    IonSpew(IonSpew_BaselineBailouts, "  Restoring frames:");
  1.1393 +    size_t frameNo = 0;
  1.1394 +
  1.1395 +    // Reconstruct baseline frames using the builder.
  1.1396 +    RootedScript caller(cx);
  1.1397 +    jsbytecode *callerPC = nullptr;
  1.1398 +    RootedFunction fun(cx, callee);
  1.1399 +    AutoValueVector startFrameFormals(cx);
  1.1400 +
  1.1401 +    RootedScript topCaller(cx);
  1.1402 +    jsbytecode *topCallerPC = nullptr;
  1.1403 +
  1.1404 +    while (true) {
  1.1405 +        MOZ_ASSERT(snapIter.instruction()->isResumePoint());
  1.1406 +
  1.1407 +        if (frameNo > 0) {
  1.1408 +            TraceLogStartEvent(logger, TraceLogCreateTextId(logger, scr));
  1.1409 +            TraceLogStartEvent(logger, TraceLogger::Baseline);
  1.1410 +        }
  1.1411 +
  1.1412 +        IonSpew(IonSpew_BaselineBailouts, "    FrameNo %d", frameNo);
  1.1413 +
  1.1414 +        // If we are bailing out to a catch or finally block in this frame,
  1.1415 +        // pass excInfo to InitFromBailout and don't unpack any other frames.
  1.1416 +        bool handleException = (catchingException && excInfo->frameNo() == frameNo);
  1.1417 +
  1.1418 +        // We also need to pass excInfo if we're bailing out in place for
  1.1419 +        // debug mode.
  1.1420 +        bool passExcInfo = handleException || propagatingExceptionForDebugMode;
  1.1421 +
  1.1422 +        jsbytecode *callPC = nullptr;
  1.1423 +        RootedFunction nextCallee(cx, nullptr);
  1.1424 +        if (!InitFromBailout(cx, caller, callerPC, fun, scr, iter.ionScript(),
  1.1425 +                             snapIter, invalidate, builder, startFrameFormals,
  1.1426 +                             &nextCallee, &callPC, passExcInfo ? excInfo : nullptr))
  1.1427 +        {
  1.1428 +            return BAILOUT_RETURN_FATAL_ERROR;
  1.1429 +        }
  1.1430 +
  1.1431 +        if (!snapIter.moreFrames()) {
  1.1432 +            JS_ASSERT(!callPC);
  1.1433 +            break;
  1.1434 +        }
  1.1435 +
  1.1436 +        if (handleException)
  1.1437 +            break;
  1.1438 +
  1.1439 +        JS_ASSERT(nextCallee);
  1.1440 +        JS_ASSERT(callPC);
  1.1441 +        caller = scr;
  1.1442 +        callerPC = callPC;
  1.1443 +        fun = nextCallee;
  1.1444 +        scr = fun->existingScriptForInlinedFunction();
  1.1445 +
  1.1446 +        // Save top caller info for adjusting SPS frames later.
  1.1447 +        if (!topCaller) {
  1.1448 +            JS_ASSERT(frameNo == 0);
  1.1449 +            topCaller = caller;
  1.1450 +            topCallerPC = callerPC;
  1.1451 +        }
  1.1452 +
  1.1453 +        frameNo++;
  1.1454 +
  1.1455 +        snapIter.nextInstruction();
  1.1456 +    }
  1.1457 +    IonSpew(IonSpew_BaselineBailouts, "  Done restoring frames");
  1.1458 +
  1.1459 +    // If there were multiple inline frames unpacked, and inline frame profiling
  1.1460 +    // is off, then the current top SPS frame is for the outermost caller, and
  1.1461 +    // has an uninitialized PC.  Initialize it now.
  1.1462 +    if (frameNo > 0 && !js_JitOptions.profileInlineFrames)
  1.1463 +        cx->runtime()->spsProfiler.updatePC(topCaller, topCallerPC);
  1.1464 +
  1.1465 +    BailoutKind bailoutKind = snapIter.bailoutKind();
  1.1466 +
  1.1467 +    if (!startFrameFormals.empty()) {
  1.1468 +        // Set the first frame's formals, see the comment in InitFromBailout.
  1.1469 +        Value *argv = builder.startFrame()->argv() + 1; // +1 to skip |this|.
  1.1470 +        mozilla::PodCopy(argv, startFrameFormals.begin(), startFrameFormals.length());
  1.1471 +    }
  1.1472 +
  1.1473 +    // Do stack check.
  1.1474 +    bool overRecursed = false;
  1.1475 +    BaselineBailoutInfo *info = builder.info();
  1.1476 +    uint8_t *newsp = info->incomingStack - (info->copyStackTop - info->copyStackBottom);
  1.1477 +#ifdef JS_ARM_SIMULATOR
  1.1478 +    if (Simulator::Current()->overRecursed(uintptr_t(newsp)))
  1.1479 +        overRecursed = true;
  1.1480 +#else
  1.1481 +    JS_CHECK_RECURSION_WITH_SP_DONT_REPORT(cx, newsp, overRecursed = true);
  1.1482 +#endif
  1.1483 +    if (overRecursed) {
  1.1484 +        IonSpew(IonSpew_BaselineBailouts, "  Overrecursion check failed!");
  1.1485 +        return BAILOUT_RETURN_OVERRECURSED;
  1.1486 +    }
  1.1487 +
  1.1488 +    // Take the reconstructed baseline stack so it doesn't get freed when builder destructs.
  1.1489 +    info = builder.takeBuffer();
  1.1490 +    info->numFrames = frameNo + 1;
  1.1491 +    info->bailoutKind = bailoutKind;
  1.1492 +    *bailoutInfo = info;
  1.1493 +    return BAILOUT_RETURN_OK;
  1.1494 +}
  1.1495 +
  1.1496 +static bool
  1.1497 +HandleBoundsCheckFailure(JSContext *cx, HandleScript outerScript, HandleScript innerScript)
  1.1498 +{
  1.1499 +    IonSpew(IonSpew_Bailouts, "Bounds check failure %s:%d, inlined into %s:%d",
  1.1500 +            innerScript->filename(), innerScript->lineno(),
  1.1501 +            outerScript->filename(), outerScript->lineno());
  1.1502 +
  1.1503 +    JS_ASSERT(!outerScript->ionScript()->invalidated());
  1.1504 +
  1.1505 +    // TODO: Currently this mimic's Ion's handling of this case.  Investigate setting
  1.1506 +    // the flag on innerScript as opposed to outerScript, and maybe invalidating both
  1.1507 +    // inner and outer scripts, instead of just the outer one.
  1.1508 +    if (!outerScript->failedBoundsCheck())
  1.1509 +        outerScript->setFailedBoundsCheck();
  1.1510 +    IonSpew(IonSpew_BaselineBailouts, "Invalidating due to bounds check failure");
  1.1511 +    return Invalidate(cx, outerScript);
  1.1512 +}
  1.1513 +
  1.1514 +static bool
  1.1515 +HandleShapeGuardFailure(JSContext *cx, HandleScript outerScript, HandleScript innerScript)
  1.1516 +{
  1.1517 +    IonSpew(IonSpew_Bailouts, "Shape guard failure %s:%d, inlined into %s:%d",
  1.1518 +            innerScript->filename(), innerScript->lineno(),
  1.1519 +            outerScript->filename(), outerScript->lineno());
  1.1520 +
  1.1521 +    JS_ASSERT(!outerScript->ionScript()->invalidated());
  1.1522 +
  1.1523 +    // TODO: Currently this mimic's Ion's handling of this case.  Investigate setting
  1.1524 +    // the flag on innerScript as opposed to outerScript, and maybe invalidating both
  1.1525 +    // inner and outer scripts, instead of just the outer one.
  1.1526 +    outerScript->setFailedShapeGuard();
  1.1527 +    IonSpew(IonSpew_BaselineBailouts, "Invalidating due to shape guard failure");
  1.1528 +    return Invalidate(cx, outerScript);
  1.1529 +}
  1.1530 +
  1.1531 +static bool
  1.1532 +HandleBaselineInfoBailout(JSContext *cx, JSScript *outerScript, JSScript *innerScript)
  1.1533 +{
  1.1534 +    IonSpew(IonSpew_Bailouts, "Baseline info failure %s:%d, inlined into %s:%d",
  1.1535 +            innerScript->filename(), innerScript->lineno(),
  1.1536 +            outerScript->filename(), outerScript->lineno());
  1.1537 +
  1.1538 +    JS_ASSERT(!outerScript->ionScript()->invalidated());
  1.1539 +
  1.1540 +    IonSpew(IonSpew_BaselineBailouts, "Invalidating due to invalid baseline info");
  1.1541 +    return Invalidate(cx, outerScript);
  1.1542 +}
  1.1543 +
  1.1544 +static bool
  1.1545 +CopyFromRematerializedFrame(JSContext *cx, JitActivation *act, uint8_t *fp, size_t inlineDepth,
  1.1546 +                            BaselineFrame *frame)
  1.1547 +{
  1.1548 +    RematerializedFrame *rematFrame = act->lookupRematerializedFrame(fp, inlineDepth);
  1.1549 +
  1.1550 +    // We might not have rematerialized a frame if the user never requested a
  1.1551 +    // Debugger.Frame for it.
  1.1552 +    if (!rematFrame)
  1.1553 +        return true;
  1.1554 +
  1.1555 +    MOZ_ASSERT(rematFrame->script() == frame->script());
  1.1556 +    MOZ_ASSERT(rematFrame->numActualArgs() == frame->numActualArgs());
  1.1557 +
  1.1558 +    frame->setScopeChain(rematFrame->scopeChain());
  1.1559 +    frame->thisValue() = rematFrame->thisValue();
  1.1560 +
  1.1561 +    for (unsigned i = 0; i < frame->numActualArgs(); i++)
  1.1562 +        frame->argv()[i] = rematFrame->argv()[i];
  1.1563 +
  1.1564 +    for (size_t i = 0; i < frame->script()->nfixed(); i++)
  1.1565 +        *frame->valueSlot(i) = rematFrame->locals()[i];
  1.1566 +
  1.1567 +    IonSpew(IonSpew_BaselineBailouts,
  1.1568 +            "  Copied from rematerialized frame at (%p,%u)",
  1.1569 +            fp, inlineDepth);
  1.1570 +
  1.1571 +    if (cx->compartment()->debugMode())
  1.1572 +        return Debugger::handleIonBailout(cx, rematFrame, frame);
  1.1573 +
  1.1574 +    return true;
  1.1575 +}
  1.1576 +
  1.1577 +uint32_t
  1.1578 +jit::FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo)
  1.1579 +{
  1.1580 +    // The caller pushes R0 and R1 on the stack without rooting them.
  1.1581 +    // Since GC here is very unlikely just suppress it.
  1.1582 +    JSContext *cx = GetJSContextFromJitCode();
  1.1583 +    js::gc::AutoSuppressGC suppressGC(cx);
  1.1584 +
  1.1585 +    IonSpew(IonSpew_BaselineBailouts, "  Done restoring frames");
  1.1586 +
  1.1587 +    // Check that we can get the current script's PC.
  1.1588 +#ifdef DEBUG
  1.1589 +    jsbytecode *pc;
  1.1590 +    cx->currentScript(&pc);
  1.1591 +    IonSpew(IonSpew_BaselineBailouts, "  Got pc=%p", pc);
  1.1592 +#endif
  1.1593 +
  1.1594 +    uint32_t numFrames = bailoutInfo->numFrames;
  1.1595 +    JS_ASSERT(numFrames > 0);
  1.1596 +    BailoutKind bailoutKind = bailoutInfo->bailoutKind;
  1.1597 +
  1.1598 +    // Free the bailout buffer.
  1.1599 +    js_free(bailoutInfo);
  1.1600 +    bailoutInfo = nullptr;
  1.1601 +
  1.1602 +    // Ensure the frame has a call object if it needs one. If the scope chain
  1.1603 +    // is nullptr, we will enter baseline code at the prologue so no need to do
  1.1604 +    // anything in that case.
  1.1605 +    BaselineFrame *topFrame = GetTopBaselineFrame(cx);
  1.1606 +    if (topFrame->scopeChain() && !EnsureHasScopeObjects(cx, topFrame))
  1.1607 +        return false;
  1.1608 +
  1.1609 +    // Create arguments objects for bailed out frames, to maintain the invariant
  1.1610 +    // that script->needsArgsObj() implies frame->hasArgsObj().
  1.1611 +    RootedScript innerScript(cx, nullptr);
  1.1612 +    RootedScript outerScript(cx, nullptr);
  1.1613 +
  1.1614 +    JS_ASSERT(cx->currentlyRunningInJit());
  1.1615 +    JitFrameIterator iter(cx);
  1.1616 +    uint8_t *outerFp = nullptr;
  1.1617 +
  1.1618 +    uint32_t frameno = 0;
  1.1619 +    while (frameno < numFrames) {
  1.1620 +        JS_ASSERT(!iter.isIonJS());
  1.1621 +
  1.1622 +        if (iter.isBaselineJS()) {
  1.1623 +            BaselineFrame *frame = iter.baselineFrame();
  1.1624 +            MOZ_ASSERT(frame->script()->hasBaselineScript());
  1.1625 +
  1.1626 +            // If the frame doesn't even have a scope chain set yet, then it's resuming
  1.1627 +            // into the the prologue before the scope chain is initialized.  Any
  1.1628 +            // necessary args object will also be initialized there.
  1.1629 +            if (frame->scopeChain() && frame->script()->needsArgsObj()) {
  1.1630 +                ArgumentsObject *argsObj;
  1.1631 +                if (frame->hasArgsObj()) {
  1.1632 +                    argsObj = &frame->argsObj();
  1.1633 +                } else {
  1.1634 +                    argsObj = ArgumentsObject::createExpected(cx, frame);
  1.1635 +                    if (!argsObj)
  1.1636 +                        return false;
  1.1637 +                }
  1.1638 +
  1.1639 +                // The arguments is a local binding and needsArgsObj does not
  1.1640 +                // check if it is clobbered. Ensure that the local binding
  1.1641 +                // restored during bailout before storing the arguments object
  1.1642 +                // to the slot.
  1.1643 +                RootedScript script(cx, frame->script());
  1.1644 +                SetFrameArgumentsObject(cx, frame, script, argsObj);
  1.1645 +            }
  1.1646 +
  1.1647 +            if (frameno == 0)
  1.1648 +                innerScript = frame->script();
  1.1649 +
  1.1650 +            if (frameno == numFrames - 1) {
  1.1651 +                outerScript = frame->script();
  1.1652 +                outerFp = iter.fp();
  1.1653 +            }
  1.1654 +
  1.1655 +            frameno++;
  1.1656 +        }
  1.1657 +
  1.1658 +        ++iter;
  1.1659 +    }
  1.1660 +
  1.1661 +    MOZ_ASSERT(innerScript);
  1.1662 +    MOZ_ASSERT(outerScript);
  1.1663 +    MOZ_ASSERT(outerFp);
  1.1664 +
  1.1665 +    // If we rematerialized Ion frames due to debug mode toggling, copy their
  1.1666 +    // values into the baseline frame. We need to do this even when debug mode
  1.1667 +    // is off, as we should respect the mutations made while debug mode was
  1.1668 +    // on.
  1.1669 +    JitActivation *act = cx->mainThread().activation()->asJit();
  1.1670 +    if (act->hasRematerializedFrame(outerFp)) {
  1.1671 +        JitFrameIterator iter(cx);
  1.1672 +        size_t inlineDepth = numFrames;
  1.1673 +        while (inlineDepth > 0) {
  1.1674 +            if (iter.isBaselineJS() &&
  1.1675 +                !CopyFromRematerializedFrame(cx, act, outerFp, --inlineDepth,
  1.1676 +                                             iter.baselineFrame()))
  1.1677 +            {
  1.1678 +                return false;
  1.1679 +            }
  1.1680 +            ++iter;
  1.1681 +        }
  1.1682 +
  1.1683 +        // After copying from all the rematerialized frames, remove them from
  1.1684 +        // the table to keep the table up to date.
  1.1685 +        act->removeRematerializedFrame(outerFp);
  1.1686 +    }
  1.1687 +
  1.1688 +    IonSpew(IonSpew_BaselineBailouts,
  1.1689 +            "  Restored outerScript=(%s:%u,%u) innerScript=(%s:%u,%u) (bailoutKind=%u)",
  1.1690 +            outerScript->filename(), outerScript->lineno(), outerScript->getUseCount(),
  1.1691 +            innerScript->filename(), innerScript->lineno(), innerScript->getUseCount(),
  1.1692 +            (unsigned) bailoutKind);
  1.1693 +
  1.1694 +    switch (bailoutKind) {
  1.1695 +      case Bailout_Normal:
  1.1696 +        // Do nothing.
  1.1697 +        break;
  1.1698 +      case Bailout_ArgumentCheck:
  1.1699 +        // Do nothing, bailout will resume before the argument monitor ICs.
  1.1700 +        break;
  1.1701 +      case Bailout_BoundsCheck:
  1.1702 +        if (!HandleBoundsCheckFailure(cx, outerScript, innerScript))
  1.1703 +            return false;
  1.1704 +        break;
  1.1705 +      case Bailout_ShapeGuard:
  1.1706 +        if (!HandleShapeGuardFailure(cx, outerScript, innerScript))
  1.1707 +            return false;
  1.1708 +        break;
  1.1709 +      case Bailout_BaselineInfo:
  1.1710 +        if (!HandleBaselineInfoBailout(cx, outerScript, innerScript))
  1.1711 +            return false;
  1.1712 +        break;
  1.1713 +      case Bailout_IonExceptionDebugMode:
  1.1714 +        // Return false to resume in HandleException with reconstructed
  1.1715 +        // baseline frame.
  1.1716 +        return false;
  1.1717 +      default:
  1.1718 +        MOZ_ASSUME_UNREACHABLE("Unknown bailout kind!");
  1.1719 +    }
  1.1720 +
  1.1721 +    if (!CheckFrequentBailouts(cx, outerScript))
  1.1722 +        return false;
  1.1723 +
  1.1724 +    return true;
  1.1725 +}

mercurial