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

source code of clang/lib/AST/ByteCode/Disasm.cpp