//===- RegionPrinter.cpp - Print regions tree pass ------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Print out the region tree of a function using dotty/graphviz. //===----------------------------------------------------------------------===// #include "llvm/Analysis/RegionPrinter.h" #include "llvm/ADT/DepthFirstIterator.h" #include "llvm/ADT/PostOrderIterator.h" #include "llvm/ADT/Statistic.h" #include "llvm/Analysis/DOTGraphTraitsPass.h" #include "llvm/Analysis/Passes.h" #include "llvm/Analysis/RegionInfo.h" #include "llvm/Analysis/RegionIterator.h" #include "llvm/InitializePasses.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #ifndef NDEBUG #include "llvm/IR/LegacyPassManager.h" #endif using namespace llvm; //===----------------------------------------------------------------------===// /// onlySimpleRegion - Show only the simple regions in the RegionViewer. static cl::opt onlySimpleRegions("only-simple-regions", cl::desc("Show only simple regions in the graphviz viewer"), cl::Hidden, cl::init(false)); namespace llvm { template<> struct DOTGraphTraits : public DefaultDOTGraphTraits { DOTGraphTraits (bool isSimple=false) : DefaultDOTGraphTraits(isSimple) {} std::string getNodeLabel(RegionNode *Node, RegionNode *Graph) { if (!Node->isSubRegion()) { BasicBlock *BB = Node->getNodeAs(); if (isSimple()) return DOTGraphTraits ::getSimpleNodeLabel(BB, nullptr); else return DOTGraphTraits ::getCompleteNodeLabel(BB, nullptr); } return "Not implemented"; } }; template <> struct DOTGraphTraits : public DOTGraphTraits { DOTGraphTraits (bool isSimple = false) : DOTGraphTraits(isSimple) {} static std::string getGraphName(const RegionInfo *) { return "Region Graph"; } std::string getNodeLabel(RegionNode *Node, RegionInfo *G) { return DOTGraphTraits::getNodeLabel( Node, reinterpret_cast(G->getTopLevelRegion())); } std::string getEdgeAttributes(RegionNode *srcNode, GraphTraits::ChildIteratorType CI, RegionInfo *G) { RegionNode *destNode = *CI; if (srcNode->isSubRegion() || destNode->isSubRegion()) return ""; // In case of a backedge, do not use it to define the layout of the nodes. BasicBlock *srcBB = srcNode->getNodeAs(); BasicBlock *destBB = destNode->getNodeAs(); Region *R = G->getRegionFor(destBB); while (R && R->getParent()) if (R->getParent()->getEntry() == destBB) R = R->getParent(); else break; if (R && R->getEntry() == destBB && R->contains(srcBB)) return "constraint=false"; return ""; } // Print the cluster of the subregions. This groups the single basic blocks // and adds a different background color for each group. static void printRegionCluster(const Region &R, GraphWriter &GW, unsigned depth = 0) { raw_ostream &O = GW.getOStream(); O.indent(2 * depth) << "subgraph cluster_" << static_cast(&R) << " {\n"; O.indent(2 * (depth + 1)) << "label = \"\";\n"; if (!onlySimpleRegions || R.isSimple()) { O.indent(2 * (depth + 1)) << "style = filled;\n"; O.indent(2 * (depth + 1)) << "color = " << ((R.getDepth() * 2 % 12) + 1) << "\n"; } else { O.indent(2 * (depth + 1)) << "style = solid;\n"; O.indent(2 * (depth + 1)) << "color = " << ((R.getDepth() * 2 % 12) + 2) << "\n"; } for (const auto &RI : R) printRegionCluster(*RI, GW, depth + 1); const RegionInfo &RI = *static_cast(R.getRegionInfo()); for (auto *BB : R.blocks()) if (RI.getRegionFor(BB) == &R) O.indent(2 * (depth + 1)) << "Node" << static_cast(RI.getTopLevelRegion()->getBBNode(BB)) << ";\n"; O.indent(2 * depth) << "}\n"; } static void addCustomGraphFeatures(const RegionInfo *G, GraphWriter &GW) { raw_ostream &O = GW.getOStream(); O << "\tcolorscheme = \"paired12\"\n"; printRegionCluster(*G->getTopLevelRegion(), GW, 4); } }; } //end namespace llvm namespace { struct RegionInfoPassGraphTraits { static RegionInfo *getGraph(RegionInfoPass *RIP) { return &RIP->getRegionInfo(); } }; struct RegionPrinter : public DOTGraphTraitsPrinter { static char ID; RegionPrinter() : DOTGraphTraitsPrinter("reg", ID) { initializeRegionPrinterPass(*PassRegistry::getPassRegistry()); } }; char RegionPrinter::ID = 0; struct RegionOnlyPrinter : public DOTGraphTraitsPrinter { static char ID; RegionOnlyPrinter() : DOTGraphTraitsPrinter("reg", ID) { initializeRegionOnlyPrinterPass(*PassRegistry::getPassRegistry()); } }; char RegionOnlyPrinter::ID = 0; struct RegionViewer : public DOTGraphTraitsViewer { static char ID; RegionViewer() : DOTGraphTraitsViewer("reg", ID) { initializeRegionViewerPass(*PassRegistry::getPassRegistry()); } }; char RegionViewer::ID = 0; struct RegionOnlyViewer : public DOTGraphTraitsViewer { static char ID; RegionOnlyViewer() : DOTGraphTraitsViewer("regonly", ID) { initializeRegionOnlyViewerPass(*PassRegistry::getPassRegistry()); } }; char RegionOnlyViewer::ID = 0; } //end anonymous namespace INITIALIZE_PASS(RegionPrinter, "dot-regions", "Print regions of function to 'dot' file", true, true) INITIALIZE_PASS( RegionOnlyPrinter, "dot-regions-only", "Print regions of function to 'dot' file (with no function bodies)", true, true) INITIALIZE_PASS(RegionViewer, "view-regions", "View regions of function", true, true) INITIALIZE_PASS(RegionOnlyViewer, "view-regions-only", "View regions of function (with no function bodies)", true, true) FunctionPass *llvm::createRegionPrinterPass() { return new RegionPrinter(); } FunctionPass *llvm::createRegionOnlyPrinterPass() { return new RegionOnlyPrinter(); } FunctionPass* llvm::createRegionViewerPass() { return new RegionViewer(); } FunctionPass* llvm::createRegionOnlyViewerPass() { return new RegionOnlyViewer(); } #ifndef NDEBUG static void viewRegionInfo(RegionInfo *RI, bool ShortNames) { assert(RI && "Argument must be non-null"); llvm::Function *F = RI->getTopLevelRegion()->getEntry()->getParent(); std::string GraphName = DOTGraphTraits::getGraphName(RI); llvm::ViewGraph(RI, "reg", ShortNames, Twine(GraphName) + " for '" + F->getName() + "' function"); } static void invokeFunctionPass(const Function *F, FunctionPass *ViewerPass) { assert(F && "Argument must be non-null"); assert(!F->isDeclaration() && "Function must have an implementation"); // The viewer and analysis passes do not modify anything, so we can safely // remove the const qualifier auto NonConstF = const_cast(F); llvm::legacy::FunctionPassManager FPM(NonConstF->getParent()); FPM.add(ViewerPass); FPM.doInitialization(); FPM.run(*NonConstF); FPM.doFinalization(); } void llvm::viewRegion(RegionInfo *RI) { viewRegionInfo(RI, false); } void llvm::viewRegion(const Function *F) { invokeFunctionPass(F, createRegionViewerPass()); } void llvm::viewRegionOnly(RegionInfo *RI) { viewRegionInfo(RI, true); } void llvm::viewRegionOnly(const Function *F) { invokeFunctionPass(F, createRegionOnlyViewerPass()); } #endif