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