• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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