diff --git a/cmd/w6c/cgen.c b/cmd/w6c/cgen.c index b9a4923c..7e29af92 100644 --- a/cmd/w6c/cgen.c +++ b/cmd/w6c/cgen.c @@ -11099,25 +11099,20 @@ cgexpr(Cg *c, Node *n, Local *locals) areg(D_CX)); goto dot_done; } - if (fu && fu->kind == TY_STR) { - ins2(c, A_MOVQ, - amem(base_reg, - 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 + if (fu && (fu->kind == TY_STR + || fu->kind == TY_SLICE)) { + /* str/slice leaf: load all three header * words into (AX=ptr, BX=len, CX=cap) * so the value follows the canonical - * slice-rhs convention. base_reg may - * be CX (global / `*T` root); load - * .cap LAST so the base survives the - * earlier reads. */ + * slice-rhs convention. str IS []u8 — + * the same 24B {ptr,len,cap} header. #29: + * the str arm here used to load only + * 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, amem(base_reg, base_disp + total_off + 0), diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index d84a8bdf..799ef7ab 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -27502,40 +27502,17 @@ fn cgdot(c: *cgen, n: *node) void = { }; return; }; - if (typeisstr(leaftype)) { - 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"); - }; - 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 (typeisstr(leaftype) || typeisslice(leaftype)) { + // str/slice leaf: load all three header words into + // (AX=ptr, BX=len, CX=cap). str IS []u8 — the same 24B + // {ptr,len,cap} header. #29: the str leaf used to load + // only ptr+len here (cap dropped → a junk strlit before + // the chain left CX stale); merged into the slice arm so + // both load the full triple, both stages (#263). 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 do not alias so order is + // free. if (viacx) { if (ptrroot) { emitline("\tMOVQ\t"); diff --git a/selfhost/cmd/wcc/cgenexpr.ww b/selfhost/cmd/wcc/cgenexpr.ww index 1d9970d3..d2876511 100644 --- a/selfhost/cmd/wcc/cgenexpr.ww +++ b/selfhost/cmd/wcc/cgenexpr.ww @@ -4351,40 +4351,17 @@ fn cgdot(c: *cgen, n: *node) void = { }; return; }; - if (typeisstr(leaftype)) { - 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"); - }; - 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 (typeisstr(leaftype) || typeisslice(leaftype)) { + // str/slice leaf: load all three header words into + // (AX=ptr, BX=len, CX=cap). str IS []u8 — the same 24B + // {ptr,len,cap} header. #29: the str leaf used to load + // only ptr+len here (cap dropped → a junk strlit before + // the chain left CX stale); merged into the slice arm so + // both load the full triple, both stages (#263). 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 do not alias so order is + // free. if (viacx) { if (ptrroot) { emitline("\tMOVQ\t"); diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 459e3a6a..c70e3eab 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -27502,40 +27502,17 @@ fn cgdot(c: *cgen, n: *node) void = { }; return; }; - if (typeisstr(leaftype)) { - 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"); - }; - 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 (typeisstr(leaftype) || typeisslice(leaftype)) { + // str/slice leaf: load all three header words into + // (AX=ptr, BX=len, CX=cap). str IS []u8 — the same 24B + // {ptr,len,cap} header. #29: the str leaf used to load + // only ptr+len here (cap dropped → a junk strlit before + // the chain left CX stale); merged into the slice arm so + // both load the full triple, both stages (#263). 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 do not alias so order is + // free. if (viacx) { if (ptrroot) { emitline("\tMOVQ\t"); diff --git a/test/wcc/949_f6_header_run.c b/test/wcc/949_f6_header_run.c index 975ed4d6..98a79df3 100644 --- a/test/wcc/949_f6_header_run.c +++ b/test/wcc/949_f6_header_run.c @@ -139,6 +139,36 @@ static const struct row rows[] = { " if (ys.len == 6) { return 0; };\n" " return 1;\n" "};\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