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