summaryrefslogtreecommitdiffstats
path: root/js/src/jit/JSJitFrameIter.h
blob: b056da46db9fa1da75da10d35676776c296bcbd0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef jit_JSJitFrameIter_h
#define jit_JSJitFrameIter_h

#include "mozilla/Maybe.h"

#include "jstypes.h"

#include "jit/JitCode.h"
#include "jit/MachineState.h"
#include "jit/Snapshots.h"
#include "js/ProfilingFrameIterator.h"
#include "vm/JSFunction.h"
#include "vm/JSScript.h"

namespace js {

class ArgumentsObject;

namespace jit {

enum class FrameType {
  // A JS frame is analogous to a js::InterpreterFrame, representing one
  // scripted function activation. IonJS frames are used by the optimizing
  // compiler.
  IonJS,

  // JS frame used by the Baseline Interpreter and Baseline JIT.
  BaselineJS,

  // Frame pushed by Baseline stubs that make non-tail calls, so that the
  // return address -> ICEntry mapping works.
  BaselineStub,

  // The entry frame is the initial prologue block transitioning from the VM
  // into the Ion world.
  CppToJSJit,

  // This entry frame sits right before the baseline interpreter
  // so that external profilers can identify which function is being
  // interpreted. Only used under the --emit-interpreter-entry option.
  BaselineInterpreterEntry,

  // A rectifier frame sits in between two JS frames, adapting argc != nargs
  // mismatches in calls.
  Rectifier,

  // Ion IC calling a scripted getter/setter or a VMFunction.
  IonICCall,

  // An exit frame is necessary for transitioning from a JS frame into C++.
  // From within C++, an exit frame is always the last frame in any
  // JitActivation.
  Exit,

  // A bailout frame is a special IonJS jit frame after a bailout, and before
  // the reconstruction of the BaselineJS frame. From within C++, a bailout
  // frame is always the last frame in a JitActivation iff the bailout frame
  // information is recorded on the JitActivation.
  Bailout,

  // A wasm to JS frame is constructed during fast calls from wasm to the JS
  // jits, used as a marker to interleave JS jit and wasm frames. From the
  // point of view of JS JITs, this is just another kind of entry frame.
  WasmToJSJit,

  // A JS to wasm frame is constructed during fast calls from any JS jits to
  // wasm, and is a special kind of exit frame that doesn't have the exit
  // footer. From the point of view of the jit, it can be skipped as an exit.
  JSJitToWasm,
};

enum class ReadFrameArgsBehavior {
  // Read all actual arguments. Will invoke the callback numActualArgs times.
  Actuals,

  // Read all argument values in the stack frame. Will invoke the callback
  // max(numFormalArgs, numActualArgs) times.
  ActualsAndFormals,
};

class CommonFrameLayout;
class JitFrameLayout;
class ExitFrameLayout;

class BaselineFrame;
class JitActivation;
class SafepointIndex;
class OsiIndex;

// Iterate over the JIT stack to assert that all invariants are respected.
//  - Check that all entry frames are aligned on JitStackAlignment.
//  - Check that all rectifier frames keep the JitStackAlignment.

void AssertJitStackInvariants(JSContext* cx);

// A JSJitFrameIter can iterate over a linear frame group of JS jit frames
// only. It will stop at the first frame that is not of the same kind, or at
// the end of an activation.
//
// If you want to handle every kind of frames (including wasm frames), use
// JitFrameIter. If you want to skip interleaved frames of other kinds, use
// OnlyJSJitFrameIter.

class JSJitFrameIter {
 protected:
  uint8_t* current_;
  FrameType type_;
  uint8_t* resumePCinCurrentFrame_;

  // Size of the current Baseline frame. Equivalent to
  // BaselineFrame::debugFrameSize_ in debug builds.
  mozilla::Maybe<uint32_t> baselineFrameSize_;

 private:
  mutable const SafepointIndex* cachedSafepointIndex_;
  const JitActivation* activation_;

  void dumpBaseline() const;

 public:
  // See comment above the class.
  explicit JSJitFrameIter(const JitActivation* activation);

  // A constructor specialized for jit->wasm frames, which starts at a
  // specific FP.
  JSJitFrameIter(const JitActivation* activation, FrameType frameType,
                 uint8_t* fp);

  void setResumePCInCurrentFrame(uint8_t* newAddr) {
    resumePCinCurrentFrame_ = newAddr;
  }

