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