wcc/ww: per-unit prefix on _S_ strlit labels (#49)
Strlit labels were emitted as `_S_<n>` from a global counter with no per-unit prefix (cgen.c intern_strlit + wwstage internstrlit twin). Under separate compilation two str-bearing packages both emit `_S_0`.. -> w6l link collision. Prefix the label with the owning package PATH (`<module>._S_<n>`, matching mklabel's spelling). The prefix is c->cur_mod, set per-fn by cgfn and now per-decl by let_pre_intern (save/restore so the later emit passes, which read cur_mod for fn-ptr relocs, are unaffected). Pure function of the module path — NOT a build-nonce — so the self-host fixed-point holds across ww2/ww3/ww4. Both stages, symmetric. Transparent rename on the live combined path: the label is interned once and shared by every reference, so ref and def move in lockstep. cs==ww byte-id holds; the w6c/wwdump combined.ww embed wcc/cgen.ww and are regenerated. 746_strdef_inline: the strdef-inline sentinel pinned the bare `LEAQ\t_S_` shape; update to the module-prefixed form (alpha._S_ for the in-module def, beta._S_ for the use-site-interned cross-module inline). 989_m3sep_run: add the #49 LINK leg. The str sub-fixture (sleaf+smid) was keystone-only — never linked — precisely because the global counter made both emit `_S_0`. With the prefix, compile both `-c` separately, link (w6l) + run (sroot reads a distinguishing byte through each string's .ptr, so a collided label would corrupt the exit), both stages + cs==ww final exe.
This commit is contained in:
@@ -34,11 +34,11 @@
|
||||
* Pin: 2 rows. Row 1 (bare ident, single module) sentinel-flips the
|
||||
* cgenexpr.ww:555 deflookup-strlit-inline branch on wwstage — revert
|
||||
* the branch and row 1 fails (`MOVQ\talpha.MSG(SB)` appears instead
|
||||
* of `LEAQ\t_S_`). Row 2 (mod-qualified, two modules) sentinel-flips
|
||||
* the cgenexpr.ww:1730 cgdot deflookup-strlit-inline branch — revert
|
||||
* and row 2 fails the same way (`MOVQ\talpha.MSG(SB)` appears in
|
||||
* of `LEAQ\t<mod>._S_`). Row 2 (mod-qualified, two modules) sentinel-
|
||||
* flips the cgenexpr.ww:1730 cgdot deflookup-strlit-inline branch —
|
||||
* revert and row 2 fails the same way (`MOVQ\talpha.MSG(SB)` appears in
|
||||
* beta.b). Asserts the strlit-inline pair lands inside the right
|
||||
* TEXT sym (LEAQ\t_S_ + MOVQ\t$<strlit_len>,) with the bad_movq
|
||||
* TEXT sym (LEAQ\t<mod>._S_ + MOVQ\t$<strlit_len>,) with the bad_movq
|
||||
* anti-check on each stage plus cs-vs-ws byte-id per row.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
@@ -77,7 +77,9 @@ static const struct row rows[] = {
|
||||
"def MSG: str = \"strdef_inline_pin_aaaaaaaaaaaaaaa_43chr\";\n"
|
||||
"export fn afn() str = { return MSG; };\n"
|
||||
"export fn main() i32 = { return 0; };\n",
|
||||
"TEXT alpha.afn", "LEAQ\t_S_", "MOVQ\t$39,", "alpha.MSG(SB)" },
|
||||
/* #49: strlit labels carry the interning module's path prefix
|
||||
* (here alpha — MSG's def interns in alpha's compile). */
|
||||
"TEXT alpha.afn", "LEAQ\talpha._S_", "MOVQ\t$39,", "alpha.MSG(SB)" },
|
||||
{ "mod_qualified_strdef",
|
||||
"package alpha;\n"
|
||||
"def MSG: str = \"strdef_inline_pin_aaaaaaaaaaaaaaa_43chr\";\n"
|
||||
@@ -85,7 +87,9 @@ static const struct row rows[] = {
|
||||
"import alpha;\n"
|
||||
"export fn bfn() str = { return alpha.MSG; };\n"
|
||||
"export fn main() i32 = { return 0; };\n",
|
||||
"TEXT beta.bfn", "LEAQ\t_S_", "MOVQ\t$39,", "alpha.MSG(SB)" },
|
||||
/* #49: the inlined sdef interns at the USE site (beta.bfn), so the
|
||||
* strlit label carries beta's path prefix, not alpha's. */
|
||||
"TEXT beta.bfn", "LEAQ\tbeta._S_", "MOVQ\t$39,", "alpha.MSG(SB)" },
|
||||
};
|
||||
|
||||
static int
|
||||
|
||||
@@ -28,8 +28,10 @@
|
||||
* w6c_ww on the identical sep-unit), sep-path determinism (compile twice ->
|
||||
* identical), and the value-global guard (an imported `export let` must NOT
|
||||
* re-emit DATA — that would duplicate the dep's own definition -> link
|
||||
* collision). A keystone-only str sub-fixture (sleaf -> smid, never linked)
|
||||
* makes the strlit-intern guard non-vacuous — see its note below. End-to-end:
|
||||
* collision). A str sub-fixture (sleaf -> smid -> sroot) makes the strlit-
|
||||
* intern guard non-vacuous AND, post-#49 (per-unit `_S_` prefix), LINKS the
|
||||
* two str-bearing packages without a label collision — see its note below.
|
||||
* End-to-end:
|
||||
* compile each package's .o under `-c`, link with w6l, run -> exit code is the
|
||||
* real cross-boundary computation; the SAME with the wwstage tools (w6c_ww/
|
||||
* w6a_ww/w6l_ww) -> cs==ww at the final-exe level + behavioral identity.
|
||||
@@ -161,10 +163,11 @@ static const char *root_src =
|
||||
" return r;\n"
|
||||
"};\n";
|
||||
|
||||
/* Str-literal sub-fixture (sleaf -> smid), KEYSTONE-ONLY (per-package
|
||||
* bodies-vs-.wwi cmp; never linked, so it sidesteps the global-counter `_S_`
|
||||
* label collision that blocks cross-unit linking of str-bearing packages —
|
||||
* filed for M3-tail). Its job is to make the strlit-intern guard (let_pre_
|
||||
/* Str-literal sub-fixture (sleaf -> smid). Two roles: (1) the per-package
|
||||
* bodies-vs-.wwi keystone cmp, and (2) post-#49 the cross-unit LINK leg below
|
||||
* (the per-unit `_S_` prefix removed the global-counter label collision that
|
||||
* used to block linking str-bearing packages). Its job is to make the strlit-
|
||||
* intern guard (let_pre_
|
||||
* intern / letpreintern) non-vacuous: sleaf carries a top-level str `export
|
||||
* let` whose initializer interns a strlit in sleaf's OWN compile but is
|
||||
* stripped from sleaf's `.wwi`. With the guard live, a dependent must NOT re-
|
||||
@@ -175,12 +178,35 @@ static const char *root_src =
|
||||
static const char *sleaf_src =
|
||||
"package sleaf;\n"
|
||||
"export let banner: str = \"sleaf-banner\";\n"
|
||||
"export fn tag(a: i32) i32 = { return a + 1; };\n";
|
||||
"export fn tag(a: i32) i32 = { return a + 1; };\n"
|
||||
"export fn getbanner() str = { return banner; };\n";
|
||||
static const char *smid_src =
|
||||
"package smid;\n"
|
||||
"import sleaf;\n"
|
||||
"export let label: str = \"smid-label\";\n"
|
||||
"export fn use(a: i32) i32 = { return sleaf.tag(a); };\n";
|
||||
"export fn use(a: i32) i32 = { return sleaf.tag(a); };\n"
|
||||
"export fn getlabel() str = { return label; };\n";
|
||||
|
||||
/* #49 LINK leg: a root over the str sub-fixture. Two str-bearing packages
|
||||
* (sleaf, smid) compiled `-c` separately now LINK without an `_S_` label
|
||||
* collision — the M3-core str sub-fixture was keystone-only (never linked)
|
||||
* precisely because the global `_S_<n>` counter made both emit `_S_0`. With
|
||||
* the per-unit prefix (sleaf._S_0 / smid._S_0) the link is clean. The exit
|
||||
* reads a DISTINGUISHING byte THROUGH each string's `.ptr` (so a collided
|
||||
* `_S_0` resolving to the wrong package's bytes would corrupt the result):
|
||||
* getbanner()[0] = 's'(115) of "sleaf-banner"
|
||||
* getlabel()[4] = '-'(45) of "smid-label"
|
||||
* => 115 + 45 = 160. */
|
||||
#define EXPECT_SLINK 160
|
||||
static const char *sroot_src =
|
||||
"package main;\n"
|
||||
"import sleaf;\n"
|
||||
"import smid;\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let a: str = sleaf.getbanner();\n"
|
||||
" let b: str = smid.getlabel();\n"
|
||||
" return (a[0]: i32) + (b[4]: i32);\n"
|
||||
"};\n";
|
||||
|
||||
/* A package's sep-unit + bodies-unit composition. `name` is the dotted package
|
||||
* path; deps are spliced ahead under //ww:module <deppath>, then the target's
|
||||
@@ -211,10 +237,12 @@ main(void)
|
||||
snprintf(p, sizeof p, "%s/root.ww", td); if (write_file(p, root_src)) { fail++; goto out; }
|
||||
snprintf(p, sizeof p, "%s/sleaf.ww", td); if (write_file(p, sleaf_src)) { fail++; goto out; }
|
||||
snprintf(p, sizeof p, "%s/smid.ww", td); if (write_file(p, smid_src)) { fail++; goto out; }
|
||||
snprintf(p, sizeof p, "%s/sroot.ww", td); if (write_file(p, sroot_src)) { fail++; goto out; }
|
||||
|
||||
char leafww[1024], midww[1024], rootww[1024];
|
||||
char leafwwi[1024], midwwi[1024];
|
||||
char sleafww[1024], smidww[1024], sleafwwi[1024];
|
||||
char smidwwi[1024], srootww[1024];
|
||||
snprintf(leafww, sizeof leafww, "%s/leaf.ww", td);
|
||||
snprintf(midww, sizeof midww, "%s/mid.ww", td);
|
||||
snprintf(rootww, sizeof rootww, "%s/root.ww", td);
|
||||
@@ -223,6 +251,8 @@ main(void)
|
||||
snprintf(sleafww, sizeof sleafww, "%s/sleaf.ww", td);
|
||||
snprintf(smidww, sizeof smidww, "%s/smid.ww", td);
|
||||
snprintf(sleafwwi, sizeof sleafwwi, "%s/sleaf.wwi", td);
|
||||
snprintf(smidwwi, sizeof smidwwi, "%s/smid.wwi", td);
|
||||
snprintf(srootww, sizeof srootww, "%s/sroot.ww", td);
|
||||
|
||||
/* ---- produce dep .wwi COLD, in dependency order -------------------- */
|
||||
/* leaf has no deps: produce directly. */
|
||||
@@ -248,6 +278,20 @@ main(void)
|
||||
"timeout 180 %s/w6c -I %s -o /dev/null %s >/dev/null 2>&1",
|
||||
bin, sleafwwi, sleafww);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "m3sep FAIL: sleaf.wwi produce\n"); fail++; goto out; }
|
||||
/* smid deps sleaf: produce smid.wwi (smid primary, sleaf.wwi scope) for
|
||||
* the #49 link leg's sroot scope. */
|
||||
{
|
||||
char unit[1024]; snprintf(unit, sizeof unit, "%s/smid.prod.ww", td);
|
||||
FILE *u = fopen(unit, "wb");
|
||||
if (!u) { fail++; goto out; }
|
||||
if (append_section(u, "//ww:module sleaf", sleafwwi) ||
|
||||
append_section(u, "//ww:module-reset", smidww)) { fclose(u); fail++; goto out; }
|
||||
fclose(u);
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"timeout 180 %s/w6c -I %s -o /dev/null %s >/dev/null 2>&1",
|
||||
bin, smidwwi, unit);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "m3sep FAIL: smid.wwi produce\n"); fail++; goto out; }
|
||||
}
|
||||
|
||||
/* ---- per-package .s gate: keystone + cs==ww + determinism ---------- */
|
||||
/* compose mid's bodies-unit and sep-unit, then root's. */
|
||||
@@ -411,6 +455,93 @@ main(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- #49 LINK leg: two str-bearing packages link without _S_ clash --- */
|
||||
/* sleaf and smid each carry a top-level str global -> each emits a strlit
|
||||
* label. Under the OLD global `_S_<n>` counter both would emit `_S_0`, so
|
||||
* linking them was impossible (the M3-core str sub-fixture is keystone-
|
||||
* ONLY for exactly this reason). With the #49 per-unit prefix the labels
|
||||
* are sleaf._S_0 / smid._S_0 -> the link is clean. sroot reads a
|
||||
* distinguishing byte THROUGH each string's `.ptr`, so a collided label
|
||||
* resolving to the wrong package's bytes would corrupt the exit code. */
|
||||
{
|
||||
char smidsep[1024], srootsep[1024];
|
||||
snprintf(smidsep, sizeof smidsep, "%s/smid.sep.ww", td);
|
||||
snprintf(srootsep, sizeof srootsep, "%s/sroot.sep.ww", td);
|
||||
/* sroot imports sleaf + smid: scope = both .wwi, then sroot's body.
|
||||
* (smid.sep.ww was already composed by the keystone loop above.) */
|
||||
{
|
||||
FILE *u = fopen(srootsep, "wb");
|
||||
if (!u) { fail++; goto out; }
|
||||
if (append_section(u, "//ww:module sleaf", sleafwwi) ||
|
||||
append_section(u, "//ww:module smid", smidwwi) ||
|
||||
append_section(u, "//ww:module-reset", srootww)) { fclose(u); fail++; goto out; }
|
||||
fclose(u);
|
||||
}
|
||||
|
||||
struct { const char *tool_c, *tool_a, *tool_l; const char *tag; } tc[] = {
|
||||
{ "w6c", "w6a", "w6l", "cs" },
|
||||
{ "w6c_ww", "w6a_ww", "w6l_ww", "ww" },
|
||||
};
|
||||
char sexe[2][1024];
|
||||
for (int t = 0; t < 2; t++) {
|
||||
char ls[1024], lo[1024], ms[1024], mo[1024], rs[1024], ro[1024];
|
||||
snprintf(ls, sizeof ls, "%s/sleaf.%s.s", td, tc[t].tag);
|
||||
snprintf(lo, sizeof lo, "%s/sleaf.%s.o", td, tc[t].tag);
|
||||
snprintf(ms, sizeof ms, "%s/smid.%s.s", td, tc[t].tag);
|
||||
snprintf(mo, sizeof mo, "%s/smid.%s.o", td, tc[t].tag);
|
||||
snprintf(rs, sizeof rs, "%s/sroot.%s.s", td, tc[t].tag);
|
||||
snprintf(ro, sizeof ro, "%s/sroot.%s.o", td, tc[t].tag);
|
||||
snprintf(sexe[t], sizeof sexe[t], "%s/sprog.%s", td, tc[t].tag);
|
||||
|
||||
int ok = 1;
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/%s -c -o %s %s >/dev/null 2>&1 && timeout 180 %s/%s -o %s %s >/dev/null 2>&1",
|
||||
bin, tc[t].tool_c, ls, sleafww, bin, tc[t].tool_a, lo, ls);
|
||||
if (runwait(cmd) != 0) ok = 0;
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/%s -c -o %s %s >/dev/null 2>&1 && timeout 180 %s/%s -o %s %s >/dev/null 2>&1",
|
||||
bin, tc[t].tool_c, ms, smidsep, bin, tc[t].tool_a, mo, ms);
|
||||
if (runwait(cmd) != 0) ok = 0;
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/%s -c -o %s %s >/dev/null 2>&1 && timeout 180 %s/%s -o %s %s >/dev/null 2>&1",
|
||||
bin, tc[t].tool_c, rs, srootsep, bin, tc[t].tool_a, ro, rs);
|
||||
if (runwait(cmd) != 0) ok = 0;
|
||||
/* #49 structural proof (cstage .s once): each str package's strlit
|
||||
* label is MODULE-PREFIXED, never a shared bare `_S_0`. */
|
||||
if (t == 0) {
|
||||
char *sl = NULL, *sm = NULL; size_t nl = 0, nm = 0;
|
||||
if (slurp(ls, &sl, &nl) == 0 && slurp(ms, &sm, &nm) == 0) {
|
||||
if (strstr(sl, "sleaf._S_") == NULL || strstr(sm, "smid._S_") == NULL) {
|
||||
fprintf(stderr, "m3sep FAIL: #49 strlit label not module-prefixed\n");
|
||||
fail++;
|
||||
}
|
||||
if (strstr(sl, "DATA _S_0(SB)") != NULL || strstr(sm, "DATA _S_0(SB)") != NULL) {
|
||||
fprintf(stderr, "m3sep FAIL: #49 bare _S_0 survives (cross-unit link collision)\n");
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
free(sl); free(sm);
|
||||
}
|
||||
/* link sroot + smid + sleaf + runtime: clean iff labels are unique. */
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/%s -o %s %s %s %s %s/../lib/libwwrt.a >/dev/null 2>&1",
|
||||
bin, tc[t].tool_l, sexe[t], ro, mo, lo, bin);
|
||||
if (runwait(cmd) != 0) ok = 0;
|
||||
if (!ok) {
|
||||
fprintf(stderr, "m3sep FAIL: %s str-pkg sep build/link failed (#49)\n", tc[t].tag);
|
||||
fail++;
|
||||
sexe[t][0] = '\0';
|
||||
continue;
|
||||
}
|
||||
int rc = runwait(sexe[t]);
|
||||
if (rc != EXPECT_SLINK) {
|
||||
fprintf(stderr, "m3sep FAIL: %s str-link program exit=%d, expected %d "
|
||||
"(#49 _S_ collision corrupts the linked strings)\n", tc[t].tag, rc, EXPECT_SLINK);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
if (sexe[0][0] && sexe[1][0] && files_eq(sexe[0], sexe[1]) != 0) {
|
||||
fprintf(stderr, "m3sep FAIL: cs str-exe != ww str-exe (rule 10, #49)\n");
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
snprintf(cmd, sizeof cmd, "rm -rf %s", td);
|
||||
runwait(cmd);
|
||||
@@ -419,7 +550,8 @@ out:
|
||||
return 1;
|
||||
}
|
||||
printf("m3sep: synth leaf->mid->root — per-pkg bodies==.wwi (-c) + cs==ww "
|
||||
".s/exe + determinism + value-global guard + behavioral (exit %d)\n",
|
||||
".s/exe + determinism + value-global guard + behavioral (exit %d) + "
|
||||
"#49 str-pkg sep-LINK (sleaf+smid, exit 160)\n",
|
||||
EXPECT_EXIT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user