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