CIRCT 23.0.0git
Loading...
Searching...
No Matches
CombOps.cpp
Go to the documentation of this file.
1//===- CombOps.cpp - Implement the 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//
9// This file implements combinational ops.
10//
11//===----------------------------------------------------------------------===//
12
16#include "mlir/IR/Builders.h"
17#include "mlir/IR/ImplicitLocOpBuilder.h"
18#include "mlir/IR/Matchers.h"
19#include "mlir/IR/PatternMatch.h"
20#include "llvm/Support/FormatVariadic.h"
21#include <limits>
22
23using namespace mlir;
24using namespace circt;
25using namespace comb;
26using namespace matchers;
27
28// Common function to identify when multipliers/partial products should be
29// lowered to Booth encoded array. Identifies zext/sext of the operands. Only
30// valid for binary multiplication.
31// Threshold default is 16
32bool comb::shouldUseBoothEncoding(Value lhs, Value rhs, unsigned threshold) {
33 // Do not booth encode multiplication by a constant
34 if (lhs.getDefiningOp<hw::ConstantOp>() ||
35 rhs.getDefiningOp<hw::ConstantOp>())
36 return false;
37
38 auto lhsWidth = lhs.getType().getIntOrFloatBitWidth();
39 auto rhsWidth = rhs.getType().getIntOrFloatBitWidth();
40
41 // Check for zext of the multiplicands
42 Value lhsZext, rhsZext;
43 if (matchPattern(lhs, comb::m_ZextBy(m_Any(&lhsZext))))
44 lhsWidth -= lhsZext.getType().getIntOrFloatBitWidth();
45 if (matchPattern(rhs, comb::m_ZextBy(m_Any(&rhsZext))))
46 rhsWidth -= rhsZext.getType().getIntOrFloatBitWidth();
47
48 // Check for sext of the multiplicands
49 Value lhsSextBits, rhsSextBits;
50 if (matchPattern(lhs, comb::m_SextBy(m_Any(&lhsSextBits))))
51 lhsWidth -= lhsSextBits.getType().getIntOrFloatBitWidth();
52 if (matchPattern(rhs, comb::m_SextBy(m_Any(&rhsSextBits))))
53 rhsWidth -= rhsSextBits.getType().getIntOrFloatBitWidth();
54
55 // Heuristic threshold based on:
56 // "Datapath Synthesis for Standard-Cell Design", Reto Zimmerman 2009
57 // If either operand is less than 16 bits (default), don't use Booth encoding.
58 return lhsWidth > threshold && rhsWidth > threshold;
59}
60
61Value comb::createZExt(OpBuilder &builder, Location loc, Value value,
62 unsigned targetWidth) {
63 assert(value.getType().isSignlessInteger());
64 auto inputWidth = value.getType().getIntOrFloatBitWidth();
65 assert(inputWidth <= targetWidth);
66
67 // Nothing to do if the width already matches.
68 if (inputWidth == targetWidth)
69 return value;
70
71 // Create a zero constant for the upper bits.
72 auto zeros = hw::ConstantOp::create(
73 builder, loc, builder.getIntegerType(targetWidth - inputWidth), 0);
74 return builder.createOrFold<ConcatOp>(loc, zeros, value);
75}
76
77/// Create a sign extension operation from a value of integer type to an equal
78/// or larger integer type.
79Value comb::createOrFoldSExt(OpBuilder &builder, Location loc, Value value,
80 Type destTy) {
81 IntegerType valueType = dyn_cast<IntegerType>(value.getType());
82 assert(valueType && isa<IntegerType>(destTy) &&
83 valueType.getWidth() <= destTy.getIntOrFloatBitWidth() &&
84 valueType.getWidth() != 0 && "invalid sext operands");
85 // If already the right size, we are done.
86 if (valueType == destTy)
87 return value;
88
89 // sext is concat with a replicate of the sign bits and the bottom part.
90 auto signBit =
91 builder.createOrFold<ExtractOp>(loc, value, valueType.getWidth() - 1, 1);
92 auto signBits = builder.createOrFold<ReplicateOp>(
93 loc, signBit, destTy.getIntOrFloatBitWidth() - valueType.getWidth());
94 return builder.createOrFold<ConcatOp>(loc, signBits, value);
95}
96
97Value comb::createOrFoldSExt(ImplicitLocOpBuilder &builder, Value value,
98 Type destTy) {
99 return createOrFoldSExt(builder, builder.getLoc(), value, destTy);
100}
101
102Value comb::createOrFoldNot(OpBuilder &builder, Location loc, Value value,
103 bool twoState) {
104 auto allOnes = hw::ConstantOp::create(builder, loc, value.getType(), -1);
105 return builder.createOrFold<XorOp>(loc, value, allOnes, twoState);
106}
107
108Value comb::createOrFoldNot(ImplicitLocOpBuilder &builder, Value value,
109 bool twoState) {
110 return createOrFoldNot(builder, builder.getLoc(), value, twoState);
111}
112
113// Extract individual bits from a value
114void comb::extractBits(OpBuilder &builder, Value val,
115 SmallVectorImpl<Value> &bits) {
116 assert(val.getType().isInteger() && "expected integer");
117 auto width = val.getType().getIntOrFloatBitWidth();
118 bits.reserve(width);
119
120 // Check if we can reuse concat operands
121 if (auto concat = val.getDefiningOp<comb::ConcatOp>()) {
122 if (concat.getNumOperands() == width &&
123 llvm::all_of(concat.getOperandTypes(), [](Type type) {
124 return type.getIntOrFloatBitWidth() == 1;
125 })) {
126 // Reverse the operands to match the bit order
127 bits.append(std::make_reverse_iterator(concat.getOperands().end()),
128 std::make_reverse_iterator(concat.getOperands().begin()));
129 return;
130 }
131 }
132
133 // Extract individual bits
134 for (int64_t i = 0; i < width; ++i)
135 bits.push_back(
136 builder.createOrFold<comb::ExtractOp>(val.getLoc(), val, i, 1));
137}
138
139// Construct a mux tree for given leaf nodes. `selectors` is the selector for
140// each level of the tree. Currently the selector is tested from MSB to LSB.
141Value comb::constructMuxTree(OpBuilder &builder, Location loc,
142 ArrayRef<Value> selectors,
143 ArrayRef<Value> leafNodes,
144 Value outOfBoundsValue) {
145 // Recursive helper function to construct the mux tree
146 std::function<Value(size_t, size_t)> constructTreeHelper =
147 [&](size_t id, size_t level) -> Value {
148 // Base case: at the lowest level, return the result
149 if (level == 0) {
150 // Return the result for the given index. If the index is out of bounds,
151 // return the out-of-bound value.
152 return id < leafNodes.size() ? leafNodes[id] : outOfBoundsValue;
153 }
154
155 auto selector = selectors[level - 1];
156
157 // Recursive case: create muxes for true and false branches
158 auto trueVal = constructTreeHelper(2 * id + 1, level - 1);
159 auto falseVal = constructTreeHelper(2 * id, level - 1);
160
161 // Combine the results with a mux
162 return builder.createOrFold<comb::MuxOp>(loc, selector, trueVal, falseVal);
163 };
164
165 return constructTreeHelper(0, llvm::Log2_64_Ceil(leafNodes.size()));
166}
167
168Value comb::createDynamicExtract(OpBuilder &builder, Location loc, Value value,
169 Value offset, unsigned width) {
170 assert(value.getType().isSignlessInteger());
171 auto valueWidth = value.getType().getIntOrFloatBitWidth();
172 assert(width <= valueWidth);
173
174 // Handle the special case where the offset is constant.
175 APInt constOffset;
176 if (matchPattern(offset, mlir::m_ConstantInt(&constOffset)))
177 if (constOffset.getActiveBits() < 32)
178 return builder.createOrFold<comb::ExtractOp>(
179 loc, value, constOffset.getZExtValue(), width);
180
181 // Zero-extend the offset, shift the value down, and extract the requested
182 // number of bits.
183 offset = createZExt(builder, loc, offset, valueWidth);
184 value = builder.createOrFold<comb::ShrUOp>(loc, value, offset);
185 return builder.createOrFold<comb::ExtractOp>(loc, value, 0, width);
186}
187
188Value comb::createDynamicInject(OpBuilder &builder, Location loc, Value value,
189 Value offset, Value replacement,
190 bool twoState) {
191 assert(value.getType().isSignlessInteger());
192 assert(replacement.getType().isSignlessInteger());
193 auto largeWidth = value.getType().getIntOrFloatBitWidth();
194 auto smallWidth = replacement.getType().getIntOrFloatBitWidth();
195 assert(smallWidth <= largeWidth);
196
197 // If we're inserting a zero-width value there's nothing to do.
198 if (smallWidth == 0)
199 return value;
200
201 // Handle the special case where the offset is constant.
202 APInt constOffset;
203 if (matchPattern(offset, mlir::m_ConstantInt(&constOffset)))
204 if (constOffset.getActiveBits() < 32)
205 return createInject(builder, loc, value, constOffset.getZExtValue(),
206 replacement);
207
208 // Zero-extend the offset and clear the value bits we are replacing.
209 offset = createZExt(builder, loc, offset, largeWidth);
210 Value mask = hw::ConstantOp::create(
211 builder, loc, APInt::getLowBitsSet(largeWidth, smallWidth));
212 mask = builder.createOrFold<comb::ShlOp>(loc, mask, offset);
213 mask = createOrFoldNot(builder, loc, mask, true);
214 value = builder.createOrFold<comb::AndOp>(loc, value, mask, twoState);
215
216 // Zero-extend the replacement value, shift it up to the offset, and merge it
217 // with the value that has the corresponding bits cleared.
218 replacement = createZExt(builder, loc, replacement, largeWidth);
219 replacement = builder.createOrFold<comb::ShlOp>(loc, replacement, offset);
220 return builder.createOrFold<comb::OrOp>(loc, value, replacement, twoState);
221}
222
223Value comb::createInject(OpBuilder &builder, Location loc, Value value,
224 unsigned offset, Value replacement) {
225 assert(value.getType().isSignlessInteger());
226 assert(replacement.getType().isSignlessInteger());
227 auto largeWidth = value.getType().getIntOrFloatBitWidth();
228 auto smallWidth = replacement.getType().getIntOrFloatBitWidth();
229 assert(smallWidth <= largeWidth);
230
231 // If the offset is outside the value there's nothing to do.
232 if (offset >= largeWidth)
233 return value;
234
235 // If we're inserting a zero-width value there's nothing to do.
236 if (smallWidth == 0)
237 return value;
238
239 // Assemble the pieces of the injection as everything below the offset, the
240 // replacement value, and everything above the replacement value.
241 SmallVector<Value, 3> fragments;
242 auto end = offset + smallWidth;
243 if (end < largeWidth)
244 fragments.push_back(
245 comb::ExtractOp::create(builder, loc, value, end, largeWidth - end));
246 if (end <= largeWidth)
247 fragments.push_back(replacement);
248 else
249 fragments.push_back(comb::ExtractOp::create(builder, loc, replacement, 0,
250 largeWidth - offset));
251 if (offset > 0)
252 fragments.push_back(
253 comb::ExtractOp::create(builder, loc, value, 0, offset));
254 return builder.createOrFold<comb::ConcatOp>(loc, fragments);
255}
256
257llvm::LogicalResult comb::convertSubToAdd(comb::SubOp subOp,
258 mlir::PatternRewriter &rewriter) {
259 auto lhs = subOp.getLhs();
260 auto rhs = subOp.getRhs();
261 // Since `-rhs = ~rhs + 1` holds, rewrite `sub(lhs, rhs)` to:
262 // sub(lhs, rhs) => add(lhs, -rhs) => add(lhs, add(~rhs, 1))
263 // => add(lhs, ~rhs, 1)
264 auto notRhs =
265 comb::createOrFoldNot(rewriter, subOp.getLoc(), rhs, subOp.getTwoState());
266 auto one =
267 hw::ConstantOp::create(rewriter, subOp.getLoc(), subOp.getType(), 1);
268 replaceOpWithNewOpAndCopyNamehint<comb::AddOp>(
269 rewriter, subOp, ValueRange{lhs, notRhs, one}, subOp.getTwoState());
270 return success();
271}
272
273static llvm::LogicalResult convertDivModUByPowerOfTwo(PatternRewriter &rewriter,
274 Operation *op, Value lhs,
275 Value rhs, bool isDiv) {
276 // Check if the divisor is a power of two constant.
277 auto rhsConstantOp = rhs.getDefiningOp<hw::ConstantOp>();
278 if (!rhsConstantOp)
279 return failure();
280
281 APInt rhsValue = rhsConstantOp.getValue();
282 if (!rhsValue.isPowerOf2())
283 return failure();
284
285 Location loc = op->getLoc();
286
287 unsigned width = lhs.getType().getIntOrFloatBitWidth();
288 unsigned bitPosition = rhsValue.ceilLogBase2();
289
290 if (isDiv) {
291 // divu(x, 2^n) -> concat(0...0, extract(x, n, width-n))
292 // This is equivalent to a right shift by n bits.
293
294 // Extract the upper bits (equivalent to right shift).
295 Value upperBits = rewriter.createOrFold<comb::ExtractOp>(
296 loc, lhs, bitPosition, width - bitPosition);
297
298 // Concatenate with zeros on the left.
299 Value zeros =
300 hw::ConstantOp::create(rewriter, loc, APInt::getZero(bitPosition));
301
302 // use replaceOpWithNewOpAndCopyNamehint?
304 rewriter, op,
305 comb::ConcatOp::create(rewriter, loc,
306 ArrayRef<Value>{zeros, upperBits}));
307 return success();
308 }
309
310 // modu(x, 2^n) -> concat(0...0, extract(x, 0, n))
311 // This extracts the lower n bits (equivalent to bitwise AND with 2^n - 1).
312
313 // Extract the lower bits.
314 Value lowerBits =
315 rewriter.createOrFold<comb::ExtractOp>(loc, lhs, 0, bitPosition);
316
317 // Concatenate with zeros on the left.
318 Value zeros = hw::ConstantOp::create(rewriter, loc,
319 APInt::getZero(width - bitPosition));
320
322 rewriter, op,
323 comb::ConcatOp::create(rewriter, loc, ArrayRef<Value>{zeros, lowerBits}));
324 return success();
325}
326
327LogicalResult comb::convertDivUByPowerOfTwo(DivUOp divOp,
328 mlir::PatternRewriter &rewriter) {
329 return convertDivModUByPowerOfTwo(rewriter, divOp, divOp.getLhs(),
330 divOp.getRhs(), /*isDiv=*/true);
331}
332
333LogicalResult comb::convertModUByPowerOfTwo(ModUOp modOp,
334 mlir::PatternRewriter &rewriter) {
335 return convertDivModUByPowerOfTwo(rewriter, modOp, modOp.getLhs(),
336 modOp.getRhs(), /*isDiv=*/false);
337}
338
339//===----------------------------------------------------------------------===//
340// ICmpOp
341//===----------------------------------------------------------------------===//
342
343ICmpPredicate ICmpOp::getFlippedPredicate(ICmpPredicate predicate) {
344 switch (predicate) {
345 case ICmpPredicate::eq:
346 return ICmpPredicate::eq;
347 case ICmpPredicate::ne:
348 return ICmpPredicate::ne;
349 case ICmpPredicate::slt:
350 return ICmpPredicate::sgt;
351 case ICmpPredicate::sle:
352 return ICmpPredicate::sge;
353 case ICmpPredicate::sgt:
354 return ICmpPredicate::slt;
355 case ICmpPredicate::sge:
356 return ICmpPredicate::sle;
357 case ICmpPredicate::ult:
358 return ICmpPredicate::ugt;
359 case ICmpPredicate::ule:
360 return ICmpPredicate::uge;
361 case ICmpPredicate::ugt:
362 return ICmpPredicate::ult;
363 case ICmpPredicate::uge:
364 return ICmpPredicate::ule;
365 case ICmpPredicate::ceq:
366 return ICmpPredicate::ceq;
367 case ICmpPredicate::cne:
368 return ICmpPredicate::cne;
369 case ICmpPredicate::weq:
370 return ICmpPredicate::weq;
371 case ICmpPredicate::wne:
372 return ICmpPredicate::wne;
373 }
374 llvm_unreachable("unknown comparison predicate");
375}
376
377bool ICmpOp::isPredicateSigned(ICmpPredicate predicate) {
378 switch (predicate) {
379 case ICmpPredicate::ult:
380 case ICmpPredicate::ugt:
381 case ICmpPredicate::ule:
382 case ICmpPredicate::uge:
383 case ICmpPredicate::ne:
384 case ICmpPredicate::eq:
385 case ICmpPredicate::cne:
386 case ICmpPredicate::ceq:
387 case ICmpPredicate::wne:
388 case ICmpPredicate::weq:
389 return false;
390 case ICmpPredicate::slt:
391 case ICmpPredicate::sgt:
392 case ICmpPredicate::sle:
393 case ICmpPredicate::sge:
394 return true;
395 }
396 llvm_unreachable("unknown comparison predicate");
397}
398
399/// Returns the predicate for a logically negated comparison, e.g. mapping
400/// EQ => NE and SLE => SGT.
401ICmpPredicate ICmpOp::getNegatedPredicate(ICmpPredicate predicate) {
402 switch (predicate) {
403 case ICmpPredicate::eq:
404 return ICmpPredicate::ne;
405 case ICmpPredicate::ne:
406 return ICmpPredicate::eq;
407 case ICmpPredicate::slt:
408 return ICmpPredicate::sge;
409 case ICmpPredicate::sle:
410 return ICmpPredicate::sgt;
411 case ICmpPredicate::sgt:
412 return ICmpPredicate::sle;
413 case ICmpPredicate::sge:
414 return ICmpPredicate::slt;
415 case ICmpPredicate::ult:
416 return ICmpPredicate::uge;
417 case ICmpPredicate::ule:
418 return ICmpPredicate::ugt;
419 case ICmpPredicate::ugt:
420 return ICmpPredicate::ule;
421 case ICmpPredicate::uge:
422 return ICmpPredicate::ult;
423 case ICmpPredicate::ceq:
424 return ICmpPredicate::cne;
425 case ICmpPredicate::cne:
426 return ICmpPredicate::ceq;
427 case ICmpPredicate::weq:
428 return ICmpPredicate::wne;
429 case ICmpPredicate::wne:
430 return ICmpPredicate::weq;
431 }
432 llvm_unreachable("unknown comparison predicate");
433}
434
435/// Return true if this is an equality test with -1, which is a "reduction
436/// and" operation in Verilog.
437bool ICmpOp::isEqualAllOnes() {
438 if (getPredicate() != ICmpPredicate::eq)
439 return false;
440
441 if (auto op1 =
442 dyn_cast_or_null<hw::ConstantOp>(getOperand(1).getDefiningOp()))
443 return op1.getValue().isAllOnes();
444 return false;
445}
446
447/// Return true if this is a not equal test with 0, which is a "reduction
448/// or" operation in Verilog.
449bool ICmpOp::isNotEqualZero() {
450 if (getPredicate() != ICmpPredicate::ne)
451 return false;
452
453 if (auto op1 =
454 dyn_cast_or_null<hw::ConstantOp>(getOperand(1).getDefiningOp()))
455 return op1.getValue().isZero();
456 return false;
457}
458
459//===----------------------------------------------------------------------===//
460// Unary Operations
461//===----------------------------------------------------------------------===//
462
463LogicalResult ReplicateOp::verify() {
464 // The source must be equal or smaller than the dest type, and an even
465 // multiple of it. Both are already known to be signless integers.
466 auto srcWidth = cast<IntegerType>(getOperand().getType()).getWidth();
467 auto dstWidth = cast<IntegerType>(getType()).getWidth();
468
469 if (srcWidth > dstWidth)
470 return emitOpError("replicate cannot shrink bitwidth of operand"),
471 failure();
472
473 if ((srcWidth == 0 && dstWidth != 0) ||
474 (srcWidth != 0 && dstWidth % srcWidth))
475 return emitOpError("replicate must produce integer multiple of operand"),
476 failure();
477
478 return success();
479}
480
481//===----------------------------------------------------------------------===//
482// Variadic operations
483//===----------------------------------------------------------------------===//
484
485static LogicalResult verifyUTBinOp(Operation *op) {
486 if (op->getOperands().empty())
487 return op->emitOpError("requires 1 or more args");
488 return success();
489}
490
491LogicalResult AddOp::verify() { return verifyUTBinOp(*this); }
492
493LogicalResult MulOp::verify() { return verifyUTBinOp(*this); }
494
495LogicalResult AndOp::verify() { return verifyUTBinOp(*this); }
496
497LogicalResult OrOp::verify() { return verifyUTBinOp(*this); }
498
499LogicalResult XorOp::verify() { return verifyUTBinOp(*this); }
500
501/// Return true if this is a two operand xor with an all ones constant as
502/// its RHS operand.
503bool XorOp::isBinaryNot() {
504 if (getNumOperands() != 2)
505 return false;
506 if (auto cst = getOperand(1).getDefiningOp<hw::ConstantOp>())
507 if (cst.getValue().isAllOnes())
508 return true;
509 return false;
510}
511
512//===----------------------------------------------------------------------===//
513// ConcatOp
514//===----------------------------------------------------------------------===//
515
516static unsigned getTotalWidth(ValueRange inputs) {
517 unsigned resultWidth = 0;
518 for (auto input : inputs) {
519 resultWidth += hw::type_cast<IntegerType>(input.getType()).getWidth();
520 }
521 return resultWidth;
522}
523
524void ConcatOp::build(OpBuilder &builder, OperationState &result, Value hd,
525 ValueRange tl) {
526 result.addOperands(ValueRange{hd});
527 result.addOperands(tl);
528 unsigned hdWidth = cast<IntegerType>(hd.getType()).getWidth();
529 result.addTypes(builder.getIntegerType(getTotalWidth(tl) + hdWidth));
530}
531
532LogicalResult ConcatOp::inferReturnTypes(
533 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
534 DictionaryAttr attrs, mlir::PropertyRef properties,
535 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
536 unsigned resultWidth = getTotalWidth(operands);
537 results.push_back(IntegerType::get(context, resultWidth));
538 return success();
539}
540
541/// Parse a ConcatOp that can either follow the format:
542/// $inputs attr-dict `:` qualified(type($inputs))
543/// or have no operands, colon and typelist.
544ParseResult ConcatOp::parse(OpAsmParser &parser, OperationState &result) {
545 SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
546 SmallVector<Type, 4> types;
547
548 llvm::SMLoc allOperandLoc = parser.getCurrentLocation();
549
550 // Parse the operand list, attributes and colon
551 if (parser.parseOperandList(operands) ||
552 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon())
553 return failure();
554
555 // Parse an optional list of types
556 Type parsedType;
557 auto parseResult = parser.parseOptionalType(parsedType);
558 if (parseResult.has_value()) {
559 if (failed(parseResult.value()))
560 return failure();
561 types.push_back(parsedType);
562 while (succeeded(parser.parseOptionalComma())) {
563 if (parser.parseType(parsedType))
564 return failure();
565 types.push_back(parsedType);
566 }
567 }
568
569 if (parser.resolveOperands(operands, types, allOperandLoc, result.operands))
570 return failure();
571
572 SmallVector<Type, 1> inferredTypes;
573 if (failed(ConcatOp::inferReturnTypes(
574 parser.getContext(), result.location, result.operands,
575 result.attributes.getDictionary(parser.getContext()),
576 result.getRawProperties(), {}, inferredTypes)))
577 return failure();
578
579 result.addTypes(inferredTypes);
580 return success();
581}
582
583void ConcatOp::print(OpAsmPrinter &p) {
584 p << " ";
585 p.printOperands(getOperands());
586 p.printOptionalAttrDict((*this)->getAttrs());
587 p << " : ";
588 llvm::interleaveComma(getOperandTypes(), p);
589}
590
591//===----------------------------------------------------------------------===//
592// ReverseOp
593//===----------------------------------------------------------------------===//
594
595// Folding of ReverseOp: if the input is constant, compute the reverse at
596// compile time.
597OpFoldResult comb::ReverseOp::fold(FoldAdaptor adaptor) {
598 // Try to cast the input attribute to an IntegerAttr.
599 auto cstInput = llvm::dyn_cast_or_null<mlir::IntegerAttr>(adaptor.getInput());
600 if (!cstInput)
601 return {};
602
603 APInt val = cstInput.getValue();
604 APInt reversedVal = val.reverseBits();
605
606 return mlir::IntegerAttr::get(getType(), reversedVal);
607}
608
609namespace {
610struct ReverseOfReverse : public OpRewritePattern<comb::ReverseOp> {
611 using OpRewritePattern<comb::ReverseOp>::OpRewritePattern;
612
613 LogicalResult matchAndRewrite(comb::ReverseOp op,
614 PatternRewriter &rewriter) const override {
615 auto inputOp = op.getInput().getDefiningOp<comb::ReverseOp>();
616 if (!inputOp)
617 return failure();
618
619 rewriter.replaceOp(op, inputOp.getInput());
620 return success();
621 }
622};
623} // namespace
624
625void comb::ReverseOp::getCanonicalizationPatterns(RewritePatternSet &results,
626 MLIRContext *context) {
627 results.add<ReverseOfReverse>(context);
628}
629
630//===----------------------------------------------------------------------===//
631// Other Operations
632//===----------------------------------------------------------------------===//
633
634LogicalResult ExtractOp::verify() {
635 unsigned srcWidth = cast<IntegerType>(getInput().getType()).getWidth();
636 unsigned dstWidth = cast<IntegerType>(getType()).getWidth();
637
638 bool checkAddWillOverflow =
639 getLowBit() > std::numeric_limits<decltype(dstWidth)>::max() - dstWidth;
640
641 // Checks that all extracted bits from the source are well-defined.
642 // While it is well-defined to extract i0 outside of the bounds of another
643 // integer (because i0 contains no bits and they are therefore all
644 // well-defined), the verifier will refuse it except for right after the input
645 // value, as it is otherwise likely a bug in user code. This constraint can be
646 // lifted and tested for if it proves useful to do so.
647 if (checkAddWillOverflow || getLowBit() + dstWidth > srcWidth)
648 return emitOpError("from bit too large for input"), failure();
649
650 return success();
651}
652
653LogicalResult TruthTableOp::verify() {
654 size_t numInputs = getInputs().size();
655 if (numInputs >= sizeof(size_t) * 8)
656 return emitOpError("Truth tables support a maximum of ")
657 << sizeof(size_t) * 8 - 1 << " inputs on your platform";
658
659 auto table = getLookupTable();
660 if (table.size() != (1ull << numInputs))
661 return emitOpError("Expected lookup table of 2^n length");
662 return success();
663}
664
665//===----------------------------------------------------------------------===//
666// TableGen generated logic.
667//===----------------------------------------------------------------------===//
668
669// Provide the autogenerated implementation guts for the Op classes.
670#define GET_OP_CLASSES
671#include "circt/Dialect/Comb/Comb.cpp.inc"
assert(baseType &&"element must be base type")
static size_t getTotalWidth(ArrayRef< Value > operands)
static LogicalResult verifyUTBinOp(Operation *op)
Definition CombOps.cpp:485
static llvm::LogicalResult convertDivModUByPowerOfTwo(PatternRewriter &rewriter, Operation *op, Value lhs, Value rhs, bool isDiv)
Definition CombOps.cpp:273
static std::unique_ptr< Context > context
create(low_bit, result_type, input=None)
Definition comb.py:187
create(data_type, value)
Definition hw.py:433
Value createOrFoldNot(OpBuilder &builder, Location loc, Value value, bool twoState=false)
Create a `‘Not’' gate on a value.
Definition CombOps.cpp:102
Value createInject(OpBuilder &builder, Location loc, Value value, unsigned offset, Value replacement)
Replace a range of bits in an integer and return the updated integer value.
Definition CombOps.cpp:223
Value createZExt(OpBuilder &builder, Location loc, Value value, unsigned targetWidth)
Create the ops to zero-extend a value to an integer of equal or larger type.
Definition CombOps.cpp:61
Value createOrFoldSExt(OpBuilder &builder, Location loc, Value value, Type destTy)
Create a sign extension operation from a value of integer type to an equal or larger integer type.
Definition CombOps.cpp:79
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
void replaceOpAndCopyNamehint(PatternRewriter &rewriter, Operation *op, Value newValue)
A wrapper of PatternRewriter::replaceOp to propagate "sv.namehint" attribute.
Definition Naming.cpp:73
Definition comb.py:1