opt_vps: fold uniform gathers over multi-bit elements - #295
Conversation
The uniform-gather fold only matched WIDTH==1 $bmux, so a table of anything wider than a bit -- a byte FIFO, a packed struct array -- had every lane skipped and left for bmuxmap/pmuxtree to expand into a one-hot $eq + mux farm per lane. A 32-lane window over a 128-entry byte table cost 1.86M AIG nodes that way, against 105K once folded. $bmux is entry-major, so a WIDTH=W cell is W independent gathers over stride-W slices of the table sharing one index. Register one candidate per element bit and the existing grouping, affine analysis and wrap guards carry over unchanged: W barrels over M entries each cost what one barrel over W*M bits would, so splitting the fold per bit is free. One cell now feeds W groups, so removal is deferred until every group has been emitted -- the later ones still read the cell for its index and src attribute. Dead output bits are skipped rather than folded. Co-authored-by: Cursor <cursoragent@cursor.com>
Greptile SummaryThe PR generalizes uniform-gather folding so wide
Confidence Score: 4/5The PR should not merge until partial per-bit group eligibility can no longer cause removal of a Wide Files Needing Attention: passes/silimate/opt_vps.cc
|
| Filename | Overview |
|---|---|
| passes/silimate/opt_vps.cc | Generalizes gather folding to wide $bmux cells, but whole-cell retirement can delete unmatched live output slices when only some per-bit groups emit. |
| tests/silimate/opt_vps.ys | Adds equivalence and cell-count tests for wide gathers, though both exercise fully foldable groups and miss partial per-bit group eligibility. |
| tests/silimate/opt_vps_gather_wide.sv | Provides a focused wide-element gather fixture with two affine-index banks and fully live four-bit outputs. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
B[Wide bmux] --> S[Create one strided slice per live output bit]
S --> G1[Slice group reaches min_gather]
S --> G2[Sibling group below min_gather]
G1 --> E[Reconnect emitted output bit]
E --> D[Mark entire bmux dead]
G2 --> K[Keep original output driver required]
D --> R[Remove entire bmux]
R --> U[Sibling live output becomes undriven]
Reviews (1): Last reviewed commit: "opt_vps: fold uniform gathers over multi..." | Re-trigger Greptile
| if (gather_dead.insert(c.cell).second) | ||
| pmux_replaced++; |
There was a problem hiding this comment.
When only some live output-bit groups from a wide $bmux meet min_gather, emitting one group adds the entire source cell to gather_dead, while reconnecting only that group's output bit. The later whole-cell removal therefore deletes the drivers for live sibling bits whose groups were skipped, changing those outputs.
Knowledge Base Used: Synthesis transformation pipeline
|
Closing as a duplicate of #294, which was opened ~20 minutes earlier and implements the same feature: extending the Beyond the duplication, the grouping key chosen here is not safe. The differenceFor a Why per-element-bit keying is unsafeTwo Under #294's whole- ReproductionFour module opt_vps_gather_partial (
input wire [1:0] idx,
input wire [3:0] shared,
input wire [3:0] priv0, priv1, priv2, priv3,
output wire [1:0] y0, y1, y2, y3
);
wire [3:0][1:0] t0, t1, t2, t3;
genvar e;
generate
for (e = 0; e < 4; e = e + 1) begin : entries
assign t0[e] = {priv0[e], shared[e]};
assign t1[e] = {priv1[e], shared[e]};
assign t2[e] = {priv2[e], shared[e]};
assign t3[e] = {priv3[e], shared[e]};
end
endgenerate
assign y0 = t0[2'(idx + 2'd0)];
assign y1 = t1[2'(idx + 2'd1)];
assign y2 = t2[2'(idx + 2'd2)];
assign y3 = t3[2'(idx + 2'd3)];
endmoduleOn this branch, all four cells are retired for the sake of the one bit-group that folded, and half the output bits are dropped:
On #294's code the same design folds nothing (four distinct Worth noting for #294's reviewers: this branch passes #294's full Not deleting |
Summary
The uniform-gather fold in
opt_vpsonly matched$bmuxcells withWIDTH == 1. Any table whose elements are wider than a bit — a byte FIFO staging window, a packed struct array — had every lane skipped, andbmuxmap -pmux+pmuxtreethen expanded each one into a one-hot$eq+ mux farm over the whole table.$bmuxis entry-major (Y = A[S*WIDTH +: WIDTH]), so aWIDTH=Wcell isWindependent gathers over stride-Wslices of the table, all sharing one index. Registering one candidate per element bit lets the existing grouping, affine analysis, wrap guards and both emitters carry over unchanged.Wbarrels overMentries each cost what one barrel overW*Mbits would, so splitting the fold per bit is free.Two details fall out of one cell feeding
Wgroups:srcattribute.Measurements
A 32-lane sliding window over a 128-entry byte table, two access sites (read window plus a scatter's read-back), through
bmuxmap -pmux; pmuxtree; techmap; aigmap:$bmux$shr17.8x fewer AIG cells. End to end in Preqorsor the same design went from 8m37s and 12,187 area to landing 14% under a hand-written barrel-shift reference.
Test plan
tests/silimate/opt_vps.ysgains two cases, on a newopt_vps_gather_wide.sv: a SAT self-equivalence proof before/after the pass, and cell counts pinning 16WIDTH=4$bmuxfolding to 8$shrwith none left, plus a-min_gather 0control proving the fold is what does it.tests/silimatesuite passes (mux_push.ysfails on unrelated in-flight work inmux_push.cc, and never invokesopt_vps).Made with Cursor