• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023-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 #include "ecmascript/jit/jit.h"
17 #include "ecmascript/base/config.h"
18 #include "ecmascript/jit/jit_task.h"
19 #include "ecmascript/dfx/vmstat/jit_warmup_profiler.h"
20 #include "ecmascript/ohos/jit_tools.h"
21 #include "ecmascript/platform/os.h"
22 #include "ecmascript/checkpoint/thread_state_transition.h"
23 
24 namespace panda::ecmascript {
25 
GetInstance()26 Jit *Jit::GetInstance()
27 {
28     static Jit instance_;
29     return &instance_;
30 }
31 
CreateJitResources()32 void Jit::CreateJitResources()
33 {
34     if (jitResources_ == nullptr) {
35         jitResources_ = std::make_unique<JitResources>();
36         jitResources_->ResolveLib();
37     }
38 }
39 
IsLibResourcesResolved() const40 bool Jit::IsLibResourcesResolved() const
41 {
42     if (jitResources_ != nullptr) {
43         return jitResources_->IsLibResolved();
44     }
45     return false;
46 }
47 
PreFork()48 void Jit::PreFork()
49 {
50     CreateJitResources();
51 }
52 
SetJitEnablePostFork(EcmaVM * vm,const std::string & bundleName)53 void Jit::SetJitEnablePostFork(EcmaVM *vm, const std::string &bundleName)
54 {
55     JSRuntimeOptions &options = vm->GetJSOptions();
56     bool jitEnable = ohos::JitTools::GetJitEscapeDisable() || !AotCrashInfo::IsJitEscape();
57     jitEnable &= ohos::EnableAotJitListHelper::GetInstance()->IsEnableJit(bundleName);
58     jitEnable &= !vm->GetJSOptions().GetAOTHasException();
59     jitEnable &= ohos::JitTools::IsSupportJitCodeSigner();
60     jitEnable &= HasJitFortACL();
61     if (jitEnable) {
62         bool isEnableFastJit = options.IsEnableJIT() && options.GetEnableAsmInterpreter();
63         bool isEnableBaselineJit = options.IsEnableBaselineJIT() && options.GetEnableAsmInterpreter();
64 
65         options.SetEnableJitFrame(ohos::JitTools::GetJitFrameEnable());
66         options.SetEnableAPPJIT(true);
67         isApp_ = true;
68         // for app threshold
69         uint32_t defaultSize = 150;
70         uint32_t threshold = ohos::JitTools::GetJitHotnessThreshold(defaultSize);
71         options.SetJitHotnessThreshold(threshold);
72         hotnessThreshold_ = threshold;
73         bundleName_ = bundleName;
74 
75         SetEnableOrDisable(options, isEnableFastJit, isEnableBaselineJit);
76         if (fastJitEnable_ || baselineJitEnable_) {
77             ConfigJit(vm);
78         }
79     }
80 }
81 
SwitchProfileStubs(EcmaVM * vm)82 void Jit::SwitchProfileStubs(EcmaVM *vm)
83 {
84     JSThread *thread = vm->GetAssociatedJSThread();
85     JSRuntimeOptions &options = vm->GetJSOptions();
86     std::shared_ptr<PGOProfiler> pgoProfiler = vm->GetPGOProfiler();
87     if (!options.IsEnableJITPGO() || pgoProfiler == nullptr) {
88         thread->SwitchJitProfileStubs(false);
89     } else {
90         // if not enable aot pgo
91         if (!pgo::PGOProfilerManager::GetInstance()->IsEnable()) {
92             // disable dump
93             options.SetEnableProfileDump(false);
94             SetProfileNeedDump(false);
95             // enable profiler
96             options.SetEnablePGOProfiler(true);
97             pgoProfiler->Reset(true);
98             // switch pgo stub
99             thread->SwitchJitProfileStubs(true);
100         }
101         pgoProfiler->InitJITProfiler();
102     }
103 }
104 
ConfigOptions(EcmaVM * vm) const105 void Jit::ConfigOptions(EcmaVM *vm) const
106 {
107     JSRuntimeOptions &options = vm->GetJSOptions();
108 
109     options.SetEnableAPPJIT(isApp_);
110     options.SetEnableProfileDump(isProfileNeedDump_);
111 
112     bool jitEnableLitecg = ohos::JitTools::IsJitEnableLitecg(options.IsCompilerEnableLiteCG());
113     options.SetCompilerEnableLiteCG(jitEnableLitecg);
114 
115     uint16_t jitCallThreshold = ohos::JitTools::GetJitCallThreshold(options.GetJitCallThreshold());
116     options.SetJitCallThreshold(jitCallThreshold);
117 
118     uint32_t jitHotnessThreshold = GetHotnessThreshold();
119     options.SetJitHotnessThreshold(jitHotnessThreshold);
120 
121     bool jitDisableCodeSign = ohos::JitTools::GetCodeSignDisable(options.GetDisableCodeSign());
122     options.SetDisableCodeSign(jitDisableCodeSign);
123 
124     bool jitEnableJitFort = ohos::JitTools::GetEnableJitFort(options.GetEnableJitFort());
125     options.SetEnableJitFort(jitEnableJitFort);
126 
127     bool jitEnableVerifyPass = ohos::JitTools::GetEnableJitVerifyPass(options.IsEnableJitVerifyPass());
128     options.SetEnableJitVerifyPass(jitEnableVerifyPass);
129 
130     bool jitEnableAsyncCopyToFort = ohos::JitTools::GetEnableAsyncCopyToFort(options.GetEnableAsyncCopyToFort());
131     options.SetEnableAsyncCopyToFort(jitEnableAsyncCopyToFort);
132 
133     vm->SetEnableJitLogSkip(ohos::JitTools::GetSkipJitLogEnable());
134 
135     std::string jitMethodDichotomy = ohos::JitTools::GetJitMethodDichotomy(options.GetJitMethodDichotomy());
136     options.SetJitMethodDichotomy(jitMethodDichotomy);
137 
138     LOG_JIT(INFO) << "enable jit bundle:" << bundleName_ <<
139         ", litecg:" << jitEnableLitecg <<
140         ", call threshold:" << static_cast<int>(jitCallThreshold) <<
141         ", hotness threshold:" << jitHotnessThreshold <<
142         ", disable codesigner:" << jitDisableCodeSign;
143 }
144 
ConfigJit(EcmaVM * vm)145 void Jit::ConfigJit(EcmaVM *vm)
146 {
147     SwitchProfileStubs(vm);
148     ConfigOptions(vm);
149     ConfigJitFortOptions(vm);
150     // initialize jit method dichotomy
151     CompileDecision::GetMethodNameCollector().Init(vm);
152     CompileDecision::GetMethodNameFilter().Init(vm);
153 }
154 
ConfigJitFortOptions(EcmaVM * vm)155 void Jit::ConfigJitFortOptions(EcmaVM *vm)
156 {
157     SetDisableCodeSign(vm->GetJSOptions().GetDisableCodeSign());
158     SetEnableJitFort(vm->GetJSOptions().GetEnableJitFort());
159     SetEnableAsyncCopyToFort(vm->GetJSOptions().GetEnableAsyncCopyToFort());
160 }
161 
SetEnableOrDisable(const JSRuntimeOptions & options,bool isEnableFastJit,bool isEnableBaselineJit)162 void Jit::SetEnableOrDisable(const JSRuntimeOptions &options, bool isEnableFastJit, bool isEnableBaselineJit)
163 {
164     LockHolder holder(setEnableLock_);
165     bool enableJit = isEnableFastJit || isEnableBaselineJit;
166     if (enableJit) {
167         CreateJitResources();
168     }
169 
170     if (IsLibResourcesResolved()) {
171         jitDfx_ = JitDfx::GetInstance();
172         jitDfx_->Init(options, bundleName_);
173         jitResources_->InitJitEnv(options);
174         initialized_ = true;
175     }
176 
177     if (initialized_) {
178         fastJitEnable_ = isEnableFastJit;
179         baselineJitEnable_ = isEnableBaselineJit;
180         hotnessThreshold_ = options.GetJitHotnessThreshold();
181     }
182 }
183 
Destroy()184 void Jit::Destroy()
185 {
186     LockHolder holder(setEnableLock_);
187     if (!initialized_) {
188         return;
189     }
190 
191     initialized_ = false;
192     fastJitEnable_ = false;
193     baselineJitEnable_ = false;
194     ASSERT(jitResources_ != nullptr);
195     jitResources_->Destroy();
196     jitResources_ = nullptr;
197 }
198 
IsEnableFastJit() const199 bool Jit::IsEnableFastJit() const
200 {
201     return fastJitEnable_;
202 }
203 
IsEnableBaselineJit() const204 bool Jit::IsEnableBaselineJit() const
205 {
206     return baselineJitEnable_;
207 }
208 
IsEnableJitFort() const209 bool Jit::IsEnableJitFort() const
210 {
211     return isEnableJitFort_;
212 }
213 
SetEnableJitFort(bool isEnableJitFort)214 void Jit::SetEnableJitFort(bool isEnableJitFort)
215 {
216     isEnableJitFort_ = isEnableJitFort;
217 }
218 
IsDisableCodeSign() const219 bool Jit::IsDisableCodeSign() const
220 {
221     return isDisableCodeSign_;
222 }
223 
SetDisableCodeSign(bool isDisableCodeSign)224 void Jit::SetDisableCodeSign(bool isDisableCodeSign)
225 {
226     isDisableCodeSign_ = isDisableCodeSign;
227 }
228 
IsEnableAsyncCopyToFort() const229 bool Jit::IsEnableAsyncCopyToFort() const
230 {
231     return isEnableAsyncCopyToFort_;
232 }
233 
SetEnableAsyncCopyToFort(bool isEnableAsyncCopyToFort)234 void Jit::SetEnableAsyncCopyToFort(bool isEnableAsyncCopyToFort)
235 {
236     isEnableAsyncCopyToFort_ = isEnableAsyncCopyToFort;
237 }
238 
~Jit()239 Jit::~Jit()
240 {
241 }
242 
CountInterpExecFuncs(JSThread * jsThread,JSHandle<JSFunction> & jsFunction)243 void Jit::CountInterpExecFuncs(JSThread *jsThread, JSHandle<JSFunction> &jsFunction)
244 {
245     Method *method = Method::Cast(jsFunction->GetMethod(jsThread).GetTaggedObject());
246     auto jSPandaFile = method->GetJSPandaFile(jsThread);
247     ASSERT(jSPandaFile != nullptr);
248     CString fileDesc = jSPandaFile->GetJSPandaFileDesc();
249     CString methodInfo =
250         fileDesc + ":" + method->GetRecordNameStr(jsThread) +"." + CString(method->GetMethodName(jsThread));
251     auto &profMap = JitWarmupProfiler::GetInstance()->profMap_;
252     if (profMap.find(methodInfo) == profMap.end()) {
253         profMap.insert({methodInfo, false});
254     }
255 }
256 
Compile(EcmaVM * vm,JSHandle<JSFunction> & jsFunction,CompilerTier tier,int32_t osrOffset,JitCompileMode mode)257 void Jit::Compile(EcmaVM *vm, JSHandle<JSFunction> &jsFunction, CompilerTier tier,
258                   int32_t osrOffset, JitCompileMode mode)
259 {
260     auto jit = Jit::GetInstance();
261     if ((!jit->IsEnableBaselineJit() && tier.IsBaseLine()) ||
262         (!jit->IsEnableFastJit() && tier.IsFast())) {
263         return;
264     }
265 
266     if (!vm->IsEnableOsr() && osrOffset != MachineCode::INVALID_OSR_OFFSET) {
267         return;
268     }
269 
270     CompileDecision compileDecision(vm, jsFunction, tier, osrOffset, mode);
271     if (!compileDecision.Decision()) {
272         return;
273     }
274 
275     jit->Compile(vm, compileDecision);
276 }
277 
Compile(EcmaVM * vm,const CompileDecision & decision)278 void Jit::Compile(EcmaVM *vm, const CompileDecision &decision)
279 {
280     auto tier = decision.GetTier();
281     auto jsFunction = decision.GetJsFunction();
282     auto methodInfo = decision.GetMethodInfo();
283     auto methodName = decision.GetMethodName();
284     auto osrOffset = decision.GetOsrOffset();
285     auto mode = decision.GetCompileMode();
286 
287     CString msg = "compile method:" + methodInfo + ", in work thread";
288     TimeScope scope(vm, msg, tier, true, true);
289 
290     ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK,
291         ConvertToStdString("JIT::Compile:" + methodInfo).c_str(), "");
292     if (tier.IsFast()) {
293         jsFunction->SetJitCompilingFlag(true);
294     } else {
295         ASSERT(tier.IsBaseLine());
296         jsFunction->SetBaselinejitCompilingFlag(true);
297     }
298     GetJitDfx()->SetTriggerCount(tier);
299 
300     {
301         {
302             ThreadNativeScope scope(vm->GetJSThread());
303             JitTaskpool::GetCurrentTaskpool()->WaitForJitTaskPoolReady();
304         }
305         EcmaVM *compilerVm = JitTaskpool::GetCurrentTaskpool()->GetCompilerVm();
306         std::shared_ptr<JitTask> jitTask = std::make_shared<JitTask>(vm->GetJSThread(),
307             // avoid check fail when enable multi-thread check
308             compilerVm->GetJSThreadNoCheck(), this, jsFunction, tier, methodName, osrOffset, mode);
309 
310         jitTask->PrepareCompile();
311         JitTaskpool::GetCurrentTaskpool()->PostTask(
312             std::make_unique<JitTask::AsyncTask>(jitTask, vm->GetJSThread()->GetThreadId()));
313         if (mode.IsSync()) {
314             // sync mode, also compile in taskpool as litecg unsupport parallel compile,
315             // wait task compile finish then install code
316             jitTask->WaitFinish();
317             jitTask->InstallCode();
318         }
319         int spendTime = scope.TotalSpentTimeInMicroseconds();
320         jitTask->SetMainThreadCompilerTime(spendTime);
321         GetJitDfx()->RecordSpentTimeAndPrintStatsLogInJsThread(spendTime);
322     }
323 }
324 
RequestInstallCode(std::shared_ptr<JitTask> jitTask)325 void Jit::RequestInstallCode(std::shared_ptr<JitTask> jitTask)
326 {
327     LockHolder holder(threadTaskInfoLock_);
328     ThreadTaskInfo &info = threadTaskInfo_[jitTask->GetHostThread()];
329     if (info.skipInstallTask_) {
330         return;
331     }
332     info.installJitTasks_.push_back(jitTask);
333 
334     // set
335     jitTask->GetHostThread()->SetInstallMachineCode(true);
336     jitTask->GetHostThread()->SetCheckSafePointStatus();
337 }
338 
GetRunningTaskCnt(EcmaVM * vm)339 uint32_t Jit::GetRunningTaskCnt(EcmaVM *vm)
340 {
341     LockHolder holder(threadTaskInfoLock_);
342     ThreadTaskInfo &info = threadTaskInfo_[vm->GetJSThread()];
343     return info.jitTaskCnt_.load();
344 }
345 
InstallTasks(JSThread * jsThread)346 void Jit::InstallTasks(JSThread *jsThread)
347 {
348     // Install tasks is only possible for JSThread in running state
349     ASSERT(jsThread->IsJSThread() && jsThread->IsInRunningState());
350     std::deque<std::shared_ptr<JitTask>> taskQueue;
351     {
352         LockHolder holder(threadTaskInfoLock_);
353         ThreadTaskInfo &info = threadTaskInfo_[jsThread];
354         taskQueue = info.installJitTasks_;
355         info.installJitTasks_.clear();
356     }
357     ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK,
358         ConvertToStdString("Jit::InstallTasks count:" + ToCString(taskQueue.size())).c_str(), "");
359 
360     for (auto it = taskQueue.begin(); it != taskQueue.end(); it++) {
361         std::shared_ptr<JitTask> task = *it;
362         // check task state
363         task->InstallCode();
364     }
365 }
366 
JitCompile(void * compiler,JitTask * jitTask)367 bool Jit::JitCompile(void *compiler, JitTask *jitTask)
368 {
369     return jitResources_->Compile(compiler, jitTask);
370 }
371 
JitFinalize(void * compiler,JitTask * jitTask)372 bool Jit::JitFinalize(void *compiler, JitTask *jitTask)
373 {
374     return jitResources_->Finalize(compiler, jitTask);
375 }
376 
CreateJitCompilerTask(JitTask * jitTask)377 void *Jit::CreateJitCompilerTask(JitTask *jitTask)
378 {
379     return jitResources_->CreateJitCompilerTask(jitTask);
380 }
381 
DeleteJitCompilerTask(void * compiler)382 void Jit::DeleteJitCompilerTask(void *compiler)
383 {
384     jitResources_->DeleteJitCompilerTask(compiler);
385 }
386 
ClearTask(const std::function<bool (common::Task * task)> & checkClear)387 void Jit::ClearTask(const std::function<bool(common::Task *task)> &checkClear)
388 {
389     JitTaskpool::GetCurrentTaskpool()->ForEachTask([&checkClear](common::Task *task) {
390         JitTask::AsyncTask *asyncTask = static_cast<JitTask::AsyncTask*>(task);
391         if (checkClear(asyncTask)) {
392             asyncTask->Terminated();
393         }
394     });
395 }
396 
ClearTaskWithVm(EcmaVM * vm)397 void Jit::ClearTaskWithVm(EcmaVM *vm)
398 {
399     ClearTask([vm](common::Task *task) {
400         JitTask::AsyncTask *asyncTask = static_cast<JitTask::AsyncTask*>(task);
401         return vm == asyncTask->GetHostVM();
402     });
403 
404     {
405         LockHolder holder(threadTaskInfoLock_);
406         ThreadTaskInfo &info = threadTaskInfo_[vm->GetJSThread()];
407         info.skipInstallTask_ = true;
408         auto &taskQueue = info.installJitTasks_;
409         taskQueue.clear();
410 
411         if (info.jitTaskCnt_.load() != 0) {
412             ThreadNativeScope threadNativeScope(vm->GetJSThread());
413             info.jitTaskCntCv_.Wait(&threadTaskInfoLock_);
414         }
415     }
416 }
417 
IncJitTaskCnt(JSThread * thread)418 void Jit::IncJitTaskCnt(JSThread *thread)
419 {
420     LockHolder holder(threadTaskInfoLock_);
421     ThreadTaskInfo &info = threadTaskInfo_[thread];
422     info.jitTaskCnt_.fetch_add(1);
423 }
424 
DecJitTaskCnt(JSThread * thread)425 void Jit::DecJitTaskCnt(JSThread *thread)
426 {
427     LockHolder holder(threadTaskInfoLock_);
428     ThreadTaskInfo &info = threadTaskInfo_[thread];
429     uint32_t old = info.jitTaskCnt_.fetch_sub(1);
430     if (old == 1) {
431         info.jitTaskCntCv_.Signal();
432     }
433 }
434 
CheckMechineCodeSpaceMemory(JSThread * thread,int remainSize)435 void Jit::CheckMechineCodeSpaceMemory(JSThread *thread, int remainSize)
436 {
437     if (!thread->IsMachineCodeLowMemory()) {
438         return;
439     }
440     if (remainSize > MIN_CODE_SPACE_SIZE) {
441         thread->SetMachineCodeLowMemory(false);
442     }
443 }
444 
ChangeTaskPoolState(bool inBackground)445 void Jit::ChangeTaskPoolState(bool inBackground)
446 {
447     if (fastJitEnable_ || baselineJitEnable_) {
448         if (inBackground) {
449             JitTaskpool::GetCurrentTaskpool()->SetThreadPriority(common::PriorityMode::BACKGROUND);
450         } else {
451             JitTaskpool::GetCurrentTaskpool()->SetThreadPriority(common::PriorityMode::FOREGROUND);
452         }
453     }
454 }
455 
TimeScope(EcmaVM * vm,CString message,CompilerTier tier,bool outPutLog,bool isDebugLevel)456 Jit::TimeScope::TimeScope(EcmaVM *vm, CString message, CompilerTier tier, bool outPutLog, bool isDebugLevel)
457     : vm_(vm), message_(message), tier_(tier), outPutLog_(outPutLog), isDebugLevel_(isDebugLevel)
458 {
459     if (outPutLog_) {
460         if (isDebugLevel_) {
461             LOG_JIT(DEBUG) << tier_ << message_ << " begin.";
462         } else {
463             auto bundleName = vm_->GetBundleName();
464             if (vm_->GetEnableJitLogSkip() && bundleName != "" && message_.find(bundleName) == std::string::npos) {
465                 return;
466             }
467             LOG_JIT(INFO) << tier_ << message_ << " begin.";
468         }
469     }
470 }
471 
~TimeScope()472 Jit::TimeScope::~TimeScope()
473 {
474     if (!outPutLog_) {
475         return;
476     }
477     if (isDebugLevel_) {
478         LOG_JIT(DEBUG) << tier_ << message_ << ": " << TotalSpentTime() << "ms";
479     } else {
480         auto bundleName = vm_->GetBundleName();
481         if (vm_->GetEnableJitLogSkip() && bundleName != "" && message_.find(bundleName) == std::string::npos) {
482             return;
483         }
484         LOG_JIT(INFO) << tier_ << message_ << ", compile time: " << TotalSpentTime() << "ms";
485     }
486 }
487 }  // namespace panda::ecmascript
488