w6c+selfhost: cg_widen_tagged_store basereg + N_ASSIGN tagged field (closes #26)
Extended cg_widen_tagged_store (cstage) / cgwidentaggedstore (wwstage) to take a base_reg/basereg parameter so the primitive supports non-BP destinations. Cstage extends body in-place via via_outer gate + spill+scratch+copy-out; wwstage splits into wrapper (non-BP) + cgwidentaggedstorebp (BP-only) to dodge the no-goto constraint. New N_ASSIGN field TY_TAGGED branch routes through the primitive for all rhs shapes. Scope-adjacent: fieldsize recurses through N_TTAGGED via slotsize and TNAME-aliased-to-tagged via aliaslookup. Needed for the test fixtures. Wwstage read-side N_DOT-of-tagged-field source is filed as task #28; test rows use mark-canary verification until that lands.
This commit is contained in:
193
cmd/w6c/cgen.c
193
cmd/w6c/cgen.c
@@ -862,7 +862,7 @@ localfind(Local *head, const char *name)
|
||||
static void cgexpr(Cg*, Node*, Local*);
|
||||
static void cgstmt(Cg*, Node*, Local**, int*);
|
||||
static void cg_widen_tagged_push(Cg*, Local**, Type*, Node*, int);
|
||||
static void cg_widen_tagged_store(Cg*, Local**, Type*, Node*, int, int);
|
||||
static void cg_widen_tagged_store(Cg*, Local**, Type*, Node*, int, int, int);
|
||||
static void cg_widen_tag_remap(Cg*, Type*, Type*, int);
|
||||
|
||||
static void
|
||||
@@ -913,9 +913,21 @@ cg_widen_tag_remap(Cg *c, Type *du, Type *su, int slot_off)
|
||||
}
|
||||
|
||||
/* cg_widen_tagged_store — write the tagged-union slot bytes for `src`
|
||||
* into BP+slot_off, sized to `sz` (8 for nullable fold, else 16/24+).
|
||||
* Used by call-site widening (via cg_widen_tagged_push) and by the
|
||||
* let/assign/return/struct-field-init paths.
|
||||
* into base_reg+slot_off, sized to `sz` (8 for nullable fold, else
|
||||
* 16/24+). Used by call-site widening (via cg_widen_tagged_push) and
|
||||
* by the let/assign/return/struct-field-init paths.
|
||||
*
|
||||
* base_reg picks the addressing root for every write:
|
||||
* - D_BP: function-frame slot. The original layout — callers pass
|
||||
* a BP-relative slot_off and the function writes directly.
|
||||
* - else (e.g. D_BX for a *struct field, D_CX for a top-level
|
||||
* struct field): pointer-rooted dst. cgexpr inside this function
|
||||
* trashes every GPR, so we can't carry base_reg across — instead
|
||||
* we route every write through a fresh BP-rooted scratch slot,
|
||||
* reload base_reg from a temp spill at the end, and word-copy
|
||||
* scratch → (base_reg, slot_off). Caller is responsible for
|
||||
* loading base_reg with the dst address before the call; the
|
||||
* function preserves it across cgexpr via the spill.
|
||||
*
|
||||
* Branches by source shape (tagged_arg_size > 0 source counts as a
|
||||
* tagged subset — possibly with different variant indices):
|
||||
@@ -929,13 +941,34 @@ cg_widen_tag_remap(Cg *c, Type *du, Type *su, int slot_off)
|
||||
* - scalar: cgexpr leaves AX; store at +8 with zero pad. */
|
||||
static void
|
||||
cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src,
|
||||
int slot_off, int sz)
|
||||
int base_reg, int slot_off, int sz)
|
||||
{
|
||||
/* For pointer-rooted dst, materialise into a BP-rooted scratch
|
||||
* slot — body writes via `amem(D_BP, write_off + k)` — then copy
|
||||
* out. Spill base_reg first so cgexpr can clobber freely. */
|
||||
int via_outer = (base_reg != D_BP);
|
||||
int base_spill = 0;
|
||||
int write_off = slot_off;
|
||||
if (via_outer) {
|
||||
const char *spname = mklabel(c, "tagbase");
|
||||
base_spill = local_alloc(c, locals_p, spname, 8, cg_frame);
|
||||
ins2(c, A_MOVQ, areg(base_reg), amem(D_BP, base_spill));
|
||||
const char *scname = mklabel(c, "tagscr");
|
||||
write_off = local_alloc(c, locals_p, scname, sz, cg_frame);
|
||||
/* Pre-zero so str/scalar branches (which leave high words
|
||||
* untouched when sz exceeds the variant's footprint) still
|
||||
* deliver a clean slot to the copy-out. */
|
||||
ins2(c, A_XORQ, areg(D_AX), areg(D_AX));
|
||||
for (int k = 0; k < sz; k += 8)
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, write_off + k));
|
||||
}
|
||||
Type *du = (dst && dst->kind == TY_NAMED) ? dst->under : dst;
|
||||
if (du == NULL || du->kind != TY_TAGGED) return;
|
||||
if (du->nullable) {
|
||||
cgexpr(c, src, *locals_p);
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, slot_off + 0));
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, write_off + 0));
|
||||
if (via_outer) goto copy_out;
|
||||
return;
|
||||
}
|
||||
/* `expr: TaggedAlias` where the cast's destination IS the union
|
||||
@@ -970,29 +1003,30 @@ cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src,
|
||||
ins2(c, A_MOVQ, amem(D_BP, soff + k),
|
||||
areg(D_AX));
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, slot_off + k));
|
||||
amem(D_BP, write_off + k));
|
||||
}
|
||||
} else {
|
||||
cgexpr(c, src, *locals_p);
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, slot_off + 0));
|
||||
amem(D_BP, write_off + 0));
|
||||
if (ssz > 8)
|
||||
ins2(c, A_MOVQ, areg(D_DX),
|
||||
amem(D_BP, slot_off + 8));
|
||||
amem(D_BP, write_off + 8));
|
||||
if (ssz > 16)
|
||||
ins2(c, A_MOVQ, areg(D_CX),
|
||||
amem(D_BP, slot_off + 16));
|
||||
amem(D_BP, write_off + 16));
|
||||
if (ssz > 24)
|
||||
ins2(c, A_MOVQ, areg(D_R8),
|
||||
amem(D_BP, slot_off + 24));
|
||||
amem(D_BP, write_off + 24));
|
||||
}
|
||||
if (ssz < sz) {
|
||||
ins2(c, A_XORQ, areg(D_AX), areg(D_AX));
|
||||
for (int k = ssz; k < sz; k += 8)
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, slot_off + k));
|
||||
amem(D_BP, write_off + k));
|
||||
}
|
||||
cg_widen_tag_remap(c, du, su, slot_off);
|
||||
cg_widen_tag_remap(c, du, su, write_off);
|
||||
if (via_outer) goto copy_out;
|
||||
return;
|
||||
}
|
||||
/* Struct payload: zero the whole slot, then write fields/words
|
||||
@@ -1002,7 +1036,7 @@ cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src,
|
||||
ins2(c, A_XORQ, areg(D_AX), areg(D_AX));
|
||||
for (int k = 0; k < sz; k += 8)
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, slot_off + k));
|
||||
amem(D_BP, write_off + k));
|
||||
int tag = cg_tag_for_variant(du, st);
|
||||
if (src->kind == N_IDENT) {
|
||||
int soff = localfind(*locals_p, src->str);
|
||||
@@ -1012,7 +1046,7 @@ cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src,
|
||||
ins2(c, A_MOVQ, amem(D_BP, soff + k),
|
||||
areg(D_AX));
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, slot_off + 8 + k));
|
||||
amem(D_BP, write_off + 8 + k));
|
||||
k += 8;
|
||||
}
|
||||
if (k < ssz) {
|
||||
@@ -1026,7 +1060,7 @@ cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src,
|
||||
ins2(c, lop,
|
||||
amem(D_BP, soff + k), areg(D_AX));
|
||||
ins2(c, lop, areg(D_AX),
|
||||
amem(D_BP, slot_off + 8 + k));
|
||||
amem(D_BP, write_off + 8 + k));
|
||||
}
|
||||
} else if (src->kind == N_STRUCTLIT) {
|
||||
for (Node *f = src->list; f; f = f->next) {
|
||||
@@ -1046,37 +1080,39 @@ cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src,
|
||||
if (fld_isfloat(ftype, &sl_isf32)) {
|
||||
int mov = sl_isf32 ? A_MOVSS : A_MOVSD;
|
||||
ins2(c, mov, areg(D_X0),
|
||||
amem(D_BP, slot_off + 8 + (int)foff));
|
||||
amem(D_BP, write_off + 8 + (int)foff));
|
||||
continue;
|
||||
}
|
||||
Type *fu = (ftype && ftype->kind == TY_NAMED)
|
||||
? ftype->under : ftype;
|
||||
if (fu && fu->kind == TY_STR) {
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, slot_off + 8 + (int)foff + 0));
|
||||
amem(D_BP, write_off + 8 + (int)foff + 0));
|
||||
ins2(c, A_MOVQ, areg(D_BX),
|
||||
amem(D_BP, slot_off + 8 + (int)foff + 8));
|
||||
amem(D_BP, write_off + 8 + (int)foff + 8));
|
||||
continue;
|
||||
}
|
||||
int op = A_MOVQ;
|
||||
if (fsz == 1) op = A_MOVB;
|
||||
else if (fsz == 4) op = A_MOVL;
|
||||
ins2(c, op, areg(D_AX),
|
||||
amem(D_BP, slot_off + 8 + (int)foff));
|
||||
amem(D_BP, write_off + 8 + (int)foff));
|
||||
}
|
||||
}
|
||||
ins2(c, A_MOVQ, aimm(tag < 0 ? 0 : tag),
|
||||
amem(D_BP, slot_off + 0));
|
||||
amem(D_BP, write_off + 0));
|
||||
if (via_outer) goto copy_out;
|
||||
return;
|
||||
}
|
||||
/* str payload: AX=ptr, BX=len from cgexpr. */
|
||||
if (type_isstr(st) || (su && su->kind == TY_STR)) {
|
||||
cgexpr(c, src, *locals_p);
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, slot_off + 8));
|
||||
ins2(c, A_MOVQ, areg(D_BX), amem(D_BP, slot_off + 16));
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, write_off + 8));
|
||||
ins2(c, A_MOVQ, areg(D_BX), amem(D_BP, write_off + 16));
|
||||
int tag = cg_tag_for_variant(du, st);
|
||||
ins2(c, A_MOVQ, aimm(tag < 0 ? 0 : tag),
|
||||
amem(D_BP, slot_off + 0));
|
||||
amem(D_BP, write_off + 0));
|
||||
if (via_outer) goto copy_out;
|
||||
return;
|
||||
}
|
||||
/* Slice payload: cgexpr leaves (AX=ptr, BX=len, CX=cap). The
|
||||
@@ -1084,12 +1120,13 @@ cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src,
|
||||
* destination tagged-union slot be at least 32B. */
|
||||
if (type_isslice(st) || (su && su->kind == TY_SLICE)) {
|
||||
cgexpr(c, src, *locals_p);
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, slot_off + 8));
|
||||
ins2(c, A_MOVQ, areg(D_BX), amem(D_BP, slot_off + 16));
|
||||
ins2(c, A_MOVQ, areg(D_CX), amem(D_BP, slot_off + 24));
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, write_off + 8));
|
||||
ins2(c, A_MOVQ, areg(D_BX), amem(D_BP, write_off + 16));
|
||||
ins2(c, A_MOVQ, areg(D_CX), amem(D_BP, write_off + 24));
|
||||
int tag = cg_tag_for_variant(du, st);
|
||||
ins2(c, A_MOVQ, aimm(tag < 0 ? 0 : tag),
|
||||
amem(D_BP, slot_off + 0));
|
||||
amem(D_BP, write_off + 0));
|
||||
if (via_outer) goto copy_out;
|
||||
return;
|
||||
}
|
||||
/* Scalar / pointer / etc. The high slot word (when sz > 16) is
|
||||
@@ -1099,9 +1136,21 @@ cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src,
|
||||
* cg_widen_tagged_push pre-zeroes the scratch slot before
|
||||
* calling us, so the call-site push still sees clean pad. */
|
||||
cgexpr(c, src, *locals_p);
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, slot_off + 8));
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, write_off + 8));
|
||||
int tag = cg_tag_for_variant(du, st);
|
||||
ins2(c, A_MOVQ, aimm(tag < 0 ? 0 : tag), amem(D_BP, slot_off + 0));
|
||||
ins2(c, A_MOVQ, aimm(tag < 0 ? 0 : tag), amem(D_BP, write_off + 0));
|
||||
copy_out:
|
||||
if (via_outer) {
|
||||
/* cgexpr above clobbered base_reg — reload from spill, then
|
||||
* word-copy scratch → caller's (base_reg, slot_off). */
|
||||
ins2(c, A_MOVQ, amem(D_BP, base_spill), areg(base_reg));
|
||||
for (int k = 0; k < sz; k += 8) {
|
||||
ins2(c, A_MOVQ, amem(D_BP, write_off + k),
|
||||
areg(D_AX));
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(base_reg, slot_off + k));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* cg_widen_tagged_push — call-site widening. For shapes where cgexpr
|
||||
@@ -1168,7 +1217,7 @@ cg_widen_tagged_push(Cg *c, Local **locals_p, Type *dst, Node *src, int sz)
|
||||
ins2(c, A_XORQ, areg(D_AX), areg(D_AX));
|
||||
for (int k = 0; k < sz; k += 8)
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, scr + k));
|
||||
cg_widen_tagged_store(c, locals_p, dst, src, scr, sz);
|
||||
cg_widen_tagged_store(c, locals_p, dst, src, D_BP, scr, sz);
|
||||
int nwords = sz / 8;
|
||||
for (int k = nwords - 1; k >= 0; k--) {
|
||||
ins2(c, A_MOVQ, amem(D_BP, scr + k * 8), areg(D_AX));
|
||||
@@ -1862,63 +1911,41 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
Type *ft = f->type;
|
||||
Type *fu = (ft && ft->kind == TY_NAMED)
|
||||
? ft->under : ft;
|
||||
/* Tagged-union field — full slot rewrite via the
|
||||
* shared widener so every rhs shape (whole-tagged
|
||||
* ident or expr with tag-remap, concrete-variant
|
||||
* widening of str/slice/struct/scalar/void) lands
|
||||
* the right tag + payload bytes. The pre-#26
|
||||
* branch synthesised a single tag from
|
||||
* cg_tag_for_variant and stored only AX at +8, so
|
||||
* whole-tagged rhs (vt == fu, no concrete tag)
|
||||
* silently wrote tag 0 and dropped trailing words.
|
||||
* cg_widen_tagged_store handles every shape by
|
||||
* branching on the source's resolved type. */
|
||||
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);
|
||||
int addr_in_bx = via_ptr;
|
||||
if (via_ptr)
|
||||
ins2(c, A_MOVQ, amem(D_BP, boff),
|
||||
int fsz = (int)fu->size;
|
||||
if (via_ptr) {
|
||||
ins2(c, A_MOVQ,
|
||||
amem(D_BP, boff),
|
||||
areg(D_BX));
|
||||
cgexpr(c, n->rhs, locals);
|
||||
if (is_global)
|
||||
cg_widen_tagged_store(c, &locals,
|
||||
fu, n->rhs, D_BX, foff, fsz);
|
||||
} else 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));
|
||||
ins2(c, A_MOVQ, areg(D_BX),
|
||||
amem(D_BP, boff + foff + 16));
|
||||
}
|
||||
areg(D_BX));
|
||||
cg_widen_tagged_store(c, &locals,
|
||||
fu, n->rhs, D_BX, foff, fsz);
|
||||
} else {
|
||||
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));
|
||||
cg_widen_tagged_store(c, &locals,
|
||||
fu, n->rhs, D_BP,
|
||||
boff + foff, fsz);
|
||||
}
|
||||
if (addr_in_bx)
|
||||
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),
|
||||
amem(D_BP, boff + foff + 0));
|
||||
break;
|
||||
}
|
||||
int fsz = (int)(f->type ? f->type->size : 8);
|
||||
@@ -2781,7 +2808,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, scr + k));
|
||||
cg_widen_tagged_store(c, &locals, esubu,
|
||||
n->rhs, scr, ssz);
|
||||
n->rhs, D_BP, scr, ssz);
|
||||
/* Compute &arr[i] → BX. */
|
||||
cgexpr(c, n->lhs->rhs, locals);
|
||||
if (ssz > 1) {
|
||||
@@ -2878,7 +2905,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
int off = localfind(locals, n->lhs->str);
|
||||
if (off == 0) break;
|
||||
cg_widen_tagged_store(c, &locals, lu, n->rhs,
|
||||
off, (int)lu->size);
|
||||
D_BP, off, (int)lu->size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -3458,7 +3485,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
if (v_is_tagged) {
|
||||
cg_widen_tagged_store(c,
|
||||
&locals, velem,
|
||||
a, slot, esz);
|
||||
a, D_BP, slot, esz);
|
||||
continue;
|
||||
}
|
||||
cgexpr(c, a, locals);
|
||||
@@ -5158,7 +5185,7 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
* literal — field-by-field at slot+8+field_off), str payload,
|
||||
* and scalar payload (with zero-pad to the slot size). */
|
||||
if (n->rhs && lu && lu->kind == TY_TAGGED) {
|
||||
cg_widen_tagged_store(c, locals, lu, n->rhs, off, sz);
|
||||
cg_widen_tagged_store(c, locals, lu, n->rhs, D_BP, off, sz);
|
||||
break;
|
||||
}
|
||||
/* slice expression initialiser: build a {ptr, len, cap} header
|
||||
@@ -5272,7 +5299,7 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
? ft->under : ft;
|
||||
if (fu && fu->kind == TY_TAGGED) {
|
||||
cg_widen_tagged_store(c, locals, fu,
|
||||
f->lhs, off + (int)foff,
|
||||
f->lhs, D_BP, off + (int)foff,
|
||||
(int)fu->size);
|
||||
continue;
|
||||
}
|
||||
@@ -5470,7 +5497,7 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, scr + k));
|
||||
cg_widen_tagged_store(c, locals, rt,
|
||||
n->lhs, scr, sz);
|
||||
n->lhs, D_BP, scr, sz);
|
||||
ins2(c, A_MOVQ, amem(D_BP, scr + 0),
|
||||
areg(D_AX));
|
||||
if (sz > 8)
|
||||
|
||||
Reference in New Issue
Block a user