w6c+cgen: emit module-level [N]str static-init data + relocations (fix #18)

A module-level `let xs: [N]str = ["a","b",...];` static init emitted no
.data: a str element carries a ptr->rodata relocation, not just bytes, so
it fell through the byte-only array-emit path and left the table symbol
undefined (w6l: undefined reference). Shared gap on both stages, not
rule-10.

emit_strarray_data / emitstrarraydata apply the scalar-str-global pattern
per element at offset idx*esz: a DATAW row of {0-ptr placeholder, LE len,
cap} plus a per-element DATAR sym+idx*esz,_S_n reloc. let_pre_intern /
letpreintern pre-intern each element strlit so the _S_ rodata rows precede
the DATAR references. Stride routes through etype->size (rule 13). Scoped
to the DATAW (`let`) directive: A_DATAR requires a DATAW holder, so
`def [N]str` and str-in-aggregate stay a filed follow-up.

919_strarray_static_run pins runtime (len-sum, element .ptr deref, var
index, empty slot, repeat suffix) + cs==ww byte-id. w6c + wwdump
combined.ww regenerated.
This commit is contained in:
2026-06-03 11:35:03 +09:00
parent a67259ca69
commit 58c8f4be02
6 changed files with 960 additions and 40 deletions

View File

@@ -11485,6 +11485,100 @@ emit_array_lit_bytes(FILE *out, Cg *c, Type *t, Node *rhs, int emit_phase)
return 1;
}
/* emit_strarray_data — module-level `let xs: [N]str = ["a","b",…];`
* static init (#18). The str-element case can't ride emit_array_lit_bytes:
* a str element carries a ptr→rodata relocation, not just bytes. So the
* scalar-str-global pattern (emit_lets str arm: DATAW header with a zero
* ptr placeholder + inline LE len, then a DATAR patching the ptr half)
* is applied per element at offset idx*esz. Each strlit was pre-interned
* by let_pre_intern so its rodata _S_ row exists before this row's DATAR
* references it.
*
* Scoped to DATAW (writable `let`): A_DATAR requires its holder be a
* DATAW slot (w6a asm.c:362), so a read-only `def [N]str` can't carry
* the relocs — that generalisation is a #18 follow-up. Returns 0 if the
* element type isn't str, leaving the generic array path / zero-init to
* the caller. */
static int
emit_strarray_data(FILE *out, Cg *c, const char *directive,
const char *name, const char *module, Type *t, Node *rhs)
{
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;
if (eu == NULL || eu->kind != TY_STR) return 0;
if (strcmp(directive, "DATAW") != 0) return 0;
int esz = (int)etype->size;
int alen = (int)u->alen;
/* Validate: each cast-peeled element is an N_STRLIT, up to an
* optional trailing `...` repeat marker. Bail (return 0) on any
* non-strlit so a non-reducible rhs still falls through to the
* generic path rather than emitting a partial row. */
Node *last_ev = NULL;
int repeat = 0;
int cnt = 0;
for (Node *e = rhs->list; e && cnt < 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_STRLIT) return 0;
last_ev = ev;
cnt++;
}
const char *sym = mod_mangle_value(c, name, module);
fprintf(out, "DATAW %s(SB),\"", sym);
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;
for (int i = 0; i < 8; i++) emit_data_byte(out, 0);
u64 v = ev->strlen;
for (int i = 0; i < 8; i++)
emit_data_byte(out, (u8)((v >> (i * 8)) & 0xff));
for (int i = 16; i < esz; i++) emit_data_byte(out, 0);
idx++;
}
while (idx < alen) {
u64 v = (repeat && last_ev != NULL) ? last_ev->strlen : 0;
for (int i = 0; i < 8; i++) emit_data_byte(out, 0);
for (int i = 0; i < 8; i++)
emit_data_byte(out, (u8)((v >> (i * 8)) & 0xff));
for (int i = 16; i < esz; i++) emit_data_byte(out, 0);
idx++;
}
fputs("\"\n", out);
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->strlen > 0) {
const char *lab = intern_strlit(c, ev->str, ev->strlen);
fprintf(out, "DATAR %s+%d(SB),%s(SB)\n",
sym, idx * esz, lab);
}
idx++;
}
while (idx < alen) {
if (repeat && last_ev != NULL && last_ev->strlen > 0) {
const char *lab = intern_strlit(c, last_ev->str,
last_ev->strlen);
fprintf(out, "DATAR %s+%d(SB),%s(SB)\n",
sym, idx * esz, lab);
}
idx++;
}
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). */
@@ -11494,6 +11588,10 @@ emit_array_data(FILE *out, Cg *c, const char *directive,
{
Type *u = type_unwrap(t);
if (u == NULL || u->kind != TY_ARRAY) return 0;
/* str-element arrays carry per-element ptr relocations — handled
* by the dedicated DATAW+DATAR helper (#18). */
if (emit_strarray_data(out, c, directive, name, module, t, rhs))
return 1;
if (!emit_array_lit_bytes(out, c, t, rhs, 0)) return 0;
fprintf(out, "%s %s(SB),\"", directive,
mod_mangle_value(c, name, module));
@@ -11704,9 +11802,53 @@ let_pre_intern(Cg *c, Node *file)
if (file == NULL) return;
for (Node *d = file->list; d; d = d->next) {
if (d->kind != N_LET) continue;
if (let_emit_size(d->type) != (int)ty_str->size) continue;
Node *r = d->rhs;
while (r != NULL && r->kind == N_CAST) r = r->lhs;
/* #18: `let xs: [N]str = […];` — pre-intern each element's
* strlit in element order (then repeat-fill) so emit_strarray_
* data's DATAR rows find an _S_ rodata row. Must match that
* helper's interning order exactly to keep labels stable. */
Type *u = type_unwrap(d->type);
if (u != NULL && u->kind == TY_ARRAY
&& r != NULL && r->kind == N_ARRLIT) {
Type *eu = (u->sub && u->sub->kind == TY_NAMED)
? u->sub->under : u->sub;
if (eu != NULL && eu->kind == TY_STR) {
int alen = (int)u->alen;
int cnt = 0;
Node *last_ev = NULL;
int repeat = 0;
for (Node *e = r->list; e && cnt < 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_STRLIT)
break;
if (ev->strlen > 0)
(void)intern_strlit(c, ev->str,
ev->strlen);
last_ev = ev;
cnt++;
}
if (repeat && last_ev != NULL
&& last_ev->strlen > 0) {
while (cnt < alen) {
(void)intern_strlit(c,
last_ev->str,
last_ev->strlen);
cnt++;
}
}
continue;
}
}
if (let_emit_size(d->type) != (int)ty_str->size) continue;
if (r == NULL || r->kind != N_STRLIT) continue;
if (r->strlen == 0) continue;
(void)intern_strlit(c, r->str, r->strlen);