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 
29 namespace panda::ecmascript {
30 class JitTask;
31 enum JitCompileMode {
32     SYNC = 0,
33     ASYNC
34 };
35 
36 enum class CompilerTier : uint8_t {
37     BASELINE,
38     FAST,
39 };
40 
41 struct 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 
49 class Jit {
50 public:
Jit()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);
IsInitialized() const75     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 
IsAppJit() const95     bool IsAppJit() const
96     {
97         return isApp_;
98     }
99 
GetHotnessThreshold() const100     uint32_t GetHotnessThreshold() const
101     {
102         return hotnessThreshold_;
103     }
104 
SetProfileNeedDump(bool isNeed)105     void SetProfileNeedDump(bool isNeed)
106     {
107         isProfileNeedDump_ = isNeed;
108     }
109 
IsProfileNeedDump() const110     bool IsProfileNeedDump() const
111     {
112         return isProfileNeedDump_;
113     }
114 
GetJitDfx() const115     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:
TimeScope(EcmaVM *vm, CString message, CompilerTier tier = CompilerTier::FAST, bool outPutLog = true, bool isDebugLevel = false)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) {}
TimeScope(EcmaVM *vm)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:
JitLockHolder(JSThread *thread)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 
JitLockHolder(const CompilationEnv *env, CString message)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 
~JitLockHolder()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:
JitGCLockHolder(JSThread *thread)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 
LockJit(JSThread *thread)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 
UnlockJit(JSThread *thread)208         static void UnlockJit(JSThread *thread)
209         {
210             thread->GetJitLock()->Unlock();
211         }
212 
~JitGCLockHolder()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 
229 private:
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