w6c+wwstage: aggregate let-init copy for ident-array/N_DOT/N_INDEX rhs (#268 fold-1b) — close addressable-rhs copy family
#265 fold-1 landed the deref-rhs aggregate copy as one slot→slot memcpy loop fed from a source address in SI. fold-1b adds the remaining addressable-rhs source-address setups, all routed into that SAME loop: - array IDENT `let c: [N]T = s` — LEAQ the source slot into SI. Pre-fix both stages truncated to the 8B scalar tail. - N_DOT field `let c: A = o.i` — cg_dotchain_addr / dotchainaddr (#253) lands &(o.i) in SI. Pre-fix truncated to 8B. - N_INDEX element `let c: A = a[i]` — the &base[i] spine (#252: scaled index + LEAQ base) lands the element address in SI. Pre-fix scalar-loaded the element address as a value → segfault. Size (the #254 non-slot-padded ABI extent) comes from the declared let type for every shape (lu->size / structabisize|tinfo.size), independent of the rhs; only the per-rhs address setup differs. The deref arm becomes one branch of the unified arm. Struct-IDENT keeps its own #32 slot-copy arm above (unchanged). With those, the whole addressable-rhs let-init-copy family is closed by construction: struct-ident / array- ident / deref / N_DOT / N_INDEX all full-copy, both stages byte-identical (rule-10). 949 gains 9 full-readback rows (every member written distinct + summed, so a partial copy fails): array-ident 16B/32B + 12B(MOVL)/11B(MOVW+MOVB) tails; N_DOT struct-field 16B + array-field 32B + 11B-tail struct field; N_INDEX struct element 16B/32B. The N_INDEX source array is populated through a `*inner` to `&a[i]` (the #135/#252 store path) because the array-of-struct element direct store (`a[i].m[j]=v` / `a[i]=s` / struct- array literal) segfaults on a SEPARATE pre-existing bug, reported alongside this fold. w6c+wwdump combined.ww regen (#110). 70/70 949, test-unit 241, sizelint, smoke green.
This commit is contained in:
138
cmd/w6c/cgen.c
138
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);
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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 }
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user