Skip to content

Commit

Permalink
[PruneZeroValuedLogic] Legalize concat/parity/icmp (#4200)
Browse files Browse the repository at this point in the history
This implements i0 value legalizations for concat/parity/icmp. 
These operations can have i0 values as an operand but we cannot 
just remove them since they produce non-i0 values. 

1. `concat` -> Filter non-i0 values
2. `partiy 0:i0` -> `0: i1`
3. ` icmp pred 0:i0, 0:i0` -> Calculate the result with `applyCmpPredicateToEqualOperands`
  • Loading branch information
uenoku authored Oct 27, 2022
1 parent d1ccafa commit fb91326
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 30 deletions.
139 changes: 109 additions & 30 deletions lib/Conversion/ExportVerilog/PruneZeroValuedLogic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,6 @@ using namespace mlir;
using namespace circt;
using namespace hw;

// Returns true if 'op' is a legal user of an I0 value.
static bool isLegalI0User(Operation *op) {
return isa<hw::OutputOp, hw::ArrayGetOp, sv::ArrayIndexInOutOp,
hw::InstanceOp>(op);
}

static bool noI0Type(TypeRange types) {
return llvm::none_of(
types, [](Type type) { return ExportVerilog::isZeroBitType(type); });
Expand All @@ -52,8 +46,6 @@ class PruneTypeConverter : public mlir::TypeConverter {
}
};

// The NoI0OperandsConversionPattern will aggressively remove any operation
// which has a zero-width operand.
template <typename TOp>
struct NoI0OperandsConversionPattern : public OpConversionPattern<TOp> {
public:
Expand All @@ -78,6 +70,102 @@ static void addNoI0OperandsLegalizationPattern(ConversionTarget &target) {
[&](auto op) { return noI0TypedValue(op->getOperands()); });
}

template <>
struct NoI0OperandsConversionPattern<comb::ICmpOp>
: public OpConversionPattern<comb::ICmpOp> {
public:
using OpConversionPattern<comb::ICmpOp>::OpConversionPattern;
using OpAdaptor = typename OpConversionPattern<comb::ICmpOp>::OpAdaptor;

// Returns the result of applying the predicate when the LHS and RHS are the
// exact same value.
static bool
applyCmpPredicateToEqualOperands(circt::comb::ICmpPredicate predicate) {
switch (predicate) {
case circt::comb::ICmpPredicate::eq:
case circt::comb::ICmpPredicate::sle:
case circt::comb::ICmpPredicate::sge:
case circt::comb::ICmpPredicate::ule:
case circt::comb::ICmpPredicate::uge:
case circt::comb::ICmpPredicate::ceq:
case circt::comb::ICmpPredicate::weq:
return true;
case circt::comb::ICmpPredicate::ne:
case circt::comb::ICmpPredicate::slt:
case circt::comb::ICmpPredicate::sgt:
case circt::comb::ICmpPredicate::ult:
case circt::comb::ICmpPredicate::ugt:
case circt::comb::ICmpPredicate::cne:
case circt::comb::ICmpPredicate::wne:
return false;
}
llvm_unreachable("unknown comparison predicate");
}

LogicalResult
matchAndRewrite(comb::ICmpOp op, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
if (noI0TypedValue(adaptor.getOperands()))
return failure();

// Caluculate the result of i0 value comparison.
bool result = applyCmpPredicateToEqualOperands(op.getPredicate());

rewriter.replaceOpWithNewOp<hw::ConstantOp>(
op, APInt(1, result, /*isSigned=*/false));
return success();
}
};

template <>
struct NoI0OperandsConversionPattern<comb::ParityOp>
: public OpConversionPattern<comb::ParityOp> {
public:
using OpConversionPattern<comb::ParityOp>::OpConversionPattern;
using OpAdaptor = typename OpConversionPattern<comb::ParityOp>::OpAdaptor;

LogicalResult
matchAndRewrite(comb::ParityOp op, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
if (noI0TypedValue(adaptor.getOperands()))
return failure();

// The value of "comb.parity i0" is 0.
rewriter.replaceOpWithNewOp<hw::ConstantOp>(
op, APInt(1, 0, /*isSigned=*/false));
return success();
}
};

template <>
struct NoI0OperandsConversionPattern<comb::ConcatOp>
: public OpConversionPattern<comb::ConcatOp> {
public:
using OpConversionPattern<comb::ConcatOp>::OpConversionPattern;
using OpAdaptor = typename OpConversionPattern<comb::ConcatOp>::OpAdaptor;

LogicalResult
matchAndRewrite(comb::ConcatOp op, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
// Replace an i0 value with i0 constant.
if (op.getType().isInteger(0)) {
rewriter.replaceOpWithNewOp<hw::ConstantOp>(
op, APInt(1, 0, /*isSigned=*/false));
return success();
}

if (noI0TypedValue(adaptor.getOperands()))
return failure();

// Filter i0 operands and create a new concat op.
SmallVector<Value> newOperands;
llvm::copy_if(op.getOperands(), std::back_inserter(newOperands),
[](auto op) { return !op.getType().isInteger(0); });
rewriter.replaceOpWithNewOp<comb::ConcatOp>(op, newOperands);
return success();
}
};

// A generic pruning pattern which prunes any operation which has an operand
// with an i0 typed value. Similarly, an operation is legal if all of its
// operands are not i0 typed.
Expand Down Expand Up @@ -105,22 +193,10 @@ struct NoI0ResultsConversionPattern : public OpConversionPattern<TOp> {
return failure();

// Part of i0-typed logic - prune!
// If the operation defines a value which is used by a valid user, we
// replace it with a zero-width constant.
for (auto res : op->getResults()) {
for (auto *user : res.getUsers()) {
if (isLegalI0User(user)) {
assert(op->getNumResults() == 1 &&
"expected single result if using rewriter.replaceOpWith");
rewriter.replaceOpWithNewOp<hw::ConstantOp>(
op, APInt(0, 0, /*isSigned=*/false));
return success();
}
}
}

// Else, just erase the op.
rewriter.eraseOp(op);
assert(op->getNumResults() == 1 &&
"expected single result if using rewriter.replaceOpWith");
rewriter.replaceOpWithNewOp<hw::ConstantOp>(
op, APInt(0, 0, /*isSigned=*/false));
return success();
}
};
Expand Down Expand Up @@ -173,17 +249,20 @@ void ExportVerilog::pruneZeroValuedLogic(hw::HWModuleOp module) {
target.addLegalDialect<sv::SVDialect, comb::CombDialect, hw::HWDialect>();
addPruningPattern<NoI0OperandPruningPattern<sv::PAssignOp>,
NoI0OperandPruningPattern<sv::BPAssignOp>,
NoI0OperandPruningPattern<sv::AssignOp>>(target, patterns,
typeConverter);
NoI0OperandPruningPattern<sv::AssignOp>,
NoI0OperandPruningPattern<comb::ICmpOp>,
NoI0OperandPruningPattern<comb::ParityOp>,
NoI0OperandPruningPattern<comb::ConcatOp>>(target, patterns,
typeConverter);

addNoI0ResultPruningPattern<
// SV ops
sv::WireOp, sv::RegOp, sv::ReadInOutOp,
// Prune all zero-width combinational logic.
comb::AddOp, comb::AndOp, comb::ConcatOp, comb::DivSOp, comb::DivUOp,
comb::ExtractOp, comb::ModSOp, comb::ModUOp, comb::MulOp, comb::MuxOp,
comb::OrOp, comb::ParityOp, comb::ReplicateOp, comb::ShlOp, comb::ShrSOp,
comb::ShrUOp, comb::SubOp, comb::XorOp>(target, patterns, typeConverter);
comb::AddOp, comb::AndOp, comb::DivSOp, comb::DivUOp, comb::ExtractOp,
comb::ModSOp, comb::ModUOp, comb::MulOp, comb::MuxOp, comb::OrOp,
comb::ReplicateOp, comb::ShlOp, comb::ShrSOp, comb::ShrUOp, comb::SubOp,
comb::XorOp>(target, patterns, typeConverter);

(void)applyPartialConversion(module, target, std::move(patterns));
}
22 changes: 22 additions & 0 deletions test/Conversion/ExportVerilog/pruning.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,25 @@ hw.module @zeroWidthLogic(%arg0: i0, %sel : i1, %clk: i1) -> (out: i0) {
%2 = comb.mux %sel, %rr, %arg0 : i0
hw.output %2 : i0
}

// CHECK-LABEL: module Concat(
hw.module @Concat(%arg0: i0, %arg1: i1, %clk: i1) -> (out: i2) {
// CHECK: assign out = {arg1, clk};
%1 = comb.concat %arg0, %arg1, %clk : i0, i1, i1
hw.output %1 : i2
}

// CHECK-LABEL: module icmp(
hw.module @icmp(%a: i0) -> (y: i1) {
// CHECK: assign y = 1'h1;
%0 = comb.icmp eq %a, %a : i0
hw.output %0 : i1
}


// CHECK-LABEL: module parity(
hw.module @parity(%arg0: i0) -> (out: i1) {
// CHECK: assign out = 1'h0;
%0 = comb.parity %arg0 : i0
hw.output %0 : i1
}

0 comments on commit fb91326

Please sign in to comment.