Skip to content

Commit

Permalink
[IR]PATCH 2/2: Add MDNode::printTree and dumpTree
Browse files Browse the repository at this point in the history
This patch adds the functionalities to print MDNode in tree shape. For
example, instead of printing a MDNode like this:
```
<0x5643e1166888> = !DILocalVariable(name: "foo", arg: 2, scope: <0x5643e11c9740>, file: <0x5643e11c6ec0>, line: 8, type: <0x5643e11ca8e0>, flags: DIFlagPublic | DIFlagFwdDecl, align: 8)
```
The printTree/dumpTree functions can give you:
```
<0x5643e1166888> = !DILocalVariable(name: "foo", arg: 2, scope: <0x5643e11c9740>, file: <0x5643e11c6ec0>, line: 8, type: <0x5643e11ca8e0>, flags: DIFlagPublic | DIFlagFwdDecl, align: 8)
  <0x5643e11c9740> = distinct !DISubprogram(scope: null, spFlags: 0)
  <0x5643e11c6ec0> = distinct !DIFile(filename: "file.c", directory: "/path/to/dir")
  <0x5643e11ca8e0> = distinct !DIDerivedType(tag: DW_TAG_pointer_type, baseType: <0x5643e11668d8>, size: 1, align: 2)
    <0x5643e11668d8> = !DIBasicType(tag: DW_TAG_unspecified_type, name: "basictype")
```
Which is useful when using it in debugger. Where sometimes printing the
whole module to see all MDNodes is too expensive.

Differential Revision: https://reviews.llvm.org/D110113
  • Loading branch information
mshockwave committed Oct 3, 2021
1 parent b2d078f commit 475de8d
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 4 deletions.
25 changes: 25 additions & 0 deletions llvm/include/llvm/IR/Metadata.h
Expand Up @@ -1038,6 +1038,31 @@ class MDNode : public Metadata {
return cast<T>(N.release()->replaceWithDistinctImpl());
}

/// Print in tree shape.
///
/// Prints definition of \c this in tree shape.
///
/// If \c M is provided, metadata nodes will be numbered canonically;
/// otherwise, pointer addresses are substituted.
/// @{
void printTree(raw_ostream &OS, const Module *M = nullptr) const;
void printTree(raw_ostream &OS, ModuleSlotTracker &MST,
const Module *M = nullptr) const;
/// @}

/// User-friendly dump in tree shape.
///
/// If \c M is provided, metadata nodes will be numbered canonically;
/// otherwise, pointer addresses are substituted.
///
/// Note: this uses an explicit overload instead of default arguments so that
/// the nullptr version is easy to call from a debugger.
///
/// @{
void dumpTree() const;
void dumpTree(const Module *M) const;
/// @}

private:
MDNode *replaceWithPermanentImpl();
MDNode *replaceWithUniquedImpl();
Expand Down
103 changes: 99 additions & 4 deletions llvm/lib/IR/AsmWriter.cpp
Expand Up @@ -23,6 +23,7 @@
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
Expand Down Expand Up @@ -1292,6 +1293,12 @@ struct AsmWriterContext {
static AsmWriterContext EmptyCtx(nullptr, nullptr);
return EmptyCtx;
}

/// A callback that will be triggered when the underlying printer
/// prints a Metadata as operand.
virtual void onWriteMetadataAsOperand(const Metadata *) {}

virtual ~AsmWriterContext() {}
};
} // end anonymous namespace

Expand Down Expand Up @@ -1636,6 +1643,7 @@ static void writeMDTuple(raw_ostream &Out, const MDTuple *Node,
WriteAsOperandInternal(Out, V, WriterCtx);
} else {
WriteAsOperandInternal(Out, MD, WriterCtx);
WriterCtx.onWriteMetadataAsOperand(MD);
}
if (mi + 1 != me)
Out << ", ";
Expand Down Expand Up @@ -1736,6 +1744,7 @@ static void writeMetadataAsOperand(raw_ostream &Out, const Metadata *MD,
return;
}
WriteAsOperandInternal(Out, MD, WriterCtx);
WriterCtx.onWriteMetadataAsOperand(MD);
}

