31#include "mlir/Pass/Pass.h"
32#include "mlir/Transforms/DialectConversion.h"
33#include "llvm/ADT/APInt.h"
34#include "llvm/ADT/PointerUnion.h"
35#include "llvm/Support/Debug.h"
36#include "llvm/Support/DivisionByConstantInfo.h"
39#define DEBUG_TYPE "comb-to-synth"
42#define GEN_PASS_DEF_CONVERTCOMBTOSYNTH
43#include "circt/Conversion/Passes.h.inc"
54static SmallVector<Value>
extractBits(OpBuilder &builder, Value val) {
55 SmallVector<Value> bits;
56 comb::extractBits(builder, val, bits);
67template <
bool isLeftShift>
69 Value shiftAmount, int64_t maxShiftAmount,
70 llvm::function_ref<Value(int64_t)> getPadding,
71 llvm::function_ref<Value(int64_t)> getExtract) {
76 SmallVector<Value> nodes;
77 nodes.reserve(maxShiftAmount);
78 for (int64_t i = 0; i < maxShiftAmount; ++i) {
79 Value extract = getExtract(i);
80 Value padding = getPadding(i);
83 nodes.push_back(extract);
97 auto outOfBoundsValue = getPadding(maxShiftAmount);
98 assert(outOfBoundsValue &&
"outOfBoundsValue must be valid");
102 comb::constructMuxTree(rewriter, loc, bits, nodes, outOfBoundsValue);
105 auto inBound = rewriter.createOrFold<comb::ICmpOp>(
106 loc, ICmpPredicate::ult, shiftAmount,
110 return rewriter.createOrFold<
comb::MuxOp>(loc, inBound, result,
117 Value b, Value carry) {
119 auto aXnorB = comb::XorOp::create(rewriter, loc, ValueRange{a, b},
true);
121 comb::AndOp::create(rewriter, loc, ValueRange{carry, aXnorB},
true);
122 auto aAndB = comb::AndOp::create(rewriter, loc, ValueRange{a, b},
true);
123 return comb::OrOp::create(rewriter, loc, ValueRange{andOp, aAndB},
true);
128 val.getLoc(), val, val.getType().getIntOrFloatBitWidth() - 1, 1);
133 val.getLoc(), val, 0, val.getType().getIntOrFloatBitWidth() - 1);
138using ConstantOrValue = llvm::PointerUnion<Value, mlir::IntegerAttr>;
143 Value value, llvm::SmallVectorImpl<ConstantOrValue> &values) {
145 if (value.getType().isInteger(0))
150 int64_t totalUnknownBits = 0;
151 for (
auto concatInput : llvm::reverse(concat.getInputs())) {
156 totalUnknownBits += unknownBits;
158 return totalUnknownBits;
163 values.push_back(constant.getValueAttr());
169 values.push_back(value);
170 return hw::getBitWidth(value.getType());
176 llvm::SmallVectorImpl<ConstantOrValue> &constantOrValues,
178 uint32_t bitPos = 0, unknownPos = 0;
179 APInt result(width, 0);
180 for (
auto constantOrValue : constantOrValues) {
182 if (
auto constant = dyn_cast<IntegerAttr>(constantOrValue)) {
183 elemWidth = constant.getValue().getBitWidth();
184 result.insertBits(constant.getValue(), bitPos);
186 elemWidth = hw::getBitWidth(cast<Value>(constantOrValue).getType());
187 assert(elemWidth >= 0 &&
"unknown bit width");
188 assert(elemWidth + unknownPos < 32 &&
"unknown bit width too large");
190 uint32_t usedBits = (mask >> unknownPos) & ((1 << elemWidth) - 1);
191 result.insertBits(APInt(elemWidth, usedBits), bitPos);
192 unknownPos += elemWidth;
204 ConversionPatternRewriter &rewriter, int64_t maxEmulationUnknownBits,
206 llvm::function_ref<APInt(
const APInt &,
const APInt &)> emulate) {
207 SmallVector<ConstantOrValue> lhsValues, rhsValues;
209 assert(op->getNumResults() == 1 && op->getNumOperands() == 2 &&
210 "op must be a single result binary operation");
212 auto lhs = op->getOperand(0);
213 auto rhs = op->getOperand(1);
214 auto width = op->getResult(0).getType().getIntOrFloatBitWidth();
215 auto loc = op->getLoc();
220 if (numLhsUnknownBits < 0 || numRhsUnknownBits < 0)
223 int64_t totalUnknownBits = numLhsUnknownBits + numRhsUnknownBits;
224 if (totalUnknownBits > maxEmulationUnknownBits)
227 SmallVector<Value> emulatedResults;
228 emulatedResults.reserve(1 << totalUnknownBits);
231 DenseMap<IntegerAttr, hw::ConstantOp> constantPool;
233 auto attr = rewriter.getIntegerAttr(rewriter.getIntegerType(width), value);
234 auto it = constantPool.find(attr);
235 if (it != constantPool.end())
238 constantPool[attr] = constant;
242 for (uint32_t lhsMask = 0, lhsMaskEnd = 1 << numLhsUnknownBits;
243 lhsMask < lhsMaskEnd; ++lhsMask) {
245 for (uint32_t rhsMask = 0, rhsMaskEnd = 1 << numRhsUnknownBits;
246 rhsMask < rhsMaskEnd; ++rhsMask) {
249 emulatedResults.push_back(
getConstant(emulate(lhsValue, rhsValue)));
254 SmallVector<Value> selectors;
255 selectors.reserve(totalUnknownBits);
256 for (
auto &concatedValues : {rhsValues, lhsValues})
257 for (
auto valueOrConstant : concatedValues) {
258 auto value = dyn_cast<Value>(valueOrConstant);
264 assert(totalUnknownBits ==
static_cast<int64_t
>(selectors.size()) &&
265 "number of selectors must match");
266 auto muxed = constructMuxTree(rewriter, loc, selectors, emulatedResults,
281 APInt(value.getType().getIntOrFloatBitWidth(), amount)));
292 APInt(value.getType().getIntOrFloatBitWidth(), amount)));
295template <
bool isSigned>
298 unsigned width = lhs.getType().getIntOrFloatBitWidth();
299 auto destTy = builder.getIntegerType(width << 1);
302 Value wideLhs = isSigned ? comb::createOrFoldSExt(builder, loc, lhs, destTy)
303 : comb::createZExt(builder, loc, lhs, width << 1);
305 builder, loc, isSigned ? rhs.sext(width << 1) : rhs.zext(width << 1));
307 loc, ValueRange{wideLhs, wideRhs},
true);
308 return builder.createOrFold<
comb::ExtractOp>(loc, product, width, width);
312 Value lhs,
const APInt &divisor) {
313 auto info = llvm::UnsignedDivisionByConstantInfo::get(divisor);
315 q = createMulHigh<false>(builder, loc, q, info.Magic);
317 Value diff = builder.createOrFold<
comb::SubOp>(loc, lhs, q);
319 q = builder.createOrFold<
comb::AddOp>(loc, q, diff);
325 Value lhs,
const APInt &divisor) {
326 unsigned width = lhs.getType().getIntOrFloatBitWidth();
327 auto info = llvm::SignedDivisionByConstantInfo::get(divisor);
328 Value q = createMulHigh<true>(builder, loc, lhs, info.Magic);
331 if (divisor.isStrictlyPositive() && info.Magic.isNegative())
332 q = builder.createOrFold<
comb::AddOp>(loc, q, lhs);
333 else if (divisor.isNegative() && info.Magic.isStrictlyPositive())
334 q = builder.createOrFold<
comb::SubOp>(loc, q, lhs);
338 Value signBit = builder.createOrFold<
comb::ExtractOp>(loc, q, width - 1, 1);
339 Value signPadded = comb::createZExt(builder, loc, signBit, width);
340 return builder.createOrFold<
comb::AddOp>(loc, q, signPadded);
354 matchAndRewrite(
AndOp op, OpAdaptor adaptor,
355 ConversionPatternRewriter &rewriter)
const override {
356 SmallVector<bool> nonInverts(adaptor.getInputs().size(),
false);
357 replaceOpWithNewOpAndCopyNamehint<synth::aig::AndInverterOp>(
358 rewriter, op, adaptor.getInputs(), nonInverts);
368 matchAndRewrite(
OrOp op, OpAdaptor adaptor,
369 ConversionPatternRewriter &rewriter)
const override {
371 SmallVector<bool> allInverts(adaptor.getInputs().size(),
true);
372 auto andOp = synth::aig::AndInverterOp::create(
373 rewriter, op.getLoc(), adaptor.getInputs(), allInverts);
374 replaceOpWithNewOpAndCopyNamehint<synth::aig::AndInverterOp>(
385 matchAndRewrite(
XorOp op, OpAdaptor adaptor,
386 ConversionPatternRewriter &rewriter)
const override {
387 SmallVector<bool> inverted(adaptor.getInputs().size(),
false);
388 replaceOpWithNewOpAndCopyNamehint<synth::XorInverterOp>(
389 rewriter, op, adaptor.getInputs(), inverted);
395struct SynthXorInverterOpConversion
400 matchAndRewrite(synth::XorInverterOp op, OpAdaptor adaptor,
401 ConversionPatternRewriter &rewriter)
const override {
402 if (op.getNumOperands() != 2)
408 auto inputs = adaptor.getInputs();
409 auto allNotInverts = op.getInverted();
410 std::array<bool, 2> allInverts = {!allNotInverts[0], !allNotInverts[1]};
412 auto notAAndNotB = synth::aig::AndInverterOp::create(rewriter, op.getLoc(),
414 auto aAndB = synth::aig::AndInverterOp::create(rewriter, op.getLoc(),
415 inputs, allNotInverts);
417 replaceOpWithNewOpAndCopyNamehint<synth::aig::AndInverterOp>(
418 rewriter, op, notAAndNotB, aAndB,
430 matchAndRewrite(
MuxOp op, OpAdaptor adaptor,
431 ConversionPatternRewriter &rewriter)
const override {
432 Value cond = adaptor.getCond();
433 Value trueVal = adaptor.getTrueValue();
434 Value falseVal = adaptor.getFalseValue();
436 if (!op.getType().isInteger()) {
437 auto widthType = rewriter.getIntegerType(hw::getBitWidth(op.getType()));
444 if (!trueVal.getType().isInteger(1))
445 cond = comb::ReplicateOp::create(rewriter, op.getLoc(), trueVal.getType(),
448 Value result = synth::MuxInverterOp::create(rewriter, op.getLoc(), cond,
451 if (result.getType() != op.getType())
461struct SynthMuxInverterOpConversion
466 matchAndRewrite(synth::MuxInverterOp op, OpAdaptor adaptor,
467 ConversionPatternRewriter &rewriter)
const override {
468 auto inputs = adaptor.getInputs();
469 auto inverted = op.getInverted();
471 auto lhs = synth::aig::AndInverterOp::create(
472 rewriter, op.getLoc(), inputs[0], inputs[1], inverted[0], inverted[1]);
474 auto rhs = synth::aig::AndInverterOp::create(
475 rewriter, op.getLoc(), inputs[0], inputs[2], !inverted[0], inverted[2]);
477 auto nand = synth::aig::AndInverterOp::create(rewriter, op.getLoc(), lhs,
479 replaceOpWithNewOpAndCopyNamehint<synth::aig::AndInverterOp>(rewriter, op,
485template <
typename OpTy>
490 matchAndRewrite(OpTy op, OpAdaptor adaptor,
491 ConversionPatternRewriter &rewriter)
const override {
498 ConversionPatternRewriter &rewriter) {
500 switch (operands.size()) {
502 llvm_unreachable(
"cannot be called with empty operand range");
509 return OpTy::create(rewriter, op.getLoc(), ValueRange{lhs, rhs},
true);
511 auto firstHalf = operands.size() / 2;
516 return OpTy::create(rewriter, op.getLoc(), ValueRange{lhs, rhs},
true);
525enum AdderArchitecture { RippleCarry, Sklanskey, KoggeStone, BrentKung };
526AdderArchitecture determineAdderArch(Operation *op, int64_t width) {
527 auto strAttr = op->getAttrOfType<StringAttr>(
"synth.test.arch");
529 return llvm::StringSwitch<AdderArchitecture>(strAttr.getValue())
530 .Case(
"SKLANSKEY", Sklanskey)
531 .Case(
"KOGGE-STONE", KoggeStone)
532 .Case(
"BRENT-KUNG", BrentKung)
533 .Case(
"RIPPLE-CARRY", RippleCarry);
543 return AdderArchitecture::RippleCarry;
548 return AdderArchitecture::Sklanskey;
552 return AdderArchitecture::KoggeStone;
561void lowerKoggeStonePrefixTree(OpBuilder &builder, Location loc,
562 SmallVector<Value> &pPrefix,
563 SmallVector<Value> &gPrefix) {
565 auto width =
static_cast<int64_t
>(pPrefix.size());
566 assert(width ==
static_cast<int64_t
>(gPrefix.size()));
567 SmallVector<Value> pPrefixNew = pPrefix;
568 SmallVector<Value> gPrefixNew = gPrefix;
571 for (int64_t stride = 1; stride < width; stride *= 2) {
573 for (int64_t i = stride; i < width; ++i) {
574 int64_t j = i - stride;
577 Value andPG = comb::AndOp::create(builder, loc, pPrefix[i], gPrefix[j]);
578 gPrefixNew[i] = comb::OrOp::create(builder, loc, gPrefix[i], andPG);
581 pPrefixNew[i] = comb::AndOp::create(builder, loc, pPrefix[i], pPrefix[j]);
584 pPrefix = pPrefixNew;
585 gPrefix = gPrefixNew;
590 for (int64_t stride = 1; stride < width; stride *= 2) {
592 <<
"--------------------------------------- Kogge-Stone Stage "
594 for (int64_t i = stride; i < width; ++i) {
595 int64_t j = i - stride;
597 llvm::dbgs() <<
"G" << i << stage + 1 <<
" = G" << i << stage
598 <<
" OR (P" << i << stage <<
" AND G" << j << stage
602 llvm::dbgs() <<
"P" << i << stage + 1 <<
" = P" << i << stage
603 <<
" AND P" << j << stage <<
"\n";
612void lowerSklanskeyPrefixTree(OpBuilder &builder, Location loc,
613 SmallVector<Value> &pPrefix,
614 SmallVector<Value> &gPrefix) {
615 auto width =
static_cast<int64_t
>(pPrefix.size());
616 assert(width ==
static_cast<int64_t
>(gPrefix.size()));
617 SmallVector<Value> pPrefixNew = pPrefix;
618 SmallVector<Value> gPrefixNew = gPrefix;
619 for (int64_t stride = 1; stride < width; stride *= 2) {
620 for (int64_t i = stride; i < width; i += 2 * stride) {
621 for (int64_t k = 0; k < stride && i + k < width; ++k) {
627 comb::AndOp::create(builder, loc, pPrefix[idx], gPrefix[j]);
628 gPrefixNew[idx] = comb::OrOp::create(builder, loc, gPrefix[idx], andPG);
632 comb::AndOp::create(builder, loc, pPrefix[idx], pPrefix[j]);
636 pPrefix = pPrefixNew;
637 gPrefix = gPrefixNew;
642 for (int64_t stride = 1; stride < width; stride *= 2) {
643 llvm::dbgs() <<
"--------------------------------------- Sklanskey Stage "
645 for (int64_t i = stride; i < width; i += 2 * stride) {
646 for (int64_t k = 0; k < stride && i + k < width; ++k) {
650 llvm::dbgs() <<
"G" << idx << stage + 1 <<
" = G" << idx << stage
651 <<
" OR (P" << idx << stage <<
" AND G" << j << stage
655 llvm::dbgs() <<
"P" << idx << stage + 1 <<
" = P" << idx << stage
656 <<
" AND P" << j << stage <<
"\n";
667void lowerBrentKungPrefixTree(OpBuilder &builder, Location loc,
668 SmallVector<Value> &pPrefix,
669 SmallVector<Value> &gPrefix) {
670 auto width =
static_cast<int64_t
>(pPrefix.size());
671 assert(width ==
static_cast<int64_t
>(gPrefix.size()));
672 SmallVector<Value> pPrefixNew = pPrefix;
673 SmallVector<Value> gPrefixNew = gPrefix;
677 for (stride = 1; stride < width; stride *= 2) {
678 for (int64_t i = stride * 2 - 1; i < width; i += stride * 2) {
679 int64_t j = i - stride;
682 Value andPG = comb::AndOp::create(builder, loc, pPrefix[i], gPrefix[j]);
683 gPrefixNew[i] = comb::OrOp::create(builder, loc, gPrefix[i], andPG);
686 pPrefixNew[i] = comb::AndOp::create(builder, loc, pPrefix[i], pPrefix[j]);
688 pPrefix = pPrefixNew;
689 gPrefix = gPrefixNew;
693 for (; stride > 0; stride /= 2) {
694 for (int64_t i = stride * 3 - 1; i < width; i += stride * 2) {
695 int64_t j = i - stride;
698 Value andPG = comb::AndOp::create(builder, loc, pPrefix[i], gPrefix[j]);
699 gPrefixNew[i] = comb::OrOp::create(builder, loc, gPrefix[i], andPG);
702 pPrefixNew[i] = comb::AndOp::create(builder, loc, pPrefix[i], pPrefix[j]);
704 pPrefix = pPrefixNew;
705 gPrefix = gPrefixNew;
710 for (stride = 1; stride < width; stride *= 2) {
711 llvm::dbgs() <<
"--------------------------------------- Brent-Kung FW "
712 << stage <<
" : Stride " << stride <<
"\n";
713 for (int64_t i = stride * 2 - 1; i < width; i += stride * 2) {
714 int64_t j = i - stride;
717 llvm::dbgs() <<
"G" << i << stage + 1 <<
" = G" << i << stage
718 <<
" OR (P" << i << stage <<
" AND G" << j << stage
722 llvm::dbgs() <<
"P" << i << stage + 1 <<
" = P" << i << stage
723 <<
" AND P" << j << stage <<
"\n";
728 for (; stride > 0; stride /= 2) {
729 if (stride * 3 - 1 < width)
730 llvm::dbgs() <<
"--------------------------------------- Brent-Kung BW "
731 << stage <<
" : Stride " << stride <<
"\n";
733 for (int64_t i = stride * 3 - 1; i < width; i += stride * 2) {
734 int64_t j = i - stride;
737 llvm::dbgs() <<
"G" << i << stage + 1 <<
" = G" << i << stage
738 <<
" OR (P" << i << stage <<
" AND G" << j << stage
742 llvm::dbgs() <<
"P" << i << stage + 1 <<
" = P" << i << stage
743 <<
" AND P" << j << stage <<
"\n";
751class LazyKoggeStonePrefixTree {
753 LazyKoggeStonePrefixTree(OpBuilder &builder, Location loc, int64_t width,
754 ArrayRef<Value> pPrefix, ArrayRef<Value> gPrefix)
755 : builder(builder), loc(loc), width(width) {
756 assert(width > 0 &&
"width must be positive");
757 for (int64_t i = 0; i < width; ++i)
758 prefixCache[{0, i}] = {pPrefix[i], gPrefix[i]};
762 std::pair<Value, Value> getFinal(int64_t i) {
763 assert(i >= 0 && i < width &&
"i out of bounds");
765 return getGroupAndPropagate(llvm::Log2_64_Ceil(width), i);
773 std::pair<Value, Value> getGroupAndPropagate(int64_t level, int64_t i);
777 DenseMap<std::pair<int64_t, int64_t>, std::pair<Value, Value>> prefixCache;
780std::pair<Value, Value>
781LazyKoggeStonePrefixTree::getGroupAndPropagate(int64_t level, int64_t i) {
782 assert(i < width &&
"i out of bounds");
783 auto key = std::make_pair(level, i);
784 auto it = prefixCache.find(key);
785 if (it != prefixCache.end())
788 assert(level > 0 &&
"If the level is 0, we should have hit the cache");
790 int64_t previousStride = 1ULL << (level - 1);
791 if (i < previousStride) {
793 auto [propagateI, generateI] = getGroupAndPropagate(level - 1, i);
794 prefixCache[key] = {propagateI, generateI};
795 return prefixCache[key];
798 int64_t j = i - previousStride;
799 auto [propagateI, generateI] = getGroupAndPropagate(level - 1, i);
800 auto [propagateJ, generateJ] = getGroupAndPropagate(level - 1, j);
802 Value andPG = comb::AndOp::create(builder, loc, propagateI, generateJ);
803 Value newGenerate = comb::OrOp::create(builder, loc, generateI, andPG);
806 comb::AndOp::create(builder, loc, propagateI, propagateJ);
807 prefixCache[key] = {newPropagate, newGenerate};
808 return prefixCache[key];
815 matchAndRewrite(
AddOp op, OpAdaptor adaptor,
816 ConversionPatternRewriter &rewriter)
const override {
817 auto inputs = adaptor.getInputs();
820 if (inputs.size() == 3) {
822 dyn_cast_or_null<hw::ConstantOp>(op.getOperand(2).getDefiningOp());
823 if (!constOp || !constOp.getValue().isOne())
832 return lowerAdder(op, inputs.take_front(2), constOne, rewriter);
837 if (inputs.size() != 2)
840 auto width = op.getType().getIntOrFloatBitWidth();
843 replaceOpWithNewOpAndCopyNamehint<hw::ConstantOp>(rewriter, op,
848 return lowerAdder(op, inputs, Value(), rewriter);
853 lowerRippleCarryAdder(
comb::AddOp op, ValueRange inputs, Value carryIn,
854 ConversionPatternRewriter &rewriter)
const {
855 auto width = op.getType().getIntOrFloatBitWidth();
857 Value carry = carryIn;
861 SmallVector<Value> results;
862 results.resize(width);
863 for (int64_t i = 0; i < width; ++i) {
864 SmallVector<Value> xorOperands = {aBits[i], bBits[i]};
866 xorOperands.push_back(carry);
870 results[width - i - 1] =
871 comb::XorOp::create(rewriter, op.getLoc(), xorOperands,
true);
880 carry = comb::AndOp::create(rewriter, op.getLoc(),
881 ValueRange{aBits[i], bBits[i]},
true);
888 LLVM_DEBUG(llvm::dbgs() <<
"Lower comb.add to Ripple-Carry Adder of width "
891 replaceOpWithNewOpAndCopyNamehint<comb::ConcatOp>(rewriter, op, results);
898 LogicalResult lowerAdder(
comb::AddOp op, ValueRange inputs, Value carryIn,
899 ConversionPatternRewriter &rewriter)
const {
903 assert(carryIn ==
nullptr ||
904 carryIn.getType().getIntOrFloatBitWidth() == 1 &&
905 "carryIn must be a 1-bit value");
908 auto width = op.getType().getIntOrFloatBitWidth();
909 auto arch = determineAdderArch(op, width);
910 if (arch == AdderArchitecture::RippleCarry)
911 return lowerRippleCarryAdder(op, inputs, carryIn, rewriter);
917 SmallVector<Value> p, g;
921 for (
auto [aBit, bBit] :
llvm::zip(aBits, bBits)) {
923 p.push_back(comb::XorOp::create(rewriter, op.getLoc(), aBit, bBit));
925 g.push_back(comb::AndOp::create(rewriter, op.getLoc(), aBit, bBit));
931 Value pAndC = comb::AndOp::create(rewriter, op.getLoc(), p[0], carryIn);
932 g[0] = comb::OrOp::create(rewriter, op.getLoc(), g[0], pAndC);
936 llvm::dbgs() <<
"Lower comb.add to Parallel-Prefix of width " << width
937 <<
"\n--------------------------------------- Init\n";
939 for (int64_t i = 0; i < width; ++i) {
941 llvm::dbgs() <<
"P0" << i <<
" = A" << i <<
" XOR B" << i <<
"\n";
942 if (i == 0 && carryIn)
943 llvm::dbgs() <<
"G0" << i <<
" = (A" << i <<
" AND B" << i
944 <<
") OR (P" << i <<
" AND CARRY_IN)\n";
947 llvm::dbgs() <<
"G0" << i <<
" = A" << i <<
" AND B" << i <<
"\n";
952 SmallVector<Value> pPrefix = p;
953 SmallVector<Value> gPrefix = g;
957 case AdderArchitecture::RippleCarry:
958 llvm_unreachable(
"Ripple-Carry handled above");
960 case AdderArchitecture::Sklanskey:
961 lowerSklanskeyPrefixTree(rewriter, op.getLoc(), pPrefix, gPrefix);
963 case AdderArchitecture::KoggeStone:
964 lowerKoggeStonePrefixTree(rewriter, op.getLoc(), pPrefix, gPrefix);
966 case AdderArchitecture::BrentKung:
967 lowerBrentKungPrefixTree(rewriter, op.getLoc(), pPrefix, gPrefix);
973 SmallVector<Value> results;
974 results.resize(width);
977 carryIn ? comb::XorOp::create(rewriter, op.getLoc(), p[0], carryIn)
982 for (int64_t i = 1; i < width; ++i)
983 results[width - 1 - i] =
984 comb::XorOp::create(rewriter, op.getLoc(), p[i], gPrefix[i - 1]);
986 replaceOpWithNewOpAndCopyNamehint<comb::ConcatOp>(rewriter, op, results);
989 llvm::dbgs() <<
"--------------------------------------- Completion\n";
992 llvm::dbgs() <<
"RES0 = P0 XOR CARRY_IN\n";
994 llvm::dbgs() <<
"RES0 = P0\n";
995 for (int64_t i = 1; i < width; ++i)
996 llvm::dbgs() <<
"RES" << i <<
" = P" << i <<
" XOR G" << i - 1 <<
"\n";
1007 matchAndRewrite(
MulOp op, OpAdaptor adaptor,
1008 ConversionPatternRewriter &rewriter)
const override {
1009 if (adaptor.getInputs().size() != 2)
1012 Location loc = op.getLoc();
1013 Value a = adaptor.getInputs()[0];
1014 Value b = adaptor.getInputs()[1];
1015 unsigned width = op.getType().getIntOrFloatBitWidth();
1019 rewriter.replaceOpWithNewOp<
hw::ConstantOp>(op, op.getType(), 0);
1024 SmallVector<Value> aBits =
extractBits(rewriter, a);
1025 SmallVector<Value> bBits =
extractBits(rewriter, b);
1030 SmallVector<SmallVector<Value>> partialProducts;
1031 partialProducts.reserve(width);
1032 for (
unsigned i = 0; i < width; ++i) {
1033 SmallVector<Value> row(i, falseValue);
1036 for (
unsigned j = 0; i + j < width; ++j)
1038 rewriter.createOrFold<
comb::AndOp>(loc, aBits[j], bBits[i]));
1040 partialProducts.push_back(row);
1045 rewriter.replaceOp(op, partialProducts[0][0]);
1051 auto addends = comp.compressToHeight(rewriter, 2);
1054 auto newAdd = comb::AddOp::create(rewriter, loc, addends,
true);
1060template <
typename OpTy>
1062 DivModOpConversionBase(MLIRContext *
context, int64_t maxEmulationUnknownBits)
1064 maxEmulationUnknownBits(maxEmulationUnknownBits) {
1065 assert(maxEmulationUnknownBits < 32 &&
1066 "maxEmulationUnknownBits must be less than 32");
1068 const int64_t maxEmulationUnknownBits;
1071struct CombDivUOpConversion : DivModOpConversionBase<DivUOp> {
1072 using DivModOpConversionBase<
DivUOp>::DivModOpConversionBase;
1074 matchAndRewrite(
DivUOp op, OpAdaptor adaptor,
1075 ConversionPatternRewriter &rewriter)
const override {
1077 if (llvm::succeeded(comb::convertDivUByPowerOfTwo(op, rewriter)))
1082 if (
auto rhsConst = adaptor.getRhs().getDefiningOp<
hw::ConstantOp>()) {
1083 APInt divisor = rhsConst.getValue();
1085 if (divisor.isZero()) {
1086 replaceOpWithNewOpAndCopyNamehint<hw::ConstantOp>(rewriter, op,
1100 rewriter, maxEmulationUnknownBits, op,
1101 [](
const APInt &lhs,
const APInt &rhs) {
1104 return APInt::getZero(rhs.getBitWidth());
1105 return lhs.udiv(rhs);
1110struct CombModUOpConversion : DivModOpConversionBase<ModUOp> {
1111 using DivModOpConversionBase<
ModUOp>::DivModOpConversionBase;
1113 matchAndRewrite(
ModUOp op, OpAdaptor adaptor,
1114 ConversionPatternRewriter &rewriter)
const override {
1116 if (llvm::succeeded(comb::convertModUByPowerOfTwo(op, rewriter)))
1121 if (
auto rhsConst = adaptor.getRhs().getDefiningOp<
hw::ConstantOp>()) {
1122 APInt divisor = rhsConst.getValue();
1124 if (divisor.isZero()) {
1125 replaceOpWithNewOpAndCopyNamehint<hw::ConstantOp>(rewriter, op,
1129 auto loc = op.getLoc();
1133 rewriter.createOrFold<
comb::MulOp>(loc, q, adaptor.getRhs());
1135 rewriter.createOrFold<
comb::SubOp>(loc, adaptor.getLhs(), product);
1144 rewriter, maxEmulationUnknownBits, op,
1145 [](
const APInt &lhs,
const APInt &rhs) {
1148 return APInt::getZero(rhs.getBitWidth());
1149 return lhs.urem(rhs);
1154struct CombDivSOpConversion : DivModOpConversionBase<DivSOp> {
1155 using DivModOpConversionBase<
DivSOp>::DivModOpConversionBase;
1158 matchAndRewrite(
DivSOp op, OpAdaptor adaptor,
1159 ConversionPatternRewriter &rewriter)
const override {
1162 if (
auto rhsConst = adaptor.getRhs().getDefiningOp<
hw::ConstantOp>()) {
1163 APInt divisor = rhsConst.getValue();
1164 unsigned width = op.getType().getIntOrFloatBitWidth();
1166 if (divisor.isZero()) {
1167 replaceOpWithNewOpAndCopyNamehint<hw::ConstantOp>(rewriter, op,
1172 if (divisor.isOne()) {
1177 if (divisor.isAllOnes()) {
1183 APInt::getZero(width)),
1195 rewriter, maxEmulationUnknownBits, op,
1196 [](
const APInt &lhs,
const APInt &rhs) {
1199 return APInt::getZero(rhs.getBitWidth());
1200 return lhs.sdiv(rhs);
1205struct CombModSOpConversion : DivModOpConversionBase<ModSOp> {
1206 using DivModOpConversionBase<
ModSOp>::DivModOpConversionBase;
1208 matchAndRewrite(
ModSOp op, OpAdaptor adaptor,
1209 ConversionPatternRewriter &rewriter)
const override {
1212 if (
auto rhsConst = adaptor.getRhs().getDefiningOp<
hw::ConstantOp>()) {
1213 APInt divisor = rhsConst.getValue();
1215 if (divisor.isZero() || divisor.isOne() || divisor.isAllOnes()) {
1216 replaceOpWithNewOpAndCopyNamehint<hw::ConstantOp>(rewriter, op,
1220 auto loc = op.getLoc();
1224 rewriter.createOrFold<
comb::MulOp>(loc, q, adaptor.getRhs());
1226 rewriter.createOrFold<
comb::SubOp>(loc, adaptor.getLhs(), product);
1232 rewriter, maxEmulationUnknownBits, op,
1233 [](
const APInt &lhs,
const APInt &rhs) {
1236 return APInt::getZero(rhs.getBitWidth());
1237 return lhs.srem(rhs);
1246 static Value constructRippleCarry(Location loc, Value a, Value b,
1248 ConversionPatternRewriter &rewriter) {
1256 for (
auto [aBit, bBit] :
llvm::zip(aBits, bBits)) {
1258 rewriter.createOrFold<
comb::XorOp>(loc, aBit, bBit,
true);
1259 auto aEqualB = rewriter.createOrFold<synth::aig::AndInverterOp>(
1260 loc, aBitXorBBit,
true);
1261 auto pred = rewriter.createOrFold<synth::aig::AndInverterOp>(
1262 loc, aBit, bBit,
true,
false);
1264 auto aBitAndBBit = rewriter.createOrFold<
comb::AndOp>(
1265 loc, ValueRange{aEqualB,
acc},
true);
1266 acc = rewriter.createOrFold<
comb::OrOp>(loc, pred, aBitAndBBit,
true);
1279 static Value computePrefixComparison(ConversionPatternRewriter &rewriter,
1280 Location loc, SmallVector<Value> pPrefix,
1281 SmallVector<Value> gPrefix,
1282 bool includeEq, AdderArchitecture arch) {
1283 auto width = pPrefix.size();
1284 Value finalGroup, finalPropagate;
1287 case AdderArchitecture::RippleCarry:
1288 llvm_unreachable(
"Ripple-Carry should be handled separately");
1290 case AdderArchitecture::Sklanskey: {
1291 lowerSklanskeyPrefixTree(rewriter, loc, pPrefix, gPrefix);
1292 finalGroup = gPrefix[width - 1];
1293 finalPropagate = pPrefix[width - 1];
1296 case AdderArchitecture::KoggeStone:
1299 std::tie(finalPropagate, finalGroup) =
1300 LazyKoggeStonePrefixTree(rewriter, loc, width, pPrefix, gPrefix)
1301 .getFinal(width - 1);
1303 case AdderArchitecture::BrentKung: {
1304 lowerBrentKungPrefixTree(rewriter, loc, pPrefix, gPrefix);
1305 finalGroup = gPrefix[width - 1];
1306 finalPropagate = pPrefix[width - 1];
1315 return comb::OrOp::create(rewriter, loc, finalGroup, finalPropagate);
1324 static Value constructUnsignedCompare(Operation *op, Location loc, Value a,
1325 Value b,
bool isLess,
bool includeEq,
1326 ConversionPatternRewriter &rewriter) {
1330 auto width = a.getType().getIntOrFloatBitWidth();
1333 auto arch = determineAdderArch(op, width);
1334 if (arch == AdderArchitecture::RippleCarry)
1335 return constructRippleCarry(loc, a, b, includeEq, rewriter);
1346 SmallVector<Value> eq, gt;
1353 for (
auto [aBit, bBit] :
llvm::zip(aBits, bBits)) {
1355 auto xorBit = comb::XorOp::create(rewriter, loc, aBit, bBit);
1356 eq.push_back(comb::XorOp::create(rewriter, loc, xorBit, one));
1359 auto notA = comb::XorOp::create(rewriter, loc, aBit, one);
1360 gt.push_back(comb::AndOp::create(rewriter, loc, notA, bBit));
1363 return computePrefixComparison(rewriter, loc, std::move(eq), std::move(gt),
1368 matchAndRewrite(ICmpOp op, OpAdaptor adaptor,
1369 ConversionPatternRewriter &rewriter)
const override {
1370 auto lhs = adaptor.getLhs();
1371 auto rhs = adaptor.getRhs();
1373 switch (op.getPredicate()) {
1377 case ICmpPredicate::eq:
1378 case ICmpPredicate::ceq: {
1380 auto xorOp = rewriter.createOrFold<
comb::XorOp>(op.getLoc(), lhs, rhs);
1382 SmallVector<bool> allInverts(xorBits.size(),
true);
1383 replaceOpWithNewOpAndCopyNamehint<synth::aig::AndInverterOp>(
1384 rewriter, op, xorBits, allInverts);
1388 case ICmpPredicate::ne:
1389 case ICmpPredicate::cne: {
1391 auto xorOp = rewriter.createOrFold<
comb::XorOp>(op.getLoc(), lhs, rhs);
1392 replaceOpWithNewOpAndCopyNamehint<comb::OrOp>(
1393 rewriter, op,
extractBits(rewriter, xorOp),
true);
1397 case ICmpPredicate::uge:
1398 case ICmpPredicate::ugt:
1399 case ICmpPredicate::ule:
1400 case ICmpPredicate::ult: {
1401 bool isLess = op.getPredicate() == ICmpPredicate::ult ||
1402 op.getPredicate() == ICmpPredicate::ule;
1403 bool includeEq = op.getPredicate() == ICmpPredicate::uge ||
1404 op.getPredicate() == ICmpPredicate::ule;
1406 constructUnsignedCompare(op, op.getLoc(), lhs,
1407 rhs, isLess, includeEq,
1411 case ICmpPredicate::slt:
1412 case ICmpPredicate::sle:
1413 case ICmpPredicate::sgt:
1414 case ICmpPredicate::sge: {
1415 if (lhs.getType().getIntOrFloatBitWidth() == 0)
1416 return rewriter.notifyMatchFailure(
1417 op.getLoc(),
"i0 signed comparison is unsupported");
1418 bool isLess = op.getPredicate() == ICmpPredicate::slt ||
1419 op.getPredicate() == ICmpPredicate::sle;
1420 bool includeEq = op.getPredicate() == ICmpPredicate::sge ||
1421 op.getPredicate() == ICmpPredicate::sle;
1430 auto sameSignResult = constructUnsignedCompare(
1431 op, op.getLoc(), aRest, bRest, isLess, includeEq, rewriter);
1435 comb::XorOp::create(rewriter, op.getLoc(), signA, signB);
1438 Value diffSignResult = isLess ? signA : signB;
1441 replaceOpWithNewOpAndCopyNamehint<comb::MuxOp>(
1442 rewriter, op, signsDiffer, diffSignResult, sameSignResult);
1453 matchAndRewrite(
ParityOp op, OpAdaptor adaptor,
1454 ConversionPatternRewriter &rewriter)
const override {
1456 replaceOpWithNewOpAndCopyNamehint<comb::XorOp>(
1457 rewriter, op,
extractBits(rewriter, adaptor.getInput()),
true);
1466 matchAndRewrite(
comb::ShlOp op, OpAdaptor adaptor,
1467 ConversionPatternRewriter &rewriter)
const override {
1468 auto width = op.getType().getIntOrFloatBitWidth();
1469 auto lhs = adaptor.getLhs();
1471 rewriter, op.getLoc(), adaptor.getRhs(), width,
1473 [&](int64_t index) {
1479 op.getLoc(), rewriter.getIntegerType(index), 0);
1482 [&](int64_t index) {
1483 assert(index < width &&
"index out of bounds");
1499 ConversionPatternRewriter &rewriter)
const override {
1500 auto width = op.getType().getIntOrFloatBitWidth();
1501 auto lhs = adaptor.getLhs();
1503 rewriter, op.getLoc(), adaptor.getRhs(), width,
1505 [&](int64_t index) {
1511 op.getLoc(), rewriter.getIntegerType(index), 0);
1514 [&](int64_t index) {
1515 assert(index < width &&
"index out of bounds");
1517 return rewriter.createOrFold<
comb::ExtractOp>(op.getLoc(), lhs, index,
1531 ConversionPatternRewriter &rewriter)
const override {
1532 auto width = op.getType().getIntOrFloatBitWidth();
1534 return rewriter.notifyMatchFailure(op.getLoc(),
1535 "i0 signed shift is unsupported");
1536 auto lhs = adaptor.getLhs();
1539 rewriter.createOrFold<
comb::ExtractOp>(op.getLoc(), lhs, width - 1, 1);
1544 rewriter, op.getLoc(), adaptor.getRhs(), width - 1,
1546 [&](int64_t index) {
1547 return rewriter.createOrFold<comb::ReplicateOp>(op.getLoc(), sign,
1551 [&](int64_t index) {
1552 return rewriter.createOrFold<
comb::ExtractOp>(op.getLoc(), lhs, index,
1568struct ConvertCombToSynthPass
1569 :
public impl::ConvertCombToSynthBase<ConvertCombToSynthPass> {
1570 void runOnOperation()
override;
1571 using ConvertCombToSynthBase<ConvertCombToSynthPass>::ConvertCombToSynthBase;
1577 uint32_t maxEmulationUnknownBits,
1581 CombAndOpConversion, CombParityOpConversion, CombXorOpToSynthConversion,
1582 CombMuxOpToSynthConversion,
1584 CombMulOpConversion, CombICmpOpConversion,
1586 CombShlOpConversion, CombShrUOpConversion, CombShrSOpConversion,
1588 CombLowerVariadicOp<AddOp>, CombLowerVariadicOp<MulOp>>(
1592 patterns.add<SynthXorInverterOpConversion, SynthMuxInverterOpConversion>(
1595 patterns.add(comb::convertSubToAdd);
1597 patterns.add<CombOrToAIGConversion, CombAddOpConversion>(
1599 synth::populateVariadicAndInverterLoweringPatterns(
patterns);
1602 synth::populateVariadicXorInverterLoweringPatterns(
patterns);
1605 patterns.add<CombDivUOpConversion, CombModUOpConversion, CombDivSOpConversion,
1606 CombModSOpConversion>(
patterns.getContext(),
1607 maxEmulationUnknownBits);
1610void ConvertCombToSynthPass::runOnOperation() {
1611 ConversionTarget target(getContext());
1614 target.addIllegalDialect<comb::CombDialect>();
1624 hw::AggregateConstantOp>();
1626 target.addLegalDialect<synth::SynthDialect>();
1628 target.addIllegalOp<synth::XorInverterOp, synth::MuxInverterOp>();
1631 if (!additionalLegalOps.empty())
1632 for (
const auto &opName : additionalLegalOps)
1633 target.addLegalOp(OperationName(opName, &getContext()));
1635 RewritePatternSet
patterns(&getContext());
1639 if (failed(mlir::applyPartialConversion(getOperation(), target,
1641 return signalPassFailure();
assert(baseType &&"element must be base type")
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 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::unique_ptr< Context > context
static std::optional< APSInt > getConstant(Attribute operand)
Determine the value of a constant operand for the sake of constant folding.
static Value lowerFullyAssociativeOp(Operation &op, OperandRange operands, SmallVector< Operation * > &newOps)
Lower a variadic fully-associative operation into an expression tree.
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.