  // Current frame information.
  FrameType type() const { return type_; }
  uint8_t* fp() const { return current_; }
  const JitActivation* activation() const { return activation_; }

  CommonFrameLayout* current() const { return (CommonFrameLayout*)current_; }

  inline uint8_t* returnAddress() const;

  // Return the pointer of the JitFrame, the iterator is assumed to be settled
  // on a scripted frame.
  JitFrameLayout* jsFrame() const;

  inline ExitFrameLayout* exitFrame() const;

  // Returns whether the JS frame has been invalidated and, if so,
  // places the invalidated Ion script in |ionScript|.
  bool checkInvalidation(IonScript** ionScript) const;
  bool checkInvalidation() const;

  bool isExitFrame() const { return type_ == FrameType::Exit; }
  bool isScripted() const {
    return type_ == FrameType::BaselineJS || type_ == FrameType::IonJS ||
           type_ == FrameType::Bailout;
  }
  bool isBaselineJS() const { return type_ == FrameType::BaselineJS; }
  bool isIonScripted() const {
    return type_ == FrameType::IonJS || type_ == FrameType::Bailout;
  }
  bool isIonJS() const { return type_ == FrameType::IonJS; }
  bool isIonICCall() const { return type_ == FrameType::IonICCall; }
  bool isBailoutJS() const { return type_ == FrameType::Bailout; }
  bool isBaselineStub() const { return type_ == FrameType::BaselineStub; }
  bool isBaselineInterpreterEntry() const {
    return type_ == FrameType::BaselineInterpreterEntry;
  }
  bool isRectifier() const { return type_ == FrameType::Rectifier; }
  bool isBareExit() const;
  bool isUnwoundJitExit() const;
  template <typename T>
  bool isExitFrameLayout() const;

  static bool isEntry(FrameType type) {
    return type == FrameType::CppToJSJit || type == FrameType::WasmToJSJit;
  }
  bool isEntry() const { return isEntry(type_); }

  bool isFunctionFrame() const;

  bool isConstructing() const;

  void* calleeToken() const;
  JSFunction* callee() const;
  JSFunction* maybeCallee() const;
  unsigned numActualArgs() const;
  JSScript* script() const;
  JSScript* maybeForwardedScript() const;
  void baselineScriptAndPc(JSScript** scriptRes, jsbytecode** pcRes) const;
  Value* actualArgs() const;

  // Returns the address of the next instruction that will execute in this
  // frame, once control returns to this frame.
  uint8_t* resumePCinCurrentFrame() const { return resumePCinCurrentFrame_; }

  // Previous frame information extracted from the current frame.
  inline FrameType prevType() const;
  uint8_t* prevFp() const;

  // Functions used to iterate on frames. When prevType is an entry,
  // the current frame is the last JS Jit frame.
  bool done() const { return isEntry(); }
  void operator++();

  // Returns the IonScript associated with this JS frame.
  IonScript* ionScript() const;

  // Returns the IonScript associated with this JS frame; the frame must
  // not be invalidated.
  IonScript* ionScriptFromCalleeToken() const;

  // Returns the Safepoint associated with this JS frame. Incurs a lookup
  // overhead.
  const SafepointIndex* safepoint() const;

  // Returns the OSI index associated with this JS frame. Incurs a lookup
  // overhead.
  const OsiIndex* osiIndex() const;

  // Returns the Snapshot offset associated with this JS frame. Incurs a
  // lookup overhead.
  SnapshotOffset snapshotOffset() const;

  uintptr_t* spillBase() const;
  MachineState machineState() const;

  template <class Op>
  void unaliasedForEachActual(Op op) const {
    MOZ_ASSERT(isBaselineJS());

    unsigned nactual = numActualArgs();
    Value* argv = actualArgs();
    for (unsigned i = 0; i < nactual; i++) {
      op(argv[i]);
    }
  }

  void dump() const;

  inline BaselineFrame* baselineFrame() const;

  // Returns the number of local and expression stack Values for the current
  // Baseline frame.
  inline uint32_t baselineFrameNumValueSlots() const;

  // This function isn't used, but we keep it here (debug-only) because it is
  // helpful when chasing issues with the jitcode map.
#ifdef DEBUG
  bool verifyReturnAddressUsingNativeToBytecodeMap();
#else
  bool verifyReturnAddressUsingNativeToBytecodeMap() { return true; }
#endif
};

class JitcodeGlobalTable;

class JSJitProfilingFrameIterator {
  uint8_t* fp_;
  // See JS::ProfilingFrameIterator::endStackAddress_ comment.
  void* endStackAddress_ = nullptr;
  FrameType type_;
  void* resumePCinCurrentFrame_;

