w6c+w6c_ww: (*p)[i] deref base materializes the array ADDRESS (#61 C)

Both stages SEGV'd identically (byte-id-blind): cgun's TK_STAR emitted
a scalar MOVQ (AX),AX for an array pointee, so the index consumed
a[0]'s VALUE as its base — a wild deref. An array value IS its address
everywhere in this cgen (#270-1a), so the ARRAY pointee now takes the
same skip as the #185 *fn deref in both stages: `*p` leaves AX = p's
value, and every consumer that materializes a complex index base via
cgexpr(base) — N_INDEX read fallback, cgassign store/compound, TK_AMP,
N_SLICE — gets the array address from the one deref choke-point.

wwstage additionally joins the N_UN-TK_STAR base to the stamped-tinfo
esz arms (cgindex / cgassign store + compound / TK_AMP &(*p)[i]) where
cstage reads base->type uniformly: without it, esz fell to the 8B
default and a narrow element would mis-stride the moment the base
started materializing (cs!=ww only reachable post-choke-point-fix,
which is why it rides this commit).

949_ptrarr_index_run grows the deref_* rows: read (8B/4B/param-base),
write (8B / 1B+neighbor-guards), compound — runtime + byte-id, the only
nets that can see a both-stages-identical miscompile.
This commit is contained in:
2026-06-04 22:08:11 +09:00
parent eea3e197c2
commit 33ec0fb1ac
5 changed files with 169 additions and 20 deletions

View File

@@ -32,6 +32,18 @@
* param / local / cast bases, read / write / compound routes,
* neighbor-corruption guards, the &p[i] pointer-difference (B), and
* the live consumer's mix-in-place shape (siphash round).
*
* C. `(*p)[i]` explicit deref + index — BOTH stages SEGV'd
* identically (byte-id-blind): cgun's TK_STAR materialized an
* 8-byte SCALAR load of a[0]'s value and the index used that
* VALUE as its base — a wild deref. Fixed in both stages at the
* deref choke-point: an ARRAY pointee takes the #185 *fn skip
* (an array value IS its address, #270-1a), so `*p` leaves AX =
* p's value and every index/addr-of/store route through
* `cgexpr(base)` materializes the array address for free. The
* deref_* rows pin read / write / compound at 8B and narrow
* widths (the narrow rows also pin the wwstage N_UN-base
* stamped-tinfo esz arm against the 8B default).
*/
#include <stdio.h>
#include <stdlib.h>
@@ -261,6 +273,60 @@ static const struct row rows[] = {
" if (v[3] != 11u64) { return 4; };\n"
" return 0;\n"
"};\n", 0 },
/* C: (*p)[i] read, 8B elem (ken p7 — SEGV'd both stages). */
{ "deref_rd_u64",
"package main;\n"
"export fn main() i32 = {\n"
" let a: [4]u64 = [100u64, 101u64, 102u64, 103u64];\n"
" let p: *[4]u64 = &a;\n"
" return (*p)[1]: i32;\n"
"};\n", 101 },
/* C: (*p)[i] read, 4B elem — pins the N_UN-base esz arm (the 8B
* default would mis-stride once the base materializes). */
{ "deref_rd_u32",
"package main;\n"
"export fn main() i32 = {\n"
" let a: [4]u32 = [11u32, 22u32, 33u32, 44u32];\n"
" let p: *[4]u32 = &a;\n"
" return (*p)[2]: i32;\n"
"};\n", 33 },
/* C: (*p)[i] read through a param base. */
{ "deref_rd_param",
"package main;\n"
"fn rd(p: *[4]u64) u64 = {\n"
" return (*p)[1];\n"
"};\n"
"export fn main() i32 = {\n"
" let a: [4]u64 = [100u64, 101u64, 102u64, 103u64];\n"
" return rd(&a): i32;\n"
"};\n", 101 },
/* C: (*p)[i] write, 8B elem. */
{ "deref_wr_u64",
"package main;\n"
"export fn main() i32 = {\n"
" let a: [4]u64 = [1u64, 2u64, 3u64, 4u64];\n"
" let p: *[4]u64 = &a;\n"
" (*p)[1] = 7u64;\n"
" return (a[0] + a[1] + a[2]): i32;\n"
"};\n", 11 },
/* C: (*p)[i] write, 1B elem, neighbor guards. */
{ "deref_wr_u8",
"package main;\n"
"export fn main() i32 = {\n"
" let a: [4]u8 = [1u8, 2u8, 3u8, 4u8];\n"
" let p: *[4]u8 = &a;\n"
" (*p)[1] = 9u8;\n"
" return (a[0] + a[1] + a[2]): i32;\n"
"};\n", 13 },
/* C: (*p)[i] compound. */
{ "deref_compound_u64",
"package main;\n"
"export fn main() i32 = {\n"
" let a: [4]u64 = [100u64, 101u64, 102u64, 103u64];\n"
" let p: *[4]u64 = &a;\n"
" (*p)[1] += 5u64;\n"
" return a[1]: i32;\n"
"};\n", 106 },
{ NULL, NULL, 0 }
};