cstage+selfhost+test: full-element store for [N]str array literals (#21)
[N]str array literals wrote only the .ptr half of each element.
cstage used esz=16 from `lu->sub->size` and a single per-element
MOVQ → .len trailed uninitialized stack residue. Wwstage was worse:
primsize("str")=0 fell through to esz=8, so element i+1's ptr-MOVQ
clobbered element i's .len slot, scrambling everything.
Worker-18 sidestepped during #18 by rewriting array primer rows to
[N]i64.
cstage cgen.c N_ARRLIT TY_STR branch: emit AX → base+i*16 then
BX → base+i*16+8. Repeat-`...` path mirrored. type_isstr handles
TY_UNTYPED_STR + TY_NAMED-aliased-str.
Wwstage cgenstmt.ww: isstrel flag conditionally drives the two-MOVQ
store in both the per-element walk and the repeat fill. The dispatch
loop was refactored to unify FIELD/ellipsis branches via isellip,
cleaning up the duplicated arms.
Wwstage cgenutil.ww slotsize/letslotsize: TNAME-"str" element gets
esz=16, replacing the primsize=0 → 8B fallback. Without this the
frame collapsed to 24B for [3]str.
Slice (24B), struct, tuple, tagged element arrays have the same root
cause but distinct width/layout concerns — deferred to #35 per rob.
Test 711 (arrlit_str_full): 7 rows × 2 stages = 14 fixtures —
str_lens_3el, str_ptrs_3el, str_repeat_5el (TK_ELLIPSIS), bool_3el,
rune_3el, i32_3el, i64_3el. Rune relies on the pre-existing esz==4
→ MOVL path (incidental correctness); sibling slot types pinned as
regression nets.
Followups filed: #34 (wwstage cgindex truncate on [N]str bare-let
read side, surfaced by this fix), #35 (composite element types),
#36 (primsize-returns-0-default-to-8 cleanup).
This commit is contained in:
@@ -5974,17 +5974,33 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
* Walk elements in declaration order, store each at off + i*esz
|
||||
* using the right width for the element type. The trailing
|
||||
* `...` repeat marker (an N_FIELD with str=="...") fills the
|
||||
* remaining slots with the last value. */
|
||||
* remaining slots with the last value.
|
||||
*
|
||||
* str element (16B = ptr+len) needs both halves stored: cgexpr
|
||||
* leaves a str as (AX=ptr, BX=len), and a single MOVQ from AX
|
||||
* would leave .len as whatever the stack held — silent
|
||||
* miscompile. The per-element store branches on TY_STR before
|
||||
* falling through to the scalar MOVB/MOVL/MOVQ path. Slice
|
||||
* (24B) and struct/tuple/tagged element arrays land in the
|
||||
* same multi-word-store gap; the read side (cgindex of an
|
||||
* [N]slice) has its own truncating-to-ptr bug, so slice
|
||||
* end-to-end repros surface sub-issues — both halves of the
|
||||
* slice-element fix are tracked as a follow-up. */
|
||||
if (n->rhs && n->rhs->kind == N_ARRLIT && lu
|
||||
&& lu->kind == TY_ARRAY) {
|
||||
int esz = lu->sub ? (int)lu->sub->size : 1;
|
||||
Type *esub = lu->sub;
|
||||
int esz = esub ? (int)esub->size : 1;
|
||||
int is_str_el = type_isstr(esub);
|
||||
int op = A_MOVQ;
|
||||
if (esz == 1) op = A_MOVB;
|
||||
else if (esz == 4) op = A_MOVL;
|
||||
/* esz == 2 (i16/u16) falls through to MOVQ — over-writes
|
||||
* by 6B; the next element store rewrites the high half.
|
||||
* For the last element this trails 6 bytes into the next
|
||||
* stack slot. Add MOVW to w6a if real i16 arrays land. */
|
||||
if (!is_str_el) {
|
||||
if (esz == 1) op = A_MOVB;
|
||||
else if (esz == 4) op = A_MOVL;
|
||||
/* esz == 2 (i16/u16) falls through to MOVQ —
|
||||
* over-writes by 6B; the next element store
|
||||
* rewrites the high half. For the last element
|
||||
* this trails 6 bytes into the next stack slot.
|
||||
* Add MOVW to w6a if real i16 arrays land. */
|
||||
}
|
||||
int idx = 0;
|
||||
Node *last = NULL;
|
||||
int repeat = 0;
|
||||
@@ -5995,16 +6011,33 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
break;
|
||||
}
|
||||
cgexpr(c, e, *locals);
|
||||
ins2(c, op, areg(D_AX),
|
||||
amem(D_BP, off + idx * esz));
|
||||
int base = off + idx * esz;
|
||||
if (is_str_el) {
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, base));
|
||||
ins2(c, A_MOVQ, areg(D_BX),
|
||||
amem(D_BP, base + 8));
|
||||
} else {
|
||||
ins2(c, op, areg(D_AX),
|
||||
amem(D_BP, base));
|
||||
}
|
||||
last = e;
|
||||
idx++;
|
||||
}
|
||||
if (repeat && last) {
|
||||
/* fill remaining slots with the value already in AX. */
|
||||
/* fill remaining slots with the value still in
|
||||
* AX (and BX for str). */
|
||||
while (idx < (int)lu->alen) {
|
||||
ins2(c, op, areg(D_AX),
|
||||
amem(D_BP, off + idx * esz));
|
||||
int base = off + idx * esz;
|
||||
if (is_str_el) {
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, base));
|
||||
ins2(c, A_MOVQ, areg(D_BX),
|
||||
amem(D_BP, base + 8));
|
||||
} else {
|
||||
ins2(c, op, areg(D_AX),
|
||||
amem(D_BP, base));
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user