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:
144
cmd/w6c/cgen.c
144
cmd/w6c/cgen.c
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user