diff --git a/src/dynarec/arm64/dynarec_arm64_00.c b/src/dynarec/arm64/dynarec_arm64_00.c index ff29f0640c..958b10a15a 100644 --- a/src/dynarec/arm64/dynarec_arm64_00.c +++ b/src/dynarec/arm64/dynarec_arm64_00.c @@ -4068,8 +4068,7 @@ uintptr_t dynarec64_00(dynarec_arm_t* dyn, uintptr_t addr, uintptr_t ip, int nin SETFLAGS(X_ALL, SF_SET_PENDING); GETEB(x1, 1); u8 = F8; - MOV32w(x2, u8); - emit_test8(dyn, ninst, x1, x2, x3, x4, x5); + emit_test8c(dyn, ninst, x1, u8, x2, x4, x5); break; case 2: INST_NAME("NOT Eb"); diff --git a/src/dynarec/arm64/dynarec_arm64_emit_tests.c b/src/dynarec/arm64/dynarec_arm64_emit_tests.c index 73c467e42b..60cda00ff2 100644 --- a/src/dynarec/arm64/dynarec_arm64_emit_tests.c +++ b/src/dynarec/arm64/dynarec_arm64_emit_tests.c @@ -80,9 +80,8 @@ void emit_cmp32(dynarec_arm_t* dyn, int ninst, rex_t rex, int s1, int s2, int s3 void emit_cmp32_0(dynarec_arm_t* dyn, int ninst, rex_t rex, int s1, int s3, int s4) { IFX_PENDOR0 { - MOV64xw(s4, 0); STRxw_U12(s1, xEmu, offsetof(x64emu_t, op1)); - STRxw_U12(s4, xEmu, offsetof(x64emu_t, op2)); + STRxw_U12(xZR, xEmu, offsetof(x64emu_t, op2)); STRxw_U12(s1, xEmu, offsetof(x64emu_t, res)); SET_DF(s4, rex.w?d_cmp64:d_cmp32); } else { @@ -198,9 +197,8 @@ void emit_cmp16(dynarec_arm_t* dyn, int ninst, int s1, int s2, int s3, int s4, i void emit_cmp16_0(dynarec_arm_t* dyn, int ninst, int s1, int s3, int s4) { IFX_PENDOR0 { - MOV32w(s3, 0); STRH_U12(s1, xEmu, offsetof(x64emu_t, op1)); - STRH_U12(s3, xEmu, offsetof(x64emu_t, op2)); + STRH_U12(wZR, xEmu, offsetof(x64emu_t, op2)); STRH_U12(s1, xEmu, offsetof(x64emu_t, res)); SET_DF(s3, d_cmp16); } else { @@ -291,8 +289,7 @@ void emit_cmp8_0(dynarec_arm_t* dyn, int ninst, int s1, int s3, int s4) { IFX_PENDOR0 { STRB_U12(s1, xEmu, offsetof(x64emu_t, op1)); - MOV32w(s4, 0); - STRB_U12(s4, xEmu, offsetof(x64emu_t, op2)); + STRB_U12(wZR, xEmu, offsetof(x64emu_t, op2)); STRB_U12(s1, xEmu, offsetof(x64emu_t, res)); SET_DF(s3, d_cmp8); } else { @@ -410,6 +407,19 @@ void emit_test16(dynarec_arm_t* dyn, int ninst, int s1, int s2, int s3, int s4, } else { SET_DFNONE(); } + if((dyn->insts[ninst].x64.gen_flags&(X_ZF|X_PEND)) && !(dyn->insts[ninst].x64.gen_flags&~(X_ZF|X_PEND))) { + ANDSw_REG(s5, s1, s2); + IFX(X_PEND) { + STRH_U12(s5, xEmu, offsetof(x64emu_t, res)); + } + IFX(X_ZF) { + IFNATIVE(NF_EQ) {} else { + CSETw(s4, cEQ); + BFIw(xFlags, s4, F_ZF, 1); + } + } + return; + } IFX(X_CF|X_ZF|X_SF|X_OF) { LSLw(s5, s1, 16); ANDSw_REG_LSL(s5, s5, s2, 16); @@ -458,6 +468,22 @@ void emit_test16c(dynarec_arm_t* dyn, int ninst, int s1, uint32_t c, int s3, int } else { SET_DFNONE(); } + if((dyn->insts[ninst].x64.gen_flags&(X_ZF|X_PEND)) && !(dyn->insts[ninst].x64.gen_flags&~(X_ZF|X_PEND))) { + mask = convert_bitmask_w(c); + if(mask) { + ANDSw_mask(s5, s1, mask&0x3F, (mask>>6)&0x3F); + IFX(X_PEND) { + STRH_U12(s5, xEmu, offsetof(x64emu_t, res)); + } + IFX(X_ZF) { + IFNATIVE(NF_EQ) {} else { + CSETw(s4, cEQ); + BFIw(xFlags, s4, F_ZF, 1); + } + } + return; + } + } IFX(X_CF|X_ZF|X_SF|X_OF) { LSLw(s5, s1, 16); mask = convert_bitmask_w(c<<16); @@ -565,6 +591,22 @@ void emit_test8c(dynarec_arm_t* dyn, int ninst, int s1, uint32_t c, int s3, int } else { SET_DFNONE(); } + if((dyn->insts[ninst].x64.gen_flags&(X_ZF|X_PEND)) && !(dyn->insts[ninst].x64.gen_flags&~(X_ZF|X_PEND))) { + mask = convert_bitmask_w(c); + if(mask) { + ANDSw_mask(s5, s1, mask&0x3F, (mask>>6)&0x3F); + IFX(X_PEND) { + STRB_U12(s5, xEmu, offsetof(x64emu_t, res)); + } + IFX(X_ZF) { + IFNATIVE(NF_EQ) {} else { + CSETw(s4, cEQ); + BFIw(xFlags, s4, F_ZF, 1); + } + } + return; + } + } IFX(X_CF|X_ZF|X_SF|X_OF) { LSLw(s5, s1, 24); mask = convert_bitmask_w(c<<24); diff --git a/src/dynarec/arm64/dynarec_arm64_functions.c b/src/dynarec/arm64/dynarec_arm64_functions.c index 91d6c7229a..2439a2d495 100644 --- a/src/dynarec/arm64/dynarec_arm64_functions.c +++ b/src/dynarec/arm64/dynarec_arm64_functions.c @@ -1167,6 +1167,34 @@ void updateNativeFlags(dynarec_native_t* dyn) if(flag2native(dyn->insts[ninst].x64.gen_flags,(dyn->insts[ninst].set_nat_flags&NF_PF_V)?1:0) && (dyn->insts[ninst].nat_flags_op==NAT_FLAG_OP_TOUCH)) { propagateNativeFlags(dyn, ninst); } + // BOX64_DYNAREC_LOOPFLAGS: a flag producer that ends a block has X_PEND forced + // onto it so the deferred op1/op2/res spill keeps full EFLAGS reconstructable for + // any successor or signal. When the producer's whole real flag need + // (gen_flags & X_ALL) is ZF/SF/CF/OF and reaches its downstream consumers purely + // through native NZCV (e.g. a loop-control CMP feeding a native Jcc), that spill is + // redundant for the branch. Clearing X_PEND makes emit_cmp/emit_test take the + // SET_DFNONE path. ZF/SF/CF/OF stay reconstructable at faults through adjust_arch's + // NZCV overlay (kept in need_nat_flags); only PF/AF turn approximate across the + // block exit, which the dynarec already is for native flags and which the x86 ABI + // (arithmetic flags not preserved across calls) makes safe across the exit. + if(BOX64ENV(dynarec_loopflags)) + for(int ninst=0; ninstsize; ++ninst) { + uint8_t gf = dyn->insts[ninst].x64.gen_flags; + if(!(gf&X_PEND) || dyn->insts[ninst].nat_flags_op!=NAT_FLAG_OP_TOUCH) + continue; + uint8_t xflags = gf & X_ALL; + if(xflags & (X_AF|X_PF)) // native NZCV cannot carry AF/PF at a fault + continue; + int vf_is_p = (dyn->insts[ninst].set_nat_flags&NF_PF_V)?1:0; + uint8_t need_nat = flag2native(xflags, vf_is_p); + if(!need_nat || (dyn->insts[ninst].set_nat_flags & need_nat) != need_nat) + continue; // producer must emit exactly the native flags needed + if(getNativeFlagsUsed(dyn, ninst, need_nat, vf_is_p) != need_nat) + continue; // every real consumer takes them natively, nothing else + // keep all NZCV flags this op sets alive so adjust_arch reconstructs ZF/SF/CF/OF + dyn->insts[ninst].need_nat_flags |= dyn->insts[ninst].set_nat_flags & (NF_EQ|NF_SF|NF_VF|NF_CF); + dyn->insts[ninst].x64.gen_flags = gf & ~X_PEND; + } } void rasNativeState(dynarec_arm_t* dyn, int ninst) diff --git a/src/include/env.h b/src/include/env.h index f2991d7b3d..6487db5fb2 100644 --- a/src/include/env.h +++ b/src/include/env.h @@ -63,6 +63,7 @@ extern char* ftrace_name; INTEGER(BOX64_DYNAREC_FORWARD, dynarec_forward, 128, 0, 1024, 1, 3) \ STRING(BOX64_DYNAREC_GDBJIT, dynarec_gdbjit_str, 0, 0) \ INTEGER(BOX64_DYNAREC_LOG, dynarec_log, 0, 0, 3, 1, 0) \ + INTEGER(BOX64_DYNAREC_LOOPFLAGS, dynarec_loopflags, 0, 0, 1, 1, 2) \ INTEGER(BOX64_DYNAREC_MISSING, dynarec_missing, 0, 0, 2, 1, 0) \ BOOLEAN(BOX64_DYNAREC_NATIVEFLAGS, dynarec_nativeflags, 1, 1, 1) \ STRING(BOX64_DYNAREC_NOHOSTEXT, dynarec_nohostext, 0, 0) \