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