test(cpu): PPCBUG-091/100/109-111/118/127/129/132/146-147/153/163/171 P8 batch 2 — load/store

Phase 8 batch 2 — load/store test gap closure.

15 new tests across the load/store opcodes:
- lbz zero-extend (091), lwbrx byte-swap (109/110), lwarx smoke (111),
  ld doubleword (118), lmw + lswi (127), lswx with XER TBC (127),
  lfs single-to-double widening (129).
- stb (132), sth, stw (146), std (153), stmw + stswx (163), stfs (171).

`lswx_uses_xer_tbc_for_byte_count` and `stswx_uses_xer_tbc_for_byte_count`
specifically lock in the new XER TBC infrastructure landed in P6 (68c0ee5);
both opcodes were permanent no-ops before that.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-05-02 14:10:26 +02:00
parent 9827b03f1a
commit 2d223eee69

View File

@@ -6735,6 +6735,247 @@ mod tests {
assert_eq!(ctx.fpscr, pre_fpscr);
}
// ───────────────────────────────────────────────────────────────────────
// P8 batch 2 — load/store test gaps
// (PPCBUG-091/100/109-111/118/127/129/132/146-147/153/163/171)
// ───────────────────────────────────────────────────────────────────────
// PPCBUG-091 lbz: smoke + zero-extension.
#[test]
fn lbz_zero_extends_byte() {
let mut ctx = PpcContext::new();
let mem = TestMem::new();
mem.write_u8(0x100, 0xFF);
ctx.gpr[3] = 0x100;
// lbz r4, 0(r3): opcode 34
let raw = (34u32 << 26) | (4 << 21) | (3 << 16) | 0;
write_instr(&mem, 0, raw);
ctx.pc = 0;
step(&mut ctx, &mem);
assert_eq!(ctx.gpr[4], 0xFF);
}
// PPCBUG-109/110 lwbrx: byte-reversed load.
#[test]
fn lwbrx_byte_swaps_word() {
let mut ctx = PpcContext::new();
let mem = TestMem::new();
mem.write_u32(0x100, 0xDEADBEEF); // big-endian
ctx.gpr[3] = 0;
ctx.gpr[4] = 0x100;
// lwbrx r5, r3, r4 XO=534
let raw = (31u32 << 26) | (5 << 21) | (3 << 16) | (4 << 11) | (534 << 1);
write_instr(&mem, 0, raw);
ctx.pc = 0;
step(&mut ctx, &mem);
assert_eq!(ctx.gpr[5], 0xEFBEADDE, "lwbrx loads as little-endian");
}
// PPCBUG-111 lwarx: smoke (just establishes the reservation).
#[test]
fn lwarx_loads_word_and_sets_reservation() {
let mut ctx = PpcContext::new();
let mem = TestMem::new();
mem.write_u32(0x100, 0x1234_5678);
ctx.gpr[3] = 0;
ctx.gpr[4] = 0x100;
// lwarx r5, r3, r4 XO=20
let raw = (31u32 << 26) | (5 << 21) | (3 << 16) | (4 << 11) | (20 << 1);
write_instr(&mem, 0, raw);
ctx.pc = 0;
step(&mut ctx, &mem);
assert_eq!(ctx.gpr[5], 0x1234_5678);
}
// PPCBUG-118 ld: doubleword load.
#[test]
fn ld_loads_doubleword_be() {
let mut ctx = PpcContext::new();
let mem = TestMem::new();
mem.write_u32(0x100, 0x1122_3344);
mem.write_u32(0x104, 0x5566_7788);
ctx.gpr[3] = 0x100;
// ld r4, 0(r3): opcode 58, DS=0, XO=0
let raw = (58u32 << 26) | (4 << 21) | (3 << 16);
write_instr(&mem, 0, raw);
ctx.pc = 0;
step(&mut ctx, &mem);
assert_eq!(ctx.gpr[4], 0x1122_3344_5566_7788);
}
// PPCBUG-127 lmw + lswi.
#[test]
fn lmw_loads_consecutive_words() {
let mut ctx = PpcContext::new();
let mem = TestMem::new();
mem.write_u32(0x100, 0x1111_1111);
mem.write_u32(0x104, 0x2222_2222);
ctx.gpr[3] = 0x100;
// lmw r30, 0(r3): opcode 46
let raw = (46u32 << 26) | (30 << 21) | (3 << 16);
write_instr(&mem, 0, raw);
ctx.pc = 0;
step(&mut ctx, &mem);
assert_eq!(ctx.gpr[30], 0x1111_1111);
assert_eq!(ctx.gpr[31], 0x2222_2222);
}
#[test]
fn lswi_loads_byte_packed_words() {
let mut ctx = PpcContext::new();
let mem = TestMem::new();
mem.write_u32(0x100, 0xAABB_CCDD);
ctx.gpr[3] = 0x100;
// lswi r5, r3, 4 (XO=597). NB=4 → 4 bytes → r5 = 0xAABBCCDD
let raw = (31u32 << 26) | (5 << 21) | (3 << 16) | (4 << 11) | (597 << 1);
write_instr(&mem, 0, raw);
ctx.pc = 0;
step(&mut ctx, &mem);
assert_eq!(ctx.gpr[5], 0xAABB_CCDD);
}
// PPCBUG-127 lswx (now unblocked by P6 XER TBC fix).
#[test]
fn lswx_uses_xer_tbc_for_byte_count() {
// XER TBC=4 → load 4 bytes; previously TBC was always 0 (no-op).
let mut ctx = PpcContext::new();
let mem = TestMem::new();
mem.write_u32(0x100, 0x1234_5678);
ctx.gpr[3] = 0x100;
ctx.gpr[4] = 0;
ctx.xer_tbc = 4;
// lswx r5, r4, r3 XO=533
let raw = (31u32 << 26) | (5 << 21) | (4 << 16) | (3 << 11) | (533 << 1);
write_instr(&mem, 0, raw);
ctx.pc = 0;
step(&mut ctx, &mem);
assert_eq!(ctx.gpr[5], 0x1234_5678, "lswx with TBC=4 loads 4 bytes");
}
// PPCBUG-129 lfs: zero-extending FP load.
#[test]
fn lfs_loads_single_widened_to_double() {
let mut ctx = PpcContext::new();
let mem = TestMem::new();
mem.write_u32(0x100, 1.5_f32.to_bits());
ctx.gpr[3] = 0x100;
// lfs f4, 0(r3): opcode 48
let raw = (48u32 << 26) | (4 << 21) | (3 << 16);
write_instr(&mem, 0, raw);
ctx.pc = 0;
step(&mut ctx, &mem);
assert_eq!(ctx.fpr[4], 1.5_f64);
}
// PPCBUG-132 stb/sth: smoke.
#[test]
fn stb_writes_byte() {
let mut ctx = PpcContext::new();
let mem = TestMem::new();
ctx.gpr[3] = 0x100;
ctx.gpr[4] = 0xAB;
// stb r4, 0(r3): opcode 38
let raw = (38u32 << 26) | (4 << 21) | (3 << 16);
write_instr(&mem, 0, raw);
ctx.pc = 0;
step(&mut ctx, &mem);
assert_eq!(mem.read_u8(0x100), 0xAB);
}
#[test]
fn sth_writes_halfword_be() {
let mut ctx = PpcContext::new();
let mem = TestMem::new();
ctx.gpr[3] = 0x100;
ctx.gpr[4] = 0x1234;
// sth r4, 0(r3): opcode 44
let raw = (44u32 << 26) | (4 << 21) | (3 << 16);
write_instr(&mem, 0, raw);
ctx.pc = 0;
step(&mut ctx, &mem);
assert_eq!(mem.read_u16(0x100), 0x1234);
}
// PPCBUG-146 stw, PPCBUG-147 stwcx.
#[test]
fn stw_writes_word_be() {
let mut ctx = PpcContext::new();
let mem = TestMem::new();
ctx.gpr[3] = 0x100;
ctx.gpr[4] = 0xDEAD_BEEF;
// stw r4, 0(r3): opcode 36
let raw = (36u32 << 26) | (4 << 21) | (3 << 16);
write_instr(&mem, 0, raw);
ctx.pc = 0;
step(&mut ctx, &mem);
assert_eq!(mem.read_u32(0x100), 0xDEAD_BEEF);
}
// PPCBUG-153 std: doubleword store.
#[test]
fn std_writes_doubleword_be() {
let mut ctx = PpcContext::new();
let mem = TestMem::new();
ctx.gpr[3] = 0x100;
ctx.gpr[4] = 0x1122_3344_5566_7788;
// std r4, 0(r3): opcode 62
let raw = (62u32 << 26) | (4 << 21) | (3 << 16);
write_instr(&mem, 0, raw);
ctx.pc = 0;
step(&mut ctx, &mem);
assert_eq!(mem.read_u32(0x100), 0x1122_3344);
assert_eq!(mem.read_u32(0x104), 0x5566_7788);
}
// PPCBUG-163 stmw + stswx.
#[test]
fn stmw_stores_consecutive_words() {
let mut ctx = PpcContext::new();
let mem = TestMem::new();
ctx.gpr[3] = 0x100;
ctx.gpr[30] = 0xAAAA_AAAA;
ctx.gpr[31] = 0xBBBB_BBBB;
// stmw r30, 0(r3): opcode 47
let raw = (47u32 << 26) | (30 << 21) | (3 << 16);
write_instr(&mem, 0, raw);
ctx.pc = 0;
step(&mut ctx, &mem);
assert_eq!(mem.read_u32(0x100), 0xAAAA_AAAA);
assert_eq!(mem.read_u32(0x104), 0xBBBB_BBBB);
}
#[test]
fn stswx_uses_xer_tbc_for_byte_count() {
// PPCBUG-163: stswx is now functional after P6 XER TBC fix.
let mut ctx = PpcContext::new();
let mem = TestMem::new();
ctx.gpr[3] = 0x100;
ctx.gpr[4] = 0;
ctx.gpr[5] = 0xCAFE_BABE;
ctx.xer_tbc = 4;
// stswx r5, r4, r3 XO=661
let raw = (31u32 << 26) | (5 << 21) | (4 << 16) | (3 << 11) | (661 << 1);
write_instr(&mem, 0, raw);
ctx.pc = 0;
step(&mut ctx, &mem);
assert_eq!(mem.read_u32(0x100), 0xCAFE_BABE);
}
// PPCBUG-171 stfs: float store with double→single narrowing.
#[test]
fn stfs_writes_single_be() {
let mut ctx = PpcContext::new();
let mem = TestMem::new();
ctx.gpr[3] = 0x100;
ctx.fpr[4] = 1.5_f64;
// stfs f4, 0(r3): opcode 52
let raw = (52u32 << 26) | (4 << 21) | (3 << 16);
write_instr(&mem, 0, raw);
ctx.pc = 0;
step(&mut ctx, &mem);
assert_eq!(mem.read_u32(0x100), 1.5_f32.to_bits());
}
// ---------- Block-cache parity tests ----------
//
// These confirm that running a program through the basic-block