//===--- Disasm.cpp - Disassembler for bytecode functions -------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Dump method for Function which disassembles the bytecode. // //===----------------------------------------------------------------------===// #include "Boolean.h" #include "Context.h" #include "EvaluationResult.h" #include "Floating.h" #include "Function.h" #include "FunctionPointer.h" #include "Integral.h" #include "IntegralAP.h" #include "InterpFrame.h" #include "MemberPointer.h" #include "Opcode.h" #include "PrimType.h" #include "Program.h" #include "clang/AST/ASTDumperUtils.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Format.h" using namespace clang; using namespace clang::interp; template inline T ReadArg(Program &P, CodePtr &OpPC) { if constexpr (std::is_pointer_v) { uint32_t ID = OpPC.read(); return reinterpret_cast(P.getNativePointer(ID)); } else { return OpPC.read(); } } template <> inline Floating ReadArg(Program &P, CodePtr &OpPC) { Floating F = Floating::deserialize(*OpPC); OpPC += align(F.bytesToSerialize()); return F; } template <> inline IntegralAP ReadArg>(Program &P, CodePtr &OpPC) { IntegralAP I = IntegralAP::deserialize(*OpPC); OpPC += align(I.bytesToSerialize()); return I; } template <> inline IntegralAP ReadArg>(Program &P, CodePtr &OpPC) { IntegralAP I = IntegralAP::deserialize(*OpPC); OpPC += align(I.bytesToSerialize()); return I; } LLVM_DUMP_METHOD void Function::dump() const { dump(llvm::errs()); } LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const { { ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_GREEN, true}); OS << getName() << " " << (const void *)this << "\n"; } OS << "frame size: " << getFrameSize() << "\n"; OS << "arg size: " << getArgSize() << "\n"; OS << "rvo: " << hasRVO() << "\n"; OS << "this arg: " << hasThisPointer() << "\n"; auto PrintName = [&OS](const char *Name) { OS << Name; long N = 30 - strlen(Name); if (N > 0) OS.indent(N); }; for (CodePtr Start = getCodeBegin(), PC = Start; PC != getCodeEnd();) { size_t Addr = PC - Start; auto Op = PC.read(); OS << llvm::format("%8d", Addr) << " "; switch (Op) { #define GET_DISASM #include "Opcodes.inc" #undef GET_DISASM } } } LLVM_DUMP_METHOD void Program::dump() const { dump(llvm::errs()); } static const char *primTypeToString(PrimType T) { switch (T) { case PT_Sint8: return "Sint8"; case PT_Uint8: return "Uint8"; case PT_Sint16: return "Sint16"; case PT_Uint16: return "Uint16"; case PT_Sint32: return "Sint32"; case PT_Uint32: return "Uint32"; case PT_Sint64: return "Sint64"; case PT_Uint64: return "Uint64"; case PT_IntAP: return "IntAP"; case PT_IntAPS: return "IntAPS"; case PT_Bool: return "Bool"; case PT_Float: return "Float"; case PT_Ptr: return "Ptr"; case PT_FnPtr: return "FnPtr"; case PT_MemberPtr: return "MemberPtr"; } llvm_unreachable("Unhandled PrimType"); } LLVM_DUMP_METHOD void Program::dump(llvm::raw_ostream &OS) const { { ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_RED, true}); OS << "\n:: Program\n"; } { ColorScope SC(OS, true, {llvm::raw_ostream::WHITE, true}); OS << "Total memory : " << Allocator.getTotalMemory() << " bytes\n"; OS << "Global Variables: " << Globals.size() << "\n"; } unsigned GI = 0; for (const Global *G : Globals) { const Descriptor *Desc = G->block()->getDescriptor(); Pointer GP = getPtrGlobal(GI); OS << GI << ": " << (const void *)G->block() << " "; { ColorScope SC(OS, true, GP.isInitialized() ? TerminalColor{llvm::raw_ostream::GREEN, false} : TerminalColor{llvm::raw_ostream::RED, false}); OS << (GP.isInitialized() ? "initialized " : "uninitialized "); } Desc->dump(OS); if (GP.isInitialized() && Desc->IsTemporary) { if (const auto *MTE = dyn_cast_if_present(Desc->asExpr()); MTE && MTE->getLifetimeExtendedTemporaryDecl()) { if (const APValue *V = MTE->getLifetimeExtendedTemporaryDecl()->getValue()) { OS << " (global temporary value: "; { ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_MAGENTA, true}); std::string VStr; llvm::raw_string_ostream SS(VStr); V->dump(SS, Ctx.getASTContext()); for (unsigned I = 0; I != VStr.size(); ++I) { if (VStr[I] == '\n') VStr[I] = ' '; } VStr.pop_back(); // Remove the newline (or now space) at the end. OS << VStr; } OS << ')'; } } } OS << "\n"; if (GP.isInitialized() && Desc->isPrimitive() && !Desc->isDummy()) { OS << " "; { ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_CYAN, false}); OS << primTypeToString(Desc->getPrimType()) << " "; } TYPE_SWITCH(Desc->getPrimType(), { GP.deref().print(OS); }); OS << "\n"; } ++GI; } { ColorScope SC(OS, true, {llvm::raw_ostream::WHITE, true}); OS << "Functions: " << Funcs.size() << "\n"; } for (const auto &Func : Funcs) { Func.second->dump(); } for (const auto &Anon : AnonFuncs) { Anon->dump(); } } LLVM_DUMP_METHOD void Descriptor::dump() const { dump(llvm::errs()); llvm::errs() << '\n'; } LLVM_DUMP_METHOD void Descriptor::dump(llvm::raw_ostream &OS) const { // Source { ColorScope SC(OS, true, {llvm::raw_ostream::BLUE, true}); if (const auto *ND = dyn_cast_if_present(asDecl())) ND->printQualifiedName(OS); else if (asExpr()) OS << "Expr " << (const void *)asExpr(); } // Print a few interesting bits about the descriptor. if (isPrimitiveArray()) OS << " primitive-array"; else if (isCompositeArray()) OS << " composite-array"; else if (isRecord()) OS << " record"; else if (isPrimitive()) OS << " primitive"; if (isZeroSizeArray()) OS << " zero-size-array"; else if (isUnknownSizeArray()) OS << " unknown-size-array"; if (isDummy()) OS << " dummy"; } LLVM_DUMP_METHOD void InlineDescriptor::dump(llvm::raw_ostream &OS) const { { ColorScope SC(OS, true, {llvm::raw_ostream::BLUE, true}); OS << "InlineDescriptor " << (const void *)this << "\n"; } OS << "Offset: " << Offset << "\n"; OS << "IsConst: " << IsConst << "\n"; OS << "IsInitialized: " << IsInitialized << "\n"; OS << "IsBase: " << IsBase << "\n"; OS << "IsActive: " << IsActive << "\n"; OS << "IsFieldMutable: " << IsFieldMutable << "\n"; OS << "Desc: "; if (Desc) Desc->dump(OS); else OS << "nullptr"; OS << "\n"; } LLVM_DUMP_METHOD void InterpFrame::dump(llvm::raw_ostream &OS, unsigned Indent) const { unsigned Spaces = Indent * 2; { ColorScope SC(OS, true, {llvm::raw_ostream::BLUE, true}); OS.indent(Spaces); if (getCallee()) describe(OS); else OS << "Frame (Depth: " << getDepth() << ")"; OS << "\n"; } OS.indent(Spaces) << "Function: " << getFunction(); if (const Function *F = getFunction()) { OS << " (" << F->getName() << ")"; } OS << "\n"; OS.indent(Spaces) << "This: " << getThis() << "\n"; OS.indent(Spaces) << "RVO: " << getRVOPtr() << "\n"; while (const InterpFrame *F = this->Caller) { F->dump(OS, Indent + 1); F = F->Caller; } } LLVM_DUMP_METHOD void Record::dump(llvm::raw_ostream &OS, unsigned Indentation, unsigned Offset) const { unsigned Indent = Indentation * 2; OS.indent(Indent); { ColorScope SC(OS, true, {llvm::raw_ostream::BLUE, true}); OS << getName() << "\n"; } unsigned I = 0; for (const Record::Base &B : bases()) { OS.indent(Indent) << "- Base " << I << ". Offset " << (Offset + B.Offset) << "\n"; B.R->dump(OS, Indentation + 1, Offset + B.Offset); ++I; } I = 0; for (const Record::Field &F : fields()) { OS.indent(Indent) << "- Field " << I << ": "; { ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_RED, true}); OS << F.Decl->getName(); } OS << ". Offset " << (Offset + F.Offset) << "\n"; ++I; } I = 0; for (const Record::Base &B : virtual_bases()) { OS.indent(Indent) << "- Virtual Base " << I << ". Offset " << (Offset + B.Offset) << "\n"; B.R->dump(OS, Indentation + 1, Offset + B.Offset); ++I; } } LLVM_DUMP_METHOD void Block::dump(llvm::raw_ostream &OS) const { { ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_BLUE, true}); OS << "Block " << (const void *)this; } OS << " ("; Desc->dump(OS); OS << ")\n"; unsigned NPointers = 0; for (const Pointer *P = Pointers; P; P = P->Next) { ++NPointers; } OS << " Pointers: " << NPointers << "\n"; OS << " Dead: " << IsDead << "\n"; OS << " Static: " << IsStatic << "\n"; OS << " Extern: " << IsExtern << "\n"; OS << " Initialized: " << IsInitialized << "\n"; } LLVM_DUMP_METHOD void EvaluationResult::dump() const { assert(Ctx); auto &OS = llvm::errs(); const ASTContext &ASTCtx = Ctx->getASTContext(); switch (Kind) { case Empty: OS << "Empty\n"; break; case RValue: OS << "RValue: "; std::get(Value).dump(OS, ASTCtx); break; case LValue: { assert(Source); QualType SourceType; if (const auto *D = Source.dyn_cast()) { if (const auto *VD = dyn_cast(D)) SourceType = VD->getType(); } else if (const auto *E = Source.dyn_cast()) { SourceType = E->getType(); } OS << "LValue: "; if (const auto *P = std::get_if(&Value)) P->toAPValue(ASTCtx).printPretty(OS, ASTCtx, SourceType); else if (const auto *FP = std::get_if(&Value)) // Nope FP->toAPValue(ASTCtx).printPretty(OS, ASTCtx, SourceType); OS << "\n"; break; } case Invalid: OS << "Invalid\n"; break; case Valid: OS << "Valid\n"; break; } }