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:
@@ -11319,15 +11319,16 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
areg(D_X0));
|
areg(D_X0));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* str IS []u8 — load (ptr, len, cap) into
|
/* str IS []u8, and a slice is the same 24B
|
||||||
* (AX, BX, CX), the canonical slice-header ABI,
|
* {ptr,len,cap} header — load all three words into
|
||||||
* so chains like `t.1.len` propagate through the
|
* (AX, BX, CX), the canonical slice-header ABI, so
|
||||||
* slice-rhs convention (#1/Phase 3 collapse).
|
* chains like `t.1.len` propagate through the
|
||||||
* UNLIKE the field arms there is no slice-element
|
* slice-rhs convention (#1/Phase 3 collapse). #28:
|
||||||
* sibling here, so the triple is hand-authored;
|
* pre-fix the gate was str-only, so a SLICE tuple
|
||||||
* base is BP (frame) or CX (global base — CX is
|
* element fell to the fldloadop tail (one ptr word;
|
||||||
* written LAST so it survives the +0/+8 reads). */
|
* len/cap stale). base is BP (frame) or CX (global
|
||||||
if (fu && fu->kind == TY_STR) {
|
* base — CX written LAST so it survives the +0/+8). */
|
||||||
|
if (fu && (fu->kind == TY_STR || fu->kind == TY_SLICE)) {
|
||||||
ins2(c, A_MOVQ,
|
ins2(c, A_MOVQ,
|
||||||
amem(base_reg, base_disp + foff + 0),
|
amem(base_reg, base_disp + foff + 0),
|
||||||
areg(D_AX));
|
areg(D_AX));
|
||||||
|
|||||||
@@ -26646,7 +26646,14 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
if (tp != nil) {
|
if (tp != nil) {
|
||||||
let tpt: *node = tp.lhs;
|
let tpt: *node = tp.lhs;
|
||||||
if (isstrtype(c, tpt)) {
|
// str IS []u8, and a slice is the same 24B
|
||||||
|
// {ptr,len,cap} header — load all three words
|
||||||
|
// into (AX, BX, CX). #28: pre-fix the gate was
|
||||||
|
// str-only, so a SLICE tuple element fell to
|
||||||
|
// the scalar tail below (one ptr word; len/cap
|
||||||
|
// took stale registers). base is BP (frame),
|
||||||
|
// so the triple order has no clobber risk.
|
||||||
|
if (isstrtype(c, tpt) || isslicetype(c, tpt)) {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff((lc.off + foff + 0): i64);
|
emitoff((lc.off + foff + 0): i64);
|
||||||
emitline("(BP), AX\n");
|
emitline("(BP), AX\n");
|
||||||
|
|||||||
@@ -3495,7 +3495,14 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
if (tp != nil) {
|
if (tp != nil) {
|
||||||
let tpt: *node = tp.lhs;
|
let tpt: *node = tp.lhs;
|
||||||
if (isstrtype(c, tpt)) {
|
// str IS []u8, and a slice is the same 24B
|
||||||
|
// {ptr,len,cap} header — load all three words
|
||||||
|
// into (AX, BX, CX). #28: pre-fix the gate was
|
||||||
|
// str-only, so a SLICE tuple element fell to
|
||||||
|
// the scalar tail below (one ptr word; len/cap
|
||||||
|
// took stale registers). base is BP (frame),
|
||||||
|
// so the triple order has no clobber risk.
|
||||||
|
if (isstrtype(c, tpt) || isslicetype(c, tpt)) {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff((lc.off + foff + 0): i64);
|
emitoff((lc.off + foff + 0): i64);
|
||||||
emitline("(BP), AX\n");
|
emitline("(BP), AX\n");
|
||||||
|
|||||||
@@ -26646,7 +26646,14 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
if (tp != nil) {
|
if (tp != nil) {
|
||||||
let tpt: *node = tp.lhs;
|
let tpt: *node = tp.lhs;
|
||||||
if (isstrtype(c, tpt)) {
|
// str IS []u8, and a slice is the same 24B
|
||||||
|
// {ptr,len,cap} header — load all three words
|
||||||
|
// into (AX, BX, CX). #28: pre-fix the gate was
|
||||||
|
// str-only, so a SLICE tuple element fell to
|
||||||
|
// the scalar tail below (one ptr word; len/cap
|
||||||
|
// took stale registers). base is BP (frame),
|
||||||
|
// so the triple order has no clobber risk.
|
||||||
|
if (isstrtype(c, tpt) || isslicetype(c, tpt)) {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff((lc.off + foff + 0): i64);
|
emitoff((lc.off + foff + 0): i64);
|
||||||
emitline("(BP), AX\n");
|
emitline("(BP), AX\n");
|
||||||
|
|||||||
@@ -100,6 +100,45 @@ static const struct row rows[] = {
|
|||||||
" let v: []u8 = s: []u8;\n"
|
" let v: []u8 = s: []u8;\n"
|
||||||
" return v.cap: i32;\n"
|
" return v.cap: i32;\n"
|
||||||
"};\n", 4, K_RUN, NULL },
|
"};\n", 4, K_RUN, NULL },
|
||||||
|
/* #28 (#263 both-stages): tuple slice-element read `t.0` over a
|
||||||
|
* ([]u8, i64). A clobber() call between the tuple build and the
|
||||||
|
* read leaves junk in BX/CX; pre-fix the str-only arm fell to the
|
||||||
|
* scalar tail (ptr word only), so ys.len read stale BX. Reads len
|
||||||
|
* == 5 (distinct from any stale value) → 0. */
|
||||||
|
{ "tuple_slice_elem",
|
||||||
|
"package main;\n"
|
||||||
|
"fn clobber() i64 = {\n"
|
||||||
|
" let a: i64 = 111;\n"
|
||||||
|
" let b: i64 = 222;\n"
|
||||||
|
" return a + b;\n"
|
||||||
|
"};\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let xs: []u8 = [1u8, 2u8, 3u8, 4u8, 5u8];\n"
|
||||||
|
" let t: ([]u8, i64) = (xs, 7);\n"
|
||||||
|
" let junk: i64 = clobber();\n"
|
||||||
|
" let ys: []u8 = t.0;\n"
|
||||||
|
" if (ys.len == 5) { return 0; };\n"
|
||||||
|
" return 1;\n"
|
||||||
|
"};\n", 0, K_RUN, NULL },
|
||||||
|
/* #28 sibling: the slice element sits at tuple position 1 (foff != 0:
|
||||||
|
* an i64 occupies slot 0, the []u8 header starts at +8). Exercises the
|
||||||
|
* widened arm's offset arithmetic, which the position-0 row leaves
|
||||||
|
* untested. Same clobber()-between-build-and-read trigger; len == 6. */
|
||||||
|
{ "tuple_slice_elem_pos1",
|
||||||
|
"package main;\n"
|
||||||
|
"fn clobber() i64 = {\n"
|
||||||
|
" let a: i64 = 111;\n"
|
||||||
|
" let b: i64 = 222;\n"
|
||||||
|
" return a + b;\n"
|
||||||
|
"};\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let xs: []u8 = [1u8, 2u8, 3u8, 4u8, 5u8, 6u8];\n"
|
||||||
|
" let t: (i64, []u8) = (9, xs);\n"
|
||||||
|
" let junk: i64 = clobber();\n"
|
||||||
|
" let ys: []u8 = t.1;\n"
|
||||||
|
" if (ys.len == 6) { return 0; };\n"
|
||||||
|
" return 1;\n"
|
||||||
|
"};\n", 0, K_RUN, NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|||||||
Reference in New Issue
Block a user