w6c+selfhost: localloadop helper for sign-aware ident loads (closes #19)
Read-side fix dual to fldloadop: signed-narrow local/global ident loads now MOVSXD/MOVSWQ/MOVSBQ from the slot instead of raw MOVQ. Deref-stores (MOVL/MOVW/MOVB) no longer corrupt downstream i64 widens. Compound RMW restructured to gate direct-mem ADDQ/SUBQ on load_op == MOVQ. Top-level lets use LEAQ+indirect (w6a doesn't expose MOVSXD/MOVSWQ/MOVSBQ for D_EXTERN). dotchainresolve out-params restored to natural *i32 (workaround retired). selfhost/CLAUDE.md graduated.
This commit is contained in:
@@ -8,8 +8,6 @@ The C bootstrap's cgen has known silent-miscompilation traps. They produce wrong
|
||||
|
||||
1. **`amalloc(n)` with n < struct size silently corrupts neighbours.** No error — the bump arena hands out n bytes and field writes overflow into the next record. When introducing or growing a struct, audit every `amalloc(_, n)` call site and over-size (we routinely pass 48 for a 40-byte struct). Symptom: linked-list prepends lose all but the most recent entry.
|
||||
|
||||
2. **Out-param `*p: *i32` deref-stores leave caller slot's upper 4 bytes stale.** `*p = v` lowers to MOVL (4B), but the caller's 8B slot was zero-init via MOVQ; a later i64-widening read emits MOVQ (8B raw) and zero-extends, so a negative i32 round-trips as a 4G-rooted positive i64. Default to `*i64` out-params for offsets / signed indices until task #19 lands. Symptom: garbage frame offsets like `MOVL AX, 4294967280(BP)` in stage-2 asm.
|
||||
|
||||
Fixed (no workaround needed):
|
||||
|
||||
- `def NAME: str = "..."` field access. `.len`/`.ptr` on an Sdef ident
|
||||
@@ -74,5 +72,22 @@ Fixed (no workaround needed):
|
||||
follow the per-index-write contract and stay uninit. See
|
||||
cmd/w6c/cgen.c N_LET (else branch, sz > 8 && !TY_ARRAY) and
|
||||
selfhost/cmd/wcc/cgenstmt.ww cglet no-rhs branch.
|
||||
- Signed-narrow scalar reads sign-extend honestly. `*p: *i32 = v`
|
||||
stores 4 bytes (MOVL — correct for a 4B pointee), and the read
|
||||
side now emits MOVSXD/MOVSWQ/MOVSBQ for any signed-narrow local
|
||||
or let load instead of a raw MOVQ. The natural N_ASSIGN / N_LET
|
||||
store paths happened to leave the slot sign-extended so MOVQ
|
||||
reads accidentally worked; deref-stores were the only path that
|
||||
touched fewer bytes than MOVQ reads, so negative i32 values
|
||||
round-tripped as 4G-rooted positive i64s. Cgen now routes
|
||||
scalar reads through `localloadop(t)`; aliases (`type err = !i32`)
|
||||
resolve the size through TBANG / TNAME / TENUM. See cmd/w6c/cgen.c
|
||||
N_IDENT/N_DOT mod.name catch-alls, cgassign compound RMW for
|
||||
locals + top-level lets, and the matching selfhost cgenexpr.ww
|
||||
cgident / cgassign / module-qualified branches plus
|
||||
cgenutil.ww `localloadop`. Top-level lets and `mod.name` go
|
||||
through a LEAQ + indirect load since w6a has no MOVSXD/MOVSWQ/
|
||||
MOVSBQ D_EXTERN form. dotchainresolve's out-params are back at
|
||||
their natural i32 width.
|
||||
|
||||
If a port "should work" but the binary is wrong, suspect these first.
|
||||
|
||||
Reference in New Issue
Block a user