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

@@ -528,13 +528,14 @@ fn cgident(c: *cgen, n: *node) void = {
};
// Fn-name used as a value (e.g. `let f = some_fn;` or
// `... = some_fn;`). LEAQ the symbol address into AX. The
// emitsymname helper handles ffiresolve and module-mangling
// emitfnname helper handles ffiresolve and module-mangling
// in one go, so a body-less FFI binding emits the C symbol
// it was declared with via @symbol(), not the ww-side ident.
// Bare ident → same-module by ww's resolver, hint with c.curmod.
let rt: *node = fnretlookup(c, nm);
if (rt != nil) {
emitline("\tLEAQ\t");
emitsymname(c, nm);
emitfnname(c, nm, c.curmod);
emitline("(SB), AX\n");
return;
};
@@ -3126,11 +3127,23 @@ fn cgcall(c: *cgen, n: *node) void = {
emitline("\tCALL\t");
if (callee != nil) {
if (callee.kind == nkind.N_IDENT) {
// Bare `f()` — same-module by ww's resolver,
// so c.curmod is the disambiguation hint.
calleename = callee.str;
emitsymname(c, calleename);
emitfnname(c, calleename, c.curmod);
} else { if (callee.kind == nkind.N_DOT) {
// `m.f()` — pass the explicit module bareword
// so cross-module same-leaf exports resolve.
calleename = callee.str;
emitsymname(c, calleename);
let hint: str;
hint.ptr = nil;
hint.len = 0;
if (callee.lhs != nil) {
if (callee.lhs.kind == nkind.N_IDENT) {
hint = callee.lhs.str;
};
};
emitfnname(c, calleename, hint);
};};
};
emitline("(SB)\n");