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);
1180 :
ReductionCat(context, OrRPrimOp::getOperationName()) {}
1183 SmallVectorImpl<Value> &remaining)
const override {
1184 if (value.getValue().isZero())
1187 replaceOpWithNewOpAndCopyName<ConstantOp>(
1188 rewriter, op, cast<IntType>(op->getResult(0).getType()),
1198 :
ReductionCat(context, AndRPrimOp::getOperationName()) {}
1201 SmallVectorImpl<Value> &remaining)
const override {
1202 if (value.getValue().isAllOnes())
1205 replaceOpWithNewOpAndCopyName<ConstantOp>(
1206 rewriter, op, cast<IntType>(op->getResult(0).getType()),
1216 :
ReductionCat(context, XorRPrimOp::getOperationName()) {}
1219 SmallVectorImpl<Value> &remaining)
const override {
1220 if (value.getValue().isZero())
1222 remaining.push_back(value);
1228OpFoldResult AndRPrimOp::fold(FoldAdaptor adaptor) {
1232 if (getInput().getType().getBitWidthOrSentinel() == 0)
1237 return getIntAttr(getType(), APInt(1, cst->isAllOnes()));
1241 if (
isUInt1(getInput().getType()))
1247void AndRPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1248 MLIRContext *context) {
1249 results.insert<patterns::AndRasSInt, patterns::AndRasUInt, patterns::AndRPadU,
1250 patterns::AndRPadS, patterns::AndRCatAndR_left,
1251 patterns::AndRCatAndR_right,
AndRCat>(context);
1254OpFoldResult OrRPrimOp::fold(FoldAdaptor adaptor) {
1258 if (getInput().getType().getBitWidthOrSentinel() == 0)
1263 return getIntAttr(getType(), APInt(1, !cst->isZero()));
1267 if (
isUInt1(getInput().getType()))
1273void OrRPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1274 MLIRContext *context) {
1275 results.insert<patterns::OrRasSInt, patterns::OrRasUInt, patterns::OrRPadU,
1276 patterns::OrRCatOrR_left, patterns::OrRCatOrR_right,
OrRCat>(
1280OpFoldResult XorRPrimOp::fold(FoldAdaptor adaptor) {
1284 if (getInput().getType().getBitWidthOrSentinel() == 0)
1289 return getIntAttr(getType(), APInt(1, cst->popcount() & 1));
1292 if (
isUInt1(getInput().getType()))
1298void XorRPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1299 MLIRContext *context) {
1301 .insert<patterns::XorRasSInt, patterns::XorRasUInt, patterns::XorRPadU,
1302 patterns::XorRCatXorR_left, patterns::XorRCatXorR_right,
XorRCat>(
1310OpFoldResult CatPrimOp::fold(FoldAdaptor adaptor) {
1311 auto inputs = getInputs();
1312 auto inputAdaptors = adaptor.getInputs();
1319 if (inputs.size() == 1 && inputs[0].getType() == getType())
1327 SmallVector<Value> nonZeroInputs;
1328 SmallVector<Attribute> nonZeroAttributes;
1329 bool allConstant =
true;
1330 for (
auto [input, attr] :
llvm::zip(inputs, inputAdaptors)) {
1331 auto inputType = type_cast<IntType>(input.getType());
1332 if (inputType.getBitWidthOrSentinel() != 0) {
1333 nonZeroInputs.push_back(input);
1335 allConstant =
false;
1336 if (nonZeroInputs.size() > 1 && !allConstant)
1342 if (nonZeroInputs.empty())
1346 if (nonZeroInputs.size() == 1 && nonZeroInputs[0].getType() == getType())
1347 return nonZeroInputs[0];
1353 SmallVector<APInt> constants;
1354 for (
auto inputAdaptor : inputAdaptors) {
1356 constants.push_back(*cst);
1361 assert(!constants.empty());
1363 APInt result = constants[0];
1364 for (
size_t i = 1; i < constants.size(); ++i)
1365 result = result.concat(constants[i]);
1370void DShlPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1371 MLIRContext *context) {
1372 results.insert<patterns::DShlOfConstant>(context);
1375void DShrPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1376 MLIRContext *context) {
1377 results.insert<patterns::DShrOfConstant>(context);
1383class FlattenCat :
public mlir::RewritePattern {
1385 FlattenCat(MLIRContext *context)
1386 : RewritePattern(CatPrimOp::getOperationName(), 0, context) {}
1389 matchAndRewrite(Operation *op,
1390 mlir::PatternRewriter &rewriter)
const override {
1391 auto cat = cast<CatPrimOp>(op);
1393 cat.getType().getBitWidthOrSentinel() == 0)
1397 if (cat->hasOneUse() && isa<CatPrimOp>(*cat->getUsers().begin()))
1401 SmallVector<Value> operands;
1402 SmallVector<Value> worklist;
1403 auto pushOperands = [&worklist](CatPrimOp op) {
1404 for (
auto operand :
llvm::reverse(op.getInputs()))
1405 worklist.push_back(operand);
1408 bool hasSigned =
false, hasUnsigned =
false;
1409 while (!worklist.empty()) {
1410 auto value = worklist.pop_back_val();
1411 auto catOp = value.getDefiningOp<CatPrimOp>();
1413 operands.push_back(value);
1414 (type_isa<UIntType>(value.getType()) ? hasUnsigned : hasSigned) =
true;
1418 pushOperands(catOp);
1423 auto castToUIntIfSigned = [&](Value value) -> Value {
1424 if (type_isa<UIntType>(value.getType()))
1426 return AsUIntPrimOp::create(rewriter, value.getLoc(), value);
1429 assert(operands.size() >= 1 &&
"zero width cast must be rejected");
1431 if (operands.size() == 1) {
1432 rewriter.replaceOp(op, castToUIntIfSigned(operands[0]));
1436 if (operands.size() == cat->getNumOperands())
1440 if (hasSigned && hasUnsigned)
1441 for (
auto &operand : operands)
1442 operand = castToUIntIfSigned(operand);
1444 replaceOpWithNewOpAndCopyName<CatPrimOp>(rewriter, op, cat.getType(),
1451class CatOfConstant :
public mlir::RewritePattern {
1453 CatOfConstant(MLIRContext *context)
1454 : RewritePattern(CatPrimOp::getOperationName(), 0, context) {}
1457 matchAndRewrite(Operation *op,
1458 mlir::PatternRewriter &rewriter)
const override {
1459 auto cat = cast<CatPrimOp>(op);
1463 SmallVector<Value> operands;
1465 for (
size_t i = 0; i < cat->getNumOperands(); ++i) {
1466 auto cst = cat.getInputs()[i].getDefiningOp<ConstantOp>();
1468 operands.push_back(cat.getInputs()[i]);
1471 APSInt value = cst.getValue();
1473 for (; j < cat->getNumOperands(); ++j) {
1474 auto nextCst = cat.getInputs()[j].getDefiningOp<ConstantOp>();
1477 value = value.concat(nextCst.getValue());
1482 operands.push_back(cst);
1485 operands.push_back(ConstantOp::create(rewriter, cat.getLoc(), value));
1491 if (operands.size() == cat->getNumOperands())
1494 replaceOpWithNewOpAndCopyName<CatPrimOp>(rewriter, op, cat.getType(),
1503void CatPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1504 MLIRContext *context) {
1505 results.insert<patterns::CatBitsBits, patterns::CatDoubleConst,
1506 patterns::CatCast, FlattenCat, CatOfConstant>(context);
1509OpFoldResult BitCastOp::fold(FoldAdaptor adaptor) {
1512 if (op.getType() == op.getInput().getType())
1513 return op.getInput();
1517 if (BitCastOp in = dyn_cast_or_null<BitCastOp>(op.getInput().getDefiningOp()))
1518 if (op.getType() == in.getInput().getType())
1519 return in.getInput();
1524OpFoldResult BitsPrimOp::fold(FoldAdaptor adaptor) {
1525 IntType inputType = getInput().getType();
1526 IntType resultType = getType();
1528 if (inputType == getType() && resultType.
hasWidth())
1535 cst->extractBits(getHi() - getLo() + 1, getLo()));
1542 : RewritePattern(BitsPrimOp::getOperationName(), 0, context) {}
1546 mlir::PatternRewriter &rewriter)
const override {
1547 auto bits = cast<BitsPrimOp>(op);
1548 auto cat = bits.getInput().getDefiningOp<CatPrimOp>();
1551 int32_t bitPos = bits.getLo();
1552 auto resultWidth = type_cast<UIntType>(bits.getType()).getWidthOrSentinel();
1553 if (resultWidth < 0)
1555 for (
auto operand : llvm::reverse(cat.getInputs())) {
1557 type_cast<IntType>(operand.getType()).getWidthOrSentinel();
1558 if (operandWidth < 0)
1560 if (bitPos < operandWidth) {
1561 if (bitPos + resultWidth <= operandWidth) {
1562 auto newBits = rewriter.createOrFold<BitsPrimOp>(
1563 op->getLoc(), operand, bitPos + resultWidth - 1, bitPos);
1569 bitPos -= operandWidth;
1575void BitsPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1576 MLIRContext *context) {
1578 .insert<patterns::BitsOfBits, patterns::BitsOfMux, patterns::BitsOfAsUInt,
1579 patterns::BitsOfAnd, patterns::BitsOfPad,
BitsOfCat>(context);
1586 unsigned loBit, PatternRewriter &rewriter) {
1587 auto resType = type_cast<IntType>(op->getResult(0).getType());
1588 if (type_cast<IntType>(value.getType()).getWidth() != resType.getWidth())
1589 value = BitsPrimOp::create(rewriter, op->getLoc(), value, hiBit, loBit);
1591 if (resType.isSigned() && !type_cast<IntType>(value.getType()).isSigned()) {
1592 value = rewriter.createOrFold<AsSIntPrimOp>(op->getLoc(), resType, value);
1593 }
else if (resType.isUnsigned() &&
1594 !type_cast<IntType>(value.getType()).isUnsigned()) {
1595 value = rewriter.createOrFold<AsUIntPrimOp>(op->getLoc(), resType, value);
1597 rewriter.replaceOp(op, value);
1600template <
typename OpTy>
1601static OpFoldResult
foldMux(OpTy op,
typename OpTy::FoldAdaptor adaptor) {
1603 if (op.getType().getBitWidthOrSentinel() == 0)
1605 APInt(0, 0, op.getType().isSignedInteger()));
1608 if (op.getHigh() == op.getLow() && op.getHigh().getType() == op.getType())
1609 return op.getHigh();
1614 if (op.getType().getBitWidthOrSentinel() < 0)
1619 if (cond->isZero() && op.getLow().getType() == op.getType())
1621 if (!cond->isZero() && op.getHigh().getType() == op.getType())
1622 return op.getHigh();
1626 if (
auto lowCst =
getConstant(adaptor.getLow())) {
1628 if (
auto highCst =
getConstant(adaptor.getHigh())) {
1630 if (highCst->getBitWidth() == lowCst->getBitWidth() &&
1631 *highCst == *lowCst)
1634 if (highCst->isOne() && lowCst->isZero() &&
1635 op.getType() == op.getSel().getType())
1648OpFoldResult MuxPrimOp::fold(FoldAdaptor adaptor) {
1649 return foldMux(*
this, adaptor);
1652OpFoldResult Mux2CellIntrinsicOp::fold(FoldAdaptor adaptor) {
1653 return foldMux(*
this, adaptor);
1656OpFoldResult Mux4CellIntrinsicOp::fold(FoldAdaptor adaptor) {
return {}; }
1663class MuxPad :
public mlir::RewritePattern {
1665 MuxPad(MLIRContext *context)
1666 : RewritePattern(MuxPrimOp::getOperationName(), 0, context) {}
1669 matchAndRewrite(Operation *op,
1670 mlir::PatternRewriter &rewriter)
const override {
1671 auto mux = cast<MuxPrimOp>(op);
1672 auto width = mux.getType().getBitWidthOrSentinel();
1676 auto pad = [&](Value input) -> Value {
1678 type_cast<FIRRTLBaseType>(input.getType()).getBitWidthOrSentinel();
1679 if (inputWidth < 0 || width == inputWidth)
1681 return PadPrimOp::create(rewriter, mux.getLoc(), mux.getType(), input,
1686 auto newHigh = pad(mux.getHigh());
1687 auto newLow = pad(mux.getLow());
1688 if (newHigh == mux.getHigh() && newLow == mux.getLow())
1691 replaceOpWithNewOpAndCopyName<MuxPrimOp>(
1692 rewriter, op, mux.getType(), ValueRange{mux.getSel(), newHigh, newLow},
1700class MuxSharedCond :
public mlir::RewritePattern {
1702 MuxSharedCond(MLIRContext *context)
1703 : RewritePattern(MuxPrimOp::getOperationName(), 0, context) {}
1705 static const int depthLimit = 5;
1707 Value updateOrClone(MuxPrimOp mux, Value high, Value low,
1708 mlir::PatternRewriter &rewriter,
1709 bool updateInPlace)
const {
1710 if (updateInPlace) {
1711 rewriter.modifyOpInPlace(mux, [&] {
1712 mux.setOperand(1, high);
1713 mux.setOperand(2, low);
1717 rewriter.setInsertionPointAfter(mux);
1718 return MuxPrimOp::create(rewriter, mux.getLoc(), mux.getType(),
1719 ValueRange{mux.getSel(), high, low})
1724 Value tryCondTrue(Value op, Value cond, mlir::PatternRewriter &rewriter,
1725 bool updateInPlace,
int limit)
const {
1726 MuxPrimOp mux = op.getDefiningOp<MuxPrimOp>();
1729 if (mux.getSel() == cond)
1730 return mux.getHigh();
1731 if (limit > depthLimit)
1733 updateInPlace &= mux->hasOneUse();
1735 if (Value v = tryCondTrue(mux.getHigh(), cond, rewriter, updateInPlace,
1737 return updateOrClone(mux, v, mux.getLow(), rewriter, updateInPlace);
1740 tryCondTrue(mux.getLow(), cond, rewriter, updateInPlace, limit + 1))
1741 return updateOrClone(mux, mux.getHigh(), v, rewriter, updateInPlace);
1746 Value tryCondFalse(Value op, Value cond, mlir::PatternRewriter &rewriter,
1747 bool updateInPlace,
int limit)
const {
1748 MuxPrimOp mux = op.getDefiningOp<MuxPrimOp>();
1751 if (mux.getSel() == cond)
1752 return mux.getLow();
1753 if (limit > depthLimit)
1755 updateInPlace &= mux->hasOneUse();
1757 if (Value v = tryCondFalse(mux.getHigh(), cond, rewriter, updateInPlace,
1759 return updateOrClone(mux, v, mux.getLow(), rewriter, updateInPlace);
1761 if (Value v = tryCondFalse(mux.getLow(), cond, rewriter, updateInPlace,
1763 return updateOrClone(mux, mux.getHigh(), v, rewriter, updateInPlace);
1769 matchAndRewrite(Operation *op,
1770 mlir::PatternRewriter &rewriter)
const override {
1771 auto mux = cast<MuxPrimOp>(op);
1772 auto width = mux.getType().getBitWidthOrSentinel();
1776 if (Value v = tryCondTrue(mux.getHigh(), mux.getSel(), rewriter,
true, 0)) {
1777 rewriter.modifyOpInPlace(mux, [&] { mux.setOperand(1, v); });
1781 if (Value v = tryCondFalse(mux.getLow(), mux.getSel(), rewriter,
true, 0)) {
1782 rewriter.modifyOpInPlace(mux, [&] { mux.setOperand(2, v); });
1791void MuxPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1792 MLIRContext *context) {
1794 .add<MuxPad, MuxSharedCond, patterns::MuxEQOperands,
1795 patterns::MuxEQOperandsSwapped, patterns::MuxNEQ, patterns::MuxNot,
1796 patterns::MuxSameTrue, patterns::MuxSameFalse,
1797 patterns::NarrowMuxLHS, patterns::NarrowMuxRHS, patterns::MuxPadSel>(
1801void Mux2CellIntrinsicOp::getCanonicalizationPatterns(
1802 RewritePatternSet &results, MLIRContext *context) {
1803 results.add<patterns::Mux2PadSel>(context);
1806void Mux4CellIntrinsicOp::getCanonicalizationPatterns(
1807 RewritePatternSet &results, MLIRContext *context) {
1808 results.add<patterns::Mux4PadSel>(context);
1811OpFoldResult PadPrimOp::fold(FoldAdaptor adaptor) {
1812 auto input = this->getInput();
1815 if (input.getType() == getType())
1819 auto inputType = input.getType().base();
1826 auto destWidth = getType().base().getWidthOrSentinel();
1827 if (destWidth == -1)
1830 if (inputType.
isSigned() && cst->getBitWidth())
1831 return getIntAttr(getType(), cst->sext(destWidth));
1832 return getIntAttr(getType(), cst->zext(destWidth));
1838OpFoldResult ShlPrimOp::fold(FoldAdaptor adaptor) {
1839 auto input = this->getInput();
1840 IntType inputType = input.getType();
1841 int shiftAmount = getAmount();
1844 if (shiftAmount == 0)
1850 if (inputWidth != -1) {
1851 auto resultWidth = inputWidth + shiftAmount;
1852 shiftAmount = std::min(shiftAmount, resultWidth);
1853 return getIntAttr(getType(), cst->zext(resultWidth).shl(shiftAmount));
1859OpFoldResult ShrPrimOp::fold(FoldAdaptor adaptor) {
1860 auto input = this->getInput();
1861 IntType inputType = input.getType();
1862 int shiftAmount = getAmount();
1868 if (shiftAmount == 0 && inputWidth > 0)
1871 if (inputWidth == -1)
1873 if (inputWidth == 0)
1878 if (shiftAmount >= inputWidth && inputType.
isUnsigned())
1879 return getIntAttr(getType(), APInt(0, 0,
false));
1885 value = cst->ashr(std::min(shiftAmount, inputWidth - 1));
1887 value = cst->lshr(std::min(shiftAmount, inputWidth));
1888 auto resultWidth = std::max(inputWidth - shiftAmount, 1);
1889 return getIntAttr(getType(), value.trunc(resultWidth));
1894LogicalResult ShrPrimOp::canonicalize(ShrPrimOp op, PatternRewriter &rewriter) {
1895 auto inputWidth = op.getInput().getType().base().getWidthOrSentinel();
1896 if (inputWidth <= 0)
1900 unsigned shiftAmount = op.getAmount();
1901 if (
int(shiftAmount) >= inputWidth) {
1903 if (op.getType().base().isUnsigned())
1909 shiftAmount = inputWidth - 1;
1912 replaceWithBits(op, op.getInput(), inputWidth - 1, shiftAmount, rewriter);
1916LogicalResult HeadPrimOp::canonicalize(HeadPrimOp op,
1917 PatternRewriter &rewriter) {
1918 auto inputWidth = op.getInput().getType().base().getWidthOrSentinel();
1919 if (inputWidth <= 0)
1923 unsigned keepAmount = op.getAmount();
1925 replaceWithBits(op, op.getInput(), inputWidth - 1, inputWidth - keepAmount,
1930OpFoldResult HeadPrimOp::fold(FoldAdaptor adaptor) {
1934 getInput().getType().base().getWidthOrSentinel() - getAmount();
1935 return getIntAttr(getType(), cst->lshr(shiftAmount).trunc(getAmount()));
1941OpFoldResult TailPrimOp::fold(FoldAdaptor adaptor) {
1945 cst->trunc(getType().base().getWidthOrSentinel()));
1949LogicalResult TailPrimOp::canonicalize(TailPrimOp op,
1950 PatternRewriter &rewriter) {
1951 auto inputWidth = op.getInput().getType().base().getWidthOrSentinel();
1952 if (inputWidth <= 0)
1956 unsigned dropAmount = op.getAmount();
1957 if (dropAmount !=
unsigned(inputWidth))
1963void SubaccessOp::getCanonicalizationPatterns(RewritePatternSet &results,
1964 MLIRContext *context) {
1965 results.add<patterns::SubaccessOfConstant>(context);
1968OpFoldResult MultibitMuxOp::fold(FoldAdaptor adaptor) {
1970 if (adaptor.getInputs().size() == 1)
1971 return getOperand(1);
1973 if (
auto constIndex =
getConstant(adaptor.getIndex())) {
1974 auto index = constIndex->getZExtValue();
1975 if (index < getInputs().size())
1976 return getInputs()[getInputs().size() - 1 - index];
1982LogicalResult MultibitMuxOp::canonicalize(MultibitMuxOp op,
1983 PatternRewriter &rewriter) {
1987 if (llvm::all_of(op.getInputs().drop_front(), [&](
auto input) {
1988 return input == op.getInputs().front();
1996 auto indexWidth = op.getIndex().getType().getBitWidthOrSentinel();
1997 uint64_t inputSize = op.getInputs().size();
1998 if (indexWidth >= 0 && indexWidth < 64 && 1ull << indexWidth < inputSize) {
1999 rewriter.modifyOpInPlace(op, [&]() {
2000 op.getInputsMutable().erase(0, inputSize - (1ull << indexWidth));
2007 if (
auto lastSubindex = op.getInputs().back().getDefiningOp<SubindexOp>()) {
2008 if (llvm::all_of(llvm::enumerate(op.getInputs()), [&](
auto e) {
2009 auto subindex = e.value().template getDefiningOp<SubindexOp>();
2010 return subindex && lastSubindex.getInput() == subindex.getInput() &&
2011 subindex.getIndex() + e.index() + 1 == op.getInputs().size();
2013 replaceOpWithNewOpAndCopyName<SubaccessOp>(
2014 rewriter, op, lastSubindex.getInput(), op.getIndex());
2020 if (op.getInputs().size() != 2)
2024 auto uintType = op.getIndex().getType();
2025 if (uintType.getBitWidthOrSentinel() != 1)
2029 replaceOpWithNewOpAndCopyName<MuxPrimOp>(
2030 rewriter, op, op.getIndex(), op.getInputs()[0], op.getInputs()[1]);
2049 MatchingConnectOp connect;
2050 for (Operation *user : value.getUsers()) {
2052 if (isa<AttachOp, SubfieldOp, SubaccessOp, SubindexOp>(user))
2055 if (
auto aConnect = dyn_cast<FConnectLike>(user))
2056 if (aConnect.getDest() == value) {
2057 auto matchingConnect = dyn_cast<MatchingConnectOp>(*aConnect);
2060 if (!matchingConnect || (connect && connect != matchingConnect) ||
2061 matchingConnect->getBlock() != value.getParentBlock())
2063 connect = matchingConnect;
2071 PatternRewriter &rewriter) {
2074 Operation *connectedDecl = op.getDest().getDefiningOp();
2079 if (!isa<WireOp>(connectedDecl) && !isa<RegOp>(connectedDecl))
2083 cast<Forceable>(connectedDecl).isForceable())
2091 if (connectedDecl->hasOneUse())
2095 auto *declBlock = connectedDecl->getBlock();
2096 auto *srcValueOp = op.getSrc().getDefiningOp();
2099 if (!isa<WireOp>(connectedDecl))
2105 if (!isa<ConstantOp>(srcValueOp))
2107 if (srcValueOp->getBlock() != declBlock)
2113 auto replacement = op.getSrc();
2116 if (srcValueOp && srcValueOp != &declBlock->front())
2117 srcValueOp->moveBefore(&declBlock->front());
2124 rewriter.eraseOp(op);
2128void ConnectOp::getCanonicalizationPatterns(RewritePatternSet &results,
2129 MLIRContext *context) {
2130 results.insert<patterns::ConnectExtension, patterns::ConnectSameType>(
2134LogicalResult MatchingConnectOp::canonicalize(MatchingConnectOp op,
2135 PatternRewriter &rewriter) {
2152 for (
auto *user : value.getUsers()) {
2153 auto attach = dyn_cast<AttachOp>(user);
2154 if (!attach || attach == dominatedAttach)
2156 if (attach->isBeforeInBlock(dominatedAttach))
2162LogicalResult AttachOp::canonicalize(AttachOp op, PatternRewriter &rewriter) {
2164 if (op.getNumOperands() <= 1) {
2165 rewriter.eraseOp(op);
2169 for (
auto operand : op.getOperands()) {
2176 SmallVector<Value> newOperands(op.getOperands());
2177 for (
auto newOperand : attach.getOperands())
2178 if (newOperand != operand)
2179 newOperands.push_back(newOperand);
2180 AttachOp::create(rewriter, op->getLoc(), newOperands);
2181 rewriter.eraseOp(attach);
2182 rewriter.eraseOp(op);
2190 if (
auto wire = dyn_cast_or_null<WireOp>(operand.getDefiningOp())) {
2191 if (!
hasDontTouch(wire.getOperation()) && wire->hasOneUse() &&
2192 !wire.isForceable()) {
2193 SmallVector<Value> newOperands;
2194 for (
auto newOperand : op.getOperands())
2195 if (newOperand != operand)
2196 newOperands.push_back(newOperand);
2198 AttachOp::create(rewriter, op->getLoc(), newOperands);
2199 rewriter.eraseOp(op);
2200 rewriter.eraseOp(wire);
2211 assert(llvm::hasSingleElement(region) &&
"expected single-region block");
2212 rewriter.inlineBlockBefore(®ion.front(), op, {});
2215LogicalResult WhenOp::canonicalize(WhenOp op, PatternRewriter &rewriter) {
2216 if (
auto constant = op.getCondition().getDefiningOp<firrtl::ConstantOp>()) {
2217 if (constant.getValue().isAllOnes())
2219 else if (op.hasElseRegion() && !op.getElseRegion().empty())
2222 rewriter.eraseOp(op);
2228 if (!op.getThenBlock().empty() && op.hasElseRegion() &&
2229 op.getElseBlock().empty()) {
2230 rewriter.eraseBlock(&op.getElseBlock());
2237 if (!op.getThenBlock().empty())
2241 if (!op.hasElseRegion() || op.getElseBlock().empty()) {
2242 rewriter.eraseOp(op);
2251struct FoldNodeName :
public mlir::RewritePattern {
2252 FoldNodeName(MLIRContext *context)
2253 : RewritePattern(NodeOp::getOperationName(), 0, context) {}
2254 LogicalResult matchAndRewrite(Operation *op,
2255 PatternRewriter &rewriter)
const override {
2256 auto node = cast<NodeOp>(op);
2257 auto name = node.getNameAttr();
2258 if (!node.hasDroppableName() || node.getInnerSym() ||
2261 auto *newOp = node.getInput().getDefiningOp();
2264 rewriter.replaceOp(node, node.getInput());
2270struct NodeBypass :
public mlir::RewritePattern {
2271 NodeBypass(MLIRContext *context)
2272 : RewritePattern(NodeOp::getOperationName(), 0, context) {}
2273 LogicalResult matchAndRewrite(Operation *op,
2274 PatternRewriter &rewriter)
const override {
2275 auto node = cast<NodeOp>(op);
2277 node.use_empty() || node.isForceable())
2279 rewriter.replaceAllUsesWith(node.getResult(), node.getInput());
2286template <
typename OpTy>
2288 PatternRewriter &rewriter) {
2289 if (!op.isForceable() || !op.getDataRef().use_empty())
2297LogicalResult NodeOp::fold(FoldAdaptor adaptor,
2298 SmallVectorImpl<OpFoldResult> &results) {
2307 if (!adaptor.getInput())
2310 results.push_back(adaptor.getInput());
2314void NodeOp::getCanonicalizationPatterns(RewritePatternSet &results,
2315 MLIRContext *context) {
2316 results.insert<FoldNodeName>(context);
2317 results.add(demoteForceableIfUnused<NodeOp>);
2323struct AggOneShot :
public mlir::RewritePattern {
2324 AggOneShot(StringRef name, uint32_t weight, MLIRContext *context)
2325 : RewritePattern(name, 0, context) {}
2327 SmallVector<Value> getCompleteWrite(Operation *lhs)
const {
2328 auto lhsTy = lhs->getResult(0).getType();
2329 if (!type_isa<BundleType, FVectorType>(lhsTy))
2332 DenseMap<uint32_t, Value> fields;
2333 for (Operation *user : lhs->getResult(0).getUsers()) {
2334 if (user->getParentOp() != lhs->getParentOp())
2336 if (
auto aConnect = dyn_cast<MatchingConnectOp>(user)) {
2337 if (aConnect.getDest() == lhs->getResult(0))
2339 }
else if (
auto subField = dyn_cast<SubfieldOp>(user)) {
2340 for (Operation *subuser : subField.getResult().getUsers()) {
2341 if (
auto aConnect = dyn_cast<MatchingConnectOp>(subuser)) {
2342 if (aConnect.getDest() == subField) {
2343 if (subuser->getParentOp() != lhs->getParentOp())
2345 if (fields.count(subField.getFieldIndex()))
2347 fields[subField.getFieldIndex()] = aConnect.getSrc();
2353 }
else if (
auto subIndex = dyn_cast<SubindexOp>(user)) {
2354 for (Operation *subuser : subIndex.getResult().getUsers()) {
2355 if (
auto aConnect = dyn_cast<MatchingConnectOp>(subuser)) {
2356 if (aConnect.getDest() == subIndex) {
2357 if (subuser->getParentOp() != lhs->getParentOp())
2359 if (fields.count(subIndex.getIndex()))
2361 fields[subIndex.getIndex()] = aConnect.getSrc();
2372 SmallVector<Value> values;
2373 uint32_t total = type_isa<BundleType>(lhsTy)
2374 ? type_cast<BundleType>(lhsTy).getNumElements()
2375 : type_cast<FVectorType>(lhsTy).getNumElements();
2376 for (uint32_t i = 0; i < total; ++i) {
2377 if (!fields.count(i))
2379 values.push_back(fields[i]);
2384 LogicalResult matchAndRewrite(Operation *op,
2385 PatternRewriter &rewriter)
const override {
2386 auto values = getCompleteWrite(op);
2389 rewriter.setInsertionPointToEnd(op->getBlock());
2390 auto dest = op->getResult(0);
2391 auto destType = dest.getType();
2394 if (!type_cast<FIRRTLBaseType>(destType).isPassive())
2397 Value newVal = type_isa<BundleType>(destType)
2398 ? rewriter.createOrFold<BundleCreateOp>(op->getLoc(),
2400 : rewriter.createOrFold<VectorCreateOp>(
2401 op->
getLoc(), destType, values);
2402 rewriter.createOrFold<MatchingConnectOp>(op->getLoc(), dest, newVal);
2403 for (Operation *user : dest.getUsers()) {
2404 if (
auto subIndex = dyn_cast<SubindexOp>(user)) {
2405 for (Operation *subuser :
2406 llvm::make_early_inc_range(subIndex.getResult().getUsers()))
2407 if (auto aConnect = dyn_cast<MatchingConnectOp>(subuser))
2408 if (aConnect.getDest() == subIndex)
2409 rewriter.eraseOp(aConnect);
2410 }
else if (
auto subField = dyn_cast<SubfieldOp>(user)) {
2411 for (Operation *subuser :
2412 llvm::make_early_inc_range(subField.getResult().getUsers()))
2413 if (auto aConnect = dyn_cast<MatchingConnectOp>(subuser))
2414 if (aConnect.getDest() == subField)
2415 rewriter.eraseOp(aConnect);
2422struct WireAggOneShot :
public AggOneShot {
2423 WireAggOneShot(MLIRContext *context)
2424 : AggOneShot(WireOp::getOperationName(), 0, context) {}
2426struct SubindexAggOneShot :
public AggOneShot {
2427 SubindexAggOneShot(MLIRContext *context)
2428 : AggOneShot(SubindexOp::getOperationName(), 0, context) {}
2430struct SubfieldAggOneShot :
public AggOneShot {
2431 SubfieldAggOneShot(MLIRContext *context)
2432 : AggOneShot(SubfieldOp::getOperationName(), 0, context) {}
2436void WireOp::getCanonicalizationPatterns(RewritePatternSet &results,
2437 MLIRContext *context) {
2438 results.insert<WireAggOneShot>(context);
2439 results.add(demoteForceableIfUnused<WireOp>);
2442void SubindexOp::getCanonicalizationPatterns(RewritePatternSet &results,
2443 MLIRContext *context) {
2444 results.insert<SubindexAggOneShot>(context);
2447OpFoldResult SubindexOp::fold(FoldAdaptor adaptor) {
2448 auto attr = dyn_cast_or_null<ArrayAttr>(adaptor.getInput());
2451 return attr[getIndex()];
2454OpFoldResult SubfieldOp::fold(FoldAdaptor adaptor) {
2455 auto attr = dyn_cast_or_null<ArrayAttr>(adaptor.getInput());
2458 auto index = getFieldIndex();
2462void SubfieldOp::getCanonicalizationPatterns(RewritePatternSet &results,
2463 MLIRContext *context) {
2464 results.insert<SubfieldAggOneShot>(context);
2468 ArrayRef<Attribute> operands) {
2469 for (
auto operand : operands)
2472 return ArrayAttr::get(context, operands);
2475OpFoldResult BundleCreateOp::fold(FoldAdaptor adaptor) {
2478 if (getNumOperands() > 0)
2479 if (SubfieldOp first = getOperand(0).getDefiningOp<SubfieldOp>())
2480 if (first.getFieldIndex() == 0 &&
2481 first.getInput().getType() == getType() &&
2483 llvm::drop_begin(llvm::enumerate(getOperands())), [&](
auto elem) {
2485 elem.value().
template getDefiningOp<SubfieldOp>();
2486 return subindex && subindex.getInput() == first.getInput() &&
2487 subindex.getFieldIndex() == elem.index();
2489 return first.getInput();
2494OpFoldResult VectorCreateOp::fold(FoldAdaptor adaptor) {
2497 if (getNumOperands() > 0)
2498 if (SubindexOp first = getOperand(0).getDefiningOp<SubindexOp>())
2499 if (first.getIndex() == 0 && first.getInput().getType() == getType() &&
2501 llvm::drop_begin(llvm::enumerate(getOperands())), [&](
auto elem) {
2503 elem.value().
template getDefiningOp<SubindexOp>();
2504 return subindex && subindex.getInput() == first.getInput() &&
2505 subindex.getIndex() == elem.index();
2507 return first.getInput();
2512OpFoldResult UninferredResetCastOp::fold(FoldAdaptor adaptor) {
2513 if (getOperand().getType() == getType())
2514 return getOperand();
2521struct FoldResetMux :
public mlir::RewritePattern {
2522 FoldResetMux(MLIRContext *context)
2523 : RewritePattern(RegResetOp::getOperationName(), 0, context) {}
2524 LogicalResult matchAndRewrite(Operation *op,
2525 PatternRewriter &rewriter)
const override {
2526 auto reg = cast<RegResetOp>(op);
2528 dyn_cast_or_null<ConstantOp>(
reg.getResetValue().getDefiningOp());
2537 auto mux = dyn_cast_or_null<MuxPrimOp>(con.getSrc().getDefiningOp());
2540 auto *high = mux.getHigh().getDefiningOp();
2541 auto *low = mux.getLow().getDefiningOp();
2542 auto constOp = dyn_cast_or_null<ConstantOp>(high);
2544 if (constOp && low != reg)
2546 if (dyn_cast_or_null<ConstantOp>(low) && high == reg)
2547 constOp = dyn_cast<ConstantOp>(low);
2549 if (!constOp || constOp.getType() != reset.getType() ||
2550 constOp.getValue() != reset.getValue())
2554 auto regTy =
reg.getResult().getType();
2555 if (con.getDest().getType() != regTy || con.getSrc().getType() != regTy ||
2556 mux.getHigh().getType() != regTy || mux.getLow().getType() != regTy ||
2557 regTy.getBitWidthOrSentinel() < 0)
2563 if (constOp != &con->getBlock()->front())
2564 constOp->moveBefore(&con->getBlock()->front());
2569 rewriter.eraseOp(con);
2576 if (
auto c = v.getDefiningOp<ConstantOp>())
2577 return c.getValue().isOne();
2578 if (
auto sc = v.getDefiningOp<SpecialConstantOp>())
2579 return sc.getValue();
2588 auto resetValue = reg.getResetValue();
2589 if (reg.getType(0) != resetValue.getType())
2593 (void)
dropWrite(rewriter, reg->getResult(0), {});
2594 replaceOpWithNewOpAndCopyName<NodeOp>(
2595 rewriter, reg, resetValue, reg.getNameAttr(), reg.getNameKind(),
2596 reg.getAnnotationsAttr(), reg.getInnerSymAttr(), reg.getForceable());
2600void RegResetOp::getCanonicalizationPatterns(RewritePatternSet &results,
2601 MLIRContext *context) {
2602 results.add<patterns::RegResetWithZeroReset, FoldResetMux>(context);
2604 results.add(demoteForceableIfUnused<RegResetOp>);
2609 auto portTy = type_cast<BundleType>(port.getType());
2610 auto fieldIndex = portTy.getElementIndex(name);
2611 assert(fieldIndex &&
"missing field on memory port");
2614 for (
auto *op : port.getUsers()) {
2615 auto portAccess = cast<SubfieldOp>(op);
2616 if (fieldIndex != portAccess.getFieldIndex())
2621 value = conn.getSrc();
2631 auto portConst = value.getDefiningOp<ConstantOp>();
2634 return portConst.getValue().isZero();
2639 auto portTy = type_cast<BundleType>(port.getType());
2640 auto fieldIndex = portTy.getElementIndex(
data);
2641 assert(fieldIndex &&
"missing enable flag on memory port");
2643 for (
auto *op : port.getUsers()) {
2644 auto portAccess = cast<SubfieldOp>(op);
2645 if (fieldIndex != portAccess.getFieldIndex())
2647 if (!portAccess.use_empty())
2656 StringRef name, Value value) {
2657 auto portTy = type_cast<BundleType>(port.getType());
2658 auto fieldIndex = portTy.getElementIndex(name);
2659 assert(fieldIndex &&
"missing field on memory port");
2661 for (
auto *op : llvm::make_early_inc_range(port.getUsers())) {
2662 auto portAccess = cast<SubfieldOp>(op);
2663 if (fieldIndex != portAccess.getFieldIndex())
2665 rewriter.replaceAllUsesWith(portAccess, value);
2666 rewriter.eraseOp(portAccess);
2671static void erasePort(PatternRewriter &rewriter, Value port) {
2674 auto getClock = [&] {
2676 clock = SpecialConstantOp::create(rewriter, port.getLoc(),
2677 ClockType::get(rewriter.getContext()),
2686 for (
auto *op : port.getUsers()) {
2687 auto subfield = dyn_cast<SubfieldOp>(op);
2689 auto ty = port.getType();
2690 auto reg = RegOp::create(rewriter, port.getLoc(), ty, getClock());
2691 rewriter.replaceAllUsesWith(port, reg.getResult());
2700 for (
auto *accessOp : llvm::make_early_inc_range(port.getUsers())) {
2701 auto access = cast<SubfieldOp>(accessOp);
2702 for (
auto *user : llvm::make_early_inc_range(access->getUsers())) {
2703 auto connect = dyn_cast<FConnectLike>(user);
2704 if (connect && connect.getDest() == access) {
2705 rewriter.eraseOp(user);
2709 if (access.use_empty()) {
2710 rewriter.eraseOp(access);
2716 auto ty = access.getType();
2717 auto reg = RegOp::create(rewriter, access.getLoc(), ty, getClock());
2718 rewriter.replaceOp(access, reg.getResult());
2720 assert(port.use_empty() &&
"port should have no remaining uses");
2725struct FoldZeroWidthMemory :
public mlir::RewritePattern {
2726 FoldZeroWidthMemory(MLIRContext *context)
2727 : RewritePattern(MemOp::getOperationName(), 0, context) {}
2728 LogicalResult matchAndRewrite(Operation *op,
2729 PatternRewriter &rewriter)
const override {
2730 MemOp mem = cast<MemOp>(op);
2734 if (!firrtl::type_isa<IntType>(mem.getDataType()) ||
2735 mem.getDataType().getBitWidthOrSentinel() != 0)
2739 for (
auto port : mem.getResults())
2740 for (auto *user : port.getUsers())
2741 if (!isa<SubfieldOp>(user))
2746 for (
auto port : op->getResults()) {
2747 for (
auto *user :
llvm::make_early_inc_range(port.getUsers())) {
2748 SubfieldOp sfop = cast<SubfieldOp>(user);
2749 StringRef fieldName = sfop.getFieldName();
2750 auto wire = replaceOpWithNewOpAndCopyName<WireOp>(
2751 rewriter, sfop, sfop.getResult().getType())
2753 if (fieldName.ends_with(
"data")) {
2755 auto zero = firrtl::ConstantOp::create(
2756 rewriter, wire.getLoc(),
2757 firrtl::type_cast<IntType>(wire.getType()), APInt::getZero(0));
2758 MatchingConnectOp::create(rewriter, wire.getLoc(), wire, zero);
2762 rewriter.eraseOp(op);
2768struct FoldReadOrWriteOnlyMemory :
public mlir::RewritePattern {
2769 FoldReadOrWriteOnlyMemory(MLIRContext *context)
2770 : RewritePattern(MemOp::getOperationName(), 0, context) {}
2771 LogicalResult matchAndRewrite(Operation *op,
2772 PatternRewriter &rewriter)
const override {
2773 MemOp mem = cast<MemOp>(op);
2776 bool isRead =
false, isWritten =
false;
2777 for (
unsigned i = 0; i < mem.getNumResults(); ++i) {
2778 switch (mem.getPortKind(i)) {
2779 case MemOp::PortKind::Read:
2784 case MemOp::PortKind::Write:
2789 case MemOp::PortKind::Debug:
2790 case MemOp::PortKind::ReadWrite:
2793 llvm_unreachable(
"unknown port kind");
2795 assert((!isWritten || !isRead) &&
"memory is in use");
2800 if (isRead && mem.getInit())
2803 for (
auto port : mem.getResults())
2806 rewriter.eraseOp(op);
2812struct FoldUnusedPorts :
public mlir::RewritePattern {
2813 FoldUnusedPorts(MLIRContext *context)
2814 : RewritePattern(MemOp::getOperationName(), 0, context) {}
2815 LogicalResult matchAndRewrite(Operation *op,
2816 PatternRewriter &rewriter)
const override {
2817 MemOp mem = cast<MemOp>(op);
2821 llvm::SmallBitVector deadPorts(mem.getNumResults());
2822 for (
auto [i, port] :
llvm::enumerate(mem.getResults())) {
2824 if (!mem.getPortAnnotation(i).empty())
2828 auto kind = mem.getPortKind(i);
2829 if (kind == MemOp::PortKind::Debug)
2838 if (kind == MemOp::PortKind::Read &&
isPortUnused(port,
"data")) {
2843 if (deadPorts.none())
2847 SmallVector<Type> resultTypes;
2848 SmallVector<StringRef> portNames;
2849 SmallVector<Attribute> portAnnotations;
2850 for (
auto [i, port] :
llvm::enumerate(mem.getResults())) {
2853 resultTypes.push_back(port.getType());
2854 portNames.push_back(mem.getPortName(i));
2855 portAnnotations.push_back(mem.getPortAnnotation(i));
2859 if (!resultTypes.empty())
2860 newOp = MemOp::create(
2861 rewriter, mem.getLoc(), resultTypes, mem.getReadLatency(),
2862 mem.getWriteLatency(), mem.getDepth(), mem.getRuw(),
2863 rewriter.getStrArrayAttr(portNames), mem.getName(), mem.getNameKind(),
2864 mem.getAnnotations(), rewriter.getArrayAttr(portAnnotations),
2865 mem.getInnerSymAttr(), mem.getInitAttr(), mem.getPrefixAttr());
2868 unsigned nextPort = 0;
2869 for (
auto [i, port] :
llvm::enumerate(mem.getResults())) {
2873 rewriter.replaceAllUsesWith(port, newOp.getResult(nextPort++));
2876 rewriter.eraseOp(op);
2882struct FoldReadWritePorts :
public mlir::RewritePattern {
2883 FoldReadWritePorts(MLIRContext *context)
2884 : RewritePattern(MemOp::getOperationName(), 0, context) {}
2885 LogicalResult matchAndRewrite(Operation *op,
2886 PatternRewriter &rewriter)
const override {
2887 MemOp mem = cast<MemOp>(op);
2892 llvm::SmallBitVector deadReads(mem.getNumResults());
2893 for (
auto [i, port] :
llvm::enumerate(mem.getResults())) {
2894 if (mem.getPortKind(i) != MemOp::PortKind::ReadWrite)
2896 if (!mem.getPortAnnotation(i).empty())
2903 if (deadReads.none())
2906 SmallVector<Type> resultTypes;
2907 SmallVector<StringRef> portNames;
2908 SmallVector<Attribute> portAnnotations;
2909 for (
auto [i, port] :
llvm::enumerate(mem.getResults())) {
2911 resultTypes.push_back(
2912 MemOp::getTypeForPort(mem.getDepth(), mem.getDataType(),
2913 MemOp::PortKind::Write, mem.getMaskBits()));
2915 resultTypes.push_back(port.getType());
2917 portNames.push_back(mem.getPortName(i));
2918 portAnnotations.push_back(mem.getPortAnnotation(i));
2921 auto newOp = MemOp::create(
2922 rewriter, mem.getLoc(), resultTypes, mem.getReadLatency(),
2923 mem.getWriteLatency(), mem.getDepth(), mem.getRuw(),
2924 rewriter.getStrArrayAttr(portNames), mem.getName(), mem.getNameKind(),
2925 mem.getAnnotations(), rewriter.getArrayAttr(portAnnotations),
2926 mem.getInnerSymAttr(), mem.getInitAttr(), mem.getPrefixAttr());
2928 for (
unsigned i = 0, n = mem.getNumResults(); i < n; ++i) {
2929 auto result = mem.getResult(i);
2930 auto newResult = newOp.getResult(i);
2932 auto resultPortTy = type_cast<BundleType>(result.getType());
2936 auto replace = [&](StringRef toName, StringRef fromName) {
2937 auto fromFieldIndex = resultPortTy.getElementIndex(fromName);
2938 assert(fromFieldIndex &&
"missing enable flag on memory port");
2940 auto toField = SubfieldOp::create(rewriter, newResult.getLoc(),
2942 for (
auto *op :
llvm::make_early_inc_range(result.getUsers())) {
2943 auto fromField = cast<SubfieldOp>(op);
2944 if (fromFieldIndex != fromField.getFieldIndex())
2946 rewriter.replaceOp(fromField, toField.getResult());
2950 replace(
"addr",
"addr");
2951 replace(
"en",
"en");
2952 replace(
"clk",
"clk");
2953 replace(
"data",
"wdata");
2954 replace(
"mask",
"wmask");
2957 auto wmodeFieldIndex = resultPortTy.getElementIndex(
"wmode");
2958 for (
auto *op :
llvm::make_early_inc_range(result.getUsers())) {
2959 auto wmodeField = cast<SubfieldOp>(op);
2960 if (wmodeFieldIndex != wmodeField.getFieldIndex())
2962 rewriter.replaceOpWithNewOp<WireOp>(wmodeField, wmodeField.getType());
2965 rewriter.replaceAllUsesWith(result, newResult);
2968 rewriter.eraseOp(op);
2974struct FoldUnusedBits :
public mlir::RewritePattern {
2975 FoldUnusedBits(MLIRContext *context)
2976 : RewritePattern(MemOp::getOperationName(), 0, context) {}
2978 LogicalResult matchAndRewrite(Operation *op,
2979 PatternRewriter &rewriter)
const override {
2980 MemOp mem = cast<MemOp>(op);
2985 const auto &summary = mem.getSummary();
2986 if (summary.isMasked || summary.isSeqMem())
2989 auto type = type_dyn_cast<IntType>(mem.getDataType());
2992 auto width = type.getBitWidthOrSentinel();
2996 llvm::SmallBitVector usedBits(width);
2997 DenseMap<unsigned, unsigned> mapping;
3002 SmallVector<BitsPrimOp> readOps;
3003 auto findReadUsers = [&](Value port, StringRef field) -> LogicalResult {
3004 auto portTy = type_cast<BundleType>(port.getType());
3005 auto fieldIndex = portTy.getElementIndex(field);
3006 assert(fieldIndex &&
"missing data port");
3008 for (
auto *op : port.getUsers()) {
3009 auto portAccess = cast<SubfieldOp>(op);
3010 if (fieldIndex != portAccess.getFieldIndex())
3013 for (
auto *user : op->getUsers()) {
3014 auto bits = dyn_cast<BitsPrimOp>(user);
3018 usedBits.set(bits.getLo(), bits.getHi() + 1);
3022 mapping[bits.getLo()] = 0;
3023 readOps.push_back(bits);
3033 SmallVector<MatchingConnectOp> writeOps;
3034 auto findWriteUsers = [&](Value port, StringRef field) -> LogicalResult {
3035 auto portTy = type_cast<BundleType>(port.getType());
3036 auto fieldIndex = portTy.getElementIndex(field);
3037 assert(fieldIndex &&
"missing data port");
3039 for (
auto *op : port.getUsers()) {
3040 auto portAccess = cast<SubfieldOp>(op);
3041 if (fieldIndex != portAccess.getFieldIndex())
3048 writeOps.push_back(conn);
3054 for (
auto [i, port] :
llvm::enumerate(mem.getResults())) {
3056 if (!mem.getPortAnnotation(i).empty())
3059 switch (mem.getPortKind(i)) {
3060 case MemOp::PortKind::Debug:
3063 case MemOp::PortKind::Write:
3064 if (failed(findWriteUsers(port,
"data")))
3067 case MemOp::PortKind::Read:
3068 if (failed(findReadUsers(port,
"data")))
3071 case MemOp::PortKind::ReadWrite:
3072 if (failed(findWriteUsers(port,
"wdata")))
3074 if (failed(findReadUsers(port,
"rdata")))
3078 llvm_unreachable(
"unknown port kind");
3082 if (usedBits.none())
3086 SmallVector<std::pair<unsigned, unsigned>> ranges;
3087 unsigned newWidth = 0;
3088 for (
int i = usedBits.find_first(); 0 <= i && i < width;) {
3089 int e = usedBits.find_next_unset(i);
3092 for (
int idx = i; idx < e; ++idx, ++newWidth) {
3093 if (
auto it = mapping.find(idx); it != mapping.end()) {
3094 it->second = newWidth;
3097 ranges.emplace_back(i, e - 1);
3098 i = e != width ? usedBits.find_next(e) : e;
3102 auto newType =
IntType::get(op->getContext(), type.isSigned(), newWidth);
3103 SmallVector<Type> portTypes;
3104 for (
auto [i, port] :
llvm::enumerate(mem.getResults())) {
3105 portTypes.push_back(
3106 MemOp::getTypeForPort(mem.getDepth(), newType, mem.getPortKind(i)));
3108 auto newMem = rewriter.replaceOpWithNewOp<MemOp>(
3109 mem, portTypes, mem.getReadLatency(), mem.getWriteLatency(),
3110 mem.getDepth(), mem.getRuw(), mem.getPortNames(), mem.getName(),
3111 mem.getNameKind(), mem.getAnnotations(), mem.getPortAnnotations(),
3112 mem.getInnerSymAttr(), mem.getInitAttr(), mem.getPrefixAttr());
3115 auto rewriteSubfield = [&](Value port, StringRef field) {
3116 auto portTy = type_cast<BundleType>(port.getType());
3117 auto fieldIndex = portTy.getElementIndex(field);
3118 assert(fieldIndex &&
"missing data port");
3120 rewriter.setInsertionPointAfter(newMem);
3121 auto newPortAccess =
3122 SubfieldOp::create(rewriter, port.getLoc(), port, field);
3124 for (
auto *op :
llvm::make_early_inc_range(port.getUsers())) {
3125 auto portAccess = cast<SubfieldOp>(op);
3126 if (op == newPortAccess || fieldIndex != portAccess.getFieldIndex())
3128 rewriter.replaceOp(portAccess, newPortAccess.getResult());
3133 for (
auto [i, port] :
llvm::enumerate(newMem.getResults())) {
3134 switch (newMem.getPortKind(i)) {
3135 case MemOp::PortKind::Debug:
3136 llvm_unreachable(
"cannot rewrite debug port");
3137 case MemOp::PortKind::Write:
3138 rewriteSubfield(port,
"data");
3140 case MemOp::PortKind::Read:
3141 rewriteSubfield(port,
"data");
3143 case MemOp::PortKind::ReadWrite:
3144 rewriteSubfield(port,
"rdata");
3145 rewriteSubfield(port,
"wdata");
3148 llvm_unreachable(
"unknown port kind");
3152 for (
auto readOp : readOps) {
3153 rewriter.setInsertionPointAfter(readOp);
3154 auto it = mapping.find(readOp.getLo());
3155 assert(it != mapping.end() &&
"bit op mapping not found");
3158 auto newReadValue = rewriter.createOrFold<BitsPrimOp>(
3159 readOp.getLoc(), readOp.getInput(),
3160 readOp.getHi() - readOp.getLo() + it->second, it->second);
3161 rewriter.replaceAllUsesWith(readOp, newReadValue);
3162 rewriter.eraseOp(readOp);
3166 for (
auto writeOp : writeOps) {
3167 Value source = writeOp.getSrc();
3168 rewriter.setInsertionPoint(writeOp);
3170 SmallVector<Value> slices;
3171 for (
auto &[start, end] :
llvm::reverse(ranges)) {
3172 Value slice = rewriter.createOrFold<BitsPrimOp>(writeOp.getLoc(),
3173 source,
end, start);
3174 slices.push_back(slice);
3178 rewriter.createOrFold<CatPrimOp>(writeOp.getLoc(), slices);
3184 if (type.isSigned())
3186 rewriter.createOrFold<AsSIntPrimOp>(writeOp.getLoc(), catOfSlices);
3188 rewriter.replaceOpWithNewOp<MatchingConnectOp>(writeOp, writeOp.getDest(),
3197struct FoldRegMems :
public mlir::RewritePattern {
3198 FoldRegMems(MLIRContext *context)
3199 : RewritePattern(MemOp::getOperationName(), 0, context) {}
3200 LogicalResult matchAndRewrite(Operation *op,
3201 PatternRewriter &rewriter)
const override {
3202 MemOp mem = cast<MemOp>(op);
3207 auto ty = mem.getDataType();
3208 auto loc = mem.getLoc();
3209 auto *block = mem->getBlock();
3213 SmallPtrSet<Operation *, 8> connects;
3214 SmallVector<SubfieldOp> portAccesses;
3215 for (
auto [i, port] :
llvm::enumerate(mem.getResults())) {
3216 if (!mem.getPortAnnotation(i).empty())
3219 auto collect = [&, port = port](ArrayRef<StringRef> fields) {
3220 auto portTy = type_cast<BundleType>(port.getType());
3221 for (
auto field : fields) {
3222 auto fieldIndex = portTy.getElementIndex(field);
3223 assert(fieldIndex &&
"missing field on memory port");
3225 for (
auto *op : port.getUsers()) {
3226 auto portAccess = cast<SubfieldOp>(op);
3227 if (fieldIndex != portAccess.getFieldIndex())
3229 portAccesses.push_back(portAccess);
3230 for (
auto *user : portAccess->getUsers()) {
3231 auto conn = dyn_cast<FConnectLike>(user);
3234 connects.insert(conn);
3241 switch (mem.getPortKind(i)) {
3242 case MemOp::PortKind::Debug:
3244 case MemOp::PortKind::Read:
3245 if (failed(collect({
"clk",
"en",
"addr"})))
3248 case MemOp::PortKind::Write:
3249 if (failed(collect({
"clk",
"en",
"addr",
"data",
"mask"})))
3252 case MemOp::PortKind::ReadWrite:
3253 if (failed(collect({
"clk",
"en",
"addr",
"wmode",
"wdata",
"wmask"})))
3259 if (!portClock || (clock && portClock != clock))
3265 rewriter.setInsertionPointAfter(mem);
3266 auto memWire = WireOp::create(rewriter, loc, ty).getResult();
3272 rewriter.setInsertionPointToEnd(block);
3274 RegOp::create(rewriter, loc, ty, clock, mem.getName()).getResult();
3277 MatchingConnectOp::create(rewriter, loc, memWire, memReg);
3281 auto pipeline = [&](Value value, Value clock,
const Twine &name,
3283 for (
unsigned i = 0; i < latency; ++i) {
3284 std::string regName;
3286 llvm::raw_string_ostream os(regName);
3287 os << mem.getName() <<
"_" << name <<
"_" << i;
3289 auto reg = RegOp::create(rewriter, mem.getLoc(), value.getType(), clock,
3290 rewriter.getStringAttr(regName))
3292 MatchingConnectOp::create(rewriter, value.getLoc(), reg, value);
3298 const unsigned writeStages =
info.writeLatency - 1;
3303 SmallVector<std::tuple<Value, Value, Value>> writes;
3304 for (
auto [i, port] :
llvm::enumerate(mem.getResults())) {
3306 StringRef name = mem.getPortName(i);
3308 auto portPipeline = [&, port = port](StringRef field,
unsigned stages) {
3311 return pipeline(value, portClock, name +
"_" + field, stages);
3314 switch (mem.getPortKind(i)) {
3315 case MemOp::PortKind::Debug:
3316 llvm_unreachable(
"unknown port kind");
3317 case MemOp::PortKind::Read: {
3325 case MemOp::PortKind::Write: {
3326 auto data = portPipeline(
"data", writeStages);
3327 auto en = portPipeline(
"en", writeStages);
3328 auto mask = portPipeline(
"mask", writeStages);
3332 case MemOp::PortKind::ReadWrite: {
3337 auto wdata = portPipeline(
"wdata", writeStages);
3338 auto wmask = portPipeline(
"wmask", writeStages);
3343 auto wen = AndPrimOp::create(rewriter, port.getLoc(),
en,
wmode);
3345 pipeline(wen, portClock, name +
"_wen", writeStages);
3346 writes.emplace_back(
wdata, wenPipelined,
wmask);
3353 Value next = memReg;
3359 Location loc = mem.getLoc();
3360 unsigned maskGran =
info.dataWidth /
info.maskBits;
3361 SmallVector<Value> chunks;
3362 for (
unsigned i = 0; i <
info.maskBits; ++i) {
3363 unsigned hi = (i + 1) * maskGran - 1;
3364 unsigned lo = i * maskGran;
3366 auto dataPart = rewriter.createOrFold<BitsPrimOp>(loc,
data, hi, lo);
3367 auto nextPart = rewriter.createOrFold<BitsPrimOp>(loc, next, hi, lo);
3368 auto bit = rewriter.createOrFold<BitsPrimOp>(loc,
mask, i, i);
3369 auto chunk = MuxPrimOp::create(rewriter, loc, bit, dataPart, nextPart);
3370 chunks.push_back(chunk);
3373 std::reverse(chunks.begin(), chunks.end());
3374 masked = rewriter.createOrFold<CatPrimOp>(loc, chunks);
3375 next = MuxPrimOp::create(rewriter, next.getLoc(),
en, masked, next);
3377 Value typedNext = rewriter.createOrFold<BitCastOp>(next.getLoc(), ty, next);
3378 MatchingConnectOp::create(rewriter, memReg.getLoc(), memReg, typedNext);
3381 for (Operation *conn : connects)
3382 rewriter.eraseOp(conn);
3383 for (
auto portAccess : portAccesses)
3384 rewriter.eraseOp(portAccess);
3385 rewriter.eraseOp(mem);
3392void MemOp::getCanonicalizationPatterns(RewritePatternSet &results,
3393 MLIRContext *context) {
3395 .insert<FoldZeroWidthMemory, FoldReadOrWriteOnlyMemory,
3396 FoldReadWritePorts, FoldUnusedPorts, FoldUnusedBits, FoldRegMems>(
3416 auto mux = dyn_cast_or_null<MuxPrimOp>(con.getSrc().getDefiningOp());
3419 auto *high = mux.getHigh().getDefiningOp();
3420 auto *low = mux.getLow().getDefiningOp();
3422 auto constOp = dyn_cast_or_null<ConstantOp>(high);
3429 bool constReg =
false;
3431 if (constOp && low == reg)
3433 else if (dyn_cast_or_null<ConstantOp>(low) && high == reg) {
3435 constOp = dyn_cast<ConstantOp>(low);
3442 if (!isa<BlockArgument>(mux.getSel()) && !constReg)
3446 auto regTy = reg.getResult().getType();
3447 if (con.getDest().getType() != regTy || con.getSrc().getType() != regTy ||
3448 mux.getHigh().getType() != regTy || mux.getLow().getType() != regTy ||
3449 regTy.getBitWidthOrSentinel() < 0)
3455 if (constOp != &con->getBlock()->front())
3456 constOp->moveBefore(&con->getBlock()->front());
3459 SmallVector<NamedAttribute, 2> attrs(reg->getDialectAttrs());
3460 auto newReg = replaceOpWithNewOpAndCopyName<RegResetOp>(
3461 rewriter, reg, reg.getResult().getType(), reg.getClockVal(),
3462 mux.getSel(), mux.getHigh(), reg.getNameAttr(), reg.getNameKindAttr(),
3463 reg.getAnnotationsAttr(), reg.getInnerSymAttr(),
3464 reg.getForceableAttr());
3465 newReg->setDialectAttrs(attrs);
3467 auto pt = rewriter.saveInsertionPoint();
3468 rewriter.setInsertionPoint(con);
3469 auto v = constReg ? (Value)constOp.getResult() : (Value)mux.getLow();
3470 replaceOpWithNewOpAndCopyName<ConnectOp>(rewriter, con, con.getDest(), v);
3471 rewriter.restoreInsertionPoint(pt);
3475LogicalResult RegOp::canonicalize(RegOp op, PatternRewriter &rewriter) {
3476 if (!
hasDontTouch(op.getOperation()) && !op.isForceable() &&
3492 PatternRewriter &rewriter,
3495 if (
auto constant = enable.getDefiningOp<firrtl::ConstantOp>()) {
3496 if (constant.getValue().isZero()) {
3497 rewriter.eraseOp(op);
3503 if (
auto constant = predicate.getDefiningOp<firrtl::ConstantOp>()) {
3504 if (constant.getValue().isZero() == eraseIfZero) {
3505 rewriter.eraseOp(op);
3513template <
class Op,
bool EraseIfZero = false>
3515 PatternRewriter &rewriter) {
3520void AssertOp::getCanonicalizationPatterns(RewritePatternSet &results,
3521 MLIRContext *context) {
3522 results.add(canonicalizeImmediateVerifOp<AssertOp>);
3523 results.add<patterns::AssertXWhenX>(context);
3526void AssumeOp::getCanonicalizationPatterns(RewritePatternSet &results,
3527 MLIRContext *context) {
3528 results.add(canonicalizeImmediateVerifOp<AssumeOp>);
3529 results.add<patterns::AssumeXWhenX>(context);
3532void UnclockedAssumeIntrinsicOp::getCanonicalizationPatterns(
3533 RewritePatternSet &results, MLIRContext *context) {
3534 results.add(canonicalizeImmediateVerifOp<UnclockedAssumeIntrinsicOp>);
3535 results.add<patterns::UnclockedAssumeIntrinsicXWhenX>(context);
3538void CoverOp::getCanonicalizationPatterns(RewritePatternSet &results,
3539 MLIRContext *context) {
3540 results.add(canonicalizeImmediateVerifOp<CoverOp, /* EraseIfZero = */ true>);
3547LogicalResult InvalidValueOp::canonicalize(InvalidValueOp op,
3548 PatternRewriter &rewriter) {
3550 if (op.use_empty()) {
3551 rewriter.eraseOp(op);
3558 if (op->hasOneUse() &&
3559 (isa<BitsPrimOp, HeadPrimOp, ShrPrimOp, TailPrimOp, SubfieldOp,
3560 SubindexOp, AsSIntPrimOp, AsUIntPrimOp, NotPrimOp, BitCastOp>(
3561 *op->user_begin()) ||
3562 (isa<CvtPrimOp>(*op->user_begin()) &&
3563 type_isa<SIntType>(op->user_begin()->getOperand(0).getType())) ||
3564 (isa<AndRPrimOp, XorRPrimOp, OrRPrimOp>(*op->user_begin()) &&
3565 type_cast<FIRRTLBaseType>(op->user_begin()->getOperand(0).getType())
3566 .getBitWidthOrSentinel() > 0))) {
3567 auto *modop = *op->user_begin();
3568 auto inv = InvalidValueOp::create(rewriter, op.getLoc(),
3569 modop->getResult(0).getType());
3570 rewriter.replaceAllOpUsesWith(modop, inv);
3571 rewriter.eraseOp(modop);
3572 rewriter.eraseOp(op);
3578OpFoldResult InvalidValueOp::fold(FoldAdaptor adaptor) {
3579 if (getType().getBitWidthOrSentinel() == 0 && isa<IntType>(getType()))
3580 return getIntAttr(getType(), APInt(0, 0, isa<SIntType>(getType())));
3588OpFoldResult ClockGateIntrinsicOp::fold(FoldAdaptor adaptor) {
3597 return BoolAttr::get(getContext(),
false);
3601 return BoolAttr::get(getContext(),
false);
3606LogicalResult ClockGateIntrinsicOp::canonicalize(ClockGateIntrinsicOp op,
3607 PatternRewriter &rewriter) {
3609 if (
auto testEnable = op.getTestEnable()) {
3610 if (
auto constOp = testEnable.getDefiningOp<ConstantOp>()) {
3611 if (constOp.getValue().isZero()) {
3612 rewriter.modifyOpInPlace(op,
3613 [&] { op.getTestEnableMutable().clear(); });
3629 auto forceable = op.getRef().getDefiningOp<Forceable>();
3630 if (!forceable || !forceable.isForceable() ||
3631 op.getRef() != forceable.getDataRef() ||
3632 op.getType() != forceable.getDataType())
3634 rewriter.replaceAllUsesWith(op, forceable.getData());
3638void RefResolveOp::getCanonicalizationPatterns(RewritePatternSet &results,
3639 MLIRContext *context) {
3640 results.insert<patterns::RefResolveOfRefSend>(context);
3644OpFoldResult RefCastOp::fold(FoldAdaptor adaptor) {
3646 if (getInput().getType() == getType())
3652 auto constOp = operand.getDefiningOp<ConstantOp>();
3653 return constOp && constOp.getValue().isZero();
3656template <
typename Op>
3659 rewriter.eraseOp(op);
3665void RefForceOp::getCanonicalizationPatterns(RewritePatternSet &results,
3666 MLIRContext *context) {
3667 results.add(eraseIfPredFalse<RefForceOp>);
3669void RefForceInitialOp::getCanonicalizationPatterns(RewritePatternSet &results,
3670 MLIRContext *context) {
3671 results.add(eraseIfPredFalse<RefForceInitialOp>);
3673void RefReleaseOp::getCanonicalizationPatterns(RewritePatternSet &results,
3674 MLIRContext *context) {
3675 results.add(eraseIfPredFalse<RefReleaseOp>);
3677void RefReleaseInitialOp::getCanonicalizationPatterns(
3678 RewritePatternSet &results, MLIRContext *context) {
3679 results.add(eraseIfPredFalse<RefReleaseInitialOp>);
3686OpFoldResult HasBeenResetIntrinsicOp::fold(FoldAdaptor adaptor) {
3692 if (adaptor.getReset())
3697 if (
isUInt1(getReset().getType()) && adaptor.getClock())
3710 [&](
auto ty) ->
bool {
return isTypeEmpty(ty.getElementType()); })
3711 .Case<BundleType>([&](
auto ty) ->
bool {
3712 for (
auto elem : ty.getElements())
3717 .Case<IntType>([&](
auto ty) {
return ty.getWidth() == 0; })
3718 .Default([](
auto) ->
bool {
return false; });
3721LogicalResult FPGAProbeIntrinsicOp::canonicalize(FPGAProbeIntrinsicOp op,
3722 PatternRewriter &rewriter) {
3723 auto firrtlTy = type_dyn_cast<FIRRTLType>(op.getInput().getType());
3730 rewriter.eraseOp(op);
3738LogicalResult LayerBlockOp::canonicalize(LayerBlockOp op,
3739 PatternRewriter &rewriter) {
3742 if (op.getBody()->empty()) {
3743 rewriter.eraseOp(op);
3754OpFoldResult UnsafeDomainCastOp::fold(FoldAdaptor adaptor) {
3756 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)