Files
ww/test/wcc/data/idx_dot_aggret_subtail_run/case.ww
Hojun-Cho b3f4990979 cgen: store the full register into padded scratch for a 3/5/6/7-byte aggregate tail (#10)
The in-cap aggregate-receive materialise emitted a single narrow tail MOV that fell to MOVB for a 3/5/6/7-byte sub-8 tail, storing one byte while the scratch->dest copy read the full tail from uninitialised scratch — silently dropping members at the C2c whole-element arm (arr[i]=mk()) and loud-stopping at the #11 field arm. The scratch slot is ceil-8 padded (local_alloc/localadd round to 8) and the copy reads only tsz bytes, so flipping the tail default MOVB->MOVQ stores the full register harmlessly into the slot's own pad (in-bounds for in-cap <=24B); 1/2/4-byte tails stay byte-identical. Both stages symmetric. Removes the now-redundant #11 sub-8-tail loud-stop (keeps the float #165 and over-cap #234 loud-stops). The same narrow-tail materialise recurs at 6 other cstage sites (task #14).

Retires the obsolete idx_dot_aggret_subtail_loud //ww:error fixture (both stages now compile the case) and converts it to a positive cstage run-test; the struct-field shape is byte-id-divergent only via the pre-existing #9 frame-size bug, so the value pin uses array-field shapes. Value-asserting, reddens under each stage's independent revert.
2026-06-27 23:44:41 +09:00

30 lines
1.5 KiB
Plaintext

//ww:run
// #10 positive (converted from the retired idx_dot_aggret_subtail_loud tripwire):
// an in-cap aggregate-returning CALL into a 14B-tail-6 STRUCT field of an indexed
// element (arr[i].x = mk()). Pre-#10 both stages LOUD-STOPped this 3/5/6/7-tail
// materialise; #10 stores the FULL register into the ceil-8-padded scratch, so the
// over-stored bytes die in the pad and every member round-trips. main returns 0
// only if all members AND the neighbour pad survived (else a distinct nonzero).
// cstage-only RUN by harness design (T1): this shape's [N]s14 local frame size
// diverges cs!=ww (task #9, slotsize-vs-natural, benign — both stages compute
// correct values) so it cannot enter the T2 byte-id corpus. Reverting #10 re-arms
// the loud-stop -> this case fails to compile (rc!=0) -> reddens.
package main;
type t14 = struct { a:i16,b:i16,c:i16,d:i16,e:i16,f:i16,g:i16 };
type s14 = struct { x: t14, pad: i16 };
fn mk() t14 = { return t14{a=10i16,b=20i16,c=30i16,d=40i16,e=50i16,f=60i16,g=70i16}; };
export fn main() i32 = {
let arr: [2]s14;
arr[1].pad = 999i16;
arr[1].x = mk();
if (arr[1].x.a != 10i16) { return 1; };
if (arr[1].x.b != 20i16) { return 2; };
if (arr[1].x.c != 30i16) { return 3; };
if (arr[1].x.d != 40i16) { return 4; };
if (arr[1].x.e != 50i16) { return 5; };
if (arr[1].x.f != 60i16) { return 6; }; // tail byte 0-1
if (arr[1].x.g != 70i16) { return 7; }; // tail byte 2-3 (dropped pre-#10)
if (arr[1].pad != 999i16) { return 8; }; // tail MOVQ must not smash pad
return 0;
};