You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
gen-zig/gen-verilog: CSE hoists an expression over a reassigned local above its declaration, printed as ID:/LIT: debug text #7703
Part of the self-host epic #5980. Following AGENTS.md "Only t27", what gen cannot express is a compiler defect and is filed here.
Goal
A repeated expression over a reassigned local must not be hoisted by CSE in gen-zig and gen-verilog.
Where we are
Found while writing specs/fpga/eth_beacon.t27 (#6655 Ethernet bring-up). Minimal repro:
module CseRepro;
fn fold(x: u32) -> u32 {
var s : u32 = x;
s = (s & 65535) + (s >> 16);
s = (s & 65535) + (s >> 16);
return s;
}
test fold_carries_twice {
assert_eq(fold(131071), 1);
}
t27c gen (t27c 0.5.0, tag t27c-v0.5.0, cb9df57) emits:
fn fold(x: u32) u32 {
const _cse1 = s & 65535 + ID:s >> LIT:16;
var s: u32 = x;
_ = &s;
s = (s & 65535) + (s >> 16);
s = _cse1;
return s;
}
gen-verilog emits the same: _cse1 = (s & 65535 + ID:s >> LIT:16);. gen-c is unaffected.
There are three defects in one:
The CSE temp is hoisted above the declaration of the variable it reads.
It treats the two textually equal right-hand sides as one value, although s is reassigned between them. The second fold would reuse the first's result: wrong semantics, silent if the text were valid.
The temp is printed in the internal debug form (ID:s, LIT:16), so today the output does not compile. That is the only reason the defect is loud.
Scope note
The CSE pass shared by gen-zig and gen-verilog, plus a regression test.
User Scenarios & Testing
Given the repro, when t27c gen / gen-verilog run, then no _cse temp reads s, and zig test passes with fold(131071) == 1.
Requirements
FR-1: an expression that reads a variable assigned between its occurrences is not a common subexpression.
FR-2: a hoisted temp is never emitted before the declarations it reads.
FR-3: no ID: / LIT: debug text reaches any backend's output.
Success Criteria
t27c gen cse_repro.t27 > r.zig && zig test r.zig passes.
t27c gen-verilog cse_repro.t27 | grep -c 'ID:' prints 0.
Workaround used in eth_beacon.t27: immutable intermediates (const a, const b, const c) instead of reassigning one var.
Fixed by #8991 (merged as 122c3ed). The CSE legality rule now lives in specs/compiler/cse_hoist.t27: a repeat gets a temp only if none of its operands is assigned, address-taken, declared, captured or exposed to a call between the first occurrence and the repeat. The temp is placed right before its use, and gen-zig emits statements in order. Regression cases are in specs/compiler/codegen_conformance.t27 (7/7).
Activity
optimizer: a repeated expression takes a temp only while its operands…
gHashTag commentedon Oct 11, 2026
Fixed by #8991 (merged as 122c3ed). The CSE legality rule now lives in
specs/compiler/cse_hoist.t27: a repeat gets a temp only if none of its operands is assigned, address-taken, declared, captured or exposed to a call between the first occurrence and the repeat. The temp is placed right before its use, and gen-zig emits statements in order. Regression cases are inspecs/compiler/codegen_conformance.t27(7/7).