/* * 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 three of the four * label-emit sites #9 touches: * 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 * return mod1.ping() + mod2.ping(); // CALL N_DOT × 2 * * mod1.ping = 3 + 11 + 11 = 25 * mod2.ping = 5 + 13 + 13 = 31 * total = 56 * * Pre-fix (cgen emitted bare `TEXT ping` for both modules' exports; * non-exported `helper` already mangled, so that half is incidental * regression coverage), the linker collapsed both `ping` symbols and * one dispatch landed on the wrong body. The bare-IDENT helper / * `let h = helper` callsites would also pick whichever module the * lookup found first, silently miscompiling fpi's intra-module call. * * Coverage notes: * - LEAQ N_DOT for fn rvalue (`let p = mod1.ping`) is not exercised. * wwstage cgdot has no working LEAQ-of-fn N_DOT branch — a row * would diverge across stages. Tracked as a follow-up. * - 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 #include #include #include #include #include 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) { char cmd[2048]; snprintf(cmd, sizeof cmd, "cd %s && %s build pos.ww >/dev/null 2>&1", fixdir, driver); if (runwait(cmd) != 0) { fprintf(stderr, "fnlabel_mangle[%s]: pos.ww build failed\n", tag); return 1; } char bin[2048]; snprintf(bin, sizeof bin, "%s/pos", fixdir); int got = runwait(bin); unlink(bin); if (got != 56) { fprintf(stderr, "fnlabel_mangle[%s]: pos.ww exit=%d want=56 — " "fn labels likely collapsed at link\n", tag, got); return 1; } return 0; } 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; }