• 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 "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