#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"
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
WriteAsOperandInternal(Out, V, WriterCtx);
} else {
WriteAsOperandInternal(Out, MD, WriterCtx);
+ WriterCtx.onWriteMetadataAsOperand(MD);
}
if (mi + 1 != me)
Out << ", ";
return;
}
WriteAsOperandInternal(Out, MD, WriterCtx);
+ WriterCtx.onWriteMetadataAsOperand(MD);
}
void MDFieldPrinter::printMetadata(StringRef Name, const Metadata *MD,
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 {
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);
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); }
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) {