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
27namespace panda::ecmascript {
28class JSThread;
29enum class SpecVregIndex: int {
30    PC_OFFSET_INDEX = -1,
31    ACC_INDEX = -2,
32    ENV_INDEX = -3,
33    FUNC_INDEX = -4,
34    NEWTARGET_INDEX = -5,
35    THIS_OBJECT_INDEX = -6,
36    ACTUAL_ARGC_INDEX = -7,
37    FIRST_METHOD_OFFSET_INDEX = -8,
38    PADDING1 = -9,
39    PADDING2 = -10,
40    PADDING3 = -11,
41    MAX_METHOD_OFFSET_INDEX = -12,
42};
43
44static constexpr uint32_t MAX_METHOD_OFFSET_NUM = static_cast<int32_t>(SpecVregIndex::FIRST_METHOD_OFFSET_INDEX) -
45                                                  static_cast<int32_t>(SpecVregIndex::MAX_METHOD_OFFSET_INDEX) + 1;
46
47struct Context {
48    uintptr_t callsiteSp;
49    uintptr_t callsiteFp;
50    kungfu::CalleeRegAndOffsetVec calleeRegAndOffset;
51};
52
53struct AsmStackContext : public base::AlignedStruct<base::AlignedPointer::Size(),
54                                                    base::AlignedPointer,
55                                                    base::AlignedPointer,
56                                                    base::AlignedPointer,
57                                                    base::AlignedPointer> {
58    enum class Index : size_t {
59        INLINE_DEPTH_INDEX = 0,
60        CALLFRAME_TOP_INDEX,
61        RETURN_ADDRESS_INDEX,
62        CALLERFRAME_POINTER_INDEX,
63        NUM_OF_MEMBER
64    };
65
66    static_assert(static_cast<size_t>(Index::NUM_OF_MEMBER) == NumOfTypes);
67
68    static size_t GetInlineDepthOffset(bool isArch32)
69    {
70        return GetOffset<static_cast<size_t>(Index::INLINE_DEPTH_INDEX)>(isArch32);
71    }
72
73    static size_t GetCallFrameTopOffset(bool isArch32)
74    {
75        return GetOffset<static_cast<size_t>(Index::CALLFRAME_TOP_INDEX)>(isArch32);
76    }
77
78    static size_t GetReturnAddressOffset(bool isArch32)
79    {
80        return GetOffset<static_cast<size_t>(Index::RETURN_ADDRESS_INDEX)>(isArch32);
81    }
82
83    static size_t GetCallerFpOffset(bool isArch32)
84    {
85        return GetOffset<static_cast<size_t>(Index::CALLERFRAME_POINTER_INDEX)>(isArch32);
86    }
87
88    static constexpr size_t GetSize(bool isArch32)
89    {
90        return isArch32 ? AsmStackContext::SizeArch32 : AsmStackContext::SizeArch64;
91    }
92
93    alignas(EAS) size_t inlineDepth_ {0};
94    alignas(EAS) uintptr_t callFrameTop_{0};
95    alignas(EAS) uintptr_t returnAddr_{0};
96    alignas(EAS) uintptr_t callerFp_{0};
97    // out put data
98};
99
100class FrameWriter;
101class Deoptimizier {
102public:
103    using ARKDeopt = kungfu::ARKDeopt;
104    using CalleeReg = kungfu::CalleeReg;
105    using CalleeRegAndOffsetVec = kungfu::CalleeRegAndOffsetVec;
106    using CommonArgIdx = kungfu::CommonArgIdx;
107    using DeoptType = kungfu::DeoptType;
108    using DwarfRegType = kungfu::LLVMStackMapType::DwarfRegType;
109    using DwarfRegAndOffsetType = kungfu::LLVMStackMapType::DwarfRegAndOffsetType;
110    using IntType = kungfu::LLVMStackMapType::IntType;
111    using LargeInt = kungfu::LLVMStackMapType::LargeInt;
112    using LocationTy = kungfu::LocationTy;
113    using OffsetType = kungfu::LLVMStackMapType::OffsetType;
114    using VRegId = kungfu::LLVMStackMapType::VRegId;
115
116    explicit Deoptimizier(JSThread *thread, size_t depth);
117
118    void CollectVregs(const std::vector<kungfu::ARKDeopt>& deoptBundle, size_t shift);
119    template<class T>
120    void AssistCollectDeoptBundleVec(FrameIterator &it, T &frame);
121    void CollectDeoptBundleVec(std::vector<kungfu::ARKDeopt>& deoptBundle);
122    JSTaggedType ConstructAsmInterpretFrame();
123    void UpdateAndDumpDeoptInfo(kungfu::DeoptType type);
124    static PUBLIC_API std::string DisplayItems(kungfu::DeoptType type);
125    static PUBLIC_API int32_t EncodeDeoptVregIndex(int32_t index, size_t depth, size_t shift);
126    static PUBLIC_API size_t ComputeShift(size_t depth);
127    static int32_t DecodeVregIndex(OffsetType id, size_t shift);
128    static size_t DecodeDeoptDepth(OffsetType id, size_t shift);
129    JSThread *GetThread() const
130    {
131        return thread_;
132    }
133
134    static const char *GetLLVMDeoptRelocateSymbol()
135    {
136        return "__llvm_deoptimize";
137    }
138
139private:
140    size_t GetFrameIndex(CommonArgIdx index)
141    {
142        return static_cast<size_t>(index) - static_cast<size_t>(CommonArgIdx::FUNC);
143    }
144    JSTaggedValue GetFrameArgv(size_t idx)
145    {
146        ASSERT(frameArgvs_ != nullptr);
147        ASSERT(idx < frameArgc_);
148        return JSTaggedValue(frameArgvs_[idx]);
149    }
150    JSTaggedValue GetFrameArgv(CommonArgIdx index)
151    {
152        return GetFrameArgv(GetFrameIndex(index));
153    }
154    JSTaggedValue GetActualFrameArgs(int32_t index)
155    {
156        index += NUM_MANDATORY_JSFUNC_ARGS;
157        return GetFrameArgv(static_cast<size_t>(index));
158    }
159    bool CollectVirtualRegisters(JSTaggedValue callTarget, Method *method, FrameWriter *frameWriter, size_t curDepth);
160    bool HasDeoptValue(size_t curDepth, int32_t index) const
161    {
162        ASSERT(curDepth <= inlineDepth_);
163        return deoptVregs_.find({curDepth, static_cast<OffsetType>(index)}) != deoptVregs_.end();
164    }
165    JSTaggedValue GetDeoptValue(size_t curDepth, int32_t index) const
166    {
167        ASSERT(curDepth <= inlineDepth_);
168        if (!HasDeoptValue(curDepth, index)) {
169            return JSTaggedValue::Undefined();
170        }
171        return deoptVregs_.at({curDepth, static_cast<OffsetType>(index)}).GetTaggedValue();
172    }
173    Method* GetMethod(JSTaggedValue &target);
174    void RelocateCalleeSave();
175    void Dump(JSTaggedValue callTarget, kungfu::DeoptType type, size_t depth);
176    size_t GetCallSize(size_t curDepth, const uint8_t *resumePc);
177    void ClearCompiledCodeStatusWhenDeopt(JSFunction *fun, Method *method);
178    void ResetJitHotness(JSFunction *jsFunc) const;
179    JSThread *thread_ {nullptr};
180    uintptr_t *calleeRegAddr_ {nullptr};
181    size_t numCalleeRegs_ {0};
182    AsmStackContext stackContext_;
183
184    std::map<std::pair<size_t, OffsetType>, JSHandle<JSTaggedValue>> deoptVregs_;
185    struct Context context_ {0, 0, {}};
186    std::unordered_map<size_t, size_t> pc_;
187    std::unordered_map<size_t, size_t> jumpSize_;
188    size_t frameArgc_ {0};
189    JSTaggedType *frameArgvs_ {nullptr};
190    bool traceDeopt_{false};
191    size_t inlineDepth_ {0};
192};
193
194}  // namespace panda::ecmascript
195#endif // ECMASCRIPT_DEOPTIMIZER_DEOPTIMIZER_H