xref: /freebsd/contrib/llvm-project/clang/lib/AST/ByteCode/Disasm.cpp (revision 770cf0a5f02dc8983a89c6568d741fbc25baa999)
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 
32 using namespace clang;
33 using namespace clang::interp;
34 
35 template <typename T>
36 inline static std::string printArg(Program &P, CodePtr &OpPC) {
37   if constexpr (std::is_pointer_v<T>) {
38     uint32_t ID = OpPC.read<uint32_t>();
39     std::string Result;
40     llvm::raw_string_ostream SS(Result);
41     SS << reinterpret_cast<T>(P.getNativePointer(ID));
42     return Result;
43   } else {
44     std::string Result;
45     llvm::raw_string_ostream SS(Result);
46     auto Arg = OpPC.read<T>();
47     SS << Arg;
48     return Result;
49   }
50 }
51 
52 template <> inline std::string printArg<Floating>(Program &P, CodePtr &OpPC) {
53   auto Sem = Floating::deserializeSemantics(*OpPC);
54 
55   unsigned BitWidth = llvm::APFloatBase::semanticsSizeInBits(
56       llvm::APFloatBase::EnumToSemantics(Sem));
57   auto Memory =
58       std::make_unique<uint64_t[]>(llvm::APInt::getNumWords(BitWidth));
59   Floating Result(Memory.get(), Sem);
60   Floating::deserialize(*OpPC, &Result);
61 
62   OpPC += align(Result.bytesToSerialize());
63 
64   std::string S;
65   llvm::raw_string_ostream SS(S);
66   SS << std::move(Result);
67   return S;
68 }
69 
70 template <>
71 inline std::string printArg<IntegralAP<false>>(Program &P, CodePtr &OpPC) {
72   using T = IntegralAP<false>;
73   uint32_t BitWidth = T::deserializeSize(*OpPC);
74   auto Memory =
75       std::make_unique<uint64_t[]>(llvm::APInt::getNumWords(BitWidth));
76 
77   T Result(Memory.get(), BitWidth);
78   T::deserialize(*OpPC, &Result);
79 
80   OpPC += align(Result.bytesToSerialize());
81 
82   std::string Str;
83   llvm::raw_string_ostream SS(Str);
84   SS << std::move(Result);
85   return Str;
86 }
87 
88 template <>
89 inline std::string printArg<IntegralAP<true>>(Program &P, CodePtr &OpPC) {
90   using T = IntegralAP<true>;
91   uint32_t BitWidth = T::deserializeSize(*OpPC);
92   auto Memory =
93       std::make_unique<uint64_t[]>(llvm::APInt::getNumWords(BitWidth));
94 
95   T Result(Memory.get(), BitWidth);
96   T::deserialize(*OpPC, &Result);
97 
98   OpPC += align(Result.bytesToSerialize());
99 
100   std::string Str;
101   llvm::raw_string_ostream SS(Str);
102   SS << std::move(Result);
103   return Str;
104 }
105 
106 template <> inline std::string printArg<FixedPoint>(Program &P, CodePtr &OpPC) {
107   auto F = FixedPoint::deserialize(*OpPC);
108   OpPC += align(F.bytesToSerialize());
109 
110   std::string Result;
111   llvm::raw_string_ostream SS(Result);
112   SS << std::move(F);
113   return Result;
114 }
115 
116 static bool isJumpOpcode(Opcode Op) {
117   return Op == OP_Jmp || Op == OP_Jf || Op == OP_Jt;
118 }
119 
120 static size_t getNumDisplayWidth(size_t N) {
121   unsigned L = 1u, M = 10u;
122   while (M <= N && ++L != std::numeric_limits<size_t>::digits10 + 1)
123     M *= 10u;
124 
125   return L;
126 }
127 
128 LLVM_DUMP_METHOD void Function::dump() const { dump(llvm::errs()); }
129 
130 LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const {
131   {
132     ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_GREEN, true});
133     OS << getName() << " " << (const void *)this << "\n";
134   }
135   OS << "frame size: " << getFrameSize() << "\n";
136   OS << "arg size:   " << getArgSize() << "\n";
137   OS << "rvo:        " << hasRVO() << "\n";
138   OS << "this arg:   " << hasThisPointer() << "\n";
139 
140   struct OpText {
141     size_t Addr;
142     std::string Op;
143     bool IsJump;
144     llvm::SmallVector<std::string> Args;
145   };
146 
147   auto PrintName = [](const char *Name) -> std::string {
148     return std::string(Name);
149   };
150 
151   llvm::SmallVector<OpText> Code;
152   size_t LongestAddr = 0;
153   size_t LongestOp = 0;
154 
155   for (CodePtr Start = getCodeBegin(), PC = Start; PC != getCodeEnd();) {
156     size_t Addr = PC - Start;
157     OpText Text;
158     auto Op = PC.read<Opcode>();
159     Text.Addr = Addr;
160     Text.IsJump = isJumpOpcode(Op);
161     switch (Op) {
162 #define GET_DISASM
163 #include "Opcodes.inc"
164 #undef GET_DISASM
165     }
166     Code.push_back(Text);
167     LongestOp = std::max(Text.Op.size(), LongestOp);
168     LongestAddr = std::max(getNumDisplayWidth(Addr), LongestAddr);
169   }
170 
171   // Record jumps and their targets.
172   struct JmpData {
173     size_t From;
174     size_t To;
175   };
176   llvm::SmallVector<JmpData> Jumps;
177   for (auto &Text : Code) {
178     if (Text.IsJump)
179       Jumps.push_back({Text.Addr, Text.Addr + std::stoi(Text.Args[0]) +
180                                       align(sizeof(Opcode)) +
181                                       align(sizeof(int32_t))});
182   }
183 
184   llvm::SmallVector<std::string> Text;
185   Text.reserve(Code.size());
186   size_t LongestLine = 0;
187   // Print code to a string, one at a time.
188   for (auto C : Code) {
189     std::string Line;
190     llvm::raw_string_ostream LS(Line);
191     LS << C.Addr;
192     LS.indent(LongestAddr - getNumDisplayWidth(C.Addr) + 4);
193     LS << C.Op;
194     LS.indent(LongestOp - C.Op.size() + 4);
195     for (auto &Arg : C.Args) {
196       LS << Arg << ' ';
197     }
198     Text.push_back(Line);
199     LongestLine = std::max(Line.size(), LongestLine);
200   }
201 
202   assert(Code.size() == Text.size());
203 
204   auto spaces = [](unsigned N) -> std::string {
205     std::string S;
206     for (unsigned I = 0; I != N; ++I)
207       S += ' ';
208     return S;
209   };
210 
211   // Now, draw the jump lines.
212   for (auto &J : Jumps) {
213     if (J.To > J.From) {
214       bool FoundStart = false;
215       for (size_t LineIndex = 0; LineIndex != Text.size(); ++LineIndex) {
216         Text[LineIndex] += spaces(LongestLine - Text[LineIndex].size());
217 
218         if (Code[LineIndex].Addr == J.From) {
219           Text[LineIndex] += "  --+";
220           FoundStart = true;
221         } else if (Code[LineIndex].Addr == J.To) {
222           Text[LineIndex] += "  <-+";
223           break;
224         } else if (FoundStart) {
225           Text[LineIndex] += "    |";
226         }
227       }
228       LongestLine += 5;
229     } else {
230       bool FoundStart = false;
231       for (ssize_t LineIndex = Text.size() - 1; LineIndex >= 0; --LineIndex) {
232         Text[LineIndex] += spaces(LongestLine - Text[LineIndex].size());
233         if (Code[LineIndex].Addr == J.From) {
234           Text[LineIndex] += "  --+";
235           FoundStart = true;
236         } else if (Code[LineIndex].Addr == J.To) {
237           Text[LineIndex] += "  <-+";
238           break;
239         } else if (FoundStart) {
240           Text[LineIndex] += "    |";
241         }
242       }
243       LongestLine += 5;
244     }
245   }
246 
247   for (auto &Line : Text)
248     OS << Line << '\n';
249 }
250 
251 LLVM_DUMP_METHOD void Program::dump() const { dump(llvm::errs()); }
252 
253 static const char *primTypeToString(PrimType T) {
254   switch (T) {
255   case PT_Sint8:
256     return "Sint8";
257   case PT_Uint8:
258     return "Uint8";
259   case PT_Sint16:
260     return "Sint16";
261   case PT_Uint16:
262     return "Uint16";
263   case PT_Sint32:
264     return "Sint32";
265   case PT_Uint32:
266     return "Uint32";
267   case PT_Sint64:
268     return "Sint64";
269   case PT_Uint64:
270     return "Uint64";
271   case PT_IntAP:
272     return "IntAP";
273   case PT_IntAPS:
274     return "IntAPS";
275   case PT_Bool:
276     return "Bool";
277   case PT_Float:
278     return "Float";
279   case PT_Ptr:
280     return "Ptr";
281   case PT_MemberPtr:
282     return "MemberPtr";
283   case PT_FixedPoint:
284     return "FixedPoint";
285   }
286   llvm_unreachable("Unhandled PrimType");
287 }
288 
289 LLVM_DUMP_METHOD void Program::dump(llvm::raw_ostream &OS) const {
290   {
291     ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_RED, true});
292     OS << "\n:: Program\n";
293   }
294 
295   {
296     ColorScope SC(OS, true, {llvm::raw_ostream::WHITE, true});
297     OS << "Total memory : " << Allocator.getTotalMemory() << " bytes\n";
298     OS << "Global Variables: " << Globals.size() << "\n";
299   }
300   unsigned GI = 0;
301   for (const Global *G : Globals) {
302     const Descriptor *Desc = G->block()->getDescriptor();
303     Pointer GP = getPtrGlobal(GI);
304 
305     OS << GI << ": " << (const void *)G->block() << " ";
306     {
307       ColorScope SC(OS, true,
308                     GP.isInitialized()
309                         ? TerminalColor{llvm::raw_ostream::GREEN, false}
310                         : TerminalColor{llvm::raw_ostream::RED, false});
311       OS << (GP.isInitialized() ? "initialized " : "uninitialized ");
312     }
313     Desc->dump(OS);
314 
315     if (GP.isInitialized() && Desc->IsTemporary) {
316       if (const auto *MTE =
317               dyn_cast_if_present<MaterializeTemporaryExpr>(Desc->asExpr());
318           MTE && MTE->getLifetimeExtendedTemporaryDecl()) {
319         if (const APValue *V =
320                 MTE->getLifetimeExtendedTemporaryDecl()->getValue()) {
321           OS << " (global temporary value: ";
322           {
323             ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_MAGENTA, true});
324             std::string VStr;
325             llvm::raw_string_ostream SS(VStr);
326             V->dump(SS, Ctx.getASTContext());
327 
328             for (unsigned I = 0; I != VStr.size(); ++I) {
329               if (VStr[I] == '\n')
330                 VStr[I] = ' ';
331             }
332             VStr.pop_back(); // Remove the newline (or now space) at the end.
333             OS << VStr;
334           }
335           OS << ')';
336         }
337       }
338     }
339 
340     OS << "\n";
341     if (GP.isInitialized() && Desc->isPrimitive() && !Desc->isDummy()) {
342       OS << "   ";
343       {
344         ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_CYAN, false});
345         OS << primTypeToString(Desc->getPrimType()) << " ";
346       }
347       TYPE_SWITCH(Desc->getPrimType(), { GP.deref<T>().print(OS); });
348       OS << "\n";
349     }
350     ++GI;
351   }
352 
353   {
354     ColorScope SC(OS, true, {llvm::raw_ostream::WHITE, true});
355     OS << "Functions: " << Funcs.size() << "\n";
356   }
357   for (const auto &Func : Funcs) {
358     Func.second->dump();
359   }
360   for (const auto &Anon : AnonFuncs) {
361     Anon->dump();
362   }
363 }
364 
365 LLVM_DUMP_METHOD void Descriptor::dump() const {
366   dump(llvm::errs());
367   llvm::errs() << '\n';
368 }
369 
370 LLVM_DUMP_METHOD void Descriptor::dump(llvm::raw_ostream &OS) const {
371   // Source
372   {
373     ColorScope SC(OS, true, {llvm::raw_ostream::BLUE, true});
374     if (const auto *ND = dyn_cast_if_present<NamedDecl>(asDecl()))
375       ND->printQualifiedName(OS);
376     else if (asExpr())
377       OS << "Expr " << (const void *)asExpr();
378   }
379 
380   // Print a few interesting bits about the descriptor.
381   if (isPrimitiveArray())
382     OS << " primitive-array";
383   else if (isCompositeArray())
384     OS << " composite-array";
385   else if (isUnion())
386     OS << " union";
387   else if (isRecord())
388     OS << " record";
389   else if (isPrimitive())
390     OS << " primitive " << primTypeToString(getPrimType());
391 
392   if (isZeroSizeArray())
393     OS << " zero-size-array";
394   else if (isUnknownSizeArray())
395     OS << " unknown-size-array";
396 
397   if (isDummy())
398     OS << " dummy";
399   if (IsConstexprUnknown)
400     OS << " constexpr-unknown";
401 }
402 
403 /// Dump descriptor, including all valid offsets.
404 LLVM_DUMP_METHOD void Descriptor::dumpFull(unsigned Offset,
405                                            unsigned Indent) const {
406   unsigned Spaces = Indent * 2;
407   llvm::raw_ostream &OS = llvm::errs();
408   OS.indent(Spaces);
409   dump(OS);
410   OS << '\n';
411   OS.indent(Spaces) << "Metadata: " << getMetadataSize() << " bytes\n";
412   OS.indent(Spaces) << "Size: " << getSize() << " bytes\n";
413   OS.indent(Spaces) << "AllocSize: " << getAllocSize() << " bytes\n";
414   Offset += getMetadataSize();
415   if (isCompositeArray()) {
416     OS.indent(Spaces) << "Elements: " << getNumElems() << '\n';
417     unsigned FO = Offset;
418     for (unsigned I = 0; I != getNumElems(); ++I) {
419       FO += sizeof(InlineDescriptor);
420       assert(ElemDesc->getMetadataSize() == 0);
421       OS.indent(Spaces) << "Element " << I << " offset: " << FO << '\n';
422       ElemDesc->dumpFull(FO, Indent + 1);
423 
424       FO += ElemDesc->getAllocSize();
425     }
426   } else if (isRecord()) {
427     ElemRecord->dump(OS, Indent + 1, Offset);
428   } else if (isPrimitive()) {
429   } else {
430   }
431 
432   OS << '\n';
433 }
434 
435 LLVM_DUMP_METHOD void InlineDescriptor::dump(llvm::raw_ostream &OS) const {
436   {
437     ColorScope SC(OS, true, {llvm::raw_ostream::BLUE, true});
438     OS << "InlineDescriptor " << (const void *)this << "\n";
439   }
440   OS << "Offset: " << Offset << "\n";
441   OS << "IsConst: " << IsConst << "\n";
442   OS << "IsInitialized: " << IsInitialized << "\n";
443   OS << "IsBase: " << IsBase << "\n";
444   OS << "IsActive: " << IsActive << "\n";
445   OS << "InUnion: " << InUnion << "\n";
446   OS << "IsFieldMutable: " << IsFieldMutable << "\n";
447   OS << "IsArrayElement: " << IsArrayElement << "\n";
448   OS << "Desc: ";
449   if (Desc)
450     Desc->dump(OS);
451   else
452     OS << "nullptr";
453   OS << "\n";
454 }
455 
456 LLVM_DUMP_METHOD void InterpFrame::dump(llvm::raw_ostream &OS,
457                                         unsigned Indent) const {
458   unsigned Spaces = Indent * 2;
459   {
460     ColorScope SC(OS, true, {llvm::raw_ostream::BLUE, true});
461     OS.indent(Spaces);
462     if (getCallee())
463       describe(OS);
464     else
465       OS << "Frame (Depth: " << getDepth() << ")";
466     OS << "\n";
467   }
468   OS.indent(Spaces) << "Function: " << getFunction();
469   if (const Function *F = getFunction()) {
470     OS << " (" << F->getName() << ")";
471   }
472   OS << "\n";
473   OS.indent(Spaces) << "This: " << getThis() << "\n";
474   OS.indent(Spaces) << "RVO: " << getRVOPtr() << "\n";
475   OS.indent(Spaces) << "Depth: " << Depth << "\n";
476   OS.indent(Spaces) << "ArgSize: " << ArgSize << "\n";
477   OS.indent(Spaces) << "Args: " << (void *)Args << "\n";
478   OS.indent(Spaces) << "FrameOffset: " << FrameOffset << "\n";
479   OS.indent(Spaces) << "FrameSize: " << (Func ? Func->getFrameSize() : 0)
480                     << "\n";
481 
482   for (const InterpFrame *F = this->Caller; F; F = F->Caller) {
483     F->dump(OS, Indent + 1);
484   }
485 }
486 
487 LLVM_DUMP_METHOD void Record::dump(llvm::raw_ostream &OS, unsigned Indentation,
488                                    unsigned Offset) const {
489   unsigned Indent = Indentation * 2;
490   OS.indent(Indent);
491   {
492     ColorScope SC(OS, true, {llvm::raw_ostream::BLUE, true});
493     OS << getName() << "\n";
494   }
495 
496   unsigned I = 0;
497   for (const Record::Base &B : bases()) {
498     OS.indent(Indent) << "- Base " << I << ". Offset " << (Offset + B.Offset)
499                       << "\n";
500     B.R->dump(OS, Indentation + 1, Offset + B.Offset);
501     ++I;
502   }
503 
504   I = 0;
505   for (const Record::Field &F : fields()) {
506     OS.indent(Indent) << "- Field " << I << ": ";
507     {
508       ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_RED, true});
509       OS << F.Decl->getName();
510     }
511     OS << ". Offset " << (Offset + F.Offset) << "\n";
512     ++I;
513   }
514 
515   I = 0;
516   for (const Record::Base &B : virtual_bases()) {
517     OS.indent(Indent) << "- Virtual Base " << I << ". Offset "
518                       << (Offset + B.Offset) << "\n";
519     B.R->dump(OS, Indentation + 1, Offset + B.Offset);
520     ++I;
521   }
522 }
523 
524 LLVM_DUMP_METHOD void Block::dump(llvm::raw_ostream &OS) const {
525   {
526     ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_BLUE, true});
527     OS << "Block " << (const void *)this;
528   }
529   OS << " (";
530   Desc->dump(OS);
531   OS << ")\n";
532   unsigned NPointers = 0;
533   for (const Pointer *P = Pointers; P; P = P->Next) {
534     ++NPointers;
535   }
536   OS << "  EvalID: " << EvalID << '\n';
537   OS << "  DeclID: ";
538   if (DeclID)
539     OS << *DeclID << '\n';
540   else
541     OS << "-\n";
542   OS << "  Pointers: " << NPointers << "\n";
543   OS << "  Dead: " << IsDead << "\n";
544   OS << "  Static: " << IsStatic << "\n";
545   OS << "  Extern: " << IsExtern << "\n";
546   OS << "  Initialized: " << IsInitialized << "\n";
547   OS << "  Weak: " << IsWeak << "\n";
548   OS << "  Dynamic: " << IsDynamic << "\n";
549 }
550 
551 LLVM_DUMP_METHOD void EvaluationResult::dump() const {
552   assert(Ctx);
553   auto &OS = llvm::errs();
554   const ASTContext &ASTCtx = Ctx->getASTContext();
555 
556   switch (Kind) {
557   case Empty:
558     OS << "Empty\n";
559     break;
560   case RValue:
561     OS << "RValue: ";
562     std::get<APValue>(Value).dump(OS, ASTCtx);
563     break;
564   case LValue: {
565     assert(Source);
566     QualType SourceType;
567     if (const auto *D = dyn_cast<const Decl *>(Source)) {
568       if (const auto *VD = dyn_cast<ValueDecl>(D))
569         SourceType = VD->getType();
570     } else if (const auto *E = dyn_cast<const Expr *>(Source)) {
571       SourceType = E->getType();
572     }
573 
574     OS << "LValue: ";
575     if (const auto *P = std::get_if<Pointer>(&Value))
576       P->toAPValue(ASTCtx).printPretty(OS, ASTCtx, SourceType);
577     else if (const auto *FP = std::get_if<FunctionPointer>(&Value)) // Nope
578       FP->toAPValue(ASTCtx).printPretty(OS, ASTCtx, SourceType);
579     OS << "\n";
580     break;
581   }
582   case Invalid:
583     OS << "Invalid\n";
584     break;
585   case Valid:
586     OS << "Valid\n";
587     break;
588   }
589 }
590