xref: /freebsd/contrib/llvm-project/clang/lib/AST/Interp/Disasm.cpp (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
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