Files
ww/test/wcc/949_aggret_source_run.c
Hojun-Cho 9d81ba77b7 w6c+wwstage: array global-aggregate-receive g = f() (#272 commit-2)
The caller-half of the global case: `g = mk()` into a GLOBAL array
stored only the first word — a ≤24B reg-return landed `MOVQ AX, g(SB)`
(8 of 24 bytes); a >24B sret-return hit the #220 sret-to-symbol gate
which was TY_STRUCT-only and fell through to the same truncation.

≤24B: the local aggregate-receive arm was `off != 0`-only, so a global
array fell to the scalar IDENT store. Add a global ARRAY arm — LEAQ
name(SB), DI then store the full+tail words from AX/DX/CX (an array is
never float-class, so AX/DX/CX is always the transport; no `g+8(SB)`
operand form exists). Mirrors the str/slice global arm.
>24B: add TY_ARRAY to the #220 sret-to-symbol gate (cg_sret_dest_sym /
sretdestnode) — the callee writes the whole array through RDI.

A ≤24B STRUCT global receive can be float-class (X0/X1, not AX/DX/CX),
so it is left at its pre-existing symmetric behaviour — no consumer.

949_aggret_source_run gains global_recv (c → 15) and global_recv_sret
(>24B → 22), both with per-row byte-id.
2026-06-02 15:00:55 +09:00

