CIRCT 23.0.0git
Loading...
Searching...
No Matches
CombFolds.cpp
Go to the documentation of this file.
1//===- CombFolds.cpp - Folds + Canonicalization for Comb operations -------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
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"
20
21using namespace mlir;
22using namespace circt;
23using namespace comb;
24using namespace matchers;
25
26// Returns true if the op has one of its own results as an operand.
27static bool isOpTriviallyRecursive(Operation *op) {
28 return llvm::any_of(op->getOperands(), [op](auto operand) {
29 return operand.getDefiningOp() == op;
30 });
31}
32
33/// Create a new instance of a generic operation that only has value operands,
34/// and has a single result value whose type matches the first operand.
35///
36/// This should not be used to create instances of ops with attributes or with
37/// more complicated type signatures.
38static Value createGenericOp(Location loc, OperationName name,
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);
44}
45
46static TypedAttr getIntAttr(const APInt &value, MLIRContext *context) {
47 return IntegerAttr::get(IntegerType::get(context, value.getBitWidth()),
48 value);
49}
50
51/// Flatten concat and mux operands into a vector.
52static void getConcatOperands(Value v, SmallVectorImpl<Value> &result) {
53 if (auto concat = v.getDefiningOp<ConcatOp>()) {
54 for (auto op : concat.getOperands())
55 getConcatOperands(op, result);
56 } else if (auto repl = v.getDefiningOp<ReplicateOp>()) {
57 for (size_t i = 0, e = repl.getMultiple(); i != e; ++i)
58 getConcatOperands(repl.getOperand(), result);
59 } else {
60 result.push_back(v);
61 }
62}
63
64// Return true if the op has SV attributes. Note that we cannot use a helper
65// function `hasSVAttributes` defined under SV dialect because of a cyclic
66// dependency.
67static bool hasSVAttributes(Operation *op) {
68 return op->hasAttr("sv.attributes");
69}
70
71namespace {
72template <typename SubType>
73struct ComplementMatcher {
74 SubType lhs;
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);
80 }
81};
82} // end anonymous namespace
83
84template <typename SubType>
85static inline ComplementMatcher<SubType> m_Complement(const SubType &subExpr) {
86 return ComplementMatcher<SubType>(subExpr);
87}
88
89/// Return true if the op will be flattened afterwards. Op will be flattend if
90/// it has a single user which has a same op type. User must be in same block.
91static bool shouldBeFlattened(Operation *op) {
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();
100 }
101 return false;
102}
103
104/// Flattens a single input in `op` if `hasOneUse` is true and it can be defined
105/// as an Op. Returns true if successful, and false otherwise.
106///
107/// Example: op(1, 2, op(3, 4), 5) -> op(1, 2, 3, 4, 5) // returns true
108///
109static bool tryFlatteningOperands(Operation *op, PatternRewriter &rewriter) {
110 // Skip if the operation should be flattened by another operation.
111 if (shouldBeFlattened(op))
112 return false;
113
114 auto inputs = op->getOperands();
115
116 SmallVector<Value, 4> newOperands;
117 SmallVector<Location, 4> newLocations{op->getLoc()};
118 newOperands.reserve(inputs.size());
119 struct Element {
120 decltype(inputs.begin()) current, end;
121 };
122
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(); // Do not pop. Take ref.
129
130 // Pop when we finished traversing the current operand range.
131 if (element.current == element.end) {
132 worklist.pop_back();
133 continue;
134 }
135
136 Value value = *element.current++;
137 auto *flattenOp = value.getDefiningOp();
138 // If not defined by a compatible operation of the same kind and
139 // from the same block, keep this as-is.
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);
144 continue;
145 }
146
147 // Don't duplicate logic when it has multiple uses.
148 if (!value.hasOneUse()) {
149 // We can fold a multi-use binary operation into this one if this allows a
150 // constant to fold though. For example, fold
151 // (or a, b, c, (or d, cst1), cst2) --> (or a, b, c, d, cst1, cst2)
152 // since the constants will both fold and we end up with the equiv cost.
153 //
154 // We don't do this for add/mul because the hardware won't be shared
155 // between the two ops if duplicated.
156 if (flattenOp->getNumOperands() != 2 || !isa<AndOp, OrOp, XorOp>(op) ||
157 !flattenOp->getOperand(1).getDefiningOp<hw::ConstantOp>() ||
158 !inputs.back().getDefiningOp<hw::ConstantOp>()) {
159 newOperands.push_back(value);
160 continue;
161 }
162 }
163
164 changed = true;
165
166 // Otherwise, push operands into worklist.
167 auto flattenOpInputs = flattenOp->getOperands();
168 worklist.push_back({flattenOpInputs.begin(), flattenOpInputs.end()});
169 newLocations.push_back(flattenOp->getLoc());
170 }
171
172 if (!changed)
173 return false;
174
175 Value result = createGenericOp(FusedLoc::get(op->getContext(), newLocations),
176 op->getName(), newOperands, rewriter);
177 if (binFlag)
178 result.getDefiningOp()->setAttr("twoState", rewriter.getUnitAttr());
179
180 replaceOpAndCopyNamehint(rewriter, op, result);
181 return true;
182}
183
184// Given a range of uses of an operation, find the lowest and highest bits
185// inclusive that are ever referenced. The range of uses must not be empty.
186static std::pair<size_t, size_t>
187getLowestBitAndHighestBitRequired(Operation *op, bool narrowTrailingBits,
188 size_t originalOpWidth) {
189 auto users = op->getUsers();
190 assert(!users.empty() &&
191 "getLowestBitAndHighestBitRequired cannot operate on "
192 "a empty list of uses.");
193
194 // when we don't want to narrowTrailingBits (namely in arithmetic
195 // operations), forcing lowestBitRequired = 0
196 size_t lowestBitRequired = narrowTrailingBits ? originalOpWidth - 1 : 0;
197 size_t highestBitRequired = 0;
198
199 for (auto *user : users) {
200 if (auto extractOp = dyn_cast<ExtractOp>(user)) {
201 size_t lowBit = extractOp.getLowBit();
202 size_t highBit =
203 cast<IntegerType>(extractOp.getType()).getWidth() + lowBit - 1;
204 highestBitRequired = std::max(highestBitRequired, highBit);
205 lowestBitRequired = std::min(lowestBitRequired, lowBit);
206 continue;
207 }
208
209 highestBitRequired = originalOpWidth - 1;
210 lowestBitRequired = 0;
211 break;
212 }
213
214 return {lowestBitRequired, highestBitRequired};
215}
216
217template <class OpTy>
218static bool narrowOperationWidth(OpTy op, bool narrowTrailingBits,
219 PatternRewriter &rewriter) {
220 IntegerType opType = dyn_cast<IntegerType>(op.getResult().getType());
221 if (!opType)
222 return false;
223
224 auto range = getLowestBitAndHighestBitRequired(op, narrowTrailingBits,
225 opType.getWidth());
226 if (range.second + 1 == opType.getWidth() && range.first == 0)
227 return false;
228
229 SmallVector<Value> args;
230 auto newType = rewriter.getIntegerType(range.second - range.first + 1);
231 for (auto inop : op.getOperands()) {
232 // deal with muxes here
233 if (inop.getType() != op.getType())
234 args.push_back(inop);
235 else
236 args.push_back(rewriter.createOrFold<ExtractOp>(inop.getLoc(), newType,
237 inop, range.first));
238 }
239 auto newop = OpTy::create(rewriter, op.getLoc(), newType, args);
240 newop->setDialectAttrs(op->getDialectAttrs());
241 if (op.getTwoState())
242 newop.setTwoState(true);
243
244 Value newResult = newop.getResult();
245 if (range.first)
246 newResult = rewriter.createOrFold<ConcatOp>(
247 op.getLoc(), newResult,
248 hw::ConstantOp::create(rewriter, op.getLoc(),
249 APInt::getZero(range.first)));
250 if (range.second + 1 < opType.getWidth())
251 newResult = rewriter.createOrFold<ConcatOp>(
252 op.getLoc(),
254 rewriter, op.getLoc(),
255 APInt::getZero(opType.getWidth() - range.second - 1)),
256 newResult);
257 rewriter.replaceOp(op, newResult);
258 return true;
259}
260
261//===----------------------------------------------------------------------===//
262// Unary Operations
263//===----------------------------------------------------------------------===//
264
265OpFoldResult ReplicateOp::fold(FoldAdaptor adaptor) {
266 if (isOpTriviallyRecursive(*this))
267 return {};
268
269 // Replicate one time -> noop.
270 if (cast<IntegerType>(getType()).getWidth() ==
271 getInput().getType().getIntOrFloatBitWidth())
272 return getInput();
273
274 // Constant fold.
275 if (auto input = dyn_cast_or_null<IntegerAttr>(adaptor.getInput())) {
276 if (input.getValue().getBitWidth() == 1) {
277 if (input.getValue().isZero())
278 return getIntAttr(
279 APInt::getZero(cast<IntegerType>(getType()).getWidth()),
280 getContext());
281 return getIntAttr(
282 APInt::getAllOnes(cast<IntegerType>(getType()).getWidth()),
283 getContext());
284 }
285
286 APInt result = APInt::getZeroWidth();
287 for (auto i = getMultiple(); i != 0; --i)
288 result = result.concat(input.getValue());
289 return getIntAttr(result, getContext());
290 }
291
292 return {};
293}
294
295OpFoldResult ParityOp::fold(FoldAdaptor adaptor) {
296 if (isOpTriviallyRecursive(*this))
297 return {};
298
299 // Constant fold.
300 if (auto input = dyn_cast_or_null<IntegerAttr>(adaptor.getInput()))
301 return getIntAttr(APInt(1, input.getValue().popcount() & 1), getContext());
302
303 // parity(x) -> x for single-bit values.
304 if (hw::getBitWidth(getInput().getType()) == 1)
305 return getInput();
306
307 return {};
308}
309
310LogicalResult ParityOp::canonicalize(ParityOp op, PatternRewriter &rewriter) {
312 return failure();
313
314 // Helper to check if a value has zero parity (even number of 1 bits)
315 auto isParityZero = [](Value v) {
316 APInt value;
317 return matchPattern(v, m_ConstantInt(&value)) && value.popcount() % 2 == 0;
318 };
319
320 // parity(concat(c, x)) -> parity(x) when parity(c) == 0
321 // parity(concat(x, c)) -> parity(x) when parity(c) == 0
322 auto concat = op.getInput().getDefiningOp<ConcatOp>();
323 if (!concat)
324 return failure();
325
326 auto operands = concat.getInputs();
327 if (operands.size() != 2)
328 return failure();
329
330 if (isParityZero(operands[0])) {
331 replaceOpWithNewOpAndCopyNamehint<ParityOp>(rewriter, op, operands[1],
332 op.getTwoState());
333 return success();
334 }
335
336 if (isParityZero(operands[1])) {
337 replaceOpWithNewOpAndCopyNamehint<ParityOp>(rewriter, op, operands[0],
338 op.getTwoState());
339 return success();
340 }
341
342 return failure();
343}
344
345//===----------------------------------------------------------------------===//
346// Binary Operations
347//===----------------------------------------------------------------------===//
348
349/// Performs constant folding `calculate` with element-wise behavior on the two
350/// attributes in `operands` and returns the result if possible.
351static Attribute constFoldBinaryOp(ArrayRef<Attribute> operands,
352 hw::PEO paramOpcode) {
353 assert(operands.size() == 2 && "binary op takes two operands");
354 if (!operands[0] || !operands[1])
355 return {};
356
357 // Fold constants with ParamExprAttr::get which handles simple constants as
358 // well as parameter expressions.
359 return hw::ParamExprAttr::get(paramOpcode, cast<TypedAttr>(operands[0]),
360 cast<TypedAttr>(operands[1]));
361}
362
363OpFoldResult ShlOp::fold(FoldAdaptor adaptor) {
364 if (isOpTriviallyRecursive(*this))
365 return {};
366
367 if (auto rhs = dyn_cast_or_null<IntegerAttr>(adaptor.getRhs())) {
368 if (rhs.getValue().isZero())
369 return getOperand(0);
370
371 unsigned width = getType().getIntOrFloatBitWidth();
372 if (rhs.getValue().uge(width))
373 return getIntAttr(APInt::getZero(width), getContext());
374 }
375 return constFoldBinaryOp(adaptor.getOperands(), hw::PEO::Shl);
376}
377
378LogicalResult ShlOp::canonicalize(ShlOp op, PatternRewriter &rewriter) {
380 return failure();
381
382 // ShlOp(x, cst) -> Concat(Extract(x), zeros)
383 APInt value;
384 if (!matchPattern(op.getRhs(), m_ConstantInt(&value)))
385 return failure();
386
387 unsigned width = cast<IntegerType>(op.getLhs().getType()).getWidth();
388 if (value.ugt(width))
389 value = width;
390 unsigned shift = value.getZExtValue();
391
392 // This case is handled by fold.
393 if (width <= shift || shift == 0)
394 return failure();
395
396 auto zeros =
397 hw::ConstantOp::create(rewriter, op.getLoc(), APInt::getZero(shift));
398
399 // Remove the high bits which would be removed by the Shl.
400 auto extract =
401 ExtractOp::create(rewriter, op.getLoc(), op.getLhs(), 0, width - shift);
402
403 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(rewriter, op, extract, zeros);
404 return success();
405}
406
407OpFoldResult ShrUOp::fold(FoldAdaptor adaptor) {
408 if (isOpTriviallyRecursive(*this))
409 return {};
410
411 if (auto rhs = dyn_cast_or_null<IntegerAttr>(adaptor.getRhs())) {
412 if (rhs.getValue().isZero())
413 return getOperand(0);
414
415 unsigned width = getType().getIntOrFloatBitWidth();
416 if (rhs.getValue().uge(width))
417 return getIntAttr(APInt::getZero(width), getContext());
418 }
419 return constFoldBinaryOp(adaptor.getOperands(), hw::PEO::ShrU);
420}
421
422LogicalResult ShrUOp::canonicalize(ShrUOp op, PatternRewriter &rewriter) {
424 return failure();
425
426 // ShrUOp(x, cst) -> Concat(zeros, Extract(x))
427 APInt value;
428 if (!matchPattern(op.getRhs(), m_ConstantInt(&value)))
429 return failure();
430
431 unsigned width = cast<IntegerType>(op.getLhs().getType()).getWidth();
432 if (value.ugt(width))
433 value = width;
434 unsigned shift = value.getZExtValue();
435
436 // This case is handled by fold.
437 if (width <= shift || shift == 0)
438 return failure();
439
440 auto zeros =
441 hw::ConstantOp::create(rewriter, op.getLoc(), APInt::getZero(shift));
442
443 // Remove the low bits which would be removed by the Shr.
444 auto extract = ExtractOp::create(rewriter, op.getLoc(), op.getLhs(), shift,
445 width - shift);
446
447 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(rewriter, op, zeros, extract);
448 return success();
449}
450
451OpFoldResult ShrSOp::fold(FoldAdaptor adaptor) {
452 if (isOpTriviallyRecursive(*this))
453 return {};
454
455 if (auto rhs = dyn_cast_or_null<IntegerAttr>(adaptor.getRhs()))
456 if (rhs.getValue().isZero())
457 return getOperand(0);
458 return constFoldBinaryOp(adaptor.getOperands(), hw::PEO::ShrS);
459}
460
461LogicalResult ShrSOp::canonicalize(ShrSOp op, PatternRewriter &rewriter) {
463 return failure();
464
465 // ShrSOp(x, cst) -> Concat(replicate(extract(x, topbit)),extract(x))
466 APInt value;
467 if (!matchPattern(op.getRhs(), m_ConstantInt(&value)))
468 return failure();
469
470 unsigned width = cast<IntegerType>(op.getLhs().getType()).getWidth();
471 if (value.ugt(width))
472 value = width;
473 unsigned shift = value.getZExtValue();
474
475 auto topbit =
476 rewriter.createOrFold<ExtractOp>(op.getLoc(), op.getLhs(), width - 1, 1);
477 auto sext = rewriter.createOrFold<ReplicateOp>(op.getLoc(), topbit, shift);
478
479 if (width == shift) {
480 replaceOpAndCopyNamehint(rewriter, op, {sext});
481 return success();
482 }
483
484 auto extract = ExtractOp::create(rewriter, op.getLoc(), op.getLhs(), shift,
485 width - shift);
486
487 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(rewriter, op, sext, extract);
488 return success();
489}
490
491//===----------------------------------------------------------------------===//
492// Other Operations
493//===----------------------------------------------------------------------===//
494
495OpFoldResult ExtractOp::fold(FoldAdaptor adaptor) {
496 if (isOpTriviallyRecursive(*this))
497 return {};
498
499 // If we are extracting the entire input, then return it.
500 if (getInput().getType() == getType())
501 return getInput();
502
503 // Constant fold.
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),
507 getContext());
508 }
509 return {};
510}
511
512// Transforms extract(lo, cat(a, b, c, d, e)) into
513// cat(extract(lo1, b), c, extract(lo2, d)).
514// innerCat must be the argument of the provided ExtractOp.
515//
516// When prefixWidths is non-empty it is used for O(log N) binary-search lookup
517// of the first relevant concat operand instead of the default O(N) linear scan.
518// The array must contain cumulative bit widths in LSB-first (reversed) order:
519// prefixWidths[i] = sum of bitwidths of concat operands [N-1, N-2, ..., N-i]
520static LogicalResult
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();
527
528 // Find the first operand (in LSB-first order) that contains lowBit.
529 size_t firstIdx;
530 size_t beginOfFirst;
531 if (!prefixWidths.empty()) {
532 // O(log N) binary search path.
533 auto it =
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;
538 } else {
539 // O(N) linear scan path.
540 firstIdx = 0;
541 beginOfFirst = 0;
542 for (size_t i = 0; i < numOperands; ++i) {
543 size_t w =
544 concatInputs[numOperands - 1 - i].getType().getIntOrFloatBitWidth();
545 if (lowBit < beginOfFirst + w) {
546 firstIdx = i;
547 break;
548 }
549 beginOfFirst += w;
550 }
551 }
552
553 SmallVector<Value> reverseConcatArgs;
554 size_t widthRemaining = op.getType().getIntOrFloatBitWidth();
555 size_t extractLo = lowBit - beginOfFirst;
556
557 // Walk forward from firstIdx in LSB-first order, building
558 // [ extract(a), b, extract(c) ], skipping an extract where possible (where
559 // the whole operand is consumed).
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);
564
565 if (widthToConsume == operandWidth && extractLo == 0) {
566 reverseConcatArgs.push_back(concatArg);
567 } else {
568 auto resultType = IntegerType::get(rewriter.getContext(), widthToConsume);
569 reverseConcatArgs.push_back(ExtractOp::create(
570 rewriter, op.getLoc(), resultType, concatArg, extractLo));
571 }
572
573 widthRemaining -= widthToConsume;
574 // Beyond the first element, all elements are extracted from position 0.
575 extractLo = 0;
576 }
577
578 if (reverseConcatArgs.size() == 1) {
579 replaceOpAndCopyNamehint(rewriter, op, reverseConcatArgs[0]);
580 } else {
581 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(
582 rewriter, op, SmallVector<Value>(llvm::reverse(reverseConcatArgs)));
583 }
584 return success();
585}
586
587// Transforms extract(lo, replicate(a, N)) into replicate(a, N-c).
588static bool extractFromReplicate(ExtractOp op, ReplicateOp replicate,
589 PatternRewriter &rewriter) {
590 auto extractResultWidth = cast<IntegerType>(op.getType()).getWidth();
591 auto replicateEltWidth =
592 replicate.getOperand().getType().getIntOrFloatBitWidth();
593
594 // If the extract starts at the base of an element and is an even multiple,
595 // we can replace the extract with a smaller replicate.
596 if (op.getLowBit() % replicateEltWidth == 0 &&
597 extractResultWidth % replicateEltWidth == 0) {
598 replaceOpWithNewOpAndCopyNamehint<ReplicateOp>(rewriter, op, op.getType(),
599 replicate.getOperand());
600 return true;
601 }
602
603 // If the extract is completely contained in one element, extract from the
604 // element.
605 if (op.getLowBit() % replicateEltWidth + extractResultWidth <=
606 replicateEltWidth) {
607 replaceOpWithNewOpAndCopyNamehint<ExtractOp>(
608 rewriter, op, op.getType(), replicate.getOperand(),
609 op.getLowBit() % replicateEltWidth);
610 return true;
611 }
612
613 // We don't currently handle the case of extracting from non-whole elements,
614 // e.g. `extract (replicate 2-bit-thing, N), 1`.
615 return false;
616}
617
618LogicalResult ExtractOp::canonicalize(ExtractOp op, PatternRewriter &rewriter) {
620 return failure();
621 auto *inputOp = op.getInput().getDefiningOp();
622
623 // This turns out to be incredibly expensive. Disable until performance is
624 // addressed.
625#if 0
626 // If the extracted bits are all known, then return the result.
627 auto knownBits = computeKnownBits(op.getInput())
628 .extractBits(cast<IntegerType>(op.getType()).getWidth(),
629 op.getLowBit());
630 if (knownBits.isConstant()) {
631 replaceOpWithNewOpAndCopyNamehint<hw::ConstantOp>(rewriter, op,
632 knownBits.getConstant());
633 return success();
634 }
635#endif
636
637 // extract(olo, extract(ilo, x)) = extract(olo + ilo, x)
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());
642 return success();
643 }
644
645 // extract(lo, cat(a, b, c, d, e)) = cat(extract(lo1, b), c, extract(lo2, d))
646 if (auto innerCat = dyn_cast_or_null<ConcatOp>(inputOp))
647 return extractConcatToConcatExtract(op, innerCat, rewriter);
648
649 // extract(lo, replicate(a))
650 if (auto replicate = dyn_cast_or_null<ReplicateOp>(inputOp))
651 if (extractFromReplicate(op, replicate, rewriter))
652 return success();
653
654 // `extract(and(a, cst))` -> `extract(a)` when the relevant bits of the
655 // and/or/xor are not modifying the extracted bits.
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());
664 return success();
665 }
666
667 // `extract(and(a, cst))` -> `concat(extract(a), 0)` if we only need one
668 // extract to represent the result. Turning it into a pile of extracts is
669 // always fine by our cost model, but we don't want to explode things into
670 // a ton of bits because it will bloat the IR and generated Verilog.
671 if (isa<AndOp>(inputOp)) {
672 // For our cost model, we only do this if the bit pattern is a
673 // contiguous series of ones.
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;
680 if (lz)
681 resultElts.push_back(hw::ConstantOp::create(rewriter, op.getLoc(),
682 APInt::getZero(lz)));
683 resultElts.push_back(rewriter.createOrFold<ExtractOp>(
684 op.getLoc(), resultTy, inputOp->getOperand(0),
685 op.getLowBit() + tz));
686 if (tz)
687 resultElts.push_back(hw::ConstantOp::create(rewriter, op.getLoc(),
688 APInt::getZero(tz)));
689 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(rewriter, op, resultElts);
690 return success();
691 }
692 }
693 }
694 }
695
696 // `extract(lowBit, shl(1, x))` -> `x == lowBit` when a single bit is
697 // extracted.
698 if (cast<IntegerType>(op.getType()).getWidth() == 1 && inputOp)
699 if (auto shlOp = dyn_cast<ShlOp>(inputOp)) {
700 // Don't canonicalize if the shift is multiply used.
701 if (shlOp->hasOneUse())
702 if (auto lhsCst = shlOp.getLhs().getDefiningOp<hw::ConstantOp>())
703 if (lhsCst.getValue().isOne()) {
704 auto newCst = hw::ConstantOp::create(
705 rewriter, shlOp.getLoc(),
706 APInt(lhsCst.getValue().getBitWidth(), op.getLowBit()));
707 replaceOpWithNewOpAndCopyNamehint<ICmpOp>(
708 rewriter, op, ICmpPredicate::eq, shlOp->getOperand(1), newCst,
709 false);
710 return success();
711 }
712 }
713
714 return failure();
715}
716
717//===----------------------------------------------------------------------===//
718// Associative Variadic operations
719//===----------------------------------------------------------------------===//
720
721// Reduce all operands to a single value (either integer constant or parameter
722// expression) if all the operands are constants.
723static Attribute constFoldAssociativeOp(ArrayRef<Attribute> operands,
724 hw::PEO paramOpcode) {
725 assert(operands.size() > 1 && "caller should handle one-operand case");
726 // We can only fold anything in the case where all operands are known to be
727 // constants. Check the least common one first for an early out.
728 if (!operands[1] || !operands[0])
729 return {};
730
731 // This will fold to a simple constant if all operands are constant.
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);
739 else
740 break;
741 }
742 if (typedOperands.size() == operands.size())
743 return hw::ParamExprAttr::get(paramOpcode, typedOperands);
744 }
745
746 return {};
747}
748
749/// When we find a logical operation (and, or, xor) with a constant e.g.
750/// `X & 42`, we want to push the constant into the computation of X if it leads
751/// to simplification.
752///
753/// This function handles the case where the logical operation has a concat
754/// operand. We check to see if we can simplify the concat, e.g. when it has
755/// constant operands.
756///
757/// This returns true when a simplification happens.
758static bool canonicalizeLogicalCstWithConcat(Operation *logicalOp,
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));
763
764 // Check to see if any operands can be simplified by pushing the logical op
765 // into all parts of the concat.
766 bool canSimplify =
767 llvm::any_of(concatOp->getOperands(), [&](Value operand) -> bool {
768 auto *operandOp = operand.getDefiningOp();
769 if (!operandOp)
770 return false;
771
772 // If the concat has a constant operand then we can transform this.
773 if (isa<hw::ConstantOp>(operandOp))
774 return true;
775 // If the concat has the same logical operation and that operation has
776 // a constant operation than we can fold it into that suboperation.
777 return operandOp->getName() == logicalOp->getName() &&
778 operandOp->hasOneUse() && operandOp->getNumOperands() != 0 &&
779 operandOp->getOperands().back().getDefiningOp<hw::ConstantOp>();
780 });
781
782 if (!canSimplify)
783 return false;
784
785 // Create a new instance of the logical operation. We have to do this the
786 // hard way since we're generic across a family of different ops.
787 auto createLogicalOp = [&](ArrayRef<Value> operands) -> Value {
788 return createGenericOp(logicalOp->getLoc(), logicalOp->getName(), operands,
789 rewriter);
790 };
791
792 // Ok, let's do the transformation. We do this by slicing up the constant
793 // for each unit of the concat and duplicate the operation into the
794 // sub-operand.
795 SmallVector<Value> newConcatOperands;
796 newConcatOperands.reserve(concatOp->getNumOperands());
797
798 // Work from MSB to LSB.
799 size_t nextOperandBit = concatOp.getType().getIntOrFloatBitWidth();
800 for (Value operand : concatOp->getOperands()) {
801 size_t operandWidth = operand.getType().getIntOrFloatBitWidth();
802 nextOperandBit -= operandWidth;
803 // Take a slice of the constant.
804 auto eltCst =
805 hw::ConstantOp::create(rewriter, logicalOp->getLoc(),
806 cst.lshr(nextOperandBit).trunc(operandWidth));
807
808 newConcatOperands.push_back(createLogicalOp({operand, eltCst}));
809 }
810
811 // Create the concat, and the rest of the logical op if we need it.
812 Value newResult =
813 ConcatOp::create(rewriter, concatOp.getLoc(), newConcatOperands);
814
815 // If we had a variadic logical op on the top level, then recreate it with the
816 // new concat and without the constant operand.
817 if (logicalOp->getNumOperands() > 2) {
818 auto origOperands = logicalOp->getOperands();
819 SmallVector<Value> operands;
820 // Take any stuff before the concat.
821 operands.append(origOperands.begin(), origOperands.begin() + concatIdx);
822 // Take any stuff after the concat but before the constant.
823 operands.append(origOperands.begin() + concatIdx + 1,
824 origOperands.begin() + (origOperands.size() - 1));
825 // Include the new concat.
826 operands.push_back(newResult);
827 newResult = createLogicalOp(operands);
828 }
829
830 replaceOpAndCopyNamehint(rewriter, logicalOp, newResult);
831 return true;
832}
833
834// Determines whether the inputs to a logical element are of opposite
835// comparisons and can lowered into a constant.
836static bool canCombineOppositeBinCmpIntoConstant(OperandRange operands) {
837 llvm::SmallDenseSet<std::tuple<ICmpPredicate, Value, Value>> seenPredicates;
838
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}))
847 return true;
848
849 seenPredicates.insert({predicate, lhs, rhs});
850 }
851 }
852 return false;
853}
854
855OpFoldResult AndOp::fold(FoldAdaptor adaptor) {
856 if (isOpTriviallyRecursive(*this))
857 return {};
858
859 APInt value = APInt::getAllOnes(cast<IntegerType>(getType()).getWidth());
860
861 auto inputs = adaptor.getInputs();
862
863 // and(x, 01, 10) -> 00 -- annulment.
864 for (auto operand : inputs) {
865 auto attr = dyn_cast_or_null<IntegerAttr>(operand);
866 if (!attr)
867 continue;
868 value &= attr.getValue();
869 if (value.isZero())
870 return getIntAttr(value, getContext());
871 }
872
873 // and(x, -1) -> x.
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];
878
879 // and(x, x, x) -> x. This also handles and(x) -> x.
880 if (llvm::all_of(getInputs(),
881 [&](auto in) { return in == this->getInputs()[0]; }))
882 return getInputs()[0];
883
884 // and(..., x, ..., ~x, ...) -> 0
885 for (Value arg : getInputs()) {
886 Value subExpr;
887 if (matchPattern(arg, m_Complement(m_Any(&subExpr)))) {
888 for (Value arg2 : getInputs())
889 if (arg2 == subExpr)
890 return getIntAttr(
891 APInt::getZero(cast<IntegerType>(getType()).getWidth()),
892 getContext());
893 }
894 }
895
896 // x0 = icmp(pred, x, y)
897 // x1 = icmp(!pred, x, y)
898 // and(x0, x1) -> 0
900 return getIntAttr(APInt::getZero(cast<IntegerType>(getType()).getWidth()),
901 getContext());
902
903 // Constant fold
904 return constFoldAssociativeOp(inputs, hw::PEO::And);
905}
906
907/// Returns a single common operand that all inputs of the operation `op` can
908/// be traced back to, or an empty `Value` if no such operand exists.
909///
910/// For example for `or(a[0], a[1], ..., a[n-1])` this function returns `a`
911/// (assuming the bit-width of `a` is `n`).
912template <typename Op>
913static Value getCommonOperand(Op op) {
914 if (!op.getType().isInteger(1))
915 return Value();
916
917 auto inputs = op.getInputs();
918 size_t size = inputs.size();
919
920 auto sourceOp = inputs[0].template getDefiningOp<ExtractOp>();
921 if (!sourceOp)
922 return Value();
923 Value source = sourceOp.getOperand();
924
925 // Fast path: the input size is not equal to the width of the source.
926 if (size != source.getType().getIntOrFloatBitWidth())
927 return Value();
928
929 // Tracks the bits that were encountered.
930 llvm::BitVector bits(size);
931 bits.set(sourceOp.getLowBit());
932
933 for (size_t i = 1; i != size; ++i) {
934 auto extractOp = inputs[i].template getDefiningOp<ExtractOp>();
935 if (!extractOp || extractOp.getOperand() != source)
936 return Value();
937 bits.set(extractOp.getLowBit());
938 }
939
940 return bits.all() ? source : Value();
941}
942
943/// Canonicalize an idempotent operation `op` so that only one input of any kind
944/// occurs.
945///
946/// Example: `and(x, y, x, z)` -> `and(x, y, z)`
947template <typename Op>
948static bool canonicalizeIdempotentInputs(Op op, PatternRewriter &rewriter) {
949 // Depth limit to search, in operations. Chosen arbitrarily, keep small.
950 constexpr unsigned limit = 3;
951 auto inputs = op.getInputs();
952
953 llvm::SmallSetVector<Value, 8> uniqueInputs(inputs.begin(), inputs.end());
954 llvm::SmallDenseSet<Op, 8> checked;
955 checked.insert(op);
956
957 struct OpWithDepth {
958 Op op;
959 unsigned depth;
960 };
961 llvm::SmallVector<OpWithDepth, 8> worklist;
962
963 auto enqueue = [&worklist, &checked, &op](Value input, unsigned depth) {
964 // Add to worklist if within depth limit, is defined in the same block by
965 // the same kind of operation, has same two-state-ness, and not enqueued
966 // previously.
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});
972 }
973 };
974
975 for (auto input : uniqueInputs)
976 enqueue(input, 0);
977
978 while (!worklist.empty()) {
979 auto item = worklist.pop_back_val();
980
981 for (auto input : item.op.getInputs()) {
982 uniqueInputs.remove(input);
983 enqueue(input, item.depth);
984 }
985 }
986
987 if (uniqueInputs.size() < inputs.size()) {
988 replaceOpWithNewOpAndCopyNamehint<Op>(rewriter, op, op.getType(),
989 uniqueInputs.getArrayRef(),
990 op.getTwoState());
991 return true;
992 }
993
994 return false;
995}
996
997LogicalResult AndOp::canonicalize(AndOp op, PatternRewriter &rewriter) {
999 return failure();
1000
1001 auto inputs = op.getInputs();
1002 auto size = inputs.size();
1003
1004 // and(x, and(...)) -> and(x, ...) -- flatten
1005 if (tryFlatteningOperands(op, rewriter))
1006 return success();
1007
1008 // and(..., x, ..., x) -> and(..., x, ...) -- idempotent
1009 // and(..., x, and(..., x, ...)) -> and(..., and(..., x, ...)) -- idempotent
1010 // Trivial and(x), and(x, x) cases are handled by [AndOp::fold] above.
1011 if (size > 1 && canonicalizeIdempotentInputs(op, rewriter))
1012 return success();
1013
1014 assert(size > 1 && "expected 2 or more operands, `fold` should handle this");
1015
1016 // Patterns for and with a constant on RHS.
1017 APInt value;
1018 if (matchPattern(inputs.back(), m_ConstantInt(&value))) {
1019 // and(..., '1) -> and(...) -- identity
1020 if (value.isAllOnes()) {
1021 replaceOpWithNewOpAndCopyNamehint<AndOp>(rewriter, op, op.getType(),
1022 inputs.drop_back(), false);
1023 return success();
1024 }
1025
1026 // TODO: Combine multiple constants together even if they aren't at the
1027 // end. and(..., c1, c2) -> and(..., c3) where c3 = c1 & c2 -- constant
1028 // folding
1029 APInt value2;
1030 if (matchPattern(inputs[size - 2], m_ConstantInt(&value2))) {
1031 auto cst = hw::ConstantOp::create(rewriter, op.getLoc(), value & value2);
1032 SmallVector<Value, 4> newOperands(inputs.drop_back(/*n=*/2));
1033 newOperands.push_back(cst);
1034 replaceOpWithNewOpAndCopyNamehint<AndOp>(rewriter, op, op.getType(),
1035 newOperands, false);
1036 return success();
1037 }
1038
1039 // Handle 'and' with a single bit constant on the RHS.
1040 if (size == 2 && value.isPowerOf2()) {
1041 // If the LHS is a replicate from a single bit, we can 'concat' it
1042 // into place. e.g.:
1043 // `replicate(x) & 4` -> `concat(zeros, x, zeros)`
1044 // TODO: Generalize this for non-single-bit operands.
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();
1050
1051 // Don't add zero bit constants unnecessarily.
1052 SmallVector<Value, 3> concatOperands;
1053 if (trailingZeros != resultWidth - 1) {
1054 auto highZeros = hw::ConstantOp::create(
1055 rewriter, op.getLoc(),
1056 APInt::getZero(resultWidth - trailingZeros - 1));
1057 concatOperands.push_back(highZeros);
1058 }
1059 concatOperands.push_back(replicateOperand);
1060 if (trailingZeros != 0) {
1061 auto lowZeros = hw::ConstantOp::create(
1062 rewriter, op.getLoc(), APInt::getZero(trailingZeros));
1063 concatOperands.push_back(lowZeros);
1064 }
1065 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(
1066 rewriter, op, op.getType(), concatOperands);
1067 return success();
1068 }
1069 }
1070 }
1071
1072 // Narrow the op if the constant has leading or trailing zeros.
1073 //
1074 // and(a, 0b00101100) -> concat(0b00, and(extract(a), 0b1011), 0b00)
1075 unsigned leadingZeros = value.countLeadingZeros();
1076 unsigned trailingZeros = value.countTrailingZeros();
1077 if (leadingZeros > 0 || trailingZeros > 0) {
1078 unsigned maskLength = value.getBitWidth() - leadingZeros - trailingZeros;
1079
1080 // Extract the non-zero regions of the operands. Look through extracts.
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();
1087 }
1088 operands.push_back(ExtractOp::create(rewriter, op.getLoc(), input,
1089 offset, maskLength));
1090 }
1091
1092 // Add the narrowed mask if needed.
1093 auto narrowMask = value.extractBits(maskLength, trailingZeros);
1094 if (!narrowMask.isAllOnes())
1095 operands.push_back(hw::ConstantOp::create(
1096 rewriter, inputs.back().getLoc(), narrowMask));
1097
1098 // Create the narrow and op.
1099 Value narrowValue = operands.back();
1100 if (operands.size() > 1)
1101 narrowValue =
1102 AndOp::create(rewriter, op.getLoc(), operands, op.getTwoState());
1103 operands.clear();
1104
1105 // Concatenate the narrow and with the leading and trailing zeros.
1106 if (leadingZeros > 0)
1107 operands.push_back(hw::ConstantOp::create(
1108 rewriter, op.getLoc(), APInt::getZero(leadingZeros)));
1109 operands.push_back(narrowValue);
1110 if (trailingZeros > 0)
1111 operands.push_back(hw::ConstantOp::create(
1112 rewriter, op.getLoc(), APInt::getZero(trailingZeros)));
1113 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(rewriter, op, operands);
1114 return success();
1115 }
1116
1117 // and(concat(x, cst1), a, b, c, cst2)
1118 // ==> and(a, b, c, concat(and(x,cst2'), and(cst1,cst2'')).
1119 // We do this for even more multi-use concats since they are "just wiring".
1120 for (size_t i = 0; i < size - 1; ++i) {
1121 if (auto concat = inputs[i].getDefiningOp<ConcatOp>())
1122 if (canonicalizeLogicalCstWithConcat(op, i, value, rewriter))
1123 return success();
1124 }
1125 }
1126
1127 // extracts only of and(...) -> and(extract()...)
1128 if (narrowOperationWidth(op, true, rewriter))
1129 return success();
1130
1131 // and(a[0], a[1], ..., a[n]) -> icmp eq(a, -1)
1132 if (auto source = getCommonOperand(op)) {
1133 auto cmpAgainst =
1134 hw::ConstantOp::create(rewriter, op.getLoc(), APInt::getAllOnes(size));
1135 replaceOpWithNewOpAndCopyNamehint<ICmpOp>(rewriter, op, ICmpPredicate::eq,
1136 source, cmpAgainst);
1137 return success();
1138 }
1139
1140 // and(x, replicate(p : i1)) -> mux(p, x, zero)
1141 if (op.getTwoState() && op.getNumOperands() == 2) {
1142 auto isReplicateOfI1 = [](Value v) {
1143 auto rep = v.getDefiningOp<ReplicateOp>();
1144 if (!rep)
1145 return false;
1146 return rep.getOperand().getType().isInteger(1);
1147 };
1148 Value x = op.getOperand(0);
1149 Value y = op.getOperand(1);
1150 if (isReplicateOfI1(x))
1151 std::swap(x, y);
1152 if (isReplicateOfI1(y)) {
1153 Value p = y.getDefiningOp<ReplicateOp>().getInput();
1154 Value zero = hw::ConstantOp::create(
1155 rewriter, op.getLoc(), rewriter.getIntegerAttr(op.getType(), 0));
1156 replaceOpWithNewOpAndCopyNamehint<MuxOp>(rewriter, op, p, x, zero,
1157 /*isTwoState=*/true);
1158 return success();
1159 }
1160 }
1161
1162 /// TODO: and(..., x, not(x)) -> and(..., 0) -- complement
1163 return failure();
1164}
1165
1166OpFoldResult OrOp::fold(FoldAdaptor adaptor) {
1167 if (isOpTriviallyRecursive(*this))
1168 return {};
1169
1170 auto value = APInt::getZero(cast<IntegerType>(getType()).getWidth());
1171 auto inputs = adaptor.getInputs();
1172 // or(x, 10, 01) -> 11
1173 for (auto operand : inputs) {
1174 auto attr = dyn_cast_or_null<IntegerAttr>(operand);
1175 if (!attr)
1176 continue;
1177 value |= attr.getValue();
1178 if (value.isAllOnes())
1179 return getIntAttr(value, getContext());
1180 }
1181
1182 // or(x, 0) -> x
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];
1187
1188 // or(x, x, x) -> x. This also handles or(x) -> x
1189 if (llvm::all_of(getInputs(),
1190 [&](auto in) { return in == this->getInputs()[0]; }))
1191 return getInputs()[0];
1192
1193 // or(..., x, ..., ~x, ...) -> -1
1194 for (Value arg : getInputs()) {
1195 Value subExpr;
1196 if (matchPattern(arg, m_Complement(m_Any(&subExpr)))) {
1197 for (Value arg2 : getInputs())
1198 if (arg2 == subExpr)
1199 return getIntAttr(
1200 APInt::getAllOnes(cast<IntegerType>(getType()).getWidth()),
1201 getContext());
1202 }
1203 }
1204
1205 // x0 = icmp(pred, x, y)
1206 // x1 = icmp(!pred, x, y)
1207 // or(x0, x1) -> 1
1208 if (canCombineOppositeBinCmpIntoConstant(getInputs()))
1209 return getIntAttr(
1210 APInt::getAllOnes(cast<IntegerType>(getType()).getWidth()),
1211 getContext());
1212
1213 // Constant fold
1214 return constFoldAssociativeOp(inputs, hw::PEO::Or);
1215}
1216
1217LogicalResult OrOp::canonicalize(OrOp op, PatternRewriter &rewriter) {
1218 if (isOpTriviallyRecursive(op))
1219 return failure();
1220
1221 auto inputs = op.getInputs();
1222 auto size = inputs.size();
1223
1224 // or(x, or(...)) -> or(x, ...) -- flatten
1225 if (tryFlatteningOperands(op, rewriter))
1226 return success();
1227
1228 // or(..., x, ..., x, ...) -> or(..., x) -- idempotent
1229 // or(..., x, or(..., x, ...)) -> or(..., or(..., x, ...)) -- idempotent
1230 // Trivial or(x), or(x, x) cases are handled by [OrOp::fold].
1231 if (size > 1 && canonicalizeIdempotentInputs(op, rewriter))
1232 return success();
1233
1234 assert(size > 1 && "expected 2 or more operands");
1235
1236 // Patterns for and with a constant on RHS.
1237 APInt value;
1238 if (matchPattern(inputs.back(), m_ConstantInt(&value))) {
1239 // or(..., '0) -> or(...) -- identity
1240 if (value.isZero()) {
1241 replaceOpWithNewOpAndCopyNamehint<OrOp>(rewriter, op, op.getType(),
1242 inputs.drop_back());
1243 return success();
1244 }
1245
1246 // or(..., c1, c2) -> or(..., c3) where c3 = c1 | c2 -- constant folding
1247 APInt value2;
1248 if (matchPattern(inputs[size - 2], m_ConstantInt(&value2))) {
1249 auto cst = hw::ConstantOp::create(rewriter, op.getLoc(), value | value2);
1250 SmallVector<Value, 4> newOperands(inputs.drop_back(/*n=*/2));
1251 newOperands.push_back(cst);
1252 replaceOpWithNewOpAndCopyNamehint<OrOp>(rewriter, op, op.getType(),
1253 newOperands);
1254 return success();
1255 }
1256
1257 // or(concat(x, cst1), a, b, c, cst2)
1258 // ==> or(a, b, c, concat(or(x,cst2'), or(cst1,cst2'')).
1259 // We do this for even more multi-use concats since they are "just wiring".
1260 for (size_t i = 0; i < size - 1; ++i) {
1261 if (auto concat = inputs[i].getDefiningOp<ConcatOp>())
1262 if (canonicalizeLogicalCstWithConcat(op, i, value, rewriter))
1263 return success();
1264 }
1265 }
1266
1267 // extracts only of or(...) -> or(extract()...)
1268 if (narrowOperationWidth(op, true, rewriter))
1269 return success();
1270
1271 // or(a[0], a[1], ..., a[n]) -> icmp ne(a, 0)
1272 if (auto source = getCommonOperand(op)) {
1273 auto cmpAgainst =
1274 hw::ConstantOp::create(rewriter, op.getLoc(), APInt::getZero(size));
1275 replaceOpWithNewOpAndCopyNamehint<ICmpOp>(rewriter, op, ICmpPredicate::ne,
1276 source, cmpAgainst);
1277 return success();
1278 }
1279
1280 // or(mux(c_1, a, 0), mux(c_2, a, 0), ..., mux(c_n, a, 0)) -> mux(or(c_1, c_2,
1281 // .., c_n), a, 0)
1282 if (auto firstMux = op.getOperand(0).getDefiningOp<comb::MuxOp>()) {
1283 APInt value;
1284 if (op.getTwoState() && firstMux.getTwoState() &&
1285 matchPattern(firstMux.getFalseValue(), m_ConstantInt(&value)) &&
1286 value.isZero()) {
1287 SmallVector<Value> conditions{firstMux.getCond()};
1288 auto check = [&](Value v) {
1289 auto mux = v.getDefiningOp<comb::MuxOp>();
1290 if (!mux)
1291 return false;
1292 conditions.push_back(mux.getCond());
1293 return mux.getTwoState() &&
1294 firstMux.getTrueValue() == mux.getTrueValue() &&
1295 firstMux.getFalseValue() == mux.getFalseValue();
1296 };
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);
1302 return success();
1303 }
1304 }
1305 }
1306
1307 /// TODO: or(..., x, not(x)) -> or(..., '1) -- complement
1308 return failure();
1309}
1310
1311OpFoldResult XorOp::fold(FoldAdaptor adaptor) {
1312 if (isOpTriviallyRecursive(*this))
1313 return {};
1314
1315 auto size = getInputs().size();
1316 auto inputs = adaptor.getInputs();
1317
1318 // xor(x) -> x -- noop
1319 if (size == 1)
1320 return getInputs()[0];
1321
1322 // xor(x, x) -> 0 -- idempotent
1323 if (size == 2 && getInputs()[0] == getInputs()[1])
1324 return IntegerAttr::get(getType(), 0);
1325
1326 // xor(x, 0) -> x
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];
1331
1332 // xor(xor(x,1),1) -> x
1333 // but not self loop
1334 Value subExpr;
1335 if (matchPattern(getResult(), m_Complement(m_Complement(m_Any(&subExpr)))) &&
1336 subExpr != getResult())
1337 return subExpr;
1338
1339 // Constant fold
1340 return constFoldAssociativeOp(inputs, hw::PEO::Xor);
1341}
1342
1343// xor(icmp, a, b, 1) -> xor(icmp, a, b) if icmp has one user.
1344static void canonicalizeXorIcmpTrue(XorOp op, unsigned icmpOperand,
1345 PatternRewriter &rewriter) {
1346 auto icmp = op.getOperand(icmpOperand).getDefiningOp<ICmpOp>();
1347 auto negatedPred = ICmpOp::getNegatedPredicate(icmp.getPredicate());
1348
1349 Value result =
1350 ICmpOp::create(rewriter, icmp.getLoc(), negatedPred, icmp.getOperand(0),
1351 icmp.getOperand(1), icmp.getTwoState());
1352
1353 // If the xor had other operands, rebuild it.
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);
1359 result =
1360 XorOp::create(rewriter, op.getLoc(), newOperands, op.getTwoState());
1361 }
1362
1363 replaceOpAndCopyNamehint(rewriter, op, result);
1364}
1365
1366LogicalResult XorOp::canonicalize(XorOp op, PatternRewriter &rewriter) {
1367 if (isOpTriviallyRecursive(op))
1368 return failure();
1369
1370 auto inputs = op.getInputs();
1371 auto size = inputs.size();
1372 assert(size > 1 && "expected 2 or more operands");
1373
1374 // xor(..., x, x) -> xor (...) -- idempotent
1375 if (inputs[size - 1] == inputs[size - 2]) {
1376 assert(size > 2 &&
1377 "expected idempotent case for 2 elements handled already.");
1378 replaceOpWithNewOpAndCopyNamehint<XorOp>(rewriter, op, op.getType(),
1379 inputs.drop_back(/*n=*/2), false);
1380 return success();
1381 }
1382
1383 // Patterns for xor with a constant on RHS.
1384 APInt value;
1385 if (matchPattern(inputs.back(), m_ConstantInt(&value))) {
1386 // xor(..., 0) -> xor(...) -- identity
1387 if (value.isZero()) {
1388 replaceOpWithNewOpAndCopyNamehint<XorOp>(rewriter, op, op.getType(),
1389 inputs.drop_back(), false);
1390 return success();
1391 }
1392
1393 // xor(..., c1, c2) -> xor(..., c3) where c3 = c1 ^ c2.
1394 APInt value2;
1395 if (matchPattern(inputs[size - 2], m_ConstantInt(&value2))) {
1396 auto cst = hw::ConstantOp::create(rewriter, op.getLoc(), value ^ value2);
1397 SmallVector<Value, 4> newOperands(inputs.drop_back(/*n=*/2));
1398 newOperands.push_back(cst);
1399 replaceOpWithNewOpAndCopyNamehint<XorOp>(rewriter, op, op.getType(),
1400 newOperands, false);
1401 return success();
1402 }
1403
1404 bool isSingleBit = value.getBitWidth() == 1;
1405
1406 // Check for subexpressions that we can simplify.
1407 for (size_t i = 0; i < size - 1; ++i) {
1408 Value operand = inputs[i];
1409
1410 // xor(concat(x, cst1), a, b, c, cst2)
1411 // ==> xor(a, b, c, concat(xor(x,cst2'), xor(cst1,cst2'')).
1412 // We do this for even more multi-use concats since they are "just
1413 // wiring".
1414 if (auto concat = operand.getDefiningOp<ConcatOp>())
1415 if (canonicalizeLogicalCstWithConcat(op, i, value, rewriter))
1416 return success();
1417
1418 // xor(icmp, a, b, 1) -> xor(icmp, a, b) if icmp has one user.
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>())
1422 return canonicalizeXorIcmpTrue(op, i, rewriter), success();
1423 }
1424 }
1425 }
1426
1427 // xor(sext(x), -1) -> sext(xor(x,-1))
1428 // More concisely: ~sext(x) = sext(~x)
1429 Value complementVal;
1430 Value signExtBits;
1431 // Check for sext of the inverted value
1432 if (matchPattern(op.getResult(), m_Complement(m_Any(&complementVal))) &&
1433 matchPattern(complementVal, m_SextBy(m_Any(&signExtBits)))) {
1434 // Matched an sext with signExtBits - extract the base (unextended) value
1435 auto baseWidth = op.getType().getIntOrFloatBitWidth() -
1436 signExtBits.getType().getIntOrFloatBitWidth();
1437 auto base =
1438 ExtractOp::create(rewriter, op.getLoc(), complementVal, 0, baseWidth);
1439
1440 // Create negated sext: ~sext(x) = sext(~x)
1441 auto negBase = createOrFoldNot(rewriter, op.getLoc(), base, true);
1442 auto sextNegBase =
1443 createOrFoldSExt(rewriter, op.getLoc(), negBase, op.getType());
1444 replaceOpAndCopyNamehint(rewriter, op, sextNegBase);
1445 return success();
1446 }
1447
1448 // xor(x, xor(...)) -> xor(x, ...) -- flatten
1449 if (tryFlatteningOperands(op, rewriter))
1450 return success();
1451
1452 // extracts only of xor(...) -> xor(extract()...)
1453 if (narrowOperationWidth(op, true, rewriter))
1454 return success();
1455
1456 // xor(a[0], a[1], ..., a[n]) -> parity(a)
1457 if (auto source = getCommonOperand(op)) {
1458 replaceOpWithNewOpAndCopyNamehint<ParityOp>(rewriter, op, source);
1459 return success();
1460 }
1461
1462 return failure();
1463}
1464
1465OpFoldResult SubOp::fold(FoldAdaptor adaptor) {
1466 if (isOpTriviallyRecursive(*this))
1467 return {};
1468
1469 // sub(x - x) -> 0
1470 if (getRhs() == getLhs())
1471 return getIntAttr(
1472 APInt::getZero(getLhs().getType().getIntOrFloatBitWidth()),
1473 getContext());
1474
1475 if (adaptor.getRhs()) {
1476 // If both are constants, we can unconditionally fold.
1477 if (adaptor.getLhs()) {
1478 // Constant fold (c1 - c2) => (c1 + -1*c2).
1479 auto negOne = getIntAttr(
1480 APInt::getAllOnes(getLhs().getType().getIntOrFloatBitWidth()),
1481 getContext());
1482 auto rhsNeg = hw::ParamExprAttr::get(
1483 hw::PEO::Mul, cast<TypedAttr>(adaptor.getRhs()), negOne);
1484 return hw::ParamExprAttr::get(hw::PEO::Add,
1485 cast<TypedAttr>(adaptor.getLhs()), rhsNeg);
1486 }
1487
1488 // sub(x - 0) -> x
1489 if (auto rhsC = dyn_cast<IntegerAttr>(adaptor.getRhs())) {
1490 if (rhsC.getValue().isZero())
1491 return getLhs();
1492 }
1493 }
1494
1495 return {};
1496}
1497
1498LogicalResult SubOp::canonicalize(SubOp op, PatternRewriter &rewriter) {
1499 if (isOpTriviallyRecursive(op))
1500 return failure();
1501
1502 // sub(x, cst) -> add(x, -cst)
1503 APInt value;
1504 if (matchPattern(op.getRhs(), m_ConstantInt(&value))) {
1505 auto negCst = hw::ConstantOp::create(rewriter, op.getLoc(), -value);
1506 replaceOpWithNewOpAndCopyNamehint<AddOp>(rewriter, op, op.getLhs(), negCst,
1507 false);
1508 return success();
1509 }
1510
1511 // extracts only of sub(...) -> sub(extract()...)
1512 if (narrowOperationWidth(op, false, rewriter))
1513 return success();
1514
1515 return failure();
1516}
1517
1518OpFoldResult AddOp::fold(FoldAdaptor adaptor) {
1519 if (isOpTriviallyRecursive(*this))
1520 return {};
1521
1522 auto size = getInputs().size();
1523
1524 // add(x) -> x -- noop
1525 if (size == 1u)
1526 return getInputs()[0];
1527
1528 // Constant fold constant operands.
1529 return constFoldAssociativeOp(adaptor.getOperands(), hw::PEO::Add);
1530}
1531
1532LogicalResult AddOp::canonicalize(AddOp op, PatternRewriter &rewriter) {
1533 if (isOpTriviallyRecursive(op))
1534 return failure();
1535
1536 auto inputs = op.getInputs();
1537 auto size = inputs.size();
1538 assert(size > 1 && "expected 2 or more operands");
1539
1540 APInt value, value2;
1541
1542 // add(..., 0) -> add(...) -- identity
1543 if (matchPattern(inputs.back(), m_ConstantInt(&value)) && value.isZero()) {
1544 replaceOpWithNewOpAndCopyNamehint<AddOp>(rewriter, op, op.getType(),
1545 inputs.drop_back(), false);
1546 return success();
1547 }
1548
1549 // add(..., c1, c2) -> add(..., c3) where c3 = c1 + c2 -- constant folding
1550 if (matchPattern(inputs[size - 1], m_ConstantInt(&value)) &&
1551 matchPattern(inputs[size - 2], m_ConstantInt(&value2))) {
1552 auto cst = hw::ConstantOp::create(rewriter, op.getLoc(), value + value2);
1553 SmallVector<Value, 4> newOperands(inputs.drop_back(/*n=*/2));
1554 newOperands.push_back(cst);
1555 replaceOpWithNewOpAndCopyNamehint<AddOp>(rewriter, op, op.getType(),
1556 newOperands, false);
1557 return success();
1558 }
1559
1560 // add(..., x, x) -> add(..., shl(x, 1))
1561 if (inputs[size - 1] == inputs[size - 2]) {
1562 SmallVector<Value, 4> newOperands(inputs.drop_back(/*n=*/2));
1563
1564 auto one = hw::ConstantOp::create(rewriter, op.getLoc(), op.getType(), 1);
1565 auto shiftLeftOp =
1566 comb::ShlOp::create(rewriter, op.getLoc(), inputs.back(), one, false);
1567
1568 newOperands.push_back(shiftLeftOp);
1569 replaceOpWithNewOpAndCopyNamehint<AddOp>(rewriter, op, op.getType(),
1570 newOperands, false);
1571 return success();
1572 }
1573
1574 auto shlOp = inputs[size - 1].getDefiningOp<comb::ShlOp>();
1575 // add(..., x, shl(x, c)) -> add(..., mul(x, (1 << c) + 1))
1576 if (shlOp && shlOp.getLhs() == inputs[size - 2] &&
1577 matchPattern(shlOp.getRhs(), m_ConstantInt(&value))) {
1578
1579 APInt one(/*numBits=*/value.getBitWidth(), 1, /*isSigned=*/false);
1580 auto rhs =
1581 hw::ConstantOp::create(rewriter, op.getLoc(), (one << value) + one);
1582
1583 std::array<Value, 2> factors = {shlOp.getLhs(), rhs};
1584 auto mulOp = comb::MulOp::create(rewriter, op.getLoc(), factors, false);
1585
1586 SmallVector<Value, 4> newOperands(inputs.drop_back(/*n=*/2));
1587 newOperands.push_back(mulOp);
1588 replaceOpWithNewOpAndCopyNamehint<AddOp>(rewriter, op, op.getType(),
1589 newOperands, false);
1590 return success();
1591 }
1592
1593 auto mulOp = inputs[size - 1].getDefiningOp<comb::MulOp>();
1594 // add(..., x, mul(x, c)) -> add(..., mul(x, c + 1))
1595 if (mulOp && mulOp.getInputs().size() == 2 &&
1596 mulOp.getInputs()[0] == inputs[size - 2] &&
1597 matchPattern(mulOp.getInputs()[1], m_ConstantInt(&value))) {
1598
1599 APInt one(/*numBits=*/value.getBitWidth(), 1, /*isSigned=*/false);
1600 auto rhs = hw::ConstantOp::create(rewriter, op.getLoc(), value + one);
1601 std::array<Value, 2> factors = {mulOp.getInputs()[0], rhs};
1602 auto newMulOp = comb::MulOp::create(rewriter, op.getLoc(), factors, false);
1603
1604 SmallVector<Value, 4> newOperands(inputs.drop_back(/*n=*/2));
1605 newOperands.push_back(newMulOp);
1606 replaceOpWithNewOpAndCopyNamehint<AddOp>(rewriter, op, op.getType(),
1607 newOperands, false);
1608 return success();
1609 }
1610
1611 // add(a, add(...)) -> add(a, ...) -- flatten
1612 if (tryFlatteningOperands(op, rewriter))
1613 return success();
1614
1615 // extracts only of add(...) -> add(extract()...)
1616 if (narrowOperationWidth(op, false, rewriter))
1617 return success();
1618
1619 // add(add(x, c1), c2) -> add(x, c1 + c2)
1620 auto addOp = inputs[0].getDefiningOp<comb::AddOp>();
1621 if (addOp && addOp.getInputs().size() == 2 &&
1622 matchPattern(addOp.getInputs()[1], m_ConstantInt(&value2)) &&
1623 inputs.size() == 2 && matchPattern(inputs[1], m_ConstantInt(&value))) {
1624
1625 auto rhs = hw::ConstantOp::create(rewriter, op.getLoc(), value + value2);
1626 replaceOpWithNewOpAndCopyNamehint<AddOp>(
1627 rewriter, op, op.getType(), ArrayRef<Value>{addOp.getInputs()[0], rhs},
1628 /*twoState=*/op.getTwoState() && addOp.getTwoState());
1629 return success();
1630 }
1631
1632 return failure();
1633}
1634
1635OpFoldResult MulOp::fold(FoldAdaptor adaptor) {
1636 if (isOpTriviallyRecursive(*this))
1637 return {};
1638
1639 auto size = getInputs().size();
1640 auto inputs = adaptor.getInputs();
1641
1642 // mul(x) -> x -- noop
1643 if (size == 1u)
1644 return getInputs()[0];
1645
1646 auto width = cast<IntegerType>(getType()).getWidth();
1647 if (width == 0)
1648 return getIntAttr(APInt::getZero(0), getContext());
1649
1650 APInt value(/*numBits=*/width, 1, /*isSigned=*/false);
1651
1652 // mul(x, 0, 1) -> 0 -- annulment
1653 for (auto operand : inputs) {
1654 auto attr = dyn_cast_or_null<IntegerAttr>(operand);
1655 if (!attr)
1656 continue;
1657 value *= attr.getValue();
1658 if (value.isZero())
1659 return getIntAttr(value, getContext());
1660 }
1661
1662 // Constant fold
1663 return constFoldAssociativeOp(inputs, hw::PEO::Mul);
1664}
1665
1666LogicalResult MulOp::canonicalize(MulOp op, PatternRewriter &rewriter) {
1667 if (isOpTriviallyRecursive(op))
1668 return failure();
1669
1670 auto inputs = op.getInputs();
1671 auto size = inputs.size();
1672 assert(size > 1 && "expected 2 or more operands");
1673
1674 APInt value, value2;
1675
1676 // mul(x, c) -> shl(x, log2(c)), where c is a power of two.
1677 if (size == 2 && matchPattern(inputs.back(), m_ConstantInt(&value)) &&
1678 value.isPowerOf2()) {
1679 auto shift = hw::ConstantOp::create(rewriter, op.getLoc(), op.getType(),
1680 value.exactLogBase2());
1681 auto shlOp =
1682 comb::ShlOp::create(rewriter, op.getLoc(), inputs[0], shift, false);
1683
1684 replaceOpWithNewOpAndCopyNamehint<MulOp>(rewriter, op, op.getType(),
1685 ArrayRef<Value>(shlOp), false);
1686 return success();
1687 }
1688
1689 // mul(..., 1) -> mul(...) -- identity
1690 if (matchPattern(inputs.back(), m_ConstantInt(&value)) && value.isOne()) {
1691 replaceOpWithNewOpAndCopyNamehint<MulOp>(rewriter, op, op.getType(),
1692 inputs.drop_back());
1693 return success();
1694 }
1695
1696 // mul(..., c1, c2) -> mul(..., c3) where c3 = c1 * c2 -- constant folding
1697 if (matchPattern(inputs[size - 1], m_ConstantInt(&value)) &&
1698 matchPattern(inputs[size - 2], m_ConstantInt(&value2))) {
1699 auto cst = hw::ConstantOp::create(rewriter, op.getLoc(), value * value2);
1700 SmallVector<Value, 4> newOperands(inputs.drop_back(/*n=*/2));
1701 newOperands.push_back(cst);
1702 replaceOpWithNewOpAndCopyNamehint<MulOp>(rewriter, op, op.getType(),
1703 newOperands);
1704 return success();
1705 }
1706
1707 // mul(a, mul(...)) -> mul(a, ...) -- flatten
1708 if (tryFlatteningOperands(op, rewriter))
1709 return success();
1710
1711 // extracts only of mul(...) -> mul(extract()...)
1712 if (narrowOperationWidth(op, false, rewriter))
1713 return success();
1714
1715 return failure();
1716}
1717
1718template <class Op, bool isSigned>
1719static OpFoldResult foldDiv(Op op, ArrayRef<Attribute> constants) {
1720 if (auto rhsValue = dyn_cast_or_null<IntegerAttr>(constants[1])) {
1721 // divu(x, 1) -> x, divs(x, 1) -> x
1722 if (rhsValue.getValue() == 1)
1723 return op.getLhs();
1724
1725 // If the divisor is zero, do not fold for now.
1726 if (rhsValue.getValue().isZero())
1727 return {};
1728 }
1729
1730 return constFoldBinaryOp(constants, isSigned ? hw::PEO::DivS : hw::PEO::DivU);
1731}
1732
1733OpFoldResult DivUOp::fold(FoldAdaptor adaptor) {
1734 if (isOpTriviallyRecursive(*this))
1735 return {};
1736 return foldDiv<DivUOp, /*isSigned=*/false>(*this, adaptor.getOperands());
1737}
1738
1739OpFoldResult DivSOp::fold(FoldAdaptor adaptor) {
1740 if (isOpTriviallyRecursive(*this))
1741 return {};
1742 return foldDiv<DivSOp, /*isSigned=*/true>(*this, adaptor.getOperands());
1743}
1744
1745template <class Op, bool isSigned>
1746static OpFoldResult foldMod(Op op, ArrayRef<Attribute> constants) {
1747 if (auto rhsValue = dyn_cast_or_null<IntegerAttr>(constants[1])) {
1748 // modu(x, 1) -> 0, mods(x, 1) -> 0
1749 if (rhsValue.getValue() == 1)
1750 return getIntAttr(APInt::getZero(op.getType().getIntOrFloatBitWidth()),
1751 op.getContext());
1752
1753 // If the divisor is zero, do not fold for now.
1754 if (rhsValue.getValue().isZero())
1755 return {};
1756 }
1757
1758 if (auto lhsValue = dyn_cast_or_null<IntegerAttr>(constants[0])) {
1759 // modu(0, x) -> 0, mods(0, x) -> 0
1760 if (lhsValue.getValue().isZero())
1761 return getIntAttr(APInt::getZero(op.getType().getIntOrFloatBitWidth()),
1762 op.getContext());
1763 }
1764
1765 return constFoldBinaryOp(constants, isSigned ? hw::PEO::ModS : hw::PEO::ModU);
1766}
1767
1768OpFoldResult ModUOp::fold(FoldAdaptor adaptor) {
1769 if (isOpTriviallyRecursive(*this))
1770 return {};
1771 return foldMod<ModUOp, /*isSigned=*/false>(*this, adaptor.getOperands());
1772}
1773
1774OpFoldResult ModSOp::fold(FoldAdaptor adaptor) {
1775 if (isOpTriviallyRecursive(*this))
1776 return {};
1777 return foldMod<ModSOp, /*isSigned=*/true>(*this, adaptor.getOperands());
1778}
1779
1780LogicalResult DivUOp::canonicalize(DivUOp op, PatternRewriter &rewriter) {
1781 if (isOpTriviallyRecursive(op) || !op.getTwoState())
1782 return failure();
1783 return convertDivUByPowerOfTwo(op, rewriter);
1784}
1785
1786LogicalResult ModUOp::canonicalize(ModUOp op, PatternRewriter &rewriter) {
1787 if (isOpTriviallyRecursive(op) || !op.getTwoState())
1788 return failure();
1789
1790 return convertModUByPowerOfTwo(op, rewriter);
1791}
1792
1793//===----------------------------------------------------------------------===//
1794// ConcatOp
1795//===----------------------------------------------------------------------===//
1796
1797// Constant folding
1798OpFoldResult ConcatOp::fold(FoldAdaptor adaptor) {
1799 if (isOpTriviallyRecursive(*this))
1800 return {};
1801
1802 if (getNumOperands() == 1)
1803 return getOperand(0);
1804
1805 // If all the operands are constant, we can fold.
1806 for (auto attr : adaptor.getInputs())
1807 if (!attr || !isa<IntegerAttr>(attr))
1808 return {};
1809
1810 // If we got here, we can constant fold.
1811 unsigned resultWidth = getType().getIntOrFloatBitWidth();
1812 APInt result(resultWidth, 0);
1813
1814 unsigned nextInsertion = resultWidth;
1815 // Insert each chunk into the result.
1816 for (auto attr : adaptor.getInputs()) {
1817 auto chunk = cast<IntegerAttr>(attr).getValue();
1818 nextInsertion -= chunk.getBitWidth();
1819 result.insertBits(chunk, nextInsertion);
1820 }
1821
1822 return getIntAttr(result, getContext());
1823}
1824
1825LogicalResult ConcatOp::canonicalize(ConcatOp op, PatternRewriter &rewriter) {
1826 if (isOpTriviallyRecursive(op))
1827 return failure();
1828
1829 auto inputs = op.getInputs();
1830 auto size = inputs.size();
1831 assert(size > 1 && "expected 2 or more operands");
1832
1833 // Holds the not-yet-processed operands in reverse order!
1834 SmallVector<Value, 4> pendingOperands, processedOperands;
1835 bool anyOperandChanged;
1836
1837 auto pushPendingOperands = [&](ValueRange operands) {
1838 auto size = operands.size();
1839 for (size_t i = 0; i != size; ++i)
1840 pendingOperands.push_back(operands[size - 1 - i]);
1841 anyOperandChanged = true;
1842 };
1843 auto replacePrevOperand = [&](Value replacement) {
1844 processedOperands.back() = replacement;
1845 anyOperandChanged = true;
1846 };
1847 pendingOperands.reserve(size);
1848 pushPendingOperands(inputs);
1849 anyOperandChanged = false;
1850
1851 while (!pendingOperands.empty()) {
1852 Value nextOperand = pendingOperands.pop_back_val();
1853
1854 // If an operand to the concat is itself a concat, then we can fold them
1855 // together.
1856 if (auto subConcat = nextOperand.getDefiningOp<ConcatOp>()) {
1857 pushPendingOperands(subConcat->getOperands());
1858 continue;
1859 }
1860
1861 // Check for canonicalization due to neighboring operands.
1862 if (!processedOperands.empty()) {
1863 Value prevOperand = processedOperands.back();
1864
1865 // Merge neighboring constants.
1866 if (auto cst = nextOperand.getDefiningOp<hw::ConstantOp>()) {
1867 if (auto prevCst = prevOperand.getDefiningOp<hw::ConstantOp>()) {
1868 unsigned prevWidth = prevCst.getValue().getBitWidth();
1869 unsigned thisWidth = cst.getValue().getBitWidth();
1870 auto resultCst = cst.getValue().zext(prevWidth + thisWidth);
1871 resultCst |= prevCst.getValue().zext(prevWidth + thisWidth)
1872 << thisWidth;
1873 Value replacement =
1874 hw::ConstantOp::create(rewriter, op.getLoc(), resultCst);
1875 replacePrevOperand(replacement);
1876 continue;
1877 }
1878 }
1879
1880 // If the two operands are the same, turn them into a replicate.
1881 if (nextOperand == prevOperand) {
1882 Value replacement =
1883 rewriter.createOrFold<ReplicateOp>(op.getLoc(), prevOperand, 2);
1884 replacePrevOperand(replacement);
1885 continue;
1886 }
1887
1888 // If this input is a replicate, see if we can fold it with the previous
1889 // one.
1890 if (auto repl = nextOperand.getDefiningOp<ReplicateOp>()) {
1891 // ... x, repl(x, n), ... ==> ..., repl(x, n+1), ...
1892 if (repl.getOperand() == prevOperand) {
1893 Value replacement = rewriter.createOrFold<ReplicateOp>(
1894 op.getLoc(), repl.getOperand(), repl.getMultiple() + 1);
1895 replacePrevOperand(replacement);
1896 continue;
1897 }
1898 // ... repl(x, n), repl(x, m), ... ==> ..., repl(x, n+m), ...
1899 if (auto prevRepl = prevOperand.getDefiningOp<ReplicateOp>()) {
1900 if (prevRepl.getOperand() == repl.getOperand()) {
1901 Value replacement = rewriter.createOrFold<ReplicateOp>(
1902 op.getLoc(), repl.getOperand(),
1903 repl.getMultiple() + prevRepl.getMultiple());
1904 replacePrevOperand(replacement);
1905 continue;
1906 }
1907 }
1908 }
1909
1910 // ... repl(x, n), x, ... ==> ..., repl(x, n+1), ...
1911 if (auto repl = prevOperand.getDefiningOp<ReplicateOp>()) {
1912 if (repl.getOperand() == nextOperand) {
1913 Value replacement = rewriter.createOrFold<ReplicateOp>(
1914 op.getLoc(), nextOperand, repl.getMultiple() + 1);
1915 replacePrevOperand(replacement);
1916 continue;
1917 }
1918 }
1919
1920 // Merge neighboring extracts of neighboring inputs, e.g.
1921 // {A[3], A[2]} -> A[3:2]
1922 if (auto extract = nextOperand.getDefiningOp<ExtractOp>()) {
1923 if (auto prevExtract = prevOperand.getDefiningOp<ExtractOp>()) {
1924 if (extract.getInput() == prevExtract.getInput()) {
1925 auto thisWidth = cast<IntegerType>(extract.getType()).getWidth();
1926 if (prevExtract.getLowBit() == extract.getLowBit() + thisWidth) {
1927 auto prevWidth = prevExtract.getType().getIntOrFloatBitWidth();
1928 auto resType = rewriter.getIntegerType(thisWidth + prevWidth);
1929 Value replacement =
1930 ExtractOp::create(rewriter, op.getLoc(), resType,
1931 extract.getInput(), extract.getLowBit());
1932 replacePrevOperand(replacement);
1933 continue;
1934 }
1935 }
1936 }
1937 }
1938 // Merge neighboring array extracts of neighboring inputs, e.g.
1939 // {Array[4], bitcast(Array[3:2])} -> bitcast(A[4:2])
1940
1941 // This represents a slice of an array.
1942 struct ArraySlice {
1943 Value input;
1944 Value index;
1945 size_t width;
1946 static std::optional<ArraySlice> get(Value value) {
1947 assert(isa<IntegerType>(value.getType()) && "expected integer type");
1948 if (auto arrayGet = value.getDefiningOp<hw::ArrayGetOp>())
1949 return ArraySlice{arrayGet.getInput(), arrayGet.getIndex(), 1};
1950 // array slice op is wrapped with bitcast.
1951 if (auto bitcast = value.getDefiningOp<hw::BitcastOp>())
1952 if (auto arraySlice =
1953 bitcast.getInput().getDefiningOp<hw::ArraySliceOp>())
1954 return ArraySlice{
1955 arraySlice.getInput(), arraySlice.getLowIndex(),
1956 hw::type_cast<hw::ArrayType>(arraySlice.getType())
1957 .getNumElements()};
1958 return std::nullopt;
1959 }
1960 };
1961 if (auto extractOpt = ArraySlice::get(nextOperand)) {
1962 if (auto prevExtractOpt = ArraySlice::get(prevOperand)) {
1963 // Check that two array slices are mergable.
1964 if (prevExtractOpt->index.getType() == extractOpt->index.getType() &&
1965 prevExtractOpt->input == extractOpt->input &&
1966 hw::isOffset(extractOpt->index, prevExtractOpt->index,
1967 extractOpt->width)) {
1968 auto resType = hw::ArrayType::get(
1969 hw::type_cast<hw::ArrayType>(prevExtractOpt->input.getType())
1970 .getElementType(),
1971 extractOpt->width + prevExtractOpt->width);
1972 auto resIntType = rewriter.getIntegerType(hw::getBitWidth(resType));
1973 Value replacement = hw::BitcastOp::create(
1974 rewriter, op.getLoc(), resIntType,
1975 hw::ArraySliceOp::create(rewriter, op.getLoc(), resType,
1976 prevExtractOpt->input,
1977 extractOpt->index));
1978 replacePrevOperand(replacement);
1979 continue;
1980 }
1981 }
1982 }
1983 }
1984
1985 processedOperands.push_back(nextOperand);
1986 }
1987
1988 // Batch-resolve all ExtractOp users of this concat using a prefix-sum array
1989 // and binary search. This avoids the O(M*N) cost of having each ExtractOp
1990 // independently do a linear scan over the concat's operands.
1991 //
1992 // Only do this when:
1993 // - the concat operands are unchanged (if they changed, we'll replace the
1994 // concat below and the extract users will be re-enqueued naturally)
1995 // - there are enough extract users to justify batching (for small counts,
1996 // the per-ExtractOp canonicalization is fine and preserves output order)
1997 constexpr size_t kBatchExtractThreshold = 16;
1998 bool anyExtractsResolved = false;
1999 if (!anyOperandChanged && processedOperands.size() > 1) {
2000 SmallVector<ExtractOp, 8> extractUsers;
2001 for (auto *user : op->getUsers()) {
2002 if (auto extract = dyn_cast<ExtractOp>(user))
2003 extractUsers.push_back(extract);
2004 }
2005
2006 if (extractUsers.size() >= kBatchExtractThreshold) {
2007 // Build prefix-sum of bit widths in LSB-first (reversed) order.
2008 auto concatInputs = op.getInputs();
2009 size_t numConcatOperands = concatInputs.size();
2010 SmallVector<size_t> prefixWidths(numConcatOperands);
2011 size_t cumWidth = 0;
2012 for (size_t i = 0; i < numConcatOperands; ++i) {
2013 // Note: we can just query bitwidth here as is as the ExtractOp above's
2014 // type constraints means this is valid in valid IR.
2015 cumWidth += concatInputs[numConcatOperands - 1 - i]
2016 .getType()
2017 .getIntOrFloatBitWidth();
2018 prefixWidths[i] = cumWidth;
2019 }
2020
2021 // Resolve each extract user.
2022 for (auto extract : extractUsers) {
2023 if (succeeded(extractConcatToConcatExtract(extract, op, rewriter,
2024 prefixWidths)))
2025 anyExtractsResolved = true;
2026 }
2027 }
2028 }
2029
2030 if (processedOperands.size() == 1) {
2031 // If the operands were all the same, we'll reach here with a single
2032 // ReplicateOp.
2033 replaceOpAndCopyNamehint(rewriter, op, processedOperands[0]);
2034 } else if (anyOperandChanged) {
2035 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(rewriter, op, op.getType(),
2036 processedOperands);
2037 } else if (!anyExtractsResolved) {
2038 return failure();
2039 }
2040 return success();
2041}
2042
2043//===----------------------------------------------------------------------===//
2044// MuxOp
2045//===----------------------------------------------------------------------===//
2046
2047OpFoldResult MuxOp::fold(FoldAdaptor adaptor) {
2048 if (isOpTriviallyRecursive(*this))
2049 return {};
2050
2051 // mux (c, b, b) -> b
2052 if (getTrueValue() == getFalseValue() && getTrueValue() != getResult())
2053 return getTrueValue();
2054 if (auto tv = adaptor.getTrueValue())
2055 if (tv == adaptor.getFalseValue())
2056 return tv;
2057
2058 // mux(0, a, b) -> b
2059 // mux(1, a, b) -> a
2060 if (auto pred = dyn_cast_or_null<IntegerAttr>(adaptor.getCond())) {
2061 if (pred.getValue().isZero() && getFalseValue() != getResult())
2062 return getFalseValue();
2063 if (pred.getValue().isOne() && getTrueValue() != getResult())
2064 return getTrueValue();
2065 }
2066
2067 // mux(cond, 1, 0) -> cond
2068 if (getCond().getType() == getTrueValue().getType())
2069 if (auto tv = dyn_cast_or_null<IntegerAttr>(adaptor.getTrueValue()))
2070 if (auto fv = dyn_cast_or_null<IntegerAttr>(adaptor.getFalseValue()))
2071 if (tv.getValue().isOne() && fv.getValue().isZero() &&
2072 hw::getBitWidth(getType()) == 1 && getCond() != getResult())
2073 return getCond();
2074
2075 return {};
2076}
2077
2078/// Check to see if the condition to the specified mux is an equality
2079/// comparison `indexValue` and one or more constants. If so, put the
2080/// constants in the constants vector and return true, otherwise return false.
2081///
2082/// This is part of foldMuxChain.
2083///
2084static bool
2085getMuxChainCondConstant(Value cond, Value indexValue, bool isInverted,
2086 std::function<void(hw::ConstantOp)> constantFn) {
2087 // Handle `idx == 42` and `idx != 42`.
2088 if (auto cmp = cond.getDefiningOp<ICmpOp>()) {
2089 // TODO: We could handle things like "x < 2" as two entries.
2090 auto requiredPredicate =
2091 (isInverted ? ICmpPredicate::eq : ICmpPredicate::ne);
2092 if (cmp.getLhs() == indexValue && cmp.getPredicate() == requiredPredicate) {
2093 if (auto cst = cmp.getRhs().getDefiningOp<hw::ConstantOp>()) {
2094 constantFn(cst);
2095 return true;
2096 }
2097 }
2098 return false;
2099 }
2100
2101 // Handle mux(`idx == 1 || idx == 3`, value, muxchain).
2102 if (auto orOp = cond.getDefiningOp<OrOp>()) {
2103 if (!isInverted)
2104 return false;
2105 for (auto operand : orOp.getOperands())
2106 if (!getMuxChainCondConstant(operand, indexValue, isInverted, constantFn))
2107 return false;
2108 return true;
2109 }
2110
2111 // Handle mux(`idx != 1 && idx != 3`, muxchain, value).
2112 if (auto andOp = cond.getDefiningOp<AndOp>()) {
2113 if (isInverted)
2114 return false;
2115 for (auto operand : andOp.getOperands())
2116 if (!getMuxChainCondConstant(operand, indexValue, isInverted, constantFn))
2117 return false;
2118 return true;
2119 }
2120
2121 return false;
2122}
2123
2124/// Given a mux, check to see if the "on true" value (or "on false" value if
2125/// isFalseSide=true) is a mux tree with the same condition. This allows us
2126/// to turn things like `mux(VAL == 0, A, (mux (VAL == 1), B, C))` into
2127/// `array_get (array_create(A, B, C), VAL)` or a balanced mux tree which is far
2128/// more compact and allows synthesis tools to do more interesting
2129/// optimizations.
2130///
2131/// This returns false if we cannot form the mux tree (or do not want to) and
2132/// returns true if the mux was replaced.
2134 PatternRewriter &rewriter, MuxOp rootMux, bool isFalseSide,
2135 llvm::function_ref<MuxChainWithComparisonFoldingStyle(size_t indexWidth,
2136 size_t numEntries)>
2137 styleFn) {
2138 // Get the index value being compared. Later we check to see if it is
2139 // compared to a constant with the right predicate.
2140 auto rootCmp = rootMux.getCond().getDefiningOp<ICmpOp>();
2141 if (!rootCmp)
2142 return false;
2143 Value indexValue = rootCmp.getLhs();
2144
2145 // Return the value to use if the equality match succeeds.
2146 auto getCaseValue = [&](MuxOp mux) -> Value {
2147 return mux.getOperand(1 + unsigned(!isFalseSide));
2148 };
2149
2150 // Return the value to use if the equality match fails. This is the next
2151 // mux in the sequence or the "otherwise" value.
2152 auto getTreeValue = [&](MuxOp mux) -> Value {
2153 return mux.getOperand(1 + unsigned(isFalseSide));
2154 };
2155
2156 // Start scanning the mux tree to see what we've got. Keep track of the
2157 // constant comparison value and the SSA value to use when equal to it.
2158 SmallVector<Location> locationsFound;
2159 SmallVector<std::pair<hw::ConstantOp, Value>, 4> valuesFound;
2160
2161 /// Extract constants and values into `valuesFound` and return true if this is
2162 /// part of the mux tree, otherwise return false.
2163 auto collectConstantValues = [&](MuxOp mux) -> bool {
2165 mux.getCond(), indexValue, isFalseSide, [&](hw::ConstantOp cst) {
2166 valuesFound.push_back({cst, getCaseValue(mux)});
2167 locationsFound.push_back(mux.getCond().getLoc());
2168 locationsFound.push_back(mux->getLoc());
2169 });
2170 };
2171
2172 // Make sure the root is a correct comparison with a constant.
2173 if (!collectConstantValues(rootMux))
2174 return false;
2175
2176 // Make sure that we're not looking at the intermediate node in a mux tree.
2177 if (rootMux->hasOneUse()) {
2178 if (auto userMux = dyn_cast<MuxOp>(*rootMux->user_begin())) {
2179 if (getTreeValue(userMux) == rootMux.getResult() &&
2180 getMuxChainCondConstant(userMux.getCond(), indexValue, isFalseSide,
2181 [&](hw::ConstantOp cst) {}))
2182 return false;
2183 }
2184 }
2185
2186 // Scan up the tree linearly.
2187 auto nextTreeValue = getTreeValue(rootMux);
2188 while (1) {
2189 auto nextMux = nextTreeValue.getDefiningOp<MuxOp>();
2190 if (!nextMux || !nextMux->hasOneUse())
2191 break;
2192 if (!collectConstantValues(nextMux))
2193 break;
2194 nextTreeValue = getTreeValue(nextMux);
2195 }
2196
2197 auto indexWidth = cast<IntegerType>(indexValue.getType()).getWidth();
2198
2199 if (indexWidth > 20)
2200 return false; // Too big to make a table.
2201
2202 auto foldingStyle = styleFn(indexWidth, valuesFound.size());
2203 if (foldingStyle == MuxChainWithComparisonFoldingStyle::None)
2204 return false;
2205
2206 uint64_t tableSize = 1ULL << indexWidth;
2207
2208 // Ok, we're going to do the transformation, start by building the table
2209 // filled with the "otherwise" value.
2210 SmallVector<Value, 8> table(tableSize, nextTreeValue);
2211
2212 // Fill in entries in the table from the leaf to the root of the expression.
2213 // This ensures that any duplicate matches end up with the ultimate value,
2214 // which is the one closer to the root.
2215 for (auto &elt : llvm::reverse(valuesFound)) {
2216 uint64_t idx = elt.first.getValue().getZExtValue();
2217 assert(idx < table.size() && "constant should be same bitwidth as index");
2218 table[idx] = elt.second;
2219 }
2220
2222 SmallVector<Value> bits;
2223 comb::extractBits(rewriter, indexValue, bits);
2224 auto result = constructMuxTree(rewriter, rootMux->getLoc(), bits, table,
2225 nextTreeValue);
2226 replaceOpAndCopyNamehint(rewriter, rootMux, result);
2227 return true;
2228 }
2229
2231 "unknown folding style");
2232
2233 // The hw.array_create operation has the operand list in unintuitive order
2234 // with a[0] stored as the last element, not the first.
2235 std::reverse(table.begin(), table.end());
2236
2237 // Build the array_create and the array_get.
2238 auto fusedLoc = rewriter.getFusedLoc(locationsFound);
2239 auto array = hw::ArrayCreateOp::create(rewriter, fusedLoc, table);
2240 replaceOpWithNewOpAndCopyNamehint<hw::ArrayGetOp>(rewriter, rootMux, array,
2241 indexValue);
2242 return true;
2243}
2244
2245/// Given a fully associative variadic operation like (a+b+c+d), break the
2246/// expression into two parts, one without the specified operand (e.g.
2247/// `tmp = a+b+d`) and one that combines that into the full expression (e.g.
2248/// `tmp+c`), and return the inner expression.
2249///
2250/// NOTE: This mutates the operation in place if it only has a single user,
2251/// which assumes that user will be removed.
2252///
2253static Value extractOperandFromFullyAssociative(Operation *fullyAssoc,
2254 size_t operandNo,
2255 PatternRewriter &rewriter) {
2256 assert(fullyAssoc->getNumOperands() >= 2 && "cannot split up unary ops");
2257 assert(operandNo < fullyAssoc->getNumOperands() && "Invalid operand #");
2258
2259 // If this expression already has two operands (the common case) no splitting
2260 // is necessary.
2261 if (fullyAssoc->getNumOperands() == 2)
2262 return fullyAssoc->getOperand(operandNo ^ 1);
2263
2264 // If the operation has a single use, mutate it in place.
2265 if (fullyAssoc->hasOneUse()) {
2266 rewriter.modifyOpInPlace(fullyAssoc,
2267 [&]() { fullyAssoc->eraseOperand(operandNo); });
2268 return fullyAssoc->getResult(0);
2269 }
2270
2271 // Form the new operation with the operands that remain.
2272 SmallVector<Value> operands;
2273 operands.append(fullyAssoc->getOperands().begin(),
2274 fullyAssoc->getOperands().begin() + operandNo);
2275 operands.append(fullyAssoc->getOperands().begin() + operandNo + 1,
2276 fullyAssoc->getOperands().end());
2277 Value opWithoutExcluded = createGenericOp(
2278 fullyAssoc->getLoc(), fullyAssoc->getName(), operands, rewriter);
2279 Value excluded = fullyAssoc->getOperand(operandNo);
2280
2281 Value fullResult =
2282 createGenericOp(fullyAssoc->getLoc(), fullyAssoc->getName(),
2283 ArrayRef<Value>{opWithoutExcluded, excluded}, rewriter);
2284 replaceOpAndCopyNamehint(rewriter, fullyAssoc, fullResult);
2285 return opWithoutExcluded;
2286}
2287
2288/// Fold things like `mux(cond, x|y|z|a, a)` -> `(x|y|z)&replicate(cond)|a` and
2289/// `mux(cond, a, x|y|z|a) -> `(x|y|z)&replicate(~cond) | a` (when isTrueOperand
2290/// is true. Return true on successful transformation, false if not.
2291///
2292/// These are various forms of "predicated ops" that can be handled with a
2293/// replicate/and combination.
2294static bool foldCommonMuxValue(MuxOp op, bool isTrueOperand,
2295 PatternRewriter &rewriter) {
2296 // Check to see the operand in question is an operation. If it is a port,
2297 // we can't simplify it.
2298 Operation *subExpr =
2299 (isTrueOperand ? op.getFalseValue() : op.getTrueValue()).getDefiningOp();
2300 if (!subExpr || subExpr->getNumOperands() < 2)
2301 return false;
2302
2303 // If this isn't an operation we can handle, don't spend energy on it.
2304 if (!isa<AndOp, XorOp, OrOp, MuxOp>(subExpr))
2305 return false;
2306
2307 // Check to see if the common value occurs in the operand list for the
2308 // subexpression op. If so, then we can simplify it.
2309 Value commonValue = isTrueOperand ? op.getTrueValue() : op.getFalseValue();
2310 size_t opNo = 0, e = subExpr->getNumOperands();
2311 while (opNo != e && subExpr->getOperand(opNo) != commonValue)
2312 ++opNo;
2313 if (opNo == e)
2314 return false;
2315
2316 // If we got a hit, then go ahead and simplify it!
2317 Value cond = op.getCond();
2318
2319 // `mux(cond, a, mux(cond2, a, b))` -> `mux(cond|cond2, a, b)`
2320 // `mux(cond, a, mux(cond2, b, a))` -> `mux(cond|~cond2, a, b)`
2321 // `mux(cond, mux(cond2, a, b), a)` -> `mux(~cond|cond2, a, b)`
2322 // `mux(cond, mux(cond2, b, a), a)` -> `mux(~cond|~cond2, a, b)`
2323 if (auto subMux = dyn_cast<MuxOp>(subExpr)) {
2324 if (subMux == op)
2325 return false;
2326
2327 Value otherValue;
2328 Value subCond = subMux.getCond();
2329
2330 // Invert th subCond if needed and dig out the 'b' value.
2331 if (subMux.getTrueValue() == commonValue)
2332 otherValue = subMux.getFalseValue();
2333 else if (subMux.getFalseValue() == commonValue) {
2334 otherValue = subMux.getTrueValue();
2335 subCond = createOrFoldNot(rewriter, op.getLoc(), subCond);
2336 } else {
2337 // We can't fold `mux(cond, a, mux(a, x, y))`.
2338 return false;
2339 }
2340
2341 // Invert the outer cond if needed, and combine the mux conditions.
2342 if (!isTrueOperand)
2343 cond = createOrFoldNot(rewriter, op.getLoc(), cond);
2344 cond = rewriter.createOrFold<OrOp>(op.getLoc(), cond, subCond, false);
2345 replaceOpWithNewOpAndCopyNamehint<MuxOp>(rewriter, op, cond, commonValue,
2346 otherValue, op.getTwoState());
2347 return true;
2348 }
2349
2350 // Invert the condition if needed. Or/Xor invert when dealing with
2351 // TrueOperand, And inverts for False operand.
2352 bool isaAndOp = isa<AndOp>(subExpr);
2353 if (isTrueOperand ^ isaAndOp)
2354 cond = createOrFoldNot(rewriter, op.getLoc(), cond);
2355
2356 auto extendedCond =
2357 rewriter.createOrFold<ReplicateOp>(op.getLoc(), op.getType(), cond);
2358
2359 // Cache this information before subExpr is erased by extraction below.
2360 bool isaXorOp = isa<XorOp>(subExpr);
2361 bool isaOrOp = isa<OrOp>(subExpr);
2362
2363 // Handle the fully associative ops, start by pulling out the subexpression
2364 // from a many operand version of the op.
2365 auto restOfAssoc =
2366 extractOperandFromFullyAssociative(subExpr, opNo, rewriter);
2367
2368 // `mux(cond, x|y|z|a, a)` -> `(x|y|z)&replicate(cond) | a`
2369 // `mux(cond, x^y^z^a, a)` -> `(x^y^z)&replicate(cond) ^ a`
2370 if (isaOrOp || isaXorOp) {
2371 auto masked = rewriter.createOrFold<AndOp>(op.getLoc(), extendedCond,
2372 restOfAssoc, false);
2373 if (isaXorOp)
2374 replaceOpWithNewOpAndCopyNamehint<XorOp>(rewriter, op, masked,
2375 commonValue, false);
2376 else
2377 replaceOpWithNewOpAndCopyNamehint<OrOp>(rewriter, op, masked, commonValue,
2378 false);
2379 return true;
2380 }
2381
2382 // `mux(cond, a, x&y&z&a)` -> `((x&y&z)|replicate(cond)) & a`
2383 assert(isaAndOp && "unexpected operation here");
2384 auto masked = rewriter.createOrFold<OrOp>(op.getLoc(), extendedCond,
2385 restOfAssoc, false);
2386 replaceOpWithNewOpAndCopyNamehint<AndOp>(rewriter, op, masked, commonValue,
2387 false);
2388 return true;
2389}
2390
2391/// This function is invoke when we find a mux with true/false operations that
2392/// have the same opcode. Check to see if we can strength reduce the mux by
2393/// applying it to less data by applying this transformation:
2394/// `mux(cond, op(a, b), op(a, c))` -> `op(a, mux(cond, b, c))`
2395static bool foldCommonMuxOperation(MuxOp mux, Operation *trueOp,
2396 Operation *falseOp,
2397 PatternRewriter &rewriter) {
2398 // Right now we only apply to concat.
2399 // TODO: Generalize this to and, or, xor, icmp(!), which all occur in practice
2400 if (!isa<ConcatOp>(trueOp))
2401 return false;
2402
2403 // Decode the operands, looking through recursive concats and replicates.
2404 SmallVector<Value> trueOperands, falseOperands;
2405 getConcatOperands(trueOp->getResult(0), trueOperands);
2406 getConcatOperands(falseOp->getResult(0), falseOperands);
2407
2408 size_t numTrueOperands = trueOperands.size();
2409 size_t numFalseOperands = falseOperands.size();
2410
2411 if (!numTrueOperands || !numFalseOperands ||
2412 (trueOperands.front() != falseOperands.front() &&
2413 trueOperands.back() != falseOperands.back()))
2414 return false;
2415
2416 // Pull all leading shared operands out into their own op if any are common.
2417 if (trueOperands.front() == falseOperands.front()) {
2418 SmallVector<Value> operands;
2419 size_t i;
2420 for (i = 0; i < numTrueOperands; ++i) {
2421 Value trueOperand = trueOperands[i];
2422 if (trueOperand == falseOperands[i])
2423 operands.push_back(trueOperand);
2424 else
2425 break;
2426 }
2427 if (i == numTrueOperands) {
2428 // Selecting between distinct, but lexically identical, concats.
2429 replaceOpAndCopyNamehint(rewriter, mux, trueOp->getResult(0));
2430 return true;
2431 }
2432
2433 Value sharedMSB;
2434 if (llvm::all_of(operands, [&](Value v) { return v == operands.front(); }))
2435 sharedMSB = rewriter.createOrFold<ReplicateOp>(
2436 mux->getLoc(), operands.front(), operands.size());
2437 else
2438 sharedMSB = rewriter.createOrFold<ConcatOp>(mux->getLoc(), operands);
2439 operands.clear();
2440
2441 // Get a concat of the LSB's on each side.
2442 operands.append(trueOperands.begin() + i, trueOperands.end());
2443 Value trueLSB = rewriter.createOrFold<ConcatOp>(trueOp->getLoc(), operands);
2444 operands.clear();
2445 operands.append(falseOperands.begin() + i, falseOperands.end());
2446 Value falseLSB =
2447 rewriter.createOrFold<ConcatOp>(falseOp->getLoc(), operands);
2448 // Merge the LSBs with a new mux and concat the MSB with the LSB to be
2449 // done.
2450 Value lsb = rewriter.createOrFold<MuxOp>(
2451 mux->getLoc(), mux.getCond(), trueLSB, falseLSB, mux.getTwoState());
2452 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(rewriter, mux, sharedMSB, lsb);
2453 return true;
2454 }
2455
2456 // If trailing operands match, try to commonize them.
2457 if (trueOperands.back() == falseOperands.back()) {
2458 SmallVector<Value> operands;
2459 size_t i;
2460 for (i = 0;; ++i) {
2461 Value trueOperand = trueOperands[numTrueOperands - i - 1];
2462 if (trueOperand == falseOperands[numFalseOperands - i - 1])
2463 operands.push_back(trueOperand);
2464 else
2465 break;
2466 }
2467 std::reverse(operands.begin(), operands.end());
2468 Value sharedLSB = rewriter.createOrFold<ConcatOp>(mux->getLoc(), operands);
2469 operands.clear();
2470
2471 // Get a concat of the MSB's on each side.
2472 operands.append(trueOperands.begin(), trueOperands.end() - i);
2473 Value trueMSB = rewriter.createOrFold<ConcatOp>(trueOp->getLoc(), operands);
2474 operands.clear();
2475 operands.append(falseOperands.begin(), falseOperands.end() - i);
2476 Value falseMSB =
2477 rewriter.createOrFold<ConcatOp>(falseOp->getLoc(), operands);
2478 // Merge the MSBs with a new mux and concat the MSB with the LSB to be done.
2479 Value msb = rewriter.createOrFold<MuxOp>(
2480 mux->getLoc(), mux.getCond(), trueMSB, falseMSB, mux.getTwoState());
2481 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(rewriter, mux, msb, sharedLSB);
2482 return true;
2483 }
2484
2485 return false;
2486}
2487
2488// If both arguments of the mux are arrays with the same elements, sink the
2489// mux and return a uniform array initializing all elements to it.
2490static bool foldMuxOfUniformArrays(MuxOp op, PatternRewriter &rewriter) {
2491 auto trueVec = op.getTrueValue().getDefiningOp<hw::ArrayCreateOp>();
2492 auto falseVec = op.getFalseValue().getDefiningOp<hw::ArrayCreateOp>();
2493 if (!trueVec || !falseVec)
2494 return false;
2495 if (!trueVec.isUniform() || !falseVec.isUniform())
2496 return false;
2497
2498 auto mux = MuxOp::create(rewriter, op.getLoc(), op.getCond(),
2499 trueVec.getUniformElement(),
2500 falseVec.getUniformElement(), op.getTwoState());
2501
2502 SmallVector<Value> values(trueVec.getInputs().size(), mux);
2503 rewriter.replaceOpWithNewOp<hw::ArrayCreateOp>(op, values);
2504 return true;
2505}
2506
2507/// If the mux condition is an operand to the op defining its true or false
2508/// value, replace the condition with 1 or 0.
2509static bool assumeMuxCondInOperand(Value muxCond, Value muxValue,
2510 bool constCond, PatternRewriter &rewriter) {
2511 if (!muxValue.hasOneUse())
2512 return false;
2513 auto *op = muxValue.getDefiningOp();
2514 if (!op || !isa_and_nonnull<CombDialect>(op->getDialect()))
2515 return false;
2516 if (!llvm::is_contained(op->getOperands(), muxCond))
2517 return false;
2518 OpBuilder::InsertionGuard guard(rewriter);
2519 rewriter.setInsertionPoint(op);
2520 auto condValue =
2521 hw::ConstantOp::create(rewriter, muxCond.getLoc(), APInt(1, constCond));
2522 rewriter.modifyOpInPlace(op, [&] {
2523 for (auto &use : op->getOpOperands())
2524 if (use.get() == muxCond)
2525 use.set(condValue);
2526 });
2527 return true;
2528}
2529
2530namespace {
2531struct MuxRewriter : public mlir::OpRewritePattern<MuxOp> {
2532 using OpRewritePattern::OpRewritePattern;
2533
2534 LogicalResult matchAndRewrite(MuxOp op,
2535 PatternRewriter &rewriter) const override;
2536};
2537
2539foldToArrayCreateOnlyWhenDense(size_t indexWidth, size_t numEntries) {
2540 // If the array is greater that 9 bits, it will take over 512 elements and
2541 // it will be too large for a single expression.
2542 if (indexWidth >= 9 || numEntries < 3)
2544
2545 // Next we need to see if the values are dense-ish. We don't want to have
2546 // a tremendous number of replicated entries in the array. Some sparsity is
2547 // ok though, so we require the table to be at least 5/8 utilized.
2548 uint64_t tableSize = 1ULL << indexWidth;
2549 if (numEntries >= tableSize * 5 / 8)
2552}
2553
2554LogicalResult MuxRewriter::matchAndRewrite(MuxOp op,
2555 PatternRewriter &rewriter) const {
2556 if (isOpTriviallyRecursive(op))
2557 return failure();
2558
2559 bool isSignlessInt = false;
2560 if (auto intType = dyn_cast<IntegerType>(op.getType()))
2561 isSignlessInt = intType.isSignless();
2562
2563 // If the op has a SV attribute, don't optimize it.
2564 if (hasSVAttributes(op))
2565 return failure();
2566 APInt value;
2567
2568 if (matchPattern(op.getTrueValue(), m_ConstantInt(&value)) && isSignlessInt) {
2569 if (value.getBitWidth() == 1) {
2570 // mux(a, 0, b) -> and(~a, b) for single-bit values.
2571 if (value.isZero()) {
2572 auto notCond = createOrFoldNot(rewriter, op.getLoc(), op.getCond());
2573 replaceOpWithNewOpAndCopyNamehint<AndOp>(rewriter, op, notCond,
2574 op.getFalseValue(), false);
2575 return success();
2576 }
2577
2578 // mux(a, 1, b) -> or(a, b) for single-bit values.
2579 replaceOpWithNewOpAndCopyNamehint<OrOp>(rewriter, op, op.getCond(),
2580 op.getFalseValue(), false);
2581 return success();
2582 }
2583
2584 // Check for mux of two constants. There are many ways to simplify them.
2585 APInt value2;
2586 if (matchPattern(op.getFalseValue(), m_ConstantInt(&value2))) {
2587 // When both inputs are constants and differ by only one bit, we can
2588 // simplify by splitting the mux into up to three contiguous chunks: one
2589 // for the differing bit and up to two for the bits that are the same.
2590 // E.g. mux(a, 3'h2, 0) -> concat(0, mux(a, 1, 0), 0) -> concat(0, a, 0)
2591 APInt xorValue = value ^ value2;
2592 if (xorValue.isPowerOf2()) {
2593 unsigned leadingZeros = xorValue.countLeadingZeros();
2594 unsigned trailingZeros = value.getBitWidth() - leadingZeros - 1;
2595 SmallVector<Value, 3> operands;
2596
2597 // Concat operands go from MSB to LSB, so we handle chunks in reverse
2598 // order of bit indexes.
2599 // For the chunks that are identical (i.e. correspond to 0s in
2600 // xorValue), we can extract directly from either input value, and we
2601 // arbitrarily pick the trueValue().
2602
2603 if (leadingZeros > 0)
2604 operands.push_back(rewriter.createOrFold<ExtractOp>(
2605 op.getLoc(), op.getTrueValue(), trailingZeros + 1, leadingZeros));
2606
2607 // Handle the differing bit, which should simplify into either cond or
2608 // ~cond.
2609 auto v1 = rewriter.createOrFold<ExtractOp>(
2610 op.getLoc(), op.getTrueValue(), trailingZeros, 1);
2611 auto v2 = rewriter.createOrFold<ExtractOp>(
2612 op.getLoc(), op.getFalseValue(), trailingZeros, 1);
2613 operands.push_back(rewriter.createOrFold<MuxOp>(
2614 op.getLoc(), op.getCond(), v1, v2, false));
2615
2616 if (trailingZeros > 0)
2617 operands.push_back(rewriter.createOrFold<ExtractOp>(
2618 op.getLoc(), op.getTrueValue(), 0, trailingZeros));
2619
2620 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(rewriter, op, op.getType(),
2621 operands);
2622 return success();
2623 }
2624
2625 // If the true value is all ones and the false is all zeros then we have a
2626 // replicate pattern.
2627 if (value.isAllOnes() && value2.isZero()) {
2628 replaceOpWithNewOpAndCopyNamehint<ReplicateOp>(
2629 rewriter, op, op.getType(), op.getCond());
2630 return success();
2631 }
2632 }
2633 }
2634
2635 if (matchPattern(op.getFalseValue(), m_ConstantInt(&value)) &&
2636 isSignlessInt && value.getBitWidth() == 1) {
2637 // mux(a, b, 0) -> and(a, b) for single-bit values.
2638 if (value.isZero()) {
2639 replaceOpWithNewOpAndCopyNamehint<AndOp>(rewriter, op, op.getCond(),
2640 op.getTrueValue(), false);
2641 return success();
2642 }
2643
2644 // mux(a, b, 1) -> or(~a, b) for single-bit values.
2645 // falseValue() is known to be a single-bit 1, which we can use for
2646 // the 1 in the representation of ~ using xor.
2647 auto notCond = rewriter.createOrFold<XorOp>(op.getLoc(), op.getCond(),
2648 op.getFalseValue(), false);
2649 replaceOpWithNewOpAndCopyNamehint<OrOp>(rewriter, op, notCond,
2650 op.getTrueValue(), false);
2651 return success();
2652 }
2653
2654 // mux(!a, b, c) -> mux(a, c, b)
2655 Value subExpr;
2656 Operation *condOp = op.getCond().getDefiningOp();
2657 if (condOp && matchPattern(condOp, m_Complement(m_Any(&subExpr))) &&
2658 op.getTwoState()) {
2659 replaceOpWithNewOpAndCopyNamehint<MuxOp>(rewriter, op, op.getType(),
2660 subExpr, op.getFalseValue(),
2661 op.getTrueValue(), true);
2662 return success();
2663 }
2664
2665 // Same but with Demorgan's law.
2666 // mux(and(~a, ~b, ~c), x, y) -> mux(or(a, b, c), y, x)
2667 // mux(or(~a, ~b, ~c), x, y) -> mux(and(a, b, c), y, x)
2668 if (condOp && condOp->hasOneUse()) {
2669 SmallVector<Value> invertedOperands;
2670
2671 /// Scan all the operands to see if they are complemented. If so, build a
2672 /// vector of them and return true, otherwise return false.
2673 auto getInvertedOperands = [&]() -> bool {
2674 for (Value operand : condOp->getOperands()) {
2675 if (matchPattern(operand, m_Complement(m_Any(&subExpr))))
2676 invertedOperands.push_back(subExpr);
2677 else
2678 return false;
2679 }
2680 return true;
2681 };
2682
2683 if (isa<AndOp>(condOp) && getInvertedOperands()) {
2684 auto newOr =
2685 rewriter.createOrFold<OrOp>(op.getLoc(), invertedOperands, false);
2686 replaceOpWithNewOpAndCopyNamehint<MuxOp>(
2687 rewriter, op, newOr, op.getFalseValue(), op.getTrueValue(),
2688 op.getTwoState());
2689 return success();
2690 }
2691 if (isa<OrOp>(condOp) && getInvertedOperands()) {
2692 auto newAnd =
2693 rewriter.createOrFold<AndOp>(op.getLoc(), invertedOperands, false);
2694 replaceOpWithNewOpAndCopyNamehint<MuxOp>(
2695 rewriter, op, newAnd, op.getFalseValue(), op.getTrueValue(),
2696 op.getTwoState());
2697 return success();
2698 }
2699 }
2700
2701 if (auto falseMux = op.getFalseValue().getDefiningOp<MuxOp>();
2702 falseMux && falseMux != op) {
2703 // mux(selector, x, mux(selector, y, z) = mux(selector, x, z)
2704 if (op.getCond() == falseMux.getCond() &&
2705 falseMux.getFalseValue() != falseMux) {
2706 replaceOpWithNewOpAndCopyNamehint<MuxOp>(
2707 rewriter, op, op.getCond(), op.getTrueValue(),
2708 falseMux.getFalseValue(), op.getTwoStateAttr());
2709 return success();
2710 }
2711
2712 // Check to see if we can fold a mux tree into an array_create/get pair.
2713 if (foldMuxChainWithComparison(rewriter, op, /*isFalse*/ true,
2714 foldToArrayCreateOnlyWhenDense))
2715 return success();
2716 }
2717
2718 if (auto trueMux = op.getTrueValue().getDefiningOp<MuxOp>();
2719 trueMux && trueMux != op) {
2720 // mux(selector, mux(selector, a, b), c) = mux(selector, a, c)
2721 if (op.getCond() == trueMux.getCond()) {
2722 replaceOpWithNewOpAndCopyNamehint<MuxOp>(
2723 rewriter, op, op.getCond(), trueMux.getTrueValue(),
2724 op.getFalseValue(), op.getTwoStateAttr());
2725 return success();
2726 }
2727
2728 // Check to see if we can fold a mux tree into an array_create/get pair.
2729 if (foldMuxChainWithComparison(rewriter, op, /*isFalseSide*/ false,
2730 foldToArrayCreateOnlyWhenDense))
2731 return success();
2732 }
2733
2734 // mux(c1, mux(c2, a, b), mux(c2, a, c)) -> mux(c2, a, mux(c1, b, c))
2735 if (auto trueMux = dyn_cast_or_null<MuxOp>(op.getTrueValue().getDefiningOp()),
2736 falseMux = dyn_cast_or_null<MuxOp>(op.getFalseValue().getDefiningOp());
2737 trueMux && falseMux && trueMux.getCond() == falseMux.getCond() &&
2738 trueMux.getTrueValue() == falseMux.getTrueValue() && trueMux != op &&
2739 falseMux != op) {
2740 auto subMux = MuxOp::create(
2741 rewriter, rewriter.getFusedLoc({trueMux.getLoc(), falseMux.getLoc()}),
2742 op.getCond(), trueMux.getFalseValue(), falseMux.getFalseValue());
2743 replaceOpWithNewOpAndCopyNamehint<MuxOp>(rewriter, op, trueMux.getCond(),
2744 trueMux.getTrueValue(), subMux,
2745 op.getTwoStateAttr());
2746 return success();
2747 }
2748
2749 // mux(c1, mux(c2, a, b), mux(c2, c, b)) -> mux(c2, mux(c1, a, c), b)
2750 if (auto trueMux = dyn_cast_or_null<MuxOp>(op.getTrueValue().getDefiningOp()),
2751 falseMux = dyn_cast_or_null<MuxOp>(op.getFalseValue().getDefiningOp());
2752 trueMux && falseMux && trueMux.getCond() == falseMux.getCond() &&
2753 trueMux.getFalseValue() == falseMux.getFalseValue() && trueMux != op &&
2754 falseMux != op) {
2755 auto subMux = MuxOp::create(
2756 rewriter, rewriter.getFusedLoc({trueMux.getLoc(), falseMux.getLoc()}),
2757 op.getCond(), trueMux.getTrueValue(), falseMux.getTrueValue());
2758 replaceOpWithNewOpAndCopyNamehint<MuxOp>(rewriter, op, trueMux.getCond(),
2759 subMux, trueMux.getFalseValue(),
2760 op.getTwoStateAttr());
2761 return success();
2762 }
2763
2764 // mux(c1, mux(c2, a, b), mux(c3, a, b)) -> mux(mux(c1, c2, c3), a, b)
2765 if (auto trueMux = dyn_cast_or_null<MuxOp>(op.getTrueValue().getDefiningOp()),
2766 falseMux = dyn_cast_or_null<MuxOp>(op.getFalseValue().getDefiningOp());
2767 trueMux && falseMux &&
2768 trueMux.getTrueValue() == falseMux.getTrueValue() &&
2769 trueMux.getFalseValue() == falseMux.getFalseValue() && trueMux != op &&
2770 falseMux != op) {
2771 auto subMux =
2772 MuxOp::create(rewriter,
2773 rewriter.getFusedLoc(
2774 {op.getLoc(), trueMux.getLoc(), falseMux.getLoc()}),
2775 op.getCond(), trueMux.getCond(), falseMux.getCond());
2776 replaceOpWithNewOpAndCopyNamehint<MuxOp>(
2777 rewriter, op, subMux, trueMux.getTrueValue(), trueMux.getFalseValue(),
2778 op.getTwoStateAttr());
2779 return success();
2780 }
2781
2782 // mux(cond, x|y|z|a, a) -> (x|y|z)&replicate(cond) | a
2783 if (foldCommonMuxValue(op, false, rewriter))
2784 return success();
2785 // mux(cond, a, x|y|z|a) -> (x|y|z)&replicate(~cond) | a
2786 if (foldCommonMuxValue(op, true, rewriter))
2787 return success();
2788
2789 // `mux(cond, op(a, b), op(a, c))` -> `op(a, mux(cond, b, c))`
2790 if (Operation *trueOp = op.getTrueValue().getDefiningOp())
2791 if (Operation *falseOp = op.getFalseValue().getDefiningOp())
2792 if (trueOp->getName() == falseOp->getName())
2793 if (foldCommonMuxOperation(op, trueOp, falseOp, rewriter))
2794 return success();
2795
2796 // extracts only of mux(...) -> mux(extract()...)
2797 if (narrowOperationWidth(op, true, rewriter))
2798 return success();
2799
2800 // mux(cond, repl(n, a1), repl(n, a2)) -> repl(n, mux(cond, a1, a2))
2801 if (foldMuxOfUniformArrays(op, rewriter))
2802 return success();
2803
2804 // mux(cond, opA(cond), opB(cond)) -> mux(cond, opA(1), opB(0))
2805 if (op.getTrueValue().getDefiningOp() &&
2806 op.getTrueValue().getDefiningOp() != op)
2807 if (assumeMuxCondInOperand(op.getCond(), op.getTrueValue(), true, rewriter))
2808 return success();
2809 if (op.getFalseValue().getDefiningOp() &&
2810 op.getFalseValue().getDefiningOp() != op)
2811
2812 if (assumeMuxCondInOperand(op.getCond(), op.getFalseValue(), false,
2813 rewriter))
2814 return success();
2815
2816 return failure();
2817}
2818
2819static bool foldArrayOfMuxes(hw::ArrayCreateOp op, PatternRewriter &rewriter) {
2820 // Do not fold uniform or singleton arrays to avoid duplicating muxes.
2821 if (op.getInputs().empty() || op.isUniform())
2822 return false;
2823 auto inputs = op.getInputs();
2824 if (inputs.size() <= 1)
2825 return false;
2826
2827 // Check the operands to the array create. Ensure all of them are the
2828 // same op with the same number of operands.
2829 auto first = inputs[0].getDefiningOp<comb::MuxOp>();
2830 if (!first || hasSVAttributes(first))
2831 return false;
2832
2833 // Check whether all operands are muxes with the same condition.
2834 for (size_t i = 1, n = inputs.size(); i < n; ++i) {
2835 auto input = inputs[i].getDefiningOp<comb::MuxOp>();
2836 if (!input || first.getCond() != input.getCond())
2837 return false;
2838 }
2839
2840 // Collect the true and the false branches into arrays.
2841 SmallVector<Value> trues{first.getTrueValue()};
2842 SmallVector<Value> falses{first.getFalseValue()};
2843 SmallVector<Location> locs{first->getLoc()};
2844 bool isTwoState = true;
2845 for (size_t i = 1, n = inputs.size(); i < n; ++i) {
2846 auto input = inputs[i].getDefiningOp<comb::MuxOp>();
2847 trues.push_back(input.getTrueValue());
2848 falses.push_back(input.getFalseValue());
2849 locs.push_back(input->getLoc());
2850 if (!input.getTwoState())
2851 isTwoState = false;
2852 }
2853
2854 // Define the location of the array create as the aggregate of all muxes.
2855 auto loc = FusedLoc::get(op.getContext(), locs);
2856
2857 // Replace the create with an aggregate operation. Push the create op
2858 // into the operands of the aggregate operation.
2859 auto arrayTy = op.getType();
2860 auto trueValues = hw::ArrayCreateOp::create(rewriter, loc, arrayTy, trues);
2861 auto falseValues = hw::ArrayCreateOp::create(rewriter, loc, arrayTy, falses);
2862 rewriter.replaceOpWithNewOp<comb::MuxOp>(op, arrayTy, first.getCond(),
2863 trueValues, falseValues, isTwoState);
2864 return true;
2865}
2866
2867struct ArrayRewriter : public mlir::OpRewritePattern<hw::ArrayCreateOp> {
2868 using OpRewritePattern::OpRewritePattern;
2869
2870 LogicalResult matchAndRewrite(hw::ArrayCreateOp op,
2871 PatternRewriter &rewriter) const override {
2872 if (foldArrayOfMuxes(op, rewriter))
2873 return success();
2874 return failure();
2875 }
2876};
2877
2878} // namespace
2879
2880void MuxOp::getCanonicalizationPatterns(RewritePatternSet &results,
2881 MLIRContext *context) {
2882 results.insert<MuxRewriter, ArrayRewriter>(context);
2883}
2884
2885//===----------------------------------------------------------------------===//
2886// ICmpOp
2887//===----------------------------------------------------------------------===//
2888
2889// Calculate the result of a comparison when the LHS and RHS are both
2890// constants.
2891static bool applyCmpPredicate(ICmpPredicate predicate, const APInt &lhs,
2892 const APInt &rhs) {
2893 switch (predicate) {
2894 case ICmpPredicate::eq:
2895 return lhs.eq(rhs);
2896 case ICmpPredicate::ne:
2897 return lhs.ne(rhs);
2898 case ICmpPredicate::slt:
2899 return lhs.slt(rhs);
2900 case ICmpPredicate::sle:
2901 return lhs.sle(rhs);
2902 case ICmpPredicate::sgt:
2903 return lhs.sgt(rhs);
2904 case ICmpPredicate::sge:
2905 return lhs.sge(rhs);
2906 case ICmpPredicate::ult:
2907 return lhs.ult(rhs);
2908 case ICmpPredicate::ule:
2909 return lhs.ule(rhs);
2910 case ICmpPredicate::ugt:
2911 return lhs.ugt(rhs);
2912 case ICmpPredicate::uge:
2913 return lhs.uge(rhs);
2914 case ICmpPredicate::ceq:
2915 return lhs.eq(rhs);
2916 case ICmpPredicate::cne:
2917 return lhs.ne(rhs);
2918 case ICmpPredicate::weq:
2919 return lhs.eq(rhs);
2920 case ICmpPredicate::wne:
2921 return lhs.ne(rhs);
2922 }
2923 llvm_unreachable("unknown comparison predicate");
2924}
2925
2926// Returns the result of applying the predicate when the LHS and RHS are the
2927// exact same value.
2928static bool applyCmpPredicateToEqualOperands(ICmpPredicate predicate) {
2929 switch (predicate) {
2930 case ICmpPredicate::eq:
2931 case ICmpPredicate::sle:
2932 case ICmpPredicate::sge:
2933 case ICmpPredicate::ule:
2934 case ICmpPredicate::uge:
2935 case ICmpPredicate::ceq:
2936 case ICmpPredicate::weq:
2937 return true;
2938 case ICmpPredicate::ne:
2939 case ICmpPredicate::slt:
2940 case ICmpPredicate::sgt:
2941 case ICmpPredicate::ult:
2942 case ICmpPredicate::ugt:
2943 case ICmpPredicate::cne:
2944 case ICmpPredicate::wne:
2945 return false;
2946 }
2947 llvm_unreachable("unknown comparison predicate");
2948}
2949
2950OpFoldResult ICmpOp::fold(FoldAdaptor adaptor) {
2951 // gt a, a -> false
2952 // gte a, a -> true
2953 if (getLhs() == getRhs()) {
2954 auto val = applyCmpPredicateToEqualOperands(getPredicate());
2955 return IntegerAttr::get(getType(), val);
2956 }
2957
2958 // gt 1, 2 -> false
2959 if (auto lhs = dyn_cast_or_null<IntegerAttr>(adaptor.getLhs())) {
2960 if (auto rhs = dyn_cast_or_null<IntegerAttr>(adaptor.getRhs())) {
2961 auto val =
2962 applyCmpPredicate(getPredicate(), lhs.getValue(), rhs.getValue());
2963 return IntegerAttr::get(getType(), val);
2964 }
2965 }
2966 return {};
2967}
2968
2969// Given a range of operands, computes the number of matching prefix and
2970// suffix elements. This does not perform cross-element matching.
2971template <typename Range>
2972static size_t computeCommonPrefixLength(const Range &a, const Range &b) {
2973 size_t commonPrefixLength = 0;
2974 auto ia = a.begin();
2975 auto ib = b.begin();
2976
2977 for (; ia != a.end() && ib != b.end(); ia++, ib++, commonPrefixLength++) {
2978 if (*ia != *ib) {
2979 break;
2980 }
2981 }
2982
2983 return commonPrefixLength;
2984}
2985
2986static size_t getTotalWidth(ArrayRef<Value> operands) {
2987 size_t totalWidth = 0;
2988 for (auto operand : operands) {
2989 // getIntOrFloatBitWidth should never raise, since all arguments to
2990 // ConcatOp are integers.
2991 ssize_t width = operand.getType().getIntOrFloatBitWidth();
2992 assert(width >= 0);
2993 totalWidth += width;
2994 }
2995 return totalWidth;
2996}
2997
2998/// Reduce the strength icmp(concat(...), concat(...)) by doing a element-wise
2999/// comparison on common prefix and suffixes. Returns success() if a rewriting
3000/// happens. This handles both concat and replicate.
3001static LogicalResult matchAndRewriteCompareConcat(ICmpOp op, Operation *lhs,
3002 Operation *rhs,
3003 PatternRewriter &rewriter) {
3004 // It is safe to assume that [{lhsOperands, rhsOperands}.size() > 0] and
3005 // all elements have non-zero length. Both these invariants are verified
3006 // by the ConcatOp verifier.
3007 SmallVector<Value> lhsOperands, rhsOperands;
3008 getConcatOperands(lhs->getResult(0), lhsOperands);
3009 getConcatOperands(rhs->getResult(0), rhsOperands);
3010 ArrayRef<Value> lhsOperandsRef = lhsOperands, rhsOperandsRef = rhsOperands;
3011
3012 auto formCatOrReplicate = [&](Location loc,
3013 ArrayRef<Value> operands) -> Value {
3014 assert(!operands.empty());
3015 Value sameElement = operands[0];
3016 for (size_t i = 1, e = operands.size(); i != e && sameElement; ++i)
3017 if (sameElement != operands[i])
3018 sameElement = Value();
3019 if (sameElement)
3020 return rewriter.createOrFold<ReplicateOp>(loc, sameElement,
3021 operands.size());
3022 return rewriter.createOrFold<ConcatOp>(loc, operands);
3023 };
3024
3025 auto replaceWith = [&](ICmpPredicate predicate, Value lhs,
3026 Value rhs) -> LogicalResult {
3027 replaceOpWithNewOpAndCopyNamehint<ICmpOp>(rewriter, op, predicate, lhs, rhs,
3028 op.getTwoState());
3029 return success();
3030 };
3031
3032 size_t commonPrefixLength =
3033 computeCommonPrefixLength(lhsOperands, rhsOperands);
3034 if (commonPrefixLength == lhsOperands.size()) {
3035 // cat(a, b, c) == cat(a, b, c) -> 1
3036 bool result = applyCmpPredicateToEqualOperands(op.getPredicate());
3037 replaceOpWithNewOpAndCopyNamehint<hw::ConstantOp>(rewriter, op,
3038 APInt(1, result));
3039 return success();
3040 }
3041
3042 size_t commonSuffixLength = computeCommonPrefixLength(
3043 llvm::reverse(lhsOperandsRef), llvm::reverse(rhsOperandsRef));
3044
3045 size_t commonPrefixTotalWidth =
3046 getTotalWidth(lhsOperandsRef.take_front(commonPrefixLength));
3047 size_t commonSuffixTotalWidth =
3048 getTotalWidth(lhsOperandsRef.take_back(commonSuffixLength));
3049 auto lhsOnly = lhsOperandsRef.drop_front(commonPrefixLength)
3050 .drop_back(commonSuffixLength);
3051 auto rhsOnly = rhsOperandsRef.drop_front(commonPrefixLength)
3052 .drop_back(commonSuffixLength);
3053
3054 auto replaceWithoutReplicatingSignBit = [&]() {
3055 auto newLhs = formCatOrReplicate(lhs->getLoc(), lhsOnly);
3056 auto newRhs = formCatOrReplicate(rhs->getLoc(), rhsOnly);
3057 return replaceWith(op.getPredicate(), newLhs, newRhs);
3058 };
3059
3060 auto replaceWithReplicatingSignBit = [&]() {
3061 auto firstNonEmptyValue = lhsOperands[0];
3062 auto firstNonEmptyElemWidth =
3063 firstNonEmptyValue.getType().getIntOrFloatBitWidth();
3064 Value signBit = rewriter.createOrFold<ExtractOp>(
3065 op.getLoc(), firstNonEmptyValue, firstNonEmptyElemWidth - 1, 1);
3066
3067 auto newLhs = ConcatOp::create(rewriter, lhs->getLoc(), signBit, lhsOnly);
3068 auto newRhs = ConcatOp::create(rewriter, rhs->getLoc(), signBit, rhsOnly);
3069 return replaceWith(op.getPredicate(), newLhs, newRhs);
3070 };
3071
3072 if (ICmpOp::isPredicateSigned(op.getPredicate())) {
3073 // scmp(cat(..x, b), cat(..y, b)) == scmp(cat(..x), cat(..y))
3074 if (commonPrefixTotalWidth == 0 && commonSuffixTotalWidth > 0)
3075 return replaceWithoutReplicatingSignBit();
3076
3077 // scmp(cat(a, ..x, b), cat(a, ..y, b)) == scmp(cat(sgn(a), ..x),
3078 // cat(sgn(b), ..y)) Note that we cannot perform this optimization if
3079 // [width(b) = 0 && width(a) <= 1]. since that common prefix is the sign
3080 // bit. Doing the rewrite can result in an infinite loop.
3081 if (commonPrefixTotalWidth > 1 || commonSuffixTotalWidth > 0)
3082 return replaceWithReplicatingSignBit();
3083
3084 } else if (commonPrefixTotalWidth > 0 || commonSuffixTotalWidth > 0) {
3085 // ucmp(cat(a, ..x, b), cat(a, ..y, b)) = ucmp(cat(..x), cat(..y))
3086 return replaceWithoutReplicatingSignBit();
3087 }
3088
3089 return failure();
3090}
3091
3092/// Given an equality comparison with a constant value and some operand that has
3093/// known bits, simplify the comparison to check only the unknown bits of the
3094/// input.
3095///
3096/// One simple example of this is that `concat(0, stuff) == 0` can be simplified
3097/// to `stuff == 0`, or `and(x, 3) == 0` can be simplified to
3098/// `extract x[1:0] == 0`
3100 ICmpOp cmpOp, const KnownBits &bitAnalysis, const APInt &rhsCst,
3101 PatternRewriter &rewriter) {
3102
3103 // If any of the known bits disagree with any of the comparison bits, then
3104 // we can constant fold this comparison right away.
3105 APInt bitsKnown = bitAnalysis.Zero | bitAnalysis.One;
3106 if ((bitsKnown & rhsCst) != bitAnalysis.One) {
3107 // If we discover a mismatch then we know an "eq" comparison is false
3108 // and a "ne" comparison is true!
3109 bool result = cmpOp.getPredicate() == ICmpPredicate::ne;
3110 replaceOpWithNewOpAndCopyNamehint<hw::ConstantOp>(rewriter, cmpOp,
3111 APInt(1, result));
3112 return;
3113 }
3114
3115 // Check to see if we can prove the result entirely of the comparison (in
3116 // which we bail out early), otherwise build a list of values to concat and a
3117 // smaller constant to compare against.
3118 SmallVector<Value> newConcatOperands;
3119 auto newConstant = APInt::getZeroWidth();
3120
3121 // Ok, some (maybe all) bits are known and some others may be unknown.
3122 // Extract out segments of the operand and compare against the
3123 // corresponding bits.
3124 unsigned knownMSB = bitsKnown.countLeadingOnes();
3125
3126 Value operand = cmpOp.getLhs();
3127
3128 // Ok, some bits are known but others are not. Extract out sequences of
3129 // bits that are unknown and compare just those bits. We work from MSB to
3130 // LSB.
3131 while (knownMSB != bitsKnown.getBitWidth()) {
3132 // Drop any high bits that are known.
3133 if (knownMSB)
3134 bitsKnown = bitsKnown.trunc(bitsKnown.getBitWidth() - knownMSB);
3135
3136 // Find the span of unknown bits, and extract it.
3137 unsigned unknownBits = bitsKnown.countLeadingZeros();
3138 unsigned lowBit = bitsKnown.getBitWidth() - unknownBits;
3139 auto spanOperand = rewriter.createOrFold<ExtractOp>(
3140 operand.getLoc(), operand, /*lowBit=*/lowBit,
3141 /*bitWidth=*/unknownBits);
3142 auto spanConstant = rhsCst.lshr(lowBit).trunc(unknownBits);
3143
3144 // Add this info to the concat we're generating.
3145 newConcatOperands.push_back(spanOperand);
3146 // FIXME(llvm merge, cc697fc292b0): concat doesn't work with zero bit values
3147 // newConstant = newConstant.concat(spanConstant);
3148 if (newConstant.getBitWidth() != 0)
3149 newConstant = newConstant.concat(spanConstant);
3150 else
3151 newConstant = spanConstant;
3152
3153 // Drop the unknown bits in prep for the next chunk.
3154 unsigned newWidth = bitsKnown.getBitWidth() - unknownBits;
3155 bitsKnown = bitsKnown.trunc(newWidth);
3156 knownMSB = bitsKnown.countLeadingOnes();
3157 }
3158
3159 // If all the operands to the concat are foldable then we have an identity
3160 // situation where all the sub-elements equal each other. This implies that
3161 // the overall result is foldable.
3162 if (newConcatOperands.empty()) {
3163 bool result = cmpOp.getPredicate() == ICmpPredicate::eq;
3164 replaceOpWithNewOpAndCopyNamehint<hw::ConstantOp>(rewriter, cmpOp,
3165 APInt(1, result));
3166 return;
3167 }
3168
3169 // If we have a single operand remaining, use it, otherwise form a concat.
3170 Value concatResult =
3171 rewriter.createOrFold<ConcatOp>(operand.getLoc(), newConcatOperands);
3172
3173 // Form the comparison against the smaller constant.
3174 auto newConstantOp = hw::ConstantOp::create(
3175 rewriter, cmpOp.getOperand(1).getLoc(), newConstant);
3176
3177 replaceOpWithNewOpAndCopyNamehint<ICmpOp>(rewriter, cmpOp,
3178 cmpOp.getPredicate(), concatResult,
3179 newConstantOp, cmpOp.getTwoState());
3180}
3181
3182// Simplify icmp eq(xor(a,b,cst1), cst2) -> icmp eq(xor(a,b), cst1^cst2).
3183static void combineEqualityICmpWithXorOfConstant(ICmpOp cmpOp, XorOp xorOp,
3184 const APInt &rhs,
3185 PatternRewriter &rewriter) {
3186 auto ip = rewriter.saveInsertionPoint();
3187 rewriter.setInsertionPoint(xorOp);
3188
3189 auto xorRHS = xorOp.getOperands().back().getDefiningOp<hw::ConstantOp>();
3190 auto newRHS = hw::ConstantOp::create(rewriter, xorRHS->getLoc(),
3191 xorRHS.getValue() ^ rhs);
3192 Value newLHS;
3193 switch (xorOp.getNumOperands()) {
3194 case 1:
3195 // This isn't common but is defined so we need to handle it.
3196 newLHS = hw::ConstantOp::create(rewriter, xorOp.getLoc(),
3197 APInt::getZero(rhs.getBitWidth()));
3198 break;
3199 case 2:
3200 // The binary case is the most common.
3201 newLHS = xorOp.getOperand(0);
3202 break;
3203 default:
3204 // The general case forces us to form a new xor with the remaining operands.
3205 SmallVector<Value> newOperands(xorOp.getOperands());
3206 newOperands.pop_back();
3207 newLHS = XorOp::create(rewriter, xorOp.getLoc(), newOperands, false);
3208 break;
3209 }
3210
3211 bool xorMultipleUses = !xorOp->hasOneUse();
3212
3213 // If the xor has multiple uses (not just the compare, then we need/want to
3214 // replace them as well.
3215 if (xorMultipleUses)
3216 replaceOpWithNewOpAndCopyNamehint<XorOp>(rewriter, xorOp, newLHS, xorRHS,
3217 false);
3218
3219 // Replace the comparison.
3220 rewriter.restoreInsertionPoint(ip);
3221 replaceOpWithNewOpAndCopyNamehint<ICmpOp>(
3222 rewriter, cmpOp, cmpOp.getPredicate(), newLHS, newRHS, false);
3223}
3224
3225LogicalResult ICmpOp::canonicalize(ICmpOp op, PatternRewriter &rewriter) {
3226 if (isOpTriviallyRecursive(op))
3227 return failure();
3228 APInt lhs, rhs;
3229
3230 // icmp 1, x -> icmp x, 1
3231 if (matchPattern(op.getLhs(), m_ConstantInt(&lhs))) {
3232 assert(!matchPattern(op.getRhs(), m_ConstantInt(&rhs)) &&
3233 "Should be folded");
3234 replaceOpWithNewOpAndCopyNamehint<ICmpOp>(
3235 rewriter, op, ICmpOp::getFlippedPredicate(op.getPredicate()),
3236 op.getRhs(), op.getLhs(), op.getTwoState());
3237 return success();
3238 }
3239
3240 // Canonicalize with RHS constant
3241 if (matchPattern(op.getRhs(), m_ConstantInt(&rhs))) {
3242 auto getConstant = [&](APInt constant) -> Value {
3243 return hw::ConstantOp::create(rewriter, op.getLoc(), std::move(constant));
3244 };
3245
3246 auto replaceWith = [&](ICmpPredicate predicate, Value lhs,
3247 Value rhs) -> LogicalResult {
3248 replaceOpWithNewOpAndCopyNamehint<ICmpOp>(rewriter, op, predicate, lhs,
3249 rhs, op.getTwoState());
3250 return success();
3251 };
3252
3253 auto replaceWithConstantI1 = [&](bool constant) -> LogicalResult {
3254 replaceOpWithNewOpAndCopyNamehint<hw::ConstantOp>(rewriter, op,
3255 APInt(1, constant));
3256 return success();
3257 };
3258
3259 switch (op.getPredicate()) {
3260 case ICmpPredicate::slt:
3261 // x < max -> x != max
3262 if (rhs.isMaxSignedValue())
3263 return replaceWith(ICmpPredicate::ne, op.getLhs(), op.getRhs());
3264 // x < min -> false
3265 if (rhs.isMinSignedValue())
3266 return replaceWithConstantI1(0);
3267 // x < min+1 -> x == min
3268 if ((rhs - 1).isMinSignedValue())
3269 return replaceWith(ICmpPredicate::eq, op.getLhs(),
3270 getConstant(rhs - 1));
3271 break;
3272 case ICmpPredicate::sgt:
3273 // x > min -> x != min
3274 if (rhs.isMinSignedValue())
3275 return replaceWith(ICmpPredicate::ne, op.getLhs(), op.getRhs());
3276 // x > max -> false
3277 if (rhs.isMaxSignedValue())
3278 return replaceWithConstantI1(0);
3279 // x > max-1 -> x == max
3280 if ((rhs + 1).isMaxSignedValue())
3281 return replaceWith(ICmpPredicate::eq, op.getLhs(),
3282 getConstant(rhs + 1));
3283 break;
3284 case ICmpPredicate::ult:
3285 // x < max -> x != max
3286 if (rhs.isAllOnes())
3287 return replaceWith(ICmpPredicate::ne, op.getLhs(), op.getRhs());
3288 // x < min -> false
3289 if (rhs.isZero())
3290 return replaceWithConstantI1(0);
3291 // x < min+1 -> x == min
3292 if ((rhs - 1).isZero())
3293 return replaceWith(ICmpPredicate::eq, op.getLhs(),
3294 getConstant(rhs - 1));
3295
3296 // x < 0xE0 -> extract(x, 5..7) != 0b111
3297 if (rhs.countLeadingOnes() + rhs.countTrailingZeros() ==
3298 rhs.getBitWidth()) {
3299 auto numOnes = rhs.countLeadingOnes();
3300 auto smaller = ExtractOp::create(rewriter, op.getLoc(), op.getLhs(),
3301 rhs.getBitWidth() - numOnes, numOnes);
3302 return replaceWith(ICmpPredicate::ne, smaller,
3303 getConstant(APInt::getAllOnes(numOnes)));
3304 }
3305
3306 break;
3307 case ICmpPredicate::ugt:
3308 // x > min -> x != min
3309 if (rhs.isZero())
3310 return replaceWith(ICmpPredicate::ne, op.getLhs(), op.getRhs());
3311 // x > max -> false
3312 if (rhs.isAllOnes())
3313 return replaceWithConstantI1(0);
3314 // x > max-1 -> x == max
3315 if ((rhs + 1).isAllOnes())
3316 return replaceWith(ICmpPredicate::eq, op.getLhs(),
3317 getConstant(rhs + 1));
3318
3319 // x > 0x07 -> extract(x, 3..7) != 0b00000
3320 if ((rhs + 1).isPowerOf2()) {
3321 auto numOnes = rhs.countTrailingOnes();
3322 auto newWidth = rhs.getBitWidth() - numOnes;
3323 auto smaller = ExtractOp::create(rewriter, op.getLoc(), op.getLhs(),
3324 numOnes, newWidth);
3325 return replaceWith(ICmpPredicate::ne, smaller,
3326 getConstant(APInt::getZero(newWidth)));
3327 }
3328
3329 break;
3330 case ICmpPredicate::sle:
3331 // x <= max -> true
3332 if (rhs.isMaxSignedValue())
3333 return replaceWithConstantI1(1);
3334 // x <= c -> x < (c+1)
3335 return replaceWith(ICmpPredicate::slt, op.getLhs(), getConstant(rhs + 1));
3336 case ICmpPredicate::sge:
3337 // x >= min -> true
3338 if (rhs.isMinSignedValue())
3339 return replaceWithConstantI1(1);
3340 // x >= c -> x > (c-1)
3341 return replaceWith(ICmpPredicate::sgt, op.getLhs(), getConstant(rhs - 1));
3342 case ICmpPredicate::ule:
3343 // x <= max -> true
3344 if (rhs.isAllOnes())
3345 return replaceWithConstantI1(1);
3346 // x <= c -> x < (c+1)
3347 return replaceWith(ICmpPredicate::ult, op.getLhs(), getConstant(rhs + 1));
3348 case ICmpPredicate::uge:
3349 // x >= min -> true
3350 if (rhs.isZero())
3351 return replaceWithConstantI1(1);
3352 // x >= c -> x > (c-1)
3353 return replaceWith(ICmpPredicate::ugt, op.getLhs(), getConstant(rhs - 1));
3354 case ICmpPredicate::eq:
3355 if (rhs.getBitWidth() == 1) {
3356 if (rhs.isZero()) {
3357 // x == 0 -> x ^ 1
3358 replaceOpWithNewOpAndCopyNamehint<XorOp>(rewriter, op, op.getLhs(),
3359 getConstant(APInt(1, 1)),
3360 op.getTwoState());
3361 return success();
3362 }
3363 if (rhs.isAllOnes()) {
3364 // x == 1 -> x
3365 replaceOpAndCopyNamehint(rewriter, op, op.getLhs());
3366 return success();
3367 }
3368 }
3369 break;
3370 case ICmpPredicate::ne:
3371 if (rhs.getBitWidth() == 1) {
3372 if (rhs.isZero()) {
3373 // x != 0 -> x
3374 replaceOpAndCopyNamehint(rewriter, op, op.getLhs());
3375 return success();
3376 }
3377 if (rhs.isAllOnes()) {
3378 // x != 1 -> x ^ 1
3379 replaceOpWithNewOpAndCopyNamehint<XorOp>(rewriter, op, op.getLhs(),
3380 getConstant(APInt(1, 1)),
3381 op.getTwoState());
3382 return success();
3383 }
3384 }
3385 break;
3386 case ICmpPredicate::ceq:
3387 case ICmpPredicate::cne:
3388 case ICmpPredicate::weq:
3389 case ICmpPredicate::wne:
3390 break;
3391 }
3392
3393 // We have some specific optimizations for comparison with a constant that
3394 // are only supported for equality comparisons.
3395 if (op.getPredicate() == ICmpPredicate::eq ||
3396 op.getPredicate() == ICmpPredicate::ne) {
3397 // Simplify `icmp(value_with_known_bits, rhscst)` into some extracts
3398 // with a smaller constant. We only support equality comparisons for
3399 // this.
3400 auto knownBits = computeKnownBits(op.getLhs());
3401 if (!knownBits.isUnknown())
3402 return combineEqualityICmpWithKnownBitsAndConstant(op, knownBits, rhs,
3403 rewriter),
3404 success();
3405
3406 // Simplify icmp eq(xor(a,b,cst1), cst2) -> icmp eq(xor(a,b),
3407 // cst1^cst2).
3408 if (auto xorOp = op.getLhs().getDefiningOp<XorOp>())
3409 if (xorOp.getOperands().back().getDefiningOp<hw::ConstantOp>())
3410 return combineEqualityICmpWithXorOfConstant(op, xorOp, rhs, rewriter),
3411 success();
3412
3413 // Simplify icmp eq(replicate(v, n), c) -> icmp eq(v, c) if c is zero or
3414 // all one.
3415 if (auto replicateOp = op.getLhs().getDefiningOp<ReplicateOp>())
3416 if (rhs.isAllOnes() || rhs.isZero()) {
3417 auto width = replicateOp.getInput().getType().getIntOrFloatBitWidth();
3418 auto cst =
3419 hw::ConstantOp::create(rewriter, op.getLoc(),
3420 rhs.isAllOnes() ? APInt::getAllOnes(width)
3421 : APInt::getZero(width));
3422 replaceOpWithNewOpAndCopyNamehint<ICmpOp>(
3423 rewriter, op, op.getPredicate(), replicateOp.getInput(), cst,
3424 op.getTwoState());
3425 return success();
3426 }
3427 }
3428 }
3429
3430 // icmp(cat(prefix, a, b, suffix), cat(prefix, c, d, suffix)) => icmp(cat(a,
3431 // b), cat(c, d)). contains special handling for sign bit in signed
3432 // compressions.
3433 if (Operation *opLHS = op.getLhs().getDefiningOp())
3434 if (Operation *opRHS = op.getRhs().getDefiningOp())
3435 if (isa<ConcatOp, ReplicateOp>(opLHS) &&
3436 isa<ConcatOp, ReplicateOp>(opRHS)) {
3437 if (succeeded(matchAndRewriteCompareConcat(op, opLHS, opRHS, rewriter)))
3438 return success();
3439 }
3440
3441 return failure();
3442}
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)
Definition CombFolds.cpp:85
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.
Definition CombFolds.cpp:52
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)
Definition CombFolds.cpp:46
static bool shouldBeFlattened(Operation *op)
Return true if the op will be flattened afterwards.
Definition CombFolds.cpp:91
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)
Definition CombFolds.cpp:27
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)
Definition CombFolds.cpp:67
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...
Definition CombFolds.cpp:38
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(low_bit, result_type, input=None)
Definition comb.py:187
create(elements, Type result_type=None)
Definition hw.py:483
create(array_value, low_index, ret_type)
Definition hw.py:466
create(data_type, value)
Definition hw.py:441
create(data_type, value)
Definition hw.py:433
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition CalyxOps.cpp:56
void extractBits(OpBuilder &builder, Value val, SmallVectorImpl< Value > &bits)
Extract bits from a value.
Definition CombOps.cpp:114
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.
Definition CombOps.cpp:102
MuxChainWithComparisonFoldingStyle
Enum for mux chain folding styles.
Definition CombOps.h:109
@ BalancedMuxTree
Definition CombOps.h:109
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.
Definition CombOps.cpp:141
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.
Definition CombOps.cpp:79
LogicalResult convertDivUByPowerOfTwo(DivUOp divOp, mlir::PatternRewriter &rewriter)
Convert unsigned division or modulo by a power of two.
uint64_t getWidth(Type t)
Definition ESIPasses.cpp:32
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.
Definition Naming.cpp:73
Definition comb.py:1