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:
@@ -10950,7 +10950,13 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node) i32 = {
|
||||
emitline("\tMOVQ\tBX, DX\n"); // DX = hi
|
||||
emitline("\tSUBQ\tAX, DX\n"); // DX = hi - lo = len
|
||||
emitline("\tADDQ\tAX, CX\n"); // CX = base + lo = ptr
|
||||
emitline("\tPUSHQ\tDX\n"); // cap
|
||||
// cap = base_cap - lo (#20); AX=lo, BX free.
|
||||
if (cgbasecap(c, base, "BX")) {
|
||||
emitline("\tSUBQ\tAX, BX\n");
|
||||
emitline("\tPUSHQ\tBX\n"); // cap
|
||||
} else {
|
||||
emitline("\tPUSHQ\tDX\n"); // cap = len
|
||||
};
|
||||
emitline("\tPUSHQ\tDX\n"); // len
|
||||
emitline("\tPUSHQ\tCX\n"); // ptr (top)
|
||||
return rest + 3;
|
||||
@@ -14775,11 +14781,91 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
|
||||
// cgbasecap — 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 in the header at +16 (mirrors the
|
||||
// hi-default +8 length dispatch, emitted unconditionally). Returns
|
||||
// false when base_cap isn't cleanly available so the caller keeps the
|
||||
// prior cap=len: a non-ident base (its header cap was discarded;
|
||||
// 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 (no +16 load here, #73 -- matching the cstage
|
||||
// carve-out keeps both stages byte-identical). cstage twin:
|
||||
// cmd/w6c/cgen.c cg_base_cap.
|
||||
fn cgbasecap(c: *cgen, base: *node, dst: str) bool = {
|
||||
if (base == nil) { return false; };
|
||||
if (base.kind != nkind.N_IDENT) { return false; };
|
||||
let baselocal: *local = localfindnode(c, base.str);
|
||||
if (baselocal != nil) {
|
||||
let tn: *node = baselocal.tnode;
|
||||
if (tn == nil) { return false; };
|
||||
if (tn.kind == nkind.N_TARRAY) {
|
||||
let lenn: *node = tn.rhs;
|
||||
if (lenn == nil) { return false; };
|
||||
if (lenn.kind != nkind.N_INTLIT) { return false; };
|
||||
emitline("\tMOVQ\t$");
|
||||
emituint(lenn.uval);
|
||||
emitline(", ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
return true;
|
||||
};
|
||||
if (tn.kind == nkind.N_TSLICE) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((baselocal.off + 16): i64);
|
||||
emitline("(BP), ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
return true;
|
||||
};
|
||||
if (tn.kind == nkind.N_TNAME) {
|
||||
if (streq(tn.str, "str")) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((baselocal.off + 16): i64);
|
||||
emitline("(BP), ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
return true;
|
||||
};
|
||||
};
|
||||
return false;
|
||||
};
|
||||
let gt: *node = letvartnode(c, base.str);
|
||||
if (gt == nil) { return false; };
|
||||
if (gt.kind == nkind.N_TARRAY) {
|
||||
let lenn: *node = gt.rhs;
|
||||
if (lenn == nil) { return false; };
|
||||
if (lenn.kind != nkind.N_INTLIT) { return false; };
|
||||
emitline("\tMOVQ\t$");
|
||||
emituint(lenn.uval);
|
||||
emitline(", ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
return true;
|
||||
};
|
||||
if (gt.kind == nkind.N_TSLICE) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, base.str);
|
||||
emitline("(SB), ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\t16(");
|
||||
emitline(dst);
|
||||
emitline("), ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
// cgslice — `base[lo:hi]` as a slice value. Leaves (AX=base+lo,
|
||||
// BX=hi-lo, CX=hi-lo) so callers can route to a slice slot,
|
||||
// return, or arg with the same triple ABI. Cap defaults to the
|
||||
// new length; no syntax for a wider cap yet. Element scaling
|
||||
// on the ptr isn't wired — non-u8 slices need a follow-up audit.
|
||||
// BX=hi-lo, CX=base_cap-lo) so callers can route to a slice slot,
|
||||
// return, or arg with the same triple ABI. cap is the storage
|
||||
// remaining to the base's end (#20, Go/Hare-identical) via cgbasecap.
|
||||
// Element scaling on the ptr isn't wired (lo*esz is #76).
|
||||
fn cgslice(c: *cgen, n: *node) void = {
|
||||
let base: *node = n.lhs;
|
||||
let lo: *node = n.rhs;
|
||||
@@ -14897,7 +14983,13 @@ fn cgslice(c: *cgen, n: *node) void = {
|
||||
emitline("\tPOPQ\tAX\n");
|
||||
emitline("\tADDQ\tCX, AX\n");
|
||||
emitline("\tSUBQ\tCX, BX\n");
|
||||
emitline("\tMOVQ\tBX, CX\n");
|
||||
// cap = base_cap - lo (#20); CX=lo, BX=len here.
|
||||
if (cgbasecap(c, base, "DX")) {
|
||||
emitline("\tSUBQ\tCX, DX\n");
|
||||
emitline("\tMOVQ\tDX, CX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tBX, CX\n");
|
||||
};
|
||||
};
|
||||
|
||||
fn cgmatch(c: *cgen, n: *node) void = {
|
||||
|
||||
@@ -942,11 +942,91 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
|
||||
// cgbasecap — 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 in the header at +16 (mirrors the
|
||||
// hi-default +8 length dispatch, emitted unconditionally). Returns
|
||||
// false when base_cap isn't cleanly available so the caller keeps the
|
||||
// prior cap=len: a non-ident base (its header cap was discarded;
|
||||
// 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 (no +16 load here, #73 -- matching the cstage
|
||||
// carve-out keeps both stages byte-identical). cstage twin:
|
||||
// cmd/w6c/cgen.c cg_base_cap.
|
||||
fn cgbasecap(c: *cgen, base: *node, dst: str) bool = {
|
||||
if (base == nil) { return false; };
|
||||
if (base.kind != nkind.N_IDENT) { return false; };
|
||||
let baselocal: *local = localfindnode(c, base.str);
|
||||
if (baselocal != nil) {
|
||||
let tn: *node = baselocal.tnode;
|
||||
if (tn == nil) { return false; };
|
||||
if (tn.kind == nkind.N_TARRAY) {
|
||||
let lenn: *node = tn.rhs;
|
||||
if (lenn == nil) { return false; };
|
||||
if (lenn.kind != nkind.N_INTLIT) { return false; };
|
||||
emitline("\tMOVQ\t$");
|
||||
emituint(lenn.uval);
|
||||
emitline(", ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
return true;
|
||||
};
|
||||
if (tn.kind == nkind.N_TSLICE) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((baselocal.off + 16): i64);
|
||||
emitline("(BP), ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
return true;
|
||||
};
|
||||
if (tn.kind == nkind.N_TNAME) {
|
||||
if (streq(tn.str, "str")) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((baselocal.off + 16): i64);
|
||||
emitline("(BP), ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
return true;
|
||||
};
|
||||
};
|
||||
return false;
|
||||
};
|
||||
let gt: *node = letvartnode(c, base.str);
|
||||
if (gt == nil) { return false; };
|
||||
if (gt.kind == nkind.N_TARRAY) {
|
||||
let lenn: *node = gt.rhs;
|
||||
if (lenn == nil) { return false; };
|
||||
if (lenn.kind != nkind.N_INTLIT) { return false; };
|
||||
emitline("\tMOVQ\t$");
|
||||
emituint(lenn.uval);
|
||||
emitline(", ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
return true;
|
||||
};
|
||||
if (gt.kind == nkind.N_TSLICE) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, base.str);
|
||||
emitline("(SB), ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\t16(");
|
||||
emitline(dst);
|
||||
emitline("), ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
// cgslice — `base[lo:hi]` as a slice value. Leaves (AX=base+lo,
|
||||
// BX=hi-lo, CX=hi-lo) so callers can route to a slice slot,
|
||||
// return, or arg with the same triple ABI. Cap defaults to the
|
||||
// new length; no syntax for a wider cap yet. Element scaling
|
||||
// on the ptr isn't wired — non-u8 slices need a follow-up audit.
|
||||
// BX=hi-lo, CX=base_cap-lo) so callers can route to a slice slot,
|
||||
// return, or arg with the same triple ABI. cap is the storage
|
||||
// remaining to the base's end (#20, Go/Hare-identical) via cgbasecap.
|
||||
// Element scaling on the ptr isn't wired (lo*esz is #76).
|
||||
fn cgslice(c: *cgen, n: *node) void = {
|
||||
let base: *node = n.lhs;
|
||||
let lo: *node = n.rhs;
|
||||
@@ -1064,7 +1144,13 @@ fn cgslice(c: *cgen, n: *node) void = {
|
||||
emitline("\tPOPQ\tAX\n");
|
||||
emitline("\tADDQ\tCX, AX\n");
|
||||
emitline("\tSUBQ\tCX, BX\n");
|
||||
emitline("\tMOVQ\tBX, CX\n");
|
||||
// cap = base_cap - lo (#20); CX=lo, BX=len here.
|
||||
if (cgbasecap(c, base, "DX")) {
|
||||
emitline("\tSUBQ\tCX, DX\n");
|
||||
emitline("\tMOVQ\tDX, CX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tBX, CX\n");
|
||||
};
|
||||
};
|
||||
|
||||
fn cgmatch(c: *cgen, n: *node) void = {
|
||||
|
||||
@@ -389,7 +389,13 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node) i32 = {
|
||||
emitline("\tMOVQ\tBX, DX\n"); // DX = hi
|
||||
emitline("\tSUBQ\tAX, DX\n"); // DX = hi - lo = len
|
||||
emitline("\tADDQ\tAX, CX\n"); // CX = base + lo = ptr
|
||||
emitline("\tPUSHQ\tDX\n"); // cap
|
||||
// cap = base_cap - lo (#20); AX=lo, BX free.
|
||||
if (cgbasecap(c, base, "BX")) {
|
||||
emitline("\tSUBQ\tAX, BX\n");
|
||||
emitline("\tPUSHQ\tBX\n"); // cap
|
||||
} else {
|
||||
emitline("\tPUSHQ\tDX\n"); // cap = len
|
||||
};
|
||||
emitline("\tPUSHQ\tDX\n"); // len
|
||||
emitline("\tPUSHQ\tCX\n"); // ptr (top)
|
||||
return rest + 3;
|
||||
|
||||
@@ -10950,7 +10950,13 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node) i32 = {
|
||||
emitline("\tMOVQ\tBX, DX\n"); // DX = hi
|
||||
emitline("\tSUBQ\tAX, DX\n"); // DX = hi - lo = len
|
||||
emitline("\tADDQ\tAX, CX\n"); // CX = base + lo = ptr
|
||||
emitline("\tPUSHQ\tDX\n"); // cap
|
||||
// cap = base_cap - lo (#20); AX=lo, BX free.
|
||||
if (cgbasecap(c, base, "BX")) {
|
||||
emitline("\tSUBQ\tAX, BX\n");
|
||||
emitline("\tPUSHQ\tBX\n"); // cap
|
||||
} else {
|
||||
emitline("\tPUSHQ\tDX\n"); // cap = len
|
||||
};
|
||||
emitline("\tPUSHQ\tDX\n"); // len
|
||||
emitline("\tPUSHQ\tCX\n"); // ptr (top)
|
||||
return rest + 3;
|
||||
@@ -14775,11 +14781,91 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
|
||||
// cgbasecap — 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 in the header at +16 (mirrors the
|
||||
// hi-default +8 length dispatch, emitted unconditionally). Returns
|
||||
// false when base_cap isn't cleanly available so the caller keeps the
|
||||
// prior cap=len: a non-ident base (its header cap was discarded;
|
||||
// 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 (no +16 load here, #73 -- matching the cstage
|
||||
// carve-out keeps both stages byte-identical). cstage twin:
|
||||
// cmd/w6c/cgen.c cg_base_cap.
|
||||
fn cgbasecap(c: *cgen, base: *node, dst: str) bool = {
|
||||
if (base == nil) { return false; };
|
||||
if (base.kind != nkind.N_IDENT) { return false; };
|
||||
let baselocal: *local = localfindnode(c, base.str);
|
||||
if (baselocal != nil) {
|
||||
let tn: *node = baselocal.tnode;
|
||||
if (tn == nil) { return false; };
|
||||
if (tn.kind == nkind.N_TARRAY) {
|
||||
let lenn: *node = tn.rhs;
|
||||
if (lenn == nil) { return false; };
|
||||
if (lenn.kind != nkind.N_INTLIT) { return false; };
|
||||
emitline("\tMOVQ\t$");
|
||||
emituint(lenn.uval);
|
||||
emitline(", ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
return true;
|
||||
};
|
||||
if (tn.kind == nkind.N_TSLICE) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((baselocal.off + 16): i64);
|
||||
emitline("(BP), ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
return true;
|
||||
};
|
||||
if (tn.kind == nkind.N_TNAME) {
|
||||
if (streq(tn.str, "str")) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((baselocal.off + 16): i64);
|
||||
emitline("(BP), ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
return true;
|
||||
};
|
||||
};
|
||||
return false;
|
||||
};
|
||||
let gt: *node = letvartnode(c, base.str);
|
||||
if (gt == nil) { return false; };
|
||||
if (gt.kind == nkind.N_TARRAY) {
|
||||
let lenn: *node = gt.rhs;
|
||||
if (lenn == nil) { return false; };
|
||||
if (lenn.kind != nkind.N_INTLIT) { return false; };
|
||||
emitline("\tMOVQ\t$");
|
||||
emituint(lenn.uval);
|
||||
emitline(", ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
return true;
|
||||
};
|
||||
if (gt.kind == nkind.N_TSLICE) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, base.str);
|
||||
emitline("(SB), ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\t16(");
|
||||
emitline(dst);
|
||||
emitline("), ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
// cgslice — `base[lo:hi]` as a slice value. Leaves (AX=base+lo,
|
||||
// BX=hi-lo, CX=hi-lo) so callers can route to a slice slot,
|
||||
// return, or arg with the same triple ABI. Cap defaults to the
|
||||
// new length; no syntax for a wider cap yet. Element scaling
|
||||
// on the ptr isn't wired — non-u8 slices need a follow-up audit.
|
||||
// BX=hi-lo, CX=base_cap-lo) so callers can route to a slice slot,
|
||||
// return, or arg with the same triple ABI. cap is the storage
|
||||
// remaining to the base's end (#20, Go/Hare-identical) via cgbasecap.
|
||||
// Element scaling on the ptr isn't wired (lo*esz is #76).
|
||||
fn cgslice(c: *cgen, n: *node) void = {
|
||||
let base: *node = n.lhs;
|
||||
let lo: *node = n.rhs;
|
||||
@@ -14897,7 +14983,13 @@ fn cgslice(c: *cgen, n: *node) void = {
|
||||
emitline("\tPOPQ\tAX\n");
|
||||
emitline("\tADDQ\tCX, AX\n");
|
||||
emitline("\tSUBQ\tCX, BX\n");
|
||||
emitline("\tMOVQ\tBX, CX\n");
|
||||
// cap = base_cap - lo (#20); CX=lo, BX=len here.
|
||||
if (cgbasecap(c, base, "DX")) {
|
||||
emitline("\tSUBQ\tCX, DX\n");
|
||||
emitline("\tMOVQ\tDX, CX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tBX, CX\n");
|
||||
};
|
||||
};
|
||||
|
||||
fn cgmatch(c: *cgen, n: *node) void = {
|
||||
|
||||
Reference in New Issue
Block a user