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 "Floating.h" 17 #include "Function.h" 18 #include "FunctionPointer.h" 19 #include "Integral.h" 20 #include "IntegralAP.h" 21 #include "InterpFrame.h" 22 #include "MemberPointer.h" 23 #include "Opcode.h" 24 #include "PrimType.h" 25 #include "Program.h" 26 #include "clang/AST/ASTDumperUtils.h" 27 #include "clang/AST/DeclCXX.h" 28 #include "clang/AST/ExprCXX.h" 29 #include "llvm/Support/Compiler.h" 30 #include "llvm/Support/Format.h" 31 32 using namespace clang; 33 using namespace clang::interp; 34 35 template <typename T> inline T ReadArg(Program &P, CodePtr &OpPC) { 36 if constexpr (std::is_pointer_v<T>) { 37 uint32_t ID = OpPC.read<uint32_t>(); 38 return reinterpret_cast<T>(P.getNativePointer(ID)); 39 } else { 40 return OpPC.read<T>(); 41 } 42 } 43 44 template <> inline Floating ReadArg<Floating>(Program &P, CodePtr &OpPC) { 45 Floating F = Floating::deserialize(*OpPC); 46 OpPC += align(F.bytesToSerialize()); 47 return F; 48 } 49 50 template <> 51 inline IntegralAP<false> ReadArg<IntegralAP<false>>(Program &P, CodePtr &OpPC) { 52 IntegralAP<false> I = IntegralAP<false>::deserialize(*OpPC); 53 OpPC += align(I.bytesToSerialize()); 54 return I; 55 } 56 57 template <> 58 inline IntegralAP<true> ReadArg<IntegralAP<true>>(Program &P, CodePtr &OpPC) { 59 IntegralAP<true> I = IntegralAP<true>::deserialize(*OpPC); 60 OpPC += align(I.bytesToSerialize()); 61 return I; 62 } 63 64 LLVM_DUMP_METHOD void Function::dump() const { dump(llvm::errs()); } 65 66 LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const { 67 { 68 ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_GREEN, true}); 69 OS << getName() << " " << (const void *)this << "\n"; 70 } 71 OS << "frame size: " << getFrameSize() << "\n"; 72 OS << "arg size: " << getArgSize() << "\n"; 73 OS << "rvo: " << hasRVO() << "\n"; 74 OS << "this arg: " << hasThisPointer() << "\n"; 75 76 auto PrintName = [&OS](const char *Name) { 77 OS << Name; 78 long N = 30 - strlen(Name); 79 if (N > 0) 80 OS.indent(N); 81 }; 82 83 for (CodePtr Start = getCodeBegin(), PC = Start; PC != getCodeEnd();) { 84 size_t Addr = PC - Start; 85 auto Op = PC.read<Opcode>(); 86 OS << llvm::format("%8d", Addr) << " "; 87 switch (Op) { 88 #define GET_DISASM 89 #include "Opcodes.inc" 90 #undef GET_DISASM 91 } 92 } 93 } 94 95 LLVM_DUMP_METHOD void Program::dump() const { dump(llvm::errs()); } 96 97 static const char *primTypeToString(PrimType T) { 98 switch (T) { 99 case PT_Sint8: 100 return "Sint8"; 101 case PT_Uint8: 102 return "Uint8"; 103 case PT_Sint16: 104 return "Sint16"; 105 case PT_Uint16: 106 return "Uint16"; 107 case PT_Sint32: 108 return "Sint32"; 109 case PT_Uint32: 110 return "Uint32"; 111 case PT_Sint64: 112 return "Sint64"; 113 case PT_Uint64: 114 return "Uint64"; 115 case PT_IntAP: 116 return "IntAP"; 117 case PT_IntAPS: 118 return "IntAPS"; 119 case PT_Bool: 120 return "Bool"; 121 case PT_Float: 122 return "Float"; 123 case PT_Ptr: 124 return "Ptr"; 125 case PT_FnPtr: 126 return "FnPtr"; 127 case PT_MemberPtr: 128 return "MemberPtr"; 129 } 130 llvm_unreachable("Unhandled PrimType"); 131 } 132 133 LLVM_DUMP_METHOD void Program::dump(llvm::raw_ostream &OS) const { 134 { 135 ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_RED, true}); 136 OS << "\n:: Program\n"; 137 } 138 139 { 140 ColorScope SC(OS, true, {llvm::raw_ostream::WHITE, true}); 141 OS << "Total memory : " << Allocator.getTotalMemory() << " bytes\n"; 142 OS << "Global Variables: " << Globals.size() << "\n"; 143 } 144 unsigned GI = 0; 145 for (const Global *G : Globals) { 146 const Descriptor *Desc = G->block()->getDescriptor(); 147 Pointer GP = getPtrGlobal(GI); 148 149 OS << GI << ": " << (const void *)G->block() << " "; 150 { 151 ColorScope SC(OS, true, 152 GP.isInitialized() 153 ? TerminalColor{llvm::raw_ostream::GREEN, false} 154 : TerminalColor{llvm::raw_ostream::RED, false}); 155 OS << (GP.isInitialized() ? "initialized " : "uninitialized "); 156 } 157 Desc->dump(OS); 158 159 if (GP.isInitialized() && Desc->IsTemporary) { 160 if (const auto *MTE = 161 dyn_cast_if_present<MaterializeTemporaryExpr>(Desc->asExpr()); 162 MTE && MTE->getLifetimeExtendedTemporaryDecl()) { 163 if (const APValue *V = 164 MTE->getLifetimeExtendedTemporaryDecl()->getValue()) { 165 OS << " (global temporary value: "; 166 { 167 ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_MAGENTA, true}); 168 std::string VStr; 169 llvm::raw_string_ostream SS(VStr); 170 V->dump(SS, Ctx.getASTContext()); 171 172 for (unsigned I = 0; I != VStr.size(); ++I) { 173 if (VStr[I] == '\n') 174 VStr[I] = ' '; 175 } 176 VStr.pop_back(); // Remove the newline (or now space) at the end. 177 OS << VStr; 178 } 179 OS << ')'; 180 } 181 } 182 } 183 184 OS << "\n"; 185 if (GP.isInitialized() && Desc->isPrimitive() && !Desc->isDummy()) { 186 OS << " "; 187 { 188 ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_CYAN, false}); 189 OS << primTypeToString(Desc->getPrimType()) << " "; 190 } 191 TYPE_SWITCH(Desc->getPrimType(), { GP.deref<T>().print(OS); }); 192 OS << "\n"; 193 } 194 ++GI; 195 } 196 197 { 198 ColorScope SC(OS, true, {llvm::raw_ostream::WHITE, true}); 199 OS << "Functions: " << Funcs.size() << "\n"; 200 } 201 for (const auto &Func : Funcs) { 202 Func.second->dump(); 203 } 204 for (const auto &Anon : AnonFuncs) { 205 Anon->dump(); 206 } 207 } 208 209 LLVM_DUMP_METHOD void Descriptor::dump() const { 210 dump(llvm::errs()); 211 llvm::errs() << '\n'; 212 } 213 214 LLVM_DUMP_METHOD void Descriptor::dump(llvm::raw_ostream &OS) const { 215 // Source 216 { 217 ColorScope SC(OS, true, {llvm::raw_ostream::BLUE, true}); 218 if (const auto *ND = dyn_cast_if_present<NamedDecl>(asDecl())) 219 ND->printQualifiedName(OS); 220 else if (asExpr()) 221 OS << "Expr " << (const void *)asExpr(); 222 } 223 224 // Print a few interesting bits about the descriptor. 225 if (isPrimitiveArray()) 226 OS << " primitive-array"; 227 else if (isCompositeArray()) 228 OS << " composite-array"; 229 else if (isRecord()) 230 OS << " record"; 231 else if (isPrimitive()) 232 OS << " primitive"; 233 234 if (isZeroSizeArray()) 235 OS << " zero-size-array"; 236 else if (isUnknownSizeArray()) 237 OS << " unknown-size-array"; 238 239 if (isDummy()) 240 OS << " dummy"; 241 } 242 243 LLVM_DUMP_METHOD void InlineDescriptor::dump(llvm::raw_ostream &OS) const { 244 { 245 ColorScope SC(OS, true, {llvm::raw_ostream::BLUE, true}); 246 OS << "InlineDescriptor " << (const void *)this << "\n"; 247 } 248 OS << "Offset: " << Offset << "\n"; 249 OS << "IsConst: " << IsConst << "\n"; 250 OS << "IsInitialized: " << IsInitialized << "\n"; 251 OS << "IsBase: " << IsBase << "\n"; 252 OS << "IsActive: " << IsActive << "\n"; 253 OS << "IsFieldMutable: " << IsFieldMutable << "\n"; 254 OS << "Desc: "; 255 if (Desc) 256 Desc->dump(OS); 257 else 258 OS << "nullptr"; 259 OS << "\n"; 260 } 261 262 LLVM_DUMP_METHOD void InterpFrame::dump(llvm::raw_ostream &OS, 263 unsigned Indent) const { 264 unsigned Spaces = Indent * 2; 265 { 266 ColorScope SC(OS, true, {llvm::raw_ostream::BLUE, true}); 267 OS.indent(Spaces); 268 if (getCallee()) 269 describe(OS); 270 else 271 OS << "Frame (Depth: " << getDepth() << ")"; 272 OS << "\n"; 273 } 274 OS.indent(Spaces) << "Function: " << getFunction(); 275 if (const Function *F = getFunction()) { 276 OS << " (" << F->getName() << ")"; 277 } 278 OS << "\n"; 279 OS.indent(Spaces) << "This: " << getThis() << "\n"; 280 OS.indent(Spaces) << "RVO: " << getRVOPtr() << "\n"; 281 282 while (const InterpFrame *F = this->Caller) { 283 F->dump(OS, Indent + 1); 284 F = F->Caller; 285 } 286 } 287 288 LLVM_DUMP_METHOD void Record::dump(llvm::raw_ostream &OS, unsigned Indentation, 289 unsigned Offset) const { 290 unsigned Indent = Indentation * 2; 291 OS.indent(Indent); 292 { 293 ColorScope SC(OS, true, {llvm::raw_ostream::BLUE, true}); 294 OS << getName() << "\n"; 295 } 296 297 unsigned I = 0; 298 for (const Record::Base &B : bases()) { 299 OS.indent(Indent) << "- Base " << I << ". Offset " << (Offset + B.Offset) 300 << "\n"; 301 B.R->dump(OS, Indentation + 1, Offset + B.Offset); 302 ++I; 303 } 304 305 I = 0; 306 for (const Record::Field &F : fields()) { 307 OS.indent(Indent) << "- Field " << I << ": "; 308 { 309 ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_RED, true}); 310 OS << F.Decl->getName(); 311 } 312 OS << ". Offset " << (Offset + F.Offset) << "\n"; 313 ++I; 314 } 315 316 I = 0; 317 for (const Record::Base &B : virtual_bases()) { 318 OS.indent(Indent) << "- Virtual Base " << I << ". Offset " 319 << (Offset + B.Offset) << "\n"; 320 B.R->dump(OS, Indentation + 1, Offset + B.Offset); 321 ++I; 322 } 323 } 324 325 LLVM_DUMP_METHOD void Block::dump(llvm::raw_ostream &OS) const { 326 { 327 ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_BLUE, true}); 328 OS << "Block " << (const void *)this; 329 } 330 OS << " ("; 331 Desc->dump(OS); 332 OS << ")\n"; 333 unsigned NPointers = 0; 334 for (const Pointer *P = Pointers; P; P = P->Next) { 335 ++NPointers; 336 } 337 OS << " Pointers: " << NPointers << "\n"; 338 OS << " Dead: " << IsDead << "\n"; 339 OS << " Static: " << IsStatic << "\n"; 340 OS << " Extern: " << IsExtern << "\n"; 341 OS << " Initialized: " << IsInitialized << "\n"; 342 } 343 344 LLVM_DUMP_METHOD void EvaluationResult::dump() const { 345 assert(Ctx); 346 auto &OS = llvm::errs(); 347 const ASTContext &ASTCtx = Ctx->getASTContext(); 348 349 switch (Kind) { 350 case Empty: 351 OS << "Empty\n"; 352 break; 353 case RValue: 354 OS << "RValue: "; 355 std::get<APValue>(Value).dump(OS, ASTCtx); 356 break; 357 case LValue: { 358 assert(Source); 359 QualType SourceType; 360 if (const auto *D = Source.dyn_cast<const Decl *>()) { 361 if (const auto *VD = dyn_cast<ValueDecl>(D)) 362 SourceType = VD->getType(); 363 } else if (const auto *E = Source.dyn_cast<const Expr *>()) { 364 SourceType = E->getType(); 365 } 366 367 OS << "LValue: "; 368 if (const auto *P = std::get_if<Pointer>(&Value)) 369 P->toAPValue(ASTCtx).printPretty(OS, ASTCtx, SourceType); 370 else if (const auto *FP = std::get_if<FunctionPointer>(&Value)) // Nope 371 FP->toAPValue(ASTCtx).printPretty(OS, ASTCtx, SourceType); 372 OS << "\n"; 373 break; 374 } 375 case Invalid: 376 OS << "Invalid\n"; 377 break; 378 case Valid: 379 OS << "Valid\n"; 380 break; 381 } 382 } 383