summaryrefslogtreecommitdiffstats
path: root/js/src/jit/BaselineFrame.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit/BaselineFrame.h')
-rw-r--r--js/src/jit/BaselineFrame.h373
1 files changed, 373 insertions, 0 deletions
diff --git a/js/src/jit/BaselineFrame.h b/js/src/jit/BaselineFrame.h
new file mode 100644
index 0000000000..6614d23e15
--- /dev/null
+++ b/js/src/jit/BaselineFrame.h
@@ -0,0 +1,373 @@
+/* -*- 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_BaselineFrame_h
+#define jit_BaselineFrame_h
+
+#include <algorithm>
+
+#include "jit/CalleeToken.h"
+#include "jit/JitFrames.h"
+#include "jit/ScriptFromCalleeToken.h"
+#include "vm/Stack.h"
+
+namespace js {
+namespace jit {
+
+class ICEntry;
+class ICScript;
+class JSJitFrameIter;
+
+// The stack looks like this, fp is the frame pointer:
+//
+// fp+y arguments
+// fp => JitFrameLayout (frame header)
+// fp-x BaselineFrame
+// locals
+// stack values
+
+class BaselineFrame {
+ public:
+ enum Flags : uint32_t {
+ // The frame has a valid return value. See also InterpreterFrame::HAS_RVAL.
+ HAS_RVAL = 1 << 0,
+
+ // The frame is running in the Baseline interpreter instead of JIT.
+ RUNNING_IN_INTERPRETER = 1 << 1,
+
+ // An initial environment has been pushed on the environment chain for
+ // function frames that need a CallObject or eval frames that need a
+ // VarEnvironmentObject.
+ HAS_INITIAL_ENV = 1 << 2,
+
+ // Frame has an arguments object, argsObj_.
+ HAS_ARGS_OBJ = 1 << 4,
+
+ // See InterpreterFrame::PREV_UP_TO_DATE.
+ PREV_UP_TO_DATE = 1 << 5,
+
+ // Frame has execution observed by a Debugger.
+ //
+ // See comment above 'isDebuggee' in vm/Realm.h for explanation
+ // of invariants of debuggee compartments, scripts, and frames.
+ DEBUGGEE = 1 << 6,
+ };
+
+ protected: // Silence Clang warning about unused private fields.
+ // The fields below are only valid if RUNNING_IN_INTERPRETER.
+ JSScript* interpreterScript_;
+ jsbytecode* interpreterPC_;
+ ICEntry* interpreterICEntry_;
+
+ JSObject* envChain_; // Environment chain (always initialized).
+ ICScript* icScript_; // IC script (initialized if Warp is enabled).
+ ArgumentsObject* argsObj_; // If HAS_ARGS_OBJ, the arguments object.
+
+ // We need to split the Value into 2 fields of 32 bits, otherwise the C++
+ // compiler may add some padding between the fields.
+ uint32_t loScratchValue_;
+ uint32_t hiScratchValue_;
+ uint32_t flags_;
+#ifdef DEBUG
+ // Size of the frame. Stored in DEBUG builds when calling into C++. This is
+ // BaselineFrame::Size() + the size of the local and expression stack Values.
+ //
+ // We don't store this in release builds because it's redundant with the frame
+ // size computed from the frame pointers. In debug builds it's still useful
+ // for assertions.
+ uint32_t debugFrameSize_;
+#else
+ uint32_t unused_;
+#endif
+ uint32_t loReturnValue_; // If HAS_RVAL, the frame's return value.
+ uint32_t hiReturnValue_;
+
+ public:
+ [[nodiscard]] bool initForOsr(InterpreterFrame* fp, uint32_t numStackValues);
+
+#ifdef DEBUG
+ uint32_t debugFrameSize() const { return debugFrameSize_; }
+ void setDebugFrameSize(uint32_t frameSize) { debugFrameSize_ = frameSize; }
+#endif
+
+ JSObject* environmentChain() const { return envChain_; }
+ void setEnvironmentChain(JSObject* envChain) { envChain_ = envChain; }
+
+ template <typename SpecificEnvironment>
+ inline void pushOnEnvironmentChain(SpecificEnvironment& env);
+ template <typename SpecificEnvironment>
+ inline void popOffEnvironmentChain();
+ inline void replaceInnermostEnvironment(EnvironmentObject& env);
+
+ CalleeToken calleeToken() const { return framePrefix()->calleeToken(); }
+ void replaceCalleeToken(CalleeToken token) {
+ framePrefix()->replaceCalleeToken(token);
+ }
+ bool isConstructing() const {
+ return CalleeTokenIsConstructing(calleeToken());
+ }
+ JSScript* script() const { return ScriptFromCalleeToken(calleeToken()); }
+ JSFunction* callee() const { return CalleeTokenToFunction(calleeToken()); }
+ Value calleev() const { return ObjectValue(*callee()); }
+
+ size_t numValueSlots(size_t frameSize) const {
+ MOZ_ASSERT(frameSize == debugFrameSize());
+
+ MOZ_ASSERT(frameSize >= BaselineFrame::Size());
+ frameSize -= BaselineFrame::Size();
+
+ MOZ_ASSERT((frameSize % sizeof(Value)) == 0);
+ return frameSize / sizeof(Value);
+ }
+
+#ifdef DEBUG
+ size_t debugNumValueSlots() const { return numValueSlots(debugFrameSize()); }
+#endif
+
+ Value* valueSlot(size_t slot) const {
+ MOZ_ASSERT(slot < debugNumValueSlots());
+ return (Value*)this - (slot + 1);
+ }
+
+ static size_t frameSizeForNumValueSlots(size_t numValueSlots) {
+ return BaselineFrame::Size() + numValueSlots * sizeof(Value);
+ }
+
+ Value& unaliasedFormal(
+ unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const {
+ MOZ_ASSERT(i < numFormalArgs());
+ MOZ_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals() &&
+ !script()->formalIsAliased(i));
+ return argv()[i];
+ }
+
+ Value& unaliasedActual(
+ unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const {
+ MOZ_ASSERT(i < numActualArgs());
+ MOZ_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals());
+ MOZ_ASSERT_IF(checkAliasing && i < numFormalArgs(),
+ !script()->formalIsAliased(i));
+ return argv()[i];
+ }
+
+ Value& unaliasedLocal(uint32_t i) const {
+ MOZ_ASSERT(i < script()->nfixed());
+ return *valueSlot(i);
+ }
+
+ unsigned numActualArgs() const { return framePrefix()->numActualArgs(); }
+ unsigned numFormalArgs() const { return script()->function()->nargs(); }
+ Value& thisArgument() const {
+ MOZ_ASSERT(isFunctionFrame());
+ return framePrefix()->thisv();
+ }
+ Value* argv() const { return framePrefix()->actualArgs(); }
+
+ [[nodiscard]] bool saveGeneratorSlots(JSContext* cx, unsigned nslots,
+ ArrayObject* dest) const;
+
+ public:
+ void prepareForBaselineInterpreterToJitOSR() {
+ // Clearing the RUNNING_IN_INTERPRETER flag is sufficient, but we also null
+ // out the interpreter fields to ensure we don't use stale values.
+ flags_ &= ~RUNNING_IN_INTERPRETER;
+ interpreterScript_ = nullptr;
+ interpreterPC_ = nullptr;
+ }
+
+ private:
+ bool uninlineIsProfilerSamplingEnabled(JSContext* cx);
+
+ public:
+ // Switch a JIT frame on the stack to Interpreter mode. The caller is
+ // responsible for patching the return address into this frame to a location
+ // in the interpreter code. Also assert profiler sampling has been suppressed
+ // so the sampler thread doesn't see an inconsistent state while we are
+ // patching frames.
+ void switchFromJitToInterpreter(JSContext* cx, jsbytecode* pc) {
+ MOZ_ASSERT(!uninlineIsProfilerSamplingEnabled(cx));
+ MOZ_ASSERT(!runningInInterpreter());
+ flags_ |= RUNNING_IN_INTERPRETER;
+ setInterpreterFields(pc);
+ }
+ void switchFromJitToInterpreterAtPrologue(JSContext* cx) {
+ MOZ_ASSERT(!uninlineIsProfilerSamplingEnabled(cx));
+ MOZ_ASSERT(!runningInInterpreter());
+ flags_ |= RUNNING_IN_INTERPRETER;
+ setInterpreterFieldsForPrologue(script());
+ }
+
+ // Like switchFromJitToInterpreter, but set the interpreterICEntry_ field to
+ // nullptr. Initializing this field requires a binary search on the
+ // JitScript's ICEntry list but the exception handler never returns to this
+ // pc anyway so we can avoid the overhead.
+ void switchFromJitToInterpreterForExceptionHandler(JSContext* cx,
+ jsbytecode* pc) {
+ MOZ_ASSERT(!uninlineIsProfilerSamplingEnabled(cx));
+ MOZ_ASSERT(!runningInInterpreter());
+ flags_ |= RUNNING_IN_INTERPRETER;
+ interpreterScript_ = script();
+ interpreterPC_ = pc;
+ interpreterICEntry_ = nullptr;
+ }
+
+ bool runningInInterpreter() const { return flags_ & RUNNING_IN_INTERPRETER; }
+
+ JSScript* interpreterScript() const {
+ MOZ_ASSERT(runningInInterpreter());
+ return interpreterScript_;
+ }
+
+ jsbytecode* interpreterPC() const {
+ MOZ_ASSERT(runningInInterpreter());
+ return interpreterPC_;
+ }
+
+ void setInterpreterFields(JSScript* script, jsbytecode* pc);
+
+ void setInterpreterFields(jsbytecode* pc) {
+ setInterpreterFields(script(), pc);
+ }
+
+ // Initialize interpreter fields for resuming in the prologue (before the
+ // argument type check ICs).
+ void setInterpreterFieldsForPrologue(JSScript* script);
+
+ ICScript* icScript() const { return icScript_; }
+ void setICScript(ICScript* icScript) { icScript_ = icScript; }
+
+ // The script that owns the current ICScript.
+ JSScript* outerScript() const;
+
+ bool hasReturnValue() const { return flags_ & HAS_RVAL; }
+ MutableHandleValue returnValue() {
+ if (!hasReturnValue()) {
+ addressOfReturnValue()->setUndefined();
+ }
+ return MutableHandleValue::fromMarkedLocation(addressOfReturnValue());
+ }
+ void setReturnValue(const Value& v) {
+ returnValue().set(v);
+ flags_ |= HAS_RVAL;
+ }
+ inline Value* addressOfReturnValue() {
+ return reinterpret_cast<Value*>(&loReturnValue_);
+ }
+
+ bool hasInitialEnvironment() const { return flags_ & HAS_INITIAL_ENV; }
+
+ inline CallObject& callObj() const;
+
+ void setFlags(uint32_t flags) { flags_ = flags; }
+
+ [[nodiscard]] inline bool pushLexicalEnvironment(JSContext* cx,
+ Handle<LexicalScope*> scope);
+ [[nodiscard]] inline bool freshenLexicalEnvironment(JSContext* cx);
+ [[nodiscard]] inline bool recreateLexicalEnvironment(JSContext* cx);
+
+ [[nodiscard]] bool initFunctionEnvironmentObjects(JSContext* cx);
+ [[nodiscard]] bool pushClassBodyEnvironment(JSContext* cx,
+ Handle<ClassBodyScope*> scope);
+ [[nodiscard]] bool pushVarEnvironment(JSContext* cx, Handle<Scope*> scope);
+
+ void initArgsObjUnchecked(ArgumentsObject& argsobj) {
+ flags_ |= HAS_ARGS_OBJ;
+ argsObj_ = &argsobj;
+ }
+ void initArgsObj(ArgumentsObject& argsobj) {
+ MOZ_ASSERT(script()->needsArgsObj());
+ initArgsObjUnchecked(argsobj);
+ }
+ bool hasArgsObj() const { return flags_ & HAS_ARGS_OBJ; }
+ ArgumentsObject& argsObj() const {
+ MOZ_ASSERT(hasArgsObj());
+ MOZ_ASSERT(script()->needsArgsObj());
+ return *argsObj_;
+ }
+
+ bool prevUpToDate() const { return flags_ & PREV_UP_TO_DATE; }
+ void setPrevUpToDate() { flags_ |= PREV_UP_TO_DATE; }
+ void unsetPrevUpToDate() { flags_ &= ~PREV_UP_TO_DATE; }
+
+ bool isDebuggee() const { return flags_ & DEBUGGEE; }
+ void setIsDebuggee() { flags_ |= DEBUGGEE; }
+ inline void unsetIsDebuggee();
+
+ void trace(JSTracer* trc, const JSJitFrameIter& frame);
+
+ bool isGlobalFrame() const { return script()->isGlobalCode(); }
+ bool isModuleFrame() const { return script()->isModule(); }
+ bool isEvalFrame() const { return script()->isForEval(); }
+ bool isFunctionFrame() const {
+ return CalleeTokenIsFunction(calleeToken()) && !isModuleFrame();
+ }
+ bool isDebuggerEvalFrame() const { return false; }
+
+ JitFrameLayout* framePrefix() const {
+ uint8_t* fp = (uint8_t*)this + Size();
+ return (JitFrameLayout*)fp;
+ }
+
+ static size_t Size() { return sizeof(BaselineFrame); }
+
+ // The reverseOffsetOf methods below compute the offset relative to the
+ // frame's base pointer. Since the stack grows down, these offsets are
+ // negative.
+
+#ifdef DEBUG
+ static int reverseOffsetOfDebugFrameSize() {
+ return -int(Size()) + offsetof(BaselineFrame, debugFrameSize_);
+ }
+#endif
+
+ // The scratch value slot can either be used as a Value slot or as two
+ // separate 32-bit integer slots.
+ static int reverseOffsetOfScratchValueLow32() {
+ return -int(Size()) + offsetof(BaselineFrame, loScratchValue_);
+ }
+ static int reverseOffsetOfScratchValueHigh32() {
+ return -int(Size()) + offsetof(BaselineFrame, hiScratchValue_);
+ }
+ static int reverseOffsetOfScratchValue() {
+ return reverseOffsetOfScratchValueLow32();
+ }
+
+ static int reverseOffsetOfEnvironmentChain() {
+ return -int(Size()) + offsetof(BaselineFrame, envChain_);
+ }
+ static int reverseOffsetOfArgsObj() {
+ return -int(Size()) + offsetof(BaselineFrame, argsObj_);
+ }
+ static int reverseOffsetOfFlags() {
+ return -int(Size()) + offsetof(BaselineFrame, flags_);
+ }
+ static int reverseOffsetOfReturnValue() {
+ return -int(Size()) + offsetof(BaselineFrame, loReturnValue_);
+ }
+ static int reverseOffsetOfInterpreterScript() {
+ return -int(Size()) + offsetof(BaselineFrame, interpreterScript_);
+ }
+ static int reverseOffsetOfInterpreterPC() {
+ return -int(Size()) + offsetof(BaselineFrame, interpreterPC_);
+ }
+ static int reverseOffsetOfInterpreterICEntry() {
+ return -int(Size()) + offsetof(BaselineFrame, interpreterICEntry_);
+ }
+ static int reverseOffsetOfICScript() {
+ return -int(Size()) + offsetof(BaselineFrame, icScript_);
+ }
+ static int reverseOffsetOfLocal(size_t index) {
+ return -int(Size()) - (index + 1) * sizeof(Value);
+ }
+};
+
+// Ensure the frame is 8-byte aligned (required on ARM).
+static_assert((sizeof(BaselineFrame) % 8) == 0, "frame must be 8-byte aligned");
+
+} // namespace jit
+} // namespace js
+
+#endif /* jit_BaselineFrame_h */