241 lines
8.1 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 949_aggret_source_run — runtime + byte-id net for #272: aggregate
* return-by-value from every ADDRESSABLE source shape, not just the
* #267 N_IDENT / N_STRUCTLIT pair.
*
* Root: the N_RETURN aggregate arms gated the return source on
* N_IDENT || N_STRUCTLIT. Every OTHER aggregate rvalue — an array
* literal (`return [..]`), a struct/array field (`return o.f`, N_DOT),
* an array element (`return a[i]`, N_INDEX), a deref (`return *p`) —
* fell through to the scalar-AX default below = a silent truncation to
* the first 8 bytes. Both stages emitted byte-IDENTICAL wrong asm
* (the byte-id gate alone could NOT catch it — #263 in its purest
* form), so each row asserts the RUNTIME value (full readback: every
* member summed, so a dropped word fails) AND cs==ww byte-id.
*
* Fix (#272 commit-1): both N_RETURN arms (≤24B @retscr and >24B sret)
* funnel N_ARRLIT through the literal element fill and N_DOT/N_INDEX/
* deref through aggarg_srcaddr + the #265/#268 whole-aggregate copy —
* the return mirror of the arg-side closure #271 landed. Closes the
* class for struct AND array returns; a close-by-construction loud-stop
* guards any future unhandled shape from reaching the scalar default.
*
* Rows: source-kind {array-literal, struct field (N_DOT), array element
* (N_INDEX), deref (*p), named-ident control, >24B deref (sret arm),
* struct field, struct deref} × return. Each callee returns an
* aggregate whose members the caller sums in full.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.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; int want_exit; };
static const struct row rows[] = {
/* array literal — `return [1,2,3]` (N_ARRLIT, ≤24B @retscr). */
{ "arrlit",
"package main;\n"
"fn mk() [3]i64 = { return [1i64, 2i64, 3i64]; };\n"
"export fn main() i32 = { let r: [3]i64 = mk();\n"
" return (r[0]+r[1]+r[2]): i32; };\n", 6 },
/* struct array-field — `return o.a` (N_DOT, ≤24B). */
{ "dot_arrfield",
"package main;\n"
"type box = struct { a: [3]i64 };\n"
"fn mk() [3]i64 = { let o: box = box { a = [10i64, 20i64, 30i64] };\n"
" return o.a; };\n"
"export fn main() i32 = { let r: [3]i64 = mk();\n"
" return (r[0]+r[1]+r[2]): i32; };\n", 60 },
/* array element — `return g2[1]` (N_INDEX, ≤24B). Source is a
* fully-init global 2D array (the element-store path is a separate
* #270/#273 item; the global static-init is wired). */
{ "index_elem",
"package main;\n"
"let g2: [2][3]i64 = [[1i64,2i64,3i64],[4i64,5i64,6i64]];\n"
"fn mk() [3]i64 = { return g2[1]; };\n"
"export fn main() i32 = { let r: [3]i64 = mk();\n"
" return (r[0]+r[1]+r[2]): i32; };\n", 15 },
/* deref — `return *p` (N_UN TK_STAR, ≤24B). */
{ "deref",
"package main;\n"
"fn mk(p: *[3]i64) [3]i64 = { return *p; };\n"
"export fn main() i32 = { let a: [3]i64 = [7i64,8i64,9i64];\n"
" let r: [3]i64 = mk(&a); return (r[0]+r[1]+r[2]): i32; };\n", 24 },
/* named-ident CONTROL — `return a` (N_IDENT, pre-#272 path). */
{ "ident_ctl",
"package main;\n"
"fn mk() [3]i64 = { let a: [3]i64 = [2i64,4i64,6i64]; return a; };\n"
"export fn main() i32 = { let r: [3]i64 = mk();\n"
" return (r[0]+r[1]+r[2]): i32; };\n", 12 },
/* >24B deref — [4]i64 (32B) rides the sret arm, not @retscr. */
{ "deref_sret",
"package main;\n"
"fn mk(p: *[4]i64) [4]i64 = { return *p; };\n"
"export fn main() i32 = { let a: [4]i64 = [1i64,2i64,3i64,4i64];\n"
" let r: [4]i64 = mk(&a);\n"
" return (r[0]+r[1]+r[2]+r[3]): i32; };\n", 10 },
/* struct field (N_DOT) returning a STRUCT (not array) — the
* type-agnostic addressable path covers both. */
{ "struct_dot",
"package main;\n"
"type pair = struct { a: i64, b: i64 };\n"
"type box = struct { p: pair };\n"
"fn mk() pair = { let bx: box = box { p = pair { a = 3i64, b = 4i64 } };\n"
" return bx.p; };\n"
"export fn main() i32 = { let r: pair = mk();\n"
" return (r.a + r.b): i32; };\n", 7 },
/* struct deref (*p) returning a STRUCT. */
{ "struct_deref",
"package main;\n"
"type pair = struct { a: i64, b: i64 };\n"
"fn mk(p: *pair) pair = { return *p; };\n"
"export fn main() i32 = { let x: pair = pair { a = 5i64, b = 9i64 };\n"
" let r: pair = mk(&x); return (r.a + r.b): i32; };\n", 14 },
/* #272 commit-2 caller-half: `g = mk()` into a GLOBAL array ≤24B.
* The receive must store the full AX/DX/CX, not the 8-byte AX
* truncation (g[1]/g[2] would read 0). */
{ "global_recv",
"package main;\n"
"let g: [3]i64 = [0i64, 0i64, 0i64];\n"
"fn mk() [3]i64 = { return [4i64, 5i64, 6i64]; };\n"
"export fn main() i32 = { g = mk();\n"
" return (g[0]+g[1]+g[2]): i32; };\n", 15 },
/* global receive into a >24B array — the sret-to-symbol path. */
{ "global_recv_sret",
"package main;\n"
"let g: [4]i64 = [0i64,0i64,0i64,0i64];\n"
"fn mk(p: *[4]i64) [4]i64 = { return *p; };\n"
"export fn main() i32 = { let a: [4]i64 = [4i64,5i64,6i64,7i64];\n"
" g = mk(&a); return (g[0]+g[1]+g[2]+g[3]): i32; };\n", 22 },
{ NULL, NULL, 0 }
};
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, "aggret_source: w6c_ww missing — cannot run "
"the cs==ww byte-id gate (the whole point of this test)\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/wwags_%d_%d.ww", getpid(), i);
FILE *f = fopen(src, "wb");
if (f == NULL) { fail++; continue; }
fputs(rows[i].src, f);
fclose(f);
char tmpdir[64];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwags_%d_d_%d",
getpid(), i);
mkdir(tmpdir, 0755);
char cmd[2048];
snprintf(cmd, sizeof cmd, "cd %s && %s/ww build %s",
tmpdir, bin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: cstage build failed\n",
rows[i].label);
fail++;
unlink(src); rmdir(tmpdir);
continue;
}
char outbin[128];
const char *base = strrchr(src, '/');
base = base ? base + 1 : src;
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
char *dot = strrchr(outbin, '.');
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
int got = runwait(outbin);
if (got != rows[i].want_exit) {
fprintf(stderr, "row[%s]: cstage exit %d, want %d\n",
rows[i].label, got, rows[i].want_exit);
fail++;
}
unlink(outbin); rmdir(tmpdir);
char cs_s[64], ws_s[64];
snprintf(cs_s, sizeof cs_s, "/tmp/wwags_%d_%d_cs.s",
getpid(), i);
snprintf(ws_s, sizeof ws_s, "/tmp/wwags_%d_%d_ww.s",
getpid(), i);
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
w6c, cs_s, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label);
fail++; unlink(src); continue;
}
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 failed\n",
rows[i].label);
fail++; unlink(src); unlink(cs_s); continue;
}
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 aggret-source tests failed\n",
fail, n);
return 1;
}
printf("aggret_source: %d/%d ok (cstage run + cs==ww byte-id)\n",
n, n);
return 0;
}