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:
@@ -1087,7 +1087,18 @@ intern_strlit(Cg *c, const char *bytes, u64 len)
|
||||
if (s->len == len && memcmp(s->bytes, bytes, len) == 0)
|
||||
return s->label;
|
||||
Strlit *s = amalloc(c->a, sizeof *s);
|
||||
s->label = aprintf(c->a, "_S_%d", strlit_seq++);
|
||||
/* #49: per-unit prefix on the strlit label. Under separate
|
||||
* compilation two str-bearing packages both emit `_S_0`.. from this
|
||||
* global counter → w6l link collision. Prefix with the owning
|
||||
* package PATH (c->cur_mod — set per-fn by cgfn, per-decl by
|
||||
* let_pre_intern for static-data strings) so the label is unique per
|
||||
* compilation unit by construction. Pure function of the module path
|
||||
* (NOT a build-nonce) so the self-host fixed-point holds across
|
||||
* ww2/ww3/ww4. Same `<module>.` spelling as mklabel (cgen.c:1853). */
|
||||
s->label = aprintf(c->a, "%s%s_S_%d",
|
||||
c->cur_mod ? c->cur_mod : "",
|
||||
c->cur_mod ? "." : "",
|
||||
strlit_seq++);
|
||||
s->bytes = bytes;
|
||||
s->len = len;
|
||||
s->next = strlits;
|
||||
@@ -16670,7 +16681,15 @@ static void
|
||||
let_pre_intern(Cg *c, Node *file)
|
||||
{
|
||||
if (file == NULL) return;
|
||||
/* #49: strlit labels allocated here (static-data initialisers) must
|
||||
* carry the OWNING decl's module prefix, not the stale last-fn
|
||||
* cur_mod. Save/restore so emit_data/emit_defs/emit_lets — which read
|
||||
* cur_mod for fn-ptr relocs (node_fnptr_sym) — see the same value
|
||||
* they did before. let_pre_intern itself only interns, so driving
|
||||
* cur_mod here has no other effect. */
|
||||
const char *save_mod = c->cur_mod;
|
||||
for (Node *d = file->list; d; d = d->next) {
|
||||
c->cur_mod = (d->module && d->module[0]) ? d->module : NULL;
|
||||
/* #22 M3: skip imported deps so the strlit table (and its _S_
|
||||
* sequence) is a pure function of THIS package's own decls. A
|
||||
* dep's body initializer would intern here, but its `.wwi`
|
||||
@@ -16789,6 +16808,7 @@ let_pre_intern(Cg *c, Node *file)
|
||||
if (r->strlen == 0) continue;
|
||||
(void)intern_strlit(c, r->str, r->strlen);
|
||||
}
|
||||
c->cur_mod = save_mod;
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
Reference in New Issue
Block a user