Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions lib/Dialect/Synth/Transforms/LowerVariadic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@
#include "circt/Dialect/Synth/SynthOps.h"
#include "circt/Dialect/Synth/Transforms/SynthPasses.h"
#include "mlir/Analysis/TopologicalSortUtils.h"
#include "mlir/IR/Block.h"
#include "mlir/IR/OpDefinition.h"
#include "mlir/IR/PatternMatch.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Casting.h"

#define DEBUG_TYPE "synth-lower-variadic"

Expand Down Expand Up @@ -93,6 +97,81 @@ static LogicalResult replaceWithBalancedTree(
return success();
}

// Returns if an operation is a subset of the other.
static bool isOpSubsetOf(aig::AndInverterOp sub, aig::AndInverterOp super) {
if (sub->getNumOperands() > super->getNumOperands())
return false;

SmallVector<bool> matched(super->getNumOperands(), false);
// All sub operands need to be in super.
for (size_t i = 0, e = sub.getNumOperands(); i < e; ++i) {
bool found = false;
for (size_t j = 0, e2 = super.getNumOperands(); j < e2; ++j) {
if (matched[j])
continue;

if (sub->getOperand(i) == super.getOperand(j) &&
sub.isInverted(i) == super.isInverted(j)) {
matched[j] = true;
found = true;
break;
}
}
if (!found)
return false;
}
return true;
}

static void simplifyWithExistingOperations(aig::AndInverterOp op,
mlir::IRRewriter &rewriter) {
mlir::Block *block = op->getBlock();
for (Operation &otherOp : *block) {
if (&otherOp == op.getOperation())
continue;

auto otherAIG = llvm::dyn_cast<aig::AndInverterOp>(otherOp);
if (!otherAIG)
continue;

if (isOpSubsetOf(otherAIG, op)) {
SmallVector<Value> newOperands;
SmallVector<bool> newInversions;

SmallVector<bool> isPartAsSubset(op.getNumOperands(), false);

// Find the operands that match.
for (size_t i = 0; i < otherAIG.getNumOperands(); ++i) {
for (size_t j = 0; j < op.getNumOperands(); ++j) {
if (!isPartAsSubset[j] &&
otherAIG.getOperand(i) == op.getOperand(j) &&
otherAIG.isInverted(i) == op.isInverted(j)) {
isPartAsSubset[j] = true;
break;
}
}
}
// Build the list of operands that are not shared.
for (size_t i = 0; i < op->getNumOperands(); ++i) {
if (!isPartAsSubset[i]) {
newOperands.push_back(op->getOperand(i));
newInversions.push_back(op.isInverted(i));
}
}

newOperands.push_back(otherAIG.getResult());
newInversions.push_back(false);

rewriter.modifyOpInPlace(op, [&]() {
op.getOperation()->setOperands(newOperands);
op->setAttr("inversions", rewriter.getBoolArrayAttr(newInversions));
});
// We found a first match, not necessarily the best one.
break;
}
}
}

void LowerVariadicPass::runOnOperation() {
// Topologically sort operations in graph regions to ensure operands are
// defined before uses.
Expand Down Expand Up @@ -144,6 +223,7 @@ void LowerVariadicPass::runOnOperation() {

// Handle AndInverterOp specially to preserve inversion flags.
if (auto andInverterOp = dyn_cast<aig::AndInverterOp>(op)) {
simplifyWithExistingOperations(andInverterOp, rewriter);
auto result = replaceWithBalancedTree(
analysis, rewriter, op,
// Check if each operand is inverted.
Expand Down
16 changes: 16 additions & 0 deletions test/Dialect/Synth/lower-variadic.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,19 @@ hw.module @Issue9115(in %a : i16, in %b : i16, in %c : i16, in %d : i16, out pro
// COMMON-NEXT: comb.mul %c, %[[TMP]] : i16
hw.output %0 : i16
}

// RUN: circt-opt %s -synth-lower-variadic | FileCheck %s
// COMMON-LABEL: hw.module @SharingHeuristic
hw.module @SharingHeuristic(in %in0 : i1, in %in1 : i1, in %in2 : i1, in %in3 : i1, in %in4 : i1, out out1 : i1, out out2 : i1) {

// CHECK: %[[N0:.+]] = synth.aig.and_inv %in1, %in2
// CHECK: %[[N1:.+]] = synth.aig.and_inv %in3, %in4
// CHECK: %[[OUT2_ROOT:.+]] = synth.aig.and_inv %[[N0]], %[[N1]]
%out2 = synth.aig.and_inv %in1, %in2, %in3, %in4 : i1

// CHECK: %[[OUT1_NODE:.+]] = synth.aig.and_inv %in4, %[[N0]]
// CHECK: %[[OUT1_ROOT:.+]] = synth.aig.and_inv {{.+}}, %[[OUT1_NODE]]
%out1 = synth.aig.and_inv %in0, %in1, %in2, %in3, %in4 : i1

hw.output %out1, %out2 : i1, i1
}
Loading