Skip to content

Commit e0d8429

Browse files
committed
[AIG] Extend longest path analysis to support Comb/HW dialects and enable timing-aware DatapathToComb optimization
This commit enhances the AIG longest path analysis infrastructure to support operations from the Comb and HW dialects, enabling comprehensive timing analysis across mixed-dialect circuits. The DatapathToComb pass has been updated to use timing-aware optimization strategies through the incremental longest path analysis framework. The longest path analysis now handles comb.and, comb.or, comb.xor, and hw.constant operations alongside existing AIG operations, providing more accurate delay calculations for circuits that haven't been fully lowered to AIG. An Oracle class has been introduced to dynamically analyze unsupported operations by creating temporary modules and running synthesis pipelines to extract timing characteristics. The DatapathToComb pass integrates IncrementalLongestPathAnalysis as a listener in its greedy rewrite driver, enabling timing-aware transformations. The compressor lowering logic now sorts addends by arrival time to minimize critical path delays during Wallace tree construction. Configuration options for timing-aware datapath optimizations have been added to the synthesis pipeline. Test coverage has been expanded to verify longest path analysis behavior with mixed dialects and ensure proper timing optimization during datapath-to-combinational lowering. This enables more sophisticated timing optimization strategies throughout the CIRCT synthesis pipeline by providing accurate delay information across
1 parent 294bfb4 commit e0d8429

File tree

14 files changed

+520
-142
lines changed

14 files changed

+520
-142
lines changed

include/circt/Conversion/Passes.td

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -885,7 +885,9 @@ def ConvertDatapathToComb : Pass<"convert-datapath-to-comb"> {
885885
Option<"lowerCompressToAdd", "lower-compress-to-add", "bool", "false",
886886
"Lower compress operators to variadic add.">,
887887
Option<"forceBooth", "lower-partial-product-to-booth", "bool", "false",
888-
"Force all partial products to be lowered to Booth arrays.">
888+
"Force all partial products to be lowered to Booth arrays.">,
889+
Option<"timingAware", "timing-aware", "bool", "false",
890+
"Lower operators in a timing-aware fashion.">
889891
];
890892
}
891893

include/circt/Dialect/AIG/Analysis/LongestPathAnalysis.h

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@
1717
#ifndef CIRCT_ANALYSIS_AIG_ANALYSIS_H
1818
#define CIRCT_ANALYSIS_AIG_ANALYSIS_H
1919

20+
#include "circt/Conversion/CombToAIG.h"
2021
#include "circt/Dialect/HW/HWOps.h"
22+
#include "circt/Dialect/HW/HWPasses.h"
2123
#include "circt/Support/InstanceGraph.h"
2224
#include "circt/Support/LLVM.h"
2325
#include "mlir/IR/BuiltinOps.h"
2426
#include "mlir/IR/MLIRContext.h"
2527
#include "mlir/IR/Operation.h"
28+
#include "mlir/Transforms/Passes.h"
2629
#include "llvm/ADT/ArrayRef.h"
2730
#include "llvm/ADT/ImmutableList.h"
2831
#include "llvm/ADT/SmallVector.h"
@@ -272,20 +275,21 @@ class IncrementalLongestPathAnalysis : private LongestPathAnalysis,
272275
: LongestPathAnalysis(moduleOp, am,
273276
LongestPathAnalysisOption(false, true)) {}
274277

275-
FailureOr<int64_t> getOrComputeDelay(Value value, size_t bitPos);
278+
FailureOr<int64_t> getOrComputeMaxDelay(Value value, size_t bitPos);
276279
FailureOr<ArrayRef<OpenPath>> getOrComputePaths(Value value, size_t bitPos);
277280

278-
// Returns true if the analysis is still valid after the value is erased.
279-
// If the client erased a value, then the analyais cannot be used anymore.
280-
// A client should check this before erasing a value, and if this
281-
// returns false, the client should raise an error or should not rely on the
282-
// analysis.
283-
bool isValueValidToErase(Value value) const;
281+
// Returns true if the analysis is still valid after the operation is
282+
// modified. A client should check this function before peforming operaton
283+
// mutations, and if this returns false, the client should raise an error or
284+
// should not rely on the analysis.
285+
bool isOperationValidToMutate(Operation *op) const;
284286

285287
// MLIR PatternRewriter::Listener interface.
288+
void notifyOperationModified(Operation *op) override;
286289
void notifyOperationReplaced(Operation *op, ValueRange replacement) override;
287290
void notifyOperationErased(Operation *op) override;
288291

292+
private:
289293
bool isAnalysisValid = true;
290294
};
291295

