CIRCT 20.0.0git
Loading...
Searching...
No Matches
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
26namespace circt {
27namespace fsm {
28
29namespace detail {
30/// This just maps a iterator of references to an iterator of addresses.
31template <typename It>
32struct 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.
50static 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.
59template <typename TOpRange>
60static 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.
75template <typename TOpRange>
76static 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
87class FSMStateNode;
88
89/// This is an edge in the FSMGraph. This tracks a transition between two
90/// states.
91class FSMTransitionEdge
92 : public llvm::ilist_node_with_parent<FSMTransitionEdge, FSMStateNode> {
93public:
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
106private:
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.
131class FSMStateNode : public llvm::ilist_node<FSMStateNode> {
132 using FSMTransitionList = llvm::iplist<FSMTransitionEdge>;
133
134public:
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
182private:
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.
205class FSMGraph {
206 /// This is the list of FSMStateNodes in the graph.
207 using NodeList = llvm::iplist<FSMStateNode>;
208
209public:
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
253private:
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.
270template <>
271struct 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.
296template <>
297struct 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.
315template <>
316struct 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)
static bool operator==(const ModuleInfo &lhs, const ModuleInfo &rhs)
Definition Dedup.cpp:101
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
FVInt operator*(uint64_t a, const FVInt &b)
Definition FVInt.h:636
Definition fsm.py:1