xref: /llvm-project/clang/lib/AST/ByteCode/Disasm.cpp (revision b5c9cba3f33512014a18622a0e3479851faf4ce9)
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