clang 20.0.0git
Disasm.cpp
Go to the documentation of this file.
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"
28#include "clang/AST/DeclCXX.h"
29#include "clang/AST/ExprCXX.h"
30#include "llvm/Support/Compiler.h"
31#include "llvm/Support/Format.h"
32
33using namespace clang;
34using namespace clang::interp;
35
36template <typename T> inline static T ReadArg(Program &P, CodePtr &OpPC) {
37 if constexpr (std::is_pointer_v<T>) {
38 uint32_t ID = OpPC.read<uint32_t>();
39 return reinterpret_cast<T>(P.getNativePointer(ID));
40 } else {
41 return OpPC.read<T>();
42 }
43}
44
45template <> inline Floating ReadArg<Floating>(Program &P, CodePtr &OpPC) {
47 OpPC += align(F.bytesToSerialize());
48 return F;
49}
50
51template <>
52inline IntegralAP<false> ReadArg<IntegralAP<false>>(Program &P, CodePtr &OpPC) {
54 OpPC += align(I.bytesToSerialize());
55 return I;
56}
57
58template <>
59inline IntegralAP<true> ReadArg<IntegralAP<true>>(Program &P, CodePtr &OpPC) {
61 OpPC += align(I.bytesToSerialize());
62 return I;
63}
64
65template <> inline FixedPoint ReadArg<FixedPoint>(Program &P, CodePtr &OpPC) {
67 OpPC += align(I.bytesToSerialize());
68 return I;
69}
70
71LLVM_DUMP_METHOD void Function::dump() const { dump(llvm::errs()); }
72
73LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const {
74 {
75 ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_GREEN, true});
76 OS << getName() << " " << (const void *)this << "\n";
77 }
78 OS << "frame size: " << getFrameSize() << "\n";
79 OS << "arg size: " << getArgSize() << "\n";
80 OS << "rvo: " << hasRVO() << "\n";
81 OS << "this arg: " << hasThisPointer() << "\n";
82
83 auto PrintName = [&OS](const char *Name) {
84 OS << Name;
85 long N = 30 - strlen(Name);
86 if (N > 0)
87 OS.indent(N);
88 };
89
90 for (CodePtr Start = getCodeBegin(), PC = Start; PC != getCodeEnd();) {
91 size_t Addr = PC - Start;
92 auto Op = PC.read<Opcode>();
93 OS << llvm::format("%8d", Addr) << " ";
94 switch (Op) {
95#define GET_DISASM
96#include "Opcodes.inc"
97#undef GET_DISASM
98 }
99 }
100}
101
102LLVM_DUMP_METHOD void Program::dump() const { dump(llvm::errs()); }
103
104static const char *primTypeToString(PrimType T) {
105 switch (T) {
106 case PT_Sint8:
107 return "Sint8";
108 case PT_Uint8:
109 return "Uint8";
110 case PT_Sint16:
111 return "Sint16";
112 case PT_Uint16:
113 return "Uint16";
114 case PT_Sint32:
115 return "Sint32";
116 case PT_Uint32:
117 return "Uint32";
118 case PT_Sint64:
119 return "Sint64";
120 case PT_Uint64:
121 return "Uint64";
122 case PT_IntAP:
123 return "IntAP";
124 case PT_IntAPS:
125 return "IntAPS";
126 case PT_Bool:
127 return "Bool";
128 case PT_Float:
129 return "Float";
130 case PT_Ptr:
131 return "Ptr";
132 case PT_FnPtr:
133 return "FnPtr";
134 case PT_MemberPtr:
135 return "MemberPtr";
136 case PT_FixedPoint:
137 return "FixedPoint";
138 }
139 llvm_unreachable("Unhandled PrimType");
140}
141
142LLVM_DUMP_METHOD void Program::dump(llvm::raw_ostream &OS) const {
143 {
144 ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_RED, true});
145 OS << "\n:: Program\n";
146 }
147
148 {
149 ColorScope SC(OS, true, {llvm::raw_ostream::WHITE, true});
150 OS << "Total memory : " << Allocator.getTotalMemory() << " bytes\n";
151 OS << "Global Variables: " << Globals.size() << "\n";
152 }
153 unsigned GI = 0;
154 for (const Global *G : Globals) {
155 const Descriptor *Desc = G->block()->getDescriptor();
156 Pointer GP = getPtrGlobal(GI);
157
158 OS << GI << ": " << (const void *)G->block() << " ";
159 {
160 ColorScope SC(OS, true,
161 GP.isInitialized()
162 ? TerminalColor{llvm::raw_ostream::GREEN, false}
163 : TerminalColor{llvm::raw_ostream::RED, false});
164 OS << (GP.isInitialized() ? "initialized " : "uninitialized ");
165 }
166 Desc->dump(OS);
167
168 if (GP.isInitialized() && Desc->IsTemporary) {
169 if (const auto *MTE =
170 dyn_cast_if_present<MaterializeTemporaryExpr>(Desc->asExpr());
171 MTE && MTE->getLifetimeExtendedTemporaryDecl()) {
172 if (const APValue *V =
173 MTE->getLifetimeExtendedTemporaryDecl()->getValue()) {
174 OS << " (global temporary value: ";
175 {
176 ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_MAGENTA, true});
177 std::string VStr;
178 llvm::raw_string_ostream SS(VStr);
179 V->dump(SS, Ctx.getASTContext());
180
181 for (unsigned I = 0; I != VStr.size(); ++I) {
182 if (VStr[I] == '\n')
183 VStr[I] = ' ';
184 }
185 VStr.pop_back(); // Remove the newline (or now space) at the end.
186 OS << VStr;
187 }
188 OS << ')';
189 }
190 }
191 }
192
193 OS << "\n";
194 if (GP.isInitialized() && Desc->isPrimitive() && !Desc->isDummy()) {
195 OS << " ";
196 {
197 ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_CYAN, false});
198 OS << primTypeToString(Desc->getPrimType()) << " ";
199 }
200 TYPE_SWITCH(Desc->getPrimType(), { GP.deref<T>().print(OS); });
201 OS << "\n";
202 }
203 ++GI;
204 }
205
206 {
207 ColorScope SC(OS, true, {llvm::raw_ostream::WHITE, true});
208 OS << "Functions: " << Funcs.size() << "\n";
209 }
210 for (const auto &Func : Funcs) {
211 Func.second->dump();
212 }
213 for (const auto &Anon : AnonFuncs) {
214 Anon->dump();
215 }
216}
217
218LLVM_DUMP_METHOD void Descriptor::dump() const {
219 dump(llvm::errs());
220 llvm::errs() << '\n';
221}
222
223LLVM_DUMP_METHOD void Descriptor::dump(llvm::raw_ostream &OS) const {
224 // Source
225 {
226 ColorScope SC(OS, true, {llvm::raw_ostream::BLUE, true});
227 if (const auto *ND = dyn_cast_if_present<NamedDecl>(asDecl()))
228 ND->printQualifiedName(OS);
229 else if (asExpr())
230 OS << "Expr " << (const void *)asExpr();
231 }
232
233 // Print a few interesting bits about the descriptor.
234 if (isPrimitiveArray())
235 OS << " primitive-array";
236 else if (isCompositeArray())
237 OS << " composite-array";
238 else if (isUnion())
239 OS << " union";
240 else if (isRecord())
241 OS << " record";
242 else if (isPrimitive())
243 OS << " primitive";
244
245 if (isZeroSizeArray())
246 OS << " zero-size-array";
247 else if (isUnknownSizeArray())
248 OS << " unknown-size-array";
249
250 if (isDummy())
251 OS << " dummy";
252}
253
254LLVM_DUMP_METHOD void InlineDescriptor::dump(llvm::raw_ostream &OS) const {
255 {
256 ColorScope SC(OS, true, {llvm::raw_ostream::BLUE, true});
257 OS << "InlineDescriptor " << (const void *)this << "\n";
258 }
259 OS << "Offset: " << Offset << "\n";
260 OS << "IsConst: " << IsConst << "\n";
261 OS << "IsInitialized: " << IsInitialized << "\n";
262 OS << "IsBase: " << IsBase << "\n";
263 OS << "IsActive: " << IsActive << "\n";
264 OS << "InUnion: " << InUnion << "\n";
265 OS << "IsFieldMutable: " << IsFieldMutable << "\n";
266 OS << "Desc: ";
267 if (Desc)
268 Desc->dump(OS);
269 else
270 OS << "nullptr";
271 OS << "\n";
272}
273
274LLVM_DUMP_METHOD void InterpFrame::dump(llvm::raw_ostream &OS,
275 unsigned Indent) const {
276 unsigned Spaces = Indent * 2;
277 {
278 ColorScope SC(OS, true, {llvm::raw_ostream::BLUE, true});
279 OS.indent(Spaces);
280 if (getCallee())
281 describe(OS);
282 else
283 OS << "Frame (Depth: " << getDepth() << ")";
284 OS << "\n";
285 }
286 OS.indent(Spaces) << "Function: " << getFunction();
287 if (const Function *F = getFunction()) {
288 OS << " (" << F->getName() << ")";
289 }
290 OS << "\n";
291 OS.indent(Spaces) << "This: " << getThis() << "\n";
292 OS.indent(Spaces) << "RVO: " << getRVOPtr() << "\n";
293 OS.indent(Spaces) << "Depth: " << Depth << "\n";
294 OS.indent(Spaces) << "ArgSize: " << ArgSize << "\n";
295 OS.indent(Spaces) << "Args: " << (void *)Args << "\n";
296 OS.indent(Spaces) << "FrameOffset: " << FrameOffset << "\n";
297 OS.indent(Spaces) << "FrameSize: " << (Func ? Func->getFrameSize() : 0)
298 << "\n";
299
300 for (const InterpFrame *F = this->Caller; F; F = F->Caller) {
301 F->dump(OS, Indent + 1);
302 }
303}
304
305LLVM_DUMP_METHOD void Record::dump(llvm::raw_ostream &OS, unsigned Indentation,
306 unsigned Offset) const {
307 unsigned Indent = Indentation * 2;
308 OS.indent(Indent);
309 {
310 ColorScope SC(OS, true, {llvm::raw_ostream::BLUE, true});
311 OS << getName() << "\n";
312 }
313
314 unsigned I = 0;
315 for (const Record::Base &B : bases()) {
316 OS.indent(Indent) << "- Base " << I << ". Offset " << (Offset + B.Offset)
317 << "\n";
318 B.R->dump(OS, Indentation + 1, Offset + B.Offset);
319 ++I;
320 }
321
322 I = 0;
323 for (const Record::Field &F : fields()) {
324 OS.indent(Indent) << "- Field " << I << ": ";
325 {
326 ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_RED, true});
327 OS << F.Decl->getName();
328 }
329 OS << ". Offset " << (Offset + F.Offset) << "\n";
330 ++I;
331 }
332
333 I = 0;
334 for (const Record::Base &B : virtual_bases()) {
335 OS.indent(Indent) << "- Virtual Base " << I << ". Offset "
336 << (Offset + B.Offset) << "\n";
337 B.R->dump(OS, Indentation + 1, Offset + B.Offset);
338 ++I;
339 }
340}
341
342LLVM_DUMP_METHOD void Block::dump(llvm::raw_ostream &OS) const {
343 {
344 ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_BLUE, true});
345 OS << "Block " << (const void *)this;
346 }
347 OS << " (";
348 Desc->dump(OS);
349 OS << ")\n";
350 unsigned NPointers = 0;
351 for (const Pointer *P = Pointers; P; P = P->Next) {
352 ++NPointers;
353 }
354 OS << " Pointers: " << NPointers << "\n";
355 OS << " Dead: " << IsDead << "\n";
356 OS << " Static: " << IsStatic << "\n";
357 OS << " Extern: " << IsExtern << "\n";
358 OS << " Initialized: " << IsInitialized << "\n";
359}
360
361LLVM_DUMP_METHOD void EvaluationResult::dump() const {
362 assert(Ctx);
363 auto &OS = llvm::errs();
364 const ASTContext &ASTCtx = Ctx->getASTContext();
365
366 switch (Kind) {
367 case Empty:
368 OS << "Empty\n";
369 break;
370 case RValue:
371 OS << "RValue: ";
372 std::get<APValue>(Value).dump(OS, ASTCtx);
373 break;
374 case LValue: {
375 assert(Source);
376 QualType SourceType;
377 if (const auto *D = dyn_cast<const Decl *>(Source)) {
378 if (const auto *VD = dyn_cast<ValueDecl>(D))
379 SourceType = VD->getType();
380 } else if (const auto *E = dyn_cast<const Expr *>(Source)) {
381 SourceType = E->getType();
382 }
383
384 OS << "LValue: ";
385 if (const auto *P = std::get_if<Pointer>(&Value))
386 P->toAPValue(ASTCtx).printPretty(OS, ASTCtx, SourceType);
387 else if (const auto *FP = std::get_if<FunctionPointer>(&Value)) // Nope
388 FP->toAPValue(ASTCtx).printPretty(OS, ASTCtx, SourceType);
389 OS << "\n";
390 break;
391 }
392 case Invalid:
393 OS << "Invalid\n";
394 break;
395 case Valid:
396 OS << "Valid\n";
397 break;
398 }
399}
#define V(N, I)
Definition: ASTContext.h:3460
StringRef P
const Decl * D
Expr * E
static void dump(llvm::raw_ostream &OS, StringRef FunctionName, ArrayRef< CounterExpression > Expressions, ArrayRef< CounterMappingRegion > Regions)
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
static const char * primTypeToString(PrimType T)
Definition: Disasm.cpp:104
FixedPoint ReadArg< FixedPoint >(Program &P, CodePtr &OpPC)
Definition: Disasm.cpp:65
Floating ReadArg< Floating >(Program &P, CodePtr &OpPC)
Definition: Disasm.cpp:45
static T ReadArg(Program &P, CodePtr &OpPC)
Definition: Disasm.cpp:36
Defines the clang::Expr interface and subclasses for C++ expressions.
#define TYPE_SWITCH(Expr, B)
Definition: PrimType.h:153
static std::string getName(const CallEvent &Call)
APValue - This class implements a discriminated union of [uninitialized] [APSInt] [APFloat],...
Definition: APValue.h:122
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:188
QualType getType() const
Definition: Expr.h:142
A (possibly-)qualified type.
Definition: Type.h:929
void dump() const
Definition: InterpBlock.h:130
Pointer into the code segment.
Definition: Source.h:30
std::enable_if_t<!std::is_pointer< T >::value, T > read()
Reads data and advances the pointer.
Definition: Source.h:60
ASTContext & getASTContext() const
Returns the AST context.
Definition: Context.h:62
void dump() const
Dump to stderr.
Definition: Disasm.cpp:361
Wrapper around fixed point types.
Definition: FixedPoint.h:23
static FixedPoint deserialize(const std::byte *Buff)
Definition: FixedPoint.h:108
size_t bytesToSerialize() const
Definition: FixedPoint.h:94
static Floating deserialize(const std::byte *Buff)
Definition: Floating.h:158
size_t bytesToSerialize() const
Definition: Floating.h:144
Bytecode function.
Definition: Function.h:81
unsigned getFrameSize() const
Returns the size of the function's local stack.
Definition: Function.h:86
static IntegralAP< Signed > deserialize(const std::byte *Buff)
Definition: IntegralAP.h:300
size_t bytesToSerialize() const
Definition: IntegralAP.h:286
Frame storing local variables.
Definition: InterpFrame.h:26
InterpFrame * Caller
The frame of the previous function.
Definition: InterpFrame.h:29
const Pointer & getThis() const
Returns the 'this' pointer.
Definition: InterpFrame.h:98
const Function * getFunction() const
Returns the current function.
Definition: InterpFrame.h:62
unsigned getDepth() const
Definition: InterpFrame.h:118
const Pointer & getRVOPtr() const
Returns the RVO pointer, if the Function has one.
Definition: InterpFrame.h:101
const FunctionDecl * getCallee() const override
Returns the caller.
void describe(llvm::raw_ostream &OS) const override
Describes the frame with arguments for diagnostic purposes.
A pointer to a memory block, live or dead.
Definition: Pointer.h:88
bool isInitialized() const
Checks if an object was initialized.
Definition: Pointer.cpp:335
The program contains and links the bytecode for all functions.
Definition: Program.h:39
Pointer getPtrGlobal(unsigned Idx) const
Returns a pointer to a global.
Definition: Program.cpp:106
void dump() const
Dumps the disassembled bytecode to llvm::errs().
Definition: Disasm.cpp:102
const std::string getName() const
Returns the name of the underlying declaration.
Definition: Record.cpp:32
llvm::iterator_range< const_virtual_iter > virtual_bases() const
Definition: Record.h:99
llvm::iterator_range< const_base_iter > bases() const
Definition: Record.h:88
llvm::iterator_range< const_field_iter > fields() const
Definition: Record.h:80
constexpr size_t align(size_t Size)
Aligns a size to the pointer alignment.
Definition: PrimType.h:131
PrimType
Enumeration of the primitive types of the VM.
Definition: PrimType.h:34
The JSON file list parser is used to communicate input to InstallAPI.
const FunctionProtoType * T
Describes a memory block created by an allocation site.
Definition: Descriptor.h:116
bool isPrimitive() const
Checks if the descriptor is of a primitive.
Definition: Descriptor.h:257
bool isCompositeArray() const
Checks if the descriptor is of an array of composites.
Definition: Descriptor.h:250
const Decl * asDecl() const
Definition: Descriptor.h:204
bool isDummy() const
Checks if this is a dummy descriptor.
Definition: Descriptor.h:266
bool isUnknownSizeArray() const
Checks if the descriptor is of an array of unknown size.
Definition: Descriptor.h:254
bool isPrimitiveArray() const
Checks if the descriptor is of an array of primitives.
Definition: Descriptor.h:248
bool isZeroSizeArray() const
Checks if the descriptor is of an array of zero size.
Definition: Descriptor.h:252
PrimType getPrimType() const
Definition: Descriptor.h:230
bool isRecord() const
Checks if the descriptor is of a record.
Definition: Descriptor.h:262
const bool IsTemporary
Flag indicating if the block is a temporary.
Definition: Descriptor.h:158
bool isUnion() const
Checks if the descriptor is of a union.
Definition: Descriptor.cpp:446
const Expr * asExpr() const
Definition: Descriptor.h:205
unsigned IsActive
Flag indicating if the field is the active member of a union.
Definition: Descriptor.h:92
unsigned IsBase
Flag indicating if the field is an embedded base class.
Definition: Descriptor.h:86
unsigned InUnion
Flat indicating if this field is in a union (even if nested).
Definition: Descriptor.h:94
unsigned Offset
Offset inside the structure/array.
Definition: Descriptor.h:72
unsigned IsInitialized
For primitive fields, it indicates if the field was initialized.
Definition: Descriptor.h:83
unsigned IsConst
Flag indicating if the storage is constant or not.
Definition: Descriptor.h:77
unsigned IsFieldMutable
Flag indicating if the field is mutable (if in a record).
Definition: Descriptor.h:98