js/src/jit/JitFrameIterator.h

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:b431bbd2da3c
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 #ifndef jit_JitFrameIterator_h
8 #define jit_JitFrameIterator_h
9
10 #ifdef JS_ION
11
12 #include "jsfun.h"
13 #include "jsscript.h"
14 #include "jstypes.h"
15
16 #include "jit/IonCode.h"
17 #include "jit/Snapshots.h"
18
19 namespace js {
20 class ActivationIterator;
21 };
22
23 namespace js {
24 namespace jit {
25
26 enum FrameType
27 {
28 // A JS frame is analagous to a js::InterpreterFrame, representing one scripted
29 // functon activation. IonJS frames are used by the optimizing compiler.
30 JitFrame_IonJS,
31
32 // JS frame used by the baseline JIT.
33 JitFrame_BaselineJS,
34
35 // Frame pushed for baseline JIT stubs that make non-tail calls, so that the
36 // return address -> ICEntry mapping works.
37 JitFrame_BaselineStub,
38
39 // The entry frame is the initial prologue block transitioning from the VM
40 // into the Ion world.
41 JitFrame_Entry,
42
43 // A rectifier frame sits in between two JS frames, adapting argc != nargs
44 // mismatches in calls.
45 JitFrame_Rectifier,
46
47 // An unwound JS frame is a JS frame signalling that its callee frame has been
48 // turned into an exit frame (see EnsureExitFrame). Used by Ion bailouts and
49 // Baseline exception unwinding.
50 JitFrame_Unwound_IonJS,
51
52 // Like Unwound_IonJS, but the caller is a baseline stub frame.
53 JitFrame_Unwound_BaselineStub,
54
55 // An unwound rectifier frame is a rectifier frame signalling that its callee
56 // frame has been turned into an exit frame (see EnsureExitFrame).
57 JitFrame_Unwound_Rectifier,
58
59 // An exit frame is necessary for transitioning from a JS frame into C++.
60 // From within C++, an exit frame is always the last frame in any
61 // JitActivation.
62 JitFrame_Exit
63 };
64
65 enum ReadFrameArgsBehavior {
66 // Only read formals (i.e. [0 ... callee()->nargs]
67 ReadFrame_Formals,
68
69 // Only read overflown args (i.e. [callee()->nargs ... numActuals()]
70 ReadFrame_Overflown,
71
72 // Read all args (i.e. [0 ... numActuals()])
73 ReadFrame_Actuals
74 };
75
76 class IonCommonFrameLayout;
77 class IonJSFrameLayout;
78 class IonExitFrameLayout;
79
80 class BaselineFrame;
81
82 class JitActivation;
83
84 class JitFrameIterator
85 {
86 protected:
87 uint8_t *current_;
88 FrameType type_;
89 uint8_t *returnAddressToFp_;
90 size_t frameSize_;
91
92 private:
93 mutable const SafepointIndex *cachedSafepointIndex_;
94 const JitActivation *activation_;
95 ExecutionMode mode_;
96
97 void dumpBaseline() const;
98
99 public:
100 explicit JitFrameIterator(uint8_t *top, ExecutionMode mode)
101 : current_(top),
102 type_(JitFrame_Exit),
103 returnAddressToFp_(nullptr),
104 frameSize_(0),
105 cachedSafepointIndex_(nullptr),
106 activation_(nullptr),
107 mode_(mode)
108 { }
109
110 explicit JitFrameIterator(JSContext *cx);
111 explicit JitFrameIterator(const ActivationIterator &activations);
112 explicit JitFrameIterator(IonJSFrameLayout *fp, ExecutionMode mode);
113
114 // Current frame information.
115 FrameType type() const {
116 return type_;
117 }
118 uint8_t *fp() const {
119 return current_;
120 }
121 const JitActivation *activation() const {
122 return activation_;
123 }
124
125 IonCommonFrameLayout *current() const {
126 return (IonCommonFrameLayout *)current_;
127 }
128
129 inline uint8_t *returnAddress() const;
130
131 IonJSFrameLayout *jsFrame() const {
132 JS_ASSERT(isScripted());
133 return (IonJSFrameLayout *) fp();
134 }
135
136 // Returns true iff this exit frame was created using EnsureExitFrame.
137 inline bool isFakeExitFrame() const;
138
139 inline IonExitFrameLayout *exitFrame() const;
140
141 // Returns whether the JS frame has been invalidated and, if so,
142 // places the invalidated Ion script in |ionScript|.
143 bool checkInvalidation(IonScript **ionScript) const;
144 bool checkInvalidation() const;
145
146 bool isScripted() const {
147 return type_ == JitFrame_BaselineJS || type_ == JitFrame_IonJS;
148 }
149 bool isBaselineJS() const {
150 return type_ == JitFrame_BaselineJS;
151 }
152 bool isIonJS() const {
153 return type_ == JitFrame_IonJS;
154 }
155 bool isBaselineStub() const {
156 return type_ == JitFrame_BaselineStub;
157 }
158 bool isNative() const;
159 bool isOOLNative() const;
160 bool isOOLPropertyOp() const;
161 bool isOOLProxy() const;
162 bool isDOMExit() const;
163 bool isEntry() const {
164 return type_ == JitFrame_Entry;
165 }
166 bool isFunctionFrame() const;
167
168 bool isConstructing() const;
169
170 void *calleeToken() const;
171 JSFunction *callee() const;
172 JSFunction *maybeCallee() const;
173 unsigned numActualArgs() const;
174 JSScript *script() const;
175 void baselineScriptAndPc(JSScript **scriptRes, jsbytecode **pcRes) const;
176 Value *actualArgs() const;
177
178 // Returns the return address of the frame above this one (that is, the
179 // return address that returns back to the current frame).
180 uint8_t *returnAddressToFp() const {
181 return returnAddressToFp_;
182 }
183
184 // Previous frame information extracted from the current frame.
185 inline size_t prevFrameLocalSize() const;
186 inline FrameType prevType() const;
187 uint8_t *prevFp() const;
188
189 // Returns the stack space used by the current frame, in bytes. This does
190 // not include the size of its fixed header.
191 size_t frameSize() const {
192 JS_ASSERT(type_ != JitFrame_Exit);
193 return frameSize_;
194 }
195
196 // Functions used to iterate on frames. When prevType is JitFrame_Entry,
197 // the current frame is the last frame.
198 inline bool done() const {
199 return type_ == JitFrame_Entry;
200 }
201 JitFrameIterator &operator++();
202
203 // Returns the IonScript associated with this JS frame.
204 IonScript *ionScript() const;
205
206 // Returns the Safepoint associated with this JS frame. Incurs a lookup
207 // overhead.
208 const SafepointIndex *safepoint() const;
209
210 // Returns the OSI index associated with this JS frame. Incurs a lookup
211 // overhead.
212 const OsiIndex *osiIndex() const;
213
214 uintptr_t *spillBase() const;
215 MachineState machineState() const;
216
217 template <class Op>
218 void unaliasedForEachActual(Op op, ReadFrameArgsBehavior behavior) const {
219 JS_ASSERT(isBaselineJS());
220
221 unsigned nactual = numActualArgs();
222 unsigned start, end;
223 switch (behavior) {
224 case ReadFrame_Formals:
225 start = 0;
226 end = callee()->nargs();
227 break;
228 case ReadFrame_Overflown:
229 start = callee()->nargs();
230 end = nactual;
231 break;
232 case ReadFrame_Actuals:
233 start = 0;
234 end = nactual;
235 }
236
237 Value *argv = actualArgs();
238 for (unsigned i = start; i < end; i++)
239 op(argv[i]);
240 }
241
242 void dump() const;
243
244 inline BaselineFrame *baselineFrame() const;
245 };
246
247 class IonJSFrameLayout;
248 class IonBailoutIterator;
249
250 class RResumePoint;
251
252 // Reads frame information in snapshot-encoding order (that is, outermost frame
253 // to innermost frame).
254 class SnapshotIterator
255 {
256 SnapshotReader snapshot_;
257 RecoverReader recover_;
258 IonJSFrameLayout *fp_;
259 MachineState machine_;
260 IonScript *ionScript_;
261
262 private:
263 // Read a spilled register from the machine state.
264 bool hasRegister(Register reg) const {
265 return machine_.has(reg);
266 }
267 uintptr_t fromRegister(Register reg) const {
268 return machine_.read(reg);
269 }
270
271 bool hasRegister(FloatRegister reg) const {
272 return machine_.has(reg);
273 }
274 double fromRegister(FloatRegister reg) const {
275 return machine_.read(reg);
276 }
277
278 // Read an uintptr_t from the stack.
279 bool hasStack(int32_t offset) const {
280 return true;
281 }
282 uintptr_t fromStack(int32_t offset) const;
283
284 Value allocationValue(const RValueAllocation &a);
285 bool allocationReadable(const RValueAllocation &a);
286 void warnUnreadableAllocation();
287
288 public:
289 // Handle iterating over RValueAllocations of the snapshots.
290 inline RValueAllocation readAllocation() {
291 MOZ_ASSERT(moreAllocations());
292 return snapshot_.readAllocation();
293 }
294 Value skip() {
295 snapshot_.skipAllocation();
296 return UndefinedValue();
297 }
298
299 const RResumePoint *resumePoint() const;
300 const RInstruction *instruction() const {
301 return recover_.instruction();
302 }
303
304 uint32_t numAllocations() const;
305 inline bool moreAllocations() const {
306 return snapshot_.numAllocationsRead() < numAllocations();
307 }
308
309 public:
310 // Exhibits frame properties contained in the snapshot.
311 uint32_t pcOffset() const;
312 inline bool resumeAfter() const {
313 // Inline frames are inlined on calls, which are considered as being
314 // resumed on the Call as baseline will push the pc once we return from
315 // the call.
316 if (moreFrames())
317 return false;
318 return recover_.resumeAfter();
319 }
320 inline BailoutKind bailoutKind() const {
321 return snapshot_.bailoutKind();
322 }
323
324 public:
325 // Read the next instruction available and get ready to either skip it or
326 // evaluate it.
327 inline void nextInstruction() {
328 MOZ_ASSERT(snapshot_.numAllocationsRead() == numAllocations());
329 recover_.nextInstruction();
330 snapshot_.resetNumAllocationsRead();
331 }
332
333 // Skip an Instruction by walking to the next instruction and by skipping
334 // all the allocations corresponding to this instruction.
335 void skipInstruction();
336
337 inline bool moreInstructions() const {
338 return recover_.moreInstructions();
339 }
340
341 public:
342 // Handle iterating over frames of the snapshots.
343 void nextFrame();
344
345 inline bool moreFrames() const {
346 // The last instruction is recovering the innermost frame, so as long as
347 // there is more instruction there is necesseray more frames.
348 return moreInstructions();
349 }
350
351 public:
352 // Connect all informations about the current script in order to recover the
353 // content of baseline frames.
354
355 SnapshotIterator(IonScript *ionScript, SnapshotOffset snapshotOffset,
356 IonJSFrameLayout *fp, const MachineState &machine);
357 SnapshotIterator(const JitFrameIterator &iter);
358 SnapshotIterator(const IonBailoutIterator &iter);
359 SnapshotIterator();
360
361 Value read() {
362 return allocationValue(readAllocation());
363 }
364 Value maybeRead(bool silentFailure = false) {
365 RValueAllocation a = readAllocation();
366 if (allocationReadable(a))
367 return allocationValue(a);
368 if (!silentFailure)
369 warnUnreadableAllocation();
370 return UndefinedValue();
371 }
372
373 void readCommonFrameSlots(Value *scopeChain, Value *rval) {
374 if (scopeChain)
375 *scopeChain = read();
376 else
377 skip();
378
379 if (rval)
380 *rval = read();
381 else
382 skip();
383 }
384
385 template <class Op>
386 void readFunctionFrameArgs(Op &op, ArgumentsObject **argsObj, Value *thisv,
387 unsigned start, unsigned end, JSScript *script)
388 {
389 // Assumes that the common frame arguments have already been read.
390 if (script->argumentsHasVarBinding()) {
391 if (argsObj) {
392 Value v = read();
393 if (v.isObject())
394 *argsObj = &v.toObject().as<ArgumentsObject>();
395 } else {
396 skip();
397 }
398 }
399
400 if (thisv)
401 *thisv = read();
402 else
403 skip();
404
405 unsigned i = 0;
406 if (end < start)
407 i = start;
408
409 for (; i < start; i++)
410 skip();
411 for (; i < end; i++) {
412 // We are not always able to read values from the snapshots, some values
413 // such as non-gc things may still be live in registers and cause an
414 // error while reading the machine state.
415 Value v = maybeRead();
416 op(v);
417 }
418 }
419
420 Value maybeReadAllocByIndex(size_t index) {
421 while (index--) {
422 JS_ASSERT(moreAllocations());
423 skip();
424 }
425
426 Value s = maybeRead(true);
427
428 while (moreAllocations())
429 skip();
430
431 return s;
432 }
433 };
434
435 // Reads frame information in callstack order (that is, innermost frame to
436 // outermost frame).
437 template <AllowGC allowGC=CanGC>
438 class InlineFrameIteratorMaybeGC
439 {
440 const JitFrameIterator *frame_;
441 SnapshotIterator start_;
442 SnapshotIterator si_;
443 uint32_t framesRead_;
444
445 // When the inline-frame-iterator is created, this variable is defined to
446 // UINT32_MAX. Then the first iteration of findNextFrame, which settle on
447 // the innermost frame, is used to update this counter to the number of
448 // frames contained in the recover buffer.
449 uint32_t frameCount_;
450
451 typename MaybeRooted<JSFunction*, allowGC>::RootType callee_;
452 typename MaybeRooted<JSScript*, allowGC>::RootType script_;
453 jsbytecode *pc_;
454 uint32_t numActualArgs_;
455
456 struct Nop {
457 void operator()(const Value &v) { }
458 };
459
460 private:
461 void findNextFrame();
462
463 JSObject *computeScopeChain(Value scopeChainValue) const {
464 if (scopeChainValue.isObject())
465 return &scopeChainValue.toObject();
466
467 if (isFunctionFrame()) {
468 // Heavyweight functions should always have a scope chain.
469 MOZ_ASSERT(!callee()->isHeavyweight());
470 return callee()->environment();
471 }
472
473 // Ion does not handle scripts that are not compile-and-go.
474 MOZ_ASSERT(!script()->isForEval());
475 MOZ_ASSERT(script()->compileAndGo());
476 return &script()->global();
477 }
478
479 public:
480 InlineFrameIteratorMaybeGC(JSContext *cx, const JitFrameIterator *iter)
481 : callee_(cx),
482 script_(cx)
483 {
484 resetOn(iter);
485 }
486
487 InlineFrameIteratorMaybeGC(JSRuntime *rt, const JitFrameIterator *iter)
488 : callee_(rt),
489 script_(rt)
490 {
491 resetOn(iter);
492 }
493
494 InlineFrameIteratorMaybeGC(JSContext *cx, const IonBailoutIterator *iter);
495
496 InlineFrameIteratorMaybeGC(JSContext *cx, const InlineFrameIteratorMaybeGC *iter)
497 : frame_(iter ? iter->frame_ : nullptr),
498 framesRead_(0),
499 frameCount_(iter ? iter->frameCount_ : UINT32_MAX),
500 callee_(cx),
501 script_(cx)
502 {
503 if (frame_) {
504 start_ = SnapshotIterator(*frame_);
505 // findNextFrame will iterate to the next frame and init. everything.
506 // Therefore to settle on the same frame, we report one frame less readed.
507 framesRead_ = iter->framesRead_ - 1;
508 findNextFrame();
509 }
510 }
511
512 bool more() const {
513 return frame_ && framesRead_ < frameCount_;
514 }
515 JSFunction *callee() const {
516 JS_ASSERT(callee_);
517 return callee_;
518 }
519 JSFunction *maybeCallee() const {
520 return callee_;
521 }
522
523 unsigned numActualArgs() const {
524 // The number of actual arguments of inline frames is recovered by the
525 // iteration process. It is recovered from the bytecode because this
526 // property still hold since the for inlined frames. This property does not
527 // hold for the parent frame because it can have optimize a call to
528 // js_fun_call or js_fun_apply.
529 if (more())
530 return numActualArgs_;
531
532 return frame_->numActualArgs();
533 }
534
535 template <class ArgOp, class LocalOp>
536 void readFrameArgsAndLocals(JSContext *cx, ArgOp &argOp, LocalOp &localOp,
537 JSObject **scopeChain, Value *rval,
538 ArgumentsObject **argsObj, Value *thisv,
539 ReadFrameArgsBehavior behavior) const
540 {
541 SnapshotIterator s(si_);
542
543 // Read frame slots common to both function and global frames.
544 Value scopeChainValue;
545 s.readCommonFrameSlots(&scopeChainValue, rval);
546
547 if (scopeChain)
548 *scopeChain = computeScopeChain(scopeChainValue);
549
550 // Read arguments, which only function frames have.
551 if (isFunctionFrame()) {
552 unsigned nactual = numActualArgs();
553 unsigned nformal = callee()->nargs();
554
555 // Get the non overflown arguments, which are taken from the inlined
556 // frame, because it will have the updated value when JSOP_SETARG is
557 // done.
558 if (behavior != ReadFrame_Overflown)
559 s.readFunctionFrameArgs(argOp, argsObj, thisv, 0, nformal, script());
560
561 if (behavior != ReadFrame_Formals) {
562 if (more()) {
563 // There is still a parent frame of this inlined frame. All
564 // arguments (also the overflown) are the last pushed values
565 // in the parent frame. To get the overflown arguments, we
566 // need to take them from there.
567
568 // The overflown arguments are not available in current frame.
569 // They are the last pushed arguments in the parent frame of
570 // this inlined frame.
571 InlineFrameIteratorMaybeGC it(cx, this);
572 ++it;
573 unsigned argsObjAdj = it.script()->argumentsHasVarBinding() ? 1 : 0;
574 SnapshotIterator parent_s(it.snapshotIterator());
575
576 // Skip over all slots until we get to the last slots
577 // (= arguments slots of callee) the +3 is for [this], [returnvalue],
578 // [scopechain], and maybe +1 for [argsObj]
579 JS_ASSERT(parent_s.numAllocations() >= nactual + 3 + argsObjAdj);
580 unsigned skip = parent_s.numAllocations() - nactual - 3 - argsObjAdj;
581 for (unsigned j = 0; j < skip; j++)
582 parent_s.skip();
583
584 // Get the overflown arguments
585 parent_s.readCommonFrameSlots(nullptr, nullptr);
586 parent_s.readFunctionFrameArgs(argOp, nullptr, nullptr,
587 nformal, nactual, it.script());
588 } else {
589 // There is no parent frame to this inlined frame, we can read
590 // from the frame's Value vector directly.
591 Value *argv = frame_->actualArgs();
592 for (unsigned i = nformal; i < nactual; i++)
593 argOp(argv[i]);
594 }
595 }
596 }
597
598 // At this point we've read all the formals in s, and can read the
599 // locals.
600 for (unsigned i = 0; i < script()->nfixed(); i++)
601 localOp(s.read());
602 }
603
604 template <class Op>
605 void unaliasedForEachActual(JSContext *cx, Op op, ReadFrameArgsBehavior behavior) const {
606 Nop nop;
607 readFrameArgsAndLocals(cx, op, nop, nullptr, nullptr, nullptr, nullptr, behavior);
608 }
609
610 JSScript *script() const {
611 return script_;
612 }
613 jsbytecode *pc() const {
614 return pc_;
615 }
616 SnapshotIterator snapshotIterator() const {
617 return si_;
618 }
619 bool isFunctionFrame() const;
620 bool isConstructing() const;
621
622 JSObject *scopeChain() const {
623 SnapshotIterator s(si_);
624
625 // scopeChain
626 Value v = s.read();
627 return computeScopeChain(v);
628 }
629
630 JSObject *thisObject() const {
631 // In strict modes, |this| may not be an object and thus may not be
632 // readable which can either segv in read or trigger the assertion.
633 Value v = thisValue();
634 JS_ASSERT(v.isObject());
635 return &v.toObject();
636 }
637
638 Value thisValue() const {
639 // JS_ASSERT(isConstructing(...));
640 SnapshotIterator s(si_);
641
642 // scopeChain
643 s.skip();
644
645 // return value
646 s.skip();
647
648 // Arguments object.
649 if (script()->argumentsHasVarBinding())
650 s.skip();
651
652 return s.read();
653 }
654
655 InlineFrameIteratorMaybeGC &operator++() {
656 findNextFrame();
657 return *this;
658 }
659
660 void dump() const;
661
662 void resetOn(const JitFrameIterator *iter);
663
664 const JitFrameIterator &frame() const {
665 return *frame_;
666 }
667
668 // Inline frame number, 0 for the outermost (non-inlined) frame.
669 size_t frameNo() const {
670 return frameCount() - framesRead_;
671 }
672 size_t frameCount() const {
673 MOZ_ASSERT(frameCount_ != UINT32_MAX);
674 return frameCount_;
675 }
676
677 private:
678 InlineFrameIteratorMaybeGC() MOZ_DELETE;
679 InlineFrameIteratorMaybeGC(const InlineFrameIteratorMaybeGC &iter) MOZ_DELETE;
680 };
681 typedef InlineFrameIteratorMaybeGC<CanGC> InlineFrameIterator;
682 typedef InlineFrameIteratorMaybeGC<NoGC> InlineFrameIteratorNoGC;
683
684 } // namespace jit
685 } // namespace js
686
687 #endif // JS_ION
688
689 #endif /* jit_JitFrameIterator_h */

mercurial