14#include "mlir/IR/Diagnostics.h"
15#include "mlir/IR/Matchers.h"
16#include "mlir/IR/PatternMatch.h"
17#include "llvm/ADT/APInt.h"
18#include "llvm/ADT/SetVector.h"
19#include "llvm/ADT/SmallBitVector.h"
20#include "llvm/ADT/TypeSwitch.h"
21#include "llvm/Support/KnownBits.h"
26using namespace matchers;
30 return llvm::any_of(op->getOperands(), [op](
auto operand) {
31 return operand.getDefiningOp() == op;
41 ArrayRef<Value> operands, OpBuilder &builder) {
42 OperationState state(loc, name);
43 state.addOperands(operands);
44 state.addTypes(operands[0].getType());
45 return builder.create(state)->getResult(0);
49 return IntegerAttr::get(IntegerType::get(
context, value.getBitWidth()),
56 for (
auto op :
concat.getOperands())
58 }
else if (
auto repl = v.getDefiningOp<ReplicateOp>()) {
59 for (
size_t i = 0, e = repl.getMultiple(); i != e; ++i)
70 return op->hasAttr(
"sv.attributes");
74template <
typename SubType>
75struct ComplementMatcher {
77 ComplementMatcher(SubType lhs) : lhs(std::move(lhs)) {}
78 bool match(Operation *op) {
79 auto xorOp = dyn_cast<XorOp>(op);
80 return xorOp && xorOp.isBinaryNot() &&
81 mlir::detail::matchOperandOrValueAtIndex(op, 0, lhs);
86template <
typename SubType>
87static inline ComplementMatcher<SubType>
m_Complement(
const SubType &subExpr) {
88 return ComplementMatcher<SubType>(subExpr);
94 assert((isa<AndOp, OrOp, XorOp, AddOp, MulOp>(op) &&
95 "must be commutative operations"));
96 if (op->hasOneUse()) {
97 auto *user = *op->getUsers().begin();
98 return user->getName() == op->getName() &&
99 op->getAttrOfType<UnitAttr>(
"twoState") ==
100 user->getAttrOfType<UnitAttr>(
"twoState") &&
101 op->getBlock() == user->getBlock();
116 auto inputs = op->getOperands();
118 SmallVector<Value, 4> newOperands;
119 SmallVector<Location, 4> newLocations{op->getLoc()};
120 newOperands.reserve(inputs.size());
122 decltype(inputs.begin()) current, end;
125 SmallVector<Element> worklist;
126 worklist.push_back({inputs.begin(), inputs.end()});
127 bool binFlag = op->hasAttrOfType<UnitAttr>(
"twoState");
128 bool changed =
false;
129 while (!worklist.empty()) {
130 auto &element = worklist.back();
133 if (element.current == element.end) {
138 Value value = *element.current++;
139 auto *flattenOp = value.getDefiningOp();
142 if (!flattenOp || flattenOp->getName() != op->getName() ||
143 flattenOp == op || binFlag != op->hasAttrOfType<UnitAttr>(
"twoState") ||
144 flattenOp->getBlock() != op->getBlock()) {
145 newOperands.push_back(value);
150 if (!value.hasOneUse()) {
158 if (flattenOp->getNumOperands() != 2 || !isa<AndOp, OrOp, XorOp>(op) ||
161 newOperands.push_back(value);
169 auto flattenOpInputs = flattenOp->getOperands();
170 worklist.push_back({flattenOpInputs.begin(), flattenOpInputs.end()});
171 newLocations.push_back(flattenOp->getLoc());
177 Value result =
createGenericOp(FusedLoc::get(op->getContext(), newLocations),
178 op->getName(), newOperands, rewriter);
180 result.getDefiningOp()->setAttr(
"twoState", rewriter.getUnitAttr());
188static std::pair<size_t, size_t>
190 size_t originalOpWidth) {
191 auto users = op->getUsers();
193 "getLowestBitAndHighestBitRequired cannot operate on "
194 "a empty list of uses.");
198 size_t lowestBitRequired = narrowTrailingBits ? originalOpWidth - 1 : 0;
199 size_t highestBitRequired = 0;
201 for (
auto *user : users) {
202 if (
auto extractOp = dyn_cast<ExtractOp>(user)) {
203 size_t lowBit = extractOp.getLowBit();
205 cast<IntegerType>(extractOp.getType()).getWidth() + lowBit - 1;
206 highestBitRequired = std::max(highestBitRequired, highBit);
207 lowestBitRequired = std::min(lowestBitRequired, lowBit);
211 highestBitRequired = originalOpWidth - 1;
212 lowestBitRequired = 0;
216 return {lowestBitRequired, highestBitRequired};
221 PatternRewriter &rewriter) {
222 IntegerType opType = dyn_cast<IntegerType>(op.getResult().getType());
226 if (highestBit + 1 == opType.getWidth() && lowestBit == 0)
229 SmallVector<Value> args;
230 auto newType = rewriter.getIntegerType(highestBit - lowestBit + 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 typename OpTy::Properties properties{};
240 OpTy::populateDefaultProperties(
241 OperationName(OpTy::getOperationName(), rewriter.getContext()),
243 auto newop = OpTy::create(rewriter, op.getLoc(), newType, args, properties);
244 newop->setDialectAttrs(op->getDialectAttrs());
245 if (op.getTwoState())
246 newop.setTwoState(
true);
248 Value newResult = newop.getResult();
250 newResult = rewriter.createOrFold<
ConcatOp>(
251 op.getLoc(), newResult,
253 APInt::getZero(lowestBit)));
254 if (highestBit + 1 < opType.getWidth())
255 newResult = rewriter.createOrFold<
ConcatOp>(
258 rewriter, op.getLoc(),
259 APInt::getZero(opType.getWidth() - highestBit - 1)),
261 rewriter.replaceOp(op, newResult);
267 PatternRewriter &rewriter) {
268 IntegerType opType = dyn_cast<IntegerType>(op.getResult().getType());
273 op, narrowTrailingBits, opType.getWidth());
281OpFoldResult ReplicateOp::fold(FoldAdaptor adaptor) {
286 if (cast<IntegerType>(getType()).
getWidth() ==
287 getInput().getType().getIntOrFloatBitWidth())
291 if (
auto input = dyn_cast_or_null<IntegerAttr>(adaptor.getInput())) {
292 if (input.getValue().getBitWidth() == 1) {
293 if (input.getValue().isZero())
295 APInt::getZero(cast<IntegerType>(getType()).
getWidth()),
298 APInt::getAllOnes(cast<IntegerType>(getType()).
getWidth()),
302 APInt result = APInt::getZeroWidth();
303 for (
auto i = getMultiple(); i != 0; --i)
304 result = result.concat(input.getValue());
311OpFoldResult ParityOp::fold(FoldAdaptor adaptor) {
316 if (
auto input = dyn_cast_or_null<IntegerAttr>(adaptor.getInput()))
317 return getIntAttr(APInt(1, input.getValue().popcount() & 1), getContext());
320 if (hw::getBitWidth(getInput().getType()) == 1)
326LogicalResult ParityOp::canonicalize(
ParityOp op, PatternRewriter &rewriter) {
331 auto isParityZero = [](Value v) {
333 return matchPattern(v, m_ConstantInt(&value)) && value.popcount() % 2 == 0;
338 auto concat = op.getInput().getDefiningOp<ConcatOp>();
342 auto operands =
concat.getInputs();
343 if (operands.size() != 2)
346 if (isParityZero(operands[0])) {
352 if (isParityZero(operands[1])) {
368 hw::PEO paramOpcode) {
369 assert(operands.size() == 2 &&
"binary op takes two operands");
370 if (!operands[0] || !operands[1])
375 return hw::ParamExprAttr::get(paramOpcode, cast<TypedAttr>(operands[0]),
376 cast<TypedAttr>(operands[1]));
379OpFoldResult ShlOp::fold(FoldAdaptor adaptor) {
383 if (
auto rhs = dyn_cast_or_null<IntegerAttr>(adaptor.getRhs())) {
384 if (rhs.getValue().isZero())
385 return getOperand(0);
387 unsigned width = getType().getIntOrFloatBitWidth();
388 if (rhs.getValue().uge(width))
389 return getIntAttr(APInt::getZero(width), getContext());
394LogicalResult ShlOp::canonicalize(
ShlOp op, PatternRewriter &rewriter) {
398 unsigned width = cast<IntegerType>(op.getLhs().getType()).getWidth();
402 if (matchPattern(op.getRhs(), m_ConstantInt(&value))) {
403 if (value.ugt(width))
405 unsigned shift = value.getZExtValue();
408 if (width <= shift || shift == 0)
429 size_t newWidth = highestBit + 1;
430 if (newWidth < 2 || newWidth >= width)
438OpFoldResult ShrUOp::fold(FoldAdaptor adaptor) {
442 if (
auto rhs = dyn_cast_or_null<IntegerAttr>(adaptor.getRhs())) {
443 if (rhs.getValue().isZero())
444 return getOperand(0);
446 unsigned width = getType().getIntOrFloatBitWidth();
447 if (rhs.getValue().uge(width))
448 return getIntAttr(APInt::getZero(width), getContext());
453LogicalResult ShrUOp::canonicalize(
ShrUOp op, PatternRewriter &rewriter) {
459 if (!matchPattern(op.getRhs(), m_ConstantInt(&value)))
462 unsigned width = cast<IntegerType>(op.getLhs().getType()).getWidth();
463 if (value.ugt(width))
465 unsigned shift = value.getZExtValue();
468 if (width <= shift || shift == 0)
482OpFoldResult ShrSOp::fold(FoldAdaptor adaptor) {
486 if (
auto rhs = dyn_cast_or_null<IntegerAttr>(adaptor.getRhs()))
487 if (rhs.getValue().isZero())
488 return getOperand(0);
492LogicalResult ShrSOp::canonicalize(
ShrSOp op, PatternRewriter &rewriter) {
498 if (!matchPattern(op.getRhs(), m_ConstantInt(&value)))
501 unsigned width = cast<IntegerType>(op.getLhs().getType()).getWidth();
502 if (value.ugt(width))
504 unsigned shift = value.getZExtValue();
507 rewriter.createOrFold<ExtractOp>(op.getLoc(), op.getLhs(), width - 1, 1);
508 auto sext = rewriter.createOrFold<ReplicateOp>(op.getLoc(), topbit, shift);
510 if (width == shift) {
526OpFoldResult ExtractOp::fold(FoldAdaptor adaptor) {
531 if (getInput().getType() == getType())
535 if (
auto input = dyn_cast_or_null<IntegerAttr>(adaptor.getInput())) {
536 unsigned dstWidth = cast<IntegerType>(getType()).getWidth();
537 return getIntAttr(input.getValue().lshr(getLowBit()).trunc(dstWidth),
553 PatternRewriter &rewriter,
554 ArrayRef<size_t> prefixWidths = {}) {
555 auto concatInputs = innerCat.getInputs();
556 size_t numOperands = concatInputs.size();
557 size_t lowBit = op.getLowBit();
562 if (!prefixWidths.empty()) {
564 const auto *it = llvm::upper_bound(prefixWidths, lowBit);
565 assert(it != prefixWidths.end());
566 firstIdx = it - prefixWidths.begin();
567 beginOfFirst = (firstIdx > 0) ? prefixWidths[firstIdx - 1] : 0;
572 for (
size_t i = 0; i < numOperands; ++i) {
574 concatInputs[numOperands - 1 - i].getType().getIntOrFloatBitWidth();
575 if (lowBit < beginOfFirst + w) {
583 SmallVector<Value> reverseConcatArgs;
584 size_t widthRemaining = op.getType().getIntOrFloatBitWidth();
585 size_t extractLo = lowBit - beginOfFirst;
590 for (
size_t i = firstIdx; widthRemaining != 0 && i < numOperands; ++i) {
591 Value concatArg = concatInputs[numOperands - 1 - i];
592 size_t operandWidth = concatArg.getType().getIntOrFloatBitWidth();
593 size_t widthToConsume = std::min(widthRemaining, operandWidth - extractLo);
595 if (widthToConsume == operandWidth && extractLo == 0) {
596 reverseConcatArgs.push_back(concatArg);
598 auto resultType = IntegerType::get(rewriter.getContext(), widthToConsume);
600 rewriter, op.getLoc(), resultType, concatArg, extractLo));
603 widthRemaining -= widthToConsume;
608 if (reverseConcatArgs.size() == 1) {
612 rewriter, op, SmallVector<Value>(llvm::reverse(reverseConcatArgs)));
619 PatternRewriter &rewriter) {
620 auto extractResultWidth = cast<IntegerType>(op.getType()).getWidth();
621 auto replicateEltWidth =
622 replicate.getOperand().getType().getIntOrFloatBitWidth();
626 if (op.getLowBit() % replicateEltWidth == 0 &&
627 extractResultWidth % replicateEltWidth == 0) {
629 replicate.getOperand());
635 if (op.getLowBit() % replicateEltWidth + extractResultWidth <=
638 rewriter, op, op.getType(), replicate.getOperand(),
639 op.getLowBit() % replicateEltWidth);
656 unsigned bitIdx = op.getLowBit();
657 if (cast<IntegerType>(op.getType()).getWidth() != 1 || bitIdx == 0 ||
658 add.getInputs().size() != 2 || !add->hasOneUse())
661 Value lhs = add.getInputs()[0];
662 Value rhs = add.getInputs()[1];
664 if (!rhsKnown.Zero[bitIdx] && !rhsKnown.One[bitIdx])
667 if (!lhsKnown.Zero[bitIdx] && !lhsKnown.One[bitIdx])
670 KnownBits rhsLow = rhsKnown.trunc(bitIdx);
671 if (!rhsLow.isConstant()) {
672 rhsLow = lhsKnown.trunc(bitIdx);
673 if (!rhsLow.isConstant())
678 bool carrySetsBit = lhsKnown.One[bitIdx] == rhsKnown.One[bitIdx];
679 Value lowLhs = rewriter.createOrFold<
ExtractOp>(op.getLoc(), lhs, 0, bitIdx);
683 rewriter, op, carrySetsBit ? ICmpPredicate::ugt : ICmpPredicate::ule,
684 lowLhs, invCst, add.getTwoState());
688LogicalResult ExtractOp::canonicalize(
ExtractOp op, PatternRewriter &rewriter) {
691 auto *inputOp = op.getInput().getDefiningOp();
698 .extractBits(cast<IntegerType>(op.getType()).getWidth(),
700 if (knownBits.isConstant()) {
702 knownBits.getConstant());
708 if (
auto innerExtract = dyn_cast_or_null<ExtractOp>(inputOp)) {
710 rewriter, op, op.getType(), innerExtract.getInput(),
711 innerExtract.getLowBit() + op.getLowBit());
716 if (
auto innerCat = dyn_cast_or_null<ConcatOp>(inputOp))
720 if (
auto replicate = dyn_cast_or_null<ReplicateOp>(inputOp))
726 if (inputOp && inputOp->getNumOperands() == 2 &&
727 isa<AndOp, OrOp, XorOp>(inputOp)) {
728 if (
auto cstRHS = inputOp->getOperand(1).getDefiningOp<hw::ConstantOp>()) {
729 auto extractedCst = cstRHS.getValue().extractBits(
730 cast<IntegerType>(op.getType()).getWidth(), op.getLowBit());
731 if (isa<OrOp, XorOp>(inputOp) && extractedCst.isZero()) {
733 rewriter, op, op.getType(), inputOp->getOperand(0), op.getLowBit());
741 if (isa<AndOp>(inputOp)) {
744 unsigned lz = extractedCst.countLeadingZeros();
745 unsigned tz = extractedCst.countTrailingZeros();
746 unsigned pop = extractedCst.popcount();
747 if (extractedCst.getBitWidth() - lz - tz == pop) {
748 auto resultTy = rewriter.getIntegerType(pop);
749 SmallVector<Value> resultElts;
752 APInt::getZero(lz)));
753 resultElts.push_back(rewriter.createOrFold<ExtractOp>(
754 op.getLoc(), resultTy, inputOp->getOperand(0),
755 op.getLowBit() + tz));
758 APInt::getZero(tz)));
768 if (cast<IntegerType>(op.getType()).getWidth() == 1 && inputOp)
769 if (
auto shlOp = dyn_cast<ShlOp>(inputOp)) {
771 if (shlOp->hasOneUse())
772 if (
auto lhsCst = shlOp.getLhs().getDefiningOp<hw::ConstantOp>())
773 if (lhsCst.getValue().isOne()) {
775 rewriter, shlOp.getLoc(),
776 APInt(lhsCst.getValue().getBitWidth(), op.getLowBit()));
778 rewriter, op, ICmpPredicate::eq, shlOp->getOperand(1), newCst,
785 if (
auto addOp = dyn_cast_or_null<AddOp>(inputOp))
799 hw::PEO paramOpcode) {
800 assert(operands.size() > 1 &&
"caller should handle one-operand case");
803 if (!operands[1] || !operands[0])
807 if (llvm::all_of(operands.drop_front(2),
808 [&](Attribute in) { return !!in; })) {
809 SmallVector<mlir::TypedAttr> typedOperands;
810 typedOperands.reserve(operands.size());
811 for (
auto operand : operands) {
812 if (
auto typedOperand = dyn_cast<mlir::TypedAttr>(operand))
813 typedOperands.push_back(typedOperand);
817 if (typedOperands.size() == operands.size())
818 return hw::ParamExprAttr::get(paramOpcode, typedOperands);
834 size_t concatIdx,
const APInt &cst,
835 PatternRewriter &rewriter) {
836 auto concatOp = logicalOp->getOperand(concatIdx).getDefiningOp<
ConcatOp>();
837 assert((isa<AndOp, OrOp, XorOp>(logicalOp) && concatOp));
842 llvm::any_of(concatOp->getOperands(), [&](Value operand) ->
bool {
843 auto *operandOp = operand.getDefiningOp();
848 if (isa<hw::ConstantOp>(operandOp))
852 return operandOp->getName() == logicalOp->getName() &&
853 operandOp->hasOneUse() && operandOp->getNumOperands() != 0 &&
854 operandOp->getOperands().back().getDefiningOp<hw::ConstantOp>();
862 auto createLogicalOp = [&](ArrayRef<Value> operands) -> Value {
863 return createGenericOp(logicalOp->getLoc(), logicalOp->getName(), operands,
870 SmallVector<Value> newConcatOperands;
871 newConcatOperands.reserve(concatOp->getNumOperands());
874 size_t nextOperandBit = concatOp.getType().getIntOrFloatBitWidth();
875 for (Value operand : concatOp->getOperands()) {
876 size_t operandWidth = operand.getType().getIntOrFloatBitWidth();
877 nextOperandBit -= operandWidth;
881 cst.lshr(nextOperandBit).trunc(operandWidth));
883 newConcatOperands.push_back(createLogicalOp({operand, eltCst}));
888 ConcatOp::create(rewriter, concatOp.getLoc(), newConcatOperands);
892 if (logicalOp->getNumOperands() > 2) {
893 auto origOperands = logicalOp->getOperands();
894 SmallVector<Value> operands;
896 operands.append(origOperands.begin(), origOperands.begin() + concatIdx);
898 operands.append(origOperands.begin() + concatIdx + 1,
899 origOperands.begin() + (origOperands.size() - 1));
901 operands.push_back(newResult);
902 newResult = createLogicalOp(operands);
912 llvm::SmallDenseSet<std::tuple<ICmpPredicate, Value, Value>> seenPredicates;
914 for (
auto op : operands) {
915 if (
auto icmpOp = op.getDefiningOp<ICmpOp>();
916 icmpOp && icmpOp.getTwoState()) {
917 auto predicate = icmpOp.getPredicate();
918 auto lhs = icmpOp.getLhs();
919 auto rhs = icmpOp.getRhs();
920 if (seenPredicates.contains(
921 {ICmpOp::getNegatedPredicate(predicate), lhs, rhs}))
924 seenPredicates.insert({predicate, lhs, rhs});
930OpFoldResult AndOp::fold(FoldAdaptor adaptor) {
934 APInt value = APInt::getAllOnes(cast<IntegerType>(getType()).
getWidth());
936 auto inputs = adaptor.getInputs();
939 for (
auto operand : inputs) {
940 auto attr = dyn_cast_or_null<IntegerAttr>(operand);
943 value &= attr.getValue();
949 if (inputs.size() == 2)
950 if (
auto intAttr = dyn_cast_or_null<IntegerAttr>(inputs[1]))
951 if (intAttr.getValue().isAllOnes())
952 return getInputs()[0];
955 if (llvm::all_of(getInputs(),
956 [&](
auto in) {
return in == this->getInputs()[0]; }))
957 return getInputs()[0];
960 for (Value arg : getInputs()) {
963 for (Value arg2 : getInputs())
966 APInt::getZero(cast<IntegerType>(getType()).
getWidth()),
987template <
typename Op>
989 if (!op.getType().isInteger(1))
992 auto inputs = op.getInputs();
993 size_t size = inputs.size();
995 auto sourceOp = inputs[0].template getDefiningOp<ExtractOp>();
998 Value source = sourceOp.getOperand();
1001 if (size != source.getType().getIntOrFloatBitWidth())
1005 llvm::BitVector bits(size);
1006 bits.set(sourceOp.getLowBit());
1008 for (
size_t i = 1; i != size; ++i) {
1009 auto extractOp = inputs[i].template getDefiningOp<ExtractOp>();
1010 if (!extractOp || extractOp.getOperand() != source)
1012 bits.set(extractOp.getLowBit());
1015 return bits.all() ? source : Value();
1022template <
typename Op>
1025 constexpr unsigned limit = 3;
1026 auto inputs = op.getInputs();
1029 llvm::SmallDenseSet<Op, 8> checked;
1032 struct OpWithDepth {
1036 llvm::SmallVector<OpWithDepth, 8> worklist;
1038 auto enqueue = [&worklist, &checked, &op](Value input,
unsigned depth) {
1042 if (depth < limit && input.getParentBlock() == op->getBlock()) {
1043 auto inputOp = input.template getDefiningOp<Op>();
1044 if (inputOp && inputOp.getTwoState() == op.getTwoState() &&
1045 checked.insert(inputOp).second)
1046 worklist.push_back({inputOp, depth + 1});
1050 for (
auto input : uniqueInputs)
1053 while (!worklist.empty()) {
1054 auto item = worklist.pop_back_val();
1056 for (
auto input : item.op.getInputs()) {
1057 uniqueInputs.remove(input);
1058 enqueue(input, item.depth);
1062 if (uniqueInputs.size() < inputs.size()) {
1064 uniqueInputs.getArrayRef(),
1072LogicalResult AndOp::canonicalize(
AndOp op, PatternRewriter &rewriter) {
1076 auto inputs = op.getInputs();
1077 auto size = inputs.size();
1089 assert(size > 1 &&
"expected 2 or more operands, `fold` should handle this");
1093 if (matchPattern(inputs.back(), m_ConstantInt(&value))) {
1095 if (value.isAllOnes()) {
1097 inputs.drop_back(),
false);
1105 if (matchPattern(inputs[size - 2], m_ConstantInt(&value2))) {
1107 SmallVector<Value, 4> newOperands(inputs.drop_back(2));
1108 newOperands.push_back(cst);
1110 newOperands,
false);
1115 if (size == 2 && value.isPowerOf2()) {
1120 if (
auto replicate = inputs[0].getDefiningOp<ReplicateOp>()) {
1121 auto replicateOperand = replicate.getOperand();
1122 if (replicateOperand.getType().isInteger(1)) {
1123 unsigned resultWidth = op.getType().getIntOrFloatBitWidth();
1124 auto trailingZeros = value.countTrailingZeros();
1127 SmallVector<Value, 3> concatOperands;
1128 if (trailingZeros != resultWidth - 1) {
1130 rewriter, op.getLoc(),
1131 APInt::getZero(resultWidth - trailingZeros - 1));
1132 concatOperands.push_back(highZeros);
1134 concatOperands.push_back(replicateOperand);
1135 if (trailingZeros != 0) {
1137 rewriter, op.getLoc(), APInt::getZero(trailingZeros));
1138 concatOperands.push_back(lowZeros);
1141 rewriter, op, op.getType(), concatOperands);
1150 unsigned leadingZeros = value.countLeadingZeros();
1151 unsigned trailingZeros = value.countTrailingZeros();
1152 if (leadingZeros > 0 || trailingZeros > 0) {
1153 unsigned maskLength = value.getBitWidth() - leadingZeros - trailingZeros;
1156 SmallVector<Value> operands;
1157 for (
auto input : inputs.drop_back()) {
1158 unsigned offset = trailingZeros;
1159 while (
auto extractOp = input.getDefiningOp<ExtractOp>()) {
1160 input = extractOp.getInput();
1161 offset += extractOp.getLowBit();
1164 offset, maskLength));
1168 auto narrowMask = value.extractBits(maskLength, trailingZeros);
1169 if (!narrowMask.isAllOnes())
1171 rewriter, inputs.back().getLoc(), narrowMask));
1174 Value narrowValue = operands.back();
1175 if (operands.size() > 1)
1177 AndOp::create(rewriter, op.getLoc(), operands, op.getTwoState());
1181 if (leadingZeros > 0)
1183 rewriter, op.getLoc(), APInt::getZero(leadingZeros)));
1184 operands.push_back(narrowValue);
1185 if (trailingZeros > 0)
1187 rewriter, op.getLoc(), APInt::getZero(trailingZeros)));
1195 for (
size_t i = 0; i < size - 1; ++i) {
1196 if (
auto concat = inputs[i].getDefiningOp<ConcatOp>())
1211 source, cmpAgainst);
1216 if (op.getTwoState() && op.getNumOperands() == 2) {
1217 auto isReplicateOfI1 = [](Value v) {
1218 auto rep = v.getDefiningOp<ReplicateOp>();
1221 return rep.getOperand().getType().isInteger(1);
1223 Value x = op.getOperand(0);
1224 Value y = op.getOperand(1);
1225 if (isReplicateOfI1(x))
1227 if (isReplicateOfI1(y)) {
1228 Value p = y.getDefiningOp<ReplicateOp>().getInput();
1230 rewriter, op.getLoc(), rewriter.getIntegerAttr(op.getType(), 0));
1241OpFoldResult OrOp::fold(FoldAdaptor adaptor) {
1245 auto value = APInt::getZero(cast<IntegerType>(getType()).
getWidth());
1246 auto inputs = adaptor.getInputs();
1248 for (
auto operand : inputs) {
1249 auto attr = dyn_cast_or_null<IntegerAttr>(operand);
1252 value |= attr.getValue();
1253 if (value.isAllOnes())
1258 if (inputs.size() == 2)
1259 if (
auto intAttr = dyn_cast_or_null<IntegerAttr>(inputs[1]))
1260 if (intAttr.getValue().isZero())
1261 return getInputs()[0];
1264 if (llvm::all_of(getInputs(),
1265 [&](
auto in) {
return in == this->getInputs()[0]; }))
1266 return getInputs()[0];
1269 for (Value arg : getInputs()) {
1271 if (matchPattern(arg,
m_Complement(m_Any(&subExpr)))) {
1272 for (Value arg2 : getInputs())
1273 if (arg2 == subExpr)
1275 APInt::getAllOnes(cast<IntegerType>(getType()).
getWidth()),
1285 APInt::getAllOnes(cast<IntegerType>(getType()).
getWidth()),
1292LogicalResult OrOp::canonicalize(
OrOp op, PatternRewriter &rewriter) {
1296 auto inputs = op.getInputs();
1297 auto size = inputs.size();
1309 assert(size > 1 &&
"expected 2 or more operands");
1313 if (matchPattern(inputs.back(), m_ConstantInt(&value))) {
1315 if (value.isZero()) {
1317 inputs.drop_back());
1323 if (matchPattern(inputs[size - 2], m_ConstantInt(&value2))) {
1325 SmallVector<Value, 4> newOperands(inputs.drop_back(2));
1326 newOperands.push_back(cst);
1335 for (
size_t i = 0; i < size - 1; ++i) {
1336 if (
auto concat = inputs[i].getDefiningOp<ConcatOp>())
1351 source, cmpAgainst);
1357 if (
auto firstMux = op.getOperand(0).getDefiningOp<
comb::MuxOp>()) {
1359 if (op.getTwoState() && firstMux.getTwoState() &&
1360 matchPattern(firstMux.getFalseValue(), m_ConstantInt(&value)) &&
1362 SmallVector<Value> conditions{firstMux.getCond()};
1363 auto check = [&](Value v) {
1367 conditions.push_back(mux.getCond());
1368 return mux.getTwoState() &&
1369 firstMux.getTrueValue() == mux.getTrueValue() &&
1370 firstMux.getFalseValue() == mux.getFalseValue();
1372 if (llvm::all_of(op.getOperands().drop_front(), check)) {
1373 auto cond = comb::OrOp::create(rewriter, op.getLoc(), conditions,
true);
1375 rewriter, op, cond, firstMux.getTrueValue(),
1376 firstMux.getFalseValue(),
true);
1386OpFoldResult XorOp::fold(FoldAdaptor adaptor) {
1390 auto size = getInputs().size();
1391 auto inputs = adaptor.getInputs();
1395 return getInputs()[0];
1398 if (size == 2 && getInputs()[0] == getInputs()[1])
1399 return IntegerAttr::get(getType(), 0);
1402 if (inputs.size() == 2)
1403 if (
auto intAttr = dyn_cast_or_null<IntegerAttr>(inputs[1]))
1404 if (intAttr.getValue().isZero())
1405 return getInputs()[0];
1411 subExpr != getResult())
1420 PatternRewriter &rewriter) {
1421 auto icmp = op.getOperand(icmpOperand).getDefiningOp<ICmpOp>();
1422 auto negatedPred = ICmpOp::getNegatedPredicate(icmp.getPredicate());
1425 ICmpOp::create(rewriter, icmp.getLoc(), negatedPred, icmp.getOperand(0),
1426 icmp.getOperand(1), icmp.getTwoState());
1429 if (op.getNumOperands() > 2) {
1430 SmallVector<Value, 4> newOperands(op.getOperands());
1431 newOperands.pop_back();
1432 newOperands.erase(newOperands.begin() + icmpOperand);
1433 newOperands.push_back(result);
1435 XorOp::create(rewriter, op.getLoc(), newOperands, op.getTwoState());
1441LogicalResult XorOp::canonicalize(
XorOp op, PatternRewriter &rewriter) {
1445 auto inputs = op.getInputs();
1446 auto size = inputs.size();
1447 assert(size > 1 &&
"expected 2 or more operands");
1450 if (inputs[size - 1] == inputs[size - 2]) {
1452 "expected idempotent case for 2 elements handled already.");
1454 inputs.drop_back(2),
false);
1460 if (matchPattern(inputs.back(), m_ConstantInt(&value))) {
1462 if (value.isZero()) {
1464 inputs.drop_back(),
false);
1470 if (matchPattern(inputs[size - 2], m_ConstantInt(&value2))) {
1472 SmallVector<Value, 4> newOperands(inputs.drop_back(2));
1473 newOperands.push_back(cst);
1475 newOperands,
false);
1479 bool isSingleBit = value.getBitWidth() == 1;
1482 for (
size_t i = 0; i < size - 1; ++i) {
1483 Value operand = inputs[i];
1489 if (
auto concat = operand.getDefiningOp<ConcatOp>())
1494 if (isSingleBit && operand.hasOneUse()) {
1495 assert(value == 1 &&
"single bit constant has to be one if not zero");
1496 if (
auto icmp = operand.getDefiningOp<ICmpOp>())
1504 Value complementVal;
1507 if (matchPattern(op.getResult(),
m_Complement(m_Any(&complementVal))) &&
1508 matchPattern(complementVal, m_SextBy(m_Any(&signExtBits)))) {
1510 auto baseWidth = op.getType().getIntOrFloatBitWidth() -
1511 signExtBits.getType().getIntOrFloatBitWidth();
1540OpFoldResult SubOp::fold(FoldAdaptor adaptor) {
1545 if (getRhs() == getLhs())
1547 APInt::getZero(getLhs().getType().getIntOrFloatBitWidth()),
1550 if (adaptor.getRhs()) {
1552 if (adaptor.getLhs()) {
1555 APInt::getAllOnes(getLhs().getType().getIntOrFloatBitWidth()),
1557 auto rhsNeg = hw::ParamExprAttr::get(
1558 hw::PEO::Mul, cast<TypedAttr>(adaptor.getRhs()), negOne);
1559 return hw::ParamExprAttr::get(hw::PEO::Add,
1560 cast<TypedAttr>(adaptor.getLhs()), rhsNeg);
1564 if (
auto rhsC = dyn_cast<IntegerAttr>(adaptor.getRhs())) {
1565 if (rhsC.getValue().isZero())
1581 return lhsMin.uge(rhsMax);
1584LogicalResult SubOp::canonicalize(
SubOp op, PatternRewriter &rewriter) {
1590 if (matchPattern(op.getRhs(), m_ConstantInt(&value))) {
1600 unsigned width = op.getType().getIntOrFloatBitWidth();
1601 auto [lowestBit, highestBit] =
1603 if (lowestBit > 0 &&
1612OpFoldResult AddOp::fold(FoldAdaptor adaptor) {
1616 auto size = getInputs().size();
1620 return getInputs()[0];
1630 APInt maxSum = APInt::getZero(numBits);
1631 for (Value operand : operands) {
1635 maxSum = maxSum.uadd_ov(operandMax, overflow);
1642LogicalResult AddOp::canonicalize(
AddOp op, PatternRewriter &rewriter) {
1646 auto inputs = op.getInputs();
1647 auto size = inputs.size();
1648 assert(size > 1 &&
"expected 2 or more operands");
1650 APInt value, value2;
1653 if (matchPattern(inputs.back(), m_ConstantInt(&value)) && value.isZero()) {
1655 inputs.drop_back(),
false);
1660 if (matchPattern(inputs[size - 1], m_ConstantInt(&value)) &&
1661 matchPattern(inputs[size - 2], m_ConstantInt(&value2))) {
1663 SmallVector<Value, 4> newOperands(inputs.drop_back(2));
1664 newOperands.push_back(cst);
1666 newOperands,
false);
1671 if (inputs[size - 1] == inputs[size - 2]) {
1672 SmallVector<Value, 4> newOperands(inputs.drop_back(2));
1676 comb::ShlOp::create(rewriter, op.getLoc(), inputs.back(), one,
false);
1678 newOperands.push_back(shiftLeftOp);
1680 newOperands,
false);
1684 auto shlOp = inputs[size - 1].getDefiningOp<
comb::ShlOp>();
1686 if (shlOp && shlOp.getLhs() == inputs[size - 2] &&
1687 matchPattern(shlOp.getRhs(), m_ConstantInt(&value))) {
1689 APInt one(value.getBitWidth(), 1,
false);
1693 std::array<Value, 2> factors = {shlOp.getLhs(), rhs};
1694 auto mulOp = comb::MulOp::create(rewriter, op.getLoc(), factors,
false);
1696 SmallVector<Value, 4> newOperands(inputs.drop_back(2));
1697 newOperands.push_back(mulOp);
1699 newOperands,
false);
1703 auto mulOp = inputs[size - 1].getDefiningOp<
comb::MulOp>();
1705 if (mulOp && mulOp.getInputs().size() == 2 &&
1706 mulOp.getInputs()[0] == inputs[size - 2] &&
1707 matchPattern(mulOp.getInputs()[1], m_ConstantInt(&value))) {
1709 APInt one(value.getBitWidth(), 1,
false);
1711 std::array<Value, 2> factors = {mulOp.getInputs()[0], rhs};
1712 auto newMulOp = comb::MulOp::create(rewriter, op.getLoc(), factors,
false);
1714 SmallVector<Value, 4> newOperands(inputs.drop_back(2));
1715 newOperands.push_back(newMulOp);
1717 newOperands,
false);
1728 unsigned width = op.getType().getIntOrFloatBitWidth();
1729 auto [lowestBit, highestBit] =
1737 auto addOp = inputs[0].getDefiningOp<
comb::AddOp>();
1738 if (addOp && addOp.getInputs().size() == 2 &&
1739 matchPattern(addOp.getInputs()[1], m_ConstantInt(&value2)) &&
1740 inputs.size() == 2 && matchPattern(inputs[1], m_ConstantInt(&value))) {
1744 rewriter, op, op.getType(), ArrayRef<Value>{addOp.getInputs()[0], rhs},
1745 op.getTwoState() && addOp.getTwoState());
1752OpFoldResult MulOp::fold(FoldAdaptor adaptor) {
1756 auto size = getInputs().size();
1757 auto inputs = adaptor.getInputs();
1761 return getInputs()[0];
1763 auto width = cast<IntegerType>(getType()).getWidth();
1765 return getIntAttr(APInt::getZero(0), getContext());
1767 APInt value(width, 1,
false);
1770 for (
auto operand : inputs) {
1771 auto attr = dyn_cast_or_null<IntegerAttr>(operand);
1774 value *= attr.getValue();
1783LogicalResult MulOp::canonicalize(
MulOp op, PatternRewriter &rewriter) {
1787 auto inputs = op.getInputs();
1788 auto size = inputs.size();
1789 assert(size > 1 &&
"expected 2 or more operands");
1791 APInt value, value2;
1794 if (size == 2 && matchPattern(inputs.back(), m_ConstantInt(&value)) &&
1795 value.isPowerOf2()) {
1797 value.exactLogBase2());
1799 comb::ShlOp::create(rewriter, op.getLoc(), inputs[0], shift,
false);
1802 ArrayRef<Value>(shlOp),
false);
1807 if (matchPattern(inputs.back(), m_ConstantInt(&value)) && value.isOne()) {
1809 inputs.drop_back());
1814 if (matchPattern(inputs[size - 1], m_ConstantInt(&value)) &&
1815 matchPattern(inputs[size - 2], m_ConstantInt(&value2))) {
1817 SmallVector<Value, 4> newOperands(inputs.drop_back(2));
1818 newOperands.push_back(cst);
1835template <
class Op,
bool isSigned>
1836static OpFoldResult
foldDiv(Op op, ArrayRef<Attribute> constants) {
1837 if (
auto rhsValue = dyn_cast_or_null<IntegerAttr>(constants[1])) {
1839 if (rhsValue.getValue() == 1)
1843 if (rhsValue.getValue().isZero())
1850OpFoldResult DivUOp::fold(FoldAdaptor adaptor) {
1853 return foldDiv<DivUOp,
false>(*
this, adaptor.getOperands());
1856OpFoldResult DivSOp::fold(FoldAdaptor adaptor) {
1859 return foldDiv<DivSOp,
true>(*
this, adaptor.getOperands());
1862template <
class Op,
bool isSigned>
1863static OpFoldResult
foldMod(Op op, ArrayRef<Attribute> constants) {
1864 if (
auto rhsValue = dyn_cast_or_null<IntegerAttr>(constants[1])) {
1866 if (rhsValue.getValue() == 1)
1867 return getIntAttr(APInt::getZero(op.getType().getIntOrFloatBitWidth()),
1871 if (rhsValue.getValue().isZero())
1875 if (
auto lhsValue = dyn_cast_or_null<IntegerAttr>(constants[0])) {
1877 if (lhsValue.getValue().isZero())
1878 return getIntAttr(APInt::getZero(op.getType().getIntOrFloatBitWidth()),
1885OpFoldResult ModUOp::fold(FoldAdaptor adaptor) {
1888 return foldMod<ModUOp,
false>(*
this, adaptor.getOperands());
1891OpFoldResult ModSOp::fold(FoldAdaptor adaptor) {
1894 return foldMod<ModSOp,
true>(*
this, adaptor.getOperands());
1897LogicalResult DivUOp::canonicalize(
DivUOp op, PatternRewriter &rewriter) {
1903LogicalResult ModUOp::canonicalize(
ModUOp op, PatternRewriter &rewriter) {
1915OpFoldResult ConcatOp::fold(FoldAdaptor adaptor) {
1919 if (getNumOperands() == 1)
1920 return getOperand(0);
1923 for (
auto attr : adaptor.getInputs())
1924 if (!attr || !isa<IntegerAttr>(attr))
1928 unsigned resultWidth = getType().getIntOrFloatBitWidth();
1929 APInt result(resultWidth, 0);
1931 unsigned nextInsertion = resultWidth;
1933 for (
auto attr : adaptor.getInputs()) {
1934 auto chunk = cast<IntegerAttr>(attr).getValue();
1935 nextInsertion -= chunk.getBitWidth();
1936 result.insertBits(chunk, nextInsertion);
1942LogicalResult ConcatOp::canonicalize(
ConcatOp op, PatternRewriter &rewriter) {
1946 auto inputs = op.getInputs();
1947 auto size = inputs.size();
1948 assert(size > 1 &&
"expected 2 or more operands");
1951 SmallVector<Value, 4> pendingOperands, processedOperands;
1952 bool anyOperandChanged;
1954 auto pushPendingOperands = [&](ValueRange operands) {
1955 auto size = operands.size();
1956 for (
size_t i = 0; i != size; ++i)
1957 pendingOperands.push_back(operands[size - 1 - i]);
1958 anyOperandChanged =
true;
1960 auto replacePrevOperand = [&](Value replacement) {
1961 processedOperands.back() = replacement;
1962 anyOperandChanged =
true;
1964 pendingOperands.reserve(size);
1965 pushPendingOperands(inputs);
1966 anyOperandChanged =
false;
1968 while (!pendingOperands.empty()) {
1969 Value nextOperand = pendingOperands.pop_back_val();
1973 if (
auto subConcat = nextOperand.getDefiningOp<ConcatOp>()) {
1974 pushPendingOperands(subConcat->getOperands());
1979 if (!processedOperands.empty()) {
1980 Value prevOperand = processedOperands.back();
1983 if (
auto cst = nextOperand.getDefiningOp<hw::ConstantOp>()) {
1984 if (
auto prevCst = prevOperand.getDefiningOp<hw::ConstantOp>()) {
1985 unsigned prevWidth = prevCst.getValue().getBitWidth();
1986 unsigned thisWidth = cst.getValue().getBitWidth();
1987 auto resultCst = cst.getValue().zext(prevWidth + thisWidth);
1988 resultCst |= prevCst.getValue().zext(prevWidth + thisWidth)
1992 replacePrevOperand(replacement);
1998 if (nextOperand == prevOperand) {
2000 rewriter.createOrFold<ReplicateOp>(op.getLoc(), prevOperand, 2);
2001 replacePrevOperand(replacement);
2007 if (
auto repl = nextOperand.getDefiningOp<ReplicateOp>()) {
2009 if (repl.getOperand() == prevOperand) {
2010 Value replacement = rewriter.createOrFold<ReplicateOp>(
2011 op.getLoc(), repl.getOperand(), repl.getMultiple() + 1);
2012 replacePrevOperand(replacement);
2016 if (
auto prevRepl = prevOperand.getDefiningOp<ReplicateOp>()) {
2017 if (prevRepl.getOperand() == repl.getOperand()) {
2018 Value replacement = rewriter.createOrFold<ReplicateOp>(
2019 op.getLoc(), repl.getOperand(),
2020 repl.getMultiple() + prevRepl.getMultiple());
2021 replacePrevOperand(replacement);
2028 if (
auto repl = prevOperand.getDefiningOp<ReplicateOp>()) {
2029 if (repl.getOperand() == nextOperand) {
2030 Value replacement = rewriter.createOrFold<ReplicateOp>(
2031 op.getLoc(), nextOperand, repl.getMultiple() + 1);
2032 replacePrevOperand(replacement);
2039 if (
auto extract = nextOperand.getDefiningOp<ExtractOp>()) {
2040 if (
auto prevExtract = prevOperand.getDefiningOp<ExtractOp>()) {
2041 if (extract.getInput() == prevExtract.getInput()) {
2042 auto thisWidth = cast<IntegerType>(extract.getType()).getWidth();
2043 if (prevExtract.getLowBit() == extract.getLowBit() + thisWidth) {
2044 auto prevWidth = prevExtract.getType().getIntOrFloatBitWidth();
2045 auto resType = rewriter.getIntegerType(thisWidth + prevWidth);
2048 extract.getInput(), extract.getLowBit());
2049 replacePrevOperand(replacement);
2063 static std::optional<ArraySlice>
get(Value value) {
2064 assert(isa<IntegerType>(value.getType()) &&
"expected integer type");
2065 if (
auto arrayGet = value.getDefiningOp<hw::ArrayGetOp>())
2066 return ArraySlice{arrayGet.getInput(), arrayGet.getIndex(), 1};
2068 if (
auto bitcast = value.getDefiningOp<hw::BitcastOp>())
2069 if (
auto arraySlice =
2070 bitcast.getInput().getDefiningOp<hw::ArraySliceOp>())
2072 arraySlice.getInput(), arraySlice.getLowIndex(),
2073 hw::type_cast<hw::ArrayType>(arraySlice.getType())
2075 return std::nullopt;
2078 if (
auto extractOpt = ArraySlice::get(nextOperand)) {
2079 if (
auto prevExtractOpt = ArraySlice::get(prevOperand)) {
2081 if (prevExtractOpt->index.getType() == extractOpt->index.getType() &&
2082 prevExtractOpt->input == extractOpt->input &&
2083 hw::isOffset(extractOpt->index, prevExtractOpt->index,
2084 extractOpt->width)) {
2085 auto resType = hw::ArrayType::get(
2086 hw::type_cast<hw::ArrayType>(prevExtractOpt->input.getType())
2088 extractOpt->width + prevExtractOpt->width);
2089 auto resIntType = rewriter.getIntegerType(hw::getBitWidth(resType));
2091 rewriter, op.getLoc(), resIntType,
2093 prevExtractOpt->input,
2094 extractOpt->index));
2095 replacePrevOperand(replacement);
2102 processedOperands.push_back(nextOperand);
2114 constexpr size_t kBatchExtractThreshold = 16;
2115 bool anyExtractsResolved =
false;
2116 if (!anyOperandChanged && processedOperands.size() > 1) {
2117 SmallVector<ExtractOp, 8> extractUsers;
2118 for (
auto *user : op->getUsers()) {
2119 if (
auto extract = dyn_cast<ExtractOp>(user))
2120 extractUsers.push_back(extract);
2123 if (extractUsers.size() >= kBatchExtractThreshold) {
2125 auto concatInputs = op.getInputs();
2126 size_t numConcatOperands = concatInputs.size();
2127 SmallVector<size_t> prefixWidths(numConcatOperands);
2128 size_t cumWidth = 0;
2129 for (
size_t i = 0; i < numConcatOperands; ++i) {
2132 cumWidth += concatInputs[numConcatOperands - 1 - i]
2134 .getIntOrFloatBitWidth();
2135 prefixWidths[i] = cumWidth;
2139 for (
auto extract : extractUsers) {
2142 anyExtractsResolved =
true;
2147 if (processedOperands.size() == 1) {
2151 }
else if (anyOperandChanged) {
2154 }
else if (!anyExtractsResolved) {
2164OpFoldResult MuxOp::fold(FoldAdaptor adaptor) {
2169 if (getTrueValue() == getFalseValue() && getTrueValue() != getResult())
2170 return getTrueValue();
2171 if (
auto tv = adaptor.getTrueValue())
2172 if (tv == adaptor.getFalseValue())
2177 if (
auto pred = dyn_cast_or_null<IntegerAttr>(adaptor.getCond())) {
2178 if (pred.getValue().isZero() && getFalseValue() != getResult())
2179 return getFalseValue();
2180 if (pred.getValue().isOne() && getTrueValue() != getResult())
2181 return getTrueValue();
2185 if (getCond().getType() == getTrueValue().getType())
2186 if (
auto tv = dyn_cast_or_null<IntegerAttr>(adaptor.getTrueValue()))
2187 if (
auto fv = dyn_cast_or_null<IntegerAttr>(adaptor.getFalseValue()))
2188 if (tv.getValue().isOne() && fv.getValue().isZero() &&
2189 hw::getBitWidth(getType()) == 1 && getCond() != getResult())
2205 if (
auto cmp = cond.getDefiningOp<ICmpOp>()) {
2207 auto requiredPredicate =
2208 (isInverted ? ICmpPredicate::eq : ICmpPredicate::ne);
2209 if (cmp.getLhs() == indexValue && cmp.getPredicate() == requiredPredicate) {
2219 if (
auto orOp = cond.getDefiningOp<
OrOp>()) {
2222 for (
auto operand : orOp.getOperands())
2229 if (
auto andOp = cond.getDefiningOp<
AndOp>()) {
2232 for (
auto operand : andOp.getOperands())
2250bool comb::foldMuxChainWithComparison(
2251 PatternRewriter &rewriter,
MuxOp rootMux,
bool isFalseSide,
2257 auto rootCmp = rootMux.getCond().getDefiningOp<ICmpOp>();
2260 Value indexValue = rootCmp.getLhs();
2263 auto getCaseValue = [&](
MuxOp mux) -> Value {
2264 return mux.getOperand(1 +
unsigned(!isFalseSide));
2269 auto getTreeValue = [&](
MuxOp mux) -> Value {
2270 return mux.getOperand(1 +
unsigned(isFalseSide));
2275 SmallVector<Location> locationsFound;
2276 SmallVector<std::pair<hw::ConstantOp, Value>, 4> valuesFound;
2280 auto collectConstantValues = [&](
MuxOp mux) ->
bool {
2282 mux.getCond(), indexValue, isFalseSide, [&](hw::ConstantOp cst) {
2283 valuesFound.push_back({cst, getCaseValue(mux)});
2284 locationsFound.push_back(mux.getCond().getLoc());
2285 locationsFound.push_back(mux->getLoc());
2290 if (!collectConstantValues(rootMux))
2294 if (rootMux->hasOneUse()) {
2295 if (
auto userMux = dyn_cast<MuxOp>(*rootMux->user_begin())) {
2296 if (getTreeValue(userMux) == rootMux.getResult() &&
2298 [&](hw::ConstantOp cst) {}))
2304 auto nextTreeValue = getTreeValue(rootMux);
2306 auto nextMux = nextTreeValue.getDefiningOp<
MuxOp>();
2307 if (!nextMux || !nextMux->hasOneUse())
2309 if (!collectConstantValues(nextMux))
2311 nextTreeValue = getTreeValue(nextMux);
2314 auto indexWidth = cast<IntegerType>(indexValue.getType()).getWidth();
2316 if (indexWidth > 20)
2319 auto foldingStyle = styleFn(indexWidth, valuesFound.size());
2323 uint64_t tableSize = 1ULL << indexWidth;
2327 SmallVector<Value, 8> table(tableSize, nextTreeValue);
2332 for (
auto &elt : llvm::reverse(valuesFound)) {
2333 uint64_t idx = elt.first.getValue().getZExtValue();
2334 assert(idx < table.size() &&
"constant should be same bitwidth as index");
2335 table[idx] = elt.second;
2339 SmallVector<Value> bits;
2340 comb::extractBits(rewriter, indexValue, bits);
2348 "unknown folding style");
2352 std::reverse(table.begin(), table.end());
2355 auto fusedLoc = rewriter.getFusedLoc(locationsFound);
2372 PatternRewriter &rewriter) {
2373 assert(fullyAssoc->getNumOperands() >= 2 &&
"cannot split up unary ops");
2374 assert(operandNo < fullyAssoc->getNumOperands() &&
"Invalid operand #");
2378 if (fullyAssoc->getNumOperands() == 2)
2379 return fullyAssoc->getOperand(operandNo ^ 1);
2382 if (fullyAssoc->hasOneUse()) {
2383 rewriter.modifyOpInPlace(fullyAssoc,
2384 [&]() { fullyAssoc->eraseOperand(operandNo); });
2385 return fullyAssoc->getResult(0);
2391 OpBuilder::InsertionGuard guard(rewriter);
2392 rewriter.setInsertionPoint(fullyAssoc);
2393 SmallVector<Value> operands;
2394 operands.append(fullyAssoc->getOperands().begin(),
2395 fullyAssoc->getOperands().begin() + operandNo);
2396 operands.append(fullyAssoc->getOperands().begin() + operandNo + 1,
2397 fullyAssoc->getOperands().end());
2399 fullyAssoc->getLoc(), fullyAssoc->getName(), operands, rewriter);
2400 Value excluded = fullyAssoc->getOperand(operandNo);
2404 ArrayRef<Value>{opWithoutExcluded, excluded}, rewriter);
2406 return opWithoutExcluded;
2415static bool foldCommonMuxValue(
MuxOp op,
bool isTrueOperand,
2416 PatternRewriter &rewriter) {
2419 Operation *subExpr =
2420 (isTrueOperand ? op.getFalseValue() : op.getTrueValue()).getDefiningOp();
2421 if (!subExpr || subExpr->getNumOperands() < 2)
2425 if (!isa<AndOp, XorOp, OrOp, MuxOp>(subExpr))
2430 Value commonValue = isTrueOperand ? op.getTrueValue() : op.getFalseValue();
2431 size_t opNo = 0, e = subExpr->getNumOperands();
2432 while (opNo != e && subExpr->getOperand(opNo) != commonValue)
2438 Value cond = op.getCond();
2444 if (
auto subMux = dyn_cast<MuxOp>(subExpr)) {
2449 Value subCond = subMux.getCond();
2452 if (subMux.getTrueValue() == commonValue)
2453 otherValue = subMux.getFalseValue();
2454 else if (subMux.getFalseValue() == commonValue) {
2455 otherValue = subMux.getTrueValue();
2465 cond = rewriter.createOrFold<
OrOp>(op.getLoc(), cond, subCond,
false);
2467 otherValue, op.getTwoState());
2473 bool isaAndOp = isa<AndOp>(subExpr);
2474 if (isTrueOperand ^ isaAndOp)
2478 rewriter.createOrFold<ReplicateOp>(op.getLoc(), op.getType(), cond);
2481 bool isaXorOp = isa<XorOp>(subExpr);
2482 bool isaOrOp = isa<OrOp>(subExpr);
2491 if (isaOrOp || isaXorOp) {
2492 auto masked = rewriter.createOrFold<
AndOp>(op.getLoc(), extendedCond,
2493 restOfAssoc,
false);
2496 commonValue,
false);
2504 assert(isaAndOp &&
"unexpected operation here");
2505 auto masked = rewriter.createOrFold<
OrOp>(op.getLoc(), extendedCond,
2506 restOfAssoc,
false);
2518 PatternRewriter &rewriter) {
2521 if (!isa<ConcatOp>(trueOp))
2525 SmallVector<Value> trueOperands, falseOperands;
2529 size_t numTrueOperands = trueOperands.size();
2530 size_t numFalseOperands = falseOperands.size();
2532 if (!numTrueOperands || !numFalseOperands ||
2533 (trueOperands.front() != falseOperands.front() &&
2534 trueOperands.back() != falseOperands.back()))
2538 if (trueOperands.front() == falseOperands.front()) {
2539 SmallVector<Value> operands;
2541 for (i = 0; i < numTrueOperands; ++i) {
2542 Value trueOperand = trueOperands[i];
2543 if (trueOperand == falseOperands[i])
2544 operands.push_back(trueOperand);
2548 if (i == numTrueOperands) {
2555 if (llvm::all_of(operands, [&](Value v) {
return v == operands.front(); }))
2556 sharedMSB = rewriter.createOrFold<ReplicateOp>(
2557 mux->getLoc(), operands.front(), operands.size());
2559 sharedMSB = rewriter.createOrFold<
ConcatOp>(mux->getLoc(), operands);
2563 operands.append(trueOperands.begin() + i, trueOperands.end());
2564 Value trueLSB = rewriter.createOrFold<
ConcatOp>(trueOp->getLoc(), operands);
2566 operands.append(falseOperands.begin() + i, falseOperands.end());
2568 rewriter.createOrFold<
ConcatOp>(falseOp->getLoc(), operands);
2571 Value lsb = rewriter.createOrFold<
MuxOp>(
2572 mux->getLoc(), mux.getCond(), trueLSB, falseLSB, mux.getTwoState());
2578 if (trueOperands.back() == falseOperands.back()) {
2579 SmallVector<Value> operands;
2582 Value trueOperand = trueOperands[numTrueOperands - i - 1];
2583 if (trueOperand == falseOperands[numFalseOperands - i - 1])
2584 operands.push_back(trueOperand);
2588 std::reverse(operands.begin(), operands.end());
2589 Value sharedLSB = rewriter.createOrFold<
ConcatOp>(mux->getLoc(), operands);
2593 operands.append(trueOperands.begin(), trueOperands.end() - i);
2594 Value trueMSB = rewriter.createOrFold<
ConcatOp>(trueOp->getLoc(), operands);
2596 operands.append(falseOperands.begin(), falseOperands.end() - i);
2598 rewriter.createOrFold<
ConcatOp>(falseOp->getLoc(), operands);
2600 Value msb = rewriter.createOrFold<
MuxOp>(
2601 mux->getLoc(), mux.getCond(), trueMSB, falseMSB, mux.getTwoState());
2614 if (!trueVec || !falseVec)
2616 if (!trueVec.isUniform() || !falseVec.isUniform())
2619 auto mux = MuxOp::create(rewriter, op.getLoc(), op.getCond(),
2620 trueVec.getUniformElement(),
2621 falseVec.getUniformElement(), op.getTwoState());
2623 SmallVector<Value> values(trueVec.getInputs().size(), mux);
2631 bool constCond, PatternRewriter &rewriter) {
2632 if (!muxValue.hasOneUse())
2634 auto *op = muxValue.getDefiningOp();
2635 if (!op || !isa_and_nonnull<CombDialect>(op->getDialect()))
2637 if (!llvm::is_contained(op->getOperands(), muxCond))
2639 OpBuilder::InsertionGuard guard(rewriter);
2640 rewriter.setInsertionPoint(op);
2643 rewriter.modifyOpInPlace(op, [&] {
2644 for (
auto &use : op->getOpOperands())
2645 if (use.get() == muxCond)
2652struct MuxRewriter :
public mlir::OpRewritePattern<MuxOp> {
2653 using OpRewritePattern::OpRewritePattern;
2655 LogicalResult matchAndRewrite(MuxOp op,
2656 PatternRewriter &rewriter)
const override;
2660foldToArrayCreateOnlyWhenDense(
size_t indexWidth,
size_t numEntries) {
2663 if (indexWidth >= 9 || numEntries < 3)
2669 uint64_t tableSize = 1ULL << indexWidth;
2670 if (numEntries >= tableSize * 5 / 8)
2675LogicalResult MuxRewriter::matchAndRewrite(
MuxOp op,
2676 PatternRewriter &rewriter)
const {
2680 bool isSignlessInt =
false;
2681 if (
auto intType = dyn_cast<IntegerType>(op.getType()))
2682 isSignlessInt = intType.isSignless();
2689 if (matchPattern(op.getTrueValue(), m_ConstantInt(&value)) && isSignlessInt) {
2690 if (value.getBitWidth() == 1) {
2692 if (value.isZero()) {
2695 op.getFalseValue(),
false);
2701 op.getFalseValue(),
false);
2707 if (matchPattern(op.getFalseValue(), m_ConstantInt(&value2))) {
2712 APInt xorValue = value ^ value2;
2713 if (xorValue.isPowerOf2()) {
2714 unsigned leadingZeros = xorValue.countLeadingZeros();
2715 unsigned trailingZeros = value.getBitWidth() - leadingZeros - 1;
2716 SmallVector<Value, 3> operands;
2724 if (leadingZeros > 0)
2725 operands.push_back(rewriter.createOrFold<
ExtractOp>(
2726 op.getLoc(), op.getTrueValue(), trailingZeros + 1, leadingZeros));
2730 auto v1 = rewriter.createOrFold<
ExtractOp>(
2731 op.getLoc(), op.getTrueValue(), trailingZeros, 1);
2732 auto v2 = rewriter.createOrFold<
ExtractOp>(
2733 op.getLoc(), op.getFalseValue(), trailingZeros, 1);
2734 operands.push_back(rewriter.createOrFold<
MuxOp>(
2735 op.getLoc(), op.getCond(), v1, v2,
false));
2737 if (trailingZeros > 0)
2738 operands.push_back(rewriter.createOrFold<
ExtractOp>(
2739 op.getLoc(), op.getTrueValue(), 0, trailingZeros));
2748 if (value.isAllOnes() && value2.isZero()) {
2750 rewriter, op, op.getType(), op.getCond());
2756 if (matchPattern(op.getFalseValue(), m_ConstantInt(&value)) &&
2757 isSignlessInt && value.getBitWidth() == 1) {
2759 if (value.isZero()) {
2761 op.getTrueValue(),
false);
2768 auto notCond = rewriter.createOrFold<
XorOp>(op.getLoc(), op.getCond(),
2769 op.getFalseValue(),
false);
2771 op.getTrueValue(),
false);
2777 Operation *condOp = op.getCond().getDefiningOp();
2778 if (condOp && matchPattern(condOp,
m_Complement(m_Any(&subExpr))) &&
2781 subExpr, op.getFalseValue(),
2782 op.getTrueValue(),
true);
2789 if (condOp && condOp->hasOneUse()) {
2790 SmallVector<Value> invertedOperands;
2794 auto getInvertedOperands = [&]() ->
bool {
2795 for (Value operand : condOp->getOperands()) {
2796 if (matchPattern(operand,
m_Complement(m_Any(&subExpr))))
2797 invertedOperands.push_back(subExpr);
2804 if (isa<AndOp>(condOp) && getInvertedOperands()) {
2806 rewriter.createOrFold<
OrOp>(op.getLoc(), invertedOperands,
false);
2808 rewriter, op, newOr, op.getFalseValue(), op.getTrueValue(),
2812 if (isa<OrOp>(condOp) && getInvertedOperands()) {
2814 rewriter.createOrFold<
AndOp>(op.getLoc(), invertedOperands,
false);
2816 rewriter, op, newAnd, op.getFalseValue(), op.getTrueValue(),
2822 if (
auto falseMux = op.getFalseValue().getDefiningOp<
MuxOp>();
2823 falseMux && falseMux != op) {
2825 if (op.getCond() == falseMux.getCond() &&
2826 falseMux.getFalseValue() != falseMux) {
2828 rewriter, op, op.getCond(), op.getTrueValue(),
2829 falseMux.getFalseValue(), op.getTwoStateAttr());
2835 foldToArrayCreateOnlyWhenDense))
2839 if (
auto trueMux = op.getTrueValue().getDefiningOp<
MuxOp>();
2840 trueMux && trueMux != op) {
2842 if (op.getCond() == trueMux.getCond()) {
2844 rewriter, op, op.getCond(), trueMux.getTrueValue(),
2845 op.getFalseValue(), op.getTwoStateAttr());
2851 foldToArrayCreateOnlyWhenDense))
2856 if (
auto trueMux = dyn_cast_or_null<MuxOp>(op.getTrueValue().getDefiningOp()),
2857 falseMux = dyn_cast_or_null<MuxOp>(op.getFalseValue().getDefiningOp());
2858 trueMux && falseMux && trueMux.getCond() == falseMux.getCond() &&
2859 trueMux.getTrueValue() == falseMux.getTrueValue() && trueMux != op &&
2861 auto subMux = MuxOp::create(
2862 rewriter, rewriter.getFusedLoc({trueMux.getLoc(), falseMux.getLoc()}),
2863 op.getCond(), trueMux.getFalseValue(), falseMux.getFalseValue());
2865 trueMux.getTrueValue(), subMux,
2866 op.getTwoStateAttr());
2871 if (
auto trueMux = dyn_cast_or_null<MuxOp>(op.getTrueValue().getDefiningOp()),
2872 falseMux = dyn_cast_or_null<MuxOp>(op.getFalseValue().getDefiningOp());
2873 trueMux && falseMux && trueMux.getCond() == falseMux.getCond() &&
2874 trueMux.getFalseValue() == falseMux.getFalseValue() && trueMux != op &&
2876 auto subMux = MuxOp::create(
2877 rewriter, rewriter.getFusedLoc({trueMux.getLoc(), falseMux.getLoc()}),
2878 op.getCond(), trueMux.getTrueValue(), falseMux.getTrueValue());
2880 subMux, trueMux.getFalseValue(),
2881 op.getTwoStateAttr());
2886 if (
auto trueMux = dyn_cast_or_null<MuxOp>(op.getTrueValue().getDefiningOp()),
2887 falseMux = dyn_cast_or_null<MuxOp>(op.getFalseValue().getDefiningOp());
2888 trueMux && falseMux &&
2889 trueMux.getTrueValue() == falseMux.getTrueValue() &&
2890 trueMux.getFalseValue() == falseMux.getFalseValue() && trueMux != op &&
2893 MuxOp::create(rewriter,
2894 rewriter.getFusedLoc(
2895 {op.getLoc(), trueMux.getLoc(), falseMux.getLoc()}),
2896 op.getCond(), trueMux.getCond(), falseMux.getCond());
2898 rewriter, op, subMux, trueMux.getTrueValue(), trueMux.getFalseValue(),
2899 op.getTwoStateAttr());
2904 if (foldCommonMuxValue(op,
false, rewriter))
2907 if (foldCommonMuxValue(op,
true, rewriter))
2911 if (Operation *trueOp = op.getTrueValue().getDefiningOp())
2912 if (Operation *falseOp = op.getFalseValue().getDefiningOp())
2913 if (trueOp->getName() == falseOp->getName())
2926 if (op.getTrueValue().getDefiningOp() &&
2927 op.getTrueValue().getDefiningOp() != op)
2930 if (op.getFalseValue().getDefiningOp() &&
2931 op.getFalseValue().getDefiningOp() != op)
2940static bool foldArrayOfMuxes(hw::ArrayCreateOp op, PatternRewriter &rewriter) {
2942 if (op.getInputs().empty() || op.isUniform())
2944 auto inputs = op.getInputs();
2945 if (inputs.size() <= 1)
2950 auto first = inputs[0].getDefiningOp<
comb::MuxOp>();
2955 for (
size_t i = 1, n = inputs.size(); i < n; ++i) {
2956 auto input = inputs[i].getDefiningOp<
comb::MuxOp>();
2957 if (!input || first.getCond() != input.getCond())
2962 SmallVector<Value> trues{first.getTrueValue()};
2963 SmallVector<Value> falses{first.getFalseValue()};
2964 SmallVector<Location> locs{first->getLoc()};
2965 bool isTwoState =
true;
2966 for (
size_t i = 1, n = inputs.size(); i < n; ++i) {
2967 auto input = inputs[i].getDefiningOp<
comb::MuxOp>();
2968 trues.push_back(input.getTrueValue());
2969 falses.push_back(input.getFalseValue());
2970 locs.push_back(input->getLoc());
2971 if (!input.getTwoState())
2976 auto loc = FusedLoc::get(op.getContext(), locs);
2980 auto arrayTy = op.getType();
2983 rewriter.replaceOpWithNewOp<
comb::MuxOp>(op, arrayTy, first.getCond(),
2984 trueValues, falseValues, isTwoState);
2988struct ArrayRewriter :
public mlir::OpRewritePattern<hw::ArrayCreateOp> {
2989 using OpRewritePattern::OpRewritePattern;
2991 LogicalResult matchAndRewrite(hw::ArrayCreateOp op,
2992 PatternRewriter &rewriter)
const override {
2993 if (foldArrayOfMuxes(op, rewriter))
3001void MuxOp::getCanonicalizationPatterns(RewritePatternSet &results,
3003 results.insert<MuxRewriter, ArrayRewriter>(
context);
3014 switch (predicate) {
3015 case ICmpPredicate::eq:
3017 case ICmpPredicate::ne:
3019 case ICmpPredicate::slt:
3020 return lhs.slt(rhs);
3021 case ICmpPredicate::sle:
3022 return lhs.sle(rhs);
3023 case ICmpPredicate::sgt:
3024 return lhs.sgt(rhs);
3025 case ICmpPredicate::sge:
3026 return lhs.sge(rhs);
3027 case ICmpPredicate::ult:
3028 return lhs.ult(rhs);
3029 case ICmpPredicate::ule:
3030 return lhs.ule(rhs);
3031 case ICmpPredicate::ugt:
3032 return lhs.ugt(rhs);
3033 case ICmpPredicate::uge:
3034 return lhs.uge(rhs);
3035 case ICmpPredicate::ceq:
3037 case ICmpPredicate::cne:
3039 case ICmpPredicate::weq:
3041 case ICmpPredicate::wne:
3044 llvm_unreachable(
"unknown comparison predicate");
3050 switch (predicate) {
3051 case ICmpPredicate::eq:
3052 case ICmpPredicate::sle:
3053 case ICmpPredicate::sge:
3054 case ICmpPredicate::ule:
3055 case ICmpPredicate::uge:
3056 case ICmpPredicate::ceq:
3057 case ICmpPredicate::weq:
3059 case ICmpPredicate::ne:
3060 case ICmpPredicate::slt:
3061 case ICmpPredicate::sgt:
3062 case ICmpPredicate::ult:
3063 case ICmpPredicate::ugt:
3064 case ICmpPredicate::cne:
3065 case ICmpPredicate::wne:
3068 llvm_unreachable(
"unknown comparison predicate");
3071OpFoldResult ICmpOp::fold(FoldAdaptor adaptor) {
3074 if (getLhs() == getRhs()) {
3076 return IntegerAttr::get(getType(), val);
3080 if (
auto lhs = dyn_cast_or_null<IntegerAttr>(adaptor.getLhs())) {
3081 if (
auto rhs = dyn_cast_or_null<IntegerAttr>(adaptor.getRhs())) {
3084 return IntegerAttr::get(getType(), val);
3092template <
typename Range>
3094 size_t commonPrefixLength = 0;
3095 auto ia = a.begin();
3096 auto ib = b.begin();
3098 for (; ia != a.end() && ib != b.end(); ia++, ib++, commonPrefixLength++) {
3104 return commonPrefixLength;
3108 size_t totalWidth = 0;
3109 for (
auto operand : operands) {
3112 ssize_t width = operand.getType().getIntOrFloatBitWidth();
3114 totalWidth += width;
3124 PatternRewriter &rewriter) {
3128 SmallVector<Value> lhsOperands, rhsOperands;
3131 ArrayRef<Value> lhsOperandsRef = lhsOperands, rhsOperandsRef = rhsOperands;
3133 auto formCatOrReplicate = [&](Location loc,
3134 ArrayRef<Value> operands) -> Value {
3135 assert(!operands.empty());
3136 Value sameElement = operands[0];
3137 for (
size_t i = 1, e = operands.size(); i != e && sameElement; ++i)
3138 if (sameElement != operands[i])
3139 sameElement = Value();
3141 return rewriter.createOrFold<ReplicateOp>(loc, sameElement,
3143 return rewriter.createOrFold<
ConcatOp>(loc, operands);
3146 auto replaceWith = [&](ICmpPredicate predicate, Value lhs,
3147 Value rhs) -> LogicalResult {
3153 size_t commonPrefixLength =
3155 if (commonPrefixLength == lhsOperands.size()) {
3164 llvm::reverse(lhsOperandsRef), llvm::reverse(rhsOperandsRef));
3166 size_t commonPrefixTotalWidth =
3167 getTotalWidth(lhsOperandsRef.take_front(commonPrefixLength));
3168 size_t commonSuffixTotalWidth =
3169 getTotalWidth(lhsOperandsRef.take_back(commonSuffixLength));
3170 auto lhsOnly = lhsOperandsRef.drop_front(commonPrefixLength)
3171 .drop_back(commonSuffixLength);
3172 auto rhsOnly = rhsOperandsRef.drop_front(commonPrefixLength)
3173 .drop_back(commonSuffixLength);
3175 auto replaceWithoutReplicatingSignBit = [&]() {
3176 auto newLhs = formCatOrReplicate(lhs->getLoc(), lhsOnly);
3177 auto newRhs = formCatOrReplicate(rhs->getLoc(), rhsOnly);
3178 return replaceWith(op.getPredicate(), newLhs, newRhs);
3181 auto replaceWithReplicatingSignBit = [&]() {
3182 auto firstNonEmptyValue = lhsOperands[0];
3183 auto firstNonEmptyElemWidth =
3184 firstNonEmptyValue.getType().getIntOrFloatBitWidth();
3185 Value signBit = rewriter.createOrFold<
ExtractOp>(
3186 op.getLoc(), firstNonEmptyValue, firstNonEmptyElemWidth - 1, 1);
3188 auto newLhs = ConcatOp::create(rewriter, lhs->getLoc(), signBit, lhsOnly);
3189 auto newRhs = ConcatOp::create(rewriter, rhs->getLoc(), signBit, rhsOnly);
3190 return replaceWith(op.getPredicate(), newLhs, newRhs);
3193 if (ICmpOp::isPredicateSigned(op.getPredicate())) {
3195 if (commonPrefixTotalWidth == 0 && commonSuffixTotalWidth > 0)
3196 return replaceWithoutReplicatingSignBit();
3202 if (commonPrefixTotalWidth > 1 || commonSuffixTotalWidth > 0)
3203 return replaceWithReplicatingSignBit();
3205 }
else if (commonPrefixTotalWidth > 0 || commonSuffixTotalWidth > 0) {
3207 return replaceWithoutReplicatingSignBit();
3221 ICmpOp cmpOp,
const KnownBits &bitAnalysis,
const APInt &rhsCst,
3222 PatternRewriter &rewriter) {
3226 APInt bitsKnown = bitAnalysis.Zero | bitAnalysis.One;
3227 if ((bitsKnown & rhsCst) != bitAnalysis.One) {
3230 bool result = cmpOp.getPredicate() == ICmpPredicate::ne;
3239 SmallVector<Value> newConcatOperands;
3240 auto newConstant = APInt::getZeroWidth();
3245 unsigned knownMSB = bitsKnown.countLeadingOnes();
3247 Value operand = cmpOp.getLhs();
3252 while (knownMSB != bitsKnown.getBitWidth()) {
3255 bitsKnown = bitsKnown.trunc(bitsKnown.getBitWidth() - knownMSB);
3258 unsigned unknownBits = bitsKnown.countLeadingZeros();
3259 unsigned lowBit = bitsKnown.getBitWidth() - unknownBits;
3260 auto spanOperand = rewriter.createOrFold<
ExtractOp>(
3261 operand.getLoc(), operand, lowBit,
3263 auto spanConstant = rhsCst.lshr(lowBit).trunc(unknownBits);
3266 newConcatOperands.push_back(spanOperand);
3269 if (newConstant.getBitWidth() != 0)
3270 newConstant = newConstant.concat(spanConstant);
3272 newConstant = spanConstant;
3275 unsigned newWidth = bitsKnown.getBitWidth() - unknownBits;
3276 bitsKnown = bitsKnown.trunc(newWidth);
3277 knownMSB = bitsKnown.countLeadingOnes();
3283 if (newConcatOperands.empty()) {
3284 bool result = cmpOp.getPredicate() == ICmpPredicate::eq;
3291 Value concatResult =
3292 rewriter.createOrFold<
ConcatOp>(operand.getLoc(), newConcatOperands);
3296 rewriter, cmpOp.getOperand(1).getLoc(), newConstant);
3299 cmpOp.getPredicate(), concatResult,
3300 newConstantOp, cmpOp.getTwoState());
3306 PatternRewriter &rewriter) {
3307 auto ip = rewriter.saveInsertionPoint();
3308 rewriter.setInsertionPoint(xorOp);
3310 auto xorRHS = xorOp.getOperands().back().getDefiningOp<
hw::ConstantOp>();
3312 xorRHS.getValue() ^ rhs);
3314 switch (xorOp.getNumOperands()) {
3318 APInt::getZero(rhs.getBitWidth()));
3322 newLHS = xorOp.getOperand(0);
3326 SmallVector<Value> newOperands(xorOp.getOperands());
3327 newOperands.pop_back();
3328 newLHS = XorOp::create(rewriter, xorOp.getLoc(), newOperands,
false);
3332 bool xorMultipleUses = !xorOp->hasOneUse();
3336 if (xorMultipleUses)
3341 rewriter.restoreInsertionPoint(ip);
3343 rewriter, cmpOp, cmpOp.getPredicate(), newLHS, newRHS,
false);
3346LogicalResult ICmpOp::canonicalize(ICmpOp op, PatternRewriter &rewriter) {
3352 if (matchPattern(op.getLhs(), m_ConstantInt(&lhs))) {
3353 assert(!matchPattern(op.getRhs(), m_ConstantInt(&rhs)) &&
3354 "Should be folded");
3356 rewriter, op, ICmpOp::getFlippedPredicate(op.getPredicate()),
3357 op.getRhs(), op.getLhs(), op.getTwoState());
3362 if (matchPattern(op.getRhs(), m_ConstantInt(&rhs))) {
3367 auto replaceWith = [&](ICmpPredicate predicate, Value lhs,
3368 Value rhs) -> LogicalResult {
3370 rhs, op.getTwoState());
3374 auto replaceWithConstantI1 = [&](
bool constant) -> LogicalResult {
3376 APInt(1, constant));
3380 switch (op.getPredicate()) {
3381 case ICmpPredicate::slt:
3383 if (rhs.isMaxSignedValue())
3384 return replaceWith(ICmpPredicate::ne, op.getLhs(), op.getRhs());
3386 if (rhs.isMinSignedValue())
3387 return replaceWithConstantI1(0);
3389 if ((rhs - 1).isMinSignedValue())
3390 return replaceWith(ICmpPredicate::eq, op.getLhs(),
3393 case ICmpPredicate::sgt:
3395 if (rhs.isMinSignedValue())
3396 return replaceWith(ICmpPredicate::ne, op.getLhs(), op.getRhs());
3398 if (rhs.isMaxSignedValue())
3399 return replaceWithConstantI1(0);
3401 if ((rhs + 1).isMaxSignedValue())
3402 return replaceWith(ICmpPredicate::eq, op.getLhs(),
3405 case ICmpPredicate::ult:
3407 if (rhs.isAllOnes())
3408 return replaceWith(ICmpPredicate::ne, op.getLhs(), op.getRhs());
3411 return replaceWithConstantI1(0);
3413 if ((rhs - 1).isZero())
3414 return replaceWith(ICmpPredicate::eq, op.getLhs(),
3418 if (rhs.countLeadingOnes() + rhs.countTrailingZeros() ==
3419 rhs.getBitWidth()) {
3420 auto numOnes = rhs.countLeadingOnes();
3422 rhs.getBitWidth() - numOnes, numOnes);
3423 return replaceWith(ICmpPredicate::ne, smaller,
3428 case ICmpPredicate::ugt:
3431 return replaceWith(ICmpPredicate::ne, op.getLhs(), op.getRhs());
3433 if (rhs.isAllOnes())
3434 return replaceWithConstantI1(0);
3436 if ((rhs + 1).isAllOnes())
3437 return replaceWith(ICmpPredicate::eq, op.getLhs(),
3441 if ((rhs + 1).isPowerOf2()) {
3442 auto numOnes = rhs.countTrailingOnes();
3443 auto newWidth = rhs.getBitWidth() - numOnes;
3446 return replaceWith(ICmpPredicate::ne, smaller,
3451 case ICmpPredicate::sle:
3453 if (rhs.isMaxSignedValue())
3454 return replaceWithConstantI1(1);
3456 return replaceWith(ICmpPredicate::slt, op.getLhs(),
getConstant(rhs + 1));
3457 case ICmpPredicate::sge:
3459 if (rhs.isMinSignedValue())
3460 return replaceWithConstantI1(1);
3462 return replaceWith(ICmpPredicate::sgt, op.getLhs(),
getConstant(rhs - 1));
3463 case ICmpPredicate::ule:
3465 if (rhs.isAllOnes())
3466 return replaceWithConstantI1(1);
3468 return replaceWith(ICmpPredicate::ult, op.getLhs(),
getConstant(rhs + 1));
3469 case ICmpPredicate::uge:
3472 return replaceWithConstantI1(1);
3474 return replaceWith(ICmpPredicate::ugt, op.getLhs(),
getConstant(rhs - 1));
3475 case ICmpPredicate::eq:
3476 if (rhs.getBitWidth() == 1) {
3484 if (rhs.isAllOnes()) {
3491 case ICmpPredicate::ne:
3492 if (rhs.getBitWidth() == 1) {
3498 if (rhs.isAllOnes()) {
3507 case ICmpPredicate::ceq:
3508 case ICmpPredicate::cne:
3509 case ICmpPredicate::weq:
3510 case ICmpPredicate::wne:
3516 if (op.getPredicate() == ICmpPredicate::eq ||
3517 op.getPredicate() == ICmpPredicate::ne) {
3522 if (!knownBits.isUnknown())
3529 if (
auto xorOp = op.getLhs().getDefiningOp<
XorOp>())
3530 if (xorOp.getOperands().back().getDefiningOp<hw::ConstantOp>())
3536 if (
auto replicateOp = op.getLhs().getDefiningOp<ReplicateOp>())
3537 if (rhs.isAllOnes() || rhs.isZero()) {
3538 auto width = replicateOp.getInput().getType().getIntOrFloatBitWidth();
3541 rhs.isAllOnes() ? APInt::getAllOnes(width)
3542 : APInt::getZero(width));
3544 rewriter, op, op.getPredicate(), replicateOp.getInput(), cst,
3554 if (Operation *opLHS = op.getLhs().getDefiningOp())
3555 if (Operation *opRHS = op.getRhs().getDefiningOp())
3556 if (isa<ConcatOp, ReplicateOp>(opLHS) &&
3557 isa<ConcatOp, ReplicateOp>(opRHS)) {
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 bool foldMuxOfUniformArrays(MuxOp op, PatternRewriter &rewriter)
static Attribute constFoldAssociativeOp(ArrayRef< Attribute > operands, hw::PEO paramOpcode)
static bool subLowBitsCannotBorrow(Value lhs, Value rhs, unsigned numBits)
Returns true if subtracting the low numBits bits of rhs from those of lhs can never borrow from bit n...
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 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 bool narrowOperationWidth(OpTy op, size_t lowestBit, size_t highestBit, PatternRewriter &rewriter)
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)
Fold things like mux(cond, x|y|z|a, a) -> (x|y|z)&replicate(cond)|a and mux(cond, a,...
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 extractFromAdd(ExtractOp op, AddOp add, PatternRewriter &rewriter)
Fold a single-bit extract of a two-operand add into an icmp when bit bitIdx is known in both operands...
static bool applyCmpPredicate(ICmpPredicate predicate, const APInt &lhs, const APInt &rhs)
static bool addLowBitsCannotCarry(ValueRange operands, unsigned numBits)
Returns true if adding the low numBits bits of all operands can never carry into bit numBits: the lar...
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 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.
false return failure()
Parse an optional keyword or string and set instance into 'result'.
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.
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)
Value constructMuxTree(OpBuilder &builder, Location loc, ArrayRef< Value > selectors, ArrayRef< Value > leafNodes, Value outOfBoundsValue)
Construct a mux tree for given leaf nodes.
KnownBits computeKnownBits(Value value, unsigned maxDepth=5)
Compute "known bits" information about the specified value - the set of bits that are guaranteed to a...
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.
static OpTy replaceOpWithNewOpAndCopyNamehint(PatternRewriter &rewriter, Operation *op, Args &&...args)
A wrapper of PatternRewriter::replaceOpWithNewOp to propagate "sv.namehint" attribute.
void replaceOpAndCopyNamehint(PatternRewriter &rewriter, Operation *op, Value newValue)
A wrapper of PatternRewriter::replaceOp to propagate "sv.namehint" attribute.