From 8aeca9851775dda8728844b1f96d3509c15e5e7d Mon Sep 17 00:00:00 2001 From: Yang Liu Date: Tue, 4 Aug 2026 01:04:45 +0800 Subject: [PATCH 1/2] [RV64_DYNAREC] Refactored deferred flags --- src/dynarec/dynarec_native_functions.c | 25 ------ src/dynarec/dynarec_native_pass.c | 27 +------ src/dynarec/rv64/dynarec_rv64_functions.c | 9 ++- src/dynarec/rv64/dynarec_rv64_helper.c | 57 ++++++++----- src/dynarec/rv64/dynarec_rv64_helper.h | 99 ++++++++++++----------- src/dynarec/rv64/dynarec_rv64_pass0.h | 43 +++++----- src/dynarec/rv64/dynarec_rv64_private.h | 8 +- 7 files changed, 126 insertions(+), 142 deletions(-) diff --git a/src/dynarec/dynarec_native_functions.c b/src/dynarec/dynarec_native_functions.c index 85b3a22711..909298ef25 100644 --- a/src/dynarec/dynarec_native_functions.c +++ b/src/dynarec/dynarec_native_functions.c @@ -784,7 +784,6 @@ static int flagsCacheNeedsTransform(dynarec_native_t* dyn, int ninst) { int jmp = dyn->insts[ninst].x64.jmp_insts; if(jmp<0) return 0; - #if defined(ARM64) || defined(LA64) || defined(PPC64LE) // df_none is now a defered information if(dyn->insts[ninst].f_exit==dyn->insts[jmp].f_entry) return 0; @@ -802,28 +801,6 @@ static int flagsCacheNeedsTransform(dynarec_native_t* dyn, int ninst) { case status_none_pending: return 1; } -#else - if(dyn->insts[ninst].f_exit.dfnone) // flags are fully known, nothing we can do more - return 0; - if(dyn->insts[jmp].f_entry.dfnone && !dyn->insts[ninst].f_exit.dfnone && !dyn->insts[jmp].df_notneeded) - return 1; - switch (dyn->insts[jmp].f_entry.pending) { - case SF_UNKNOWN: return 0; - case SF_SET: - if(dyn->insts[ninst].f_exit.pending!=SF_SET && dyn->insts[ninst].f_exit.pending!=SF_SET_PENDING) - return 1; - else - return 0; - case SF_SET_PENDING: - if(dyn->insts[ninst].f_exit.pending==SF_SET_PENDING) - return 0; - return 1; - case SF_PENDING: - if(dyn->insts[ninst].f_exit.pending==SF_PENDING || dyn->insts[ninst].f_exit.pending==SF_SET_PENDING) - return 0; - return (dyn->insts[jmp].f_entry.dfnone == dyn->insts[ninst].f_exit.dfnone)?0:1; - } - #endif return 0; } @@ -885,7 +862,6 @@ uint8_t geted_ib(dynarec_native_t* dyn, uintptr_t addr, int ninst, uint8_t nexto } #undef F8 -#if defined(ARM64) || defined(LA64) || defined(PPC64LE) static void propagate_dfneeded_internal(dynarec_native_t* dyn, int ninst) { while(ninst>=0) { @@ -937,7 +913,6 @@ void propagate_nodf(dynarec_native_t* dyn) propagate_nodf_internal(dyn, ninst); } } -#endif void x64disas_add_register_mapping_annotations(char* buf, const char* disas, const register_mapping_t* mappings, size_t mappings_sz) { diff --git a/src/dynarec/dynarec_native_pass.c b/src/dynarec/dynarec_native_pass.c index 15f15caa43..032d64e05f 100644 --- a/src/dynarec/dynarec_native_pass.c +++ b/src/dynarec/dynarec_native_pass.c @@ -105,12 +105,7 @@ uintptr_t native_pass(dynarec_native_t* dyn, uintptr_t addr, int alternate, int int rep = 0; // 0 none, 1=F2 prefix, 2=F3 prefix int need_epilog = 1; // Clean up (because there are multiple passes) - #if defined(ARM64) || defined(LA64) || defined(PPC64LE) dyn->f = status_unk; - #else - dyn->f.pending = 0; - dyn->f.dfnone = 0; - #endif dyn->forward = 0; dyn->forward_to = 0; dyn->forward_size = 0; @@ -184,12 +179,7 @@ uintptr_t native_pass(dynarec_native_t* dyn, uintptr_t addr, int alternate, int dyn->last_ip = 0; if(reset_n==-2) { MESSAGE(LOG_DEBUG, "Reset Caches to zero\n"); - #if defined(ARM64) || defined(LA64) || defined(PPC64LE) dyn->f = status_unk; - #else - dyn->f.dfnone = 0; - dyn->f.pending = 0; - #endif fpu_reset(dyn); ARCH_RESET(); } else { @@ -201,12 +191,7 @@ uintptr_t native_pass(dynarec_native_t* dyn, uintptr_t addr, int alternate, int } if(dyn->insts[ninst].x64.barrier&BARRIER_FLAGS) { MESSAGE(LOG_DEBUG, "Apply Barrier Flags\n"); - #if defined(ARM64) || defined(LA64) || defined(PPC64LE) dyn->f = status_unk; - #else - dyn->f.dfnone = 0; - dyn->f.pending = 0; - #endif } } reset_n = -1; @@ -340,13 +325,8 @@ uintptr_t native_pass(dynarec_native_t* dyn, uintptr_t addr, int alternate, int fpu_purgecache(dyn, ninst, 0, x1, x2, x3, 0); #endif } - if(dyn->insts[next].x64.barrier&BARRIER_FLAGS) { - #if defined(ARM64) || defined(LA64) || defined(PPC64LE) + if (dyn->insts[next].x64.barrier & BARRIER_FLAGS) { dyn->f = status_unk; - #else - dyn->f.pending = 0; - dyn->f.dfnone = 0; - #endif dyn->last_ip = 0; } } @@ -367,12 +347,7 @@ uintptr_t native_pass(dynarec_native_t* dyn, uintptr_t addr, int alternate, int // we use the 1st predecessor here if((ninst+1)size && !dyn->insts[ninst+1].x64.alive) { // reset fpu value... - #if defined(ARM64) || defined(LA64) || defined(PPC64LE) dyn->f = status_unk; - #else - dyn->f.dfnone = 0; - dyn->f.pending = 0; - #endif fpu_reset(dyn); while((ninst+1)size && !dyn->insts[ninst+1].x64.alive) { // may need to skip opcodes to advance diff --git a/src/dynarec/rv64/dynarec_rv64_functions.c b/src/dynarec/rv64/dynarec_rv64_functions.c index ecc8210f9f..71fb443139 100644 --- a/src/dynarec/rv64/dynarec_rv64_functions.c +++ b/src/dynarec/rv64/dynarec_rv64_functions.c @@ -687,6 +687,8 @@ static register_mapping_t register_mappings[] = { { "rip", "s6" }, }; +static const char* df_status[] = { "unknown", "set", "none_pending", "none" }; + void printf_x64_instruction(dynarec_native_t* dyn, zydis_dec_t* dec, instruction_x64_t* inst, const char* name); void inst_name_pass3(dynarec_native_t* dyn, int ninst, const char* name, rex_t rex) { @@ -697,11 +699,12 @@ void inst_name_pass3(dynarec_native_t* dyn, int ninst, const char* name, rex_t r if (!dyn->need_dump && !BOX64ENV(dynarec_gdbjit) && !BOX64ENV(dynarec_perf_map)) return; static char buf[4096]; - int length = sprintf(buf, "barrier=%d state=%d/%d(%d), set=%X/%X, use=%X, need=%X/%X, fuse=%d/%d, sm=%d(%d/%d), sew@entry=%d, sew@exit=%d", + int length = sprintf(buf, "barrier=%d state=%d/%s(%s->%s), set=%X/%X, use=%X, need=%X/%X, fuse=%d/%d, sm=%d(%d/%d), sew@entry=%d, sew@exit=%d", dyn->insts[ninst].x64.barrier, dyn->insts[ninst].x64.state_flags, - dyn->f.pending, - dyn->f.dfnone, + df_status[dyn->f], + df_status[dyn->insts[ninst].f_entry], + df_status[dyn->insts[ninst].f_exit], dyn->insts[ninst].x64.set_flags, dyn->insts[ninst].x64.gen_flags, dyn->insts[ninst].x64.use_flags, diff --git a/src/dynarec/rv64/dynarec_rv64_helper.c b/src/dynarec/rv64/dynarec_rv64_helper.c index 5f1d0bcaf8..0f9b0ecad5 100644 --- a/src/dynarec/rv64/dynarec_rv64_helper.c +++ b/src/dynarec/rv64/dynarec_rv64_helper.c @@ -379,6 +379,7 @@ void jump_to_epilog(dynarec_rv64_t* dyn, uintptr_t ip, int reg, int ninst) } TABLE64C(x2, const_epilog); SMEND(); + CHECK_DFNONE(0); BR(x2); } @@ -398,6 +399,7 @@ void jump_to_epilog_fast(dynarec_rv64_t* dyn, uintptr_t ip, int reg, int ninst) } TABLE64C(x2, const_epilog_fast); SMEND(); + CHECK_DFNONE(0); BR(x2); } #ifdef JMPTABLE_SHIFT4 @@ -462,6 +464,8 @@ void jump_to_next(dynarec_rv64_t* dyn, uintptr_t ip, int reg, int ninst, int is3 if (is32bits) ip &= 0xffffffffLL; + CHECK_DFNONE(0); + int dest; if (reg) { if (reg != xRIP) { @@ -492,13 +496,14 @@ void ret_to_next(dynarec_rv64_t* dyn, uintptr_t ip, int ninst, rex_t rex) MAYUSE(dyn); MAYUSE(ninst); MESSAGE(LOG_DUMP, "Ret to next\n"); + CHECK_DFNONE(0); MVz(x1, xRIP); SMEND(); if (BOX64DRENV(dynarec_callret)) { // pop the actual return address from LA64 stack - LD(xRA, xSP, 0); // native addr - LD(x6, xSP, 8); // x86 addr - ADDI(xSP, xSP, 16); // pop + LD(xRA, xSP, 0); // native addr + LD(x6, xSP, 8); // x86 addr + ADDI(xSP, xSP, 16); // pop BNE(x6, xRIP, 2 * 4); // is it the right address? BR(xRA); // not the correct return address, regular jump, but purge the stack first, it's unsync now... @@ -529,7 +534,7 @@ void iret_to_next(dynarec_rv64_t* dyn, uintptr_t ip, int ninst, int is32bits, in SH(x2, xEmu, offsetof(x64emu_t, segs[_CS])); // clean EFLAGS - MOV32w(x1, 0x3E7FF7); // also masking RF + MOV32w(x1, 0x3E7FF7); // also masking RF AND(xFlags, xFlags, x1); ORI(xFlags, xFlags, 0x2); SET_DFNONE(); @@ -563,6 +568,7 @@ void iret_to_next(dynarec_rv64_t* dyn, uintptr_t ip, int ninst, int is32bits, in void call_c(dynarec_rv64_t* dyn, int ninst, rv64_consts_t fnc, int reg, int ret, int saveflags, int savereg, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6) { MAYUSE(fnc); + CHECK_DFNONE(1); if (savereg == 0) savereg = x87pc; if (saveflags) { @@ -628,6 +634,7 @@ 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); + CHECK_DFNONE(1); fpu_pushcache(dyn, ninst, x3, 1); // save RSP in case there are x86 callbacks... SD(xRSP, xEmu, offsetof(x64emu_t, regs[_SP])); @@ -641,7 +648,7 @@ void call_n(dynarec_rv64_t* dyn, int ninst, void* fnc, int w) } // native call TABLE64_(x3, *(uintptr_t*)fnc); // using x16 as scratch regs for call address - // Note that if need_reloc is active, the TABLE64 will trigger cancel block, + // Note that if need_reloc is active, the TABLE64 will trigger cancel block, // because native function might be very different on a next run: different function address, different brick, different everything basicaly // and we don't have a relocation mecanism here, it's too complex JALR(xRA, x3); @@ -2457,31 +2464,45 @@ static void flagsCacheTransform(dynarec_rv64_t* dyn, int ninst, int s1) int jmp = dyn->insts[ninst].x64.jmp_insts; if (jmp < 0) return; - if (dyn->f.dfnone || ((dyn->insts[jmp].f_exit.dfnone && !dyn->insts[jmp].f_entry.dfnone) && !dyn->insts[jmp].x64.use_flags)) // flags are fully known, nothing we can do more + if (dyn->insts[ninst].f_exit == dyn->insts[jmp].f_entry) // flags will be fully known, nothing we can do more return; MESSAGE(LOG_DUMP, "\tFlags fetch ---- ninst=%d -> %d\n", ninst, jmp); - int go = (dyn->insts[jmp].f_entry.dfnone && !dyn->f.dfnone && !dyn->insts[jmp].df_notneeded) ? 1 : 0; - switch (dyn->insts[jmp].f_entry.pending) { - case SF_UNKNOWN: - go = 0; + int go_fetch = 0; + switch (dyn->insts[jmp].f_entry) { + case status_unk: + if (dyn->insts[ninst].f_exit == status_none_pending) { + FORCE_DFNONE(); + } break; - default: - if (go && !(dyn->insts[jmp].x64.need_before & X_PEND) && (dyn->f.pending != SF_UNKNOWN)) { - // just clear df flags - go = 0; - SW(xZR, xEmu, offsetof(x64emu_t, df)); + case status_set: + if (dyn->insts[ninst].f_exit == status_none_pending) { + FORCE_DFNONE(); } + if (dyn->insts[ninst].f_exit == status_unk) + go_fetch = 1; + break; + case status_none_pending: + if (dyn->insts[ninst].f_exit != status_none) + go_fetch = 1; + break; + case status_none: + if (dyn->insts[ninst].f_exit == status_none_pending) { + FORCE_DFNONE(); + } else + go_fetch = 1; break; } - if (go) { - if (dyn->f.pending != SF_PENDING) { + if (go_fetch) { + if (dyn->f == status_unk) { LWU(s1, xEmu, offsetof(x64emu_t, df)); j64 = (GETMARKF2) - (dyn->native_size); BEQZ(s1, j64); } CALL_(const_updateflags, -1, 0, 0, 0); MARKF2; + dyn->f = status_none; } + MESSAGE(LOG_DUMP, "\t---- Flags fetch\n"); } static void sewTransform(dynarec_rv64_t* dyn, int ninst, int s1) @@ -2596,7 +2617,7 @@ void fpu_reset_cache(dynarec_rv64_t* dyn, int ninst, int reset_n) dyn->vector_sew = dyn->insts[reset_n].vector_sew_exit; #endif #if STEP == 0 - if(dyn->need_dump && dyn->need_dump != 3 && dyn->e.x87stack) dynarec_log(LOG_NONE, "New x87stack=%d at ResetCache in inst %d with %d\n", dyn->e.x87stack, ninst, reset_n); + if (dyn->need_dump && dyn->need_dump != 3 && dyn->e.x87stack) dynarec_log(LOG_NONE, "New x87stack=%d at ResetCache in inst %d with %d\n", dyn->e.x87stack, ninst, reset_n); #endif #if defined(HAVE_TRACE) && (STEP > 2) if (dyn->need_dump && 0) // disable for now diff --git a/src/dynarec/rv64/dynarec_rv64_helper.h b/src/dynarec/rv64/dynarec_rv64_helper.h index 72e519479f..fbe4183266 100644 --- a/src/dynarec/rv64/dynarec_rv64_helper.h +++ b/src/dynarec/rv64/dynarec_rv64_helper.h @@ -871,30 +871,32 @@ LOAD_REG(R15); #define FORCE_DFNONE() SW(xZR, xEmu, offsetof(x64emu_t, df)) +#define CHECK_DFNONE(N) \ + do { \ + if (dyn->f == status_none_pending) { \ + FORCE_DFNONE(); \ + if (N) dyn->f = status_none; \ + } \ + } while (0) -#define SET_DFNONE() \ - do { \ - if (!dyn->f.dfnone) { \ - FORCE_DFNONE(); \ - } \ - dyn->f.dfnone = 1; \ +#define SET_DFNONE() \ + do { \ + if (dyn->f != status_none) { \ + dyn->f = status_none_pending; \ + } \ } while (0) -#define SET_DF(S, N) \ - if ((N) != d_none) { \ - MOV_U12(S, (N)); \ - SW(S, xEmu, offsetof(x64emu_t, df)); \ - if (dyn->f.pending == SF_PENDING && dyn->insts[ninst].x64.need_after && !(dyn->insts[ninst].x64.need_after & X_PEND)) { \ - CALL_(const_updateflags, -1, 0, 0, 0); \ - dyn->f.pending = SF_SET; \ - SET_NODF(); \ - } \ - dyn->f.dfnone = 0; \ - } else \ +#define SET_DF(S, N) \ + if ((N) != d_none) { \ + MOV_U12(S, (N)); \ + SW(S, xEmu, offsetof(x64emu_t, df)); \ + dyn->f = status_set; \ + if (dyn->insts[ninst].x64.state_flags == SF_PENDING && dyn->insts[ninst].x64.need_after && !(dyn->insts[ninst].x64.need_after & X_PEND)) { \ + CALL_(const_updateflags, -1, 0, 0, 0); \ + dyn->f = status_none; \ + } \ + } else \ SET_DFNONE() -#define SET_NODF() dyn->f.dfnone = 0 -#define SET_DFOK() \ - dyn->f.dfnone = 1 #define CLEAR_FLAGS() \ IFX (X_ALL) { ANDI(xFlags, xFlags, ~((1UL << F_AF) | (1UL << F_CF) | (1UL << F_OF2) | (1UL << F_ZF) | (1UL << F_SF) | (1UL << F_PF))); } @@ -1027,18 +1029,28 @@ #endif #ifndef READFLAGS -#define READFLAGS(A) \ - if (((A) != X_PEND && dyn->f.pending != SF_SET) \ - && (dyn->f.pending != SF_SET_PENDING)) { \ - if (dyn->f.pending != SF_PENDING) { \ - LWU(x3, xEmu, offsetof(x64emu_t, df)); \ - j64 = (GETMARKF) - (dyn->native_size); \ - BEQ(x3, xZR, j64); \ - } \ - CALL_(const_updateflags, -1, 0, 0, 0); \ - MARKF; \ - dyn->f.pending = SF_SET; \ - SET_DFOK(); \ +#define READFLAGS(A) \ + if ((A) != X_PEND \ + && (dyn->f == status_unk)) { \ + LWU(x3, xEmu, offsetof(x64emu_t, df)); \ + j64 = (GETMARKF) - (dyn->native_size); \ + BEQ(x3, xZR, j64); \ + CALL_(const_updateflags, -1, 0, 0, 0); \ + MARKF; \ + dyn->f = status_none; \ + } +#endif + +#ifndef GRABFLAGS +#define GRABFLAGS(A) \ + if ((A) != X_PEND \ + && ((dyn->f == status_unk) || (dyn->f == status_set))) { \ + LWU(x3, xEmu, offsetof(x64emu_t, df)); \ + j64 = (GETMARKF) - (dyn->native_size); \ + BEQ(x3, xZR, j64); \ + CALL_(const_updateflags, -1, 0, 0, 0); \ + MARKF; \ + dyn->f = status_none; \ } #endif @@ -1073,29 +1085,22 @@ #ifndef SETFLAGS #define SETFLAGS(A, B, FUSION) \ - if (dyn->f.pending != SF_SET \ - && ((B) & SF_SUB) \ + if (((B) & SF_SUB) \ && (dyn->insts[ninst].x64.gen_flags & (~(A)))) \ - READFLAGS(((dyn->insts[ninst].x64.gen_flags & X_PEND) ? X_ALL : dyn->insts[ninst].x64.gen_flags) & (~(A))); \ + GRABFLAGS(((dyn->insts[ninst].x64.gen_flags & X_PEND) ? X_ALL : dyn->insts[ninst].x64.gen_flags) & (~(A))); \ if (dyn->insts[ninst].x64.gen_flags) switch (B) { \ + case SF_SET_DF: dyn->f = status_set; break; \ + case SF_SET_NODF: SET_DFNONE(); break; \ case SF_SUBSET: \ - case SF_SET: dyn->f.pending = SF_SET; break; \ - case SF_SET_DF: \ - dyn->f.pending = SF_SET; \ - dyn->f.dfnone = 1; \ - break; \ - case SF_SET_NODF: \ - dyn->f.pending = SF_SET; \ - dyn->f.dfnone = 0; \ - break; \ - case SF_PENDING: dyn->f.pending = SF_PENDING; break; \ case SF_SUBSET_PENDING: \ + case SF_SET: \ + case SF_PENDING: \ case SF_SET_PENDING: \ - dyn->f.pending = (dyn->insts[ninst].x64.gen_flags & X_PEND) ? SF_SET_PENDING : SF_SET; \ + SET_DFNONE(); \ break; \ } \ else \ - dyn->f.pending = SF_SET; \ + SET_DFNONE(); \ dyn->insts[ninst].nat_flags_nofusion = (FUSION) #endif #ifndef JUMP diff --git a/src/dynarec/rv64/dynarec_rv64_pass0.h b/src/dynarec/rv64/dynarec_rv64_pass0.h index c2b0ba7822..095b3cdb1a 100644 --- a/src/dynarec/rv64/dynarec_rv64_pass0.h +++ b/src/dynarec/rv64/dynarec_rv64_pass0.h @@ -8,10 +8,13 @@ #define MESSAGE(A, ...) \ do { \ } while (0) -#define READFLAGS(A) \ - dyn->insts[ninst].x64.use_flags = A; \ - dyn->f.dfnone = 1; \ - dyn->f.pending = SF_SET +#define READFLAGS(A) \ + do { \ + dyn->insts[ninst].x64.use_flags = A; \ + if (dyn->f != status_none_pending) dyn->f = status_none; \ + if (!BOX64ENV(dynarec_df) && (A) & X_PEND) dyn->insts[ninst].x64.use_flags = X_ALL; \ + dyn->f = status_none; \ + } while (0) #define READFLAGS_FUSION(A, s1, s2, s3, s4, s5) \ if (BOX64ENV(dynarec_nativeflags) && ninst > 0) { \ @@ -30,22 +33,22 @@ READFLAGS(A); /* SF_SET_NODF and SF_SET_PENDING may leave the incoming deferred flags untouched. */ -#define SETFLAGS(A, B, FUSION) \ - do { \ - dyn->insts[ninst].x64.set_flags = A; \ - dyn->insts[ninst].x64.state_flags = (B) & ~SF_DF; \ - if (((B) & SF_SET_PENDING) != SF_SET_PENDING) { \ - if ((B) & SF_SET) { \ - if ((B) != SF_SET_NODF) { \ - dyn->f.pending = SF_SET; \ - dyn->f.dfnone = 1; \ - } \ - } else if (!dyn->f.dfnone) { \ - dyn->f.pending = SF_SET; \ - dyn->f.dfnone = 0; \ - } \ - } \ - dyn->insts[ninst].nat_flags_nofusion = (FUSION); \ +#define SETFLAGS(A, B, FUSION) \ + do { \ + dyn->insts[ninst].x64.set_flags = A; \ + dyn->insts[ninst].x64.state_flags = (B) & ~SF_DF; \ + if (((B) & SF_SET_PENDING) != SF_SET_PENDING) { \ + dyn->f = ((B) & SF_SET) ? (((B) == SF_SET_NODF) ? dyn->f : status_none_pending) \ + : ((dyn->f == status_none) ? status_none : status_none_pending); \ + } \ + if (!BOX64ENV(dynarec_df)) { \ + dyn->f = status_none; \ + if ((B) == SF_PENDING) { \ + printf_log(LOG_INFO, "Warning, some opcode use SF_PENDING, forcing deferedflags ON\n"); \ + SET_BOX64ENV(dynarec_df, 1); \ + } \ + } \ + dyn->insts[ninst].nat_flags_nofusion = (FUSION); \ } while (0) #define EMIT(A) dyn->native_size += 4 diff --git a/src/dynarec/rv64/dynarec_rv64_private.h b/src/dynarec/rv64/dynarec_rv64_private.h index 160f6cec9f..9985c1b502 100644 --- a/src/dynarec/rv64/dynarec_rv64_private.h +++ b/src/dynarec/rv64/dynarec_rv64_private.h @@ -91,9 +91,11 @@ typedef struct extcache_s { int8_t fpu_scratch; // scratch counter } extcache_t; -typedef struct flagcache_s { - int pending; // is there a pending flags here, or to check? - uint8_t dfnone; // if deferred flags is already set to df_none +typedef enum flagcache_s { + status_unk = 0, // unknown deferred flags status + status_set, // deferred flags set to something (not 0) + status_none_pending, // deferred flags set to 0, but still pending the write to x64emu_t + status_none, // deferred flags set to 0, written to x64emu_t } flagcache_t; typedef struct callret_s callret_t; From 7c8b079c76be6ad8da0eb6ff0160c59d3dd94a56 Mon Sep 17 00:00:00 2001 From: Yang Liu Date: Sat, 8 Aug 2026 19:20:51 +0800 Subject: [PATCH 2/2] [RV64_DYNAREC] Refactored MUL opcodes --- src/dynarec/rv64/dynarec_rv64_00_1.c | 86 +++++++++++++++++++++------- src/dynarec/rv64/dynarec_rv64_00_3.c | 44 ++++++++++++-- src/dynarec/rv64/dynarec_rv64_0f.c | 44 ++++++++++---- src/dynarec/rv64/dynarec_rv64_66.c | 44 ++++++++++++-- src/dynarec/rv64/dynarec_rv64_660f.c | 23 +++++++- 5 files changed, 195 insertions(+), 46 deletions(-) diff --git a/src/dynarec/rv64/dynarec_rv64_00_1.c b/src/dynarec/rv64/dynarec_rv64_00_1.c index 922304e127..f7ff6ab765 100644 --- a/src/dynarec/rv64/dynarec_rv64_00_1.c +++ b/src/dynarec/rv64/dynarec_rv64_00_1.c @@ -221,20 +221,29 @@ uintptr_t dynarec64_00_1(dynarec_rv64_t* dyn, uintptr_t addr, uintptr_t ip, int break; case 0x69: INST_NAME("IMUL Gd, Ed, Id"); - SETFLAGS(X_ALL, SF_PENDING, NAT_FLAGS_NOFUSION); + SETFLAGS(X_ALL, SF_SET_NODF, NAT_FLAGS_NOFUSION); nextop = F8; GETGD; GETED(4); i64 = F32S; MOV64x(x4, i64); + CLEAR_FLAGS(); if (rex.w) { // 64bits imul UFLAG_IF { MULH(x3, ed, x4); MUL(gd, ed, x4); - UFLAG_OP1(x3); - UFLAG_RES(gd); - UFLAG_DF(x3, d_imul64); + SET_DFNONE(); + IFX (X_CF | X_OF) { + SRAI(x5, gd, 63); + XOR(x3, x3, x5); + SNEZ(x3, x3); + IFX (X_CF) OR(xFlags, xFlags, x3); // F_CF == 0 + IFX (X_OF) { + SLLI(x3, x3, F_OF2); + OR(xFlags, xFlags, x3); + } + } } else { MULxw(gd, ed, x4); } @@ -242,16 +251,30 @@ uintptr_t dynarec64_00_1(dynarec_rv64_t* dyn, uintptr_t addr, uintptr_t ip, int // 32bits imul UFLAG_IF { SEXT_W(x3, ed); - MUL(gd, x3, x4); - UFLAG_RES(gd); - SRLI(x3, gd, 32); - UFLAG_OP1(x3); - UFLAG_DF(x3, d_imul32); + MUL(x5, x3, x4); + SET_DFNONE(); + IFX (X_CF | X_OF) { + SEXT_W(x3, x5); + XOR(x3, x5, x3); + SNEZ(x3, x3); + IFX (X_CF) OR(xFlags, xFlags, x3); // F_CF == 0 + IFX (X_OF) { + SLLI(x3, x3, F_OF2); + OR(xFlags, xFlags, x3); + } + } + ZEXTW2(gd, x5); } else { MULxw(gd, ed, x4); + ZEROUP(gd); } - ZEROUP(gd); } + IFX (X_SF) { + SRLI(x5, gd, rex.w ? 63 : 31); + SLLI(x5, x5, F_SF); + OR(xFlags, xFlags, x5); + } + IFX (X_PF) emit_pf(dyn, ninst, gd, x3, x4); break; case 0x6A: INST_NAME("PUSH Ib"); @@ -265,20 +288,29 @@ uintptr_t dynarec64_00_1(dynarec_rv64_t* dyn, uintptr_t addr, uintptr_t ip, int break; case 0x6B: INST_NAME("IMUL Gd, Ed, Ib"); - SETFLAGS(X_ALL, SF_PENDING, NAT_FLAGS_NOFUSION); + SETFLAGS(X_ALL, SF_SET_NODF, NAT_FLAGS_NOFUSION); nextop = F8; GETGD; GETED(1); i64 = F8S; MOV64x(x4, i64); + CLEAR_FLAGS(); if (rex.w) { // 64bits imul UFLAG_IF { MULH(x3, ed, x4); MUL(gd, ed, x4); - UFLAG_OP1(x3); - UFLAG_RES(gd); - UFLAG_DF(x3, d_imul64); + SET_DFNONE(); + IFX (X_CF | X_OF) { + SRAI(x5, gd, 63); + XOR(x3, x3, x5); + SNEZ(x3, x3); + IFX (X_CF) OR(xFlags, xFlags, x3); // F_CF == 0 + IFX (X_OF) { + SLLI(x3, x3, F_OF2); + OR(xFlags, xFlags, x3); + } + } } else { MUL(gd, ed, x4); } @@ -286,16 +318,30 @@ uintptr_t dynarec64_00_1(dynarec_rv64_t* dyn, uintptr_t addr, uintptr_t ip, int // 32bits imul UFLAG_IF { SEXT_W(x3, ed); - MUL(gd, x3, x4); - UFLAG_RES(gd); - SRLI(x3, gd, 32); - UFLAG_OP1(x3); - UFLAG_DF(x3, d_imul32); + MUL(x5, x3, x4); // full 64bits product + SET_DFNONE(); + IFX (X_CF | X_OF) { + SEXT_W(x3, x5); + XOR(x3, x5, x3); + SNEZ(x3, x3); + IFX (X_CF) OR(xFlags, xFlags, x3); // F_CF == 0 + IFX (X_OF) { + SLLI(x3, x3, F_OF2); + OR(xFlags, xFlags, x3); + } + } + ZEXTW2(gd, x5); } else { MULW(gd, ed, x4); + ZEROUP(gd); } - ZEROUP(gd); } + IFX (X_SF) { + SRLI(x5, gd, rex.w ? 63 : 31); + SLLI(x5, x5, F_SF); + OR(xFlags, xFlags, x5); + } + IFX (X_PF) emit_pf(dyn, ninst, gd, x3, x4); break; case 0x6C: diff --git a/src/dynarec/rv64/dynarec_rv64_00_3.c b/src/dynarec/rv64/dynarec_rv64_00_3.c index bdd1e948c6..39ecb8f481 100644 --- a/src/dynarec/rv64/dynarec_rv64_00_3.c +++ b/src/dynarec/rv64/dynarec_rv64_00_3.c @@ -1374,30 +1374,62 @@ uintptr_t dynarec64_00_3(dynarec_rv64_t* dyn, uintptr_t addr, uintptr_t ip, int break; case 4: INST_NAME("MUL AL, Ed"); - SETFLAGS(X_ALL, SF_PENDING, NAT_FLAGS_NOFUSION); + SETFLAGS(X_ALL, SF_SET_NODF, NAT_FLAGS_NOFUSION); GETEB(x1, 0); ANDI(x2, xRAX, 0xff); MULW(x1, x2, x1); - UFLAG_RES(x1); + SET_DFNONE(); + CLEAR_FLAGS(); + IFX (X_CF | X_OF) { + SRLI(x3, x1, 8); + SNEZ(x3, x3); + IFX (X_CF) OR(xFlags, xFlags, x3); // F_CF == 0 + IFX (X_OF) { + SLLI(x3, x3, F_OF2); + OR(xFlags, xFlags, x3); + } + } LUI(x2, 0xffff0); AND(xRAX, xRAX, x2); ZEXTH(x1, x1); OR(xRAX, xRAX, x1); - UFLAG_DF(x1, d_mul8); + IFX (X_SF) { + SRLI(x3, xRAX, 7); + SLLI(x3, x3, F_SF); + OR(xFlags, xFlags, x3); + } + IFX (X_PF) emit_pf(dyn, ninst, xRAX, x3, x4); break; case 5: INST_NAME("IMUL AL, Eb"); - SETFLAGS(X_ALL, SF_PENDING, NAT_FLAGS_NOFUSION); + SETFLAGS(X_ALL, SF_SET_NODF, NAT_FLAGS_NOFUSION); GETSEB(x1, 0); SLLI(x2, xRAX, 56); SRAI(x2, x2, 56); MULW(x1, x2, x1); - UFLAG_RES(x1); + SET_DFNONE(); + CLEAR_FLAGS(); + IFX (X_CF | X_OF) { + SLLI(x3, x1, 48); + SRAI(x3, x3, 48); // x3 = SignExtend16(result) + XOR(x3, x3, x1); + SNEZ(x3, x3); + IFX (X_CF) OR(xFlags, xFlags, x3); // F_CF == 0 + IFX (X_OF) { + SLLI(x3, x3, F_OF2); + OR(xFlags, xFlags, x3); + } + } LUI(x2, 0xffff0); AND(xRAX, xRAX, x2); ZEXTH(x1, x1); OR(xRAX, xRAX, x1); - UFLAG_DF(x1, d_imul8); + IFX (X_SF) { + SRLI(x3, xRAX, 7); + SLLI(x3, x3, F_SF); + OR(xFlags, xFlags, x3); + } + IFX (X_PF) emit_pf(dyn, ninst, xRAX, x3, x4); break; case 6: INST_NAME("DIV Eb"); diff --git a/src/dynarec/rv64/dynarec_rv64_0f.c b/src/dynarec/rv64/dynarec_rv64_0f.c index a01a48ddf2..6fd5ccb076 100644 --- a/src/dynarec/rv64/dynarec_rv64_0f.c +++ b/src/dynarec/rv64/dynarec_rv64_0f.c @@ -2205,20 +2205,28 @@ uintptr_t dynarec64_0F(dynarec_rv64_t* dyn, uintptr_t addr, uintptr_t ip, int ni } break; case 0xAF: - // TODO: Refine this INST_NAME("IMUL Gd, Ed"); - SETFLAGS(X_ALL, SF_PENDING, NAT_FLAGS_NOFUSION); + SETFLAGS(X_ALL, SF_SET_NODF, NAT_FLAGS_NOFUSION); nextop = F8; GETGD; GETED(0); + CLEAR_FLAGS(); if (rex.w) { // 64bits imul UFLAG_IF { MULH(x3, gd, ed); MUL(gd, gd, ed); - UFLAG_OP1(x3); - UFLAG_RES(gd); - UFLAG_DF(x3, d_imul64); + SET_DFNONE(); + IFX (X_CF | X_OF) { + SRAI(x5, gd, 63); + XOR(x3, x3, x5); + SNEZ(x3, x3); + IFX (X_CF) OR(xFlags, xFlags, x3); // F_CF == 0 + IFX (X_OF) { + SLLI(x3, x3, F_OF2); + OR(xFlags, xFlags, x3); + } + } } else { MULxw(gd, gd, ed); } @@ -2227,16 +2235,30 @@ uintptr_t dynarec64_0F(dynarec_rv64_t* dyn, uintptr_t addr, uintptr_t ip, int ni UFLAG_IF { SEXT_W(gd, gd); SEXT_W(x3, ed); - MUL(gd, gd, x3); - UFLAG_RES(gd); - SRLI(x3, gd, 32); - UFLAG_OP1(x3); - UFLAG_DF(x3, d_imul32); + MUL(x5, gd, x3); // full 64bits product + SET_DFNONE(); + IFX (X_CF | X_OF) { + SEXT_W(x3, x5); + XOR(x3, x5, x3); + SNEZ(x3, x3); + IFX (X_CF) OR(xFlags, xFlags, x3); // F_CF == 0 + IFX (X_OF) { + SLLI(x3, x3, F_OF2); + OR(xFlags, xFlags, x3); + } + } + ZEXTW2(gd, x5); } else { MULxw(gd, gd, ed); + ZEROUP(gd); } - ZEROUP(gd); } + IFX (X_SF) { + SRLI(x5, gd, rex.w ? 63 : 31); + SLLI(x5, x5, F_SF); + OR(xFlags, xFlags, x5); + } + IFX (X_PF) emit_pf(dyn, ninst, gd, x3, x4); break; case 0xB3: INST_NAME("BTR Ed, Gd"); diff --git a/src/dynarec/rv64/dynarec_rv64_66.c b/src/dynarec/rv64/dynarec_rv64_66.c index b5364aa11e..1a1a0cc6ed 100644 --- a/src/dynarec/rv64/dynarec_rv64_66.c +++ b/src/dynarec/rv64/dynarec_rv64_66.c @@ -1482,34 +1482,66 @@ uintptr_t dynarec64_66(dynarec_rv64_t* dyn, uintptr_t addr, uintptr_t ip, int ni break; case 4: INST_NAME("MUL AX, Ew"); - SETFLAGS(X_ALL, SF_PENDING, NAT_FLAGS_NOFUSION); + SETFLAGS(X_ALL, SF_SET_NODF, NAT_FLAGS_NOFUSION); GETEW(x1, 0); ZEXTH(x2, xRAX); MULW(x1, x2, x1); ZEROUP(x1); - UFLAG_RES(x1); + SET_DFNONE(); + CLEAR_FLAGS(); + IFX (X_CF | X_OF) { + SRLI(x3, x1, 16); + SNEZ(x3, x3); + IFX (X_CF) OR(xFlags, xFlags, x3); // F_CF == 0 + IFX (X_OF) { + SLLI(x3, x3, F_OF2); + OR(xFlags, xFlags, x3); + } + } INSHz(xRAX, x1, x4, x5, 1, 1); SRLI(xRDX, xRDX, 16); SLLI(xRDX, xRDX, 16); SRLI(x1, x1, 16); OR(xRDX, xRDX, x1); - UFLAG_DF(x1, d_mul16); + IFX (X_SF) { + SRLI(x3, xRDX, 15); + SLLI(x3, x3, F_SF); + OR(xFlags, xFlags, x3); + } + IFX (X_PF) emit_pf(dyn, ninst, xRAX, x3, x4); break; case 5: INST_NAME("IMUL AX, Ew"); - SETFLAGS(X_ALL, SF_PENDING, NAT_FLAGS_NOFUSION); + SETFLAGS(X_ALL, SF_SET_NODF, NAT_FLAGS_NOFUSION); GETSEW(x1, 0); SLLI(x2, xRAX, 16); SRAIW(x2, x2, 16); MULW(x1, x2, x1); ZEROUP(x1); - UFLAG_RES(x1); + SET_DFNONE(); + CLEAR_FLAGS(); + IFX (X_CF | X_OF) { + SLLI(x3, x1, 48); + SRAI(x3, x3, 48); // x3 = SignExtend16(result) + XOR(x3, x3, x1); + SNEZ(x3, x3); + IFX (X_CF) OR(xFlags, xFlags, x3); // F_CF == 0 + IFX (X_OF) { + SLLI(x3, x3, F_OF2); + OR(xFlags, xFlags, x3); + } + } INSHz(xRAX, x1, x4, x5, 1, 1); SRLI(xRDX, xRDX, 16); SLLI(xRDX, xRDX, 16); SRLI(x1, x1, 16); OR(xRDX, xRDX, x1); - UFLAG_DF(x1, d_imul16); + IFX (X_SF) { + SRLI(x3, xRAX, 15); + SLLI(x3, x3, F_SF); + OR(xFlags, xFlags, x3); + } + IFX (X_PF) emit_pf(dyn, ninst, xRAX, x3, x4); break; case 6: INST_NAME("DIV Ew"); diff --git a/src/dynarec/rv64/dynarec_rv64_660f.c b/src/dynarec/rv64/dynarec_rv64_660f.c index b6051f8ab7..d02fc362b7 100644 --- a/src/dynarec/rv64/dynarec_rv64_660f.c +++ b/src/dynarec/rv64/dynarec_rv64_660f.c @@ -1425,15 +1425,32 @@ uintptr_t dynarec64_660F(dynarec_rv64_t* dyn, uintptr_t addr, uintptr_t ip, int break; case 0xAF: INST_NAME("IMUL Gw, Ew"); - SETFLAGS(X_ALL, SF_PENDING, NAT_FLAGS_NOFUSION); + SETFLAGS(X_ALL, SF_SET_NODF, NAT_FLAGS_NOFUSION); nextop = F8; GETSEW(x1, 0); GETSGW(x2); MULW(x2, x2, x1); - UFLAG_RES(x2); + SET_DFNONE(); + CLEAR_FLAGS(); + IFX (X_CF | X_OF) { + SLLI(x3, x2, 48); + SRAI(x3, x3, 48); // x3 = SignExtend16(result) + XOR(x3, x3, x2); + SNEZ(x3, x3); + IFX (X_CF) OR(xFlags, xFlags, x3); // F_CF == 0 + IFX (X_OF) { + SLLI(x3, x3, F_OF2); + OR(xFlags, xFlags, x3); + } + } ZEXTH(x2, x2); GWBACK; - UFLAG_DF(x1, d_imul16); + IFX (X_SF) { + SRLI(x3, x2, 15); + SLLI(x3, x3, F_SF); + OR(xFlags, xFlags, x3); + } + IFX (X_PF) emit_pf(dyn, ninst, x2, x3, x4); break; case 0xB3: INST_NAME("BTR Ew, Gw");