diff --git a/crates/xenia-cpu/src/jit.rs b/crates/xenia-cpu/src/jit.rs index 1725478..767d2e1 100644 --- a/crates/xenia-cpu/src/jit.rs +++ b/crates/xenia-cpu/src/jit.rs @@ -197,10 +197,73 @@ struct Trampolines { /// Per-compile emit context: the two entry-block pointer params plus the /// trampoline `FuncRef`s. `ctxp` is `*mut PpcContext`, `memenv` is `*const /// MemEnv`, both as IR `Value`s. +/// +/// `regs` is a per-block **GPR register cache**: instead of re-loading a guest +/// GPR from `PpcContext` memory on every use and storing it back after every +/// write, a GPR is loaded once (on first read within a block) into a Cranelift +/// SSA value that the register allocator keeps in a host register, and dirty +/// GPRs are written back to memory only at the block boundary ([`cache_flush`]). +/// This removes the redundant per-instruction memory traffic that kept the JIT +/// bodies merely at interpreter parity. It is sound because nothing a block +/// calls back into (the memory trampolines take addr/val as args; the FP-punt +/// runs interpreter FP ops that touch fpr/fpscr/cr only) reads or writes guest +/// GPRs — so the cache never goes stale mid-block. struct EmitCtx { ctxp: Value, memenv: Value, tr: Trampolines, + regs: std::cell::RefCell, +} + +/// Per-block GPR register cache (see [`EmitCtx::regs`]). Reset at each block +/// boundary; `gpr[r]` is the current SSA value of guest r`r` (I64), `dirty[r]` +/// marks it as written-but-not-yet-flushed to `PpcContext` memory. +struct RegCache { + gpr: [Option; 32], + dirty: [bool; 32], +} + +impl RegCache { + fn new() -> Self { + Self { gpr: [None; 32], dirty: [false; 32] } + } +} + +/// Read guest GPR `r` as an I64: the cached SSA value if present, else load it +/// from `PpcContext` memory once and cache it. +fn cache_read_gpr(b: &mut FunctionBuilder, ec: &EmitCtx, r: usize) -> Value { + // Copy the cached Option out and drop the borrow before any re-borrow below + // (RefCell would panic on an overlapping borrow_mut). + let cached = ec.regs.borrow().gpr[r]; + if let Some(v) = cached { + return v; + } + let v = load_gpr(b, ec.ctxp, r); + ec.regs.borrow_mut().gpr[r] = Some(v); + v +} + +/// Write guest GPR `r` (I64 SSA value) into the cache, marking it dirty. The +/// store to `PpcContext` memory is deferred to [`cache_flush`] at the block +/// boundary. +fn cache_write_gpr(ec: &EmitCtx, r: usize, v: Value) { + let mut c = ec.regs.borrow_mut(); + c.gpr[r] = Some(v); + c.dirty[r] = true; +} + +/// Flush all dirty cached GPRs back to `PpcContext` memory and clear the cache, +/// so the block boundary (and the next block, which re-loads) sees the same +/// `gpr[]` state the interpreter would. Called at the end of every block body. +fn cache_flush(b: &mut FunctionBuilder, ec: &EmitCtx) { + let mut c = ec.regs.borrow_mut(); + for r in 0..32 { + if c.dirty[r] { + let v = c.gpr[r].expect("dirty gpr has a value"); + store_gpr(b, ec.ctxp, r, v); + } + } + *c = RegCache::new(); } // ---- compiled-block cache ------------------------------------------------- @@ -508,6 +571,7 @@ impl Jit { ctxp: b.block_params(entry)[0], memenv: b.block_params(entry)[1], tr, + regs: std::cell::RefCell::new(RegCache::new()), }; let _ = emit_node_body(&mut b, &ec, block); let ret = b.ins().iconst(types::I32, RET_CONTINUE as i64); @@ -580,7 +644,7 @@ impl Jit { let ctxp = b.block_params(ib)[0]; let memenv = b.block_params(ib)[1]; let remaining = b.block_params(ib)[2]; // I64 remaining budget - let ec = EmitCtx { ctxp, memenv, tr }; + let ec = EmitCtx { ctxp, memenv, tr, regs: std::cell::RefCell::new(RegCache::new()) }; // Entry-sampled constants. The entry block dominates every node // (including across loop back-edges), so these are usable at all @@ -736,6 +800,9 @@ fn emit_node_body(b: &mut FunctionBuilder, ec: &EmitCtx, block: &DecodedBlock) - emit_op(b, ec, instr); } } + // Write back any GPRs cached in host registers during the block, so the + // boundary and the next block observe the interpreter-identical `gpr[]`. + cache_flush(b, ec); // A branch terminator writes `pc` itself (to its computed target); otherwise // the block is straight-line and `pc` advances to `end_pc`. `writes_pc` is // only ever true for the last instruction, so testing it is sufficient. @@ -995,19 +1062,19 @@ fn store_lr(b: &mut FunctionBuilder, ctxp: Value, val_u64: u64) { b.ins().store(MemFlags::trusted(), v, ctxp, off(offset_of!(PpcContext, lr))); } -/// Low 32 bits of GPR `r` as an `I32`. +/// Low 32 bits of GPR `r` as an `I32` (via the register cache). #[inline] -fn gpr32(b: &mut FunctionBuilder, ctxp: Value, r: usize) -> Value { - let v = load_gpr(b, ctxp, r); +fn gpr32(b: &mut FunctionBuilder, ec: &EmitCtx, r: usize) -> Value { + let v = cache_read_gpr(b, ec, r); b.ins().ireduce(types::I32, v) } /// Store an `I32` result into GPR `ra`, zero-extended to 64 bits — the width /// contract of the `*cx`/`nor`/`nand`/`rlwinm` ops (which zero the upper half). #[inline] -fn store_gpr32z(b: &mut FunctionBuilder, ctxp: Value, ra: usize, v32: Value) { +fn store_gpr32z(b: &mut FunctionBuilder, ec: &EmitCtx, ra: usize, v32: Value) { let z = b.ins().uextend(types::I64, v32); - store_gpr(b, ctxp, ra, z); + cache_write_gpr(ec, ra, z); } /// PPC `rlwnm`-family mask. Mirrors the interpreter's `rlw_mask`; `mb`/`me` are @@ -1144,7 +1211,7 @@ fn ea_d(b: &mut FunctionBuilder, ec: &EmitCtx, ra: usize, disp: i32) -> Value { let base = if ra == 0 { b.ins().iconst(types::I64, 0) } else { - load_gpr(b, ec.ctxp, ra) + cache_read_gpr(b, ec, ra) }; let ea64 = b.ins().iadd_imm(base, disp as i64); b.ins().ireduce(types::I32, ea64) @@ -1156,9 +1223,9 @@ fn ea_x(b: &mut FunctionBuilder, ec: &EmitCtx, ra: usize, rb: usize) -> Value { let base = if ra == 0 { b.ins().iconst(types::I64, 0) } else { - load_gpr(b, ec.ctxp, ra) + cache_read_gpr(b, ec, ra) }; - let rbv = load_gpr(b, ec.ctxp, rb); + let rbv = cache_read_gpr(b, ec, rb); let ea64 = b.ins().iadd(base, rbv); b.ins().ireduce(types::I32, ea64) } @@ -1305,10 +1372,10 @@ fn emit_op(b: &mut FunctionBuilder, ec: &EmitCtx, instr: &DecodedInstr) { let rav = if ra == 0 { b.ins().iconst(types::I64, 0) } else { - load_gpr(b, ctxp, ra) + cache_read_gpr(b, ec, ra) }; let res = b.ins().iadd_imm(rav, instr.simm16() as i64); - store_gpr(b, ctxp, instr.rd(), res); + cache_write_gpr(ec, instr.rd(), res); } // rd = (ra==0 ? 0 : gpr[ra]) + (EXTS(simm) << 16) [64-bit] PpcOpcode::addis => { @@ -1316,18 +1383,18 @@ fn emit_op(b: &mut FunctionBuilder, ec: &EmitCtx, instr: &DecodedInstr) { let rav = if ra == 0 { b.ins().iconst(types::I64, 0) } else { - load_gpr(b, ctxp, ra) + cache_read_gpr(b, ec, ra) }; let res = b.ins().iadd_imm(rav, (instr.simm16() as i64) << 16); - store_gpr(b, ctxp, instr.rd(), res); + cache_write_gpr(ec, instr.rd(), res); } // ---- reg-reg add/sub (OE=0). Full 64-bit result; CR0 on low 32. ---- PpcOpcode::addx => { - let ra = load_gpr(b, ctxp, instr.ra()); - let rb = load_gpr(b, ctxp, instr.rb()); + let ra = cache_read_gpr(b, ec, instr.ra()); + let rb = cache_read_gpr(b, ec, instr.rb()); let res = b.ins().iadd(ra, rb); - store_gpr(b, ctxp, instr.rd(), res); + cache_write_gpr(ec, instr.rd(), res); if instr.rc_bit() { let lo = b.ins().ireduce(types::I32, res); emit_cr0_signed32(b, ctxp, lo); @@ -1335,10 +1402,10 @@ fn emit_op(b: &mut FunctionBuilder, ec: &EmitCtx, instr: &DecodedInstr) { } PpcOpcode::subfx => { // rd = rb - ra - let ra = load_gpr(b, ctxp, instr.ra()); - let rb = load_gpr(b, ctxp, instr.rb()); + let ra = cache_read_gpr(b, ec, instr.ra()); + let rb = cache_read_gpr(b, ec, instr.rb()); let res = b.ins().isub(rb, ra); - store_gpr(b, ctxp, instr.rd(), res); + cache_write_gpr(ec, instr.rd(), res); if instr.rc_bit() { let lo = b.ins().ireduce(types::I32, res); emit_cr0_signed32(b, ctxp, lo); @@ -1347,30 +1414,30 @@ fn emit_op(b: &mut FunctionBuilder, ec: &EmitCtx, instr: &DecodedInstr) { // ---- reg-reg logical preserving all 64 bits; CR0 on low 32 if Rc. ---- PpcOpcode::orx => { - let rs = load_gpr(b, ctxp, instr.rs()); - let rb = load_gpr(b, ctxp, instr.rb()); + let rs = cache_read_gpr(b, ec, instr.rs()); + let rb = cache_read_gpr(b, ec, instr.rb()); let res = b.ins().bor(rs, rb); - store_gpr(b, ctxp, instr.ra(), res); + cache_write_gpr(ec, instr.ra(), res); if instr.rc_bit() { let lo = b.ins().ireduce(types::I32, res); emit_cr0_signed32(b, ctxp, lo); } } PpcOpcode::andx => { - let rs = load_gpr(b, ctxp, instr.rs()); - let rb = load_gpr(b, ctxp, instr.rb()); + let rs = cache_read_gpr(b, ec, instr.rs()); + let rb = cache_read_gpr(b, ec, instr.rb()); let res = b.ins().band(rs, rb); - store_gpr(b, ctxp, instr.ra(), res); + cache_write_gpr(ec, instr.ra(), res); if instr.rc_bit() { let lo = b.ins().ireduce(types::I32, res); emit_cr0_signed32(b, ctxp, lo); } } PpcOpcode::xorx => { - let rs = load_gpr(b, ctxp, instr.rs()); - let rb = load_gpr(b, ctxp, instr.rb()); + let rs = cache_read_gpr(b, ec, instr.rs()); + let rb = cache_read_gpr(b, ec, instr.rb()); let res = b.ins().bxor(rs, rb); - store_gpr(b, ctxp, instr.ra(), res); + cache_write_gpr(ec, instr.ra(), res); if instr.rc_bit() { let lo = b.ins().ireduce(types::I32, res); emit_cr0_signed32(b, ctxp, lo); @@ -1379,41 +1446,41 @@ fn emit_op(b: &mut FunctionBuilder, ec: &EmitCtx, instr: &DecodedInstr) { // ---- reg-reg logical operating in u32 (zeroes upper 32); CR0 low 32. ---- PpcOpcode::norx => { - let rs = gpr32(b, ctxp, instr.rs()); - let rb = gpr32(b, ctxp, instr.rb()); + let rs = gpr32(b, ec, instr.rs()); + let rb = gpr32(b, ec, instr.rb()); let t = b.ins().bor(rs, rb); let res = b.ins().bnot(t); - store_gpr32z(b, ctxp, instr.ra(), res); + store_gpr32z(b, ec, instr.ra(), res); if instr.rc_bit() { emit_cr0_signed32(b, ctxp, res); } } PpcOpcode::nandx => { - let rs = gpr32(b, ctxp, instr.rs()); - let rb = gpr32(b, ctxp, instr.rb()); + let rs = gpr32(b, ec, instr.rs()); + let rb = gpr32(b, ec, instr.rb()); let t = b.ins().band(rs, rb); let res = b.ins().bnot(t); - store_gpr32z(b, ctxp, instr.ra(), res); + store_gpr32z(b, ec, instr.ra(), res); if instr.rc_bit() { emit_cr0_signed32(b, ctxp, res); } } PpcOpcode::andcx => { - let rs = gpr32(b, ctxp, instr.rs()); - let rb = gpr32(b, ctxp, instr.rb()); + let rs = gpr32(b, ec, instr.rs()); + let rb = gpr32(b, ec, instr.rb()); let nrb = b.ins().bnot(rb); let res = b.ins().band(rs, nrb); - store_gpr32z(b, ctxp, instr.ra(), res); + store_gpr32z(b, ec, instr.ra(), res); if instr.rc_bit() { emit_cr0_signed32(b, ctxp, res); } } PpcOpcode::orcx => { - let rs = gpr32(b, ctxp, instr.rs()); - let rb = gpr32(b, ctxp, instr.rb()); + let rs = gpr32(b, ec, instr.rs()); + let rb = gpr32(b, ec, instr.rb()); let nrb = b.ins().bnot(rb); let res = b.ins().bor(rs, nrb); - store_gpr32z(b, ctxp, instr.ra(), res); + store_gpr32z(b, ec, instr.ra(), res); if instr.rc_bit() { emit_cr0_signed32(b, ctxp, res); } @@ -1421,49 +1488,49 @@ fn emit_op(b: &mut FunctionBuilder, ec: &EmitCtx, instr: &DecodedInstr) { // ---- immediate logical. 64-bit result. ori/oris/xori/xoris: no CR. ---- PpcOpcode::ori => { - let rs = load_gpr(b, ctxp, instr.rs()); + let rs = cache_read_gpr(b, ec, instr.rs()); let res = b.ins().bor_imm(rs, instr.uimm16() as i64); - store_gpr(b, ctxp, instr.ra(), res); + cache_write_gpr(ec, instr.ra(), res); } PpcOpcode::oris => { - let rs = load_gpr(b, ctxp, instr.rs()); + let rs = cache_read_gpr(b, ec, instr.rs()); let res = b.ins().bor_imm(rs, (instr.uimm16() as i64) << 16); - store_gpr(b, ctxp, instr.ra(), res); + cache_write_gpr(ec, instr.ra(), res); } PpcOpcode::xori => { - let rs = load_gpr(b, ctxp, instr.rs()); + let rs = cache_read_gpr(b, ec, instr.rs()); let res = b.ins().bxor_imm(rs, instr.uimm16() as i64); - store_gpr(b, ctxp, instr.ra(), res); + cache_write_gpr(ec, instr.ra(), res); } PpcOpcode::xoris => { - let rs = load_gpr(b, ctxp, instr.rs()); + let rs = cache_read_gpr(b, ec, instr.rs()); let res = b.ins().bxor_imm(rs, (instr.uimm16() as i64) << 16); - store_gpr(b, ctxp, instr.ra(), res); + cache_write_gpr(ec, instr.ra(), res); } // andi./andis. always update CR0 (the `.` forms). PpcOpcode::andix => { - let rs = load_gpr(b, ctxp, instr.rs()); + let rs = cache_read_gpr(b, ec, instr.rs()); let res = b.ins().band_imm(rs, instr.uimm16() as i64); - store_gpr(b, ctxp, instr.ra(), res); + cache_write_gpr(ec, instr.ra(), res); let lo = b.ins().ireduce(types::I32, res); emit_cr0_signed32(b, ctxp, lo); } PpcOpcode::andisx => { - let rs = load_gpr(b, ctxp, instr.rs()); + let rs = cache_read_gpr(b, ec, instr.rs()); let res = b.ins().band_imm(rs, (instr.uimm16() as i64) << 16); - store_gpr(b, ctxp, instr.ra(), res); + cache_write_gpr(ec, instr.ra(), res); let lo = b.ins().ireduce(types::I32, res); emit_cr0_signed32(b, ctxp, lo); } // ---- rotate-left word immediate then AND mask; zeroes upper 32. ---- PpcOpcode::rlwinmx => { - let rs = gpr32(b, ctxp, instr.rs()); + let rs = gpr32(b, ec, instr.rs()); let shv = b.ins().iconst(types::I32, instr.sh() as i64); let rot = b.ins().rotl(rs, shv); let mask = rlw_mask(instr.mb(), instr.me()); let res = b.ins().band_imm(rot, mask as i64); - store_gpr32z(b, ctxp, instr.ra(), res); + store_gpr32z(b, ec, instr.ra(), res); if instr.rc_bit() { emit_cr0_signed32(b, ctxp, res); } @@ -1472,16 +1539,16 @@ fn emit_op(b: &mut FunctionBuilder, ec: &EmitCtx, instr: &DecodedInstr) { // ---- compares. Write CR field crfd() unconditionally. ---- PpcOpcode::cmp => { let (lt, gt, eq) = if instr.l() { - let ra = load_gpr(b, ctxp, instr.ra()); - let rb = load_gpr(b, ctxp, instr.rb()); + let ra = cache_read_gpr(b, ec, instr.ra()); + let rb = cache_read_gpr(b, ec, instr.rb()); ( b.ins().icmp(IntCC::SignedLessThan, ra, rb), b.ins().icmp(IntCC::SignedGreaterThan, ra, rb), b.ins().icmp(IntCC::Equal, ra, rb), ) } else { - let ra = gpr32(b, ctxp, instr.ra()); - let rb = gpr32(b, ctxp, instr.rb()); + let ra = gpr32(b, ec, instr.ra()); + let rb = gpr32(b, ec, instr.rb()); ( b.ins().icmp(IntCC::SignedLessThan, ra, rb), b.ins().icmp(IntCC::SignedGreaterThan, ra, rb), @@ -1492,16 +1559,16 @@ fn emit_op(b: &mut FunctionBuilder, ec: &EmitCtx, instr: &DecodedInstr) { } PpcOpcode::cmpl => { let (lt, gt, eq) = if instr.l() { - let ra = load_gpr(b, ctxp, instr.ra()); - let rb = load_gpr(b, ctxp, instr.rb()); + let ra = cache_read_gpr(b, ec, instr.ra()); + let rb = cache_read_gpr(b, ec, instr.rb()); ( b.ins().icmp(IntCC::UnsignedLessThan, ra, rb), b.ins().icmp(IntCC::UnsignedGreaterThan, ra, rb), b.ins().icmp(IntCC::Equal, ra, rb), ) } else { - let ra = gpr32(b, ctxp, instr.ra()); - let rb = gpr32(b, ctxp, instr.rb()); + let ra = gpr32(b, ec, instr.ra()); + let rb = gpr32(b, ec, instr.rb()); ( b.ins().icmp(IntCC::UnsignedLessThan, ra, rb), b.ins().icmp(IntCC::UnsignedGreaterThan, ra, rb), @@ -1513,14 +1580,14 @@ fn emit_op(b: &mut FunctionBuilder, ec: &EmitCtx, instr: &DecodedInstr) { PpcOpcode::cmpi => { let imm = instr.simm16() as i64; // sign-extended let (lt, gt, eq) = if instr.l() { - let ra = load_gpr(b, ctxp, instr.ra()); + let ra = cache_read_gpr(b, ec, instr.ra()); ( b.ins().icmp_imm(IntCC::SignedLessThan, ra, imm), b.ins().icmp_imm(IntCC::SignedGreaterThan, ra, imm), b.ins().icmp_imm(IntCC::Equal, ra, imm), ) } else { - let ra = gpr32(b, ctxp, instr.ra()); + let ra = gpr32(b, ec, instr.ra()); // 32-bit signed compare against sign-extended SIMM. let imm32 = instr.simm16() as i32 as i64; ( @@ -1534,14 +1601,14 @@ fn emit_op(b: &mut FunctionBuilder, ec: &EmitCtx, instr: &DecodedInstr) { PpcOpcode::cmpli => { let imm = instr.uimm16() as i64; // zero-extended let (lt, gt, eq) = if instr.l() { - let ra = load_gpr(b, ctxp, instr.ra()); + let ra = cache_read_gpr(b, ec, instr.ra()); ( b.ins().icmp_imm(IntCC::UnsignedLessThan, ra, imm), b.ins().icmp_imm(IntCC::UnsignedGreaterThan, ra, imm), b.ins().icmp_imm(IntCC::Equal, ra, imm), ) } else { - let ra = gpr32(b, ctxp, instr.ra()); + let ra = gpr32(b, ec, instr.ra()); ( b.ins().icmp_imm(IntCC::UnsignedLessThan, ra, imm), b.ins().icmp_imm(IntCC::UnsignedGreaterThan, ra, imm), @@ -1555,83 +1622,83 @@ fn emit_op(b: &mut FunctionBuilder, ec: &EmitCtx, instr: &DecodedInstr) { PpcOpcode::lbz => { let ea = ea_d(b, ec, instr.ra(), instr.d()); let v = emit_inline_load(b, ec, ea, 1, ec.tr.read8); - store_gpr(b, ctxp, instr.rd(), v); + cache_write_gpr(ec, instr.rd(), v); } PpcOpcode::lbzx => { let ea = ea_x(b, ec, instr.ra(), instr.rb()); let v = emit_inline_load(b, ec, ea, 1, ec.tr.read8); - store_gpr(b, ctxp, instr.rd(), v); + cache_write_gpr(ec, instr.rd(), v); } PpcOpcode::lhz => { let ea = ea_d(b, ec, instr.ra(), instr.d()); let v = emit_inline_load(b, ec, ea, 2, ec.tr.read16); - store_gpr(b, ctxp, instr.rd(), v); + cache_write_gpr(ec, instr.rd(), v); } PpcOpcode::lhzx => { let ea = ea_x(b, ec, instr.ra(), instr.rb()); let v = emit_inline_load(b, ec, ea, 2, ec.tr.read16); - store_gpr(b, ctxp, instr.rd(), v); + cache_write_gpr(ec, instr.rd(), v); } PpcOpcode::lwz => { let ea = ea_d(b, ec, instr.ra(), instr.d()); let v = emit_inline_load(b, ec, ea, 4, ec.tr.read32); - store_gpr(b, ctxp, instr.rd(), v); + cache_write_gpr(ec, instr.rd(), v); } PpcOpcode::lwzx => { let ea = ea_x(b, ec, instr.ra(), instr.rb()); let v = emit_inline_load(b, ec, ea, 4, ec.tr.read32); - store_gpr(b, ctxp, instr.rd(), v); + cache_write_gpr(ec, instr.rd(), v); } PpcOpcode::ld => { let ea = ea_d(b, ec, instr.ra(), instr.ds()); let v = emit_inline_load(b, ec, ea, 8, ec.tr.read64); - store_gpr(b, ctxp, instr.rd(), v); + cache_write_gpr(ec, instr.rd(), v); } PpcOpcode::ldx => { let ea = ea_x(b, ec, instr.ra(), instr.rb()); let v = emit_inline_load(b, ec, ea, 8, ec.tr.read64); - store_gpr(b, ctxp, instr.rd(), v); + cache_write_gpr(ec, instr.rd(), v); } // ---- integer stores. Value is the low bits of gpr[rs]. ---- PpcOpcode::stb => { let ea = ea_d(b, ec, instr.ra(), instr.d()); - let v = gpr32(b, ctxp, instr.rs()); + let v = gpr32(b, ec, instr.rs()); call_store(b, ec, ec.tr.write8, ea, v); } PpcOpcode::stbx => { let ea = ea_x(b, ec, instr.ra(), instr.rb()); - let v = gpr32(b, ctxp, instr.rs()); + let v = gpr32(b, ec, instr.rs()); call_store(b, ec, ec.tr.write8, ea, v); } PpcOpcode::sth => { let ea = ea_d(b, ec, instr.ra(), instr.d()); - let v = gpr32(b, ctxp, instr.rs()); + let v = gpr32(b, ec, instr.rs()); call_store(b, ec, ec.tr.write16, ea, v); } PpcOpcode::sthx => { let ea = ea_x(b, ec, instr.ra(), instr.rb()); - let v = gpr32(b, ctxp, instr.rs()); + let v = gpr32(b, ec, instr.rs()); call_store(b, ec, ec.tr.write16, ea, v); } PpcOpcode::stw => { let ea = ea_d(b, ec, instr.ra(), instr.d()); - let v = gpr32(b, ctxp, instr.rs()); + let v = gpr32(b, ec, instr.rs()); call_store(b, ec, ec.tr.write32, ea, v); } PpcOpcode::stwx => { let ea = ea_x(b, ec, instr.ra(), instr.rb()); - let v = gpr32(b, ctxp, instr.rs()); + let v = gpr32(b, ec, instr.rs()); call_store(b, ec, ec.tr.write32, ea, v); } PpcOpcode::std => { let ea = ea_d(b, ec, instr.ra(), instr.ds()); - let v = load_gpr(b, ctxp, instr.rs()); + let v = cache_read_gpr(b, ec, instr.rs()); call_store(b, ec, ec.tr.write64, ea, v); } PpcOpcode::stdx => { let ea = ea_x(b, ec, instr.ra(), instr.rb()); - let v = load_gpr(b, ctxp, instr.rs()); + let v = cache_read_gpr(b, ec, instr.rs()); call_store(b, ec, ec.tr.write64, ea, v); }