wcc+w6c_ww: aggregate-field stores via cgplaceaddr (C1.25)
Wire struct/array/tuple field STORE through the C1 assign-resolver (task #23): structlit rhs materialises into a FRESH-per-use @placescr slot (the @slicescr discipline — a cached slot is the #31 multi-live trap) then word-copies to the resolved address; addressable rhs (ident/global/dot/deref) sources via aggarg_srcaddr with the dest spilled around the dispatch (#270-1b order). Kept loud: compound on aggregate, sret call rhs (#234-tail), <=24B call rhs (task #24), unaddressable literal rhs, ww-only anonymous-struct structinfo miss. test/wcc/805: +6 rows (40B structlit incl ... autofill, ident+deref source, nested literal, [3]u8 MOVW/MOVB and [3]u32 MOVL tails, two same-size stores in one fn pinning fresh-per-use) +3 exact-text reject rows. Tuple-field row blocked by the pre-existing tuple param/let-init word-2 drops (tasks #32/#33, documented in-row). p7b_capstore_only graduates byte-id and runs; p7_composed builds and runs on cstage, wwstage stays behind the pre-existing #29 asserttyped bail (verified identical at master with w6c_ww).
This commit is contained in:
108
cmd/w6c/cgen.c
108
cmd/w6c/cgen.c
@@ -6074,11 +6074,113 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
if (fu && fu->kind == TY_TAGGED)
|
if (fu && fu->kind == TY_TAGGED)
|
||||||
fatal("assign-resolver: tagged field not "
|
fatal("assign-resolver: tagged field not "
|
||||||
"wired (rule-7)");
|
"wired (rule-7)");
|
||||||
|
/* C1.25 (#23): aggregate field STORE through the
|
||||||
|
* resolver — run_thread's 40B capture store
|
||||||
|
* `(*ts)[i].root_capture = capture{...}`. Dest address
|
||||||
|
* from cgplaceaddr (BX), source address in SI per rhs
|
||||||
|
* shape, then the #270-1b word-copy tail (SI)→(BX).
|
||||||
|
* Pre-C1 this was a SILENT no-op; C1 made it loud;
|
||||||
|
* this wires it (loud-first, wire-next). Compound on
|
||||||
|
* an aggregate is meaningless and stays loud. */
|
||||||
if (fu && (fu->kind == TY_STRUCT
|
if (fu && (fu->kind == TY_STRUCT
|
||||||
|| fu->kind == TY_ARRAY
|
|| fu->kind == TY_ARRAY
|
||||||
|| fu->kind == TY_TUPLE))
|
|| fu->kind == TY_TUPLE)) {
|
||||||
fatal("assign-resolver: aggregate field not "
|
if (n->op != TK_ASSIGN)
|
||||||
"wired (rule-7)");
|
fatal("assign-resolver: compound on "
|
||||||
|
"aggregate field not wired "
|
||||||
|
"(rule-7)");
|
||||||
|
if (n->rhs && n->rhs->kind == N_CALL) {
|
||||||
|
/* sret-class needs a runtime-RDI dest
|
||||||
|
* (the #234-tail deferral); the ≤24B
|
||||||
|
* reg-return receive is task #24. */
|
||||||
|
if (cg_sret_retsize(ft) > 0)
|
||||||
|
fatal("assign-resolver: sret "
|
||||||
|
"call into aggregate field "
|
||||||
|
"unwired (#234-tail/"
|
||||||
|
"rule-7)");
|
||||||
|
fatal("assign-resolver: call result "
|
||||||
|
"into aggregate field unwired "
|
||||||
|
"(task #24/rule-7)");
|
||||||
|
}
|
||||||
|
int placed = 0;
|
||||||
|
if (n->rhs && n->rhs->kind == N_STRUCTLIT
|
||||||
|
&& fu->kind == TY_STRUCT) {
|
||||||
|
/* @placescr — FRESH slot PER USE (the
|
||||||
|
* @slicescr discipline, NOT the cached
|
||||||
|
* @tagscr table: a cached slot is the
|
||||||
|
* #31 multi-live corruption trap; rob
|
||||||
|
* ruling). Funnel contract, #44
|
||||||
|
* discipline: this arm is the ONLY
|
||||||
|
* @placescr alloc site. Fill handles
|
||||||
|
* nested literals (#18), tagged
|
||||||
|
* fields, TK_ELLIPSIS autofill; the
|
||||||
|
* value sits in memory, so the
|
||||||
|
* resolver below may clobber AX/CX
|
||||||
|
* freely. */
|
||||||
|
int scr = local_alloc(c, &locals,
|
||||||
|
"@placescr", fsz, cg_frame);
|
||||||
|
cg_structlit_fill_bp(c, &locals, fu,
|
||||||
|
n->rhs, scr);
|
||||||
|
placed = cgplaceaddr(c, n->lhs, D_BX,
|
||||||
|
locals);
|
||||||
|
if (placed)
|
||||||
|
ins2(c, A_LEAQ,
|
||||||
|
amem(D_BP, scr),
|
||||||
|
areg(D_SI));
|
||||||
|
} else {
|
||||||
|
/* Addressable source — ident / global
|
||||||
|
* / N_DOT chain / deref — via the
|
||||||
|
* closed #265/#268 dispatch. Its
|
||||||
|
* N_INDEX arm clobbers BX, so the dest
|
||||||
|
* spills around it (the #270-1b
|
||||||
|
* order). Literal arrays/tuples have
|
||||||
|
* no storage address and stay loud. */
|
||||||
|
placed = cgplaceaddr(c, n->lhs, D_BX,
|
||||||
|
locals);
|
||||||
|
if (placed) {
|
||||||
|
ins1(c, A_PUSHQ, areg(D_BX));
|
||||||
|
if (!aggarg_srcaddr(c, n->rhs,
|
||||||
|
D_SI, locals))
|
||||||
|
fatal("assign-resolver"
|
||||||
|
": aggregate rhs "
|
||||||
|
"shape unwired "
|
||||||
|
"(rule-7)");
|
||||||
|
ins1(c, A_POPQ, areg(D_BX));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!placed)
|
||||||
|
fatal("unsupported assign target "
|
||||||
|
"shape");
|
||||||
|
int k = 0;
|
||||||
|
for (; k + 8 <= fsz; k += 8) {
|
||||||
|
ins2(c, A_MOVQ, amem(D_SI, k),
|
||||||
|
areg(D_AX));
|
||||||
|
ins2(c, A_MOVQ, areg(D_AX),
|
||||||
|
amem(D_BX, k));
|
||||||
|
}
|
||||||
|
if (k + 4 <= fsz) {
|
||||||
|
ins2(c, A_MOVL, amem(D_SI, k),
|
||||||
|
areg(D_AX));
|
||||||
|
ins2(c, A_MOVL, areg(D_AX),
|
||||||
|
amem(D_BX, k));
|
||||||
|
k += 4;
|
||||||
|
}
|
||||||
|
if (k + 2 <= fsz) {
|
||||||
|
ins2(c, A_MOVW, amem(D_SI, k),
|
||||||
|
areg(D_AX));
|
||||||
|
ins2(c, A_MOVW, areg(D_AX),
|
||||||
|
amem(D_BX, k));
|
||||||
|
k += 2;
|
||||||
|
}
|
||||||
|
if (k + 1 <= fsz) {
|
||||||
|
ins2(c, A_MOVB, amem(D_SI, k),
|
||||||
|
areg(D_AX));
|
||||||
|
ins2(c, A_MOVB, areg(D_AX),
|
||||||
|
amem(D_BX, k));
|
||||||
|
k += 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (fu && (fu->kind == TY_STR
|
if (fu && (fu->kind == TY_STR
|
||||||
|| fu->kind == TY_SLICE)) {
|
|| fu->kind == TY_SLICE)) {
|
||||||
if (n->op != TK_ASSIGN)
|
if (n->op != TK_ASSIGN)
|
||||||
|
|||||||
@@ -20015,7 +20015,8 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *node,
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Thin wrapper preserving the BP-rel call shape used by cglet,
|
// Thin wrapper preserving the BP-rel call shape used by cglet,
|
||||||
// cgreturn, and cgassign N_IDENT-lhs N_STRUCTLIT.
|
// cgreturn, cgassign N_IDENT-lhs N_STRUCTLIT, and the C1.25
|
||||||
|
// @placescr materialise (cg_structlit_fill_bp's named twin).
|
||||||
fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
||||||
if (si == nil) { return; };
|
if (si == nil) { return; };
|
||||||
cgstructlitfill(c, si, lit, 0, 0, "", bpoff);
|
cgstructlitfill(c, si, lit, 0, 0, "", bpoff);
|
||||||
@@ -29029,12 +29030,152 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
os.write(2, mt.ptr, mt.len: u64);
|
os.write(2, mt.ptr, mt.len: u64);
|
||||||
os.exit(1);
|
os.exit(1);
|
||||||
};
|
};
|
||||||
|
// C1.25 (#23): aggregate field STORE through the
|
||||||
|
// resolver — run_thread's 40B capture store
|
||||||
|
// `(*ts)[i].root_capture = capture{...}`. Dest
|
||||||
|
// address from cgplaceaddr (BX), source address
|
||||||
|
// in SI per rhs shape, then the #270-1b
|
||||||
|
// word-copy tail (SI)→(BX). Pre-C1 a SILENT
|
||||||
|
// no-op; C1 made it loud; this wires it
|
||||||
|
// (loud-first, wire-next). Compound on an
|
||||||
|
// aggregate is meaningless and stays loud.
|
||||||
|
// Mirror of the cstage cgen.c C1.25 arm.
|
||||||
if (fu.kind == tykind.TY_STRUCT
|
if (fu.kind == tykind.TY_STRUCT
|
||||||
|| fu.kind == tykind.TY_ARRAY
|
|| fu.kind == tykind.TY_ARRAY
|
||||||
|| fu.kind == tykind.TY_TUPLE) {
|
|| fu.kind == tykind.TY_TUPLE) {
|
||||||
let ma: str = "assign-resolver: aggregate field not wired (rule-7)\n";
|
if (n.op != tkind.TK_ASSIGN) {
|
||||||
os.write(2, ma.ptr, ma.len: u64);
|
let mac: str = "assign-resolver: compound on aggregate field not wired (rule-7)\n";
|
||||||
os.exit(1);
|
os.write(2, mac.ptr, mac.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
if (n.rhs != nil) {
|
||||||
|
if (n.rhs.kind == nkind.N_CALL) {
|
||||||
|
// sret-class needs a runtime-RDI dest
|
||||||
|
// (the #234-tail deferral); the ≤24B
|
||||||
|
// reg-return receive is task #24. The
|
||||||
|
// callee return type equals the field
|
||||||
|
// type (checker-guaranteed), so
|
||||||
|
// callsretsize gives cstage's
|
||||||
|
// cg_sret_retsize(ft) verdict.
|
||||||
|
if (callsretsize(c, n.rhs) > 0) {
|
||||||
|
let mas: str = "assign-resolver: sret call into aggregate field unwired (#234-tail/rule-7)\n";
|
||||||
|
os.write(2, mas.ptr, mas.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
let ma24: str = "assign-resolver: call result into aggregate field unwired (task #24/rule-7)\n";
|
||||||
|
os.write(2, ma24.ptr, ma24.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
let placed: bool = false;
|
||||||
|
let isslit: bool = false;
|
||||||
|
if (n.rhs != nil) {
|
||||||
|
if (n.rhs.kind == nkind.N_STRUCTLIT
|
||||||
|
&& fu.kind == tykind.TY_STRUCT) {
|
||||||
|
isslit = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (isslit) {
|
||||||
|
// @placescr — FRESH slot PER USE (the
|
||||||
|
// @slicescr discipline via localalloc,
|
||||||
|
// NOT the cached @tagscr table: a
|
||||||
|
// cached slot is the #31 multi-live
|
||||||
|
// corruption trap; rob ruling). Funnel
|
||||||
|
// contract, #44 discipline: this arm is
|
||||||
|
// the ONLY @placescr alloc site. Fill
|
||||||
|
// handles nested literals (#18), tagged
|
||||||
|
// fields, TK_ELLIPSIS autofill; the
|
||||||
|
// value sits in memory, so the resolver
|
||||||
|
// below may clobber AX/CX freely.
|
||||||
|
let sname: str;
|
||||||
|
sname.ptr = nil; sname.len = 0;
|
||||||
|
if (ft.kind == tykind.TY_NAMED) {
|
||||||
|
sname = ft.name;
|
||||||
|
};
|
||||||
|
let si: *structinfo = nil;
|
||||||
|
if (sname.len > 0) {
|
||||||
|
si = structlookup(c, sname);
|
||||||
|
};
|
||||||
|
if (si == nil) {
|
||||||
|
// wwstage-only bail: the fill is
|
||||||
|
// structinfo-keyed, so an anonymous-
|
||||||
|
// struct field type has no registry
|
||||||
|
// entry (cstage fills from Type
|
||||||
|
// directly). Loud, rule 7.
|
||||||
|
let man: str = "assign-resolver: structlit field layout unresolved (rule-7)\n";
|
||||||
|
os.write(2, man.ptr, man.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
let scr: i32 = localalloc(c, "@placescr", fsz, nil);
|
||||||
|
cgstructlitfillbp(c, si, n.rhs, scr);
|
||||||
|
placed = cgplaceaddr(c, lhs, "BX");
|
||||||
|
if (placed) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(scr: i64);
|
||||||
|
emitline("(BP), SI\n");
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Addressable source — ident / global /
|
||||||
|
// N_DOT chain / deref — via the closed
|
||||||
|
// #265/#268 dispatch. Its N_INDEX arm
|
||||||
|
// clobbers BX, so the dest spills around
|
||||||
|
// it (the #270-1b order). Literal
|
||||||
|
// arrays/tuples have no storage address
|
||||||
|
// and stay loud.
|
||||||
|
placed = cgplaceaddr(c, lhs, "BX");
|
||||||
|
if (placed) {
|
||||||
|
emitline("\tPUSHQ\tBX\n");
|
||||||
|
if (!aggargsrcaddr(c, n.rhs, "SI")) {
|
||||||
|
let mar: str = "assign-resolver: aggregate rhs shape unwired (rule-7)\n";
|
||||||
|
os.write(2, mar.ptr, mar.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
emitline("\tPOPQ\tBX\n");
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (!placed) {
|
||||||
|
let mau: str = "unsupported assign target shape\n";
|
||||||
|
os.write(2, mau.ptr, mau.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
let kc: i32 = 0;
|
||||||
|
for (kc + 8 <= fsz) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(BX)\n");
|
||||||
|
kc += 8;
|
||||||
|
};
|
||||||
|
if (kc + 4 <= fsz) {
|
||||||
|
emitline("\tMOVL\t");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVL\tAX, ");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(BX)\n");
|
||||||
|
kc += 4;
|
||||||
|
};
|
||||||
|
if (kc + 2 <= fsz) {
|
||||||
|
emitline("\tMOVW\t");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVW\tAX, ");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(BX)\n");
|
||||||
|
kc += 2;
|
||||||
|
};
|
||||||
|
if (kc + 1 <= fsz) {
|
||||||
|
emitline("\tMOVB\t");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVB\tAX, ");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(BX)\n");
|
||||||
|
kc += 1;
|
||||||
|
};
|
||||||
|
return;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
let fstrsl: bool = false;
|
let fstrsl: bool = false;
|
||||||
|
|||||||
@@ -9006,12 +9006,152 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
os.write(2, mt.ptr, mt.len: u64);
|
os.write(2, mt.ptr, mt.len: u64);
|
||||||
os.exit(1);
|
os.exit(1);
|
||||||
};
|
};
|
||||||
|
// C1.25 (#23): aggregate field STORE through the
|
||||||
|
// resolver — run_thread's 40B capture store
|
||||||
|
// `(*ts)[i].root_capture = capture{...}`. Dest
|
||||||
|
// address from cgplaceaddr (BX), source address
|
||||||
|
// in SI per rhs shape, then the #270-1b
|
||||||
|
// word-copy tail (SI)→(BX). Pre-C1 a SILENT
|
||||||
|
// no-op; C1 made it loud; this wires it
|
||||||
|
// (loud-first, wire-next). Compound on an
|
||||||
|
// aggregate is meaningless and stays loud.
|
||||||
|
// Mirror of the cstage cgen.c C1.25 arm.
|
||||||
if (fu.kind == tykind.TY_STRUCT
|
if (fu.kind == tykind.TY_STRUCT
|
||||||
|| fu.kind == tykind.TY_ARRAY
|
|| fu.kind == tykind.TY_ARRAY
|
||||||
|| fu.kind == tykind.TY_TUPLE) {
|
|| fu.kind == tykind.TY_TUPLE) {
|
||||||
let ma: str = "assign-resolver: aggregate field not wired (rule-7)\n";
|
if (n.op != tkind.TK_ASSIGN) {
|
||||||
os.write(2, ma.ptr, ma.len: u64);
|
let mac: str = "assign-resolver: compound on aggregate field not wired (rule-7)\n";
|
||||||
os.exit(1);
|
os.write(2, mac.ptr, mac.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
if (n.rhs != nil) {
|
||||||
|
if (n.rhs.kind == nkind.N_CALL) {
|
||||||
|
// sret-class needs a runtime-RDI dest
|
||||||
|
// (the #234-tail deferral); the ≤24B
|
||||||
|
// reg-return receive is task #24. The
|
||||||
|
// callee return type equals the field
|
||||||
|
// type (checker-guaranteed), so
|
||||||
|
// callsretsize gives cstage's
|
||||||
|
// cg_sret_retsize(ft) verdict.
|
||||||
|
if (callsretsize(c, n.rhs) > 0) {
|
||||||
|
let mas: str = "assign-resolver: sret call into aggregate field unwired (#234-tail/rule-7)\n";
|
||||||
|
os.write(2, mas.ptr, mas.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
let ma24: str = "assign-resolver: call result into aggregate field unwired (task #24/rule-7)\n";
|
||||||
|
os.write(2, ma24.ptr, ma24.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
let placed: bool = false;
|
||||||
|
let isslit: bool = false;
|
||||||
|
if (n.rhs != nil) {
|
||||||
|
if (n.rhs.kind == nkind.N_STRUCTLIT
|
||||||
|
&& fu.kind == tykind.TY_STRUCT) {
|
||||||
|
isslit = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (isslit) {
|
||||||
|
// @placescr — FRESH slot PER USE (the
|
||||||
|
// @slicescr discipline via localalloc,
|
||||||
|
// NOT the cached @tagscr table: a
|
||||||
|
// cached slot is the #31 multi-live
|
||||||
|
// corruption trap; rob ruling). Funnel
|
||||||
|
// contract, #44 discipline: this arm is
|
||||||
|
// the ONLY @placescr alloc site. Fill
|
||||||
|
// handles nested literals (#18), tagged
|
||||||
|
// fields, TK_ELLIPSIS autofill; the
|
||||||
|
// value sits in memory, so the resolver
|
||||||
|
// below may clobber AX/CX freely.
|
||||||
|
let sname: str;
|
||||||
|
sname.ptr = nil; sname.len = 0;
|
||||||
|
if (ft.kind == tykind.TY_NAMED) {
|
||||||
|
sname = ft.name;
|
||||||
|
};
|
||||||
|
let si: *structinfo = nil;
|
||||||
|
if (sname.len > 0) {
|
||||||
|
si = structlookup(c, sname);
|
||||||
|
};
|
||||||
|
if (si == nil) {
|
||||||
|
// wwstage-only bail: the fill is
|
||||||
|
// structinfo-keyed, so an anonymous-
|
||||||
|
// struct field type has no registry
|
||||||
|
// entry (cstage fills from Type
|
||||||
|
// directly). Loud, rule 7.
|
||||||
|
let man: str = "assign-resolver: structlit field layout unresolved (rule-7)\n";
|
||||||
|
os.write(2, man.ptr, man.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
let scr: i32 = localalloc(c, "@placescr", fsz, nil);
|
||||||
|
cgstructlitfillbp(c, si, n.rhs, scr);
|
||||||
|
placed = cgplaceaddr(c, lhs, "BX");
|
||||||
|
if (placed) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(scr: i64);
|
||||||
|
emitline("(BP), SI\n");
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Addressable source — ident / global /
|
||||||
|
// N_DOT chain / deref — via the closed
|
||||||
|
// #265/#268 dispatch. Its N_INDEX arm
|
||||||
|
// clobbers BX, so the dest spills around
|
||||||
|
// it (the #270-1b order). Literal
|
||||||
|
// arrays/tuples have no storage address
|
||||||
|
// and stay loud.
|
||||||
|
placed = cgplaceaddr(c, lhs, "BX");
|
||||||
|
if (placed) {
|
||||||
|
emitline("\tPUSHQ\tBX\n");
|
||||||
|
if (!aggargsrcaddr(c, n.rhs, "SI")) {
|
||||||
|
let mar: str = "assign-resolver: aggregate rhs shape unwired (rule-7)\n";
|
||||||
|
os.write(2, mar.ptr, mar.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
emitline("\tPOPQ\tBX\n");
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (!placed) {
|
||||||
|
let mau: str = "unsupported assign target shape\n";
|
||||||
|
os.write(2, mau.ptr, mau.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
let kc: i32 = 0;
|
||||||
|
for (kc + 8 <= fsz) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(BX)\n");
|
||||||
|
kc += 8;
|
||||||
|
};
|
||||||
|
if (kc + 4 <= fsz) {
|
||||||
|
emitline("\tMOVL\t");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVL\tAX, ");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(BX)\n");
|
||||||
|
kc += 4;
|
||||||
|
};
|
||||||
|
if (kc + 2 <= fsz) {
|
||||||
|
emitline("\tMOVW\t");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVW\tAX, ");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(BX)\n");
|
||||||
|
kc += 2;
|
||||||
|
};
|
||||||
|
if (kc + 1 <= fsz) {
|
||||||
|
emitline("\tMOVB\t");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVB\tAX, ");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(BX)\n");
|
||||||
|
kc += 1;
|
||||||
|
};
|
||||||
|
return;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
let fstrsl: bool = false;
|
let fstrsl: bool = false;
|
||||||
|
|||||||
@@ -4283,7 +4283,8 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *node,
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Thin wrapper preserving the BP-rel call shape used by cglet,
|
// Thin wrapper preserving the BP-rel call shape used by cglet,
|
||||||
// cgreturn, and cgassign N_IDENT-lhs N_STRUCTLIT.
|
// cgreturn, cgassign N_IDENT-lhs N_STRUCTLIT, and the C1.25
|
||||||
|
// @placescr materialise (cg_structlit_fill_bp's named twin).
|
||||||
fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
||||||
if (si == nil) { return; };
|
if (si == nil) { return; };
|
||||||
cgstructlitfill(c, si, lit, 0, 0, "", bpoff);
|
cgstructlitfill(c, si, lit, 0, 0, "", bpoff);
|
||||||
|
|||||||
@@ -20015,7 +20015,8 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *node,
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Thin wrapper preserving the BP-rel call shape used by cglet,
|
// Thin wrapper preserving the BP-rel call shape used by cglet,
|
||||||
// cgreturn, and cgassign N_IDENT-lhs N_STRUCTLIT.
|
// cgreturn, cgassign N_IDENT-lhs N_STRUCTLIT, and the C1.25
|
||||||
|
// @placescr materialise (cg_structlit_fill_bp's named twin).
|
||||||
fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
||||||
if (si == nil) { return; };
|
if (si == nil) { return; };
|
||||||
cgstructlitfill(c, si, lit, 0, 0, "", bpoff);
|
cgstructlitfill(c, si, lit, 0, 0, "", bpoff);
|
||||||
@@ -29029,12 +29030,152 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
os.write(2, mt.ptr, mt.len: u64);
|
os.write(2, mt.ptr, mt.len: u64);
|
||||||
os.exit(1);
|
os.exit(1);
|
||||||
};
|
};
|
||||||
|
// C1.25 (#23): aggregate field STORE through the
|
||||||
|
// resolver — run_thread's 40B capture store
|
||||||
|
// `(*ts)[i].root_capture = capture{...}`. Dest
|
||||||
|
// address from cgplaceaddr (BX), source address
|
||||||
|
// in SI per rhs shape, then the #270-1b
|
||||||
|
// word-copy tail (SI)→(BX). Pre-C1 a SILENT
|
||||||
|
// no-op; C1 made it loud; this wires it
|
||||||
|
// (loud-first, wire-next). Compound on an
|
||||||
|
// aggregate is meaningless and stays loud.
|
||||||
|
// Mirror of the cstage cgen.c C1.25 arm.
|
||||||
if (fu.kind == tykind.TY_STRUCT
|
if (fu.kind == tykind.TY_STRUCT
|
||||||
|| fu.kind == tykind.TY_ARRAY
|
|| fu.kind == tykind.TY_ARRAY
|
||||||
|| fu.kind == tykind.TY_TUPLE) {
|
|| fu.kind == tykind.TY_TUPLE) {
|
||||||
let ma: str = "assign-resolver: aggregate field not wired (rule-7)\n";
|
if (n.op != tkind.TK_ASSIGN) {
|
||||||
os.write(2, ma.ptr, ma.len: u64);
|
let mac: str = "assign-resolver: compound on aggregate field not wired (rule-7)\n";
|
||||||
os.exit(1);
|
os.write(2, mac.ptr, mac.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
if (n.rhs != nil) {
|
||||||
|
if (n.rhs.kind == nkind.N_CALL) {
|
||||||
|
// sret-class needs a runtime-RDI dest
|
||||||
|
// (the #234-tail deferral); the ≤24B
|
||||||
|
// reg-return receive is task #24. The
|
||||||
|
// callee return type equals the field
|
||||||
|
// type (checker-guaranteed), so
|
||||||
|
// callsretsize gives cstage's
|
||||||
|
// cg_sret_retsize(ft) verdict.
|
||||||
|
if (callsretsize(c, n.rhs) > 0) {
|
||||||
|
let mas: str = "assign-resolver: sret call into aggregate field unwired (#234-tail/rule-7)\n";
|
||||||
|
os.write(2, mas.ptr, mas.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
let ma24: str = "assign-resolver: call result into aggregate field unwired (task #24/rule-7)\n";
|
||||||
|
os.write(2, ma24.ptr, ma24.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
let placed: bool = false;
|
||||||
|
let isslit: bool = false;
|
||||||
|
if (n.rhs != nil) {
|
||||||
|
if (n.rhs.kind == nkind.N_STRUCTLIT
|
||||||
|
&& fu.kind == tykind.TY_STRUCT) {
|
||||||
|
isslit = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (isslit) {
|
||||||
|
// @placescr — FRESH slot PER USE (the
|
||||||
|
// @slicescr discipline via localalloc,
|
||||||
|
// NOT the cached @tagscr table: a
|
||||||
|
// cached slot is the #31 multi-live
|
||||||
|
// corruption trap; rob ruling). Funnel
|
||||||
|
// contract, #44 discipline: this arm is
|
||||||
|
// the ONLY @placescr alloc site. Fill
|
||||||
|
// handles nested literals (#18), tagged
|
||||||
|
// fields, TK_ELLIPSIS autofill; the
|
||||||
|
// value sits in memory, so the resolver
|
||||||
|
// below may clobber AX/CX freely.
|
||||||
|
let sname: str;
|
||||||
|
sname.ptr = nil; sname.len = 0;
|
||||||
|
if (ft.kind == tykind.TY_NAMED) {
|
||||||
|
sname = ft.name;
|
||||||
|
};
|
||||||
|
let si: *structinfo = nil;
|
||||||
|
if (sname.len > 0) {
|
||||||
|
si = structlookup(c, sname);
|
||||||
|
};
|
||||||
|
if (si == nil) {
|
||||||
|
// wwstage-only bail: the fill is
|
||||||
|
// structinfo-keyed, so an anonymous-
|
||||||
|
// struct field type has no registry
|
||||||
|
// entry (cstage fills from Type
|
||||||
|
// directly). Loud, rule 7.
|
||||||
|
let man: str = "assign-resolver: structlit field layout unresolved (rule-7)\n";
|
||||||
|
os.write(2, man.ptr, man.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
let scr: i32 = localalloc(c, "@placescr", fsz, nil);
|
||||||
|
cgstructlitfillbp(c, si, n.rhs, scr);
|
||||||
|
placed = cgplaceaddr(c, lhs, "BX");
|
||||||
|
if (placed) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(scr: i64);
|
||||||
|
emitline("(BP), SI\n");
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Addressable source — ident / global /
|
||||||
|
// N_DOT chain / deref — via the closed
|
||||||
|
// #265/#268 dispatch. Its N_INDEX arm
|
||||||
|
// clobbers BX, so the dest spills around
|
||||||
|
// it (the #270-1b order). Literal
|
||||||
|
// arrays/tuples have no storage address
|
||||||
|
// and stay loud.
|
||||||
|
placed = cgplaceaddr(c, lhs, "BX");
|
||||||
|
if (placed) {
|
||||||
|
emitline("\tPUSHQ\tBX\n");
|
||||||
|
if (!aggargsrcaddr(c, n.rhs, "SI")) {
|
||||||
|
let mar: str = "assign-resolver: aggregate rhs shape unwired (rule-7)\n";
|
||||||
|
os.write(2, mar.ptr, mar.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
emitline("\tPOPQ\tBX\n");
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (!placed) {
|
||||||
|
let mau: str = "unsupported assign target shape\n";
|
||||||
|
os.write(2, mau.ptr, mau.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
let kc: i32 = 0;
|
||||||
|
for (kc + 8 <= fsz) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(BX)\n");
|
||||||
|
kc += 8;
|
||||||
|
};
|
||||||
|
if (kc + 4 <= fsz) {
|
||||||
|
emitline("\tMOVL\t");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVL\tAX, ");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(BX)\n");
|
||||||
|
kc += 4;
|
||||||
|
};
|
||||||
|
if (kc + 2 <= fsz) {
|
||||||
|
emitline("\tMOVW\t");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVW\tAX, ");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(BX)\n");
|
||||||
|
kc += 2;
|
||||||
|
};
|
||||||
|
if (kc + 1 <= fsz) {
|
||||||
|
emitline("\tMOVB\t");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVB\tAX, ");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(BX)\n");
|
||||||
|
kc += 1;
|
||||||
|
};
|
||||||
|
return;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
let fstrsl: bool = false;
|
let fstrsl: bool = false;
|
||||||
|
|||||||
@@ -14,10 +14,15 @@
|
|||||||
* selfhost/cmd/wcc/cgenexpr.ww); each call-site keeps its own
|
* selfhost/cmd/wcc/cgenexpr.ww); each call-site keeps its own
|
||||||
* load/store emission, and every N_DOT lvalue the resolver can't
|
* load/store emission, and every N_DOT lvalue the resolver can't
|
||||||
* address now dies LOUD ("unsupported assign target shape") instead of
|
* address now dies LOUD ("unsupported assign target shape") instead of
|
||||||
* silently dropping (rule 7). Field kinds the resolver arm does not
|
* silently dropping (rule 7). C1.25 (#23) wires the aggregate field
|
||||||
* wire yet (float / tagged / aggregate / str-slice compound) hard-stop
|
* STORE on top of the resolver: literal rhs materialises into a FRESH
|
||||||
* with their own diagnostics — the reject rows pin the exact text on
|
* per-use @placescr slot then word-copies to the resolved address;
|
||||||
* BOTH stages. Non-DOT lvalue tail residue is task #22.
|
* addressable rhs (ident/global/dot/deref) takes its source address
|
||||||
|
* straight from the #265/#268 dispatch. Field kinds the resolver arm
|
||||||
|
* does not wire yet (float / tagged / aggregate-compound / str-slice
|
||||||
|
* compound / call-rhs into aggregate) hard-stop with their own
|
||||||
|
* diagnostics — the reject rows pin the exact text on BOTH stages.
|
||||||
|
* Non-DOT lvalue tail residue is task #22.
|
||||||
*
|
*
|
||||||
* row | shape | want
|
* row | shape | want
|
||||||
* --------------------+----------------------------------------+------
|
* --------------------+----------------------------------------+------
|
||||||
@@ -36,7 +41,17 @@
|
|||||||
* neutral_ident_bases | x.f / a[i].f / p.f = and += (untouched | 51
|
* neutral_ident_bases | x.f / a[i].f / p.f = and += (untouched | 51
|
||||||
* | enumerated arms — runtime-pins the |
|
* | enumerated arms — runtime-pins the |
|
||||||
* | resolver's asm-neutrality claim) |
|
* | resolver's asm-neutrality claim) |
|
||||||
* reject_aggregate | struct-typed field store | BUILD_FAIL
|
* agg_structlit_40b | C1.25: 40B capture literal store via | 52
|
||||||
|
* | fresh @placescr + word-copy, incl ... |
|
||||||
|
* agg_from_ident_deref| C1.25: aggregate from ident + *p rhs | 53
|
||||||
|
* agg_nested_lit | C1.25: nested struct literal (#18) | 54
|
||||||
|
* agg_array_field_odd | C1.25: [3]u8 field, MOVW/MOVB tails | 55
|
||||||
|
* agg_fresh_two_stores| C1.25: two same-size structlit stores | 56
|
||||||
|
* | in ONE fn — distinct @placescr slots |
|
||||||
|
* agg_array_field_movl| C1.25: [3]u32 field, MOVQ+MOVL tail | 57
|
||||||
|
* reject_agg_compound | compound on aggregate field | BUILD_FAIL
|
||||||
|
* reject_agg_call_sret| >24B call rhs (#234-tail) | BUILD_FAIL
|
||||||
|
* reject_agg_call_reg | ≤24B call rhs (task #24) | BUILD_FAIL
|
||||||
* reject_float | f64 field store | BUILD_FAIL
|
* reject_float | f64 field store | BUILD_FAIL
|
||||||
* reject_tagged | tagged-union field store | BUILD_FAIL
|
* reject_tagged | tagged-union field store | BUILD_FAIL
|
||||||
* reject_str_compound | (*ts)[i].name += — str/slice compound | BUILD_FAIL
|
* reject_str_compound | (*ts)[i].name += — str/slice compound | BUILD_FAIL
|
||||||
@@ -363,19 +378,245 @@ static const struct row rows[] = {
|
|||||||
/* The fold-2b 40B capture store (p7b/p7_composed) — wired in a
|
/* The fold-2b 40B capture store (p7b/p7_composed) — wired in a
|
||||||
* follow-up resolver commit; until then it must die LOUD, never
|
* follow-up resolver commit; until then it must die LOUD, never
|
||||||
* the pre-C1 silent drop. */
|
* the pre-C1 silent drop. */
|
||||||
{ "reject_aggregate",
|
/* C1.25 (#23): the run_thread 40B capture store, graduated from
|
||||||
|
* BUILD_FAIL (C1's loud boundary) to wired. Literal rhs goes
|
||||||
|
* through a FRESH per-use @placescr materialise + word-copy; the
|
||||||
|
* `...` setter pins the autofill zero loop; neighbour element +
|
||||||
|
* sibling field readbacks pin the stride and copy length. */
|
||||||
|
/* Readback is RAW bytes over ts.ptr (the 804 tagged_56b
|
||||||
|
* precedent): a depth-2 read behind an index (ts[1].cap.start)
|
||||||
|
* is the F4 walker gap (task #6) and link-fails today on the
|
||||||
|
* cgen.c:9038 global-leaf fallback — typed readbacks graduate
|
||||||
|
* with the C2 read-walker. Layout: t={pc@0,cap@8}, size 48;
|
||||||
|
* capture={content(str ptr@0,len@8,cap@16),start@24,end@32}.
|
||||||
|
* Elem1 base 48 → content.len byte 64, start 80, end 88;
|
||||||
|
* elem0 → 16/32/40. Stored values fit one byte. */
|
||||||
|
{ "agg_structlit_40b",
|
||||||
"package main;\n"
|
"package main;\n"
|
||||||
"type capture = struct { content: str, start: size, end: size };\n"
|
"type capture = struct { content: str, start: size, end: size };\n"
|
||||||
"type t = struct { pc: size, cap: capture };\n"
|
"type t = struct { pc: size, cap: capture };\n"
|
||||||
"fn setcap(ts: *[]t, i: size) void = {\n"
|
"fn setcap(ts: *[]t, i: size) void = {\n"
|
||||||
"\t(*ts)[i].cap = capture { content = \"x\", start = 1, end = 2 };\n"
|
"\t(*ts)[i].cap = capture { content = \"hit\", start = 2, end = 4 };\n"
|
||||||
"};\n"
|
"};\n"
|
||||||
|
"fn clearcap(ts: *[]t, i: size) void = {\n"
|
||||||
|
"\t(*ts)[i].cap = capture { content = \"z\", ... };\n"
|
||||||
|
"};\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet z: capture = capture { content = \"\", start = 0, end = 0 };\n"
|
||||||
|
"\tlet ts: []t = [];\n"
|
||||||
|
"\tappend(ts, t { pc = 7, cap = z });\n"
|
||||||
|
"\tappend(ts, t { pc = 8, cap = z });\n"
|
||||||
|
"\tsetcap(&ts, 1);\n"
|
||||||
|
"\tlet bp: *u8 = ts.ptr: *u8;\n"
|
||||||
|
"\tif (bp[64] != 3u8) { return 1; };\n"
|
||||||
|
"\tif (bp[80] != 2u8) { return 2; };\n"
|
||||||
|
"\tif (bp[88] != 4u8) { return 3; };\n"
|
||||||
|
"\tif (ts[1].pc != 8) { return 4; };\n"
|
||||||
|
"\tif (bp[16] != 0u8) { return 5; };\n"
|
||||||
|
"\tif (ts[0].pc != 7) { return 6; };\n"
|
||||||
|
"\tclearcap(&ts, 1);\n"
|
||||||
|
"\tif (bp[64] != 1u8) { return 7; };\n"
|
||||||
|
"\tif (bp[80] != 0u8) { return 8; };\n"
|
||||||
|
"\tif (bp[88] != 0u8) { return 9; };\n"
|
||||||
|
"\treturn 52;\n"
|
||||||
|
"};\n",
|
||||||
|
52, NULL },
|
||||||
|
|
||||||
|
/* Addressable aggregate sources via the #265/#268 dispatch:
|
||||||
|
* local ident (LEAQ slot) and deref (*pc). Raw-byte readback per
|
||||||
|
* the agg_structlit_40b layout note (F4 blocks typed depth-2). */
|
||||||
|
{ "agg_from_ident_deref",
|
||||||
|
"package main;\n"
|
||||||
|
"type capture = struct { content: str, start: size, end: size };\n"
|
||||||
|
"type t = struct { pc: size, cap: capture };\n"
|
||||||
|
"fn copycap(ts: *[]t, i: size, src: capture) void = {\n"
|
||||||
|
"\t(*ts)[i].cap = src;\n"
|
||||||
|
"};\n"
|
||||||
|
"fn derefcap(ts: *[]t, i: size, pc: *capture) void = {\n"
|
||||||
|
"\t(*ts)[i].cap = *pc;\n"
|
||||||
|
"};\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet z: capture = capture { content = \"\", start = 0, end = 0 };\n"
|
||||||
|
"\tlet ts: []t = [];\n"
|
||||||
|
"\tappend(ts, t { pc = 7, cap = z });\n"
|
||||||
|
"\tappend(ts, t { pc = 8, cap = z });\n"
|
||||||
|
"\tlet a: capture = capture { content = \"ab\", start = 1, end = 3 };\n"
|
||||||
|
"\tcopycap(&ts, 1, a);\n"
|
||||||
|
"\tlet bp: *u8 = ts.ptr: *u8;\n"
|
||||||
|
"\tif (bp[64] != 2u8) { return 1; };\n"
|
||||||
|
"\tif (bp[80] != 1u8) { return 2; };\n"
|
||||||
|
"\tif (bp[88] != 3u8) { return 3; };\n"
|
||||||
|
"\tlet b: capture = capture { content = \"wxyz\", start = 5, end = 9 };\n"
|
||||||
|
"\tderefcap(&ts, 0, &b);\n"
|
||||||
|
"\tif (bp[16] != 4u8) { return 4; };\n"
|
||||||
|
"\tif (bp[32] != 5u8) { return 5; };\n"
|
||||||
|
"\tif (bp[40] != 9u8) { return 6; };\n"
|
||||||
|
"\tif (bp[80] != 1u8) { return 7; };\n"
|
||||||
|
"\treturn 53;\n"
|
||||||
|
"};\n",
|
||||||
|
53, NULL },
|
||||||
|
|
||||||
|
/* Nested struct-typed literal field inside the stored literal —
|
||||||
|
* the #18 recursion through cg_structlit_fill must land the inner
|
||||||
|
* bytes in the @placescr image before the copy. */
|
||||||
|
{ "agg_nested_lit",
|
||||||
|
"package main;\n"
|
||||||
|
"type inner = struct { x: i64, y: i64 };\n"
|
||||||
|
"type big = struct { a: i64, ib: inner };\n"
|
||||||
|
"type t = struct { pc: size, bg: big };\n"
|
||||||
|
"fn setbg(ts: *[]t, i: size) void = {\n"
|
||||||
|
"\t(*ts)[i].bg = big { a = 1, ib = inner { x = 2, y = 3 } };\n"
|
||||||
|
"};\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet z: big = big { a = 0, ib = inner { x = 0, y = 0 } };\n"
|
||||||
|
"\tlet ts: []t = [];\n"
|
||||||
|
"\tappend(ts, t { pc = 7, bg = z });\n"
|
||||||
|
"\tsetbg(&ts, 0);\n"
|
||||||
|
"\tlet bp: *u8 = ts.ptr: *u8;\n"
|
||||||
|
"\tif (bp[8] != 1u8) { return 1; };\n"
|
||||||
|
"\tif (bp[16] != 2u8) { return 2; };\n"
|
||||||
|
"\tif (bp[24] != 3u8) { return 3; };\n"
|
||||||
|
"\tif (ts[0].pc != 7) { return 4; };\n"
|
||||||
|
"\treturn 54;\n"
|
||||||
|
"};\n",
|
||||||
|
54, NULL },
|
||||||
|
|
||||||
|
/* [3]u8 array field from an ident source — TY_ARRAY aggregate arm
|
||||||
|
* + the MOVW/MOVB copy tails (fsz=3). */
|
||||||
|
{ "agg_array_field_odd",
|
||||||
|
"package main;\n"
|
||||||
|
"type t = struct { pc: size, arr: [3]u8 };\n"
|
||||||
|
"fn setarr(ts: *[]t, i: size, src: [3]u8) void = {\n"
|
||||||
|
"\t(*ts)[i].arr = src;\n"
|
||||||
|
"};\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet a3: [3]u8 = [9u8, 8u8, 7u8];\n"
|
||||||
|
"\tlet z3: [3]u8 = [0u8, 0u8, 0u8];\n"
|
||||||
|
"\tlet ts: []t = [];\n"
|
||||||
|
"\tappend(ts, t { pc = 5, arr = z3 });\n"
|
||||||
|
"\tsetarr(&ts, 0, a3);\n"
|
||||||
|
"\tif (ts[0].arr[0] != 9u8) { return 1; };\n"
|
||||||
|
"\tif (ts[0].arr[1] != 8u8) { return 2; };\n"
|
||||||
|
"\tif (ts[0].arr[2] != 7u8) { return 3; };\n"
|
||||||
|
"\tif (ts[0].pc != 5) { return 4; };\n"
|
||||||
|
"\treturn 55;\n"
|
||||||
|
"};\n",
|
||||||
|
55, NULL },
|
||||||
|
|
||||||
|
/* Two same-size structlit stores in ONE fn — each must get its
|
||||||
|
* own FRESH @placescr slot and both must land (the #31 multi-
|
||||||
|
* live discipline pin). The stronger shape — a structlit whose
|
||||||
|
* FIELD expr recurses into a same-size aggregate assign, the
|
||||||
|
* fresh-per-use rationale proper — is not constructible today:
|
||||||
|
* match-expr (the only stmt-carrying expr) is a parse reject in
|
||||||
|
* field position; fresh-per-use stays defensive (rob ruling). */
|
||||||
|
{ "agg_fresh_two_stores",
|
||||||
|
"package main;\n"
|
||||||
|
"type capture = struct { content: str, start: size, end: size };\n"
|
||||||
|
"type t = struct { pc: size, cap: capture };\n"
|
||||||
|
"fn settwo(ts: *[]t) void = {\n"
|
||||||
|
"\t(*ts)[0].cap = capture { content = \"aa\", start = 1, end = 2 };\n"
|
||||||
|
"\t(*ts)[1].cap = capture { content = \"bbb\", start = 3, end = 4 };\n"
|
||||||
|
"};\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet z: capture = capture { content = \"\", start = 0, end = 0 };\n"
|
||||||
|
"\tlet ts: []t = [];\n"
|
||||||
|
"\tappend(ts, t { pc = 7, cap = z });\n"
|
||||||
|
"\tappend(ts, t { pc = 8, cap = z });\n"
|
||||||
|
"\tsettwo(&ts);\n"
|
||||||
|
"\tlet bp: *u8 = ts.ptr: *u8;\n"
|
||||||
|
"\tif (bp[16] != 2u8) { return 1; };\n"
|
||||||
|
"\tif (bp[32] != 1u8) { return 2; };\n"
|
||||||
|
"\tif (bp[40] != 2u8) { return 3; };\n"
|
||||||
|
"\tif (bp[64] != 3u8) { return 4; };\n"
|
||||||
|
"\tif (bp[80] != 3u8) { return 5; };\n"
|
||||||
|
"\tif (bp[88] != 4u8) { return 6; };\n"
|
||||||
|
"\treturn 56;\n"
|
||||||
|
"};\n",
|
||||||
|
56, NULL },
|
||||||
|
|
||||||
|
/* [3]u32 field (fsz=12) — the MOVL copy tail, untouched by the
|
||||||
|
* 40B (all-MOVQ) and [3]u8 (MOVW+MOVB) rows.
|
||||||
|
*
|
||||||
|
* Matrix honesty, TY_TUPLE: the arm wires tuple fields and a
|
||||||
|
* local-ident tuple source stores correctly (cstage-verified),
|
||||||
|
* but a dual-stage row is blocked by three PRE-EXISTING tuple
|
||||||
|
* gaps outside this arm: by-value tuple param drops word 2 at
|
||||||
|
* runtime (both stages, master too), wwstage tuple-let init
|
||||||
|
* `let a: (i64,i64) = (3,4)` drops the word-2 store (cs≠ww),
|
||||||
|
* and `a.0 =` element assign is unwired (loud). Tuple-field row
|
||||||
|
* graduates with that family (filed). */
|
||||||
|
{ "agg_array_field_movl",
|
||||||
|
"package main;\n"
|
||||||
|
"type t = struct { pc: size, arr: [3]u32 };\n"
|
||||||
|
"fn setarr(ts: *[]t, i: size, src: [3]u32) void = {\n"
|
||||||
|
"\t(*ts)[i].arr = src;\n"
|
||||||
|
"};\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet a3: [3]u32 = [9u32, 8u32, 7u32];\n"
|
||||||
|
"\tlet z3: [3]u32 = [0u32, 0u32, 0u32];\n"
|
||||||
|
"\tlet ts: []t = [];\n"
|
||||||
|
"\tappend(ts, t { pc = 5, arr = z3 });\n"
|
||||||
|
"\tsetarr(&ts, 0, a3);\n"
|
||||||
|
"\tif (ts[0].arr[0] != 9u32) { return 1; };\n"
|
||||||
|
"\tif (ts[0].arr[1] != 8u32) { return 2; };\n"
|
||||||
|
"\tif (ts[0].arr[2] != 7u32) { return 3; };\n"
|
||||||
|
"\tif (ts[0].pc != 5) { return 4; };\n"
|
||||||
|
"\treturn 57;\n"
|
||||||
|
"};\n",
|
||||||
|
57, NULL },
|
||||||
|
|
||||||
|
/* Compound on an aggregate field is meaningless — stays loud. */
|
||||||
|
{ "reject_agg_compound",
|
||||||
|
"package main;\n"
|
||||||
|
"type capture = struct { content: str, start: size, end: size };\n"
|
||||||
|
"type t = struct { pc: size, cap: capture };\n"
|
||||||
|
"fn addcap(ts: *[]t, i: size, src: capture) void = {\n"
|
||||||
|
"\t(*ts)[i].cap += src;\n"
|
||||||
|
"};\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet ts: []t = [];\n"
|
||||||
|
"\tlet a: capture = capture { content = \"\", start = 0, end = 0 };\n"
|
||||||
|
"\taddcap(&ts, 0, a);\n"
|
||||||
|
"\treturn 0;\n"
|
||||||
|
"};\n",
|
||||||
|
BUILD_FAIL,
|
||||||
|
"assign-resolver: compound on aggregate field not wired (rule-7)" },
|
||||||
|
|
||||||
|
/* sret-class call rhs (>24B return) needs a runtime-RDI dest —
|
||||||
|
* the #234-tail deferral. */
|
||||||
|
{ "reject_agg_call_sret",
|
||||||
|
"package main;\n"
|
||||||
|
"type capture = struct { content: str, start: size, end: size };\n"
|
||||||
|
"type t = struct { pc: size, cap: capture };\n"
|
||||||
|
"fn mk() capture = {\n"
|
||||||
|
"\treturn capture { content = \"x\", start = 1, end = 2 };\n"
|
||||||
|
"};\n"
|
||||||
|
"fn setcap(ts: *[]t, i: size) void = { (*ts)[i].cap = mk(); };\n"
|
||||||
"export fn main() i32 = {\n"
|
"export fn main() i32 = {\n"
|
||||||
"\tlet ts: []t = [];\n"
|
"\tlet ts: []t = [];\n"
|
||||||
"\tsetcap(&ts, 0);\n"
|
"\tsetcap(&ts, 0);\n"
|
||||||
"\treturn 0;\n"
|
"\treturn 0;\n"
|
||||||
"};\n",
|
"};\n",
|
||||||
BUILD_FAIL, "assign-resolver: aggregate field not wired (rule-7)" },
|
BUILD_FAIL,
|
||||||
|
"assign-resolver: sret call into aggregate field unwired "
|
||||||
|
"(#234-tail/rule-7)" },
|
||||||
|
|
||||||
|
/* ≤24B reg-return call rhs — deferred, task #24. */
|
||||||
|
{ "reject_agg_call_reg",
|
||||||
|
"package main;\n"
|
||||||
|
"type pair = struct { a: i64, b: i64 };\n"
|
||||||
|
"type t = struct { pc: size, pr: pair };\n"
|
||||||
|
"fn mk() pair = { return pair { a = 1, b = 2 }; };\n"
|
||||||
|
"fn setpr(ts: *[]t, i: size) void = { (*ts)[i].pr = mk(); };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet ts: []t = [];\n"
|
||||||
|
"\tsetpr(&ts, 0);\n"
|
||||||
|
"\treturn 0;\n"
|
||||||
|
"};\n",
|
||||||
|
BUILD_FAIL,
|
||||||
|
"assign-resolver: call result into aggregate field unwired "
|
||||||
|
"(task #24/rule-7)" },
|
||||||
|
|
||||||
{ "reject_float",
|
{ "reject_float",
|
||||||
"package main;\n"
|
"package main;\n"
|
||||||
|
|||||||
Reference in New Issue
Block a user