@@ -301,7 +305,7 @@ class LongestPathAnalysisWithTrace : public LongestPathAnalysis {
301305
// for computing statistics and CAPI.
302306
class LongestPathCollection {
303307
public:
304-
LongestPathCollection(MLIRContext *ctx) : ctx(ctx){};
308+
LongestPathCollection(MLIRContext *ctx) : ctx(ctx) {};
305309
const DataflowPath &getPath(unsigned index) const { return paths[index]; }
306310
MLIRContext *getContext() const { return ctx; }
307311
llvm::SmallVector<DataflowPath, 64> paths;
@@ -316,6 +320,18 @@ class LongestPathCollection {
316320
MLIRContext *ctx;
317321
};
318322

323+
// Register prerequisite passes for AIG longest path analysis.
324+
// This includes the Comb to AIG conversion pass and necessary
325+
// canonicalization passes to clean up the IR.
326+
inline void registerAIGAnalysisPrerequisitePasses() {
327+
hw::registerHWAggregateToCombPass();
328+
::mlir::registerPass([]() -> std::unique_ptr<::mlir::Pass> {
329+
return createConvertCombToAIG();
330+
});
331+
mlir::registerCSEPass();
332+
mlir::registerCanonicalizerPass();
333+
}
334+
319335
} // namespace aig
320336
} // namespace circt
321337

@@ -344,6 +360,7 @@ struct DenseMapInfo<circt::aig::Object> {
344360
{b.instancePath, b.value, b.bitPos});
345361
}
346362
};
363+
347364
} // namespace llvm
348365

349366
#endif // CIRCT_ANALYSIS_AIG_ANALYSIS_H

include/circt/Dialect/Synth/Transforms/SynthesisPipeline.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ struct AIGLoweringPipelineOptions
3131
*this, "disable-datapath",
3232
llvm::cl::desc("Disable datapath optimization passes"),
3333
llvm::cl::init(false)};
34+
PassOptions::Option<bool> timingAware{
35+
*this, "timing-aware",
36+
llvm::cl::desc("Lower operators in a timing-aware fashion"),
37+
llvm::cl::init(false)};
3438
};
3539

3640
/// Options for the aig optimization pipeline.

