wcc: 2D array [N][M]T static-init + double-index read (#156, A.3 capstone)

Close A.3's deferred shape-14 (nested array). (a) emit_array_lit_bytes
gains a TY_ARRAY-element arm (mechanical clone of the TY_STRUCT-element
arm — recurses; esz=etype->size, rule-13; ...-nested loud-reject). (b)
double-index read tbl[i][j]: when the indexed element is TY_ARRAY, leave
the sub-array ADDRESS in AX instead of dereferencing (sister of #135's
N_DOT-base fix, on the N_INDEX path) — new elemisarrayc/tinfoisarray
helpers, both stages. Storage + read = one 2D-end-to-end concern (A.2/A.3
storage+LOAD precedent).

Unblocks strconv fold-4's powers_of_ten[596][2]u64 (direct double-index
access). Bootstrap-NEUTRAL (new arms gate on TY_ARRAY-element; 1D
consumers byte-identical, 990-997 green). Test 919 +2D rows + 3D +
...-nested-reject. Deferred siblings: #155 (sub-array bind / whole-
aggregate copy), #160 (global-struct-field index base).
This commit is contained in:
2026-05-27 12:21:15 +09:00
parent 88f3d67b28
commit cbeffea7d8
7 changed files with 508 additions and 1 deletions

View File

@@ -6977,6 +6977,18 @@ cgexpr(Cg *c, Node *n, Local *locals)
ins2(c, A_MOVQ, amem(D_BP, off), areg(D_BX));
}
ins2(c, A_ADDQ, areg(D_AX), areg(D_BX));
/* #156 (PREREQ-1 read-half): element is itself an array
* ([N][M]T → element [M]T). This index yields the sub-
* array's ADDRESS, not a loaded value — the outer index
* adds its own offset and only the final scalar element
* dereferences. Sister of #135 (N_DOT-base-on-[N]T-field
* needs ADDRESS). BX holds base+idx*esz; move it to AX (the
* value-result reg). Gated on TY_ARRAY element so 1D arrays
* are byte-identical (no 2D consumer pre-#156). */
if (esubu && esubu->kind == TY_ARRAY) {
ins2(c, A_MOVQ, areg(D_BX), areg(D_AX));
break;
}
/* str/slice element: load the full (ptr, len, cap) header
* into (AX, BX, CX) — both are 24B since #1, so the cap
* word must survive. Kind-gate on type_isstr||type_isslice,
@@ -7045,6 +7057,11 @@ cgexpr(Cg *c, Node *n, Local *locals)
cgexpr(c, n->lhs, locals);
ins1(c, A_POPQ, areg(D_BX));
ins2(c, A_ADDQ, areg(D_BX), areg(D_AX));
/* #156 (PREREQ-1 read-half): array element → AX already holds
* &elem (base+idx*esz); a nested index adds its offset and
* dereferences. See the N_IDENT arm above. */
if (esubu && esubu->kind == TY_ARRAY)
break;
/* str/slice element via fallback base: load the full (ptr, len,
* cap) header into (AX, BX, CX). Kind-gate on type_isstr||
* type_isslice, never size==24 (see Site A). Base is AX. */
@@ -8914,6 +8931,46 @@ emit_array_lit_bytes(FILE *out, Cg *c, Type *t, Node *rhs, int emit_phase)
return 1;
}
/* #129 A.3 capstone (PREREQ-1, #156): nested-array element [M]T
* inside [N][M]T. Mirror of the TY_STRUCT-element arm above and of
* the TY_ARRAY-field-in-struct arm in emit_struct_lit_bytes — recurse
* into emit_array_lit_bytes per element; recursion bottoms out at
* scalar (int/float) elements. esz = etype->size gives the per-
* element stride (rule 13, no manual stride math). The `...` repeat
* marker with nested-array elements is rejected loud (rule 7): no
* consumer needs it (powers_of_ten is fully enumerated) and the
* scalar-repeat byte-fill cannot reduce a nested N_ARRLIT. */
if (eu && eu->kind == TY_ARRAY) {
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)
fatal("emit_array_lit_bytes: '...' repeat with "
"nested-array elements unsupported "
"(#129 A.3, rule 7)");
Node *ev = e;
while (ev && ev->kind == N_CAST) ev = ev->lhs;
if (ev == NULL || ev->kind != N_ARRLIT) return 0;
if (!emit_array_lit_bytes(out, c, etype, ev, 0))
return 0;
idx++;
}
if (!emit_phase) return 1;
idx = 0;
for (Node *e = rhs->list; e && idx < alen; e = e->next) {
Node *ev = e;
while (ev && ev->kind == N_CAST) ev = ev->lhs;
emit_array_lit_bytes(out, c, etype, ev, 1);
idx++;
}
while (idx < alen) {
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