wcc: mangle private decls as <module>.<name> via // MODULE: marker
This commit is contained in:
@@ -1727,10 +1727,11 @@ type node = struct {
|
||||
exported: i32, // bool — `export` keyword present
|
||||
type_: *void, // filled in by checker; type.ww treats it as *tinfo
|
||||
tsuffix: str, // typed numeric literal suffix ("i32", "u64", ...)
|
||||
module: str, // originating module from `// MODULE: foo`; "" if none
|
||||
};
|
||||
|
||||
export fn newnode(a: *arena, k: i32, file: str, line: i32, col: i32) *node = {
|
||||
let n: *node = amalloc(a, 192u64): *node; // 192 ≥ struct size
|
||||
let n: *node = amalloc(a, 208u64): *node; // ≥ struct size
|
||||
n.kind = k;
|
||||
n.file = file;
|
||||
n.line = line;
|
||||
@@ -2750,6 +2751,7 @@ fn parsedef(p: *parser, exported: i32) *node = {
|
||||
let pc: i32 = p.cur_col;
|
||||
advance(p); // past `def`
|
||||
let n: *node = newnode(p.a, N_DEF, pf, pl, pc);
|
||||
n.module = p.l.module;
|
||||
let id: str;
|
||||
expectident(p, &id);
|
||||
n.str = id;
|
||||
@@ -2768,6 +2770,7 @@ fn parselet(p: *parser, exported: i32) *node = {
|
||||
let pc: i32 = p.cur_col;
|
||||
advance(p); // past `let`
|
||||
let n: *node = newnode(p.a, N_LET, pf, pl, pc);
|
||||
n.module = p.l.module;
|
||||
let id: str;
|
||||
expectident(p, &id);
|
||||
n.str = id;
|
||||
@@ -2835,6 +2838,7 @@ fn parsefn(p: *parser, exported: i32, attrs: *node) *node = {
|
||||
let pc: i32 = p.cur_col;
|
||||
advance(p); // past `fn`
|
||||
let n: *node = newnode(p.a, N_FNDECL, pf, pl, pc);
|
||||
n.module = p.l.module;
|
||||
let id: str;
|
||||
expectident(p, &id);
|
||||
n.str = id;
|
||||
@@ -2864,6 +2868,7 @@ fn parsetypedecl(p: *parser, exported: i32) *node = {
|
||||
let pc: i32 = p.cur_col;
|
||||
advance(p); // past `type`
|
||||
let n: *node = newnode(p.a, N_TYPEDECL, pf, pl, pc);
|
||||
n.module = p.l.module;
|
||||
let id: str;
|
||||
expectident(p, &id);
|
||||
n.str = id;
|
||||
@@ -3787,6 +3792,7 @@ type cgen = struct {
|
||||
fnrets: *fnret,
|
||||
aliases: *aliasent,
|
||||
structs: *structinfo,
|
||||
mods: *modent, // non-exported decls → originating module
|
||||
fn_name: str,
|
||||
fn_ret: *node, // declared return type of current fn (or nil)
|
||||
loop_top: i32,
|
||||
@@ -4025,6 +4031,12 @@ fn emitdefconstants(c: *cgen, file: *node) void = {
|
||||
};
|
||||
if (ok) {
|
||||
emitline("DATA ");
|
||||
if (d.exported == 0) {
|
||||
if (d.module.len > 0) {
|
||||
os.write(1, d.module.ptr, d.module.len: u64);
|
||||
os.write(1, ".".ptr, 1u64);
|
||||
};
|
||||
};
|
||||
let nm: str = d.str;
|
||||
os.write(1, nm.ptr, nm.len: u64);
|
||||
emitline("(SB),\"");
|
||||
@@ -4201,6 +4213,107 @@ fn deflookup(c: *cgen, name: str) bool = {
|
||||
return false;
|
||||
};
|
||||
|
||||
// ---- module-private symbol map --------------------------------------
|
||||
//
|
||||
// Non-exported top-level decls live in their originating module's
|
||||
// namespace. cgen mangles those names to `<module>.<name>` at emission
|
||||
// time, both at the def site (TEXT/DATA) and at every call/load site,
|
||||
// so two modules can each privately define `cstrlen` without colliding
|
||||
// at link time. Exported decls and FFI-bound decls keep their bare name.
|
||||
|
||||
type modent = struct {
|
||||
mname: str, // the bare ident as it appears in source
|
||||
module: str, // the originating module (`// MODULE: foo`)
|
||||
mnext: *modent,
|
||||
};
|
||||
|
||||
fn collectmods(c: *cgen, file: *node) void = {
|
||||
c.mods = nil;
|
||||
if (file == nil) { return; };
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
// Mirror collectfnrets' shape exactly (plain prepend in one
|
||||
// branch). Earlier nested-if/early-return variants tickled a
|
||||
// wwstage cgen bug that dropped most prepends.
|
||||
if (d.kind == N_FNDECL) {
|
||||
if (d.exported == 0) {
|
||||
if (d.module.len > 0) {
|
||||
let m: *modent = amalloc(c.a, 48u64): *modent;
|
||||
m.mname = d.str;
|
||||
m.module = d.module;
|
||||
m.mnext = c.mods;
|
||||
c.mods = m;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (d.kind == N_DEF) {
|
||||
if (d.exported == 0) {
|
||||
if (d.module.len > 0) {
|
||||
let m: *modent = amalloc(c.a, 48u64): *modent;
|
||||
m.mname = d.str;
|
||||
m.module = d.module;
|
||||
m.mnext = c.mods;
|
||||
c.mods = m;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (d.kind == N_TYPEDECL) {
|
||||
if (d.exported == 0) {
|
||||
if (d.module.len > 0) {
|
||||
let m: *modent = amalloc(c.a, 48u64): *modent;
|
||||
m.mname = d.str;
|
||||
m.module = d.module;
|
||||
m.mnext = c.mods;
|
||||
c.mods = m;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (d.kind == N_LET) {
|
||||
if (d.exported == 0) {
|
||||
if (d.module.len > 0) {
|
||||
let m: *modent = amalloc(c.a, 48u64): *modent;
|
||||
m.mname = d.str;
|
||||
m.module = d.module;
|
||||
m.mnext = c.mods;
|
||||
c.mods = m;
|
||||
};
|
||||
};
|
||||
};
|
||||
d = d.next;
|
||||
};
|
||||
};
|
||||
|
||||
fn modlookup(c: *cgen, name: str) str = {
|
||||
let m: *modent = c.mods;
|
||||
for (m != nil) {
|
||||
if (streq(m.mname, name)) { return m.module; };
|
||||
m = m.mnext;
|
||||
};
|
||||
let empty: str;
|
||||
empty.ptr = nil;
|
||||
empty.len = 0;
|
||||
return empty;
|
||||
};
|
||||
|
||||
// emitsymname — write the asm symbol name for `ident`. Honours, in
|
||||
// order: FFI mapping (@symbol), module mangling (private decls), bare
|
||||
// name. Use everywhere a top-level name is emitted before `(SB)` or in
|
||||
// a `TEXT name,$N` header.
|
||||
fn emitsymname(c: *cgen, ident: str) void = {
|
||||
let resolved: str = ffiresolve(c, ident);
|
||||
if (resolved.ptr != ident.ptr) {
|
||||
// FFI hit — emit the mapped linker symbol verbatim.
|
||||
os.write(1, resolved.ptr, resolved.len: u64);
|
||||
return;
|
||||
};
|
||||
let mod: str = modlookup(c, ident);
|
||||
if (mod.len > 0) {
|
||||
os.write(1, mod.ptr, mod.len: u64);
|
||||
os.write(1, ".".ptr, 1u64);
|
||||
};
|
||||
os.write(1, ident.ptr, ident.len: u64);
|
||||
};
|
||||
|
||||
// ---- FFI map ---------------------------------------------------------
|
||||
|
||||
fn fficollect(c: *cgen, file: *node) void = {
|
||||
@@ -4895,20 +5008,19 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
// Top-level `def` constant — load from its DATA symbol.
|
||||
if (deflookup(c, nm)) {
|
||||
emitline("\tMOVQ\t");
|
||||
os.write(1, nm.ptr, nm.len: u64);
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), AX\n");
|
||||
return;
|
||||
};
|
||||
// Fn-name used as a value (e.g. `let f = some_fn;` or
|
||||
// `... = some_fn;`). LEAQ the symbol address into AX —
|
||||
// resolved through ffiresolve so a body-less FFI binding
|
||||
// emits the C symbol it was declared with via @symbol(),
|
||||
// not the ww-side ident.
|
||||
// `... = some_fn;`). LEAQ the symbol address into AX. The
|
||||
// emitsymname 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.
|
||||
let rt: *node = fnretlookup(c, nm);
|
||||
if (rt != nil) {
|
||||
let resolved: str = ffiresolve(c, nm);
|
||||
emitline("\tLEAQ\t");
|
||||
os.write(1, resolved.ptr, resolved.len: u64);
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), AX\n");
|
||||
return;
|
||||
};
|
||||
@@ -5246,7 +5358,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == N_IDENT) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(fld);
|
||||
emitsymname(c, fld);
|
||||
emitline("(SB), AX\n");
|
||||
return;
|
||||
};
|
||||
@@ -5498,12 +5610,10 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
if (callee != nil) {
|
||||
if (callee.kind == N_IDENT) {
|
||||
calleename = callee.str;
|
||||
let resolved: str = ffiresolve(c, calleename);
|
||||
os.write(1, resolved.ptr, resolved.len: u64);
|
||||
emitsymname(c, calleename);
|
||||
} else { if (callee.kind == N_DOT) {
|
||||
calleename = callee.str;
|
||||
let resolved: str = ffiresolve(c, calleename);
|
||||
os.write(1, resolved.ptr, resolved.len: u64);
|
||||
emitsymname(c, calleename);
|
||||
};};
|
||||
};
|
||||
emitline("(SB)\n");
|
||||
@@ -6641,6 +6751,23 @@ fn cgfn(c: *cgen, fn_: *node) void = {
|
||||
c.fn_ret = fn_.lhs;
|
||||
|
||||
emitline("TEXT ");
|
||||
if (fn_.exported == 0) {
|
||||
if (fn_.module.len > 0) {
|
||||
let isffi: bool = false;
|
||||
let a: *node = fn_.attr;
|
||||
for (a != nil) {
|
||||
if (a.kind == N_ATTR) {
|
||||
let an: str = a.str;
|
||||
if (streq(an, "symbol")) { isffi = true; };
|
||||
};
|
||||
a = a.next;
|
||||
};
|
||||
if (!isffi) {
|
||||
os.write(1, fn_.module.ptr, fn_.module.len: u64);
|
||||
os.write(1, ".".ptr, 1u64);
|
||||
};
|
||||
};
|
||||
};
|
||||
let nm: str = fn_.str;
|
||||
os.write(1, nm.ptr, nm.len: u64);
|
||||
emitline(",$");
|
||||
@@ -6703,6 +6830,7 @@ export fn cgfile(c: *cgen, file: *node) void = {
|
||||
collectdefs(c, file);
|
||||
collectfnrets(c, file);
|
||||
fficollect(c, file);
|
||||
collectmods(c, file);
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
if (d.kind == N_FNDECL) {
|
||||
|
||||
@@ -108,10 +108,11 @@ type node = struct {
|
||||
exported: i32, // bool — `export` keyword present
|
||||
type_: *void, // filled in by checker; type.ww treats it as *tinfo
|
||||
tsuffix: str, // typed numeric literal suffix ("i32", "u64", ...)
|
||||
module: str, // originating module from `// MODULE: foo`; "" if none
|
||||
};
|
||||
|
||||
export fn newnode(a: *arena, k: i32, file: str, line: i32, col: i32) *node = {
|
||||
let n: *node = amalloc(a, 192u64): *node; // 192 ≥ struct size
|
||||
let n: *node = amalloc(a, 208u64): *node; // ≥ struct size
|
||||
n.kind = k;
|
||||
n.file = file;
|
||||
n.line = line;
|
||||
|
||||
@@ -152,6 +152,7 @@ type cgen = struct {
|
||||
fnrets: *fnret,
|
||||
aliases: *aliasent,
|
||||
structs: *structinfo,
|
||||
mods: *modent, // non-exported decls → originating module
|
||||
fn_name: str,
|
||||
fn_ret: *node, // declared return type of current fn (or nil)
|
||||
loop_top: i32,
|
||||
@@ -390,6 +391,12 @@ fn emitdefconstants(c: *cgen, file: *node) void = {
|
||||
};
|
||||
if (ok) {
|
||||
emitline("DATA ");
|
||||
if (d.exported == 0) {
|
||||
if (d.module.len > 0) {
|
||||
os.write(1, d.module.ptr, d.module.len: u64);
|
||||
os.write(1, ".".ptr, 1u64);
|
||||
};
|
||||
};
|
||||
let nm: str = d.str;
|
||||
os.write(1, nm.ptr, nm.len: u64);
|
||||
emitline("(SB),\"");
|
||||
@@ -566,6 +573,107 @@ fn deflookup(c: *cgen, name: str) bool = {
|
||||
return false;
|
||||
};
|
||||
|
||||
// ---- module-private symbol map --------------------------------------
|
||||
//
|
||||
// Non-exported top-level decls live in their originating module's
|
||||
// namespace. cgen mangles those names to `<module>.<name>` at emission
|
||||
// time, both at the def site (TEXT/DATA) and at every call/load site,
|
||||
// so two modules can each privately define `cstrlen` without colliding
|
||||
// at link time. Exported decls and FFI-bound decls keep their bare name.
|
||||
|
||||
type modent = struct {
|
||||
mname: str, // the bare ident as it appears in source
|
||||
module: str, // the originating module (`// MODULE: foo`)
|
||||
mnext: *modent,
|
||||
};
|
||||
|
||||
fn collectmods(c: *cgen, file: *node) void = {
|
||||
c.mods = nil;
|
||||
if (file == nil) { return; };
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
// Mirror collectfnrets' shape exactly (plain prepend in one
|
||||
// branch). Earlier nested-if/early-return variants tickled a
|
||||
// wwstage cgen bug that dropped most prepends.
|
||||
if (d.kind == N_FNDECL) {
|
||||
if (d.exported == 0) {
|
||||
if (d.module.len > 0) {
|
||||
let m: *modent = amalloc(c.a, 48u64): *modent;
|
||||
m.mname = d.str;
|
||||
m.module = d.module;
|
||||
m.mnext = c.mods;
|
||||
c.mods = m;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (d.kind == N_DEF) {
|
||||
if (d.exported == 0) {
|
||||
if (d.module.len > 0) {
|
||||
let m: *modent = amalloc(c.a, 48u64): *modent;
|
||||
m.mname = d.str;
|
||||
m.module = d.module;
|
||||
m.mnext = c.mods;
|
||||
c.mods = m;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (d.kind == N_TYPEDECL) {
|
||||
if (d.exported == 0) {
|
||||
if (d.module.len > 0) {
|
||||
let m: *modent = amalloc(c.a, 48u64): *modent;
|
||||
m.mname = d.str;
|
||||
m.module = d.module;
|
||||
m.mnext = c.mods;
|
||||
c.mods = m;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (d.kind == N_LET) {
|
||||
if (d.exported == 0) {
|
||||
if (d.module.len > 0) {
|
||||
let m: *modent = amalloc(c.a, 48u64): *modent;
|
||||
m.mname = d.str;
|
||||
m.module = d.module;
|
||||
m.mnext = c.mods;
|
||||
c.mods = m;
|
||||
};
|
||||
};
|
||||
};
|
||||
d = d.next;
|
||||
};
|
||||
};
|
||||
|
||||
fn modlookup(c: *cgen, name: str) str = {
|
||||
let m: *modent = c.mods;
|
||||
for (m != nil) {
|
||||
if (streq(m.mname, name)) { return m.module; };
|
||||
m = m.mnext;
|
||||
};
|
||||
let empty: str;
|
||||
empty.ptr = nil;
|
||||
empty.len = 0;
|
||||
return empty;
|
||||
};
|
||||
|
||||
// emitsymname — write the asm symbol name for `ident`. Honours, in
|
||||
// order: FFI mapping (@symbol), module mangling (private decls), bare
|
||||
// name. Use everywhere a top-level name is emitted before `(SB)` or in
|
||||
// a `TEXT name,$N` header.
|
||||
fn emitsymname(c: *cgen, ident: str) void = {
|
||||
let resolved: str = ffiresolve(c, ident);
|
||||
if (resolved.ptr != ident.ptr) {
|
||||
// FFI hit — emit the mapped linker symbol verbatim.
|
||||
os.write(1, resolved.ptr, resolved.len: u64);
|
||||
return;
|
||||
};
|
||||
let mod: str = modlookup(c, ident);
|
||||
if (mod.len > 0) {
|
||||
os.write(1, mod.ptr, mod.len: u64);
|
||||
os.write(1, ".".ptr, 1u64);
|
||||
};
|
||||
os.write(1, ident.ptr, ident.len: u64);
|
||||
};
|
||||
|
||||
// ---- FFI map ---------------------------------------------------------
|
||||
|
||||
fn fficollect(c: *cgen, file: *node) void = {
|
||||
@@ -1260,20 +1368,19 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
// Top-level `def` constant — load from its DATA symbol.
|
||||
if (deflookup(c, nm)) {
|
||||
emitline("\tMOVQ\t");
|
||||
os.write(1, nm.ptr, nm.len: u64);
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), AX\n");
|
||||
return;
|
||||
};
|
||||
// Fn-name used as a value (e.g. `let f = some_fn;` or
|
||||
// `... = some_fn;`). LEAQ the symbol address into AX —
|
||||
// resolved through ffiresolve so a body-less FFI binding
|
||||
// emits the C symbol it was declared with via @symbol(),
|
||||
// not the ww-side ident.
|
||||
// `... = some_fn;`). LEAQ the symbol address into AX. The
|
||||
// emitsymname 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.
|
||||
let rt: *node = fnretlookup(c, nm);
|
||||
if (rt != nil) {
|
||||
let resolved: str = ffiresolve(c, nm);
|
||||
emitline("\tLEAQ\t");
|
||||
os.write(1, resolved.ptr, resolved.len: u64);
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), AX\n");
|
||||
return;
|
||||
};
|
||||
@@ -1611,7 +1718,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == N_IDENT) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(fld);
|
||||
emitsymname(c, fld);
|
||||
emitline("(SB), AX\n");
|
||||
return;
|
||||
};
|
||||
@@ -1863,12 +1970,10 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
if (callee != nil) {
|
||||
if (callee.kind == N_IDENT) {
|
||||
calleename = callee.str;
|
||||
let resolved: str = ffiresolve(c, calleename);
|
||||
os.write(1, resolved.ptr, resolved.len: u64);
|
||||
emitsymname(c, calleename);
|
||||
} else { if (callee.kind == N_DOT) {
|
||||
calleename = callee.str;
|
||||
let resolved: str = ffiresolve(c, calleename);
|
||||
os.write(1, resolved.ptr, resolved.len: u64);
|
||||
emitsymname(c, calleename);
|
||||
};};
|
||||
};
|
||||
emitline("(SB)\n");
|
||||
@@ -3006,6 +3111,23 @@ fn cgfn(c: *cgen, fn_: *node) void = {
|
||||
c.fn_ret = fn_.lhs;
|
||||
|
||||
emitline("TEXT ");
|
||||
if (fn_.exported == 0) {
|
||||
if (fn_.module.len > 0) {
|
||||
let isffi: bool = false;
|
||||
let a: *node = fn_.attr;
|
||||
for (a != nil) {
|
||||
if (a.kind == N_ATTR) {
|
||||
let an: str = a.str;
|
||||
if (streq(an, "symbol")) { isffi = true; };
|
||||
};
|
||||
a = a.next;
|
||||
};
|
||||
if (!isffi) {
|
||||
os.write(1, fn_.module.ptr, fn_.module.len: u64);
|
||||
os.write(1, ".".ptr, 1u64);
|
||||
};
|
||||
};
|
||||
};
|
||||
let nm: str = fn_.str;
|
||||
os.write(1, nm.ptr, nm.len: u64);
|
||||
emitline(",$");
|
||||
@@ -3068,6 +3190,7 @@ export fn cgfile(c: *cgen, file: *node) void = {
|
||||
collectdefs(c, file);
|
||||
collectfnrets(c, file);
|
||||
fficollect(c, file);
|
||||
collectmods(c, file);
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
if (d.kind == N_FNDECL) {
|
||||
|
||||
@@ -794,6 +794,7 @@ fn parsedef(p: *parser, exported: i32) *node = {
|
||||
let pc: i32 = p.cur_col;
|
||||
advance(p); // past `def`
|
||||
let n: *node = newnode(p.a, N_DEF, pf, pl, pc);
|
||||
n.module = p.l.module;
|
||||
let id: str;
|
||||
expectident(p, &id);
|
||||
n.str = id;
|
||||
@@ -812,6 +813,7 @@ fn parselet(p: *parser, exported: i32) *node = {
|
||||
let pc: i32 = p.cur_col;
|
||||
advance(p); // past `let`
|
||||
let n: *node = newnode(p.a, N_LET, pf, pl, pc);
|
||||
n.module = p.l.module;
|
||||
let id: str;
|
||||
expectident(p, &id);
|
||||
n.str = id;
|
||||
@@ -879,6 +881,7 @@ fn parsefn(p: *parser, exported: i32, attrs: *node) *node = {
|
||||
let pc: i32 = p.cur_col;
|
||||
advance(p); // past `fn`
|
||||
let n: *node = newnode(p.a, N_FNDECL, pf, pl, pc);
|
||||
n.module = p.l.module;
|
||||
let id: str;
|
||||
expectident(p, &id);
|
||||
n.str = id;
|
||||
@@ -908,6 +911,7 @@ fn parsetypedecl(p: *parser, exported: i32) *node = {
|
||||
let pc: i32 = p.cur_col;
|
||||
advance(p); // past `type`
|
||||
let n: *node = newnode(p.a, N_TYPEDECL, pf, pl, pc);
|
||||
n.module = p.l.module;
|
||||
let id: str;
|
||||
expectident(p, &id);
|
||||
n.str = id;
|
||||
|
||||
@@ -1727,10 +1727,11 @@ type node = struct {
|
||||
exported: i32, // bool — `export` keyword present
|
||||
type_: *void, // filled in by checker; type.ww treats it as *tinfo
|
||||
tsuffix: str, // typed numeric literal suffix ("i32", "u64", ...)
|
||||
module: str, // originating module from `// MODULE: foo`; "" if none
|
||||
};
|
||||
|
||||
export fn newnode(a: *arena, k: i32, file: str, line: i32, col: i32) *node = {
|
||||
let n: *node = amalloc(a, 192u64): *node; // 192 ≥ struct size
|
||||
let n: *node = amalloc(a, 208u64): *node; // ≥ struct size
|
||||
n.kind = k;
|
||||
n.file = file;
|
||||
n.line = line;
|
||||
@@ -2750,6 +2751,7 @@ fn parsedef(p: *parser, exported: i32) *node = {
|
||||
let pc: i32 = p.cur_col;
|
||||
advance(p); // past `def`
|
||||
let n: *node = newnode(p.a, N_DEF, pf, pl, pc);
|
||||
n.module = p.l.module;
|
||||
let id: str;
|
||||
expectident(p, &id);
|
||||
n.str = id;
|
||||
@@ -2768,6 +2770,7 @@ fn parselet(p: *parser, exported: i32) *node = {
|
||||
let pc: i32 = p.cur_col;
|
||||
advance(p); // past `let`
|
||||
let n: *node = newnode(p.a, N_LET, pf, pl, pc);
|
||||
n.module = p.l.module;
|
||||
let id: str;
|
||||
expectident(p, &id);
|
||||
n.str = id;
|
||||
@@ -2835,6 +2838,7 @@ fn parsefn(p: *parser, exported: i32, attrs: *node) *node = {
|
||||
let pc: i32 = p.cur_col;
|
||||
advance(p); // past `fn`
|
||||
let n: *node = newnode(p.a, N_FNDECL, pf, pl, pc);
|
||||
n.module = p.l.module;
|
||||
let id: str;
|
||||
expectident(p, &id);
|
||||
n.str = id;
|
||||
@@ -2864,6 +2868,7 @@ fn parsetypedecl(p: *parser, exported: i32) *node = {
|
||||
let pc: i32 = p.cur_col;
|
||||
advance(p); // past `type`
|
||||
let n: *node = newnode(p.a, N_TYPEDECL, pf, pl, pc);
|
||||
n.module = p.l.module;
|
||||
let id: str;
|
||||
expectident(p, &id);
|
||||
n.str = id;
|
||||
@@ -3787,6 +3792,7 @@ type cgen = struct {
|
||||
fnrets: *fnret,
|
||||
aliases: *aliasent,
|
||||
structs: *structinfo,
|
||||
mods: *modent, // non-exported decls → originating module
|
||||
fn_name: str,
|
||||
fn_ret: *node, // declared return type of current fn (or nil)
|
||||
loop_top: i32,
|
||||
@@ -4025,6 +4031,12 @@ fn emitdefconstants(c: *cgen, file: *node) void = {
|
||||
};
|
||||
if (ok) {
|
||||
emitline("DATA ");
|
||||
if (d.exported == 0) {
|
||||
if (d.module.len > 0) {
|
||||
os.write(1, d.module.ptr, d.module.len: u64);
|
||||
os.write(1, ".".ptr, 1u64);
|
||||
};
|
||||
};
|
||||
let nm: str = d.str;
|
||||
os.write(1, nm.ptr, nm.len: u64);
|
||||
emitline("(SB),\"");
|
||||
@@ -4201,6 +4213,107 @@ fn deflookup(c: *cgen, name: str) bool = {
|
||||
return false;
|
||||
};
|
||||
|
||||
// ---- module-private symbol map --------------------------------------
|
||||
//
|
||||
// Non-exported top-level decls live in their originating module's
|
||||
// namespace. cgen mangles those names to `<module>.<name>` at emission
|
||||
// time, both at the def site (TEXT/DATA) and at every call/load site,
|
||||
// so two modules can each privately define `cstrlen` without colliding
|
||||
// at link time. Exported decls and FFI-bound decls keep their bare name.
|
||||
|
||||
type modent = struct {
|
||||
mname: str, // the bare ident as it appears in source
|
||||
module: str, // the originating module (`// MODULE: foo`)
|
||||
mnext: *modent,
|
||||
};
|
||||
|
||||
fn collectmods(c: *cgen, file: *node) void = {
|
||||
c.mods = nil;
|
||||
if (file == nil) { return; };
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
// Mirror collectfnrets' shape exactly (plain prepend in one
|
||||
// branch). Earlier nested-if/early-return variants tickled a
|
||||
// wwstage cgen bug that dropped most prepends.
|
||||
if (d.kind == N_FNDECL) {
|
||||
if (d.exported == 0) {
|
||||
if (d.module.len > 0) {
|
||||
let m: *modent = amalloc(c.a, 48u64): *modent;
|
||||
m.mname = d.str;
|
||||
m.module = d.module;
|
||||
m.mnext = c.mods;
|
||||
c.mods = m;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (d.kind == N_DEF) {
|
||||
if (d.exported == 0) {
|
||||
if (d.module.len > 0) {
|
||||
let m: *modent = amalloc(c.a, 48u64): *modent;
|
||||
m.mname = d.str;
|
||||
m.module = d.module;
|
||||
m.mnext = c.mods;
|
||||
c.mods = m;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (d.kind == N_TYPEDECL) {
|
||||
if (d.exported == 0) {
|
||||
if (d.module.len > 0) {
|
||||
let m: *modent = amalloc(c.a, 48u64): *modent;
|
||||
m.mname = d.str;
|
||||
m.module = d.module;
|
||||
m.mnext = c.mods;
|
||||
c.mods = m;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (d.kind == N_LET) {
|
||||
if (d.exported == 0) {
|
||||
if (d.module.len > 0) {
|
||||
let m: *modent = amalloc(c.a, 48u64): *modent;
|
||||
m.mname = d.str;
|
||||
m.module = d.module;
|
||||
m.mnext = c.mods;
|
||||
c.mods = m;
|
||||
};
|
||||
};
|
||||
};
|
||||
d = d.next;
|
||||
};
|
||||
};
|
||||
|
||||
fn modlookup(c: *cgen, name: str) str = {
|
||||
let m: *modent = c.mods;
|
||||
for (m != nil) {
|
||||
if (streq(m.mname, name)) { return m.module; };
|
||||
m = m.mnext;
|
||||
};
|
||||
let empty: str;
|
||||
empty.ptr = nil;
|
||||
empty.len = 0;
|
||||
return empty;
|
||||
};
|
||||
|
||||
// emitsymname — write the asm symbol name for `ident`. Honours, in
|
||||
// order: FFI mapping (@symbol), module mangling (private decls), bare
|
||||
// name. Use everywhere a top-level name is emitted before `(SB)` or in
|
||||
// a `TEXT name,$N` header.
|
||||
fn emitsymname(c: *cgen, ident: str) void = {
|
||||
let resolved: str = ffiresolve(c, ident);
|
||||
if (resolved.ptr != ident.ptr) {
|
||||
// FFI hit — emit the mapped linker symbol verbatim.
|
||||
os.write(1, resolved.ptr, resolved.len: u64);
|
||||
return;
|
||||
};
|
||||
let mod: str = modlookup(c, ident);
|
||||
if (mod.len > 0) {
|
||||
os.write(1, mod.ptr, mod.len: u64);
|
||||
os.write(1, ".".ptr, 1u64);
|
||||
};
|
||||
os.write(1, ident.ptr, ident.len: u64);
|
||||
};
|
||||
|
||||
// ---- FFI map ---------------------------------------------------------
|
||||
|
||||
fn fficollect(c: *cgen, file: *node) void = {
|
||||
@@ -4895,20 +5008,19 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
// Top-level `def` constant — load from its DATA symbol.
|
||||
if (deflookup(c, nm)) {
|
||||
emitline("\tMOVQ\t");
|
||||
os.write(1, nm.ptr, nm.len: u64);
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), AX\n");
|
||||
return;
|
||||
};
|
||||
// Fn-name used as a value (e.g. `let f = some_fn;` or
|
||||
// `... = some_fn;`). LEAQ the symbol address into AX —
|
||||
// resolved through ffiresolve so a body-less FFI binding
|
||||
// emits the C symbol it was declared with via @symbol(),
|
||||
// not the ww-side ident.
|
||||
// `... = some_fn;`). LEAQ the symbol address into AX. The
|
||||
// emitsymname 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.
|
||||
let rt: *node = fnretlookup(c, nm);
|
||||
if (rt != nil) {
|
||||
let resolved: str = ffiresolve(c, nm);
|
||||
emitline("\tLEAQ\t");
|
||||
os.write(1, resolved.ptr, resolved.len: u64);
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), AX\n");
|
||||
return;
|
||||
};
|
||||
@@ -5246,7 +5358,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == N_IDENT) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(fld);
|
||||
emitsymname(c, fld);
|
||||
emitline("(SB), AX\n");
|
||||
return;
|
||||
};
|
||||
@@ -5498,12 +5610,10 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
if (callee != nil) {
|
||||
if (callee.kind == N_IDENT) {
|
||||
calleename = callee.str;
|
||||
let resolved: str = ffiresolve(c, calleename);
|
||||
os.write(1, resolved.ptr, resolved.len: u64);
|
||||
emitsymname(c, calleename);
|
||||
} else { if (callee.kind == N_DOT) {
|
||||
calleename = callee.str;
|
||||
let resolved: str = ffiresolve(c, calleename);
|
||||
os.write(1, resolved.ptr, resolved.len: u64);
|
||||
emitsymname(c, calleename);
|
||||
};};
|
||||
};
|
||||
emitline("(SB)\n");
|
||||
@@ -6641,6 +6751,23 @@ fn cgfn(c: *cgen, fn_: *node) void = {
|
||||
c.fn_ret = fn_.lhs;
|
||||
|
||||
emitline("TEXT ");
|
||||
if (fn_.exported == 0) {
|
||||
if (fn_.module.len > 0) {
|
||||
let isffi: bool = false;
|
||||
let a: *node = fn_.attr;
|
||||
for (a != nil) {
|
||||
if (a.kind == N_ATTR) {
|
||||
let an: str = a.str;
|
||||
if (streq(an, "symbol")) { isffi = true; };
|
||||
};
|
||||
a = a.next;
|
||||
};
|
||||
if (!isffi) {
|
||||
os.write(1, fn_.module.ptr, fn_.module.len: u64);
|
||||
os.write(1, ".".ptr, 1u64);
|
||||
};
|
||||
};
|
||||
};
|
||||
let nm: str = fn_.str;
|
||||
os.write(1, nm.ptr, nm.len: u64);
|
||||
emitline(",$");
|
||||
@@ -6703,6 +6830,7 @@ export fn cgfile(c: *cgen, file: *node) void = {
|
||||
collectdefs(c, file);
|
||||
collectfnrets(c, file);
|
||||
fficollect(c, file);
|
||||
collectmods(c, file);
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
if (d.kind == N_FNDECL) {
|
||||
|
||||
Reference in New Issue
Block a user