cstage+selfhost+test: mangle fn labels by module (#9)

Both stages emitted fn TEXT labels by leaf only; lib/os and lib/io
exporting the same leaves (read, write, close) collided at link.
lib/fmt + lib/log worked around with @symbol("rt_syscall") stubs.

Drop d->export from the fn skip rule in mod_collect (both stages) so
exported fns mangle as <module>.<name>. Let/def/type keep current
behavior. Skip retained for {@symbol, main, empty-module}.

Add cur_mod thread through cgfn + mod_lookup_for_fn(name, hint) at
all 4 label-emit sites (TEXT def, LEAQ N_IDENT, CALL N_IDENT, CALL
N_DOT). Wwstage mirror: emitfnname + modlookupforfn + curmod.
Invariant comment pinned in both stages.

ww2 == ww3 == ww4 byte-identical at the new label format.
706_fnlabel_mangle covers same-leaf cross-module CALL + private-leaf
cur_mod disambiguation through a fn-pointer rvalue.

Wwstage LEAQ-of-fn N_DOT (`let p = mod.fn` rvalue) is a pre-existing
gap; deferred to a follow-up. fmt/log rt_syscall stubs untouched
here; cleanup follows.
This commit is contained in:
2026-05-15 23:41:01 +09:00
parent 98460e0220
commit f1440bf9e8
12 changed files with 602 additions and 136 deletions

View File

@@ -34,6 +34,13 @@ struct Cg {
Arena *a;
Prog *head, *tail;
const char *fnname;
const char *cur_mod; /* current fn's `// MODULE: foo` directive,
* NULL when the fn lives in the primary
* file (no MODULE: stamp). Drives bare-IDENT
* call mangling — `frob()` from within
* lib/foo binds to `foo.frob` regardless
* of which other modules also export `frob`.
* Set by cgfn before walking the body. */
int framesize; /* bytes of locals; 16-byte aligned */
int curoff; /* current top of locals */
Scope *locals; /* (name → offset) tracked via Sym */