/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ECMASCRIPT_FRAMES_H #define ECMASCRIPT_FRAMES_H #include "ecmascript/js_tagged_value.h" // Frame Layout // Interpreter Frame(alias **iframe** ) Layout as follow: // ``` // +----------------------------------+-------------------+ // | argv[n-1] | ^ // |----------------------------------| | // | ...... | | // |----------------------------------| | // | thisArg [maybe not exist] | | // |----------------------------------| | // | newTarget [maybe not exist] | | // |----------------------------------| | // | ...... | | // |----------------------------------| | // | Vregs [not exist in native] | | // +----------------------------------+--------+ interpreter frame // | base.frameType | ^ | // |----------------------------------| | | // | base.prev(prev stack pointer) | | | // |----------------------------------| | | // | pc(bytecode addr) | | | // |----------------------------------| | | // | sp(current stack pointer) | | | // |----------------------------------| | | // | env | | | // |----------------------------------| | | // | acc | | | // |----------------------------------|InterpretedFrame | // | profileTypeInfo | | | // |----------------------------------| | | // | function | | | // |----------------------------------| | | // | constpool | v v // +----------------------------------+--------+----------+ // Optimized Leave Frame(alias OptimizedLeaveFrame) layout // +--------------------------+ // | argv[argc-1] | // +--------------------------+ // | .......... | // +--------------------------+ // | argv[1] | // +--------------------------+ // | argv[0] | // +--------------------------+ --- // | argc | ^ // |--------------------------| Fixed // | RuntimeId | OptimizedLeaveFrame // |--------------------------| | // | returnAddr | | // |--------------------------| | // | callsiteFp | | // |--------------------------| | // | frameType | v // +--------------------------+ --- // | callee save registers | // +--------------------------+ // Optimized Leave Frame with Argv(alias OptimizedWithArgvLeaveFrame) layout // +--------------------------+ // | argv[] | // +--------------------------+ --- // | argc | ^ // |--------------------------| Fixed // | RuntimeId | OptimizedWithArgvLeaveFrame // |--------------------------| | // | returnAddr | | // |--------------------------| | // | callsiteFp | | // |--------------------------| | // | frameType | v // +--------------------------+ --- // | callee save registers | // +--------------------------+ // Optimized Frame(alias OptimizedFrame) layout // +--------------------------+ // | calleesave registers | ^ // |----------------------| | // | returnaddress | Fixed // |----------------------| OptimizedFrame // | prevFp | | // |----------------------| | // | frameType | | // |----------------------| | // | callSiteSp | v // +--------------------------+ // Optimized JSFunction Frame Frame(alias OptimizedJSFunctionFrame) layout // +--------------------------+ // | calleesave registers | ^ // |----------------------| | // | returnaddress | Fixed // |----------------------| OptimizedJSFunctionFrame // | prevFp | | // |----------------------| | // | frameType | | // |----------------------| | // | lexEnv | v // +--------------------------+ // Optimized Entry Frame(alias OptimizedEntryFrame) layout // +--------------------------+ // | returnaddress | ^ // |----------------------| | // |calleesave registers | Fixed // |----------------------| OptimizedEntryFrame // | prevFp | | // |----------------------| | // | frameType | | // |----------------------| | // | prevLeaveFrameFp | v // +--------------------------+ // Interpreted Entry Frame(alias InterpretedEntryFrame) layout // +--------------------------+ // | base.type | ^ // |----------------------| | // | base.prev | InterpretedEntryFrame // |----------------------| | // | pc | v // +--------------------------+ // ``` // address space grow from high address to low address.we add new field **FrameType** , // the field's value is INTERPRETER_FRAME(represent interpreter frame). // **currentsp** is pointer to callTarget field address, sp field 's value is **currentsp** , // pre field pointer pre stack frame point. fill JSThread's sp field with iframe sp field // by calling JSThread->SetCurrentSPFrame and save pre Frame address to iframe pre field. // For Example: // ``` // call call // foo -----------------> bar ----------------------->baz ---------------------> rtfunc // (interpret frame) (OptimizedEntryFrame) (OptimizedFrame) (OptimizedLeaveFrame + Runtime Frame) // ``` // Frame Layout as follow: // ``` // +----------------------------------+-------------------+ // | argv[n-1] | ^ // |----------------------------------| | // | ...... | | // |----------------------------------| | // | thisArg [maybe not exist] | | // |----------------------------------| | // | newTarget [maybe not exist] | | // |----------------------------------| | // | ...... | | // |----------------------------------| | // | Vregs | | // +----------------------------------+--------+ foo's frame // | base.frameType | ^ | // |----------------------------------| | | // | base.prev(prev stack pointer) | | | // |----------------------------------| | | // | pc(bytecode addr) | | | // +----------------------------------| | | // | sp(current stack pointer) | | | // |----------------------------------| | | // | env | | | // |----------------------------------| | | // | acc | | | // |----------------------------------| | | // | profileTypeInfo |InterpretedFrame | // |----------------------------------| | | // | function | | | // |----------------------------------| | | // | constpool | v v // +----------------------------------+--------+----------+ // | ............. | // +--------------------------+---------------------------+ // | returnaddress | ^ ^ // |----------------------| | | // |calleesave registers | Fixed | // |----------------------| OptimizedEntryFrame bar's frame // | prevFp | | | // |----------------------| | | // | frameType | | | // |----------------------| | | // | prevLeaveFrameFp | v | // +--------------------------+ V // +--------------------------+---------------------------+ // | ............. | // +--------------------------+---------------------------+ // +--------------------------+---------------------------+ // | calleesave registers | ^ ^ // |----------------------| | | // | returnaddress | Fixed | // |----------------------| OptimizedJSFunctionFrame | // | prevFp | | | // |----------------------| | baz's frame Header // | frameType | | | // |----------------------| | | // | lexEnv | | V // +--------------------------+ // +--------------------------+---------------------------+ // | ............. | // +--------------------------+---------------------------+ // +--------------------------+---------------------------+ // | argv[] | ^ // +--------------------------+---- | // | argc | ^ | // |--------------------------| Fixed | // | RuntimeId | OptimizedLeaveFrame | // |--------------------------| | OptimizedLeaveFrame // | returnAddr | | | // |--------------------------| | | // | callsiteFp | | | // |--------------------------| | | // | frameType | V | // +--------------------------+ ---- | // | callee save registers | V // +--------------------------+---------------------------+ // | ............. | // +--------------------------+---------------------------+ // | | // | rtfunc's Frame | // | | // +------------------------------------------------------+ // ``` // Iterator: // rtfunc get OptimizedLeaveFrame by calling GetCurrentSPFrame. // get baz's Frame by OptimizedLeaveFrame.prev field. // get bar's Frame by baz's frame fp field // get foo's Frame by bar's Frame prev field #include "ecmascript/base/aligned_struct.h" #include "ecmascript/mem/chunk_containers.h" #include "ecmascript/mem/visitor.h" namespace panda::ecmascript { class JSThread; class EcmaVM; class FrameIterator; namespace kungfu { class LLVMStackMapParser; }; using DerivedDataKey = std::pair; enum class FrameType: uintptr_t { OPTIMIZED_FRAME = 0, OPTIMIZED_ENTRY_FRAME = 1, OPTIMIZED_JS_FUNCTION_FRAME = 2, LEAVE_FRAME = 3, LEAVE_FRAME_WITH_ARGV = 4, BUILTIN_CALL_LEAVE_FRAME = 5, INTERPRETER_FRAME = 6, ASM_INTERPRETER_FRAME = 7, INTERPRETER_CONSTRUCTOR_FRAME = 8, BUILTIN_FRAME = 9, BUILTIN_FRAME_WITH_ARGV = 10, BUILTIN_ENTRY_FRAME = 11, INTERPRETER_BUILTIN_FRAME = 12, INTERPRETER_FAST_NEW_FRAME = 13, INTERPRETER_ENTRY_FRAME = 14, ASM_INTERPRETER_ENTRY_FRAME = 15, ASM_INTERPRETER_BRIDGE_FRAME = 16, OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME = 17, INTERPRETER_BEGIN = INTERPRETER_FRAME, INTERPRETER_END = INTERPRETER_FAST_NEW_FRAME, BUILTIN_BEGIN = BUILTIN_FRAME, BUILTIN_END = BUILTIN_ENTRY_FRAME, }; enum class ReservedSlots: int { OPTIMIZED_RESERVED_SLOT = 1, OPTIMIZED_JS_FUNCTION_RESERVED_SLOT = 2, OPTIMIZED_ENTRY_RESERVED_SLOT = 2, }; enum class JSCallMode : uintptr_t { CALL_ARG0 = 0, CALL_ARG1, CALL_ARG2, CALL_ARG3, CALL_WITH_ARGV, CALL_THIS_WITH_ARGV, CALL_CONSTRUCTOR_WITH_ARGV, CALL_SUPER_CALL_WITH_ARGV, CALL_GETTER, CALL_SETTER, CALL_ENTRY, CALL_GENERATOR, CALL_FROM_AOT, }; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) struct OptimizedFrame : public base::AlignedStruct { public: void GCIterate(const FrameIterator &it, const RootVisitor &v0, [[maybe_unused]] const RootRangeVisitor &v1, ChunkMap *derivedPointers, bool isVerifying) const; private: enum class Index : size_t { TypeIndex = 0, PrevFpIndex, ReturnAddrIndex, NumOfMembers }; static_assert(static_cast(Index::NumOfMembers) == NumOfTypes); static OptimizedFrame* GetFrameFromSp(const JSTaggedType *sp) { return reinterpret_cast(reinterpret_cast(sp) - MEMBER_OFFSET(OptimizedFrame, prevFp)); } inline JSTaggedType* GetPrevFrameFp() { return prevFp; } uintptr_t GetReturnAddr() const { return returnAddr; } [[maybe_unused]] alignas(EAS) FrameType type {0}; alignas(EAS) JSTaggedType *prevFp {nullptr}; alignas(EAS) uintptr_t returnAddr {0}; friend class FrameIterator; }; STATIC_ASSERT_EQ_ARCH(sizeof(OptimizedFrame), OptimizedFrame::SizeArch32, OptimizedFrame::SizeArch64); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) struct OptimizedJSFunctionArgConfigFrame : public base::AlignedStruct { private: enum class Index : size_t { TypeIndex = 0, PrevFpIndex, NumOfMembers }; static_assert(static_cast(Index::NumOfMembers) == NumOfTypes); static OptimizedJSFunctionArgConfigFrame* GetFrameFromSp(const JSTaggedType *sp) { return reinterpret_cast(reinterpret_cast(sp) - MEMBER_OFFSET(OptimizedJSFunctionArgConfigFrame, prevFp)); } inline JSTaggedType* GetPrevFrameFp() { return prevFp; } [[maybe_unused]] alignas(EAS) FrameType type {0}; alignas(EAS) JSTaggedType *prevFp {nullptr}; friend class FrameIterator; }; STATIC_ASSERT_EQ_ARCH(sizeof(OptimizedJSFunctionArgConfigFrame), OptimizedJSFunctionArgConfigFrame::SizeArch32, OptimizedJSFunctionArgConfigFrame::SizeArch64); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) struct OptimizedJSFunctionFrame : public base::AlignedStruct { public: static constexpr size_t ENV_SLOT_DIFF = 2; enum class Index : size_t { EnvIndex = 0, TypeIndex, PrevFpIndex, ReturnAddrIndex, NumOfMembers }; static_assert(static_cast(Index::NumOfMembers) == NumOfTypes); inline JSTaggedType* GetPrevFrameFp() { return prevFp; } uintptr_t* ComputePrevFrameSp(const FrameIterator &it) const; JSTaggedType* GetArgv(uintptr_t *preFrameSp) const { return reinterpret_cast(preFrameSp + ENV_SLOT_DIFF * sizeof(uint64_t) / sizeof(uintptr_t)); } size_t GetArgc(uintptr_t *preFrameSp) const { return *(preFrameSp + sizeof(uint64_t) / sizeof(uintptr_t)); } JSTaggedType* GetArgv(const FrameIterator &it) const; uintptr_t GetReturnAddr() const { return returnAddr; } void GCIterate( const FrameIterator &it, const RootVisitor &v0, const RootRangeVisitor &v1, ChunkMap *derivedPointers, bool isVerifying) const; inline JSTaggedValue GetEnv() const { return env; } inline void SetEnv(JSTaggedValue lexEnv) { env = lexEnv; } friend class FrameIterator; private: static OptimizedJSFunctionFrame* GetFrameFromSp(const JSTaggedType *sp) { return reinterpret_cast(reinterpret_cast(sp) - MEMBER_OFFSET(OptimizedJSFunctionFrame, prevFp)); } // dynamic callee saveregisters for x86-64 alignas(EAS) JSTaggedValue env {JSTaggedValue::Hole()}; [[maybe_unused]] alignas(EAS) FrameType type {0}; alignas(EAS) JSTaggedType *prevFp {nullptr}; alignas(EAS) uintptr_t returnAddr {0}; // dynamic callee saveregisters for arm64 // argc // argv[0] // argv[1] // ... argv[n - 1] }; STATIC_ASSERT_EQ_ARCH(sizeof(OptimizedJSFunctionFrame), OptimizedJSFunctionFrame::SizeArch32, OptimizedJSFunctionFrame::SizeArch64); struct OptimizedEntryFrame { public: OptimizedEntryFrame() = default; ~OptimizedEntryFrame() = default; JSTaggedType *preLeaveFrameFp; [[maybe_unused]] FrameType type; JSTaggedType *prevFp; inline JSTaggedType* GetPrevFrameFp() { return preLeaveFrameFp; } friend class FrameIterator; private: static OptimizedEntryFrame* GetFrameFromSp(const JSTaggedType *sp) { return reinterpret_cast(reinterpret_cast(sp) - MEMBER_OFFSET(OptimizedEntryFrame, prevFp)); } }; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) struct InterpretedFrameBase : public base::AlignedStruct { enum class Index : size_t { PrevIndex = 0, TypeIndex, NumOfMembers }; static_assert(static_cast(Index::NumOfMembers) == NumOfTypes); inline JSTaggedType* GetPrevFrameFp() { return prev; } static InterpretedFrameBase* GetFrameFromSp(const JSTaggedType *sp) { return reinterpret_cast(const_cast(sp)) - 1; } static size_t GetPrevOffset(bool isArch32) { return GetOffset(Index::PrevIndex)>(isArch32); } static size_t GetTypeOffset(bool isArch32) { return GetOffset(Index::TypeIndex)>(isArch32); } alignas(EAS) JSTaggedType *prev {nullptr}; // for llvm :c-fp ; for interrupt: thread-fp for gc alignas(EAS) FrameType type {FrameType::OPTIMIZED_FRAME}; // 0 }; STATIC_ASSERT_EQ_ARCH(sizeof(InterpretedFrameBase), InterpretedFrameBase::SizeArch32, InterpretedFrameBase::SizeArch64); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) struct InterpretedFrame : public base::AlignedStruct { public: enum class Index : size_t { ConstPoolIndex = 0, FunctionIndex, ProFileTypeInfoIndex, AccIndex, EnvIndex, PcIndex, BaseIndex, NumOfMembers }; static_assert(static_cast(Index::NumOfMembers) == NumOfTypes); inline JSTaggedType* GetPrevFrameFp() const { return base.prev; } static InterpretedFrame* GetFrameFromSp(const JSTaggedType *sp) { return reinterpret_cast(const_cast(sp)) - 1; } inline const uint8_t *GetPc() const { return pc; } inline JSTaggedValue GetEnv() const { return env; } static uint32_t NumOfMembers() { return sizeof(InterpretedFrame) / JSTaggedValue::TaggedTypeSize(); } void GCIterate(const FrameIterator &it, const RootVisitor &v0, const RootRangeVisitor &v1) const; alignas(EAS) JSTaggedValue constpool {JSTaggedValue::Hole()}; alignas(EAS) JSTaggedValue function {JSTaggedValue::Hole()}; alignas(EAS) JSTaggedValue profileTypeInfo {JSTaggedValue::Hole()}; alignas(EAS) JSTaggedValue acc {JSTaggedValue::Hole()}; alignas(EAS) JSTaggedValue env {JSTaggedValue::Hole()}; alignas(EAS) const uint8_t *pc {nullptr}; alignas(EAS) InterpretedFrameBase base; friend class FrameIterator; }; STATIC_ASSERT_EQ_ARCH(sizeof(InterpretedFrame), InterpretedFrame::SizeArch32, InterpretedFrame::SizeArch64); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) struct InterpretedBuiltinFrame : public base::AlignedStruct { enum class Index : size_t { FunctionIndex = 0, PcIndex, BaseIndex, NumOfMembers }; static_assert(static_cast(Index::NumOfMembers) == NumOfTypes); inline JSTaggedType* GetPrevFrameFp() { return base.prev; } static InterpretedBuiltinFrame* GetFrameFromSp(const JSTaggedType *sp) { return reinterpret_cast(const_cast(sp)) - 1; } static uint32_t NumOfMembers() { return sizeof(InterpretedBuiltinFrame) / JSTaggedValue::TaggedTypeSize(); } void GCIterate(const FrameIterator &it, const RootVisitor &v0, const RootRangeVisitor &v1) const; alignas(EAS) JSTaggedValue function {JSTaggedValue::Hole()}; alignas(EAS) const uint8_t *pc {nullptr}; alignas(EAS) InterpretedFrameBase base; }; STATIC_ASSERT_EQ_ARCH(sizeof(InterpretedBuiltinFrame), InterpretedBuiltinFrame::SizeArch32, InterpretedBuiltinFrame::SizeArch64); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) struct AsmInterpretedFrame : public base::AlignedStruct { enum class Index : size_t { FunctionIndex = 0, AccIndex, EnvIndex, CallSizeIndex, FpIndex, PcIndex, BaseIndex, NumOfMembers }; static_assert(static_cast(Index::NumOfMembers) == NumOfTypes); inline JSTaggedType* GetCurrentFramePointer() { return fp; } inline JSTaggedType* GetPrevFrameFp() { return base.prev; } static AsmInterpretedFrame* GetFrameFromSp(const JSTaggedType *sp) { return reinterpret_cast(const_cast(sp)) - 1; } static size_t GetFpOffset(bool isArch32) { return GetOffset(Index::FpIndex)>(isArch32); } static size_t GetCallSizeOffset(bool isArch32) { return GetOffset(Index::CallSizeIndex)>(isArch32); } static size_t GetFunctionOffset(bool isArch32) { return GetOffset(Index::FunctionIndex)>(isArch32); } static size_t GetAccOffset(bool isArch32) { return GetOffset(Index::AccIndex)>(isArch32); } static size_t GetEnvOffset(bool isArch32) { return GetOffset(Index::EnvIndex)>(isArch32); } static size_t GetBaseOffset(bool isArch32) { return GetOffset(Index::BaseIndex)>(isArch32); } static size_t GetPcOffset(bool isArch32) { return GetOffset(Index::PcIndex)>(isArch32); } static constexpr size_t GetSize(bool isArch32) { return isArch32 ? AsmInterpretedFrame::SizeArch32 : AsmInterpretedFrame::SizeArch64; } static uint32_t NumOfMembers() { return sizeof(AsmInterpretedFrame) / JSTaggedValue::TaggedTypeSize(); } void GCIterate(const FrameIterator &it, const RootVisitor &v0, const RootRangeVisitor &v1, ChunkMap *derivedPointers, bool isVerifying) const; JSTaggedValue GetEnv() const { return env; } const uint8_t *GetPc() { return pc; } alignas(EAS) JSTaggedValue function {JSTaggedValue::Hole()}; alignas(EAS) JSTaggedValue acc {JSTaggedValue::Hole()}; alignas(EAS) JSTaggedValue env {JSTaggedValue::Hole()}; alignas(EAS) uintptr_t callSize {0}; alignas(EAS) JSTaggedType *fp {nullptr}; alignas(EAS) const uint8_t *pc {nullptr}; alignas(EAS) InterpretedFrameBase base; // vregs, not exist in native // args, may be truncated if not extra // thisObject, used in asm constructor frame // numArgs, used if extra or asm constructor frame enum ReverseIndex : int32_t { THIS_OBJECT_REVERSE_INDEX = -2 }; }; STATIC_ASSERT_EQ_ARCH(sizeof(AsmInterpretedFrame), AsmInterpretedFrame::SizeArch32, AsmInterpretedFrame::SizeArch64); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) struct InterpretedEntryFrame : public base::AlignedStruct { enum class Index : size_t { PcIndex = 0, BaseIndex, NumOfMembers }; static_assert(static_cast(Index::NumOfMembers) == NumOfTypes); inline JSTaggedType* GetPrevFrameFp() { return base.prev; } static InterpretedEntryFrame* GetFrameFromSp(const JSTaggedType *sp) { return reinterpret_cast(const_cast(sp)) - 1; } static uint32_t NumOfMembers() { return sizeof(InterpretedEntryFrame) / JSTaggedValue::TaggedTypeSize(); } void GCIterate(const FrameIterator &it, const RootVisitor &v0, const RootRangeVisitor &v1) const; alignas(EAS) const uint8_t *pc {nullptr}; alignas(EAS) InterpretedFrameBase base; }; STATIC_ASSERT_EQ_ARCH(sizeof(InterpretedEntryFrame), InterpretedEntryFrame::SizeArch32, InterpretedEntryFrame::SizeArch64); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) struct AsmInterpretedEntryFrame : public base::AlignedStruct { enum class Index : size_t { PcIndex = 0, BaseIndex, NumOfMembers }; static_assert(static_cast(Index::NumOfMembers) == NumOfTypes); inline JSTaggedType* GetPrevFrameFp() { return base.prev; } static AsmInterpretedEntryFrame* GetFrameFromSp(const JSTaggedType *sp) { return reinterpret_cast(const_cast(sp)) - 1; } alignas(EAS) const uint8_t *pc {nullptr}; alignas(EAS) InterpretedFrameBase base; }; struct AsmInterpretedBridgeFrame : public base::AlignedStruct { enum class Index : size_t { EntryIndex = 0, ReturnAddrIndex, NumOfMembers }; static_assert(static_cast(Index::NumOfMembers) == NumOfTypes); static AsmInterpretedBridgeFrame* GetFrameFromSp(const JSTaggedType *sp) { return reinterpret_cast(reinterpret_cast(sp) - MEMBER_OFFSET(AsmInterpretedBridgeFrame, returnAddr)); } uintptr_t GetCallSiteSp() const { return ToUintPtr(this) + sizeof(AsmInterpretedBridgeFrame); } inline JSTaggedType* GetPrevFrameFp() { return entry.base.prev; } alignas(EAS) AsmInterpretedEntryFrame entry; alignas(EAS) uintptr_t returnAddr; uintptr_t GetReturnAddr() const { return returnAddr; } }; struct OptimizedLeaveFrame { FrameType type; uintptr_t callsiteFp; // thread sp set here uintptr_t returnAddr; #ifndef PANDA_TARGET_32 uint64_t argRuntimeId; #endif uint64_t argc; // argv[0]...argv[argc-1] dynamic according to agc static OptimizedLeaveFrame* GetFrameFromSp(const JSTaggedType *sp) { return reinterpret_cast(reinterpret_cast(sp) - MEMBER_OFFSET(OptimizedLeaveFrame, callsiteFp)); } uintptr_t GetCallSiteSp() const { #ifndef PANDA_TARGET_32 return ToUintPtr(this) + MEMBER_OFFSET(OptimizedLeaveFrame, argRuntimeId); #else return ToUintPtr(this) + MEMBER_OFFSET(OptimizedLeaveFrame, argc) + argc * sizeof(JSTaggedType); #endif } inline JSTaggedType* GetPrevFrameFp() const { return reinterpret_cast(callsiteFp); } JSTaggedType *GetJsFuncFrameArgv(JSThread *thread) const; uintptr_t GetReturnAddr() const { return returnAddr; } void GCIterate( const FrameIterator &it, const RootVisitor &v0, const RootRangeVisitor &v1, ChunkMap *derivedPointers, bool isVerifying) const; }; struct OptimizedWithArgvLeaveFrame { FrameType type; uintptr_t callsiteFp; // thread sp set here uintptr_t returnAddr; #ifndef PANDA_TARGET_32 uint64_t argRuntimeId; #endif uint64_t argc; // uintptr_t argv[] static OptimizedWithArgvLeaveFrame* GetFrameFromSp(const JSTaggedType *sp) { return reinterpret_cast(reinterpret_cast(sp) - MEMBER_OFFSET(OptimizedWithArgvLeaveFrame, callsiteFp)); } uintptr_t GetCallSiteSp() const { #ifndef PANDA_TARGET_32 return ToUintPtr(this) + MEMBER_OFFSET(OptimizedWithArgvLeaveFrame, argRuntimeId); #else return ToUintPtr(this) + MEMBER_OFFSET(OptimizedWithArgvLeaveFrame, argc) + argc * sizeof(JSTaggedType); #endif } inline JSTaggedType* GetPrevFrameFp() { return reinterpret_cast(callsiteFp); } uintptr_t GetReturnAddr() const { return returnAddr; } void GCIterate( const FrameIterator &it, const RootVisitor &v0, const RootRangeVisitor &v1, ChunkMap *derivedPointers, bool isVerifying) const; }; struct OptimizedBuiltinLeaveFrame { public: static OptimizedBuiltinLeaveFrame* GetFrameFromSp(const JSTaggedType *sp) { return reinterpret_cast(reinterpret_cast(sp) - MEMBER_OFFSET(OptimizedBuiltinLeaveFrame, callsiteFp)); } uintptr_t GetCallSiteSp() const { return ToUintPtr(this) + MEMBER_OFFSET(OptimizedBuiltinLeaveFrame, argRuntimeId); } inline JSTaggedType* GetPrevFrameFp() const { return reinterpret_cast(callsiteFp); } uintptr_t GetReturnAddr() const { return returnAddr; } void GCIterate( const FrameIterator &it, const RootVisitor &v0, const RootRangeVisitor &v1, ChunkMap *derivedPointers, bool isVerifying) const; private: [[maybe_unused]] FrameType type; uintptr_t callsiteFp; // thread sp set here uintptr_t returnAddr; [[maybe_unused]] uint64_t argRuntimeId; JSTaggedValue env; uint64_t argc; // argv[0]...argv[argc-1] dynamic according to agc }; struct BuiltinFrame : public base::AlignedStruct { enum class Index : size_t { TypeIndex = 0, PrevFpIndex, ReturnAddrIndex, ThreadIndex, NumArgsIndex, StackArgsIndex, NumOfMembers }; static_assert(static_cast(Index::NumOfMembers) == NumOfTypes); static BuiltinFrame* GetFrameFromSp(const JSTaggedType *sp) { return reinterpret_cast(reinterpret_cast(sp) - MEMBER_OFFSET(BuiltinFrame, prevFp)); } inline JSTaggedType* GetPrevFrameFp() { return prevFp; } uintptr_t GetCallSiteSp() const { return ToUintPtr(this) + MEMBER_OFFSET(BuiltinFrame, thread); } static size_t GetPreFpOffset(bool isArch32) { return GetOffset(Index::PrevFpIndex)>(isArch32); } static size_t GetNumArgsToFpDelta(bool isArch32) { auto offset = GetOffset(Index::NumArgsIndex)>(isArch32); return offset - GetPreFpOffset(isArch32); } static size_t GetStackArgsToFpDelta(bool isArch32) { auto offset = GetOffset(Index::StackArgsIndex)>(isArch32); return offset - GetPreFpOffset(isArch32); } uintptr_t GetStackArgsAddress() { return reinterpret_cast(&stackArgs); } JSTaggedValue GetFunction() { auto functionAddress = reinterpret_cast(GetStackArgsAddress()); return JSTaggedValue(*functionAddress); } int32_t GetNumArgs() { return numArgs; } uintptr_t GetReturnAddr() const { return returnAddr; } void GCIterate( const FrameIterator &it, const RootVisitor &v0, const RootRangeVisitor &v1, ChunkMap *derivedPointers, bool isVerifying) const; alignas(EAS) FrameType type; alignas(EAS) JSTaggedType *prevFp; alignas(EAS) uintptr_t returnAddr; alignas(EAS) uintptr_t thread; alignas(EAS) int32_t numArgs; alignas(EAS) uintptr_t stackArgs; }; struct BuiltinWithArgvFrame : public base::AlignedStruct { enum class Index : int { StackArgsTopIndex = -1, NumArgsIndex = -1, TypeIndex = 0, PrevFpIndex, ReturnAddrIndex, NumOfMembers }; static_assert(static_cast(Index::NumOfMembers) == NumOfTypes); static BuiltinWithArgvFrame* GetFrameFromSp(const JSTaggedType *sp) { return reinterpret_cast(reinterpret_cast(sp) - MEMBER_OFFSET(BuiltinFrame, prevFp)); } inline JSTaggedType* GetPrevFrameFp() { return prevFp; } uintptr_t GetCallSiteSp() const { return ToUintPtr(this) + sizeof(BuiltinWithArgvFrame); } uintptr_t GetStackArgsAddress() { auto topAddress = ToUintPtr(this) + (static_cast(Index::StackArgsTopIndex) * sizeof(uintptr_t)); auto numberArgs = GetNumArgs() + NUM_MANDATORY_JSFUNC_ARGS; return topAddress - static_cast(numberArgs) * sizeof(uintptr_t); } JSTaggedValue GetFunction() { auto functionAddress = reinterpret_cast(GetStackArgsAddress()); return JSTaggedValue(*functionAddress); } int32_t GetNumArgs() { auto argcAddress = reinterpret_cast( ToUintPtr(this) + (static_cast(Index::NumArgsIndex) * sizeof(uintptr_t))); return *argcAddress; } uintptr_t GetReturnAddr() const { return returnAddr; } void GCIterate( const FrameIterator &it, const RootVisitor &v0, const RootRangeVisitor &v1, ChunkMap *derivedPointers, bool isVerifying) const; // argv(... this, new.target, function) // numargs alignas(EAS) FrameType type; alignas(EAS) JSTaggedType *prevFp; alignas(EAS) uintptr_t returnAddr; }; class FrameIterator { public: explicit FrameIterator(JSTaggedType *sp, const JSThread *thread = nullptr); FrameType GetFrameType() const { ASSERT(current_ != nullptr); FrameType *typeAddr = reinterpret_cast( reinterpret_cast(current_) - sizeof(FrameType)); return *typeAddr; } template T* GetFrame() { return T::GetFrameFromSp(current_); } template const T* GetFrame() const { return T::GetFrameFromSp(current_); } bool Done() const { return current_ == nullptr; } JSTaggedType *GetSp() const { return current_; } JSTaggedType *GetSp() { return current_; } int ComputeDelta() const; void Advance(); uintptr_t GetPrevFrameCallSiteSp(uintptr_t curPc = 0) const; uintptr_t GetPrevFrame() const; uintptr_t GetCallSiteSp() const { return optimizedCallSiteSp_; } uintptr_t GetOptimizedReturnAddr() const { return optimizedReturnAddr_; } const JSThread *GetThread() const { return thread_; } bool CollectGCSlots(std::set &baseSet, ChunkMap *data, bool isVerifying) const; private: JSTaggedType *current_ {nullptr}; const JSThread *thread_ {nullptr}; const kungfu::LLVMStackMapParser *stackmapParser_ {nullptr}; uintptr_t optimizedCallSiteSp_ {0}; uintptr_t optimizedReturnAddr_ {0}; }; } // namespace panda::ecmascript #endif // ECMASCRIPT_FRAMES_H