// glob_ptr_field_test — a scalar field store through a module-GLOBAL `*struct` // pointer (`gp.f = v`) must load the pointer value from gp(SB), not deref the // saved-BP word, migrated from test/wcc/989_globptrfield_run.c (inverse-set // #47, report-item [58]). cstage's N_DOT-lhs via_ptr scalar store loaded the // base with `MOVQ boff(BP),BX`; for a module-global base boff==0, so it emitted // `MOVQ (BP),BX` — derefing saved BP as the pointer → store through garbage → // SEGV. wwstage was already correct (LEAQ gp(SB),BX; MOVQ (BX),BX); the cstage // half aligned UP and the .s is byte-identical, so a behavioral @test is the // net. backing is a fresh (zeroed) global; a store that lands in garbage never // writes the symbol, so a read-back of backing's field catches the miss. The // static-init form `let gp: *S = &backing;` is #48-blocked, so the runtime // `gp = &backing` form is used (as in the C twin). package glob_ptr_field_test; type S = struct { f: i64, g: i64 }; let backing: S = S { f = 0, g = 0 }; let gp: *S = nil; @test fn store_off0() void = { // the exact #58 repro: field at offset 0 through the global pointer. gp = &backing; gp.f = 7; assert(backing.f: i32 == 7); }; @test fn store_off8() void = { // #8 (rule-10): a NON-zero-offset field (g at +8) through the global // pointer. wwstage folded the offset into BX (`ADDQ $8,BX` + plain // `MOVQ AX,(BX)`) while cstage emitted the displacement store // `MOVQ AX,8(BX)` — a cs!=ww .s divergence T2 catches. Offset-0 rows // CANNOT pin this (`ADDQ $0` is suppressed). backing.g is poisoned // non-zero first, then overwritten via gp.g, so a store that lands at // the wrong displacement is caught on read-back. gp = &backing; backing.g = 11; gp.g = 7; assert(backing.g: i32 == 7); };