wcc+w6c_ww: typed dot-read resolver — kill both silent N_DOT fallbacks (F4)
A typed depth-2+ field chain behind an index/deref spine (threads[0].cap.end, (*p)[i].f.g) aborted the cgdot chain walker (N_IDENT roots only) and fell into the module-qualified-leaf fallback — a silent `MOVQ <leaf>(SB)` global read of a colliding symbol (p6min10 exit 66) or a link error. Single-dot reads behind a deref- index ((*ts)[i].pc, (*p)[i].slicefield) fell into the bottom catch-all instead, which is offset- and header-blind: offset-0 scalars read correctly by COINCIDENCE, nonzero offsets and slice headers were silently wrong in BOTH stages (FA3, pA5). `&threads[0].cap` dropped the address-of silently and SEGFAULTed at the deref (task #6, reviewer-A route). cgplaceaddr (C1) grows an N_IDENT root (local / let / DATA-backed def) and the N_INDEX base gate relaxes to recursion, so indexed-ident and deref-rooted spines resolve; enumerated arms still dispatch first, keeping every pre-C1 shape's asm. case N_DOT routes any TYPED read no arm matched through the resolver (scalar fldloadop, float X0, str/ slice 3-word header, [N]T address); the module-leaf fallback is gated to UNTYPED chains, the catch-all to untyped-str pseudo-fields, and the TK_AMP tail is resolver-or-loud. Leaf kinds without a register convention (tagged, aggregate) and unaddressable shapes die LOUD (rule 7). wwstage mirrors symmetrically; two of its arm gaps must not take the resolver (its sequence differs from cstage's arms — cs!=ww): ident-indexed alias reads loud-cite C3 (task #8) and non-local-rooted ptr-chains loud-cite task #37. A third verdict divergence is comment- documented at the wwstage aggregate gate: cstage's let-init consumes `let c = (*ts)[i].cap` BEFORE its N_DOT tail (emitting NO copy — the F5 bug), so that shape cs-builds/ww-louds until the F5 let-copy lands (task #7); absent from the gate corpus. 806 identroot_dot graduates from BUILD_FAIL: the C2 ident root makes append(h.xs, v) through *holder resolve via C1.5's place consumer (run-verified, byte-id). p6min9/p6min10 read-halves are fixed but the probes stay blocked on the #36 literal under-copy this commit unmasked (struct-ident field rhs copies 8B; repro filed with the task). test/805: +7 rows (typed depth-2 behind ident-index incl the 777 global-collision pin, deref-index, width/float/[N]u8 matrix, FA3 slice-field + .cap-behind-spine, &-route with compound-through- pointer, C1's reject_tail graduated to stores, neutrality pins) and +4 reject rows pinning the new loud texts; the C1.25 raw-byte readbacks graduate to typed depth-2 reads.
This commit is contained in:
126
cmd/w6c/cgen.c
126
cmd/w6c/cgen.c
@@ -2019,12 +2019,13 @@ aggarg_srcaddr(Cg *c, Node *src, int dst, Local *locals)
|
||||
/* cgplaceaddr — compute the ADDRESS of an arbitrary place (lvalue)
|
||||
* expression into dst_reg; returns 1 when the shape is wired, 0
|
||||
* otherwise (the caller loud-stops — rule 7, never a silent drop).
|
||||
* F6 resolver, commit C1: only the deref-rooted spine is wired —
|
||||
* `(*p)[i].f` as N_UN(STAR) root, N_INDEX hop over a slice/array
|
||||
* place, N_DOT struct-field hop with one deref for a *struct base.
|
||||
* Ident-rooted spines stay with the enumerated N_ASSIGN arms so this
|
||||
* resolver never perturbs their asm; the F4 read-walker and F5
|
||||
* let-copy accrete here in follow-up commits. ADDRESS COMPUTATION
|
||||
* F6 resolver, commit C1: `(*p)[i].f` as N_UN(STAR) root, N_INDEX hop
|
||||
* over a slice/array place, N_DOT struct-field hop with one deref for
|
||||
* a *struct base. C2 (F4 read-walker) adds the N_IDENT root (local /
|
||||
* let / DATA-backed def) so indexed-ident spines (`threads[0].cap.end`)
|
||||
* resolve too. Enumerated arms still win at every dispatch site (they
|
||||
* are checked first), so shapes that worked pre-C1 keep their asm; the
|
||||
* F5 let-copy accretes here in a follow-up commit. ADDRESS COMPUTATION
|
||||
* ONLY — every call-site keeps its own load/store/copy emission.
|
||||
* Clobbers AX/CX (cgexpr on index / pointer operands) and balances
|
||||
* its own PUSHQ/POPQ; dst_reg must not be AX or CX. */
|
||||
@@ -2032,6 +2033,19 @@ static int
|
||||
cgplaceaddr(Cg *c, Node *n, int dst_reg, Local *locals)
|
||||
{
|
||||
if (n == NULL) return 0;
|
||||
if (n->kind == N_IDENT) {
|
||||
int off = localfind(locals, n->str);
|
||||
if (off != 0) {
|
||||
ins2(c, A_LEAQ, amem(D_BP, off), areg(dst_reg));
|
||||
return 1;
|
||||
}
|
||||
if (let_islet(n->str) || def_isstructdef(n->str)
|
||||
|| def_isarraydef(n->str)) {
|
||||
ins2(c, A_LEAQ, masym(c, n->str), areg(dst_reg));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (n->kind == N_UN && n->op == TK_STAR) {
|
||||
/* &(*e) is e's value — no load. */
|
||||
cgexpr(c, n->lhs, locals);
|
||||
@@ -2042,11 +2056,10 @@ cgplaceaddr(Cg *c, Node *n, int dst_reg, Local *locals)
|
||||
Node *base = n->lhs;
|
||||
Node *idx = n->rhs;
|
||||
if (base == NULL || idx == NULL) return 0;
|
||||
/* Deref base only: ident/dot index bases all have
|
||||
* enumerated arms; routing them here would change
|
||||
* their asm. */
|
||||
if (!(base->kind == N_UN && base->op == TK_STAR))
|
||||
return 0;
|
||||
/* C2: any addressable base — recursion decides (deref /
|
||||
* ident / dot / index spine). Ident-rooted shapes with
|
||||
* enumerated arms never reach the resolver (those arms
|
||||
* dispatch first), so their asm is untouched. */
|
||||
Type *bu = type_chase_named(base->type);
|
||||
if (bu == NULL) return 0;
|
||||
if (bu->kind != TY_SLICE && bu->kind != TY_ARRAY)
|
||||
@@ -3809,9 +3822,18 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
ins2(c, A_ADDQ, areg(D_BX), areg(D_AX));
|
||||
break;
|
||||
}
|
||||
/* Other shapes (& on a complex expr): silent drop,
|
||||
* mirrors the pre-existing fallback. */
|
||||
break;
|
||||
/* C2 (F4 family, reviewer-A route): address-of through
|
||||
* an indexed/deref dot spine (`&threads[0].cap`,
|
||||
* `&(*p)[i].f`) — the spine walker above roots only at
|
||||
* idents. Route the place address through cgplaceaddr
|
||||
* (read-twin in case N_DOT). Any remaining shape dies
|
||||
* LOUD: the pre-C2 silent drop left stale AX as the
|
||||
* "address" — a gate-blind SEGFAULT at the deref. */
|
||||
if (cgplaceaddr(c, opnd, D_BX, locals)) {
|
||||
ins2(c, A_MOVQ, areg(D_BX), areg(D_AX));
|
||||
break;
|
||||
}
|
||||
fatal("unsupported address-of shape");
|
||||
}
|
||||
cgexpr(c, n->lhs, locals);
|
||||
switch (n->op) {
|
||||
@@ -9514,8 +9536,13 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* linker surfaces a clean undefined-symbol error on the leaf
|
||||
* — mirrors the bare-N_IDENT unresolved fallback used by
|
||||
* single-segment N_DOTs. Keeps cstage / wwstage byte-aligned
|
||||
* on the cgen-match isolation probes. */
|
||||
if (n->lhs && n->lhs->kind == N_DOT && n->str) {
|
||||
* on the cgen-match isolation probes. C2 (F4): gated to
|
||||
* UNTYPED chains only — pre-C2 it swallowed every unmatched
|
||||
* dot-over-dot chain, turning a TYPED depth-2 read behind an
|
||||
* index/deref spine (`threads[0].cap.end`) into a silent
|
||||
* global read of a colliding leaf symbol (p6min10 exit 66). */
|
||||
if (n->lhs && n->lhs->kind == N_DOT && n->str
|
||||
&& (n->lhs->type == NULL || n->lhs->type == ty_err)) {
|
||||
ins2(c, A_MOVQ, masym(c, n->str), areg(D_AX));
|
||||
break;
|
||||
}
|
||||
@@ -9524,10 +9551,69 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* TY_STR, so it misses the typed slice/str gate above and
|
||||
* lands here. cgexpr leaves (AX=ptr, BX=len); `.ptr` keeps AX,
|
||||
* `.len` shuffles BX→AX. Mirrors wwstage cgdot's catch-all
|
||||
* (selfhost/cmd/wcc/cgenexpr.ww). #14. */
|
||||
cgexpr(c, n->lhs, locals);
|
||||
if (lenfld)
|
||||
ins2(c, A_MOVQ, areg(D_BX), areg(D_AX));
|
||||
* (selfhost/cmd/wcc/cgenexpr.ww). #14. C2 (F4/FA3): gated to
|
||||
* TY_UNTYPED_STR — pre-C2 this was the offset- and header-
|
||||
* blind catch-all every unmatched typed dot fell into, so a
|
||||
* nonzero-offset field behind a deref-index spine read the
|
||||
* element's word 0 (`(*p)[i].slicefield` → 1-word wrong-
|
||||
* offset read; offset-0 scalars worked by COINCIDENCE). */
|
||||
{
|
||||
Type *cu = type_chase_named(
|
||||
n->lhs ? n->lhs->type : NULL);
|
||||
if (cu && cu->kind == TY_UNTYPED_STR) {
|
||||
cgexpr(c, n->lhs, locals);
|
||||
if (lenfld)
|
||||
ins2(c, A_MOVQ, areg(D_BX),
|
||||
areg(D_AX));
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* C2 read-resolver (F4 + FA3-cstage): a TYPED N_DOT read no
|
||||
* enumerated arm matched — depth-2+ chains and slice/str/
|
||||
* scalar fields behind index/deref spines. Address via
|
||||
* cgplaceaddr (the C1 resolver), leaf load emitted here by
|
||||
* kind. Leaf kinds with no canonical register convention in
|
||||
* expr position stay LOUD; any shape the resolver can't
|
||||
* address dies LOUD (rule 7) — the pre-C2 tails guessed. */
|
||||
{
|
||||
Type *rt = n->type;
|
||||
Type *ru = type_chase_named(rt);
|
||||
if (ru && ru->kind == TY_TAGGED)
|
||||
fatal("read-resolver: tagged field read not "
|
||||
"wired (rule-7)");
|
||||
if (ru && (ru->kind == TY_STRUCT
|
||||
|| ru->kind == TY_TUPLE))
|
||||
fatal("read-resolver: aggregate field read "
|
||||
"not wired (rule-7)");
|
||||
if (!cgplaceaddr(c, n, D_BX, locals))
|
||||
fatal("unsupported field-read shape");
|
||||
int rd_isf32 = 0;
|
||||
if (fld_isfloat(rt, &rd_isf32)) {
|
||||
ins2(c, rd_isf32 ? A_MOVSS : A_MOVSD,
|
||||
amem(D_BX, 0), areg(D_X0));
|
||||
goto dot_done;
|
||||
}
|
||||
if (ru && ru->kind == TY_ARRAY) {
|
||||
/* `[N]T` leaf: leave the field ADDRESS — a
|
||||
* base for an outer index, never a value
|
||||
* (#270-1a semantics). */
|
||||
ins2(c, A_MOVQ, areg(D_BX), areg(D_AX));
|
||||
goto dot_done;
|
||||
}
|
||||
if (ru && (ru->kind == TY_STR
|
||||
|| ru->kind == TY_SLICE)) {
|
||||
/* str IS []u8 — 3-word {ptr,len,cap} into
|
||||
* (AX, BX, CX). BX is the place base, so
|
||||
* load .len (which targets BX) LAST. */
|
||||
ins2(c, A_MOVQ, amem(D_BX, 0), areg(D_AX));
|
||||
ins2(c, A_MOVQ, amem(D_BX, 16), areg(D_CX));
|
||||
ins2(c, A_MOVQ, amem(D_BX, 8), areg(D_BX));
|
||||
goto dot_done;
|
||||
}
|
||||
int rdsz = (int)(rt ? rt->size : 8);
|
||||
ins2(c, fldloadop(rt, rdsz), amem(D_BX, 0),
|
||||
areg(D_AX));
|
||||
}
|
||||
dot_done:
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user