• 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 
18 #include <cstring>
19 #include <iomanip>
20 #include <vector>
21 
22 #if defined(__clang__)
23 #pragma clang diagnostic push
24 #pragma clang diagnostic ignored "-Wshadow"
25 #pragma clang diagnostic ignored "-Wunused-parameter"
26 #elif defined(__GNUC__)
27 #pragma GCC diagnostic push
28 #pragma GCC diagnostic ignored "-Wshadow"
29 #pragma GCC diagnostic ignored "-Wunused-parameter"
30 #endif
31 
32 #include "llvm-c/Analysis.h"
33 #include "llvm-c/Core.h"
34 #include "llvm-c/Disassembler.h"
35 #include "llvm-c/DisassemblerTypes.h"
36 #include "llvm-c/Target.h"
37 #include "llvm-c/Transforms/PassManagerBuilder.h"
38 #include "llvm-c/Transforms/Scalar.h"
39 #include "llvm/ADT/APInt.h"
40 #include "llvm/CodeGen/BuiltinGCs.h"
41 #include "llvm/ExecutionEngine/ExecutionEngine.h"
42 #include "llvm/ExecutionEngine/GenericValue.h"
43 #include "llvm/IR/Argument.h"
44 #include "llvm/IR/BasicBlock.h"
45 #include "llvm/IR/Constants.h"
46 #include "llvm/IR/DerivedTypes.h"
47 #include "llvm/IR/Function.h"
48 #include "llvm/IR/InstrTypes.h"
49 #include "llvm/IR/Instructions.h"
50 #include "llvm/IR/LegacyPassManager.h"
51 #include "llvm/IR/LLVMContext.h"
52 #include "llvm/IR/Module.h"
53 #include "llvm/IR/Type.h"
54 #include "llvm/IR/Verifier.h"
55 #include "llvm/IRReader/IRReader.h"
56 #include "llvm/Support/Casting.h"
57 #include "llvm/Support/Debug.h"
58 #include "llvm/Support/Host.h"
59 #include "llvm/Support/TargetSelect.h"
60 #include "llvm/Transforms/Scalar.h"
61 
62 #include "ecmascript/compiler/call_signature.h"
63 #include "ecmascript/ecma_macros.h"
64 #include "ecmascript/object_factory.h"
65 #include "ecmascript/stackmap/llvm_stackmap_parser.h"
66 
67 #if defined(__clang__)
68 #pragma clang diagnostic pop
69 #elif defined(__GNUC__)
70 #pragma GCC diagnostic pop
71 #endif
72 
73 using namespace panda::ecmascript;
74 namespace panda::ecmascript::kungfu {
GenerateCodeForStub(Circuit * circuit,const ControlFlowGraph & graph,size_t index,const CompilationConfig * cfg)75 void LLVMIRGeneratorImpl::GenerateCodeForStub(Circuit *circuit, const ControlFlowGraph &graph, size_t index,
76                                               const CompilationConfig *cfg)
77 {
78     LLVMValueRef function = module_->GetFunction(index);
79     const CallSignature* cs = module_->GetCSign(index);
80     LLVMIRBuilder builder(&graph, circuit, module_, function, cfg, cs->GetCallConv(), enableLog_);
81     builder.Build();
82 }
83 
GenerateCode(Circuit * circuit,const ControlFlowGraph & graph,const CompilationConfig * cfg,const panda::ecmascript::MethodLiteral * methodLiteral,const JSPandaFile * jsPandaFile)84 void LLVMIRGeneratorImpl::GenerateCode(Circuit *circuit, const ControlFlowGraph &graph, const CompilationConfig *cfg,
85                                        const panda::ecmascript::MethodLiteral *methodLiteral,
86                                        const JSPandaFile *jsPandaFile)
87 {
88     auto function = module_->AddFunc(methodLiteral, jsPandaFile);
89     circuit->SetFrameType(FrameType::OPTIMIZED_JS_FUNCTION_FRAME);
90     LLVMIRBuilder builder(&graph, circuit, module_, function, cfg, CallSignature::CallConv::WebKitJSCallConv,
91                           enableLog_);
92     builder.Build();
93 }
94 
RoundTripAllocateCodeSection(void * object,uintptr_t size,unsigned alignment,unsigned sectionID,const char * sectionName)95 static uint8_t *RoundTripAllocateCodeSection(void *object, uintptr_t size, [[maybe_unused]] unsigned alignment,
96                                              [[maybe_unused]] unsigned sectionID, const char *sectionName)
97 {
98     struct CodeInfo& state = *static_cast<struct CodeInfo*>(object);
99     return state.AllocaCodeSection(size, sectionName);
100 }
101 
RoundTripAllocateDataSection(void * object,uintptr_t size,unsigned alignment,unsigned sectionID,const char * sectionName,LLVMBool isReadOnly)102 static uint8_t *RoundTripAllocateDataSection(void *object, uintptr_t size, [[maybe_unused]] unsigned alignment,
103                                              [[maybe_unused]] unsigned sectionID, const char *sectionName,
104                                              [[maybe_unused]] LLVMBool isReadOnly)
105 {
106     struct CodeInfo& state = *static_cast<struct CodeInfo*>(object);
107     return state.AllocaDataSection(size, sectionName);
108 }
109 
RoundTripFinalizeMemory(void * object,char ** errMsg)110 static LLVMBool RoundTripFinalizeMemory([[maybe_unused]] void *object, [[maybe_unused]] char **errMsg)
111 {
112     return 0;
113 }
114 
RoundTripDestroy(void * object)115 static void RoundTripDestroy([[maybe_unused]] void *object)
116 {
117     return;
118 }
119 
UseRoundTripSectionMemoryManager()120 void LLVMAssembler::UseRoundTripSectionMemoryManager()
121 {
122     auto sectionMemoryManager = std::make_unique<llvm::SectionMemoryManager>();
123     options_.MCJMM =
124         LLVMCreateSimpleMCJITMemoryManager(&codeInfo_, RoundTripAllocateCodeSection,
125             RoundTripAllocateDataSection, RoundTripFinalizeMemory, RoundTripDestroy);
126 }
127 
BuildMCJITEngine()128 bool LLVMAssembler::BuildMCJITEngine()
129 {
130     LLVMBool ret = LLVMCreateMCJITCompilerForModule(&engine_, module_, &options_, sizeof(options_), &error_);
131     if (ret) {
132         LOG_COMPILER(FATAL) << "error_ : " << error_;
133         return false;
134     }
135     return true;
136 }
137 
BuildAndRunPasses()138 void LLVMAssembler::BuildAndRunPasses()
139 {
140     LLVMPassManagerBuilderRef pmBuilder = LLVMPassManagerBuilderCreate();
141     LLVMPassManagerBuilderSetOptLevel(pmBuilder, options_.OptLevel); // using O3 optimization level
142     LLVMPassManagerBuilderSetSizeLevel(pmBuilder, 0);
143 
144     // pass manager creation:rs4gc pass is the only pass in modPass, other opt module-based pass are in modPass1
145     LLVMPassManagerRef funcPass = LLVMCreateFunctionPassManagerForModule(module_);
146     LLVMPassManagerRef modPass = LLVMCreatePassManager();
147 
148     // add pass into pass managers
149     LLVMPassManagerBuilderPopulateFunctionPassManager(pmBuilder, funcPass);
150     llvm::unwrap(modPass)->add(llvm::createRewriteStatepointsForGCLegacyPass()); // rs4gc pass added
151 
152     LLVMInitializeFunctionPassManager(funcPass);
153     for (LLVMValueRef fn = LLVMGetFirstFunction(module_); fn; fn = LLVMGetNextFunction(fn)) {
154         LLVMRunFunctionPassManager(funcPass, fn);
155     }
156     LLVMFinalizeFunctionPassManager(funcPass);
157     LLVMRunPassManager(modPass, module_);
158 
159     LLVMPassManagerBuilderDispose(pmBuilder);
160     LLVMDisposePassManager(funcPass);
161     LLVMDisposePassManager(modPass);
162 }
163 
LLVMAssembler(LLVMModuleRef module,LOptions option)164 LLVMAssembler::LLVMAssembler(LLVMModuleRef module, LOptions option) : module_(module)
165 {
166     Initialize(option);
167 }
168 
~LLVMAssembler()169 LLVMAssembler::~LLVMAssembler()
170 {
171     if (engine_ != nullptr) {
172         if (module_ != nullptr) {
173             char *error = nullptr;
174             LLVMRemoveModule(engine_, module_, &module_, &error);
175             if (error != nullptr) {
176                 LLVMDisposeMessage(error);
177             }
178         }
179         LLVMDisposeExecutionEngine(engine_);
180         engine_ = nullptr;
181     }
182     module_ = nullptr;
183     error_ = nullptr;
184 }
185 
Run(const CompilerLog & log)186 void LLVMAssembler::Run(const CompilerLog &log)
187 {
188     char *error = nullptr;
189     std::string originName = llvm::unwrap(module_)->getModuleIdentifier() + ".ll";
190     std::string optName = llvm::unwrap(module_)->getModuleIdentifier() + "_opt.ll";
191     if (!log.NoneMethod() && log.OutputLLIR()) {
192         LLVMPrintModuleToFile(module_, originName.c_str(), &error);
193         std::string errInfo = (error != nullptr) ? error : "";
194         LOG_COMPILER(INFO) << "generate " << originName << " " << errInfo;
195     }
196     LLVMVerifyModule(module_, LLVMAbortProcessAction, &error);
197     LLVMDisposeMessage(error);
198     UseRoundTripSectionMemoryManager();
199     if (!BuildMCJITEngine()) {
200         return;
201     }
202     llvm::unwrap(engine_)->setProcessAllSections(true);
203     BuildAndRunPasses();
204     if (!log.NoneMethod() && log.OutputLLIR()) {
205         error = nullptr;
206         LLVMPrintModuleToFile(module_, optName.c_str(), &error);
207         std::string errInfo = (error != nullptr) ? error : "";
208         LOG_COMPILER(INFO) << "generate " << optName << " " << errInfo;
209     }
210 }
211 
Initialize(LOptions option)212 void LLVMAssembler::Initialize(LOptions option)
213 {
214     std::string triple(LLVMGetTarget(module_));
215     if (triple.compare("x86_64-unknown-linux-gnu") == 0) {
216 #if defined(PANDA_TARGET_MACOS) || !defined(PANDA_TARGET_ARM64)
217         LLVMInitializeX86TargetInfo();
218         LLVMInitializeX86TargetMC();
219         LLVMInitializeX86Disassembler();
220         /* this method must be called, ohterwise "Target does not support MC emission" */
221         LLVMInitializeX86AsmPrinter();
222         LLVMInitializeX86AsmParser();
223         LLVMInitializeX86Target();
224 #endif
225     } else if (triple.compare("aarch64-unknown-linux-gnu") == 0) {
226         LLVMInitializeAArch64TargetInfo();
227         LLVMInitializeAArch64TargetMC();
228         LLVMInitializeAArch64Disassembler();
229         LLVMInitializeAArch64AsmPrinter();
230         LLVMInitializeAArch64AsmParser();
231         LLVMInitializeAArch64Target();
232     } else if (triple.compare("arm-unknown-linux-gnu") == 0) {
233 #if defined(PANDA_TARGET_MACOS) || !defined(PANDA_TARGET_ARM64)
234         LLVMInitializeARMTargetInfo();
235         LLVMInitializeARMTargetMC();
236         LLVMInitializeARMDisassembler();
237         LLVMInitializeARMAsmPrinter();
238         LLVMInitializeARMAsmParser();
239         LLVMInitializeARMTarget();
240 #endif
241     } else {
242         UNREACHABLE();
243     }
244     llvm::linkAllBuiltinGCs();
245     LLVMInitializeMCJITCompilerOptions(&options_, sizeof(options_));
246     options_.OptLevel = option.optLevel;
247     // NOTE: Just ensure that this field still exists for PIC option
248     options_.RelMode = static_cast<LLVMRelocMode>(option.relocMode);
249     options_.NoFramePointerElim = static_cast<int32_t>(option.genFp);
250     options_.CodeModel = LLVMCodeModelSmall;
251 }
252 
SymbolLookupCallback(void * disInfo,uint64_t referenceValue,uint64_t * referenceType,uint64_t referencePC,const char ** referenceName)253 static const char *SymbolLookupCallback([[maybe_unused]] void *disInfo, [[maybe_unused]] uint64_t referenceValue,
254                                         uint64_t *referenceType, [[maybe_unused]]uint64_t referencePC,
255                                         [[maybe_unused]] const char **referenceName)
256 {
257     *referenceType = LLVMDisassembler_ReferenceType_InOut_None;
258     return nullptr;
259 }
260 
261 
GetCalleeReg2Offset(LLVMValueRef fn,const CompilerLog & log)262 kungfu::CalleeRegAndOffsetVec LLVMAssembler::GetCalleeReg2Offset(LLVMValueRef fn, const CompilerLog &log)
263 {
264     kungfu::CalleeRegAndOffsetVec info;
265     llvm::Function* func = llvm::unwrap<llvm::Function>(fn);
266     for (const auto &Attr : func->getAttributes().getFnAttributes()) {
267         if (Attr.isStringAttribute()) {
268             std::string str = std::string(Attr.getKindAsString().data());
269             std::string expectedKey = "DwarfReg";
270             if (str.size() >= expectedKey.size() &&
271                 str.substr(0, expectedKey.size()) == expectedKey) {
272                 int RegNum = std::stoi(str.substr(expectedKey.size(), str.size() - expectedKey.size()));
273                 auto value = std::stoi(std::string(Attr.getValueAsString()));
274                 info.push_back(std::make_pair(RegNum, value));
275                 (void)log;
276             }
277         }
278     }
279     return info;
280 }
281 
282 
GetFpDeltaPrevFramSp(LLVMValueRef fn,const CompilerLog & log)283 int LLVMAssembler::GetFpDeltaPrevFramSp(LLVMValueRef fn, const CompilerLog &log)
284 {
285     int fpToCallerSpDelta = 0;
286     const char attrKey[] = "fpToCallerSpDelta"; // this key must consistent with llvm backend.
287     LLVMAttributeRef attrirbuteRef = LLVMGetStringAttributeAtIndex(fn,
288         llvm::AttributeList::FunctionIndex, attrKey, strlen(attrKey));
289     if (attrirbuteRef) {
290         llvm::Attribute attr = llvm::unwrap(attrirbuteRef);
291         auto value = attr.getValueAsString().data();
292         fpToCallerSpDelta = atoi(value);
293         if (log.AllMethod()) {
294             size_t length;
295             LOG_COMPILER(DEBUG) << " funcName: " << LLVMGetValueName2(fn, &length) << " fpToCallerSpDelta:"
296             << fpToCallerSpDelta;
297         }
298     }
299     return fpToCallerSpDelta;
300 }
301 
PrintInstAndStep(unsigned & pc,uint8_t ** byteSp,uintptr_t & numBytes,size_t instSize,char * outString,bool logFlag)302 void LLVMAssembler::PrintInstAndStep(unsigned &pc, uint8_t **byteSp, uintptr_t &numBytes,
303     size_t instSize, char *outString, bool logFlag)
304 {
305     if (instSize == 0) {
306         instSize = 4; // 4: default instruction step size while instruction can't be resolved or be constant
307     }
308     if (logFlag) {
309         // 8: length of output content
310         LOG_COMPILER(INFO) << std::setw(8) << std::setfill('0') << std::hex << pc << ":" << std::setw(8)
311                             << *reinterpret_cast<uint32_t *>(*byteSp) << " " << outString;
312     }
313     pc += instSize;
314     *byteSp += instSize;
315     numBytes -= instSize;
316 }
317 
Disassemble(uint8_t * buf,size_t size)318 void LLVMAssembler::Disassemble(uint8_t *buf, size_t size)
319 {
320     std::string triple = "x86_64-unknown-linux-gnu";
321     LLVMModuleRef module = LLVMModuleCreateWithName("Emit");
322     LLVMSetTarget(module, triple.c_str());
323     LLVMDisasmContextRef dcr = LLVMCreateDisasm(LLVMGetTarget(module), nullptr, 0, nullptr, SymbolLookupCallback);
324     if (!dcr) {
325         LOG_COMPILER(ERROR) << "ERROR: Couldn't create disassembler for triple!";
326         return;
327     }
328     uint8_t *byteSp = buf;
329     uintptr_t numBytes = size;
330     unsigned pc = 0;
331     const size_t outStringSize = 128;
332     char outString[outStringSize];
333     while (numBytes > 0) {
334         size_t instSize = LLVMDisasmInstruction(dcr, byteSp, numBytes, pc, outString, outStringSize);
335         PrintInstAndStep(pc, &byteSp, numBytes, instSize, outString);
336     }
337     LLVMDisasmDispose(dcr);
338 }
339 
Disassemble(const std::map<uintptr_t,std::string> & addr2name,const CompilerLog & log,const MethodLogList & logList) const340 void LLVMAssembler::Disassemble(const std::map<uintptr_t, std::string> &addr2name,
341                                 const CompilerLog &log, const MethodLogList &logList) const
342 {
343     LLVMDisasmContextRef dcr = LLVMCreateDisasm(LLVMGetTarget(module_), nullptr, 0, nullptr, SymbolLookupCallback);
344     bool logFlag = false;
345     unsigned pc = 0;
346 
347     for (auto it : codeInfo_.GetCodeInfo()) {
348         uint8_t *byteSp = it.first;
349         uintptr_t numBytes = it.second;
350 
351         const size_t outStringSize = 128;
352         char outString[outStringSize];
353         std::string methodName;
354         while (numBytes > 0) {
355             uint64_t addr = reinterpret_cast<uint64_t>(byteSp);
356             if (addr2name.find(addr) != addr2name.end()) {
357                 methodName = addr2name.at(addr);
358                 logFlag = log.OutputASM();
359                 if (log.CertainMethod()) {
360                     logFlag = logFlag && logList.IncludesMethod(methodName);
361                 } else if (log.NoneMethod()) {
362                     logFlag = false;
363                 }
364                 if (logFlag) {
365                     LOG_COMPILER(INFO) << "\033[34m"
366                                        << "========================  Generated Asm Code ============================="
367                                        << "\033[0m";
368                     LOG_COMPILER(INFO) << "\033[34m" << "aot method [" << methodName << "]:" << "\033[0m";
369                 }
370             }
371 
372             size_t instSize = LLVMDisasmInstruction(dcr, byteSp, numBytes, pc, outString, outStringSize);
373             PrintInstAndStep(pc, &byteSp, numBytes, instSize, outString, logFlag);
374         }
375     }
376     LLVMDisasmDispose(dcr);
377 }
378 }  // namespace panda::ecmascript::kungfu
379