13 #include "mlir/IR/BuiltinOps.h"
14 #include "mlir/IR/DialectImplementation.h"
15 #include "mlir/IR/PatternMatch.h"
16 #include "mlir/IR/SymbolTable.h"
17 #include "mlir/Interfaces/FunctionImplementation.h"
18 #include "llvm/ADT/TypeSwitch.h"
21 using namespace circt;
24 template <
typename TSymAttr>
32 template <
typename TSymAttr>
41 static llvm::raw_string_ostream &
genValueName(llvm::raw_string_ostream &os,
43 auto *definingOp = value.getDefiningOp();
44 assert(definingOp &&
"scoperef should always be defined by some op");
45 llvm::TypeSwitch<Operation *, void>(definingOp)
46 .Case<ThisOp>([&](
auto op) { os <<
"this"; })
47 .Case<InstanceOp, ContainerInstanceOp>(
48 [&](
auto op) { os << op.getInstanceNameAttr().strref(); })
49 .Case<PortOpInterface>([&](
auto op) { os << op.getPortName().strref(); })
50 .Case<PathOp>([&](
auto op) {
52 op.getPathAsRange(), os,
53 [&](PathStepAttr step) {
54 if (step.getDirection() == PathDirection::Parent)
57 os << step.getChild().getAttr().strref();
61 .Case<GetPortOp>([&](
auto op) {
63 <<
"." << op.getPortSymbol() <<
".ref";
66 [&](
auto op) {
genValueName(os, op.getPort()) <<
".val"; })
67 .Default([&](
auto op) {
68 op->emitOpError() <<
"unhandled value type";
69 assert(
false &&
"unhandled value type");
78 llvm::raw_string_ostream os(s);
89 auto scopeOp = cast<ScopeOpInterface>(op);
90 auto thisOps = scopeOp.getBodyBlock()->getOps<ibis::ThisOp>();
92 return op->emitOpError(
"must contain a 'ibis.this' operation");
94 if (std::next(thisOps.begin()) != thisOps.end())
95 return op->emitOpError(
"must contain only one 'ibis.this' operation");
97 return (*thisOps.begin()).getThisRef();
104 if (!isa<hw::InnerSymbolOpInterface>(op))
105 return op->emitOpError(
"must implement 'InnerSymbolOpInterface'");
114 template <
typename TOp>
118 if (parser.parseSymbolName(nameAttr))
121 result.attributes.append(hw::InnerSymbolTable::getInnerSymbolAttrName(),
125 SmallVector<OpAsmParser::Argument, 4> args;
126 SmallVector<Attribute> argNames;
127 SmallVector<Type> resultTypes;
128 TypeAttr functionType;
130 using namespace mlir::function_interface_impl;
131 auto *context = parser.getContext();
134 if (parser.parseArgumentList(args, OpAsmParser::Delimiter::Paren,
139 if (parser.parseOptionalArrowTypeList(resultTypes))
143 SmallVector<Type> argTypes;
144 for (
auto &arg : args) {
146 argTypes.push_back(arg.type);
148 arg.sourceLoc = parser.getEncodedSourceLoc(arg.ssaName.location);
155 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
158 result.addAttribute(
"argNames",
ArrayAttr::get(context, argNames));
159 result.addAttribute(TOp::getFunctionTypeAttrName(result.name), functionType);
162 auto *body = result.addRegion();
163 if (parser.parseRegion(*body, args))
169 template <
typename TOp>
171 FunctionType funcTy = op.getFunctionType();
173 p.printSymbolName(op.getInnerSym().getSymName());
174 Region &body = op.getBody();
176 llvm::interleaveComma(body.getArguments(), p,
177 [&](BlockArgument arg) { p.printRegionArgument(arg); });
179 p.printArrowTypeList(funcTy.getResults());
180 p.printOptionalAttrDictWithKeyword(op.getOperation()->getAttrs(),
181 op.getAttributeNames());
184 p.printRegion(body,
false,
189 ParseResult MethodOp::parse(OpAsmParser &parser, OperationState &result) {
190 return parseMethodLikeOp<MethodOp>(parser, result);
195 void MethodOp::getAsmBlockArgumentNames(mlir::Region ®ion,
200 auto func = cast<MethodOp>(region.getParentOp());
201 auto argNames = func.getArgNames().getAsRange<StringAttr>();
202 auto *block = ®ion.front();
204 for (
auto [idx, argName] : llvm::enumerate(argNames))
205 if (!argName.getValue().empty())
206 setNameFn(block->getArgument(idx), argName);
213 ParseResult DataflowMethodOp::parse(OpAsmParser &parser,
214 OperationState &result) {
215 return parseMethodLikeOp<DataflowMethodOp>(parser, result);
218 void DataflowMethodOp::print(OpAsmPrinter &p) {
226 void ReturnOp::build(OpBuilder &odsBuilder, OperationState &odsState) {}
228 LogicalResult ReturnOp::verify() {
230 auto methodLike = cast<MethodLikeOpInterface>((*this)->getParentOp());
231 ArrayRef<Type> resTypes = methodLike.getResultTypes();
233 if (getNumOperands() != resTypes.size())
235 "must have the same number of operands as the method has results");
237 for (
auto [arg, resType] : llvm::zip(getOperands(), resTypes))
238 if (arg.getType() != resType)
239 return emitOpError(
"operand type (")
240 << arg.getType() <<
") must match function return type ("
250 LogicalResult GetVarOp::verifyInnerRefs(hw::InnerRefNamespace &ns) {
251 auto varOp = getVar(ns);
256 if (varOp.getType() != getType())
257 return emitOpError() <<
"dereferenced type (" << getType()
258 <<
") must match variable type (" << varOp.getType()
264 VarOp GetVarOp::getVar(
const hw::InnerRefNamespace &ns) {
265 ScopeRefType parentType = cast<ScopeRefType>(getInstance().getType());
266 auto scopeRefOp = ns.lookupOp<ScopeOpInterface>(parentType.getScopeRef());
271 return dyn_cast_or_null<VarOp>(scopeRefOp.lookupInnerSym(getVarName()));
278 LogicalResult InstanceOp::verifyInnerRefs(hw::InnerRefNamespace &ns) {
280 return emitOpError() <<
"'" << getTargetName() <<
"' does not exist";
293 LogicalResult GetPortOp::verifyInnerRefs(hw::InnerRefNamespace &ns) {
296 return emitOpError() <<
"port '@" << getPortSymbol()
297 <<
"' does not exist in @"
298 << cast<ScopeRefType>(getInstance().getType())
303 Type targetPortType = portOp.getPortType();
304 Type thisPortType = getType().getPortType();
305 if (targetPortType != thisPortType)
306 return emitOpError() <<
"symbol '" << getPortSymbolAttr()
307 <<
"' refers to a port of type " << targetPortType
308 <<
", but this op has type " << thisPortType;
315 auto targetScope = ns.lookupOp<ScopeOpInterface>(
316 cast<ScopeRefType>(getInstance().getType()).getScopeRef());
321 return dyn_cast_or_null<PortOpInterface>(
322 targetScope.lookupInnerSym(getPortSymbol()));
325 LogicalResult GetPortOp::canonicalize(GetPortOp op, PatternRewriter &rewriter) {
329 auto parentScope = dyn_cast<ScopeOpInterface>(op->getParentOp());
331 auto scopeThis = parentScope.getThis();
332 if (op.getInstance() == scopeThis) {
333 auto definingPort = parentScope.lookupPort(op.getPortSymbol());
334 rewriter.replaceOp(op, {definingPort.getPort()});
350 LogicalResult ThisOp::verifyInnerRefs(hw::InnerRefNamespace &ns) {
352 return emitOpError() <<
"'" << getScopeName() <<
"' does not exist";
358 setNameFn(getResult(),
"this");
361 ScopeOpInterface ThisOp::getScope(
const hw::InnerRefNamespace &ns) {
362 return ns.lookupOp<ScopeOpInterface>(getScopeNameAttr());
377 LogicalResult ContainerInstanceOp::verifyInnerRefs(hw::InnerRefNamespace &ns) {
378 auto targetContainer = getContainer(ns);
379 if (!targetContainer)
380 return emitOpError() <<
"'" << getTargetName() <<
"' does not exist";
393 MethodOp CallOp::getTarget(
const hw::InnerRefNamespace &ns) {
394 return ns.lookupOp<MethodOp>(getCallee());
397 LogicalResult CallOp::verifyInnerRefs(hw::InnerRefNamespace &ns) {
399 return emitOpError() <<
"'" << getCallee() <<
"' does not exist";
410 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
411 DictionaryAttr attrs, mlir::OpaqueProperties properties,
412 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
413 auto path = cast<ArrayAttr>(attrs.get(
"path"));
417 auto lastStep = cast<PathStepAttr>(path.getValue().back());
418 results.push_back(lastStep.getType());
422 LogicalResult PathStepAttr::verify(function_ref<InFlightDiagnostic()> emitError,
423 PathDirection direction, mlir::Type type,
424 mlir::FlatSymbolRefAttr instance) {
426 if (direction == PathDirection::Parent && instance)
427 return emitError() <<
"ibis.step 'parent' may not specify an instance name";
429 if (direction == PathDirection::Child && !instance)
430 return emitError() <<
"ibis.step 'child' must specify an instance name";
433 auto scoperefType = llvm::dyn_cast<ScopeRefType>(type);
435 return emitError() <<
"ibis.step type must be an !ibis.scoperef type";
440 LogicalResult PathOp::verifyInnerRefs(hw::InnerRefNamespace &ns) {
441 auto pathRange = getPathAsRange();
442 if (pathRange.empty())
443 return emitOpError() <<
"ibis.path must have at least one step";
447 for (PathStepAttr step : getPathAsRange()) {
448 auto scoperefType = cast<ScopeRefType>(step.getType());
449 hw::InnerRefAttr scopeRefSym = scoperefType.getScopeRef();
453 auto *targetScope = ns.lookupOp(scopeRefSym);
455 return emitOpError() <<
"ibis.step scoperef symbol '@"
456 << scopeRefSym.getName().getValue()
457 <<
"' does not exist";
461 PathStepAttr lastStep = *std::prev(getPathAsRange().
end());
462 ScopeRefType lastStepType = cast<ScopeRefType>(lastStep.getType());
463 if (!lastStepType.getScopeRef())
465 <<
"last ibis.step in path must specify a symbol for the scoperef";
470 LogicalResult PathOp::canonicalize(PathOp op, PatternRewriter &rewriter) {
473 auto range = op.getPathAsRange();
474 size_t pathSize = std::distance(range.begin(), range.end());
475 PathStepAttr firstStep = *range.begin();
476 if (pathSize == 1 && firstStep.getDirection() == PathDirection::Child) {
477 auto parentScope = cast<ScopeOpInterface>(op->getParentOp());
478 auto childInstance = dyn_cast_or_null<ContainerInstanceOp>(
479 parentScope.lookupInnerSym(firstStep.getChild().getValue()));
480 assert(childInstance &&
"should have been verified by the op verifier");
481 rewriter.replaceOp(op, {childInstance.getResult()});
496 LogicalResult OutputPortOp::canonicalize(OutputPortOp op,
497 PatternRewriter &rewriter) {
501 llvm::SmallVector<PortReadOp, 4> readers;
502 for (
auto *user : op.getResult().getUsers()) {
503 if (
auto read = dyn_cast<PortReadOp>(user)) {
504 readers.push_back(read);
505 }
else if (
auto write = dyn_cast<PortWriteOp>(user);
506 write && write.getPort() == op.getPort()) {
507 assert(!writer &&
"should only have one writer");
512 if (!readers.empty()) {
513 for (
auto reader : readers)
514 rewriter.replaceOp(reader, writer.getValue());
525 LogicalResult InputWireOp::canonicalize(InputWireOp op,
526 PatternRewriter &rewriter) {
528 auto portUsers = op.getPort().getUsers();
529 size_t nPortUsers = std::distance(portUsers.begin(), portUsers.end());
530 for (
auto *portUser : op.getPort().getUsers()) {
531 auto writer = dyn_cast<PortWriteOp>(portUser);
532 if (writer && writer.getPort() == op.getPort() && nPortUsers == 1) {
533 rewriter.replaceAllUsesWith(op.getOutput(), writer.getValue());
534 rewriter.eraseOp(writer);
535 rewriter.eraseOp(op);
547 LogicalResult OutputWireOp::canonicalize(OutputWireOp op,
548 PatternRewriter &rewriter) {
552 auto portUsers = op.getPort().getUsers();
553 size_t nPortUsers = std::distance(portUsers.begin(), portUsers.end());
554 for (
auto *portUser : op.getPort().getUsers()) {
555 auto reader = dyn_cast<PortReadOp>(portUser);
556 if (reader && reader.getPort() == op.getPort() && nPortUsers == 1) {
557 rewriter.replaceOp(reader, op.getInput());
558 rewriter.eraseOp(op);
570 template <
typename TOp>
572 OpAsmParser &parser, OperationState &result,
573 llvm::function_ref<ParseResult(OpAsmParser::Argument &)> argAdjuster = {}) {
575 llvm::SmallVector<OpAsmParser::UnresolvedOperand> inputOperands;
576 llvm::SmallVector<OpAsmParser::Argument> inputArguments;
577 llvm::SmallVector<Type> inputTypes;
578 ArrayAttr inputNames;
580 inputTypes, inputNames))
584 llvm::SmallVector<Type> resultTypes;
585 if (parser.parseOptionalArrowTypeList(resultTypes))
587 result.addTypes(resultTypes);
590 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
594 if (parser.resolveOperands(inputOperands, inputTypes, parser.getNameLoc(),
600 for (
auto &arg : inputArguments)
601 if (failed(argAdjuster(arg)))
606 Region *body = result.addRegion();
607 if (parser.parseRegion(*body, inputArguments))
610 TOp::ensureTerminator(*body, parser.getBuilder(), result.location);
614 template <
typename T>
618 op.getBodyBlock()->getArguments());
619 p.printOptionalArrowTypeList(op.getResultTypes());
620 p.printOptionalAttrDictWithKeyword(op.getOperation()->getAttrs());
622 p.printRegion(op.getBody(),
false);
625 LogicalResult StaticBlockOp::verify() {
626 if (getInputs().size() != getBodyBlock()->getNumArguments())
627 return emitOpError(
"number of inputs must match number of block arguments");
629 for (
auto [arg, barg] :
630 llvm::zip(getInputs(), getBodyBlock()->getArguments())) {
631 if (arg.getType() != barg.getType())
632 return emitOpError(
"block argument type must match input type");
638 ParseResult StaticBlockOp::parse(OpAsmParser &parser, OperationState &result) {
639 return parseBlockLikeOp<StaticBlockOp>(parser, result);
642 void StaticBlockOp::print(OpAsmPrinter &p) {
650 LogicalResult IsolatedStaticBlockOp::verify() {
651 if (getInputs().size() != getBodyBlock()->getNumArguments())
652 return emitOpError(
"number of inputs must match number of block arguments");
654 for (
auto [arg, barg] :
655 llvm::zip(getInputs(), getBodyBlock()->getArguments())) {
656 if (arg.getType() != barg.getType())
657 return emitOpError(
"block argument type must match input type");
663 ParseResult IsolatedStaticBlockOp::parse(OpAsmParser &parser,
664 OperationState &result) {
665 return parseBlockLikeOp<IsolatedStaticBlockOp>(parser, result);
668 void IsolatedStaticBlockOp::print(OpAsmPrinter &p) {
676 void DCBlockOp::build(OpBuilder &odsBuilder, OperationState &odsState,
678 IntegerAttr maxThreads) {
679 odsState.addOperands(
inputs);
681 odsState.addAttribute(getMaxThreadsAttrName(odsState.name), maxThreads);
682 auto *region = odsState.addRegion();
683 llvm::SmallVector<Type> resTypes;
685 dc::ValueType dcType = dyn_cast<dc::ValueType>(output);
686 assert(dcType &&
"DCBlockOp outputs must be dc::ValueType");
687 resTypes.push_back(dcType);
689 odsState.addTypes(resTypes);
690 ensureTerminator(*region, odsBuilder, odsState.location);
691 llvm::SmallVector<Location> argLocs;
692 llvm::SmallVector<Type> argTypes;
693 for (
auto input :
inputs) {
694 argLocs.push_back(input.getLoc());
695 dc::ValueType dcType = dyn_cast<dc::ValueType>(input.getType());
696 assert(dcType &&
"DCBlockOp inputs must be dc::ValueType");
697 argTypes.push_back(dcType.getInnerType());
699 region->front().addArguments(argTypes, argLocs);
702 LogicalResult DCBlockOp::verify() {
703 if (getInputs().size() != getBodyBlock()->getNumArguments())
704 return emitOpError(
"number of inputs must match number of block arguments");
706 for (
auto [arg, barg] :
707 llvm::zip(getInputs(), getBodyBlock()->getArguments())) {
708 dc::ValueType dcType = dyn_cast<dc::ValueType>(arg.getType());
710 return emitOpError(
"DCBlockOp inputs must be dc::ValueType but got ")
713 if (dcType.getInnerType() != barg.getType())
714 return emitOpError(
"block argument type must match input type. Got ")
715 << barg.getType() <<
" expected " << dcType.getInnerType();
721 ParseResult DCBlockOp::parse(OpAsmParser &parser, OperationState &result) {
722 return parseBlockLikeOp<DCBlockOp>(
723 parser, result, [&](OpAsmParser::Argument &arg) -> LogicalResult {
724 dc::ValueType valueType = dyn_cast<dc::ValueType>(arg.type);
726 return parser.emitError(parser.getCurrentLocation(),
727 "DCBlockOp inputs must be dc::ValueType");
728 arg.type = valueType.getInnerType();
733 void DCBlockOp::print(OpAsmPrinter &p) {
return printBlockLikeOp(*
this, p); }
739 LogicalResult BlockReturnOp::verify() {
740 Operation *parent = getOperation()->getParentOp();
741 auto parentBlock = dyn_cast<BlockOpInterface>(parent);
743 return emitOpError(
"must be nested in a block");
745 if (getNumOperands() != parent->getNumResults())
746 return emitOpError(
"number of operands must match number of block outputs");
748 for (
auto [op, out] :
749 llvm::zip(getOperands(), parentBlock.getInternalResultTypes())) {
750 if (op.getType() != out)
752 "operand type must match parent block output type. Expected ")
753 << out <<
" got " << op.getType();
763 InlineStaticBlockBeginOp InlineStaticBlockEndOp::getBeginOp() {
764 auto curr = getOperation()->getReverseIterator();
765 Operation *firstOp = &getOperation()->getBlock()->front();
767 if (
auto beginOp = dyn_cast<InlineStaticBlockBeginOp>(*curr))
769 if (curr.getNodePtr() == firstOp)
780 InlineStaticBlockEndOp InlineStaticBlockBeginOp::getEndOp() {
781 auto curr = getOperation()->getIterator();
782 auto end = getOperation()->getBlock()->end();
783 while (curr != end) {
784 if (
auto endOp = dyn_cast<InlineStaticBlockEndOp>(*curr))
796 #include "circt/Dialect/Ibis/IbisInterfaces.cpp.inc"
799 #define GET_OP_CLASSES
800 #include "circt/Dialect/Ibis/Ibis.cpp.inc"
assert(baseType &&"element must be base type")
static PortInfo getPort(ModuleTy &mod, size_t idx)
ParseResult parseMethodLikeOp(OpAsmParser &parser, OperationState &result)
void printScopeRefFromName(OpAsmPrinter &p, Operation *op, Type type, TSymAttr sym)
static llvm::raw_string_ostream & genValueName(llvm::raw_string_ostream &os, Value value)
void printMethodLikeOp(TOp op, OpAsmPrinter &p)
static StringAttr genValueNameAttr(Value v)
ParseResult parseScopeRefFromName(OpAsmParser &parser, Type &scopeRefType, TSymAttr sym)
static void printBlockLikeOp(T op, OpAsmPrinter &p)
static ParseResult parseBlockLikeOp(OpAsmParser &parser, OperationState &result, llvm::function_ref< ParseResult(OpAsmParser::Argument &)> argAdjuster={})
llvm::SmallVector< StringAttr > inputs
llvm::SmallVector< StringAttr > outputs
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
LogicalResult inferReturnTypes(MLIRContext *context, std::optional< Location > loc, ValueRange operands, DictionaryAttr attrs, mlir::OpaqueProperties properties, mlir::RegionRange regions, SmallVectorImpl< Type > &results, llvm::function_ref< FIRRTLType(ValueRange, ArrayRef< NamedAttribute >, std::optional< Location >)> callback)
void getAsmResultNames(OpAsmSetValueNameFn setNameFn, StringRef instanceName, ArrayAttr resultNames, ValueRange results)
Suggest a name for each result value based on the saved result names attribute.
LogicalResult verifyScopeOpInterface(Operation *op)
mlir::FailureOr< mlir::TypedValue< ScopeRefType > > getThisFromScope(Operation *op)
ParseResult parseInitializerList(mlir::OpAsmParser &parser, llvm::SmallVector< mlir::OpAsmParser::Argument > &inputArguments, llvm::SmallVector< mlir::OpAsmParser::UnresolvedOperand > &inputOperands, llvm::SmallVector< Type > &inputTypes, ArrayAttr &inputNames)
Parses an initializer.
void printInitializerList(OpAsmPrinter &p, ValueRange ins, ArrayRef< BlockArgument > args)
static StringAttr getNameFromSSA(MLIRContext *context, StringRef name)
Get a name from an SSA value string, if said value name is not a number.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn