From 914abe64c14a528b6dd1da867ed1035f95a742e3 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Thu, 5 Dec 2019 02:02:19 +1000 Subject: [PATCH] CPU/Recompiler: Extend sign for add/sub/cmp immediates in AArch64 --- src/core/cpu_recompiler_code_generator.cpp | 1 + .../cpu_recompiler_code_generator_aarch64.cpp | 47 ++++++++++--------- src/core/cpu_recompiler_register_cache.h | 22 ++++++++- 3 files changed, 47 insertions(+), 23 deletions(-) diff --git a/src/core/cpu_recompiler_code_generator.cpp b/src/core/cpu_recompiler_code_generator.cpp index 65a16deec..2c878ffa8 100644 --- a/src/core/cpu_recompiler_code_generator.cpp +++ b/src/core/cpu_recompiler_code_generator.cpp @@ -5,6 +5,7 @@ Log_SetChannel(CPU::Recompiler); // TODO: Turn load+sext/zext into a single signed/unsigned load // TODO: mulx/shlx/etc +// TODO: when writing to the same register, don't allocate a temporary and copy it (mainly for shifts) namespace CPU::Recompiler { diff --git a/src/core/cpu_recompiler_code_generator_aarch64.cpp b/src/core/cpu_recompiler_code_generator_aarch64.cpp index 036872424..44cbb5582 100644 --- a/src/core/cpu_recompiler_code_generator_aarch64.cpp +++ b/src/core/cpu_recompiler_code_generator_aarch64.cpp @@ -359,21 +359,22 @@ void CodeGenerator::EmitAdd(HostReg to_reg, const Value& value, bool set_flags) } // do we need temporary storage for the constant, if it won't fit in an immediate? - if (a64::Assembler::IsImmAddSub(value.constant_value)) + const s64 constant_value = value.GetS64ConstantValue(); + if (a64::Assembler::IsImmAddSub(constant_value)) { if (value.size < RegSize_64) { if (set_flags) - m_emit->adds(GetHostReg32(to_reg), GetHostReg32(to_reg), s64(value.constant_value)); + m_emit->adds(GetHostReg32(to_reg), GetHostReg32(to_reg), constant_value); else - m_emit->add(GetHostReg32(to_reg), GetHostReg32(to_reg), s64(value.constant_value)); + m_emit->add(GetHostReg32(to_reg), GetHostReg32(to_reg), constant_value); } else { if (set_flags) - m_emit->adds(GetHostReg64(to_reg), GetHostReg64(to_reg), s64(value.constant_value)); + m_emit->adds(GetHostReg64(to_reg), GetHostReg64(to_reg), constant_value); else - m_emit->add(GetHostReg64(to_reg), GetHostReg64(to_reg), s64(value.constant_value)); + m_emit->add(GetHostReg64(to_reg), GetHostReg64(to_reg), constant_value); } return; @@ -382,9 +383,9 @@ void CodeGenerator::EmitAdd(HostReg to_reg, const Value& value, bool set_flags) // need a temporary Value temp_value = m_register_cache.AllocateScratch(value.size); if (value.size < RegSize_64) - m_emit->Mov(GetHostReg32(temp_value.host_reg), s64(value.constant_value)); + m_emit->Mov(GetHostReg32(temp_value.host_reg), constant_value); else - m_emit->Mov(GetHostReg64(temp_value.host_reg), s64(value.constant_value)); + m_emit->Mov(GetHostReg64(temp_value.host_reg), constant_value); EmitAdd(to_reg, temp_value, set_flags); } @@ -414,21 +415,22 @@ void CodeGenerator::EmitSub(HostReg to_reg, const Value& value, bool set_flags) } // do we need temporary storage for the constant, if it won't fit in an immediate? + const s64 constant_value = value.GetS64ConstantValue(); if (a64::Assembler::IsImmAddSub(value.constant_value)) { if (value.size < RegSize_64) { if (set_flags) - m_emit->subs(GetHostReg32(to_reg), GetHostReg32(to_reg), s64(value.constant_value)); + m_emit->subs(GetHostReg32(to_reg), GetHostReg32(to_reg), constant_value); else - m_emit->sub(GetHostReg32(to_reg), GetHostReg32(to_reg), s64(value.constant_value)); + m_emit->sub(GetHostReg32(to_reg), GetHostReg32(to_reg), constant_value); } else { if (set_flags) - m_emit->subs(GetHostReg64(to_reg), GetHostReg64(to_reg), s64(value.constant_value)); + m_emit->subs(GetHostReg64(to_reg), GetHostReg64(to_reg), constant_value); else - m_emit->sub(GetHostReg64(to_reg), GetHostReg64(to_reg), s64(value.constant_value)); + m_emit->sub(GetHostReg64(to_reg), GetHostReg64(to_reg), constant_value); } return; @@ -437,9 +439,9 @@ void CodeGenerator::EmitSub(HostReg to_reg, const Value& value, bool set_flags) // need a temporary Value temp_value = m_register_cache.AllocateScratch(value.size); if (value.size < RegSize_64) - m_emit->Mov(GetHostReg32(temp_value.host_reg), s64(value.constant_value)); + m_emit->Mov(GetHostReg32(temp_value.host_reg), constant_value); else - m_emit->Mov(GetHostReg64(temp_value.host_reg), s64(value.constant_value)); + m_emit->Mov(GetHostReg64(temp_value.host_reg), constant_value); EmitSub(to_reg, temp_value, set_flags); } @@ -459,12 +461,13 @@ void CodeGenerator::EmitCmp(HostReg to_reg, const Value& value) } // do we need temporary storage for the constant, if it won't fit in an immediate? - if (a64::Assembler::IsImmAddSub(value.constant_value)) + const s64 constant_value = value.GetS64ConstantValue(); + if (a64::Assembler::IsImmAddSub(constant_value)) { if (value.size < RegSize_64) - m_emit->cmp(GetHostReg32(to_reg), s64(value.constant_value)); + m_emit->cmp(GetHostReg32(to_reg), constant_value); else - m_emit->cmp(GetHostReg64(to_reg), s64(value.constant_value)); + m_emit->cmp(GetHostReg64(to_reg), constant_value); return; } @@ -472,9 +475,9 @@ void CodeGenerator::EmitCmp(HostReg to_reg, const Value& value) // need a temporary Value temp_value = m_register_cache.AllocateScratch(value.size); if (value.size < RegSize_64) - m_emit->Mov(GetHostReg32(temp_value.host_reg), s64(value.constant_value)); + m_emit->Mov(GetHostReg32(temp_value.host_reg), constant_value); else - m_emit->Mov(GetHostReg64(temp_value.host_reg), s64(value.constant_value)); + m_emit->Mov(GetHostReg64(temp_value.host_reg), constant_value); EmitCmp(to_reg, temp_value); } @@ -1137,7 +1140,7 @@ void CodeGenerator::EmitAddCPUStructField(u32 offset, const Value& value) { m_emit->Ldrb(GetHostReg8(temp), o_offset); if (value.IsConstant()) - m_emit->Add(GetHostReg8(temp), GetHostReg8(temp), Truncate8(value.constant_value)); + m_emit->Add(GetHostReg8(temp), GetHostReg8(temp), value.GetS64ConstantValue()); else m_emit->Add(GetHostReg8(temp), GetHostReg8(temp), GetHostReg8(value)); m_emit->Strb(GetHostReg8(temp), o_offset); @@ -1148,7 +1151,7 @@ void CodeGenerator::EmitAddCPUStructField(u32 offset, const Value& value) { m_emit->Ldrh(GetHostReg16(temp), o_offset); if (value.IsConstant()) - m_emit->Add(GetHostReg16(temp), GetHostReg16(temp), Truncate16(value.constant_value)); + m_emit->Add(GetHostReg16(temp), GetHostReg16(temp), value.GetS64ConstantValue()); else m_emit->Add(GetHostReg16(temp), GetHostReg16(temp), GetHostReg16(value)); m_emit->Strh(GetHostReg16(temp), o_offset); @@ -1159,7 +1162,7 @@ void CodeGenerator::EmitAddCPUStructField(u32 offset, const Value& value) { m_emit->Ldr(GetHostReg32(temp), o_offset); if (value.IsConstant()) - m_emit->Add(GetHostReg32(temp), GetHostReg32(temp), Truncate32(value.constant_value)); + m_emit->Add(GetHostReg32(temp), GetHostReg32(temp), value.GetS64ConstantValue()); else m_emit->Add(GetHostReg32(temp), GetHostReg32(temp), GetHostReg32(value)); m_emit->Str(GetHostReg32(temp), o_offset); @@ -1170,7 +1173,7 @@ void CodeGenerator::EmitAddCPUStructField(u32 offset, const Value& value) { m_emit->Ldr(GetHostReg64(temp), o_offset); if (value.IsConstant()) - m_emit->Add(GetHostReg64(temp), GetHostReg64(temp), value.constant_value); + m_emit->Add(GetHostReg64(temp), GetHostReg64(temp), s64(value.constant_value)); else m_emit->Add(GetHostReg64(temp), GetHostReg64(temp), GetHostReg64(value)); m_emit->Str(GetHostReg64(temp), o_offset); diff --git a/src/core/cpu_recompiler_register_cache.h b/src/core/cpu_recompiler_register_cache.h index 818681a3b..9ed86037d 100644 --- a/src/core/cpu_recompiler_register_cache.h +++ b/src/core/cpu_recompiler_register_cache.h @@ -4,8 +4,8 @@ #include "cpu_types.h" #include -#include #include +#include namespace CPU::Recompiler { @@ -137,6 +137,26 @@ struct Value return Value(); } + /// Returns the constant value as a signed 64-bit integer, suitable as an immediate. + s64 GetS64ConstantValue() const + { + switch (size) + { + case RegSize_8: + return static_cast(SignExtend64(Truncate8(constant_value))); + + case RegSize_16: + return static_cast(SignExtend64(Truncate16(constant_value))); + + case RegSize_32: + return static_cast(SignExtend64(Truncate32(constant_value))); + + case RegSize_64: + default: + return static_cast(constant_value); + } + } + static Value FromHostReg(RegisterCache* regcache, HostReg reg, RegSize size) { return Value(regcache, reg, size, ValueFlags::Valid | ValueFlags::InHostRegister);