summaryrefslogtreecommitdiff
path: root/js/src/jit/JitFrameIterator.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit/JitFrameIterator.h')
-rw-r--r--js/src/jit/JitFrameIterator.h864
1 files changed, 864 insertions, 0 deletions
diff --git a/js/src/jit/JitFrameIterator.h b/js/src/jit/JitFrameIterator.h
new file mode 100644
index 0000000000..ba5efef6a5
--- /dev/null
+++ b/js/src/jit/JitFrameIterator.h
@@ -0,0 +1,864 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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_JitFrameIterator_h
+#define jit_JitFrameIterator_h
+
+#include "jsfun.h"
+#include "jsscript.h"
+#include "jstypes.h"
+
+#include "jit/IonCode.h"
+#include "jit/Snapshots.h"
+
+#include "js/ProfilingFrameIterator.h"
+
+namespace js {
+ class ActivationIterator;
+} // namespace js
+
+namespace js {
+namespace jit {
+
+typedef void * CalleeToken;
+
+enum FrameType
+{
+ // A JS frame is analogous to a js::InterpreterFrame, representing one scripted
+ // function activation. IonJS frames are used by the optimizing compiler.
+ JitFrame_IonJS,
+
+ // JS frame used by the baseline JIT.
+ JitFrame_BaselineJS,
+
+ // Frame pushed for JIT stubs that make non-tail calls, so that the
+ // return address -> ICEntry mapping works.
+ JitFrame_BaselineStub,
+ JitFrame_IonStub,
+
+ // The entry frame is the initial prologue block transitioning from the VM
+ // into the Ion world.
+ JitFrame_Entry,
+
+ // A rectifier frame sits in between two JS frames, adapting argc != nargs
+ // mismatches in calls.
+ JitFrame_Rectifier,
+
+ // Ion IC calling a scripted getter/setter.
+ JitFrame_IonAccessorIC,
+
+ // 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.
+ JitFrame_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.
+ JitFrame_Bailout,
+};
+
+enum ReadFrameArgsBehavior {
+ // Only read formals (i.e. [0 ... callee()->nargs]
+ ReadFrame_Formals,
+
+ // Only read overflown args (i.e. [callee()->nargs ... numActuals()]
+ ReadFrame_Overflown,
+
+ // Read all args (i.e. [0 ... numActuals()])
+ ReadFrame_Actuals
+};
+
+class CommonFrameLayout;
+class JitFrameLayout;
+class ExitFrameLayout;
+
+class BaselineFrame;
+
+class JitActivation;
+
+// 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);
+
+class JitFrameIterator
+{
+ protected:
+ uint8_t* current_;
+ FrameType type_;
+ uint8_t* returnAddressToFp_;
+ size_t frameSize_;
+
+ private:
+ mutable const SafepointIndex* cachedSafepointIndex_;
+ const JitActivation* activation_;
+
+ void dumpBaseline() const;
+
+ public:
+ explicit JitFrameIterator();
+ explicit JitFrameIterator(JSContext* cx);
+ explicit JitFrameIterator(const ActivationIterator& activations);
+
+ // 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_ == JitFrame_Exit;
+ }
+ bool isScripted() const {
+ return type_ == JitFrame_BaselineJS || type_ == JitFrame_IonJS || type_ == JitFrame_Bailout;
+ }
+ bool isBaselineJS() const {
+ return type_ == JitFrame_BaselineJS;
+ }
+ bool isIonScripted() const {
+ return type_ == JitFrame_IonJS || type_ == JitFrame_Bailout;
+ }
+ bool isIonJS() const {
+ return type_ == JitFrame_IonJS;
+ }
+ bool isIonStub() const {
+ return type_ == JitFrame_IonStub;
+ }
+ bool isIonAccessorIC() const {
+ return type_ == JitFrame_IonAccessorIC;
+ }
+ bool isBailoutJS() const {
+ return type_ == JitFrame_Bailout;
+ }
+ bool isBaselineStub() const {
+ return type_ == JitFrame_BaselineStub;
+ }
+ bool isRectifier() const {
+ return type_ == JitFrame_Rectifier;
+ }
+ bool isBareExit() const;
+ template <typename T> bool isExitFrameLayout() const;
+
+ bool isEntry() const {
+ return type_ == JitFrame_Entry;
+ }
+ bool isFunctionFrame() const;
+
+ bool isConstructing() const;
+
+ void* calleeToken() const;
+ JSFunction* callee() const;
+ JSFunction* maybeCallee() const;
+ unsigned numActualArgs() const;
+ JSScript* script() const;
+ void baselineScriptAndPc(JSScript** scriptRes, jsbytecode** pcRes) const;
+ Value* actualArgs() const;
+
+ // Returns the return address of the frame above this one (that is, the
+ // return address that returns back to the current frame).
+ uint8_t* returnAddressToFp() const {
+ return returnAddressToFp_;
+ }
+
+ // Previous frame information extracted from the current frame.
+ inline size_t prevFrameLocalSize() const;
+ inline FrameType prevType() const;
+ uint8_t* prevFp() const;
+
+ // Returns the stack space used by the current frame, in bytes. This does
+ // not include the size of its fixed header.
+ size_t frameSize() const {
+ MOZ_ASSERT(!isExitFrame());
+ return frameSize_;
+ }
+
+ // Functions used to iterate on frames. When prevType is JitFrame_Entry,
+ // the current frame is the last frame.
+ inline bool done() const {
+ return type_ == JitFrame_Entry;
+ }
+ JitFrameIterator& 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, ReadFrameArgsBehavior behavior) const {
+ MOZ_ASSERT(isBaselineJS());
+
+ unsigned nactual = numActualArgs();
+ unsigned start, end;
+ switch (behavior) {
+ case ReadFrame_Formals:
+ start = 0;
+ end = callee()->nargs();
+ break;
+ case ReadFrame_Overflown:
+ start = callee()->nargs();
+ end = nactual;
+ break;
+ case ReadFrame_Actuals:
+ start = 0;
+ end = nactual;
+ }
+
+ Value* argv = actualArgs();
+ for (unsigned i = start; i < end; i++)
+ op(argv[i]);
+ }
+
+ void dump() const;
+
+ inline BaselineFrame* baselineFrame() 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
+ inline bool verifyReturnAddressUsingNativeToBytecodeMap() { return true; }
+#endif
+};
+
+class JitcodeGlobalTable;
+
+class JitProfilingFrameIterator
+{
+ uint8_t* fp_;
+ FrameType type_;
+ void* returnAddressToFp_;
+
+ inline JitFrameLayout* framePtr();
+ inline JSScript* frameScript();
+ MOZ_MUST_USE bool tryInitWithPC(void* pc);
+ MOZ_MUST_USE bool tryInitWithTable(JitcodeGlobalTable* table, void* pc, JSRuntime* rt,
+ bool forLastCallSite);
+ void fixBaselineReturnAddress();
+
+ void moveToNextFrame(CommonFrameLayout* frame);
+
+ public:
+ JitProfilingFrameIterator(JSRuntime* rt,
+ const JS::ProfilingFrameIterator::RegisterState& state);
+ explicit JitProfilingFrameIterator(void* exitFrame);
+
+ void operator++();
+ bool done() const { return fp_ == nullptr; }
+
+ void* fp() const { MOZ_ASSERT(!done()); return fp_; }
+ void* stackAddress() const { return fp(); }
+ FrameType frameType() const { MOZ_ASSERT(!done()); return type_; }
+ void* returnAddressToFp() const { MOZ_ASSERT(!done()); return returnAddressToFp_; }
+};
+
+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();
+
+ MOZ_MUST_USE bool init(JSContext* cx, uint32_t numResults);
+ bool isInitialized() const;
+#ifdef DEBUG
+ size_t length() const;
+#endif
+
+ JitFrameLayout* frame() const;
+
+ HeapPtr<Value>& operator[](size_t index);
+
+ void trace(JSTracer* trc);
+};
+
+struct MaybeReadFallback
+{
+ enum NoGCValue {
+ NoGC_UndefinedValue,
+ NoGC_MagicOptimizedOut
+ };
+
+ enum FallbackConsequence {
+ Fallback_Invalidate,
+ Fallback_DoNothing
+ };
+
+ JSContext* maybeCx;
+ JitActivation* activation;
+ const JitFrameIterator* frame;
+ const NoGCValue unreadablePlaceholder_;
+ const FallbackConsequence consequence;
+
+ explicit MaybeReadFallback(const Value& placeholder = UndefinedValue())
+ : maybeCx(nullptr),
+ activation(nullptr),
+ frame(nullptr),
+ unreadablePlaceholder_(noGCPlaceholder(placeholder)),
+ consequence(Fallback_Invalidate)
+ {
+ }
+
+ MaybeReadFallback(JSContext* cx, JitActivation* activation, const JitFrameIterator* frame,
+ FallbackConsequence consequence = Fallback_Invalidate)
+ : maybeCx(cx),
+ activation(activation),
+ frame(frame),
+ unreadablePlaceholder_(NoGC_UndefinedValue),
+ consequence(consequence)
+ {
+ }
+
+ bool canRecoverResults() { return maybeCx; }
+
+ Value unreadablePlaceholder() const {
+ if (unreadablePlaceholder_ == NoGC_MagicOptimizedOut)
+ return MagicValue(JS_OPTIMIZED_OUT);
+ return UndefinedValue();
+ }
+
+ NoGCValue noGCPlaceholder(const Value& v) const {
+ if (v.isMagic(JS_OPTIMIZED_OUT))
+ return NoGC_MagicOptimizedOut;
+ return NoGC_UndefinedValue;
+ }
+};
+
+
+class RResumePoint;
+class RSimdBox;
+
+// 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 ReadMethod {
+ // Read the normal value.
+ RM_Normal = 1 << 0,
+
+ // Read the default value, or the normal value if there is no default.
+ RM_AlwaysDefault = 1 << 1,
+
+ // Try to read the normal value if it is readable, otherwise default to
+ // the Default value.
+ RM_NormalOrDefault = RM_Normal | RM_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);
+ }
+ double fromRegister(FloatRegister reg) const {
+ return machine_->read(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 = RM_Normal);
+ MOZ_MUST_USE bool allocationReadable(const RValueAllocation& a, ReadMethod rm = RM_Normal);
+ void writeAllocationValuePayload(const RValueAllocation& a, const Value& v);
+ void warnUnreadableAllocation();
+
+ private:
+ friend class RSimdBox;
+ const FloatRegisters::RegisterContent* floatAllocationPointer(const RValueAllocation& a) const;
+
+ public:
+ // Handle iterating over RValueAllocations of the snapshots.
+ inline RValueAllocation readAllocation() {
+ MOZ_ASSERT(moreAllocations());
+ return snapshot_.readAllocation();
+ }
+ Value skip() {
+ snapshot_.skipAllocation();
+ return UndefinedValue();
+ }
+
+ const RResumePoint* resumePoint() const;
+ const RInstruction* instruction() const {
+ return recover_.instruction();
+ }
+
+ uint32_t numAllocations() const;
+ inline bool moreAllocations() const {
+ return snapshot_.numAllocationsRead() < numAllocations();
+ }
+
+ int32_t readOuterNumActualArgs() const;
+
+ // 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;
+ inline MOZ_MUST_USE bool resumeAfter() const {
+ // Inline frames are inlined on calls, which are considered as being
+ // resumed on the Call as baseline will push the pc once we return from
+ // the call.
+ if (moreFrames())
+ return false;
+ return recover_.resumeAfter();
+ }
+ 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();
+ }
+
+ protected:
+ // 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.
+ MOZ_MUST_USE bool initInstructionResults(MaybeReadFallback& fallback);
+
+ // This function is used internally for computing the result of the recover
+ // instructions.
+ MOZ_MUST_USE 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 JitFrameIterator& 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, RM_AlwaysDefault);
+ }
+
+ Value maybeRead(const RValueAllocation& a, MaybeReadFallback& fallback);
+ Value maybeRead(MaybeReadFallback& fallback) {
+ RValueAllocation a = readAllocation();
+ return maybeRead(a, fallback);
+ }
+
+ 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->argumentsHasVarBinding()) {
+ if (argsObj) {
+ Value v = read();
+ 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 JitFrameIterator* 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 JitFrameIterator* iter);
+ InlineFrameIterator(JSRuntime* rt, const JitFrameIterator* 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 of inline frames is recovered by the
+ // iteration process. It is recovered from the bytecode because this
+ // property still hold since the for inlined frames. This property does not
+ // hold for the parent frame because it can have optimize a call to
+ // js_fun_call or js_fun_apply.
+ 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, Value* newTarget,
+ 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();
+
+ if (newTarget) {
+ // For now, only support reading new.target when we are reading
+ // overflown arguments.
+ MOZ_ASSERT(behavior != ReadFrame_Formals);
+ newTarget->setUndefined();
+ }
+
+ // Read arguments, which only function frames have.
+ if (isFunctionFrame()) {
+ unsigned nactual = numActualArgs();
+ unsigned nformal = calleeTemplate()->nargs();
+
+ // Get the non overflown arguments, which are taken from the inlined
+ // frame, because it will have the updated value when JSOP_SETARG is
+ // done.
+ if (behavior != ReadFrame_Overflown)
+ s.readFunctionFrameArgs(argOp, argsObj, thisv, 0, nformal, script(), fallback);
+
+ if (behavior != ReadFrame_Formals) {
+ 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()->argumentsHasVarBinding() ? 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
+ MaybeReadFallback unusedFallback;
+ parent_s.skip(); // env chain
+ parent_s.skip(); // return value
+ parent_s.readFunctionFrameArgs(argOp, nullptr, nullptr,
+ nformal, nactual, it.script(),
+ fallback);
+ if (newTarget && isConstructing())
+ *newTarget = parent_s.maybeRead(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]);
+ if (newTarget && isConstructing())
+ *newTarget = argv[nactual];
+ }
+ }
+ }
+
+ // 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,
+ ReadFrameArgsBehavior behavior,
+ MaybeReadFallback& fallback) const
+ {
+ Nop nop;
+ readFrameArgsAndLocals(cx, op, nop, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, behavior, fallback);
+ }
+
+ JSScript* script() const {
+ return script_;
+ }
+ jsbytecode* pc() const {
+ return pc_;
+ }
+ SnapshotIterator snapshotIterator() const {
+ return si_;
+ }
+ bool isFunctionFrame() const;
+ bool isConstructing() const;
+
+ JSObject* environmentChain(MaybeReadFallback& fallback) const {
+ SnapshotIterator s(si_);
+
+ // envChain
+ Value v = s.maybeRead(fallback);
+ return computeEnvironmentChain(v, fallback);
+ }
+
+ Value thisArgument(MaybeReadFallback& fallback) const {
+ SnapshotIterator s(si_);
+
+ // envChain
+ s.skip();
+
+ // return value
+ s.skip();
+
+ // Arguments object.
+ if (script()->argumentsHasVarBinding())
+ s.skip();
+
+ return s.maybeRead(fallback);
+ }
+
+ InlineFrameIterator& operator++() {
+ findNextFrame();
+ return *this;
+ }
+
+ void dump() const;
+
+ void resetOn(const JitFrameIterator* iter);
+
+ const JitFrameIterator& 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_JitFrameIterator_h */