Commit Graph

2 Commits

Author SHA1 Message Date
2fb594748c cstage+selfhost+test: principled identity-cast skip (#33)
Generalizes b5632b1's single-site dst_is_enum gate. Skip the narrow-
clamp MOVL when src.width == dst.width && src.signed == dst.signed.
Closes #25's followup.

Both stages need symmetric source-type derivation for byte-id. cstage
deliberately throws away the checker's richer typed-AST and uses a
structural walker (castsrcprim) that mirrors wwstage's exprprimresolved
case-for-case. Otherwise cstage's `.len: i32` resolves to i32 (skip)
while wwstage's misses the pseudo-field (clamp) — bootstrap diverges.
Pseudo-fields, N_BIN, N_INDEX, N_CALL, match-bindings all yield sz=0
→ clamp emits defensively on both.

The N_TENUM walker now follows enum aliases in wwstage's
typenodeprimresolved (was the original lacuna behind #25), and bool
is excluded early in the same helper (mirrors cstage's
type_isint(TY_BOOL)=false). bool→bool keeps its dedicated is_bool
ANDQ $255 emit; bool→i8 / bool→u8 etc. fall through to the clamp on
both stages.

Walker shape (cstage castsrcprim / wwstage exprprimresolved):
  N_INTLIT     → tsuffix gated, untyped excluded
  N_IDENT      → trust local's resolved tnode
  N_CAST       → recurse on declared dst
  N_UN         → recurse on operand
  N_DOT        → real-struct only (TY_STRUCT or TY_PTR→TY_STRUCT)
  others       → sz=0 → identity false → clamp emits

Test 710 grew from 5 → 16 rows: 6 identity-width pins (u32/i32/u8/
i8/u16/i16 self), 1 sign-change pin (u32→i32 clamp MUST fire), 2
silent-miscompile exit-validating rows (truncate via divide), 1
pseudo-field defensive pin (`s.len: i32`), 1 bool-source pin
(`b: i8`). Asm byte-id asserted on every row.

Out of scope: redundant clamps remain for patterns wwstage can't
structurally derive (N_BIN, N_CALL, N_INDEX, pseudo-fields). A
sibling task extending wwstage's type inference closes those.
2026-05-16 12:20:56 +09:00
b5632b1fbe 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.
2026-05-16 09:53:58 +09:00