21#include <mlir/IR/BuiltinOps.h>
23#include <llvm/ADT/SmallPtrSet.h>
24#include <llvm/ADT/SmallSet.h>
25#include <llvm/Support/GraphWriter.h>
36 if (this->successors.insert(node)) {
37 node->predecessors.insert(
this);
42 if (this->successors.remove(node)) {
43 node->predecessors.remove(
this);
47FailureOr<SymbolLookupResultUntyped>
54 if (succeeded(res) || !reportMissing) {
58 return lookupFrom->emitError().append(
59 "Could not find symbol referenced in UseGraph: ",
getSymbolPath()
70R getPathAndCall(SymbolOpInterface defOp, llvm::function_ref<R(ModuleOp, SymbolRefAttr)> callback) {
78 auto diag = defOp.emitError(
"in SymbolUseGraph, failed to build symbol path");
79 diag.attachNote(defOp.getLoc()).append(
"for this SymbolOp");
83 return callback(foundRoot, path.value());
89 assert(rootSymbolTableOp && rootSymbolTableOp->hasTrait<OpTrait::SymbolTable>());
90 buildGraph(rootSymbolTableOp);
93void SymbolUseGraph::buildGraph(Operation *symbolTableOp) {
96 DenseMap<Region *, SymbolOpInterface> regionOwners;
97 DenseMap<Operation *, SymbolUseGraphNode *> ownerNodes;
98 auto getOwner = [®ionOwners](Operation *user) {
99 if (
auto symbol = llvm::dyn_cast<SymbolOpInterface>(user)) {
102 SmallVector<Region *> pending;
103 Region *region = user->getParentRegion();
104 SymbolOpInterface owner;
106 auto found = regionOwners.find(region);
107 if (found != regionOwners.end()) {
108 owner = found->second;
111 pending.push_back(region);
112 Operation *parent = region->getParentOp();
113 owner = llvm::dyn_cast<SymbolOpInterface>(parent);
117 region = parent->getParentRegion();
119 for (Region *visited : pending) {
120 regionOwners.try_emplace(visited, owner);
124 auto getUserNode = [
this, &getOwner, &ownerNodes](Operation *user) {
125 SymbolOpInterface owner = getOwner(user);
126 auto found = ownerNodes.find(owner.getOperation());
127 if (found != ownerNodes.end()) {
128 return found->second;
130 auto *node = getPathAndCall<SymbolUseGraphNode *>(
131 owner, [
this, owner](ModuleOp pathRoot, SymbolRefAttr path) {
132 auto *n = getOrAddNode(pathRoot, path,
nullptr);
133 n->opsThatUseTheSymbol.insert(owner);
137 ownerNodes.try_emplace(owner.getOperation(), node);
140 auto walkFn = [
this, &getUserNode](Operation *op, bool) {
141 assert(op->hasTrait<OpTrait::SymbolTable>());
143 if (failed(opRootModule)) {
149 for (SymbolTable::SymbolUse u : usesOpt.value()) {
150 bool isTemplateSymbol =
false;
151 Operation *user = u.getUser();
152 SymbolRefAttr symRef = u.getSymbolRef();
155 if (FlatSymbolRefAttr flatSymRef = llvm::dyn_cast<FlatSymbolRefAttr>(symRef)) {
156 if (
auto fref = llvm::dyn_cast<component::MemberRefOpInterface>(user);
157 fref && fref.getMemberNameAttr() == flatSymRef) {
160 StringAttr localName = flatSymRef.getAttr();
162 userTemplate.hasConstNamed<polymorphic::TemplateSymbolBindingOpInterface>(
165 if (isTemplateSymbol) {
169 assert(succeeded(parentPath));
174 auto *node = this->getOrAddNode(opRootModule.value(), symRef, getUserNode(user));
175 node->isTemplateSymBinding = isTemplateSymbol;
176 node->opsThatUseTheSymbol.insert(user);
180 SymbolTable::walkSymbolTables(symbolTableOp,
true, walkFn);
183 for (SymbolUseGraphNode *n :
nodesIter()) {
184 if (!n->hasSuccessor()) {
185 n->addSuccessor(&tail);
193 NodeMapKeyT key = std::make_pair(pathRoot, path);
194 std::unique_ptr<SymbolUseGraphNode> &nodeRef = nodes[key];
196 nodeRef.reset(
new SymbolUseGraphNode(pathRoot, path));
199 if (predecessorNode) {
200 predecessorNode->addSuccessor(nodeRef.get());
202 root.addSuccessor(nodeRef.get());
204 }
else if (predecessorNode) {
207 SymbolUseGraphNode *node = nodeRef.get();
208 predecessorNode->addSuccessor(node);
209 if (node != predecessorNode) {
210 root.removeSuccessor(node);
213 return nodeRef.get();
217 NodeMapKeyT key = std::make_pair(pathRoot, path);
218 const auto *it = nodes.find(key);
219 return it == nodes.end() ? nullptr : it->second.get();
223 return getPathAndCall<const SymbolUseGraphNode *>(symbolDef, [
this](ModuleOp r, SymbolRefAttr p) {
238inline void safeAppendPathRoot(llvm::raw_ostream &os, ModuleOp root) {
241 if (succeeded(unambiguousRoot)) {
242 os << unambiguousRoot.value() <<
'\n';
244 os <<
"<<unknown path>>\n";
247 os <<
"<<NULL MODULE>>\n";
254 llvm::raw_ostream &os,
bool showLocations,
const std::string &locationLinePrefix
256 os <<
'\'' << symbolPath <<
'\'';
257 if (isTemplateSymBinding) {
258 os <<
" (struct param)";
260 os <<
" with root module ";
261 safeAppendPathRoot(os, symbolPathRoot);
266 llvm::SmallSet<mlir::Location, 3, LocationComparator> locations;
268 locations.insert(user->getLoc());
270 for (Location loc : locations) {
271 os << locationLinePrefix << loc <<
'\n';
280 SmallPtrSet<SymbolUseGraphNode *, 16> done;
285 if (!done.insert(node).second) {
289 os <<
"// - Node : [" << node <<
"] ";
290 node->print(os,
true,
"// --- ");
292 os <<
"// --- Predecessors : [";
293 llvm::interleaveComma(
294 llvm::make_filter_range(
300 os <<
"// --- Successors : [";
301 llvm::interleaveComma(node->successorIter(), os);
309 os <<
"// ---- SymbolUseGraph ----\n";
313 os <<
"// ------------------------\n";
314 assert(done.size() == this->size() &&
"All nodes were not printed!");
319 llvm::WriteGraph(
this,
"SymbolUseGraph",
false, title, std::move(filename));
mlir::FailureOr< SymbolLookupResultUntyped > lookupSymbol(mlir::SymbolTableCollection &tables, bool reportMissing=true) const
bool isRealNode() const
Return 'false' iff this node is an artificial node created for the graph head/tail.
std::string toString(bool showLocations=false) const
Print the node in a human readable format.
mlir::SymbolRefAttr getSymbolPath() const
The symbol path+name relative to the closest root ModuleOp.
const OpSet & getUserOps() const
The set of operations that use the symbol.
mlir::ModuleOp getSymbolPathRoot() const
Return the root ModuleOp for the path.
llvm::iterator_range< iterator > successorIter() const
Range over successor nodes.
void print(llvm::raw_ostream &os, bool showLocations=false, const std::string &locationLinePrefix="") const
void dumpToDotFile(std::string filename="") const
Dump the graph to file in dot graph format.
llvm::iterator_range< iterator > nodesIter() const
Range over all nodes in the graph.
void print(llvm::raw_ostream &os) const
const SymbolUseGraphNode * lookupNode(mlir::ModuleOp pathRoot, mlir::SymbolRefAttr path) const
Return the existing node for the symbol reference relative to the given module, else nullptr.
SymbolUseGraph(mlir::Operation *rootSymbolTableOp)
Build a use graph rooted at an operation with the SymbolTable trait.
std::optional< mlir::SymbolTable::UseRange > getSymbolUses(mlir::Operation *from)
Get an iterator range for all of the uses, for any symbol, that are nested within the given operation...
FailureOr< ModuleOp > getRootModule(Operation *from)
SymbolRefAttr appendLeaf(SymbolRefAttr orig, FlatSymbolRefAttr newLeaf)
std::string buildStringViaPrint(const T &base, Args &&...args)
Generate a string by calling base.print(llvm::raw_ostream &) on a stream backed by the returned strin...
mlir::FailureOr< SymbolLookupResultUntyped > lookupSymbolIn(mlir::SymbolTableCollection &tables, mlir::SymbolRefAttr symbol, Within &&lookupWithin, mlir::Operation *origin, bool reportMissing=true)
FailureOr< SymbolRefAttr > getPathFromTopRoot(SymbolOpInterface to, ModuleOp *foundRoot)
OpClass getSelfOrParentOfType(mlir::Operation *op)
Return the closest surrounding parent/ancestor operation that is of type 'OpClass',...
FailureOr< SymbolRefAttr > getPathFromRoot(SymbolOpInterface to, ModuleOp *foundRoot)
static std::string getGraphName(GraphType)