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 "Floating.h" |
15 | #include "Function.h" |
16 | #include "FunctionPointer.h" |
17 | #include "Integral.h" |
18 | #include "IntegralAP.h" |
19 | #include "InterpFrame.h" |
20 | #include "Opcode.h" |
21 | #include "PrimType.h" |
22 | #include "Program.h" |
23 | #include "clang/AST/ASTDumperUtils.h" |
24 | #include "clang/AST/DeclCXX.h" |
25 | #include "llvm/Support/Compiler.h" |
26 | #include "llvm/Support/Format.h" |
27 | |
28 | using namespace clang; |
29 | using namespace clang::interp; |
30 | |
31 | template <typename T> inline T ReadArg(Program &P, CodePtr &OpPC) { |
32 | if constexpr (std::is_pointer_v<T>) { |
33 | uint32_t ID = OpPC.read<uint32_t>(); |
34 | return reinterpret_cast<T>(P.getNativePointer(Idx: ID)); |
35 | } else { |
36 | return OpPC.read<T>(); |
37 | } |
38 | } |
39 | |
40 | template <> inline Floating ReadArg<Floating>(Program &P, CodePtr &OpPC) { |
41 | Floating F = Floating::deserialize(Buff: *OpPC); |
42 | OpPC += align(Size: F.bytesToSerialize()); |
43 | return F; |
44 | } |
45 | |
46 | template <> |
47 | inline IntegralAP<false> ReadArg<IntegralAP<false>>(Program &P, CodePtr &OpPC) { |
48 | IntegralAP<false> I = IntegralAP<false>::deserialize(Buff: *OpPC); |
49 | OpPC += align(Size: I.bytesToSerialize()); |
50 | return I; |
51 | } |
52 | |
53 | template <> |
54 | inline IntegralAP<true> ReadArg<IntegralAP<true>>(Program &P, CodePtr &OpPC) { |
55 | IntegralAP<true> I = IntegralAP<true>::deserialize(Buff: *OpPC); |
56 | OpPC += align(Size: I.bytesToSerialize()); |
57 | return I; |
58 | } |
59 | |
60 | LLVM_DUMP_METHOD void Function::dump() const { dump(OS&: llvm::errs()); } |
61 | |
62 | LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const { |
63 | { |
64 | ColorScope SC(OS, true, {.Color: llvm::raw_ostream::BRIGHT_GREEN, .Bold: true}); |
65 | OS << getName() << " " << (const void *)this << "\n" ; |
66 | } |
67 | OS << "frame size: " << getFrameSize() << "\n" ; |
68 | OS << "arg size: " << getArgSize() << "\n" ; |
69 | OS << "rvo: " << hasRVO() << "\n" ; |
70 | OS << "this arg: " << hasThisPointer() << "\n" ; |
71 | |
72 | auto PrintName = [&OS](const char *Name) { |
73 | OS << Name; |
74 | long N = 30 - strlen(s: Name); |
75 | if (N > 0) |
76 | OS.indent(NumSpaces: N); |
77 | }; |
78 | |
79 | for (CodePtr Start = getCodeBegin(), PC = Start; PC != getCodeEnd();) { |
80 | size_t Addr = PC - Start; |
81 | auto Op = PC.read<Opcode>(); |
82 | OS << llvm::format(Fmt: "%8d" , Vals: Addr) << " " ; |
83 | switch (Op) { |
84 | #define GET_DISASM |
85 | #include "Opcodes.inc" |
86 | #undef GET_DISASM |
87 | } |
88 | } |
89 | } |
90 | |
91 | LLVM_DUMP_METHOD void Program::dump() const { dump(OS&: llvm::errs()); } |
92 | |
93 | static const char *primTypeToString(PrimType T) { |
94 | switch (T) { |
95 | case PT_Sint8: |
96 | return "Sint8" ; |
97 | case PT_Uint8: |
98 | return "Uint8" ; |
99 | case PT_Sint16: |
100 | return "Sint16" ; |
101 | case PT_Uint16: |
102 | return "Uint16" ; |
103 | case PT_Sint32: |
104 | return "Sint32" ; |
105 | case PT_Uint32: |
106 | return "Uint32" ; |
107 | case PT_Sint64: |
108 | return "Sint64" ; |
109 | case PT_Uint64: |
110 | return "Uint64" ; |
111 | case PT_IntAP: |
112 | return "IntAP" ; |
113 | case PT_IntAPS: |
114 | return "IntAPS" ; |
115 | case PT_Bool: |
116 | return "Bool" ; |
117 | case PT_Float: |
118 | return "Float" ; |
119 | case PT_Ptr: |
120 | return "Ptr" ; |
121 | case PT_FnPtr: |
122 | return "FnPtr" ; |
123 | } |
124 | llvm_unreachable("Unhandled PrimType" ); |
125 | } |
126 | |
127 | LLVM_DUMP_METHOD void Program::dump(llvm::raw_ostream &OS) const { |
128 | { |
129 | ColorScope SC(OS, true, {.Color: llvm::raw_ostream::BRIGHT_RED, .Bold: true}); |
130 | OS << "\n:: Program\n" ; |
131 | } |
132 | |
133 | { |
134 | ColorScope SC(OS, true, {.Color: llvm::raw_ostream::WHITE, .Bold: true}); |
135 | OS << "Total memory : " << Allocator.getTotalMemory() << " bytes\n" ; |
136 | OS << "Global Variables: " << Globals.size() << "\n" ; |
137 | } |
138 | unsigned GI = 0; |
139 | for (const Global *G : Globals) { |
140 | const Descriptor *Desc = G->block()->getDescriptor(); |
141 | Pointer GP = getPtrGlobal(GI); |
142 | |
143 | OS << GI << ": " << (const void *)G->block() << " " ; |
144 | { |
145 | ColorScope SC(OS, true, |
146 | GP.isInitialized() |
147 | ? TerminalColor{llvm::raw_ostream::GREEN, false} |
148 | : TerminalColor{llvm::raw_ostream::RED, false}); |
149 | OS << (GP.isInitialized() ? "initialized " : "uninitialized " ); |
150 | } |
151 | Desc->dump(OS); |
152 | OS << "\n" ; |
153 | if (Desc->isPrimitive() && !Desc->isDummy()) { |
154 | OS << " " ; |
155 | { |
156 | ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_CYAN, false}); |
157 | OS << primTypeToString(Desc->getPrimType()) << " " ; |
158 | } |
159 | TYPE_SWITCH(Desc->getPrimType(), { GP.deref<T>().print(OS); }); |
160 | OS << "\n" ; |
161 | } |
162 | ++GI; |
163 | } |
164 | |
165 | { |
166 | ColorScope SC(OS, true, {.Color: llvm::raw_ostream::WHITE, .Bold: true}); |
167 | OS << "Functions: " << Funcs.size() << "\n" ; |
168 | } |
169 | for (const auto &Func : Funcs) { |
170 | Func.second->dump(); |
171 | } |
172 | for (const auto &Anon : AnonFuncs) { |
173 | Anon->dump(); |
174 | } |
175 | } |
176 | |
177 | LLVM_DUMP_METHOD void Descriptor::dump() const { |
178 | dump(OS&: llvm::errs()); |
179 | llvm::errs() << '\n'; |
180 | } |
181 | |
182 | LLVM_DUMP_METHOD void Descriptor::dump(llvm::raw_ostream &OS) const { |
183 | // Source |
184 | { |
185 | ColorScope SC(OS, true, {.Color: llvm::raw_ostream::BLUE, .Bold: true}); |
186 | if (const auto *ND = dyn_cast_if_present<NamedDecl>(Val: asDecl())) |
187 | ND->printQualifiedName(OS); |
188 | else if (asExpr()) |
189 | OS << "expr (TODO)" ; |
190 | } |
191 | |
192 | // Print a few interesting bits about the descriptor. |
193 | if (isPrimitiveArray()) |
194 | OS << " primitive-array" ; |
195 | else if (isCompositeArray()) |
196 | OS << " composite-array" ; |
197 | else if (isRecord()) |
198 | OS << " record" ; |
199 | else if (isPrimitive()) |
200 | OS << " primitive" ; |
201 | |
202 | if (isZeroSizeArray()) |
203 | OS << " zero-size-arrary" ; |
204 | else if (isUnknownSizeArray()) |
205 | OS << " unknown-size-array" ; |
206 | |
207 | if (isDummy()) |
208 | OS << " dummy" ; |
209 | } |
210 | |
211 | LLVM_DUMP_METHOD void InterpFrame::dump(llvm::raw_ostream &OS, |
212 | unsigned Indent) const { |
213 | unsigned Spaces = Indent * 2; |
214 | { |
215 | ColorScope SC(OS, true, {.Color: llvm::raw_ostream::BLUE, .Bold: true}); |
216 | OS.indent(NumSpaces: Spaces); |
217 | if (getCallee()) |
218 | describe(OS); |
219 | else |
220 | OS << "Frame (Depth: " << getDepth() << ")" ; |
221 | OS << "\n" ; |
222 | } |
223 | OS.indent(NumSpaces: Spaces) << "Function: " << getFunction(); |
224 | if (const Function *F = getFunction()) { |
225 | OS << " (" << F->getName() << ")" ; |
226 | } |
227 | OS << "\n" ; |
228 | OS.indent(NumSpaces: Spaces) << "This: " << getThis() << "\n" ; |
229 | OS.indent(NumSpaces: Spaces) << "RVO: " << getRVOPtr() << "\n" ; |
230 | |
231 | while (const InterpFrame *F = this->Caller) { |
232 | F->dump(OS, Indent: Indent + 1); |
233 | F = F->Caller; |
234 | } |
235 | } |
236 | |
237 | LLVM_DUMP_METHOD void Record::dump(llvm::raw_ostream &OS, unsigned Indentation, |
238 | unsigned Offset) const { |
239 | unsigned Indent = Indentation * 2; |
240 | OS.indent(NumSpaces: Indent); |
241 | { |
242 | ColorScope SC(OS, true, {.Color: llvm::raw_ostream::BLUE, .Bold: true}); |
243 | OS << getName() << "\n" ; |
244 | } |
245 | |
246 | unsigned I = 0; |
247 | for (const Record::Base &B : bases()) { |
248 | OS.indent(NumSpaces: Indent) << "- Base " << I << ". Offset " << (Offset + B.Offset) |
249 | << "\n" ; |
250 | B.R->dump(OS, Indentation: Indentation + 1, Offset: Offset + B.Offset); |
251 | ++I; |
252 | } |
253 | |
254 | // FIXME: Virtual bases. |
255 | |
256 | I = 0; |
257 | for (const Record::Field &F : fields()) { |
258 | OS.indent(NumSpaces: Indent) << "- Field " << I << ": " ; |
259 | { |
260 | ColorScope SC(OS, true, {.Color: llvm::raw_ostream::BRIGHT_RED, .Bold: true}); |
261 | OS << F.Decl->getName(); |
262 | } |
263 | OS << ". Offset " << (Offset + F.Offset) << "\n" ; |
264 | ++I; |
265 | } |
266 | } |
267 | |
268 | LLVM_DUMP_METHOD void Block::dump(llvm::raw_ostream &OS) const { |
269 | { |
270 | ColorScope SC(OS, true, {.Color: llvm::raw_ostream::BRIGHT_BLUE, .Bold: true}); |
271 | OS << "Block " << (const void *)this << "\n" ; |
272 | } |
273 | unsigned NPointers = 0; |
274 | for (const Pointer *P = Pointers; P; P = P->Next) { |
275 | ++NPointers; |
276 | } |
277 | OS << " Pointers: " << NPointers << "\n" ; |
278 | OS << " Dead: " << IsDead << "\n" ; |
279 | OS << " Static: " << IsStatic << "\n" ; |
280 | OS << " Extern: " << IsExtern << "\n" ; |
281 | OS << " Initialized: " << IsInitialized << "\n" ; |
282 | } |
283 | |