lib/Conversion/DatapathToComb/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ add_circt_conversion_library(CIRCTDatapathToComb
55
CIRCTConversionPassIncGen
66

77
LINK_LIBS PUBLIC
8+
CIRCTAIGAnalysis
89
CIRCTComb
910
CIRCTDatapath
1011
CIRCTHW

lib/Conversion/DatapathToComb/DatapathToComb.cpp

Lines changed: 57 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@
1313
#include "circt/Dialect/HW/HWOps.h"
1414
#include "mlir/Analysis/TopologicalSortUtils.h"
1515
#include "mlir/Dialect/Func/IR/FuncOps.h"
16+
#include "mlir/IR/PatternMatch.h"
1617
#include "mlir/Pass/Pass.h"
17-
#include "mlir/Transforms/DialectConversion.h"
18+
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
1819
#include "llvm/Support/Debug.h"
1920

2021
#define DEBUG_TYPE "datapath-to-comb"
@@ -42,11 +43,11 @@ namespace {
4243
// Replace compressor by an adder of the inputs and zero for the other results:
4344
// compress(a,b,c,d) -> {a+b+c+d, 0}
4445
// Facilitates use of downstream compression algorithms e.g. Yosys
45-
struct DatapathCompressOpAddConversion : OpConversionPattern<CompressOp> {
46-
using OpConversionPattern<CompressOp>::OpConversionPattern;
46+
struct DatapathCompressOpAddConversion : mlir::OpRewritePattern<CompressOp> {
47+
using mlir::OpRewritePattern<CompressOp>::OpRewritePattern;
4748
LogicalResult
48-
matchAndRewrite(CompressOp op, OpAdaptor adaptor,
49-
ConversionPatternRewriter &rewriter) const override {
49+
matchAndRewrite(CompressOp op,
50+
mlir::PatternRewriter &rewriter) const override {
5051
Location loc = op.getLoc();
5152
auto inputs = op.getOperands();
5253
unsigned width = inputs[0].getType().getIntOrFloatBitWidth();
@@ -62,15 +63,14 @@ struct DatapathCompressOpAddConversion : OpConversionPattern<CompressOp> {
6263
};
6364

6465
// Replace compressor by a wallace tree of full-adders
65-
struct DatapathCompressOpConversion : OpConversionPattern<CompressOp> {
66-
using OpConversionPattern<CompressOp>::OpConversionPattern;
66+
struct DatapathCompressOpConversion : mlir::OpRewritePattern<CompressOp> {
6767
DatapathCompressOpConversion(MLIRContext *context,
6868
aig::IncrementalLongestPathAnalysis *analysis)
69-
: OpConversionPattern<CompressOp>(context), analysis(analysis) {}
69+
: mlir::OpRewritePattern<CompressOp>(context), analysis(analysis) {}
7070

7171
LogicalResult
72-
matchAndRewrite(CompressOp op, OpAdaptor adaptor,
73-
ConversionPatternRewriter &rewriter) const override {
72+
matchAndRewrite(CompressOp op,
73+
mlir::PatternRewriter &rewriter) const override {
7474
Location loc = op.getLoc();
7575
auto inputs = op.getOperands();
7676
unsigned width = inputs[0].getType().getIntOrFloatBitWidth();
@@ -79,30 +79,6 @@ struct DatapathCompressOpConversion : OpConversionPattern<CompressOp> {
7979
for (auto input : inputs) {
8080
addends.push_back(
8181
extractBits(rewriter, input)); // Extract bits from each input
82-
83-
// NOTE: Following change will be splitted into a separate PR.
84-
if (analysis) {
85-
auto delay = analysis->getOrComputePaths(input, 0);
86-
if (failed(delay))
87-
return op.emitError("Failed to get delay for input");
88-
// TODO: Use the delay information to sort the inputs.
89-
}
90-
91-
LLVM_DEBUG({
92-
llvm::dbgs() << "Input: " << input << " delay: ";
93-
assert(analysis && "Expected analysis to be set");
94-
if (analysis) {
95-
auto delay = analysis->getOrComputeDelay(
96-
input, 0); // Query delay for each input
97-
if (llvm::succeeded(delay))
98-
llvm::dbgs() << *delay;
99-
else
100-
llvm::dbgs() << "N/A";
101-
} else {
102-
llvm::dbgs() << "N/A(analysis not set)";
103-
}
104-
llvm::dbgs() << "\n";
105-
});
10682
}
10783

10884
// Wallace tree reduction
@@ -111,6 +87,26 @@ struct DatapathCompressOpConversion : OpConversionPattern<CompressOp> {
11187
// sort the inputs according to arrival time.
11288
// TODO: Use the listener to get arrival time information.
11389
auto targetAddends = op.getNumResults();
90+
if (analysis) {
91+
// Sort the addends row based on the delay of the input.
92+
for (size_t j = 0; j < addends[0].size(); ++j) {
93+
SmallVector<std::pair<int64_t, Value>> delays;
94+
for (size_t i = 0; i < addends.size(); ++i) {
95+
auto delay = analysis->getOrComputeMaxDelay(addends[i][j], 0);
96+
if (failed(delay))
97+
return rewriter.notifyMatchFailure(op,
98+
"Failed to get delay for input");
99+
delays.push_back(std::make_pair(*delay, addends[i][j]));
100+
}
101+
std::stable_sort(delays.begin(), delays.end(),
102+
[](const std::pair<int64_t, Value> &a,
103+
const std::pair<int64_t, Value> &b) {
104+
return a.first < b.first;
105+
});
106+
for (size_t i = 0; i < addends.size(); ++i)
107+
addends[i][j] = delays[i].second;
108+
}
109+
}
114110
rewriter.replaceOp(op, comb::wallaceReduction(rewriter, loc, width,
115111
targetAddends, addends));
116112
return success();
@@ -120,19 +116,16 @@ struct DatapathCompressOpConversion : OpConversionPattern<CompressOp> {
120116
aig::IncrementalLongestPathAnalysis *analysis = nullptr;
121117
};
122118

123-
struct DatapathPartialProductOpConversion
124-
: OpConversionPattern<PartialProductOp> {
125-
using OpConversionPattern<PartialProductOp>::OpConversionPattern;
119+
struct DatapathPartialProductOpConversion : OpRewritePattern<PartialProductOp> {
120+
using OpRewritePattern<PartialProductOp>::OpRewritePattern;
126121

127122
DatapathPartialProductOpConversion(MLIRContext *context, bool forceBooth)
128-
: OpConversionPattern<PartialProductOp>(context),
129-
forceBooth(forceBooth){};
123+
: OpRewritePattern<PartialProductOp>(context), forceBooth(forceBooth){};
130124

131125
const bool forceBooth;
132126

133-
LogicalResult
134-
matchAndRewrite(PartialProductOp op, OpAdaptor adaptor,
135-
ConversionPatternRewriter &rewriter) const override {
127+
LogicalResult matchAndRewrite(PartialProductOp op,
128+
PatternRewriter &rewriter) const override {
136129

137130
Value a = op.getLhs();
138131
Value b = op.getRhs();
@@ -152,8 +145,8 @@ struct DatapathPartialProductOpConversion
152145
}
153146

154147
private:
155-
static LogicalResult lowerAndArray(ConversionPatternRewriter &rewriter,
156-
Value a, Value b, PartialProductOp op,
148+
static LogicalResult lowerAndArray(PatternRewriter &rewriter, Value a,
149+
Value b, PartialProductOp op,
157150
unsigned width) {
158151

159152
Location loc = op.getLoc();
@@ -179,8 +172,8 @@ struct DatapathPartialProductOpConversion
179172
return success();
180173
}
181174

182-
static LogicalResult lowerBoothArray(ConversionPatternRewriter &rewriter,
183-
Value a, Value b, PartialProductOp op,
175+
static LogicalResult lowerBoothArray(PatternRewriter &rewriter, Value a,
176+
Value b, PartialProductOp op,
184177
unsigned width) {
185178
Location loc = op.getLoc();
186179
auto zeroFalse = hw::ConstantOp::create(rewriter, loc, APInt(1, 0));
@@ -288,42 +281,40 @@ struct ConvertDatapathToCombPass
288281
};
289282
} // namespace
290283

291-
static LogicalResult
292-
applyConversionWithTimingInfo(Operation *op, const ConversionTarget &target,
293-
RewritePatternSet &&patterns,
294-
aig::IncrementalLongestPathAnalysis *analysis) {
284+
static LogicalResult applyPatternsGreedilyWithTimingInfo(
285+
Operation *op, RewritePatternSet &&patterns,
286+
aig::IncrementalLongestPathAnalysis *analysis) {
295287
// TODO: Topologically sort the operations in the module to ensure that all
296288
// dependencies are processed before their users.
297-
mlir::ConversionConfig config;
298-
config.listener = analysis;
299-
300-
// Apply the conversion patterns
301-
if (failed(mlir::applyPartialConversion(op, target, std::move(patterns))))
289+
mlir::GreedyRewriteConfig config;
290+
// Set the listener to update timing information
291+
// HACK: Setting max iterations to 2 to ensure that the patterns are one-shot,
292+
// making sure target operations are datapath operations are replaced.
293+
config.setMaxIterations(2).setListener(analysis).setUseTopDownTraversal(true);
294+
295+
// Apply the patterns greedily
296+
if (failed(mlir::applyPatternsGreedily(op, std::move(patterns), config)))
302297
return failure();
303298

304299
return success();
305300
}
306301

307302
void ConvertDatapathToCombPass::runOnOperation() {
308-
ConversionTarget target(getContext());
309-
310-
target.addLegalDialect<comb::CombDialect, hw::HWDialect>();
311-
target.addIllegalDialect<DatapathDialect>();
312-
313303
RewritePatternSet patterns(&getContext());
314304

315305
patterns.add<DatapathPartialProductOpConversion>(patterns.getContext(),
316306
forceBooth);
317-
auto &analysis = getAnalysis<aig::IncrementalLongestPathAnalysis>();
307+
aig::IncrementalLongestPathAnalysis *analysis = nullptr;
308+
if (timingAware)
309+
analysis = &getAnalysis<aig::IncrementalLongestPathAnalysis>();
318310
if (lowerCompressToAdd)
319-
// Lower compressors to simple add operations for downstream optimisations
311+
// Lower compressors to simple add operations for downstream optimizations
320312
patterns.add<DatapathCompressOpAddConversion>(patterns.getContext());
321313
else
322314
// Lower compressors to a complete gate-level implementation
323-
patterns.add<DatapathCompressOpConversion>(patterns.getContext(),
324-
&analysis);
315+
patterns.add<DatapathCompressOpConversion>(patterns.getContext(), analysis);
325316

326-
if (failed(applyConversionWithTimingInfo(getOperation(), target,
327-
std::move(patterns), &analysis)))
317+
if (failed(applyPatternsGreedilyWithTimingInfo(
318+
getOperation(), std::move(patterns), analysis)))
328319
return signalPassFailure();
329320
}

lib/Dialect/AIG/Analysis/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ add_circt_dialect_library(CIRCTAIGAnalysis
99
MLIRSupport
1010
CIRCTAIG
1111
CIRCTComb
12+
CIRCTCombToAIG
13+
CIRCTTransforms
1214
CIRCTHW
1315
CIRCTSeq
1416
)

0 commit comments

Comments
 (0)