wwstage cgdot lacked a TY_FN branch for module-qualified N_DOT
rvalues. `let p = mod1.ping` fell through to the MOVQ/LEAQ-narrow
fallback, loading 8 prologue bytes from the fn's first instruction
instead of taking its address. Cstage cgdot already handled this
case (wired during #9, f1440bf).
Mirror cstage: gate on fnretlookup(c, fld) before the localloadop
fallback; emit `LEAQ <module>.<name>(SB), AX` via emitfnname with
the hint from lhs.str.
Extend 706_fnlabel_mangle: pos.ww now stores mod1.ping/mod2.ping
into local fn-pointer slots and dispatches through them in addition
to the existing direct calls. Expected exit 56 → 112. Regression
shape: without the new branch, MOVQ leaf(SB) loads the prologue
bytes; indirect call jumps into garbage → SIGSEGV.
ww2 == ww3 == ww4 byte-identical at the new emit.
23 lines
778 B
Plaintext
23 lines
778 B
Plaintext
// 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`).
|
|
// `p1` / `p2` pin the LEAQ N_DOT path (`let p = mod.ping` for fn
|
|
// rvalue) — cgdot must emit `LEAQ mod.ping(SB), AX`, not
|
|
// `MOVQ ping(SB), AX`. Pre-#12 wwstage cgdot fell through to the
|
|
// latter and `p1() / p2()` jumped into mid-prologue garbage.
|
|
//
|
|
// mod1.ping = 3 + 11 + 11 = 25
|
|
// mod2.ping = 5 + 13 + 13 = 31
|
|
// p1() = mod1.ping = 25
|
|
// p2() = mod2.ping = 31
|
|
// total = 112
|
|
|
|
use mod1;
|
|
use mod2;
|
|
|
|
fn main() i32 = {
|
|
let p1: fn() i32 = mod1.ping;
|
|
let p2: fn() i32 = mod2.ping;
|
|
return mod1.ping() + mod2.ping() + p1() + p2();
|
|
};
|