cgen: load full 24B header on whole str/slice deref-by-value
`let s: str = *h` (a str/slice loaded by value through *str / *[]T)
fell through the N_UN deref arm to the scalar load, emitting a single
MOVQ that read only the 8B .ptr and left .len/.cap from stale registers,
so len(*p) returned garbage. Both stages emitted byte-identical wrong
code, so the self-compile byte-id gate was blind to it. Add a str/slice
arm that loads the full {ptr,len,cap} via cgslicehdr when the chased
pointee is TY_STR/TY_SLICE.
Surfaced by the codegen miscompile hunt (finding C1b). Pinned by
test/lang/deref_hdr_test.ww, which interposes a different-sized decoy
header so the test reddens when the arm is reverted.
This commit is contained in:
@@ -5525,6 +5525,19 @@ fn cgun(c: *cgen, n: *syntax.node) void = {
|
||||
if (rti != nil && rti.kind == syntax.tykind.TY_TAGGED) {
|
||||
if (rti.nullable == 0 && rti.size: i32 > 8) { return; };
|
||||
};
|
||||
// C1b: a whole str/slice loaded BY VALUE through *str /
|
||||
// *[]T — AX (the operand value) IS the 24B {ptr,len,cap}
|
||||
// header address. The scalar load below pulled ONLY word0
|
||||
// (.ptr); .len/.cap were then stored from stale BX/CX, so
|
||||
// len(*p) read garbage — byte-id-blind on both stages. Reuse
|
||||
// the same 3-word header load as the slice-field / cgindex
|
||||
// str-element arms.
|
||||
if (rti != nil) {
|
||||
if (rti.kind == syntax.tykind.TY_STR || rti.kind == syntax.tykind.TY_SLICE) {
|
||||
cgslicehdr(c, "AX");
|
||||
return;
|
||||
};
|
||||
};
|
||||
// f64/f32 result rides X0 (SSE), not AX — an integer MOVQ
|
||||
// strands the value off the float ABI and the caller's
|
||||
// MOVSD X0 reads stale bits (#96). Mirrors the float
|
||||
|
||||
Reference in New Issue
Block a user