22 #include "mlir/IR/Builders.h"
23 #include "mlir/IR/BuiltinTypes.h"
24 #include "mlir/IR/Matchers.h"
25 #include "mlir/IR/PatternMatch.h"
26 #include "mlir/Interfaces/FunctionImplementation.h"
27 #include "llvm/ADT/SmallString.h"
28 #include "llvm/ADT/StringExtras.h"
29 #include "llvm/ADT/TypeSwitch.h"
33 using namespace circt;
35 using mlir::TypedAttr;
41 if (
auto *op = v.getDefiningOp()) {
42 if (
auto attr = op->getAttrOfType<UnitAttr>(
"twoState"))
51 return isa<VerbatimExprOp, VerbatimExprSEOp, GetModportOp,
52 ReadInterfaceSignalOp, ConstantXOp, ConstantZOp, ConstantStrOp,
53 MacroRefExprOp, MacroRefExprSEOp>(op);
59 op->emitError() << op->getName() <<
" should be in a procedural region";
66 op->emitError() << op->getName() <<
" should be in a non-procedural region";
75 Region ®ion = symbolTableOp->getRegion(0);
81 SymbolTable::getSymbolAttrName());
82 for (Block &block : region)
83 for (Operation &nestedOp : block) {
84 auto nameAttr = nestedOp.getAttrOfType<StringAttr>(symbolNameId);
85 if (nameAttr && nameAttr.getValue() == symbol)
87 if (!nestedOp.hasTrait<OpTrait::SymbolTable>() &&
88 nestedOp.getNumRegions()) {
99 SymbolTableCollection &symbolTable) {
100 auto *refOp = symbolTable.lookupNearestSymbolFrom(op, attr);
102 return op->emitError(
"references an undefined symbol: ") << attr;
103 if (!isa<MacroDeclOp>(refOp))
104 return op->emitError(
"must reference a macro declaration");
115 function_ref<
void(Value, StringRef)> setNameFn) {
119 auto isOkCharacter = [](
char c) {
return llvm::isAlnum(c) || c ==
'_'; };
120 auto name = op->getAttrOfType<StringAttr>(
"format_string").getValue();
122 if (name.starts_with(
"`"))
123 name = name.drop_front();
124 name = name.take_while(isOkCharacter);
126 setNameFn(op->getResult(0), name);
130 function_ref<
void(Value, StringRef)> setNameFn) {
135 function_ref<
void(Value, StringRef)> setNameFn) {
144 function_ref<
void(Value, StringRef)> setNameFn) {
145 setNameFn(getResult(), getMacroName());
149 function_ref<
void(Value, StringRef)> setNameFn) {
150 setNameFn(getResult(), getMacroName());
155 FlatSymbolRefAttr macroName) {
157 if (
auto *result = cache->getDefinition(macroName.getAttr()))
158 return cast<MacroDeclOp>(result);
160 auto topLevelModuleOp = op->getParentOfType<ModuleOp>();
161 return topLevelModuleOp.lookupSymbol<MacroDeclOp>(macroName.getValue());
185 MacroRefExprOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
191 MacroRefExprSEOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
196 LogicalResult MacroDefOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
201 LogicalResult MacroRefOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
209 StringRef MacroDeclOp::getMacroIdentifier() {
210 return getVerilogName().value_or(getSymName());
218 function_ref<
void(Value, StringRef)> setNameFn) {
219 SmallVector<char, 32> specialNameBuffer;
220 llvm::raw_svector_ostream specialName(specialNameBuffer);
222 setNameFn(getResult(), specialName.str());
228 return emitError(
"unsupported type");
233 function_ref<
void(Value, StringRef)> setNameFn) {
234 SmallVector<char, 32> specialNameBuffer;
235 llvm::raw_svector_ostream specialName(specialNameBuffer);
237 setNameFn(getResult(), specialName.str());
243 return emitError(
"unsupported type");
253 auto nameAttr = (*this)->getAttrOfType<StringAttr>(
"name");
254 if (!nameAttr.getValue().empty())
255 setNameFn(getResult(), nameAttr.getValue());
270 std::optional<OpAsmParser::UnresolvedOperand> &initValue,
271 mlir::Type &initType) {
272 if (!initValue.has_value())
277 return p.emitError(p.getCurrentLocation(),
"expected inout type for reg");
279 initType = ioType.getElementType();
284 mlir::Type regType, mlir::Value initValue,
285 mlir::Type initType) {}
287 void RegOp::build(OpBuilder &builder, OperationState &odsState,
288 Type
elementType, StringAttr name, hw::InnerSymAttr innerSym,
289 mlir::Value initValue) {
291 name = builder.getStringAttr(
"");
292 odsState.addAttribute(
"name", name);
298 odsState.addOperands(initValue);
305 auto nameAttr = (*this)->getAttrOfType<StringAttr>(
"name");
306 if (!nameAttr.getValue().empty())
307 setNameFn(getResult(), nameAttr.getValue());
310 std::optional<size_t> RegOp::getTargetResultIndex() {
return 0; }
319 if (op.getInnerSymAttr())
323 for (
auto *user : op.getResult().getUsers())
324 if (!isa<AssignOp>(user))
328 for (
auto *user : llvm::make_early_inc_range(op.getResult().getUsers()))
329 rewriter.eraseOp(user);
332 rewriter.eraseOp(op);
340 void LogicOp::build(OpBuilder &builder, OperationState &odsState,
342 hw::InnerSymAttr innerSym) {
344 name = builder.getStringAttr(
"");
345 odsState.addAttribute(
"name", name);
356 auto nameAttr = (*this)->getAttrOfType<StringAttr>(
"name");
357 if (!nameAttr.getValue().empty())
358 setNameFn(getResult(), nameAttr.getValue());
361 std::optional<size_t> LogicOp::getTargetResultIndex() {
return 0; }
371 void IfDefOp::build(OpBuilder &builder, OperationState &result, StringRef cond,
372 std::function<
void()> thenCtor,
373 std::function<
void()> elseCtor) {
374 build(builder, result, builder.getStringAttr(cond), std::move(thenCtor),
375 std::move(elseCtor));
378 void IfDefOp::build(OpBuilder &builder, OperationState &result, StringAttr cond,
379 std::function<
void()> thenCtor,
380 std::function<
void()> elseCtor) {
382 std::move(thenCtor), std::move(elseCtor));
385 void IfDefOp::build(OpBuilder &builder, OperationState &result,
386 FlatSymbolRefAttr cond, std::function<
void()> thenCtor,
387 std::function<
void()> elseCtor) {
389 std::move(thenCtor), std::move(elseCtor));
392 void IfDefOp::build(OpBuilder &builder, OperationState &result,
393 MacroIdentAttr cond, std::function<
void()> thenCtor,
394 std::function<
void()> elseCtor) {
395 OpBuilder::InsertionGuard guard(builder);
397 result.addAttribute(
"cond", cond);
398 builder.createBlock(result.addRegion());
404 Region *elseRegion = result.addRegion();
406 builder.createBlock(elseRegion);
411 LogicalResult IfDefOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
418 if (!op.getThenBlock()->empty())
421 if (op.hasElse() && !op.getElseBlock()->empty())
424 rewriter.eraseOp(op);
436 void IfDefProceduralOp::build(OpBuilder &builder, OperationState &result,
437 StringRef cond, std::function<
void()> thenCtor,
438 std::function<
void()> elseCtor) {
439 build(builder, result, builder.getStringAttr(cond), std::move(thenCtor),
440 std::move(elseCtor));
443 void IfDefProceduralOp::build(OpBuilder &builder, OperationState &result,
444 StringAttr cond, std::function<
void()> thenCtor,
445 std::function<
void()> elseCtor) {
447 std::move(thenCtor), std::move(elseCtor));
450 void IfDefProceduralOp::build(OpBuilder &builder, OperationState &result,
451 FlatSymbolRefAttr cond,
452 std::function<
void()> thenCtor,
453 std::function<
void()> elseCtor) {
455 std::move(thenCtor), std::move(elseCtor));
458 void IfDefProceduralOp::build(OpBuilder &builder, OperationState &result,
460 std::function<
void()> thenCtor,
461 std::function<
void()> elseCtor) {
462 OpBuilder::InsertionGuard guard(builder);
464 result.addAttribute(
"cond", cond);
465 builder.createBlock(result.addRegion());
471 Region *elseRegion = result.addRegion();
473 builder.createBlock(elseRegion);
479 PatternRewriter &rewriter) {
484 IfDefProceduralOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
492 void IfOp::build(OpBuilder &builder, OperationState &result, Value cond,
493 std::function<
void()> thenCtor,
494 std::function<
void()> elseCtor) {
495 OpBuilder::InsertionGuard guard(builder);
497 result.addOperands(cond);
498 builder.createBlock(result.addRegion());
504 Region *elseRegion = result.addRegion();
506 builder.createBlock(elseRegion);
514 assert(llvm::hasSingleElement(region) &&
"expected single-region block");
515 Block *fromBlock = ®ion.front();
517 op->getBlock()->getOperations().splice(Block::iterator(op),
518 fromBlock->getOperations());
526 if (
auto constant = op.getCond().getDefiningOp<
hw::ConstantOp>()) {
528 if (constant.getValue().isAllOnes())
530 else if (!op.getElseRegion().empty())
533 rewriter.eraseOp(op);
539 if (!op.getThenBlock()->empty() && op.hasElse() &&
540 op.getElseBlock()->empty()) {
541 rewriter.eraseBlock(op.getElseBlock());
548 if (!op.getThenBlock()->empty())
552 if (!op.hasElse() || op.getElseBlock()->empty()) {
553 rewriter.eraseOp(op);
564 auto *thenBlock = op.getThenBlock(), *elseBlock = op.getElseBlock();
567 thenBlock->getOperations().splice(thenBlock->end(),
568 elseBlock->getOperations());
569 rewriter.eraseBlock(elseBlock);
579 AlwaysOp::Condition AlwaysOp::getCondition(
size_t idx) {
580 return Condition{EventControl(cast<IntegerAttr>(getEvents()[idx]).
getInt()),
584 void AlwaysOp::build(OpBuilder &builder, OperationState &result,
585 ArrayRef<sv::EventControl> events, ArrayRef<Value> clocks,
586 std::function<
void()> bodyCtor) {
587 assert(events.size() == clocks.size() &&
588 "mismatch between event and clock list");
589 OpBuilder::InsertionGuard guard(builder);
591 SmallVector<Attribute> eventAttrs;
592 for (
auto event : events)
593 eventAttrs.push_back(
594 builder.getI32IntegerAttr(
static_cast<int32_t
>(event)));
595 result.addAttribute(
"events", builder.getArrayAttr(eventAttrs));
596 result.addOperands(clocks);
599 builder.createBlock(result.addRegion());
608 if (getEvents().size() != getNumOperands())
609 return emitError(
"different number of operands and events");
614 OpAsmParser &p, Attribute &eventsAttr,
615 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &clocksOperands) {
618 SmallVector<Attribute> events;
620 auto loc = p.getCurrentLocation();
622 if (!p.parseOptionalKeyword(&keyword)) {
624 auto kind = sv::symbolizeEventControl(keyword);
625 if (!kind.has_value())
626 return p.emitError(loc,
"expected 'posedge', 'negedge', or 'edge'");
627 auto eventEnum =
static_cast<int32_t
>(*kind);
628 events.push_back(p.getBuilder().getI32IntegerAttr(eventEnum));
630 clocksOperands.push_back({});
631 if (p.parseOperand(clocksOperands.back()))
634 if (failed(p.parseOptionalComma()))
636 if (p.parseKeyword(&keyword))
640 eventsAttr = p.getBuilder().getArrayAttr(events);
645 OperandRange operands) {
646 for (
size_t i = 0, e = op.getNumConditions(); i != e; ++i) {
649 auto cond = op.getCondition(i);
650 p << stringifyEventControl(cond.event);
652 p.printOperand(cond.value);
660 void AlwaysFFOp::build(OpBuilder &builder, OperationState &result,
661 EventControl clockEdge, Value clock,
662 std::function<
void()> bodyCtor) {
663 OpBuilder::InsertionGuard guard(builder);
666 "clockEdge", builder.getI32IntegerAttr(
static_cast<int32_t
>(clockEdge)));
667 result.addOperands(clock);
670 builder.getI32IntegerAttr(
static_cast<int32_t
>(ResetType::NoReset)));
673 builder.createBlock(result.addRegion());
682 void AlwaysFFOp::build(OpBuilder &builder, OperationState &result,
683 EventControl clockEdge, Value clock,
684 ResetType resetStyle, EventControl resetEdge,
685 Value reset, std::function<
void()> bodyCtor,
686 std::function<
void()> resetCtor) {
687 OpBuilder::InsertionGuard guard(builder);
690 "clockEdge", builder.getI32IntegerAttr(
static_cast<int32_t
>(clockEdge)));
691 result.addOperands(clock);
692 result.addAttribute(
"resetStyle", builder.getI32IntegerAttr(
693 static_cast<int32_t
>(resetStyle)));
695 "resetEdge", builder.getI32IntegerAttr(
static_cast<int32_t
>(resetEdge)));
696 result.addOperands(reset);
699 builder.createBlock(result.addRegion());
705 builder.createBlock(result.addRegion());
715 void AlwaysCombOp::build(OpBuilder &builder, OperationState &result,
716 std::function<
void()> bodyCtor) {
717 OpBuilder::InsertionGuard guard(builder);
719 builder.createBlock(result.addRegion());
729 void InitialOp::build(OpBuilder &builder, OperationState &result,
730 std::function<
void()> bodyCtor) {
731 OpBuilder::InsertionGuard guard(builder);
733 builder.createBlock(result.addRegion());
756 llvm_unreachable(
"invalid casez PatternBit");
761 return CasePatternBit(
unsigned(intAttr.getValue()[bitNumber * 2]) +
762 2 *
unsigned(intAttr.getValue()[bitNumber * 2 + 1]));
766 for (
size_t i = 0, e =
getWidth(); i != e; ++i)
773 for (
size_t i = 0, e =
getWidth(); i != e; ++i)
779 SmallVector<CasePatternBit> result;
780 result.reserve(value.getBitWidth());
781 for (
size_t i = 0, e = value.getBitWidth(); i != e; ++i)
797 MLIRContext *context)
799 APInt
pattern(bits.size() * 2, 0);
800 for (
auto elt : llvm::reverse(bits)) {
808 auto CaseOp::getCases() -> SmallVector<CaseInfo, 4> {
809 SmallVector<CaseInfo, 4> result;
810 assert(getCasePatterns().size() == getNumRegions() &&
811 "case pattern / region count mismatch");
812 size_t nextRegion = 0;
813 for (
auto elt : getCasePatterns()) {
814 llvm::TypeSwitch<Attribute>(elt)
815 .Case<hw::EnumFieldAttr>([&](
auto enumAttr) {
816 result.push_back({std::make_unique<CaseEnumPattern>(enumAttr),
817 &getRegion(nextRegion++).front()});
819 .Case<IntegerAttr>([&](
auto intAttr) {
820 result.push_back({std::make_unique<CaseBitPattern>(intAttr),
821 &getRegion(nextRegion++).front()});
823 .Case<CaseDefaultPattern::AttrType>([&](
auto) {
824 result.push_back({std::make_unique<CaseDefaultPattern>(getContext()),
825 &getRegion(nextRegion++).front()});
828 assert(
false &&
"invalid case pattern attribute type");
836 return cast<hw::EnumFieldAttr>(
enumAttr).getField();
846 ParseResult CaseOp::parse(OpAsmParser &parser, OperationState &result) {
847 auto &builder = parser.getBuilder();
849 OpAsmParser::UnresolvedOperand condOperand;
852 auto loc = parser.getCurrentLocation();
855 if (!parser.parseOptionalKeyword(&keyword, {
"case",
"casex",
"casez"})) {
856 auto kind = symbolizeCaseStmtType(keyword);
857 auto caseEnum =
static_cast<int32_t
>(kind.value());
858 result.addAttribute(
"caseStyle", builder.getI32IntegerAttr(caseEnum));
862 if (!parser.parseOptionalKeyword(
863 &keyword, {
"plain",
"priority",
"unique",
"unique0"})) {
864 auto kind = symbolizeValidationQualifierTypeEnum(keyword);
865 result.addAttribute(
"validationQualifier",
867 builder.getContext(), kind.value()));
870 if (parser.parseOperand(condOperand) || parser.parseColonType(condType) ||
871 parser.parseOptionalAttrDict(result.attributes) ||
872 parser.resolveOperand(condOperand, condType, result.operands))
877 hw::EnumType enumType = dyn_cast<hw::EnumType>(canonicalCondType);
878 unsigned condWidth = 0;
880 if (!result.operands[0].getType().isSignlessInteger())
881 return parser.emitError(loc,
"condition must have signless integer type");
882 condWidth = condType.getIntOrFloatBitWidth();
886 SmallVector<Attribute> casePatterns;
887 SmallVector<CasePatternBit, 16> caseBits;
889 if (succeeded(parser.parseOptionalKeyword(
"default"))) {
890 casePatterns.push_back(CaseDefaultPattern(parser.getContext()).attr());
891 }
else if (failed(parser.parseOptionalKeyword(
"case"))) {
894 }
else if (enumType) {
898 if (parser.parseKeyword(&caseVal))
901 if (!enumType.contains(caseVal))
902 return parser.emitError(loc)
903 <<
"case value '" + caseVal +
"' is not a member of enum type "
905 casePatterns.push_back(
907 builder.getStringAttr(caseVal), condType));
912 loc = parser.getCurrentLocation();
913 if (parser.parseKeyword(&caseVal))
916 if (caseVal.front() !=
'b')
917 return parser.emitError(loc,
"expected case value starting with 'b'");
918 caseVal = caseVal.drop_front();
921 for (; !caseVal.empty(); caseVal = caseVal.drop_front()) {
923 switch (caseVal.front()) {
937 return parser.emitError(loc,
"unexpected case bit '")
938 << caseVal.front() <<
"'";
940 caseBits.push_back(bit);
943 if (caseVal.size() > condWidth)
944 return parser.emitError(loc,
"too many bits specified in pattern");
945 std::reverse(caseBits.begin(), caseBits.end());
948 if (caseBits.size() < condWidth)
951 auto resultPattern = CaseBitPattern(caseBits, builder.getContext());
952 casePatterns.push_back(resultPattern.attr());
957 auto caseRegion = std::make_unique<Region>();
958 if (parser.parseColon() || parser.parseRegion(*caseRegion))
960 result.addRegion(std::move(caseRegion));
963 result.addAttribute(
"casePatterns", builder.getArrayAttr(casePatterns));
967 void CaseOp::print(OpAsmPrinter &p) {
969 if (getCaseStyle() == CaseStmtType::CaseXStmt)
971 else if (getCaseStyle() == CaseStmtType::CaseZStmt)
974 if (getValidationQualifier() !=
975 ValidationQualifierTypeEnum::ValidationQualifierPlain)
976 p << stringifyValidationQualifierTypeEnum(getValidationQualifier()) <<
' ';
978 p << getCond() <<
" : " << getCond().getType();
979 p.printOptionalAttrDict(
981 {
"casePatterns",
"caseStyle",
"validationQualifier"});
983 for (
auto &caseInfo : getCases()) {
985 auto &
pattern = caseInfo.pattern;
987 llvm::TypeSwitch<CasePattern *>(
pattern.get())
988 .Case<CaseBitPattern>([&](
auto bitPattern) {
990 for (
size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
991 p <<
getLetter(bitPattern->getBit(e - bit - 1));
993 .Case<CaseEnumPattern>([&](
auto enumPattern) {
994 p <<
"case " << enumPattern->getFieldValue();
996 .Case<CaseDefaultPattern>([&](
auto) { p <<
"default"; })
997 .Default([&](
auto) {
assert(
false &&
"unhandled case pattern"); });
1000 p.printRegion(*caseInfo.block->getParent(),
false,
1008 return emitError(
"condition must have either integer or enum type");
1011 if (getCasePatterns().size() != getNumRegions())
1012 return emitOpError(
"case pattern / region count mismatch");
1019 OpBuilder &builder, OperationState &result, CaseStmtType caseStyle,
1020 ValidationQualifierTypeEnum validationQualifier, Value cond,
1022 std::function<std::unique_ptr<CasePattern>(
size_t)> caseCtor) {
1023 result.addOperands(cond);
1024 result.addAttribute(
"caseStyle",
1026 result.addAttribute(
"validationQualifier",
1028 builder.getContext(), validationQualifier));
1029 SmallVector<Attribute> casePatterns;
1031 OpBuilder::InsertionGuard guard(builder);
1034 for (
size_t i = 0, e = numCases; i != e; ++i) {
1035 builder.createBlock(result.addRegion());
1036 casePatterns.push_back(caseCtor(i)->attr());
1039 result.addAttribute(
"casePatterns", builder.getArrayAttr(casePatterns));
1044 if (op.getCaseStyle() == CaseStmtType::CaseStmt)
1046 if (isa<hw::EnumType>(op.getCond().getType()))
1049 auto caseInfo = op.getCases();
1050 bool noXZ = llvm::all_of(caseInfo, [](
const CaseInfo &ci) {
1051 return !ci.pattern.get()->hasX() && !ci.pattern.get()->hasZ();
1053 bool noX = llvm::all_of(caseInfo, [](
const CaseInfo &ci) {
1054 if (isa<CaseDefaultPattern>(ci.pattern))
1056 return !ci.pattern.get()->hasX();
1058 bool noZ = llvm::all_of(caseInfo, [](
const CaseInfo &ci) {
1059 if (isa<CaseDefaultPattern>(ci.pattern))
1061 return !ci.pattern.get()->hasZ();
1064 if (op.getCaseStyle() == CaseStmtType::CaseXStmt) {
1066 rewriter.modifyOpInPlace(op, [&]() {
1067 op.setCaseStyleAttr(
1073 rewriter.modifyOpInPlace(op, [&]() {
1074 op.setCaseStyleAttr(
1081 if (op.getCaseStyle() == CaseStmtType::CaseZStmt && noZ) {
1082 rewriter.modifyOpInPlace(op, [&]() {
1083 op.setCaseStyleAttr(
1096 void OrderedOutputOp::build(OpBuilder &builder, OperationState &result,
1097 std::function<
void()> body) {
1098 OpBuilder::InsertionGuard guard(builder);
1100 builder.createBlock(result.addRegion());
1111 void ForOp::build(OpBuilder &builder, OperationState &result,
1112 int64_t lowerBound, int64_t upperBound, int64_t step,
1113 IntegerType type, StringRef name,
1114 llvm::function_ref<
void(BlockArgument)> body) {
1115 auto lb = builder.create<
hw::ConstantOp>(result.location, type, lowerBound);
1118 build(builder, result, lb, ub, st, name, body);
1120 void ForOp::build(OpBuilder &builder, OperationState &result, Value lowerBound,
1121 Value upperBound, Value step, StringRef name,
1122 llvm::function_ref<
void(BlockArgument)> body) {
1123 OpBuilder::InsertionGuard guard(builder);
1124 build(builder, result, lowerBound, upperBound, step, name);
1125 auto *region = result.regions.front().get();
1126 builder.createBlock(region);
1127 BlockArgument blockArgument =
1128 region->addArgument(lowerBound.getType(), result.location);
1131 body(blockArgument);
1134 void ForOp::getAsmBlockArgumentNames(mlir::Region ®ion,
1136 auto *block = ®ion.front();
1137 setNameFn(block->getArgument(0), getInductionVarNameAttr());
1140 ParseResult ForOp::parse(OpAsmParser &parser, OperationState &result) {
1141 auto &builder = parser.getBuilder();
1144 OpAsmParser::Argument inductionVariable;
1145 OpAsmParser::UnresolvedOperand lb, ub, step;
1147 SmallVector<OpAsmParser::Argument, 4> regionArgs;
1150 if (parser.parseOperand(inductionVariable.ssaName) || parser.parseEqual() ||
1152 parser.parseOperand(lb) || parser.parseKeyword(
"to") ||
1153 parser.parseOperand(ub) || parser.parseKeyword(
"step") ||
1154 parser.parseOperand(step) || parser.parseColon() ||
1155 parser.parseType(type))
1158 regionArgs.push_back(inductionVariable);
1161 regionArgs.front().type = type;
1162 if (parser.resolveOperand(lb, type, result.operands) ||
1163 parser.resolveOperand(ub, type, result.operands) ||
1164 parser.resolveOperand(step, type, result.operands))
1168 Region *body = result.addRegion();
1169 if (parser.parseRegion(*body, regionArgs))
1173 if (parser.parseOptionalAttrDict(result.attributes))
1176 if (!inductionVariable.ssaName.name.empty()) {
1177 if (!
isdigit(inductionVariable.ssaName.name[1]))
1179 result.attributes.append(
1180 {builder.getStringAttr(
"inductionVarName"),
1181 builder.getStringAttr(inductionVariable.ssaName.name.drop_front())});
1187 void ForOp::print(OpAsmPrinter &p) {
1188 p <<
" " << getInductionVar() <<
" = " << getLowerBound() <<
" to "
1189 << getUpperBound() <<
" step " << getStep();
1190 p <<
" : " << getInductionVar().getType() <<
' ';
1191 p.printRegion(getRegion(),
1194 p.printOptionalAttrDict((*this)->getAttrs(), {
"inductionVarName"});
1199 if (matchPattern(op.getLowerBound(), mlir::m_ConstantInt(&lb)) &&
1200 matchPattern(op.getUpperBound(), mlir::m_ConstantInt(&ub)) &&
1201 matchPattern(op.getStep(), mlir::m_ConstantInt(&step)) &&
1204 rewriter.replaceAllUsesWith(op.getInductionVar(), op.getLowerBound());
1206 rewriter.eraseOp(op);
1217 if (isa<sv::WireOp>(getDest().getDefiningOp()))
1219 "Verilog disallows procedural assignment to a net type (did you intend "
1220 "to use a variable type, e.g., sv.reg?)");
1225 if (isa<sv::WireOp>(getDest().getDefiningOp()))
1227 "Verilog disallows procedural assignment to a net type (did you intend "
1228 "to use a variable type, e.g., sv.reg?)");
1241 static std::optional<ArraySlice> getArraySlice(Value v) {
1242 auto *op = v.getDefiningOp();
1244 return std::nullopt;
1245 return TypeSwitch<Operation *, std::optional<ArraySlice>>(op)
1246 .Case<hw::ArrayGetOp, ArrayIndexInOutOp>(
1247 [](
auto arrayIndex) -> std::optional<ArraySlice> {
1249 arrayIndex.getIndex()
1250 .template getDefiningOp<hw::ConstantOp>();
1252 return std::nullopt;
1253 return ArraySlice{arrayIndex.getInput(),
1258 -> std::optional<ArraySlice> {
1259 auto constant = slice.getLowIndex().getDefiningOp<
hw::ConstantOp>();
1261 return std::nullopt;
1263 slice.getInput(), constant,
1265 hw::type_cast<hw::ArrayType>(slice.getType()).getNumElements()};
1267 .Case<sv::IndexedPartSelectInOutOp>(
1268 [](sv::IndexedPartSelectInOutOp index)
1269 -> std::optional<ArraySlice> {
1271 if (!constant || index.getDecrement())
1272 return std::nullopt;
1273 return ArraySlice{index.getInput(),
1277 .Default([](
auto) {
return std::nullopt; });
1281 static std::optional<std::pair<ArraySlice, ArraySlice>>
1282 getAssignedRange(Operation *op) {
1283 assert((isa<PAssignOp, BPAssignOp>(op) &&
"assignments are expected"));
1284 auto srcRange = ArraySlice::getArraySlice(op->getOperand(1));
1286 return std::nullopt;
1287 auto destRange = ArraySlice::getArraySlice(op->getOperand(0));
1289 return std::nullopt;
1291 return std::make_pair(*destRange, *srcRange);
1302 template <
typename AssignTy>
1304 PatternRewriter &rewriter) {
1306 auto assignedRangeOpt = ArraySlice::getAssignedRange(op);
1307 if (!assignedRangeOpt)
1310 auto [dest, src] = *assignedRangeOpt;
1311 AssignTy nextAssign = dyn_cast_or_null<AssignTy>(op->getNextNode());
1312 bool changed =
false;
1313 SmallVector<Location> loc{op.getLoc()};
1315 while (nextAssign) {
1316 auto nextAssignedRange = ArraySlice::getAssignedRange(nextAssign);
1317 if (!nextAssignedRange)
1319 auto [nextDest, nextSrc] = *nextAssignedRange;
1321 if (dest.array != nextDest.array || src.array != nextSrc.array ||
1322 !
hw::isOffset(dest.start, nextDest.start, dest.size) ||
1326 dest.size += nextDest.size;
1327 src.size += nextSrc.size;
1329 loc.push_back(nextAssign.getLoc());
1330 rewriter.eraseOp(nextAssign);
1331 nextAssign = dyn_cast_or_null<AssignTy>(op->getNextNode());
1339 hw::type_cast<hw::ArrayType>(src.array.getType()).getElementType(),
1341 auto newDest = rewriter.create<sv::IndexedPartSelectInOutOp>(
1342 op.getLoc(), dest.array, dest.start, dest.size);
1344 src.array, src.start);
1345 auto newLoc = rewriter.getFusedLoc(loc);
1346 auto newOp = rewriter.replaceOpWithNewOp<AssignTy>(op, newDest, newSrc);
1347 newOp->setLoc(newLoc);
1356 PatternRewriter &rewriter) {
1364 void InterfaceOp::build(OpBuilder &builder, OperationState &result,
1365 StringRef sym_name, std::function<
void()> body) {
1366 OpBuilder::InsertionGuard guard(builder);
1368 result.addAttribute(::SymbolTable::getSymbolAttrName(),
1369 builder.getStringAttr(sym_name));
1370 builder.createBlock(result.addRegion());
1375 ModportType InterfaceOp::getModportType(StringRef modportName) {
1376 assert(lookupSymbol<InterfaceModportOp>(modportName) &&
1377 "Modport symbol not found.");
1378 auto *
ctxt = getContext();
1385 Type InterfaceOp::getSignalType(StringRef signalName) {
1386 InterfaceSignalOp signal = lookupSymbol<InterfaceSignalOp>(signalName);
1387 assert(signal &&
"Interface signal symbol not found.");
1388 return signal.getType();
1392 ArrayAttr &portsAttr) {
1394 auto context = parser.getBuilder().getContext();
1396 SmallVector<Attribute, 8> ports;
1397 auto parseElement = [&]() -> ParseResult {
1398 auto direction = ModportDirectionAttr::parse(parser, {});
1402 FlatSymbolRefAttr signal;
1403 if (parser.parseAttribute(signal))
1407 context, cast<ModportDirectionAttr>(direction), signal));
1410 if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
1419 ArrayAttr portsAttr) {
1421 llvm::interleaveComma(portsAttr, p, [&](Attribute attr) {
1422 auto port = cast<ModportStructAttr>(attr);
1423 p << stringifyEnum(port.getDirection().getValue());
1425 p.printSymbolName(port.getSignal().getRootReference().getValue());
1430 void InterfaceSignalOp::build(mlir::OpBuilder &builder,
1431 ::mlir::OperationState &state, StringRef name,
1436 void InterfaceModportOp::build(OpBuilder &builder, OperationState &state,
1437 StringRef name, ArrayRef<StringRef> inputs,
1438 ArrayRef<StringRef> outputs) {
1439 auto *
ctxt = builder.getContext();
1440 SmallVector<Attribute, 8> directions;
1443 for (
auto input : inputs)
1446 for (
auto output : outputs)
1452 std::optional<size_t> InterfaceInstanceOp::getTargetResultIndex() {
1454 return std::nullopt;
1460 setNameFn(getResult(),
getName());
1466 return emitOpError(
"requires non-empty name");
1471 InterfaceInstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1472 auto *symtable = SymbolTable::getNearestSymbolTable(*
this);
1474 return emitError(
"sv.interface.instance must exist within a region "
1475 "which has a symbol table.");
1476 auto ifaceTy = getType();
1477 auto *referencedOp =
1478 symbolTable.lookupSymbolIn(symtable, ifaceTy.getInterface());
1480 return emitError(
"Symbol not found: ") << ifaceTy.getInterface() <<
".";
1481 if (!isa<InterfaceOp>(referencedOp))
1482 return emitError(
"Symbol ")
1483 << ifaceTy.getInterface() <<
" is not an InterfaceOp.";
1490 GetModportOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1491 auto *symtable = SymbolTable::getNearestSymbolTable(*
this);
1493 return emitError(
"sv.interface.instance must exist within a region "
1494 "which has a symbol table.");
1496 auto ifaceTy = getType();
1497 auto *referencedOp =
1498 symbolTable.lookupSymbolIn(symtable, ifaceTy.getModport());
1500 return emitError(
"Symbol not found: ") << ifaceTy.getModport() <<
".";
1501 if (!isa<InterfaceModportOp>(referencedOp))
1502 return emitError(
"Symbol ")
1503 << ifaceTy.getModport() <<
" is not an InterfaceModportOp.";
1507 void GetModportOp::build(OpBuilder &builder, OperationState &state, Value value,
1509 auto ifaceTy = dyn_cast<InterfaceType>(value.getType());
1510 assert(ifaceTy &&
"GetModportOp expects an InterfaceType.");
1521 GetModportOp::getReferencedDecl(
const hw::HWSymbolCache &cache) {
1522 return dyn_cast_or_null<InterfaceModportOp>(
1523 cache.getDefinition(getFieldAttr()));
1526 void ReadInterfaceSignalOp::build(OpBuilder &builder, OperationState &state,
1527 Value iface, StringRef signalName) {
1528 auto ifaceTy = dyn_cast<InterfaceType>(iface.getType());
1529 assert(ifaceTy &&
"ReadInterfaceSignalOp expects an InterfaceType.");
1531 InterfaceOp ifaceDefOp = SymbolTable::lookupNearestSymbolFrom<InterfaceOp>(
1532 iface.getDefiningOp(), ifaceTy.getInterface());
1534 "ReadInterfaceSignalOp could not resolve an InterfaceOp.");
1535 build(builder, state, ifaceDefOp.getSignalType(signalName), iface, fieldAttr);
1541 ReadInterfaceSignalOp::getReferencedDecl(
const hw::HWSymbolCache &cache) {
1542 return dyn_cast_or_null<InterfaceSignalOp>(
1543 cache.getDefinition(getSignalNameAttr()));
1547 FlatSymbolRefAttr &signalName) {
1548 SymbolRefAttr fullSym;
1549 if (p.parseAttribute(fullSym) || fullSym.getNestedReferences().size() != 1)
1552 auto *
ctxt = p.getBuilder().getContext();
1560 FlatSymbolRefAttr signalName) {
1561 InterfaceType ifaceTy = dyn_cast<InterfaceType>(type);
1562 assert(ifaceTy &&
"Expected an InterfaceType");
1569 auto ifaceTy = dyn_cast<InterfaceType>(ifaceVal.getType());
1572 InterfaceOp iface = SymbolTable::lookupNearestSymbolFrom<InterfaceOp>(
1573 ifaceVal.getDefiningOp(), ifaceTy.getInterface());
1576 InterfaceSignalOp signal = iface.lookupSymbol<InterfaceSignalOp>(signalName);
1583 InterfaceInstanceOp::getReferencedInterface(
const hw::HWSymbolCache *cache) {
1584 FlatSymbolRefAttr
interface = getInterfaceType().getInterface();
1586 if (
auto *result = cache->getDefinition(interface))
1589 auto topLevelModuleOp = (*this)->getParentOfType<ModuleOp>();
1590 if (!topLevelModuleOp)
1593 return topLevelModuleOp.lookupSymbol(interface);
1608 void WireOp::build(OpBuilder &builder, OperationState &odsState,
1610 hw::InnerSymAttr innerSym) {
1612 name = builder.getStringAttr(
"");
1617 odsState.addAttribute(
"name", name);
1625 auto nameAttr = (*this)->getAttrOfType<StringAttr>(
"name");
1626 if (!nameAttr.getValue().empty())
1627 setNameFn(getResult(), nameAttr.getValue());
1630 std::optional<size_t> WireOp::getTargetResultIndex() {
return 0; }
1639 if (wire.getInnerSymAttr())
1644 SmallVector<sv::ReadInOutOp> reads;
1647 for (
auto *user : wire->getUsers()) {
1648 if (
auto read = dyn_cast<sv::ReadInOutOp>(user)) {
1649 reads.push_back(read);
1654 auto assign = dyn_cast<sv::AssignOp>(user);
1657 if (!assign || write)
1673 connected = rewriter.
create<ConstantZOp>(
1675 cast<InOutType>(wire.getResult().getType()).getElementType());
1676 }
else if (isa<hw::HWModuleOp>(write->getParentOp()))
1677 connected = write.getSrc();
1684 if (
auto *connectedOp = connected.getDefiningOp())
1685 if (!wire.getName().empty())
1686 rewriter.modifyOpInPlace(connectedOp, [&] {
1687 connectedOp->setAttr(
"sv.namehint", wire.getNameAttr());
1691 for (
auto read : reads)
1692 rewriter.replaceOp(read, connected);
1696 rewriter.eraseOp(write);
1697 rewriter.eraseOp(wire);
1707 auto elemTy = cast<hw::InOutType>(type).getElementType();
1708 if (isa<IntegerType>(elemTy))
1710 if (isa<hw::ArrayType>(elemTy))
1712 cast<hw::ArrayType>(elemTy).getElementType(), width));
1716 LogicalResult IndexedPartSelectInOutOp::inferReturnTypes(
1717 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
1718 DictionaryAttr attrs, mlir::OpaqueProperties properties,
1719 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
1720 Adaptor adaptor(operands, attrs, properties, regions);
1721 auto width = adaptor.getWidthAttr();
1726 width.getValue().getZExtValue());
1729 results.push_back(typ);
1734 unsigned inputWidth = 0, resultWidth = 0;
1736 auto inputElemTy = cast<InOutType>(getInput().getType()).getElementType();
1737 auto resultElemTy = cast<InOutType>(getType()).getElementType();
1738 if (
auto i = dyn_cast<IntegerType>(inputElemTy))
1739 inputWidth = i.getWidth();
1740 else if (
auto i = hw::type_cast<hw::ArrayType>(inputElemTy))
1741 inputWidth = i.getNumElements();
1743 return emitError(
"input element type must be Integer or Array");
1745 if (
auto resType = dyn_cast<IntegerType>(resultElemTy))
1746 resultWidth = resType.getWidth();
1747 else if (
auto resType = hw::type_cast<hw::ArrayType>(resultElemTy))
1748 resultWidth = resType.getNumElements();
1750 return emitError(
"result element type must be Integer or Array");
1752 if (opWidth > inputWidth)
1753 return emitError(
"slice width should not be greater than input width");
1754 if (opWidth != resultWidth)
1755 return emitError(
"result width must be equal to slice width");
1759 OpFoldResult IndexedPartSelectInOutOp::fold(FoldAdaptor) {
1760 if (getType() == getInput().getType())
1769 LogicalResult IndexedPartSelectOp::inferReturnTypes(
1770 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
1771 DictionaryAttr attrs, mlir::OpaqueProperties properties,
1772 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
1773 Adaptor adaptor(operands, attrs, properties, regions);
1774 auto width = adaptor.getWidthAttr();
1785 unsigned resultWidth = cast<IntegerType>(getType()).getWidth();
1786 unsigned inputWidth = cast<IntegerType>(getInput().getType()).getWidth();
1788 if (opWidth > inputWidth)
1789 return emitError(
"slice width should not be greater than input width");
1790 if (opWidth != resultWidth)
1791 return emitError(
"result width must be equal to slice width");
1799 LogicalResult StructFieldInOutOp::inferReturnTypes(
1800 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
1801 DictionaryAttr attrs, mlir::OpaqueProperties properties,
1802 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
1803 Adaptor adaptor(operands, attrs, properties, regions);
1804 auto field = adaptor.getFieldAttr();
1809 auto resultType = structType.getFieldType(field);
1823 if (getAliases().size() < 2)
1824 return emitOpError(
"alias must have at least two operands");
1837 for (
auto &op : llvm::reverse(body->getOperations())) {
1838 if (
auto instance = dyn_cast<Op>(op)) {
1839 if (
auto innerSym = instance.getInnerSym())
1840 if (innerSym->getSymName() == name)
1844 if (
auto ifdef = dyn_cast<IfDefOp>(op)) {
1846 findInstanceSymbolInBlock<Op>(name, ifdef.getThenBlock()))
1848 if (ifdef.hasElse())
1850 findInstanceSymbolInBlock<Op>(name, ifdef.getElseBlock()))
1857 hw::InstanceOp BindOp::getReferencedInstance(
const hw::HWSymbolCache *cache) {
1860 auto result = cache->getInnerDefinition(getInstance());
1861 return cast<hw::InstanceOp>(result.getOp());
1865 auto topLevelModuleOp = (*this)->getParentOfType<ModuleOp>();
1866 if (!topLevelModuleOp)
1869 auto hwModule = dyn_cast_or_null<hw::HWModuleOp>(
1870 topLevelModuleOp.lookupSymbol(getInstance().getModule()));
1875 return findInstanceSymbolInBlock<hw::InstanceOp>(getInstance().
getName(),
1876 hwModule.getBodyBlock());
1880 LogicalResult BindOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1881 auto module = (*this)->getParentOfType<mlir::ModuleOp>();
1882 auto hwModule = dyn_cast_or_null<hw::HWModuleOp>(
1883 symbolTable.lookupSymbolIn(module, getInstance().getModule()));
1885 return emitError(
"Referenced module doesn't exist ")
1886 << getInstance().getModule() <<
"::" << getInstance().getName();
1888 auto inst = findInstanceSymbolInBlock<hw::InstanceOp>(
1889 getInstance().
getName(), hwModule.getBodyBlock());
1891 return emitError(
"Referenced instance doesn't exist ")
1892 << getInstance().getModule() <<
"::" << getInstance().getName();
1893 if (!inst.getDoNotPrint())
1894 return emitError(
"Referenced instance isn't marked as doNotPrint");
1898 void BindOp::build(OpBuilder &builder, OperationState &odsState, StringAttr mod,
1901 odsState.addAttribute(
"instance", ref);
1908 sv::InterfaceInstanceOp
1909 BindInterfaceOp::getReferencedInstance(
const hw::HWSymbolCache *cache) {
1912 auto result = cache->getInnerDefinition(getInstance());
1913 return cast<sv::InterfaceInstanceOp>(result.getOp());
1917 auto *symbolTable = SymbolTable::getNearestSymbolTable(*
this);
1926 return findInstanceSymbolInBlock<sv::InterfaceInstanceOp>(
1927 getInstance().
getName(), &parentOp->getRegion(0).front());
1932 BindInterfaceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1934 symbolTable.lookupNearestSymbolFrom(*
this, getInstance().getModule());
1936 return emitError(
"Referenced module doesn't exist ")
1937 << getInstance().getModule() <<
"::" << getInstance().getName();
1939 auto inst = findInstanceSymbolInBlock<sv::InterfaceInstanceOp>(
1940 getInstance().
getName(), &parentOp->getRegion(0).front());
1942 return emitError(
"Referenced interface doesn't exist ")
1943 << getInstance().getModule() <<
"::" << getInstance().getName();
1944 if (!inst.getDoNotPrint())
1945 return emitError(
"Referenced interface isn't marked as doNotPrint");
1954 StringAttr &terminalAttr) {
1955 SmallVector<Attribute> strings;
1956 ParseResult ret = parser.parseCommaSeparatedList([&]() {
1959 if (succeeded(parser.parseOptionalKeyword(&keyword))) {
1960 strings.push_back(parser.getBuilder().getStringAttr(keyword));
1963 if (succeeded(parser.parseAttribute(
1964 result, parser.getBuilder().getType<NoneType>()))) {
1965 strings.push_back(result);
1970 if (succeeded(ret)) {
1971 pathAttr = parser.getBuilder().getArrayAttr(
1972 ArrayRef<Attribute>(strings).drop_back());
1973 terminalAttr = cast<StringAttr>(*strings.rbegin());
1979 StringAttr terminalAttr) {
1980 llvm::interleaveComma(pathAttr, p);
1981 p <<
", " << terminalAttr;
1985 LogicalResult XMRRefOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1986 auto *table = SymbolTable::getNearestSymbolTable(*
this);
1987 auto path = dyn_cast_or_null<hw::HierPathOp>(
1988 symbolTable.lookupSymbolIn(table, getRefAttr()));
1990 return emitError(
"Referenced path doesn't exist ") << getRefAttr();
1995 hw::HierPathOp XMRRefOp::getReferencedPath(
const hw::HWSymbolCache *cache) {
1997 if (
auto *result = cache->getDefinition(getRefAttr().getAttr()))
1998 return cast<hw::HierPathOp>(result);
2000 auto topLevelModuleOp = (*this)->getParentOfType<ModuleOp>();
2001 return topLevelModuleOp.lookupSymbol<hw::HierPathOp>(getRefAttr().getValue());
2009 PatternRewriter &rewriter,
2012 if (constant.getValue().isZero() == eraseIfZero) {
2013 rewriter.eraseOp(op);
2020 template <
class Op,
bool EraseIfZero = false>
2022 PatternRewriter &rewriter) {
2026 void AssertOp::getCanonicalizationPatterns(RewritePatternSet &results,
2027 MLIRContext *context) {
2028 results.add(canonicalizeImmediateVerifOp<AssertOp>);
2031 void AssumeOp::getCanonicalizationPatterns(RewritePatternSet &results,
2032 MLIRContext *context) {
2033 results.add(canonicalizeImmediateVerifOp<AssumeOp>);
2036 void CoverOp::getCanonicalizationPatterns(RewritePatternSet &results,
2037 MLIRContext *context) {
2038 results.add(canonicalizeImmediateVerifOp<CoverOp, /* EraseIfZero = */ true>);
2041 template <
class Op,
bool EraseIfZero = false>
2043 PatternRewriter &rewriter) {
2047 void AssertConcurrentOp::getCanonicalizationPatterns(RewritePatternSet &results,
2048 MLIRContext *context) {
2049 results.add(canonicalizeConcurrentVerifOp<AssertConcurrentOp>);
2052 void AssumeConcurrentOp::getCanonicalizationPatterns(RewritePatternSet &results,
2053 MLIRContext *context) {
2054 results.add(canonicalizeConcurrentVerifOp<AssumeConcurrentOp>);
2057 void CoverConcurrentOp::getCanonicalizationPatterns(RewritePatternSet &results,
2058 MLIRContext *context) {
2060 canonicalizeConcurrentVerifOp<CoverConcurrentOp, /* EraseIfZero */ true>);
2070 ArrayAttr &caseNamesArray,
2071 SmallVectorImpl<std::unique_ptr<Region>> &caseRegions) {
2073 SmallVector<Attribute> names;
2074 while (!p.parseOptionalKeyword(
"case")) {
2077 std::unique_ptr<Region> region = std::make_unique<Region>();
2078 if (p.parseLParen() || p.parseAttribute(
pattern) || p.parseComma() ||
2079 p.parseAttribute(name) || p.parseRParen() || p.parseRegion(*region))
2082 names.push_back(name);
2083 if (region->empty())
2084 region->push_back(
new Block());
2085 caseRegions.push_back(std::move(region));
2087 patternsArray = p.getBuilder().getArrayAttr(
patterns);
2088 caseNamesArray = p.getBuilder().getArrayAttr(names);
2095 ArrayAttr namesArray,
2096 MutableArrayRef<Region> caseRegions) {
2097 assert(patternsArray.size() == caseRegions.size());
2098 assert(patternsArray.size() == namesArray.size());
2099 for (
size_t i = 0, e = caseRegions.size(); i < e; ++i) {
2101 p <<
"case (" << patternsArray[i] <<
", " << namesArray[i] <<
") ";
2102 p.printRegion(caseRegions[i]);
2108 size_t numPatterns = getCasePatterns().size();
2109 if (getCaseRegions().size() != numPatterns ||
2110 getCaseNames().size() != numPatterns)
2112 "Size of caseRegions, patterns, and caseNames must match");
2114 StringSet<> usedNames;
2115 for (Attribute name : getCaseNames()) {
2116 StringAttr nameStr = dyn_cast<StringAttr>(name);
2118 return emitOpError(
"caseNames must all be string attributes");
2119 if (usedNames.contains(nameStr.getValue()))
2120 return emitOpError(
"caseNames must be unique");
2121 usedNames.insert(nameStr.getValue());
2130 ModportDirection direction,
2131 FlatSymbolRefAttr signal) {
2139 ParseResult FuncOp::parse(OpAsmParser &parser, OperationState &result) {
2140 auto builder = parser.getBuilder();
2142 (void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
2145 StringAttr nameAttr;
2146 if (parser.parseSymbolName(nameAttr, SymbolTable::getSymbolAttrName(),
2150 SmallVector<hw::module_like_impl::PortParse> ports;
2156 result.addAttribute(FuncOp::getModuleTypeAttrName(result.name), modType);
2160 auto unknownLoc = builder.getUnknownLoc();
2161 SmallVector<Attribute> attrs, inputLocs, outputLocs;
2162 auto nonEmptyLocsFn = [unknownLoc](Attribute attr) {
2163 return attr && cast<Location>(attr) != unknownLoc;
2166 for (
auto &port : ports) {
2167 attrs.push_back(port.attrs ? port.attrs : builder.getDictionaryAttr({}));
2168 auto loc = port.sourceLoc ? Location(*port.sourceLoc) : unknownLoc;
2173 result.addAttribute(FuncOp::getPerArgumentAttrsAttrName(result.name),
2174 builder.getArrayAttr(attrs));
2176 if (llvm::any_of(outputLocs, nonEmptyLocsFn))
2177 result.addAttribute(FuncOp::getResultLocsAttrName(result.name),
2178 builder.getArrayAttr(outputLocs));
2180 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
2184 SmallVector<OpAsmParser::Argument, 4> entryArgs;
2185 for (
auto &port : ports)
2187 entryArgs.push_back(port);
2191 auto *body = result.addRegion();
2192 llvm::SMLoc loc = parser.getCurrentLocation();
2194 mlir::OptionalParseResult parseResult =
2195 parser.parseOptionalRegion(*body, entryArgs,
2197 if (parseResult.has_value()) {
2198 if (failed(*parseResult))
2202 return parser.emitError(loc,
"expected non-empty function body");
2204 if (llvm::any_of(inputLocs, nonEmptyLocsFn))
2205 result.addAttribute(FuncOp::getInputLocsAttrName(result.name),
2206 builder.getArrayAttr(inputLocs));
2212 void FuncOp::getAsmBlockArgumentNames(mlir::Region ®ion,
2217 auto func = cast<FuncOp>(region.getParentOp());
2219 auto *block = ®ion.front();
2221 auto names = func.getModuleType().getInputNames();
2222 for (
size_t i = 0, e = block->getNumArguments(); i != e; ++i) {
2224 setNameFn(block->getArgument(i), cast<StringAttr>(names[i]));
2228 Type FuncOp::getExplicitlyReturnedType() {
2229 if (!getPerArgumentAttrs() || getNumOutputs() == 0)
2234 auto lastArgumentAttr = dyn_cast<DictionaryAttr>(
2235 getPerArgumentAttrsAttr()[getPerArgumentAttrsAttr().size() - 1]);
2238 lastArgumentAttr.getAs<UnitAttr>(getExplicitlyReturnedAttrName()))
2239 return lastArgument.type;
2243 ArrayRef<Attribute> FuncOp::getAllPortAttrs() {
2244 if (getPerArgumentAttrs())
2245 return getPerArgumentAttrs()->getValue();
2249 void FuncOp::setAllPortAttrs(ArrayRef<Attribute> attrs) {
2253 void FuncOp::removeAllPortAttrs() { setPerArgumentAttrsAttr({}); }
2255 SmallVector<Location> portLocs;
2257 auto resultLocs = getResultLocsAttr();
2258 unsigned inputCount = 0;
2262 auto inputLocs = getInputLocsAttr();
2263 for (
unsigned i = 0, e =
getNumPorts(); i < e; ++i) {
2264 if (modType.isOutput(i)) {
2265 auto loc = resultLocs
2267 resultLocs.getValue()[portLocs.size() - inputCount])
2269 portLocs.push_back(loc);
2271 auto loc = body ? body->getArgument(inputCount).getLoc()
2272 : (inputLocs ? cast<Location>(inputLocs[inputCount])
2274 portLocs.push_back(loc);
2281 void FuncOp::setAllPortLocsAttrs(llvm::ArrayRef<mlir::Attribute> locs) {
2282 SmallVector<Attribute> resultLocs, inputLocs;
2283 unsigned inputCount = 0;
2286 for (
unsigned i = 0, e =
getNumPorts(); i < e; ++i) {
2287 if (modType.isOutput(i))
2288 resultLocs.push_back(locs[i]);
2290 body->getArgument(inputCount++).setLoc(cast<Location>(locs[i]));
2292 inputLocs.push_back(locs[i]);
2302 auto modTy = getHWModuleType();
2304 LocationAttr loc = getPortLoc(idx);
2305 DictionaryAttr attrs = dyn_cast_or_null<DictionaryAttr>(getPortAttrs(idx));
2308 return {modTy.getPorts()[idx],
2309 modTy.isOutput(idx) ? modTy.getOutputIdForPortId(idx)
2310 : modTy.getInputIdForPortId(idx),
2317 auto skipLastArgument = getExplicitlyReturnedType() && excludeExplicitReturn;
2318 SmallVector<hw::PortInfo> retval;
2320 for (
unsigned i = 0, e = skipLastArgument ? modTy.getNumPorts() - 1
2321 : modTy.getNumPorts();
2323 DictionaryAttr attrs = emptyDict;
2324 if (
auto perArgumentAttr = getPerArgumentAttrs())
2325 if (
auto argumentAttr =
2326 dyn_cast_or_null<DictionaryAttr>((*perArgumentAttr)[i]))
2327 attrs = argumentAttr;
2329 retval.push_back({modTy.getPorts()[i],
2330 modTy.isOutput(i) ? modTy.getOutputIdForPortId(i)
2331 : modTy.getInputIdForPortId(i),
2332 attrs, portAttr[i]});
2337 void FuncOp::print(OpAsmPrinter &p) {
2341 op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName())
2345 StringRef visibilityAttrName = SymbolTable::getVisibilityAttrName();
2346 if (
auto visibility = op->getAttrOfType<StringAttr>(visibilityAttrName))
2347 p << visibility.getValue() <<
' ';
2348 p.printSymbolName(funcName);
2350 p, op.getBody(), op.getModuleType(),
2351 op.getPerArgumentAttrsAttr()
2352 ? ArrayRef<Attribute>(op.getPerArgumentAttrsAttr().getValue())
2353 : ArrayRef<Attribute>{},
2356 mlir::function_interface_impl::printFunctionAttributes(
2358 {visibilityAttrName, getModuleTypeAttrName(),
2359 getPerArgumentAttrsAttrName(), getInputLocsAttrName(),
2360 getResultLocsAttrName()});
2362 Region &body = op->getRegion(0);
2363 if (!body.empty()) {
2365 p.printRegion(body,
false,
2375 auto func = getParentOp<sv::FuncOp>();
2376 auto funcResults = func.getResultTypes();
2377 auto returnedValues = getOperands();
2378 if (funcResults.size() != returnedValues.size())
2379 return emitOpError(
"must have same number of operands as region results.");
2381 for (
size_t i = 0, e = funcResults.size(); i < e; ++i) {
2382 if (funcResults[i] != returnedValues[i].getType()) {
2383 emitOpError(
"output types must match function. In "
2385 << i <<
", expected " << funcResults[i] <<
", but got "
2386 << returnedValues[i].getType() <<
".";
2399 mlir::Operation::result_range results) {
2400 if (!op.getExplicitlyReturnedType())
2402 return results.back();
2405 Value FuncCallOp::getExplicitlyReturnedValue(sv::FuncOp op) {
2409 Value FuncCallProceduralOp::getExplicitlyReturnedValue(sv::FuncOp op) {
2414 FuncCallProceduralOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2415 auto referencedOp = dyn_cast_or_null<sv::FuncOp>(
2416 symbolTable.lookupNearestSymbolFrom(*
this, getCalleeAttr()));
2418 return emitError(
"cannot find function declaration '")
2419 << getCallee() <<
"'";
2423 LogicalResult FuncCallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2424 auto referencedOp = dyn_cast_or_null<sv::FuncOp>(
2425 symbolTable.lookupNearestSymbolFrom(*
this, getCalleeAttr()));
2427 return emitError(
"cannot find function declaration '")
2428 << getCallee() <<
"'";
2431 if (referencedOp.getNumOutputs() != 1 ||
2432 !referencedOp.getExplicitlyReturnedType()) {
2433 auto diag = emitError()
2434 <<
"function called in a non-procedural region must "
2435 "return a single result";
2436 diag.attachNote(referencedOp.getLoc()) <<
"doesn't satisfy the constraint";
2447 FuncDPIImportOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2448 auto referencedOp = dyn_cast_or_null<sv::FuncOp>(
2449 symbolTable.lookupNearestSymbolFrom(*
this, getCalleeAttr()));
2452 return emitError(
"cannot find function declaration '")
2453 << getCallee() <<
"'";
2454 if (!referencedOp.isDeclaration())
2455 return emitError(
"imported function must be a declaration but '")
2456 << getCallee() <<
"' is defined";
2467 static LogicalResult
verify(Value clock,
bool eventExists, mlir::Location loc) {
2468 if ((!clock && eventExists) || (clock && !eventExists))
2469 return mlir::emitError(
2470 loc,
"Every clock must be associated to an even and vice-versa!");
2495 #define GET_OP_CLASSES
2496 #include "circt/Dialect/SV/SV.cpp.inc"
assert(baseType &&"element must be base type")
static SmallVector< Location > getAllPortLocs(ModTy module)
static SmallVector< PortInfo > getPortList(ModuleTy &mod)
static PortInfo getPort(ModuleTy &mod, size_t idx)
static InstancePath empty
static std::optional< APInt > getInt(Value value)
Helper to convert a value to a constant integer if it is one.
static Block * getBodyBlock(FModuleLike mod)
RewritePatternSet pattern
ParseResult parseIfaceTypeAndSignal(OpAsmParser &p, Type &ifaceTy, FlatSymbolRefAttr &signalName)
static LogicalResult eraseIfZeroOrNotZero(Operation *op, Value value, PatternRewriter &rewriter, bool eraseIfZero)
static LogicalResult canonicalizeImmediateVerifOp(Op op, PatternRewriter &rewriter)
LogicalResult verifySignalExists(Value ifaceVal, FlatSymbolRefAttr signalName)
static void replaceOpWithRegion(PatternRewriter &rewriter, Operation *op, Region ®ion)
Replaces the given op with the contents of the given single-block region.
bool parseCaseRegions(OpAsmParser &p, ArrayAttr &patternsArray, ArrayAttr &caseNamesArray, SmallVectorImpl< std::unique_ptr< Region >> &caseRegions)
Parse cases formatted like: case (pattern, "name") { ...
void printCaseRegions(OpAsmPrinter &p, Operation *, ArrayAttr patternsArray, ArrayAttr namesArray, MutableArrayRef< Region > caseRegions)
Print cases formatted like: case (pattern, "name") { ...
static Value getExplicitlyReturnedValueImpl(sv::FuncOp op, mlir::Operation::result_range results)
void printIfaceTypeAndSignal(OpAsmPrinter &p, Operation *op, Type type, FlatSymbolRefAttr signalName)
static void printModportStructs(OpAsmPrinter &p, Operation *, ArrayAttr portsAttr)
static LogicalResult canonicalizeConcurrentVerifOp(Op op, PatternRewriter &rewriter)
static ParseResult parseEventList(OpAsmParser &p, Attribute &eventsAttr, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &clocksOperands)
static MacroDeclOp getReferencedMacro(const hw::HWSymbolCache *cache, Operation *op, FlatSymbolRefAttr macroName)
static LogicalResult canonicalizeIfDefLike(Op op, PatternRewriter &rewriter)
static SmallVector< CasePatternBit > getPatternBitsForValue(const APInt &value)
ParseResult parseXMRPath(::mlir::OpAsmParser &parser, ArrayAttr &pathAttr, StringAttr &terminalAttr)
static Type getElementTypeOfWidth(Type type, int32_t width)
static LogicalResult mergeNeiboringAssignments(AssignTy op, PatternRewriter &rewriter)
static Op findInstanceSymbolInBlock(StringAttr name, Block *body)
Instances must be at the top level of the hw.module (or within a `ifdef)
static void printEventList(OpAsmPrinter &p, AlwaysOp op, ArrayAttr portsAttr, OperandRange operands)
static Operation * lookupSymbolInNested(Operation *symbolTableOp, StringRef symbol)
Returns the operation registered with the given symbol name with the regions of 'symbolTableOp'.
static ParseResult parseImplicitInitType(OpAsmParser &p, mlir::Type regType, std::optional< OpAsmParser::UnresolvedOperand > &initValue, mlir::Type &initType)
static LogicalResult verifyMacroIdentSymbolUses(Operation *op, FlatSymbolRefAttr attr, SymbolTableCollection &symbolTable)
Verifies symbols referenced by macro identifiers.
static void getVerbatimExprAsmResultNames(Operation *op, function_ref< void(Value, StringRef)> setNameFn)
Get the asm name for sv.verbatim.expr and sv.verbatim.expr.se.
static void printImplicitInitType(OpAsmPrinter &p, Operation *op, mlir::Type regType, mlir::Value initValue, mlir::Type initType)
static ParseResult parseModportStructs(OpAsmParser &parser, ArrayAttr &portsAttr)
void printXMRPath(OpAsmPrinter &p, XMROp op, ArrayAttr pathAttr, StringAttr terminalAttr)
static StringRef getInnerSymbolAttrName()
Return the name of the attribute used for inner symbol names.
CasePatternBit getBit(size_t bitNumber) const
Return the specified bit, bit 0 is the least significant bit.
bool hasZ() const override
Return true if this pattern has an Z.
CaseBitPattern(ArrayRef< CasePatternBit > bits, MLIRContext *context)
Get a CasePattern from a specified list of CasePatternBit.
bool hasX() const override
Return true if this pattern has an X.
hw::EnumFieldAttr enumAttr
StringRef getFieldValue() const
Signals that an operations regions are procedural.
def create(data_type, value)
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.
Value createOrFoldNot(Location loc, Value value, OpBuilder &builder, bool twoState=false)
Create a `‘Not’' gate on a value.
uint64_t getWidth(Type t)
size_t getNumPorts(Operation *op)
Return the number of ports in a module-like thing (modules, memories, etc)
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
void getAsmResultNames(OpAsmSetValueNameFn setNameFn, StringRef instanceName, ArrayAttr resultNames, ValueRange results)
Suggest a name for each result value based on the saved result names attribute.
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)
bool isHWIntegerType(mlir::Type type)
Return true if the specified type is a value HW Integer type.
bool isOffset(Value base, Value index, uint64_t offset)
FunctionType getModuleType(Operation *module)
Return the signature for the specified module as a function type.
LogicalResult checkParameterInContext(Attribute value, Operation *module, Operation *usingOp, bool disallowParamRefs=false)
Check parameter specified by value to see if it is valid within the scope of the specified module mod...
bool isHWEnumType(mlir::Type type)
Return true if the specified type is a HW Enum type.
mlir::Type getCanonicalType(mlir::Type type)
LogicalResult verifyInNonProceduralRegion(Operation *op)
Return true if the specified operation is not in a procedural region.
CasePatternBit
This describes the bit in a pattern, 0/1/x/z.
bool isExpression(Operation *op)
Return true if the specified operation is an expression.
char getLetter(CasePatternBit bit)
Return the letter for the specified pattern bit, e.g. "0", "1", "x" or "z".
bool hasSVAttributes(mlir::Operation *op)
Helper functions to handle SV attributes.
circt::hw::InOutType InOutType
bool is2StateExpression(Value v)
Returns if the expression is known to be 2-state (binary)
mlir::Type getInOutElementType(mlir::Type type)
Return the element type of an InOutType or null if the operand isn't an InOut type.
LogicalResult verifyInProceduralRegion(Operation *op)
Return true if the specified operation is in a procedural region.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn