From 10aae81b32ef0d9017beb9fdaf659945a393ab72 Mon Sep 17 00:00:00 2001 From: Zhijin Zeng Date: Wed, 20 May 2026 17:43:15 +0800 Subject: [PATCH] [DYNAREC] Save temporary registers on the stack before calling PrintTrace The PrintTrace function may modify temporary registers, so we need to push them onto the stack before execution and restore them upon return. For example, in the RV64 implementation, register `t3` stores the comparison result. As its value may be overwritten by PrintTrace, the subsequent `jz` instruction will use invalid data directly. ``` [BOX64] 0x3f0000239b: 48 85 C0 test rax, rax [BOX64] 0x3ff7af34d4: 53 emitted opcodes, inst=2, barrier=0 state=3/1(0), set=3F/80, use=0, need=0/80, fuse=1/0, sm=0(0/0), sew@entry=7, sew@exit=7, pred=1 03f00e13 ADDI t3, zero, 0x3f(63) 45ccaa23 SW t3, emu_s9, 0x454(1108) 01087e33 AND t3, rax_a6, rax_a6 47ccb423 SD t3, emu_s9, 0x468(1128) [BOX64] New Instruction x64:0x3f0000239e, native:0x3ff7af35a8 [BOX64] TRACE ---- 01f80b37 LUI rip_s6, 0x1f80000(33030144) 001b0b1b ADDIW rip_s6, rip_s6, 0x1(1) 00db1b13 SLLI rip_s6, rip_s6, 0xd(13) 39eb0b13 ADDI rip_s6, rip_s6, 0x39e(926) 000b0313 MV t1, rip_s6 018cbc23 SD rbx_s8, emu_s9, 0x18(24) 029cb023 SD rsp_s1, emu_s9, 0x20(32) 028cb423 SD rbp_s0, emu_s9, 0x28(40) 05acb823 SD r10_s10, emu_s9, 0x50(80) 05bcbc23 SD r11_s11, emu_s9, 0x58(88) 072cb023 SD r12_s2, emu_s9, 0x60(96) 073cb423 SD r13_s3, emu_s9, 0x68(104) 074cb823 SD r14_s4, emu_s9, 0x70(112) 075cbc23 SD r15_s5, emu_s9, 0x78(120) 0010039b ADDIW t2, zero, 0x1(1) ffffffb7 LUI t6, 0xfffff000(-4096) 7dff8f9b ADDIW t6, t6, 0x7df(2015) 01fbffb3 AND t6, flags_s7, t6 020bfb93 ANDI flags_s7, flags_s7, 0x20(32) 006b9b93 SLLI flags_s7, flags_s7, 0x6(6) 01fbebb3 OR flags_s7, flags_s7, t6 097cb023 SD flags_s7, emu_s9, 0x80(128) ff010113 ADDI sp, sp, 0xfffffff0(-16) 00513023 SD t0, sp, 0x0(0) 02acbc23 SD rdi_a0, emu_s9, 0x38(56) 02bcb823 SD rsi_a1, emu_s9, 0x30(48) 00ccb823 SD rdx_a2, emu_s9, 0x10(16) 00dcb423 SD rcx_a3, emu_s9, 0x8(8) 04ecb023 SD r8_a4, emu_s9, 0x40(64) 04fcb423 SD r9_a5, emu_s9, 0x48(72) 010cb023 SD rax_a6, emu_s9, 0x0(0) 096cb423 SD rip_s6, emu_s9, 0x88(136) [BOX64] Table64: 0x5d 00000f97 AUIPC t6, 0x0(0) 3a8fbf83 LD t6, t6, 0x3a8(936) 00030593 MV rsi_a1, t1 00038613 MV rdx_a2, t2 000c8513 MV rdi_a0, emu_s9 000f80e7 JALR ra, t6, 0x0(0) 00013283 LD t0, sp, 0x0(0) 01010113 ADDI sp, sp, 0x10(16) 038cb503 LD rdi_a0, emu_s9, 0x38(56) 030cb583 LD rsi_a1, emu_s9, 0x30(48) 010cb603 LD rdx_a2, emu_s9, 0x10(16) 008cb683 LD rcx_a3, emu_s9, 0x8(8) 040cb703 LD r8_a4, emu_s9, 0x40(64) 048cb783 LD r9_a5, emu_s9, 0x48(72) 000cb803 LD rax_a6, emu_s9, 0x0(0) 088cbb03 LD rip_s6, emu_s9, 0x88(136) 080cbb83 LD flags_s7, emu_s9, 0x80(128) fdfbfb93 ANDI flags_s7, flags_s7, 0xffffffdf(-33) 006bdf93 SRLI t6, flags_s7, 0x6(6) 020fff93 ANDI t6, t6, 0x20(32) 01fbebb3 OR flags_s7, flags_s7, t6 [BOX64] ---------- [BOX64] 0x3f0000239e: 74 02 jz 0x0000003F000023A2 [BOX64] 0x3ff7af35a8: 55 emitted opcodes, inst=3, barrier=0 state=0/3(0), set=0/0, use=0, need=80/80, fuse=1/0, sm=0(0/0), sew@entry=7, sew@exit=7, pred=2, jmp=5 140e0463 BEQ t3, zero, 0x148(328) # +82i(0x3ff7af37c4) 00000013 NOP ``` --- src/dynarec/arm64/dynarec_arm64_private.h | 4 +++ src/dynarec/dynarec_native_pass.c | 2 ++ src/dynarec/la64/dynarec_la64_private.h | 4 +++ src/dynarec/ppc64le/dynarec_ppc64le_private.h | 4 +++ src/dynarec/rv64/dynarec_rv64_private.h | 26 +++++++++++++++++++ src/dynarec/rv64/rv64_mapping.h | 1 + 6 files changed, 41 insertions(+) diff --git a/src/dynarec/arm64/dynarec_arm64_private.h b/src/dynarec/arm64/dynarec_arm64_private.h index c00ce4bba4..477ab5daf4 100644 --- a/src/dynarec/arm64/dynarec_arm64_private.h +++ b/src/dynarec/arm64/dynarec_arm64_private.h @@ -225,6 +225,10 @@ int Table64(dynarec_arm_t *dyn, uint64_t val, int pass); // add a value to tabl void CreateJmpNext(void* addr, void* next); +// TODO: Save and restore the temp register. +#define SAVE_ACTIVE_SCRATCH_REGISTERS do{} while(0); +#define LOAD_ACTIVE_SCRATCH_REGISTERS do{} while(0); + #define GO_TRACE(A, B, s0) \ GETIP(addr); \ MOVx_REG(x1, xRIP); \ diff --git a/src/dynarec/dynarec_native_pass.c b/src/dynarec/dynarec_native_pass.c index e13a316f22..75798ae6c8 100644 --- a/src/dynarec/dynarec_native_pass.c +++ b/src/dynarec/dynarec_native_pass.c @@ -257,9 +257,11 @@ uintptr_t native_pass(dynarec_native_t* dyn, uintptr_t addr, int alternate, int if((trace_end == 0) || ((ip >= trace_start) && (ip < trace_end))) { MESSAGE(LOG_DUMP, "TRACE ----\n"); + if (BOX64ENV(dynarec_nativeflags)) SAVE_ACTIVE_SCRATCH_REGISTERS; fpu_reflectcache(dyn, ninst, x1, x2, x3); GO_TRACE(PrintTrace, 1, x5); fpu_unreflectcache(dyn, ninst, x1, x2, x3); + if (BOX64ENV(dynarec_nativeflags)) LOAD_ACTIVE_SCRATCH_REGISTERS; MESSAGE(LOG_DUMP, "----------\n"); } } diff --git a/src/dynarec/la64/dynarec_la64_private.h b/src/dynarec/la64/dynarec_la64_private.h index 9fa89c240d..e70be9c261 100644 --- a/src/dynarec/la64/dynarec_la64_private.h +++ b/src/dynarec/la64/dynarec_la64_private.h @@ -200,6 +200,10 @@ int Table64(dynarec_la64_t *dyn, uint64_t val, int pass); // add a value to tab void CreateJmpNext(void* addr, void* next); +// TODO: Save and restore the temp register. +#define SAVE_ACTIVE_SCRATCH_REGISTERS do{} while(0); +#define LOAD_ACTIVE_SCRATCH_REGISTERS do{} while(0); + #define GO_TRACE(A, B, s0) \ GETIP(addr, s0); \ MV(x1, xRIP); \ diff --git a/src/dynarec/ppc64le/dynarec_ppc64le_private.h b/src/dynarec/ppc64le/dynarec_ppc64le_private.h index 9d7db48f79..e3a1ab85a2 100644 --- a/src/dynarec/ppc64le/dynarec_ppc64le_private.h +++ b/src/dynarec/ppc64le/dynarec_ppc64le_private.h @@ -218,6 +218,10 @@ int Table64(dynarec_ppc64le_t *dyn, uint64_t val, int pass); // add a value to void CreateJmpNext(void* addr, void* next); +// TODO: Save and restore the temp register. +#define SAVE_ACTIVE_SCRATCH_REGISTERS do{} while(0); +#define LOAD_ACTIVE_SCRATCH_REGISTERS do{} while(0); + #define GO_TRACE(A, B, s0) \ GETIP(addr, s0); \ MV(x1, xRIP); \ diff --git a/src/dynarec/rv64/dynarec_rv64_private.h b/src/dynarec/rv64/dynarec_rv64_private.h index 806a6b9e13..416832a2f7 100644 --- a/src/dynarec/rv64/dynarec_rv64_private.h +++ b/src/dynarec/rv64/dynarec_rv64_private.h @@ -216,6 +216,32 @@ int Table64(dynarec_rv64_t *dyn, uint64_t val, int pass); // add a value to tab void CreateJmpNext(void* addr, void* next); +#define SAVE_ACTIVE_SCRATCH_REGISTERS \ + do { \ + uint8_t n1 = dyn->insts[ninst].nat_flags_op1; \ + uint8_t n2 = dyn->insts[ninst].nat_flags_op2; \ + if (IS_SCRATCH(n1) || IS_SCRATCH(n2)) { \ + SUBI(xSP, xSP, 16); \ + if (IS_SCRATCH(n1)) \ + SD(n1, xSP, 0); \ + if (n1 != n2 && IS_SCRATCH(n2)) \ + SD(n2, xSP, 8); \ + } \ + } while(0); + +#define LOAD_ACTIVE_SCRATCH_REGISTERS \ + do { \ + uint8_t n1 = dyn->insts[ninst].nat_flags_op1; \ + uint8_t n2 = dyn->insts[ninst].nat_flags_op2; \ + if (IS_SCRATCH(n1) || IS_SCRATCH(n2)) { \ + if (IS_SCRATCH(n1)) \ + LD(n1, xSP, 0); \ + if (n1 != n2 && IS_SCRATCH(n2)) \ + LD(n2, xSP, 8); \ + ADDI(xSP, xSP, 16); \ + } \ + } while(0); + #define GO_TRACE(A, B, s0) \ GETIP(addr, s0); \ MV(x1, xRIP); \ diff --git a/src/dynarec/rv64/rv64_mapping.h b/src/dynarec/rv64/rv64_mapping.h index 675d8ef2aa..45a5773fcf 100644 --- a/src/dynarec/rv64/rv64_mapping.h +++ b/src/dynarec/rv64/rv64_mapping.h @@ -69,6 +69,7 @@ x31 t6 x6 Temporary Scratch // convert a x86 register to native according to the register mapping #define TO_NAT(A) (((uint8_t[]) { 16, 13, 12, 24, 9, 8, 11, 10, 14, 15, 26, 27, 18, 19, 20, 21 })[(A)]) #define IS_GPR(A) (((uint8_t[]) { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0 })[(A)]) +#define IS_SCRATCH(A) (((uint8_t[]) { 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 })[(A)]) #define x1 6 #define x2 7