w6c+selfhost: cgen *T-rooted chained N_DOT (closes #22)

Spine walker now accepts *T root at the last hop (cur->lhs->kind ==
N_IDENT, pu->kind == TY_PTR), substitutes pointee struct, emits
MOVQ off(BP), CX before offset arithmetic. Symmetric in N_DOT and
N_ASSIGN. Distinct gate from existing mid-chain *T-field branch
(cgen.c:4615) — no shadow.

Unblocks task #18 (bufio writer first-field embed). Pre-existing
*T-field mid-chain path unchanged.
This commit is contained in:
2026-05-14 02:44:57 +09:00
parent d2f4659305
commit d66aef382a
6 changed files with 659 additions and 127 deletions

View File

@@ -2400,6 +2400,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
&& n->lhs->lhs->kind == N_DOT && n->op == TK_ASSIGN) {
struct { Type *pu; const char *name; } steps[16];
int nsteps = 0;
int ptr_root = 0;
Node *cur = n->lhs;
int abort = 0;
while (cur && cur->kind == N_DOT && cur->lhs) {
@@ -2410,7 +2411,23 @@ cgexpr(Cg *c, Node *n, Local *locals)
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) {
} else if (pu->kind == TY_STRUCT) {
/* value-struct hop */
} else if (pu->kind == TY_PTR && pu->sub
&& cur->lhs->kind == N_IDENT) {
/* `*T` root: dereference at emit time;
* walk through pointee struct fields.
* Last-hop only (root is a bare ident). */
Type *sub = (pu->sub->kind == TY_NAMED)
? pu->sub->under : pu->sub;
if (sub && sub->kind == TY_STRUCT) {
pu = sub;
ptr_root = 1;
} else {
abort = 1;
break;
}
} else {
abort = 1;
break;
}
@@ -2449,7 +2466,6 @@ cgexpr(Cg *c, Node *n, Local *locals)
}
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);
@@ -2458,16 +2474,26 @@ cgexpr(Cg *c, Node *n, Local *locals)
is_global = 1;
}
if (root_resolved) {
/* `*T` root and global both store via CX as
* the base register; only the loader differs
* (LEAQ name(SB) vs MOVQ off(BP)). Compute it
* AFTER cgexpr(rhs) so AX/BX/X0 stay intact. */
int via_cx = is_global || ptr_root;
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));
if (via_cx) {
if (ptr_root)
ins2(c, A_MOVQ,
amem(D_BP, base_disp),
areg(D_CX));
else
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 {
@@ -2484,10 +2510,15 @@ cgexpr(Cg *c, Node *n, Local *locals)
int store_op = fldstoreop(leaf_type, fsz);
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));
if (via_cx) {
if (ptr_root)
ins2(c, A_MOVQ,
amem(D_BP, base_disp),
areg(D_CX));
else
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),
@@ -2504,10 +2535,15 @@ cgexpr(Cg *c, Node *n, Local *locals)
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));
if (via_cx) {
if (ptr_root)
ins2(c, A_MOVQ,
amem(D_BP, base_disp),
areg(D_CX));
else
ins2(c, A_LEAQ,
masym(c, cur->str),
areg(D_CX));
ins2(c, mov, areg(D_X0),
amem(D_CX, total_off));
} else {
@@ -2517,10 +2553,15 @@ cgexpr(Cg *c, Node *n, Local *locals)
break;
}
cgexpr(c, n->rhs, locals);
if (is_global) {
ins2(c, A_LEAQ,
masym(c, cur->str),
areg(D_CX));
if (via_cx) {
if (ptr_root)
ins2(c, A_MOVQ,
amem(D_BP, base_disp),
areg(D_CX));
else
ins2(c, A_LEAQ,
masym(c, cur->str),
areg(D_CX));
ins2(c, store_op, areg(D_AX),
amem(D_CX, total_off));
} else {
@@ -4193,6 +4234,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
if (leaf_is_pseudo || leaf_in_struct) {
struct { Type *pu; const char *name; } steps[16];
int nsteps = 0;
int ptr_root = 0;
Node *cur = n;
int abort = 0;
while (cur && cur->kind == N_DOT && cur->lhs) {
@@ -4203,7 +4245,26 @@ cgexpr(Cg *c, Node *n, Local *locals)
if (cur == n && (pu->kind == TY_SLICE
|| pu->kind == TY_STR)) {
/* leaf pseudo on slice/str header */
} else if (pu->kind != TY_STRUCT) {
} else if (pu->kind == TY_STRUCT) {
/* value-struct hop */
} else if (pu->kind == TY_PTR && pu->sub
&& cur->lhs->kind == N_IDENT) {
/* `*T` root: dereference once at emit
* time, then walk offsets through the
* pointee. Only at the last hop (root
* is a bare ident) — `*T`-field mid-
* chain keeps its cgexpr-based pointer-
* field branch further down. */
Type *sub = (pu->sub->kind == TY_NAMED)
? pu->sub->under : pu->sub;
if (sub && sub->kind == TY_STRUCT) {
pu = sub;
ptr_root = 1;
} else {
abort = 1;
break;
}
} else {
abort = 1;
break;
}
@@ -4252,6 +4313,21 @@ cgexpr(Cg *c, Node *n, Local *locals)
base_disp = 0;
root_resolved = 1;
}
if (root_resolved && ptr_root) {
/* `*T` root: load the pointer value
* once; field accesses then index at
* total_off off the pointer. */
if (base_reg == D_BP) {
ins2(c, A_MOVQ,
amem(D_BP, base_disp),
areg(D_CX));
} else {
ins2(c, A_MOVQ,
amem(D_CX, 0), areg(D_CX));
}
base_reg = D_CX;
base_disp = 0;
}
if (root_resolved) {
if (slice_delta >= 0) {
ins2(c, A_MOVQ,