-
Notifications
You must be signed in to change notification settings - Fork 302
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
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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. | ||
|
||
|
||
## Operations | ||
|
||
[include "Dialects/AIGOps.md"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
//===- CombToAIG.h - Comb to AIG dialect conversion ---------*- C++ -*-===// | ||
uenoku marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// | ||
// 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_COMBTOARITH_H | ||
uenoku marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
//===- AIG.td - AIG Definitions ----------*- tablegen -*-===// | ||
uenoku marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// | ||
// 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" | ||
include "mlir/IR/OpBase.td" | ||
uenoku marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
def AIG_Dialect : Dialect { | ||
let name = "aig"; | ||
let cppNamespace = "::circt::aig"; | ||
let summary = "Representation of AIGs"; | ||
|
||
let usePropertiesForAttributes = 0; | ||
uenoku marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
include "circt/Dialect/AIG/AIGOps.td" | ||
|
||
#endif // CIRCT_AIG_DIALECT_TD |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
//===- AIGDialect.h - AIG Definitions --------------------------*- C++ -*-===// | ||
uenoku marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// | ||
// 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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
//===- AIGOps.h - AIG Op Definitions ---------------------------*- C++ -*-===// | ||
uenoku marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// | ||
// 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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
//===- 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. | ||
uenoku marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Really nice that you don't use the folder for that. 💯 |
||
|
||
// 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 | ||
} | ||
``` | ||
|
||
}]; | ||
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>:$body); | ||
let assemblyFormat = [{ | ||
$inputs attr-dict `:` functional-type($inputs, $results) $body | ||
}]; | ||
|
||
let builders = [ | ||
OpBuilder<(ins | ||
CArg<"TypeRange", "{}">:$resultTypes, | ||
CArg<"ValueRange", "{}">:$inputs, | ||
CArg<"std::function<void()>", "{}">:$ctor)> | ||
]; | ||
|
||
let extraClassDeclaration = [{ | ||
Block *getBodyBlock() { return &getBody().front(); } | ||
}]; | ||
uenoku marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
//===- 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" | ||
|
||
std::unique_ptr<mlir::Pass> createLowerCutToLUTPass(); | ||
std::unique_ptr<mlir::Pass> createLowerVariadicPass(); | ||
std::unique_ptr<mlir::Pass> createLowerWordToBitsPass(); | ||
std::unique_ptr<mlir::Pass> | ||
createGreedyCutDecompPass(const GreedyCutDecompOptions &options = {}); | ||
|
||
#define GEN_PASS_REGISTRATION | ||
#include "circt/Dialect/AIG/AIGPasses.h.inc" | ||
|
||
} // namespace aig | ||
} // namespace circt | ||
|
||
#endif // CIRCT_DIALECT_AIG_AIGPASSES_H |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
//===- ArcPasses.td - Arc dialect passes -------------------*- tablegen -*-===// | ||
uenoku marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// | ||
// 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_ARC_ARCPASSES_TD | ||
#define CIRCT_DIALECT_ARC_ARCPASSES_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"]; | ||
let constructor = "circt::aig::createLowerCutToLUTPass()"; | ||
uenoku marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
def LowerVariadic : Pass<"aig-lower-variadic", "hw::HWModuleOp"> { | ||
let summary = "Lower variadic AndInverter operations to binary AndInverter"; | ||
let constructor = "circt::aig::createLowerVariadicPass()"; | ||
} | ||
|
||
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"]; | ||
let constructor = "circt::aig::createLowerWordToBitsPass()"; | ||
} | ||
|
||
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 constructor = "circt::aig::createGreedyCutDecompPass()"; | ||
let options = [ | ||
Option<"cutSizes", "cut-sizes", "uint32_t", "6", | ||
"The sizes of the cuts to decompose">, | ||
]; | ||
} | ||
|
||
#endif // CIRCT_DIALECT_ARC_ARCPASSES_TD |
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) |
There was a problem hiding this comment.
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?