CIRCT  20.0.0git
MaterializeFSM.cpp
Go to the documentation of this file.
1 //===- MaterializeCalyxToFSM.cpp - FSM Materialization Pass -----*- 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 // Contains the definitions of the FSM materialization pass.
10 //
11 //===----------------------------------------------------------------------===//
12 
16 #include "circt/Support/LLVM.h"
17 #include "mlir/IR/BuiltinTypes.h"
18 #include "mlir/IR/OperationSupport.h"
19 #include "mlir/Pass/Pass.h"
20 #include "llvm/ADT/STLExtras.h"
21 
22 namespace circt {
23 #define GEN_PASS_DEF_MATERIALIZECALYXTOFSM
24 #include "circt/Conversion/Passes.h.inc"
25 } // namespace circt
26 
27 using namespace circt;
28 using namespace calyx;
29 using namespace mlir;
30 using namespace fsm;
31 
32 namespace {
33 
34 struct MaterializeCalyxToFSMPass
35  : public circt::impl::MaterializeCalyxToFSMBase<MaterializeCalyxToFSMPass> {
36  void runOnOperation() override;
37 
38  /// Assigns the 'fsm.output' operation of the provided 'state' to enabled the
39  /// set of provided groups. If 'topLevelDone' is set, also asserts the
40  /// top-level done signal.
41  void assignStateOutputOperands(OpBuilder &b, StateOp stateOp,
42  bool topLevelDone = false) {
43  SmallVector<Value> outputOperands;
44  auto &enabledGroups = stateEnables[stateOp];
45  for (StringAttr group : referencedGroups)
46  outputOperands.push_back(
47  getOrCreateConstant(b, APInt(1, enabledGroups.contains(group))));
48 
49  assert(outputOperands.size() == machineOp.getNumArguments() - 1 &&
50  "Expected exactly one value for each uniquely referenced group in "
51  "this machine");
52  outputOperands.push_back(getOrCreateConstant(b, APInt(1, topLevelDone)));
53  stateOp.ensureOutput(b);
54  auto outputOp = stateOp.getOutputOp();
55  outputOp->setOperands(outputOperands);
56  }
57 
58  /// Extends every `fsm.return` guard in the transitions of this state to also
59  /// include the provided set of 'doneGuards'. 'doneGuards' is passed by value
60  /// to allow the caller to provide additional done guards apart from group
61  /// enable-generated guards.
62  void assignStateTransitionGuard(OpBuilder &b, StateOp stateOp,
63  SmallVector<Value> doneGuards = {}) {
64  auto &enabledGroups = stateEnables[stateOp];
65  for (auto groupIt : llvm::enumerate(referencedGroups))
66  if (enabledGroups.contains(groupIt.value()))
67  doneGuards.push_back(machineOp.getArgument(groupIt.index()));
68 
69  for (auto transition :
70  stateOp.getTransitions().getOps<fsm::TransitionOp>()) {
71 
72  if (!transition.hasGuard() && doneGuards.empty())
73  continue;
74  transition.ensureGuard(b);
75  auto guardOp = transition.getGuardReturn();
76  llvm::SmallVector<Value> guards;
77  llvm::append_range(guards, doneGuards);
78  if (guardOp.getNumOperands() != 0)
79  guards.push_back(guardOp.getOperand());
80 
81  if (guards.empty())
82  continue;
83 
84  b.setInsertionPoint(guardOp);
85  Value guardConjunction;
86  if (guards.size() == 1)
87  guardConjunction = guards.front();
88  else
89  guardConjunction =
90  b.create<comb::AndOp>(transition.getLoc(), guards, false);
91  guardOp.setOperand(guardConjunction);
92  }
93  }
94 
95  Value getOrCreateConstant(OpBuilder &b, APInt value) {
96  auto it = constants.find(value);
97  if (it != constants.end())
98  return it->second;
99 
100  OpBuilder::InsertionGuard g(b);
101  b.setInsertionPointToStart(&machineOp.getBody().front());
102  auto constantOp = b.create<hw::ConstantOp>(machineOp.getLoc(), value);
103  constants[value] = constantOp;
104  return constantOp;
105  }
106 
107  /// Maintain a set of all groups referenced within this fsm.machine.
108  /// Use a SetVector to ensure a deterministic ordering - strong assumptions
109  /// are placed on the order of referenced groups wrt. the top-level I/O
110  /// created for the group done/go signals.
111  SetVector<StringAttr> referencedGroups;
112 
113  /// Maintain a relation between states and the groups which they enable.
114  DenseMap<fsm::StateOp, DenseSet<StringAttr>> stateEnables;
115 
116  /// A handle to the machine under transformation.
117  MachineOp machineOp;
118 
119  /// Constant cache.
120  DenseMap<APInt, Value> constants;
121 
122  OpBuilder *b;
123 
124  FSMStateNode *entryState;
125  FSMStateNode *exitState;
126 
127  // Walks the machine and gathers the set of referenced groups and SSA values.
128  void walkMachine();
129 
130  // Creates the top-level group go/done I/O for the machine.
131  void materializeGroupIO();
132 
133  // Add attributes to the machine op to indicate which in/out ports are
134  // associated with group activations and which are additional inputs to the
135  // FSM.
136  void assignAttributes();
137 };
138 
139 } // end anonymous namespace
140 
141 void MaterializeCalyxToFSMPass::walkMachine() {
142  // Walk the states of the machine and gather the relation between states and
143  // the groups which they enable as well as the set of all enabled states.
144  for (auto stateOp : machineOp.getOps<fsm::StateOp>()) {
145  for (auto enableOp : llvm::make_early_inc_range(
146  stateOp.getOutput().getOps<calyx::EnableOp>())) {
147  auto groupName = enableOp.getGroupNameAttr().getAttr();
148  stateEnables[stateOp].insert(groupName);
149  referencedGroups.insert(groupName);
150  // Erase the enable op now that we've recorded the information.
151  enableOp.erase();
152  }
153  }
154 }
155 
156 void MaterializeCalyxToFSMPass::materializeGroupIO() {
157  // Materialize the top-level I/O ports of the fsm.machine. We add an in- and
158  // output for every unique group referenced within the machine, as well as an
159  // additional in- and output to represent the top-level "go" input and "done"
160  // output ports.
161  SmallVector<Type> ioTypes = SmallVector<Type>(
162  referencedGroups.size() + /*top-level go/done*/ 1, b->getI1Type());
163  size_t nGroups = ioTypes.size() - 1;
164  machineOp.setType(b->getFunctionType(ioTypes, ioTypes));
165  assert(machineOp.getBody().getNumArguments() == 0 &&
166  "expected no inputs to the FSM");
167  machineOp.getBody().addArguments(
168  ioTypes, SmallVector<Location, 4>(ioTypes.size(), b->getUnknownLoc()));
169 
170  // Build output assignments and transition guards in every state. We here
171  // assume that the ordering of states in referencedGroups is fixed and
172  // deterministic, since it is used as an analogue for port I/O ordering.
173  for (auto stateOp : machineOp.getOps<fsm::StateOp>()) {
174  assignStateOutputOperands(*b, stateOp,
175  /*topLevelDone=*/false);
176  assignStateTransitionGuard(*b, stateOp);
177  }
178 
179  // Assign top-level go guard in the transition state.
180  size_t topLevelGoIdx = nGroups;
181  assignStateTransitionGuard(*b, entryState->getState(),
182  {machineOp.getArgument(topLevelGoIdx)});
183 
184  // Assign top-level done in the exit state.
185  assignStateOutputOperands(*b, exitState->getState(),
186  /*topLevelDone=*/true);
187 }
188 
189 void MaterializeCalyxToFSMPass::assignAttributes() {
190  // sGroupDoneInputs is a mapping from group name to the index of the
191  // corresponding done input port.
192  llvm::SmallVector<NamedAttribute> groupDoneInputs;
193  for (size_t i = 0; i < referencedGroups.size(); ++i)
194  groupDoneInputs.push_back({referencedGroups[i], b->getI64IntegerAttr(i)});
195  machineOp->setAttr(calyxToFSM::sGroupDoneInputs,
196  b->getDictionaryAttr(groupDoneInputs));
197 
198  // sGroupGoOutputs is a mapping from group name to the index of the
199  // corresponding go output port.
200  llvm::SmallVector<NamedAttribute> groupGoOutputs;
201  for (size_t i = 0; i < referencedGroups.size(); ++i)
202  groupGoOutputs.push_back({referencedGroups[i], b->getI64IntegerAttr(i)});
203  machineOp->setAttr(calyxToFSM::sGroupGoOutputs,
204  b->getDictionaryAttr(groupGoOutputs));
205 
206  // Assign top level go/done attributes
207  machineOp->setAttr(calyxToFSM::sFSMTopLevelGoIndex,
208  b->getI64IntegerAttr(referencedGroups.size()));
209  machineOp->setAttr(calyxToFSM::sFSMTopLevelDoneIndex,
210  b->getI64IntegerAttr(referencedGroups.size()));
211 }
212 
213 void MaterializeCalyxToFSMPass::runOnOperation() {
214  ComponentOp component = getOperation();
215  auto *ctx = &getContext();
216  auto builder = OpBuilder(ctx);
217  b = &builder;
218  auto controlOp = component.getControlOp();
219  machineOp =
220  dyn_cast_or_null<fsm::MachineOp>(controlOp.getBodyBlock()->front());
221  if (!machineOp) {
222  controlOp.emitOpError()
223  << "expected an 'fsm.machine' operation as the top-level operation "
224  "within the control region of this component.";
225  signalPassFailure();
226  return;
227  }
228 
229  // Ensure a well-formed FSM.
230  auto graph = FSMGraph(machineOp);
231  entryState = graph.lookup(b->getStringAttr(calyxToFSM::sEntryStateName));
232  exitState = graph.lookup(b->getStringAttr(calyxToFSM::sExitStateName));
233 
234  if (!(entryState && exitState)) {
235  machineOp.emitOpError()
236  << "Expected an '" << calyxToFSM::sEntryStateName << "' and '"
237  << calyxToFSM::sExitStateName << "' state to be present in the FSM.";
238  signalPassFailure();
239  return;
240  }
241 
242  walkMachine();
243  materializeGroupIO();
244  assignAttributes();
245 }
246 
247 std::unique_ptr<mlir::Pass> circt::createMaterializeCalyxToFSMPass() {
248  return std::make_unique<MaterializeCalyxToFSMPass>();
249 }
assert(baseType &&"element must be base type")
static constexpr std::string_view sGroupDoneInputs
Definition: CalyxToFSM.h:37
static constexpr std::string_view sFSMTopLevelGoIndex
Definition: CalyxToFSM.h:42
static constexpr std::string_view sExitStateName
Definition: CalyxToFSM.h:36
static constexpr std::string_view sFSMTopLevelDoneIndex
Definition: CalyxToFSM.h:44
static constexpr std::string_view sEntryStateName
Definition: CalyxToFSM.h:35
static constexpr std::string_view sGroupGoOutputs
Definition: CalyxToFSM.h:39
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
std::unique_ptr< mlir::Pass > createMaterializeCalyxToFSMPass()
Definition: fsm.py:1