Skip to content

Commit b809bf8

Browse files
authored
[Synth] Refactor and expose dropNonCriticalPaths to Python bindings (#9141)
Refactored LongestPathCollection::sortAndDropNonCriticalPathsPerEndPoint to dropNonCriticalPaths with a parameter to filter by end point or start point. Separated sorting from dropping for better control. Exposed the functionality through C API and Python bindings.
1 parent 1384077 commit b809bf8

File tree

10 files changed

+84
-22
lines changed

10 files changed

+84
-22
lines changed

include/circt-c/Dialect/Synth.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ MLIR_CAPI_EXPORTED void
104104
synthLongestPathCollectionMerge(SynthLongestPathCollection dest,
105105
SynthLongestPathCollection src);
106106

107+
MLIR_CAPI_EXPORTED void synthLongestPathCollectionDropNonCriticalPaths(
108+
SynthLongestPathCollection collection, bool perEndPoint);
109+
107110
//===----------------------------------------------------------------------===//
108111
// DataflowPath API
109112
//===----------------------------------------------------------------------===//

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -342,8 +342,8 @@ class LongestPathCollection {
342342
// Sort the paths by delay in descending order.
343343
void sortInDescendingOrder();
344344

345-
// Sort and drop all paths except the longest path per end point.
346-
void sortAndDropNonCriticalPathsPerEndPoint();
345+
// Drop all paths except the longest path per end point or start point.
346+
void dropNonCriticalPaths(bool perEndPoint = true);
347347

348348
// Merge another collection into this one.
349349
void merge(const LongestPathCollection &other);

integration_test/Bindings/Python/dialects/synth.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,15 @@ def build_top(module):
133133
# CHECK: top:test_aig;child:test_child;a[0] 2
134134
print(collection.longest_path.to_flamegraph())
135135

136+
original_length = len(collection)
137+
collection.drop_non_critical_paths(per_end_point=True)
138+
after_per_end_point = len(collection)
139+
collection.drop_non_critical_paths(per_end_point=False)
140+
after_per_start_point = len(collection)
141+
# CHECK-NEXT: drop_non_critical_paths: True True
142+
print("drop_non_critical_paths:", original_length > after_per_end_point,
143+
after_per_end_point > after_per_start_point)
144+
136145
test_child = m.body.operations[0]
137146
body_block = test_child.regions[0].blocks[0]
138147
result0 = body_block.operations[0].results[0]

lib/Bindings/Python/SynthModule.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,15 @@ void circt::python::populateDialectSynthSubmodule(nb::module_ &m) {
128128
int pathIndex) -> SynthLongestPathDataflowPath {
129129
return synthLongestPathCollectionGetDataflowPath(self, pathIndex);
130130
})
131-
.def("merge", [](SynthLongestPathCollection &self,
132-
SynthLongestPathCollection &src) {
133-
synthLongestPathCollectionMerge(self, src);
134-
});
131+
.def("merge",
132+
[](SynthLongestPathCollection &self,
133+
SynthLongestPathCollection &src) {
134+
synthLongestPathCollectionMerge(self, src);
135+
})
136+
.def("drop_non_critical_paths",
137+
[](SynthLongestPathCollection &self, bool perEndPoint) {
138+
synthLongestPathCollectionDropNonCriticalPaths(self, perEndPoint);
139+
});
135140

136141
nb::class_<SynthLongestPathDataflowPath>(m, "_LongestPathDataflowPath")
137142
.def_prop_ro("delay",

lib/Bindings/Python/dialects/synth.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,17 @@ def merge(self, src: "LongestPathCollection"):
332332
# Re-initialize to reflect the merged collection
333333
self.__init__(self.collection)
334334

335+
def drop_non_critical_paths(self, per_end_point: bool = True):
336+
"""
337+
Drop all paths except the longest path per end point or start point.
338+
Args:
339+
per_end_point: Whether to keep only the longest path per end point
340+
(True) or per start point (False)
341+
"""
342+
self.collection.drop_non_critical_paths(per_end_point)
343+
# Re-initialize to reflect the dropped paths
344+
self.__init__(self.collection)
345+
335346

336347
# ============================================================================
337348
# Main Analysis Interface

lib/CAPI/Dialect/Synth.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,12 @@ void synthLongestPathCollectionMerge(SynthLongestPathCollection dest,
203203
destWrapper->merge(*srcWrapper);
204204
}
205205

206+
void synthLongestPathCollectionDropNonCriticalPaths(
207+
SynthLongestPathCollection collection, bool perEndPoint) {
208+
auto *wrapper = unwrap(collection);
209+
wrapper->dropNonCriticalPaths(perEndPoint);
210+
}
211+
206212
//===----------------------------------------------------------------------===//
207213
// DataflowPath
208214
//===----------------------------------------------------------------------===//

lib/Dialect/Synth/Analysis/LongestPathAnalysis.cpp

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2047,17 +2047,24 @@ void LongestPathCollection::sortInDescendingOrder() {
20472047
});
20482048
}
20492049

