• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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