diff --git a/src/dynarec/rv64/dynarec_rv64_helper.c b/src/dynarec/rv64/dynarec_rv64_helper.c index 80e80f6d0..4cd4524ea 100644 --- a/src/dynarec/rv64/dynarec_rv64_helper.c +++ b/src/dynarec/rv64/dynarec_rv64_helper.c @@ -657,10 +657,17 @@ void call_c(dynarec_rv64_t* dyn, int ninst, rv64_consts_t fnc, int reg, int ret, void call_n(dynarec_rv64_t* dyn, int ninst, void* fnc, int w) { MAYUSE(fnc); + int calln_restore = BOX64DRENV(dynarec_calln_restore); fpu_pushcache(dyn, ninst, x3, 1); // save RSP in case there are x86 callbacks... SD(xRSP, xEmu, offsetof(x64emu_t, regs[_SP])); SD(xRBP, xEmu, offsetof(x64emu_t, regs[_BP])); + if (calln_restore) { + // Save caller-saved x86 regs that alias RISC-V return value regs (a0/a1). + // Needed for ABI-violating code that depends on caller-saved regs surviving calls. + STORE_REG(RDI); // xRDI = a0 = RISC-V return value register + STORE_REG(RSI); // xRSI = a1 = RISC-V second return value register + } // check if additional sextw needed int sextw_mask = ((w > 0 ? w : -w) >> 4) & 0b111111; for (int i = 0; i < 6; i++) { @@ -682,7 +689,11 @@ void call_n(dynarec_rv64_t* dyn, int ninst, void* fnc, int w) MV(xRAX, A0); MV(xRDX, A1); } - // all done, restore all regs + if (calln_restore) { + // Restore xRDI/xRSI from emu state, since native call clobbered a0/a1 + LOAD_REG(RDI); + LOAD_REG(RSI); + } // reinitialize sew if (dyn->vector_sew != VECTOR_SEWNA) diff --git a/src/include/env.h b/src/include/env.h index ea0c14918..50684c4cb 100644 --- a/src/include/env.h +++ b/src/include/env.h @@ -52,6 +52,7 @@ extern char* ftrace_name; INTEGER(BOX64_DYNAREC_BIGBLOCK, dynarec_bigblock, 2, 0, 3, 1) \ BOOLEAN(BOX64_DYNAREC_BLEEDING_EDGE, dynarec_bleeding_edge, 1, 0) \ INTEGER(BOX64_DYNAREC_CALLRET, dynarec_callret, 0, 0, 2, 1) \ + BOOLEAN(BOX64_DYNAREC_CALLN_RESTORE, dynarec_calln_restore, 0, 1) \ INTEGER(BOX64_DYNAREC_SEP, dynarec_sep, 1, 0, 2, 1) \ BOOLEAN(BOX64_DYNAREC_DF, dynarec_df, 1, 1) \ INTEGER(BOX64_DYNAREC_DIRTY, dynarec_dirty, 0, 0, 2, 0) \