From 8acce713504fb370e765822b73063286086e22ff Mon Sep 17 00:00:00 2001 From: Hideto Ueno Date: Mon, 28 Oct 2024 03:05:46 +0900 Subject: [PATCH] [AIGToComb] [circt-synth] Add a AIG to Comb conversion pass This adds a conversion pass from AIG dialect to Comb dialect. AndInverterOp can be easily converted into comb.and + comb.xor + hw.constant. This enables us to utilize core dialects tools for synthesis results without any addition. Primarly use case is running LEC on IR before and after synthesis. --- include/circt/Conversion/AIGToComb.h | 22 ++++++ include/circt/Conversion/Passes.h | 1 + include/circt/Conversion/Passes.td | 16 ++++ lib/CAPI/Conversion/CMakeLists.txt | 1 + lib/Conversion/AIGToComb/AIGToComb.cpp | 86 ++++++++++++++++++++++ lib/Conversion/AIGToComb/CMakeLists.txt | 15 ++++ lib/Conversion/CMakeLists.txt | 1 + test/Conversion/AIGToComb/aig-to-comb.mlir | 12 +++ test/circt-synth/basic.mlir | 5 +- tools/circt-synth/CMakeLists.txt | 1 + tools/circt-synth/circt-synth.cpp | 16 ++++ 11 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 include/circt/Conversion/AIGToComb.h create mode 100644 lib/Conversion/AIGToComb/AIGToComb.cpp create mode 100644 lib/Conversion/AIGToComb/CMakeLists.txt create mode 100644 test/Conversion/AIGToComb/aig-to-comb.mlir diff --git a/include/circt/Conversion/AIGToComb.h b/include/circt/Conversion/AIGToComb.h new file mode 100644 index 000000000000..5af6d28aa062 --- /dev/null +++ b/include/circt/Conversion/AIGToComb.h @@ -0,0 +1,22 @@ +//===- AIGToComb.h - AIG to Comb dialect conversion -------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_CONVERSION_AIGTOCOMB_H +#define CIRCT_CONVERSION_AIGTOCOMB_H + +#include "circt/Support/LLVM.h" +#include + +namespace circt { + +#define GEN_PASS_DECL_CONVERTAIGTOCOMB +#include "circt/Conversion/Passes.h.inc" + +} // namespace circt + +#endif // CIRCT_CONVERSION_AIGTOCOMB_H diff --git a/include/circt/Conversion/Passes.h b/include/circt/Conversion/Passes.h index 887da277f8aa..30722ab98991 100644 --- a/include/circt/Conversion/Passes.h +++ b/include/circt/Conversion/Passes.h @@ -13,6 +13,7 @@ #ifndef CIRCT_CONVERSION_PASSES_H #define CIRCT_CONVERSION_PASSES_H +#include "circt/Conversion/AIGToComb.h" #include "circt/Conversion/AffineToLoopSchedule.h" #include "circt/Conversion/ArcToLLVM.h" #include "circt/Conversion/CFToHandshake.h" diff --git a/include/circt/Conversion/Passes.td b/include/circt/Conversion/Passes.td index 71fee748a6b2..10ccbc5278e5 100644 --- a/include/circt/Conversion/Passes.td +++ b/include/circt/Conversion/Passes.td @@ -808,4 +808,20 @@ def ConvertCombToAIG: Pass<"convert-comb-to-aig", "hw::HWModuleOp"> { ]; } +//===----------------------------------------------------------------------===// +// ConvertAIGToComb +//===----------------------------------------------------------------------===// + +def ConvertAIGToComb: Pass<"convert-aig-to-comb", "hw::HWModuleOp"> { + let summary = "Lower AIG ops to Comb ops."; + let description = [{ + This pass converts AIG operations to Comb operations. This is mostly + used for verifying post-synthesis results. + }]; + let dependentDialects = [ + "circt::comb::CombDialect", "circt::hw::HWDialect" + ]; +} + + #endif // CIRCT_CONVERSION_PASSES_TD diff --git a/lib/CAPI/Conversion/CMakeLists.txt b/lib/CAPI/Conversion/CMakeLists.txt index 8f6176f027a6..fcb480f3e63e 100644 --- a/lib/CAPI/Conversion/CMakeLists.txt +++ b/lib/CAPI/Conversion/CMakeLists.txt @@ -3,6 +3,7 @@ add_circt_public_c_api_library(CIRCTCAPIConversion LINK_LIBS PUBLIC CIRCTAffineToLoopSchedule + CIRCTAIGToComb CIRCTArcToLLVM CIRCTCalyxToFSM CIRCTCalyxToHW diff --git a/lib/Conversion/AIGToComb/AIGToComb.cpp b/lib/Conversion/AIGToComb/AIGToComb.cpp new file mode 100644 index 000000000000..9d128b025859 --- /dev/null +++ b/lib/Conversion/AIGToComb/AIGToComb.cpp @@ -0,0 +1,86 @@ +//===- AIGToComb.cpp - AIG to Comb Conversion Pass --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This is the main AIG to Comb Conversion Pass Implementation. +// +//===----------------------------------------------------------------------===// + +#include "circt/Conversion/AIGToComb.h" +#include "circt/Dialect/AIG/AIGOps.h" +#include "circt/Dialect/Comb/CombOps.h" +#include "circt/Dialect/HW/HWOps.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Transforms/DialectConversion.h" + +namespace circt { +#define GEN_PASS_DEF_CONVERTAIGTOCOMB +#include "circt/Conversion/Passes.h.inc" +} // namespace circt + +using namespace circt; +using namespace comb; + +//===----------------------------------------------------------------------===// +// Conversion patterns +//===----------------------------------------------------------------------===// + +namespace { + +struct AIGAndInverterOpConversion : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + LogicalResult + matchAndRewrite(aig::AndInverterOp op, OpAdaptor adaptor, + ConversionPatternRewriter &rewriter) const override { + // Convert to comb.and + comb.xor + hw.constant + auto width = op.getResult().getType().getIntOrFloatBitWidth(); + auto allOnes = + rewriter.create(op.getLoc(), APInt::getAllOnes(width)); + SmallVector operands; + operands.reserve(op.getNumOperands()); + for (auto [input, inverted] : llvm::zip(op.getOperands(), op.getInverted())) + operands.push_back(inverted ? rewriter.createOrFold( + op.getLoc(), input, allOnes, true) + : input); + // NOTE: Use createOrFold to avoid creating a new operation if possible. + rewriter.replaceOp( + op, rewriter.createOrFold(op.getLoc(), operands, true)); + return success(); + } +}; + +} // namespace + +//===----------------------------------------------------------------------===// +// Convert AIG to Comb pass +//===----------------------------------------------------------------------===// + +namespace { +struct ConvertAIGToCombPass + : public impl::ConvertAIGToCombBase { + + void runOnOperation() override; + using ConvertAIGToCombBase::ConvertAIGToCombBase; +}; +} // namespace + +static void populateAIGToCombConversionPatterns(RewritePatternSet &patterns) { + patterns.add(patterns.getContext()); +} + +void ConvertAIGToCombPass::runOnOperation() { + ConversionTarget target(getContext()); + target.addLegalDialect(); + target.addIllegalDialect(); + + RewritePatternSet patterns(&getContext()); + populateAIGToCombConversionPatterns(patterns); + + if (failed(mlir::applyPartialConversion(getOperation(), target, + std::move(patterns)))) + return signalPassFailure(); +} diff --git a/lib/Conversion/AIGToComb/CMakeLists.txt b/lib/Conversion/AIGToComb/CMakeLists.txt new file mode 100644 index 000000000000..6e025ba4fd55 --- /dev/null +++ b/lib/Conversion/AIGToComb/CMakeLists.txt @@ -0,0 +1,15 @@ +add_circt_conversion_library(CIRCTAIGToComb + AIGToComb.cpp + + DEPENDS + CIRCTConversionPassIncGen + + LINK_LIBS PUBLIC + CIRCTAIG + CIRCTHW + CIRCTComb + MLIRIR + MLIRPass + MLIRSupport + MLIRTransforms +) diff --git a/lib/Conversion/CMakeLists.txt b/lib/Conversion/CMakeLists.txt index 27bff3679ec4..61ddd4965cab 100644 --- a/lib/Conversion/CMakeLists.txt +++ b/lib/Conversion/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(AffineToLoopSchedule) +add_subdirectory(AIGToComb) add_subdirectory(ArcToLLVM) add_subdirectory(CalyxToFSM) add_subdirectory(CalyxToHW) diff --git a/test/Conversion/AIGToComb/aig-to-comb.mlir b/test/Conversion/AIGToComb/aig-to-comb.mlir new file mode 100644 index 000000000000..3f4c0bc3de65 --- /dev/null +++ b/test/Conversion/AIGToComb/aig-to-comb.mlir @@ -0,0 +1,12 @@ +// RUN: circt-opt %s --convert-aig-to-comb | FileCheck %s + +// CHECK-LABEL: @test +hw.module @test(in %a: i32, in %b: i32, in %c: i32, in %d: i32, out out0: i32) { + // CHECK: %c-1_i32 = hw.constant -1 : i32 + // CHECK: %[[NOT_A:.+]] = comb.xor bin %a, %c-1_i32 : i32 + // CHECK: %[[NOT_C:.+]] = comb.xor bin %c, %c-1_i32 : i32 + // CHECK: %[[RESULT:.+]] = comb.and bin %[[NOT_A]], %b, %[[NOT_C]], %d : i32 + // CHECK: hw.output %[[RESULT]] : i32 + %0 = aig.and_inv not %a, %b, not %c, %d : i32 + hw.output %0 : i32 +} diff --git a/test/circt-synth/basic.mlir b/test/circt-synth/basic.mlir index 110a977e3f87..4b8242279e89 100644 --- a/test/circt-synth/basic.mlir +++ b/test/circt-synth/basic.mlir @@ -1,10 +1,11 @@ // RUN: circt-synth %s | FileCheck %s // RUN: circt-synth %s --until-before aig-lowering | FileCheck %s --check-prefix=AIG +// RUN: circt-synth %s --until-before aig-lowering --convert-to-comb | FileCheck %s --check-prefix=COMB // AIG-LABEL: @and // CHECK-LABEL: @and +// COMB-LABEL: @and hw.module @and(in %a: i2, in %b: i2, in %c: i2, out and: i2) { - %0 = comb.and %a, %b, %c : i2 // AIG-NEXT: %[[AND_INV:.+]] = aig.and_inv %a, %b, %c : i2 // AIG-NEXT: hw.output %[[AND_INV]] : i2 // CHECK-DAG: %[[B_1:.+]] = comb.extract %b from 1 : (i2) -> i1 @@ -19,5 +20,7 @@ hw.module @and(in %a: i2, in %b: i2, in %c: i2, out and: i2) { // CHECK-DAG: %[[AND_INV_3:.+]] = aig.and_inv %[[A_0]], %[[AND_INV_1]] : i1 // CHECK-DAG: %[[CONCAT:.+]] = comb.concat %[[AND_INV_2]], %[[AND_INV_3]] : i1, i1 // CHECK-NEXT: hw.output %[[CONCAT]] : i2 + // COMB-NOT: aig.and_inv + %0 = comb.and %a, %b, %c : i2 hw.output %0 : i2 } diff --git a/tools/circt-synth/CMakeLists.txt b/tools/circt-synth/CMakeLists.txt index 9b0b4b79b81b..5ad952b3c81f 100644 --- a/tools/circt-synth/CMakeLists.txt +++ b/tools/circt-synth/CMakeLists.txt @@ -2,6 +2,7 @@ add_circt_tool(circt-synth circt-synth.cpp) target_link_libraries(circt-synth PRIVATE CIRCTAIG + CIRCTAIGToComb CIRCTAIGTransforms CIRCTComb CIRCTCombToAIG diff --git a/tools/circt-synth/circt-synth.cpp b/tools/circt-synth/circt-synth.cpp index b54c7f90dd92..6f556b71c93a 100644 --- a/tools/circt-synth/circt-synth.cpp +++ b/tools/circt-synth/circt-synth.cpp @@ -11,6 +11,7 @@ /// //===----------------------------------------------------------------------===// +#include "circt/Conversion/AIGToComb.h" #include "circt/Conversion/CombToAIG.h" #include "circt/Dialect/AIG/AIGDialect.h" #include "circt/Dialect/AIG/AIGPasses.h" @@ -26,6 +27,7 @@ #include "mlir/Support/FileUtilities.h" #include "mlir/Support/LogicalResult.h" #include "mlir/Transforms/Passes.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/PrettyStackTrace.h" @@ -77,6 +79,11 @@ static llvm::cl::opt runUntilAfter( "until-after", llvm::cl::desc("Stop pipeline after a specified point"), runUntilValues, llvm::cl::init(UntilEnd), llvm::cl::cat(mainCategory)); +static cl::opt + convertToComb("convert-to-comb", + cl::desc("Convert AIG to Comb at the end of the pipeline"), + cl::init(false), cl::cat(mainCategory)); + //===----------------------------------------------------------------------===// // Main Tool Logic //===----------------------------------------------------------------------===// @@ -90,6 +97,15 @@ static bool untilReached(Until until) { //===----------------------------------------------------------------------===// static void populateSynthesisPipeline(PassManager &pm) { + // Add the AIG to Comb at the scope exit if requested. + auto addAIGToComb = llvm::make_scope_exit([&]() { + if (convertToComb) { + auto &mpm = pm.nest(); + mpm.addPass(circt::createConvertAIGToComb()); + mpm.addPass(createCSEPass()); + } + }); + auto &mpm = pm.nest(); mpm.addPass(circt::createConvertCombToAIG()); mpm.addPass(createCSEPass());