cgen: str N_INDEX read -> 3-word {ptr,len,cap} -- Phase 2 F2 (both stages)
str element value read at N_INDEX dropped the cap word (2-word ptr,len load); str is 24B {ptr,len,cap} since 1140a59. A new named helper cgslicehdr (both stages) loads the full 3-word header and is called by the N_INDEX str-element sites, kind-gated type_isstr/elemisstr -- never size==24, since str and slice collide at 24B. The base-targeting word loads last (clobber-safe). cstage==wwstage byte-identical. The #9 typeassert leaf is split out to F2b (it needs a wwstage spill twin first).
test/wcc/932: table-driven runtime .cap-survives probe over both N_INDEX base forms and both drivers; verified fail-before/pass-after. NNN<950 mirrors the 928 precedent -- the fixtures are self-contained (/tmp, no imports), so rule-14's selfhost-sibling race does not apply.
main.combined.ww regenerated via the canonical make path (md5-stable) and committed alongside source, per the 1140a59 precedent.
This commit is contained in:
@@ -14527,6 +14527,22 @@ fn cgident(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
|
||||
// cgslicehdr — load the 24B slice/str header at base+0 into the
|
||||
// (AX=ptr, BX=len, CX=cap) triple. base holds the element address;
|
||||
// the load that targets base destroys it, so that word is emitted
|
||||
// LAST. Order otherwise mirrors the slice-field arm (len, cap, ptr).
|
||||
// Shared by the cgindex str-element arms (caller does the kind-gate)
|
||||
// and, later, the typeassert str-variant leaf (#9). c retained unused
|
||||
// for callsite symmetry with cstage cgslicehdr.
|
||||
fn cgslicehdr(c: *cgen, base: str) void = {
|
||||
if (!streq(base, "BX")) { emitmovqload(8i64, base, "BX"); };
|
||||
if (!streq(base, "CX")) { emitmovqload(16i64, base, "CX"); };
|
||||
if (!streq(base, "AX")) { emitmovqload(0i64, base, "AX"); };
|
||||
if (streq(base, "BX")) { emitmovqload(8i64, base, "BX"); };
|
||||
if (streq(base, "CX")) { emitmovqload(16i64, base, "CX"); };
|
||||
if (streq(base, "AX")) { emitmovqload(0i64, base, "AX"); };
|
||||
};
|
||||
|
||||
fn cgindex(c: *cgen, n: *node) void = {
|
||||
// Element-size-aware load: u8 → MOVZBQ, i32 → MOVSXD, u32 → MOVL,
|
||||
// str → (ptr, len) into (AX, BX), everything else → MOVQ. Fast
|
||||
@@ -14665,12 +14681,11 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
return;
|
||||
};
|
||||
// #43: str element-stride routes through primtypesize so the
|
||||
// 16-vs-24 dispatch tracks ty_str.size for #1.
|
||||
// str element: load the full (ptr, len, cap) header into
|
||||
// (AX, BX, CX) — str is 24B since #1, so cap must survive.
|
||||
// Kind-gate on isstrtype, never size==24. Base is BX.
|
||||
if (elemisstr) {
|
||||
emitline("\tMOVQ\t8(BX), CX\n");
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
emitline("\tMOVQ\tCX, BX\n");
|
||||
cgslicehdr(c, "BX");
|
||||
return;
|
||||
};
|
||||
let lop1: str = loadopsz(signed_elem, esz);
|
||||
@@ -14706,13 +14721,10 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
return;
|
||||
};
|
||||
// str element (16B today): load (ptr, len) into (AX, BX) so
|
||||
// the value flows through the str-rhs convention.
|
||||
// #43: route via primtypesize so the stride tracks #1.
|
||||
// str element: full (ptr, len, cap) header into (AX, BX, CX);
|
||||
// cap must survive (#1). Kind-gate, never size==24. Base BX.
|
||||
if (elemisstr) {
|
||||
emitline("\tMOVQ\t8(BX), CX\n");
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
emitline("\tMOVQ\tCX, BX\n");
|
||||
cgslicehdr(c, "BX");
|
||||
return;
|
||||
};
|
||||
let lop2: str = loadopsz(signed_elem, esz);
|
||||
@@ -14739,11 +14751,11 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
return;
|
||||
};
|
||||
// #43: str element-stride routes through primtypesize so the
|
||||
// 16-vs-24 dispatch tracks ty_str.size for #1.
|
||||
// str element via fallback base: full (ptr, len, cap) header
|
||||
// into (AX, BX, CX); cap must survive (#1). Kind-gate, never
|
||||
// size==24. Base AX.
|
||||
if (elemisstr) {
|
||||
emitline("\tMOVQ\t8(AX), BX\n");
|
||||
emitline("\tMOVQ\t(AX), AX\n");
|
||||
cgslicehdr(c, "AX");
|
||||
return;
|
||||
};
|
||||
let lop3: str = loadopsz(signed_elem, esz);
|
||||
@@ -22725,6 +22737,16 @@ fn emitdispreg(off: i64, reg: str) void = {
|
||||
emitline(")");
|
||||
};
|
||||
|
||||
// emitmovqload — `MOVQ off(base), dst`, the per-word unit of a
|
||||
// 3-word slice/str header load (cgslicehdr).
|
||||
fn emitmovqload(off: i64, base: str, dst: str) void = {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(off, base);
|
||||
emitline(", ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
};
|
||||
|
||||
// emitoff — print an integer offset, suppressing it entirely when 0.
|
||||
// Use before any emitline("(BP)...") or emitline("(SB)...") sequence.
|
||||
// Plan 9 cc convention: "(BP)" not "0(BP)".
|
||||
|
||||
@@ -747,6 +747,16 @@ fn emitdispreg(off: i64, reg: str) void = {
|
||||
emitline(")");
|
||||
};
|
||||
|
||||
// emitmovqload — `MOVQ off(base), dst`, the per-word unit of a
|
||||
// 3-word slice/str header load (cgslicehdr).
|
||||
fn emitmovqload(off: i64, base: str, dst: str) void = {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(off, base);
|
||||
emitline(", ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
};
|
||||
|
||||
// emitoff — print an integer offset, suppressing it entirely when 0.
|
||||
// Use before any emitline("(BP)...") or emitline("(SB)...") sequence.
|
||||
// Plan 9 cc convention: "(BP)" not "0(BP)".
|
||||
|
||||
@@ -699,6 +699,22 @@ fn cgident(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
|
||||
// cgslicehdr — load the 24B slice/str header at base+0 into the
|
||||
// (AX=ptr, BX=len, CX=cap) triple. base holds the element address;
|
||||
// the load that targets base destroys it, so that word is emitted
|
||||
// LAST. Order otherwise mirrors the slice-field arm (len, cap, ptr).
|
||||
// Shared by the cgindex str-element arms (caller does the kind-gate)
|
||||
// and, later, the typeassert str-variant leaf (#9). c retained unused
|
||||
// for callsite symmetry with cstage cgslicehdr.
|
||||
fn cgslicehdr(c: *cgen, base: str) void = {
|
||||
if (!streq(base, "BX")) { emitmovqload(8i64, base, "BX"); };
|
||||
if (!streq(base, "CX")) { emitmovqload(16i64, base, "CX"); };
|
||||
if (!streq(base, "AX")) { emitmovqload(0i64, base, "AX"); };
|
||||
if (streq(base, "BX")) { emitmovqload(8i64, base, "BX"); };
|
||||
if (streq(base, "CX")) { emitmovqload(16i64, base, "CX"); };
|
||||
if (streq(base, "AX")) { emitmovqload(0i64, base, "AX"); };
|
||||
};
|
||||
|
||||
fn cgindex(c: *cgen, n: *node) void = {
|
||||
// Element-size-aware load: u8 → MOVZBQ, i32 → MOVSXD, u32 → MOVL,
|
||||
// str → (ptr, len) into (AX, BX), everything else → MOVQ. Fast
|
||||
@@ -837,12 +853,11 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
return;
|
||||
};
|
||||
// #43: str element-stride routes through primtypesize so the
|
||||
// 16-vs-24 dispatch tracks ty_str.size for #1.
|
||||
// str element: load the full (ptr, len, cap) header into
|
||||
// (AX, BX, CX) — str is 24B since #1, so cap must survive.
|
||||
// Kind-gate on isstrtype, never size==24. Base is BX.
|
||||
if (elemisstr) {
|
||||
emitline("\tMOVQ\t8(BX), CX\n");
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
emitline("\tMOVQ\tCX, BX\n");
|
||||
cgslicehdr(c, "BX");
|
||||
return;
|
||||
};
|
||||
let lop1: str = loadopsz(signed_elem, esz);
|
||||
@@ -878,13 +893,10 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
return;
|
||||
};
|
||||
// str element (16B today): load (ptr, len) into (AX, BX) so
|
||||
// the value flows through the str-rhs convention.
|
||||
// #43: route via primtypesize so the stride tracks #1.
|
||||
// str element: full (ptr, len, cap) header into (AX, BX, CX);
|
||||
// cap must survive (#1). Kind-gate, never size==24. Base BX.
|
||||
if (elemisstr) {
|
||||
emitline("\tMOVQ\t8(BX), CX\n");
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
emitline("\tMOVQ\tCX, BX\n");
|
||||
cgslicehdr(c, "BX");
|
||||
return;
|
||||
};
|
||||
let lop2: str = loadopsz(signed_elem, esz);
|
||||
@@ -911,11 +923,11 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
return;
|
||||
};
|
||||
// #43: str element-stride routes through primtypesize so the
|
||||
// 16-vs-24 dispatch tracks ty_str.size for #1.
|
||||
// str element via fallback base: full (ptr, len, cap) header
|
||||
// into (AX, BX, CX); cap must survive (#1). Kind-gate, never
|
||||
// size==24. Base AX.
|
||||
if (elemisstr) {
|
||||
emitline("\tMOVQ\t8(AX), BX\n");
|
||||
emitline("\tMOVQ\t(AX), AX\n");
|
||||
cgslicehdr(c, "AX");
|
||||
return;
|
||||
};
|
||||
let lop3: str = loadopsz(signed_elem, esz);
|
||||
|
||||
@@ -14527,6 +14527,22 @@ fn cgident(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
|
||||
// cgslicehdr — load the 24B slice/str header at base+0 into the
|
||||
// (AX=ptr, BX=len, CX=cap) triple. base holds the element address;
|
||||
// the load that targets base destroys it, so that word is emitted
|
||||
// LAST. Order otherwise mirrors the slice-field arm (len, cap, ptr).
|
||||
// Shared by the cgindex str-element arms (caller does the kind-gate)
|
||||
// and, later, the typeassert str-variant leaf (#9). c retained unused
|
||||
// for callsite symmetry with cstage cgslicehdr.
|
||||
fn cgslicehdr(c: *cgen, base: str) void = {
|
||||
if (!streq(base, "BX")) { emitmovqload(8i64, base, "BX"); };
|
||||
if (!streq(base, "CX")) { emitmovqload(16i64, base, "CX"); };
|
||||
if (!streq(base, "AX")) { emitmovqload(0i64, base, "AX"); };
|
||||
if (streq(base, "BX")) { emitmovqload(8i64, base, "BX"); };
|
||||
if (streq(base, "CX")) { emitmovqload(16i64, base, "CX"); };
|
||||
if (streq(base, "AX")) { emitmovqload(0i64, base, "AX"); };
|
||||
};
|
||||
|
||||
fn cgindex(c: *cgen, n: *node) void = {
|
||||
// Element-size-aware load: u8 → MOVZBQ, i32 → MOVSXD, u32 → MOVL,
|
||||
// str → (ptr, len) into (AX, BX), everything else → MOVQ. Fast
|
||||
@@ -14665,12 +14681,11 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
return;
|
||||
};
|
||||
// #43: str element-stride routes through primtypesize so the
|
||||
// 16-vs-24 dispatch tracks ty_str.size for #1.
|
||||
// str element: load the full (ptr, len, cap) header into
|
||||
// (AX, BX, CX) — str is 24B since #1, so cap must survive.
|
||||
// Kind-gate on isstrtype, never size==24. Base is BX.
|
||||
if (elemisstr) {
|
||||
emitline("\tMOVQ\t8(BX), CX\n");
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
emitline("\tMOVQ\tCX, BX\n");
|
||||
cgslicehdr(c, "BX");
|
||||
return;
|
||||
};
|
||||
let lop1: str = loadopsz(signed_elem, esz);
|
||||
@@ -14706,13 +14721,10 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
return;
|
||||
};
|
||||
// str element (16B today): load (ptr, len) into (AX, BX) so
|
||||
// the value flows through the str-rhs convention.
|
||||
// #43: route via primtypesize so the stride tracks #1.
|
||||
// str element: full (ptr, len, cap) header into (AX, BX, CX);
|
||||
// cap must survive (#1). Kind-gate, never size==24. Base BX.
|
||||
if (elemisstr) {
|
||||
emitline("\tMOVQ\t8(BX), CX\n");
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
emitline("\tMOVQ\tCX, BX\n");
|
||||
cgslicehdr(c, "BX");
|
||||
return;
|
||||
};
|
||||
let lop2: str = loadopsz(signed_elem, esz);
|
||||
@@ -14739,11 +14751,11 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
return;
|
||||
};
|
||||
// #43: str element-stride routes through primtypesize so the
|
||||
// 16-vs-24 dispatch tracks ty_str.size for #1.
|
||||
// str element via fallback base: full (ptr, len, cap) header
|
||||
// into (AX, BX, CX); cap must survive (#1). Kind-gate, never
|
||||
// size==24. Base AX.
|
||||
if (elemisstr) {
|
||||
emitline("\tMOVQ\t8(AX), BX\n");
|
||||
emitline("\tMOVQ\t(AX), AX\n");
|
||||
cgslicehdr(c, "AX");
|
||||
return;
|
||||
};
|
||||
let lop3: str = loadopsz(signed_elem, esz);
|
||||
@@ -22725,6 +22737,16 @@ fn emitdispreg(off: i64, reg: str) void = {
|
||||
emitline(")");
|
||||
};
|
||||
|
||||
// emitmovqload — `MOVQ off(base), dst`, the per-word unit of a
|
||||
// 3-word slice/str header load (cgslicehdr).
|
||||
fn emitmovqload(off: i64, base: str, dst: str) void = {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(off, base);
|
||||
emitline(", ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
};
|
||||
|
||||
// emitoff — print an integer offset, suppressing it entirely when 0.
|
||||
// Use before any emitline("(BP)...") or emitline("(SB)...") sequence.
|
||||
// Plan 9 cc convention: "(BP)" not "0(BP)".
|
||||
|
||||
Reference in New Issue
Block a user