cgen: #38a chained-dot TAGGED leaf — cursor read + widen store, value-chain + ptr-chain, both stages
A tagged leaf at the end of a dot chain (depth >= 2) fell to the
one-word scalar tail in FOUR walkers — the value-chain read spine,
the value-chain assign walker, the chained-*struct read branch, and
the chained-*struct assign branch — in BOTH stages, byte-identical,
gate-blind:
READ: one MOVQ pulled the TAG word only; is-tests passed by
tag-luck while as/match/let consumers read stale DX/CX/R8
as payload (ken x5c: o.r.min as size added entry-zero DX —
want 28 got 20; ken b8 same through a mid-chain *rep).
ASSIGN: one sized store landed the rhs ON the tag slot, payload
kept its old bytes (ken x5d: `o.r.min = 8: size` made the
following is-test false).
The single-dot arms were already correct — each fix is that arm
verbatim at the chained site:
read: the tagged cursor (AX=tag, DX=val0, R8=val2 before
CX=val1 — the base may live in CX/AX; >32B box leaves its
ADDRESS in AX, the #37 mem-read convention). wwstage rides
the existing cgloadtaggedfield helper; the *struct-base
branches stage AX into BX first (cursor targets AX).
assign: full slot rewrite via the shared widener
(cg_widen_tagged_store / cgwidentaggedstore, base BX/BP),
plain `=` only — compound on tagged stays loud (#133).
The nested-LITERAL half of the old #38 filing dissolved: the
canonical fill's tagged-field widen delegation (#23, 413aafa) already
covers any depth — x5c's literal init emits a correct tag at HEAD;
only the chained READ/ASSIGN halves were live (probe-proven, asm at
/tmp/pB).
Probes converge byte-id and runtime-correct: x5c/d/e (28), depth-2
(b3/b9), (void|str) 32B box + match readback (b5), *outer root (b7),
mid-chain *rep (b8), non-tagged control (f38a). 938 rows chained_*
pin all six shapes. Residuals filed, not folded (rule 11): the
INDEX-spine sibling xs[i].min payload read (task #58); tagged-field
GLOBALS stay loud at DATA emit (#129 A.2), so the walkers'
global arms are mirror-only today.
Task #38.
This commit is contained in:
@@ -24438,6 +24438,38 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
// tagged leaf (#38a): load the box into the tagged
|
||||
// cursor via cgloadtaggedfield (AX=tag, DX=word0,
|
||||
// R8=word2 before CX=word1 — CX may be the base;
|
||||
// >32B box leaves its ADDRESS in AX, the #37
|
||||
// convention) — the single-dot tagged-field arm
|
||||
// verbatim. Pre-#38a the scalar loadopsz tail pulled
|
||||
// ONE word (the tag): is-tests passed by tag-luck
|
||||
// while as/match/let consumers read stale payload
|
||||
// registers (ken x5c: o.r.min as size added DX).
|
||||
if (typeistagged(leaftype)) {
|
||||
let tlu: *tinfo = leaftype;
|
||||
for (tlu != nil && tlu.kind == tykind.TY_NAMED) {
|
||||
tlu = tlu.under;
|
||||
};
|
||||
let ttsz: i32 = tlu.size: i32;
|
||||
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");
|
||||
};
|
||||
cgloadtaggedfield(c, "CX", totaloff, ttsz);
|
||||
} else {
|
||||
cgloadtaggedfield(c, "BP",
|
||||
rootoff + totaloff, ttsz);
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (typeisstr(leaftype)) {
|
||||
if (viacx) {
|
||||
if (ptrroot) {
|
||||
@@ -24667,6 +24699,23 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
if (streq(tf.name, fld)) {
|
||||
let ft: *tinfo = tf.type_;
|
||||
cgexpr(c, lhs); // AX = ptr to inner struct
|
||||
// tagged leaf (#38a): AX holds the
|
||||
// *struct base and the tagged cursor
|
||||
// targets AX (tag) — stage the base in
|
||||
// BX, then cgloadtaggedfield (cstage
|
||||
// chained-*struct twin; ken b8: the
|
||||
// scalar tail read stale DX as payload).
|
||||
if (typeistagged(ft)) {
|
||||
let plu: *tinfo = ft;
|
||||
for (plu != nil && plu.kind == tykind.TY_NAMED) {
|
||||
plu = plu.under;
|
||||
};
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
cgloadtaggedfield(c, "BX",
|
||||
tf.offset: i32,
|
||||
plu.size: i32);
|
||||
return;
|
||||
};
|
||||
// str IS []u8 — same 3-word {ptr,len,cap}
|
||||
// as a slice field: load (ptr, len, cap)
|
||||
// into (AX, BX, CX). AX is the *struct
|
||||
@@ -30110,6 +30159,26 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
if (streq(tf.name, fld)) {
|
||||
let ft: *tinfo = tf.type_;
|
||||
if (n.op == tkind.TK_ASSIGN) {
|
||||
// tagged leaf (#38a): eval the *struct
|
||||
// base into BX, then the shared widener
|
||||
// (it spills BX across its internal
|
||||
// cgexpr) — same base-then-widen order
|
||||
// as the single-dot via-ptr arm. The
|
||||
// scalar tail below stored ONE sized
|
||||
// word at the field offset: the rhs
|
||||
// landed in the TAG slot (ken b8).
|
||||
if (typeistagged(ft)) {
|
||||
let flu: *tinfo = ft;
|
||||
for (flu != nil && flu.kind == tykind.TY_NAMED) {
|
||||
flu = flu.under;
|
||||
};
|
||||
cgexpr(c, base);
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
cgwidentaggedstore(c, flu, n.rhs,
|
||||
"BX", tf.offset: i32,
|
||||
flu.size: i32);
|
||||
return;
|
||||
};
|
||||
if (typeisstr(ft) || typeisslice(ft)) {
|
||||
// str/slice: rhs leaves AX=ptr,
|
||||
// BX=len, CX=cap (#1/Phase 3). Spill
|
||||
@@ -30317,6 +30386,38 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
// tagged leaf (#38a): full slot rewrite via the shared
|
||||
// widener — the single-dot tagged-field arm verbatim
|
||||
// (cgwidentaggedstore spills the BX base itself). The
|
||||
// scalar tail below stored ONE sized word at the field
|
||||
// offset: the rhs landed in the TAG slot and the payload
|
||||
// kept its old bytes (ken x5d: `o.r.min = 8: size` left
|
||||
// `is size` false). Only plain `=` reaches this walker
|
||||
// (TK_ASSIGN gate above).
|
||||
if (typeistagged(leaftype)) {
|
||||
let wlu: *tinfo = leaftype;
|
||||
for (wlu != nil && wlu.kind == tykind.TY_NAMED) {
|
||||
wlu = wlu.under;
|
||||
};
|
||||
let wtsz: i32 = wlu.size: i32;
|
||||
if (viacx) {
|
||||
if (ptrroot) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(rootoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
cgwidentaggedstore(c, wlu, n.rhs,
|
||||
"BX", totaloff, wtsz);
|
||||
} else {
|
||||
cgwidentaggedstore(c, wlu, n.rhs,
|
||||
"BP", rootoff + totaloff, wtsz);
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (typeisstr(leaftype) || typeisslice(leaftype)) {
|
||||
// str/slice: store ptr/len/cap. cgexpr leaves
|
||||
// CX=cap, so the viacx base goes in DX (not CX) to
|
||||
|
||||
@@ -3594,6 +3594,38 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
// tagged leaf (#38a): load the box into the tagged
|
||||
// cursor via cgloadtaggedfield (AX=tag, DX=word0,
|
||||
// R8=word2 before CX=word1 — CX may be the base;
|
||||
// >32B box leaves its ADDRESS in AX, the #37
|
||||
// convention) — the single-dot tagged-field arm
|
||||
// verbatim. Pre-#38a the scalar loadopsz tail pulled
|
||||
// ONE word (the tag): is-tests passed by tag-luck
|
||||
// while as/match/let consumers read stale payload
|
||||
// registers (ken x5c: o.r.min as size added DX).
|
||||
if (typeistagged(leaftype)) {
|
||||
let tlu: *tinfo = leaftype;
|
||||
for (tlu != nil && tlu.kind == tykind.TY_NAMED) {
|
||||
tlu = tlu.under;
|
||||
};
|
||||
let ttsz: i32 = tlu.size: i32;
|
||||
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");
|
||||
};
|
||||
cgloadtaggedfield(c, "CX", totaloff, ttsz);
|
||||
} else {
|
||||
cgloadtaggedfield(c, "BP",
|
||||
rootoff + totaloff, ttsz);
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (typeisstr(leaftype)) {
|
||||
if (viacx) {
|
||||
if (ptrroot) {
|
||||
@@ -3823,6 +3855,23 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
if (streq(tf.name, fld)) {
|
||||
let ft: *tinfo = tf.type_;
|
||||
cgexpr(c, lhs); // AX = ptr to inner struct
|
||||
// tagged leaf (#38a): AX holds the
|
||||
// *struct base and the tagged cursor
|
||||
// targets AX (tag) — stage the base in
|
||||
// BX, then cgloadtaggedfield (cstage
|
||||
// chained-*struct twin; ken b8: the
|
||||
// scalar tail read stale DX as payload).
|
||||
if (typeistagged(ft)) {
|
||||
let plu: *tinfo = ft;
|
||||
for (plu != nil && plu.kind == tykind.TY_NAMED) {
|
||||
plu = plu.under;
|
||||
};
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
cgloadtaggedfield(c, "BX",
|
||||
tf.offset: i32,
|
||||
plu.size: i32);
|
||||
return;
|
||||
};
|
||||
// str IS []u8 — same 3-word {ptr,len,cap}
|
||||
// as a slice field: load (ptr, len, cap)
|
||||
// into (AX, BX, CX). AX is the *struct
|
||||
@@ -9266,6 +9315,26 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
if (streq(tf.name, fld)) {
|
||||
let ft: *tinfo = tf.type_;
|
||||
if (n.op == tkind.TK_ASSIGN) {
|
||||
// tagged leaf (#38a): eval the *struct
|
||||
// base into BX, then the shared widener
|
||||
// (it spills BX across its internal
|
||||
// cgexpr) — same base-then-widen order
|
||||
// as the single-dot via-ptr arm. The
|
||||
// scalar tail below stored ONE sized
|
||||
// word at the field offset: the rhs
|
||||
// landed in the TAG slot (ken b8).
|
||||
if (typeistagged(ft)) {
|
||||
let flu: *tinfo = ft;
|
||||
for (flu != nil && flu.kind == tykind.TY_NAMED) {
|
||||
flu = flu.under;
|
||||
};
|
||||
cgexpr(c, base);
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
cgwidentaggedstore(c, flu, n.rhs,
|
||||
"BX", tf.offset: i32,
|
||||
flu.size: i32);
|
||||
return;
|
||||
};
|
||||
if (typeisstr(ft) || typeisslice(ft)) {
|
||||
// str/slice: rhs leaves AX=ptr,
|
||||
// BX=len, CX=cap (#1/Phase 3). Spill
|
||||
@@ -9473,6 +9542,38 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
// tagged leaf (#38a): full slot rewrite via the shared
|
||||
// widener — the single-dot tagged-field arm verbatim
|
||||
// (cgwidentaggedstore spills the BX base itself). The
|
||||
// scalar tail below stored ONE sized word at the field
|
||||
// offset: the rhs landed in the TAG slot and the payload
|
||||
// kept its old bytes (ken x5d: `o.r.min = 8: size` left
|
||||
// `is size` false). Only plain `=` reaches this walker
|
||||
// (TK_ASSIGN gate above).
|
||||
if (typeistagged(leaftype)) {
|
||||
let wlu: *tinfo = leaftype;
|
||||
for (wlu != nil && wlu.kind == tykind.TY_NAMED) {
|
||||
wlu = wlu.under;
|
||||
};
|
||||
let wtsz: i32 = wlu.size: i32;
|
||||
if (viacx) {
|
||||
if (ptrroot) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(rootoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
cgwidentaggedstore(c, wlu, n.rhs,
|
||||
"BX", totaloff, wtsz);
|
||||
} else {
|
||||
cgwidentaggedstore(c, wlu, n.rhs,
|
||||
"BP", rootoff + totaloff, wtsz);
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (typeisstr(leaftype) || typeisslice(leaftype)) {
|
||||
// str/slice: store ptr/len/cap. cgexpr leaves
|
||||
// CX=cap, so the viacx base goes in DX (not CX) to
|
||||
|
||||
@@ -24438,6 +24438,38 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
// tagged leaf (#38a): load the box into the tagged
|
||||
// cursor via cgloadtaggedfield (AX=tag, DX=word0,
|
||||
// R8=word2 before CX=word1 — CX may be the base;
|
||||
// >32B box leaves its ADDRESS in AX, the #37
|
||||
// convention) — the single-dot tagged-field arm
|
||||
// verbatim. Pre-#38a the scalar loadopsz tail pulled
|
||||
// ONE word (the tag): is-tests passed by tag-luck
|
||||
// while as/match/let consumers read stale payload
|
||||
// registers (ken x5c: o.r.min as size added DX).
|
||||
if (typeistagged(leaftype)) {
|
||||
let tlu: *tinfo = leaftype;
|
||||
for (tlu != nil && tlu.kind == tykind.TY_NAMED) {
|
||||
tlu = tlu.under;
|
||||
};
|
||||
let ttsz: i32 = tlu.size: i32;
|
||||
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");
|
||||
};
|
||||
cgloadtaggedfield(c, "CX", totaloff, ttsz);
|
||||
} else {
|
||||
cgloadtaggedfield(c, "BP",
|
||||
rootoff + totaloff, ttsz);
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (typeisstr(leaftype)) {
|
||||
if (viacx) {
|
||||
if (ptrroot) {
|
||||
@@ -24667,6 +24699,23 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
if (streq(tf.name, fld)) {
|
||||
let ft: *tinfo = tf.type_;
|
||||
cgexpr(c, lhs); // AX = ptr to inner struct
|
||||
// tagged leaf (#38a): AX holds the
|
||||
// *struct base and the tagged cursor
|
||||
// targets AX (tag) — stage the base in
|
||||
// BX, then cgloadtaggedfield (cstage
|
||||
// chained-*struct twin; ken b8: the
|
||||
// scalar tail read stale DX as payload).
|
||||
if (typeistagged(ft)) {
|
||||
let plu: *tinfo = ft;
|
||||
for (plu != nil && plu.kind == tykind.TY_NAMED) {
|
||||
plu = plu.under;
|
||||
};
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
cgloadtaggedfield(c, "BX",
|
||||
tf.offset: i32,
|
||||
plu.size: i32);
|
||||
return;
|
||||
};
|
||||
// str IS []u8 — same 3-word {ptr,len,cap}
|
||||
// as a slice field: load (ptr, len, cap)
|
||||
// into (AX, BX, CX). AX is the *struct
|
||||
@@ -30110,6 +30159,26 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
if (streq(tf.name, fld)) {
|
||||
let ft: *tinfo = tf.type_;
|
||||
if (n.op == tkind.TK_ASSIGN) {
|
||||
// tagged leaf (#38a): eval the *struct
|
||||
// base into BX, then the shared widener
|
||||
// (it spills BX across its internal
|
||||
// cgexpr) — same base-then-widen order
|
||||
// as the single-dot via-ptr arm. The
|
||||
// scalar tail below stored ONE sized
|
||||
// word at the field offset: the rhs
|
||||
// landed in the TAG slot (ken b8).
|
||||
if (typeistagged(ft)) {
|
||||
let flu: *tinfo = ft;
|
||||
for (flu != nil && flu.kind == tykind.TY_NAMED) {
|
||||
flu = flu.under;
|
||||
};
|
||||
cgexpr(c, base);
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
cgwidentaggedstore(c, flu, n.rhs,
|
||||
"BX", tf.offset: i32,
|
||||
flu.size: i32);
|
||||
return;
|
||||
};
|
||||
if (typeisstr(ft) || typeisslice(ft)) {
|
||||
// str/slice: rhs leaves AX=ptr,
|
||||
// BX=len, CX=cap (#1/Phase 3). Spill
|
||||
@@ -30317,6 +30386,38 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
// tagged leaf (#38a): full slot rewrite via the shared
|
||||
// widener — the single-dot tagged-field arm verbatim
|
||||
// (cgwidentaggedstore spills the BX base itself). The
|
||||
// scalar tail below stored ONE sized word at the field
|
||||
// offset: the rhs landed in the TAG slot and the payload
|
||||
// kept its old bytes (ken x5d: `o.r.min = 8: size` left
|
||||
// `is size` false). Only plain `=` reaches this walker
|
||||
// (TK_ASSIGN gate above).
|
||||
if (typeistagged(leaftype)) {
|
||||
let wlu: *tinfo = leaftype;
|
||||
for (wlu != nil && wlu.kind == tykind.TY_NAMED) {
|
||||
wlu = wlu.under;
|
||||
};
|
||||
let wtsz: i32 = wlu.size: i32;
|
||||
if (viacx) {
|
||||
if (ptrroot) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(rootoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
cgwidentaggedstore(c, wlu, n.rhs,
|
||||
"BX", totaloff, wtsz);
|
||||
} else {
|
||||
cgwidentaggedstore(c, wlu, n.rhs,
|
||||
"BP", rootoff + totaloff, wtsz);
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (typeisstr(leaftype) || typeisslice(leaftype)) {
|
||||
// str/slice: store ptr/len/cap. cgexpr leaves
|
||||
// CX=cap, so the viacx base goes in DX (not CX) to
|
||||
|
||||
Reference in New Issue
Block a user