• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-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/compiler/codegen/llvm/llvm_codegen.h"
17 #if defined(PANDA_TARGET_MACOS) || defined(PANDA_TARGET_IOS)
18 #include "ecmascript/base/llvm_helper.h"
19 #endif
20 
21 
22 #if defined(__clang__)
23 #pragma clang diagnostic push
24 #pragma clang diagnostic ignored "-Wshadow"
25 #pragma clang diagnostic ignored "-Wunused-parameter"
26 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
27 #elif defined(__GNUC__)
28 #pragma GCC diagnostic push
29 #pragma GCC diagnostic ignored "-Wshadow"
30 #pragma GCC diagnostic ignored "-Wunused-parameter"
31 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
32 #endif
33 
34 #include "llvm-c/Analysis.h"
35 #include "llvm-c/Disassembler.h"
36 #include "llvm-c/Transforms/PassManagerBuilder.h"
37 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
38 #include "llvm/ExecutionEngine/SectionMemoryManager.h"
39 #include "llvm/ExecutionEngine/MCJIT.h"
40 #include "llvm/IR/LegacyPassManager.h"
41 #include "lib/llvm_interface.h"
42 
43 #include "ecmascript/compiler/aot_file/aot_file_info.h"
44 #include "ecmascript/compiler/codegen/llvm/llvm_ir_builder.h"
45 #include "ecmascript/compiler/compiler_log.h"
46 
47 #if defined(__clang__)
48 #pragma clang diagnostic pop
49 #elif defined(__GNUC__)
50 #pragma GCC diagnostic pop
51 #endif
52 
53 namespace panda::ecmascript::kungfu {
54 using namespace panda::ecmascript;
55 using namespace llvm;
56 
CodeInfo(CodeSpaceOnDemand & codeSpaceOnDemand,bool useOwnSpace)57 CodeInfo::CodeInfo(CodeSpaceOnDemand &codeSpaceOnDemand, bool useOwnSpace)
58     : codeSpaceOnDemand_(codeSpaceOnDemand),
59       useOwnSpace_(useOwnSpace)
60 {
61     secInfos_.fill(std::make_pair(nullptr, 0));
62     if (useOwnSpace_) {
63         ownCodeSpace_ = std::make_unique<CodeSpace>();
64     }
65 }
66 
~CodeInfo()67 CodeInfo::~CodeInfo()
68 {
69     Reset();
70 }
71 
GetInstance()72 CodeInfo::CodeSpace *CodeInfo::CodeSpace::GetInstance()
73 {
74     static CodeSpace *codeSpace = new CodeSpace();
75     return codeSpace;
76 }
77 
CodeSpace()78 CodeInfo::CodeSpace::CodeSpace()
79 {
80     ASSERT(REQUIRED_SECS_LIMIT == AlignUp(REQUIRED_SECS_LIMIT, PageSize()));
81     reqSecs_ = static_cast<uint8_t *>(PageMap(REQUIRED_SECS_LIMIT, PAGE_PROT_READWRITE).GetMem());
82     if (reqSecs_ == reinterpret_cast<uint8_t *>(-1)) {
83         reqSecs_ = nullptr;
84     }
85     ASSERT(UNREQUIRED_SECS_LIMIT == AlignUp(UNREQUIRED_SECS_LIMIT, PageSize()));
86     unreqSecs_ = static_cast<uint8_t *>(PageMap(UNREQUIRED_SECS_LIMIT, PAGE_PROT_READWRITE).GetMem());
87     if (unreqSecs_ == reinterpret_cast<uint8_t *>(-1)) {
88         unreqSecs_ = nullptr;
89     }
90 }
91 
~CodeSpace()92 CodeInfo::CodeSpace::~CodeSpace()
93 {
94     reqBufPos_ = 0;
95     unreqBufPos_ = 0;
96     if (reqSecs_ != nullptr) {
97         PageUnmap(MemMap(reqSecs_, REQUIRED_SECS_LIMIT));
98     }
99     reqSecs_ = nullptr;
100     if (unreqSecs_ != nullptr) {
101         PageUnmap(MemMap(unreqSecs_, UNREQUIRED_SECS_LIMIT));
102     }
103     unreqSecs_ = nullptr;
104 }
105 
Alloca(uintptr_t size,bool isReq,size_t alignSize)106 uint8_t *CodeInfo::CodeSpace::Alloca(uintptr_t size, bool isReq, size_t alignSize)
107 {
108     LockHolder lock(mutex_);
109     uint8_t *addr = nullptr;
110     auto bufBegin = isReq ? reqSecs_ : unreqSecs_;
111     auto &curPos = isReq ? reqBufPos_ : unreqBufPos_;
112     size_t limit = isReq ? REQUIRED_SECS_LIMIT : UNREQUIRED_SECS_LIMIT;
113     if (curPos + size > limit) {
114         LOG_COMPILER(ERROR) << std::hex << "Alloca Section failed. Current curPos:" << curPos
115                             << " plus size:" << size << "exceed limit:" << limit;
116         exit(-1);
117     }
118     if (alignSize > 0) {
119         curPos = AlignUp(curPos, alignSize);
120     }
121     addr = bufBegin + curPos;
122     curPos += size;
123     return addr;
124 }
125 
Alloca(uintptr_t size,bool isReq,size_t alignSize)126 uint8_t *CodeInfo::CodeSpaceOnDemand::Alloca(uintptr_t size, [[maybe_unused]] bool isReq, size_t alignSize)
127 {
128     // Always apply for an aligned memory block here.
129     auto alignedSize = alignSize > 0 ? AlignUp(size, alignSize) : size;
130     // Verify the size and temporarily use REQUIREd_SECS.LIMITED as the online option, allowing for adjustments.
131     if (alignedSize > SECTION_LIMIT) {
132         LOG_COMPILER(FATAL) << std::hex << "invalid memory size: " << alignedSize;
133         return nullptr;
134     }
135     uint8_t *addr = static_cast<uint8_t *>(malloc(alignedSize));
136     if (addr == nullptr) {
137         LOG_COMPILER(FATAL) << "malloc section failed.";
138         return nullptr;
139     }
140     sections_.push_back({addr, alignedSize});
141     return addr;
142 }
143 
~CodeSpaceOnDemand()144 CodeInfo::CodeSpaceOnDemand::~CodeSpaceOnDemand()
145 {
146     // release all used memory.
147     for (auto &section : sections_) {
148         if ((section.first != nullptr) && (section.second != 0)) {
149             free(section.first);
150         }
151     }
152     sections_.clear();
153 }
154 
AllocaOnDemand(uintptr_t size,size_t alignSize)155 uint8_t *CodeInfo::AllocaOnDemand(uintptr_t size, size_t alignSize)
156 {
157     return codeSpaceOnDemand_.Alloca(size, true, alignSize);
158 }
159 
AllocaInReqSecBuffer(uintptr_t size,size_t alignSize)160 uint8_t *CodeInfo::AllocaInReqSecBuffer(uintptr_t size, size_t alignSize)
161 {
162     if (useOwnSpace_) {
163         return ownCodeSpace_->Alloca(size, true, alignSize);
164     }
165     return CodeSpace::GetInstance()->Alloca(size, true, alignSize);
166 }
167 
AllocaInNotReqSecBuffer(uintptr_t size,size_t alignSize)168 uint8_t *CodeInfo::AllocaInNotReqSecBuffer(uintptr_t size, size_t alignSize)
169 {
170     if (useOwnSpace_) {
171         return ownCodeSpace_->Alloca(size, false, alignSize);
172     }
173     return CodeSpace::GetInstance()->Alloca(size, false, alignSize);
174 }
175 
AllocaCodeSectionImp(uintptr_t size,const char * sectionName,AllocaSectionCallback allocaInReqSecBuffer)176 uint8_t *CodeInfo::AllocaCodeSectionImp(uintptr_t size, const char *sectionName,
177                                         AllocaSectionCallback allocaInReqSecBuffer)
178 {
179     uint8_t *addr = nullptr;
180     auto curSec = ElfSection(sectionName);
181     if (curSec.isValidAOTSec()) {
182         if (!alreadyPageAlign_) {
183             addr = (this->*allocaInReqSecBuffer)(size, AOTFileInfo::PAGE_ALIGN);
184             alreadyPageAlign_ = true;
185         } else {
186             addr = (this->*allocaInReqSecBuffer)(size, AOTFileInfo::TEXT_SEC_ALIGN);
187         }
188     } else {
189         addr = (this->*allocaInReqSecBuffer)(size, 0);
190     }
191     codeInfo_.push_back({addr, size});
192     if (curSec.isValidAOTSec()) {
193         secInfos_[curSec.GetIntIndex()] = std::make_pair(addr, size);
194     }
195     return addr;
196 }
197 
AllocaCodeSection(uintptr_t size,const char * sectionName)198 uint8_t *CodeInfo::AllocaCodeSection(uintptr_t size, const char *sectionName)
199 {
200     return AllocaCodeSectionImp(size, sectionName, &CodeInfo::AllocaInReqSecBuffer);
201 }
202 
AllocaCodeSectionOnDemand(uintptr_t size,const char * sectionName)203 uint8_t *CodeInfo::AllocaCodeSectionOnDemand(uintptr_t size, const char *sectionName)
204 {
205     return AllocaCodeSectionImp(size, sectionName, &CodeInfo::AllocaOnDemand);
206 }
207 
AllocaDataSectionImp(uintptr_t size,const char * sectionName,AllocaSectionCallback allocaInReqSecBuffer,AllocaSectionCallback allocaInNotReqSecBuffer)208 uint8_t *CodeInfo::AllocaDataSectionImp(uintptr_t size, const char *sectionName,
209                                         AllocaSectionCallback allocaInReqSecBuffer,
210                                         AllocaSectionCallback allocaInNotReqSecBuffer)
211 {
212     uint8_t *addr = nullptr;
213     auto curSec = ElfSection(sectionName);
214     // rodata section needs 16 bytes alignment
215     if (curSec.InRodataSection()) {
216         size = AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_REGION));
217         if (!alreadyPageAlign_) {
218             addr = curSec.isSequentialAOTSec() ? (this->*allocaInReqSecBuffer)(size, AOTFileInfo::PAGE_ALIGN)
219                                                : (this->*allocaInNotReqSecBuffer)(size, AOTFileInfo::PAGE_ALIGN);
220             alreadyPageAlign_ = true;
221         } else {
222             uint32_t alignedSize = curSec.InRodataSection() ? AOTFileInfo::RODATA_SEC_ALIGN
223                                                             : AOTFileInfo::DATA_SEC_ALIGN;
224             addr = curSec.isSequentialAOTSec() ? (this->*allocaInReqSecBuffer)(size, alignedSize)
225                                                : (this->*allocaInNotReqSecBuffer)(size, alignedSize);
226         }
227     } else {
228         addr = curSec.isSequentialAOTSec() ? (this->*allocaInReqSecBuffer)(size, 0)
229                                            : (this->*allocaInNotReqSecBuffer)(size, 0);
230     }
231     if (curSec.isValidAOTSec()) {
232         secInfos_[curSec.GetIntIndex()] = std::make_pair(addr, size);
233     }
234     return addr;
235 
236 }
237 
AllocaDataSection(uintptr_t size,const char * sectionName)238 uint8_t *CodeInfo::AllocaDataSection(uintptr_t size, const char *sectionName)
239 {
240     return AllocaDataSectionImp(size, sectionName, &CodeInfo::AllocaInReqSecBuffer, &CodeInfo::AllocaInNotReqSecBuffer);
241 }
242 
AllocaDataSectionOnDemand(uintptr_t size,const char * sectionName)243 uint8_t *CodeInfo::AllocaDataSectionOnDemand(uintptr_t size, const char *sectionName)
244 {
245     return AllocaDataSectionImp(size, sectionName, &CodeInfo::AllocaOnDemand, &CodeInfo::AllocaOnDemand);
246 }
247 
SaveFunc2Addr(std::string funcName,uint32_t address)248 void CodeInfo::SaveFunc2Addr(std::string funcName, uint32_t address)
249 {
250     auto itr = func2FuncInfo.find(funcName);
251     if (itr != func2FuncInfo.end()) {
252         itr->second.addr = address;
253         return;
254     }
255     func2FuncInfo.insert(
256         std::pair<std::string, FuncInfo>(funcName, {address, 0, kungfu::CalleeRegAndOffsetVec()}));
257 }
258 
SaveFunc2FPtoPrevSPDelta(std::string funcName,int32_t fp2PrevSpDelta)259 void CodeInfo::SaveFunc2FPtoPrevSPDelta(std::string funcName, int32_t fp2PrevSpDelta)
260 {
261     auto itr = func2FuncInfo.find(funcName);
262     if (itr != func2FuncInfo.end()) {
263         itr->second.fp2PrevFrameSpDelta = fp2PrevSpDelta;
264         return;
265     }
266     func2FuncInfo.insert(
267         std::pair<std::string, FuncInfo>(funcName, {0, fp2PrevSpDelta, kungfu::CalleeRegAndOffsetVec()}));
268 }
269 
SaveFunc2CalleeOffsetInfo(std::string funcName,kungfu::CalleeRegAndOffsetVec calleeRegInfo)270 void CodeInfo::SaveFunc2CalleeOffsetInfo(std::string funcName, kungfu::CalleeRegAndOffsetVec calleeRegInfo)
271 {
272     auto itr = func2FuncInfo.find(funcName);
273     if (itr != func2FuncInfo.end()) {
274         itr->second.calleeRegInfo = calleeRegInfo;
275         return;
276     }
277     func2FuncInfo.insert(
278         std::pair<std::string, FuncInfo>(funcName, {0, 0, calleeRegInfo}));
279 }
280 
SavePC2DeoptInfo(uint64_t pc,std::vector<uint8_t> deoptInfo)281 void CodeInfo::SavePC2DeoptInfo(uint64_t pc, std::vector<uint8_t> deoptInfo)
282 {
283     pc2DeoptInfo.insert(std::pair<uint64_t, std::vector<uint8_t>>(pc, deoptInfo));
284 }
285 
SavePC2CallSiteInfo(uint64_t pc,std::vector<uint8_t> callSiteInfo)286 void CodeInfo::SavePC2CallSiteInfo(uint64_t pc, std::vector<uint8_t> callSiteInfo)
287 {
288     pc2CallsiteInfo.insert(std::pair<uint64_t, std::vector<uint8_t>>(pc, callSiteInfo));
289 }
290 
GetFuncInfos() const291 const std::map<std::string, CodeInfo::FuncInfo> &CodeInfo::GetFuncInfos() const
292 {
293     return func2FuncInfo;
294 }
295 
GetPC2DeoptInfo() const296 const std::map<uint64_t, std::vector<uint8_t>> &CodeInfo::GetPC2DeoptInfo() const
297 {
298     return pc2DeoptInfo;
299 }
300 
GetPC2CallsiteInfo() const301 const std::unordered_map<uint64_t, std::vector<uint8_t>> &CodeInfo::GetPC2CallsiteInfo() const
302 {
303     return pc2CallsiteInfo;
304 }
305 
Reset()306 void CodeInfo::Reset()
307 {
308     codeInfo_.clear();
309 }
310 
GetSectionAddr(ElfSecName sec) const311 uint8_t *CodeInfo::GetSectionAddr(ElfSecName sec) const
312 {
313     auto curSection = ElfSection(sec);
314     auto idx = curSection.GetIntIndex();
315     return const_cast<uint8_t *>(secInfos_[idx].first);
316 }
317 
GetSectionSize(ElfSecName sec) const318 size_t CodeInfo::GetSectionSize(ElfSecName sec) const
319 {
320     auto curSection = ElfSection(sec);
321     auto idx = curSection.GetIntIndex();
322     return secInfos_[idx].second;
323 }
324 
GetCodeInfo() const325 std::vector<std::pair<uint8_t *, uintptr_t>> CodeInfo::GetCodeInfo() const
326 {
327     return codeInfo_;
328 }
329 
GenerateCodeForStub(Circuit * circuit,const ControlFlowGraph & graph,size_t index,const CompilationConfig * cfg)330 void LLVMIRGeneratorImpl::GenerateCodeForStub(Circuit *circuit, const ControlFlowGraph &graph, size_t index,
331                                               const CompilationConfig *cfg)
332 {
333     LLVMValueRef function = module_->GetFunction(index);
334     const CallSignature* cs = module_->GetCSign(index);
335 #if ENABLE_NEXT_OPTIMIZATION
336     LLVMIRBuilder builder(&graph, circuit, module_, function, cfg, cs->GetCallConv(), enableLog_, false, cs->GetName(),
337                           true, false, true, cs->IsStwCopyStub());
338 #else
339     LLVMIRBuilder builder(&graph, circuit, module_, function, cfg, cs->GetCallConv(), enableLog_, false, cs->GetName(),
340                           false, false, true, cs->IsStwCopyStub());
341 #endif
342     builder.Build();
343 }
344 
GenerateCode(Circuit * circuit,const ControlFlowGraph & graph,const CompilationConfig * cfg,const panda::ecmascript::MethodLiteral * methodLiteral,const JSPandaFile * jsPandaFile,const std::string & methodName,const FrameType frameType,bool enableOptInlining,bool enableOptBranchProfiling)345 void LLVMIRGeneratorImpl::GenerateCode(Circuit *circuit, const ControlFlowGraph &graph, const CompilationConfig *cfg,
346                                        const panda::ecmascript::MethodLiteral *methodLiteral,
347                                        const JSPandaFile *jsPandaFile, const std::string &methodName,
348                                        const FrameType frameType, bool enableOptInlining, bool enableOptBranchProfiling)
349 {
350     auto function = module_->AddFunc(methodLiteral, jsPandaFile);
351     circuit->SetFrameType(frameType);
352     CallSignature::CallConv conv;
353     if (methodLiteral->IsFastCall()) {
354         conv = CallSignature::CallConv::CCallConv;
355     } else {
356         conv = CallSignature::CallConv::WebKitJSCallConv;
357     }
358     LLVMIRBuilder builder(&graph, circuit, module_, function, cfg, conv,
359                           enableLog_, methodLiteral->IsFastCall(), methodName,
360                           false, enableOptInlining, enableOptBranchProfiling);
361     builder.Build();
362 }
363 
RoundTripAllocateCodeSection(void * object,uintptr_t size,unsigned alignment,unsigned sectionID,const char * sectionName)364 static uint8_t *RoundTripAllocateCodeSection(void *object, uintptr_t size, [[maybe_unused]] unsigned alignment,
365                                              [[maybe_unused]] unsigned sectionID, const char *sectionName)
366 {
367     struct CodeInfo& state = *static_cast<struct CodeInfo*>(object);
368     return state.AllocaCodeSection(size, sectionName);
369 }
370 
RoundTripAllocateCodeSectionOnDemand(void * object,uintptr_t size,unsigned alignment,unsigned sectionID,const char * sectionName)371 static uint8_t *RoundTripAllocateCodeSectionOnDemand(void *object, uintptr_t size, [[maybe_unused]] unsigned alignment,
372                                                      [[maybe_unused]] unsigned sectionID, const char *sectionName)
373 {
374     struct CodeInfo& state = *static_cast<struct CodeInfo*>(object);
375     return state.AllocaCodeSectionOnDemand(size, sectionName);
376 }
377 
RoundTripAllocateDataSection(void * object,uintptr_t size,unsigned alignment,unsigned sectionID,const char * sectionName,LLVMBool isReadOnly)378 static uint8_t *RoundTripAllocateDataSection(void *object, uintptr_t size, [[maybe_unused]] unsigned alignment,
379                                              [[maybe_unused]] unsigned sectionID, const char *sectionName,
380                                              [[maybe_unused]] LLVMBool isReadOnly)
381 {
382     struct CodeInfo& state = *static_cast<struct CodeInfo*>(object);
383     return state.AllocaDataSection(size, sectionName);
384 }
385 
RoundTripAllocateDataSectionOnDemand(void * object,uintptr_t size,unsigned alignment,unsigned sectionID,const char * sectionName,LLVMBool isReadOnly)386 static uint8_t *RoundTripAllocateDataSectionOnDemand(void *object, uintptr_t size, [[maybe_unused]] unsigned alignment,
387                                                      [[maybe_unused]] unsigned sectionID, const char *sectionName,
388                                                      [[maybe_unused]] LLVMBool isReadOnly)
389 {
390     ASSERT(object != nullptr);
391     struct CodeInfo& state = *static_cast<struct CodeInfo*>(object);
392     return state.AllocaDataSectionOnDemand(size, sectionName);
393 }
394 
RoundTripFinalizeMemory(void * object,char ** errMsg)395 static LLVMBool RoundTripFinalizeMemory([[maybe_unused]] void *object, [[maybe_unused]] char **errMsg)
396 {
397     return 0;
398 }
399 
RoundTripDestroy(void * object)400 static void RoundTripDestroy([[maybe_unused]] void *object)
401 {
402     return;
403 }
404 
UseRoundTripSectionMemoryManager(bool isJit)405 void LLVMAssembler::UseRoundTripSectionMemoryManager(bool isJit)
406 {
407     auto sectionMemoryManager = std::make_unique<llvm::SectionMemoryManager>();
408     options_.MCJMM = LLVMCreateSimpleMCJITMemoryManager(
409         &codeInfo_, isJit ? RoundTripAllocateCodeSectionOnDemand : RoundTripAllocateCodeSection,
410         isJit ? RoundTripAllocateDataSectionOnDemand : RoundTripAllocateDataSection, RoundTripFinalizeMemory,
411         RoundTripDestroy);
412 }
413 
BuildMCJITEngine()414 bool LLVMAssembler::BuildMCJITEngine()
415 {
416     LLVMBool ret = LLVMCreateMCJITCompilerForModule(&engine_, module_, &options_, sizeof(options_), &error_);
417     if (ret) {
418         LOG_COMPILER(FATAL) << "error_ : " << error_;
419         return false;
420     }
421     llvm::unwrap(engine_)->RegisterJITEventListener(&listener_);
422     return true;
423 }
424 
BuildAndRunPasses()425 void LLVMAssembler::BuildAndRunPasses()
426 {
427     LLVMPassManagerBuilderRef pmBuilder = LLVMPassManagerBuilderCreate();
428     LLVMPassManagerBuilderSetOptLevel(pmBuilder, options_.OptLevel); // using O3 optimization level
429     LLVMPassManagerBuilderSetSizeLevel(pmBuilder, 0);
430     LLVMPassManagerBuilderSetDisableUnrollLoops(pmBuilder, 0);
431 
432     // pass manager creation:rs4gc pass is the only pass in modPass, other opt module-based pass are in modPass1
433     LLVMPassManagerRef funcPass = LLVMCreateFunctionPassManagerForModule(module_);
434     LLVMPassManagerRef modPass = LLVMCreatePassManager();
435     LLVMPassManagerRef modPass1 = LLVMCreatePassManager();
436 
437     // add pass into pass managers
438     LLVMPassManagerBuilderPopulateFunctionPassManager(pmBuilder, funcPass);
439     llvm::unwrap(modPass)->add(LLVMCreateRewriteStatepointsForGCLegacyPass()); // rs4gc pass added
440     LLVMPassManagerBuilderPopulateModulePassManager(pmBuilder, modPass1);
441 
442     LLVMRunPassManager(modPass, module_);
443     LLVMInitializeFunctionPassManager(funcPass);
444     for (LLVMValueRef fn = LLVMGetFirstFunction(module_); fn; fn = LLVMGetNextFunction(fn)) {
445         LLVMRunFunctionPassManager(funcPass, fn);
446     }
447     LLVMFinalizeFunctionPassManager(funcPass);
448     LLVMRunPassManager(modPass1, module_);
449 
450     LLVMPassManagerBuilderDispose(pmBuilder);
451     LLVMDisposePassManager(funcPass);
452     LLVMDisposePassManager(modPass);
453     LLVMDisposePassManager(modPass1);
454 }
455 
BuildAndRunPassesFastMode()456 void LLVMAssembler::BuildAndRunPassesFastMode()
457 {
458     LLVMPassManagerBuilderRef pmBuilder = LLVMPassManagerBuilderCreate();
459     LLVMPassManagerBuilderSetOptLevel(pmBuilder, options_.OptLevel); // using O3 optimization level
460     LLVMPassManagerBuilderSetSizeLevel(pmBuilder, 0);
461 
462     // pass manager creation:rs4gc pass is the only pass in modPass, other opt module-based pass are in modPass1
463     LLVMPassManagerRef funcPass = LLVMCreateFunctionPassManagerForModule(module_);
464     LLVMPassManagerRef modPass = LLVMCreatePassManager();
465 
466     // add pass into pass managers
467     LLVMPassManagerBuilderPopulateFunctionPassManager(pmBuilder, funcPass);
468     llvm::unwrap(modPass)->add(LLVMCreateRewriteStatepointsForGCLegacyPass()); // rs4gc pass added
469 
470     LLVMInitializeFunctionPassManager(funcPass);
471     for (LLVMValueRef fn = LLVMGetFirstFunction(module_); fn; fn = LLVMGetNextFunction(fn)) {
472         LLVMRunFunctionPassManager(funcPass, fn);
473     }
474     LLVMFinalizeFunctionPassManager(funcPass);
475     LLVMRunPassManager(modPass, module_);
476 
477     LLVMPassManagerBuilderDispose(pmBuilder);
478     LLVMDisposePassManager(funcPass);
479     LLVMDisposePassManager(modPass);
480 }
481 
LLVMAssembler(LLVMModule * lm,CodeInfo::CodeSpaceOnDemand & codeSpaceOnDemand,LOptions option,bool isStubCompiler)482 LLVMAssembler::LLVMAssembler(LLVMModule *lm, CodeInfo::CodeSpaceOnDemand &codeSpaceOnDemand, LOptions option,
483                              bool isStubCompiler)
484     : Assembler(codeSpaceOnDemand, isStubCompiler),
485       llvmModule_(lm),
486       module_(llvmModule_->GetModule()),
487       listener_(this)
488 {
489     Initialize(option);
490 }
491 
~LLVMAssembler()492 LLVMAssembler::~LLVMAssembler()
493 {
494     if (engine_ != nullptr) {
495         if (module_ != nullptr) {
496             char *error = nullptr;
497             LLVMRemoveModule(engine_, module_, &module_, &error);
498             if (error != nullptr) {
499                 LLVMDisposeMessage(error);
500             }
501         }
502         LLVMDisposeExecutionEngine(engine_);
503         engine_ = nullptr;
504     }
505     module_ = nullptr;
506     error_ = nullptr;
507 }
508 
Run(const CompilerLog & log,bool fastCompileMode,bool isJit)509 void LLVMAssembler::Run(const CompilerLog &log, bool fastCompileMode, bool isJit)
510 {
511     char *error = nullptr;
512     std::string originName = llvm::unwrap(module_)->getModuleIdentifier() + ".ll";
513     std::string optName = llvm::unwrap(module_)->getModuleIdentifier() + "_opt.ll";
514     if (log.OutputLLIR()) {
515         LLVMPrintModuleToFile(module_, originName.c_str(), &error);
516         std::string errInfo = (error != nullptr) ? error : "";
517         LOG_COMPILER(INFO) << "generate " << originName << " " << errInfo;
518     }
519     LLVMVerifyModule(module_, LLVMAbortProcessAction, &error);
520     LLVMDisposeMessage(error);
521     UseRoundTripSectionMemoryManager(isJit);
522     if (!BuildMCJITEngine()) {
523         return;
524     }
525     llvm::unwrap(engine_)->setProcessAllSections(true);
526     if (fastCompileMode) {
527         BuildAndRunPassesFastMode();
528     } else {
529         BuildAndRunPasses();
530     }
531     if (log.OutputLLIR()) {
532         error = nullptr;
533         LLVMPrintModuleToFile(module_, optName.c_str(), &error);
534         std::string errInfo = (error != nullptr) ? error : "";
535         LOG_COMPILER(INFO) << "generate " << optName << " " << errInfo;
536     }
537 }
538 
Initialize(LOptions option)539 void LLVMAssembler::Initialize(LOptions option)
540 {
541     std::string triple(LLVMGetTarget(module_));
542     if (triple.compare(TARGET_X64) == 0) {
543 #if defined(PANDA_TARGET_MACOS) || !defined(PANDA_TARGET_ARM64)
544         LLVMInitializeX86TargetInfo();
545         LLVMInitializeX86TargetMC();
546         LLVMInitializeX86Disassembler();
547         /* this method must be called, ohterwise "Target does not support MC emission" */
548         LLVMInitializeX86AsmPrinter();
549         LLVMInitializeX86AsmParser();
550         LLVMInitializeX86Target();
551 #endif
552     } else if (triple.compare(TARGET_AARCH64) == 0) {
553         LLVMInitializeAArch64TargetInfo();
554         LLVMInitializeAArch64TargetMC();
555         LLVMInitializeAArch64Disassembler();
556         LLVMInitializeAArch64AsmPrinter();
557         LLVMInitializeAArch64AsmParser();
558         LLVMInitializeAArch64Target();
559     } else {
560         LOG_ECMA(FATAL) << "this branch is unreachable";
561         UNREACHABLE();
562     }
563 
564     LLVMLinkAllBuiltinGCs();
565     LLVMInitializeMCJITCompilerOptions(&options_, sizeof(options_));
566     options_.OptLevel = option.optLevel;
567     // NOTE: Just ensure that this field still exists for PIC option
568     options_.RelMode = static_cast<LLVMRelocMode>(option.relocMode);
569     options_.NoFramePointerElim = static_cast<int32_t>(option.genFp);
570     options_.CodeModel = LLVMCodeModelSmall;
571 }
572 
SymbolLookupCallback(void * disInfo,uint64_t referenceValue,uint64_t * referenceType,uint64_t referencePC,const char ** referenceName)573 static const char *SymbolLookupCallback([[maybe_unused]] void *disInfo, [[maybe_unused]] uint64_t referenceValue,
574                                         uint64_t *referenceType, [[maybe_unused]] uint64_t referencePC,
575                                         [[maybe_unused]] const char **referenceName)
576 {
577     *referenceType = LLVMDisassembler_ReferenceType_InOut_None;
578     return nullptr;
579 }
580 
GetCalleeReg2Offset(LLVMValueRef fn,const CompilerLog & log)581 kungfu::CalleeRegAndOffsetVec LLVMAssembler::GetCalleeReg2Offset(LLVMValueRef fn, const CompilerLog &log)
582 {
583     kungfu::CalleeRegAndOffsetVec info;
584     llvm::Function* func = llvm::unwrap<llvm::Function>(fn);
585     ASSERT(func != nullptr);
586 #if defined(PANDA_TARGET_MACOS)
587     for (const auto &Attr : func->getAttributes().getFnAttributes()) {
588 #else
589     for (const auto &Attr : func->getAttributes().getFnAttrs()) {
590 #endif
591         if (Attr.isStringAttribute()) {
592             std::string str = std::string(Attr.getKindAsString().data());
593             std::string expectedKey = "DwarfReg";
594             size_t keySZ = expectedKey.size();
595             size_t strSZ = str.size();
596             if (strSZ >= keySZ && str.substr(0, keySZ) == expectedKey) {
597                 int RegNum = std::stoi(str.substr(keySZ, strSZ - keySZ));
598                 auto value = std::stoi(std::string(Attr.getValueAsString()));
599                 info.push_back(std::make_pair(RegNum, value));
600                 (void)log;
601             }
602         }
603     }
604     return info;
605 }
606 
607 int LLVMAssembler::GetFpDeltaPrevFramSp(LLVMValueRef fn, const CompilerLog &log)
608 {
609     int fpToCallerSpDelta = 0;
610     const char attrKey[] = "fpToCallerSpDelta"; // this key must consistent with llvm backend.
611     LLVMAttributeRef attrirbuteRef = LLVMGetStringAttributeAtIndex(fn, llvm::AttributeList::FunctionIndex,
612                                                                    attrKey, strlen(attrKey));
613     if (attrirbuteRef) {
614         llvm::Attribute attr = llvm::unwrap(attrirbuteRef);
615         auto value = attr.getValueAsString().data();
616         fpToCallerSpDelta = atoi(value);
617         if (log.AllMethod()) {
618             size_t length;
619             LOG_COMPILER(DEBUG) << " funcName: " << LLVMGetValueName2(fn, &length) << " fpToCallerSpDelta:"
620             << fpToCallerSpDelta;
621         }
622     }
623     return fpToCallerSpDelta;
624 }
625 
626 static uint32_t GetInstrValue(size_t instrSize, uint8_t *instrAddr)
627 {
628     uint32_t value = 0;
629     if (instrSize <= sizeof(uint32_t)) {
630         if (memcpy_s(&value, sizeof(uint32_t), instrAddr, instrSize) != EOK) {
631             LOG_FULL(FATAL) << "memcpy_s failed";
632             UNREACHABLE();
633         }
634     }
635     return value;
636 }
637 
638 void LLVMAssembler::PrintInstAndStep(uint64_t &instrOffset, uint8_t **instrAddr, uintptr_t &numBytes,
639                                      size_t instSize, uint64_t textOffset, char *outString,
640                                      std::ostringstream &codeStream, bool logFlag)
641 {
642     if (instSize == 0) {
643         instSize = 4; // 4: default instruction step size while instruction can't be resolved or be constant
644     }
645     if (logFlag) {
646         uint64_t unitedInstOffset = instrOffset + textOffset;
647         // 8: length of output content
648         codeStream << std::setw(8) << std::setfill('0') << std::hex << unitedInstOffset << ":" << std::setw(8)
649                            << GetInstrValue(instSize, *instrAddr) << " " << outString << std::endl;
650     }
651     instrOffset += instSize;
652     *instrAddr += instSize;
653     numBytes -= instSize;
654 }
655 
656 void LLVMAssembler::Disassemble(const std::map<uintptr_t, std::string> *addr2name,
657                                 const std::string& triple, uint8_t *buf, size_t size)
658 {
659     LLVMModuleRef module = LLVMModuleCreateWithName("Emit");
660     LLVMSetTarget(module, triple.c_str());
661     LLVMDisasmContextRef ctx = LLVMCreateDisasm(LLVMGetTarget(module), nullptr, 0, nullptr, SymbolLookupCallback);
662     if (!ctx) {
663         LOG_COMPILER(ERROR) << "ERROR: Couldn't create disassembler for triple!";
664         return;
665     }
666     uint8_t *instrAddr = buf;
667     uint64_t bufAddr = reinterpret_cast<uint64_t>(buf);
668     size_t numBytes = size;
669     uint64_t instrOffset = 0;
670     const size_t outStringSize = 256;
671     char outString[outStringSize];
672     std::ostringstream codeStream;
673     while (numBytes > 0) {
674         uint64_t addr = reinterpret_cast<uint64_t>(instrAddr) - bufAddr;
675         if (addr2name != nullptr && addr2name->find(addr) != addr2name->end()) {
676             std::string methodName = addr2name->at(addr);
677             codeStream << "------------------- asm code [" << methodName << "] -------------------"
678                        << std::endl;
679         }
680         size_t instSize = LLVMDisasmInstruction(ctx, instrAddr, numBytes, instrOffset, outString, outStringSize);
681         PrintInstAndStep(instrOffset, &instrAddr, numBytes, instSize, 0, outString, codeStream);
682     }
683     LOG_ECMA(INFO) << "\n" << codeStream.str();
684     LLVMDisasmDispose(ctx);
685 }
686 
687 static void DecodeDebugInfo(uint64_t addr, uint64_t secIndex, char* outString, size_t outStringSize,
688                             DWARFContext *ctx, LLVMModule* module, const std::string &funcName)
689 {
690     object::SectionedAddress secAddr = {addr, secIndex};
691     DILineInfoSpecifier spec;
692     spec.FNKind = DINameKind::ShortName;
693 
694     DILineInfo info = ctx->getLineInfoForAddress(secAddr, spec);
695     if (info && info.Line > 0) {
696         std::string debugInfo = "\t\t;";
697         debugInfo += module->GetDebugInfo()->GetComment(funcName, info.Line - 1);
698         size_t len = strlen(outString);
699         if (len + debugInfo.size() < outStringSize) {
700             if (strcpy_s(outString + len, outStringSize - len, debugInfo.c_str()) != EOK) {
701                 LOG_FULL(FATAL) << "strcpy_s failed";
702                 UNREACHABLE();
703             }
704         }
705     }
706 }
707 
708 uint64_t LLVMAssembler::GetTextSectionIndex() const
709 {
710     uint64_t index = 0;
711     for (object::section_iterator it = objFile_->section_begin(); it != objFile_->section_end(); ++it) {
712         auto name = it->getName();
713         if (name) {
714             std::string str = name->str();
715             if (str == ".text") {
716                 index = it->getIndex();
717                 ASSERT(it->isText());
718                 break;
719             }
720         }
721     }
722     return index;
723 }
724 
725 void LLVMAssembler::Disassemble(const std::map<uintptr_t, std::string> &addr2name, uint64_t textOffset,
726                                 const CompilerLog &log, const MethodLogList &logList,
727                                 std::ostringstream &codeStream) const
728 {
729     const uint64_t textSecIndex = GetTextSectionIndex();
730     LLVMDisasmContextRef disCtx = LLVMCreateDisasm(LLVMGetTarget(module_), nullptr, 0, nullptr, SymbolLookupCallback);
731     bool logFlag = false;
732     std::unique_ptr<DWARFContext> dwarfCtx = DWARFContext::create(*objFile_);
733 
734     for (auto it : codeInfo_.GetCodeInfo()) {
735         uint8_t *instrAddr = it.first;
736         size_t numBytes = it.second;
737         uint64_t instrOffset = 0;
738 
739         const size_t outStringSize = 512;
740         char outString[outStringSize] = {'\0'};
741         std::string methodName;
742 
743         while (numBytes > 0) {
744             uint64_t addr = reinterpret_cast<uint64_t>(instrAddr);
745             if (addr2name.find(addr) != addr2name.end()) {
746                 methodName = addr2name.at(addr);
747                 logFlag = log.OutputASM();
748                 if (log.CertainMethod()) {
749                     logFlag = logFlag && logList.IncludesMethod(methodName);
750                 } else if (log.NoneMethod()) {
751                     logFlag = false;
752                 }
753                 if (logFlag) {
754                     codeStream << "------------------- asm code [" << methodName << "] -------------------"
755                                << std::endl;
756                 }
757             }
758 
759             size_t instSize = LLVMDisasmInstruction(disCtx, instrAddr, numBytes, instrOffset, outString, outStringSize);
760             DecodeDebugInfo(instrOffset, textSecIndex, outString, outStringSize,
761                             dwarfCtx.get(), llvmModule_, methodName);
762             PrintInstAndStep(instrOffset, &instrAddr, numBytes, instSize, textOffset, outString, codeStream, logFlag);
763         }
764     }
765     LLVMDisasmDispose(disCtx);
766 }
767 }  // namespace panda::ecmascript::kungfu
768