#ifndef jit_JitFrames_h
#define jit_JitFrames_h
#include <stdint.h>
#include "jit/JSJitFrameIter.h"
#include "vm/JSContext.h"
#include "vm/JSFunction.h"
namespace js {
namespace jit {
struct SafepointSlotEntry;
struct VMFunctionData;
enum CalleeTokenTag {
CalleeToken_Function = 0x0, CalleeToken_FunctionConstructing = 0x1,
CalleeToken_Script = 0x2
};
static const uintptr_t CalleeTokenScriptBit = CalleeToken_Script;
static const uintptr_t CalleeTokenMask = ~uintptr_t(0x3);
static inline CalleeTokenTag GetCalleeTokenTag(CalleeToken token) {
CalleeTokenTag tag = CalleeTokenTag(uintptr_t(token) & 0x3);
MOZ_ASSERT(tag <= CalleeToken_Script);
return tag;
}
static inline CalleeToken CalleeToToken(JSFunction* fun, bool constructing) {
CalleeTokenTag tag =
constructing ? CalleeToken_FunctionConstructing : CalleeToken_Function;
return CalleeToken(uintptr_t(fun) | uintptr_t(tag));
}
static inline CalleeToken CalleeToToken(JSScript* script) {
return CalleeToken(uintptr_t(script) | uintptr_t(CalleeToken_Script));
}
static inline bool CalleeTokenIsFunction(CalleeToken token) {
CalleeTokenTag tag = GetCalleeTokenTag(token);
return tag == CalleeToken_Function || tag == CalleeToken_FunctionConstructing;
}
static inline bool CalleeTokenIsConstructing(CalleeToken token) {
return GetCalleeTokenTag(token) == CalleeToken_FunctionConstructing;
}
static inline JSFunction* CalleeTokenToFunction(CalleeToken token) {
MOZ_ASSERT(CalleeTokenIsFunction(token));
return (JSFunction*)(uintptr_t(token) & CalleeTokenMask);
}
static inline JSScript* CalleeTokenToScript(CalleeToken token) {
MOZ_ASSERT(GetCalleeTokenTag(token) == CalleeToken_Script);
return (JSScript*)(uintptr_t(token) & CalleeTokenMask);
}
static inline bool CalleeTokenIsModuleScript(CalleeToken token) {
CalleeTokenTag tag = GetCalleeTokenTag(token);
return tag == CalleeToken_Script && CalleeTokenToScript(token)->module();
}
static inline JSScript* ScriptFromCalleeToken(CalleeToken token) {
switch (GetCalleeTokenTag(token)) {
case CalleeToken_Script:
return CalleeTokenToScript(token);
case CalleeToken_Function:
case CalleeToken_FunctionConstructing:
return CalleeTokenToFunction(token)->nonLazyScript();
}
MOZ_CRASH("invalid callee token tag");
}
class LSafepoint;
class SafepointIndex {
uint32_t displacement_;
union {
LSafepoint* safepoint_;
uint32_t safepointOffset_;
};
#ifdef DEBUG
bool resolved;
#endif
public:
SafepointIndex(uint32_t displacement, LSafepoint* safepoint)
: displacement_(displacement),
safepoint_(safepoint)
#ifdef DEBUG
,
resolved(false)
#endif
{
}
void resolve();
LSafepoint* safepoint() {
MOZ_ASSERT(!resolved);
return safepoint_;
}
uint32_t displacement() const { return displacement_; }
uint32_t safepointOffset() const { return safepointOffset_; }
void adjustDisplacement(uint32_t offset) {
MOZ_ASSERT(offset >= displacement_);
displacement_ = offset;
}
inline SnapshotOffset snapshotOffset() const;
inline bool hasSnapshotOffset() const;
};
class MacroAssembler;
class OsiIndex {
uint32_t callPointDisplacement_;
uint32_t snapshotOffset_;
public:
OsiIndex(uint32_t callPointDisplacement, uint32_t snapshotOffset)
: callPointDisplacement_(callPointDisplacement),
snapshotOffset_(snapshotOffset) {}
uint32_t returnPointDisplacement() const;
uint32_t callPointDisplacement() const { return callPointDisplacement_; }
uint32_t snapshotOffset() const { return snapshotOffset_; }
};
static const uintptr_t FRAMETYPE_BITS = 4;
static const uintptr_t FRAMETYPE_MASK = (1 << FRAMETYPE_BITS) - 1;
static const uintptr_t FRAME_HEADER_SIZE_SHIFT = FRAMETYPE_BITS;
static const uintptr_t FRAME_HEADER_SIZE_BITS = 3;
static const uintptr_t FRAME_HEADER_SIZE_MASK =
(1 << FRAME_HEADER_SIZE_BITS) - 1;
static const uintptr_t HASCACHEDSAVEDFRAME_BIT =
1 << (FRAMETYPE_BITS + FRAME_HEADER_SIZE_BITS);
static const uintptr_t FRAMESIZE_SHIFT =
FRAMETYPE_BITS + FRAME_HEADER_SIZE_BITS + 1 ;
static const uintptr_t FRAMESIZE_BITS = 32 - FRAMESIZE_SHIFT;
static const uintptr_t FRAMESIZE_MASK = (1 << FRAMESIZE_BITS) - 1;
static const uint32_t NO_FRAME_SIZE_CLASS_ID = uint32_t(-1);
class FrameSizeClass {
uint32_t class_;
explicit FrameSizeClass(uint32_t class_) : class_(class_) {}
public:
FrameSizeClass() = delete;
static FrameSizeClass None() {
return FrameSizeClass(NO_FRAME_SIZE_CLASS_ID);
}
static FrameSizeClass FromClass(uint32_t class_) {
return FrameSizeClass(class_);
}
static FrameSizeClass FromDepth(uint32_t frameDepth);
static FrameSizeClass ClassLimit();
uint32_t frameSize() const;
uint32_t classId() const {
MOZ_ASSERT(class_ != NO_FRAME_SIZE_CLASS_ID);
return class_;
}
bool operator==(const FrameSizeClass& other) const {
return class_ == other.class_;
}
bool operator!=(const FrameSizeClass& other) const {
return class_ != other.class_;
}
};
struct BaselineBailoutInfo;
struct ResumeFromException {
static const uint32_t RESUME_ENTRY_FRAME = 0;
static const uint32_t RESUME_CATCH = 1;
static const uint32_t RESUME_FINALLY = 2;
static const uint32_t RESUME_FORCED_RETURN = 3;
static const uint32_t RESUME_BAILOUT = 4;
static const uint32_t RESUME_WASM = 5;
uint8_t* framePointer;
uint8_t* stackPointer;
uint8_t* target;
uint32_t kind;
Value exception;
BaselineBailoutInfo* bailoutInfo;
};
void HandleException(ResumeFromException* rfe);
void EnsureBareExitFrame(JitActivation* act, JitFrameLayout* frame);
void TraceJitActivations(JSContext* cx, JSTracer* trc);
void UpdateJitActivationsForMinorGC(JSRuntime* rt);
static inline uint32_t EncodeFrameHeaderSize(size_t headerSize) {
MOZ_ASSERT((headerSize % sizeof(uintptr_t)) == 0);
uint32_t headerSizeWords = headerSize / sizeof(uintptr_t);
MOZ_ASSERT(headerSizeWords <= FRAME_HEADER_SIZE_MASK);
return headerSizeWords;
}
static inline uint32_t MakeFrameDescriptor(uint32_t frameSize, FrameType type,
uint32_t headerSize) {
MOZ_ASSERT(frameSize < FRAMESIZE_MASK);
headerSize = EncodeFrameHeaderSize(headerSize);
return 0 | (frameSize << FRAMESIZE_SHIFT) |
(headerSize << FRAME_HEADER_SIZE_SHIFT) | uint32_t(type);
}
inline JSScript* GetTopJitJSScript(JSContext* cx) {
JSJitFrameIter frame(cx->activation()->asJit());
MOZ_ASSERT(frame.type() == FrameType::Exit);
++frame;
if (frame.isBaselineStub()) {
++frame;
MOZ_ASSERT(frame.isBaselineJS());
}
MOZ_ASSERT(frame.isScripted());
return frame.script();
}
#ifdef JS_CODEGEN_MIPS32
uint8_t* alignDoubleSpillWithOffset(uint8_t* pointer, int32_t offset);
#else
inline uint8_t* alignDoubleSpillWithOffset(uint8_t* pointer, int32_t offset) {
return pointer;
}
#endif
class CommonFrameLayout {
uint8_t* returnAddress_;
uintptr_t descriptor_;
public:
static size_t offsetOfDescriptor() {
return offsetof(CommonFrameLayout, descriptor_);
}
uintptr_t descriptor() const { return descriptor_; }
static size_t offsetOfReturnAddress() {
return offsetof(CommonFrameLayout, returnAddress_);
}
FrameType prevType() const { return FrameType(descriptor_ & FRAMETYPE_MASK); }
void changePrevType(FrameType type) {
descriptor_ &= ~FRAMETYPE_MASK;
descriptor_ |= uintptr_t(type);
}
size_t prevFrameLocalSize() const { return descriptor_ >> FRAMESIZE_SHIFT; }
size_t headerSize() const {
return sizeof(uintptr_t) *
((descriptor_ >> FRAME_HEADER_SIZE_SHIFT) & FRAME_HEADER_SIZE_MASK);
}
bool hasCachedSavedFrame() const {
return descriptor_ & HASCACHEDSAVEDFRAME_BIT;
}
void setHasCachedSavedFrame() { descriptor_ |= HASCACHEDSAVEDFRAME_BIT; }
void clearHasCachedSavedFrame() { descriptor_ &= ~HASCACHEDSAVEDFRAME_BIT; }
uint8_t* returnAddress() const { return returnAddress_; }
void setReturnAddress(uint8_t* addr) { returnAddress_ = addr; }
};
class JitFrameLayout : public CommonFrameLayout {
CalleeToken calleeToken_;
uintptr_t numActualArgs_;
public:
CalleeToken calleeToken() const { return calleeToken_; }
void replaceCalleeToken(CalleeToken calleeToken) {
calleeToken_ = calleeToken;
}
static size_t offsetOfCalleeToken() {
return offsetof(JitFrameLayout, calleeToken_);
}
static size_t offsetOfNumActualArgs() {
return offsetof(JitFrameLayout, numActualArgs_);
}
static size_t offsetOfThis() { return sizeof(JitFrameLayout); }
static size_t offsetOfEvalNewTarget() { return sizeof(JitFrameLayout); }
static size_t offsetOfActualArgs() { return offsetOfThis() + sizeof(Value); }
static size_t offsetOfActualArg(size_t arg) {
return offsetOfActualArgs() + arg * sizeof(Value);
}
Value thisv() {
MOZ_ASSERT(CalleeTokenIsFunction(calleeToken()));
return argv()[0];
}
Value* argv() {
MOZ_ASSERT(CalleeTokenIsFunction(calleeToken()));
return (Value*)(this + 1);
}
uintptr_t numActualArgs() const { return numActualArgs_; }
uintptr_t* slotRef(SafepointSlotEntry where);
static inline size_t Size() { return sizeof(JitFrameLayout); }
};
class RectifierFrameLayout : public JitFrameLayout {
public:
static inline size_t Size() { return sizeof(RectifierFrameLayout); }
};
class WasmToJSJitFrameLayout : public JitFrameLayout {
public:
static inline size_t Size() { return sizeof(WasmToJSJitFrameLayout); }
};
class IonICCallFrameLayout : public CommonFrameLayout {
protected:
JitCode* stubCode_;
public:
JitCode** stubCode() { return &stubCode_; }
static size_t Size() { return sizeof(IonICCallFrameLayout); }
};
enum class ExitFrameType : uint8_t {
CallNative = 0x0,
ConstructNative = 0x1,
IonDOMGetter = 0x2,
IonDOMSetter = 0x3,
IonDOMMethod = 0x4,
IonOOLNative = 0x5,
IonOOLProxy = 0x6,
WasmGenericJitEntry = 0x7,
DirectWasmJitCall = 0x8,
InterpreterStub = 0xFC,
VMFunction = 0xFD,
LazyLink = 0xFE,
Bare = 0xFF,
};
class ExitFooterFrame {
uintptr_t data_;
public:
static inline size_t Size() { return sizeof(ExitFooterFrame); }
void setBareExitFrame() { data_ = uintptr_t(ExitFrameType::Bare); }
ExitFrameType type() const {
static_assert(sizeof(ExitFrameType) == sizeof(uint8_t),
"Code assumes ExitFrameType fits in a byte");
if (data_ > UINT8_MAX) {
return ExitFrameType::VMFunction;
}
MOZ_ASSERT(ExitFrameType(data_) != ExitFrameType::VMFunction);
return ExitFrameType(data_);
}
inline const VMFunctionData* function() const {
MOZ_ASSERT(type() == ExitFrameType::VMFunction);
return reinterpret_cast<const VMFunctionData*>(data_);
}
template <typename T>
T* outParam() {
uint8_t* address = reinterpret_cast<uint8_t*>(this);
address = alignDoubleSpillWithOffset(address, sizeof(intptr_t));
return reinterpret_cast<T*>(address - sizeof(T));
}
};
class NativeExitFrameLayout;
class IonOOLNativeExitFrameLayout;
class IonOOLProxyExitFrameLayout;
class IonDOMExitFrameLayout;
class ExitFrameLayout : public CommonFrameLayout {
inline uint8_t* top() { return reinterpret_cast<uint8_t*>(this + 1); }
public:
static inline size_t Size() { return sizeof(ExitFrameLayout); }
static inline size_t SizeWithFooter() {
return Size() + ExitFooterFrame::Size();
}
inline ExitFooterFrame* footer() {
uint8_t* sp = reinterpret_cast<uint8_t*>(this);
return reinterpret_cast<ExitFooterFrame*>(sp - ExitFooterFrame::Size());
}
inline uint8_t* argBase() {
MOZ_ASSERT(isWrapperExit());
return top();
}
inline bool isWrapperExit() {
return footer()->type() == ExitFrameType::VMFunction;
}
inline bool isBareExit() { return footer()->type() == ExitFrameType::Bare; }
template <typename T>
inline bool is() {
return footer()->type() == T::Type();
}
template <typename T>
inline T* as() {
MOZ_ASSERT(this->is<T>());
return reinterpret_cast<T*>(footer());
}
};
class NativeExitFrameLayout {
protected: ExitFooterFrame footer_;
ExitFrameLayout exit_;
uintptr_t argc_;
uint32_t loCalleeResult_;
uint32_t hiCalleeResult_;
public:
static inline size_t Size() { return sizeof(NativeExitFrameLayout); }
static size_t offsetOfResult() {
return offsetof(NativeExitFrameLayout, loCalleeResult_);
}
inline Value* vp() { return reinterpret_cast<Value*>(&loCalleeResult_); }
inline uintptr_t argc() const { return argc_; }
};
class CallNativeExitFrameLayout : public NativeExitFrameLayout {
public:
static ExitFrameType Type() { return ExitFrameType::CallNative; }
};
class ConstructNativeExitFrameLayout : public NativeExitFrameLayout {
public:
static ExitFrameType Type() { return ExitFrameType::ConstructNative; }
};
template <>
inline bool ExitFrameLayout::is<NativeExitFrameLayout>() {
return is<CallNativeExitFrameLayout>() ||
is<ConstructNativeExitFrameLayout>();
}
class IonOOLNativeExitFrameLayout {
protected: ExitFooterFrame footer_;
ExitFrameLayout exit_;
JitCode* stubCode_;
uintptr_t argc_;
uint32_t loCalleeResult_;
uint32_t hiCalleeResult_;
uint32_t loThis_;
uint32_t hiThis_;
public:
static ExitFrameType Type() { return ExitFrameType::IonOOLNative; }
static inline size_t Size(size_t argc) {
return sizeof(IonOOLNativeExitFrameLayout) + (argc * sizeof(Value));
}
static size_t offsetOfResult() {
return offsetof(IonOOLNativeExitFrameLayout, loCalleeResult_);
}
inline JitCode** stubCode() { return &stubCode_; }
inline Value* vp() { return reinterpret_cast<Value*>(&loCalleeResult_); }
inline Value* thisp() { return reinterpret_cast<Value*>(&loThis_); }
inline uintptr_t argc() const { return argc_; }
};
class IonOOLProxyExitFrameLayout {
protected: ExitFooterFrame footer_;
ExitFrameLayout exit_;
JSObject* proxy_;
jsid id_;
uint32_t vp0_;
uint32_t vp1_;
JitCode* stubCode_;
public:
static ExitFrameType Type() { return ExitFrameType::IonOOLProxy; }
static inline size_t Size() { return sizeof(IonOOLProxyExitFrameLayout); }
static size_t offsetOfResult() {
return offsetof(IonOOLProxyExitFrameLayout, vp0_);
}
inline JitCode** stubCode() { return &stubCode_; }
inline Value* vp() { return reinterpret_cast<Value*>(&vp0_); }
inline jsid* id() { return &id_; }
inline JSObject** proxy() { return &proxy_; }
};
class IonDOMExitFrameLayout {
protected: ExitFooterFrame footer_;
ExitFrameLayout exit_;
JSObject* thisObj;
uint32_t loCalleeResult_;
uint32_t hiCalleeResult_;
public:
static ExitFrameType GetterType() { return ExitFrameType::IonDOMGetter; }
static ExitFrameType SetterType() { return ExitFrameType::IonDOMSetter; }
static inline size_t Size() { return sizeof(IonDOMExitFrameLayout); }
static size_t offsetOfResult() {
return offsetof(IonDOMExitFrameLayout, loCalleeResult_);
}
inline Value* vp() { return reinterpret_cast<Value*>(&loCalleeResult_); }
inline JSObject** thisObjAddress() { return &thisObj; }
inline bool isMethodFrame();
};
struct IonDOMMethodExitFrameLayoutTraits;
class IonDOMMethodExitFrameLayout {
protected: ExitFooterFrame footer_;
ExitFrameLayout exit_;
JSObject* thisObj_;
Value* argv_;
uintptr_t argc_;
uint32_t loCalleeResult_;
uint32_t hiCalleeResult_;
friend struct IonDOMMethodExitFrameLayoutTraits;
public:
static ExitFrameType Type() { return ExitFrameType::IonDOMMethod; }
static inline size_t Size() { return sizeof(IonDOMMethodExitFrameLayout); }
static size_t offsetOfResult() {
return offsetof(IonDOMMethodExitFrameLayout, loCalleeResult_);
}
inline Value* vp() {
JS_STATIC_ASSERT(
offsetof(IonDOMMethodExitFrameLayout, loCalleeResult_) ==
(offsetof(IonDOMMethodExitFrameLayout, argc_) + sizeof(uintptr_t)));
return reinterpret_cast<Value*>(&loCalleeResult_);
}
inline JSObject** thisObjAddress() { return &thisObj_; }
inline uintptr_t argc() { return argc_; }
};
inline bool IonDOMExitFrameLayout::isMethodFrame() {
return footer_.type() == IonDOMMethodExitFrameLayout::Type();
}
template <>
inline bool ExitFrameLayout::is<IonDOMExitFrameLayout>() {
ExitFrameType type = footer()->type();
return type == IonDOMExitFrameLayout::GetterType() ||
type == IonDOMExitFrameLayout::SetterType() ||
type == IonDOMMethodExitFrameLayout::Type();
}
template <>
inline IonDOMExitFrameLayout* ExitFrameLayout::as<IonDOMExitFrameLayout>() {
MOZ_ASSERT(is<IonDOMExitFrameLayout>());
return reinterpret_cast<IonDOMExitFrameLayout*>(footer());
}
struct IonDOMMethodExitFrameLayoutTraits {
static const size_t offsetOfArgcFromArgv =
offsetof(IonDOMMethodExitFrameLayout, argc_) -
offsetof(IonDOMMethodExitFrameLayout, argv_);
};
class CalledFromJitExitFrameLayout {
protected: ExitFooterFrame footer_;
JitFrameLayout exit_;
public:
static inline size_t Size() { return sizeof(CalledFromJitExitFrameLayout); }
inline JitFrameLayout* jsFrame() { return &exit_; }
static size_t offsetOfExitFrame() {
return offsetof(CalledFromJitExitFrameLayout, exit_);
}
};
class LazyLinkExitFrameLayout : public CalledFromJitExitFrameLayout {
public:
static ExitFrameType Type() { return ExitFrameType::LazyLink; }
};
class InterpreterStubExitFrameLayout : public CalledFromJitExitFrameLayout {
public:
static ExitFrameType Type() { return ExitFrameType::InterpreterStub; }
};
class WasmGenericJitEntryFrameLayout : CalledFromJitExitFrameLayout {
public:
static ExitFrameType Type() { return ExitFrameType::WasmGenericJitEntry; }
};
template <>
inline bool ExitFrameLayout::is<CalledFromJitExitFrameLayout>() {
return is<InterpreterStubExitFrameLayout>() ||
is<LazyLinkExitFrameLayout>() || is<WasmGenericJitEntryFrameLayout>();
}
template <>
inline CalledFromJitExitFrameLayout*
ExitFrameLayout::as<CalledFromJitExitFrameLayout>() {
MOZ_ASSERT(is<CalledFromJitExitFrameLayout>());
uint8_t* sp = reinterpret_cast<uint8_t*>(this);
sp -= CalledFromJitExitFrameLayout::offsetOfExitFrame();
return reinterpret_cast<CalledFromJitExitFrameLayout*>(sp);
}
class DirectWasmJitCallFrameLayout {
protected: ExitFooterFrame footer_;
ExitFrameLayout exit_;
public:
static ExitFrameType Type() { return ExitFrameType::DirectWasmJitCall; }
};
class ICStub;
class JitStubFrameLayout : public CommonFrameLayout {
public:
static size_t Size() { return sizeof(JitStubFrameLayout); }
static inline int reverseOffsetOfStubPtr() { return -int(sizeof(void*)); }
inline ICStub* maybeStubPtr() {
uint8_t* fp = reinterpret_cast<uint8_t*>(this);
return *reinterpret_cast<ICStub**>(fp + reverseOffsetOfStubPtr());
}
};
class BaselineStubFrameLayout : public JitStubFrameLayout {
public:
static inline size_t Size() { return sizeof(BaselineStubFrameLayout); }
static inline int reverseOffsetOfSavedFramePtr() {
return -int(2 * sizeof(void*));
}
void* reverseSavedFramePtr() {
uint8_t* addr = ((uint8_t*)this) + reverseOffsetOfSavedFramePtr();
return *(void**)addr;
}
inline void setStubPtr(ICStub* stub) {
uint8_t* fp = reinterpret_cast<uint8_t*>(this);
*reinterpret_cast<ICStub**>(fp + reverseOffsetOfStubPtr()) = stub;
}
};
class InvalidationBailoutStack {
RegisterDump::FPUArray fpregs_;
RegisterDump::GPRArray regs_;
IonScript* ionScript_;
uint8_t* osiPointReturnAddress_;
public:
uint8_t* sp() const {
return (uint8_t*)this + sizeof(InvalidationBailoutStack);
}
JitFrameLayout* fp() const;
MachineState machine() { return MachineState::FromBailout(regs_, fpregs_); }
IonScript* ionScript() const { return ionScript_; }
uint8_t* osiPointReturnAddress() const { return osiPointReturnAddress_; }
static size_t offsetOfFpRegs() {
return offsetof(InvalidationBailoutStack, fpregs_);
}
static size_t offsetOfRegs() {
return offsetof(InvalidationBailoutStack, regs_);
}
void checkInvariants() const;
};
void GetPcScript(JSContext* cx, JSScript** scriptRes, jsbytecode** pcRes);
CalleeToken TraceCalleeToken(JSTracer* trc, CalleeToken token);
static const uint32_t MinJITStackSize = 1;
}
}
#endif