From c9f194dda12f9bef329479425d1a858615f12294 Mon Sep 17 00:00:00 2001 From: MechaCat02 Date: Fri, 1 May 2026 20:47:32 +0200 Subject: [PATCH] =?UTF-8?q?fix(cpu):=20review=20fixes=20=E2=80=94=20stswi/?= =?UTF-8?q?stswx=20two-line=20guard,=20dcbz/dcbz128=20invalidate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PPCBUG-160 partial: stswi's single invalidate_for_write(ea) only covered the first cache line; with nb up to 32, the write span can cross a 128-byte line boundary. Replace with two-call guard: first_line = ea & !RESERVATION_MASK last_line = ea.wrapping_add(nb - 1) & !RESERVATION_MASK invalidate first; if last != first, invalidate last. PPCBUG-160 partial: stswx had the same single-call gap; nb from XER[0:6] can be up to 127 bytes. Same two-call guard applied; wrapped in `if nb > 0` to guard against nb==0 underflow (XER TBC field is 0 when no bytes to store). dcbz: zeroes 32 bytes at a 32-byte-aligned EA — touches exactly one 128-byte cache line; add canonical single-call invalidate guard (was entirely missing). dcbz128: zeroes 128 bytes at a 128-byte-aligned EA — one full reservation line; add canonical single-call invalidate guard (was entirely missing). Co-Authored-By: Claude Opus 4.7 (1M context) --- crates/xenia-cpu/src/interpreter.rs | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/crates/xenia-cpu/src/interpreter.rs b/crates/xenia-cpu/src/interpreter.rs index 9795ac8..c22cd0b 100644 --- a/crates/xenia-cpu/src/interpreter.rs +++ b/crates/xenia-cpu/src/interpreter.rs @@ -1464,7 +1464,12 @@ fn execute(ctx: &mut PpcContext, mem: &dyn MemoryAccess, instr: &DecodedInstr) - let mut rs = instr.rs(); let mut bytes_left = nb; if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) { - if t.has_active_reservers() { t.invalidate_for_write(ea); } + if t.has_active_reservers() { + let first_line = ea & !RESERVATION_MASK; + let last_line = ea.wrapping_add(nb - 1) & !RESERVATION_MASK; + t.invalidate_for_write(first_line); + if last_line != first_line { t.invalidate_for_write(last_line); } + } } while bytes_left > 0 { let val = ctx.gpr[rs] as u32; @@ -1600,6 +1605,9 @@ fn execute(ctx: &mut PpcContext, mem: &dyn MemoryAccess, instr: &DecodedInstr) - // Zero 32 bytes at effective address let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] }; let ea = (ea.wrapping_add(ctx.gpr[instr.rb()]) as u32) & !31; + if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) { + if t.has_active_reservers() { t.invalidate_for_write(ea); } + } for i in 0..8 { mem.write_u32(ea + i * 4, 0); } @@ -1609,6 +1617,9 @@ fn execute(ctx: &mut PpcContext, mem: &dyn MemoryAccess, instr: &DecodedInstr) - // Zero 128 bytes let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] }; let ea = (ea.wrapping_add(ctx.gpr[instr.rb()]) as u32) & !127; + if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) { + if t.has_active_reservers() { t.invalidate_for_write(ea); } + } for i in 0..32 { mem.write_u32(ea + i * 4, 0); } @@ -4418,8 +4429,15 @@ fn execute(ctx: &mut PpcContext, mem: &dyn MemoryAccess, instr: &DecodedInstr) - let nb = ctx.xer() & 0x7F; let mut rs = instr.rs(); let mut bytes_left = nb; - if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) { - if t.has_active_reservers() { t.invalidate_for_write(ea); } + if nb > 0 { + if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) { + if t.has_active_reservers() { + let first_line = ea & !RESERVATION_MASK; + let last_line = ea.wrapping_add(nb - 1) & !RESERVATION_MASK; + t.invalidate_for_write(first_line); + if last_line != first_line { t.invalidate_for_write(last_line); } + } + } } while bytes_left > 0 { let val = ctx.gpr[rs] as u32;