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
32using namespace clang;
33using namespace clang::interp;
34
35template <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(Idx: ID));
39 } else {
40 return OpPC.read<T>();
41 }
42}
43
44template <> inline Floating ReadArg<Floating>(Program &P, CodePtr &OpPC) {
45 Floating F = Floating::deserialize(Buff: *OpPC);
46 OpPC += align(Size: F.bytesToSerialize());
47 return F;
48}
49
50template <>
51inline IntegralAP<false> ReadArg<IntegralAP<false>>(Program &P, CodePtr &OpPC) {
52 IntegralAP<false> I = IntegralAP<false>::deserialize(Buff: *OpPC);
53 OpPC += align(Size: I.bytesToSerialize());
54 return I;
55}
56
57template <>
58inline IntegralAP<true> ReadArg<IntegralAP<true>>(Program &P, CodePtr &OpPC) {
59 IntegralAP<true> I = IntegralAP<true>::deserialize(Buff: *OpPC);
60 OpPC += align(Size: I.bytesToSerialize());
61 return I;
62}
63
64LLVM_DUMP_METHOD void Function::dump() const { dump(OS&: llvm::errs()); }
65
66LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const {
67 {
68 ColorScope SC(OS, true, {.Color: llvm::raw_ostream::BRIGHT_GREEN, .Bold: 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(s: Name);
79 if (N > 0)
80 OS.indent(NumSpaces: 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(Fmt: "%8d", Vals: Addr) << " ";
87 switch (Op) {
88#define GET_DISASM
89#include "Opcodes.inc"
90#undef GET_DISASM
91 }
92 }
93}
94
95LLVM_DUMP_METHOD void Program::dump() const { dump(OS&: llvm::errs()); }
96
97static 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
133LLVM_DUMP_METHOD void Program::dump(llvm::raw_ostream &OS) const {
134 {
135 ColorScope SC(OS, true, {.Color: llvm::raw_ostream::BRIGHT_RED, .Bold: true});
136 OS << "\n:: Program\n";
137 }
138
139 {
140 ColorScope SC(OS, true, {.Color: llvm::raw_ostream::WHITE, .Bold: 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(Idx: GI);
148
149 OS << GI << ": " << (const void *)G->block() << " ";
150 {
151 ColorScope SC(OS, true,
152 GP.isInitialized()
153 ? TerminalColor{.Color: llvm::raw_ostream::GREEN, .Bold: false}
154 : TerminalColor{.Color: llvm::raw_ostream::RED, .Bold: 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>(Val: 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, {.Color: llvm::raw_ostream::BRIGHT_MAGENTA, .Bold: true});
168 std::string VStr;
169 llvm::raw_string_ostream SS(VStr);
170 V->dump(OS&: SS, Context: 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, {.Color: llvm::raw_ostream::BRIGHT_CYAN, .Bold: false});
189 OS << primTypeToString(T: 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, {.Color: llvm::raw_ostream::WHITE, .Bold: 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
209LLVM_DUMP_METHOD void Descriptor::dump() const {
210 dump(OS&: llvm::errs());
211 llvm::errs() << '\n';
212}
213
214LLVM_DUMP_METHOD void Descriptor::dump(llvm::raw_ostream &OS) const {
215 // Source
216 {
217 ColorScope SC(OS, true, {.Color: llvm::raw_ostream::BLUE, .Bold: true});
218 if (const auto *ND = dyn_cast_if_present<NamedDecl>(Val: 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
243LLVM_DUMP_METHOD void InlineDescriptor::dump(llvm::raw_ostream &OS) const {
244 {
245 ColorScope SC(OS, true, {.Color: llvm::raw_ostream::BLUE, .Bold: 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
262LLVM_DUMP_METHOD void InterpFrame::dump(llvm::raw_ostream &OS,
263 unsigned Indent) const {
264 unsigned Spaces = Indent * 2;
265 {
266 ColorScope SC(OS, true, {.Color: llvm::raw_ostream::BLUE, .Bold: true});
267 OS.indent(NumSpaces: Spaces);
268 if (getCallee())
269 describe(OS);
270 else
271 OS << "Frame (Depth: " << getDepth() << ")";
272 OS << "\n";
273 }
274 OS.indent(NumSpaces: Spaces) << "Function: " << getFunction();
275 if (const Function *F = getFunction()) {
276 OS << " (" << F->getName() << ")";
277 }
278 OS << "\n";
279 OS.indent(NumSpaces: Spaces) << "This: " << getThis() << "\n";
280 OS.indent(NumSpaces: Spaces) << "RVO: " << getRVOPtr() << "\n";
281
282 while (const InterpFrame *F = this->Caller) {
283 F->dump(OS, Indent: Indent + 1);
284 F = F->Caller;
285 }
286}
287
288LLVM_DUMP_METHOD void Record::dump(llvm::raw_ostream &OS, unsigned Indentation,
289 unsigned Offset) const {
290 unsigned Indent = Indentation * 2;
291 OS.indent(NumSpaces: Indent);
292 {
293 ColorScope SC(OS, true, {.Color: llvm::raw_ostream::BLUE, .Bold: true});
294 OS << getName() << "\n";
295 }
296
297 unsigned I = 0;
298 for (const Record::Base &B : bases()) {
299 OS.indent(NumSpaces: Indent) << "- Base " << I << ". Offset " << (Offset + B.Offset)
300 << "\n";
301 B.R->dump(OS, Indentation: Indentation + 1, Offset: Offset + B.Offset);
302 ++I;
303 }
304
305 I = 0;
306 for (const Record::Field &F : fields()) {
307 OS.indent(NumSpaces: Indent) << "- Field " << I << ": ";
308 {
309 ColorScope SC(OS, true, {.Color: llvm::raw_ostream::BRIGHT_RED, .Bold: 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(NumSpaces: Indent) << "- Virtual Base " << I << ". Offset "
319 << (Offset + B.Offset) << "\n";
320 B.R->dump(OS, Indentation: Indentation + 1, Offset: Offset + B.Offset);
321 ++I;
322 }
323}
324
325LLVM_DUMP_METHOD void Block::dump(llvm::raw_ostream &OS) const {
326 {
327 ColorScope SC(OS, true, {.Color: llvm::raw_ostream::BRIGHT_BLUE, .Bold: 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
344LLVM_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>(v: Value).dump(OS, Context: 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>(Val: 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>(ptr: &Value))
369 P->toAPValue(ASTCtx).printPretty(OS, Ctx: ASTCtx, Ty: SourceType);
370 else if (const auto *FP = std::get_if<FunctionPointer>(ptr: &Value)) // Nope
371 FP->toAPValue(ASTCtx).printPretty(OS, Ctx: ASTCtx, Ty: 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