void MDFieldPrinter::printMetadata(StringRef Name, const Metadata *MD,
Expand Down Expand Up @@ -4658,22 +4667,87 @@ void Value::printAsOperand(raw_ostream &O, bool PrintType,
printAsOperandImpl(*this, O, PrintType, MST);
}

/// Recursive version of printMetadataImpl.
static void printMetadataImplRec(raw_ostream &ROS, const Metadata &MD,
AsmWriterContext &WriterCtx) {
formatted_raw_ostream OS(ROS);
WriteAsOperandInternal(OS, &MD, WriterCtx, /* FromValue */ true);

auto *N = dyn_cast<MDNode>(&MD);
if (!N || isa<DIExpression>(MD) || isa<DIArgList>(MD))
return;

OS << " = ";
WriteMDNodeBodyInternal(OS, N, WriterCtx);
}

namespace {
struct MDTreeAsmWriterContext : public AsmWriterContext {
unsigned Level;
// {Level, Printed string}
using EntryTy = std::pair<unsigned, std::string>;
SmallVector<EntryTy, 4> Buffer;

// Used to break the cycle in case there is any.
SmallPtrSet<const Metadata *, 4> Visited;

raw_ostream &MainOS;

MDTreeAsmWriterContext(TypePrinting *TP, SlotTracker *ST, const Module *M,
raw_ostream &OS, const Metadata *InitMD)
: AsmWriterContext(TP, ST, M), Level(0U), Visited({InitMD}), MainOS(OS) {}

void onWriteMetadataAsOperand(const Metadata *MD) override {
if (Visited.count(MD))
return;
Visited.insert(MD);

std::string Str;
raw_string_ostream SS(Str);
++Level;
// A placeholder entry to memorize the correct
// position in buffer.
Buffer.emplace_back(std::make_pair(Level, ""));
unsigned InsertIdx = Buffer.size() - 1;

printMetadataImplRec(SS, *MD, *this);
Buffer[InsertIdx].second = std::move(SS.str());
--Level;
}

~MDTreeAsmWriterContext() {
for (const auto &Entry : Buffer) {
MainOS << "\n";
unsigned NumIndent = Entry.first * 2U;
MainOS.indent(NumIndent) << Entry.second;
}
}
};
} // end anonymous namespace

static void printMetadataImpl(raw_ostream &ROS, const Metadata &MD,
ModuleSlotTracker &MST, const Module *M,
bool OnlyAsOperand) {
bool OnlyAsOperand, bool PrintAsTree = false) {
formatted_raw_ostream OS(ROS);

TypePrinting TypePrinter(M);

AsmWriterContext WriterCtx(&TypePrinter, MST.getMachine(), M);
WriteAsOperandInternal(OS, &MD, WriterCtx, /* FromValue */ true);
std::unique_ptr<AsmWriterContext> WriterCtx;
if (PrintAsTree && !OnlyAsOperand)
WriterCtx = std::make_unique<MDTreeAsmWriterContext>(
&TypePrinter, MST.getMachine(), M, OS, &MD);
else
WriterCtx =
std::make_unique<AsmWriterContext>(&TypePrinter, MST.getMachine(), M);

WriteAsOperandInternal(OS, &MD, *WriterCtx, /* FromValue */ true);

auto *N = dyn_cast<MDNode>(&MD);
if (OnlyAsOperand || !N || isa<DIExpression>(MD) || isa<DIArgList>(MD))
return;

OS << " = ";
WriteMDNodeBodyInternal(OS, N, WriterCtx);
WriteMDNodeBodyInternal(OS, N, *WriterCtx);
}

void Metadata::printAsOperand(raw_ostream &OS, const Module *M) const {
Expand All @@ -4697,6 +4771,18 @@ void Metadata::print(raw_ostream &OS, ModuleSlotTracker &MST,
printMetadataImpl(OS, *this, MST, M, /* OnlyAsOperand */ false);
}

void MDNode::printTree(raw_ostream &OS, const Module *M) const {
ModuleSlotTracker MST(M, true);
printMetadataImpl(OS, *this, MST, M, /* OnlyAsOperand */ false,
/*PrintAsTree=*/true);
}

void MDNode::printTree(raw_ostream &OS, ModuleSlotTracker &MST,
const Module *M) const {
printMetadataImpl(OS, *this, MST, M, /* OnlyAsOperand */ false,
/*PrintAsTree=*/true);
}

void ModuleSummaryIndex::print(raw_ostream &ROS, bool IsForDebug) const {
SlotTracker SlotTable(this);
formatted_raw_ostream OS(ROS);
Expand Down Expand Up @@ -4748,6 +4834,15 @@ void Metadata::dump(const Module *M) const {
dbgs() << '\n';
}

LLVM_DUMP_METHOD
void MDNode::dumpTree() const { dumpTree(nullptr); }

LLVM_DUMP_METHOD
void MDNode::dumpTree(const Module *M) const {
printTree(dbgs(), M);
dbgs() << '\n';
}

// Allow printing of ModuleSummaryIndex from the debugger.
LLVM_DUMP_METHOD
void ModuleSummaryIndex::dump() const { print(dbgs(), /*IsForDebug=*/true); }
Expand Down
61 changes: 61 additions & 0 deletions llvm/unittests/IR/MetadataTest.cpp
Expand Up @@ -423,6 +423,67 @@ TEST_F(MDNodeTest, PrintWithDroppedCallOperand) {
ModuleSlotTracker MST(&M);
EXPECT_PRINTER_EQ("!0 = distinct !{}", N0->print(OS, MST));
}

TEST_F(MDNodeTest, PrintTree) {
DILocalScope *Scope = getSubprogram();
DIFile *File = getFile();
DINode::DIFlags Flags = static_cast<DINode::DIFlags>(7);
{
DIType *Type = getDerivedType();
auto *Var = DILocalVariable::get(Context, Scope, "foo", File,
/*LineNo=*/8, Type, /*ArgNo=*/2, Flags,
/*Align=*/8, nullptr);
std::string Expected;
{
raw_string_ostream SS(Expected);
Var->print(SS);
// indent level 1
Scope->print((SS << "\n").indent(2));
File->print((SS << "\n").indent(2));
Type->print((SS << "\n").indent(2));
// indent level 2
auto *BaseType = cast<DIDerivedType>(Type)->getBaseType();
BaseType->print((SS << "\n").indent(4));
}

EXPECT_PRINTER_EQ(Expected, Var->printTree(OS));
}

{
// Test if printTree works correctly when there is
// a cycle in the MDNode and its dependencies.
//
// We're trying to create type like this:
// struct LinkedList {
// LinkedList *Head;
// };
auto *StructTy = cast<DICompositeType>(getCompositeType());
DIType *PointerTy = DIDerivedType::getDistinct(
Context, dwarf::DW_TAG_pointer_type, "", nullptr, 0, nullptr, StructTy,
1, 2, 0, None, DINode::FlagZero);
StructTy->replaceElements(MDTuple::get(Context, PointerTy));

auto *Var = DILocalVariable::get(Context, Scope, "foo", File,
/*LineNo=*/8, StructTy, /*ArgNo=*/2, Flags,
/*Align=*/8, nullptr);
std::string Expected;
{
raw_string_ostream SS(Expected);
Var->print(SS);
// indent level 1
Scope->print((SS << "\n").indent(2));
File->print((SS << "\n").indent(2));
StructTy->print((SS << "\n").indent(2));
// indent level 2
StructTy->getRawElements()->print((SS << "\n").indent(4));
// indent level 3
auto Elements = StructTy->getElements();
Elements[0]->print((SS << "\n").indent(6));
}

EXPECT_PRINTER_EQ(Expected, Var->printTree(OS));
}
}
#undef EXPECT_PRINTER_EQ

TEST_F(MDNodeTest, NullOperand) {
Expand Down

0 comments on commit 475de8d

Please sign in to comment.