Skip to content

Commit ba41ee0

Browse files
authored
[CombToSynth] Use parallel-prefix tree for unsigned comparisons (#9048)
This commit extends the unsigned comparison lowering to support multiple parallel-prefix architectures (Sklanskey, Kogge-Stone, Brent-Kung) in addition to the existing ripple-carry implementation. No functional change in prefix-tree/adder lowering. Previously, all comparisons used a ripple-carry style implementation that processed bits sequentially from LSB to MSB, resulting in O(n) depth for n-bit comparisons. This was a significant performance bottleneck for wide comparisons. The comparison lowering is now refactored to use the same parallel-prefix tree algorithms as the adder, reducing depth to O(log n). The comparison logic is formulated as a prefix computation where equal bits are computed as ~(a_i ^ b_i) and greater bits as ~a_i & b_i, with propagate and generate signals based on equality and greater-than conditions. Integration tests for all architectures verify logical equivalence via circt-lec.
1 parent de8566f commit ba41ee0

File tree

3 files changed

+460
-282
lines changed

3 files changed

+460
-282
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// REQUIRES: libz3
2+
// REQUIRES: circt-lec-jit
3+
4+
// RUN: circt-opt %s --convert-comb-to-synth --convert-synth-to-comb -o %t.mlir
5+
6+
// RUN: circt-lec %t.mlir %s -c1=icmp_unsigned_ripple_carry -c2=icmp_unsigned_ripple_carry --shared-libs=%libz3 | FileCheck %s --check-prefix=COMB_ICMP_UNSIGNED_RIPPLE_CARRY
7+
// COMB_ICMP_UNSIGNED_RIPPLE_CARRY: c1 == c2
8+
hw.module @icmp_unsigned_ripple_carry(in %lhs: i3, in %rhs: i3, out out_ugt: i1, out out_uge: i1, out out_ult: i1, out out_ule: i1) {
9+
%ugt = comb.icmp ugt %lhs, %rhs {synth.test.arch = "RIPPLE-CARRY"} : i3
10+
%uge = comb.icmp uge %lhs, %rhs {synth.test.arch = "RIPPLE-CARRY"} : i3
11+
%ult = comb.icmp ult %lhs, %rhs {synth.test.arch = "RIPPLE-CARRY"} : i3
12+
%ule = comb.icmp ule %lhs, %rhs {synth.test.arch = "RIPPLE-CARRY"} : i3
13+
hw.output %ugt, %uge, %ult, %ule : i1, i1, i1, i1
14+
}
15+
16+
// RUN: circt-lec %t.mlir %s -c1=icmp_unsigned_sklanskey -c2=icmp_unsigned_sklanskey --shared-libs=%libz3 | FileCheck %s --check-prefix=COMB_ICMP_UNSIGNED_SKLANSKEY
17+
// COMB_ICMP_UNSIGNED_SKLANSKEY: c1 == c2
18+
hw.module @icmp_unsigned_sklanskey(in %lhs: i3, in %rhs: i3, out out_ugt: i1, out out_uge: i1, out out_ult: i1, out out_ule: i1) {
19+
%ugt = comb.icmp ugt %lhs, %rhs {synth.test.arch = "SKLANSKEY"} : i3
20+
%uge = comb.icmp uge %lhs, %rhs {synth.test.arch = "SKLANSKEY"} : i3
21+
%ult = comb.icmp ult %lhs, %rhs {synth.test.arch = "SKLANSKEY"} : i3
22+
%ule = comb.icmp ule %lhs, %rhs {synth.test.arch = "SKLANSKEY"} : i3
23+
hw.output %ugt, %uge, %ult, %ule : i1, i1, i1, i1
24+
}
25+
26+
// RUN: circt-lec %t.mlir %s -c1=icmp_unsigned_kogge_stone -c2=icmp_unsigned_kogge_stone --shared-libs=%libz3 | FileCheck %s --check-prefix=COMB_ICMP_UNSIGNED_KOGGE_STONE
27+
// COMB_ICMP_UNSIGNED_KOGGE_STONE: c1 == c2
28+
hw.module @icmp_unsigned_kogge_stone(in %lhs: i3, in %rhs: i3, out out_ugt: i1, out out_uge: i1, out out_ult: i1, out out_ule: i1) {
29+
%ugt = comb.icmp ugt %lhs, %rhs {synth.test.arch = "KOGGE-STONE"} : i3
30+
%uge = comb.icmp uge %lhs, %rhs {synth.test.arch = "KOGGE-STONE"} : i3
31+
%ult = comb.icmp ult %lhs, %rhs {synth.test.arch = "KOGGE-STONE"} : i3
32+
%ule = comb.icmp ule %lhs, %rhs {synth.test.arch = "KOGGE-STONE"} : i3
33+
hw.output %ugt, %uge, %ult, %ule : i1, i1, i1, i1
34+
}
35+
36+
// RUN: circt-lec %t.mlir %s -c1=icmp_unsigned_brent_kung -c2=icmp_unsigned_brent_kung --shared-libs=%libz3 | FileCheck %s --check-prefix=COMB_ICMP_UNSIGNED_BRENT_KUNG
37+
// COMB_ICMP_UNSIGNED_BRENT_KUNG: c1 == c2
38+
hw.module @icmp_unsigned_brent_kung(in %lhs: i4, in %rhs: i4, out out_ugt: i1, out out_uge: i1, out out_ult: i1, out out_ule: i1) {
39+
%ugt = comb.icmp ugt %lhs, %rhs {synth.test.arch = "BRENT-KUNG"} : i4
40+
%uge = comb.icmp uge %lhs, %rhs {synth.test.arch = "BRENT-KUNG"} : i4
41+
%ult = comb.icmp ult %lhs, %rhs {synth.test.arch = "BRENT-KUNG"} : i4
42+
%ule = comb.icmp ule %lhs, %rhs {synth.test.arch = "BRENT-KUNG"} : i4
43+
hw.output %ugt, %uge, %ult, %ule : i1, i1, i1, i1
44+
}
45+
46+
// RUN: circt-lec %t.mlir %s -c1=icmp_signed -c2=icmp_signed --shared-libs=%libz3 | FileCheck %s --check-prefix=COMB_ICMP_SIGNED
47+
// COMB_ICMP_SIGNED: c1 == c2
48+
hw.module @icmp_signed(in %lhs: i4, in %rhs: i4, out out_ugt: i1, out out_uge: i1, out out_ult: i1, out out_ule: i1) {
49+
// Sign comparisons are just unsigned comparisons with inverted inputs and outputs.
50+
// No need to test all architectures here.
51+
%ugt = comb.icmp sgt %lhs, %rhs {synth.test.arch = "RIPPLE-CARRY"} : i4
52+
%uge = comb.icmp sge %lhs, %rhs {synth.test.arch = "SKLANSKEY"} : i4
53+
%ult = comb.icmp slt %lhs, %rhs {synth.test.arch = "KOGGE-STONE"} : i4
54+
%ule = comb.icmp sle %lhs, %rhs {synth.test.arch = "BRENT-KUNG"} : i4
55+
hw.output %ugt, %uge, %ult, %ule : i1, i1, i1, i1
56+
}

0 commit comments

Comments
 (0)