Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "jit/JitFrames-inl.h"
8 :
9 : #include "mozilla/SizePrintfMacros.h"
10 :
11 : #include "jsfun.h"
12 : #include "jsobj.h"
13 : #include "jsscript.h"
14 : #include "jsutil.h"
15 :
16 : #include "gc/Marking.h"
17 : #include "jit/BaselineDebugModeOSR.h"
18 : #include "jit/BaselineFrame.h"
19 : #include "jit/BaselineIC.h"
20 : #include "jit/BaselineJIT.h"
21 : #include "jit/Ion.h"
22 : #include "jit/JitcodeMap.h"
23 : #include "jit/JitCompartment.h"
24 : #include "jit/JitSpewer.h"
25 : #include "jit/MacroAssembler.h"
26 : #include "jit/PcScriptCache.h"
27 : #include "jit/Recover.h"
28 : #include "jit/Safepoints.h"
29 : #include "jit/Snapshots.h"
30 : #include "jit/VMFunctions.h"
31 : #include "vm/ArgumentsObject.h"
32 : #include "vm/Debugger.h"
33 : #include "vm/GeckoProfiler.h"
34 : #include "vm/Interpreter.h"
35 : #include "vm/TraceLogging.h"
36 : #include "vm/TypeInference.h"
37 :
38 : #include "jsscriptinlines.h"
39 : #include "gc/Nursery-inl.h"
40 : #include "jit/JitFrameIterator-inl.h"
41 : #include "vm/Debugger-inl.h"
42 : #include "vm/Probes-inl.h"
43 : #include "vm/TypeInference-inl.h"
44 :
45 : namespace js {
46 : namespace jit {
47 :
48 : // Given a slot index, returns the offset, in bytes, of that slot from an
49 : // JitFrameLayout. Slot distances are uniform across architectures, however,
50 : // the distance does depend on the size of the frame header.
51 : static inline int32_t
52 0 : OffsetOfFrameSlot(int32_t slot)
53 : {
54 0 : return -slot;
55 : }
56 :
57 : static inline uint8_t*
58 0 : AddressOfFrameSlot(JitFrameLayout* fp, int32_t slot)
59 : {
60 0 : return (uint8_t*) fp + OffsetOfFrameSlot(slot);
61 : }
62 :
63 : static inline uintptr_t
64 0 : ReadFrameSlot(JitFrameLayout* fp, int32_t slot)
65 : {
66 0 : return *(uintptr_t*) AddressOfFrameSlot(fp, slot);
67 : }
68 :
69 : static inline void
70 0 : WriteFrameSlot(JitFrameLayout* fp, int32_t slot, uintptr_t value)
71 : {
72 0 : *(uintptr_t*) AddressOfFrameSlot(fp, slot) = value;
73 0 : }
74 :
75 : static inline double
76 0 : ReadFrameDoubleSlot(JitFrameLayout* fp, int32_t slot)
77 : {
78 0 : return *(double*) AddressOfFrameSlot(fp, slot);
79 : }
80 :
81 : static inline float
82 0 : ReadFrameFloat32Slot(JitFrameLayout* fp, int32_t slot)
83 : {
84 0 : return *(float*) AddressOfFrameSlot(fp, slot);
85 : }
86 :
87 : static inline int32_t
88 0 : ReadFrameInt32Slot(JitFrameLayout* fp, int32_t slot)
89 : {
90 0 : return *(int32_t*) AddressOfFrameSlot(fp, slot);
91 : }
92 :
93 : static inline bool
94 0 : ReadFrameBooleanSlot(JitFrameLayout* fp, int32_t slot)
95 : {
96 0 : return *(bool*) AddressOfFrameSlot(fp, slot);
97 : }
98 :
99 3186 : JitFrameIterator::JitFrameIterator()
100 : : current_(nullptr),
101 : type_(JitFrame_Exit),
102 : returnAddressToFp_(nullptr),
103 : frameSize_(0),
104 : cachedSafepointIndex_(nullptr),
105 3186 : activation_(nullptr)
106 : {
107 3186 : }
108 :
109 2074 : JitFrameIterator::JitFrameIterator(JSContext* cx)
110 2074 : : current_(cx->activation()->asJit()->exitFP()),
111 : type_(JitFrame_Exit),
112 : returnAddressToFp_(nullptr),
113 : frameSize_(0),
114 : cachedSafepointIndex_(nullptr),
115 2074 : activation_(cx->activation()->asJit())
116 : {
117 2074 : if (activation_->bailoutData()) {
118 0 : current_ = activation_->bailoutData()->fp();
119 0 : frameSize_ = activation_->bailoutData()->topFrameSize();
120 0 : type_ = JitFrame_Bailout;
121 : }
122 2074 : }
123 :
124 7327 : JitFrameIterator::JitFrameIterator(const ActivationIterator& activations)
125 7327 : : current_(activations->asJit()->exitFP()),
126 : type_(JitFrame_Exit),
127 : returnAddressToFp_(nullptr),
128 : frameSize_(0),
129 : cachedSafepointIndex_(nullptr),
130 7327 : activation_(activations->asJit())
131 : {
132 7327 : if (activation_->bailoutData()) {
133 0 : current_ = activation_->bailoutData()->fp();
134 0 : frameSize_ = activation_->bailoutData()->topFrameSize();
135 0 : type_ = JitFrame_Bailout;
136 : }
137 7327 : }
138 :
139 : bool
140 9240 : JitFrameIterator::checkInvalidation() const
141 : {
142 : IonScript* dummy;
143 9240 : return checkInvalidation(&dummy);
144 : }
145 :
146 : bool
147 18480 : JitFrameIterator::checkInvalidation(IonScript** ionScriptOut) const
148 : {
149 18480 : JSScript* script = this->script();
150 18480 : if (isBailoutJS()) {
151 0 : *ionScriptOut = activation_->bailoutData()->ionScript();
152 0 : return !script->hasIonScript() || script->ionScript() != *ionScriptOut;
153 : }
154 :
155 18480 : uint8_t* returnAddr = returnAddressToFp();
156 : // N.B. the current IonScript is not the same as the frame's
157 : // IonScript if the frame has since been invalidated.
158 36960 : bool invalidated = !script->hasIonScript() ||
159 36960 : !script->ionScript()->containsReturnAddress(returnAddr);
160 18480 : if (!invalidated)
161 18480 : return false;
162 :
163 0 : int32_t invalidationDataOffset = ((int32_t*) returnAddr)[-1];
164 0 : uint8_t* ionScriptDataOffset = returnAddr + invalidationDataOffset;
165 0 : IonScript* ionScript = (IonScript*) Assembler::GetPointer(ionScriptDataOffset);
166 0 : MOZ_ASSERT(ionScript->containsReturnAddress(returnAddr));
167 0 : *ionScriptOut = ionScript;
168 0 : return true;
169 : }
170 :
171 : CalleeToken
172 39486 : JitFrameIterator::calleeToken() const
173 : {
174 39486 : return ((JitFrameLayout*) current_)->calleeToken();
175 : }
176 :
177 : JSFunction*
178 5018 : JitFrameIterator::callee() const
179 : {
180 5018 : MOZ_ASSERT(isScripted());
181 5018 : MOZ_ASSERT(isFunctionFrame());
182 5018 : return CalleeTokenToFunction(calleeToken());
183 : }
184 :
185 : JSFunction*
186 840 : JitFrameIterator::maybeCallee() const
187 : {
188 840 : if (isScripted() && (isFunctionFrame()))
189 840 : return callee();
190 0 : return nullptr;
191 : }
192 :
193 : bool
194 19 : JitFrameIterator::isBareExit() const
195 : {
196 19 : if (type_ != JitFrame_Exit)
197 0 : return false;
198 19 : return exitFrame()->isBareExit();
199 : }
200 :
201 : bool
202 5858 : JitFrameIterator::isFunctionFrame() const
203 : {
204 5858 : return CalleeTokenIsFunction(calleeToken());
205 : }
206 :
207 : JSScript*
208 40644 : JitFrameIterator::script() const
209 : {
210 40644 : MOZ_ASSERT(isScripted());
211 40644 : if (isBaselineJS())
212 12084 : return baselineFrame()->script();
213 28560 : JSScript* script = ScriptFromCalleeToken(calleeToken());
214 28560 : MOZ_ASSERT(script);
215 28560 : return script;
216 : }
217 :
218 : void
219 7623 : JitFrameIterator::baselineScriptAndPc(JSScript** scriptRes, jsbytecode** pcRes) const
220 : {
221 7623 : MOZ_ASSERT(isBaselineJS());
222 7623 : JSScript* script = this->script();
223 7623 : if (scriptRes)
224 1333 : *scriptRes = script;
225 :
226 7623 : MOZ_ASSERT(pcRes);
227 :
228 : // Use the frame's override pc, if we have one. This should only happen
229 : // when we're in FinishBailoutToBaseline, handling an exception or toggling
230 : // debug mode.
231 7623 : if (jsbytecode* overridePc = baselineFrame()->maybeOverridePc()) {
232 0 : *pcRes = overridePc;
233 0 : return;
234 : }
235 :
236 : // Else, there must be an ICEntry for the current return address.
237 7623 : uint8_t* retAddr = returnAddressToFp();
238 7623 : ICEntry& icEntry = script->baselineScript()->icEntryFromReturnAddress(retAddr);
239 7623 : *pcRes = icEntry.pc(script);
240 : }
241 :
242 : Value*
243 0 : JitFrameIterator::actualArgs() const
244 : {
245 0 : return jsFrame()->argv() + 1;
246 : }
247 :
248 : uint8_t*
249 20063 : JitFrameIterator::prevFp() const
250 : {
251 20063 : return current_ + current()->prevFrameLocalSize() + current()->headerSize();
252 : }
253 :
254 : JitFrameIterator&
255 23230 : JitFrameIterator::operator++()
256 : {
257 23230 : MOZ_ASSERT(type_ != JitFrame_Entry);
258 :
259 23230 : frameSize_ = prevFrameLocalSize();
260 23230 : cachedSafepointIndex_ = nullptr;
261 :
262 : // If the next frame is the entry frame, just exit. Don't update current_,
263 : // since the entry and first frames overlap.
264 23230 : if (current()->prevType() == JitFrame_Entry) {
265 3167 : type_ = JitFrame_Entry;
266 3167 : return *this;
267 : }
268 :
269 20063 : type_ = current()->prevType();
270 20063 : returnAddressToFp_ = current()->returnAddress();
271 20063 : current_ = prevFp();
272 :
273 20063 : return *this;
274 : }
275 :
276 : uintptr_t*
277 840 : JitFrameIterator::spillBase() const
278 : {
279 840 : MOZ_ASSERT(isIonJS());
280 :
281 : // Get the base address to where safepoint registers are spilled.
282 : // Out-of-line calls do not unwind the extra padding space used to
283 : // aggregate bailout tables, so we use frameSize instead of frameLocals,
284 : // which would only account for local stack slots.
285 840 : return reinterpret_cast<uintptr_t*>(fp() - ionScript()->frameSize());
286 : }
287 :
288 : MachineState
289 840 : JitFrameIterator::machineState() const
290 : {
291 840 : MOZ_ASSERT(isIonScripted());
292 :
293 : // The MachineState is used by GCs for tracing call-sites.
294 840 : if (MOZ_UNLIKELY(isBailoutJS()))
295 0 : return *activation_->bailoutData()->machineState();
296 :
297 840 : SafepointReader reader(ionScript(), safepoint());
298 840 : uintptr_t* spill = spillBase();
299 840 : MachineState machine;
300 :
301 840 : for (GeneralRegisterBackwardIterator iter(reader.allGprSpills()); iter.more(); ++iter)
302 0 : machine.setRegisterLocation(*iter, --spill);
303 :
304 840 : uint8_t* spillAlign = alignDoubleSpillWithOffset(reinterpret_cast<uint8_t*>(spill), 0);
305 :
306 840 : char* floatSpill = reinterpret_cast<char*>(spillAlign);
307 840 : FloatRegisterSet fregs = reader.allFloatSpills().set();
308 840 : fregs = fregs.reduceSetForPush();
309 840 : for (FloatRegisterBackwardIterator iter(fregs); iter.more(); ++iter) {
310 0 : floatSpill -= (*iter).size();
311 0 : for (uint32_t a = 0; a < (*iter).numAlignedAliased(); a++) {
312 : // Only say that registers that actually start here start here.
313 : // e.g. d0 should not start at s1, only at s0.
314 0 : FloatRegister ftmp;
315 0 : (*iter).alignedAliased(a, &ftmp);
316 0 : machine.setRegisterLocation(ftmp, (double*)floatSpill);
317 : }
318 : }
319 :
320 840 : return machine;
321 : }
322 :
323 : static uint32_t
324 0 : NumArgAndLocalSlots(const InlineFrameIterator& frame)
325 : {
326 0 : JSScript* script = frame.script();
327 0 : return CountArgSlots(script, frame.maybeCalleeTemplate()) + script->nfixed();
328 : }
329 :
330 : static void
331 0 : CloseLiveIteratorIon(JSContext* cx, const InlineFrameIterator& frame, JSTryNote* tn)
332 : {
333 0 : MOZ_ASSERT(tn->kind == JSTRY_FOR_IN ||
334 : tn->kind == JSTRY_DESTRUCTURING_ITERCLOSE);
335 :
336 0 : bool isDestructuring = tn->kind == JSTRY_DESTRUCTURING_ITERCLOSE;
337 0 : MOZ_ASSERT_IF(!isDestructuring, tn->stackDepth > 0);
338 0 : MOZ_ASSERT_IF(isDestructuring, tn->stackDepth > 1);
339 :
340 0 : SnapshotIterator si = frame.snapshotIterator();
341 :
342 : // Skip stack slots until we reach the iterator object on the stack. For
343 : // the destructuring case, we also need to get the "done" value.
344 0 : uint32_t stackSlot = tn->stackDepth;
345 0 : uint32_t adjust = isDestructuring ? 2 : 1;
346 0 : uint32_t skipSlots = NumArgAndLocalSlots(frame) + stackSlot - adjust;
347 :
348 0 : for (unsigned i = 0; i < skipSlots; i++)
349 0 : si.skip();
350 :
351 0 : MaybeReadFallback recover(cx, cx->activation()->asJit(), &frame.frame(), MaybeReadFallback::Fallback_DoNothing);
352 0 : Value v = si.maybeRead(recover);
353 0 : RootedObject iterObject(cx, &v.toObject());
354 :
355 0 : if (isDestructuring) {
356 0 : RootedValue doneValue(cx, si.read());
357 0 : bool done = ToBoolean(doneValue);
358 : // Do not call IteratorClose if the destructuring iterator is already
359 : // done.
360 0 : if (done)
361 0 : return;
362 : }
363 :
364 0 : if (cx->isExceptionPending()) {
365 0 : if (tn->kind == JSTRY_FOR_IN)
366 0 : UnwindIteratorForException(cx, iterObject);
367 : else
368 0 : IteratorCloseForException(cx, iterObject);
369 : } else {
370 0 : UnwindIteratorForUncatchableException(cx, iterObject);
371 : }
372 : }
373 :
374 : class IonFrameStackDepthOp
375 : {
376 : uint32_t depth_;
377 :
378 : public:
379 0 : explicit IonFrameStackDepthOp(const InlineFrameIterator& frame) {
380 0 : uint32_t base = NumArgAndLocalSlots(frame);
381 0 : SnapshotIterator si = frame.snapshotIterator();
382 0 : MOZ_ASSERT(si.numAllocations() >= base);
383 0 : depth_ = si.numAllocations() - base;
384 0 : }
385 :
386 0 : uint32_t operator()() { return depth_; }
387 : };
388 :
389 0 : class TryNoteIterIon : public TryNoteIter<IonFrameStackDepthOp>
390 : {
391 : public:
392 0 : TryNoteIterIon(JSContext* cx, const InlineFrameIterator& frame)
393 0 : : TryNoteIter(cx, frame.script(), frame.pc(), IonFrameStackDepthOp(frame))
394 0 : { }
395 : };
396 :
397 : static void
398 0 : HandleExceptionIon(JSContext* cx, const InlineFrameIterator& frame, ResumeFromException* rfe,
399 : bool* overrecursed)
400 : {
401 0 : if (cx->compartment()->isDebuggee()) {
402 : // We need to bail when there is a catchable exception, and we are the
403 : // debuggee of a Debugger with a live onExceptionUnwind hook, or if a
404 : // Debugger has observed this frame (e.g., for onPop).
405 0 : bool shouldBail = Debugger::hasLiveHook(cx->global(), Debugger::OnExceptionUnwind);
406 0 : RematerializedFrame* rematFrame = nullptr;
407 0 : if (!shouldBail) {
408 0 : JitActivation* act = cx->activation()->asJit();
409 0 : rematFrame = act->lookupRematerializedFrame(frame.frame().fp(), frame.frameNo());
410 0 : shouldBail = rematFrame && rematFrame->isDebuggee();
411 : }
412 :
413 0 : if (shouldBail) {
414 : // If we have an exception from within Ion and the debugger is active,
415 : // we do the following:
416 : //
417 : // 1. Bailout to baseline to reconstruct a baseline frame.
418 : // 2. Resume immediately into the exception tail afterwards, and
419 : // handle the exception again with the top frame now a baseline
420 : // frame.
421 : //
422 : // An empty exception info denotes that we're propagating an Ion
423 : // exception due to debug mode, which BailoutIonToBaseline needs to
424 : // know. This is because we might not be able to fully reconstruct up
425 : // to the stack depth at the snapshot, as we could've thrown in the
426 : // middle of a call.
427 0 : ExceptionBailoutInfo propagateInfo;
428 0 : uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, propagateInfo, overrecursed);
429 0 : if (retval == BAILOUT_RETURN_OK)
430 0 : return;
431 : }
432 :
433 0 : MOZ_ASSERT_IF(rematFrame, !Debugger::inFrameMaps(rematFrame));
434 : }
435 :
436 0 : RootedScript script(cx, frame.script());
437 0 : if (!script->hasTrynotes())
438 0 : return;
439 :
440 0 : bool inForOfIterClose = false;
441 :
442 0 : for (TryNoteIterIon tni(cx, frame); !tni.done(); ++tni) {
443 0 : JSTryNote* tn = *tni;
444 :
445 0 : switch (tn->kind) {
446 : case JSTRY_FOR_IN:
447 : case JSTRY_DESTRUCTURING_ITERCLOSE:
448 0 : MOZ_ASSERT_IF(tn->kind == JSTRY_FOR_IN,
449 : JSOp(*(script->main() + tn->start + tn->length)) == JSOP_ENDITER);
450 0 : CloseLiveIteratorIon(cx, frame, tn);
451 0 : break;
452 :
453 : case JSTRY_FOR_OF_ITERCLOSE:
454 0 : inForOfIterClose = true;
455 0 : break;
456 :
457 : case JSTRY_FOR_OF:
458 0 : inForOfIterClose = false;
459 0 : break;
460 :
461 : case JSTRY_LOOP:
462 0 : break;
463 :
464 : case JSTRY_CATCH:
465 0 : if (cx->isExceptionPending()) {
466 : // See corresponding comment in ProcessTryNotes.
467 0 : if (inForOfIterClose)
468 0 : break;
469 :
470 : // Ion can compile try-catch, but bailing out to catch
471 : // exceptions is slow. Reset the warm-up counter so that if we
472 : // catch many exceptions we won't Ion-compile the script.
473 0 : script->resetWarmUpCounter();
474 :
475 : // Bailout at the start of the catch block.
476 0 : jsbytecode* catchPC = script->main() + tn->start + tn->length;
477 0 : ExceptionBailoutInfo excInfo(frame.frameNo(), catchPC, tn->stackDepth);
478 0 : uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, excInfo, overrecursed);
479 0 : if (retval == BAILOUT_RETURN_OK) {
480 : // Record exception locations to allow scope unwinding in
481 : // |FinishBailoutToBaseline|
482 0 : MOZ_ASSERT(cx->isExceptionPending());
483 0 : rfe->bailoutInfo->tryPC = UnwindEnvironmentToTryPc(frame.script(), tn);
484 0 : rfe->bailoutInfo->faultPC = frame.pc();
485 0 : return;
486 : }
487 :
488 : // Error on bailout clears pending exception.
489 0 : MOZ_ASSERT(!cx->isExceptionPending());
490 : }
491 0 : break;
492 :
493 : default:
494 0 : MOZ_CRASH("Unexpected try note");
495 : }
496 : }
497 : }
498 :
499 : static void
500 78 : OnLeaveBaselineFrame(JSContext* cx, const JitFrameIterator& frame, jsbytecode* pc,
501 : ResumeFromException* rfe, bool frameOk)
502 : {
503 78 : BaselineFrame* baselineFrame = frame.baselineFrame();
504 78 : if (jit::DebugEpilogue(cx, baselineFrame, pc, frameOk)) {
505 0 : rfe->kind = ResumeFromException::RESUME_FORCED_RETURN;
506 0 : rfe->framePointer = frame.fp() - BaselineFrame::FramePointerOffset;
507 0 : rfe->stackPointer = reinterpret_cast<uint8_t*>(baselineFrame);
508 : }
509 78 : }
510 :
511 : static inline void
512 0 : ForcedReturn(JSContext* cx, const JitFrameIterator& frame, jsbytecode* pc,
513 : ResumeFromException* rfe)
514 : {
515 0 : OnLeaveBaselineFrame(cx, frame, pc, rfe, true);
516 0 : }
517 :
518 : static inline void
519 1915 : BaselineFrameAndStackPointersFromTryNote(JSTryNote* tn, const JitFrameIterator& frame,
520 : uint8_t** framePointer, uint8_t** stackPointer)
521 : {
522 1915 : JSScript* script = frame.baselineFrame()->script();
523 1915 : *framePointer = frame.fp() - BaselineFrame::FramePointerOffset;
524 3830 : *stackPointer = *framePointer - BaselineFrame::Size() -
525 1915 : (script->nfixed() + tn->stackDepth) * sizeof(Value);
526 1915 : }
527 :
528 : static void
529 1915 : SettleOnTryNote(JSContext* cx, JSTryNote* tn, const JitFrameIterator& frame,
530 : EnvironmentIter& ei, ResumeFromException* rfe, jsbytecode** pc)
531 : {
532 3830 : RootedScript script(cx, frame.baselineFrame()->script());
533 :
534 : // Unwind environment chain (pop block objects).
535 1915 : if (cx->isExceptionPending())
536 1915 : UnwindEnvironment(cx, ei, UnwindEnvironmentToTryPc(script, tn));
537 :
538 : // Compute base pointer and stack pointer.
539 1915 : BaselineFrameAndStackPointersFromTryNote(tn, frame, &rfe->framePointer, &rfe->stackPointer);
540 :
541 : // Compute the pc.
542 1915 : *pc = script->main() + tn->start + tn->length;
543 1915 : }
544 :
545 : struct AutoBaselineHandlingException
546 : {
547 : BaselineFrame* frame;
548 1993 : AutoBaselineHandlingException(BaselineFrame* frame, jsbytecode* pc)
549 1993 : : frame(frame)
550 : {
551 1993 : frame->setIsHandlingException();
552 1993 : frame->setOverridePc(pc);
553 1993 : }
554 3986 : ~AutoBaselineHandlingException() {
555 1993 : frame->unsetIsHandlingException();
556 1993 : frame->clearOverridePc();
557 1993 : }
558 : };
559 :
560 : class BaselineFrameStackDepthOp
561 : {
562 : BaselineFrame* frame_;
563 : public:
564 1993 : explicit BaselineFrameStackDepthOp(BaselineFrame* frame)
565 1993 : : frame_(frame)
566 1993 : { }
567 1915 : uint32_t operator()() {
568 1915 : MOZ_ASSERT(frame_->numValueSlots() >= frame_->script()->nfixed());
569 1915 : return frame_->numValueSlots() - frame_->script()->nfixed();
570 : }
571 : };
572 :
573 1993 : class TryNoteIterBaseline : public TryNoteIter<BaselineFrameStackDepthOp>
574 : {
575 : public:
576 1993 : TryNoteIterBaseline(JSContext* cx, BaselineFrame* frame, jsbytecode* pc)
577 1993 : : TryNoteIter(cx, frame->script(), pc, BaselineFrameStackDepthOp(frame))
578 1993 : { }
579 : };
580 :
581 : // Close all live iterators on a BaselineFrame due to exception unwinding. The
582 : // pc parameter is updated to where the envs have been unwound to.
583 : static void
584 0 : CloseLiveIteratorsBaselineForUncatchableException(JSContext* cx, const JitFrameIterator& frame,
585 : jsbytecode* pc)
586 : {
587 0 : for (TryNoteIterBaseline tni(cx, frame.baselineFrame(), pc); !tni.done(); ++tni) {
588 0 : JSTryNote* tn = *tni;
589 :
590 0 : if (tn->kind == JSTRY_FOR_IN) {
591 : uint8_t* framePointer;
592 : uint8_t* stackPointer;
593 0 : BaselineFrameAndStackPointersFromTryNote(tn, frame, &framePointer, &stackPointer);
594 0 : Value iterValue(*(Value*) stackPointer);
595 0 : RootedObject iterObject(cx, &iterValue.toObject());
596 0 : UnwindIteratorForUncatchableException(cx, iterObject);
597 : }
598 : }
599 0 : }
600 :
601 : static bool
602 1993 : ProcessTryNotesBaseline(JSContext* cx, const JitFrameIterator& frame, EnvironmentIter& ei,
603 : ResumeFromException* rfe, jsbytecode** pc)
604 : {
605 3986 : RootedScript script(cx, frame.baselineFrame()->script());
606 1993 : bool inForOfIterClose = false;
607 :
608 1993 : for (TryNoteIterBaseline tni(cx, frame.baselineFrame(), *pc); !tni.done(); ++tni) {
609 1915 : JSTryNote* tn = *tni;
610 :
611 1915 : MOZ_ASSERT(cx->isExceptionPending());
612 1915 : switch (tn->kind) {
613 : case JSTRY_CATCH: {
614 : // If we're closing a legacy generator, we have to skip catch
615 : // blocks.
616 1915 : if (cx->isClosingGenerator())
617 0 : break;
618 :
619 : // See corresponding comment in ProcessTryNotes.
620 1915 : if (inForOfIterClose)
621 0 : break;
622 :
623 1915 : SettleOnTryNote(cx, tn, frame, ei, rfe, pc);
624 :
625 : // Ion can compile try-catch, but bailing out to catch
626 : // exceptions is slow. Reset the warm-up counter so that if we
627 : // catch many exceptions we won't Ion-compile the script.
628 1915 : script->resetWarmUpCounter();
629 :
630 : // Resume at the start of the catch block.
631 1915 : rfe->kind = ResumeFromException::RESUME_CATCH;
632 1915 : rfe->target = script->baselineScript()->nativeCodeForPC(script, *pc);
633 1915 : return true;
634 : }
635 :
636 : case JSTRY_FINALLY: {
637 : // See corresponding comment in ProcessTryNotes.
638 0 : if (inForOfIterClose)
639 0 : break;
640 :
641 0 : SettleOnTryNote(cx, tn, frame, ei, rfe, pc);
642 0 : rfe->kind = ResumeFromException::RESUME_FINALLY;
643 0 : rfe->target = script->baselineScript()->nativeCodeForPC(script, *pc);
644 : // Drop the exception instead of leaking cross compartment data.
645 0 : if (!cx->getPendingException(MutableHandleValue::fromMarkedLocation(&rfe->exception)))
646 0 : rfe->exception = UndefinedValue();
647 0 : cx->clearPendingException();
648 0 : return true;
649 : }
650 :
651 : case JSTRY_FOR_IN: {
652 : uint8_t* framePointer;
653 : uint8_t* stackPointer;
654 0 : BaselineFrameAndStackPointersFromTryNote(tn, frame, &framePointer, &stackPointer);
655 0 : Value iterValue(*reinterpret_cast<Value*>(stackPointer));
656 0 : RootedObject iterObject(cx, &iterValue.toObject());
657 0 : if (!UnwindIteratorForException(cx, iterObject)) {
658 : // See comment in the JSTRY_FOR_IN case in Interpreter.cpp's
659 : // ProcessTryNotes.
660 0 : SettleOnTryNote(cx, tn, frame, ei, rfe, pc);
661 0 : MOZ_ASSERT(**pc == JSOP_ENDITER);
662 0 : return false;
663 : }
664 0 : break;
665 : }
666 :
667 : case JSTRY_DESTRUCTURING_ITERCLOSE: {
668 : uint8_t* framePointer;
669 : uint8_t* stackPointer;
670 0 : BaselineFrameAndStackPointersFromTryNote(tn, frame, &framePointer, &stackPointer);
671 0 : RootedValue doneValue(cx, *(reinterpret_cast<Value*>(stackPointer)));
672 0 : bool done = ToBoolean(doneValue);
673 0 : if (!done) {
674 0 : Value iterValue(*(reinterpret_cast<Value*>(stackPointer) + 1));
675 0 : RootedObject iterObject(cx, &iterValue.toObject());
676 0 : if (!IteratorCloseForException(cx, iterObject)) {
677 0 : SettleOnTryNote(cx, tn, frame, ei, rfe, pc);
678 0 : return false;
679 : }
680 : }
681 0 : break;
682 : }
683 :
684 : case JSTRY_FOR_OF_ITERCLOSE:
685 0 : inForOfIterClose = true;
686 0 : break;
687 :
688 : case JSTRY_FOR_OF:
689 0 : inForOfIterClose = false;
690 0 : break;
691 :
692 : case JSTRY_LOOP:
693 0 : break;
694 :
695 : default:
696 0 : MOZ_CRASH("Invalid try note");
697 : }
698 : }
699 78 : return true;
700 : }
701 :
702 : static void
703 1993 : HandleExceptionBaseline(JSContext* cx, const JitFrameIterator& frame, ResumeFromException* rfe,
704 : jsbytecode* pc)
705 : {
706 1993 : MOZ_ASSERT(frame.isBaselineJS());
707 :
708 1993 : bool frameOk = false;
709 2071 : RootedScript script(cx, frame.baselineFrame()->script());
710 :
711 1993 : if (script->hasScriptCounts()) {
712 1993 : PCCounts* counts = script->getThrowCounts(pc);
713 : // If we failed to allocate, then skip the increment and continue to
714 : // handle the exception.
715 1993 : if (counts)
716 1993 : counts->numExec()++;
717 : }
718 :
719 : // We may be propagating a forced return from the interrupt
720 : // callback, which cannot easily force a return.
721 1993 : if (cx->isPropagatingForcedReturn()) {
722 0 : cx->clearPropagatingForcedReturn();
723 0 : ForcedReturn(cx, frame, pc, rfe);
724 0 : return;
725 : }
726 :
727 : again:
728 1993 : if (cx->isExceptionPending()) {
729 1993 : if (!cx->isClosingGenerator()) {
730 1993 : switch (Debugger::onExceptionUnwind(cx, frame.baselineFrame())) {
731 : case JSTRAP_ERROR:
732 : // Uncatchable exception.
733 0 : MOZ_ASSERT(!cx->isExceptionPending());
734 0 : goto again;
735 :
736 : case JSTRAP_CONTINUE:
737 : case JSTRAP_THROW:
738 1993 : MOZ_ASSERT(cx->isExceptionPending());
739 1993 : break;
740 :
741 : case JSTRAP_RETURN:
742 0 : if (script->hasTrynotes())
743 0 : CloseLiveIteratorsBaselineForUncatchableException(cx, frame, pc);
744 0 : ForcedReturn(cx, frame, pc, rfe);
745 0 : return;
746 :
747 : default:
748 0 : MOZ_CRASH("Invalid trap status");
749 : }
750 : }
751 :
752 1993 : if (script->hasTrynotes()) {
753 2071 : EnvironmentIter ei(cx, frame.baselineFrame(), pc);
754 1993 : if (!ProcessTryNotesBaseline(cx, frame, ei, rfe, &pc))
755 0 : goto again;
756 1993 : if (rfe->kind != ResumeFromException::RESUME_ENTRY_FRAME) {
757 : // No need to increment the PCCounts number of execution here,
758 : // as the interpreter increments any PCCounts if present.
759 1915 : MOZ_ASSERT_IF(script->hasScriptCounts(), script->maybeGetPCCounts(pc));
760 1915 : return;
761 : }
762 : }
763 :
764 78 : frameOk = HandleClosingGeneratorReturn(cx, frame.baselineFrame(), frameOk);
765 78 : frameOk = Debugger::onLeaveFrame(cx, frame.baselineFrame(), pc, frameOk);
766 0 : } else if (script->hasTrynotes()) {
767 0 : CloseLiveIteratorsBaselineForUncatchableException(cx, frame, pc);
768 : }
769 :
770 78 : OnLeaveBaselineFrame(cx, frame, pc, rfe, frameOk);
771 : }
772 :
773 : struct AutoDeleteDebugModeOSRInfo
774 : {
775 : BaselineFrame* frame;
776 1993 : explicit AutoDeleteDebugModeOSRInfo(BaselineFrame* frame) : frame(frame) { MOZ_ASSERT(frame); }
777 1993 : ~AutoDeleteDebugModeOSRInfo() { frame->deleteDebugModeOSRInfo(); }
778 : };
779 :
780 : struct AutoResetLastProfilerFrameOnReturnFromException
781 : {
782 : JSContext* cx;
783 : ResumeFromException* rfe;
784 :
785 1993 : AutoResetLastProfilerFrameOnReturnFromException(JSContext* cx, ResumeFromException* rfe)
786 1993 : : cx(cx), rfe(rfe) {}
787 :
788 1993 : ~AutoResetLastProfilerFrameOnReturnFromException() {
789 1993 : if (!cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime()))
790 3986 : return;
791 :
792 0 : MOZ_ASSERT(cx->jitActivation == cx->profilingActivation());
793 :
794 0 : void* lastProfilingFrame = getLastProfilingFrame();
795 0 : cx->jitActivation->setLastProfilingFrame(lastProfilingFrame);
796 1993 : }
797 :
798 0 : void* getLastProfilingFrame() {
799 0 : switch (rfe->kind) {
800 : case ResumeFromException::RESUME_ENTRY_FRAME:
801 0 : return nullptr;
802 :
803 : // The following all return into baseline frames.
804 : case ResumeFromException::RESUME_CATCH:
805 : case ResumeFromException::RESUME_FINALLY:
806 : case ResumeFromException::RESUME_FORCED_RETURN:
807 0 : return rfe->framePointer + BaselineFrame::FramePointerOffset;
808 :
809 : // When resuming into a bailed-out ion frame, use the bailout info to
810 : // find the frame we are resuming into.
811 : case ResumeFromException::RESUME_BAILOUT:
812 0 : return rfe->bailoutInfo->incomingStack;
813 : }
814 :
815 0 : MOZ_CRASH("Invalid ResumeFromException type!");
816 : return nullptr;
817 : }
818 : };
819 :
820 : void
821 1993 : HandleException(ResumeFromException* rfe)
822 : {
823 1993 : JSContext* cx = TlsContext.get();
824 1993 : TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
825 :
826 2071 : AutoResetLastProfilerFrameOnReturnFromException profFrameReset(cx, rfe);
827 :
828 1993 : rfe->kind = ResumeFromException::RESUME_ENTRY_FRAME;
829 :
830 1993 : JitSpew(JitSpew_IonInvalidate, "handling exception");
831 :
832 : // Clear any Ion return override that's been set.
833 : // This may happen if a callVM function causes an invalidation (setting the
834 : // override), and then fails, bypassing the bailout handlers that would
835 : // otherwise clear the return override.
836 1993 : if (cx->hasIonReturnOverride())
837 0 : cx->takeIonReturnOverride();
838 :
839 1993 : JitActivation* activation = cx->activation()->asJit();
840 :
841 : #ifdef CHECK_OSIPOINT_REGISTERS
842 1993 : if (JitOptions.checkOsiPointRegisters)
843 0 : activation->setCheckRegs(false);
844 : #endif
845 :
846 : // The Debugger onExceptionUnwind hook (reachable via
847 : // HandleExceptionBaseline below) may cause on-stack recompilation of
848 : // baseline scripts, which may patch return addresses on the stack. Since
849 : // JitFrameIterators cache the previous frame's return address when
850 : // iterating, we need a variant here that is automatically updated should
851 : // on-stack recompilation occur.
852 2071 : DebugModeOSRVolatileJitFrameIterator iter(cx);
853 9963 : while (!iter.isEntry()) {
854 5900 : bool overrecursed = false;
855 5900 : if (iter.isIonJS()) {
856 : // Search each inlined frame for live iterator objects, and close
857 : // them.
858 0 : InlineFrameIterator frames(cx, &iter);
859 :
860 : // Invalidation state will be the same for all inlined scripts in the frame.
861 0 : IonScript* ionScript = nullptr;
862 0 : bool invalidated = iter.checkInvalidation(&ionScript);
863 :
864 : #ifdef JS_TRACE_LOGGING
865 0 : if (logger && cx->compartment()->isDebuggee() && logger->enabled()) {
866 : logger->disable(/* force = */ true,
867 : "Forcefully disabled tracelogger, due to "
868 : "throwing an exception with an active Debugger "
869 0 : "in IonMonkey.");
870 : }
871 : #endif
872 :
873 : for (;;) {
874 0 : HandleExceptionIon(cx, frames, rfe, &overrecursed);
875 :
876 0 : if (rfe->kind == ResumeFromException::RESUME_BAILOUT) {
877 0 : if (invalidated)
878 0 : ionScript->decrementInvalidationCount(cx->runtime()->defaultFreeOp());
879 0 : return;
880 : }
881 :
882 0 : MOZ_ASSERT(rfe->kind == ResumeFromException::RESUME_ENTRY_FRAME);
883 :
884 : // When profiling, each frame popped needs a notification that
885 : // the function has exited, so invoke the probe that a function
886 : // is exiting.
887 :
888 0 : JSScript* script = frames.script();
889 0 : probes::ExitScript(cx, script, script->functionNonDelazifying(),
890 0 : /* popProfilerFrame = */ false);
891 0 : if (!frames.more()) {
892 0 : TraceLogStopEvent(logger, TraceLogger_IonMonkey);
893 0 : TraceLogStopEvent(logger, TraceLogger_Scripts);
894 0 : break;
895 : }
896 0 : ++frames;
897 0 : }
898 :
899 0 : activation->removeIonFrameRecovery(iter.jsFrame());
900 0 : if (invalidated)
901 0 : ionScript->decrementInvalidationCount(cx->runtime()->defaultFreeOp());
902 :
903 5900 : } else if (iter.isBaselineJS()) {
904 : // Set a flag on the frame to signal to DebugModeOSR that we're
905 : // handling an exception. Also ensure the frame has an override
906 : // pc. We clear the frame's override pc when we leave this block,
907 : // this is fine because we're either:
908 : //
909 : // (1) Going to enter a catch or finally block. We don't want to
910 : // keep the old pc when we're executing JIT code.
911 : // (2) Going to pop the frame, either here or a forced return.
912 : // In this case nothing will observe the frame's pc.
913 : // (3) Performing an exception bailout. In this case
914 : // FinishBailoutToBaseline will set the pc to the resume pc
915 : // and clear it before it returns to JIT code.
916 : jsbytecode* pc;
917 1993 : iter.baselineScriptAndPc(nullptr, &pc);
918 2071 : AutoBaselineHandlingException handlingException(iter.baselineFrame(), pc);
919 :
920 1993 : HandleExceptionBaseline(cx, iter, rfe, pc);
921 :
922 : // If we are propagating an exception through a frame with
923 : // on-stack recompile info, we should free the allocated
924 : // RecompileInfo struct before we leave this block, as we will not
925 : // be returning to the recompile handler.
926 2071 : AutoDeleteDebugModeOSRInfo deleteDebugModeOSRInfo(iter.baselineFrame());
927 :
928 3908 : if (rfe->kind != ResumeFromException::RESUME_ENTRY_FRAME &&
929 1915 : rfe->kind != ResumeFromException::RESUME_FORCED_RETURN)
930 : {
931 1915 : return;
932 : }
933 :
934 78 : TraceLogStopEvent(logger, TraceLogger_Baseline);
935 78 : TraceLogStopEvent(logger, TraceLogger_Scripts);
936 :
937 : // Unwind profiler pseudo-stack
938 78 : JSScript* script = iter.script();
939 78 : probes::ExitScript(cx, script, script->functionNonDelazifying(),
940 78 : /* popProfilerFrame = */ false);
941 :
942 78 : if (rfe->kind == ResumeFromException::RESUME_FORCED_RETURN)
943 0 : return;
944 : }
945 :
946 3985 : JitFrameLayout* current = iter.isScripted() ? iter.jsFrame() : nullptr;
947 :
948 3985 : ++iter;
949 :
950 3985 : if (current) {
951 : // Unwind the frame by updating exitFP. This is necessary so that
952 : // (1) debugger exception unwind and leave frame hooks don't see this
953 : // frame when they use ScriptFrameIter, and (2) ScriptFrameIter does
954 : // not crash when accessing an IonScript that's destroyed by the
955 : // ionScript->decref call.
956 78 : EnsureBareExitFrame(cx, current);
957 : }
958 :
959 3985 : if (overrecursed) {
960 : // We hit an overrecursion error during bailout. Report it now.
961 0 : ReportOverRecursed(cx);
962 : }
963 : }
964 :
965 78 : rfe->stackPointer = iter.fp();
966 : }
967 :
968 : // Turns a JitFrameLayout into an ExitFrameLayout. Note that it has to be a
969 : // bare exit frame so it's ignored by TraceJitExitFrame.
970 : void
971 156 : EnsureBareExitFrame(JSContext* cx, JitFrameLayout* frame)
972 : {
973 156 : ExitFrameLayout* exitFrame = reinterpret_cast<ExitFrameLayout*>(frame);
974 :
975 156 : if (cx->activation()->asJit()->exitFP() == (uint8_t*)frame) {
976 : // If we already called this function for the current frame, do
977 : // nothing.
978 78 : MOZ_ASSERT(exitFrame->isBareExit());
979 78 : return;
980 : }
981 :
982 : #ifdef DEBUG
983 78 : JitFrameIterator iter(cx);
984 234 : while (!iter.isScripted())
985 78 : ++iter;
986 78 : MOZ_ASSERT(iter.current() == frame, "|frame| must be the top JS frame");
987 :
988 78 : MOZ_ASSERT(!!cx->activation()->asJit()->exitFP());
989 78 : MOZ_ASSERT((uint8_t*)exitFrame->footer() >= cx->activation()->asJit()->exitFP(),
990 : "Must have space for ExitFooterFrame before exitFP");
991 : #endif
992 :
993 78 : cx->activation()->asJit()->setExitFP((uint8_t*)frame);
994 78 : *exitFrame->footer()->addressOfJitCode() = ExitFrameLayout::BareToken();
995 78 : MOZ_ASSERT(exitFrame->isBareExit());
996 : }
997 :
998 : CalleeToken
999 27 : TraceCalleeToken(JSTracer* trc, CalleeToken token)
1000 : {
1001 27 : switch (CalleeTokenTag tag = GetCalleeTokenTag(token)) {
1002 : case CalleeToken_Function:
1003 : case CalleeToken_FunctionConstructing:
1004 : {
1005 27 : JSFunction* fun = CalleeTokenToFunction(token);
1006 27 : TraceRoot(trc, &fun, "jit-callee");
1007 27 : return CalleeToToken(fun, tag == CalleeToken_FunctionConstructing);
1008 : }
1009 : case CalleeToken_Script:
1010 : {
1011 0 : JSScript* script = CalleeTokenToScript(token);
1012 0 : TraceRoot(trc, &script, "jit-script");
1013 0 : return CalleeToToken(script);
1014 : }
1015 : default:
1016 0 : MOZ_CRASH("unknown callee token type");
1017 : }
1018 : }
1019 :
1020 : uintptr_t*
1021 0 : JitFrameLayout::slotRef(SafepointSlotEntry where)
1022 : {
1023 0 : if (where.stack)
1024 0 : return (uintptr_t*)((uint8_t*)this - where.slot);
1025 0 : return (uintptr_t*)((uint8_t*)argv() + where.slot);
1026 : }
1027 :
1028 : #ifdef JS_NUNBOX32
1029 : static inline uintptr_t
1030 : ReadAllocation(const JitFrameIterator& frame, const LAllocation* a)
1031 : {
1032 : if (a->isGeneralReg()) {
1033 : Register reg = a->toGeneralReg()->reg();
1034 : return frame.machineState().read(reg);
1035 : }
1036 : return *frame.jsFrame()->slotRef(SafepointSlotEntry(a));
1037 : }
1038 : #endif
1039 :
1040 : static void
1041 0 : TraceThisAndArguments(JSTracer* trc, const JitFrameIterator& frame)
1042 : {
1043 : // Trace |this| and any extra actual arguments for an Ion frame. Tracinging
1044 : // of formal arguments is taken care of by the frame's safepoint/snapshot,
1045 : // except when the script might have lazy arguments or rest, in which case
1046 : // we trace them as well. We also have to trace formals if we have a
1047 : // LazyLink frame.
1048 :
1049 0 : JitFrameLayout* layout = frame.isExitFrameLayout<LazyLinkExitFrameLayout>()
1050 0 : ? frame.exitFrame()->as<LazyLinkExitFrameLayout>()->jsFrame()
1051 0 : : frame.jsFrame();
1052 :
1053 0 : if (!CalleeTokenIsFunction(layout->calleeToken()))
1054 0 : return;
1055 :
1056 0 : size_t nargs = layout->numActualArgs();
1057 0 : size_t nformals = 0;
1058 :
1059 0 : JSFunction* fun = CalleeTokenToFunction(layout->calleeToken());
1060 0 : if (!frame.isExitFrameLayout<LazyLinkExitFrameLayout>() &&
1061 0 : !fun->nonLazyScript()->mayReadFrameArgsDirectly())
1062 : {
1063 0 : nformals = fun->nargs();
1064 : }
1065 :
1066 0 : size_t newTargetOffset = Max(nargs, fun->nargs());
1067 :
1068 0 : Value* argv = layout->argv();
1069 :
1070 : // Trace |this|.
1071 0 : TraceRoot(trc, argv, "ion-thisv");
1072 :
1073 : // Trace actual arguments beyond the formals. Note + 1 for thisv.
1074 0 : for (size_t i = nformals + 1; i < nargs + 1; i++)
1075 0 : TraceRoot(trc, &argv[i], "ion-argv");
1076 :
1077 : // Always trace the new.target from the frame. It's not in the snapshots.
1078 : // +1 to pass |this|
1079 0 : if (CalleeTokenIsConstructing(layout->calleeToken()))
1080 0 : TraceRoot(trc, &argv[1 + newTargetOffset], "ion-newTarget");
1081 : }
1082 :
1083 : #ifdef JS_NUNBOX32
1084 : static inline void
1085 : WriteAllocation(const JitFrameIterator& frame, const LAllocation* a, uintptr_t value)
1086 : {
1087 : if (a->isGeneralReg()) {
1088 : Register reg = a->toGeneralReg()->reg();
1089 : frame.machineState().write(reg, value);
1090 : } else {
1091 : *frame.jsFrame()->slotRef(SafepointSlotEntry(a)) = value;
1092 : }
1093 : }
1094 : #endif
1095 :
1096 : static void
1097 0 : TraceIonJSFrame(JSTracer* trc, const JitFrameIterator& frame)
1098 : {
1099 0 : JitFrameLayout* layout = (JitFrameLayout*)frame.fp();
1100 :
1101 0 : layout->replaceCalleeToken(TraceCalleeToken(trc, layout->calleeToken()));
1102 :
1103 0 : IonScript* ionScript = nullptr;
1104 0 : if (frame.checkInvalidation(&ionScript)) {
1105 : // This frame has been invalidated, meaning that its IonScript is no
1106 : // longer reachable through the callee token (JSFunction/JSScript->ion
1107 : // is now nullptr or recompiled). Manually trace it here.
1108 0 : IonScript::Trace(trc, ionScript);
1109 : } else {
1110 0 : ionScript = frame.ionScriptFromCalleeToken();
1111 : }
1112 :
1113 0 : TraceThisAndArguments(trc, frame);
1114 :
1115 0 : const SafepointIndex* si = ionScript->getSafepointIndex(frame.returnAddressToFp());
1116 :
1117 0 : SafepointReader safepoint(ionScript, si);
1118 :
1119 : // Scan through slots which contain pointers (or on punboxing systems,
1120 : // actual values).
1121 0 : SafepointSlotEntry entry;
1122 :
1123 0 : while (safepoint.getGcSlot(&entry)) {
1124 0 : uintptr_t* ref = layout->slotRef(entry);
1125 0 : TraceGenericPointerRoot(trc, reinterpret_cast<gc::Cell**>(ref), "ion-gc-slot");
1126 : }
1127 :
1128 0 : while (safepoint.getValueSlot(&entry)) {
1129 0 : Value* v = (Value*)layout->slotRef(entry);
1130 0 : TraceRoot(trc, v, "ion-gc-slot");
1131 : }
1132 :
1133 0 : uintptr_t* spill = frame.spillBase();
1134 0 : LiveGeneralRegisterSet gcRegs = safepoint.gcSpills();
1135 0 : LiveGeneralRegisterSet valueRegs = safepoint.valueSpills();
1136 0 : for (GeneralRegisterBackwardIterator iter(safepoint.allGprSpills()); iter.more(); ++iter) {
1137 0 : --spill;
1138 0 : if (gcRegs.has(*iter))
1139 0 : TraceGenericPointerRoot(trc, reinterpret_cast<gc::Cell**>(spill), "ion-gc-spill");
1140 0 : else if (valueRegs.has(*iter))
1141 0 : TraceRoot(trc, reinterpret_cast<Value*>(spill), "ion-value-spill");
1142 : }
1143 :
1144 : #ifdef JS_NUNBOX32
1145 : LAllocation type, payload;
1146 : while (safepoint.getNunboxSlot(&type, &payload)) {
1147 : JSValueTag tag = JSValueTag(ReadAllocation(frame, &type));
1148 : uintptr_t rawPayload = ReadAllocation(frame, &payload);
1149 :
1150 : Value v = Value::fromTagAndPayload(tag, rawPayload);
1151 : TraceRoot(trc, &v, "ion-torn-value");
1152 :
1153 : if (v != Value::fromTagAndPayload(tag, rawPayload)) {
1154 : // GC moved the value, replace the stored payload.
1155 : rawPayload = *v.payloadUIntPtr();
1156 : WriteAllocation(frame, &payload, rawPayload);
1157 : }
1158 : }
1159 : #endif
1160 0 : }
1161 :
1162 : static void
1163 0 : TraceBailoutFrame(JSTracer* trc, const JitFrameIterator& frame)
1164 : {
1165 0 : JitFrameLayout* layout = (JitFrameLayout*)frame.fp();
1166 :
1167 0 : layout->replaceCalleeToken(TraceCalleeToken(trc, layout->calleeToken()));
1168 :
1169 : // We have to trace the list of actual arguments, as only formal arguments
1170 : // are represented in the Snapshot.
1171 0 : TraceThisAndArguments(trc, frame);
1172 :
1173 : // Under a bailout, do not have a Safepoint to only iterate over GC-things.
1174 : // Thus we use a SnapshotIterator to trace all the locations which would be
1175 : // used to reconstruct the Baseline frame.
1176 : //
1177 : // Note that at the time where this function is called, we have not yet
1178 : // started to reconstruct baseline frames.
1179 :
1180 : // The vector of recover instructions is already traced as part of the
1181 : // JitActivation.
1182 0 : SnapshotIterator snapIter(frame, frame.activation()->bailoutData()->machineState());
1183 :
1184 : // For each instruction, we read the allocations without evaluating the
1185 : // recover instruction, nor reconstructing the frame. We are only looking at
1186 : // tracing readable allocations.
1187 0 : while (true) {
1188 0 : while (snapIter.moreAllocations())
1189 0 : snapIter.traceAllocation(trc);
1190 :
1191 0 : if (!snapIter.moreInstructions())
1192 0 : break;
1193 0 : snapIter.nextInstruction();
1194 : }
1195 :
1196 0 : }
1197 :
1198 : void
1199 0 : UpdateIonJSFrameForMinorGC(JSTracer* trc, const JitFrameIterator& frame)
1200 : {
1201 : // Minor GCs may move slots/elements allocated in the nursery. Update
1202 : // any slots/elements pointers stored in this frame.
1203 :
1204 0 : JitFrameLayout* layout = (JitFrameLayout*)frame.fp();
1205 :
1206 0 : IonScript* ionScript = nullptr;
1207 0 : if (frame.checkInvalidation(&ionScript)) {
1208 : // This frame has been invalidated, meaning that its IonScript is no
1209 : // longer reachable through the callee token (JSFunction/JSScript->ion
1210 : // is now nullptr or recompiled).
1211 : } else {
1212 0 : ionScript = frame.ionScriptFromCalleeToken();
1213 : }
1214 :
1215 0 : Nursery& nursery = ionScript->method()->zone()->group()->nursery();
1216 :
1217 0 : const SafepointIndex* si = ionScript->getSafepointIndex(frame.returnAddressToFp());
1218 0 : SafepointReader safepoint(ionScript, si);
1219 :
1220 0 : LiveGeneralRegisterSet slotsRegs = safepoint.slotsOrElementsSpills();
1221 0 : uintptr_t* spill = frame.spillBase();
1222 0 : for (GeneralRegisterBackwardIterator iter(safepoint.allGprSpills()); iter.more(); ++iter) {
1223 0 : --spill;
1224 0 : if (slotsRegs.has(*iter))
1225 0 : nursery.forwardBufferPointer(reinterpret_cast<HeapSlot**>(spill));
1226 : }
1227 :
1228 : // Skip to the right place in the safepoint
1229 0 : SafepointSlotEntry entry;
1230 0 : while (safepoint.getGcSlot(&entry));
1231 0 : while (safepoint.getValueSlot(&entry));
1232 : #ifdef JS_NUNBOX32
1233 : LAllocation type, payload;
1234 : while (safepoint.getNunboxSlot(&type, &payload));
1235 : #endif
1236 :
1237 0 : while (safepoint.getSlotsOrElementsSlot(&entry)) {
1238 0 : HeapSlot** slots = reinterpret_cast<HeapSlot**>(layout->slotRef(entry));
1239 0 : nursery.forwardBufferPointer(slots);
1240 : }
1241 0 : }
1242 :
1243 : static void
1244 22 : TraceBaselineStubFrame(JSTracer* trc, const JitFrameIterator& frame)
1245 : {
1246 : // Trace the ICStub pointer stored in the stub frame. This is necessary
1247 : // so that we don't destroy the stub code after unlinking the stub.
1248 :
1249 22 : MOZ_ASSERT(frame.type() == JitFrame_BaselineStub);
1250 22 : JitStubFrameLayout* layout = (JitStubFrameLayout*)frame.fp();
1251 :
1252 22 : if (ICStub* stub = layout->maybeStubPtr()) {
1253 22 : MOZ_ASSERT(stub->makesGCCalls());
1254 22 : stub->trace(trc);
1255 : }
1256 22 : }
1257 :
1258 : static void
1259 0 : TraceIonICCallFrame(JSTracer* trc, const JitFrameIterator& frame)
1260 : {
1261 0 : MOZ_ASSERT(frame.type() == JitFrame_IonICCall);
1262 0 : IonICCallFrameLayout* layout = (IonICCallFrameLayout*)frame.fp();
1263 0 : TraceRoot(trc, layout->stubCode(), "ion-ic-call-code");
1264 0 : }
1265 :
1266 : #ifdef JS_CODEGEN_MIPS32
1267 : uint8_t*
1268 : alignDoubleSpillWithOffset(uint8_t* pointer, int32_t offset)
1269 : {
1270 : uint32_t address = reinterpret_cast<uint32_t>(pointer);
1271 : address = (address - offset) & ~(ABIStackAlignment - 1);
1272 : return reinterpret_cast<uint8_t*>(address);
1273 : }
1274 :
1275 : static void
1276 : TraceJitExitFrameCopiedArguments(JSTracer* trc, const VMFunction* f, ExitFooterFrame* footer)
1277 : {
1278 : uint8_t* doubleArgs = reinterpret_cast<uint8_t*>(footer);
1279 : doubleArgs = alignDoubleSpillWithOffset(doubleArgs, sizeof(intptr_t));
1280 : if (f->outParam == Type_Handle)
1281 : doubleArgs -= sizeof(Value);
1282 : doubleArgs -= f->doubleByRefArgs() * sizeof(double);
1283 :
1284 : for (uint32_t explicitArg = 0; explicitArg < f->explicitArgs; explicitArg++) {
1285 : if (f->argProperties(explicitArg) == VMFunction::DoubleByRef) {
1286 : // Arguments with double size can only have RootValue type.
1287 : if (f->argRootType(explicitArg) == VMFunction::RootValue)
1288 : TraceRoot(trc, reinterpret_cast<Value*>(doubleArgs), "ion-vm-args");
1289 : else
1290 : MOZ_ASSERT(f->argRootType(explicitArg) == VMFunction::RootNone);
1291 : doubleArgs += sizeof(double);
1292 : }
1293 : }
1294 : }
1295 : #else
1296 : static void
1297 19 : TraceJitExitFrameCopiedArguments(JSTracer* trc, const VMFunction* f, ExitFooterFrame* footer)
1298 : {
1299 : // This is NO-OP on other platforms.
1300 19 : }
1301 : #endif
1302 :
1303 : static void
1304 24 : TraceJitExitFrame(JSTracer* trc, const JitFrameIterator& frame)
1305 : {
1306 24 : ExitFooterFrame* footer = frame.exitFrame()->footer();
1307 :
1308 : // Trace the code of the code handling the exit path. This is needed because
1309 : // invalidated script are no longer traced because data are erased by the
1310 : // invalidation and relocation data are no longer reliable. So the VM
1311 : // wrapper or the invalidation code may be GC if no JitCode keep reference
1312 : // on them.
1313 24 : MOZ_ASSERT(uintptr_t(footer->jitCode()) != uintptr_t(-1));
1314 :
1315 : // This corresponds to the case where we have build a fake exit frame which
1316 : // handles the case of a native function call. We need to trace the argument
1317 : // vector of the function call, and also new.target if it was a constructing
1318 : // call.
1319 24 : if (frame.isExitFrameLayout<NativeExitFrameLayout>()) {
1320 5 : NativeExitFrameLayout* native = frame.exitFrame()->as<NativeExitFrameLayout>();
1321 5 : size_t len = native->argc() + 2;
1322 5 : Value* vp = native->vp();
1323 5 : TraceRootRange(trc, len, vp, "ion-native-args");
1324 5 : if (frame.isExitFrameLayout<ConstructNativeExitFrameLayout>())
1325 0 : TraceRoot(trc, vp + len, "ion-native-new-target");
1326 5 : return;
1327 : }
1328 :
1329 19 : if (frame.isExitFrameLayout<IonOOLNativeExitFrameLayout>()) {
1330 : IonOOLNativeExitFrameLayout* oolnative =
1331 0 : frame.exitFrame()->as<IonOOLNativeExitFrameLayout>();
1332 0 : TraceRoot(trc, oolnative->stubCode(), "ion-ool-native-code");
1333 0 : TraceRoot(trc, oolnative->vp(), "iol-ool-native-vp");
1334 0 : size_t len = oolnative->argc() + 1;
1335 0 : TraceRootRange(trc, len, oolnative->thisp(), "ion-ool-native-thisargs");
1336 0 : return;
1337 : }
1338 :
1339 38 : if (frame.isExitFrameLayout<IonOOLPropertyOpExitFrameLayout>() ||
1340 19 : frame.isExitFrameLayout<IonOOLSetterOpExitFrameLayout>())
1341 : {
1342 : // A SetterOp frame is a different size, but that's the only relevant
1343 : // difference between the two. The fields that need tracing are all in
1344 : // the common base class.
1345 : IonOOLPropertyOpExitFrameLayout* oolgetter =
1346 0 : frame.isExitFrameLayout<IonOOLPropertyOpExitFrameLayout>()
1347 0 : ? frame.exitFrame()->as<IonOOLPropertyOpExitFrameLayout>()
1348 0 : : frame.exitFrame()->as<IonOOLSetterOpExitFrameLayout>();
1349 0 : TraceRoot(trc, oolgetter->stubCode(), "ion-ool-property-op-code");
1350 0 : TraceRoot(trc, oolgetter->vp(), "ion-ool-property-op-vp");
1351 0 : TraceRoot(trc, oolgetter->id(), "ion-ool-property-op-id");
1352 0 : TraceRoot(trc, oolgetter->obj(), "ion-ool-property-op-obj");
1353 0 : return;
1354 : }
1355 :
1356 19 : if (frame.isExitFrameLayout<IonOOLProxyExitFrameLayout>()) {
1357 0 : IonOOLProxyExitFrameLayout* oolproxy = frame.exitFrame()->as<IonOOLProxyExitFrameLayout>();
1358 0 : TraceRoot(trc, oolproxy->stubCode(), "ion-ool-proxy-code");
1359 0 : TraceRoot(trc, oolproxy->vp(), "ion-ool-proxy-vp");
1360 0 : TraceRoot(trc, oolproxy->id(), "ion-ool-proxy-id");
1361 0 : TraceRoot(trc, oolproxy->proxy(), "ion-ool-proxy-proxy");
1362 0 : return;
1363 : }
1364 :
1365 19 : if (frame.isExitFrameLayout<IonDOMExitFrameLayout>()) {
1366 0 : IonDOMExitFrameLayout* dom = frame.exitFrame()->as<IonDOMExitFrameLayout>();
1367 0 : TraceRoot(trc, dom->thisObjAddress(), "ion-dom-args");
1368 0 : if (dom->isMethodFrame()) {
1369 : IonDOMMethodExitFrameLayout* method =
1370 0 : reinterpret_cast<IonDOMMethodExitFrameLayout*>(dom);
1371 0 : size_t len = method->argc() + 2;
1372 0 : Value* vp = method->vp();
1373 0 : TraceRootRange(trc, len, vp, "ion-dom-args");
1374 : } else {
1375 0 : TraceRoot(trc, dom->vp(), "ion-dom-args");
1376 : }
1377 0 : return;
1378 : }
1379 :
1380 19 : if (frame.isExitFrameLayout<LazyLinkExitFrameLayout>()) {
1381 0 : LazyLinkExitFrameLayout* ll = frame.exitFrame()->as<LazyLinkExitFrameLayout>();
1382 0 : JitFrameLayout* layout = ll->jsFrame();
1383 :
1384 0 : TraceRoot(trc, ll->stubCode(), "lazy-link-code");
1385 0 : layout->replaceCalleeToken(TraceCalleeToken(trc, layout->calleeToken()));
1386 0 : TraceThisAndArguments(trc, frame);
1387 0 : return;
1388 : }
1389 :
1390 19 : if (frame.isBareExit()) {
1391 : // Nothing to trace. Fake exit frame pushed for VM functions with
1392 : // nothing to trace on the stack.
1393 0 : return;
1394 : }
1395 :
1396 19 : TraceRoot(trc, footer->addressOfJitCode(), "ion-exit-code");
1397 :
1398 19 : const VMFunction* f = footer->function();
1399 19 : if (f == nullptr)
1400 0 : return;
1401 :
1402 : // Trace arguments of the VM wrapper.
1403 19 : uint8_t* argBase = frame.exitFrame()->argBase();
1404 90 : for (uint32_t explicitArg = 0; explicitArg < f->explicitArgs; explicitArg++) {
1405 71 : switch (f->argRootType(explicitArg)) {
1406 : case VMFunction::RootNone:
1407 62 : break;
1408 : case VMFunction::RootObject: {
1409 : // Sometimes we can bake in HandleObjects to nullptr.
1410 1 : JSObject** pobj = reinterpret_cast<JSObject**>(argBase);
1411 1 : if (*pobj)
1412 1 : TraceRoot(trc, pobj, "ion-vm-args");
1413 1 : break;
1414 : }
1415 : case VMFunction::RootString:
1416 1 : TraceRoot(trc, reinterpret_cast<JSString**>(argBase), "ion-vm-args");
1417 1 : break;
1418 : case VMFunction::RootFunction:
1419 0 : TraceRoot(trc, reinterpret_cast<JSFunction**>(argBase), "ion-vm-args");
1420 0 : break;
1421 : case VMFunction::RootValue:
1422 7 : TraceRoot(trc, reinterpret_cast<Value*>(argBase), "ion-vm-args");
1423 7 : break;
1424 : case VMFunction::RootId:
1425 0 : TraceRoot(trc, reinterpret_cast<jsid*>(argBase), "ion-vm-args");
1426 0 : break;
1427 : case VMFunction::RootCell:
1428 0 : TraceGenericPointerRoot(trc, reinterpret_cast<gc::Cell**>(argBase), "ion-vm-args");
1429 0 : break;
1430 : }
1431 :
1432 71 : switch (f->argProperties(explicitArg)) {
1433 : case VMFunction::WordByValue:
1434 : case VMFunction::WordByRef:
1435 71 : argBase += sizeof(void*);
1436 71 : break;
1437 : case VMFunction::DoubleByValue:
1438 : case VMFunction::DoubleByRef:
1439 0 : argBase += 2 * sizeof(void*);
1440 0 : break;
1441 : }
1442 : }
1443 :
1444 19 : if (f->outParam == Type_Handle) {
1445 15 : switch (f->outParamRootType) {
1446 : case VMFunction::RootNone:
1447 0 : MOZ_CRASH("Handle outparam must have root type");
1448 : case VMFunction::RootObject:
1449 0 : TraceRoot(trc, footer->outParam<JSObject*>(), "ion-vm-out");
1450 0 : break;
1451 : case VMFunction::RootString:
1452 0 : TraceRoot(trc, footer->outParam<JSString*>(), "ion-vm-out");
1453 0 : break;
1454 : case VMFunction::RootFunction:
1455 0 : TraceRoot(trc, footer->outParam<JSFunction*>(), "ion-vm-out");
1456 0 : break;
1457 : case VMFunction::RootValue:
1458 15 : TraceRoot(trc, footer->outParam<Value>(), "ion-vm-outvp");
1459 15 : break;
1460 : case VMFunction::RootId:
1461 0 : TraceRoot(trc, footer->outParam<jsid>(), "ion-vm-outvp");
1462 0 : break;
1463 : case VMFunction::RootCell:
1464 0 : TraceGenericPointerRoot(trc, footer->outParam<gc::Cell*>(), "ion-vm-out");
1465 0 : break;
1466 : }
1467 : }
1468 :
1469 19 : TraceJitExitFrameCopiedArguments(trc, f, footer);
1470 : }
1471 :
1472 : static void
1473 1 : TraceRectifierFrame(JSTracer* trc, const JitFrameIterator& frame)
1474 : {
1475 : // Trace thisv.
1476 : //
1477 : // Baseline JIT code generated as part of the ICCall_Fallback stub may use
1478 : // it if we're calling a constructor that returns a primitive value.
1479 1 : RectifierFrameLayout* layout = (RectifierFrameLayout*)frame.fp();
1480 1 : TraceRoot(trc, &layout->argv()[0], "ion-thisv");
1481 1 : }
1482 :
1483 : static void
1484 24 : TraceJitActivation(JSTracer* trc, const JitActivationIterator& activations)
1485 : {
1486 24 : JitActivation* activation = activations->asJit();
1487 :
1488 : #ifdef CHECK_OSIPOINT_REGISTERS
1489 24 : if (JitOptions.checkOsiPointRegisters) {
1490 : // GC can modify spilled registers, breaking our register checks.
1491 : // To handle this, we disable these checks for the current VM call
1492 : // when a GC happens.
1493 0 : activation->setCheckRegs(false);
1494 : }
1495 : #endif
1496 :
1497 24 : activation->traceRematerializedFrames(trc);
1498 24 : activation->traceIonRecovery(trc);
1499 :
1500 98 : for (JitFrameIterator frames(activations); !frames.done(); ++frames) {
1501 74 : switch (frames.type()) {
1502 : case JitFrame_Exit:
1503 24 : TraceJitExitFrame(trc, frames);
1504 24 : break;
1505 : case JitFrame_BaselineJS:
1506 27 : frames.baselineFrame()->trace(trc, frames);
1507 27 : break;
1508 : case JitFrame_IonJS:
1509 0 : TraceIonJSFrame(trc, frames);
1510 0 : break;
1511 : case JitFrame_BaselineStub:
1512 22 : TraceBaselineStubFrame(trc, frames);
1513 22 : break;
1514 : case JitFrame_Bailout:
1515 0 : TraceBailoutFrame(trc, frames);
1516 0 : break;
1517 : case JitFrame_Rectifier:
1518 1 : TraceRectifierFrame(trc, frames);
1519 1 : break;
1520 : case JitFrame_IonICCall:
1521 0 : TraceIonICCallFrame(trc, frames);
1522 0 : break;
1523 : default:
1524 0 : MOZ_CRASH("unexpected frame type");
1525 : }
1526 : }
1527 24 : }
1528 :
1529 : void
1530 22 : TraceJitActivations(JSContext* cx, const CooperatingContext& target, JSTracer* trc)
1531 : {
1532 46 : for (JitActivationIterator activations(cx, target); !activations.done(); ++activations)
1533 24 : TraceJitActivation(trc, activations);
1534 22 : }
1535 :
1536 : void
1537 21 : UpdateJitActivationsForMinorGC(JSRuntime* rt, JSTracer* trc)
1538 : {
1539 21 : MOZ_ASSERT(JS::CurrentThreadIsHeapMinorCollecting());
1540 21 : JSContext* cx = TlsContext.get();
1541 42 : for (const CooperatingContext& target : rt->cooperatingContexts()) {
1542 45 : for (JitActivationIterator activations(cx, target); !activations.done(); ++activations) {
1543 98 : for (JitFrameIterator frames(activations); !frames.done(); ++frames) {
1544 74 : if (frames.type() == JitFrame_IonJS)
1545 0 : UpdateIonJSFrameForMinorGC(trc, frames);
1546 : }
1547 : }
1548 : }
1549 21 : }
1550 :
1551 : void
1552 4072 : GetPcScript(JSContext* cx, JSScript** scriptRes, jsbytecode** pcRes)
1553 : {
1554 4072 : JitSpew(JitSpew_IonSnapshots, "Recover PC & Script from the last frame.");
1555 :
1556 : // Recover the return address so that we can look it up in the
1557 : // PcScriptCache, as script/pc computation is expensive.
1558 4072 : JitActivationIterator iter(cx);
1559 4072 : JitFrameIterator it(iter);
1560 : uint8_t* retAddr;
1561 4072 : if (it.isExitFrame()) {
1562 4072 : ++it;
1563 :
1564 : // Skip rectifier frames.
1565 4072 : if (it.isRectifier()) {
1566 0 : ++it;
1567 0 : MOZ_ASSERT(it.isBaselineStub() || it.isBaselineJS() || it.isIonJS());
1568 : }
1569 :
1570 : // Skip Baseline/Ion stub and IC call frames.
1571 4072 : if (it.isBaselineStub()) {
1572 2273 : ++it;
1573 2273 : MOZ_ASSERT(it.isBaselineJS());
1574 1799 : } else if (it.isIonICCall()) {
1575 0 : ++it;
1576 0 : MOZ_ASSERT(it.isIonJS());
1577 : }
1578 :
1579 4072 : MOZ_ASSERT(it.isBaselineJS() || it.isIonJS());
1580 :
1581 : // Don't use the return address if the BaselineFrame has an override pc.
1582 : // The override pc is cheap to get, so we won't benefit from the cache,
1583 : // and the override pc could change without the return address changing.
1584 : // Moreover, sometimes when an override pc is present during exception
1585 : // handling, the return address is set to nullptr as a sanity check,
1586 : // since we do not return to the frame that threw the exception.
1587 4072 : if (!it.isBaselineJS() || !it.baselineFrame()->hasOverridePc()) {
1588 4072 : retAddr = it.returnAddressToFp();
1589 4072 : MOZ_ASSERT(retAddr);
1590 : } else {
1591 0 : retAddr = nullptr;
1592 : }
1593 : } else {
1594 0 : MOZ_ASSERT(it.isBailoutJS());
1595 0 : retAddr = it.returnAddress();
1596 : }
1597 :
1598 : uint32_t hash;
1599 4072 : if (retAddr) {
1600 4072 : hash = PcScriptCache::Hash(retAddr);
1601 :
1602 : // Lazily initialize the cache. The allocation may safely fail and will not GC.
1603 4072 : if (MOZ_UNLIKELY(cx->ionPcScriptCache == nullptr)) {
1604 4 : cx->ionPcScriptCache = (PcScriptCache*)js_malloc(sizeof(struct PcScriptCache));
1605 4 : if (cx->ionPcScriptCache)
1606 4 : cx->ionPcScriptCache->clear(cx->runtime()->gc.gcNumber());
1607 : }
1608 :
1609 4072 : if (cx->ionPcScriptCache && cx->ionPcScriptCache->get(cx->runtime(), hash, retAddr, scriptRes, pcRes))
1610 2739 : return;
1611 : }
1612 :
1613 : // Lookup failed: undertake expensive process to recover the innermost inlined frame.
1614 1333 : jsbytecode* pc = nullptr;
1615 1333 : if (it.isIonJS() || it.isBailoutJS()) {
1616 0 : InlineFrameIterator ifi(cx, &it);
1617 0 : *scriptRes = ifi.script();
1618 0 : pc = ifi.pc();
1619 : } else {
1620 1333 : MOZ_ASSERT(it.isBaselineJS());
1621 1333 : it.baselineScriptAndPc(scriptRes, &pc);
1622 : }
1623 :
1624 1333 : if (pcRes)
1625 246 : *pcRes = pc;
1626 :
1627 : // Add entry to cache.
1628 1333 : if (retAddr && cx->ionPcScriptCache)
1629 1333 : cx->ionPcScriptCache->add(hash, retAddr, pc, *scriptRes);
1630 : }
1631 :
1632 : uint32_t
1633 8400 : OsiIndex::returnPointDisplacement() const
1634 : {
1635 : // In general, pointer arithmetic on code is bad, but in this case,
1636 : // getting the return address from a call instruction, stepping over pools
1637 : // would be wrong.
1638 8400 : return callPointDisplacement_ + Assembler::PatchWrite_NearCallSize();
1639 : }
1640 :
1641 0 : RInstructionResults::RInstructionResults(JitFrameLayout* fp)
1642 : : results_(nullptr),
1643 : fp_(fp),
1644 0 : initialized_(false)
1645 : {
1646 0 : }
1647 :
1648 0 : RInstructionResults::RInstructionResults(RInstructionResults&& src)
1649 0 : : results_(mozilla::Move(src.results_)),
1650 0 : fp_(src.fp_),
1651 0 : initialized_(src.initialized_)
1652 : {
1653 0 : src.initialized_ = false;
1654 0 : }
1655 :
1656 : RInstructionResults&
1657 0 : RInstructionResults::operator=(RInstructionResults&& rhs)
1658 : {
1659 0 : MOZ_ASSERT(&rhs != this, "self-moves are prohibited");
1660 0 : this->~RInstructionResults();
1661 0 : new(this) RInstructionResults(mozilla::Move(rhs));
1662 0 : return *this;
1663 : }
1664 :
1665 0 : RInstructionResults::~RInstructionResults()
1666 : {
1667 : // results_ is freed by the UniquePtr.
1668 0 : }
1669 :
1670 : bool
1671 0 : RInstructionResults::init(JSContext* cx, uint32_t numResults)
1672 : {
1673 0 : if (numResults) {
1674 0 : results_ = cx->make_unique<Values>();
1675 0 : if (!results_ || !results_->growBy(numResults))
1676 0 : return false;
1677 :
1678 0 : Value guard = MagicValue(JS_ION_BAILOUT);
1679 0 : for (size_t i = 0; i < numResults; i++)
1680 0 : (*results_)[i].init(guard);
1681 : }
1682 :
1683 0 : initialized_ = true;
1684 0 : return true;
1685 : }
1686 :
1687 : bool
1688 0 : RInstructionResults::isInitialized() const
1689 : {
1690 0 : return initialized_;
1691 : }
1692 :
1693 : #ifdef DEBUG
1694 : size_t
1695 0 : RInstructionResults::length() const
1696 : {
1697 0 : return results_->length();
1698 : }
1699 : #endif
1700 :
1701 : JitFrameLayout*
1702 0 : RInstructionResults::frame() const
1703 : {
1704 0 : MOZ_ASSERT(fp_);
1705 0 : return fp_;
1706 : }
1707 :
1708 : HeapPtr<Value>&
1709 0 : RInstructionResults::operator [](size_t index)
1710 : {
1711 0 : return (*results_)[index];
1712 : }
1713 :
1714 : void
1715 0 : RInstructionResults::trace(JSTracer* trc)
1716 : {
1717 : // Note: The vector necessary exists, otherwise this object would not have
1718 : // been stored on the activation from where the trace function is called.
1719 0 : TraceRange(trc, results_->length(), results_->begin(), "ion-recover-results");
1720 0 : }
1721 :
1722 :
1723 840 : SnapshotIterator::SnapshotIterator(const JitFrameIterator& iter, const MachineState* machineState)
1724 : : snapshot_(iter.ionScript()->snapshots(),
1725 : iter.snapshotOffset(),
1726 840 : iter.ionScript()->snapshotsRVATableSize(),
1727 840 : iter.ionScript()->snapshotsListSize()),
1728 : recover_(snapshot_,
1729 : iter.ionScript()->recovers(),
1730 840 : iter.ionScript()->recoversSize()),
1731 840 : fp_(iter.jsFrame()),
1732 : machine_(machineState),
1733 840 : ionScript_(iter.ionScript()),
1734 4200 : instructionResults_(nullptr)
1735 : {
1736 840 : }
1737 :
1738 6372 : SnapshotIterator::SnapshotIterator()
1739 : : snapshot_(nullptr, 0, 0, 0),
1740 : recover_(snapshot_, nullptr, 0),
1741 : fp_(nullptr),
1742 : ionScript_(nullptr),
1743 6372 : instructionResults_(nullptr)
1744 : {
1745 6372 : }
1746 :
1747 : int32_t
1748 0 : SnapshotIterator::readOuterNumActualArgs() const
1749 : {
1750 0 : return fp_->numActualArgs();
1751 : }
1752 :
1753 : uintptr_t
1754 0 : SnapshotIterator::fromStack(int32_t offset) const
1755 : {
1756 0 : return ReadFrameSlot(fp_, offset);
1757 : }
1758 :
1759 : static Value
1760 0 : FromObjectPayload(uintptr_t payload)
1761 : {
1762 : // Note: Both MIRType::Object and MIRType::ObjectOrNull are encoded in
1763 : // snapshots using JSVAL_TYPE_OBJECT.
1764 0 : return ObjectOrNullValue(reinterpret_cast<JSObject*>(payload));
1765 : }
1766 :
1767 : static Value
1768 0 : FromStringPayload(uintptr_t payload)
1769 : {
1770 0 : return StringValue(reinterpret_cast<JSString*>(payload));
1771 : }
1772 :
1773 : static Value
1774 0 : FromSymbolPayload(uintptr_t payload)
1775 : {
1776 0 : return SymbolValue(reinterpret_cast<JS::Symbol*>(payload));
1777 : }
1778 :
1779 : static Value
1780 0 : FromTypedPayload(JSValueType type, uintptr_t payload)
1781 : {
1782 0 : switch (type) {
1783 : case JSVAL_TYPE_INT32:
1784 0 : return Int32Value(payload);
1785 : case JSVAL_TYPE_BOOLEAN:
1786 0 : return BooleanValue(!!payload);
1787 : case JSVAL_TYPE_STRING:
1788 0 : return FromStringPayload(payload);
1789 : case JSVAL_TYPE_SYMBOL:
1790 0 : return FromSymbolPayload(payload);
1791 : case JSVAL_TYPE_OBJECT:
1792 0 : return FromObjectPayload(payload);
1793 : default:
1794 0 : MOZ_CRASH("unexpected type - needs payload");
1795 : }
1796 : }
1797 :
1798 : bool
1799 0 : SnapshotIterator::allocationReadable(const RValueAllocation& alloc, ReadMethod rm)
1800 : {
1801 : // If we have to recover stores, and if we are not interested in the
1802 : // default value of the instruction, then we have to check if the recover
1803 : // instruction results are available.
1804 0 : if (alloc.needSideEffect() && !(rm & RM_AlwaysDefault)) {
1805 0 : if (!hasInstructionResults())
1806 0 : return false;
1807 : }
1808 :
1809 0 : switch (alloc.mode()) {
1810 : case RValueAllocation::DOUBLE_REG:
1811 0 : return hasRegister(alloc.fpuReg());
1812 :
1813 : case RValueAllocation::TYPED_REG:
1814 0 : return hasRegister(alloc.reg2());
1815 :
1816 : #if defined(JS_NUNBOX32)
1817 : case RValueAllocation::UNTYPED_REG_REG:
1818 : return hasRegister(alloc.reg()) && hasRegister(alloc.reg2());
1819 : case RValueAllocation::UNTYPED_REG_STACK:
1820 : return hasRegister(alloc.reg()) && hasStack(alloc.stackOffset2());
1821 : case RValueAllocation::UNTYPED_STACK_REG:
1822 : return hasStack(alloc.stackOffset()) && hasRegister(alloc.reg2());
1823 : case RValueAllocation::UNTYPED_STACK_STACK:
1824 : return hasStack(alloc.stackOffset()) && hasStack(alloc.stackOffset2());
1825 : #elif defined(JS_PUNBOX64)
1826 : case RValueAllocation::UNTYPED_REG:
1827 0 : return hasRegister(alloc.reg());
1828 : case RValueAllocation::UNTYPED_STACK:
1829 0 : return hasStack(alloc.stackOffset());
1830 : #endif
1831 :
1832 : case RValueAllocation::RECOVER_INSTRUCTION:
1833 0 : return hasInstructionResult(alloc.index());
1834 : case RValueAllocation::RI_WITH_DEFAULT_CST:
1835 0 : return rm & RM_AlwaysDefault || hasInstructionResult(alloc.index());
1836 :
1837 : default:
1838 0 : return true;
1839 : }
1840 : }
1841 :
1842 : Value
1843 0 : SnapshotIterator::allocationValue(const RValueAllocation& alloc, ReadMethod rm)
1844 : {
1845 0 : switch (alloc.mode()) {
1846 : case RValueAllocation::CONSTANT:
1847 0 : return ionScript_->getConstant(alloc.index());
1848 :
1849 : case RValueAllocation::CST_UNDEFINED:
1850 0 : return UndefinedValue();
1851 :
1852 : case RValueAllocation::CST_NULL:
1853 0 : return NullValue();
1854 :
1855 : case RValueAllocation::DOUBLE_REG:
1856 0 : return DoubleValue(fromRegister(alloc.fpuReg()));
1857 :
1858 : case RValueAllocation::ANY_FLOAT_REG:
1859 : {
1860 : union {
1861 : double d;
1862 : float f;
1863 : } pun;
1864 0 : MOZ_ASSERT(alloc.fpuReg().isSingle());
1865 0 : pun.d = fromRegister(alloc.fpuReg());
1866 : // The register contains the encoding of a float32. We just read
1867 : // the bits without making any conversion.
1868 0 : return Float32Value(pun.f);
1869 : }
1870 :
1871 : case RValueAllocation::ANY_FLOAT_STACK:
1872 0 : return Float32Value(ReadFrameFloat32Slot(fp_, alloc.stackOffset()));
1873 :
1874 : case RValueAllocation::TYPED_REG:
1875 0 : return FromTypedPayload(alloc.knownType(), fromRegister(alloc.reg2()));
1876 :
1877 : case RValueAllocation::TYPED_STACK:
1878 : {
1879 0 : switch (alloc.knownType()) {
1880 : case JSVAL_TYPE_DOUBLE:
1881 0 : return DoubleValue(ReadFrameDoubleSlot(fp_, alloc.stackOffset2()));
1882 : case JSVAL_TYPE_INT32:
1883 0 : return Int32Value(ReadFrameInt32Slot(fp_, alloc.stackOffset2()));
1884 : case JSVAL_TYPE_BOOLEAN:
1885 0 : return BooleanValue(ReadFrameBooleanSlot(fp_, alloc.stackOffset2()));
1886 : case JSVAL_TYPE_STRING:
1887 0 : return FromStringPayload(fromStack(alloc.stackOffset2()));
1888 : case JSVAL_TYPE_SYMBOL:
1889 0 : return FromSymbolPayload(fromStack(alloc.stackOffset2()));
1890 : case JSVAL_TYPE_OBJECT:
1891 0 : return FromObjectPayload(fromStack(alloc.stackOffset2()));
1892 : default:
1893 0 : MOZ_CRASH("Unexpected type");
1894 : }
1895 : }
1896 :
1897 : #if defined(JS_NUNBOX32)
1898 : case RValueAllocation::UNTYPED_REG_REG:
1899 : {
1900 : return Value::fromTagAndPayload(JSValueTag(fromRegister(alloc.reg())),
1901 : fromRegister(alloc.reg2()));
1902 : }
1903 :
1904 : case RValueAllocation::UNTYPED_REG_STACK:
1905 : {
1906 : return Value::fromTagAndPayload(JSValueTag(fromRegister(alloc.reg())),
1907 : fromStack(alloc.stackOffset2()));
1908 : }
1909 :
1910 : case RValueAllocation::UNTYPED_STACK_REG:
1911 : {
1912 : return Value::fromTagAndPayload(JSValueTag(fromStack(alloc.stackOffset())),
1913 : fromRegister(alloc.reg2()));
1914 : }
1915 :
1916 : case RValueAllocation::UNTYPED_STACK_STACK:
1917 : {
1918 : return Value::fromTagAndPayload(JSValueTag(fromStack(alloc.stackOffset())),
1919 : fromStack(alloc.stackOffset2()));
1920 : }
1921 : #elif defined(JS_PUNBOX64)
1922 : case RValueAllocation::UNTYPED_REG:
1923 : {
1924 0 : return Value::fromRawBits(fromRegister(alloc.reg()));
1925 : }
1926 :
1927 : case RValueAllocation::UNTYPED_STACK:
1928 : {
1929 0 : return Value::fromRawBits(fromStack(alloc.stackOffset()));
1930 : }
1931 : #endif
1932 :
1933 : case RValueAllocation::RECOVER_INSTRUCTION:
1934 0 : return fromInstructionResult(alloc.index());
1935 :
1936 : case RValueAllocation::RI_WITH_DEFAULT_CST:
1937 0 : if (rm & RM_Normal && hasInstructionResult(alloc.index()))
1938 0 : return fromInstructionResult(alloc.index());
1939 0 : MOZ_ASSERT(rm & RM_AlwaysDefault);
1940 0 : return ionScript_->getConstant(alloc.index2());
1941 :
1942 : default:
1943 0 : MOZ_CRASH("huh?");
1944 : }
1945 : }
1946 :
1947 : const FloatRegisters::RegisterContent*
1948 0 : SnapshotIterator::floatAllocationPointer(const RValueAllocation& alloc) const
1949 : {
1950 0 : switch (alloc.mode()) {
1951 : case RValueAllocation::ANY_FLOAT_REG:
1952 0 : return machine_->address(alloc.fpuReg());
1953 :
1954 : case RValueAllocation::ANY_FLOAT_STACK:
1955 0 : return (FloatRegisters::RegisterContent*) AddressOfFrameSlot(fp_, alloc.stackOffset());
1956 :
1957 : default:
1958 0 : MOZ_CRASH("Not a float allocation.");
1959 : }
1960 : }
1961 :
1962 : Value
1963 0 : SnapshotIterator::maybeRead(const RValueAllocation& a, MaybeReadFallback& fallback)
1964 : {
1965 0 : if (allocationReadable(a))
1966 0 : return allocationValue(a);
1967 :
1968 0 : if (fallback.canRecoverResults()) {
1969 : // Code paths which are calling maybeRead are not always capable of
1970 : // returning an error code, as these code paths used to be infallible.
1971 0 : AutoEnterOOMUnsafeRegion oomUnsafe;
1972 0 : if (!initInstructionResults(fallback))
1973 0 : oomUnsafe.crash("js::jit::SnapshotIterator::maybeRead");
1974 :
1975 0 : if (allocationReadable(a))
1976 0 : return allocationValue(a);
1977 :
1978 0 : MOZ_ASSERT_UNREACHABLE("All allocations should be readable.");
1979 : }
1980 :
1981 0 : return fallback.unreadablePlaceholder();
1982 : }
1983 :
1984 : void
1985 0 : SnapshotIterator::writeAllocationValuePayload(const RValueAllocation& alloc, const Value& v)
1986 : {
1987 0 : uintptr_t payload = *v.payloadUIntPtr();
1988 : #if defined(JS_PUNBOX64)
1989 : // Do not write back the tag, as this will trigger an assertion when we will
1990 : // reconstruct the JS Value while tracing again or when bailing out.
1991 0 : payload &= JSVAL_PAYLOAD_MASK;
1992 : #endif
1993 :
1994 0 : switch (alloc.mode()) {
1995 : case RValueAllocation::CONSTANT:
1996 0 : ionScript_->getConstant(alloc.index()) = v;
1997 0 : break;
1998 :
1999 : case RValueAllocation::CST_UNDEFINED:
2000 : case RValueAllocation::CST_NULL:
2001 : case RValueAllocation::DOUBLE_REG:
2002 : case RValueAllocation::ANY_FLOAT_REG:
2003 : case RValueAllocation::ANY_FLOAT_STACK:
2004 0 : MOZ_CRASH("Not a GC thing: Unexpected write");
2005 : break;
2006 :
2007 : case RValueAllocation::TYPED_REG:
2008 0 : machine_->write(alloc.reg2(), payload);
2009 0 : break;
2010 :
2011 : case RValueAllocation::TYPED_STACK:
2012 0 : switch (alloc.knownType()) {
2013 : default:
2014 0 : MOZ_CRASH("Not a GC thing: Unexpected write");
2015 : break;
2016 : case JSVAL_TYPE_STRING:
2017 : case JSVAL_TYPE_SYMBOL:
2018 : case JSVAL_TYPE_OBJECT:
2019 0 : WriteFrameSlot(fp_, alloc.stackOffset2(), payload);
2020 0 : break;
2021 : }
2022 0 : break;
2023 :
2024 : #if defined(JS_NUNBOX32)
2025 : case RValueAllocation::UNTYPED_REG_REG:
2026 : case RValueAllocation::UNTYPED_STACK_REG:
2027 : machine_->write(alloc.reg2(), payload);
2028 : break;
2029 :
2030 : case RValueAllocation::UNTYPED_REG_STACK:
2031 : case RValueAllocation::UNTYPED_STACK_STACK:
2032 : WriteFrameSlot(fp_, alloc.stackOffset2(), payload);
2033 : break;
2034 : #elif defined(JS_PUNBOX64)
2035 : case RValueAllocation::UNTYPED_REG:
2036 0 : machine_->write(alloc.reg(), v.asRawBits());
2037 0 : break;
2038 :
2039 : case RValueAllocation::UNTYPED_STACK:
2040 0 : WriteFrameSlot(fp_, alloc.stackOffset(), v.asRawBits());
2041 0 : break;
2042 : #endif
2043 :
2044 : case RValueAllocation::RECOVER_INSTRUCTION:
2045 0 : MOZ_CRASH("Recover instructions are handled by the JitActivation.");
2046 : break;
2047 :
2048 : case RValueAllocation::RI_WITH_DEFAULT_CST:
2049 : // Assume that we are always going to be writing on the default value
2050 : // while tracing.
2051 0 : ionScript_->getConstant(alloc.index2()) = v;
2052 0 : break;
2053 :
2054 : default:
2055 0 : MOZ_CRASH("huh?");
2056 : }
2057 0 : }
2058 :
2059 : void
2060 0 : SnapshotIterator::traceAllocation(JSTracer* trc)
2061 : {
2062 0 : RValueAllocation alloc = readAllocation();
2063 0 : if (!allocationReadable(alloc, RM_AlwaysDefault))
2064 0 : return;
2065 :
2066 0 : Value v = allocationValue(alloc, RM_AlwaysDefault);
2067 0 : if (!v.isGCThing())
2068 0 : return;
2069 :
2070 0 : Value copy = v;
2071 0 : TraceRoot(trc, &v, "ion-typed-reg");
2072 0 : if (v != copy) {
2073 0 : MOZ_ASSERT(SameType(v, copy));
2074 0 : writeAllocationValuePayload(alloc, v);
2075 : }
2076 : }
2077 :
2078 : const RResumePoint*
2079 840 : SnapshotIterator::resumePoint() const
2080 : {
2081 840 : return instruction()->toResumePoint();
2082 : }
2083 :
2084 : uint32_t
2085 0 : SnapshotIterator::numAllocations() const
2086 : {
2087 0 : return instruction()->numOperands();
2088 : }
2089 :
2090 : uint32_t
2091 840 : SnapshotIterator::pcOffset() const
2092 : {
2093 840 : return resumePoint()->pcOffset();
2094 : }
2095 :
2096 : void
2097 0 : SnapshotIterator::skipInstruction()
2098 : {
2099 0 : MOZ_ASSERT(snapshot_.numAllocationsRead() == 0);
2100 0 : size_t numOperands = instruction()->numOperands();
2101 0 : for (size_t i = 0; i < numOperands; i++)
2102 0 : skip();
2103 0 : nextInstruction();
2104 0 : }
2105 :
2106 : bool
2107 0 : SnapshotIterator::initInstructionResults(MaybeReadFallback& fallback)
2108 : {
2109 0 : MOZ_ASSERT(fallback.canRecoverResults());
2110 0 : JSContext* cx = fallback.maybeCx;
2111 :
2112 : // If there is only one resume point in the list of instructions, then there
2113 : // is no instruction to recover, and thus no need to register any results.
2114 0 : if (recover_.numInstructions() == 1)
2115 0 : return true;
2116 :
2117 0 : JitFrameLayout* fp = fallback.frame->jsFrame();
2118 0 : RInstructionResults* results = fallback.activation->maybeIonFrameRecovery(fp);
2119 0 : if (!results) {
2120 0 : AutoCompartment ac(cx, fallback.frame->script());
2121 :
2122 : // We do not have the result yet, which means that an observable stack
2123 : // slot is requested. As we do not want to bailout every time for the
2124 : // same reason, we need to recompile without optimizing away the
2125 : // observable stack slots. The script would later be recompiled to have
2126 : // support for Argument objects.
2127 0 : if (fallback.consequence == MaybeReadFallback::Fallback_Invalidate)
2128 0 : ionScript_->invalidate(cx, /* resetUses = */ false, "Observe recovered instruction.");
2129 :
2130 : // Register the list of result on the activation. We need to do that
2131 : // before we initialize the list such as if any recover instruction
2132 : // cause a GC, we can ensure that the results are properly traced by the
2133 : // activation.
2134 0 : RInstructionResults tmp(fallback.frame->jsFrame());
2135 0 : if (!fallback.activation->registerIonFrameRecovery(mozilla::Move(tmp)))
2136 0 : return false;
2137 :
2138 0 : results = fallback.activation->maybeIonFrameRecovery(fp);
2139 :
2140 : // Start a new snapshot at the beginning of the JitFrameIterator. This
2141 : // SnapshotIterator is used for evaluating the content of all recover
2142 : // instructions. The result is then saved on the JitActivation.
2143 0 : MachineState machine = fallback.frame->machineState();
2144 0 : SnapshotIterator s(*fallback.frame, &machine);
2145 0 : if (!s.computeInstructionResults(cx, results)) {
2146 :
2147 : // If the evaluation failed because of OOMs, then we discard the
2148 : // current set of result that we collected so far.
2149 0 : fallback.activation->removeIonFrameRecovery(fp);
2150 0 : return false;
2151 : }
2152 : }
2153 :
2154 0 : MOZ_ASSERT(results->isInitialized());
2155 0 : MOZ_ASSERT(results->length() == recover_.numInstructions() - 1);
2156 0 : instructionResults_ = results;
2157 0 : return true;
2158 : }
2159 :
2160 : bool
2161 0 : SnapshotIterator::computeInstructionResults(JSContext* cx, RInstructionResults* results) const
2162 : {
2163 0 : MOZ_ASSERT(!results->isInitialized());
2164 0 : MOZ_ASSERT(recover_.numInstructionsRead() == 1);
2165 :
2166 : // The last instruction will always be a resume point.
2167 0 : size_t numResults = recover_.numInstructions() - 1;
2168 0 : if (!results->isInitialized()) {
2169 0 : if (!results->init(cx, numResults))
2170 0 : return false;
2171 :
2172 : // No need to iterate over the only resume point.
2173 0 : if (!numResults) {
2174 0 : MOZ_ASSERT(results->isInitialized());
2175 0 : return true;
2176 : }
2177 :
2178 : // Use AutoEnterAnalysis to avoid invoking the object metadata callback,
2179 : // which could try to walk the stack while bailing out.
2180 0 : AutoEnterAnalysis enter(cx);
2181 :
2182 : // Fill with the results of recover instructions.
2183 0 : SnapshotIterator s(*this);
2184 0 : s.instructionResults_ = results;
2185 0 : while (s.moreInstructions()) {
2186 : // Skip resume point and only interpret recover instructions.
2187 0 : if (s.instruction()->isResumePoint()) {
2188 0 : s.skipInstruction();
2189 0 : continue;
2190 : }
2191 :
2192 0 : if (!s.instruction()->recover(cx, s))
2193 0 : return false;
2194 0 : s.nextInstruction();
2195 : }
2196 : }
2197 :
2198 0 : MOZ_ASSERT(results->isInitialized());
2199 0 : return true;
2200 : }
2201 :
2202 : void
2203 0 : SnapshotIterator::storeInstructionResult(const Value& v)
2204 : {
2205 0 : uint32_t currIns = recover_.numInstructionsRead() - 1;
2206 0 : MOZ_ASSERT((*instructionResults_)[currIns].isMagic(JS_ION_BAILOUT));
2207 0 : (*instructionResults_)[currIns] = v;
2208 0 : }
2209 :
2210 : Value
2211 0 : SnapshotIterator::fromInstructionResult(uint32_t index) const
2212 : {
2213 0 : MOZ_ASSERT(!(*instructionResults_)[index].isMagic(JS_ION_BAILOUT));
2214 0 : return (*instructionResults_)[index];
2215 : }
2216 :
2217 : void
2218 840 : SnapshotIterator::settleOnFrame()
2219 : {
2220 : // Check that the current instruction can still be use.
2221 840 : MOZ_ASSERT(snapshot_.numAllocationsRead() == 0);
2222 0 : while (!instruction()->isResumePoint())
2223 0 : skipInstruction();
2224 840 : }
2225 :
2226 : void
2227 0 : SnapshotIterator::nextFrame()
2228 : {
2229 0 : nextInstruction();
2230 0 : settleOnFrame();
2231 0 : }
2232 :
2233 : Value
2234 0 : SnapshotIterator::maybeReadAllocByIndex(size_t index)
2235 : {
2236 0 : while (index--) {
2237 0 : MOZ_ASSERT(moreAllocations());
2238 0 : skip();
2239 : }
2240 :
2241 0 : Value s;
2242 : {
2243 : // This MaybeReadFallback method cannot GC.
2244 0 : JS::AutoSuppressGCAnalysis nogc;
2245 0 : MaybeReadFallback fallback(UndefinedValue());
2246 0 : s = maybeRead(fallback);
2247 : }
2248 :
2249 0 : while (moreAllocations())
2250 0 : skip();
2251 :
2252 0 : return s;
2253 : }
2254 :
2255 : JitFrameLayout*
2256 918 : JitFrameIterator::jsFrame() const
2257 : {
2258 918 : MOZ_ASSERT(isScripted());
2259 918 : if (isBailoutJS())
2260 0 : return (JitFrameLayout*) activation_->bailoutData()->fp();
2261 :
2262 918 : return (JitFrameLayout*) fp();
2263 : }
2264 :
2265 : IonScript*
2266 9240 : JitFrameIterator::ionScript() const
2267 : {
2268 9240 : MOZ_ASSERT(isIonScripted());
2269 9240 : if (isBailoutJS())
2270 0 : return activation_->bailoutData()->ionScript();
2271 :
2272 9240 : IonScript* ionScript = nullptr;
2273 9240 : if (checkInvalidation(&ionScript))
2274 0 : return ionScript;
2275 9240 : return ionScriptFromCalleeToken();
2276 : }
2277 :
2278 : IonScript*
2279 9240 : JitFrameIterator::ionScriptFromCalleeToken() const
2280 : {
2281 9240 : MOZ_ASSERT(isIonJS());
2282 9240 : MOZ_ASSERT(!checkInvalidation());
2283 9240 : return script()->ionScript();
2284 : }
2285 :
2286 : const SafepointIndex*
2287 1680 : JitFrameIterator::safepoint() const
2288 : {
2289 1680 : MOZ_ASSERT(isIonJS());
2290 1680 : if (!cachedSafepointIndex_)
2291 840 : cachedSafepointIndex_ = ionScript()->getSafepointIndex(returnAddressToFp());
2292 1680 : return cachedSafepointIndex_;
2293 : }
2294 :
2295 : SnapshotOffset
2296 840 : JitFrameIterator::snapshotOffset() const
2297 : {
2298 840 : MOZ_ASSERT(isIonScripted());
2299 840 : if (isBailoutJS())
2300 0 : return activation_->bailoutData()->snapshotOffset();
2301 840 : return osiIndex()->snapshotOffset();
2302 : }
2303 :
2304 : const OsiIndex*
2305 840 : JitFrameIterator::osiIndex() const
2306 : {
2307 840 : MOZ_ASSERT(isIonJS());
2308 840 : SafepointReader reader(ionScript(), safepoint());
2309 840 : return ionScript()->getOsiIndex(reader.osiReturnPointOffset());
2310 : }
2311 :
2312 3186 : InlineFrameIterator::InlineFrameIterator(JSContext* cx, const JitFrameIterator* iter)
2313 : : calleeTemplate_(cx),
2314 : calleeRVA_(),
2315 3186 : script_(cx)
2316 : {
2317 3186 : resetOn(iter);
2318 3186 : }
2319 :
2320 0 : InlineFrameIterator::InlineFrameIterator(JSContext* cx, const InlineFrameIterator* iter)
2321 0 : : frame_(iter ? iter->frame_ : nullptr),
2322 : framesRead_(0),
2323 0 : frameCount_(iter ? iter->frameCount_ : UINT32_MAX),
2324 : calleeTemplate_(cx),
2325 : calleeRVA_(),
2326 0 : script_(cx)
2327 : {
2328 0 : if (frame_) {
2329 0 : machine_ = iter->machine_;
2330 0 : start_ = SnapshotIterator(*frame_, &machine_);
2331 :
2332 : // findNextFrame will iterate to the next frame and init. everything.
2333 : // Therefore to settle on the same frame, we report one frame less readed.
2334 0 : framesRead_ = iter->framesRead_ - 1;
2335 0 : findNextFrame();
2336 : }
2337 0 : }
2338 :
2339 : void
2340 4026 : InlineFrameIterator::resetOn(const JitFrameIterator* iter)
2341 : {
2342 4026 : frame_ = iter;
2343 4026 : framesRead_ = 0;
2344 4026 : frameCount_ = UINT32_MAX;
2345 :
2346 4026 : if (iter) {
2347 840 : machine_ = iter->machineState();
2348 840 : start_ = SnapshotIterator(*iter, &machine_);
2349 840 : findNextFrame();
2350 : }
2351 4026 : }
2352 :
2353 : void
2354 840 : InlineFrameIterator::findNextFrame()
2355 : {
2356 840 : MOZ_ASSERT(more());
2357 :
2358 840 : si_ = start_;
2359 :
2360 : // Read the initial frame out of the C stack.
2361 840 : calleeTemplate_ = frame_->maybeCallee();
2362 840 : calleeRVA_ = RValueAllocation();
2363 840 : script_ = frame_->script();
2364 840 : MOZ_ASSERT(script_->hasBaselineScript());
2365 :
2366 : // Settle on the outermost frame without evaluating any instructions before
2367 : // looking for a pc.
2368 840 : si_.settleOnFrame();
2369 :
2370 840 : pc_ = script_->offsetToPC(si_.pcOffset());
2371 840 : numActualArgs_ = 0xbadbad;
2372 :
2373 : // This unfortunately is O(n*m), because we must skip over outer frames
2374 : // before reading inner ones.
2375 :
2376 : // The first time (frameCount_ == UINT32_MAX) we do not know the number of
2377 : // frames that we are going to inspect. So we are iterating until there is
2378 : // no more frames, to settle on the inner most frame and to count the number
2379 : // of frames.
2380 840 : size_t remaining = (frameCount_ != UINT32_MAX) ? frameNo() - 1 : SIZE_MAX;
2381 :
2382 840 : size_t i = 1;
2383 840 : for (; i <= remaining && si_.moreFrames(); i++) {
2384 0 : MOZ_ASSERT(IsIonInlinablePC(pc_));
2385 :
2386 : // Recover the number of actual arguments from the script.
2387 0 : if (JSOp(*pc_) != JSOP_FUNAPPLY)
2388 0 : numActualArgs_ = GET_ARGC(pc_);
2389 0 : if (JSOp(*pc_) == JSOP_FUNCALL) {
2390 0 : MOZ_ASSERT(GET_ARGC(pc_) > 0);
2391 0 : numActualArgs_ = GET_ARGC(pc_) - 1;
2392 0 : } else if (IsGetPropPC(pc_)) {
2393 0 : numActualArgs_ = 0;
2394 0 : } else if (IsSetPropPC(pc_)) {
2395 0 : numActualArgs_ = 1;
2396 : }
2397 :
2398 0 : if (numActualArgs_ == 0xbadbad)
2399 0 : MOZ_CRASH("Couldn't deduce the number of arguments of an ionmonkey frame");
2400 :
2401 : // Skip over non-argument slots, as well as |this|.
2402 0 : bool skipNewTarget = IsConstructorCallPC(pc_);
2403 0 : unsigned skipCount = (si_.numAllocations() - 1) - numActualArgs_ - 1 - skipNewTarget;
2404 0 : for (unsigned j = 0; j < skipCount; j++)
2405 0 : si_.skip();
2406 :
2407 : // This value should correspond to the function which is being inlined.
2408 : // The value must be readable to iterate over the inline frame. Most of
2409 : // the time, these functions are stored as JSFunction constants,
2410 : // register which are holding the JSFunction pointer, or recover
2411 : // instruction with Default value.
2412 0 : Value funval = si_.readWithDefault(&calleeRVA_);
2413 :
2414 : // Skip extra value allocations.
2415 0 : while (si_.moreAllocations())
2416 0 : si_.skip();
2417 :
2418 0 : si_.nextFrame();
2419 :
2420 0 : calleeTemplate_ = &funval.toObject().as<JSFunction>();
2421 :
2422 : // Inlined functions may be clones that still point to the lazy script
2423 : // for the executed script, if they are clones. The actual script
2424 : // exists though, just make sure the function points to it.
2425 0 : script_ = calleeTemplate_->existingScript();
2426 0 : MOZ_ASSERT(script_->hasBaselineScript());
2427 :
2428 0 : pc_ = script_->offsetToPC(si_.pcOffset());
2429 : }
2430 :
2431 : // The first time we do not know the number of frames, we only settle on the
2432 : // last frame, and update the number of frames based on the number of
2433 : // iteration that we have done.
2434 840 : if (frameCount_ == UINT32_MAX) {
2435 840 : MOZ_ASSERT(!si_.moreFrames());
2436 840 : frameCount_ = i;
2437 : }
2438 :
2439 840 : framesRead_++;
2440 840 : }
2441 :
2442 : JSFunction*
2443 0 : InlineFrameIterator::callee(MaybeReadFallback& fallback) const
2444 : {
2445 0 : MOZ_ASSERT(isFunctionFrame());
2446 0 : if (calleeRVA_.mode() == RValueAllocation::INVALID || !fallback.canRecoverResults())
2447 0 : return calleeTemplate_;
2448 :
2449 0 : SnapshotIterator s(si_);
2450 : // :TODO: Handle allocation failures from recover instruction.
2451 0 : Value funval = s.maybeRead(calleeRVA_, fallback);
2452 0 : return &funval.toObject().as<JSFunction>();
2453 : }
2454 :
2455 : JSObject*
2456 0 : InlineFrameIterator::computeEnvironmentChain(const Value& envChainValue,
2457 : MaybeReadFallback& fallback,
2458 : bool* hasInitialEnv) const
2459 : {
2460 0 : if (envChainValue.isObject()) {
2461 0 : if (hasInitialEnv) {
2462 0 : if (fallback.canRecoverResults()) {
2463 0 : RootedObject obj(fallback.maybeCx, &envChainValue.toObject());
2464 0 : *hasInitialEnv = isFunctionFrame() &&
2465 0 : callee(fallback)->needsFunctionEnvironmentObjects();
2466 0 : return obj;
2467 : } else {
2468 0 : JS::AutoSuppressGCAnalysis nogc; // If we cannot recover then we cannot GC.
2469 0 : *hasInitialEnv = isFunctionFrame() &&
2470 0 : callee(fallback)->needsFunctionEnvironmentObjects();
2471 : }
2472 : }
2473 :
2474 0 : return &envChainValue.toObject();
2475 : }
2476 :
2477 : // Note we can hit this case even for functions with a CallObject, in case
2478 : // we are walking the frame during the function prologue, before the env
2479 : // chain has been initialized.
2480 0 : if (isFunctionFrame())
2481 0 : return callee(fallback)->environment();
2482 :
2483 0 : if (isModuleFrame())
2484 0 : return script()->module()->environment();
2485 :
2486 : // Ion does not handle non-function scripts that have anything other than
2487 : // the global on their env chain.
2488 0 : MOZ_ASSERT(!script()->isForEval());
2489 0 : MOZ_ASSERT(!script()->hasNonSyntacticScope());
2490 0 : return &script()->global().lexicalEnvironment();
2491 : }
2492 :
2493 : bool
2494 840 : InlineFrameIterator::isFunctionFrame() const
2495 : {
2496 840 : return !!calleeTemplate_;
2497 : }
2498 :
2499 : bool
2500 0 : InlineFrameIterator::isModuleFrame() const
2501 : {
2502 0 : return script()->module();
2503 : }
2504 :
2505 : MachineState
2506 0 : MachineState::FromBailout(RegisterDump::GPRArray& regs, RegisterDump::FPUArray& fpregs)
2507 : {
2508 0 : MachineState machine;
2509 :
2510 0 : for (unsigned i = 0; i < Registers::Total; i++)
2511 0 : machine.setRegisterLocation(Register::FromCode(i), ®s[i].r);
2512 : #ifdef JS_CODEGEN_ARM
2513 : float* fbase = (float*)&fpregs[0];
2514 : for (unsigned i = 0; i < FloatRegisters::TotalDouble; i++)
2515 : machine.setRegisterLocation(FloatRegister(i, FloatRegister::Double), &fpregs[i].d);
2516 : for (unsigned i = 0; i < FloatRegisters::TotalSingle; i++)
2517 : machine.setRegisterLocation(FloatRegister(i, FloatRegister::Single), (double*)&fbase[i]);
2518 : #elif defined(JS_CODEGEN_MIPS32)
2519 : float* fbase = (float*)&fpregs[0];
2520 : for (unsigned i = 0; i < FloatRegisters::TotalDouble; i++) {
2521 : machine.setRegisterLocation(FloatRegister::FromIndex(i, FloatRegister::Double),
2522 : &fpregs[i].d);
2523 : }
2524 : for (unsigned i = 0; i < FloatRegisters::TotalSingle; i++) {
2525 : machine.setRegisterLocation(FloatRegister::FromIndex(i, FloatRegister::Single),
2526 : (double*)&fbase[i]);
2527 : }
2528 : #elif defined(JS_CODEGEN_MIPS64)
2529 : for (unsigned i = 0; i < FloatRegisters::TotalPhys; i++) {
2530 : machine.setRegisterLocation(FloatRegister(i, FloatRegisters::Double), &fpregs[i]);
2531 : machine.setRegisterLocation(FloatRegister(i, FloatRegisters::Single), &fpregs[i]);
2532 : }
2533 : #elif defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
2534 0 : for (unsigned i = 0; i < FloatRegisters::TotalPhys; i++) {
2535 0 : machine.setRegisterLocation(FloatRegister(i, FloatRegisters::Single), &fpregs[i]);
2536 0 : machine.setRegisterLocation(FloatRegister(i, FloatRegisters::Double), &fpregs[i]);
2537 0 : machine.setRegisterLocation(FloatRegister(i, FloatRegisters::Simd128), &fpregs[i]);
2538 : }
2539 : #elif defined(JS_CODEGEN_ARM64)
2540 : for (unsigned i = 0; i < FloatRegisters::TotalPhys; i++) {
2541 : machine.setRegisterLocation(FloatRegister(i, FloatRegisters::Single), &fpregs[i]);
2542 : machine.setRegisterLocation(FloatRegister(i, FloatRegisters::Double), &fpregs[i]);
2543 : }
2544 :
2545 : #elif defined(JS_CODEGEN_NONE)
2546 : MOZ_CRASH();
2547 : #else
2548 : # error "Unknown architecture!"
2549 : #endif
2550 0 : return machine;
2551 : }
2552 :
2553 : bool
2554 0 : InlineFrameIterator::isConstructing() const
2555 : {
2556 : // Skip the current frame and look at the caller's.
2557 0 : if (more()) {
2558 0 : InlineFrameIterator parent(TlsContext.get(), this);
2559 0 : ++parent;
2560 :
2561 : // Inlined Getters and Setters are never constructing.
2562 0 : if (IsGetPropPC(parent.pc()) || IsSetPropPC(parent.pc()))
2563 0 : return false;
2564 :
2565 : // In the case of a JS frame, look up the pc from the snapshot.
2566 0 : MOZ_ASSERT(IsCallPC(parent.pc()) && !IsSpreadCallPC(parent.pc()));
2567 :
2568 0 : return IsConstructorCallPC(parent.pc());
2569 : }
2570 :
2571 0 : return frame_->isConstructing();
2572 : }
2573 :
2574 : bool
2575 50 : JitFrameIterator::isConstructing() const
2576 : {
2577 50 : return CalleeTokenIsConstructing(calleeToken());
2578 : }
2579 :
2580 : unsigned
2581 0 : JitFrameIterator::numActualArgs() const
2582 : {
2583 0 : if (isScripted())
2584 0 : return jsFrame()->numActualArgs();
2585 :
2586 0 : MOZ_ASSERT(isExitFrameLayout<NativeExitFrameLayout>());
2587 0 : return exitFrame()->as<NativeExitFrameLayout>()->argc();
2588 : }
2589 :
2590 : void
2591 0 : SnapshotIterator::warnUnreadableAllocation()
2592 : {
2593 0 : fprintf(stderr, "Warning! Tried to access unreadable value allocation (possible f.arguments).\n");
2594 0 : }
2595 :
2596 : struct DumpOp {
2597 0 : explicit DumpOp(unsigned int i) : i_(i) {}
2598 :
2599 : unsigned int i_;
2600 0 : void operator()(const Value& v) {
2601 0 : fprintf(stderr, " actual (arg %d): ", i_);
2602 : #ifdef DEBUG
2603 0 : DumpValue(v);
2604 : #else
2605 : fprintf(stderr, "?\n");
2606 : #endif
2607 0 : i_++;
2608 0 : }
2609 : };
2610 :
2611 : void
2612 0 : JitFrameIterator::dumpBaseline() const
2613 : {
2614 0 : MOZ_ASSERT(isBaselineJS());
2615 :
2616 0 : fprintf(stderr, " JS Baseline frame\n");
2617 0 : if (isFunctionFrame()) {
2618 0 : fprintf(stderr, " callee fun: ");
2619 : #ifdef DEBUG
2620 0 : DumpObject(callee());
2621 : #else
2622 : fprintf(stderr, "?\n");
2623 : #endif
2624 : } else {
2625 0 : fprintf(stderr, " global frame, no callee\n");
2626 : }
2627 :
2628 0 : fprintf(stderr, " file %s line %" PRIuSIZE "\n",
2629 0 : script()->filename(), script()->lineno());
2630 :
2631 0 : JSContext* cx = TlsContext.get();
2632 0 : RootedScript script(cx);
2633 : jsbytecode* pc;
2634 0 : baselineScriptAndPc(script.address(), &pc);
2635 :
2636 0 : fprintf(stderr, " script = %p, pc = %p (offset %u)\n", (void*)script, pc, uint32_t(script->pcToOffset(pc)));
2637 0 : fprintf(stderr, " current op: %s\n", CodeName[*pc]);
2638 :
2639 0 : fprintf(stderr, " actual args: %d\n", numActualArgs());
2640 :
2641 0 : BaselineFrame* frame = baselineFrame();
2642 :
2643 0 : for (unsigned i = 0; i < frame->numValueSlots(); i++) {
2644 0 : fprintf(stderr, " slot %u: ", i);
2645 : #ifdef DEBUG
2646 0 : Value* v = frame->valueSlot(i);
2647 0 : DumpValue(*v);
2648 : #else
2649 : fprintf(stderr, "?\n");
2650 : #endif
2651 : }
2652 0 : }
2653 :
2654 : void
2655 0 : InlineFrameIterator::dump() const
2656 : {
2657 0 : MaybeReadFallback fallback(UndefinedValue());
2658 :
2659 0 : if (more())
2660 0 : fprintf(stderr, " JS frame (inlined)\n");
2661 : else
2662 0 : fprintf(stderr, " JS frame\n");
2663 :
2664 0 : bool isFunction = false;
2665 0 : if (isFunctionFrame()) {
2666 0 : isFunction = true;
2667 0 : fprintf(stderr, " callee fun: ");
2668 : #ifdef DEBUG
2669 0 : DumpObject(callee(fallback));
2670 : #else
2671 : fprintf(stderr, "?\n");
2672 : #endif
2673 : } else {
2674 0 : fprintf(stderr, " global frame, no callee\n");
2675 : }
2676 :
2677 0 : fprintf(stderr, " file %s line %" PRIuSIZE "\n",
2678 0 : script()->filename(), script()->lineno());
2679 :
2680 0 : fprintf(stderr, " script = %p, pc = %p\n", (void*) script(), pc());
2681 0 : fprintf(stderr, " current op: %s\n", CodeName[*pc()]);
2682 :
2683 0 : if (!more()) {
2684 0 : numActualArgs();
2685 : }
2686 :
2687 0 : SnapshotIterator si = snapshotIterator();
2688 0 : fprintf(stderr, " slots: %u\n", si.numAllocations() - 1);
2689 0 : for (unsigned i = 0; i < si.numAllocations() - 1; i++) {
2690 0 : if (isFunction) {
2691 0 : if (i == 0)
2692 0 : fprintf(stderr, " env chain: ");
2693 0 : else if (i == 1)
2694 0 : fprintf(stderr, " this: ");
2695 0 : else if (i - 2 < calleeTemplate()->nargs())
2696 0 : fprintf(stderr, " formal (arg %d): ", i - 2);
2697 : else {
2698 0 : if (i - 2 == calleeTemplate()->nargs() && numActualArgs() > calleeTemplate()->nargs()) {
2699 0 : DumpOp d(calleeTemplate()->nargs());
2700 0 : unaliasedForEachActual(TlsContext.get(), d, ReadFrame_Overflown, fallback);
2701 : }
2702 :
2703 0 : fprintf(stderr, " slot %d: ", int(i - 2 - calleeTemplate()->nargs()));
2704 : }
2705 : } else
2706 0 : fprintf(stderr, " slot %u: ", i);
2707 : #ifdef DEBUG
2708 0 : DumpValue(si.maybeRead(fallback));
2709 : #else
2710 : fprintf(stderr, "?\n");
2711 : #endif
2712 : }
2713 :
2714 0 : fputc('\n', stderr);
2715 0 : }
2716 :
2717 : void
2718 0 : JitFrameIterator::dump() const
2719 : {
2720 0 : switch (type_) {
2721 : case JitFrame_Entry:
2722 0 : fprintf(stderr, " Entry frame\n");
2723 0 : fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize()));
2724 0 : break;
2725 : case JitFrame_BaselineJS:
2726 0 : dumpBaseline();
2727 0 : break;
2728 : case JitFrame_BaselineStub:
2729 0 : fprintf(stderr, " Baseline stub frame\n");
2730 0 : fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize()));
2731 0 : break;
2732 : case JitFrame_Bailout:
2733 : case JitFrame_IonJS:
2734 : {
2735 0 : InlineFrameIterator frames(TlsContext.get(), this);
2736 : for (;;) {
2737 0 : frames.dump();
2738 0 : if (!frames.more())
2739 0 : break;
2740 0 : ++frames;
2741 : }
2742 0 : break;
2743 : }
2744 : case JitFrame_Rectifier:
2745 0 : fprintf(stderr, " Rectifier frame\n");
2746 0 : fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize()));
2747 0 : break;
2748 : case JitFrame_IonICCall:
2749 0 : fprintf(stderr, " Ion IC call\n");
2750 0 : fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize()));
2751 0 : break;
2752 : case JitFrame_Exit:
2753 0 : fprintf(stderr, " Exit frame\n");
2754 0 : break;
2755 : };
2756 0 : fputc('\n', stderr);
2757 0 : }
2758 :
2759 : #ifdef DEBUG
2760 : bool
2761 0 : JitFrameIterator::verifyReturnAddressUsingNativeToBytecodeMap()
2762 : {
2763 0 : MOZ_ASSERT(returnAddressToFp_ != nullptr);
2764 :
2765 : // Only handle Ion frames for now.
2766 0 : if (type_ != JitFrame_IonJS && type_ != JitFrame_BaselineJS)
2767 0 : return true;
2768 :
2769 0 : JSRuntime* rt = TlsContext.get()->runtime();
2770 :
2771 : // Don't verify while off thread.
2772 0 : if (!CurrentThreadCanAccessRuntime(rt))
2773 0 : return true;
2774 :
2775 : // Don't verify if sampling is being suppressed.
2776 0 : if (!TlsContext.get()->isProfilerSamplingEnabled())
2777 0 : return true;
2778 :
2779 0 : if (JS::CurrentThreadIsHeapMinorCollecting())
2780 0 : return true;
2781 :
2782 0 : JitRuntime* jitrt = rt->jitRuntime();
2783 :
2784 : // Look up and print bytecode info for the native address.
2785 0 : const JitcodeGlobalEntry* entry = jitrt->getJitcodeGlobalTable()->lookup(returnAddressToFp_);
2786 0 : if (!entry)
2787 0 : return true;
2788 :
2789 0 : JitSpew(JitSpew_Profiling, "Found nativeToBytecode entry for %p: %p - %p",
2790 0 : returnAddressToFp_, entry->nativeStartAddr(), entry->nativeEndAddr());
2791 :
2792 0 : JitcodeGlobalEntry::BytecodeLocationVector location;
2793 0 : uint32_t depth = UINT32_MAX;
2794 0 : if (!entry->callStackAtAddr(rt, returnAddressToFp_, location, &depth))
2795 0 : return false;
2796 0 : MOZ_ASSERT(depth > 0 && depth != UINT32_MAX);
2797 0 : MOZ_ASSERT(location.length() == depth);
2798 :
2799 0 : JitSpew(JitSpew_Profiling, "Found bytecode location of depth %d:", depth);
2800 0 : for (size_t i = 0; i < location.length(); i++) {
2801 0 : JitSpew(JitSpew_Profiling, " %s:%" PRIuSIZE " - %" PRIuSIZE,
2802 0 : location[i].script->filename(), location[i].script->lineno(),
2803 0 : size_t(location[i].pc - location[i].script->code()));
2804 : }
2805 :
2806 0 : if (type_ == JitFrame_IonJS) {
2807 : // Create an InlineFrameIterator here and verify the mapped info against the iterator info.
2808 0 : InlineFrameIterator inlineFrames(TlsContext.get(), this);
2809 0 : for (size_t idx = 0; idx < location.length(); idx++) {
2810 0 : MOZ_ASSERT(idx < location.length());
2811 0 : MOZ_ASSERT_IF(idx < location.length() - 1, inlineFrames.more());
2812 :
2813 0 : JitSpew(JitSpew_Profiling,
2814 : "Match %d: ION %s:%" PRIuSIZE "(%" PRIuSIZE ") vs N2B %s:%" PRIuSIZE "(%" PRIuSIZE ")",
2815 : (int)idx,
2816 : inlineFrames.script()->filename(),
2817 : inlineFrames.script()->lineno(),
2818 0 : size_t(inlineFrames.pc() - inlineFrames.script()->code()),
2819 0 : location[idx].script->filename(),
2820 0 : location[idx].script->lineno(),
2821 0 : size_t(location[idx].pc - location[idx].script->code()));
2822 :
2823 0 : MOZ_ASSERT(inlineFrames.script() == location[idx].script);
2824 :
2825 0 : if (inlineFrames.more())
2826 0 : ++inlineFrames;
2827 : }
2828 : }
2829 :
2830 0 : return true;
2831 : }
2832 : #endif // DEBUG
2833 :
2834 0 : JitProfilingFrameIterator::JitProfilingFrameIterator(
2835 0 : JSContext* cx, const JS::ProfilingFrameIterator::RegisterState& state)
2836 : {
2837 : // If no profilingActivation is live, initialize directly to
2838 : // end-of-iteration state.
2839 0 : if (!cx->profilingActivation()) {
2840 0 : type_ = JitFrame_Entry;
2841 0 : fp_ = nullptr;
2842 0 : returnAddressToFp_ = nullptr;
2843 0 : return;
2844 : }
2845 :
2846 0 : MOZ_ASSERT(cx->profilingActivation()->isJit());
2847 :
2848 0 : JitActivation* act = cx->profilingActivation()->asJit();
2849 :
2850 : // If the top JitActivation has a null lastProfilingFrame, assume that
2851 : // it's a trivially empty activation, and initialize directly
2852 : // to end-of-iteration state.
2853 0 : if (!act->lastProfilingFrame()) {
2854 0 : type_ = JitFrame_Entry;
2855 0 : fp_ = nullptr;
2856 0 : returnAddressToFp_ = nullptr;
2857 0 : return;
2858 : }
2859 :
2860 : // Get the fp from the current profilingActivation
2861 0 : fp_ = (uint8_t*) act->lastProfilingFrame();
2862 0 : void* lastCallSite = act->lastProfilingCallSite();
2863 :
2864 0 : JitcodeGlobalTable* table = cx->runtime()->jitRuntime()->getJitcodeGlobalTable();
2865 :
2866 : // Profiler sampling must NOT be suppressed if we are here.
2867 0 : MOZ_ASSERT(cx->isProfilerSamplingEnabled());
2868 :
2869 : // Try initializing with sampler pc
2870 0 : if (tryInitWithPC(state.pc))
2871 0 : return;
2872 :
2873 : // Try initializing with sampler pc using native=>bytecode table.
2874 0 : if (tryInitWithTable(table, state.pc, cx->runtime(), /* forLastCallSite = */ false))
2875 0 : return;
2876 :
2877 : // Try initializing with lastProfilingCallSite pc
2878 0 : if (lastCallSite) {
2879 0 : if (tryInitWithPC(lastCallSite))
2880 0 : return;
2881 :
2882 : // Try initializing with lastProfilingCallSite pc using native=>bytecode table.
2883 0 : if (tryInitWithTable(table, lastCallSite, cx->runtime(), /* forLastCallSite = */ true))
2884 0 : return;
2885 : }
2886 :
2887 0 : MOZ_ASSERT(frameScript()->hasBaselineScript());
2888 :
2889 : // If nothing matches, for now just assume we are at the start of the last frame's
2890 : // baseline jit code.
2891 0 : type_ = JitFrame_BaselineJS;
2892 0 : returnAddressToFp_ = frameScript()->baselineScript()->method()->raw();
2893 : }
2894 :
2895 : template <typename ReturnType = CommonFrameLayout*>
2896 : inline ReturnType
2897 0 : GetPreviousRawFrame(CommonFrameLayout* frame)
2898 : {
2899 0 : size_t prevSize = frame->prevFrameLocalSize() + frame->headerSize();
2900 0 : return ReturnType((uint8_t*)frame + prevSize);
2901 : }
2902 :
2903 0 : JitProfilingFrameIterator::JitProfilingFrameIterator(void* exitFrame)
2904 : {
2905 : // Skip the exit frame.
2906 0 : ExitFrameLayout* frame = (ExitFrameLayout*) exitFrame;
2907 0 : moveToNextFrame(frame);
2908 0 : }
2909 :
2910 : bool
2911 0 : JitProfilingFrameIterator::tryInitWithPC(void* pc)
2912 : {
2913 0 : JSScript* callee = frameScript();
2914 :
2915 : // Check for Ion first, since it's more likely for hot code.
2916 0 : if (callee->hasIonScript() && callee->ionScript()->method()->containsNativePC(pc)) {
2917 0 : type_ = JitFrame_IonJS;
2918 0 : returnAddressToFp_ = pc;
2919 0 : return true;
2920 : }
2921 :
2922 : // Check for containment in Baseline jitcode second.
2923 0 : if (callee->hasBaselineScript() && callee->baselineScript()->method()->containsNativePC(pc)) {
2924 0 : type_ = JitFrame_BaselineJS;
2925 0 : returnAddressToFp_ = pc;
2926 0 : return true;
2927 : }
2928 :
2929 0 : return false;
2930 : }
2931 :
2932 : bool
2933 0 : JitProfilingFrameIterator::tryInitWithTable(JitcodeGlobalTable* table, void* pc, JSRuntime* rt,
2934 : bool forLastCallSite)
2935 : {
2936 0 : if (!pc)
2937 0 : return false;
2938 :
2939 0 : const JitcodeGlobalEntry* entry = table->lookup(pc);
2940 0 : if (!entry)
2941 0 : return false;
2942 :
2943 0 : JSScript* callee = frameScript();
2944 :
2945 0 : MOZ_ASSERT(entry->isIon() || entry->isBaseline() || entry->isIonCache() || entry->isDummy());
2946 :
2947 : // Treat dummy lookups as an empty frame sequence.
2948 0 : if (entry->isDummy()) {
2949 0 : type_ = JitFrame_Entry;
2950 0 : fp_ = nullptr;
2951 0 : returnAddressToFp_ = nullptr;
2952 0 : return true;
2953 : }
2954 :
2955 0 : if (entry->isIon()) {
2956 : // If looked-up callee doesn't match frame callee, don't accept lastProfilingCallSite
2957 0 : if (entry->ionEntry().getScript(0) != callee)
2958 0 : return false;
2959 :
2960 0 : type_ = JitFrame_IonJS;
2961 0 : returnAddressToFp_ = pc;
2962 0 : return true;
2963 : }
2964 :
2965 0 : if (entry->isBaseline()) {
2966 : // If looked-up callee doesn't match frame callee, don't accept lastProfilingCallSite
2967 0 : if (forLastCallSite && entry->baselineEntry().script() != callee)
2968 0 : return false;
2969 :
2970 0 : type_ = JitFrame_BaselineJS;
2971 0 : returnAddressToFp_ = pc;
2972 0 : return true;
2973 : }
2974 :
2975 0 : if (entry->isIonCache()) {
2976 0 : void* ptr = entry->ionCacheEntry().rejoinAddr();
2977 0 : const JitcodeGlobalEntry& ionEntry = table->lookupInfallible(ptr);
2978 0 : MOZ_ASSERT(ionEntry.isIon());
2979 :
2980 0 : if (ionEntry.ionEntry().getScript(0) != callee)
2981 0 : return false;
2982 :
2983 0 : type_ = JitFrame_IonJS;
2984 0 : returnAddressToFp_ = pc;
2985 0 : return true;
2986 : }
2987 :
2988 0 : return false;
2989 : }
2990 :
2991 : void
2992 0 : JitProfilingFrameIterator::fixBaselineReturnAddress()
2993 : {
2994 0 : MOZ_ASSERT(type_ == JitFrame_BaselineJS);
2995 0 : BaselineFrame* bl = (BaselineFrame*)(fp_ - BaselineFrame::FramePointerOffset -
2996 0 : BaselineFrame::Size());
2997 :
2998 : // Debug mode OSR for Baseline uses a "continuation fixer" and stashes the
2999 : // actual return address in an auxiliary structure.
3000 0 : if (BaselineDebugModeOSRInfo* info = bl->getDebugModeOSRInfo()) {
3001 0 : returnAddressToFp_ = info->resumeAddr;
3002 0 : return;
3003 : }
3004 :
3005 : // Resuming a generator via .throw() pushes a bogus return address onto
3006 : // the stack. We have the actual jsbytecode* stashed on the frame itself;
3007 : // translate that into the Baseline code address.
3008 0 : if (jsbytecode* override = bl->maybeOverridePc()) {
3009 0 : JSScript* script = bl->script();
3010 0 : returnAddressToFp_ = script->baselineScript()->nativeCodeForPC(script, override);
3011 0 : return;
3012 : }
3013 : }
3014 :
3015 : void
3016 0 : JitProfilingFrameIterator::operator++()
3017 : {
3018 0 : JitFrameLayout* frame = framePtr();
3019 0 : moveToNextFrame(frame);
3020 0 : }
3021 :
3022 : void
3023 0 : JitProfilingFrameIterator::moveToNextFrame(CommonFrameLayout* frame)
3024 : {
3025 : /*
3026 : * fp_ points to a Baseline or Ion frame. The possible call-stacks
3027 : * patterns occurring between this frame and a previous Ion or Baseline
3028 : * frame are as follows:
3029 : *
3030 : * <Baseline-Or-Ion>
3031 : * ^
3032 : * |
3033 : * ^--- Ion
3034 : * |
3035 : * ^--- Baseline Stub <---- Baseline
3036 : * |
3037 : * ^--- Argument Rectifier
3038 : * | ^
3039 : * | |
3040 : * | ^--- Ion
3041 : * | |
3042 : * | ^--- Baseline Stub <---- Baseline
3043 : * |
3044 : * ^--- Entry Frame (From C++)
3045 : * Exit Frame (From previous JitActivation)
3046 : * ^
3047 : * |
3048 : * ^--- Ion
3049 : * |
3050 : * ^--- Baseline
3051 : * |
3052 : * ^--- Baseline Stub <---- Baseline
3053 : */
3054 0 : FrameType prevType = frame->prevType();
3055 :
3056 0 : if (prevType == JitFrame_IonJS) {
3057 0 : returnAddressToFp_ = frame->returnAddress();
3058 0 : fp_ = GetPreviousRawFrame<uint8_t*>(frame);
3059 0 : type_ = JitFrame_IonJS;
3060 0 : return;
3061 : }
3062 :
3063 0 : if (prevType == JitFrame_BaselineJS) {
3064 0 : returnAddressToFp_ = frame->returnAddress();
3065 0 : fp_ = GetPreviousRawFrame<uint8_t*>(frame);
3066 0 : type_ = JitFrame_BaselineJS;
3067 0 : fixBaselineReturnAddress();
3068 0 : return;
3069 : }
3070 :
3071 0 : if (prevType == JitFrame_BaselineStub) {
3072 0 : BaselineStubFrameLayout* stubFrame = GetPreviousRawFrame<BaselineStubFrameLayout*>(frame);
3073 0 : MOZ_ASSERT(stubFrame->prevType() == JitFrame_BaselineJS);
3074 :
3075 0 : returnAddressToFp_ = stubFrame->returnAddress();
3076 0 : fp_ = ((uint8_t*) stubFrame->reverseSavedFramePtr())
3077 0 : + jit::BaselineFrame::FramePointerOffset;
3078 0 : type_ = JitFrame_BaselineJS;
3079 0 : return;
3080 : }
3081 :
3082 0 : if (prevType == JitFrame_Rectifier) {
3083 0 : RectifierFrameLayout* rectFrame = GetPreviousRawFrame<RectifierFrameLayout*>(frame);
3084 0 : FrameType rectPrevType = rectFrame->prevType();
3085 :
3086 0 : if (rectPrevType == JitFrame_IonJS) {
3087 0 : returnAddressToFp_ = rectFrame->returnAddress();
3088 0 : fp_ = GetPreviousRawFrame<uint8_t*>(rectFrame);
3089 0 : type_ = JitFrame_IonJS;
3090 0 : return;
3091 : }
3092 :
3093 0 : if (rectPrevType == JitFrame_BaselineStub) {
3094 : BaselineStubFrameLayout* stubFrame =
3095 0 : GetPreviousRawFrame<BaselineStubFrameLayout*>(rectFrame);
3096 0 : returnAddressToFp_ = stubFrame->returnAddress();
3097 0 : fp_ = ((uint8_t*) stubFrame->reverseSavedFramePtr())
3098 0 : + jit::BaselineFrame::FramePointerOffset;
3099 0 : type_ = JitFrame_BaselineJS;
3100 0 : return;
3101 : }
3102 :
3103 0 : MOZ_CRASH("Bad frame type prior to rectifier frame.");
3104 : }
3105 :
3106 0 : if (prevType == JitFrame_IonICCall) {
3107 : IonICCallFrameLayout* callFrame =
3108 0 : GetPreviousRawFrame<IonICCallFrameLayout*>(frame);
3109 :
3110 0 : MOZ_ASSERT(callFrame->prevType() == JitFrame_IonJS);
3111 :
3112 0 : returnAddressToFp_ = callFrame->returnAddress();
3113 0 : fp_ = GetPreviousRawFrame<uint8_t*>(callFrame);
3114 0 : type_ = JitFrame_IonJS;
3115 0 : return;
3116 : }
3117 :
3118 0 : if (prevType == JitFrame_Entry) {
3119 : // No previous frame, set to null to indicate that JitFrameIterator is done()
3120 0 : returnAddressToFp_ = nullptr;
3121 0 : fp_ = nullptr;
3122 0 : type_ = JitFrame_Entry;
3123 0 : return;
3124 : }
3125 :
3126 0 : MOZ_CRASH("Bad frame type.");
3127 : }
3128 :
3129 : JitFrameLayout*
3130 0 : InvalidationBailoutStack::fp() const
3131 : {
3132 0 : return (JitFrameLayout*) (sp() + ionScript_->frameSize());
3133 : }
3134 :
3135 : void
3136 0 : InvalidationBailoutStack::checkInvariants() const
3137 : {
3138 : #ifdef DEBUG
3139 0 : JitFrameLayout* frame = fp();
3140 0 : CalleeToken token = frame->calleeToken();
3141 0 : MOZ_ASSERT(token);
3142 :
3143 0 : uint8_t* rawBase = ionScript()->method()->raw();
3144 0 : uint8_t* rawLimit = rawBase + ionScript()->method()->instructionsSize();
3145 0 : uint8_t* osiPoint = osiPointReturnAddress();
3146 0 : MOZ_ASSERT(rawBase <= osiPoint && osiPoint <= rawLimit);
3147 : #endif
3148 0 : }
3149 :
3150 : void
3151 0 : AssertJitStackInvariants(JSContext* cx)
3152 : {
3153 0 : for (JitActivationIterator activations(cx); !activations.done(); ++activations) {
3154 0 : JitFrameIterator frames(activations);
3155 0 : size_t prevFrameSize = 0;
3156 0 : size_t frameSize = 0;
3157 0 : bool isScriptedCallee = false;
3158 0 : for (; !frames.done(); ++frames) {
3159 0 : size_t calleeFp = reinterpret_cast<size_t>(frames.fp());
3160 0 : size_t callerFp = reinterpret_cast<size_t>(frames.prevFp());
3161 0 : MOZ_ASSERT(callerFp >= calleeFp);
3162 0 : prevFrameSize = frameSize;
3163 0 : frameSize = callerFp - calleeFp;
3164 :
3165 0 : if (frames.prevType() == JitFrame_Rectifier) {
3166 0 : MOZ_RELEASE_ASSERT(frameSize % JitStackAlignment == 0,
3167 : "The rectifier frame should keep the alignment");
3168 :
3169 : size_t expectedFrameSize = 0
3170 : #if defined(JS_CODEGEN_X86)
3171 : + sizeof(void*) /* frame pointer */
3172 : #endif
3173 0 : + sizeof(Value) * (frames.callee()->nargs() +
3174 0 : 1 /* |this| argument */ +
3175 0 : frames.isConstructing() /* new.target */)
3176 0 : + sizeof(JitFrameLayout);
3177 0 : MOZ_RELEASE_ASSERT(frameSize >= expectedFrameSize,
3178 : "The frame is large enough to hold all arguments");
3179 0 : MOZ_RELEASE_ASSERT(expectedFrameSize + JitStackAlignment > frameSize,
3180 : "The frame size is optimal");
3181 : }
3182 :
3183 0 : if (frames.isExitFrame()) {
3184 : // For the moment, we do not keep the JitStackAlignment
3185 : // alignment for exit frames.
3186 0 : frameSize -= ExitFrameLayout::Size();
3187 : }
3188 :
3189 0 : if (frames.isIonJS()) {
3190 : // Ideally, we should not have such requirement, but keep the
3191 : // alignment-delta as part of the Safepoint such that we can pad
3192 : // accordingly when making out-of-line calls. In the mean time,
3193 : // let us have check-points where we can garantee that
3194 : // everything can properly be aligned before adding complexity.
3195 0 : MOZ_RELEASE_ASSERT(frames.ionScript()->frameSize() % JitStackAlignment == 0,
3196 : "Ensure that if the Ion frame is aligned, then the spill base is also aligned");
3197 :
3198 0 : if (isScriptedCallee) {
3199 0 : MOZ_RELEASE_ASSERT(prevFrameSize % JitStackAlignment == 0,
3200 : "The ion frame should keep the alignment");
3201 : }
3202 : }
3203 :
3204 : // The stack is dynamically aligned by baseline stubs before calling
3205 : // any jitted code.
3206 0 : if (frames.prevType() == JitFrame_BaselineStub && isScriptedCallee) {
3207 0 : MOZ_RELEASE_ASSERT(calleeFp % JitStackAlignment == 0,
3208 : "The baseline stub restores the stack alignment");
3209 : }
3210 :
3211 0 : isScriptedCallee = false
3212 0 : || frames.isScripted()
3213 0 : || frames.type() == JitFrame_Rectifier;
3214 : }
3215 :
3216 0 : MOZ_RELEASE_ASSERT(frames.type() == JitFrame_Entry,
3217 : "The first frame of a Jit activation should be an entry frame");
3218 0 : MOZ_RELEASE_ASSERT(reinterpret_cast<size_t>(frames.fp()) % JitStackAlignment == 0,
3219 : "The entry frame should be properly aligned");
3220 : }
3221 0 : }
3222 :
3223 : } // namespace jit
3224 9 : } // namespace js
|