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:
114
cmd/w6c/cgen.c
114
cmd/w6c/cgen.c
@@ -2400,6 +2400,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
&& n->lhs->lhs->kind == N_DOT && n->op == TK_ASSIGN) {
|
&& n->lhs->lhs->kind == N_DOT && n->op == TK_ASSIGN) {
|
||||||
struct { Type *pu; const char *name; } steps[16];
|
struct { Type *pu; const char *name; } steps[16];
|
||||||
int nsteps = 0;
|
int nsteps = 0;
|
||||||
|
int ptr_root = 0;
|
||||||
Node *cur = n->lhs;
|
Node *cur = n->lhs;
|
||||||
int abort = 0;
|
int abort = 0;
|
||||||
while (cur && cur->kind == N_DOT && cur->lhs) {
|
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
|
if (cur == n->lhs && (pu->kind == TY_SLICE
|
||||||
|| pu->kind == TY_STR)) {
|
|| pu->kind == TY_STR)) {
|
||||||
/* leaf pseudo-field on slice/str header */
|
/* 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;
|
abort = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2449,7 +2466,6 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
}
|
}
|
||||||
if (ok) {
|
if (ok) {
|
||||||
int root_off = localfind(locals, cur->str);
|
int root_off = localfind(locals, cur->str);
|
||||||
int base_reg = D_BP;
|
|
||||||
int base_disp = root_off;
|
int base_disp = root_off;
|
||||||
int is_global = 0;
|
int is_global = 0;
|
||||||
int root_resolved = (root_off != 0);
|
int root_resolved = (root_off != 0);
|
||||||
@@ -2458,16 +2474,26 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
is_global = 1;
|
is_global = 1;
|
||||||
}
|
}
|
||||||
if (root_resolved) {
|
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) {
|
if (slice_delta >= 0) {
|
||||||
/* slice/str pseudo-field store. .ptr writes
|
/* slice/str pseudo-field store. .ptr writes
|
||||||
* 8 bytes; .len / .cap write 8 bytes each
|
* 8 bytes; .len / .cap write 8 bytes each
|
||||||
* (matches the existing N_IDENT pseudo-
|
* (matches the existing N_IDENT pseudo-
|
||||||
* field branch). */
|
* field branch). */
|
||||||
cgexpr(c, n->rhs, locals);
|
cgexpr(c, n->rhs, locals);
|
||||||
if (is_global) {
|
if (via_cx) {
|
||||||
ins2(c, A_LEAQ,
|
if (ptr_root)
|
||||||
masym(c, cur->str),
|
ins2(c, A_MOVQ,
|
||||||
areg(D_CX));
|
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),
|
ins2(c, A_MOVQ, areg(D_AX),
|
||||||
amem(D_CX, total_off + slice_delta));
|
amem(D_CX, total_off + slice_delta));
|
||||||
} else {
|
} else {
|
||||||
@@ -2484,10 +2510,15 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
int store_op = fldstoreop(leaf_type, fsz);
|
int store_op = fldstoreop(leaf_type, fsz);
|
||||||
if (fu && fu->kind == TY_STR) {
|
if (fu && fu->kind == TY_STR) {
|
||||||
cgexpr(c, n->rhs, locals);
|
cgexpr(c, n->rhs, locals);
|
||||||
if (is_global) {
|
if (via_cx) {
|
||||||
ins2(c, A_LEAQ,
|
if (ptr_root)
|
||||||
masym(c, cur->str),
|
ins2(c, A_MOVQ,
|
||||||
areg(D_CX));
|
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),
|
ins2(c, A_MOVQ, areg(D_AX),
|
||||||
amem(D_CX, total_off + 0));
|
amem(D_CX, total_off + 0));
|
||||||
ins2(c, A_MOVQ, areg(D_BX),
|
ins2(c, A_MOVQ, areg(D_BX),
|
||||||
@@ -2504,10 +2535,15 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
if (fld_isfloat(leaf_type, &sf32)) {
|
if (fld_isfloat(leaf_type, &sf32)) {
|
||||||
int mov = sf32 ? A_MOVSS : A_MOVSD;
|
int mov = sf32 ? A_MOVSS : A_MOVSD;
|
||||||
cgexpr(c, n->rhs, locals);
|
cgexpr(c, n->rhs, locals);
|
||||||
if (is_global) {
|
if (via_cx) {
|
||||||
ins2(c, A_LEAQ,
|
if (ptr_root)
|
||||||
masym(c, cur->str),
|
ins2(c, A_MOVQ,
|
||||||
areg(D_CX));
|
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),
|
ins2(c, mov, areg(D_X0),
|
||||||
amem(D_CX, total_off));
|
amem(D_CX, total_off));
|
||||||
} else {
|
} else {
|
||||||
@@ -2517,10 +2553,15 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
cgexpr(c, n->rhs, locals);
|
cgexpr(c, n->rhs, locals);
|
||||||
if (is_global) {
|
if (via_cx) {
|
||||||
ins2(c, A_LEAQ,
|
if (ptr_root)
|
||||||
masym(c, cur->str),
|
ins2(c, A_MOVQ,
|
||||||
areg(D_CX));
|
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),
|
ins2(c, store_op, areg(D_AX),
|
||||||
amem(D_CX, total_off));
|
amem(D_CX, total_off));
|
||||||
} else {
|
} else {
|
||||||
@@ -4193,6 +4234,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
if (leaf_is_pseudo || leaf_in_struct) {
|
if (leaf_is_pseudo || leaf_in_struct) {
|
||||||
struct { Type *pu; const char *name; } steps[16];
|
struct { Type *pu; const char *name; } steps[16];
|
||||||
int nsteps = 0;
|
int nsteps = 0;
|
||||||
|
int ptr_root = 0;
|
||||||
Node *cur = n;
|
Node *cur = n;
|
||||||
int abort = 0;
|
int abort = 0;
|
||||||
while (cur && cur->kind == N_DOT && cur->lhs) {
|
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
|
if (cur == n && (pu->kind == TY_SLICE
|
||||||
|| pu->kind == TY_STR)) {
|
|| pu->kind == TY_STR)) {
|
||||||
/* leaf pseudo on slice/str header */
|
/* 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;
|
abort = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -4252,6 +4313,21 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
base_disp = 0;
|
base_disp = 0;
|
||||||
root_resolved = 1;
|
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 (root_resolved) {
|
||||||
if (slice_delta >= 0) {
|
if (slice_delta >= 0) {
|
||||||
ins2(c, A_MOVQ,
|
ins2(c, A_MOVQ,
|
||||||
|
|||||||
@@ -7934,10 +7934,11 @@ fn cgwidentaggedstore(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz:
|
|||||||
export fn dotchainresolve(c: *cgen, n: *node,
|
export fn dotchainresolve(c: *cgen, n: *node,
|
||||||
outrootname: *str, outrootoff: *i32, outtotaloff: *i32,
|
outrootname: *str, outrootoff: *i32, outtotaloff: *i32,
|
||||||
outleaffi: **fieldinfo, outslicedelta: *i32,
|
outleaffi: **fieldinfo, outslicedelta: *i32,
|
||||||
outisglobal: *bool) bool = {
|
outisglobal: *bool, outptrroot: *bool) bool = {
|
||||||
*outrootname = "";
|
*outrootname = "";
|
||||||
*outrootoff = 0;
|
*outrootoff = 0;
|
||||||
*outisglobal = false;
|
*outisglobal = false;
|
||||||
|
*outptrroot = false;
|
||||||
*outtotaloff = 0;
|
*outtotaloff = 0;
|
||||||
*outleaffi = nil;
|
*outleaffi = nil;
|
||||||
*outslicedelta = -1;
|
*outslicedelta = -1;
|
||||||
@@ -7965,6 +7966,20 @@ export fn dotchainresolve(c: *cgen, n: *node,
|
|||||||
rootstruct = lc.tnode.str;
|
rootstruct = lc.tnode.str;
|
||||||
*outrootoff = lc.off;
|
*outrootoff = lc.off;
|
||||||
};
|
};
|
||||||
|
// `*T` root (param/local): dereference at emit time;
|
||||||
|
// pointee struct supplies the field layout. Callers
|
||||||
|
// that opt in via *outptrroot emit a MOVQ load of the
|
||||||
|
// slot before indexing.
|
||||||
|
if (lc.tnode.kind == nkind.N_TPTR) {
|
||||||
|
let pe: *node = lc.tnode.lhs;
|
||||||
|
if (pe != nil) {
|
||||||
|
if (pe.kind == nkind.N_TNAME) {
|
||||||
|
rootstruct = pe.str;
|
||||||
|
*outrootoff = lc.off;
|
||||||
|
*outptrroot = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
if (rootstruct.len == 0) {
|
if (rootstruct.len == 0) {
|
||||||
@@ -9648,15 +9663,27 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
let leaffi: *fieldinfo = nil;
|
let leaffi: *fieldinfo = nil;
|
||||||
let slicedelta: i32 = -1;
|
let slicedelta: i32 = -1;
|
||||||
let isglobal: bool = false;
|
let isglobal: bool = false;
|
||||||
|
let ptrroot: bool = false;
|
||||||
let pok: bool = dotchainresolve(c, n,
|
let pok: bool = dotchainresolve(c, n,
|
||||||
&rootname, &rootoff, &totaloff,
|
&rootname, &rootoff, &totaloff,
|
||||||
&leaffi, &slicedelta, &isglobal);
|
&leaffi, &slicedelta, &isglobal, &ptrroot);
|
||||||
if (pok) {
|
if (pok) {
|
||||||
|
// `*T` root: load the pointer slot once into CX,
|
||||||
|
// then index every leaf at total_off off CX. Same
|
||||||
|
// emit shape as the global path (LEAQ → CX) — only
|
||||||
|
// the loader instruction differs.
|
||||||
|
let viacx: bool = isglobal || ptrroot;
|
||||||
if (slicedelta >= 0) {
|
if (slicedelta >= 0) {
|
||||||
if (isglobal) {
|
if (viacx) {
|
||||||
emitline("\tLEAQ\t");
|
if (ptrroot) {
|
||||||
emitsymname(c, rootname);
|
emitline("\tMOVQ\t");
|
||||||
emitline("(SB), CX\n");
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg((totaloff + slicedelta): i64, "CX");
|
emitdispreg((totaloff + slicedelta): i64, "CX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
@@ -9668,10 +9695,16 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (isstrtype(c, leaffi.tnode)) {
|
if (isstrtype(c, leaffi.tnode)) {
|
||||||
if (isglobal) {
|
if (viacx) {
|
||||||
emitline("\tLEAQ\t");
|
if (ptrroot) {
|
||||||
emitsymname(c, rootname);
|
emitline("\tMOVQ\t");
|
||||||
emitline("(SB), CX\n");
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg(totaloff: i64, "CX");
|
emitdispreg(totaloff: i64, "CX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
@@ -9691,10 +9724,16 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
if (isfloattype(c, leaffi.tnode)) {
|
if (isfloattype(c, leaffi.tnode)) {
|
||||||
let mov: str = "MOVSD";
|
let mov: str = "MOVSD";
|
||||||
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
||||||
if (isglobal) {
|
if (viacx) {
|
||||||
emitline("\tLEAQ\t");
|
if (ptrroot) {
|
||||||
emitsymname(c, rootname);
|
emitline("\tMOVQ\t");
|
||||||
emitline("(SB), CX\n");
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(mov);
|
emitline(mov);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -9710,10 +9749,16 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let lop: str = fieldloadop(c, leaffi);
|
let lop: str = fieldloadop(c, leaffi);
|
||||||
if (isglobal) {
|
if (viacx) {
|
||||||
emitline("\tLEAQ\t");
|
if (ptrroot) {
|
||||||
emitsymname(c, rootname);
|
emitline("\tMOVQ\t");
|
||||||
emitline("(SB), CX\n");
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(lop);
|
emitline(lop);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -10005,9 +10050,16 @@ fn cgun(c: *cgen, n: *node) void = {
|
|||||||
let leaffi: *fieldinfo = nil;
|
let leaffi: *fieldinfo = nil;
|
||||||
let slicedelta: i32 = -1;
|
let slicedelta: i32 = -1;
|
||||||
let isglobal: bool = false;
|
let isglobal: bool = false;
|
||||||
|
let ptrroot: bool = false;
|
||||||
let pok: bool = dotchainresolve(c, opnd,
|
let pok: bool = dotchainresolve(c, opnd,
|
||||||
&rootname, &rootoff, &totaloff,
|
&rootname, &rootoff, &totaloff,
|
||||||
&leaffi, &slicedelta, &isglobal);
|
&leaffi, &slicedelta, &isglobal,
|
||||||
|
&ptrroot);
|
||||||
|
// `&` through a `*T`-rooted chain is a
|
||||||
|
// separate shape (would need MOVQ + LEAQ
|
||||||
|
// disp(CX), AX). Not exercised by current
|
||||||
|
// callers — skip and fall through.
|
||||||
|
if (ptrroot) { pok = false; };
|
||||||
if (pok) {
|
if (pok) {
|
||||||
let extra: i32 = 0;
|
let extra: i32 = 0;
|
||||||
if (slicedelta >= 0) { extra = slicedelta; };
|
if (slicedelta >= 0) { extra = slicedelta; };
|
||||||
@@ -11957,16 +12009,27 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
let leaffi: *fieldinfo = nil;
|
let leaffi: *fieldinfo = nil;
|
||||||
let slicedelta: i32 = -1;
|
let slicedelta: i32 = -1;
|
||||||
let isglobal: bool = false;
|
let isglobal: bool = false;
|
||||||
|
let ptrroot: bool = false;
|
||||||
let yok: bool = dotchainresolve(c, lhs,
|
let yok: bool = dotchainresolve(c, lhs,
|
||||||
&rootname, &rootoff, &totaloff,
|
&rootname, &rootoff, &totaloff,
|
||||||
&leaffi, &slicedelta, &isglobal);
|
&leaffi, &slicedelta, &isglobal, &ptrroot);
|
||||||
if (yok) {
|
if (yok) {
|
||||||
|
// `*T` root and global share the CX-based emit:
|
||||||
|
// loader runs AFTER cgexpr(rhs) so AX/BX/X0 stay
|
||||||
|
// intact, then stores at total_off off CX.
|
||||||
|
let viacx: bool = isglobal || ptrroot;
|
||||||
if (slicedelta >= 0) {
|
if (slicedelta >= 0) {
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (isglobal) {
|
if (viacx) {
|
||||||
emitline("\tLEAQ\t");
|
if (ptrroot) {
|
||||||
emitsymname(c, rootname);
|
emitline("\tMOVQ\t");
|
||||||
emitline("(SB), CX\n");
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
emitline("\tMOVQ\tAX, ");
|
emitline("\tMOVQ\tAX, ");
|
||||||
emitdispreg((totaloff + slicedelta): i64, "CX");
|
emitdispreg((totaloff + slicedelta): i64, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
@@ -11979,10 +12042,16 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
if (isstrtype(c, leaffi.tnode)) {
|
if (isstrtype(c, leaffi.tnode)) {
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (isglobal) {
|
if (viacx) {
|
||||||
emitline("\tLEAQ\t");
|
if (ptrroot) {
|
||||||
emitsymname(c, rootname);
|
emitline("\tMOVQ\t");
|
||||||
emitline("(SB), CX\n");
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
emitline("\tMOVQ\tAX, ");
|
emitline("\tMOVQ\tAX, ");
|
||||||
emitdispreg(totaloff: i64, "CX");
|
emitdispreg(totaloff: i64, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
@@ -12003,10 +12072,16 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
let mov: str = "MOVSD";
|
let mov: str = "MOVSD";
|
||||||
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (isglobal) {
|
if (viacx) {
|
||||||
emitline("\tLEAQ\t");
|
if (ptrroot) {
|
||||||
emitsymname(c, rootname);
|
emitline("\tMOVQ\t");
|
||||||
emitline("(SB), CX\n");
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(mov);
|
emitline(mov);
|
||||||
emitline("\tX0, ");
|
emitline("\tX0, ");
|
||||||
@@ -12023,10 +12098,16 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
let sop: str = fieldstoreop(c, leaffi);
|
let sop: str = fieldstoreop(c, leaffi);
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (isglobal) {
|
if (viacx) {
|
||||||
emitline("\tLEAQ\t");
|
if (ptrroot) {
|
||||||
emitsymname(c, rootname);
|
emitline("\tMOVQ\t");
|
||||||
emitline("(SB), CX\n");
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
|
|||||||
@@ -1615,15 +1615,27 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
let leaffi: *fieldinfo = nil;
|
let leaffi: *fieldinfo = nil;
|
||||||
let slicedelta: i32 = -1;
|
let slicedelta: i32 = -1;
|
||||||
let isglobal: bool = false;
|
let isglobal: bool = false;
|
||||||
|
let ptrroot: bool = false;
|
||||||
let pok: bool = dotchainresolve(c, n,
|
let pok: bool = dotchainresolve(c, n,
|
||||||
&rootname, &rootoff, &totaloff,
|
&rootname, &rootoff, &totaloff,
|
||||||
&leaffi, &slicedelta, &isglobal);
|
&leaffi, &slicedelta, &isglobal, &ptrroot);
|
||||||
if (pok) {
|
if (pok) {
|
||||||
|
// `*T` root: load the pointer slot once into CX,
|
||||||
|
// then index every leaf at total_off off CX. Same
|
||||||
|
// emit shape as the global path (LEAQ → CX) — only
|
||||||
|
// the loader instruction differs.
|
||||||
|
let viacx: bool = isglobal || ptrroot;
|
||||||
if (slicedelta >= 0) {
|
if (slicedelta >= 0) {
|
||||||
if (isglobal) {
|
if (viacx) {
|
||||||
emitline("\tLEAQ\t");
|
if (ptrroot) {
|
||||||
emitsymname(c, rootname);
|
emitline("\tMOVQ\t");
|
||||||
emitline("(SB), CX\n");
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg((totaloff + slicedelta): i64, "CX");
|
emitdispreg((totaloff + slicedelta): i64, "CX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
@@ -1635,10 +1647,16 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (isstrtype(c, leaffi.tnode)) {
|
if (isstrtype(c, leaffi.tnode)) {
|
||||||
if (isglobal) {
|
if (viacx) {
|
||||||
emitline("\tLEAQ\t");
|
if (ptrroot) {
|
||||||
emitsymname(c, rootname);
|
emitline("\tMOVQ\t");
|
||||||
emitline("(SB), CX\n");
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg(totaloff: i64, "CX");
|
emitdispreg(totaloff: i64, "CX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
@@ -1658,10 +1676,16 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
if (isfloattype(c, leaffi.tnode)) {
|
if (isfloattype(c, leaffi.tnode)) {
|
||||||
let mov: str = "MOVSD";
|
let mov: str = "MOVSD";
|
||||||
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
||||||
if (isglobal) {
|
if (viacx) {
|
||||||
emitline("\tLEAQ\t");
|
if (ptrroot) {
|
||||||
emitsymname(c, rootname);
|
emitline("\tMOVQ\t");
|
||||||
emitline("(SB), CX\n");
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(mov);
|
emitline(mov);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -1677,10 +1701,16 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let lop: str = fieldloadop(c, leaffi);
|
let lop: str = fieldloadop(c, leaffi);
|
||||||
if (isglobal) {
|
if (viacx) {
|
||||||
emitline("\tLEAQ\t");
|
if (ptrroot) {
|
||||||
emitsymname(c, rootname);
|
emitline("\tMOVQ\t");
|
||||||
emitline("(SB), CX\n");
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(lop);
|
emitline(lop);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -1972,9 +2002,16 @@ fn cgun(c: *cgen, n: *node) void = {
|
|||||||
let leaffi: *fieldinfo = nil;
|
let leaffi: *fieldinfo = nil;
|
||||||
let slicedelta: i32 = -1;
|
let slicedelta: i32 = -1;
|
||||||
let isglobal: bool = false;
|
let isglobal: bool = false;
|
||||||
|
let ptrroot: bool = false;
|
||||||
let pok: bool = dotchainresolve(c, opnd,
|
let pok: bool = dotchainresolve(c, opnd,
|
||||||
&rootname, &rootoff, &totaloff,
|
&rootname, &rootoff, &totaloff,
|
||||||
&leaffi, &slicedelta, &isglobal);
|
&leaffi, &slicedelta, &isglobal,
|
||||||
|
&ptrroot);
|
||||||
|
// `&` through a `*T`-rooted chain is a
|
||||||
|
// separate shape (would need MOVQ + LEAQ
|
||||||
|
// disp(CX), AX). Not exercised by current
|
||||||
|
// callers — skip and fall through.
|
||||||
|
if (ptrroot) { pok = false; };
|
||||||
if (pok) {
|
if (pok) {
|
||||||
let extra: i32 = 0;
|
let extra: i32 = 0;
|
||||||
if (slicedelta >= 0) { extra = slicedelta; };
|
if (slicedelta >= 0) { extra = slicedelta; };
|
||||||
@@ -3924,16 +3961,27 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
let leaffi: *fieldinfo = nil;
|
let leaffi: *fieldinfo = nil;
|
||||||
let slicedelta: i32 = -1;
|
let slicedelta: i32 = -1;
|
||||||
let isglobal: bool = false;
|
let isglobal: bool = false;
|
||||||
|
let ptrroot: bool = false;
|
||||||
let yok: bool = dotchainresolve(c, lhs,
|
let yok: bool = dotchainresolve(c, lhs,
|
||||||
&rootname, &rootoff, &totaloff,
|
&rootname, &rootoff, &totaloff,
|
||||||
&leaffi, &slicedelta, &isglobal);
|
&leaffi, &slicedelta, &isglobal, &ptrroot);
|
||||||
if (yok) {
|
if (yok) {
|
||||||
|
// `*T` root and global share the CX-based emit:
|
||||||
|
// loader runs AFTER cgexpr(rhs) so AX/BX/X0 stay
|
||||||
|
// intact, then stores at total_off off CX.
|
||||||
|
let viacx: bool = isglobal || ptrroot;
|
||||||
if (slicedelta >= 0) {
|
if (slicedelta >= 0) {
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (isglobal) {
|
if (viacx) {
|
||||||
emitline("\tLEAQ\t");
|
if (ptrroot) {
|
||||||
emitsymname(c, rootname);
|
emitline("\tMOVQ\t");
|
||||||
emitline("(SB), CX\n");
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
emitline("\tMOVQ\tAX, ");
|
emitline("\tMOVQ\tAX, ");
|
||||||
emitdispreg((totaloff + slicedelta): i64, "CX");
|
emitdispreg((totaloff + slicedelta): i64, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
@@ -3946,10 +3994,16 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
if (isstrtype(c, leaffi.tnode)) {
|
if (isstrtype(c, leaffi.tnode)) {
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (isglobal) {
|
if (viacx) {
|
||||||
emitline("\tLEAQ\t");
|
if (ptrroot) {
|
||||||
emitsymname(c, rootname);
|
emitline("\tMOVQ\t");
|
||||||
emitline("(SB), CX\n");
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
emitline("\tMOVQ\tAX, ");
|
emitline("\tMOVQ\tAX, ");
|
||||||
emitdispreg(totaloff: i64, "CX");
|
emitdispreg(totaloff: i64, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
@@ -3970,10 +4024,16 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
let mov: str = "MOVSD";
|
let mov: str = "MOVSD";
|
||||||
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (isglobal) {
|
if (viacx) {
|
||||||
emitline("\tLEAQ\t");
|
if (ptrroot) {
|
||||||
emitsymname(c, rootname);
|
emitline("\tMOVQ\t");
|
||||||
emitline("(SB), CX\n");
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(mov);
|
emitline(mov);
|
||||||
emitline("\tX0, ");
|
emitline("\tX0, ");
|
||||||
@@ -3990,10 +4050,16 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
let sop: str = fieldstoreop(c, leaffi);
|
let sop: str = fieldstoreop(c, leaffi);
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (isglobal) {
|
if (viacx) {
|
||||||
emitline("\tLEAQ\t");
|
if (ptrroot) {
|
||||||
emitsymname(c, rootname);
|
emitline("\tMOVQ\t");
|
||||||
emitline("(SB), CX\n");
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
|
|||||||
@@ -2364,10 +2364,11 @@ fn cgwidentaggedstore(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz:
|
|||||||
export fn dotchainresolve(c: *cgen, n: *node,
|
export fn dotchainresolve(c: *cgen, n: *node,
|
||||||
outrootname: *str, outrootoff: *i32, outtotaloff: *i32,
|
outrootname: *str, outrootoff: *i32, outtotaloff: *i32,
|
||||||
outleaffi: **fieldinfo, outslicedelta: *i32,
|
outleaffi: **fieldinfo, outslicedelta: *i32,
|
||||||
outisglobal: *bool) bool = {
|
outisglobal: *bool, outptrroot: *bool) bool = {
|
||||||
*outrootname = "";
|
*outrootname = "";
|
||||||
*outrootoff = 0;
|
*outrootoff = 0;
|
||||||
*outisglobal = false;
|
*outisglobal = false;
|
||||||
|
*outptrroot = false;
|
||||||
*outtotaloff = 0;
|
*outtotaloff = 0;
|
||||||
*outleaffi = nil;
|
*outleaffi = nil;
|
||||||
*outslicedelta = -1;
|
*outslicedelta = -1;
|
||||||
@@ -2395,6 +2396,20 @@ export fn dotchainresolve(c: *cgen, n: *node,
|
|||||||
rootstruct = lc.tnode.str;
|
rootstruct = lc.tnode.str;
|
||||||
*outrootoff = lc.off;
|
*outrootoff = lc.off;
|
||||||
};
|
};
|
||||||
|
// `*T` root (param/local): dereference at emit time;
|
||||||
|
// pointee struct supplies the field layout. Callers
|
||||||
|
// that opt in via *outptrroot emit a MOVQ load of the
|
||||||
|
// slot before indexing.
|
||||||
|
if (lc.tnode.kind == nkind.N_TPTR) {
|
||||||
|
let pe: *node = lc.tnode.lhs;
|
||||||
|
if (pe != nil) {
|
||||||
|
if (pe.kind == nkind.N_TNAME) {
|
||||||
|
rootstruct = pe.str;
|
||||||
|
*outrootoff = lc.off;
|
||||||
|
*outptrroot = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
if (rootstruct.len == 0) {
|
if (rootstruct.len == 0) {
|
||||||
|
|||||||
@@ -7934,10 +7934,11 @@ fn cgwidentaggedstore(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz:
|
|||||||
export fn dotchainresolve(c: *cgen, n: *node,
|
export fn dotchainresolve(c: *cgen, n: *node,
|
||||||
outrootname: *str, outrootoff: *i32, outtotaloff: *i32,
|
outrootname: *str, outrootoff: *i32, outtotaloff: *i32,
|
||||||
outleaffi: **fieldinfo, outslicedelta: *i32,
|
outleaffi: **fieldinfo, outslicedelta: *i32,
|
||||||
outisglobal: *bool) bool = {
|
outisglobal: *bool, outptrroot: *bool) bool = {
|
||||||
*outrootname = "";
|
*outrootname = "";
|
||||||
*outrootoff = 0;
|
*outrootoff = 0;
|
||||||
*outisglobal = false;
|
*outisglobal = false;
|
||||||
|
*outptrroot = false;
|
||||||
*outtotaloff = 0;
|
*outtotaloff = 0;
|
||||||
*outleaffi = nil;
|
*outleaffi = nil;
|
||||||
*outslicedelta = -1;
|
*outslicedelta = -1;
|
||||||
@@ -7965,6 +7966,20 @@ export fn dotchainresolve(c: *cgen, n: *node,
|
|||||||
rootstruct = lc.tnode.str;
|
rootstruct = lc.tnode.str;
|
||||||
*outrootoff = lc.off;
|
*outrootoff = lc.off;
|
||||||
};
|
};
|
||||||
|
// `*T` root (param/local): dereference at emit time;
|
||||||
|
// pointee struct supplies the field layout. Callers
|
||||||
|
// that opt in via *outptrroot emit a MOVQ load of the
|
||||||
|
// slot before indexing.
|
||||||
|
if (lc.tnode.kind == nkind.N_TPTR) {
|
||||||
|
let pe: *node = lc.tnode.lhs;
|
||||||
|
if (pe != nil) {
|
||||||
|
if (pe.kind == nkind.N_TNAME) {
|
||||||
|
rootstruct = pe.str;
|
||||||
|
*outrootoff = lc.off;
|
||||||
|
*outptrroot = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
if (rootstruct.len == 0) {
|
if (rootstruct.len == 0) {
|
||||||
@@ -9648,15 +9663,27 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
let leaffi: *fieldinfo = nil;
|
let leaffi: *fieldinfo = nil;
|
||||||
let slicedelta: i32 = -1;
|
let slicedelta: i32 = -1;
|
||||||
let isglobal: bool = false;
|
let isglobal: bool = false;
|
||||||
|
let ptrroot: bool = false;
|
||||||
let pok: bool = dotchainresolve(c, n,
|
let pok: bool = dotchainresolve(c, n,
|
||||||
&rootname, &rootoff, &totaloff,
|
&rootname, &rootoff, &totaloff,
|
||||||
&leaffi, &slicedelta, &isglobal);
|
&leaffi, &slicedelta, &isglobal, &ptrroot);
|
||||||
if (pok) {
|
if (pok) {
|
||||||
|
// `*T` root: load the pointer slot once into CX,
|
||||||
|
// then index every leaf at total_off off CX. Same
|
||||||
|
// emit shape as the global path (LEAQ → CX) — only
|
||||||
|
// the loader instruction differs.
|
||||||
|
let viacx: bool = isglobal || ptrroot;
|
||||||
if (slicedelta >= 0) {
|
if (slicedelta >= 0) {
|
||||||
if (isglobal) {
|
if (viacx) {
|
||||||
emitline("\tLEAQ\t");
|
if (ptrroot) {
|
||||||
emitsymname(c, rootname);
|
emitline("\tMOVQ\t");
|
||||||
emitline("(SB), CX\n");
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg((totaloff + slicedelta): i64, "CX");
|
emitdispreg((totaloff + slicedelta): i64, "CX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
@@ -9668,10 +9695,16 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (isstrtype(c, leaffi.tnode)) {
|
if (isstrtype(c, leaffi.tnode)) {
|
||||||
if (isglobal) {
|
if (viacx) {
|
||||||
emitline("\tLEAQ\t");
|
if (ptrroot) {
|
||||||
emitsymname(c, rootname);
|
emitline("\tMOVQ\t");
|
||||||
emitline("(SB), CX\n");
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg(totaloff: i64, "CX");
|
emitdispreg(totaloff: i64, "CX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
@@ -9691,10 +9724,16 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
if (isfloattype(c, leaffi.tnode)) {
|
if (isfloattype(c, leaffi.tnode)) {
|
||||||
let mov: str = "MOVSD";
|
let mov: str = "MOVSD";
|
||||||
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
||||||
if (isglobal) {
|
if (viacx) {
|
||||||
emitline("\tLEAQ\t");
|
if (ptrroot) {
|
||||||
emitsymname(c, rootname);
|
emitline("\tMOVQ\t");
|
||||||
emitline("(SB), CX\n");
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(mov);
|
emitline(mov);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -9710,10 +9749,16 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let lop: str = fieldloadop(c, leaffi);
|
let lop: str = fieldloadop(c, leaffi);
|
||||||
if (isglobal) {
|
if (viacx) {
|
||||||
emitline("\tLEAQ\t");
|
if (ptrroot) {
|
||||||
emitsymname(c, rootname);
|
emitline("\tMOVQ\t");
|
||||||
emitline("(SB), CX\n");
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(lop);
|
emitline(lop);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -10005,9 +10050,16 @@ fn cgun(c: *cgen, n: *node) void = {
|
|||||||
let leaffi: *fieldinfo = nil;
|
let leaffi: *fieldinfo = nil;
|
||||||
let slicedelta: i32 = -1;
|
let slicedelta: i32 = -1;
|
||||||
let isglobal: bool = false;
|
let isglobal: bool = false;
|
||||||
|
let ptrroot: bool = false;
|
||||||
let pok: bool = dotchainresolve(c, opnd,
|
let pok: bool = dotchainresolve(c, opnd,
|
||||||
&rootname, &rootoff, &totaloff,
|
&rootname, &rootoff, &totaloff,
|
||||||
&leaffi, &slicedelta, &isglobal);
|
&leaffi, &slicedelta, &isglobal,
|
||||||
|
&ptrroot);
|
||||||
|
// `&` through a `*T`-rooted chain is a
|
||||||
|
// separate shape (would need MOVQ + LEAQ
|
||||||
|
// disp(CX), AX). Not exercised by current
|
||||||
|
// callers — skip and fall through.
|
||||||
|
if (ptrroot) { pok = false; };
|
||||||
if (pok) {
|
if (pok) {
|
||||||
let extra: i32 = 0;
|
let extra: i32 = 0;
|
||||||
if (slicedelta >= 0) { extra = slicedelta; };
|
if (slicedelta >= 0) { extra = slicedelta; };
|
||||||
@@ -11957,16 +12009,27 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
let leaffi: *fieldinfo = nil;
|
let leaffi: *fieldinfo = nil;
|
||||||
let slicedelta: i32 = -1;
|
let slicedelta: i32 = -1;
|
||||||
let isglobal: bool = false;
|
let isglobal: bool = false;
|
||||||
|
let ptrroot: bool = false;
|
||||||
let yok: bool = dotchainresolve(c, lhs,
|
let yok: bool = dotchainresolve(c, lhs,
|
||||||
&rootname, &rootoff, &totaloff,
|
&rootname, &rootoff, &totaloff,
|
||||||
&leaffi, &slicedelta, &isglobal);
|
&leaffi, &slicedelta, &isglobal, &ptrroot);
|
||||||
if (yok) {
|
if (yok) {
|
||||||
|
// `*T` root and global share the CX-based emit:
|
||||||
|
// loader runs AFTER cgexpr(rhs) so AX/BX/X0 stay
|
||||||
|
// intact, then stores at total_off off CX.
|
||||||
|
let viacx: bool = isglobal || ptrroot;
|
||||||
if (slicedelta >= 0) {
|
if (slicedelta >= 0) {
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (isglobal) {
|
if (viacx) {
|
||||||
emitline("\tLEAQ\t");
|
if (ptrroot) {
|
||||||
emitsymname(c, rootname);
|
emitline("\tMOVQ\t");
|
||||||
emitline("(SB), CX\n");
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
emitline("\tMOVQ\tAX, ");
|
emitline("\tMOVQ\tAX, ");
|
||||||
emitdispreg((totaloff + slicedelta): i64, "CX");
|
emitdispreg((totaloff + slicedelta): i64, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
@@ -11979,10 +12042,16 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
if (isstrtype(c, leaffi.tnode)) {
|
if (isstrtype(c, leaffi.tnode)) {
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (isglobal) {
|
if (viacx) {
|
||||||
emitline("\tLEAQ\t");
|
if (ptrroot) {
|
||||||
emitsymname(c, rootname);
|
emitline("\tMOVQ\t");
|
||||||
emitline("(SB), CX\n");
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
emitline("\tMOVQ\tAX, ");
|
emitline("\tMOVQ\tAX, ");
|
||||||
emitdispreg(totaloff: i64, "CX");
|
emitdispreg(totaloff: i64, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
@@ -12003,10 +12072,16 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
let mov: str = "MOVSD";
|
let mov: str = "MOVSD";
|
||||||
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (isglobal) {
|
if (viacx) {
|
||||||
emitline("\tLEAQ\t");
|
if (ptrroot) {
|
||||||
emitsymname(c, rootname);
|
emitline("\tMOVQ\t");
|
||||||
emitline("(SB), CX\n");
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(mov);
|
emitline(mov);
|
||||||
emitline("\tX0, ");
|
emitline("\tX0, ");
|
||||||
@@ -12023,10 +12098,16 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
let sop: str = fieldstoreop(c, leaffi);
|
let sop: str = fieldstoreop(c, leaffi);
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (isglobal) {
|
if (viacx) {
|
||||||
emitline("\tLEAQ\t");
|
if (ptrroot) {
|
||||||
emitsymname(c, rootname);
|
emitline("\tMOVQ\t");
|
||||||
emitline("(SB), CX\n");
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
|
|||||||
@@ -129,6 +129,219 @@ static const struct row rows[] = {
|
|||||||
" return g.i.a + g.x;\n"
|
" return g.i.a + g.x;\n"
|
||||||
"};\n",
|
"};\n",
|
||||||
14 },
|
14 },
|
||||||
|
/* `*T` parameter, single-dot read: `fn f(o: *outer) i32 = o.f`.
|
||||||
|
* Already worked via the existing pointer-to-struct-field
|
||||||
|
* branch; pinned here so future refactors don't regress it. */
|
||||||
|
{ "ptr_root_single_read",
|
||||||
|
"type outer = struct { f: i32, g: i32 };\n"
|
||||||
|
"fn get(o: *outer) i32 = { return o.f; };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let x: outer; x.f = 42; x.g = 0;\n"
|
||||||
|
" return get(&x);\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* `*T` parameter, single-dot write: `fn f(o: *outer) void = …`.
|
||||||
|
* Already worked via the via_ptr branch in N_ASSIGN; pinned
|
||||||
|
* for the same reason. */
|
||||||
|
{ "ptr_root_single_write",
|
||||||
|
"type outer = struct { f: i32, g: i32 };\n"
|
||||||
|
"fn set(o: *outer) void = { o.f = 42; };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let x: outer; x.f = 0; x.g = 0;\n"
|
||||||
|
" set(&x);\n"
|
||||||
|
" return x.f;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* Drew's report shape — `*T` parameter, 2-deep read.
|
||||||
|
* Used to lower to `MOVQ a(SB), AX` (link-time undefined ref).
|
||||||
|
* Now the spine walker dereferences the root pointer. */
|
||||||
|
{ "ptr_root_2deep_read",
|
||||||
|
"type inner = struct { a: i32, b: i32 };\n"
|
||||||
|
"type outer = struct { i: inner, x: i32 };\n"
|
||||||
|
"fn get(o: *outer) i32 = { return o.i.a; };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let v: outer; v.i.a = 7; v.i.b = 0; v.x = 0;\n"
|
||||||
|
" return get(&v);\n"
|
||||||
|
"};\n",
|
||||||
|
7 },
|
||||||
|
/* `*T` parameter, 2-deep write — bufio.init shape.
|
||||||
|
* Previously silently dropped; now MOVQ off(BP), CX +
|
||||||
|
* store at total_off(CX). */
|
||||||
|
{ "ptr_root_2deep_write",
|
||||||
|
"type inner = struct { a: i32, b: i32 };\n"
|
||||||
|
"type outer = struct { i: inner, x: i32 };\n"
|
||||||
|
"fn set(o: *outer) void = { o.i.a = 5; };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let v: outer; v.i.a = 0; v.i.b = 0; v.x = 0;\n"
|
||||||
|
" set(&v);\n"
|
||||||
|
" return v.i.a;\n"
|
||||||
|
"};\n",
|
||||||
|
5 },
|
||||||
|
/* 3-deep `*T`-rooted: proves the spine walker loops past the
|
||||||
|
* pointer-root hop and isn't hardcoded at depth 2. */
|
||||||
|
{ "ptr_root_3deep",
|
||||||
|
"type a3 = struct { x: i32 };\n"
|
||||||
|
"type a2 = struct { a: a3 };\n"
|
||||||
|
"type a1 = struct { a: a2 };\n"
|
||||||
|
"fn put(p: *a1) void = { p.a.a.x = 9; };\n"
|
||||||
|
"fn get(p: *a1) i32 = { return p.a.a.x; };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let v: a1; v.a.a.x = 0;\n"
|
||||||
|
" put(&v);\n"
|
||||||
|
" return get(&v);\n"
|
||||||
|
"};\n",
|
||||||
|
9 },
|
||||||
|
/* `*T` *local* (not just param) — same emit path through
|
||||||
|
* localfind, but exercised separately for symmetry. */
|
||||||
|
{ "ptr_root_local",
|
||||||
|
"type inner = struct { a: i32, b: i32 };\n"
|
||||||
|
"type outer = struct { i: inner, x: i32 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let v: outer; v.i.a = 0; v.i.b = 0; v.x = 0;\n"
|
||||||
|
" let p: *outer = &v;\n"
|
||||||
|
" p.i.a = 13;\n"
|
||||||
|
" return p.i.a;\n"
|
||||||
|
"};\n",
|
||||||
|
13 },
|
||||||
|
/* str leaf field through a `*T`-rooted chain — exercises the
|
||||||
|
* (AX=ptr, BX=len) convention through the CX-base load. */
|
||||||
|
{ "ptr_root_str_leaf",
|
||||||
|
"type holder = struct { s: str, pad: i32 };\n"
|
||||||
|
"type box = struct { h: holder, tag: i32 };\n"
|
||||||
|
"fn put(b: *box) void = { b.h.s = \"abc\"; };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let b: box;\n"
|
||||||
|
" let empty: str; b.h.s = empty; b.h.pad = 0; b.tag = 0;\n"
|
||||||
|
" put(&b);\n"
|
||||||
|
" if (b.h.s.len == 3) { return 42; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* slice pseudo-field on a `*T`-rooted chain. .len of an empty
|
||||||
|
* slice round-trips through the CX-base load. */
|
||||||
|
{ "ptr_root_slice_pseudo",
|
||||||
|
"type bag = struct { buf: []u8, x: i32 };\n"
|
||||||
|
"fn setlen(b: *bag, n: i32) void = { b.buf.len = n; };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let b: bag;\n"
|
||||||
|
" let nope: []u8; b.buf = nope; b.x = 0;\n"
|
||||||
|
" setlen(&b, 5);\n"
|
||||||
|
" return b.buf.len: i32;\n"
|
||||||
|
"};\n",
|
||||||
|
5 },
|
||||||
|
/* f64 leaf field through a `*T`-rooted chain — exercises the
|
||||||
|
* X0 spill / MOVSD store path off the CX base. */
|
||||||
|
{ "ptr_root_f64_leaf",
|
||||||
|
"type holder = struct { v: f64, pad: i32 };\n"
|
||||||
|
"type box = struct { h: holder, tag: i32 };\n"
|
||||||
|
"fn put(b: *box) void = { b.h.v = 0.5f64; };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let b: box; b.h.v = 0.0f64; b.h.pad = 0; b.tag = 0;\n"
|
||||||
|
" put(&b);\n"
|
||||||
|
" if (b.h.v == 0.5f64) { return 42; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* Sub-word leaf widths through a `*T`-rooted chain — pins
|
||||||
|
* fldloadop / fldstoreop on the CX-base path. u8/i8/u16/i16/i32
|
||||||
|
* all stored then read back through the inner pointer. */
|
||||||
|
{ "ptr_root_subword_u8",
|
||||||
|
"type holder = struct { v: u8, pad: u8 };\n"
|
||||||
|
"type box = struct { h: holder, tag: i32 };\n"
|
||||||
|
"fn put(b: *box) void = { b.h.v = 99u8; };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let b: box; b.h.v = 0u8; b.h.pad = 0u8; b.tag = 0;\n"
|
||||||
|
" put(&b);\n"
|
||||||
|
" return b.h.v: i32;\n"
|
||||||
|
"};\n",
|
||||||
|
99 },
|
||||||
|
{ "ptr_root_subword_i32",
|
||||||
|
"type holder = struct { v: i32, pad: i32 };\n"
|
||||||
|
"type box = struct { h: holder, tag: i32 };\n"
|
||||||
|
"fn put(b: *box) void = { b.h.v = 0x12345; };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let b: box; b.h.v = 0; b.h.pad = 0; b.tag = 0;\n"
|
||||||
|
" put(&b);\n"
|
||||||
|
" if (b.h.v == 0x12345) { return 42; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
{ "ptr_root_subword_i16",
|
||||||
|
"type holder = struct { v: i16, pad: i16 };\n"
|
||||||
|
"type box = struct { h: holder, tag: i32 };\n"
|
||||||
|
"fn put(b: *box) void = { b.h.v = 4321i16; };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let b: box; b.h.v = 0i16; b.h.pad = 0i16; b.tag = 0;\n"
|
||||||
|
" put(&b);\n"
|
||||||
|
" if (b.h.v == 4321i16) { return 42; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* Negative signed leaf widths through a `*T`-rooted chain.
|
||||||
|
* Stored value's sign bit must round-trip — pins localloadop /
|
||||||
|
* fldloadop sign-extension on the CX-base path so `*T`-rooted
|
||||||
|
* chains don't silently widen to a positive bit pattern. i8, i16
|
||||||
|
* and i32 each store −7 and check `< 0` after the chained read. */
|
||||||
|
{ "ptr_root_subword_i8_neg",
|
||||||
|
"type holder = struct { v: i8, pad: i8 };\n"
|
||||||
|
"type box = struct { h: holder, tag: i32 };\n"
|
||||||
|
"fn put(b: *box) void = { b.h.v = -7i8; };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let b: box; b.h.v = 0i8; b.h.pad = 0i8; b.tag = 0;\n"
|
||||||
|
" put(&b);\n"
|
||||||
|
" let r: i32 = b.h.v: i32;\n"
|
||||||
|
" if (r == -7) { return 42; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
{ "ptr_root_subword_i16_neg",
|
||||||
|
"type holder = struct { v: i16, pad: i16 };\n"
|
||||||
|
"type box = struct { h: holder, tag: i32 };\n"
|
||||||
|
"fn put(b: *box) void = { b.h.v = -7i16; };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let b: box; b.h.v = 0i16; b.h.pad = 0i16; b.tag = 0;\n"
|
||||||
|
" put(&b);\n"
|
||||||
|
" let r: i32 = b.h.v: i32;\n"
|
||||||
|
" if (r == -7) { return 42; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
{ "ptr_root_subword_i32_neg",
|
||||||
|
"type holder = struct { v: i32, pad: i32 };\n"
|
||||||
|
"type box = struct { h: holder, tag: i32 };\n"
|
||||||
|
"fn put(b: *box) void = { b.h.v = -7; };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let b: box; b.h.v = 0; b.h.pad = 0; b.tag = 0;\n"
|
||||||
|
" put(&b);\n"
|
||||||
|
" if (b.h.v < 0) { return 42; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* Round-trip across the aliasing seam: caller writes via the
|
||||||
|
* local value, callee reads via the `*T`-rooted chain — and
|
||||||
|
* vice versa. Pins that both branches index the SAME slot
|
||||||
|
* (same field offsets resolved off the right base). */
|
||||||
|
{ "ptr_root_roundtrip_local_write_ptr_read",
|
||||||
|
"type inner = struct { a: i32, b: i32 };\n"
|
||||||
|
"type outer = struct { i: inner, x: i32 };\n"
|
||||||
|
"fn get(o: *outer) i32 = { return o.i.a; };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let o: outer; o.i.a = 0; o.i.b = 0; o.x = 0;\n"
|
||||||
|
" let p: *outer = &o;\n"
|
||||||
|
" o.i.a = 42;\n"
|
||||||
|
" return get(p);\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
{ "ptr_root_roundtrip_ptr_write_local_read",
|
||||||
|
"type inner = struct { a: i32, b: i32 };\n"
|
||||||
|
"type outer = struct { i: inner, x: i32 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let o: outer; o.i.a = 0; o.i.b = 0; o.x = 0;\n"
|
||||||
|
" let p: *outer = &o;\n"
|
||||||
|
" p.i.a = 42;\n"
|
||||||
|
" return o.i.a;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
};
|
};
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|||||||
Reference in New Issue
Block a user