w6c+wwstage: &aggregate-array-element addressing + store/copy (#270-1)
The array-of-struct element store/copy family — one primitive (&(array element) for an AGGREGATE element, used as address, never deref/truncate) across three consumers. Both stages were symmetric-broken; converge on the runtime-correct full-address/full-copy (#263). (1a) `a[i].m[j] = v` (a:[N]struct) segfaulted: the `arr[i].field` arm computed &a[i] then DEREF'd it (loaded the struct's first 8 bytes as a value) for an `[N]T`-typed field → garbage base. Now an array-typed field of an array element leaves the field ADDRESS (the #135 read-side, applied to the array-element base). cgen.c arm + cgenexpr.ww cgdot N_INDEX-lhs branch. (1b) `a[i] = aggregateval` truncated the copy to an 8B MOVQ. New aggregate (struct/array/tuple >8B) element-store branch word-copies the element from the rhs source address (ident / N_DOT field / `*p` deref) — the WRITE-twin of the #268 let-init loop. cgen.c N_INDEX store + cgenexpr.ww cgassign. (3a) `let c = x.arr[i]` (N_DOT base) / `let c = a[i][j]` (nested) dropped the copy: the #268 let-init N_INDEX source-addr arm was N_IDENT-base- gated. Now computes &base[idx] via cg_dotbase_addr (N_DOT field) or the &abase[bidx] spine (nested N_IDENT-array base). cgen.c N_LET + cgenstmt.ww cglet. 949 rows: elemfield_store, elem_struct_store, elem_arr_store, letcopy_{dot,nest}_prim, letcopy_subarr (byteid=1); letcopy_{dot,nest}_ struct (byteid=0 — run-correct, byte-id blocked by the orthogonal value-nested-struct frame divergence #254). All 94 pass; test-unit 241 green.
This commit is contained in:
190
cmd/w6c/cgen.c
190
cmd/w6c/cgen.c
@@ -5020,6 +5020,108 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
cg_sret_dest_off = 0;
|
cg_sret_dest_off = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
/* #270-1b: aggregate (struct/array/tuple >8B) element
|
||||||
|
* STORE `a[i] = val`. The scalar store path below copies
|
||||||
|
* only the first 8 bytes (fldstoreop MOVQ) — a silent
|
||||||
|
* truncation. Compute &a[i] (dest) and the rhs SOURCE
|
||||||
|
* address, then word-copy esz bytes: the WRITE-twin of the
|
||||||
|
* #268 let-init copy loop. Source shapes mirror that loop
|
||||||
|
* (ident local/global, N_DOT field via cg_dotchain_addr,
|
||||||
|
* `*p` deref); a by-value call result is the deferred #271,
|
||||||
|
* so N_CALL/literal sources fall through unchanged. */
|
||||||
|
if ((is_arr || is_sl || is_ptr) && n->op == TK_ASSIGN
|
||||||
|
&& esubu && (esubu->kind == TY_STRUCT
|
||||||
|
|| esubu->kind == TY_ARRAY
|
||||||
|
|| esubu->kind == TY_TUPLE)
|
||||||
|
&& esz > 8
|
||||||
|
&& ((n->rhs->kind == N_IDENT)
|
||||||
|
|| (n->rhs->kind == N_DOT)
|
||||||
|
|| (n->rhs->kind == N_UN
|
||||||
|
&& n->rhs->op == TK_STAR))) {
|
||||||
|
/* dest &a[i] → BX */
|
||||||
|
cgexpr(c, n->lhs->rhs, locals); /* idx → AX */
|
||||||
|
if (esz > 1) {
|
||||||
|
ins2(c, A_MOVQ, aimm(esz), areg(D_CX));
|
||||||
|
ins2(c, A_IMULQ, areg(D_CX), areg(D_AX));
|
||||||
|
}
|
||||||
|
ins1(c, A_PUSHQ, areg(D_AX)); /* scaled idx */
|
||||||
|
if (base->kind == N_IDENT) {
|
||||||
|
int off = localfind(locals, base->str);
|
||||||
|
int isglobal = (off == 0)
|
||||||
|
&& let_islet(base->str);
|
||||||
|
if (isglobal && is_arr)
|
||||||
|
ins2(c, A_LEAQ,
|
||||||
|
masym(c, base->str),
|
||||||
|
areg(D_BX));
|
||||||
|
else if (isglobal)
|
||||||
|
ins2(c, A_MOVQ,
|
||||||
|
masym(c, base->str),
|
||||||
|
areg(D_BX));
|
||||||
|
else if (is_arr)
|
||||||
|
ins2(c, A_LEAQ,
|
||||||
|
amem(D_BP, off), areg(D_BX));
|
||||||
|
else
|
||||||
|
ins2(c, A_MOVQ,
|
||||||
|
amem(D_BP, off), areg(D_BX));
|
||||||
|
} else if (cg_dotbase_addr(c, base, D_BX, locals)) {
|
||||||
|
/* N_DOT array-field base resolved inline. */
|
||||||
|
} else {
|
||||||
|
cgexpr(c, base, locals);
|
||||||
|
ins2(c, A_MOVQ, areg(D_AX), areg(D_BX));
|
||||||
|
}
|
||||||
|
ins1(c, A_POPQ, areg(D_AX)); /* scaled idx */
|
||||||
|
ins2(c, A_ADDQ, areg(D_AX), areg(D_BX));
|
||||||
|
ins1(c, A_PUSHQ, areg(D_BX)); /* spill dest */
|
||||||
|
/* rhs source address → SI */
|
||||||
|
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));
|
||||||
|
} 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));
|
||||||
|
else
|
||||||
|
ins2(c, A_LEAQ,
|
||||||
|
masym(c, n->rhs->str),
|
||||||
|
areg(D_SI));
|
||||||
|
} else {
|
||||||
|
cg_dotchain_addr(c, n->rhs, D_SI, locals);
|
||||||
|
}
|
||||||
|
ins1(c, A_POPQ, areg(D_BX)); /* dest */
|
||||||
|
int k = 0;
|
||||||
|
for (; k + 8 <= esz; 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 <= esz) {
|
||||||
|
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 <= esz) {
|
||||||
|
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 <= esz) {
|
||||||
|
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 ((is_arr || is_sl || is_ptr) && n->op == TK_ASSIGN) {
|
if ((is_arr || is_sl || is_ptr) && n->op == TK_ASSIGN) {
|
||||||
cgexpr(c, n->rhs, locals); /* AX=ptr (BX=len,CX=cap if str) */
|
cgexpr(c, n->rhs, locals); /* AX=ptr (BX=len,CX=cap if str) */
|
||||||
/* str/slice: stash cap+len so all three store
|
/* str/slice: stash cap+len so all three store
|
||||||
@@ -8024,6 +8126,23 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
Type *ft = f->type;
|
Type *ft = f->type;
|
||||||
Type *fu = (ft && ft->kind == TY_NAMED)
|
Type *fu = (ft && ft->kind == TY_NAMED)
|
||||||
? ft->under : ft;
|
? ft->under : ft;
|
||||||
|
/* #270-1a: an `[N]T`-typed field of an
|
||||||
|
* array element (`a[i].m[j]`) — leave the
|
||||||
|
* field's ADDRESS, a base for the outer
|
||||||
|
* index, NEVER deref. AX holds &a[i]; the
|
||||||
|
* field address is &a[i]+foff. The #135
|
||||||
|
* read-side for `d.m[i]`, applied to an
|
||||||
|
* array-element base. Without this an array
|
||||||
|
* field fell to fldloadop below and loaded
|
||||||
|
* its first 8 bytes as a value → garbage
|
||||||
|
* base → SEGFAULT in the outer index. */
|
||||||
|
if (fu && fu->kind == TY_ARRAY) {
|
||||||
|
if (foff != 0)
|
||||||
|
ins2(c, A_ADDQ,
|
||||||
|
aimm(foff),
|
||||||
|
areg(D_AX));
|
||||||
|
goto dot_done;
|
||||||
|
}
|
||||||
if (fu && (fu->kind == TY_STR
|
if (fu && (fu->kind == TY_STR
|
||||||
|| fu->kind == TY_SLICE)) {
|
|| fu->kind == TY_SLICE)) {
|
||||||
/* str/slice: the 3-word {ptr,len,cap}
|
/* str/slice: the 3-word {ptr,len,cap}
|
||||||
@@ -8847,6 +8966,77 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
|||||||
ins2(c, A_MOVQ, areg(D_AX),
|
ins2(c, A_MOVQ, areg(D_AX),
|
||||||
areg(D_SI));
|
areg(D_SI));
|
||||||
havesrc = 1;
|
havesrc = 1;
|
||||||
|
} else if (base && (base->kind == N_DOT
|
||||||
|
|| base->kind == N_INDEX)) {
|
||||||
|
/* #270-3a: the index BASE is an N_DOT
|
||||||
|
* array-field (`x.arr[i]`) or a nested
|
||||||
|
* N_INDEX (`a[i][j]`); the N_IDENT-base arm
|
||||||
|
* above missed both, so the copy fell to the
|
||||||
|
* 8B truncation below. Compute &base[idx]:
|
||||||
|
* scaled idx on the stack, then &base via
|
||||||
|
* cg_dotbase_addr (N_DOT field address) or
|
||||||
|
* the &abase[bidx] spine (nested N_IDENT-
|
||||||
|
* array base), then add. */
|
||||||
|
int esz = (bu && 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));
|
||||||
|
}
|
||||||
|
ins1(c, A_PUSHQ, areg(D_AX));
|
||||||
|
int baseok = 0;
|
||||||
|
if (base->kind == N_DOT) {
|
||||||
|
baseok = cg_dotbase_addr(c, base,
|
||||||
|
D_AX, *locals);
|
||||||
|
} else {
|
||||||
|
Node *ab = base->lhs;
|
||||||
|
Node *bidx = base->rhs;
|
||||||
|
Type *abt = ab ? ab->type : NULL;
|
||||||
|
Type *abu = type_chase_named(abt);
|
||||||
|
if (ab && ab->kind == N_IDENT
|
||||||
|
&& abu
|
||||||
|
&& abu->kind == TY_ARRAY) {
|
||||||
|
int aesz = (abu->sub)
|
||||||
|
? (int)abu->sub->size
|
||||||
|
: 1;
|
||||||
|
cgexpr(c, bidx, *locals);
|
||||||
|
if (aesz > 1) {
|
||||||
|
ins2(c, A_MOVQ,
|
||||||
|
aimm(aesz),
|
||||||
|
areg(D_CX));
|
||||||
|
ins2(c, A_IMULQ,
|
||||||
|
areg(D_CX),
|
||||||
|
areg(D_AX));
|
||||||
|
}
|
||||||
|
int aoff = localfind(
|
||||||
|
*locals, ab->str);
|
||||||
|
if (aoff != 0)
|
||||||
|
ins2(c, A_LEAQ,
|
||||||
|
amem(D_BP,
|
||||||
|
aoff),
|
||||||
|
areg(D_BX));
|
||||||
|
else
|
||||||
|
ins2(c, A_LEAQ,
|
||||||
|
masym(c,
|
||||||
|
ab->str),
|
||||||
|
areg(D_BX));
|
||||||
|
ins2(c, A_ADDQ,
|
||||||
|
areg(D_BX),
|
||||||
|
areg(D_AX));
|
||||||
|
baseok = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ins1(c, A_POPQ, areg(D_BX));
|
||||||
|
if (baseok) {
|
||||||
|
ins2(c, A_ADDQ, areg(D_BX),
|
||||||
|
areg(D_AX));
|
||||||
|
ins2(c, A_MOVQ, areg(D_AX),
|
||||||
|
areg(D_SI));
|
||||||
|
havesrc = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (havesrc) {
|
if (havesrc) {
|
||||||
|
|||||||
@@ -21994,6 +21994,24 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
} else {
|
} else {
|
||||||
emitline("\tMOVQ\tBX, AX\n");
|
emitline("\tMOVQ\tBX, AX\n");
|
||||||
};
|
};
|
||||||
|
// #270-1a: an `[N]T`-typed field of an
|
||||||
|
// array element (`a[i].m[j]`) — leave the
|
||||||
|
// field's ADDRESS, a base for the outer
|
||||||
|
// index, NEVER deref. AX holds &a[i]; the
|
||||||
|
// field address is &a[i]+foff. The #135
|
||||||
|
// read-side for `d.m[i]`, applied to an
|
||||||
|
// array-element base. Without this an array
|
||||||
|
// field fell to fieldloadop below and loaded
|
||||||
|
// its first 8 bytes as a value → garbage
|
||||||
|
// base → SEGFAULT in the outer index.
|
||||||
|
if (tinfoisarray(fi.tnode.type_: *tinfo)) {
|
||||||
|
if (fi.foff != 0) {
|
||||||
|
emitline("\tADDQ\t$");
|
||||||
|
emitint(fi.foff: i64);
|
||||||
|
emitline(", AX\n");
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
};
|
||||||
if (isstrtype(c, fi.tnode) || isslicetype(c, fi.tnode)) {
|
if (isstrtype(c, fi.tnode) || isslicetype(c, fi.tnode)) {
|
||||||
// str/slice: the 3-word {ptr,len,cap}
|
// str/slice: the 3-word {ptr,len,cap}
|
||||||
// slice header (#1). AX holds the
|
// slice header (#1). AX holds the
|
||||||
@@ -24620,6 +24638,120 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
c.sretdestoff = 0;
|
c.sretdestoff = 0;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
// #270-1b: aggregate (struct/array/tuple >8B) element
|
||||||
|
// STORE `a[i] = val`. The scalar store path below copies
|
||||||
|
// only the first 8 bytes (tnodestoreop MOVQ) — a silent
|
||||||
|
// truncation. Compute &a[i] (dest) and the rhs SOURCE
|
||||||
|
// address, then word-copy esz bytes: the WRITE-twin of
|
||||||
|
// the #268 let-init copy loop. Source shapes mirror that
|
||||||
|
// loop (ident, N_DOT field via dotchainaddr, `*p`
|
||||||
|
// deref); a by-value call result is the deferred #271, so
|
||||||
|
// N_CALL/literal sources fall through unchanged. esz>8
|
||||||
|
// non-str/non-slice IS a struct/array/tuple here (the
|
||||||
|
// tagged element already returned above; floats are ≤8).
|
||||||
|
let aggsrc: bool = (n.rhs.kind == nkind.N_IDENT)
|
||||||
|
|| (n.rhs.kind == nkind.N_DOT)
|
||||||
|
|| (n.rhs.kind == nkind.N_UN
|
||||||
|
&& n.rhs.op == tkind.TK_STAR);
|
||||||
|
if (esz > 8 && !isstrtype(c, elemtn)
|
||||||
|
&& !isslicetype(c, elemtn) && aggsrc) {
|
||||||
|
cgexpr(c, idx); // idx → AX
|
||||||
|
if (esz > 1) {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(esz: i64);
|
||||||
|
emitline(", CX\n");
|
||||||
|
emitline("\tIMULQ\tCX, AX\n");
|
||||||
|
};
|
||||||
|
emitline("\tPUSHQ\tAX\n"); // scaled idx
|
||||||
|
if (isglobalarr) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, globalname);
|
||||||
|
emitline("(SB), BX\n");
|
||||||
|
} else { if (isglobalptr) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitsymname(c, globalname);
|
||||||
|
emitline("(SB), BX\n");
|
||||||
|
} else { if (baselocal != nil) {
|
||||||
|
let tn2: *node = baselocal.tnode;
|
||||||
|
let isarr2: bool = false;
|
||||||
|
if (tn2 != nil) { if (tn2.kind == nkind.N_TARRAY) { isarr2 = true; }; };
|
||||||
|
if (isarr2) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(baselocal.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(baselocal.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
};
|
||||||
|
} else { if (dotbaseaddr(c, base, "BX")) {
|
||||||
|
// N_DOT array-field base resolved inline.
|
||||||
|
} else {
|
||||||
|
cgexpr(c, base);
|
||||||
|
emitline("\tMOVQ\tAX, BX\n");
|
||||||
|
};};};};
|
||||||
|
emitline("\tPOPQ\tAX\n"); // scaled idx
|
||||||
|
emitline("\tADDQ\tAX, BX\n");
|
||||||
|
emitline("\tPUSHQ\tBX\n"); // spill dest
|
||||||
|
// rhs source address → SI
|
||||||
|
if (n.rhs.kind == nkind.N_UN
|
||||||
|
&& n.rhs.op == tkind.TK_STAR) {
|
||||||
|
cgexpr(c, n.rhs.lhs);
|
||||||
|
emitline("\tMOVQ\tAX, SI\n");
|
||||||
|
} else { if (n.rhs.kind == nkind.N_IDENT) {
|
||||||
|
let sl: *local = localfindnode(c, n.rhs.str);
|
||||||
|
if (sl != nil) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(sl.off: i64);
|
||||||
|
emitline("(BP), SI\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, n.rhs.str);
|
||||||
|
emitline("(SB), SI\n");
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
dotchainaddr(c, n.rhs, "SI");
|
||||||
|
};};
|
||||||
|
emitline("\tPOPQ\tBX\n"); // dest
|
||||||
|
let kc: i32 = 0;
|
||||||
|
for (kc + 8 <= esz) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(BX)\n");
|
||||||
|
kc += 8;
|
||||||
|
};
|
||||||
|
if (kc + 4 <= esz) {
|
||||||
|
emitline("\tMOVL\t");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVL\tAX, ");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(BX)\n");
|
||||||
|
kc += 4;
|
||||||
|
};
|
||||||
|
if (kc + 2 <= esz) {
|
||||||
|
emitline("\tMOVW\t");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVW\tAX, ");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(BX)\n");
|
||||||
|
kc += 2;
|
||||||
|
};
|
||||||
|
if (kc + 1 <= esz) {
|
||||||
|
emitline("\tMOVB\t");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVB\tAX, ");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(BX)\n");
|
||||||
|
kc += 1;
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
};
|
||||||
cgexpr(c, n.rhs); // value → AX
|
cgexpr(c, n.rhs); // value → AX
|
||||||
// str/slice: spill cap (CX) + len (BX) before
|
// str/slice: spill cap (CX) + len (BX) before
|
||||||
// computing the index so the post-index store can
|
// computing the index so the post-index store can
|
||||||
@@ -29186,6 +29318,76 @@ fn cglet(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tMOVQ\tAX, SI\n");
|
emitline("\tMOVQ\tAX, SI\n");
|
||||||
havesrc = true;
|
havesrc = true;
|
||||||
};
|
};
|
||||||
|
// #270-3a: the index BASE is an N_DOT
|
||||||
|
// array-field (`x.arr[i]`) or a nested N_INDEX
|
||||||
|
// (`a[i][j]`); the N_IDENT-base arm above missed
|
||||||
|
// both, so the copy fell to the 8B truncation
|
||||||
|
// below. Compute &base[idx]: scaled idx on the
|
||||||
|
// stack, then &base via dotbaseaddr (N_DOT field
|
||||||
|
// address) or the &abase[bidx] spine (nested
|
||||||
|
// N_IDENT-array base), then add.
|
||||||
|
if (!havesrc && base != nil
|
||||||
|
&& (base.kind == nkind.N_DOT
|
||||||
|
|| base.kind == nkind.N_INDEX)) {
|
||||||
|
let esz2: i32 = 1;
|
||||||
|
if (bu != nil && bu.sub != nil) {
|
||||||
|
esz2 = bu.sub.size: i32;
|
||||||
|
};
|
||||||
|
cgexpr(c, idx);
|
||||||
|
if (esz2 > 1) {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(esz2: i64);
|
||||||
|
emitline(", CX\n");
|
||||||
|
emitline("\tIMULQ\tCX, AX\n");
|
||||||
|
};
|
||||||
|
emitline("\tPUSHQ\tAX\n");
|
||||||
|
let baseok: bool = false;
|
||||||
|
if (base.kind == nkind.N_DOT) {
|
||||||
|
if (dotbaseaddr(c, base, "AX")) {
|
||||||
|
baseok = true;
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
let ab: *node = base.lhs;
|
||||||
|
let bidx: *node = base.rhs;
|
||||||
|
let abu: *tinfo = nil;
|
||||||
|
if (ab != nil) { abu = ab.type_: *tinfo; };
|
||||||
|
for (abu != nil && abu.kind == tykind.TY_NAMED) {
|
||||||
|
abu = abu.under;
|
||||||
|
};
|
||||||
|
if (ab != nil && ab.kind == nkind.N_IDENT
|
||||||
|
&& abu != nil && abu.kind == tykind.TY_ARRAY) {
|
||||||
|
let aesz: i32 = 1;
|
||||||
|
if (abu.sub != nil) {
|
||||||
|
aesz = abu.sub.size: i32;
|
||||||
|
};
|
||||||
|
cgexpr(c, bidx);
|
||||||
|
if (aesz > 1) {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(aesz: i64);
|
||||||
|
emitline(", CX\n");
|
||||||
|
emitline("\tIMULQ\tCX, AX\n");
|
||||||
|
};
|
||||||
|
let abl: *local = localfindnode(c, ab.str);
|
||||||
|
if (abl != nil) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(abl.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, ab.str);
|
||||||
|
emitline("(SB), BX\n");
|
||||||
|
};
|
||||||
|
emitline("\tADDQ\tBX, AX\n");
|
||||||
|
baseok = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
emitline("\tPOPQ\tBX\n");
|
||||||
|
if (baseok) {
|
||||||
|
emitline("\tADDQ\tBX, AX\n");
|
||||||
|
emitline("\tMOVQ\tAX, SI\n");
|
||||||
|
havesrc = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
}; };
|
}; };
|
||||||
if (havesrc) {
|
if (havesrc) {
|
||||||
let k: i32 = 0;
|
let k: i32 = 0;
|
||||||
|
|||||||
@@ -2496,6 +2496,24 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
} else {
|
} else {
|
||||||
emitline("\tMOVQ\tBX, AX\n");
|
emitline("\tMOVQ\tBX, AX\n");
|
||||||
};
|
};
|
||||||
|
// #270-1a: an `[N]T`-typed field of an
|
||||||
|
// array element (`a[i].m[j]`) — leave the
|
||||||
|
// field's ADDRESS, a base for the outer
|
||||||
|
// index, NEVER deref. AX holds &a[i]; the
|
||||||
|
// field address is &a[i]+foff. The #135
|
||||||
|
// read-side for `d.m[i]`, applied to an
|
||||||
|
// array-element base. Without this an array
|
||||||
|
// field fell to fieldloadop below and loaded
|
||||||
|
// its first 8 bytes as a value → garbage
|
||||||
|
// base → SEGFAULT in the outer index.
|
||||||
|
if (tinfoisarray(fi.tnode.type_: *tinfo)) {
|
||||||
|
if (fi.foff != 0) {
|
||||||
|
emitline("\tADDQ\t$");
|
||||||
|
emitint(fi.foff: i64);
|
||||||
|
emitline(", AX\n");
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
};
|
||||||
if (isstrtype(c, fi.tnode) || isslicetype(c, fi.tnode)) {
|
if (isstrtype(c, fi.tnode) || isslicetype(c, fi.tnode)) {
|
||||||
// str/slice: the 3-word {ptr,len,cap}
|
// str/slice: the 3-word {ptr,len,cap}
|
||||||
// slice header (#1). AX holds the
|
// slice header (#1). AX holds the
|
||||||
@@ -5122,6 +5140,120 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
c.sretdestoff = 0;
|
c.sretdestoff = 0;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
// #270-1b: aggregate (struct/array/tuple >8B) element
|
||||||
|
// STORE `a[i] = val`. The scalar store path below copies
|
||||||
|
// only the first 8 bytes (tnodestoreop MOVQ) — a silent
|
||||||
|
// truncation. Compute &a[i] (dest) and the rhs SOURCE
|
||||||
|
// address, then word-copy esz bytes: the WRITE-twin of
|
||||||
|
// the #268 let-init copy loop. Source shapes mirror that
|
||||||
|
// loop (ident, N_DOT field via dotchainaddr, `*p`
|
||||||
|
// deref); a by-value call result is the deferred #271, so
|
||||||
|
// N_CALL/literal sources fall through unchanged. esz>8
|
||||||
|
// non-str/non-slice IS a struct/array/tuple here (the
|
||||||
|
// tagged element already returned above; floats are ≤8).
|
||||||
|
let aggsrc: bool = (n.rhs.kind == nkind.N_IDENT)
|
||||||
|
|| (n.rhs.kind == nkind.N_DOT)
|
||||||
|
|| (n.rhs.kind == nkind.N_UN
|
||||||
|
&& n.rhs.op == tkind.TK_STAR);
|
||||||
|
if (esz > 8 && !isstrtype(c, elemtn)
|
||||||
|
&& !isslicetype(c, elemtn) && aggsrc) {
|
||||||
|
cgexpr(c, idx); // idx → AX
|
||||||
|
if (esz > 1) {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(esz: i64);
|
||||||
|
emitline(", CX\n");
|
||||||
|
emitline("\tIMULQ\tCX, AX\n");
|
||||||
|
};
|
||||||
|
emitline("\tPUSHQ\tAX\n"); // scaled idx
|
||||||
|
if (isglobalarr) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, globalname);
|
||||||
|
emitline("(SB), BX\n");
|
||||||
|
} else { if (isglobalptr) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitsymname(c, globalname);
|
||||||
|
emitline("(SB), BX\n");
|
||||||
|
} else { if (baselocal != nil) {
|
||||||
|
let tn2: *node = baselocal.tnode;
|
||||||
|
let isarr2: bool = false;
|
||||||
|
if (tn2 != nil) { if (tn2.kind == nkind.N_TARRAY) { isarr2 = true; }; };
|
||||||
|
if (isarr2) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(baselocal.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(baselocal.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
};
|
||||||
|
} else { if (dotbaseaddr(c, base, "BX")) {
|
||||||
|
// N_DOT array-field base resolved inline.
|
||||||
|
} else {
|
||||||
|
cgexpr(c, base);
|
||||||
|
emitline("\tMOVQ\tAX, BX\n");
|
||||||
|
};};};};
|
||||||
|
emitline("\tPOPQ\tAX\n"); // scaled idx
|
||||||
|
emitline("\tADDQ\tAX, BX\n");
|
||||||
|
emitline("\tPUSHQ\tBX\n"); // spill dest
|
||||||
|
// rhs source address → SI
|
||||||
|
if (n.rhs.kind == nkind.N_UN
|
||||||
|
&& n.rhs.op == tkind.TK_STAR) {
|
||||||
|
cgexpr(c, n.rhs.lhs);
|
||||||
|
emitline("\tMOVQ\tAX, SI\n");
|
||||||
|
} else { if (n.rhs.kind == nkind.N_IDENT) {
|
||||||
|
let sl: *local = localfindnode(c, n.rhs.str);
|
||||||
|
if (sl != nil) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(sl.off: i64);
|
||||||
|
emitline("(BP), SI\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, n.rhs.str);
|
||||||
|
emitline("(SB), SI\n");
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
dotchainaddr(c, n.rhs, "SI");
|
||||||
|
};};
|
||||||
|
emitline("\tPOPQ\tBX\n"); // dest
|
||||||
|
let kc: i32 = 0;
|
||||||
|
for (kc + 8 <= esz) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(BX)\n");
|
||||||
|
kc += 8;
|
||||||
|
};
|
||||||
|
if (kc + 4 <= esz) {
|
||||||
|
emitline("\tMOVL\t");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVL\tAX, ");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(BX)\n");
|
||||||
|
kc += 4;
|
||||||
|
};
|
||||||
|
if (kc + 2 <= esz) {
|
||||||
|
emitline("\tMOVW\t");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVW\tAX, ");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(BX)\n");
|
||||||
|
kc += 2;
|
||||||
|
};
|
||||||
|
if (kc + 1 <= esz) {
|
||||||
|
emitline("\tMOVB\t");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVB\tAX, ");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(BX)\n");
|
||||||
|
kc += 1;
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
};
|
||||||
cgexpr(c, n.rhs); // value → AX
|
cgexpr(c, n.rhs); // value → AX
|
||||||
// str/slice: spill cap (CX) + len (BX) before
|
// str/slice: spill cap (CX) + len (BX) before
|
||||||
// computing the index so the post-index store can
|
// computing the index so the post-index store can
|
||||||
|
|||||||
@@ -1953,6 +1953,76 @@ fn cglet(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tMOVQ\tAX, SI\n");
|
emitline("\tMOVQ\tAX, SI\n");
|
||||||
havesrc = true;
|
havesrc = true;
|
||||||
};
|
};
|
||||||
|
// #270-3a: the index BASE is an N_DOT
|
||||||
|
// array-field (`x.arr[i]`) or a nested N_INDEX
|
||||||
|
// (`a[i][j]`); the N_IDENT-base arm above missed
|
||||||
|
// both, so the copy fell to the 8B truncation
|
||||||
|
// below. Compute &base[idx]: scaled idx on the
|
||||||
|
// stack, then &base via dotbaseaddr (N_DOT field
|
||||||
|
// address) or the &abase[bidx] spine (nested
|
||||||
|
// N_IDENT-array base), then add.
|
||||||
|
if (!havesrc && base != nil
|
||||||
|
&& (base.kind == nkind.N_DOT
|
||||||
|
|| base.kind == nkind.N_INDEX)) {
|
||||||
|
let esz2: i32 = 1;
|
||||||
|
if (bu != nil && bu.sub != nil) {
|
||||||
|
esz2 = bu.sub.size: i32;
|
||||||
|
};
|
||||||
|
cgexpr(c, idx);
|
||||||
|
if (esz2 > 1) {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(esz2: i64);
|
||||||
|
emitline(", CX\n");
|
||||||
|
emitline("\tIMULQ\tCX, AX\n");
|
||||||
|
};
|
||||||
|
emitline("\tPUSHQ\tAX\n");
|
||||||
|
let baseok: bool = false;
|
||||||
|
if (base.kind == nkind.N_DOT) {
|
||||||
|
if (dotbaseaddr(c, base, "AX")) {
|
||||||
|
baseok = true;
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
let ab: *node = base.lhs;
|
||||||
|
let bidx: *node = base.rhs;
|
||||||
|
let abu: *tinfo = nil;
|
||||||
|
if (ab != nil) { abu = ab.type_: *tinfo; };
|
||||||
|
for (abu != nil && abu.kind == tykind.TY_NAMED) {
|
||||||
|
abu = abu.under;
|
||||||
|
};
|
||||||
|
if (ab != nil && ab.kind == nkind.N_IDENT
|
||||||
|
&& abu != nil && abu.kind == tykind.TY_ARRAY) {
|
||||||
|
let aesz: i32 = 1;
|
||||||
|
if (abu.sub != nil) {
|
||||||
|
aesz = abu.sub.size: i32;
|
||||||
|
};
|
||||||
|
cgexpr(c, bidx);
|
||||||
|
if (aesz > 1) {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(aesz: i64);
|
||||||
|
emitline(", CX\n");
|
||||||
|
emitline("\tIMULQ\tCX, AX\n");
|
||||||
|
};
|
||||||
|
let abl: *local = localfindnode(c, ab.str);
|
||||||
|
if (abl != nil) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(abl.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, ab.str);
|
||||||
|
emitline("(SB), BX\n");
|
||||||
|
};
|
||||||
|
emitline("\tADDQ\tBX, AX\n");
|
||||||
|
baseok = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
emitline("\tPOPQ\tBX\n");
|
||||||
|
if (baseok) {
|
||||||
|
emitline("\tADDQ\tBX, AX\n");
|
||||||
|
emitline("\tMOVQ\tAX, SI\n");
|
||||||
|
havesrc = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
}; };
|
}; };
|
||||||
if (havesrc) {
|
if (havesrc) {
|
||||||
let k: i32 = 0;
|
let k: i32 = 0;
|
||||||
|
|||||||
@@ -21994,6 +21994,24 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
} else {
|
} else {
|
||||||
emitline("\tMOVQ\tBX, AX\n");
|
emitline("\tMOVQ\tBX, AX\n");
|
||||||
};
|
};
|
||||||
|
// #270-1a: an `[N]T`-typed field of an
|
||||||
|
// array element (`a[i].m[j]`) — leave the
|
||||||
|
// field's ADDRESS, a base for the outer
|
||||||
|
// index, NEVER deref. AX holds &a[i]; the
|
||||||
|
// field address is &a[i]+foff. The #135
|
||||||
|
// read-side for `d.m[i]`, applied to an
|
||||||
|
// array-element base. Without this an array
|
||||||
|
// field fell to fieldloadop below and loaded
|
||||||
|
// its first 8 bytes as a value → garbage
|
||||||
|
// base → SEGFAULT in the outer index.
|
||||||
|
if (tinfoisarray(fi.tnode.type_: *tinfo)) {
|
||||||
|
if (fi.foff != 0) {
|
||||||
|
emitline("\tADDQ\t$");
|
||||||
|
emitint(fi.foff: i64);
|
||||||
|
emitline(", AX\n");
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
};
|
||||||
if (isstrtype(c, fi.tnode) || isslicetype(c, fi.tnode)) {
|
if (isstrtype(c, fi.tnode) || isslicetype(c, fi.tnode)) {
|
||||||
// str/slice: the 3-word {ptr,len,cap}
|
// str/slice: the 3-word {ptr,len,cap}
|
||||||
// slice header (#1). AX holds the
|
// slice header (#1). AX holds the
|
||||||
@@ -24620,6 +24638,120 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
c.sretdestoff = 0;
|
c.sretdestoff = 0;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
// #270-1b: aggregate (struct/array/tuple >8B) element
|
||||||
|
// STORE `a[i] = val`. The scalar store path below copies
|
||||||
|
// only the first 8 bytes (tnodestoreop MOVQ) — a silent
|
||||||
|
// truncation. Compute &a[i] (dest) and the rhs SOURCE
|
||||||
|
// address, then word-copy esz bytes: the WRITE-twin of
|
||||||
|
// the #268 let-init copy loop. Source shapes mirror that
|
||||||
|
// loop (ident, N_DOT field via dotchainaddr, `*p`
|
||||||
|
// deref); a by-value call result is the deferred #271, so
|
||||||
|
// N_CALL/literal sources fall through unchanged. esz>8
|
||||||
|
// non-str/non-slice IS a struct/array/tuple here (the
|
||||||
|
// tagged element already returned above; floats are ≤8).
|
||||||
|
let aggsrc: bool = (n.rhs.kind == nkind.N_IDENT)
|
||||||
|
|| (n.rhs.kind == nkind.N_DOT)
|
||||||
|
|| (n.rhs.kind == nkind.N_UN
|
||||||
|
&& n.rhs.op == tkind.TK_STAR);
|
||||||
|
if (esz > 8 && !isstrtype(c, elemtn)
|
||||||
|
&& !isslicetype(c, elemtn) && aggsrc) {
|
||||||
|
cgexpr(c, idx); // idx → AX
|
||||||
|
if (esz > 1) {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(esz: i64);
|
||||||
|
emitline(", CX\n");
|
||||||
|
emitline("\tIMULQ\tCX, AX\n");
|
||||||
|
};
|
||||||
|
emitline("\tPUSHQ\tAX\n"); // scaled idx
|
||||||
|
if (isglobalarr) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, globalname);
|
||||||
|
emitline("(SB), BX\n");
|
||||||
|
} else { if (isglobalptr) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitsymname(c, globalname);
|
||||||
|
emitline("(SB), BX\n");
|
||||||
|
} else { if (baselocal != nil) {
|
||||||
|
let tn2: *node = baselocal.tnode;
|
||||||
|
let isarr2: bool = false;
|
||||||
|
if (tn2 != nil) { if (tn2.kind == nkind.N_TARRAY) { isarr2 = true; }; };
|
||||||
|
if (isarr2) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(baselocal.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(baselocal.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
};
|
||||||
|
} else { if (dotbaseaddr(c, base, "BX")) {
|
||||||
|
// N_DOT array-field base resolved inline.
|
||||||
|
} else {
|
||||||
|
cgexpr(c, base);
|
||||||
|
emitline("\tMOVQ\tAX, BX\n");
|
||||||
|
};};};};
|
||||||
|
emitline("\tPOPQ\tAX\n"); // scaled idx
|
||||||
|
emitline("\tADDQ\tAX, BX\n");
|
||||||
|
emitline("\tPUSHQ\tBX\n"); // spill dest
|
||||||
|
// rhs source address → SI
|
||||||
|
if (n.rhs.kind == nkind.N_UN
|
||||||
|
&& n.rhs.op == tkind.TK_STAR) {
|
||||||
|
cgexpr(c, n.rhs.lhs);
|
||||||
|
emitline("\tMOVQ\tAX, SI\n");
|
||||||
|
} else { if (n.rhs.kind == nkind.N_IDENT) {
|
||||||
|
let sl: *local = localfindnode(c, n.rhs.str);
|
||||||
|
if (sl != nil) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(sl.off: i64);
|
||||||
|
emitline("(BP), SI\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, n.rhs.str);
|
||||||
|
emitline("(SB), SI\n");
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
dotchainaddr(c, n.rhs, "SI");
|
||||||
|
};};
|
||||||
|
emitline("\tPOPQ\tBX\n"); // dest
|
||||||
|
let kc: i32 = 0;
|
||||||
|
for (kc + 8 <= esz) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(BX)\n");
|
||||||
|
kc += 8;
|
||||||
|
};
|
||||||
|
if (kc + 4 <= esz) {
|
||||||
|
emitline("\tMOVL\t");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVL\tAX, ");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(BX)\n");
|
||||||
|
kc += 4;
|
||||||
|
};
|
||||||
|
if (kc + 2 <= esz) {
|
||||||
|
emitline("\tMOVW\t");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVW\tAX, ");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(BX)\n");
|
||||||
|
kc += 2;
|
||||||
|
};
|
||||||
|
if (kc + 1 <= esz) {
|
||||||
|
emitline("\tMOVB\t");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVB\tAX, ");
|
||||||
|
emitoff(kc: i64);
|
||||||
|
emitline("(BX)\n");
|
||||||
|
kc += 1;
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
};
|
||||||
cgexpr(c, n.rhs); // value → AX
|
cgexpr(c, n.rhs); // value → AX
|
||||||
// str/slice: spill cap (CX) + len (BX) before
|
// str/slice: spill cap (CX) + len (BX) before
|
||||||
// computing the index so the post-index store can
|
// computing the index so the post-index store can
|
||||||
@@ -29186,6 +29318,76 @@ fn cglet(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tMOVQ\tAX, SI\n");
|
emitline("\tMOVQ\tAX, SI\n");
|
||||||
havesrc = true;
|
havesrc = true;
|
||||||
};
|
};
|
||||||
|
// #270-3a: the index BASE is an N_DOT
|
||||||
|
// array-field (`x.arr[i]`) or a nested N_INDEX
|
||||||
|
// (`a[i][j]`); the N_IDENT-base arm above missed
|
||||||
|
// both, so the copy fell to the 8B truncation
|
||||||
|
// below. Compute &base[idx]: scaled idx on the
|
||||||
|
// stack, then &base via dotbaseaddr (N_DOT field
|
||||||
|
// address) or the &abase[bidx] spine (nested
|
||||||
|
// N_IDENT-array base), then add.
|
||||||
|
if (!havesrc && base != nil
|
||||||
|
&& (base.kind == nkind.N_DOT
|
||||||
|
|| base.kind == nkind.N_INDEX)) {
|
||||||
|
let esz2: i32 = 1;
|
||||||
|
if (bu != nil && bu.sub != nil) {
|
||||||
|
esz2 = bu.sub.size: i32;
|
||||||
|
};
|
||||||
|
cgexpr(c, idx);
|
||||||
|
if (esz2 > 1) {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(esz2: i64);
|
||||||
|
emitline(", CX\n");
|
||||||
|
emitline("\tIMULQ\tCX, AX\n");
|
||||||
|
};
|
||||||
|
emitline("\tPUSHQ\tAX\n");
|
||||||
|
let baseok: bool = false;
|
||||||
|
if (base.kind == nkind.N_DOT) {
|
||||||
|
if (dotbaseaddr(c, base, "AX")) {
|
||||||
|
baseok = true;
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
let ab: *node = base.lhs;
|
||||||
|
let bidx: *node = base.rhs;
|
||||||
|
let abu: *tinfo = nil;
|
||||||
|
if (ab != nil) { abu = ab.type_: *tinfo; };
|
||||||
|
for (abu != nil && abu.kind == tykind.TY_NAMED) {
|
||||||
|
abu = abu.under;
|
||||||
|
};
|
||||||
|
if (ab != nil && ab.kind == nkind.N_IDENT
|
||||||
|
&& abu != nil && abu.kind == tykind.TY_ARRAY) {
|
||||||
|
let aesz: i32 = 1;
|
||||||
|
if (abu.sub != nil) {
|
||||||
|
aesz = abu.sub.size: i32;
|
||||||
|
};
|
||||||
|
cgexpr(c, bidx);
|
||||||
|
if (aesz > 1) {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(aesz: i64);
|
||||||
|
emitline(", CX\n");
|
||||||
|
emitline("\tIMULQ\tCX, AX\n");
|
||||||
|
};
|
||||||
|
let abl: *local = localfindnode(c, ab.str);
|
||||||
|
if (abl != nil) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(abl.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, ab.str);
|
||||||
|
emitline("(SB), BX\n");
|
||||||
|
};
|
||||||
|
emitline("\tADDQ\tBX, AX\n");
|
||||||
|
baseok = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
emitline("\tPOPQ\tBX\n");
|
||||||
|
if (baseok) {
|
||||||
|
emitline("\tADDQ\tBX, AX\n");
|
||||||
|
emitline("\tMOVQ\tAX, SI\n");
|
||||||
|
havesrc = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
}; };
|
}; };
|
||||||
if (havesrc) {
|
if (havesrc) {
|
||||||
let k: i32 = 0;
|
let k: i32 = 0;
|
||||||
|
|||||||
@@ -1057,6 +1057,107 @@ static const struct row rows[] = {
|
|||||||
" a[1][1] = 88;\n"
|
" a[1][1] = 88;\n"
|
||||||
" return (a[1][1] - a[0][1]): i32;\n"
|
" return (a[1][1] - a[0][1]): i32;\n"
|
||||||
"};\n", 48, 1 },
|
"};\n", 48, 1 },
|
||||||
|
/* #270-1a: `a[i].m[j] = v` — array-of-struct element field, then
|
||||||
|
* index INTO that field. The `arr[i].field` read/write arm computed
|
||||||
|
* &a[i] then DEREF'd it (loaded the struct's first 8 bytes as a
|
||||||
|
* value) for an `[N]T`-typed field → garbage base → SEGFAULT on the
|
||||||
|
* outer store. Fix: an array-typed field of an array element leaves
|
||||||
|
* the field ADDRESS (the #135 read-side, applied to the array-element
|
||||||
|
* base). Multiple cells written then read back; byteid=1. */
|
||||||
|
{ "elemfield_store",
|
||||||
|
"package main;\n"
|
||||||
|
"type inner = struct { m: [4]u32 };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let a: [3]inner;\n"
|
||||||
|
" a[0].m[1] = 5u32;\n"
|
||||||
|
" a[2].m[3] = 7u32;\n"
|
||||||
|
" a[2].m[0] = 9u32;\n"
|
||||||
|
" return (a[0].m[1] + a[2].m[3] + a[2].m[0]): i32;\n"
|
||||||
|
"};\n", 21, 1 },
|
||||||
|
/* #270-1b: `a[i] = aggregateval` — whole-element STORE. The scalar
|
||||||
|
* store path copied only the first 8 bytes (fldstoreop MOVQ). Fix:
|
||||||
|
* an aggregate (struct/array >8B) element store word-copies the
|
||||||
|
* element from the rhs source address (WRITE-twin of the #268
|
||||||
|
* let-init loop). Struct element + array element, full readback;
|
||||||
|
* byteid=1. */
|
||||||
|
/* 16B struct (slot == natural) keeps byteid=1: a struct whose
|
||||||
|
* natural size is NOT an 8-multiple trips the orthogonal
|
||||||
|
* elemsizeofc slot-vs-natural array-stride divergence (cstage strides
|
||||||
|
* by sub->size, wwstage by slotsize) — see letcopy_dot_struct. */
|
||||||
|
{ "elem_struct_store",
|
||||||
|
"package main;\n"
|
||||||
|
"type inner = struct { a: u32, b: u32, c: u32, d: u32 };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let arr: [3]inner;\n"
|
||||||
|
" let v: inner; v.a=10u32; v.b=20u32; v.c=30u32; v.d=40u32;\n"
|
||||||
|
" arr[2] = v;\n"
|
||||||
|
" return (arr[2].a + arr[2].b + arr[2].c + arr[2].d): i32;\n"
|
||||||
|
"};\n", 100, 1 },
|
||||||
|
{ "elem_arr_store",
|
||||||
|
"package main;\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let arr: [2][4]u32;\n"
|
||||||
|
" let s: [4]u32; s[0]=1u32; s[1]=2u32; s[2]=3u32; s[3]=4u32;\n"
|
||||||
|
" arr[1] = s;\n"
|
||||||
|
" return (arr[1][0]+arr[1][1]+arr[1][2]+arr[1][3]): i32;\n"
|
||||||
|
"};\n", 10, 1 },
|
||||||
|
/* #270-3a: aggregate let-init COPY whose index base is an N_DOT
|
||||||
|
* array-field (`x.arr[i]`) or a nested N_INDEX (`a[i][j]`) — the
|
||||||
|
* let-init N_INDEX source-addr arm was N_IDENT-base-gated (#268
|
||||||
|
* residual), so both fell to the 8B truncation. Fix computes
|
||||||
|
* &base[idx] via cg_dotbase_addr (N_DOT) / the &abase[bidx] spine
|
||||||
|
* (nested). Primitive-element rows are byteid=1; the value-struct
|
||||||
|
* rows below run-correct but trip the orthogonal value-nested-struct
|
||||||
|
* frame divergence (#254), so byteid=0 (same carve-out as chain_val_*
|
||||||
|
* above). */
|
||||||
|
{ "letcopy_dot_prim",
|
||||||
|
"package main;\n"
|
||||||
|
"type box = struct { arr: [2][4]u32 };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let s: [4]u32; s[0]=1u32; s[1]=2u32; s[2]=3u32; s[3]=4u32;\n"
|
||||||
|
" let x: box;\n"
|
||||||
|
" x.arr[1] = s;\n"
|
||||||
|
" let c: [4]u32 = x.arr[1];\n"
|
||||||
|
" return (c[0]+c[1]+c[2]+c[3]): i32;\n"
|
||||||
|
"};\n", 10, 1 },
|
||||||
|
{ "letcopy_nest_prim",
|
||||||
|
"package main;\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let a: [2][2][4]u32;\n"
|
||||||
|
" let s: [4]u32; s[0]=2u32; s[1]=4u32; s[2]=6u32; s[3]=8u32;\n"
|
||||||
|
" a[1][0] = s;\n"
|
||||||
|
" let c: [4]u32 = a[1][0];\n"
|
||||||
|
" return (c[0]+c[1]+c[2]+c[3]): i32;\n"
|
||||||
|
"};\n", 20, 1 },
|
||||||
|
{ "letcopy_subarr",
|
||||||
|
"package main;\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let a: [2][3]u32;\n"
|
||||||
|
" a[1][0] = 5u32; a[1][1] = 6u32; a[1][2] = 7u32;\n"
|
||||||
|
" let c: [3]u32 = a[1];\n"
|
||||||
|
" return (c[0] + c[1] + c[2]): i32;\n"
|
||||||
|
"};\n", 18, 1 },
|
||||||
|
{ "letcopy_dot_struct",
|
||||||
|
"package main;\n"
|
||||||
|
"type inner = struct { a: u32, b: u32, c: u32 };\n"
|
||||||
|
"type box = struct { arr: [3]inner };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let x: box;\n"
|
||||||
|
" let v: inner; v.a = 10u32; v.b = 20u32; v.c = 30u32;\n"
|
||||||
|
" x.arr[1] = v;\n"
|
||||||
|
" let c: inner = x.arr[1];\n"
|
||||||
|
" return (c.a + c.b + c.c): i32;\n"
|
||||||
|
"};\n", 60, 0 },
|
||||||
|
{ "letcopy_nest_struct",
|
||||||
|
"package main;\n"
|
||||||
|
"type inner = struct { a: u32, b: u32, c: u32 };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let a: [2][2]inner;\n"
|
||||||
|
" let v: inner; v.a = 11u32; v.b = 22u32; v.c = 33u32;\n"
|
||||||
|
" a[1][0] = v;\n"
|
||||||
|
" let c: inner = a[1][0];\n"
|
||||||
|
" return (c.a + c.b + c.c): i32;\n"
|
||||||
|
"};\n", 66, 0 },
|
||||||
{ NULL, NULL, 0, 0 }
|
{ NULL, NULL, 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user