13#include "mlir/Analysis/TopologicalSortUtils.h"
14#include "mlir/IR/BuiltinAttributes.h"
15#include "mlir/IR/Matchers.h"
16#include "mlir/IR/OpDefinition.h"
17#include "mlir/IR/PatternMatch.h"
18#include "llvm/ADT/APInt.h"
19#include "llvm/Support/Casting.h"
20#include "llvm/Support/LogicalResult.h"
24using namespace circt::synth::mig;
25using namespace circt::synth::aig;
28#include "circt/Dialect/Synth/Synth.cpp.inc"
30LogicalResult MajorityInverterOp::verify() {
31 if (getNumOperands() % 2 != 1)
32 return emitOpError(
"requires an odd number of operands");
37llvm::APInt MajorityInverterOp::evaluate(ArrayRef<APInt> inputs) {
38 assert(inputs.size() == getNumOperands() &&
39 "Number of inputs must match number of operands");
41 if (inputs.size() == 3) {
42 auto a = (isInverted(0) ? ~inputs[0] : inputs[0]);
43 auto b = (isInverted(1) ? ~inputs[1] : inputs[1]);
44 auto c = (isInverted(2) ? ~inputs[2] : inputs[2]);
45 return (a & b) | (a & c) | (b & c);
49 auto width = inputs[0].getBitWidth();
50 APInt result(width, 0);
52 for (
size_t bit = 0; bit < width; ++bit) {
54 for (
size_t i = 0; i < inputs.size(); ++i) {
56 if (isInverted(i) ^ inputs[i][bit])
60 if (count > inputs.size() / 2)
67OpFoldResult MajorityInverterOp::fold(FoldAdaptor adaptor) {
70 SmallVector<APInt, 3> inputValues;
71 for (
auto input : adaptor.getInputs()) {
72 auto attr = llvm::dyn_cast_or_null<IntegerAttr>(input);
75 inputValues.push_back(attr.getValue());
78 auto result = evaluate(inputValues);
79 return IntegerAttr::get(getType(), result);
82LogicalResult MajorityInverterOp::canonicalize(MajorityInverterOp op,
83 PatternRewriter &rewriter) {
84 if (op.getNumOperands() == 1) {
85 if (op.getInverted()[0])
87 rewriter.replaceOp(op, op.getOperand(0));
92 if (op.getNumOperands() != 3)
97 auto getConstant = [&](
unsigned index) -> std::optional<llvm::APInt> {
99 if (mlir::matchPattern(op.getInputs()[index], mlir::m_ConstantInt(&value)))
100 return op.isInverted(index) ? ~value : value;
105 auto replaceWithIndex = [&](
int index) {
106 bool inverted = op.isInverted(index);
108 rewriter.replaceOpWithNewOp<MajorityInverterOp>(
109 op, op.getType(), op.getOperand(index),
true);
111 rewriter.replaceOp(op, op.getOperand(index));
118 for (
int i = 0; i < 2; ++i) {
119 for (
int j = i + 1; j < 3; ++j) {
123 if (op.getOperand(i) == op.getOperand(j)) {
125 if (op.isInverted(i) != op.isInverted(j))
126 return replaceWithIndex(k);
127 return replaceWithIndex(i);
136 op, op.getType(), mlir::IntegerAttr::get(op.getType(), *c1));
141 return replaceWithIndex(k);
153OpFoldResult AndInverterOp::fold(FoldAdaptor adaptor) {
154 if (getNumOperands() == 1 && !isInverted(0))
155 return getOperand(0);
157 auto inputs = adaptor.getInputs();
158 if (inputs.size() == 2)
159 if (
auto intAttr = dyn_cast_or_null<IntegerAttr>(inputs[1])) {
160 auto value = intAttr.getValue();
164 return IntegerAttr::get(
165 IntegerType::get(getContext(), value.getBitWidth()), value);
166 if (value.isAllOnes()) {
170 return getOperand(0);
176LogicalResult AndInverterOp::canonicalize(AndInverterOp op,
177 PatternRewriter &rewriter) {
179 SmallVector<Value> uniqueValues;
180 SmallVector<bool> uniqueInverts;
183 APInt::getAllOnes(op.getResult().getType().getIntOrFloatBitWidth());
185 bool invertedConstFound =
false;
186 bool flippedFound =
false;
188 for (
auto [value, inverted] :
llvm::zip(op.getInputs(), op.getInverted())) {
189 bool newInverted = inverted;
192 constValue &= ~constOp.getValue();
193 invertedConstFound =
true;
195 constValue &= constOp.getValue();
200 if (
auto andInverterOp = value.getDefiningOp<synth::aig::AndInverterOp>()) {
201 if (andInverterOp.getInputs().size() == 1 &&
202 andInverterOp.isInverted(0)) {
203 value = andInverterOp.getOperand(0);
204 newInverted = andInverterOp.isInverted(0) ^ inverted;
209 auto it = seen.find(value);
210 if (it == seen.end()) {
211 seen.insert({value, newInverted});
212 uniqueValues.push_back(value);
213 uniqueInverts.push_back(newInverted);
214 }
else if (it->second != newInverted) {
217 op, APInt::getZero(value.getType().getIntOrFloatBitWidth()));
223 if (constValue.isZero()) {
229 if ((uniqueValues.size() == op.getInputs().size() && !flippedFound) ||
230 (!constValue.isAllOnes() && !invertedConstFound &&
231 uniqueValues.size() + 1 == op.getInputs().size()))
234 if (!constValue.isAllOnes()) {
236 uniqueInverts.push_back(
false);
237 uniqueValues.push_back(constOp);
241 if (uniqueValues.empty()) {
247 replaceOpWithNewOpAndCopyNamehint<synth::aig::AndInverterOp>(
248 rewriter, op, uniqueValues, uniqueInverts);
252APInt AndInverterOp::evaluate(ArrayRef<APInt> inputs) {
253 assert(inputs.size() == getNumOperands() &&
254 "Expected as many inputs as operands");
255 assert(!inputs.empty() &&
"Expected non-empty input list");
256 APInt result = APInt::getAllOnes(inputs.front().getBitWidth());
257 for (
auto [idx, input] :
llvm::enumerate(inputs)) {
267 ArrayRef<bool> inverts,
268 PatternRewriter &rewriter) {
269 switch (operands.size()) {
271 assert(0 &&
"cannot be called with empty operand range");
275 return AndInverterOp::create(rewriter, op.getLoc(), operands[0],
true);
279 return AndInverterOp::create(rewriter, op.getLoc(), operands[0],
280 operands[1], inverts[0], inverts[1]);
282 auto firstHalf = operands.size() / 2;
285 inverts.take_front(firstHalf), rewriter);
288 inverts.drop_front(firstHalf), rewriter);
289 return AndInverterOp::create(rewriter, op.getLoc(), lhs, rhs);
295 AndInverterOp op, PatternRewriter &rewriter)
const {
296 if (op.getInputs().size() <= 2)
302 op, op.getOperands(), op.getInverted(), rewriter));
308 llvm::function_ref<
bool(mlir::Value, mlir::Operation *)> isOperandReady) {
310 auto walkResult = op->walk([&](Region *region) {
312 dyn_cast<mlir::RegionKindInterface>(region->getParentOp());
314 regionKindOp.hasSSADominance(region->getRegionNumber()))
315 return WalkResult::advance();
318 for (
auto &block : *region) {
319 if (!mlir::sortTopologically(&block, isOperandReady))
320 return WalkResult::interrupt();
322 return WalkResult::advance();
325 return success(!walkResult.wasInterrupted());
assert(baseType &&"element must be base type")
static std::optional< APSInt > getConstant(Attribute operand)
Determine the value of a constant operand for the sake of constant folding.
static Value lowerVariadicAndInverterOp(AndInverterOp op, OperandRange operands, ArrayRef< bool > inverts, PatternRewriter &rewriter)
LogicalResult topologicallySortGraphRegionBlocks(mlir::Operation *op, llvm::function_ref< bool(mlir::Value, mlir::Operation *)> isOperandReady)
This function performs a topological sort on the operations within each block of graph regions in the...
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
mlir::LogicalResult matchAndRewrite(aig::AndInverterOp op, mlir::PatternRewriter &rewriter) const override