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/Support/DOTGraphTraits.h>
21
22#include <utility>
23
24namespace llzk {
25
26class SymbolUseGraphNode {
27 mlir::ModuleOp symbolPathRoot;
28 mlir::SymbolRefAttr symbolPath;
29 bool isStructConstParam;
30
31 /* Tree structure. The SymbolUseGraph owns the nodes so just pointers here. */
33 mlir::SetVector<SymbolUseGraphNode *> predecessors;
35 mlir::SetVector<SymbolUseGraphNode *> successors;
36
37 SymbolUseGraphNode(mlir::ModuleOp pathRoot, mlir::SymbolRefAttr path)
38 : symbolPathRoot(pathRoot), symbolPath(path), isStructConstParam(false) {
39 assert(pathRoot && "'pathRoot' cannot be nullptr");
40 assert(path && "'path' cannot be nullptr");
41 }
42
44 SymbolUseGraphNode() : symbolPathRoot(nullptr), symbolPath(nullptr), isStructConstParam(false) {}
45
47 static bool isRealNodeImpl(const SymbolUseGraphNode *node) { return node->symbolPath != nullptr; }
48
50 void addSuccessor(SymbolUseGraphNode *node);
51
53 void removeSuccessor(SymbolUseGraphNode *node);
54
55 // Provide access to private members.
56 friend class SymbolUseGraph;
57
58public:
61 bool isRealNode() const { return isRealNodeImpl(this); }
62
64 mlir::ModuleOp getSymbolPathRoot() const { return symbolPathRoot; }
65
67 mlir::SymbolRefAttr getSymbolPath() const { return symbolPath; }
68
70 bool isStructParam() const { return isStructConstParam; }
71
73 bool hasPredecessor() const {
74 return llvm::find_if(predecessors, isRealNodeImpl) != predecessors.end();
75 }
76 size_t numPredecessors() const { return llvm::count_if(predecessors, isRealNodeImpl); }
77
79 bool hasSuccessor() const {
80 return llvm::find_if(successors, isRealNodeImpl) != successors.end();
81 }
82 size_t numSuccessors() const { return llvm::count_if(successors, isRealNodeImpl); }
83
85 using iterator = llvm::filter_iterator<
86 mlir::SetVector<SymbolUseGraphNode *>::const_iterator, bool (*)(const SymbolUseGraphNode *)>;
87
88 inline iterator predecessors_begin() const { return predecessorIter().begin(); }
89 inline iterator predecessors_end() const { return predecessorIter().end(); }
90 inline iterator successors_begin() const { return successorIter().begin(); }
91 inline iterator successors_end() const { return successorIter().end(); }
92
94 llvm::iterator_range<iterator> predecessorIter() const {
95 return llvm::make_filter_range(predecessors, isRealNodeImpl);
96 }
97
99 llvm::iterator_range<iterator> successorIter() const {
100 return llvm::make_filter_range(successors, isRealNodeImpl);
101 }
102
103 mlir::FailureOr<SymbolLookupResultUntyped>
104 lookupSymbol(mlir::SymbolTableCollection &tables, bool reportMissing = true) const;
105
107 std::string toString() const;
108 void print(llvm::raw_ostream &os) const;
109};
110
115 using NodeMapKeyT = std::pair<mlir::ModuleOp, mlir::SymbolRefAttr>;
117 using NodeMapT = llvm::MapVector<NodeMapKeyT, std::unique_ptr<SymbolUseGraphNode>>;
118
120 NodeMapT nodes;
121
122 // The singleton artificial (i.e., no associated op) root/head and tail nodes of the graph. Every
123 // newly created SymbolUseGraphNode is initially a successor of the root node until a real
124 // successor (if any) is added. Similarly, all leaf nodes in the graph have the tail as successor.
125 //
126 // Implementation note: An actual SymbolUseGraphNode is used instead of lists of head/tail nodes
127 // because the GraphTraits implementations require a single entry node. These nodes are not added
128 // to the `nodes` set and should be transparent to users of this graph (other than through the
129 // GraphTraits `getEntryNode()` function implementations).
130 SymbolUseGraphNode root, tail;
131
133 class NodeIterator final
134 : public llvm::mapped_iterator<
135 NodeMapT::const_iterator, SymbolUseGraphNode *(*)(const NodeMapT::value_type &)> {
136 static SymbolUseGraphNode *unwrap(const NodeMapT::value_type &value) {
137 return value.second.get();
138 }
139
140 public:
142 NodeIterator(NodeMapT::const_iterator it)
143 : llvm::mapped_iterator<
144 NodeMapT::const_iterator, SymbolUseGraphNode *(*)(const NodeMapT::value_type &)>(
145 it, &unwrap
146 ) {}
147 };
148
150 SymbolUseGraphNode *getOrAddNode(
151 mlir::ModuleOp pathRoot, mlir::SymbolRefAttr path, SymbolUseGraphNode *predecessorNode
152 );
153
154 SymbolUseGraphNode *getSymbolUserNode(const mlir::SymbolTable::SymbolUse &u);
155 void buildGraph(mlir::SymbolOpInterface symbolOp);
156
157 // Friend declarations for the specializations of GraphTraits
158 friend struct llvm::GraphTraits<const llzk::SymbolUseGraph *>;
159 friend struct llvm::GraphTraits<llvm::Inverse<const llzk::SymbolUseGraph *>>;
160
161public:
162 SymbolUseGraph(mlir::SymbolOpInterface rootSymbolOp);
163
165 const SymbolUseGraphNode *lookupNode(mlir::ModuleOp pathRoot, mlir::SymbolRefAttr path) const;
166
168 const SymbolUseGraphNode *lookupNode(mlir::SymbolOpInterface symbolDef) const;
169
171 size_t size() const { return nodes.size(); }
172
175 roots_iterator roots_begin() const { return root.successors_begin(); }
176 roots_iterator roots_end() const { return root.successors_end(); }
177
179 inline llvm::iterator_range<roots_iterator> rootsIter() const {
180 return llvm::make_range(roots_begin(), roots_end());
181 }
182
184 using iterator = NodeIterator;
185 iterator begin() const { return nodes.begin(); }
186 iterator end() const { return nodes.end(); }
187
189 inline llvm::iterator_range<iterator> nodesIter() const {
190 return llvm::make_range(begin(), end());
191 }
192
194 inline void dump() const { print(llvm::errs()); }
195 void print(llvm::raw_ostream &os) const;
196
198 void dumpToDotFile(std::string filename = "") const;
199};
200
201} // namespace llzk
202
203namespace llvm {
204
205// Provide graph traits for traversing SymbolUseGraph using standard graph traversals.
206template <> struct GraphTraits<const llzk::SymbolUseGraphNode *> {
208 static NodeRef getEntryNode(NodeRef node) { return node; }
209
212 static ChildIteratorType child_begin(NodeRef node) { return node->successors_begin(); }
213 static ChildIteratorType child_end(NodeRef node) { return node->successors_end(); }
214};
215
216template <>
217struct GraphTraits<const llzk::SymbolUseGraph *>
218 : public GraphTraits<const llzk::SymbolUseGraphNode *> {
220
222 static NodeRef getEntryNode(GraphType g) { return &g->root; }
223
226 static nodes_iterator nodes_begin(GraphType g) { return g->begin(); }
227 static nodes_iterator nodes_end(GraphType g) { return g->end(); }
228
230 static unsigned size(GraphType g) { return g->size(); }
231};
232
233// Provide graph traits for traversing SymbolUseGraph using INVERSE graph traversals.
234template <> struct GraphTraits<Inverse<const llzk::SymbolUseGraphNode *>> {
236 static NodeRef getEntryNode(Inverse<NodeRef> node) { return node.Graph; }
237
240 static ChildIteratorType child_end(NodeRef node) { return node->predecessors_end(); }
241};
242
243template <>
244struct GraphTraits<Inverse<const llzk::SymbolUseGraph *>>
245 : public GraphTraits<Inverse<const llzk::SymbolUseGraphNode *>> {
246 using GraphType = Inverse<const llzk::SymbolUseGraph *>;
247
249 static NodeRef getEntryNode(GraphType g) { return &g.Graph->tail; }
250
253 static nodes_iterator nodes_begin(GraphType g) { return g.Graph->begin(); }
254 static nodes_iterator nodes_end(GraphType g) { return g.Graph->end(); }
255
257 static unsigned size(GraphType g) { return g.Graph->size(); }
258};
259
260// Provide graph traits for printing SymbolUseGraph using dot graph printer.
261template <> struct DOTGraphTraits<const llzk::SymbolUseGraphNode *> : public DefaultDOTGraphTraits {
264
265 DOTGraphTraits(bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {}
266
267 std::string getNodeLabel(NodeRef n, GraphType) { return n->toString(); }
268};
269
270template <>
271struct DOTGraphTraits<const llzk::SymbolUseGraph *>
272 : public DOTGraphTraits<const llzk::SymbolUseGraphNode *> {
273
274 DOTGraphTraits(bool isSimple = false) : DOTGraphTraits<NodeRef>(isSimple) {}
275
276 static std::string getGraphName(GraphType) { return "Symbol Use Graph"; }
277
278 std::string getNodeLabel(NodeRef n, GraphType g) {
280 }
281};
282
283} // 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
std::string toString() const
Print the node in a human readable format.
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.
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.
iterator successors_end() const
mlir::ModuleOp getSymbolPathRoot() const
Return the root ModuleOp for the path.
void print(llvm::raw_ostream &os) const
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)