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