1 //===--- Disasm.cpp - Disassembler for bytecode functions -------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Dump method for Function which disassembles the bytecode. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "Boolean.h" 14 #include "Context.h" 15 #include "EvaluationResult.h" 16 #include "FixedPoint.h" 17 #include "Floating.h" 18 #include "Function.h" 19 #include "FunctionPointer.h" 20 #include "Integral.h" 21 #include "IntegralAP.h" 22 #include "InterpFrame.h" 23 #include "MemberPointer.h" 24 #include "Opcode.h" 25 #include "PrimType.h" 26 #include "Program.h" 27 #include "clang/AST/ASTDumperUtils.h" 28 #include "clang/AST/DeclCXX.h" 29 #include "clang/AST/ExprCXX.h" 30 #include "llvm/Support/Compiler.h" 31 32 using namespace clang; 33 using namespace clang::interp; 34 35 template <typename T> 36 inline static std::string printArg(Program &P, CodePtr &OpPC) { 37 if constexpr (std::is_pointer_v<T>) { 38 uint32_t ID = OpPC.read<uint32_t>(); 39 std::string Result; 40 llvm::raw_string_ostream SS(Result); 41 SS << reinterpret_cast<T>(P.getNativePointer(ID)); 42 return Result; 43 } else { 44 std::string Result; 45 llvm::raw_string_ostream SS(Result); 46 auto Arg = OpPC.read<T>(); 47 SS << Arg; 48 return Result; 49 } 50 } 51 52 template <> inline std::string printArg<Floating>(Program &P, CodePtr &OpPC) { 53 auto Sem = Floating::deserializeSemantics(*OpPC); 54 55 unsigned BitWidth = llvm::APFloatBase::semanticsSizeInBits( 56 llvm::APFloatBase::EnumToSemantics(Sem)); 57 auto Memory = 58 std::make_unique<uint64_t[]>(llvm::APInt::getNumWords(BitWidth)); 59 Floating Result(Memory.get(), Sem); 60 Floating::deserialize(*OpPC, &Result); 61 62 OpPC += align(Result.bytesToSerialize()); 63 64 std::string S; 65 llvm::raw_string_ostream SS(S); 66 SS << std::move(Result); 67 return S; 68 } 69 70 template <> 71 inline std::string printArg<IntegralAP<false>>(Program &P, CodePtr &OpPC) { 72 using T = IntegralAP<false>; 73 uint32_t BitWidth = T::deserializeSize(*OpPC); 74 auto Memory = 75 std::make_unique<uint64_t[]>(llvm::APInt::getNumWords(BitWidth)); 76 77 T Result(Memory.get(), BitWidth); 78 T::deserialize(*OpPC, &Result); 79 80 OpPC += align(Result.bytesToSerialize()); 81 82 std::string Str; 83 llvm::raw_string_ostream SS(Str); 84 SS << std::move(Result); 85 return Str; 86 } 87 88 template <> 89 inline std::string printArg<IntegralAP<true>>(Program &P, CodePtr &OpPC) { 90 using T = IntegralAP<true>; 91 uint32_t BitWidth = T::deserializeSize(*OpPC); 92 auto Memory = 93 std::make_unique<uint64_t[]>(llvm::APInt::getNumWords(BitWidth)); 94 95 T Result(Memory.get(), BitWidth); 96 T::deserialize(*OpPC, &Result); 97 98 OpPC += align(Result.bytesToSerialize()); 99 100 std::string Str; 101 llvm::raw_string_ostream SS(Str); 102 SS << std::move(Result); 103 return Str; 104 } 105 106 template <> inline std::string printArg<FixedPoint>(Program &P, CodePtr &OpPC) { 107 auto F = FixedPoint::deserialize(*OpPC); 108 OpPC += align(F.bytesToSerialize()); 109 110 std::string Result; 111 llvm::raw_string_ostream SS(Result); 112 SS << std::move(F); 113 return Result; 114 } 115 116 static bool isJumpOpcode(Opcode Op) { 117 return Op == OP_Jmp || Op == OP_Jf || Op == OP_Jt; 118 } 119 120 static size_t getNumDisplayWidth(size_t N) { 121 unsigned L = 1u, M = 10u; 122 while (M <= N && ++L != std::numeric_limits<size_t>::digits10 + 1) 123 M *= 10u; 124 125 return L; 126 } 127 128 LLVM_DUMP_METHOD void Function::dump() const { dump(llvm::errs()); } 129 130 LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const { 131 { 132 ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_GREEN, true}); 133 OS << getName() << " " << (const void *)this << "\n"; 134 } 135 OS << "frame size: " << getFrameSize() << "\n"; 136 OS << "arg size: " << getArgSize() << "\n"; 137 OS << "rvo: " << hasRVO() << "\n"; 138 OS << "this arg: " << hasThisPointer() << "\n"; 139 140 struct OpText { 141 size_t Addr; 142 std::string Op; 143 bool IsJump; 144 llvm::SmallVector<std::string> Args; 145 }; 146 147 auto PrintName = [](const char *Name) -> std::string { 148 return std::string(Name); 149 }; 150 151 llvm::SmallVector<OpText> Code; 152 size_t LongestAddr = 0; 153 size_t LongestOp = 0; 154 155 for (CodePtr Start = getCodeBegin(), PC = Start; PC != getCodeEnd();) { 156 size_t Addr = PC - Start; 157 OpText Text; 158 auto Op = PC.read<Opcode>(); 159 Text.Addr = Addr; 160 Text.IsJump = isJumpOpcode(Op); 161 switch (Op) { 162 #define GET_DISASM 163 #include "Opcodes.inc" 164 #undef GET_DISASM 165 } 166 Code.push_back(Text); 167 LongestOp = std::max(Text.Op.size(), LongestOp); 168 LongestAddr = std::max(getNumDisplayWidth(Addr), LongestAddr); 169 } 170 171 // Record jumps and their targets. 172 struct JmpData { 173 size_t From; 174 size_t To; 175 }; 176 llvm::SmallVector<JmpData> Jumps; 177 for (auto &Text : Code) { 178 if (Text.IsJump) 179 Jumps.push_back({Text.Addr, Text.Addr + std::stoi(Text.Args[0]) + 180 align(sizeof(Opcode)) + 181 align(sizeof(int32_t))}); 182 } 183 184 llvm::SmallVector<std::string> Text; 185 Text.reserve(Code.size()); 186 size_t LongestLine = 0; 187 // Print code to a string, one at a time. 188 for (auto C : Code) { 189 std::string Line; 190 llvm::raw_string_ostream LS(Line); 191 LS << C.Addr; 192 LS.indent(LongestAddr - getNumDisplayWidth(C.Addr) + 4); 193 LS << C.Op; 194 LS.indent(LongestOp - C.Op.size() + 4); 195 for (auto &Arg : C.Args) { 196 LS << Arg << ' '; 197 } 198 Text.push_back(Line); 199 LongestLine = std::max(Line.size(), LongestLine); 200 } 201 202 assert(Code.size() == Text.size()); 203 204 auto spaces = [](unsigned N) -> std::string { 205 std::string S; 206 for (unsigned I = 0; I != N; ++I) 207 S += ' '; 208 return S; 209 }; 210 211 // Now, draw the jump lines. 212 for (auto &J : Jumps) { 213 if (J.To > J.From) { 214 bool FoundStart = false; 215 for (size_t LineIndex = 0; LineIndex != Text.size(); ++LineIndex) { 216 Text[LineIndex] += spaces(LongestLine - Text[LineIndex].size()); 217 218 if (Code[LineIndex].Addr == J.From) { 219 Text[LineIndex] += " --+"; 220 FoundStart = true; 221 } else if (Code[LineIndex].Addr == J.To) { 222 Text[LineIndex] += " <-+"; 223 break; 224 } else if (FoundStart) { 225 Text[LineIndex] += " |"; 226 } 227 } 228 LongestLine += 5; 229 } else { 230 bool FoundStart = false; 231 for (ssize_t LineIndex = Text.size() - 1; LineIndex >= 0; --LineIndex) { 232 Text[LineIndex] += spaces(LongestLine - Text[LineIndex].size()); 233 if (Code[LineIndex].Addr == J.From) { 234 Text[LineIndex] += " --+"; 235 FoundStart = true; 236 } else if (Code[LineIndex].Addr == J.To) { 237 Text[LineIndex] += " <-+"; 238 break; 239 } else if (FoundStart) { 240 Text[LineIndex] += " |"; 241 } 242 } 243 LongestLine += 5; 244 } 245 } 246 247 for (auto &Line : Text) 248 OS << Line << '\n'; 249 } 250 251 LLVM_DUMP_METHOD void Program::dump() const { dump(llvm::errs()); } 252 253 static const char *primTypeToString(PrimType T) { 254 switch (T) { 255 case PT_Sint8: 256 return "Sint8"; 257 case PT_Uint8: 258 return "Uint8"; 259 case PT_Sint16: 260 return "Sint16"; 261 case PT_Uint16: 262 return "Uint16"; 263 case PT_Sint32: 264 return "Sint32"; 265 case PT_Uint32: 266 return "Uint32"; 267 case PT_Sint64: 268 return "Sint64"; 269 case PT_Uint64: 270 return "Uint64"; 271 case PT_IntAP: 272 return "IntAP"; 273 case PT_IntAPS: 274 return "IntAPS"; 275 case PT_Bool: 276 return "Bool"; 277 case PT_Float: 278 return "Float"; 279 case PT_Ptr: 280 return "Ptr"; 281 case PT_MemberPtr: 282 return "MemberPtr"; 283 case PT_FixedPoint: 284 return "FixedPoint"; 285 } 286 llvm_unreachable("Unhandled PrimType"); 287 } 288 289 LLVM_DUMP_METHOD void Program::dump(llvm::raw_ostream &OS) const { 290 { 291 ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_RED, true}); 292 OS << "\n:: Program\n"; 293 } 294 295 { 296 ColorScope SC(OS, true, {llvm::raw_ostream::WHITE, true}); 297 OS << "Total memory : " << Allocator.getTotalMemory() << " bytes\n"; 298 OS << "Global Variables: " << Globals.size() << "\n"; 299 } 300 unsigned GI = 0; 301 for (const Global *G : Globals) { 302 const Descriptor *Desc = G->block()->getDescriptor(); 303 Pointer GP = getPtrGlobal(GI); 304 305 OS << GI << ": " << (const void *)G->block() << " "; 306 { 307 ColorScope SC(OS, true, 308 GP.isInitialized() 309 ? TerminalColor{llvm::raw_ostream::GREEN, false} 310 : TerminalColor{llvm::raw_ostream::RED, false}); 311 OS << (GP.isInitialized() ? "initialized " : "uninitialized "); 312 } 313 Desc->dump(OS); 314 315 if (GP.isInitialized() && Desc->IsTemporary) { 316 if (const auto *MTE = 317 dyn_cast_if_present<MaterializeTemporaryExpr>(Desc->asExpr()); 318 MTE && MTE->getLifetimeExtendedTemporaryDecl()) { 319 if (const APValue *V = 320 MTE->getLifetimeExtendedTemporaryDecl()->getValue()) { 321 OS << " (global temporary value: "; 322 { 323 ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_MAGENTA, true}); 324 std::string VStr; 325 llvm::raw_string_ostream SS(VStr); 326 V->dump(SS, Ctx.getASTContext()); 327 328 for (unsigned I = 0; I != VStr.size(); ++I) { 329 if (VStr[I] == '\n') 330 VStr[I] = ' '; 331 } 332 VStr.pop_back(); // Remove the newline (or now space) at the end. 333 OS << VStr; 334 } 335 OS << ')'; 336 } 337 } 338 } 339 340 OS << "\n"; 341 if (GP.isInitialized() && Desc->isPrimitive() && !Desc->isDummy()) { 342 OS << " "; 343 { 344 ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_CYAN, false}); 345 OS << primTypeToString(Desc->getPrimType()) << " "; 346 } 347 TYPE_SWITCH(Desc->getPrimType(), { GP.deref<T>().print(OS); }); 348 OS << "\n"; 349 } 350 ++GI; 351 } 352 353 { 354 ColorScope SC(OS, true, {llvm::raw_ostream::WHITE, true}); 355 OS << "Functions: " << Funcs.size() << "\n"; 356 } 357 for (const auto &Func : Funcs) { 358 Func.second->dump(); 359 } 360 for (const auto &Anon : AnonFuncs) { 361 Anon->dump(); 362 } 363 } 364 365 LLVM_DUMP_METHOD void Descriptor::dump() const { 366 dump(llvm::errs()); 367 llvm::errs() << '\n'; 368 } 369 370 LLVM_DUMP_METHOD void Descriptor::dump(llvm::raw_ostream &OS) const { 371 // Source 372 { 373 ColorScope SC(OS, true, {llvm::raw_ostream::BLUE, true}); 374 if (const auto *ND = dyn_cast_if_present<NamedDecl>(asDecl())) 375 ND->printQualifiedName(OS); 376 else if (asExpr()) 377 OS << "Expr " << (const void *)asExpr(); 378 } 379 380 // Print a few interesting bits about the descriptor. 381 if (isPrimitiveArray()) 382 OS << " primitive-array"; 383 else if (isCompositeArray()) 384 OS << " composite-array"; 385 else if (isUnion()) 386 OS << " union"; 387 else if (isRecord()) 388 OS << " record"; 389 else if (isPrimitive()) 390 OS << " primitive " << primTypeToString(getPrimType()); 391 392 if (isZeroSizeArray()) 393 OS << " zero-size-array"; 394 else if (isUnknownSizeArray()) 395 OS << " unknown-size-array"; 396 397 if (isDummy()) 398 OS << " dummy"; 399 if (IsConstexprUnknown) 400 OS << " constexpr-unknown"; 401 } 402 403 /// Dump descriptor, including all valid offsets. 404 LLVM_DUMP_METHOD void Descriptor::dumpFull(unsigned Offset, 405 unsigned Indent) const { 406 unsigned Spaces = Indent * 2; 407 llvm::raw_ostream &OS = llvm::errs(); 408 OS.indent(Spaces); 409 dump(OS); 410 OS << '\n'; 411 OS.indent(Spaces) << "Metadata: " << getMetadataSize() << " bytes\n"; 412 OS.indent(Spaces) << "Size: " << getSize() << " bytes\n"; 413 OS.indent(Spaces) << "AllocSize: " << getAllocSize() << " bytes\n"; 414 Offset += getMetadataSize(); 415 if (isCompositeArray()) { 416 OS.indent(Spaces) << "Elements: " << getNumElems() << '\n'; 417 unsigned FO = Offset; 418 for (unsigned I = 0; I != getNumElems(); ++I) { 419 FO += sizeof(InlineDescriptor); 420 assert(ElemDesc->getMetadataSize() == 0); 421 OS.indent(Spaces) << "Element " << I << " offset: " << FO << '\n'; 422 ElemDesc->dumpFull(FO, Indent + 1); 423 424 FO += ElemDesc->getAllocSize(); 425 } 426 } else if (isRecord()) { 427 ElemRecord->dump(OS, Indent + 1, Offset); 428 } else if (isPrimitive()) { 429 } else { 430 } 431 432 OS << '\n'; 433 } 434 435 LLVM_DUMP_METHOD void InlineDescriptor::dump(llvm::raw_ostream &OS) const { 436 { 437 ColorScope SC(OS, true, {llvm::raw_ostream::BLUE, true}); 438 OS << "InlineDescriptor " << (const void *)this << "\n"; 439 } 440 OS << "Offset: " << Offset << "\n"; 441 OS << "IsConst: " << IsConst << "\n"; 442 OS << "IsInitialized: " << IsInitialized << "\n"; 443 OS << "IsBase: " << IsBase << "\n"; 444 OS << "IsActive: " << IsActive << "\n"; 445 OS << "InUnion: " << InUnion << "\n"; 446 OS << "IsFieldMutable: " << IsFieldMutable << "\n"; 447 OS << "IsArrayElement: " << IsArrayElement << "\n"; 448 OS << "Desc: "; 449 if (Desc) 450 Desc->dump(OS); 451 else 452 OS << "nullptr"; 453 OS << "\n"; 454 } 455 456 LLVM_DUMP_METHOD void InterpFrame::dump(llvm::raw_ostream &OS, 457 unsigned Indent) const { 458 unsigned Spaces = Indent * 2; 459 { 460 ColorScope SC(OS, true, {llvm::raw_ostream::BLUE, true}); 461 OS.indent(Spaces); 462 if (getCallee()) 463 describe(OS); 464 else 465 OS << "Frame (Depth: " << getDepth() << ")"; 466 OS << "\n"; 467 } 468 OS.indent(Spaces) << "Function: " << getFunction(); 469 if (const Function *F = getFunction()) { 470 OS << " (" << F->getName() << ")"; 471 } 472 OS << "\n"; 473 OS.indent(Spaces) << "This: " << getThis() << "\n"; 474 OS.indent(Spaces) << "RVO: " << getRVOPtr() << "\n"; 475 OS.indent(Spaces) << "Depth: " << Depth << "\n"; 476 OS.indent(Spaces) << "ArgSize: " << ArgSize << "\n"; 477 OS.indent(Spaces) << "Args: " << (void *)Args << "\n"; 478 OS.indent(Spaces) << "FrameOffset: " << FrameOffset << "\n"; 479 OS.indent(Spaces) << "FrameSize: " << (Func ? Func->getFrameSize() : 0) 480 << "\n"; 481 482 for (const InterpFrame *F = this->Caller; F; F = F->Caller) { 483 F->dump(OS, Indent + 1); 484 } 485 } 486 487 LLVM_DUMP_METHOD void Record::dump(llvm::raw_ostream &OS, unsigned Indentation, 488 unsigned Offset) const { 489 unsigned Indent = Indentation * 2; 490 OS.indent(Indent); 491 { 492 ColorScope SC(OS, true, {llvm::raw_ostream::BLUE, true}); 493 OS << getName() << "\n"; 494 } 495 496 unsigned I = 0; 497 for (const Record::Base &B : bases()) { 498 OS.indent(Indent) << "- Base " << I << ". Offset " << (Offset + B.Offset) 499 << "\n"; 500 B.R->dump(OS, Indentation + 1, Offset + B.Offset); 501 ++I; 502 } 503 504 I = 0; 505 for (const Record::Field &F : fields()) { 506 OS.indent(Indent) << "- Field " << I << ": "; 507 { 508 ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_RED, true}); 509 OS << F.Decl->getName(); 510 } 511 OS << ". Offset " << (Offset + F.Offset) << "\n"; 512 ++I; 513 } 514 515 I = 0; 516 for (const Record::Base &B : virtual_bases()) { 517 OS.indent(Indent) << "- Virtual Base " << I << ". Offset " 518 << (Offset + B.Offset) << "\n"; 519 B.R->dump(OS, Indentation + 1, Offset + B.Offset); 520 ++I; 521 } 522 } 523 524 LLVM_DUMP_METHOD void Block::dump(llvm::raw_ostream &OS) const { 525 { 526 ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_BLUE, true}); 527 OS << "Block " << (const void *)this; 528 } 529 OS << " ("; 530 Desc->dump(OS); 531 OS << ")\n"; 532 unsigned NPointers = 0; 533 for (const Pointer *P = Pointers; P; P = P->Next) { 534 ++NPointers; 535 } 536 OS << " EvalID: " << EvalID << '\n'; 537 OS << " DeclID: "; 538 if (DeclID) 539 OS << *DeclID << '\n'; 540 else 541 OS << "-\n"; 542 OS << " Pointers: " << NPointers << "\n"; 543 OS << " Dead: " << IsDead << "\n"; 544 OS << " Static: " << IsStatic << "\n"; 545 OS << " Extern: " << IsExtern << "\n"; 546 OS << " Initialized: " << IsInitialized << "\n"; 547 OS << " Weak: " << IsWeak << "\n"; 548 OS << " Dynamic: " << IsDynamic << "\n"; 549 } 550 551 LLVM_DUMP_METHOD void EvaluationResult::dump() const { 552 assert(Ctx); 553 auto &OS = llvm::errs(); 554 const ASTContext &ASTCtx = Ctx->getASTContext(); 555 556 switch (Kind) { 557 case Empty: 558 OS << "Empty\n"; 559 break; 560 case RValue: 561 OS << "RValue: "; 562 std::get<APValue>(Value).dump(OS, ASTCtx); 563 break; 564 case LValue: { 565 assert(Source); 566 QualType SourceType; 567 if (const auto *D = dyn_cast<const Decl *>(Source)) { 568 if (const auto *VD = dyn_cast<ValueDecl>(D)) 569 SourceType = VD->getType(); 570 } else if (const auto *E = dyn_cast<const Expr *>(Source)) { 571 SourceType = E->getType(); 572 } 573 574 OS << "LValue: "; 575 if (const auto *P = std::get_if<Pointer>(&Value)) 576 P->toAPValue(ASTCtx).printPretty(OS, ASTCtx, SourceType); 577 else if (const auto *FP = std::get_if<FunctionPointer>(&Value)) // Nope 578 FP->toAPValue(ASTCtx).printPretty(OS, ASTCtx, SourceType); 579 OS << "\n"; 580 break; 581 } 582 case Invalid: 583 OS << "Invalid\n"; 584 break; 585 case Valid: 586 OS << "Valid\n"; 587 break; 588 } 589 } 590