w6c: tagged-union fields on struct globals — LEAQ-based read+write
Field write extends the existing TY_TAGGED branch with an is_global arm: LEAQ name(SB),CX after cgexpr (no AX/BX clobber), then MOVQ into slot+foff+0 (tag) and slot+foff+8 (value, plus +16 for str- typed variants). Field read now treats tagged fields specially — load AX=tag, DX=val0, CX=val1 (when union >16B), mirroring the tagged-return ABI that let-init and match dispatch already expect. Previously the scalar-load path read 8B into AX and left DX/CX with junk, which silently broke local tagged-field reads too.
This commit is contained in:
@@ -1160,29 +1160,49 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
if (f == NULL) break;
|
if (f == NULL) break;
|
||||||
/* Tagged-union field: synthesise tag and store
|
/* Tagged-union field: synthesise tag and store
|
||||||
* value bytes. Compound ops on tagged fields are
|
* value bytes. Compound ops on tagged fields are
|
||||||
* not meaningful, so only plain `=` is wired. */
|
* not meaningful, so only plain `=` is wired.
|
||||||
|
* Three base shapes:
|
||||||
|
* - via_ptr: base is *struct local; address
|
||||||
|
* pre-loaded into BX. Buggy with a str
|
||||||
|
* variant since cgexpr will overwrite BX,
|
||||||
|
* but matches the existing pre-global
|
||||||
|
* behaviour.
|
||||||
|
* - is_global: struct global. LEAQ after
|
||||||
|
* cgexpr drops the slot address into CX
|
||||||
|
* without touching AX/BX, so str variants
|
||||||
|
* work cleanly.
|
||||||
|
* - else: struct local, BP-relative. */
|
||||||
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;
|
||||||
if (fu && fu->kind == TY_TAGGED
|
if (fu && fu->kind == TY_TAGGED
|
||||||
&& n->op == TK_ASSIGN) {
|
&& n->op == TK_ASSIGN) {
|
||||||
int boff = localfind(locals, base->str);
|
int boff = localfind(locals, base->str);
|
||||||
|
int is_global = (boff == 0 && !via_ptr
|
||||||
|
&& let_islet(base->str));
|
||||||
int foff = (int)f->offset;
|
int foff = (int)f->offset;
|
||||||
Type *vt = n->rhs ? n->rhs->type : NULL;
|
Type *vt = n->rhs ? n->rhs->type : NULL;
|
||||||
int tag = cg_tag_for_variant(fu, vt);
|
int tag = cg_tag_for_variant(fu, vt);
|
||||||
/* Resolve the slot's base address
|
|
||||||
* (either struct local or *struct). */
|
|
||||||
int addr_in_bx = via_ptr;
|
int addr_in_bx = via_ptr;
|
||||||
if (via_ptr)
|
if (via_ptr)
|
||||||
ins2(c, A_MOVQ, amem(D_BP, boff),
|
ins2(c, A_MOVQ, amem(D_BP, boff),
|
||||||
areg(D_BX));
|
areg(D_BX));
|
||||||
cgexpr(c, n->rhs, locals);
|
cgexpr(c, n->rhs, locals);
|
||||||
|
if (is_global)
|
||||||
|
ins2(c, A_LEAQ,
|
||||||
|
masym(c, base->str),
|
||||||
|
areg(D_CX));
|
||||||
if (type_isstr(vt)) {
|
if (type_isstr(vt)) {
|
||||||
if (addr_in_bx) {
|
if (addr_in_bx) {
|
||||||
ins2(c, A_MOVQ, areg(D_AX),
|
ins2(c, A_MOVQ, areg(D_AX),
|
||||||
amem(D_BX, foff + 8));
|
amem(D_BX, foff + 8));
|
||||||
ins2(c, A_MOVQ, areg(D_BX),
|
ins2(c, A_MOVQ, areg(D_BX),
|
||||||
amem(D_BX, foff + 16));
|
amem(D_BX, foff + 16));
|
||||||
|
} else if (is_global) {
|
||||||
|
ins2(c, A_MOVQ, areg(D_AX),
|
||||||
|
amem(D_CX, foff + 8));
|
||||||
|
ins2(c, A_MOVQ, areg(D_BX),
|
||||||
|
amem(D_CX, foff + 16));
|
||||||
} else {
|
} else {
|
||||||
ins2(c, A_MOVQ, areg(D_AX),
|
ins2(c, A_MOVQ, areg(D_AX),
|
||||||
amem(D_BP, boff + foff + 8));
|
amem(D_BP, boff + foff + 8));
|
||||||
@@ -1193,6 +1213,9 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
if (addr_in_bx)
|
if (addr_in_bx)
|
||||||
ins2(c, A_MOVQ, areg(D_AX),
|
ins2(c, A_MOVQ, areg(D_AX),
|
||||||
amem(D_BX, foff + 8));
|
amem(D_BX, foff + 8));
|
||||||
|
else if (is_global)
|
||||||
|
ins2(c, A_MOVQ, areg(D_AX),
|
||||||
|
amem(D_CX, foff + 8));
|
||||||
else
|
else
|
||||||
ins2(c, A_MOVQ, areg(D_AX),
|
ins2(c, A_MOVQ, areg(D_AX),
|
||||||
amem(D_BP, boff + foff + 8));
|
amem(D_BP, boff + foff + 8));
|
||||||
@@ -1201,6 +1224,10 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
ins2(c, A_MOVQ,
|
ins2(c, A_MOVQ,
|
||||||
aimm(tag < 0 ? 0 : tag),
|
aimm(tag < 0 ? 0 : tag),
|
||||||
amem(D_BX, foff + 0));
|
amem(D_BX, foff + 0));
|
||||||
|
else if (is_global)
|
||||||
|
ins2(c, A_MOVQ,
|
||||||
|
aimm(tag < 0 ? 0 : tag),
|
||||||
|
amem(D_CX, foff + 0));
|
||||||
else
|
else
|
||||||
ins2(c, A_MOVQ,
|
ins2(c, A_MOVQ,
|
||||||
aimm(tag < 0 ? 0 : tag),
|
aimm(tag < 0 ? 0 : tag),
|
||||||
@@ -2636,9 +2663,28 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
base_reg = D_CX;
|
base_reg = D_CX;
|
||||||
base_disp = 0;
|
base_disp = 0;
|
||||||
}
|
}
|
||||||
(void)is_global;
|
|
||||||
for (Tfield *f = u->fields; f; f = f->next) {
|
for (Tfield *f = u->fields; f; f = f->next) {
|
||||||
if (strcmp(f->name, n->str) != 0) continue;
|
if (strcmp(f->name, n->str) != 0) continue;
|
||||||
|
/* tagged-union field: load AX=tag, DX=val0,
|
||||||
|
* CX=val1 (last, since for globals CX is also
|
||||||
|
* the base addr). Mirrors the tagged-return
|
||||||
|
* ABI so the let-init / match dispatch shapes
|
||||||
|
* just work. */
|
||||||
|
Type *tag_fu = (f->type && f->type->kind == TY_NAMED)
|
||||||
|
? f->type->under : f->type;
|
||||||
|
if (tag_fu && tag_fu->kind == TY_TAGGED) {
|
||||||
|
int fo = base_disp + (int)f->offset;
|
||||||
|
ins2(c, A_MOVQ,
|
||||||
|
amem(base_reg, fo + 0), areg(D_AX));
|
||||||
|
ins2(c, A_MOVQ,
|
||||||
|
amem(base_reg, fo + 8), areg(D_DX));
|
||||||
|
if (tag_fu->size > 16)
|
||||||
|
ins2(c, A_MOVQ,
|
||||||
|
amem(base_reg, fo + 16),
|
||||||
|
areg(D_CX));
|
||||||
|
(void)is_global;
|
||||||
|
break;
|
||||||
|
}
|
||||||
/* str field: load (ptr, len) into (AX, BX) so the
|
/* str field: load (ptr, len) into (AX, BX) so the
|
||||||
* value flows through the str-rhs convention. */
|
* value flows through the str-rhs convention. */
|
||||||
Type *str_fu = (f->type && f->type->kind == TY_NAMED)
|
Type *str_fu = (f->type && f->type->kind == TY_NAMED)
|
||||||
|
|||||||
@@ -232,6 +232,61 @@ static const struct fixture fixtures[] = {
|
|||||||
"};\n",
|
"};\n",
|
||||||
11,
|
11,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"struct-tagged-field-i64",
|
||||||
|
/* Tagged-union field on a struct global. Write goes
|
||||||
|
* LEAQ name(SB),CX + MOVQ to slot+foff+8 (value) and
|
||||||
|
* slot+foff+0 (tag). Read pulls AX=tag, DX=val0,
|
||||||
|
* CX=val1 from those same slots and match dispatches. */
|
||||||
|
"type tok = (i64 | str | void);\n"
|
||||||
|
"type ent = struct { id: i32, t: tok };\n"
|
||||||
|
"let e: ent;\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
"\te.t = 42i64: tok;\n"
|
||||||
|
"\tlet v: tok = e.t;\n"
|
||||||
|
"\tmatch (v) {\n"
|
||||||
|
"\tcase let n: i64 => return n: i32;\n"
|
||||||
|
"\tcase let s: str => return 1;\n"
|
||||||
|
"\tcase void => return 2;\n"
|
||||||
|
"\t};\n"
|
||||||
|
"\treturn 0;\n"
|
||||||
|
"};\n",
|
||||||
|
42,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"struct-tagged-field-str",
|
||||||
|
"type tok = (i64 | str | void);\n"
|
||||||
|
"type ent = struct { id: i32, t: tok };\n"
|
||||||
|
"let e: ent;\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
"\te.t = \"hello\";\n"
|
||||||
|
"\tlet v: tok = e.t;\n"
|
||||||
|
"\tmatch (v) {\n"
|
||||||
|
"\tcase let n: i64 => return 1;\n"
|
||||||
|
"\tcase let s: str => return s.len: i32;\n"
|
||||||
|
"\tcase void => return 2;\n"
|
||||||
|
"\t};\n"
|
||||||
|
"\treturn 0;\n"
|
||||||
|
"};\n",
|
||||||
|
5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"struct-tagged-field-void",
|
||||||
|
"type tok = (i64 | str | void);\n"
|
||||||
|
"type ent = struct { id: i32, t: tok };\n"
|
||||||
|
"let e: ent;\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
"\te.t = void;\n"
|
||||||
|
"\tlet v: tok = e.t;\n"
|
||||||
|
"\tmatch (v) {\n"
|
||||||
|
"\tcase let n: i64 => return 1;\n"
|
||||||
|
"\tcase let s: str => return 2;\n"
|
||||||
|
"\tcase void => return 7;\n"
|
||||||
|
"\t};\n"
|
||||||
|
"\treturn 0;\n"
|
||||||
|
"};\n",
|
||||||
|
7,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"struct-narrow-field",
|
"struct-narrow-field",
|
||||||
/* u8 field on a struct global. Read uses MOVZBQ, store
|
/* u8 field on a struct global. Read uses MOVZBQ, store
|
||||||
|
|||||||
Reference in New Issue
Block a user