20 #include "mlir/IR/Builders.h"
21 #include "mlir/IR/BuiltinTypes.h"
22 #include "mlir/IR/PatternMatch.h"
23 #include "mlir/IR/SymbolTable.h"
25 using namespace circt;
32 ParseResult ChannelBufferOp::parse(OpAsmParser &parser,
33 OperationState &result) {
34 llvm::SMLoc inputOperandsLoc = parser.getCurrentLocation();
36 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
37 if (parser.parseOperandList(operands, 3,
38 OpAsmParser::Delimiter::None))
42 if (parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
43 parser.parseType(innerOutputType))
47 result.addTypes({outputType});
51 if (parser.resolveOperands(operands, {clkTy, i1, outputType},
52 inputOperandsLoc, result.operands))
57 void ChannelBufferOp::print(OpAsmPrinter &p) {
58 p <<
" " << getClk() <<
", " << getRst() <<
", " << getInput();
59 p.printOptionalAttrDict((*this)->getAttrs());
63 circt::esi::ChannelType ChannelBufferOp::channelType() {
64 return cast<circt::esi::ChannelType>(getInput().getType());
68 if (getInput().getType().getSignaling() != ChannelSignaling::ValidReady)
69 return emitOpError(
"currently only supports valid-ready signaling");
70 if (getOutput().getType().getDataDelay() != 0)
71 return emitOpError(
"currently only supports channels with zero data delay");
80 if (getOutput().getType().getInner() != getInput().getType().getInner())
81 return emitOpError(
"input and output types must match");
89 ParseResult PipelineStageOp::parse(OpAsmParser &parser,
90 OperationState &result) {
91 llvm::SMLoc inputOperandsLoc = parser.getCurrentLocation();
93 SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
95 if (parser.parseOperandList(operands, 3) ||
96 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
97 parser.parseType(innerOutputType))
101 result.addTypes({type});
105 if (parser.resolveOperands(operands, {clkTy, i1, type}, inputOperandsLoc,
111 void PipelineStageOp::print(OpAsmPrinter &p) {
112 p <<
" " << getClk() <<
", " << getRst() <<
", " << getInput();
113 p.printOptionalAttrDict((*this)->getAttrs());
117 circt::esi::ChannelType PipelineStageOp::channelType() {
118 return cast<circt::esi::ChannelType>(getInput().getType());
125 ParseResult WrapValidReadyOp::parse(OpAsmParser &parser,
126 OperationState &result) {
127 llvm::SMLoc inputOperandsLoc = parser.getCurrentLocation();
129 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 2> opList;
130 Type innerOutputType;
131 if (parser.parseOperandList(opList, 2, OpAsmParser::Delimiter::None) ||
132 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
133 parser.parseType(innerOutputType))
136 auto boolType = parser.getBuilder().getI1Type();
139 result.addTypes({outputType, boolType});
140 if (parser.resolveOperands(opList, {innerOutputType, boolType},
141 inputOperandsLoc, result.operands))
146 void WrapValidReadyOp::print(OpAsmPrinter &p) {
147 p <<
" " << getRawInput() <<
", " << getValid();
148 p.printOptionalAttrDict((*this)->getAttrs());
152 void WrapValidReadyOp::build(OpBuilder &b, OperationState &state, Value data,
155 b.getI1Type(), data, valid);
159 mlir::TypedValue<ChannelType> chanOut = getChanOutput();
160 if (chanOut.getType().getSignaling() != ChannelSignaling::ValidReady)
161 return emitOpError(
"only supports valid-ready signaling");
162 if (!chanOut.hasOneUse() && !chanOut.getUses().empty()) {
163 llvm::errs() <<
"chanOut: " << chanOut.getLoc() <<
"\n";
164 return emitOpError(
"only supports zero or one use");
169 ParseResult UnwrapValidReadyOp::parse(OpAsmParser &parser,
170 OperationState &result) {
171 llvm::SMLoc inputOperandsLoc = parser.getCurrentLocation();
173 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 2> opList;
175 if (parser.parseOperandList(opList, 2, OpAsmParser::Delimiter::None) ||
176 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
177 parser.parseType(outputType))
183 auto boolType = parser.getBuilder().getI1Type();
185 result.addTypes({inputType.getInner(), boolType});
186 if (parser.resolveOperands(opList, {inputType, boolType}, inputOperandsLoc,
192 void UnwrapValidReadyOp::print(OpAsmPrinter &p) {
193 p <<
" " << getChanInput() <<
", " << getReady();
194 p.printOptionalAttrDict((*this)->getAttrs());
195 p <<
" : " << getRawOutput().getType();
199 if (getChanInput().getType().getSignaling() != ChannelSignaling::ValidReady)
200 return emitOpError(
"only supports valid-ready signaling");
204 circt::esi::ChannelType WrapValidReadyOp::channelType() {
205 return cast<circt::esi::ChannelType>(getChanOutput().getType());
208 void UnwrapValidReadyOp::build(OpBuilder &b, OperationState &state,
209 Value inChan, Value ready) {
210 auto inChanType = cast<ChannelType>(inChan.getType());
211 build(b, state, inChanType.getInner(), b.getI1Type(), inChan, ready);
214 circt::esi::ChannelType UnwrapValidReadyOp::channelType() {
215 return cast<circt::esi::ChannelType>(getChanInput().getType());
218 circt::esi::ChannelType WrapFIFOOp::channelType() {
219 return cast<circt::esi::ChannelType>(getChanOutput().getType());
223 Type &chanInputType) {
224 auto loc = p.getCurrentLocation();
226 if (p.parseType(chType))
228 if (chType.getSignaling() != ChannelSignaling::FIFO)
229 return p.emitError(loc,
"can only wrap into FIFO type");
230 dataType = chType.getInner();
231 chanInputType = chType;
241 if (getChanOutput().getType().getSignaling() != ChannelSignaling::FIFO)
242 return emitOpError(
"only supports FIFO signaling");
246 circt::esi::ChannelType UnwrapFIFOOp::channelType() {
247 return cast<circt::esi::ChannelType>(getChanInput().getType());
251 if (getChanInput().getType().getSignaling() != ChannelSignaling::FIFO)
252 return emitOpError(
"only supports FIFO signaling");
257 UnwrapFIFOOp::inferReturnTypes(MLIRContext *context, std::optional<Location>,
258 ValueRange operands, DictionaryAttr,
259 mlir::OpaqueProperties, mlir::RegionRange,
260 SmallVectorImpl<Type> &inferredResulTypes) {
261 inferredResulTypes.push_back(
262 cast<ChannelType>(operands[0].getType()).getInner());
263 inferredResulTypes.push_back(
271 if (!iface.lookupSymbol<InterfaceSignalOp>(
"valid"))
273 if (!iface.lookupSymbol<InterfaceSignalOp>(
"ready"))
275 auto dataSig = iface.lookupSymbol<InterfaceSignalOp>(
"data");
278 return dataSig.getType();
285 circt::sv::ModportType modportType,
286 ChannelType chanType) {
288 SymbolTable::lookupNearestSymbolFrom<circt::sv::InterfaceModportOp>(
289 op, modportType.getModport());
291 return op->emitError(
"Could not find modport ")
292 << modportType.getModport() <<
" in symbol table.";
293 auto iface = cast<circt::sv::InterfaceOp>(modport->getParentOp());
296 return op->emitOpError(
"Interface is not a valid ESI interface.");
297 if (esiDataType != chanType.getInner())
298 return op->emitOpError(
"Operation specifies ")
299 << chanType <<
" but type inside doesn't match interface data type "
300 << esiDataType <<
".";
305 auto modportType = cast<circt::sv::ModportType>(getInterfaceSink().getType());
306 auto chanType = cast<ChannelType>(getOutput().getType());
310 circt::esi::ChannelType WrapSVInterfaceOp::channelType() {
311 return cast<circt::esi::ChannelType>(getOutput().getType());
316 cast<circt::sv::ModportType>(getInterfaceSource().getType());
317 auto chanType = cast<ChannelType>(getChanInput().getType());
321 circt::esi::ChannelType UnwrapSVInterfaceOp::channelType() {
322 return cast<circt::esi::ChannelType>(getChanInput().getType());
326 hw::UnionType expectedInput = getWindow().getType().getLoweredType();
327 if (expectedInput == getFrame().getType())
329 return emitOpError(
"Expected input type is ") << expectedInput;
333 UnwrapWindow::inferReturnTypes(MLIRContext *, std::optional<Location>,
334 ValueRange operands, DictionaryAttr,
335 mlir::OpaqueProperties, mlir::RegionRange,
336 SmallVectorImpl<Type> &inferredReturnTypes) {
337 auto windowType = cast<WindowType>(operands.front().getType());
338 inferredReturnTypes.push_back(windowType.getLoweredType());
345 if (p.parseType(window))
348 frame = window.getLoweredType();
362 static FailureOr<ServiceDeclOpInterface>
364 hw::InnerRefAttr servicePort) {
365 ModuleOp top = op->getParentOfType<mlir::ModuleOp>();
366 SymbolTable &topSyms = symbolTable.getSymbolTable(top);
368 StringAttr modName = servicePort.getModule();
369 auto serviceDecl = topSyms.lookup<ServiceDeclOpInterface>(modName);
371 return op->emitOpError(
"Could not find service declaration ")
372 << servicePort.getModuleRef();
377 static FailureOr<ServicePortInfo>
379 hw::InnerRefAttr servicePort) {
381 if (failed(serviceDecl))
383 auto portInfo = serviceDecl->getPortInfo(servicePort.getName());
384 if (failed(portInfo))
385 return op->emitOpError(
"Could not locate port ") << servicePort.getName();
394 if (expected == actual)
398 return TypeSwitch<Type, LogicalResult>(expected)
400 .Case<AnyType>([&](Type) {
return success(); })
402 .Case<ChannelType>([&](ChannelType expectedChannel) {
403 auto actualChannel = dyn_cast<ChannelType>(actual);
407 actualChannel.getInner());
410 .Case<hw::StructType>([&](hw::StructType expectedStruct) {
411 auto actualStruct = dyn_cast<hw::StructType>(actual);
414 auto expectedFields = expectedStruct.getElements();
415 auto actualFields = actualStruct.getElements();
416 if (expectedFields.size() != actualFields.size())
418 for (
auto [efield, afield] : llvm::zip(expectedFields, actualFields)) {
419 if (efield.name != afield.name)
427 .Case<hw::ArrayType>([&](hw::ArrayType expectedArray) {
428 auto actualArray = dyn_cast<hw::ArrayType>(actual);
431 if (expectedArray.getNumElements() != actualArray.getNumElements())
434 actualArray.getElementType());
437 .Case<hw::UnionType>([&](hw::UnionType expectedUnion) {
438 auto actualUnion = dyn_cast<hw::UnionType>(actual);
441 auto expectedElements = expectedUnion.getElements();
442 auto actualElements = actualUnion.getElements();
443 if (expectedElements.size() != actualElements.size())
445 for (
auto [efield, afield] :
446 llvm::zip(expectedElements, actualElements)) {
447 if (efield.name != afield.name)
449 if (efield.offset != afield.offset)
457 .Case<ListType>([&](ListType expectedList) {
458 auto actualList = dyn_cast<ListType>(actual);
462 actualList.getElementType());
465 .Case<WindowType>([&](WindowType expectedWindow) {
466 auto actualWindow = dyn_cast<WindowType>(actual);
470 actualWindow.getInto());
473 .Case<hw::TypeAliasType>([&](hw::TypeAliasType expectedAlias) {
474 auto actualAlias = dyn_cast<hw::TypeAliasType>(actual);
478 actualAlias.getCanonicalType());
481 .Default([&](Type) {
return failure(); });
487 ChannelBundleType svcBundleType,
488 ChannelBundleType reqBundleType,
489 bool skipDirectionCheck) {
490 if (svcBundleType.getChannels().size() != reqBundleType.getChannels().size())
491 return req->emitOpError(
492 "Request port bundle channel count does not match service "
493 "port bundle channel count");
496 DenseMap<StringAttr, BundledChannel> declBundleChannels;
498 declBundleChannels[bc.name] = bc;
502 auto f = declBundleChannels.find(bc.name);
503 if (f == declBundleChannels.end())
504 return req->emitOpError(
505 "Request channel name not found in service port bundle");
506 if (!skipDirectionCheck && f->second.direction != bc.direction)
507 return req->emitOpError(
508 "Request channel direction does not match service "
509 "port bundle channel direction");
512 return req->emitOpError(
513 "Request channel type does not match service port "
514 "bundle channel type")
516 <<
"Service port '" << bc.name.getValue()
517 <<
"' type: " << f->second.type;
526 RequestConnectionOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
534 LogicalResult ServiceImplementConnReqOp::verifySymbolUses(
535 SymbolTableCollection &symbolTable) {
544 for (
auto toClient : getOps<ServiceDeclPortOp>())
547 toClient.getToClientType()});
556 SmallVectorImpl<Type> &toChannelTypes,
557 SmallVectorImpl<Type> &fromChannelTypes, Type &type) {
559 ChannelBundleType bundleType;
560 if (parser.parseType(bundleType))
565 if (ch.direction == ChannelDirection::to)
566 toChannelTypes.push_back(ch.type);
567 else if (ch.direction == ChannelDirection::from)
568 fromChannelTypes.push_back(ch.type);
570 assert(
false &&
"Channel direction invalid");
573 template <
typename T3,
typename T4>
576 p.printType(bundleType);
578 void UnpackBundleOp::build(::mlir::OpBuilder &odsBuilder,
579 ::mlir::OperationState &odsState, Value bundle,
580 mlir::ValueRange fromChannels) {
582 cast<ChannelBundleType>(bundle.getType()).getChannels())
583 if (ch.direction == ChannelDirection::to)
584 odsState.addTypes(ch.type);
585 odsState.addOperands(bundle);
586 odsState.addOperands(fromChannels);
590 if (!getBundle().hasOneUse())
591 return emitOpError(
"bundles must have exactly one user");
594 void PackBundleOp::build(::mlir::OpBuilder &odsBuilder,
595 ::mlir::OperationState &odsState,
596 ChannelBundleType bundleType,
597 mlir::ValueRange toChannels) {
598 odsState.addTypes(bundleType);
599 for (
BundledChannel ch : cast<ChannelBundleType>(bundleType).getChannels())
600 if (ch.direction == ChannelDirection::from)
601 odsState.addTypes(ch.type);
602 odsState.addOperands(toChannels);
606 if (!getBundle().hasOneUse())
607 return emitOpError(
"bundles must have exactly one user");
612 if (getNumResults() == 0)
614 setNameFn(getResult(0),
"bundle");
615 for (
auto [idx, from] : llvm::enumerate(llvm::make_filter_range(
617 return ch.
direction == ChannelDirection::from;
619 if (idx + 1 < getNumResults())
620 setNameFn(getResult(idx + 1), from.name.getValue());
624 for (
auto [idx, to] : llvm::enumerate(llvm::make_filter_range(
626 return ch.
direction == ChannelDirection::to;
628 if (idx < getNumResults())
629 setNameFn(getResult(idx), to.name.getValue());
636 ESIDialect *esiDialect = getContext()->getLoadedDialect<ESIDialect>();
637 Block &body = getBody().front();
638 auto channelOrOutput = [](Value v) {
639 if (isa<ChannelType, ChannelBundleType>(v.getType()))
641 if (v.getUsers().empty())
643 return llvm::all_of(v.getUsers(), [](Operation *op) {
644 return isa<ESIPureModuleOutputOp>(op);
648 DenseMap<StringAttr, std::tuple<hw::ModulePort::Direction, Type, Operation *>>
650 for (Operation &op : body.getOperations()) {
651 if (igraph::InstanceOpInterface inst =
652 dyn_cast<igraph::InstanceOpInterface>(op)) {
653 if (llvm::any_of(op.getOperands(), [](Value v) {
654 return !(isa<ChannelType, ChannelBundleType>(v.getType()) ||
655 isa<ESIPureModuleInputOp>(v.getDefiningOp()));
657 return inst.emitOpError(
658 "instances in ESI pure modules can only contain channel ports or "
659 "ports driven by 'input' ops");
660 if (!llvm::all_of(op.getResults(), channelOrOutput))
661 return inst.emitOpError(
662 "instances in ESI pure modules can only contain channel ports or "
663 "drive only 'outputs'");
666 if (op.getDialect() != esiDialect)
667 return op.emitOpError(
"operation not allowed in ESI pure modules");
671 if (
auto port = dyn_cast<ESIPureModuleInputOp>(op)) {
672 auto existing = ports.find(port.getNameAttr());
673 Type portType = port.getResult().getType();
674 if (existing != ports.end()) {
675 auto [dir, type, op] = existing->getSecond();
677 return (port.emitOpError(
"port '")
678 << port.getName() <<
"' previously declared as type " << type)
679 .attachNote(op->getLoc());
681 ports[port.getNameAttr()] = std::make_tuple(
683 }
else if (
auto port = dyn_cast<ESIPureModuleOutputOp>(op)) {
684 auto existing = ports.find(port.getNameAttr());
685 if (existing != ports.end())
686 return (port.emitOpError(
"port '")
687 << port.getName() <<
"' previously declared")
688 .attachNote(std::get<2>(existing->getSecond())->getLoc());
689 ports[port.getNameAttr()] =
691 port.getValue().getType(), port.getOperation());
697 hw::ModuleType ESIPureModuleOp::getHWModuleType() {
703 ::llvm::report_fatal_error(
"not supported");
707 size_t ESIPureModuleOp::getNumInputPorts() {
return 0; }
708 size_t ESIPureModuleOp::getNumOutputPorts() {
return 0; }
709 size_t ESIPureModuleOp::getPortIdForInputId(
size_t) {
710 assert(0 &&
"Out of bounds input port id");
713 size_t ESIPureModuleOp::getPortIdForOutputId(
size_t) {
714 assert(0 &&
"Out of bounds output port id");
719 SmallVector<Location> retval;
723 void ESIPureModuleOp::setAllPortLocsAttrs(ArrayRef<Attribute> locs) {
724 emitError(
"No ports for port locations");
728 emitError(
"No ports for port naming");
731 void ESIPureModuleOp::setAllPortAttrs(ArrayRef<Attribute> attrs) {
732 emitError(
"No ports for port attributes");
735 void ESIPureModuleOp::removeAllPortAttrs() {
736 emitError(
"No ports for port attributes)");
739 ArrayRef<Attribute> ESIPureModuleOp::getAllPortAttrs() {
return {}; }
742 emitError(
"No ports for port types");
749 StringRef ServiceImplRecordOp::getManifestClass() {
return "service"; }
751 void ServiceImplRecordOp::getDetails(SmallVectorImpl<NamedAttribute> &results) {
752 auto *
ctxt = getContext();
754 results.emplace_back(getAppIDAttrName(), getAppIDAttr());
756 results.emplace_back(getServiceAttrName(), getServiceAttr());
757 results.emplace_back(getServiceImplNameAttrName(), getServiceImplNameAttr());
759 for (
auto implDetail : getImplDetailsAttr().getValue())
760 results.push_back(implDetail);
763 SmallVector<Attribute, 8> reqDetails;
764 for (
auto reqDetail : getReqDetails().front().getOps<IsManifestData>())
765 reqDetails.push_back(reqDetail.getDetailsAsDict());
771 Region &reqDetailsRegion) {
772 parser.parseOptionalRegion(reqDetailsRegion);
773 if (reqDetailsRegion.empty())
774 reqDetailsRegion.emplaceBlock();
779 Region &reqDetailsRegion) {
780 if (!reqDetailsRegion.empty() && !reqDetailsRegion.front().empty())
781 p.printRegion(reqDetailsRegion,
false,
785 StringRef ServiceImplClientRecordOp::getManifestClass() {
786 return "serviceClient";
788 void ServiceImplClientRecordOp::getDetails(
789 SmallVectorImpl<NamedAttribute> &results) {
793 results.emplace_back(getRelAppIDPathAttrName(), getRelAppIDPathAttr());
794 auto servicePort = getServicePortAttr();
795 results.emplace_back(
796 getServicePortAttrName(),
803 servicePort.getName()),
805 if (
const auto &channelAssignments = getChannelAssignments())
807 NamedAttribute(getChannelAssignmentsAttrName(), *channelAssignments));
809 if (
const auto &implDetails = getImplDetails())
810 for (
const auto &implDetail : *implDetails)
811 results.push_back(implDetail);
814 StringRef ServiceRequestRecordOp::getManifestClass() {
return "clientPort"; }
816 void ServiceRequestRecordOp::getDetails(
817 SmallVectorImpl<NamedAttribute> &results) {
818 auto *
ctxt = getContext();
820 results.emplace_back(getTypeIDAttrName(), getTypeIDAttr());
821 auto servicePort = getServicePortAttr();
822 results.emplace_back(
823 getServicePortAttrName(),
830 servicePort.getName()),
834 StringRef SymbolMetadataOp::getManifestClass() {
return "symInfo"; }
836 StringRef SymbolConstantsOp::getManifestClass() {
return "symConsts"; }
837 void SymbolConstantsOp::getDetails(SmallVectorImpl<NamedAttribute> &results) {
838 for (
auto &attr : getConstantsAttr())
839 results.push_back(attr);
842 #define GET_OP_CLASSES
843 #include "circt/Dialect/ESI/ESI.cpp.inc"
845 #include "circt/Dialect/ESI/ESIInterfaces.cpp.inc"
assert(baseType &&"element must be base type")
return wrap(CMemoryType::get(unwrap(ctx), baseType, numElements))
static bool parseInferWindowRet(OpAsmParser &p, Type &frame, Type &windowOut)
Determine the input type ('frame') from the return type ('window').
ParseResult parseWrapFIFOType(OpAsmParser &p, Type &dataType, Type &chanInputType)
static LogicalResult verifySVInterface(Operation *op, circt::sv::ModportType modportType, ChannelType chanType)
Verify that the modport type of 'modportArg' points to an interface which looks like an ESI interface...
void printServiceImplRecordReqDetails(OpAsmPrinter &p, ServiceImplRecordOp, Region &reqDetailsRegion)
static FailureOr< ServicePortInfo > getServicePortInfo(Operation *op, SymbolTableCollection &symbolTable, hw::InnerRefAttr servicePort)
Get the port info for the specified service decl and port name.
static Type getEsiDataType(circt::sv::InterfaceOp iface)
If 'iface' looks like an ESI interface, return the inner data type.
static FailureOr< ServiceDeclOpInterface > getServiceDecl(Operation *op, SymbolTableCollection &symbolTable, hw::InnerRefAttr servicePort)
Get the port declaration op for the specified service decl, port name.
static void printUnPackBundleType(OpAsmPrinter &p, Operation *, T3, T4, Type bundleType)
static ParseResult parseUnPackBundleType(OpAsmParser &parser, SmallVectorImpl< Type > &toChannelTypes, SmallVectorImpl< Type > &fromChannelTypes, Type &type)
static void printInferWindowRet(OpAsmPrinter &p, Operation *, Type, Type window)
void printWrapFIFOType(OpAsmPrinter &p, WrapFIFOOp wrap, Type dataType, Type chanType)
bool parseServiceImplRecordReqDetails(OpAsmParser &parser, Region &reqDetailsRegion)
static SmallVector< Location > getAllPortLocs(ModTy module)
static void setAllPortNames(ArrayRef< Attribute > names, ModTy module)
static void setHWModuleType(ModTy &mod, ModuleType type)
static SmallVector< PortInfo > getPortList(ModuleTy &mod)
static PortInfo getPort(ModuleTy &mod, size_t idx)
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.
mlir::Type innerType(mlir::Type type)
LogicalResult checkBundleTypeMatch(Operation *req, ChannelBundleType svcBundleType, ChannelBundleType reqBundleType, bool skipDirectionCheck)
Check that the channels on two bundles match allowing for AnyType in the 'svc' bundle.
LogicalResult checkInnerTypeMatch(Type expected, Type actual)
size_t getNumPorts(Operation *op)
Return the number of ports in a module-like thing (modules, memories, etc)
void getAsmResultNames(OpAsmSetValueNameFn setNameFn, StringRef instanceName, ArrayAttr resultNames, ValueRange results)
Suggest a name for each result value based on the saved result names attribute.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
ChannelDirection direction
Describes a service port.
This holds the name, type, direction of a module's ports.