wcc: chained-dot str leaf loads the cap word, both stages
A str field reached through a chained dot (o.i.s) emitted two loads (ptr, len) and stored a stale CX as the cap — both stages, at any non-zero chain depth. Emit the full header at the chained-dot leaf. Review item #29. Both stages move in one commit: one emission contract; splitting would leave the byte-id gates red between the halves.
This commit is contained in:
@@ -11099,25 +11099,20 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
areg(D_CX));
|
areg(D_CX));
|
||||||
goto dot_done;
|
goto dot_done;
|
||||||
}
|
}
|
||||||
if (fu && fu->kind == TY_STR) {
|
if (fu && (fu->kind == TY_STR
|
||||||
ins2(c, A_MOVQ,
|
|| fu->kind == TY_SLICE)) {
|
||||||
amem(base_reg,
|
/* str/slice leaf: load all three header
|
||||||
base_disp + total_off + 0),
|
|
||||||
areg(D_AX));
|
|
||||||
ins2(c, A_MOVQ,
|
|
||||||
amem(base_reg,
|
|
||||||
base_disp + total_off + 8),
|
|
||||||
areg(D_BX));
|
|
||||||
goto dot_done;
|
|
||||||
}
|
|
||||||
if (fu && fu->kind == TY_SLICE) {
|
|
||||||
/* Slice leaf: load all three header
|
|
||||||
* words into (AX=ptr, BX=len, CX=cap)
|
* words into (AX=ptr, BX=len, CX=cap)
|
||||||
* so the value follows the canonical
|
* so the value follows the canonical
|
||||||
* slice-rhs convention. base_reg may
|
* slice-rhs convention. str IS []u8 —
|
||||||
* be CX (global / `*T` root); load
|
* the same 24B {ptr,len,cap} header. #29:
|
||||||
* .cap LAST so the base survives the
|
* the str arm here used to load only
|
||||||
* earlier reads. */
|
* ptr+len (cap dropped → a junk strlit
|
||||||
|
* before the chain left CX stale); merged
|
||||||
|
* so both load all three, both stages
|
||||||
|
* (#263). base_reg may be CX (global /
|
||||||
|
* `*T` root); load .cap LAST so the base
|
||||||
|
* survives the earlier reads. */
|
||||||
ins2(c, A_MOVQ,
|
ins2(c, A_MOVQ,
|
||||||
amem(base_reg,
|
amem(base_reg,
|
||||||
base_disp + total_off + 0),
|
base_disp + total_off + 0),
|
||||||
|
|||||||
@@ -27502,40 +27502,17 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (typeisstr(leaftype)) {
|
if (typeisstr(leaftype) || typeisslice(leaftype)) {
|
||||||
if (viacx) {
|
// str/slice leaf: load all three header words into
|
||||||
if (ptrroot) {
|
// (AX=ptr, BX=len, CX=cap). str IS []u8 — the same 24B
|
||||||
emitline("\tMOVQ\t");
|
// {ptr,len,cap} header. #29: the str leaf used to load
|
||||||
emitoff(rootoff: i64);
|
// only ptr+len here (cap dropped → a junk strlit before
|
||||||
emitline("(BP), CX\n");
|
// the chain left CX stale); merged into the slice arm so
|
||||||
} else {
|
// both load the full triple, both stages (#263). For the
|
||||||
emitline("\tLEAQ\t");
|
// viacx path (global or `*T` root) CX is the base; load
|
||||||
emitsymname(c, rootname);
|
// .cap LAST so the base survives the earlier reads. For
|
||||||
emitline("(SB), CX\n");
|
// BP-rooted locals the registers do not alias so order is
|
||||||
};
|
// free.
|
||||||
emitline("\tMOVQ\t");
|
|
||||||
emitdispreg(totaloff: i64, "CX");
|
|
||||||
emitline(", AX\n");
|
|
||||||
emitline("\tMOVQ\t");
|
|
||||||
emitdispreg((totaloff + 8): i64, "CX");
|
|
||||||
emitline(", BX\n");
|
|
||||||
} else {
|
|
||||||
emitline("\tMOVQ\t");
|
|
||||||
emitoff((rootoff + totaloff): i64);
|
|
||||||
emitline("(BP), AX\n");
|
|
||||||
emitline("\tMOVQ\t");
|
|
||||||
emitoff((rootoff + totaloff + 8): i64);
|
|
||||||
emitline("(BP), BX\n");
|
|
||||||
};
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
if (typeisslice(leaftype)) {
|
|
||||||
// Slice leaf: load all three header words into
|
|
||||||
// (AX=ptr, BX=len, CX=cap). For the viacx path
|
|
||||||
// (global or `*T` root) CX is the base; load
|
|
||||||
// .cap LAST so the base survives the earlier
|
|
||||||
// reads. For BP-rooted locals the registers
|
|
||||||
// don't alias so order is free.
|
|
||||||
if (viacx) {
|
if (viacx) {
|
||||||
if (ptrroot) {
|
if (ptrroot) {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
|
|||||||
@@ -4351,40 +4351,17 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (typeisstr(leaftype)) {
|
if (typeisstr(leaftype) || typeisslice(leaftype)) {
|
||||||
if (viacx) {
|
// str/slice leaf: load all three header words into
|
||||||
if (ptrroot) {
|
// (AX=ptr, BX=len, CX=cap). str IS []u8 — the same 24B
|
||||||
emitline("\tMOVQ\t");
|
// {ptr,len,cap} header. #29: the str leaf used to load
|
||||||
emitoff(rootoff: i64);
|
// only ptr+len here (cap dropped → a junk strlit before
|
||||||
emitline("(BP), CX\n");
|
// the chain left CX stale); merged into the slice arm so
|
||||||
} else {
|
// both load the full triple, both stages (#263). For the
|
||||||
emitline("\tLEAQ\t");
|
// viacx path (global or `*T` root) CX is the base; load
|
||||||
emitsymname(c, rootname);
|
// .cap LAST so the base survives the earlier reads. For
|
||||||
emitline("(SB), CX\n");
|
// BP-rooted locals the registers do not alias so order is
|
||||||
};
|
// free.
|
||||||
emitline("\tMOVQ\t");
|
|
||||||
emitdispreg(totaloff: i64, "CX");
|
|
||||||
emitline(", AX\n");
|
|
||||||
emitline("\tMOVQ\t");
|
|
||||||
emitdispreg((totaloff + 8): i64, "CX");
|
|
||||||
emitline(", BX\n");
|
|
||||||
} else {
|
|
||||||
emitline("\tMOVQ\t");
|
|
||||||
emitoff((rootoff + totaloff): i64);
|
|
||||||
emitline("(BP), AX\n");
|
|
||||||
emitline("\tMOVQ\t");
|
|
||||||
emitoff((rootoff + totaloff + 8): i64);
|
|
||||||
emitline("(BP), BX\n");
|
|
||||||
};
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
if (typeisslice(leaftype)) {
|
|
||||||
// Slice leaf: load all three header words into
|
|
||||||
// (AX=ptr, BX=len, CX=cap). For the viacx path
|
|
||||||
// (global or `*T` root) CX is the base; load
|
|
||||||
// .cap LAST so the base survives the earlier
|
|
||||||
// reads. For BP-rooted locals the registers
|
|
||||||
// don't alias so order is free.
|
|
||||||
if (viacx) {
|
if (viacx) {
|
||||||
if (ptrroot) {
|
if (ptrroot) {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
|
|||||||
@@ -27502,40 +27502,17 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (typeisstr(leaftype)) {
|
if (typeisstr(leaftype) || typeisslice(leaftype)) {
|
||||||
if (viacx) {
|
// str/slice leaf: load all three header words into
|
||||||
if (ptrroot) {
|
// (AX=ptr, BX=len, CX=cap). str IS []u8 — the same 24B
|
||||||
emitline("\tMOVQ\t");
|
// {ptr,len,cap} header. #29: the str leaf used to load
|
||||||
emitoff(rootoff: i64);
|
// only ptr+len here (cap dropped → a junk strlit before
|
||||||
emitline("(BP), CX\n");
|
// the chain left CX stale); merged into the slice arm so
|
||||||
} else {
|
// both load the full triple, both stages (#263). For the
|
||||||
emitline("\tLEAQ\t");
|
// viacx path (global or `*T` root) CX is the base; load
|
||||||
emitsymname(c, rootname);
|
// .cap LAST so the base survives the earlier reads. For
|
||||||
emitline("(SB), CX\n");
|
// BP-rooted locals the registers do not alias so order is
|
||||||
};
|
// free.
|
||||||
emitline("\tMOVQ\t");
|
|
||||||
emitdispreg(totaloff: i64, "CX");
|
|
||||||
emitline(", AX\n");
|
|
||||||
emitline("\tMOVQ\t");
|
|
||||||
emitdispreg((totaloff + 8): i64, "CX");
|
|
||||||
emitline(", BX\n");
|
|
||||||
} else {
|
|
||||||
emitline("\tMOVQ\t");
|
|
||||||
emitoff((rootoff + totaloff): i64);
|
|
||||||
emitline("(BP), AX\n");
|
|
||||||
emitline("\tMOVQ\t");
|
|
||||||
emitoff((rootoff + totaloff + 8): i64);
|
|
||||||
emitline("(BP), BX\n");
|
|
||||||
};
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
if (typeisslice(leaftype)) {
|
|
||||||
// Slice leaf: load all three header words into
|
|
||||||
// (AX=ptr, BX=len, CX=cap). For the viacx path
|
|
||||||
// (global or `*T` root) CX is the base; load
|
|
||||||
// .cap LAST so the base survives the earlier
|
|
||||||
// reads. For BP-rooted locals the registers
|
|
||||||
// don't alias so order is free.
|
|
||||||
if (viacx) {
|
if (viacx) {
|
||||||
if (ptrroot) {
|
if (ptrroot) {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
|
|||||||
@@ -139,6 +139,36 @@ static const struct row rows[] = {
|
|||||||
" if (ys.len == 6) { return 0; };\n"
|
" if (ys.len == 6) { return 0; };\n"
|
||||||
" return 1;\n"
|
" return 1;\n"
|
||||||
"};\n", 0, K_RUN, NULL },
|
"};\n", 0, K_RUN, NULL },
|
||||||
|
/* #29 (#263 both-stages): chained-dot str leaf `o.i.s` (depth-2)
|
||||||
|
* dropped the cap word — a junk strlit before the chain left CX
|
||||||
|
* stale, so t.cap read 2 (the junk's cap) instead of 5. Now the
|
||||||
|
* str leaf loads all three header words like the slice arm. */
|
||||||
|
{ "chained_str_cap",
|
||||||
|
"package main;\n"
|
||||||
|
"type inner = struct { s: str, x: i64 };\n"
|
||||||
|
"type outer = struct { i: inner, y: i64 };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let iv: inner = inner { s = \"hello\", x = 0 };\n"
|
||||||
|
" let o: outer = outer { i = iv, y = 0 };\n"
|
||||||
|
" let junk: str = \"ab\";\n"
|
||||||
|
" let t: str = o.i.s;\n"
|
||||||
|
" return t.cap: i32;\n"
|
||||||
|
"};\n", 5, K_RUN, NULL },
|
||||||
|
/* #29 sibling: the str leaf sits at a NON-ZERO field offset within the
|
||||||
|
* inner struct (a leading i64 pad pushes `s` to +8, so totaloff != 0).
|
||||||
|
* Exercises the chained-leaf offset arithmetic the offset-0 row leaves
|
||||||
|
* untested. Junk strlit before the chain; cap == len == 7. */
|
||||||
|
{ "chained_str_cap_offset",
|
||||||
|
"package main;\n"
|
||||||
|
"type inner = struct { pad: i64, s: str };\n"
|
||||||
|
"type outer = struct { i: inner, y: i64 };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let iv: inner = inner { pad = 0, s = \"worldly\" };\n"
|
||||||
|
" let o: outer = outer { i = iv, y = 0 };\n"
|
||||||
|
" let junk: str = \"ab\";\n"
|
||||||
|
" let t: str = o.i.s;\n"
|
||||||
|
" return t.cap: i32;\n"
|
||||||
|
"};\n", 7, K_RUN, NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|||||||
Reference in New Issue
Block a user