LLZK 2.0.0
An open-source IR for Zero Knowledge (ZK) circuits
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 isTemplateSymBinding;
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), isTemplateSymBinding(false) {
43 assert(pathRoot && "'pathRoot' cannot be nullptr");
44 assert(path && "'path' cannot be nullptr");
45 }
46
48 SymbolUseGraphNode()
49 : symbolPathRoot(nullptr), symbolPath(nullptr), isTemplateSymBinding(false) {}
50
52 static bool isRealNodeImpl(const SymbolUseGraphNode *node) { return node->symbolPath != nullptr; }
53
55 void addSuccessor(SymbolUseGraphNode *node);
56
58 void removeSuccessor(SymbolUseGraphNode *node);
59
60 // Provide access to private members.
61 friend class SymbolUseGraph;
62
63public:
66 bool isRealNode() const { return isRealNodeImpl(this); }
67
69 mlir::ModuleOp getSymbolPathRoot() const { return symbolPathRoot; }
70
72 mlir::SymbolRefAttr getSymbolPath() const { return symbolPath; }
73
75 const OpSet &getUserOps() const { return opsThatUseTheSymbol; }
76
78 bool isTemplateSymbolBinding() const { return isTemplateSymBinding; }
79
81 bool hasPredecessor() const {
82 return llvm::find_if(predecessors, isRealNodeImpl) != predecessors.end();
83 }
84 size_t numPredecessors() const { return llvm::count_if(predecessors, isRealNodeImpl); }
85
87 bool hasSuccessor() const {
88 return llvm::find_if(successors, isRealNodeImpl) != successors.end();
89 }
90 size_t numSuccessors() const { return llvm::count_if(successors, isRealNodeImpl); }
91
93 using iterator = llvm::filter_iterator<
94 mlir::SetVector<SymbolUseGraphNode *>::const_iterator, bool (*)(const SymbolUseGraphNode *)>;
95
96 inline iterator predecessors_begin() const { return predecessorIter().begin(); }
97 inline iterator predecessors_end() const { return predecessorIter().end(); }
98 inline iterator successors_begin() const { return successorIter().begin(); }
99 inline iterator successors_end() const { return successorIter().end(); }
100
102 llvm::iterator_range<iterator> predecessorIter() const {
103 return llvm::make_filter_range(predecessors, isRealNodeImpl);
104 }
105
107 llvm::iterator_range<iterator> successorIter() const {
108 return llvm::make_filter_range(successors, isRealNodeImpl);
109 }
110
111 mlir::FailureOr<SymbolLookupResultUntyped>
112 lookupSymbol(mlir::SymbolTableCollection &tables, bool reportMissing = true) const;
113
115 std::string toString(bool showLocations = false) const;
116 void print(
117 llvm::raw_ostream &os, bool showLocations = false, const std::string &locationLinePrefix = ""
118 ) const;
119};
120
125 using NodeMapKeyT = std::pair<mlir::ModuleOp, mlir::SymbolRefAttr>;
127 using NodeMapT = llvm::MapVector<NodeMapKeyT, std::unique_ptr<SymbolUseGraphNode>>;
128
130 NodeMapT nodes;
131
132 // The singleton artificial (i.e., no associated op) root/head and tail nodes of the graph. Every
133 // newly created SymbolUseGraphNode is initially a successor of the root node until a real
134 // successor (if any) is added. Similarly, all leaf nodes in the graph have the tail as successor.
135 //
136 // Implementation note: An actual SymbolUseGraphNode is used instead of lists of head/tail nodes
137 // because the GraphTraits implementations require a single entry node. These nodes are not added
138 // to the `nodes` set and should be transparent to users of this graph (other than through the
139 // GraphTraits `getEntryNode()` function implementations).
140 SymbolUseGraphNode root, tail;
141
143 class NodeIterator final
144 : public llvm::mapped_iterator<
145 NodeMapT::const_iterator, SymbolUseGraphNode *(*)(const NodeMapT::value_type &)> {
146 static SymbolUseGraphNode *unwrap(const NodeMapT::value_type &value) {
147 return value.second.get();
148 }
149
150 public:
152 NodeIterator(NodeMapT::const_iterator it)
153 : llvm::mapped_iterator<
154 NodeMapT::const_iterator, SymbolUseGraphNode *(*)(const NodeMapT::value_type &)>(
155 it, &unwrap
156 ) {}
157 };
158
160 SymbolUseGraphNode *getOrAddNode(
161 mlir::ModuleOp pathRoot, mlir::SymbolRefAttr path, SymbolUseGraphNode *predecessorNode
162 );
163
164 SymbolUseGraphNode *getSymbolUserNode(const mlir::SymbolTable::SymbolUse &u);
165 void buildGraph(mlir::SymbolOpInterface symbolOp);
166
167 // Friend declarations for the specializations of GraphTraits
168 friend struct llvm::GraphTraits<const llzk::SymbolUseGraph *>;
169 friend struct llvm::GraphTraits<llvm::Inverse<const llzk::SymbolUseGraph *>>;
170
171public:
172 SymbolUseGraph(mlir::SymbolOpInterface rootSymbolOp);
173
175 const SymbolUseGraphNode *lookupNode(mlir::ModuleOp pathRoot, mlir::SymbolRefAttr path) const;
176
178 const SymbolUseGraphNode *lookupNode(mlir::SymbolOpInterface symbolDef) const;
179
181 size_t size() const { return nodes.size(); }
182
185 roots_iterator roots_begin() const { return root.successors_begin(); }
186 roots_iterator roots_end() const { return root.successors_end(); }
187
189 inline llvm::iterator_range<roots_iterator> rootsIter() const {
190 return llvm::make_range(roots_begin(), roots_end());
191 }
192
194 using iterator = NodeIterator;
195 iterator begin() const { return nodes.begin(); }
196 iterator end() const { return nodes.end(); }
197
199 inline llvm::iterator_range<iterator> nodesIter() const {
200 return llvm::make_range(begin(), end());
201 }
202
204 inline void dump() const { print(llvm::errs()); }
205 void print(llvm::raw_ostream &os) const;
206
208 void dumpToDotFile(std::string filename = "") const;
209};
210
211} // namespace llzk
212
213namespace llvm {
214
215// Provide graph traits for traversing SymbolUseGraph using standard graph traversals.
216template <> struct GraphTraits<const llzk::SymbolUseGraphNode *> {
218 static NodeRef getEntryNode(NodeRef node) { return node; }
219
222 static ChildIteratorType child_begin(NodeRef node) { return node->successors_begin(); }
223 static ChildIteratorType child_end(NodeRef node) { return node->successors_end(); }
224};
225
226template <>
227struct GraphTraits<const llzk::SymbolUseGraph *>
228 : public GraphTraits<const llzk::SymbolUseGraphNode *> {
230
232 static NodeRef getEntryNode(GraphType g) { return &g->root; }
233
236 static nodes_iterator nodes_begin(GraphType g) { return g->begin(); }
237 static nodes_iterator nodes_end(GraphType g) { return g->end(); }
238
240 static unsigned size(GraphType g) { return g->size(); }
241};
242
243// Provide graph traits for traversing SymbolUseGraph using INVERSE graph traversals.
244template <> struct GraphTraits<Inverse<const llzk::SymbolUseGraphNode *>> {
246 static NodeRef getEntryNode(Inverse<NodeRef> node) { return node.Graph; }
247
250 static ChildIteratorType child_end(NodeRef node) { return node->predecessors_end(); }
251};
252
253template <>
254struct GraphTraits<Inverse<const llzk::SymbolUseGraph *>>
255 : public GraphTraits<Inverse<const llzk::SymbolUseGraphNode *>> {
256 using GraphType = Inverse<const llzk::SymbolUseGraph *>;
257
259 static NodeRef getEntryNode(GraphType g) { return &g.Graph->tail; }
260
263 static nodes_iterator nodes_begin(GraphType g) { return g.Graph->begin(); }
264 static nodes_iterator nodes_end(GraphType g) { return g.Graph->end(); }
265
267 static unsigned size(GraphType g) { return g.Graph->size(); }
268};
269
270// Provide graph traits for printing SymbolUseGraph using dot graph printer.
271template <> struct DOTGraphTraits<const llzk::SymbolUseGraphNode *> : public DefaultDOTGraphTraits {
274
275 DOTGraphTraits(bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {}
276
277 std::string getNodeLabel(NodeRef n, GraphType) { return n->toString(true); }
278};
279
280template <>
281struct DOTGraphTraits<const llzk::SymbolUseGraph *>
282 : public DOTGraphTraits<const llzk::SymbolUseGraphNode *> {
283
284 DOTGraphTraits(bool isSimple = false) : DOTGraphTraits<NodeRef>(isSimple) {}
285
286 static std::string getGraphName(GraphType) { return "Symbol Use Graph"; }
287
288 std::string getNodeLabel(NodeRef n, GraphType g) {
290 }
291};
292
293} // 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
iterator predecessors_end() const
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.
bool isTemplateSymbolBinding() const
Return true iff the symbol is a defined by a TemplateSymbolBindingOpInterface.
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.
void print(llvm::raw_ostream &os, bool showLocations=false, const std::string &locationLinePrefix="") const
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)