A `let t: [N][]u8 = [a, b]` / `[N]str` literal init lowered each
element's {ptr,len,cap} header into AX/BX/CX (cgexpr) but stored only
some words: a slice element fell through to the scalar 1-word MOVQ
(dropping .len AND .cap), a str element stored 2 words (dropping .cap,
latent). Each element is 24B (post-#1) and must be copied whole.
wwstage was worse — a slice element matched no esz branch, so esz
stayed the 8 sentinel: the per-element stride collapsed (element i+1
overwrote element i's tail), the -96-vs-80 cs!=ww frame divergence.
This is the str/slice arm of the #270 aggregate-element-store family.
struct/array/tuple already copy correctly via the #270-1c is_agg
multi-word path; str/slice were the documented follow-up (cgen.c:9037,
cgenstmt.ww deferral). They can't join is_agg (that path word-copies
from a source slot and rejects non-ident/structlit elements, whereas
str/slice elements are commonly exprs cgexpr lowers into registers) —
the correct mechanism is the existing register header store, extended.
Fix (BOTH stages, converged byte-identical): cstage adds
is_slice_el = type_isslice(esub) and stores 3 words (incl CX->base+16,
the cap) for `is_str_el || is_slice_el`, in the main loop and the
repeat-fill. wwstage adds isslicel (esubti.kind == TY_SLICE -> esz =
esubti.size, fixing the stride) and the matching 3-word store. Closes
[N][]u8 (the bug) and the latent [N]str cap-drop in one branch.
The latent str cap-drop is now stored, but the indexed-element `.cap`
READ (`t[i].cap`) stays broken — a distinct cgindex/dot-selector bug,
cs!=ww divergent, filed as task #13. The new test validates the stored
cap via a whole-element copy (`let q = t[i]; q.cap`), which reads
through the correct ident-load path. [N]tagged literal init is the
remaining sibling (is_agg excludes TY_TAGGED), task #12.
Test 683_arr_strslice_elem: table-driven, dual-stage runtime + asm
byte-id; slice/str .len, 3-element stride-24, cap-via-copy, .ptr deref,
plus a [N]struct regression pin proving the is_agg path is untouched.
This commit is contained in:
@@ -9030,16 +9030,14 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
* `...` repeat marker (an N_FIELD with str=="...") fills the
|
||||
* 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. */
|
||||
* str/slice element (24B = ptr+len+cap, post-#1) needs all
|
||||
* three words stored: cgexpr leaves it as (AX=ptr, BX=len,
|
||||
* CX=cap), and a single MOVQ from AX would leave .len/.cap as
|
||||
* whatever the stack held — silent miscompile (#20/#270 str-
|
||||
* slice arm). The per-element store branches on TY_STR/TY_SLICE
|
||||
* before falling through to the scalar MOVB/MOVL/MOVQ path.
|
||||
* [N]tagged element arrays still land in the multi-word gap
|
||||
* (is_agg excludes TY_TAGGED) — tracked as task #12. */
|
||||
if (n->rhs && n->rhs->kind == N_ARRLIT && lu
|
||||
&& lu->kind == TY_ARRAY) {
|
||||
Type *esub = lu->sub;
|
||||
@@ -9054,6 +9052,11 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
|| esubu->kind == TY_ARRAY
|
||||
|| esubu->kind == TY_TUPLE);
|
||||
int is_str_el = type_isstr(esub);
|
||||
/* #20/#270 str-slice arm: a slice element is a 24B
|
||||
* {ptr,len,cap} header just like str; cgexpr lowers it
|
||||
* into AX/BX/CX. Both must store all three words — the
|
||||
* scalar 1-word MOVQ below drops .len and .cap. */
|
||||
int is_slice_el = type_isslice(esub);
|
||||
/* float element → store FROM X0; the AX path stores
|
||||
* raw double low-bits, garbage for f32 (#122, twin of
|
||||
* the arr[i]= store fix and the cgen.c:6423 read). */
|
||||
@@ -9134,11 +9137,13 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
continue;
|
||||
}
|
||||
cgexpr(c, e, *locals);
|
||||
if (is_str_el) {
|
||||
if (is_str_el || is_slice_el) {
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, base));
|
||||
ins2(c, A_MOVQ, areg(D_BX),
|
||||
amem(D_BP, base + 8));
|
||||
ins2(c, A_MOVQ, areg(D_CX),
|
||||
amem(D_BP, base + 16));
|
||||
} else if (is_float_el) {
|
||||
ins2(c, fmov, areg(D_X0),
|
||||
amem(D_BP, base));
|
||||
@@ -9157,11 +9162,13 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
* AX (and BX for str). */
|
||||
while (idx < (int)lu->alen) {
|
||||
int base = off + idx * esz;
|
||||
if (is_str_el) {
|
||||
if (is_str_el || is_slice_el) {
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, base));
|
||||
ins2(c, A_MOVQ, areg(D_BX),
|
||||
amem(D_BP, base + 8));
|
||||
ins2(c, A_MOVQ, areg(D_CX),
|
||||
amem(D_BP, base + 16));
|
||||
} else if (is_float_el) {
|
||||
ins2(c, fmov, areg(D_X0),
|
||||
amem(D_BP, base));
|
||||
|
||||
Reference in New Issue
Block a user