13#include "mlir/IR/Diagnostics.h"
14#include "mlir/IR/Matchers.h"
15#include "mlir/IR/PatternMatch.h"
16#include "llvm/ADT/SetVector.h"
17#include "llvm/ADT/SmallBitVector.h"
18#include "llvm/ADT/TypeSwitch.h"
19#include "llvm/Support/KnownBits.h"
24using namespace matchers;
28 return llvm::any_of(op->getOperands(), [op](
auto operand) {
29 return operand.getDefiningOp() == op;
39 ArrayRef<Value> operands, OpBuilder &builder) {
40 OperationState state(loc, name);
41 state.addOperands(operands);
42 state.addTypes(operands[0].getType());
43 return builder.create(state)->getResult(0);
47 return IntegerAttr::get(IntegerType::get(
context, value.getBitWidth()),
53 if (
auto concat = v.getDefiningOp<
ConcatOp>()) {
54 for (
auto op : concat.getOperands())
56 }
else if (
auto repl = v.getDefiningOp<ReplicateOp>()) {
57 for (
size_t i = 0, e = repl.getMultiple(); i != e; ++i)
68 return op->hasAttr(
"sv.attributes");
72template <
typename SubType>
73struct ComplementMatcher {
75 ComplementMatcher(SubType lhs) : lhs(std::move(lhs)) {}
76 bool match(Operation *op) {
77 auto xorOp = dyn_cast<XorOp>(op);
78 return xorOp && xorOp.isBinaryNot() &&
79 mlir::detail::matchOperandOrValueAtIndex(op, 0, lhs);
84template <
typename SubType>
85static inline ComplementMatcher<SubType>
m_Complement(
const SubType &subExpr) {
86 return ComplementMatcher<SubType>(subExpr);
92 assert((isa<AndOp, OrOp, XorOp, AddOp, MulOp>(op) &&
93 "must be commutative operations"));
94 if (op->hasOneUse()) {
95 auto *user = *op->getUsers().begin();
96 return user->getName() == op->getName() &&
97 op->getAttrOfType<UnitAttr>(
"twoState") ==
98 user->getAttrOfType<UnitAttr>(
"twoState") &&
99 op->getBlock() == user->getBlock();
114 auto inputs = op->getOperands();
116 SmallVector<Value, 4> newOperands;
117 SmallVector<Location, 4> newLocations{op->getLoc()};
118 newOperands.reserve(inputs.size());
120 decltype(inputs.begin()) current, end;
123 SmallVector<Element> worklist;
124 worklist.push_back({inputs.begin(), inputs.end()});
125 bool binFlag = op->hasAttrOfType<UnitAttr>(
"twoState");
126 bool changed =
false;
127 while (!worklist.empty()) {
128 auto &element = worklist.back();
131 if (element.current == element.end) {
136 Value value = *element.current++;
137 auto *flattenOp = value.getDefiningOp();
140 if (!flattenOp || flattenOp->getName() != op->getName() ||
141 flattenOp == op || binFlag != op->hasAttrOfType<UnitAttr>(
"twoState") ||
142 flattenOp->getBlock() != op->getBlock()) {
143 newOperands.push_back(value);
148 if (!value.hasOneUse()) {
156 if (flattenOp->getNumOperands() != 2 || !isa<AndOp, OrOp, XorOp>(op) ||
159 newOperands.push_back(value);
167 auto flattenOpInputs = flattenOp->getOperands();
168 worklist.push_back({flattenOpInputs.begin(), flattenOpInputs.end()});
169 newLocations.push_back(flattenOp->getLoc());
175 Value result =
createGenericOp(FusedLoc::get(op->getContext(), newLocations),
176 op->getName(), newOperands, rewriter);
178 result.getDefiningOp()->setAttr(
"twoState", rewriter.getUnitAttr());
186static std::pair<size_t, size_t>
188 size_t originalOpWidth) {
189 auto users = op->getUsers();
191 "getLowestBitAndHighestBitRequired cannot operate on "
192 "a empty list of uses.");
196 size_t lowestBitRequired = narrowTrailingBits ? originalOpWidth - 1 : 0;
197 size_t highestBitRequired = 0;
199 for (
auto *user : users) {
200 if (
auto extractOp = dyn_cast<ExtractOp>(user)) {
201 size_t lowBit = extractOp.getLowBit();
203 cast<IntegerType>(extractOp.getType()).getWidth() + lowBit - 1;
204 highestBitRequired = std::max(highestBitRequired, highBit);
205 lowestBitRequired = std::min(lowestBitRequired, lowBit);
209 highestBitRequired = originalOpWidth - 1;
210 lowestBitRequired = 0;
214 return {lowestBitRequired, highestBitRequired};
219 PatternRewriter &rewriter) {
220 IntegerType opType = dyn_cast<IntegerType>(op.getResult().getType());
226 if (range.second + 1 == opType.getWidth() && range.first == 0)
229 SmallVector<Value> args;
230 auto newType = rewriter.getIntegerType(range.second - range.first + 1);
231 for (
auto inop : op.getOperands()) {
233 if (inop.getType() != op.getType())
234 args.push_back(inop);
236 args.push_back(rewriter.createOrFold<
ExtractOp>(inop.getLoc(), newType,
239 auto newop = OpTy::create(rewriter, op.getLoc(), newType, args);
240 newop->setDialectAttrs(op->getDialectAttrs());
241 if (op.getTwoState())
242 newop.setTwoState(
true);
244 Value newResult = newop.getResult();
246 newResult = rewriter.createOrFold<
ConcatOp>(
247 op.getLoc(), newResult,
249 APInt::getZero(range.first)));
250 if (range.second + 1 < opType.getWidth())
251 newResult = rewriter.createOrFold<
ConcatOp>(
254 rewriter, op.getLoc(),
255 APInt::getZero(opType.getWidth() - range.second - 1)),
257 rewriter.replaceOp(op, newResult);
265OpFoldResult ReplicateOp::fold(FoldAdaptor adaptor) {
270 if (cast<IntegerType>(getType()).
getWidth() ==
271 getInput().getType().getIntOrFloatBitWidth())
275 if (
auto input = dyn_cast_or_null<IntegerAttr>(adaptor.getInput())) {
276 if (input.getValue().getBitWidth() == 1) {
277 if (input.getValue().isZero())
279 APInt::getZero(cast<IntegerType>(getType()).
getWidth()),
282 APInt::getAllOnes(cast<IntegerType>(getType()).
getWidth()),
286 APInt result = APInt::getZeroWidth();
287 for (
auto i = getMultiple(); i != 0; --i)
288 result = result.concat(input.getValue());
295OpFoldResult ParityOp::fold(FoldAdaptor adaptor) {
300 if (
auto input = dyn_cast_or_null<IntegerAttr>(adaptor.getInput()))
301 return getIntAttr(APInt(1, input.getValue().popcount() & 1), getContext());
304 if (hw::getBitWidth(getInput().getType()) == 1)
310LogicalResult ParityOp::canonicalize(
ParityOp op, PatternRewriter &rewriter) {
315 auto isParityZero = [](Value v) {
317 return matchPattern(v, m_ConstantInt(&value)) && value.popcount() % 2 == 0;
322 auto concat = op.getInput().getDefiningOp<
ConcatOp>();
326 auto operands = concat.getInputs();
327 if (operands.size() != 2)
330 if (isParityZero(operands[0])) {
331 replaceOpWithNewOpAndCopyNamehint<ParityOp>(rewriter, op, operands[1],
336 if (isParityZero(operands[1])) {
337 replaceOpWithNewOpAndCopyNamehint<ParityOp>(rewriter, op, operands[0],
352 hw::PEO paramOpcode) {
353 assert(operands.size() == 2 &&
"binary op takes two operands");
354 if (!operands[0] || !operands[1])
359 return hw::ParamExprAttr::get(paramOpcode, cast<TypedAttr>(operands[0]),
360 cast<TypedAttr>(operands[1]));
363OpFoldResult ShlOp::fold(FoldAdaptor adaptor) {
367 if (
auto rhs = dyn_cast_or_null<IntegerAttr>(adaptor.getRhs())) {
368 if (rhs.getValue().isZero())
369 return getOperand(0);
371 unsigned width = getType().getIntOrFloatBitWidth();
372 if (rhs.getValue().uge(width))
373 return getIntAttr(APInt::getZero(width), getContext());
378LogicalResult ShlOp::canonicalize(
ShlOp op, PatternRewriter &rewriter) {
384 if (!matchPattern(op.getRhs(), m_ConstantInt(&value)))
387 unsigned width = cast<IntegerType>(op.getLhs().getType()).getWidth();
388 if (value.ugt(width))
390 unsigned shift = value.getZExtValue();
393 if (width <= shift || shift == 0)
403 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(rewriter, op, extract, zeros);
407OpFoldResult ShrUOp::fold(FoldAdaptor adaptor) {
411 if (
auto rhs = dyn_cast_or_null<IntegerAttr>(adaptor.getRhs())) {
412 if (rhs.getValue().isZero())
413 return getOperand(0);
415 unsigned width = getType().getIntOrFloatBitWidth();
416 if (rhs.getValue().uge(width))
417 return getIntAttr(APInt::getZero(width), getContext());
422LogicalResult ShrUOp::canonicalize(
ShrUOp op, PatternRewriter &rewriter) {
428 if (!matchPattern(op.getRhs(), m_ConstantInt(&value)))
431 unsigned width = cast<IntegerType>(op.getLhs().getType()).getWidth();
432 if (value.ugt(width))
434 unsigned shift = value.getZExtValue();
437 if (width <= shift || shift == 0)
447 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(rewriter, op, zeros, extract);
451OpFoldResult ShrSOp::fold(FoldAdaptor adaptor) {
455 if (
auto rhs = dyn_cast_or_null<IntegerAttr>(adaptor.getRhs()))
456 if (rhs.getValue().isZero())
457 return getOperand(0);
461LogicalResult ShrSOp::canonicalize(
ShrSOp op, PatternRewriter &rewriter) {
467 if (!matchPattern(op.getRhs(), m_ConstantInt(&value)))
470 unsigned width = cast<IntegerType>(op.getLhs().getType()).getWidth();
471 if (value.ugt(width))
473 unsigned shift = value.getZExtValue();
476 rewriter.createOrFold<
ExtractOp>(op.getLoc(), op.getLhs(), width - 1, 1);
477 auto sext = rewriter.createOrFold<ReplicateOp>(op.getLoc(), topbit, shift);
479 if (width == shift) {
487 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(rewriter, op, sext, extract);
495OpFoldResult ExtractOp::fold(FoldAdaptor adaptor) {
500 if (getInput().getType() == getType())
504 if (
auto input = dyn_cast_or_null<IntegerAttr>(adaptor.getInput())) {
505 unsigned dstWidth = cast<IntegerType>(getType()).getWidth();
506 return getIntAttr(input.getValue().lshr(getLowBit()).trunc(dstWidth),
522 PatternRewriter &rewriter,
523 ArrayRef<size_t> prefixWidths = {}) {
524 auto concatInputs = innerCat.getInputs();
525 size_t numOperands = concatInputs.size();
526 size_t lowBit = op.getLowBit();
531 if (!prefixWidths.empty()) {
534 std::upper_bound(prefixWidths.begin(), prefixWidths.end(), lowBit);
535 assert(it != prefixWidths.end());
536 firstIdx = it - prefixWidths.begin();
537 beginOfFirst = (firstIdx > 0) ? prefixWidths[firstIdx - 1] : 0;
542 for (
size_t i = 0; i < numOperands; ++i) {
544 concatInputs[numOperands - 1 - i].getType().getIntOrFloatBitWidth();
545 if (lowBit < beginOfFirst + w) {
553 SmallVector<Value> reverseConcatArgs;
554 size_t widthRemaining = op.getType().getIntOrFloatBitWidth();
555 size_t extractLo = lowBit - beginOfFirst;
560 for (
size_t i = firstIdx; widthRemaining != 0 && i < numOperands; ++i) {
561 Value concatArg = concatInputs[numOperands - 1 - i];
562 size_t operandWidth = concatArg.getType().getIntOrFloatBitWidth();
563 size_t widthToConsume = std::min(widthRemaining, operandWidth - extractLo);
565 if (widthToConsume == operandWidth && extractLo == 0) {
566 reverseConcatArgs.push_back(concatArg);
568 auto resultType = IntegerType::get(rewriter.getContext(), widthToConsume);
570 rewriter, op.getLoc(), resultType, concatArg, extractLo));
573 widthRemaining -= widthToConsume;
578 if (reverseConcatArgs.size() == 1) {
581 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(
582 rewriter, op, SmallVector<Value>(llvm::reverse(reverseConcatArgs)));
589 PatternRewriter &rewriter) {
590 auto extractResultWidth = cast<IntegerType>(op.getType()).getWidth();
591 auto replicateEltWidth =
592 replicate.getOperand().getType().getIntOrFloatBitWidth();
596 if (op.getLowBit() % replicateEltWidth == 0 &&
597 extractResultWidth % replicateEltWidth == 0) {
598 replaceOpWithNewOpAndCopyNamehint<ReplicateOp>(rewriter, op, op.getType(),
599 replicate.getOperand());
605 if (op.getLowBit() % replicateEltWidth + extractResultWidth <=
607 replaceOpWithNewOpAndCopyNamehint<ExtractOp>(
608 rewriter, op, op.getType(), replicate.getOperand(),
609 op.getLowBit() % replicateEltWidth);
618LogicalResult ExtractOp::canonicalize(
ExtractOp op, PatternRewriter &rewriter) {
621 auto *inputOp = op.getInput().getDefiningOp();
628 .extractBits(cast<IntegerType>(op.getType()).getWidth(),
630 if (knownBits.isConstant()) {
631 replaceOpWithNewOpAndCopyNamehint<hw::ConstantOp>(rewriter, op,
632 knownBits.getConstant());
638 if (
auto innerExtract = dyn_cast_or_null<ExtractOp>(inputOp)) {
639 replaceOpWithNewOpAndCopyNamehint<ExtractOp>(
640 rewriter, op, op.getType(), innerExtract.getInput(),
641 innerExtract.getLowBit() + op.getLowBit());
646 if (
auto innerCat = dyn_cast_or_null<ConcatOp>(inputOp))
650 if (
auto replicate = dyn_cast_or_null<ReplicateOp>(inputOp))
656 if (inputOp && inputOp->getNumOperands() == 2 &&
657 isa<AndOp, OrOp, XorOp>(inputOp)) {
658 if (
auto cstRHS = inputOp->getOperand(1).getDefiningOp<
hw::ConstantOp>()) {
659 auto extractedCst = cstRHS.getValue().extractBits(
660 cast<IntegerType>(op.getType()).getWidth(), op.getLowBit());
661 if (isa<OrOp, XorOp>(inputOp) && extractedCst.isZero()) {
662 replaceOpWithNewOpAndCopyNamehint<ExtractOp>(
663 rewriter, op, op.getType(), inputOp->getOperand(0), op.getLowBit());
671 if (isa<AndOp>(inputOp)) {
674 unsigned lz = extractedCst.countLeadingZeros();
675 unsigned tz = extractedCst.countTrailingZeros();
676 unsigned pop = extractedCst.popcount();
677 if (extractedCst.getBitWidth() - lz - tz == pop) {
678 auto resultTy = rewriter.getIntegerType(pop);
679 SmallVector<Value> resultElts;
682 APInt::getZero(lz)));
683 resultElts.push_back(rewriter.createOrFold<
ExtractOp>(
684 op.getLoc(), resultTy, inputOp->getOperand(0),
685 op.getLowBit() + tz));
688 APInt::getZero(tz)));
689 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(rewriter, op, resultElts);
698 if (cast<IntegerType>(op.getType()).getWidth() == 1 && inputOp)
699 if (
auto shlOp = dyn_cast<ShlOp>(inputOp)) {
701 if (shlOp->hasOneUse())
703 if (lhsCst.getValue().isOne()) {
705 rewriter, shlOp.getLoc(),
706 APInt(lhsCst.getValue().getBitWidth(), op.getLowBit()));
707 replaceOpWithNewOpAndCopyNamehint<ICmpOp>(
708 rewriter, op, ICmpPredicate::eq, shlOp->getOperand(1), newCst,
724 hw::PEO paramOpcode) {
725 assert(operands.size() > 1 &&
"caller should handle one-operand case");
728 if (!operands[1] || !operands[0])
732 if (llvm::all_of(operands.drop_front(2),
733 [&](Attribute in) { return !!in; })) {
734 SmallVector<mlir::TypedAttr> typedOperands;
735 typedOperands.reserve(operands.size());
736 for (
auto operand : operands) {
737 if (
auto typedOperand = dyn_cast<mlir::TypedAttr>(operand))
738 typedOperands.push_back(typedOperand);
742 if (typedOperands.size() == operands.size())
743 return hw::ParamExprAttr::get(paramOpcode, typedOperands);
759 size_t concatIdx,
const APInt &cst,
760 PatternRewriter &rewriter) {
761 auto concatOp = logicalOp->getOperand(concatIdx).getDefiningOp<
ConcatOp>();
762 assert((isa<AndOp, OrOp, XorOp>(logicalOp) && concatOp));
767 llvm::any_of(concatOp->getOperands(), [&](Value operand) ->
bool {
768 auto *operandOp = operand.getDefiningOp();
773 if (isa<hw::ConstantOp>(operandOp))
777 return operandOp->getName() == logicalOp->getName() &&
778 operandOp->hasOneUse() && operandOp->getNumOperands() != 0 &&
779 operandOp->getOperands().back().getDefiningOp<hw::ConstantOp>();
787 auto createLogicalOp = [&](ArrayRef<Value> operands) -> Value {
788 return createGenericOp(logicalOp->getLoc(), logicalOp->getName(), operands,
795 SmallVector<Value> newConcatOperands;
796 newConcatOperands.reserve(concatOp->getNumOperands());
799 size_t nextOperandBit = concatOp.getType().getIntOrFloatBitWidth();
800 for (Value operand : concatOp->getOperands()) {
801 size_t operandWidth = operand.getType().getIntOrFloatBitWidth();
802 nextOperandBit -= operandWidth;
806 cst.lshr(nextOperandBit).trunc(operandWidth));
808 newConcatOperands.push_back(createLogicalOp({operand, eltCst}));
813 ConcatOp::create(rewriter, concatOp.getLoc(), newConcatOperands);
817 if (logicalOp->getNumOperands() > 2) {
818 auto origOperands = logicalOp->getOperands();
819 SmallVector<Value> operands;
821 operands.append(origOperands.begin(), origOperands.begin() + concatIdx);
823 operands.append(origOperands.begin() + concatIdx + 1,
824 origOperands.begin() + (origOperands.size() - 1));
826 operands.push_back(newResult);
827 newResult = createLogicalOp(operands);
837 llvm::SmallDenseSet<std::tuple<ICmpPredicate, Value, Value>> seenPredicates;
839 for (
auto op : operands) {
840 if (
auto icmpOp = op.getDefiningOp<ICmpOp>();
841 icmpOp && icmpOp.getTwoState()) {
842 auto predicate = icmpOp.getPredicate();
843 auto lhs = icmpOp.getLhs();
844 auto rhs = icmpOp.getRhs();
845 if (seenPredicates.contains(
846 {ICmpOp::getNegatedPredicate(predicate), lhs, rhs}))
849 seenPredicates.insert({predicate, lhs, rhs});
855OpFoldResult AndOp::fold(FoldAdaptor adaptor) {
859 APInt value = APInt::getAllOnes(cast<IntegerType>(getType()).
getWidth());
861 auto inputs = adaptor.getInputs();
864 for (
auto operand : inputs) {
865 auto attr = dyn_cast_or_null<IntegerAttr>(operand);
868 value &= attr.getValue();
874 if (inputs.size() == 2)
875 if (
auto intAttr = dyn_cast_or_null<IntegerAttr>(inputs[1]))
876 if (intAttr.getValue().isAllOnes())
877 return getInputs()[0];
880 if (llvm::all_of(getInputs(),
881 [&](
auto in) {
return in == this->getInputs()[0]; }))
882 return getInputs()[0];
885 for (Value arg : getInputs()) {
888 for (Value arg2 : getInputs())
891 APInt::getZero(cast<IntegerType>(getType()).
getWidth()),
912template <
typename Op>
914 if (!op.getType().isInteger(1))
917 auto inputs = op.getInputs();
918 size_t size = inputs.size();
920 auto sourceOp = inputs[0].template getDefiningOp<ExtractOp>();
923 Value source = sourceOp.getOperand();
926 if (size != source.getType().getIntOrFloatBitWidth())
930 llvm::BitVector bits(size);
931 bits.set(sourceOp.getLowBit());
933 for (
size_t i = 1; i != size; ++i) {
934 auto extractOp = inputs[i].template getDefiningOp<ExtractOp>();
935 if (!extractOp || extractOp.getOperand() != source)
937 bits.set(extractOp.getLowBit());
940 return bits.all() ? source : Value();
947template <
typename Op>
950 constexpr unsigned limit = 3;
951 auto inputs = op.getInputs();
954 llvm::SmallDenseSet<Op, 8> checked;
961 llvm::SmallVector<OpWithDepth, 8> worklist;
963 auto enqueue = [&worklist, &checked, &op](Value input,
unsigned depth) {
967 if (depth < limit && input.getParentBlock() == op->getBlock()) {
968 auto inputOp = input.template getDefiningOp<Op>();
969 if (inputOp && inputOp.getTwoState() == op.getTwoState() &&
970 checked.insert(inputOp).second)
971 worklist.push_back({inputOp, depth + 1});
975 for (
auto input : uniqueInputs)
978 while (!worklist.empty()) {
979 auto item = worklist.pop_back_val();
981 for (
auto input : item.op.getInputs()) {
982 uniqueInputs.remove(input);
983 enqueue(input, item.depth);
987 if (uniqueInputs.size() < inputs.size()) {
988 replaceOpWithNewOpAndCopyNamehint<Op>(rewriter, op, op.getType(),
989 uniqueInputs.getArrayRef(),
997LogicalResult AndOp::canonicalize(
AndOp op, PatternRewriter &rewriter) {
1001 auto inputs = op.getInputs();
1002 auto size = inputs.size();
1014 assert(size > 1 &&
"expected 2 or more operands, `fold` should handle this");
1018 if (matchPattern(inputs.back(), m_ConstantInt(&value))) {
1020 if (value.isAllOnes()) {
1021 replaceOpWithNewOpAndCopyNamehint<AndOp>(rewriter, op, op.getType(),
1022 inputs.drop_back(),
false);
1030 if (matchPattern(inputs[size - 2], m_ConstantInt(&value2))) {
1032 SmallVector<Value, 4> newOperands(inputs.drop_back(2));
1033 newOperands.push_back(cst);
1034 replaceOpWithNewOpAndCopyNamehint<AndOp>(rewriter, op, op.getType(),
1035 newOperands,
false);
1040 if (size == 2 && value.isPowerOf2()) {
1045 if (
auto replicate = inputs[0].getDefiningOp<ReplicateOp>()) {
1046 auto replicateOperand = replicate.getOperand();
1047 if (replicateOperand.getType().isInteger(1)) {
1048 unsigned resultWidth = op.getType().getIntOrFloatBitWidth();
1049 auto trailingZeros = value.countTrailingZeros();
1052 SmallVector<Value, 3> concatOperands;
1053 if (trailingZeros != resultWidth - 1) {
1055 rewriter, op.getLoc(),
1056 APInt::getZero(resultWidth - trailingZeros - 1));
1057 concatOperands.push_back(highZeros);
1059 concatOperands.push_back(replicateOperand);
1060 if (trailingZeros != 0) {
1062 rewriter, op.getLoc(), APInt::getZero(trailingZeros));
1063 concatOperands.push_back(lowZeros);
1065 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(
1066 rewriter, op, op.getType(), concatOperands);
1075 unsigned leadingZeros = value.countLeadingZeros();
1076 unsigned trailingZeros = value.countTrailingZeros();
1077 if (leadingZeros > 0 || trailingZeros > 0) {
1078 unsigned maskLength = value.getBitWidth() - leadingZeros - trailingZeros;
1081 SmallVector<Value> operands;
1082 for (
auto input : inputs.drop_back()) {
1083 unsigned offset = trailingZeros;
1084 while (
auto extractOp = input.getDefiningOp<
ExtractOp>()) {
1085 input = extractOp.getInput();
1086 offset += extractOp.getLowBit();
1089 offset, maskLength));
1093 auto narrowMask = value.extractBits(maskLength, trailingZeros);
1094 if (!narrowMask.isAllOnes())
1096 rewriter, inputs.back().getLoc(), narrowMask));
1099 Value narrowValue = operands.back();
1100 if (operands.size() > 1)
1102 AndOp::create(rewriter, op.getLoc(), operands, op.getTwoState());
1106 if (leadingZeros > 0)
1108 rewriter, op.getLoc(), APInt::getZero(leadingZeros)));
1109 operands.push_back(narrowValue);
1110 if (trailingZeros > 0)
1112 rewriter, op.getLoc(), APInt::getZero(trailingZeros)));
1113 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(rewriter, op, operands);
1120 for (
size_t i = 0; i < size - 1; ++i) {
1121 if (
auto concat = inputs[i].getDefiningOp<ConcatOp>())
1135 replaceOpWithNewOpAndCopyNamehint<ICmpOp>(rewriter, op, ICmpPredicate::eq,
1136 source, cmpAgainst);
1141 if (op.getTwoState() && op.getNumOperands() == 2) {
1142 auto isReplicateOfI1 = [](Value v) {
1143 auto rep = v.getDefiningOp<ReplicateOp>();
1146 return rep.getOperand().getType().isInteger(1);
1148 Value x = op.getOperand(0);
1149 Value y = op.getOperand(1);
1150 if (isReplicateOfI1(x))
1152 if (isReplicateOfI1(y)) {
1153 Value p = y.getDefiningOp<ReplicateOp>().getInput();
1155 rewriter, op.getLoc(), rewriter.getIntegerAttr(op.getType(), 0));
1156 replaceOpWithNewOpAndCopyNamehint<MuxOp>(rewriter, op, p, x, zero,
1166OpFoldResult OrOp::fold(FoldAdaptor adaptor) {
1170 auto value = APInt::getZero(cast<IntegerType>(getType()).
getWidth());
1171 auto inputs = adaptor.getInputs();
1173 for (
auto operand : inputs) {
1174 auto attr = dyn_cast_or_null<IntegerAttr>(operand);
1177 value |= attr.getValue();
1178 if (value.isAllOnes())
1183 if (inputs.size() == 2)
1184 if (
auto intAttr = dyn_cast_or_null<IntegerAttr>(inputs[1]))
1185 if (intAttr.getValue().isZero())
1186 return getInputs()[0];
1189 if (llvm::all_of(getInputs(),
1190 [&](
auto in) {
return in == this->getInputs()[0]; }))
1191 return getInputs()[0];
1194 for (Value arg : getInputs()) {
1196 if (matchPattern(arg,
m_Complement(m_Any(&subExpr)))) {
1197 for (Value arg2 : getInputs())
1198 if (arg2 == subExpr)
1200 APInt::getAllOnes(cast<IntegerType>(getType()).
getWidth()),
1210 APInt::getAllOnes(cast<IntegerType>(getType()).
getWidth()),
1217LogicalResult OrOp::canonicalize(
OrOp op, PatternRewriter &rewriter) {
1221 auto inputs = op.getInputs();
1222 auto size = inputs.size();
1234 assert(size > 1 &&
"expected 2 or more operands");
1238 if (matchPattern(inputs.back(), m_ConstantInt(&value))) {
1240 if (value.isZero()) {
1241 replaceOpWithNewOpAndCopyNamehint<OrOp>(rewriter, op, op.getType(),
1242 inputs.drop_back());
1248 if (matchPattern(inputs[size - 2], m_ConstantInt(&value2))) {
1250 SmallVector<Value, 4> newOperands(inputs.drop_back(2));
1251 newOperands.push_back(cst);
1252 replaceOpWithNewOpAndCopyNamehint<OrOp>(rewriter, op, op.getType(),
1260 for (
size_t i = 0; i < size - 1; ++i) {
1261 if (
auto concat = inputs[i].getDefiningOp<ConcatOp>())
1275 replaceOpWithNewOpAndCopyNamehint<ICmpOp>(rewriter, op, ICmpPredicate::ne,
1276 source, cmpAgainst);
1282 if (
auto firstMux = op.getOperand(0).getDefiningOp<
comb::MuxOp>()) {
1284 if (op.getTwoState() && firstMux.getTwoState() &&
1285 matchPattern(firstMux.getFalseValue(), m_ConstantInt(&value)) &&
1287 SmallVector<Value> conditions{firstMux.getCond()};
1288 auto check = [&](Value v) {
1292 conditions.push_back(mux.getCond());
1293 return mux.getTwoState() &&
1294 firstMux.getTrueValue() == mux.getTrueValue() &&
1295 firstMux.getFalseValue() == mux.getFalseValue();
1297 if (llvm::all_of(op.getOperands().drop_front(), check)) {
1298 auto cond = comb::OrOp::create(rewriter, op.getLoc(), conditions,
true);
1299 replaceOpWithNewOpAndCopyNamehint<comb::MuxOp>(
1300 rewriter, op, cond, firstMux.getTrueValue(),
1301 firstMux.getFalseValue(),
true);
1311OpFoldResult XorOp::fold(FoldAdaptor adaptor) {
1315 auto size = getInputs().size();
1316 auto inputs = adaptor.getInputs();
1320 return getInputs()[0];
1323 if (size == 2 && getInputs()[0] == getInputs()[1])
1324 return IntegerAttr::get(getType(), 0);
1327 if (inputs.size() == 2)
1328 if (
auto intAttr = dyn_cast_or_null<IntegerAttr>(inputs[1]))
1329 if (intAttr.getValue().isZero())
1330 return getInputs()[0];
1336 subExpr != getResult())
1345 PatternRewriter &rewriter) {
1346 auto icmp = op.getOperand(icmpOperand).getDefiningOp<ICmpOp>();
1347 auto negatedPred = ICmpOp::getNegatedPredicate(icmp.getPredicate());
1350 ICmpOp::create(rewriter, icmp.getLoc(), negatedPred, icmp.getOperand(0),
1351 icmp.getOperand(1), icmp.getTwoState());
1354 if (op.getNumOperands() > 2) {
1355 SmallVector<Value, 4> newOperands(op.getOperands());
1356 newOperands.pop_back();
1357 newOperands.erase(newOperands.begin() + icmpOperand);
1358 newOperands.push_back(result);
1360 XorOp::create(rewriter, op.getLoc(), newOperands, op.getTwoState());
1366LogicalResult XorOp::canonicalize(
XorOp op, PatternRewriter &rewriter) {
1370 auto inputs = op.getInputs();
1371 auto size = inputs.size();
1372 assert(size > 1 &&
"expected 2 or more operands");
1375 if (inputs[size - 1] == inputs[size - 2]) {
1377 "expected idempotent case for 2 elements handled already.");
1378 replaceOpWithNewOpAndCopyNamehint<XorOp>(rewriter, op, op.getType(),
1379 inputs.drop_back(2),
false);
1385 if (matchPattern(inputs.back(), m_ConstantInt(&value))) {
1387 if (value.isZero()) {
1388 replaceOpWithNewOpAndCopyNamehint<XorOp>(rewriter, op, op.getType(),
1389 inputs.drop_back(),
false);
1395 if (matchPattern(inputs[size - 2], m_ConstantInt(&value2))) {
1397 SmallVector<Value, 4> newOperands(inputs.drop_back(2));
1398 newOperands.push_back(cst);
1399 replaceOpWithNewOpAndCopyNamehint<XorOp>(rewriter, op, op.getType(),
1400 newOperands,
false);
1404 bool isSingleBit = value.getBitWidth() == 1;
1407 for (
size_t i = 0; i < size - 1; ++i) {
1408 Value operand = inputs[i];
1414 if (
auto concat = operand.getDefiningOp<
ConcatOp>())
1419 if (isSingleBit && operand.hasOneUse()) {
1420 assert(value == 1 &&
"single bit constant has to be one if not zero");
1421 if (
auto icmp = operand.getDefiningOp<ICmpOp>())
1431 if (matchPattern(op.getResult(),
m_Complement(m_Sext(m_Any(&base))))) {
1450 replaceOpWithNewOpAndCopyNamehint<ParityOp>(rewriter, op, source);
1457OpFoldResult SubOp::fold(FoldAdaptor adaptor) {
1462 if (getRhs() == getLhs())
1464 APInt::getZero(getLhs().getType().getIntOrFloatBitWidth()),
1467 if (adaptor.getRhs()) {
1469 if (adaptor.getLhs()) {
1472 APInt::getAllOnes(getLhs().getType().getIntOrFloatBitWidth()),
1474 auto rhsNeg = hw::ParamExprAttr::get(
1475 hw::PEO::Mul, cast<TypedAttr>(adaptor.getRhs()), negOne);
1476 return hw::ParamExprAttr::get(hw::PEO::Add,
1477 cast<TypedAttr>(adaptor.getLhs()), rhsNeg);
1481 if (
auto rhsC = dyn_cast<IntegerAttr>(adaptor.getRhs())) {
1482 if (rhsC.getValue().isZero())
1490LogicalResult SubOp::canonicalize(
SubOp op, PatternRewriter &rewriter) {
1496 if (matchPattern(op.getRhs(), m_ConstantInt(&value))) {
1498 replaceOpWithNewOpAndCopyNamehint<AddOp>(rewriter, op, op.getLhs(), negCst,
1510OpFoldResult AddOp::fold(FoldAdaptor adaptor) {
1514 auto size = getInputs().size();
1518 return getInputs()[0];
1524LogicalResult AddOp::canonicalize(
AddOp op, PatternRewriter &rewriter) {
1528 auto inputs = op.getInputs();
1529 auto size = inputs.size();
1530 assert(size > 1 &&
"expected 2 or more operands");
1532 APInt value, value2;
1535 if (matchPattern(inputs.back(), m_ConstantInt(&value)) && value.isZero()) {
1536 replaceOpWithNewOpAndCopyNamehint<AddOp>(rewriter, op, op.getType(),
1537 inputs.drop_back(),
false);
1542 if (matchPattern(inputs[size - 1], m_ConstantInt(&value)) &&
1543 matchPattern(inputs[size - 2], m_ConstantInt(&value2))) {
1545 SmallVector<Value, 4> newOperands(inputs.drop_back(2));
1546 newOperands.push_back(cst);
1547 replaceOpWithNewOpAndCopyNamehint<AddOp>(rewriter, op, op.getType(),
1548 newOperands,
false);
1553 if (inputs[size - 1] == inputs[size - 2]) {
1554 SmallVector<Value, 4> newOperands(inputs.drop_back(2));
1558 comb::ShlOp::create(rewriter, op.getLoc(), inputs.back(), one,
false);
1560 newOperands.push_back(shiftLeftOp);
1561 replaceOpWithNewOpAndCopyNamehint<AddOp>(rewriter, op, op.getType(),
1562 newOperands,
false);
1566 auto shlOp = inputs[size - 1].getDefiningOp<
comb::ShlOp>();
1568 if (shlOp && shlOp.getLhs() == inputs[size - 2] &&
1569 matchPattern(shlOp.getRhs(), m_ConstantInt(&value))) {
1571 APInt one(value.getBitWidth(), 1,
false);
1575 std::array<Value, 2> factors = {shlOp.getLhs(), rhs};
1576 auto mulOp = comb::MulOp::create(rewriter, op.getLoc(), factors,
false);
1578 SmallVector<Value, 4> newOperands(inputs.drop_back(2));
1579 newOperands.push_back(mulOp);
1580 replaceOpWithNewOpAndCopyNamehint<AddOp>(rewriter, op, op.getType(),
1581 newOperands,
false);
1585 auto mulOp = inputs[size - 1].getDefiningOp<
comb::MulOp>();
1587 if (mulOp && mulOp.getInputs().size() == 2 &&
1588 mulOp.getInputs()[0] == inputs[size - 2] &&
1589 matchPattern(mulOp.getInputs()[1], m_ConstantInt(&value))) {
1591 APInt one(value.getBitWidth(), 1,
false);
1593 std::array<Value, 2> factors = {mulOp.getInputs()[0], rhs};
1594 auto newMulOp = comb::MulOp::create(rewriter, op.getLoc(), factors,
false);
1596 SmallVector<Value, 4> newOperands(inputs.drop_back(2));
1597 newOperands.push_back(newMulOp);
1598 replaceOpWithNewOpAndCopyNamehint<AddOp>(rewriter, op, op.getType(),
1599 newOperands,
false);
1612 auto addOp = inputs[0].getDefiningOp<
comb::AddOp>();
1613 if (addOp && addOp.getInputs().size() == 2 &&
1614 matchPattern(addOp.getInputs()[1], m_ConstantInt(&value2)) &&
1615 inputs.size() == 2 && matchPattern(inputs[1], m_ConstantInt(&value))) {
1618 replaceOpWithNewOpAndCopyNamehint<AddOp>(
1619 rewriter, op, op.getType(), ArrayRef<Value>{addOp.getInputs()[0], rhs},
1620 op.getTwoState() && addOp.getTwoState());
1627OpFoldResult MulOp::fold(FoldAdaptor adaptor) {
1631 auto size = getInputs().size();
1632 auto inputs = adaptor.getInputs();
1636 return getInputs()[0];
1638 auto width = cast<IntegerType>(getType()).getWidth();
1640 return getIntAttr(APInt::getZero(0), getContext());
1642 APInt value(width, 1,
false);
1645 for (
auto operand : inputs) {
1646 auto attr = dyn_cast_or_null<IntegerAttr>(operand);
1649 value *= attr.getValue();
1658LogicalResult MulOp::canonicalize(
MulOp op, PatternRewriter &rewriter) {
1662 auto inputs = op.getInputs();
1663 auto size = inputs.size();
1664 assert(size > 1 &&
"expected 2 or more operands");
1666 APInt value, value2;
1669 if (size == 2 && matchPattern(inputs.back(), m_ConstantInt(&value)) &&
1670 value.isPowerOf2()) {
1672 value.exactLogBase2());
1674 comb::ShlOp::create(rewriter, op.getLoc(), inputs[0], shift,
false);
1676 replaceOpWithNewOpAndCopyNamehint<MulOp>(rewriter, op, op.getType(),
1677 ArrayRef<Value>(shlOp),
false);
1682 if (matchPattern(inputs.back(), m_ConstantInt(&value)) && value.isOne()) {
1683 replaceOpWithNewOpAndCopyNamehint<MulOp>(rewriter, op, op.getType(),
1684 inputs.drop_back());
1689 if (matchPattern(inputs[size - 1], m_ConstantInt(&value)) &&
1690 matchPattern(inputs[size - 2], m_ConstantInt(&value2))) {
1692 SmallVector<Value, 4> newOperands(inputs.drop_back(2));
1693 newOperands.push_back(cst);
1694 replaceOpWithNewOpAndCopyNamehint<MulOp>(rewriter, op, op.getType(),
1710template <
class Op,
bool isSigned>
1711static OpFoldResult
foldDiv(Op op, ArrayRef<Attribute> constants) {
1712 if (
auto rhsValue = dyn_cast_or_null<IntegerAttr>(constants[1])) {
1714 if (rhsValue.getValue() == 1)
1718 if (rhsValue.getValue().isZero())
1725OpFoldResult DivUOp::fold(FoldAdaptor adaptor) {
1728 return foldDiv<
DivUOp,
false>(*
this, adaptor.getOperands());
1731OpFoldResult DivSOp::fold(FoldAdaptor adaptor) {
1737template <
class Op,
bool isSigned>
1738static OpFoldResult
foldMod(Op op, ArrayRef<Attribute> constants) {
1739 if (
auto rhsValue = dyn_cast_or_null<IntegerAttr>(constants[1])) {
1741 if (rhsValue.getValue() == 1)
1742 return getIntAttr(APInt::getZero(op.getType().getIntOrFloatBitWidth()),
1746 if (rhsValue.getValue().isZero())
1750 if (
auto lhsValue = dyn_cast_or_null<IntegerAttr>(constants[0])) {
1752 if (lhsValue.getValue().isZero())
1753 return getIntAttr(APInt::getZero(op.getType().getIntOrFloatBitWidth()),
1760OpFoldResult ModUOp::fold(FoldAdaptor adaptor) {
1763 return foldMod<
ModUOp,
false>(*
this, adaptor.getOperands());
1766OpFoldResult ModSOp::fold(FoldAdaptor adaptor) {
1772LogicalResult DivUOp::canonicalize(
DivUOp op, PatternRewriter &rewriter) {
1778LogicalResult ModUOp::canonicalize(
ModUOp op, PatternRewriter &rewriter) {
1790OpFoldResult ConcatOp::fold(FoldAdaptor adaptor) {
1794 if (getNumOperands() == 1)
1795 return getOperand(0);
1798 for (
auto attr : adaptor.getInputs())
1799 if (!attr || !isa<IntegerAttr>(attr))
1803 unsigned resultWidth = getType().getIntOrFloatBitWidth();
1804 APInt result(resultWidth, 0);
1806 unsigned nextInsertion = resultWidth;
1808 for (
auto attr : adaptor.getInputs()) {
1809 auto chunk = cast<IntegerAttr>(attr).getValue();
1810 nextInsertion -= chunk.getBitWidth();
1811 result.insertBits(chunk, nextInsertion);
1817LogicalResult ConcatOp::canonicalize(
ConcatOp op, PatternRewriter &rewriter) {
1821 auto inputs = op.getInputs();
1822 auto size = inputs.size();
1823 assert(size > 1 &&
"expected 2 or more operands");
1826 SmallVector<Value, 4> pendingOperands, processedOperands;
1827 bool anyOperandChanged;
1829 auto pushPendingOperands = [&](ValueRange operands) {
1830 auto size = operands.size();
1831 for (
size_t i = 0; i != size; ++i)
1832 pendingOperands.push_back(operands[size - 1 - i]);
1833 anyOperandChanged =
true;
1835 auto replacePrevOperand = [&](Value replacement) {
1836 processedOperands.back() = replacement;
1837 anyOperandChanged =
true;
1839 pendingOperands.reserve(size);
1840 pushPendingOperands(inputs);
1841 anyOperandChanged =
false;
1843 while (!pendingOperands.empty()) {
1844 Value nextOperand = pendingOperands.pop_back_val();
1848 if (
auto subConcat = nextOperand.getDefiningOp<
ConcatOp>()) {
1849 pushPendingOperands(subConcat->getOperands());
1854 if (!processedOperands.empty()) {
1855 Value prevOperand = processedOperands.back();
1859 if (
auto prevCst = prevOperand.getDefiningOp<
hw::ConstantOp>()) {
1860 unsigned prevWidth = prevCst.getValue().getBitWidth();
1861 unsigned thisWidth = cst.getValue().getBitWidth();
1862 auto resultCst = cst.getValue().zext(prevWidth + thisWidth);
1863 resultCst |= prevCst.getValue().zext(prevWidth + thisWidth)
1867 replacePrevOperand(replacement);
1873 if (nextOperand == prevOperand) {
1875 rewriter.createOrFold<ReplicateOp>(op.getLoc(), prevOperand, 2);
1876 replacePrevOperand(replacement);
1882 if (
auto repl = nextOperand.getDefiningOp<ReplicateOp>()) {
1884 if (repl.getOperand() == prevOperand) {
1885 Value replacement = rewriter.createOrFold<ReplicateOp>(
1886 op.getLoc(), repl.getOperand(), repl.getMultiple() + 1);
1887 replacePrevOperand(replacement);
1891 if (
auto prevRepl = prevOperand.getDefiningOp<ReplicateOp>()) {
1892 if (prevRepl.getOperand() == repl.getOperand()) {
1893 Value replacement = rewriter.createOrFold<ReplicateOp>(
1894 op.getLoc(), repl.getOperand(),
1895 repl.getMultiple() + prevRepl.getMultiple());
1896 replacePrevOperand(replacement);
1903 if (
auto repl = prevOperand.getDefiningOp<ReplicateOp>()) {
1904 if (repl.getOperand() == nextOperand) {
1905 Value replacement = rewriter.createOrFold<ReplicateOp>(
1906 op.getLoc(), nextOperand, repl.getMultiple() + 1);
1907 replacePrevOperand(replacement);
1914 if (
auto extract = nextOperand.getDefiningOp<
ExtractOp>()) {
1915 if (
auto prevExtract = prevOperand.getDefiningOp<
ExtractOp>()) {
1916 if (extract.getInput() == prevExtract.getInput()) {
1917 auto thisWidth = cast<IntegerType>(extract.getType()).getWidth();
1918 if (prevExtract.getLowBit() == extract.getLowBit() + thisWidth) {
1919 auto prevWidth = prevExtract.getType().getIntOrFloatBitWidth();
1920 auto resType = rewriter.getIntegerType(thisWidth + prevWidth);
1923 extract.getInput(), extract.getLowBit());
1924 replacePrevOperand(replacement);
1938 static std::optional<ArraySlice>
get(Value value) {
1939 assert(isa<IntegerType>(value.getType()) &&
"expected integer type");
1941 return ArraySlice{arrayGet.getInput(), arrayGet.getIndex(), 1};
1944 if (
auto arraySlice =
1947 arraySlice.getInput(), arraySlice.getLowIndex(),
1948 hw::type_cast<hw::ArrayType>(arraySlice.getType())
1950 return std::nullopt;
1953 if (
auto extractOpt = ArraySlice::get(nextOperand)) {
1954 if (
auto prevExtractOpt = ArraySlice::get(prevOperand)) {
1956 if (prevExtractOpt->index.getType() == extractOpt->index.getType() &&
1957 prevExtractOpt->input == extractOpt->input &&
1958 hw::isOffset(extractOpt->index, prevExtractOpt->index,
1959 extractOpt->width)) {
1960 auto resType = hw::ArrayType::get(
1961 hw::type_cast<hw::ArrayType>(prevExtractOpt->input.getType())
1963 extractOpt->width + prevExtractOpt->width);
1964 auto resIntType = rewriter.getIntegerType(hw::getBitWidth(resType));
1966 rewriter, op.getLoc(), resIntType,
1968 prevExtractOpt->input,
1969 extractOpt->index));
1970 replacePrevOperand(replacement);
1977 processedOperands.push_back(nextOperand);
1989 constexpr size_t kBatchExtractThreshold = 16;
1990 bool anyExtractsResolved =
false;
1991 if (!anyOperandChanged && processedOperands.size() > 1) {
1992 SmallVector<ExtractOp, 8> extractUsers;
1993 for (
auto *user : op->getUsers()) {
1994 if (
auto extract = dyn_cast<ExtractOp>(user))
1995 extractUsers.push_back(extract);
1998 if (extractUsers.size() >= kBatchExtractThreshold) {
2000 auto concatInputs = op.getInputs();
2001 size_t numConcatOperands = concatInputs.size();
2002 SmallVector<size_t> prefixWidths(numConcatOperands);
2003 size_t cumWidth = 0;
2004 for (
size_t i = 0; i < numConcatOperands; ++i) {
2007 cumWidth += concatInputs[numConcatOperands - 1 - i]
2009 .getIntOrFloatBitWidth();
2010 prefixWidths[i] = cumWidth;
2014 for (
auto extract : extractUsers) {
2017 anyExtractsResolved =
true;
2022 if (processedOperands.size() == 1) {
2026 }
else if (anyOperandChanged) {
2027 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(rewriter, op, op.getType(),
2029 }
else if (!anyExtractsResolved) {
2039OpFoldResult MuxOp::fold(FoldAdaptor adaptor) {
2044 if (getTrueValue() == getFalseValue() && getTrueValue() != getResult())
2045 return getTrueValue();
2046 if (
auto tv = adaptor.getTrueValue())
2047 if (tv == adaptor.getFalseValue())
2052 if (
auto pred = dyn_cast_or_null<IntegerAttr>(adaptor.getCond())) {
2053 if (pred.getValue().isZero() && getFalseValue() != getResult())
2054 return getFalseValue();
2055 if (pred.getValue().isOne() && getTrueValue() != getResult())
2056 return getTrueValue();
2060 if (getCond().getType() == getTrueValue().getType())
2061 if (
auto tv = dyn_cast_or_null<IntegerAttr>(adaptor.getTrueValue()))
2062 if (
auto fv = dyn_cast_or_null<IntegerAttr>(adaptor.getFalseValue()))
2063 if (tv.getValue().isOne() && fv.getValue().isZero() &&
2064 hw::getBitWidth(getType()) == 1 && getCond() != getResult())
2080 if (
auto cmp = cond.getDefiningOp<ICmpOp>()) {
2082 auto requiredPredicate =
2083 (isInverted ? ICmpPredicate::eq : ICmpPredicate::ne);
2084 if (cmp.getLhs() == indexValue && cmp.getPredicate() == requiredPredicate) {
2094 if (
auto orOp = cond.getDefiningOp<
OrOp>()) {
2097 for (
auto operand : orOp.getOperands())
2104 if (
auto andOp = cond.getDefiningOp<
AndOp>()) {
2107 for (
auto operand : andOp.getOperands())
2126 PatternRewriter &rewriter,
MuxOp rootMux,
bool isFalseSide,
2132 auto rootCmp = rootMux.getCond().getDefiningOp<ICmpOp>();
2135 Value indexValue = rootCmp.getLhs();
2138 auto getCaseValue = [&](
MuxOp mux) -> Value {
2139 return mux.getOperand(1 +
unsigned(!isFalseSide));
2144 auto getTreeValue = [&](
MuxOp mux) -> Value {
2145 return mux.getOperand(1 +
unsigned(isFalseSide));
2150 SmallVector<Location> locationsFound;
2151 SmallVector<std::pair<hw::ConstantOp, Value>, 4> valuesFound;
2155 auto collectConstantValues = [&](
MuxOp mux) ->
bool {
2157 mux.getCond(), indexValue, isFalseSide, [&](
hw::ConstantOp cst) {
2158 valuesFound.push_back({cst, getCaseValue(mux)});
2159 locationsFound.push_back(mux.getCond().getLoc());
2160 locationsFound.push_back(mux->getLoc());
2165 if (!collectConstantValues(rootMux))
2169 if (rootMux->hasOneUse()) {
2170 if (
auto userMux = dyn_cast<MuxOp>(*rootMux->user_begin())) {
2171 if (getTreeValue(userMux) == rootMux.getResult() &&
2179 auto nextTreeValue = getTreeValue(rootMux);
2181 auto nextMux = nextTreeValue.getDefiningOp<
MuxOp>();
2182 if (!nextMux || !nextMux->hasOneUse())
2184 if (!collectConstantValues(nextMux))
2186 nextTreeValue = getTreeValue(nextMux);
2189 auto indexWidth = cast<IntegerType>(indexValue.getType()).getWidth();
2191 if (indexWidth > 20)
2194 auto foldingStyle = styleFn(indexWidth, valuesFound.size());
2198 uint64_t tableSize = 1ULL << indexWidth;
2202 SmallVector<Value, 8> table(tableSize, nextTreeValue);
2207 for (
auto &elt :
llvm::reverse(valuesFound)) {
2208 uint64_t idx = elt.first.getValue().getZExtValue();
2209 assert(idx < table.size() &&
"constant should be same bitwidth as index");
2210 table[idx] = elt.second;
2214 SmallVector<Value> bits;
2223 "unknown folding style");
2227 std::reverse(table.begin(), table.end());
2230 auto fusedLoc = rewriter.getFusedLoc(locationsFound);
2232 replaceOpWithNewOpAndCopyNamehint<hw::ArrayGetOp>(rewriter, rootMux, array,
2247 PatternRewriter &rewriter) {
2248 assert(fullyAssoc->getNumOperands() >= 2 &&
"cannot split up unary ops");
2249 assert(operandNo < fullyAssoc->getNumOperands() &&
"Invalid operand #");
2253 if (fullyAssoc->getNumOperands() == 2)
2254 return fullyAssoc->getOperand(operandNo ^ 1);
2257 if (fullyAssoc->hasOneUse()) {
2258 rewriter.modifyOpInPlace(fullyAssoc,
2259 [&]() { fullyAssoc->eraseOperand(operandNo); });
2260 return fullyAssoc->getResult(0);
2264 SmallVector<Value> operands;
2265 operands.append(fullyAssoc->getOperands().begin(),
2266 fullyAssoc->getOperands().begin() + operandNo);
2267 operands.append(fullyAssoc->getOperands().begin() + operandNo + 1,
2268 fullyAssoc->getOperands().end());
2270 fullyAssoc->getLoc(), fullyAssoc->getName(), operands, rewriter);
2271 Value excluded = fullyAssoc->getOperand(operandNo);
2275 ArrayRef<Value>{opWithoutExcluded, excluded}, rewriter);
2277 return opWithoutExcluded;
2287 PatternRewriter &rewriter) {
2290 Operation *subExpr =
2291 (isTrueOperand ? op.getFalseValue() : op.getTrueValue()).getDefiningOp();
2292 if (!subExpr || subExpr->getNumOperands() < 2)
2296 if (!isa<AndOp, XorOp, OrOp, MuxOp>(subExpr))
2301 Value commonValue = isTrueOperand ? op.getTrueValue() : op.getFalseValue();
2302 size_t opNo = 0, e = subExpr->getNumOperands();
2303 while (opNo != e && subExpr->getOperand(opNo) != commonValue)
2309 Value cond = op.getCond();
2315 if (
auto subMux = dyn_cast<MuxOp>(subExpr)) {
2320 Value subCond = subMux.getCond();
2323 if (subMux.getTrueValue() == commonValue)
2324 otherValue = subMux.getFalseValue();
2325 else if (subMux.getFalseValue() == commonValue) {
2326 otherValue = subMux.getTrueValue();
2336 cond = rewriter.createOrFold<
OrOp>(op.getLoc(), cond, subCond,
false);
2337 replaceOpWithNewOpAndCopyNamehint<MuxOp>(rewriter, op, cond, commonValue,
2338 otherValue, op.getTwoState());
2344 bool isaAndOp = isa<AndOp>(subExpr);
2345 if (isTrueOperand ^ isaAndOp)
2349 rewriter.createOrFold<ReplicateOp>(op.getLoc(), op.getType(), cond);
2352 bool isaXorOp = isa<XorOp>(subExpr);
2353 bool isaOrOp = isa<OrOp>(subExpr);
2362 if (isaOrOp || isaXorOp) {
2363 auto masked = rewriter.createOrFold<
AndOp>(op.getLoc(), extendedCond,
2364 restOfAssoc,
false);
2366 replaceOpWithNewOpAndCopyNamehint<XorOp>(rewriter, op, masked,
2367 commonValue,
false);
2369 replaceOpWithNewOpAndCopyNamehint<OrOp>(rewriter, op, masked, commonValue,
2375 assert(isaAndOp &&
"unexpected operation here");
2376 auto masked = rewriter.createOrFold<
OrOp>(op.getLoc(), extendedCond,
2377 restOfAssoc,
false);
2378 replaceOpWithNewOpAndCopyNamehint<AndOp>(rewriter, op, masked, commonValue,
2389 PatternRewriter &rewriter) {
2392 if (!isa<ConcatOp>(trueOp))
2396 SmallVector<Value> trueOperands, falseOperands;
2400 size_t numTrueOperands = trueOperands.size();
2401 size_t numFalseOperands = falseOperands.size();
2403 if (!numTrueOperands || !numFalseOperands ||
2404 (trueOperands.front() != falseOperands.front() &&
2405 trueOperands.back() != falseOperands.back()))
2409 if (trueOperands.front() == falseOperands.front()) {
2410 SmallVector<Value> operands;
2412 for (i = 0; i < numTrueOperands; ++i) {
2413 Value trueOperand = trueOperands[i];
2414 if (trueOperand == falseOperands[i])
2415 operands.push_back(trueOperand);
2419 if (i == numTrueOperands) {
2426 if (llvm::all_of(operands, [&](Value v) {
return v == operands.front(); }))
2427 sharedMSB = rewriter.createOrFold<ReplicateOp>(
2428 mux->getLoc(), operands.front(), operands.size());
2430 sharedMSB = rewriter.createOrFold<
ConcatOp>(mux->getLoc(), operands);
2434 operands.append(trueOperands.begin() + i, trueOperands.end());
2435 Value trueLSB = rewriter.createOrFold<
ConcatOp>(trueOp->getLoc(), operands);
2437 operands.append(falseOperands.begin() + i, falseOperands.end());
2439 rewriter.createOrFold<
ConcatOp>(falseOp->getLoc(), operands);
2442 Value lsb = rewriter.createOrFold<
MuxOp>(
2443 mux->getLoc(), mux.getCond(), trueLSB, falseLSB, mux.getTwoState());
2444 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(rewriter, mux, sharedMSB, lsb);
2449 if (trueOperands.back() == falseOperands.back()) {
2450 SmallVector<Value> operands;
2453 Value trueOperand = trueOperands[numTrueOperands - i - 1];
2454 if (trueOperand == falseOperands[numFalseOperands - i - 1])
2455 operands.push_back(trueOperand);
2459 std::reverse(operands.begin(), operands.end());
2460 Value sharedLSB = rewriter.createOrFold<
ConcatOp>(mux->getLoc(), operands);
2464 operands.append(trueOperands.begin(), trueOperands.end() - i);
2465 Value trueMSB = rewriter.createOrFold<
ConcatOp>(trueOp->getLoc(), operands);
2467 operands.append(falseOperands.begin(), falseOperands.end() - i);
2469 rewriter.createOrFold<
ConcatOp>(falseOp->getLoc(), operands);
2471 Value msb = rewriter.createOrFold<
MuxOp>(
2472 mux->getLoc(), mux.getCond(), trueMSB, falseMSB, mux.getTwoState());
2473 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(rewriter, mux, msb, sharedLSB);
2485 if (!trueVec || !falseVec)
2487 if (!trueVec.isUniform() || !falseVec.isUniform())
2490 auto mux = MuxOp::create(rewriter, op.getLoc(), op.getCond(),
2491 trueVec.getUniformElement(),
2492 falseVec.getUniformElement(), op.getTwoState());
2494 SmallVector<Value> values(trueVec.getInputs().size(), mux);
2502 bool constCond, PatternRewriter &rewriter) {
2503 if (!muxValue.hasOneUse())
2505 auto *op = muxValue.getDefiningOp();
2506 if (!op || !isa_and_nonnull<CombDialect>(op->getDialect()))
2508 if (!llvm::is_contained(op->getOperands(), muxCond))
2510 OpBuilder::InsertionGuard guard(rewriter);
2511 rewriter.setInsertionPoint(op);
2514 rewriter.modifyOpInPlace(op, [&] {
2515 for (
auto &use : op->getOpOperands())
2516 if (use.get() == muxCond)
2524 using OpRewritePattern::OpRewritePattern;
2526 LogicalResult matchAndRewrite(
MuxOp op,
2527 PatternRewriter &rewriter)
const override;
2531foldToArrayCreateOnlyWhenDense(
size_t indexWidth,
size_t numEntries) {
2534 if (indexWidth >= 9 || numEntries < 3)
2540 uint64_t tableSize = 1ULL << indexWidth;
2541 if (numEntries >= tableSize * 5 / 8)
2546LogicalResult MuxRewriter::matchAndRewrite(
MuxOp op,
2547 PatternRewriter &rewriter)
const {
2551 bool isSignlessInt =
false;
2552 if (
auto intType = dyn_cast<IntegerType>(op.getType()))
2553 isSignlessInt = intType.isSignless();
2560 if (matchPattern(op.getTrueValue(), m_ConstantInt(&value)) && isSignlessInt) {
2561 if (value.getBitWidth() == 1) {
2563 if (value.isZero()) {
2565 replaceOpWithNewOpAndCopyNamehint<AndOp>(rewriter, op, notCond,
2566 op.getFalseValue(),
false);
2571 replaceOpWithNewOpAndCopyNamehint<OrOp>(rewriter, op, op.getCond(),
2572 op.getFalseValue(),
false);
2578 if (matchPattern(op.getFalseValue(), m_ConstantInt(&value2))) {
2583 APInt xorValue = value ^ value2;
2584 if (xorValue.isPowerOf2()) {
2585 unsigned leadingZeros = xorValue.countLeadingZeros();
2586 unsigned trailingZeros = value.getBitWidth() - leadingZeros - 1;
2587 SmallVector<Value, 3> operands;
2595 if (leadingZeros > 0)
2596 operands.push_back(rewriter.createOrFold<
ExtractOp>(
2597 op.getLoc(), op.getTrueValue(), trailingZeros + 1, leadingZeros));
2601 auto v1 = rewriter.createOrFold<
ExtractOp>(
2602 op.getLoc(), op.getTrueValue(), trailingZeros, 1);
2603 auto v2 = rewriter.createOrFold<
ExtractOp>(
2604 op.getLoc(), op.getFalseValue(), trailingZeros, 1);
2605 operands.push_back(rewriter.createOrFold<
MuxOp>(
2606 op.getLoc(), op.getCond(), v1, v2,
false));
2608 if (trailingZeros > 0)
2609 operands.push_back(rewriter.createOrFold<
ExtractOp>(
2610 op.getLoc(), op.getTrueValue(), 0, trailingZeros));
2612 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(rewriter, op, op.getType(),
2619 if (value.isAllOnes() && value2.isZero()) {
2620 replaceOpWithNewOpAndCopyNamehint<ReplicateOp>(
2621 rewriter, op, op.getType(), op.getCond());
2627 if (matchPattern(op.getFalseValue(), m_ConstantInt(&value)) &&
2628 isSignlessInt && value.getBitWidth() == 1) {
2630 if (value.isZero()) {
2631 replaceOpWithNewOpAndCopyNamehint<AndOp>(rewriter, op, op.getCond(),
2632 op.getTrueValue(),
false);
2639 auto notCond = rewriter.createOrFold<
XorOp>(op.getLoc(), op.getCond(),
2640 op.getFalseValue(),
false);
2641 replaceOpWithNewOpAndCopyNamehint<OrOp>(rewriter, op, notCond,
2642 op.getTrueValue(),
false);
2648 Operation *condOp = op.getCond().getDefiningOp();
2649 if (condOp && matchPattern(condOp,
m_Complement(m_Any(&subExpr))) &&
2651 replaceOpWithNewOpAndCopyNamehint<MuxOp>(rewriter, op, op.getType(),
2652 subExpr, op.getFalseValue(),
2653 op.getTrueValue(),
true);
2660 if (condOp && condOp->hasOneUse()) {
2661 SmallVector<Value> invertedOperands;
2665 auto getInvertedOperands = [&]() ->
bool {
2666 for (Value operand : condOp->getOperands()) {
2667 if (matchPattern(operand,
m_Complement(m_Any(&subExpr))))
2668 invertedOperands.push_back(subExpr);
2675 if (isa<AndOp>(condOp) && getInvertedOperands()) {
2677 rewriter.createOrFold<
OrOp>(op.getLoc(), invertedOperands,
false);
2678 replaceOpWithNewOpAndCopyNamehint<MuxOp>(
2679 rewriter, op, newOr, op.getFalseValue(), op.getTrueValue(),
2683 if (isa<OrOp>(condOp) && getInvertedOperands()) {
2685 rewriter.createOrFold<
AndOp>(op.getLoc(), invertedOperands,
false);
2686 replaceOpWithNewOpAndCopyNamehint<MuxOp>(
2687 rewriter, op, newAnd, op.getFalseValue(), op.getTrueValue(),
2693 if (
auto falseMux = op.getFalseValue().getDefiningOp<
MuxOp>();
2694 falseMux && falseMux != op) {
2696 if (op.getCond() == falseMux.getCond() &&
2697 falseMux.getFalseValue() != falseMux) {
2698 replaceOpWithNewOpAndCopyNamehint<MuxOp>(
2699 rewriter, op, op.getCond(), op.getTrueValue(),
2700 falseMux.getFalseValue(), op.getTwoStateAttr());
2706 foldToArrayCreateOnlyWhenDense))
2710 if (
auto trueMux = op.getTrueValue().getDefiningOp<
MuxOp>();
2711 trueMux && trueMux != op) {
2713 if (op.getCond() == trueMux.getCond()) {
2714 replaceOpWithNewOpAndCopyNamehint<MuxOp>(
2715 rewriter, op, op.getCond(), trueMux.getTrueValue(),
2716 op.getFalseValue(), op.getTwoStateAttr());
2722 foldToArrayCreateOnlyWhenDense))
2727 if (
auto trueMux = dyn_cast_or_null<MuxOp>(op.getTrueValue().getDefiningOp()),
2728 falseMux = dyn_cast_or_null<MuxOp>(op.getFalseValue().getDefiningOp());
2729 trueMux && falseMux && trueMux.getCond() == falseMux.getCond() &&
2730 trueMux.getTrueValue() == falseMux.getTrueValue() && trueMux != op &&
2732 auto subMux = MuxOp::create(
2733 rewriter, rewriter.getFusedLoc({trueMux.getLoc(), falseMux.getLoc()}),
2734 op.getCond(), trueMux.getFalseValue(), falseMux.getFalseValue());
2735 replaceOpWithNewOpAndCopyNamehint<MuxOp>(rewriter, op, trueMux.getCond(),
2736 trueMux.getTrueValue(), subMux,
2737 op.getTwoStateAttr());
2742 if (
auto trueMux = dyn_cast_or_null<MuxOp>(op.getTrueValue().getDefiningOp()),
2743 falseMux = dyn_cast_or_null<MuxOp>(op.getFalseValue().getDefiningOp());
2744 trueMux && falseMux && trueMux.getCond() == falseMux.getCond() &&
2745 trueMux.getFalseValue() == falseMux.getFalseValue() && trueMux != op &&
2747 auto subMux = MuxOp::create(
2748 rewriter, rewriter.getFusedLoc({trueMux.getLoc(), falseMux.getLoc()}),
2749 op.getCond(), trueMux.getTrueValue(), falseMux.getTrueValue());
2750 replaceOpWithNewOpAndCopyNamehint<MuxOp>(rewriter, op, trueMux.getCond(),
2751 subMux, trueMux.getFalseValue(),
2752 op.getTwoStateAttr());
2757 if (
auto trueMux = dyn_cast_or_null<MuxOp>(op.getTrueValue().getDefiningOp()),
2758 falseMux = dyn_cast_or_null<MuxOp>(op.getFalseValue().getDefiningOp());
2759 trueMux && falseMux &&
2760 trueMux.getTrueValue() == falseMux.getTrueValue() &&
2761 trueMux.getFalseValue() == falseMux.getFalseValue() && trueMux != op &&
2764 MuxOp::create(rewriter,
2765 rewriter.getFusedLoc(
2766 {op.getLoc(), trueMux.getLoc(), falseMux.getLoc()}),
2767 op.getCond(), trueMux.getCond(), falseMux.getCond());
2768 replaceOpWithNewOpAndCopyNamehint<MuxOp>(
2769 rewriter, op, subMux, trueMux.getTrueValue(), trueMux.getFalseValue(),
2770 op.getTwoStateAttr());
2782 if (Operation *trueOp = op.getTrueValue().getDefiningOp())
2783 if (Operation *falseOp = op.getFalseValue().getDefiningOp())
2784 if (trueOp->getName() == falseOp->getName())
2797 if (op.getTrueValue().getDefiningOp() &&
2798 op.getTrueValue().getDefiningOp() != op)
2801 if (op.getFalseValue().getDefiningOp() &&
2802 op.getFalseValue().getDefiningOp() != op)
2813 if (op.getInputs().empty() || op.isUniform())
2815 auto inputs = op.getInputs();
2816 if (inputs.size() <= 1)
2821 auto first = inputs[0].getDefiningOp<
comb::MuxOp>();
2826 for (
size_t i = 1, n = inputs.size(); i < n; ++i) {
2827 auto input = inputs[i].getDefiningOp<
comb::MuxOp>();
2828 if (!input || first.getCond() != input.getCond())
2833 SmallVector<Value> trues{first.getTrueValue()};
2834 SmallVector<Value> falses{first.getFalseValue()};
2835 SmallVector<Location> locs{first->getLoc()};
2836 bool isTwoState =
true;
2837 for (
size_t i = 1, n = inputs.size(); i < n; ++i) {
2838 auto input = inputs[i].getDefiningOp<
comb::MuxOp>();
2839 trues.push_back(input.getTrueValue());
2840 falses.push_back(input.getFalseValue());
2841 locs.push_back(input->getLoc());
2842 if (!input.getTwoState())
2847 auto loc = FusedLoc::get(op.getContext(), locs);
2851 auto arrayTy = op.getType();
2854 rewriter.replaceOpWithNewOp<
comb::MuxOp>(op, arrayTy, first.getCond(),
2855 trueValues, falseValues, isTwoState);
2860 using OpRewritePattern::OpRewritePattern;
2863 PatternRewriter &rewriter)
const override {
2864 if (foldArrayOfMuxes(op, rewriter))
2872void MuxOp::getCanonicalizationPatterns(RewritePatternSet &results,
2874 results.insert<MuxRewriter, ArrayRewriter>(
context);
2885 switch (predicate) {
2886 case ICmpPredicate::eq:
2888 case ICmpPredicate::ne:
2890 case ICmpPredicate::slt:
2891 return lhs.slt(rhs);
2892 case ICmpPredicate::sle:
2893 return lhs.sle(rhs);
2894 case ICmpPredicate::sgt:
2895 return lhs.sgt(rhs);
2896 case ICmpPredicate::sge:
2897 return lhs.sge(rhs);
2898 case ICmpPredicate::ult:
2899 return lhs.ult(rhs);
2900 case ICmpPredicate::ule:
2901 return lhs.ule(rhs);
2902 case ICmpPredicate::ugt:
2903 return lhs.ugt(rhs);
2904 case ICmpPredicate::uge:
2905 return lhs.uge(rhs);
2906 case ICmpPredicate::ceq:
2908 case ICmpPredicate::cne:
2910 case ICmpPredicate::weq:
2912 case ICmpPredicate::wne:
2915 llvm_unreachable(
"unknown comparison predicate");
2921 switch (predicate) {
2922 case ICmpPredicate::eq:
2923 case ICmpPredicate::sle:
2924 case ICmpPredicate::sge:
2925 case ICmpPredicate::ule:
2926 case ICmpPredicate::uge:
2927 case ICmpPredicate::ceq:
2928 case ICmpPredicate::weq:
2930 case ICmpPredicate::ne:
2931 case ICmpPredicate::slt:
2932 case ICmpPredicate::sgt:
2933 case ICmpPredicate::ult:
2934 case ICmpPredicate::ugt:
2935 case ICmpPredicate::cne:
2936 case ICmpPredicate::wne:
2939 llvm_unreachable(
"unknown comparison predicate");
2942OpFoldResult ICmpOp::fold(FoldAdaptor adaptor) {
2945 if (getLhs() == getRhs()) {
2947 return IntegerAttr::get(getType(), val);
2951 if (
auto lhs = dyn_cast_or_null<IntegerAttr>(adaptor.getLhs())) {
2952 if (
auto rhs = dyn_cast_or_null<IntegerAttr>(adaptor.getRhs())) {
2955 return IntegerAttr::get(getType(), val);
2963template <
typename Range>
2965 size_t commonPrefixLength = 0;
2966 auto ia = a.begin();
2967 auto ib = b.begin();
2969 for (; ia != a.end() && ib != b.end(); ia++, ib++, commonPrefixLength++) {
2975 return commonPrefixLength;
2979 size_t totalWidth = 0;
2980 for (
auto operand : operands) {
2983 ssize_t width = operand.getType().getIntOrFloatBitWidth();
2985 totalWidth += width;
2995 PatternRewriter &rewriter) {
2999 SmallVector<Value> lhsOperands, rhsOperands;
3002 ArrayRef<Value> lhsOperandsRef = lhsOperands, rhsOperandsRef = rhsOperands;
3004 auto formCatOrReplicate = [&](Location loc,
3005 ArrayRef<Value> operands) -> Value {
3006 assert(!operands.empty());
3007 Value sameElement = operands[0];
3008 for (
size_t i = 1, e = operands.size(); i != e && sameElement; ++i)
3009 if (sameElement != operands[i])
3010 sameElement = Value();
3012 return rewriter.createOrFold<ReplicateOp>(loc, sameElement,
3014 return rewriter.createOrFold<
ConcatOp>(loc, operands);
3017 auto replaceWith = [&](ICmpPredicate predicate, Value lhs,
3018 Value rhs) -> LogicalResult {
3019 replaceOpWithNewOpAndCopyNamehint<ICmpOp>(rewriter, op, predicate, lhs, rhs,
3024 size_t commonPrefixLength =
3026 if (commonPrefixLength == lhsOperands.size()) {
3029 replaceOpWithNewOpAndCopyNamehint<hw::ConstantOp>(rewriter, op,
3035 llvm::reverse(lhsOperandsRef), llvm::reverse(rhsOperandsRef));
3037 size_t commonPrefixTotalWidth =
3038 getTotalWidth(lhsOperandsRef.take_front(commonPrefixLength));
3039 size_t commonSuffixTotalWidth =
3040 getTotalWidth(lhsOperandsRef.take_back(commonSuffixLength));
3041 auto lhsOnly = lhsOperandsRef.drop_front(commonPrefixLength)
3042 .drop_back(commonSuffixLength);
3043 auto rhsOnly = rhsOperandsRef.drop_front(commonPrefixLength)
3044 .drop_back(commonSuffixLength);
3046 auto replaceWithoutReplicatingSignBit = [&]() {
3047 auto newLhs = formCatOrReplicate(lhs->getLoc(), lhsOnly);
3048 auto newRhs = formCatOrReplicate(rhs->getLoc(), rhsOnly);
3049 return replaceWith(op.getPredicate(), newLhs, newRhs);
3052 auto replaceWithReplicatingSignBit = [&]() {
3053 auto firstNonEmptyValue = lhsOperands[0];
3054 auto firstNonEmptyElemWidth =
3055 firstNonEmptyValue.getType().getIntOrFloatBitWidth();
3056 Value signBit = rewriter.createOrFold<
ExtractOp>(
3057 op.getLoc(), firstNonEmptyValue, firstNonEmptyElemWidth - 1, 1);
3059 auto newLhs = ConcatOp::create(rewriter, lhs->getLoc(), signBit, lhsOnly);
3060 auto newRhs = ConcatOp::create(rewriter, rhs->getLoc(), signBit, rhsOnly);
3061 return replaceWith(op.getPredicate(), newLhs, newRhs);
3064 if (ICmpOp::isPredicateSigned(op.getPredicate())) {
3066 if (commonPrefixTotalWidth == 0 && commonSuffixTotalWidth > 0)
3067 return replaceWithoutReplicatingSignBit();
3073 if (commonPrefixTotalWidth > 1 || commonSuffixTotalWidth > 0)
3074 return replaceWithReplicatingSignBit();
3076 }
else if (commonPrefixTotalWidth > 0 || commonSuffixTotalWidth > 0) {
3078 return replaceWithoutReplicatingSignBit();
3092 ICmpOp cmpOp,
const KnownBits &bitAnalysis,
const APInt &rhsCst,
3093 PatternRewriter &rewriter) {
3097 APInt bitsKnown = bitAnalysis.Zero | bitAnalysis.One;
3098 if ((bitsKnown & rhsCst) != bitAnalysis.One) {
3101 bool result = cmpOp.getPredicate() == ICmpPredicate::ne;
3102 replaceOpWithNewOpAndCopyNamehint<hw::ConstantOp>(rewriter, cmpOp,
3110 SmallVector<Value> newConcatOperands;
3111 auto newConstant = APInt::getZeroWidth();
3116 unsigned knownMSB = bitsKnown.countLeadingOnes();
3118 Value operand = cmpOp.getLhs();
3123 while (knownMSB != bitsKnown.getBitWidth()) {
3126 bitsKnown = bitsKnown.trunc(bitsKnown.getBitWidth() - knownMSB);
3129 unsigned unknownBits = bitsKnown.countLeadingZeros();
3130 unsigned lowBit = bitsKnown.getBitWidth() - unknownBits;
3131 auto spanOperand = rewriter.createOrFold<
ExtractOp>(
3132 operand.getLoc(), operand, lowBit,
3134 auto spanConstant = rhsCst.lshr(lowBit).trunc(unknownBits);
3137 newConcatOperands.push_back(spanOperand);
3140 if (newConstant.getBitWidth() != 0)
3141 newConstant = newConstant.concat(spanConstant);
3143 newConstant = spanConstant;
3146 unsigned newWidth = bitsKnown.getBitWidth() - unknownBits;
3147 bitsKnown = bitsKnown.trunc(newWidth);
3148 knownMSB = bitsKnown.countLeadingOnes();
3154 if (newConcatOperands.empty()) {
3155 bool result = cmpOp.getPredicate() == ICmpPredicate::eq;
3156 replaceOpWithNewOpAndCopyNamehint<hw::ConstantOp>(rewriter, cmpOp,
3162 Value concatResult =
3163 rewriter.createOrFold<
ConcatOp>(operand.getLoc(), newConcatOperands);
3167 rewriter, cmpOp.getOperand(1).getLoc(), newConstant);
3169 replaceOpWithNewOpAndCopyNamehint<ICmpOp>(rewriter, cmpOp,
3170 cmpOp.getPredicate(), concatResult,
3171 newConstantOp, cmpOp.getTwoState());
3177 PatternRewriter &rewriter) {
3178 auto ip = rewriter.saveInsertionPoint();
3179 rewriter.setInsertionPoint(xorOp);
3181 auto xorRHS = xorOp.getOperands().back().getDefiningOp<
hw::ConstantOp>();
3183 xorRHS.getValue() ^ rhs);
3185 switch (xorOp.getNumOperands()) {
3189 APInt::getZero(rhs.getBitWidth()));
3193 newLHS = xorOp.getOperand(0);
3197 SmallVector<Value> newOperands(xorOp.getOperands());
3198 newOperands.pop_back();
3199 newLHS = XorOp::create(rewriter, xorOp.getLoc(), newOperands,
false);
3203 bool xorMultipleUses = !xorOp->hasOneUse();
3207 if (xorMultipleUses)
3208 replaceOpWithNewOpAndCopyNamehint<XorOp>(rewriter, xorOp, newLHS, xorRHS,
3212 rewriter.restoreInsertionPoint(ip);
3213 replaceOpWithNewOpAndCopyNamehint<ICmpOp>(
3214 rewriter, cmpOp, cmpOp.getPredicate(), newLHS, newRHS,
false);
3217LogicalResult ICmpOp::canonicalize(ICmpOp op, PatternRewriter &rewriter) {
3223 if (matchPattern(op.getLhs(), m_ConstantInt(&lhs))) {
3224 assert(!matchPattern(op.getRhs(), m_ConstantInt(&rhs)) &&
3225 "Should be folded");
3226 replaceOpWithNewOpAndCopyNamehint<ICmpOp>(
3227 rewriter, op, ICmpOp::getFlippedPredicate(op.getPredicate()),
3228 op.getRhs(), op.getLhs(), op.getTwoState());
3233 if (matchPattern(op.getRhs(), m_ConstantInt(&rhs))) {
3238 auto replaceWith = [&](ICmpPredicate predicate, Value lhs,
3239 Value rhs) -> LogicalResult {
3240 replaceOpWithNewOpAndCopyNamehint<ICmpOp>(rewriter, op, predicate, lhs,
3241 rhs, op.getTwoState());
3245 auto replaceWithConstantI1 = [&](
bool constant) -> LogicalResult {
3246 replaceOpWithNewOpAndCopyNamehint<hw::ConstantOp>(rewriter, op,
3247 APInt(1, constant));
3251 switch (op.getPredicate()) {
3252 case ICmpPredicate::slt:
3254 if (rhs.isMaxSignedValue())
3255 return replaceWith(ICmpPredicate::ne, op.getLhs(), op.getRhs());
3257 if (rhs.isMinSignedValue())
3258 return replaceWithConstantI1(0);
3260 if ((rhs - 1).isMinSignedValue())
3261 return replaceWith(ICmpPredicate::eq, op.getLhs(),
3264 case ICmpPredicate::sgt:
3266 if (rhs.isMinSignedValue())
3267 return replaceWith(ICmpPredicate::ne, op.getLhs(), op.getRhs());
3269 if (rhs.isMaxSignedValue())
3270 return replaceWithConstantI1(0);
3272 if ((rhs + 1).isMaxSignedValue())
3273 return replaceWith(ICmpPredicate::eq, op.getLhs(),
3276 case ICmpPredicate::ult:
3278 if (rhs.isAllOnes())
3279 return replaceWith(ICmpPredicate::ne, op.getLhs(), op.getRhs());
3282 return replaceWithConstantI1(0);
3284 if ((rhs - 1).isZero())
3285 return replaceWith(ICmpPredicate::eq, op.getLhs(),
3289 if (rhs.countLeadingOnes() + rhs.countTrailingZeros() ==
3290 rhs.getBitWidth()) {
3291 auto numOnes = rhs.countLeadingOnes();
3293 rhs.getBitWidth() - numOnes, numOnes);
3294 return replaceWith(ICmpPredicate::ne, smaller,
3299 case ICmpPredicate::ugt:
3302 return replaceWith(ICmpPredicate::ne, op.getLhs(), op.getRhs());
3304 if (rhs.isAllOnes())
3305 return replaceWithConstantI1(0);
3307 if ((rhs + 1).isAllOnes())
3308 return replaceWith(ICmpPredicate::eq, op.getLhs(),
3312 if ((rhs + 1).isPowerOf2()) {
3313 auto numOnes = rhs.countTrailingOnes();
3314 auto newWidth = rhs.getBitWidth() - numOnes;
3317 return replaceWith(ICmpPredicate::ne, smaller,
3322 case ICmpPredicate::sle:
3324 if (rhs.isMaxSignedValue())
3325 return replaceWithConstantI1(1);
3327 return replaceWith(ICmpPredicate::slt, op.getLhs(),
getConstant(rhs + 1));
3328 case ICmpPredicate::sge:
3330 if (rhs.isMinSignedValue())
3331 return replaceWithConstantI1(1);
3333 return replaceWith(ICmpPredicate::sgt, op.getLhs(),
getConstant(rhs - 1));
3334 case ICmpPredicate::ule:
3336 if (rhs.isAllOnes())
3337 return replaceWithConstantI1(1);
3339 return replaceWith(ICmpPredicate::ult, op.getLhs(),
getConstant(rhs + 1));
3340 case ICmpPredicate::uge:
3343 return replaceWithConstantI1(1);
3345 return replaceWith(ICmpPredicate::ugt, op.getLhs(),
getConstant(rhs - 1));
3346 case ICmpPredicate::eq:
3347 if (rhs.getBitWidth() == 1) {
3350 replaceOpWithNewOpAndCopyNamehint<XorOp>(rewriter, op, op.getLhs(),
3355 if (rhs.isAllOnes()) {
3362 case ICmpPredicate::ne:
3363 if (rhs.getBitWidth() == 1) {
3369 if (rhs.isAllOnes()) {
3371 replaceOpWithNewOpAndCopyNamehint<XorOp>(rewriter, op, op.getLhs(),
3378 case ICmpPredicate::ceq:
3379 case ICmpPredicate::cne:
3380 case ICmpPredicate::weq:
3381 case ICmpPredicate::wne:
3387 if (op.getPredicate() == ICmpPredicate::eq ||
3388 op.getPredicate() == ICmpPredicate::ne) {
3393 if (!knownBits.isUnknown())
3400 if (
auto xorOp = op.getLhs().getDefiningOp<
XorOp>())
3407 if (
auto replicateOp = op.getLhs().getDefiningOp<ReplicateOp>())
3408 if (rhs.isAllOnes() || rhs.isZero()) {
3409 auto width = replicateOp.getInput().getType().getIntOrFloatBitWidth();
3412 rhs.isAllOnes() ? APInt::getAllOnes(width)
3413 : APInt::getZero(width));
3414 replaceOpWithNewOpAndCopyNamehint<ICmpOp>(
3415 rewriter, op, op.getPredicate(), replicateOp.getInput(), cst,
3425 if (Operation *opLHS = op.getLhs().getDefiningOp())
3426 if (Operation *opRHS = op.getRhs().getDefiningOp())
3427 if (isa<ConcatOp, ReplicateOp>(opLHS) &&
3428 isa<ConcatOp, ReplicateOp>(opRHS)) {
assert(baseType &&"element must be base type")
static KnownBits computeKnownBits(Value v, unsigned depth)
Given an integer SSA value, check to see if we know anything about the result of the computation.
static bool foldMuxOfUniformArrays(MuxOp op, PatternRewriter &rewriter)
static Attribute constFoldAssociativeOp(ArrayRef< Attribute > operands, hw::PEO paramOpcode)
static Attribute constFoldBinaryOp(ArrayRef< Attribute > operands, hw::PEO paramOpcode)
Performs constant folding calculate with element-wise behavior on the two attributes in operands and ...
static bool applyCmpPredicateToEqualOperands(ICmpPredicate predicate)
static ComplementMatcher< SubType > m_Complement(const SubType &subExpr)
static bool canonicalizeLogicalCstWithConcat(Operation *logicalOp, size_t concatIdx, const APInt &cst, PatternRewriter &rewriter)
When we find a logical operation (and, or, xor) with a constant e.g.
static bool narrowOperationWidth(OpTy op, bool narrowTrailingBits, PatternRewriter &rewriter)
static OpFoldResult foldDiv(Op op, ArrayRef< Attribute > constants)
static Value getCommonOperand(Op op)
Returns a single common operand that all inputs of the operation op can be traced back to,...
static bool canCombineOppositeBinCmpIntoConstant(OperandRange operands)
static void getConcatOperands(Value v, SmallVectorImpl< Value > &result)
Flatten concat and mux operands into a vector.
static Value extractOperandFromFullyAssociative(Operation *fullyAssoc, size_t operandNo, PatternRewriter &rewriter)
Given a fully associative variadic operation like (a+b+c+d), break the expression into two parts,...
static bool getMuxChainCondConstant(Value cond, Value indexValue, bool isInverted, std::function< void(hw::ConstantOp)> constantFn)
Check to see if the condition to the specified mux is an equality comparison indexValue and one or mo...
static TypedAttr getIntAttr(const APInt &value, MLIRContext *context)
static bool shouldBeFlattened(Operation *op)
Return true if the op will be flattened afterwards.
static void canonicalizeXorIcmpTrue(XorOp op, unsigned icmpOperand, PatternRewriter &rewriter)
static bool assumeMuxCondInOperand(Value muxCond, Value muxValue, bool constCond, PatternRewriter &rewriter)
If the mux condition is an operand to the op defining its true or false value, replace the condition ...
static bool extractFromReplicate(ExtractOp op, ReplicateOp replicate, PatternRewriter &rewriter)
static void combineEqualityICmpWithXorOfConstant(ICmpOp cmpOp, XorOp xorOp, const APInt &rhs, PatternRewriter &rewriter)
static size_t getTotalWidth(ArrayRef< Value > operands)
static bool foldCommonMuxOperation(MuxOp mux, Operation *trueOp, Operation *falseOp, PatternRewriter &rewriter)
This function is invoke when we find a mux with true/false operations that have the same opcode.
static std::pair< size_t, size_t > getLowestBitAndHighestBitRequired(Operation *op, bool narrowTrailingBits, size_t originalOpWidth)
static bool tryFlatteningOperands(Operation *op, PatternRewriter &rewriter)
Flattens a single input in op if hasOneUse is true and it can be defined as an Op.
static bool isOpTriviallyRecursive(Operation *op)
static LogicalResult extractConcatToConcatExtract(ExtractOp op, ConcatOp innerCat, PatternRewriter &rewriter, ArrayRef< size_t > prefixWidths={})
static bool canonicalizeIdempotentInputs(Op op, PatternRewriter &rewriter)
Canonicalize an idempotent operation op so that only one input of any kind occurs.
static bool applyCmpPredicate(ICmpPredicate predicate, const APInt &lhs, const APInt &rhs)
static void combineEqualityICmpWithKnownBitsAndConstant(ICmpOp cmpOp, const KnownBits &bitAnalysis, const APInt &rhsCst, PatternRewriter &rewriter)
Given an equality comparison with a constant value and some operand that has known bits,...
static bool hasSVAttributes(Operation *op)
static OpFoldResult foldMod(Op op, ArrayRef< Attribute > constants)
static size_t computeCommonPrefixLength(const Range &a, const Range &b)
static bool foldCommonMuxValue(MuxOp op, bool isTrueOperand, PatternRewriter &rewriter)
Fold things like mux(cond, x|y|z|a, a) -> (x|y|z)&replicate(cond)|a and mux(cond, a,...
static LogicalResult matchAndRewriteCompareConcat(ICmpOp op, Operation *lhs, Operation *rhs, PatternRewriter &rewriter)
Reduce the strength icmp(concat(...), concat(...)) by doing a element-wise comparison on common prefi...
static Value createGenericOp(Location loc, OperationName name, ArrayRef< Value > operands, OpBuilder &builder)
Create a new instance of a generic operation that only has value operands, and has a single result va...
static TypedAttr getIntAttr(MLIRContext *ctx, Type t, const APInt &value)
static std::unique_ptr< Context > context
static std::optional< APSInt > getConstant(Attribute operand)
Determine the value of a constant operand for the sake of constant folding.
create(elements, Type result_type=None)
create(array_value, low_index, ret_type)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
void extractBits(OpBuilder &builder, Value val, SmallVectorImpl< Value > &bits)
Extract bits from a value.
bool foldMuxChainWithComparison(PatternRewriter &rewriter, MuxOp rootMux, bool isFalseSide, llvm::function_ref< MuxChainWithComparisonFoldingStyle(size_t indexWidth, size_t numEntries)> styleFn)
Mux chain folding that converts chains of muxes with index comparisons into array operations or balan...
Value createOrFoldNot(OpBuilder &builder, Location loc, Value value, bool twoState=false)
Create a `‘Not’' gate on a value.
MuxChainWithComparisonFoldingStyle
Enum for mux chain folding styles.
LogicalResult convertModUByPowerOfTwo(ModUOp modOp, mlir::PatternRewriter &rewriter)
KnownBits computeKnownBits(Value value)
Compute "known bits" information about the specified value - the set of bits that are guaranteed to a...
Value constructMuxTree(OpBuilder &builder, Location loc, ArrayRef< Value > selectors, ArrayRef< Value > leafNodes, Value outOfBoundsValue)
Construct a mux tree for given leaf nodes.
Value createOrFoldSExt(OpBuilder &builder, Location loc, Value value, Type destTy)
Create a sign extension operation from a value of integer type to an equal or larger integer type.
LogicalResult convertDivUByPowerOfTwo(DivUOp divOp, mlir::PatternRewriter &rewriter)
Convert unsigned division or modulo by a power of two.
uint64_t getWidth(Type t)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
void replaceOpAndCopyNamehint(PatternRewriter &rewriter, Operation *op, Value newValue)
A wrapper of PatternRewriter::replaceOp to propagate "sv.namehint" attribute.