18#include "mlir/IR/Builders.h"
19#include "llvm/ADT/APSInt.h"
20#include "llvm/ADT/SmallString.h"
21#include "llvm/ADT/TypeSwitch.h"
22#include <mlir/Dialect/Func/IR/FuncOps.h>
32void SVModuleOp::build(mlir::OpBuilder &builder, mlir::OperationState &state,
33 llvm::StringRef name, hw::ModuleType type) {
34 state.addAttribute(SymbolTable::getSymbolAttrName(),
35 builder.getStringAttr(name));
36 state.addAttribute(getModuleTypeAttrName(state.name), TypeAttr::get(type));
40void SVModuleOp::print(OpAsmPrinter &p) {
44 StringRef visibilityAttrName = SymbolTable::getVisibilityAttrName();
45 if (
auto visibility = (*this)->getAttrOfType<StringAttr>(visibilityAttrName))
46 p << visibility.getValue() <<
' ';
48 p.printSymbolName(SymbolTable::getSymbolName(*this).getValue());
52 p.printRegion(getBodyRegion(),
false,
55 p.printOptionalAttrDictWithKeyword(getOperation()->getAttrs(),
59ParseResult SVModuleOp::parse(OpAsmParser &parser, OperationState &result) {
61 (void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
65 if (parser.parseSymbolName(nameAttr, getSymNameAttrName(result.name),
70 SmallVector<hw::module_like_impl::PortParse> ports;
75 result.addAttribute(getModuleTypeAttrName(result.name), modType);
78 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
82 SmallVector<OpAsmParser::Argument, 4> entryArgs;
83 for (
auto &port : ports)
85 entryArgs.push_back(port);
88 auto &bodyRegion = *result.addRegion();
89 if (parser.parseRegion(bodyRegion, entryArgs))
92 ensureTerminator(bodyRegion, parser.getBuilder(), result.location);
96void SVModuleOp::getAsmBlockArgumentNames(mlir::Region ®ion,
98 if (®ion != &getBodyRegion())
101 for (
auto [index, arg] :
llvm::enumerate(region.front().getArguments()))
102 setNameFn(arg, moduleType.getInputNameAttr(index));
105OutputOp SVModuleOp::getOutputOp() {
106 return cast<OutputOp>(getBody()->getTerminator());
109OperandRange SVModuleOp::getOutputs() {
return getOutputOp().getOperands(); }
115LogicalResult OutputOp::verify() {
116 auto module = getParentOp();
119 auto outputTypes =
module.getModuleType().getOutputTypes();
120 if (outputTypes.size() != getNumOperands())
121 return emitOpError(
"has ")
122 << getNumOperands() <<
" operands, but enclosing module @"
123 <<
module.getSymName() << " has " << outputTypes.size()
127 for (
unsigned i = 0, e = outputTypes.size(); i != e; ++i)
128 if (outputTypes[i] != getOperand(i).getType())
129 return emitOpError() <<
"operand " << i <<
" (" << getOperand(i).getType()
130 <<
") does not match output type (" << outputTypes[i]
131 <<
") of module @" <<
module.getSymName();
140LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
143 symbolTable.lookupNearestSymbolFrom(*
this, getModuleNameAttr());
145 return emitOpError(
"references unknown symbol @") << getModuleName();
148 auto module = dyn_cast<SVModuleOp>(symbol);
150 return emitOpError(
"must reference a 'moore.module', but @")
151 << getModuleName() <<
" is a '" << symbol->getName() <<
"'";
154 auto moduleType =
module.getModuleType();
155 auto inputTypes = moduleType.getInputTypes();
157 if (inputTypes.size() != getNumOperands())
158 return emitOpError(
"has ")
159 << getNumOperands() <<
" operands, but target module @"
160 <<
module.getSymName() << " has " << inputTypes.size() << " inputs";
162 for (
unsigned i = 0, e = inputTypes.size(); i != e; ++i)
163 if (inputTypes[i] != getOperand(i).getType())
164 return emitOpError() <<
"operand " << i <<
" (" << getOperand(i).getType()
165 <<
") does not match input type (" << inputTypes[i]
166 <<
") of module @" <<
module.getSymName();
169 auto outputTypes = moduleType.getOutputTypes();
171 if (outputTypes.size() != getNumResults())
172 return emitOpError(
"has ")
173 << getNumOperands() <<
" results, but target module @"
174 <<
module.getSymName() << " has " << outputTypes.size()
177 for (
unsigned i = 0, e = outputTypes.size(); i != e; ++i)
178 if (outputTypes[i] != getResult(i).getType())
179 return emitOpError() <<
"result " << i <<
" (" << getResult(i).getType()
180 <<
") does not match output type (" << outputTypes[i]
181 <<
") of module @" <<
module.getSymName();
186void InstanceOp::print(OpAsmPrinter &p) {
188 p.printAttributeWithoutType(getInstanceNameAttr());
190 p.printAttributeWithoutType(getModuleNameAttr());
196 p.printOptionalAttrDict(getOperation()->getAttrs(), getAttributeNames());
199ParseResult InstanceOp::parse(OpAsmParser &parser, OperationState &result) {
201 StringAttr instanceName;
202 if (parser.parseAttribute(instanceName,
"instanceName", result.attributes))
206 FlatSymbolRefAttr moduleName;
207 if (parser.parseAttribute(moduleName,
"moduleName", result.attributes))
211 auto loc = parser.getCurrentLocation();
212 SmallVector<OpAsmParser::UnresolvedOperand> inputs;
213 SmallVector<Type> types;
217 if (parser.resolveOperands(inputs, types, loc, result.operands))
219 result.addAttribute(
"inputNames", names);
222 if (parser.parseArrow())
229 result.addAttribute(
"outputNames", names);
230 result.addTypes(types);
233 if (parser.parseOptionalAttrDict(result.attributes))
240 SmallString<32> name;
243 auto baseLen = name.size();
245 for (
auto [result, portName] :
246 llvm::zip(getOutputs(), getOutputNames().getAsRange<StringAttr>())) {
247 if (!portName || portName.empty())
249 name.resize(baseLen);
250 name += portName.getValue();
251 setNameFn(result, name);
261 setNameFn(getResult(), *
getName());
264LogicalResult VariableOp::canonicalize(VariableOp op,
265 PatternRewriter &rewriter) {
269 auto initial = op.getInitial();
270 if (initial && mlir::mayHaveSSADominance(*op->getParentRegion())) {
271 rewriter.modifyOpInPlace(op, [&] { op.getInitialMutable().clear(); });
272 rewriter.setInsertionPointAfter(op);
273 BlockingAssignOp::create(rewriter, initial.getLoc(), op, initial);
280 auto *block = op->getBlock();
281 ContinuousAssignOp uniqueAssignOp;
282 for (
auto *user : op->getUsers()) {
284 if (user->getBlock() != block)
288 if (
auto assignOp = dyn_cast<ContinuousAssignOp>(user)) {
291 uniqueAssignOp = assignOp;
296 if (!isa<ReadOp>(user))
304 Value assignedValue = uniqueAssignOp.getSrc();
305 if (
auto name = op.getNameAttr(); name && !name.empty())
306 assignedValue = AssignedVariableOp::create(rewriter, op.getLoc(), name,
307 uniqueAssignOp.getSrc());
310 rewriter.eraseOp(uniqueAssignOp);
311 for (
auto *user :
llvm::make_early_inc_range(op->getUsers())) {
312 auto readOp = cast<ReadOp>(user);
313 rewriter.replaceOp(readOp, assignedValue);
317 rewriter.eraseOp(op);
321SmallVector<MemorySlot> VariableOp::getPromotableSlots() {
324 if (mlir::mayBeGraphRegion(*getOperation()->getParentRegion()) ||
330 auto nestedType = dyn_cast<PackedType>(getType().getNestedType());
331 if (!nestedType || !nestedType.getBitSize())
334 return {MemorySlot{getResult(), getType().getNestedType()}};
337Value VariableOp::getDefaultValue(
const MemorySlot &slot, OpBuilder &builder) {
338 auto packedType = dyn_cast<PackedType>(slot.elemType);
341 auto bitWidth = packedType.getBitSize();
344 auto fvint = packedType.getDomain() == Domain::FourValued
346 :
FVInt::getZero(*bitWidth);
347 Value value = ConstantOp::create(
349 IntType::get(getContext(), *bitWidth, packedType.getDomain()), fvint);
350 if (value.getType() != packedType)
351 SBVToPackedOp::create(builder,
getLoc(), packedType, value);
355void VariableOp::handleBlockArgument(
const MemorySlot &slot,
356 BlockArgument argument,
357 OpBuilder &builder) {}
359std::optional<mlir::PromotableAllocationOpInterface>
360VariableOp::handlePromotionComplete(
const MemorySlot &slot, Value defaultValue,
361 OpBuilder &builder) {
362 if (defaultValue && defaultValue.use_empty())
363 defaultValue.getDefiningOp()->erase();
368SmallVector<DestructurableMemorySlot> VariableOp::getDestructurableSlots() {
369 if (isa<SVModuleOp>(getOperation()->getParentOp()))
374 auto refType = getType();
375 auto destructurable = llvm::dyn_cast<DestructurableTypeInterface>(refType);
379 auto destructuredType = destructurable.getSubelementIndexMap();
380 if (!destructuredType)
383 return {DestructurableMemorySlot{{getResult(), refType}, *destructuredType}};
386DenseMap<Attribute, MemorySlot> VariableOp::destructure(
387 const DestructurableMemorySlot &slot,
388 const SmallPtrSetImpl<Attribute> &usedIndices, OpBuilder &builder,
389 SmallVectorImpl<DestructurableAllocationOpInterface> &newAllocators) {
390 assert(slot.ptr == getResult());
392 builder.setInsertionPointAfter(*
this);
394 auto destructurableType = cast<DestructurableTypeInterface>(getType());
395 DenseMap<Attribute, MemorySlot> slotMap;
396 for (Attribute index : usedIndices) {
397 auto elemType = cast<RefType>(destructurableType.getTypeAtIndex(index));
398 assert(elemType &&
"used index must exist");
400 if (
auto name =
getName(); name && !name->empty())
401 varName = StringAttr::get(
402 getContext(), (*name) +
"." + cast<StringAttr>(index).getValue());
404 VariableOp::create(builder,
getLoc(), elemType, varName, Value());
405 newAllocators.push_back(varOp);
406 slotMap.try_emplace<MemorySlot>(index, {varOp.getResult(), elemType});
412std::optional<DestructurableAllocationOpInterface>
413VariableOp::handleDestructuringComplete(
const DestructurableMemorySlot &slot,
414 OpBuilder &builder) {
415 assert(slot.ptr == getResult());
426 setNameFn(getResult(), *
getName());
429LogicalResult NetOp::canonicalize(NetOp op, PatternRewriter &rewriter) {
430 bool modified =
false;
434 auto *block = op->getBlock();
435 ContinuousAssignOp uniqueAssignOp;
436 bool allUsesAreReads =
true;
437 for (
auto *user : op->getUsers()) {
439 if (user->getBlock() != block)
443 if (
auto assignOp = dyn_cast<ContinuousAssignOp>(user)) {
446 uniqueAssignOp = assignOp;
451 if (!isa<ReadOp>(user))
452 allUsesAreReads =
false;
457 if (uniqueAssignOp && !op.getAssignment()) {
458 rewriter.modifyOpInPlace(
459 op, [&] { op.getAssignmentMutable().assign(uniqueAssignOp.getSrc()); });
460 rewriter.eraseOp(uniqueAssignOp);
468 if (!uniqueAssignOp && allUsesAreReads && op.getAssignment()) {
471 auto assignedValue = op.getAssignment();
472 if (
auto name = op.getNameAttr(); name && !name.empty())
473 assignedValue = AssignedVariableOp::create(rewriter, op.getLoc(), name,
478 for (
auto *user :
llvm::make_early_inc_range(op->getUsers())) {
479 auto readOp = cast<ReadOp>(user);
480 rewriter.replaceOp(readOp, assignedValue);
482 rewriter.eraseOp(op);
486 return success(modified);
495 setNameFn(getResult(), *
getName());
498LogicalResult AssignedVariableOp::canonicalize(AssignedVariableOp op,
499 PatternRewriter &rewriter) {
502 if (
auto otherOp = op.getInput().getDefiningOp<AssignedVariableOp>()) {
503 if (otherOp.getNameAttr() == op.getNameAttr()) {
504 rewriter.replaceOp(op, otherOp);
510 if (
auto blockArg = dyn_cast<BlockArgument>(op.getInput())) {
512 dyn_cast<SVModuleOp>(blockArg.getOwner()->getParentOp())) {
513 auto moduleType = moduleOp.getModuleType();
514 auto portName = moduleType.getInputNameAttr(blockArg.getArgNumber());
515 if (portName == op.getNameAttr()) {
516 rewriter.replaceOp(op, blockArg);
523 for (
auto &use : op->getUses()) {
524 auto *useOwner = use.getOwner();
525 if (
auto outputOp = dyn_cast<OutputOp>(useOwner)) {
526 if (
auto moduleOp = dyn_cast<SVModuleOp>(outputOp->getParentOp())) {
527 auto moduleType = moduleOp.getModuleType();
528 auto portName = moduleType.getOutputNameAttr(use.getOperandNumber());
529 if (portName == op.getNameAttr()) {
530 rewriter.replaceOp(op, op.getInput());
545LogicalResult GlobalVariableOp::verifyRegions() {
546 if (
auto *block = getInitBlock()) {
547 auto &terminator = block->back();
548 if (!isa<YieldOp>(terminator))
549 return emitOpError() <<
"must have a 'moore.yield' terminator";
554Block *GlobalVariableOp::getInitBlock() {
555 if (getInitRegion().
empty())
557 return &getInitRegion().front();
565GetGlobalVariableOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
568 symbolTable.lookupNearestSymbolFrom(*
this, getGlobalNameAttr());
570 return emitOpError() <<
"references unknown symbol " << getGlobalNameAttr();
573 auto var = dyn_cast<GlobalVariableOp>(symbol);
575 return emitOpError() <<
"must reference a 'moore.global_variable', but "
576 << getGlobalNameAttr() <<
" is a '"
577 << symbol->getName() <<
"'";
580 auto expType = var.getType();
581 auto actType = getType().getNestedType();
582 if (expType != actType)
583 return emitOpError() <<
"returns a " << actType <<
" reference, but "
584 << getGlobalNameAttr() <<
" is of type " << expType;
593void ConstantOp::print(OpAsmPrinter &p) {
596 p.printOptionalAttrDict((*this)->getAttrs(), {
"value"});
598 p.printStrippedAttrOrType(getType());
601ParseResult ConstantOp::parse(OpAsmParser &parser, OperationState &result) {
604 auto valueLoc = parser.getCurrentLocation();
609 if (parser.parseOptionalAttrDict(result.attributes) || parser.parseColon())
614 if (parser.parseCustomTypeWithFallback(type))
622 value = value.
sext(type.getWidth());
623 }
else if (type.getWidth() < value.
getBitWidth()) {
626 unsigned neededBits =
628 if (type.getWidth() < neededBits)
629 return parser.emitError(valueLoc)
630 <<
"value requires " << neededBits
631 <<
" bits, but result type only has " << type.getWidth();
632 value = value.
trunc(type.getWidth());
637 if (value.
hasUnknown() && type.getDomain() != Domain::FourValued)
638 return parser.emitError(valueLoc)
639 <<
"value contains X or Z bits, but result type " << type
640 <<
" only allows two-valued bits";
643 auto attrValue = FVIntegerAttr::get(parser.getContext(), value);
644 result.addAttribute(
"value", attrValue);
645 result.addTypes(type);
649LogicalResult ConstantOp::verify() {
650 auto attrWidth = getValue().getBitWidth();
651 auto typeWidth = getType().getWidth();
652 if (attrWidth != typeWidth)
653 return emitError(
"attribute width ")
654 << attrWidth <<
" does not match return type's width " << typeWidth;
658void ConstantOp::build(OpBuilder &builder, OperationState &result, IntType type,
659 const FVInt &value) {
661 "FVInt width must match type width");
662 build(builder, result, type, FVIntegerAttr::get(builder.getContext(), value));
665void ConstantOp::build(OpBuilder &builder, OperationState &result, IntType type,
666 const APInt &value) {
667 assert(type.getWidth() == value.getBitWidth() &&
668 "APInt width must match type width");
669 build(builder, result, type,
FVInt(value));
676void ConstantOp::build(OpBuilder &builder, OperationState &result, IntType type,
677 int64_t value,
bool isSigned) {
678 build(builder, result, type,
679 APInt(type.getWidth(), (uint64_t)value, isSigned));
682OpFoldResult ConstantOp::fold(FoldAdaptor adaptor) {
683 assert(adaptor.getOperands().empty() &&
"constant has no operands");
684 return getValueAttr();
691OpFoldResult ConstantTimeOp::fold(FoldAdaptor adaptor) {
692 return getValueAttr();
699LogicalResult ConstantRealOp::inferReturnTypes(
700 MLIRContext *
context, std::optional<Location> loc, ValueRange operands,
701 DictionaryAttr attrs, mlir::OpaqueProperties properties,
702 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
703 ConstantRealOp::Adaptor adaptor(operands, attrs, properties);
704 results.push_back(RealType::get(
706 adaptor.getValueAttr().getType().getIntOrFloatBitWidth())));
714LogicalResult ConcatOp::inferReturnTypes(
715 MLIRContext *
context, std::optional<Location> loc, ValueRange operands,
716 DictionaryAttr attrs, mlir::OpaqueProperties properties,
717 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
718 Domain domain = Domain::TwoValued;
720 for (
auto operand : operands) {
721 auto type = cast<IntType>(operand.getType());
722 if (type.getDomain() == Domain::FourValued)
723 domain = Domain::FourValued;
724 width += type.getWidth();
726 results.push_back(IntType::get(
context, width, domain));
734LogicalResult ConcatRefOp::inferReturnTypes(
735 MLIRContext *
context, std::optional<Location> loc, ValueRange operands,
736 DictionaryAttr attrs, mlir::OpaqueProperties properties,
737 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
738 Domain domain = Domain::TwoValued;
740 for (Value operand : operands) {
741 UnpackedType nestedType = cast<RefType>(operand.getType()).getNestedType();
742 PackedType packedType = dyn_cast<PackedType>(nestedType);
748 if (packedType.
getDomain() == Domain::FourValued)
749 domain = Domain::FourValued;
752 std::optional<int> bitSize = packedType.
getBitSize();
758 results.push_back(RefType::get(IntType::get(
context, width, domain)));
767 if (
auto arrayType = dyn_cast<ArrayType>(type))
768 return {arrayType.getSize(), arrayType.getElementType()};
769 if (
auto arrayType = dyn_cast<UnpackedArrayType>(type))
770 return {arrayType.getSize(), arrayType.getElementType()};
771 assert(0 &&
"expected ArrayType or UnpackedArrayType");
775LogicalResult ArrayCreateOp::verify() {
779 if (getElements().size() != size)
780 return emitOpError() <<
"has " << getElements().size()
781 <<
" operands, but result type requires " << size;
787 auto value = getElements()[0];
789 return emitOpError() <<
"operands have type " << value.getType()
800 if (
auto structType = dyn_cast<StructType>(type))
801 return structType.getFieldIndex(name);
802 if (
auto structType = dyn_cast<UnpackedStructType>(type))
803 return structType.getFieldIndex(name);
804 assert(0 &&
"expected StructType or UnpackedStructType");
809 if (
auto structType = dyn_cast<StructType>(type))
810 return structType.getMembers();
811 if (
auto structType = dyn_cast<UnpackedStructType>(type))
812 return structType.getMembers();
813 assert(0 &&
"expected StructType or UnpackedStructType");
823LogicalResult StructCreateOp::verify() {
827 if (getFields().size() != members.size())
828 return emitOpError() <<
"has " << getFields().size()
829 <<
" operands, but result type requires "
833 for (
auto [index, pair] :
llvm::enumerate(
llvm::zip(getFields(), members))) {
834 auto [value, member] = pair;
835 if (value.getType() != member.type)
836 return emitOpError() <<
"operand #" << index <<
" has type "
837 << value.getType() <<
", but struct field "
838 << member.name <<
" requires " << member.type;
843OpFoldResult StructCreateOp::fold(FoldAdaptor adaptor) {
844 SmallVector<NamedAttribute> fields;
845 for (
auto [member, field] :
849 fields.push_back(NamedAttribute(member.name, field));
851 return DictionaryAttr::get(getContext(), fields);
858LogicalResult StructExtractOp::verify() {
861 return emitOpError() <<
"extracts field " << getFieldNameAttr()
862 <<
" which does not exist in " << getInput().getType();
863 if (type != getType())
864 return emitOpError() <<
"result type " << getType()
865 <<
" must match struct field type " << type;
869OpFoldResult StructExtractOp::fold(FoldAdaptor adaptor) {
871 if (
auto fields = dyn_cast_or_null<DictionaryAttr>(adaptor.getInput()))
872 if (
auto value = fields.get(getFieldNameAttr()))
876 if (
auto inject = getInput().getDefiningOp<StructInjectOp>()) {
877 if (inject.getFieldNameAttr() == getFieldNameAttr())
878 return inject.getNewValue();
883 if (
auto create = getInput().getDefiningOp<StructCreateOp>()) {
885 return create.getFields()[*index];
896LogicalResult StructExtractRefOp::verify() {
898 cast<RefType>(getInput().getType()).getNestedType(), getFieldNameAttr());
900 return emitOpError() <<
"extracts field " << getFieldNameAttr()
901 <<
" which does not exist in " << getInput().getType();
902 if (type != getType().getNestedType())
903 return emitOpError() <<
"result ref of type " << getType().getNestedType()
904 <<
" must match struct field type " << type;
908bool StructExtractRefOp::canRewire(
909 const DestructurableMemorySlot &slot,
910 SmallPtrSetImpl<Attribute> &usedIndices,
911 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
912 const DataLayout &dataLayout) {
913 if (slot.ptr != getInput())
915 auto index = getFieldNameAttr();
916 if (!index || !slot.subelementTypes.contains(index))
918 usedIndices.insert(index);
923StructExtractRefOp::rewire(
const DestructurableMemorySlot &slot,
924 DenseMap<Attribute, MemorySlot> &subslots,
925 OpBuilder &builder,
const DataLayout &dataLayout) {
926 auto index = getFieldNameAttr();
927 const MemorySlot &memorySlot = subslots.at(index);
928 replaceAllUsesWith(memorySlot.ptr);
929 getInputMutable().drop();
931 return DeletionKind::Keep;
938LogicalResult StructInjectOp::verify() {
941 return emitOpError() <<
"injects field " << getFieldNameAttr()
942 <<
" which does not exist in " << getInput().getType();
943 if (type != getNewValue().getType())
944 return emitOpError() <<
"injected value " << getNewValue().getType()
945 <<
" must match struct field type " << type;
949OpFoldResult StructInjectOp::fold(FoldAdaptor adaptor) {
950 auto input = adaptor.getInput();
951 auto newValue = adaptor.getNewValue();
952 if (!input || !newValue)
954 NamedAttrList fields(cast<DictionaryAttr>(input));
955 fields.set(getFieldNameAttr(), newValue);
956 return fields.getDictionary(getContext());
959LogicalResult StructInjectOp::canonicalize(StructInjectOp op,
960 PatternRewriter &rewriter) {
965 SmallPtrSet<Operation *, 4> injectOps;
966 DenseMap<StringAttr, Value> fieldValues;
968 while (
auto injectOp = input.getDefiningOp<StructInjectOp>()) {
969 if (!injectOps.insert(injectOp).second)
971 fieldValues.insert({injectOp.getFieldNameAttr(), injectOp.getNewValue()});
972 input = injectOp.getInput();
974 if (
auto createOp = input.getDefiningOp<StructCreateOp>())
975 for (
auto [value, member] :
llvm::zip(createOp.getFields(), members))
976 fieldValues.insert({member.name, value});
979 if (fieldValues.size() == members.size()) {
980 SmallVector<Value> values;
981 values.reserve(fieldValues.size());
982 for (
auto member : members)
983 values.push_back(fieldValues.lookup(member.name));
984 rewriter.replaceOpWithNewOp<StructCreateOp>(op, op.getType(), values);
990 if (injectOps.size() == fieldValues.size())
995 for (
auto member : members)
996 if (auto value = fieldValues.lookup(member.name))
997 input = StructInjectOp::create(rewriter, op.
getLoc(), op.getType(), input,
999 rewriter.replaceOp(op, input);
1007LogicalResult UnionCreateOp::verify() {
1010 return TypeSwitch<Type, LogicalResult>(getType())
1011 .Case<UnionType, UnpackedUnionType>([
this](
auto &type) {
1012 auto members = type.getMembers();
1013 auto resultType = getType();
1015 for (
const auto &member : members)
1016 if (member.name == fieldName && member.type == resultType)
1018 emitOpError(
"input type must match the union field type");
1021 .Default([
this](
auto &) {
1022 emitOpError(
"input type must be UnionType or UnpackedUnionType");
1031LogicalResult UnionExtractOp::verify() {
1034 return TypeSwitch<Type, LogicalResult>(getInput().getType())
1035 .Case<UnionType, UnpackedUnionType>([
this](
auto &type) {
1036 auto members = type.getMembers();
1038 auto resultType = getType();
1039 for (
const auto &member : members)
1040 if (member.name == fieldName && member.type == resultType)
1042 emitOpError(
"result type must match the union field type");
1045 .Default([
this](
auto &) {
1046 emitOpError(
"input type must be UnionType or UnpackedUnionType");
1055LogicalResult UnionExtractRefOp::verify() {
1058 return TypeSwitch<Type, LogicalResult>(getInput().getType().getNestedType())
1059 .Case<UnionType, UnpackedUnionType>([
this](
auto &type) {
1060 auto members = type.getMembers();
1062 auto resultType = getType().getNestedType();
1063 for (
const auto &member : members)
1064 if (member.name == fieldName && member.type == resultType)
1066 emitOpError(
"result type must match the union field type");
1069 .Default([
this](
auto &) {
1070 emitOpError(
"input type must be UnionType or UnpackedUnionType");
1079LogicalResult YieldOp::verify() {
1081 auto *parentOp = getOperation()->getParentOp();
1082 if (
auto cond = dyn_cast<ConditionalOp>(parentOp)) {
1083 expType = cond.getType();
1084 }
else if (
auto varOp = dyn_cast<GlobalVariableOp>(parentOp)) {
1085 expType = varOp.getType();
1087 llvm_unreachable(
"all in ParentOneOf handled");
1090 auto actType = getOperand().getType();
1091 if (expType != actType) {
1092 return emitOpError() <<
"yields " << actType <<
", but parent expects "
1102OpFoldResult ConversionOp::fold(FoldAdaptor adaptor) {
1104 if (getInput().getType() == getResult().getType())
1108 auto intInput = dyn_cast_or_null<FVIntegerAttr>(adaptor.getInput());
1109 auto fromIntType = dyn_cast<IntType>(getInput().getType());
1110 auto toIntType = dyn_cast<IntType>(getResult().getType());
1111 if (intInput && fromIntType && toIntType &&
1112 fromIntType.getWidth() == toIntType.getWidth()) {
1115 if (toIntType.getDomain() == Domain::FourValued)
1120 return FVIntegerAttr::get(getContext(), intInput.getValue().toAPInt(
false));
1130OpFoldResult LogicToIntOp::fold(FoldAdaptor adaptor) {
1132 if (
auto reverseOp = getInput().getDefiningOp<IntToLogicOp>())
1133 return reverseOp.getInput();
1137 if (
auto intInput = dyn_cast_or_null<FVIntegerAttr>(adaptor.getInput()))
1138 return FVIntegerAttr::get(getContext(), intInput.getValue().toAPInt(
false));
1147OpFoldResult IntToLogicOp::fold(FoldAdaptor adaptor) {
1152 if (
auto intInput = dyn_cast_or_null<FVIntegerAttr>(adaptor.getInput()))
1162OpFoldResult TimeToLogicOp::fold(FoldAdaptor adaptor) {
1164 if (
auto reverseOp = getInput().getDefiningOp<LogicToTimeOp>())
1165 return reverseOp.getInput();
1168 if (
auto attr = dyn_cast_or_null<IntegerAttr>(adaptor.getInput()))
1169 return FVIntegerAttr::get(getContext(), attr.getValue());
1178OpFoldResult LogicToTimeOp::fold(FoldAdaptor adaptor) {
1180 if (
auto reverseOp = getInput().getDefiningOp<TimeToLogicOp>())
1181 return reverseOp.getInput();
1184 if (
auto attr = dyn_cast_or_null<FVIntegerAttr>(adaptor.getInput()))
1185 return IntegerAttr::get(getContext(), APSInt(attr.getValue().toAPInt(
false),
1195OpFoldResult ConvertRealOp::fold(FoldAdaptor adaptor) {
1196 if (getInput().getType() == getResult().getType())
1206OpFoldResult TruncOp::fold(FoldAdaptor adaptor) {
1208 if (
auto intAttr = dyn_cast_or_null<FVIntegerAttr>(adaptor.getInput())) {
1209 auto width = getType().getWidth();
1210 return FVIntegerAttr::get(getContext(), intAttr.getValue().trunc(width));
1220OpFoldResult ZExtOp::fold(FoldAdaptor adaptor) {
1222 if (
auto intAttr = dyn_cast_or_null<FVIntegerAttr>(adaptor.getInput())) {
1223 auto width = getType().getWidth();
1224 return FVIntegerAttr::get(getContext(), intAttr.getValue().zext(width));
1234OpFoldResult SExtOp::fold(FoldAdaptor adaptor) {
1236 if (
auto intAttr = dyn_cast_or_null<FVIntegerAttr>(adaptor.getInput())) {
1237 auto width = getType().getWidth();
1238 return FVIntegerAttr::get(getContext(), intAttr.getValue().sext(width));
1248OpFoldResult BoolCastOp::fold(FoldAdaptor adaptor) {
1250 if (getInput().getType() == getResult().getType())
1259bool BlockingAssignOp::loadsFrom(
const MemorySlot &slot) {
return false; }
1261bool BlockingAssignOp::storesTo(
const MemorySlot &slot) {
1262 return getDst() == slot.ptr;
1265Value BlockingAssignOp::getStored(
const MemorySlot &slot, OpBuilder &builder,
1267 const DataLayout &dataLayout) {
1271bool BlockingAssignOp::canUsesBeRemoved(
1272 const MemorySlot &slot,
const SmallPtrSetImpl<OpOperand *> &blockingUses,
1273 SmallVectorImpl<OpOperand *> &newBlockingUses,
1274 const DataLayout &dataLayout) {
1276 if (blockingUses.size() != 1)
1278 Value blockingUse = (*blockingUses.begin())->
get();
1279 return blockingUse == slot.ptr && getDst() == slot.ptr &&
1280 getSrc() != slot.ptr && getSrc().getType() == slot.elemType;
1283DeletionKind BlockingAssignOp::removeBlockingUses(
1284 const MemorySlot &slot,
const SmallPtrSetImpl<OpOperand *> &blockingUses,
1285 OpBuilder &builder, Value reachingDefinition,
1286 const DataLayout &dataLayout) {
1287 return DeletionKind::Delete;
1294bool ReadOp::loadsFrom(
const MemorySlot &slot) {
1295 return getInput() == slot.ptr;
1298bool ReadOp::storesTo(
const MemorySlot &slot) {
return false; }
1300Value ReadOp::getStored(
const MemorySlot &slot, OpBuilder &builder,
1301 Value reachingDef,
const DataLayout &dataLayout) {
1302 llvm_unreachable(
"getStored should not be called on ReadOp");
1305bool ReadOp::canUsesBeRemoved(
const MemorySlot &slot,
1306 const SmallPtrSetImpl<OpOperand *> &blockingUses,
1307 SmallVectorImpl<OpOperand *> &newBlockingUses,
1308 const DataLayout &dataLayout) {
1310 if (blockingUses.size() != 1)
1312 Value blockingUse = (*blockingUses.begin())->
get();
1313 return blockingUse == slot.ptr && getOperand() == slot.ptr &&
1314 getResult().getType() == slot.elemType;
1318ReadOp::removeBlockingUses(
const MemorySlot &slot,
1319 const SmallPtrSetImpl<OpOperand *> &blockingUses,
1320 OpBuilder &builder, Value reachingDefinition,
1321 const DataLayout &dataLayout) {
1322 getResult().replaceAllUsesWith(reachingDefinition);
1323 return DeletionKind::Delete;
1332 auto lhsValue = dyn_cast_or_null<FVIntegerAttr>(lhs);
1333 if (lhsValue && lhsValue.getValue() == 1)
1336 auto rhsValue = dyn_cast_or_null<FVIntegerAttr>(rhs);
1337 if (rhsValue && rhsValue.getValue().isZero())
1338 return FVIntegerAttr::get(ctxt,
1339 FVInt(rhsValue.getValue().getBitWidth(), 1));
1344OpFoldResult PowSOp::fold(FoldAdaptor adaptor) {
1348LogicalResult PowSOp::canonicalize(PowSOp op, PatternRewriter &rewriter) {
1349 Location loc = op.getLoc();
1350 auto intType = cast<IntType>(op.getRhs().getType());
1351 if (
auto baseOp = op.getLhs().getDefiningOp<ConstantOp>()) {
1352 if (baseOp.getValue() == 2) {
1353 Value constOne = ConstantOp::create(rewriter, loc, intType, 1);
1354 Value constZero = ConstantOp::create(rewriter, loc, intType, 0);
1355 Value shift = ShlOp::create(rewriter, loc, constOne, op.getRhs());
1356 Value isNegative = SltOp::create(rewriter, loc, op.getRhs(), constZero);
1357 auto condOp = rewriter.replaceOpWithNewOp<ConditionalOp>(
1358 op, op.getLhs().getType(), isNegative);
1359 Block *thenBlock = rewriter.createBlock(&condOp.getTrueRegion());
1360 rewriter.setInsertionPointToStart(thenBlock);
1361 YieldOp::create(rewriter, loc, constZero);
1362 Block *elseBlock = rewriter.createBlock(&condOp.getFalseRegion());
1363 rewriter.setInsertionPointToStart(elseBlock);
1364 YieldOp::create(rewriter, loc, shift);
1376OpFoldResult PowUOp::fold(FoldAdaptor adaptor) {
1380LogicalResult PowUOp::canonicalize(PowUOp op, PatternRewriter &rewriter) {
1381 Location loc = op.getLoc();
1382 auto intType = cast<IntType>(op.getRhs().getType());
1383 if (
auto baseOp = op.getLhs().getDefiningOp<ConstantOp>()) {
1384 if (baseOp.getValue() == 2) {
1385 Value constOne = ConstantOp::create(rewriter, loc, intType, 1);
1386 rewriter.replaceOpWithNewOp<ShlOp>(op, constOne, op.getRhs());
1398OpFoldResult SubOp::fold(FoldAdaptor adaptor) {
1399 if (
auto intAttr = dyn_cast_or_null<FVIntegerAttr>(adaptor.getRhs()))
1400 if (intAttr.getValue().isZero())
1410OpFoldResult MulOp::fold(FoldAdaptor adaptor) {
1411 auto lhs = dyn_cast_or_null<FVIntegerAttr>(adaptor.getLhs());
1412 auto rhs = dyn_cast_or_null<FVIntegerAttr>(adaptor.getRhs());
1414 return FVIntegerAttr::get(getContext(), lhs.getValue() * rhs.getValue());
1422OpFoldResult DivUOp::fold(FoldAdaptor adaptor) {
1423 auto lhs = dyn_cast_or_null<FVIntegerAttr>(adaptor.getLhs());
1424 auto rhs = dyn_cast_or_null<FVIntegerAttr>(adaptor.getRhs());
1426 return FVIntegerAttr::get(getContext(),
1427 lhs.getValue().udiv(rhs.getValue()));
1435OpFoldResult DivSOp::fold(FoldAdaptor adaptor) {
1436 auto lhs = dyn_cast_or_null<FVIntegerAttr>(adaptor.getLhs());
1437 auto rhs = dyn_cast_or_null<FVIntegerAttr>(adaptor.getRhs());
1439 return FVIntegerAttr::get(getContext(),
1440 lhs.getValue().sdiv(rhs.getValue()));
1448LogicalResult ClassDeclOp::verify() {
1449 mlir::Region &body = getBody();
1451 return mlir::success();
1453 auto &block = body.front();
1454 for (mlir::Operation &op : block) {
1457 if (llvm::isa<circt::moore::ClassPropertyDeclOp,
1458 circt::moore::ClassMethodDeclOp>(&op))
1461 return emitOpError()
1462 <<
"body may only contain 'moore.class.propertydecl' operations";
1464 return mlir::success();
1467LogicalResult ClassNewOp::verify() {
1470 auto handleTy = cast<ClassHandleType>(getResult().getType());
1471 mlir::SymbolRefAttr classSym = handleTy.getClassSym();
1473 return emitOpError(
"result type is missing a class symbol");
1476 mlir::Operation *sym =
1477 mlir::SymbolTable::lookupNearestSymbolFrom(getOperation(), classSym);
1479 return emitOpError(
"referenced class symbol `")
1480 << classSym <<
"` was not found";
1482 if (!llvm::isa<ClassDeclOp>(sym))
1483 return emitOpError(
"symbol `")
1484 << classSym <<
"` does not name a `moore.class.classdecl`";
1486 return mlir::success();
1489void ClassNewOp::getEffects(
1490 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
1493 effects.emplace_back(MemoryEffects::Allocate::get());
1497ClassUpcastOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1499 auto srcTy = dyn_cast<ClassHandleType>(getOperand().getType());
1501 return emitOpError() <<
"operand must be !moore.class<...>; got "
1502 << getOperand().getType();
1504 auto dstTy = dyn_cast<ClassHandleType>(getResult().getType());
1506 return emitOpError() <<
"result must be !moore.class<...>; got "
1507 << getResult().getType();
1512 auto *op = getOperation();
1515 symbolTable.lookupNearestSymbolFrom(op, srcTy.getClassSym());
1517 symbolTable.lookupNearestSymbolFrom(op, dstTy.getClassSym());
1518 if (!srcDeclOp || !dstDeclOp)
1519 return emitOpError() <<
"failed to resolve class symbol(s): src="
1520 << srcTy.getClassSym()
1521 <<
", dst=" << dstTy.getClassSym();
1523 auto srcDecl = dyn_cast<ClassDeclOp>(srcDeclOp);
1524 auto dstDecl = dyn_cast<ClassDeclOp>(dstDeclOp);
1525 if (!srcDecl || !dstDecl)
1526 return emitOpError()
1527 <<
"symbol(s) do not name `moore.class.classdecl` ops: src="
1528 << srcTy.getClassSym() <<
", dst=" << dstTy.getClassSym();
1535 auto baseSym = cur.getBaseAttr();
1539 auto *baseOp = symbolTable.lookupNearestSymbolFrom(op, baseSym);
1540 cur = llvm::dyn_cast_or_null<ClassDeclOp>(baseOp);
1543 return emitOpError() <<
"cannot upcast from " << srcTy.getClassSym() <<
" to "
1544 << dstTy.getClassSym()
1545 <<
" (destination is not a base class)";
1549ClassPropertyRefOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1551 Type instTy = getInstance().getType();
1552 auto handleTy = dyn_cast<moore::ClassHandleType>(instTy);
1554 return emitOpError() <<
"instance must be a !moore.class<@C> value, got "
1558 SymbolRefAttr classSym = handleTy.getClassSym();
1560 return emitOpError(
"instance type is missing a class symbol");
1564 symbolTable.lookupNearestSymbolFrom(getOperation(), classSym);
1566 return emitOpError(
"referenced class symbol `")
1567 << classSym <<
"` was not found";
1568 auto classDecl = dyn_cast<ClassDeclOp>(clsSym);
1570 return emitOpError(
"symbol `")
1571 << classSym <<
"` does not name a `moore.class.classdecl`";
1574 FlatSymbolRefAttr fieldSym = getPropertyAttr();
1576 return emitOpError(
"missing field symbol");
1578 Operation *fldSym = symbolTable.lookupSymbolIn(classDecl, fieldSym.getAttr());
1580 return emitOpError(
"no field `") << fieldSym <<
"` in class " << classSym;
1582 auto fieldDecl = dyn_cast<ClassPropertyDeclOp>(fldSym);
1584 return emitOpError(
"symbol `")
1585 << fieldSym <<
"` is not a `moore.class.propertydecl`";
1588 auto resRefTy = cast<RefType>(getPropertyRef().getType());
1590 return emitOpError(
"result must be a !moore.ref<T>");
1592 Type expectedElemTy = fieldDecl.getPropertyType();
1593 if (resRefTy.getNestedType() != expectedElemTy)
1594 return emitOpError(
"result element type (")
1595 << resRefTy.getNestedType() <<
") does not match field type ("
1596 << expectedElemTy <<
")";
1602VTableLoadMethodOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1603 Operation *op = getOperation();
1605 auto object = getObject();
1606 auto implSym =
object.getType().getClassSym();
1609 Operation *implOp = symbolTable.lookupNearestSymbolFrom(op, implSym);
1611 return emitOpError() <<
"implementing class " << implSym <<
" not found";
1612 auto implClass = cast<moore::ClassDeclOp>(implOp);
1614 StringAttr methodName = getMethodSymAttr().getLeafReference();
1615 if (!methodName || methodName.getValue().empty())
1616 return emitOpError() <<
"empty method name";
1618 moore::ClassDeclOp cursor = implClass;
1619 Operation *methodDeclOp =
nullptr;
1622 while (cursor && !methodDeclOp) {
1623 methodDeclOp = symbolTable.lookupSymbolIn(cursor, methodName);
1626 SymbolRefAttr baseSym = cursor.getBaseAttr();
1629 Operation *baseOp = symbolTable.lookupNearestSymbolFrom(op, baseSym);
1630 cursor = baseOp ? cast<moore::ClassDeclOp>(baseOp) : moore::ClassDeclOp();
1634 return emitOpError() <<
"no method `" << methodName <<
"` found in "
1635 << implClass.getSymName() <<
" or its bases";
1638 auto methodDecl = dyn_cast<moore::ClassMethodDeclOp>(methodDeclOp);
1640 return emitOpError() <<
"`" << methodName
1641 <<
"` is not a method declaration";
1644 auto resFnTy = cast<FunctionType>(getResult().getType());
1645 auto declFnTy = cast<FunctionType>(methodDecl.getFunctionType());
1646 if (resFnTy != declFnTy)
1647 return emitOpError() <<
"result type " << resFnTy
1648 <<
" does not match method erased ABI " << declFnTy;
1653LogicalResult VTableOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1654 Operation *self = getOperation();
1657 SymbolRefAttr name = getSymNameAttr();
1659 return emitOpError(
"requires 'sym_name' SymbolRefAttr");
1662 Operation *rootDef = symbolTable.lookupNearestSymbolFrom(
1663 self, SymbolRefAttr::get(name.getRootReference()));
1665 return emitOpError() <<
"cannot resolve root class symbol '"
1666 << name.getRootReference() <<
"' for sym_name "
1669 if (!isa<ClassDeclOp>(rootDef))
1670 return emitOpError()
1671 <<
"root of sym_name must name a 'moore.class.classdecl', got "
1678LogicalResult VTableOp::verifyRegions() {
1680 for (Operation &op : getBody().front()) {
1681 if (!isa<VTableOp, VTableEntryOp>(op))
1683 "body may only contain 'moore.vtable' or 'moore.vtable_entry' ops");
1685 return mlir::success();
1689VTableEntryOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1690 Operation *self = getOperation();
1694 SymbolRefAttr target = getTargetAttr();
1696 symbolTable.lookupNearestSymbolFrom<func::FuncOp>(self, target);
1698 return emitOpError()
1699 <<
"cannot resolve target symbol to a function operation " << target;
1702 if (!isa<VTableOp>(self->getParentOp()))
1703 return emitOpError(
"must be nested directly inside a 'moore.vtable' op");
1705 Operation *currentOp = self;
1706 VTableOp currentVTable;
1707 bool defined =
false;
1714 while (
auto parentOp = dyn_cast<VTableOp>(currentOp->getParentOp())) {
1715 currentOp = parentOp;
1716 currentVTable = cast<VTableOp>(currentOp);
1718 auto classSymName = currentVTable.getSymName();
1719 ClassDeclOp parentClassDecl =
1720 symbolTable.lookupNearestSymbolFrom<ClassDeclOp>(
1721 parentOp, classSymName.getRootReference());
1722 assert(parentClassDecl &&
"VTableOp must point to a classdeclop");
1724 for (
auto method : parentClassDecl.getBody().getOps<ClassMethodDeclOp>()) {
1726 if (!method.getImpl())
1730 if (method.getSymName() ==
getName() && method.getImplAttr() == target)
1738 else if (method.getSymName() ==
getName() &&
1739 method.getImplAttr() != target && defined)
1740 return emitOpError() <<
"Target " << target
1741 <<
" should be overridden by " << classSymName;
1745 return emitOpError()
1746 <<
"Parent class does not point to any implementation!";
1756#define GET_OP_CLASSES
1757#include "circt/Dialect/Moore/Moore.cpp.inc"
1758#include "circt/Dialect/Moore/MooreEnums.cpp.inc"
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static bool getFieldName(const FieldRef &fieldRef, SmallString< 32 > &string)
static Location getLoc(DefSlot slot)
static OpFoldResult powCommonFolding(MLIRContext *ctxt, Attribute lhs, Attribute rhs)
static ArrayRef< StructLikeMember > getStructMembers(Type type)
static std::optional< uint32_t > getStructFieldIndex(Type type, StringAttr name)
static UnpackedType getStructFieldType(Type type, StringAttr name)
static std::pair< unsigned, UnpackedType > getArrayElements(Type type)
static InstancePath empty
Four-valued arbitrary precision integers.
bool isNegative() const
Determine whether the integer interpreted as a signed number would be negative.
FVInt sext(unsigned bitWidth) const
Sign-extend the integer to a new bit width.
unsigned getSignificantBits() const
Compute the minimum bit width necessary to accurately represent this integer's value and sign.
static FVInt getAllX(unsigned numBits)
Construct an FVInt with all bits set to X.
bool hasUnknown() const
Determine if any bits are X or Z.
unsigned getActiveBits() const
Compute the number of active bits in the value.
unsigned getBitWidth() const
Return the number of bits this integer has.
FVInt trunc(unsigned bitWidth) const
Truncate the integer to a smaller bit width.
A packed SystemVerilog type.
std::optional< unsigned > getBitSize() const
Get the size of this type in bits.
Domain getDomain() const
Get the value domain of this type.
An unpacked SystemVerilog type.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Direction
The direction of a Component or Cell port.
std::string getInstanceName(mlir::func::CallOp callOp)
A helper function to get the instance name.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
ParseResult parseModuleSignature(OpAsmParser &parser, SmallVectorImpl< PortParse > &args, TypeAttr &modType)
New Style parsing.
void printModuleSignatureNew(OpAsmPrinter &p, Region &body, hw::ModuleType modType, ArrayRef< Attribute > portAttrs, ArrayRef< Location > locAttrs)
FunctionType getModuleType(Operation *module)
Return the signature for the specified module as a function type.
Domain
The number of values each bit of a type can assume.
RealWidth
The type of floating point / real number behind a RealType.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
ParseResult parseInputPortList(OpAsmParser &parser, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &inputs, SmallVectorImpl< Type > &inputTypes, ArrayAttr &inputNames)
Parse a list of instance input ports.
void printOutputPortList(OpAsmPrinter &p, Operation *op, TypeRange resultTypes, ArrayAttr resultNames)
Print a list of instance output ports.
void printFVInt(AsmPrinter &p, const FVInt &value)
Print a four-valued integer usign an AsmPrinter.
ParseResult parseFVInt(AsmParser &p, FVInt &result)
Parse a four-valued integer using an AsmParser.
void printInputPortList(OpAsmPrinter &p, Operation *op, OperandRange inputs, TypeRange inputTypes, ArrayAttr inputNames)
Print a list of instance input ports.
ParseResult parseOutputPortList(OpAsmParser &parser, SmallVectorImpl< Type > &resultTypes, ArrayAttr &resultNames)
Parse a list of instance output ports.
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn