• 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_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