1 /* 2 * Copyright (c) 2022-2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef ECMASCRIPT_DEOPTIMIZER_DEOPTIMIZER_H 17 #define ECMASCRIPT_DEOPTIMIZER_DEOPTIMIZER_H 18 19 #include "ecmascript/base/aligned_struct.h" 20 #include "ecmascript/compiler/argument_accessor.h" 21 #include "ecmascript/deoptimizer/calleeReg.h" 22 #include "ecmascript/ecma_vm.h" 23 #include "ecmascript/js_handle.h" 24 #include "ecmascript/js_tagged_value.h" 25 #include "ecmascript/stackmap/llvm/llvm_stackmap_type.h" 26 27 namespace panda::ecmascript { 28 class JSThread; 29 enum class SpecVregIndex: int { 30 INLINE_DEPTH = -1, // INLINE_DEPTH wont be decoded, and put it here to 31 // avoiding confilict with the decoding of inlined frame args below 32 PC_OFFSET_INDEX = -2, 33 ACC_INDEX = -3, 34 ENV_INDEX = -4, 35 FUNC_INDEX = -5, 36 NEWTARGET_INDEX = -6, 37 THIS_OBJECT_INDEX = -7, 38 ACTUAL_ARGC_INDEX = -8, 39 }; 40 41 struct Context { 42 uintptr_t callsiteSp; 43 uintptr_t callsiteFp; 44 kungfu::CalleeRegAndOffsetVec calleeRegAndOffset; 45 }; 46 47 // |--------------------------| --------------- 48 // | callerFp_ | ^ 49 // | returnAddr_ | | 50 // | callFrameTop_ | stackContext 51 // | inlineDepth_ | | 52 // | hasException_ | | 53 // | isFrameLazyDeopt_ | v 54 // |--------------------------| --------------- 55 struct AsmStackContext : public base::AlignedStruct<base::AlignedPointer::Size(), 56 base::AlignedPointer, 57 base::AlignedPointer, 58 base::AlignedPointer, 59 base::AlignedPointer, 60 base::AlignedBool, 61 base::AlignedBool> { 62 enum class Index : size_t { 63 INLINE_DEPTH_INDEX = 0, 64 CALLFRAME_TOP_INDEX, 65 RETURN_ADDRESS_INDEX, 66 CALLERFRAME_POINTER_INDEX, 67 HAS_EXCEPTION_INDEX, 68 IS_FRAME_LAZY_DEOPT_INDEX, 69 NUM_OF_MEMBER 70 }; 71 72 static_assert(static_cast<size_t>(Index::NUM_OF_MEMBER) == NumOfTypes); 73 GetInlineDepthOffsetAsmStackContext74 static size_t GetInlineDepthOffset(bool isArch32) 75 { 76 return GetOffset<static_cast<size_t>(Index::INLINE_DEPTH_INDEX)>(isArch32); 77 } 78 GetCallFrameTopOffsetAsmStackContext79 static size_t GetCallFrameTopOffset(bool isArch32) 80 { 81 return GetOffset<static_cast<size_t>(Index::CALLFRAME_TOP_INDEX)>(isArch32); 82 } 83 GetReturnAddressOffsetAsmStackContext84 static size_t GetReturnAddressOffset(bool isArch32) 85 { 86 return GetOffset<static_cast<size_t>(Index::RETURN_ADDRESS_INDEX)>(isArch32); 87 } 88 GetCallerFpOffsetAsmStackContext89 static size_t GetCallerFpOffset(bool isArch32) 90 { 91 return GetOffset<static_cast<size_t>(Index::CALLERFRAME_POINTER_INDEX)>(isArch32); 92 } 93 GetHasExceptionOffsetAsmStackContext94 static size_t GetHasExceptionOffset(bool isArch32) 95 { 96 return GetOffset<static_cast<size_t>(Index::HAS_EXCEPTION_INDEX)>(isArch32); 97 } 98 GetIsFrameLazyDeoptOffsetAsmStackContext99 static size_t GetIsFrameLazyDeoptOffset(bool isArch32) 100 { 101 return GetOffset<static_cast<size_t>(Index::IS_FRAME_LAZY_DEOPT_INDEX)>(isArch32); 102 } 103 GetSizeAsmStackContext104 static constexpr size_t GetSize(bool isArch32) 105 { 106 return isArch32 ? AsmStackContext::SizeArch32 : AsmStackContext::SizeArch64; 107 } 108 109 alignas(EAS) size_t inlineDepth_ {0}; 110 alignas(EAS) uintptr_t callFrameTop_{0}; 111 alignas(EAS) uintptr_t returnAddr_{0}; 112 alignas(EAS) uintptr_t callerFp_{0}; 113 alignas(EAS) bool hasException_{false}; 114 alignas(EAS) bool isFrameLazyDeopt_{false}; 115 // out put data 116 }; 117 118 class FrameWriter; 119 class Deoptimizier { 120 public: 121 using ARKDeopt = kungfu::ARKDeopt; 122 using CalleeReg = kungfu::CalleeReg; 123 using CalleeRegAndOffsetVec = kungfu::CalleeRegAndOffsetVec; 124 using CommonArgIdx = kungfu::CommonArgIdx; 125 using DeoptType = kungfu::DeoptType; 126 using DwarfRegType = kungfu::LLVMStackMapType::DwarfRegType; 127 using DwarfRegAndOffsetType = kungfu::LLVMStackMapType::DwarfRegAndOffsetType; 128 using IntType = kungfu::LLVMStackMapType::IntType; 129 using LargeInt = kungfu::LLVMStackMapType::LargeInt; 130 using LocationTy = kungfu::LocationTy; 131 using OffsetType = kungfu::LLVMStackMapType::OffsetType; 132 using VRegId = kungfu::LLVMStackMapType::VRegId; 133 134 explicit Deoptimizier(JSThread *thread, size_t depth, kungfu::DeoptType type); 135 136 void CollectVregs(const std::vector<kungfu::ARKDeopt>& deoptBundle, size_t shift); 137 template<class T> 138 void AssistCollectDeoptBundleVec(FrameIterator &it, T &frame); 139 void CollectDeoptBundleVec(std::vector<kungfu::ARKDeopt>& deoptBundle); 140 JSTaggedType ConstructAsmInterpretFrame(JSHandle<JSTaggedValue> maybeAcc); 141 void UpdateAndDumpDeoptInfo(kungfu::DeoptType type); 142 static PUBLIC_API std::string DisplayItems(kungfu::DeoptType type); 143 static PUBLIC_API int32_t EncodeDeoptVregIndex(int32_t index, size_t depth, size_t shift); 144 static PUBLIC_API size_t ComputeShift(size_t depth); 145 static int32_t DecodeVregIndex(OffsetType id, size_t shift); 146 static size_t DecodeDeoptDepth(OffsetType id, size_t shift); 147 static size_t GetInlineDepth(JSThread *thread); 148 static void ClearCompiledCodeStatusWhenDeopt(JSThread *thread, JSFunction *fun, 149 Method *method, kungfu::DeoptType type); 150 static void ReplaceReturnAddrWithLazyDeoptTrampline(JSThread *thread, uintptr_t *returnAddraddress, 151 FrameType *prevFrameTypeAddress, uintptr_t prevFrameCallSiteSp); 152 static void PrepareForLazyDeopt(JSThread *thread); 153 void ProcessLazyDeopt(JSHandle<JSTaggedValue> maybeAcc, const uint8_t* &resumePc, 154 AsmInterpretedFrame *statePtr); 155 bool NeedOverwriteAcc(const uint8_t *pc) const; GetThread()156 JSThread *GetThread() const 157 { 158 return thread_; 159 } 160 GetLLVMDeoptRelocateSymbol()161 static const char *GetLLVMDeoptRelocateSymbol() 162 { 163 return "__llvm_deoptimize"; 164 } 165 166 private: 167 static bool IsNeedLazyDeopt(const FrameIterator &it); GetFrameIndex(CommonArgIdx index)168 size_t GetFrameIndex(CommonArgIdx index) 169 { 170 return static_cast<size_t>(index) - static_cast<size_t>(CommonArgIdx::FUNC); 171 } GetFrameArgv(size_t idx)172 JSTaggedValue GetFrameArgv(size_t idx) 173 { 174 ASSERT(frameArgvs_ != nullptr); 175 ASSERT(idx < frameArgc_); 176 return JSTaggedValue(frameArgvs_[idx]); 177 } GetFrameArgv(CommonArgIdx index)178 JSTaggedValue GetFrameArgv(CommonArgIdx index) 179 { 180 return GetFrameArgv(GetFrameIndex(index)); 181 } GetActualFrameArgs(int32_t index)182 JSTaggedValue GetActualFrameArgs(int32_t index) 183 { 184 index += NUM_MANDATORY_JSFUNC_ARGS; 185 return GetFrameArgv(static_cast<size_t>(index)); 186 } 187 bool CollectVirtualRegisters(JSTaggedValue callTarget, Method *method, FrameWriter *frameWriter, size_t curDepth); HasDeoptValue(size_t curDepth,int32_t index)188 bool HasDeoptValue(size_t curDepth, int32_t index) const 189 { 190 ASSERT(curDepth <= inlineDepth_); 191 return deoptVregs_.find({curDepth, static_cast<OffsetType>(index)}) != deoptVregs_.end(); 192 } GetDeoptValue(size_t curDepth,int32_t index)193 JSTaggedValue GetDeoptValue(size_t curDepth, int32_t index) const 194 { 195 ASSERT(curDepth <= inlineDepth_); 196 if (!HasDeoptValue(curDepth, index)) { 197 return JSTaggedValue::Undefined(); 198 } 199 return deoptVregs_.at({curDepth, static_cast<OffsetType>(index)}).GetTaggedValue(); 200 } 201 Method* GetMethod(JSTaggedValue &target); 202 void RelocateCalleeSave(); 203 void Dump(JSTaggedValue callTarget, kungfu::DeoptType type, size_t depth); 204 int64_t GetCallSize(size_t curDepth, const uint8_t *resumePc); 205 static void ResetJitHotness(JSThread *thread, JSFunction *jsFunc); 206 JSThread *thread_ {nullptr}; 207 uintptr_t *calleeRegAddr_ {nullptr}; 208 size_t numCalleeRegs_ {0}; 209 AsmStackContext stackContext_; 210 211 std::map<std::pair<size_t, OffsetType>, JSHandle<JSTaggedValue>> deoptVregs_; 212 struct Context context_ {0, 0, {}}; 213 std::unordered_map<size_t, size_t> pc_; 214 std::unordered_map<size_t, size_t> jumpSize_; 215 size_t frameArgc_ {0}; 216 JSTaggedType *frameArgvs_ {nullptr}; 217 bool traceDeopt_{false}; 218 size_t inlineDepth_ {0}; 219 uint32_t type_ {static_cast<uint32_t>(DeoptType::NONE)}; 220 }; 221 222 } // namespace panda::ecmascript 223 #endif // ECMASCRIPT_DEOPTIMIZER_DEOPTIMIZER_H