2050-
void LongestPathCollection::sortAndDropNonCriticalPathsPerEndPoint() {
2051-
sortInDescendingOrder();
2052-
// Deduplicate paths by end-point, keeping only the worst-case delay per
2053-
// end-point. This gives us the critical delay for each end-point in the
2054-
// design
2055-
llvm::DenseSet<DataflowPath::EndPointType> seen;
2056-
for (size_t i = 0; i < paths.size(); ++i) {
2057-
if (seen.insert(paths[i].getEndPoint()).second)
2058-
paths[seen.size() - 1] = std::move(paths[i]);
2050+
void LongestPathCollection::dropNonCriticalPaths(bool perEndPoint) {
2051+
// Deduplicate paths by start/end-point, keeping only the worst-case delay.
2052+
if (perEndPoint) {
2053+
llvm::DenseSet<DataflowPath::EndPointType> seen;
2054+
for (size_t i = 0; i < paths.size(); ++i) {
2055+
if (seen.insert(paths[i].getEndPoint()).second)
2056+
paths[seen.size() - 1] = std::move(paths[i]);
2057+
}
2058+
2059+
paths.resize(seen.size());
2060+
} else {
2061+
llvm::DenseSet<Object> seen;
2062+
for (size_t i = 0; i < paths.size(); ++i) {
2063+
if (seen.insert(paths[i].getStartPoint()).second)
2064+
paths[seen.size() - 1] = std::move(paths[i]);
2065+
}
2066+
paths.resize(seen.size());
20592067
}
2060-
paths.resize(seen.size());
20612068
}
20622069

20632070
void LongestPathCollection::merge(const LongestPathCollection &other) {

lib/Dialect/Synth/Analysis/PrintLongestPathAnalysis.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ LogicalResult PrintLongestPathAnalysisPass::printAnalysisResult(
9494
}
9595

9696
size_t oldPathCount = collection.paths.size();
97-
collection.sortAndDropNonCriticalPathsPerEndPoint();
97+
collection.sortInDescendingOrder();
98+
collection.dropNonCriticalPaths(/*perEndPoint=*/true);
9899
auto &longestPathForEachEndPoint = collection.paths;
99100

100101
// Print analysis header

test/CAPI/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ target_link_libraries(circt-capi-synth-test
108108
PRIVATE
109109

110110
MLIRCAPIIR
111+
CIRCTCAPIComb
111112
CIRCTCAPIHW
112113
CIRCTCAPISeq
113114
CIRCTCAPISynth

test/CAPI/synth.c

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
*/
1111

1212
#include "circt-c/Dialect/Synth.h"
13+
#include "circt-c/Dialect/Comb.h"
1314
#include "circt-c/Dialect/HW.h"
1415
#include "circt-c/Dialect/Seq.h"
1516
#include "mlir-c/BuiltinAttributes.h"
@@ -39,12 +40,17 @@ void testLongestPathAnalysis(void) {
3940
mlirDialectHandleLoadDialect(mlirGetDialectHandle__synth__(), ctx);
4041
mlirDialectHandleLoadDialect(mlirGetDialectHandle__hw__(), ctx);
4142
mlirDialectHandleLoadDialect(mlirGetDialectHandle__seq__(), ctx);
43+
mlirDialectHandleLoadDialect(mlirGetDialectHandle__comb__(), ctx);
4244

4345
// clang-format off
4446
const char *moduleStr =
4547
"hw.module private @ch(in %c : !seq.clock, in %a: i2, out x: i2) {\n"
4648
" %p = seq.compreg %q, %c : i2\n"
47-
" %q = synth.aig.and_inv %p, %p {sv.namehint = \"q\"}: i2\n"
49+
" %p0 = comb.extract %p from 0 : (i2) -> i1\n"
50+
" %p1 = comb.extract %p from 1 : (i2) -> i1\n"
51+
" %q0 = synth.aig.and_inv %p0, %p1 : i1\n"
52+
" %q1 = synth.aig.and_inv %p0, not %p1 : i1\n"
53+
" %q = comb.concat %q0, %q1 {sv.namehint = \"q\"} : i1, i1\n"
4854
" hw.output %p: i2\n"
4955
"}\n"
5056
"hw.module private @top(in %c : !seq.clock, in %a: i2) {\n"
@@ -71,11 +77,11 @@ void testLongestPathAnalysis(void) {
7177

7278
size_t pathCount = synthLongestPathCollectionGetSize(collection1);
7379
printf("Path count with elaboration: %zu\n", pathCount);
74-
// CHECK: Path count with elaboration: 4
80+
// CHECK: Path count with elaboration: 8
7581

7682
pathCount = synthLongestPathCollectionGetSize(collection2);
7783
printf("Path count without elaboration: %zu\n", pathCount);
78-
// CHECK: Path count without elaboration: 2
84+
// CHECK: Path count without elaboration: 4
7985

8086
// Test DataflowPath API
8187
if (pathCount > 0) {
@@ -101,8 +107,8 @@ void testLongestPathAnalysis(void) {
101107
startPointName.data, startPointBitPos);
102108
printf("EndPoint: %.*s[%zu]\n", (int)endPointName.length,
103109
endPointName.data, endPointBitPos);
104-
// CHECK: StartPoint: p[[[BIT:[0-9]]]]
105-
// CHECK: EndPoint: p[[[BIT]]]
110+
// CHECK: StartPoint: p[{{[0-9]}}]
111+
// CHECK: EndPoint: p[{{[0-9]}}]
106112

107113
// Test instance path
108114
IgraphInstancePath startPointPath =
@@ -162,6 +168,19 @@ void testLongestPathAnalysis(void) {
162168
// CHECK: Input-to-internal paths count: 0
163169
// CHECK-NEXT: Internal-to-output paths count: 2
164170

171+
printf("Path count before drop: %zu\n",
172+
synthLongestPathCollectionGetSize(collection1));
173+
// CHECK: Path count before drop: 8
174+
synthLongestPathCollectionDropNonCriticalPaths(collection1, true);
175+
size_t pathCountAfterDrop = synthLongestPathCollectionGetSize(collection1);
176+
printf("Path count after drop perEndPoint=true: %zu\n", pathCountAfterDrop);
177+
// CHECK: Path count after drop perEndPoint=true: 4
178+
synthLongestPathCollectionDropNonCriticalPaths(collection1, false);
179+
pathCountAfterDrop = synthLongestPathCollectionGetSize(collection1);
180+
printf("Path count after drop perEndPoint=false: %zu\n",
181+
pathCountAfterDrop);
182+
// CHECK: Path count after drop perEndPoint=false: 2
183+
165184
// Cleanup
166185
synthLongestPathCollectionDestroy(collection1);
167186
synthLongestPathCollectionDestroy(collection2);

0 commit comments

Comments
 (0)