/* * 989_globfloatstructarg_run — F8-c9 (report-item #31, the EXCEPTION): * passing a module-GLOBAL float-bearing struct by value must drain its float * eightbytes into the SSE arg regs (X0..), not all-GP. * * THE BUG (cat-A silent miscompile, align-UP): cgcall's per-arg drain decides * the SysV eightbyte class via structfloatclass, but read it from the local * slot's tnode ONLY (`if (lc != nil) stfc = structfloatclass(lc.tnode)`). A * module-global struct arg (lc==nil) kept stfc=0, so the float eightbytes * drained as integers (POPQ into DI/SI) and X0 was never loaded — the callee * read its f64 fields from the wrong registers. cstage classifies off the * operand TYPE, so it loaded X0 and ran correct — the cat-A divergence. * * THIS IS THE EXCEPTION among the F8 members: the bug is a float-CLASS * decision keyed to a local-only slot, NOT an address-routing fallback — an * F7-flavoured stamp/type-keying fix. THE FIX: for a global ident, key * structfloatclass off the global's declared tnode (letvartnode), the same * classification the local path uses. align ww UP; the .s is byte-identical. * * Rows (build+run on cstage `ww` and wwstage `ww_ww`; rule-10 — agree+hit; * each take() returns 0 iff every field round-trips through the call): * row | shape | want * ----------------+----------------------------------+----- * float_first | gp:{f:f64,i:i64}={1.5,9}; take | 0 [#31: f→X0] * int_first | gp:{i:i64,f:f64}={9,2.5}; take | 0 [int eb→GP, f→X0] * both_float | gp:{a:f64,b:f64}={1.5,2.5}; take | 0 [two SSE eightbytes] */ #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; } struct row { const char *label; const char *src; int want_exit; }; static const struct row rows[] = { { "float_first", "package main;\n" "type pair = struct { f: f64, i: i64 };\n" "let gp: pair = pair { f = 1.5, i = 9 };\n" "fn take(p: pair) int = { if (p.f == 1.5 && p.i == 9) { return 0; }; return 2; };\n" "export fn main() int = { return take(gp); };\n", 0 }, { "int_first", "package main;\n" "type pair = struct { i: i64, f: f64 };\n" "let gp: pair = pair { i = 9, f = 2.5 };\n" "fn take(p: pair) int = { if (p.i == 9 && p.f == 2.5) { return 0; }; return 2; };\n" "export fn main() int = { return take(gp); };\n", 0 }, { "both_float", "package main;\n" "type pp = struct { a: f64, b: f64 };\n" "let gp: pp = pp { a = 1.5, b = 2.5 };\n" "fn take(p: pp) int = { if (p.a == 1.5 && p.b == 2.5) { return 0; }; return 2; };\n" "export fn main() int = { return take(gp); };\n", 0 }, }; /* run_build — build+run `src` via `driver`; returns the binary's exit * code, or -1 on a build failure. */ static int run_build(const char *driver, const struct row *r, int i) { char src[128], tmpdir[64], outbin[128], rmcmd[160], cmd[1024]; snprintf(tmpdir, sizeof tmpdir, "/tmp/gfsa_%d_d_%d", getpid(), i); mkdir(tmpdir, 0755); snprintf(src, sizeof src, "%s/gfsa_%d_%d.ww", tmpdir, getpid(), i); snprintf(outbin, sizeof outbin, "%s/gfsa_%d_%d", tmpdir, getpid(), i); snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); FILE *f = fopen(src, "wb"); if (!f) { runwait(rmcmd); return -2; } fputs(r->src, f); fclose(f); snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null", driver, outbin, src); int brc = runwait(cmd); int got = -1; if (brc == 0) got = runwait(outbin); runwait(rmcmd); return brc == 0 ? got : -1; } 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], wdrv[1024]; snprintf(cdrv, sizeof cdrv, "%s/ww", bin); snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); struct { const char *name; const char *drv; int gated; } drivers[] = { { "cstage", cdrv, 0 }, { "wwstage", wdrv, 1 }, { NULL, NULL, 0 }, }; int n = (int)(sizeof rows / sizeof rows[0]); int total = 0, fail = 0; for (int d = 0; drivers[d].name; d++) { if (drivers[d].gated && access(drivers[d].drv, X_OK) != 0) { fprintf(stderr, "globfloatstructarg_run: skip %s (no %s)\n", drivers[d].name, drivers[d].drv); continue; } for (int i = 0; i < n; i++) { total++; int got = run_build(drivers[d].drv, &rows[i], i); if (got != rows[i].want_exit) { fprintf(stderr, "globfloatstructarg_run[%s][%s]: " "exit=%d want=%d\n", drivers[d].name, rows[i].label, got, rows[i].want_exit); fail++; } } } if (fail) { fprintf(stderr, "globfloatstructarg_run: %d/%d fixtures failed\n", fail, total); return 1; } printf("globfloatstructarg_run: %d/%d ok\n", total, total); return 0; }