wwstage: fold chained global-ptr field READ to cstage offset-fold (#16)

wwstage's chained-N_DOT resolver (dotchainresolve) didn't resolve a global
*struct root (only local *T and global value-struct), so gp.sf.len / gp.x.y
bailed to an inner-dot load + shuffle, byte-diverging from cstage's offset-fold.
Both stages already ran correct after #15 (475c003) -- a pure rule-10 asm
divergence. cstage is untouched (the oracle); wwstage aligns up.

Resolve a global N_TPTR root, and extract emitchainbase for the viacx base-load
(byte-identical across the 5 read + 2 store sites it replaces). The chained
STORE caller declines the global-ptr root (yok=false) so it falls to cstage's
address-spine mirror -- matching the #6/#15 decline-to-resolver discipline;
local *T chained stores still fold.

Test: +2 chained rows (gp.sf.len, gp.x.q), runtime + byte-id; proven to fail
byte-id with only the compiler files reverted, pass with the fix.

Sibling follow-ups filed: #17 (>32B tagged word-order), #18 (chained read into
an i64 sink MOVSXD check).
This commit is contained in:
2026-06-23 07:25:33 +09:00
parent 475c003b0d
commit ab3ac67afd
3 changed files with 95 additions and 67 deletions

View File

@@ -29,14 +29,19 @@
* gp.len (*[]u8 pseudo-field) | 3
* lp.f (LOCAL ptr control) | 42
* bump param p.f (param control) | 10
* gp.sf.len (CHAINED str leaf) | 3 (#16)
* gp.x.q (CHAINED struct leaf)| 7 (#16)
* Each row also asserts w6c .s == w6c_ww .s byte-identical.
* Pre-fix: cstage SEGV(139) on every global-ptr row; wwstage ran wrong;
* cs vs ww .s differed.
*
* SCOPE: single-dot field reads only. Chained-through-global-ptr-root
* (`gp.x.y`, `gp.sf.len` inline) is a sibling — both stages run correct
* after this fix, but the chained-N_DOT spines still diverge in idiom
* (cstage offset-folds; wwstage inner-loads + shuffles), filed separately.
* #16 (chained-through-global-ptr-root, the last two rows): after #15
* both stages RAN correct but byte-DIVERGED on the chained-N_DOT spine —
* cstage offset-folds (LEAQ name(SB),CX; MOVQ (CX),CX; MOVQ <folded>(CX))
* while wwstage's chained spine bailed (dotchainresolve missed a global
* `*struct` root) onto the naive inner-load-and-shuffle catch-all. Fix
* aligns wwstage UP: dotchainresolve resolves a global `*struct` root as
* isglobal && ptrroot and the spine emits the same offset-fold (rule 10).
*/
#include <stdio.h>
#include <stdlib.h>
@@ -106,6 +111,24 @@ static const struct row ROWS[] = {
"type S = struct { f: i64, g: i64 };\n"
"fn rd(p: *S) i32 = { return (p.f + p.g): i32; };\n"
"export fn main() i32 = { let s: S = S{f=4,g=6}; return rd(&s); };\n", 10 },
/* #16: CHAINED read through a global-`*struct` root. Pre-fix BOTH stages
* ran correct after #15 but byte-DIVERGED — cstage offset-folds (LEAQ
* name(SB),CX; MOVQ (CX),CX; MOVQ <folded>(CX)) while wwstage's chained
* spine bailed (dotchainresolve missed a global `*struct` root) onto the
* naive inner-load-and-shuffle catch-all. */
{ "chain_str_len",
"package main;\n"
"type S = struct { a: i64, sf: str };\n"
"let gp: *S = nil;\n"
"export fn main() i32 = { let s: S = S{a=0,sf=\"abc\"}; gp = &s;"
" return gp.sf.len: i32; };\n", 3 },
{ "chain_struct_field",
"package main;\n"
"type Inner = struct { p: i64, q: i64 };\n"
"type S = struct { a: i64, x: Inner };\n"
"let gp: *S = nil;\n"
"export fn main() i32 = { let s: S = S{a=0,x=Inner{p=0,q=0}}; gp = &s;"
" s.x.q = 7; return gp.x.q: i32; };\n", 7 },
};
#define NROWS ((int)(sizeof ROWS / sizeof ROWS[0]))