Files
ww/test/wcc/706_fnlabel_mangle.c
Hojun-Cho a95a7a316b test/wcc: carrier ownership repair and driver-contract adaptation
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.
2026-08-07 23:21:04 +09:00

159 lines
4.9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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;
}