wcc+w6c_ww: append() spread place-chain sources via cgplaceaddr (#35)
#35 (fold-5 blocker, PG6): the append() spread arm dispatched on SOURCE node kind — ident-local only; both fold-5 dup shapes loud-rejected on BOTH stages: deref-spine `append(dup, (*tsp)[0].caps...)` (add_thread regex.ha:569/572) and indexed `append(res, ts[0].caps...)` (search ha:820). Route every non-ident-local source through cgplaceaddr (the established place choke-point: C1 stores, C1.25 aggregate fields, FA1 append targets, #49 element sources): the source header ADDRESS resolves ONCE, pre-grow, into a fresh per-site @appsprscr spill, and every loop iteration re-reads .ptr/.len THROUGH the spilled header after the grow. This adapts the #49 split ruling to the spread's full-range copy: the chain's rvalues (deref-root pointer expr, index exprs) evaluate exactly once PRE-grow — an index reading the dst's len sees the pre-append value — while the source data base and len re-derive live each iteration, so a source header aliasing the dst header re-roots post-realloc. A header reached through a buffer the grow reallocs reads the STALE copy — bit-identical under the non-reclaiming rt/malloc, the same documented #49 stale-base hole (pinned by the spread_selfalias_chain row). Ident-local sources keep the legacy BP-disp emission byte-identical. New loudness with the same fix: the spread source's stamped type must chase to TY_SLICE/TY_STR — a [N]T array ident source previously read its first 16 DATA bytes as a {ptr,len} header, silently. Array wiring plus the remaining rvalue sources (CALL, slicing exprs) stay loud, filed task #27. Global-ident sources now resolve on cstage but are blocked by a pre-existing wwstage checker reject ("let: not assignable", task #29) — no dual-stage row until that closes. 806_append_place: reject_spread_src GRADUATES to a runtime row (spread_place_deref, the old reject source verbatim + readback); new rows spread_dup_copy (PG6 verbatim: both fold-5 shapes, 56B capture elements, copy-semantics mutation pin, want 139), spread_place_kinds (str 24B headers / narrow i32 / empty deref source), spread_growth_place (40-elem spread crossing cap doublings), spread_selfalias_chain (source header inside the dst's grown buffer), and the two new loud-tail rejects (CALL rvalue, [N]T array) pinning the "#35:" diagnostic on both stages. 87 fixtures green (was 70), per-row cs/ww asm byte-cmp included. Unblocks regex fold-5a: add_thread regex.ha:569/572 + search ha:820 spreads go from loud-bound to real spread.
This commit is contained in:
@@ -25244,21 +25244,53 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
// #34 review: a non-ident/non-local spread source used
|
||||
// to be silently SKIPPED here while cstage fell past
|
||||
// its spread arm into the single-value stores with the
|
||||
// N_SPREAD node (garbage store) — divergent. Deferred
|
||||
// source shape, task #37.
|
||||
// N_SPREAD node (garbage store) — divergent.
|
||||
let it: *node = vn.lhs;
|
||||
if (it == nil || it.kind != nkind.N_IDENT) {
|
||||
let m34p: str = "#34: append() spread source shape unsupported (rule-7)\n";
|
||||
os.write(2, m34p.ptr, m34p.len: u64);
|
||||
// #35: only a {ptr,len,cap}-headered source reads as a
|
||||
// header below; a [N]T array place IS its storage — the
|
||||
// ident path used to read its first 16 data bytes as
|
||||
// ptr/len, silently. Loud until wired (task #27); str
|
||||
// shares the slice header layout.
|
||||
let itu: *tinfo = nil;
|
||||
if (it != nil) { itu = it.type_: *tinfo; };
|
||||
for (itu != nil && itu.kind == tykind.TY_NAMED) { itu = itu.under; };
|
||||
if (itu == nil || (itu.kind != tykind.TY_SLICE
|
||||
&& itu.kind != tykind.TY_STR)) {
|
||||
let m35p: str = "#35: append() spread source shape unsupported (rule-7)\n";
|
||||
os.write(2, m35p.ptr, m35p.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let itlocal: *local = localfindnode(c, it.str);
|
||||
if (itlocal == nil) {
|
||||
let m34q: str = "#34: append() spread source ident is not a local (rule-7)\n";
|
||||
os.write(2, m34q.ptr, m34q.len: u64);
|
||||
os.exit(1);
|
||||
let it_off: i32 = 0;
|
||||
let itscr: i32 = 0;
|
||||
if (it.kind == nkind.N_IDENT) {
|
||||
let itlocal: *local = localfindnode(c, it.str);
|
||||
if (itlocal != nil) { it_off = itlocal.off; };
|
||||
};
|
||||
if (it_off == 0) {
|
||||
// #35: place-chain source (deref spine, indexed
|
||||
// chain, global ident — the regex.ha:569/820 dup
|
||||
// shapes) resolves its header ADDRESS through
|
||||
// cgplaceaddr ONCE, pre-grow: the chain's rvalues
|
||||
// run exactly once (the #49 split's pre-grow
|
||||
// half) and every iteration re-reads .ptr/.len
|
||||
// THROUGH the spilled header post-grow (the live
|
||||
// re-derivation half). A header reached through a
|
||||
// buffer the grow reallocs keeps Hare's
|
||||
// stale-base hole — see the #49 comment below.
|
||||
// Rvalue sources (CALL, slicing exprs) have no
|
||||
// place — loud, task #27. Fresh spill slot per
|
||||
// SITE for the same nesting reason as @apphdrscr
|
||||
// above.
|
||||
if (!cgplaceaddr(c, it, "BX")) {
|
||||
let m35q: str = "#35: append() spread source shape unsupported (rule-7)\n";
|
||||
os.write(2, m35q.ptr, m35q.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
itscr = localalloc(c, "@appsprscr", 8, nil);
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff(itscr: i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
let it_off: i32 = itlocal.off;
|
||||
let load_op: str = tnodeloadop(c, etnode, esz);
|
||||
if (!sndirect) {
|
||||
// FA1: no etnode behind `*p` — signedness off the
|
||||
@@ -25272,9 +25304,16 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
let le: str = mklabel(c, "spr_e");
|
||||
emitlabel(ll);
|
||||
emitline("\tMOVQ\t(SP), CX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((it_off + 8): i64);
|
||||
emitline("(BP), DX\n");
|
||||
if (itscr != 0) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(itscr: i64);
|
||||
emitline("(BP), DX\n");
|
||||
emitline("\tMOVQ\t8(DX), DX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((it_off + 8): i64);
|
||||
emitline("(BP), DX\n");
|
||||
};
|
||||
emitline("\tCMPQ\tDX, CX\n");
|
||||
emitline("\tJGE\t"); emitline(le); emitline("\n");
|
||||
if (elwide) {
|
||||
@@ -25293,9 +25332,16 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
emitline(", AX\n");
|
||||
emitline("\tIMULQ\tAX, CX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(it_off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
if (itscr != 0) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(itscr: i64);
|
||||
emitline("(BP), BX\n");
|
||||
emitline("\tMOVQ\t(BX), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(it_off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
emitline("\tADDQ\tCX, BX\n");
|
||||
cgappendslot(c, sndirect, sn_off, snscr, esz, "DX");
|
||||
let wk: i32 = 0;
|
||||
@@ -25342,9 +25388,16 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
vn = vn.next;
|
||||
continue;
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(it_off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
if (itscr != 0) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(itscr: i64);
|
||||
emitline("(BP), BX\n");
|
||||
emitline("\tMOVQ\t(BX), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(it_off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
|
||||
@@ -4810,21 +4810,53 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
// #34 review: a non-ident/non-local spread source used
|
||||
// to be silently SKIPPED here while cstage fell past
|
||||
// its spread arm into the single-value stores with the
|
||||
// N_SPREAD node (garbage store) — divergent. Deferred
|
||||
// source shape, task #37.
|
||||
// N_SPREAD node (garbage store) — divergent.
|
||||
let it: *node = vn.lhs;
|
||||
if (it == nil || it.kind != nkind.N_IDENT) {
|
||||
let m34p: str = "#34: append() spread source shape unsupported (rule-7)\n";
|
||||
os.write(2, m34p.ptr, m34p.len: u64);
|
||||
// #35: only a {ptr,len,cap}-headered source reads as a
|
||||
// header below; a [N]T array place IS its storage — the
|
||||
// ident path used to read its first 16 data bytes as
|
||||
// ptr/len, silently. Loud until wired (task #27); str
|
||||
// shares the slice header layout.
|
||||
let itu: *tinfo = nil;
|
||||
if (it != nil) { itu = it.type_: *tinfo; };
|
||||
for (itu != nil && itu.kind == tykind.TY_NAMED) { itu = itu.under; };
|
||||
if (itu == nil || (itu.kind != tykind.TY_SLICE
|
||||
&& itu.kind != tykind.TY_STR)) {
|
||||
let m35p: str = "#35: append() spread source shape unsupported (rule-7)\n";
|
||||
os.write(2, m35p.ptr, m35p.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let itlocal: *local = localfindnode(c, it.str);
|
||||
if (itlocal == nil) {
|
||||
let m34q: str = "#34: append() spread source ident is not a local (rule-7)\n";
|
||||
os.write(2, m34q.ptr, m34q.len: u64);
|
||||
os.exit(1);
|
||||
let it_off: i32 = 0;
|
||||
let itscr: i32 = 0;
|
||||
if (it.kind == nkind.N_IDENT) {
|
||||
let itlocal: *local = localfindnode(c, it.str);
|
||||
if (itlocal != nil) { it_off = itlocal.off; };
|
||||
};
|
||||
if (it_off == 0) {
|
||||
// #35: place-chain source (deref spine, indexed
|
||||
// chain, global ident — the regex.ha:569/820 dup
|
||||
// shapes) resolves its header ADDRESS through
|
||||
// cgplaceaddr ONCE, pre-grow: the chain's rvalues
|
||||
// run exactly once (the #49 split's pre-grow
|
||||
// half) and every iteration re-reads .ptr/.len
|
||||
// THROUGH the spilled header post-grow (the live
|
||||
// re-derivation half). A header reached through a
|
||||
// buffer the grow reallocs keeps Hare's
|
||||
// stale-base hole — see the #49 comment below.
|
||||
// Rvalue sources (CALL, slicing exprs) have no
|
||||
// place — loud, task #27. Fresh spill slot per
|
||||
// SITE for the same nesting reason as @apphdrscr
|
||||
// above.
|
||||
if (!cgplaceaddr(c, it, "BX")) {
|
||||
let m35q: str = "#35: append() spread source shape unsupported (rule-7)\n";
|
||||
os.write(2, m35q.ptr, m35q.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
itscr = localalloc(c, "@appsprscr", 8, nil);
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff(itscr: i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
let it_off: i32 = itlocal.off;
|
||||
let load_op: str = tnodeloadop(c, etnode, esz);
|
||||
if (!sndirect) {
|
||||
// FA1: no etnode behind `*p` — signedness off the
|
||||
@@ -4838,9 +4870,16 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
let le: str = mklabel(c, "spr_e");
|
||||
emitlabel(ll);
|
||||
emitline("\tMOVQ\t(SP), CX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((it_off + 8): i64);
|
||||
emitline("(BP), DX\n");
|
||||
if (itscr != 0) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(itscr: i64);
|
||||
emitline("(BP), DX\n");
|
||||
emitline("\tMOVQ\t8(DX), DX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((it_off + 8): i64);
|
||||
emitline("(BP), DX\n");
|
||||
};
|
||||
emitline("\tCMPQ\tDX, CX\n");
|
||||
emitline("\tJGE\t"); emitline(le); emitline("\n");
|
||||
if (elwide) {
|
||||
@@ -4859,9 +4898,16 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
emitline(", AX\n");
|
||||
emitline("\tIMULQ\tAX, CX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(it_off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
if (itscr != 0) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(itscr: i64);
|
||||
emitline("(BP), BX\n");
|
||||
emitline("\tMOVQ\t(BX), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(it_off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
emitline("\tADDQ\tCX, BX\n");
|
||||
cgappendslot(c, sndirect, sn_off, snscr, esz, "DX");
|
||||
let wk: i32 = 0;
|
||||
@@ -4908,9 +4954,16 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
vn = vn.next;
|
||||
continue;
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(it_off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
if (itscr != 0) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(itscr: i64);
|
||||
emitline("(BP), BX\n");
|
||||
emitline("\tMOVQ\t(BX), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(it_off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
|
||||
@@ -25244,21 +25244,53 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
// #34 review: a non-ident/non-local spread source used
|
||||
// to be silently SKIPPED here while cstage fell past
|
||||
// its spread arm into the single-value stores with the
|
||||
// N_SPREAD node (garbage store) — divergent. Deferred
|
||||
// source shape, task #37.
|
||||
// N_SPREAD node (garbage store) — divergent.
|
||||
let it: *node = vn.lhs;
|
||||
if (it == nil || it.kind != nkind.N_IDENT) {
|
||||
let m34p: str = "#34: append() spread source shape unsupported (rule-7)\n";
|
||||
os.write(2, m34p.ptr, m34p.len: u64);
|
||||
// #35: only a {ptr,len,cap}-headered source reads as a
|
||||
// header below; a [N]T array place IS its storage — the
|
||||
// ident path used to read its first 16 data bytes as
|
||||
// ptr/len, silently. Loud until wired (task #27); str
|
||||
// shares the slice header layout.
|
||||
let itu: *tinfo = nil;
|
||||
if (it != nil) { itu = it.type_: *tinfo; };
|
||||
for (itu != nil && itu.kind == tykind.TY_NAMED) { itu = itu.under; };
|
||||
if (itu == nil || (itu.kind != tykind.TY_SLICE
|
||||
&& itu.kind != tykind.TY_STR)) {
|
||||
let m35p: str = "#35: append() spread source shape unsupported (rule-7)\n";
|
||||
os.write(2, m35p.ptr, m35p.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let itlocal: *local = localfindnode(c, it.str);
|
||||
if (itlocal == nil) {
|
||||
let m34q: str = "#34: append() spread source ident is not a local (rule-7)\n";
|
||||
os.write(2, m34q.ptr, m34q.len: u64);
|
||||
os.exit(1);
|
||||
let it_off: i32 = 0;
|
||||
let itscr: i32 = 0;
|
||||
if (it.kind == nkind.N_IDENT) {
|
||||
let itlocal: *local = localfindnode(c, it.str);
|
||||
if (itlocal != nil) { it_off = itlocal.off; };
|
||||
};
|
||||
if (it_off == 0) {
|
||||
// #35: place-chain source (deref spine, indexed
|
||||
// chain, global ident — the regex.ha:569/820 dup
|
||||
// shapes) resolves its header ADDRESS through
|
||||
// cgplaceaddr ONCE, pre-grow: the chain's rvalues
|
||||
// run exactly once (the #49 split's pre-grow
|
||||
// half) and every iteration re-reads .ptr/.len
|
||||
// THROUGH the spilled header post-grow (the live
|
||||
// re-derivation half). A header reached through a
|
||||
// buffer the grow reallocs keeps Hare's
|
||||
// stale-base hole — see the #49 comment below.
|
||||
// Rvalue sources (CALL, slicing exprs) have no
|
||||
// place — loud, task #27. Fresh spill slot per
|
||||
// SITE for the same nesting reason as @apphdrscr
|
||||
// above.
|
||||
if (!cgplaceaddr(c, it, "BX")) {
|
||||
let m35q: str = "#35: append() spread source shape unsupported (rule-7)\n";
|
||||
os.write(2, m35q.ptr, m35q.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
itscr = localalloc(c, "@appsprscr", 8, nil);
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff(itscr: i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
let it_off: i32 = itlocal.off;
|
||||
let load_op: str = tnodeloadop(c, etnode, esz);
|
||||
if (!sndirect) {
|
||||
// FA1: no etnode behind `*p` — signedness off the
|
||||
@@ -25272,9 +25304,16 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
let le: str = mklabel(c, "spr_e");
|
||||
emitlabel(ll);
|
||||
emitline("\tMOVQ\t(SP), CX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((it_off + 8): i64);
|
||||
emitline("(BP), DX\n");
|
||||
if (itscr != 0) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(itscr: i64);
|
||||
emitline("(BP), DX\n");
|
||||
emitline("\tMOVQ\t8(DX), DX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((it_off + 8): i64);
|
||||
emitline("(BP), DX\n");
|
||||
};
|
||||
emitline("\tCMPQ\tDX, CX\n");
|
||||
emitline("\tJGE\t"); emitline(le); emitline("\n");
|
||||
if (elwide) {
|
||||
@@ -25293,9 +25332,16 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
emitline(", AX\n");
|
||||
emitline("\tIMULQ\tAX, CX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(it_off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
if (itscr != 0) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(itscr: i64);
|
||||
emitline("(BP), BX\n");
|
||||
emitline("\tMOVQ\t(BX), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(it_off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
emitline("\tADDQ\tCX, BX\n");
|
||||
cgappendslot(c, sndirect, sn_off, snscr, esz, "DX");
|
||||
let wk: i32 = 0;
|
||||
@@ -25342,9 +25388,16 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
vn = vn.next;
|
||||
continue;
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(it_off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
if (itscr != 0) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(itscr: i64);
|
||||
emitline("(BP), BX\n");
|
||||
emitline("\tMOVQ\t(BX), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(it_off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
|
||||
Reference in New Issue
Block a user