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