w6c: keep unwrap destination off the call stack

This commit is contained in:
2026-08-09 03:44:12 +09:00
parent a030268c52
commit edacb40bfe
3 changed files with 113 additions and 28 deletions

View File

@@ -39,6 +39,11 @@ static int *cg_frame;
* semantics for synthetic scratches). 0 means "not yet allocated";
* negative offsets returned by local_alloc are the live value. */
static int cg_retscr;
/* The aggregate-unwrap receiver must survive its producer call without
* perturbing SP: an outstanding PUSHQ at CALL violates SysV alignment.
* One BP-relative address spill per function mirrors wwstage's @-prefix
* scratch reuse. */
static int cg_unwrapdst;
/* Per-fn @tupfscr offset (single-slot SSoT). A multi-float tuple return
* (#164/#107) spills each float out of X0 to this scratch as the L→R
* element walk clobbers X0, then reloads X0/X1 by SSE index after the
@@ -5362,9 +5367,17 @@ cgexpr(Cg *c, Node *n, Local *locals)
fatal("#16: global/chained aggregate "
"unwrap field dest unresolved "
"(cgplaceaddr)");
ins1(c, A_PUSHQ, areg(D_BX));
int dstscr;
if (cg_unwrapdst != 0) {
dstscr = cg_unwrapdst;
} else {
dstscr = local_alloc(c, &locals, "@unwrapdst",
8, cg_frame);
cg_unwrapdst = dstscr;
}
ins2(c, A_MOVQ, areg(D_BX), amem(D_BP, dstscr));
cgexpr(c, n->rhs, locals);
ins1(c, A_POPQ, areg(D_BX));
ins2(c, A_MOVQ, amem(D_BP, dstscr), areg(D_BX));
cg_agg_reg_store(c, &locals, D_BX, 0, ssz, 0);
break;
}
@@ -5377,9 +5390,8 @@ cgexpr(Cg *c, Node *n, Local *locals)
* explicit-deref form `(*p).f = ...` where the parser emits
* N_UN(STAR, IDENT(p)). For (*p).f, retarget base to the
* inner IDENT so the via_ptr branch fires identically to
* `p.f = v`. v1 scope: bare-IDENT inner only; (*expr).f
* (non-IDENT inner) falls through to the existing drop
* behaviour pending follow-up task. */
* `p.f = v`. Other addressable bases fall through to the
* shared place resolver. */
if (!global_ptr_field_decline(n->lhs, n->op, locals)
&& n->lhs && n->lhs->kind == N_DOT && n->lhs->lhs &&
(n->lhs->lhs->kind == N_IDENT ||
@@ -5452,20 +5464,8 @@ cgexpr(Cg *c, Node *n, Local *locals)
if (strcmp(fl->name, n->lhs->str) == 0)
{ f = fl; break; }
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.
* 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. */
/* Compound ops on tagged fields are not meaningful,
* so only plain `=` is wired. */
Type *ft = f->type;
/* Transitive chase (#5-F1 fold): 2-level alias
* slice/str field skipped the 3-word arm — ptr
@@ -11876,10 +11876,10 @@ cgexpr(Cg *c, Node *n, Local *locals)
* IDENT(p)) with type T (post-deref struct). Pull the inner
* IDENT in as dot_lhs so bt resolves to *T and the pointer-
* auto-deref branch below fires (mirror of the N_ASSIGN
* N_DOT lhs retarget). v1 scope: N_IDENT inner only;
* (*expr).f follow-up task pending. Branches that gate on
* N_DOT lhs retarget). Branches that gate on
* `n->lhs->kind == N_DOT/N_INDEX/...` keep checking the raw
* n->lhs since (*p) isn't either of those shapes. */
* n->lhs since (*p) isn't either of those shapes; remaining
* addressable bases use the shared place resolver. */
Node *dot_lhs = n->lhs;
if (dot_lhs && dot_lhs->kind == N_UN && dot_lhs->op == TK_STAR
&& dot_lhs->lhs && dot_lhs->lhs->kind == N_IDENT)
@@ -15803,6 +15803,7 @@ cgfn(Cg *c, FILE *out, Node *fn)
nloops = 0;
cg_ret_type = fn->type ? fn->type->ret : NULL;
cg_retscr = 0;
cg_unwrapdst = 0;
cg_tupfscr = 0;
cg_tupargscr = 0;
cg_tupargscr_sz = 0;