Every surviving carrier now owns its artifacts: checked mkdir/mkdtemp/ fopen acquisition, one all-exit cleanup funnel per carrier, ENOENT- tolerant checked unlinks, exact-path deletion (rm -rf only for an owned pid-keyed dir or a .sepwork beneath one), and cleanup failure fails a passing carrier without overwriting its diagnostic. In the same pass the carriers adapt to the driver contract this branch lands: --sep and WW_PKGCACHE are gone, -S and the /tmp/ww_run_<pid> scratch contract are asserted, and rows whose runtime or reject coverage moved to test/wcc/data fixtures or test/lang @test owners are trimmed to the byte/artifact/diagnostic observations only they can make. Repair and adaptation ride together because most files interleave both in the same hunks; splitting would manufacture intermediate carrier states that never existed and cannot run against either driver.
159 lines
4.9 KiB
C
159 lines
4.9 KiB
C
/*
|
||
* 706_fnlabel_mangle — cgen mangles fn labels by module so two
|
||
* modules can each `export fn ping` (and each define a private
|
||
* `fn helper`) without colliding at link time.
|
||
*
|
||
* The fixture in test/wcc/data/fnlabelmangle/ pins all four
|
||
* label-emit sites #9 / #12 touch:
|
||
* mod{1,2}/mod{1,2}.ww
|
||
* export fn ping = bareval + helper() + fpi();
|
||
* fn helper — private, same-leaf across modules
|
||
* fn fpi — `let h = helper; h();` pins LEAQ N_IDENT
|
||
* pos.ww
|
||
* let p1 = mod1.ping; let p2 = mod2.ping; // LEAQ N_DOT × 2
|
||
* return mod1.ping() + mod2.ping() // CALL N_DOT × 2
|
||
* + p1() + p2(); // through fn ptr
|
||
*
|
||
* mod1.ping = 3 + 11 + 11 = 25
|
||
* mod2.ping = 5 + 13 + 13 = 31
|
||
* total = 25 + 31 + 25 + 31 = 112
|
||
*
|
||
* Pre-#9 (cgen emitted bare `TEXT ping` for both modules' exports),
|
||
* the linker collapsed both `ping` symbols and one dispatch landed
|
||
* on the wrong body. The bare-IDENT helper / `let h = helper` call-
|
||
* sites would also pick whichever module the lookup found first,
|
||
* silently miscompiling fpi's intra-module call.
|
||
*
|
||
* Pre-#12 wwstage cgdot's `let p = mod.ping` fell through to the
|
||
* MOVQ leaf(SB) module-qualified-value fallback, loading 8 bytes
|
||
* of fn-prologue into AX; `p()` then jumped into mid-prologue
|
||
* garbage. Cstage cgdot already had a TY_FN LEAQ branch from #9.
|
||
*
|
||
* Coverage notes:
|
||
* - The skip rule's three remaining cases (@symbol / main / empty-
|
||
* module) are not pinned by a dedicated row because every passing
|
||
* run of `make test` already exercises them: the bootstrap and
|
||
* stdlib pull through `@symbol("rt_syscall")` bindings (lib/fmt,
|
||
* lib/log), every test program links a bare `main`, and the inline-
|
||
* source tests (700, 701, ...) compile fixtures with no module
|
||
* directive. A regression in any of those rules would cascade
|
||
* across the suite, not show up here.
|
||
*
|
||
* Both stages run the positive case — cstage and wwstage cgen must
|
||
* agree on the mangling rule for ww2/ww3/ww4 byte-identity to hold.
|
||
*/
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <errno.h>
|
||
#include <unistd.h>
|
||
#include <sys/stat.h>
|
||
#include <sys/wait.h>
|
||
|
||
static int
|
||
runwait(const char *cmd)
|
||
{
|
||
int rc = system(cmd);
|
||
if (rc == -1) return -1;
|
||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||
return -1;
|
||
}
|
||
|
||
static int
|
||
run_pos(const char *driver, const char *fixdir, const char *tag)
|
||
{
|
||
/* Keep the output and caller-owned pos.sepwork outside the tracked
|
||
* fixture tree; cd <fixdir> stays so pos.ww imports siblings mod1/mod2. */
|
||
char td[1024];
|
||
snprintf(td, sizeof td, "/tmp/fnlbl_%d_%s", getpid(), tag);
|
||
if (mkdir(td, 0755) != 0) {
|
||
fprintf(stderr, "fnlabel_mangle[%s]: mkdir %s: %s\n",
|
||
tag, td, strerror(errno));
|
||
return 1;
|
||
}
|
||
char bin[2048], sepdir[2048], rmcmd[2112];
|
||
snprintf(bin, sizeof bin, "%s/pos", td);
|
||
snprintf(sepdir, sizeof sepdir, "%s.sepwork", bin);
|
||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepdir);
|
||
|
||
char cmd[2048];
|
||
int failed = 0, got;
|
||
snprintf(cmd, sizeof cmd,
|
||
"cd %s && %s build -o %s/pos pos.ww >/dev/null 2>&1",
|
||
fixdir, driver, td);
|
||
if (runwait(cmd) != 0) {
|
||
fprintf(stderr,
|
||
"fnlabel_mangle[%s]: pos.ww build failed\n", tag);
|
||
failed = 1;
|
||
goto cleanup;
|
||
}
|
||
got = runwait(bin);
|
||
if (got != 112) {
|
||
fprintf(stderr,
|
||
"fnlabel_mangle[%s]: pos.ww exit=%d want=112 — "
|
||
"fn labels likely collapsed at link, or LEAQ-of-fn "
|
||
"N_DOT branch missing in cgdot\n", tag, got);
|
||
failed = 1;
|
||
}
|
||
|
||
cleanup:
|
||
{
|
||
int cleanup_failed = 0;
|
||
if (runwait(rmcmd) != 0) {
|
||
fprintf(stderr, "fnlabel_mangle[%s]: remove %s failed\n",
|
||
tag, sepdir);
|
||
cleanup_failed = 1;
|
||
}
|
||
if (unlink(bin) != 0 && errno != ENOENT) {
|
||
fprintf(stderr, "fnlabel_mangle[%s]: unlink %s: %s\n",
|
||
tag, bin, strerror(errno));
|
||
cleanup_failed = 1;
|
||
}
|
||
if (rmdir(td) != 0) {
|
||
fprintf(stderr, "fnlabel_mangle[%s]: rmdir %s: %s\n",
|
||
tag, td, strerror(errno));
|
||
cleanup_failed = 1;
|
||
}
|
||
if (!failed && cleanup_failed) failed = 1;
|
||
}
|
||
return failed;
|
||
}
|
||
|
||
int
|
||
main(void)
|
||
{
|
||
const char *bin = getenv("BIN");
|
||
if (!bin) bin = "out/bin";
|
||
char absbin[1024];
|
||
if (bin[0] != '/') {
|
||
char cwd[1024];
|
||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||
bin = absbin;
|
||
}
|
||
|
||
char cdrv[1024];
|
||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||
char wdrv[1024];
|
||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||
|
||
char fixdir[1024];
|
||
if (getcwd(fixdir, sizeof fixdir) == NULL) return 1;
|
||
size_t cwd_n = strlen(fixdir);
|
||
const char *rel = "/test/wcc/data/fnlabelmangle";
|
||
if (cwd_n + strlen(rel) + 1 >= sizeof fixdir) return 1;
|
||
memcpy(fixdir + cwd_n, rel, strlen(rel) + 1);
|
||
|
||
int fail = 0;
|
||
fail += run_pos(cdrv, fixdir, "cstage");
|
||
fail += run_pos(wdrv, fixdir, "wwstage");
|
||
|
||
if (fail) {
|
||
fprintf(stderr,
|
||
"fnlabel_mangle: %d case(s) failed\n", fail);
|
||
return 1;
|
||
}
|
||
printf("fnlabel_mangle: 2/2 ok\n");
|
||
return 0;
|
||
}
|