Coverage Report

Created: 2018-09-25 14:53

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