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 "llvm_codegen.h"
17 #include <string>
18 #include <vector>
19 #include "ecmascript/compiler/compiler_macros.h"
20 #include "ecmascript/compiler/stub-inl.h"
21 #include "ecmascript/ecma_macros.h"
22 #include "ecmascript/object_factory.h"
23 #include "llvm-c/Analysis.h"
24 #include "llvm-c/Core.h"
25 #include "llvm-c/Disassembler.h"
26 #include "llvm-c/DisassemblerTypes.h"
27 #include "llvm-c/Target.h"
28 #include "llvm-c/Transforms/PassManagerBuilder.h"
29 #include "llvm-c/Transforms/Scalar.h"
30 #include "llvm/ADT/APInt.h"
31 #include "llvm/CodeGen/BuiltinGCs.h"
32 #include "llvm/ExecutionEngine/ExecutionEngine.h"
33 #include "llvm/ExecutionEngine/GenericValue.h"
34 #include "llvm/IR/Argument.h"
35 #include "llvm/IR/BasicBlock.h"
36 #include "llvm/IR/Constants.h"
37 #include "llvm/IR/DerivedTypes.h"
38 #include "llvm/IR/Function.h"
39 #include "llvm/IR/InstrTypes.h"
40 #include "llvm/IR/Instructions.h"
41 #include "llvm/IR/LegacyPassManager.h"
42 #include "llvm/IR/LLVMContext.h"
43 #include "llvm/IR/Module.h"
44 #include "llvm/IR/Type.h"
45 #include "llvm/IR/Verifier.h"
46 #include "llvm/IRReader/IRReader.h"
47 #include "llvm/Support/Casting.h"
48 #include "llvm/Support/Debug.h"
49 #include "llvm/Support/Host.h"
50 #include "llvm/Support/TargetSelect.h"
51 #include "llvm/Transforms/Scalar.h"
52 #include "llvm/llvm_stackmap_parser.h"
53 #include "stub_descriptor.h"
54
55 using namespace panda::ecmascript;
56 namespace panda::ecmascript::kungfu {
GenerateCodeForStub(Circuit * circuit,const ControlFlowGraph & graph,int index,const CompilationConfig * cfg)57 void LLVMIRGeneratorImpl::GenerateCodeForStub(Circuit *circuit, const ControlFlowGraph &graph, int index,
58 const CompilationConfig *cfg)
59 {
60 LLVMValueRef function = module_->GetStubFunction(index);
61 LLVMIRBuilder builder(&graph, circuit, module_, function, cfg);
62 builder.Build();
63 }
64
AssembleModule()65 void LLVMModuleAssembler::AssembleModule()
66 {
67 assembler_.Run();
68 }
69
AssembleStubModule(StubModule * module)70 void LLVMModuleAssembler::AssembleStubModule(StubModule *module)
71 {
72 auto codeBuff = reinterpret_cast<uintptr_t>(assembler_.GetCodeBuffer());
73 auto engine = assembler_.GetEngine();
74 std::map<uint64_t, std::string> addr2name;
75 for (int i = 0; i < ALL_STUB_MAXCOUNT; i++) {
76 auto stubfunction = stubmodule_->GetStubFunction(i);
77 #ifndef NDEBUG
78 COMPILER_LOG(DEBUG) << " AssembleStubModule :" << i << " th " << std::endl;
79 #endif
80 if (stubfunction != nullptr) {
81 uintptr_t stubEntry = reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(engine, stubfunction));
82 module->SetStubEntry(i, stubEntry - codeBuff);
83 if (i >= FAST_STUB_MAXCOUNT) {
84 addr2name[stubEntry] = FastStubDescriptors::GetInstance()
85 .GetStubDescriptor(CallStubId::NAME_BytecodeHandler)->GetName();
86 } else {
87 addr2name[stubEntry] = FastStubDescriptors::GetInstance().GetStubDescriptor(i)->GetName();
88 }
89 #ifndef NDEBUG
90 COMPILER_LOG(DEBUG) << "name : " << addr2name[codeBuff] << std::endl;
91 #endif
92 }
93 }
94 module->SetHostCodeSectionAddr(codeBuff);
95 // stackmaps ptr and size
96 module->SetStackMapAddr(reinterpret_cast<uintptr_t>(assembler_.GetStackMapsSection()));
97 module->SetStackMapSize(assembler_.GetStackMapsSize());
98 #ifndef NDEBUG
99 assembler_.Disassemble(addr2name);
100 #endif
101 }
102
RoundTripAllocateCodeSection(void * object,uintptr_t size,unsigned alignment,unsigned sectionID,const char * sectionName)103 static uint8_t *RoundTripAllocateCodeSection(void *object, uintptr_t size, [[maybe_unused]] unsigned alignment,
104 [[maybe_unused]] unsigned sectionID, const char *sectionName)
105 {
106 COMPILER_LOG(DEBUG) << "RoundTripAllocateCodeSection object " << object << " - ";
107 struct CodeInfo& state = *static_cast<struct CodeInfo*>(object);
108 uint8_t *addr = state.AllocaCodeSection(size, sectionName);
109 COMPILER_LOG(DEBUG) << "RoundTripAllocateCodeSection addr:" << std::hex <<
110 reinterpret_cast<std::uintptr_t>(addr) << addr << " size:0x" << size << " + ";
111 return addr;
112 }
113
RoundTripAllocateDataSection(void * object,uintptr_t size,unsigned alignment,unsigned sectionID,const char * sectionName,LLVMBool isReadOnly)114 static uint8_t *RoundTripAllocateDataSection(void *object, uintptr_t size, [[maybe_unused]] unsigned alignment,
115 [[maybe_unused]] unsigned sectionID, const char *sectionName,
116 [[maybe_unused]] LLVMBool isReadOnly)
117 {
118 struct CodeInfo& state = *static_cast<struct CodeInfo*>(object);
119 return state.AllocaDataSection(size, sectionName);
120 }
121
RoundTripFinalizeMemory(void * object,char ** errMsg)122 static LLVMBool RoundTripFinalizeMemory(void *object, [[maybe_unused]] char **errMsg)
123 {
124 COMPILER_LOG(DEBUG) << "RoundTripFinalizeMemory object " << object << " - ";
125 return 0;
126 }
127
RoundTripDestroy(void * object)128 static void RoundTripDestroy(void *object)
129 {
130 COMPILER_LOG(DEBUG) << "RoundTripDestroy object " << object << " - ";
131 }
132
UseRoundTripSectionMemoryManager()133 void LLVMAssembler::UseRoundTripSectionMemoryManager()
134 {
135 auto sectionMemoryManager = std::make_unique<llvm::SectionMemoryManager>();
136 options_.MCJMM =
137 LLVMCreateSimpleMCJITMemoryManager(&codeInfo_, RoundTripAllocateCodeSection,
138 RoundTripAllocateDataSection, RoundTripFinalizeMemory, RoundTripDestroy);
139 }
140
BuildMCJITEngine()141 bool LLVMAssembler::BuildMCJITEngine()
142 {
143 COMPILER_LOG(DEBUG) << " BuildMCJITEngine - ";
144 LLVMBool ret = LLVMCreateMCJITCompilerForModule(&engine_, module_, &options_, sizeof(options_), &error_);
145 if (ret) {
146 LOG_ECMA(FATAL) << "error_ : " << error_;
147 return false;
148 }
149 COMPILER_LOG(DEBUG) << " BuildMCJITEngine + ";
150 return true;
151 }
152
153 // rewrite patchpoint id value that is planned to be stored into JS thread temporarily
RewritePatchPointIdStoredOnThread(LLVMValueRef instruction,uint64_t id)154 void LLVMAssembler::RewritePatchPointIdStoredOnThread(LLVMValueRef instruction, uint64_t id)
155 {
156 LLVMValueRef targetInst = LLVMGetPreviousInstruction(instruction);
157 while (targetInst != nullptr) {
158 if (LLVMGetInstructionOpcode(targetInst) == LLVMStore) {
159 size_t len = 0;
160 std::string destName(LLVMGetValueName2(LLVMGetOperand(targetInst, 1), &len));
161 std::string targetName("getFramePatchPointIdAddr");
162 // find the store instruction that planned to change getFramePatchPointIdAddr and rewrite src value
163 if (len >= targetName.size() && destName.substr(0, targetName.size()) == targetName) {
164 LLVMSetOperand(targetInst, 0, LLVMConstInt(LLVMInt64Type(), id, 0));
165 }
166 }
167 targetInst = LLVMGetPreviousInstruction(targetInst);
168 }
169 }
170
RewritePatchPointIdOfStatePoint(LLVMValueRef instruction,uint64_t & callInsNum,uint64_t & funcNum)171 void LLVMAssembler::RewritePatchPointIdOfStatePoint(LLVMValueRef instruction, uint64_t &callInsNum, uint64_t &funcNum)
172 {
173 if (LLVMIsACallInst(instruction) && !LLVMIsAIntrinsicInst(instruction)) {
174 const char attrName[] = "statepoint-id";
175 uint64_t id = (funcNum << 16) | callInsNum;
176 std::string patchId = std::to_string(id);
177 LLVMAttributeRef attr = LLVMCreateStringAttribute(LLVMGetGlobalContext(),
178 attrName, sizeof(attrName) - 1, patchId.c_str(), patchId.size());
179 LLVMAddCallSiteAttribute(instruction, LLVMAttributeFunctionIndex, attr);
180 callInsNum++;
181 if (LLVMWebKitJSCallConv == LLVMGetInstructionCallConv(instruction)) {
182 // 2 : 2 means patch id arguments order
183 LLVMSetOperand(instruction, 2, LLVMConstInt(LLVMInt64Type(), id, 0));
184 } else {
185 RewritePatchPointIdStoredOnThread(instruction, id);
186 }
187 }
188 }
189
FillPatchPointIDs()190 void LLVMAssembler::FillPatchPointIDs()
191 {
192 uint64_t funcNum = 0;
193 LLVMValueRef func = LLVMGetFirstFunction(module_);
194 while (func) {
195 if (LLVMIsDeclaration(func)) {
196 func = LLVMGetNextFunction(func);
197 funcNum++;
198 continue;
199 }
200 uint64_t callInsNum = 0;
201 for (LLVMBasicBlockRef bb = LLVMGetFirstBasicBlock(func); bb; bb = LLVMGetNextBasicBlock(bb)) {
202 for (LLVMValueRef instruction = LLVMGetFirstInstruction(bb); instruction;
203 instruction = LLVMGetNextInstruction(instruction)) {
204 RewritePatchPointIdOfStatePoint(instruction, callInsNum, funcNum);
205 }
206 }
207 func = LLVMGetNextFunction(func);
208 funcNum++;
209 }
210 }
211
BuildAndRunPasses()212 void LLVMAssembler::BuildAndRunPasses()
213 {
214 COMPILER_LOG(DEBUG) << "BuildAndRunPasses - ";
215 LLVMPassManagerBuilderRef pmBuilder = LLVMPassManagerBuilderCreate();
216 LLVMPassManagerBuilderSetOptLevel(pmBuilder, options_.OptLevel); // using O3 optimization level
217 LLVMPassManagerBuilderSetSizeLevel(pmBuilder, 0);
218
219 // pass manager creation:rs4gc pass is the only pass in modPass, other opt module-based pass are in modPass1
220 LLVMPassManagerRef funcPass = LLVMCreateFunctionPassManagerForModule(module_);
221 LLVMPassManagerRef modPass = LLVMCreatePassManager();
222 LLVMPassManagerRef modPass1 = LLVMCreatePassManager();
223
224 // add pass into pass managers
225 LLVMPassManagerBuilderPopulateFunctionPassManager(pmBuilder, funcPass);
226 llvm::unwrap(modPass)->add(llvm::createRewriteStatepointsForGCLegacyPass()); // rs4gc pass added
227 LLVMPassManagerBuilderPopulateModulePassManager(pmBuilder, modPass1);
228
229 // run module pass, function pass, module pass1
230 FillPatchPointIDs(); // add "statepoint-id" callsite attribute for statepoint ID rewrite and rewrite store value
231 LLVMRunPassManager(modPass, module_); // make sure rs4gc pass run first
232 LLVMInitializeFunctionPassManager(funcPass);
233 for (LLVMValueRef fn = LLVMGetFirstFunction(module_); fn; fn = LLVMGetNextFunction(fn)) {
234 LLVMRunFunctionPassManager(funcPass, fn);
235 }
236 LLVMFinalizeFunctionPassManager(funcPass);
237 LLVMRunPassManager(modPass1, module_);
238
239 LLVMPassManagerBuilderDispose(pmBuilder);
240 LLVMDisposePassManager(funcPass);
241 LLVMDisposePassManager(modPass);
242 LLVMDisposePassManager(modPass1);
243 COMPILER_LOG(DEBUG) << "BuildAndRunPasses + ";
244 }
245
LLVMAssembler(LLVMModuleRef module)246 LLVMAssembler::LLVMAssembler(LLVMModuleRef module)
247 : module_(module)
248 {
249 Initialize();
250 }
251
~LLVMAssembler()252 LLVMAssembler::~LLVMAssembler()
253 {
254 if (engine_ != nullptr) {
255 if (module_ != nullptr) {
256 char *error = nullptr;
257 LLVMRemoveModule(engine_, module_, &module_, &error);
258 if (error != nullptr) {
259 LLVMDisposeMessage(error);
260 }
261 }
262 LLVMDisposeExecutionEngine(engine_);
263 engine_ = nullptr;
264 }
265 module_ = nullptr;
266 error_ = nullptr;
267 }
268
Run()269 void LLVMAssembler::Run()
270 {
271 char *error = nullptr;
272 #if ECMASCRIPT_ENABLE_COMPILER_LOG
273 char *info = LLVMPrintModuleToString(module_);
274 COMPILER_LOG(INFO) << "Current Module: " << info;
275 LLVMDisposeMessage(info);
276 #endif
277 LLVMPrintModuleToFile(module_, "stub.ll", &error);
278 LLVMVerifyModule(module_, LLVMAbortProcessAction, &error);
279 LLVMDisposeMessage(error);
280 UseRoundTripSectionMemoryManager();
281 if (!BuildMCJITEngine()) {
282 return;
283 }
284 BuildAndRunPasses();
285 LLVMPrintModuleToFile(module_, "opt_stub.ll", &error);
286 }
287
Initialize()288 void LLVMAssembler::Initialize()
289 {
290 std::string triple(LLVMGetTarget(module_));
291 if (triple.compare("x86_64-unknown-linux-gnu") == 0) {
292 LLVMInitializeX86TargetInfo();
293 LLVMInitializeX86TargetMC();
294 LLVMInitializeX86Disassembler();
295 /* this method must be called, ohterwise "Target does not support MC emission" */
296 LLVMInitializeX86AsmPrinter();
297 LLVMInitializeX86AsmParser();
298 LLVMInitializeX86Target();
299 } else if (triple.compare("aarch64-unknown-linux-gnu") == 0) {
300 LLVMInitializeAArch64TargetInfo();
301 LLVMInitializeAArch64TargetMC();
302 LLVMInitializeAArch64Disassembler();
303 LLVMInitializeAArch64AsmPrinter();
304 LLVMInitializeAArch64AsmParser();
305 LLVMInitializeAArch64Target();
306 } else if (triple.compare("arm-unknown-linux-gnu") == 0) {
307 LLVMInitializeARMTargetInfo();
308 LLVMInitializeARMTargetMC();
309 LLVMInitializeARMDisassembler();
310 LLVMInitializeARMAsmPrinter();
311 LLVMInitializeARMAsmParser();
312 LLVMInitializeARMTarget();
313 } else {
314 UNREACHABLE();
315 }
316 llvm::linkAllBuiltinGCs();
317 LLVMInitializeMCJITCompilerOptions(&options_, sizeof(options_));
318 options_.OptLevel = 3; // opt level 2
319 // Just ensure that this field still exists.
320 #if ECMASCRIPT_ENABLE_INTERPRETER_ASM
321 // tmp for interpreter stub
322 options_.NoFramePointerElim = false;
323 #else
324 options_.NoFramePointerElim = true;
325 #endif
326 options_.CodeModel = LLVMCodeModelSmall;
327 }
328
329 #if ECMASCRIPT_ENABLE_COMPILER_LOG
SymbolLookupCallback(void * disInfo,uint64_t referenceValue,uint64_t * referenceType,uint64_t referencePC,const char ** referenceName)330 static const char *SymbolLookupCallback([[maybe_unused]] void *disInfo, [[maybe_unused]] uint64_t referenceValue,
331 uint64_t *referenceType, [[maybe_unused]]uint64_t referencePC,
332 [[maybe_unused]] const char **referenceName)
333 {
334 *referenceType = LLVMDisassembler_ReferenceType_InOut_None;
335 return nullptr;
336 }
337 #endif
338
Disassemble(std::map<uint64_t,std::string> addr2name) const339 void LLVMAssembler::Disassemble(std::map<uint64_t, std::string> addr2name) const
340 {
341 #if ECMASCRIPT_ENABLE_COMPILER_LOG
342 LLVMDisasmContextRef dcr = LLVMCreateDisasm(LLVMGetTarget(module_), nullptr, 0, nullptr, SymbolLookupCallback);
343 std::cout << "========================================================================" << std::endl;
344 for (auto it : codeInfo_.GetCodeInfo()) {
345 uint8_t *byteSp;
346 uintptr_t numBytes;
347 byteSp = it.first;
348 numBytes = it.second;
349 std::cout << " byteSp:" << std::hex << reinterpret_cast<std::uintptr_t>(byteSp) << " numBytes:0x" << numBytes
350 << std::endl;
351
352 unsigned pc = 0;
353 const char outStringSize = 100;
354 char outString[outStringSize];
355 while (numBytes != 0) {
356 size_t InstSize = LLVMDisasmInstruction(dcr, byteSp, numBytes, pc, outString, outStringSize);
357 if (InstSize == 0) {
358 std::cerr.fill('0');
359 std::cerr.width(8); // 8:fixed hex print width
360 std::cerr << std::hex << pc << ":";
361 std::cerr.width(8); // 8:fixed hex print width
362 std::cerr << std::hex << *reinterpret_cast<uint32_t *>(byteSp) << "maybe constant" << std::endl;
363 pc += 4; // 4 pc length
364 byteSp += 4; // 4 sp offset
365 numBytes -= 4; // 4 num bytes
366 }
367 uint64_t addr = reinterpret_cast<uint64_t>(byteSp);
368 if (addr2name.find(addr) != addr2name.end()) {
369 std::cout << addr2name[addr].c_str() << ":" << std::endl;
370 }
371 std::cerr.fill('0');
372 std::cerr.width(8); // 8:fixed hex print width
373 std::cerr << std::hex << pc << ":";
374 std::cerr.width(8); // 8:fixed hex print width
375 std::cerr << std::hex << *reinterpret_cast<uint32_t *>(byteSp) << " " << outString << std::endl;
376 pc += InstSize;
377 byteSp += InstSize;
378 numBytes -= InstSize;
379 }
380 }
381 std::cout << "========================================================================" << std::endl;
382 LLVMDisasmDispose(dcr);
383 #endif
384 }
385 } // namespace panda::ecmascript::kungfu
386