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