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;
22 using namespace kanagawa;
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.getNameHint(); })
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);
87 FailureOr<mlir::TypedValue<ScopeRefType>>
89 auto scopeOp = cast<ScopeOpInterface>(op);
90 auto thisOps = scopeOp.getBodyBlock()->getOps<kanagawa::ThisOp>();
92 return op->emitOpError(
"must contain a 'kanagawa.this' operation");
94 if (std::next(thisOps.begin()) != thisOps.end())
95 return op->emitOpError(
"must contain only one 'kanagawa.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) {}
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()));
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";
409 LogicalResult PathOp::inferReturnTypes(
410 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
411 DictionaryAttr attrs, mlir::OpaqueProperties properties,
412 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
413 Adaptor adaptor(operands, attrs, properties, regions);
414 auto path = adaptor.getPathAttr();
418 auto lastStep = cast<PathStepAttr>(path.getValue().back());
419 results.push_back(lastStep.getType());
424 PathDirection direction, mlir::Type type,
425 mlir::FlatSymbolRefAttr instance) {
427 if (direction == PathDirection::Parent && instance)
429 <<
"kanagawa.step 'parent' may not specify an instance name";
431 if (direction == PathDirection::Child && !instance)
432 return emitError() <<
"kanagawa.step 'child' must specify an instance name";
435 auto scoperefType = llvm::dyn_cast<ScopeRefType>(type);
438 <<
"kanagawa.step type must be an !kanagawa.scoperef type";
443 LogicalResult PathOp::verifyInnerRefs(hw::InnerRefNamespace &ns) {
444 auto pathRange = getPathAsRange();
445 if (pathRange.empty())
446 return emitOpError() <<
"kanagawa.path must have at least one step";
450 for (PathStepAttr step : getPathAsRange()) {
451 auto scoperefType = cast<ScopeRefType>(step.getType());
452 hw::InnerRefAttr scopeRefSym = scoperefType.getScopeRef();
456 auto *targetScope = ns.lookupOp(scopeRefSym);
458 return emitOpError() <<
"kanagawa.step scoperef symbol '@"
459 << scopeRefSym.getName().getValue()
460 <<
"' does not exist";
464 PathStepAttr lastStep = *std::prev(getPathAsRange().
end());
465 ScopeRefType lastStepType = cast<ScopeRefType>(lastStep.getType());
466 if (!lastStepType.getScopeRef())
467 return emitOpError() <<
"last kanagawa.step in path must specify a symbol "
476 auto range = op.getPathAsRange();
477 size_t pathSize = std::distance(range.begin(), range.end());
478 PathStepAttr firstStep = *range.begin();
479 if (pathSize == 1 && firstStep.getDirection() == PathDirection::Child) {
480 auto parentScope = cast<ScopeOpInterface>(op->getParentOp());
481 auto childInstance = dyn_cast_or_null<ContainerInstanceOp>(
482 parentScope.lookupInnerSym(firstStep.getChild().getValue()));
483 assert(childInstance &&
"should have been verified by the op verifier");
484 rewriter.replaceOp(op, {childInstance.getResult()});
500 PatternRewriter &rewriter) {
504 llvm::SmallVector<PortReadOp, 4> readers;
505 for (
auto *user : op.getResult().getUsers()) {
506 if (
auto read = dyn_cast<PortReadOp>(user)) {
507 readers.push_back(read);
508 }
else if (
auto write = dyn_cast<PortWriteOp>(user);
509 write && write.getPort() == op.getPort()) {
510 assert(!writer &&
"should only have one writer");
515 if (!readers.empty()) {
516 for (
auto reader : readers)
517 rewriter.replaceOp(reader, writer.getValue());
541 PatternRewriter &rewriter) {
543 auto portUsers = op.getPort().getUsers();
544 size_t nPortUsers = std::distance(portUsers.begin(), portUsers.end());
545 for (
auto *portUser : op.getPort().getUsers()) {
546 auto writer = dyn_cast<PortWriteOp>(portUser);
547 if (writer && writer.getPort() == op.getPort() && nPortUsers == 1) {
548 rewriter.replaceAllUsesWith(op.getOutput(), writer.getValue());
549 rewriter.eraseOp(writer);
550 rewriter.eraseOp(op);
561 setNameFn(getOutput(),
570 PatternRewriter &rewriter) {
574 auto portUsers = op.getPort().getUsers();
575 size_t nPortUsers = std::distance(portUsers.begin(), portUsers.end());
576 for (
auto *portUser : op.getPort().getUsers()) {
577 auto reader = dyn_cast<PortReadOp>(portUser);
578 if (reader && reader.getPort() == op.getPort() && nPortUsers == 1) {
579 rewriter.replaceOp(reader, op.getInput());
580 rewriter.eraseOp(op);
596 template <
typename TOp>
598 OpAsmParser &parser, OperationState &result,
599 llvm::function_ref<ParseResult(OpAsmParser::Argument &)> argAdjuster = {}) {
601 llvm::SmallVector<OpAsmParser::UnresolvedOperand> inputOperands;
602 llvm::SmallVector<OpAsmParser::Argument> inputArguments;
603 llvm::SmallVector<Type> inputTypes;
604 ArrayAttr inputNames;
606 inputTypes, inputNames))
610 llvm::SmallVector<Type> resultTypes;
611 if (parser.parseOptionalArrowTypeList(resultTypes))
613 result.addTypes(resultTypes);
616 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
620 if (parser.resolveOperands(inputOperands, inputTypes, parser.getNameLoc(),
626 for (
auto &arg : inputArguments)
627 if (failed(argAdjuster(arg)))
632 Region *body = result.addRegion();
633 if (parser.parseRegion(*body, inputArguments))
636 TOp::ensureTerminator(*body, parser.getBuilder(), result.location);
640 template <
typename T>
644 op.getBodyBlock()->getArguments());
645 p.printOptionalArrowTypeList(op.getResultTypes());
646 p.printOptionalAttrDictWithKeyword(op.getOperation()->getAttrs());
648 p.printRegion(op.getBody(),
false);
652 if (getInputs().size() !=
getBodyBlock()->getNumArguments())
653 return emitOpError(
"number of inputs must match number of block arguments");
655 for (
auto [arg, barg] :
656 llvm::zip(getInputs(),
getBodyBlock()->getArguments())) {
657 if (arg.getType() != barg.getType())
658 return emitOpError(
"block argument type must match input type");
664 ParseResult StaticBlockOp::parse(OpAsmParser &parser, OperationState &result) {
665 return parseBlockLikeOp<StaticBlockOp>(parser, result);
668 void StaticBlockOp::print(OpAsmPrinter &p) {
677 if (getInputs().size() !=
getBodyBlock()->getNumArguments())
678 return emitOpError(
"number of inputs must match number of block arguments");
680 for (
auto [arg, barg] :
681 llvm::zip(getInputs(),
getBodyBlock()->getArguments())) {
682 if (arg.getType() != barg.getType())
683 return emitOpError(
"block argument type must match input type");
689 ParseResult IsolatedStaticBlockOp::parse(OpAsmParser &parser,
690 OperationState &result) {
691 return parseBlockLikeOp<IsolatedStaticBlockOp>(parser, result);
694 void IsolatedStaticBlockOp::print(OpAsmPrinter &p) {
702 void DCBlockOp::build(OpBuilder &odsBuilder, OperationState &odsState,
703 TypeRange outputs, ValueRange inputs,
704 IntegerAttr maxThreads) {
705 odsState.addOperands(inputs);
707 odsState.addAttribute(getMaxThreadsAttrName(odsState.name), maxThreads);
708 auto *region = odsState.addRegion();
709 llvm::SmallVector<Type> resTypes;
710 for (
auto output : outputs) {
711 dc::ValueType dcType = dyn_cast<dc::ValueType>(output);
712 assert(dcType &&
"DCBlockOp outputs must be dc::ValueType");
713 resTypes.push_back(dcType);
715 odsState.addTypes(resTypes);
716 ensureTerminator(*region, odsBuilder, odsState.location);
717 llvm::SmallVector<Location> argLocs;
718 llvm::SmallVector<Type> argTypes;
719 for (
auto input : inputs) {
720 argLocs.push_back(input.getLoc());
721 dc::ValueType dcType = dyn_cast<dc::ValueType>(input.getType());
722 assert(dcType &&
"DCBlockOp inputs must be dc::ValueType");
723 argTypes.push_back(dcType.getInnerType());
725 region->front().addArguments(argTypes, argLocs);
729 if (getInputs().size() !=
getBodyBlock()->getNumArguments())
730 return emitOpError(
"number of inputs must match number of block arguments");
732 for (
auto [arg, barg] :
733 llvm::zip(getInputs(),
getBodyBlock()->getArguments())) {
734 dc::ValueType dcType = dyn_cast<dc::ValueType>(arg.getType());
736 return emitOpError(
"DCBlockOp inputs must be dc::ValueType but got ")
739 if (dcType.getInnerType() != barg.getType())
740 return emitOpError(
"block argument type must match input type. Got ")
741 << barg.getType() <<
" expected " << dcType.getInnerType();
747 ParseResult DCBlockOp::parse(OpAsmParser &parser, OperationState &result) {
748 return parseBlockLikeOp<DCBlockOp>(
749 parser, result, [&](OpAsmParser::Argument &arg) -> LogicalResult {
750 dc::ValueType valueType = dyn_cast<dc::ValueType>(arg.type);
752 return parser.emitError(parser.getCurrentLocation(),
753 "DCBlockOp inputs must be dc::ValueType");
754 arg.type = valueType.getInnerType();
759 void DCBlockOp::print(OpAsmPrinter &p) {
return printBlockLikeOp(*
this, p); }
766 Operation *parent = getOperation()->getParentOp();
767 auto parentBlock = dyn_cast<BlockOpInterface>(parent);
769 return emitOpError(
"must be nested in a block");
771 if (getNumOperands() != parent->getNumResults())
772 return emitOpError(
"number of operands must match number of block outputs");
774 for (
auto [op, out] :
775 llvm::zip(getOperands(), parentBlock.getInternalResultTypes())) {
776 if (op.getType() != out)
778 "operand type must match parent block output type. Expected ")
779 << out <<
" got " << op.getType();
789 InlineStaticBlockBeginOp InlineStaticBlockEndOp::getBeginOp() {
790 auto curr = getOperation()->getReverseIterator();
791 Operation *firstOp = &getOperation()->getBlock()->front();
793 if (
auto beginOp = dyn_cast<InlineStaticBlockBeginOp>(*curr))
795 if (curr.getNodePtr() == firstOp)
806 InlineStaticBlockEndOp InlineStaticBlockBeginOp::getEndOp() {
807 auto curr = getOperation()->getIterator();
808 auto end = getOperation()->getBlock()->end();
809 while (curr != end) {
810 if (
auto endOp = dyn_cast<InlineStaticBlockEndOp>(*curr))
822 #include "circt/Dialect/Kanagawa/KanagawaInterfaces.cpp.inc"
825 #define GET_OP_CLASSES
826 #include "circt/Dialect/Kanagawa/Kanagawa.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={})
static Block * getBodyBlock(FModuleLike mod)
static LogicalResult canonicalize(Op op, PatternRewriter &rewriter)
static LogicalResult verify(Value clock, bool eventExists, mlir::Location loc)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
void getAsmResultNames(OpAsmSetValueNameFn setNameFn, StringRef instanceName, ArrayAttr resultNames, ValueRange results)
Suggest a name for each result value based on the saved result names attribute.
mlir::FailureOr< mlir::TypedValue< ScopeRefType > > getThisFromScope(Operation *op)
LogicalResult verifyScopeOpInterface(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