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;
|
||||
/* Tagged-union field: synthesise tag and store
|
||||
* 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 *fu = (ft && ft->kind == TY_NAMED)
|
||||
? ft->under : ft;
|
||||
if (fu && fu->kind == TY_TAGGED
|
||||
&& n->op == TK_ASSIGN) {
|
||||
int boff = localfind(locals, base->str);
|
||||
int is_global = (boff == 0 && !via_ptr
|
||||
&& let_islet(base->str));
|
||||
int foff = (int)f->offset;
|
||||
Type *vt = n->rhs ? n->rhs->type : NULL;
|
||||
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;
|
||||
if (via_ptr)
|
||||
ins2(c, A_MOVQ, amem(D_BP, boff),
|
||||
areg(D_BX));
|
||||
cgexpr(c, n->rhs, locals);
|
||||
if (is_global)
|
||||
ins2(c, A_LEAQ,
|
||||
masym(c, base->str),
|
||||
areg(D_CX));
|
||||
if (type_isstr(vt)) {
|
||||
if (addr_in_bx) {
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BX, foff + 8));
|
||||
ins2(c, A_MOVQ, areg(D_BX),
|
||||
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 {
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, boff + foff + 8));
|
||||
@@ -1193,6 +1213,9 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
if (addr_in_bx)
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BX, foff + 8));
|
||||
else if (is_global)
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_CX, foff + 8));
|
||||
else
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, boff + foff + 8));
|
||||
@@ -1201,6 +1224,10 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
ins2(c, A_MOVQ,
|
||||
aimm(tag < 0 ? 0 : tag),
|
||||
amem(D_BX, foff + 0));
|
||||
else if (is_global)
|
||||
ins2(c, A_MOVQ,
|
||||
aimm(tag < 0 ? 0 : tag),
|
||||
amem(D_CX, foff + 0));
|
||||
else
|
||||
ins2(c, A_MOVQ,
|
||||
aimm(tag < 0 ? 0 : tag),
|
||||
@@ -2636,9 +2663,28 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
base_reg = D_CX;
|
||||
base_disp = 0;
|
||||
}
|
||||
(void)is_global;
|
||||
for (Tfield *f = u->fields; f; f = f->next) {
|
||||
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
|
||||
* value flows through the str-rhs convention. */
|
||||
Type *str_fu = (f->type && f->type->kind == TY_NAMED)
|
||||
|
||||
Reference in New Issue
Block a user