LLZK 0.1.0
Veridise's ZK Language IR
Loading...
Searching...
No Matches
SymbolUseGraph.h
Go to the documentation of this file.
1//===-- SymbolUseGraph.h ----------------------------------------*- C++ -*-===//
2//
3// Part of the LLZK Project, under the Apache License v2.0.
4// See LICENSE.txt for license information.
5// Copyright 2025 Veridise Inc.
6// SPDX-License-Identifier: Apache-2.0
7//
8//===----------------------------------------------------------------------===//
9
10#pragma once
11
13
14#include <mlir/IR/BuiltinOps.h>
15#include <mlir/IR/SymbolTable.h>
16
17#include <llvm/ADT/GraphTraits.h>
18#include <llvm/ADT/MapVector.h>
19#include <llvm/ADT/SetVector.h>
20#include <llvm/ADT/SmallPtrSet.h>
21#include <llvm/Support/DOTGraphTraits.h>
22
23#include <utility>
24
25namespace llzk {
26
27class SymbolUseGraphNode {
28 using OpSet = llvm::SmallPtrSet<mlir::Operation *, 3>;
29
30 mlir::ModuleOp symbolPathRoot;
31 mlir::SymbolRefAttr symbolPath;
32 OpSet opsThatUseTheSymbol;
33 bool isStructConstParam;
34
35 /* Tree structure. The SymbolUseGraph owns the nodes so just pointers here. */
37 mlir::SetVector<SymbolUseGraphNode *> predecessors;
39 mlir::SetVector<SymbolUseGraphNode *> successors;
40
41 SymbolUseGraphNode(mlir::ModuleOp pathRoot, mlir::SymbolRefAttr path)
42 : symbolPathRoot(pathRoot), symbolPath(path), isStructConstParam(false) {
43 assert(pathRoot && "'pathRoot' cannot be nullptr");
44 assert(path && "'path' cannot be nullptr");
45 }
46
48 SymbolUseGraphNode() : symbolPathRoot(nullptr), symbolPath(nullptr), isStructConstParam(false) {}
49
51 static bool isRealNodeImpl(const SymbolUseGraphNode *node) { return node->symbolPath != nullptr; }
52
54 void addSuccessor(SymbolUseGraphNode *node);
55
57 void removeSuccessor(SymbolUseGraphNode *node);
58
59 // Provide access to private members.
60 friend class SymbolUseGraph;
61
62public:
65 bool isRealNode() const { return isRealNodeImpl(this); }
66
68 mlir::ModuleOp getSymbolPathRoot() const { return symbolPathRoot; }
69
71 mlir::SymbolRefAttr getSymbolPath() const { return symbolPath; }
72
74 const OpSet &getUserOps() const { return opsThatUseTheSymbol; }
75
77 bool isStructParam() const { return isStructConstParam; }
78
80 bool hasPredecessor() const {
81 return llvm::find_if(predecessors, isRealNodeImpl) != predecessors.end();
82 }
83 size_t numPredecessors() const { return llvm::count_if(predecessors, isRealNodeImpl); }
84
86 bool hasSuccessor() const {
87 return llvm::find_if(successors, isRealNodeImpl) != successors.end();
88 }
89 size_t numSuccessors() const { return llvm::count_if(successors, isRealNodeImpl); }
90
92 using iterator = llvm::filter_iterator<
93 mlir::SetVector<SymbolUseGraphNode *>::const_iterator, bool (*)(const SymbolUseGraphNode *)>;
94
95 inline iterator predecessors_begin() const { return predecessorIter().begin(); }
96 inline iterator predecessors_end() const { return predecessorIter().end(); }
97 inline iterator successors_begin() const { return successorIter().begin(); }
98 inline iterator successors_end() const { return successorIter().end(); }
99
101 llvm::iterator_range<iterator> predecessorIter() const {
102 return llvm::make_filter_range(predecessors, isRealNodeImpl);
103 }
104
106 llvm::iterator_range<iterator> successorIter() const {
107 return llvm::make_filter_range(successors, isRealNodeImpl);
108 }
109
110 mlir::FailureOr<SymbolLookupResultUntyped>
111 lookupSymbol(mlir::SymbolTableCollection &tables, bool reportMissing = true) const;
112
114 std::string toString(bool showLocations = false) const;
115 void print(
116 llvm::raw_ostream &os, bool showLocations = false, std::string locationLinePrefix = ""
117 ) const;
118};
119
124 using NodeMapKeyT = std::pair<mlir::ModuleOp, mlir::SymbolRefAttr>;
126 using NodeMapT = llvm::MapVector<NodeMapKeyT, std::unique_ptr<SymbolUseGraphNode>>;
127
129 NodeMapT nodes;
130
131 // The singleton artificial (i.e., no associated op) root/head and tail nodes of the graph. Every
132 // newly created SymbolUseGraphNode is initially a successor of the root node until a real
133 // successor (if any) is added. Similarly, all leaf nodes in the graph have the tail as successor.
134 //
135 // Implementation note: An actual SymbolUseGraphNode is used instead of lists of head/tail nodes
136 // because the GraphTraits implementations require a single entry node. These nodes are not added
137 // to the `nodes` set and should be transparent to users of this graph (other than through the
138 // GraphTraits `getEntryNode()` function implementations).
139 SymbolUseGraphNode root, tail;
140
142 class NodeIterator final
143 : public llvm::mapped_iterator<
144 NodeMapT::const_iterator, SymbolUseGraphNode *(*)(const NodeMapT::value_type &)> {
145 static SymbolUseGraphNode *unwrap(const NodeMapT::value_type &value) {
146 return value.second.get();
147 }
148
149 public:
151 NodeIterator(NodeMapT::const_iterator it)
152 : llvm::mapped_iterator<
153 NodeMapT::const_iterator, SymbolUseGraphNode *(*)(const NodeMapT::value_type &)>(
154 it, &unwrap
155 ) {}
156 };
157
159 SymbolUseGraphNode *getOrAddNode(
160 mlir::ModuleOp pathRoot, mlir::SymbolRefAttr path, SymbolUseGraphNode *predecessorNode
161 );
162
163 SymbolUseGraphNode *getSymbolUserNode(const mlir::SymbolTable::SymbolUse &u);
164 void buildGraph(mlir::SymbolOpInterface symbolOp);
165
166 // Friend declarations for the specializations of GraphTraits
167 friend struct llvm::GraphTraits<const llzk::SymbolUseGraph *>;
168 friend struct llvm::GraphTraits<llvm::Inverse<const llzk::SymbolUseGraph *>>;
169
170public:
171 SymbolUseGraph(mlir::SymbolOpInterface rootSymbolOp);
172
174 const SymbolUseGraphNode *lookupNode(mlir::ModuleOp pathRoot, mlir::SymbolRefAttr path) const;
175
177 const SymbolUseGraphNode *lookupNode(mlir::SymbolOpInterface symbolDef) const;
178
180 size_t size() const { return nodes.size(); }
181
184 roots_iterator roots_begin() const { return root.successors_begin(); }
185 roots_iterator roots_end() const { return root.successors_end(); }
186
188 inline llvm::iterator_range<roots_iterator> rootsIter() const {
189 return llvm::make_range(roots_begin(), roots_end());
190 }
191
193 using iterator = NodeIterator;
194 iterator begin() const { return nodes.begin(); }
195 iterator end() const { return nodes.end(); }
196
198 inline llvm::iterator_range<iterator> nodesIter() const {
199 return llvm::make_range(begin(), end());
200 }
201
203 inline void dump() const { print(llvm::errs()); }
204 void print(llvm::raw_ostream &os) const;
205
207 void dumpToDotFile(std::string filename = "") const;
208};
209
210} // namespace llzk
211
212namespace llvm {
213
214// Provide graph traits for traversing SymbolUseGraph using standard graph traversals.
215template <> struct GraphTraits<const llzk::SymbolUseGraphNode *> {
217 static NodeRef getEntryNode(NodeRef node) { return node; }
218
221 static ChildIteratorType child_begin(NodeRef node) { return node->successors_begin(); }
222 static ChildIteratorType child_end(NodeRef node) { return node->successors_end(); }
223};
224
225template <>
226struct GraphTraits<const llzk::SymbolUseGraph *>
227 : public GraphTraits<const llzk::SymbolUseGraphNode *> {
229
231 static NodeRef getEntryNode(GraphType g) { return &g->root; }
232
235 static nodes_iterator nodes_begin(GraphType g) { return g->begin(); }
236 static nodes_iterator nodes_end(GraphType g) { return g->end(); }
237
239 static unsigned size(GraphType g) { return g->size(); }
240};
241
242// Provide graph traits for traversing SymbolUseGraph using INVERSE graph traversals.
243template <> struct GraphTraits<Inverse<const llzk::SymbolUseGraphNode *>> {
245 static NodeRef getEntryNode(Inverse<NodeRef> node) { return node.Graph; }
246
249 static ChildIteratorType child_end(NodeRef node) { return node->predecessors_end(); }
250};
251
252template <>
253struct GraphTraits<Inverse<const llzk::SymbolUseGraph *>>
254 : public GraphTraits<Inverse<const llzk::SymbolUseGraphNode *>> {
255 using GraphType = Inverse<const llzk::SymbolUseGraph *>;
256
258 static NodeRef getEntryNode(GraphType g) { return &g.Graph->tail; }
259
262 static nodes_iterator nodes_begin(GraphType g) { return g.Graph->begin(); }
263 static nodes_iterator nodes_end(GraphType g) { return g.Graph->end(); }
264
266 static unsigned size(GraphType g) { return g.Graph->size(); }
267};
268
269// Provide graph traits for printing SymbolUseGraph using dot graph printer.
270template <> struct DOTGraphTraits<const llzk::SymbolUseGraphNode *> : public DefaultDOTGraphTraits {
273
274 DOTGraphTraits(bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {}
275
276 std::string getNodeLabel(NodeRef n, GraphType) { return n->toString(true); }
277};
278
279template <>
280struct DOTGraphTraits<const llzk::SymbolUseGraph *>
281 : public DOTGraphTraits<const llzk::SymbolUseGraphNode *> {
282
283 DOTGraphTraits(bool isSimple = false) : DOTGraphTraits<NodeRef>(isSimple) {}
284
285 static std::string getGraphName(GraphType) { return "Symbol Use Graph"; }
286
287 std::string getNodeLabel(NodeRef n, GraphType g) {
289 }
290};
291
292} // namespace llvm
This file defines methods symbol lookup across LLZK operations and included files.
size_t numSuccessors() const
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.
bool hasSuccessor() const
Return true if this node has any successors.
iterator predecessors_begin() const
size_t numPredecessors() const
void print(llvm::raw_ostream &os, bool showLocations=false, std::string locationLinePrefix="") const
iterator predecessors_end() const
bool isStructParam() const
Return true iff the symbol is a struct constant parameter name.
bool hasPredecessor() const
Return true if this node has any predecessors.
std::string toString(bool showLocations=false) const
Print the node in a human readable format.
iterator successors_begin() const
llvm::iterator_range< iterator > predecessorIter() const
Range over predecessor nodes.
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.
iterator successors_end() const
mlir::ModuleOp getSymbolPathRoot() const
Return the root ModuleOp for the path.
llvm::iterator_range< iterator > successorIter() const
Range over successor nodes.
llvm::filter_iterator< mlir::SetVector< SymbolUseGraphNode * >::const_iterator, bool(*)(const SymbolUseGraphNode *)> iterator
Iterator over predecessors/successors.
Builds a graph structure representing the relationships between symbols and their uses.
size_t size() const
Return the total number of nodes in the graph.
void dump() const
Dump the graph in a human readable format.
roots_iterator roots_end() const
SymbolUseGraphNode::iterator roots_iterator
Iterator over the root nodes (i.e., nodes that have no predecessors).
llvm::iterator_range< roots_iterator > rootsIter() const
Range over root nodes (i.e., nodes that have no predecessors).
void dumpToDotFile(std::string filename="") const
Dump the graph to file in dot graph format.
iterator begin() const
iterator end() const
roots_iterator roots_begin() const
NodeIterator iterator
An iterator over the nodes of the graph.
SymbolUseGraph(mlir::SymbolOpInterface rootSymbolOp)
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.
std::string getNodeLabel(NodeRef n, GraphType g)
llzk::SymbolUseGraph::iterator nodes_iterator
nodes_iterator/begin/end - Allow iteration over all nodes in the graph.
static NodeRef getEntryNode(GraphType g)
The entry node into the inverse graph is the tail node.
static unsigned size(GraphType g)
Return total number of nodes in the graph.
static ChildIteratorType child_end(NodeRef node)
llzk::SymbolUseGraphNode::iterator ChildIteratorType
ChildIteratorType/begin/end - Allow iteration over all nodes in the graph.
static ChildIteratorType child_begin(NodeRef node)
static nodes_iterator nodes_begin(GraphType g)
llzk::SymbolUseGraph::iterator nodes_iterator
nodes_iterator/begin/end - Allow iteration over all nodes in the graph.
static unsigned size(GraphType g)
Return total number of nodes in the graph.
static NodeRef getEntryNode(GraphType g)
The entry node into the graph is the root node.
static nodes_iterator nodes_end(GraphType g)