cstage+test: skip redundant MOVL on u32 → enum-u32 cast (#25)

cstage's N_CAST narrow-clamp emitted `MOVL AX, AX` on u32 → enum-u32
casts. wwstage's cgcast walker steps through N_TBANG / N_TNAME alias
links only; an enum's aliaslookup returns the N_TENUM body, which
breaks the loop and skips the clamp. The asymmetry surfaced during
#10 (lib/os/stat) when kstat.mode typed as u32 tripped the cross-
stage diff; worker-stat sidestepped by typing kstat.mode as `mode`.

Single-site gate: skip the narrow-clamp when the destination type is
TY_ENUM. Mirrors wwstage's N_TENUM lacuna exactly. Predicate
recursion (type_isint, type_isunsigned) over TY_ENUM stays intact —
this is emit-side only.

Wwstage unchanged.

Test 710 (cast_enum_movl): 5 rows × {exit-code per driver, asm
byte-id when w6c_ww built} = 10+ assertions. u32→enum-u32 headline,
enum-u32→u32 reverse (pins direction-of-asymmetry), u32→enum-u8
different-width, i64→enum-i32 signed-narrow, struct-field rt mirror
of `out.mode = k.mode` (the #10 trip-wire).

A principled identity-width identity-sign skip across both stages is
filed as #33. The lib/os.ww kstat.mode workaround revert is a
sibling cleanup, not in #25 scope.
This commit is contained in:
2026-05-16 09:53:58 +09:00
parent 1292f98c91
commit b5632b1fbe
3 changed files with 326 additions and 1 deletions

View File

@@ -4793,8 +4793,20 @@ cgexpr(Cg *c, Node *n, Local *locals)
if (!from_f && !to_f && n->type) {
Type *tt = n->type;
Type *tu = (tt && tt->kind == TY_NAMED) ? tt->under : tt;
/* Wwstage's cgcast walker only steps through N_TBANG /
* N_TNAME alias links; an enum's `aliaslookup` returns
* the N_TENUM body, which breaks the loop and skips the
* clamp. Mirror that here so cstage agrees byte-for-byte
* on a u32→enum-u32 cast (task #25). Predicate recursion
* through TY_ENUM in `type_isint`/`type_isunsigned`
* stays — other call sites rely on it; only this site
* gates on TY_ENUM explicitly. Skip is by dst kind only:
* `enum-u32 → u32` still emits the clamp, matching
* wwstage's asymmetry. (See followup: identity-width
* identity-sign hygiene across both stages.) */
int dst_is_enum = tu && tu->kind == TY_ENUM;
if (tu && type_isint(tu) && tu->size > 0
&& tu->size < 8) {
&& tu->size < 8 && !dst_is_enum) {
if (type_isunsigned(tu)) {
if (tu->size == 4) {
ins2(c, A_MOVL,