w6c+selfhost: cgen chained N_DOT/N_ASSIGN spine walk
Loop-shaped spine walker for value-struct chains (o.i.a) and slice/str pseudo-fields (s.buf.len), read+write, both stages. SB-fallback at the catch-all preserved for unresolved module-qualified idents. Follow-ups filed: tasks #7-#10 (wwstage >6-arg frame over-alloc, chained array-elem field BX loss, & through chained DOT, signed sub-word field loads zero-extend).
This commit is contained in:
279
cmd/w6c/cgen.c
279
cmd/w6c/cgen.c
@@ -1943,6 +1943,153 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Chained `<chain>.field = v` where <chain> spans value-struct
|
||||
* dots ending at a root ident — `o.i.a = 10`, `v.a.b.c = …`.
|
||||
* Also handles a slice/str pseudo-field leaf (`b.buf.len = 5`):
|
||||
* spine walks down to the slice/str header, then the +0/+8/+16
|
||||
* delta selects ptr/len/cap. Sibling of the chained-pointer-
|
||||
* field branch above; without this the LHS is silently dropped
|
||||
* (the existing 1-deep branch only fires for `ident.field = …`).
|
||||
* Only plain `=` is wired — compound on a chained value-struct
|
||||
* field is rare and stays unhandled. */
|
||||
if (n->lhs && n->lhs->kind == N_DOT && n->lhs->lhs
|
||||
&& n->lhs->lhs->kind == N_DOT && n->op == TK_ASSIGN) {
|
||||
struct { Type *pu; const char *name; } steps[16];
|
||||
int nsteps = 0;
|
||||
Node *cur = n->lhs;
|
||||
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 == n->lhs && (pu->kind == TY_SLICE
|
||||
|| pu->kind == TY_STR)) {
|
||||
/* leaf pseudo-field 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;
|
||||
Type *leaf_type = NULL;
|
||||
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;
|
||||
leaf_type = f->type;
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
int root_off = localfind(locals, cur->str);
|
||||
int base_reg = D_BP;
|
||||
int base_disp = root_off;
|
||||
int is_global = 0;
|
||||
int root_resolved = (root_off != 0);
|
||||
if (!root_resolved && let_islet(cur->str)) {
|
||||
root_resolved = 1;
|
||||
is_global = 1;
|
||||
}
|
||||
if (root_resolved) {
|
||||
if (slice_delta >= 0) {
|
||||
/* slice/str pseudo-field store. .ptr writes
|
||||
* 8 bytes; .len / .cap write 8 bytes each
|
||||
* (matches the existing N_IDENT pseudo-
|
||||
* field branch). */
|
||||
cgexpr(c, n->rhs, locals);
|
||||
if (is_global) {
|
||||
ins2(c, A_LEAQ,
|
||||
masym(c, cur->str),
|
||||
areg(D_CX));
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_CX, total_off + slice_delta));
|
||||
} else {
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, base_disp + total_off + slice_delta));
|
||||
}
|
||||
break;
|
||||
}
|
||||
Type *fu = (leaf_type
|
||||
&& leaf_type->kind == TY_NAMED)
|
||||
? leaf_type->under : leaf_type;
|
||||
int fsz = (int)(leaf_type
|
||||
? leaf_type->size : 8);
|
||||
int store_op = A_MOVQ;
|
||||
if (fsz == 1) store_op = A_MOVB;
|
||||
else if (fsz == 4) store_op = A_MOVL;
|
||||
if (fu && fu->kind == TY_STR) {
|
||||
cgexpr(c, n->rhs, locals);
|
||||
if (is_global) {
|
||||
ins2(c, A_LEAQ,
|
||||
masym(c, cur->str),
|
||||
areg(D_CX));
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_CX, total_off + 0));
|
||||
ins2(c, A_MOVQ, areg(D_BX),
|
||||
amem(D_CX, total_off + 8));
|
||||
} else {
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, base_disp + total_off + 0));
|
||||
ins2(c, A_MOVQ, areg(D_BX),
|
||||
amem(D_BP, base_disp + total_off + 8));
|
||||
}
|
||||
break;
|
||||
}
|
||||
int sf32 = 0;
|
||||
if (fld_isfloat(leaf_type, &sf32)) {
|
||||
int mov = sf32 ? A_MOVSS : A_MOVSD;
|
||||
cgexpr(c, n->rhs, locals);
|
||||
if (is_global) {
|
||||
ins2(c, A_LEAQ,
|
||||
masym(c, cur->str),
|
||||
areg(D_CX));
|
||||
ins2(c, mov, areg(D_X0),
|
||||
amem(D_CX, total_off));
|
||||
} else {
|
||||
ins2(c, mov, areg(D_X0),
|
||||
amem(D_BP, base_disp + total_off));
|
||||
}
|
||||
break;
|
||||
}
|
||||
cgexpr(c, n->rhs, locals);
|
||||
if (is_global) {
|
||||
ins2(c, A_LEAQ,
|
||||
masym(c, cur->str),
|
||||
areg(D_CX));
|
||||
ins2(c, store_op, areg(D_AX),
|
||||
amem(D_CX, total_off));
|
||||
} else {
|
||||
ins2(c, store_op, areg(D_AX),
|
||||
amem(D_BP, base_disp + total_off));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* float assignment to a local or top-level global. Globals
|
||||
* route through LEAQ+indirect (no D_EXTERN SSE in w6a).
|
||||
* Compound (`acc += d` etc.) loads slot into X1, combines
|
||||
@@ -3561,6 +3708,138 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
ins2(c, A_MOVQ, masym(c, n->str), areg(D_AX));
|
||||
goto dot_done;
|
||||
}
|
||||
/* Chained N_DOT spine through value-struct fields. Handles any
|
||||
* depth `root.f0.f1.…leaf` where every intermediate field is a
|
||||
* value struct, plus the slice/str pseudo-field tail (`s.buf.len`)
|
||||
* where the innermost field is a slice/str header. Walks inward
|
||||
* collecting (parent_struct, field_name); reverses to sum field
|
||||
* offsets; emits one load at (base + total_off). Placed BEFORE
|
||||
* the slice/str pseudo-field branch so its else-arm (cgexpr lhs
|
||||
* + shuffle BX→AX) doesn't mis-handle `b.buf.len` — cgexpr on a
|
||||
* value-struct→slice chain only loads .ptr into AX, leaving BX
|
||||
* stale. Sibling of the pointer-chain branch further down. */
|
||||
if (n->lhs && n->lhs->kind == N_DOT) {
|
||||
Type *lt0 = n->lhs->type;
|
||||
Type *lu0 = (lt0 && lt0->kind == TY_NAMED) ? lt0->under : lt0;
|
||||
int leaf_is_pseudo = lu0 && n->str
|
||||
&& (lu0->kind == TY_SLICE || lu0->kind == TY_STR)
|
||||
&& (strcmp(n->str, "ptr") == 0
|
||||
|| strcmp(n->str, "len") == 0
|
||||
|| strcmp(n->str, "cap") == 0);
|
||||
int leaf_in_struct = lu0 && lu0->kind == TY_STRUCT;
|
||||
if (leaf_is_pseudo || leaf_in_struct) {
|
||||
struct { Type *pu; const char *name; } steps[16];
|
||||
int nsteps = 0;
|
||||
Node *cur = n;
|
||||
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 == n && (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;
|
||||
Type *leaf_type = NULL;
|
||||
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;
|
||||
leaf_type = f->type;
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
int root_off = localfind(locals, cur->str);
|
||||
int base_reg = D_BP;
|
||||
int base_disp = root_off;
|
||||
int root_resolved = (root_off != 0);
|
||||
if (!root_resolved && let_islet(cur->str)) {
|
||||
ins2(c, A_LEAQ,
|
||||
masym(c, cur->str), areg(D_CX));
|
||||
base_reg = D_CX;
|
||||
base_disp = 0;
|
||||
root_resolved = 1;
|
||||
}
|
||||
if (root_resolved) {
|
||||
if (slice_delta >= 0) {
|
||||
ins2(c, A_MOVQ,
|
||||
amem(base_reg,
|
||||
base_disp + total_off + slice_delta),
|
||||
areg(D_AX));
|
||||
goto dot_done;
|
||||
}
|
||||
Type *fu = (leaf_type
|
||||
&& leaf_type->kind == TY_NAMED)
|
||||
? leaf_type->under : leaf_type;
|
||||
if (fu && fu->kind == TY_STR) {
|
||||
ins2(c, A_MOVQ,
|
||||
amem(base_reg,
|
||||
base_disp + total_off + 0),
|
||||
areg(D_AX));
|
||||
ins2(c, A_MOVQ,
|
||||
amem(base_reg,
|
||||
base_disp + total_off + 8),
|
||||
areg(D_BX));
|
||||
goto dot_done;
|
||||
}
|
||||
int g_isf32 = 0;
|
||||
if (fld_isfloat(leaf_type, &g_isf32)) {
|
||||
int mov = g_isf32 ? A_MOVSS : A_MOVSD;
|
||||
ins2(c, mov,
|
||||
amem(base_reg,
|
||||
base_disp + total_off),
|
||||
areg(D_X0));
|
||||
goto dot_done;
|
||||
}
|
||||
int fsz = (int)(leaf_type
|
||||
? leaf_type->size : 8);
|
||||
int signed_field = leaf_type && (
|
||||
leaf_type->kind == TY_I8
|
||||
|| leaf_type->kind == TY_I16
|
||||
|| leaf_type->kind == TY_I32);
|
||||
int op = A_MOVQ;
|
||||
if (fsz == 1) op = A_MOVZBQ;
|
||||
else if (fsz == 4)
|
||||
op = signed_field ? A_MOVSXD : A_MOVL;
|
||||
ins2(c, op,
|
||||
amem(base_reg,
|
||||
base_disp + total_off),
|
||||
areg(D_AX));
|
||||
goto dot_done;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int lenfld = (n->str && strcmp(n->str, "len") == 0);
|
||||
int capfld = (n->str && strcmp(n->str, "cap") == 0);
|
||||
int ptrfld = (n->str && strcmp(n->str, "ptr") == 0);
|
||||
|
||||
Reference in New Issue
Block a user