  inline JSScript* frameScript() const;
  [[nodiscard]] bool tryInitWithPC(void* pc);
  [[nodiscard]] bool tryInitWithTable(JitcodeGlobalTable* table, void* pc,
                                      bool forLastCallSite);

  void moveToNextFrame(CommonFrameLayout* frame);

 public:
  JSJitProfilingFrameIterator(JSContext* cx, void* pc, void* sp);
  explicit JSJitProfilingFrameIterator(CommonFrameLayout* exitFP);

  void operator++();
  bool done() const { return fp_ == nullptr; }

  const char* baselineInterpreterLabel() const;
  void baselineInterpreterScriptPC(JSScript** script, jsbytecode** pc,
                                   uint64_t* realmID) const;

  void* fp() const {
    MOZ_ASSERT(!done());
    return fp_;
  }
  inline JitFrameLayout* framePtr() const;
  void* stackAddress() const { return fp(); }
  FrameType frameType() const {
    MOZ_ASSERT(!done());
    return type_;
  }
  void* resumePCinCurrentFrame() const {
    MOZ_ASSERT(!done());
    return resumePCinCurrentFrame_;
  }

  void* endStackAddress() const { return endStackAddress_; }
};

class RInstructionResults {
  // Vector of results of recover instructions.
  typedef mozilla::Vector<HeapPtr<Value>, 1, SystemAllocPolicy> Values;
  UniquePtr<Values> results_;

  // The frame pointer is used as a key to check if the current frame already
  // bailed out.
  JitFrameLayout* fp_;

  // Record if we tried and succeed at allocating and filling the vector of
  // recover instruction results, if needed.  This flag is needed in order to
  // avoid evaluating the recover instruction twice.
  bool initialized_;

 public:
  explicit RInstructionResults(JitFrameLayout* fp);
  RInstructionResults(RInstructionResults&& src);

  RInstructionResults& operator=(RInstructionResults&& rhs);

  ~RInstructionResults();

  [[nodiscard]] bool init(JSContext* cx, uint32_t numResults);
  bool isInitialized() const;
  size_t length() const;

  JitFrameLayout* frame() const;

  HeapPtr<Value>& operator[](size_t index);

  void trace(JSTracer* trc);
};

struct MaybeReadFallback {
  enum FallbackConsequence { Fallback_Invalidate, Fallback_DoNothing };

  JSContext* maybeCx = nullptr;
  JitActivation* activation = nullptr;
  const JSJitFrameIter* frame = nullptr;
  const FallbackConsequence consequence = Fallback_Invalidate;

  MaybeReadFallback() = default;

  MaybeReadFallback(JSContext* cx, JitActivation* activation,
                    const JSJitFrameIter* frame,
                    FallbackConsequence consequence = Fallback_Invalidate)
      : maybeCx(cx),
        activation(activation),
        frame(frame),
        consequence(consequence) {}

  bool canRecoverResults() { return maybeCx; }
};

class RResumePoint;

// Reads frame information in snapshot-encoding order (that is, outermost frame
// to innermost frame).
class SnapshotIterator {
 protected:
  SnapshotReader snapshot_;
  RecoverReader recover_;
  JitFrameLayout* fp_;
  const MachineState* machine_;
  IonScript* ionScript_;
  RInstructionResults* instructionResults_;

  enum class ReadMethod : bool {
    // Read the normal value.
    Normal,

    // Read the default value, or the normal value if there is no default.
    AlwaysDefault,
  };

 private:
  // Read a spilled register from the machine state.
  bool hasRegister(Register reg) const { return machine_->has(reg); }
  uintptr_t fromRegister(Register reg) const { return machine_->read(reg); }

  bool hasRegister(FloatRegister reg) const { return machine_->has(reg); }
  template <typename T>
  T fromRegister(FloatRegister reg) const {
    return machine_->read<T>(reg);
  }

  // Read an uintptr_t from the stack.
  bool hasStack(int32_t offset) const { return true; }
  uintptr_t fromStack(int32_t offset) const;

  bool hasInstructionResult(uint32_t index) const {
    return instructionResults_;
  }
  bool hasInstructionResults() const { return instructionResults_; }
  Value fromInstructionResult(uint32_t index) const;

