diff --git a/integration_test/circt-synth/comb-lowering-lec.mlir b/integration_test/circt-synth/comb-lowering-lec.mlir index 4891299e7d63..0d51431e578f 100644 --- a/integration_test/circt-synth/comb-lowering-lec.mlir +++ b/integration_test/circt-synth/comb-lowering-lec.mlir @@ -6,9 +6,9 @@ // COMB_BIT_LOGICAL: c1 == c2 hw.module @bit_logical(in %arg0: i32, in %arg1: i32, in %arg2: i32, in %arg3: i32, in %cond: i1, out out0: i32, out out1: i32, out out2: i32, out out3: i32) { - %0 = comb.or %arg0, %arg1 : i32 - %1 = comb.and %arg0, %arg1 : i32 - %2 = comb.xor %arg0, %arg1 : i32 + %0 = comb.or %arg0, %arg1, %arg2, %arg3 : i32 + %1 = comb.and %arg0, %arg1, %arg2, %arg3 : i32 + %2 = comb.xor %arg0, %arg1, %arg2, %arg3 : i32 %3 = comb.mux %cond, %arg0, %arg1 : i32 hw.output %0, %1, %2, %3 : i32, i32, i32, i32 diff --git a/lib/Conversion/CombToAIG/CombToAIG.cpp b/lib/Conversion/CombToAIG/CombToAIG.cpp index 94ec4f85461e..19711a068ca2 100644 --- a/lib/Conversion/CombToAIG/CombToAIG.cpp +++ b/lib/Conversion/CombToAIG/CombToAIG.cpp @@ -91,6 +91,42 @@ struct CombXorOpConversion : OpConversionPattern { } }; +template +struct CombLowerVariadicOp : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + using OpAdaptor = typename OpConversionPattern::OpAdaptor; + LogicalResult + matchAndRewrite(OpTy op, OpAdaptor adaptor, + ConversionPatternRewriter &rewriter) const override { + auto result = lowerFullyAssociativeOp(op, op.getOperands(), rewriter); + rewriter.replaceOp(op, result); + return success(); + } + + static Value lowerFullyAssociativeOp(OpTy op, OperandRange operands, + ConversionPatternRewriter &rewriter) { + Value lhs, rhs; + switch (operands.size()) { + case 0: + assert(false && "cannot be called with empty operand range"); + break; + case 1: + return operands[0]; + case 2: + lhs = operands[0]; + rhs = operands[1]; + return rewriter.create(op.getLoc(), ValueRange{lhs, rhs}, true); + default: + auto firstHalf = operands.size() / 2; + lhs = + lowerFullyAssociativeOp(op, operands.take_front(firstHalf), rewriter); + rhs = + lowerFullyAssociativeOp(op, operands.drop_front(firstHalf), rewriter); + return rewriter.create(op.getLoc(), ValueRange{lhs, rhs}, true); + } + } +}; + // Lower comb::MuxOp to AIG operations. struct CombMuxOpConversion : OpConversionPattern { using OpConversionPattern::OpConversionPattern; @@ -150,7 +186,9 @@ static void populateCombToAIGConversionPatterns(RewritePatternSet &patterns) { patterns.add< // Bitwise Logical Ops CombAndOpConversion, CombOrOpConversion, CombXorOpConversion, - CombMuxOpConversion>(patterns.getContext()); + CombMuxOpConversion, + // Variadic ops that must be lowered to binary operations + CombLowerVariadicOp>(patterns.getContext()); } void ConvertCombToAIGPass::runOnOperation() { diff --git a/test/Conversion/CombToAIG/comb-to-aig.mlir b/test/Conversion/CombToAIG/comb-to-aig.mlir index 4628603cb1e7..d65c0c150b29 100644 --- a/test/Conversion/CombToAIG/comb-to-aig.mlir +++ b/test/Conversion/CombToAIG/comb-to-aig.mlir @@ -1,18 +1,27 @@ // RUN: circt-opt %s --convert-comb-to-aig | FileCheck %s // CHECK-LABEL: @test -hw.module @test(in %arg0: i32, in %arg1: i32, in %arg2: i32, in %arg3: i32, out out0: i32, out out1: i32, out out2: i32) { +hw.module @test(in %arg0: i32, in %arg1: i32, in %arg2: i32, in %arg3: i32, out out0: i32, out out1: i32) { // CHECK-NEXT: %[[OR_TMP:.+]] = aig.and_inv not %arg0, not %arg1, not %arg2, not %arg3 : i32 // CHECK-NEXT: %[[OR:.+]] = aig.and_inv not %0 : i32 // CHECK-NEXT: %[[AND:.+]] = aig.and_inv %arg0, %arg1, %arg2, %arg3 : i32 - // CHECK-NEXT: %[[XOR_NOT_AND:.+]] = aig.and_inv not %arg0, not %arg1 : i32 - // CHECK-NEXT: %[[XOR_AND:.+]] = aig.and_inv %arg0, %arg1 : i32 - // CHECK-NEXT: %[[XOR:.+]] = aig.and_inv not %[[XOR_NOT_AND]], not %[[XOR_AND]] : i32 - // CHECK-NEXT: hw.output %[[OR]], %[[AND]], %[[XOR]] : i32, i32, i32 + // CHECK-NEXT: hw.output %[[OR]], %[[AND]] : i32, i32 %0 = comb.or %arg0, %arg1, %arg2, %arg3 : i32 %1 = comb.and %arg0, %arg1, %arg2, %arg3 : i32 - %2 = comb.xor %arg0, %arg1 : i32 - hw.output %0, %1, %2 : i32, i32, i32 + hw.output %0, %1 : i32, i32 +} + +// CHECK-LABEL: @xor +hw.module @xor(in %arg0: i32, in %arg1: i32, in %arg2: i32, out out0: i32) { + // CHECK-NEXT: %[[RHS_NOT_AND:.+]] = aig.and_inv not %arg1, not %arg2 : i32 + // CHECK-NEXT: %[[RHS_AND:.+]] = aig.and_inv %arg1, %arg2 : i32 + // CHECK-NEXT: %[[RHS_XOR:.+]] = aig.and_inv not %[[RHS_NOT_AND]], not %[[RHS_AND]] : i32 + // CHECK-NEXT: %[[NOT_AND:.+]] = aig.and_inv not %arg0, not %[[RHS_XOR]] : i32 + // CHECK-NEXT: %[[AND:.+]] = aig.and_inv %arg0, %[[RHS_XOR]] : i32 + // CHECK-NEXT: %[[RESULT:.+]] = aig.and_inv not %[[NOT_AND]], not %[[AND]] : i32 + // CHECK-NEXT: hw.output %[[RESULT]] + %0 = comb.xor %arg0, %arg1, %arg2 : i32 + hw.output %0 : i32 } // CHECK-LABEL: @pass