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"
40bool sv::is2StateExpression(Value v) {
41 if (
auto *op = v.getDefiningOp()) {
42 if (
auto attr = op->getAttrOfType<UnitAttr>(
"twoState"))
40bool sv::is2StateExpression(Value v) {
…}
50bool sv::isExpression(Operation *op) {
51 return isa<VerbatimExprOp, VerbatimExprSEOp, GetModportOp,
52 ReadInterfaceSignalOp, ConstantXOp, ConstantZOp, ConstantStrOp,
53 MacroRefExprOp, MacroRefExprSEOp>(op);
50bool sv::isExpression(Operation *op) {
…}
56LogicalResult sv::verifyInProceduralRegion(Operation *op) {
59 op->emitError() << op->getName() <<
" should be in a procedural region";
56LogicalResult sv::verifyInProceduralRegion(Operation *op) {
…}
63LogicalResult sv::verifyInNonProceduralRegion(Operation *op) {
66 op->emitError() << op->getName() <<
" should be in a non-procedural region";
63LogicalResult sv::verifyInNonProceduralRegion(Operation *op) {
…}
75 Region ®ion = symbolTableOp->getRegion(0);
80 StringAttr symbolNameId = StringAttr::get(symbolTableOp->getContext(),
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);
129void VerbatimExprOp::getAsmResultNames(
130 function_ref<
void(Value, StringRef)> setNameFn) {
134void VerbatimExprSEOp::getAsmResultNames(
135 function_ref<
void(Value, StringRef)> setNameFn) {
143void MacroRefExprOp::getAsmResultNames(
144 function_ref<
void(Value, StringRef)> setNameFn) {
145 setNameFn(getResult(), getMacroName());
148void MacroRefExprSEOp::getAsmResultNames(
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());
167 return ::getReferencedMacro(cache, *
this, getMacroNameAttr());
172 return ::getReferencedMacro(cache, *
this, getMacroNameAttr());
179std::string MacroErrorOp::getMacroIdentifier() {
180 const auto *prefix =
"_ERROR";
181 auto msg = getMessage();
182 if (!msg || msg->empty())
185 std::string id(prefix);
187 for (
auto c : *msg) {
188 if (llvm::isAlnum(c))
201 return ::getReferencedMacro(cache, *
this, getMacroNameAttr());
205 return ::getReferencedMacro(cache, *
this, getMacroNameAttr());
210MacroRefExprOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
216MacroRefExprSEOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
221LogicalResult MacroDefOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
226LogicalResult MacroRefOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
234StringRef MacroDeclOp::getMacroIdentifier() {
235 return getVerilogName().value_or(getSymName());
242void ConstantXOp::getAsmResultNames(
243 function_ref<
void(Value, StringRef)> setNameFn) {
244 SmallVector<char, 32> specialNameBuffer;
245 llvm::raw_svector_ostream specialName(specialNameBuffer);
247 setNameFn(getResult(), specialName.str());
250LogicalResult ConstantXOp::verify() {
253 return emitError(
"unsupported type");
257void ConstantZOp::getAsmResultNames(
258 function_ref<
void(Value, StringRef)> setNameFn) {
259 SmallVector<char, 32> specialNameBuffer;
260 llvm::raw_svector_ostream specialName(specialNameBuffer);
262 setNameFn(getResult(), specialName.str());
265LogicalResult ConstantZOp::verify() {
268 return emitError(
"unsupported type");
276void LocalParamOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
278 auto nameAttr = (*this)->getAttrOfType<StringAttr>(
"name");
279 if (!nameAttr.getValue().empty())
280 setNameFn(getResult(), nameAttr.getValue());
283LogicalResult LocalParamOp::verify() {
285 return hw::checkParameterInContext(
295 std::optional<OpAsmParser::UnresolvedOperand> &initValue,
296 mlir::Type &initType) {
297 if (!initValue.has_value())
300 hw::InOutType ioType = dyn_cast<hw::InOutType>(regType);
302 return p.emitError(p.getCurrentLocation(),
"expected inout type for reg");
304 initType = ioType.getElementType();
309 mlir::Type regType, mlir::Value initValue,
310 mlir::Type initType) {}
312void RegOp::build(OpBuilder &builder, OperationState &odsState,
313 Type
elementType, StringAttr name, hw::InnerSymAttr innerSym,
314 mlir::Value initValue) {
316 name = builder.getStringAttr(
"");
317 odsState.addAttribute(
"name", name);
321 odsState.addTypes(hw::InOutType::get(
elementType));
323 odsState.addOperands(initValue);
328void RegOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
330 auto nameAttr = (*this)->getAttrOfType<StringAttr>(
"name");
331 if (!nameAttr.getValue().empty())
332 setNameFn(getResult(), nameAttr.getValue());
335std::optional<size_t> RegOp::getTargetResultIndex() {
return 0; }
338LogicalResult RegOp::canonicalize(
RegOp op, PatternRewriter &rewriter) {
344 if (op.getInnerSymAttr())
348 for (
auto *user : op.getResult().getUsers())
353 for (
auto *user :
llvm::make_early_inc_range(op.getResult().getUsers()))
354 rewriter.eraseOp(user);
357 rewriter.eraseOp(op);
365void LogicOp::build(OpBuilder &builder, OperationState &odsState,
367 hw::InnerSymAttr innerSym) {
369 name = builder.getStringAttr(
"");
370 odsState.addAttribute(
"name", name);
374 odsState.addTypes(hw::InOutType::get(
elementType));
379void LogicOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
381 auto nameAttr = (*this)->getAttrOfType<StringAttr>(
"name");
382 if (!nameAttr.getValue().empty())
383 setNameFn(getResult(), nameAttr.getValue());
386std::optional<size_t> LogicOp::getTargetResultIndex() {
return 0; }
396void IfDefOp::build(OpBuilder &builder, OperationState &result, StringRef cond,
397 std::function<
void()> thenCtor,
398 std::function<
void()> elseCtor) {
399 build(builder, result, builder.getStringAttr(cond), std::move(thenCtor),
400 std::move(elseCtor));
403void IfDefOp::build(OpBuilder &builder, OperationState &result, StringAttr cond,
404 std::function<
void()> thenCtor,
405 std::function<
void()> elseCtor) {
406 build(builder, result, FlatSymbolRefAttr::get(builder.getContext(), cond),
407 std::move(thenCtor), std::move(elseCtor));
410void IfDefOp::build(OpBuilder &builder, OperationState &result,
411 FlatSymbolRefAttr cond, std::function<
void()> thenCtor,
412 std::function<
void()> elseCtor) {
413 build(builder, result, MacroIdentAttr::get(builder.getContext(), cond),
414 std::move(thenCtor), std::move(elseCtor));
417void IfDefOp::build(OpBuilder &builder, OperationState &result,
418 MacroIdentAttr cond, std::function<
void()> thenCtor,
419 std::function<
void()> elseCtor) {
420 OpBuilder::InsertionGuard guard(builder);
422 result.addAttribute(
"cond", cond);
423 builder.createBlock(result.addRegion());
429 Region *elseRegion = result.addRegion();
431 builder.createBlock(elseRegion);
436LogicalResult IfDefOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
443 if (!op.getThenBlock()->empty())
446 if (op.hasElse() && !op.getElseBlock()->empty())
449 rewriter.eraseOp(op);
453LogicalResult IfDefOp::canonicalize(
IfDefOp op, PatternRewriter &rewriter) {
461void IfDefProceduralOp::build(OpBuilder &builder, OperationState &result,
462 StringRef cond, std::function<
void()> thenCtor,
463 std::function<
void()> elseCtor) {
464 build(builder, result, builder.getStringAttr(cond), std::move(thenCtor),
465 std::move(elseCtor));
468void IfDefProceduralOp::build(OpBuilder &builder, OperationState &result,
469 StringAttr cond, std::function<
void()> thenCtor,
470 std::function<
void()> elseCtor) {
471 build(builder, result, FlatSymbolRefAttr::get(builder.getContext(), cond),
472 std::move(thenCtor), std::move(elseCtor));
475void IfDefProceduralOp::build(OpBuilder &builder, OperationState &result,
476 FlatSymbolRefAttr cond,
477 std::function<
void()> thenCtor,
478 std::function<
void()> elseCtor) {
479 build(builder, result, MacroIdentAttr::get(builder.getContext(), cond),
480 std::move(thenCtor), std::move(elseCtor));
483void IfDefProceduralOp::build(OpBuilder &builder, OperationState &result,
485 std::function<
void()> thenCtor,
486 std::function<
void()> elseCtor) {
487 OpBuilder::InsertionGuard guard(builder);
489 result.addAttribute(
"cond", cond);
490 builder.createBlock(result.addRegion());
496 Region *elseRegion = result.addRegion();
498 builder.createBlock(elseRegion);
503LogicalResult IfDefProceduralOp::canonicalize(IfDefProceduralOp op,
504 PatternRewriter &rewriter) {
509IfDefProceduralOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
517void IfOp::build(OpBuilder &builder, OperationState &result, Value cond,
518 std::function<
void()> thenCtor,
519 std::function<
void()> elseCtor) {
520 OpBuilder::InsertionGuard guard(builder);
522 result.addOperands(cond);
523 builder.createBlock(result.addRegion());
529 Region *elseRegion = result.addRegion();
531 builder.createBlock(elseRegion);
539 assert(llvm::hasSingleElement(region) &&
"expected single-region block");
540 Block *fromBlock = ®ion.front();
542 op->getBlock()->getOperations().splice(Block::iterator(op),
543 fromBlock->getOperations());
546LogicalResult IfOp::canonicalize(IfOp op, PatternRewriter &rewriter) {
551 if (
auto constant = op.getCond().getDefiningOp<
hw::ConstantOp>()) {
553 if (constant.getValue().isAllOnes())
555 else if (!op.getElseRegion().empty())
558 rewriter.eraseOp(op);
564 if (!op.getThenBlock()->empty() && op.hasElse() &&
565 op.getElseBlock()->empty()) {
566 rewriter.eraseBlock(op.getElseBlock());
573 if (!op.getThenBlock()->empty())
577 if (!op.hasElse() || op.getElseBlock()->empty()) {
578 rewriter.eraseOp(op);
589 auto *thenBlock = op.getThenBlock(), *elseBlock = op.getElseBlock();
592 thenBlock->getOperations().splice(thenBlock->end(),
593 elseBlock->getOperations());
594 rewriter.eraseBlock(elseBlock);
604AlwaysOp::Condition AlwaysOp::getCondition(
size_t idx) {
605 return Condition{EventControl(cast<IntegerAttr>(getEvents()[idx]).
getInt()),
609void AlwaysOp::build(OpBuilder &builder, OperationState &result,
610 ArrayRef<sv::EventControl> events, ArrayRef<Value> clocks,
611 std::function<
void()> bodyCtor) {
612 assert(events.size() == clocks.size() &&
613 "mismatch between event and clock list");
614 OpBuilder::InsertionGuard guard(builder);
616 SmallVector<Attribute> eventAttrs;
617 for (
auto event : events)
618 eventAttrs.push_back(
619 builder.getI32IntegerAttr(static_cast<int32_t>(event)));
620 result.addAttribute(
"events", builder.getArrayAttr(eventAttrs));
621 result.addOperands(clocks);
624 builder.createBlock(result.addRegion());
632LogicalResult AlwaysOp::verify() {
633 if (getEvents().size() != getNumOperands())
634 return emitError(
"different number of operands and events");
639 OpAsmParser &p, Attribute &eventsAttr,
640 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &clocksOperands) {
643 SmallVector<Attribute> events;
645 auto loc = p.getCurrentLocation();
647 if (!p.parseOptionalKeyword(&keyword)) {
649 auto kind = sv::symbolizeEventControl(keyword);
650 if (!kind.has_value())
651 return p.emitError(loc,
"expected 'posedge', 'negedge', or 'edge'");
652 auto eventEnum =
static_cast<int32_t
>(*kind);
653 events.push_back(p.getBuilder().getI32IntegerAttr(eventEnum));
655 clocksOperands.push_back({});
656 if (p.parseOperand(clocksOperands.back()))
659 if (failed(p.parseOptionalComma()))
661 if (p.parseKeyword(&keyword))
665 eventsAttr = p.getBuilder().getArrayAttr(events);
670 OperandRange operands) {
671 for (
size_t i = 0, e = op.getNumConditions(); i != e; ++i) {
674 auto cond = op.getCondition(i);
675 p << stringifyEventControl(cond.event);
677 p.printOperand(cond.value);
685void AlwaysFFOp::build(OpBuilder &builder, OperationState &result,
686 EventControl clockEdge, Value clock,
687 std::function<
void()> bodyCtor) {
688 OpBuilder::InsertionGuard guard(builder);
691 "clockEdge", builder.getI32IntegerAttr(
static_cast<int32_t
>(clockEdge)));
692 result.addOperands(clock);
695 builder.getI32IntegerAttr(
static_cast<int32_t
>(ResetType::NoReset)));
698 builder.createBlock(result.addRegion());
707void AlwaysFFOp::build(OpBuilder &builder, OperationState &result,
708 EventControl clockEdge, Value clock,
709 ResetType resetStyle, EventControl resetEdge,
710 Value reset, std::function<
void()> bodyCtor,
711 std::function<
void()> resetCtor) {
712 OpBuilder::InsertionGuard guard(builder);
715 "clockEdge", builder.getI32IntegerAttr(
static_cast<int32_t
>(clockEdge)));
716 result.addOperands(clock);
717 result.addAttribute(
"resetStyle", builder.getI32IntegerAttr(
718 static_cast<int32_t
>(resetStyle)));
720 "resetEdge", builder.getI32IntegerAttr(
static_cast<int32_t
>(resetEdge)));
721 result.addOperands(reset);
724 builder.createBlock(result.addRegion());
730 builder.createBlock(result.addRegion());
740void AlwaysCombOp::build(OpBuilder &builder, OperationState &result,
741 std::function<
void()> bodyCtor) {
742 OpBuilder::InsertionGuard guard(builder);
744 builder.createBlock(result.addRegion());
754void InitialOp::build(OpBuilder &builder, OperationState &result,
755 std::function<
void()> bodyCtor) {
756 OpBuilder::InsertionGuard guard(builder);
758 builder.createBlock(result.addRegion());
781 llvm_unreachable(
"invalid casez PatternBit");
786 return CasePatternBit(
unsigned(intAttr.getValue()[bitNumber * 2]) +
787 2 *
unsigned(intAttr.getValue()[bitNumber * 2 + 1]));
791 for (
size_t i = 0, e = getWidth(); i != e; ++i)
798 for (
size_t i = 0, e = getWidth(); i != e; ++i)
804 SmallVector<CasePatternBit> result;
805 result.reserve(value.getBitWidth());
806 for (
size_t i = 0, e = value.getBitWidth(); i != e; ++i)
822 MLIRContext *context)
824 APInt
pattern(bits.size() * 2, 0);
825 for (
auto elt : llvm::reverse(bits)) {
829 auto patternType = IntegerType::get(context, bits.size() * 2);
833auto CaseOp::getCases() -> SmallVector<CaseInfo, 4> {
834 SmallVector<CaseInfo, 4> result;
835 assert(getCasePatterns().size() == getNumRegions() &&
836 "case pattern / region count mismatch");
837 size_t nextRegion = 0;
838 for (
auto elt : getCasePatterns()) {
839 llvm::TypeSwitch<Attribute>(elt)
840 .Case<hw::EnumFieldAttr>([&](
auto enumAttr) {
841 result.push_back({std::make_unique<CaseEnumPattern>(enumAttr),
842 &getRegion(nextRegion++).front()});
844 .Case<IntegerAttr>([&](
auto intAttr) {
845 result.push_back({std::make_unique<CaseBitPattern>(intAttr),
846 &getRegion(nextRegion++).front()});
848 .Case<CaseDefaultPattern::AttrType>([&](
auto) {
849 result.push_back({std::make_unique<CaseDefaultPattern>(getContext()),
850 &getRegion(nextRegion++).front()});
853 assert(
false &&
"invalid case pattern attribute type");
861 return cast<hw::EnumFieldAttr>(
enumAttr).getField();
871ParseResult CaseOp::parse(OpAsmParser &parser, OperationState &result) {
872 auto &builder = parser.getBuilder();
874 OpAsmParser::UnresolvedOperand condOperand;
877 auto loc = parser.getCurrentLocation();
880 if (!parser.parseOptionalKeyword(&keyword, {
"case",
"casex",
"casez"})) {
881 auto kind = symbolizeCaseStmtType(keyword);
882 auto caseEnum =
static_cast<int32_t
>(kind.value());
883 result.addAttribute(
"caseStyle", builder.getI32IntegerAttr(caseEnum));
887 if (!parser.parseOptionalKeyword(
888 &keyword, {
"plain",
"priority",
"unique",
"unique0"})) {
889 auto kind = symbolizeValidationQualifierTypeEnum(keyword);
890 result.addAttribute(
"validationQualifier",
891 ValidationQualifierTypeEnumAttr::get(
892 builder.getContext(), kind.value()));
895 if (parser.parseOperand(condOperand) || parser.parseColonType(condType) ||
896 parser.parseOptionalAttrDict(result.attributes) ||
897 parser.resolveOperand(condOperand, condType, result.operands))
902 hw::EnumType enumType = dyn_cast<hw::EnumType>(canonicalCondType);
903 unsigned condWidth = 0;
905 if (!result.operands[0].getType().isSignlessInteger())
906 return parser.emitError(loc,
"condition must have signless integer type");
907 condWidth = condType.getIntOrFloatBitWidth();
911 SmallVector<Attribute> casePatterns;
912 SmallVector<CasePatternBit, 16> caseBits;
914 if (succeeded(parser.parseOptionalKeyword(
"default"))) {
915 casePatterns.push_back(CaseDefaultPattern(parser.getContext()).attr());
916 }
else if (failed(parser.parseOptionalKeyword(
"case"))) {
919 }
else if (enumType) {
923 if (parser.parseKeyword(&caseVal))
926 if (!enumType.contains(caseVal))
927 return parser.emitError(loc)
928 <<
"case value '" + caseVal +
"' is not a member of enum type "
930 casePatterns.push_back(
931 hw::EnumFieldAttr::get(parser.getEncodedSourceLoc(loc),
932 builder.getStringAttr(caseVal), condType));
937 loc = parser.getCurrentLocation();
938 if (parser.parseKeyword(&caseVal))
941 if (caseVal.front() !=
'b')
942 return parser.emitError(loc,
"expected case value starting with 'b'");
943 caseVal = caseVal.drop_front();
946 for (; !caseVal.empty(); caseVal = caseVal.drop_front()) {
948 switch (caseVal.front()) {
962 return parser.emitError(loc,
"unexpected case bit '")
963 << caseVal.front() <<
"'";
965 caseBits.push_back(bit);
968 if (caseVal.size() > condWidth)
969 return parser.emitError(loc,
"too many bits specified in pattern");
970 std::reverse(caseBits.begin(), caseBits.end());
973 if (caseBits.size() < condWidth)
976 auto resultPattern = CaseBitPattern(caseBits, builder.getContext());
977 casePatterns.push_back(resultPattern.attr());
982 auto caseRegion = std::make_unique<Region>();
983 if (parser.parseColon() || parser.parseRegion(*caseRegion))
985 result.addRegion(std::move(caseRegion));
988 result.addAttribute(
"casePatterns", builder.getArrayAttr(casePatterns));
992void CaseOp::print(OpAsmPrinter &p) {
994 if (getCaseStyle() == CaseStmtType::CaseXStmt)
996 else if (getCaseStyle() == CaseStmtType::CaseZStmt)
999 if (getValidationQualifier() !=
1000 ValidationQualifierTypeEnum::ValidationQualifierPlain)
1001 p << stringifyValidationQualifierTypeEnum(getValidationQualifier()) <<
' ';
1003 p << getCond() <<
" : " << getCond().getType();
1004 p.printOptionalAttrDict(
1005 (*this)->getAttrs(),
1006 {
"casePatterns",
"caseStyle",
"validationQualifier"});
1008 for (
auto &caseInfo : getCases()) {
1010 auto &
pattern = caseInfo.pattern;
1012 llvm::TypeSwitch<CasePattern *>(
pattern.get())
1013 .Case<CaseBitPattern>([&](
auto bitPattern) {
1015 for (
size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
1016 p <<
getLetter(bitPattern->getBit(e - bit - 1));
1018 .Case<CaseEnumPattern>([&](
auto enumPattern) {
1019 p <<
"case " << enumPattern->getFieldValue();
1021 .Case<CaseDefaultPattern>([&](
auto) { p <<
"default"; })
1022 .Default([&](
auto) {
assert(
false &&
"unhandled case pattern"); });
1025 p.printRegion(*caseInfo.block->getParent(),
false,
1030LogicalResult CaseOp::verify() {
1033 return emitError(
"condition must have either integer or enum type");
1036 if (getCasePatterns().size() != getNumRegions())
1037 return emitOpError(
"case pattern / region count mismatch");
1044 OpBuilder &builder, OperationState &result, CaseStmtType caseStyle,
1045 ValidationQualifierTypeEnum validationQualifier, Value cond,
1047 std::function<std::unique_ptr<CasePattern>(
size_t)> caseCtor) {
1048 result.addOperands(cond);
1049 result.addAttribute(
"caseStyle",
1050 CaseStmtTypeAttr::get(builder.getContext(), caseStyle));
1051 result.addAttribute(
"validationQualifier",
1052 ValidationQualifierTypeEnumAttr::get(
1053 builder.getContext(), validationQualifier));
1054 SmallVector<Attribute> casePatterns;
1056 OpBuilder::InsertionGuard guard(builder);
1059 for (
size_t i = 0, e = numCases; i != e; ++i) {
1060 builder.createBlock(result.addRegion());
1061 casePatterns.push_back(caseCtor(i)->attr());
1064 result.addAttribute(
"casePatterns", builder.getArrayAttr(casePatterns));
1068LogicalResult CaseOp::canonicalize(CaseOp op, PatternRewriter &rewriter) {
1069 if (op.getCaseStyle() == CaseStmtType::CaseStmt)
1071 if (isa<hw::EnumType>(op.getCond().getType()))
1074 auto caseInfo = op.getCases();
1075 bool noXZ = llvm::all_of(caseInfo, [](
const CaseInfo &ci) {
1076 return !ci.pattern.get()->hasX() && !ci.pattern.get()->hasZ();
1078 bool noX = llvm::all_of(caseInfo, [](
const CaseInfo &ci) {
1079 if (isa<CaseDefaultPattern>(ci.pattern))
1081 return !ci.pattern.get()->hasX();
1083 bool noZ = llvm::all_of(caseInfo, [](
const CaseInfo &ci) {
1084 if (isa<CaseDefaultPattern>(ci.pattern))
1086 return !ci.pattern.get()->hasZ();
1089 if (op.getCaseStyle() == CaseStmtType::CaseXStmt) {
1091 rewriter.modifyOpInPlace(op, [&]() {
1092 op.setCaseStyleAttr(
1093 CaseStmtTypeAttr::get(op.getContext(), CaseStmtType::CaseStmt));
1098 rewriter.modifyOpInPlace(op, [&]() {
1099 op.setCaseStyleAttr(
1100 CaseStmtTypeAttr::get(op.getContext(), CaseStmtType::CaseZStmt));
1106 if (op.getCaseStyle() == CaseStmtType::CaseZStmt && noZ) {
1107 rewriter.modifyOpInPlace(op, [&]() {
1108 op.setCaseStyleAttr(
1109 CaseStmtTypeAttr::get(op.getContext(), CaseStmtType::CaseStmt));
1121void OrderedOutputOp::build(OpBuilder &builder, OperationState &result,
1122 std::function<
void()> body) {
1123 OpBuilder::InsertionGuard guard(builder);
1125 builder.createBlock(result.addRegion());
1136void ForOp::build(OpBuilder &builder, OperationState &result,
1137 int64_t lowerBound, int64_t upperBound, int64_t step,
1138 IntegerType type, StringRef name,
1139 llvm::function_ref<
void(BlockArgument)> body) {
1143 build(builder, result, lb, ub, st, name, body);
1145void ForOp::build(OpBuilder &builder, OperationState &result, Value lowerBound,
1146 Value upperBound, Value step, StringRef name,
1147 llvm::function_ref<
void(BlockArgument)> body) {
1148 OpBuilder::InsertionGuard guard(builder);
1149 build(builder, result, lowerBound, upperBound, step, name);
1150 auto *region = result.regions.front().get();
1151 builder.createBlock(region);
1152 BlockArgument blockArgument =
1153 region->addArgument(lowerBound.getType(), result.location);
1156 body(blockArgument);
1159void ForOp::getAsmBlockArgumentNames(mlir::Region ®ion,
1161 auto *block = ®ion.front();
1162 setNameFn(block->getArgument(0), getInductionVarNameAttr());
1165ParseResult ForOp::parse(OpAsmParser &parser, OperationState &result) {
1166 auto &builder = parser.getBuilder();
1169 OpAsmParser::Argument inductionVariable;
1170 OpAsmParser::UnresolvedOperand lb, ub, step;
1172 SmallVector<OpAsmParser::Argument, 4> regionArgs;
1175 if (parser.parseOperand(inductionVariable.ssaName) || parser.parseEqual() ||
1177 parser.parseOperand(lb) || parser.parseKeyword(
"to") ||
1178 parser.parseOperand(ub) || parser.parseKeyword(
"step") ||
1179 parser.parseOperand(step) || parser.parseColon() ||
1180 parser.parseType(type))
1183 regionArgs.push_back(inductionVariable);
1186 regionArgs.front().type = type;
1187 if (parser.resolveOperand(lb, type, result.operands) ||
1188 parser.resolveOperand(ub, type, result.operands) ||
1189 parser.resolveOperand(step, type, result.operands))
1193 Region *body = result.addRegion();
1194 if (parser.parseRegion(*body, regionArgs))
1198 if (parser.parseOptionalAttrDict(result.attributes))
1201 if (!inductionVariable.ssaName.name.empty()) {
1202 if (!
isdigit(inductionVariable.ssaName.name[1]))
1204 result.attributes.append(
1205 {builder.getStringAttr(
"inductionVarName"),
1206 builder.getStringAttr(inductionVariable.ssaName.name.drop_front())});
1212void ForOp::print(OpAsmPrinter &p) {
1213 p <<
" " << getInductionVar() <<
" = " << getLowerBound() <<
" to "
1214 << getUpperBound() <<
" step " << getStep();
1215 p <<
" : " << getInductionVar().getType() <<
' ';
1216 p.printRegion(getRegion(),
1219 p.printOptionalAttrDict((*this)->getAttrs(), {
"inductionVarName"});
1222LogicalResult ForOp::canonicalize(
ForOp op, PatternRewriter &rewriter) {
1224 if (matchPattern(op.getLowerBound(), mlir::m_ConstantInt(&lb)) &&
1225 matchPattern(op.getUpperBound(), mlir::m_ConstantInt(&ub)) &&
1226 matchPattern(op.getStep(), mlir::m_ConstantInt(&step)) &&
1229 rewriter.replaceAllUsesWith(op.getInductionVar(), op.getLowerBound());
1231 rewriter.eraseOp(op);
1241LogicalResult BPAssignOp::verify() {
1242 if (isa<sv::WireOp>(getDest().getDefiningOp()))
1244 "Verilog disallows procedural assignment to a net type (did you intend "
1245 "to use a variable type, e.g., sv.reg?)");
1249LogicalResult PAssignOp::verify() {
1250 if (isa<sv::WireOp>(getDest().getDefiningOp()))
1252 "Verilog disallows procedural assignment to a net type (did you intend "
1253 "to use a variable type, e.g., sv.reg?)");
1266 static std::optional<ArraySlice> getArraySlice(Value v) {
1267 auto *op = v.getDefiningOp();
1269 return std::nullopt;
1270 return TypeSwitch<Operation *, std::optional<ArraySlice>>(op)
1271 .Case<hw::ArrayGetOp, ArrayIndexInOutOp>(
1272 [](
auto arrayIndex) -> std::optional<ArraySlice> {
1274 arrayIndex.getIndex()
1275 .template getDefiningOp<hw::ConstantOp>();
1277 return std::nullopt;
1278 return ArraySlice{arrayIndex.getInput(),
1283 -> std::optional<ArraySlice> {
1284 auto constant = slice.getLowIndex().getDefiningOp<
hw::ConstantOp>();
1286 return std::nullopt;
1288 slice.getInput(), constant,
1290 hw::type_cast<hw::ArrayType>(slice.getType()).getNumElements()};
1292 .Case<sv::IndexedPartSelectInOutOp>(
1293 [](sv::IndexedPartSelectInOutOp index)
1294 -> std::optional<ArraySlice> {
1296 if (!constant || index.getDecrement())
1297 return std::nullopt;
1298 return ArraySlice{index.getInput(),
1302 .Default([](
auto) {
return std::nullopt; });
1306 static std::optional<std::pair<ArraySlice, ArraySlice>>
1307 getAssignedRange(Operation *op) {
1308 assert((isa<PAssignOp, BPAssignOp>(op) &&
"assignments are expected"));
1309 auto srcRange = ArraySlice::getArraySlice(op->getOperand(1));
1311 return std::nullopt;
1312 auto destRange = ArraySlice::getArraySlice(op->getOperand(0));
1314 return std::nullopt;
1316 return std::make_pair(*destRange, *srcRange);
1327template <
typename AssignTy>
1329 PatternRewriter &rewriter) {
1331 auto assignedRangeOpt = ArraySlice::getAssignedRange(op);
1332 if (!assignedRangeOpt)
1335 auto [dest, src] = *assignedRangeOpt;
1336 AssignTy nextAssign = dyn_cast_or_null<AssignTy>(op->getNextNode());
1337 bool changed =
false;
1338 SmallVector<Location> loc{op.getLoc()};
1340 while (nextAssign) {
1341 auto nextAssignedRange = ArraySlice::getAssignedRange(nextAssign);
1342 if (!nextAssignedRange)
1344 auto [nextDest, nextSrc] = *nextAssignedRange;
1346 if (dest.array != nextDest.array || src.array != nextSrc.array ||
1347 !
hw::isOffset(dest.start, nextDest.start, dest.size) ||
1351 dest.size += nextDest.size;
1352 src.size += nextSrc.size;
1354 loc.push_back(nextAssign.getLoc());
1355 rewriter.eraseOp(nextAssign);
1356 nextAssign = dyn_cast_or_null<AssignTy>(op->getNextNode());
1363 auto resultType = hw::ArrayType::get(
1364 hw::type_cast<hw::ArrayType>(src.array.getType()).getElementType(),
1366 auto newDest = sv::IndexedPartSelectInOutOp::create(
1367 rewriter, op.getLoc(), dest.array, dest.start, dest.size);
1369 src.array, src.start);
1370 auto newLoc = rewriter.getFusedLoc(loc);
1371 auto newOp = rewriter.replaceOpWithNewOp<AssignTy>(op, newDest, newSrc);
1372 newOp->setLoc(newLoc);
1376LogicalResult PAssignOp::canonicalize(PAssignOp op, PatternRewriter &rewriter) {
1380LogicalResult BPAssignOp::canonicalize(BPAssignOp op,
1381 PatternRewriter &rewriter) {
1389void InterfaceOp::build(OpBuilder &builder, OperationState &result,
1390 StringRef sym_name, std::function<
void()> body) {
1391 OpBuilder::InsertionGuard guard(builder);
1393 result.addAttribute(::SymbolTable::getSymbolAttrName(),
1394 builder.getStringAttr(sym_name));
1395 builder.createBlock(result.addRegion());
1400ModportType InterfaceOp::getModportType(StringRef modportName) {
1401 assert(lookupSymbol<InterfaceModportOp>(modportName) &&
1402 "Modport symbol not found.");
1403 auto *
ctxt = getContext();
1404 return ModportType::get(
1406 SymbolRefAttr::get(ctxt, getSymName(),
1407 {SymbolRefAttr::get(ctxt, modportName)}));
1410Type InterfaceOp::getSignalType(StringRef signalName) {
1411 InterfaceSignalOp signal = lookupSymbol<InterfaceSignalOp>(signalName);
1412 assert(signal &&
"Interface signal symbol not found.");
1413 return signal.getType();
1417 ArrayAttr &portsAttr) {
1419 auto *context = parser.getBuilder().getContext();
1421 SmallVector<Attribute, 8> ports;
1422 auto parseElement = [&]() -> ParseResult {
1423 auto direction = ModportDirectionAttr::parse(parser, {});
1427 FlatSymbolRefAttr signal;
1428 if (parser.parseAttribute(signal))
1431 ports.push_back(ModportStructAttr::get(
1432 context, cast<ModportDirectionAttr>(direction), signal));
1435 if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
1439 portsAttr = ArrayAttr::get(context, ports);
1444 ArrayAttr portsAttr) {
1446 llvm::interleaveComma(portsAttr, p, [&](Attribute attr) {
1447 auto port = cast<ModportStructAttr>(attr);
1448 p << stringifyEnum(port.getDirection().getValue());
1450 p.printSymbolName(port.getSignal().getRootReference().getValue());
1455void InterfaceSignalOp::build(mlir::OpBuilder &builder,
1456 ::mlir::OperationState &state, StringRef name,
1458 build(builder, state, name, mlir::TypeAttr::get(type));
1461void InterfaceModportOp::build(OpBuilder &builder, OperationState &state,
1462 StringRef name, ArrayRef<StringRef> inputs,
1463 ArrayRef<StringRef> outputs) {
1464 auto *
ctxt = builder.getContext();
1465 SmallVector<Attribute, 8> directions;
1466 auto inputDir = ModportDirectionAttr::get(ctxt, ModportDirection::input);
1467 auto outputDir = ModportDirectionAttr::get(ctxt, ModportDirection::output);
1468 for (
auto input : inputs)
1469 directions.push_back(ModportStructAttr::
get(
1470 ctxt, inputDir, SymbolRefAttr::
get(
ctxt, input)));
1471 for (
auto output : outputs)
1472 directions.push_back(ModportStructAttr::
get(
1473 ctxt, outputDir, SymbolRefAttr::
get(
ctxt, output)));
1474 build(builder, state, name, ArrayAttr::get(ctxt, directions));
1477std::optional<size_t> InterfaceInstanceOp::getTargetResultIndex() {
1479 return std::nullopt;
1484void InterfaceInstanceOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1485 setNameFn(getResult(),
getName());
1489LogicalResult InterfaceInstanceOp::verify() {
1491 return emitOpError(
"requires non-empty name");
1496InterfaceInstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1497 auto *symtable = SymbolTable::getNearestSymbolTable(*
this);
1499 return emitError(
"sv.interface.instance must exist within a region "
1500 "which has a symbol table.");
1501 auto ifaceTy = getType();
1502 auto *referencedOp =
1503 symbolTable.lookupSymbolIn(symtable, ifaceTy.getInterface());
1505 return emitError(
"Symbol not found: ") << ifaceTy.getInterface() <<
".";
1506 if (!isa<InterfaceOp>(referencedOp))
1507 return emitError(
"Symbol ")
1508 << ifaceTy.getInterface() <<
" is not an InterfaceOp.";
1515GetModportOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1516 auto *symtable = SymbolTable::getNearestSymbolTable(*
this);
1518 return emitError(
"sv.interface.instance must exist within a region "
1519 "which has a symbol table.");
1521 auto ifaceTy = getType();
1522 auto *referencedOp =
1523 symbolTable.lookupSymbolIn(symtable, ifaceTy.getModport());
1525 return emitError(
"Symbol not found: ") << ifaceTy.getModport() <<
".";
1526 if (!isa<InterfaceModportOp>(referencedOp))
1527 return emitError(
"Symbol ")
1528 << ifaceTy.getModport() <<
" is not an InterfaceModportOp.";
1532void GetModportOp::build(OpBuilder &builder, OperationState &state, Value value,
1534 auto ifaceTy = dyn_cast<InterfaceType>(value.getType());
1535 assert(ifaceTy &&
"GetModportOp expects an InterfaceType.");
1536 auto fieldAttr = SymbolRefAttr::get(builder.getContext(), field);
1538 SymbolRefAttr::get(ifaceTy.getInterface().getRootReference(), fieldAttr);
1539 build(builder, state, ModportType::get(builder.getContext(), modportSym),
1547 return dyn_cast_or_null<InterfaceModportOp>(
1551void ReadInterfaceSignalOp::build(OpBuilder &builder, OperationState &state,
1552 Value iface, StringRef signalName) {
1553 auto ifaceTy = dyn_cast<InterfaceType>(iface.getType());
1554 assert(ifaceTy &&
"ReadInterfaceSignalOp expects an InterfaceType.");
1555 auto fieldAttr = SymbolRefAttr::get(builder.getContext(), signalName);
1556 InterfaceOp ifaceDefOp = SymbolTable::lookupNearestSymbolFrom<InterfaceOp>(
1557 iface.getDefiningOp(), ifaceTy.getInterface());
1559 "ReadInterfaceSignalOp could not resolve an InterfaceOp.");
1560 build(builder, state, ifaceDefOp.getSignalType(signalName), iface, fieldAttr);
1567 return dyn_cast_or_null<InterfaceSignalOp>(
1572 FlatSymbolRefAttr &signalName) {
1573 SymbolRefAttr fullSym;
1574 if (p.parseAttribute(fullSym) || fullSym.getNestedReferences().size() != 1)
1577 auto *ctxt = p.getBuilder().getContext();
1578 ifaceTy = InterfaceType::get(
1579 ctxt, FlatSymbolRefAttr::get(fullSym.getRootReference()));
1580 signalName = FlatSymbolRefAttr::get(fullSym.getLeafReference());
1585 FlatSymbolRefAttr signalName) {
1586 InterfaceType ifaceTy = dyn_cast<InterfaceType>(type);
1587 assert(ifaceTy &&
"Expected an InterfaceType");
1588 auto sym = SymbolRefAttr::get(ifaceTy.getInterface().getRootReference(),
1594 auto ifaceTy = dyn_cast<InterfaceType>(ifaceVal.getType());
1597 InterfaceOp iface = SymbolTable::lookupNearestSymbolFrom<InterfaceOp>(
1598 ifaceVal.getDefiningOp(), ifaceTy.getInterface());
1601 InterfaceSignalOp signal = iface.lookupSymbol<InterfaceSignalOp>(signalName);
1609 FlatSymbolRefAttr
interface = getInterfaceType().getInterface();
1614 auto topLevelModuleOp = (*this)->getParentOfType<ModuleOp>();
1615 if (!topLevelModuleOp)
1618 return topLevelModuleOp.lookupSymbol(interface);
1621LogicalResult AssignInterfaceSignalOp::verify() {
1625LogicalResult ReadInterfaceSignalOp::verify() {
1633void WireOp::build(OpBuilder &builder, OperationState &odsState,
1635 hw::InnerSymAttr innerSym) {
1637 name = builder.getStringAttr(
"");
1642 odsState.addAttribute(
"name", name);
1648void WireOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1650 auto nameAttr = (*this)->getAttrOfType<StringAttr>(
"name");
1651 if (!nameAttr.getValue().empty())
1652 setNameFn(getResult(), nameAttr.getValue());
1655std::optional<size_t> WireOp::getTargetResultIndex() {
return 0; }
1658LogicalResult WireOp::canonicalize(
WireOp wire, PatternRewriter &rewriter) {
1664 if (wire.getInnerSymAttr())
1669 SmallVector<sv::ReadInOutOp> reads;
1672 for (
auto *user : wire->getUsers()) {
1673 if (
auto read = dyn_cast<sv::ReadInOutOp>(user)) {
1674 reads.push_back(read);
1679 auto assign = dyn_cast<sv::AssignOp>(user);
1682 if (!assign || write)
1698 connected = ConstantZOp::create(
1699 rewriter, wire.getLoc(),
1700 cast<InOutType>(wire.getResult().getType()).getElementType());
1701 }
else if (isa<hw::HWModuleOp>(write->getParentOp()))
1702 connected = write.getSrc();
1709 if (
auto *connectedOp = connected.getDefiningOp())
1710 if (!wire.getName().empty())
1711 rewriter.modifyOpInPlace(connectedOp, [&] {
1712 connectedOp->setAttr(
"sv.namehint", wire.getNameAttr());
1716 for (
auto read : reads)
1717 rewriter.replaceOp(read, connected);
1721 rewriter.eraseOp(write);
1722 rewriter.eraseOp(wire);
1732 auto elemTy = cast<hw::InOutType>(type).getElementType();
1733 if (isa<IntegerType>(elemTy))
1734 return hw::InOutType::get(IntegerType::get(type.getContext(), width));
1735 if (isa<hw::ArrayType>(elemTy))
1736 return hw::InOutType::get(hw::ArrayType::get(
1737 cast<hw::ArrayType>(elemTy).getElementType(), width));
1741LogicalResult IndexedPartSelectInOutOp::inferReturnTypes(
1742 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
1743 DictionaryAttr attrs, mlir::OpaqueProperties properties,
1744 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
1745 Adaptor adaptor(operands, attrs, properties, regions);
1746 auto width = adaptor.getWidthAttr();
1751 width.getValue().getZExtValue());
1754 results.push_back(typ);
1758LogicalResult IndexedPartSelectInOutOp::verify() {
1759 unsigned inputWidth = 0, resultWidth = 0;
1761 auto inputElemTy = cast<InOutType>(getInput().getType()).getElementType();
1762 auto resultElemTy = cast<InOutType>(getType()).getElementType();
1763 if (
auto i = dyn_cast<IntegerType>(inputElemTy))
1764 inputWidth = i.getWidth();
1765 else if (
auto i = hw::type_cast<hw::ArrayType>(inputElemTy))
1766 inputWidth = i.getNumElements();
1768 return emitError(
"input element type must be Integer or Array");
1770 if (
auto resType = dyn_cast<IntegerType>(resultElemTy))
1771 resultWidth = resType.getWidth();
1772 else if (
auto resType = hw::type_cast<hw::ArrayType>(resultElemTy))
1773 resultWidth = resType.getNumElements();
1775 return emitError(
"result element type must be Integer or Array");
1777 if (opWidth > inputWidth)
1778 return emitError(
"slice width should not be greater than input width");
1779 if (opWidth != resultWidth)
1780 return emitError(
"result width must be equal to slice width");
1784OpFoldResult IndexedPartSelectInOutOp::fold(FoldAdaptor) {
1785 if (getType() == getInput().getType())
1794LogicalResult IndexedPartSelectOp::inferReturnTypes(
1795 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
1796 DictionaryAttr attrs, mlir::OpaqueProperties properties,
1797 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
1798 Adaptor adaptor(operands, attrs, properties, regions);
1799 auto width = adaptor.getWidthAttr();
1803 results.push_back(IntegerType::get(context, width.getInt()));
1807LogicalResult IndexedPartSelectOp::verify() {
1810 unsigned resultWidth = cast<IntegerType>(getType()).getWidth();
1811 unsigned inputWidth = cast<IntegerType>(getInput().getType()).getWidth();
1813 if (opWidth > inputWidth)
1814 return emitError(
"slice width should not be greater than input width");
1815 if (opWidth != resultWidth)
1816 return emitError(
"result width must be equal to slice width");
1824LogicalResult StructFieldInOutOp::inferReturnTypes(
1825 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
1826 DictionaryAttr attrs, mlir::OpaqueProperties properties,
1827 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
1828 Adaptor adaptor(operands, attrs, properties, regions);
1829 auto field = adaptor.getFieldAttr();
1834 auto resultType = structType.getFieldType(field);
1838 results.push_back(hw::InOutType::get(resultType));
1846LogicalResult AliasOp::verify() {
1848 if (getAliases().size() < 2)
1849 return emitOpError(
"alias must have at least two operands");
1862 for (
auto &op : llvm::reverse(body->getOperations())) {
1863 if (
auto instance = dyn_cast<Op>(op)) {
1864 if (
auto innerSym = instance.getInnerSym())
1865 if (innerSym->getSymName() == name)
1869 if (
auto ifdef = dyn_cast<IfDefOp>(op)) {
1871 findInstanceSymbolInBlock<Op>(name, ifdef.getThenBlock()))
1873 if (ifdef.hasElse())
1875 findInstanceSymbolInBlock<Op>(name, ifdef.getElseBlock()))
1886 return cast<hw::InstanceOp>(result.getOp());
1890 auto topLevelModuleOp = (*this)->getParentOfType<ModuleOp>();
1891 if (!topLevelModuleOp)
1894 auto hwModule = dyn_cast_or_null<hw::HWModuleOp>(
1895 topLevelModuleOp.lookupSymbol(getInstance().getModule()));
1900 return findInstanceSymbolInBlock<hw::InstanceOp>(getInstance().
getName(),
1901 hwModule.getBodyBlock());
1905LogicalResult BindOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1906 auto module = (*this)->getParentOfType<mlir::ModuleOp>();
1907 auto hwModule = dyn_cast_or_null<hw::HWModuleOp>(
1908 symbolTable.lookupSymbolIn(module, getInstance().getModule()));
1910 return emitError(
"Referenced module doesn't exist ")
1911 << getInstance().getModule() <<
"::" << getInstance().getName();
1913 auto inst = findInstanceSymbolInBlock<hw::InstanceOp>(
1914 getInstance().
getName(), hwModule.getBodyBlock());
1916 return emitError(
"Referenced instance doesn't exist ")
1917 << getInstance().getModule() <<
"::" << getInstance().getName();
1918 if (!inst.getDoNotPrint())
1919 return emitError(
"Referenced instance isn't marked as doNotPrint");
1923void BindOp::build(OpBuilder &builder, OperationState &odsState, StringAttr mod,
1925 auto ref = hw::InnerRefAttr::get(mod, name);
1926 odsState.addAttribute(
"instance", ref);
1933sv::InterfaceInstanceOp
1938 return cast<sv::InterfaceInstanceOp>(result.getOp());
1942 auto *symbolTable = SymbolTable::getNearestSymbolTable(*
this);
1951 return findInstanceSymbolInBlock<sv::InterfaceInstanceOp>(
1952 getInstance().
getName(), &parentOp->getRegion(0).front());
1957BindInterfaceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1959 symbolTable.lookupNearestSymbolFrom(*
this, getInstance().getModule());
1961 return emitError(
"Referenced module doesn't exist ")
1962 << getInstance().getModule() <<
"::" << getInstance().getName();
1964 auto inst = findInstanceSymbolInBlock<sv::InterfaceInstanceOp>(
1965 getInstance().
getName(), &parentOp->getRegion(0).front());
1967 return emitError(
"Referenced interface doesn't exist ")
1968 << getInstance().getModule() <<
"::" << getInstance().getName();
1969 if (!inst.getDoNotPrint())
1970 return emitError(
"Referenced interface isn't marked as doNotPrint");
1979 StringAttr &terminalAttr) {
1980 SmallVector<Attribute> strings;
1981 ParseResult ret = parser.parseCommaSeparatedList([&]() {
1984 if (succeeded(parser.parseOptionalKeyword(&keyword))) {
1985 strings.push_back(parser.getBuilder().getStringAttr(keyword));
1988 if (succeeded(parser.parseAttribute(
1989 result, parser.getBuilder().getType<NoneType>()))) {
1990 strings.push_back(result);
1995 if (succeeded(ret)) {
1996 pathAttr = parser.getBuilder().getArrayAttr(
1997 ArrayRef<Attribute>(strings).drop_back());
1998 terminalAttr = cast<StringAttr>(*strings.rbegin());
2004 StringAttr terminalAttr) {
2005 llvm::interleaveComma(pathAttr, p);
2006 p <<
", " << terminalAttr;
2010LogicalResult XMRRefOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2011 auto *table = SymbolTable::getNearestSymbolTable(*
this);
2012 auto path = dyn_cast_or_null<hw::HierPathOp>(
2013 symbolTable.lookupSymbolIn(table, getRefAttr()));
2015 return emitError(
"Referenced path doesn't exist ") << getRefAttr();
2022 if (
auto *result = cache->
getDefinition(getRefAttr().getAttr()))
2023 return cast<hw::HierPathOp>(result);
2025 auto topLevelModuleOp = (*this)->getParentOfType<ModuleOp>();
2026 return topLevelModuleOp.lookupSymbol<hw::HierPathOp>(getRefAttr().getValue());
2034 PatternRewriter &rewriter,
2037 if (constant.getValue().isZero() == eraseIfZero) {
2038 rewriter.eraseOp(op);
2045template <
class Op,
bool EraseIfZero = false>
2047 PatternRewriter &rewriter) {
2051void AssertOp::getCanonicalizationPatterns(RewritePatternSet &results,
2052 MLIRContext *context) {
2053 results.add(canonicalizeImmediateVerifOp<AssertOp>);
2056void AssumeOp::getCanonicalizationPatterns(RewritePatternSet &results,
2057 MLIRContext *context) {
2058 results.add(canonicalizeImmediateVerifOp<AssumeOp>);
2061void CoverOp::getCanonicalizationPatterns(RewritePatternSet &results,
2062 MLIRContext *context) {
2063 results.add(canonicalizeImmediateVerifOp<CoverOp, /* EraseIfZero = */ true>);
2066template <
class Op,
bool EraseIfZero = false>
2068 PatternRewriter &rewriter) {
2072void AssertConcurrentOp::getCanonicalizationPatterns(RewritePatternSet &results,
2073 MLIRContext *context) {
2074 results.add(canonicalizeConcurrentVerifOp<AssertConcurrentOp>);
2077void AssumeConcurrentOp::getCanonicalizationPatterns(RewritePatternSet &results,
2078 MLIRContext *context) {
2079 results.add(canonicalizeConcurrentVerifOp<AssumeConcurrentOp>);
2082void CoverConcurrentOp::getCanonicalizationPatterns(RewritePatternSet &results,
2083 MLIRContext *context) {
2085 canonicalizeConcurrentVerifOp<CoverConcurrentOp, /* EraseIfZero */ true>);
2095 ArrayAttr &caseNamesArray,
2096 SmallVectorImpl<std::unique_ptr<Region>> &caseRegions) {
2098 SmallVector<Attribute> names;
2099 while (!p.parseOptionalKeyword(
"case")) {
2102 std::unique_ptr<Region> region = std::make_unique<Region>();
2103 if (p.parseLParen() || p.parseAttribute(
pattern) || p.parseComma() ||
2104 p.parseAttribute(name) || p.parseRParen() || p.parseRegion(*region))
2107 names.push_back(name);
2108 if (region->empty())
2109 region->push_back(
new Block());
2110 caseRegions.push_back(std::move(region));
2112 patternsArray = p.getBuilder().getArrayAttr(
patterns);
2113 caseNamesArray = p.getBuilder().getArrayAttr(names);
2120 ArrayAttr namesArray,
2121 MutableArrayRef<Region> caseRegions) {
2122 assert(patternsArray.size() == caseRegions.size());
2123 assert(patternsArray.size() == namesArray.size());
2124 for (
size_t i = 0, e = caseRegions.size(); i < e; ++i) {
2126 p <<
"case (" << patternsArray[i] <<
", " << namesArray[i] <<
") ";
2127 p.printRegion(caseRegions[i]);
2132LogicalResult GenerateCaseOp::verify() {
2133 size_t numPatterns = getCasePatterns().size();
2134 if (getCaseRegions().size() != numPatterns ||
2135 getCaseNames().size() != numPatterns)
2137 "Size of caseRegions, patterns, and caseNames must match");
2139 StringSet<> usedNames;
2140 for (Attribute name : getCaseNames()) {
2141 StringAttr nameStr = dyn_cast<StringAttr>(name);
2143 return emitOpError(
"caseNames must all be string attributes");
2144 if (usedNames.contains(nameStr.getValue()))
2145 return emitOpError(
"caseNames must be unique");
2146 usedNames.insert(nameStr.getValue());
2154ModportStructAttr ModportStructAttr::get(MLIRContext *context,
2155 ModportDirection direction,
2156 FlatSymbolRefAttr signal) {
2157 return get(context, ModportDirectionAttr::get(context, direction), signal);
2164ParseResult FuncOp::parse(OpAsmParser &parser, OperationState &result) {
2165 auto builder = parser.getBuilder();
2167 (void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
2170 StringAttr nameAttr;
2171 if (parser.parseSymbolName(nameAttr, SymbolTable::getSymbolAttrName(),
2175 SmallVector<hw::module_like_impl::PortParse> ports;
2181 result.addAttribute(FuncOp::getModuleTypeAttrName(result.name), modType);
2185 auto unknownLoc = builder.getUnknownLoc();
2186 SmallVector<Attribute> attrs, inputLocs, outputLocs;
2187 auto nonEmptyLocsFn = [unknownLoc](Attribute attr) {
2188 return attr && cast<Location>(attr) != unknownLoc;
2191 for (
auto &port : ports) {
2192 attrs.push_back(port.attrs ? port.attrs : builder.getDictionaryAttr({}));
2193 auto loc = port.sourceLoc ? Location(*port.sourceLoc) : unknownLoc;
2198 result.addAttribute(FuncOp::getPerArgumentAttrsAttrName(result.name),
2199 builder.getArrayAttr(attrs));
2201 if (llvm::any_of(outputLocs, nonEmptyLocsFn))
2202 result.addAttribute(FuncOp::getResultLocsAttrName(result.name),
2203 builder.getArrayAttr(outputLocs));
2205 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
2209 SmallVector<OpAsmParser::Argument, 4> entryArgs;
2210 for (
auto &port : ports)
2212 entryArgs.push_back(port);
2216 auto *body = result.addRegion();
2217 llvm::SMLoc loc = parser.getCurrentLocation();
2219 mlir::OptionalParseResult parseResult =
2220 parser.parseOptionalRegion(*body, entryArgs,
2222 if (parseResult.has_value()) {
2223 if (failed(*parseResult))
2227 return parser.emitError(loc,
"expected non-empty function body");
2229 if (llvm::any_of(inputLocs, nonEmptyLocsFn))
2230 result.addAttribute(FuncOp::getInputLocsAttrName(result.name),
2231 builder.getArrayAttr(inputLocs));
2237void FuncOp::getAsmBlockArgumentNames(mlir::Region ®ion,
2242 auto func = cast<FuncOp>(region.getParentOp());
2244 auto *block = ®ion.front();
2246 auto names = func.getModuleType().getInputNames();
2247 for (
size_t i = 0, e = block->getNumArguments(); i != e; ++i) {
2249 setNameFn(block->getArgument(i), cast<StringAttr>(names[i]));
2253Type FuncOp::getExplicitlyReturnedType() {
2254 if (!getPerArgumentAttrs() || getNumOutputs() == 0)
2259 auto lastArgumentAttr = dyn_cast<DictionaryAttr>(
2260 getPerArgumentAttrsAttr()[getPerArgumentAttrsAttr().size() - 1]);
2263 lastArgumentAttr.getAs<UnitAttr>(getExplicitlyReturnedAttrName()))
2264 return lastArgument.type;
2268ArrayRef<Attribute> FuncOp::getAllPortAttrs() {
2269 if (getPerArgumentAttrs())
2270 return getPerArgumentAttrs()->getValue();
2274void FuncOp::setAllPortAttrs(ArrayRef<Attribute> attrs) {
2275 setPerArgumentAttrsAttr(ArrayAttr::get(getContext(), attrs));
2278void FuncOp::removeAllPortAttrs() { setPerArgumentAttrsAttr({}); }
2279SmallVector<Location> FuncOp::getAllPortLocs() {
2280 SmallVector<Location> portLocs;
2282 auto resultLocs = getResultLocsAttr();
2283 unsigned inputCount = 0;
2285 auto unknownLoc = UnknownLoc::get(getContext());
2287 auto inputLocs = getInputLocsAttr();
2288 for (
unsigned i = 0, e =
getNumPorts(); i < e; ++i) {
2289 if (modType.isOutput(i)) {
2290 auto loc = resultLocs
2292 resultLocs.getValue()[portLocs.size() - inputCount])
2294 portLocs.push_back(loc);
2296 auto loc = body ? body->getArgument(inputCount).getLoc()
2297 : (inputLocs ? cast<Location>(inputLocs[inputCount])
2299 portLocs.push_back(loc);
2306void FuncOp::setAllPortLocsAttrs(llvm::ArrayRef<mlir::Attribute> locs) {
2307 SmallVector<Attribute> resultLocs, inputLocs;
2308 unsigned inputCount = 0;
2311 for (
unsigned i = 0, e =
getNumPorts(); i < e; ++i) {
2312 if (modType.isOutput(i))
2313 resultLocs.push_back(locs[i]);
2315 body->getArgument(inputCount++).setLoc(cast<Location>(locs[i]));
2317 inputLocs.push_back(locs[i]);
2319 setResultLocsAttr(ArrayAttr::get(getContext(), resultLocs));
2321 setInputLocsAttr(ArrayAttr::get(getContext(), inputLocs));
2324SmallVector<hw::PortInfo> FuncOp::getPortList() {
return getPortList(
false); }
2327 auto modTy = getHWModuleType();
2328 auto emptyDict = DictionaryAttr::get(getContext());
2329 LocationAttr loc = getPortLoc(idx);
2330 DictionaryAttr attrs = dyn_cast_or_null<DictionaryAttr>(getPortAttrs(idx));
2333 return {modTy.getPorts()[idx],
2334 modTy.isOutput(idx) ? modTy.getOutputIdForPortId(idx)
2335 : modTy.getInputIdForPortId(idx),
2339SmallVector<hw::PortInfo> FuncOp::getPortList(
bool excludeExplicitReturn) {
2341 auto emptyDict = DictionaryAttr::get(getContext());
2342 auto skipLastArgument = getExplicitlyReturnedType() && excludeExplicitReturn;
2343 SmallVector<hw::PortInfo> retval;
2345 for (
unsigned i = 0, e = skipLastArgument ? modTy.getNumPorts() - 1
2348 DictionaryAttr attrs = emptyDict;
2349 if (
auto perArgumentAttr = getPerArgumentAttrs())
2350 if (
auto argumentAttr =
2351 dyn_cast_or_null<DictionaryAttr>((*perArgumentAttr)[i]))
2352 attrs = argumentAttr;
2354 retval.push_back({modTy.getPorts()[i],
2355 modTy.isOutput(i) ? modTy.getOutputIdForPortId(i)
2356 : modTy.getInputIdForPortId(i),
2357 attrs, portAttr[i]});
2362void FuncOp::print(OpAsmPrinter &p) {
2366 op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName())
2370 StringRef visibilityAttrName = SymbolTable::getVisibilityAttrName();
2371 if (
auto visibility = op->getAttrOfType<StringAttr>(visibilityAttrName))
2372 p << visibility.getValue() <<
' ';
2373 p.printSymbolName(funcName);
2375 p, op.getBody(), op.getModuleType(),
2376 op.getPerArgumentAttrsAttr()
2377 ? ArrayRef<Attribute>(op.getPerArgumentAttrsAttr().getValue())
2378 : ArrayRef<Attribute>{},
2381 mlir::function_interface_impl::printFunctionAttributes(
2383 {visibilityAttrName, getModuleTypeAttrName(),
2384 getPerArgumentAttrsAttrName(), getInputLocsAttrName(),
2385 getResultLocsAttrName()});
2387 Region &body = op->getRegion(0);
2388 if (!body.empty()) {
2390 p.printRegion(body,
false,
2399LogicalResult ReturnOp::verify() {
2400 auto func = getParentOp<sv::FuncOp>();
2401 auto funcResults = func.getResultTypes();
2402 auto returnedValues = getOperands();
2403 if (funcResults.size() != returnedValues.size())
2404 return emitOpError(
"must have same number of operands as region results.");
2406 for (
size_t i = 0, e = funcResults.size(); i < e; ++i) {
2407 if (funcResults[i] != returnedValues[i].getType()) {
2408 emitOpError(
"output types must match function. In "
2410 << i <<
", expected " << funcResults[i] <<
", but got "
2411 << returnedValues[i].getType() <<
".";
2424 mlir::Operation::result_range results) {
2425 if (!op.getExplicitlyReturnedType())
2427 return results.back();
2430Value FuncCallOp::getExplicitlyReturnedValue(sv::FuncOp op) {
2434Value FuncCallProceduralOp::getExplicitlyReturnedValue(sv::FuncOp op) {
2439FuncCallProceduralOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2440 auto referencedOp = dyn_cast_or_null<sv::FuncOp>(
2441 symbolTable.lookupNearestSymbolFrom(*
this, getCalleeAttr()));
2443 return emitError(
"cannot find function declaration '")
2444 << getCallee() <<
"'";
2448LogicalResult FuncCallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2449 auto referencedOp = dyn_cast_or_null<sv::FuncOp>(
2450 symbolTable.lookupNearestSymbolFrom(*
this, getCalleeAttr()));
2452 return emitError(
"cannot find function declaration '")
2453 << getCallee() <<
"'";
2456 if (referencedOp.getNumOutputs() != 1 ||
2457 !referencedOp.getExplicitlyReturnedType()) {
2458 auto diag = emitError()
2459 <<
"function called in a non-procedural region must "
2460 "return a single result";
2461 diag.attachNote(referencedOp.getLoc()) <<
"doesn't satisfy the constraint";
2472FuncDPIImportOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2473 auto referencedOp = dyn_cast_or_null<sv::FuncOp>(
2474 symbolTable.lookupNearestSymbolFrom(*
this, getCalleeAttr()));
2477 return emitError(
"cannot find function declaration '")
2478 << getCallee() <<
"'";
2479 if (!referencedOp.isDeclaration())
2480 return emitError(
"imported function must be a declaration but '")
2481 << getCallee() <<
"' is defined";
2492static LogicalResult
verify(Value clock,
bool eventExists, mlir::Location loc) {
2493 if ((!clock && eventExists) || (clock && !eventExists))
2494 return mlir::emitError(
2495 loc,
"Every clock must be associated to an even and vice-versa!");
2492static LogicalResult
verify(Value clock,
bool eventExists, mlir::Location loc) {
…}
2500LogicalResult AssertPropertyOp::verify() {
2505LogicalResult AssumePropertyOp::verify() {
2510LogicalResult CoverPropertyOp::verify() {
2520#define GET_OP_CLASSES
2521#include "circt/Dialect/SV/SV.cpp.inc"
assert(baseType &&"element must be base type")
static bool hasSVAttributes(Operation *op)
static LogicalResult canonicalizeImmediateVerifOp(Op op, PatternRewriter &rewriter)
static void replaceOpWithRegion(PatternRewriter &rewriter, Operation *op, Region ®ion)
Replaces the given op with the contents of the given single-block region.
static LogicalResult eraseIfZeroOrNotZero(Operation *op, Value predicate, Value enable, PatternRewriter &rewriter, bool eraseIfZero)
static SmallVector< PortInfo > getPortList(ModuleTy &mod)
static SmallVector< Location > getAllPortLocs(ModTy module)
static Location getLoc(DefSlot slot)
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
bool parseCaseRegions(OpAsmParser &p, ArrayAttr &patternsArray, ArrayAttr &caseNamesArray, SmallVectorImpl< std::unique_ptr< Region > > &caseRegions)
Parse cases formatted like: case (pattern, "name") { ... }.
ParseResult parseIfaceTypeAndSignal(OpAsmParser &p, Type &ifaceTy, FlatSymbolRefAttr &signalName)
LogicalResult verifySignalExists(Value ifaceVal, FlatSymbolRefAttr signalName)
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)
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 SmallVector< CasePatternBit > getPatternBitsForValue(const APInt &value)
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)
static Operation * lookupSymbolInNested(Operation *symbolTableOp, StringRef symbol)
Returns the operation registered with the given symbol name with the regions of 'symbolTableOp'.
void printXMRPath(OpAsmPrinter &p, XMROp op, ArrayAttr pathAttr, StringAttr terminalAttr)
static InstancePath empty
This stores lookup tables to make manipulating and working with the IR more efficient.
HWSymbolCache::Item getInnerDefinition(mlir::StringAttr modSymbol, mlir::StringAttr name) const
mlir::Operation * getDefinition(mlir::Attribute attr) const override
Lookup a definition for 'symbol' in the cache.
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.
create(array_value, low_index, ret_type)
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.
Direction
The direction of a Component or Cell port.
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.
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.
bool isHWEnumType(mlir::Type type)
Return true if the specified type is a HW Enum type.
mlir::Type getCanonicalType(mlir::Type type)
CasePatternBit
This describes the bit in a pattern, 0/1/x/z.
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.
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.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
This holds the name, type, direction of a module's ports.