Files
ww/test/wcc/798_tuple_sret_callee.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

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;
}