CIRCT  20.0.0git
FSMGraph.h
Go to the documentation of this file.
1 //===- FSMGraph.h - FSM 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 the FSMGraph.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef CIRCT_DIALECT_FSM_FSMGRAPH_H
14 #define CIRCT_DIALECT_FSM_FSMGRAPH_H
15 
17 #include "circt/Support/LLVM.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 #include "llvm/Support/GraphWriter.h"
23 
24 #include <regex>
25 
26 namespace circt {
27 namespace fsm {
28 
29 namespace detail {
30 /// This just maps a iterator of references to an iterator of addresses.
31 template <typename It>
32 struct AddressIterator
33  : public llvm::mapped_iterator<It, typename It::pointer (*)(
34  typename It::reference)> {
35  // This using statement is to get around a bug in MSVC. Without it, it
36  // tries to look up "It" as a member type of the parent class.
37  using Iterator = It;
38  static typename Iterator::value_type *
39  addrOf(typename Iterator::value_type &v) noexcept {
40  return std::addressof(v);
41  }
42  /* implicit */ AddressIterator(Iterator iterator)
43  : llvm::mapped_iterator<It, typename Iterator::pointer (*)(
44  typename Iterator::reference)>(iterator,
45  addrOf) {}
46 };
47 
48 // Escapes any occurance of (regex) characters 'c' in 'str'. If 'noEscape' is
49 // set, does not prepend a '\' character to the replaced value.
50 static void escape(std::string &str, const char *c, bool noEscape = false) {
51  std::string replacement = std::string(c);
52  if (!noEscape)
53  replacement = R"(\)" + replacement;
54  str = std::regex_replace(str, std::regex(c), replacement);
55 }
56 
57 // Dumps a range of operations to a string in a format suitable for embedding
58 // inside a .dot edge/node label.
59 template <typename TOpRange>
60 static std::string dotSafeDumpOps(TOpRange ops) {
61  std::string dump;
62  llvm::raw_string_ostream ss(dump);
63  llvm::interleave(
64  ops, ss, [&](mlir::Operation &op) { op.print(ss); }, "\\n");
65 
66  // Ensure that special characters which may be present in the Op dumps are
67  // properly escaped.
68  escape(dump, R"(")");
69  escape(dump, R"(\{)", /*noEscape=*/true);
70  escape(dump, R"(\})", /*noEscape=*/true);
71  return dump;
72 }
73 
74 // Dumps a range of operations to a string.
75 template <typename TOpRange>
76 static std::string dumpOps(TOpRange ops) {
77  std::string dump;
78  llvm::raw_string_ostream ss(dump);
79  llvm::interleave(
80  ops, ss, [&](mlir::Operation &op) { op.print(ss); }, "\n");
81 
82  return dump;
83 }
84 
85 } // namespace detail
86 
87 class FSMStateNode;
88 
89 /// This is an edge in the FSMGraph. This tracks a transition between two
90 /// states.
91 class FSMTransitionEdge
92  : public llvm::ilist_node_with_parent<FSMTransitionEdge, FSMStateNode> {
93 public:
94  /// Get the state where the transition originates from.
95  FSMStateNode *getCurrentState() const { return currentState; }
96 
97  /// Get the module which the FSM-like is instantiating.
98  FSMStateNode *getNextState() const { return nextState; }
99 
100  TransitionOp getTransition() const { return transition; }
101 
102  /// Erase this transition, removing it from the source state and the target
103  /// state's use-list.
104  void erase();
105 
106 private:
107  friend class FSMGraphBase;
108  friend class FSMStateNode;
109 
110  FSMTransitionEdge(FSMStateNode *currentState, TransitionOp transition,
111  FSMStateNode *nextState)
112  : currentState(currentState), transition(transition),
113  nextState(nextState) {}
114  FSMTransitionEdge(const FSMTransitionEdge &) = delete;
115 
116  /// The state where this transition originates from.
117  FSMStateNode *currentState;
118 
119  /// The transition that this is tracking.
120  TransitionOp transition;
121 
122  /// The next state of this transition.
123  FSMStateNode *nextState;
124 
125  /// Intrusive linked list for other uses.
126  FSMTransitionEdge *nextUse = nullptr;
127  FSMTransitionEdge *prevUse = nullptr;
128 };
129 
130 /// This is a Node in the FSMGraph. Each node represents a state in the FSM.
131 class FSMStateNode : public llvm::ilist_node<FSMStateNode> {
132  using FSMTransitionList = llvm::iplist<FSMTransitionEdge>;
133 
134 public:
135  FSMStateNode() : state(nullptr) {}
136 
137  /// Get the state operation that this node is tracking.
138  StateOp getState() const { return state; }
139 
140  /// Adds a new transition edge from this state to 'nextState'.
141  FSMTransitionEdge *addTransitionEdge(FSMStateNode *nextState,
142  TransitionOp transition);
143 
144  /// Erases a transition edge from this state. This also removes the underlying
145  /// TransitionOp.
146  void eraseTransitionEdge(FSMTransitionEdge *edge);
147 
148  /// Iterate outgoing FSM transitions of this state.
149  using iterator = detail::AddressIterator<FSMTransitionList::iterator>;
150  iterator begin() { return transitions.begin(); }
151  iterator end() { return transitions.end(); }
152 
153  /// Iterator for state uses.
154  struct UseIterator
155  : public llvm::iterator_facade_base<
156  UseIterator, std::forward_iterator_tag, FSMTransitionEdge *> {
157  UseIterator() : current(nullptr) {}
158  UseIterator(FSMStateNode *node) : current(node->firstUse) {}
159  FSMTransitionEdge *operator*() const { return current; }
160  using llvm::iterator_facade_base<UseIterator, std::forward_iterator_tag,
161  FSMTransitionEdge *>::operator++;
162  UseIterator &operator++() {
163  assert(current && "incrementing past end");
164  current = current->nextUse;
165  return *this;
166  }
167  bool operator==(const UseIterator &other) const {
168  return current == other.current;
169  }
170 
171  private:
172  FSMTransitionEdge *current;
173  };
174 
175  /// Iterate the instance records which instantiate this module.
176  UseIterator usesBegin() { return {this}; }
177  UseIterator usesEnd() { return {}; }
178  llvm::iterator_range<UseIterator> uses() {
179  return llvm::make_range(usesBegin(), usesEnd());
180  }
181 
182 private:
183  friend class FSMTransitionEdge;
184 
185  FSMStateNode(StateOp state) : state(state) {}
186  FSMStateNode(const FSMStateNode &) = delete;
187 
188  /// Record that a tramsition referenced this state.
189  void recordUse(FSMTransitionEdge *transition);
190 
191  /// The state.
192  StateOp state;
193 
194  /// List of outgoing transitions from this state.
195  FSMTransitionList transitions;
196 
197  /// List of transitions which reference this state.
198  FSMTransitionEdge *firstUse = nullptr;
199 
200  /// Provide access to the constructor.
201  friend class FSMGraph;
202 };
203 
204 /// Graph representing FSM machines, their states and transitions between these.
205 class FSMGraph {
206  /// This is the list of FSMStateNodes in the graph.
207  using NodeList = llvm::iplist<FSMStateNode>;
208 
209 public:
210  /// Create a new graph of an FSM machine operation.
211  explicit FSMGraph(Operation *operation);
212 
213  /// Look up a FSMStateNode for a state.
214  FSMStateNode *lookup(StateOp op);
215 
216  /// Lookup a FSMStateNode by name.
217  FSMStateNode *lookup(StringAttr name);
218 
219  /// Lookup a FSMStateNode for a state.
220  FSMStateNode *operator[](StateOp op) { return lookup(op); }
221 
222  /// Get the node corresponding to the entry state of the FSM.
223  FSMStateNode *getEntryNode();
224 
225  /// Return the FSM machine operation which this graph tracks.
226  MachineOp getMachine() const { return machine; }
227 
228  /// Retrieves the state node for a 'state'. If the node does not yet exists, a
229  /// new state node is created.
230  FSMStateNode *getOrAddState(StateOp state);
231 
232  /// Creates a new StateOp operation in this machine and updates the graph.
233  FSMStateNode *createState(OpBuilder &builder, Location loc, StringRef name);
234 
235  /// Creates a new transition operation between two states and updates the
236  /// graph.
237  FSMTransitionEdge *createTransition(OpBuilder &builder, Location loc,
238  StateOp from, StateOp to);
239 
240  /// Removes this state from the graph. This will also remove the state in the
241  /// underlying machine and all transitions to the state.
242  void eraseState(StateOp state);
243 
244  /// Renames the 'state' to the provided 'name', and updates all referencing
245  /// transitions.
246  void renameState(StateOp state, StringRef name);
247 
248  /// Iterate through all states.
249  using iterator = detail::AddressIterator<NodeList::iterator>;
250  iterator begin() { return nodes.begin(); }
251  iterator end() { return nodes.end(); }
252 
253 private:
254  FSMGraph(const FSMGraph &) = delete;
255 
256  /// The node under which all states are nested.
257  MachineOp machine;
258 
259  /// The storage for graph nodes, with deterministic iteration.
260  NodeList nodes;
261 
262  /// This maps each StateOp to its graph node.
263  llvm::DenseMap<Attribute, FSMStateNode *> nodeMap;
264 };
265 
266 } // namespace fsm
267 } // namespace circt
268 
269 // Provide graph traits for iterating the states.
270 template <>
271 struct llvm::GraphTraits<circt::fsm::FSMStateNode *> {
272  using NodeType = circt::fsm::FSMStateNode;
273  using NodeRef = NodeType *;
274 
275  // Helper for getting the next state of a transition edge.
276  static NodeRef getNextState(const circt::fsm::FSMTransitionEdge *transition) {
277  return transition->getNextState();
278  }
279 
280  using ChildIteratorType =
281  llvm::mapped_iterator<circt::fsm::FSMStateNode::iterator,
282  decltype(&getNextState)>;
283 
284  static NodeRef getEntryNode(NodeRef node) { return node; }
285  // NOLINTNEXTLINE(readability-identifier-naming)
286  static ChildIteratorType child_begin(NodeRef node) {
287  return {node->begin(), &getNextState};
288  }
289  // NOLINTNEXTLINE(readability-identifier-naming)
290  static ChildIteratorType child_end(NodeRef node) {
291  return {node->end(), &getNextState};
292  }
293 };
294 
295 // Graph traits for the FSM graph.
296 template <>
297 struct llvm::GraphTraits<circt::fsm::FSMGraph *>
298  : public llvm::GraphTraits<circt::fsm::FSMStateNode *> {
299  using nodes_iterator = circt::fsm::FSMGraph::iterator;
300 
301  static NodeRef getEntryNode(circt::fsm::FSMGraph *graph) {
302  return graph->getEntryNode();
303  }
304  // NOLINTNEXTLINE(readability-identifier-naming)
305  static nodes_iterator nodes_begin(circt::fsm::FSMGraph *graph) {
306  return graph->begin();
307  }
308  // NOLINTNEXTLINE(readability-identifier-naming)
309  static nodes_iterator nodes_end(circt::fsm::FSMGraph *graph) {
310  return graph->end();
311  }
312 };
313 
314 // Graph traits for DOT labelling.
315 template <>
316 struct llvm::DOTGraphTraits<circt::fsm::FSMGraph *>
317  : public llvm::DefaultDOTGraphTraits {
318  using DefaultDOTGraphTraits::DefaultDOTGraphTraits;
319 
320  static std::string getNodeLabel(circt::fsm::FSMStateNode *node,
321  circt::fsm::FSMGraph *) {
322  // The name of the graph node is the state name.
323  return node->getState().getSymName().str();
324  }
325 
326  static std::string getNodeDescription(circt::fsm::FSMStateNode *node,
327  circt::fsm::FSMGraph *) {
328  // The description of the node is the dump of its Output region.
329  return circt::fsm::detail::dumpOps(node->getState().getOutput().getOps());
330  }
331 
332  template <typename Iterator>
333  static std::string getEdgeAttributes(const circt::fsm::FSMStateNode *node,
334  Iterator it, circt::fsm::FSMGraph *) {
335  // Set an edge label that is the dump of the inner contents of the guard of
336  // the transition.
337  circt::fsm::FSMTransitionEdge *edge = *it.getCurrent();
338  circt::fsm::TransitionOp transition = edge->getTransition();
339  if (!transition.hasGuard())
340  return "";
341 
342  std::string attrs = "label=\"";
343  attrs += circt::fsm::detail::dotSafeDumpOps(llvm::make_filter_range(
344  transition.getGuard().getOps(), [](mlir::Operation &op) {
345  // Ignore implicit fsm.return/fsm.output operations with no operands.
346  if (isa<circt::fsm::ReturnOp, circt::fsm::OutputOp>(op))
347  return op.getNumOperands() != 0;
348 
349  return true;
350  }));
351  attrs += "\"";
352  return attrs;
353  }
354 
355  static void addCustomGraphFeatures(const circt::fsm::FSMGraph *graph,
356  GraphWriter<circt::fsm::FSMGraph *> &gw) {
357  // Print a separate node for global variables in the FSM.
358  llvm::raw_ostream &os = gw.getOStream();
359 
360  os << "variables [shape=record,label=\"Variables|";
361  os << circt::fsm::detail::dotSafeDumpOps(llvm::make_filter_range(
362  graph->getMachine().getOps(), [](mlir::Operation &op) {
363  // Filter state ops; these are printed as separate nodes in the graph.
364  return !isa<circt::fsm::StateOp>(&op);
365  }));
366  os << "\"]";
367  }
368 };
369 
370 #endif // CIRCT_DIALECT_FSM_FSMGRAPH_H
assert(baseType &&"element must be base type")
static void dump(DIModule &module, raw_indented_ostream &os)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
FVInt operator*(uint64_t a, const FVInt &b)
Definition: FVInt.h:636
bool operator==(uint64_t a, const FVInt &b)
Definition: FVInt.h:650
Definition: fsm.py:1