Three sibling arms of the #10 global-str/slice INDEX miscompile (23670d7,
the READ path) shared the identical N_TARRAY/N_TPTR tnode-KIND whitelist in
their global-ident resolution arm and were still LIVE and silently cs!=ww:
- cgun `&s[1]` / `&g[1]` (cgenexpr.ww N_INDEX addr-of) — a global str
(tnode N_TNAME) / slice (N_TSLICE) matched neither arm, so esz stayed at
the default 8 and the base fell to the complex-base fallback: a wide
{ptr,len,cap} header + 8-byte stride instead of MOVQ name(SB) (.ptr) +
ADDQ.
- cgassign `g[1] = v` store AND `g[1] OP= v` compound (two arms) — same
whitelist; a global slice store emitted a full-word MOVQ at an 8-byte
stride: an 8-BYTE OUT-OF-BOUNDS WRITE past a 1-byte element (memory
corruption) instead of MOVB at .ptr+1.
cstage (cmd/w6c/cgen.c) is the runtime-correct reference and was already
uniform across all three: esz off idx_eff(base->type)->sub->size and the
base load gated by is_arr (TY_ARRAY -> LEAQ name(SB), every other -> MOVQ
name(SB), since a str/slice's .ptr IS the symbol's first word). Align the
wwstage UP to that, mirroring the just-landed cgindex template (#10): resolve
esz via elemsizeofc with no kind gate, dispatch the base by N_TARRAY ? LEAQ :
MOVQ name(SB). The store/compound arms also resolve elemtn exactly like their
local branch (element node for ARRAY/SLICE/PTR; nil for str so tnodestoreop
picks MOVB) so a global []str store routes to the 3-word header store and the
compound arm's str/slice hard-error still fires.
Close-by-construction: the global element base/stride is now computed off the
resolved type at every wwstage index site — read (cgindex, #10), addr-of
(cgun), store + compound (cgassign) — with no remaining tnode-kind whitelist.
cgslice/cgbaselen already resolved via elemsizeofc.
803_globalidx_run extends from 9 to 18 rows: global str/slice addr-of (read
back through the pointer), global slice store AND compound store `g[i] OP= v`
(the distinct third fixed arm, with adjacent-element addends as the OOB-write
guard on both), a WIDTH>1 signed variant of each (esz=4 stride/store-width pin),
and local addr-of/store regression pins. Runtime (cstage build+run) + cs==ww
byte-id per row. combined.ww embeds (w6c + wwdump) regenerate.
Indexing a GLOBAL `str` or GLOBAL slice (`s[i]` / `g[i]` where s/g are
module-level lets) read a wide {ptr,len,cap} header with an 8-byte stride
and a full-word MOVQ load instead of the .ptr + element-width load. So
`s[1]` over a global str read 8 bytes at ptr+8 rather than the single byte
at ptr+1 (cstage emits MOVZBQ). LOCAL str/slice index was already clean.
Root: wwstage cgindex (selfhost/cmd/wcc/cgenexpr.ww) dispatched the element
size + base-materialisation off the base tnode KIND, enumerating only
N_TARRAY (global `[N]T`) and N_TPTR (global `*T`). A global str (tnode
N_TNAME "str") and a global slice (N_TSLICE) matched NEITHER arm, so esz
stayed at the default 8 and the base fell through to the wide-header
fallback. cstage `case N_INDEX:` (cmd/w6c/cgen.c) dispatches esz off the
RESOLVED base type (`idx_eff(lhs->type)->sub->size`), uniform across
local/global/str/slice/ptr.
Fix aligns cgindex's global-resolution arm UP to cstage's uniform type-
driven dispatch — the same template the sister fn cgslice already uses:
resolve esz via elemsizeofc(c, tn) with no kind gate, then drive the base
load by tn.kind == N_TARRAY ? LEAQ : MOVQ name(SB). A global str/slice now
resolves esz=1 off the type table (elemsizeofc, just fixed in #8 to read
stamped tinfo) and routes through the EXISTING isglobalptr emission
(MOVQ name(SB),BX; ADDQ; MOVZBQ (BX),AX) — byte-identical to cstage. The
element-kind flags (elemisstr/elemisslice) for a global `[]str`/`[][]u8`
element are still set by the downstream block, so those route to cgslicehdr
unchanged.
Close-by-construction: cgindex's one global-ident resolution arm is the
single site computing a global element base for the read-index path (the
&arr[i] address-of in cgun and the arr[i]=v store in cgassign are separate
node paths, out of scope). Any indexable global base now resolves esz off
the type table, exactly like cstage and like cgslice.
combined.ww embeds regenerate (w6c + wwdump). New 803_globalidx_run pins
runtime (cstage build+run) + cs==ww byte-id across global str index
(positions 0/1/2 + sum), global slice index (TEXT-only byte-id — a bare
`let g: []u8;` decl emits a divergent zero-header DATAW orthogonal to the
index read, the #7/#18 static-init family), and local str/slice/array
index regression pins. A stride-8 regression re-fails the 5 global rows.