Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
7 #include "jsprf.h"
8 #include "jit/arm/Simulator-arm.h"
9 #include "jit/BaselineIC.h"
10 #include "jit/BaselineJIT.h"
11 #include "jit/CompileInfo.h"
12 #include "jit/IonSpewer.h"
13 #include "jit/Recover.h"
14 #include "jit/RematerializedFrame.h"
16 #include "vm/ArgumentsObject.h"
17 #include "vm/Debugger.h"
18 #include "vm/TraceLogging.h"
20 #include "jsscriptinlines.h"
22 #include "jit/IonFrames-inl.h"
24 using namespace js;
25 using namespace js::jit;
27 // BaselineStackBuilder may reallocate its buffer if the current one is too
28 // small. To avoid dangling pointers, BufferPointer represents a pointer into
29 // this buffer as a pointer to the header and a fixed offset.
30 template <typename T>
31 class BufferPointer
32 {
33 BaselineBailoutInfo **header_;
34 size_t offset_;
35 bool heap_;
37 public:
38 BufferPointer(BaselineBailoutInfo **header, size_t offset, bool heap)
39 : header_(header), offset_(offset), heap_(heap)
40 { }
42 T *get() const {
43 BaselineBailoutInfo *header = *header_;
44 if (!heap_)
45 return (T*)(header->incomingStack + offset_);
47 uint8_t *p = header->copyStackTop - offset_;
48 JS_ASSERT(p >= header->copyStackBottom && p < header->copyStackTop);
49 return (T*)p;
50 }
52 T &operator*() const { return *get(); }
53 T *operator->() const { return get(); }
54 };
56 /**
57 * BaselineStackBuilder helps abstract the process of rebuilding the C stack on the heap.
58 * It takes a bailout iterator and keeps track of the point on the C stack from which
59 * the reconstructed frames will be written.
60 *
61 * It exposes methods to write data into the heap memory storing the reconstructed
62 * stack. It also exposes method to easily calculate addresses. This includes both the
63 * virtual address that a particular value will be at when it's eventually copied onto
64 * the stack, as well as the current actual address of that value (whether on the heap
65 * allocated portion being constructed or the existing stack).
66 *
67 * The abstraction handles transparent re-allocation of the heap memory when it
68 * needs to be enlarged to accomodate new data. Similarly to the C stack, the
69 * data that's written to the reconstructed stack grows from high to low in memory.
70 *
71 * The lowest region of the allocated memory contains a BaselineBailoutInfo structure that
72 * points to the start and end of the written data.
73 */
74 struct BaselineStackBuilder
75 {
76 IonBailoutIterator &iter_;
77 IonJSFrameLayout *frame_;
79 static size_t HeaderSize() {
80 return AlignBytes(sizeof(BaselineBailoutInfo), sizeof(void *));
81 };
82 size_t bufferTotal_;
83 size_t bufferAvail_;
84 size_t bufferUsed_;
85 uint8_t *buffer_;
86 BaselineBailoutInfo *header_;
88 size_t framePushed_;
90 BaselineStackBuilder(IonBailoutIterator &iter, size_t initialSize)
91 : iter_(iter),
92 frame_(static_cast<IonJSFrameLayout*>(iter.current())),
93 bufferTotal_(initialSize),
94 bufferAvail_(0),
95 bufferUsed_(0),
96 buffer_(nullptr),
97 header_(nullptr),
98 framePushed_(0)
99 {
100 JS_ASSERT(bufferTotal_ >= HeaderSize());
101 }
103 ~BaselineStackBuilder() {
104 js_free(buffer_);
105 }
107 bool init() {
108 JS_ASSERT(!buffer_);
109 JS_ASSERT(bufferUsed_ == 0);
110 buffer_ = reinterpret_cast<uint8_t *>(js_calloc(bufferTotal_));
111 if (!buffer_)
112 return false;
113 bufferAvail_ = bufferTotal_ - HeaderSize();
114 bufferUsed_ = 0;
116 header_ = reinterpret_cast<BaselineBailoutInfo *>(buffer_);
117 header_->incomingStack = reinterpret_cast<uint8_t *>(frame_);
118 header_->copyStackTop = buffer_ + bufferTotal_;
119 header_->copyStackBottom = header_->copyStackTop;
120 header_->setR0 = 0;
121 header_->valueR0 = UndefinedValue();
122 header_->setR1 = 0;
123 header_->valueR1 = UndefinedValue();
124 header_->resumeFramePtr = nullptr;
125 header_->resumeAddr = nullptr;
126 header_->monitorStub = nullptr;
127 header_->numFrames = 0;
128 return true;
129 }
131 bool enlarge() {
132 JS_ASSERT(buffer_ != nullptr);
133 if (bufferTotal_ & mozilla::tl::MulOverflowMask<2>::value)
134 return false;
135 size_t newSize = bufferTotal_ * 2;
136 uint8_t *newBuffer = reinterpret_cast<uint8_t *>(js_calloc(newSize));
137 if (!newBuffer)
138 return false;
139 memcpy((newBuffer + newSize) - bufferUsed_, header_->copyStackBottom, bufferUsed_);
140 memcpy(newBuffer, header_, sizeof(BaselineBailoutInfo));
141 js_free(buffer_);
142 buffer_ = newBuffer;
143 bufferTotal_ = newSize;
144 bufferAvail_ = newSize - (HeaderSize() + bufferUsed_);
146 header_ = reinterpret_cast<BaselineBailoutInfo *>(buffer_);
147 header_->copyStackTop = buffer_ + bufferTotal_;
148 header_->copyStackBottom = header_->copyStackTop - bufferUsed_;
149 return true;
150 }
152 BaselineBailoutInfo *info() {
153 JS_ASSERT(header_ == reinterpret_cast<BaselineBailoutInfo *>(buffer_));
154 return header_;
155 }
157 BaselineBailoutInfo *takeBuffer() {
158 JS_ASSERT(header_ == reinterpret_cast<BaselineBailoutInfo *>(buffer_));
159 buffer_ = nullptr;
160 return header_;
161 }
163 void resetFramePushed() {
164 framePushed_ = 0;
165 }
167 size_t framePushed() const {
168 return framePushed_;
169 }
171 bool subtract(size_t size, const char *info = nullptr) {
172 // enlarge the buffer if need be.
173 while (size > bufferAvail_) {
174 if (!enlarge())
175 return false;
176 }
178 // write out element.
179 header_->copyStackBottom -= size;
180 bufferAvail_ -= size;
181 bufferUsed_ += size;
182 framePushed_ += size;
183 if (info) {
184 IonSpew(IonSpew_BaselineBailouts,
185 " SUB_%03d %p/%p %-15s",
186 (int) size, header_->copyStackBottom, virtualPointerAtStackOffset(0), info);
187 }
188 return true;
189 }
191 template <typename T>
192 bool write(const T &t) {
193 if (!subtract(sizeof(T)))
194 return false;
195 memcpy(header_->copyStackBottom, &t, sizeof(T));
196 return true;
197 }
199 template <typename T>
200 bool writePtr(T *t, const char *info) {
201 if (!write<T *>(t))
202 return false;
203 if (info)
204 IonSpew(IonSpew_BaselineBailouts,
205 " WRITE_PTR %p/%p %-15s %p",
206 header_->copyStackBottom, virtualPointerAtStackOffset(0), info, t);
207 return true;
208 }
210 bool writeWord(size_t w, const char *info) {
211 if (!write<size_t>(w))
212 return false;
213 if (info) {
214 if (sizeof(size_t) == 4) {
215 IonSpew(IonSpew_BaselineBailouts,
216 " WRITE_WRD %p/%p %-15s %08x",
217 header_->copyStackBottom, virtualPointerAtStackOffset(0), info, w);
218 } else {
219 IonSpew(IonSpew_BaselineBailouts,
220 " WRITE_WRD %p/%p %-15s %016llx",
221 header_->copyStackBottom, virtualPointerAtStackOffset(0), info, w);
222 }
223 }
224 return true;
225 }
227 bool writeValue(Value val, const char *info) {
228 if (!write<Value>(val))
229 return false;
230 if (info) {
231 IonSpew(IonSpew_BaselineBailouts,
232 " WRITE_VAL %p/%p %-15s %016llx",
233 header_->copyStackBottom, virtualPointerAtStackOffset(0), info,
234 *((uint64_t *) &val));
235 }
236 return true;
237 }
239 Value popValue() {
240 JS_ASSERT(bufferUsed_ >= sizeof(Value));
241 JS_ASSERT(framePushed_ >= sizeof(Value));
242 bufferAvail_ += sizeof(Value);
243 bufferUsed_ -= sizeof(Value);
244 framePushed_ -= sizeof(Value);
245 Value result = *((Value *) header_->copyStackBottom);
246 header_->copyStackBottom += sizeof(Value);
247 return result;
248 }
250 void popValueInto(PCMappingSlotInfo::SlotLocation loc) {
251 JS_ASSERT(PCMappingSlotInfo::ValidSlotLocation(loc));
252 switch(loc) {
253 case PCMappingSlotInfo::SlotInR0:
254 header_->setR0 = 1;
255 header_->valueR0 = popValue();
256 break;
257 case PCMappingSlotInfo::SlotInR1:
258 header_->setR1 = 1;
259 header_->valueR1 = popValue();
260 break;
261 default:
262 JS_ASSERT(loc == PCMappingSlotInfo::SlotIgnore);
263 popValue();
264 break;
265 }
266 }
268 void setResumeFramePtr(void *resumeFramePtr) {
269 header_->resumeFramePtr = resumeFramePtr;
270 }
272 void setResumeAddr(void *resumeAddr) {
273 header_->resumeAddr = resumeAddr;
274 }
276 void setMonitorStub(ICStub *stub) {
277 header_->monitorStub = stub;
278 }
280 template <typename T>
281 BufferPointer<T> pointerAtStackOffset(size_t offset) {
282 if (offset < bufferUsed_) {
283 // Calculate offset from copyStackTop.
284 offset = header_->copyStackTop - (header_->copyStackBottom + offset);
285 return BufferPointer<T>(&header_, offset, /* heap = */ true);
286 }
288 return BufferPointer<T>(&header_, offset - bufferUsed_, /* heap = */ false);
289 }
291 BufferPointer<Value> valuePointerAtStackOffset(size_t offset) {
292 return pointerAtStackOffset<Value>(offset);
293 }
295 inline uint8_t *virtualPointerAtStackOffset(size_t offset) {
296 if (offset < bufferUsed_)
297 return reinterpret_cast<uint8_t *>(frame_) - (bufferUsed_ - offset);
298 return reinterpret_cast<uint8_t *>(frame_) + (offset - bufferUsed_);
299 }
301 inline IonJSFrameLayout *startFrame() {
302 return frame_;
303 }
305 BufferPointer<IonJSFrameLayout> topFrameAddress() {
306 return pointerAtStackOffset<IonJSFrameLayout>(0);
307 }
309 //
310 // This method should only be called when the builder is in a state where it is
311 // starting to construct the stack frame for the next callee. This means that
312 // the lowest value on the constructed stack is the return address for the previous
313 // caller frame.
314 //
315 // This method is used to compute the value of the frame pointer (e.g. ebp on x86)
316 // that would have been saved by the baseline jitcode when it was entered. In some
317 // cases, this value can be bogus since we can ensure that the caller would have saved
318 // it anyway.
319 //
320 void *calculatePrevFramePtr() {
321 // Get the incoming frame.
322 BufferPointer<IonJSFrameLayout> topFrame = topFrameAddress();
323 FrameType type = topFrame->prevType();
325 // For IonJS and Entry frames, the "saved" frame pointer in the baseline
326 // frame is meaningless, since Ion saves all registers before calling other ion
327 // frames, and the entry frame saves all registers too.
328 if (type == JitFrame_IonJS || type == JitFrame_Entry)
329 return nullptr;
331 // BaselineStub - Baseline calling into Ion.
332 // PrevFramePtr needs to point to the BaselineStubFrame's saved frame pointer.
333 // STACK_START_ADDR + IonJSFrameLayout::Size() + PREV_FRAME_SIZE
334 // - IonBaselineStubFrameLayout::reverseOffsetOfSavedFramePtr()
335 if (type == JitFrame_BaselineStub) {
336 size_t offset = IonJSFrameLayout::Size() + topFrame->prevFrameLocalSize() +
337 IonBaselineStubFrameLayout::reverseOffsetOfSavedFramePtr();
338 return virtualPointerAtStackOffset(offset);
339 }
341 JS_ASSERT(type == JitFrame_Rectifier);
342 // Rectifier - behaviour depends on the frame preceding the rectifier frame, and
343 // whether the arch is x86 or not. The x86 rectifier frame saves the frame pointer,
344 // so we can calculate it directly. For other archs, the previous frame pointer
345 // is stored on the stack in the frame that precedes the rectifier frame.
346 size_t priorOffset = IonJSFrameLayout::Size() + topFrame->prevFrameLocalSize();
347 #if defined(JS_CODEGEN_X86)
348 // On X86, the FramePointer is pushed as the first value in the Rectifier frame.
349 JS_ASSERT(BaselineFrameReg == FramePointer);
350 priorOffset -= sizeof(void *);
351 return virtualPointerAtStackOffset(priorOffset);
352 #elif defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
353 // On X64, ARM and MIPS, the frame pointer save location depends on
354 // the caller of the rectifier frame.
355 BufferPointer<IonRectifierFrameLayout> priorFrame =
356 pointerAtStackOffset<IonRectifierFrameLayout>(priorOffset);
357 FrameType priorType = priorFrame->prevType();
358 JS_ASSERT(priorType == JitFrame_IonJS || priorType == JitFrame_BaselineStub);
360 // If the frame preceding the rectifier is an IonJS frame, then once again
361 // the frame pointer does not matter.
362 if (priorType == JitFrame_IonJS)
363 return nullptr;
365 // Otherwise, the frame preceding the rectifier is a BaselineStub frame.
366 // let X = STACK_START_ADDR + IonJSFrameLayout::Size() + PREV_FRAME_SIZE
367 // X + IonRectifierFrameLayout::Size()
368 // + ((IonRectifierFrameLayout *) X)->prevFrameLocalSize()
369 // - BaselineStubFrameLayout::reverseOffsetOfSavedFramePtr()
370 size_t extraOffset = IonRectifierFrameLayout::Size() + priorFrame->prevFrameLocalSize() +
371 IonBaselineStubFrameLayout::reverseOffsetOfSavedFramePtr();
372 return virtualPointerAtStackOffset(priorOffset + extraOffset);
373 #else
374 # error "Bad architecture!"
375 #endif
376 }
377 };
379 static inline bool
380 IsInlinableFallback(ICFallbackStub *icEntry)
381 {
382 return icEntry->isCall_Fallback() || icEntry->isGetProp_Fallback() ||
383 icEntry->isSetProp_Fallback();
384 }
386 static inline void*
387 GetStubReturnAddress(JSContext *cx, jsbytecode *pc)
388 {
389 if (IsGetPropPC(pc))
390 return cx->compartment()->jitCompartment()->baselineGetPropReturnFromIonAddr();
391 if (IsSetPropPC(pc))
392 return cx->compartment()->jitCompartment()->baselineSetPropReturnFromIonAddr();
393 // This should be a call op of some kind, now.
394 JS_ASSERT(IsCallPC(pc));
395 return cx->compartment()->jitCompartment()->baselineCallReturnFromIonAddr();
396 }
398 static inline jsbytecode *
399 GetNextNonLoopEntryPc(jsbytecode *pc)
400 {
401 JSOp op = JSOp(*pc);
402 if (op == JSOP_GOTO)
403 return pc + GET_JUMP_OFFSET(pc);
404 if (op == JSOP_LOOPENTRY || op == JSOP_NOP || op == JSOP_LOOPHEAD)
405 return GetNextPc(pc);
406 return pc;
407 }
409 // For every inline frame, we write out the following data:
410 //
411 // | ... |
412 // +---------------+
413 // | Descr(???) | --- Descr size here is (PREV_FRAME_SIZE)
414 // +---------------+
415 // | ReturnAddr |
416 // -- +===============+ --- OVERWRITE STARTS HERE (START_STACK_ADDR)
417 // | | PrevFramePtr |
418 // | +-> +---------------+
419 // | | | Baseline |
420 // | | | Frame |
421 // | | +---------------+
422 // | | | Fixed0 |
423 // | | +---------------+
424 // +--< | | ... |
425 // | | | +---------------+
426 // | | | | FixedF |
427 // | | | +---------------+
428 // | | | | Stack0 |
429 // | | | +---------------+
430 // | | | | ... |
431 // | | | +---------------+
432 // | | | | StackS |
433 // | -- | +---------------+ --- IF NOT LAST INLINE FRAME,
434 // +------------| Descr(BLJS) | --- CALLING INFO STARTS HERE
435 // | +---------------+
436 // | | ReturnAddr | <-- return into main jitcode after IC
437 // -- | +===============+
438 // | | | StubPtr |
439 // | | +---------------+
440 // | +---| FramePtr |
441 // | +---------------+
442 // | | ArgA |
443 // | +---------------+
444 // | | ... |
445 // +--< +---------------+
446 // | | | Arg0 |
447 // | | +---------------+
448 // | | | ThisV |
449 // | -- +---------------+
450 // | | ActualArgC |
451 // | +---------------+
452 // | | CalleeToken |
453 // | +---------------+
454 // +------------| Descr(BLStub) |
455 // +---------------+
456 // | ReturnAddr | <-- return into ICCall_Scripted IC
457 // -- +===============+ --- IF CALLEE FORMAL ARGS > ActualArgC
458 // | | UndefinedU |
459 // | +---------------+
460 // | | ... |
461 // | +---------------+
462 // | | Undefined0 |
463 // | +---------------+
464 // +--< | ArgA |
465 // | | +---------------+
466 // | | | ... |
467 // | | +---------------+
468 // | | | Arg0 |
469 // | | +---------------+
470 // | | | ThisV |
471 // | -- +---------------+
472 // | | ActualArgC |
473 // | +---------------+
474 // | | CalleeToken |
475 // | +---------------+
476 // +------------| Descr(Rect) |
477 // +---------------+
478 // | ReturnAddr | <-- return into ArgumentsRectifier after call
479 // +===============+
480 //
481 static bool
482 InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
483 HandleFunction fun, HandleScript script, IonScript *ionScript,
484 SnapshotIterator &iter, bool invalidate, BaselineStackBuilder &builder,
485 AutoValueVector &startFrameFormals, MutableHandleFunction nextCallee,
486 jsbytecode **callPC, const ExceptionBailoutInfo *excInfo)
487 {
488 MOZ_ASSERT(script->hasBaselineScript());
490 // Are we catching an exception?
491 bool catchingException = excInfo && excInfo->catchingException();
493 // If we are catching an exception, we are bailing out to a catch or
494 // finally block and this is the frame where we will resume. Usually the
495 // expression stack should be empty in this case but there can be
496 // iterators on the stack.
497 uint32_t exprStackSlots;
498 if (catchingException)
499 exprStackSlots = excInfo->numExprSlots();
500 else
501 exprStackSlots = iter.numAllocations() - (script->nfixed() + CountArgSlots(script, fun));
503 builder.resetFramePushed();
505 // Build first baseline frame:
506 // +===============+
507 // | PrevFramePtr |
508 // +---------------+
509 // | Baseline |
510 // | Frame |
511 // +---------------+
512 // | Fixed0 |
513 // +---------------+
514 // | ... |
515 // +---------------+
516 // | FixedF |
517 // +---------------+
518 // | Stack0 |
519 // +---------------+
520 // | ... |
521 // +---------------+
522 // | StackS |
523 // +---------------+ --- IF NOT LAST INLINE FRAME,
524 // | Descr(BLJS) | --- CALLING INFO STARTS HERE
525 // +---------------+
526 // | ReturnAddr | <-- return into main jitcode after IC
527 // +===============+
529 IonSpew(IonSpew_BaselineBailouts, " Unpacking %s:%d", script->filename(), script->lineno());
530 IonSpew(IonSpew_BaselineBailouts, " [BASELINE-JS FRAME]");
532 // Calculate and write the previous frame pointer value.
533 // Record the virtual stack offset at this location. Later on, if we end up
534 // writing out a BaselineStub frame for the next callee, we'll need to save the
535 // address.
536 void *prevFramePtr = builder.calculatePrevFramePtr();
537 if (!builder.writePtr(prevFramePtr, "PrevFramePtr"))
538 return false;
539 prevFramePtr = builder.virtualPointerAtStackOffset(0);
541 // Write struct BaselineFrame.
542 if (!builder.subtract(BaselineFrame::Size(), "BaselineFrame"))
543 return false;
544 BufferPointer<BaselineFrame> blFrame = builder.pointerAtStackOffset<BaselineFrame>(0);
546 // Initialize BaselineFrame::frameSize
547 uint32_t frameSize = BaselineFrame::Size() + BaselineFrame::FramePointerOffset +
548 (sizeof(Value) * (script->nfixed() + exprStackSlots));
549 IonSpew(IonSpew_BaselineBailouts, " FrameSize=%d", (int) frameSize);
550 blFrame->setFrameSize(frameSize);
552 uint32_t flags = 0;
554 // If SPS Profiler is enabled, mark the frame as having pushed an SPS entry.
555 // This may be wrong for the last frame of ArgumentCheck bailout, but
556 // that will be fixed later.
557 if (ionScript->hasSPSInstrumentation()) {
558 if (callerPC == nullptr) {
559 IonSpew(IonSpew_BaselineBailouts, " Setting SPS flag on top frame!");
560 flags |= BaselineFrame::HAS_PUSHED_SPS_FRAME;
561 } else if (js_JitOptions.profileInlineFrames) {
562 IonSpew(IonSpew_BaselineBailouts, " Setting SPS flag on inline frame!");
563 flags |= BaselineFrame::HAS_PUSHED_SPS_FRAME;
564 }
565 }
567 // Initialize BaselineFrame's scopeChain and argsObj
568 JSObject *scopeChain = nullptr;
569 Value returnValue;
570 ArgumentsObject *argsObj = nullptr;
571 BailoutKind bailoutKind = iter.bailoutKind();
572 if (bailoutKind == Bailout_ArgumentCheck) {
573 // Temporary hack -- skip the (unused) scopeChain, because it could be
574 // bogus (we can fail before the scope chain slot is set). Strip the
575 // hasScopeChain flag and this will be fixed up later in |FinishBailoutToBaseline|,
576 // which calls |EnsureHasScopeObjects|.
577 IonSpew(IonSpew_BaselineBailouts, " Bailout_ArgumentCheck! (no valid scopeChain)");
578 iter.skip();
580 // skip |return value|
581 iter.skip();
582 returnValue = UndefinedValue();
584 // Scripts with |argumentsHasVarBinding| have an extra slot.
585 if (script->argumentsHasVarBinding()) {
586 IonSpew(IonSpew_BaselineBailouts,
587 " Bailout_ArgumentCheck for script with argumentsHasVarBinding!"
588 "Using empty arguments object");
589 iter.skip();
590 }
591 } else {
592 Value v = iter.read();
593 if (v.isObject()) {
594 scopeChain = &v.toObject();
595 if (fun && fun->isHeavyweight())
596 flags |= BaselineFrame::HAS_CALL_OBJ;
597 } else {
598 JS_ASSERT(v.isUndefined() || v.isMagic(JS_OPTIMIZED_OUT));
600 // Get scope chain from function or script.
601 if (fun) {
602 // If pcOffset == 0, we may have to push a new call object, so
603 // we leave scopeChain nullptr and enter baseline code before
604 // the prologue.
605 if (iter.pcOffset() != 0 || iter.resumeAfter())
606 scopeChain = fun->environment();
607 } else {
608 // For global, compile-and-go scripts the scope chain is the
609 // script's global (Ion does not compile non-compile-and-go
610 // scripts). Also note that it's invalid to resume into the
611 // prologue in this case because the prologue expects the scope
612 // chain in R1 for eval and global scripts.
613 JS_ASSERT(!script->isForEval());
614 JS_ASSERT(script->compileAndGo());
615 scopeChain = &(script->global());
616 }
617 }
619 // Make sure to add HAS_RVAL to flags here because setFlags() below
620 // will clobber it.
621 returnValue = iter.read();
622 flags |= BaselineFrame::HAS_RVAL;
624 // If script maybe has an arguments object, the third slot will hold it.
625 if (script->argumentsHasVarBinding()) {
626 v = iter.read();
627 JS_ASSERT(v.isObject() || v.isUndefined() || v.isMagic(JS_OPTIMIZED_OUT));
628 if (v.isObject())
629 argsObj = &v.toObject().as<ArgumentsObject>();
630 }
631 }
632 IonSpew(IonSpew_BaselineBailouts, " ScopeChain=%p", scopeChain);
633 blFrame->setScopeChain(scopeChain);
634 IonSpew(IonSpew_BaselineBailouts, " ReturnValue=%016llx", *((uint64_t *) &returnValue));
635 blFrame->setReturnValue(returnValue);
637 // Do not need to initialize scratchValue field in BaselineFrame.
638 blFrame->setFlags(flags);
640 // initArgsObjUnchecked modifies the frame's flags, so call it after setFlags.
641 if (argsObj)
642 blFrame->initArgsObjUnchecked(*argsObj);
644 if (fun) {
645 // The unpacked thisv and arguments should overwrite the pushed args present
646 // in the calling frame.
647 Value thisv = iter.read();
648 IonSpew(IonSpew_BaselineBailouts, " Is function!");
649 IonSpew(IonSpew_BaselineBailouts, " thisv=%016llx", *((uint64_t *) &thisv));
651 size_t thisvOffset = builder.framePushed() + IonJSFrameLayout::offsetOfThis();
652 *builder.valuePointerAtStackOffset(thisvOffset) = thisv;
654 JS_ASSERT(iter.numAllocations() >= CountArgSlots(script, fun));
655 IonSpew(IonSpew_BaselineBailouts, " frame slots %u, nargs %u, nfixed %u",
656 iter.numAllocations(), fun->nargs(), script->nfixed());
658 if (!callerPC) {
659 // This is the first frame. Store the formals in a Vector until we
660 // are done. Due to UCE and phi elimination, we could store an
661 // UndefinedValue() here for formals we think are unused, but
662 // locals may still reference the original argument slot
663 // (MParameter/LArgument) and expect the original Value.
664 JS_ASSERT(startFrameFormals.empty());
665 if (!startFrameFormals.resize(fun->nargs()))
666 return false;
667 }
669 for (uint32_t i = 0; i < fun->nargs(); i++) {
670 Value arg = iter.read();
671 IonSpew(IonSpew_BaselineBailouts, " arg %d = %016llx",
672 (int) i, *((uint64_t *) &arg));
673 if (callerPC) {
674 size_t argOffset = builder.framePushed() + IonJSFrameLayout::offsetOfActualArg(i);
675 *builder.valuePointerAtStackOffset(argOffset) = arg;
676 } else {
677 startFrameFormals[i] = arg;
678 }
679 }
680 }
682 for (uint32_t i = 0; i < script->nfixed(); i++) {
683 Value slot = iter.read();
684 if (!builder.writeValue(slot, "FixedValue"))
685 return false;
686 }
688 // Get the pc. If we are handling an exception, resume at the pc of the
689 // catch or finally block.
690 jsbytecode *pc = catchingException ? excInfo->resumePC() : script->offsetToPC(iter.pcOffset());
691 bool resumeAfter = catchingException ? false : iter.resumeAfter();
693 JSOp op = JSOp(*pc);
695 // Fixup inlined JSOP_FUNCALL, JSOP_FUNAPPLY, and accessors on the caller side.
696 // On the caller side this must represent like the function wasn't inlined.
697 uint32_t pushedSlots = 0;
698 AutoValueVector savedCallerArgs(cx);
699 bool needToSaveArgs = op == JSOP_FUNAPPLY || IsGetPropPC(pc) || IsSetPropPC(pc);
700 if (iter.moreFrames() && (op == JSOP_FUNCALL || needToSaveArgs))
701 {
702 uint32_t inlined_args = 0;
703 if (op == JSOP_FUNCALL)
704 inlined_args = 2 + GET_ARGC(pc) - 1;
705 else if (op == JSOP_FUNAPPLY)
706 inlined_args = 2 + blFrame->numActualArgs();
707 else
708 inlined_args = 2 + IsSetPropPC(pc);
710 JS_ASSERT(exprStackSlots >= inlined_args);
711 pushedSlots = exprStackSlots - inlined_args;
713 IonSpew(IonSpew_BaselineBailouts,
714 " pushing %u expression stack slots before fixup",
715 pushedSlots);
716 for (uint32_t i = 0; i < pushedSlots; i++) {
717 Value v = iter.read();
718 if (!builder.writeValue(v, "StackValue"))
719 return false;
720 }
722 if (op == JSOP_FUNCALL) {
723 // When funcall got inlined and the native js_fun_call was bypassed,
724 // the stack state is incorrect. To restore correctly it must look like
725 // js_fun_call was actually called. This means transforming the stack
726 // from |target, this, args| to |js_fun_call, target, this, args|
727 // The js_fun_call is never read, so just pushing undefined now.
728 IonSpew(IonSpew_BaselineBailouts, " pushing undefined to fixup funcall");
729 if (!builder.writeValue(UndefinedValue(), "StackValue"))
730 return false;
731 }
733 if (needToSaveArgs) {
734 // When an accessor is inlined, the whole thing is a lie. There
735 // should never have been a call there. Fix the caller's stack to
736 // forget it ever happened.
738 // When funapply gets inlined we take all arguments out of the
739 // arguments array. So the stack state is incorrect. To restore
740 // correctly it must look like js_fun_apply was actually called.
741 // This means transforming the stack from |target, this, arg1, ...|
742 // to |js_fun_apply, target, this, argObject|.
743 // Since the information is never read, we can just push undefined
744 // for all values.
745 if (op == JSOP_FUNAPPLY) {
746 IonSpew(IonSpew_BaselineBailouts, " pushing 4x undefined to fixup funapply");
747 if (!builder.writeValue(UndefinedValue(), "StackValue"))
748 return false;
749 if (!builder.writeValue(UndefinedValue(), "StackValue"))
750 return false;
751 if (!builder.writeValue(UndefinedValue(), "StackValue"))
752 return false;
753 if (!builder.writeValue(UndefinedValue(), "StackValue"))
754 return false;
755 }
756 // Save the actual arguments. They are needed on the callee side
757 // as the arguments. Else we can't recover them.
758 if (!savedCallerArgs.resize(inlined_args))
759 return false;
760 for (uint32_t i = 0; i < inlined_args; i++)
761 savedCallerArgs[i] = iter.read();
763 if (IsSetPropPC(pc)) {
764 // We would love to just save all the arguments and leave them
765 // in the stub frame pushed below, but we will lose the inital
766 // argument which the function was called with, which we must
767 // return to the caller, even if the setter internally modifies
768 // its arguments. Stash the initial argument on the stack, to be
769 // later retrieved by the SetProp_Fallback stub.
770 Value initialArg = savedCallerArgs[inlined_args - 1];
771 IonSpew(IonSpew_BaselineBailouts, " pushing setter's initial argument");
772 if (!builder.writeValue(initialArg, "StackValue"))
773 return false;
774 }
775 pushedSlots = exprStackSlots;
776 }
777 }
779 IonSpew(IonSpew_BaselineBailouts, " pushing %u expression stack slots",
780 exprStackSlots - pushedSlots);
781 for (uint32_t i = pushedSlots; i < exprStackSlots; i++) {
782 Value v;
784 if (!iter.moreFrames() && i == exprStackSlots - 1 &&
785 cx->runtime()->hasIonReturnOverride())
786 {
787 // If coming from an invalidation bailout, and this is the topmost
788 // value, and a value override has been specified, don't read from the
789 // iterator. Otherwise, we risk using a garbage value.
790 JS_ASSERT(invalidate);
791 iter.skip();
792 IonSpew(IonSpew_BaselineBailouts, " [Return Override]");
793 v = cx->runtime()->takeIonReturnOverride();
794 } else if (excInfo && excInfo->propagatingIonExceptionForDebugMode()) {
795 // If we are in the middle of propagating an exception from Ion by
796 // bailing to baseline due to debug mode, we might not have all
797 // the stack if we are at the newest frame.
798 //
799 // For instance, if calling |f()| pushed an Ion frame which threw,
800 // the snapshot expects the return value to be pushed, but it's
801 // possible nothing was pushed before we threw. Iterators might
802 // still be on the stack, so we can't just drop the stack.
803 MOZ_ASSERT(cx->compartment()->debugMode());
804 if (iter.moreFrames())
805 v = iter.read();
806 else
807 v = MagicValue(JS_OPTIMIZED_OUT);
808 } else {
809 v = iter.read();
810 }
811 if (!builder.writeValue(v, "StackValue"))
812 return false;
813 }
815 size_t endOfBaselineJSFrameStack = builder.framePushed();
817 // If we are resuming at a LOOPENTRY op, resume at the next op to avoid
818 // a bailout -> enter Ion -> bailout loop with --ion-eager. See also
819 // ThunkToInterpreter.
820 //
821 // The algorithm below is the "tortoise and the hare" algorithm. See bug
822 // 994444 for more explanation.
823 if (!resumeAfter) {
824 jsbytecode *fasterPc = pc;
825 while (true) {
826 pc = GetNextNonLoopEntryPc(pc);
827 fasterPc = GetNextNonLoopEntryPc(GetNextNonLoopEntryPc(fasterPc));
828 if (fasterPc == pc)
829 break;
830 }
831 op = JSOp(*pc);
832 }
834 uint32_t pcOff = script->pcToOffset(pc);
835 bool isCall = IsCallPC(pc);
836 BaselineScript *baselineScript = script->baselineScript();
838 #ifdef DEBUG
839 uint32_t expectedDepth;
840 bool reachablePC;
841 if (!ReconstructStackDepth(cx, script, resumeAfter ? GetNextPc(pc) : pc, &expectedDepth, &reachablePC))
842 return false;
844 if (reachablePC) {
845 if (op != JSOP_FUNAPPLY || !iter.moreFrames() || resumeAfter) {
846 if (op == JSOP_FUNCALL) {
847 // For fun.call(this, ...); the reconstructStackDepth will
848 // include the this. When inlining that is not included.
849 // So the exprStackSlots will be one less.
850 JS_ASSERT(expectedDepth - exprStackSlots <= 1);
851 } else if (iter.moreFrames() && (IsGetPropPC(pc) || IsSetPropPC(pc))) {
852 // Accessors coming out of ion are inlined via a complete
853 // lie perpetrated by the compiler internally. Ion just rearranges
854 // the stack, and pretends that it looked like a call all along.
855 // This means that the depth is actually one *more* than expected
856 // by the interpreter, as there is now a JSFunction, |this| and [arg],
857 // rather than the expected |this| and [arg]
858 // Note that none of that was pushed, but it's still reflected
859 // in exprStackSlots.
860 JS_ASSERT(exprStackSlots - expectedDepth == 1);
861 } else {
862 // For fun.apply({}, arguments) the reconstructStackDepth will
863 // have stackdepth 4, but it could be that we inlined the
864 // funapply. In that case exprStackSlots, will have the real
865 // arguments in the slots and not be 4.
866 JS_ASSERT(exprStackSlots == expectedDepth);
867 }
868 }
869 }
871 IonSpew(IonSpew_BaselineBailouts, " Resuming %s pc offset %d (op %s) (line %d) of %s:%d",
872 resumeAfter ? "after" : "at", (int) pcOff, js_CodeName[op],
873 PCToLineNumber(script, pc), script->filename(), (int) script->lineno());
874 IonSpew(IonSpew_BaselineBailouts, " Bailout kind: %s",
875 BailoutKindString(bailoutKind));
876 #endif
878 // If this was the last inline frame, or we are bailing out to a catch or
879 // finally block in this frame, then unpacking is almost done.
880 if (!iter.moreFrames() || catchingException) {
881 // Last frame, so PC for call to next frame is set to nullptr.
882 *callPC = nullptr;
884 // If the bailout was a resumeAfter, and the opcode is monitored,
885 // then the bailed out state should be in a position to enter
886 // into the ICTypeMonitor chain for the op.
887 bool enterMonitorChain = false;
888 if (resumeAfter && (js_CodeSpec[op].format & JOF_TYPESET)) {
889 // Not every monitored op has a monitored fallback stub, e.g.
890 // JSOP_NEWOBJECT, which always returns the same type for a
891 // particular script/pc location.
892 ICEntry &icEntry = baselineScript->icEntryFromPCOffset(pcOff);
893 ICFallbackStub *fallbackStub = icEntry.firstStub()->getChainFallback();
894 if (fallbackStub->isMonitoredFallback())
895 enterMonitorChain = true;
896 }
898 uint32_t numCallArgs = isCall ? GET_ARGC(pc) : 0;
900 if (resumeAfter && !enterMonitorChain)
901 pc = GetNextPc(pc);
903 builder.setResumeFramePtr(prevFramePtr);
905 if (enterMonitorChain) {
906 ICEntry &icEntry = baselineScript->icEntryFromPCOffset(pcOff);
907 ICFallbackStub *fallbackStub = icEntry.firstStub()->getChainFallback();
908 JS_ASSERT(fallbackStub->isMonitoredFallback());
909 IonSpew(IonSpew_BaselineBailouts, " [TYPE-MONITOR CHAIN]");
910 ICMonitoredFallbackStub *monFallbackStub = fallbackStub->toMonitoredFallbackStub();
911 ICStub *firstMonStub = monFallbackStub->fallbackMonitorStub()->firstMonitorStub();
913 // To enter a monitoring chain, we load the top stack value into R0
914 IonSpew(IonSpew_BaselineBailouts, " Popping top stack value into R0.");
915 builder.popValueInto(PCMappingSlotInfo::SlotInR0);
917 // Need to adjust the frameSize for the frame to match the values popped
918 // into registers.
919 frameSize -= sizeof(Value);
920 blFrame->setFrameSize(frameSize);
921 IonSpew(IonSpew_BaselineBailouts, " Adjusted framesize -= %d: %d",
922 (int) sizeof(Value), (int) frameSize);
924 // If resuming into a JSOP_CALL, baseline keeps the arguments on the
925 // stack and pops them only after returning from the call IC.
926 // Push undefs onto the stack in anticipation of the popping of the
927 // callee, thisv, and actual arguments passed from the caller's frame.
928 if (isCall) {
929 builder.writeValue(UndefinedValue(), "CallOp FillerCallee");
930 builder.writeValue(UndefinedValue(), "CallOp FillerThis");
931 for (uint32_t i = 0; i < numCallArgs; i++)
932 builder.writeValue(UndefinedValue(), "CallOp FillerArg");
934 frameSize += (numCallArgs + 2) * sizeof(Value);
935 blFrame->setFrameSize(frameSize);
936 IonSpew(IonSpew_BaselineBailouts, " Adjusted framesize += %d: %d",
937 (int) ((numCallArgs + 2) * sizeof(Value)), (int) frameSize);
938 }
940 // Set the resume address to the return point from the IC, and set
941 // the monitor stub addr.
942 builder.setResumeAddr(baselineScript->returnAddressForIC(icEntry));
943 builder.setMonitorStub(firstMonStub);
944 IonSpew(IonSpew_BaselineBailouts, " Set resumeAddr=%p monitorStub=%p",
945 baselineScript->returnAddressForIC(icEntry), firstMonStub);
947 } else {
948 // If needed, initialize BaselineBailoutInfo's valueR0 and/or valueR1 with the
949 // top stack values.
950 PCMappingSlotInfo slotInfo;
951 uint8_t *nativeCodeForPC = baselineScript->nativeCodeForPC(script, pc, &slotInfo);
952 unsigned numUnsynced = slotInfo.numUnsynced();
953 JS_ASSERT(numUnsynced <= 2);
954 PCMappingSlotInfo::SlotLocation loc1, loc2;
955 if (numUnsynced > 0) {
956 loc1 = slotInfo.topSlotLocation();
957 IonSpew(IonSpew_BaselineBailouts, " Popping top stack value into %d.",
958 (int) loc1);
959 builder.popValueInto(loc1);
960 }
961 if (numUnsynced > 1) {
962 loc2 = slotInfo.nextSlotLocation();
963 IonSpew(IonSpew_BaselineBailouts, " Popping next stack value into %d.",
964 (int) loc2);
965 JS_ASSERT_IF(loc1 != PCMappingSlotInfo::SlotIgnore, loc1 != loc2);
966 builder.popValueInto(loc2);
967 }
969 // Need to adjust the frameSize for the frame to match the values popped
970 // into registers.
971 frameSize -= sizeof(Value) * numUnsynced;
972 blFrame->setFrameSize(frameSize);
973 IonSpew(IonSpew_BaselineBailouts, " Adjusted framesize -= %d: %d",
974 int(sizeof(Value) * numUnsynced), int(frameSize));
976 // If scopeChain is nullptr, then bailout is occurring during argument check.
977 // In this case, resume into the prologue.
978 uint8_t *opReturnAddr;
979 if (scopeChain == nullptr) {
980 // Global and eval scripts expect the scope chain in R1, so only
981 // resume into the prologue for function scripts.
982 JS_ASSERT(fun);
983 JS_ASSERT(numUnsynced == 0);
984 opReturnAddr = baselineScript->prologueEntryAddr();
985 IonSpew(IonSpew_BaselineBailouts, " Resuming into prologue.");
987 // If bailing into prologue, HAS_PUSHED_SPS_FRAME should not be set on frame.
988 blFrame->unsetPushedSPSFrame();
990 if (cx->runtime()->spsProfiler.enabled()) {
991 if (js_JitOptions.profileInlineFrames) {
992 // If SPS is enabled, there are two corner cases to handle:
993 // 1. If resuming into the prologue, and innermost frame is an inlined
994 // frame, and bailout is because of argument check failure, then:
995 // Top SPS profiler entry would be for caller frame.
996 // Ion would not have set the PC index field on that frame
997 // (since this bailout happens before MFunctionBoundary).
998 // Make sure that's done now.
999 // 2. If resuming into the prologue, and the bailout is NOT because of an
1000 // argument check, then:
1001 // Top SPS profiler entry would be for callee frame.
1002 // Ion would already have pushed an SPS entry for this frame.
1003 // The pc for this entry would be set to nullptr.
1004 // Make sure it's set to script->pc.
1005 if (caller && bailoutKind == Bailout_ArgumentCheck) {
1006 IonSpew(IonSpew_BaselineBailouts, " Setting PCidx on innermost "
1007 "inlined frame's parent's SPS entry (%s:%d) (pcIdx=%d)!",
1008 caller->filename(), caller->lineno(),
1009 caller->pcToOffset(callerPC));
1010 cx->runtime()->spsProfiler.updatePC(caller, callerPC);
1012 } else if (bailoutKind != Bailout_ArgumentCheck) {
1013 IonSpew(IonSpew_BaselineBailouts,
1014 " Popping SPS entry for innermost inlined frame");
1015 cx->runtime()->spsProfiler.exit(script, fun);
1016 }
1018 } else {
1019 // If not profiling inline frames, then this is logically simpler.
1020 //
1021 // 1. If resuming into inline code, then the top SPS entry will be
1022 // for the outermost caller, and will have an uninitialized PC.
1023 // This will be fixed up later in BailoutIonToBaseline.
1024 //
1025 // 2. If resuming into top-level code prologue, with ArgumentCheck,
1026 // no SPS entry will have been pushed. Can be left alone.
1027 //
1028 // 3. If resuming into top-level code prologue, without ArgumentCheck,
1029 // an SPS entry will have been pushed, and needs to be popped.
1030 //
1031 // 4. If resuming into top-level code main body, an SPS entry will
1032 // have been pushed, and can be left alone.
1033 //
1034 // Only need to handle case 3 here.
1035 if (!caller && bailoutKind != Bailout_ArgumentCheck) {
1036 IonSpew(IonSpew_BaselineBailouts,
1037 " Popping SPS entry for outermost frame");
1038 cx->runtime()->spsProfiler.exit(script, fun);
1039 }
1040 }
1041 }
1042 } else {
1043 opReturnAddr = nativeCodeForPC;
1044 }
1045 builder.setResumeAddr(opReturnAddr);
1046 IonSpew(IonSpew_BaselineBailouts, " Set resumeAddr=%p", opReturnAddr);
1047 }
1049 if (cx->runtime()->spsProfiler.enabled()) {
1050 if (blFrame->hasPushedSPSFrame()) {
1051 // Set PC index to 0 for the innermost frame to match what the
1052 // interpreter and Baseline do: they update the SPS pc for
1053 // JSOP_CALL ops but set it to 0 when running other ops. Ion code
1054 // can set the pc to NullPCIndex and this will confuse SPS when
1055 // Baseline calls into the VM at non-CALL ops and re-enters JS.
1056 IonSpew(IonSpew_BaselineBailouts, " Setting PCidx for last frame to 0");
1057 cx->runtime()->spsProfiler.updatePC(script, script->code());
1058 }
1060 // Register bailout with profiler.
1061 const char *filename = script->filename();
1062 if (filename == nullptr)
1063 filename = "<unknown>";
1064 unsigned len = strlen(filename) + 200;
1065 char *buf = js_pod_malloc<char>(len);
1066 if (buf == nullptr)
1067 return false;
1068 JS_snprintf(buf, len, "%s %s %s on line %d of %s:%d",
1069 BailoutKindString(bailoutKind),
1070 resumeAfter ? "after" : "at",
1071 js_CodeName[op],
1072 int(PCToLineNumber(script, pc)),
1073 filename,
1074 int(script->lineno()));
1075 cx->runtime()->spsProfiler.markEvent(buf);
1076 js_free(buf);
1077 }
1079 return true;
1080 }
1082 *callPC = pc;
1084 // Write out descriptor of BaselineJS frame.
1085 size_t baselineFrameDescr = MakeFrameDescriptor((uint32_t) builder.framePushed(),
1086 JitFrame_BaselineJS);
1087 if (!builder.writeWord(baselineFrameDescr, "Descriptor"))
1088 return false;
1090 // Calculate and write out return address.
1091 // The icEntry in question MUST have an inlinable fallback stub.
1092 ICEntry &icEntry = baselineScript->icEntryFromPCOffset(pcOff);
1093 JS_ASSERT(IsInlinableFallback(icEntry.firstStub()->getChainFallback()));
1094 if (!builder.writePtr(baselineScript->returnAddressForIC(icEntry), "ReturnAddr"))
1095 return false;
1097 // Build baseline stub frame:
1098 // +===============+
1099 // | StubPtr |
1100 // +---------------+
1101 // | FramePtr |
1102 // +---------------+
1103 // | ArgA |
1104 // +---------------+
1105 // | ... |
1106 // +---------------+
1107 // | Arg0 |
1108 // +---------------+
1109 // | ThisV |
1110 // +---------------+
1111 // | ActualArgC |
1112 // +---------------+
1113 // | CalleeToken |
1114 // +---------------+
1115 // | Descr(BLStub) |
1116 // +---------------+
1117 // | ReturnAddr |
1118 // +===============+
1120 IonSpew(IonSpew_BaselineBailouts, " [BASELINE-STUB FRAME]");
1122 size_t startOfBaselineStubFrame = builder.framePushed();
1124 // Write stub pointer.
1125 JS_ASSERT(IsInlinableFallback(icEntry.fallbackStub()));
1126 if (!builder.writePtr(icEntry.fallbackStub(), "StubPtr"))
1127 return false;
1129 // Write previous frame pointer (saved earlier).
1130 if (!builder.writePtr(prevFramePtr, "PrevFramePtr"))
1131 return false;
1132 prevFramePtr = builder.virtualPointerAtStackOffset(0);
1134 // Write out actual arguments (and thisv), copied from unpacked stack of BaselineJS frame.
1135 // Arguments are reversed on the BaselineJS frame's stack values.
1136 JS_ASSERT(IsIonInlinablePC(pc));
1137 unsigned actualArgc;
1138 if (needToSaveArgs) {
1139 // For FUNAPPLY or an accessor, the arguments are not on the stack anymore,
1140 // but they are copied in a vector and are written here.
1141 if (op == JSOP_FUNAPPLY)
1142 actualArgc = blFrame->numActualArgs();
1143 else
1144 actualArgc = IsSetPropPC(pc);
1146 JS_ASSERT(actualArgc + 2 <= exprStackSlots);
1147 JS_ASSERT(savedCallerArgs.length() == actualArgc + 2);
1148 for (unsigned i = 0; i < actualArgc + 1; i++) {
1149 size_t arg = savedCallerArgs.length() - (i + 1);
1150 if (!builder.writeValue(savedCallerArgs[arg], "ArgVal"))
1151 return false;
1152 }
1153 } else {
1154 actualArgc = GET_ARGC(pc);
1155 if (op == JSOP_FUNCALL) {
1156 JS_ASSERT(actualArgc > 0);
1157 actualArgc--;
1158 }
1160 JS_ASSERT(actualArgc + 2 <= exprStackSlots);
1161 for (unsigned i = 0; i < actualArgc + 1; i++) {
1162 size_t argSlot = (script->nfixed() + exprStackSlots) - (i + 1);
1163 if (!builder.writeValue(*blFrame->valueSlot(argSlot), "ArgVal"))
1164 return false;
1165 }
1166 }
1168 // In case these arguments need to be copied on the stack again for a rectifier frame,
1169 // save the framePushed values here for later use.
1170 size_t endOfBaselineStubArgs = builder.framePushed();
1172 // Calculate frame size for descriptor.
1173 size_t baselineStubFrameSize = builder.framePushed() - startOfBaselineStubFrame;
1174 size_t baselineStubFrameDescr = MakeFrameDescriptor((uint32_t) baselineStubFrameSize,
1175 JitFrame_BaselineStub);
1177 // Push actual argc
1178 if (!builder.writeWord(actualArgc, "ActualArgc"))
1179 return false;
1181 // Push callee token (must be a JS Function)
1182 Value callee;
1183 if (needToSaveArgs) {
1184 // The arguments of FUNAPPLY or inlined accessors are not writen to the stack.
1185 // So get the callee from the specially saved vector.
1186 callee = savedCallerArgs[0];
1187 } else {
1188 uint32_t calleeStackSlot = exprStackSlots - uint32_t(actualArgc + 2);
1189 size_t calleeOffset = (builder.framePushed() - endOfBaselineJSFrameStack)
1190 + ((exprStackSlots - (calleeStackSlot + 1)) * sizeof(Value));
1191 callee = *builder.valuePointerAtStackOffset(calleeOffset);
1192 IonSpew(IonSpew_BaselineBailouts, " CalleeStackSlot=%d", (int) calleeStackSlot);
1193 }
1194 IonSpew(IonSpew_BaselineBailouts, " Callee = %016llx", *((uint64_t *) &callee));
1195 JS_ASSERT(callee.isObject() && callee.toObject().is<JSFunction>());
1196 JSFunction *calleeFun = &callee.toObject().as<JSFunction>();
1197 if (!builder.writePtr(CalleeToToken(calleeFun), "CalleeToken"))
1198 return false;
1199 nextCallee.set(calleeFun);
1201 // Push BaselineStub frame descriptor
1202 if (!builder.writeWord(baselineStubFrameDescr, "Descriptor"))
1203 return false;
1205 // Push return address into ICCall_Scripted stub, immediately after the call.
1206 void *baselineCallReturnAddr = GetStubReturnAddress(cx, pc);
1207 JS_ASSERT(baselineCallReturnAddr);
1208 if (!builder.writePtr(baselineCallReturnAddr, "ReturnAddr"))
1209 return false;
1211 // If actualArgc >= fun->nargs, then we are done. Otherwise, we need to push on
1212 // a reconstructed rectifier frame.
1213 if (actualArgc >= calleeFun->nargs())
1214 return true;
1216 // Push a reconstructed rectifier frame.
1217 // +===============+
1218 // | UndefinedU |
1219 // +---------------+
1220 // | ... |
1221 // +---------------+
1222 // | Undefined0 |
1223 // +---------------+
1224 // | ArgA |
1225 // +---------------+
1226 // | ... |
1227 // +---------------+
1228 // | Arg0 |
1229 // +---------------+
1230 // | ThisV |
1231 // +---------------+
1232 // | ActualArgC |
1233 // +---------------+
1234 // | CalleeToken |
1235 // +---------------+
1236 // | Descr(Rect) |
1237 // +---------------+
1238 // | ReturnAddr |
1239 // +===============+
1241 IonSpew(IonSpew_BaselineBailouts, " [RECTIFIER FRAME]");
1243 size_t startOfRectifierFrame = builder.framePushed();
1245 // On x86-only, the frame pointer is saved again in the rectifier frame.
1246 #if defined(JS_CODEGEN_X86)
1247 if (!builder.writePtr(prevFramePtr, "PrevFramePtr-X86Only"))
1248 return false;
1249 #endif
1251 // Push undefined for missing arguments.
1252 for (unsigned i = 0; i < (calleeFun->nargs() - actualArgc); i++) {
1253 if (!builder.writeValue(UndefinedValue(), "FillerVal"))
1254 return false;
1255 }
1257 // Copy arguments + thisv from BaselineStub frame.
1258 if (!builder.subtract((actualArgc + 1) * sizeof(Value), "CopiedArgs"))
1259 return false;
1260 BufferPointer<uint8_t> stubArgsEnd =
1261 builder.pointerAtStackOffset<uint8_t>(builder.framePushed() - endOfBaselineStubArgs);
1262 IonSpew(IonSpew_BaselineBailouts, " MemCpy from %p", stubArgsEnd.get());
1263 memcpy(builder.pointerAtStackOffset<uint8_t>(0).get(), stubArgsEnd.get(),
1264 (actualArgc + 1) * sizeof(Value));
1266 // Calculate frame size for descriptor.
1267 size_t rectifierFrameSize = builder.framePushed() - startOfRectifierFrame;
1268 size_t rectifierFrameDescr = MakeFrameDescriptor((uint32_t) rectifierFrameSize,
1269 JitFrame_Rectifier);
1271 // Push actualArgc
1272 if (!builder.writeWord(actualArgc, "ActualArgc"))
1273 return false;
1275 // Push calleeToken again.
1276 if (!builder.writePtr(CalleeToToken(calleeFun), "CalleeToken"))
1277 return false;
1279 // Push rectifier frame descriptor
1280 if (!builder.writeWord(rectifierFrameDescr, "Descriptor"))
1281 return false;
1283 // Push return address into the ArgumentsRectifier code, immediately after the ioncode
1284 // call.
1285 void *rectReturnAddr = cx->runtime()->jitRuntime()->getArgumentsRectifierReturnAddr();
1286 JS_ASSERT(rectReturnAddr);
1287 if (!builder.writePtr(rectReturnAddr, "ReturnAddr"))
1288 return false;
1290 return true;
1291 }
1293 uint32_t
1294 jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIterator &iter,
1295 bool invalidate, BaselineBailoutInfo **bailoutInfo,
1296 const ExceptionBailoutInfo *excInfo)
1297 {
1298 JS_ASSERT(bailoutInfo != nullptr);
1299 JS_ASSERT(*bailoutInfo == nullptr);
1301 TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
1302 TraceLogStopEvent(logger, TraceLogger::IonMonkey);
1303 TraceLogStartEvent(logger, TraceLogger::Baseline);
1305 // The caller of the top frame must be one of the following:
1306 // IonJS - Ion calling into Ion.
1307 // BaselineStub - Baseline calling into Ion.
1308 // Entry - Interpreter or other calling into Ion.
1309 // Rectifier - Arguments rectifier calling into Ion.
1310 JS_ASSERT(iter.isIonJS());
1311 FrameType prevFrameType = iter.prevType();
1312 JS_ASSERT(prevFrameType == JitFrame_IonJS ||
1313 prevFrameType == JitFrame_BaselineStub ||
1314 prevFrameType == JitFrame_Entry ||
1315 prevFrameType == JitFrame_Rectifier);
1317 // All incoming frames are going to look like this:
1318 //
1319 // +---------------+
1320 // | ... |
1321 // +---------------+
1322 // | Args |
1323 // | ... |
1324 // +---------------+
1325 // | ThisV |
1326 // +---------------+
1327 // | ActualArgC |
1328 // +---------------+
1329 // | CalleeToken |
1330 // +---------------+
1331 // | Descriptor |
1332 // +---------------+
1333 // | ReturnAddr |
1334 // +---------------+
1335 // | ||||| | <---- Overwrite starting here.
1336 // | ||||| |
1337 // | ||||| |
1338 // +---------------+
1340 IonSpew(IonSpew_BaselineBailouts, "Bailing to baseline %s:%u (IonScript=%p) (FrameType=%d)",
1341 iter.script()->filename(), iter.script()->lineno(), (void *) iter.ionScript(),
1342 (int) prevFrameType);
1344 bool catchingException;
1345 bool propagatingExceptionForDebugMode;
1346 if (excInfo) {
1347 catchingException = excInfo->catchingException();
1348 propagatingExceptionForDebugMode = excInfo->propagatingIonExceptionForDebugMode();
1350 if (catchingException)
1351 IonSpew(IonSpew_BaselineBailouts, "Resuming in catch or finally block");
1353 if (propagatingExceptionForDebugMode)
1354 IonSpew(IonSpew_BaselineBailouts, "Resuming in-place for debug mode");
1355 } else {
1356 catchingException = false;
1357 propagatingExceptionForDebugMode = false;
1358 }
1360 IonSpew(IonSpew_BaselineBailouts, " Reading from snapshot offset %u size %u",
1361 iter.snapshotOffset(), iter.ionScript()->snapshotsListSize());
1363 if (!excInfo)
1364 iter.ionScript()->incNumBailouts();
1365 iter.script()->updateBaselineOrIonRaw();
1367 // Allocate buffer to hold stack replacement data.
1368 BaselineStackBuilder builder(iter, 1024);
1369 if (!builder.init())
1370 return BAILOUT_RETURN_FATAL_ERROR;
1371 IonSpew(IonSpew_BaselineBailouts, " Incoming frame ptr = %p", builder.startFrame());
1373 SnapshotIterator snapIter(iter);
1375 RootedFunction callee(cx, iter.maybeCallee());
1376 RootedScript scr(cx, iter.script());
1377 if (callee) {
1378 IonSpew(IonSpew_BaselineBailouts, " Callee function (%s:%u)",
1379 scr->filename(), scr->lineno());
1380 } else {
1381 IonSpew(IonSpew_BaselineBailouts, " No callee!");
1382 }
1384 if (iter.isConstructing())
1385 IonSpew(IonSpew_BaselineBailouts, " Constructing!");
1386 else
1387 IonSpew(IonSpew_BaselineBailouts, " Not constructing!");
1389 IonSpew(IonSpew_BaselineBailouts, " Restoring frames:");
1390 size_t frameNo = 0;
1392 // Reconstruct baseline frames using the builder.
1393 RootedScript caller(cx);
1394 jsbytecode *callerPC = nullptr;
1395 RootedFunction fun(cx, callee);
1396 AutoValueVector startFrameFormals(cx);
1398 RootedScript topCaller(cx);
1399 jsbytecode *topCallerPC = nullptr;
1401 while (true) {
1402 MOZ_ASSERT(snapIter.instruction()->isResumePoint());
1404 if (frameNo > 0) {
1405 TraceLogStartEvent(logger, TraceLogCreateTextId(logger, scr));
1406 TraceLogStartEvent(logger, TraceLogger::Baseline);
1407 }
1409 IonSpew(IonSpew_BaselineBailouts, " FrameNo %d", frameNo);
1411 // If we are bailing out to a catch or finally block in this frame,
1412 // pass excInfo to InitFromBailout and don't unpack any other frames.
1413 bool handleException = (catchingException && excInfo->frameNo() == frameNo);
1415 // We also need to pass excInfo if we're bailing out in place for
1416 // debug mode.
1417 bool passExcInfo = handleException || propagatingExceptionForDebugMode;
1419 jsbytecode *callPC = nullptr;
1420 RootedFunction nextCallee(cx, nullptr);
1421 if (!InitFromBailout(cx, caller, callerPC, fun, scr, iter.ionScript(),
1422 snapIter, invalidate, builder, startFrameFormals,
1423 &nextCallee, &callPC, passExcInfo ? excInfo : nullptr))
1424 {
1425 return BAILOUT_RETURN_FATAL_ERROR;
1426 }
1428 if (!snapIter.moreFrames()) {
1429 JS_ASSERT(!callPC);
1430 break;
1431 }
1433 if (handleException)
1434 break;
1436 JS_ASSERT(nextCallee);
1437 JS_ASSERT(callPC);
1438 caller = scr;
1439 callerPC = callPC;
1440 fun = nextCallee;
1441 scr = fun->existingScriptForInlinedFunction();
1443 // Save top caller info for adjusting SPS frames later.
1444 if (!topCaller) {
1445 JS_ASSERT(frameNo == 0);
1446 topCaller = caller;
1447 topCallerPC = callerPC;
1448 }
1450 frameNo++;
1452 snapIter.nextInstruction();
1453 }
1454 IonSpew(IonSpew_BaselineBailouts, " Done restoring frames");
1456 // If there were multiple inline frames unpacked, and inline frame profiling
1457 // is off, then the current top SPS frame is for the outermost caller, and
1458 // has an uninitialized PC. Initialize it now.
1459 if (frameNo > 0 && !js_JitOptions.profileInlineFrames)
1460 cx->runtime()->spsProfiler.updatePC(topCaller, topCallerPC);
1462 BailoutKind bailoutKind = snapIter.bailoutKind();
1464 if (!startFrameFormals.empty()) {
1465 // Set the first frame's formals, see the comment in InitFromBailout.
1466 Value *argv = builder.startFrame()->argv() + 1; // +1 to skip |this|.
1467 mozilla::PodCopy(argv, startFrameFormals.begin(), startFrameFormals.length());
1468 }
1470 // Do stack check.
1471 bool overRecursed = false;
1472 BaselineBailoutInfo *info = builder.info();
1473 uint8_t *newsp = info->incomingStack - (info->copyStackTop - info->copyStackBottom);
1474 #ifdef JS_ARM_SIMULATOR
1475 if (Simulator::Current()->overRecursed(uintptr_t(newsp)))
1476 overRecursed = true;
1477 #else
1478 JS_CHECK_RECURSION_WITH_SP_DONT_REPORT(cx, newsp, overRecursed = true);
1479 #endif
1480 if (overRecursed) {
1481 IonSpew(IonSpew_BaselineBailouts, " Overrecursion check failed!");
1482 return BAILOUT_RETURN_OVERRECURSED;
1483 }
1485 // Take the reconstructed baseline stack so it doesn't get freed when builder destructs.
1486 info = builder.takeBuffer();
1487 info->numFrames = frameNo + 1;
1488 info->bailoutKind = bailoutKind;
1489 *bailoutInfo = info;
1490 return BAILOUT_RETURN_OK;
1491 }
1493 static bool
1494 HandleBoundsCheckFailure(JSContext *cx, HandleScript outerScript, HandleScript innerScript)
1495 {
1496 IonSpew(IonSpew_Bailouts, "Bounds check failure %s:%d, inlined into %s:%d",
1497 innerScript->filename(), innerScript->lineno(),
1498 outerScript->filename(), outerScript->lineno());
1500 JS_ASSERT(!outerScript->ionScript()->invalidated());
1502 // TODO: Currently this mimic's Ion's handling of this case. Investigate setting
1503 // the flag on innerScript as opposed to outerScript, and maybe invalidating both
1504 // inner and outer scripts, instead of just the outer one.
1505 if (!outerScript->failedBoundsCheck())
1506 outerScript->setFailedBoundsCheck();
1507 IonSpew(IonSpew_BaselineBailouts, "Invalidating due to bounds check failure");
1508 return Invalidate(cx, outerScript);
1509 }
1511 static bool
1512 HandleShapeGuardFailure(JSContext *cx, HandleScript outerScript, HandleScript innerScript)
1513 {
1514 IonSpew(IonSpew_Bailouts, "Shape guard failure %s:%d, inlined into %s:%d",
1515 innerScript->filename(), innerScript->lineno(),
1516 outerScript->filename(), outerScript->lineno());
1518 JS_ASSERT(!outerScript->ionScript()->invalidated());
1520 // TODO: Currently this mimic's Ion's handling of this case. Investigate setting
1521 // the flag on innerScript as opposed to outerScript, and maybe invalidating both
1522 // inner and outer scripts, instead of just the outer one.
1523 outerScript->setFailedShapeGuard();
1524 IonSpew(IonSpew_BaselineBailouts, "Invalidating due to shape guard failure");
1525 return Invalidate(cx, outerScript);
1526 }
1528 static bool
1529 HandleBaselineInfoBailout(JSContext *cx, JSScript *outerScript, JSScript *innerScript)
1530 {
1531 IonSpew(IonSpew_Bailouts, "Baseline info failure %s:%d, inlined into %s:%d",
1532 innerScript->filename(), innerScript->lineno(),
1533 outerScript->filename(), outerScript->lineno());
1535 JS_ASSERT(!outerScript->ionScript()->invalidated());
1537 IonSpew(IonSpew_BaselineBailouts, "Invalidating due to invalid baseline info");
1538 return Invalidate(cx, outerScript);
1539 }
1541 static bool
1542 CopyFromRematerializedFrame(JSContext *cx, JitActivation *act, uint8_t *fp, size_t inlineDepth,
1543 BaselineFrame *frame)
1544 {
1545 RematerializedFrame *rematFrame = act->lookupRematerializedFrame(fp, inlineDepth);
1547 // We might not have rematerialized a frame if the user never requested a
1548 // Debugger.Frame for it.
1549 if (!rematFrame)
1550 return true;
1552 MOZ_ASSERT(rematFrame->script() == frame->script());
1553 MOZ_ASSERT(rematFrame->numActualArgs() == frame->numActualArgs());
1555 frame->setScopeChain(rematFrame->scopeChain());
1556 frame->thisValue() = rematFrame->thisValue();
1558 for (unsigned i = 0; i < frame->numActualArgs(); i++)
1559 frame->argv()[i] = rematFrame->argv()[i];
1561 for (size_t i = 0; i < frame->script()->nfixed(); i++)
1562 *frame->valueSlot(i) = rematFrame->locals()[i];
1564 IonSpew(IonSpew_BaselineBailouts,
1565 " Copied from rematerialized frame at (%p,%u)",
1566 fp, inlineDepth);
1568 if (cx->compartment()->debugMode())
1569 return Debugger::handleIonBailout(cx, rematFrame, frame);
1571 return true;
1572 }
1574 uint32_t
1575 jit::FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo)
1576 {
1577 // The caller pushes R0 and R1 on the stack without rooting them.
1578 // Since GC here is very unlikely just suppress it.
1579 JSContext *cx = GetJSContextFromJitCode();
1580 js::gc::AutoSuppressGC suppressGC(cx);
1582 IonSpew(IonSpew_BaselineBailouts, " Done restoring frames");
1584 // Check that we can get the current script's PC.
1585 #ifdef DEBUG
1586 jsbytecode *pc;
1587 cx->currentScript(&pc);
1588 IonSpew(IonSpew_BaselineBailouts, " Got pc=%p", pc);
1589 #endif
1591 uint32_t numFrames = bailoutInfo->numFrames;
1592 JS_ASSERT(numFrames > 0);
1593 BailoutKind bailoutKind = bailoutInfo->bailoutKind;
1595 // Free the bailout buffer.
1596 js_free(bailoutInfo);
1597 bailoutInfo = nullptr;
1599 // Ensure the frame has a call object if it needs one. If the scope chain
1600 // is nullptr, we will enter baseline code at the prologue so no need to do
1601 // anything in that case.
1602 BaselineFrame *topFrame = GetTopBaselineFrame(cx);
1603 if (topFrame->scopeChain() && !EnsureHasScopeObjects(cx, topFrame))
1604 return false;
1606 // Create arguments objects for bailed out frames, to maintain the invariant
1607 // that script->needsArgsObj() implies frame->hasArgsObj().
1608 RootedScript innerScript(cx, nullptr);
1609 RootedScript outerScript(cx, nullptr);
1611 JS_ASSERT(cx->currentlyRunningInJit());
1612 JitFrameIterator iter(cx);
1613 uint8_t *outerFp = nullptr;
1615 uint32_t frameno = 0;
1616 while (frameno < numFrames) {
1617 JS_ASSERT(!iter.isIonJS());
1619 if (iter.isBaselineJS()) {
1620 BaselineFrame *frame = iter.baselineFrame();
1621 MOZ_ASSERT(frame->script()->hasBaselineScript());
1623 // If the frame doesn't even have a scope chain set yet, then it's resuming
1624 // into the the prologue before the scope chain is initialized. Any
1625 // necessary args object will also be initialized there.
1626 if (frame->scopeChain() && frame->script()->needsArgsObj()) {
1627 ArgumentsObject *argsObj;
1628 if (frame->hasArgsObj()) {
1629 argsObj = &frame->argsObj();
1630 } else {
1631 argsObj = ArgumentsObject::createExpected(cx, frame);
1632 if (!argsObj)
1633 return false;
1634 }
1636 // The arguments is a local binding and needsArgsObj does not
1637 // check if it is clobbered. Ensure that the local binding
1638 // restored during bailout before storing the arguments object
1639 // to the slot.
1640 RootedScript script(cx, frame->script());
1641 SetFrameArgumentsObject(cx, frame, script, argsObj);
1642 }
1644 if (frameno == 0)
1645 innerScript = frame->script();
1647 if (frameno == numFrames - 1) {
1648 outerScript = frame->script();
1649 outerFp = iter.fp();
1650 }
1652 frameno++;
1653 }
1655 ++iter;
1656 }
1658 MOZ_ASSERT(innerScript);
1659 MOZ_ASSERT(outerScript);
1660 MOZ_ASSERT(outerFp);
1662 // If we rematerialized Ion frames due to debug mode toggling, copy their
1663 // values into the baseline frame. We need to do this even when debug mode
1664 // is off, as we should respect the mutations made while debug mode was
1665 // on.
1666 JitActivation *act = cx->mainThread().activation()->asJit();
1667 if (act->hasRematerializedFrame(outerFp)) {
1668 JitFrameIterator iter(cx);
1669 size_t inlineDepth = numFrames;
1670 while (inlineDepth > 0) {
1671 if (iter.isBaselineJS() &&
1672 !CopyFromRematerializedFrame(cx, act, outerFp, --inlineDepth,
1673 iter.baselineFrame()))
1674 {
1675 return false;
1676 }
1677 ++iter;
1678 }
1680 // After copying from all the rematerialized frames, remove them from
1681 // the table to keep the table up to date.
1682 act->removeRematerializedFrame(outerFp);
1683 }
1685 IonSpew(IonSpew_BaselineBailouts,
1686 " Restored outerScript=(%s:%u,%u) innerScript=(%s:%u,%u) (bailoutKind=%u)",
1687 outerScript->filename(), outerScript->lineno(), outerScript->getUseCount(),
1688 innerScript->filename(), innerScript->lineno(), innerScript->getUseCount(),
1689 (unsigned) bailoutKind);
1691 switch (bailoutKind) {
1692 case Bailout_Normal:
1693 // Do nothing.
1694 break;
1695 case Bailout_ArgumentCheck:
1696 // Do nothing, bailout will resume before the argument monitor ICs.
1697 break;
1698 case Bailout_BoundsCheck:
1699 if (!HandleBoundsCheckFailure(cx, outerScript, innerScript))
1700 return false;
1701 break;
1702 case Bailout_ShapeGuard:
1703 if (!HandleShapeGuardFailure(cx, outerScript, innerScript))
1704 return false;
1705 break;
1706 case Bailout_BaselineInfo:
1707 if (!HandleBaselineInfoBailout(cx, outerScript, innerScript))
1708 return false;
1709 break;
1710 case Bailout_IonExceptionDebugMode:
1711 // Return false to resume in HandleException with reconstructed
1712 // baseline frame.
1713 return false;
1714 default:
1715 MOZ_ASSUME_UNREACHABLE("Unknown bailout kind!");
1716 }
1718 if (!CheckFrequentBailouts(cx, outerScript))
1719 return false;
1721 return true;
1722 }