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