/* * 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 #include #include static int runwait(const char *cmd) { int rc = system(cmd); if (rc == -1) return -1; return rc; } 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); /* (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); fail++; unlink(src); continue; } /* (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); fail++; unlink(src); unlink(cs_s); continue; } /* (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); fail++; } unlink(src); unlink(cs_s); unlink(ws_s); } 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; }