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:
123
cmd/w6c/cgen.c
123
cmd/w6c/cgen.c
@@ -5217,6 +5217,26 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
int store_op = fldstoreop(ft, fsz);
|
int store_op = fldstoreop(ft, fsz);
|
||||||
int foff = (int)f->offset;
|
int foff = (int)f->offset;
|
||||||
if (n->op == TK_ASSIGN) {
|
if (n->op == 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:
|
||||||
|
* `o.p.min = 8: size` left `is size`
|
||||||
|
* false). */
|
||||||
|
if (fu && fu->kind == TY_TAGGED) {
|
||||||
|
cgexpr(c, n->lhs->lhs, locals);
|
||||||
|
ins2(c, A_MOVQ, areg(D_AX),
|
||||||
|
areg(D_BX));
|
||||||
|
cg_widen_tagged_store(c,
|
||||||
|
&locals, fu, n->rhs,
|
||||||
|
D_BX, foff,
|
||||||
|
(int)fu->size);
|
||||||
|
break;
|
||||||
|
}
|
||||||
int c_isf32 = 0;
|
int c_isf32 = 0;
|
||||||
if (fld_isfloat(ft, &c_isf32)) {
|
if (fld_isfloat(ft, &c_isf32)) {
|
||||||
/* f64/f32 chained-store: cgexpr rhs
|
/* f64/f32 chained-store: cgexpr rhs
|
||||||
@@ -5501,6 +5521,39 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
int fsz = (int)(leaf_type
|
int fsz = (int)(leaf_type
|
||||||
? leaf_type->size : 8);
|
? leaf_type->size : 8);
|
||||||
int store_op = fldstoreop(leaf_type, fsz);
|
int store_op = fldstoreop(leaf_type, fsz);
|
||||||
|
/* tagged leaf (#38a): full slot rewrite via
|
||||||
|
* the shared widener — the single-dot
|
||||||
|
* tagged-field arm (after_dot_assign)
|
||||||
|
* verbatim. 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 (fu && fu->kind == TY_TAGGED) {
|
||||||
|
int tsz = (int)fu->size;
|
||||||
|
if (via_cx) {
|
||||||
|
if (ptr_root)
|
||||||
|
ins2(c, A_MOVQ,
|
||||||
|
amem(D_BP, base_disp),
|
||||||
|
areg(D_BX));
|
||||||
|
else
|
||||||
|
ins2(c, A_LEAQ,
|
||||||
|
masym(c, cur->str),
|
||||||
|
areg(D_BX));
|
||||||
|
cg_widen_tagged_store(c,
|
||||||
|
&locals, fu, n->rhs,
|
||||||
|
D_BX, total_off, tsz);
|
||||||
|
} else {
|
||||||
|
cg_widen_tagged_store(c,
|
||||||
|
&locals, fu, n->rhs,
|
||||||
|
D_BP,
|
||||||
|
base_disp + total_off,
|
||||||
|
tsz);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (fu && (fu->kind == TY_STR
|
if (fu && (fu->kind == TY_STR
|
||||||
|| fu->kind == TY_SLICE)) {
|
|| fu->kind == TY_SLICE)) {
|
||||||
/* str/slice: store ptr/len/cap. cgexpr
|
/* str/slice: store ptr/len/cap. cgexpr
|
||||||
@@ -10225,6 +10278,42 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
Type *fu = (leaf_type
|
Type *fu = (leaf_type
|
||||||
&& leaf_type->kind == TY_NAMED)
|
&& leaf_type->kind == TY_NAMED)
|
||||||
? leaf_type->under : leaf_type;
|
? leaf_type->under : leaf_type;
|
||||||
|
/* tagged leaf (#38a): load the box into
|
||||||
|
* the tagged cursor (AX=tag, DX=val0,
|
||||||
|
* R8=val2 before CX=val1 — base_reg may
|
||||||
|
* be CX), the single-dot tagged-field arm
|
||||||
|
* verbatim. Pre-#38a the fldloadop 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).
|
||||||
|
* >32B box: ADDRESS in AX (the #37
|
||||||
|
* cg_tagged_memread convention). */
|
||||||
|
if (fu && fu->kind == TY_TAGGED) {
|
||||||
|
int fo = base_disp + total_off;
|
||||||
|
if ((int)fu->size
|
||||||
|
> TUPLE_GPCAP * 8) {
|
||||||
|
ins2(c, A_LEAQ,
|
||||||
|
amem(base_reg, fo),
|
||||||
|
areg(D_AX));
|
||||||
|
goto dot_done;
|
||||||
|
}
|
||||||
|
ins2(c, A_MOVQ,
|
||||||
|
amem(base_reg, fo + 0),
|
||||||
|
areg(D_AX));
|
||||||
|
ins2(c, A_MOVQ,
|
||||||
|
amem(base_reg, fo + 8),
|
||||||
|
areg(D_DX));
|
||||||
|
if (fu->size > 24)
|
||||||
|
ins2(c, A_MOVQ,
|
||||||
|
amem(base_reg, fo + 24),
|
||||||
|
areg(D_R8));
|
||||||
|
if (fu->size > 16)
|
||||||
|
ins2(c, A_MOVQ,
|
||||||
|
amem(base_reg, fo + 16),
|
||||||
|
areg(D_CX));
|
||||||
|
goto dot_done;
|
||||||
|
}
|
||||||
if (fu && fu->kind == TY_STR) {
|
if (fu && fu->kind == TY_STR) {
|
||||||
ins2(c, A_MOVQ,
|
ins2(c, A_MOVQ,
|
||||||
amem(base_reg,
|
amem(base_reg,
|
||||||
@@ -10729,6 +10818,40 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
Type *ft = f->type;
|
Type *ft = f->type;
|
||||||
Type *fu = (ft && ft->kind == TY_NAMED)
|
Type *fu = (ft && ft->kind == TY_NAMED)
|
||||||
? ft->under : ft;
|
? ft->under : ft;
|
||||||
|
/* tagged leaf (#38a): AX holds the *struct
|
||||||
|
* base and the tagged cursor targets AX
|
||||||
|
* (tag) — stage the base in BX, then the
|
||||||
|
* cursor load (AX=tag, DX=val0, R8=val2,
|
||||||
|
* CX=val1; >32B → ADDRESS in AX, #37). The
|
||||||
|
* fldloadop tail pulled ONE word (ken b8:
|
||||||
|
* o.p.min read stale DX as payload). */
|
||||||
|
if (fu && fu->kind == TY_TAGGED) {
|
||||||
|
int fo = (int)f->offset;
|
||||||
|
ins2(c, A_MOVQ, areg(D_AX),
|
||||||
|
areg(D_BX));
|
||||||
|
if ((int)fu->size
|
||||||
|
> TUPLE_GPCAP * 8) {
|
||||||
|
ins2(c, A_LEAQ,
|
||||||
|
amem(D_BX, fo),
|
||||||
|
areg(D_AX));
|
||||||
|
goto dot_done;
|
||||||
|
}
|
||||||
|
ins2(c, A_MOVQ,
|
||||||
|
amem(D_BX, fo + 0),
|
||||||
|
areg(D_AX));
|
||||||
|
ins2(c, A_MOVQ,
|
||||||
|
amem(D_BX, fo + 8),
|
||||||
|
areg(D_DX));
|
||||||
|
if (fu->size > 24)
|
||||||
|
ins2(c, A_MOVQ,
|
||||||
|
amem(D_BX, fo + 24),
|
||||||
|
areg(D_R8));
|
||||||
|
if (fu->size > 16)
|
||||||
|
ins2(c, A_MOVQ,
|
||||||
|
amem(D_BX, fo + 16),
|
||||||
|
areg(D_CX));
|
||||||
|
goto dot_done;
|
||||||
|
}
|
||||||
/* str IS []u8 — same 3-word {ptr,len,cap} as a
|
/* str IS []u8 — same 3-word {ptr,len,cap} as a
|
||||||
* slice field: load (ptr, len, cap) into
|
* slice field: load (ptr, len, cap) into
|
||||||
* (AX, BX, CX). AX is the *struct base, so
|
* (AX, BX, CX). AX is the *struct base, so
|
||||||
|
|||||||
@@ -24438,6 +24438,38 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
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 (typeisstr(leaftype)) {
|
||||||
if (viacx) {
|
if (viacx) {
|
||||||
if (ptrroot) {
|
if (ptrroot) {
|
||||||
@@ -24667,6 +24699,23 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
if (streq(tf.name, fld)) {
|
if (streq(tf.name, fld)) {
|
||||||
let ft: *tinfo = tf.type_;
|
let ft: *tinfo = tf.type_;
|
||||||
cgexpr(c, lhs); // AX = ptr to inner struct
|
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}
|
// str IS []u8 — same 3-word {ptr,len,cap}
|
||||||
// as a slice field: load (ptr, len, cap)
|
// as a slice field: load (ptr, len, cap)
|
||||||
// into (AX, BX, CX). AX is the *struct
|
// into (AX, BX, CX). AX is the *struct
|
||||||
@@ -30110,6 +30159,26 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
if (streq(tf.name, fld)) {
|
if (streq(tf.name, fld)) {
|
||||||
let ft: *tinfo = tf.type_;
|
let ft: *tinfo = tf.type_;
|
||||||
if (n.op == tkind.TK_ASSIGN) {
|
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)) {
|
if (typeisstr(ft) || typeisslice(ft)) {
|
||||||
// str/slice: rhs leaves AX=ptr,
|
// str/slice: rhs leaves AX=ptr,
|
||||||
// BX=len, CX=cap (#1/Phase 3). Spill
|
// BX=len, CX=cap (#1/Phase 3). Spill
|
||||||
@@ -30317,6 +30386,38 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
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)) {
|
if (typeisstr(leaftype) || typeisslice(leaftype)) {
|
||||||
// str/slice: store ptr/len/cap. cgexpr leaves
|
// str/slice: store ptr/len/cap. cgexpr leaves
|
||||||
// CX=cap, so the viacx base goes in DX (not CX) to
|
// CX=cap, so the viacx base goes in DX (not CX) to
|
||||||
|
|||||||
@@ -3594,6 +3594,38 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
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 (typeisstr(leaftype)) {
|
||||||
if (viacx) {
|
if (viacx) {
|
||||||
if (ptrroot) {
|
if (ptrroot) {
|
||||||
@@ -3823,6 +3855,23 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
if (streq(tf.name, fld)) {
|
if (streq(tf.name, fld)) {
|
||||||
let ft: *tinfo = tf.type_;
|
let ft: *tinfo = tf.type_;
|
||||||
cgexpr(c, lhs); // AX = ptr to inner struct
|
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}
|
// str IS []u8 — same 3-word {ptr,len,cap}
|
||||||
// as a slice field: load (ptr, len, cap)
|
// as a slice field: load (ptr, len, cap)
|
||||||
// into (AX, BX, CX). AX is the *struct
|
// into (AX, BX, CX). AX is the *struct
|
||||||
@@ -9266,6 +9315,26 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
if (streq(tf.name, fld)) {
|
if (streq(tf.name, fld)) {
|
||||||
let ft: *tinfo = tf.type_;
|
let ft: *tinfo = tf.type_;
|
||||||
if (n.op == tkind.TK_ASSIGN) {
|
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)) {
|
if (typeisstr(ft) || typeisslice(ft)) {
|
||||||
// str/slice: rhs leaves AX=ptr,
|
// str/slice: rhs leaves AX=ptr,
|
||||||
// BX=len, CX=cap (#1/Phase 3). Spill
|
// BX=len, CX=cap (#1/Phase 3). Spill
|
||||||
@@ -9473,6 +9542,38 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
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)) {
|
if (typeisstr(leaftype) || typeisslice(leaftype)) {
|
||||||
// str/slice: store ptr/len/cap. cgexpr leaves
|
// str/slice: store ptr/len/cap. cgexpr leaves
|
||||||
// CX=cap, so the viacx base goes in DX (not CX) to
|
// CX=cap, so the viacx base goes in DX (not CX) to
|
||||||
|
|||||||
@@ -24438,6 +24438,38 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
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 (typeisstr(leaftype)) {
|
||||||
if (viacx) {
|
if (viacx) {
|
||||||
if (ptrroot) {
|
if (ptrroot) {
|
||||||
@@ -24667,6 +24699,23 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
if (streq(tf.name, fld)) {
|
if (streq(tf.name, fld)) {
|
||||||
let ft: *tinfo = tf.type_;
|
let ft: *tinfo = tf.type_;
|
||||||
cgexpr(c, lhs); // AX = ptr to inner struct
|
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}
|
// str IS []u8 — same 3-word {ptr,len,cap}
|
||||||
// as a slice field: load (ptr, len, cap)
|
// as a slice field: load (ptr, len, cap)
|
||||||
// into (AX, BX, CX). AX is the *struct
|
// into (AX, BX, CX). AX is the *struct
|
||||||
@@ -30110,6 +30159,26 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
if (streq(tf.name, fld)) {
|
if (streq(tf.name, fld)) {
|
||||||
let ft: *tinfo = tf.type_;
|
let ft: *tinfo = tf.type_;
|
||||||
if (n.op == tkind.TK_ASSIGN) {
|
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)) {
|
if (typeisstr(ft) || typeisslice(ft)) {
|
||||||
// str/slice: rhs leaves AX=ptr,
|
// str/slice: rhs leaves AX=ptr,
|
||||||
// BX=len, CX=cap (#1/Phase 3). Spill
|
// BX=len, CX=cap (#1/Phase 3). Spill
|
||||||
@@ -30317,6 +30386,38 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
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)) {
|
if (typeisstr(leaftype) || typeisslice(leaftype)) {
|
||||||
// str/slice: store ptr/len/cap. cgexpr leaves
|
// str/slice: store ptr/len/cap. cgexpr leaves
|
||||||
// CX=cap, so the viacx base goes in DX (not CX) to
|
// CX=cap, so the viacx base goes in DX (not CX) to
|
||||||
|
|||||||
@@ -433,6 +433,102 @@ static const struct row rows[] = {
|
|||||||
* after void, arg widen, the bool-leading skip, and the signed
|
* after void, arg widen, the bool-leading skip, and the signed
|
||||||
* (void|i64) variant (ken hB3); the cast form rides alongside as
|
* (void|i64) variant (ken hB3); the cast form rides alongside as
|
||||||
* the no-drift control (it was always right). */
|
* the no-drift control (it was always right). */
|
||||||
|
/* #38a: chained-dot TAGGED leaf — the dot-spine walkers (value
|
||||||
|
* chain + ptr chain, both stages) had no TY_TAGGED leaf arm, so
|
||||||
|
* READS pulled one word (tag; as/match consumers read stale DX
|
||||||
|
* payload, ken x5c want-28-got-20) and ASSIGNS stored the rhs
|
||||||
|
* over the TAG slot (ken x5d). Six rows: literal-init read,
|
||||||
|
* assign-then-read, depth-3 chain read-before-write (ken hB1),
|
||||||
|
* (void|str) 32B box incl. void-over-str write (ken hB2),
|
||||||
|
* *outer root, mid-chain *rep. The INDEX-spine sibling
|
||||||
|
* (xs[i].min) is filed separately (task #58) — NOT a row here. */
|
||||||
|
{ "chained_tagged_literal_read",
|
||||||
|
"package main;\n"
|
||||||
|
"type rep = struct { id: size, min: (void | size), name: (void | str) };\n"
|
||||||
|
"type outer = struct { tag: size, r: rep };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let o = outer { tag = 4: size, r = rep { id = 6: size, min = 8: size, name = void } };\n"
|
||||||
|
" if (o.tag != 4) { return 1; };\n"
|
||||||
|
" if (o.r.id != 6) { return 2; };\n"
|
||||||
|
" if (!(o.r.min is size)) { return 3; };\n"
|
||||||
|
" if (o.r.min as size != 8) { return 4; };\n"
|
||||||
|
" if (!(o.r.name is void)) { return 5; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n", 0, 0 },
|
||||||
|
{ "chained_tagged_assign",
|
||||||
|
"package main;\n"
|
||||||
|
"type rep = struct { id: size, min: (void | size) };\n"
|
||||||
|
"type outer = struct { tag: size, r: rep };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let o: outer = outer { tag = 4: size, r = rep { id = 6: size, min = void } };\n"
|
||||||
|
" o.r.min = 8: size;\n"
|
||||||
|
" if (!(o.r.min is size)) { return 1; };\n"
|
||||||
|
" if (o.r.min as size != 8) { return 2; };\n"
|
||||||
|
" o.r.min = 9;\n"
|
||||||
|
" if (!(o.r.min is size)) { return 3; };\n"
|
||||||
|
" if (o.r.min as size != 9) { return 4; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n", 0, 0 },
|
||||||
|
{ "chained_tagged_depth2",
|
||||||
|
"package main;\n"
|
||||||
|
"type rep = struct { id: size, min: (void | size) };\n"
|
||||||
|
"type mid = struct { m: size, r: rep };\n"
|
||||||
|
"type outer = struct { tag: size, w: mid };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let o = outer { tag = 4: size, w = mid { m = 2: size, r = rep { id = 6: size, min = 5: size } } };\n"
|
||||||
|
" if (!(o.w.r.min is size)) { return 5; };\n"
|
||||||
|
" if (o.w.r.min as size != 5) { return 6; };\n"
|
||||||
|
" o.w.r.min = 8: size;\n"
|
||||||
|
" if (o.w.m != 2) { return 1; };\n"
|
||||||
|
" if (o.w.r.id != 6) { return 2; };\n"
|
||||||
|
" if (!(o.w.r.min is size)) { return 3; };\n"
|
||||||
|
" if (o.w.r.min as size != 8) { return 4; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n", 0, 0 },
|
||||||
|
{ "chained_tagged_strvariant",
|
||||||
|
"package main;\n"
|
||||||
|
"type rep = struct { id: size, nm: (void | str) };\n"
|
||||||
|
"type outer = struct { tag: size, r: rep };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let o = outer { tag = 4: size, r = rep { id = 6: size, nm = void } };\n"
|
||||||
|
" o.r.nm = \"hello\";\n"
|
||||||
|
" if (!(o.r.nm is str)) { return 1; };\n"
|
||||||
|
" if ((o.r.nm as str).len != 5) { return 2; };\n"
|
||||||
|
" match (o.r.nm) {\n"
|
||||||
|
" case let s: str => { if (s.len != 5) { return 3; }; };\n"
|
||||||
|
" case void => { return 4; };\n"
|
||||||
|
" };\n"
|
||||||
|
" o.r.nm = void;\n"
|
||||||
|
" if (!(o.r.nm is void)) { return 5; };\n"
|
||||||
|
" if (o.r.id != 6) { return 6; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n", 0, 0 },
|
||||||
|
{ "chained_tagged_ptrroot",
|
||||||
|
"package main;\n"
|
||||||
|
"type rep = struct { id: size, min: (void | size) };\n"
|
||||||
|
"type outer = struct { tag: size, r: rep };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let o = outer { tag = 4: size, r = rep { id = 6: size, min = void } };\n"
|
||||||
|
" let p: *outer = &o;\n"
|
||||||
|
" p.r.min = 8: size;\n"
|
||||||
|
" if (p.r.id != 6) { return 1; };\n"
|
||||||
|
" if (!(p.r.min is size)) { return 2; };\n"
|
||||||
|
" if (p.r.min as size != 8) { return 3; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n", 0, 0 },
|
||||||
|
{ "chained_tagged_ptrmid",
|
||||||
|
"package main;\n"
|
||||||
|
"type rep = struct { id: size, min: (void | size) };\n"
|
||||||
|
"type outer = struct { tag: size, p: *rep };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let r = rep { id = 6: size, min = void };\n"
|
||||||
|
" let o = outer { tag = 4: size, p = &r };\n"
|
||||||
|
" o.p.min = 8: size;\n"
|
||||||
|
" if (o.p.id != 6) { return 1; };\n"
|
||||||
|
" if (!(o.p.min is size)) { return 2; };\n"
|
||||||
|
" if (o.p.min as size != 8) { return 3; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n", 0, 0 },
|
||||||
{ "untyped_int_bare_widen",
|
{ "untyped_int_bare_widen",
|
||||||
"package main;\n"
|
"package main;\n"
|
||||||
"fn take(v: (void | size)) i32 = {\n"
|
"fn take(v: (void | size)) i32 = {\n"
|
||||||
|
|||||||
Reference in New Issue
Block a user