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