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 base;
1430 // Check for sext of the inverted value
1431 if (matchPattern(op.getResult(), m_Complement(m_Sext(m_Any(&base))))) {
1432 // Create negated sext: ~sext(x) = sext(~x)
1433 auto negBase = createOrFoldNot(rewriter, op.getLoc(), base, true);
1434 auto sextNegBase =
1435 createOrFoldSExt(rewriter, op.getLoc(), negBase, op.getType());
1436 replaceOpAndCopyNamehint(rewriter, op, sextNegBase);
1437 return success();
1438 }
1439
1440 // xor(x, xor(...)) -> xor(x, ...) -- flatten
1441 if (tryFlatteningOperands(op, rewriter))
1442 return success();
1443
1444 // extracts only of xor(...) -> xor(extract()...)
1445 if (narrowOperationWidth(op, true, rewriter))
1446 return success();
1447
1448 // xor(a[0], a[1], ..., a[n]) -> parity(a)
1449 if (auto source = getCommonOperand(op)) {
1450 replaceOpWithNewOpAndCopyNamehint<ParityOp>(rewriter, op, source);
1451 return success();
1452 }
1453
1454 return failure();
1455}
1456
1457OpFoldResult SubOp::fold(FoldAdaptor adaptor) {
1458 if (isOpTriviallyRecursive(*this))
1459 return {};
1460
1461 // sub(x - x) -> 0
1462 if (getRhs() == getLhs())
1463 return getIntAttr(
1464 APInt::getZero(getLhs().getType().getIntOrFloatBitWidth()),
1465 getContext());
1466
1467 if (adaptor.getRhs()) {
1468 // If both are constants, we can unconditionally fold.
1469 if (adaptor.getLhs()) {
1470 // Constant fold (c1 - c2) => (c1 + -1*c2).
1471 auto negOne = getIntAttr(
1472 APInt::getAllOnes(getLhs().getType().getIntOrFloatBitWidth()),
1473 getContext());
1474 auto rhsNeg = hw::ParamExprAttr::get(
1475 hw::PEO::Mul, cast<TypedAttr>(adaptor.getRhs()), negOne);
1476 return hw::ParamExprAttr::get(hw::PEO::Add,
1477 cast<TypedAttr>(adaptor.getLhs()), rhsNeg);
1478 }
1479
1480 // sub(x - 0) -> x
1481 if (auto rhsC = dyn_cast<IntegerAttr>(adaptor.getRhs())) {
1482 if (rhsC.getValue().isZero())
1483 return getLhs();
1484 }
1485 }
1486
1487 return {};
1488}
1489
1490LogicalResult SubOp::canonicalize(SubOp op, PatternRewriter &rewriter) {
1491 if (isOpTriviallyRecursive(op))
1492 return failure();
1493
1494 // sub(x, cst) -> add(x, -cst)
1495 APInt value;
1496 if (matchPattern(op.getRhs(), m_ConstantInt(&value))) {
1497 auto negCst = hw::ConstantOp::create(rewriter, op.getLoc(), -value);
1498 replaceOpWithNewOpAndCopyNamehint<AddOp>(rewriter, op, op.getLhs(), negCst,
1499 false);
1500 return success();
1501 }
1502
1503 // extracts only of sub(...) -> sub(extract()...)
1504 if (narrowOperationWidth(op, false, rewriter))
1505 return success();
1506
1507 return failure();
1508}
1509
1510OpFoldResult AddOp::fold(FoldAdaptor adaptor) {
1511 if (isOpTriviallyRecursive(*this))
1512 return {};
1513
1514 auto size = getInputs().size();
1515
1516 // add(x) -> x -- noop
1517 if (size == 1u)
1518 return getInputs()[0];
1519
1520 // Constant fold constant operands.
1521 return constFoldAssociativeOp(adaptor.getOperands(), hw::PEO::Add);
1522}
1523
1524LogicalResult AddOp::canonicalize(AddOp op, PatternRewriter &rewriter) {
1525 if (isOpTriviallyRecursive(op))
1526 return failure();
1527
1528 auto inputs = op.getInputs();
1529 auto size = inputs.size();
1530 assert(size > 1 && "expected 2 or more operands");
1531
1532 APInt value, value2;
1533
1534 // add(..., 0) -> add(...) -- identity
1535 if (matchPattern(inputs.back(), m_ConstantInt(&value)) && value.isZero()) {
1536 replaceOpWithNewOpAndCopyNamehint<AddOp>(rewriter, op, op.getType(),
1537 inputs.drop_back(), false);
1538 return success();
1539 }
1540
1541 // add(..., c1, c2) -> add(..., c3) where c3 = c1 + c2 -- constant folding
1542 if (matchPattern(inputs[size - 1], m_ConstantInt(&value)) &&
1543 matchPattern(inputs[size - 2], m_ConstantInt(&value2))) {
1544 auto cst = hw::ConstantOp::create(rewriter, op.getLoc(), value + value2);
1545 SmallVector<Value, 4> newOperands(inputs.drop_back(/*n=*/2));
1546 newOperands.push_back(cst);
1547 replaceOpWithNewOpAndCopyNamehint<AddOp>(rewriter, op, op.getType(),
1548 newOperands, false);
1549 return success();
1550 }
1551
1552 // add(..., x, x) -> add(..., shl(x, 1))
1553 if (inputs[size - 1] == inputs[size - 2]) {
1554 SmallVector<Value, 4> newOperands(inputs.drop_back(/*n=*/2));
1555
1556 auto one = hw::ConstantOp::create(rewriter, op.getLoc(), op.getType(), 1);
1557 auto shiftLeftOp =
1558 comb::ShlOp::create(rewriter, op.getLoc(), inputs.back(), one, false);
1559
1560 newOperands.push_back(shiftLeftOp);
1561 replaceOpWithNewOpAndCopyNamehint<AddOp>(rewriter, op, op.getType(),
1562 newOperands, false);
1563 return success();
1564 }
1565
1566 auto shlOp = inputs[size - 1].getDefiningOp<comb::ShlOp>();
1567 // add(..., x, shl(x, c)) -> add(..., mul(x, (1 << c) + 1))
1568 if (shlOp && shlOp.getLhs() == inputs[size - 2] &&
1569 matchPattern(shlOp.getRhs(), m_ConstantInt(&value))) {
1570
1571 APInt one(/*numBits=*/value.getBitWidth(), 1, /*isSigned=*/false);
1572 auto rhs =
1573 hw::ConstantOp::create(rewriter, op.getLoc(), (one << value) + one);
1574
1575 std::array<Value, 2> factors = {shlOp.getLhs(), rhs};
1576 auto mulOp = comb::MulOp::create(rewriter, op.getLoc(), factors, false);
1577
1578 SmallVector<Value, 4> newOperands(inputs.drop_back(/*n=*/2));
1579 newOperands.push_back(mulOp);
1580 replaceOpWithNewOpAndCopyNamehint<AddOp>(rewriter, op, op.getType(),
1581 newOperands, false);
1582 return success();
1583 }
1584
1585 auto mulOp = inputs[size - 1].getDefiningOp<comb::MulOp>();
1586 // add(..., x, mul(x, c)) -> add(..., mul(x, c + 1))
1587 if (mulOp && mulOp.getInputs().size() == 2 &&
1588 mulOp.getInputs()[0] == inputs[size - 2] &&
1589 matchPattern(mulOp.getInputs()[1], m_ConstantInt(&value))) {
1590
1591 APInt one(/*numBits=*/value.getBitWidth(), 1, /*isSigned=*/false);
1592 auto rhs = hw::ConstantOp::create(rewriter, op.getLoc(), value + one);
1593 std::array<Value, 2> factors = {mulOp.getInputs()[0], rhs};
1594 auto newMulOp = comb::MulOp::create(rewriter, op.getLoc(), factors, false);
1595
1596 SmallVector<Value, 4> newOperands(inputs.drop_back(/*n=*/2));
1597 newOperands.push_back(newMulOp);
1598 replaceOpWithNewOpAndCopyNamehint<AddOp>(rewriter, op, op.getType(),
1599 newOperands, false);
1600 return success();
1601 }
1602
1603 // add(a, add(...)) -> add(a, ...) -- flatten
1604 if (tryFlatteningOperands(op, rewriter))
1605 return success();
1606
1607 // extracts only of add(...) -> add(extract()...)
1608 if (narrowOperationWidth(op, false, rewriter))
1609 return success();
1610
1611 // add(add(x, c1), c2) -> add(x, c1 + c2)
1612 auto addOp = inputs[0].getDefiningOp<comb::AddOp>();
1613 if (addOp && addOp.getInputs().size() == 2 &&
1614 matchPattern(addOp.getInputs()[1], m_ConstantInt(&value2)) &&
1615 inputs.size() == 2 && matchPattern(inputs[1], m_ConstantInt(&value))) {
1616
1617 auto rhs = hw::ConstantOp::create(rewriter, op.getLoc(), value + value2);
1618 replaceOpWithNewOpAndCopyNamehint<AddOp>(
1619 rewriter, op, op.getType(), ArrayRef<Value>{addOp.getInputs()[0], rhs},
1620 /*twoState=*/op.getTwoState() && addOp.getTwoState());
1621 return success();
1622 }
1623
1624 return failure();
1625}
1626
1627OpFoldResult MulOp::fold(FoldAdaptor adaptor) {
1628 if (isOpTriviallyRecursive(*this))
1629 return {};
1630
1631 auto size = getInputs().size();
1632 auto inputs = adaptor.getInputs();
1633
1634 // mul(x) -> x -- noop
1635 if (size == 1u)
1636 return getInputs()[0];
1637
1638 auto width = cast<IntegerType>(getType()).getWidth();
1639 if (width == 0)
1640 return getIntAttr(APInt::getZero(0), getContext());
1641
1642 APInt value(/*numBits=*/width, 1, /*isSigned=*/false);
1643
1644 // mul(x, 0, 1) -> 0 -- annulment
1645 for (auto operand : inputs) {
1646 auto attr = dyn_cast_or_null<IntegerAttr>(operand);
1647 if (!attr)
1648 continue;
1649 value *= attr.getValue();
1650 if (value.isZero())
1651 return getIntAttr(value, getContext());
1652 }
1653
1654 // Constant fold
1655 return constFoldAssociativeOp(inputs, hw::PEO::Mul);
1656}
1657
1658LogicalResult MulOp::canonicalize(MulOp op, PatternRewriter &rewriter) {
1659 if (isOpTriviallyRecursive(op))
1660 return failure();
1661
1662 auto inputs = op.getInputs();
1663 auto size = inputs.size();
1664 assert(size > 1 && "expected 2 or more operands");
1665
1666 APInt value, value2;
1667
1668 // mul(x, c) -> shl(x, log2(c)), where c is a power of two.
1669 if (size == 2 && matchPattern(inputs.back(), m_ConstantInt(&value)) &&
1670 value.isPowerOf2()) {
1671 auto shift = hw::ConstantOp::create(rewriter, op.getLoc(), op.getType(),
1672 value.exactLogBase2());
1673 auto shlOp =
1674 comb::ShlOp::create(rewriter, op.getLoc(), inputs[0], shift, false);
1675
1676 replaceOpWithNewOpAndCopyNamehint<MulOp>(rewriter, op, op.getType(),
1677 ArrayRef<Value>(shlOp), false);
1678 return success();
1679 }
1680
1681 // mul(..., 1) -> mul(...) -- identity
1682 if (matchPattern(inputs.back(), m_ConstantInt(&value)) && value.isOne()) {
1683 replaceOpWithNewOpAndCopyNamehint<MulOp>(rewriter, op, op.getType(),
1684 inputs.drop_back());
1685 return success();
1686 }
1687
1688 // mul(..., c1, c2) -> mul(..., c3) where c3 = c1 * c2 -- constant folding
1689 if (matchPattern(inputs[size - 1], m_ConstantInt(&value)) &&
1690 matchPattern(inputs[size - 2], m_ConstantInt(&value2))) {
1691 auto cst = hw::ConstantOp::create(rewriter, op.getLoc(), value * value2);
1692 SmallVector<Value, 4> newOperands(inputs.drop_back(/*n=*/2));
1693 newOperands.push_back(cst);
1694 replaceOpWithNewOpAndCopyNamehint<MulOp>(rewriter, op, op.getType(),
1695 newOperands);
1696 return success();
1697 }
1698
1699 // mul(a, mul(...)) -> mul(a, ...) -- flatten
1700 if (tryFlatteningOperands(op, rewriter))
1701 return success();
1702
1703 // extracts only of mul(...) -> mul(extract()...)
1704 if (narrowOperationWidth(op, false, rewriter))
1705 return success();
1706
1707 return failure();
1708}
1709
1710template <class Op, bool isSigned>
1711static OpFoldResult foldDiv(Op op, ArrayRef<Attribute> constants) {
1712 if (auto rhsValue = dyn_cast_or_null<IntegerAttr>(constants[1])) {
1713 // divu(x, 1) -> x, divs(x, 1) -> x
1714 if (rhsValue.getValue() == 1)
1715 return op.getLhs();
1716
1717 // If the divisor is zero, do not fold for now.
1718 if (rhsValue.getValue().isZero())
1719 return {};
1720 }
1721
1722 return constFoldBinaryOp(constants, isSigned ? hw::PEO::DivS : hw::PEO::DivU);
1723}
1724
1725OpFoldResult DivUOp::fold(FoldAdaptor adaptor) {
1726 if (isOpTriviallyRecursive(*this))
1727 return {};
1728 return foldDiv<DivUOp, /*isSigned=*/false>(*this, adaptor.getOperands());
1729}
1730
1731OpFoldResult DivSOp::fold(FoldAdaptor adaptor) {
1732 if (isOpTriviallyRecursive(*this))
1733 return {};
1734 return foldDiv<DivSOp, /*isSigned=*/true>(*this, adaptor.getOperands());
1735}
1736
1737template <class Op, bool isSigned>
1738static OpFoldResult foldMod(Op op, ArrayRef<Attribute> constants) {
1739 if (auto rhsValue = dyn_cast_or_null<IntegerAttr>(constants[1])) {
1740 // modu(x, 1) -> 0, mods(x, 1) -> 0
1741 if (rhsValue.getValue() == 1)
1742 return getIntAttr(APInt::getZero(op.getType().getIntOrFloatBitWidth()),
1743 op.getContext());
1744
1745 // If the divisor is zero, do not fold for now.
1746 if (rhsValue.getValue().isZero())
1747 return {};
1748 }
1749
1750 if (auto lhsValue = dyn_cast_or_null<IntegerAttr>(constants[0])) {
1751 // modu(0, x) -> 0, mods(0, x) -> 0
1752 if (lhsValue.getValue().isZero())
1753 return getIntAttr(APInt::getZero(op.getType().getIntOrFloatBitWidth()),
1754 op.getContext());
1755 }
1756
1757 return constFoldBinaryOp(constants, isSigned ? hw::PEO::ModS : hw::PEO::ModU);
1758}
1759
1760OpFoldResult ModUOp::fold(FoldAdaptor adaptor) {
1761 if (isOpTriviallyRecursive(*this))
1762 return {};
1763 return foldMod<ModUOp, /*isSigned=*/false>(*this, adaptor.getOperands());
1764}
1765
1766OpFoldResult ModSOp::fold(FoldAdaptor adaptor) {
1767 if (isOpTriviallyRecursive(*this))
1768 return {};
1769 return foldMod<ModSOp, /*isSigned=*/true>(*this, adaptor.getOperands());
1770}
1771
1772LogicalResult DivUOp::canonicalize(DivUOp op, PatternRewriter &rewriter) {
1773 if (isOpTriviallyRecursive(op) || !op.getTwoState())
1774 return failure();
1775 return convertDivUByPowerOfTwo(op, rewriter);
1776}
1777
1778LogicalResult ModUOp::canonicalize(ModUOp op, PatternRewriter &rewriter) {
1779 if (isOpTriviallyRecursive(op) || !op.getTwoState())
1780 return failure();
1781
1782 return convertModUByPowerOfTwo(op, rewriter);
1783}
1784
1785//===----------------------------------------------------------------------===//
1786// ConcatOp
1787//===----------------------------------------------------------------------===//
1788
1789// Constant folding
1790OpFoldResult ConcatOp::fold(FoldAdaptor adaptor) {
1791 if (isOpTriviallyRecursive(*this))
1792 return {};
1793
1794 if (getNumOperands() == 1)
1795 return getOperand(0);
1796
1797 // If all the operands are constant, we can fold.
1798 for (auto attr : adaptor.getInputs())
1799 if (!attr || !isa<IntegerAttr>(attr))
1800 return {};
1801
1802 // If we got here, we can constant fold.
1803 unsigned resultWidth = getType().getIntOrFloatBitWidth();
1804 APInt result(resultWidth, 0);
1805
1806 unsigned nextInsertion = resultWidth;
1807 // Insert each chunk into the result.
1808 for (auto attr : adaptor.getInputs()) {
1809 auto chunk = cast<IntegerAttr>(attr).getValue();
1810 nextInsertion -= chunk.getBitWidth();
1811 result.insertBits(chunk, nextInsertion);
1812 }
1813
1814 return getIntAttr(result, getContext());
1815}
1816
1817LogicalResult ConcatOp::canonicalize(ConcatOp op, PatternRewriter &rewriter) {
1818 if (isOpTriviallyRecursive(op))
1819 return failure();
1820
1821 auto inputs = op.getInputs();
1822 auto size = inputs.size();
1823 assert(size > 1 && "expected 2 or more operands");
1824
1825 // Holds the not-yet-processed operands in reverse order!
1826 SmallVector<Value, 4> pendingOperands, processedOperands;
1827 bool anyOperandChanged;
1828
1829 auto pushPendingOperands = [&](ValueRange operands) {
1830 auto size = operands.size();
1831 for (size_t i = 0; i != size; ++i)
1832 pendingOperands.push_back(operands[size - 1 - i]);
1833 anyOperandChanged = true;
1834 };
1835 auto replacePrevOperand = [&](Value replacement) {
1836 processedOperands.back() = replacement;
1837 anyOperandChanged = true;
1838 };
1839 pendingOperands.reserve(size);
1840 pushPendingOperands(inputs);
1841 anyOperandChanged = false;
1842
1843 while (!pendingOperands.empty()) {
1844 Value nextOperand = pendingOperands.pop_back_val();
1845
1846 // If an operand to the concat is itself a concat, then we can fold them
1847 // together.
1848 if (auto subConcat = nextOperand.getDefiningOp<ConcatOp>()) {
1849 pushPendingOperands(subConcat->getOperands());
1850 continue;
1851 }
1852
1853 // Check for canonicalization due to neighboring operands.
1854 if (!processedOperands.empty()) {
1855 Value prevOperand = processedOperands.back();
1856
1857 // Merge neighboring constants.
1858 if (auto cst = nextOperand.getDefiningOp<hw::ConstantOp>()) {
1859 if (auto prevCst = prevOperand.getDefiningOp<hw::ConstantOp>()) {
1860 unsigned prevWidth = prevCst.getValue().getBitWidth();
1861 unsigned thisWidth = cst.getValue().getBitWidth();
1862 auto resultCst = cst.getValue().zext(prevWidth + thisWidth);
1863 resultCst |= prevCst.getValue().zext(prevWidth + thisWidth)
1864 << thisWidth;
1865 Value replacement =
1866 hw::ConstantOp::create(rewriter, op.getLoc(), resultCst);
1867 replacePrevOperand(replacement);
1868 continue;
1869 }
1870 }
1871
1872 // If the two operands are the same, turn them into a replicate.
1873 if (nextOperand == prevOperand) {
1874 Value replacement =
1875 rewriter.createOrFold<ReplicateOp>(op.getLoc(), prevOperand, 2);
1876 replacePrevOperand(replacement);
1877 continue;
1878 }
1879
1880 // If this input is a replicate, see if we can fold it with the previous
1881 // one.
1882 if (auto repl = nextOperand.getDefiningOp<ReplicateOp>()) {
1883 // ... x, repl(x, n), ... ==> ..., repl(x, n+1), ...
1884 if (repl.getOperand() == prevOperand) {
1885 Value replacement = rewriter.createOrFold<ReplicateOp>(
1886 op.getLoc(), repl.getOperand(), repl.getMultiple() + 1);
1887 replacePrevOperand(replacement);
1888 continue;
1889 }
1890 // ... repl(x, n), repl(x, m), ... ==> ..., repl(x, n+m), ...
1891 if (auto prevRepl = prevOperand.getDefiningOp<ReplicateOp>()) {
1892 if (prevRepl.getOperand() == repl.getOperand()) {
1893 Value replacement = rewriter.createOrFold<ReplicateOp>(
1894 op.getLoc(), repl.getOperand(),
1895 repl.getMultiple() + prevRepl.getMultiple());
1896 replacePrevOperand(replacement);
1897 continue;
1898 }
1899 }
1900 }
1901
1902 // ... repl(x, n), x, ... ==> ..., repl(x, n+1), ...
1903 if (auto repl = prevOperand.getDefiningOp<ReplicateOp>()) {
1904 if (repl.getOperand() == nextOperand) {
1905 Value replacement = rewriter.createOrFold<ReplicateOp>(
1906 op.getLoc(), nextOperand, repl.getMultiple() + 1);
1907 replacePrevOperand(replacement);
1908 continue;
1909 }
1910 }
1911
1912 // Merge neighboring extracts of neighboring inputs, e.g.
1913 // {A[3], A[2]} -> A[3:2]
1914 if (auto extract = nextOperand.getDefiningOp<ExtractOp>()) {
1915 if (auto prevExtract = prevOperand.getDefiningOp<ExtractOp>()) {
1916 if (extract.getInput() == prevExtract.getInput()) {
1917 auto thisWidth = cast<IntegerType>(extract.getType()).getWidth();
1918 if (prevExtract.getLowBit() == extract.getLowBit() + thisWidth) {
1919 auto prevWidth = prevExtract.getType().getIntOrFloatBitWidth();
1920 auto resType = rewriter.getIntegerType(thisWidth + prevWidth);
1921 Value replacement =
1922 ExtractOp::create(rewriter, op.getLoc(), resType,
1923 extract.getInput(), extract.getLowBit());
1924 replacePrevOperand(replacement);
1925 continue;
1926 }
1927 }
1928 }
1929 }
1930 // Merge neighboring array extracts of neighboring inputs, e.g.
1931 // {Array[4], bitcast(Array[3:2])} -> bitcast(A[4:2])
1932
1933 // This represents a slice of an array.
1934 struct ArraySlice {
1935 Value input;
1936 Value index;
1937 size_t width;
1938 static std::optional<ArraySlice> get(Value value) {
1939 assert(isa<IntegerType>(value.getType()) && "expected integer type");
1940 if (auto arrayGet = value.getDefiningOp<hw::ArrayGetOp>())
1941 return ArraySlice{arrayGet.getInput(), arrayGet.getIndex(), 1};
1942 // array slice op is wrapped with bitcast.
1943 if (auto bitcast = value.getDefiningOp<hw::BitcastOp>())
1944 if (auto arraySlice =
1945 bitcast.getInput().getDefiningOp<hw::ArraySliceOp>())
1946 return ArraySlice{
1947 arraySlice.getInput(), arraySlice.getLowIndex(),
1948 hw::type_cast<hw::ArrayType>(arraySlice.getType())
1949 .getNumElements()};
1950 return std::nullopt;
1951 }
1952 };
1953 if (auto extractOpt = ArraySlice::get(nextOperand)) {
1954 if (auto prevExtractOpt = ArraySlice::get(prevOperand)) {
1955 // Check that two array slices are mergable.
1956 if (prevExtractOpt->index.getType() == extractOpt->index.getType() &&
1957 prevExtractOpt->input == extractOpt->input &&
1958 hw::isOffset(extractOpt->index, prevExtractOpt->index,
1959 extractOpt->width)) {
1960 auto resType = hw::ArrayType::get(
1961 hw::type_cast<hw::ArrayType>(prevExtractOpt->input.getType())
1962 .getElementType(),
1963 extractOpt->width + prevExtractOpt->width);
1964 auto resIntType = rewriter.getIntegerType(hw::getBitWidth(resType));
1965 Value replacement = hw::BitcastOp::create(
1966 rewriter, op.getLoc(), resIntType,
1967 hw::ArraySliceOp::create(rewriter, op.getLoc(), resType,
1968 prevExtractOpt->input,
1969 extractOpt->index));
1970 replacePrevOperand(replacement);
1971 continue;
1972 }
1973 }
1974 }
1975 }
1976
1977 processedOperands.push_back(nextOperand);
1978 }
1979
1980 // Batch-resolve all ExtractOp users of this concat using a prefix-sum array
1981 // and binary search. This avoids the O(M*N) cost of having each ExtractOp
1982 // independently do a linear scan over the concat's operands.
1983 //
1984 // Only do this when:
1985 // - the concat operands are unchanged (if they changed, we'll replace the
1986 // concat below and the extract users will be re-enqueued naturally)
1987 // - there are enough extract users to justify batching (for small counts,
1988 // the per-ExtractOp canonicalization is fine and preserves output order)
1989 constexpr size_t kBatchExtractThreshold = 16;
1990 bool anyExtractsResolved = false;
1991 if (!anyOperandChanged && processedOperands.size() > 1) {
1992 SmallVector<ExtractOp, 8> extractUsers;
1993 for (auto *user : op->getUsers()) {
1994 if (auto extract = dyn_cast<ExtractOp>(user))
1995 extractUsers.push_back(extract);
1996 }
1997
1998 if (extractUsers.size() >= kBatchExtractThreshold) {
1999 // Build prefix-sum of bit widths in LSB-first (reversed) order.
2000 auto concatInputs = op.getInputs();
2001 size_t numConcatOperands = concatInputs.size();
2002 SmallVector<size_t> prefixWidths(numConcatOperands);
2003 size_t cumWidth = 0;
2004 for (size_t i = 0; i < numConcatOperands; ++i) {
2005 // Note: we can just query bitwidth here as is as the ExtractOp above's
2006 // type constraints means this is valid in valid IR.
2007 cumWidth += concatInputs[numConcatOperands - 1 - i]
2008 .getType()
2009 .getIntOrFloatBitWidth();
2010 prefixWidths[i] = cumWidth;
2011 }
2012
2013 // Resolve each extract user.
2014 for (auto extract : extractUsers) {
2015 if (succeeded(extractConcatToConcatExtract(extract, op, rewriter,
2016 prefixWidths)))
2017 anyExtractsResolved = true;
2018 }
2019 }
2020 }
2021
2022 if (processedOperands.size() == 1) {
2023 // If the operands were all the same, we'll reach here with a single
2024 // ReplicateOp.
2025 replaceOpAndCopyNamehint(rewriter, op, processedOperands[0]);
2026 } else if (anyOperandChanged) {
2027 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(rewriter, op, op.getType(),
2028 processedOperands);
2029 } else if (!anyExtractsResolved) {
2030 return failure();
2031 }
2032 return success();
2033}
2034
2035//===----------------------------------------------------------------------===//
2036// MuxOp
2037//===----------------------------------------------------------------------===//
2038
2039OpFoldResult MuxOp::fold(FoldAdaptor adaptor) {
2040 if (isOpTriviallyRecursive(*this))
2041 return {};
2042
2043 // mux (c, b, b) -> b
2044 if (getTrueValue() == getFalseValue() && getTrueValue() != getResult())
2045 return getTrueValue();
2046 if (auto tv = adaptor.getTrueValue())
2047 if (tv == adaptor.getFalseValue())
2048 return tv;
2049
2050 // mux(0, a, b) -> b
2051 // mux(1, a, b) -> a
2052 if (auto pred = dyn_cast_or_null<IntegerAttr>(adaptor.getCond())) {
2053 if (pred.getValue().isZero() && getFalseValue() != getResult())
2054 return getFalseValue();
2055 if (pred.getValue().isOne() && getTrueValue() != getResult())
2056 return getTrueValue();
2057 }
2058
2059 // mux(cond, 1, 0) -> cond
2060 if (getCond().getType() == getTrueValue().getType())
2061 if (auto tv = dyn_cast_or_null<IntegerAttr>(adaptor.getTrueValue()))
2062 if (auto fv = dyn_cast_or_null<IntegerAttr>(adaptor.getFalseValue()))
2063 if (tv.getValue().isOne() && fv.getValue().isZero() &&
2064 hw::getBitWidth(getType()) == 1 && getCond() != getResult())
2065 return getCond();
2066
2067 return {};
2068}
2069
2070/// Check to see if the condition to the specified mux is an equality
2071/// comparison `indexValue` and one or more constants. If so, put the
2072/// constants in the constants vector and return true, otherwise return false.
2073///
2074/// This is part of foldMuxChain.
2075///
2076static bool
2077getMuxChainCondConstant(Value cond, Value indexValue, bool isInverted,
2078 std::function<void(hw::ConstantOp)> constantFn) {
2079 // Handle `idx == 42` and `idx != 42`.
2080 if (auto cmp = cond.getDefiningOp<ICmpOp>()) {
2081 // TODO: We could handle things like "x < 2" as two entries.
2082 auto requiredPredicate =
2083 (isInverted ? ICmpPredicate::eq : ICmpPredicate::ne);
2084 if (cmp.getLhs() == indexValue && cmp.getPredicate() == requiredPredicate) {
2085 if (auto cst = cmp.getRhs().getDefiningOp<hw::ConstantOp>()) {
2086 constantFn(cst);
2087 return true;
2088 }
2089 }
2090 return false;
2091 }
2092
2093 // Handle mux(`idx == 1 || idx == 3`, value, muxchain).
2094 if (auto orOp = cond.getDefiningOp<OrOp>()) {
2095 if (!isInverted)
2096 return false;
2097 for (auto operand : orOp.getOperands())
2098 if (!getMuxChainCondConstant(operand, indexValue, isInverted, constantFn))
2099 return false;
2100 return true;
2101 }
2102
2103 // Handle mux(`idx != 1 && idx != 3`, muxchain, value).
2104 if (auto andOp = cond.getDefiningOp<AndOp>()) {
2105 if (isInverted)
2106 return false;
2107 for (auto operand : andOp.getOperands())
2108 if (!getMuxChainCondConstant(operand, indexValue, isInverted, constantFn))
2109 return false;
2110 return true;
2111 }
2112
2113 return false;
2114}
2115
2116/// Given a mux, check to see if the "on true" value (or "on false" value if
2117/// isFalseSide=true) is a mux tree with the same condition. This allows us
2118/// to turn things like `mux(VAL == 0, A, (mux (VAL == 1), B, C))` into
2119/// `array_get (array_create(A, B, C), VAL)` or a balanced mux tree which is far
2120/// more compact and allows synthesis tools to do more interesting
2121/// optimizations.
2122///
2123/// This returns false if we cannot form the mux tree (or do not want to) and
2124/// returns true if the mux was replaced.
2126 PatternRewriter &rewriter, MuxOp rootMux, bool isFalseSide,
2127 llvm::function_ref<MuxChainWithComparisonFoldingStyle(size_t indexWidth,
2128 size_t numEntries)>
2129 styleFn) {
2130 // Get the index value being compared. Later we check to see if it is
2131 // compared to a constant with the right predicate.
2132 auto rootCmp = rootMux.getCond().getDefiningOp<ICmpOp>();
2133 if (!rootCmp)
2134 return false;
2135 Value indexValue = rootCmp.getLhs();
2136
2137 // Return the value to use if the equality match succeeds.
2138 auto getCaseValue = [&](MuxOp mux) -> Value {
2139 return mux.getOperand(1 + unsigned(!isFalseSide));
2140 };
2141
2142 // Return the value to use if the equality match fails. This is the next
2143 // mux in the sequence or the "otherwise" value.
2144 auto getTreeValue = [&](MuxOp mux) -> Value {
2145 return mux.getOperand(1 + unsigned(isFalseSide));
2146 };
2147
2148 // Start scanning the mux tree to see what we've got. Keep track of the
2149 // constant comparison value and the SSA value to use when equal to it.
2150 SmallVector<Location> locationsFound;
2151 SmallVector<std::pair<hw::ConstantOp, Value>, 4> valuesFound;
2152
2153 /// Extract constants and values into `valuesFound` and return true if this is
2154 /// part of the mux tree, otherwise return false.
2155 auto collectConstantValues = [&](MuxOp mux) -> bool {
2157 mux.getCond(), indexValue, isFalseSide, [&](hw::ConstantOp cst) {
2158 valuesFound.push_back({cst, getCaseValue(mux)});
2159 locationsFound.push_back(mux.getCond().getLoc());
2160 locationsFound.push_back(mux->getLoc());
2161 });
2162 };
2163
2164 // Make sure the root is a correct comparison with a constant.
2165 if (!collectConstantValues(rootMux))
2166 return false;
2167
2168 // Make sure that we're not looking at the intermediate node in a mux tree.
2169 if (rootMux->hasOneUse()) {
2170 if (auto userMux = dyn_cast<MuxOp>(*rootMux->user_begin())) {
2171 if (getTreeValue(userMux) == rootMux.getResult() &&
2172 getMuxChainCondConstant(userMux.getCond(), indexValue, isFalseSide,
2173 [&](hw::ConstantOp cst) {}))
2174 return false;
2175 }
2176 }
2177
2178 // Scan up the tree linearly.
2179 auto nextTreeValue = getTreeValue(rootMux);
2180 while (1) {
2181 auto nextMux = nextTreeValue.getDefiningOp<MuxOp>();
2182 if (!nextMux || !nextMux->hasOneUse())
2183 break;
2184 if (!collectConstantValues(nextMux))
2185 break;
2186 nextTreeValue = getTreeValue(nextMux);
2187 }
2188
2189 auto indexWidth = cast<IntegerType>(indexValue.getType()).getWidth();
2190
2191 if (indexWidth > 20)
2192 return false; // Too big to make a table.
2193
2194 auto foldingStyle = styleFn(indexWidth, valuesFound.size());
2195 if (foldingStyle == MuxChainWithComparisonFoldingStyle::None)
2196 return false;
2197
2198 uint64_t tableSize = 1ULL << indexWidth;
2199
2200 // Ok, we're going to do the transformation, start by building the table
2201 // filled with the "otherwise" value.
2202 SmallVector<Value, 8> table(tableSize, nextTreeValue);
2203
2204 // Fill in entries in the table from the leaf to the root of the expression.
2205 // This ensures that any duplicate matches end up with the ultimate value,
2206 // which is the one closer to the root.
2207 for (auto &elt : llvm::reverse(valuesFound)) {
2208 uint64_t idx = elt.first.getValue().getZExtValue();
2209 assert(idx < table.size() && "constant should be same bitwidth as index");
2210 table[idx] = elt.second;
2211 }
2212
2214 SmallVector<Value> bits;
2215 comb::extractBits(rewriter, indexValue, bits);
2216 auto result = constructMuxTree(rewriter, rootMux->getLoc(), bits, table,
2217 nextTreeValue);
2218 replaceOpAndCopyNamehint(rewriter, rootMux, result);
2219 return true;
2220 }
2221
2223 "unknown folding style");
2224
2225 // The hw.array_create operation has the operand list in unintuitive order
2226 // with a[0] stored as the last element, not the first.
2227 std::reverse(table.begin(), table.end());
2228
2229 // Build the array_create and the array_get.
2230 auto fusedLoc = rewriter.getFusedLoc(locationsFound);
2231 auto array = hw::ArrayCreateOp::create(rewriter, fusedLoc, table);
2232 replaceOpWithNewOpAndCopyNamehint<hw::ArrayGetOp>(rewriter, rootMux, array,
2233 indexValue);
2234 return true;
2235}
2236
2237/// Given a fully associative variadic operation like (a+b+c+d), break the
2238/// expression into two parts, one without the specified operand (e.g.
2239/// `tmp = a+b+d`) and one that combines that into the full expression (e.g.
2240/// `tmp+c`), and return the inner expression.
2241///
2242/// NOTE: This mutates the operation in place if it only has a single user,
2243/// which assumes that user will be removed.
2244///
2245static Value extractOperandFromFullyAssociative(Operation *fullyAssoc,
2246 size_t operandNo,
2247 PatternRewriter &rewriter) {
2248 assert(fullyAssoc->getNumOperands() >= 2 && "cannot split up unary ops");
2249 assert(operandNo < fullyAssoc->getNumOperands() && "Invalid operand #");
2250
2251 // If this expression already has two operands (the common case) no splitting
2252 // is necessary.
2253 if (fullyAssoc->getNumOperands() == 2)
2254 return fullyAssoc->getOperand(operandNo ^ 1);
2255
2256 // If the operation has a single use, mutate it in place.
2257 if (fullyAssoc->hasOneUse()) {
2258 rewriter.modifyOpInPlace(fullyAssoc,
2259 [&]() { fullyAssoc->eraseOperand(operandNo); });
2260 return fullyAssoc->getResult(0);
2261 }
2262
2263 // Form the new operation with the operands that remain.
2264 SmallVector<Value> operands;
2265 operands.append(fullyAssoc->getOperands().begin(),
2266 fullyAssoc->getOperands().begin() + operandNo);
2267 operands.append(fullyAssoc->getOperands().begin() + operandNo + 1,
2268 fullyAssoc->getOperands().end());
2269 Value opWithoutExcluded = createGenericOp(
2270 fullyAssoc->getLoc(), fullyAssoc->getName(), operands, rewriter);
2271 Value excluded = fullyAssoc->getOperand(operandNo);
2272
2273 Value fullResult =
2274 createGenericOp(fullyAssoc->getLoc(), fullyAssoc->getName(),
2275 ArrayRef<Value>{opWithoutExcluded, excluded}, rewriter);
2276 replaceOpAndCopyNamehint(rewriter, fullyAssoc, fullResult);
2277 return opWithoutExcluded;
2278}
2279
2280/// Fold things like `mux(cond, x|y|z|a, a)` -> `(x|y|z)&replicate(cond)|a` and
2281/// `mux(cond, a, x|y|z|a) -> `(x|y|z)&replicate(~cond) | a` (when isTrueOperand
2282/// is true. Return true on successful transformation, false if not.
2283///
2284/// These are various forms of "predicated ops" that can be handled with a
2285/// replicate/and combination.
2286static bool foldCommonMuxValue(MuxOp op, bool isTrueOperand,
2287 PatternRewriter &rewriter) {
2288 // Check to see the operand in question is an operation. If it is a port,
2289 // we can't simplify it.
2290 Operation *subExpr =
2291 (isTrueOperand ? op.getFalseValue() : op.getTrueValue()).getDefiningOp();
2292 if (!subExpr || subExpr->getNumOperands() < 2)
2293 return false;
2294
2295 // If this isn't an operation we can handle, don't spend energy on it.
2296 if (!isa<AndOp, XorOp, OrOp, MuxOp>(subExpr))
2297 return false;
2298
2299 // Check to see if the common value occurs in the operand list for the
2300 // subexpression op. If so, then we can simplify it.
2301 Value commonValue = isTrueOperand ? op.getTrueValue() : op.getFalseValue();
2302 size_t opNo = 0, e = subExpr->getNumOperands();
2303 while (opNo != e && subExpr->getOperand(opNo) != commonValue)
2304 ++opNo;
2305 if (opNo == e)
2306 return false;
2307
2308 // If we got a hit, then go ahead and simplify it!
2309 Value cond = op.getCond();
2310
2311 // `mux(cond, a, mux(cond2, a, b))` -> `mux(cond|cond2, a, b)`
2312 // `mux(cond, a, mux(cond2, b, a))` -> `mux(cond|~cond2, a, b)`
2313 // `mux(cond, mux(cond2, a, b), a)` -> `mux(~cond|cond2, a, b)`
2314 // `mux(cond, mux(cond2, b, a), a)` -> `mux(~cond|~cond2, a, b)`
2315 if (auto subMux = dyn_cast<MuxOp>(subExpr)) {
2316 if (subMux == op)
2317 return false;
2318
2319 Value otherValue;
2320 Value subCond = subMux.getCond();
2321
2322 // Invert th subCond if needed and dig out the 'b' value.
2323 if (subMux.getTrueValue() == commonValue)
2324 otherValue = subMux.getFalseValue();
2325 else if (subMux.getFalseValue() == commonValue) {
2326 otherValue = subMux.getTrueValue();
2327 subCond = createOrFoldNot(rewriter, op.getLoc(), subCond);
2328 } else {
2329 // We can't fold `mux(cond, a, mux(a, x, y))`.
2330 return false;
2331 }
2332
2333 // Invert the outer cond if needed, and combine the mux conditions.
2334 if (!isTrueOperand)
2335 cond = createOrFoldNot(rewriter, op.getLoc(), cond);
2336 cond = rewriter.createOrFold<OrOp>(op.getLoc(), cond, subCond, false);
2337 replaceOpWithNewOpAndCopyNamehint<MuxOp>(rewriter, op, cond, commonValue,
2338 otherValue, op.getTwoState());
2339 return true;
2340 }
2341
2342 // Invert the condition if needed. Or/Xor invert when dealing with
2343 // TrueOperand, And inverts for False operand.
2344 bool isaAndOp = isa<AndOp>(subExpr);
2345 if (isTrueOperand ^ isaAndOp)
2346 cond = createOrFoldNot(rewriter, op.getLoc(), cond);
2347
2348 auto extendedCond =
2349 rewriter.createOrFold<ReplicateOp>(op.getLoc(), op.getType(), cond);
2350
2351 // Cache this information before subExpr is erased by extraction below.
2352 bool isaXorOp = isa<XorOp>(subExpr);
2353 bool isaOrOp = isa<OrOp>(subExpr);
2354
2355 // Handle the fully associative ops, start by pulling out the subexpression
2356 // from a many operand version of the op.
2357 auto restOfAssoc =
2358 extractOperandFromFullyAssociative(subExpr, opNo, rewriter);
2359
2360 // `mux(cond, x|y|z|a, a)` -> `(x|y|z)&replicate(cond) | a`
2361 // `mux(cond, x^y^z^a, a)` -> `(x^y^z)&replicate(cond) ^ a`
2362 if (isaOrOp || isaXorOp) {
2363 auto masked = rewriter.createOrFold<AndOp>(op.getLoc(), extendedCond,
2364 restOfAssoc, false);
2365 if (isaXorOp)
2366 replaceOpWithNewOpAndCopyNamehint<XorOp>(rewriter, op, masked,
2367 commonValue, false);
2368 else
2369 replaceOpWithNewOpAndCopyNamehint<OrOp>(rewriter, op, masked, commonValue,
2370 false);
2371 return true;
2372 }
2373
2374 // `mux(cond, a, x&y&z&a)` -> `((x&y&z)|replicate(cond)) & a`
2375 assert(isaAndOp && "unexpected operation here");
2376 auto masked = rewriter.createOrFold<OrOp>(op.getLoc(), extendedCond,
2377 restOfAssoc, false);
2378 replaceOpWithNewOpAndCopyNamehint<AndOp>(rewriter, op, masked, commonValue,
2379 false);
2380 return true;
2381}
2382
2383/// This function is invoke when we find a mux with true/false operations that
2384/// have the same opcode. Check to see if we can strength reduce the mux by
2385/// applying it to less data by applying this transformation:
2386/// `mux(cond, op(a, b), op(a, c))` -> `op(a, mux(cond, b, c))`
2387static bool foldCommonMuxOperation(MuxOp mux, Operation *trueOp,
2388 Operation *falseOp,
2389 PatternRewriter &rewriter) {
2390 // Right now we only apply to concat.
2391 // TODO: Generalize this to and, or, xor, icmp(!), which all occur in practice
2392 if (!isa<ConcatOp>(trueOp))
2393 return false;
2394
2395 // Decode the operands, looking through recursive concats and replicates.
2396 SmallVector<Value> trueOperands, falseOperands;
2397 getConcatOperands(trueOp->getResult(0), trueOperands);
2398 getConcatOperands(falseOp->getResult(0), falseOperands);
2399
2400 size_t numTrueOperands = trueOperands.size();
2401 size_t numFalseOperands = falseOperands.size();
2402
2403 if (!numTrueOperands || !numFalseOperands ||
2404 (trueOperands.front() != falseOperands.front() &&
2405 trueOperands.back() != falseOperands.back()))
2406 return false;
2407
2408 // Pull all leading shared operands out into their own op if any are common.
2409 if (trueOperands.front() == falseOperands.front()) {
2410 SmallVector<Value> operands;
2411 size_t i;
2412 for (i = 0; i < numTrueOperands; ++i) {
2413 Value trueOperand = trueOperands[i];
2414 if (trueOperand == falseOperands[i])
2415 operands.push_back(trueOperand);
2416 else
2417 break;
2418 }
2419 if (i == numTrueOperands) {
2420 // Selecting between distinct, but lexically identical, concats.
2421 replaceOpAndCopyNamehint(rewriter, mux, trueOp->getResult(0));
2422 return true;
2423 }
2424
2425 Value sharedMSB;
2426 if (llvm::all_of(operands, [&](Value v) { return v == operands.front(); }))
2427 sharedMSB = rewriter.createOrFold<ReplicateOp>(
2428 mux->getLoc(), operands.front(), operands.size());
2429 else
2430 sharedMSB = rewriter.createOrFold<ConcatOp>(mux->getLoc(), operands);
2431 operands.clear();
2432
2433 // Get a concat of the LSB's on each side.
2434 operands.append(trueOperands.begin() + i, trueOperands.end());
2435 Value trueLSB = rewriter.createOrFold<ConcatOp>(trueOp->getLoc(), operands);
2436 operands.clear();
2437 operands.append(falseOperands.begin() + i, falseOperands.end());
2438 Value falseLSB =
2439 rewriter.createOrFold<ConcatOp>(falseOp->getLoc(), operands);
2440 // Merge the LSBs with a new mux and concat the MSB with the LSB to be
2441 // done.
2442 Value lsb = rewriter.createOrFold<MuxOp>(
2443 mux->getLoc(), mux.getCond(), trueLSB, falseLSB, mux.getTwoState());
2444 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(rewriter, mux, sharedMSB, lsb);
2445 return true;
2446 }
2447
2448 // If trailing operands match, try to commonize them.
2449 if (trueOperands.back() == falseOperands.back()) {
2450 SmallVector<Value> operands;
2451 size_t i;
2452 for (i = 0;; ++i) {
2453 Value trueOperand = trueOperands[numTrueOperands - i - 1];
2454 if (trueOperand == falseOperands[numFalseOperands - i - 1])
2455 operands.push_back(trueOperand);
2456 else
2457 break;
2458 }
2459 std::reverse(operands.begin(), operands.end());
2460 Value sharedLSB = rewriter.createOrFold<ConcatOp>(mux->getLoc(), operands);
2461 operands.clear();
2462
2463 // Get a concat of the MSB's on each side.
2464 operands.append(trueOperands.begin(), trueOperands.end() - i);
2465 Value trueMSB = rewriter.createOrFold<ConcatOp>(trueOp->getLoc(), operands);
2466 operands.clear();
2467 operands.append(falseOperands.begin(), falseOperands.end() - i);
2468 Value falseMSB =
2469 rewriter.createOrFold<ConcatOp>(falseOp->getLoc(), operands);
2470 // Merge the MSBs with a new mux and concat the MSB with the LSB to be done.
2471 Value msb = rewriter.createOrFold<MuxOp>(
2472 mux->getLoc(), mux.getCond(), trueMSB, falseMSB, mux.getTwoState());
2473 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(rewriter, mux, msb, sharedLSB);
2474 return true;
2475 }
2476
2477 return false;
2478}
2479
2480// If both arguments of the mux are arrays with the same elements, sink the
2481// mux and return a uniform array initializing all elements to it.
2482static bool foldMuxOfUniformArrays(MuxOp op, PatternRewriter &rewriter) {
2483 auto trueVec = op.getTrueValue().getDefiningOp<hw::ArrayCreateOp>();
2484 auto falseVec = op.getFalseValue().getDefiningOp<hw::ArrayCreateOp>();
2485 if (!trueVec || !falseVec)
2486 return false;
2487 if (!trueVec.isUniform() || !falseVec.isUniform())
2488 return false;
2489
2490 auto mux = MuxOp::create(rewriter, op.getLoc(), op.getCond(),
2491 trueVec.getUniformElement(),
2492 falseVec.getUniformElement(), op.getTwoState());
2493
2494 SmallVector<Value> values(trueVec.getInputs().size(), mux);
2495 rewriter.replaceOpWithNewOp<hw::ArrayCreateOp>(op, values);
2496 return true;
2497}
2498
2499/// If the mux condition is an operand to the op defining its true or false
2500/// value, replace the condition with 1 or 0.
2501static bool assumeMuxCondInOperand(Value muxCond, Value muxValue,
2502 bool constCond, PatternRewriter &rewriter) {
2503 if (!muxValue.hasOneUse())
2504 return false;
2505 auto *op = muxValue.getDefiningOp();
2506 if (!op || !isa_and_nonnull<CombDialect>(op->getDialect()))
2507 return false;
2508 if (!llvm::is_contained(op->getOperands(), muxCond))
2509 return false;
2510 OpBuilder::InsertionGuard guard(rewriter);
2511 rewriter.setInsertionPoint(op);
2512 auto condValue =
2513 hw::ConstantOp::create(rewriter, muxCond.getLoc(), APInt(1, constCond));
2514 rewriter.modifyOpInPlace(op, [&] {
2515 for (auto &use : op->getOpOperands())
2516 if (use.get() == muxCond)
2517 use.set(condValue);
2518 });
2519 return true;
2520}
2521
2522namespace {
2523struct MuxRewriter : public mlir::OpRewritePattern<MuxOp> {
2524 using OpRewritePattern::OpRewritePattern;
2525
2526 LogicalResult matchAndRewrite(MuxOp op,
2527 PatternRewriter &rewriter) const override;
2528};
2529
2531foldToArrayCreateOnlyWhenDense(size_t indexWidth, size_t numEntries) {
2532 // If the array is greater that 9 bits, it will take over 512 elements and
2533 // it will be too large for a single expression.
2534 if (indexWidth >= 9 || numEntries < 3)
2536
2537 // Next we need to see if the values are dense-ish. We don't want to have
2538 // a tremendous number of replicated entries in the array. Some sparsity is
2539 // ok though, so we require the table to be at least 5/8 utilized.
2540 uint64_t tableSize = 1ULL << indexWidth;
2541 if (numEntries >= tableSize * 5 / 8)
2544}
2545
2546LogicalResult MuxRewriter::matchAndRewrite(MuxOp op,
2547 PatternRewriter &rewriter) const {
2548 if (isOpTriviallyRecursive(op))
2549 return failure();
2550
2551 bool isSignlessInt = false;
2552 if (auto intType = dyn_cast<IntegerType>(op.getType()))
2553 isSignlessInt = intType.isSignless();
2554
2555 // If the op has a SV attribute, don't optimize it.
2556 if (hasSVAttributes(op))
2557 return failure();
2558 APInt value;
2559
2560 if (matchPattern(op.getTrueValue(), m_ConstantInt(&value)) && isSignlessInt) {
2561 if (value.getBitWidth() == 1) {
2562 // mux(a, 0, b) -> and(~a, b) for single-bit values.
2563 if (value.isZero()) {
2564 auto notCond = createOrFoldNot(rewriter, op.getLoc(), op.getCond());
2565 replaceOpWithNewOpAndCopyNamehint<AndOp>(rewriter, op, notCond,
2566 op.getFalseValue(), false);
2567 return success();
2568 }
2569
2570 // mux(a, 1, b) -> or(a, b) for single-bit values.
2571 replaceOpWithNewOpAndCopyNamehint<OrOp>(rewriter, op, op.getCond(),
2572 op.getFalseValue(), false);
2573 return success();
2574 }
2575
2576 // Check for mux of two constants. There are many ways to simplify them.
2577 APInt value2;
2578 if (matchPattern(op.getFalseValue(), m_ConstantInt(&value2))) {
2579 // When both inputs are constants and differ by only one bit, we can
2580 // simplify by splitting the mux into up to three contiguous chunks: one
2581 // for the differing bit and up to two for the bits that are the same.
2582 // E.g. mux(a, 3'h2, 0) -> concat(0, mux(a, 1, 0), 0) -> concat(0, a, 0)
2583 APInt xorValue = value ^ value2;
2584 if (xorValue.isPowerOf2()) {
2585 unsigned leadingZeros = xorValue.countLeadingZeros();
2586 unsigned trailingZeros = value.getBitWidth() - leadingZeros - 1;
2587 SmallVector<Value, 3> operands;
2588
2589 // Concat operands go from MSB to LSB, so we handle chunks in reverse
2590 // order of bit indexes.
2591 // For the chunks that are identical (i.e. correspond to 0s in
2592 // xorValue), we can extract directly from either input value, and we
2593 // arbitrarily pick the trueValue().
2594
2595 if (leadingZeros > 0)
2596 operands.push_back(rewriter.createOrFold<ExtractOp>(
2597 op.getLoc(), op.getTrueValue(), trailingZeros + 1, leadingZeros));
2598
2599 // Handle the differing bit, which should simplify into either cond or
2600 // ~cond.
2601 auto v1 = rewriter.createOrFold<ExtractOp>(
2602 op.getLoc(), op.getTrueValue(), trailingZeros, 1);
2603 auto v2 = rewriter.createOrFold<ExtractOp>(
2604 op.getLoc(), op.getFalseValue(), trailingZeros, 1);
2605 operands.push_back(rewriter.createOrFold<MuxOp>(
2606 op.getLoc(), op.getCond(), v1, v2, false));
2607
2608 if (trailingZeros > 0)
2609 operands.push_back(rewriter.createOrFold<ExtractOp>(
2610 op.getLoc(), op.getTrueValue(), 0, trailingZeros));
2611
2612 replaceOpWithNewOpAndCopyNamehint<ConcatOp>(rewriter, op, op.getType(),
2613 operands);
2614 return success();
2615 }
2616
2617 // If the true value is all ones and the false is all zeros then we have a
2618 // replicate pattern.
2619 if (value.isAllOnes() && value2.isZero()) {
2620 replaceOpWithNewOpAndCopyNamehint<ReplicateOp>(
2621 rewriter, op, op.getType(), op.getCond());
2622 return success();
2623 }
2624 }
2625 }
2626
2627 if (matchPattern(op.getFalseValue(), m_ConstantInt(&value)) &&
2628 isSignlessInt && value.getBitWidth() == 1) {
2629 // mux(a, b, 0) -> and(a, b) for single-bit values.
2630 if (value.isZero()) {
2631 replaceOpWithNewOpAndCopyNamehint<AndOp>(rewriter, op, op.getCond(),
2632 op.getTrueValue(), false);
2633 return success();
2634 }
2635
2636 // mux(a, b, 1) -> or(~a, b) for single-bit values.
2637 // falseValue() is known to be a single-bit 1, which we can use for
2638 // the 1 in the representation of ~ using xor.
2639 auto notCond = rewriter.createOrFold<XorOp>(op.getLoc(), op.getCond(),
2640 op.getFalseValue(), false);
2641 replaceOpWithNewOpAndCopyNamehint<OrOp>(rewriter, op, notCond,
2642 op.getTrueValue(), false);
2643 return success();
2644 }
2645
2646 // mux(!a, b, c) -> mux(a, c, b)
2647 Value subExpr;
2648 Operation *condOp = op.getCond().getDefiningOp();
2649 if (condOp && matchPattern(condOp, m_Complement(m_Any(&subExpr))) &&
2650 op.getTwoState()) {
2651 replaceOpWithNewOpAndCopyNamehint<MuxOp>(rewriter, op, op.getType(),
2652 subExpr, op.getFalseValue(),
2653 op.getTrueValue(), true);
2654 return success();
2655 }
2656
2657 // Same but with Demorgan's law.
2658 // mux(and(~a, ~b, ~c), x, y) -> mux(or(a, b, c), y, x)
2659 // mux(or(~a, ~b, ~c), x, y) -> mux(and(a, b, c), y, x)
2660 if (condOp && condOp->hasOneUse()) {
2661 SmallVector<Value> invertedOperands;
2662
2663 /// Scan all the operands to see if they are complemented. If so, build a
2664 /// vector of them and return true, otherwise return false.
2665 auto getInvertedOperands = [&]() -> bool {
2666 for (Value operand : condOp->getOperands()) {
2667 if (matchPattern(operand, m_Complement(m_Any(&subExpr))))
2668 invertedOperands.push_back(subExpr);
2669 else
2670 return false;
2671 }
2672 return true;
2673 };
2674
2675 if (isa<AndOp>(condOp) && getInvertedOperands()) {
2676 auto newOr =
2677 rewriter.createOrFold<OrOp>(op.getLoc(), invertedOperands, false);
2678 replaceOpWithNewOpAndCopyNamehint<MuxOp>(
2679 rewriter, op, newOr, op.getFalseValue(), op.getTrueValue(),
2680 op.getTwoState());
2681 return success();
2682 }
2683 if (isa<OrOp>(condOp) && getInvertedOperands()) {
2684 auto newAnd =
2685 rewriter.createOrFold<AndOp>(op.getLoc(), invertedOperands, false);
2686 replaceOpWithNewOpAndCopyNamehint<MuxOp>(
2687 rewriter, op, newAnd, op.getFalseValue(), op.getTrueValue(),
2688 op.getTwoState());
2689 return success();
2690 }
2691 }
2692
2693 if (auto falseMux = op.getFalseValue().getDefiningOp<MuxOp>();
2694 falseMux && falseMux != op) {
2695 // mux(selector, x, mux(selector, y, z) = mux(selector, x, z)
2696 if (op.getCond() == falseMux.getCond() &&
2697 falseMux.getFalseValue() != falseMux) {
2698 replaceOpWithNewOpAndCopyNamehint<MuxOp>(
2699 rewriter, op, op.getCond(), op.getTrueValue(),
2700 falseMux.getFalseValue(), op.getTwoStateAttr());
2701 return success();
2702 }
2703
2704 // Check to see if we can fold a mux tree into an array_create/get pair.
2705 if (foldMuxChainWithComparison(rewriter, op, /*isFalse*/ true,
2706 foldToArrayCreateOnlyWhenDense))
2707 return success();
2708 }
2709
2710 if (auto trueMux = op.getTrueValue().getDefiningOp<MuxOp>();
2711 trueMux && trueMux != op) {
2712 // mux(selector, mux(selector, a, b), c) = mux(selector, a, c)
2713 if (op.getCond() == trueMux.getCond()) {
2714 replaceOpWithNewOpAndCopyNamehint<MuxOp>(
2715 rewriter, op, op.getCond(), trueMux.getTrueValue(),
2716 op.getFalseValue(), op.getTwoStateAttr());
2717 return success();
2718 }
2719
2720 // Check to see if we can fold a mux tree into an array_create/get pair.
2721 if (foldMuxChainWithComparison(rewriter, op, /*isFalseSide*/ false,
2722 foldToArrayCreateOnlyWhenDense))
2723 return success();
2724 }
2725
2726 // mux(c1, mux(c2, a, b), mux(c2, a, c)) -> mux(c2, a, mux(c1, b, c))
2727 if (auto trueMux = dyn_cast_or_null<MuxOp>(op.getTrueValue().getDefiningOp()),
2728 falseMux = dyn_cast_or_null<MuxOp>(op.getFalseValue().getDefiningOp());
2729 trueMux && falseMux && trueMux.getCond() == falseMux.getCond() &&
2730 trueMux.getTrueValue() == falseMux.getTrueValue() && trueMux != op &&
2731 falseMux != op) {
2732 auto subMux = MuxOp::create(
2733 rewriter, rewriter.getFusedLoc({trueMux.getLoc(), falseMux.getLoc()}),
2734 op.getCond(), trueMux.getFalseValue(), falseMux.getFalseValue());
2735 replaceOpWithNewOpAndCopyNamehint<MuxOp>(rewriter, op, trueMux.getCond(),
2736 trueMux.getTrueValue(), subMux,
2737 op.getTwoStateAttr());
2738 return success();
2739 }
2740
2741 // mux(c1, mux(c2, a, b), mux(c2, c, b)) -> mux(c2, mux(c1, a, c), b)
2742 if (auto trueMux = dyn_cast_or_null<MuxOp>(op.getTrueValue().getDefiningOp()),
2743 falseMux = dyn_cast_or_null<MuxOp>(op.getFalseValue().getDefiningOp());
2744 trueMux && falseMux && trueMux.getCond() == falseMux.getCond() &&
2745 trueMux.getFalseValue() == falseMux.getFalseValue() && trueMux != op &&
2746 falseMux != op) {
2747 auto subMux = MuxOp::create(
2748 rewriter, rewriter.getFusedLoc({trueMux.getLoc(), falseMux.getLoc()}),
2749 op.getCond(), trueMux.getTrueValue(), falseMux.getTrueValue());
2750 replaceOpWithNewOpAndCopyNamehint<MuxOp>(rewriter, op, trueMux.getCond(),
2751 subMux, trueMux.getFalseValue(),
2752 op.getTwoStateAttr());
2753 return success();
2754 }
2755
2756 // mux(c1, mux(c2, a, b), mux(c3, a, b)) -> mux(mux(c1, c2, c3), a, b)
2757 if (auto trueMux = dyn_cast_or_null<MuxOp>(op.getTrueValue().getDefiningOp()),
2758 falseMux = dyn_cast_or_null<MuxOp>(op.getFalseValue().getDefiningOp());
2759 trueMux && falseMux &&
2760 trueMux.getTrueValue() == falseMux.getTrueValue() &&
2761 trueMux.getFalseValue() == falseMux.getFalseValue() && trueMux != op &&
2762 falseMux != op) {
2763 auto subMux =
2764 MuxOp::create(rewriter,
2765 rewriter.getFusedLoc(
2766 {op.getLoc(), trueMux.getLoc(), falseMux.getLoc()}),
2767 op.getCond(), trueMux.getCond(), falseMux.getCond());
2768 replaceOpWithNewOpAndCopyNamehint<MuxOp>(
2769 rewriter, op, subMux, trueMux.getTrueValue(), trueMux.getFalseValue(),
2770 op.getTwoStateAttr());
2771 return success();
2772 }
2773
2774 // mux(cond, x|y|z|a, a) -> (x|y|z)&replicate(cond) | a
2775 if (foldCommonMuxValue(op, false, rewriter))
2776 return success();
2777 // mux(cond, a, x|y|z|a) -> (x|y|z)&replicate(~cond) | a
2778 if (foldCommonMuxValue(op, true, rewriter))
2779 return success();
2780
2781 // `mux(cond, op(a, b), op(a, c))` -> `op(a, mux(cond, b, c))`
2782 if (Operation *trueOp = op.getTrueValue().getDefiningOp())
2783 if (Operation *falseOp = op.getFalseValue().getDefiningOp())
2784 if (trueOp->getName() == falseOp->getName())
2785 if (foldCommonMuxOperation(op, trueOp, falseOp, rewriter))
2786 return success();
2787
2788 // extracts only of mux(...) -> mux(extract()...)
2789 if (narrowOperationWidth(op, true, rewriter))
2790 return success();
2791
2792 // mux(cond, repl(n, a1), repl(n, a2)) -> repl(n, mux(cond, a1, a2))
2793 if (foldMuxOfUniformArrays(op, rewriter))
2794 return success();
2795
2796 // mux(cond, opA(cond), opB(cond)) -> mux(cond, opA(1), opB(0))
2797 if (op.getTrueValue().getDefiningOp() &&
2798 op.getTrueValue().getDefiningOp() != op)
2799 if (assumeMuxCondInOperand(op.getCond(), op.getTrueValue(), true, rewriter))
2800 return success();
2801 if (op.getFalseValue().getDefiningOp() &&
2802 op.getFalseValue().getDefiningOp() != op)
2803
2804 if (assumeMuxCondInOperand(op.getCond(), op.getFalseValue(), false,
2805 rewriter))
2806 return success();
2807
2808 return failure();
2809}
2810
2811static bool foldArrayOfMuxes(hw::ArrayCreateOp op, PatternRewriter &rewriter) {
2812 // Do not fold uniform or singleton arrays to avoid duplicating muxes.
2813 if (op.getInputs().empty() || op.isUniform())
2814 return false;
2815 auto inputs = op.getInputs();
2816 if (inputs.size() <= 1)
2817 return false;
2818
2819 // Check the operands to the array create. Ensure all of them are the
2820 // same op with the same number of operands.
2821 auto first = inputs[0].getDefiningOp<comb::MuxOp>();
2822 if (!first || hasSVAttributes(first))
2823 return false;
2824
2825 // Check whether all operands are muxes with the same condition.
2826 for (size_t i = 1, n = inputs.size(); i < n; ++i) {
2827 auto input = inputs[i].getDefiningOp<comb::MuxOp>();
2828 if (!input || first.getCond() != input.getCond())
2829 return false;
2830 }
2831
2832 // Collect the true and the false branches into arrays.
2833 SmallVector<Value> trues{first.getTrueValue()};
2834 SmallVector<Value> falses{first.getFalseValue()};
2835 SmallVector<Location> locs{first->getLoc()};
2836 bool isTwoState = true;
2837 for (size_t i = 1, n = inputs.size(); i < n; ++i) {
2838 auto input = inputs[i].getDefiningOp<comb::MuxOp>();
2839 trues.push_back(input.getTrueValue());
2840 falses.push_back(input.getFalseValue());
2841 locs.push_back(input->getLoc());
2842 if (!input.getTwoState())
2843 isTwoState = false;
2844 }
2845
2846 // Define the location of the array create as the aggregate of all muxes.
2847 auto loc = FusedLoc::get(op.getContext(), locs);
2848
2849 // Replace the create with an aggregate operation. Push the create op
2850 // into the operands of the aggregate operation.
2851 auto arrayTy = op.getType();
2852 auto trueValues = hw::ArrayCreateOp::create(rewriter, loc, arrayTy, trues);
2853 auto falseValues = hw::ArrayCreateOp::create(rewriter, loc, arrayTy, falses);
2854 rewriter.replaceOpWithNewOp<comb::MuxOp>(op, arrayTy, first.getCond(),
2855 trueValues, falseValues, isTwoState);
2856 return true;
2857}
2858
2859struct ArrayRewriter : public mlir::OpRewritePattern<hw::ArrayCreateOp> {
2860 using OpRewritePattern::OpRewritePattern;
2861
2862 LogicalResult matchAndRewrite(hw::ArrayCreateOp op,
2863 PatternRewriter &rewriter) const override {
2864 if (foldArrayOfMuxes(op, rewriter))
2865 return success();
2866 return failure();
2867 }
2868};
2869
2870} // namespace
2871
2872void MuxOp::getCanonicalizationPatterns(RewritePatternSet &results,
2873 MLIRContext *context) {
2874 results.insert<MuxRewriter, ArrayRewriter>(context);
2875}
2876
2877//===----------------------------------------------------------------------===//
2878// ICmpOp
2879//===----------------------------------------------------------------------===//
2880
2881// Calculate the result of a comparison when the LHS and RHS are both
2882// constants.
2883static bool applyCmpPredicate(ICmpPredicate predicate, const APInt &lhs,
2884 const APInt &rhs) {
2885 switch (predicate) {
2886 case ICmpPredicate::eq:
2887 return lhs.eq(rhs);
2888 case ICmpPredicate::ne:
2889 return lhs.ne(rhs);
2890 case ICmpPredicate::slt:
2891 return lhs.slt(rhs);
2892 case ICmpPredicate::sle:
2893 return lhs.sle(rhs);
2894 case ICmpPredicate::sgt:
2895 return lhs.sgt(rhs);
2896 case ICmpPredicate::sge:
2897 return lhs.sge(rhs);
2898 case ICmpPredicate::ult:
2899 return lhs.ult(rhs);
2900 case ICmpPredicate::ule:
2901 return lhs.ule(rhs);
2902 case ICmpPredicate::ugt:
2903 return lhs.ugt(rhs);
2904 case ICmpPredicate::uge:
2905 return lhs.uge(rhs);
2906 case ICmpPredicate::ceq:
2907 return lhs.eq(rhs);
2908 case ICmpPredicate::cne:
2909 return lhs.ne(rhs);
2910 case ICmpPredicate::weq:
2911 return lhs.eq(rhs);
2912 case ICmpPredicate::wne:
2913 return lhs.ne(rhs);
2914 }
2915 llvm_unreachable("unknown comparison predicate");
2916}
2917
2918// Returns the result of applying the predicate when the LHS and RHS are the
2919// exact same value.
2920static bool applyCmpPredicateToEqualOperands(ICmpPredicate predicate) {
2921 switch (predicate) {
2922 case ICmpPredicate::eq:
2923 case ICmpPredicate::sle:
2924 case ICmpPredicate::sge:
2925 case ICmpPredicate::ule:
2926 case ICmpPredicate::uge:
2927 case ICmpPredicate::ceq:
2928 case ICmpPredicate::weq:
2929 return true;
2930 case ICmpPredicate::ne:
2931 case ICmpPredicate::slt:
2932 case ICmpPredicate::sgt:
2933 case ICmpPredicate::ult:
2934 case ICmpPredicate::ugt:
2935 case ICmpPredicate::cne:
2936 case ICmpPredicate::wne:
2937 return false;
2938 }
2939 llvm_unreachable("unknown comparison predicate");
2940}
2941
2942OpFoldResult ICmpOp::fold(FoldAdaptor adaptor) {
2943 // gt a, a -> false
2944 // gte a, a -> true
2945 if (getLhs() == getRhs()) {
2946 auto val = applyCmpPredicateToEqualOperands(getPredicate());
2947 return IntegerAttr::get(getType(), val);
2948 }
2949
2950 // gt 1, 2 -> false
2951 if (auto lhs = dyn_cast_or_null<IntegerAttr>(adaptor.getLhs())) {
2952 if (auto rhs = dyn_cast_or_null<IntegerAttr>(adaptor.getRhs())) {
2953 auto val =
2954 applyCmpPredicate(getPredicate(), lhs.getValue(), rhs.getValue());
2955 return IntegerAttr::get(getType(), val);
2956 }
2957 }
2958 return {};
2959}
2960
2961// Given a range of operands, computes the number of matching prefix and
2962// suffix elements. This does not perform cross-element matching.
2963template <typename Range>
2964static size_t computeCommonPrefixLength(const Range &a, const Range &b) {
2965 size_t commonPrefixLength = 0;
2966 auto ia = a.begin();
2967 auto ib = b.begin();
2968
2969 for (; ia != a.end() && ib != b.end(); ia++, ib++, commonPrefixLength++) {
2970 if (*ia != *ib) {
2971 break;
2972 }
2973 }
2974
2975 return commonPrefixLength;
2976}
2977
2978static size_t getTotalWidth(ArrayRef<Value> operands) {
2979 size_t totalWidth = 0;
2980 for (auto operand : operands) {
2981 // getIntOrFloatBitWidth should never raise, since all arguments to
2982 // ConcatOp are integers.
2983 ssize_t width = operand.getType().getIntOrFloatBitWidth();
2984 assert(width >= 0);
2985 totalWidth += width;
2986 }
2987 return totalWidth;
2988}
2989
2990/// Reduce the strength icmp(concat(...), concat(...)) by doing a element-wise
2991/// comparison on common prefix and suffixes. Returns success() if a rewriting
2992/// happens. This handles both concat and replicate.
2993static LogicalResult matchAndRewriteCompareConcat(ICmpOp op, Operation *lhs,
2994 Operation *rhs,
2995 PatternRewriter &rewriter) {
2996 // It is safe to assume that [{lhsOperands, rhsOperands}.size() > 0] and
2997 // all elements have non-zero length. Both these invariants are verified
2998 // by the ConcatOp verifier.
2999 SmallVector<Value> lhsOperands, rhsOperands;
3000 getConcatOperands(lhs->getResult(0), lhsOperands);
3001 getConcatOperands(rhs->getResult(0), rhsOperands);
3002 ArrayRef<Value> lhsOperandsRef = lhsOperands, rhsOperandsRef = rhsOperands;
3003
3004 auto formCatOrReplicate = [&](Location loc,
3005 ArrayRef<Value> operands) -> Value {
3006 assert(!operands.empty());
3007 Value sameElement = operands[0];
3008 for (size_t i = 1, e = operands.size(); i != e && sameElement; ++i)
3009 if (sameElement != operands[i])
3010 sameElement = Value();
3011 if (sameElement)
3012 return rewriter.createOrFold<ReplicateOp>(loc, sameElement,
3013 operands.size());
3014 return rewriter.createOrFold<ConcatOp>(loc, operands);
3015 };
3016
3017 auto replaceWith = [&](ICmpPredicate predicate, Value lhs,
3018 Value rhs) -> LogicalResult {
3019 replaceOpWithNewOpAndCopyNamehint<ICmpOp>(rewriter, op, predicate, lhs, rhs,
3020 op.getTwoState());
3021 return success();
3022 };
3023
3024 size_t commonPrefixLength =
3025 computeCommonPrefixLength(lhsOperands, rhsOperands);
3026 if (commonPrefixLength == lhsOperands.size()) {
3027 // cat(a, b, c) == cat(a, b, c) -> 1
3028 bool result = applyCmpPredicateToEqualOperands(op.getPredicate());
3029 replaceOpWithNewOpAndCopyNamehint<hw::ConstantOp>(rewriter, op,
3030 APInt(1, result));
3031 return success();
3032 }
3033
3034 size_t commonSuffixLength = computeCommonPrefixLength(
3035 llvm::reverse(lhsOperandsRef), llvm::reverse(rhsOperandsRef));
3036
3037 size_t commonPrefixTotalWidth =
3038 getTotalWidth(lhsOperandsRef.take_front(commonPrefixLength));
3039 size_t commonSuffixTotalWidth =
3040 getTotalWidth(lhsOperandsRef.take_back(commonSuffixLength));
3041 auto lhsOnly = lhsOperandsRef.drop_front(commonPrefixLength)
3042 .drop_back(commonSuffixLength);
3043 auto rhsOnly = rhsOperandsRef.drop_front(commonPrefixLength)
3044 .drop_back(commonSuffixLength);
3045
3046 auto replaceWithoutReplicatingSignBit = [&]() {
3047 auto newLhs = formCatOrReplicate(lhs->getLoc(), lhsOnly);
3048 auto newRhs = formCatOrReplicate(rhs->getLoc(), rhsOnly);
3049 return replaceWith(op.getPredicate(), newLhs, newRhs);
3050 };
3051
3052 auto replaceWithReplicatingSignBit = [&]() {
3053 auto firstNonEmptyValue = lhsOperands[0];
3054 auto firstNonEmptyElemWidth =
3055 firstNonEmptyValue.getType().getIntOrFloatBitWidth();
3056 Value signBit = rewriter.createOrFold<ExtractOp>(
3057 op.getLoc(), firstNonEmptyValue, firstNonEmptyElemWidth - 1, 1);
3058
3059 auto newLhs = ConcatOp::create(rewriter, lhs->getLoc(), signBit, lhsOnly);
3060 auto newRhs = ConcatOp::create(rewriter, rhs->getLoc(), signBit, rhsOnly);
3061 return replaceWith(op.getPredicate(), newLhs, newRhs);
3062 };
3063
3064 if (ICmpOp::isPredicateSigned(op.getPredicate())) {
3065 // scmp(cat(..x, b), cat(..y, b)) == scmp(cat(..x), cat(..y))
3066 if (commonPrefixTotalWidth == 0 && commonSuffixTotalWidth > 0)
3067 return replaceWithoutReplicatingSignBit();
3068
3069 // scmp(cat(a, ..x, b), cat(a, ..y, b)) == scmp(cat(sgn(a), ..x),
3070 // cat(sgn(b), ..y)) Note that we cannot perform this optimization if
3071 // [width(b) = 0 && width(a) <= 1]. since that common prefix is the sign
3072 // bit. Doing the rewrite can result in an infinite loop.
3073 if (commonPrefixTotalWidth > 1 || commonSuffixTotalWidth > 0)
3074 return replaceWithReplicatingSignBit();
3075
3076 } else if (commonPrefixTotalWidth > 0 || commonSuffixTotalWidth > 0) {
3077 // ucmp(cat(a, ..x, b), cat(a, ..y, b)) = ucmp(cat(..x), cat(..y))
3078 return replaceWithoutReplicatingSignBit();
3079 }
3080
3081 return failure();
3082}
3083
3084/// Given an equality comparison with a constant value and some operand that has
3085/// known bits, simplify the comparison to check only the unknown bits of the
3086/// input.
3087///
3088/// One simple example of this is that `concat(0, stuff) == 0` can be simplified
3089/// to `stuff == 0`, or `and(x, 3) == 0` can be simplified to
3090/// `extract x[1:0] == 0`
3092 ICmpOp cmpOp, const KnownBits &bitAnalysis, const APInt &rhsCst,
3093 PatternRewriter &rewriter) {
3094
3095 // If any of the known bits disagree with any of the comparison bits, then
3096 // we can constant fold this comparison right away.
3097 APInt bitsKnown = bitAnalysis.Zero | bitAnalysis.One;
3098 if ((bitsKnown & rhsCst) != bitAnalysis.One) {
3099 // If we discover a mismatch then we know an "eq" comparison is false
3100 // and a "ne" comparison is true!
3101 bool result = cmpOp.getPredicate() == ICmpPredicate::ne;
3102 replaceOpWithNewOpAndCopyNamehint<hw::ConstantOp>(rewriter, cmpOp,
3103 APInt(1, result));
3104 return;
3105 }
3106
3107 // Check to see if we can prove the result entirely of the comparison (in
3108 // which we bail out early), otherwise build a list of values to concat and a
3109 // smaller constant to compare against.
3110 SmallVector<Value> newConcatOperands;
3111 auto newConstant = APInt::getZeroWidth();
3112
3113 // Ok, some (maybe all) bits are known and some others may be unknown.
3114 // Extract out segments of the operand and compare against the
3115 // corresponding bits.
3116 unsigned knownMSB = bitsKnown.countLeadingOnes();
3117
3118 Value operand = cmpOp.getLhs();
3119
3120 // Ok, some bits are known but others are not. Extract out sequences of
3121 // bits that are unknown and compare just those bits. We work from MSB to
3122 // LSB.
3123 while (knownMSB != bitsKnown.getBitWidth()) {
3124 // Drop any high bits that are known.
3125 if (knownMSB)
3126 bitsKnown = bitsKnown.trunc(bitsKnown.getBitWidth() - knownMSB);
3127
3128 // Find the span of unknown bits, and extract it.
3129 unsigned unknownBits = bitsKnown.countLeadingZeros();
3130 unsigned lowBit = bitsKnown.getBitWidth() - unknownBits;
3131 auto spanOperand = rewriter.createOrFold<ExtractOp>(
3132 operand.getLoc(), operand, /*lowBit=*/lowBit,
3133 /*bitWidth=*/unknownBits);
3134 auto spanConstant = rhsCst.lshr(lowBit).trunc(unknownBits);
3135
3136 // Add this info to the concat we're generating.
3137 newConcatOperands.push_back(spanOperand);
3138 // FIXME(llvm merge, cc697fc292b0): concat doesn't work with zero bit values
3139 // newConstant = newConstant.concat(spanConstant);
3140 if (newConstant.getBitWidth() != 0)
3141 newConstant = newConstant.concat(spanConstant);
3142 else
3143 newConstant = spanConstant;
3144
3145 // Drop the unknown bits in prep for the next chunk.
3146 unsigned newWidth = bitsKnown.getBitWidth() - unknownBits;
3147 bitsKnown = bitsKnown.trunc(newWidth);
3148 knownMSB = bitsKnown.countLeadingOnes();
3149 }
3150
3151 // If all the operands to the concat are foldable then we have an identity
3152 // situation where all the sub-elements equal each other. This implies that
3153 // the overall result is foldable.
3154 if (newConcatOperands.empty()) {
3155 bool result = cmpOp.getPredicate() == ICmpPredicate::eq;
3156 replaceOpWithNewOpAndCopyNamehint<hw::ConstantOp>(rewriter, cmpOp,
3157 APInt(1, result));
3158 return;
3159 }
3160
3161 // If we have a single operand remaining, use it, otherwise form a concat.
3162 Value concatResult =
3163 rewriter.createOrFold<ConcatOp>(operand.getLoc(), newConcatOperands);
3164
3165 // Form the comparison against the smaller constant.
3166 auto newConstantOp = hw::ConstantOp::create(
3167 rewriter, cmpOp.getOperand(1).getLoc(), newConstant);
3168
3169 replaceOpWithNewOpAndCopyNamehint<ICmpOp>(rewriter, cmpOp,
3170 cmpOp.getPredicate(), concatResult,
3171 newConstantOp, cmpOp.getTwoState());
3172}
3173
3174// Simplify icmp eq(xor(a,b,cst1), cst2) -> icmp eq(xor(a,b), cst1^cst2).
3175static void combineEqualityICmpWithXorOfConstant(ICmpOp cmpOp, XorOp xorOp,
3176 const APInt &rhs,
3177 PatternRewriter &rewriter) {
3178 auto ip = rewriter.saveInsertionPoint();
3179 rewriter.setInsertionPoint(xorOp);
3180
3181 auto xorRHS = xorOp.getOperands().back().getDefiningOp<hw::ConstantOp>();
3182 auto newRHS = hw::ConstantOp::create(rewriter, xorRHS->getLoc(),
3183 xorRHS.getValue() ^ rhs);
3184 Value newLHS;
3185 switch (xorOp.getNumOperands()) {
3186 case 1:
3187 // This isn't common but is defined so we need to handle it.
3188 newLHS = hw::ConstantOp::create(rewriter, xorOp.getLoc(),
3189 APInt::getZero(rhs.getBitWidth()));
3190 break;
3191 case 2:
3192 // The binary case is the most common.
3193 newLHS = xorOp.getOperand(0);
3194 break;
3195 default:
3196 // The general case forces us to form a new xor with the remaining operands.
3197 SmallVector<Value> newOperands(xorOp.getOperands());
3198 newOperands.pop_back();
3199 newLHS = XorOp::create(rewriter, xorOp.getLoc(), newOperands, false);
3200 break;
3201 }
3202
3203 bool xorMultipleUses = !xorOp->hasOneUse();
3204
3205 // If the xor has multiple uses (not just the compare, then we need/want to
3206 // replace them as well.
3207 if (xorMultipleUses)
3208 replaceOpWithNewOpAndCopyNamehint<XorOp>(rewriter, xorOp, newLHS, xorRHS,
3209 false);
3210
3211 // Replace the comparison.
3212 rewriter.restoreInsertionPoint(ip);
3213 replaceOpWithNewOpAndCopyNamehint<ICmpOp>(
3214 rewriter, cmpOp, cmpOp.getPredicate(), newLHS, newRHS, false);
3215}
3216
3217LogicalResult ICmpOp::canonicalize(ICmpOp op, PatternRewriter &rewriter) {
3218 if (isOpTriviallyRecursive(op))
3219 return failure();
3220 APInt lhs, rhs;
3221
3222 // icmp 1, x -> icmp x, 1
3223 if (matchPattern(op.getLhs(), m_ConstantInt(&lhs))) {
3224 assert(!matchPattern(op.getRhs(), m_ConstantInt(&rhs)) &&
3225 "Should be folded");
3226 replaceOpWithNewOpAndCopyNamehint<ICmpOp>(
3227 rewriter, op, ICmpOp::getFlippedPredicate(op.getPredicate()),
3228 op.getRhs(), op.getLhs(), op.getTwoState());
3229 return success();
3230 }
3231
3232 // Canonicalize with RHS constant
3233 if (matchPattern(op.getRhs(), m_ConstantInt(&rhs))) {
3234 auto getConstant = [&](APInt constant) -> Value {
3235 return hw::ConstantOp::create(rewriter, op.getLoc(), std::move(constant));
3236 };
3237
3238 auto replaceWith = [&](ICmpPredicate predicate, Value lhs,
3239 Value rhs) -> LogicalResult {
3240 replaceOpWithNewOpAndCopyNamehint<ICmpOp>(rewriter, op, predicate, lhs,
3241 rhs, op.getTwoState());
3242 return success();
3243 };
3244
3245 auto replaceWithConstantI1 = [&](bool constant) -> LogicalResult {
3246 replaceOpWithNewOpAndCopyNamehint<hw::ConstantOp>(rewriter, op,
3247 APInt(1, constant));
3248 return success();
3249 };
3250
3251 switch (op.getPredicate()) {
3252 case ICmpPredicate::slt:
3253 // x < max -> x != max
3254 if (rhs.isMaxSignedValue())
3255 return replaceWith(ICmpPredicate::ne, op.getLhs(), op.getRhs());
3256 // x < min -> false
3257 if (rhs.isMinSignedValue())
3258 return replaceWithConstantI1(0);
3259 // x < min+1 -> x == min
3260 if ((rhs - 1).isMinSignedValue())
3261 return replaceWith(ICmpPredicate::eq, op.getLhs(),
3262 getConstant(rhs - 1));
3263 break;
3264 case ICmpPredicate::sgt:
3265 // x > min -> x != min
3266 if (rhs.isMinSignedValue())
3267 return replaceWith(ICmpPredicate::ne, op.getLhs(), op.getRhs());
3268 // x > max -> false
3269 if (rhs.isMaxSignedValue())
3270 return replaceWithConstantI1(0);
3271 // x > max-1 -> x == max
3272 if ((rhs + 1).isMaxSignedValue())
3273 return replaceWith(ICmpPredicate::eq, op.getLhs(),
3274 getConstant(rhs + 1));
3275 break;
3276 case ICmpPredicate::ult:
3277 // x < max -> x != max
3278 if (rhs.isAllOnes())
3279 return replaceWith(ICmpPredicate::ne, op.getLhs(), op.getRhs());
3280 // x < min -> false
3281 if (rhs.isZero())
3282 return replaceWithConstantI1(0);
3283 // x < min+1 -> x == min
3284 if ((rhs - 1).isZero())
3285 return replaceWith(ICmpPredicate::eq, op.getLhs(),
3286 getConstant(rhs - 1));
3287
3288 // x < 0xE0 -> extract(x, 5..7) != 0b111
3289 if (rhs.countLeadingOnes() + rhs.countTrailingZeros() ==
3290 rhs.getBitWidth()) {
3291 auto numOnes = rhs.countLeadingOnes();
3292 auto smaller = ExtractOp::create(rewriter, op.getLoc(), op.getLhs(),
3293 rhs.getBitWidth() - numOnes, numOnes);
3294 return replaceWith(ICmpPredicate::ne, smaller,
3295 getConstant(APInt::getAllOnes(numOnes)));
3296 }
3297
3298 break;
3299 case ICmpPredicate::ugt:
3300 // x > min -> x != min
3301 if (rhs.isZero())
3302 return replaceWith(ICmpPredicate::ne, op.getLhs(), op.getRhs());
3303 // x > max -> false
3304 if (rhs.isAllOnes())
3305 return replaceWithConstantI1(0);
3306 // x > max-1 -> x == max
3307 if ((rhs + 1).isAllOnes())
3308 return replaceWith(ICmpPredicate::eq, op.getLhs(),
3309 getConstant(rhs + 1));
3310
3311 // x > 0x07 -> extract(x, 3..7) != 0b00000
3312 if ((rhs + 1).isPowerOf2()) {
3313 auto numOnes = rhs.countTrailingOnes();
3314 auto newWidth = rhs.getBitWidth() - numOnes;
3315 auto smaller = ExtractOp::create(rewriter, op.getLoc(), op.getLhs(),
3316 numOnes, newWidth);
3317 return replaceWith(ICmpPredicate::ne, smaller,
3318 getConstant(APInt::getZero(newWidth)));
3319 }
3320
3321 break;
3322 case ICmpPredicate::sle:
3323 // x <= max -> true
3324 if (rhs.isMaxSignedValue())
3325 return replaceWithConstantI1(1);
3326 // x <= c -> x < (c+1)
3327 return replaceWith(ICmpPredicate::slt, op.getLhs(), getConstant(rhs + 1));
3328 case ICmpPredicate::sge:
3329 // x >= min -> true
3330 if (rhs.isMinSignedValue())
3331 return replaceWithConstantI1(1);
3332 // x >= c -> x > (c-1)
3333 return replaceWith(ICmpPredicate::sgt, op.getLhs(), getConstant(rhs - 1));
3334 case ICmpPredicate::ule:
3335 // x <= max -> true
3336 if (rhs.isAllOnes())
3337 return replaceWithConstantI1(1);
3338 // x <= c -> x < (c+1)
3339 return replaceWith(ICmpPredicate::ult, op.getLhs(), getConstant(rhs + 1));
3340 case ICmpPredicate::uge:
3341 // x >= min -> true
3342 if (rhs.isZero())
3343 return replaceWithConstantI1(1);
3344 // x >= c -> x > (c-1)
3345 return replaceWith(ICmpPredicate::ugt, op.getLhs(), getConstant(rhs - 1));
3346 case ICmpPredicate::eq:
3347 if (rhs.getBitWidth() == 1) {
3348 if (rhs.isZero()) {
3349 // x == 0 -> x ^ 1
3350 replaceOpWithNewOpAndCopyNamehint<XorOp>(rewriter, op, op.getLhs(),
3351 getConstant(APInt(1, 1)),
3352 op.getTwoState());
3353 return success();
3354 }
3355 if (rhs.isAllOnes()) {
3356 // x == 1 -> x
3357 replaceOpAndCopyNamehint(rewriter, op, op.getLhs());
3358 return success();
3359 }
3360 }
3361 break;
3362 case ICmpPredicate::ne:
3363 if (rhs.getBitWidth() == 1) {
3364 if (rhs.isZero()) {
3365 // x != 0 -> x
3366 replaceOpAndCopyNamehint(rewriter, op, op.getLhs());
3367 return success();
3368 }
3369 if (rhs.isAllOnes()) {
3370 // x != 1 -> x ^ 1
3371 replaceOpWithNewOpAndCopyNamehint<XorOp>(rewriter, op, op.getLhs(),
3372 getConstant(APInt(1, 1)),
3373 op.getTwoState());
3374 return success();
3375 }
3376 }
3377 break;
3378 case ICmpPredicate::ceq:
3379 case ICmpPredicate::cne:
3380 case ICmpPredicate::weq:
3381 case ICmpPredicate::wne:
3382 break;
3383 }
3384
3385 // We have some specific optimizations for comparison with a constant that
3386 // are only supported for equality comparisons.
3387 if (op.getPredicate() == ICmpPredicate::eq ||
3388 op.getPredicate() == ICmpPredicate::ne) {
3389 // Simplify `icmp(value_with_known_bits, rhscst)` into some extracts
3390 // with a smaller constant. We only support equality comparisons for
3391 // this.
3392 auto knownBits = computeKnownBits(op.getLhs());
3393 if (!knownBits.isUnknown())
3394 return combineEqualityICmpWithKnownBitsAndConstant(op, knownBits, rhs,
3395 rewriter),
3396 success();
3397
3398 // Simplify icmp eq(xor(a,b,cst1), cst2) -> icmp eq(xor(a,b),
3399 // cst1^cst2).
3400 if (auto xorOp = op.getLhs().getDefiningOp<XorOp>())
3401 if (xorOp.getOperands().back().getDefiningOp<hw::ConstantOp>())
3402 return combineEqualityICmpWithXorOfConstant(op, xorOp, rhs, rewriter),
3403 success();
3404
3405 // Simplify icmp eq(replicate(v, n), c) -> icmp eq(v, c) if c is zero or
3406 // all one.
3407 if (auto replicateOp = op.getLhs().getDefiningOp<ReplicateOp>())
3408 if (rhs.isAllOnes() || rhs.isZero()) {
3409 auto width = replicateOp.getInput().getType().getIntOrFloatBitWidth();
3410 auto cst =
3411 hw::ConstantOp::create(rewriter, op.getLoc(),
3412 rhs.isAllOnes() ? APInt::getAllOnes(width)
3413 : APInt::getZero(width));
3414 replaceOpWithNewOpAndCopyNamehint<ICmpOp>(
3415 rewriter, op, op.getPredicate(), replicateOp.getInput(), cst,
3416 op.getTwoState());
3417 return success();
3418 }
3419 }
3420 }
3421
3422 // icmp(cat(prefix, a, b, suffix), cat(prefix, c, d, suffix)) => icmp(cat(a,
3423 // b), cat(c, d)). contains special handling for sign bit in signed
3424 // compressions.
3425 if (Operation *opLHS = op.getLhs().getDefiningOp())
3426 if (Operation *opRHS = op.getRhs().getDefiningOp())
3427 if (isa<ConcatOp, ReplicateOp>(opLHS) &&
3428 isa<ConcatOp, ReplicateOp>(opRHS)) {
3429 if (succeeded(matchAndRewriteCompareConcat(op, opLHS, opRHS, rewriter)))
3430 return success();
3431 }
3432
3433 return failure();
3434}
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:79
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:67
MuxChainWithComparisonFoldingStyle
Enum for mux chain folding styles.
Definition CombOps.h:104
@ BalancedMuxTree
Definition CombOps.h:104
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:106
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:44
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