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