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