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/ecma_context.h"
17 #include "ecmascript/jit/jit_task.h"
18 #include "ecmascript/ic/profile_type_info.h"
19 #include "ecmascript/jspandafile/program_object.h"
20 #include "ecmascript/ohos/jit_tools.h"
21
22 namespace panda::ecmascript {
23
GetCurrentTaskpool()24 JitTaskpool *JitTaskpool::GetCurrentTaskpool()
25 {
26 static JitTaskpool *taskpool = new JitTaskpool();
27 return taskpool;
28 }
29
TheMostSuitableThreadNum(uint32_t threadNum) const30 uint32_t JitTaskpool::TheMostSuitableThreadNum([[maybe_unused]]uint32_t threadNum) const
31 {
32 return 1;
33 }
34
JitTask(JSThread * hostThread,JSThread * compilerThread,Jit * jit,JSHandle<JSFunction> & jsFunction,CompilerTier tier,CString & methodName,int32_t offset,uint32_t taskThreadId,JitCompileMode mode)35 JitTask::JitTask(JSThread *hostThread, JSThread *compilerThread, Jit *jit, JSHandle<JSFunction> &jsFunction,
36 CompilerTier tier, CString &methodName, int32_t offset, uint32_t taskThreadId,
37 JitCompileMode mode)
38 : hostThread_(hostThread),
39 compilerThread_(compilerThread),
40 jit_(jit),
41 jsFunction_(jsFunction),
42 compilerTask_(nullptr),
43 state_(CompileState::SUCCESS),
44 compilerTier_(tier),
45 methodName_(methodName),
46 offset_(offset),
47 taskThreadId_(taskThreadId),
48 ecmaContext_(nullptr),
49 jitCompileMode_(mode),
50 runState_(RunState::INIT)
51 {
52 jit->IncJitTaskCnt(hostThread);
53 ecmaContext_ = hostThread->GetCurrentEcmaContext();
54 sustainingJSHandle_ = std::make_unique<SustainingJSHandle>(hostThread->GetEcmaVM());
55 }
56
PrepareCompile()57 void JitTask::PrepareCompile()
58 {
59 CloneProfileTypeInfo();
60 SustainingJSHandles();
61 compilerTask_ = jit_->CreateJitCompilerTask(this);
62
63 Method *method = Method::Cast(jsFunction_->GetMethod().GetTaggedObject());
64 JSTaggedValue constpool = method->GetConstantPool();
65 if (!ConstantPool::CheckUnsharedConstpool(constpool)) {
66 hostThread_->GetCurrentEcmaContext()->FindOrCreateUnsharedConstpool(constpool);
67 }
68
69 SetRunState(RunState::INIT);
70 }
71
Optimize()72 void JitTask::Optimize()
73 {
74 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "JIT::Compiler frontend");
75 bool res = jit_->JitCompile(compilerTask_, this);
76 if (!res) {
77 SetCompileFailed();
78 }
79 }
80
Finalize()81 void JitTask::Finalize()
82 {
83 if (!IsCompileSuccess()) {
84 return;
85 }
86
87 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "JIT::Compiler backend");
88 bool res = jit_->JitFinalize(compilerTask_, this);
89 if (!res) {
90 SetCompileFailed();
91 }
92 }
93
InstallOsrCode(JSHandle<MachineCode> & codeObj)94 void JitTask::InstallOsrCode(JSHandle<MachineCode> &codeObj)
95 {
96 auto profile = jsFunction_->GetProfileTypeInfo();
97 if (profile.IsUndefined()) {
98 LOG_JIT(DEBUG) << "[OSR] Empty profile for installing code:" << GetMethodName();
99 return;
100 }
101 FuncEntryDes *funcEntryDes = reinterpret_cast<FuncEntryDes*>(codeObj->GetFuncEntryDes());
102 jsFunction_->SetIsCompiledFastCall(funcEntryDes->isFastCall_);
103 JSHandle<ProfileTypeInfo> profileInfoHandle =
104 JSHandle<ProfileTypeInfo>::Cast(JSHandle<JSTaggedValue>(hostThread_, profile));
105 uint32_t slotId = profileInfoHandle->GetIcSlotAndOsrLength() - 1; // 1 : get last slot
106 auto profileData = profileInfoHandle->Get(slotId);
107 auto factory = hostThread_->GetEcmaVM()->GetFactory();
108 if (!profileData.IsTaggedArray()) {
109 const uint32_t initLen = 1;
110 JSHandle<TaggedArray> newArr = factory->NewTaggedArray(initLen);
111 newArr->Set(hostThread_, 0, codeObj.GetTaggedValue());
112 profileInfoHandle->Set(hostThread_, slotId, newArr.GetTaggedValue());
113 LOG_JIT(DEBUG) << "[OSR] Install machine code:" << GetMethodName()
114 << ", code address: " << reinterpret_cast<void*>(codeObj->GetFuncAddr())
115 << ", index: " << newArr->GetLength() - 1;
116 return;
117 }
118 JSHandle<TaggedArray> arr(hostThread_, profileData);
119 JSHandle<TaggedArray> newArr = factory->NewTaggedArray(arr->GetLength() + 1); // 1 : added for current codeObj
120 uint32_t i = 0;
121 for (; i < arr->GetLength(); i++) {
122 newArr->Set(hostThread_, i, arr->Get(i));
123 }
124 newArr->Set(hostThread_, i, codeObj.GetTaggedValue());
125 profileInfoHandle->Set(hostThread_, slotId, newArr.GetTaggedValue());
126 LOG_JIT(DEBUG) << "[OSR] Install machine code:" << GetMethodName()
127 << ", code address: " << reinterpret_cast<void*>(codeObj->GetFuncAddr())
128 << ", index: " << newArr->GetLength() - 1;
129 return;
130 }
131
ComputeAlignedSizes(MachineCodeDesc & desc)132 static void ComputeAlignedSizes(MachineCodeDesc &desc)
133 {
134 desc.funcEntryDesSizeAlign = AlignUp(desc.funcEntryDesSize, MachineCode::TEXT_ALIGN);
135 desc.stackMapSizeAlign = AlignUp(desc.stackMapOrOffsetTableSize, MachineCode::DATA_ALIGN);
136 desc.rodataSizeBeforeTextAlign = AlignUp(desc.rodataSizeBeforeText, MachineCode::TEXT_ALIGN);
137
138 if (desc.codeType == MachineCodeType::BASELINE_CODE) {
139 desc.codeSizeAlign = Jit::GetInstance()->IsEnableJitFort() ?
140 AlignUp(desc.codeSize, MachineCode::TEXT_ALIGN) :
141 AlignUp(desc.codeSize, MachineCode::DATA_ALIGN);
142 return;
143 }
144
145 // FastJit
146 if (Jit::GetInstance()->IsEnableJitFort()) {
147 // align for multiple instruction blocks installed in JitFort
148 if (desc.rodataSizeAfterText) {
149 desc.codeSizeAlign = AlignUp(desc.codeSize, MachineCode::DATA_ALIGN);
150 desc.rodataSizeAfterTextAlign = AlignUp(desc.rodataSizeAfterText, MachineCode::TEXT_ALIGN);
151 } else {
152 desc.codeSizeAlign = AlignUp(desc.codeSize, MachineCode::TEXT_ALIGN);
153 }
154 } else {
155 desc.codeSizeAlign = AlignUp(desc.codeSize, MachineCode::DATA_ALIGN);
156 desc.rodataSizeAfterTextAlign = AlignUp(desc.rodataSizeAfterText, MachineCode::DATA_ALIGN);
157 }
158 }
159
ComputePayLoadSize(MachineCodeDesc & codeDesc)160 size_t JitTask::ComputePayLoadSize(MachineCodeDesc &codeDesc)
161 {
162 ComputeAlignedSizes(codeDesc);
163 if (codeDesc.codeType == MachineCodeType::BASELINE_CODE) {
164 // only code section in BaselineCode
165 if (Jit::GetInstance()->IsEnableJitFort()) {
166 size_t payLoadSize = codeDesc.stackMapSizeAlign + codeDesc.codeSizeAlign;
167 size_t allocSize = AlignUp(payLoadSize + MachineCode::SIZE,
168 static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
169 codeDesc.instructionsSize = codeDesc.codeSizeAlign;
170 LOG_JIT(DEBUG) << "InstallCode:: MachineCode Object size to allocate: "
171 << allocSize << " (instruction size): " << codeDesc.codeSizeAlign;
172 if (allocSize > MAX_REGULAR_HEAP_OBJECT_SIZE) {
173 return payLoadSize;
174 } else {
175 // regular sized machine code object instructions are installed in separate jit fort space
176 return payLoadSize - codeDesc.codeSizeAlign;
177 }
178 } else {
179 return codeDesc.stackMapSizeAlign + codeDesc.codeSizeAlign;
180 }
181 }
182
183 ASSERT(codeDesc.codeType == MachineCodeType::FAST_JIT_CODE);
184 if (Jit::GetInstance()->IsEnableJitFort()) {
185 // instructionsSize: size of JIT generated native instructions
186 // payLoadSize: size of JIT generated output including native code
187 size_t instructionsSize =
188 codeDesc.rodataSizeBeforeTextAlign + codeDesc.codeSizeAlign + codeDesc.rodataSizeAfterTextAlign;
189 size_t payLoadSize = codeDesc.funcEntryDesSizeAlign + instructionsSize + codeDesc.stackMapSizeAlign;
190 size_t allocSize = AlignUp(payLoadSize + MachineCode::SIZE,
191 static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
192 LOG_JIT(DEBUG) << "InstallCode:: MachineCode Object size to allocate: "
193 << allocSize << " (instruction size): " << instructionsSize;
194
195 codeDesc.instructionsSize = instructionsSize;
196 if (allocSize > MAX_REGULAR_HEAP_OBJECT_SIZE) {
197 //
198 // A Huge machine code object is consisted of contiguous 256Kb aligned blocks.
199 // With JitFort, a huge machine code object starts with a page aligned mutable area
200 // (that holds Region and MachineCode object header, FuncEntryDesc and StackMap), followed
201 // by a page aligned nonmutable (JitFort space) area of JIT generated native instructions.
202 // i.e.
203 // mutableSize = align up to PageSize
204 // (sizeof(Region) + HUGE_OBJECT_BITSET_SIZE +MachineCode::SIZE + payLoadSize - instructionsSize)
205 // immutableSize = instructionsSize (native page boundary aligned)
206 // See comments at HugeMachineCodeSpace::Allocate()
207 //
208 codeDesc.isHugeObj = true;
209 return payLoadSize;
210 } else {
211 // regular sized machine code object instructions are installed in separate jit fort space
212 return payLoadSize - instructionsSize;
213 }
214 } else {
215 return codeDesc.funcEntryDesSizeAlign + codeDesc.rodataSizeBeforeTextAlign + codeDesc.codeSizeAlign +
216 codeDesc.rodataSizeAfterTextAlign + codeDesc.stackMapSizeAlign;
217 }
218 }
219
DumpJitCode(JSHandle<MachineCode> & machineCode,JSHandle<Method> & method)220 void DumpJitCode(JSHandle<MachineCode> &machineCode, JSHandle<Method> &method)
221 {
222 if (!ohos::JitTools::GetJitDumpObjEanble()) {
223 return;
224 }
225 JsJitDumpElf jitDumpElf;
226 jitDumpElf.Init();
227 char *funcAddr = reinterpret_cast<char *>(machineCode->GetFuncAddr());
228 size_t len = machineCode->GetTextSize();
229 std::vector<uint8> vec(len);
230 if (memmove_s(vec.data(), len, funcAddr, len) != EOK) {
231 LOG_JIT(DEBUG) << "Fail to get machineCode on function addr: " << funcAddr;
232 }
233 jitDumpElf.AppendData(vec);
234 const char *filename = method->GetMethodName();
235 std::string fileName = std::string(filename);
236 uintptr_t addr = machineCode->GetFuncAddr();
237 fileName = fileName + "_" + std::to_string(addr) + "+" + std::to_string(len);
238 jitDumpElf.AppendSymbolToSymTab(0, 0, len, std::string(filename));
239 std::string realOutPath;
240 std::string sanboxPath = panda::os::file::File::GetExtendedFilePath(AotCrashInfo::GetSandBoxPath());
241 if (!ecmascript::RealPath(sanboxPath, realOutPath, false)) {
242 return;
243 }
244 std::string outFile = realOutPath + "/" + std::string(fileName);
245 if (!ecmascript::FileExist(outFile.c_str())) {
246 return;
247 }
248 int fd = open(outFile.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0644);
249 jitDumpElf.WriteJitElfFile(fd);
250 close(fd);
251 }
252
InstallCode()253 void JitTask::InstallCode()
254 {
255 if (!IsCompileSuccess()) {
256 return;
257 }
258 [[maybe_unused]] EcmaHandleScope handleScope(hostThread_);
259
260 JSHandle<Method> methodHandle(hostThread_, Method::Cast(jsFunction_->GetMethod().GetTaggedObject()));
261
262 size_t size = ComputePayLoadSize(codeDesc_);
263
264 codeDesc_.isAsyncCompileMode = IsAsyncTask();
265 JSHandle<MachineCode> machineCodeObj;
266 if (Jit::GetInstance()->IsEnableJitFort()) {
267 // skip install if JitFort out of memory
268 TaggedObject *machineCode = hostThread_->GetEcmaVM()->GetFactory()->NewMachineCodeObject(size, codeDesc_);
269 if (machineCode == nullptr) {
270 LOG_JIT(DEBUG) << "InstallCode skipped. NewMachineCode NULL for size " << size;
271 if (hostThread_->HasPendingException()) {
272 hostThread_->SetMachineCodeLowMemory(true);
273 hostThread_->ClearException();
274 }
275 return;
276 }
277 machineCodeObj = hostThread_->GetEcmaVM()->GetFactory()->SetMachineCodeObjectData(
278 machineCode, size, codeDesc_, methodHandle);
279 } else {
280 machineCodeObj = hostThread_->GetEcmaVM()->GetFactory()->NewMachineCodeObject(
281 size, codeDesc_, methodHandle);
282 }
283 if (machineCodeObj.GetAddress() == ToUintPtr(nullptr)) {
284 // skip install
285 return;
286 }
287 machineCodeObj->SetOSROffset(offset_);
288
289 if (hostThread_->HasPendingException()) {
290 // check is oom exception
291 hostThread_->SetMachineCodeLowMemory(true);
292 hostThread_->ClearException();
293 }
294
295 if (IsOsrTask()) {
296 InstallOsrCode(machineCodeObj);
297 } else {
298 InstallCodeByCompilerTier(machineCodeObj, methodHandle);
299 }
300
301 // sometimes get ILL_ILLOPC error if i-cache not flushed for Jit code
302 uintptr_t codeAddr = machineCodeObj->GetFuncAddr();
303 uintptr_t codeAddrEnd = codeAddr + machineCodeObj->GetInstructionsSize();
304 __builtin___clear_cache(reinterpret_cast<char *>(codeAddr), reinterpret_cast<char*>(codeAddrEnd));
305
306 if (Jit::GetInstance()->IsEnableJitFort()) {
307 if (codeDesc_.memDesc) {
308 ASSERT(codeDesc_.isHugeObj == false);
309 codeDesc_.memDesc->SetInstalled(true);
310 }
311 }
312 }
313
InstallCodeByCompilerTier(JSHandle<MachineCode> & machineCodeObj,JSHandle<Method> & methodHandle)314 void JitTask::InstallCodeByCompilerTier(JSHandle<MachineCode> &machineCodeObj,
315 JSHandle<Method> &methodHandle)
316 {
317 uintptr_t codeAddr = machineCodeObj->GetFuncAddr();
318 if (compilerTier_ == CompilerTier::FAST) {
319 FuncEntryDes *funcEntryDes = reinterpret_cast<FuncEntryDes*>(machineCodeObj->GetFuncEntryDes());
320 jsFunction_->SetCompiledFuncEntry(codeAddr, funcEntryDes->isFastCall_);
321 methodHandle->SetDeoptThreshold(hostThread_->GetEcmaVM()->GetJSOptions().GetDeoptThreshold());
322 jsFunction_->SetMachineCode(hostThread_, machineCodeObj);
323 jsFunction_->SetJitMachineCodeCache(hostThread_, machineCodeObj);
324 uintptr_t codeAddrEnd = codeAddr + machineCodeObj->GetInstructionsSize();
325 LOG_JIT(DEBUG) <<"Install fast jit machine code:" << GetMethodName() << ", code range:" <<
326 reinterpret_cast<void*>(codeAddr) <<"--" << reinterpret_cast<void*>(codeAddrEnd);
327 #if ECMASCRIPT_ENABLE_JIT_WARMUP_PROFILER
328 auto &profMap = JitWarmupProfiler::GetInstance()->profMap_;
329 if (profMap.find(GetMethodName()) != profMap.end()) {
330 profMap[GetMethodName()] = true;
331 }
332 #endif
333 } else {
334 ASSERT(compilerTier_ == CompilerTier::BASELINE);
335 methodHandle->SetDeoptThreshold(hostThread_->GetEcmaVM()->GetJSOptions().GetDeoptThreshold());
336 jsFunction_->SetBaselineCode(hostThread_, machineCodeObj);
337 LOG_BASELINEJIT(DEBUG) <<"Install baseline jit machine code:" << GetMethodName();
338 }
339 }
340
SustainingJSHandles()341 void JitTask::SustainingJSHandles()
342 {
343 // transfer to sustaining handle
344 JSHandle<JSFunction> sustainingJsFunctionHandle = sustainingJSHandle_->NewHandle(jsFunction_);
345 SetJsFunction(sustainingJsFunctionHandle);
346
347 JSHandle<ProfileTypeInfo> profileTypeInfo = sustainingJSHandle_->NewHandle(profileTypeInfo_);
348 SetProfileTypeInfo(profileTypeInfo);
349 }
350
ReleaseSustainingJSHandle()351 void JitTask::ReleaseSustainingJSHandle()
352 {
353 // in abort case, vm exit before task finish, release by explict
354 sustainingJSHandle_ = nullptr;
355 }
356
CloneProfileTypeInfo()357 void JitTask::CloneProfileTypeInfo()
358 {
359 [[maybe_unused]] EcmaHandleScope handleScope(hostThread_);
360
361 Method *method = Method::Cast(jsFunction_->GetMethod().GetTaggedObject());
362 uint32_t slotSize = method->GetSlotSize();
363 JSTaggedValue profileTypeInfoVal = jsFunction_->GetProfileTypeInfo();
364 JSHandle<ProfileTypeInfo> newProfileTypeInfo;
365 ObjectFactory *factory = hostThread_->GetEcmaVM()->GetFactory();
366 if (profileTypeInfoVal.IsUndefined() || slotSize == 0) {
367 slotSize = slotSize == 0 ? 1 : slotSize; // there's no profiletypeinfo, just generate a temp profiletypeinfo
368 newProfileTypeInfo = factory->NewProfileTypeInfo(slotSize);
369 } else {
370 JSHandle<ProfileTypeInfo> profileTypeInfo(hostThread_,
371 ProfileTypeInfo::Cast(profileTypeInfoVal.GetTaggedObject()));
372 newProfileTypeInfo = factory->NewProfileTypeInfo(slotSize);
373 for (uint32_t i = 0; i < slotSize; i++) {
374 JSTaggedValue value = profileTypeInfo->Get(i);
375 newProfileTypeInfo->Set(hostThread_, i, value);
376 }
377 }
378 SetProfileTypeInfo(newProfileTypeInfo);
379 }
380
~JitTask()381 JitTask::~JitTask()
382 {
383 ReleaseSustainingJSHandle();
384 jit_->DeleteJitCompile(compilerTask_);
385 jit_->DecJitTaskCnt(hostThread_);
386 }
387
WaitFinish()388 void JitTask::WaitFinish()
389 {
390 LockHolder lock(runStateMutex_);
391 if (!IsFinish()) {
392 runStateCondition_.Wait(&runStateMutex_);
393 }
394 }
395
Run(uint32_t threadIndex)396 bool JitTask::AsyncTask::Run([[maybe_unused]] uint32_t threadIndex)
397 {
398 if (IsTerminate() || !jitTask_->GetHostThread()->GetEcmaVM()->IsInitialized()) {
399 return false;
400 }
401 DISALLOW_HEAP_ACCESS;
402
403 CString info = "compile method:" + jitTask_->GetMethodName();
404 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, ConvertToStdString("JIT::Compile:" + info));
405
406 AsyncTaskRunScope asyncTaskRunScope(jitTask_.get());
407
408 if (jitTask_->GetJsFunction().GetAddress() == 0) {
409 // for unit test
410 } else {
411 info = info + ", in jit thread";
412 Jit::TimeScope scope(jitTask_->GetHostThread()->GetEcmaVM(), info, jitTask_->GetCompilerTier());
413
414 jitTask_->Optimize();
415 jitTask_->Finalize();
416
417 // info main thread compile complete
418 if (!jitTask_->IsCompileSuccess()) {
419 return false;
420 }
421
422 if (jitTask_->IsAsyncTask()) {
423 jitTask_->jit_->RequestInstallCode(jitTask_);
424 }
425 int compilerTime = scope.TotalSpentTimeInMicroseconds();
426 JitDfx::GetInstance()->RecordSpentTimeAndPrintStatsLogInJitThread(compilerTime, jitTask_->methodName_,
427 jitTask_->compilerTier_ == CompilerTier::BASELINE, jitTask_->mainThreadCompileTime_);
428 }
429 return true;
430 }
431
AsyncTaskRunScope(JitTask * jitTask)432 JitTask::AsyncTask::AsyncTaskRunScope::AsyncTaskRunScope(JitTask *jitTask)
433 {
434 jitTask_ = jitTask;
435 jitTask_->SetRunState(RunState::RUNNING);
436 JSThread *compilerThread = jitTask_->GetCompilerThread();
437 ASSERT(compilerThread->IsJitThread());
438 JitThread *jitThread = static_cast<JitThread*>(compilerThread);
439 jitvm_ = jitThread->GetJitVM();
440 jitvm_->SetHostVM(jitTask_->GetHostThread());
441 }
442
~AsyncTaskRunScope()443 JitTask::AsyncTask::AsyncTaskRunScope::~AsyncTaskRunScope()
444 {
445 jitvm_->ReSetHostVM();
446 jitTask_->SetRunStateFinish();
447 }
448 } // namespace panda::ecmascript
449