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)
This commit is contained in:
6
Makefile
6
Makefile
@@ -341,6 +341,7 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \
|
||||
$(BIN)/test_arr_float_call_index_run \
|
||||
$(BIN)/test_def_float_lit_run \
|
||||
$(BIN)/test_struct_composite_init_run \
|
||||
$(BIN)/test_array_static_init_run \
|
||||
$(BIN)/test_f64cgen_run \
|
||||
$(BIN)/test_f64crossmod_run \
|
||||
$(BIN)/test_tuprecv_run \
|
||||
@@ -1159,6 +1160,11 @@ $(BIN)/test_struct_composite_init_run: test/wcc/918_struct_composite_init_run.c
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_array_static_init_run: test/wcc/919_array_static_init_run.c \
|
||||
$(BIN)/ww $(BIN)/w6c $(BIN)/w6c_ww $(BIN)/w6a $(BIN)/w6l \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_f64cgen_run: test/wcc/951_f64cgen_run.c $(BIN)/ww $(BIN)/w6c \
|
||||
$(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
353
cmd/w6c/cgen.c
353
cmd/w6c/cgen.c
@@ -711,6 +711,17 @@ struct DefStruct {
|
||||
};
|
||||
static DefStruct *defstructs;
|
||||
|
||||
/* #129 A.3: array-typed defs now have DATA storage and need the same
|
||||
* LEAQ name(SB) + indexed-load shape as array-typed lets at cgindex
|
||||
* and N_DOT base-resolution sites. Mirrors DefStruct (A.2). */
|
||||
typedef struct DefArray DefArray;
|
||||
struct DefArray {
|
||||
const char *name;
|
||||
Type *type;
|
||||
DefArray *next;
|
||||
};
|
||||
static DefArray *defarrays;
|
||||
|
||||
/* Slot size for a top-level `let` of type t, or 0 if the type isn't
|
||||
* supported as a writable global yet. Tagged unions are deferred.
|
||||
* enums route through their storage type.
|
||||
@@ -914,6 +925,7 @@ let_collect(Cg *c, Node *file)
|
||||
{
|
||||
letvars = NULL;
|
||||
defstructs = NULL;
|
||||
defarrays = NULL;
|
||||
if (file == NULL) return;
|
||||
for (Node *d = file->list; d; d = d->next) {
|
||||
if (d->kind == N_LET) {
|
||||
@@ -927,18 +939,32 @@ let_collect(Cg *c, Node *file)
|
||||
continue;
|
||||
}
|
||||
if (d->kind == N_DEF) {
|
||||
if (d->str == NULL || d->str[0] == '\0') continue;
|
||||
/* #129 A.2: struct-typed defs now have DATA storage
|
||||
* (emit_defs struct arm); register them so the N_DOT
|
||||
* struct-let LEAQ-and-offset shape widens to cover
|
||||
* them too. Other def kinds (int / float / str)
|
||||
* stay on their existing load paths. */
|
||||
if (d->str == NULL || d->str[0] == '\0') continue;
|
||||
if (!let_isstruct(d->type)) continue;
|
||||
DefStruct *ds = amalloc(c->a, sizeof *ds);
|
||||
ds->name = d->str;
|
||||
ds->type = d->type;
|
||||
ds->next = defstructs;
|
||||
defstructs = ds;
|
||||
if (let_isstruct(d->type)) {
|
||||
DefStruct *ds = amalloc(c->a, sizeof *ds);
|
||||
ds->name = d->str;
|
||||
ds->type = d->type;
|
||||
ds->next = defstructs;
|
||||
defstructs = ds;
|
||||
continue;
|
||||
}
|
||||
/* #129 A.3: array-typed defs now have DATA storage
|
||||
* (emit_defs array arm); register them so cgindex's
|
||||
* `let_islet`-gated LEAQ name(SB) base-load widens
|
||||
* to defs too (LOAD-side twin of the struct-def
|
||||
* registry). */
|
||||
if (let_isarray(d->type)) {
|
||||
DefArray *da = amalloc(c->a, sizeof *da);
|
||||
da->name = d->str;
|
||||
da->type = d->type;
|
||||
da->next = defarrays;
|
||||
defarrays = da;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -952,6 +978,15 @@ def_isstructdef(const char *name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
def_isarraydef(const char *name)
|
||||
{
|
||||
if (name == NULL) return 0;
|
||||
for (DefArray *da = defarrays; da; da = da->next)
|
||||
if (strcmp(da->name, name) == 0) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
let_islet(const char *name)
|
||||
{
|
||||
@@ -6797,7 +6832,12 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
|
||||
if (n->lhs->kind == N_IDENT && u) {
|
||||
int off = localfind(locals, n->lhs->str);
|
||||
int isglobal = (off == 0) && let_islet(n->lhs->str);
|
||||
/* #129 A.3: array-typed defs now have DATA storage; the
|
||||
* LEAQ name(SB) base-load must fire for them too, not
|
||||
* just let_islet. Parallel to A.2's def_isstructdef
|
||||
* gate at the N_DOT direct-struct-ident arm. */
|
||||
int isglobal = (off == 0) && (let_islet(n->lhs->str)
|
||||
|| def_isarraydef(n->lhs->str));
|
||||
cgexpr(c, n->rhs, locals); /* idx → AX */
|
||||
if (esz > 1) {
|
||||
ins2(c, A_MOVQ, aimm(esz), areg(D_CX));
|
||||
@@ -8523,6 +8563,12 @@ emit_floatlit_data(FILE *out, Cg *c, const char *directive,
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Forward declaration: emit_struct_lit_bytes recurses into
|
||||
* emit_array_lit_bytes for nested array fields (#129 A.3 closes the
|
||||
* A.2 shape-15 park). Defined further down. */
|
||||
static int emit_array_lit_bytes(FILE *out, Cg *c, Type *t, Node *rhs,
|
||||
int emit_phase);
|
||||
|
||||
/* emit_struct_lit_bytes — emit the byte sequence for a struct-typed
|
||||
* top-level let/def whose rhs is an N_STRUCTLIT (or NULL for bare
|
||||
* no-rhs). Walks Tfield list in declaration order, zero-fills padding
|
||||
@@ -8585,6 +8631,24 @@ emit_struct_lit_bytes(FILE *out, Cg *c, Type *t, Node *rhs, u64 base)
|
||||
pos = fstart + (u64)fsz;
|
||||
continue;
|
||||
}
|
||||
/* #129 A.3: array-typed field with N_ARRLIT rhs (the shape
|
||||
* parked in A.2). Calls emit_array_lit_bytes which dispatches
|
||||
* by element kind (int/float/struct). Returns 0 if the rhs
|
||||
* shape can't reduce — fatal here per rule-7 since the field
|
||||
* is declared array-typed and a non-reducible inner rhs is
|
||||
* a real bug surface, not a fall-through. */
|
||||
if (fu && fu->kind == TY_ARRAY) {
|
||||
if (vr == NULL || vr->kind != N_ARRLIT)
|
||||
fatal("emit_struct_lit_bytes: array field "
|
||||
"'%s' rhs is not N_ARRLIT (#129 A.3)",
|
||||
f->name ? f->name : "?");
|
||||
if (!emit_array_lit_bytes(out, c, f->type, vr, 1))
|
||||
fatal("emit_struct_lit_bytes: array field "
|
||||
"'%s' rhs has non-reducible elements "
|
||||
"(#129 A.3)", f->name ? f->name : "?");
|
||||
pos = fstart + (u64)fsz;
|
||||
continue;
|
||||
}
|
||||
if (type_isfloat(f->type)) {
|
||||
int isf32 = type_isf32(f->type);
|
||||
u64 fv = 0;
|
||||
@@ -8654,6 +8718,210 @@ emit_struct_data(FILE *out, Cg *c, const char *directive,
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* emit_array_lit_bytes — emit alen * esz bytes for an [N]T top-level
|
||||
* let/def with N_ARRLIT rhs. Per-element dispatch:
|
||||
* - int element (covers bool/rune/typed-int/N_UN-int): fold_int_literal
|
||||
* per element, emit LE bytes. Existing pre-#129-A.3 emit_lets array
|
||||
* arm logic preserved byte-for-byte so the bootstrap consumers in
|
||||
* lib/os, lib/bufio, lib/strings, lib/encoding/utf8, lib/strconv/
|
||||
* stof_data don't shift.
|
||||
* - float element (f32/f64): peel N_CAST/N_UN(±), bitcast magnitude
|
||||
* via union (mirrors emit_floatlit_data), sign-XOR top byte of each
|
||||
* element inline. NO 2^63 immediate.
|
||||
* - struct element: per element call emit_struct_lit_bytes (#129 A.2
|
||||
* helper).
|
||||
* - other element kinds (str/slice/ptr-with-address/nested-array):
|
||||
* return 0 — caller falls through to zero-init (str/slice accepts
|
||||
* no-rhs already).
|
||||
*
|
||||
* Trailing `...` repeat marker fills remaining slots with the last
|
||||
* value (mirrors the scalar repeat path). Returns 1 on emit, 0 if the
|
||||
* rhs shape can't reduce to a foldable literal — caller MUST then
|
||||
* fall back to zero-init / skip path; the caller opens the DATA/DATAW
|
||||
* directive AFTER a successful validate-only call. Two-call pattern
|
||||
* keeps emit-on-failure from emitting partial bytes.
|
||||
*
|
||||
* `emit_phase = 0` runs validate-only (returns 1 if ok); `emit_phase
|
||||
* = 1` actually emits. */
|
||||
static int
|
||||
emit_array_lit_bytes(FILE *out, Cg *c, Type *t, Node *rhs, int emit_phase)
|
||||
{
|
||||
Type *u = type_unwrap(t);
|
||||
if (u == NULL || u->kind != TY_ARRAY) return 0;
|
||||
Type *etype = u->sub;
|
||||
Type *eu = (etype && etype->kind == TY_NAMED) ? etype->under : etype;
|
||||
int esz = etype ? (int)etype->size : 1;
|
||||
int alen = (int)u->alen;
|
||||
|
||||
if (eu && eu->kind == TY_STRUCT) {
|
||||
/* Validate: every element must be N_STRUCTLIT (after N_CAST
|
||||
* peel). */
|
||||
int idx = 0;
|
||||
Node *last_ev = NULL;
|
||||
int repeat = 0;
|
||||
for (Node *e = rhs->list; e && idx < alen; e = e->next) {
|
||||
if (e->kind == N_FIELD && e->str
|
||||
&& strcmp(e->str, "...") == 0) {
|
||||
repeat = 1;
|
||||
break;
|
||||
}
|
||||
Node *ev = e;
|
||||
while (ev && ev->kind == N_CAST) ev = ev->lhs;
|
||||
if (ev == NULL || ev->kind != N_STRUCTLIT) return 0;
|
||||
last_ev = ev;
|
||||
idx++;
|
||||
}
|
||||
if (!emit_phase) return 1;
|
||||
idx = 0;
|
||||
repeat = 0;
|
||||
for (Node *e = rhs->list; e && idx < alen; e = e->next) {
|
||||
if (e->kind == N_FIELD && e->str
|
||||
&& strcmp(e->str, "...") == 0) {
|
||||
repeat = 1;
|
||||
break;
|
||||
}
|
||||
Node *ev = e;
|
||||
while (ev && ev->kind == N_CAST) ev = ev->lhs;
|
||||
emit_struct_lit_bytes(out, c, etype, ev, 0);
|
||||
idx++;
|
||||
}
|
||||
while (idx < alen) {
|
||||
if (repeat && last_ev != NULL)
|
||||
emit_struct_lit_bytes(out, c, etype, last_ev, 0);
|
||||
else
|
||||
for (int b = 0; b < esz; b++)
|
||||
emit_data_byte(out, 0);
|
||||
idx++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (type_isfloat(etype)) {
|
||||
int isf32 = type_isf32(etype);
|
||||
/* Validate: every element must be N_FLOATLIT (after N_CAST
|
||||
* + optional N_UN(±) peel). */
|
||||
int idx = 0;
|
||||
for (Node *e = rhs->list; e && idx < alen; e = e->next) {
|
||||
if (e->kind == N_FIELD && e->str
|
||||
&& strcmp(e->str, "...") == 0) break;
|
||||
Node *ev = e;
|
||||
while (ev && ev->kind == N_CAST) ev = ev->lhs;
|
||||
if (ev != NULL && ev->kind == N_UN
|
||||
&& (ev->op == TK_MINUS || ev->op == TK_PLUS)) {
|
||||
ev = ev->lhs;
|
||||
while (ev && ev->kind == N_CAST) ev = ev->lhs;
|
||||
}
|
||||
if (ev == NULL || ev->kind != N_FLOATLIT) return 0;
|
||||
idx++;
|
||||
}
|
||||
if (!emit_phase) return 1;
|
||||
idx = 0;
|
||||
u64 last_bits = 0;
|
||||
int last_neg = 0;
|
||||
int repeat = 0;
|
||||
for (Node *e = rhs->list; e && idx < alen; e = e->next) {
|
||||
if (e->kind == N_FIELD && e->str
|
||||
&& strcmp(e->str, "...") == 0) {
|
||||
repeat = 1;
|
||||
break;
|
||||
}
|
||||
Node *ev = e;
|
||||
while (ev && ev->kind == N_CAST) ev = ev->lhs;
|
||||
int neg = 0;
|
||||
if (ev != NULL && ev->kind == N_UN
|
||||
&& (ev->op == TK_MINUS || ev->op == TK_PLUS)) {
|
||||
if (ev->op == TK_MINUS) neg = 1;
|
||||
ev = ev->lhs;
|
||||
while (ev && ev->kind == N_CAST) ev = ev->lhs;
|
||||
}
|
||||
u64 bits = 0;
|
||||
if (isf32) {
|
||||
union { float f; u32 u; } x;
|
||||
x.f = (float)ev->fval;
|
||||
bits = (u64)x.u;
|
||||
} else {
|
||||
union { double d; u64 u; } x;
|
||||
x.d = ev->fval;
|
||||
bits = x.u;
|
||||
}
|
||||
for (int b = 0; b < esz; b++) {
|
||||
u8 byt = (u8)((bits >> (b * 8)) & 0xff);
|
||||
if (neg && b == esz - 1) byt = (u8)(byt ^ 0x80);
|
||||
emit_data_byte(out, byt);
|
||||
}
|
||||
last_bits = bits;
|
||||
last_neg = neg;
|
||||
idx++;
|
||||
}
|
||||
while (idx < alen) {
|
||||
if (repeat) {
|
||||
for (int b = 0; b < esz; b++) {
|
||||
u8 byt = (u8)((last_bits >> (b * 8)) & 0xff);
|
||||
if (last_neg && b == esz - 1)
|
||||
byt = (u8)(byt ^ 0x80);
|
||||
emit_data_byte(out, byt);
|
||||
}
|
||||
} else {
|
||||
for (int b = 0; b < esz; b++)
|
||||
emit_data_byte(out, 0);
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Int-element path — preserved BYTE-FOR-BYTE from the pre-A.3
|
||||
* emit_lets in-place array arm so the bootstrap consumers (u8 /
|
||||
* i8 / u16 arrays in lib/os, lib/bufio, lib/strings, lib/
|
||||
* encoding/utf8, lib/strconv/stof_data) don't shift. */
|
||||
u64 *vals = amalloc(c->a, sizeof(u64) * (size_t)alen);
|
||||
int idx = 0;
|
||||
int ok = 1;
|
||||
u64 last = 0;
|
||||
int repeat = 0;
|
||||
for (Node *e = rhs->list; e && idx < alen; e = e->next) {
|
||||
if (e->kind == N_FIELD && e->str
|
||||
&& strcmp(e->str, "...") == 0) {
|
||||
repeat = 1;
|
||||
break;
|
||||
}
|
||||
Node *ev = e;
|
||||
while (ev && ev->kind == N_CAST) ev = ev->lhs;
|
||||
if (ev == NULL) { ok = 0; break; }
|
||||
if (!fold_int_literal(ev, &last)) { ok = 0; break; }
|
||||
vals[idx++] = last;
|
||||
}
|
||||
if (!ok) return 0;
|
||||
if (!emit_phase) return 1;
|
||||
if (repeat) {
|
||||
while (idx < alen) vals[idx++] = last;
|
||||
} else {
|
||||
while (idx < alen) vals[idx++] = 0;
|
||||
}
|
||||
for (int i = 0; i < alen; i++) {
|
||||
u64 v = vals[i];
|
||||
for (int b = 0; b < esz; b++)
|
||||
emit_data_byte(out, (u8)((v >> (b * 8)) & 0xff));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* emit_array_data — opens DATA/DATAW prefix on validate success, then
|
||||
* emits payload. Two-pass keeps emit-on-failure from emitting partial
|
||||
* bytes (would corrupt the asm if rhs reduces partway through). */
|
||||
static int
|
||||
emit_array_data(FILE *out, Cg *c, const char *directive,
|
||||
const char *name, Type *t, Node *rhs)
|
||||
{
|
||||
Type *u = type_unwrap(t);
|
||||
if (u == NULL || u->kind != TY_ARRAY) return 0;
|
||||
if (!emit_array_lit_bytes(out, c, t, rhs, 0)) return 0;
|
||||
fprintf(out, "%s %s(SB),\"", directive, mod_mangle(c, name));
|
||||
emit_array_lit_bytes(out, c, t, rhs, 1);
|
||||
fputs("\"\n", out);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
emit_lets(Cg *c, FILE *out, Node *file)
|
||||
{
|
||||
@@ -8718,59 +8986,17 @@ emit_lets(Cg *c, FILE *out, Node *file)
|
||||
fprintf(out, "DATAR %s+0(SB),%s(SB)\n", sym, lab);
|
||||
continue;
|
||||
}
|
||||
/* Array literal init: `let xs: [N]T = [v0, v1, ...];`. Walk
|
||||
* elements in declaration order; each must reduce to an
|
||||
* integer literal (casts are stripped). The trailing `...`
|
||||
* repeat marker fills remaining slots with the last value.
|
||||
* Falls through to zero-init if any element isn't a
|
||||
* constant we can evaluate at emit time. */
|
||||
/* Array literal init: `let xs: [N]T = [v0, v1, ...];`. The
|
||||
* helper dispatches per element kind (int/float/struct).
|
||||
* Int-element path preserved BYTE-FOR-BYTE from pre-A.3 so
|
||||
* bootstrap consumers (lib/os, lib/bufio, lib/strings, lib/
|
||||
* encoding/utf8, lib/strconv/stof_data) don't shift. Float
|
||||
* + struct elements gain emit; ptr / nested-array fall
|
||||
* through to zero-init (existing path below). */
|
||||
if (r != NULL && r->kind == N_ARRLIT && let_isarray(d->type)) {
|
||||
Type *u = type_unwrap(d->type);
|
||||
int esz = (u && u->sub) ? (int)u->sub->size : 1;
|
||||
int alen = (u) ? (int)u->alen : 0;
|
||||
u64 *vals = amalloc(c->a, sizeof(u64) * (size_t)alen);
|
||||
int idx = 0;
|
||||
int ok = 1;
|
||||
u64 last = 0;
|
||||
int repeat = 0;
|
||||
for (Node *e = r->list; e && idx < alen; e = e->next) {
|
||||
if (e->kind == N_FIELD && e->str &&
|
||||
strcmp(e->str, "...") == 0) {
|
||||
repeat = 1;
|
||||
break;
|
||||
}
|
||||
Node *ev = e;
|
||||
while (ev && ev->kind == N_CAST) ev = ev->lhs;
|
||||
if (ev == NULL) { ok = 0; break; }
|
||||
/* Same helper as the scalar arm above —
|
||||
* fold_int_literal covers N_UN over a leaf
|
||||
* so signed-negative array elements (`-1i8`)
|
||||
* encode as their two's-complement bytes
|
||||
* once truncated to esz below. */
|
||||
if (!fold_int_literal(ev, &last)) {
|
||||
ok = 0;
|
||||
break;
|
||||
}
|
||||
vals[idx++] = last;
|
||||
}
|
||||
if (ok) {
|
||||
if (repeat) {
|
||||
while (idx < alen) vals[idx++] = last;
|
||||
} else {
|
||||
while (idx < alen) vals[idx++] = 0;
|
||||
}
|
||||
fprintf(out, "DATAW %s(SB),\"",
|
||||
mod_mangle(c, d->str));
|
||||
for (int i = 0; i < alen; i++) {
|
||||
u64 v = vals[i];
|
||||
for (int b = 0; b < esz; b++) {
|
||||
emit_data_byte(out,
|
||||
(u8)((v >> (b * 8)) & 0xff));
|
||||
}
|
||||
}
|
||||
fputs("\"\n", out);
|
||||
if (emit_array_data(out, c, "DATAW", d->str,
|
||||
d->type, r))
|
||||
continue;
|
||||
}
|
||||
/* fall through to zero-init */
|
||||
}
|
||||
/* Otherwise: zero-init. str accepts nil / ""; struct
|
||||
@@ -8846,6 +9072,15 @@ emit_defs(Cg *c, FILE *out, Node *file)
|
||||
d->str, d->type, d->rhs);
|
||||
continue;
|
||||
}
|
||||
/* #129 A.3: array-typed def with N_ARRLIT rhs. Parallel to
|
||||
* emit_lets's array arm; uses DATA (read-only). LOAD-side
|
||||
* widening at cgindex/cgdot resolves the def's address via
|
||||
* LEAQ name(SB). */
|
||||
if (let_isarray(d->type) && d->rhs->kind == N_ARRLIT) {
|
||||
(void)emit_array_data(out, c, "DATA",
|
||||
d->str, d->type, d->rhs);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
(void)c;
|
||||
}
|
||||
|
||||
@@ -15515,6 +15515,12 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
f32_elem = elemisf32c(c, baselocal.tnode);
|
||||
} else {
|
||||
let tn: *node = letvartnode(c, bn);
|
||||
// #129 A.3: array-typed defs now have DATA storage;
|
||||
// resolve their base via the same N_TARRAY path as
|
||||
// lets. defvartnode is the def-side sister of
|
||||
// letvartnode (parallel to defvarstructinfo at the
|
||||
// A.2 cgdot widening site).
|
||||
if (tn == nil) { tn = defvartnode(c, bn); };
|
||||
if (tn != nil) {
|
||||
if (tn.kind == nkind.N_TARRAY) {
|
||||
isglobalarr = true;
|
||||
@@ -24939,6 +24945,21 @@ fn defvarstructinfo(c: *cgen, name: str) *structinfo = {
|
||||
return nil;
|
||||
};
|
||||
|
||||
// defvartnode — sister of letvartnode for top-level `def`s. Returns
|
||||
// the type-spec node (defent.dtnode) for the named def, or nil. #129
|
||||
// A.3 uses it in cgindex's array-base resolution so a `def: [N]T`
|
||||
// resolves through the same N_TARRAY-detect → LEAQ name(SB) shape as
|
||||
// a let array. Parallel to defvarstructinfo (#129 A.2) at the LOAD
|
||||
// side widening.
|
||||
fn defvartnode(c: *cgen, name: str) *node = {
|
||||
let e: *defent = c.defs;
|
||||
for (e != nil) {
|
||||
if (streq(e.dname, name)) { return e.dtnode; };
|
||||
e = e.dnext;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// emitdatawbyte — write one byte of an asm string literal using
|
||||
// the same escape rules as emitdefconstants / emitdatasection.
|
||||
fn emitdatawbyte(b: u8) void = {
|
||||
@@ -25163,6 +25184,30 @@ fn emitstructlitbytes(c: *cgen, structt: *tinfo, rhs: *node,
|
||||
f = f.tnext;
|
||||
continue;
|
||||
};
|
||||
// #129 A.3: array-typed field with N_ARRLIT rhs (the shape
|
||||
// parked in A.2). Recurses through emitarraylitbytes for
|
||||
// element-kind dispatch. Rule-7 stops loudly if rhs shape
|
||||
// doesn't match.
|
||||
if (fu != nil && fu.kind == tykind.TY_ARRAY) {
|
||||
if (vr == nil) {
|
||||
let m: str = "emitstructlitbytes: array field rhs nil (#129 A.3)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
if (vr.kind != nkind.N_ARRLIT) {
|
||||
let m: str = "emitstructlitbytes: array field rhs not N_ARRLIT (#129 A.3)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
if (!emitarraylitbytes(c, f.type_, vr, 1)) {
|
||||
let m: str = "emitstructlitbytes: array field rhs has non-reducible elements (#129 A.3)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
pos = fstart + fsz: u64;
|
||||
f = f.tnext;
|
||||
continue;
|
||||
};
|
||||
if (typeisfloat(f.type_)) {
|
||||
let isf32: bool = (fsz == 4);
|
||||
let neg: bool = false;
|
||||
@@ -25251,6 +25296,260 @@ fn emitstructdata(c: *cgen, directive: str, name: str,
|
||||
return true;
|
||||
};
|
||||
|
||||
// emitarraylitbytes — emit alen * esz bytes for an [N]T top-level let/
|
||||
// def with N_ARRLIT rhs. Mirrors cstage emit_array_lit_bytes. Per-
|
||||
// element dispatch:
|
||||
// - int (covers bool/rune/typed-int/N_UN-int): foldintliteral per
|
||||
// element. Existing pre-#129-A.3 emitletdataw array arm logic
|
||||
// preserved byte-for-byte so bootstrap consumers (lib/os, lib/
|
||||
// bufio, lib/strings, lib/encoding/utf8, lib/strconv/stof_data)
|
||||
// don't shift.
|
||||
// - float (f32/f64): peel N_CAST/N_UN(±), bitcast magnitude via
|
||||
// pointer-cast round-trip (mirror emitfloatlitdata), sign-XOR
|
||||
// top byte of each element inline. No 2^63 immediate.
|
||||
// - struct: per element call emitstructlitbytes (#129 A.2 helper).
|
||||
// - other element kinds (ptr/nested-array): returns false — caller
|
||||
// falls through to zero-init.
|
||||
//
|
||||
// Two-pass validate-then-emit (`emit_phase=0` validate-only, `=1`
|
||||
// actually emit) keeps emit-on-failure from emitting partial bytes
|
||||
// into an open DATA literal.
|
||||
fn emitarraylitbytes(c: *cgen, arrt: *tinfo, rhs: *node,
|
||||
emit_phase: i32) bool = {
|
||||
let au: *tinfo = arrt;
|
||||
for (au != nil && au.kind == tykind.TY_NAMED) { au = au.under; };
|
||||
if (au == nil) { return false; };
|
||||
if (au.kind != tykind.TY_ARRAY) { return false; };
|
||||
let esz: i32 = au.sub.size: i32;
|
||||
let alen: i32 = au.alen: i32;
|
||||
let eu: *tinfo = au.sub;
|
||||
for (eu != nil && eu.kind == tykind.TY_NAMED) { eu = eu.under; };
|
||||
|
||||
if (eu != nil && eu.kind == tykind.TY_STRUCT) {
|
||||
// Validate: every element must be N_STRUCTLIT (after N_CAST).
|
||||
let idx: i32 = 0;
|
||||
let last_ev: *node = nil;
|
||||
let e: *node = rhs.list;
|
||||
for (e != nil && idx < alen) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) { break; };
|
||||
};
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
if (ev == nil) { return false; };
|
||||
if (ev.kind != nkind.N_STRUCTLIT) { return false; };
|
||||
last_ev = ev;
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
if (emit_phase == 0) { return true; };
|
||||
idx = 0;
|
||||
let repeat: bool = false;
|
||||
e = rhs.list;
|
||||
for (e != nil && idx < alen) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) { repeat = true; break; };
|
||||
};
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
emitstructlitbytes(c, au.sub, ev, 0u64);
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
for (idx < alen) {
|
||||
if (repeat && last_ev != nil) {
|
||||
emitstructlitbytes(c, au.sub, last_ev, 0u64);
|
||||
} else {
|
||||
let bb: i32 = 0;
|
||||
for (bb < esz) { emitdatawbyte(0u8); bb += 1; };
|
||||
};
|
||||
idx += 1;
|
||||
};
|
||||
return true;
|
||||
};
|
||||
|
||||
if (typeisfloat(au.sub)) {
|
||||
let isf32: bool = typeisf32(au.sub);
|
||||
// Validate.
|
||||
let idx: i32 = 0;
|
||||
let e: *node = rhs.list;
|
||||
for (e != nil && idx < alen) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) { break; };
|
||||
};
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
if (ev != nil) { if (ev.kind == nkind.N_UN) {
|
||||
if (ev.op == tkind.TK_MINUS) {
|
||||
ev = ev.lhs;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
} else { if (ev.op == tkind.TK_PLUS) {
|
||||
ev = ev.lhs;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
};};
|
||||
};};
|
||||
if (ev == nil) { return false; };
|
||||
if (ev.kind != nkind.N_FLOATLIT) { return false; };
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
if (emit_phase == 0) { return true; };
|
||||
idx = 0;
|
||||
let last_bits: u64 = 0u64;
|
||||
let last_neg: bool = false;
|
||||
let repeat: bool = false;
|
||||
e = rhs.list;
|
||||
for (e != nil && idx < alen) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) { repeat = true; break; };
|
||||
};
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
let neg: bool = false;
|
||||
if (ev != nil) { if (ev.kind == nkind.N_UN) {
|
||||
if (ev.op == tkind.TK_MINUS) {
|
||||
neg = true;
|
||||
ev = ev.lhs;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
} else { if (ev.op == tkind.TK_PLUS) {
|
||||
ev = ev.lhs;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
};};
|
||||
};};
|
||||
let bits: u64 = ev.uval;
|
||||
if (isf32) {
|
||||
let dv: f64 = *((&bits): *f64);
|
||||
let fv: f32 = (dv: f32);
|
||||
let uv: u32 = *((&fv): *u32);
|
||||
bits = uv: u64;
|
||||
};
|
||||
let bb: i32 = 0;
|
||||
let nb: u64 = bits;
|
||||
for (bb < esz) {
|
||||
let byt: u8 = (nb & 255u64): u8;
|
||||
if (neg) {
|
||||
if (bb == esz - 1) { byt = byt ^ 128u8; };
|
||||
};
|
||||
emitdatawbyte(byt);
|
||||
nb = nb >> 8u64;
|
||||
bb += 1;
|
||||
};
|
||||
last_bits = bits;
|
||||
last_neg = neg;
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
for (idx < alen) {
|
||||
if (repeat) {
|
||||
let bb: i32 = 0;
|
||||
let nb: u64 = last_bits;
|
||||
for (bb < esz) {
|
||||
let byt: u8 = (nb & 255u64): u8;
|
||||
if (last_neg) {
|
||||
if (bb == esz - 1) { byt = byt ^ 128u8; };
|
||||
};
|
||||
emitdatawbyte(byt);
|
||||
nb = nb >> 8u64;
|
||||
bb += 1;
|
||||
};
|
||||
} else {
|
||||
let bb: i32 = 0;
|
||||
for (bb < esz) { emitdatawbyte(0u8); bb += 1; };
|
||||
};
|
||||
idx += 1;
|
||||
};
|
||||
return true;
|
||||
};
|
||||
|
||||
// Int-element path — preserved byte-for-byte from the pre-A.3
|
||||
// emitletdataw in-place arm so bootstrap consumers (u8/i8/u16
|
||||
// arrays) don't shift.
|
||||
let idx: i32 = 0;
|
||||
let e: *node = rhs.list;
|
||||
let last: u64 = 0u64;
|
||||
let repeat: bool = false;
|
||||
// Validate first.
|
||||
for (e != nil && idx < alen) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) { repeat = true; break; };
|
||||
};
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
if (ev == nil) { return false; };
|
||||
if (!foldintliteral(ev, &last)) { return false; };
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
if (emit_phase == 0) { return true; };
|
||||
idx = 0;
|
||||
last = 0u64;
|
||||
repeat = false;
|
||||
e = rhs.list;
|
||||
let inrepeat: bool = false;
|
||||
for (idx < alen) {
|
||||
let v: u64 = last;
|
||||
if (!inrepeat && e != nil) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) {
|
||||
inrepeat = true;
|
||||
} else {
|
||||
e = e.next;
|
||||
};
|
||||
} else {
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
if (!foldintliteral(ev, &v)) { v = 0u64; };
|
||||
last = v;
|
||||
e = e.next;
|
||||
};
|
||||
};
|
||||
let nb: u64 = v;
|
||||
let bb: i32 = 0;
|
||||
for (bb < esz) {
|
||||
emitdatawbyte((nb & 255u64): u8);
|
||||
nb = nb >> 8u64;
|
||||
bb += 1;
|
||||
};
|
||||
idx += 1;
|
||||
};
|
||||
return true;
|
||||
};
|
||||
|
||||
// emitarraydata — top-level wrapper. Two-pass validate-then-emit
|
||||
// avoids partial-byte corruption if the rhs shape can't reduce.
|
||||
// nil rhs is the "no-rhs zero-init" shape (e.g. `let buf: [N]u8;`
|
||||
// in lib/strconv/strconv.ww:287, lib/os/os.ww:92, etc.) — emit
|
||||
// alen*esz zero bytes. This was the implicit pre-A.3 emitletdataw
|
||||
// behavior (the old loop emitted zeros when `elems` was nil); the
|
||||
// refactor would have skipped emit entirely without this branch,
|
||||
// causing `undefined reference to strconv.f64tos_buf` at link.
|
||||
fn emitarraydata(c: *cgen, directive: str, name: str,
|
||||
arrt: *tinfo, rhs: *node) bool = {
|
||||
let au: *tinfo = arrt;
|
||||
for (au != nil && au.kind == tykind.TY_NAMED) { au = au.under; };
|
||||
if (au == nil) { return false; };
|
||||
if (au.kind != tykind.TY_ARRAY) { return false; };
|
||||
if (rhs == nil) {
|
||||
let total: u64 = arrt.size;
|
||||
emitline(directive);
|
||||
emitline(" ");
|
||||
emitsymname(c, name);
|
||||
emitline("(SB),\"");
|
||||
let i: u64 = 0u64;
|
||||
for (i < total) { emitdatawbyte(0u8); i = i + 1u64; };
|
||||
emitline("\"\n");
|
||||
return true;
|
||||
};
|
||||
if (!emitarraylitbytes(c, arrt, rhs, 0)) { return false; };
|
||||
emitline(directive);
|
||||
emitline(" ");
|
||||
emitsymname(c, name);
|
||||
emitline("(SB),\"");
|
||||
emitarraylitbytes(c, arrt, rhs, 1);
|
||||
emitline("\"\n");
|
||||
return true;
|
||||
};
|
||||
|
||||
fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
@@ -25437,72 +25736,31 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
emitline("\"\n");
|
||||
};
|
||||
};
|
||||
// Top-level `[N]T = [a, b, ...]` array global.
|
||||
// Emits N*esz bytes with each element's bytes
|
||||
// little-endian for the declared primitive width.
|
||||
// Element fold goes through foldintliteral (same
|
||||
// helper as emitdefconstants / scalar arm above)
|
||||
// so `-1i8` and friends emit their two's-complement
|
||||
// bytes after the leading N_CAST peel — pre-#19
|
||||
// this arm only matched bare N_INTLIT/N_RUNELIT and
|
||||
// silently emitted zero for unfoldable elements.
|
||||
// `...` (N_FIELD with str="...") repeats the last
|
||||
// folded value across the remaining slots.
|
||||
// #129 A.3: array global routes through the
|
||||
// emitarraydata SSoT helper. Int-elem path is
|
||||
// byte-for-byte preserved (bootstrap consumers in
|
||||
// lib/os, lib/bufio, lib/strings, lib/encoding/
|
||||
// utf8, lib/strconv/stof_data don't shift). Float/
|
||||
// struct elements gain emit via element-kind
|
||||
// dispatch. Helper validates pre-emit so partial
|
||||
// fold-failures don't corrupt the DATA literal.
|
||||
// No-rhs arrays (e.g. `let buf: [N]u8;`) go through
|
||||
// the same helper with rhs=nil → zero-fill branch.
|
||||
if (d.lhs != nil) {
|
||||
if (d.lhs.kind == nkind.N_TARRAY) {
|
||||
let elemn: *node = d.lhs.lhs;
|
||||
let esz: i32 = 8;
|
||||
if (elemn != nil) {
|
||||
if (elemn.kind == nkind.N_TNAME) {
|
||||
let ps: i32 = primsize(elemn.str);
|
||||
if (ps > 0) { esz = ps; };
|
||||
let rh: *node = d.rhs;
|
||||
let route: bool = false;
|
||||
if (rh == nil) { route = true; };
|
||||
if (rh != nil) {
|
||||
if (rh.kind == nkind.N_ARRLIT) {
|
||||
route = true;
|
||||
};
|
||||
};
|
||||
let total: i32 = sz;
|
||||
let alen: i32 = total / esz;
|
||||
let elems: *node = nil;
|
||||
if (d.rhs != nil) {
|
||||
if (d.rhs.kind == nkind.N_ARRLIT) {
|
||||
elems = d.rhs.list;
|
||||
};
|
||||
if (route) {
|
||||
let at: *tinfo = d.lhs.type_: *tinfo;
|
||||
emitarraydata(c, "DATAW", nm,
|
||||
at, rh);
|
||||
};
|
||||
emitline("DATAW ");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB),\"");
|
||||
let i: i32 = 0;
|
||||
let e: *node = elems;
|
||||
let last: u64 = 0u64;
|
||||
let inrepeat: bool = false;
|
||||
for (i < alen) {
|
||||
let v: u64 = last;
|
||||
if (!inrepeat && e != nil) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) {
|
||||
inrepeat = true;
|
||||
} else {
|
||||
e = e.next;
|
||||
};
|
||||
} else {
|
||||
let ev: *node = e;
|
||||
for (ev != nil) {
|
||||
if (ev.kind != nkind.N_CAST) { break; };
|
||||
ev = ev.lhs;
|
||||
};
|
||||
if (!foldintliteral(ev, &v)) { v = 0u64; };
|
||||
last = v;
|
||||
e = e.next;
|
||||
};
|
||||
};
|
||||
let nb: u64 = v;
|
||||
let b: i32 = 0;
|
||||
for (b < esz) {
|
||||
emitdatawbyte((nb & 255u64): u8);
|
||||
nb = nb >> 8u64;
|
||||
b += 1;
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
emitline("\"\n");
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -25565,6 +25823,21 @@ fn emitdefconstants(c: *cgen, file: *node) void = {
|
||||
};
|
||||
};
|
||||
};};
|
||||
// #129 A.3: array-typed def with N_ARRLIT rhs.
|
||||
// Parallel to emitletdataw array arm; uses DATA.
|
||||
if (r != nil) { if (r.kind == nkind.N_ARRLIT) {
|
||||
let at: *tinfo = d.lhs.type_: *tinfo;
|
||||
let au: *tinfo = at;
|
||||
for (au != nil && au.kind == tykind.TY_NAMED) {
|
||||
au = au.under;
|
||||
};
|
||||
if (au != nil) {
|
||||
if (au.kind == tykind.TY_ARRAY) {
|
||||
emitarraydata(c, "DATA",
|
||||
d.str, at, r);
|
||||
};
|
||||
};
|
||||
};};
|
||||
};
|
||||
};
|
||||
if (ok) {
|
||||
|
||||
@@ -1144,6 +1144,21 @@ fn defvarstructinfo(c: *cgen, name: str) *structinfo = {
|
||||
return nil;
|
||||
};
|
||||
|
||||
// defvartnode — sister of letvartnode for top-level `def`s. Returns
|
||||
// the type-spec node (defent.dtnode) for the named def, or nil. #129
|
||||
// A.3 uses it in cgindex's array-base resolution so a `def: [N]T`
|
||||
// resolves through the same N_TARRAY-detect → LEAQ name(SB) shape as
|
||||
// a let array. Parallel to defvarstructinfo (#129 A.2) at the LOAD
|
||||
// side widening.
|
||||
fn defvartnode(c: *cgen, name: str) *node = {
|
||||
let e: *defent = c.defs;
|
||||
for (e != nil) {
|
||||
if (streq(e.dname, name)) { return e.dtnode; };
|
||||
e = e.dnext;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// emitdatawbyte — write one byte of an asm string literal using
|
||||
// the same escape rules as emitdefconstants / emitdatasection.
|
||||
fn emitdatawbyte(b: u8) void = {
|
||||
@@ -1368,6 +1383,30 @@ fn emitstructlitbytes(c: *cgen, structt: *tinfo, rhs: *node,
|
||||
f = f.tnext;
|
||||
continue;
|
||||
};
|
||||
// #129 A.3: array-typed field with N_ARRLIT rhs (the shape
|
||||
// parked in A.2). Recurses through emitarraylitbytes for
|
||||
// element-kind dispatch. Rule-7 stops loudly if rhs shape
|
||||
// doesn't match.
|
||||
if (fu != nil && fu.kind == tykind.TY_ARRAY) {
|
||||
if (vr == nil) {
|
||||
let m: str = "emitstructlitbytes: array field rhs nil (#129 A.3)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
if (vr.kind != nkind.N_ARRLIT) {
|
||||
let m: str = "emitstructlitbytes: array field rhs not N_ARRLIT (#129 A.3)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
if (!emitarraylitbytes(c, f.type_, vr, 1)) {
|
||||
let m: str = "emitstructlitbytes: array field rhs has non-reducible elements (#129 A.3)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
pos = fstart + fsz: u64;
|
||||
f = f.tnext;
|
||||
continue;
|
||||
};
|
||||
if (typeisfloat(f.type_)) {
|
||||
let isf32: bool = (fsz == 4);
|
||||
let neg: bool = false;
|
||||
@@ -1456,6 +1495,260 @@ fn emitstructdata(c: *cgen, directive: str, name: str,
|
||||
return true;
|
||||
};
|
||||
|
||||
// emitarraylitbytes — emit alen * esz bytes for an [N]T top-level let/
|
||||
// def with N_ARRLIT rhs. Mirrors cstage emit_array_lit_bytes. Per-
|
||||
// element dispatch:
|
||||
// - int (covers bool/rune/typed-int/N_UN-int): foldintliteral per
|
||||
// element. Existing pre-#129-A.3 emitletdataw array arm logic
|
||||
// preserved byte-for-byte so bootstrap consumers (lib/os, lib/
|
||||
// bufio, lib/strings, lib/encoding/utf8, lib/strconv/stof_data)
|
||||
// don't shift.
|
||||
// - float (f32/f64): peel N_CAST/N_UN(±), bitcast magnitude via
|
||||
// pointer-cast round-trip (mirror emitfloatlitdata), sign-XOR
|
||||
// top byte of each element inline. No 2^63 immediate.
|
||||
// - struct: per element call emitstructlitbytes (#129 A.2 helper).
|
||||
// - other element kinds (ptr/nested-array): returns false — caller
|
||||
// falls through to zero-init.
|
||||
//
|
||||
// Two-pass validate-then-emit (`emit_phase=0` validate-only, `=1`
|
||||
// actually emit) keeps emit-on-failure from emitting partial bytes
|
||||
// into an open DATA literal.
|
||||
fn emitarraylitbytes(c: *cgen, arrt: *tinfo, rhs: *node,
|
||||
emit_phase: i32) bool = {
|
||||
let au: *tinfo = arrt;
|
||||
for (au != nil && au.kind == tykind.TY_NAMED) { au = au.under; };
|
||||
if (au == nil) { return false; };
|
||||
if (au.kind != tykind.TY_ARRAY) { return false; };
|
||||
let esz: i32 = au.sub.size: i32;
|
||||
let alen: i32 = au.alen: i32;
|
||||
let eu: *tinfo = au.sub;
|
||||
for (eu != nil && eu.kind == tykind.TY_NAMED) { eu = eu.under; };
|
||||
|
||||
if (eu != nil && eu.kind == tykind.TY_STRUCT) {
|
||||
// Validate: every element must be N_STRUCTLIT (after N_CAST).
|
||||
let idx: i32 = 0;
|
||||
let last_ev: *node = nil;
|
||||
let e: *node = rhs.list;
|
||||
for (e != nil && idx < alen) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) { break; };
|
||||
};
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
if (ev == nil) { return false; };
|
||||
if (ev.kind != nkind.N_STRUCTLIT) { return false; };
|
||||
last_ev = ev;
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
if (emit_phase == 0) { return true; };
|
||||
idx = 0;
|
||||
let repeat: bool = false;
|
||||
e = rhs.list;
|
||||
for (e != nil && idx < alen) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) { repeat = true; break; };
|
||||
};
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
emitstructlitbytes(c, au.sub, ev, 0u64);
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
for (idx < alen) {
|
||||
if (repeat && last_ev != nil) {
|
||||
emitstructlitbytes(c, au.sub, last_ev, 0u64);
|
||||
} else {
|
||||
let bb: i32 = 0;
|
||||
for (bb < esz) { emitdatawbyte(0u8); bb += 1; };
|
||||
};
|
||||
idx += 1;
|
||||
};
|
||||
return true;
|
||||
};
|
||||
|
||||
if (typeisfloat(au.sub)) {
|
||||
let isf32: bool = typeisf32(au.sub);
|
||||
// Validate.
|
||||
let idx: i32 = 0;
|
||||
let e: *node = rhs.list;
|
||||
for (e != nil && idx < alen) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) { break; };
|
||||
};
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
if (ev != nil) { if (ev.kind == nkind.N_UN) {
|
||||
if (ev.op == tkind.TK_MINUS) {
|
||||
ev = ev.lhs;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
} else { if (ev.op == tkind.TK_PLUS) {
|
||||
ev = ev.lhs;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
};};
|
||||
};};
|
||||
if (ev == nil) { return false; };
|
||||
if (ev.kind != nkind.N_FLOATLIT) { return false; };
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
if (emit_phase == 0) { return true; };
|
||||
idx = 0;
|
||||
let last_bits: u64 = 0u64;
|
||||
let last_neg: bool = false;
|
||||
let repeat: bool = false;
|
||||
e = rhs.list;
|
||||
for (e != nil && idx < alen) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) { repeat = true; break; };
|
||||
};
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
let neg: bool = false;
|
||||
if (ev != nil) { if (ev.kind == nkind.N_UN) {
|
||||
if (ev.op == tkind.TK_MINUS) {
|
||||
neg = true;
|
||||
ev = ev.lhs;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
} else { if (ev.op == tkind.TK_PLUS) {
|
||||
ev = ev.lhs;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
};};
|
||||
};};
|
||||
let bits: u64 = ev.uval;
|
||||
if (isf32) {
|
||||
let dv: f64 = *((&bits): *f64);
|
||||
let fv: f32 = (dv: f32);
|
||||
let uv: u32 = *((&fv): *u32);
|
||||
bits = uv: u64;
|
||||
};
|
||||
let bb: i32 = 0;
|
||||
let nb: u64 = bits;
|
||||
for (bb < esz) {
|
||||
let byt: u8 = (nb & 255u64): u8;
|
||||
if (neg) {
|
||||
if (bb == esz - 1) { byt = byt ^ 128u8; };
|
||||
};
|
||||
emitdatawbyte(byt);
|
||||
nb = nb >> 8u64;
|
||||
bb += 1;
|
||||
};
|
||||
last_bits = bits;
|
||||
last_neg = neg;
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
for (idx < alen) {
|
||||
if (repeat) {
|
||||
let bb: i32 = 0;
|
||||
let nb: u64 = last_bits;
|
||||
for (bb < esz) {
|
||||
let byt: u8 = (nb & 255u64): u8;
|
||||
if (last_neg) {
|
||||
if (bb == esz - 1) { byt = byt ^ 128u8; };
|
||||
};
|
||||
emitdatawbyte(byt);
|
||||
nb = nb >> 8u64;
|
||||
bb += 1;
|
||||
};
|
||||
} else {
|
||||
let bb: i32 = 0;
|
||||
for (bb < esz) { emitdatawbyte(0u8); bb += 1; };
|
||||
};
|
||||
idx += 1;
|
||||
};
|
||||
return true;
|
||||
};
|
||||
|
||||
// Int-element path — preserved byte-for-byte from the pre-A.3
|
||||
// emitletdataw in-place arm so bootstrap consumers (u8/i8/u16
|
||||
// arrays) don't shift.
|
||||
let idx: i32 = 0;
|
||||
let e: *node = rhs.list;
|
||||
let last: u64 = 0u64;
|
||||
let repeat: bool = false;
|
||||
// Validate first.
|
||||
for (e != nil && idx < alen) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) { repeat = true; break; };
|
||||
};
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
if (ev == nil) { return false; };
|
||||
if (!foldintliteral(ev, &last)) { return false; };
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
if (emit_phase == 0) { return true; };
|
||||
idx = 0;
|
||||
last = 0u64;
|
||||
repeat = false;
|
||||
e = rhs.list;
|
||||
let inrepeat: bool = false;
|
||||
for (idx < alen) {
|
||||
let v: u64 = last;
|
||||
if (!inrepeat && e != nil) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) {
|
||||
inrepeat = true;
|
||||
} else {
|
||||
e = e.next;
|
||||
};
|
||||
} else {
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
if (!foldintliteral(ev, &v)) { v = 0u64; };
|
||||
last = v;
|
||||
e = e.next;
|
||||
};
|
||||
};
|
||||
let nb: u64 = v;
|
||||
let bb: i32 = 0;
|
||||
for (bb < esz) {
|
||||
emitdatawbyte((nb & 255u64): u8);
|
||||
nb = nb >> 8u64;
|
||||
bb += 1;
|
||||
};
|
||||
idx += 1;
|
||||
};
|
||||
return true;
|
||||
};
|
||||
|
||||
// emitarraydata — top-level wrapper. Two-pass validate-then-emit
|
||||
// avoids partial-byte corruption if the rhs shape can't reduce.
|
||||
// nil rhs is the "no-rhs zero-init" shape (e.g. `let buf: [N]u8;`
|
||||
// in lib/strconv/strconv.ww:287, lib/os/os.ww:92, etc.) — emit
|
||||
// alen*esz zero bytes. This was the implicit pre-A.3 emitletdataw
|
||||
// behavior (the old loop emitted zeros when `elems` was nil); the
|
||||
// refactor would have skipped emit entirely without this branch,
|
||||
// causing `undefined reference to strconv.f64tos_buf` at link.
|
||||
fn emitarraydata(c: *cgen, directive: str, name: str,
|
||||
arrt: *tinfo, rhs: *node) bool = {
|
||||
let au: *tinfo = arrt;
|
||||
for (au != nil && au.kind == tykind.TY_NAMED) { au = au.under; };
|
||||
if (au == nil) { return false; };
|
||||
if (au.kind != tykind.TY_ARRAY) { return false; };
|
||||
if (rhs == nil) {
|
||||
let total: u64 = arrt.size;
|
||||
emitline(directive);
|
||||
emitline(" ");
|
||||
emitsymname(c, name);
|
||||
emitline("(SB),\"");
|
||||
let i: u64 = 0u64;
|
||||
for (i < total) { emitdatawbyte(0u8); i = i + 1u64; };
|
||||
emitline("\"\n");
|
||||
return true;
|
||||
};
|
||||
if (!emitarraylitbytes(c, arrt, rhs, 0)) { return false; };
|
||||
emitline(directive);
|
||||
emitline(" ");
|
||||
emitsymname(c, name);
|
||||
emitline("(SB),\"");
|
||||
emitarraylitbytes(c, arrt, rhs, 1);
|
||||
emitline("\"\n");
|
||||
return true;
|
||||
};
|
||||
|
||||
fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
@@ -1642,72 +1935,31 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
emitline("\"\n");
|
||||
};
|
||||
};
|
||||
// Top-level `[N]T = [a, b, ...]` array global.
|
||||
// Emits N*esz bytes with each element's bytes
|
||||
// little-endian for the declared primitive width.
|
||||
// Element fold goes through foldintliteral (same
|
||||
// helper as emitdefconstants / scalar arm above)
|
||||
// so `-1i8` and friends emit their two's-complement
|
||||
// bytes after the leading N_CAST peel — pre-#19
|
||||
// this arm only matched bare N_INTLIT/N_RUNELIT and
|
||||
// silently emitted zero for unfoldable elements.
|
||||
// `...` (N_FIELD with str="...") repeats the last
|
||||
// folded value across the remaining slots.
|
||||
// #129 A.3: array global routes through the
|
||||
// emitarraydata SSoT helper. Int-elem path is
|
||||
// byte-for-byte preserved (bootstrap consumers in
|
||||
// lib/os, lib/bufio, lib/strings, lib/encoding/
|
||||
// utf8, lib/strconv/stof_data don't shift). Float/
|
||||
// struct elements gain emit via element-kind
|
||||
// dispatch. Helper validates pre-emit so partial
|
||||
// fold-failures don't corrupt the DATA literal.
|
||||
// No-rhs arrays (e.g. `let buf: [N]u8;`) go through
|
||||
// the same helper with rhs=nil → zero-fill branch.
|
||||
if (d.lhs != nil) {
|
||||
if (d.lhs.kind == nkind.N_TARRAY) {
|
||||
let elemn: *node = d.lhs.lhs;
|
||||
let esz: i32 = 8;
|
||||
if (elemn != nil) {
|
||||
if (elemn.kind == nkind.N_TNAME) {
|
||||
let ps: i32 = primsize(elemn.str);
|
||||
if (ps > 0) { esz = ps; };
|
||||
let rh: *node = d.rhs;
|
||||
let route: bool = false;
|
||||
if (rh == nil) { route = true; };
|
||||
if (rh != nil) {
|
||||
if (rh.kind == nkind.N_ARRLIT) {
|
||||
route = true;
|
||||
};
|
||||
};
|
||||
let total: i32 = sz;
|
||||
let alen: i32 = total / esz;
|
||||
let elems: *node = nil;
|
||||
if (d.rhs != nil) {
|
||||
if (d.rhs.kind == nkind.N_ARRLIT) {
|
||||
elems = d.rhs.list;
|
||||
};
|
||||
if (route) {
|
||||
let at: *tinfo = d.lhs.type_: *tinfo;
|
||||
emitarraydata(c, "DATAW", nm,
|
||||
at, rh);
|
||||
};
|
||||
emitline("DATAW ");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB),\"");
|
||||
let i: i32 = 0;
|
||||
let e: *node = elems;
|
||||
let last: u64 = 0u64;
|
||||
let inrepeat: bool = false;
|
||||
for (i < alen) {
|
||||
let v: u64 = last;
|
||||
if (!inrepeat && e != nil) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) {
|
||||
inrepeat = true;
|
||||
} else {
|
||||
e = e.next;
|
||||
};
|
||||
} else {
|
||||
let ev: *node = e;
|
||||
for (ev != nil) {
|
||||
if (ev.kind != nkind.N_CAST) { break; };
|
||||
ev = ev.lhs;
|
||||
};
|
||||
if (!foldintliteral(ev, &v)) { v = 0u64; };
|
||||
last = v;
|
||||
e = e.next;
|
||||
};
|
||||
};
|
||||
let nb: u64 = v;
|
||||
let b: i32 = 0;
|
||||
for (b < esz) {
|
||||
emitdatawbyte((nb & 255u64): u8);
|
||||
nb = nb >> 8u64;
|
||||
b += 1;
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
emitline("\"\n");
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -1770,6 +2022,21 @@ fn emitdefconstants(c: *cgen, file: *node) void = {
|
||||
};
|
||||
};
|
||||
};};
|
||||
// #129 A.3: array-typed def with N_ARRLIT rhs.
|
||||
// Parallel to emitletdataw array arm; uses DATA.
|
||||
if (r != nil) { if (r.kind == nkind.N_ARRLIT) {
|
||||
let at: *tinfo = d.lhs.type_: *tinfo;
|
||||
let au: *tinfo = at;
|
||||
for (au != nil && au.kind == tykind.TY_NAMED) {
|
||||
au = au.under;
|
||||
};
|
||||
if (au != nil) {
|
||||
if (au.kind == tykind.TY_ARRAY) {
|
||||
emitarraydata(c, "DATA",
|
||||
d.str, at, r);
|
||||
};
|
||||
};
|
||||
};};
|
||||
};
|
||||
};
|
||||
if (ok) {
|
||||
|
||||
@@ -896,6 +896,12 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
f32_elem = elemisf32c(c, baselocal.tnode);
|
||||
} else {
|
||||
let tn: *node = letvartnode(c, bn);
|
||||
// #129 A.3: array-typed defs now have DATA storage;
|
||||
// resolve their base via the same N_TARRAY path as
|
||||
// lets. defvartnode is the def-side sister of
|
||||
// letvartnode (parallel to defvarstructinfo at the
|
||||
// A.2 cgdot widening site).
|
||||
if (tn == nil) { tn = defvartnode(c, bn); };
|
||||
if (tn != nil) {
|
||||
if (tn.kind == nkind.N_TARRAY) {
|
||||
isglobalarr = true;
|
||||
|
||||
@@ -15515,6 +15515,12 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
f32_elem = elemisf32c(c, baselocal.tnode);
|
||||
} else {
|
||||
let tn: *node = letvartnode(c, bn);
|
||||
// #129 A.3: array-typed defs now have DATA storage;
|
||||
// resolve their base via the same N_TARRAY path as
|
||||
// lets. defvartnode is the def-side sister of
|
||||
// letvartnode (parallel to defvarstructinfo at the
|
||||
// A.2 cgdot widening site).
|
||||
if (tn == nil) { tn = defvartnode(c, bn); };
|
||||
if (tn != nil) {
|
||||
if (tn.kind == nkind.N_TARRAY) {
|
||||
isglobalarr = true;
|
||||
@@ -24939,6 +24945,21 @@ fn defvarstructinfo(c: *cgen, name: str) *structinfo = {
|
||||
return nil;
|
||||
};
|
||||
|
||||
// defvartnode — sister of letvartnode for top-level `def`s. Returns
|
||||
// the type-spec node (defent.dtnode) for the named def, or nil. #129
|
||||
// A.3 uses it in cgindex's array-base resolution so a `def: [N]T`
|
||||
// resolves through the same N_TARRAY-detect → LEAQ name(SB) shape as
|
||||
// a let array. Parallel to defvarstructinfo (#129 A.2) at the LOAD
|
||||
// side widening.
|
||||
fn defvartnode(c: *cgen, name: str) *node = {
|
||||
let e: *defent = c.defs;
|
||||
for (e != nil) {
|
||||
if (streq(e.dname, name)) { return e.dtnode; };
|
||||
e = e.dnext;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// emitdatawbyte — write one byte of an asm string literal using
|
||||
// the same escape rules as emitdefconstants / emitdatasection.
|
||||
fn emitdatawbyte(b: u8) void = {
|
||||
@@ -25163,6 +25184,30 @@ fn emitstructlitbytes(c: *cgen, structt: *tinfo, rhs: *node,
|
||||
f = f.tnext;
|
||||
continue;
|
||||
};
|
||||
// #129 A.3: array-typed field with N_ARRLIT rhs (the shape
|
||||
// parked in A.2). Recurses through emitarraylitbytes for
|
||||
// element-kind dispatch. Rule-7 stops loudly if rhs shape
|
||||
// doesn't match.
|
||||
if (fu != nil && fu.kind == tykind.TY_ARRAY) {
|
||||
if (vr == nil) {
|
||||
let m: str = "emitstructlitbytes: array field rhs nil (#129 A.3)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
if (vr.kind != nkind.N_ARRLIT) {
|
||||
let m: str = "emitstructlitbytes: array field rhs not N_ARRLIT (#129 A.3)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
if (!emitarraylitbytes(c, f.type_, vr, 1)) {
|
||||
let m: str = "emitstructlitbytes: array field rhs has non-reducible elements (#129 A.3)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
pos = fstart + fsz: u64;
|
||||
f = f.tnext;
|
||||
continue;
|
||||
};
|
||||
if (typeisfloat(f.type_)) {
|
||||
let isf32: bool = (fsz == 4);
|
||||
let neg: bool = false;
|
||||
@@ -25251,6 +25296,260 @@ fn emitstructdata(c: *cgen, directive: str, name: str,
|
||||
return true;
|
||||
};
|
||||
|
||||
// emitarraylitbytes — emit alen * esz bytes for an [N]T top-level let/
|
||||
// def with N_ARRLIT rhs. Mirrors cstage emit_array_lit_bytes. Per-
|
||||
// element dispatch:
|
||||
// - int (covers bool/rune/typed-int/N_UN-int): foldintliteral per
|
||||
// element. Existing pre-#129-A.3 emitletdataw array arm logic
|
||||
// preserved byte-for-byte so bootstrap consumers (lib/os, lib/
|
||||
// bufio, lib/strings, lib/encoding/utf8, lib/strconv/stof_data)
|
||||
// don't shift.
|
||||
// - float (f32/f64): peel N_CAST/N_UN(±), bitcast magnitude via
|
||||
// pointer-cast round-trip (mirror emitfloatlitdata), sign-XOR
|
||||
// top byte of each element inline. No 2^63 immediate.
|
||||
// - struct: per element call emitstructlitbytes (#129 A.2 helper).
|
||||
// - other element kinds (ptr/nested-array): returns false — caller
|
||||
// falls through to zero-init.
|
||||
//
|
||||
// Two-pass validate-then-emit (`emit_phase=0` validate-only, `=1`
|
||||
// actually emit) keeps emit-on-failure from emitting partial bytes
|
||||
// into an open DATA literal.
|
||||
fn emitarraylitbytes(c: *cgen, arrt: *tinfo, rhs: *node,
|
||||
emit_phase: i32) bool = {
|
||||
let au: *tinfo = arrt;
|
||||
for (au != nil && au.kind == tykind.TY_NAMED) { au = au.under; };
|
||||
if (au == nil) { return false; };
|
||||
if (au.kind != tykind.TY_ARRAY) { return false; };
|
||||
let esz: i32 = au.sub.size: i32;
|
||||
let alen: i32 = au.alen: i32;
|
||||
let eu: *tinfo = au.sub;
|
||||
for (eu != nil && eu.kind == tykind.TY_NAMED) { eu = eu.under; };
|
||||
|
||||
if (eu != nil && eu.kind == tykind.TY_STRUCT) {
|
||||
// Validate: every element must be N_STRUCTLIT (after N_CAST).
|
||||
let idx: i32 = 0;
|
||||
let last_ev: *node = nil;
|
||||
let e: *node = rhs.list;
|
||||
for (e != nil && idx < alen) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) { break; };
|
||||
};
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
if (ev == nil) { return false; };
|
||||
if (ev.kind != nkind.N_STRUCTLIT) { return false; };
|
||||
last_ev = ev;
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
if (emit_phase == 0) { return true; };
|
||||
idx = 0;
|
||||
let repeat: bool = false;
|
||||
e = rhs.list;
|
||||
for (e != nil && idx < alen) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) { repeat = true; break; };
|
||||
};
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
emitstructlitbytes(c, au.sub, ev, 0u64);
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
for (idx < alen) {
|
||||
if (repeat && last_ev != nil) {
|
||||
emitstructlitbytes(c, au.sub, last_ev, 0u64);
|
||||
} else {
|
||||
let bb: i32 = 0;
|
||||
for (bb < esz) { emitdatawbyte(0u8); bb += 1; };
|
||||
};
|
||||
idx += 1;
|
||||
};
|
||||
return true;
|
||||
};
|
||||
|
||||
if (typeisfloat(au.sub)) {
|
||||
let isf32: bool = typeisf32(au.sub);
|
||||
// Validate.
|
||||
let idx: i32 = 0;
|
||||
let e: *node = rhs.list;
|
||||
for (e != nil && idx < alen) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) { break; };
|
||||
};
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
if (ev != nil) { if (ev.kind == nkind.N_UN) {
|
||||
if (ev.op == tkind.TK_MINUS) {
|
||||
ev = ev.lhs;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
} else { if (ev.op == tkind.TK_PLUS) {
|
||||
ev = ev.lhs;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
};};
|
||||
};};
|
||||
if (ev == nil) { return false; };
|
||||
if (ev.kind != nkind.N_FLOATLIT) { return false; };
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
if (emit_phase == 0) { return true; };
|
||||
idx = 0;
|
||||
let last_bits: u64 = 0u64;
|
||||
let last_neg: bool = false;
|
||||
let repeat: bool = false;
|
||||
e = rhs.list;
|
||||
for (e != nil && idx < alen) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) { repeat = true; break; };
|
||||
};
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
let neg: bool = false;
|
||||
if (ev != nil) { if (ev.kind == nkind.N_UN) {
|
||||
if (ev.op == tkind.TK_MINUS) {
|
||||
neg = true;
|
||||
ev = ev.lhs;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
} else { if (ev.op == tkind.TK_PLUS) {
|
||||
ev = ev.lhs;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
};};
|
||||
};};
|
||||
let bits: u64 = ev.uval;
|
||||
if (isf32) {
|
||||
let dv: f64 = *((&bits): *f64);
|
||||
let fv: f32 = (dv: f32);
|
||||
let uv: u32 = *((&fv): *u32);
|
||||
bits = uv: u64;
|
||||
};
|
||||
let bb: i32 = 0;
|
||||
let nb: u64 = bits;
|
||||
for (bb < esz) {
|
||||
let byt: u8 = (nb & 255u64): u8;
|
||||
if (neg) {
|
||||
if (bb == esz - 1) { byt = byt ^ 128u8; };
|
||||
};
|
||||
emitdatawbyte(byt);
|
||||
nb = nb >> 8u64;
|
||||
bb += 1;
|
||||
};
|
||||
last_bits = bits;
|
||||
last_neg = neg;
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
for (idx < alen) {
|
||||
if (repeat) {
|
||||
let bb: i32 = 0;
|
||||
let nb: u64 = last_bits;
|
||||
for (bb < esz) {
|
||||
let byt: u8 = (nb & 255u64): u8;
|
||||
if (last_neg) {
|
||||
if (bb == esz - 1) { byt = byt ^ 128u8; };
|
||||
};
|
||||
emitdatawbyte(byt);
|
||||
nb = nb >> 8u64;
|
||||
bb += 1;
|
||||
};
|
||||
} else {
|
||||
let bb: i32 = 0;
|
||||
for (bb < esz) { emitdatawbyte(0u8); bb += 1; };
|
||||
};
|
||||
idx += 1;
|
||||
};
|
||||
return true;
|
||||
};
|
||||
|
||||
// Int-element path — preserved byte-for-byte from the pre-A.3
|
||||
// emitletdataw in-place arm so bootstrap consumers (u8/i8/u16
|
||||
// arrays) don't shift.
|
||||
let idx: i32 = 0;
|
||||
let e: *node = rhs.list;
|
||||
let last: u64 = 0u64;
|
||||
let repeat: bool = false;
|
||||
// Validate first.
|
||||
for (e != nil && idx < alen) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) { repeat = true; break; };
|
||||
};
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
if (ev == nil) { return false; };
|
||||
if (!foldintliteral(ev, &last)) { return false; };
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
if (emit_phase == 0) { return true; };
|
||||
idx = 0;
|
||||
last = 0u64;
|
||||
repeat = false;
|
||||
e = rhs.list;
|
||||
let inrepeat: bool = false;
|
||||
for (idx < alen) {
|
||||
let v: u64 = last;
|
||||
if (!inrepeat && e != nil) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) {
|
||||
inrepeat = true;
|
||||
} else {
|
||||
e = e.next;
|
||||
};
|
||||
} else {
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
if (!foldintliteral(ev, &v)) { v = 0u64; };
|
||||
last = v;
|
||||
e = e.next;
|
||||
};
|
||||
};
|
||||
let nb: u64 = v;
|
||||
let bb: i32 = 0;
|
||||
for (bb < esz) {
|
||||
emitdatawbyte((nb & 255u64): u8);
|
||||
nb = nb >> 8u64;
|
||||
bb += 1;
|
||||
};
|
||||
idx += 1;
|
||||
};
|
||||
return true;
|
||||
};
|
||||
|
||||
// emitarraydata — top-level wrapper. Two-pass validate-then-emit
|
||||
// avoids partial-byte corruption if the rhs shape can't reduce.
|
||||
// nil rhs is the "no-rhs zero-init" shape (e.g. `let buf: [N]u8;`
|
||||
// in lib/strconv/strconv.ww:287, lib/os/os.ww:92, etc.) — emit
|
||||
// alen*esz zero bytes. This was the implicit pre-A.3 emitletdataw
|
||||
// behavior (the old loop emitted zeros when `elems` was nil); the
|
||||
// refactor would have skipped emit entirely without this branch,
|
||||
// causing `undefined reference to strconv.f64tos_buf` at link.
|
||||
fn emitarraydata(c: *cgen, directive: str, name: str,
|
||||
arrt: *tinfo, rhs: *node) bool = {
|
||||
let au: *tinfo = arrt;
|
||||
for (au != nil && au.kind == tykind.TY_NAMED) { au = au.under; };
|
||||
if (au == nil) { return false; };
|
||||
if (au.kind != tykind.TY_ARRAY) { return false; };
|
||||
if (rhs == nil) {
|
||||
let total: u64 = arrt.size;
|
||||
emitline(directive);
|
||||
emitline(" ");
|
||||
emitsymname(c, name);
|
||||
emitline("(SB),\"");
|
||||
let i: u64 = 0u64;
|
||||
for (i < total) { emitdatawbyte(0u8); i = i + 1u64; };
|
||||
emitline("\"\n");
|
||||
return true;
|
||||
};
|
||||
if (!emitarraylitbytes(c, arrt, rhs, 0)) { return false; };
|
||||
emitline(directive);
|
||||
emitline(" ");
|
||||
emitsymname(c, name);
|
||||
emitline("(SB),\"");
|
||||
emitarraylitbytes(c, arrt, rhs, 1);
|
||||
emitline("\"\n");
|
||||
return true;
|
||||
};
|
||||
|
||||
fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
@@ -25437,72 +25736,31 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
emitline("\"\n");
|
||||
};
|
||||
};
|
||||
// Top-level `[N]T = [a, b, ...]` array global.
|
||||
// Emits N*esz bytes with each element's bytes
|
||||
// little-endian for the declared primitive width.
|
||||
// Element fold goes through foldintliteral (same
|
||||
// helper as emitdefconstants / scalar arm above)
|
||||
// so `-1i8` and friends emit their two's-complement
|
||||
// bytes after the leading N_CAST peel — pre-#19
|
||||
// this arm only matched bare N_INTLIT/N_RUNELIT and
|
||||
// silently emitted zero for unfoldable elements.
|
||||
// `...` (N_FIELD with str="...") repeats the last
|
||||
// folded value across the remaining slots.
|
||||
// #129 A.3: array global routes through the
|
||||
// emitarraydata SSoT helper. Int-elem path is
|
||||
// byte-for-byte preserved (bootstrap consumers in
|
||||
// lib/os, lib/bufio, lib/strings, lib/encoding/
|
||||
// utf8, lib/strconv/stof_data don't shift). Float/
|
||||
// struct elements gain emit via element-kind
|
||||
// dispatch. Helper validates pre-emit so partial
|
||||
// fold-failures don't corrupt the DATA literal.
|
||||
// No-rhs arrays (e.g. `let buf: [N]u8;`) go through
|
||||
// the same helper with rhs=nil → zero-fill branch.
|
||||
if (d.lhs != nil) {
|
||||
if (d.lhs.kind == nkind.N_TARRAY) {
|
||||
let elemn: *node = d.lhs.lhs;
|
||||
let esz: i32 = 8;
|
||||
if (elemn != nil) {
|
||||
if (elemn.kind == nkind.N_TNAME) {
|
||||
let ps: i32 = primsize(elemn.str);
|
||||
if (ps > 0) { esz = ps; };
|
||||
let rh: *node = d.rhs;
|
||||
let route: bool = false;
|
||||
if (rh == nil) { route = true; };
|
||||
if (rh != nil) {
|
||||
if (rh.kind == nkind.N_ARRLIT) {
|
||||
route = true;
|
||||
};
|
||||
};
|
||||
let total: i32 = sz;
|
||||
let alen: i32 = total / esz;
|
||||
let elems: *node = nil;
|
||||
if (d.rhs != nil) {
|
||||
if (d.rhs.kind == nkind.N_ARRLIT) {
|
||||
elems = d.rhs.list;
|
||||
};
|
||||
if (route) {
|
||||
let at: *tinfo = d.lhs.type_: *tinfo;
|
||||
emitarraydata(c, "DATAW", nm,
|
||||
at, rh);
|
||||
};
|
||||
emitline("DATAW ");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB),\"");
|
||||
let i: i32 = 0;
|
||||
let e: *node = elems;
|
||||
let last: u64 = 0u64;
|
||||
let inrepeat: bool = false;
|
||||
for (i < alen) {
|
||||
let v: u64 = last;
|
||||
if (!inrepeat && e != nil) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) {
|
||||
inrepeat = true;
|
||||
} else {
|
||||
e = e.next;
|
||||
};
|
||||
} else {
|
||||
let ev: *node = e;
|
||||
for (ev != nil) {
|
||||
if (ev.kind != nkind.N_CAST) { break; };
|
||||
ev = ev.lhs;
|
||||
};
|
||||
if (!foldintliteral(ev, &v)) { v = 0u64; };
|
||||
last = v;
|
||||
e = e.next;
|
||||
};
|
||||
};
|
||||
let nb: u64 = v;
|
||||
let b: i32 = 0;
|
||||
for (b < esz) {
|
||||
emitdatawbyte((nb & 255u64): u8);
|
||||
nb = nb >> 8u64;
|
||||
b += 1;
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
emitline("\"\n");
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -25565,6 +25823,21 @@ fn emitdefconstants(c: *cgen, file: *node) void = {
|
||||
};
|
||||
};
|
||||
};};
|
||||
// #129 A.3: array-typed def with N_ARRLIT rhs.
|
||||
// Parallel to emitletdataw array arm; uses DATA.
|
||||
if (r != nil) { if (r.kind == nkind.N_ARRLIT) {
|
||||
let at: *tinfo = d.lhs.type_: *tinfo;
|
||||
let au: *tinfo = at;
|
||||
for (au != nil && au.kind == tykind.TY_NAMED) {
|
||||
au = au.under;
|
||||
};
|
||||
if (au != nil) {
|
||||
if (au.kind == tykind.TY_ARRAY) {
|
||||
emitarraydata(c, "DATA",
|
||||
d.str, at, r);
|
||||
};
|
||||
};
|
||||
};};
|
||||
};
|
||||
};
|
||||
if (ok) {
|
||||
|
||||
260
test/wcc/919_array_static_init_run.c
Normal file
260
test/wcc/919_array_static_init_run.c
Normal file
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
Reference in New Issue
Block a user