cgen: materialize and store all eightbytes of an aggregate unwrap success (#12)
A struct/array success variant in an (S|e)! / r? unwrap dropped eightbytes on BOTH stages (byte-id blind). Two layers: (L1) the unwrap success shuffle (cgtrytaggedshift) matched no arm for a struct/array success and fell to a bare MOVQ DX,AX, materializing only w0 — widen the existing nested-TAGGED shift's gate to admit TY_STRUCT/TY_ARRAY (the in-cap union packs the payload as raw GP words past the tag, so that shift is exact); (L2) the aggregate store arms gated on rhs.kind==N_CALL and stored one word for an unwrap rhs — relax to also admit N_TRYUNW/N_TRYPROP at the three silent store shapes (arr[i]=, single-dot field, indexed-field), reusing the materialise scratch path (now #10-correct). Rule-7 LOUD-STOP for a float-bearing success variant (an SSE eightbyte cannot ride the GP {AX,DX,CX} shift, #165). The four already-loud unwrap consumers (let-receive #7, call-arg #271, assign-existing #49, resolver-field #24) stay loud; global/chained single-dot field (#16) and the sub-8-tail-through-unwrap union-maker frame clobber (#15) are separate follow-ups. Value-asserting pin, reddens under each stage's independent revert.
This commit is contained in:
105
cmd/w6c/cgen.c
105
cmd/w6c/cgen.c
@@ -557,6 +557,36 @@ struct_arg_size(Type *t)
|
||||
return (int)t->size;
|
||||
}
|
||||
|
||||
/* agg_has_float — does an aggregate Type carry ANY float leaf
|
||||
* (recursively through struct fields / array element / tuple
|
||||
* positionals)? The #12 producer (N_TRYPROP/N_TRYUNW success shuffle)
|
||||
* loud-stops a float-bearing aggregate success variant: the union
|
||||
* return places a float eightbyte in the SSE class (X0/X1) which the GP
|
||||
* AX/DX/CX payload shuffle cannot reach (mirror #11/#165). Conservative
|
||||
* — ANY float, not a per-eightbyte SSE classify like struct_float_class
|
||||
* — a loud-stop only needs to refuse, not transport. Mirrors wwstage
|
||||
* tinfo_agg_float (cgenutil.ww). */
|
||||
static int
|
||||
agg_has_float(Type *t)
|
||||
{
|
||||
t = type_chase_named(t);
|
||||
if (t == NULL) return 0;
|
||||
if (cg_isfloat(t)) return 1;
|
||||
if (t->kind == TY_STRUCT) {
|
||||
for (Tfield *f = t->fields; f; f = f->next)
|
||||
if (agg_has_float(f->type)) return 1;
|
||||
return 0;
|
||||
}
|
||||
if (t->kind == TY_ARRAY)
|
||||
return agg_has_float(t->sub);
|
||||
if (t->kind == TY_TUPLE) {
|
||||
for (Tparam *p = t->params; p; p = p->next)
|
||||
if (agg_has_float(p->type)) return 1;
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* struct_float_class — SysV per-eightbyte classification for the #165
|
||||
* float-bearing-struct param case (the param twin of #171's struct
|
||||
* return, classifying per-eightbyte rather than #163's per-element).
|
||||
@@ -5385,10 +5415,21 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* store; for via_ptr/is_global the dst base addr
|
||||
* is reloaded into BX before each store so cgexpr
|
||||
* can clobber AX/BX between fields. */
|
||||
/* #12: an unwrap `b.f = mk()!` (direct) / `p.f =
|
||||
* mk()!` (via-ptr) rides the same {AX,DX,CX} shape
|
||||
* (producer shift) — admit it alongside N_CALL. Gated
|
||||
* !is_global: the wwstage single-dot arms cover a direct
|
||||
* / via-ptr base only; a global `g.f` unwrap stays on
|
||||
* its pre-existing path in BOTH stages (out of scope,
|
||||
* not regressed). Float/over-cap loud-stop at the
|
||||
* producer. */
|
||||
if (n->op == TK_ASSIGN && str_fu
|
||||
&& str_fu->kind == TY_STRUCT
|
||||
&& (int)str_fu->size <= 24
|
||||
&& n->rhs && n->rhs->kind == N_CALL
|
||||
&& n->rhs && (n->rhs->kind == N_CALL
|
||||
|| ((n->rhs->kind == N_TRYUNW
|
||||
|| n->rhs->kind == N_TRYPROP)
|
||||
&& !is_global))
|
||||
&& (str_fu->size % 8 == 0
|
||||
|| str_fu->size % 8 == 1
|
||||
|| str_fu->size % 8 == 2
|
||||
@@ -5828,8 +5869,17 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* float return eightbyte rides X0/X1 which the GP
|
||||
* AX/DX/CX cursor cannot read). Mirrors wwstage
|
||||
* cgenexpr.ww. */
|
||||
/* #12: an unwrap `arr[i].f = mk()!` rides the
|
||||
* same {AX,DX,CX} shape (producer shift) — admit
|
||||
* it alongside N_CALL; the #11b non-call arm below
|
||||
* excludes the unwrap kinds so this arm is the
|
||||
* sole handler. cg_sret_retsize keys off ft (the
|
||||
* field = success variant type), so an over-cap
|
||||
* unwrap stays loud here too. */
|
||||
if (n->op == TK_ASSIGN && n->rhs
|
||||
&& n->rhs->kind == N_CALL) {
|
||||
&& (n->rhs->kind == N_CALL
|
||||
|| n->rhs->kind == N_TRYUNW
|
||||
|| n->rhs->kind == N_TRYPROP)) {
|
||||
if (cg_sret_retsize(ft) > 0)
|
||||
fatal("#11c/#234: over-cap "
|
||||
"(sret) aggregate field "
|
||||
@@ -5991,8 +6041,14 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* cg_aggcopy — the ONE copy emitter the non-
|
||||
* indexed bases use (DRY). fsz natural
|
||||
* (ft->size). Mirrors wwstage cgenexpr.ww. */
|
||||
/* #12: exclude the unwrap kinds — the #11 arm
|
||||
* above is their sole handler (they ride the
|
||||
* {AX,DX,CX} register cursor, NOT a source
|
||||
* address). */
|
||||
if (n->op == TK_ASSIGN && n->rhs
|
||||
&& n->rhs->kind != N_CALL && fu
|
||||
&& n->rhs->kind != N_CALL
|
||||
&& n->rhs->kind != N_TRYUNW
|
||||
&& n->rhs->kind != N_TRYPROP && fu
|
||||
&& (fu->kind == TY_STRUCT || fu->kind == TY_ARRAY
|
||||
|| fu->kind == TY_TUPLE) && fsz > 8) {
|
||||
cgexpr(c, idx, locals);
|
||||
@@ -6986,12 +7042,20 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* copy scratch → dest. Scratch-first (not a dest spill across
|
||||
* the call) keeps the call at the frame's natural alignment.
|
||||
* In-cap only (cg_sret_retsize==0). */
|
||||
/* #12: an unwrap `mk()!` / `r?` whose success variant is
|
||||
* an in-cap struct/array rides the SAME {AX,DX,CX} payload
|
||||
* shape as the call return (the producer shift materialises
|
||||
* it); admit it alongside N_CALL. cg_sret_retsize(esub)==0
|
||||
* holds for an in-cap variant; the float/over-cap loud-
|
||||
* stops live at the producer. */
|
||||
if ((is_arr || is_sl || is_ptr) && n->op == TK_ASSIGN
|
||||
&& esubu && (esubu->kind == TY_STRUCT
|
||||
|| esubu->kind == TY_ARRAY
|
||||
|| esubu->kind == TY_TUPLE)
|
||||
&& esz > 8
|
||||
&& n->rhs->kind == N_CALL
|
||||
&& (n->rhs->kind == N_CALL
|
||||
|| n->rhs->kind == N_TRYUNW
|
||||
|| n->rhs->kind == N_TRYPROP)
|
||||
&& cg_sret_retsize(esub) == 0) {
|
||||
int scr = cg_tagscr_slot(c, &locals, esz);
|
||||
cgexpr(c, n->rhs, locals); /* call → AX/DX/CX */
|
||||
@@ -11113,8 +11177,24 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* standard AX=tag cursor. The scalar MOVQ DX,AX
|
||||
* below carried only the inner tag and dropped the
|
||||
* payload (ken unw16). Nullable folds to one word
|
||||
* and stays on the scalar move. */
|
||||
if (stu && stu->kind == TY_TAGGED && !stu->nullable) {
|
||||
* and stays on the scalar move.
|
||||
*
|
||||
* #12: a general-aggregate (struct/array) success
|
||||
* variant rides the SAME in-cap {AX,DX,CX} payload
|
||||
* shuffle — the union return packs the payload as raw
|
||||
* GP words past the outer tag. Pre-#12 it matched no
|
||||
* arm and fell to the bare MOVQ DX,AX below,
|
||||
* materialising only w0 (w1/w2 dropped) — a SILENT
|
||||
* both-stage word-drop. A float-bearing aggregate rides
|
||||
* X0/X1 (the SSE return-class) which this GP cursor
|
||||
* cannot reach, so LOUD-STOP it (mirror #11/#165). */
|
||||
if (stu && ((stu->kind == TY_TAGGED && !stu->nullable)
|
||||
|| stu->kind == TY_STRUCT || stu->kind == TY_ARRAY)) {
|
||||
if (agg_has_float(stu))
|
||||
fatal("#12/#165: float-bearing "
|
||||
"aggregate success variant unwrap "
|
||||
"(S|e)! rides SSE X0/X1; GP cursor "
|
||||
"unwired");
|
||||
ins2(c, A_MOVQ, areg(D_DX), areg(D_AX));
|
||||
if (stu->size > 8)
|
||||
ins2(c, A_MOVQ, areg(D_CX),
|
||||
@@ -11225,8 +11305,17 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
}
|
||||
/* Family C (#35): TAGGED success = nested box on the
|
||||
* payload words — shift past the outer tag (see the
|
||||
* N_TRYPROP twin). */
|
||||
if (stu && stu->kind == TY_TAGGED && !stu->nullable) {
|
||||
* N_TRYPROP twin). #12: a struct/array success variant
|
||||
* rides the same in-cap {AX,DX,CX} shuffle; a float-
|
||||
* bearing aggregate LOUD-STOPS (X0/X1, GP cursor can't
|
||||
* reach it). Full WHY at the N_TRYPROP twin. */
|
||||
if (stu && ((stu->kind == TY_TAGGED && !stu->nullable)
|
||||
|| stu->kind == TY_STRUCT || stu->kind == TY_ARRAY)) {
|
||||
if (agg_has_float(stu))
|
||||
fatal("#12/#165: float-bearing "
|
||||
"aggregate success variant unwrap "
|
||||
"(S|e)! rides SSE X0/X1; GP cursor "
|
||||
"unwired");
|
||||
ins2(c, A_MOVQ, areg(D_DX), areg(D_AX));
|
||||
if (stu->size > 8)
|
||||
ins2(c, A_MOVQ, areg(D_CX),
|
||||
|
||||
Reference in New Issue
Block a user