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