|
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 "jit/IonFrames-inl.h" |
|
8 |
|
9 #include "jsfun.h" |
|
10 #include "jsobj.h" |
|
11 #include "jsscript.h" |
|
12 |
|
13 #include "gc/Marking.h" |
|
14 #include "jit/BaselineDebugModeOSR.h" |
|
15 #include "jit/BaselineFrame.h" |
|
16 #include "jit/BaselineIC.h" |
|
17 #include "jit/BaselineJIT.h" |
|
18 #include "jit/Ion.h" |
|
19 #include "jit/IonMacroAssembler.h" |
|
20 #include "jit/IonSpewer.h" |
|
21 #include "jit/JitCompartment.h" |
|
22 #include "jit/ParallelFunctions.h" |
|
23 #include "jit/PcScriptCache.h" |
|
24 #include "jit/Recover.h" |
|
25 #include "jit/Safepoints.h" |
|
26 #include "jit/Snapshots.h" |
|
27 #include "jit/VMFunctions.h" |
|
28 #include "vm/ArgumentsObject.h" |
|
29 #include "vm/ForkJoin.h" |
|
30 #include "vm/Interpreter.h" |
|
31 |
|
32 #include "jsscriptinlines.h" |
|
33 #include "jit/JitFrameIterator-inl.h" |
|
34 #include "vm/Probes-inl.h" |
|
35 |
|
36 namespace js { |
|
37 namespace jit { |
|
38 |
|
39 // Given a slot index, returns the offset, in bytes, of that slot from an |
|
40 // IonJSFrameLayout. Slot distances are uniform across architectures, however, |
|
41 // the distance does depend on the size of the frame header. |
|
42 static inline int32_t |
|
43 OffsetOfFrameSlot(int32_t slot) |
|
44 { |
|
45 return -slot; |
|
46 } |
|
47 |
|
48 static inline uintptr_t |
|
49 ReadFrameSlot(IonJSFrameLayout *fp, int32_t slot) |
|
50 { |
|
51 return *(uintptr_t *)((char *)fp + OffsetOfFrameSlot(slot)); |
|
52 } |
|
53 |
|
54 static inline double |
|
55 ReadFrameDoubleSlot(IonJSFrameLayout *fp, int32_t slot) |
|
56 { |
|
57 return *(double *)((char *)fp + OffsetOfFrameSlot(slot)); |
|
58 } |
|
59 |
|
60 static inline float |
|
61 ReadFrameFloat32Slot(IonJSFrameLayout *fp, int32_t slot) |
|
62 { |
|
63 return *(float *)((char *)fp + OffsetOfFrameSlot(slot)); |
|
64 } |
|
65 |
|
66 static inline int32_t |
|
67 ReadFrameInt32Slot(IonJSFrameLayout *fp, int32_t slot) |
|
68 { |
|
69 return *(int32_t *)((char *)fp + OffsetOfFrameSlot(slot)); |
|
70 } |
|
71 |
|
72 static inline bool |
|
73 ReadFrameBooleanSlot(IonJSFrameLayout *fp, int32_t slot) |
|
74 { |
|
75 return *(bool *)((char *)fp + OffsetOfFrameSlot(slot)); |
|
76 } |
|
77 |
|
78 JitFrameIterator::JitFrameIterator(JSContext *cx) |
|
79 : current_(cx->mainThread().ionTop), |
|
80 type_(JitFrame_Exit), |
|
81 returnAddressToFp_(nullptr), |
|
82 frameSize_(0), |
|
83 cachedSafepointIndex_(nullptr), |
|
84 activation_(nullptr), |
|
85 mode_(SequentialExecution) |
|
86 { |
|
87 } |
|
88 |
|
89 JitFrameIterator::JitFrameIterator(const ActivationIterator &activations) |
|
90 : current_(activations.jitTop()), |
|
91 type_(JitFrame_Exit), |
|
92 returnAddressToFp_(nullptr), |
|
93 frameSize_(0), |
|
94 cachedSafepointIndex_(nullptr), |
|
95 activation_(activations->asJit()), |
|
96 mode_(SequentialExecution) |
|
97 { |
|
98 } |
|
99 |
|
100 JitFrameIterator::JitFrameIterator(IonJSFrameLayout *fp, ExecutionMode mode) |
|
101 : current_((uint8_t *)fp), |
|
102 type_(JitFrame_IonJS), |
|
103 returnAddressToFp_(fp->returnAddress()), |
|
104 frameSize_(fp->prevFrameLocalSize()), |
|
105 mode_(mode) |
|
106 { |
|
107 } |
|
108 |
|
109 bool |
|
110 JitFrameIterator::checkInvalidation() const |
|
111 { |
|
112 IonScript *dummy; |
|
113 return checkInvalidation(&dummy); |
|
114 } |
|
115 |
|
116 bool |
|
117 JitFrameIterator::checkInvalidation(IonScript **ionScriptOut) const |
|
118 { |
|
119 uint8_t *returnAddr = returnAddressToFp(); |
|
120 JSScript *script = this->script(); |
|
121 // N.B. the current IonScript is not the same as the frame's |
|
122 // IonScript if the frame has since been invalidated. |
|
123 bool invalidated; |
|
124 if (mode_ == ParallelExecution) { |
|
125 // Parallel execution does not have invalidating bailouts. |
|
126 invalidated = false; |
|
127 } else { |
|
128 invalidated = !script->hasIonScript() || |
|
129 !script->ionScript()->containsReturnAddress(returnAddr); |
|
130 } |
|
131 if (!invalidated) |
|
132 return false; |
|
133 |
|
134 int32_t invalidationDataOffset = ((int32_t *) returnAddr)[-1]; |
|
135 uint8_t *ionScriptDataOffset = returnAddr + invalidationDataOffset; |
|
136 IonScript *ionScript = (IonScript *) Assembler::getPointer(ionScriptDataOffset); |
|
137 JS_ASSERT(ionScript->containsReturnAddress(returnAddr)); |
|
138 *ionScriptOut = ionScript; |
|
139 return true; |
|
140 } |
|
141 |
|
142 CalleeToken |
|
143 JitFrameIterator::calleeToken() const |
|
144 { |
|
145 return ((IonJSFrameLayout *) current_)->calleeToken(); |
|
146 } |
|
147 |
|
148 JSFunction * |
|
149 JitFrameIterator::callee() const |
|
150 { |
|
151 JS_ASSERT(isScripted()); |
|
152 JS_ASSERT(isFunctionFrame()); |
|
153 return CalleeTokenToFunction(calleeToken()); |
|
154 } |
|
155 |
|
156 JSFunction * |
|
157 JitFrameIterator::maybeCallee() const |
|
158 { |
|
159 if (isScripted() && (isFunctionFrame())) |
|
160 return callee(); |
|
161 return nullptr; |
|
162 } |
|
163 |
|
164 bool |
|
165 JitFrameIterator::isNative() const |
|
166 { |
|
167 if (type_ != JitFrame_Exit || isFakeExitFrame()) |
|
168 return false; |
|
169 return exitFrame()->footer()->jitCode() == nullptr; |
|
170 } |
|
171 |
|
172 bool |
|
173 JitFrameIterator::isOOLNative() const |
|
174 { |
|
175 if (type_ != JitFrame_Exit) |
|
176 return false; |
|
177 return exitFrame()->footer()->jitCode() == ION_FRAME_OOL_NATIVE; |
|
178 } |
|
179 |
|
180 bool |
|
181 JitFrameIterator::isOOLPropertyOp() const |
|
182 { |
|
183 if (type_ != JitFrame_Exit) |
|
184 return false; |
|
185 return exitFrame()->footer()->jitCode() == ION_FRAME_OOL_PROPERTY_OP; |
|
186 } |
|
187 |
|
188 bool |
|
189 JitFrameIterator::isOOLProxy() const |
|
190 { |
|
191 if (type_ != JitFrame_Exit) |
|
192 return false; |
|
193 return exitFrame()->footer()->jitCode() == ION_FRAME_OOL_PROXY; |
|
194 } |
|
195 |
|
196 bool |
|
197 JitFrameIterator::isDOMExit() const |
|
198 { |
|
199 if (type_ != JitFrame_Exit) |
|
200 return false; |
|
201 return exitFrame()->isDomExit(); |
|
202 } |
|
203 |
|
204 bool |
|
205 JitFrameIterator::isFunctionFrame() const |
|
206 { |
|
207 return CalleeTokenIsFunction(calleeToken()); |
|
208 } |
|
209 |
|
210 JSScript * |
|
211 JitFrameIterator::script() const |
|
212 { |
|
213 JS_ASSERT(isScripted()); |
|
214 if (isBaselineJS()) |
|
215 return baselineFrame()->script(); |
|
216 JSScript *script = ScriptFromCalleeToken(calleeToken()); |
|
217 JS_ASSERT(script); |
|
218 return script; |
|
219 } |
|
220 |
|
221 void |
|
222 JitFrameIterator::baselineScriptAndPc(JSScript **scriptRes, jsbytecode **pcRes) const |
|
223 { |
|
224 JS_ASSERT(isBaselineJS()); |
|
225 JSScript *script = this->script(); |
|
226 if (scriptRes) |
|
227 *scriptRes = script; |
|
228 uint8_t *retAddr = returnAddressToFp(); |
|
229 |
|
230 // If we are in the middle of a recompile handler, get the real return |
|
231 // address as stashed in the RecompileInfo. |
|
232 if (BaselineDebugModeOSRInfo *info = baselineFrame()->getDebugModeOSRInfo()) |
|
233 retAddr = info->resumeAddr; |
|
234 |
|
235 if (pcRes) { |
|
236 // If the return address is into the prologue entry address or just |
|
237 // after the debug prologue, then assume start of script. |
|
238 if (retAddr == script->baselineScript()->prologueEntryAddr() || |
|
239 retAddr == script->baselineScript()->postDebugPrologueAddr()) |
|
240 { |
|
241 *pcRes = script->code(); |
|
242 return; |
|
243 } |
|
244 |
|
245 // The return address _may_ be a return from a callVM or IC chain call done for |
|
246 // some op. |
|
247 ICEntry *icEntry = script->baselineScript()->maybeICEntryFromReturnAddress(retAddr); |
|
248 if (icEntry) { |
|
249 *pcRes = icEntry->pc(script); |
|
250 return; |
|
251 } |
|
252 |
|
253 // If not, the return address _must_ be the start address of an op, which can |
|
254 // be computed from the pc mapping table. |
|
255 *pcRes = script->baselineScript()->pcForReturnAddress(script, retAddr); |
|
256 } |
|
257 } |
|
258 |
|
259 Value * |
|
260 JitFrameIterator::actualArgs() const |
|
261 { |
|
262 return jsFrame()->argv() + 1; |
|
263 } |
|
264 |
|
265 static inline size_t |
|
266 SizeOfFramePrefix(FrameType type) |
|
267 { |
|
268 switch (type) { |
|
269 case JitFrame_Entry: |
|
270 return IonEntryFrameLayout::Size(); |
|
271 case JitFrame_BaselineJS: |
|
272 case JitFrame_IonJS: |
|
273 case JitFrame_Unwound_IonJS: |
|
274 return IonJSFrameLayout::Size(); |
|
275 case JitFrame_BaselineStub: |
|
276 return IonBaselineStubFrameLayout::Size(); |
|
277 case JitFrame_Rectifier: |
|
278 return IonRectifierFrameLayout::Size(); |
|
279 case JitFrame_Unwound_Rectifier: |
|
280 return IonUnwoundRectifierFrameLayout::Size(); |
|
281 case JitFrame_Exit: |
|
282 return IonExitFrameLayout::Size(); |
|
283 default: |
|
284 MOZ_ASSUME_UNREACHABLE("unknown frame type"); |
|
285 } |
|
286 } |
|
287 |
|
288 uint8_t * |
|
289 JitFrameIterator::prevFp() const |
|
290 { |
|
291 size_t currentSize = SizeOfFramePrefix(type_); |
|
292 // This quick fix must be removed as soon as bug 717297 land. This is |
|
293 // needed because the descriptor size of JS-to-JS frame which is just after |
|
294 // a Rectifier frame should not change. (cf EnsureExitFrame function) |
|
295 if (isFakeExitFrame()) { |
|
296 JS_ASSERT(SizeOfFramePrefix(JitFrame_BaselineJS) == |
|
297 SizeOfFramePrefix(JitFrame_IonJS)); |
|
298 currentSize = SizeOfFramePrefix(JitFrame_IonJS); |
|
299 } |
|
300 currentSize += current()->prevFrameLocalSize(); |
|
301 return current_ + currentSize; |
|
302 } |
|
303 |
|
304 JitFrameIterator & |
|
305 JitFrameIterator::operator++() |
|
306 { |
|
307 JS_ASSERT(type_ != JitFrame_Entry); |
|
308 |
|
309 frameSize_ = prevFrameLocalSize(); |
|
310 cachedSafepointIndex_ = nullptr; |
|
311 |
|
312 // If the next frame is the entry frame, just exit. Don't update current_, |
|
313 // since the entry and first frames overlap. |
|
314 if (current()->prevType() == JitFrame_Entry) { |
|
315 type_ = JitFrame_Entry; |
|
316 return *this; |
|
317 } |
|
318 |
|
319 // Note: prevFp() needs the current type, so set it after computing the |
|
320 // next frame. |
|
321 uint8_t *prev = prevFp(); |
|
322 type_ = current()->prevType(); |
|
323 if (type_ == JitFrame_Unwound_IonJS) |
|
324 type_ = JitFrame_IonJS; |
|
325 else if (type_ == JitFrame_Unwound_BaselineStub) |
|
326 type_ = JitFrame_BaselineStub; |
|
327 returnAddressToFp_ = current()->returnAddress(); |
|
328 current_ = prev; |
|
329 return *this; |
|
330 } |
|
331 |
|
332 uintptr_t * |
|
333 JitFrameIterator::spillBase() const |
|
334 { |
|
335 // Get the base address to where safepoint registers are spilled. |
|
336 // Out-of-line calls do not unwind the extra padding space used to |
|
337 // aggregate bailout tables, so we use frameSize instead of frameLocals, |
|
338 // which would only account for local stack slots. |
|
339 return reinterpret_cast<uintptr_t *>(fp() - ionScript()->frameSize()); |
|
340 } |
|
341 |
|
342 MachineState |
|
343 JitFrameIterator::machineState() const |
|
344 { |
|
345 SafepointReader reader(ionScript(), safepoint()); |
|
346 uintptr_t *spill = spillBase(); |
|
347 |
|
348 MachineState machine; |
|
349 for (GeneralRegisterBackwardIterator iter(reader.allGprSpills()); iter.more(); iter++) |
|
350 machine.setRegisterLocation(*iter, --spill); |
|
351 |
|
352 double *floatSpill = reinterpret_cast<double *>(spill); |
|
353 for (FloatRegisterBackwardIterator iter(reader.allFloatSpills()); iter.more(); iter++) |
|
354 machine.setRegisterLocation(*iter, --floatSpill); |
|
355 |
|
356 return machine; |
|
357 } |
|
358 |
|
359 static void |
|
360 CloseLiveIterator(JSContext *cx, const InlineFrameIterator &frame, uint32_t localSlot) |
|
361 { |
|
362 SnapshotIterator si = frame.snapshotIterator(); |
|
363 |
|
364 // Skip stack slots until we reach the iterator object. |
|
365 uint32_t base = CountArgSlots(frame.script(), frame.maybeCallee()) + frame.script()->nfixed(); |
|
366 uint32_t skipSlots = base + localSlot - 1; |
|
367 |
|
368 for (unsigned i = 0; i < skipSlots; i++) |
|
369 si.skip(); |
|
370 |
|
371 Value v = si.read(); |
|
372 RootedObject obj(cx, &v.toObject()); |
|
373 |
|
374 if (cx->isExceptionPending()) |
|
375 UnwindIteratorForException(cx, obj); |
|
376 else |
|
377 UnwindIteratorForUncatchableException(cx, obj); |
|
378 } |
|
379 |
|
380 static void |
|
381 HandleExceptionIon(JSContext *cx, const InlineFrameIterator &frame, ResumeFromException *rfe, |
|
382 bool *overrecursed) |
|
383 { |
|
384 RootedScript script(cx, frame.script()); |
|
385 jsbytecode *pc = frame.pc(); |
|
386 |
|
387 bool bailedOutForDebugMode = false; |
|
388 if (cx->compartment()->debugMode()) { |
|
389 // If we have an exception from within Ion and the debugger is active, |
|
390 // we do the following: |
|
391 // |
|
392 // 1. Bailout to baseline to reconstruct a baseline frame. |
|
393 // 2. Resume immediately into the exception tail afterwards, and |
|
394 // handle the exception again with the top frame now a baseline |
|
395 // frame. |
|
396 // |
|
397 // An empty exception info denotes that we're propagating an Ion |
|
398 // exception due to debug mode, which BailoutIonToBaseline needs to |
|
399 // know. This is because we might not be able to fully reconstruct up |
|
400 // to the stack depth at the snapshot, as we could've thrown in the |
|
401 // middle of a call. |
|
402 ExceptionBailoutInfo propagateInfo; |
|
403 uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, propagateInfo, overrecursed); |
|
404 bailedOutForDebugMode = retval == BAILOUT_RETURN_OK; |
|
405 } |
|
406 |
|
407 if (!script->hasTrynotes()) |
|
408 return; |
|
409 |
|
410 JSTryNote *tn = script->trynotes()->vector; |
|
411 JSTryNote *tnEnd = tn + script->trynotes()->length; |
|
412 |
|
413 uint32_t pcOffset = uint32_t(pc - script->main()); |
|
414 for (; tn != tnEnd; ++tn) { |
|
415 if (pcOffset < tn->start) |
|
416 continue; |
|
417 if (pcOffset >= tn->start + tn->length) |
|
418 continue; |
|
419 |
|
420 switch (tn->kind) { |
|
421 case JSTRY_ITER: { |
|
422 JS_ASSERT(JSOp(*(script->main() + tn->start + tn->length)) == JSOP_ENDITER); |
|
423 JS_ASSERT(tn->stackDepth > 0); |
|
424 |
|
425 uint32_t localSlot = tn->stackDepth; |
|
426 CloseLiveIterator(cx, frame, localSlot); |
|
427 break; |
|
428 } |
|
429 |
|
430 case JSTRY_LOOP: |
|
431 break; |
|
432 |
|
433 case JSTRY_CATCH: |
|
434 if (cx->isExceptionPending() && !bailedOutForDebugMode) { |
|
435 // Ion can compile try-catch, but bailing out to catch |
|
436 // exceptions is slow. Reset the use count so that if we |
|
437 // catch many exceptions we won't Ion-compile the script. |
|
438 script->resetUseCount(); |
|
439 |
|
440 // Bailout at the start of the catch block. |
|
441 jsbytecode *catchPC = script->main() + tn->start + tn->length; |
|
442 ExceptionBailoutInfo excInfo(frame.frameNo(), catchPC, tn->stackDepth); |
|
443 uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, excInfo, overrecursed); |
|
444 if (retval == BAILOUT_RETURN_OK) |
|
445 return; |
|
446 |
|
447 // Error on bailout clears pending exception. |
|
448 MOZ_ASSERT(!cx->isExceptionPending()); |
|
449 } |
|
450 break; |
|
451 |
|
452 default: |
|
453 MOZ_ASSUME_UNREACHABLE("Unexpected try note"); |
|
454 } |
|
455 } |
|
456 } |
|
457 |
|
458 static void |
|
459 HandleExceptionBaseline(JSContext *cx, const JitFrameIterator &frame, ResumeFromException *rfe, |
|
460 bool *calledDebugEpilogue) |
|
461 { |
|
462 JS_ASSERT(frame.isBaselineJS()); |
|
463 JS_ASSERT(!*calledDebugEpilogue); |
|
464 |
|
465 RootedScript script(cx); |
|
466 jsbytecode *pc; |
|
467 frame.baselineScriptAndPc(script.address(), &pc); |
|
468 |
|
469 if (cx->isExceptionPending() && cx->compartment()->debugMode()) { |
|
470 BaselineFrame *baselineFrame = frame.baselineFrame(); |
|
471 JSTrapStatus status = DebugExceptionUnwind(cx, baselineFrame, pc); |
|
472 switch (status) { |
|
473 case JSTRAP_ERROR: |
|
474 // Uncatchable exception. |
|
475 JS_ASSERT(!cx->isExceptionPending()); |
|
476 break; |
|
477 |
|
478 case JSTRAP_CONTINUE: |
|
479 case JSTRAP_THROW: |
|
480 JS_ASSERT(cx->isExceptionPending()); |
|
481 break; |
|
482 |
|
483 case JSTRAP_RETURN: |
|
484 JS_ASSERT(baselineFrame->hasReturnValue()); |
|
485 if (jit::DebugEpilogue(cx, baselineFrame, pc, true)) { |
|
486 rfe->kind = ResumeFromException::RESUME_FORCED_RETURN; |
|
487 rfe->framePointer = frame.fp() - BaselineFrame::FramePointerOffset; |
|
488 rfe->stackPointer = reinterpret_cast<uint8_t *>(baselineFrame); |
|
489 return; |
|
490 } |
|
491 |
|
492 // DebugEpilogue threw an exception. Propagate to the caller frame. |
|
493 *calledDebugEpilogue = true; |
|
494 return; |
|
495 |
|
496 default: |
|
497 MOZ_ASSUME_UNREACHABLE("Invalid trap status"); |
|
498 } |
|
499 } |
|
500 |
|
501 if (!script->hasTrynotes()) |
|
502 return; |
|
503 |
|
504 JSTryNote *tn = script->trynotes()->vector; |
|
505 JSTryNote *tnEnd = tn + script->trynotes()->length; |
|
506 |
|
507 uint32_t pcOffset = uint32_t(pc - script->main()); |
|
508 ScopeIter si(frame.baselineFrame(), pc, cx); |
|
509 for (; tn != tnEnd; ++tn) { |
|
510 if (pcOffset < tn->start) |
|
511 continue; |
|
512 if (pcOffset >= tn->start + tn->length) |
|
513 continue; |
|
514 |
|
515 // Skip if the try note's stack depth exceeds the frame's stack depth. |
|
516 // See the big comment in TryNoteIter::settle for more info. |
|
517 JS_ASSERT(frame.baselineFrame()->numValueSlots() >= script->nfixed()); |
|
518 size_t stackDepth = frame.baselineFrame()->numValueSlots() - script->nfixed(); |
|
519 if (tn->stackDepth > stackDepth) |
|
520 continue; |
|
521 |
|
522 // Unwind scope chain (pop block objects). |
|
523 if (cx->isExceptionPending()) |
|
524 UnwindScope(cx, si, script->main() + tn->start); |
|
525 |
|
526 // Compute base pointer and stack pointer. |
|
527 rfe->framePointer = frame.fp() - BaselineFrame::FramePointerOffset; |
|
528 rfe->stackPointer = rfe->framePointer - BaselineFrame::Size() - |
|
529 (script->nfixed() + tn->stackDepth) * sizeof(Value); |
|
530 |
|
531 switch (tn->kind) { |
|
532 case JSTRY_CATCH: |
|
533 if (cx->isExceptionPending()) { |
|
534 // Ion can compile try-catch, but bailing out to catch |
|
535 // exceptions is slow. Reset the use count so that if we |
|
536 // catch many exceptions we won't Ion-compile the script. |
|
537 script->resetUseCount(); |
|
538 |
|
539 // Resume at the start of the catch block. |
|
540 rfe->kind = ResumeFromException::RESUME_CATCH; |
|
541 jsbytecode *catchPC = script->main() + tn->start + tn->length; |
|
542 rfe->target = script->baselineScript()->nativeCodeForPC(script, catchPC); |
|
543 return; |
|
544 } |
|
545 break; |
|
546 |
|
547 case JSTRY_FINALLY: |
|
548 if (cx->isExceptionPending()) { |
|
549 rfe->kind = ResumeFromException::RESUME_FINALLY; |
|
550 jsbytecode *finallyPC = script->main() + tn->start + tn->length; |
|
551 rfe->target = script->baselineScript()->nativeCodeForPC(script, finallyPC); |
|
552 // Drop the exception instead of leaking cross compartment data. |
|
553 if (!cx->getPendingException(MutableHandleValue::fromMarkedLocation(&rfe->exception))) |
|
554 rfe->exception = UndefinedValue(); |
|
555 cx->clearPendingException(); |
|
556 return; |
|
557 } |
|
558 break; |
|
559 |
|
560 case JSTRY_ITER: { |
|
561 Value iterValue(* (Value *) rfe->stackPointer); |
|
562 RootedObject iterObject(cx, &iterValue.toObject()); |
|
563 if (cx->isExceptionPending()) |
|
564 UnwindIteratorForException(cx, iterObject); |
|
565 else |
|
566 UnwindIteratorForUncatchableException(cx, iterObject); |
|
567 break; |
|
568 } |
|
569 |
|
570 case JSTRY_LOOP: |
|
571 break; |
|
572 |
|
573 default: |
|
574 MOZ_ASSUME_UNREACHABLE("Invalid try note"); |
|
575 } |
|
576 } |
|
577 |
|
578 } |
|
579 |
|
580 struct AutoDeleteDebugModeOSRInfo |
|
581 { |
|
582 BaselineFrame *frame; |
|
583 AutoDeleteDebugModeOSRInfo(BaselineFrame *frame) : frame(frame) { MOZ_ASSERT(frame); } |
|
584 ~AutoDeleteDebugModeOSRInfo() { frame->deleteDebugModeOSRInfo(); } |
|
585 }; |
|
586 |
|
587 void |
|
588 HandleException(ResumeFromException *rfe) |
|
589 { |
|
590 JSContext *cx = GetJSContextFromJitCode(); |
|
591 |
|
592 rfe->kind = ResumeFromException::RESUME_ENTRY_FRAME; |
|
593 |
|
594 IonSpew(IonSpew_Invalidate, "handling exception"); |
|
595 |
|
596 // Clear any Ion return override that's been set. |
|
597 // This may happen if a callVM function causes an invalidation (setting the |
|
598 // override), and then fails, bypassing the bailout handlers that would |
|
599 // otherwise clear the return override. |
|
600 if (cx->runtime()->hasIonReturnOverride()) |
|
601 cx->runtime()->takeIonReturnOverride(); |
|
602 |
|
603 JitFrameIterator iter(cx); |
|
604 while (!iter.isEntry()) { |
|
605 bool overrecursed = false; |
|
606 if (iter.isIonJS()) { |
|
607 // Search each inlined frame for live iterator objects, and close |
|
608 // them. |
|
609 InlineFrameIterator frames(cx, &iter); |
|
610 |
|
611 // Invalidation state will be the same for all inlined scripts in the frame. |
|
612 IonScript *ionScript = nullptr; |
|
613 bool invalidated = iter.checkInvalidation(&ionScript); |
|
614 |
|
615 for (;;) { |
|
616 HandleExceptionIon(cx, frames, rfe, &overrecursed); |
|
617 |
|
618 if (rfe->kind == ResumeFromException::RESUME_BAILOUT) { |
|
619 if (invalidated) |
|
620 ionScript->decref(cx->runtime()->defaultFreeOp()); |
|
621 return; |
|
622 } |
|
623 |
|
624 JS_ASSERT(rfe->kind == ResumeFromException::RESUME_ENTRY_FRAME); |
|
625 |
|
626 // Figure out whether SPS frame was pushed for this frame or not. |
|
627 // Even if profiler is enabled, the frame being popped might have |
|
628 // been entered prior to SPS being enabled, and thus not have |
|
629 // a pushed SPS frame. |
|
630 bool popSPSFrame = cx->runtime()->spsProfiler.enabled(); |
|
631 if (invalidated) |
|
632 popSPSFrame = ionScript->hasSPSInstrumentation(); |
|
633 |
|
634 // If inline-frames are not profiled, then don't pop an SPS frame |
|
635 // for them. |
|
636 if (frames.more() && !js_JitOptions.profileInlineFrames) |
|
637 popSPSFrame = false; |
|
638 |
|
639 // When profiling, each frame popped needs a notification that |
|
640 // the function has exited, so invoke the probe that a function |
|
641 // is exiting. |
|
642 JSScript *script = frames.script(); |
|
643 probes::ExitScript(cx, script, script->functionNonDelazifying(), popSPSFrame); |
|
644 if (!frames.more()) |
|
645 break; |
|
646 ++frames; |
|
647 } |
|
648 |
|
649 if (invalidated) |
|
650 ionScript->decref(cx->runtime()->defaultFreeOp()); |
|
651 |
|
652 } else if (iter.isBaselineJS()) { |
|
653 // It's invalid to call DebugEpilogue twice for the same frame. |
|
654 bool calledDebugEpilogue = false; |
|
655 |
|
656 HandleExceptionBaseline(cx, iter, rfe, &calledDebugEpilogue); |
|
657 |
|
658 // If we are propagating an exception through a frame with |
|
659 // on-stack recompile info, we should free the allocated |
|
660 // RecompileInfo struct before we leave this block, as we will not |
|
661 // be returning to the recompile handler. |
|
662 // |
|
663 // We cannot delete it immediately because of the call to |
|
664 // iter.baselineScriptAndPc below. |
|
665 AutoDeleteDebugModeOSRInfo deleteDebugModeOSRInfo(iter.baselineFrame()); |
|
666 |
|
667 if (rfe->kind != ResumeFromException::RESUME_ENTRY_FRAME) |
|
668 return; |
|
669 |
|
670 // Unwind profiler pseudo-stack |
|
671 JSScript *script = iter.script(); |
|
672 probes::ExitScript(cx, script, script->functionNonDelazifying(), |
|
673 iter.baselineFrame()->hasPushedSPSFrame()); |
|
674 // After this point, any pushed SPS frame would have been popped if it needed |
|
675 // to be. Unset the flag here so that if we call DebugEpilogue below, |
|
676 // it doesn't try to pop the SPS frame again. |
|
677 iter.baselineFrame()->unsetPushedSPSFrame(); |
|
678 |
|
679 if (cx->compartment()->debugMode() && !calledDebugEpilogue) { |
|
680 // If DebugEpilogue returns |true|, we have to perform a forced |
|
681 // return, e.g. return frame->returnValue() to the caller. |
|
682 BaselineFrame *frame = iter.baselineFrame(); |
|
683 RootedScript script(cx); |
|
684 jsbytecode *pc; |
|
685 iter.baselineScriptAndPc(script.address(), &pc); |
|
686 if (jit::DebugEpilogue(cx, frame, pc, false)) { |
|
687 JS_ASSERT(frame->hasReturnValue()); |
|
688 rfe->kind = ResumeFromException::RESUME_FORCED_RETURN; |
|
689 rfe->framePointer = iter.fp() - BaselineFrame::FramePointerOffset; |
|
690 rfe->stackPointer = reinterpret_cast<uint8_t *>(frame); |
|
691 return; |
|
692 } |
|
693 } |
|
694 } |
|
695 |
|
696 IonJSFrameLayout *current = iter.isScripted() ? iter.jsFrame() : nullptr; |
|
697 |
|
698 ++iter; |
|
699 |
|
700 if (current) { |
|
701 // Unwind the frame by updating ionTop. This is necessary so that |
|
702 // (1) debugger exception unwind and leave frame hooks don't see this |
|
703 // frame when they use ScriptFrameIter, and (2) ScriptFrameIter does |
|
704 // not crash when accessing an IonScript that's destroyed by the |
|
705 // ionScript->decref call. |
|
706 EnsureExitFrame(current); |
|
707 cx->mainThread().ionTop = (uint8_t *)current; |
|
708 } |
|
709 |
|
710 if (overrecursed) { |
|
711 // We hit an overrecursion error during bailout. Report it now. |
|
712 js_ReportOverRecursed(cx); |
|
713 } |
|
714 } |
|
715 |
|
716 rfe->stackPointer = iter.fp(); |
|
717 } |
|
718 |
|
719 void |
|
720 HandleParallelFailure(ResumeFromException *rfe) |
|
721 { |
|
722 ForkJoinContext *cx = ForkJoinContext::current(); |
|
723 JitFrameIterator iter(cx->perThreadData->ionTop, ParallelExecution); |
|
724 |
|
725 parallel::Spew(parallel::SpewBailouts, "Bailing from VM reentry"); |
|
726 |
|
727 while (!iter.isEntry()) { |
|
728 if (iter.isScripted()) { |
|
729 cx->bailoutRecord->updateCause(ParallelBailoutUnsupportedVM, |
|
730 iter.script(), iter.script(), nullptr); |
|
731 break; |
|
732 } |
|
733 ++iter; |
|
734 } |
|
735 |
|
736 while (!iter.isEntry()) { |
|
737 if (iter.isScripted()) |
|
738 PropagateAbortPar(iter.script(), iter.script()); |
|
739 ++iter; |
|
740 } |
|
741 |
|
742 rfe->kind = ResumeFromException::RESUME_ENTRY_FRAME; |
|
743 rfe->stackPointer = iter.fp(); |
|
744 } |
|
745 |
|
746 void |
|
747 EnsureExitFrame(IonCommonFrameLayout *frame) |
|
748 { |
|
749 if (frame->prevType() == JitFrame_Unwound_IonJS || |
|
750 frame->prevType() == JitFrame_Unwound_BaselineStub || |
|
751 frame->prevType() == JitFrame_Unwound_Rectifier) |
|
752 { |
|
753 // Already an exit frame, nothing to do. |
|
754 return; |
|
755 } |
|
756 |
|
757 if (frame->prevType() == JitFrame_Entry) { |
|
758 // The previous frame type is the entry frame, so there's no actual |
|
759 // need for an exit frame. |
|
760 return; |
|
761 } |
|
762 |
|
763 if (frame->prevType() == JitFrame_Rectifier) { |
|
764 // The rectifier code uses the frame descriptor to discard its stack, |
|
765 // so modifying its descriptor size here would be dangerous. Instead, |
|
766 // we change the frame type, and teach the stack walking code how to |
|
767 // deal with this edge case. bug 717297 would obviate the need |
|
768 frame->changePrevType(JitFrame_Unwound_Rectifier); |
|
769 return; |
|
770 } |
|
771 |
|
772 if (frame->prevType() == JitFrame_BaselineStub) { |
|
773 frame->changePrevType(JitFrame_Unwound_BaselineStub); |
|
774 return; |
|
775 } |
|
776 |
|
777 JS_ASSERT(frame->prevType() == JitFrame_IonJS); |
|
778 frame->changePrevType(JitFrame_Unwound_IonJS); |
|
779 } |
|
780 |
|
781 CalleeToken |
|
782 MarkCalleeToken(JSTracer *trc, CalleeToken token) |
|
783 { |
|
784 switch (GetCalleeTokenTag(token)) { |
|
785 case CalleeToken_Function: |
|
786 { |
|
787 JSFunction *fun = CalleeTokenToFunction(token); |
|
788 MarkObjectRoot(trc, &fun, "ion-callee"); |
|
789 return CalleeToToken(fun); |
|
790 } |
|
791 case CalleeToken_Script: |
|
792 { |
|
793 JSScript *script = CalleeTokenToScript(token); |
|
794 MarkScriptRoot(trc, &script, "ion-entry"); |
|
795 return CalleeToToken(script); |
|
796 } |
|
797 default: |
|
798 MOZ_ASSUME_UNREACHABLE("unknown callee token type"); |
|
799 } |
|
800 } |
|
801 |
|
802 #ifdef JS_NUNBOX32 |
|
803 static inline uintptr_t |
|
804 ReadAllocation(const JitFrameIterator &frame, const LAllocation *a) |
|
805 { |
|
806 if (a->isGeneralReg()) { |
|
807 Register reg = a->toGeneralReg()->reg(); |
|
808 return frame.machineState().read(reg); |
|
809 } |
|
810 if (a->isStackSlot()) { |
|
811 uint32_t slot = a->toStackSlot()->slot(); |
|
812 return *frame.jsFrame()->slotRef(slot); |
|
813 } |
|
814 uint32_t index = a->toArgument()->index(); |
|
815 uint8_t *argv = reinterpret_cast<uint8_t *>(frame.jsFrame()->argv()); |
|
816 return *reinterpret_cast<uintptr_t *>(argv + index); |
|
817 } |
|
818 #endif |
|
819 |
|
820 static void |
|
821 MarkActualArguments(JSTracer *trc, const JitFrameIterator &frame) |
|
822 { |
|
823 IonJSFrameLayout *layout = frame.jsFrame(); |
|
824 JS_ASSERT(CalleeTokenIsFunction(layout->calleeToken())); |
|
825 |
|
826 size_t nargs = frame.numActualArgs(); |
|
827 |
|
828 // Trace function arguments. Note + 1 for thisv. |
|
829 Value *argv = layout->argv(); |
|
830 for (size_t i = 0; i < nargs + 1; i++) |
|
831 gc::MarkValueRoot(trc, &argv[i], "ion-argv"); |
|
832 } |
|
833 |
|
834 #ifdef JS_NUNBOX32 |
|
835 static inline void |
|
836 WriteAllocation(const JitFrameIterator &frame, const LAllocation *a, uintptr_t value) |
|
837 { |
|
838 if (a->isGeneralReg()) { |
|
839 Register reg = a->toGeneralReg()->reg(); |
|
840 frame.machineState().write(reg, value); |
|
841 return; |
|
842 } |
|
843 if (a->isStackSlot()) { |
|
844 uint32_t slot = a->toStackSlot()->slot(); |
|
845 *frame.jsFrame()->slotRef(slot) = value; |
|
846 return; |
|
847 } |
|
848 uint32_t index = a->toArgument()->index(); |
|
849 uint8_t *argv = reinterpret_cast<uint8_t *>(frame.jsFrame()->argv()); |
|
850 *reinterpret_cast<uintptr_t *>(argv + index) = value; |
|
851 } |
|
852 #endif |
|
853 |
|
854 static void |
|
855 MarkIonJSFrame(JSTracer *trc, const JitFrameIterator &frame) |
|
856 { |
|
857 IonJSFrameLayout *layout = (IonJSFrameLayout *)frame.fp(); |
|
858 |
|
859 layout->replaceCalleeToken(MarkCalleeToken(trc, layout->calleeToken())); |
|
860 |
|
861 IonScript *ionScript = nullptr; |
|
862 if (frame.checkInvalidation(&ionScript)) { |
|
863 // This frame has been invalidated, meaning that its IonScript is no |
|
864 // longer reachable through the callee token (JSFunction/JSScript->ion |
|
865 // is now nullptr or recompiled). Manually trace it here. |
|
866 IonScript::Trace(trc, ionScript); |
|
867 } else if (CalleeTokenIsFunction(layout->calleeToken())) { |
|
868 ionScript = CalleeTokenToFunction(layout->calleeToken())->nonLazyScript()->ionScript(); |
|
869 } else { |
|
870 ionScript = CalleeTokenToScript(layout->calleeToken())->ionScript(); |
|
871 } |
|
872 |
|
873 if (CalleeTokenIsFunction(layout->calleeToken())) |
|
874 MarkActualArguments(trc, frame); |
|
875 |
|
876 const SafepointIndex *si = ionScript->getSafepointIndex(frame.returnAddressToFp()); |
|
877 |
|
878 SafepointReader safepoint(ionScript, si); |
|
879 |
|
880 // Scan through slots which contain pointers (or on punboxing systems, |
|
881 // actual values). |
|
882 uint32_t slot; |
|
883 while (safepoint.getGcSlot(&slot)) { |
|
884 uintptr_t *ref = layout->slotRef(slot); |
|
885 gc::MarkGCThingRoot(trc, reinterpret_cast<void **>(ref), "ion-gc-slot"); |
|
886 } |
|
887 |
|
888 while (safepoint.getValueSlot(&slot)) { |
|
889 Value *v = (Value *)layout->slotRef(slot); |
|
890 gc::MarkValueRoot(trc, v, "ion-gc-slot"); |
|
891 } |
|
892 |
|
893 uintptr_t *spill = frame.spillBase(); |
|
894 GeneralRegisterSet gcRegs = safepoint.gcSpills(); |
|
895 GeneralRegisterSet valueRegs = safepoint.valueSpills(); |
|
896 for (GeneralRegisterBackwardIterator iter(safepoint.allGprSpills()); iter.more(); iter++) { |
|
897 --spill; |
|
898 if (gcRegs.has(*iter)) |
|
899 gc::MarkGCThingRoot(trc, reinterpret_cast<void **>(spill), "ion-gc-spill"); |
|
900 else if (valueRegs.has(*iter)) |
|
901 gc::MarkValueRoot(trc, reinterpret_cast<Value *>(spill), "ion-value-spill"); |
|
902 } |
|
903 |
|
904 #ifdef JS_NUNBOX32 |
|
905 LAllocation type, payload; |
|
906 while (safepoint.getNunboxSlot(&type, &payload)) { |
|
907 jsval_layout layout; |
|
908 layout.s.tag = (JSValueTag)ReadAllocation(frame, &type); |
|
909 layout.s.payload.uintptr = ReadAllocation(frame, &payload); |
|
910 |
|
911 Value v = IMPL_TO_JSVAL(layout); |
|
912 gc::MarkValueRoot(trc, &v, "ion-torn-value"); |
|
913 |
|
914 if (v != IMPL_TO_JSVAL(layout)) { |
|
915 // GC moved the value, replace the stored payload. |
|
916 layout = JSVAL_TO_IMPL(v); |
|
917 WriteAllocation(frame, &payload, layout.s.payload.uintptr); |
|
918 } |
|
919 } |
|
920 #endif |
|
921 } |
|
922 |
|
923 #ifdef JSGC_GENERATIONAL |
|
924 static void |
|
925 UpdateIonJSFrameForMinorGC(JSTracer *trc, const JitFrameIterator &frame) |
|
926 { |
|
927 // Minor GCs may move slots/elements allocated in the nursery. Update |
|
928 // any slots/elements pointers stored in this frame. |
|
929 |
|
930 IonJSFrameLayout *layout = (IonJSFrameLayout *)frame.fp(); |
|
931 |
|
932 IonScript *ionScript = nullptr; |
|
933 if (frame.checkInvalidation(&ionScript)) { |
|
934 // This frame has been invalidated, meaning that its IonScript is no |
|
935 // longer reachable through the callee token (JSFunction/JSScript->ion |
|
936 // is now nullptr or recompiled). |
|
937 } else if (CalleeTokenIsFunction(layout->calleeToken())) { |
|
938 ionScript = CalleeTokenToFunction(layout->calleeToken())->nonLazyScript()->ionScript(); |
|
939 } else { |
|
940 ionScript = CalleeTokenToScript(layout->calleeToken())->ionScript(); |
|
941 } |
|
942 |
|
943 const SafepointIndex *si = ionScript->getSafepointIndex(frame.returnAddressToFp()); |
|
944 SafepointReader safepoint(ionScript, si); |
|
945 |
|
946 GeneralRegisterSet slotsRegs = safepoint.slotsOrElementsSpills(); |
|
947 uintptr_t *spill = frame.spillBase(); |
|
948 for (GeneralRegisterBackwardIterator iter(safepoint.allGprSpills()); iter.more(); iter++) { |
|
949 --spill; |
|
950 if (slotsRegs.has(*iter)) |
|
951 trc->runtime()->gcNursery.forwardBufferPointer(reinterpret_cast<HeapSlot **>(spill)); |
|
952 } |
|
953 |
|
954 // Skip to the right place in the safepoint |
|
955 uint32_t slot; |
|
956 while (safepoint.getGcSlot(&slot)); |
|
957 while (safepoint.getValueSlot(&slot)); |
|
958 #ifdef JS_NUNBOX32 |
|
959 LAllocation type, payload; |
|
960 while (safepoint.getNunboxSlot(&type, &payload)); |
|
961 #endif |
|
962 |
|
963 while (safepoint.getSlotsOrElementsSlot(&slot)) { |
|
964 HeapSlot **slots = reinterpret_cast<HeapSlot **>(layout->slotRef(slot)); |
|
965 trc->runtime()->gcNursery.forwardBufferPointer(slots); |
|
966 } |
|
967 } |
|
968 #endif |
|
969 |
|
970 static void |
|
971 MarkBaselineStubFrame(JSTracer *trc, const JitFrameIterator &frame) |
|
972 { |
|
973 // Mark the ICStub pointer stored in the stub frame. This is necessary |
|
974 // so that we don't destroy the stub code after unlinking the stub. |
|
975 |
|
976 JS_ASSERT(frame.type() == JitFrame_BaselineStub); |
|
977 IonBaselineStubFrameLayout *layout = (IonBaselineStubFrameLayout *)frame.fp(); |
|
978 |
|
979 if (ICStub *stub = layout->maybeStubPtr()) { |
|
980 JS_ASSERT(ICStub::CanMakeCalls(stub->kind())); |
|
981 stub->trace(trc); |
|
982 } |
|
983 } |
|
984 |
|
985 void |
|
986 JitActivationIterator::jitStackRange(uintptr_t *&min, uintptr_t *&end) |
|
987 { |
|
988 JitFrameIterator frames(jitTop(), SequentialExecution); |
|
989 |
|
990 if (frames.isFakeExitFrame()) { |
|
991 min = reinterpret_cast<uintptr_t *>(frames.fp()); |
|
992 } else { |
|
993 IonExitFrameLayout *exitFrame = frames.exitFrame(); |
|
994 IonExitFooterFrame *footer = exitFrame->footer(); |
|
995 const VMFunction *f = footer->function(); |
|
996 if (exitFrame->isWrapperExit() && f->outParam == Type_Handle) { |
|
997 switch (f->outParamRootType) { |
|
998 case VMFunction::RootNone: |
|
999 MOZ_ASSUME_UNREACHABLE("Handle outparam must have root type"); |
|
1000 case VMFunction::RootObject: |
|
1001 case VMFunction::RootString: |
|
1002 case VMFunction::RootPropertyName: |
|
1003 case VMFunction::RootFunction: |
|
1004 case VMFunction::RootCell: |
|
1005 // These are all handles to GCThing pointers. |
|
1006 min = reinterpret_cast<uintptr_t *>(footer->outParam<void *>()); |
|
1007 break; |
|
1008 case VMFunction::RootValue: |
|
1009 min = reinterpret_cast<uintptr_t *>(footer->outParam<Value>()); |
|
1010 break; |
|
1011 } |
|
1012 } else { |
|
1013 min = reinterpret_cast<uintptr_t *>(footer); |
|
1014 } |
|
1015 } |
|
1016 |
|
1017 while (!frames.done()) |
|
1018 ++frames; |
|
1019 |
|
1020 end = reinterpret_cast<uintptr_t *>(frames.prevFp()); |
|
1021 } |
|
1022 |
|
1023 static void |
|
1024 MarkJitExitFrame(JSTracer *trc, const JitFrameIterator &frame) |
|
1025 { |
|
1026 // Ignore fake exit frames created by EnsureExitFrame. |
|
1027 if (frame.isFakeExitFrame()) |
|
1028 return; |
|
1029 |
|
1030 IonExitFooterFrame *footer = frame.exitFrame()->footer(); |
|
1031 |
|
1032 // Mark the code of the code handling the exit path. This is needed because |
|
1033 // invalidated script are no longer marked because data are erased by the |
|
1034 // invalidation and relocation data are no longer reliable. So the VM |
|
1035 // wrapper or the invalidation code may be GC if no JitCode keep reference |
|
1036 // on them. |
|
1037 JS_ASSERT(uintptr_t(footer->jitCode()) != uintptr_t(-1)); |
|
1038 |
|
1039 // This correspond to the case where we have build a fake exit frame in |
|
1040 // CodeGenerator.cpp which handle the case of a native function call. We |
|
1041 // need to mark the argument vector of the function call. |
|
1042 if (frame.isNative()) { |
|
1043 IonNativeExitFrameLayout *native = frame.exitFrame()->nativeExit(); |
|
1044 size_t len = native->argc() + 2; |
|
1045 Value *vp = native->vp(); |
|
1046 gc::MarkValueRootRange(trc, len, vp, "ion-native-args"); |
|
1047 return; |
|
1048 } |
|
1049 |
|
1050 if (frame.isOOLNative()) { |
|
1051 IonOOLNativeExitFrameLayout *oolnative = frame.exitFrame()->oolNativeExit(); |
|
1052 gc::MarkJitCodeRoot(trc, oolnative->stubCode(), "ion-ool-native-code"); |
|
1053 gc::MarkValueRoot(trc, oolnative->vp(), "iol-ool-native-vp"); |
|
1054 size_t len = oolnative->argc() + 1; |
|
1055 gc::MarkValueRootRange(trc, len, oolnative->thisp(), "ion-ool-native-thisargs"); |
|
1056 return; |
|
1057 } |
|
1058 |
|
1059 if (frame.isOOLPropertyOp()) { |
|
1060 IonOOLPropertyOpExitFrameLayout *oolgetter = frame.exitFrame()->oolPropertyOpExit(); |
|
1061 gc::MarkJitCodeRoot(trc, oolgetter->stubCode(), "ion-ool-property-op-code"); |
|
1062 gc::MarkValueRoot(trc, oolgetter->vp(), "ion-ool-property-op-vp"); |
|
1063 gc::MarkIdRoot(trc, oolgetter->id(), "ion-ool-property-op-id"); |
|
1064 gc::MarkObjectRoot(trc, oolgetter->obj(), "ion-ool-property-op-obj"); |
|
1065 return; |
|
1066 } |
|
1067 |
|
1068 if (frame.isOOLProxy()) { |
|
1069 IonOOLProxyExitFrameLayout *oolproxy = frame.exitFrame()->oolProxyExit(); |
|
1070 gc::MarkJitCodeRoot(trc, oolproxy->stubCode(), "ion-ool-proxy-code"); |
|
1071 gc::MarkValueRoot(trc, oolproxy->vp(), "ion-ool-proxy-vp"); |
|
1072 gc::MarkIdRoot(trc, oolproxy->id(), "ion-ool-proxy-id"); |
|
1073 gc::MarkObjectRoot(trc, oolproxy->proxy(), "ion-ool-proxy-proxy"); |
|
1074 gc::MarkObjectRoot(trc, oolproxy->receiver(), "ion-ool-proxy-receiver"); |
|
1075 return; |
|
1076 } |
|
1077 |
|
1078 if (frame.isDOMExit()) { |
|
1079 IonDOMExitFrameLayout *dom = frame.exitFrame()->DOMExit(); |
|
1080 gc::MarkObjectRoot(trc, dom->thisObjAddress(), "ion-dom-args"); |
|
1081 if (dom->isMethodFrame()) { |
|
1082 IonDOMMethodExitFrameLayout *method = |
|
1083 reinterpret_cast<IonDOMMethodExitFrameLayout *>(dom); |
|
1084 size_t len = method->argc() + 2; |
|
1085 Value *vp = method->vp(); |
|
1086 gc::MarkValueRootRange(trc, len, vp, "ion-dom-args"); |
|
1087 } else { |
|
1088 gc::MarkValueRoot(trc, dom->vp(), "ion-dom-args"); |
|
1089 } |
|
1090 return; |
|
1091 } |
|
1092 |
|
1093 MarkJitCodeRoot(trc, footer->addressOfJitCode(), "ion-exit-code"); |
|
1094 |
|
1095 const VMFunction *f = footer->function(); |
|
1096 if (f == nullptr) |
|
1097 return; |
|
1098 |
|
1099 // Mark arguments of the VM wrapper. |
|
1100 uint8_t *argBase = frame.exitFrame()->argBase(); |
|
1101 for (uint32_t explicitArg = 0; explicitArg < f->explicitArgs; explicitArg++) { |
|
1102 switch (f->argRootType(explicitArg)) { |
|
1103 case VMFunction::RootNone: |
|
1104 break; |
|
1105 case VMFunction::RootObject: { |
|
1106 // Sometimes we can bake in HandleObjects to nullptr. |
|
1107 JSObject **pobj = reinterpret_cast<JSObject **>(argBase); |
|
1108 if (*pobj) |
|
1109 gc::MarkObjectRoot(trc, pobj, "ion-vm-args"); |
|
1110 break; |
|
1111 } |
|
1112 case VMFunction::RootString: |
|
1113 case VMFunction::RootPropertyName: |
|
1114 gc::MarkStringRoot(trc, reinterpret_cast<JSString**>(argBase), "ion-vm-args"); |
|
1115 break; |
|
1116 case VMFunction::RootFunction: |
|
1117 gc::MarkObjectRoot(trc, reinterpret_cast<JSFunction**>(argBase), "ion-vm-args"); |
|
1118 break; |
|
1119 case VMFunction::RootValue: |
|
1120 gc::MarkValueRoot(trc, reinterpret_cast<Value*>(argBase), "ion-vm-args"); |
|
1121 break; |
|
1122 case VMFunction::RootCell: |
|
1123 gc::MarkGCThingRoot(trc, reinterpret_cast<void **>(argBase), "ion-vm-args"); |
|
1124 break; |
|
1125 } |
|
1126 |
|
1127 switch (f->argProperties(explicitArg)) { |
|
1128 case VMFunction::WordByValue: |
|
1129 case VMFunction::WordByRef: |
|
1130 argBase += sizeof(void *); |
|
1131 break; |
|
1132 case VMFunction::DoubleByValue: |
|
1133 case VMFunction::DoubleByRef: |
|
1134 argBase += 2 * sizeof(void *); |
|
1135 break; |
|
1136 } |
|
1137 } |
|
1138 |
|
1139 if (f->outParam == Type_Handle) { |
|
1140 switch (f->outParamRootType) { |
|
1141 case VMFunction::RootNone: |
|
1142 MOZ_ASSUME_UNREACHABLE("Handle outparam must have root type"); |
|
1143 case VMFunction::RootObject: |
|
1144 gc::MarkObjectRoot(trc, footer->outParam<JSObject *>(), "ion-vm-out"); |
|
1145 break; |
|
1146 case VMFunction::RootString: |
|
1147 case VMFunction::RootPropertyName: |
|
1148 gc::MarkStringRoot(trc, footer->outParam<JSString *>(), "ion-vm-out"); |
|
1149 break; |
|
1150 case VMFunction::RootFunction: |
|
1151 gc::MarkObjectRoot(trc, footer->outParam<JSFunction *>(), "ion-vm-out"); |
|
1152 break; |
|
1153 case VMFunction::RootValue: |
|
1154 gc::MarkValueRoot(trc, footer->outParam<Value>(), "ion-vm-outvp"); |
|
1155 break; |
|
1156 case VMFunction::RootCell: |
|
1157 gc::MarkGCThingRoot(trc, footer->outParam<void *>(), "ion-vm-out"); |
|
1158 break; |
|
1159 } |
|
1160 } |
|
1161 } |
|
1162 |
|
1163 static void |
|
1164 MarkRectifierFrame(JSTracer *trc, const JitFrameIterator &frame) |
|
1165 { |
|
1166 // Mark thisv. |
|
1167 // |
|
1168 // Baseline JIT code generated as part of the ICCall_Fallback stub may use |
|
1169 // it if we're calling a constructor that returns a primitive value. |
|
1170 IonRectifierFrameLayout *layout = (IonRectifierFrameLayout *)frame.fp(); |
|
1171 gc::MarkValueRoot(trc, &layout->argv()[0], "ion-thisv"); |
|
1172 } |
|
1173 |
|
1174 static void |
|
1175 MarkJitActivation(JSTracer *trc, const JitActivationIterator &activations) |
|
1176 { |
|
1177 JitActivation *activation = activations->asJit(); |
|
1178 |
|
1179 #ifdef CHECK_OSIPOINT_REGISTERS |
|
1180 if (js_JitOptions.checkOsiPointRegisters) { |
|
1181 // GC can modify spilled registers, breaking our register checks. |
|
1182 // To handle this, we disable these checks for the current VM call |
|
1183 // when a GC happens. |
|
1184 activation->setCheckRegs(false); |
|
1185 } |
|
1186 #endif |
|
1187 |
|
1188 activation->markRematerializedFrames(trc); |
|
1189 |
|
1190 for (JitFrameIterator frames(activations); !frames.done(); ++frames) { |
|
1191 switch (frames.type()) { |
|
1192 case JitFrame_Exit: |
|
1193 MarkJitExitFrame(trc, frames); |
|
1194 break; |
|
1195 case JitFrame_BaselineJS: |
|
1196 frames.baselineFrame()->trace(trc, frames); |
|
1197 break; |
|
1198 case JitFrame_BaselineStub: |
|
1199 MarkBaselineStubFrame(trc, frames); |
|
1200 break; |
|
1201 case JitFrame_IonJS: |
|
1202 MarkIonJSFrame(trc, frames); |
|
1203 break; |
|
1204 case JitFrame_Unwound_IonJS: |
|
1205 MOZ_ASSUME_UNREACHABLE("invalid"); |
|
1206 case JitFrame_Rectifier: |
|
1207 MarkRectifierFrame(trc, frames); |
|
1208 break; |
|
1209 case JitFrame_Unwound_Rectifier: |
|
1210 break; |
|
1211 default: |
|
1212 MOZ_ASSUME_UNREACHABLE("unexpected frame type"); |
|
1213 } |
|
1214 } |
|
1215 } |
|
1216 |
|
1217 void |
|
1218 MarkJitActivations(JSRuntime *rt, JSTracer *trc) |
|
1219 { |
|
1220 for (JitActivationIterator activations(rt); !activations.done(); ++activations) |
|
1221 MarkJitActivation(trc, activations); |
|
1222 } |
|
1223 |
|
1224 #ifdef JSGC_GENERATIONAL |
|
1225 void |
|
1226 UpdateJitActivationsForMinorGC(JSRuntime *rt, JSTracer *trc) |
|
1227 { |
|
1228 JS_ASSERT(trc->runtime()->isHeapMinorCollecting()); |
|
1229 for (JitActivationIterator activations(rt); !activations.done(); ++activations) { |
|
1230 for (JitFrameIterator frames(activations); !frames.done(); ++frames) { |
|
1231 if (frames.type() == JitFrame_IonJS) |
|
1232 UpdateIonJSFrameForMinorGC(trc, frames); |
|
1233 } |
|
1234 } |
|
1235 } |
|
1236 #endif |
|
1237 |
|
1238 void |
|
1239 AutoTempAllocatorRooter::trace(JSTracer *trc) |
|
1240 { |
|
1241 for (CompilerRootNode *root = temp->rootList(); root != nullptr; root = root->next) |
|
1242 gc::MarkGCThingRoot(trc, root->address(), "ion-compiler-root"); |
|
1243 } |
|
1244 |
|
1245 void |
|
1246 GetPcScript(JSContext *cx, JSScript **scriptRes, jsbytecode **pcRes) |
|
1247 { |
|
1248 IonSpew(IonSpew_Snapshots, "Recover PC & Script from the last frame."); |
|
1249 |
|
1250 JSRuntime *rt = cx->runtime(); |
|
1251 |
|
1252 // Recover the return address. |
|
1253 JitFrameIterator it(rt->mainThread.ionTop, SequentialExecution); |
|
1254 |
|
1255 // If the previous frame is a rectifier frame (maybe unwound), |
|
1256 // skip past it. |
|
1257 if (it.prevType() == JitFrame_Rectifier || it.prevType() == JitFrame_Unwound_Rectifier) { |
|
1258 ++it; |
|
1259 JS_ASSERT(it.prevType() == JitFrame_BaselineStub || |
|
1260 it.prevType() == JitFrame_BaselineJS || |
|
1261 it.prevType() == JitFrame_IonJS); |
|
1262 } |
|
1263 |
|
1264 // If the previous frame is a stub frame, skip the exit frame so that |
|
1265 // returnAddress below gets the return address into the BaselineJS |
|
1266 // frame. |
|
1267 if (it.prevType() == JitFrame_BaselineStub || it.prevType() == JitFrame_Unwound_BaselineStub) { |
|
1268 ++it; |
|
1269 JS_ASSERT(it.prevType() == JitFrame_BaselineJS); |
|
1270 } |
|
1271 |
|
1272 uint8_t *retAddr = it.returnAddress(); |
|
1273 uint32_t hash = PcScriptCache::Hash(retAddr); |
|
1274 JS_ASSERT(retAddr != nullptr); |
|
1275 |
|
1276 // Lazily initialize the cache. The allocation may safely fail and will not GC. |
|
1277 if (MOZ_UNLIKELY(rt->ionPcScriptCache == nullptr)) { |
|
1278 rt->ionPcScriptCache = (PcScriptCache *)js_malloc(sizeof(struct PcScriptCache)); |
|
1279 if (rt->ionPcScriptCache) |
|
1280 rt->ionPcScriptCache->clear(rt->gcNumber); |
|
1281 } |
|
1282 |
|
1283 // Attempt to lookup address in cache. |
|
1284 if (rt->ionPcScriptCache && rt->ionPcScriptCache->get(rt, hash, retAddr, scriptRes, pcRes)) |
|
1285 return; |
|
1286 |
|
1287 // Lookup failed: undertake expensive process to recover the innermost inlined frame. |
|
1288 ++it; // Skip exit frame. |
|
1289 jsbytecode *pc = nullptr; |
|
1290 |
|
1291 if (it.isIonJS()) { |
|
1292 InlineFrameIterator ifi(cx, &it); |
|
1293 *scriptRes = ifi.script(); |
|
1294 pc = ifi.pc(); |
|
1295 } else { |
|
1296 JS_ASSERT(it.isBaselineJS()); |
|
1297 it.baselineScriptAndPc(scriptRes, &pc); |
|
1298 } |
|
1299 |
|
1300 if (pcRes) |
|
1301 *pcRes = pc; |
|
1302 |
|
1303 // Add entry to cache. |
|
1304 if (rt->ionPcScriptCache) |
|
1305 rt->ionPcScriptCache->add(hash, retAddr, pc, *scriptRes); |
|
1306 } |
|
1307 |
|
1308 void |
|
1309 OsiIndex::fixUpOffset(MacroAssembler &masm) |
|
1310 { |
|
1311 callPointDisplacement_ = masm.actualOffset(callPointDisplacement_); |
|
1312 } |
|
1313 |
|
1314 uint32_t |
|
1315 OsiIndex::returnPointDisplacement() const |
|
1316 { |
|
1317 // In general, pointer arithmetic on code is bad, but in this case, |
|
1318 // getting the return address from a call instruction, stepping over pools |
|
1319 // would be wrong. |
|
1320 return callPointDisplacement_ + Assembler::patchWrite_NearCallSize(); |
|
1321 } |
|
1322 |
|
1323 SnapshotIterator::SnapshotIterator(IonScript *ionScript, SnapshotOffset snapshotOffset, |
|
1324 IonJSFrameLayout *fp, const MachineState &machine) |
|
1325 : snapshot_(ionScript->snapshots(), |
|
1326 snapshotOffset, |
|
1327 ionScript->snapshotsRVATableSize(), |
|
1328 ionScript->snapshotsListSize()), |
|
1329 recover_(snapshot_, |
|
1330 ionScript->recovers(), |
|
1331 ionScript->recoversSize()), |
|
1332 fp_(fp), |
|
1333 machine_(machine), |
|
1334 ionScript_(ionScript) |
|
1335 { |
|
1336 JS_ASSERT(snapshotOffset < ionScript->snapshotsListSize()); |
|
1337 } |
|
1338 |
|
1339 SnapshotIterator::SnapshotIterator(const JitFrameIterator &iter) |
|
1340 : snapshot_(iter.ionScript()->snapshots(), |
|
1341 iter.osiIndex()->snapshotOffset(), |
|
1342 iter.ionScript()->snapshotsRVATableSize(), |
|
1343 iter.ionScript()->snapshotsListSize()), |
|
1344 recover_(snapshot_, |
|
1345 iter.ionScript()->recovers(), |
|
1346 iter.ionScript()->recoversSize()), |
|
1347 fp_(iter.jsFrame()), |
|
1348 machine_(iter.machineState()), |
|
1349 ionScript_(iter.ionScript()) |
|
1350 { |
|
1351 } |
|
1352 |
|
1353 SnapshotIterator::SnapshotIterator() |
|
1354 : snapshot_(nullptr, 0, 0, 0), |
|
1355 recover_(snapshot_, nullptr, 0), |
|
1356 fp_(nullptr), |
|
1357 ionScript_(nullptr) |
|
1358 { |
|
1359 } |
|
1360 |
|
1361 uintptr_t |
|
1362 SnapshotIterator::fromStack(int32_t offset) const |
|
1363 { |
|
1364 return ReadFrameSlot(fp_, offset); |
|
1365 } |
|
1366 |
|
1367 static Value |
|
1368 FromObjectPayload(uintptr_t payload) |
|
1369 { |
|
1370 return ObjectValue(*reinterpret_cast<JSObject *>(payload)); |
|
1371 } |
|
1372 |
|
1373 static Value |
|
1374 FromStringPayload(uintptr_t payload) |
|
1375 { |
|
1376 return StringValue(reinterpret_cast<JSString *>(payload)); |
|
1377 } |
|
1378 |
|
1379 static Value |
|
1380 FromTypedPayload(JSValueType type, uintptr_t payload) |
|
1381 { |
|
1382 switch (type) { |
|
1383 case JSVAL_TYPE_INT32: |
|
1384 return Int32Value(payload); |
|
1385 case JSVAL_TYPE_BOOLEAN: |
|
1386 return BooleanValue(!!payload); |
|
1387 case JSVAL_TYPE_STRING: |
|
1388 return FromStringPayload(payload); |
|
1389 case JSVAL_TYPE_OBJECT: |
|
1390 return FromObjectPayload(payload); |
|
1391 default: |
|
1392 MOZ_ASSUME_UNREACHABLE("unexpected type - needs payload"); |
|
1393 } |
|
1394 } |
|
1395 |
|
1396 bool |
|
1397 SnapshotIterator::allocationReadable(const RValueAllocation &alloc) |
|
1398 { |
|
1399 switch (alloc.mode()) { |
|
1400 case RValueAllocation::DOUBLE_REG: |
|
1401 return hasRegister(alloc.fpuReg()); |
|
1402 |
|
1403 case RValueAllocation::TYPED_REG: |
|
1404 return hasRegister(alloc.reg2()); |
|
1405 |
|
1406 #if defined(JS_NUNBOX32) |
|
1407 case RValueAllocation::UNTYPED_REG_REG: |
|
1408 return hasRegister(alloc.reg()) && hasRegister(alloc.reg2()); |
|
1409 case RValueAllocation::UNTYPED_REG_STACK: |
|
1410 return hasRegister(alloc.reg()) && hasStack(alloc.stackOffset2()); |
|
1411 case RValueAllocation::UNTYPED_STACK_REG: |
|
1412 return hasStack(alloc.stackOffset()) && hasRegister(alloc.reg2()); |
|
1413 case RValueAllocation::UNTYPED_STACK_STACK: |
|
1414 return hasStack(alloc.stackOffset()) && hasStack(alloc.stackOffset2()); |
|
1415 #elif defined(JS_PUNBOX64) |
|
1416 case RValueAllocation::UNTYPED_REG: |
|
1417 return hasRegister(alloc.reg()); |
|
1418 case RValueAllocation::UNTYPED_STACK: |
|
1419 return hasStack(alloc.stackOffset()); |
|
1420 #endif |
|
1421 |
|
1422 default: |
|
1423 return true; |
|
1424 } |
|
1425 } |
|
1426 |
|
1427 Value |
|
1428 SnapshotIterator::allocationValue(const RValueAllocation &alloc) |
|
1429 { |
|
1430 switch (alloc.mode()) { |
|
1431 case RValueAllocation::CONSTANT: |
|
1432 return ionScript_->getConstant(alloc.index()); |
|
1433 |
|
1434 case RValueAllocation::CST_UNDEFINED: |
|
1435 return UndefinedValue(); |
|
1436 |
|
1437 case RValueAllocation::CST_NULL: |
|
1438 return NullValue(); |
|
1439 |
|
1440 case RValueAllocation::DOUBLE_REG: |
|
1441 return DoubleValue(fromRegister(alloc.fpuReg())); |
|
1442 |
|
1443 case RValueAllocation::FLOAT32_REG: |
|
1444 { |
|
1445 union { |
|
1446 double d; |
|
1447 float f; |
|
1448 } pun; |
|
1449 pun.d = fromRegister(alloc.fpuReg()); |
|
1450 // The register contains the encoding of a float32. We just read |
|
1451 // the bits without making any conversion. |
|
1452 return Float32Value(pun.f); |
|
1453 } |
|
1454 |
|
1455 case RValueAllocation::FLOAT32_STACK: |
|
1456 return Float32Value(ReadFrameFloat32Slot(fp_, alloc.stackOffset())); |
|
1457 |
|
1458 case RValueAllocation::TYPED_REG: |
|
1459 return FromTypedPayload(alloc.knownType(), fromRegister(alloc.reg2())); |
|
1460 |
|
1461 case RValueAllocation::TYPED_STACK: |
|
1462 { |
|
1463 switch (alloc.knownType()) { |
|
1464 case JSVAL_TYPE_DOUBLE: |
|
1465 return DoubleValue(ReadFrameDoubleSlot(fp_, alloc.stackOffset2())); |
|
1466 case JSVAL_TYPE_INT32: |
|
1467 return Int32Value(ReadFrameInt32Slot(fp_, alloc.stackOffset2())); |
|
1468 case JSVAL_TYPE_BOOLEAN: |
|
1469 return BooleanValue(ReadFrameBooleanSlot(fp_, alloc.stackOffset2())); |
|
1470 case JSVAL_TYPE_STRING: |
|
1471 return FromStringPayload(fromStack(alloc.stackOffset2())); |
|
1472 case JSVAL_TYPE_OBJECT: |
|
1473 return FromObjectPayload(fromStack(alloc.stackOffset2())); |
|
1474 default: |
|
1475 MOZ_ASSUME_UNREACHABLE("Unexpected type"); |
|
1476 } |
|
1477 } |
|
1478 |
|
1479 #if defined(JS_NUNBOX32) |
|
1480 case RValueAllocation::UNTYPED_REG_REG: |
|
1481 { |
|
1482 jsval_layout layout; |
|
1483 layout.s.tag = (JSValueTag) fromRegister(alloc.reg()); |
|
1484 layout.s.payload.word = fromRegister(alloc.reg2()); |
|
1485 return IMPL_TO_JSVAL(layout); |
|
1486 } |
|
1487 |
|
1488 case RValueAllocation::UNTYPED_REG_STACK: |
|
1489 { |
|
1490 jsval_layout layout; |
|
1491 layout.s.tag = (JSValueTag) fromRegister(alloc.reg()); |
|
1492 layout.s.payload.word = fromStack(alloc.stackOffset2()); |
|
1493 return IMPL_TO_JSVAL(layout); |
|
1494 } |
|
1495 |
|
1496 case RValueAllocation::UNTYPED_STACK_REG: |
|
1497 { |
|
1498 jsval_layout layout; |
|
1499 layout.s.tag = (JSValueTag) fromStack(alloc.stackOffset()); |
|
1500 layout.s.payload.word = fromRegister(alloc.reg2()); |
|
1501 return IMPL_TO_JSVAL(layout); |
|
1502 } |
|
1503 |
|
1504 case RValueAllocation::UNTYPED_STACK_STACK: |
|
1505 { |
|
1506 jsval_layout layout; |
|
1507 layout.s.tag = (JSValueTag) fromStack(alloc.stackOffset()); |
|
1508 layout.s.payload.word = fromStack(alloc.stackOffset2()); |
|
1509 return IMPL_TO_JSVAL(layout); |
|
1510 } |
|
1511 #elif defined(JS_PUNBOX64) |
|
1512 case RValueAllocation::UNTYPED_REG: |
|
1513 { |
|
1514 jsval_layout layout; |
|
1515 layout.asBits = fromRegister(alloc.reg()); |
|
1516 return IMPL_TO_JSVAL(layout); |
|
1517 } |
|
1518 |
|
1519 case RValueAllocation::UNTYPED_STACK: |
|
1520 { |
|
1521 jsval_layout layout; |
|
1522 layout.asBits = fromStack(alloc.stackOffset()); |
|
1523 return IMPL_TO_JSVAL(layout); |
|
1524 } |
|
1525 #endif |
|
1526 |
|
1527 default: |
|
1528 MOZ_ASSUME_UNREACHABLE("huh?"); |
|
1529 } |
|
1530 } |
|
1531 |
|
1532 const RResumePoint * |
|
1533 SnapshotIterator::resumePoint() const |
|
1534 { |
|
1535 return instruction()->toResumePoint(); |
|
1536 } |
|
1537 |
|
1538 uint32_t |
|
1539 SnapshotIterator::numAllocations() const |
|
1540 { |
|
1541 return resumePoint()->numOperands(); |
|
1542 } |
|
1543 |
|
1544 uint32_t |
|
1545 SnapshotIterator::pcOffset() const |
|
1546 { |
|
1547 return resumePoint()->pcOffset(); |
|
1548 } |
|
1549 |
|
1550 void |
|
1551 SnapshotIterator::skipInstruction() |
|
1552 { |
|
1553 MOZ_ASSERT(snapshot_.numAllocationsRead() == 0); |
|
1554 size_t numOperands = instruction()->numOperands(); |
|
1555 for (size_t i = 0; i < numOperands; i++) |
|
1556 skip(); |
|
1557 nextInstruction(); |
|
1558 } |
|
1559 |
|
1560 void |
|
1561 SnapshotIterator::nextFrame() |
|
1562 { |
|
1563 nextInstruction(); |
|
1564 while (!instruction()->isResumePoint()) |
|
1565 skipInstruction(); |
|
1566 } |
|
1567 |
|
1568 IonScript * |
|
1569 JitFrameIterator::ionScript() const |
|
1570 { |
|
1571 JS_ASSERT(type() == JitFrame_IonJS); |
|
1572 |
|
1573 IonScript *ionScript = nullptr; |
|
1574 if (checkInvalidation(&ionScript)) |
|
1575 return ionScript; |
|
1576 switch (GetCalleeTokenTag(calleeToken())) { |
|
1577 case CalleeToken_Function: |
|
1578 case CalleeToken_Script: |
|
1579 return mode_ == ParallelExecution ? script()->parallelIonScript() : script()->ionScript(); |
|
1580 default: |
|
1581 MOZ_ASSUME_UNREACHABLE("unknown callee token type"); |
|
1582 } |
|
1583 } |
|
1584 |
|
1585 const SafepointIndex * |
|
1586 JitFrameIterator::safepoint() const |
|
1587 { |
|
1588 if (!cachedSafepointIndex_) |
|
1589 cachedSafepointIndex_ = ionScript()->getSafepointIndex(returnAddressToFp()); |
|
1590 return cachedSafepointIndex_; |
|
1591 } |
|
1592 |
|
1593 const OsiIndex * |
|
1594 JitFrameIterator::osiIndex() const |
|
1595 { |
|
1596 SafepointReader reader(ionScript(), safepoint()); |
|
1597 return ionScript()->getOsiIndex(reader.osiReturnPointOffset()); |
|
1598 } |
|
1599 |
|
1600 template <AllowGC allowGC> |
|
1601 void |
|
1602 InlineFrameIteratorMaybeGC<allowGC>::resetOn(const JitFrameIterator *iter) |
|
1603 { |
|
1604 frame_ = iter; |
|
1605 framesRead_ = 0; |
|
1606 frameCount_ = UINT32_MAX; |
|
1607 |
|
1608 if (iter) { |
|
1609 start_ = SnapshotIterator(*iter); |
|
1610 findNextFrame(); |
|
1611 } |
|
1612 } |
|
1613 template void InlineFrameIteratorMaybeGC<NoGC>::resetOn(const JitFrameIterator *iter); |
|
1614 template void InlineFrameIteratorMaybeGC<CanGC>::resetOn(const JitFrameIterator *iter); |
|
1615 |
|
1616 template <AllowGC allowGC> |
|
1617 void |
|
1618 InlineFrameIteratorMaybeGC<allowGC>::findNextFrame() |
|
1619 { |
|
1620 JS_ASSERT(more()); |
|
1621 |
|
1622 si_ = start_; |
|
1623 |
|
1624 // Read the initial frame out of the C stack. |
|
1625 callee_ = frame_->maybeCallee(); |
|
1626 script_ = frame_->script(); |
|
1627 MOZ_ASSERT(script_->hasBaselineScript()); |
|
1628 |
|
1629 // Settle on the outermost frame without evaluating any instructions before |
|
1630 // looking for a pc. |
|
1631 if (!si_.instruction()->isResumePoint()) |
|
1632 si_.nextFrame(); |
|
1633 |
|
1634 pc_ = script_->offsetToPC(si_.pcOffset()); |
|
1635 #ifdef DEBUG |
|
1636 numActualArgs_ = 0xbadbad; |
|
1637 #endif |
|
1638 |
|
1639 // This unfortunately is O(n*m), because we must skip over outer frames |
|
1640 // before reading inner ones. |
|
1641 |
|
1642 // The first time (frameCount_ == UINT32_MAX) we do not know the number of |
|
1643 // frames that we are going to inspect. So we are iterating until there is |
|
1644 // no more frames, to settle on the inner most frame and to count the number |
|
1645 // of frames. |
|
1646 size_t remaining = (frameCount_ != UINT32_MAX) ? frameNo() - 1 : SIZE_MAX; |
|
1647 |
|
1648 size_t i = 1; |
|
1649 for (; i <= remaining && si_.moreFrames(); i++) { |
|
1650 JS_ASSERT(IsIonInlinablePC(pc_)); |
|
1651 |
|
1652 // Recover the number of actual arguments from the script. |
|
1653 if (JSOp(*pc_) != JSOP_FUNAPPLY) |
|
1654 numActualArgs_ = GET_ARGC(pc_); |
|
1655 if (JSOp(*pc_) == JSOP_FUNCALL) { |
|
1656 JS_ASSERT(GET_ARGC(pc_) > 0); |
|
1657 numActualArgs_ = GET_ARGC(pc_) - 1; |
|
1658 } else if (IsGetPropPC(pc_)) { |
|
1659 numActualArgs_ = 0; |
|
1660 } else if (IsSetPropPC(pc_)) { |
|
1661 numActualArgs_ = 1; |
|
1662 } |
|
1663 |
|
1664 JS_ASSERT(numActualArgs_ != 0xbadbad); |
|
1665 |
|
1666 // Skip over non-argument slots, as well as |this|. |
|
1667 unsigned skipCount = (si_.numAllocations() - 1) - numActualArgs_ - 1; |
|
1668 for (unsigned j = 0; j < skipCount; j++) |
|
1669 si_.skip(); |
|
1670 |
|
1671 // The JSFunction is a constant, otherwise we would not have inlined it. |
|
1672 Value funval = si_.read(); |
|
1673 |
|
1674 // Skip extra value allocations. |
|
1675 while (si_.moreAllocations()) |
|
1676 si_.skip(); |
|
1677 |
|
1678 si_.nextFrame(); |
|
1679 |
|
1680 callee_ = &funval.toObject().as<JSFunction>(); |
|
1681 |
|
1682 // Inlined functions may be clones that still point to the lazy script |
|
1683 // for the executed script, if they are clones. The actual script |
|
1684 // exists though, just make sure the function points to it. |
|
1685 script_ = callee_->existingScriptForInlinedFunction(); |
|
1686 MOZ_ASSERT(script_->hasBaselineScript()); |
|
1687 |
|
1688 pc_ = script_->offsetToPC(si_.pcOffset()); |
|
1689 } |
|
1690 |
|
1691 // The first time we do not know the number of frames, we only settle on the |
|
1692 // last frame, and update the number of frames based on the number of |
|
1693 // iteration that we have done. |
|
1694 if (frameCount_ == UINT32_MAX) { |
|
1695 MOZ_ASSERT(!si_.moreFrames()); |
|
1696 frameCount_ = i; |
|
1697 } |
|
1698 |
|
1699 framesRead_++; |
|
1700 } |
|
1701 template void InlineFrameIteratorMaybeGC<NoGC>::findNextFrame(); |
|
1702 template void InlineFrameIteratorMaybeGC<CanGC>::findNextFrame(); |
|
1703 |
|
1704 template <AllowGC allowGC> |
|
1705 bool |
|
1706 InlineFrameIteratorMaybeGC<allowGC>::isFunctionFrame() const |
|
1707 { |
|
1708 return !!callee_; |
|
1709 } |
|
1710 template bool InlineFrameIteratorMaybeGC<NoGC>::isFunctionFrame() const; |
|
1711 template bool InlineFrameIteratorMaybeGC<CanGC>::isFunctionFrame() const; |
|
1712 |
|
1713 MachineState |
|
1714 MachineState::FromBailout(mozilla::Array<uintptr_t, Registers::Total> ®s, |
|
1715 mozilla::Array<double, FloatRegisters::Total> &fpregs) |
|
1716 { |
|
1717 MachineState machine; |
|
1718 |
|
1719 for (unsigned i = 0; i < Registers::Total; i++) |
|
1720 machine.setRegisterLocation(Register::FromCode(i), ®s[i]); |
|
1721 for (unsigned i = 0; i < FloatRegisters::Total; i++) |
|
1722 machine.setRegisterLocation(FloatRegister::FromCode(i), &fpregs[i]); |
|
1723 |
|
1724 return machine; |
|
1725 } |
|
1726 |
|
1727 template <AllowGC allowGC> |
|
1728 bool |
|
1729 InlineFrameIteratorMaybeGC<allowGC>::isConstructing() const |
|
1730 { |
|
1731 // Skip the current frame and look at the caller's. |
|
1732 if (more()) { |
|
1733 InlineFrameIteratorMaybeGC<allowGC> parent(GetJSContextFromJitCode(), this); |
|
1734 ++parent; |
|
1735 |
|
1736 // Inlined Getters and Setters are never constructing. |
|
1737 if (IsGetPropPC(parent.pc()) || IsSetPropPC(parent.pc())) |
|
1738 return false; |
|
1739 |
|
1740 // In the case of a JS frame, look up the pc from the snapshot. |
|
1741 JS_ASSERT(IsCallPC(parent.pc())); |
|
1742 |
|
1743 return (JSOp)*parent.pc() == JSOP_NEW; |
|
1744 } |
|
1745 |
|
1746 return frame_->isConstructing(); |
|
1747 } |
|
1748 template bool InlineFrameIteratorMaybeGC<NoGC>::isConstructing() const; |
|
1749 template bool InlineFrameIteratorMaybeGC<CanGC>::isConstructing() const; |
|
1750 |
|
1751 bool |
|
1752 JitFrameIterator::isConstructing() const |
|
1753 { |
|
1754 JitFrameIterator parent(*this); |
|
1755 |
|
1756 // Skip the current frame and look at the caller's. |
|
1757 do { |
|
1758 ++parent; |
|
1759 } while (!parent.done() && !parent.isScripted()); |
|
1760 |
|
1761 if (parent.isIonJS()) { |
|
1762 // In the case of a JS frame, look up the pc from the snapshot. |
|
1763 InlineFrameIterator inlinedParent(GetJSContextFromJitCode(), &parent); |
|
1764 |
|
1765 //Inlined Getters and Setters are never constructing. |
|
1766 if (IsGetPropPC(inlinedParent.pc()) || IsSetPropPC(inlinedParent.pc())) |
|
1767 return false; |
|
1768 |
|
1769 JS_ASSERT(IsCallPC(inlinedParent.pc())); |
|
1770 |
|
1771 return (JSOp)*inlinedParent.pc() == JSOP_NEW; |
|
1772 } |
|
1773 |
|
1774 if (parent.isBaselineJS()) { |
|
1775 jsbytecode *pc; |
|
1776 parent.baselineScriptAndPc(nullptr, &pc); |
|
1777 |
|
1778 // Inlined Getters and Setters are never constructing. |
|
1779 // Baseline may call getters from [GET|SET]PROP or [GET|SET]ELEM ops. |
|
1780 if (IsGetPropPC(pc) || IsSetPropPC(pc) || IsGetElemPC(pc) || IsSetElemPC(pc)) |
|
1781 return false; |
|
1782 |
|
1783 JS_ASSERT(IsCallPC(pc)); |
|
1784 |
|
1785 return JSOp(*pc) == JSOP_NEW; |
|
1786 } |
|
1787 |
|
1788 JS_ASSERT(parent.done()); |
|
1789 return activation_->firstFrameIsConstructing(); |
|
1790 } |
|
1791 |
|
1792 unsigned |
|
1793 JitFrameIterator::numActualArgs() const |
|
1794 { |
|
1795 if (isScripted()) |
|
1796 return jsFrame()->numActualArgs(); |
|
1797 |
|
1798 JS_ASSERT(isNative()); |
|
1799 return exitFrame()->nativeExit()->argc(); |
|
1800 } |
|
1801 |
|
1802 void |
|
1803 SnapshotIterator::warnUnreadableAllocation() |
|
1804 { |
|
1805 fprintf(stderr, "Warning! Tried to access unreadable value allocation (possible f.arguments).\n"); |
|
1806 } |
|
1807 |
|
1808 struct DumpOp { |
|
1809 DumpOp(unsigned int i) : i_(i) {} |
|
1810 |
|
1811 unsigned int i_; |
|
1812 void operator()(const Value& v) { |
|
1813 fprintf(stderr, " actual (arg %d): ", i_); |
|
1814 #ifdef DEBUG |
|
1815 js_DumpValue(v); |
|
1816 #else |
|
1817 fprintf(stderr, "?\n"); |
|
1818 #endif |
|
1819 i_++; |
|
1820 } |
|
1821 }; |
|
1822 |
|
1823 void |
|
1824 JitFrameIterator::dumpBaseline() const |
|
1825 { |
|
1826 JS_ASSERT(isBaselineJS()); |
|
1827 |
|
1828 fprintf(stderr, " JS Baseline frame\n"); |
|
1829 if (isFunctionFrame()) { |
|
1830 fprintf(stderr, " callee fun: "); |
|
1831 #ifdef DEBUG |
|
1832 js_DumpObject(callee()); |
|
1833 #else |
|
1834 fprintf(stderr, "?\n"); |
|
1835 #endif |
|
1836 } else { |
|
1837 fprintf(stderr, " global frame, no callee\n"); |
|
1838 } |
|
1839 |
|
1840 fprintf(stderr, " file %s line %u\n", |
|
1841 script()->filename(), (unsigned) script()->lineno()); |
|
1842 |
|
1843 JSContext *cx = GetJSContextFromJitCode(); |
|
1844 RootedScript script(cx); |
|
1845 jsbytecode *pc; |
|
1846 baselineScriptAndPc(script.address(), &pc); |
|
1847 |
|
1848 fprintf(stderr, " script = %p, pc = %p (offset %u)\n", (void *)script, pc, uint32_t(script->pcToOffset(pc))); |
|
1849 fprintf(stderr, " current op: %s\n", js_CodeName[*pc]); |
|
1850 |
|
1851 fprintf(stderr, " actual args: %d\n", numActualArgs()); |
|
1852 |
|
1853 BaselineFrame *frame = baselineFrame(); |
|
1854 |
|
1855 for (unsigned i = 0; i < frame->numValueSlots(); i++) { |
|
1856 fprintf(stderr, " slot %u: ", i); |
|
1857 #ifdef DEBUG |
|
1858 Value *v = frame->valueSlot(i); |
|
1859 js_DumpValue(*v); |
|
1860 #else |
|
1861 fprintf(stderr, "?\n"); |
|
1862 #endif |
|
1863 } |
|
1864 } |
|
1865 |
|
1866 template <AllowGC allowGC> |
|
1867 void |
|
1868 InlineFrameIteratorMaybeGC<allowGC>::dump() const |
|
1869 { |
|
1870 if (more()) |
|
1871 fprintf(stderr, " JS frame (inlined)\n"); |
|
1872 else |
|
1873 fprintf(stderr, " JS frame\n"); |
|
1874 |
|
1875 bool isFunction = false; |
|
1876 if (isFunctionFrame()) { |
|
1877 isFunction = true; |
|
1878 fprintf(stderr, " callee fun: "); |
|
1879 #ifdef DEBUG |
|
1880 js_DumpObject(callee()); |
|
1881 #else |
|
1882 fprintf(stderr, "?\n"); |
|
1883 #endif |
|
1884 } else { |
|
1885 fprintf(stderr, " global frame, no callee\n"); |
|
1886 } |
|
1887 |
|
1888 fprintf(stderr, " file %s line %u\n", |
|
1889 script()->filename(), (unsigned) script()->lineno()); |
|
1890 |
|
1891 fprintf(stderr, " script = %p, pc = %p\n", (void*) script(), pc()); |
|
1892 fprintf(stderr, " current op: %s\n", js_CodeName[*pc()]); |
|
1893 |
|
1894 if (!more()) { |
|
1895 numActualArgs(); |
|
1896 } |
|
1897 |
|
1898 SnapshotIterator si = snapshotIterator(); |
|
1899 fprintf(stderr, " slots: %u\n", si.numAllocations() - 1); |
|
1900 for (unsigned i = 0; i < si.numAllocations() - 1; i++) { |
|
1901 if (isFunction) { |
|
1902 if (i == 0) |
|
1903 fprintf(stderr, " scope chain: "); |
|
1904 else if (i == 1) |
|
1905 fprintf(stderr, " this: "); |
|
1906 else if (i - 2 < callee()->nargs()) |
|
1907 fprintf(stderr, " formal (arg %d): ", i - 2); |
|
1908 else { |
|
1909 if (i - 2 == callee()->nargs() && numActualArgs() > callee()->nargs()) { |
|
1910 DumpOp d(callee()->nargs()); |
|
1911 unaliasedForEachActual(GetJSContextFromJitCode(), d, ReadFrame_Overflown); |
|
1912 } |
|
1913 |
|
1914 fprintf(stderr, " slot %d: ", int(i - 2 - callee()->nargs())); |
|
1915 } |
|
1916 } else |
|
1917 fprintf(stderr, " slot %u: ", i); |
|
1918 #ifdef DEBUG |
|
1919 js_DumpValue(si.maybeRead()); |
|
1920 #else |
|
1921 fprintf(stderr, "?\n"); |
|
1922 #endif |
|
1923 } |
|
1924 |
|
1925 fputc('\n', stderr); |
|
1926 } |
|
1927 template void InlineFrameIteratorMaybeGC<NoGC>::dump() const; |
|
1928 template void InlineFrameIteratorMaybeGC<CanGC>::dump() const; |
|
1929 |
|
1930 void |
|
1931 JitFrameIterator::dump() const |
|
1932 { |
|
1933 switch (type_) { |
|
1934 case JitFrame_Entry: |
|
1935 fprintf(stderr, " Entry frame\n"); |
|
1936 fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize())); |
|
1937 break; |
|
1938 case JitFrame_BaselineJS: |
|
1939 dumpBaseline(); |
|
1940 break; |
|
1941 case JitFrame_BaselineStub: |
|
1942 case JitFrame_Unwound_BaselineStub: |
|
1943 fprintf(stderr, " Baseline stub frame\n"); |
|
1944 fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize())); |
|
1945 break; |
|
1946 case JitFrame_IonJS: |
|
1947 { |
|
1948 InlineFrameIterator frames(GetJSContextFromJitCode(), this); |
|
1949 for (;;) { |
|
1950 frames.dump(); |
|
1951 if (!frames.more()) |
|
1952 break; |
|
1953 ++frames; |
|
1954 } |
|
1955 break; |
|
1956 } |
|
1957 case JitFrame_Rectifier: |
|
1958 case JitFrame_Unwound_Rectifier: |
|
1959 fprintf(stderr, " Rectifier frame\n"); |
|
1960 fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize())); |
|
1961 break; |
|
1962 case JitFrame_Unwound_IonJS: |
|
1963 fprintf(stderr, "Warning! Unwound JS frames are not observable.\n"); |
|
1964 break; |
|
1965 case JitFrame_Exit: |
|
1966 break; |
|
1967 }; |
|
1968 fputc('\n', stderr); |
|
1969 } |
|
1970 |
|
1971 IonJSFrameLayout * |
|
1972 InvalidationBailoutStack::fp() const |
|
1973 { |
|
1974 return (IonJSFrameLayout *) (sp() + ionScript_->frameSize()); |
|
1975 } |
|
1976 |
|
1977 void |
|
1978 InvalidationBailoutStack::checkInvariants() const |
|
1979 { |
|
1980 #ifdef DEBUG |
|
1981 IonJSFrameLayout *frame = fp(); |
|
1982 CalleeToken token = frame->calleeToken(); |
|
1983 JS_ASSERT(token); |
|
1984 |
|
1985 uint8_t *rawBase = ionScript()->method()->raw(); |
|
1986 uint8_t *rawLimit = rawBase + ionScript()->method()->instructionsSize(); |
|
1987 uint8_t *osiPoint = osiPointReturnAddress(); |
|
1988 JS_ASSERT(rawBase <= osiPoint && osiPoint <= rawLimit); |
|
1989 #endif |
|
1990 } |
|
1991 |
|
1992 } // namespace jit |
|
1993 } // namespace js |