Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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 MaybeForwardedScriptFromCalleeToken(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);
}
Value newTarget() const {
MOZ_ASSERT(isFunctionFrame());
MOZ_ASSERT(!callee()->isArrow());
if (isConstructing()) {
unsigned pushedArgs = std::max(numFormalArgs(), numActualArgs());
return argv()[pushedArgs];
}
return UndefinedValue();
}
#ifdef DEBUG
size_t debugNumValueSlots() const { return numValueSlots(debugFrameSize()); }
#endif
Value* valueSlot(size_t slot) const {
#ifndef ENABLE_PORTABLE_BASELINE_INTERP
// Assert that we're within the frame, but only if the "debug
// frame size" has been set. Ordinarily if we are in C++ code
// looking upward at a baseline frame, it will be, because it is
// set for the *previous* frame when we push an exit frame and
// call back into C++ from generated baseline code. However, the
// portable baseline interpreter uses accessors on BaselineFrame
// directly within the active frame and so the "debug frame size"
// hasn't been set (and it would be expensive to constantly update
// it). Because this is only used for assertions, and is not
// needed for correctness, we can disable this check below when
// PBL is enabled.
MOZ_ASSERT(slot < debugNumValueSlots());
#endif
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_;
}
jsbytecode*& interpreterPC() {
MOZ_ASSERT(runningInInterpreter());
return interpreterPC_;
}
ICEntry* interpreterICEntry() const {
MOZ_ASSERT(runningInInterpreter());
return interpreterICEntry_;
}
ICEntry*& interpreterICEntry() {
MOZ_ASSERT(runningInInterpreter());
return interpreterICEntry_;
}
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 setFlag(uint32_t flag) { flags_ |= flag; }
void setFlags(uint32_t flags) { flags_ = flags; }
[[nodiscard]] inline bool pushLexicalEnvironment(JSContext* cx,
Handle<LexicalScope*> scope);
template <bool IsDebuggee>
[[nodiscard]] inline bool freshenLexicalEnvironment(
JSContext* cx, const jsbytecode* pc = nullptr);
template <bool IsDebuggee>
[[nodiscard]] inline bool recreateLexicalEnvironment(
JSContext* cx, const jsbytecode* pc = nullptr);
[[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 */