Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: [AIG] Add an AIG dialect and circt-synth #7717

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
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
33 changes: 33 additions & 0 deletions docs/Dialects/AIG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# 'aig' Dialect

This document outlines the rationale of the AIG dialect, a dialect used for representing and transforming And-Inverter Graphs.

[TOC]

## Rationale

# Why use the AIG dialect instead of the `comb` dialect?

And-Inverter Graphs have proven to be a scalable approach for logic synthesis, serving as the underlying data structure for ABC, one of the most performant open-source logic synthesis tools.

While it's technically possible to represent `aig.and_inv` using a combination of `comb.and`, `comb.xor`, and `hw.constant`, the ability to represent everything with `aig.and_inv` offers significant advantages. This unified representation simplifies complex analyses such as path retiming and area analysis, as well as logic mappings. Moreover, it allows for direct application of existing AIG research results and tools, further enhancing its utility in the synthesis process.

# Operations
## aig.and_inv

The `aig.and_inv` operation directly represents an And-Node in the AIG. Traditionally, an And-Node in AIG has two operands. However, `aig.and_inv` extends this concept by allowing variadic operands and non-i1 integer types. Although the final stage of the pipeline requires lowering everything to i1-binary operands, it's more efficient to progressively lower the variadic multibit operations.

Variadic operands have demonstrated their utility in low-level optimizations within the `comb` dialect. Furthermore, in synthesis, it's common practice to re-balance the logic path. Variadic operands enable the compiler to select more efficient solutions without the need to traverse binary trees multiple times.

The ability to represent multibit operations during synthesis is crucial for scalable logic optimization. This approach enables a form of vectorization, allowing for batch processing of logic synthesis when multibit operations are constructed in a similar manner. Such vectorization can significantly improve the efficiency and performance of the synthesis process.

## aig.cut

The `aig.cut` operation represents a "cut" in the logic tree. This operation possesses the `IsolatedAbove` trait and contains a single block. Its input operands represent the input edges, while the returned value represents the output edges.

This operation proves particularly useful for progressive LUT mapping. For instance, a k-input cut can be readily mapped to a k-input LUT. Consequently, the subsequent stages of the pipeline can concentrate on replacing combinational logic with k-input Cut operations, simplifying the overall process.
Comment on lines +16 to +28
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe move this documentation to the description fields in the tablgen files to avoid having the same heading twice in this page?



## Operations

[include "Dialects/AIGOps.md"]
25 changes: 25 additions & 0 deletions include/circt/Conversion/CombToAIG.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//===- CombToAIG.h - Comb to AIG 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_COMBTOAIG_H
#define CIRCT_CONVERSION_COMBTOAIG_H

#include "circt/Support/LLVM.h"
#include <memory>

namespace circt {
namespace hw {
class HWModuleOp;
}

#define GEN_PASS_DECL_CONVERTCOMBTOAIG
#include "circt/Conversion/Passes.h.inc"

} // namespace circt

#endif // CIRCT_CONVERSION_COMBTOAIG_H
1 change: 1 addition & 0 deletions include/circt/Conversion/Passes.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "circt/Conversion/CalyxNative.h"
#include "circt/Conversion/CalyxToFSM.h"
#include "circt/Conversion/CalyxToHW.h"
#include "circt/Conversion/CombToAIG.h"
#include "circt/Conversion/CombToArith.h"
#include "circt/Conversion/CombToSMT.h"
#include "circt/Conversion/ConvertToArcs.h"
Expand Down
12 changes: 12 additions & 0 deletions include/circt/Conversion/Passes.td
Original file line number Diff line number Diff line change
Expand Up @@ -790,4 +790,16 @@ def LowerSimToSV: Pass<"lower-sim-to-sv", "mlir::ModuleOp"> {
];
}

//===----------------------------------------------------------------------===//
// ConvertCombToAIG
//===----------------------------------------------------------------------===//

def ConvertCombToAIG: Pass<"convert-comb-to-aig", "hw::HWModuleOp"> {
let summary = "Lower comb ops to aig ops.";
let dependentDialects = [
"circt::comb::CombDialect",
"circt::aig::AIGDialect",
];
}

#endif // CIRCT_CONVERSION_PASSES_TD
22 changes: 22 additions & 0 deletions include/circt/Dialect/AIG/AIG.td
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//===- AIG.td - AIG Definitions ----------------------------*- tablegen -*-===//
//
// 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_AIG_DIALECT_TD
#define CIRCT_AIG_DIALECT_TD

include "mlir/IR/DialectBase.td"

def AIG_Dialect : Dialect {
let name = "aig";
let cppNamespace = "::circt::aig";
let summary = "Representation of AIGs";
}

include "circt/Dialect/AIG/AIGOps.td"

#endif // CIRCT_AIG_DIALECT_TD
22 changes: 22 additions & 0 deletions include/circt/Dialect/AIG/AIGDialect.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//===- AIGDialect.h - AIG Definitions ---------------------------*- 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 file defines the AIG CIRCT dialect.
//
//===----------------------------------------------------------------------===//

