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:
7
Makefile
7
Makefile
@@ -218,6 +218,7 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \
|
|||||||
$(BIN)/test_at_test $(BIN)/test_let_global \
|
$(BIN)/test_at_test $(BIN)/test_let_global \
|
||||||
$(BIN)/test_int_cast_signed $(BIN)/test_dot_chain \
|
$(BIN)/test_int_cast_signed $(BIN)/test_dot_chain \
|
||||||
$(BIN)/test_amp_dot $(BIN)/test_arr_elem_field \
|
$(BIN)/test_amp_dot $(BIN)/test_arr_elem_field \
|
||||||
|
$(BIN)/test_arr_elem_field_write \
|
||||||
$(BIN)/test_field_signed $(BIN)/test_frame_argcount \
|
$(BIN)/test_field_signed $(BIN)/test_frame_argcount \
|
||||||
$(BIN)/test_selfhost $(BIN)/test_w6a_ww $(BIN)/test_w6l_ww \
|
$(BIN)/test_selfhost $(BIN)/test_w6a_ww $(BIN)/test_w6l_ww \
|
||||||
$(BIN)/test_w6c_ww $(BIN)/test_ww_ww $(BIN)/test_self_rebuild \
|
$(BIN)/test_w6c_ww $(BIN)/test_ww_ww $(BIN)/test_self_rebuild \
|
||||||
@@ -303,6 +304,12 @@ $(BIN)/test_arr_elem_field: test/wcc/680_arr_elem_field.c $(BIN)/ww \
|
|||||||
$(LIB)/libwwrt.a | $(BIN)
|
$(LIB)/libwwrt.a | $(BIN)
|
||||||
$(CC) $(CFLAGS) -o $@ $<
|
$(CC) $(CFLAGS) -o $@ $<
|
||||||
|
|
||||||
|
$(BIN)/test_arr_elem_field_write: test/wcc/681_arr_elem_field_write.c $(BIN)/ww \
|
||||||
|
$(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||||
|
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||||
|
$(LIB)/libwwrt.a | $(BIN)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $<
|
||||||
|
|
||||||
$(BIN)/test_field_signed: test/wcc/660_field_signed.c $(BIN)/ww \
|
$(BIN)/test_field_signed: test/wcc/660_field_signed.c $(BIN)/ww \
|
||||||
$(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
$(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||||
|
|||||||
256
cmd/w6c/cgen.c
256
cmd/w6c/cgen.c
@@ -1976,6 +1976,262 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
break;
|
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.
|
/* Chained `<expr>.field = v` where <expr> evaluates to a *struct.
|
||||||
* cgexpr on the inner expression already returns the pointer;
|
* cgexpr on the inner expression already returns the pointer;
|
||||||
* we then store at (ptr + field.offset). Without this, only the
|
* we then store at (ptr + field.offset). Without this, only the
|
||||||
|
|||||||
@@ -11289,6 +11289,216 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
// `arr[i].field = v`: N_DOT lhs whose lhs is N_INDEX. Symmetric
|
||||||
|
// write-side of the cgdot N_INDEX-lhs branch added for task #8.
|
||||||
|
// Compute &arr[i] inline (LEAQ for `[N]Struct`, MOVQ for
|
||||||
|
// `[N]*Struct` / `[]Struct` / `*Struct`), deref once when the
|
||||||
|
// element is `*Struct`, then store rhs at field.offset(addr).
|
||||||
|
// Without this both shapes silently drop the store — there is no
|
||||||
|
// existing wwstage branch for N_DOT(N_INDEX,...) lhs at all (the
|
||||||
|
// N_INDEX-lhs branch above handles bare `arr[i] = v`, not the
|
||||||
|
// field write).
|
||||||
|
if (lhs != nil) {
|
||||||
|
if (lhs.kind == nkind.N_DOT && lhs.lhs != nil
|
||||||
|
&& lhs.lhs.kind == nkind.N_INDEX) {
|
||||||
|
let idxbase: *node = lhs.lhs.lhs;
|
||||||
|
let idx: *node = lhs.lhs.rhs;
|
||||||
|
let fld2: str = lhs.str;
|
||||||
|
if (idxbase != nil) { if (idxbase.kind == nkind.N_IDENT) {
|
||||||
|
if (idx != nil) {
|
||||||
|
let lc: *local = localfindnode(c, idxbase.str);
|
||||||
|
if (lc != nil) { if (lc.tnode != nil) {
|
||||||
|
let tn: *node = lc.tnode;
|
||||||
|
let elemt: *node = nil;
|
||||||
|
let baseisarray: bool = false;
|
||||||
|
let tk: nkind = tn.kind;
|
||||||
|
if (tk == nkind.N_TSLICE) { elemt = tn.lhs; };
|
||||||
|
if (tk == nkind.N_TARRAY) { elemt = tn.lhs; baseisarray = true; };
|
||||||
|
if (tk == nkind.N_TPTR) { elemt = tn.lhs; };
|
||||||
|
let sname: str;
|
||||||
|
sname.ptr = nil; sname.len = 0;
|
||||||
|
let viaptr: bool = false;
|
||||||
|
if (elemt != nil) {
|
||||||
|
if (elemt.kind == nkind.N_TPTR) {
|
||||||
|
let inner: *node = elemt.lhs;
|
||||||
|
if (inner != nil) { if (inner.kind == nkind.N_TNAME) {
|
||||||
|
sname = inner.str;
|
||||||
|
viaptr = true;
|
||||||
|
};};
|
||||||
|
} else { if (elemt.kind == nkind.N_TNAME) {
|
||||||
|
sname = elemt.str;
|
||||||
|
};};
|
||||||
|
};
|
||||||
|
if (sname.len > 0) {
|
||||||
|
let si: *structinfo = structlookup(c, sname);
|
||||||
|
if (si != nil) {
|
||||||
|
let fi: *fieldinfo = si.fields;
|
||||||
|
for (fi != nil) {
|
||||||
|
if (streq(fi.fname, fld2)) {
|
||||||
|
let esz: i32 = elemsizeofc(c, tn);
|
||||||
|
// f64/f32: rhs in X0. Spill to stack,
|
||||||
|
// compute &arr[i] in BX (deref if *T),
|
||||||
|
// then reload X0 and MOVSD/MOVSS.
|
||||||
|
if (n.op == tkind.TK_ASSIGN) {
|
||||||
|
if (isfloattype(c, fi.tnode)) {
|
||||||
|
let mov: str = "MOVSD";
|
||||||
|
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
||||||
|
cgexpr(c, n.rhs);
|
||||||
|
emitline("\tSUBQ\t$8, SP\n");
|
||||||
|
emitline("\t");
|
||||||
|
emitline(mov);
|
||||||
|
emitline("\tX0, (SP)\n");
|
||||||
|
cgexpr(c, idx);
|
||||||
|
if (esz > 1) {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(esz: i64);
|
||||||
|
emitline(", CX\n");
|
||||||
|
emitline("\tIMULQ\tCX, AX\n");
|
||||||
|
};
|
||||||
|
if (baseisarray) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
};
|
||||||
|
emitline("\tADDQ\tAX, BX\n");
|
||||||
|
if (viaptr) { emitline("\tMOVQ\t(BX), BX\n"); };
|
||||||
|
emitline("\t");
|
||||||
|
emitline(mov);
|
||||||
|
emitline("\t(SP), X0\n");
|
||||||
|
emitline("\tADDQ\t$8, SP\n");
|
||||||
|
emitline("\t");
|
||||||
|
emitline(mov);
|
||||||
|
emitline("\tX0, ");
|
||||||
|
emitdispreg(fi.foff: i64, "BX");
|
||||||
|
emitline("\n");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
// str rhs: AX=ptr, BX=len. Stash both,
|
||||||
|
// compute addr in CX so the pop pair
|
||||||
|
// restores AX/BX intact.
|
||||||
|
if (isstrtype(c, fi.tnode)) {
|
||||||
|
cgexpr(c, n.rhs);
|
||||||
|
emitline("\tPUSHQ\tBX\n");
|
||||||
|
emitline("\tPUSHQ\tAX\n");
|
||||||
|
cgexpr(c, idx);
|
||||||
|
if (esz > 1) {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(esz: i64);
|
||||||
|
emitline(", CX\n");
|
||||||
|
emitline("\tIMULQ\tCX, AX\n");
|
||||||
|
};
|
||||||
|
if (baseisarray) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
};
|
||||||
|
emitline("\tADDQ\tAX, CX\n");
|
||||||
|
if (viaptr) { emitline("\tMOVQ\t(CX), CX\n"); };
|
||||||
|
emitline("\tPOPQ\tAX\n");
|
||||||
|
emitline("\tPOPQ\tBX\n");
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitdispreg(fi.foff: i64, "CX");
|
||||||
|
emitline("\n");
|
||||||
|
emitline("\tMOVQ\tBX, ");
|
||||||
|
emitdispreg((fi.foff + 8): i64, "CX");
|
||||||
|
emitline("\n");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
// scalar plain `=`
|
||||||
|
cgexpr(c, n.rhs);
|
||||||
|
emitline("\tPUSHQ\tAX\n");
|
||||||
|
cgexpr(c, idx);
|
||||||
|
if (esz > 1) {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(esz: i64);
|
||||||
|
emitline(", CX\n");
|
||||||
|
emitline("\tIMULQ\tCX, AX\n");
|
||||||
|
};
|
||||||
|
if (baseisarray) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
};
|
||||||
|
emitline("\tADDQ\tAX, BX\n");
|
||||||
|
if (viaptr) { emitline("\tMOVQ\t(BX), BX\n"); };
|
||||||
|
emitline("\tPOPQ\tAX\n");
|
||||||
|
let sop: str = fieldstoreop(c, fi);
|
||||||
|
emitline("\t");
|
||||||
|
emitline(sop);
|
||||||
|
emitline("\tAX, ");
|
||||||
|
emitdispreg(fi.foff: i64, "BX");
|
||||||
|
emitline("\n");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
// 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);
|
||||||
|
emitline("\tPUSHQ\tAX\n");
|
||||||
|
cgexpr(c, idx);
|
||||||
|
if (esz > 1) {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(esz: i64);
|
||||||
|
emitline(", CX\n");
|
||||||
|
emitline("\tIMULQ\tCX, AX\n");
|
||||||
|
};
|
||||||
|
if (baseisarray) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
};
|
||||||
|
emitline("\tADDQ\tAX, BX\n");
|
||||||
|
if (viaptr) { emitline("\tMOVQ\t(BX), BX\n"); };
|
||||||
|
emitline("\tPUSHQ\tBX\n");
|
||||||
|
let lop: str = fieldloadop(c, fi);
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\t");
|
||||||
|
emitdispreg(fi.foff: i64, "BX");
|
||||||
|
emitline(", AX\n");
|
||||||
|
emitline("\tPOPQ\tBX\n");
|
||||||
|
emitline("\tPOPQ\tCX\n");
|
||||||
|
if (n.op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tCX, AX\n"); };
|
||||||
|
if (n.op == tkind.TK_MINUSEQ) { emitline("\tSUBQ\tCX, AX\n"); };
|
||||||
|
if (n.op == tkind.TK_STAREQ) { emitline("\tIMULQ\tCX, AX\n"); };
|
||||||
|
if (n.op == tkind.TK_AMPEQ) { emitline("\tANDQ\tCX, AX\n"); };
|
||||||
|
if (n.op == tkind.TK_PIPEEQ) { emitline("\tORQ\tCX, AX\n"); };
|
||||||
|
if (n.op == tkind.TK_CARETEQ) { emitline("\tXORQ\tCX, AX\n"); };
|
||||||
|
let sop2: str = fieldstoreop(c, fi);
|
||||||
|
emitline("\t");
|
||||||
|
emitline(sop2);
|
||||||
|
emitline("\tAX, ");
|
||||||
|
emitdispreg(fi.foff: i64, "BX");
|
||||||
|
emitline("\n");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
fi = fi.finext;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};};
|
||||||
|
};
|
||||||
|
};};
|
||||||
|
};
|
||||||
|
};
|
||||||
// Struct/ptr-to-struct field assignment: `s.f = expr;` or
|
// Struct/ptr-to-struct field assignment: `s.f = expr;` or
|
||||||
// `p.f = expr;`. Only plain `=` is wired (compound on field
|
// `p.f = expr;`. Only plain `=` is wired (compound on field
|
||||||
// is rare and not yet needed by our fixtures).
|
// is rare and not yet needed by our fixtures).
|
||||||
|
|||||||
@@ -3246,6 +3246,216 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
// `arr[i].field = v`: N_DOT lhs whose lhs is N_INDEX. Symmetric
|
||||||
|
// write-side of the cgdot N_INDEX-lhs branch added for task #8.
|
||||||
|
// Compute &arr[i] inline (LEAQ for `[N]Struct`, MOVQ for
|
||||||
|
// `[N]*Struct` / `[]Struct` / `*Struct`), deref once when the
|
||||||
|
// element is `*Struct`, then store rhs at field.offset(addr).
|
||||||
|
// Without this both shapes silently drop the store — there is no
|
||||||
|
// existing wwstage branch for N_DOT(N_INDEX,...) lhs at all (the
|
||||||
|
// N_INDEX-lhs branch above handles bare `arr[i] = v`, not the
|
||||||
|
// field write).
|
||||||
|
if (lhs != nil) {
|
||||||
|
if (lhs.kind == nkind.N_DOT && lhs.lhs != nil
|
||||||
|
&& lhs.lhs.kind == nkind.N_INDEX) {
|
||||||
|
let idxbase: *node = lhs.lhs.lhs;
|
||||||
|
let idx: *node = lhs.lhs.rhs;
|
||||||
|
let fld2: str = lhs.str;
|
||||||
|
if (idxbase != nil) { if (idxbase.kind == nkind.N_IDENT) {
|
||||||
|
if (idx != nil) {
|
||||||
|
let lc: *local = localfindnode(c, idxbase.str);
|
||||||
|
if (lc != nil) { if (lc.tnode != nil) {
|
||||||
|
let tn: *node = lc.tnode;
|
||||||
|
let elemt: *node = nil;
|
||||||
|
let baseisarray: bool = false;
|
||||||
|
let tk: nkind = tn.kind;
|
||||||
|
if (tk == nkind.N_TSLICE) { elemt = tn.lhs; };
|
||||||
|
if (tk == nkind.N_TARRAY) { elemt = tn.lhs; baseisarray = true; };
|
||||||
|
if (tk == nkind.N_TPTR) { elemt = tn.lhs; };
|
||||||
|
let sname: str;
|
||||||
|
sname.ptr = nil; sname.len = 0;
|
||||||
|
let viaptr: bool = false;
|
||||||
|
if (elemt != nil) {
|
||||||
|
if (elemt.kind == nkind.N_TPTR) {
|
||||||
|
let inner: *node = elemt.lhs;
|
||||||
|
if (inner != nil) { if (inner.kind == nkind.N_TNAME) {
|
||||||
|
sname = inner.str;
|
||||||
|
viaptr = true;
|
||||||
|
};};
|
||||||
|
} else { if (elemt.kind == nkind.N_TNAME) {
|
||||||
|
sname = elemt.str;
|
||||||
|
};};
|
||||||
|
};
|
||||||
|
if (sname.len > 0) {
|
||||||
|
let si: *structinfo = structlookup(c, sname);
|
||||||
|
if (si != nil) {
|
||||||
|
let fi: *fieldinfo = si.fields;
|
||||||
|
for (fi != nil) {
|
||||||
|
if (streq(fi.fname, fld2)) {
|
||||||
|
let esz: i32 = elemsizeofc(c, tn);
|
||||||
|
// f64/f32: rhs in X0. Spill to stack,
|
||||||
|
// compute &arr[i] in BX (deref if *T),
|
||||||
|
// then reload X0 and MOVSD/MOVSS.
|
||||||
|
if (n.op == tkind.TK_ASSIGN) {
|
||||||
|
if (isfloattype(c, fi.tnode)) {
|
||||||
|
let mov: str = "MOVSD";
|
||||||
|
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
||||||
|
cgexpr(c, n.rhs);
|
||||||
|
emitline("\tSUBQ\t$8, SP\n");
|
||||||
|
emitline("\t");
|
||||||
|
emitline(mov);
|
||||||
|
emitline("\tX0, (SP)\n");
|
||||||
|
cgexpr(c, idx);
|
||||||
|
if (esz > 1) {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(esz: i64);
|
||||||
|
emitline(", CX\n");
|
||||||
|
emitline("\tIMULQ\tCX, AX\n");
|
||||||
|
};
|
||||||
|
if (baseisarray) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
};
|
||||||
|
emitline("\tADDQ\tAX, BX\n");
|
||||||
|
if (viaptr) { emitline("\tMOVQ\t(BX), BX\n"); };
|
||||||
|
emitline("\t");
|
||||||
|
emitline(mov);
|
||||||
|
emitline("\t(SP), X0\n");
|
||||||
|
emitline("\tADDQ\t$8, SP\n");
|
||||||
|
emitline("\t");
|
||||||
|
emitline(mov);
|
||||||
|
emitline("\tX0, ");
|
||||||
|
emitdispreg(fi.foff: i64, "BX");
|
||||||
|
emitline("\n");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
// str rhs: AX=ptr, BX=len. Stash both,
|
||||||
|
// compute addr in CX so the pop pair
|
||||||
|
// restores AX/BX intact.
|
||||||
|
if (isstrtype(c, fi.tnode)) {
|
||||||
|
cgexpr(c, n.rhs);
|
||||||
|
emitline("\tPUSHQ\tBX\n");
|
||||||
|
emitline("\tPUSHQ\tAX\n");
|
||||||
|
cgexpr(c, idx);
|
||||||
|
if (esz > 1) {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(esz: i64);
|
||||||
|
emitline(", CX\n");
|
||||||
|
emitline("\tIMULQ\tCX, AX\n");
|
||||||
|
};
|
||||||
|
if (baseisarray) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
};
|
||||||
|
emitline("\tADDQ\tAX, CX\n");
|
||||||
|
if (viaptr) { emitline("\tMOVQ\t(CX), CX\n"); };
|
||||||
|
emitline("\tPOPQ\tAX\n");
|
||||||
|
emitline("\tPOPQ\tBX\n");
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitdispreg(fi.foff: i64, "CX");
|
||||||
|
emitline("\n");
|
||||||
|
emitline("\tMOVQ\tBX, ");
|
||||||
|
emitdispreg((fi.foff + 8): i64, "CX");
|
||||||
|
emitline("\n");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
// scalar plain `=`
|
||||||
|
cgexpr(c, n.rhs);
|
||||||
|
emitline("\tPUSHQ\tAX\n");
|
||||||
|
cgexpr(c, idx);
|
||||||
|
if (esz > 1) {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(esz: i64);
|
||||||
|
emitline(", CX\n");
|
||||||
|
emitline("\tIMULQ\tCX, AX\n");
|
||||||
|
};
|
||||||
|
if (baseisarray) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
};
|
||||||
|
emitline("\tADDQ\tAX, BX\n");
|
||||||
|
if (viaptr) { emitline("\tMOVQ\t(BX), BX\n"); };
|
||||||
|
emitline("\tPOPQ\tAX\n");
|
||||||
|
let sop: str = fieldstoreop(c, fi);
|
||||||
|
emitline("\t");
|
||||||
|
emitline(sop);
|
||||||
|
emitline("\tAX, ");
|
||||||
|
emitdispreg(fi.foff: i64, "BX");
|
||||||
|
emitline("\n");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
// 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);
|
||||||
|
emitline("\tPUSHQ\tAX\n");
|
||||||
|
cgexpr(c, idx);
|
||||||
|
if (esz > 1) {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(esz: i64);
|
||||||
|
emitline(", CX\n");
|
||||||
|
emitline("\tIMULQ\tCX, AX\n");
|
||||||
|
};
|
||||||
|
if (baseisarray) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
};
|
||||||
|
emitline("\tADDQ\tAX, BX\n");
|
||||||
|
if (viaptr) { emitline("\tMOVQ\t(BX), BX\n"); };
|
||||||
|
emitline("\tPUSHQ\tBX\n");
|
||||||
|
let lop: str = fieldloadop(c, fi);
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\t");
|
||||||
|
emitdispreg(fi.foff: i64, "BX");
|
||||||
|
emitline(", AX\n");
|
||||||
|
emitline("\tPOPQ\tBX\n");
|
||||||
|
emitline("\tPOPQ\tCX\n");
|
||||||
|
if (n.op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tCX, AX\n"); };
|
||||||
|
if (n.op == tkind.TK_MINUSEQ) { emitline("\tSUBQ\tCX, AX\n"); };
|
||||||
|
if (n.op == tkind.TK_STAREQ) { emitline("\tIMULQ\tCX, AX\n"); };
|
||||||
|
if (n.op == tkind.TK_AMPEQ) { emitline("\tANDQ\tCX, AX\n"); };
|
||||||
|
if (n.op == tkind.TK_PIPEEQ) { emitline("\tORQ\tCX, AX\n"); };
|
||||||
|
if (n.op == tkind.TK_CARETEQ) { emitline("\tXORQ\tCX, AX\n"); };
|
||||||
|
let sop2: str = fieldstoreop(c, fi);
|
||||||
|
emitline("\t");
|
||||||
|
emitline(sop2);
|
||||||
|
emitline("\tAX, ");
|
||||||
|
emitdispreg(fi.foff: i64, "BX");
|
||||||
|
emitline("\n");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
fi = fi.finext;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};};
|
||||||
|
};
|
||||||
|
};};
|
||||||
|
};
|
||||||
|
};
|
||||||
// Struct/ptr-to-struct field assignment: `s.f = expr;` or
|
// Struct/ptr-to-struct field assignment: `s.f = expr;` or
|
||||||
// `p.f = expr;`. Only plain `=` is wired (compound on field
|
// `p.f = expr;`. Only plain `=` is wired (compound on field
|
||||||
// is rare and not yet needed by our fixtures).
|
// is rare and not yet needed by our fixtures).
|
||||||
|
|||||||
@@ -11289,6 +11289,216 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
// `arr[i].field = v`: N_DOT lhs whose lhs is N_INDEX. Symmetric
|
||||||
|
// write-side of the cgdot N_INDEX-lhs branch added for task #8.
|
||||||
|
// Compute &arr[i] inline (LEAQ for `[N]Struct`, MOVQ for
|
||||||
|
// `[N]*Struct` / `[]Struct` / `*Struct`), deref once when the
|
||||||
|
// element is `*Struct`, then store rhs at field.offset(addr).
|
||||||
|
// Without this both shapes silently drop the store — there is no
|
||||||
|
// existing wwstage branch for N_DOT(N_INDEX,...) lhs at all (the
|
||||||
|
// N_INDEX-lhs branch above handles bare `arr[i] = v`, not the
|
||||||
|
// field write).
|
||||||
|
if (lhs != nil) {
|
||||||
|
if (lhs.kind == nkind.N_DOT && lhs.lhs != nil
|
||||||
|
&& lhs.lhs.kind == nkind.N_INDEX) {
|
||||||
|
let idxbase: *node = lhs.lhs.lhs;
|
||||||
|
let idx: *node = lhs.lhs.rhs;
|
||||||
|
let fld2: str = lhs.str;
|
||||||
|
if (idxbase != nil) { if (idxbase.kind == nkind.N_IDENT) {
|
||||||
|
if (idx != nil) {
|
||||||
|
let lc: *local = localfindnode(c, idxbase.str);
|
||||||
|
if (lc != nil) { if (lc.tnode != nil) {
|
||||||
|
let tn: *node = lc.tnode;
|
||||||
|
let elemt: *node = nil;
|
||||||
|
let baseisarray: bool = false;
|
||||||
|
let tk: nkind = tn.kind;
|
||||||
|
if (tk == nkind.N_TSLICE) { elemt = tn.lhs; };
|
||||||
|
if (tk == nkind.N_TARRAY) { elemt = tn.lhs; baseisarray = true; };
|
||||||
|
if (tk == nkind.N_TPTR) { elemt = tn.lhs; };
|
||||||
|
let sname: str;
|
||||||
|
sname.ptr = nil; sname.len = 0;
|
||||||
|
let viaptr: bool = false;
|
||||||
|
if (elemt != nil) {
|
||||||
|
if (elemt.kind == nkind.N_TPTR) {
|
||||||
|
let inner: *node = elemt.lhs;
|
||||||
|
if (inner != nil) { if (inner.kind == nkind.N_TNAME) {
|
||||||
|
sname = inner.str;
|
||||||
|
viaptr = true;
|
||||||
|
};};
|
||||||
|
} else { if (elemt.kind == nkind.N_TNAME) {
|
||||||
|
sname = elemt.str;
|
||||||
|
};};
|
||||||
|
};
|
||||||
|
if (sname.len > 0) {
|
||||||
|
let si: *structinfo = structlookup(c, sname);
|
||||||
|
if (si != nil) {
|
||||||
|
let fi: *fieldinfo = si.fields;
|
||||||
|
for (fi != nil) {
|
||||||
|
if (streq(fi.fname, fld2)) {
|
||||||
|
let esz: i32 = elemsizeofc(c, tn);
|
||||||
|
// f64/f32: rhs in X0. Spill to stack,
|
||||||
|
// compute &arr[i] in BX (deref if *T),
|
||||||
|
// then reload X0 and MOVSD/MOVSS.
|
||||||
|
if (n.op == tkind.TK_ASSIGN) {
|
||||||
|
if (isfloattype(c, fi.tnode)) {
|
||||||
|
let mov: str = "MOVSD";
|
||||||
|
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
||||||
|
cgexpr(c, n.rhs);
|
||||||
|
emitline("\tSUBQ\t$8, SP\n");
|
||||||
|
emitline("\t");
|
||||||
|
emitline(mov);
|
||||||
|
emitline("\tX0, (SP)\n");
|
||||||
|
cgexpr(c, idx);
|
||||||
|
if (esz > 1) {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(esz: i64);
|
||||||
|
emitline(", CX\n");
|
||||||
|
emitline("\tIMULQ\tCX, AX\n");
|
||||||
|
};
|
||||||
|
if (baseisarray) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
};
|
||||||
|
emitline("\tADDQ\tAX, BX\n");
|
||||||
|
if (viaptr) { emitline("\tMOVQ\t(BX), BX\n"); };
|
||||||
|
emitline("\t");
|
||||||
|
emitline(mov);
|
||||||
|
emitline("\t(SP), X0\n");
|
||||||
|
emitline("\tADDQ\t$8, SP\n");
|
||||||
|
emitline("\t");
|
||||||
|
emitline(mov);
|
||||||
|
emitline("\tX0, ");
|
||||||
|
emitdispreg(fi.foff: i64, "BX");
|
||||||
|
emitline("\n");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
// str rhs: AX=ptr, BX=len. Stash both,
|
||||||
|
// compute addr in CX so the pop pair
|
||||||
|
// restores AX/BX intact.
|
||||||
|
if (isstrtype(c, fi.tnode)) {
|
||||||
|
cgexpr(c, n.rhs);
|
||||||
|
emitline("\tPUSHQ\tBX\n");
|
||||||
|
emitline("\tPUSHQ\tAX\n");
|
||||||
|
cgexpr(c, idx);
|
||||||
|
if (esz > 1) {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(esz: i64);
|
||||||
|
emitline(", CX\n");
|
||||||
|
emitline("\tIMULQ\tCX, AX\n");
|
||||||
|
};
|
||||||
|
if (baseisarray) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
};
|
||||||
|
emitline("\tADDQ\tAX, CX\n");
|
||||||
|
if (viaptr) { emitline("\tMOVQ\t(CX), CX\n"); };
|
||||||
|
emitline("\tPOPQ\tAX\n");
|
||||||
|
emitline("\tPOPQ\tBX\n");
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitdispreg(fi.foff: i64, "CX");
|
||||||
|
emitline("\n");
|
||||||
|
emitline("\tMOVQ\tBX, ");
|
||||||
|
emitdispreg((fi.foff + 8): i64, "CX");
|
||||||
|
emitline("\n");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
// scalar plain `=`
|
||||||
|
cgexpr(c, n.rhs);
|
||||||
|
emitline("\tPUSHQ\tAX\n");
|
||||||
|
cgexpr(c, idx);
|
||||||
|
if (esz > 1) {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(esz: i64);
|
||||||
|
emitline(", CX\n");
|
||||||
|
emitline("\tIMULQ\tCX, AX\n");
|
||||||
|
};
|
||||||
|
if (baseisarray) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
};
|
||||||
|
emitline("\tADDQ\tAX, BX\n");
|
||||||
|
if (viaptr) { emitline("\tMOVQ\t(BX), BX\n"); };
|
||||||
|
emitline("\tPOPQ\tAX\n");
|
||||||
|
let sop: str = fieldstoreop(c, fi);
|
||||||
|
emitline("\t");
|
||||||
|
emitline(sop);
|
||||||
|
emitline("\tAX, ");
|
||||||
|
emitdispreg(fi.foff: i64, "BX");
|
||||||
|
emitline("\n");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
// 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);
|
||||||
|
emitline("\tPUSHQ\tAX\n");
|
||||||
|
cgexpr(c, idx);
|
||||||
|
if (esz > 1) {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(esz: i64);
|
||||||
|
emitline(", CX\n");
|
||||||
|
emitline("\tIMULQ\tCX, AX\n");
|
||||||
|
};
|
||||||
|
if (baseisarray) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
};
|
||||||
|
emitline("\tADDQ\tAX, BX\n");
|
||||||
|
if (viaptr) { emitline("\tMOVQ\t(BX), BX\n"); };
|
||||||
|
emitline("\tPUSHQ\tBX\n");
|
||||||
|
let lop: str = fieldloadop(c, fi);
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\t");
|
||||||
|
emitdispreg(fi.foff: i64, "BX");
|
||||||
|
emitline(", AX\n");
|
||||||
|
emitline("\tPOPQ\tBX\n");
|
||||||
|
emitline("\tPOPQ\tCX\n");
|
||||||
|
if (n.op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tCX, AX\n"); };
|
||||||
|
if (n.op == tkind.TK_MINUSEQ) { emitline("\tSUBQ\tCX, AX\n"); };
|
||||||
|
if (n.op == tkind.TK_STAREQ) { emitline("\tIMULQ\tCX, AX\n"); };
|
||||||
|
if (n.op == tkind.TK_AMPEQ) { emitline("\tANDQ\tCX, AX\n"); };
|
||||||
|
if (n.op == tkind.TK_PIPEEQ) { emitline("\tORQ\tCX, AX\n"); };
|
||||||
|
if (n.op == tkind.TK_CARETEQ) { emitline("\tXORQ\tCX, AX\n"); };
|
||||||
|
let sop2: str = fieldstoreop(c, fi);
|
||||||
|
emitline("\t");
|
||||||
|
emitline(sop2);
|
||||||
|
emitline("\tAX, ");
|
||||||
|
emitdispreg(fi.foff: i64, "BX");
|
||||||
|
emitline("\n");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
fi = fi.finext;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};};
|
||||||
|
};
|
||||||
|
};};
|
||||||
|
};
|
||||||
|
};
|
||||||
// Struct/ptr-to-struct field assignment: `s.f = expr;` or
|
// Struct/ptr-to-struct field assignment: `s.f = expr;` or
|
||||||
// `p.f = expr;`. Only plain `=` is wired (compound on field
|
// `p.f = expr;`. Only plain `=` is wired (compound on field
|
||||||
// is rare and not yet needed by our fixtures).
|
// is rare and not yet needed by our fixtures).
|
||||||
|
|||||||
284
test/wcc/681_arr_elem_field_write.c
Normal file
284
test/wcc/681_arr_elem_field_write.c
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
/*
|
||||||
|
* 681_arr_elem_field_write — `arr[i].field = v` (write-side counterpart
|
||||||
|
* of 680_arr_elem_field). Both stages had a silent store-drop:
|
||||||
|
*
|
||||||
|
* cstage's chained-pointer-field-write branch (cgen.c near 1986) caught
|
||||||
|
* `[N]*Struct` writes via its `!= N_IDENT` guard but skipped `[N]Struct`
|
||||||
|
* value-arrays (TY_STRUCT element fails the TY_PTR guard).
|
||||||
|
*
|
||||||
|
* wwstage had no N_DOT(N_INDEX,...) lhs branch at all in cgassign
|
||||||
|
* (cgenexpr.ww). Both shapes silently emitted no store.
|
||||||
|
*
|
||||||
|
* Closed in task #16 by mirroring task #8's read-side N_INDEX-lhs branch
|
||||||
|
* onto the write side (fldstoreop, str/float leaf coverage, deref-or-not
|
||||||
|
* for `*Struct` vs `Struct` element).
|
||||||
|
*
|
||||||
|
* Coverage — both array shapes, scalar/sub-word/str/float leaves, dyn
|
||||||
|
* idx, and read-modify-write compound (`arr[i].x += 5`). Cstage and
|
||||||
|
* wwstage on every fixture; wwstage gated on access(X_OK).
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
static int
|
||||||
|
runwait(const char *cmd)
|
||||||
|
{
|
||||||
|
int rc = system(cmd);
|
||||||
|
if (rc == -1) return -1;
|
||||||
|
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct row { const char *label; const char *src; int want; };
|
||||||
|
|
||||||
|
static const struct row rows[] = {
|
||||||
|
/* [N]*Struct, i32 field write. Sole write into stk[0].x; read it
|
||||||
|
* back. Returns 42. */
|
||||||
|
{ "ptr_arr_i32_write",
|
||||||
|
"type nd = struct { name: str, x: i32 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let v: nd; v.name = \"hi\"; v.x = 0;\n"
|
||||||
|
" let stk: [16]*nd; stk[0] = &v;\n"
|
||||||
|
" stk[0].x = 42;\n"
|
||||||
|
" return stk[0].x;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* [N]*Struct, u8 field write. Sub-word store via MOVB through
|
||||||
|
* fieldstoreop. Returns 9. */
|
||||||
|
{ "ptr_arr_u8_write",
|
||||||
|
"type nd = struct { tag: u8, pad: u8, x: i32 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let v: nd; v.tag = 0u8; v.pad = 0u8; v.x = 0;\n"
|
||||||
|
" let stk: [4]*nd; stk[0] = &v;\n"
|
||||||
|
" stk[0].tag = 9u8;\n"
|
||||||
|
" return stk[0].tag: i32;\n"
|
||||||
|
"};\n",
|
||||||
|
9 },
|
||||||
|
/* [N]*Struct, str field write — 16B store of both ptr+len.
|
||||||
|
* Returns len("hello") = 5. */
|
||||||
|
{ "ptr_arr_str_write",
|
||||||
|
"type nd = struct { a: i32, b: i32, name: str };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let v: nd; v.a = 0; v.b = 0; v.name = \"old\";\n"
|
||||||
|
" let stk: [16]*nd; stk[0] = &v;\n"
|
||||||
|
" stk[0].name = \"hello\";\n"
|
||||||
|
" return stk[0].name.len: i32;\n"
|
||||||
|
"};\n",
|
||||||
|
5 },
|
||||||
|
/* [N]Struct, i32 field write (value-array). The cstage chained-
|
||||||
|
* pointer-field branch's TY_PTR guard skips this; without the new
|
||||||
|
* value-array path the store silently drops. Returns 50. */
|
||||||
|
{ "val_arr_i32_write",
|
||||||
|
"type nd = struct { name: str, x: i32 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let arr: [4]nd;\n"
|
||||||
|
" arr[2].x = 50;\n"
|
||||||
|
" return arr[2].x;\n"
|
||||||
|
"};\n",
|
||||||
|
50 },
|
||||||
|
/* [N]Struct, u8 field write — value-array sub-word store. Returns 7. */
|
||||||
|
{ "val_arr_u8_write",
|
||||||
|
"type nd = struct { tag: u8, pad: u8, x: i32 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let arr: [4]nd;\n"
|
||||||
|
" arr[1].tag = 7u8;\n"
|
||||||
|
" return arr[1].tag: i32;\n"
|
||||||
|
"};\n",
|
||||||
|
7 },
|
||||||
|
/* [N]Struct, str field write — value-array 16B store. Returns
|
||||||
|
* len("world") = 5. */
|
||||||
|
{ "val_arr_str_write",
|
||||||
|
"type nd = struct { name: str, x: i32 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let arr: [4]nd;\n"
|
||||||
|
" arr[1].name = \"world\";\n"
|
||||||
|
" return arr[1].name.len: i32;\n"
|
||||||
|
"};\n",
|
||||||
|
5 },
|
||||||
|
/* Sub-word signed: write -3i8 then read back as i32. If MOVB
|
||||||
|
* stored the truncated low byte and the field's fldloadop sign-
|
||||||
|
* extends correctly, the i32 read returns -3. Returns 42 when
|
||||||
|
* the equality check passes, 0 otherwise. */
|
||||||
|
{ "ptr_arr_i8_signed_write",
|
||||||
|
"type nd = struct { tag: i8, pad: u8, x: i32 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let v: nd; v.tag = 0i8; v.pad = 0u8; v.x = 0;\n"
|
||||||
|
" let stk: [4]*nd; stk[0] = &v;\n"
|
||||||
|
" stk[0].tag = -3i8;\n"
|
||||||
|
" let t: i32 = stk[0].tag: i32;\n"
|
||||||
|
" if (t == -3) { return 42; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* Value-array sub-word signed: distinct from ptr_arr_i8_signed —
|
||||||
|
* this path takes LEAQ &arr[i] (no MOVQ-to-deref) and stores the
|
||||||
|
* truncated byte via fldstoreop MOVB. Read back via fldloadop
|
||||||
|
* MOVSBQ sign-extends to i32; -3 round-trips. Returns 42. */
|
||||||
|
{ "val_arr_i8_signed_write",
|
||||||
|
"type nd = struct { tag: i8, pad: u8, x: i32 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let arr: [4]nd;\n"
|
||||||
|
" arr[1].tag = -3i8;\n"
|
||||||
|
" let t: i32 = arr[1].tag: i32;\n"
|
||||||
|
" if (t == -3) { return 42; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* f64 field write through value-array — pins MOVSD from X0 into
|
||||||
|
* field offset of &arr[i]. Returns 23. */
|
||||||
|
{ "val_arr_f64_write",
|
||||||
|
"type nd = struct { x: i32, d: f64 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let arr: [4]nd;\n"
|
||||||
|
" arr[1].d = 23.0f64;\n"
|
||||||
|
" let v: f64 = arr[1].d;\n"
|
||||||
|
" return v: i32;\n"
|
||||||
|
"};\n",
|
||||||
|
23 },
|
||||||
|
/* Dyn idx through [N]*Struct write — index isn't a literal, so
|
||||||
|
* the IMULQ path fires. Returns 99. */
|
||||||
|
{ "ptr_arr_dyn_idx_write",
|
||||||
|
"type nd = struct { name: str, x: i32 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let a: nd; a.name = \"a\"; a.x = 0;\n"
|
||||||
|
" let b: nd; b.name = \"b\"; b.x = 0;\n"
|
||||||
|
" let c: nd; c.name = \"c\"; c.x = 0;\n"
|
||||||
|
" let stk: [4]*nd;\n"
|
||||||
|
" stk[0] = &a; stk[1] = &b; stk[2] = &c;\n"
|
||||||
|
" let i: i32 = 2;\n"
|
||||||
|
" stk[i].x = 99;\n"
|
||||||
|
" return stk[2].x;\n"
|
||||||
|
"};\n",
|
||||||
|
99 },
|
||||||
|
/* Read-modify-write compound on [N]Struct — exercises BOTH the
|
||||||
|
* read (load old field) and the write (store combined). For
|
||||||
|
* `arr[1].x += 5` with arr[1].x = 37 → 42. */
|
||||||
|
{ "val_arr_compound_plus",
|
||||||
|
"type nd = struct { name: str, x: i32 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let arr: [4]nd;\n"
|
||||||
|
" arr[1].x = 37;\n"
|
||||||
|
" arr[1].x += 5;\n"
|
||||||
|
" return arr[1].x;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* Non-PLUSEQ compound (CARETEQ) on [N]Struct — exercises the XORQ
|
||||||
|
* arm of the compound switch. Worker wired all six integer
|
||||||
|
* compound ops; this pins one of the non-PLUSEQ arms so a future
|
||||||
|
* regression in the switch table is caught. 0x2A ^ 0x14 = 0x3E
|
||||||
|
* (62), then return 62 - 20 = 42. */
|
||||||
|
{ "val_arr_compound_xor",
|
||||||
|
"type nd = struct { name: str, x: i32 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let arr: [4]nd;\n"
|
||||||
|
" arr[2].x = 42;\n"
|
||||||
|
" arr[2].x ^= 20;\n"
|
||||||
|
" return arr[2].x - 20;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* Compound through [N]*Struct — same shape but viaptr is true,
|
||||||
|
* so the address compute deref-loads (BX), and the load_op picks
|
||||||
|
* MOVSXD for i32. 10 += 22 → 32. */
|
||||||
|
{ "ptr_arr_compound_plus",
|
||||||
|
"type nd = struct { name: str, x: i32 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let v: nd; v.name = \"a\"; v.x = 10;\n"
|
||||||
|
" let stk: [4]*nd; stk[0] = &v;\n"
|
||||||
|
" stk[0].x += 22;\n"
|
||||||
|
" return stk[0].x;\n"
|
||||||
|
"};\n",
|
||||||
|
32 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
run_driver(const char *driver, const struct row *r, int i)
|
||||||
|
{
|
||||||
|
char src[64], tmpdir[64], cmd[1024];
|
||||||
|
snprintf(src, sizeof src, "/tmp/waew_%d_%d.ww", getpid(), i);
|
||||||
|
snprintf(tmpdir, sizeof tmpdir, "/tmp/waew_%d_d_%d", getpid(), i);
|
||||||
|
|
||||||
|
FILE *f = fopen(src, "wb");
|
||||||
|
if (!f) return -1;
|
||||||
|
fputs(r->src, f);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
mkdir(tmpdir, 0755);
|
||||||
|
snprintf(cmd, sizeof cmd, "cd %s && %s build %s",
|
||||||
|
tmpdir, driver, src);
|
||||||
|
if (runwait(cmd) != 0) {
|
||||||
|
fprintf(stderr, "row[%s]: build via %s failed\n",
|
||||||
|
r->label, driver);
|
||||||
|
unlink(src); rmdir(tmpdir);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *base = strrchr(src, '/');
|
||||||
|
base = base ? base + 1 : src;
|
||||||
|
char outbin[128];
|
||||||
|
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
|
||||||
|
char *dot = strrchr(outbin, '.');
|
||||||
|
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
|
||||||
|
int got = runwait(outbin);
|
||||||
|
|
||||||
|
unlink(src); unlink(outbin); rmdir(tmpdir);
|
||||||
|
return got;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
const char *bin = getenv("BIN");
|
||||||
|
if (!bin) bin = "out/bin";
|
||||||
|
char absbin[1024];
|
||||||
|
if (bin[0] != '/') {
|
||||||
|
char cwd[1024];
|
||||||
|
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||||
|
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||||
|
bin = absbin;
|
||||||
|
}
|
||||||
|
|
||||||
|
char cdrv[1024];
|
||||||
|
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||||
|
char wdrv[1024];
|
||||||
|
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||||
|
|
||||||
|
struct { const char *name; const char *path; int gated_on_existence; }
|
||||||
|
drivers[] = {
|
||||||
|
{ "cstage", cdrv, 0 },
|
||||||
|
{ "wwstage", wdrv, 1 },
|
||||||
|
{ NULL, NULL, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||||
|
int total = 0, fail = 0;
|
||||||
|
for (int d = 0; drivers[d].name; d++) {
|
||||||
|
if (drivers[d].gated_on_existence
|
||||||
|
&& access(drivers[d].path, X_OK) != 0) {
|
||||||
|
fprintf(stderr, "arr_elem_field_write: skip %s (no %s)\n",
|
||||||
|
drivers[d].name, drivers[d].path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
int got = run_driver(drivers[d].path, &rows[i], i);
|
||||||
|
total++;
|
||||||
|
if (got != rows[i].want) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"arr_elem_field_write[%s][%s]: exit=%d want=%d\n",
|
||||||
|
drivers[d].name, rows[i].label,
|
||||||
|
got, rows[i].want);
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fail) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"arr_elem_field_write: %d/%d fixtures failed\n", fail, total);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("arr_elem_field_write: %d/%d ok\n", total, total);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user