Files
ww/test/wcc/919_array_static_init_run.c
Hojun-Cho 9e3bc4ea37 wcc: array static-init let/def DATA emit via SSoT helper (#129 A.3)
Extract emit_array_data + emit_array_lit_bytes helpers (both stages,
mirrored) for module-level let/def with N_ARRLIT initializer or no-rhs
zero-init. Two-pass validate-then-emit: validate pass walks elements
and fails atomically on any non-foldable element (no partial-byte
emit on failure); emit pass writes element bytes after success.
Element-kind dispatch: integer via fold_int_literal byte-for-byte
preserved from pre-A.3 inline arm (bootstrap NEUTRAL — 6 live consumers
in lib/os/bufio/strings/encoding-utf8/strconv-stof_data), float via
inline bitcast + sign-XOR byte-loop (A.1 shape, no INT64_MIN — sibling
#144), struct via recursion into emit_struct_lit_bytes (A.2 helper).
Out-of-scope element kinds (ptr-elem, nested-array) rule-7 fatal.

emit_struct_lit_bytes gains TY_ARRAY field arm calling emit_array_lit_
bytes recursively — closes A.2 parked shape-15 (array-in-struct
`def D: dt = dt{tag=42, buf=[1u8,2u8,3u8,4u8]};`).

LOAD-side widened symmetric to A.2 precedent: cstage cgindex N_INDEX
direct-ident isglobal gate widened via new DefArray registry
(def_isarraydef populated in let_collect parallel to DefStruct);
wwstage cgindex N_INDEX falls through to defvartnode on letvartnode nil
(reads defent.dtnode field added in A.2). Both stages materialise
array-def via LEAQ name(SB) same as array-let.

Mid-impl rule-7 stop: refactor initially routed only rhs==N_ARRLIT
through emitarraydata, leaving nil-rhs zero-init arrays (e.g.
`let f64tos_buf: [64]u8;` in lib/strconv) silently SKIPPED → undef-ref
at link of wwstage-rebuilt selfhost binaries. Caught on first gate run
via bootstrap 994/995 RED. Fixed by adding nil-rhs branch to
emitarraydata (zero-fills arrt.size bytes) + widening wwstage caller
to route both N_ARRLIT and nil through helper. Same-class-lower-stratum
pattern (recurring across A.1 N_UN-peel, A.2 sz==8-short-circuit, A.3
nil-rhs-drop); banked as feedback memory.

Test 919 (11 rows: int-elem 1B/4B/8B + signed-N_UN-peel + float-elem
f64/f32 + def-int / def-float / struct-with-array-field shape-15 +
explicit-zero + single-elem-regression) registered. Make test:
182/182 incl. 990-997 byte-id + combined_ww_fresh.

Followups filed:
- #43 — wwstage emitletdataw str/slice-size arms lack !isarr guards;
  hypothetical no-rhs [16/24]u8 triple-emits (NOT A.3-introduced;
  no live consumer; 2-line parity fix)
2026-05-27 05:56:24 +09:00

261 lines
8.8 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.
/*
* 919_array_static_init_run — runtime + byte-id net for #129 Phase
* A.3: module-level let/def with array initializer.
*
* Pre-A.3 state:
* - Int-element arrays (u8/i8/u16/u32/u64/i32 etc) already worked
* in both stages via fold_int_literal.
* - Float-element arrays ([N]f64, [N]f32) → undef-ref at link
* (emit_lets array arm fold_int_literal fails on FLOATLIT).
* - Array DEFs (def A: [N]T = [...]) → emit_defs no array arm
* (storage missing) AND cgindex broken (reads LEAQ (BP), BX —
* stack frame, not data section).
* - Array-in-struct field (`def D: dt = dt{tag=42, buf=[...]}`) →
* #129 A.2 rule-7 fatal "array field rhs not foldable" — the
* shape parked in A.2 awaiting A.3.
*
* Phase A.3 fix (mirror A.1/A.2 SSoT-helper precedent):
* - cstage: emit_array_data + emit_array_lit_bytes helpers with
* element-kind dispatch (int via fold_int_literal preserving
* bootstrap byte-id, float via inline bitcast + sign-XOR byte-
* loop mirror of A.1, struct via emit_struct_lit_bytes recursion
* mirror of A.2). Two-pass validate-then-emit avoids partial-byte
* corruption on rhs-fold-failure.
* - cstage: emit_lets array arm routes through helper; emit_defs
* gains array arm.
* - cstage: DefArray registry + def_isarraydef populated in
* let_collect; cgindex N_INDEX direct-ident `isglobal` gate
* widened to (let_islet || def_isarraydef).
* - cstage: emit_struct_lit_bytes (A.2 helper) gains TY_ARRAY field
* arm calling emit_array_lit_bytes recursively (closes A.2 parked
* shape 15).
* - wwstage: parallel emitarraydata + emitarraylitbytes; emitstruct
* litbytes TY_ARRAY arm; defvartnode helper; cgindex falls through
* to defvartnode after letvartnode nil.
*
* Bootstrap RISK: live consumers in lib/os, lib/bufio, lib/strings,
* lib/encoding/utf8 (dfa + masks), lib/strconv/stof_data
* (left_shift_table). All use typed-int-literal elements; the int-elem
* helper path is byte-for-byte preserved → bootstrap NEUTRAL.
*
* Rows (size strata 1B/2B/4B/8B × count strata 1/2/4 × int/float/struct/
* empty/no-rhs/def-variant, avoiding the 16B-evade pattern from A.2):
*
* - (a) `let A: [4]u8 = [1u8, 2u8, 3u8, 4u8]` — 1B regression
* - (b) `let A: [4]u32 = [1u32, 2u32, 3u32, 4u32]` — 4B regression
* - (c) `let A: [2]u64 = [1u64, 2u64]` — 8B regression
* - (d) `let A: [4]i32 = [-1, -2, -3, -4]` — N_UN peel regression
* - (e) `let A: [4]f64 = [1.5, -2.5, 3.5, -4.5]` — NEW float-elem
* - (f) `let A: [4]f32 = [1.5f32, -2.5f32, 3.5f32, -4.5f32]` — NEW
* f32 narrow + sign-XOR per element
* - (g) `def A: [4]u32 = [11u32, 22u32, 33u32, 44u32]` — NEW
* def-storage + LOAD-widening
* - (h) `def A: [4]f64 = [1.5, 2.5, 3.5, 4.5]` — NEW def-variant of
* float
* - (i) `def D: dt = dt{tag=42, buf=[1u8,2u8,3u8,4u8]}` — NEW
* closes A.2 shape 15 (struct-with-array-field)
* - (j) `let A: [4]u8 = [0u8, 0u8, 0u8, 0u8]` — explicit-zero
* regression
* - (k) `let A: [1]u8 = [0u8]` — single-elem (matches lib/os/
* emptypath pattern)
*
* Each row: cstage `ww build` + run asserting exit code + w6c vs
* w6c_ww `.s` cmp (rule-10 byte-id).
*
* Deferred:
* - Pointer-element arrays `[N]*T = [&G, &H]` — needs DATAR per
* element (own task/fold).
* - Nested arrays `[N][M]T` — no current consumer.
* - Bare-int `[N]u8 = [1, 2, 3, 4]` — #130 (checker issue).
* - Partial init `[4]u8 = [1u8]` — checker rejects (parser/checker
* decision).
*/
#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[] = {
{ "let_u8_arr",
"package main;\n"
"let A: [4]u8 = [1u8, 2u8, 3u8, 4u8];\n"
"export fn main() i32 = { return A[0]: i32; };\n", 1 },
{ "let_u32_arr",
"package main;\n"
"let A: [4]u32 = [1u32, 2u32, 3u32, 4u32];\n"
"export fn main() i32 = { return A[0]: i32; };\n", 1 },
{ "let_u64_arr",
"package main;\n"
"let A: [2]u64 = [1u64, 2u64];\n"
"export fn main() i32 = { return A[0]: i32; };\n", 1 },
{ "let_i32_neg_arr",
"package main;\n"
"let A: [4]i32 = [-1, -2, -3, -4];\n"
"export fn main() i32 = { return A[0]; };\n", 255 /* -1 */ },
/* Float-element array — NEW in A.3. Includes both signs to exercise
* the sign-XOR byte-loop per element. */
{ "let_f64_arr",
"package main;\n"
"let A: [4]f64 = [1.5, -2.5, 3.5, -4.5];\n"
"export fn main() i32 = { return (A[0]: i32); };\n", 1 },
{ "let_f32_arr",
"package main;\n"
"let A: [4]f32 = [1.5f32, -2.5f32, 3.5f32, -4.5f32];\n"
"export fn main() i32 = { return (A[0]: i32); };\n", 1 },
/* def-variant exercises the LOAD-widening (def_isarraydef) at
* cgindex/cgident. Storage emit also new. */
{ "def_u32_arr",
"package main;\n"
"def A: [4]u32 = [11u32, 22u32, 33u32, 44u32];\n"
"export fn main() i32 = { return A[0]: i32; };\n", 11 },
{ "def_f64_arr",
"package main;\n"
"def A: [4]f64 = [1.5, 2.5, 3.5, 4.5];\n"
"export fn main() i32 = { return (A[0]: i32); };\n", 1 },
/* The A.2-parked shape-15 — closes via emit_struct_lit_bytes
* TY_ARRAY field arm. */
{ "let_struct_with_arr_field",
"package main;\n"
"type dt = struct { tag: i32, buf: [4]u8 };\n"
"let D: dt = dt{tag=42, buf=[1u8, 2u8, 3u8, 4u8]};\n"
"export fn main() i32 = { return D.tag; };\n", 42 },
{ "let_u8_zero_arr",
"package main;\n"
"let A: [4]u8 = [0u8, 0u8, 0u8, 0u8];\n"
"export fn main() i32 = { return A[0]: i32; };\n", 0 },
{ "let_u8_single",
"package main;\n"
"let A: [1]u8 = [7u8];\n"
"export fn main() i32 = { return A[0]: i32; };\n", 7 },
{ 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, "arrinit: 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/wwari_%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/wwari_%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/wwari_%d_%d_cs.s",
getpid(), i);
snprintf(ws_s, sizeof ws_s, "/tmp/wwari_%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 array-static-init tests failed\n", fail, n);
return 1;
}
printf("arrinit: %d/%d ok (cstage run + cs==ww byte-id)\n",
n, n);
return 0;
}