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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user