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(Idx: 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(Buff: *OpPC); |
46 | OpPC += align(Size: 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(Buff: *OpPC); |
53 | OpPC += align(Size: 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(Buff: *OpPC); |
60 | OpPC += align(Size: I.bytesToSerialize()); |
61 | return I; |
62 | } |
63 | |
64 | LLVM_DUMP_METHOD void Function::dump() const { dump(OS&: llvm::errs()); } |
65 | |
66 | LLVM_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 | |
95 | LLVM_DUMP_METHOD void Program::dump() const { dump(OS&: 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, {.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 | |
209 | LLVM_DUMP_METHOD void Descriptor::dump() const { |
210 | dump(OS&: 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, {.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 | |
243 | LLVM_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 | |
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, {.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 | |
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(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 | |
325 | LLVM_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 | |
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>(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 | |