wcc: tuple slice-element read loads the full 24B header, both stages

Reading a slice-typed tuple element (t.0) loaded only the pointer
word; len and cap took whatever was left in BX/CX — silent garbage in
BOTH stages once anything clobbered the registers between build and
read. Load all three header words at the tuple-element arm. Review
item #28.

Both stages move in one commit: one emission contract; splitting
would leave the byte-id gates red between the halves.
This commit is contained in:
2026-06-12 21:04:35 +09:00
parent 850746cfa8
commit 0a6f500b8c
5 changed files with 73 additions and 12 deletions

View File

@@ -11319,15 +11319,16 @@ cgexpr(Cg *c, Node *n, Local *locals)
areg(D_X0));
break;
}
/* str IS []u8 — load (ptr, len, cap) into
* (AX, BX, CX), the canonical slice-header ABI,
* so chains like `t.1.len` propagate through the
* slice-rhs convention (#1/Phase 3 collapse).
* UNLIKE the field arms there is no slice-element
* sibling here, so the triple is hand-authored;
* base is BP (frame) or CX (global base — CX is
* written LAST so it survives the +0/+8 reads). */
if (fu && fu->kind == TY_STR) {
/* str IS []u8, and a slice is the same 24B
* {ptr,len,cap} header — load all three words into
* (AX, BX, CX), the canonical slice-header ABI, so
* chains like `t.1.len` propagate through the
* slice-rhs convention (#1/Phase 3 collapse). #28:
* pre-fix the gate was str-only, so a SLICE tuple
* element fell to the fldloadop tail (one ptr word;
* len/cap stale). base is BP (frame) or CX (global
* base — CX written LAST so it survives the +0/+8). */
if (fu && (fu->kind == TY_STR || fu->kind == TY_SLICE)) {
ins2(c, A_MOVQ,
amem(base_reg, base_disp + foff + 0),
areg(D_AX));