From 93959a9d88bd88b099b8f6b2b520bcbe28b22430 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Wed, 13 Jan 2021 19:47:00 +1000 Subject: [PATCH] CPU: Fix crash with some block cycle counts on ARM Fixes crash in Capcom vs SNK Pro. --- .../cpu_recompiler_code_generator_aarch32.cpp | 35 +++++++++++++---- .../cpu_recompiler_code_generator_aarch64.cpp | 39 ++++++++++++++----- 2 files changed, 56 insertions(+), 18 deletions(-) diff --git a/src/core/cpu_recompiler_code_generator_aarch32.cpp b/src/core/cpu_recompiler_code_generator_aarch32.cpp index 7c21a2835..b8fb1802d 100644 --- a/src/core/cpu_recompiler_code_generator_aarch32.cpp +++ b/src/core/cpu_recompiler_code_generator_aarch32.cpp @@ -1077,11 +1077,30 @@ void CodeGenerator::EmitStoreCPUStructField(u32 offset, const Value& value) void CodeGenerator::EmitAddCPUStructField(u32 offset, const Value& value) { - DebugAssert(value.IsInHostRegister() || value.IsConstant()); - const s32 s_offset = static_cast(offset); const a32::MemOperand o_offset(GetCPUPtrReg(), s_offset); + Value real_value; + if (value.IsInHostRegister()) + { + real_value.SetHostReg(&m_register_cache, value.host_reg, value.size); + } + else + { + // do we need temporary storage for the constant, if it won't fit in an immediate? + Assert(value.IsConstant()); + const s32 constant_value = value.GetS32ConstantValue(); + if (a32::ImmediateA32::IsImmediateA32(static_cast(constant_value))) + { + real_value.SetHostReg(&m_register_cache, RARG2, value.size); + EmitCopyValue(real_value.host_reg, value); + } + else + { + real_value = value; + } + } + // Don't need to mask here because we're storing back to memory. switch (value.size) { @@ -1089,9 +1108,9 @@ void CodeGenerator::EmitAddCPUStructField(u32 offset, const Value& value) { m_emit->Ldrb(GetHostReg8(RARG1), o_offset); if (value.IsConstant()) - m_emit->Add(GetHostReg8(RARG1), GetHostReg8(RARG1), value.GetS32ConstantValue()); + m_emit->Add(GetHostReg8(RARG1), GetHostReg8(RARG1), real_value.GetS32ConstantValue()); else - m_emit->Add(GetHostReg8(RARG1), GetHostReg8(RARG1), GetHostReg8(value)); + m_emit->Add(GetHostReg8(RARG1), GetHostReg8(RARG1), GetHostReg8(real_value)); m_emit->Strb(GetHostReg8(RARG1), o_offset); } break; @@ -1100,9 +1119,9 @@ void CodeGenerator::EmitAddCPUStructField(u32 offset, const Value& value) { m_emit->Ldrh(GetHostReg16(RARG1), o_offset); if (value.IsConstant()) - m_emit->Add(GetHostReg16(RARG1), GetHostReg16(RARG1), value.GetS32ConstantValue()); + m_emit->Add(GetHostReg16(RARG1), GetHostReg16(RARG1), real_value.GetS32ConstantValue()); else - m_emit->Add(GetHostReg16(RARG1), GetHostReg16(RARG1), GetHostReg16(value)); + m_emit->Add(GetHostReg16(RARG1), GetHostReg16(RARG1), GetHostReg16(real_value)); m_emit->Strh(GetHostReg16(RARG1), o_offset); } break; @@ -1111,9 +1130,9 @@ void CodeGenerator::EmitAddCPUStructField(u32 offset, const Value& value) { m_emit->Ldr(GetHostReg32(RARG1), o_offset); if (value.IsConstant()) - m_emit->Add(GetHostReg32(RARG1), GetHostReg32(RARG1), value.GetS32ConstantValue()); + m_emit->Add(GetHostReg32(RARG1), GetHostReg32(RARG1), real_value.GetS32ConstantValue()); else - m_emit->Add(GetHostReg32(RARG1), GetHostReg32(RARG1), GetHostReg32(value)); + m_emit->Add(GetHostReg32(RARG1), GetHostReg32(RARG1), GetHostReg32(real_value)); m_emit->Str(GetHostReg32(RARG1), o_offset); } break; diff --git a/src/core/cpu_recompiler_code_generator_aarch64.cpp b/src/core/cpu_recompiler_code_generator_aarch64.cpp index 6efe27320..64d870907 100644 --- a/src/core/cpu_recompiler_code_generator_aarch64.cpp +++ b/src/core/cpu_recompiler_code_generator_aarch64.cpp @@ -1248,11 +1248,30 @@ void CodeGenerator::EmitStoreCPUStructField(u32 offset, const Value& value) void CodeGenerator::EmitAddCPUStructField(u32 offset, const Value& value) { - DebugAssert(value.IsInHostRegister() || value.IsConstant()); - const s64 s_offset = static_cast(ZeroExtend64(offset)); const a64::MemOperand o_offset(GetCPUPtrReg(), s_offset); + Value real_value; + if (value.IsInHostRegister()) + { + real_value.SetHostReg(&m_register_cache, value.host_reg, value.size); + } + else + { + // do we need temporary storage for the constant, if it won't fit in an immediate? + Assert(value.IsConstant()); + const s64 constant_value = value.GetS64ConstantValue(); + if (!a64::Assembler::IsImmAddSub(constant_value)) + { + real_value.SetHostReg(&m_register_cache, RARG4, value.size); + EmitCopyValue(real_value.host_reg, value); + } + else + { + real_value = value; + } + } + // Don't need to mask here because we're storing back to memory. switch (value.size) { @@ -1260,9 +1279,9 @@ void CodeGenerator::EmitAddCPUStructField(u32 offset, const Value& value) { m_emit->Ldrb(GetHostReg8(RSCRATCH), o_offset); if (value.IsConstant()) - m_emit->Add(GetHostReg8(RSCRATCH), GetHostReg8(RSCRATCH), value.GetS64ConstantValue()); + m_emit->Add(GetHostReg8(RSCRATCH), GetHostReg8(RSCRATCH), real_value.GetS64ConstantValue()); else - m_emit->Add(GetHostReg8(RSCRATCH), GetHostReg8(RSCRATCH), GetHostReg8(value)); + m_emit->Add(GetHostReg8(RSCRATCH), GetHostReg8(RSCRATCH), GetHostReg8(real_value)); m_emit->Strb(GetHostReg8(RSCRATCH), o_offset); } break; @@ -1271,9 +1290,9 @@ void CodeGenerator::EmitAddCPUStructField(u32 offset, const Value& value) { m_emit->Ldrh(GetHostReg16(RSCRATCH), o_offset); if (value.IsConstant()) - m_emit->Add(GetHostReg16(RSCRATCH), GetHostReg16(RSCRATCH), value.GetS64ConstantValue()); + m_emit->Add(GetHostReg16(RSCRATCH), GetHostReg16(RSCRATCH), real_value.GetS64ConstantValue()); else - m_emit->Add(GetHostReg16(RSCRATCH), GetHostReg16(RSCRATCH), GetHostReg16(value)); + m_emit->Add(GetHostReg16(RSCRATCH), GetHostReg16(RSCRATCH), GetHostReg16(real_value)); m_emit->Strh(GetHostReg16(RSCRATCH), o_offset); } break; @@ -1282,9 +1301,9 @@ void CodeGenerator::EmitAddCPUStructField(u32 offset, const Value& value) { m_emit->Ldr(GetHostReg32(RSCRATCH), o_offset); if (value.IsConstant()) - m_emit->Add(GetHostReg32(RSCRATCH), GetHostReg32(RSCRATCH), value.GetS64ConstantValue()); + m_emit->Add(GetHostReg32(RSCRATCH), GetHostReg32(RSCRATCH), real_value.GetS64ConstantValue()); else - m_emit->Add(GetHostReg32(RSCRATCH), GetHostReg32(RSCRATCH), GetHostReg32(value)); + m_emit->Add(GetHostReg32(RSCRATCH), GetHostReg32(RSCRATCH), GetHostReg32(real_value)); m_emit->Str(GetHostReg32(RSCRATCH), o_offset); } break; @@ -1293,9 +1312,9 @@ void CodeGenerator::EmitAddCPUStructField(u32 offset, const Value& value) { m_emit->Ldr(GetHostReg64(RSCRATCH), o_offset); if (value.IsConstant()) - m_emit->Add(GetHostReg64(RSCRATCH), GetHostReg64(RSCRATCH), s64(value.constant_value)); + m_emit->Add(GetHostReg64(RSCRATCH), GetHostReg64(RSCRATCH), s64(real_value.constant_value)); else - m_emit->Add(GetHostReg64(RSCRATCH), GetHostReg64(RSCRATCH), GetHostReg64(value)); + m_emit->Add(GetHostReg64(RSCRATCH), GetHostReg64(RSCRATCH), GetHostReg64(real_value)); m_emit->Str(GetHostReg64(RSCRATCH), o_offset); } break;