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"
22using namespace kanagawa;
24template <
typename TSymAttr>
28 scopeRefType = ScopeRefType::get(parser.getContext(), sym);
32template <
typename TSymAttr>
41static 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<InstanceOp, ContainerInstanceOp>(
47 [&](
auto op) { os << op.getInstanceNameAttr().strref(); })
48 .Case<PortOpInterface>([&](
auto op) { os << op.getNameHint(); })
49 .Case<PathOp>([&](
auto op) {
51 op.getPathAsRange(), os,
52 [&](PathStepAttr step) {
53 if (step.getDirection() == PathDirection::Parent)
56 os << step.getChild().getAttr().strref();
60 .Case<GetPortOp>([&](
auto op) {
62 <<
"." << op.getPortSymbol() <<
".ref";
65 [&](
auto op) {
genValueName(os, op.getPort()) <<
".val"; })
66 .Default([&](
auto op) {
67 op->emitOpError() <<
"unhandled value type";
68 assert(
false &&
"unhandled value type");
77 llvm::raw_string_ostream os(s);
79 return StringAttr::get(v.getContext(), s);
87 if (!isa<hw::InnerSymbolOpInterface>(op))
88 return op->emitOpError(
"must implement 'InnerSymbolOpInterface'");
97template <
typename TOp>
101 if (parser.parseSymbolName(nameAttr))
105 hw::InnerSymAttr::get(nameAttr));
108 SmallVector<OpAsmParser::Argument, 4> args;
109 SmallVector<Attribute> argNames;
110 SmallVector<Type> resultTypes;
111 TypeAttr functionType;
113 using namespace mlir::function_interface_impl;
114 auto *context = parser.getContext();
117 if (parser.parseArgumentList(args, OpAsmParser::Delimiter::Paren,
122 if (parser.parseOptionalArrowTypeList(resultTypes))
126 SmallVector<Type> argTypes;
127 for (
auto &arg : args) {
129 argTypes.push_back(arg.type);
131 arg.sourceLoc = parser.getEncodedSourceLoc(arg.ssaName.location);
135 TypeAttr::get(FunctionType::get(context, argTypes, resultTypes));
138 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
141 result.addAttribute(
"argNames", ArrayAttr::get(context, argNames));
142 result.addAttribute(TOp::getFunctionTypeAttrName(result.name), functionType);
145 auto *body = result.addRegion();
146 if (parser.parseRegion(*body, args))
152template <
typename TOp>
154 FunctionType funcTy = op.getFunctionType();
156 p.printSymbolName(op.getInnerSym().getSymName());
157 Region &body = op.getBody();
159 llvm::interleaveComma(body.getArguments(), p,
160 [&](BlockArgument arg) { p.printRegionArgument(arg); });
162 p.printArrowTypeList(funcTy.getResults());
163 p.printOptionalAttrDictWithKeyword(op.getOperation()->getAttrs(),
164 op.getAttributeNames());
167 p.printRegion(body,
false,
172ParseResult MethodOp::parse(OpAsmParser &parser, OperationState &result) {
173 return parseMethodLikeOp<MethodOp>(parser, result);
178void MethodOp::getAsmBlockArgumentNames(mlir::Region ®ion,
183 auto func = cast<MethodOp>(region.getParentOp());
184 auto argNames = func.getArgNames().getAsRange<StringAttr>();
185 auto *block = ®ion.front();
187 for (
auto [idx, argName] :
llvm::enumerate(argNames))
188 if (!argName.getValue().
empty())
189 setNameFn(block->getArgument(idx), argName);
196ParseResult DataflowMethodOp::parse(OpAsmParser &parser,
197 OperationState &result) {
198 return parseMethodLikeOp<DataflowMethodOp>(parser, result);
201void DataflowMethodOp::print(OpAsmPrinter &p) {
209void ReturnOp::build(OpBuilder &odsBuilder, OperationState &odsState) {}
211LogicalResult ReturnOp::verify() {
213 auto methodLike = cast<MethodLikeOpInterface>((*this)->getParentOp());
214 ArrayRef<Type> resTypes = methodLike.getResultTypes();
216 if (getNumOperands() != resTypes.size())
218 "must have the same number of operands as the method has results");
220 for (
auto [arg, resType] :
llvm::zip(getOperands(), resTypes))
221 if (arg.getType() != resType)
222 return emitOpError(
"operand type (")
223 << arg.getType() <<
") must match function return type ("
234 auto varOp = getVar(ns);
239 if (varOp.getType() != getType())
240 return emitOpError() <<
"dereferenced type (" << getType()
241 <<
") must match variable type (" << varOp.getType()
248 ScopeRefType parentType = cast<ScopeRefType>(getInstance().getType());
249 auto scopeRefOp = ns.
lookupOp<ScopeOpInterface>(parentType.getScopeRef());
254 return dyn_cast_or_null<VarOp>(scopeRefOp.lookupInnerSym(getVarName()));
263 return emitOpError() <<
"'" << getTargetName() <<
"' does not exist";
279 return emitOpError() <<
"port '@" << getPortSymbol()
280 <<
"' does not exist in @"
281 << cast<ScopeRefType>(getInstance().getType())
286 Type targetPortType = portOp.getPortType();
287 Type thisPortType = getType().getPortType();
288 if (targetPortType != thisPortType)
289 return emitOpError() <<
"symbol '" << getPortSymbolAttr()
290 <<
"' refers to a port of type " << targetPortType
291 <<
", but this op has type " << thisPortType;
298 auto targetScope = ns.
lookupOp<ScopeOpInterface>(
299 cast<ScopeRefType>(getInstance().getType()).getScopeRef());
304 return dyn_cast_or_null<PortOpInterface>(
305 targetScope.lookupInnerSym(getPortSymbol()));
325 auto targetContainer = getContainer(ns);
326 if (!targetContainer)
327 return emitOpError() <<
"'" << getTargetName() <<
"' does not exist";
341 return ns.
lookupOp<MethodOp>(getCallee());
346 return emitOpError() <<
"'" << getCallee() <<
"' does not exist";
356LogicalResult PathOp::inferReturnTypes(
357 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
358 DictionaryAttr attrs, mlir::OpaqueProperties properties,
359 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
360 Adaptor adaptor(operands, attrs, properties, regions);
361 auto path = adaptor.getPathAttr();
365 auto lastStep = cast<PathStepAttr>(path.getValue().back());
366 results.push_back(lastStep.getType());
370LogicalResult PathStepAttr::verify(function_ref<InFlightDiagnostic()> emitError,
371 PathDirection direction, mlir::Type type,
372 mlir::FlatSymbolRefAttr instance) {
374 if (direction == PathDirection::Parent && instance)
376 <<
"kanagawa.step 'parent' may not specify an instance name";
378 if (direction == PathDirection::Child && !instance)
379 return emitError() <<
"kanagawa.step 'child' must specify an instance name";
382 auto scoperefType = llvm::dyn_cast<ScopeRefType>(type);
385 <<
"kanagawa.step type must be an !kanagawa.scoperef type";
391 auto pathRange = getPathAsRange();
392 if (pathRange.empty())
393 return emitOpError() <<
"kanagawa.path must have at least one step";
397 for (PathStepAttr step : getPathAsRange()) {
398 auto scoperefType = cast<ScopeRefType>(step.getType());
399 hw::InnerRefAttr scopeRefSym = scoperefType.getScopeRef();
403 auto *targetScope = ns.
lookupOp(scopeRefSym);
405 return emitOpError() <<
"kanagawa.step scoperef symbol '@"
406 << scopeRefSym.getName().getValue()
407 <<
"' does not exist";
411 PathStepAttr lastStep = *std::prev(getPathAsRange().
end());
412 ScopeRefType lastStepType = cast<ScopeRefType>(lastStep.getType());
413 if (!lastStepType.getScopeRef())
414 return emitOpError() <<
"last kanagawa.step in path must specify a symbol "
420LogicalResult PathOp::canonicalize(PathOp op, PatternRewriter &rewriter) {
423 auto range = op.getPathAsRange();
424 size_t pathSize = std::distance(range.begin(), range.end());
425 PathStepAttr firstStep = *range.begin();
426 if (pathSize == 1 && firstStep.getDirection() == PathDirection::Child) {
427 auto parentScope = cast<ScopeOpInterface>(op->getParentOp());
428 auto childInstance = dyn_cast_or_null<ContainerInstanceOp>(
429 parentScope.lookupInnerSym(firstStep.getChild().getValue()));
430 assert(childInstance &&
"should have been verified by the op verifier");
431 rewriter.replaceOp(op, {childInstance.getResult()});
446LogicalResult OutputPortOp::canonicalize(OutputPortOp op,
447 PatternRewriter &rewriter) {
451 llvm::SmallVector<PortReadOp, 4> readers;
452 for (
auto *user : op.getResult().getUsers()) {
453 if (
auto read = dyn_cast<PortReadOp>(user)) {
454 readers.push_back(read);
455 }
else if (
auto write = dyn_cast<PortWriteOp>(user);
456 write && write.getPort() == op.getPort()) {
457 assert(!writer &&
"should only have one writer");
462 if (!readers.empty()) {
463 for (
auto reader : readers)
464 rewriter.replaceOp(reader, writer.getValue());
487LogicalResult InputWireOp::canonicalize(InputWireOp op,
488 PatternRewriter &rewriter) {
490 auto portUsers = op.getPort().getUsers();
491 size_t nPortUsers = std::distance(portUsers.begin(), portUsers.end());
492 for (
auto *portUser : op.
getPort().getUsers()) {
493 auto writer = dyn_cast<PortWriteOp>(portUser);
494 if (writer && writer.getPort() == op.getPort() && nPortUsers == 1) {
495 rewriter.replaceAllUsesWith(op.getOutput(), writer.getValue());
496 rewriter.eraseOp(writer);
497 rewriter.eraseOp(op);
508 setNameFn(getOutput(),
509 StringAttr::get(getContext(), name.strref() +
".out").strref());
516LogicalResult OutputWireOp::canonicalize(OutputWireOp op,
517 PatternRewriter &rewriter) {
521 auto portUsers = op.getPort().getUsers();
522 size_t nPortUsers = std::distance(portUsers.begin(), portUsers.end());
523 for (
auto *portUser : op.
getPort().getUsers()) {
524 auto reader = dyn_cast<PortReadOp>(portUser);
525 if (reader && reader.getPort() == op.getPort() && nPortUsers == 1) {
526 rewriter.replaceOp(reader, op.getInput());
527 rewriter.eraseOp(op);
543template <
typename TOp>
545 OpAsmParser &parser, OperationState &result,
546 llvm::function_ref<ParseResult(OpAsmParser::Argument &)> argAdjuster = {}) {
548 llvm::SmallVector<OpAsmParser::UnresolvedOperand> inputOperands;
549 llvm::SmallVector<OpAsmParser::Argument> inputArguments;
550 llvm::SmallVector<Type> inputTypes;
551 ArrayAttr inputNames;
553 inputTypes, inputNames))
557 llvm::SmallVector<Type> resultTypes;
558 if (parser.parseOptionalArrowTypeList(resultTypes))
560 result.addTypes(resultTypes);
563 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
567 if (parser.resolveOperands(inputOperands, inputTypes, parser.getNameLoc(),
573 for (
auto &arg : inputArguments)
574 if (failed(argAdjuster(arg)))
579 Region *body = result.addRegion();
580 if (parser.parseRegion(*body, inputArguments))
583 TOp::ensureTerminator(*body, parser.getBuilder(), result.location);
591 op.getBodyBlock()->getArguments());
592 p.printOptionalArrowTypeList(op.getResultTypes());
593 p.printOptionalAttrDictWithKeyword(op.getOperation()->getAttrs());
595 p.printRegion(op.getBody(),
false);
598LogicalResult StaticBlockOp::verify() {
599 if (getInputs().size() !=
getBodyBlock()->getNumArguments())
600 return emitOpError(
"number of inputs must match number of block arguments");
602 for (
auto [arg, barg] :
604 if (arg.getType() != barg.getType())
605 return emitOpError(
"block argument type must match input type");
611ParseResult StaticBlockOp::parse(OpAsmParser &parser, OperationState &result) {
612 return parseBlockLikeOp<StaticBlockOp>(parser, result);
615void StaticBlockOp::print(OpAsmPrinter &p) {
623LogicalResult IsolatedStaticBlockOp::verify() {
624 if (getInputs().size() !=
getBodyBlock()->getNumArguments())
625 return emitOpError(
"number of inputs must match number of block arguments");
627 for (
auto [arg, barg] :
629 if (arg.getType() != barg.getType())
630 return emitOpError(
"block argument type must match input type");
636ParseResult IsolatedStaticBlockOp::parse(OpAsmParser &parser,
637 OperationState &result) {
638 return parseBlockLikeOp<IsolatedStaticBlockOp>(parser, result);
641void IsolatedStaticBlockOp::print(OpAsmPrinter &p) {
649void DCBlockOp::build(OpBuilder &odsBuilder, OperationState &odsState,
650 TypeRange outputs, ValueRange inputs,
651 IntegerAttr maxThreads) {
652 odsState.addOperands(inputs);
654 odsState.addAttribute(getMaxThreadsAttrName(odsState.name), maxThreads);
655 auto *region = odsState.addRegion();
656 llvm::SmallVector<Type> resTypes;
657 for (
auto output : outputs) {
658 dc::ValueType dcType = dyn_cast<dc::ValueType>(output);
659 assert(dcType &&
"DCBlockOp outputs must be dc::ValueType");
660 resTypes.push_back(dcType);
662 odsState.addTypes(resTypes);
663 ensureTerminator(*region, odsBuilder, odsState.location);
664 llvm::SmallVector<Location> argLocs;
665 llvm::SmallVector<Type> argTypes;
666 for (
auto input : inputs) {
667 argLocs.push_back(input.getLoc());
668 dc::ValueType dcType = dyn_cast<dc::ValueType>(input.getType());
669 assert(dcType &&
"DCBlockOp inputs must be dc::ValueType");
670 argTypes.push_back(dcType.getInnerType());
672 region->front().addArguments(argTypes, argLocs);
675LogicalResult DCBlockOp::verify() {
676 if (getInputs().size() !=
getBodyBlock()->getNumArguments())
677 return emitOpError(
"number of inputs must match number of block arguments");
679 for (
auto [arg, barg] :
681 dc::ValueType dcType = dyn_cast<dc::ValueType>(arg.getType());
683 return emitOpError(
"DCBlockOp inputs must be dc::ValueType but got ")
686 if (dcType.getInnerType() != barg.getType())
687 return emitOpError(
"block argument type must match input type. Got ")
688 << barg.getType() <<
" expected " << dcType.getInnerType();
694ParseResult DCBlockOp::parse(OpAsmParser &parser, OperationState &result) {
695 return parseBlockLikeOp<DCBlockOp>(
696 parser, result, [&](OpAsmParser::Argument &arg) -> LogicalResult {
697 dc::ValueType valueType = dyn_cast<dc::ValueType>(arg.type);
699 return parser.emitError(parser.getCurrentLocation(),
700 "DCBlockOp inputs must be dc::ValueType");
701 arg.type = valueType.getInnerType();
706void DCBlockOp::print(OpAsmPrinter &p) {
return printBlockLikeOp(*
this, p); }
712LogicalResult BlockReturnOp::verify() {
713 Operation *parent = getOperation()->getParentOp();
714 auto parentBlock = dyn_cast<BlockOpInterface>(parent);
716 return emitOpError(
"must be nested in a block");
718 if (getNumOperands() != parent->getNumResults())
719 return emitOpError(
"number of operands must match number of block outputs");
721 for (
auto [op, out] :
722 llvm::zip(getOperands(), parentBlock.getInternalResultTypes())) {
723 if (op.getType() != out)
725 "operand type must match parent block output type. Expected ")
726 << out <<
" got " << op.getType();
736InlineStaticBlockBeginOp InlineStaticBlockEndOp::getBeginOp() {
737 auto curr = getOperation()->getReverseIterator();
738 Operation *firstOp = &getOperation()->getBlock()->front();
740 if (
auto beginOp = dyn_cast<InlineStaticBlockBeginOp>(*curr))
742 if (curr.getNodePtr() == firstOp)
753InlineStaticBlockEndOp InlineStaticBlockBeginOp::getEndOp() {
754 auto curr = getOperation()->getIterator();
755 auto end = getOperation()->getBlock()->end();
756 while (curr != end) {
757 if (
auto endOp = dyn_cast<InlineStaticBlockEndOp>(*curr))
769#include "circt/Dialect/Kanagawa/KanagawaInterfaces.cpp.inc"
772#define GET_OP_CLASSES
773#include "circt/Dialect/Kanagawa/Kanagawa.cpp.inc"
assert(baseType &&"element must be base type")
static PortInfo getPort(ModuleTy &mod, size_t idx)
static InstancePath empty
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 StringRef getInnerSymbolAttrName()
Return the name of the attribute used for inner symbol names.
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
This class represents the namespace in which InnerRef's can be resolved.
Operation * lookupOp(hw::InnerRefAttr inner) const
Resolve the InnerRef to its target within this namespace, returning empty target if no such name exis...