#ifndef CIRCT_DIALECT_AIG_AIGDIALECT_H
#define CIRCT_DIALECT_AIG_AIGDIALECT_H

#include "circt/Support/LLVM.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/Dialect.h"

#include "circt/Dialect/AIG/AIGDialect.h.inc"

#endif // CIRCT_DIALECT_AIG_AIGDIALECT_H
31 changes: 31 additions & 0 deletions include/circt/Dialect/AIG/AIGOps.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//===- AIGOps.h - AIG Op Definitions ----------------------------*- 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_DIALECT_AIG_AIGOPS_H
#define CIRCT_DIALECT_AIG_AIGOPS_H

#include "circt/Support/LLVM.h"
#include "mlir/IR/Attributes.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/BuiltinTypes.h"
#include "mlir/IR/Dialect.h"
#include "mlir/IR/OpDefinition.h"
#include "mlir/IR/OpImplementation.h"
#include "mlir/IR/Operation.h"
#include "mlir/Interfaces/ControlFlowInterfaces.h"
#include "mlir/Interfaces/FunctionInterfaces.h"
#include "mlir/Interfaces/InferTypeOpInterface.h"
#include "mlir/Interfaces/SideEffectInterfaces.h"

#include "circt/Dialect/AIG/AIGDialect.h"

#define GET_OP_CLASSES
#include "circt/Dialect/AIG/AIG.h.inc"

#endif // CIRCT_DIALECT_AIG_AIGOPS_H
123 changes: 123 additions & 0 deletions include/circt/Dialect/AIG/AIGOps.td
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
//===- AIGOps.td - AIG Op Definitions -------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// AIG Ops are defined in tablegen.
//
//===----------------------------------------------------------------------===//

#ifndef CIRCT_DIALECT_AIG_OPS_TD
#define CIRCT_DIALECT_AIG_OPS_TD

include "circt/Dialect/AIG/AIG.td"
include "mlir/IR/OpBase.td"
include "mlir/Interfaces/ControlFlowInterfaces.td"
include "mlir/Interfaces/InferTypeOpInterface.td"
include "mlir/Interfaces/SideEffectInterfaces.td"


class AIGOp<string mnemonic, list<Trait> traits = []> :
Op<AIG_Dialect, mnemonic, traits>;

def AndInverterOp : AIGOp<"and_inv", [SameOperandsAndResultType, Pure]> {
let summary = "AIG dialect AND operation";
let description = [{
The `aig.and_inv` operation represents an And-Inverter in the AIG dialect.
Unlike `comb.and`, operands can be inverted respectively.

Example:
```mlir
%r1 = aig.and_inv %a, %b: i3
%r2 = aig.and_inv not %a, %b, not %c : i3
%r3 = aig.and_inv not %a : i3
```
}];
// TODO: Restrict to HWIntegerType.
let arguments = (ins Variadic<AnyType>:$inputs, DenseBoolArrayAttr:$inverted);
let results = (outs AnyType:$result);

// NOTE: Custom assembly format is needed to pretty print the `inverted`
// attribute.
let hasCustomAssemblyFormat = 1;

let builders = [
OpBuilder<(ins "Value":$input, CArg<"bool", "false">:$invert), [{
SmallVector<bool> inverted {invert};
return build($_builder, $_state, {input}, inverted);
}]>,
OpBuilder<(ins "Value":$lhs, "Value":$rhs, CArg<"bool", "false">:$invertLhs,
CArg<"bool", "false">:$invertRhs), [{
SmallVector<bool> inverted {invertLhs, invertRhs};
return build($_builder, $_state, {lhs, rhs}, inverted);
}]>
];

let extraClassDeclaration = [{
// Evaluate the operation with the given input values.
APInt evaluate(ArrayRef<APInt> inputs);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really nice that you don't use the folder for that. 💯
At some point we should probably have some OpInterface for that because I'm doing basically the same in LLHD desequentialization for comb ops.


// Check if the input is inverted.
bool isInverted(size_t idx) {
return getInverted()[idx];
}
}];
let hasFolder = 1;
let hasCanonicalizeMethod = 1;
}

