selfhost: retire dotchain bundle workaround (closes #17)
dotchain struct + bundle pre-declares retired. dotchainresolve now takes 6 direct out-params; per-iter locals declared inline. Bootstrap ww2==ww3==ww4 byte-identical — end-to-end validation of session's N_DOT / N_INDEX / TK_AMP / fldloadop fixes. i64 widths retained on out-params via task #19 workaround (i32 deref- stores leave caller slot upper 4B stale, MOVQ reads zero-extend to garbage). Documented in selfhost/CLAUDE.md active-workarounds.
This commit is contained in:
@@ -8,6 +8,8 @@ The C bootstrap's cgen has known silent-miscompilation traps. They produce wrong
|
|||||||
|
|
||||||
1. **`amalloc(n)` with n < struct size silently corrupts neighbours.** No error — the bump arena hands out n bytes and field writes overflow into the next record. When introducing or growing a struct, audit every `amalloc(_, n)` call site and over-size (we routinely pass 48 for a 40-byte struct). Symptom: linked-list prepends lose all but the most recent entry.
|
1. **`amalloc(n)` with n < struct size silently corrupts neighbours.** No error — the bump arena hands out n bytes and field writes overflow into the next record. When introducing or growing a struct, audit every `amalloc(_, n)` call site and over-size (we routinely pass 48 for a 40-byte struct). Symptom: linked-list prepends lose all but the most recent entry.
|
||||||
|
|
||||||
|
2. **Out-param `*p: *i32` deref-stores leave caller slot's upper 4 bytes stale.** `*p = v` lowers to MOVL (4B), but the caller's 8B slot was zero-init via MOVQ; a later i64-widening read emits MOVQ (8B raw) and zero-extends, so a negative i32 round-trips as a 4G-rooted positive i64. Default to `*i64` out-params for offsets / signed indices until task #19 lands. Symptom: garbage frame offsets like `MOVL AX, 4294967280(BP)` in stage-2 asm.
|
||||||
|
|
||||||
Fixed (no workaround needed):
|
Fixed (no workaround needed):
|
||||||
|
|
||||||
- `def NAME: str = "..."` field access. `.len`/`.ptr` on an Sdef ident
|
- `def NAME: str = "..."` field access. `.len`/`.ptr` on an Sdef ident
|
||||||
|
|||||||
@@ -7878,58 +7878,41 @@ fn cgwidentaggedstore(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz:
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// dotchain — packed result struct for dotchainresolve. Out-params are
|
|
||||||
// bundled to keep the helper at <= 6 register-passed args; wwstage's
|
|
||||||
// per-fn arg-frame computation over-allocates by 16 bytes for any
|
|
||||||
// function with > 6 args (task #7, a pre-existing quirk independent
|
|
||||||
// of this fix), which would silently break the bootstrap fixed-point
|
|
||||||
// gate (993 / 994 / 995).
|
|
||||||
//
|
|
||||||
// Numeric fields are all i64, not i32. wwstage zero-inits an i32
|
|
||||||
// local with MOVQ (8-byte store) but subsequent `out.totaloff = …`
|
|
||||||
// updates would emit MOVL (4-byte store), leaving the upper 4 bytes
|
|
||||||
// stale from the wider init. Keeping the out-params at i64 makes the
|
|
||||||
// init width and the update width agree, so the field reads back
|
|
||||||
// what was written across both stages.
|
|
||||||
type dotchain = struct {
|
|
||||||
rootname: str,
|
|
||||||
rootoff: i64,
|
|
||||||
totaloff: i64,
|
|
||||||
leaffi: *fieldinfo,
|
|
||||||
slicedelta: i64,
|
|
||||||
isglobal: bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Spine-walk a chained N_DOT (n) inward to a root ident, summing field
|
// Spine-walk a chained N_DOT (n) inward to a root ident, summing field
|
||||||
// offsets through value-struct intermediates. Optional slice/str leaf
|
// offsets through value-struct intermediates. Optional slice/str leaf
|
||||||
// pseudo-field (.ptr / .len / .cap) on the last segment is folded into
|
// pseudo-field (.ptr / .len / .cap) on the last segment is folded into
|
||||||
// `out.slicedelta` (0/8/16); otherwise out.leaffi is the leaf fieldinfo
|
// *outslicedelta (0/8/16); otherwise *outleaffi is the leaf fieldinfo
|
||||||
// and slicedelta stays -1. Returns true on success; on false the caller
|
// and *outslicedelta stays -1. Returns true on success; on false the
|
||||||
// falls through to other branches.
|
// caller falls through to other branches.
|
||||||
//
|
//
|
||||||
// Mirrors cmd/w6c/cgen.c's N_DOT chained walker; both stages must agree
|
// Mirrors cmd/w6c/cgen.c's N_DOT chained walker; both stages must agree
|
||||||
// on the same shapes so the bootstrap fixed-point holds. The chain
|
// on the same shapes so the bootstrap fixed-point holds. The chain
|
||||||
// depth is capped at 16 — deeper chains are vanishingly rare and fall
|
// depth is capped at 16 — deeper chains are vanishingly rare and fall
|
||||||
// through.
|
// through.
|
||||||
//
|
//
|
||||||
// On success the caller emits one load/store at root_base + out.totaloff
|
// On success the caller emits one load/store at root_base + *outtotaloff
|
||||||
// (+ slicedelta for pseudo leaf). Root resolves as: local frame slot
|
// (+ slicedelta for pseudo leaf). Root resolves as: local frame slot
|
||||||
// (out.rootoff != 0, isglobal false) or top-level let (isglobal true,
|
// (*outisglobal false, base = *outrootoff(BP)) or top-level let
|
||||||
// root accessed via LEAQ name(SB), CX).
|
// (*outisglobal true, base reached via LEAQ *outrootname(SB), CX).
|
||||||
export fn dotchainresolve(c: *cgen, n: *node, out: *dotchain) bool = {
|
//
|
||||||
out.rootname = "";
|
// Numeric out-params are i64 so the deref-stores stay 8-byte (MOVQ).
|
||||||
out.rootoff = 0i64;
|
// `*p: *i32 = v` writes only 4 bytes via MOVL, leaving the caller's
|
||||||
out.isglobal = false;
|
// 8-byte slot's upper half stale from its zero-init — and a later MOVQ
|
||||||
out.totaloff = 0i64;
|
// read sees the zero-extended low half, so a negative i32 root offset
|
||||||
out.leaffi = nil;
|
// would come back as a huge positive i64. Tracked as task #19; until
|
||||||
out.slicedelta = -1i64;
|
// it lands, callers cast to i32 at the assign sites.
|
||||||
|
export fn dotchainresolve(c: *cgen, n: *node,
|
||||||
|
outrootname: *str, outrootoff: *i64, outtotaloff: *i64,
|
||||||
|
outleaffi: **fieldinfo, outslicedelta: *i64,
|
||||||
|
outisglobal: *bool) bool = {
|
||||||
|
*outrootname = "";
|
||||||
|
*outrootoff = 0i64;
|
||||||
|
*outisglobal = false;
|
||||||
|
*outtotaloff = 0i64;
|
||||||
|
*outleaffi = nil;
|
||||||
|
*outslicedelta = -1i64;
|
||||||
if (n == nil) { return false; };
|
if (n == nil) { return false; };
|
||||||
if (n.kind != nkind.N_DOT) { return false; };
|
if (n.kind != nkind.N_DOT) { return false; };
|
||||||
// Walk inward, recording the N_DOT node at each step (leaf first).
|
|
||||||
// We hold *node pointers (8B each, slotsize-stable across stages)
|
|
||||||
// and read .str on demand — a [16]str array would mis-slot at
|
|
||||||
// wwstage where slotsize("str") returns 8, breaking the bootstrap
|
|
||||||
// fixed-point.
|
|
||||||
let stk: [16]*node;
|
let stk: [16]*node;
|
||||||
let nsteps: i32 = 0;
|
let nsteps: i32 = 0;
|
||||||
let cur: *node = n;
|
let cur: *node = n;
|
||||||
@@ -7943,95 +7926,72 @@ export fn dotchainresolve(c: *cgen, n: *node, out: *dotchain) bool = {
|
|||||||
if (nsteps < 2) { return false; };
|
if (nsteps < 2) { return false; };
|
||||||
if (cur == nil) { return false; };
|
if (cur == nil) { return false; };
|
||||||
if (cur.kind != nkind.N_IDENT) { return false; };
|
if (cur.kind != nkind.N_IDENT) { return false; };
|
||||||
out.rootname = cur.str;
|
*outrootname = cur.str;
|
||||||
// Resolve the root's struct type and base.
|
|
||||||
let rootstruct: str = "";
|
let rootstruct: str = "";
|
||||||
let lc: *local = localfindnode(c, cur.str);
|
let lc: *local = localfindnode(c, cur.str);
|
||||||
let gsi: *structinfo = nil;
|
|
||||||
if (lc != nil) {
|
if (lc != nil) {
|
||||||
if (lc.tnode != nil) {
|
if (lc.tnode != nil) {
|
||||||
if (lc.tnode.kind == nkind.N_TNAME) {
|
if (lc.tnode.kind == nkind.N_TNAME) {
|
||||||
rootstruct = lc.tnode.str;
|
rootstruct = lc.tnode.str;
|
||||||
out.rootoff = lc.off: i64;
|
*outrootoff = lc.off: i64;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
if (rootstruct.len == 0) {
|
if (rootstruct.len == 0) {
|
||||||
gsi = letvarstructinfo(c, cur.str);
|
let gsi: *structinfo = letvarstructinfo(c, cur.str);
|
||||||
if (gsi != nil) {
|
if (gsi != nil) {
|
||||||
rootstruct = gsi.sname;
|
rootstruct = gsi.sname;
|
||||||
out.isglobal = true;
|
*outisglobal = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
if (rootstruct.len == 0) { return false; };
|
if (rootstruct.len == 0) { return false; };
|
||||||
// Walk outward, resolving each field. stk is leaf-first; iterate
|
|
||||||
// from i = nsteps - 1 (the root-most field) down to i = 0 (leaf).
|
|
||||||
let curstruct: str = rootstruct;
|
let curstruct: str = rootstruct;
|
||||||
// Pre-declare per-iteration spills here so cstage / wwstage agree
|
|
||||||
// on the frame layout. Both must emit byte-identical asm for the
|
|
||||||
// bootstrap fixed-point (tests 993/995) — letting these locals get
|
|
||||||
// declared inside the branch bodies trips a per-stage divergence in
|
|
||||||
// slot counting.
|
|
||||||
let stepnm: str = "";
|
|
||||||
let fi: *fieldinfo = nil;
|
|
||||||
let found: *fieldinfo = nil;
|
|
||||||
let ft: *node = nil;
|
|
||||||
let s0nd: *node = nil;
|
|
||||||
let pseudo: str = "";
|
|
||||||
let delta: i64 = 0i64;
|
|
||||||
let i: i32 = nsteps - 1;
|
let i: i32 = nsteps - 1;
|
||||||
for (i >= 0) {
|
for (i >= 0) {
|
||||||
let csi: *structinfo = structlookup(c, curstruct);
|
let csi: *structinfo = structlookup(c, curstruct);
|
||||||
if (csi == nil) { return false; };
|
if (csi == nil) { return false; };
|
||||||
if (stk[i] == nil) { return false; };
|
if (stk[i] == nil) { return false; };
|
||||||
stepnm = stk[i].str;
|
let stepnm: str = stk[i].str;
|
||||||
fi = csi.fields;
|
let fi: *fieldinfo = csi.fields;
|
||||||
found = nil;
|
let found: *fieldinfo = nil;
|
||||||
for (fi != nil) {
|
for (fi != nil) {
|
||||||
if (streq(fi.fname, stepnm)) { found = fi; break; };
|
if (streq(fi.fname, stepnm)) { found = fi; break; };
|
||||||
fi = fi.finext;
|
fi = fi.finext;
|
||||||
};
|
};
|
||||||
if (found == nil) { return false; };
|
if (found == nil) { return false; };
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
out.totaloff = out.totaloff + (found.foff: i64);
|
*outtotaloff = *outtotaloff + (found.foff: i64);
|
||||||
out.leaffi = found;
|
*outleaffi = found;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
// Intermediate step. Must be a nested value-struct, OR a slice/
|
let ft: *node = found.tnode;
|
||||||
// str field with the leaf (i == 1, stk[0]) as a pseudo-field.
|
|
||||||
ft = found.tnode;
|
|
||||||
if (ft == nil) { return false; };
|
if (ft == nil) { return false; };
|
||||||
if (ft.kind == nkind.N_TNAME) {
|
if (ft.kind == nkind.N_TNAME) {
|
||||||
if (streq(ft.str, "str")) {
|
if (streq(ft.str, "str")) {
|
||||||
if (i != 1) { return false; };
|
if (i != 1) { return false; };
|
||||||
s0nd = stk[0];
|
let pseudo: str = stk[0].str;
|
||||||
if (s0nd == nil) { return false; };
|
let delta: i64 = -1i64;
|
||||||
pseudo = s0nd.str;
|
|
||||||
delta = -1i64;
|
|
||||||
if (streq(pseudo, "ptr")) { delta = 0i64; }
|
if (streq(pseudo, "ptr")) { delta = 0i64; }
|
||||||
else { if (streq(pseudo, "len")) { delta = 8i64; }; };
|
else { if (streq(pseudo, "len")) { delta = 8i64; }; };
|
||||||
if (delta < 0i64) { return false; };
|
if (delta < 0i64) { return false; };
|
||||||
out.totaloff = out.totaloff + (found.foff: i64);
|
*outtotaloff = *outtotaloff + (found.foff: i64);
|
||||||
out.slicedelta = delta;
|
*outslicedelta = delta;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
if (primsize(ft.str) != 0) { return false; };
|
if (primsize(ft.str) != 0) { return false; };
|
||||||
// Nested value-struct (named).
|
*outtotaloff = *outtotaloff + (found.foff: i64);
|
||||||
out.totaloff = out.totaloff + (found.foff: i64);
|
|
||||||
curstruct = ft.str;
|
curstruct = ft.str;
|
||||||
i -= 1;
|
i -= 1;
|
||||||
} else { if (ft.kind == nkind.N_TSLICE) {
|
} else { if (ft.kind == nkind.N_TSLICE) {
|
||||||
if (i != 1) { return false; };
|
if (i != 1) { return false; };
|
||||||
s0nd = stk[0];
|
let pseudo: str = stk[0].str;
|
||||||
if (s0nd == nil) { return false; };
|
let delta: i64 = -1i64;
|
||||||
pseudo = s0nd.str;
|
|
||||||
delta = -1i64;
|
|
||||||
if (streq(pseudo, "ptr")) { delta = 0i64; }
|
if (streq(pseudo, "ptr")) { delta = 0i64; }
|
||||||
else { if (streq(pseudo, "len")) { delta = 8i64; }
|
else { if (streq(pseudo, "len")) { delta = 8i64; }
|
||||||
else { if (streq(pseudo, "cap")) { delta = 16i64; }; }; };
|
else { if (streq(pseudo, "cap")) { delta = 16i64; }; }; };
|
||||||
if (delta < 0i64) { return false; };
|
if (delta < 0i64) { return false; };
|
||||||
out.totaloff = out.totaloff + (found.foff: i64);
|
*outtotaloff = *outtotaloff + (found.foff: i64);
|
||||||
out.slicedelta = delta;
|
*outslicedelta = delta;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@@ -9618,81 +9578,88 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
// Placed BEFORE the .ptr/.len fast paths so the chain wins.
|
// Placed BEFORE the .ptr/.len fast paths so the chain wins.
|
||||||
if (lhs != nil) {
|
if (lhs != nil) {
|
||||||
if (lhs.kind == nkind.N_DOT) {
|
if (lhs.kind == nkind.N_DOT) {
|
||||||
let r: dotchain;
|
let rootname: str = "";
|
||||||
let pok: bool = dotchainresolve(c, n, &r);
|
let rootoff: i64 = 0i64;
|
||||||
|
let totaloff: i64 = 0i64;
|
||||||
|
let leaffi: *fieldinfo = nil;
|
||||||
|
let slicedelta: i64 = -1i64;
|
||||||
|
let isglobal: bool = false;
|
||||||
|
let pok: bool = dotchainresolve(c, n,
|
||||||
|
&rootname, &rootoff, &totaloff,
|
||||||
|
&leaffi, &slicedelta, &isglobal);
|
||||||
if (pok) {
|
if (pok) {
|
||||||
if (r.slicedelta >= 0i64) {
|
if (slicedelta >= 0i64) {
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg(r.totaloff + r.slicedelta, "CX");
|
emitdispreg(totaloff + slicedelta, "CX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff(r.rootoff + r.totaloff + r.slicedelta);
|
emitoff(rootoff + totaloff + slicedelta);
|
||||||
emitline("(BP), AX\n");
|
emitline("(BP), AX\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (isstrtype(c, r.leaffi.tnode)) {
|
if (isstrtype(c, leaffi.tnode)) {
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg(r.totaloff + 0i64, "CX");
|
emitdispreg(totaloff, "CX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg(r.totaloff + 8i64, "CX");
|
emitdispreg(totaloff + 8i64, "CX");
|
||||||
emitline(", BX\n");
|
emitline(", BX\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff(r.rootoff + r.totaloff);
|
emitoff(rootoff + totaloff);
|
||||||
emitline("(BP), AX\n");
|
emitline("(BP), AX\n");
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff(r.rootoff + r.totaloff + 8i64);
|
emitoff(rootoff + totaloff + 8i64);
|
||||||
emitline("(BP), BX\n");
|
emitline("(BP), BX\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (isfloattype(c, r.leaffi.tnode)) {
|
if (isfloattype(c, leaffi.tnode)) {
|
||||||
let mov: str = "MOVSD";
|
let mov: str = "MOVSD";
|
||||||
if (isf32type(c, r.leaffi.tnode)) { mov = "MOVSS"; };
|
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(mov);
|
emitline(mov);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitdispreg(r.totaloff, "CX");
|
emitdispreg(totaloff, "CX");
|
||||||
emitline(", X0\n");
|
emitline(", X0\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(mov);
|
emitline(mov);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitoff(r.rootoff + r.totaloff);
|
emitoff(rootoff + totaloff);
|
||||||
emitline("(BP), X0\n");
|
emitline("(BP), X0\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let lop: str = fieldloadop(c, r.leaffi);
|
let lop: str = fieldloadop(c, leaffi);
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(lop);
|
emitline(lop);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitdispreg(r.totaloff, "CX");
|
emitdispreg(totaloff, "CX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(lop);
|
emitline(lop);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitoff(r.rootoff + r.totaloff);
|
emitoff(rootoff + totaloff);
|
||||||
emitline("(BP), AX\n");
|
emitline("(BP), AX\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
@@ -9968,21 +9935,28 @@ fn cgun(c: *cgen, n: *node) void = {
|
|||||||
// nsteps ≥ 2 (matches the read path's gate).
|
// nsteps ≥ 2 (matches the read path's gate).
|
||||||
if (opnd.lhs != nil) {
|
if (opnd.lhs != nil) {
|
||||||
if (opnd.lhs.kind == nkind.N_DOT) {
|
if (opnd.lhs.kind == nkind.N_DOT) {
|
||||||
let r: dotchain;
|
let rootname: str = "";
|
||||||
let pok: bool = dotchainresolve(c, opnd, &r);
|
let rootoff: i64 = 0i64;
|
||||||
|
let totaloff: i64 = 0i64;
|
||||||
|
let leaffi: *fieldinfo = nil;
|
||||||
|
let slicedelta: i64 = -1i64;
|
||||||
|
let isglobal: bool = false;
|
||||||
|
let pok: bool = dotchainresolve(c, opnd,
|
||||||
|
&rootname, &rootoff, &totaloff,
|
||||||
|
&leaffi, &slicedelta, &isglobal);
|
||||||
if (pok) {
|
if (pok) {
|
||||||
let extra: i64 = 0i64;
|
let extra: i64 = 0i64;
|
||||||
if (r.slicedelta >= 0i64) { extra = r.slicedelta; };
|
if (slicedelta >= 0i64) { extra = slicedelta; };
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitdispreg(r.totaloff + extra, "CX");
|
emitdispreg(totaloff + extra, "CX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitoff(r.rootoff + r.totaloff + extra);
|
emitoff(rootoff + totaloff + extra);
|
||||||
emitline("(BP), AX\n");
|
emitline("(BP), AX\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
@@ -11913,85 +11887,92 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
if (lhs.kind == nkind.N_DOT && lhs.lhs != nil
|
if (lhs.kind == nkind.N_DOT && lhs.lhs != nil
|
||||||
&& lhs.lhs.kind == nkind.N_DOT
|
&& lhs.lhs.kind == nkind.N_DOT
|
||||||
&& n.op == tkind.TK_ASSIGN) {
|
&& n.op == tkind.TK_ASSIGN) {
|
||||||
let r: dotchain;
|
let rootname: str = "";
|
||||||
let yok: bool = dotchainresolve(c, lhs, &r);
|
let rootoff: i64 = 0i64;
|
||||||
|
let totaloff: i64 = 0i64;
|
||||||
|
let leaffi: *fieldinfo = nil;
|
||||||
|
let slicedelta: i64 = -1i64;
|
||||||
|
let isglobal: bool = false;
|
||||||
|
let yok: bool = dotchainresolve(c, lhs,
|
||||||
|
&rootname, &rootoff, &totaloff,
|
||||||
|
&leaffi, &slicedelta, &isglobal);
|
||||||
if (yok) {
|
if (yok) {
|
||||||
if (r.slicedelta >= 0i64) {
|
if (slicedelta >= 0i64) {
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\tMOVQ\tAX, ");
|
emitline("\tMOVQ\tAX, ");
|
||||||
emitdispreg(r.totaloff + r.slicedelta, "CX");
|
emitdispreg(totaloff + slicedelta, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\tMOVQ\tAX, ");
|
emitline("\tMOVQ\tAX, ");
|
||||||
emitoff(r.rootoff + r.totaloff + r.slicedelta);
|
emitoff(rootoff + totaloff + slicedelta);
|
||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (isstrtype(c, r.leaffi.tnode)) {
|
if (isstrtype(c, leaffi.tnode)) {
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\tMOVQ\tAX, ");
|
emitline("\tMOVQ\tAX, ");
|
||||||
emitdispreg(r.totaloff + 0i64, "CX");
|
emitdispreg(totaloff, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
emitline("\tMOVQ\tBX, ");
|
emitline("\tMOVQ\tBX, ");
|
||||||
emitdispreg(r.totaloff + 8i64, "CX");
|
emitdispreg(totaloff + 8i64, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\tMOVQ\tAX, ");
|
emitline("\tMOVQ\tAX, ");
|
||||||
emitoff(r.rootoff + r.totaloff);
|
emitoff(rootoff + totaloff);
|
||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
emitline("\tMOVQ\tBX, ");
|
emitline("\tMOVQ\tBX, ");
|
||||||
emitoff(r.rootoff + r.totaloff + 8i64);
|
emitoff(rootoff + totaloff + 8i64);
|
||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (isfloattype(c, r.leaffi.tnode)) {
|
if (isfloattype(c, leaffi.tnode)) {
|
||||||
let mov: str = "MOVSD";
|
let mov: str = "MOVSD";
|
||||||
if (isf32type(c, r.leaffi.tnode)) { mov = "MOVSS"; };
|
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(mov);
|
emitline(mov);
|
||||||
emitline("\tX0, ");
|
emitline("\tX0, ");
|
||||||
emitdispreg(r.totaloff, "CX");
|
emitdispreg(totaloff, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(mov);
|
emitline(mov);
|
||||||
emitline("\tX0, ");
|
emitline("\tX0, ");
|
||||||
emitoff(r.rootoff + r.totaloff);
|
emitoff(rootoff + totaloff);
|
||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let sop: str = fieldstoreop(c, r.leaffi);
|
let sop: str = fieldstoreop(c, leaffi);
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
emitdispreg(r.totaloff, "CX");
|
emitdispreg(totaloff, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
emitoff(r.rootoff + r.totaloff);
|
emitoff(rootoff + totaloff);
|
||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1575,81 +1575,88 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
// Placed BEFORE the .ptr/.len fast paths so the chain wins.
|
// Placed BEFORE the .ptr/.len fast paths so the chain wins.
|
||||||
if (lhs != nil) {
|
if (lhs != nil) {
|
||||||
if (lhs.kind == nkind.N_DOT) {
|
if (lhs.kind == nkind.N_DOT) {
|
||||||
let r: dotchain;
|
let rootname: str = "";
|
||||||
let pok: bool = dotchainresolve(c, n, &r);
|
let rootoff: i64 = 0i64;
|
||||||
|
let totaloff: i64 = 0i64;
|
||||||
|
let leaffi: *fieldinfo = nil;
|
||||||
|
let slicedelta: i64 = -1i64;
|
||||||
|
let isglobal: bool = false;
|
||||||
|
let pok: bool = dotchainresolve(c, n,
|
||||||
|
&rootname, &rootoff, &totaloff,
|
||||||
|
&leaffi, &slicedelta, &isglobal);
|
||||||
if (pok) {
|
if (pok) {
|
||||||
if (r.slicedelta >= 0i64) {
|
if (slicedelta >= 0i64) {
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg(r.totaloff + r.slicedelta, "CX");
|
emitdispreg(totaloff + slicedelta, "CX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff(r.rootoff + r.totaloff + r.slicedelta);
|
emitoff(rootoff + totaloff + slicedelta);
|
||||||
emitline("(BP), AX\n");
|
emitline("(BP), AX\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (isstrtype(c, r.leaffi.tnode)) {
|
if (isstrtype(c, leaffi.tnode)) {
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg(r.totaloff + 0i64, "CX");
|
emitdispreg(totaloff, "CX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg(r.totaloff + 8i64, "CX");
|
emitdispreg(totaloff + 8i64, "CX");
|
||||||
emitline(", BX\n");
|
emitline(", BX\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff(r.rootoff + r.totaloff);
|
emitoff(rootoff + totaloff);
|
||||||
emitline("(BP), AX\n");
|
emitline("(BP), AX\n");
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff(r.rootoff + r.totaloff + 8i64);
|
emitoff(rootoff + totaloff + 8i64);
|
||||||
emitline("(BP), BX\n");
|
emitline("(BP), BX\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (isfloattype(c, r.leaffi.tnode)) {
|
if (isfloattype(c, leaffi.tnode)) {
|
||||||
let mov: str = "MOVSD";
|
let mov: str = "MOVSD";
|
||||||
if (isf32type(c, r.leaffi.tnode)) { mov = "MOVSS"; };
|
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(mov);
|
emitline(mov);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitdispreg(r.totaloff, "CX");
|
emitdispreg(totaloff, "CX");
|
||||||
emitline(", X0\n");
|
emitline(", X0\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(mov);
|
emitline(mov);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitoff(r.rootoff + r.totaloff);
|
emitoff(rootoff + totaloff);
|
||||||
emitline("(BP), X0\n");
|
emitline("(BP), X0\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let lop: str = fieldloadop(c, r.leaffi);
|
let lop: str = fieldloadop(c, leaffi);
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(lop);
|
emitline(lop);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitdispreg(r.totaloff, "CX");
|
emitdispreg(totaloff, "CX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(lop);
|
emitline(lop);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitoff(r.rootoff + r.totaloff);
|
emitoff(rootoff + totaloff);
|
||||||
emitline("(BP), AX\n");
|
emitline("(BP), AX\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
@@ -1925,21 +1932,28 @@ fn cgun(c: *cgen, n: *node) void = {
|
|||||||
// nsteps ≥ 2 (matches the read path's gate).
|
// nsteps ≥ 2 (matches the read path's gate).
|
||||||
if (opnd.lhs != nil) {
|
if (opnd.lhs != nil) {
|
||||||
if (opnd.lhs.kind == nkind.N_DOT) {
|
if (opnd.lhs.kind == nkind.N_DOT) {
|
||||||
let r: dotchain;
|
let rootname: str = "";
|
||||||
let pok: bool = dotchainresolve(c, opnd, &r);
|
let rootoff: i64 = 0i64;
|
||||||
|
let totaloff: i64 = 0i64;
|
||||||
|
let leaffi: *fieldinfo = nil;
|
||||||
|
let slicedelta: i64 = -1i64;
|
||||||
|
let isglobal: bool = false;
|
||||||
|
let pok: bool = dotchainresolve(c, opnd,
|
||||||
|
&rootname, &rootoff, &totaloff,
|
||||||
|
&leaffi, &slicedelta, &isglobal);
|
||||||
if (pok) {
|
if (pok) {
|
||||||
let extra: i64 = 0i64;
|
let extra: i64 = 0i64;
|
||||||
if (r.slicedelta >= 0i64) { extra = r.slicedelta; };
|
if (slicedelta >= 0i64) { extra = slicedelta; };
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitdispreg(r.totaloff + extra, "CX");
|
emitdispreg(totaloff + extra, "CX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitoff(r.rootoff + r.totaloff + extra);
|
emitoff(rootoff + totaloff + extra);
|
||||||
emitline("(BP), AX\n");
|
emitline("(BP), AX\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
@@ -3870,85 +3884,92 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
if (lhs.kind == nkind.N_DOT && lhs.lhs != nil
|
if (lhs.kind == nkind.N_DOT && lhs.lhs != nil
|
||||||
&& lhs.lhs.kind == nkind.N_DOT
|
&& lhs.lhs.kind == nkind.N_DOT
|
||||||
&& n.op == tkind.TK_ASSIGN) {
|
&& n.op == tkind.TK_ASSIGN) {
|
||||||
let r: dotchain;
|
let rootname: str = "";
|
||||||
let yok: bool = dotchainresolve(c, lhs, &r);
|
let rootoff: i64 = 0i64;
|
||||||
|
let totaloff: i64 = 0i64;
|
||||||
|
let leaffi: *fieldinfo = nil;
|
||||||
|
let slicedelta: i64 = -1i64;
|
||||||
|
let isglobal: bool = false;
|
||||||
|
let yok: bool = dotchainresolve(c, lhs,
|
||||||
|
&rootname, &rootoff, &totaloff,
|
||||||
|
&leaffi, &slicedelta, &isglobal);
|
||||||
if (yok) {
|
if (yok) {
|
||||||
if (r.slicedelta >= 0i64) {
|
if (slicedelta >= 0i64) {
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\tMOVQ\tAX, ");
|
emitline("\tMOVQ\tAX, ");
|
||||||
emitdispreg(r.totaloff + r.slicedelta, "CX");
|
emitdispreg(totaloff + slicedelta, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\tMOVQ\tAX, ");
|
emitline("\tMOVQ\tAX, ");
|
||||||
emitoff(r.rootoff + r.totaloff + r.slicedelta);
|
emitoff(rootoff + totaloff + slicedelta);
|
||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (isstrtype(c, r.leaffi.tnode)) {
|
if (isstrtype(c, leaffi.tnode)) {
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\tMOVQ\tAX, ");
|
emitline("\tMOVQ\tAX, ");
|
||||||
emitdispreg(r.totaloff + 0i64, "CX");
|
emitdispreg(totaloff, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
emitline("\tMOVQ\tBX, ");
|
emitline("\tMOVQ\tBX, ");
|
||||||
emitdispreg(r.totaloff + 8i64, "CX");
|
emitdispreg(totaloff + 8i64, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\tMOVQ\tAX, ");
|
emitline("\tMOVQ\tAX, ");
|
||||||
emitoff(r.rootoff + r.totaloff);
|
emitoff(rootoff + totaloff);
|
||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
emitline("\tMOVQ\tBX, ");
|
emitline("\tMOVQ\tBX, ");
|
||||||
emitoff(r.rootoff + r.totaloff + 8i64);
|
emitoff(rootoff + totaloff + 8i64);
|
||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (isfloattype(c, r.leaffi.tnode)) {
|
if (isfloattype(c, leaffi.tnode)) {
|
||||||
let mov: str = "MOVSD";
|
let mov: str = "MOVSD";
|
||||||
if (isf32type(c, r.leaffi.tnode)) { mov = "MOVSS"; };
|
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(mov);
|
emitline(mov);
|
||||||
emitline("\tX0, ");
|
emitline("\tX0, ");
|
||||||
emitdispreg(r.totaloff, "CX");
|
emitdispreg(totaloff, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(mov);
|
emitline(mov);
|
||||||
emitline("\tX0, ");
|
emitline("\tX0, ");
|
||||||
emitoff(r.rootoff + r.totaloff);
|
emitoff(rootoff + totaloff);
|
||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let sop: str = fieldstoreop(c, r.leaffi);
|
let sop: str = fieldstoreop(c, leaffi);
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
emitdispreg(r.totaloff, "CX");
|
emitdispreg(totaloff, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
emitoff(r.rootoff + r.totaloff);
|
emitoff(rootoff + totaloff);
|
||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -2308,58 +2308,41 @@ fn cgwidentaggedstore(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz:
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// dotchain — packed result struct for dotchainresolve. Out-params are
|
|
||||||
// bundled to keep the helper at <= 6 register-passed args; wwstage's
|
|
||||||
// per-fn arg-frame computation over-allocates by 16 bytes for any
|
|
||||||
// function with > 6 args (task #7, a pre-existing quirk independent
|
|
||||||
// of this fix), which would silently break the bootstrap fixed-point
|
|
||||||
// gate (993 / 994 / 995).
|
|
||||||
//
|
|
||||||
// Numeric fields are all i64, not i32. wwstage zero-inits an i32
|
|
||||||
// local with MOVQ (8-byte store) but subsequent `out.totaloff = …`
|
|
||||||
// updates would emit MOVL (4-byte store), leaving the upper 4 bytes
|
|
||||||
// stale from the wider init. Keeping the out-params at i64 makes the
|
|
||||||
// init width and the update width agree, so the field reads back
|
|
||||||
// what was written across both stages.
|
|
||||||
type dotchain = struct {
|
|
||||||
rootname: str,
|
|
||||||
rootoff: i64,
|
|
||||||
totaloff: i64,
|
|
||||||
leaffi: *fieldinfo,
|
|
||||||
slicedelta: i64,
|
|
||||||
isglobal: bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Spine-walk a chained N_DOT (n) inward to a root ident, summing field
|
// Spine-walk a chained N_DOT (n) inward to a root ident, summing field
|
||||||
// offsets through value-struct intermediates. Optional slice/str leaf
|
// offsets through value-struct intermediates. Optional slice/str leaf
|
||||||
// pseudo-field (.ptr / .len / .cap) on the last segment is folded into
|
// pseudo-field (.ptr / .len / .cap) on the last segment is folded into
|
||||||
// `out.slicedelta` (0/8/16); otherwise out.leaffi is the leaf fieldinfo
|
// *outslicedelta (0/8/16); otherwise *outleaffi is the leaf fieldinfo
|
||||||
// and slicedelta stays -1. Returns true on success; on false the caller
|
// and *outslicedelta stays -1. Returns true on success; on false the
|
||||||
// falls through to other branches.
|
// caller falls through to other branches.
|
||||||
//
|
//
|
||||||
// Mirrors cmd/w6c/cgen.c's N_DOT chained walker; both stages must agree
|
// Mirrors cmd/w6c/cgen.c's N_DOT chained walker; both stages must agree
|
||||||
// on the same shapes so the bootstrap fixed-point holds. The chain
|
// on the same shapes so the bootstrap fixed-point holds. The chain
|
||||||
// depth is capped at 16 — deeper chains are vanishingly rare and fall
|
// depth is capped at 16 — deeper chains are vanishingly rare and fall
|
||||||
// through.
|
// through.
|
||||||
//
|
//
|
||||||
// On success the caller emits one load/store at root_base + out.totaloff
|
// On success the caller emits one load/store at root_base + *outtotaloff
|
||||||
// (+ slicedelta for pseudo leaf). Root resolves as: local frame slot
|
// (+ slicedelta for pseudo leaf). Root resolves as: local frame slot
|
||||||
// (out.rootoff != 0, isglobal false) or top-level let (isglobal true,
|
// (*outisglobal false, base = *outrootoff(BP)) or top-level let
|
||||||
// root accessed via LEAQ name(SB), CX).
|
// (*outisglobal true, base reached via LEAQ *outrootname(SB), CX).
|
||||||
export fn dotchainresolve(c: *cgen, n: *node, out: *dotchain) bool = {
|
//
|
||||||
out.rootname = "";
|
// Numeric out-params are i64 so the deref-stores stay 8-byte (MOVQ).
|
||||||
out.rootoff = 0i64;
|
// `*p: *i32 = v` writes only 4 bytes via MOVL, leaving the caller's
|
||||||
out.isglobal = false;
|
// 8-byte slot's upper half stale from its zero-init — and a later MOVQ
|
||||||
out.totaloff = 0i64;
|
// read sees the zero-extended low half, so a negative i32 root offset
|
||||||
out.leaffi = nil;
|
// would come back as a huge positive i64. Tracked as task #19; until
|
||||||
out.slicedelta = -1i64;
|
// it lands, callers cast to i32 at the assign sites.
|
||||||
|
export fn dotchainresolve(c: *cgen, n: *node,
|
||||||
|
outrootname: *str, outrootoff: *i64, outtotaloff: *i64,
|
||||||
|
outleaffi: **fieldinfo, outslicedelta: *i64,
|
||||||
|
outisglobal: *bool) bool = {
|
||||||
|
*outrootname = "";
|
||||||
|
*outrootoff = 0i64;
|
||||||
|
*outisglobal = false;
|
||||||
|
*outtotaloff = 0i64;
|
||||||
|
*outleaffi = nil;
|
||||||
|
*outslicedelta = -1i64;
|
||||||
if (n == nil) { return false; };
|
if (n == nil) { return false; };
|
||||||
if (n.kind != nkind.N_DOT) { return false; };
|
if (n.kind != nkind.N_DOT) { return false; };
|
||||||
// Walk inward, recording the N_DOT node at each step (leaf first).
|
|
||||||
// We hold *node pointers (8B each, slotsize-stable across stages)
|
|
||||||
// and read .str on demand — a [16]str array would mis-slot at
|
|
||||||
// wwstage where slotsize("str") returns 8, breaking the bootstrap
|
|
||||||
// fixed-point.
|
|
||||||
let stk: [16]*node;
|
let stk: [16]*node;
|
||||||
let nsteps: i32 = 0;
|
let nsteps: i32 = 0;
|
||||||
let cur: *node = n;
|
let cur: *node = n;
|
||||||
@@ -2373,95 +2356,72 @@ export fn dotchainresolve(c: *cgen, n: *node, out: *dotchain) bool = {
|
|||||||
if (nsteps < 2) { return false; };
|
if (nsteps < 2) { return false; };
|
||||||
if (cur == nil) { return false; };
|
if (cur == nil) { return false; };
|
||||||
if (cur.kind != nkind.N_IDENT) { return false; };
|
if (cur.kind != nkind.N_IDENT) { return false; };
|
||||||
out.rootname = cur.str;
|
*outrootname = cur.str;
|
||||||
// Resolve the root's struct type and base.
|
|
||||||
let rootstruct: str = "";
|
let rootstruct: str = "";
|
||||||
let lc: *local = localfindnode(c, cur.str);
|
let lc: *local = localfindnode(c, cur.str);
|
||||||
let gsi: *structinfo = nil;
|
|
||||||
if (lc != nil) {
|
if (lc != nil) {
|
||||||
if (lc.tnode != nil) {
|
if (lc.tnode != nil) {
|
||||||
if (lc.tnode.kind == nkind.N_TNAME) {
|
if (lc.tnode.kind == nkind.N_TNAME) {
|
||||||
rootstruct = lc.tnode.str;
|
rootstruct = lc.tnode.str;
|
||||||
out.rootoff = lc.off: i64;
|
*outrootoff = lc.off: i64;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
if (rootstruct.len == 0) {
|
if (rootstruct.len == 0) {
|
||||||
gsi = letvarstructinfo(c, cur.str);
|
let gsi: *structinfo = letvarstructinfo(c, cur.str);
|
||||||
if (gsi != nil) {
|
if (gsi != nil) {
|
||||||
rootstruct = gsi.sname;
|
rootstruct = gsi.sname;
|
||||||
out.isglobal = true;
|
*outisglobal = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
if (rootstruct.len == 0) { return false; };
|
if (rootstruct.len == 0) { return false; };
|
||||||
// Walk outward, resolving each field. stk is leaf-first; iterate
|
|
||||||
// from i = nsteps - 1 (the root-most field) down to i = 0 (leaf).
|
|
||||||
let curstruct: str = rootstruct;
|
let curstruct: str = rootstruct;
|
||||||
// Pre-declare per-iteration spills here so cstage / wwstage agree
|
|
||||||
// on the frame layout. Both must emit byte-identical asm for the
|
|
||||||
// bootstrap fixed-point (tests 993/995) — letting these locals get
|
|
||||||
// declared inside the branch bodies trips a per-stage divergence in
|
|
||||||
// slot counting.
|
|
||||||
let stepnm: str = "";
|
|
||||||
let fi: *fieldinfo = nil;
|
|
||||||
let found: *fieldinfo = nil;
|
|
||||||
let ft: *node = nil;
|
|
||||||
let s0nd: *node = nil;
|
|
||||||
let pseudo: str = "";
|
|
||||||
let delta: i64 = 0i64;
|
|
||||||
let i: i32 = nsteps - 1;
|
let i: i32 = nsteps - 1;
|
||||||
for (i >= 0) {
|
for (i >= 0) {
|
||||||
let csi: *structinfo = structlookup(c, curstruct);
|
let csi: *structinfo = structlookup(c, curstruct);
|
||||||
if (csi == nil) { return false; };
|
if (csi == nil) { return false; };
|
||||||
if (stk[i] == nil) { return false; };
|
if (stk[i] == nil) { return false; };
|
||||||
stepnm = stk[i].str;
|
let stepnm: str = stk[i].str;
|
||||||
fi = csi.fields;
|
let fi: *fieldinfo = csi.fields;
|
||||||
found = nil;
|
let found: *fieldinfo = nil;
|
||||||
for (fi != nil) {
|
for (fi != nil) {
|
||||||
if (streq(fi.fname, stepnm)) { found = fi; break; };
|
if (streq(fi.fname, stepnm)) { found = fi; break; };
|
||||||
fi = fi.finext;
|
fi = fi.finext;
|
||||||
};
|
};
|
||||||
if (found == nil) { return false; };
|
if (found == nil) { return false; };
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
out.totaloff = out.totaloff + (found.foff: i64);
|
*outtotaloff = *outtotaloff + (found.foff: i64);
|
||||||
out.leaffi = found;
|
*outleaffi = found;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
// Intermediate step. Must be a nested value-struct, OR a slice/
|
let ft: *node = found.tnode;
|
||||||
// str field with the leaf (i == 1, stk[0]) as a pseudo-field.
|
|
||||||
ft = found.tnode;
|
|
||||||
if (ft == nil) { return false; };
|
if (ft == nil) { return false; };
|
||||||
if (ft.kind == nkind.N_TNAME) {
|
if (ft.kind == nkind.N_TNAME) {
|
||||||
if (streq(ft.str, "str")) {
|
if (streq(ft.str, "str")) {
|
||||||
if (i != 1) { return false; };
|
if (i != 1) { return false; };
|
||||||
s0nd = stk[0];
|
let pseudo: str = stk[0].str;
|
||||||
if (s0nd == nil) { return false; };
|
let delta: i64 = -1i64;
|
||||||
pseudo = s0nd.str;
|
|
||||||
delta = -1i64;
|
|
||||||
if (streq(pseudo, "ptr")) { delta = 0i64; }
|
if (streq(pseudo, "ptr")) { delta = 0i64; }
|
||||||
else { if (streq(pseudo, "len")) { delta = 8i64; }; };
|
else { if (streq(pseudo, "len")) { delta = 8i64; }; };
|
||||||
if (delta < 0i64) { return false; };
|
if (delta < 0i64) { return false; };
|
||||||
out.totaloff = out.totaloff + (found.foff: i64);
|
*outtotaloff = *outtotaloff + (found.foff: i64);
|
||||||
out.slicedelta = delta;
|
*outslicedelta = delta;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
if (primsize(ft.str) != 0) { return false; };
|
if (primsize(ft.str) != 0) { return false; };
|
||||||
// Nested value-struct (named).
|
*outtotaloff = *outtotaloff + (found.foff: i64);
|
||||||
out.totaloff = out.totaloff + (found.foff: i64);
|
|
||||||
curstruct = ft.str;
|
curstruct = ft.str;
|
||||||
i -= 1;
|
i -= 1;
|
||||||
} else { if (ft.kind == nkind.N_TSLICE) {
|
} else { if (ft.kind == nkind.N_TSLICE) {
|
||||||
if (i != 1) { return false; };
|
if (i != 1) { return false; };
|
||||||
s0nd = stk[0];
|
let pseudo: str = stk[0].str;
|
||||||
if (s0nd == nil) { return false; };
|
let delta: i64 = -1i64;
|
||||||
pseudo = s0nd.str;
|
|
||||||
delta = -1i64;
|
|
||||||
if (streq(pseudo, "ptr")) { delta = 0i64; }
|
if (streq(pseudo, "ptr")) { delta = 0i64; }
|
||||||
else { if (streq(pseudo, "len")) { delta = 8i64; }
|
else { if (streq(pseudo, "len")) { delta = 8i64; }
|
||||||
else { if (streq(pseudo, "cap")) { delta = 16i64; }; }; };
|
else { if (streq(pseudo, "cap")) { delta = 16i64; }; }; };
|
||||||
if (delta < 0i64) { return false; };
|
if (delta < 0i64) { return false; };
|
||||||
out.totaloff = out.totaloff + (found.foff: i64);
|
*outtotaloff = *outtotaloff + (found.foff: i64);
|
||||||
out.slicedelta = delta;
|
*outslicedelta = delta;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -7878,58 +7878,41 @@ fn cgwidentaggedstore(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz:
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// dotchain — packed result struct for dotchainresolve. Out-params are
|
|
||||||
// bundled to keep the helper at <= 6 register-passed args; wwstage's
|
|
||||||
// per-fn arg-frame computation over-allocates by 16 bytes for any
|
|
||||||
// function with > 6 args (task #7, a pre-existing quirk independent
|
|
||||||
// of this fix), which would silently break the bootstrap fixed-point
|
|
||||||
// gate (993 / 994 / 995).
|
|
||||||
//
|
|
||||||
// Numeric fields are all i64, not i32. wwstage zero-inits an i32
|
|
||||||
// local with MOVQ (8-byte store) but subsequent `out.totaloff = …`
|
|
||||||
// updates would emit MOVL (4-byte store), leaving the upper 4 bytes
|
|
||||||
// stale from the wider init. Keeping the out-params at i64 makes the
|
|
||||||
// init width and the update width agree, so the field reads back
|
|
||||||
// what was written across both stages.
|
|
||||||
type dotchain = struct {
|
|
||||||
rootname: str,
|
|
||||||
rootoff: i64,
|
|
||||||
totaloff: i64,
|
|
||||||
leaffi: *fieldinfo,
|
|
||||||
slicedelta: i64,
|
|
||||||
isglobal: bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Spine-walk a chained N_DOT (n) inward to a root ident, summing field
|
// Spine-walk a chained N_DOT (n) inward to a root ident, summing field
|
||||||
// offsets through value-struct intermediates. Optional slice/str leaf
|
// offsets through value-struct intermediates. Optional slice/str leaf
|
||||||
// pseudo-field (.ptr / .len / .cap) on the last segment is folded into
|
// pseudo-field (.ptr / .len / .cap) on the last segment is folded into
|
||||||
// `out.slicedelta` (0/8/16); otherwise out.leaffi is the leaf fieldinfo
|
// *outslicedelta (0/8/16); otherwise *outleaffi is the leaf fieldinfo
|
||||||
// and slicedelta stays -1. Returns true on success; on false the caller
|
// and *outslicedelta stays -1. Returns true on success; on false the
|
||||||
// falls through to other branches.
|
// caller falls through to other branches.
|
||||||
//
|
//
|
||||||
// Mirrors cmd/w6c/cgen.c's N_DOT chained walker; both stages must agree
|
// Mirrors cmd/w6c/cgen.c's N_DOT chained walker; both stages must agree
|
||||||
// on the same shapes so the bootstrap fixed-point holds. The chain
|
// on the same shapes so the bootstrap fixed-point holds. The chain
|
||||||
// depth is capped at 16 — deeper chains are vanishingly rare and fall
|
// depth is capped at 16 — deeper chains are vanishingly rare and fall
|
||||||
// through.
|
// through.
|
||||||
//
|
//
|
||||||
// On success the caller emits one load/store at root_base + out.totaloff
|
// On success the caller emits one load/store at root_base + *outtotaloff
|
||||||
// (+ slicedelta for pseudo leaf). Root resolves as: local frame slot
|
// (+ slicedelta for pseudo leaf). Root resolves as: local frame slot
|
||||||
// (out.rootoff != 0, isglobal false) or top-level let (isglobal true,
|
// (*outisglobal false, base = *outrootoff(BP)) or top-level let
|
||||||
// root accessed via LEAQ name(SB), CX).
|
// (*outisglobal true, base reached via LEAQ *outrootname(SB), CX).
|
||||||
export fn dotchainresolve(c: *cgen, n: *node, out: *dotchain) bool = {
|
//
|
||||||
out.rootname = "";
|
// Numeric out-params are i64 so the deref-stores stay 8-byte (MOVQ).
|
||||||
out.rootoff = 0i64;
|
// `*p: *i32 = v` writes only 4 bytes via MOVL, leaving the caller's
|
||||||
out.isglobal = false;
|
// 8-byte slot's upper half stale from its zero-init — and a later MOVQ
|
||||||
out.totaloff = 0i64;
|
// read sees the zero-extended low half, so a negative i32 root offset
|
||||||
out.leaffi = nil;
|
// would come back as a huge positive i64. Tracked as task #19; until
|
||||||
out.slicedelta = -1i64;
|
// it lands, callers cast to i32 at the assign sites.
|
||||||
|
export fn dotchainresolve(c: *cgen, n: *node,
|
||||||
|
outrootname: *str, outrootoff: *i64, outtotaloff: *i64,
|
||||||
|
outleaffi: **fieldinfo, outslicedelta: *i64,
|
||||||
|
outisglobal: *bool) bool = {
|
||||||
|
*outrootname = "";
|
||||||
|
*outrootoff = 0i64;
|
||||||
|
*outisglobal = false;
|
||||||
|
*outtotaloff = 0i64;
|
||||||
|
*outleaffi = nil;
|
||||||
|
*outslicedelta = -1i64;
|
||||||
if (n == nil) { return false; };
|
if (n == nil) { return false; };
|
||||||
if (n.kind != nkind.N_DOT) { return false; };
|
if (n.kind != nkind.N_DOT) { return false; };
|
||||||
// Walk inward, recording the N_DOT node at each step (leaf first).
|
|
||||||
// We hold *node pointers (8B each, slotsize-stable across stages)
|
|
||||||
// and read .str on demand — a [16]str array would mis-slot at
|
|
||||||
// wwstage where slotsize("str") returns 8, breaking the bootstrap
|
|
||||||
// fixed-point.
|
|
||||||
let stk: [16]*node;
|
let stk: [16]*node;
|
||||||
let nsteps: i32 = 0;
|
let nsteps: i32 = 0;
|
||||||
let cur: *node = n;
|
let cur: *node = n;
|
||||||
@@ -7943,95 +7926,72 @@ export fn dotchainresolve(c: *cgen, n: *node, out: *dotchain) bool = {
|
|||||||
if (nsteps < 2) { return false; };
|
if (nsteps < 2) { return false; };
|
||||||
if (cur == nil) { return false; };
|
if (cur == nil) { return false; };
|
||||||
if (cur.kind != nkind.N_IDENT) { return false; };
|
if (cur.kind != nkind.N_IDENT) { return false; };
|
||||||
out.rootname = cur.str;
|
*outrootname = cur.str;
|
||||||
// Resolve the root's struct type and base.
|
|
||||||
let rootstruct: str = "";
|
let rootstruct: str = "";
|
||||||
let lc: *local = localfindnode(c, cur.str);
|
let lc: *local = localfindnode(c, cur.str);
|
||||||
let gsi: *structinfo = nil;
|
|
||||||
if (lc != nil) {
|
if (lc != nil) {
|
||||||
if (lc.tnode != nil) {
|
if (lc.tnode != nil) {
|
||||||
if (lc.tnode.kind == nkind.N_TNAME) {
|
if (lc.tnode.kind == nkind.N_TNAME) {
|
||||||
rootstruct = lc.tnode.str;
|
rootstruct = lc.tnode.str;
|
||||||
out.rootoff = lc.off: i64;
|
*outrootoff = lc.off: i64;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
if (rootstruct.len == 0) {
|
if (rootstruct.len == 0) {
|
||||||
gsi = letvarstructinfo(c, cur.str);
|
let gsi: *structinfo = letvarstructinfo(c, cur.str);
|
||||||
if (gsi != nil) {
|
if (gsi != nil) {
|
||||||
rootstruct = gsi.sname;
|
rootstruct = gsi.sname;
|
||||||
out.isglobal = true;
|
*outisglobal = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
if (rootstruct.len == 0) { return false; };
|
if (rootstruct.len == 0) { return false; };
|
||||||
// Walk outward, resolving each field. stk is leaf-first; iterate
|
|
||||||
// from i = nsteps - 1 (the root-most field) down to i = 0 (leaf).
|
|
||||||
let curstruct: str = rootstruct;
|
let curstruct: str = rootstruct;
|
||||||
// Pre-declare per-iteration spills here so cstage / wwstage agree
|
|
||||||
// on the frame layout. Both must emit byte-identical asm for the
|
|
||||||
// bootstrap fixed-point (tests 993/995) — letting these locals get
|
|
||||||
// declared inside the branch bodies trips a per-stage divergence in
|
|
||||||
// slot counting.
|
|
||||||
let stepnm: str = "";
|
|
||||||
let fi: *fieldinfo = nil;
|
|
||||||
let found: *fieldinfo = nil;
|
|
||||||
let ft: *node = nil;
|
|
||||||
let s0nd: *node = nil;
|
|
||||||
let pseudo: str = "";
|
|
||||||
let delta: i64 = 0i64;
|
|
||||||
let i: i32 = nsteps - 1;
|
let i: i32 = nsteps - 1;
|
||||||
for (i >= 0) {
|
for (i >= 0) {
|
||||||
let csi: *structinfo = structlookup(c, curstruct);
|
let csi: *structinfo = structlookup(c, curstruct);
|
||||||
if (csi == nil) { return false; };
|
if (csi == nil) { return false; };
|
||||||
if (stk[i] == nil) { return false; };
|
if (stk[i] == nil) { return false; };
|
||||||
stepnm = stk[i].str;
|
let stepnm: str = stk[i].str;
|
||||||
fi = csi.fields;
|
let fi: *fieldinfo = csi.fields;
|
||||||
found = nil;
|
let found: *fieldinfo = nil;
|
||||||
for (fi != nil) {
|
for (fi != nil) {
|
||||||
if (streq(fi.fname, stepnm)) { found = fi; break; };
|
if (streq(fi.fname, stepnm)) { found = fi; break; };
|
||||||
fi = fi.finext;
|
fi = fi.finext;
|
||||||
};
|
};
|
||||||
if (found == nil) { return false; };
|
if (found == nil) { return false; };
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
out.totaloff = out.totaloff + (found.foff: i64);
|
*outtotaloff = *outtotaloff + (found.foff: i64);
|
||||||
out.leaffi = found;
|
*outleaffi = found;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
// Intermediate step. Must be a nested value-struct, OR a slice/
|
let ft: *node = found.tnode;
|
||||||
// str field with the leaf (i == 1, stk[0]) as a pseudo-field.
|
|
||||||
ft = found.tnode;
|
|
||||||
if (ft == nil) { return false; };
|
if (ft == nil) { return false; };
|
||||||
if (ft.kind == nkind.N_TNAME) {
|
if (ft.kind == nkind.N_TNAME) {
|
||||||
if (streq(ft.str, "str")) {
|
if (streq(ft.str, "str")) {
|
||||||
if (i != 1) { return false; };
|
if (i != 1) { return false; };
|
||||||
s0nd = stk[0];
|
let pseudo: str = stk[0].str;
|
||||||
if (s0nd == nil) { return false; };
|
let delta: i64 = -1i64;
|
||||||
pseudo = s0nd.str;
|
|
||||||
delta = -1i64;
|
|
||||||
if (streq(pseudo, "ptr")) { delta = 0i64; }
|
if (streq(pseudo, "ptr")) { delta = 0i64; }
|
||||||
else { if (streq(pseudo, "len")) { delta = 8i64; }; };
|
else { if (streq(pseudo, "len")) { delta = 8i64; }; };
|
||||||
if (delta < 0i64) { return false; };
|
if (delta < 0i64) { return false; };
|
||||||
out.totaloff = out.totaloff + (found.foff: i64);
|
*outtotaloff = *outtotaloff + (found.foff: i64);
|
||||||
out.slicedelta = delta;
|
*outslicedelta = delta;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
if (primsize(ft.str) != 0) { return false; };
|
if (primsize(ft.str) != 0) { return false; };
|
||||||
// Nested value-struct (named).
|
*outtotaloff = *outtotaloff + (found.foff: i64);
|
||||||
out.totaloff = out.totaloff + (found.foff: i64);
|
|
||||||
curstruct = ft.str;
|
curstruct = ft.str;
|
||||||
i -= 1;
|
i -= 1;
|
||||||
} else { if (ft.kind == nkind.N_TSLICE) {
|
} else { if (ft.kind == nkind.N_TSLICE) {
|
||||||
if (i != 1) { return false; };
|
if (i != 1) { return false; };
|
||||||
s0nd = stk[0];
|
let pseudo: str = stk[0].str;
|
||||||
if (s0nd == nil) { return false; };
|
let delta: i64 = -1i64;
|
||||||
pseudo = s0nd.str;
|
|
||||||
delta = -1i64;
|
|
||||||
if (streq(pseudo, "ptr")) { delta = 0i64; }
|
if (streq(pseudo, "ptr")) { delta = 0i64; }
|
||||||
else { if (streq(pseudo, "len")) { delta = 8i64; }
|
else { if (streq(pseudo, "len")) { delta = 8i64; }
|
||||||
else { if (streq(pseudo, "cap")) { delta = 16i64; }; }; };
|
else { if (streq(pseudo, "cap")) { delta = 16i64; }; }; };
|
||||||
if (delta < 0i64) { return false; };
|
if (delta < 0i64) { return false; };
|
||||||
out.totaloff = out.totaloff + (found.foff: i64);
|
*outtotaloff = *outtotaloff + (found.foff: i64);
|
||||||
out.slicedelta = delta;
|
*outslicedelta = delta;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@@ -9618,81 +9578,88 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
// Placed BEFORE the .ptr/.len fast paths so the chain wins.
|
// Placed BEFORE the .ptr/.len fast paths so the chain wins.
|
||||||
if (lhs != nil) {
|
if (lhs != nil) {
|
||||||
if (lhs.kind == nkind.N_DOT) {
|
if (lhs.kind == nkind.N_DOT) {
|
||||||
let r: dotchain;
|
let rootname: str = "";
|
||||||
let pok: bool = dotchainresolve(c, n, &r);
|
let rootoff: i64 = 0i64;
|
||||||
|
let totaloff: i64 = 0i64;
|
||||||
|
let leaffi: *fieldinfo = nil;
|
||||||
|
let slicedelta: i64 = -1i64;
|
||||||
|
let isglobal: bool = false;
|
||||||
|
let pok: bool = dotchainresolve(c, n,
|
||||||
|
&rootname, &rootoff, &totaloff,
|
||||||
|
&leaffi, &slicedelta, &isglobal);
|
||||||
if (pok) {
|
if (pok) {
|
||||||
if (r.slicedelta >= 0i64) {
|
if (slicedelta >= 0i64) {
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg(r.totaloff + r.slicedelta, "CX");
|
emitdispreg(totaloff + slicedelta, "CX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff(r.rootoff + r.totaloff + r.slicedelta);
|
emitoff(rootoff + totaloff + slicedelta);
|
||||||
emitline("(BP), AX\n");
|
emitline("(BP), AX\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (isstrtype(c, r.leaffi.tnode)) {
|
if (isstrtype(c, leaffi.tnode)) {
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg(r.totaloff + 0i64, "CX");
|
emitdispreg(totaloff, "CX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg(r.totaloff + 8i64, "CX");
|
emitdispreg(totaloff + 8i64, "CX");
|
||||||
emitline(", BX\n");
|
emitline(", BX\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff(r.rootoff + r.totaloff);
|
emitoff(rootoff + totaloff);
|
||||||
emitline("(BP), AX\n");
|
emitline("(BP), AX\n");
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff(r.rootoff + r.totaloff + 8i64);
|
emitoff(rootoff + totaloff + 8i64);
|
||||||
emitline("(BP), BX\n");
|
emitline("(BP), BX\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (isfloattype(c, r.leaffi.tnode)) {
|
if (isfloattype(c, leaffi.tnode)) {
|
||||||
let mov: str = "MOVSD";
|
let mov: str = "MOVSD";
|
||||||
if (isf32type(c, r.leaffi.tnode)) { mov = "MOVSS"; };
|
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(mov);
|
emitline(mov);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitdispreg(r.totaloff, "CX");
|
emitdispreg(totaloff, "CX");
|
||||||
emitline(", X0\n");
|
emitline(", X0\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(mov);
|
emitline(mov);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitoff(r.rootoff + r.totaloff);
|
emitoff(rootoff + totaloff);
|
||||||
emitline("(BP), X0\n");
|
emitline("(BP), X0\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let lop: str = fieldloadop(c, r.leaffi);
|
let lop: str = fieldloadop(c, leaffi);
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(lop);
|
emitline(lop);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitdispreg(r.totaloff, "CX");
|
emitdispreg(totaloff, "CX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(lop);
|
emitline(lop);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitoff(r.rootoff + r.totaloff);
|
emitoff(rootoff + totaloff);
|
||||||
emitline("(BP), AX\n");
|
emitline("(BP), AX\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
@@ -9968,21 +9935,28 @@ fn cgun(c: *cgen, n: *node) void = {
|
|||||||
// nsteps ≥ 2 (matches the read path's gate).
|
// nsteps ≥ 2 (matches the read path's gate).
|
||||||
if (opnd.lhs != nil) {
|
if (opnd.lhs != nil) {
|
||||||
if (opnd.lhs.kind == nkind.N_DOT) {
|
if (opnd.lhs.kind == nkind.N_DOT) {
|
||||||
let r: dotchain;
|
let rootname: str = "";
|
||||||
let pok: bool = dotchainresolve(c, opnd, &r);
|
let rootoff: i64 = 0i64;
|
||||||
|
let totaloff: i64 = 0i64;
|
||||||
|
let leaffi: *fieldinfo = nil;
|
||||||
|
let slicedelta: i64 = -1i64;
|
||||||
|
let isglobal: bool = false;
|
||||||
|
let pok: bool = dotchainresolve(c, opnd,
|
||||||
|
&rootname, &rootoff, &totaloff,
|
||||||
|
&leaffi, &slicedelta, &isglobal);
|
||||||
if (pok) {
|
if (pok) {
|
||||||
let extra: i64 = 0i64;
|
let extra: i64 = 0i64;
|
||||||
if (r.slicedelta >= 0i64) { extra = r.slicedelta; };
|
if (slicedelta >= 0i64) { extra = slicedelta; };
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitdispreg(r.totaloff + extra, "CX");
|
emitdispreg(totaloff + extra, "CX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitoff(r.rootoff + r.totaloff + extra);
|
emitoff(rootoff + totaloff + extra);
|
||||||
emitline("(BP), AX\n");
|
emitline("(BP), AX\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
@@ -11913,85 +11887,92 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
if (lhs.kind == nkind.N_DOT && lhs.lhs != nil
|
if (lhs.kind == nkind.N_DOT && lhs.lhs != nil
|
||||||
&& lhs.lhs.kind == nkind.N_DOT
|
&& lhs.lhs.kind == nkind.N_DOT
|
||||||
&& n.op == tkind.TK_ASSIGN) {
|
&& n.op == tkind.TK_ASSIGN) {
|
||||||
let r: dotchain;
|
let rootname: str = "";
|
||||||
let yok: bool = dotchainresolve(c, lhs, &r);
|
let rootoff: i64 = 0i64;
|
||||||
|
let totaloff: i64 = 0i64;
|
||||||
|
let leaffi: *fieldinfo = nil;
|
||||||
|
let slicedelta: i64 = -1i64;
|
||||||
|
let isglobal: bool = false;
|
||||||
|
let yok: bool = dotchainresolve(c, lhs,
|
||||||
|
&rootname, &rootoff, &totaloff,
|
||||||
|
&leaffi, &slicedelta, &isglobal);
|
||||||
if (yok) {
|
if (yok) {
|
||||||
if (r.slicedelta >= 0i64) {
|
if (slicedelta >= 0i64) {
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\tMOVQ\tAX, ");
|
emitline("\tMOVQ\tAX, ");
|
||||||
emitdispreg(r.totaloff + r.slicedelta, "CX");
|
emitdispreg(totaloff + slicedelta, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\tMOVQ\tAX, ");
|
emitline("\tMOVQ\tAX, ");
|
||||||
emitoff(r.rootoff + r.totaloff + r.slicedelta);
|
emitoff(rootoff + totaloff + slicedelta);
|
||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (isstrtype(c, r.leaffi.tnode)) {
|
if (isstrtype(c, leaffi.tnode)) {
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\tMOVQ\tAX, ");
|
emitline("\tMOVQ\tAX, ");
|
||||||
emitdispreg(r.totaloff + 0i64, "CX");
|
emitdispreg(totaloff, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
emitline("\tMOVQ\tBX, ");
|
emitline("\tMOVQ\tBX, ");
|
||||||
emitdispreg(r.totaloff + 8i64, "CX");
|
emitdispreg(totaloff + 8i64, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\tMOVQ\tAX, ");
|
emitline("\tMOVQ\tAX, ");
|
||||||
emitoff(r.rootoff + r.totaloff);
|
emitoff(rootoff + totaloff);
|
||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
emitline("\tMOVQ\tBX, ");
|
emitline("\tMOVQ\tBX, ");
|
||||||
emitoff(r.rootoff + r.totaloff + 8i64);
|
emitoff(rootoff + totaloff + 8i64);
|
||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (isfloattype(c, r.leaffi.tnode)) {
|
if (isfloattype(c, leaffi.tnode)) {
|
||||||
let mov: str = "MOVSD";
|
let mov: str = "MOVSD";
|
||||||
if (isf32type(c, r.leaffi.tnode)) { mov = "MOVSS"; };
|
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(mov);
|
emitline(mov);
|
||||||
emitline("\tX0, ");
|
emitline("\tX0, ");
|
||||||
emitdispreg(r.totaloff, "CX");
|
emitdispreg(totaloff, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(mov);
|
emitline(mov);
|
||||||
emitline("\tX0, ");
|
emitline("\tX0, ");
|
||||||
emitoff(r.rootoff + r.totaloff);
|
emitoff(rootoff + totaloff);
|
||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let sop: str = fieldstoreop(c, r.leaffi);
|
let sop: str = fieldstoreop(c, leaffi);
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (r.isglobal) {
|
if (isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, rootname);
|
||||||
emitline("(SB), CX\n");
|
emitline("(SB), CX\n");
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
emitdispreg(r.totaloff, "CX");
|
emitdispreg(totaloff, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
} else {
|
} else {
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
emitoff(r.rootoff + r.totaloff);
|
emitoff(rootoff + totaloff);
|
||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -175,6 +175,27 @@ static const struct row rows[] = {
|
|||||||
" return 0;\n"
|
" return 0;\n"
|
||||||
"};\n",
|
"};\n",
|
||||||
42 },
|
42 },
|
||||||
|
/* Task #19 pin (widen-deref). Natural shape `fn f(p: *i32)`
|
||||||
|
* with `*p = -16i32` lowers to MOVL (4B store) into the
|
||||||
|
* caller's 8B slot. The caller's slot was zero-init via MOVQ,
|
||||||
|
* so the upper 4B stay zero; a later i64-widening read emits
|
||||||
|
* MOVQ (8B raw) and returns 0x00000000_FFFFFFF0 = 4294967280
|
||||||
|
* instead of -16. Surfaced inside dotchainresolve when
|
||||||
|
* worker-retire-dotchain tried i32 out-params; the helper now
|
||||||
|
* widens all numeric out-params to *i64 as a workaround until
|
||||||
|
* #19 lands. This row pins the WORKAROUND: out-param typed
|
||||||
|
* *i64, caller reads i64 → MOVQ store/load pair agree, -16
|
||||||
|
* round-trips intact. When #19 lands, add a sibling row that
|
||||||
|
* exercises the natural `*p: *i32` shape and expects -16. */
|
||||||
|
{ "i64_out_param_neg_widen_deref_workaround",
|
||||||
|
"fn setneg(p: *i64) void = { *p = -16i64; };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let x: i64 = 0i64;\n"
|
||||||
|
" setneg(&x);\n"
|
||||||
|
" if (x == -16i64) { return 42; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
};
|
};
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|||||||
Reference in New Issue
Block a user