cgen: sub-slice cap = base_cap - lo
A sub-slice `base[lo:hi]` now sets cap to base_cap - lo (the storage remaining to the underlying end; Go/Hare-identical) instead of hi - lo (== len). base_cap is the array length N for [N]T, or the .capacity word carried in a slice/str header at +16. Authored once per stage in the cg_base_cap / cgbasecap helper, applied at both cap sites: the N_SLICE value path (which serves let-init since the prior commit) and the call-arg push. Both stages stay byte-identical (find-4 closed). cap arithmetic per ref/harec/src/eval.c:1017 (slice: slice.cap -= start) and eval.c:1024 (array: cap = array.length - start); capacity is a distinct field per ref/hare/rt/ensure.ha:4-8 and cap >= len per ref/harec/src/check.c:596. Only the cap arithmetic transfers: the ptr stays unscaled (lo*esz is #76) and eval.c's stricter start>=end bound is not ported (ww's runtime bound is start>end). str[lo:hi] yields str with a real .capacity (D1), so the str base uses the same +16 load -- no downgrade to []u8. base_cap falls back to len (prior behavior) where it isn't cleanly available: a non-ident base (its header cap was discarded by cgexpr; len is likewise wrong for a defaulted hi there, pre-existing) and a global str base (wwstage cgslice has no global-str load, #73 -- the carve-out keeps both stages byte-identical). Test: 942_subslice_cap_run, table-driven over both drivers, array / slice / str base + an append-no-realloc row, each shape chosen so base_cap-lo != hi-lo. Fold in three pre-existing fixtures that asserted the old cap == len and so failed under the corrected semantics (project #20): 681_arr_elem_field_write (slice_field_value_write, slice_field_ptr_write, slice_field_distinct_bytes), 693_dot_tagged_source (local_struct_slice_variant, via_ptr_slice_variant, letinit_slice_roundtrip, top_level_global_slice), and 695_match_bind_struct (slice_neg_control). Each cap word updated to base_cap - lo: a [8]u8 base sliced at lo=0 yields cap 8 (5->8, 3->8); distinct_bytes slices a [16]u8 at lo=0, yielding cap 16 (6->16). len / mark / ptr assertions are unchanged -- only the cap word moved.
This commit is contained in:
@@ -1155,6 +1155,44 @@ localfind(Local *head, const char *name)
|
||||
return 0; /* 0 = not found (caller must verify) */
|
||||
}
|
||||
|
||||
/* cg_base_cap — load the capacity of a sub-slice's UNDERLYING storage
|
||||
* into `dst` for the #20 cap = base_cap - lo formula (drew: harec
|
||||
* eval.c:1017 slice cap-=start / eval.c:1024 array cap=length-start;
|
||||
* ensure.ha:4-8 distinct capacity field). array [N]T -> N (literal);
|
||||
* slice/str -> the .capacity word carried in the header at +16 (the
|
||||
* +16 load mirrors the hi-default +8 length dispatch, but emitted
|
||||
* unconditionally). Returns 0 when base_cap isn't cleanly available so
|
||||
* the caller keeps the prior cap=len: a non-ident base (cgexpr already
|
||||
* discarded its header cap; recomputing would re-evaluate a possibly
|
||||
* side-effecting base -- #74, which also owns the pre-existing
|
||||
* defaulted-hi len gap there), or a GLOBAL str base (wwstage cgslice
|
||||
* has no global-str load, #73 -- matching it keeps the stages
|
||||
* byte-identical rather than introducing a fresh divergence). */
|
||||
static int
|
||||
cg_base_cap(Cg *c, Node *base, Type *bu, Local *locals, int dst)
|
||||
{
|
||||
if (!base || base->kind != N_IDENT)
|
||||
return 0;
|
||||
if (bu && bu->kind == TY_ARRAY) {
|
||||
ins2(c, A_MOVQ, aimm((long long)bu->alen), areg(dst));
|
||||
return 1;
|
||||
}
|
||||
if (bu && (bu->kind == TY_SLICE || bu->kind == TY_STR)) {
|
||||
int boff = localfind(locals, base->str);
|
||||
int isglobal = (boff == 0) && let_islet(base->str);
|
||||
if (isglobal && bu->kind == TY_STR)
|
||||
return 0;
|
||||
if (isglobal) {
|
||||
ins2(c, A_LEAQ, masym(c, base->str), areg(dst));
|
||||
ins2(c, A_MOVQ, amem(dst, 16), areg(dst));
|
||||
} else {
|
||||
ins2(c, A_MOVQ, amem(D_BP, boff + 16), areg(dst));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* expressions: result lands in AX. Returns 1 on success. */
|
||||
|
||||
@@ -4606,8 +4644,14 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
ins2(c, A_SUBQ, areg(D_AX), areg(D_DX));
|
||||
/* ptr = base + lo */
|
||||
ins2(c, A_ADDQ, areg(D_AX), areg(D_CX));
|
||||
/* push cap, len, ptr (top) */
|
||||
ins1(c, A_PUSHQ, areg(D_DX)); /* cap */
|
||||
/* push cap, len, ptr (top). cap = base_cap - lo
|
||||
* (#20); AX=lo, BX free. */
|
||||
if (cg_base_cap(c, base, bu, locals, D_BX)) {
|
||||
ins2(c, A_SUBQ, areg(D_AX), areg(D_BX));
|
||||
ins1(c, A_PUSHQ, areg(D_BX)); /* cap */
|
||||
} else {
|
||||
ins1(c, A_PUSHQ, areg(D_DX)); /* cap = len */
|
||||
}
|
||||
ins1(c, A_PUSHQ, areg(D_DX)); /* len */
|
||||
ins1(c, A_PUSHQ, areg(D_CX)); /* ptr */
|
||||
continue;
|
||||
@@ -6317,12 +6361,12 @@ 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=hi-lo) so callers can route
|
||||
* to a slice slot, return, or arg with the same ABI. Cap
|
||||
* defaults to the new length — there's no syntax for a
|
||||
* larger cap yet. Element scaling on the ptr isn't wired
|
||||
* (matches the let-init path), so non-u8 slices need a
|
||||
* follow-up audit when fixtures exercise them. */
|
||||
* (AX=base+lo, 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. */
|
||||
Node *base = n->lhs;
|
||||
Node *lo = n->rhs;
|
||||
Node *hi = n->cond;
|
||||
@@ -6373,7 +6417,13 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
ins1(c, A_POPQ, areg(D_AX));
|
||||
ins2(c, A_ADDQ, areg(D_CX), areg(D_AX));
|
||||
ins2(c, A_SUBQ, areg(D_CX), areg(D_BX));
|
||||
ins2(c, A_MOVQ, areg(D_BX), areg(D_CX));
|
||||
/* cap = base_cap - lo (#20); CX=lo, BX=len here. */
|
||||
if (cg_base_cap(c, base, bu, locals, D_DX)) {
|
||||
ins2(c, A_SUBQ, areg(D_CX), areg(D_DX));
|
||||
ins2(c, A_MOVQ, areg(D_DX), areg(D_CX));
|
||||
} else {
|
||||
ins2(c, A_MOVQ, areg(D_BX), areg(D_CX));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user