selfhost: graduate wwstage &N_DOT[N_INDEX] to cstage canonical lean form (#21)

Latent #21 has two surface shapes — register polarity in cgun
TK_AMP N_INDEX's complex-base arm, and indexbaseesz's
over-broad .ptr pseudo-field gate — that share a single semantic
path: &N_DOT[N_INDEX] where the inner N_DOT cannot be peeled
into a plain ident base. Polarity-A (cgenexpr.ww) lifted to
cstage's three-line shape; stride-B (cgenutil.ww) narrowed so
the .ptr arm only fires on actual str/slice inners and falls
through to the generic struct-field arm for struct N_TNAME
bases. The fixes compose at the same call site (esz from
indexbaseesz, then the IMULQ-or-elide gate, then complex-base
emit), so splitting them into two commits would leave a
half-fixed intermediate — neither half stands alone as a
bisect-clean closure. Sentinel 755_amp_dot_idx exercises both
shapes across 4 stride classes (slice-elem 24, struct-elem 16,
u8 stride-1 elide, i64 stride-8); pre-fix 5/12 fail, post-fix
12/12 ok. Latent silent miscompile in lib/memio + lib/bufio's
.ptr[i] shape also unmasked.
This commit is contained in:
2026-05-19 13:43:45 +09:00
parent f0b8c25b29
commit d5e8d699d1
6 changed files with 360 additions and 33 deletions

View File

@@ -2484,11 +2484,16 @@ fn cgun(c: *cgen, n: *node) void = {
};
} else {
// Complex base: spill scaled idx, eval
// base to AX, move to BX, restore idx.
// base to AX, restore idx into BX.
// Mirrors cstage's lean three-line shape
// (cmd/w6c/cgen.c TK_AMP N_INDEX complex
// base 2104-2107); the prior MOVQ AX, BX
// + POPQ AX scratch shuffle was rule-10
// verbose-defensive on the wwstage side
// with no semantic asymmetry (task #21).
emitline("\tPUSHQ\tAX\n");
cgexpr(c, base);
emitline("\tMOVQ\tAX, BX\n");
emitline("\tPOPQ\tAX\n");
emitline("\tPOPQ\tBX\n");
};};};
emitline("\tADDQ\tBX, AX\n");
return;