20#include "mlir/IR/Matchers.h"
21#include "mlir/IR/PatternMatch.h"
22#include "llvm/ADT/APSInt.h"
23#include "llvm/ADT/SmallPtrSet.h"
24#include "llvm/ADT/StringExtras.h"
25#include "llvm/ADT/TypeSwitch.h"
28using namespace firrtl;
32static Value
dropWrite(PatternRewriter &rewriter, OpResult old,
34 SmallPtrSet<Operation *, 8> users;
35 for (
auto *user : old.getUsers())
37 for (Operation *user : users)
38 if (
auto connect = dyn_cast<FConnectLike>(user))
39 if (connect.getDest() == old)
40 rewriter.eraseOp(user);
50 if (op->getNumRegions() != 0)
52 return mlir::isPure(op) || isa<NodeOp, WireOp>(op);
60 Operation *op = passthrough.getDefiningOp();
63 assert(op &&
"passthrough must be an operation");
64 Operation *oldOp = old.getOwner();
65 auto name = oldOp->getAttrOfType<StringAttr>(
"name");
67 op->setAttr(
"name", name);
75#include "circt/Dialect/FIRRTL/FIRRTLCanonicalization.h.inc"
83 auto resultType = type_cast<IntType>(op->getResult(0).getType());
84 if (!resultType.hasWidth())
86 for (Value operand : op->getOperands())
87 if (!type_cast<IntType>(operand.getType()).hasWidth())
94 auto t = type_dyn_cast<UIntType>(type);
95 if (!t || !t.hasWidth() || t.getWidth() != 1)
102static void updateName(PatternRewriter &rewriter, Operation *op,
107 assert((!isa<InstanceOp, RegOp, RegResetOp>(op)) &&
"Should never rename");
108 auto newName = name.getValue();
109 auto newOpName = op->getAttrOfType<StringAttr>(
"name");
112 newName =
chooseName(newOpName.getValue(), name.getValue());
114 if (!newOpName || newOpName.getValue() != newName)
115 rewriter.modifyOpInPlace(
116 op, [&] { op->setAttr(
"name", rewriter.getStringAttr(newName)); });
124 if (
auto *newOp = newValue.getDefiningOp()) {
125 auto name = op->getAttrOfType<StringAttr>(
"name");
128 rewriter.replaceOp(op, newValue);
134template <
typename OpTy,
typename... Args>
136 Operation *op, Args &&...args) {
137 auto name = op->getAttrOfType<StringAttr>(
"name");
139 rewriter.replaceOpWithNewOp<OpTy>(op, std::forward<Args>(args)...);
147 if (
auto namableOp = dyn_cast<firrtl::FNamableOp>(op))
148 return namableOp.hasDroppableName();
159static std::optional<APSInt>
161 assert(type_cast<IntType>(operand.getType()) &&
162 "getExtendedConstant is limited to integer types");
169 if (IntegerAttr result = dyn_cast_or_null<IntegerAttr>(constant))
174 if (type_cast<IntType>(operand.getType()).getWidth() == 0)
175 return APSInt(destWidth,
176 type_cast<IntType>(operand.getType()).isUnsigned());
184 if (
auto attr = dyn_cast<BoolAttr>(operand))
185 return APSInt(APInt(1, attr.getValue()));
186 if (
auto attr = dyn_cast<IntegerAttr>(operand))
187 return attr.getAPSInt();
195 return cst->isZero();
222 Operation *op, ArrayRef<Attribute> operands,
BinOpKind opKind,
223 const function_ref<APInt(
const APSInt &,
const APSInt &)> &calculate) {
224 assert(operands.size() == 2 &&
"binary op takes two operands");
227 auto resultType = type_cast<IntType>(op->getResult(0).getType());
228 if (resultType.getWidthOrSentinel() < 0)
232 if (resultType.getWidthOrSentinel() == 0)
233 return getIntAttr(resultType, APInt(0, 0, resultType.isSigned()));
239 type_cast<IntType>(op->getOperand(0).getType()).getWidthOrSentinel();
241 type_cast<IntType>(op->getOperand(1).getType()).getWidthOrSentinel();
242 if (
auto lhs = dyn_cast_or_null<IntegerAttr>(operands[0]))
243 lhsWidth = std::max<int32_t>(lhsWidth, lhs.getValue().getBitWidth());
244 if (
auto rhs = dyn_cast_or_null<IntegerAttr>(operands[1]))
245 rhsWidth = std::max<int32_t>(rhsWidth, rhs.getValue().getBitWidth());
249 int32_t operandWidth;
252 operandWidth = resultType.getWidthOrSentinel();
257 operandWidth = std::max(1, std::max(lhsWidth, rhsWidth));
261 std::max(std::max(lhsWidth, rhsWidth), resultType.getWidthOrSentinel());
272 APInt resultValue = calculate(*lhs, *rhs);
277 resultValue = resultValue.trunc(resultType.getWidthOrSentinel());
279 assert((
unsigned)resultType.getWidthOrSentinel() ==
280 resultValue.getBitWidth());
293 Operation *op, PatternRewriter &rewriter,
294 const function_ref<OpFoldResult(ArrayRef<Attribute>)> &canonicalize) {
296 if (op->getNumResults() != 1)
298 auto type = type_dyn_cast<FIRRTLBaseType>(op->getResult(0).getType());
303 auto width = type.getBitWidthOrSentinel();
308 SmallVector<Attribute, 3> constOperands;
309 constOperands.reserve(op->getNumOperands());
310 for (
auto operand : op->getOperands()) {
312 if (
auto *defOp = operand.getDefiningOp())
313 TypeSwitch<Operation *>(defOp).Case<ConstantOp, SpecialConstantOp>(
314 [&](
auto op) { attr = op.getValueAttr(); });
315 constOperands.push_back(attr);
320 auto result = canonicalize(constOperands);
324 if (
auto cst = dyn_cast<Attribute>(result))
325 resultValue = op->getDialect()
326 ->materializeConstant(rewriter, cst, type, op->getLoc())
329 resultValue = cast<Value>(result);
333 type_cast<FIRRTLBaseType>(resultValue.getType()).getBitWidthOrSentinel())
334 resultValue = PadPrimOp::create(rewriter, op->getLoc(), resultValue, width);
337 if (type_isa<SIntType>(type) && type_isa<UIntType>(resultValue.getType()))
338 resultValue = AsSIntPrimOp::create(rewriter, op->getLoc(), resultValue);
339 else if (type_isa<UIntType>(type) &&
340 type_isa<SIntType>(resultValue.getType()))
341 resultValue = AsUIntPrimOp::create(rewriter, op->getLoc(), resultValue);
343 assert(type == resultValue.getType() &&
"canonicalization changed type");
351 return bitWidth > 0 ? APInt::getMaxValue(bitWidth) : APInt();
357 return bitWidth > 0 ? APInt::getSignedMinValue(bitWidth) : APInt();
363 return bitWidth > 0 ? APInt::getSignedMaxValue(bitWidth) : APInt();
370OpFoldResult ConstantOp::fold(FoldAdaptor adaptor) {
371 assert(adaptor.getOperands().empty() &&
"constant has no operands");
372 return getValueAttr();
375OpFoldResult SpecialConstantOp::fold(FoldAdaptor adaptor) {
376 assert(adaptor.getOperands().empty() &&
"constant has no operands");
377 return getValueAttr();
380OpFoldResult AggregateConstantOp::fold(FoldAdaptor adaptor) {
381 assert(adaptor.getOperands().empty() &&
"constant has no operands");
382 return getFieldsAttr();
385OpFoldResult StringConstantOp::fold(FoldAdaptor adaptor) {
386 assert(adaptor.getOperands().empty() &&
"constant has no operands");
387 return getValueAttr();
390OpFoldResult FIntegerConstantOp::fold(FoldAdaptor adaptor) {
391 assert(adaptor.getOperands().empty() &&
"constant has no operands");
392 return getValueAttr();
395OpFoldResult BoolConstantOp::fold(FoldAdaptor adaptor) {
396 assert(adaptor.getOperands().empty() &&
"constant has no operands");
397 return getValueAttr();
400OpFoldResult DoubleConstantOp::fold(FoldAdaptor adaptor) {
401 assert(adaptor.getOperands().empty() &&
"constant has no operands");
402 return getValueAttr();
409OpFoldResult AddPrimOp::fold(FoldAdaptor adaptor) {
412 [=](
const APSInt &a,
const APSInt &b) { return a + b; });
415void AddPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
416 MLIRContext *context) {
417 results.insert<patterns::moveConstAdd, patterns::AddOfZero,
418 patterns::AddOfSelf, patterns::AddOfPad>(context);
421OpFoldResult SubPrimOp::fold(FoldAdaptor adaptor) {
424 [=](
const APSInt &a,
const APSInt &b) { return a - b; });
427void SubPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
428 MLIRContext *context) {
429 results.insert<patterns::SubOfZero, patterns::SubFromZeroSigned,
430 patterns::SubFromZeroUnsigned, patterns::SubOfSelf,
431 patterns::SubOfPadL, patterns::SubOfPadR>(context);
434OpFoldResult MulPrimOp::fold(FoldAdaptor adaptor) {
446 [=](
const APSInt &a,
const APSInt &b) { return a * b; });
449OpFoldResult DivPrimOp::fold(FoldAdaptor adaptor) {
456 if (getLhs() == getRhs()) {
457 auto width = getType().base().getWidthOrSentinel();
462 return getIntAttr(getType(), APInt(width, 1));
479 if (
auto rhsCst = dyn_cast_or_null<IntegerAttr>(adaptor.getRhs()))
480 if (rhsCst.getValue().isOne() && getLhs().getType() == getType())
485 [=](
const APSInt &a,
const APSInt &b) -> APInt {
488 return APInt(a.getBitWidth(), 0);
492OpFoldResult RemPrimOp::fold(FoldAdaptor adaptor) {
499 if (getLhs() == getRhs())
513 [=](
const APSInt &a,
const APSInt &b) -> APInt {
516 return APInt(a.getBitWidth(), 0);
520OpFoldResult DShlPrimOp::fold(FoldAdaptor adaptor) {
523 [=](
const APSInt &a,
const APSInt &b) -> APInt { return a.shl(b); });
526OpFoldResult DShlwPrimOp::fold(FoldAdaptor adaptor) {
529 [=](
const APSInt &a,
const APSInt &b) -> APInt { return a.shl(b); });
532OpFoldResult DShrPrimOp::fold(FoldAdaptor adaptor) {
535 [=](
const APSInt &a,
const APSInt &b) -> APInt {
536 return getType().base().isUnsigned() || !a.getBitWidth() ? a.lshr(b)
542OpFoldResult AndPrimOp::fold(FoldAdaptor adaptor) {
545 if (rhsCst->isZero())
549 if (rhsCst->isAllOnes() && getLhs().getType() == getType() &&
550 getRhs().getType() == getType())
556 if (lhsCst->isZero())
560 if (lhsCst->isAllOnes() && getLhs().getType() == getType() &&
561 getRhs().getType() == getType())
566 if (getLhs() == getRhs() && getRhs().getType() == getType())
571 [](
const APSInt &a,
const APSInt &b) -> APInt { return a & b; });
574void AndPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
575 MLIRContext *context) {
577 .insert<patterns::extendAnd, patterns::moveConstAnd, patterns::AndOfZero,
578 patterns::AndOfAllOne, patterns::AndOfSelf, patterns::AndOfPad,
579 patterns::AndOfAsSIntL, patterns::AndOfAsSIntR>(context);
582OpFoldResult OrPrimOp::fold(FoldAdaptor adaptor) {
585 if (rhsCst->isZero() && getLhs().getType() == getType())
589 if (rhsCst->isAllOnes() && getRhs().getType() == getType() &&
590 getLhs().getType() == getType())
596 if (lhsCst->isZero() && getRhs().getType() == getType())
600 if (lhsCst->isAllOnes() && getLhs().getType() == getType() &&
601 getRhs().getType() == getType())
606 if (getLhs() == getRhs() && getRhs().getType() == getType())
611 [](
const APSInt &a,
const APSInt &b) -> APInt { return a | b; });
614void OrPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
615 MLIRContext *context) {
616 results.insert<patterns::extendOr, patterns::moveConstOr, patterns::OrOfZero,
617 patterns::OrOfAllOne, patterns::OrOfSelf, patterns::OrOfPad,
618 patterns::OrOrr>(context);
621OpFoldResult XorPrimOp::fold(FoldAdaptor adaptor) {
624 if (rhsCst->isZero() &&
630 if (lhsCst->isZero() &&
635 if (getLhs() == getRhs())
638 APInt(std::max(getType().base().getWidthOrSentinel(), 0), 0));
642 [](
const APSInt &a,
const APSInt &b) -> APInt { return a ^ b; });
645void XorPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
646 MLIRContext *context) {
647 results.insert<patterns::extendXor, patterns::moveConstXor,
648 patterns::XorOfZero, patterns::XorOfSelf, patterns::XorOfPad>(
652void LEQPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
653 MLIRContext *context) {
654 results.insert<patterns::LEQWithConstLHS>(context);
657OpFoldResult LEQPrimOp::fold(FoldAdaptor adaptor) {
658 bool isUnsigned = getLhs().getType().base().isUnsigned();
661 if (getLhs() == getRhs())
665 if (
auto width = getLhs().getType().base().
getWidth()) {
667 auto commonWidth = std::max<int32_t>(*width, rhsCst->getBitWidth());
668 commonWidth = std::max(commonWidth, 1);
679 if (isUnsigned && rhsCst->zext(commonWidth)
692 [=](
const APSInt &a,
const APSInt &b) -> APInt {
693 return APInt(1, a <= b);
697void LTPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
698 MLIRContext *context) {
699 results.insert<patterns::LTWithConstLHS>(context);
702OpFoldResult LTPrimOp::fold(FoldAdaptor adaptor) {
703 IntType lhsType = getLhs().getType();
707 if (getLhs() == getRhs())
717 if (
auto width = lhsType.
getWidth()) {
719 auto commonWidth = std::max<int32_t>(*width, rhsCst->getBitWidth());
720 commonWidth = std::max(commonWidth, 1);
731 if (isUnsigned && rhsCst->zext(commonWidth)
744 [=](
const APSInt &a,
const APSInt &b) -> APInt {
745 return APInt(1, a < b);
749void GEQPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
750 MLIRContext *context) {
751 results.insert<patterns::GEQWithConstLHS>(context);
754OpFoldResult GEQPrimOp::fold(FoldAdaptor adaptor) {
755 IntType lhsType = getLhs().getType();
759 if (getLhs() == getRhs())
764 if (rhsCst->isZero() && isUnsigned)
769 if (
auto width = lhsType.
getWidth()) {
771 auto commonWidth = std::max<int32_t>(*width, rhsCst->getBitWidth());
772 commonWidth = std::max(commonWidth, 1);
775 if (isUnsigned && rhsCst->zext(commonWidth)
796 [=](
const APSInt &a,
const APSInt &b) -> APInt {
797 return APInt(1, a >= b);
801void GTPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
802 MLIRContext *context) {
803 results.insert<patterns::GTWithConstLHS>(context);
806OpFoldResult GTPrimOp::fold(FoldAdaptor adaptor) {
807 IntType lhsType = getLhs().getType();
811 if (getLhs() == getRhs())
815 if (
auto width = lhsType.
getWidth()) {
817 auto commonWidth = std::max<int32_t>(*width, rhsCst->getBitWidth());
818 commonWidth = std::max(commonWidth, 1);
821 if (isUnsigned && rhsCst->zext(commonWidth)
842 [=](
const APSInt &a,
const APSInt &b) -> APInt {
843 return APInt(1, a > b);
847OpFoldResult EQPrimOp::fold(FoldAdaptor adaptor) {
849 if (getLhs() == getRhs())
855 if (rhsCst->isAllOnes() && getLhs().getType() == getType() &&
856 getRhs().getType() == getType())
862 [=](
const APSInt &a,
const APSInt &b) -> APInt {
863 return APInt(1, a == b);
867LogicalResult EQPrimOp::canonicalize(EQPrimOp op, PatternRewriter &rewriter) {
869 op, rewriter, [&](ArrayRef<Attribute> operands) -> OpFoldResult {
871 auto width = op.getLhs().getType().getBitWidthOrSentinel();
874 if (rhsCst->isZero() && op.getLhs().getType() == op.getType() &&
875 op.getRhs().getType() == op.getType()) {
876 return NotPrimOp::create(rewriter, op.getLoc(), op.getLhs())
881 if (rhsCst->isZero() && width > 1) {
882 auto orrOp = OrRPrimOp::create(rewriter, op.getLoc(), op.getLhs());
883 return NotPrimOp::create(rewriter, op.getLoc(), orrOp).getResult();
887 if (rhsCst->isAllOnes() && width > 1 &&
888 op.getLhs().getType() == op.getRhs().getType()) {
889 return AndRPrimOp::create(rewriter, op.getLoc(), op.getLhs())
897OpFoldResult NEQPrimOp::fold(FoldAdaptor adaptor) {
899 if (getLhs() == getRhs())
905 if (rhsCst->isZero() && getLhs().getType() == getType() &&
906 getRhs().getType() == getType())
912 [=](
const APSInt &a,
const APSInt &b) -> APInt {
913 return APInt(1, a != b);
917LogicalResult NEQPrimOp::canonicalize(NEQPrimOp op, PatternRewriter &rewriter) {
919 op, rewriter, [&](ArrayRef<Attribute> operands) -> OpFoldResult {
921 auto width = op.getLhs().getType().getBitWidthOrSentinel();
924 if (rhsCst->isAllOnes() && op.getLhs().getType() == op.getType() &&
925 op.getRhs().getType() == op.getType()) {
926 return NotPrimOp::create(rewriter, op.getLoc(), op.getLhs())
931 if (rhsCst->isZero() && width > 1) {
932 return OrRPrimOp::create(rewriter, op.getLoc(), op.getLhs())
937 if (rhsCst->isAllOnes() && width > 1 &&
938 op.getLhs().getType() == op.getRhs().getType()) {
940 AndRPrimOp::create(rewriter, op.getLoc(), op.getLhs());
941 return NotPrimOp::create(rewriter, op.getLoc(), andrOp).getResult();
949OpFoldResult IntegerAddOp::fold(FoldAdaptor adaptor) {
955OpFoldResult IntegerMulOp::fold(FoldAdaptor adaptor) {
961OpFoldResult IntegerShrOp::fold(FoldAdaptor adaptor) {
967OpFoldResult IntegerShlOp::fold(FoldAdaptor adaptor) {
972 return IntegerAttr::get(
973 IntegerType::get(getContext(), lhsCst->getBitWidth()),
974 lhsCst->shl(*rhsCst));
977 if (rhsCst->isZero())
988OpFoldResult SizeOfIntrinsicOp::fold(FoldAdaptor) {
989 auto base = getInput().getType();
996OpFoldResult IsXIntrinsicOp::fold(FoldAdaptor adaptor) {
1003OpFoldResult AsSIntPrimOp::fold(FoldAdaptor adaptor) {
1011 if (getType().base().hasWidth())
1018void AsSIntPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1019 MLIRContext *context) {
1020 results.insert<patterns::StoUtoS>(context);
1023OpFoldResult AsUIntPrimOp::fold(FoldAdaptor adaptor) {
1031 if (getType().base().hasWidth())
1038void AsUIntPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1039 MLIRContext *context) {
1040 results.insert<patterns::UtoStoU>(context);
1043OpFoldResult AsAsyncResetPrimOp::fold(FoldAdaptor adaptor) {
1045 if (getInput().getType() == getType())
1050 return BoolAttr::get(getContext(), cst->getBoolValue());
1055OpFoldResult AsClockPrimOp::fold(FoldAdaptor adaptor) {
1057 if (getInput().getType() == getType())
1062 return BoolAttr::get(getContext(), cst->getBoolValue());
1067OpFoldResult CvtPrimOp::fold(FoldAdaptor adaptor) {
1073 getType().base().getWidthOrSentinel()))
1079void CvtPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1080 MLIRContext *context) {
1081 results.insert<patterns::CVTSigned, patterns::CVTUnSigned>(context);
1084OpFoldResult NegPrimOp::fold(FoldAdaptor adaptor) {
1091 getType().base().getWidthOrSentinel()))
1092 return getIntAttr(getType(), APInt((*cst).getBitWidth(), 0) - *cst);
1097OpFoldResult NotPrimOp::fold(FoldAdaptor adaptor) {
1102 getType().base().getWidthOrSentinel()))
1108void NotPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1109 MLIRContext *context) {
1110 results.insert<patterns::NotNot, patterns::NotEq, patterns::NotNeq,
1111 patterns::NotLeq, patterns::NotLt, patterns::NotGeq,
1112 patterns::NotGt>(context);
1118 : RewritePattern(opName, 0, context) {}
1124 ConstantOp constantOp,
1125 SmallVectorImpl<Value> &remaining)
const = 0;
1132 mlir::PatternRewriter &rewriter)
const override {
1134 auto catOp = op->getOperand(0).getDefiningOp<CatPrimOp>();
1138 SmallVector<Value> nonConstantOperands;
1141 for (
auto operand : catOp.getInputs()) {
1142 if (
auto constantOp = operand.getDefiningOp<ConstantOp>()) {
1144 if (
handleConstant(rewriter, op, constantOp, nonConstantOperands))
1148 nonConstantOperands.push_back(operand);
1153 if (nonConstantOperands.empty()) {
1154 replaceOpWithNewOpAndCopyName<ConstantOp>(
1155 rewriter, op, cast<IntType>(op->getResult(0).getType()),
1161 if (nonConstantOperands.size() == 1) {
1162 rewriter.modifyOpInPlace(
1163 op, [&] { op->setOperand(0, nonConstantOperands.front()); });
1168 if (catOp->hasOneUse() &&
1169 nonConstantOperands.size() < catOp->getNumOperands()) {
1170 replaceOpWithNewOpAndCopyName<CatPrimOp>(rewriter, catOp,
1171 nonConstantOperands);
1181 :
ReductionCat(context, OrRPrimOp::getOperationName()) {}
1184 SmallVectorImpl<Value> &remaining)
const override {
1185 if (value.getValue().isZero())
1188 replaceOpWithNewOpAndCopyName<ConstantOp>(
1189 rewriter, op, cast<IntType>(op->getResult(0).getType()),
1199 :
ReductionCat(context, AndRPrimOp::getOperationName()) {}
1202 SmallVectorImpl<Value> &remaining)
const override {
1203 if (value.getValue().isAllOnes())
1206 replaceOpWithNewOpAndCopyName<ConstantOp>(
1207 rewriter, op, cast<IntType>(op->getResult(0).getType()),
1217 :
ReductionCat(context, XorRPrimOp::getOperationName()) {}
1220 SmallVectorImpl<Value> &remaining)
const override {
1221 if (value.getValue().isZero())
1223 remaining.push_back(value);
1229OpFoldResult AndRPrimOp::fold(FoldAdaptor adaptor) {
1233 if (getInput().getType().getBitWidthOrSentinel() == 0)
1238 return getIntAttr(getType(), APInt(1, cst->isAllOnes()));
1242 if (
isUInt1(getInput().getType()))
1248void AndRPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1249 MLIRContext *context) {
1250 results.insert<patterns::AndRasSInt, patterns::AndRasUInt, patterns::AndRPadU,
1251 patterns::AndRPadS, patterns::AndRCatAndR_left,
1252 patterns::AndRCatAndR_right,
AndRCat>(context);
1255OpFoldResult OrRPrimOp::fold(FoldAdaptor adaptor) {
1259 if (getInput().getType().getBitWidthOrSentinel() == 0)
1264 return getIntAttr(getType(), APInt(1, !cst->isZero()));
1268 if (
isUInt1(getInput().getType()))
1274void OrRPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1275 MLIRContext *context) {
1276 results.insert<patterns::OrRasSInt, patterns::OrRasUInt, patterns::OrRPadU,
1277 patterns::OrRCatOrR_left, patterns::OrRCatOrR_right,
OrRCat>(
1281OpFoldResult XorRPrimOp::fold(FoldAdaptor adaptor) {
1285 if (getInput().getType().getBitWidthOrSentinel() == 0)
1290 return getIntAttr(getType(), APInt(1, cst->popcount() & 1));
1293 if (
isUInt1(getInput().getType()))
1299void XorRPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1300 MLIRContext *context) {
1302 .insert<patterns::XorRasSInt, patterns::XorRasUInt, patterns::XorRPadU,
1303 patterns::XorRCatXorR_left, patterns::XorRCatXorR_right,
XorRCat>(
1311OpFoldResult CatPrimOp::fold(FoldAdaptor adaptor) {
1312 auto inputs = getInputs();
1313 auto inputAdaptors = adaptor.getInputs();
1320 if (inputs.size() == 1 && inputs[0].getType() == getType())
1328 SmallVector<Value> nonZeroInputs;
1329 SmallVector<Attribute> nonZeroAttributes;
1330 bool allConstant =
true;
1331 for (
auto [input, attr] :
llvm::zip(inputs, inputAdaptors)) {
1332 auto inputType = type_cast<IntType>(input.getType());
1333 if (inputType.getBitWidthOrSentinel() != 0) {
1334 nonZeroInputs.push_back(input);
1336 allConstant =
false;
1337 if (nonZeroInputs.size() > 1 && !allConstant)
1343 if (nonZeroInputs.empty())
1347 if (nonZeroInputs.size() == 1 && nonZeroInputs[0].getType() == getType())
1348 return nonZeroInputs[0];
1354 SmallVector<APInt> constants;
1355 for (
auto inputAdaptor : inputAdaptors) {
1357 constants.push_back(*cst);
1362 assert(!constants.empty());
1364 APInt result = constants[0];
1365 for (
size_t i = 1; i < constants.size(); ++i)
1366 result = result.concat(constants[i]);
1371void DShlPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1372 MLIRContext *context) {
1373 results.insert<patterns::DShlOfConstant>(context);
1376void DShrPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1377 MLIRContext *context) {
1378 results.insert<patterns::DShrOfConstant>(context);
1384class FlattenCat :
public mlir::RewritePattern {
1386 FlattenCat(MLIRContext *context)
1387 : RewritePattern(CatPrimOp::getOperationName(), 0, context) {}
1390 matchAndRewrite(Operation *op,
1391 mlir::PatternRewriter &rewriter)
const override {
1392 auto cat = cast<CatPrimOp>(op);
1394 cat.getType().getBitWidthOrSentinel() == 0)
1398 if (cat->hasOneUse() && isa<CatPrimOp>(*cat->getUsers().begin()))
1402 SmallVector<Value> operands;
1403 SmallVector<Value> worklist;
1404 auto pushOperands = [&worklist](CatPrimOp op) {
1405 for (
auto operand :
llvm::reverse(op.getInputs()))
1406 worklist.push_back(operand);
1409 bool hasSigned =
false, hasUnsigned =
false;
1410 while (!worklist.empty()) {
1411 auto value = worklist.pop_back_val();
1412 auto catOp = value.getDefiningOp<CatPrimOp>();
1414 operands.push_back(value);
1415 (type_isa<UIntType>(value.getType()) ? hasUnsigned : hasSigned) =
true;
1419 pushOperands(catOp);
1424 auto castToUIntIfSigned = [&](Value value) -> Value {
1425 if (type_isa<UIntType>(value.getType()))
1427 return AsUIntPrimOp::create(rewriter, value.getLoc(), value);
1430 assert(operands.size() >= 1 &&
"zero width cast must be rejected");
1432 if (operands.size() == 1) {
1433 rewriter.replaceOp(op, castToUIntIfSigned(operands[0]));
1437 if (operands.size() == cat->getNumOperands())
1441 if (hasSigned && hasUnsigned)
1442 for (
auto &operand : operands)
1443 operand = castToUIntIfSigned(operand);
1445 replaceOpWithNewOpAndCopyName<CatPrimOp>(rewriter, op, cat.getType(),
1452class CatOfConstant :
public mlir::RewritePattern {
1454 CatOfConstant(MLIRContext *context)
1455 : RewritePattern(CatPrimOp::getOperationName(), 0, context) {}
1458 matchAndRewrite(Operation *op,
1459 mlir::PatternRewriter &rewriter)
const override {
1460 auto cat = cast<CatPrimOp>(op);
1464 SmallVector<Value> operands;
1466 for (
size_t i = 0; i < cat->getNumOperands(); ++i) {
1467 auto cst = cat.getInputs()[i].getDefiningOp<ConstantOp>();
1469 operands.push_back(cat.getInputs()[i]);
1472 APSInt value = cst.getValue();
1474 for (; j < cat->getNumOperands(); ++j) {
1475 auto nextCst = cat.getInputs()[j].getDefiningOp<ConstantOp>();
1478 value = value.concat(nextCst.getValue());
1483 operands.push_back(cst);
1486 operands.push_back(ConstantOp::create(rewriter, cat.getLoc(), value));
1492 if (operands.size() == cat->getNumOperands())
1495 replaceOpWithNewOpAndCopyName<CatPrimOp>(rewriter, op, cat.getType(),
1504void CatPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1505 MLIRContext *context) {
1506 results.insert<patterns::CatBitsBits, patterns::CatDoubleConst,
1507 patterns::CatCast, FlattenCat, CatOfConstant>(context);
1510OpFoldResult BitCastOp::fold(FoldAdaptor adaptor) {
1513 if (op.getType() == op.getInput().getType())
1514 return op.getInput();
1518 if (BitCastOp in = dyn_cast_or_null<BitCastOp>(op.getInput().getDefiningOp()))
1519 if (op.getType() == in.getInput().getType())
1520 return in.getInput();
1525OpFoldResult BitsPrimOp::fold(FoldAdaptor adaptor) {
1526 IntType inputType = getInput().getType();
1527 IntType resultType = getType();
1529 if (inputType == getType() && resultType.
hasWidth())
1536 cst->extractBits(getHi() - getLo() + 1, getLo()));
1543 : RewritePattern(BitsPrimOp::getOperationName(), 0, context) {}
1547 mlir::PatternRewriter &rewriter)
const override {
1548 auto bits = cast<BitsPrimOp>(op);
1549 auto cat = bits.getInput().getDefiningOp<CatPrimOp>();
1552 int32_t bitPos = bits.getLo();
1553 auto resultWidth = type_cast<UIntType>(bits.getType()).getWidthOrSentinel();
1554 if (resultWidth < 0)
1556 for (
auto operand : llvm::reverse(cat.getInputs())) {
1558 type_cast<IntType>(operand.getType()).getWidthOrSentinel();
1559 if (operandWidth < 0)
1561 if (bitPos < operandWidth) {
1562 if (bitPos + resultWidth <= operandWidth) {
1563 auto newBits = rewriter.createOrFold<BitsPrimOp>(
1564 op->getLoc(), operand, bitPos + resultWidth - 1, bitPos);
1570 bitPos -= operandWidth;
1576void BitsPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1577 MLIRContext *context) {
1579 .insert<patterns::BitsOfBits, patterns::BitsOfMux, patterns::BitsOfAsUInt,
1580 patterns::BitsOfAnd, patterns::BitsOfPad,
BitsOfCat>(context);
1587 unsigned loBit, PatternRewriter &rewriter) {
1588 auto resType = type_cast<IntType>(op->getResult(0).getType());
1589 if (type_cast<IntType>(value.getType()).getWidth() != resType.getWidth())
1590 value = BitsPrimOp::create(rewriter, op->getLoc(), value, hiBit, loBit);
1592 if (resType.isSigned() && !type_cast<IntType>(value.getType()).isSigned()) {
1593 value = rewriter.createOrFold<AsSIntPrimOp>(op->getLoc(), resType, value);
1594 }
else if (resType.isUnsigned() &&
1595 !type_cast<IntType>(value.getType()).isUnsigned()) {
1596 value = rewriter.createOrFold<AsUIntPrimOp>(op->getLoc(), resType, value);
1598 rewriter.replaceOp(op, value);
1601template <
typename OpTy>
1602static OpFoldResult
foldMux(OpTy op,
typename OpTy::FoldAdaptor adaptor) {
1604 if (op.getType().getBitWidthOrSentinel() == 0)
1606 APInt(0, 0, op.getType().isSignedInteger()));
1609 if (op.getHigh() == op.getLow() && op.getHigh().getType() == op.getType())
1610 return op.getHigh();
1615 if (op.getType().getBitWidthOrSentinel() < 0)
1620 if (cond->isZero() && op.getLow().getType() == op.getType())
1622 if (!cond->isZero() && op.getHigh().getType() == op.getType())
1623 return op.getHigh();
1627 if (
auto lowCst =
getConstant(adaptor.getLow())) {
1629 if (
auto highCst =
getConstant(adaptor.getHigh())) {
1631 if (highCst->getBitWidth() == lowCst->getBitWidth() &&
1632 *highCst == *lowCst)
1635 if (highCst->isOne() && lowCst->isZero() &&
1636 op.getType() == op.getSel().getType())
1649OpFoldResult MuxPrimOp::fold(FoldAdaptor adaptor) {
1650 return foldMux(*
this, adaptor);
1653OpFoldResult Mux2CellIntrinsicOp::fold(FoldAdaptor adaptor) {
1654 return foldMux(*
this, adaptor);
1657OpFoldResult Mux4CellIntrinsicOp::fold(FoldAdaptor adaptor) {
return {}; }
1664class MuxPad :
public mlir::RewritePattern {
1666 MuxPad(MLIRContext *context)
1667 : RewritePattern(MuxPrimOp::getOperationName(), 0, context) {}
1670 matchAndRewrite(Operation *op,
1671 mlir::PatternRewriter &rewriter)
const override {
1672 auto mux = cast<MuxPrimOp>(op);
1673 auto width = mux.getType().getBitWidthOrSentinel();
1677 auto pad = [&](Value input) -> Value {
1679 type_cast<FIRRTLBaseType>(input.getType()).getBitWidthOrSentinel();
1680 if (inputWidth < 0 || width == inputWidth)
1682 return PadPrimOp::create(rewriter, mux.getLoc(), mux.getType(), input,
1687 auto newHigh = pad(mux.getHigh());
1688 auto newLow = pad(mux.getLow());
1689 if (newHigh == mux.getHigh() && newLow == mux.getLow())
1692 replaceOpWithNewOpAndCopyName<MuxPrimOp>(
1693 rewriter, op, mux.getType(), ValueRange{mux.getSel(), newHigh, newLow},
1701class MuxSharedCond :
public mlir::RewritePattern {
1703 MuxSharedCond(MLIRContext *context)
1704 : RewritePattern(MuxPrimOp::getOperationName(), 0, context) {}
1706 static const int depthLimit = 5;
1708 Value updateOrClone(MuxPrimOp mux, Value high, Value low,
1709 mlir::PatternRewriter &rewriter,
1710 bool updateInPlace)
const {
1711 if (updateInPlace) {
1712 rewriter.modifyOpInPlace(mux, [&] {
1713 mux.setOperand(1, high);
1714 mux.setOperand(2, low);
1718 rewriter.setInsertionPointAfter(mux);
1719 return MuxPrimOp::create(rewriter, mux.getLoc(), mux.getType(),
1720 ValueRange{mux.getSel(), high, low})
1725 Value tryCondTrue(Value op, Value cond, mlir::PatternRewriter &rewriter,
1726 bool updateInPlace,
int limit)
const {
1727 MuxPrimOp mux = op.getDefiningOp<MuxPrimOp>();
1730 if (mux.getSel() == cond)
1731 return mux.getHigh();
1732 if (limit > depthLimit)
1734 updateInPlace &= mux->hasOneUse();
1736 if (Value v = tryCondTrue(mux.getHigh(), cond, rewriter, updateInPlace,
1738 return updateOrClone(mux, v, mux.getLow(), rewriter, updateInPlace);
1741 tryCondTrue(mux.getLow(), cond, rewriter, updateInPlace, limit + 1))
1742 return updateOrClone(mux, mux.getHigh(), v, rewriter, updateInPlace);
1747 Value tryCondFalse(Value op, Value cond, mlir::PatternRewriter &rewriter,
1748 bool updateInPlace,
int limit)
const {
1749 MuxPrimOp mux = op.getDefiningOp<MuxPrimOp>();
1752 if (mux.getSel() == cond)
1753 return mux.getLow();
1754 if (limit > depthLimit)
1756 updateInPlace &= mux->hasOneUse();
1758 if (Value v = tryCondFalse(mux.getHigh(), cond, rewriter, updateInPlace,
1760 return updateOrClone(mux, v, mux.getLow(), rewriter, updateInPlace);
1762 if (Value v = tryCondFalse(mux.getLow(), cond, rewriter, updateInPlace,
1764 return updateOrClone(mux, mux.getHigh(), v, rewriter, updateInPlace);
1770 matchAndRewrite(Operation *op,
1771 mlir::PatternRewriter &rewriter)
const override {
1772 auto mux = cast<MuxPrimOp>(op);
1773 auto width = mux.getType().getBitWidthOrSentinel();
1777 if (Value v = tryCondTrue(mux.getHigh(), mux.getSel(), rewriter,
true, 0)) {
1778 rewriter.modifyOpInPlace(mux, [&] { mux.setOperand(1, v); });
1782 if (Value v = tryCondFalse(mux.getLow(), mux.getSel(), rewriter,
true, 0)) {
1783 rewriter.modifyOpInPlace(mux, [&] { mux.setOperand(2, v); });
1792void MuxPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1793 MLIRContext *context) {
1795 .add<MuxPad, MuxSharedCond, patterns::MuxEQOperands,
1796 patterns::MuxEQOperandsSwapped, patterns::MuxNEQ, patterns::MuxNot,
1797 patterns::MuxSameTrue, patterns::MuxSameFalse,
1798 patterns::NarrowMuxLHS, patterns::NarrowMuxRHS, patterns::MuxPadSel>(
1802void Mux2CellIntrinsicOp::getCanonicalizationPatterns(
1803 RewritePatternSet &results, MLIRContext *context) {
1804 results.add<patterns::Mux2PadSel>(context);
1807void Mux4CellIntrinsicOp::getCanonicalizationPatterns(
1808 RewritePatternSet &results, MLIRContext *context) {
1809 results.add<patterns::Mux4PadSel>(context);
1812OpFoldResult PadPrimOp::fold(FoldAdaptor adaptor) {
1813 auto input = this->getInput();
1816 if (input.getType() == getType())
1820 auto inputType = input.getType().base();
1827 auto destWidth = getType().base().getWidthOrSentinel();
1828 if (destWidth == -1)
1831 if (inputType.
isSigned() && cst->getBitWidth())
1832 return getIntAttr(getType(), cst->sext(destWidth));
1833 return getIntAttr(getType(), cst->zext(destWidth));
1839OpFoldResult ShlPrimOp::fold(FoldAdaptor adaptor) {
1840 auto input = this->getInput();
1841 IntType inputType = input.getType();
1842 int shiftAmount = getAmount();
1845 if (shiftAmount == 0)
1851 if (inputWidth != -1) {
1852 auto resultWidth = inputWidth + shiftAmount;
1853 shiftAmount = std::min(shiftAmount, resultWidth);
1854 return getIntAttr(getType(), cst->zext(resultWidth).shl(shiftAmount));
1860OpFoldResult ShrPrimOp::fold(FoldAdaptor adaptor) {
1861 auto input = this->getInput();
1862 IntType inputType = input.getType();
1863 int shiftAmount = getAmount();
1869 if (shiftAmount == 0 && inputWidth > 0)
1872 if (inputWidth == -1)
1874 if (inputWidth == 0)
1879 if (shiftAmount >= inputWidth && inputType.
isUnsigned())
1880 return getIntAttr(getType(), APInt(0, 0,
false));
1886 value = cst->ashr(std::min(shiftAmount, inputWidth - 1));
1888 value = cst->lshr(std::min(shiftAmount, inputWidth));
1889 auto resultWidth = std::max(inputWidth - shiftAmount, 1);
1890 return getIntAttr(getType(), value.trunc(resultWidth));
1895LogicalResult ShrPrimOp::canonicalize(ShrPrimOp op, PatternRewriter &rewriter) {
1896 auto inputWidth = op.getInput().getType().base().getWidthOrSentinel();
1897 if (inputWidth <= 0)
1901 unsigned shiftAmount = op.getAmount();
1902 if (
int(shiftAmount) >= inputWidth) {
1904 if (op.getType().base().isUnsigned())
1910 shiftAmount = inputWidth - 1;
1913 replaceWithBits(op, op.getInput(), inputWidth - 1, shiftAmount, rewriter);
1917LogicalResult HeadPrimOp::canonicalize(HeadPrimOp op,
1918 PatternRewriter &rewriter) {
1919 auto inputWidth = op.getInput().getType().base().getWidthOrSentinel();
1920 if (inputWidth <= 0)
1924 unsigned keepAmount = op.getAmount();
1926 replaceWithBits(op, op.getInput(), inputWidth - 1, inputWidth - keepAmount,
1931OpFoldResult HeadPrimOp::fold(FoldAdaptor adaptor) {
1935 getInput().getType().base().getWidthOrSentinel() - getAmount();
1936 return getIntAttr(getType(), cst->lshr(shiftAmount).trunc(getAmount()));
1942OpFoldResult TailPrimOp::fold(FoldAdaptor adaptor) {
1946 cst->trunc(getType().base().getWidthOrSentinel()));
1950LogicalResult TailPrimOp::canonicalize(TailPrimOp op,
1951 PatternRewriter &rewriter) {
1952 auto inputWidth = op.getInput().getType().base().getWidthOrSentinel();
1953 if (inputWidth <= 0)
1957 unsigned dropAmount = op.getAmount();
1958 if (dropAmount !=
unsigned(inputWidth))
1964void SubaccessOp::getCanonicalizationPatterns(RewritePatternSet &results,
1965 MLIRContext *context) {
1966 results.add<patterns::SubaccessOfConstant>(context);
1969OpFoldResult MultibitMuxOp::fold(FoldAdaptor adaptor) {
1971 if (adaptor.getInputs().size() == 1)
1972 return getOperand(1);
1974 if (
auto constIndex =
getConstant(adaptor.getIndex())) {
1975 auto index = constIndex->getZExtValue();
1976 if (index < getInputs().size())
1977 return getInputs()[getInputs().size() - 1 - index];
1983LogicalResult MultibitMuxOp::canonicalize(MultibitMuxOp op,
1984 PatternRewriter &rewriter) {
1988 if (llvm::all_of(op.getInputs().drop_front(), [&](
auto input) {
1989 return input == op.getInputs().front();
1997 auto indexWidth = op.getIndex().getType().getBitWidthOrSentinel();
1998 uint64_t inputSize = op.getInputs().size();
1999 if (indexWidth >= 0 && indexWidth < 64 && 1ull << indexWidth < inputSize) {
2000 rewriter.modifyOpInPlace(op, [&]() {
2001 op.getInputsMutable().erase(0, inputSize - (1ull << indexWidth));
2008 if (
auto lastSubindex = op.getInputs().back().getDefiningOp<SubindexOp>()) {
2009 if (llvm::all_of(llvm::enumerate(op.getInputs()), [&](
auto e) {
2010 auto subindex = e.value().template getDefiningOp<SubindexOp>();
2011 return subindex && lastSubindex.getInput() == subindex.getInput() &&
2012 subindex.getIndex() + e.index() + 1 == op.getInputs().size();
2014 replaceOpWithNewOpAndCopyName<SubaccessOp>(
2015 rewriter, op, lastSubindex.getInput(), op.getIndex());
2021 if (op.getInputs().size() != 2)
2025 auto uintType = op.getIndex().getType();
2026 if (uintType.getBitWidthOrSentinel() != 1)
2030 replaceOpWithNewOpAndCopyName<MuxPrimOp>(
2031 rewriter, op, op.getIndex(), op.getInputs()[0], op.getInputs()[1]);
2050 MatchingConnectOp connect;
2051 for (Operation *user : value.getUsers()) {
2053 if (isa<AttachOp, SubfieldOp, SubaccessOp, SubindexOp>(user))
2056 if (
auto aConnect = dyn_cast<FConnectLike>(user))
2057 if (aConnect.getDest() == value) {
2058 auto matchingConnect = dyn_cast<MatchingConnectOp>(*aConnect);
2061 if (!matchingConnect || (connect && connect != matchingConnect) ||
2062 matchingConnect->getBlock() != value.getParentBlock())
2064 connect = matchingConnect;
2072 PatternRewriter &rewriter) {
2075 Operation *connectedDecl = op.getDest().getDefiningOp();
2080 if (!isa<WireOp>(connectedDecl) && !isa<RegOp>(connectedDecl))
2084 cast<Forceable>(connectedDecl).isForceable())
2092 if (connectedDecl->hasOneUse())
2096 auto *declBlock = connectedDecl->getBlock();
2097 auto *srcValueOp = op.getSrc().getDefiningOp();
2100 if (!isa<WireOp>(connectedDecl))
2106 if (!isa<ConstantOp>(srcValueOp))
2108 if (srcValueOp->getBlock() != declBlock)
2114 auto replacement = op.getSrc();
2117 if (srcValueOp && srcValueOp != &declBlock->front())
2118 srcValueOp->moveBefore(&declBlock->front());
2125 rewriter.eraseOp(op);
2129void ConnectOp::getCanonicalizationPatterns(RewritePatternSet &results,
2130 MLIRContext *context) {
2131 results.insert<patterns::ConnectExtension, patterns::ConnectSameType>(
2135LogicalResult MatchingConnectOp::canonicalize(MatchingConnectOp op,
2136 PatternRewriter &rewriter) {
2153 for (
auto *user : value.getUsers()) {
2154 auto attach = dyn_cast<AttachOp>(user);
2155 if (!attach || attach == dominatedAttach)
2157 if (attach->isBeforeInBlock(dominatedAttach))
2163LogicalResult AttachOp::canonicalize(AttachOp op, PatternRewriter &rewriter) {
2165 if (op.getNumOperands() <= 1) {
2166 rewriter.eraseOp(op);
2170 for (
auto operand : op.getOperands()) {
2177 SmallVector<Value> newOperands(op.getOperands());
2178 for (
auto newOperand : attach.getOperands())
2179 if (newOperand != operand)
2180 newOperands.push_back(newOperand);
2181 AttachOp::create(rewriter, op->getLoc(), newOperands);
2182 rewriter.eraseOp(attach);
2183 rewriter.eraseOp(op);
2191 if (
auto wire = dyn_cast_or_null<WireOp>(operand.getDefiningOp())) {
2192 if (!
hasDontTouch(wire.getOperation()) && wire->hasOneUse() &&
2193 !wire.isForceable()) {
2194 SmallVector<Value> newOperands;
2195 for (
auto newOperand : op.getOperands())
2196 if (newOperand != operand)
2197 newOperands.push_back(newOperand);
2199 AttachOp::create(rewriter, op->getLoc(), newOperands);
2200 rewriter.eraseOp(op);
2201 rewriter.eraseOp(wire);
2212 assert(llvm::hasSingleElement(region) &&
"expected single-region block");
2213 rewriter.inlineBlockBefore(®ion.front(), op, {});
2216LogicalResult WhenOp::canonicalize(WhenOp op, PatternRewriter &rewriter) {
2217 if (
auto constant = op.getCondition().getDefiningOp<firrtl::ConstantOp>()) {
2218 if (constant.getValue().isAllOnes())
2220 else if (op.hasElseRegion() && !op.getElseRegion().empty())
2223 rewriter.eraseOp(op);
2229 if (!op.getThenBlock().empty() && op.hasElseRegion() &&
2230 op.getElseBlock().empty()) {
2231 rewriter.eraseBlock(&op.getElseBlock());
2238 if (!op.getThenBlock().empty())
2242 if (!op.hasElseRegion() || op.getElseBlock().empty()) {
2243 rewriter.eraseOp(op);
2252struct FoldNodeName :
public mlir::RewritePattern {
2253 FoldNodeName(MLIRContext *context)
2254 : RewritePattern(NodeOp::getOperationName(), 0, context) {}
2255 LogicalResult matchAndRewrite(Operation *op,
2256 PatternRewriter &rewriter)
const override {
2257 auto node = cast<NodeOp>(op);
2258 auto name = node.getNameAttr();
2259 if (!node.hasDroppableName() || node.getInnerSym() ||
2262 auto *newOp = node.getInput().getDefiningOp();
2265 rewriter.replaceOp(node, node.getInput());
2271struct NodeBypass :
public mlir::RewritePattern {
2272 NodeBypass(MLIRContext *context)
2273 : RewritePattern(NodeOp::getOperationName(), 0, context) {}
2274 LogicalResult matchAndRewrite(Operation *op,
2275 PatternRewriter &rewriter)
const override {
2276 auto node = cast<NodeOp>(op);
2278 node.use_empty() || node.isForceable())
2280 rewriter.replaceAllUsesWith(node.getResult(), node.getInput());
2287template <
typename OpTy>
2289 PatternRewriter &rewriter) {
2290 if (!op.isForceable() || !op.getDataRef().use_empty())
2298LogicalResult NodeOp::fold(FoldAdaptor adaptor,
2299 SmallVectorImpl<OpFoldResult> &results) {
2308 if (!adaptor.getInput())
2311 results.push_back(adaptor.getInput());
2315void NodeOp::getCanonicalizationPatterns(RewritePatternSet &results,
2316 MLIRContext *context) {
2317 results.insert<FoldNodeName>(context);
2318 results.add(demoteForceableIfUnused<NodeOp>);
2324struct AggOneShot :
public mlir::RewritePattern {
2325 AggOneShot(StringRef name, uint32_t weight, MLIRContext *context)
2326 : RewritePattern(name, 0, context) {}
2328 SmallVector<Value> getCompleteWrite(Operation *lhs)
const {
2329 auto lhsTy = lhs->getResult(0).getType();
2330 if (!type_isa<BundleType, FVectorType>(lhsTy))
2333 DenseMap<uint32_t, Value> fields;
2334 for (Operation *user : lhs->getResult(0).getUsers()) {
2335 if (user->getParentOp() != lhs->getParentOp())
2337 if (
auto aConnect = dyn_cast<MatchingConnectOp>(user)) {
2338 if (aConnect.getDest() == lhs->getResult(0))
2340 }
else if (
auto subField = dyn_cast<SubfieldOp>(user)) {
2341 for (Operation *subuser : subField.getResult().getUsers()) {
2342 if (
auto aConnect = dyn_cast<MatchingConnectOp>(subuser)) {
2343 if (aConnect.getDest() == subField) {
2344 if (subuser->getParentOp() != lhs->getParentOp())
2346 if (fields.count(subField.getFieldIndex()))
2348 fields[subField.getFieldIndex()] = aConnect.getSrc();
2354 }
else if (
auto subIndex = dyn_cast<SubindexOp>(user)) {
2355 for (Operation *subuser : subIndex.getResult().getUsers()) {
2356 if (
auto aConnect = dyn_cast<MatchingConnectOp>(subuser)) {
2357 if (aConnect.getDest() == subIndex) {
2358 if (subuser->getParentOp() != lhs->getParentOp())
2360 if (fields.count(subIndex.getIndex()))
2362 fields[subIndex.getIndex()] = aConnect.getSrc();
2373 SmallVector<Value> values;
2374 uint32_t total = type_isa<BundleType>(lhsTy)
2375 ? type_cast<BundleType>(lhsTy).getNumElements()
2376 : type_cast<FVectorType>(lhsTy).getNumElements();
2377 for (uint32_t i = 0; i < total; ++i) {
2378 if (!fields.count(i))
2380 values.push_back(fields[i]);
2385 LogicalResult matchAndRewrite(Operation *op,
2386 PatternRewriter &rewriter)
const override {
2387 auto values = getCompleteWrite(op);
2390 rewriter.setInsertionPointToEnd(op->getBlock());
2391 auto dest = op->getResult(0);
2392 auto destType = dest.getType();
2395 if (!type_cast<FIRRTLBaseType>(destType).isPassive())
2398 Value newVal = type_isa<BundleType>(destType)
2399 ? rewriter.createOrFold<BundleCreateOp>(op->getLoc(),
2401 : rewriter.createOrFold<VectorCreateOp>(
2402 op->
getLoc(), destType, values);
2403 rewriter.createOrFold<MatchingConnectOp>(op->getLoc(), dest, newVal);
2404 for (Operation *user : dest.getUsers()) {
2405 if (
auto subIndex = dyn_cast<SubindexOp>(user)) {
2406 for (Operation *subuser :
2407 llvm::make_early_inc_range(subIndex.getResult().getUsers()))
2408 if (auto aConnect = dyn_cast<MatchingConnectOp>(subuser))
2409 if (aConnect.getDest() == subIndex)
2410 rewriter.eraseOp(aConnect);
2411 }
else if (
auto subField = dyn_cast<SubfieldOp>(user)) {
2412 for (Operation *subuser :
2413 llvm::make_early_inc_range(subField.getResult().getUsers()))
2414 if (auto aConnect = dyn_cast<MatchingConnectOp>(subuser))
2415 if (aConnect.getDest() == subField)
2416 rewriter.eraseOp(aConnect);
2423struct WireAggOneShot :
public AggOneShot {
2424 WireAggOneShot(MLIRContext *context)
2425 : AggOneShot(WireOp::getOperationName(), 0, context) {}
2427struct SubindexAggOneShot :
public AggOneShot {
2428 SubindexAggOneShot(MLIRContext *context)
2429 : AggOneShot(SubindexOp::getOperationName(), 0, context) {}
2431struct SubfieldAggOneShot :
public AggOneShot {
2432 SubfieldAggOneShot(MLIRContext *context)
2433 : AggOneShot(SubfieldOp::getOperationName(), 0, context) {}
2437void WireOp::getCanonicalizationPatterns(RewritePatternSet &results,
2438 MLIRContext *context) {
2439 results.insert<WireAggOneShot>(context);
2440 results.add(demoteForceableIfUnused<WireOp>);
2443void SubindexOp::getCanonicalizationPatterns(RewritePatternSet &results,
2444 MLIRContext *context) {
2445 results.insert<SubindexAggOneShot>(context);
2448OpFoldResult SubindexOp::fold(FoldAdaptor adaptor) {
2449 auto attr = dyn_cast_or_null<ArrayAttr>(adaptor.getInput());
2452 return attr[getIndex()];
2455OpFoldResult SubfieldOp::fold(FoldAdaptor adaptor) {
2456 auto attr = dyn_cast_or_null<ArrayAttr>(adaptor.getInput());
2459 auto index = getFieldIndex();
2463void SubfieldOp::getCanonicalizationPatterns(RewritePatternSet &results,
2464 MLIRContext *context) {
2465 results.insert<SubfieldAggOneShot>(context);
2469 ArrayRef<Attribute> operands) {
2470 for (
auto operand : operands)
2473 return ArrayAttr::get(context, operands);
2476OpFoldResult BundleCreateOp::fold(FoldAdaptor adaptor) {
2479 if (getNumOperands() > 0)
2480 if (SubfieldOp first = getOperand(0).getDefiningOp<SubfieldOp>())
2481 if (first.getFieldIndex() == 0 &&
2482 first.getInput().getType() == getType() &&
2484 llvm::drop_begin(llvm::enumerate(getOperands())), [&](
auto elem) {
2486 elem.value().
template getDefiningOp<SubfieldOp>();
2487 return subindex && subindex.getInput() == first.getInput() &&
2488 subindex.getFieldIndex() == elem.index();
2490 return first.getInput();
2495OpFoldResult VectorCreateOp::fold(FoldAdaptor adaptor) {
2498 if (getNumOperands() > 0)
2499 if (SubindexOp first = getOperand(0).getDefiningOp<SubindexOp>())
2500 if (first.getIndex() == 0 && first.getInput().getType() == getType() &&
2502 llvm::drop_begin(llvm::enumerate(getOperands())), [&](
auto elem) {
2504 elem.value().
template getDefiningOp<SubindexOp>();
2505 return subindex && subindex.getInput() == first.getInput() &&
2506 subindex.getIndex() == elem.index();
2508 return first.getInput();
2513OpFoldResult UninferredResetCastOp::fold(FoldAdaptor adaptor) {
2514 if (getOperand().getType() == getType())
2515 return getOperand();
2522struct FoldResetMux :
public mlir::RewritePattern {
2523 FoldResetMux(MLIRContext *context)
2524 : RewritePattern(RegResetOp::getOperationName(), 0, context) {}
2525 LogicalResult matchAndRewrite(Operation *op,
2526 PatternRewriter &rewriter)
const override {
2527 auto reg = cast<RegResetOp>(op);
2529 dyn_cast_or_null<ConstantOp>(
reg.getResetValue().getDefiningOp());
2538 auto mux = dyn_cast_or_null<MuxPrimOp>(con.getSrc().getDefiningOp());
2541 auto *high = mux.getHigh().getDefiningOp();
2542 auto *low = mux.getLow().getDefiningOp();
2543 auto constOp = dyn_cast_or_null<ConstantOp>(high);
2545 if (constOp && low != reg)
2547 if (dyn_cast_or_null<ConstantOp>(low) && high == reg)
2548 constOp = dyn_cast<ConstantOp>(low);
2550 if (!constOp || constOp.getType() != reset.getType() ||
2551 constOp.getValue() != reset.getValue())
2555 auto regTy =
reg.getResult().getType();
2556 if (con.getDest().getType() != regTy || con.getSrc().getType() != regTy ||
2557 mux.getHigh().getType() != regTy || mux.getLow().getType() != regTy ||
2558 regTy.getBitWidthOrSentinel() < 0)
2564 if (constOp != &con->getBlock()->front())
2565 constOp->moveBefore(&con->getBlock()->front());
2570 rewriter.eraseOp(con);
2577 if (
auto c = v.getDefiningOp<ConstantOp>())
2578 return c.getValue().isOne();
2579 if (
auto sc = v.getDefiningOp<SpecialConstantOp>())
2580 return sc.getValue();
2589 auto resetValue = reg.getResetValue();
2590 if (reg.getType(0) != resetValue.getType())
2594 (void)
dropWrite(rewriter, reg->getResult(0), {});
2595 replaceOpWithNewOpAndCopyName<NodeOp>(
2596 rewriter, reg, resetValue, reg.getNameAttr(), reg.getNameKind(),
2597 reg.getAnnotationsAttr(), reg.getInnerSymAttr(), reg.getForceable());
2601void RegResetOp::getCanonicalizationPatterns(RewritePatternSet &results,
2602 MLIRContext *context) {
2603 results.add<patterns::RegResetWithZeroReset, FoldResetMux>(context);
2605 results.add(demoteForceableIfUnused<RegResetOp>);
2610 auto portTy = type_cast<BundleType>(port.getType());
2611 auto fieldIndex = portTy.getElementIndex(name);
2612 assert(fieldIndex &&
"missing field on memory port");
2615 for (
auto *op : port.getUsers()) {
2616 auto portAccess = cast<SubfieldOp>(op);
2617 if (fieldIndex != portAccess.getFieldIndex())
2622 value = conn.getSrc();
2632 auto portConst = value.getDefiningOp<ConstantOp>();
2635 return portConst.getValue().isZero();
2640 auto portTy = type_cast<BundleType>(port.getType());
2641 auto fieldIndex = portTy.getElementIndex(
data);
2642 assert(fieldIndex &&
"missing enable flag on memory port");
2644 for (
auto *op : port.getUsers()) {
2645 auto portAccess = cast<SubfieldOp>(op);
2646 if (fieldIndex != portAccess.getFieldIndex())
2648 if (!portAccess.use_empty())
2657 StringRef name, Value value) {
2658 auto portTy = type_cast<BundleType>(port.getType());
2659 auto fieldIndex = portTy.getElementIndex(name);
2660 assert(fieldIndex &&
"missing field on memory port");
2662 for (
auto *op : llvm::make_early_inc_range(port.getUsers())) {
2663 auto portAccess = cast<SubfieldOp>(op);
2664 if (fieldIndex != portAccess.getFieldIndex())
2666 rewriter.replaceAllUsesWith(portAccess, value);
2667 rewriter.eraseOp(portAccess);
2672static void erasePort(PatternRewriter &rewriter, Value port) {
2675 auto getClock = [&] {
2677 clock = SpecialConstantOp::create(rewriter, port.getLoc(),
2678 ClockType::get(rewriter.getContext()),
2687 for (
auto *op : port.getUsers()) {
2688 auto subfield = dyn_cast<SubfieldOp>(op);
2690 auto ty = port.getType();
2691 auto reg = RegOp::create(rewriter, port.getLoc(), ty, getClock());
2692 rewriter.replaceAllUsesWith(port, reg.getResult());
2701 for (
auto *accessOp : llvm::make_early_inc_range(port.getUsers())) {
2702 auto access = cast<SubfieldOp>(accessOp);
2703 for (
auto *user : llvm::make_early_inc_range(access->getUsers())) {
2704 auto connect = dyn_cast<FConnectLike>(user);
2705 if (connect && connect.getDest() == access) {
2706 rewriter.eraseOp(user);
2710 if (access.use_empty()) {
2711 rewriter.eraseOp(access);
2717 auto ty = access.getType();
2718 auto reg = RegOp::create(rewriter, access.getLoc(), ty, getClock());
2719 rewriter.replaceOp(access, reg.getResult());
2721 assert(port.use_empty() &&
"port should have no remaining uses");
2726struct FoldZeroWidthMemory :
public mlir::RewritePattern {
2727 FoldZeroWidthMemory(MLIRContext *context)
2728 : RewritePattern(MemOp::getOperationName(), 0, context) {}
2729 LogicalResult matchAndRewrite(Operation *op,
2730 PatternRewriter &rewriter)
const override {
2731 MemOp mem = cast<MemOp>(op);
2735 if (!firrtl::type_isa<IntType>(mem.getDataType()) ||
2736 mem.getDataType().getBitWidthOrSentinel() != 0)
2740 for (
auto port : mem.getResults())
2741 for (auto *user : port.getUsers())
2742 if (!isa<SubfieldOp>(user))
2747 for (
auto port : op->getResults()) {
2748 for (
auto *user :
llvm::make_early_inc_range(port.getUsers())) {
2749 SubfieldOp sfop = cast<SubfieldOp>(user);
2750 StringRef fieldName = sfop.getFieldName();
2751 auto wire = replaceOpWithNewOpAndCopyName<WireOp>(
2752 rewriter, sfop, sfop.getResult().getType())
2754 if (fieldName.ends_with(
"data")) {
2756 auto zero = firrtl::ConstantOp::create(
2757 rewriter, wire.getLoc(),
2758 firrtl::type_cast<IntType>(wire.getType()), APInt::getZero(0));
2759 MatchingConnectOp::create(rewriter, wire.getLoc(), wire, zero);
2763 rewriter.eraseOp(op);
2769struct FoldReadOrWriteOnlyMemory :
public mlir::RewritePattern {
2770 FoldReadOrWriteOnlyMemory(MLIRContext *context)
2771 : RewritePattern(MemOp::getOperationName(), 0, context) {}
2772 LogicalResult matchAndRewrite(Operation *op,
2773 PatternRewriter &rewriter)
const override {
2774 MemOp mem = cast<MemOp>(op);
2777 bool isRead =
false, isWritten =
false;
2778 for (
unsigned i = 0; i < mem.getNumResults(); ++i) {
2779 switch (mem.getPortKind(i)) {
2780 case MemOp::PortKind::Read:
2785 case MemOp::PortKind::Write:
2790 case MemOp::PortKind::Debug:
2791 case MemOp::PortKind::ReadWrite:
2794 llvm_unreachable(
"unknown port kind");
2796 assert((!isWritten || !isRead) &&
"memory is in use");
2801 if (isRead && mem.getInit())
2804 for (
auto port : mem.getResults())
2807 rewriter.eraseOp(op);
2813struct FoldUnusedPorts :
public mlir::RewritePattern {
2814 FoldUnusedPorts(MLIRContext *context)
2815 : RewritePattern(MemOp::getOperationName(), 0, context) {}
2816 LogicalResult matchAndRewrite(Operation *op,
2817 PatternRewriter &rewriter)
const override {
2818 MemOp mem = cast<MemOp>(op);
2822 llvm::SmallBitVector deadPorts(mem.getNumResults());
2823 for (
auto [i, port] :
llvm::enumerate(mem.getResults())) {
2825 if (!mem.getPortAnnotation(i).empty())
2829 auto kind = mem.getPortKind(i);
2830 if (kind == MemOp::PortKind::Debug)
2839 if (kind == MemOp::PortKind::Read &&
isPortUnused(port,
"data")) {
2844 if (deadPorts.none())
2848 SmallVector<Type> resultTypes;
2849 SmallVector<StringRef> portNames;
2850 SmallVector<Attribute> portAnnotations;
2851 for (
auto [i, port] :
llvm::enumerate(mem.getResults())) {
2854 resultTypes.push_back(port.getType());
2855 portNames.push_back(mem.getPortName(i));
2856 portAnnotations.push_back(mem.getPortAnnotation(i));
2860 if (!resultTypes.empty())
2861 newOp = MemOp::create(
2862 rewriter, mem.getLoc(), resultTypes, mem.getReadLatency(),
2863 mem.getWriteLatency(), mem.getDepth(), mem.getRuw(),
2864 rewriter.getStrArrayAttr(portNames), mem.getName(), mem.getNameKind(),
2865 mem.getAnnotations(), rewriter.getArrayAttr(portAnnotations),
2866 mem.getInnerSymAttr(), mem.getInitAttr(), mem.getPrefixAttr());
2869 unsigned nextPort = 0;
2870 for (
auto [i, port] :
llvm::enumerate(mem.getResults())) {
2874 rewriter.replaceAllUsesWith(port, newOp.getResult(nextPort++));
2877 rewriter.eraseOp(op);
2883struct FoldReadWritePorts :
public mlir::RewritePattern {
2884 FoldReadWritePorts(MLIRContext *context)
2885 : RewritePattern(MemOp::getOperationName(), 0, context) {}
2886 LogicalResult matchAndRewrite(Operation *op,
2887 PatternRewriter &rewriter)
const override {
2888 MemOp mem = cast<MemOp>(op);
2893 llvm::SmallBitVector deadReads(mem.getNumResults());
2894 for (
auto [i, port] :
llvm::enumerate(mem.getResults())) {
2895 if (mem.getPortKind(i) != MemOp::PortKind::ReadWrite)
2897 if (!mem.getPortAnnotation(i).empty())
2904 if (deadReads.none())
2907 SmallVector<Type> resultTypes;
2908 SmallVector<StringRef> portNames;
2909 SmallVector<Attribute> portAnnotations;
2910 for (
auto [i, port] :
llvm::enumerate(mem.getResults())) {
2912 resultTypes.push_back(
2913 MemOp::getTypeForPort(mem.getDepth(), mem.getDataType(),
2914 MemOp::PortKind::Write, mem.getMaskBits()));
2916 resultTypes.push_back(port.getType());
2918 portNames.push_back(mem.getPortName(i));
2919 portAnnotations.push_back(mem.getPortAnnotation(i));
2922 auto newOp = MemOp::create(
2923 rewriter, mem.getLoc(), resultTypes, mem.getReadLatency(),
2924 mem.getWriteLatency(), mem.getDepth(), mem.getRuw(),
2925 rewriter.getStrArrayAttr(portNames), mem.getName(), mem.getNameKind(),
2926 mem.getAnnotations(), rewriter.getArrayAttr(portAnnotations),
2927 mem.getInnerSymAttr(), mem.getInitAttr(), mem.getPrefixAttr());
2929 for (
unsigned i = 0, n = mem.getNumResults(); i < n; ++i) {
2930 auto result = mem.getResult(i);
2931 auto newResult = newOp.getResult(i);
2933 auto resultPortTy = type_cast<BundleType>(result.getType());
2937 auto replace = [&](StringRef toName, StringRef fromName) {
2938 auto fromFieldIndex = resultPortTy.getElementIndex(fromName);
2939 assert(fromFieldIndex &&
"missing enable flag on memory port");
2941 auto toField = SubfieldOp::create(rewriter, newResult.getLoc(),
2943 for (
auto *op :
llvm::make_early_inc_range(result.getUsers())) {
2944 auto fromField = cast<SubfieldOp>(op);
2945 if (fromFieldIndex != fromField.getFieldIndex())
2947 rewriter.replaceOp(fromField, toField.getResult());
2951 replace(
"addr",
"addr");
2952 replace(
"en",
"en");
2953 replace(
"clk",
"clk");
2954 replace(
"data",
"wdata");
2955 replace(
"mask",
"wmask");
2958 auto wmodeFieldIndex = resultPortTy.getElementIndex(
"wmode");
2959 for (
auto *op :
llvm::make_early_inc_range(result.getUsers())) {
2960 auto wmodeField = cast<SubfieldOp>(op);
2961 if (wmodeFieldIndex != wmodeField.getFieldIndex())
2963 rewriter.replaceOpWithNewOp<WireOp>(wmodeField, wmodeField.getType());
2966 rewriter.replaceAllUsesWith(result, newResult);
2969 rewriter.eraseOp(op);
2975struct FoldUnusedBits :
public mlir::RewritePattern {
2976 FoldUnusedBits(MLIRContext *context)
2977 : RewritePattern(MemOp::getOperationName(), 0, context) {}
2979 LogicalResult matchAndRewrite(Operation *op,
2980 PatternRewriter &rewriter)
const override {
2981 MemOp mem = cast<MemOp>(op);
2986 const auto &summary = mem.getSummary();
2987 if (summary.isMasked || summary.isSeqMem())
2990 auto type = type_dyn_cast<IntType>(mem.getDataType());
2993 auto width = type.getBitWidthOrSentinel();
2997 llvm::SmallBitVector usedBits(width);
2998 DenseMap<unsigned, unsigned> mapping;
3003 SmallVector<BitsPrimOp> readOps;
3004 auto findReadUsers = [&](Value port, StringRef field) -> LogicalResult {
3005 auto portTy = type_cast<BundleType>(port.getType());
3006 auto fieldIndex = portTy.getElementIndex(field);
3007 assert(fieldIndex &&
"missing data port");
3009 for (
auto *op : port.getUsers()) {
3010 auto portAccess = cast<SubfieldOp>(op);
3011 if (fieldIndex != portAccess.getFieldIndex())
3014 for (
auto *user : op->getUsers()) {
3015 auto bits = dyn_cast<BitsPrimOp>(user);
3019 usedBits.set(bits.getLo(), bits.getHi() + 1);
3023 mapping[bits.getLo()] = 0;
3024 readOps.push_back(bits);
3034 SmallVector<MatchingConnectOp> writeOps;
3035 auto findWriteUsers = [&](Value port, StringRef field) -> LogicalResult {
3036 auto portTy = type_cast<BundleType>(port.getType());
3037 auto fieldIndex = portTy.getElementIndex(field);
3038 assert(fieldIndex &&
"missing data port");
3040 for (
auto *op : port.getUsers()) {
3041 auto portAccess = cast<SubfieldOp>(op);
3042 if (fieldIndex != portAccess.getFieldIndex())
3049 writeOps.push_back(conn);
3055 for (
auto [i, port] :
llvm::enumerate(mem.getResults())) {
3057 if (!mem.getPortAnnotation(i).empty())
3060 switch (mem.getPortKind(i)) {
3061 case MemOp::PortKind::Debug:
3064 case MemOp::PortKind::Write:
3065 if (failed(findWriteUsers(port,
"data")))
3068 case MemOp::PortKind::Read:
3069 if (failed(findReadUsers(port,
"data")))
3072 case MemOp::PortKind::ReadWrite:
3073 if (failed(findWriteUsers(port,
"wdata")))
3075 if (failed(findReadUsers(port,
"rdata")))
3079 llvm_unreachable(
"unknown port kind");
3083 if (usedBits.none())
3087 SmallVector<std::pair<unsigned, unsigned>> ranges;
3088 unsigned newWidth = 0;
3089 for (
int i = usedBits.find_first(); 0 <= i && i < width;) {
3090 int e = usedBits.find_next_unset(i);
3093 for (
int idx = i; idx < e; ++idx, ++newWidth) {
3094 if (
auto it = mapping.find(idx); it != mapping.end()) {
3095 it->second = newWidth;
3098 ranges.emplace_back(i, e - 1);
3099 i = e != width ? usedBits.find_next(e) : e;
3103 auto newType =
IntType::get(op->getContext(), type.isSigned(), newWidth);
3104 SmallVector<Type> portTypes;
3105 for (
auto [i, port] :
llvm::enumerate(mem.getResults())) {
3106 portTypes.push_back(
3107 MemOp::getTypeForPort(mem.getDepth(), newType, mem.getPortKind(i)));
3109 auto newMem = rewriter.replaceOpWithNewOp<MemOp>(
3110 mem, portTypes, mem.getReadLatency(), mem.getWriteLatency(),
3111 mem.getDepth(), mem.getRuw(), mem.getPortNames(), mem.getName(),
3112 mem.getNameKind(), mem.getAnnotations(), mem.getPortAnnotations(),
3113 mem.getInnerSymAttr(), mem.getInitAttr(), mem.getPrefixAttr());
3116 auto rewriteSubfield = [&](Value port, StringRef field) {
3117 auto portTy = type_cast<BundleType>(port.getType());
3118 auto fieldIndex = portTy.getElementIndex(field);
3119 assert(fieldIndex &&
"missing data port");
3121 rewriter.setInsertionPointAfter(newMem);
3122 auto newPortAccess =
3123 SubfieldOp::create(rewriter, port.getLoc(), port, field);
3125 for (
auto *op :
llvm::make_early_inc_range(port.getUsers())) {
3126 auto portAccess = cast<SubfieldOp>(op);
3127 if (op == newPortAccess || fieldIndex != portAccess.getFieldIndex())
3129 rewriter.replaceOp(portAccess, newPortAccess.getResult());
3134 for (
auto [i, port] :
llvm::enumerate(newMem.getResults())) {
3135 switch (newMem.getPortKind(i)) {
3136 case MemOp::PortKind::Debug:
3137 llvm_unreachable(
"cannot rewrite debug port");
3138 case MemOp::PortKind::Write:
3139 rewriteSubfield(port,
"data");
3141 case MemOp::PortKind::Read:
3142 rewriteSubfield(port,
"data");
3144 case MemOp::PortKind::ReadWrite:
3145 rewriteSubfield(port,
"rdata");
3146 rewriteSubfield(port,
"wdata");
3149 llvm_unreachable(
"unknown port kind");
3153 for (
auto readOp : readOps) {
3154 rewriter.setInsertionPointAfter(readOp);
3155 auto it = mapping.find(readOp.getLo());
3156 assert(it != mapping.end() &&
"bit op mapping not found");
3159 auto newReadValue = rewriter.createOrFold<BitsPrimOp>(
3160 readOp.getLoc(), readOp.getInput(),
3161 readOp.getHi() - readOp.getLo() + it->second, it->second);
3162 rewriter.replaceAllUsesWith(readOp, newReadValue);
3163 rewriter.eraseOp(readOp);
3167 for (
auto writeOp : writeOps) {
3168 Value source = writeOp.getSrc();
3169 rewriter.setInsertionPoint(writeOp);
3171 SmallVector<Value> slices;
3172 for (
auto &[start, end] :
llvm::reverse(ranges)) {
3173 Value slice = rewriter.createOrFold<BitsPrimOp>(writeOp.getLoc(),
3174 source,
end, start);
3175 slices.push_back(slice);
3179 rewriter.createOrFold<CatPrimOp>(writeOp.getLoc(), slices);
3185 if (type.isSigned())
3187 rewriter.createOrFold<AsSIntPrimOp>(writeOp.getLoc(), catOfSlices);
3189 rewriter.replaceOpWithNewOp<MatchingConnectOp>(writeOp, writeOp.getDest(),
3198struct FoldRegMems :
public mlir::RewritePattern {
3199 FoldRegMems(MLIRContext *context)
3200 : RewritePattern(MemOp::getOperationName(), 0, context) {}
3201 LogicalResult matchAndRewrite(Operation *op,
3202 PatternRewriter &rewriter)
const override {
3203 MemOp mem = cast<MemOp>(op);
3208 auto ty = mem.getDataType();
3209 auto loc = mem.getLoc();
3210 auto *block = mem->getBlock();
3214 SmallPtrSet<Operation *, 8> connects;
3215 SmallVector<SubfieldOp> portAccesses;
3216 for (
auto [i, port] :
llvm::enumerate(mem.getResults())) {
3217 if (!mem.getPortAnnotation(i).empty())
3220 auto collect = [&, port = port](ArrayRef<StringRef> fields) {
3221 auto portTy = type_cast<BundleType>(port.getType());
3222 for (
auto field : fields) {
3223 auto fieldIndex = portTy.getElementIndex(field);
3224 assert(fieldIndex &&
"missing field on memory port");
3226 for (
auto *op : port.getUsers()) {
3227 auto portAccess = cast<SubfieldOp>(op);
3228 if (fieldIndex != portAccess.getFieldIndex())
3230 portAccesses.push_back(portAccess);
3231 for (
auto *user : portAccess->getUsers()) {
3232 auto conn = dyn_cast<FConnectLike>(user);
3235 connects.insert(conn);
3242 switch (mem.getPortKind(i)) {
3243 case MemOp::PortKind::Debug:
3245 case MemOp::PortKind::Read:
3246 if (failed(collect({
"clk",
"en",
"addr"})))
3249 case MemOp::PortKind::Write:
3250 if (failed(collect({
"clk",
"en",
"addr",
"data",
"mask"})))
3253 case MemOp::PortKind::ReadWrite:
3254 if (failed(collect({
"clk",
"en",
"addr",
"wmode",
"wdata",
"wmask"})))
3260 if (!portClock || (clock && portClock != clock))
3266 rewriter.setInsertionPointAfter(mem);
3267 auto memWire = WireOp::create(rewriter, loc, ty).getResult();
3273 rewriter.setInsertionPointToEnd(block);
3275 RegOp::create(rewriter, loc, ty, clock, mem.getName()).getResult();
3278 MatchingConnectOp::create(rewriter, loc, memWire, memReg);
3282 auto pipeline = [&](Value value, Value clock,
const Twine &name,
3284 for (
unsigned i = 0; i < latency; ++i) {
3285 std::string regName;
3287 llvm::raw_string_ostream os(regName);
3288 os << mem.getName() <<
"_" << name <<
"_" << i;
3290 auto reg = RegOp::create(rewriter, mem.getLoc(), value.getType(), clock,
3291 rewriter.getStringAttr(regName))
3293 MatchingConnectOp::create(rewriter, value.getLoc(), reg, value);
3299 const unsigned writeStages =
info.writeLatency - 1;
3304 SmallVector<std::tuple<Value, Value, Value>> writes;
3305 for (
auto [i, port] :
llvm::enumerate(mem.getResults())) {
3307 StringRef name = mem.getPortName(i);
3309 auto portPipeline = [&, port = port](StringRef field,
unsigned stages) {
3312 return pipeline(value, portClock, name +
"_" + field, stages);
3315 switch (mem.getPortKind(i)) {
3316 case MemOp::PortKind::Debug:
3317 llvm_unreachable(
"unknown port kind");
3318 case MemOp::PortKind::Read: {
3326 case MemOp::PortKind::Write: {
3327 auto data = portPipeline(
"data", writeStages);
3328 auto en = portPipeline(
"en", writeStages);
3329 auto mask = portPipeline(
"mask", writeStages);
3333 case MemOp::PortKind::ReadWrite: {
3338 auto wdata = portPipeline(
"wdata", writeStages);
3339 auto wmask = portPipeline(
"wmask", writeStages);
3344 auto wen = AndPrimOp::create(rewriter, port.getLoc(),
en,
wmode);
3346 pipeline(wen, portClock, name +
"_wen", writeStages);
3347 writes.emplace_back(
wdata, wenPipelined,
wmask);
3354 Value next = memReg;
3360 Location loc = mem.getLoc();
3361 unsigned maskGran =
info.dataWidth /
info.maskBits;
3362 SmallVector<Value> chunks;
3363 for (
unsigned i = 0; i <
info.maskBits; ++i) {
3364 unsigned hi = (i + 1) * maskGran - 1;
3365 unsigned lo = i * maskGran;
3367 auto dataPart = rewriter.createOrFold<BitsPrimOp>(loc,
data, hi, lo);
3368 auto nextPart = rewriter.createOrFold<BitsPrimOp>(loc, next, hi, lo);
3369 auto bit = rewriter.createOrFold<BitsPrimOp>(loc,
mask, i, i);
3370 auto chunk = MuxPrimOp::create(rewriter, loc, bit, dataPart, nextPart);
3371 chunks.push_back(chunk);
3374 std::reverse(chunks.begin(), chunks.end());
3375 masked = rewriter.createOrFold<CatPrimOp>(loc, chunks);
3376 next = MuxPrimOp::create(rewriter, next.getLoc(),
en, masked, next);
3378 Value typedNext = rewriter.createOrFold<BitCastOp>(next.getLoc(), ty, next);
3379 MatchingConnectOp::create(rewriter, memReg.getLoc(), memReg, typedNext);
3382 for (Operation *conn : connects)
3383 rewriter.eraseOp(conn);
3384 for (
auto portAccess : portAccesses)
3385 rewriter.eraseOp(portAccess);
3386 rewriter.eraseOp(mem);
3393void MemOp::getCanonicalizationPatterns(RewritePatternSet &results,
3394 MLIRContext *context) {
3396 .insert<FoldZeroWidthMemory, FoldReadOrWriteOnlyMemory,
3397 FoldReadWritePorts, FoldUnusedPorts, FoldUnusedBits, FoldRegMems>(
3417 auto mux = dyn_cast_or_null<MuxPrimOp>(con.getSrc().getDefiningOp());
3420 auto *high = mux.getHigh().getDefiningOp();
3421 auto *low = mux.getLow().getDefiningOp();
3423 auto constOp = dyn_cast_or_null<ConstantOp>(high);
3430 bool constReg =
false;
3432 if (constOp && low == reg)
3434 else if (dyn_cast_or_null<ConstantOp>(low) && high == reg) {
3436 constOp = dyn_cast<ConstantOp>(low);
3443 if (!isa<BlockArgument>(mux.getSel()) && !constReg)
3447 auto regTy = reg.getResult().getType();
3448 if (con.getDest().getType() != regTy || con.getSrc().getType() != regTy ||
3449 mux.getHigh().getType() != regTy || mux.getLow().getType() != regTy ||
3450 regTy.getBitWidthOrSentinel() < 0)
3456 if (constOp != &con->getBlock()->front())
3457 constOp->moveBefore(&con->getBlock()->front());
3460 SmallVector<NamedAttribute, 2> attrs(reg->getDialectAttrs());
3461 auto newReg = replaceOpWithNewOpAndCopyName<RegResetOp>(
3462 rewriter, reg, reg.getResult().getType(), reg.getClockVal(),
3463 mux.getSel(), mux.getHigh(), reg.getNameAttr(), reg.getNameKindAttr(),
3464 reg.getAnnotationsAttr(), reg.getInnerSymAttr(),
3465 reg.getForceableAttr());
3466 newReg->setDialectAttrs(attrs);
3468 auto pt = rewriter.saveInsertionPoint();
3469 rewriter.setInsertionPoint(con);
3470 auto v = constReg ? (Value)constOp.getResult() : (Value)mux.getLow();
3471 replaceOpWithNewOpAndCopyName<ConnectOp>(rewriter, con, con.getDest(), v);
3472 rewriter.restoreInsertionPoint(pt);
3476LogicalResult RegOp::canonicalize(RegOp op, PatternRewriter &rewriter) {
3477 if (!
hasDontTouch(op.getOperation()) && !op.isForceable() &&
3493 PatternRewriter &rewriter,
3496 if (
auto constant = enable.getDefiningOp<firrtl::ConstantOp>()) {
3497 if (constant.getValue().isZero()) {
3498 rewriter.eraseOp(op);
3504 if (
auto constant = predicate.getDefiningOp<firrtl::ConstantOp>()) {
3505 if (constant.getValue().isZero() == eraseIfZero) {
3506 rewriter.eraseOp(op);
3514template <
class Op,
bool EraseIfZero = false>
3516 PatternRewriter &rewriter) {
3521void AssertOp::getCanonicalizationPatterns(RewritePatternSet &results,
3522 MLIRContext *context) {
3523 results.add(canonicalizeImmediateVerifOp<AssertOp>);
3524 results.add<patterns::AssertXWhenX>(context);
3527void AssumeOp::getCanonicalizationPatterns(RewritePatternSet &results,
3528 MLIRContext *context) {
3529 results.add(canonicalizeImmediateVerifOp<AssumeOp>);
3530 results.add<patterns::AssumeXWhenX>(context);
3533void UnclockedAssumeIntrinsicOp::getCanonicalizationPatterns(
3534 RewritePatternSet &results, MLIRContext *context) {
3535 results.add(canonicalizeImmediateVerifOp<UnclockedAssumeIntrinsicOp>);
3536 results.add<patterns::UnclockedAssumeIntrinsicXWhenX>(context);
3539void CoverOp::getCanonicalizationPatterns(RewritePatternSet &results,
3540 MLIRContext *context) {
3541 results.add(canonicalizeImmediateVerifOp<CoverOp, /* EraseIfZero = */ true>);
3548LogicalResult InvalidValueOp::canonicalize(InvalidValueOp op,
3549 PatternRewriter &rewriter) {
3551 if (op.use_empty()) {
3552 rewriter.eraseOp(op);
3559 if (op->hasOneUse() &&
3560 (isa<BitsPrimOp, HeadPrimOp, ShrPrimOp, TailPrimOp, SubfieldOp,
3561 SubindexOp, AsSIntPrimOp, AsUIntPrimOp, NotPrimOp, BitCastOp>(
3562 *op->user_begin()) ||
3563 (isa<CvtPrimOp>(*op->user_begin()) &&
3564 type_isa<SIntType>(op->user_begin()->getOperand(0).getType())) ||
3565 (isa<AndRPrimOp, XorRPrimOp, OrRPrimOp>(*op->user_begin()) &&
3566 type_cast<FIRRTLBaseType>(op->user_begin()->getOperand(0).getType())
3567 .getBitWidthOrSentinel() > 0))) {
3568 auto *modop = *op->user_begin();
3569 auto inv = InvalidValueOp::create(rewriter, op.getLoc(),
3570 modop->getResult(0).getType());
3571 rewriter.replaceAllOpUsesWith(modop, inv);
3572 rewriter.eraseOp(modop);
3573 rewriter.eraseOp(op);
3579OpFoldResult InvalidValueOp::fold(FoldAdaptor adaptor) {
3580 if (getType().getBitWidthOrSentinel() == 0 && isa<IntType>(getType()))
3581 return getIntAttr(getType(), APInt(0, 0, isa<SIntType>(getType())));
3589OpFoldResult ClockGateIntrinsicOp::fold(FoldAdaptor adaptor) {
3598 return BoolAttr::get(getContext(),
false);
3602 return BoolAttr::get(getContext(),
false);
3607LogicalResult ClockGateIntrinsicOp::canonicalize(ClockGateIntrinsicOp op,
3608 PatternRewriter &rewriter) {
3610 if (
auto testEnable = op.getTestEnable()) {
3611 if (
auto constOp = testEnable.getDefiningOp<ConstantOp>()) {
3612 if (constOp.getValue().isZero()) {
3613 rewriter.modifyOpInPlace(op,
3614 [&] { op.getTestEnableMutable().clear(); });
3630 auto forceable = op.getRef().getDefiningOp<Forceable>();
3631 if (!forceable || !forceable.isForceable() ||
3632 op.getRef() != forceable.getDataRef() ||
3633 op.getType() != forceable.getDataType())
3635 rewriter.replaceAllUsesWith(op, forceable.getData());
3639void RefResolveOp::getCanonicalizationPatterns(RewritePatternSet &results,
3640 MLIRContext *context) {
3641 results.insert<patterns::RefResolveOfRefSend>(context);
3645OpFoldResult RefCastOp::fold(FoldAdaptor adaptor) {
3647 if (getInput().getType() == getType())
3653 auto constOp = operand.getDefiningOp<ConstantOp>();
3654 return constOp && constOp.getValue().isZero();
3657template <
typename Op>
3660 rewriter.eraseOp(op);
3666void RefForceOp::getCanonicalizationPatterns(RewritePatternSet &results,
3667 MLIRContext *context) {
3668 results.add(eraseIfPredFalse<RefForceOp>);
3670void RefForceInitialOp::getCanonicalizationPatterns(RewritePatternSet &results,
3671 MLIRContext *context) {
3672 results.add(eraseIfPredFalse<RefForceInitialOp>);
3674void RefReleaseOp::getCanonicalizationPatterns(RewritePatternSet &results,
3675 MLIRContext *context) {
3676 results.add(eraseIfPredFalse<RefReleaseOp>);
3678void RefReleaseInitialOp::getCanonicalizationPatterns(
3679 RewritePatternSet &results, MLIRContext *context) {
3680 results.add(eraseIfPredFalse<RefReleaseInitialOp>);
3687OpFoldResult HasBeenResetIntrinsicOp::fold(FoldAdaptor adaptor) {
3693 if (adaptor.getReset())
3698 if (
isUInt1(getReset().getType()) && adaptor.getClock())
3711 [&](
auto ty) ->
bool {
return isTypeEmpty(ty.getElementType()); })
3712 .Case<BundleType>([&](
auto ty) ->
bool {
3713 for (
auto elem : ty.getElements())
3718 .Case<IntType>([&](
auto ty) {
return ty.getWidth() == 0; })
3719 .Default([](
auto) ->
bool {
return false; });
3722LogicalResult FPGAProbeIntrinsicOp::canonicalize(FPGAProbeIntrinsicOp op,
3723 PatternRewriter &rewriter) {
3724 auto firrtlTy = type_dyn_cast<FIRRTLType>(op.getInput().getType());
3731 rewriter.eraseOp(op);
3739LogicalResult LayerBlockOp::canonicalize(LayerBlockOp op,
3740 PatternRewriter &rewriter) {
3743 if (op.getBody()->empty()) {
3744 rewriter.eraseOp(op);
3755OpFoldResult UnsafeDomainCastOp::fold(FoldAdaptor adaptor) {
3757 if (getDomains().
empty())
assert(baseType &&"element must be base type")
static bool hasKnownWidthIntTypes(Operation *op)
Return true if this operation's operands and results all have a known width.
static LogicalResult canonicalizeImmediateVerifOp(Op op, PatternRewriter &rewriter)
static bool isDefinedByOneConstantOp(Value v)
static Attribute collectFields(MLIRContext *context, ArrayRef< Attribute > operands)
static LogicalResult canonicalizeSingleSetConnect(MatchingConnectOp op, PatternRewriter &rewriter)
static void erasePort(PatternRewriter &rewriter, Value port)
static void replaceOpWithRegion(PatternRewriter &rewriter, Operation *op, Region ®ion)
Replaces the given op with the contents of the given single-block region.
static std::optional< APSInt > getExtendedConstant(Value operand, Attribute constant, int32_t destWidth)
Implicitly replace the operand to a constant folding operation with a const 0 in case the operand is ...
static Value getPortFieldValue(Value port, StringRef name)
static AttachOp getDominatingAttachUser(Value value, AttachOp dominatedAttach)
If the specified value has an AttachOp user strictly dominating by "dominatingAttach" then return it.
static OpTy replaceOpWithNewOpAndCopyName(PatternRewriter &rewriter, Operation *op, Args &&...args)
A wrapper of PatternRewriter::replaceOpWithNewOp to propagate "name" attribute.
static void updateName(PatternRewriter &rewriter, Operation *op, StringAttr name)
Set the name of an op based on the best of two names: The current name, and the name passed in.
static bool isTypeEmpty(FIRRTLType type)
static bool isUInt1(Type type)
Return true if this value is 1 bit UInt.
static LogicalResult demoteForceableIfUnused(OpTy op, PatternRewriter &rewriter)
static bool isPortDisabled(Value port)
static LogicalResult eraseIfZeroOrNotZero(Operation *op, Value predicate, Value enable, PatternRewriter &rewriter, bool eraseIfZero)
static APInt getMaxSignedValue(unsigned bitWidth)
Get the largest signed value of a given bit width.
static Value dropWrite(PatternRewriter &rewriter, OpResult old, Value passthrough)
static LogicalResult canonicalizePrimOp(Operation *op, PatternRewriter &rewriter, const function_ref< OpFoldResult(ArrayRef< Attribute >)> &canonicalize)
Applies the canonicalization function canonicalize to the given operation.
static void replaceWithBits(Operation *op, Value value, unsigned hiBit, unsigned loBit, PatternRewriter &rewriter)
Replace the specified operation with a 'bits' op from the specified hi/lo bits.
static LogicalResult canonicalizeRegResetWithOneReset(RegResetOp reg, PatternRewriter &rewriter)
static LogicalResult eraseIfPredFalse(Op op, PatternRewriter &rewriter)
static OpFoldResult foldMux(OpTy op, typename OpTy::FoldAdaptor adaptor)
static APInt getMaxUnsignedValue(unsigned bitWidth)
Get the largest unsigned value of a given bit width.
static std::optional< APSInt > getConstant(Attribute operand)
Determine the value of a constant operand for the sake of constant folding.
static void replacePortField(PatternRewriter &rewriter, Value port, StringRef name, Value value)
BinOpKind
This is the policy for folding, which depends on the sort of operator we're processing.
static bool isPortUnused(Value port, StringRef data)
static bool isOkToPropagateName(Operation *op)
static LogicalResult canonicalizeRefResolveOfForceable(RefResolveOp op, PatternRewriter &rewriter)
static Attribute constFoldFIRRTLBinaryOp(Operation *op, ArrayRef< Attribute > operands, BinOpKind opKind, const function_ref< APInt(const APSInt &, const APSInt &)> &calculate)
Applies the constant folding function calculate to the given operands.
static APInt getMinSignedValue(unsigned bitWidth)
Get the smallest signed value of a given bit width.
static LogicalResult foldHiddenReset(RegOp reg, PatternRewriter &rewriter)
static Value moveNameHint(OpResult old, Value passthrough)
static void replaceOpAndCopyName(PatternRewriter &rewriter, Operation *op, Value newValue)
A wrapper of PatternRewriter::replaceOp to propagate "name" attribute.
static Location getLoc(DefSlot slot)
static InstancePath empty
AndRCat(MLIRContext *context)
bool handleConstant(mlir::PatternRewriter &rewriter, Operation *op, ConstantOp value, SmallVectorImpl< Value > &remaining) const override
Handle a constant operand in the cat operation.
bool getIdentityValue() const override
Return the unit value for this reduction operation:
bool handleConstant(mlir::PatternRewriter &rewriter, Operation *op, ConstantOp value, SmallVectorImpl< Value > &remaining) const override
Handle a constant operand in the cat operation.
OrRCat(MLIRContext *context)
bool getIdentityValue() const override
Return the unit value for this reduction operation:
virtual bool getIdentityValue() const =0
Return the unit value for this reduction operation:
virtual bool handleConstant(mlir::PatternRewriter &rewriter, Operation *op, ConstantOp constantOp, SmallVectorImpl< Value > &remaining) const =0
Handle a constant operand in the cat operation.
LogicalResult matchAndRewrite(Operation *op, mlir::PatternRewriter &rewriter) const override
ReductionCat(MLIRContext *context, llvm::StringLiteral opName)
XorRCat(MLIRContext *context)
bool handleConstant(mlir::PatternRewriter &rewriter, Operation *op, ConstantOp value, SmallVectorImpl< Value > &remaining) const override
Handle a constant operand in the cat operation.
bool getIdentityValue() const override
Return the unit value for this reduction operation:
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
This class implements the same functionality as TypeSwitch except that it uses firrtl::type_dyn_cast ...
FIRRTLTypeSwitch< T, ResultT > & Case(CallableT &&caseFn)
Add a case on the given type.
This is the common base class between SIntType and UIntType.
int32_t getWidthOrSentinel() const
Return the width of this type, or -1 if it has none specified.
static IntType get(MLIRContext *context, bool isSigned, int32_t widthOrSentinel=-1, bool isConst=false)
Return an SIntType or UIntType with the specified signedness, width, and constness.
bool hasWidth() const
Return true if this integer type has a known width.
std::optional< int32_t > getWidth() const
Return an optional containing the width, if the width is known (or empty if width is unknown).
uint64_t getWidth(Type t)
Forceable replaceWithNewForceability(Forceable op, bool forceable, ::mlir::PatternRewriter *rewriter=nullptr)
Replace a Forceable op with equivalent, changing whether forceable.
bool areAnonymousTypesEquivalent(FIRRTLBaseType lhs, FIRRTLBaseType rhs)
Return true if anonymous types of given arguments are equivalent by pointer comparison.
IntegerAttr getIntAttr(Type type, const APInt &value)
Utiility for generating a constant attribute.
bool hasDontTouch(Value value)
Check whether a block argument ("port") or the operation defining a value has a DontTouch annotation,...
bool hasDroppableName(Operation *op)
Return true if the name is droppable.
MatchingConnectOp getSingleConnectUserOf(Value value)
Scan all the uses of the specified value, checking to see if there is exactly one connect that has th...
std::optional< int64_t > getBitWidth(FIRRTLBaseType type, bool ignoreFlip=false)
IntegerAttr getIntZerosAttr(Type type)
Utility for generating a constant zero attribute.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
APSInt extOrTruncZeroWidth(APSInt value, unsigned width)
A safe version of APSInt::extOrTrunc that will NOT assert on zero-width signed APSInts.
APInt sextZeroWidth(APInt value, unsigned width)
A safe version of APInt::sext that will NOT assert on zero-width signed APSInts.
StringRef chooseName(StringRef a, StringRef b)
Choose a good name for an item from two options.
static bool isConstantZero(Attribute operand)
Determine whether a constant operand is a zero value.
static bool isConstantOne(Attribute operand)
Determine whether a constant operand is a one value.
reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
LogicalResult matchAndRewrite(Operation *op, mlir::PatternRewriter &rewriter) const override
BitsOfCat(MLIRContext *context)