1 /*
2 * Copyright (c) 2021 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/compiler/file_generators.h"
17
18 #include "ecmascript/platform/code_sign.h"
19 #include "ecmascript/platform/directory.h"
20 #include "ecmascript/snapshot/mem/snapshot.h"
21 #include "ecmascript/stackmap/ark_stackmap_builder.h"
22 #include "ecmascript/stackmap/llvm_stackmap_parser.h"
23
24 namespace panda::ecmascript::kungfu {
CollectStackMapDes(ModuleSectionDes & des) const25 void Module::CollectStackMapDes(ModuleSectionDes& des) const
26 {
27 uint32_t stackmapSize = des.GetSecSize(ElfSecName::LLVM_STACKMAP);
28 std::unique_ptr<uint8_t[]> stackmapPtr(std::make_unique<uint8_t[]>(stackmapSize));
29 uint64_t addr = des.GetSecAddr(ElfSecName::LLVM_STACKMAP);
30 if (addr == 0) { // assembler stub don't existed llvm stackmap
31 return;
32 }
33 uint64_t textAddr = des.GetSecAddr(ElfSecName::TEXT);
34 if (memcpy_s(stackmapPtr.get(), stackmapSize, reinterpret_cast<void *>(addr), stackmapSize) != EOK) {
35 LOG_COMPILER(FATAL) << "memcpy_s failed";
36 UNREACHABLE();
37 }
38 std::shared_ptr<uint8_t> ptr = nullptr;
39 uint32_t size = 0;
40 ArkStackMapBuilder builder;
41 std::tie(ptr, size) = builder.Run(std::move(stackmapPtr), textAddr, llvmModule_->GetTriple());
42 des.EraseSec(ElfSecName::LLVM_STACKMAP);
43 des.SetArkStackMapPtr(ptr);
44 des.SetArkStackMapSize(size);
45 }
46
CollectAnStackMapDes(ModuleSectionDes & des,uint64_t textOffset,std::vector<LLVMStackMapType::Pc2CallSiteInfo> & pc2CallsiteInfoVec,std::vector<LLVMStackMapType::Pc2Deopt> & pc2DeoptVec) const47 void Module::CollectAnStackMapDes(ModuleSectionDes& des, uint64_t textOffset,
48 std::vector<LLVMStackMapType::Pc2CallSiteInfo> &pc2CallsiteInfoVec,
49 std::vector<LLVMStackMapType::Pc2Deopt> &pc2DeoptVec) const
50 {
51 uint32_t stackmapSize = des.GetSecSize(ElfSecName::LLVM_STACKMAP);
52 std::unique_ptr<uint8_t[]> stackmapPtr(std::make_unique<uint8_t[]>(stackmapSize));
53 uint64_t addr = des.GetSecAddr(ElfSecName::LLVM_STACKMAP);
54 if (addr == 0) { // assembler stub don't existed llvm stackmap
55 return;
56 }
57 uint64_t textAddr = des.GetSecAddr(ElfSecName::TEXT);
58 if (memcpy_s(stackmapPtr.get(), stackmapSize, reinterpret_cast<void *>(addr), stackmapSize) != EOK) {
59 LOG_COMPILER(FATAL) << "memcpy_s failed";
60 UNREACHABLE();
61 }
62 ArkStackMapBuilder builder;
63 builder.Collect(std::move(stackmapPtr), textAddr, textOffset, pc2CallsiteInfoVec, pc2DeoptVec);
64 des.EraseSec(ElfSecName::LLVM_STACKMAP);
65 }
66
CollectFuncEntryInfo(std::map<uintptr_t,std::string> & addr2name,StubFileInfo & stubInfo,uint32_t moduleIndex,const CompilerLog & log)67 void Module::CollectFuncEntryInfo(std::map<uintptr_t, std::string> &addr2name, StubFileInfo &stubInfo,
68 uint32_t moduleIndex, const CompilerLog &log)
69 {
70 auto engine = assembler_->GetEngine();
71 auto callSigns = llvmModule_->GetCSigns();
72 std::vector<uintptr_t> entrys;
73 for (size_t j = 0; j < llvmModule_->GetFuncCount(); j++) {
74 LLVMValueRef func = llvmModule_->GetFunction(j);
75 ASSERT(func != nullptr);
76 uintptr_t entry = reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(engine, func));
77 entrys.push_back(entry);
78 }
79 auto codeBuff = assembler_->GetSectionAddr(ElfSecName::TEXT);
80 const size_t funcCount = llvmModule_->GetFuncCount();
81 funcCount_ = funcCount;
82 startIndex_ = stubInfo.GetEntrySize();
83 for (size_t j = 0; j < funcCount; j++) {
84 auto cs = callSigns[j];
85 LLVMValueRef func = llvmModule_->GetFunction(j);
86 ASSERT(func != nullptr);
87 int delta = assembler_->GetFpDeltaPrevFramSp(func, log);
88 ASSERT(delta >= 0 && (delta % sizeof(uintptr_t) == 0));
89 uint32_t funcSize = 0;
90 if (j < funcCount - 1) {
91 funcSize = entrys[j + 1] - entrys[j];
92 } else {
93 funcSize = codeBuff + assembler_->GetSectionSize(ElfSecName::TEXT) - entrys[j];
94 }
95 kungfu::CalleeRegAndOffsetVec info = assembler_->GetCalleeReg2Offset(func, log);
96 stubInfo.AddEntry(cs->GetTargetKind(), false, false, cs->GetID(), entrys[j] - codeBuff, moduleIndex, delta,
97 funcSize, info);
98 ASSERT(!cs->GetName().empty());
99 addr2name[entrys[j]] = cs->GetName();
100 }
101 }
102
CollectFuncEntryInfo(std::map<uintptr_t,std::string> & addr2name,AnFileInfo & aotInfo,uint32_t moduleIndex,const CompilerLog & log)103 void Module::CollectFuncEntryInfo(std::map<uintptr_t, std::string> &addr2name, AnFileInfo &aotInfo,
104 uint32_t moduleIndex, const CompilerLog &log)
105 {
106 auto engine = assembler_->GetEngine();
107 std::vector<std::tuple<uint64_t, size_t, int, bool>> funcInfo; // entry idx delta
108 std::vector<kungfu::CalleeRegAndOffsetVec> calleeSaveRegisters; // entry idx delta
109 // 1.Compile all functions and collect function infos
110 llvmModule_->IteratefuncIndexMap([&](size_t idx, LLVMValueRef func, bool isFastCall) {
111 uint64_t funcEntry = reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(engine, func));
112 uint64_t length = 0;
113 std::string funcName(LLVMGetValueName2(func, reinterpret_cast<size_t *>(&length)));
114 ASSERT(length != 0);
115 addr2name[funcEntry] = funcName;
116 int delta = assembler_->GetFpDeltaPrevFramSp(func, log);
117 ASSERT(delta >= 0 && (delta % sizeof(uintptr_t) == 0));
118 funcInfo.emplace_back(std::tuple(funcEntry, idx, delta, isFastCall));
119 kungfu::CalleeRegAndOffsetVec info = assembler_->GetCalleeReg2Offset(func, log);
120 calleeSaveRegisters.emplace_back(info);
121 });
122 // 2.After all functions compiled, the module sections would be fixed
123 uintptr_t textAddr = GetTextAddr();
124 uint32_t textSize = GetTextSize();
125 uintptr_t rodataAddr = 0;
126 uint32_t rodataSize = 0;
127 std::tie(rodataAddr, rodataSize) = GetMergedRODataAddrAndSize();
128 aotInfo.AlignTextSec(AOTFileInfo::PAGE_ALIGN);
129 if (rodataAddr < textAddr) {
130 aotInfo.UpdateCurTextSecOffset(rodataSize);
131 aotInfo.AlignTextSec(AOTFileInfo::TEXT_SEC_ALIGN);
132 }
133
134 const size_t funcCount = funcInfo.size();
135 funcCount_ = funcCount;
136 startIndex_ = aotInfo.GetEntrySize();
137 // 3.Add function entries based on the module sections
138 for (size_t i = 0; i < funcInfo.size(); i++) {
139 uint64_t funcEntry;
140 size_t idx;
141 int delta;
142 bool isFastCall;
143 uint32_t funcSize;
144 std::tie(funcEntry, idx, delta, isFastCall) = funcInfo[i];
145 if (i < funcCount - 1) {
146 funcSize = std::get<0>(funcInfo[i + 1]) - funcEntry;
147 } else {
148 funcSize = textAddr + textSize - funcEntry;
149 }
150 auto found = addr2name[funcEntry].find(panda::ecmascript::JSPandaFile::ENTRY_FUNCTION_NAME);
151 bool isMainFunc = found != std::string::npos;
152 uint64_t offset = funcEntry - textAddr + aotInfo.GetCurTextSecOffset();
153 aotInfo.AddEntry(CallSignature::TargetKind::JSFUNCTION, isMainFunc, isFastCall, idx,
154 offset, moduleIndex, delta, funcSize, calleeSaveRegisters[i]);
155 }
156 aotInfo.UpdateCurTextSecOffset(textSize);
157 if (rodataAddr > textAddr) {
158 aotInfo.AlignTextSec(AOTFileInfo::DATA_SEC_ALIGN);
159 aotInfo.UpdateCurTextSecOffset(rodataSize);
160 }
161 }
162
CollectModuleSectionDes(ModuleSectionDes & moduleDes) const163 void Module::CollectModuleSectionDes(ModuleSectionDes &moduleDes) const
164 {
165 ASSERT(assembler_ != nullptr);
166 assembler_->IterateSecInfos([&](size_t i, std::pair<uint8_t *, size_t> secInfo) {
167 auto curSec = ElfSection(i);
168 ElfSecName sec = curSec.GetElfEnumValue();
169 if (IsRelaSection(sec)) {
170 moduleDes.EraseSec(sec);
171 } else { // aot need relocated; stub don't need collect relocated section
172 moduleDes.SetSecAddrAndSize(sec, reinterpret_cast<uint64_t>(secInfo.first), secInfo.second);
173 moduleDes.SetStartIndex(startIndex_);
174 moduleDes.SetFuncCount(funcCount_);
175 }
176 });
177 CollectStackMapDes(moduleDes);
178 }
179
CollectAnModuleSectionDes(ModuleSectionDes & moduleDes,uint64_t textOffset,std::vector<LLVMStackMapType::Pc2CallSiteInfo> & pc2CallsiteInfoVec,std::vector<LLVMStackMapType::Pc2Deopt> & pc2DeoptVec) const180 void Module::CollectAnModuleSectionDes(ModuleSectionDes &moduleDes, uint64_t textOffset,
181 std::vector<LLVMStackMapType::Pc2CallSiteInfo> &pc2CallsiteInfoVec,
182 std::vector<LLVMStackMapType::Pc2Deopt> &pc2DeoptVec) const
183 {
184 ASSERT(assembler_ != nullptr);
185 assembler_->IterateSecInfos([&](size_t i, std::pair<uint8_t *, size_t> secInfo) {
186 auto curSec = ElfSection(i);
187 ElfSecName sec = curSec.GetElfEnumValue();
188 // aot need relocated; stub don't need collect relocated section
189 moduleDes.SetSecAddrAndSize(sec, reinterpret_cast<uint64_t>(secInfo.first), secInfo.second);
190 moduleDes.SetStartIndex(startIndex_);
191 moduleDes.SetFuncCount(funcCount_);
192 });
193 CollectAnStackMapDes(moduleDes, textOffset, pc2CallsiteInfoVec, pc2DeoptVec);
194 }
195
GetSectionSize(ElfSecName sec) const196 uint32_t Module::GetSectionSize(ElfSecName sec) const
197 {
198 return assembler_->GetSectionSize(sec);
199 }
200
GetSectionAddr(ElfSecName sec) const201 uintptr_t Module::GetSectionAddr(ElfSecName sec) const
202 {
203 return assembler_->GetSectionAddr(sec);
204 }
205
RunAssembler(const CompilerLog & log,bool fastCompileMode)206 void Module::RunAssembler(const CompilerLog &log, bool fastCompileMode)
207 {
208 assembler_->Run(log, fastCompileMode);
209 }
210
DisassemblerFunc(std::map<uintptr_t,std::string> & addr2name,uint64_t textOffset,const CompilerLog & log,const MethodLogList & logList,std::ostringstream & codeStream)211 void Module::DisassemblerFunc(std::map<uintptr_t, std::string> &addr2name, uint64_t textOffset,
212 const CompilerLog &log, const MethodLogList &logList, std::ostringstream &codeStream)
213 {
214 assembler_->Disassemble(addr2name, textOffset, log, logList, codeStream);
215 }
216
DestroyModule()217 void Module::DestroyModule()
218 {
219 if (llvmModule_ != nullptr) {
220 delete llvmModule_;
221 llvmModule_ = nullptr;
222 }
223 if (assembler_ != nullptr) {
224 delete assembler_;
225 assembler_ = nullptr;
226 }
227 }
228
CollectAsmStubCodeInfo(std::map<uintptr_t,std::string> & addr2name,uint32_t bridgeModuleIdx)229 void StubFileGenerator::CollectAsmStubCodeInfo(std::map<uintptr_t, std::string> &addr2name, uint32_t bridgeModuleIdx)
230 {
231 uint32_t funSize = 0;
232 for (size_t i = 0; i < asmModule_.GetFunctionCount(); i++) {
233 auto cs = asmModule_.GetCSign(i);
234 auto entryOffset = asmModule_.GetFunction(cs->GetID());
235 if (i < asmModule_.GetFunctionCount() - 1) {
236 auto nextcs = asmModule_.GetCSign(i + 1);
237 funSize = asmModule_.GetFunction(nextcs->GetID()) - entryOffset;
238 } else {
239 funSize = asmModule_.GetBufferSize() - entryOffset;
240 }
241 stubInfo_.AddEntry(cs->GetTargetKind(), false, false, cs->GetID(), entryOffset, bridgeModuleIdx, 0, funSize);
242 ASSERT(!cs->GetName().empty());
243 addr2name[entryOffset] = cs->GetName();
244 }
245 }
246
CollectCodeInfo()247 void StubFileGenerator::CollectCodeInfo()
248 {
249 std::map<uintptr_t, std::string> stubAddr2Name;
250 for (size_t i = 0; i < modulePackage_.size(); i++) {
251 modulePackage_[i].CollectFuncEntryInfo(stubAddr2Name, stubInfo_, i, GetLog());
252 ModuleSectionDes des;
253 modulePackage_[i].CollectModuleSectionDes(des);
254 stubInfo_.AddModuleDes(des);
255 }
256 std::map<uintptr_t, std::string> asmAddr2Name;
257 // idx for bridge module is the one after last module in modulePackage
258 CollectAsmStubCodeInfo(asmAddr2Name, modulePackage_.size());
259 if (log_->OutputASM()) {
260 DisassembleAsmStubs(asmAddr2Name);
261 DisassembleEachFunc(stubAddr2Name);
262 }
263 }
264
DisassembleAsmStubs(std::map<uintptr_t,std::string> & addr2name)265 void StubFileGenerator::DisassembleAsmStubs(std::map<uintptr_t, std::string> &addr2name)
266 {
267 std::string tri = cfg_.GetTripleStr();
268 uint8_t *buf = reinterpret_cast<uint8_t*>(stubInfo_.GetAsmStubAddr());
269 size_t size = stubInfo_.GetAsmStubSize();
270 LLVMAssembler::Disassemble(&addr2name, tri, buf, size);
271 }
272
RollbackTextSize(Module * module)273 uint64_t AOTFileGenerator::RollbackTextSize(Module *module)
274 {
275 uint64_t textAddr = module->GetSectionAddr(ElfSecName::TEXT);
276 uint32_t textSize = module->GetSectionSize(ElfSecName::TEXT);
277 uint64_t rodataAddr = 0;
278 uint32_t rodataSize = 0;
279 std::tie(rodataAddr, rodataSize) = module->GetMergedRODataAddrAndSize();
280 uint64_t textStart = 0;
281 if (textAddr > rodataAddr) {
282 textStart = aotInfo_.GetCurTextSecOffset() - textSize;
283 } else {
284 textStart = aotInfo_.GetCurTextSecOffset() - textSize - rodataSize;
285 textStart = AlignDown(textStart, AOTFileInfo::DATA_SEC_ALIGN);
286 }
287 return textStart;
288 }
289
CollectCodeInfo(Module * module,uint32_t moduleIdx)290 void AOTFileGenerator::CollectCodeInfo(Module *module, uint32_t moduleIdx)
291 {
292 std::map<uintptr_t, std::string> addr2name;
293 module->CollectFuncEntryInfo(addr2name, aotInfo_, moduleIdx, GetLog());
294 ModuleSectionDes des;
295 uint64_t textOffset = RollbackTextSize(module);
296 module->CollectAnModuleSectionDes(des, textOffset, pc2CallSiteInfoVec_, pc2DeoptVec_);
297 aotInfo_.AddModuleDes(des);
298 if (log_->OutputASM()) {
299 module->DisassemblerFunc(addr2name, textOffset, *(log_), *(logList_), codeStream_);
300 }
301 }
302
GetLatestModule()303 Module* AOTFileGenerator::GetLatestModule()
304 {
305 return &modulePackage_.back();
306 }
307
GetModuleVecSize() const308 uint32_t AOTFileGenerator::GetModuleVecSize() const
309 {
310 return modulePackage_.size();
311 }
312
AddModule(const std::string & name,const std::string & triple,LOptions option,bool logDebug)313 Module* AOTFileGenerator::AddModule(const std::string &name, const std::string &triple, LOptions option, bool logDebug)
314 {
315 LLVMModule* m = new LLVMModule(vm_->GetNativeAreaAllocator(), name, logDebug, triple);
316 LLVMAssembler* ass = new LLVMAssembler(m, option);
317 modulePackage_.emplace_back(Module(m, ass));
318 return &modulePackage_.back();
319 }
320
GenerateMethodToEntryIndexMap()321 void AOTFileGenerator::GenerateMethodToEntryIndexMap()
322 {
323 const std::vector<AOTFileInfo::FuncEntryDes> &entries = aotInfo_.GetStubs();
324 uint32_t entriesSize = entries.size();
325 for (uint32_t i = 0; i < entriesSize; ++i) {
326 const AOTFileInfo::FuncEntryDes &entry = entries[i];
327 methodToEntryIndexMap_[entry.indexInKindOrMethodId_] = i;
328 }
329 }
330
AddModule(NativeAreaAllocator * allocator,const std::string & name,const std::string & triple,LOptions option,bool logDebug,StubFileKind kind)331 Module* StubFileGenerator::AddModule(NativeAreaAllocator *allocator, const std::string &name, const std::string &triple,
332 LOptions option, bool logDebug, StubFileKind kind)
333 {
334 LLVMModule* m = new LLVMModule(allocator, name, logDebug, triple);
335 if (kind == StubFileKind::BC) {
336 m->SetUpForBytecodeHandlerStubs();
337 } else if (kind == StubFileKind::COM) {
338 m->SetUpForCommonStubs();
339 } else {
340 ASSERT(kind == StubFileKind::BUILTIN);
341 m->SetUpForBuiltinsStubs();
342 }
343 LLVMAssembler* ass = new LLVMAssembler(m, option);
344 modulePackage_.emplace_back(Module(m, ass));
345 return &modulePackage_.back();
346 }
347
RunAsmAssembler()348 void StubFileGenerator::RunAsmAssembler()
349 {
350 NativeAreaAllocator allocator;
351 Chunk chunk(&allocator);
352 asmModule_.Run(&cfg_, &chunk);
353
354 auto buffer = asmModule_.GetBuffer();
355 auto bufferSize = asmModule_.GetBufferSize();
356 if (bufferSize == 0U) {
357 return;
358 }
359 stubInfo_.FillAsmStubTempHolder(buffer, bufferSize);
360 stubInfo_.accumulateTotalSize(bufferSize);
361 }
362
SaveStubFile(const std::string & filename)363 void StubFileGenerator::SaveStubFile(const std::string &filename)
364 {
365 RunLLVMAssembler();
366 RunAsmAssembler();
367 CollectCodeInfo();
368 stubInfo_.Save(filename, cfg_.GetTriple());
369 }
370
CompileLatestModuleThenDestroy()371 void AOTFileGenerator::CompileLatestModuleThenDestroy()
372 {
373 Module *latestModule = GetLatestModule();
374 uint32_t latestModuleIdx = GetModuleVecSize() - 1;
375 {
376 TimeScope timescope("LLVMIROpt", const_cast<CompilerLog *>(log_));
377 bool fastCompileMode = vm_->GetJSOptions().GetFastAOTCompileMode();
378 latestModule->RunAssembler(*(log_), fastCompileMode);
379 }
380 {
381 TimeScope timescope("LLVMCodeGen", const_cast<CompilerLog *>(log_));
382 CollectCodeInfo(latestModule, latestModuleIdx);
383 }
384 // message has been put into aotInfo, so latestModule could be destroyed
385 latestModule->DestroyModule();
386 }
387
DestroyCollectedStackMapInfo()388 void AOTFileGenerator::DestroyCollectedStackMapInfo()
389 {
390 pc2CallSiteInfoVec_.clear();
391 pc2DeoptVec_.clear();
392 }
393
GenerateMergedStackmapSection()394 void AOTFileGenerator::GenerateMergedStackmapSection()
395 {
396 ArkStackMapBuilder builder;
397 std::shared_ptr<uint8_t> ptr = nullptr;
398 uint32_t size = 0;
399 std::tie(ptr, size) = builder.GenerateArkStackMap(pc2CallSiteInfoVec_, pc2DeoptVec_, cfg_.GetTriple());
400 aotInfo_.UpdateStackMap(ptr, size, 0);
401 DestroyCollectedStackMapInfo();
402 }
403
CreateDirIfNotExist(const std::string & filename)404 bool AOTFileGenerator::CreateDirIfNotExist(const std::string &filename)
405 {
406 std::string realPath;
407 if (!panda::ecmascript::RealPath(filename, realPath, false)) {
408 return false;
409 }
410 auto index = realPath.find_last_of('/');
411 if (index == std::string::npos) {
412 return true;
413 }
414 std::string path = realPath.substr(0, index);
415 if (!panda::ecmascript::ForceCreateDirectory(path)) {
416 LOG_COMPILER(ERROR) << "Fail to make dir:" << path;
417 return false;
418 }
419 return panda::ecmascript::SetDirModeAsDefault(path);
420 }
421
SaveAOTFile(const std::string & filename)422 void AOTFileGenerator::SaveAOTFile(const std::string &filename)
423 {
424 if (aotInfo_.GetTotalCodeSize() == 0) {
425 LOG_HOST_TOOL_ERROR << "The an file generated by the aot compiler is empty! "
426 << "Maybe file in apPath is empty or all methods in ap file are mismatched";
427 LOG_COMPILER(ERROR) << "error: code size of generated an file is empty!";
428 return;
429 }
430 if (!CreateDirIfNotExist(filename)) {
431 LOG_COMPILER(ERROR) << "Fail to access dir:" << filename;
432 return;
433 }
434 PrintMergedCodeComment();
435 GenerateMergedStackmapSection();
436 GenerateMethodToEntryIndexMap();
437 aotInfo_.Save(filename, cfg_.GetTriple());
438 if (!panda::ecmascript::SetFileModeAsDefault(filename)) {
439 LOG_COMPILER(ERROR) << "Fail to set an file mode:" << filename;
440 }
441 panda::ecmascript::CodeSignForAOTFile(filename);
442 }
443
SaveSnapshotFile()444 void AOTFileGenerator::SaveSnapshotFile()
445 {
446 TimeScope timescope("LLVMCodeGenPass-AI", const_cast<CompilerLog *>(log_));
447 Snapshot snapshot(vm_);
448 const CString snapshotPath(vm_->GetJSOptions().GetAOTOutputFile().c_str());
449 vm_->GetJSThread()->GetCurrentEcmaContext()->GetTSManager()->ResolveSnapshotConstantPool(methodToEntryIndexMap_);
450 CString aiPath = snapshotPath + AOTFileManager::FILE_EXTENSION_AI;
451 snapshot.Serialize(aiPath);
452 if (!panda::ecmascript::SetFileModeAsDefault(aiPath.c_str())) {
453 LOG_COMPILER(ERROR) << "Fail to set ai file mode:" << aiPath;
454 }
455 }
456 } // namespace panda::ecmascript::kungfu
457