def CutOp : AIGOp<"cut", [IsolatedFromAbove, SingleBlock]> {
let summary = "AIG dialect Cut operation";
let description = [{
The `aig.cut` operation represents a cut in the And-Inverter-Graph.
This operation is variadic and can take multiple inputs and outputs,
which corresponds to the input and output edges in AIG conceptually.

```mlir
%0, %1 = aig.cut %a, %b, %c, %d : (i1, i1, i1, i1) -> (i1, i1) {
^bb0(%arg0: i1, %arg1: i1, %arg2: i1, %arg3: i1):
%0 = aig.and_inv not %arg0, %arg1 : i1
%1 = aig.and_inv %arg1, %arg3 : i1
aig.output %0, %1 : i1 }
```

}];

// TODO: Restrict to HWIntegerType.
let arguments = (ins Variadic<AnyType>:$inputs);
let results = (outs Variadic<AnyType>:$results);
uenoku marked this conversation as resolved.
Show resolved Hide resolved
let regions = (region SizedRegion<1>:$bodyRegion);
let assemblyFormat = [{
$inputs attr-dict `:` functional-type($inputs, $results) $bodyRegion
}];

let builders = [
OpBuilder<(ins
CArg<"TypeRange", "{}">:$resultTypes,
CArg<"ValueRange", "{}">:$inputs,
CArg<"std::function<void(mlir::Block::BlockArgListType)>", "{}">:$ctor)>
];

let hasVerifier = 1;

let extraClassDeclaration = [{
Block *getBodyBlock() { return getBody(); }
}];
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably add a verifier that checks that the block arguments and input operands match. Same for the output op operands and results.


def OutputOp : AIGOp<"output", [Terminator,
ReturnLike, ParentOneOf<["CutOp"]>]> {
let summary = "AIG dialect Output operation";
let description = [{
The `aig.output` operation represents out edges of a cut.
}];
let arguments = (ins Variadic<AnyType>:$outputs);
let assemblyFormat = [{
attr-dict ($outputs^ `:` qualified(type($outputs)))?
}];
}

#endif // CIRCT_DIALECT_AIG_OPS_TD
34 changes: 34 additions & 0 deletions include/circt/Dialect/AIG/AIGPasses.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//===- AIGPasses.h - AIG dialect passes -----------------------------------===//
//
// 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_DIALECT_AIG_AIGPASSES_H
#define CIRCT_DIALECT_AIG_AIGPASSES_H

#include "mlir/Pass/Pass.h"
#include <memory>
#include <optional>

namespace mlir {
class Pass;
} // namespace mlir

#include "circt/Dialect/AIG/AIGPassesEnums.h.inc"

namespace circt {
namespace aig {

#define GEN_PASS_DECL
#include "circt/Dialect/AIG/AIGPasses.h.inc"

#define GEN_PASS_REGISTRATION
#include "circt/Dialect/AIG/AIGPasses.h.inc"

} // namespace aig
} // namespace circt

#endif // CIRCT_DIALECT_AIG_AIGPASSES_H
37 changes: 37 additions & 0 deletions include/circt/Dialect/AIG/AIGPasses.td
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//===- AIGPasses.td - AIG dialect passes -------------------*- tablegen -*-===//
//
// 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_DIALECT_AIG_AIGPASSES_TD
#define CIRCT_DIALECT_AIG_AIGPASSES_TD

include "mlir/Pass/PassBase.td"

def LowerCutToLUT : Pass<"aig-lower-cut-to-lut", "hw::HWModuleOp"> {
let summary = "Lower a cut to a LUT";
let dependentDialects = ["comb::CombDialect"];
}

def LowerVariadic : Pass<"aig-lower-variadic", "hw::HWModuleOp"> {
let summary = "Lower variadic AndInverter operations to binary AndInverter";
}

def LowerWordToBits : Pass<"aig-lower-word-to-bits", "hw::HWModuleOp"> {
let summary = "Lower multi-bit AIG operations to single-bit ones";
let dependentDialects = ["comb::CombDialect"];
}

def GreedyCutDecomp : Pass<"aig-greedy-cut-decomp", "hw::HWModuleOp"> {
let summary = "Decompose AIGs into k-feasible Cuts using a greedy algorithm";
let dependentDialects = ["comb::CombDialect"];
let options = [
Option<"cutSizes", "cut-sizes", "uint32_t", "6",
"The sizes of the cuts to decompose">,
];
}

#endif // CIRCT_DIALECT_AIG_AIGPASSES_TD
9 changes: 9 additions & 0 deletions include/circt/Dialect/AIG/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
add_circt_dialect(AIG aig)
add_circt_dialect_doc(AIG aig)

set(LLVM_TARGET_DEFINITIONS AIGPasses.td)
mlir_tablegen(AIGPasses.h.inc -gen-pass-decls)
mlir_tablegen(AIGPassesEnums.h.inc -gen-enum-decls)
mlir_tablegen(AIGPassesEnums.cpp.inc -gen-enum-defs)
add_public_tablegen_target(CIRCTAIGTransformsIncGen)
add_circt_doc(AIGPasses AIGPasses -gen-pass-doc)
1 change: 1 addition & 0 deletions include/circt/Dialect/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
##
##===----------------------------------------------------------------------===//

add_subdirectory(AIG)
add_subdirectory(Arc)
add_subdirectory(Calyx)
add_subdirectory(Comb)
Expand Down
2 changes: 2 additions & 0 deletions include/circt/InitAllDialects.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#ifndef CIRCT_INITALLDIALECTS_H_
#define CIRCT_INITALLDIALECTS_H_

#include "circt/Dialect/AIG/AIGDialect.h"
#include "circt/Dialect/Arc/ArcDialect.h"
#include "circt/Dialect/Calyx/CalyxDialect.h"
#include "circt/Dialect/Comb/CombDialect.h"
Expand Down Expand Up @@ -51,6 +52,7 @@ namespace circt {
inline void registerAllDialects(mlir::DialectRegistry &registry) {
// clang-format off
registry.insert<
aig::AIGDialect,
arc::ArcDialect,
calyx::CalyxDialect,
chirrtl::CHIRRTLDialect,
Expand Down
Loading
Loading