CIRCT  19.0.0git
InstanceGraph.h
Go to the documentation of this file.
1 //===- InstanceGraph.h - Instance graph -------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines a generic instance graph for module- and instance-likes.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef CIRCT_SUPPORT_INSTANCEGRAPH_H
14 #define CIRCT_SUPPORT_INSTANCEGRAPH_H
15 
16 #include "circt/Support/LLVM.h"
17 #include "mlir/IR/OpDefinition.h"
18 #include "llvm/ADT/GraphTraits.h"
19 #include "llvm/ADT/STLExtras.h"
20 #include "llvm/ADT/iterator.h"
21 #include "llvm/Support/DOTGraphTraits.h"
22 
23 /// The InstanceGraph op interface, see InstanceGraphInterface.td for more
24 /// details.
26 
27 namespace circt {
28 namespace igraph {
29 
30 namespace detail {
31 /// This just maps a iterator of references to an iterator of addresses.
32 template <typename It>
34  : public llvm::mapped_iterator<It, typename It::pointer (*)(
35  typename It::reference)> {
36  // This using statement is to get around a bug in MSVC. Without it, it
37  // tries to look up "It" as a member type of the parent class.
38  using Iterator = It;
39  static typename Iterator::value_type *
40  addrOf(typename Iterator::value_type &v) noexcept {
41  return std::addressof(v);
42  }
43  /* implicit */ AddressIterator(Iterator iterator)
44  : llvm::mapped_iterator<It, typename Iterator::pointer (*)(
45  typename Iterator::reference)>(iterator,
46  addrOf) {}
47 };
48 } // namespace detail
49 
50 class InstanceGraphNode;
51 
52 /// This is an edge in the InstanceGraph. This tracks a specific instantiation
53 /// of a module.
55  : public llvm::ilist_node_with_parent<InstanceRecord, InstanceGraphNode> {
56 public:
57  /// Get the instance-like op that this is tracking.
58  template <typename TTarget = InstanceOpInterface>
59  auto getInstance() {
60  if constexpr (std::is_same<TTarget, InstanceOpInterface>::value)
61  return instance;
62  return dyn_cast_or_null<TTarget>(instance.getOperation());
63  }
64 
65  /// Get the module where the instantiation lives.
66  InstanceGraphNode *getParent() const { return parent; }
67 
68  /// Get the module which the instance-like is instantiating.
69  InstanceGraphNode *getTarget() const { return target; }
70 
71  /// Erase this instance record, removing it from the parent module and the
72  /// target's use-list.
73  void erase();
74 
75 private:
76  friend class InstanceGraph;
77  friend class InstanceGraphNode;
78 
82  InstanceRecord(const InstanceRecord &) = delete;
83 
84  /// This is the module where the instantiation lives.
86 
87  /// The InstanceLike that this is tracking.
88  InstanceOpInterface instance;
89 
90  /// This is the module which the instance-like is instantiating.
92  /// Intrusive linked list for other uses.
93  InstanceRecord *nextUse = nullptr;
94  InstanceRecord *prevUse = nullptr;
95 };
96 
97 /// This is a Node in the InstanceGraph. Each node represents a Module in a
98 /// Circuit. Both external modules and regular modules can be represented by
99 /// this class. It is possible to efficiently iterate all modules instantiated
100 /// by this module, as well as all instantiations of this module.
101 class InstanceGraphNode : public llvm::ilist_node<InstanceGraphNode> {
102  using InstanceList = llvm::iplist<InstanceRecord>;
103 
104 public:
105  InstanceGraphNode() : module(nullptr) {}
106 
107  /// Get the module that this node is tracking.
108  template <typename TTarget = ModuleOpInterface>
109  auto getModule() {
110  if constexpr (std::is_same<TTarget, ModuleOpInterface>::value)
111  return module;
112  return cast<TTarget>(module.getOperation());
113  }
114 
115  /// Iterate the instance records in this module.
117  iterator begin() { return instances.begin(); }
118  iterator end() { return instances.end(); }
119 
120  /// Return true if there are no more instances of this module.
121  bool noUses() { return !firstUse; }
122 
123  /// Return true if this module has exactly one use.
124  bool hasOneUse() { return llvm::hasSingleElement(uses()); }
125 
126  /// Get the number of direct instantiations of this module.
127  size_t getNumUses() { return std::distance(usesBegin(), usesEnd()); }
128 
129  /// Iterator for module uses.
130  struct UseIterator
131  : public llvm::iterator_facade_base<
132  UseIterator, std::forward_iterator_tag, InstanceRecord *> {
133  UseIterator() : current(nullptr) {}
135  InstanceRecord *operator*() const { return current; }
136  using llvm::iterator_facade_base<UseIterator, std::forward_iterator_tag,
137  InstanceRecord *>::operator++;
139  assert(current && "incrementing past end");
141  return *this;
142  }
143  bool operator==(const UseIterator &other) const {
144  return current == other.current;
145  }
146 
147  private:
149  };
150 
151  /// Iterate the instance records which instantiate this module.
152  UseIterator usesBegin() { return {this}; }
153  UseIterator usesEnd() { return {}; }
154  llvm::iterator_range<UseIterator> uses() {
155  return llvm::make_range(usesBegin(), usesEnd());
156  }
157 
158  /// Record a new instance op in the body of this module. Returns a newly
159  /// allocated InstanceRecord which will be owned by this node.
160  InstanceRecord *addInstance(InstanceOpInterface instance,
161  InstanceGraphNode *target);
162 
163 private:
164  friend class InstanceRecord;
165 
167 
168  /// Record that a module instantiates this module.
169  void recordUse(InstanceRecord *record);
170 
171  /// The module.
172  ModuleOpInterface module;
173 
174  /// List of instance operations in this module. This member owns the
175  /// InstanceRecords, which may be pointed to by other InstanceGraphNode's use
176  /// lists.
178 
179  /// List of instances which instantiate this module.
181 
182  // Provide access to the constructor.
183  friend class InstanceGraph;
184 };
185 
186 /// This graph tracks modules and where they are instantiated. This is intended
187 /// to be used as a cached analysis on circuits. This class can be used
188 /// to walk the modules efficiently in a bottom-up or top-down order.
189 ///
190 /// To use this class, retrieve a cached copy from the analysis manager:
191 /// auto &instanceGraph = getAnalysis<InstanceGraph>(getOperation());
193  /// This is the list of InstanceGraphNodes in the graph.
194  using NodeList = llvm::iplist<InstanceGraphNode>;
195 
196 public:
197  /// Create a new module graph of a circuit. Must be called on the parent
198  /// operation of ModuleOpInterface ops.
199  InstanceGraph(Operation *parent);
200  InstanceGraph(const InstanceGraph &) = delete;
201  virtual ~InstanceGraph() = default;
202 
203  /// Look up an InstanceGraphNode for a module.
204  InstanceGraphNode *lookup(ModuleOpInterface op);
205 
206  /// Lookup an module by name.
207  InstanceGraphNode *lookup(StringAttr name);
208 
209  /// Lookup an InstanceGraphNode for a module.
210  InstanceGraphNode *operator[](ModuleOpInterface op) { return lookup(op); }
211 
212  /// Check if child is instantiated by a parent.
213  bool isAncestor(
214  ModuleOpInterface child, ModuleOpInterface parent,
215  llvm::function_ref<bool(InstanceRecord *)> skipInstance =
216  [](InstanceRecord *_) { return false; });
217 
218  /// Get the node corresponding to the top-level module of a circuit.
219  virtual InstanceGraphNode *getTopLevelNode() { return nullptr; }
220 
221  /// Get the nodes corresponding to the inferred top-level modules of a
222  /// circuit.
224 
225  /// Return the parent under which all nodes are nested.
226  Operation *getParent() { return parent; }
227 
228  /// Returns pointer to member of operation list.
229  static NodeList InstanceGraph::*getSublistAccess(Operation *) {
230  return &InstanceGraph::nodes;
231  }
232 
233  /// Iterate through all modules.
235  iterator begin() { return nodes.begin(); }
236  iterator end() { return nodes.end(); }
237 
238  //===-------------------------------------------------------------------------
239  // Methods to keep an InstanceGraph up to date.
240  //
241  // These methods are not thread safe. Make sure that modifications are
242  // properly synchronized or performed in a serial context. When the
243  // InstanceGraph is used as an analysis, this is only safe when the pass is
244  // on a CircuitOp or a ModuleOp.
245 
246  /// Add a newly created module to the instance graph.
247  virtual InstanceGraphNode *addModule(ModuleOpInterface module);
248 
249  /// Remove this module from the instance graph. This will also remove all
250  /// InstanceRecords in this module. All instances of this module must have
251  /// been removed from the graph.
252  virtual void erase(InstanceGraphNode *node);
253 
254  /// Replaces an instance of a module with another instance. The target module
255  /// of both InstanceOps must be the same.
256  virtual void replaceInstance(InstanceOpInterface inst,
257  InstanceOpInterface newInst);
258 
259 protected:
260  ModuleOpInterface getReferencedModuleImpl(InstanceOpInterface op);
261 
262  /// Get the node corresponding to the module. If the node has does not exist
263  /// yet, it will be created.
264  InstanceGraphNode *getOrAddNode(StringAttr name);
265 
266  /// The node under which all modules are nested.
267  Operation *parent;
268 
269  /// The storage for graph nodes, with deterministic iteration.
271 
272  /// This maps each operation to its graph node.
273  llvm::DenseMap<Attribute, InstanceGraphNode *> nodeMap;
274 
275  /// A caching of the inferred top level module(s).
276  llvm::SmallVector<InstanceGraphNode *> inferredTopLevelNodes;
277 };
278 
279 struct InstancePathCache;
280 
281 /**
282  * An instance path composed of a series of instances.
283  */
284 class InstancePath final {
285 public:
286  InstancePath() = default;
287 
288  InstanceOpInterface top() const {
289  assert(!empty() && "instance path is empty");
290  return path[0];
291  }
292 
293  InstanceOpInterface leaf() const {
294  assert(!empty() && "instance path is empty");
295  return path.back();
296  }
297 
298  InstancePath dropFront() const { return InstancePath(path.drop_front()); }
299 
300  InstancePath dropBack() const { return InstancePath(path.drop_back()); }
301 
302  InstanceOpInterface operator[](size_t idx) const { return path[idx]; }
303  ArrayRef<InstanceOpInterface>::iterator begin() const { return path.begin(); }
304  ArrayRef<InstanceOpInterface>::iterator end() const { return path.end(); }
305  size_t size() const { return path.size(); }
306  bool empty() const { return path.empty(); }
307 
308  bool operator==(const InstancePath &that) const { return path == that.path; }
309 
310  /// Print the path to any stream-like object.
311  void print(llvm::raw_ostream &into) const;
312 
313 private:
314  // Only the path cache is allowed to create paths.
315  friend struct InstancePathCache;
316  InstancePath(ArrayRef<InstanceOpInterface> path) : path(path) {}
317 
318  ArrayRef<InstanceOpInterface> path;
319 };
320 
321 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
322  const InstancePath &path) {
323  path.print(os);
324  return os;
325 }
326 
327 /// A data structure that caches and provides absolute paths to module
328 /// instances in the IR.
330  /// The instance graph of the IR.
332 
335  ArrayRef<InstancePath> getAbsolutePaths(ModuleOpInterface op);
336  ArrayRef<InstancePath> getAbsolutePaths(ModuleOpInterface op,
337  InstanceGraphNode *top);
338 
339  /// Replace an InstanceOp. This is required to keep the cache updated.
340  void replaceInstance(InstanceOpInterface oldOp, InstanceOpInterface newOp);
341 
342  /// Append an instance to a path.
343  InstancePath appendInstance(InstancePath path, InstanceOpInterface inst);
344 
345  /// Prepend an instance to a path.
346  InstancePath prependInstance(InstanceOpInterface inst, InstancePath path);
347 
348 private:
349  /// An allocator for individual instance paths and entire path lists.
350  llvm::BumpPtrAllocator allocator;
351 
352  /// Cached absolute instance paths.
353  DenseMap<Operation *, ArrayRef<InstancePath>> absolutePathsCache;
354 };
355 
356 } // namespace igraph
357 } // namespace circt
358 
359 // Graph traits for modules.
360 template <>
361 struct llvm::GraphTraits<circt::igraph::InstanceGraphNode *> {
363  using NodeRef = NodeType *;
364 
365  // Helper for getting the module referenced by the instance op.
367  return record->getTarget();
368  }
369 
371  llvm::mapped_iterator<NodeType::iterator, decltype(&getChild)>;
372 
373  static NodeRef getEntryNode(NodeRef node) { return node; }
375  return {node->begin(), &getChild};
376  }
378  return {node->end(), &getChild};
379  }
380 };
381 
382 // Provide graph traits for iterating the modules in inverse order.
383 template <>
384 struct llvm::GraphTraits<llvm::Inverse<circt::igraph::InstanceGraphNode *>> {
386  using NodeRef = NodeType *;
387 
388  // Helper for getting the module containing the instance op.
390  return record->getParent();
391  }
392 
394  llvm::mapped_iterator<NodeType::UseIterator, decltype(&getParent)>;
395 
396  static NodeRef getEntryNode(Inverse<NodeRef> inverse) {
397  return inverse.Graph;
398  }
400  return {node->usesBegin(), &getParent};
401  }
403  return {node->usesEnd(), &getParent};
404  }
405 };
406 
407 // Graph traits for the common instance graph.
408 template <>
409 struct llvm::GraphTraits<circt::igraph::InstanceGraph *>
410  : public llvm::GraphTraits<circt::igraph::InstanceGraphNode *> {
412 
414  return graph->getTopLevelNode();
415  }
416  // NOLINTNEXTLINE(readability-identifier-naming)
418  return graph->begin();
419  }
420  // NOLINTNEXTLINE(readability-identifier-naming)
422  return graph->end();
423  }
424 };
425 
426 // Graph traits for DOT labeling.
427 template <>
428 struct llvm::DOTGraphTraits<circt::igraph::InstanceGraph *>
429  : public llvm::DefaultDOTGraphTraits {
430  using DefaultDOTGraphTraits::DefaultDOTGraphTraits;
431 
434  // The name of the graph node is the module name.
435  return node->getModule().getModuleName().str();
436  }
437 
438  template <typename Iterator>
439  static std::string
442  // Set an edge label that is the name of the instance.
443  auto *instanceRecord = *it.getCurrent();
444  auto instanceOp = instanceRecord->getInstance();
445  return ("label=" + instanceOp.getInstanceName()).str();
446  }
447 };
448 
449 #endif // CIRCT_SUPPORT_INSTANCEGRAPH_H
assert(baseType &&"element must be base type")
This is a Node in the InstanceGraph.
InstanceRecord * firstUse
List of instances which instantiate this module.
bool noUses()
Return true if there are no more instances of this module.
InstanceRecord * addInstance(InstanceOpInterface instance, InstanceGraphNode *target)
Record a new instance op in the body of this module.
InstanceGraphNode(const InstanceGraphNode &)=delete
auto getModule()
Get the module that this node is tracking.
InstanceList instances
List of instance operations in this module.
UseIterator usesBegin()
Iterate the instance records which instantiate this module.
llvm::iplist< InstanceRecord > InstanceList
size_t getNumUses()
Get the number of direct instantiations of this module.
ModuleOpInterface module
The module.
bool hasOneUse()
Return true if this module has exactly one use.
llvm::iterator_range< UseIterator > uses()
void recordUse(InstanceRecord *record)
Record that a module instantiates this module.
This graph tracks modules and where they are instantiated.
virtual ~InstanceGraph()=default
virtual InstanceGraphNode * addModule(ModuleOpInterface module)
Add a newly created module to the instance graph.
Operation * parent
The node under which all modules are nested.
llvm::DenseMap< Attribute, InstanceGraphNode * > nodeMap
This maps each operation to its graph node.
virtual void replaceInstance(InstanceOpInterface inst, InstanceOpInterface newInst)
Replaces an instance of a module with another instance.
FailureOr< llvm::ArrayRef< InstanceGraphNode * > > getInferredTopLevelNodes()
Get the nodes corresponding to the inferred top-level modules of a circuit.
llvm::SmallVector< InstanceGraphNode * > inferredTopLevelNodes
A caching of the inferred top level module(s).
NodeList nodes
The storage for graph nodes, with deterministic iteration.
virtual void erase(InstanceGraphNode *node)
Remove this module from the instance graph.
InstanceGraphNode * operator[](ModuleOpInterface op)
Lookup an InstanceGraphNode for a module.
bool isAncestor(ModuleOpInterface child, ModuleOpInterface parent, llvm::function_ref< bool(InstanceRecord *)> skipInstance=[](InstanceRecord *_) { return false;})
Check if child is instantiated by a parent.
InstanceGraphNode * lookup(ModuleOpInterface op)
Look up an InstanceGraphNode for a module.
virtual InstanceGraphNode * getTopLevelNode()
Get the node corresponding to the top-level module of a circuit.
Operation * getParent()
Return the parent under which all nodes are nested.
detail::AddressIterator< NodeList::iterator > iterator
Iterate through all modules.
InstanceGraph(const InstanceGraph &)=delete
InstanceGraphNode * getOrAddNode(StringAttr name)
Get the node corresponding to the module.
llvm::iplist< InstanceGraphNode > NodeList
This is the list of InstanceGraphNodes in the graph.
InstanceGraph(Operation *parent)
Create a new module graph of a circuit.
static NodeList InstanceGraph::* getSublistAccess(Operation *)
Returns pointer to member of operation list.
ModuleOpInterface getReferencedModuleImpl(InstanceOpInterface op)
An instance path composed of a series of instances.
InstanceOpInterface leaf() const
bool operator==(const InstancePath &that) const
InstanceOpInterface operator[](size_t idx) const
ArrayRef< InstanceOpInterface > path
ArrayRef< InstanceOpInterface >::iterator end() const
InstancePath(ArrayRef< InstanceOpInterface > path)
ArrayRef< InstanceOpInterface >::iterator begin() const
void print(llvm::raw_ostream &into) const
Print the path to any stream-like object.
InstancePath dropFront() const
InstancePath dropBack() const
InstanceOpInterface top() const
This is an edge in the InstanceGraph.
Definition: InstanceGraph.h:55
InstanceGraphNode * getTarget() const
Get the module which the instance-like is instantiating.
Definition: InstanceGraph.h:69
auto getInstance()
Get the instance-like op that this is tracking.
Definition: InstanceGraph.h:59
void erase()
Erase this instance record, removing it from the parent module and the target's use-list.
InstanceGraphNode * target
This is the module which the instance-like is instantiating.
Definition: InstanceGraph.h:91
InstanceRecord * nextUse
Intrusive linked list for other uses.
Definition: InstanceGraph.h:93
InstanceOpInterface instance
The InstanceLike that this is tracking.
Definition: InstanceGraph.h:88
InstanceGraphNode * getParent() const
Get the module where the instantiation lives.
Definition: InstanceGraph.h:66
InstanceRecord(InstanceGraphNode *parent, InstanceOpInterface instance, InstanceGraphNode *target)
Definition: InstanceGraph.h:79
InstanceGraphNode * parent
This is the module where the instantiation lives.
Definition: InstanceGraph.h:85
InstanceRecord(const InstanceRecord &)=delete
igraph::InstanceGraphNode InstanceGraphNode
std::map< std::string, std::set< std::string > > InstanceGraph
Iterates over the handshake::FuncOp's in the program to build an instance graph.
llvm::raw_ostream & operator<<(llvm::raw_ostream &os, const InstancePath &path)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
bool operator==(const UseIterator &other) const
A data structure that caches and provides absolute paths to module instances in the IR.
ArrayRef< InstancePath > getAbsolutePaths(ModuleOpInterface op)
void replaceInstance(InstanceOpInterface oldOp, InstanceOpInterface newOp)
Replace an InstanceOp. This is required to keep the cache updated.
DenseMap< Operation *, ArrayRef< InstancePath > > absolutePathsCache
Cached absolute instance paths.
InstancePath prependInstance(InstanceOpInterface inst, InstancePath path)
Prepend an instance to a path.
InstancePath appendInstance(InstancePath path, InstanceOpInterface inst)
Append an instance to a path.
llvm::BumpPtrAllocator allocator
An allocator for individual instance paths and entire path lists.
InstanceGraph & instanceGraph
The instance graph of the IR.
InstancePathCache(InstanceGraph &instanceGraph)
This just maps a iterator of references to an iterator of addresses.
Definition: InstanceGraph.h:35
static Iterator::value_type * addrOf(typename Iterator::value_type &v) noexcept
Definition: InstanceGraph.h:40
static std::string getEdgeAttributes(const circt::igraph::InstanceGraphNode *node, Iterator it, circt::igraph::InstanceGraph *)
static std::string getNodeLabel(circt::igraph::InstanceGraphNode *node, circt::igraph::InstanceGraph *)
static ChildIteratorType child_begin(NodeRef node)
static NodeRef getChild(const circt::igraph::InstanceRecord *record)
llvm::mapped_iterator< NodeType::iterator, decltype(&getChild)> ChildIteratorType
static ChildIteratorType child_end(NodeRef node)
static nodes_iterator nodes_begin(circt::igraph::InstanceGraph *graph)
static NodeRef getEntryNode(circt::igraph::InstanceGraph *graph)
static nodes_iterator nodes_end(circt::igraph::InstanceGraph *graph)
llvm::mapped_iterator< NodeType::UseIterator, decltype(&getParent)> ChildIteratorType
static NodeRef getParent(const circt::igraph::InstanceRecord *record)