  Value allocationValue(const RValueAllocation& a,
                        ReadMethod rm = ReadMethod::Normal);
  [[nodiscard]] bool allocationReadable(const RValueAllocation& a,
                                        ReadMethod rm = ReadMethod::Normal);
  void writeAllocationValuePayload(const RValueAllocation& a, const Value& v);
  void warnUnreadableAllocation();

 public:
  // Handle iterating over RValueAllocations of the snapshots.
  inline RValueAllocation readAllocation() {
    MOZ_ASSERT(moreAllocations());
    return snapshot_.readAllocation();
  }
  void skip() { snapshot_.skipAllocation(); }

  const RResumePoint* resumePoint() const;
  const RInstruction* instruction() const { return recover_.instruction(); }

  uint32_t numAllocations() const;
  inline bool moreAllocations() const {
    return snapshot_.numAllocationsRead() < numAllocations();
  }

  JitFrameLayout* frame() { return fp_; };

  // Used by recover instruction to store the value back into the instruction
  // results array.
  void storeInstructionResult(const Value& v);

 public:
  // Exhibits frame properties contained in the snapshot.
  uint32_t pcOffset() const;
  ResumeMode resumeMode() const;

  bool resumeAfter() const {
    // Calls in outer frames are never considered resume-after.
    MOZ_ASSERT_IF(moreFrames(), !IsResumeAfter(resumeMode()));
    return IsResumeAfter(resumeMode());
  }
  inline BailoutKind bailoutKind() const { return snapshot_.bailoutKind(); }

 public:
  // Read the next instruction available and get ready to either skip it or
  // evaluate it.
  inline void nextInstruction() {
    MOZ_ASSERT(snapshot_.numAllocationsRead() == numAllocations());
    recover_.nextInstruction();
    snapshot_.resetNumAllocationsRead();
  }

  // Skip an Instruction by walking to the next instruction and by skipping
  // all the allocations corresponding to this instruction.
  void skipInstruction();

  inline bool moreInstructions() const { return recover_.moreInstructions(); }

  // Register a vector used for storing the results of the evaluation of
  // recover instructions. This vector should be registered before the
  // beginning of the iteration. This function is in charge of allocating
  // enough space for all instructions results, and return false iff it fails.
  [[nodiscard]] bool initInstructionResults(MaybeReadFallback& fallback);

 protected:
  // This function is used internally for computing the result of the recover
  // instructions.
  [[nodiscard]] bool computeInstructionResults(
      JSContext* cx, RInstructionResults* results) const;

 public:
  // Handle iterating over frames of the snapshots.
  void nextFrame();
  void settleOnFrame();

  inline bool moreFrames() const {
    // The last instruction is recovering the innermost frame, so as long as
    // there is more instruction there is necesseray more frames.
    return moreInstructions();
  }

 public:
  // Connect all informations about the current script in order to recover the
  // content of baseline frames.

  SnapshotIterator(const JSJitFrameIter& iter,
                   const MachineState* machineState);
  SnapshotIterator();

  Value read() { return allocationValue(readAllocation()); }

  // Read the |Normal| value unless it is not available and that the snapshot
  // provides a |Default| value. This is useful to avoid invalidations of the
  // frame while we are only interested in a few properties which are provided
  // by the |Default| value.
  Value readWithDefault(RValueAllocation* alloc) {
    *alloc = RValueAllocation();
    RValueAllocation a = readAllocation();
    if (allocationReadable(a)) {
      return allocationValue(a);
    }

    *alloc = a;
    return allocationValue(a, ReadMethod::AlwaysDefault);
  }

  Value maybeRead(const RValueAllocation& a, MaybeReadFallback& fallback);
  Value maybeRead(MaybeReadFallback& fallback) {
    RValueAllocation a = readAllocation();
    return maybeRead(a, fallback);
  }

  bool tryRead(Value* result);

  void traceAllocation(JSTracer* trc);

  template <class Op>
  void readFunctionFrameArgs(Op& op, ArgumentsObject** argsObj, Value* thisv,
                             unsigned start, unsigned end, JSScript* script,
                             MaybeReadFallback& fallback) {
    // Assumes that the common frame arguments have already been read.
    if (script->needsArgsObj()) {
      if (argsObj) {
        Value v = maybeRead(fallback);
        if (v.isObject()) {
          *argsObj = &v.toObject().as<ArgumentsObject>();
        }
      } else {
        skip();
      }
    }

    if (thisv) {
      *thisv = maybeRead(fallback);
    } else {
      skip();
    }

    unsigned i = 0;
    if (end < start) {
      i = start;
    }

    for (; i < start; i++) {
      skip();
    }
    for (; i < end; i++) {
      // We are not always able to read values from the snapshots, some values
      // such as non-gc things may still be live in registers and cause an
      // error while reading the machine state.
      Value v = maybeRead(fallback);
      op(v);
    }
  }

