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