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