/* * 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/base/aligned_struct.h" #include "ecmascript/js_tagged_value.h" #include "ecmascript/mem/visitor.h" #include "ecmascript/method.h" #include "ecmascript/stackmap/ark_stackmap.h" #include "ecmascript/stackmap/llvm_stackmap_type.h" namespace panda::ecmascript { class JSThread; class EcmaVM; class FrameIterator; namespace kungfu { class ArkStackMapParser; }; // Here list all scenarios of calling between Runtime/CInterpreter/ASMInterpreter/AOTCompiler/CBuiltin/ASMBuitlin. // Please note that the "[]" means a must frame while "<>" means an optional frame. Each case is from top to down. // // * Runtime (C++) => CInterpreter: // 1) [INTERPRETER_FRAME] // // * Runtime (C++) -> AOTCompiler: // 1) [OPTIMIZED_ENTRY_FRAME] // // // [OPTIMIZED_JS_FUNCTION_FRAME] // // * Runtime (C++) => ASMInterpreter: // 1) [INTERPRETER_ENTRY_FRAME][ASM_INTERPRETER_FRAME] // // * Runtime (C++) => CBuiltin: // 1) [not supported] // // * Runtime (C++) => ASMBuiltin: // 1) [not supported] // // * CInterpreter => CInterpreter: // 1) [INTERPRETER_FRAME] // // * CInterpreter => Runtime (C++): // 1) [INTERPRETER_FAST_NEW_FRAME] // 2) [INTERPRETER_CONSTRUCTOR_FRAME] // // * CInterpreter => AOTCompiler: // 1) [not supported] // // * CInterperter => CBuiltin: // 1) [INTERPRETER_BUILTIN_FRAME] // // * CInterpreter => ASMBuiltin: // 1) [not supported] // // * ASMInterpreter => Runtime (C++): // 1) [LEAVE_FRAME] // 2) [LEAVE_FRAME_WITH_ARGV] // // * ASMInterpreter => AOTCompiler: // 1) [OPTIMIZED_ENTRY_FRAME] // // // [OPTIMIZED_JS_FUNCTION_FRAME] // // * ASMInterpreter => ASMInterpreter: // 1) [ASM_INTERPRETER_FRAME] // // * ASMInterpreter => AsmBuiltin: // 1) [BUILTIN_ENTRY_FRAME] // [BUILTIN_FRAME] // 2) [BUILTIN_ENTRY_FRAME] // [BUILTIN_FRAME_WITH_ARGV] // // * ASMInterpreter => CBuiltin: // 1) [LEAVE_FRAME] // 2) [LEAVE_FRAME_WITH_ARGV] // // * AOTCompiler => Runtime (C++): // 1) [LEAVE_FRAME] // 2) [LEAVE_FRAME_WITH_ARGV] // // * AOTCompiler => ASMInterpreter: // 1) [ASM_INTERPRETER_BRIDGE_FRAME] // 2) [ASM_INTERPRETER_FRAME] // // * AOTCompiler => CBuiltin: // 1) [LEAVE_FRAME] // 2) [LEAVE_FRAME_WITH_ARGV] // // * AOTCompiler => ASMBuiltin: // 1) [BUILTIN_ENTRY_FRAME] // [BUILTIN_FRAME] // 2) [BUILTIN_ENTRY_FRAME] // [BUILTIN_FRAME_WITH_ARGV] enum class FrameType: uintptr_t { OPTIMIZED_FRAME = 0, OPTIMIZED_ENTRY_FRAME, OPTIMIZED_JS_FUNCTION_FRAME, OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME, ASM_BRIDGE_FRAME, LEAVE_FRAME, LEAVE_FRAME_WITH_ARGV, BUILTIN_CALL_LEAVE_FRAME, INTERPRETER_FRAME, ASM_INTERPRETER_FRAME, INTERPRETER_CONSTRUCTOR_FRAME, BUILTIN_FRAME, BUILTIN_FRAME_WITH_ARGV, BUILTIN_ENTRY_FRAME, INTERPRETER_BUILTIN_FRAME, INTERPRETER_FAST_NEW_FRAME, INTERPRETER_ENTRY_FRAME, ASM_INTERPRETER_ENTRY_FRAME, ASM_INTERPRETER_BRIDGE_FRAME, OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME, OPTIMIZED_JS_FUNCTION_UNFOLD_ARGV_FRAME, BUILTIN_FRAME_WITH_ARGV_STACK_OVER_FLOW_FRAME, FRAME_TYPE_FIRST = OPTIMIZED_FRAME, FRAME_TYPE_LAST = OPTIMIZED_JS_FUNCTION_UNFOLD_ARGV_FRAME, INTERPRETER_FIRST = INTERPRETER_FRAME, INTERPRETER_LAST = INTERPRETER_FAST_NEW_FRAME, BUILTIN_FIRST = BUILTIN_FRAME, BUILTIN_LAST = BUILTIN_ENTRY_FRAME, }; enum class JSCallMode : uintptr_t { CALL_ARG0 = 0, CALL_ARG1, CALL_ARG2, CALL_ARG3, CALL_THIS_ARG0, CALL_THIS_ARG1, CALL_THIS_ARG2, CALL_THIS_ARG3, CALL_WITH_ARGV, CALL_THIS_WITH_ARGV, CALL_CONSTRUCTOR_WITH_ARGV, DEPRECATED_CALL_ARG0, DEPRECATED_CALL_ARG1, DEPRECATED_CALL_ARG2, DEPRECATED_CALL_ARG3, DEPRECATED_CALL_WITH_ARGV, DEPRECATED_CALL_THIS_WITH_ARGV, DEPRECATED_CALL_CONSTRUCTOR_WITH_ARGV, CALL_GETTER, CALL_SETTER, CALL_THIS_ARG3_WITH_RETURN, CALL_THIS_ARGV_WITH_RETURN, 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 &visitor, const RootRangeVisitor &rangeVisitor, const RootBaseAndDerivedVisitor &derivedVisitor) const; static size_t GetTypeOffset(bool isArch32 = false) { return GetOffset(Index::TypeIndex)>(isArch32); } static size_t GetPrevOffset(bool isArch32 = false) { return GetOffset(Index::PrevFpIndex)>(isArch32); } static size_t ComputeReservedSize(size_t slotSize) { size_t slotOffset = static_cast(Index::PrevFpIndex) - static_cast(Index::TypeIndex); return slotSize * slotOffset; } FrameType GetType() const { return type; } 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; } 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); struct AsmBridgeFrame : public base::AlignedStruct { public: static size_t GetTypeOffset(bool isArch32 = false) { return GetOffset(Index::TypeIndex)>(isArch32); } static size_t GetPrevOffset(bool isArch32 = false) { return GetOffset(Index::PrevFpIndex)>(isArch32); } uintptr_t GetCallSiteSp() const { return ToUintPtr(this) + sizeof(AsmBridgeFrame); } FrameType GetType() const { return type; } private: enum class Index : size_t { TypeIndex = 0, PrevFpIndex, ReturnAddrIndex, NumOfMembers }; static_assert(static_cast(Index::NumOfMembers) == NumOfTypes); static AsmBridgeFrame* GetFrameFromSp(const JSTaggedType *sp) { return reinterpret_cast(reinterpret_cast(sp) - MEMBER_OFFSET(AsmBridgeFrame, prevFp)); } inline JSTaggedType* GetPrevFrameFp() { return prevFp; } uintptr_t GetReturnAddr() const { return returnAddr; } alignas(EAS) FrameType type {0}; alignas(EAS) JSTaggedType *prevFp {nullptr}; alignas(EAS) uintptr_t returnAddr {0}; friend class FrameIterator; }; STATIC_ASSERT_EQ_ARCH(sizeof(AsmBridgeFrame), AsmBridgeFrame::SizeArch32, AsmBridgeFrame::SizeArch64); // * OptimizedUnfoldArgVFrame layout description as the following: // sp ----> |--------------------------| --------------- // | returnAddr | ^ // currentFp--> |--------------------------| | // | prevFp | | // |--------------------------| OptimizedUnfoldArgVFrame // | frameType | | // |--------------------------| | // | currentFp | v // +--------------------------+ --------------- // // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) struct OptimizedJSFunctionUnfoldArgVFrame : public base::AlignedStruct { public: static size_t GetTypeOffset(bool isArch32 = false) { return GetOffset(Index::TypeIndex)>(isArch32); } static size_t GetPrevOffset(bool isArch32 = false) { return GetOffset(Index::PrevFpIndex)>(isArch32); } FrameType GetType() const { return type; } private: enum class Index : size_t { CallSiteSpIndex = 0, TypeIndex, PrevFpIndex, ReturnAddrIndex, NumOfMembers }; static_assert(static_cast(Index::NumOfMembers) == NumOfTypes); static OptimizedJSFunctionUnfoldArgVFrame* GetFrameFromSp(const JSTaggedType *sp) { return reinterpret_cast(reinterpret_cast(sp) - MEMBER_OFFSET(OptimizedJSFunctionUnfoldArgVFrame, prevFp)); } inline JSTaggedType* GetPrevFrameFp() const { return prevFp; } uintptr_t GetReturnAddr() const { return returnAddr; } uintptr_t GetPrevFrameSp() const { return callSiteSp; } alignas(EAS) uintptr_t callSiteSp {0}; alignas(EAS) FrameType type {0}; alignas(EAS) JSTaggedType *prevFp {nullptr}; alignas(EAS) uintptr_t returnAddr {0}; friend class FrameIterator; }; STATIC_ASSERT_EQ_ARCH(sizeof(OptimizedJSFunctionUnfoldArgVFrame), OptimizedJSFunctionUnfoldArgVFrame::SizeArch32, OptimizedJSFunctionUnfoldArgVFrame::SizeArch64); // * The OptimizedJSFunctionArgsConfig Frame's structure is illustrated as the following: // +--------------------------+ // | arg[N-1] | // +--------------------------+ // | . . . . | // +--------------------------+ // | arg[0] | // +--------------------------+ // | argC | // sp ---> +--------------------------+ ----------------- // | | ^ // | prevFP | | // |--------------------------| OptimizedJSFunctionArgsConfigFrame // | frameType | | // | | V // +--------------------------+ ----------------- // // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) struct OptimizedJSFunctionArgConfigFrame : public base::AlignedStruct { public: static size_t GetTypeOffset(bool isArch32 = false) { return GetOffset(Index::TypeIndex)>(isArch32); } static size_t GetPrevOffset(bool isArch32 = false) { return GetOffset(Index::PrevFpIndex)>(isArch32); } FrameType GetType() const { return type; } 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; } alignas(EAS) FrameType type {0}; alignas(EAS) JSTaggedType *prevFp {nullptr}; friend class FrameIterator; }; STATIC_ASSERT_EQ_ARCH(sizeof(OptimizedJSFunctionArgConfigFrame), OptimizedJSFunctionArgConfigFrame::SizeArch32, OptimizedJSFunctionArgConfigFrame::SizeArch64); // * OptimizedJSFunctionFrame layout description as the following: // +--------------------------+ // | arg[N-1] | // +--------------------------+ // | ... | // +--------------------------+ // | arg[1] | // +--------------------------+ // | arg[0] | // +--------------------------+ // | this | // +--------------------------+ // | new-target | // +--------------------------+ // | call-target | // |--------------------------| // | argc | // sp ----> |--------------------------| --------------- // | returnAddr | ^ // |--------------------------| | // | callsiteFp | | // |--------------------------| OptimizedJSFunctionFrame // | frameType | | // |--------------------------| | // | call-target | v // +--------------------------+ --------------- // // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) struct OptimizedJSFunctionFrame : public base::AlignedStruct { public: using ConstInfo = kungfu::LLVMStackMapType::ConstInfo; enum class Index : size_t { JSFuncIndex = 0, TypeIndex, PrevFpIndex, ReturnAddrIndex, NumOfMembers }; static_assert(static_cast(Index::NumOfMembers) == NumOfTypes); static constexpr size_t GetFunctionDeltaReturnAddr() { return static_cast(Index::ReturnAddrIndex) - static_cast(Index::JSFuncIndex); } inline JSTaggedType* GetPrevFrameFp() { return prevFp; } JSTaggedType* GetArgv(uintptr_t *preFrameSp) const { return reinterpret_cast(preFrameSp + sizeof(uint64_t) / sizeof(uintptr_t)); } size_t GetArgc(uintptr_t *preFrameSp) const { return *preFrameSp; } JSTaggedType* GetArgv(const FrameIterator &it) const; uintptr_t GetReturnAddr() const { return returnAddr; } void GCIterate(const FrameIterator &it, const RootVisitor &visitor, const RootRangeVisitor &rangeVisitor, const RootBaseAndDerivedVisitor &derivedVisitor, FrameType frameType) const; void CollectPcOffsetInfo(const FrameIterator &it, ConstInfo &info) const; inline JSTaggedValue GetFunction() const { return jsFunc; } static uintptr_t ComputeArgsConfigFrameSp(JSTaggedType *fp) { const size_t offset = 2; // 2: skip prevFp and return address. return reinterpret_cast(fp) + offset * sizeof(uintptr_t); } static size_t GetTypeOffset(bool isArch32 = false) { return GetOffset(Index::TypeIndex)>(isArch32); } static size_t GetPrevOffset(bool isArch32 = false) { return GetOffset(Index::PrevFpIndex)>(isArch32); } static size_t ComputeReservedJSFuncOffset(size_t slotSize) { size_t slotOffset = static_cast(Index::PrevFpIndex) - static_cast(Index::JSFuncIndex); return slotSize * slotOffset; } FrameType GetType() const { return type; } friend class FrameIterator; friend class FrameHandler; void GetDeoptBundleInfo(const FrameIterator &it, std::vector& deopts) const; void GetFuncCalleeRegAndOffset( const FrameIterator &it, kungfu::CalleeRegAndOffsetVec &ret) const; uintptr_t* ComputePrevFrameSp(const FrameIterator &it) const; 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 jsFunc {JSTaggedValue::Undefined()}; alignas(EAS) FrameType type {0}; alignas(EAS) JSTaggedType *prevFp {nullptr}; alignas(EAS) uintptr_t returnAddr {0}; // dynamic callee saveregisters for arm64 }; STATIC_ASSERT_EQ_ARCH(sizeof(OptimizedJSFunctionFrame), OptimizedJSFunctionFrame::SizeArch32, OptimizedJSFunctionFrame::SizeArch64); // 2: return addr & prevFp, type and js function should be pairs to update type and js function at the same time. static_assert((OptimizedJSFunctionFrame::GetFunctionDeltaReturnAddr() % 2) == 1); // * The JSFunctionEntry Frame's structure is illustrated as the following: // +--------------------------+ // | . . . . . . | // sp ---> +--------------------------+ ----------------- // | prevFP | ^ // |--------------------------| | // | frameType | JSFunctionEntryFrame // |--------------------------| | // | preLeaveFrameFp | v // +--------------------------+ ----------------- struct OptimizedEntryFrame : public base::AlignedStruct { public: enum class CallType : size_t { CALL_FUNC = 0, CALL_NEW, }; enum class Index : size_t { PreLeaveFrameFpIndex = 0, TypeIndex, PrevFpIndex, NumOfMembers }; static size_t GetTypeOffset(bool isArch32 = false) { return GetOffset(Index::TypeIndex)>(isArch32); } static size_t GetLeaveFrameFpOffset(bool isArch32 = false) { return GetOffset(Index::PreLeaveFrameFpIndex)>(isArch32); } inline JSTaggedType* GetPrevFrameFp() { return preLeaveFrameFp; } static size_t ComputeReservedSize(size_t slotSize) { size_t slotOffset = static_cast(Index::PrevFpIndex) - static_cast(Index::PreLeaveFrameFpIndex); return slotSize * slotOffset; } FrameType GetType() const { return type; } friend class FrameIterator; private: static OptimizedEntryFrame* GetFrameFromSp(const JSTaggedType *sp) { return reinterpret_cast(reinterpret_cast(sp) - MEMBER_OFFSET(OptimizedEntryFrame, prevFp)); } JSTaggedType* GetLeaveFp() const { return preLeaveFrameFp; } JSTaggedType* GetPrevFp() const { return prevFp; } alignas(EAS) JSTaggedType *preLeaveFrameFp {nullptr}; alignas(EAS) FrameType type {0}; alignas(EAS) JSTaggedType *prevFp {nullptr}; }; STATIC_ASSERT_EQ_ARCH(sizeof(OptimizedEntryFrame), OptimizedEntryFrame::SizeArch32, OptimizedEntryFrame::SizeArch64); // 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 = false) { return GetOffset(Index::PrevIndex)>(isArch32); } static size_t GetTypeOffset(bool isArch32 = false) { return GetOffset(Index::TypeIndex)>(isArch32); } static constexpr size_t GetSize(bool isArch32) { return isArch32 ? InterpretedFrameBase::SizeArch32 : InterpretedFrameBase::SizeArch64; } 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); // Interpreter Frame Layout as the following: // +----------------------------------+ // | argv[n-1] | // |----------------------------------| // | ...... | // |----------------------------------| // | thisArg [maybe not exist] | // |----------------------------------| // | newTarget [maybe not exist] | // |----------------------------------| // | ...... | // |----------------------------------| // | Vregs [not exist in native] | // +----------------------------------+--------+ // | base.frameType | ^ // |----------------------------------| | // | base.prev(prev stack pointer) | | // |----------------------------------| | // | pc(bytecode addr) | | // |----------------------------------| | // | sp(current stack pointer) | | // |----------------------------------| | // | env | | // |----------------------------------| | // | acc | | // |----------------------------------| InterpretedFrame // | profileTypeInfo | | // |----------------------------------| | // | thisObj | | // |----------------------------------| | // | function | | // |----------------------------------| | // | constpool | v // +----------------------------------+--------+ // // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) struct InterpretedFrame : public base::AlignedStruct { public: enum class Index : size_t { ConstPoolIndex = 0, FunctionIndex, ThisObjIndex, 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(); } static size_t GetTypeOffset(bool isArch32 = false) { return GetOffset(Index::BaseIndex)>(isArch32) + InterpretedFrameBase::GetTypeOffset(isArch32); } static size_t GetPrevOffset(bool isArch32 = false) { return GetOffset(Index::BaseIndex)>(isArch32) + InterpretedFrameBase::GetPrevOffset(isArch32); } void GCIterate(const FrameIterator &it, const RootVisitor &visitor, const RootRangeVisitor &rangeVisitor) const; alignas(EAS) JSTaggedValue constpool {JSTaggedValue::Hole()}; alignas(EAS) JSTaggedValue function {JSTaggedValue::Hole()}; alignas(EAS) JSTaggedValue thisObj {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); // * InterpretedBuiltinFrame layout description as the following: // |--------------------------| --------------- // | . . . . . | ^ // | InterpretedFrameBase | | // | . . . . . | | // |--------------------------| InterpretedBuiltinFrame // | bytecode-PC | | // |--------------------------| | // | call-target | v // +--------------------------+ --------------- // // 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(); } static size_t GetTypeOffset(bool isArch32 = false) { return GetOffset(Index::BaseIndex)>(isArch32) + InterpretedFrameBase::GetTypeOffset(isArch32); } static size_t GetPrevOffset(bool isArch32 = false) { return GetOffset(Index::BaseIndex)>(isArch32) + InterpretedFrameBase::GetPrevOffset(isArch32); } void GCIterate(const FrameIterator &it, const RootVisitor &visitor, const RootRangeVisitor &rangeVisitor) 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); // AsmInterpretedFrame Layout as the following: // +----------------------------------+ // | argv[n-1] | // |----------------------------------| // | ...... | // |----------------------------------| // | thisArg [maybe not exist] | // |----------------------------------| // | newTarget [maybe not exist] | // |----------------------------------| // | ...... | // |----------------------------------| // | Vregs [not exist in native] | // +----------------------------------+--------+ // | . . . . | ^ // | InterpretedFrameBase | | // | . . . . | | // |----------------------------------| | // | pc(bytecode addr) | | // |----------------------------------| | // | sp(current stack pointer) | | // |----------------------------------| AsmInterpretedFrame // | callSize | | // |----------------------------------| | // | env | | // |----------------------------------| | // | acc | | // |----------------------------------| | // | thisObj | | // |----------------------------------| | // | call-target | v // +----------------------------------+--------+ // // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) struct AsmInterpretedFrame : public base::AlignedStruct { enum class Index : size_t { FunctionIndex = 0, ThisObjIndex, 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 GetThisOffset(bool isArch32) { return GetOffset(Index::ThisObjIndex)>(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 &visitor, const RootRangeVisitor &rangeVisitor, const RootBaseAndDerivedVisitor &derivedVisitor) const; JSTaggedValue GetEnv() const { return env; } const uint8_t *GetPc() const { return pc; } static size_t GetTypeOffset() { return MEMBER_OFFSET(AsmInterpretedFrame, base) + MEMBER_OFFSET(InterpretedFrameBase, type); } static size_t GetPrevOffset() { return MEMBER_OFFSET(AsmInterpretedFrame, base) + MEMBER_OFFSET(InterpretedFrameBase, prev); } alignas(EAS) JSTaggedValue function {JSTaggedValue::Hole()}; alignas(EAS) JSTaggedValue thisObj {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 }; STATIC_ASSERT_EQ_ARCH(sizeof(AsmInterpretedFrame), AsmInterpretedFrame::SizeArch32, AsmInterpretedFrame::SizeArch64); // InterpretedEntryFrame Layout as the following: // +----------------------------------+--------------- // | . . . . | ^ // | InterpretedFrameBase | | // | . . . . | InterpretedEntryFrame // |----------------------------------| | // | pc(bytecode addr) | v // |----------------------------------|--------------- // // 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(); } static size_t GetTypeOffset(bool isArch32 = false) { return GetOffset(Index::BaseIndex)>(isArch32) + InterpretedFrameBase::GetTypeOffset(isArch32); } static size_t GetPrevOffset(bool isArch32 = false) { return GetOffset(Index::BaseIndex)>(isArch32) + InterpretedFrameBase::GetPrevOffset(isArch32); } void GCIterate(const FrameIterator &it, const RootVisitor &visitor, const RootRangeVisitor &rangeVisitor) const; alignas(EAS) const uint8_t *pc {nullptr}; alignas(EAS) InterpretedFrameBase base; }; STATIC_ASSERT_EQ_ARCH(sizeof(InterpretedEntryFrame), InterpretedEntryFrame::SizeArch32, InterpretedEntryFrame::SizeArch64); // AsmInterpretedEntryFrame Layout as the following: // +----------------------------------+--------------- // | . . . . | ^ // | InterpretedFrameBase | | // | . . . . | AsmInterpretedEntryFrame // |----------------------------------| | // | pc(bytecode addr) | v // |----------------------------------|--------------- // // 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 size_t GetBaseOffset(bool isArch32) { return GetOffset(Index::BaseIndex)>(isArch32); } static AsmInterpretedEntryFrame* GetFrameFromSp(const JSTaggedType *sp) { return reinterpret_cast(const_cast(sp)) - 1; } static size_t GetTypeOffset(bool isArch32 = false) { return GetOffset(Index::BaseIndex)>(isArch32) + InterpretedFrameBase::GetTypeOffset(isArch32); } static size_t GetPrevOffset(bool isArch32 = false) { return GetOffset(Index::BaseIndex)>(isArch32) + InterpretedFrameBase::GetPrevOffset(isArch32); } alignas(EAS) const uint8_t *pc {nullptr}; alignas(EAS) InterpretedFrameBase base; }; // AsmInterpretedBridgeFrame Layout as the following: // +----------------------------------+--------------- // | ret-address | ^ // |----------------------------------| | // | . . . . | AsmInterpretedBridgeFrame // | AsmInterpretedEntryFrame | | // | . . . . | v // |----------------------------------|--------------- // // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) 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; } static size_t GetReturnAddrOffset(bool isArch32) { return GetOffset(Index::ReturnAddrIndex)>(isArch32); } static constexpr size_t GetSize(bool isArch32) { return isArch32 ? AsmInterpretedBridgeFrame::SizeArch32 : AsmInterpretedBridgeFrame::SizeArch64; } static size_t GetTypeOffset(bool isArch32 = false) { return GetOffset(Index::EntryIndex)>(isArch32) + AsmInterpretedEntryFrame::GetBaseOffset(isArch32) + InterpretedFrameBase::GetTypeOffset(isArch32); } static size_t GetPrevOffset(bool isArch32 = false) { return GetOffset(Index::EntryIndex)>(isArch32) + AsmInterpretedEntryFrame::GetBaseOffset(isArch32) + InterpretedFrameBase::GetPrevOffset(isArch32); } uintptr_t GetReturnAddr() const { return returnAddr; } AsmInterpretedEntryFrame entry; alignas(EAS) uintptr_t returnAddr; }; // * Optimized-leaved-frame layout as the following: // +--------------------------+ // | argv[N-1] | // |--------------------------| // | . . . . . | // |--------------------------| // | argv[0] | // +--------------------------+------------- // | argc | ^ // |--------------------------| | // | RuntimeId | | // sp --> |--------------------------| OptimizedLeaveFrame // | ret-addr | | // |--------------------------| | // | prevFp | | // |--------------------------| | // | frameType | v // +--------------------------+------------- // // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) struct OptimizedLeaveFrame { FrameType type; uintptr_t callsiteFp; // thread sp set here uintptr_t returnAddr; uint64_t argRuntimeId; 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 { return ToUintPtr(this) + MEMBER_OFFSET(OptimizedLeaveFrame, argRuntimeId); } inline JSTaggedType* GetPrevFrameFp() const { return reinterpret_cast(callsiteFp); } uintptr_t GetReturnAddr() const { return returnAddr; } static size_t GetTypeOffset() { return MEMBER_OFFSET(OptimizedLeaveFrame, type); } static size_t GetPrevOffset() { return MEMBER_OFFSET(OptimizedLeaveFrame, callsiteFp); } void GCIterate(const FrameIterator &it, const RootVisitor &visitor, const RootRangeVisitor &rangeVisitor) const; }; // * Optimized-leaved-frame-with-argv layout as the following: // +--------------------------+ // | argv[] | // +--------------------------+------------- // | argc | ^ // |--------------------------| | // | RuntimeId | OptimizedWithArgvLeaveFrame // sp --> |--------------------------| | // | returnAddr | | // |--------------------------| | // | callsiteFp | | // |--------------------------| | // | frameType | v // +--------------------------+------------- struct OptimizedWithArgvLeaveFrame { FrameType type; uintptr_t callsiteFp; // thread sp set here uintptr_t returnAddr; uint64_t argRuntimeId; uint64_t argc; static OptimizedWithArgvLeaveFrame* GetFrameFromSp(const JSTaggedType *sp) { return reinterpret_cast(reinterpret_cast(sp) - MEMBER_OFFSET(OptimizedWithArgvLeaveFrame, callsiteFp)); } uintptr_t GetCallSiteSp() const { return ToUintPtr(this) + MEMBER_OFFSET(OptimizedWithArgvLeaveFrame, argRuntimeId); } inline JSTaggedType* GetPrevFrameFp() { return reinterpret_cast(callsiteFp); } uintptr_t GetReturnAddr() const { return returnAddr; } static size_t GetTypeOffset() { return MEMBER_OFFSET(OptimizedWithArgvLeaveFrame, type); } static size_t GetPrevOffset() { return MEMBER_OFFSET(OptimizedWithArgvLeaveFrame, callsiteFp); } void GCIterate(const FrameIterator &it, const RootVisitor &visitor, const RootRangeVisitor &rangeVisitor) const; }; // * OptimizedBuiltinLeaveFrame layout as the following: // +--------------------------+ // | argv[N-1] | // |--------------------------| // | . . . . . | // |--------------------------| // | argv[0] | // +--------------------------+------------- // | argc | ^ // |--------------------------| | // | thread | | // +--------------------------+ | // | ret-addr | | // sp --> |--------------------------| OptimizedBuiltinLeaveFrame // | prevFp | | // |--------------------------| | // | frameType | | // |--------------------------| | // | align byte | v // +--------------------------+------------- // // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) 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, argc); } inline JSTaggedType* GetPrevFrameFp() const { return reinterpret_cast(callsiteFp); } uintptr_t GetReturnAddr() const { return returnAddr; } void GCIterate(const FrameIterator &it, const RootVisitor &visitor, const RootRangeVisitor &rangeVisitor) const; static size_t GetTypeOffset() { return MEMBER_OFFSET(OptimizedBuiltinLeaveFrame, type); } static size_t GetPrevOffset() { return MEMBER_OFFSET(OptimizedBuiltinLeaveFrame, callsiteFp); } const JSTaggedType* GetArgv() const { return reinterpret_cast(&argc + 1); } FrameType GetType() const { return type; } private: FrameType type; uintptr_t callsiteFp; // thread sp set here uintptr_t returnAddr; JSTaggedValue thread; uint64_t argc; // argv[0]...argv[argc-1] dynamic according to agc }; // * BuiltinFrame layout as the following: // +--------------------------+ // | argV[N - 1] | // |--------------------------| // | . . . . | // |--------------------------+ // | argV[2]=this | // +--------------------------+ // | argV[1]=new-target | // +--------------------------+ // | argV[0]=call-target | // +--------------------------+ --------- // | argc | ^ // |--------------------------| | // | thread | | // |--------------------------| | // | returnAddr | BuiltinFrame // |--------------------------| | // | callsiteFp | | // |--------------------------| | // | frameType | v // +--------------------------+ --------- // // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) 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); } uint32_t GetNumArgs() { return numArgs; } uintptr_t GetReturnAddr() const { return returnAddr; } static size_t GetTypeOffset(bool isArch32 = false) { return GetOffset(Index::TypeIndex)>(isArch32); } static size_t GetPrevOffset(bool isArch32 = false) { return GetOffset(Index::PrevFpIndex)>(isArch32); } void GCIterate(const FrameIterator &it, const RootVisitor &visitor, const RootRangeVisitor &rangeVisitor) const; alignas(EAS) FrameType type; alignas(EAS) JSTaggedType *prevFp; alignas(EAS) uintptr_t returnAddr; alignas(EAS) uintptr_t thread; alignas(EAS) uint32_t numArgs; alignas(EAS) uintptr_t stackArgs; }; // * BuiltinWithArgvFrame layout as the following: // +--------------------------+ --------- // | . . . . . | ^ // |--------------------------| | // | returnAddr | | // |--------------------------| | // | callsiteFp | BuiltinWithArgvFrame // |--------------------------| | // | frameType | | // +--------------------------+ | // | argc | v // +--------------------------+ --------- // | argV[0] | // +--------------------------+ // | argV[1] | // +--------------------------+ // | . . . . | // +--------------------------+ // // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) 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; } static size_t GetTypeOffset(bool isArch32 = false) { return GetOffset(Index::TypeIndex)>(isArch32); } static size_t GetPrevOffset(bool isArch32 = false) { return GetOffset(Index::PrevFpIndex)>(isArch32); } void GCIterate(const FrameIterator &it, const RootVisitor &visitor, const RootRangeVisitor &rangeVisitor) const; // argv(... this, new.target, function) // numargs alignas(EAS) FrameType type; alignas(EAS) JSTaggedType *prevFp; alignas(EAS) uintptr_t returnAddr; }; enum class GCVisitedFlag : bool { VISITED = true, IGNORED = false, HYBRID_STACK = true, }; class FrameIterator { public: using ConstInfo = kungfu::LLVMStackMapType::ConstInfo; 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_; } void GetCalleeRegAndOffsetVec(kungfu::CalleeRegAndOffsetVec &ret) const { ret = calleeRegInfo_; } int ComputeDelta() const; template void Advance(); uint32_t GetBytecodeOffset() const; uintptr_t GetPrevFrameCallSiteSp() const; uintptr_t GetPrevFrame() const; uintptr_t GetCallSiteSp() const { return optimizedCallSiteSp_; } uintptr_t GetOptimizedReturnAddr() const { return optimizedReturnAddr_; } const JSThread *GetThread() const { return thread_; } bool IteratorStackMap(const RootVisitor &visitor, const RootBaseAndDerivedVisitor &derivedVisitor) const; void CollectPcOffsetInfo(ConstInfo &info) const; void CollectArkDeopt(std::vector& deopts) const; std::tuple CalCallSiteInfo(uintptr_t retAddr) const; int GetCallSiteDelta(uintptr_t retAddr) const; Method *CheckAndGetMethod() const; JSTaggedValue GetFunction() const; bool IsLeaveFrame() const { FrameType type = GetFrameType(); return (type == FrameType::LEAVE_FRAME) || (type == FrameType::LEAVE_FRAME_WITH_ARGV); } bool IsOptimizedFrame() const { FrameType type = GetFrameType(); return (type == FrameType::OPTIMIZED_FRAME); } bool IsInterpretedFrame(FrameType type) const { return (type >= FrameType::INTERPRETER_FIRST) && (type <= FrameType::INTERPRETER_LAST); } bool IsJSFrame() const { FrameType type = GetFrameType(); return IsInterpretedFrame(type) || IsOptimizedJSFunctionFrame(type); } bool IsOptimizedJSFunctionFrame(FrameType type) const { return type == FrameType::OPTIMIZED_JS_FUNCTION_FRAME || type == FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME; } bool IsOptimizedJSFunctionFrame() const { FrameType type = GetFrameType(); return IsOptimizedJSFunctionFrame(type); } private: JSTaggedType *current_ {nullptr}; const JSThread *thread_ {nullptr}; const kungfu::ArkStackMapParser *arkStackMapParser_ {nullptr}; uintptr_t optimizedCallSiteSp_ {0}; uintptr_t optimizedReturnAddr_ {0}; uint8_t *stackMapAddr_ {nullptr}; int fpDeltaPrevFrameSp_ {0}; kungfu::CalleeRegAndOffsetVec calleeRegInfo_; }; } // namespace panda::ecmascript #endif // ECMASCRIPT_FRAMES_H