|
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 "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" |
|
15 |
|
16 #include "vm/ArgumentsObject.h" |
|
17 #include "vm/Debugger.h" |
|
18 #include "vm/TraceLogging.h" |
|
19 |
|
20 #include "jsscriptinlines.h" |
|
21 |
|
22 #include "jit/IonFrames-inl.h" |
|
23 |
|
24 using namespace js; |
|
25 using namespace js::jit; |
|
26 |
|
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_; |
|
36 |
|
37 public: |
|
38 BufferPointer(BaselineBailoutInfo **header, size_t offset, bool heap) |
|
39 : header_(header), offset_(offset), heap_(heap) |
|
40 { } |
|
41 |
|
42 T *get() const { |
|
43 BaselineBailoutInfo *header = *header_; |
|
44 if (!heap_) |
|
45 return (T*)(header->incomingStack + offset_); |
|
46 |
|
47 uint8_t *p = header->copyStackTop - offset_; |
|
48 JS_ASSERT(p >= header->copyStackBottom && p < header->copyStackTop); |
|
49 return (T*)p; |
|
50 } |
|
51 |
|
52 T &operator*() const { return *get(); } |
|
53 T *operator->() const { return get(); } |
|
54 }; |
|
55 |
|
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_; |
|
78 |
|
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_; |
|
87 |
|
88 size_t framePushed_; |
|
89 |
|
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 } |
|
102 |
|
103 ~BaselineStackBuilder() { |
|
104 js_free(buffer_); |
|
105 } |
|
106 |
|
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; |
|
115 |
|
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 } |
|
130 |
|
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_); |
|
145 |
|
146 header_ = reinterpret_cast<BaselineBailoutInfo *>(buffer_); |
|
147 header_->copyStackTop = buffer_ + bufferTotal_; |
|
148 header_->copyStackBottom = header_->copyStackTop - bufferUsed_; |
|
149 return true; |
|
150 } |
|
151 |
|
152 BaselineBailoutInfo *info() { |
|
153 JS_ASSERT(header_ == reinterpret_cast<BaselineBailoutInfo *>(buffer_)); |
|
154 return header_; |
|
155 } |
|
156 |
|
157 BaselineBailoutInfo *takeBuffer() { |
|
158 JS_ASSERT(header_ == reinterpret_cast<BaselineBailoutInfo *>(buffer_)); |
|
159 buffer_ = nullptr; |
|
160 return header_; |
|
161 } |
|
162 |
|
163 void resetFramePushed() { |
|
164 framePushed_ = 0; |
|
165 } |
|
166 |
|
167 size_t framePushed() const { |
|
168 return framePushed_; |
|
169 } |
|
170 |
|
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 } |
|
177 |
|
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 } |
|
190 |
|
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 } |
|
198 |
|
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 } |
|
209 |
|
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 } |
|
226 |
|
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 } |
|
238 |
|
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 } |
|
249 |
|
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 } |
|
267 |
|
268 void setResumeFramePtr(void *resumeFramePtr) { |
|
269 header_->resumeFramePtr = resumeFramePtr; |
|
270 } |
|
271 |
|
272 void setResumeAddr(void *resumeAddr) { |
|
273 header_->resumeAddr = resumeAddr; |
|
274 } |
|
275 |
|
276 void setMonitorStub(ICStub *stub) { |
|
277 header_->monitorStub = stub; |
|
278 } |
|
279 |
|
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 } |
|
287 |
|
288 return BufferPointer<T>(&header_, offset - bufferUsed_, /* heap = */ false); |
|
289 } |
|
290 |
|
291 BufferPointer<Value> valuePointerAtStackOffset(size_t offset) { |
|
292 return pointerAtStackOffset<Value>(offset); |
|
293 } |
|
294 |
|
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 } |
|
300 |
|
301 inline IonJSFrameLayout *startFrame() { |
|
302 return frame_; |
|
303 } |
|
304 |
|
305 BufferPointer<IonJSFrameLayout> topFrameAddress() { |
|
306 return pointerAtStackOffset<IonJSFrameLayout>(0); |
|
307 } |
|
308 |
|
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(); |
|
324 |
|
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; |
|
330 |
|
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 } |
|
340 |
|
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); |
|
359 |
|
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; |
|
364 |
|
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 }; |
|
378 |
|
379 static inline bool |
|
380 IsInlinableFallback(ICFallbackStub *icEntry) |
|
381 { |
|
382 return icEntry->isCall_Fallback() || icEntry->isGetProp_Fallback() || |
|
383 icEntry->isSetProp_Fallback(); |
|
384 } |
|
385 |
|
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 } |
|
397 |
|
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 } |
|
408 |
|
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()); |
|
489 |
|
490 // Are we catching an exception? |
|
491 bool catchingException = excInfo && excInfo->catchingException(); |
|
492 |
|
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)); |
|
502 |
|
503 builder.resetFramePushed(); |
|
504 |
|
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 // +===============+ |
|
528 |
|
529 IonSpew(IonSpew_BaselineBailouts, " Unpacking %s:%d", script->filename(), script->lineno()); |
|
530 IonSpew(IonSpew_BaselineBailouts, " [BASELINE-JS FRAME]"); |
|
531 |
|
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); |
|
540 |
|
541 // Write struct BaselineFrame. |
|
542 if (!builder.subtract(BaselineFrame::Size(), "BaselineFrame")) |
|
543 return false; |
|
544 BufferPointer<BaselineFrame> blFrame = builder.pointerAtStackOffset<BaselineFrame>(0); |
|
545 |
|
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); |
|
551 |
|
552 uint32_t flags = 0; |
|
553 |
|
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 } |
|
566 |
|
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(); |
|
579 |
|
580 // skip |return value| |
|
581 iter.skip(); |
|
582 returnValue = UndefinedValue(); |
|
583 |
|
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)); |
|
599 |
|
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 } |
|
618 |
|
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; |
|
623 |
|
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); |
|
636 |
|
637 // Do not need to initialize scratchValue field in BaselineFrame. |
|
638 blFrame->setFlags(flags); |
|
639 |
|
640 // initArgsObjUnchecked modifies the frame's flags, so call it after setFlags. |
|
641 if (argsObj) |
|
642 blFrame->initArgsObjUnchecked(*argsObj); |
|
643 |
|
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)); |
|
650 |
|
651 size_t thisvOffset = builder.framePushed() + IonJSFrameLayout::offsetOfThis(); |
|
652 *builder.valuePointerAtStackOffset(thisvOffset) = thisv; |
|
653 |
|
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()); |
|
657 |
|
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 } |
|
668 |
|
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 } |
|
681 |
|
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 } |
|
687 |
|
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(); |
|
692 |
|
693 JSOp op = JSOp(*pc); |
|
694 |
|
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); |
|
709 |
|
710 JS_ASSERT(exprStackSlots >= inlined_args); |
|
711 pushedSlots = exprStackSlots - inlined_args; |
|
712 |
|
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 } |
|
721 |
|
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 } |
|
732 |
|
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. |
|
737 |
|
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(); |
|
762 |
|
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 } |
|
778 |
|
779 IonSpew(IonSpew_BaselineBailouts, " pushing %u expression stack slots", |
|
780 exprStackSlots - pushedSlots); |
|
781 for (uint32_t i = pushedSlots; i < exprStackSlots; i++) { |
|
782 Value v; |
|
783 |
|
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 } |
|
814 |
|
815 size_t endOfBaselineJSFrameStack = builder.framePushed(); |
|
816 |
|
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 } |
|
833 |
|
834 uint32_t pcOff = script->pcToOffset(pc); |
|
835 bool isCall = IsCallPC(pc); |
|
836 BaselineScript *baselineScript = script->baselineScript(); |
|
837 |
|
838 #ifdef DEBUG |
|
839 uint32_t expectedDepth; |
|
840 bool reachablePC; |
|
841 if (!ReconstructStackDepth(cx, script, resumeAfter ? GetNextPc(pc) : pc, &expectedDepth, &reachablePC)) |
|
842 return false; |
|
843 |
|
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 } |
|
870 |
|
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 |
|
877 |
|
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; |
|
883 |
|
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 } |
|
897 |
|
898 uint32_t numCallArgs = isCall ? GET_ARGC(pc) : 0; |
|
899 |
|
900 if (resumeAfter && !enterMonitorChain) |
|
901 pc = GetNextPc(pc); |
|
902 |
|
903 builder.setResumeFramePtr(prevFramePtr); |
|
904 |
|
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(); |
|
912 |
|
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); |
|
916 |
|
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); |
|
923 |
|
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"); |
|
933 |
|
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 } |
|
939 |
|
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); |
|
946 |
|
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 } |
|
968 |
|
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)); |
|
975 |
|
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."); |
|
986 |
|
987 // If bailing into prologue, HAS_PUSHED_SPS_FRAME should not be set on frame. |
|
988 blFrame->unsetPushedSPSFrame(); |
|
989 |
|
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); |
|
1011 |
|
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 } |
|
1017 |
|
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 } |
|
1048 |
|
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 } |
|
1059 |
|
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 } |
|
1078 |
|
1079 return true; |
|
1080 } |
|
1081 |
|
1082 *callPC = pc; |
|
1083 |
|
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; |
|
1089 |
|
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; |
|
1096 |
|
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 // +===============+ |
|
1119 |
|
1120 IonSpew(IonSpew_BaselineBailouts, " [BASELINE-STUB FRAME]"); |
|
1121 |
|
1122 size_t startOfBaselineStubFrame = builder.framePushed(); |
|
1123 |
|
1124 // Write stub pointer. |
|
1125 JS_ASSERT(IsInlinableFallback(icEntry.fallbackStub())); |
|
1126 if (!builder.writePtr(icEntry.fallbackStub(), "StubPtr")) |
|
1127 return false; |
|
1128 |
|
1129 // Write previous frame pointer (saved earlier). |
|
1130 if (!builder.writePtr(prevFramePtr, "PrevFramePtr")) |
|
1131 return false; |
|
1132 prevFramePtr = builder.virtualPointerAtStackOffset(0); |
|
1133 |
|
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); |
|
1145 |
|
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 } |
|
1159 |
|
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 } |
|
1167 |
|
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(); |
|
1171 |
|
1172 // Calculate frame size for descriptor. |
|
1173 size_t baselineStubFrameSize = builder.framePushed() - startOfBaselineStubFrame; |
|
1174 size_t baselineStubFrameDescr = MakeFrameDescriptor((uint32_t) baselineStubFrameSize, |
|
1175 JitFrame_BaselineStub); |
|
1176 |
|
1177 // Push actual argc |
|
1178 if (!builder.writeWord(actualArgc, "ActualArgc")) |
|
1179 return false; |
|
1180 |
|
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); |
|
1200 |
|
1201 // Push BaselineStub frame descriptor |
|
1202 if (!builder.writeWord(baselineStubFrameDescr, "Descriptor")) |
|
1203 return false; |
|
1204 |
|
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; |
|
1210 |
|
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; |
|
1215 |
|
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 // +===============+ |
|
1240 |
|
1241 IonSpew(IonSpew_BaselineBailouts, " [RECTIFIER FRAME]"); |
|
1242 |
|
1243 size_t startOfRectifierFrame = builder.framePushed(); |
|
1244 |
|
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 |
|
1250 |
|
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 } |
|
1256 |
|
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)); |
|
1265 |
|
1266 // Calculate frame size for descriptor. |
|
1267 size_t rectifierFrameSize = builder.framePushed() - startOfRectifierFrame; |
|
1268 size_t rectifierFrameDescr = MakeFrameDescriptor((uint32_t) rectifierFrameSize, |
|
1269 JitFrame_Rectifier); |
|
1270 |
|
1271 // Push actualArgc |
|
1272 if (!builder.writeWord(actualArgc, "ActualArgc")) |
|
1273 return false; |
|
1274 |
|
1275 // Push calleeToken again. |
|
1276 if (!builder.writePtr(CalleeToToken(calleeFun), "CalleeToken")) |
|
1277 return false; |
|
1278 |
|
1279 // Push rectifier frame descriptor |
|
1280 if (!builder.writeWord(rectifierFrameDescr, "Descriptor")) |
|
1281 return false; |
|
1282 |
|
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; |
|
1289 |
|
1290 return true; |
|
1291 } |
|
1292 |
|
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); |
|
1300 |
|
1301 TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); |
|
1302 TraceLogStopEvent(logger, TraceLogger::IonMonkey); |
|
1303 TraceLogStartEvent(logger, TraceLogger::Baseline); |
|
1304 |
|
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); |
|
1316 |
|
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 // +---------------+ |
|
1339 |
|
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); |
|
1343 |
|
1344 bool catchingException; |
|
1345 bool propagatingExceptionForDebugMode; |
|
1346 if (excInfo) { |
|
1347 catchingException = excInfo->catchingException(); |
|
1348 propagatingExceptionForDebugMode = excInfo->propagatingIonExceptionForDebugMode(); |
|
1349 |
|
1350 if (catchingException) |
|
1351 IonSpew(IonSpew_BaselineBailouts, "Resuming in catch or finally block"); |
|
1352 |
|
1353 if (propagatingExceptionForDebugMode) |
|
1354 IonSpew(IonSpew_BaselineBailouts, "Resuming in-place for debug mode"); |
|
1355 } else { |
|
1356 catchingException = false; |
|
1357 propagatingExceptionForDebugMode = false; |
|
1358 } |
|
1359 |
|
1360 IonSpew(IonSpew_BaselineBailouts, " Reading from snapshot offset %u size %u", |
|
1361 iter.snapshotOffset(), iter.ionScript()->snapshotsListSize()); |
|
1362 |
|
1363 if (!excInfo) |
|
1364 iter.ionScript()->incNumBailouts(); |
|
1365 iter.script()->updateBaselineOrIonRaw(); |
|
1366 |
|
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()); |
|
1372 |
|
1373 SnapshotIterator snapIter(iter); |
|
1374 |
|
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 } |
|
1383 |
|
1384 if (iter.isConstructing()) |
|
1385 IonSpew(IonSpew_BaselineBailouts, " Constructing!"); |
|
1386 else |
|
1387 IonSpew(IonSpew_BaselineBailouts, " Not constructing!"); |
|
1388 |
|
1389 IonSpew(IonSpew_BaselineBailouts, " Restoring frames:"); |
|
1390 size_t frameNo = 0; |
|
1391 |
|
1392 // Reconstruct baseline frames using the builder. |
|
1393 RootedScript caller(cx); |
|
1394 jsbytecode *callerPC = nullptr; |
|
1395 RootedFunction fun(cx, callee); |
|
1396 AutoValueVector startFrameFormals(cx); |
|
1397 |
|
1398 RootedScript topCaller(cx); |
|
1399 jsbytecode *topCallerPC = nullptr; |
|
1400 |
|
1401 while (true) { |
|
1402 MOZ_ASSERT(snapIter.instruction()->isResumePoint()); |
|
1403 |
|
1404 if (frameNo > 0) { |
|
1405 TraceLogStartEvent(logger, TraceLogCreateTextId(logger, scr)); |
|
1406 TraceLogStartEvent(logger, TraceLogger::Baseline); |
|
1407 } |
|
1408 |
|
1409 IonSpew(IonSpew_BaselineBailouts, " FrameNo %d", frameNo); |
|
1410 |
|
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); |
|
1414 |
|
1415 // We also need to pass excInfo if we're bailing out in place for |
|
1416 // debug mode. |
|
1417 bool passExcInfo = handleException || propagatingExceptionForDebugMode; |
|
1418 |
|
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 } |
|
1427 |
|
1428 if (!snapIter.moreFrames()) { |
|
1429 JS_ASSERT(!callPC); |
|
1430 break; |
|
1431 } |
|
1432 |
|
1433 if (handleException) |
|
1434 break; |
|
1435 |
|
1436 JS_ASSERT(nextCallee); |
|
1437 JS_ASSERT(callPC); |
|
1438 caller = scr; |
|
1439 callerPC = callPC; |
|
1440 fun = nextCallee; |
|
1441 scr = fun->existingScriptForInlinedFunction(); |
|
1442 |
|
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 } |
|
1449 |
|
1450 frameNo++; |
|
1451 |
|
1452 snapIter.nextInstruction(); |
|
1453 } |
|
1454 IonSpew(IonSpew_BaselineBailouts, " Done restoring frames"); |
|
1455 |
|
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); |
|
1461 |
|
1462 BailoutKind bailoutKind = snapIter.bailoutKind(); |
|
1463 |
|
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 } |
|
1469 |
|
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 } |
|
1484 |
|
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 } |
|
1492 |
|
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()); |
|
1499 |
|
1500 JS_ASSERT(!outerScript->ionScript()->invalidated()); |
|
1501 |
|
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 } |
|
1510 |
|
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()); |
|
1517 |
|
1518 JS_ASSERT(!outerScript->ionScript()->invalidated()); |
|
1519 |
|
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 } |
|
1527 |
|
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()); |
|
1534 |
|
1535 JS_ASSERT(!outerScript->ionScript()->invalidated()); |
|
1536 |
|
1537 IonSpew(IonSpew_BaselineBailouts, "Invalidating due to invalid baseline info"); |
|
1538 return Invalidate(cx, outerScript); |
|
1539 } |
|
1540 |
|
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); |
|
1546 |
|
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; |
|
1551 |
|
1552 MOZ_ASSERT(rematFrame->script() == frame->script()); |
|
1553 MOZ_ASSERT(rematFrame->numActualArgs() == frame->numActualArgs()); |
|
1554 |
|
1555 frame->setScopeChain(rematFrame->scopeChain()); |
|
1556 frame->thisValue() = rematFrame->thisValue(); |
|
1557 |
|
1558 for (unsigned i = 0; i < frame->numActualArgs(); i++) |
|
1559 frame->argv()[i] = rematFrame->argv()[i]; |
|
1560 |
|
1561 for (size_t i = 0; i < frame->script()->nfixed(); i++) |
|
1562 *frame->valueSlot(i) = rematFrame->locals()[i]; |
|
1563 |
|
1564 IonSpew(IonSpew_BaselineBailouts, |
|
1565 " Copied from rematerialized frame at (%p,%u)", |
|
1566 fp, inlineDepth); |
|
1567 |
|
1568 if (cx->compartment()->debugMode()) |
|
1569 return Debugger::handleIonBailout(cx, rematFrame, frame); |
|
1570 |
|
1571 return true; |
|
1572 } |
|
1573 |
|
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); |
|
1581 |
|
1582 IonSpew(IonSpew_BaselineBailouts, " Done restoring frames"); |
|
1583 |
|
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 |
|
1590 |
|
1591 uint32_t numFrames = bailoutInfo->numFrames; |
|
1592 JS_ASSERT(numFrames > 0); |
|
1593 BailoutKind bailoutKind = bailoutInfo->bailoutKind; |
|
1594 |
|
1595 // Free the bailout buffer. |
|
1596 js_free(bailoutInfo); |
|
1597 bailoutInfo = nullptr; |
|
1598 |
|
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; |
|
1605 |
|
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); |
|
1610 |
|
1611 JS_ASSERT(cx->currentlyRunningInJit()); |
|
1612 JitFrameIterator iter(cx); |
|
1613 uint8_t *outerFp = nullptr; |
|
1614 |
|
1615 uint32_t frameno = 0; |
|
1616 while (frameno < numFrames) { |
|
1617 JS_ASSERT(!iter.isIonJS()); |
|
1618 |
|
1619 if (iter.isBaselineJS()) { |
|
1620 BaselineFrame *frame = iter.baselineFrame(); |
|
1621 MOZ_ASSERT(frame->script()->hasBaselineScript()); |
|
1622 |
|
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 } |
|
1635 |
|
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 } |
|
1643 |
|
1644 if (frameno == 0) |
|
1645 innerScript = frame->script(); |
|
1646 |
|
1647 if (frameno == numFrames - 1) { |
|
1648 outerScript = frame->script(); |
|
1649 outerFp = iter.fp(); |
|
1650 } |
|
1651 |
|
1652 frameno++; |
|
1653 } |
|
1654 |
|
1655 ++iter; |
|
1656 } |
|
1657 |
|
1658 MOZ_ASSERT(innerScript); |
|
1659 MOZ_ASSERT(outerScript); |
|
1660 MOZ_ASSERT(outerFp); |
|
1661 |
|
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 } |
|
1679 |
|
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 } |
|
1684 |
|
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); |
|
1690 |
|
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 } |
|
1717 |
|
1718 if (!CheckFrequentBailouts(cx, outerScript)) |
|
1719 return false; |
|
1720 |
|
1721 return true; |
|
1722 } |