32#include "mlir/Pass/Pass.h"
33#include "mlir/Transforms/DialectConversion.h"
34#include "llvm/ADT/APInt.h"
35#include "llvm/ADT/PointerUnion.h"
36#include "llvm/ADT/STLExtras.h"
37#include "llvm/Support/Debug.h"
38#include "llvm/Support/DivisionByConstantInfo.h"
42#define DEBUG_TYPE "comb-to-synth"
45#define GEN_PASS_DEF_CONVERTCOMBTOSYNTH
46#include "circt/Conversion/Passes.h.inc"
57static SmallVector<Value>
extractBits(OpBuilder &builder, Value val) {
58 SmallVector<Value> bits;
59 comb::extractBits(builder, val, bits);
70template <
bool isLeftShift>
72 Value shiftAmount, int64_t maxShiftAmount,
73 llvm::function_ref<Value(int64_t)> getPadding,
74 llvm::function_ref<Value(int64_t)> getExtract) {
79 SmallVector<Value> nodes;
80 nodes.reserve(maxShiftAmount);
81 for (int64_t i = 0; i < maxShiftAmount; ++i) {
82 Value extract = getExtract(i);
83 Value padding = getPadding(i);
86 nodes.push_back(extract);
100 auto outOfBoundsValue = getPadding(maxShiftAmount);
101 assert(outOfBoundsValue &&
"outOfBoundsValue must be valid");
105 comb::constructMuxTree(rewriter, loc, bits, nodes, outOfBoundsValue);
108 auto inBound = rewriter.createOrFold<comb::ICmpOp>(
109 loc, ICmpPredicate::ult, shiftAmount,
113 return rewriter.createOrFold<
comb::MuxOp>(loc, inBound, result,
120 Value b, Value carry) {
122 auto aXnorB = comb::XorOp::create(rewriter, loc, ValueRange{a, b},
true);
124 comb::AndOp::create(rewriter, loc, ValueRange{carry, aXnorB},
true);
125 auto aAndB = comb::AndOp::create(rewriter, loc, ValueRange{a, b},
true);
126 return comb::OrOp::create(rewriter, loc, ValueRange{andOp, aAndB},
true);
131 val.getLoc(), val, val.getType().getIntOrFloatBitWidth() - 1, 1);
136 val.getLoc(), val, 0, val.getType().getIntOrFloatBitWidth() - 1);
141using ConstantOrValue = llvm::PointerUnion<Value, mlir::IntegerAttr>;
146 Value value, llvm::SmallVectorImpl<ConstantOrValue> &values) {
148 if (value.getType().isInteger(0))
153 int64_t totalUnknownBits = 0;
154 for (
auto concatInput : llvm::reverse(
concat.getInputs())) {
159 totalUnknownBits += unknownBits;
161 return totalUnknownBits;
166 values.push_back(constant.getValueAttr());
172 values.push_back(value);
179 llvm::SmallVectorImpl<ConstantOrValue> &constantOrValues,
181 uint32_t bitPos = 0, unknownPos = 0;
182 APInt result(width, 0);
183 for (
auto constantOrValue : constantOrValues) {
185 if (
auto constant = dyn_cast<IntegerAttr>(constantOrValue)) {
186 elemWidth = constant.getValue().getBitWidth();
187 result.insertBits(constant.getValue(), bitPos);
190 assert(elemWidth >= 0 &&
"unknown bit width");
191 assert(elemWidth + unknownPos < 32 &&
"unknown bit width too large");
193 uint32_t usedBits = (mask >> unknownPos) & ((1 << elemWidth) - 1);
194 result.insertBits(APInt(elemWidth, usedBits), bitPos);
195 unknownPos += elemWidth;
207 ConversionPatternRewriter &rewriter, int64_t maxEmulationUnknownBits,
209 llvm::function_ref<APInt(
const APInt &,
const APInt &)> emulate) {
210 SmallVector<ConstantOrValue> lhsValues, rhsValues;
212 assert(op->getNumResults() == 1 && op->getNumOperands() == 2 &&
213 "op must be a single result binary operation");
215 auto lhs = op->getOperand(0);
216 auto rhs = op->getOperand(1);
217 auto width = op->getResult(0).getType().getIntOrFloatBitWidth();
218 auto loc = op->getLoc();
223 if (numLhsUnknownBits < 0 || numRhsUnknownBits < 0)
226 int64_t totalUnknownBits = numLhsUnknownBits + numRhsUnknownBits;
227 if (totalUnknownBits > maxEmulationUnknownBits)
230 SmallVector<Value> emulatedResults;
231 emulatedResults.reserve(1 << totalUnknownBits);
234 DenseMap<IntegerAttr, hw::ConstantOp> constantPool;
236 auto attr = rewriter.getIntegerAttr(rewriter.getIntegerType(width), value);
237 auto it = constantPool.find(attr);
238 if (it != constantPool.end())
241 constantPool[attr] = constant;
245 for (uint32_t lhsMask = 0, lhsMaskEnd = 1 << numLhsUnknownBits;
246 lhsMask < lhsMaskEnd; ++lhsMask) {
248 for (uint32_t rhsMask = 0, rhsMaskEnd = 1 << numRhsUnknownBits;
249 rhsMask < rhsMaskEnd; ++rhsMask) {
252 emulatedResults.push_back(
getConstant(emulate(lhsValue, rhsValue)));
257 SmallVector<Value> selectors;
258 selectors.reserve(totalUnknownBits);
259 for (
auto &concatedValues : {rhsValues, lhsValues})
260 for (
auto valueOrConstant : concatedValues) {
261 auto value = dyn_cast<Value>(valueOrConstant);
267 assert(totalUnknownBits ==
static_cast<int64_t
>(selectors.size()) &&
268 "number of selectors must match");
269 auto muxed = constructMuxTree(rewriter, loc, selectors, emulatedResults,
284 APInt(value.getType().getIntOrFloatBitWidth(), amount)));
295 APInt(value.getType().getIntOrFloatBitWidth(), amount)));
298template <
bool isSigned>
301 unsigned width = lhs.getType().getIntOrFloatBitWidth();
302 auto destTy = builder.getIntegerType(width << 1);
305 Value wideLhs = isSigned ? comb::createOrFoldSExt(builder, loc, lhs, destTy)
306 : comb::createZExt(builder, loc, lhs, width << 1);
308 builder, loc, isSigned ? rhs.sext(width << 1) : rhs.zext(width << 1));
310 loc, ValueRange{wideLhs, wideRhs},
true);
311 return builder.createOrFold<
comb::ExtractOp>(loc, product, width, width);
315 Value lhs,
const APInt &divisor) {
316 auto info = llvm::UnsignedDivisionByConstantInfo::get(divisor);
320 Value diff = builder.createOrFold<
comb::SubOp>(loc, lhs, q);
322 q = builder.createOrFold<
comb::AddOp>(loc, q, diff);
328 Value lhs,
const APInt &divisor) {
329 unsigned width = lhs.getType().getIntOrFloatBitWidth();
330 auto info = llvm::SignedDivisionByConstantInfo::get(divisor);
334 if (divisor.isStrictlyPositive() && info.Magic.isNegative())
335 q = builder.createOrFold<
comb::AddOp>(loc, q, lhs);
336 else if (divisor.isNegative() && info.Magic.isStrictlyPositive())
337 q = builder.createOrFold<
comb::SubOp>(loc, q, lhs);
341 Value signBit = builder.createOrFold<
comb::ExtractOp>(loc, q, width - 1, 1);
342 Value signPadded = comb::createZExt(builder, loc, signBit, width);
343 return builder.createOrFold<
comb::AddOp>(loc, q, signPadded);
358static std::pair<Value, Value>
360 unsigned width = lhs.getType().getIntOrFloatBitWidth();
361 unsigned remWidth = width + 1;
362 Value wideRhs = comb::createZExt(builder, loc, rhs, remWidth);
364 SmallVector<Value> lhsBits =
extractBits(builder, lhs);
370 SmallVector<Value> quotientBits;
371 quotientBits.reserve(width);
372 for (Value lhsBit : llvm::reverse(lhsBits)) {
375 builder.createOrFold<
comb::ExtractOp>(loc, remainder, 0, remWidth - 1);
376 remainder = builder.createOrFold<
comb::ConcatOp>(loc, shifted, lhsBit);
378 Value diff = builder.createOrFold<
comb::SubOp>(loc, remainder, wideRhs);
381 quotientBits.push_back(comb::createOrFoldNot(builder, loc, borrow));
382 remainder = builder.createOrFold<
comb::MuxOp>(loc, borrow, remainder, diff);
385 Value quotient = builder.createOrFold<
comb::ConcatOp>(loc, quotientBits);
386 Value finalRemainder =
388 return {quotient, finalRemainder};
396static std::pair<Value, Value>
398 unsigned width = lhs.getType().getIntOrFloatBitWidth();
400 auto negateIf = [&](Value value, Value shouldNegate) {
401 Value negated = builder.createOrFold<
comb::SubOp>(loc, zero, value);
402 return builder.createOrFold<
comb::MuxOp>(loc, shouldNegate, negated, value);
411 Value lhsMagnitude = negateIf(lhs, lhsNegative);
412 Value rhsMagnitude = negateIf(rhs, rhsNegative);
413 auto [quotient, remainder] =
416 Value quotientNegative =
417 builder.createOrFold<
comb::XorOp>(loc, lhsNegative, rhsNegative,
true);
418 Value signedQuotient = negateIf(quotient, quotientNegative);
419 Value signedRemainder = negateIf(remainder, lhsNegative);
420 return {signedQuotient, signedRemainder};
431 using OpConversionPattern<AndOp>::OpConversionPattern;
434 matchAndRewrite(AndOp op, OpAdaptor adaptor,
435 ConversionPatternRewriter &rewriter)
const override {
436 SmallVector<bool> nonInverts(adaptor.getInputs().size(),
false);
438 rewriter, op, adaptor.getInputs(), nonInverts);
445 using OpConversionPattern<OrOp>::OpConversionPattern;
448 matchAndRewrite(OrOp op, OpAdaptor adaptor,
449 ConversionPatternRewriter &rewriter)
const override {
451 SmallVector<bool> allInverts(adaptor.getInputs().size(),
true);
452 auto andOp = synth::aig::AndInverterOp::create(
453 rewriter, op.getLoc(), adaptor.getInputs(), allInverts);
462 using OpConversionPattern<XorOp>::OpConversionPattern;
465 matchAndRewrite(XorOp op, OpAdaptor adaptor,
466 ConversionPatternRewriter &rewriter)
const override {
467 SmallVector<bool> inverted(adaptor.getInputs().size(),
false);
469 rewriter, op, adaptor.getInputs(), inverted);
475struct SynthXorInverterOpConversion
477 using OpConversionPattern<synth::XorInverterOp>::OpConversionPattern;
480 matchAndRewrite(synth::XorInverterOp op, OpAdaptor adaptor,
481 ConversionPatternRewriter &rewriter)
const override {
482 if (op.getNumOperands() != 2)
488 auto inputs = adaptor.getInputs();
489 auto allNotInverts = op.getInverted();
490 std::array<bool, 2> allInverts = {!allNotInverts[0], !allNotInverts[1]};
492 auto notAAndNotB = synth::aig::AndInverterOp::create(rewriter, op.getLoc(),
494 auto aAndB = synth::aig::AndInverterOp::create(rewriter, op.getLoc(),
495 inputs, allNotInverts);
498 rewriter, op, notAAndNotB, aAndB,
507 using OpConversionPattern<MuxOp>::OpConversionPattern;
510 matchAndRewrite(MuxOp op, OpAdaptor adaptor,
511 ConversionPatternRewriter &rewriter)
const override {
512 Value cond = adaptor.getCond();
513 Value trueVal = adaptor.getTrueValue();
514 Value falseVal = adaptor.getFalseValue();
516 if (!op.getType().isInteger()) {
517 auto widthType = rewriter.getIntegerType(hw::getBitWidth(op.getType()));
524 if (!trueVal.getType().isInteger(1))
525 cond = comb::ReplicateOp::create(rewriter, op.getLoc(), trueVal.getType(),
528 Value result = synth::MuxInverterOp::create(rewriter, op.getLoc(), cond,
531 if (result.getType() != op.getType())
541struct SynthMuxInverterOpConversion
543 using OpConversionPattern<synth::MuxInverterOp>::OpConversionPattern;
546 matchAndRewrite(synth::MuxInverterOp op, OpAdaptor adaptor,
547 ConversionPatternRewriter &rewriter)
const override {
548 auto inputs = adaptor.getInputs();
549 auto inverted = op.getInverted();
551 auto lhs = synth::aig::AndInverterOp::create(
552 rewriter, op.getLoc(), inputs[0], inputs[1], inverted[0], inverted[1]);
554 auto rhs = synth::aig::AndInverterOp::create(
555 rewriter, op.getLoc(), inputs[0], inputs[2], !inverted[0], inverted[2]);
557 auto nand = synth::aig::AndInverterOp::create(rewriter, op.getLoc(), lhs,
565template <
typename OpTy>
567 using OpConversionPattern<OpTy>::OpConversionPattern;
568 using OpAdaptor =
typename OpConversionPattern<OpTy>::OpAdaptor;
570 matchAndRewrite(OpTy op, OpAdaptor adaptor,
571 ConversionPatternRewriter &rewriter)
const override {
578 ConversionPatternRewriter &rewriter) {
580 switch (operands.size()) {
582 llvm_unreachable(
"cannot be called with empty operand range");
589 return OpTy::create(rewriter, op.getLoc(), ValueRange{lhs, rhs},
true);
591 auto firstHalf = operands.size() / 2;
596 return OpTy::create(rewriter, op.getLoc(), ValueRange{lhs, rhs},
true);
605enum AdderArchitecture { RippleCarry, Sklanskey, KoggeStone, BrentKung };
606AdderArchitecture determineAdderArch(Operation *op, int64_t width) {
607 auto strAttr = op->getAttrOfType<StringAttr>(
"synth.test.arch");
609 return llvm::StringSwitch<AdderArchitecture>(strAttr.getValue())
610 .Case(
"SKLANSKEY", Sklanskey)
611 .Case(
"KOGGE-STONE", KoggeStone)
612 .Case(
"BRENT-KUNG", BrentKung)
613 .Case(
"RIPPLE-CARRY", RippleCarry);
623 return AdderArchitecture::RippleCarry;
628 return AdderArchitecture::Sklanskey;
632 return AdderArchitecture::KoggeStone;
641void lowerKoggeStonePrefixTree(OpBuilder &builder, Location loc,
642 SmallVector<Value> &pPrefix,
643 SmallVector<Value> &gPrefix) {
645 auto width =
static_cast<int64_t
>(pPrefix.size());
646 assert(width ==
static_cast<int64_t
>(gPrefix.size()));
647 SmallVector<Value> pPrefixNew = pPrefix;
648 SmallVector<Value> gPrefixNew = gPrefix;
651 for (int64_t stride = 1; stride < width; stride *= 2) {
653 for (int64_t i = stride; i < width; ++i) {
654 int64_t j = i - stride;
657 Value andPG = comb::AndOp::create(builder, loc, pPrefix[i], gPrefix[j]);
658 gPrefixNew[i] = comb::OrOp::create(builder, loc, gPrefix[i], andPG);
661 pPrefixNew[i] = comb::AndOp::create(builder, loc, pPrefix[i], pPrefix[j]);
664 pPrefix = pPrefixNew;
665 gPrefix = gPrefixNew;
670 for (int64_t stride = 1; stride < width; stride *= 2) {
672 <<
"--------------------------------------- Kogge-Stone Stage "
674 for (int64_t i = stride; i < width; ++i) {
675 int64_t j = i - stride;
677 llvm::dbgs() <<
"G" << i << stage + 1 <<
" = G" << i << stage
678 <<
" OR (P" << i << stage <<
" AND G" << j << stage
682 llvm::dbgs() <<
"P" << i << stage + 1 <<
" = P" << i << stage
683 <<
" AND P" << j << stage <<
"\n";
692void lowerSklanskeyPrefixTree(OpBuilder &builder, Location loc,
693 SmallVector<Value> &pPrefix,
694 SmallVector<Value> &gPrefix) {
695 auto width =
static_cast<int64_t
>(pPrefix.size());
696 assert(width ==
static_cast<int64_t
>(gPrefix.size()));
697 SmallVector<Value> pPrefixNew = pPrefix;
698 SmallVector<Value> gPrefixNew = gPrefix;
699 for (int64_t stride = 1; stride < width; stride *= 2) {
700 for (int64_t i = stride; i < width; i += 2 * stride) {
701 for (int64_t k = 0; k < stride && i + k < width; ++k) {
707 comb::AndOp::create(builder, loc, pPrefix[idx], gPrefix[j]);
708 gPrefixNew[idx] = comb::OrOp::create(builder, loc, gPrefix[idx], andPG);
712 comb::AndOp::create(builder, loc, pPrefix[idx], pPrefix[j]);
716 pPrefix = pPrefixNew;
717 gPrefix = gPrefixNew;
722 for (int64_t stride = 1; stride < width; stride *= 2) {
723 llvm::dbgs() <<
"--------------------------------------- Sklanskey Stage "
725 for (int64_t i = stride; i < width; i += 2 * stride) {
726 for (int64_t k = 0; k < stride && i + k < width; ++k) {
730 llvm::dbgs() <<
"G" << idx << stage + 1 <<
" = G" << idx << stage
731 <<
" OR (P" << idx << stage <<
" AND G" << j << stage
735 llvm::dbgs() <<
"P" << idx << stage + 1 <<
" = P" << idx << stage
736 <<
" AND P" << j << stage <<
"\n";
747void lowerBrentKungPrefixTree(OpBuilder &builder, Location loc,
748 SmallVector<Value> &pPrefix,
749 SmallVector<Value> &gPrefix) {
750 auto width =
static_cast<int64_t
>(pPrefix.size());
751 assert(width ==
static_cast<int64_t
>(gPrefix.size()));
752 SmallVector<Value> pPrefixNew = pPrefix;
753 SmallVector<Value> gPrefixNew = gPrefix;
757 for (stride = 1; stride < width; stride *= 2) {
758 for (int64_t i = stride * 2 - 1; i < width; i += stride * 2) {
759 int64_t j = i - stride;
762 Value andPG = comb::AndOp::create(builder, loc, pPrefix[i], gPrefix[j]);
763 gPrefixNew[i] = comb::OrOp::create(builder, loc, gPrefix[i], andPG);
766 pPrefixNew[i] = comb::AndOp::create(builder, loc, pPrefix[i], pPrefix[j]);
768 pPrefix = pPrefixNew;
769 gPrefix = gPrefixNew;
773 for (; stride > 0; stride /= 2) {
774 for (int64_t i = stride * 3 - 1; i < width; i += stride * 2) {
775 int64_t j = i - stride;
778 Value andPG = comb::AndOp::create(builder, loc, pPrefix[i], gPrefix[j]);
779 gPrefixNew[i] = comb::OrOp::create(builder, loc, gPrefix[i], andPG);
782 pPrefixNew[i] = comb::AndOp::create(builder, loc, pPrefix[i], pPrefix[j]);
784 pPrefix = pPrefixNew;
785 gPrefix = gPrefixNew;
790 for (stride = 1; stride < width; stride *= 2) {
791 llvm::dbgs() <<
"--------------------------------------- Brent-Kung FW "
792 << stage <<
" : Stride " << stride <<
"\n";
793 for (int64_t i = stride * 2 - 1; i < width; i += stride * 2) {
794 int64_t j = i - stride;
797 llvm::dbgs() <<
"G" << i << stage + 1 <<
" = G" << i << stage
798 <<
" OR (P" << i << stage <<
" AND G" << j << stage
802 llvm::dbgs() <<
"P" << i << stage + 1 <<
" = P" << i << stage
803 <<
" AND P" << j << stage <<
"\n";
808 for (; stride > 0; stride /= 2) {
809 if (stride * 3 - 1 < width)
810 llvm::dbgs() <<
"--------------------------------------- Brent-Kung BW "
811 << stage <<
" : Stride " << stride <<
"\n";
813 for (int64_t i = stride * 3 - 1; i < width; i += stride * 2) {
814 int64_t j = i - stride;
817 llvm::dbgs() <<
"G" << i << stage + 1 <<
" = G" << i << stage
818 <<
" OR (P" << i << stage <<
" AND G" << j << stage
822 llvm::dbgs() <<
"P" << i << stage + 1 <<
" = P" << i << stage
823 <<
" AND P" << j << stage <<
"\n";
831class LazyKoggeStonePrefixTree {
833 LazyKoggeStonePrefixTree(OpBuilder &builder, Location loc, int64_t width,
834 ArrayRef<Value> pPrefix, ArrayRef<Value> gPrefix)
835 : builder(builder), loc(loc), width(width) {
836 assert(width > 0 &&
"width must be positive");
837 for (int64_t i = 0; i < width; ++i)
838 prefixCache[{0, i}] = {pPrefix[i], gPrefix[i]};
842 std::pair<Value, Value> getFinal(int64_t i) {
843 assert(i >= 0 && i < width &&
"i out of bounds");
845 return getGroupAndPropagate(llvm::Log2_64_Ceil(width), i);
853 std::pair<Value, Value> getGroupAndPropagate(int64_t level, int64_t i);
857 DenseMap<std::pair<int64_t, int64_t>, std::pair<Value, Value>> prefixCache;
860std::pair<Value, Value>
861LazyKoggeStonePrefixTree::getGroupAndPropagate(int64_t level, int64_t i) {
862 assert(i < width &&
"i out of bounds");
863 auto key = std::make_pair(level, i);
864 auto it = prefixCache.find(key);
865 if (it != prefixCache.end())
868 assert(level > 0 &&
"If the level is 0, we should have hit the cache");
870 int64_t previousStride = 1ULL << (level - 1);
871 if (i < previousStride) {
873 auto [propagateI, generateI] = getGroupAndPropagate(level - 1, i);
874 prefixCache[key] = {propagateI, generateI};
875 return prefixCache[key];
878 int64_t j = i - previousStride;
879 auto [propagateI, generateI] = getGroupAndPropagate(level - 1, i);
880 auto [propagateJ, generateJ] = getGroupAndPropagate(level - 1, j);
882 Value andPG = comb::AndOp::create(builder, loc, propagateI, generateJ);
883 Value newGenerate = comb::OrOp::create(builder, loc, generateI, andPG);
886 comb::AndOp::create(builder, loc, propagateI, propagateJ);
887 prefixCache[key] = {newPropagate, newGenerate};
888 return prefixCache[key];
891struct CombAddOpConversion : OpConversionPattern<AddOp> {
892 using OpConversionPattern<AddOp>::OpConversionPattern;
895 matchAndRewrite(AddOp op, OpAdaptor adaptor,
896 ConversionPatternRewriter &rewriter)
const override {
897 auto inputs = adaptor.getInputs();
900 if (inputs.size() == 3) {
902 dyn_cast_or_null<hw::ConstantOp>(op.getOperand(2).getDefiningOp());
903 if (!constOp || !constOp.getValue().isOne())
912 return lowerAdder(op, inputs.take_front(2), constOne, rewriter);
917 if (inputs.size() != 2)
920 auto width = op.getType().getIntOrFloatBitWidth();
928 return lowerAdder(op, inputs, Value(), rewriter);
933 lowerRippleCarryAdder(comb::AddOp op, ValueRange inputs, Value carryIn,
934 ConversionPatternRewriter &rewriter)
const {
935 auto width = op.getType().getIntOrFloatBitWidth();
937 Value carry = carryIn;
941 SmallVector<Value> results;
942 results.resize(width);
943 for (int64_t i = 0; i < width; ++i) {
944 SmallVector<Value> xorOperands = {aBits[i], bBits[i]};
946 xorOperands.push_back(carry);
950 results[width - i - 1] =
951 comb::XorOp::create(rewriter, op.getLoc(), xorOperands,
true);
960 carry = comb::AndOp::create(rewriter, op.getLoc(),
961 ValueRange{aBits[i], bBits[i]},
true);
968 LLVM_DEBUG(llvm::dbgs() <<
"Lower comb.add to Ripple-Carry Adder of width "
978 LogicalResult lowerAdder(comb::AddOp op, ValueRange inputs, Value carryIn,
979 ConversionPatternRewriter &rewriter)
const {
983 assert(carryIn ==
nullptr ||
984 carryIn.getType().getIntOrFloatBitWidth() == 1 &&
985 "carryIn must be a 1-bit value");
988 auto width = op.getType().getIntOrFloatBitWidth();
989 auto arch = determineAdderArch(op, width);
990 if (arch == AdderArchitecture::RippleCarry)
991 return lowerRippleCarryAdder(op, inputs, carryIn, rewriter);
997 SmallVector<Value> p, g;
1001 for (
auto [aBit, bBit] : llvm::zip(aBits, bBits)) {
1003 p.push_back(comb::XorOp::create(rewriter, op.getLoc(), aBit, bBit));
1005 g.push_back(comb::AndOp::create(rewriter, op.getLoc(), aBit, bBit));
1011 Value pAndC = comb::AndOp::create(rewriter, op.getLoc(), p[0], carryIn);
1012 g[0] = comb::OrOp::create(rewriter, op.getLoc(), g[0], pAndC);
1016 llvm::dbgs() <<
"Lower comb.add to Parallel-Prefix of width " << width
1017 <<
"\n--------------------------------------- Init\n";
1019 for (int64_t i = 0; i < width; ++i) {
1021 llvm::dbgs() <<
"P0" << i <<
" = A" << i <<
" XOR B" << i <<
"\n";
1022 if (i == 0 && carryIn)
1023 llvm::dbgs() <<
"G0" << i <<
" = (A" << i <<
" AND B" << i
1024 <<
") OR (P" << i <<
" AND CARRY_IN)\n";
1027 llvm::dbgs() <<
"G0" << i <<
" = A" << i <<
" AND B" << i <<
"\n";
1032 SmallVector<Value> pPrefix = p;
1033 SmallVector<Value> gPrefix = g;
1037 case AdderArchitecture::RippleCarry:
1038 llvm_unreachable(
"Ripple-Carry handled above");
1040 case AdderArchitecture::Sklanskey:
1041 lowerSklanskeyPrefixTree(rewriter, op.getLoc(), pPrefix, gPrefix);
1043 case AdderArchitecture::KoggeStone:
1044 lowerKoggeStonePrefixTree(rewriter, op.getLoc(), pPrefix, gPrefix);
1046 case AdderArchitecture::BrentKung:
1047 lowerBrentKungPrefixTree(rewriter, op.getLoc(), pPrefix, gPrefix);
1053 SmallVector<Value> results;
1054 results.resize(width);
1056 results[width - 1] =
1057 carryIn ? comb::XorOp::create(rewriter, op.getLoc(), p[0], carryIn)
1062 for (int64_t i = 1; i < width; ++i)
1063 results[width - 1 - i] =
1064 comb::XorOp::create(rewriter, op.getLoc(), p[i], gPrefix[i - 1]);
1069 llvm::dbgs() <<
"--------------------------------------- Completion\n";
1072 llvm::dbgs() <<
"RES0 = P0 XOR CARRY_IN\n";
1074 llvm::dbgs() <<
"RES0 = P0\n";
1075 for (int64_t i = 1; i < width; ++i)
1076 llvm::dbgs() <<
"RES" << i <<
" = P" << i <<
" XOR G" << i - 1 <<
"\n";
1083struct CombMulOpConversion : OpConversionPattern<MulOp> {
1084 using OpConversionPattern<MulOp>::OpConversionPattern;
1085 using OpAdaptor =
typename OpConversionPattern<MulOp>::OpAdaptor;
1087 matchAndRewrite(MulOp op, OpAdaptor adaptor,
1088 ConversionPatternRewriter &rewriter)
const override {
1089 if (adaptor.getInputs().size() != 2)
1092 Location loc = op.getLoc();
1093 Value a = adaptor.getInputs()[0];
1094 Value b = adaptor.getInputs()[1];
1095 unsigned width = op.getType().getIntOrFloatBitWidth();
1099 rewriter.replaceOpWithNewOp<hw::ConstantOp>(op, op.getType(), 0);
1104 SmallVector<Value> aBits =
extractBits(rewriter, a);
1105 SmallVector<Value> bBits =
extractBits(rewriter, b);
1110 SmallVector<SmallVector<Value>> partialProducts;
1111 partialProducts.reserve(width);
1112 for (
unsigned i = 0; i < width; ++i) {
1113 SmallVector<Value> row(i, falseValue);
1116 for (
unsigned j = 0; i + j < width; ++j)
1118 rewriter.createOrFold<comb::AndOp>(loc, aBits[j], bBits[i]));
1120 partialProducts.push_back(row);
1125 rewriter.replaceOp(op, partialProducts[0][0]);
1130 datapath::CompressorTree comp(width, partialProducts, loc, rewriter);
1131 auto addends = comp.compressToHeight(rewriter, 2);
1134 auto newAdd = comb::AddOp::create(rewriter, loc, addends,
true);
1140template <
typename OpTy>
1141struct DivModOpConversionBase : OpConversionPattern<OpTy> {
1142 DivModOpConversionBase(MLIRContext *
context, int64_t maxEmulationUnknownBits)
1143 : OpConversionPattern<OpTy>(
context),
1144 maxEmulationUnknownBits(maxEmulationUnknownBits) {
1145 assert(maxEmulationUnknownBits < 32 &&
1146 "maxEmulationUnknownBits must be less than 32");
1148 const int64_t maxEmulationUnknownBits;
1151struct CombDivUOpConversion : DivModOpConversionBase<DivUOp> {
1152 using DivModOpConversionBase<DivUOp>::DivModOpConversionBase;
1154 matchAndRewrite(DivUOp op, OpAdaptor adaptor,
1155 ConversionPatternRewriter &rewriter)
const override {
1157 if (llvm::succeeded(comb::convertDivUByPowerOfTwo(op, rewriter)))
1162 if (
auto rhsConst = adaptor.getRhs().getDefiningOp<hw::ConstantOp>()) {
1163 APInt divisor = rhsConst.getValue();
1165 if (divisor.isZero()) {
1180 rewriter, maxEmulationUnknownBits, op,
1181 [](
const APInt &lhs,
const APInt &rhs) {
1184 return APInt::getZero(rhs.getBitWidth());
1185 return lhs.udiv(rhs);
1199struct CombModUOpConversion : DivModOpConversionBase<ModUOp> {
1200 using DivModOpConversionBase<ModUOp>::DivModOpConversionBase;
1202 matchAndRewrite(ModUOp op, OpAdaptor adaptor,
1203 ConversionPatternRewriter &rewriter)
const override {
1205 if (llvm::succeeded(comb::convertModUByPowerOfTwo(op, rewriter)))
1210 if (
auto rhsConst = adaptor.getRhs().getDefiningOp<hw::ConstantOp>()) {
1211 APInt divisor = rhsConst.getValue();
1213 if (divisor.isZero()) {
1218 auto loc = op.getLoc();
1222 rewriter.createOrFold<comb::MulOp>(loc, q, adaptor.getRhs());
1224 rewriter.createOrFold<comb::SubOp>(loc, adaptor.getLhs(), product);
1233 rewriter, maxEmulationUnknownBits, op,
1234 [](
const APInt &lhs,
const APInt &rhs) {
1237 return APInt::getZero(rhs.getBitWidth());
1238 return lhs.urem(rhs);
1252struct CombDivSOpConversion : DivModOpConversionBase<DivSOp> {
1253 using DivModOpConversionBase<DivSOp>::DivModOpConversionBase;
1256 matchAndRewrite(DivSOp op, OpAdaptor adaptor,
1257 ConversionPatternRewriter &rewriter)
const override {
1260 if (
auto rhsConst = adaptor.getRhs().getDefiningOp<hw::ConstantOp>()) {
1261 APInt divisor = rhsConst.getValue();
1262 unsigned width = op.getType().getIntOrFloatBitWidth();
1264 if (divisor.isZero()) {
1270 if (divisor.isOne()) {
1275 if (divisor.isAllOnes()) {
1278 rewriter.createOrFold<comb::SubOp>(
1281 APInt::getZero(width)),
1295 rewriter, maxEmulationUnknownBits, op,
1296 [](
const APInt &lhs,
const APInt &rhs) {
1299 return APInt::getZero(rhs.getBitWidth());
1300 return lhs.sdiv(rhs);
1314struct CombModSOpConversion : DivModOpConversionBase<ModSOp> {
1315 using DivModOpConversionBase<ModSOp>::DivModOpConversionBase;
1317 matchAndRewrite(ModSOp op, OpAdaptor adaptor,
1318 ConversionPatternRewriter &rewriter)
const override {
1321 if (
auto rhsConst = adaptor.getRhs().getDefiningOp<hw::ConstantOp>()) {
1322 APInt divisor = rhsConst.getValue();
1324 if (divisor.isZero() || divisor.isOne() || divisor.isAllOnes()) {
1329 auto loc = op.getLoc();
1333 rewriter.createOrFold<comb::MulOp>(loc, q, adaptor.getRhs());
1335 rewriter.createOrFold<comb::SubOp>(loc, adaptor.getLhs(), product);
1343 rewriter, maxEmulationUnknownBits, op,
1344 [](
const APInt &lhs,
const APInt &rhs) {
1347 return APInt::getZero(rhs.getBitWidth());
1348 return lhs.srem(rhs);
1362struct CombICmpOpConversion : OpConversionPattern<ICmpOp> {
1363 using OpConversionPattern<ICmpOp>::OpConversionPattern;
1366 static Value constructRippleCarry(Location loc, Value a, Value b,
1368 ConversionPatternRewriter &rewriter) {
1376 for (
auto [aBit, bBit] : llvm::zip(aBits, bBits)) {
1378 rewriter.createOrFold<comb::XorOp>(loc, aBit, bBit,
true);
1379 auto aEqualB = rewriter.createOrFold<synth::aig::AndInverterOp>(
1380 loc, aBitXorBBit,
true);
1381 auto pred = rewriter.createOrFold<synth::aig::AndInverterOp>(
1382 loc, aBit, bBit,
true,
false);
1384 auto aBitAndBBit = rewriter.createOrFold<comb::AndOp>(
1385 loc, ValueRange{aEqualB,
acc},
true);
1386 acc = rewriter.createOrFold<comb::OrOp>(loc, pred, aBitAndBBit,
true);
1399 static Value computePrefixComparison(ConversionPatternRewriter &rewriter,
1400 Location loc, SmallVector<Value> pPrefix,
1401 SmallVector<Value> gPrefix,
1402 bool includeEq, AdderArchitecture arch) {
1403 auto width = pPrefix.size();
1404 Value finalGroup, finalPropagate;
1407 case AdderArchitecture::RippleCarry:
1408 llvm_unreachable(
"Ripple-Carry should be handled separately");
1410 case AdderArchitecture::Sklanskey: {
1411 lowerSklanskeyPrefixTree(rewriter, loc, pPrefix, gPrefix);
1412 finalGroup = gPrefix[width - 1];
1413 finalPropagate = pPrefix[width - 1];
1416 case AdderArchitecture::KoggeStone:
1419 std::tie(finalPropagate, finalGroup) =
1420 LazyKoggeStonePrefixTree(rewriter, loc, width, pPrefix, gPrefix)
1421 .getFinal(width - 1);
1423 case AdderArchitecture::BrentKung: {
1424 lowerBrentKungPrefixTree(rewriter, loc, pPrefix, gPrefix);
1425 finalGroup = gPrefix[width - 1];
1426 finalPropagate = pPrefix[width - 1];
1435 return comb::OrOp::create(rewriter, loc, finalGroup, finalPropagate);
1444 static Value constructUnsignedCompare(Operation *op, Location loc, Value a,
1445 Value b,
bool isLess,
bool includeEq,
1446 ConversionPatternRewriter &rewriter) {
1450 auto width = a.getType().getIntOrFloatBitWidth();
1453 auto arch = determineAdderArch(op, width);
1454 if (arch == AdderArchitecture::RippleCarry)
1455 return constructRippleCarry(loc, a, b, includeEq, rewriter);
1466 SmallVector<Value> eq, gt;
1473 for (
auto [aBit, bBit] : llvm::zip(aBits, bBits)) {
1475 auto xorBit = comb::XorOp::create(rewriter, loc, aBit, bBit);
1476 eq.push_back(comb::XorOp::create(rewriter, loc, xorBit, one));
1479 auto notA = comb::XorOp::create(rewriter, loc, aBit, one);
1480 gt.push_back(comb::AndOp::create(rewriter, loc, notA, bBit));
1483 return computePrefixComparison(rewriter, loc, std::move(eq), std::move(gt),
1488 matchAndRewrite(ICmpOp op, OpAdaptor adaptor,
1489 ConversionPatternRewriter &rewriter)
const override {
1490 auto lhs = adaptor.getLhs();
1491 auto rhs = adaptor.getRhs();
1493 switch (op.getPredicate()) {
1497 case ICmpPredicate::eq:
1498 case ICmpPredicate::ceq: {
1500 auto xorOp = rewriter.createOrFold<comb::XorOp>(op.getLoc(), lhs, rhs);
1502 SmallVector<bool> allInverts(xorBits.size(),
true);
1504 rewriter, op, xorBits, allInverts);
1508 case ICmpPredicate::ne:
1509 case ICmpPredicate::cne: {
1511 auto xorOp = rewriter.createOrFold<comb::XorOp>(op.getLoc(), lhs, rhs);
1513 rewriter, op,
extractBits(rewriter, xorOp),
true);
1517 case ICmpPredicate::uge:
1518 case ICmpPredicate::ugt:
1519 case ICmpPredicate::ule:
1520 case ICmpPredicate::ult: {
1521 bool isLess = op.getPredicate() == ICmpPredicate::ult ||
1522 op.getPredicate() == ICmpPredicate::ule;
1523 bool includeEq = op.getPredicate() == ICmpPredicate::uge ||
1524 op.getPredicate() == ICmpPredicate::ule;
1526 constructUnsignedCompare(op, op.getLoc(), lhs,
1527 rhs, isLess, includeEq,
1531 case ICmpPredicate::slt:
1532 case ICmpPredicate::sle:
1533 case ICmpPredicate::sgt:
1534 case ICmpPredicate::sge: {
1535 if (lhs.getType().getIntOrFloatBitWidth() == 0)
1536 return rewriter.notifyMatchFailure(
1537 op.getLoc(),
"i0 signed comparison is unsupported");
1538 bool isLess = op.getPredicate() == ICmpPredicate::slt ||
1539 op.getPredicate() == ICmpPredicate::sle;
1540 bool includeEq = op.getPredicate() == ICmpPredicate::sge ||
1541 op.getPredicate() == ICmpPredicate::sle;
1550 auto sameSignResult = constructUnsignedCompare(
1551 op, op.getLoc(), aRest, bRest, isLess, includeEq, rewriter);
1555 comb::XorOp::create(rewriter, op.getLoc(), signA, signB);
1558 Value diffSignResult = isLess ? signA : signB;
1562 rewriter, op, signsDiffer, diffSignResult, sameSignResult);
1569struct CombParityOpConversion : OpConversionPattern<ParityOp> {
1570 using OpConversionPattern<ParityOp>::OpConversionPattern;
1573 matchAndRewrite(ParityOp op, OpAdaptor adaptor,
1574 ConversionPatternRewriter &rewriter)
const override {
1577 rewriter, op,
extractBits(rewriter, adaptor.getInput()),
true);
1582struct CombShlOpConversion : OpConversionPattern<comb::ShlOp> {
1583 using OpConversionPattern<comb::ShlOp>::OpConversionPattern;
1586 matchAndRewrite(comb::ShlOp op, OpAdaptor adaptor,
1587 ConversionPatternRewriter &rewriter)
const override {
1588 auto width = op.getType().getIntOrFloatBitWidth();
1589 auto lhs = adaptor.getLhs();
1591 rewriter, op.getLoc(), adaptor.getRhs(), width,
1593 [&](int64_t index) {
1598 return rewriter.createOrFold<hw::ConstantOp>(
1599 op.getLoc(), rewriter.getIntegerType(index), 0);
1602 [&](int64_t index) {
1603 assert(index < width &&
"index out of bounds");
1605 return rewriter.createOrFold<comb::ExtractOp>(op.getLoc(), lhs, 0,
1614struct CombShrUOpConversion : OpConversionPattern<comb::ShrUOp> {
1615 using OpConversionPattern<comb::ShrUOp>::OpConversionPattern;
1618 matchAndRewrite(comb::ShrUOp op, OpAdaptor adaptor,
1619 ConversionPatternRewriter &rewriter)
const override {
1620 auto width = op.getType().getIntOrFloatBitWidth();
1621 auto lhs = adaptor.getLhs();
1623 rewriter, op.getLoc(), adaptor.getRhs(), width,
1625 [&](int64_t index) {
1630 return rewriter.createOrFold<hw::ConstantOp>(
1631 op.getLoc(), rewriter.getIntegerType(index), 0);
1634 [&](int64_t index) {
1635 assert(index < width &&
"index out of bounds");
1637 return rewriter.createOrFold<comb::ExtractOp>(op.getLoc(), lhs, index,
1646struct CombShrSOpConversion : OpConversionPattern<comb::ShrSOp> {
1647 using OpConversionPattern<comb::ShrSOp>::OpConversionPattern;
1650 matchAndRewrite(comb::ShrSOp op, OpAdaptor adaptor,
1651 ConversionPatternRewriter &rewriter)
const override {
1652 auto width = op.getType().getIntOrFloatBitWidth();
1654 return rewriter.notifyMatchFailure(op.getLoc(),
1655 "i0 signed shift is unsupported");
1656 auto lhs = adaptor.getLhs();
1659 rewriter.createOrFold<comb::ExtractOp>(op.getLoc(), lhs, width - 1, 1);
1664 rewriter, op.getLoc(), adaptor.getRhs(), width - 1,
1666 [&](int64_t index) {
1667 return rewriter.createOrFold<comb::ReplicateOp>(op.getLoc(), sign,
1671 [&](int64_t index) {
1672 return rewriter.createOrFold<comb::ExtractOp>(op.getLoc(), lhs, index,
1688struct ConvertCombToSynthPass
1689 :
public impl::ConvertCombToSynthBase<ConvertCombToSynthPass> {
1690 void runOnOperation()
override;
1691 using ConvertCombToSynthBase<ConvertCombToSynthPass>::ConvertCombToSynthBase;
1697 uint32_t maxEmulationUnknownBits,
1701 CombAndOpConversion, CombParityOpConversion, CombXorOpToSynthConversion,
1702 CombMuxOpToSynthConversion,
1704 CombMulOpConversion, CombICmpOpConversion,
1706 CombShlOpConversion, CombShrUOpConversion, CombShrSOpConversion,
1708 CombLowerVariadicOp<AddOp>, CombLowerVariadicOp<MulOp>>(
1712 patterns.add<SynthXorInverterOpConversion, SynthMuxInverterOpConversion>(
1715 patterns.add(comb::convertSubToAdd);
1717 patterns.add<CombOrToAIGConversion, CombAddOpConversion>(
1725 patterns.add<CombDivUOpConversion, CombModUOpConversion, CombDivSOpConversion,
1726 CombModSOpConversion>(
patterns.getContext(),
1727 maxEmulationUnknownBits);
1730void ConvertCombToSynthPass::runOnOperation() {
1731 ConversionTarget target(getContext());
1734 target.addIllegalDialect<comb::CombDialect>();
1736 target.addLegalOp<comb::ExtractOp, comb::ConcatOp, comb::ReplicateOp,
1737 hw::BitcastOp, hw::ConstantOp>();
1743 target.addIllegalOp<hw::ArrayGetOp, hw::ArrayCreateOp, hw::ArrayConcatOp,
1744 hw::AggregateConstantOp>();
1746 target.addLegalDialect<synth::SynthDialect>();
1748 target.addIllegalOp<synth::XorInverterOp, synth::MuxInverterOp>();
1751 if (!additionalLegalOps.empty())
1752 for (
const auto &opName : additionalLegalOps)
1753 target.addLegalOp(OperationName(opName, &getContext()));
1755 RewritePatternSet patterns(&getContext());
1759 if (failed(mlir::applyPartialConversion(getOperation(), target,
1760 std::move(patterns))))
1761 return signalPassFailure();
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.
static SmallVector< Value > extractBits(OpBuilder &builder, Value val)
static Value createShiftLogic(ConversionPatternRewriter &rewriter, Location loc, Value shiftAmount, int64_t maxShiftAmount, llvm::function_ref< Value(int64_t)> getPadding, llvm::function_ref< Value(int64_t)> getExtract)
static Value createAShrByConstant(OpBuilder &builder, Location loc, Value value, unsigned amount)
static Value createMulHigh(OpBuilder &builder, Location loc, Value lhs, const APInt &rhs)
static APInt substitueMaskToValues(size_t width, llvm::SmallVectorImpl< ConstantOrValue > &constantOrValues, uint32_t mask)
static std::pair< Value, Value > lowerUnsignedDivRem(OpBuilder &builder, Location loc, Value lhs, Value rhs)
Lower an unsigned division with a non-constant divisor to a combinational restoring array divider.
static Value lowerSignedDivByConstant(OpBuilder &builder, Location loc, Value lhs, const APInt &divisor)
static Value createLShrByConstant(OpBuilder &builder, Location loc, Value value, unsigned amount)
static LogicalResult emulateBinaryOpForUnknownBits(ConversionPatternRewriter &rewriter, int64_t maxEmulationUnknownBits, Operation *op, llvm::function_ref< APInt(const APInt &, const APInt &)> emulate)
static int64_t getNumUnknownBitsAndPopulateValues(Value value, llvm::SmallVectorImpl< ConstantOrValue > &values)
static Value createMajorityFunction(OpBuilder &rewriter, Location loc, Value a, Value b, Value carry)
static Value extractOtherThanMSB(OpBuilder &builder, Value val)
static Value extractMSB(OpBuilder &builder, Value val)
static void populateCombToAIGConversionPatterns(RewritePatternSet &patterns, uint32_t maxEmulationUnknownBits, bool forceAIG)
static Value lowerUnsignedDivByConstant(OpBuilder &builder, Location loc, Value lhs, const APInt &divisor)
static std::pair< Value, Value > lowerSignedDivRem(OpBuilder &builder, Location loc, Value lhs, Value rhs)
Lower a signed division with a non-constant divisor to a restoring array divider operating on the mag...
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'.
static Value lowerFullyAssociativeOp(Operation &op, OperandRange operands, SmallVector< Operation * > &newOps)
Lower a variadic fully-associative operation into an expression tree.
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
void populateVariadicXorInverterLoweringPatterns(mlir::RewritePatternSet &patterns)
void populateVariadicAndInverterLoweringPatterns(mlir::RewritePatternSet &patterns)
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.
void replaceOpAndCopyNamehint(PatternRewriter &rewriter, Operation *op, Value newValue)
A wrapper of PatternRewriter::replaceOp to propagate "sv.namehint" attribute.