wcc: mangle private decls as <module>.<name> via // MODULE: marker

This commit is contained in:
2026-05-11 15:16:22 +09:00
parent d7036be0e5
commit 895f221b6c
5 changed files with 423 additions and 39 deletions

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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) {