w6c+selfhost: TK_AMP through chained N_DOT (closes #9)
Three shapes handled: value-struct chains (&o.i.a), pointer-field (&p.f), slice/str pseudo-fields (&s.len). cstage inlines #6's spine walker; wwstage reuses dotchainresolve unchanged. Silent-drop fallback preserved. Slice-header width mismatch in *&s.len writes filed as task #13.
This commit is contained in:
128
cmd/w6c/cgen.c
128
cmd/w6c/cgen.c
@@ -1287,6 +1287,134 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (opnd && opnd->kind == N_DOT) {
|
||||
/* Address-of through a DOT chain. The early-exit
|
||||
* above handled `&ident` and `&base[i]`; everything
|
||||
* else was silently dropped. Three shapes converge
|
||||
* here, all returning an 8B address (so no
|
||||
* fldloadop dispatch — just LEAQ).
|
||||
*
|
||||
* 1. Value-struct fields, any depth (`&o.f`,
|
||||
* `&o.i.a`, `&o.a.b.c`): walk the spine to a
|
||||
* root ident, sum field offsets, emit LEAQ at
|
||||
* base + sum. Mirror of the read at line 3722.
|
||||
* 2. Slice/str pseudo-field tail (`&s.len`,
|
||||
* `&b.buf.len`): folds into the spine walk
|
||||
* with slice_delta 0/8/16.
|
||||
* 3. Pointer-field (`&p.f` where p:*T): the spine
|
||||
* walk aborts at the *T base; the fallback
|
||||
* below loads p into AX and adds field_off.
|
||||
*/
|
||||
int amped = 0;
|
||||
/* Spine walk — same shape as the read at 3722.
|
||||
* Records (parent_struct, field_name) leaf-first,
|
||||
* then iterates root-first to sum offsets. */
|
||||
struct { Type *pu; const char *name; } steps[16];
|
||||
int nsteps = 0;
|
||||
Node *cur = opnd;
|
||||
int abort = 0;
|
||||
while (cur && cur->kind == N_DOT && cur->lhs) {
|
||||
Type *pt = cur->lhs->type;
|
||||
Type *pu = (pt && pt->kind == TY_NAMED)
|
||||
? pt->under : pt;
|
||||
if (!pu) { abort = 1; break; }
|
||||
if (cur == opnd && (pu->kind == TY_SLICE
|
||||
|| pu->kind == TY_STR)) {
|
||||
/* leaf pseudo on slice/str header */
|
||||
} else if (pu->kind != TY_STRUCT) {
|
||||
abort = 1;
|
||||
break;
|
||||
}
|
||||
if (nsteps >= 16) { abort = 1; break; }
|
||||
steps[nsteps].pu = pu;
|
||||
steps[nsteps].name = cur->str;
|
||||
nsteps++;
|
||||
cur = cur->lhs;
|
||||
}
|
||||
if (!abort && cur && cur->kind == N_IDENT
|
||||
&& nsteps > 0) {
|
||||
int total_off = 0;
|
||||
int slice_delta = -1;
|
||||
int ok = 1;
|
||||
for (int i = nsteps - 1; i >= 0; i--) {
|
||||
Type *pu = steps[i].pu;
|
||||
if (pu->kind == TY_SLICE
|
||||
|| pu->kind == TY_STR) {
|
||||
if (strcmp(steps[i].name, "ptr") == 0)
|
||||
slice_delta = 0;
|
||||
else if (strcmp(steps[i].name, "len") == 0)
|
||||
slice_delta = 8;
|
||||
else if (strcmp(steps[i].name, "cap") == 0)
|
||||
slice_delta = 16;
|
||||
else { ok = 0; break; }
|
||||
} else {
|
||||
Tfield *f = NULL;
|
||||
for (Tfield *fl = pu->fields; fl; fl = fl->next)
|
||||
if (strcmp(fl->name, steps[i].name) == 0)
|
||||
{ f = fl; break; }
|
||||
if (!f) { ok = 0; break; }
|
||||
total_off += (int)f->offset;
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
int extra = (slice_delta >= 0)
|
||||
? slice_delta : 0;
|
||||
int root_off = localfind(locals, cur->str);
|
||||
if (root_off != 0) {
|
||||
ins2(c, A_LEAQ,
|
||||
amem(D_BP,
|
||||
root_off + total_off + extra),
|
||||
areg(D_AX));
|
||||
amped = 1;
|
||||
} else if (let_islet(cur->str)) {
|
||||
/* Two-step global form mirrors the
|
||||
* read path's `LEAQ name,CX → MOVQ
|
||||
* disp(CX),AX`, swapping the MOVQ
|
||||
* for LEAQ. */
|
||||
ins2(c, A_LEAQ,
|
||||
masym(c, cur->str), areg(D_CX));
|
||||
ins2(c, A_LEAQ,
|
||||
amem(D_CX, total_off + extra),
|
||||
areg(D_AX));
|
||||
amped = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Pointer-field fallback for `&p.f` where p:*T —
|
||||
* the spine walker aborts on the *T base. Load p
|
||||
* into AX, then LEAQ field_off(AX),AX. Mirror of
|
||||
* the read at line 4033. */
|
||||
if (!amped && opnd->lhs
|
||||
&& opnd->lhs->kind == N_IDENT) {
|
||||
Type *bt = opnd->lhs->type;
|
||||
Type *bu = (bt && bt->kind == TY_NAMED)
|
||||
? bt->under : bt;
|
||||
if (bu && bu->kind == TY_PTR && bu->sub) {
|
||||
Type *inner = bu->sub;
|
||||
if (inner->kind == TY_NAMED)
|
||||
inner = inner->under;
|
||||
if (inner && inner->kind == TY_STRUCT) {
|
||||
for (Tfield *f = inner->fields;
|
||||
f; f = f->next) {
|
||||
if (strcmp(f->name, opnd->str) != 0)
|
||||
continue;
|
||||
int off = localfind(locals,
|
||||
opnd->lhs->str);
|
||||
ins2(c, A_MOVQ,
|
||||
amem(D_BP, off),
|
||||
areg(D_AX));
|
||||
ins2(c, A_LEAQ,
|
||||
amem(D_AX, (int)f->offset),
|
||||
areg(D_AX));
|
||||
amped = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (amped) break;
|
||||
/* Fall through to silent-drop fallback below. */
|
||||
}
|
||||
if (opnd && opnd->kind == N_INDEX) {
|
||||
/* &base[i] = base + i*esz, no dereference. */
|
||||
Node *base = opnd->lhs;
|
||||
|
||||
Reference in New Issue
Block a user