10#include "mlir/Dialect/Arith/IR/Arith.h"
11#include "mlir/Dialect/Func/IR/FuncOps.h"
12#include "mlir/IR/BuiltinOps.h"
13#include "mlir/IR/DialectImplementation.h"
14#include "mlir/IR/PatternMatch.h"
15#include "mlir/Interfaces/FunctionImplementation.h"
16#include "llvm/Support/FormatVariadic.h"
26void MachineOp::build(OpBuilder &builder, OperationState &state, StringRef name,
27 StringRef initialStateName, FunctionType type,
28 ArrayRef<NamedAttribute>
attrs,
29 ArrayRef<DictionaryAttr> argAttrs) {
30 state.addAttribute(MachineOp::getSymNameAttrName(state.name),
31 builder.getStringAttr(name));
32 state.addAttribute(MachineOp::getFunctionTypeAttrName(state.name),
34 state.addAttribute(
"initialState",
35 StringAttr::get(state.getContext(), initialStateName));
36 state.attributes.append(
attrs.begin(),
attrs.end());
37 Region *region = state.addRegion();
39 region->push_back(
body);
42 SmallVector<Location, 4>(
type.getNumInputs(), builder.getUnknownLoc()));
46 assert(
type.getNumInputs() == argAttrs.size());
47 call_interface_impl::addArgAndResultAttrs(
48 builder, state, argAttrs,
49 {}, MachineOp::getArgAttrsAttrName(state.name),
50 MachineOp::getResAttrsAttrName(state.name));
54StateOp MachineOp::getInitialStateOp() {
55 return dyn_cast_or_null<StateOp>(lookupSymbol(getInitialState()));
58size_t MachineOp::getNumStates() {
59 auto stateOps = getBody().getOps<StateOp>();
60 return std::distance(stateOps.begin(), stateOps.end());
63StringAttr MachineOp::getArgName(
size_t i) {
64 if (
auto args = getArgNames())
65 return cast<StringAttr>((*args)[i]);
67 return StringAttr::get(getContext(),
"in" + std::to_string(i));
70StringAttr MachineOp::getResName(
size_t i) {
71 if (
auto resNameAttrs = getResNames())
72 return cast<StringAttr>((*resNameAttrs)[i]);
74 return StringAttr::get(getContext(),
"out" + std::to_string(i));
78void MachineOp::getHWPortInfo(SmallVectorImpl<hw::PortInfo> &ports) {
80 auto machineType = getFunctionType();
81 for (
unsigned i = 0, e = machineType.getNumInputs(); i < e; ++i) {
85 port.
name = StringAttr::get(getContext(),
"in" + std::to_string(i));
87 port.
type = machineType.getInput(i);
89 ports.push_back(port);
92 for (
unsigned i = 0, e = machineType.getNumResults(); i < e; ++i) {
96 port.
name = StringAttr::get(getContext(),
"out" + std::to_string(i));
98 port.
type = machineType.getResult(i);
100 ports.push_back(port);
104ParseResult MachineOp::parse(OpAsmParser &parser, OperationState &result) {
106 [&](Builder &builder, ArrayRef<Type> argTypes, ArrayRef<Type> results,
107 function_interface_impl::VariadicFlag,
108 std::string &) {
return builder.getFunctionType(argTypes, results); };
110 return function_interface_impl::parseFunctionOp(
111 parser, result,
false,
112 MachineOp::getFunctionTypeAttrName(result.name), buildFuncType,
113 MachineOp::getArgAttrsAttrName(result.name),
114 MachineOp::getResAttrsAttrName(result.name));
117void MachineOp::print(OpAsmPrinter &p) {
118 function_interface_impl::printFunctionOp(
119 p, *
this,
false, getFunctionTypeAttrName(),
120 getArgAttrsAttrName(), getResAttrsAttrName());
125 if (rangeA.size() != rangeB.size())
126 return emitError(loc) <<
"mismatch in number of types compared ("
127 << rangeA.size() <<
" != " << rangeB.size() <<
")";
130 for (
auto zip : llvm::zip(rangeA, rangeB)) {
131 auto typeA = std::get<0>(zip);
132 auto typeB = std::get<1>(zip);
134 return emitError(loc) <<
"type mismatch at index " << index <<
" ("
135 << typeA <<
" != " << typeB <<
")";
142LogicalResult MachineOp::verify() {
151 front().getArgumentTypes())))
153 "entry block argument types must match the machine input types");
156 if (!llvm::hasSingleElement(*
this))
157 return emitOpError(
"must only have a single block");
160 if (!getInitialStateOp())
161 return emitOpError(
"initial state '" + getInitialState() +
162 "' was not defined in the machine");
164 if (getArgNames() && getArgNames()->size() != getArgumentTypes().size())
165 return emitOpError() <<
"number of machine arguments ("
166 << getArgumentTypes().size()
168 "not match the provided number "
169 "of argument names ("
170 << getArgNames()->size() <<
")";
172 if (getResNames() && getResNames()->size() != getResultTypes().size())
173 return emitOpError() <<
"number of machine results ("
174 << getResultTypes().size()
176 "not match the provided number "
178 << getResNames()->size() <<
")";
183SmallVector<hw::PortInfo> MachineOp::getPortList() {
184 SmallVector<hw::PortInfo> ports;
185 auto fnType = getFunctionType();
186 auto addPorts = [&](TypeRange types, std::optional<ArrayAttr> names,
188 for (
auto [i,
type] : llvm::enumerate(types)) {
189 StringAttr name = (names && i < names->size())
190 ? cast<StringAttr>((*names)[i])
191 : StringAttr::
get(getContext(), prefix + Twine(i));
192 ports.push_back({{name,
type, dir},
static_cast<size_t>(i), {}, {}});
196 addPorts(fnType.getInputs(), getArgNames(), hw::ModulePort::Direction::Input,
198 addPorts(fnType.getResults(), getResNames(),
199 hw::ModulePort::Direction::Output,
"output");
210 auto module = (*this)->getParentOfType<ModuleOp>();
211 return module.lookupSymbol<MachineOp>(getMachine());
214LogicalResult InstanceOp::verify() {
215 auto m = getMachineOp();
217 return emitError(
"cannot find machine definition '") << getMachine() <<
"'";
222void InstanceOp::getAsmResultNames(
223 function_ref<
void(Value, StringRef)> setNameFn) {
224 setNameFn(getInstance(),
getName());
231template <
typename OpType>
233 auto machine = op.getMachineOp();
235 return op.emitError(
"cannot find machine definition");
238 if (failed(
compareTypes(op.getLoc(), machine.getArgumentTypes(),
239 op.getInputs().getTypes()))) {
241 op.emitOpError(
"operand types must match the machine input types");
242 diag.attachNote(machine->getLoc()) <<
"original machine declared here";
247 if (failed(
compareTypes(op.getLoc(), machine.getResultTypes(),
248 op.getOutputs().getTypes()))) {
250 op.emitOpError(
"result types must match the machine output types");
251 diag.attachNote(machine->getLoc()) <<
"original machine declared here";
260 auto instanceOp = getInstance().getDefiningOp<InstanceOp>();
264 return instanceOp.getMachineOp();
277 auto module = (*this)->getParentOfType<ModuleOp>();
278 return module.lookupSymbol<MachineOp>(getMachine());
283SmallVector<hw::PortInfo> HWInstanceOp::getPortList() {
284 return getMachineOp().getPortList();
288StringRef HWInstanceOp::getModuleName() {
return getMachine(); }
289FlatSymbolRefAttr HWInstanceOp::getModuleNameAttr() {
return getMachineAttr(); }
291mlir::StringAttr HWInstanceOp::getInstanceNameAttr() {
return getNameAttr(); }
293llvm::StringRef HWInstanceOp::getInstanceName() {
return getName(); }
299void StateOp::build(OpBuilder &builder, OperationState &state,
300 StringRef stateName) {
301 OpBuilder::InsertionGuard guard(builder);
302 Region *
output = state.addRegion();
304 builder.setInsertionPointToEnd(&
output->back());
308 state.addAttribute(StateOp::getSymNameAttrName(state.name),
309 builder.getStringAttr(stateName));
312void StateOp::build(OpBuilder &builder, OperationState &state,
313 StringRef stateName, ValueRange outputs) {
314 OpBuilder::InsertionGuard guard(builder);
315 Region *
output = state.addRegion();
317 builder.setInsertionPointToEnd(&
output->back());
321 state.addAttribute(StateOp::getSymNameAttrName(state.name),
322 builder.getStringAttr(stateName));
325SetVector<StateOp> StateOp::getNextStates() {
326 SmallVector<StateOp> nextStates;
328 getTransitions().getOps<TransitionOp>(),
329 std::inserter(nextStates, nextStates.begin()),
330 [](TransitionOp transition) { return transition.getNextStateOp(); });
331 return SetVector<StateOp>(nextStates.begin(), nextStates.end());
334LogicalResult StateOp::canonicalize(
StateOp op, PatternRewriter &rewriter) {
335 bool hasAlwaysTakenTransition =
false;
336 SmallVector<TransitionOp, 4> transitionsToErase;
338 for (
auto transition : op.getTransitions().getOps<TransitionOp>()) {
339 if (!hasAlwaysTakenTransition)
340 hasAlwaysTakenTransition = transition.isAlwaysTaken();
342 transitionsToErase.push_back(transition);
345 for (
auto transition : transitionsToErase)
346 rewriter.eraseOp(transition);
348 return failure(transitionsToErase.empty());
351LogicalResult StateOp::verify() {
352 MachineOp parent = getOperation()->getParentOfType<MachineOp>();
354 if (parent.getNumResults() != 0 && (getOutput().
empty()))
355 return emitOpError(
"state must have a non-empty output region when the "
356 "machine has results.");
358 if (!getOutput().
empty()) {
360 Block *outputBlock = &getOutput().front();
361 if (outputBlock->empty() || !isa<fsm::OutputOp>(outputBlock->back()))
362 return emitOpError(
"output block must have a single OutputOp terminator");
368Block *StateOp::ensureOutput(OpBuilder &builder) {
369 if (getOutput().
empty()) {
370 OpBuilder::InsertionGuard g(builder);
371 auto *block =
new Block();
372 getOutput().push_back(block);
373 builder.setInsertionPointToStart(block);
376 return &getOutput().front();
383LogicalResult OutputOp::verify() {
384 if ((*this)->getParentRegion() ==
385 &(*this)->getParentOfType<StateOp>().getTransitions()) {
386 if (getNumOperands() != 0)
387 emitOpError(
"transitions region must not output any value");
393 auto machine = (*this)->getParentOfType<MachineOp>();
396 return emitOpError(
"operand types must match the machine output types");
405void TransitionOp::build(OpBuilder &builder, OperationState &state,
407 build(builder, state, nextState.getName());
410void TransitionOp::build(OpBuilder &builder, OperationState &state,
412 llvm::function_ref<
void()> guardCtor,
413 llvm::function_ref<
void()> actionCtor) {
414 state.addAttribute(
"nextState",
415 FlatSymbolRefAttr::get(builder.getStringAttr(nextState)));
416 OpBuilder::InsertionGuard guard(builder);
418 Region *guardRegion = state.addRegion();
420 builder.createBlock(guardRegion);
424 Region *actionRegion = state.addRegion();
426 builder.createBlock(actionRegion);
431Block *TransitionOp::ensureGuard(OpBuilder &builder) {
432 if (getGuard().
empty()) {
433 OpBuilder::InsertionGuard g(builder);
434 auto *block =
new Block();
435 getGuard().push_back(block);
436 builder.setInsertionPointToStart(block);
437 fsm::ReturnOp::create(builder,
getLoc());
439 return &getGuard().front();
442Block *TransitionOp::ensureAction(OpBuilder &builder) {
443 if (getAction().
empty())
444 getAction().push_back(
new Block());
445 return &getAction().front();
449StateOp TransitionOp::getNextStateOp() {
450 auto machineOp = (*this)->getParentOfType<MachineOp>();
454 return machineOp.lookupSymbol<StateOp>(getNextState());
457bool TransitionOp::isAlwaysTaken() {
461 auto guardReturn = getGuardReturn();
462 if (guardReturn.getNumOperands() == 0)
465 if (
auto constantOp =
466 guardReturn.getOperand().getDefiningOp<mlir::arith::ConstantOp>())
467 return cast<BoolAttr>(constantOp.getValue()).getValue();
472LogicalResult TransitionOp::canonicalize(
TransitionOp op,
473 PatternRewriter &rewriter) {
475 auto guardReturn = op.getGuardReturn();
476 if (guardReturn.getNumOperands() == 1)
477 if (
auto constantOp = guardReturn.getOperand()
478 .getDefiningOp<mlir::arith::ConstantOp>()) {
480 if (cast<BoolAttr>(constantOp.getValue()).getValue()) {
483 rewriter.setInsertionPoint(guardReturn);
484 fsm::ReturnOp::create(rewriter, guardReturn.getLoc());
485 rewriter.eraseOp(guardReturn);
489 rewriter.eraseOp(op);
498LogicalResult TransitionOp::verify() {
499 if (!getNextStateOp())
500 return emitOpError(
"cannot find the definition of the next state `")
501 << getNextState() <<
"`";
505 if (getGuard().front().
empty() ||
506 !isa_and_nonnull<fsm::ReturnOp>(&getGuard().front().back()))
507 return emitOpError(
"guard region must terminate with a ReturnOp");
511 if ((*this)->getParentRegion() != &getCurrentState().getTransitions())
512 return emitOpError(
"must only be located in the transitions region");
521void VariableOp::getAsmResultNames(
522 function_ref<
void(Value, StringRef)> setNameFn) {
523 setNameFn(getResult(),
getName());
530void ReturnOp::setOperand(Value value) {
532 getOperation()->setOperand(0, value);
534 getOperation()->insertOperands(0, {value});
542VariableOp UpdateOp::getVariableOp() {
543 return getVariable().getDefiningOp<VariableOp>();
546LogicalResult UpdateOp::canonicalize(UpdateOp op, PatternRewriter &rewriter) {
548 if (op.getVariable() == op.getValue()) {
549 rewriter.eraseOp(op);
555LogicalResult UpdateOp::verify() {
557 return emitOpError(
"destination is not a variable operation");
559 if (!(*this)->getParentOfType<
TransitionOp>().getAction().isAncestor(
560 (*this)->getParentRegion()))
561 return emitOpError(
"must only be located in the action region");
563 auto transition = (*this)->getParentOfType<
TransitionOp>();
564 for (
auto otherUpdateOp : transition.getAction().getOps<UpdateOp>()) {
565 if (otherUpdateOp == *
this)
567 if (otherUpdateOp.getVariable() == getVariable())
568 return otherUpdateOp.emitOpError(
569 "multiple updates to the same variable within a single action region "
581#define GET_OP_CLASSES
582#include "circt/Dialect/FSM/FSM.cpp.inc"
585#include "circt/Dialect/FSM/FSMDialect.cpp.inc"
assert(baseType &&"element must be base type")
static LogicalResult verifyCallerTypes(OpType op)
static LogicalResult compareTypes(Location loc, TypeRange rangeA, TypeRange rangeB)
static Location getLoc(DefSlot slot)
false return failure()
Parse an optional keyword or string and set instance into 'result'.
static StringAttr getResName(Operation *op, size_t idx)
static StringAttr getArgName(Operation *op, size_t idx)
static InstancePath empty
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
size_t argNum
This is the argument index or the result index depending on the direction.