Every section banner dies (103 -> 0) across test/lang, the observer suites, the C carriers, and the five comment-heavy corpus fixtures; banner provenance (#N cites, carrier numbers, repair-cluster labels) folded into headers or adjacent WHY comments. Narration deleted; row provenance, ref cites, divergence pins, and layout contracts kept (fwd-ref decl-order guards and bootstrap-gate corpus rationale restored where the sweep over-cut). Comment-only proven: all 3742 wwbuild workdir .s byte-identical before/after; test-commit and test-byteid (161 lang + 1399 data, 0 pinned-divergent) green.
97 lines
3.2 KiB
Plaintext
97 lines
3.2 KiB
Plaintext
// An integer pointer deref (`*p`) must read its pointee's
|
||
// ACTUAL width, not a fixed 8B MOVQ, migrated from test/wcc/947_deref_narrow_run.c
|
||
// (#116). Before #116 the TK_STAR integer arm emitted a raw `MOVQ (AX),AX`
|
||
// regardless of pointee size, so `*p` over a packed `*i32`/`*u32`/`*iN` slot
|
||
// pulled the next 4–7 bytes into the high half of RAX. Truncating sinks (an i32
|
||
// store) dropped the garbage; WIDTH-PRESERVING sinks (a `==` compare that
|
||
// widens the literal to 8B → CMPQ) saw it. The fix routes the load through
|
||
// localloadop(n->type), the MOVSXD/MOVSWQ/MOVSBQ + MOVL/MOVZWQ/MOVZBQ dispatch
|
||
// already shared by IDENT/DOT/index loads. The .s is byte-identical cs==ww, so
|
||
// these behavioral @tests are the runtime net the byte-id gate can't be.
|
||
//
|
||
// Each @test deliberately uses a `==` (CMPQ-width) sink so a stale high half
|
||
// would make the assert FAIL; the narrow-signed rows route through an `: i32`
|
||
// cast whose compare against a 64B-widened literal forces the sign-extension
|
||
// onto the load. alias/enum rows exercise the TY_NAMED/TBANG/TY_ENUM peel in
|
||
// localloadop's tinfo lookup; the struct-field base on the i16/u16/alias/enum
|
||
// rows dodges the sibling [N]alias array-init store-width concern (out of #116
|
||
// scope). i64_ctrl is the regression guard that a width-correct case keeps MOVQ.
|
||
|
||
package deref_narrow_test;
|
||
|
||
type err = !i32;
|
||
type abox = struct { v: err };
|
||
type ibox = struct { v: i16, m: u16 };
|
||
type ubox = struct { v: u16, m: u16 };
|
||
type flag = enum i8 { A = -2i8, B = 1i8 };
|
||
type fbox = struct { v: flag, m: u8 };
|
||
|
||
@test fn i32_cmpq() void = {
|
||
let buf: [4]i32 = [10, 20, 30, 40];
|
||
let p: *i32 = &buf[1];
|
||
assert(*p == 20);
|
||
};
|
||
|
||
@test fn u32_zeroext() void = {
|
||
let buf: [3]u32 = [10u32, 20u32, 0xFFFFFFFFu32];
|
||
let p: *u32 = &buf[2];
|
||
assert(*p == 0xFFFFFFFFu32);
|
||
};
|
||
|
||
@test fn u8_zeroext() void = {
|
||
let buf: [3]u8 = [10u8, 20u8, 0xFFu8];
|
||
let p: *u8 = &buf[2];
|
||
assert(*p == 0xFFu8);
|
||
};
|
||
|
||
@test fn i8_signext() void = {
|
||
let buf: [3]i8 = [-1i8, -2i8, -3i8];
|
||
let p: *i8 = &buf[1];
|
||
let v: i32 = (*p): i32;
|
||
assert(v == -2i32);
|
||
};
|
||
|
||
@test fn alias_signext() void = {
|
||
// `type err = !i32; *err` — the TY_NAMED/TBANG peel must reach the
|
||
// underlying i32 width for MOVSXD to fire.
|
||
let b: abox = abox { v = -7i32: err };
|
||
let p: *err = &b.v;
|
||
let r: i32 = (*p): i32;
|
||
assert(r == -7i32);
|
||
};
|
||
|
||
@test fn bool_ctrl() void = {
|
||
let buf: [3]bool = [true, false, true];
|
||
let p: *bool = &buf[1];
|
||
assert(*p == false);
|
||
};
|
||
|
||
@test fn i16_signext() void = {
|
||
let b: ibox = ibox { v = -200i16, m = 0xABCDu16 };
|
||
let p: *i16 = &b.v;
|
||
let r: i32 = (*p): i32;
|
||
assert(r == -200i32);
|
||
};
|
||
|
||
@test fn u16_zeroext() void = {
|
||
let b: ubox = ubox { v = 0xFFFFu16, m = 0xABCDu16 };
|
||
let p: *u16 = &b.v;
|
||
assert(*p == 0xFFFFu16);
|
||
};
|
||
|
||
@test fn enum_signext() void = {
|
||
// `type flag = enum i8 { ... }; *flag` — TY_NAMED/TY_ENUM peel in
|
||
// typeissigned + tinfo.size on the NAMED wrapper must land on MOVSBQ.
|
||
let b: fbox = fbox { v = flag.A, m = 0xABu8 };
|
||
let p: *flag = &b.v;
|
||
let r: i32 = ((*p): i8): i32;
|
||
assert(r == -2i32);
|
||
};
|
||
|
||
@test fn i64_ctrl() void = {
|
||
// width-correct case — MUST still emit MOVQ; regression guard.
|
||
let buf: [3]i64 = [100i64, 200i64, 300i64];
|
||
let p: *i64 = &buf[1];
|
||
assert(*p == 200i64);
|
||
};
|