diff --git a/src/dynarmic/backend/A64/a32_emit_a64.cpp b/src/dynarmic/backend/A64/a32_emit_a64.cpp index ba684ce3..f0130d87 100644 --- a/src/dynarmic/backend/A64/a32_emit_a64.cpp +++ b/src/dynarmic/backend/A64/a32_emit_a64.cpp @@ -504,7 +504,49 @@ void A32EmitA64::EmitA32SetCpsrNZCVQ(A32EmitContext& ctx, IR::Inst* inst) { code._MSR(FIELD_FPSR, host_fpsr); } +void A32EmitA64::EmitA32SetCpsrNZ(A32EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + const ARM64Reg nz = EncodeRegTo32(ctx.reg_alloc.UseGpr(args[0])); + const ARM64Reg nzcv = EncodeRegTo32(ctx.reg_alloc.ScratchGpr()); + + code.LDR(INDEX_UNSIGNED, nzcv, X28, offsetof(A32JitState, cpsr_nzcv)); + code.ANDI2R(nzcv, nzcv, 0x3000'0000); + code.ORR(nzcv, nzcv, nz); + code.STR(INDEX_UNSIGNED, nzcv, X28, offsetof(A32JitState, cpsr_nzcv)); +} + +void A32EmitA64::EmitA32SetCpsrNZC(A32EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + const ARM64Reg nzcv = EncodeRegTo32(ctx.reg_alloc.ScratchGpr()); + code.LDR(INDEX_UNSIGNED, nzcv, X28, offsetof(A32JitState, cpsr_nzcv)); + code.ANDI2R(nzcv, nzcv, 0x1000'0000); + if (args[0].IsImmediate()) { + if (args[1].IsImmediate()) { + const u32 c = (args[1].GetImmediateU32() << 29); + if (c) { + code.ORRI2R(nzcv, nzcv, c); + } + } else { + const ARM64Reg c = EncodeRegTo32(ctx.reg_alloc.UseGpr(args[1])); + code.ORR(nzcv, nzcv, c, {nzcv, ST_LSL, 29}); + } + } else { + const ARM64Reg nz = EncodeRegTo32(ctx.reg_alloc.UseScratchGpr(args[0])); + if (args[1].IsImmediate()) { + const u32 c = args[1].GetImmediateU32() << 29; + code.ORR(nzcv, nzcv, nz); + if (c) { + code.ORRI2R(nzcv, nzcv, c); + } + } else { + const ARM64Reg c = EncodeRegTo32(ctx.reg_alloc.UseGpr(args[1])); + code.ORR(nzcv, nzcv, nz); + code.ORR(nzcv, nzcv, c, {nzcv, ST_LSL, 29}); + } + } + code.STR(INDEX_UNSIGNED, nzcv, X28, offsetof(A32JitState, cpsr_nzcv)); +} void A32EmitA64::EmitA32SetCheckBit(A32EmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst); @@ -1409,8 +1451,8 @@ void A32EmitA64::EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDesc PushRSBHelper(X1, X2, terminal.next); code.ForceReturnFromRunCode(); - //Todo: find a better/generic place to FlushIcache when switching between - // far code and near code + // Todo: find a better/generic place to FlushIcache when switching between + // far code and near code code.FlushIcache(); code.SwitchToNearCode(); } @@ -1551,7 +1593,9 @@ void A32EmitA64::Unpatch(const IR::LocationDescriptor& location) { EmitA64::Unpatch(location); if (config.HasOptimization(OptimizationFlag::FastDispatch)) { code.DisableWriting(); - SCOPE_EXIT { code.EnableWriting(); }; + SCOPE_EXIT { + code.EnableWriting(); + }; (*fast_dispatch_table_lookup)(location.Value()) = {}; } diff --git a/src/dynarmic/backend/A64/a32_interface.cpp b/src/dynarmic/backend/A64/a32_interface.cpp index 6a26377e..52d16f4b 100644 --- a/src/dynarmic/backend/A64/a32_interface.cpp +++ b/src/dynarmic/backend/A64/a32_interface.cpp @@ -151,7 +151,7 @@ private: IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, config.callbacks, {config.arch_version, config.define_unpredictable_behaviour, config.hook_hint_instructions}); Optimization::PolyfillPass(ir_block, polyfill_options); if (config.HasOptimization(OptimizationFlag::GetSetElimination)) { - Optimization::A32GetSetElimination(ir_block); + Optimization::A32GetSetElimination(ir_block, {.convert_nz_to_nzc = true}); Optimization::DeadCodeElimination(ir_block); } if (config.HasOptimization(OptimizationFlag::ConstProp)) { diff --git a/src/dynarmic/backend/A64/emit_a64.cpp b/src/dynarmic/backend/A64/emit_a64.cpp index b017f9ff..d17658b2 100644 --- a/src/dynarmic/backend/A64/emit_a64.cpp +++ b/src/dynarmic/backend/A64/emit_a64.cpp @@ -116,16 +116,80 @@ void EmitA64::EmitGetLowerFromOp(EmitContext&, IR::Inst*) { ASSERT_FALSE("should never happen"); } +void EmitA64::EmitGetNZFromOp(EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + + const auto sign_extend = [&](ARM64Reg value) { + switch (args[0].GetType()) { + case IR::Type::U8: + code.SXTB(Arm64Gen::EncodeRegTo64(value), Arm64Gen::EncodeRegTo32(value)); + break; + case IR::Type::U16: + code.SXTH(Arm64Gen::EncodeRegTo64(value), Arm64Gen::EncodeRegTo32(value)); + break; + case IR::Type::U32: + code.SXTW(Arm64Gen::EncodeRegTo64(value), Arm64Gen::EncodeRegTo32(value)); + break; + case IR::Type::U64: + break; + default: + UNREACHABLE(); + } + }; + + const Arm64Gen::ARM64Reg nz = ctx.reg_alloc.ScratchGpr(); + const Arm64Gen::ARM64Reg value = ctx.reg_alloc.UseGpr(args[0]); + sign_extend(value); + code.CMP(value, ZR); + code.MRS(nz, FIELD_NZCV); + code.ANDI2R(nz, nz, 0xC0000000); + ctx.reg_alloc.DefineValue(inst, nz); +} + void EmitA64::EmitGetNZCVFromOp(EmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst); + const auto sign_extend = [&](ARM64Reg value) { + switch (args[0].GetType()) { + case IR::Type::U8: + code.SXTB(Arm64Gen::EncodeRegTo64(value), Arm64Gen::EncodeRegTo32(value)); + break; + case IR::Type::U16: + code.SXTH(Arm64Gen::EncodeRegTo64(value), Arm64Gen::EncodeRegTo32(value)); + break; + case IR::Type::U32: + code.SXTW(Arm64Gen::EncodeRegTo64(value), Arm64Gen::EncodeRegTo32(value)); + break; + case IR::Type::U64: + return; + default: + UNREACHABLE(); + } + }; + Arm64Gen::ARM64Reg nzcv = ctx.reg_alloc.ScratchGpr(); Arm64Gen::ARM64Reg value = ctx.reg_alloc.UseGpr(args[0]); + sign_extend(value); code.CMP(value, ZR); code.MRS(nzcv, FIELD_NZCV); ctx.reg_alloc.DefineValue(inst, nzcv); } +void EmitA64::EmitGetCFlagFromNZCV(EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + + if (args[0].IsImmediate()) { + const ARM64Reg result = EncodeRegTo32(ctx.reg_alloc.ScratchGpr()); + const u32 value = (args[0].GetImmediateU32() >> 29) & 1; + code.MOVI2R(result, value); + ctx.reg_alloc.DefineValue(inst, result); + } else { + const ARM64Reg result = EncodeRegTo32(ctx.reg_alloc.UseScratchGpr(args[0])); + code.UBFX(result, result, 29, 1); + ctx.reg_alloc.DefineValue(inst, result); + } +} + void EmitA64::EmitNZCVFromPackedFlags(EmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst); diff --git a/src/dynarmic/backend/A64/opcodes.inc b/src/dynarmic/backend/A64/opcodes.inc index 550d557c..ccb1f246 100644 --- a/src/dynarmic/backend/A64/opcodes.inc +++ b/src/dynarmic/backend/A64/opcodes.inc @@ -20,6 +20,8 @@ A32OPC(SetExtendedRegister64, Void, A32E A32OPC(GetCpsr, U32, ) A32OPC(SetCpsr, Void, U32 ) A32OPC(SetCpsrNZCV, Void, NZCV ) +A32OPC(SetCpsrNZ, Void, NZCV ) +A32OPC(SetCpsrNZC, Void, NZCV, U1 ) A32OPC(SetCpsrNZCVRaw, Void, U32 ) A32OPC(SetCpsrNZCVQ, Void, U32 ) A32OPC(GetCFlag, U1, ) @@ -86,9 +88,10 @@ OPCODE(GetCarryFromOp, U1, Opaq OPCODE(GetOverflowFromOp, U1, Opaque ) OPCODE(GetGEFromOp, U32, Opaque ) OPCODE(GetNZCVFromOp, NZCV, Opaque ) +OPCODE(GetNZFromOp, NZCV, Opaque ) OPCODE(GetUpperFromOp, U128, Opaque ) OPCODE(GetLowerFromOp, U128, Opaque ) - +OPCODE(GetCFlagFromNZCV, U1, NZCV ) OPCODE(NZCVFromPackedFlags, NZCV, U32 ) // Calculations