Files
ww/test/wcc/949_aggret_source_run.c
Hojun-Cho 0d39129741 w6c+wwstage: aggregate return from any addressable source (#272 commit-1)
The N_RETURN aggregate arms gated the return source on N_IDENT ||
N_STRUCTLIT; every other aggregate rvalue (array literal, o.field N_DOT,
a[i] N_INDEX, *p deref) fell through to the scalar-AX default = a silent
8-byte truncation. Both stages emitted byte-IDENTICAL wrong asm, so the
byte-id gate could not catch it (#263 class) — the fix converges on the
runtime oracle.

Mirror the arg-side closure #271 landed: both arms (≤24B @retscr and
>24B sret) now funnel N_ARRLIT through the literal element fill and
N_DOT/N_INDEX/deref through aggarg_srcaddr + the #265/#268 whole-
aggregate copy. Type-agnostic, so struct AND array returns are closed.
A close-by-construction loud-stop (rule 7) guards any future unhandled
aggregate source from reaching the scalar default.

Closes the callee-half of (b)/(c) and the addressable siblings. The
g = mk() global-receive caller-half is commit-2.

949_aggret_source_run pins the class: array-literal / N_DOT / N_INDEX /
deref / named-ident control / >24B-sret-deref / struct-field / struct-
deref, each summing all members (full readback) with per-row byte-id.
2026-06-02 14:52:44 +09:00

225 lines
7.4 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 },
{ 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;
}