wcc: c-side mangles private decls; main exempt as entry-point convention
This commit is contained in:
111
cmd/w6c/cgen.c
111
cmd/w6c/cgen.c
@@ -301,6 +301,94 @@ ffi_collect(Cg *c, Node *file)
|
||||
}
|
||||
}
|
||||
|
||||
/* Module-private symbol map. Mirrors selfhost/cmd/wcc/cgen.ww. Each
|
||||
* non-exported, non-FFI top-level decl is mangled to <module>.<name>
|
||||
* at emission time so two modules can each privately define the same
|
||||
* helper without colliding at link time. */
|
||||
typedef struct Mod Mod;
|
||||
struct Mod {
|
||||
const char *name;
|
||||
const char *module;
|
||||
Mod *next;
|
||||
};
|
||||
static Mod *mod_map;
|
||||
|
||||
static int
|
||||
decl_has_ffisym(Node *d)
|
||||
{
|
||||
for (Node *a = d->attr; a; a = a->next) {
|
||||
if (a->kind != N_ATTR) continue;
|
||||
if (strcmp(a->str, "symbol") == 0) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
mod_collect(Cg *c, Node *file)
|
||||
{
|
||||
mod_map = NULL;
|
||||
if (file == NULL) return;
|
||||
for (Node *d = file->list; d; d = d->next) {
|
||||
int track = (d->kind == N_FNDECL) || (d->kind == N_TYPEDECL)
|
||||
|| (d->kind == N_DEF) || (d->kind == N_LET);
|
||||
if (!track) continue;
|
||||
if (d->export) continue;
|
||||
if (d->module == NULL || d->module[0] == '\0') continue;
|
||||
if (decl_has_ffisym(d)) continue;
|
||||
/* `main` is the linker entry-point convention. Even when not
|
||||
* marked `export`, it must keep its bare name so w6l can
|
||||
* resolve `_start`'s `CALL main(SB)`. */
|
||||
if (d->str && strcmp(d->str, "main") == 0) continue;
|
||||
Mod *m = amalloc(c->a, sizeof *m);
|
||||
m->name = d->str;
|
||||
m->module = d->module;
|
||||
m->next = mod_map;
|
||||
mod_map = m;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns the originating module for a name, or NULL if the name
|
||||
* isn't a registered private decl. */
|
||||
static const char *
|
||||
mod_lookup(const char *name)
|
||||
{
|
||||
for (Mod *m = mod_map; m; m = m->next)
|
||||
if (strcmp(m->name, name) == 0) return m->module;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Mangle an AST identifier into its asm linker symbol:
|
||||
* - @symbol("...") binding wins (return mapped name).
|
||||
* - module-private decl → <module>.<name>.
|
||||
* - else → name unchanged.
|
||||
* Used at every CALL/MOVQ/LEAQ site that targets an AST name. Plain
|
||||
* `asym(s)` still emits `s` verbatim — use it for strlit labels and
|
||||
* hard-coded runtime symbols like "rt_streq". */
|
||||
static const char *
|
||||
mod_mangle(Cg *c, const char *ident)
|
||||
{
|
||||
const char *resolved = ffi_resolve(ident);
|
||||
if (resolved != ident) return resolved;
|
||||
const char *mod = mod_lookup(ident);
|
||||
if (mod == NULL) return ident;
|
||||
size_t mn = strlen(mod), in = strlen(ident);
|
||||
char *buf = amalloc(c->a, mn + 1 + in + 1);
|
||||
memcpy(buf, mod, mn);
|
||||
buf[mn] = '.';
|
||||
memcpy(buf + mn + 1, ident, in);
|
||||
buf[mn + 1 + in] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Forward decl — masym below depends on asym defined further down. */
|
||||
static Adr asym(const char *s);
|
||||
|
||||
static Adr
|
||||
masym(Cg *c, const char *ident)
|
||||
{
|
||||
return asym(mod_mangle(c, ident));
|
||||
}
|
||||
|
||||
void
|
||||
cg_init(Cg *c, Arena *a)
|
||||
{
|
||||
@@ -514,7 +602,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* the same `$symname` for both call and
|
||||
* address-of via QBE; here we mirror that. */
|
||||
ins2(c, A_LEAQ,
|
||||
asym(ffi_resolve(n->str)), areg(D_AX));
|
||||
masym(c, n->str), areg(D_AX));
|
||||
break;
|
||||
}
|
||||
for (Sdef *s = sdefs; s; s = s->next) {
|
||||
@@ -526,7 +614,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
areg(D_BX));
|
||||
goto ident_done;
|
||||
}
|
||||
ins2(c, A_MOVQ, asym(n->str), areg(D_AX));
|
||||
ins2(c, A_MOVQ, masym(c, n->str), areg(D_AX));
|
||||
}
|
||||
ident_done:
|
||||
break;
|
||||
@@ -1253,7 +1341,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
ins2(c, A_LEAQ, amem(D_BP, sn_off), areg(D_AX));
|
||||
ins1(c, A_POPQ, areg(D_SI));
|
||||
ins2(c, A_MOVQ, areg(D_AX), areg(D_DI));
|
||||
ins1(c, A_CALL, asym(fn));
|
||||
ins1(c, A_CALL, masym(c, fn));
|
||||
ins2(c, A_ADDQ, aimm(1), amem(D_SP, 0));
|
||||
ins1(c, A_JMP, abranch(ll));
|
||||
label(c, le);
|
||||
@@ -1266,7 +1354,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
ins2(c, A_LEAQ, amem(D_BP, sn_off), areg(D_AX));
|
||||
ins1(c, A_POPQ, areg(D_SI));
|
||||
ins2(c, A_MOVQ, areg(D_AX), areg(D_DI));
|
||||
ins1(c, A_CALL, asym(fn));
|
||||
ins1(c, A_CALL, masym(c, fn));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1452,7 +1540,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
ins1(c, A_CALL, areg(D_AX));
|
||||
} else {
|
||||
ins1(c, A_CALL,
|
||||
asym(ffi_resolve(n->lhs->str)));
|
||||
masym(c, n->lhs->str));
|
||||
}
|
||||
} else if (n->lhs->kind == N_DOT && n->lhs->lhs &&
|
||||
n->lhs->lhs->kind == N_IDENT) {
|
||||
@@ -1462,7 +1550,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* function pointer — we load the field and indirect. */
|
||||
Type *bt = n->lhs->lhs->type;
|
||||
if (bt == NULL || bt == ty_err) {
|
||||
ins1(c, A_CALL, asym(ffi_resolve(n->lhs->str)));
|
||||
ins1(c, A_CALL, masym(c, n->lhs->str));
|
||||
} else {
|
||||
cgexpr(c, n->lhs, locals); /* AX = fn ptr */
|
||||
ins1(c, A_CALL, areg(D_AX));
|
||||
@@ -1669,7 +1757,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
Type *tu = (t && t->kind == TY_NAMED) ? t->under : t;
|
||||
if (tu && tu->kind == TY_FN) {
|
||||
ins2(c, A_LEAQ,
|
||||
asym(ffi_resolve(n->str)), areg(D_AX));
|
||||
masym(c, n->str), areg(D_AX));
|
||||
break;
|
||||
}
|
||||
for (Sdef *s = sdefs; s; s = s->next) {
|
||||
@@ -1681,7 +1769,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
areg(D_BX));
|
||||
goto dot_done;
|
||||
}
|
||||
ins2(c, A_MOVQ, asym(n->str), areg(D_AX));
|
||||
ins2(c, A_MOVQ, masym(c, n->str), areg(D_AX));
|
||||
goto dot_done;
|
||||
}
|
||||
int lenfld = (n->str && strcmp(n->str, "len") == 0);
|
||||
@@ -2513,7 +2601,9 @@ cgfn(Cg *c, FILE *out, Node *fn)
|
||||
|
||||
/* TEXT directive comes first; framesize is filled at the end. */
|
||||
Prog *text = newprog(c, A_TEXT);
|
||||
text->to = asym(fn->str);
|
||||
/* Mangle the label for non-exported, non-FFI decls; mod_mangle
|
||||
* does the FFI/module lookup in one step. */
|
||||
text->to = masym(c, fn->str);
|
||||
text->from.offset = 0; /* framesize patched below */
|
||||
emit(c, text);
|
||||
|
||||
@@ -2630,7 +2720,7 @@ emit_defs(Cg *c, FILE *out, Node *file)
|
||||
} else {
|
||||
continue; /* skip non-integer-literal defs */
|
||||
}
|
||||
fprintf(out, "DATA %s(SB),\"", d->str);
|
||||
fprintf(out, "DATA %s(SB),\"", mod_mangle(c, d->str));
|
||||
for (int i = 0; i < 8; i++) {
|
||||
unsigned b = (unsigned)((v >> (i * 8)) & 0xff);
|
||||
if (b == '"' || b == '\\')
|
||||
@@ -2672,6 +2762,7 @@ cg_file(Cg *c, FILE *out, Node *file)
|
||||
{
|
||||
if (file == NULL || file->kind != N_FILE) return;
|
||||
ffi_collect(c, file);
|
||||
mod_collect(c, file);
|
||||
sdef_collect(c, file);
|
||||
strlits = NULL;
|
||||
strlit_seq = 0;
|
||||
|
||||
@@ -94,6 +94,27 @@ skipws(Lex *l)
|
||||
continue;
|
||||
}
|
||||
if (c == '/' && lpeek(l, 1) == '/') {
|
||||
lget(l); lget(l); /* consume '//' */
|
||||
/* `// MODULE: foo` directive emitted by the ww
|
||||
* driver before each source-file's section in
|
||||
* combined.ww. Captured so cgen can mangle
|
||||
* non-exported symbols by module. */
|
||||
if (lpeek(l, 0) == ' '
|
||||
&& lpeek(l, 1) == 'M' && lpeek(l, 2) == 'O'
|
||||
&& lpeek(l, 3) == 'D' && lpeek(l, 4) == 'U'
|
||||
&& lpeek(l, 5) == 'L' && lpeek(l, 6) == 'E'
|
||||
&& lpeek(l, 7) == ':' && lpeek(l, 8) == ' ') {
|
||||
for (int i = 0; i < 9; i++) lget(l);
|
||||
u64 start = l->pos;
|
||||
while ((c = lpeek(l, 0)) >= 0
|
||||
&& c != '\n' && c != '\r')
|
||||
lget(l);
|
||||
u64 n = l->pos - start;
|
||||
char *m = amalloc(l->a, n + 1);
|
||||
memcpy(m, l->src + start, n);
|
||||
m[n] = '\0';
|
||||
l->module = m;
|
||||
}
|
||||
while ((c = lpeek(l, 0)) >= 0 && c != '\n')
|
||||
lget(l);
|
||||
continue;
|
||||
|
||||
@@ -1174,6 +1174,10 @@ parsefile(Parser *p)
|
||||
advance(p);
|
||||
continue;
|
||||
}
|
||||
/* Stamp the lex's current `// MODULE: foo` directive on the
|
||||
* decl. cgen uses it to mangle non-exported names so two
|
||||
* modules can each privately define `cstrlen`/`streq`/etc. */
|
||||
if (d != NULL) d->module = p->l->module;
|
||||
if (head == NULL) head = d;
|
||||
else tail->next = d;
|
||||
tail = d;
|
||||
|
||||
@@ -196,6 +196,7 @@ struct Lex {
|
||||
i32 col;
|
||||
Arena *a; /* token-text arena */
|
||||
int errs;
|
||||
const char *module; /* current `// MODULE: foo` directive, or NULL */
|
||||
};
|
||||
|
||||
void lexinit(Lex*, Arena*, const char *file, const char *src, u64 len);
|
||||
@@ -303,6 +304,11 @@ struct Node {
|
||||
int export;
|
||||
Type *type; /* filled in by checker */
|
||||
const char *tsuffix; /* typed numeric literal suffix */
|
||||
const char *module; /* `// MODULE: foo` directive at the
|
||||
* decl's section in combined.ww.
|
||||
* NULL for nested nodes; only top-
|
||||
* level decls (fn/def/type/let)
|
||||
* carry it. */
|
||||
};
|
||||
|
||||
Node *newnode(Arena*, Nkind, Pos);
|
||||
|
||||
@@ -4238,6 +4238,7 @@ fn collectmods(c: *cgen, file: *node) void = {
|
||||
if (d.kind == N_FNDECL) {
|
||||
if (d.exported == 0) {
|
||||
if (d.module.len > 0) {
|
||||
if (!streq(d.str, "main")) {
|
||||
let m: *modent = amalloc(c.a, 48u64): *modent;
|
||||
m.mname = d.str;
|
||||
m.module = d.module;
|
||||
@@ -4246,6 +4247,7 @@ fn collectmods(c: *cgen, file: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (d.kind == N_DEF) {
|
||||
if (d.exported == 0) {
|
||||
if (d.module.len > 0) {
|
||||
|
||||
@@ -598,6 +598,7 @@ fn collectmods(c: *cgen, file: *node) void = {
|
||||
if (d.kind == N_FNDECL) {
|
||||
if (d.exported == 0) {
|
||||
if (d.module.len > 0) {
|
||||
if (!streq(d.str, "main")) {
|
||||
let m: *modent = amalloc(c.a, 48u64): *modent;
|
||||
m.mname = d.str;
|
||||
m.module = d.module;
|
||||
@@ -606,6 +607,7 @@ fn collectmods(c: *cgen, file: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (d.kind == N_DEF) {
|
||||
if (d.exported == 0) {
|
||||
if (d.module.len > 0) {
|
||||
|
||||
@@ -4238,6 +4238,7 @@ fn collectmods(c: *cgen, file: *node) void = {
|
||||
if (d.kind == N_FNDECL) {
|
||||
if (d.exported == 0) {
|
||||
if (d.module.len > 0) {
|
||||
if (!streq(d.str, "main")) {
|
||||
let m: *modent = amalloc(c.a, 48u64): *modent;
|
||||
m.mname = d.str;
|
||||
m.module = d.module;
|
||||
@@ -4246,6 +4247,7 @@ fn collectmods(c: *cgen, file: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (d.kind == N_DEF) {
|
||||
if (d.exported == 0) {
|
||||
if (d.module.len > 0) {
|
||||
|
||||
Reference in New Issue
Block a user