1/*
2 * Copyright (c) 2023 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_JIT_H
17#define ECMASCRIPT_JIT_H
18
19#include "ecmascript/common.h"
20#include "ecmascript/compiler/compilation_env.h"
21#include "ecmascript/platform/mutex.h"
22#include "ecmascript/ecma_vm.h"
23#include "ecmascript/mem/clock_scope.h"
24#include "ecmascript/mem/machine_code.h"
25#include "ecmascript/compiler/compiler_log.h"
26#include "ecmascript/jit/jit_thread.h"
27#include "ecmascript/jit/jit_dfx.h"
28
29namespace panda::ecmascript {
30class JitTask;
31enum JitCompileMode {
32    SYNC = 0,
33    ASYNC
34};
35
36enum class CompilerTier : uint8_t {
37    BASELINE,
38    FAST,
39};
40
41struct ThreadTaskInfo {
42    std::deque<std::shared_ptr<JitTask>> installJitTasks_;
43    bool skipInstallTask_ { false };
44
45    std::atomic<uint32_t> jitTaskCnt_;
46    ConditionVariable jitTaskCntCv_;
47};
48
49class Jit {
50public:
51    Jit() {}
52    ~Jit();
53    static PUBLIC_API Jit *GetInstance();
54    void SetJitEnablePostFork(EcmaVM *vm, const std::string &bundleName);
55    void ConfigJit(EcmaVM *vm);
56    void SwitchProfileStubs(EcmaVM *vm);
57    void ConfigOptions(EcmaVM *vm) const;
58    void ConfigJitFortOptions(EcmaVM *vm);
59    void SetEnableOrDisable(const JSRuntimeOptions &options, bool isEnableFastJit, bool isEnableBaselineJit);
60    bool PUBLIC_API IsEnableFastJit() const;
61    bool PUBLIC_API IsEnableBaselineJit() const;
62    bool PUBLIC_API IsEnableJitFort() const;
63    void SetEnableJitFort(bool isEnableJitFort);
64    bool IsDisableCodeSign() const;
65    void SetDisableCodeSign(bool isEnableCodeSign);
66    bool PUBLIC_API IsEnableAsyncCopyToFort() const;
67    void SetEnableAsyncCopyToFort(bool isEnableiAsyncCopyToFort);
68    void Initialize();
69
70    static void Compile(EcmaVM *vm, JSHandle<JSFunction> &jsFunction, CompilerTier tier = CompilerTier::FAST,
71                        int32_t offset = MachineCode::INVALID_OSR_OFFSET, JitCompileMode mode = SYNC);
72    bool JitCompile(void *compiler, JitTask *jitTask);
73    bool JitFinalize(void *compiler, JitTask *jitTask);
74    void *CreateJitCompilerTask(JitTask *jitTask);
75    bool IsInitialized() const
76    {
77        return initialized_;
78    }
79
80    void DeleteJitCompile(void *compiler);
81
82    void RequestInstallCode(std::shared_ptr<JitTask> jitTask);
83    void InstallTasks(JSThread *jsThread);
84    void ClearTask(const std::function<bool(Task *task)> &checkClear);
85    void ClearTask(EcmaContext *ecmaContext);
86    void ClearTaskWithVm(EcmaVM *vm);
87    void Destroy();
88    uint32_t GetRunningTaskCnt(EcmaVM *vm);
89    void CheckMechineCodeSpaceMemory(JSThread *thread, int remainSize);
90    void ChangeTaskPoolState(bool inBackground);
91
92    // dfx for jit warmup compile
93    static void CountInterpExecFuncs(JSHandle<JSFunction> &jsFunction);
94
95    bool IsAppJit() const
96    {
97        return isApp_;
98    }
99
100    uint32_t GetHotnessThreshold() const
101    {
102        return hotnessThreshold_;
103    }
104
105    void SetProfileNeedDump(bool isNeed)
106    {
107        isProfileNeedDump_ = isNeed;
108    }
109
110    bool IsProfileNeedDump() const
111    {
112        return isProfileNeedDump_;
113    }
114
115    JitDfx *GetJitDfx() const
116    {
117        return jitDfx_;
118    }
119
120    void IncJitTaskCnt(JSThread *thread);
121    void DecJitTaskCnt(JSThread *thread);
122
123    NO_COPY_SEMANTIC(Jit);
124    NO_MOVE_SEMANTIC(Jit);
125
126    class TimeScope : public ClockScope {
127    public:
128        explicit TimeScope(EcmaVM *vm, CString message, CompilerTier tier = CompilerTier::FAST, bool outPutLog = true,
129            bool isDebugLevel = false)
130            : vm_(vm), message_(message), tier_(tier), outPutLog_(outPutLog), isDebugLevel_(isDebugLevel) {}
131        explicit TimeScope(EcmaVM *vm)
132            : vm_(vm), message_(""), tier_(CompilerTier::FAST), outPutLog_(false), isDebugLevel_(true) {}
133        PUBLIC_API ~TimeScope();
134    private:
135        EcmaVM *vm_;
136        CString message_;
137        CompilerTier tier_;
138        bool outPutLog_;
139        bool isDebugLevel_;
140    };
141
142    class JitLockHolder {
143    public:
144        explicit JitLockHolder(JSThread *thread) : thread_(nullptr), scope_(thread->GetEcmaVM())
145        {
146            if (thread->IsJitThread()) {
147                thread_ = static_cast<JitThread*>(thread);
148                if (thread_->GetState() != ThreadState::RUNNING) {
149                    thread_->ManagedCodeBegin();
150                    isInManagedCode_ = true;
151                }
152                thread_->GetHostThread()->GetJitLock()->Lock();
153            }
154        }
155
156        explicit JitLockHolder(const CompilationEnv *env, CString message) : thread_(nullptr),
157            scope_(env->GetJSThread()->GetEcmaVM(),
158                "Jit Compile Pass: " + message + ", Time:", CompilerTier::FAST, false)
159        {
160            if (env->IsJitCompiler()) {
161                JSThread *thread = env->GetJSThread();
162                ASSERT(thread->IsJitThread());
163                thread_ = static_cast<JitThread*>(thread);
164                if (thread_->GetState() != ThreadState::RUNNING) {
165                    thread_->ManagedCodeBegin();
166                    isInManagedCode_ = true;
167                }
168                thread_->GetHostThread()->GetJitLock()->Lock();
169            }
170        }
171
172        ~JitLockHolder()
173        {
174            if (thread_ != nullptr) {
175                thread_->GetHostThread()->GetJitLock()->Unlock();
176                if (isInManagedCode_) {
177                    thread_->ManagedCodeEnd();
178                }
179            }
180        }
181        JitThread *thread_ {nullptr};
182        TimeScope scope_;
183        bool isInManagedCode_{false};
184        ALLOW_HEAP_ACCESS
185        NO_COPY_SEMANTIC(JitLockHolder);
186        NO_MOVE_SEMANTIC(JitLockHolder);
187    };
188
189    class JitGCLockHolder {
190    public:
191        explicit JitGCLockHolder(JSThread *thread) : thread_(thread)
192        {
193            ASSERT(!thread->IsJitThread());
194            if (Jit::GetInstance()->IsEnableFastJit() || Jit::GetInstance()->IsEnableBaselineJit()) {
195                LockJit(thread_);
196                locked_ = true;
197            }
198        }
199
200        static void LockJit(JSThread *thread)
201        {
202            Clock::time_point start = Clock::now();
203            thread->GetJitLock()->Lock();
204            Jit::GetInstance()->GetJitDfx()->SetLockHoldingTime(
205            std::chrono::duration_cast<std::chrono::microseconds>(Clock::now() - start).count());
206        }
207
208        static void UnlockJit(JSThread *thread)
209        {
210            thread->GetJitLock()->Unlock();
211        }
212
213        ~JitGCLockHolder()
214        {
215            if (locked_) {
216                UnlockJit(thread_);
217                locked_ = false;
218            }
219        }
220
221    private:
222        JSThread *thread_;
223        bool locked_ { false };
224
225        NO_COPY_SEMANTIC(JitGCLockHolder);
226        NO_MOVE_SEMANTIC(JitGCLockHolder);
227    };
228
229private:
230    bool SupportJIT(JSHandle<JSFunction> &jsFunction, EcmaVM *vm, CompilerTier tier) const;
231    bool initialized_ { false };
232    bool fastJitEnable_ { false };
233    bool baselineJitEnable_ { false };
234    bool isApp_ { false };
235    bool isProfileNeedDump_ { true };
236    uint32_t hotnessThreshold_ { 0 };
237    std::string bundleName_;
238    bool isEnableAppPGO_ { true };
239
240    std::unordered_map<JSThread*, ThreadTaskInfo> threadTaskInfo_;
241    RecursiveMutex threadTaskInfoLock_;
242    bool isEnableJitFort_ { true };
243    bool isDisableCodeSign_ { true };
244    bool isEnableAsyncCopyToFort_ { true };
245
246    Mutex setEnableLock_;
247
248    JitDfx *jitDfx_ { nullptr };
249    static constexpr int MIN_CODE_SPACE_SIZE = 1_KB;
250
251    static void (*initJitCompiler_)(JSRuntimeOptions);
252    static bool(*jitCompile_)(void*, JitTask*);
253    static bool(*jitFinalize_)(void*, JitTask*);
254    static void*(*createJitCompilerTask_)(JitTask*);
255    static void(*deleteJitCompile_)(void*);
256    static void *libHandle_;
257    static bool CheckJitCompileStatus(JSHandle<JSFunction> &jsFunction,
258        const CString &methodName, CompilerTier tier);
259};
260}  // namespace panda::ecmascript
261#endif  // ECMASCRIPT_JIT_H
262