w6c+selfhost: cgen N_DOT slice-field through *T root in call args (closes #29)
This commit is contained in:
@@ -5983,6 +5983,75 @@ fn nodeisslice(c: *cgen, n: *node) bool = {
|
||||
};
|
||||
if (k == nkind.N_SLICE) { return true; };
|
||||
if (k == nkind.N_CAST) { return isslicetype(c, n.rhs); };
|
||||
// N_DOT to a slice field: resolve the field through the struct
|
||||
// (or *struct) the base ident / inner chain lands on, then check
|
||||
// the field tnode. Mirrors nodeisstr's N_DOT branch so call-arg
|
||||
// push/pop counts 3 words for `p.sl` and `p.inner.sl` shapes.
|
||||
// `.ptr` / `.len` / `.cap` are pseudo-fields — they yield ptr
|
||||
// (*u8) and i32, not a slice — so we exclude them up front.
|
||||
if (k == nkind.N_DOT) {
|
||||
let base: *node = n.lhs;
|
||||
let fld: str = n.str;
|
||||
if (streq(fld, "ptr")) { return false; };
|
||||
if (streq(fld, "len")) { return false; };
|
||||
if (streq(fld, "cap")) { return false; };
|
||||
if (base != nil) {
|
||||
let sname: str;
|
||||
sname.ptr = nil; sname.len = 0;
|
||||
if (base.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, base.str);
|
||||
if (lc != nil) {
|
||||
let tn: *node = lc.tnode;
|
||||
let lkind: nkind = nkind.N_NONE;
|
||||
if (tn != nil) { lkind = tn.kind; };
|
||||
if (lkind == nkind.N_TNAME) { sname = tn.str; };
|
||||
if (lkind == nkind.N_TPTR) {
|
||||
let inner: *node = tn.lhs;
|
||||
if (inner != nil) {
|
||||
if (inner.kind == nkind.N_TNAME) { sname = inner.str; };
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (base.kind == nkind.N_DOT) {
|
||||
let innert: *node = dotinnerstructptr(c, base);
|
||||
if (innert != nil) {
|
||||
if (innert.kind == nkind.N_TNAME) { sname = innert.str; };
|
||||
};
|
||||
};
|
||||
if (sname.len > 0) {
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
if (streq(fi.fname, fld)) {
|
||||
return isslicetype(c, fi.tnode);
|
||||
};
|
||||
fi = fi.finext;
|
||||
};
|
||||
};
|
||||
};
|
||||
// Chained dot through value-struct hops (`o.inner.sl`,
|
||||
// `p.inner.sl`): dotinnerstructptr above only walks
|
||||
// *struct fields, so a value-struct chain falls through.
|
||||
// dotchainresolve handles arbitrary depth through value
|
||||
// struct AND `*T` root, returning the leaf fieldinfo.
|
||||
let rootnm: str = "";
|
||||
let rootoff: i32 = 0;
|
||||
let totaloff: i32 = 0;
|
||||
let lfi: *fieldinfo = nil;
|
||||
let sdelta: i32 = -1;
|
||||
let isglobal: bool = false;
|
||||
let ptrroot: bool = false;
|
||||
let ok: bool = dotchainresolve(c, n,
|
||||
&rootnm, &rootoff, &totaloff,
|
||||
&lfi, &sdelta, &isglobal, &ptrroot);
|
||||
if (ok && sdelta < 0 && lfi != nil) {
|
||||
return isslicetype(c, lfi.tnode);
|
||||
};
|
||||
};
|
||||
return false;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -6064,12 +6133,10 @@ fn nodeisstr(c: *cgen, n: *node) bool = {
|
||||
};
|
||||
};
|
||||
// Chained dot through value-struct hops (`p.inner.s`):
|
||||
// dotinnerstructptr above only walks *struct fields, so
|
||||
// a value-struct chain falls through and the call-arg
|
||||
// path then pushes only 1 word for the str instead of
|
||||
// 2 (ptr+len), silently dropping the len half.
|
||||
// dotchainresolve handles arbitrary depth through value
|
||||
// struct AND `*T` root, returning the leaf fieldinfo.
|
||||
// dotinnerstructptr above only walks *struct fields;
|
||||
// dotchainresolve handles arbitrary depth through
|
||||
// value struct AND `*T` root. Mirror of the nodeisslice
|
||||
// fallback so chained str-field args also push 2 words.
|
||||
let rootnm: str = "";
|
||||
let rootoff: i32 = 0;
|
||||
let totaloff: i32 = 0;
|
||||
@@ -9335,6 +9402,21 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitdispreg(fi.foff: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\tCX, BX\n");
|
||||
} else { if (isslicetype(c, fi.tnode)) {
|
||||
// slice field via *struct: load
|
||||
// (ptr, len, cap) into (AX, BX, CX).
|
||||
// BX holds the *struct pointer, so
|
||||
// load .len LAST so the earlier
|
||||
// reads still index off the base.
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(fi.foff: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((fi.foff + 16): i64, "BX");
|
||||
emitline(", CX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((fi.foff + 8): i64, "BX");
|
||||
emitline(", BX\n");
|
||||
} else { if (isfloattype(c, fi.tnode)) {
|
||||
// f64/f32 via *struct: route through X0.
|
||||
// MOVQ into AX leaves the SSE reg stale
|
||||
@@ -9355,7 +9437,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline("\t");
|
||||
emitdispreg(fi.foff: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
}; };
|
||||
}; }; };
|
||||
return;
|
||||
};
|
||||
fi = fi.finext;
|
||||
@@ -9381,6 +9463,19 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((lc.off + fi.foff + 8): i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else { if (isslicetype(c, fi.tnode)) {
|
||||
// slice field: load (ptr, len, cap)
|
||||
// into (AX, BX, CX). Base is BP so
|
||||
// no aliasing — order doesn't matter.
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((lc.off + fi.foff): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((lc.off + fi.foff + 8): i64);
|
||||
emitline("(BP), BX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((lc.off + fi.foff + 16): i64);
|
||||
emitline("(BP), CX\n");
|
||||
} else { if (isfloattype(c, fi.tnode)) {
|
||||
// f64/f32 field: route through X0.
|
||||
let mov: str = "MOVSD";
|
||||
@@ -9397,7 +9492,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline("\t");
|
||||
emitoff((lc.off + fi.foff): i64);
|
||||
emitline("(BP), AX\n");
|
||||
}; };
|
||||
}; }; };
|
||||
return;
|
||||
};
|
||||
fi = fi.finext;
|
||||
@@ -9806,6 +9901,45 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (isslicetype(c, leaffi.tnode)) {
|
||||
// Slice leaf: load all three header words into
|
||||
// (AX=ptr, BX=len, CX=cap). For the viacx path
|
||||
// (global or `*T` root) CX is the base; load
|
||||
// .cap LAST so the base survives the earlier
|
||||
// reads. For BP-rooted locals the registers
|
||||
// don't alias so order is free.
|
||||
if (viacx) {
|
||||
if (ptrroot) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(rootoff: i64);
|
||||
emitline("(BP), CX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), CX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(totaloff: i64, "CX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((totaloff + 8): i64, "CX");
|
||||
emitline(", BX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((totaloff + 16): i64, "CX");
|
||||
emitline(", CX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((rootoff + totaloff): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((rootoff + totaloff + 8): i64);
|
||||
emitline("(BP), BX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((rootoff + totaloff + 16): i64);
|
||||
emitline("(BP), CX\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (isfloattype(c, leaffi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
||||
@@ -9900,6 +10034,22 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
// slice field: load (ptr, len, cap)
|
||||
// into (AX, BX, CX). AX is the *struct
|
||||
// base, so load .ptr (which targets
|
||||
// AX) LAST.
|
||||
if (isslicetype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((fi.foff + 8): i64, "AX");
|
||||
emitline(", BX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((fi.foff + 16): i64, "AX");
|
||||
emitline(", CX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(fi.foff: i64, "AX");
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
// f64/f32 chained field: route through X0.
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
|
||||
@@ -1202,6 +1202,21 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitdispreg(fi.foff: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\tCX, BX\n");
|
||||
} else { if (isslicetype(c, fi.tnode)) {
|
||||
// slice field via *struct: load
|
||||
// (ptr, len, cap) into (AX, BX, CX).
|
||||
// BX holds the *struct pointer, so
|
||||
// load .len LAST so the earlier
|
||||
// reads still index off the base.
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(fi.foff: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((fi.foff + 16): i64, "BX");
|
||||
emitline(", CX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((fi.foff + 8): i64, "BX");
|
||||
emitline(", BX\n");
|
||||
} else { if (isfloattype(c, fi.tnode)) {
|
||||
// f64/f32 via *struct: route through X0.
|
||||
// MOVQ into AX leaves the SSE reg stale
|
||||
@@ -1222,7 +1237,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline("\t");
|
||||
emitdispreg(fi.foff: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
}; };
|
||||
}; }; };
|
||||
return;
|
||||
};
|
||||
fi = fi.finext;
|
||||
@@ -1248,6 +1263,19 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((lc.off + fi.foff + 8): i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else { if (isslicetype(c, fi.tnode)) {
|
||||
// slice field: load (ptr, len, cap)
|
||||
// into (AX, BX, CX). Base is BP so
|
||||
// no aliasing — order doesn't matter.
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((lc.off + fi.foff): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((lc.off + fi.foff + 8): i64);
|
||||
emitline("(BP), BX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((lc.off + fi.foff + 16): i64);
|
||||
emitline("(BP), CX\n");
|
||||
} else { if (isfloattype(c, fi.tnode)) {
|
||||
// f64/f32 field: route through X0.
|
||||
let mov: str = "MOVSD";
|
||||
@@ -1264,7 +1292,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline("\t");
|
||||
emitoff((lc.off + fi.foff): i64);
|
||||
emitline("(BP), AX\n");
|
||||
}; };
|
||||
}; }; };
|
||||
return;
|
||||
};
|
||||
fi = fi.finext;
|
||||
@@ -1673,6 +1701,45 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (isslicetype(c, leaffi.tnode)) {
|
||||
// Slice leaf: load all three header words into
|
||||
// (AX=ptr, BX=len, CX=cap). For the viacx path
|
||||
// (global or `*T` root) CX is the base; load
|
||||
// .cap LAST so the base survives the earlier
|
||||
// reads. For BP-rooted locals the registers
|
||||
// don't alias so order is free.
|
||||
if (viacx) {
|
||||
if (ptrroot) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(rootoff: i64);
|
||||
emitline("(BP), CX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), CX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(totaloff: i64, "CX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((totaloff + 8): i64, "CX");
|
||||
emitline(", BX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((totaloff + 16): i64, "CX");
|
||||
emitline(", CX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((rootoff + totaloff): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((rootoff + totaloff + 8): i64);
|
||||
emitline("(BP), BX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((rootoff + totaloff + 16): i64);
|
||||
emitline("(BP), CX\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (isfloattype(c, leaffi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
||||
@@ -1767,6 +1834,22 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
// slice field: load (ptr, len, cap)
|
||||
// into (AX, BX, CX). AX is the *struct
|
||||
// base, so load .ptr (which targets
|
||||
// AX) LAST.
|
||||
if (isslicetype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((fi.foff + 8): i64, "AX");
|
||||
emitline(", BX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((fi.foff + 16): i64, "AX");
|
||||
emitline(", CX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(fi.foff: i64, "AX");
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
// f64/f32 chained field: route through X0.
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
|
||||
@@ -413,6 +413,75 @@ fn nodeisslice(c: *cgen, n: *node) bool = {
|
||||
};
|
||||
if (k == nkind.N_SLICE) { return true; };
|
||||
if (k == nkind.N_CAST) { return isslicetype(c, n.rhs); };
|
||||
// N_DOT to a slice field: resolve the field through the struct
|
||||
// (or *struct) the base ident / inner chain lands on, then check
|
||||
// the field tnode. Mirrors nodeisstr's N_DOT branch so call-arg
|
||||
// push/pop counts 3 words for `p.sl` and `p.inner.sl` shapes.
|
||||
// `.ptr` / `.len` / `.cap` are pseudo-fields — they yield ptr
|
||||
// (*u8) and i32, not a slice — so we exclude them up front.
|
||||
if (k == nkind.N_DOT) {
|
||||
let base: *node = n.lhs;
|
||||
let fld: str = n.str;
|
||||
if (streq(fld, "ptr")) { return false; };
|
||||
if (streq(fld, "len")) { return false; };
|
||||
if (streq(fld, "cap")) { return false; };
|
||||
if (base != nil) {
|
||||
let sname: str;
|
||||
sname.ptr = nil; sname.len = 0;
|
||||
if (base.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, base.str);
|
||||
if (lc != nil) {
|
||||
let tn: *node = lc.tnode;
|
||||
let lkind: nkind = nkind.N_NONE;
|
||||
if (tn != nil) { lkind = tn.kind; };
|
||||
if (lkind == nkind.N_TNAME) { sname = tn.str; };
|
||||
if (lkind == nkind.N_TPTR) {
|
||||
let inner: *node = tn.lhs;
|
||||
if (inner != nil) {
|
||||
if (inner.kind == nkind.N_TNAME) { sname = inner.str; };
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (base.kind == nkind.N_DOT) {
|
||||
let innert: *node = dotinnerstructptr(c, base);
|
||||
if (innert != nil) {
|
||||
if (innert.kind == nkind.N_TNAME) { sname = innert.str; };
|
||||
};
|
||||
};
|
||||
if (sname.len > 0) {
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
if (streq(fi.fname, fld)) {
|
||||
return isslicetype(c, fi.tnode);
|
||||
};
|
||||
fi = fi.finext;
|
||||
};
|
||||
};
|
||||
};
|
||||
// Chained dot through value-struct hops (`o.inner.sl`,
|
||||
// `p.inner.sl`): dotinnerstructptr above only walks
|
||||
// *struct fields, so a value-struct chain falls through.
|
||||
// dotchainresolve handles arbitrary depth through value
|
||||
// struct AND `*T` root, returning the leaf fieldinfo.
|
||||
let rootnm: str = "";
|
||||
let rootoff: i32 = 0;
|
||||
let totaloff: i32 = 0;
|
||||
let lfi: *fieldinfo = nil;
|
||||
let sdelta: i32 = -1;
|
||||
let isglobal: bool = false;
|
||||
let ptrroot: bool = false;
|
||||
let ok: bool = dotchainresolve(c, n,
|
||||
&rootnm, &rootoff, &totaloff,
|
||||
&lfi, &sdelta, &isglobal, &ptrroot);
|
||||
if (ok && sdelta < 0 && lfi != nil) {
|
||||
return isslicetype(c, lfi.tnode);
|
||||
};
|
||||
};
|
||||
return false;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -494,12 +563,10 @@ fn nodeisstr(c: *cgen, n: *node) bool = {
|
||||
};
|
||||
};
|
||||
// Chained dot through value-struct hops (`p.inner.s`):
|
||||
// dotinnerstructptr above only walks *struct fields, so
|
||||
// a value-struct chain falls through and the call-arg
|
||||
// path then pushes only 1 word for the str instead of
|
||||
// 2 (ptr+len), silently dropping the len half.
|
||||
// dotchainresolve handles arbitrary depth through value
|
||||
// struct AND `*T` root, returning the leaf fieldinfo.
|
||||
// dotinnerstructptr above only walks *struct fields;
|
||||
// dotchainresolve handles arbitrary depth through
|
||||
// value struct AND `*T` root. Mirror of the nodeisslice
|
||||
// fallback so chained str-field args also push 2 words.
|
||||
let rootnm: str = "";
|
||||
let rootoff: i32 = 0;
|
||||
let totaloff: i32 = 0;
|
||||
|
||||
@@ -5983,6 +5983,75 @@ fn nodeisslice(c: *cgen, n: *node) bool = {
|
||||
};
|
||||
if (k == nkind.N_SLICE) { return true; };
|
||||
if (k == nkind.N_CAST) { return isslicetype(c, n.rhs); };
|
||||
// N_DOT to a slice field: resolve the field through the struct
|
||||
// (or *struct) the base ident / inner chain lands on, then check
|
||||
// the field tnode. Mirrors nodeisstr's N_DOT branch so call-arg
|
||||
// push/pop counts 3 words for `p.sl` and `p.inner.sl` shapes.
|
||||
// `.ptr` / `.len` / `.cap` are pseudo-fields — they yield ptr
|
||||
// (*u8) and i32, not a slice — so we exclude them up front.
|
||||
if (k == nkind.N_DOT) {
|
||||
let base: *node = n.lhs;
|
||||
let fld: str = n.str;
|
||||
if (streq(fld, "ptr")) { return false; };
|
||||
if (streq(fld, "len")) { return false; };
|
||||
if (streq(fld, "cap")) { return false; };
|
||||
if (base != nil) {
|
||||
let sname: str;
|
||||
sname.ptr = nil; sname.len = 0;
|
||||
if (base.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, base.str);
|
||||
if (lc != nil) {
|
||||
let tn: *node = lc.tnode;
|
||||
let lkind: nkind = nkind.N_NONE;
|
||||
if (tn != nil) { lkind = tn.kind; };
|
||||
if (lkind == nkind.N_TNAME) { sname = tn.str; };
|
||||
if (lkind == nkind.N_TPTR) {
|
||||
let inner: *node = tn.lhs;
|
||||
if (inner != nil) {
|
||||
if (inner.kind == nkind.N_TNAME) { sname = inner.str; };
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (base.kind == nkind.N_DOT) {
|
||||
let innert: *node = dotinnerstructptr(c, base);
|
||||
if (innert != nil) {
|
||||
if (innert.kind == nkind.N_TNAME) { sname = innert.str; };
|
||||
};
|
||||
};
|
||||
if (sname.len > 0) {
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
if (streq(fi.fname, fld)) {
|
||||
return isslicetype(c, fi.tnode);
|
||||
};
|
||||
fi = fi.finext;
|
||||
};
|
||||
};
|
||||
};
|
||||
// Chained dot through value-struct hops (`o.inner.sl`,
|
||||
// `p.inner.sl`): dotinnerstructptr above only walks
|
||||
// *struct fields, so a value-struct chain falls through.
|
||||
// dotchainresolve handles arbitrary depth through value
|
||||
// struct AND `*T` root, returning the leaf fieldinfo.
|
||||
let rootnm: str = "";
|
||||
let rootoff: i32 = 0;
|
||||
let totaloff: i32 = 0;
|
||||
let lfi: *fieldinfo = nil;
|
||||
let sdelta: i32 = -1;
|
||||
let isglobal: bool = false;
|
||||
let ptrroot: bool = false;
|
||||
let ok: bool = dotchainresolve(c, n,
|
||||
&rootnm, &rootoff, &totaloff,
|
||||
&lfi, &sdelta, &isglobal, &ptrroot);
|
||||
if (ok && sdelta < 0 && lfi != nil) {
|
||||
return isslicetype(c, lfi.tnode);
|
||||
};
|
||||
};
|
||||
return false;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -6064,12 +6133,10 @@ fn nodeisstr(c: *cgen, n: *node) bool = {
|
||||
};
|
||||
};
|
||||
// Chained dot through value-struct hops (`p.inner.s`):
|
||||
// dotinnerstructptr above only walks *struct fields, so
|
||||
// a value-struct chain falls through and the call-arg
|
||||
// path then pushes only 1 word for the str instead of
|
||||
// 2 (ptr+len), silently dropping the len half.
|
||||
// dotchainresolve handles arbitrary depth through value
|
||||
// struct AND `*T` root, returning the leaf fieldinfo.
|
||||
// dotinnerstructptr above only walks *struct fields;
|
||||
// dotchainresolve handles arbitrary depth through
|
||||
// value struct AND `*T` root. Mirror of the nodeisslice
|
||||
// fallback so chained str-field args also push 2 words.
|
||||
let rootnm: str = "";
|
||||
let rootoff: i32 = 0;
|
||||
let totaloff: i32 = 0;
|
||||
@@ -9335,6 +9402,21 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitdispreg(fi.foff: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\tCX, BX\n");
|
||||
} else { if (isslicetype(c, fi.tnode)) {
|
||||
// slice field via *struct: load
|
||||
// (ptr, len, cap) into (AX, BX, CX).
|
||||
// BX holds the *struct pointer, so
|
||||
// load .len LAST so the earlier
|
||||
// reads still index off the base.
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(fi.foff: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((fi.foff + 16): i64, "BX");
|
||||
emitline(", CX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((fi.foff + 8): i64, "BX");
|
||||
emitline(", BX\n");
|
||||
} else { if (isfloattype(c, fi.tnode)) {
|
||||
// f64/f32 via *struct: route through X0.
|
||||
// MOVQ into AX leaves the SSE reg stale
|
||||
@@ -9355,7 +9437,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline("\t");
|
||||
emitdispreg(fi.foff: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
}; };
|
||||
}; }; };
|
||||
return;
|
||||
};
|
||||
fi = fi.finext;
|
||||
@@ -9381,6 +9463,19 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((lc.off + fi.foff + 8): i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else { if (isslicetype(c, fi.tnode)) {
|
||||
// slice field: load (ptr, len, cap)
|
||||
// into (AX, BX, CX). Base is BP so
|
||||
// no aliasing — order doesn't matter.
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((lc.off + fi.foff): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((lc.off + fi.foff + 8): i64);
|
||||
emitline("(BP), BX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((lc.off + fi.foff + 16): i64);
|
||||
emitline("(BP), CX\n");
|
||||
} else { if (isfloattype(c, fi.tnode)) {
|
||||
// f64/f32 field: route through X0.
|
||||
let mov: str = "MOVSD";
|
||||
@@ -9397,7 +9492,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline("\t");
|
||||
emitoff((lc.off + fi.foff): i64);
|
||||
emitline("(BP), AX\n");
|
||||
}; };
|
||||
}; }; };
|
||||
return;
|
||||
};
|
||||
fi = fi.finext;
|
||||
@@ -9806,6 +9901,45 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (isslicetype(c, leaffi.tnode)) {
|
||||
// Slice leaf: load all three header words into
|
||||
// (AX=ptr, BX=len, CX=cap). For the viacx path
|
||||
// (global or `*T` root) CX is the base; load
|
||||
// .cap LAST so the base survives the earlier
|
||||
// reads. For BP-rooted locals the registers
|
||||
// don't alias so order is free.
|
||||
if (viacx) {
|
||||
if (ptrroot) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(rootoff: i64);
|
||||
emitline("(BP), CX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), CX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(totaloff: i64, "CX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((totaloff + 8): i64, "CX");
|
||||
emitline(", BX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((totaloff + 16): i64, "CX");
|
||||
emitline(", CX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((rootoff + totaloff): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((rootoff + totaloff + 8): i64);
|
||||
emitline("(BP), BX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((rootoff + totaloff + 16): i64);
|
||||
emitline("(BP), CX\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (isfloattype(c, leaffi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
||||
@@ -9900,6 +10034,22 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
// slice field: load (ptr, len, cap)
|
||||
// into (AX, BX, CX). AX is the *struct
|
||||
// base, so load .ptr (which targets
|
||||
// AX) LAST.
|
||||
if (isslicetype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((fi.foff + 8): i64, "AX");
|
||||
emitline(", BX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((fi.foff + 16): i64, "AX");
|
||||
emitline(", CX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(fi.foff: i64, "AX");
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
// f64/f32 chained field: route through X0.
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
|
||||
Reference in New Issue
Block a user