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;
28 SymbolTable *symbolTable) {
29 auto mod = base->getParentOfType<mlir::ModuleOp>();
31 return dyn_cast<T>(symbolTable->lookupSymbolIn(mod, sym));
33 return mod.lookupSymbol<T>(sym);
36 template <
typename TSymAttr>
44 template <
typename TSymAttr>
53 static llvm::raw_string_ostream &
genValueName(llvm::raw_string_ostream &os,
55 auto *definingOp =
value.getDefiningOp();
56 assert(definingOp &&
"scoperef should always be defined by some op");
57 llvm::TypeSwitch<Operation *, void>(definingOp)
58 .Case<ThisOp>([&](
auto op) { os <<
"this"; })
59 .Case<InstanceOp, ContainerInstanceOp>(
60 [&](
auto op) { os << op.getInstanceNameAttr().strref(); })
61 .Case<PortOpInterface>([&](
auto op) { os << op.getPortName().strref(); })
62 .Case<PathOp>([&](
auto op) {
64 op.getPathAsRange(), os,
65 [&](PathStepAttr step) {
66 if (step.getDirection() == PathDirection::Parent)
69 os << step.getChild().getAttr().strref();
73 .Case<GetPortOp>([&](
auto op) {
75 <<
"." << op.getPortSymbol() <<
".ref";
78 [&](
auto op) {
genValueName(os, op.getPort()) <<
".val"; })
79 .Default([&](
auto op) {
80 op->emitOpError() <<
"unhandled value type";
81 assert(
false &&
"unhandled value type");
90 llvm::raw_string_ostream os(s);
101 auto scopeOp = cast<ScopeOpInterface>(op);
102 auto thisOps = scopeOp.getBodyBlock()->getOps<ibis::ThisOp>();
104 return op->emitOpError(
"must contain a 'ibis.this' operation");
106 if (std::next(thisOps.begin()) != thisOps.end())
107 return op->emitOpError(
"must contain only one 'ibis.this' operation");
109 return {*thisOps.begin()};
116 if (!isa<SymbolOpInterface>(op))
117 return op->emitOpError(
"must implement 'SymbolOpInterface'");
126 template <
typename TOp>
130 if (parser.parseSymbolName(nameAttr))
133 result.attributes.append(hw::InnerSymbolTable::getInnerSymbolAttrName(),
137 SmallVector<OpAsmParser::Argument, 4> args;
138 SmallVector<Attribute> argNames;
139 SmallVector<Type> resultTypes;
140 TypeAttr functionType;
142 using namespace mlir::function_interface_impl;
143 auto *context = parser.getContext();
146 if (parser.parseArgumentList(args, OpAsmParser::Delimiter::Paren,
151 if (parser.parseOptionalArrowTypeList(resultTypes))
155 SmallVector<Type> argTypes;
156 for (
auto &arg : args) {
158 argTypes.push_back(arg.type);
160 arg.sourceLoc = parser.getEncodedSourceLoc(arg.ssaName.location);
167 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
170 result.addAttribute(
"argNames",
ArrayAttr::get(context, argNames));
171 result.addAttribute(TOp::getFunctionTypeAttrName(result.name), functionType);
174 auto *body = result.addRegion();
175 if (parser.parseRegion(*body, args))
181 template <
typename TOp>
183 FunctionType funcTy = op.getFunctionType();
185 p.printSymbolName(op.getInnerSym().getSymName());
186 Region &body = op.getBody();
188 llvm::interleaveComma(body.getArguments(), p,
189 [&](BlockArgument arg) { p.printRegionArgument(arg); });
191 p.printArrowTypeList(funcTy.getResults());
192 p.printOptionalAttrDictWithKeyword(op.getOperation()->getAttrs(),
193 op.getAttributeNames());
196 p.printRegion(body,
false,
201 ParseResult MethodOp::parse(OpAsmParser &parser, OperationState &result) {
202 return parseMethodLikeOp<MethodOp>(parser, result);
207 void MethodOp::getAsmBlockArgumentNames(mlir::Region ®ion,
212 auto func = cast<MethodOp>(region.getParentOp());
213 auto argNames = func.getArgNames().getAsRange<StringAttr>();
214 auto *block = ®ion.front();
216 for (
auto [idx, argName] : llvm::enumerate(argNames))
217 if (!argName.getValue().empty())
218 setNameFn(block->getArgument(idx), argName);
225 ParseResult DataflowMethodOp::parse(OpAsmParser &parser,
226 OperationState &result) {
227 return parseMethodLikeOp<DataflowMethodOp>(parser, result);
230 void DataflowMethodOp::print(OpAsmPrinter &p) {
238 void ReturnOp::build(OpBuilder &odsBuilder, OperationState &odsState) {}
240 LogicalResult ReturnOp::verify() {
242 auto methodLike = cast<MethodLikeOpInterface>((*this)->getParentOp());
243 ArrayRef<Type> resTypes = methodLike.getResultTypes();
245 if (getNumOperands() != resTypes.size())
247 "must have the same number of operands as the method has results");
249 for (
auto [arg, resType] : llvm::zip(getOperands(), resTypes))
250 if (arg.getType() != resType)
251 return emitOpError(
"operand type (")
252 << arg.getType() <<
") must match function return type ("
262 LogicalResult GetVarOp::verifyInnerRefs(hw::InnerRefNamespace &ns) {
263 ScopeRefType parentType = getInstance().getType().cast<ScopeRefType>();
265 parentType.getScopeRef().getAttr(), getVarNameAttr().
getAttr()));
271 if (varOp.getType() != getType())
272 return emitOpError() <<
"dereferenced type (" << getType()
273 <<
") must match variable type (" << varOp.getType()
280 auto targetClassSym =
281 getInstance().getType().cast<ScopeRefType>().getScopeRef();
283 lookupInModule<ClassOp>(getOperation(), targetClassSym, symbolTable);
286 return emitOpError() <<
"'" << targetClassSym <<
"' does not exist";
289 auto varName = getVarName();
292 auto var = dyn_cast_or_null<VarOp>(
293 symbolTable->lookupSymbolIn(targetClass.getOperation(), varName));
295 return emitOpError() <<
"'" << varName <<
"' does not exist in '"
296 << targetClassSym <<
"'";
304 LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
305 auto targetClass = getClass(&symbolTable.getSymbolTable(
306 getOperation()->getParentOfType<mlir::ModuleOp>()));
308 return emitOpError() <<
"'" << getTargetName() <<
"' does not exist";
313 ClassOp InstanceOp::getClass(SymbolTable *symbolTable) {
314 return lookupInModule<ClassOp>(getOperation(), getTargetNameAttr(),
326 LogicalResult GetPortOp::verifyInnerRefs(hw::InnerRefNamespace &ns) {
328 ScopeRefType crt = getInstance().getType().cast<ScopeRefType>();
330 crt.getScopeRef().getAttr(), getPortSymbolAttr().
getAttr()));
333 return emitOpError() <<
"port '" << getPortSymbolAttr()
334 <<
"' does not exist in " << crt.getScopeRef();
336 auto portOp = dyn_cast<PortOpInterface>(targetOp);
338 return emitOpError() <<
"symbol '" << getPortSymbolAttr()
339 <<
"' does not refer to a port";
341 Type targetPortType = portOp.getPortType();
342 Type thisPortType = getType().getPortType();
343 if (targetPortType != thisPortType)
344 return emitOpError() <<
"symbol '" << getPortSymbolAttr()
345 <<
"' refers to a port of type " << targetPortType
346 <<
", but this op has type " << thisPortType;
351 LogicalResult GetPortOp::canonicalize(GetPortOp op, PatternRewriter &rewriter) {
355 auto parentScope = dyn_cast<ScopeOpInterface>(op->getParentOp());
357 auto scopeThis = parentScope.getThis();
358 if (op.getInstance() == scopeThis) {
359 auto definingPort = parentScope.lookupPort(op.getPortSymbol());
360 rewriter.replaceOp(op, {definingPort.getPort()});
376 LogicalResult ThisOp::verifyInnerRefs(hw::InnerRefNamespace &ns) {
380 dyn_cast_or_null<ScopeOpInterface>(getOperation()->getParentOp());
382 return emitOpError() <<
"thisOp must be nested in a scope op";
384 if (parentScope.getScopeName() != getScopeName())
385 return emitOpError() <<
"thisOp refers to a parent scope of name "
386 << getScopeName() <<
", but the parent scope is named "
387 << parentScope.getScopeName();
393 setNameFn(getResult(),
"this");
408 ContainerOp ContainerInstanceOp::getContainer(SymbolTable *symbolTable) {
409 auto mod = getOperation()->getParentOfType<mlir::ModuleOp>();
411 return dyn_cast_or_null<ContainerOp>(
412 symbolTable->lookupSymbolIn(mod, getTargetNameAttr()));
414 return mod.lookupSymbol<ContainerOp>(getTargetNameAttr());
418 ContainerInstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
419 auto targetContainer = getContainer(&symbolTable.getSymbolTable(
420 getOperation()->getParentOfType<mlir::ModuleOp>()));
421 if (!targetContainer)
422 return emitOpError() <<
"'" << getTargetName() <<
"' does not exist";
437 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
438 DictionaryAttr attrs, mlir::OpaqueProperties properties,
439 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
440 auto path = attrs.get(
"path").cast<ArrayAttr>();
444 auto lastStep = path.getValue().back().cast<PathStepAttr>();
445 results.push_back(lastStep.getType());
449 LogicalResult PathStepAttr::verify(function_ref<InFlightDiagnostic()> emitError,
450 PathDirection direction, mlir::Type type,
451 mlir::FlatSymbolRefAttr instance) {
453 if (direction == PathDirection::Parent && instance)
454 return emitError() <<
"ibis.step 'parent' may not specify an instance name";
456 if (direction == PathDirection::Child && !instance)
457 return emitError() <<
"ibis.step 'child' must specify an instance name";
460 auto scoperefType = type.dyn_cast<ScopeRefType>();
462 return emitError() <<
"ibis.step type must be an !ibis.scoperef type";
467 LogicalResult PathOp::verify() {
468 auto pathRange = getPathAsRange();
469 if (pathRange.empty())
470 return emitOpError() <<
"ibis.path must have at least one step";
474 auto mod = getOperation()->getParentOfType<mlir::ModuleOp>();
475 for (PathStepAttr step : getPathAsRange()) {
476 auto scoperefType = step.getType().cast<ScopeRefType>();
477 FlatSymbolRefAttr scopeRefSym = scoperefType.getScopeRef();
481 auto *targetScope = mod.lookupSymbol(scopeRefSym);
483 return emitOpError() <<
"ibis.step scoperef symbol '" << scopeRefSym
484 <<
"' does not exist";
488 PathStepAttr lastStep = *std::prev(getPathAsRange().
end());
489 ScopeRefType lastStepType = lastStep.getType().cast<ScopeRefType>();
490 if (!lastStepType.getScopeRef())
492 <<
"last ibis.step in path must specify a symbol for the scoperef";
497 LogicalResult PathOp::canonicalize(PathOp op, PatternRewriter &rewriter) {
500 auto range = op.getPathAsRange();
501 size_t pathSize = std::distance(range.begin(), range.end());
502 PathStepAttr firstStep = *range.begin();
503 if (pathSize == 1 && firstStep.getDirection() == PathDirection::Child) {
504 auto parentScope = cast<ScopeOpInterface>(op->getParentOp());
505 auto childInstance = dyn_cast_or_null<ContainerInstanceOp>(
506 parentScope.lookupInnerSym(firstStep.getChild().getValue()));
507 assert(childInstance &&
"should have been verified by the op verifier");
508 rewriter.replaceOp(op, {childInstance.getResult()});
523 LogicalResult OutputPortOp::canonicalize(OutputPortOp op,
524 PatternRewriter &rewriter) {
528 llvm::SmallVector<PortReadOp, 4> readers;
529 for (
auto *user : op.getResult().getUsers()) {
530 if (
auto read = dyn_cast<PortReadOp>(user)) {
531 readers.push_back(read);
532 }
else if (
auto write = dyn_cast<PortWriteOp>(user);
533 write && write.getPort() == op.getPort()) {
534 assert(!writer &&
"should only have one writer");
539 if (!readers.empty()) {
540 for (
auto reader : readers)
541 rewriter.replaceOp(reader, writer.getValue());
552 LogicalResult InputWireOp::canonicalize(InputWireOp op,
553 PatternRewriter &rewriter) {
555 auto portUsers = op.getPort().getUsers();
556 size_t nPortUsers = std::distance(portUsers.begin(), portUsers.end());
557 for (
auto *portUser : op.getPort().getUsers()) {
558 auto writer = dyn_cast<PortWriteOp>(portUser);
559 if (writer && writer.getPort() == op.getPort() && nPortUsers == 1) {
560 rewriter.replaceAllUsesWith(op.getOutput(), writer.getValue());
561 rewriter.eraseOp(writer);
562 rewriter.eraseOp(op);
574 LogicalResult OutputWireOp::canonicalize(OutputWireOp op,
575 PatternRewriter &rewriter) {
579 auto portUsers = op.getPort().getUsers();
580 size_t nPortUsers = std::distance(portUsers.begin(), portUsers.end());
581 for (
auto *portUser : op.getPort().getUsers()) {
582 auto reader = dyn_cast<PortReadOp>(portUser);
583 if (reader && reader.getPort() == op.getPort() && nPortUsers == 1) {
584 rewriter.replaceOp(reader, op.getInput());
585 rewriter.eraseOp(op);
597 template <
typename TOp>
599 OpAsmParser &parser, OperationState &result,
600 llvm::function_ref<ParseResult(OpAsmParser::Argument &)> argAdjuster = {}) {
602 llvm::SmallVector<OpAsmParser::UnresolvedOperand> inputOperands;
603 llvm::SmallVector<OpAsmParser::Argument> inputArguments;
604 llvm::SmallVector<Type> inputTypes;
605 ArrayAttr inputNames;
607 inputTypes, inputNames))
611 llvm::SmallVector<Type> resultTypes;
612 if (parser.parseOptionalArrowTypeList(resultTypes))
614 result.addTypes(resultTypes);
617 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
621 if (parser.resolveOperands(inputOperands, inputTypes, parser.getNameLoc(),
627 for (
auto &arg : inputArguments)
628 if (failed(argAdjuster(arg)))
633 Region *body = result.addRegion();
634 if (parser.parseRegion(*body, inputArguments))
637 TOp::ensureTerminator(*body, parser.getBuilder(), result.location);
641 template <
typename T>
645 op.getBodyBlock()->getArguments());
646 p.printOptionalArrowTypeList(op.getResultTypes());
647 p.printOptionalAttrDictWithKeyword(op.getOperation()->getAttrs());
649 p.printRegion(op.getBody(),
false);
652 LogicalResult StaticBlockOp::verify() {
653 if (getInputs().size() != getBodyBlock()->getNumArguments())
654 return emitOpError(
"number of inputs must match number of block arguments");
656 for (
auto [arg, barg] :
657 llvm::zip(getInputs(), getBodyBlock()->getArguments())) {
658 if (arg.getType() != barg.getType())
659 return emitOpError(
"block argument type must match input type");
665 ParseResult StaticBlockOp::parse(OpAsmParser &parser, OperationState &result) {
666 return parseBlockLikeOp<StaticBlockOp>(parser, result);
669 void StaticBlockOp::print(OpAsmPrinter &p) {
677 LogicalResult IsolatedStaticBlockOp::verify() {
678 if (getInputs().size() != getBodyBlock()->getNumArguments())
679 return emitOpError(
"number of inputs must match number of block arguments");
681 for (
auto [arg, barg] :
682 llvm::zip(getInputs(), getBodyBlock()->getArguments())) {
683 if (arg.getType() != barg.getType())
684 return emitOpError(
"block argument type must match input type");
690 ParseResult IsolatedStaticBlockOp::parse(OpAsmParser &parser,
691 OperationState &result) {
692 return parseBlockLikeOp<IsolatedStaticBlockOp>(parser, result);
695 void IsolatedStaticBlockOp::print(OpAsmPrinter &p) {
703 void DCBlockOp::build(OpBuilder &odsBuilder, OperationState &odsState,
705 IntegerAttr maxThreads) {
706 odsState.addOperands(
inputs);
708 odsState.addAttribute(getMaxThreadsAttrName(odsState.name), maxThreads);
709 auto *region = odsState.addRegion();
710 llvm::SmallVector<Type> resTypes;
712 dc::ValueType dcType = output.dyn_cast<dc::ValueType>();
713 assert(dcType &&
"DCBlockOp outputs must be dc::ValueType");
714 resTypes.push_back(dcType);
716 odsState.addTypes(resTypes);
717 ensureTerminator(*region, odsBuilder, odsState.location);
718 llvm::SmallVector<Location> argLocs;
719 llvm::SmallVector<Type> argTypes;
720 for (
auto input :
inputs) {
721 argLocs.push_back(input.getLoc());
722 dc::ValueType dcType = input.getType().dyn_cast<dc::ValueType>();
723 assert(dcType &&
"DCBlockOp inputs must be dc::ValueType");
724 argTypes.push_back(dcType.getInnerType());
726 region->front().addArguments(argTypes, argLocs);
729 LogicalResult DCBlockOp::verify() {
730 if (getInputs().size() != getBodyBlock()->getNumArguments())
731 return emitOpError(
"number of inputs must match number of block arguments");
733 for (
auto [arg, barg] :
734 llvm::zip(getInputs(), getBodyBlock()->getArguments())) {
735 dc::ValueType dcType = arg.getType().dyn_cast<dc::ValueType>();
737 return emitOpError(
"DCBlockOp inputs must be dc::ValueType but got ")
740 if (dcType.getInnerType() != barg.getType())
741 return emitOpError(
"block argument type must match input type. Got ")
742 << barg.getType() <<
" expected " << dcType.getInnerType();
748 ParseResult DCBlockOp::parse(OpAsmParser &parser, OperationState &result) {
749 return parseBlockLikeOp<DCBlockOp>(
750 parser, result, [&](OpAsmParser::Argument &arg) -> LogicalResult {
751 dc::ValueType valueType = arg.type.dyn_cast<dc::ValueType>();
753 return parser.emitError(parser.getCurrentLocation(),
754 "DCBlockOp inputs must be dc::ValueType");
755 arg.type = valueType.getInnerType();
760 void DCBlockOp::print(OpAsmPrinter &p) {
return printBlockLikeOp(*
this, p); }
766 LogicalResult BlockReturnOp::verify() {
767 Operation *parent = getOperation()->getParentOp();
768 auto parentBlock = dyn_cast<BlockOpInterface>(parent);
770 return emitOpError(
"must be nested in a block");
772 if (getNumOperands() != parent->getNumResults())
773 return emitOpError(
"number of operands must match number of block outputs");
775 for (
auto [op, out] :
776 llvm::zip(getOperands(), parentBlock.getInternalResultTypes())) {
777 if (op.getType() != out)
779 "operand type must match parent block output type. Expected ")
780 << out <<
" got " << op.getType();
790 InlineStaticBlockBeginOp InlineStaticBlockEndOp::getBeginOp() {
791 auto curr = getOperation()->getReverseIterator();
792 Operation *firstOp = &getOperation()->getBlock()->front();
794 if (
auto beginOp = dyn_cast<InlineStaticBlockBeginOp>(*curr))
796 if (curr.getNodePtr() == firstOp)
807 InlineStaticBlockEndOp InlineStaticBlockBeginOp::getEndOp() {
808 auto curr = getOperation()->getIterator();
809 auto end = getOperation()->getBlock()->end();
810 while (curr != end) {
811 if (
auto endOp = dyn_cast<InlineStaticBlockEndOp>(*curr))
823 #include "circt/Dialect/Ibis/IbisInterfaces.cpp.inc"
826 #define GET_OP_CLASSES
827 #include "circt/Dialect/Ibis/Ibis.cpp.inc"
assert(baseType &&"element must be base type")
static Attribute getAttr(ArrayRef< NamedAttribute > attrs, StringRef name)
Get an attribute by name from a list of named attributes.
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 T lookupInModule(Operation *base, FlatSymbolRefAttr sym, SymbolTable *symbolTable)
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.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn