Files
ww/test/lang/deref_narrow_test.ww
Hojun-Cho c9c5f6406f test: migrate deref/narrow/stride family-6 to test/lang @test, retire C twins (fold-2)
Continue fold-2: migrate the pointer-deref / narrow-load / pointer-array-stride
family from bespoke build+run C twins to test/lang @test, retiring each twin in
the same commit. Runtime coverage MOVES from $(TESTS) to test-lang (T1 runs +
asserts via `ww test`) + test-lang-byteid (T2 keeps cs==ww .s byte-id); the
$(TESTS) headline drops 5. Every assert is a primitive int/bool comparison with
a width-preserving (`==`) sink so a stale high half or wrong stride FAILS; the
narrow-signed rows route through an `: i32` cast against a 64B-widened literal
to force sign-extension onto the load. Each .c row maps to one inline @test fn
(50 cases total, strict superset of the C rows):

  947_deref_narrow_run.c  -> deref_narrow_test.ww  (#116, 10 rows: *p reads pointee width not 8B MOVQ; i32/u32/u8/i8/i16/u16 + !i32-alias + enum-i8 + bool/i64 controls)
  949_ptrarr_index_run.c  -> ptrarr_index_test.ww  (#61, 23 rows: p[i]/(&p[i])/(*p)[i] stride by size(T); {1,2,4,8}B, const+var idx, param/local/cast bases, neighbor guards, nested *[2][3], *[3]str header, siphash round())
  949_dotbase_arr_run.c   -> dotbase_arr_test.ww   (#135, 3 rows: (*struct).arrayfield[i] read/write/compound addresses the field)
  944_def_amp_idx_run.c   -> def_amp_idx_test.ww   (#94, 6 rows: &D[i] over a def-array; +plain/read/2D controls)
  944_alias_amp_idx_run.c -> alias_amp_idx_test.ww (#5, 8 rows: &a[i] over an alias-typed base classifies off the chased type; local/global, narrow, fwd-ref, plain+str controls)

The two sibling .c (949_dotbase_addr_slice_run, 944_alias_def_addr_run) stay in
$(TESTS): the first is run-only byteid=0 (#254 cs!=ww rows), the second carries
a LOUD rule-7 reject row — both routed to fold-3 (task #7). Bump
LANGBYTEID_EXPECTED_MIN 38->43.
2026-06-22 13:34:28 +09:00

97 lines
3.2 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// deref_narrow_test — 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 47 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);
};