diff --git a/cmd/w6c/wwi.c b/cmd/w6c/wwi.c index b3c083fc..797666a7 100644 --- a/cmd/w6c/wwi.c +++ b/cmd/w6c/wwi.c @@ -310,11 +310,45 @@ wwi_expr(FILE *of, Node *e) } } +/* Only codegen/link-relevant attributes round-trip into the `.wwi`. Today + * that is exactly @symbol (the FFI link-symbol override, read back at cgen + * fficollect — dropping it makes sep-compile emit `CALL malloc` for a + * `@symbol("rt_malloc")` fn). Named for the class so @align/@offset would + * slot in here IF ww ever grows field-layout attributes — it has none + * today (task #47 report). @test never reaches a `.wwi` (test fns are not + * export-marked), so it needs no exclusion arm. */ +static int +wwi_attr_relevant(const char *nm) +{ + return nm && strcmp(nm, "symbol") == 0; +} + +static void +wwi_attrs(FILE *of, Node *d) +{ + for (Node *a = d->attr; a; a = a->next) { + if (a->kind != N_ATTR || !wwi_attr_relevant(a->str)) + continue; + fputc('@', of); + fputs(a->str, of); + if (a->list) { + fputc('(', of); + for (Node *arg = a->list; arg; arg = arg->next) { + if (arg != a->list) fputs(", ", of); + wwi_expr(of, arg); + } + fputc(')', of); + } + fputc(' ', of); + } +} + static void wwi_decl(FILE *of, Node *d) { switch (d->kind) { case N_FNDECL: + wwi_attrs(of, d); fputs("export fn ", of); fputs(d->str, of); fputc('(', of); diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index 7673f38d..b649a21c 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -45676,8 +45676,42 @@ fn wwitype(fd: i32, t: *node) void = { };};};};};};};};};};}; }; +// Only codegen/link-relevant attributes round-trip into the `.wwi`. Today +// that is exactly @symbol (the FFI link-symbol override, read back at cgen +// fficollect — dropping it makes sep-compile emit `CALL malloc` for a +// `@symbol("rt_malloc")` fn). Named for the class so @align/@offset would +// slot in here IF ww ever grows field-layout attributes — it has none +// today (task #47 report). @test never reaches a `.wwi` (test fns are not +// export-marked), so it needs no exclusion arm. +fn wwiattrrelevant(nm: str) bool = { + return streq(nm, "symbol"); +}; + +fn wwiattrs(fd: i32, d: *node) void = { + let a: *node = d.attr; + for (a != nil) { + if (a.kind == nkind.N_ATTR && wwiattrrelevant(a.str)) { + wputb(fd, '@'); + wputs(fd, a.str); + if (a.list != nil) { + wputb(fd, '('); + let arg: *node = a.list; + for (arg != nil) { + if (arg != a.list) { wputs(fd, ", "); }; + wwiexpr(fd, arg); + arg = arg.next; + }; + wputb(fd, ')'); + }; + wputb(fd, 32u8); + }; + a = a.next; + }; +}; + fn wwidecl(fd: i32, d: *node) void = { if (d.kind == nkind.N_FNDECL) { + wwiattrs(fd, d); wputs(fd, "export fn "); wputs(fd, d.str); wputb(fd, '('); diff --git a/selfhost/cmd/wcc/wwi.ww b/selfhost/cmd/wcc/wwi.ww index 46d31de8..928b2bf5 100644 --- a/selfhost/cmd/wcc/wwi.ww +++ b/selfhost/cmd/wcc/wwi.ww @@ -349,8 +349,42 @@ fn wwitype(fd: i32, t: *node) void = { };};};};};};};};};};}; }; +// Only codegen/link-relevant attributes round-trip into the `.wwi`. Today +// that is exactly @symbol (the FFI link-symbol override, read back at cgen +// fficollect — dropping it makes sep-compile emit `CALL malloc` for a +// `@symbol("rt_malloc")` fn). Named for the class so @align/@offset would +// slot in here IF ww ever grows field-layout attributes — it has none +// today (task #47 report). @test never reaches a `.wwi` (test fns are not +// export-marked), so it needs no exclusion arm. +fn wwiattrrelevant(nm: str) bool = { + return streq(nm, "symbol"); +}; + +fn wwiattrs(fd: i32, d: *node) void = { + let a: *node = d.attr; + for (a != nil) { + if (a.kind == nkind.N_ATTR && wwiattrrelevant(a.str)) { + wputb(fd, '@'); + wputs(fd, a.str); + if (a.list != nil) { + wputb(fd, '('); + let arg: *node = a.list; + for (arg != nil) { + if (arg != a.list) { wputs(fd, ", "); }; + wwiexpr(fd, arg); + arg = arg.next; + }; + wputb(fd, ')'); + }; + wputb(fd, 32u8); + }; + a = a.next; + }; +}; + fn wwidecl(fd: i32, d: *node) void = { if (d.kind == nkind.N_FNDECL) { + wwiattrs(fd, d); wputs(fd, "export fn "); wputs(fd, d.str); wputb(fd, '('); diff --git a/test/wcc/989_m2wwi_run.c b/test/wcc/989_m2wwi_run.c index 7171c8e7..5c59cb2f 100644 --- a/test/wcc/989_m2wwi_run.c +++ b/test/wcc/989_m2wwi_run.c @@ -77,6 +77,17 @@ slurp(const char *path, char **outbuf, size_t *outlen) return 0; } +static int +file_contains(const char *path, const char *needle) +{ + char *b = NULL; + size_t n = 0; + if (slurp(path, &b, &n) < 0) return -1; + int found = (strstr(b, needle) != NULL); + free(b); + return found ? 0 : 1; +} + static int files_eq(const char *a, const char *b) { @@ -246,7 +257,11 @@ static const char *synth_src = "export fn risky() !i32;\n" "export fn matrix() [LIMIT]u8;\n" "export fn pair() (i32, i32);\n" - "export fn opt(p: *color, b: []u8) (i32 | void);\n"; + "export fn opt(p: *color, b: []u8) (i32 | void);\n" + /* #47: a codegen/link-relevant attribute (@symbol — the FFI link- + * symbol override) MUST round-trip into the `.wwi`; dropping it makes + * sep-compile emit `CALL ext` for a `@symbol("rt_ext")` fn. */ + "@symbol(\"rt_ext\") export fn ext(n: i32) i32;\n"; static int synth(const char *bin) @@ -288,6 +303,12 @@ synth(const char *bin) "/ decl-kind unparse diverges across stages)\n"); goto out; } + /* #47: the @symbol attribute must survive the round-trip verbatim. */ + if (file_contains(cs, "@symbol(\"rt_ext\") export fn ext") != 0) { + fprintf(stderr, "m2wwi FAIL: synth — .wwi dropped @symbol " + "(sep-compile would emit the wrong link symbol)\n"); + goto out; + } snprintf(cmd, sizeof cmd, "timeout 180 %s/wwdump -a %s >/dev/null 2>&1", bin, cs); if (runwait(cmd) != 0) {