  // Iterate over all the allocations and return only the value of the
  // allocation located at one index.
  Value maybeReadAllocByIndex(size_t index);

#ifdef TRACK_SNAPSHOTS
  void spewBailingFrom() const { snapshot_.spewBailingFrom(); }
#endif
};

// Reads frame information in callstack order (that is, innermost frame to
// outermost frame).
class InlineFrameIterator {
  const JSJitFrameIter* frame_;
  SnapshotIterator start_;
  SnapshotIterator si_;
  uint32_t framesRead_;

  // When the inline-frame-iterator is created, this variable is defined to
  // UINT32_MAX. Then the first iteration of findNextFrame, which settle on
  // the innermost frame, is used to update this counter to the number of
  // frames contained in the recover buffer.
  uint32_t frameCount_;

  // The |calleeTemplate_| fields contains either the JSFunction or the
  // template from which it is supposed to be cloned. The |calleeRVA_| is an
  // Invalid value allocation, if the |calleeTemplate_| field is the effective
  // JSFunction, and not its template. On the other hand, any other value
  // allocation implies that the |calleeTemplate_| is the template JSFunction
  // from which the effective one would be derived and cached by the Recover
  // instruction result.
  RootedFunction calleeTemplate_;
  RValueAllocation calleeRVA_;

  RootedScript script_;
  jsbytecode* pc_;
  uint32_t numActualArgs_;

  // Register state, used by all snapshot iterators.
  MachineState machine_;

  struct Nop {
    void operator()(const Value& v) {}
  };

 private:
  void findNextFrame();
  JSObject* computeEnvironmentChain(const Value& envChainValue,
                                    MaybeReadFallback& fallback,
                                    bool* hasInitialEnv = nullptr) const;

 public:
  InlineFrameIterator(JSContext* cx, const JSJitFrameIter* iter);
  InlineFrameIterator(JSContext* cx, const InlineFrameIterator* iter);

  bool more() const { return frame_ && framesRead_ < frameCount_; }

  // Due to optimizations, we are not always capable of reading the callee of
  // inlined frames without invalidating the IonCode. This function might
  // return either the effective callee of the JSFunction which might be used
  // to create it.
  //
  // As such, the |calleeTemplate()| can be used to read most of the metadata
  // which are conserved across clones.
  JSFunction* calleeTemplate() const {
    MOZ_ASSERT(isFunctionFrame());
    return calleeTemplate_;
  }
  JSFunction* maybeCalleeTemplate() const { return calleeTemplate_; }

  JSFunction* callee(MaybeReadFallback& fallback) const;

  unsigned numActualArgs() const {
    // The number of actual arguments for inline frames is determined by this
    // iterator based on the caller's bytecode instruction (Call, FunCall,
    // GetProp/SetProp, etc). For the outer function it's stored in the stack
    // frame.
    if (more()) {
      return numActualArgs_;
    }

    return frame_->numActualArgs();
  }

