cgen: sub-slice ptr = base + lo*esz (both stages, #76)

A sub-slice base[lo:hi] advanced its data pointer by lo (element
COUNT) instead of lo*esz (BYTES), so the base pointer was wrong for
any esz>1 element. Pointer arithmetic is membsz-unit per the rt
invariant (ref/hare/rt/ensure.ha:30); esz==1 (u8/str) is unchanged.

Four emission sites, fixed byte-identically across stages (rule 10):
  - value path:  cmd/w6c/cgen.c N_SLICE  <-> cgenexpr.ww cgslice
  - call-arg:    cmd/w6c/cgen.c:4646     <-> cgenutil.ww pushargsrev

Scaling mirrors the cgindex idiom: esz from the type table (rule 13;
cstage bu->sub->size, wwstage elemsizeofc) gated to an N_IDENT base,
uniform IMULQ (no SHL special-case, no immediate form -- w6a is
reg-reg only). The live lo reg is the multiplicand so the one free
GP (DX value / BX arg) holds esz*lo; lo is preserved for len (hi-lo)
and cap (base_cap-lo, #20). The esz==1 path keeps the single ADDQ,
byte-identical to before (#75/#20/str unaffected). Non-ident bases
stay unscaled in both stages (wwstage has no tnode there), tracked
as a #76 residual alongside #74.

New 943_subslice_ptresz_run: table-driven, dual-driver (ww/ww_ww),
esz in {2,4,8} array+slice base, lo>0, let-form + call-arg form;
asserts s[0]==base[lo] & s[1]==base[lo+1]. Fails on every fixture
pre-fix on both stages, passes post-fix. Registered in Makefile
(TESTS + target) so test/run builds and runs it.
This commit is contained in:
2026-05-25 02:26:15 +09:00
parent 8b23ff3517
commit ab9de65b8e
7 changed files with 397 additions and 19 deletions

View File

@@ -4590,6 +4590,13 @@ cgexpr(Cg *c, Node *n, Local *locals)
Type *bt = base ? base->type : NULL;
Type *bu = (bt && bt->kind == TY_NAMED) ?
bt->under : bt;
/* N_IDENT-gated: non-ident bases stay esz=1
* (unscaled), byte-id with wwstage which has no
* tnode there (rule 10) -- #76 residual, non-
* ident cluster #74. */
int esz = (base && base->kind == N_IDENT
&& bu && bu->sub)
? (int)bu->sub->size : 1;
/* base addr → push */
if (base->kind == N_IDENT) {
int boff = localfind(locals, base->str);
@@ -4642,8 +4649,16 @@ cgexpr(Cg *c, Node *n, Local *locals)
/* len = hi - lo (DX) */
ins2(c, A_MOVQ, areg(D_BX), areg(D_DX));
ins2(c, A_SUBQ, areg(D_AX), areg(D_DX));
/* ptr = base + lo */
ins2(c, A_ADDQ, areg(D_AX), areg(D_CX));
/* ptr = base + lo*esz (#76; ensure.ha:30
* membsz-unit). BX=lo*esz; AX=lo PRESERVED
* for cap. BX (dead hi) reloaded by cap below. */
if (esz > 1) {
ins2(c, A_MOVQ, aimm(esz), areg(D_BX));
ins2(c, A_IMULQ, areg(D_AX), areg(D_BX));
ins2(c, A_ADDQ, areg(D_BX), areg(D_CX));
} else {
ins2(c, A_ADDQ, areg(D_AX), areg(D_CX));
}
/* push cap, len, ptr (top). cap = base_cap - lo
* (#20); AX=lo, BX free. */
if (cg_base_cap(c, base, bu, locals, D_BX)) {
@@ -6361,17 +6376,22 @@ cgexpr(Cg *c, Node *n, Local *locals)
}
case N_SLICE: {
/* base[lo:hi] as a slice value. Leaves the triple in
* (AX=base+lo, BX=hi-lo, CX=base_cap-lo) so callers can
* (AX=base+lo*esz, BX=hi-lo, CX=base_cap-lo) so callers can
* route to a slice slot, return, or arg with the same ABI.
* cap is the storage remaining to the base's end (#20,
* Go/Hare-identical), via cg_base_cap. Element scaling on
* the ptr isn't wired (lo*esz is #76), so non-u8 slices
* need that follow-up before their ptr is correct. */
* Go/Hare-identical), via cg_base_cap. ptr advances by BYTES
* (lo*esz, #76; ref/hare/rt/ensure.ha:30 membsz-unit); esz
* from the type table, mirroring the N_INDEX idiom. */
Node *base = n->lhs;
Node *lo = n->rhs;
Node *hi = n->cond;
Type *bt = base ? base->type : NULL;
Type *bu = (bt && bt->kind == TY_NAMED) ? bt->under : bt;
/* N_IDENT-gated: non-ident bases stay esz=1 (unscaled),
* byte-id with wwstage which has no tnode there to resolve
* esz (rule 10) -- #76 residual, non-ident cluster #74. */
int esz = (base && base->kind == N_IDENT && bu && bu->sub)
? (int)bu->sub->size : 1;
if (base && base->kind == N_IDENT) {
int boff = localfind(locals, base->str);
int isglobal = (boff == 0) && let_islet(base->str);
@@ -6415,7 +6435,15 @@ cgexpr(Cg *c, Node *n, Local *locals)
ins2(c, A_MOVQ, areg(D_AX), areg(D_BX));
ins1(c, A_POPQ, areg(D_CX));
ins1(c, A_POPQ, areg(D_AX));
ins2(c, A_ADDQ, areg(D_CX), areg(D_AX));
/* ptr = base + lo*esz (#76; ensure.ha:30 membsz-unit).
* DX=lo*esz; CX=lo PRESERVED for len + cap (#20). */
if (esz > 1) {
ins2(c, A_MOVQ, aimm(esz), areg(D_DX));
ins2(c, A_IMULQ, areg(D_CX), areg(D_DX));
ins2(c, A_ADDQ, areg(D_DX), areg(D_AX));
} else {
ins2(c, A_ADDQ, areg(D_CX), areg(D_AX));
}
ins2(c, A_SUBQ, areg(D_CX), areg(D_BX));
/* cap = base_cap - lo (#20); CX=lo, BX=len here. */
if (cg_base_cap(c, base, bu, locals, D_DX)) {