w6c+selfhost: cgassign N_DOT N_INDEX-lhs branch (closes #16)
Symmetric write-side counterpart of #8, bundled across both stages. cstage [N]Struct write was broken (1986 gated on TY_PTR); wwstage had no N_DOT(N_INDEX) write branch at all. New branch covers both [N]*Struct and [N]Struct via viaptr flag, uses fldstoreop for scalar/sub-word, MOVSS/MOVSD for float, two-MOVQ for str rhs. Compound (PLUSEQ etc.) wired for integer scalar.
This commit is contained in:
256
cmd/w6c/cgen.c
256
cmd/w6c/cgen.c
@@ -1976,6 +1976,262 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* `arr[i].field = v`: N_DOT lhs whose lhs is N_INDEX. Symmetric
|
||||
* write-side of the cgdot N_INDEX-lhs branch. Compute &arr[i]
|
||||
* inline (LEAQ for `[N]Struct`, MOVQ-load for `[N]*Struct` /
|
||||
* `[]Struct` / `*Struct`), deref once when the element is
|
||||
* `*Struct`, then store rhs at `field.offset(addr)`. The
|
||||
* chained-pointer-field branch below catches `[N]*Struct`
|
||||
* writes via its `!= N_IDENT` guard, but `[N]Struct` value-arrays
|
||||
* fall through and silently drop the store. Placed before the
|
||||
* `!= N_IDENT` branch so both shapes share one path. */
|
||||
if (n->lhs && n->lhs->kind == N_DOT && n->lhs->lhs
|
||||
&& n->lhs->lhs->kind == N_INDEX) {
|
||||
Node *idxbase = n->lhs->lhs->lhs;
|
||||
Node *idx = n->lhs->lhs->rhs;
|
||||
if (idxbase && idxbase->kind == N_IDENT && idx) {
|
||||
Type *elemt = n->lhs->lhs->type;
|
||||
Type *elemu = (elemt && elemt->kind == TY_NAMED)
|
||||
? elemt->under : elemt;
|
||||
Type *struct_t = NULL;
|
||||
int viaptr = 0;
|
||||
if (elemu && elemu->kind == TY_PTR) {
|
||||
Type *inner = elemu->sub;
|
||||
if (inner && inner->kind == TY_NAMED)
|
||||
inner = inner->under;
|
||||
if (inner && inner->kind == TY_STRUCT) {
|
||||
struct_t = inner;
|
||||
viaptr = 1;
|
||||
}
|
||||
} else if (elemu && elemu->kind == TY_STRUCT) {
|
||||
struct_t = elemu;
|
||||
}
|
||||
if (struct_t) {
|
||||
Tfield *f = NULL;
|
||||
for (Tfield *fl = struct_t->fields; fl;
|
||||
fl = fl->next)
|
||||
if (strcmp(fl->name,
|
||||
n->lhs->str) == 0)
|
||||
{ f = fl; break; }
|
||||
Type *bt = idxbase->type;
|
||||
Type *bu = (bt && bt->kind == TY_NAMED)
|
||||
? bt->under : bt;
|
||||
int is_arr = bu && bu->kind == TY_ARRAY;
|
||||
int is_sl = bu && bu->kind == TY_SLICE;
|
||||
int is_ptr = bu && bu->kind == TY_PTR;
|
||||
int off = localfind(locals, idxbase->str);
|
||||
if (f != NULL && (is_arr || is_sl || is_ptr)
|
||||
&& off != 0) {
|
||||
Type *ft = f->type;
|
||||
Type *fu = (ft && ft->kind == TY_NAMED)
|
||||
? ft->under : ft;
|
||||
int fsz = (int)(ft ? ft->size : 8);
|
||||
int store_op = fldstoreop(ft, fsz);
|
||||
int foff = (int)f->offset;
|
||||
int esz = (int)elemt->size;
|
||||
int h_isf32 = 0;
|
||||
if (n->op == TK_ASSIGN
|
||||
&& fld_isfloat(ft, &h_isf32)) {
|
||||
int mov = h_isf32
|
||||
? A_MOVSS : A_MOVSD;
|
||||
cgexpr(c, n->rhs, locals);
|
||||
ins2(c, A_SUBQ, aimm(8),
|
||||
areg(D_SP));
|
||||
ins2(c, mov, areg(D_X0),
|
||||
amem(D_SP, 0));
|
||||
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));
|
||||
}
|
||||
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));
|
||||
ins2(c, A_ADDQ,
|
||||
areg(D_AX),
|
||||
areg(D_BX));
|
||||
if (viaptr)
|
||||
ins2(c, A_MOVQ,
|
||||
amem(D_BX, 0),
|
||||
areg(D_BX));
|
||||
ins2(c, mov,
|
||||
amem(D_SP, 0),
|
||||
areg(D_X0));
|
||||
ins2(c, A_ADDQ, aimm(8),
|
||||
areg(D_SP));
|
||||
ins2(c, mov, areg(D_X0),
|
||||
amem(D_BX, foff));
|
||||
break;
|
||||
}
|
||||
if (n->op == TK_ASSIGN
|
||||
&& fu && fu->kind == TY_STR) {
|
||||
/* str rhs: AX=ptr, BX=len.
|
||||
* Stash both, compute addr
|
||||
* in CX so the pop pair
|
||||
* restores AX/BX cleanly. */
|
||||
cgexpr(c, n->rhs, locals);
|
||||
ins1(c, A_PUSHQ,
|
||||
areg(D_BX));
|
||||
ins1(c, A_PUSHQ,
|
||||
areg(D_AX));
|
||||
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));
|
||||
}
|
||||
if (is_arr)
|
||||
ins2(c, A_LEAQ,
|
||||
amem(D_BP, off),
|
||||
areg(D_CX));
|
||||
else
|
||||
ins2(c, A_MOVQ,
|
||||
amem(D_BP, off),
|
||||
areg(D_CX));
|
||||
ins2(c, A_ADDQ,
|
||||
areg(D_AX),
|
||||
areg(D_CX));
|
||||
if (viaptr)
|
||||
ins2(c, A_MOVQ,
|
||||
amem(D_CX, 0),
|
||||
areg(D_CX));
|
||||
ins1(c, A_POPQ,
|
||||
areg(D_AX));
|
||||
ins1(c, A_POPQ,
|
||||
areg(D_BX));
|
||||
ins2(c, A_MOVQ,
|
||||
areg(D_AX),
|
||||
amem(D_CX, foff + 0));
|
||||
ins2(c, A_MOVQ,
|
||||
areg(D_BX),
|
||||
amem(D_CX, foff + 8));
|
||||
break;
|
||||
}
|
||||
if (n->op == TK_ASSIGN) {
|
||||
cgexpr(c, n->rhs, locals);
|
||||
ins1(c, A_PUSHQ,
|
||||
areg(D_AX));
|
||||
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));
|
||||
}
|
||||
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));
|
||||
ins2(c, A_ADDQ,
|
||||
areg(D_AX),
|
||||
areg(D_BX));
|
||||
if (viaptr)
|
||||
ins2(c, A_MOVQ,
|
||||
amem(D_BX, 0),
|
||||
areg(D_BX));
|
||||
ins1(c, A_POPQ,
|
||||
areg(D_AX));
|
||||
ins2(c, store_op,
|
||||
areg(D_AX),
|
||||
amem(D_BX, foff));
|
||||
break;
|
||||
}
|
||||
/* compound: rhs→push; compute
|
||||
* struct addr→BX (deref if *T);
|
||||
* push addr; load old field→AX;
|
||||
* pop addr→BX, rhs→CX; combine;
|
||||
* store. Float/str compound
|
||||
* not wired. */
|
||||
cgexpr(c, n->rhs, locals);
|
||||
ins1(c, A_PUSHQ, areg(D_AX));
|
||||
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));
|
||||
}
|
||||
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));
|
||||
ins2(c, A_ADDQ, areg(D_AX),
|
||||
areg(D_BX));
|
||||
if (viaptr)
|
||||
ins2(c, A_MOVQ,
|
||||
amem(D_BX, 0),
|
||||
areg(D_BX));
|
||||
ins1(c, A_PUSHQ, areg(D_BX));
|
||||
int load_op = fldloadop(ft, fsz);
|
||||
ins2(c, load_op,
|
||||
amem(D_BX, foff),
|
||||
areg(D_AX));
|
||||
ins1(c, A_POPQ, areg(D_BX));
|
||||
ins1(c, A_POPQ, areg(D_CX));
|
||||
switch (n->op) {
|
||||
case TK_PLUSEQ:
|
||||
ins2(c, A_ADDQ,
|
||||
areg(D_CX),
|
||||
areg(D_AX));
|
||||
break;
|
||||
case TK_MINUSEQ:
|
||||
ins2(c, A_SUBQ,
|
||||
areg(D_CX),
|
||||
areg(D_AX));
|
||||
break;
|
||||
case TK_STAREQ:
|
||||
ins2(c, A_IMULQ,
|
||||
areg(D_CX),
|
||||
areg(D_AX));
|
||||
break;
|
||||
case TK_AMPEQ:
|
||||
ins2(c, A_ANDQ,
|
||||
areg(D_CX),
|
||||
areg(D_AX));
|
||||
break;
|
||||
case TK_PIPEEQ:
|
||||
ins2(c, A_ORQ,
|
||||
areg(D_CX),
|
||||
areg(D_AX));
|
||||
break;
|
||||
case TK_CARETEQ:
|
||||
ins2(c, A_XORQ,
|
||||
areg(D_CX),
|
||||
areg(D_AX));
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
ins2(c, store_op, areg(D_AX),
|
||||
amem(D_BX, foff));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Chained `<expr>.field = v` where <expr> evaluates to a *struct.
|
||||
* cgexpr on the inner expression already returns the pointer;
|
||||
* we then store at (ptr + field.offset). Without this, only the
|
||||
|
||||
Reference in New Issue
Block a user