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 #if defined(PANDA_TARGET_MACOS) || defined(PANDA_TARGET_IOS)
18 #include "ecmascript/base/llvm_helper.h"
19 #endif
20
21 #include <cstring>
22 #include <iomanip>
23 #include <vector>
24
25 #if defined(__clang__)
26 #pragma clang diagnostic push
27 #pragma clang diagnostic ignored "-Wshadow"
28 #pragma clang diagnostic ignored "-Wunused-parameter"
29 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
30 #elif defined(__GNUC__)
31 #pragma GCC diagnostic push
32 #pragma GCC diagnostic ignored "-Wshadow"
33 #pragma GCC diagnostic ignored "-Wunused-parameter"
34 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
35 #endif
36
37 #include "llvm-c/Analysis.h"
38 #include "llvm-c/Disassembler.h"
39 #include "llvm-c/DisassemblerTypes.h"
40 #include "llvm-c/Target.h"
41 #include "llvm-c/Transforms/PassManagerBuilder.h"
42 #if defined(PANDA_TARGET_MACOS)
43 #include "llvm/CodeGen/BuiltinGCs.h"
44 #else
45 #include "llvm/IR/BuiltinGCs.h"
46 #endif
47 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
48 #include "llvm/DebugInfo/DIContext.h"
49 #include "llvm/ExecutionEngine/ExecutionEngine.h"
50 #include "llvm/ExecutionEngine/SectionMemoryManager.h"
51 #include "llvm/ExecutionEngine/MCJIT.h"
52 #include "llvm/IR/LegacyPassManager.h"
53 #include "llvm/IR/Verifier.h"
54 #include "llvm/Transforms/Scalar.h"
55
56 #include "ecmascript/compiler/call_signature.h"
57 #include "ecmascript/compiler/compiler_log.h"
58 #include "ecmascript/compiler/debug_info.h"
59 #include "ecmascript/compiler/llvm_ir_builder.h"
60 #include "ecmascript/ecma_macros.h"
61 #include "ecmascript/mem/region.h"
62 #include "ecmascript/object_factory.h"
63 #include "ecmascript/stackmap/llvm_stackmap_parser.h"
64
65 #if defined(__clang__)
66 #pragma clang diagnostic pop
67 #elif defined(__GNUC__)
68 #pragma GCC diagnostic pop
69 #endif
70
71 namespace panda::ecmascript::kungfu {
72 using namespace panda::ecmascript;
73 using namespace llvm;
74
CodeInfo()75 CodeInfo::CodeInfo()
76 {
77 secInfos_.fill(std::make_pair(nullptr, 0));
78 }
79
~CodeInfo()80 CodeInfo::~CodeInfo()
81 {
82 Reset();
83 }
84
GetInstance()85 CodeInfo::CodeSpace *CodeInfo::CodeSpace::GetInstance()
86 {
87 static CodeSpace *codeSpace = new CodeSpace();
88 return codeSpace;
89 }
90
CodeSpace()91 CodeInfo::CodeSpace::CodeSpace()
92 {
93 ASSERT(REQUIRED_SECS_LIMIT == AlignUp(REQUIRED_SECS_LIMIT, PageSize()));
94 reqSecs_ = static_cast<uint8_t *>(PageMap(REQUIRED_SECS_LIMIT, PAGE_PROT_READWRITE).GetMem());
95 if (reqSecs_ == reinterpret_cast<uint8_t *>(-1)) {
96 reqSecs_ = nullptr;
97 }
98 ASSERT(UNREQUIRED_SECS_LIMIT == AlignUp(UNREQUIRED_SECS_LIMIT, PageSize()));
99 unreqSecs_ = static_cast<uint8_t *>(PageMap(UNREQUIRED_SECS_LIMIT, PAGE_PROT_READWRITE).GetMem());
100 if (unreqSecs_ == reinterpret_cast<uint8_t *>(-1)) {
101 unreqSecs_ = nullptr;
102 }
103 }
104
~CodeSpace()105 CodeInfo::CodeSpace::~CodeSpace()
106 {
107 reqBufPos_ = 0;
108 unreqBufPos_ = 0;
109 if (reqSecs_ != nullptr) {
110 PageUnmap(MemMap(reqSecs_, REQUIRED_SECS_LIMIT));
111 }
112 reqSecs_ = nullptr;
113 if (unreqSecs_ != nullptr) {
114 PageUnmap(MemMap(unreqSecs_, UNREQUIRED_SECS_LIMIT));
115 }
116 unreqSecs_ = nullptr;
117 }
118
Alloca(uintptr_t size,bool isReq,size_t alignSize)119 uint8_t *CodeInfo::CodeSpace::Alloca(uintptr_t size, bool isReq, size_t alignSize)
120 {
121 uint8_t *addr = nullptr;
122 auto bufBegin = isReq ? reqSecs_ : unreqSecs_;
123 auto &curPos = isReq ? reqBufPos_ : unreqBufPos_;
124 size_t limit = isReq ? REQUIRED_SECS_LIMIT : UNREQUIRED_SECS_LIMIT;
125 if (curPos + size > limit) {
126 LOG_COMPILER(FATAL) << std::hex << "Alloca Section failed. Current curPos:" << curPos
127 << " plus size:" << size << "exceed limit:" << limit;
128 return nullptr;
129 }
130 if (alignSize > 0) {
131 curPos = AlignUp(curPos, alignSize);
132 }
133 addr = bufBegin + curPos;
134 curPos += size;
135 return addr;
136 }
137
AllocaInReqSecBuffer(uintptr_t size,size_t alignSize)138 uint8_t *CodeInfo::AllocaInReqSecBuffer(uintptr_t size, size_t alignSize)
139 {
140 return CodeSpace::GetInstance()->Alloca(size, true, alignSize);
141 }
142
AllocaInNotReqSecBuffer(uintptr_t size,size_t alignSize)143 uint8_t *CodeInfo::AllocaInNotReqSecBuffer(uintptr_t size, size_t alignSize)
144 {
145 return CodeSpace::GetInstance()->Alloca(size, false, alignSize);
146 }
147
AllocaCodeSection(uintptr_t size,const char * sectionName)148 uint8_t *CodeInfo::AllocaCodeSection(uintptr_t size, const char *sectionName)
149 {
150 uint8_t *addr = nullptr;
151 auto curSec = ElfSection(sectionName);
152 if (curSec.isValidAOTSec()) {
153 if (!alreadyPageAlign_) {
154 addr = AllocaInReqSecBuffer(size, AOTFileInfo::PAGE_ALIGN);
155 alreadyPageAlign_ = true;
156 } else {
157 addr = AllocaInReqSecBuffer(size, AOTFileInfo::TEXT_SEC_ALIGN);
158 }
159 } else {
160 addr = AllocaInReqSecBuffer(size);
161 }
162 codeInfo_.push_back({addr, size});
163 if (curSec.isValidAOTSec()) {
164 secInfos_[curSec.GetIntIndex()] = std::make_pair(addr, size);
165 }
166 return addr;
167 }
168
AllocaDataSection(uintptr_t size,const char * sectionName)169 uint8_t *CodeInfo::AllocaDataSection(uintptr_t size, const char *sectionName)
170 {
171 uint8_t *addr = nullptr;
172 auto curSec = ElfSection(sectionName);
173 // rodata section needs 16 bytes alignment
174 if (curSec.InRodataSection()) {
175 size = AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_REGION));
176 if (!alreadyPageAlign_) {
177 addr = curSec.isSequentialAOTSec() ? AllocaInReqSecBuffer(size, AOTFileInfo::PAGE_ALIGN)
178 : AllocaInNotReqSecBuffer(size, AOTFileInfo::PAGE_ALIGN);
179 alreadyPageAlign_ = true;
180 } else {
181 addr = curSec.isSequentialAOTSec() ? AllocaInReqSecBuffer(size, AOTFileInfo::DATA_SEC_ALIGN)
182 : AllocaInNotReqSecBuffer(size, AOTFileInfo::DATA_SEC_ALIGN);
183 }
184 } else {
185 addr = curSec.isSequentialAOTSec() ? AllocaInReqSecBuffer(size) : AllocaInNotReqSecBuffer(size);
186 }
187 if (curSec.isValidAOTSec()) {
188 secInfos_[curSec.GetIntIndex()] = std::make_pair(addr, size);
189 }
190 return addr;
191 }
192
Reset()193 void CodeInfo::Reset()
194 {
195 codeInfo_.clear();
196 }
197
GetSectionAddr(ElfSecName sec) const198 uint8_t *CodeInfo::GetSectionAddr(ElfSecName sec) const
199 {
200 auto curSection = ElfSection(sec);
201 auto idx = curSection.GetIntIndex();
202 return const_cast<uint8_t *>(secInfos_[idx].first);
203 }
204
GetSectionSize(ElfSecName sec) const205 size_t CodeInfo::GetSectionSize(ElfSecName sec) const
206 {
207 auto curSection = ElfSection(sec);
208 auto idx = curSection.GetIntIndex();
209 return secInfos_[idx].second;
210 }
211
GetCodeInfo() const212 std::vector<std::pair<uint8_t *, uintptr_t>> CodeInfo::GetCodeInfo() const
213 {
214 return codeInfo_;
215 }
216
GenerateCodeForStub(Circuit * circuit,const ControlFlowGraph & graph,size_t index,const CompilationConfig * cfg)217 void LLVMIRGeneratorImpl::GenerateCodeForStub(Circuit *circuit, const ControlFlowGraph &graph, size_t index,
218 const CompilationConfig *cfg)
219 {
220 LLVMValueRef function = module_->GetFunction(index);
221 const CallSignature* cs = module_->GetCSign(index);
222 LLVMIRBuilder builder(&graph, circuit, module_, function, cfg, cs->GetCallConv(), enableLog_, false, cs->GetName());
223 builder.Build();
224 }
225
GenerateCode(Circuit * circuit,const ControlFlowGraph & graph,const CompilationConfig * cfg,const panda::ecmascript::MethodLiteral * methodLiteral,const JSPandaFile * jsPandaFile,const std::string & methodName)226 void LLVMIRGeneratorImpl::GenerateCode(Circuit *circuit, const ControlFlowGraph &graph, const CompilationConfig *cfg,
227 const panda::ecmascript::MethodLiteral *methodLiteral,
228 const JSPandaFile *jsPandaFile, const std::string &methodName)
229 {
230 auto function = module_->AddFunc(methodLiteral, jsPandaFile);
231 circuit->SetFrameType(FrameType::OPTIMIZED_JS_FUNCTION_FRAME);
232 CallSignature::CallConv conv;
233 if (methodLiteral->IsFastCall()) {
234 conv = CallSignature::CallConv::CCallConv;
235 } else {
236 conv = CallSignature::CallConv::WebKitJSCallConv;
237 }
238 LLVMIRBuilder builder(&graph, circuit, module_, function, cfg, conv,
239 enableLog_, methodLiteral->IsFastCall(), methodName);
240 builder.Build();
241 }
242
RoundTripAllocateCodeSection(void * object,uintptr_t size,unsigned alignment,unsigned sectionID,const char * sectionName)243 static uint8_t *RoundTripAllocateCodeSection(void *object, uintptr_t size, [[maybe_unused]] unsigned alignment,
244 [[maybe_unused]] unsigned sectionID, const char *sectionName)
245 {
246 struct CodeInfo& state = *static_cast<struct CodeInfo*>(object);
247 return state.AllocaCodeSection(size, sectionName);
248 }
249
RoundTripAllocateDataSection(void * object,uintptr_t size,unsigned alignment,unsigned sectionID,const char * sectionName,LLVMBool isReadOnly)250 static uint8_t *RoundTripAllocateDataSection(void *object, uintptr_t size, [[maybe_unused]] unsigned alignment,
251 [[maybe_unused]] unsigned sectionID, const char *sectionName,
252 [[maybe_unused]] LLVMBool isReadOnly)
253 {
254 struct CodeInfo& state = *static_cast<struct CodeInfo*>(object);
255 return state.AllocaDataSection(size, sectionName);
256 }
257
RoundTripFinalizeMemory(void * object,char ** errMsg)258 static LLVMBool RoundTripFinalizeMemory([[maybe_unused]] void *object, [[maybe_unused]] char **errMsg)
259 {
260 return 0;
261 }
262
RoundTripDestroy(void * object)263 static void RoundTripDestroy([[maybe_unused]] void *object)
264 {
265 return;
266 }
267
UseRoundTripSectionMemoryManager()268 void LLVMAssembler::UseRoundTripSectionMemoryManager()
269 {
270 auto sectionMemoryManager = std::make_unique<llvm::SectionMemoryManager>();
271 options_.MCJMM =
272 LLVMCreateSimpleMCJITMemoryManager(&codeInfo_, RoundTripAllocateCodeSection,
273 RoundTripAllocateDataSection, RoundTripFinalizeMemory, RoundTripDestroy);
274 }
275
BuildMCJITEngine()276 bool LLVMAssembler::BuildMCJITEngine()
277 {
278 LLVMBool ret = LLVMCreateMCJITCompilerForModule(&engine_, module_, &options_, sizeof(options_), &error_);
279 if (ret) {
280 LOG_COMPILER(FATAL) << "error_ : " << error_;
281 return false;
282 }
283 llvm::unwrap(engine_)->RegisterJITEventListener(&listener_);
284 return true;
285 }
286
BuildAndRunPasses()287 void LLVMAssembler::BuildAndRunPasses()
288 {
289 LLVMPassManagerBuilderRef pmBuilder = LLVMPassManagerBuilderCreate();
290 LLVMPassManagerBuilderSetOptLevel(pmBuilder, options_.OptLevel); // using O3 optimization level
291 LLVMPassManagerBuilderSetSizeLevel(pmBuilder, 0);
292 LLVMPassManagerBuilderSetDisableUnrollLoops(pmBuilder, 0);
293
294 // pass manager creation:rs4gc pass is the only pass in modPass, other opt module-based pass are in modPass1
295 LLVMPassManagerRef funcPass = LLVMCreateFunctionPassManagerForModule(module_);
296 LLVMPassManagerRef modPass = LLVMCreatePassManager();
297 LLVMPassManagerRef modPass1 = LLVMCreatePassManager();
298
299 // add pass into pass managers
300 LLVMPassManagerBuilderPopulateFunctionPassManager(pmBuilder, funcPass);
301 llvm::unwrap(modPass)->add(llvm::createRewriteStatepointsForGCLegacyPass()); // rs4gc pass added
302 LLVMPassManagerBuilderPopulateModulePassManager(pmBuilder, modPass1);
303
304 LLVMRunPassManager(modPass, module_);
305 LLVMInitializeFunctionPassManager(funcPass);
306 for (LLVMValueRef fn = LLVMGetFirstFunction(module_); fn; fn = LLVMGetNextFunction(fn)) {
307 LLVMRunFunctionPassManager(funcPass, fn);
308 }
309 LLVMFinalizeFunctionPassManager(funcPass);
310 LLVMRunPassManager(modPass1, module_);
311
312 LLVMPassManagerBuilderDispose(pmBuilder);
313 LLVMDisposePassManager(funcPass);
314 LLVMDisposePassManager(modPass);
315 LLVMDisposePassManager(modPass1);
316 }
317
BuildAndRunPassesFastMode()318 void LLVMAssembler::BuildAndRunPassesFastMode()
319 {
320 LLVMPassManagerBuilderRef pmBuilder = LLVMPassManagerBuilderCreate();
321 LLVMPassManagerBuilderSetOptLevel(pmBuilder, options_.OptLevel); // using O3 optimization level
322 LLVMPassManagerBuilderSetSizeLevel(pmBuilder, 0);
323
324 // pass manager creation:rs4gc pass is the only pass in modPass, other opt module-based pass are in modPass1
325 LLVMPassManagerRef funcPass = LLVMCreateFunctionPassManagerForModule(module_);
326 LLVMPassManagerRef modPass = LLVMCreatePassManager();
327
328 // add pass into pass managers
329 LLVMPassManagerBuilderPopulateFunctionPassManager(pmBuilder, funcPass);
330 llvm::unwrap(modPass)->add(llvm::createRewriteStatepointsForGCLegacyPass()); // rs4gc pass added
331
332 LLVMInitializeFunctionPassManager(funcPass);
333 for (LLVMValueRef fn = LLVMGetFirstFunction(module_); fn; fn = LLVMGetNextFunction(fn)) {
334 LLVMRunFunctionPassManager(funcPass, fn);
335 }
336 LLVMFinalizeFunctionPassManager(funcPass);
337 LLVMRunPassManager(modPass, module_);
338
339 LLVMPassManagerBuilderDispose(pmBuilder);
340 LLVMDisposePassManager(funcPass);
341 LLVMDisposePassManager(modPass);
342 }
343
LLVMAssembler(LLVMModule * lm,LOptions option)344 LLVMAssembler::LLVMAssembler(LLVMModule *lm, LOptions option)
345 : llvmModule_(lm),
346 module_(llvmModule_->GetModule()),
347 listener_(this)
348 {
349 Initialize(option);
350 }
351
~LLVMAssembler()352 LLVMAssembler::~LLVMAssembler()
353 {
354 if (engine_ != nullptr) {
355 if (module_ != nullptr) {
356 char *error = nullptr;
357 LLVMRemoveModule(engine_, module_, &module_, &error);
358 if (error != nullptr) {
359 LLVMDisposeMessage(error);
360 }
361 }
362 LLVMDisposeExecutionEngine(engine_);
363 engine_ = nullptr;
364 }
365 module_ = nullptr;
366 error_ = nullptr;
367 }
368
Run(const CompilerLog & log,bool fastCompileMode)369 void LLVMAssembler::Run(const CompilerLog &log, bool fastCompileMode)
370 {
371 char *error = nullptr;
372 std::string originName = llvm::unwrap(module_)->getModuleIdentifier() + ".ll";
373 std::string optName = llvm::unwrap(module_)->getModuleIdentifier() + "_opt.ll";
374 if (log.OutputLLIR()) {
375 LLVMPrintModuleToFile(module_, originName.c_str(), &error);
376 std::string errInfo = (error != nullptr) ? error : "";
377 LOG_COMPILER(INFO) << "generate " << originName << " " << errInfo;
378 }
379 LLVMVerifyModule(module_, LLVMAbortProcessAction, &error);
380 LLVMDisposeMessage(error);
381 UseRoundTripSectionMemoryManager();
382 if (!BuildMCJITEngine()) {
383 return;
384 }
385 llvm::unwrap(engine_)->setProcessAllSections(true);
386 if (fastCompileMode) {
387 BuildAndRunPassesFastMode();
388 } else {
389 BuildAndRunPasses();
390 }
391 if (log.OutputLLIR()) {
392 error = nullptr;
393 LLVMPrintModuleToFile(module_, optName.c_str(), &error);
394 std::string errInfo = (error != nullptr) ? error : "";
395 LOG_COMPILER(INFO) << "generate " << optName << " " << errInfo;
396 }
397 }
398
Initialize(LOptions option)399 void LLVMAssembler::Initialize(LOptions option)
400 {
401 std::string triple(LLVMGetTarget(module_));
402 if (triple.compare(TARGET_X64) == 0) {
403 #if defined(PANDA_TARGET_MACOS) || !defined(PANDA_TARGET_ARM64)
404 LLVMInitializeX86TargetInfo();
405 LLVMInitializeX86TargetMC();
406 LLVMInitializeX86Disassembler();
407 /* this method must be called, ohterwise "Target does not support MC emission" */
408 LLVMInitializeX86AsmPrinter();
409 LLVMInitializeX86AsmParser();
410 LLVMInitializeX86Target();
411 #endif
412 } else if (triple.compare(TARGET_AARCH64) == 0) {
413 LLVMInitializeAArch64TargetInfo();
414 LLVMInitializeAArch64TargetMC();
415 LLVMInitializeAArch64Disassembler();
416 LLVMInitializeAArch64AsmPrinter();
417 LLVMInitializeAArch64AsmParser();
418 LLVMInitializeAArch64Target();
419 } else if (triple.compare(TARGET_ARM32) == 0) {
420 #if defined(PANDA_TARGET_MACOS) || !defined(PANDA_TARGET_ARM64)
421 LLVMInitializeARMTargetInfo();
422 LLVMInitializeARMTargetMC();
423 LLVMInitializeARMDisassembler();
424 LLVMInitializeARMAsmPrinter();
425 LLVMInitializeARMAsmParser();
426 LLVMInitializeARMTarget();
427 #endif
428 } else {
429 LOG_ECMA(FATAL) << "this branch is unreachable";
430 UNREACHABLE();
431 }
432
433 llvm::linkAllBuiltinGCs();
434 LLVMInitializeMCJITCompilerOptions(&options_, sizeof(options_));
435 options_.OptLevel = option.optLevel;
436 // NOTE: Just ensure that this field still exists for PIC option
437 options_.RelMode = static_cast<LLVMRelocMode>(option.relocMode);
438 options_.NoFramePointerElim = static_cast<int32_t>(option.genFp);
439 options_.CodeModel = LLVMCodeModelSmall;
440 }
441
SymbolLookupCallback(void * disInfo,uint64_t referenceValue,uint64_t * referenceType,uint64_t referencePC,const char ** referenceName)442 static const char *SymbolLookupCallback([[maybe_unused]] void *disInfo, [[maybe_unused]] uint64_t referenceValue,
443 uint64_t *referenceType, [[maybe_unused]] uint64_t referencePC,
444 [[maybe_unused]] const char **referenceName)
445 {
446 *referenceType = LLVMDisassembler_ReferenceType_InOut_None;
447 return nullptr;
448 }
449
GetCalleeReg2Offset(LLVMValueRef fn,const CompilerLog & log)450 kungfu::CalleeRegAndOffsetVec LLVMAssembler::GetCalleeReg2Offset(LLVMValueRef fn, const CompilerLog &log)
451 {
452 kungfu::CalleeRegAndOffsetVec info;
453 llvm::Function* func = llvm::unwrap<llvm::Function>(fn);
454 #if defined(PANDA_TARGET_MACOS)
455 for (const auto &Attr : func->getAttributes().getFnAttributes()) {
456 #else
457 for (const auto &Attr : func->getAttributes().getFnAttrs()) {
458 #endif
459 if (Attr.isStringAttribute()) {
460 std::string str = std::string(Attr.getKindAsString().data());
461 std::string expectedKey = "DwarfReg";
462 size_t keySZ = expectedKey.size();
463 size_t strSZ = str.size();
464 if (strSZ >= keySZ && str.substr(0, keySZ) == expectedKey) {
465 int RegNum = std::stoi(str.substr(keySZ, strSZ - keySZ));
466 auto value = std::stoi(std::string(Attr.getValueAsString()));
467 info.push_back(std::make_pair(RegNum, value));
468 (void)log;
469 }
470 }
471 }
472 return info;
473 }
474
475 int LLVMAssembler::GetFpDeltaPrevFramSp(LLVMValueRef fn, const CompilerLog &log)
476 {
477 int fpToCallerSpDelta = 0;
478 const char attrKey[] = "fpToCallerSpDelta"; // this key must consistent with llvm backend.
479 LLVMAttributeRef attrirbuteRef = LLVMGetStringAttributeAtIndex(fn, llvm::AttributeList::FunctionIndex,
480 attrKey, strlen(attrKey));
481 if (attrirbuteRef) {
482 llvm::Attribute attr = llvm::unwrap(attrirbuteRef);
483 auto value = attr.getValueAsString().data();
484 fpToCallerSpDelta = atoi(value);
485 if (log.AllMethod()) {
486 size_t length;
487 LOG_COMPILER(DEBUG) << " funcName: " << LLVMGetValueName2(fn, &length) << " fpToCallerSpDelta:"
488 << fpToCallerSpDelta;
489 }
490 }
491 return fpToCallerSpDelta;
492 }
493
494 static uint32_t GetInstrValue(size_t instrSize, uint8_t *instrAddr)
495 {
496 uint32_t value = 0;
497 if (instrSize <= sizeof(uint32_t)) {
498 if (memcpy_s(&value, sizeof(uint32_t), instrAddr, instrSize) != EOK) {
499 LOG_FULL(FATAL) << "memcpy_s failed";
500 UNREACHABLE();
501 }
502 }
503 return value;
504 }
505
506 void LLVMAssembler::PrintInstAndStep(uint64_t &instrOffset, uint8_t **instrAddr, uintptr_t &numBytes,
507 size_t instSize, uint64_t textOffset, char *outString,
508 std::ostringstream &codeStream, bool logFlag)
509 {
510 if (instSize == 0) {
511 instSize = 4; // 4: default instruction step size while instruction can't be resolved or be constant
512 }
513 if (logFlag) {
514 uint64_t unitedInstOffset = instrOffset + textOffset;
515 // 8: length of output content
516 codeStream << std::setw(8) << std::setfill('0') << std::hex << unitedInstOffset << ":" << std::setw(8)
517 << GetInstrValue(instSize, *instrAddr) << " " << outString << std::endl;
518 }
519 instrOffset += instSize;
520 *instrAddr += instSize;
521 numBytes -= instSize;
522 }
523
524 void LLVMAssembler::Disassemble(const std::map<uintptr_t, std::string> *addr2name,
525 const std::string& triple, uint8_t *buf, size_t size)
526 {
527 LLVMModuleRef module = LLVMModuleCreateWithName("Emit");
528 LLVMSetTarget(module, triple.c_str());
529 LLVMDisasmContextRef ctx = LLVMCreateDisasm(LLVMGetTarget(module), nullptr, 0, nullptr, SymbolLookupCallback);
530 if (!ctx) {
531 LOG_COMPILER(ERROR) << "ERROR: Couldn't create disassembler for triple!";
532 return;
533 }
534 uint8_t *instrAddr = buf;
535 uint64_t bufAddr = reinterpret_cast<uint64_t>(buf);
536 size_t numBytes = size;
537 uint64_t instrOffset = 0;
538 const size_t outStringSize = 256;
539 char outString[outStringSize];
540 std::ostringstream codeStream;
541 while (numBytes > 0) {
542 uint64_t addr = reinterpret_cast<uint64_t>(instrAddr) - bufAddr;
543 if (addr2name != nullptr && addr2name->find(addr) != addr2name->end()) {
544 std::string methodName = addr2name->at(addr);
545 codeStream << "------------------- asm code [" << methodName << "] -------------------"
546 << std::endl;
547 }
548 size_t instSize = LLVMDisasmInstruction(ctx, instrAddr, numBytes, instrOffset, outString, outStringSize);
549 PrintInstAndStep(instrOffset, &instrAddr, numBytes, instSize, 0, outString, codeStream);
550 }
551 LOG_ECMA(INFO) << "\n" << codeStream.str();
552 LLVMDisasmDispose(ctx);
553 }
554
555 static void DecodeDebugInfo(uint64_t addr, uint64_t secIndex, char* outString, size_t outStringSize,
556 DWARFContext *ctx, LLVMModule* module, const std::string &funcName)
557 {
558 object::SectionedAddress secAddr = {addr, secIndex};
559 DILineInfoSpecifier spec;
560 spec.FNKind = DINameKind::ShortName;
561
562 DILineInfo info = ctx->getLineInfoForAddress(secAddr, spec);
563 if (info && info.Line > 0) {
564 std::string debugInfo = "\t\t;";
565 debugInfo += module->GetDebugInfo()->GetComment(funcName, info.Line - 1);
566 size_t len = strlen(outString);
567 if (len + debugInfo.size() <= outStringSize) {
568 if (strcpy_s(outString + len, outStringSize - len, debugInfo.c_str()) != EOK) {
569 LOG_FULL(FATAL) << "strcpy_s failed";
570 UNREACHABLE();
571 }
572 }
573 }
574 }
575
576 uint64_t LLVMAssembler::GetTextSectionIndex() const
577 {
578 uint64_t index = 0;
579 for (object::section_iterator it = objFile_->section_begin(); it != objFile_->section_end(); ++it) {
580 auto name = it->getName();
581 if (name) {
582 std::string str = name->str();
583 if (str == ".text") {
584 index = it->getIndex();
585 ASSERT(it->isText());
586 break;
587 }
588 }
589 }
590 return index;
591 }
592
593 void LLVMAssembler::Disassemble(const std::map<uintptr_t, std::string> &addr2name, uint64_t textOffset,
594 const CompilerLog &log, const MethodLogList &logList,
595 std::ostringstream &codeStream) const
596 {
597 const uint64_t textSecIndex = GetTextSectionIndex();
598 LLVMDisasmContextRef disCtx = LLVMCreateDisasm(LLVMGetTarget(module_), nullptr, 0, nullptr, SymbolLookupCallback);
599 bool logFlag = false;
600 std::unique_ptr<DWARFContext> dwarfCtx = DWARFContext::create(*objFile_);
601
602 for (auto it : codeInfo_.GetCodeInfo()) {
603 uint8_t *instrAddr = it.first;
604 size_t numBytes = it.second;
605 uint64_t instrOffset = 0;
606
607 const size_t outStringSize = 256;
608 char outString[outStringSize] = {'\0'};
609 std::string methodName;
610
611 while (numBytes > 0) {
612 uint64_t addr = reinterpret_cast<uint64_t>(instrAddr);
613 if (addr2name.find(addr) != addr2name.end()) {
614 methodName = addr2name.at(addr);
615 logFlag = log.OutputASM();
616 if (log.CertainMethod()) {
617 logFlag = logFlag && logList.IncludesMethod(methodName);
618 } else if (log.NoneMethod()) {
619 logFlag = false;
620 }
621 if (logFlag) {
622 codeStream << "------------------- asm code [" << methodName << "] -------------------"
623 << std::endl;
624 }
625 }
626
627 size_t instSize = LLVMDisasmInstruction(disCtx, instrAddr, numBytes, instrOffset, outString, outStringSize);
628 DecodeDebugInfo(instrOffset, textSecIndex, outString, outStringSize,
629 dwarfCtx.get(), llvmModule_, methodName);
630 PrintInstAndStep(instrOffset, &instrAddr, numBytes, instSize, textOffset, outString, codeStream, logFlag);
631 }
632 }
633 LLVMDisasmDispose(disCtx);
634 }
635 } // namespace panda::ecmascript::kungfu
636