From 5e310ff119d0308f5c467b814a540ec99a872e8b Mon Sep 17 00:00:00 2001 From: Yang Liu Date: Thu, 29 Oct 2026 17:02:19 +0800 Subject: [PATCH 1/2] [ARM64_DYNAREC] Don't skip dirty preloaded regs on loopback --- src/dynarec/arm64/dynarec_arm64_helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dynarec/arm64/dynarec_arm64_helper.c b/src/dynarec/arm64/dynarec_arm64_helper.c index ac4f39b8c9..b760db843b 100644 --- a/src/dynarec/arm64/dynarec_arm64_helper.c +++ b/src/dynarec/arm64/dynarec_arm64_helper.c @@ -2128,6 +2128,7 @@ static void fpuCacheTransform(dynarec_arm_t* dyn, int ninst, int s1, int s2, int MESSAGE(LOG_DUMP, "\tFPU Cache Transform ---- ninst=%d -> %d\n", ninst, i2); uint32_t unneeded = dyn->insts[i2].n.xmm_unneeded | (dyn->insts[i2].n.ymm_unneeded<<16); if((!i2) || (dyn->insts[i2].x64.barrier&BARRIER_FLOAT)) { + unneeded &= ~dyn->insts[i2].preload_xmmymm; int need_purge = 0; if(dyn->n.stack_next) need_purge = 1; From bfda40d60edd01cce8a1e8da0b0fcb6a5961d6c1 Mon Sep 17 00:00:00 2001 From: Yang Liu Date: Fri, 30 Oct 2026 14:53:25 +0800 Subject: [PATCH 2/2] [ARM64_DYNAREC] Added host_call and refined SSE preload handling --- src/dynarec/arm64/dynarec_arm64_functions.c | 7 ++++++- src/dynarec/arm64/dynarec_arm64_helper.c | 7 ++++++- src/dynarec/arm64/dynarec_arm64_pass0.h | 2 ++ src/dynarec/arm64/dynarec_arm64_private.h | 3 +++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/dynarec/arm64/dynarec_arm64_functions.c b/src/dynarec/arm64/dynarec_arm64_functions.c index c1dbe812f5..54b07a96fe 100644 --- a/src/dynarec/arm64/dynarec_arm64_functions.c +++ b/src/dynarec/arm64/dynarec_arm64_functions.c @@ -1573,12 +1573,17 @@ void addSSEPreload(dynarec_arm_t* dyn, int ninst, int i2, uint32_t preload) if(i2 && !dyn->insts[i2-1].x64.has_next) // need a previous opcode to set the preload, unless it's the #0 return; // check if there is jump out or outside this loop + int has_host_call = 0; for(int i=i2; iinsts[i].x64.has_next || (dyn->insts[i].x64.barrier&BARRIER_FLOAT)) return; // for now, unneeded stop at first jump, so this should also stop there if(dyn->insts[i].x64.jmp/* && ((dyn->insts[i].x64.jmp_insts>ninst) || (dyn->insts[i].x64.jmp_instsinsts[i].host_call_clobber) + return; + if(dyn->insts[i].host_call) + has_host_call = 1; } uint16_t ymm_mask = 0; for(int i=0; i<16; ++i) { @@ -1617,9 +1622,9 @@ void addSSEPreload(dynarec_arm_t* dyn, int ninst, int i2, uint32_t preload) } } preload = (preload&0xffff) | (ymm_mask<<16); // update ymm_mask - //TODO: should check native calls too? dyn->insts[i2].preload_xmmymm = preload; dyn->insts[i2].preload_from = ninst; + dyn->insts[i2].preload_host_call = has_host_call; dyn->insts[i2].n.xmm_unneeded |= preload&0xffff; dyn->insts[i2].n.ymm_unneeded |= ymm_mask; } diff --git a/src/dynarec/arm64/dynarec_arm64_helper.c b/src/dynarec/arm64/dynarec_arm64_helper.c index b760db843b..99aca34f2f 100644 --- a/src/dynarec/arm64/dynarec_arm64_helper.c +++ b/src/dynarec/arm64/dynarec_arm64_helper.c @@ -548,6 +548,7 @@ void iret_to_next(dynarec_arm_t* dyn, uintptr_t ip, int ninst, int is32bits, int void call_c(dynarec_arm_t* dyn, int ninst, arm64_consts_t fnc, int reg, int ret, int saveflags, int savereg) { MAYUSE(fnc); + dyn->insts[ninst].host_call = 1; CHECK_DFNONE(1); #if STEP == 0 dyn->insts[ninst].nat_flags_op = NAT_FLAG_OP_UNUSABLE; @@ -607,6 +608,7 @@ void call_c(dynarec_arm_t* dyn, int ninst, arm64_consts_t fnc, int reg, int ret, void call_d(dynarec_arm_t* dyn, int ninst, arm64_consts_t fnc, int ret, int arg1, int arg2, int sav1, int sav2) { MAYUSE(fnc); + dyn->insts[ninst].host_call = 1; CHECK_DFNONE(1); #if STEP == 0 dyn->insts[ninst].nat_flags_op = NAT_FLAG_OP_UNUSABLE; @@ -653,6 +655,8 @@ void call_d(dynarec_arm_t* dyn, int ninst, arm64_consts_t fnc, int ret, int arg1 void call_n(dynarec_arm_t* dyn, int ninst, void* fnc, int w) { MAYUSE(fnc); + dyn->insts[ninst].host_call = 1; + dyn->insts[ninst].host_call_clobber = 1; CHECK_DFNONE(1); #if STEP == 0 dyn->insts[ninst].nat_flags_op = NAT_FLAG_OP_UNUSABLE; @@ -2128,7 +2132,8 @@ static void fpuCacheTransform(dynarec_arm_t* dyn, int ninst, int s1, int s2, int MESSAGE(LOG_DUMP, "\tFPU Cache Transform ---- ninst=%d -> %d\n", ninst, i2); uint32_t unneeded = dyn->insts[i2].n.xmm_unneeded | (dyn->insts[i2].n.ymm_unneeded<<16); if((!i2) || (dyn->insts[i2].x64.barrier&BARRIER_FLOAT)) { - unneeded &= ~dyn->insts[i2].preload_xmmymm; + if(dyn->insts[i2].preload_host_call) + unneeded &= ~dyn->insts[i2].preload_xmmymm; int need_purge = 0; if(dyn->n.stack_next) need_purge = 1; diff --git a/src/dynarec/arm64/dynarec_arm64_pass0.h b/src/dynarec/arm64/dynarec_arm64_pass0.h index 812a9aae5d..2f194e72ab 100644 --- a/src/dynarec/arm64/dynarec_arm64_pass0.h +++ b/src/dynarec/arm64/dynarec_arm64_pass0.h @@ -38,6 +38,8 @@ dyn->n.combined1 = dyn->n.combined2 = 0;\ dyn->n.swapped = 0; dyn->n.barrier = 0; \ dyn->insts[ninst].f_entry = dyn->f; \ + dyn->insts[ninst].host_call = 0; \ + dyn->insts[ninst].host_call_clobber = 0;\ dyn->insts[ninst].x64.strongmem = ((box64_wine && VolatileRangesContains(ip)) ? 0 : BOX64DRENV(dynarec_strongmem)); \ if(dyn->insts[ninst].x64.strongmem && cpuext.lrcpc && cpuext.lrcpc2 && cpuext.uscat) dyn->insts[ninst].use_lrcpc = 1; \ if(ninst) {dyn->insts[ninst-1].x64.size = dyn->insts[ninst].x64.addr - dyn->insts[ninst-1].x64.addr;} \ diff --git a/src/dynarec/arm64/dynarec_arm64_private.h b/src/dynarec/arm64/dynarec_arm64_private.h index 040e96eb81..3df190d9a7 100644 --- a/src/dynarec/arm64/dynarec_arm64_private.h +++ b/src/dynarec/arm64/dynarec_arm64_private.h @@ -156,6 +156,9 @@ typedef struct instruction_arm64_s { unsigned x87_used:1; // no fine tracking, just a global "any reg used" unsigned fpu_used:1; // any xmm/ymm/x87/mmx reg used unsigned fpupurge:1; // this opcode will purge all fpu regs + unsigned host_call:1; // this opcode performs a native call + unsigned host_call_clobber:1; // native call does not preserve cached xmm0-7 (call_n) + unsigned preload_host_call:1; // preload range contains a host call, needs write-back at merge unsigned use_lrcpc:1;// use the various lrcpc opcode here instead of regular LD/ST + memory barrier unsigned has_lrcpc:1;// set if the opcode has some lrcpc opcode in it flagcache_t f_exit; // flags status at end of instruction