20#include "mlir/IR/Matchers.h"
21#include "mlir/IR/PatternMatch.h"
22#include "llvm/ADT/APSInt.h"
23#include "llvm/ADT/STLExtras.h"
24#include "llvm/ADT/SmallPtrSet.h"
25#include "llvm/ADT/StringExtras.h"
26#include "llvm/ADT/TypeSwitch.h"
33static Value
dropWrite(PatternRewriter &rewriter, OpResult old,
35 SmallPtrSet<Operation *, 8> users;
36 for (
auto *user : old.getUsers())
38 for (Operation *user : users)
39 if (
auto connect = dyn_cast<FConnectLike>(user))
40 if (connect.getDest() == old)
41 rewriter.eraseOp(user);
51 if (op->getNumRegions() != 0)
53 return mlir::isPure(op) || isa<NodeOp, WireOp>(op);
61 Operation *op = passthrough.getDefiningOp();
64 assert(op &&
"passthrough must be an operation");
65 Operation *oldOp = old.getOwner();
66 auto name = oldOp->getAttrOfType<StringAttr>(
"name");
68 op->setAttr(
"name", name);
76#include "circt/Dialect/FIRRTL/FIRRTLCanonicalization.h.inc"
85 if (!resultType.hasWidth())
87 for (Value operand : op->getOperands())
96 if (!t || !t.hasWidth() || t.getWidth() != 1)
103static void updateName(PatternRewriter &rewriter, Operation *op,
108 assert((!isa<InstanceOp, RegOp, RegResetOp>(op)) &&
"Should never rename");
109 auto newName = name.getValue();
110 auto newOpName = op->getAttrOfType<StringAttr>(
"name");
113 newName =
chooseName(newOpName.getValue(), name.getValue());
115 if (!newOpName || newOpName.getValue() != newName)
116 rewriter.modifyOpInPlace(
117 op, [&] { op->setAttr(
"name", rewriter.getStringAttr(newName)); });
125 if (
auto *newOp = newValue.getDefiningOp()) {
126 auto name = op->getAttrOfType<StringAttr>(
"name");
129 rewriter.replaceOp(op, newValue);
135template <
typename OpTy,
typename... Args>
137 Operation *op, Args &&...args) {
138 auto name = op->getAttrOfType<StringAttr>(
"name");
140 rewriter.replaceOpWithNewOp<OpTy>(op, std::forward<Args>(args)...);
148 if (
auto namableOp = dyn_cast<firrtl::FNamableOp>(op))
149 return namableOp.hasDroppableName();
160static std::optional<APSInt>
163 "getExtendedConstant is limited to integer types");
170 if (IntegerAttr result = dyn_cast_or_null<IntegerAttr>(constant))
176 return APSInt(destWidth,
185 if (
auto attr = dyn_cast<BoolAttr>(operand))
186 return APSInt(APInt(1, attr.getValue()));
187 if (
auto attr = dyn_cast<IntegerAttr>(operand))
188 return attr.getAPSInt();
196 return cst->isZero();
223 Operation *op, ArrayRef<Attribute> operands,
BinOpKind opKind,
224 const function_ref<APInt(
const APSInt &,
const APSInt &)> &calculate) {
225 assert(operands.size() == 2 &&
"binary op takes two operands");
229 if (resultType.getWidthOrSentinel() < 0)
233 if (resultType.getWidthOrSentinel() == 0)
234 return getIntAttr(resultType, APInt(0, 0, resultType.isSigned()));
243 if (
auto lhs = dyn_cast_or_null<IntegerAttr>(operands[0]))
244 lhsWidth = std::max<int32_t>(lhsWidth, lhs.getValue().getBitWidth());
245 if (
auto rhs = dyn_cast_or_null<IntegerAttr>(operands[1]))
246 rhsWidth = std::max<int32_t>(rhsWidth, rhs.getValue().getBitWidth());
250 int32_t operandWidth;
253 operandWidth = resultType.getWidthOrSentinel();
258 operandWidth = std::max(1, std::max(lhsWidth, rhsWidth));
262 std::max(std::max(lhsWidth, rhsWidth), resultType.getWidthOrSentinel());
273 APInt resultValue = calculate(*lhs, *rhs);
278 resultValue = resultValue.trunc(resultType.getWidthOrSentinel());
280 assert((
unsigned)resultType.getWidthOrSentinel() ==
281 resultValue.getBitWidth());
294 Operation *op, PatternRewriter &rewriter,
295 const function_ref<OpFoldResult(ArrayRef<Attribute>)> &canonicalize) {
297 if (op->getNumResults() != 1)
304 auto width = type.getBitWidthOrSentinel();
309 SmallVector<Attribute, 3> constOperands;
310 constOperands.reserve(op->getNumOperands());
311 for (
auto operand : op->getOperands()) {
313 if (
auto *defOp = operand.getDefiningOp())
314 TypeSwitch<Operation *>(defOp).Case<ConstantOp, SpecialConstantOp>(
315 [&](
auto op) { attr = op.getValueAttr(); });
316 constOperands.push_back(attr);
321 auto result = canonicalize(constOperands);
325 if (
auto cst = dyn_cast<Attribute>(result))
326 resultValue = op->getDialect()
327 ->materializeConstant(rewriter, cst, type, op->getLoc())
330 resultValue = cast<Value>(result);
335 resultValue = PadPrimOp::create(rewriter, op->getLoc(), resultValue, width);
339 resultValue = AsSIntPrimOp::create(rewriter, op->getLoc(), resultValue);
342 resultValue = AsUIntPrimOp::create(rewriter, op->getLoc(), resultValue);
344 assert(type == resultValue.getType() &&
"canonicalization changed type");
352 return bitWidth > 0 ? APInt::getMaxValue(bitWidth) : APInt();
358 return bitWidth > 0 ? APInt::getSignedMinValue(bitWidth) : APInt();
364 return bitWidth > 0 ? APInt::getSignedMaxValue(bitWidth) : APInt();
371OpFoldResult ConstantOp::fold(FoldAdaptor adaptor) {
372 assert(adaptor.getOperands().empty() &&
"constant has no operands");
373 return getValueAttr();
376OpFoldResult SpecialConstantOp::fold(FoldAdaptor adaptor) {
377 assert(adaptor.getOperands().empty() &&
"constant has no operands");
378 return getValueAttr();
381OpFoldResult AggregateConstantOp::fold(FoldAdaptor adaptor) {
382 assert(adaptor.getOperands().empty() &&
"constant has no operands");
383 return getFieldsAttr();
386OpFoldResult StringConstantOp::fold(FoldAdaptor adaptor) {
387 assert(adaptor.getOperands().empty() &&
"constant has no operands");
388 return getValueAttr();
391OpFoldResult FIntegerConstantOp::fold(FoldAdaptor adaptor) {
392 assert(adaptor.getOperands().empty() &&
"constant has no operands");
393 return getValueAttr();
396OpFoldResult BoolConstantOp::fold(FoldAdaptor adaptor) {
397 assert(adaptor.getOperands().empty() &&
"constant has no operands");
398 return getValueAttr();
401OpFoldResult DoubleConstantOp::fold(FoldAdaptor adaptor) {
402 assert(adaptor.getOperands().empty() &&
"constant has no operands");
403 return getValueAttr();
410OpFoldResult AddPrimOp::fold(FoldAdaptor adaptor) {
413 [=](
const APSInt &a,
const APSInt &b) { return a + b; });
416void AddPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
418 results.insert<patterns::moveConstAdd, patterns::AddOfZero,
419 patterns::AddOfSelf, patterns::AddOfPad>(
context);
422OpFoldResult SubPrimOp::fold(FoldAdaptor adaptor) {
425 [=](
const APSInt &a,
const APSInt &b) { return a - b; });
428void SubPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
430 results.insert<patterns::SubOfZero, patterns::SubFromZeroSigned,
431 patterns::SubFromZeroUnsigned, patterns::SubOfSelf,
432 patterns::SubOfPadL, patterns::SubOfPadR>(
context);
435OpFoldResult MulPrimOp::fold(FoldAdaptor adaptor) {
447 [=](
const APSInt &a,
const APSInt &b) { return a * b; });
450OpFoldResult DivPrimOp::fold(FoldAdaptor adaptor) {
457 if (getLhs() == getRhs()) {
458 auto width = getType().base().getWidthOrSentinel();
463 return getIntAttr(getType(), APInt(width, 1));
480 if (
auto rhsCst = dyn_cast_or_null<IntegerAttr>(adaptor.getRhs()))
481 if (rhsCst.getValue().isOne() && getLhs().getType() == getType())
486 [=](
const APSInt &a,
const APSInt &b) -> APInt {
489 return APInt(a.getBitWidth(), 0);
493OpFoldResult RemPrimOp::fold(FoldAdaptor adaptor) {
500 if (getLhs() == getRhs())
514 [=](
const APSInt &a,
const APSInt &b) -> APInt {
517 return APInt(a.getBitWidth(), 0);
521OpFoldResult DShlPrimOp::fold(FoldAdaptor adaptor) {
524 [=](
const APSInt &a,
const APSInt &b) -> APInt { return a.shl(b); });
527OpFoldResult DShlwPrimOp::fold(FoldAdaptor adaptor) {
530 [=](
const APSInt &a,
const APSInt &b) -> APInt { return a.shl(b); });
533OpFoldResult DShrPrimOp::fold(FoldAdaptor adaptor) {
536 [=](
const APSInt &a,
const APSInt &b) -> APInt {
537 return getType().base().isUnsigned() || !a.getBitWidth() ? a.lshr(b)
543OpFoldResult AndPrimOp::fold(FoldAdaptor adaptor) {
546 if (rhsCst->isZero())
550 if (rhsCst->isAllOnes() && getLhs().getType() == getType() &&
551 getRhs().getType() == getType())
557 if (lhsCst->isZero())
561 if (lhsCst->isAllOnes() && getLhs().getType() == getType() &&
562 getRhs().getType() == getType())
567 if (getLhs() == getRhs() && getRhs().getType() == getType())
572 [](
const APSInt &a,
const APSInt &b) -> APInt { return a & b; });
575void AndPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
578 .insert<patterns::extendAnd, patterns::moveConstAnd, patterns::AndOfZero,
579 patterns::AndOfAllOne, patterns::AndOfSelf, patterns::AndOfPad,
580 patterns::AndOfAsSIntL, patterns::AndOfAsSIntR>(
context);
583OpFoldResult OrPrimOp::fold(FoldAdaptor adaptor) {
586 if (rhsCst->isZero() && getLhs().getType() == getType())
590 if (rhsCst->isAllOnes() && getRhs().getType() == getType() &&
591 getLhs().getType() == getType())
597 if (lhsCst->isZero() && getRhs().getType() == getType())
601 if (lhsCst->isAllOnes() && getLhs().getType() == getType() &&
602 getRhs().getType() == getType())
607 if (getLhs() == getRhs() && getRhs().getType() == getType())
612 [](
const APSInt &a,
const APSInt &b) -> APInt { return a | b; });
615void OrPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
617 results.insert<patterns::extendOr, patterns::moveConstOr, patterns::OrOfZero,
618 patterns::OrOfAllOne, patterns::OrOfSelf, patterns::OrOfPad,
622OpFoldResult XorPrimOp::fold(FoldAdaptor adaptor) {
625 if (rhsCst->isZero() &&
631 if (lhsCst->isZero() &&
636 if (getLhs() == getRhs())
639 APInt(std::max(getType().base().getWidthOrSentinel(), 0), 0));
643 [](
const APSInt &a,
const APSInt &b) -> APInt { return a ^ b; });
646void XorPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
648 results.insert<patterns::extendXor, patterns::moveConstXor,
649 patterns::XorOfZero, patterns::XorOfSelf, patterns::XorOfPad>(
653void LEQPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
655 results.insert<patterns::LEQWithConstLHS>(
context);
658OpFoldResult LEQPrimOp::fold(FoldAdaptor adaptor) {
659 bool isUnsigned = getLhs().getType().base().isUnsigned();
662 if (getLhs() == getRhs())
666 if (
auto width = getLhs().getType().base().
getWidth()) {
668 auto commonWidth = std::max<int32_t>(*width, rhsCst->getBitWidth());
669 commonWidth = std::max(commonWidth, 1);
680 if (isUnsigned && rhsCst->zext(commonWidth)
693 [=](
const APSInt &a,
const APSInt &b) -> APInt {
694 return APInt(1, a <= b);
698void LTPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
700 results.insert<patterns::LTWithConstLHS>(
context);
703OpFoldResult LTPrimOp::fold(FoldAdaptor adaptor) {
704 IntType lhsType = getLhs().getType();
708 if (getLhs() == getRhs())
718 if (
auto width = lhsType.
getWidth()) {
720 auto commonWidth = std::max<int32_t>(*width, rhsCst->getBitWidth());
721 commonWidth = std::max(commonWidth, 1);
732 if (isUnsigned && rhsCst->zext(commonWidth)
745 [=](
const APSInt &a,
const APSInt &b) -> APInt {
746 return APInt(1, a < b);
750void GEQPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
752 results.insert<patterns::GEQWithConstLHS>(
context);
755OpFoldResult GEQPrimOp::fold(FoldAdaptor adaptor) {
756 IntType lhsType = getLhs().getType();
760 if (getLhs() == getRhs())
765 if (rhsCst->isZero() && isUnsigned)
770 if (
auto width = lhsType.
getWidth()) {
772 auto commonWidth = std::max<int32_t>(*width, rhsCst->getBitWidth());
773 commonWidth = std::max(commonWidth, 1);
776 if (isUnsigned && rhsCst->zext(commonWidth)
797 [=](
const APSInt &a,
const APSInt &b) -> APInt {
798 return APInt(1, a >= b);
802void GTPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
804 results.insert<patterns::GTWithConstLHS>(
context);
807OpFoldResult GTPrimOp::fold(FoldAdaptor adaptor) {
808 IntType lhsType = getLhs().getType();
812 if (getLhs() == getRhs())
816 if (
auto width = lhsType.
getWidth()) {
818 auto commonWidth = std::max<int32_t>(*width, rhsCst->getBitWidth());
819 commonWidth = std::max(commonWidth, 1);
822 if (isUnsigned && rhsCst->zext(commonWidth)
843 [=](
const APSInt &a,
const APSInt &b) -> APInt {
844 return APInt(1, a > b);
848OpFoldResult EQPrimOp::fold(FoldAdaptor adaptor) {
850 if (getLhs() == getRhs())
856 if (rhsCst->isAllOnes() && getLhs().getType() == getType() &&
857 getRhs().getType() == getType())
863 [=](
const APSInt &a,
const APSInt &b) -> APInt {
864 return APInt(1, a == b);
868LogicalResult EQPrimOp::canonicalize(EQPrimOp op, PatternRewriter &rewriter) {
870 op, rewriter, [&](ArrayRef<Attribute> operands) -> OpFoldResult {
872 auto width = op.getLhs().getType().getBitWidthOrSentinel();
875 if (rhsCst->isZero() && op.getLhs().getType() == op.getType() &&
876 op.getRhs().getType() == op.getType()) {
877 return NotPrimOp::create(rewriter, op.getLoc(), op.getLhs())
882 if (rhsCst->isZero() && width > 1) {
883 auto orrOp = OrRPrimOp::create(rewriter, op.getLoc(), op.getLhs());
884 return NotPrimOp::create(rewriter, op.getLoc(), orrOp).getResult();
888 if (rhsCst->isAllOnes() && width > 1 &&
889 op.getLhs().getType() == op.getRhs().getType()) {
890 return AndRPrimOp::create(rewriter, op.getLoc(), op.getLhs())
898OpFoldResult NEQPrimOp::fold(FoldAdaptor adaptor) {
900 if (getLhs() == getRhs())
906 if (rhsCst->isZero() && getLhs().getType() == getType() &&
907 getRhs().getType() == getType())
913 [=](
const APSInt &a,
const APSInt &b) -> APInt {
914 return APInt(1, a != b);
918LogicalResult NEQPrimOp::canonicalize(NEQPrimOp op, PatternRewriter &rewriter) {
920 op, rewriter, [&](ArrayRef<Attribute> operands) -> OpFoldResult {
922 auto width = op.getLhs().getType().getBitWidthOrSentinel();
925 if (rhsCst->isAllOnes() && op.getLhs().getType() == op.getType() &&
926 op.getRhs().getType() == op.getType()) {
927 return NotPrimOp::create(rewriter, op.getLoc(), op.getLhs())
932 if (rhsCst->isZero() && width > 1) {
933 return OrRPrimOp::create(rewriter, op.getLoc(), op.getLhs())
938 if (rhsCst->isAllOnes() && width > 1 &&
939 op.getLhs().getType() == op.getRhs().getType()) {
941 AndRPrimOp::create(rewriter, op.getLoc(), op.getLhs());
942 return NotPrimOp::create(rewriter, op.getLoc(), andrOp).getResult();
950OpFoldResult IntegerAddOp::fold(FoldAdaptor adaptor) {
956OpFoldResult IntegerMulOp::fold(FoldAdaptor adaptor) {
962OpFoldResult IntegerShrOp::fold(FoldAdaptor adaptor) {
966 return IntegerAttr::get(IntegerType::get(getContext(),
967 lhsCst->getBitWidth(),
968 IntegerType::Signed),
969 lhsCst->ashr(*rhsCst));
972 if (rhsCst->isZero())
979OpFoldResult IntegerShlOp::fold(FoldAdaptor adaptor) {
984 return IntegerAttr::get(IntegerType::get(getContext(),
985 lhsCst->getBitWidth(),
986 IntegerType::Signed),
987 lhsCst->shl(*rhsCst));
990 if (rhsCst->isZero())
1002 MLIRContext *
context, Attribute lhsAttr, Attribute rhsAttr,
1003 const function_ref<APInt(
const APInt &,
const APInt &)> &evaluate) {
1004 auto lhs = dyn_cast_or_null<IntegerAttr>(lhsAttr);
1005 auto rhs = dyn_cast_or_null<IntegerAttr>(rhsAttr);
1006 if (!lhs || !rhs || !lhs.getType().isSignedInteger() ||
1007 !rhs.getType().isSignedInteger())
1010 APSInt lhsValue = lhs.getAPSInt();
1011 APSInt rhsValue = rhs.getAPSInt();
1012 unsigned width = std::max(lhsValue.getBitWidth(), rhsValue.getBitWidth());
1013 lhsValue = lhsValue.extend(width);
1014 rhsValue = rhsValue.extend(width);
1016 APSInt result(evaluate(lhsValue, rhsValue),
false);
1017 auto resultType = IntegerType::get(
context, width, IntegerType::Signed);
1018 return IntegerAttr::get(resultType, result);
1021OpFoldResult IntegerAndOp::fold(FoldAdaptor adaptor) {
1023 getContext(), adaptor.getLhs(), adaptor.getRhs(),
1024 [](
const APInt &lhs,
const APInt &rhs) { return lhs & rhs; });
1027OpFoldResult IntegerOrOp::fold(FoldAdaptor adaptor) {
1029 getContext(), adaptor.getLhs(), adaptor.getRhs(),
1030 [](
const APInt &lhs,
const APInt &rhs) { return lhs | rhs; });
1033OpFoldResult IntegerNotOp::fold(FoldAdaptor adaptor) {
1034 auto input = dyn_cast_or_null<IntegerAttr>(adaptor.getInput());
1035 if (!input || !input.getType().isSignedInteger())
1038 APSInt result(~input.getAPSInt(),
false);
1040 IntegerType::get(getContext(), result.getBitWidth(), IntegerType::Signed);
1041 return IntegerAttr::get(resultType, result);
1048OpFoldResult SizeOfIntrinsicOp::fold(FoldAdaptor) {
1049 auto base = getInput().getType();
1056OpFoldResult IsXIntrinsicOp::fold(FoldAdaptor adaptor) {
1063OpFoldResult AsSIntPrimOp::fold(FoldAdaptor adaptor) {
1071 if (getType().base().hasWidth())
1078void AsSIntPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1080 results.insert<patterns::StoUtoS>(
context);
1083OpFoldResult AsUIntPrimOp::fold(FoldAdaptor adaptor) {
1091 if (getType().base().hasWidth())
1098void AsUIntPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1100 results.insert<patterns::UtoStoU>(
context);
1103OpFoldResult AsAsyncResetPrimOp::fold(FoldAdaptor adaptor) {
1105 if (getInput().getType() == getType())
1110 return BoolAttr::get(getContext(), cst->getBoolValue());
1115OpFoldResult AsResetPrimOp::fold(FoldAdaptor adaptor) {
1117 return BoolAttr::get(getContext(), cst->getBoolValue());
1121OpFoldResult AsClockPrimOp::fold(FoldAdaptor adaptor) {
1123 if (getInput().getType() == getType())
1128 return BoolAttr::get(getContext(), cst->getBoolValue());
1133OpFoldResult CvtPrimOp::fold(FoldAdaptor adaptor) {
1139 getType().base().getWidthOrSentinel()))
1145void CvtPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1147 results.insert<patterns::CVTSigned, patterns::CVTUnSigned>(
context);
1150OpFoldResult NegPrimOp::fold(FoldAdaptor adaptor) {
1157 getType().base().getWidthOrSentinel()))
1158 return getIntAttr(getType(), APInt((*cst).getBitWidth(), 0) - *cst);
1163OpFoldResult NotPrimOp::fold(FoldAdaptor adaptor) {
1168 getType().base().getWidthOrSentinel()))
1174void NotPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1176 results.insert<patterns::NotNot, patterns::NotEq, patterns::NotNeq,
1177 patterns::NotLeq, patterns::NotLt, patterns::NotGeq,
1184 : RewritePattern(opName, 0,
context) {}
1190 ConstantOp constantOp,
1191 SmallVectorImpl<Value> &remaining)
const = 0;
1198 mlir::PatternRewriter &rewriter)
const override {
1200 auto catOp = op->getOperand(0).getDefiningOp<CatPrimOp>();
1204 SmallVector<Value> nonConstantOperands;
1207 for (
auto operand : catOp.getInputs()) {
1208 if (
auto constantOp = operand.getDefiningOp<ConstantOp>()) {
1210 if (
handleConstant(rewriter, op, constantOp, nonConstantOperands))
1214 nonConstantOperands.push_back(operand);
1219 if (nonConstantOperands.empty()) {
1221 rewriter, op, cast<IntType>(op->getResult(0).getType()),
1227 if (nonConstantOperands.size() == 1) {
1228 rewriter.modifyOpInPlace(
1229 op, [&] { op->setOperand(0, nonConstantOperands.front()); });
1234 if (catOp->hasOneUse() &&
1235 nonConstantOperands.size() < catOp->getNumOperands()) {
1237 nonConstantOperands);
1250 SmallVectorImpl<Value> &remaining)
const override {
1251 if (value.getValue().isZero())
1255 rewriter, op, cast<IntType>(op->getResult(0).getType()),
1268 SmallVectorImpl<Value> &remaining)
const override {
1269 if (value.getValue().isAllOnes())
1273 rewriter, op, cast<IntType>(op->getResult(0).getType()),
1286 SmallVectorImpl<Value> &remaining)
const override {
1287 if (value.getValue().isZero())
1289 remaining.push_back(value);
1295OpFoldResult AndRPrimOp::fold(FoldAdaptor adaptor) {
1299 if (getInput().getType().getBitWidthOrSentinel() == 0)
1304 return getIntAttr(getType(), APInt(1, cst->isAllOnes()));
1308 if (
isUInt1(getInput().getType()))
1314void AndRPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1316 results.insert<patterns::AndRasSInt, patterns::AndRasUInt, patterns::AndRPadU,
1317 patterns::AndRPadS, patterns::AndRCatAndR_left,
1321OpFoldResult OrRPrimOp::fold(FoldAdaptor adaptor) {
1325 if (getInput().getType().getBitWidthOrSentinel() == 0)
1330 return getIntAttr(getType(), APInt(1, !cst->isZero()));
1334 if (
isUInt1(getInput().getType()))
1340void OrRPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1342 results.insert<patterns::OrRasSInt, patterns::OrRasUInt, patterns::OrRPadU,
1343 patterns::OrRCatOrR_left, patterns::OrRCatOrR_right,
OrRCat>(
1347OpFoldResult XorRPrimOp::fold(FoldAdaptor adaptor) {
1351 if (getInput().getType().getBitWidthOrSentinel() == 0)
1356 return getIntAttr(getType(), APInt(1, cst->popcount() & 1));
1359 if (
isUInt1(getInput().getType()))
1365void XorRPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1368 .insert<patterns::XorRasSInt, patterns::XorRasUInt, patterns::XorRPadU,
1369 patterns::XorRCatXorR_left, patterns::XorRCatXorR_right,
XorRCat>(
1377OpFoldResult CatPrimOp::fold(FoldAdaptor adaptor) {
1378 auto inputs = getInputs();
1379 auto inputAdaptors = adaptor.getInputs();
1386 if (inputs.size() == 1 && inputs[0].getType() == getType())
1394 SmallVector<Value> nonZeroInputs;
1395 SmallVector<Attribute> nonZeroAttributes;
1396 bool allConstant =
true;
1397 for (
auto [input, attr] : llvm::zip(inputs, inputAdaptors)) {
1399 if (inputType.getBitWidthOrSentinel() != 0) {
1400 nonZeroInputs.push_back(input);
1402 allConstant =
false;
1403 if (nonZeroInputs.size() > 1 && !allConstant)
1409 if (nonZeroInputs.empty())
1413 if (nonZeroInputs.size() == 1 && nonZeroInputs[0].getType() == getType())
1414 return nonZeroInputs[0];
1420 SmallVector<APInt> constants;
1421 for (
auto inputAdaptor : inputAdaptors) {
1423 constants.push_back(*cst);
1428 assert(!constants.empty());
1430 APInt result = constants[0];
1431 for (
size_t i = 1; i < constants.size(); ++i)
1432 result = result.concat(constants[i]);
1437void DShlPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1439 results.insert<patterns::DShlOfConstant>(
context);
1442void DShrPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1444 results.insert<patterns::DShrOfConstant>(
context);
1452 using OpRewritePattern::OpRewritePattern;
1455 matchAndRewrite(CatPrimOp cat,
1456 mlir::PatternRewriter &rewriter)
const override {
1458 cat.getType().getBitWidthOrSentinel() == 0)
1462 if (cat->hasOneUse() && isa<CatPrimOp>(*cat->getUsers().begin()))
1466 SmallVector<Value> operands;
1467 SmallVector<Value> worklist;
1468 auto pushOperands = [&worklist](CatPrimOp op) {
1469 for (
auto operand : llvm::reverse(op.getInputs()))
1470 worklist.push_back(operand);
1473 bool hasSigned =
false, hasUnsigned =
false;
1474 while (!worklist.empty()) {
1475 auto value = worklist.pop_back_val();
1476 auto catOp = value.getDefiningOp<CatPrimOp>();
1478 operands.push_back(value);
1483 pushOperands(catOp);
1488 auto castToUIntIfSigned = [&](Value value) -> Value {
1491 return AsUIntPrimOp::create(rewriter, value.getLoc(), value);
1494 assert(operands.size() >= 1 &&
"zero width cast must be rejected");
1496 if (operands.size() == 1) {
1497 rewriter.replaceOp(cat, castToUIntIfSigned(operands[0]));
1501 if (operands.size() == cat->getNumOperands())
1505 if (hasSigned && hasUnsigned)
1506 for (
auto &operand : operands)
1507 operand = castToUIntIfSigned(operand);
1518 using OpRewritePattern::OpRewritePattern;
1521 matchAndRewrite(CatPrimOp cat,
1522 mlir::PatternRewriter &rewriter)
const override {
1526 SmallVector<Value> operands;
1528 for (
size_t i = 0; i < cat->getNumOperands(); ++i) {
1529 auto cst = cat.getInputs()[i].getDefiningOp<ConstantOp>();
1531 operands.push_back(cat.getInputs()[i]);
1534 APSInt value = cst.getValue();
1536 for (; j < cat->getNumOperands(); ++j) {
1537 auto nextCst = cat.getInputs()[j].getDefiningOp<ConstantOp>();
1540 value = value.concat(nextCst.getValue());
1545 operands.push_back(cst);
1548 operands.push_back(ConstantOp::create(rewriter, cat.getLoc(), value));
1554 if (operands.size() == cat->getNumOperands())
1566void CatPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1568 results.insert<patterns::CatBitsBits, patterns::CatDoubleConst,
1569 patterns::CatCast, FlattenCat, CatOfConstant>(
context);
1576OpFoldResult StringConcatOp::fold(FoldAdaptor adaptor) {
1578 if (getInputs().size() == 1)
1579 return getInputs()[0];
1582 if (!llvm::all_of(adaptor.getInputs(), [](Attribute operand) {
1583 return isa_and_nonnull<StringAttr>(operand);
1588 SmallString<64> result;
1589 for (
auto operand : adaptor.getInputs())
1590 result += cast<StringAttr>(operand).getValue();
1592 return StringAttr::get(getContext(), result);
1600 using OpRewritePattern::OpRewritePattern;
1603 matchAndRewrite(StringConcatOp
concat,
1604 mlir::PatternRewriter &rewriter)
const override {
1608 bool hasNestedConcat = llvm::any_of(
concat.getInputs(), [](Value operand) {
1609 auto nestedConcat = operand.getDefiningOp<StringConcatOp>();
1610 return nestedConcat && operand.hasOneUse();
1613 if (!hasNestedConcat)
1617 SmallVector<Value> flatOperands;
1618 for (
auto input :
concat.getInputs()) {
1619 if (
auto nestedConcat = input.getDefiningOp<StringConcatOp>();
1620 nestedConcat && input.hasOneUse())
1621 llvm::append_range(flatOperands, nestedConcat.getInputs());
1623 flatOperands.push_back(input);
1626 rewriter.modifyOpInPlace(
concat,
1627 [&]() {
concat->setOperands(flatOperands); });
1634class MergeAdjacentStringConstants
1637 using OpRewritePattern::OpRewritePattern;
1640 matchAndRewrite(StringConcatOp
concat,
1641 mlir::PatternRewriter &rewriter)
const override {
1643 SmallVector<Value> newOperands;
1644 SmallString<64> accumulatedLit;
1645 SmallVector<StringConstantOp> accumulatedOps;
1646 bool changed =
false;
1648 auto flushLiterals = [&]() {
1649 if (accumulatedOps.empty())
1653 if (accumulatedOps.size() == 1) {
1654 newOperands.push_back(accumulatedOps[0]);
1657 auto newLit = rewriter.createOrFold<StringConstantOp>(
1658 concat.getLoc(), StringAttr::get(getContext(), accumulatedLit));
1659 newOperands.push_back(newLit);
1662 accumulatedLit.clear();
1663 accumulatedOps.clear();
1666 for (
auto operand :
concat.getInputs()) {
1667 if (
auto litOp = operand.getDefiningOp<StringConstantOp>()) {
1669 if (litOp.getValue().empty()) {
1673 accumulatedLit += litOp.getValue();
1674 accumulatedOps.push_back(litOp);
1677 newOperands.push_back(operand);
1688 if (newOperands.empty())
1689 return rewriter.replaceOpWithNewOp<StringConstantOp>(
1690 concat, StringAttr::get(getContext(),
"")),
1694 rewriter.modifyOpInPlace(
concat,
1695 [&]() {
concat->setOperands(newOperands); });
1702void StringConcatOp::getCanonicalizationPatterns(RewritePatternSet &results,
1704 results.insert<FlattenStringConcat, MergeAdjacentStringConstants>(
context);
1711OpFoldResult PropEqOp::fold(FoldAdaptor adaptor) {
1712 auto lhsAttr = adaptor.getLhs();
1713 auto rhsAttr = adaptor.getRhs();
1714 if (!lhsAttr || !rhsAttr)
1717 return BoolAttr::get(getContext(), lhsAttr == rhsAttr);
1726 if (
auto boolAttr = dyn_cast_or_null<BoolAttr>(attr))
1727 return boolAttr.getValue();
1728 return std::nullopt;
1731OpFoldResult BoolAndOp::fold(FoldAdaptor adaptor) {
1735 return BoolAttr::get(getContext(), *lhs && *rhs);
1737 if ((lhs && !*lhs) || (rhs && !*rhs))
1738 return BoolAttr::get(getContext(),
false);
1747OpFoldResult BoolOrOp::fold(FoldAdaptor adaptor) {
1751 return BoolAttr::get(getContext(), *lhs || *rhs);
1753 if ((lhs && *lhs) || (rhs && *rhs))
1754 return BoolAttr::get(getContext(),
true);
1763OpFoldResult BoolXorOp::fold(FoldAdaptor adaptor) {
1767 return BoolAttr::get(getContext(), *lhs ^ *rhs);
1776OpFoldResult BitCastOp::fold(FoldAdaptor adaptor) {
1779 if (op.getType() == op.getInput().getType())
1780 return op.getInput();
1784 if (BitCastOp in = dyn_cast_or_null<BitCastOp>(op.getInput().getDefiningOp()))
1785 if (op.getType() == in.getInput().getType())
1786 return in.getInput();
1791OpFoldResult BitsPrimOp::fold(FoldAdaptor adaptor) {
1792 IntType inputType = getInput().getType();
1793 IntType resultType = getType();
1795 if (inputType == getType() && resultType.
hasWidth())
1802 cst->extractBits(getHi() - getLo() + 1, getLo()));
1808 using OpRewritePattern::OpRewritePattern;
1812 mlir::PatternRewriter &rewriter)
const override {
1813 auto cat = bits.getInput().getDefiningOp<CatPrimOp>();
1816 int32_t bitPos = bits.getLo();
1818 if (resultWidth < 0)
1820 for (
auto operand : llvm::reverse(cat.getInputs())) {
1823 if (operandWidth < 0)
1825 if (bitPos < operandWidth) {
1826 if (bitPos + resultWidth <= operandWidth) {
1827 auto newBits = rewriter.createOrFold<BitsPrimOp>(
1828 bits.getLoc(), operand, bitPos + resultWidth - 1, bitPos);
1834 bitPos -= operandWidth;
1840void BitsPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
1843 .insert<patterns::BitsOfBits, patterns::BitsOfMux, patterns::BitsOfAsUInt,
1851 unsigned loBit, PatternRewriter &rewriter) {
1854 value = BitsPrimOp::create(rewriter, op->getLoc(), value, hiBit, loBit);
1857 value = rewriter.createOrFold<AsSIntPrimOp>(op->getLoc(), resType, value);
1858 }
else if (resType.isUnsigned() &&
1860 value = rewriter.createOrFold<AsUIntPrimOp>(op->getLoc(), resType, value);
1862 rewriter.replaceOp(op, value);
1865template <
typename OpTy>
1866static OpFoldResult
foldMux(OpTy op,
typename OpTy::FoldAdaptor adaptor) {
1868 if (op.getType().getBitWidthOrSentinel() == 0)
1870 APInt(0, 0, op.getType().isSignedInteger()));
1873 if (op.getHigh() == op.getLow() && op.getHigh().getType() == op.getType())
1874 return op.getHigh();
1879 if (op.getType().hasUninferredWidth())
1884 if (cond->isZero() && op.getLow().getType() == op.getType())
1886 if (!cond->isZero() && op.getHigh().getType() == op.getType())
1887 return op.getHigh();
1891 if (
auto lowCst =
getConstant(adaptor.getLow())) {
1893 if (
auto highCst =
getConstant(adaptor.getHigh())) {
1895 if (highCst->getBitWidth() == lowCst->getBitWidth() &&
1896 *highCst == *lowCst)
1900 if (intType.hasWidth() &&
1901 (
unsigned)intType.getWidthOrSentinel() == highCst->getBitWidth())
1904 if (highCst->isOne() && lowCst->isZero() &&
1905 op.getType() == op.getSel().getType())
1918OpFoldResult MuxPrimOp::fold(FoldAdaptor adaptor) {
1919 return foldMux(*
this, adaptor);
1922OpFoldResult Mux2CellIntrinsicOp::fold(FoldAdaptor adaptor) {
1923 return foldMux(*
this, adaptor);
1926OpFoldResult Mux4CellIntrinsicOp::fold(FoldAdaptor adaptor) {
return {}; }
1935 using OpRewritePattern::OpRewritePattern;
1938 matchAndRewrite(MuxPrimOp mux,
1939 mlir::PatternRewriter &rewriter)
const override {
1940 auto width = mux.getType().getBitWidthOrSentinel();
1944 auto pad = [&](Value input) -> Value {
1947 if (inputWidth < 0 || width == inputWidth)
1949 return PadPrimOp::create(rewriter, mux.getLoc(), mux.getType(), input,
1954 auto newHigh = pad(mux.getHigh());
1955 auto newLow = pad(mux.getLow());
1956 if (newHigh == mux.getHigh() && newLow == mux.getLow())
1960 rewriter, mux, mux.getType(), ValueRange{mux.getSel(), newHigh, newLow},
1970 using OpRewritePattern::OpRewritePattern;
1972 static const int depthLimit = 5;
1974 Value updateOrClone(MuxPrimOp mux, Value high, Value low,
1975 mlir::PatternRewriter &rewriter,
1976 bool updateInPlace)
const {
1977 if (updateInPlace) {
1978 rewriter.modifyOpInPlace(mux, [&] {
1979 mux.setOperand(1, high);
1980 mux.setOperand(2, low);
1984 rewriter.setInsertionPointAfter(mux);
1985 return MuxPrimOp::create(rewriter, mux.getLoc(), mux.getType(),
1986 ValueRange{mux.getSel(), high, low})
1991 Value tryCondTrue(Value op, Value cond, mlir::PatternRewriter &rewriter,
1992 bool updateInPlace,
int limit)
const {
1993 MuxPrimOp mux = op.getDefiningOp<MuxPrimOp>();
1996 if (mux.getSel() == cond)
1997 return mux.getHigh();
1998 if (limit > depthLimit)
2000 updateInPlace &= mux->hasOneUse();
2002 if (Value v = tryCondTrue(mux.getHigh(), cond, rewriter, updateInPlace,
2004 return updateOrClone(mux, v, mux.getLow(), rewriter, updateInPlace);
2007 tryCondTrue(mux.getLow(), cond, rewriter, updateInPlace, limit + 1))
2008 return updateOrClone(mux, mux.getHigh(), v, rewriter, updateInPlace);
2013 Value tryCondFalse(Value op, Value cond, mlir::PatternRewriter &rewriter,
2014 bool updateInPlace,
int limit)
const {
2015 MuxPrimOp mux = op.getDefiningOp<MuxPrimOp>();
2018 if (mux.getSel() == cond)
2019 return mux.getLow();
2020 if (limit > depthLimit)
2022 updateInPlace &= mux->hasOneUse();
2024 if (Value v = tryCondFalse(mux.getHigh(), cond, rewriter, updateInPlace,
2026 return updateOrClone(mux, v, mux.getLow(), rewriter, updateInPlace);
2028 if (Value v = tryCondFalse(mux.getLow(), cond, rewriter, updateInPlace,
2030 return updateOrClone(mux, mux.getHigh(), v, rewriter, updateInPlace);
2036 matchAndRewrite(MuxPrimOp mux,
2037 mlir::PatternRewriter &rewriter)
const override {
2038 auto width = mux.getType().getBitWidthOrSentinel();
2042 if (Value v = tryCondTrue(mux.getHigh(), mux.getSel(), rewriter,
true, 0)) {
2043 rewriter.modifyOpInPlace(mux, [&] { mux.setOperand(1, v); });
2047 if (Value v = tryCondFalse(mux.getLow(), mux.getSel(), rewriter,
true, 0)) {
2048 rewriter.modifyOpInPlace(mux, [&] { mux.setOperand(2, v); });
2057void MuxPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
2060 .add<MuxPad, MuxSharedCond, patterns::MuxEQOperands,
2061 patterns::MuxEQOperandsSwapped, patterns::MuxNEQ, patterns::MuxNot,
2062 patterns::MuxSameTrue, patterns::MuxSameFalse,
2063 patterns::NarrowMuxLHS, patterns::NarrowMuxRHS, patterns::MuxPadSel>(
2067void Mux2CellIntrinsicOp::getCanonicalizationPatterns(
2068 RewritePatternSet &results, MLIRContext *
context) {
2069 results.add<patterns::Mux2PadSel>(
context);
2072void Mux4CellIntrinsicOp::getCanonicalizationPatterns(
2073 RewritePatternSet &results, MLIRContext *
context) {
2074 results.add<patterns::Mux4PadSel>(
context);
2077OpFoldResult PadPrimOp::fold(FoldAdaptor adaptor) {
2078 auto input = this->getInput();
2081 if (input.getType() == getType())
2085 auto inputType = input.getType().base();
2092 auto destWidth = getType().base().getWidthOrSentinel();
2093 if (destWidth == -1)
2096 if (inputType.
isSigned() && cst->getBitWidth())
2097 return getIntAttr(getType(), cst->sext(destWidth));
2098 return getIntAttr(getType(), cst->zext(destWidth));
2104OpFoldResult ShlPrimOp::fold(FoldAdaptor adaptor) {
2105 auto input = this->getInput();
2106 IntType inputType = input.getType();
2107 int shiftAmount = getAmount();
2110 if (shiftAmount == 0)
2116 if (inputWidth != -1) {
2117 auto resultWidth = inputWidth + shiftAmount;
2118 shiftAmount = std::min(shiftAmount, resultWidth);
2119 return getIntAttr(getType(), cst->zext(resultWidth).shl(shiftAmount));
2125OpFoldResult ShrPrimOp::fold(FoldAdaptor adaptor) {
2126 auto input = this->getInput();
2127 IntType inputType = input.getType();
2128 int shiftAmount = getAmount();
2134 if (shiftAmount == 0 && inputWidth > 0)
2137 if (inputWidth == -1)
2139 if (inputWidth == 0)
2144 if (shiftAmount >= inputWidth && inputType.
isUnsigned())
2145 return getIntAttr(getType(), APInt(0, 0,
false));
2151 value = cst->ashr(std::min(shiftAmount, inputWidth - 1));
2153 value = cst->lshr(std::min(shiftAmount, inputWidth));
2154 auto resultWidth = std::max(inputWidth - shiftAmount, 1);
2155 return getIntAttr(getType(), value.trunc(resultWidth));
2160LogicalResult ShrPrimOp::canonicalize(ShrPrimOp op, PatternRewriter &rewriter) {
2161 auto inputWidth = op.getInput().getType().base().getWidthOrSentinel();
2162 if (inputWidth <= 0)
2166 unsigned shiftAmount = op.getAmount();
2167 if (
int(shiftAmount) >= inputWidth) {
2169 if (op.getType().base().isUnsigned())
2175 shiftAmount = inputWidth - 1;
2178 replaceWithBits(op, op.getInput(), inputWidth - 1, shiftAmount, rewriter);
2182LogicalResult HeadPrimOp::canonicalize(HeadPrimOp op,
2183 PatternRewriter &rewriter) {
2184 auto inputWidth = op.getInput().getType().base().getWidthOrSentinel();
2185 if (inputWidth <= 0)
2189 unsigned keepAmount = op.getAmount();
2191 replaceWithBits(op, op.getInput(), inputWidth - 1, inputWidth - keepAmount,
2196OpFoldResult HeadPrimOp::fold(FoldAdaptor adaptor) {
2200 getInput().getType().base().getWidthOrSentinel() - getAmount();
2201 return getIntAttr(getType(), cst->lshr(shiftAmount).trunc(getAmount()));
2207OpFoldResult TailPrimOp::fold(FoldAdaptor adaptor) {
2211 cst->trunc(getType().base().getWidthOrSentinel()));
2215LogicalResult TailPrimOp::canonicalize(TailPrimOp op,
2216 PatternRewriter &rewriter) {
2217 auto inputWidth = op.getInput().getType().base().getWidthOrSentinel();
2218 if (inputWidth <= 0)
2222 unsigned dropAmount = op.getAmount();
2223 if (dropAmount !=
unsigned(inputWidth))
2229void SubaccessOp::getCanonicalizationPatterns(RewritePatternSet &results,
2231 results.add<patterns::SubaccessOfConstant>(
context);
2234OpFoldResult MultibitMuxOp::fold(FoldAdaptor adaptor) {
2236 if (adaptor.getInputs().size() == 1)
2237 return getOperand(1);
2239 if (
auto constIndex =
getConstant(adaptor.getIndex())) {
2240 auto index = constIndex->getZExtValue();
2241 if (index < getInputs().size())
2242 return getInputs()[getInputs().size() - 1 - index];
2248LogicalResult MultibitMuxOp::canonicalize(MultibitMuxOp op,
2249 PatternRewriter &rewriter) {
2253 if (llvm::all_of(op.getInputs().drop_front(), [&](
auto input) {
2254 return input == op.getInputs().front();
2262 auto indexWidth = op.getIndex().getType().getBitWidthOrSentinel();
2263 uint64_t inputSize = op.getInputs().size();
2264 if (indexWidth >= 0 && indexWidth < 64 && 1ull << indexWidth < inputSize) {
2265 rewriter.modifyOpInPlace(op, [&]() {
2266 op.getInputsMutable().erase(0, inputSize - (1ull << indexWidth));
2273 if (
auto lastSubindex = op.getInputs().back().getDefiningOp<SubindexOp>()) {
2274 if (llvm::all_of(llvm::enumerate(op.getInputs()), [&](
auto e) {
2275 auto subindex = e.value().template getDefiningOp<SubindexOp>();
2276 return subindex && lastSubindex.getInput() == subindex.getInput() &&
2277 subindex.getIndex() + e.index() + 1 == op.getInputs().size();
2280 rewriter, op, lastSubindex.getInput(), op.getIndex());
2286 if (op.getInputs().size() != 2)
2290 auto uintType = op.getIndex().getType();
2291 if (uintType.getBitWidthOrSentinel() != 1)
2296 rewriter, op, op.getIndex(), op.getInputs()[0], op.getInputs()[1]);
2315 MatchingConnectOp connect;
2316 for (Operation *user : value.getUsers()) {
2318 if (isa<AttachOp, SubfieldOp, SubaccessOp, SubindexOp>(user))
2321 if (
auto aConnect = dyn_cast<FConnectLike>(user))
2322 if (aConnect.getDest() == value) {
2323 auto matchingConnect = dyn_cast<MatchingConnectOp>(*aConnect);
2326 if (!matchingConnect || (connect && connect != matchingConnect) ||
2327 matchingConnect->getBlock() != value.getParentBlock())
2329 connect = matchingConnect;
2337 PatternRewriter &rewriter) {
2340 Operation *connectedDecl = op.getDest().getDefiningOp();
2345 if (!isa<WireOp>(connectedDecl) && !isa<RegOp>(connectedDecl))
2349 cast<Forceable>(connectedDecl).isForceable())
2357 if (connectedDecl->hasOneUse())
2361 auto *declBlock = connectedDecl->getBlock();
2362 auto *srcValueOp = op.getSrc().getDefiningOp();
2365 if (!isa<WireOp>(connectedDecl))
2371 auto cnst = dyn_cast<ConstantOp>(srcValueOp);
2374 if (srcValueOp->getBlock() != declBlock)
2379 if (
auto reg = dyn_cast<RegOp>(connectedDecl))
2386 auto replacement = op.getSrc();
2389 if (srcValueOp && srcValueOp != &declBlock->front())
2390 srcValueOp->moveBefore(&declBlock->front());
2397 rewriter.eraseOp(op);
2401void ConnectOp::getCanonicalizationPatterns(RewritePatternSet &results,
2403 results.insert<patterns::ConnectExtension, patterns::ConnectSameType>(
2407LogicalResult MatchingConnectOp::canonicalize(MatchingConnectOp op,
2408 PatternRewriter &rewriter) {
2425 for (
auto *user : value.getUsers()) {
2426 auto attach = dyn_cast<AttachOp>(user);
2427 if (!attach || attach == dominatedAttach)
2429 if (attach->isBeforeInBlock(dominatedAttach))
2435LogicalResult AttachOp::canonicalize(AttachOp op, PatternRewriter &rewriter) {
2437 if (op.getNumOperands() <= 1) {
2438 rewriter.eraseOp(op);
2442 for (
auto operand : op.getOperands()) {
2449 SmallVector<Value> newOperands(op.getOperands());
2450 for (
auto newOperand : attach.getOperands())
2451 if (newOperand != operand)
2452 newOperands.push_back(newOperand);
2453 AttachOp::create(rewriter, op->getLoc(), newOperands);
2454 rewriter.eraseOp(attach);
2455 rewriter.eraseOp(op);
2463 if (
auto wire = dyn_cast_or_null<WireOp>(operand.getDefiningOp())) {
2464 if (!
hasDontTouch(wire.getOperation()) && wire->hasOneUse() &&
2465 !wire.isForceable()) {
2466 SmallVector<Value> newOperands;
2467 for (
auto newOperand : op.getOperands())
2468 if (newOperand != operand)
2469 newOperands.push_back(newOperand);
2471 AttachOp::create(rewriter, op->getLoc(), newOperands);
2472 rewriter.eraseOp(op);
2473 rewriter.eraseOp(wire);
2484 assert(llvm::hasSingleElement(region) &&
"expected single-region block");
2485 rewriter.inlineBlockBefore(®ion.front(), op, {});
2488LogicalResult WhenOp::canonicalize(WhenOp op, PatternRewriter &rewriter) {
2489 if (
auto constant = op.getCondition().getDefiningOp<firrtl::ConstantOp>()) {
2490 if (constant.getValue().isAllOnes())
2492 else if (op.hasElseRegion() && !op.getElseRegion().empty())
2495 rewriter.eraseOp(op);
2501 if (!op.getThenBlock().empty() && op.hasElseRegion() &&
2502 op.getElseBlock().empty()) {
2503 rewriter.eraseBlock(&op.getElseBlock());
2510 if (!op.getThenBlock().empty())
2514 if (!op.hasElseRegion() || op.getElseBlock().empty()) {
2515 rewriter.eraseOp(op);
2525 using OpRewritePattern::OpRewritePattern;
2526 LogicalResult matchAndRewrite(NodeOp node,
2527 PatternRewriter &rewriter)
const override {
2528 auto name = node.getNameAttr();
2529 if (!node.hasDroppableName() || node.getInnerSym() ||
2530 !AnnotationSet(node).
empty() || node.isForceable())
2532 auto *newOp = node.getInput().getDefiningOp();
2535 rewriter.replaceOp(node, node.getInput());
2542 using OpRewritePattern::OpRewritePattern;
2543 LogicalResult matchAndRewrite(NodeOp node,
2544 PatternRewriter &rewriter)
const override {
2545 if (node.getInnerSym() || !AnnotationSet(node).
empty() ||
2546 node.use_empty() || node.isForceable())
2548 rewriter.replaceAllUsesWith(node.getResult(), node.getInput());
2555template <
typename OpTy>
2557 PatternRewriter &rewriter) {
2558 if (!op.isForceable() || !op.getDataRef().use_empty())
2566LogicalResult NodeOp::fold(FoldAdaptor adaptor,
2567 SmallVectorImpl<OpFoldResult> &results) {
2576 if (!adaptor.getInput())
2579 results.push_back(adaptor.getInput());
2583void NodeOp::getCanonicalizationPatterns(RewritePatternSet &results,
2585 results.insert<FoldNodeName>(
context);
2592struct AggOneShot :
public mlir::RewritePattern {
2593 AggOneShot(StringRef name, uint32_t weight, MLIRContext *
context)
2594 : RewritePattern(name, 0,
context) {}
2596 SmallVector<Value> getCompleteWrite(Operation *lhs)
const {
2597 auto lhsTy = lhs->getResult(0).getType();
2601 DenseMap<uint32_t, Value> fields;
2602 for (Operation *user : lhs->getResult(0).getUsers()) {
2603 if (user->getParentOp() != lhs->getParentOp())
2605 if (
auto aConnect = dyn_cast<MatchingConnectOp>(user)) {
2606 if (aConnect.getDest() == lhs->getResult(0))
2608 }
else if (
auto subField = dyn_cast<SubfieldOp>(user)) {
2609 for (Operation *subuser : subField.getResult().getUsers()) {
2610 if (
auto aConnect = dyn_cast<MatchingConnectOp>(subuser)) {
2611 if (aConnect.getDest() == subField) {
2612 if (subuser->getParentOp() != lhs->getParentOp())
2614 if (fields.count(subField.getFieldIndex()))
2616 fields[subField.getFieldIndex()] = aConnect.getSrc();
2622 }
else if (
auto subIndex = dyn_cast<SubindexOp>(user)) {
2623 for (Operation *subuser : subIndex.getResult().getUsers()) {
2624 if (
auto aConnect = dyn_cast<MatchingConnectOp>(subuser)) {
2625 if (aConnect.getDest() == subIndex) {
2626 if (subuser->getParentOp() != lhs->getParentOp())
2628 if (fields.count(subIndex.getIndex()))
2630 fields[subIndex.getIndex()] = aConnect.getSrc();
2641 SmallVector<Value> values;
2645 for (uint32_t i = 0; i < total; ++i) {
2646 if (!fields.count(i))
2648 values.push_back(fields[i]);
2653 LogicalResult matchAndRewrite(Operation *op,
2654 PatternRewriter &rewriter)
const override {
2655 auto values = getCompleteWrite(op);
2658 rewriter.setInsertionPointToEnd(op->getBlock());
2659 auto dest = op->getResult(0);
2660 auto destType = dest.getType();
2667 ? rewriter.createOrFold<BundleCreateOp>(op->getLoc(),
2669 : rewriter.createOrFold<VectorCreateOp>(
2670 op->
getLoc(), destType, values);
2671 rewriter.createOrFold<MatchingConnectOp>(op->getLoc(), dest, newVal);
2672 for (Operation *user : dest.getUsers()) {
2673 if (auto subIndex = dyn_cast<SubindexOp>(user)) {
2674 for (Operation *subuser :
2675 llvm::make_early_inc_range(subIndex.getResult().getUsers()))
2676 if (auto aConnect = dyn_cast<MatchingConnectOp>(subuser))
2677 if (aConnect.getDest() == subIndex)
2678 rewriter.eraseOp(aConnect);
2679 }
else if (
auto subField = dyn_cast<SubfieldOp>(user)) {
2680 for (Operation *subuser :
2681 llvm::make_early_inc_range(subField.getResult().getUsers()))
2682 if (
auto aConnect = dyn_cast<MatchingConnectOp>(subuser))
2683 if (aConnect.getDest() == subField)
2684 rewriter.eraseOp(aConnect);
2691struct WireAggOneShot :
public AggOneShot {
2692 WireAggOneShot(MLIRContext *
context)
2693 : AggOneShot(WireOp::getOperationName(), 0,
context) {}
2695struct SubindexAggOneShot :
public AggOneShot {
2696 SubindexAggOneShot(MLIRContext *
context)
2697 : AggOneShot(SubindexOp::getOperationName(), 0,
context) {}
2699struct SubfieldAggOneShot :
public AggOneShot {
2700 SubfieldAggOneShot(MLIRContext *
context)
2701 : AggOneShot(SubfieldOp::getOperationName(), 0,
context) {}
2705void WireOp::getCanonicalizationPatterns(RewritePatternSet &results,
2707 results.insert<WireAggOneShot>(
context);
2711void SubindexOp::getCanonicalizationPatterns(RewritePatternSet &results,
2713 results.insert<SubindexAggOneShot>(
context);
2716OpFoldResult SubindexOp::fold(FoldAdaptor adaptor) {
2717 auto attr = dyn_cast_or_null<ArrayAttr>(adaptor.getInput());
2720 return attr[getIndex()];
2723OpFoldResult SubfieldOp::fold(FoldAdaptor adaptor) {
2724 auto attr = dyn_cast_or_null<ArrayAttr>(adaptor.getInput());
2727 auto index = getFieldIndex();
2731void SubfieldOp::getCanonicalizationPatterns(RewritePatternSet &results,
2733 results.insert<SubfieldAggOneShot>(
context);
2737 ArrayRef<Attribute> operands) {
2738 for (
auto operand : operands)
2741 return ArrayAttr::get(
context, operands);
2744OpFoldResult BundleCreateOp::fold(FoldAdaptor adaptor) {
2747 if (getNumOperands() > 0)
2748 if (SubfieldOp first = getOperand(0).getDefiningOp<SubfieldOp>())
2749 if (first.getFieldIndex() == 0 &&
2750 first.getInput().getType() == getType() &&
2752 llvm::drop_begin(llvm::enumerate(getOperands())), [&](
auto elem) {
2754 elem.value().template getDefiningOp<SubfieldOp>();
2755 return subindex && subindex.getInput() == first.getInput() &&
2756 subindex.getFieldIndex() == elem.index();
2758 return first.getInput();
2763OpFoldResult VectorCreateOp::fold(FoldAdaptor adaptor) {
2766 if (getNumOperands() > 0)
2767 if (SubindexOp first = getOperand(0).getDefiningOp<SubindexOp>())
2768 if (first.getIndex() == 0 && first.getInput().getType() == getType() &&
2770 llvm::drop_begin(llvm::enumerate(getOperands())), [&](
auto elem) {
2772 elem.value().template getDefiningOp<SubindexOp>();
2773 return subindex && subindex.getInput() == first.getInput() &&
2774 subindex.getIndex() == elem.index();
2776 return first.getInput();
2781OpFoldResult UninferredResetCastOp::fold(FoldAdaptor adaptor) {
2782 if (getOperand().getType() == getType())
2783 return getOperand();
2790struct FoldResetMux :
public mlir::OpRewritePattern<RegResetOp> {
2791 using OpRewritePattern::OpRewritePattern;
2792 LogicalResult matchAndRewrite(RegResetOp reg,
2793 PatternRewriter &rewriter)
const override {
2795 dyn_cast_or_null<ConstantOp>(
reg.getResetValue().getDefiningOp());
2797 !AnnotationSet(reg).
empty() ||
reg.isForceable())
2804 auto mux = dyn_cast_or_null<MuxPrimOp>(con.getSrc().getDefiningOp());
2807 auto *high = mux.getHigh().getDefiningOp();
2808 auto *low = mux.getLow().getDefiningOp();
2809 auto constOp = dyn_cast_or_null<ConstantOp>(high);
2811 if (constOp && low != reg)
2813 if (dyn_cast_or_null<ConstantOp>(low) && high == reg)
2814 constOp = dyn_cast<ConstantOp>(low);
2816 if (!constOp || constOp.getType() != reset.getType() ||
2817 constOp.getValue() != reset.getValue())
2821 auto regTy =
reg.getResult().getType();
2822 if (con.getDest().getType() != regTy || con.getSrc().getType() != regTy ||
2823 mux.getHigh().getType() != regTy || mux.getLow().getType() != regTy ||
2824 regTy.getBitWidthOrSentinel() < 0)
2835 if (constOp != &con->getBlock()->front())
2836 constOp->moveBefore(&con->getBlock()->front());
2841 rewriter.eraseOp(con);
2848 if (
auto c = v.getDefiningOp<ConstantOp>())
2849 return c.getValue().isOne();
2850 if (
auto sc = v.getDefiningOp<SpecialConstantOp>())
2851 return sc.getValue();
2860 auto resetValue = reg.getResetValue();
2861 if (reg.getType(0) != resetValue.getType())
2866 if (
auto constOp = dyn_cast_or_null<ConstantOp>(resetValue.getDefiningOp())) {
2874 (void)
dropWrite(rewriter, reg->getResult(0), {});
2876 rewriter, reg, resetValue, reg.getNameAttr(), reg.getNameKind(),
2877 reg.getAnnotationsAttr(), reg.getInnerSymAttr(), reg.getForceable());
2881void RegResetOp::getCanonicalizationPatterns(RewritePatternSet &results,
2883 results.add<patterns::RegResetWithZeroReset, FoldResetMux>(
context);
2891 auto fieldIndex = portTy.getElementIndex(name);
2892 assert(fieldIndex &&
"missing field on memory port");
2895 for (
auto *op : port.getUsers()) {
2896 auto portAccess = cast<SubfieldOp>(op);
2897 if (fieldIndex != portAccess.getFieldIndex())
2902 value = conn.getSrc();
2912 auto portConst = value.getDefiningOp<ConstantOp>();
2915 return portConst.getValue().isZero();
2921 auto fieldIndex = portTy.getElementIndex(
data);
2922 assert(fieldIndex &&
"missing enable flag on memory port");
2924 for (
auto *op : port.getUsers()) {
2925 auto portAccess = cast<SubfieldOp>(op);
2926 if (fieldIndex != portAccess.getFieldIndex())
2928 if (!portAccess.use_empty())
2937 StringRef name, Value value) {
2939 auto fieldIndex = portTy.getElementIndex(name);
2940 assert(fieldIndex &&
"missing field on memory port");
2942 for (
auto *op : llvm::make_early_inc_range(port.getUsers())) {
2943 auto portAccess = cast<SubfieldOp>(op);
2944 if (fieldIndex != portAccess.getFieldIndex())
2946 rewriter.replaceAllUsesWith(portAccess, value);
2947 rewriter.eraseOp(portAccess);
2952static void erasePort(PatternRewriter &rewriter, Value port) {
2955 auto getClock = [&] {
2957 clock = SpecialConstantOp::create(rewriter, port.getLoc(),
2958 ClockType::get(rewriter.getContext()),
2967 for (
auto *op : port.getUsers()) {
2968 auto subfield = dyn_cast<SubfieldOp>(op);
2970 auto ty = port.getType();
2971 auto reg = RegOp::create(rewriter, port.getLoc(), ty, getClock());
2972 rewriter.replaceAllUsesWith(port, reg.getResult());
2981 for (
auto *accessOp : llvm::make_early_inc_range(port.getUsers())) {
2982 auto access = cast<SubfieldOp>(accessOp);
2983 for (
auto *user : llvm::make_early_inc_range(access->getUsers())) {
2984 auto connect = dyn_cast<FConnectLike>(user);
2985 if (connect && connect.getDest() == access) {
2986 rewriter.eraseOp(user);
2990 if (access.use_empty()) {
2991 rewriter.eraseOp(access);
2997 auto ty = access.getType();
2998 auto reg = RegOp::create(rewriter, access.getLoc(), ty, getClock());
2999 rewriter.replaceOp(access, reg.getResult());
3001 assert(port.use_empty() &&
"port should have no remaining uses");
3006struct FoldZeroWidthMemory :
public mlir::OpRewritePattern<MemOp> {
3007 using OpRewritePattern::OpRewritePattern;
3008 LogicalResult matchAndRewrite(MemOp mem,
3009 PatternRewriter &rewriter)
const override {
3014 mem.getDataType().getBitWidthOrSentinel() != 0)
3018 for (
auto port : mem.getResults())
3019 for (
auto *user : port.getUsers())
3020 if (!isa<SubfieldOp>(user))
3025 for (
auto port : mem.getResults()) {
3026 for (
auto *user : llvm::make_early_inc_range(port.getUsers())) {
3027 SubfieldOp sfop = cast<SubfieldOp>(user);
3028 StringRef fieldName = sfop.getFieldName();
3030 rewriter, sfop, sfop.getResult().getType())
3032 if (fieldName.ends_with(
"data")) {
3034 auto zero = firrtl::ConstantOp::create(
3035 rewriter, wire.getLoc(),
3037 MatchingConnectOp::create(rewriter, wire.getLoc(), wire, zero);
3041 rewriter.eraseOp(mem);
3047struct FoldReadOrWriteOnlyMemory :
public mlir::OpRewritePattern<MemOp> {
3048 using OpRewritePattern::OpRewritePattern;
3049 LogicalResult matchAndRewrite(MemOp mem,
3050 PatternRewriter &rewriter)
const override {
3053 bool isRead =
false, isWritten =
false;
3054 for (
unsigned i = 0; i < mem.getNumResults(); ++i) {
3055 switch (mem.getPortKind(i)) {
3056 case MemOp::PortKind::Read:
3061 case MemOp::PortKind::Write:
3066 case MemOp::PortKind::Debug:
3067 case MemOp::PortKind::ReadWrite:
3070 llvm_unreachable(
"unknown port kind");
3072 assert((!isWritten || !isRead) &&
"memory is in use");
3077 if (isRead && mem.getInit())
3080 for (
auto port : mem.getResults())
3083 rewriter.eraseOp(mem);
3089struct FoldUnusedPorts :
public mlir::OpRewritePattern<MemOp> {
3090 using OpRewritePattern::OpRewritePattern;
3091 LogicalResult matchAndRewrite(MemOp mem,
3092 PatternRewriter &rewriter)
const override {
3096 llvm::SmallBitVector deadPorts(mem.getNumResults());
3097 for (
auto [i, port] : llvm::enumerate(mem.getResults())) {
3099 if (!mem.getPortAnnotation(i).empty())
3103 auto kind = mem.getPortKind(i);
3104 if (kind == MemOp::PortKind::Debug)
3113 if (kind == MemOp::PortKind::Read &&
isPortUnused(port,
"data")) {
3118 if (deadPorts.none())
3122 SmallVector<Type> resultTypes;
3123 SmallVector<StringRef> portNames;
3124 SmallVector<Attribute> portAnnotations;
3125 for (
auto [i, port] : llvm::enumerate(mem.getResults())) {
3128 resultTypes.push_back(port.getType());
3129 portNames.push_back(mem.getPortName(i));
3130 portAnnotations.push_back(mem.getPortAnnotation(i));
3134 if (!resultTypes.empty())
3135 newOp = MemOp::create(
3136 rewriter, mem.getLoc(), resultTypes, mem.getReadLatency(),
3137 mem.getWriteLatency(), mem.getDepth(), mem.getRuw(),
3138 rewriter.getStrArrayAttr(portNames), mem.getName(), mem.getNameKind(),
3139 mem.getAnnotations(), rewriter.getArrayAttr(portAnnotations),
3140 mem.getInnerSymAttr(), mem.getInitAttr(), mem.getPrefixAttr());
3143 unsigned nextPort = 0;
3144 for (
auto [i, port] : llvm::enumerate(mem.getResults())) {
3148 rewriter.replaceAllUsesWith(port, newOp.getResult(nextPort++));
3151 rewriter.eraseOp(mem);
3157struct FoldReadWritePorts :
public mlir::OpRewritePattern<MemOp> {
3158 using OpRewritePattern::OpRewritePattern;
3159 LogicalResult matchAndRewrite(MemOp mem,
3160 PatternRewriter &rewriter)
const override {
3165 llvm::SmallBitVector deadReads(mem.getNumResults());
3166 for (
auto [i, port] : llvm::enumerate(mem.getResults())) {
3167 if (mem.getPortKind(i) != MemOp::PortKind::ReadWrite)
3169 if (!mem.getPortAnnotation(i).empty())
3176 if (deadReads.none())
3179 SmallVector<Type> resultTypes;
3180 SmallVector<StringRef> portNames;
3181 SmallVector<Attribute> portAnnotations;
3182 for (
auto [i, port] : llvm::enumerate(mem.getResults())) {
3184 resultTypes.push_back(
3185 MemOp::getTypeForPort(mem.getDepth(), mem.getDataType(),
3186 MemOp::PortKind::Write, mem.getMaskBits()));
3188 resultTypes.push_back(port.getType());
3190 portNames.push_back(mem.getPortName(i));
3191 portAnnotations.push_back(mem.getPortAnnotation(i));
3194 auto newOp = MemOp::create(
3195 rewriter, mem.getLoc(), resultTypes, mem.getReadLatency(),
3196 mem.getWriteLatency(), mem.getDepth(), mem.getRuw(),
3197 rewriter.getStrArrayAttr(portNames), mem.getName(), mem.getNameKind(),
3198 mem.getAnnotations(), rewriter.getArrayAttr(portAnnotations),
3199 mem.getInnerSymAttr(), mem.getInitAttr(), mem.getPrefixAttr());
3201 for (
unsigned i = 0, n = mem.getNumResults(); i < n; ++i) {
3202 auto result = mem.getResult(i);
3203 auto newResult = newOp.getResult(i);
3209 auto replace = [&](StringRef toName, StringRef fromName) {
3210 auto fromFieldIndex = resultPortTy.getElementIndex(fromName);
3211 assert(fromFieldIndex &&
"missing enable flag on memory port");
3213 auto toField = SubfieldOp::create(rewriter, newResult.getLoc(),
3215 for (
auto *op : llvm::make_early_inc_range(result.getUsers())) {
3216 auto fromField = cast<SubfieldOp>(op);
3217 if (fromFieldIndex != fromField.getFieldIndex())
3219 rewriter.replaceOp(fromField, toField.getResult());
3223 replace(
"addr",
"addr");
3224 replace(
"en",
"en");
3225 replace(
"clk",
"clk");
3226 replace(
"data",
"wdata");
3227 replace(
"mask",
"wmask");
3230 auto wmodeFieldIndex = resultPortTy.getElementIndex(
"wmode");
3231 for (
auto *op : llvm::make_early_inc_range(result.getUsers())) {
3232 auto wmodeField = cast<SubfieldOp>(op);
3233 if (wmodeFieldIndex != wmodeField.getFieldIndex())
3235 rewriter.replaceOpWithNewOp<WireOp>(wmodeField, wmodeField.getType());
3238 rewriter.replaceAllUsesWith(result, newResult);
3241 rewriter.eraseOp(mem);
3247struct FoldUnusedBits :
public mlir::OpRewritePattern<MemOp> {
3248 using OpRewritePattern::OpRewritePattern;
3250 LogicalResult matchAndRewrite(MemOp mem,
3251 PatternRewriter &rewriter)
const override {
3256 const auto &summary = mem.getSummary();
3257 if (summary.isMasked || summary.isSeqMem())
3263 auto width = type.getBitWidthOrSentinel();
3267 llvm::SmallBitVector usedBits(width);
3268 DenseMap<unsigned, unsigned> mapping;
3273 SmallVector<BitsPrimOp> readOps;
3274 auto findReadUsers = [&](Value port, StringRef field) -> LogicalResult {
3276 auto fieldIndex = portTy.getElementIndex(field);
3277 assert(fieldIndex &&
"missing data port");
3279 for (
auto *op : port.getUsers()) {
3280 auto portAccess = cast<SubfieldOp>(op);
3281 if (fieldIndex != portAccess.getFieldIndex())
3284 for (
auto *user : op->getUsers()) {
3285 auto bits = dyn_cast<BitsPrimOp>(user);
3289 usedBits.set(bits.getLo(), bits.getHi() + 1);
3293 mapping[bits.getLo()] = 0;
3294 readOps.push_back(bits);
3304 SmallVector<MatchingConnectOp> writeOps;
3305 auto findWriteUsers = [&](Value port, StringRef field) -> LogicalResult {
3307 auto fieldIndex = portTy.getElementIndex(field);
3308 assert(fieldIndex &&
"missing data port");
3310 for (
auto *op : port.getUsers()) {
3311 auto portAccess = cast<SubfieldOp>(op);
3312 if (fieldIndex != portAccess.getFieldIndex())
3319 writeOps.push_back(conn);
3325 for (
auto [i, port] : llvm::enumerate(mem.getResults())) {
3327 if (!mem.getPortAnnotation(i).empty())
3330 switch (mem.getPortKind(i)) {
3331 case MemOp::PortKind::Debug:
3334 case MemOp::PortKind::Write:
3335 if (failed(findWriteUsers(port,
"data")))
3338 case MemOp::PortKind::Read:
3339 if (failed(findReadUsers(port,
"data")))
3342 case MemOp::PortKind::ReadWrite:
3343 if (failed(findWriteUsers(port,
"wdata")))
3345 if (failed(findReadUsers(port,
"rdata")))
3349 llvm_unreachable(
"unknown port kind");
3353 if (usedBits.none())
3357 SmallVector<std::pair<unsigned, unsigned>> ranges;
3358 unsigned newWidth = 0;
3359 for (
int i = usedBits.find_first(); 0 <= i && i < width;) {
3360 int e = usedBits.find_next_unset(i);
3363 for (
int idx = i; idx < e; ++idx, ++newWidth) {
3364 if (
auto it = mapping.find(idx); it != mapping.end()) {
3365 it->second = newWidth;
3368 ranges.emplace_back(i, e - 1);
3369 i = e != width ? usedBits.find_next(e) : e;
3373 auto newType =
IntType::get(mem->getContext(), type.isSigned(), newWidth);
3374 SmallVector<Type> portTypes;
3375 for (
auto [i, port] : llvm::enumerate(mem.getResults())) {
3376 portTypes.push_back(
3377 MemOp::getTypeForPort(mem.getDepth(), newType, mem.getPortKind(i)));
3379 auto newMem = rewriter.replaceOpWithNewOp<MemOp>(
3380 mem, portTypes, mem.getReadLatency(), mem.getWriteLatency(),
3381 mem.getDepth(), mem.getRuw(), mem.getPortNames(), mem.getName(),
3382 mem.getNameKind(), mem.getAnnotations(), mem.getPortAnnotations(),
3383 mem.getInnerSymAttr(), mem.getInitAttr(), mem.getPrefixAttr());
3386 auto rewriteSubfield = [&](Value port, StringRef field) {
3388 auto fieldIndex = portTy.getElementIndex(field);
3389 assert(fieldIndex &&
"missing data port");
3391 rewriter.setInsertionPointAfter(newMem);
3392 auto newPortAccess =
3393 SubfieldOp::create(rewriter, port.getLoc(), port, field);
3395 for (
auto *op : llvm::make_early_inc_range(port.getUsers())) {
3396 auto portAccess = cast<SubfieldOp>(op);
3397 if (op == newPortAccess || fieldIndex != portAccess.getFieldIndex())
3399 rewriter.replaceOp(portAccess, newPortAccess.getResult());
3404 for (
auto [i, port] : llvm::enumerate(newMem.getResults())) {
3405 switch (newMem.getPortKind(i)) {
3406 case MemOp::PortKind::Debug:
3407 llvm_unreachable(
"cannot rewrite debug port");
3408 case MemOp::PortKind::Write:
3409 rewriteSubfield(port,
"data");
3411 case MemOp::PortKind::Read:
3412 rewriteSubfield(port,
"data");
3414 case MemOp::PortKind::ReadWrite:
3415 rewriteSubfield(port,
"rdata");
3416 rewriteSubfield(port,
"wdata");
3419 llvm_unreachable(
"unknown port kind");
3423 for (
auto readOp : readOps) {
3424 rewriter.setInsertionPointAfter(readOp);
3425 auto it = mapping.find(readOp.getLo());
3426 assert(it != mapping.end() &&
"bit op mapping not found");
3429 auto newReadValue = rewriter.createOrFold<BitsPrimOp>(
3430 readOp.getLoc(), readOp.getInput(),
3431 readOp.getHi() - readOp.getLo() + it->second, it->second);
3432 rewriter.replaceAllUsesWith(readOp, newReadValue);
3433 rewriter.eraseOp(readOp);
3437 for (
auto writeOp : writeOps) {
3438 Value source = writeOp.getSrc();
3439 rewriter.setInsertionPoint(writeOp);
3441 SmallVector<Value> slices;
3442 for (
auto &[start, end] : llvm::reverse(ranges)) {
3443 Value slice = rewriter.createOrFold<BitsPrimOp>(writeOp.getLoc(),
3444 source, end, start);
3445 slices.push_back(slice);
3449 rewriter.createOrFold<CatPrimOp>(writeOp.getLoc(), slices);
3455 if (type.isSigned())
3457 rewriter.createOrFold<AsSIntPrimOp>(writeOp.getLoc(), catOfSlices);
3459 rewriter.replaceOpWithNewOp<MatchingConnectOp>(writeOp, writeOp.getDest(),
3468struct FoldRegMems :
public mlir::OpRewritePattern<MemOp> {
3469 using OpRewritePattern::OpRewritePattern;
3470 LogicalResult matchAndRewrite(MemOp mem,
3471 PatternRewriter &rewriter)
const override {
3472 const FirMemory &
info = mem.getSummary();
3476 auto ty = mem.getDataType();
3477 auto loc = mem.getLoc();
3478 auto *block = mem->getBlock();
3482 SmallPtrSet<Operation *, 8> connects;
3483 SmallVector<SubfieldOp> portAccesses;
3484 for (
auto [i, port] : llvm::enumerate(mem.getResults())) {
3485 if (!mem.getPortAnnotation(i).empty())
3488 auto collect = [&, port = port](ArrayRef<StringRef> fields) {
3490 for (
auto field : fields) {
3491 auto fieldIndex = portTy.getElementIndex(field);
3492 assert(fieldIndex &&
"missing field on memory port");
3494 for (
auto *op : port.getUsers()) {
3495 auto portAccess = cast<SubfieldOp>(op);
3496 if (fieldIndex != portAccess.getFieldIndex())
3498 portAccesses.push_back(portAccess);
3499 for (
auto *user : portAccess->getUsers()) {
3500 auto conn = dyn_cast<FConnectLike>(user);
3503 connects.insert(conn);
3510 switch (mem.getPortKind(i)) {
3511 case MemOp::PortKind::Debug:
3513 case MemOp::PortKind::Read:
3514 if (failed(collect({
"clk",
"en",
"addr"})))
3517 case MemOp::PortKind::Write:
3518 if (failed(collect({
"clk",
"en",
"addr",
"data",
"mask"})))
3521 case MemOp::PortKind::ReadWrite:
3522 if (failed(collect({
"clk",
"en",
"addr",
"wmode",
"wdata",
"wmask"})))
3528 if (!portClock || (clock && portClock != clock))
3534 rewriter.setInsertionPointAfter(mem);
3535 auto memWire = WireOp::create(rewriter, loc, ty).getResult();
3541 rewriter.setInsertionPointToEnd(block);
3543 RegOp::create(rewriter, loc, ty, clock, mem.getName()).getResult();
3546 MatchingConnectOp::create(rewriter, loc, memWire, memReg);
3550 auto pipeline = [&](Value value, Value clock,
const Twine &name,
3552 for (
unsigned i = 0; i < latency; ++i) {
3553 std::string regName;
3555 llvm::raw_string_ostream os(regName);
3556 os << mem.getName() <<
"_" << name <<
"_" << i;
3558 auto reg = RegOp::create(rewriter, mem.getLoc(), value.getType(), clock,
3559 rewriter.getStringAttr(regName))
3561 MatchingConnectOp::create(rewriter, value.getLoc(), reg, value);
3567 const unsigned writeStages =
info.writeLatency - 1;
3572 SmallVector<std::tuple<Value, Value, Value>> writes;
3573 for (
auto [i, port] : llvm::enumerate(mem.getResults())) {
3575 StringRef name = mem.getPortName(i);
3577 auto portPipeline = [&, port = port](StringRef field,
unsigned stages) {
3580 return pipeline(value, portClock, name +
"_" + field, stages);
3583 switch (mem.getPortKind(i)) {
3584 case MemOp::PortKind::Debug:
3585 llvm_unreachable(
"unknown port kind");
3586 case MemOp::PortKind::Read: {
3594 case MemOp::PortKind::Write: {
3595 auto data = portPipeline(
"data", writeStages);
3596 auto en = portPipeline(
"en", writeStages);
3597 auto mask = portPipeline(
"mask", writeStages);
3601 case MemOp::PortKind::ReadWrite: {
3606 auto wdata = portPipeline(
"wdata", writeStages);
3607 auto wmask = portPipeline(
"wmask", writeStages);
3612 auto wen = AndPrimOp::create(rewriter, port.getLoc(),
en,
wmode);
3614 pipeline(wen, portClock, name +
"_wen", writeStages);
3615 writes.emplace_back(
wdata, wenPipelined,
wmask);
3622 Value next = memReg;
3628 Location loc = mem.getLoc();
3629 unsigned maskGran =
info.dataWidth /
info.maskBits;
3630 SmallVector<Value> chunks;
3631 for (
unsigned i = 0; i <
info.maskBits; ++i) {
3632 unsigned hi = (i + 1) * maskGran - 1;
3633 unsigned lo = i * maskGran;
3635 auto dataPart = rewriter.createOrFold<BitsPrimOp>(loc,
data, hi, lo);
3636 auto nextPart = rewriter.createOrFold<BitsPrimOp>(loc, next, hi, lo);
3637 auto bit = rewriter.createOrFold<BitsPrimOp>(loc,
mask, i, i);
3638 auto chunk = MuxPrimOp::create(rewriter, loc, bit, dataPart, nextPart);
3639 chunks.push_back(chunk);
3642 std::reverse(chunks.begin(), chunks.end());
3643 masked = rewriter.createOrFold<CatPrimOp>(loc, chunks);
3644 next = MuxPrimOp::create(rewriter, next.getLoc(),
en, masked, next);
3646 Value typedNext = rewriter.createOrFold<BitCastOp>(next.getLoc(), ty, next);
3647 MatchingConnectOp::create(rewriter, memReg.getLoc(), memReg, typedNext);
3650 for (Operation *conn : connects)
3651 rewriter.eraseOp(conn);
3652 for (
auto portAccess : portAccesses)
3653 rewriter.eraseOp(portAccess);
3654 rewriter.eraseOp(mem);
3661void MemOp::getCanonicalizationPatterns(RewritePatternSet &results,
3664 .insert<FoldZeroWidthMemory, FoldReadOrWriteOnlyMemory,
3665 FoldReadWritePorts, FoldUnusedPorts, FoldUnusedBits, FoldRegMems>(
3685 auto mux = dyn_cast_or_null<MuxPrimOp>(con.getSrc().getDefiningOp());
3688 auto *high = mux.getHigh().getDefiningOp();
3689 auto *low = mux.getLow().getDefiningOp();
3691 auto constOp = dyn_cast_or_null<ConstantOp>(high);
3698 bool constReg =
false;
3700 if (constOp && low == reg)
3702 else if (dyn_cast_or_null<ConstantOp>(low) && high == reg) {
3704 constOp = dyn_cast<ConstantOp>(low);
3711 if (!isa<BlockArgument>(mux.getSel()) && !constReg)
3715 auto regTy = reg.getResult().getType();
3716 if (con.getDest().getType() != regTy || con.getSrc().getType() != regTy ||
3717 mux.getHigh().getType() != regTy || mux.getLow().getType() != regTy ||
3718 regTy.getBitWidthOrSentinel() < 0)
3725 if (constReg && !
preservesInitial(reg.getInitialAttr(), constOp.getValue()))
3729 if (constOp != &con->getBlock()->front())
3730 constOp->moveBefore(&con->getBlock()->front());
3733 SmallVector<NamedAttribute, 2>
attrs(reg->getDialectAttrs());
3735 rewriter, reg, reg.getResult().getType(), reg.getClockVal(),
3736 mux.getSel(), mux.getHigh(), reg.getNameAttr(), reg.getNameKindAttr(),
3737 reg.getAnnotationsAttr(), reg.getInnerSymAttr(), reg.getForceableAttr(),
3738 reg.getInitialAttr());
3739 newReg->setDialectAttrs(
attrs);
3741 auto pt = rewriter.saveInsertionPoint();
3742 rewriter.setInsertionPoint(con);
3743 auto v = constReg ? (Value)constOp.getResult() : (Value)mux.getLow();
3745 rewriter.restoreInsertionPoint(pt);
3749LogicalResult RegOp::canonicalize(RegOp op, PatternRewriter &rewriter) {
3750 if (!
hasDontTouch(op.getOperation()) && !op.isForceable() &&
3766 PatternRewriter &rewriter,
3769 if (
auto constant = enable.getDefiningOp<firrtl::ConstantOp>()) {
3770 if (constant.getValue().isZero()) {
3771 rewriter.eraseOp(op);
3777 if (
auto constant = predicate.getDefiningOp<firrtl::ConstantOp>()) {
3778 if (constant.getValue().isZero() == eraseIfZero) {
3779 rewriter.eraseOp(op);
3787template <
class Op,
bool EraseIfZero = false>
3789 PatternRewriter &rewriter) {
3794void AssertOp::getCanonicalizationPatterns(RewritePatternSet &results,
3797 results.add<patterns::AssertXWhenX>(
context);
3800void AssumeOp::getCanonicalizationPatterns(RewritePatternSet &results,
3803 results.add<patterns::AssumeXWhenX>(
context);
3806void UnclockedAssumeIntrinsicOp::getCanonicalizationPatterns(
3807 RewritePatternSet &results, MLIRContext *
context) {
3809 results.add<patterns::UnclockedAssumeIntrinsicXWhenX>(
context);
3812void CoverOp::getCanonicalizationPatterns(RewritePatternSet &results,
3821LogicalResult InvalidValueOp::canonicalize(InvalidValueOp op,
3822 PatternRewriter &rewriter) {
3824 if (op.use_empty()) {
3825 rewriter.eraseOp(op);
3832 if (op->hasOneUse() &&
3833 (isa<BitsPrimOp, HeadPrimOp, ShrPrimOp, TailPrimOp, SubfieldOp,
3834 SubindexOp, AsSIntPrimOp, AsUIntPrimOp, NotPrimOp, BitCastOp>(
3835 *op->user_begin()) ||
3836 (isa<CvtPrimOp>(*op->user_begin()) &&
3838 (isa<AndRPrimOp, XorRPrimOp, OrRPrimOp>(*op->user_begin()) &&
3840 .getBitWidthOrSentinel() > 0))) {
3841 auto *modop = *op->user_begin();
3842 auto inv = InvalidValueOp::create(rewriter, op.getLoc(),
3843 modop->getResult(0).getType());
3844 rewriter.replaceAllOpUsesWith(modop, inv);
3845 rewriter.eraseOp(modop);
3846 rewriter.eraseOp(op);
3852OpFoldResult InvalidValueOp::fold(FoldAdaptor adaptor) {
3853 if (getType().getBitWidthOrSentinel() == 0 && isa<IntType>(getType()))
3854 return getIntAttr(getType(), APInt(0, 0, isa<SIntType>(getType())));
3862OpFoldResult ClockGateIntrinsicOp::fold(FoldAdaptor adaptor) {
3871 return BoolAttr::get(getContext(),
false);
3875 return BoolAttr::get(getContext(),
false);
3880LogicalResult ClockGateIntrinsicOp::canonicalize(ClockGateIntrinsicOp op,
3881 PatternRewriter &rewriter) {
3883 if (
auto testEnable = op.getTestEnable()) {
3884 if (
auto constOp = testEnable.getDefiningOp<
ConstantOp>()) {
3885 if (constOp.getValue().isZero()) {
3886 rewriter.modifyOpInPlace(op,
3887 [&] { op.getTestEnableMutable().clear(); });
3903 auto forceable = op.getRef().getDefiningOp<Forceable>();
3904 if (!forceable || !forceable.isForceable() ||
3905 op.getRef() != forceable.getDataRef() ||
3906 op.getType() != forceable.getDataType())
3908 rewriter.replaceAllUsesWith(op, forceable.getData());
3912void RefResolveOp::getCanonicalizationPatterns(RewritePatternSet &results,
3914 results.insert<patterns::RefResolveOfRefSend>(
context);
3918OpFoldResult RefCastOp::fold(FoldAdaptor adaptor) {
3920 if (getInput().getType() == getType())
3926 auto constOp = operand.getDefiningOp<ConstantOp>();
3927 return constOp && constOp.getValue().isZero();
3930template <
typename Op>
3933 rewriter.eraseOp(op);
3939void RefForceOp::getCanonicalizationPatterns(RewritePatternSet &results,
3943void RefForceInitialOp::getCanonicalizationPatterns(RewritePatternSet &results,
3947void RefReleaseOp::getCanonicalizationPatterns(RewritePatternSet &results,
3951void RefReleaseInitialOp::getCanonicalizationPatterns(
3952 RewritePatternSet &results, MLIRContext *
context) {
3960OpFoldResult HasBeenResetIntrinsicOp::fold(FoldAdaptor adaptor) {
3966 if (adaptor.getReset())
3971 if (
isUInt1(getReset().getType()) && adaptor.getClock())
3984 [&](
auto ty) ->
bool {
return isTypeEmpty(ty.getElementType()); })
3985 .Case<BundleType>([&](
auto ty) ->
bool {
3986 for (
auto elem : ty.getElements())
3991 .Case<IntType>([&](
auto ty) {
return ty.getWidth() == 0; })
3992 .Default([](
auto) ->
bool {
return false; });
3995LogicalResult FPGAProbeIntrinsicOp::canonicalize(FPGAProbeIntrinsicOp op,
3996 PatternRewriter &rewriter) {
4004 rewriter.eraseOp(op);
4012LogicalResult LayerBlockOp::canonicalize(LayerBlockOp op,
4013 PatternRewriter &rewriter) {
4016 if (op.getBody()->empty()) {
4017 rewriter.eraseOp(op);
4028OpFoldResult UnsafeDomainCastOp::fold(FoldAdaptor adaptor) {
4030 if (getDomains().
empty())
assert(baseType &&"element must be base type")
static SmallVector< T > concat(const SmallVectorImpl< T > &a, const SmallVectorImpl< T > &b)
Returns a new vector containing the concatenation of vectors a and b.
static std::unique_ptr< Context > context
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 IntegerAttr foldFIntegerBinaryBitwise(MLIRContext *context, Attribute lhsAttr, Attribute rhsAttr, const function_ref< APInt(const APInt &, const APInt &)> &evaluate)
Fold bitwise operations on FIRRTL arbitrary precision signed integer properties.
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 std::optional< bool > getBoolValue(Attribute attr)
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)
false return failure()
Parse an optional keyword or string and set instance into 'result'.
if(parseOptionalKeywordOrOptionalString(parser, result.rawName, found)) return failure()
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.
BaseTy type_cast(Type type)
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.
bool preservesInitial(IntegerAttr initial, std::optional< APInt > foldedValue=std::nullopt)
Return true if replacing a register carrying the time-zero initial value with foldedValue does not ch...
BaseTy type_dyn_cast(Type type)
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.
BaseTy type_cast(Type type)
BaseTy type_dyn_cast(Type type)
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(BitsPrimOp bits, mlir::PatternRewriter &rewriter) const override