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