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.
168 lines
5.6 KiB
C
168 lines
5.6 KiB
C
/*
|
|
* 798_tuple_sret_callee — project #10 Fold A (wide tuple-return / sret,
|
|
* CALLEE side). An over-capacity tuple return (> 4 integer eightbytes or
|
|
* > 2 SSE eightbytes) used to LOUD-STOP at the N_RETURN SEND site
|
|
* ("tuple return exceeds ... register-return ABI capacity"). Fold A makes
|
|
* the CALLEE emit such a return via the existing sret skeleton: the
|
|
* prologue wires @sretarg (the caller-prealloc dest in RDI), and the
|
|
* return stores each element through *(@sretarg) at its packed layout
|
|
* offset, then returns @sretarg in RAX.
|
|
*
|
|
* THIS TEST IS COMPILE + BYTE-ID ONLY (no runtime row):
|
|
* (a) w6c (cstage) AND w6c_ww (wwstage) must now COMPILE the over-cap
|
|
* tuple-returning fn — no loud-stop. The pre-Fold-A behavior was a
|
|
* hard error; a green compile here proves the SEND emits sret.
|
|
* (b) the two .s outputs must be BYTE-IDENTICAL (rule-10). The classifier
|
|
* (cg_sret_retsize / sretretsize) and the SEND emitter share a single
|
|
* cap SSoT, so both stages classify + lay out the tuple the same way.
|
|
*
|
|
* WHY NO RUNTIME ROW: the CALL/receive side stays deliberately loud-stopped
|
|
* — an N_MLET/N_MASSIGN destructure of an over-cap tuple still aborts
|
|
* ("tuple destructure exceeds ... capacity"), so the over-cap callee is not
|
|
* yet usefully callable. The end-to-end round-trip (allocate dest, call,
|
|
* read the elements back) arrives with Fold B (#10-B), which wires the
|
|
* receive. So this fn is compiled but never called/destructured here.
|
|
*
|
|
* ROWS exercise the layout arithmetic the SEND must get right:
|
|
* - ([]u8, []u8) 6 GP eightbytes, all-wide, offsets 0 / 24.
|
|
* - (str, str) 6 GP, str IS []u8 (24B header), offsets 0 / 24.
|
|
* - (str, str, i32) 7 GP; the trailing narrow i32 must store MOVL at
|
|
* its NATURAL packed offset 48 (#169), not over-MOVQ.
|
|
* - (f64, f64, f64) 3 SSE eightbytes > the 2-wide SSE cap.
|
|
*
|
|
* GATE POLARITY: must stay GREEN. A non-zero compile means the SEND
|
|
* loud-stop regressed (Fold A reverted); a byte-id FAIL means cstage and
|
|
* wwstage diverged on the sret classification or the element layout
|
|
* (rule-10 violation / cap-SSoT drift).
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <unistd.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;
|
|
}
|
|
|
|
struct row { const char *label; const char *src; };
|
|
|
|
static const struct row rows[] = {
|
|
{ "two_slices",
|
|
"package main;\n"
|
|
"export fn f(a: []u8, b: []u8) ([]u8, []u8) = { return (a, b); };\n" },
|
|
{ "two_str",
|
|
"package main;\n"
|
|
"export fn f(a: str, b: str) (str, str) = { return (a, b); };\n" },
|
|
{ "str_str_i32",
|
|
"package main;\n"
|
|
"export fn f(a: str, b: str, n: i32) (str, str, i32) = {\n"
|
|
" return (a, b, n);\n"
|
|
"};\n" },
|
|
{ "three_f64",
|
|
"package main;\n"
|
|
"export fn f(x: f64, y: f64, z: f64) (f64, f64, f64) = {\n"
|
|
" return (x, y, z);\n"
|
|
"};\n" },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
static int
|
|
slurp_eq(const char *a, const char *b)
|
|
{
|
|
FILE *fa = fopen(a, "rb");
|
|
FILE *fb = fopen(b, "rb");
|
|
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
|
|
int rc = 0;
|
|
for (;;) {
|
|
int ca = fgetc(fa);
|
|
int cb = fgetc(fb);
|
|
if (ca != cb) { rc = -1; break; }
|
|
if (ca == EOF) break;
|
|
}
|
|
fclose(fa); fclose(fb);
|
|
return rc;
|
|
}
|
|
|
|
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 w6c[1100], w6c_ww[1100];
|
|
snprintf(w6c, sizeof w6c, "%s/w6c", bin);
|
|
snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin);
|
|
if (access(w6c_ww, X_OK) != 0) {
|
|
fprintf(stderr, "tuple_sret_callee: w6c_ww missing — cannot run "
|
|
"the cs==ww byte-id gate\n");
|
|
return 1;
|
|
}
|
|
|
|
int n = 0, fail = 0;
|
|
for (int i = 0; rows[i].src; i++, n++) {
|
|
char src[64];
|
|
snprintf(src, sizeof src, "/tmp/wwtsc_%d_%d.ww", getpid(), i);
|
|
FILE *f = fopen(src, "wb");
|
|
if (f == NULL) { fail++; continue; }
|
|
fputs(rows[i].src, f);
|
|
fclose(f);
|
|
|
|
char cs_s[64], ws_s[64], cmd[2048];
|
|
snprintf(cs_s, sizeof cs_s, "/tmp/wwtsc_%d_%d_cs.s", getpid(), i);
|
|
snprintf(ws_s, sizeof ws_s, "/tmp/wwtsc_%d_%d_ww.s", getpid(), i);
|
|
int rowfail = 0;
|
|
|
|
/* (a) cstage must COMPILE the over-cap tuple callee (no loud-stop). */
|
|
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, cs_s, src);
|
|
if (runwait(cmd) != 0) {
|
|
fprintf(stderr, "row[%s]: w6c loud-stopped (Fold A regressed)\n",
|
|
rows[i].label);
|
|
rowfail = 1;
|
|
goto rowcleanup;
|
|
}
|
|
/* (a') wwstage must compile it too. */
|
|
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c_ww, ws_s, src);
|
|
if (runwait(cmd) != 0) {
|
|
fprintf(stderr, "row[%s]: w6c_ww loud-stopped (Fold A regressed)\n",
|
|
rows[i].label);
|
|
rowfail = 1;
|
|
goto rowcleanup;
|
|
}
|
|
/* (b) cs==ww byte-id gate. */
|
|
if (slurp_eq(cs_s, ws_s) != 0) {
|
|
fprintf(stderr, "row[%s]: cstage/wwstage .s DIFFER "
|
|
"(rule-10 byte-id violation)\n", rows[i].label);
|
|
rowfail = 1;
|
|
}
|
|
rowcleanup:
|
|
/* a failed leg may still have written a partial .s; ENOENT is
|
|
* the leg-never-wrote case, any other cleanup failure must not
|
|
* silently leak. */
|
|
if (unlink(src) != 0 && errno != ENOENT) { perror(src); rowfail = 1; }
|
|
if (unlink(cs_s) != 0 && errno != ENOENT) { perror(cs_s); rowfail = 1; }
|
|
if (unlink(ws_s) != 0 && errno != ENOENT) { perror(ws_s); rowfail = 1; }
|
|
if (rowfail)
|
|
fail++;
|
|
}
|
|
|
|
if (fail) {
|
|
fprintf(stderr, "%d/%d tuple-sret-callee tests failed\n", fail, n);
|
|
return 1;
|
|
}
|
|
printf("tuple_sret_callee: %d/%d ok (compile + cs==ww byte-id)\n", n, n);
|
|
return 0;
|
|
}
|