  template <class ArgOp, class LocalOp>
  void readFrameArgsAndLocals(JSContext* cx, ArgOp& argOp, LocalOp& localOp,
                              JSObject** envChain, bool* hasInitialEnv,
                              Value* rval, ArgumentsObject** argsObj,
                              Value* thisv, ReadFrameArgsBehavior behavior,
                              MaybeReadFallback& fallback) const {
    SnapshotIterator s(si_);

    // Read the env chain.
    if (envChain) {
      Value envChainValue = s.maybeRead(fallback);
      *envChain =
          computeEnvironmentChain(envChainValue, fallback, hasInitialEnv);
    } else {
      s.skip();
    }

    // Read return value.
    if (rval) {
      *rval = s.maybeRead(fallback);
    } else {
      s.skip();
    }

    // Read arguments, which only function frames have.
    if (isFunctionFrame()) {
      unsigned nactual = numActualArgs();
      unsigned nformal = calleeTemplate()->nargs();

      // Read the formal arguments, which are taken from the inlined frame,
      // because it will have the updated value when JSOp::SetArg is used.
      unsigned numFormalsToRead;
      if (behavior == ReadFrameArgsBehavior::Actuals) {
        numFormalsToRead = std::min(nactual, nformal);
      } else {
        MOZ_ASSERT(behavior == ReadFrameArgsBehavior::ActualsAndFormals);
        numFormalsToRead = nformal;
      }
      s.readFunctionFrameArgs(argOp, argsObj, thisv, 0, numFormalsToRead,
                              script(), fallback);

      // Skip formals we didn't read.
      for (unsigned i = numFormalsToRead; i < nformal; i++) {
        s.skip();
      }

      if (nactual > nformal) {
        if (more()) {
          // There is still a parent frame of this inlined frame.  All
          // arguments (also the overflown) are the last pushed values
          // in the parent frame.  To get the overflown arguments, we
          // need to take them from there.

          // The overflown arguments are not available in current frame.
          // They are the last pushed arguments in the parent frame of
          // this inlined frame.
          InlineFrameIterator it(cx, this);
          ++it;
          unsigned argsObjAdj = it.script()->needsArgsObj() ? 1 : 0;
          bool hasNewTarget = isConstructing();
          SnapshotIterator parent_s(it.snapshotIterator());

          // Skip over all slots until we get to the last slots
          // (= arguments slots of callee) the +3 is for [this], [returnvalue],
          // [envchain], and maybe +1 for [argsObj]
          MOZ_ASSERT(parent_s.numAllocations() >=
                     nactual + 3 + argsObjAdj + hasNewTarget);
          unsigned skip = parent_s.numAllocations() - nactual - 3 - argsObjAdj -
                          hasNewTarget;
          for (unsigned j = 0; j < skip; j++) {
            parent_s.skip();
          }

          // Get the overflown arguments
          parent_s.skip();  // env chain
          parent_s.skip();  // return value
          parent_s.readFunctionFrameArgs(argOp, nullptr, nullptr, nformal,
                                         nactual, it.script(), fallback);
        } else {
          // There is no parent frame to this inlined frame, we can read
          // from the frame's Value vector directly.
          Value* argv = frame_->actualArgs();
          for (unsigned i = nformal; i < nactual; i++) {
            argOp(argv[i]);
          }
        }
      }
    }

    // At this point we've read all the formals in s, and can read the
    // locals.
    for (unsigned i = 0; i < script()->nfixed(); i++) {
      localOp(s.maybeRead(fallback));
    }
  }

  template <class Op>
  void unaliasedForEachActual(JSContext* cx, Op op,
                              MaybeReadFallback& fallback) const {
    Nop nop;
    readFrameArgsAndLocals(cx, op, nop, nullptr, nullptr, nullptr, nullptr,
                           nullptr, ReadFrameArgsBehavior::Actuals, fallback);
  }

  JSScript* script() const { return script_; }
  jsbytecode* pc() const { return pc_; }
  SnapshotIterator snapshotIterator() const { return si_; }
  bool isFunctionFrame() const;
  bool isModuleFrame() const;
  bool isConstructing() const;

  JSObject* environmentChain(MaybeReadFallback& fallback,
                             bool* hasInitialEnvironment = nullptr) const {
    SnapshotIterator s(si_);

    // envChain
    Value v = s.maybeRead(fallback);
    return computeEnvironmentChain(v, fallback, hasInitialEnvironment);
  }

  Value thisArgument(MaybeReadFallback& fallback) const {
    SnapshotIterator s(si_);

    // envChain
    s.skip();

    // return value
    s.skip();

    // Arguments object.
    if (script()->needsArgsObj()) {
      s.skip();
    }

    return s.maybeRead(fallback);
  }

  InlineFrameIterator& operator++() {
    findNextFrame();
    return *this;
  }

  void dump() const;

  void resetOn(const JSJitFrameIter* iter);

  const JSJitFrameIter& frame() const { return *frame_; }

  // Inline frame number, 0 for the outermost (non-inlined) frame.
  size_t frameNo() const { return frameCount() - framesRead_; }
  size_t frameCount() const {
    MOZ_ASSERT(frameCount_ != UINT32_MAX);
    return frameCount_;
  }

 private:
  InlineFrameIterator() = delete;
  InlineFrameIterator(const InlineFrameIterator& iter) = delete;
};

}  // namespace jit
}  // namespace js

#endif /* jit_JSJitFrameIter_h */