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

@@ -0,0 +1,19 @@
// Two modules each `export fn ping` and each define a private
// `helper` with the same leaf. Together with mod2 they pin three of
// the four label-emit sites #9 touches:
// - CALL N_DOT : main calls mod1.ping / mod2.ping
// - CALL N_IDENT : ping bare-calls helper inside its own module
// - LEAQ N_IDENT : fpi takes `helper` by value, then calls it
//
// The LEAQ N_DOT path (`let p = mod1.ping`) is intentionally not
// exercised here — wwstage cgdot has no working LEAQ-of-fn N_DOT
// branch, so a row would diverge across stages.
fn helper() i32 = { return 11i32; };
fn fpi() i32 = {
let h: fn() i32 = helper;
return h();
};
export fn ping() i32 = { return 3i32 + helper() + fpi(); };

View File

@@ -0,0 +1,11 @@
// Sibling of mod1.ww — same leaves (`ping`, `helper`, `fpi`),
// distinct values. See mod1.ww for the coverage-rationale comment.
fn helper() i32 = { return 13i32; };
fn fpi() i32 = {
let h: fn() i32 = helper;
return h();
};
export fn ping() i32 = { return 5i32 + helper() + fpi(); };

View File

@@ -0,0 +1,18 @@
// Positive case: import both modules, dispatch through the explicit
// module qualifier. Each module's ping = bare-value + helper() (CALL
// N_IDENT) + fpi() (which exercises LEAQ N_IDENT via `let h = helper`).
//
// mod1.ping = 3 + 11 + 11 = 25
// mod2.ping = 5 + 13 + 13 = 31
// total = 56
//
// Pre-fix the four `helper` / `ping` symbols would collapse at link
// or the hint-less lookup would silently grab the wrong module's
// body — exit would land at 50/62/some other value, never 56.
use mod1;
use mod2;
fn main() i32 = {
return mod1.ping() + mod2.ping();
};