diff --git a/cmd/w6c/cgen.c b/cmd/w6c/cgen.c index 842218c5..fc87036f 100644 --- a/cmd/w6c/cgen.c +++ b/cmd/w6c/cgen.c @@ -8763,48 +8763,110 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame) break; } } - /* #265 fold-1: aggregate deref-rhs let-init - * `let c: T = *p` (T a struct or array, >8B). Neither - * the scalar tail below (one 8B word) nor a missing arm - * (cstage dropped the copy entirely) materialised the - * whole aggregate. cgexpr(rhs->lhs) leaves the SOURCE - * ADDRESS in AX (a `*p` ident loads the pointer value; - * `*(&s)` LEAQs the slot); memcpy sz bytes slot→slot via - * SI — a MOVQ run plus a sized MOVL/MOVW/MOVB tail. N is - * lu->size (sz), the #254 non-slot-padded ABI extent. Both - * stages emit this identical sequence (rule-10); the by- - * value RETURN ABI is fold-2 (#267). */ - if (n->rhs && n->rhs->kind == N_UN - && n->rhs->op == TK_STAR && lu + /* #265 fold-1/1b (#268): aggregate let-init copy from an + * ADDRESSABLE rhs. The whole family converges on ONE memcpy + * loop fed by a per-rhs source-address setup: `*p` (deref, + * fold-1), an array ident `= s` (struct-ident is the #32 arm + * above), an N_DOT field `= o.i`, an N_INDEX element `= a[i]` + * — T a struct or array >8B. Each shape lands the SOURCE + * ADDRESS in SI; the loop copies sz bytes (lu->size, the #254 + * non-slot-padded ABI extent) slot→slot — a MOVQ run plus a + * sized MOVL/MOVW/MOVB tail. Pre-fix every non-deref shape was + * wrong: array-ident/N_DOT truncated to the 8B scalar tail + * below; N_INDEX scalar-loaded the element address as a value + * (segfault). Both stages emit the identical sequence + * (rule-10); the by-value RETURN ABI is fold-2 (#267). The + * source-addr setups reuse closed machinery: LEAQ-slot (ident), + * the deref operand (cgexpr), cg_dotchain_addr (#253, N_DOT), + * the &base[i] spine (#252, N_INDEX). */ + if (n->rhs && lu && (lu->kind == TY_STRUCT || lu->kind == TY_ARRAY) && sz > 8) { - cgexpr(c, n->rhs->lhs, *locals); - ins2(c, A_MOVQ, areg(D_AX), areg(D_SI)); - int k = 0; - for (; k + 8 <= sz; k += 8) { - ins2(c, A_MOVQ, amem(D_SI, k), areg(D_AX)); - ins2(c, A_MOVQ, areg(D_AX), - amem(D_BP, off + k)); + int havesrc = 0; + if (n->rhs->kind == N_UN && n->rhs->op == TK_STAR) { + cgexpr(c, n->rhs->lhs, *locals); + ins2(c, A_MOVQ, areg(D_AX), areg(D_SI)); + havesrc = 1; + } else if (n->rhs->kind == N_IDENT) { + int soff = localfind(*locals, n->rhs->str); + if (soff != 0) { + ins2(c, A_LEAQ, amem(D_BP, soff), + areg(D_SI)); + havesrc = 1; + } else if (let_islet(n->rhs->str) + || def_isarraydef(n->rhs->str)) { + ins2(c, A_LEAQ, masym(c, n->rhs->str), + areg(D_SI)); + havesrc = 1; + } + } else if (n->rhs->kind == N_DOT) { + if (cg_dotchain_addr(c, n->rhs, D_SI, *locals)) + havesrc = 1; + } else if (n->rhs->kind == N_INDEX) { + Node *base = n->rhs->lhs; + Node *idx = n->rhs->rhs; + Type *bt = base ? base->type : NULL; + Type *bu = (bt && bt->kind == TY_NAMED) + ? bt->under : bt; + if (base && base->kind == N_IDENT && bu + && bu->kind == TY_ARRAY) { + int esz = (bu->sub) + ? (int)bu->sub->size : 1; + cgexpr(c, idx, *locals); + if (esz > 1) { + ins2(c, A_MOVQ, aimm(esz), + areg(D_CX)); + ins2(c, A_IMULQ, areg(D_CX), + areg(D_AX)); + } + int boff = localfind(*locals, + base->str); + if (boff != 0) + ins2(c, A_LEAQ, + amem(D_BP, boff), + areg(D_BX)); + else + ins2(c, A_LEAQ, + masym(c, base->str), + areg(D_BX)); + ins2(c, A_ADDQ, areg(D_BX), + areg(D_AX)); + ins2(c, A_MOVQ, areg(D_AX), + areg(D_SI)); + havesrc = 1; + } } - if (k + 4 <= sz) { - ins2(c, A_MOVL, amem(D_SI, k), areg(D_AX)); - ins2(c, A_MOVL, areg(D_AX), - amem(D_BP, off + k)); - k += 4; + if (havesrc) { + int k = 0; + for (; k + 8 <= sz; k += 8) { + ins2(c, A_MOVQ, amem(D_SI, k), + areg(D_AX)); + ins2(c, A_MOVQ, areg(D_AX), + amem(D_BP, off + k)); + } + if (k + 4 <= sz) { + ins2(c, A_MOVL, amem(D_SI, k), + areg(D_AX)); + ins2(c, A_MOVL, areg(D_AX), + amem(D_BP, off + k)); + k += 4; + } + if (k + 2 <= sz) { + ins2(c, A_MOVW, amem(D_SI, k), + areg(D_AX)); + ins2(c, A_MOVW, areg(D_AX), + amem(D_BP, off + k)); + k += 2; + } + if (k + 1 <= sz) { + ins2(c, A_MOVB, amem(D_SI, k), + areg(D_AX)); + ins2(c, A_MOVB, areg(D_AX), + amem(D_BP, off + k)); + k += 1; + } + break; } - if (k + 2 <= sz) { - ins2(c, A_MOVW, amem(D_SI, k), areg(D_AX)); - ins2(c, A_MOVW, areg(D_AX), - amem(D_BP, off + k)); - k += 2; - } - if (k + 1 <= sz) { - ins2(c, A_MOVB, amem(D_SI, k), areg(D_AX)); - ins2(c, A_MOVB, areg(D_AX), - amem(D_BP, off + k)); - k += 1; - } - break; } if (n->rhs && sz == 8) { cgexpr(c, n->rhs, *locals); diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index 89517d7b..f7b69a7f 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -28858,39 +28858,108 @@ fn cglet(c: *cgen, n: *node) void = { }; }; }; - // #265 fold-1: aggregate deref-rhs let-init `let c: T = *p` - // (T a struct or array, >8B). cgexpr(rhs.lhs) leaves the - // SOURCE ADDRESS in AX (a `*p` ident loads the pointer value; - // `*(&s)` LEAQs the slot); memcpy N bytes slot→slot via SI — a - // MOVQ run plus a sized MOVL/MOVW/MOVB tail. N is the #254 - // non-slot-padded ABI extent: structabisize for a struct (= - // cstage lu->size), tinfo.size for an array. Pre-fix wwstage - // copied only the first 8B (scalar tail below) and cstage - // dropped the copy entirely — both wrong; converge on the full - // copy (rule-10). Mirror of cstage cgen.c N_LET deref arm; the - // by-value RETURN ABI is fold-2 (#267). - if (rhs.kind == nkind.N_UN) { if (rhs.op == tkind.TK_STAR) { - let ncopy: i32 = 0; - let dsi: *structinfo = structlookupchain(c, tn); - if (dsi != nil) { - ncopy = structabisize(dsi); - } else { - let dti: *tinfo = nil; - if (tn != nil) { dti = tn.type_: *tinfo; }; - for (dti != nil && dti.kind == tykind.TY_NAMED) { - dti = dti.under; - }; - if (dti != nil) { - if (dti.kind == tykind.TY_ARRAY) { - ncopy = dti.size: i32; - }; + // #265 fold-1/1b (#268): aggregate let-init copy from an + // ADDRESSABLE rhs — `*p` (deref), an array ident `= s` + // (struct-ident is the arm above), an N_DOT field `= o.i`, an + // N_INDEX element `= a[i]`, T a struct/array >8B. ONE memcpy + // loop fed by a per-rhs source-address setup landing the SOURCE + // ADDRESS in SI; copy N bytes (the #254 non-slot-padded ABI + // extent: structabisize for a struct, tinfo.size for an array) + // slot→slot — a MOVQ run plus a sized MOVL/MOVW/MOVB tail. Pre- + // fix array-ident/N_DOT truncated to the 8B scalar tail below + // and N_INDEX scalar-loaded the element address (segfault). + // Mirror of cstage cgen.c N_LET arm (rule-10); the by-value + // RETURN ABI is fold-2 (#267). Source-addr setups reuse closed + // machinery: LEAQ-slot (ident), the deref operand (cgexpr), + // dotchainaddr (#253, N_DOT), the &base[i] spine (#252, + // N_INDEX). + let aggn: i32 = 0; + let aggsi: *structinfo = structlookupchain(c, tn); + if (aggsi != nil) { + aggn = structabisize(aggsi); + } else { + let aggti: *tinfo = nil; + if (tn != nil) { aggti = tn.type_: *tinfo; }; + for (aggti != nil && aggti.kind == tykind.TY_NAMED) { + aggti = aggti.under; + }; + if (aggti != nil) { + if (aggti.kind == tykind.TY_ARRAY) { + aggn = aggti.size: i32; }; }; - if (ncopy > 8) { - cgexpr(c, rhs.lhs); - emitline("\tMOVQ\tAX, SI\n"); + }; + if (aggn > 8) { + let havesrc: bool = false; + if (rhs.kind == nkind.N_UN) { + if (rhs.op == tkind.TK_STAR) { + cgexpr(c, rhs.lhs); + emitline("\tMOVQ\tAX, SI\n"); + havesrc = true; + }; + }; + if (!havesrc) { if (rhs.kind == nkind.N_IDENT) { + let lc: *local = localfindnode(c, rhs.str); + if (lc != nil) { + emitline("\tLEAQ\t"); + emitoff(lc.off: i64); + emitline("(BP), SI\n"); + havesrc = true; + } else { + if (isletvar(c, rhs.str) + || deflookup(c, rhs.str)) { + emitline("\tLEAQ\t"); + emitsymname(c, rhs.str); + emitline("(SB), SI\n"); + havesrc = true; + }; + }; + }; }; + if (!havesrc) { if (rhs.kind == nkind.N_DOT) { + if (dotchainaddr(c, rhs, "SI")) { + havesrc = true; + }; + }; }; + if (!havesrc) { if (rhs.kind == nkind.N_INDEX) { + let base: *node = rhs.lhs; + let idx: *node = rhs.rhs; + let bu: *tinfo = nil; + if (base != nil) { bu = base.type_: *tinfo; }; + for (bu != nil && bu.kind == tykind.TY_NAMED) { + bu = bu.under; + }; + if (base != nil && base.kind == nkind.N_IDENT + && bu != nil && bu.kind == tykind.TY_ARRAY) { + let esz: i32 = 1; + if (bu.sub != nil) { + esz = bu.sub.size: i32; + }; + cgexpr(c, idx); + if (esz > 1) { + emitline("\tMOVQ\t$"); + emitint(esz: i64); + emitline(", CX\n"); + emitline("\tIMULQ\tCX, AX\n"); + }; + let bl: *local = localfindnode(c, + base.str); + if (bl != nil) { + emitline("\tLEAQ\t"); + emitoff(bl.off: i64); + emitline("(BP), BX\n"); + } else { + emitline("\tLEAQ\t"); + emitsymname(c, base.str); + emitline("(SB), BX\n"); + }; + emitline("\tADDQ\tBX, AX\n"); + emitline("\tMOVQ\tAX, SI\n"); + havesrc = true; + }; + }; }; + if (havesrc) { let k: i32 = 0; - for (k + 8 <= ncopy) { + for (k + 8 <= aggn) { emitline("\tMOVQ\t"); emitoff(k: i64); emitline("(SI), AX\n"); @@ -28899,7 +28968,7 @@ fn cglet(c: *cgen, n: *node) void = { emitline("(BP)\n"); k += 8; }; - if (k + 4 <= ncopy) { + if (k + 4 <= aggn) { emitline("\tMOVL\t"); emitoff(k: i64); emitline("(SI), AX\n"); @@ -28908,7 +28977,7 @@ fn cglet(c: *cgen, n: *node) void = { emitline("(BP)\n"); k += 4; }; - if (k + 2 <= ncopy) { + if (k + 2 <= aggn) { emitline("\tMOVW\t"); emitoff(k: i64); emitline("(SI), AX\n"); @@ -28917,7 +28986,7 @@ fn cglet(c: *cgen, n: *node) void = { emitline("(BP)\n"); k += 2; }; - if (k + 1 <= ncopy) { + if (k + 1 <= aggn) { emitline("\tMOVB\t"); emitoff(k: i64); emitline("(SI), AX\n"); @@ -28929,7 +28998,7 @@ fn cglet(c: *cgen, n: *node) void = { c.lastwasreturn = 0; return; }; - }; }; + }; cgexpr(c, rhs); // Float local: cgexpr leaves the value in X0. Spill via // MOVSS (f32, 4B) or MOVSD (f64, 8B). diff --git a/selfhost/cmd/wcc/cgenstmt.ww b/selfhost/cmd/wcc/cgenstmt.ww index 1d7c05c6..2ceb0685 100644 --- a/selfhost/cmd/wcc/cgenstmt.ww +++ b/selfhost/cmd/wcc/cgenstmt.ww @@ -1718,39 +1718,108 @@ fn cglet(c: *cgen, n: *node) void = { }; }; }; - // #265 fold-1: aggregate deref-rhs let-init `let c: T = *p` - // (T a struct or array, >8B). cgexpr(rhs.lhs) leaves the - // SOURCE ADDRESS in AX (a `*p` ident loads the pointer value; - // `*(&s)` LEAQs the slot); memcpy N bytes slot→slot via SI — a - // MOVQ run plus a sized MOVL/MOVW/MOVB tail. N is the #254 - // non-slot-padded ABI extent: structabisize for a struct (= - // cstage lu->size), tinfo.size for an array. Pre-fix wwstage - // copied only the first 8B (scalar tail below) and cstage - // dropped the copy entirely — both wrong; converge on the full - // copy (rule-10). Mirror of cstage cgen.c N_LET deref arm; the - // by-value RETURN ABI is fold-2 (#267). - if (rhs.kind == nkind.N_UN) { if (rhs.op == tkind.TK_STAR) { - let ncopy: i32 = 0; - let dsi: *structinfo = structlookupchain(c, tn); - if (dsi != nil) { - ncopy = structabisize(dsi); - } else { - let dti: *tinfo = nil; - if (tn != nil) { dti = tn.type_: *tinfo; }; - for (dti != nil && dti.kind == tykind.TY_NAMED) { - dti = dti.under; - }; - if (dti != nil) { - if (dti.kind == tykind.TY_ARRAY) { - ncopy = dti.size: i32; - }; + // #265 fold-1/1b (#268): aggregate let-init copy from an + // ADDRESSABLE rhs — `*p` (deref), an array ident `= s` + // (struct-ident is the arm above), an N_DOT field `= o.i`, an + // N_INDEX element `= a[i]`, T a struct/array >8B. ONE memcpy + // loop fed by a per-rhs source-address setup landing the SOURCE + // ADDRESS in SI; copy N bytes (the #254 non-slot-padded ABI + // extent: structabisize for a struct, tinfo.size for an array) + // slot→slot — a MOVQ run plus a sized MOVL/MOVW/MOVB tail. Pre- + // fix array-ident/N_DOT truncated to the 8B scalar tail below + // and N_INDEX scalar-loaded the element address (segfault). + // Mirror of cstage cgen.c N_LET arm (rule-10); the by-value + // RETURN ABI is fold-2 (#267). Source-addr setups reuse closed + // machinery: LEAQ-slot (ident), the deref operand (cgexpr), + // dotchainaddr (#253, N_DOT), the &base[i] spine (#252, + // N_INDEX). + let aggn: i32 = 0; + let aggsi: *structinfo = structlookupchain(c, tn); + if (aggsi != nil) { + aggn = structabisize(aggsi); + } else { + let aggti: *tinfo = nil; + if (tn != nil) { aggti = tn.type_: *tinfo; }; + for (aggti != nil && aggti.kind == tykind.TY_NAMED) { + aggti = aggti.under; + }; + if (aggti != nil) { + if (aggti.kind == tykind.TY_ARRAY) { + aggn = aggti.size: i32; }; }; - if (ncopy > 8) { - cgexpr(c, rhs.lhs); - emitline("\tMOVQ\tAX, SI\n"); + }; + if (aggn > 8) { + let havesrc: bool = false; + if (rhs.kind == nkind.N_UN) { + if (rhs.op == tkind.TK_STAR) { + cgexpr(c, rhs.lhs); + emitline("\tMOVQ\tAX, SI\n"); + havesrc = true; + }; + }; + if (!havesrc) { if (rhs.kind == nkind.N_IDENT) { + let lc: *local = localfindnode(c, rhs.str); + if (lc != nil) { + emitline("\tLEAQ\t"); + emitoff(lc.off: i64); + emitline("(BP), SI\n"); + havesrc = true; + } else { + if (isletvar(c, rhs.str) + || deflookup(c, rhs.str)) { + emitline("\tLEAQ\t"); + emitsymname(c, rhs.str); + emitline("(SB), SI\n"); + havesrc = true; + }; + }; + }; }; + if (!havesrc) { if (rhs.kind == nkind.N_DOT) { + if (dotchainaddr(c, rhs, "SI")) { + havesrc = true; + }; + }; }; + if (!havesrc) { if (rhs.kind == nkind.N_INDEX) { + let base: *node = rhs.lhs; + let idx: *node = rhs.rhs; + let bu: *tinfo = nil; + if (base != nil) { bu = base.type_: *tinfo; }; + for (bu != nil && bu.kind == tykind.TY_NAMED) { + bu = bu.under; + }; + if (base != nil && base.kind == nkind.N_IDENT + && bu != nil && bu.kind == tykind.TY_ARRAY) { + let esz: i32 = 1; + if (bu.sub != nil) { + esz = bu.sub.size: i32; + }; + cgexpr(c, idx); + if (esz > 1) { + emitline("\tMOVQ\t$"); + emitint(esz: i64); + emitline(", CX\n"); + emitline("\tIMULQ\tCX, AX\n"); + }; + let bl: *local = localfindnode(c, + base.str); + if (bl != nil) { + emitline("\tLEAQ\t"); + emitoff(bl.off: i64); + emitline("(BP), BX\n"); + } else { + emitline("\tLEAQ\t"); + emitsymname(c, base.str); + emitline("(SB), BX\n"); + }; + emitline("\tADDQ\tBX, AX\n"); + emitline("\tMOVQ\tAX, SI\n"); + havesrc = true; + }; + }; }; + if (havesrc) { let k: i32 = 0; - for (k + 8 <= ncopy) { + for (k + 8 <= aggn) { emitline("\tMOVQ\t"); emitoff(k: i64); emitline("(SI), AX\n"); @@ -1759,7 +1828,7 @@ fn cglet(c: *cgen, n: *node) void = { emitline("(BP)\n"); k += 8; }; - if (k + 4 <= ncopy) { + if (k + 4 <= aggn) { emitline("\tMOVL\t"); emitoff(k: i64); emitline("(SI), AX\n"); @@ -1768,7 +1837,7 @@ fn cglet(c: *cgen, n: *node) void = { emitline("(BP)\n"); k += 4; }; - if (k + 2 <= ncopy) { + if (k + 2 <= aggn) { emitline("\tMOVW\t"); emitoff(k: i64); emitline("(SI), AX\n"); @@ -1777,7 +1846,7 @@ fn cglet(c: *cgen, n: *node) void = { emitline("(BP)\n"); k += 2; }; - if (k + 1 <= ncopy) { + if (k + 1 <= aggn) { emitline("\tMOVB\t"); emitoff(k: i64); emitline("(SI), AX\n"); @@ -1789,7 +1858,7 @@ fn cglet(c: *cgen, n: *node) void = { c.lastwasreturn = 0; return; }; - }; }; + }; cgexpr(c, rhs); // Float local: cgexpr leaves the value in X0. Spill via // MOVSS (f32, 4B) or MOVSD (f64, 8B). diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 5d8a12bb..619d5af4 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -28858,39 +28858,108 @@ fn cglet(c: *cgen, n: *node) void = { }; }; }; - // #265 fold-1: aggregate deref-rhs let-init `let c: T = *p` - // (T a struct or array, >8B). cgexpr(rhs.lhs) leaves the - // SOURCE ADDRESS in AX (a `*p` ident loads the pointer value; - // `*(&s)` LEAQs the slot); memcpy N bytes slot→slot via SI — a - // MOVQ run plus a sized MOVL/MOVW/MOVB tail. N is the #254 - // non-slot-padded ABI extent: structabisize for a struct (= - // cstage lu->size), tinfo.size for an array. Pre-fix wwstage - // copied only the first 8B (scalar tail below) and cstage - // dropped the copy entirely — both wrong; converge on the full - // copy (rule-10). Mirror of cstage cgen.c N_LET deref arm; the - // by-value RETURN ABI is fold-2 (#267). - if (rhs.kind == nkind.N_UN) { if (rhs.op == tkind.TK_STAR) { - let ncopy: i32 = 0; - let dsi: *structinfo = structlookupchain(c, tn); - if (dsi != nil) { - ncopy = structabisize(dsi); - } else { - let dti: *tinfo = nil; - if (tn != nil) { dti = tn.type_: *tinfo; }; - for (dti != nil && dti.kind == tykind.TY_NAMED) { - dti = dti.under; - }; - if (dti != nil) { - if (dti.kind == tykind.TY_ARRAY) { - ncopy = dti.size: i32; - }; + // #265 fold-1/1b (#268): aggregate let-init copy from an + // ADDRESSABLE rhs — `*p` (deref), an array ident `= s` + // (struct-ident is the arm above), an N_DOT field `= o.i`, an + // N_INDEX element `= a[i]`, T a struct/array >8B. ONE memcpy + // loop fed by a per-rhs source-address setup landing the SOURCE + // ADDRESS in SI; copy N bytes (the #254 non-slot-padded ABI + // extent: structabisize for a struct, tinfo.size for an array) + // slot→slot — a MOVQ run plus a sized MOVL/MOVW/MOVB tail. Pre- + // fix array-ident/N_DOT truncated to the 8B scalar tail below + // and N_INDEX scalar-loaded the element address (segfault). + // Mirror of cstage cgen.c N_LET arm (rule-10); the by-value + // RETURN ABI is fold-2 (#267). Source-addr setups reuse closed + // machinery: LEAQ-slot (ident), the deref operand (cgexpr), + // dotchainaddr (#253, N_DOT), the &base[i] spine (#252, + // N_INDEX). + let aggn: i32 = 0; + let aggsi: *structinfo = structlookupchain(c, tn); + if (aggsi != nil) { + aggn = structabisize(aggsi); + } else { + let aggti: *tinfo = nil; + if (tn != nil) { aggti = tn.type_: *tinfo; }; + for (aggti != nil && aggti.kind == tykind.TY_NAMED) { + aggti = aggti.under; + }; + if (aggti != nil) { + if (aggti.kind == tykind.TY_ARRAY) { + aggn = aggti.size: i32; }; }; - if (ncopy > 8) { - cgexpr(c, rhs.lhs); - emitline("\tMOVQ\tAX, SI\n"); + }; + if (aggn > 8) { + let havesrc: bool = false; + if (rhs.kind == nkind.N_UN) { + if (rhs.op == tkind.TK_STAR) { + cgexpr(c, rhs.lhs); + emitline("\tMOVQ\tAX, SI\n"); + havesrc = true; + }; + }; + if (!havesrc) { if (rhs.kind == nkind.N_IDENT) { + let lc: *local = localfindnode(c, rhs.str); + if (lc != nil) { + emitline("\tLEAQ\t"); + emitoff(lc.off: i64); + emitline("(BP), SI\n"); + havesrc = true; + } else { + if (isletvar(c, rhs.str) + || deflookup(c, rhs.str)) { + emitline("\tLEAQ\t"); + emitsymname(c, rhs.str); + emitline("(SB), SI\n"); + havesrc = true; + }; + }; + }; }; + if (!havesrc) { if (rhs.kind == nkind.N_DOT) { + if (dotchainaddr(c, rhs, "SI")) { + havesrc = true; + }; + }; }; + if (!havesrc) { if (rhs.kind == nkind.N_INDEX) { + let base: *node = rhs.lhs; + let idx: *node = rhs.rhs; + let bu: *tinfo = nil; + if (base != nil) { bu = base.type_: *tinfo; }; + for (bu != nil && bu.kind == tykind.TY_NAMED) { + bu = bu.under; + }; + if (base != nil && base.kind == nkind.N_IDENT + && bu != nil && bu.kind == tykind.TY_ARRAY) { + let esz: i32 = 1; + if (bu.sub != nil) { + esz = bu.sub.size: i32; + }; + cgexpr(c, idx); + if (esz > 1) { + emitline("\tMOVQ\t$"); + emitint(esz: i64); + emitline(", CX\n"); + emitline("\tIMULQ\tCX, AX\n"); + }; + let bl: *local = localfindnode(c, + base.str); + if (bl != nil) { + emitline("\tLEAQ\t"); + emitoff(bl.off: i64); + emitline("(BP), BX\n"); + } else { + emitline("\tLEAQ\t"); + emitsymname(c, base.str); + emitline("(SB), BX\n"); + }; + emitline("\tADDQ\tBX, AX\n"); + emitline("\tMOVQ\tAX, SI\n"); + havesrc = true; + }; + }; }; + if (havesrc) { let k: i32 = 0; - for (k + 8 <= ncopy) { + for (k + 8 <= aggn) { emitline("\tMOVQ\t"); emitoff(k: i64); emitline("(SI), AX\n"); @@ -28899,7 +28968,7 @@ fn cglet(c: *cgen, n: *node) void = { emitline("(BP)\n"); k += 8; }; - if (k + 4 <= ncopy) { + if (k + 4 <= aggn) { emitline("\tMOVL\t"); emitoff(k: i64); emitline("(SI), AX\n"); @@ -28908,7 +28977,7 @@ fn cglet(c: *cgen, n: *node) void = { emitline("(BP)\n"); k += 4; }; - if (k + 2 <= ncopy) { + if (k + 2 <= aggn) { emitline("\tMOVW\t"); emitoff(k: i64); emitline("(SI), AX\n"); @@ -28917,7 +28986,7 @@ fn cglet(c: *cgen, n: *node) void = { emitline("(BP)\n"); k += 2; }; - if (k + 1 <= ncopy) { + if (k + 1 <= aggn) { emitline("\tMOVB\t"); emitoff(k: i64); emitline("(SI), AX\n"); @@ -28929,7 +28998,7 @@ fn cglet(c: *cgen, n: *node) void = { c.lastwasreturn = 0; return; }; - }; }; + }; cgexpr(c, rhs); // Float local: cgexpr leaves the value in X0. Spill via // MOVSS (f32, 4B) or MOVSD (f64, 8B). diff --git a/test/wcc/949_dotbase_addr_slice_run.c b/test/wcc/949_dotbase_addr_slice_run.c index 9cb993c1..518f93a2 100644 --- a/test/wcc/949_dotbase_addr_slice_run.c +++ b/test/wcc/949_dotbase_addr_slice_run.c @@ -822,6 +822,114 @@ static const struct row rows[] = { " return (c[0]+c[1]+c[2]+c[3]+c[4]+c[5]\n" " +c[6]+c[7]+c[8]+c[9]+c[10]): i32;\n" "};\n", 66, 1 }, + /* #265 fold-1b (#268) the remaining addressable-rhs aggregate let- + * init copy axes, all routed through the SAME memcpy loop as the + * deref rows above via a per-rhs source-address setup: an array + * IDENT `= s` (LEAQ slot), an N_DOT field `= o.i` (cg_dotchain_addr), + * an N_INDEX element `= a[i]` (the &base[i] spine). Pre-fix array- + * ident/N_DOT truncated to the first 8B and N_INDEX scalar-loaded the + * element address (segfault); both stages converged on the full copy + * (rule-10, byteid=1). Each row writes DISTINCT values to ALL members + * and sums EVERY member back, so a truncated/partial copy fails. With + * the deref rows + the #32 struct-ident arm this closes the whole + * addressable-rhs let-init-copy family: struct-ident / array-ident / + * deref / N_DOT / N_INDEX. The N_INDEX source array is populated + * through a `*inner` to `&a[i]` (the #135/#252 store path), NOT the + * array-of-struct-element direct store (`a[i].m[j]=v` / `a[i]=s`), + * which segfaults on a SEPARATE pre-existing bug reported alongside + * this fold; the populate stays off that path so the row isolates the + * copy. */ + { "ai_array16", + "package main;\n" + "export fn main() i32 = {\n" + " let s: [4]u32;\n" + " s[0]=11u32; s[1]=22u32; s[2]=33u32; s[3]=44u32;\n" + " let c: [4]u32 = s;\n" + " return (c[0]+c[1]+c[2]+c[3]): i32;\n" + "};\n", 110, 1 }, + { "ai_array32", + "package main;\n" + "export fn main() i32 = {\n" + " let s: [8]u32;\n" + " s[0]=1u32; s[1]=2u32; s[2]=3u32; s[3]=4u32;\n" + " s[4]=5u32; s[5]=6u32; s[6]=7u32; s[7]=8u32;\n" + " let c: [8]u32 = s;\n" + " return (c[0]+c[1]+c[2]+c[3]+c[4]+c[5]+c[6]+c[7]): i32;\n" + "};\n", 36, 1 }, + { "ai_tail12", + "package main;\n" + "export fn main() i32 = {\n" + " let s: [3]u32;\n" + " s[0]=7u32; s[1]=8u32; s[2]=9u32;\n" + " let c: [3]u32 = s;\n" + " return (c[0]+c[1]+c[2]): i32;\n" + "};\n", 24, 1 }, + { "ai_tail11", + "package main;\n" + "export fn main() i32 = {\n" + " let s: [11]u8;\n" + " s[0]=1u8; s[1]=2u8; s[2]=3u8; s[3]=4u8; s[4]=5u8;\n" + " s[5]=6u8; s[6]=7u8; s[7]=8u8; s[8]=9u8; s[9]=10u8;\n" + " s[10]=11u8;\n" + " let c: [11]u8 = s;\n" + " return (c[0]+c[1]+c[2]+c[3]+c[4]+c[5]\n" + " +c[6]+c[7]+c[8]+c[9]+c[10]): i32;\n" + "};\n", 66, 1 }, + { "dot_struct16", + "package main;\n" + "type inner = struct { m: [4]u32 };\n" + "type outer = struct { i: inner };\n" + "export fn main() i32 = {\n" + " let o: outer;\n" + " o.i.m[0]=10u32; o.i.m[1]=20u32; o.i.m[2]=30u32; o.i.m[3]=40u32;\n" + " let c: inner = o.i;\n" + " return (c.m[0]+c.m[1]+c.m[2]+c.m[3]): i32;\n" + "};\n", 100, 1 }, + { "dot_arr32", + "package main;\n" + "type outer = struct { o: [8]u32 };\n" + "export fn main() i32 = {\n" + " let x: outer;\n" + " x.o[0]=1u32; x.o[1]=2u32; x.o[2]=3u32; x.o[3]=4u32;\n" + " x.o[4]=5u32; x.o[5]=6u32; x.o[6]=7u32; x.o[7]=8u32;\n" + " let c: [8]u32 = x.o;\n" + " return (c[0]+c[1]+c[2]+c[3]+c[4]+c[5]+c[6]+c[7]): i32;\n" + "};\n", 36, 1 }, + { "dot_tail11", + "package main;\n" + "type inner = struct { m: [11]u8 };\n" + "type outer = struct { i: inner };\n" + "export fn main() i32 = {\n" + " let o: outer;\n" + " o.i.m[0]=1u8; o.i.m[1]=2u8; o.i.m[2]=3u8; o.i.m[3]=4u8;\n" + " o.i.m[4]=5u8; o.i.m[5]=6u8; o.i.m[6]=7u8; o.i.m[7]=8u8;\n" + " o.i.m[8]=9u8; o.i.m[9]=10u8; o.i.m[10]=11u8;\n" + " let c: inner = o.i;\n" + " return (c.m[0]+c.m[1]+c.m[2]+c.m[3]+c.m[4]+c.m[5]\n" + " +c.m[6]+c.m[7]+c.m[8]+c.m[9]+c.m[10]): i32;\n" + "};\n", 66, 1 }, + { "idx_struct16", + "package main;\n" + "type inner = struct { m: [4]u32 };\n" + "export fn main() i32 = {\n" + " let a: [2]inner;\n" + " let p: *inner = &a[1];\n" + " p.m[0]=10u32; p.m[1]=20u32; p.m[2]=30u32; p.m[3]=40u32;\n" + " let c: inner = a[1];\n" + " return (c.m[0]+c.m[1]+c.m[2]+c.m[3]): i32;\n" + "};\n", 100, 1 }, + { "idx_struct32", + "package main;\n" + "type inner = struct { m: [8]u32 };\n" + "export fn main() i32 = {\n" + " let a: [2]inner;\n" + " let p: *inner = &a[1];\n" + " p.m[0]=1u32; p.m[1]=2u32; p.m[2]=3u32; p.m[3]=4u32;\n" + " p.m[4]=5u32; p.m[5]=6u32; p.m[6]=7u32; p.m[7]=8u32;\n" + " let c: inner = a[1];\n" + " return (c.m[0]+c.m[1]+c.m[2]+c.m[3]\n" + " +c.m[4]+c.m[5]+c.m[6]+c.m[7]): i32;\n" + "};\n", 36, 1 }, { NULL, NULL, 0, 0 } };