diff --git a/Source/Core/Common/Arm64Emitter.h b/Source/Core/Common/Arm64Emitter.h index a7c65e413706..78052def11f3 100644 --- a/Source/Core/Common/Arm64Emitter.h +++ b/Source/Core/Common/Arm64Emitter.h @@ -597,6 +597,29 @@ class ARM64XEmitter // ABI related void ABI_PushRegisters(BitSet32 registers); void ABI_PopRegisters(BitSet32 registers, BitSet32 ignore_mask = BitSet32(0)); + + // Utility to generate a call to a std::function object. + // + // Unfortunately, calling operator() directly is undefined behavior in C++ + // (this method might be a thunk in the case of multi-inheritance) so we + // have to go through a trampoline function. + template + static void CallLambdaTrampoline(const std::function* f, + Args... args) + { + (*f)(args...); + } + + // This function expects you to have set up the state. + // Overwrites X0 and X30 + template + ARM64Reg ABI_SetupLambda(const std::function* f) + { + auto trampoline = &ARM64XEmitter::CallLambdaTrampoline; + MOVI2R(X30, (u64)trampoline); + MOVI2R(X0, (u64)const_cast((const void*)f)); + return X30; + } }; class ARM64FloatEmitter diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index c03e436d56ed..f5a9bb2e92dc 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -237,6 +237,7 @@ elseif(_M_ARM_64) PowerPC/JitArm64/JitArm64_Paired.cpp PowerPC/JitArm64/JitArm64_LoadStorePaired.cpp PowerPC/JitArm64/JitArm64_SystemRegisters.cpp + PowerPC/JitArm64/Jit_Util.cpp PowerPC/JitArm64/JitArm64_Tables.cpp) endif() diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_BackPatch.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_BackPatch.cpp index 7c8552ac79d8..7075a3a81be2 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitArm64_BackPatch.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_BackPatch.cpp @@ -180,10 +180,7 @@ u32 JitArm64::EmitBackpatchRoutine(ARM64XEmitter* emit, u32 flags, bool fastmem, else if (flags & BackPatchInfo::FLAG_SIZE_16) emit->STRH(INDEX_UNSIGNED, temp, addr, 0); else - { emit->STRB(INDEX_UNSIGNED, RS, addr, 0); - emit->HINT(HINT_NOP); - } } else { diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStore.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStore.cpp index ad06be4ccd5e..51b4f32fc561 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStore.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStore.cpp @@ -7,9 +7,12 @@ #include "Core/Core.h" #include "Core/CoreTiming.h" +#include "Core/HW/MMIO.h" + #include "Core/PowerPC/PowerPC.h" #include "Core/PowerPC/PPCTables.h" #include "Core/PowerPC/JitArm64/Jit.h" +#include "Core/PowerPC/JitArm64/Jit_Util.h" #include "Core/PowerPC/JitArm64/JitArm64_RegCache.h" #include "Core/PowerPC/JitArm64/JitAsm.h" @@ -42,10 +45,9 @@ void JitArm64::SafeLoadToReg(u32 dest, s32 addr, s32 offsetReg, u32 flags, s32 o BitSet32 regs_in_use = gpr.GetCallerSavedUsed(); BitSet32 fprs_in_use = fpr.GetCallerSavedUsed(); - BitSet32 ignore_mask(0); regs_in_use[W0] = 0; regs_in_use[W30] = 0; - ignore_mask[dest_reg] = 1; + regs_in_use[dest_reg] = 0; ARM64Reg addr_reg = W0; u32 imm_addr = 0; @@ -149,6 +151,12 @@ void JitArm64::SafeLoadToReg(u32 dest, s32 addr, s32 offsetReg, u32 flags, s32 o { EmitBackpatchRoutine(this, flags, true, false, dest_reg, XA); } + else if (is_immediate && MMIO::IsMMIOAddress(imm_addr)) + { + MMIOLoadToReg(Memory::mmio_mapping, this, + regs_in_use, fprs_in_use, dest_reg, + imm_addr, flags); + } else { // Has a chance of being backpatched which will destroy our state @@ -160,7 +168,7 @@ void JitArm64::SafeLoadToReg(u32 dest, s32 addr, s32 offsetReg, u32 flags, s32 o SConfig::GetInstance().m_LocalCoreStartupParameter.bFastmem, dest_reg, XA); m_float_emit.ABI_PopRegisters(fprs_in_use); - ABI_PopRegisters(regs_in_use, ignore_mask); + ABI_PopRegisters(regs_in_use); } gpr.Unlock(W0, W30); @@ -280,15 +288,24 @@ void JitArm64::SafeStoreFromReg(s32 dest, u32 value, s32 regOffset, u32 flags, s ARM64Reg XA = EncodeRegTo64(addr_reg); - if (is_immediate) - MOVI2R(XA, imm_addr); - if (is_immediate && Memory::IsRAMAddress(imm_addr)) { + MOVI2R(XA, imm_addr); + EmitBackpatchRoutine(this, flags, true, false, RS, XA); } + else if (is_immediate && MMIO::IsMMIOAddress(imm_addr) && + !(flags & BackPatchInfo::FLAG_REVERSE)) + { + MMIOWriteRegToAddr(Memory::mmio_mapping, this, + regs_in_use, fprs_in_use, RS, + imm_addr, flags); + } else { + if (is_immediate) + MOVI2R(XA, imm_addr); + // Has a chance of being backpatched which will destroy our state // push and pop everything in this instance ABI_PushRegisters(regs_in_use); diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStoreFloating.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStoreFloating.cpp index e8cb2502bbc4..392854b7f5a8 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStoreFloating.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStoreFloating.cpp @@ -176,11 +176,10 @@ void JitArm64::lfXX(UGeckoInstruction inst) BitSet32 regs_in_use = gpr.GetCallerSavedUsed(); BitSet32 fprs_in_use = fpr.GetCallerSavedUsed(); - BitSet32 fpr_ignore_mask(0); regs_in_use[W0] = 0; regs_in_use[W30] = 0; fprs_in_use[0] = 0; // Q0 - fpr_ignore_mask[VD - Q0] = 1; + fprs_in_use[VD - Q0] = 0; if (is_immediate && Memory::IsRAMAddress(imm_addr)) { @@ -196,7 +195,7 @@ void JitArm64::lfXX(UGeckoInstruction inst) SConfig::GetInstance().m_LocalCoreStartupParameter.bFastmem, SConfig::GetInstance().m_LocalCoreStartupParameter.bFastmem, VD, XA); - m_float_emit.ABI_PopRegisters(fprs_in_use, fpr_ignore_mask); + m_float_emit.ABI_PopRegisters(fprs_in_use); ABI_PopRegisters(regs_in_use); } diff --git a/Source/Core/Core/PowerPC/JitArm64/JitAsm.cpp b/Source/Core/Core/PowerPC/JitArm64/JitAsm.cpp index 45d81f0d8601..f1f4510d1e71 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitAsm.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/JitAsm.cpp @@ -273,8 +273,7 @@ void JitArm64AsmRoutineManager::GenerateCommon() float_emit.REV32(8, D0, D0); MOVK(addr_reg, ((u64)Memory::base >> 32) & 0xFFFF, SHIFT_32); - float_emit.ST1(32, Q0, 0, addr_reg, SP); - float_emit.ST1(32, Q0, 1, addr_reg, SP); + float_emit.ST1(64, Q0, 0, addr_reg, SP); RET(X30); SetJumpTarget(argh); @@ -304,9 +303,9 @@ void JitArm64AsmRoutineManager::GenerateCommon() TST(DecodeReg(addr_reg), 6, 1); FixupBranch argh = B(CC_NEQ); + MOVK(addr_reg, ((u64)Memory::base >> 32) & 0xFFFF, SHIFT_32); - float_emit.ST1(8, Q0, 0, addr_reg, SP); - float_emit.ST1(8, Q0, 1, addr_reg, SP); + float_emit.ST1(16, Q0, 0, addr_reg, SP); RET(X30); SetJumpTarget(argh); @@ -335,9 +334,9 @@ void JitArm64AsmRoutineManager::GenerateCommon() TST(DecodeReg(addr_reg), 6, 1); FixupBranch argh = B(CC_NEQ); + MOVK(addr_reg, ((u64)Memory::base >> 32) & 0xFFFF, SHIFT_32); - float_emit.ST1(8, Q0, 0, addr_reg, SP); - float_emit.ST1(8, Q0, 1, addr_reg, SP); + float_emit.ST1(16, Q0, 0, addr_reg, SP); RET(X30); SetJumpTarget(argh); @@ -368,8 +367,7 @@ void JitArm64AsmRoutineManager::GenerateCommon() TST(DecodeReg(addr_reg), 6, 1); FixupBranch argh = B(CC_NEQ); MOVK(addr_reg, ((u64)Memory::base >> 32) & 0xFFFF, SHIFT_32); - float_emit.ST1(16, Q0, 0, addr_reg, SP); - float_emit.ST1(16, Q0, 1, addr_reg, SP); + float_emit.ST1(32, Q0, 0, addr_reg, SP); RET(X30); SetJumpTarget(argh); @@ -399,8 +397,7 @@ void JitArm64AsmRoutineManager::GenerateCommon() TST(DecodeReg(addr_reg), 6, 1); FixupBranch argh = B(CC_NEQ); MOVK(addr_reg, ((u64)Memory::base >> 32) & 0xFFFF, SHIFT_32); - float_emit.ST1(16, Q0, 0, addr_reg, SP); - float_emit.ST1(16, Q0, 1, addr_reg, SP); + float_emit.ST1(32, Q0, 0, addr_reg, SP); RET(X30); SetJumpTarget(argh); diff --git a/Source/Core/Core/PowerPC/JitArm64/Jit_Util.cpp b/Source/Core/Core/PowerPC/JitArm64/Jit_Util.cpp new file mode 100644 index 000000000000..6c35cc06494d --- /dev/null +++ b/Source/Core/Core/PowerPC/JitArm64/Jit_Util.cpp @@ -0,0 +1,248 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "Common/Arm64Emitter.h" +#include "Common/Common.h" + +#include "Core/HW/MMIO.h" + +#include "Core/PowerPC/JitArm64/Jit.h" +#include "Core/PowerPC/JitArm64/Jit_Util.h" +template +class MMIOWriteCodeGenerator : public MMIO::WriteHandlingMethodVisitor +{ +public: + MMIOWriteCodeGenerator(ARM64XEmitter* emit, BitSet32 gprs_in_use, BitSet32 fprs_in_use, + ARM64Reg src_reg, u32 address) + : m_emit(emit), m_gprs_in_use(gprs_in_use), m_fprs_in_use(fprs_in_use), + m_src_reg(src_reg), m_address(address) + { + } + + virtual void VisitNop() + { + // Do nothing + } + virtual void VisitDirect(T* addr, u32 mask) + { + WriteRegToAddr(8 * sizeof (T), addr, mask); + } + virtual void VisitComplex(const std::function* lambda) + { + CallLambda(8 * sizeof (T), lambda); + } + +private: + + void StoreFromRegister(int sbits, ARM64Reg reg) + { + switch (sbits) + { + case 8: + m_emit->STRB(INDEX_UNSIGNED, reg, X0, 0); + break; + case 16: + m_emit->STRH(INDEX_UNSIGNED, reg, X0, 0); + break; + case 32: + m_emit->STR(INDEX_UNSIGNED, reg, X0, 0); + break; + default: + _assert_msg_(DYNA_REC, false, "Unknown size %d passed to MMIOWriteCodeGenerator!", sbits); + break; + } + } + + void WriteRegToAddr(int sbits, const void* ptr, u32 mask) + { + m_emit->MOVI2R(X0, (u64)ptr); + + // If we do not need to mask, we can do the sign extend while loading + // from memory. If masking is required, we have to first zero extend, + // then mask, then sign extend if needed (1 instr vs. ~4). + u32 all_ones = (1ULL << sbits) - 1; + if ((all_ones & mask) == all_ones) + { + StoreFromRegister(sbits, m_src_reg); + } + else + { + m_emit->MOVI2R(W1, mask); + m_emit->AND(W1, m_src_reg, W1, ArithOption(W1, ST_LSL, 0)); + StoreFromRegister(sbits, W1); + } + } + + void CallLambda(int sbits, const std::function* lambda) + { + ARM64FloatEmitter float_emit(m_emit); + + m_emit->ABI_PushRegisters(m_gprs_in_use); + float_emit.ABI_PushRegisters(m_fprs_in_use); + m_emit->MOVI2R(W1, m_address); + m_emit->MOV(W2, m_src_reg); + m_emit->BLR(m_emit->ABI_SetupLambda(lambda)); + float_emit.ABI_PopRegisters(m_fprs_in_use); + m_emit->ABI_PopRegisters(m_gprs_in_use); + } + + ARM64XEmitter* m_emit; + BitSet32 m_gprs_in_use; + BitSet32 m_fprs_in_use; + ARM64Reg m_src_reg; + u32 m_address; +}; +// Visitor that generates code to read a MMIO value. +template +class MMIOReadCodeGenerator : public MMIO::ReadHandlingMethodVisitor +{ +public: + MMIOReadCodeGenerator(ARM64XEmitter* emit, BitSet32 gprs_in_use, BitSet32 fprs_in_use, + ARM64Reg dst_reg, u32 address, bool sign_extend) + : m_emit(emit), m_gprs_in_use(gprs_in_use), m_fprs_in_use(fprs_in_use), + m_dst_reg(dst_reg), m_address(address), m_sign_extend(sign_extend) + { + } + + virtual void VisitConstant(T value) + { + LoadConstantToReg(8 * sizeof (T), value); + } + virtual void VisitDirect(const T* addr, u32 mask) + { + LoadAddrMaskToReg(8 * sizeof (T), addr, mask); + } + virtual void VisitComplex(const std::function* lambda) + { + CallLambda(8 * sizeof (T), lambda); + } + +private: + + void LoadConstantToReg(int sbits, u32 value) + { + m_emit->MOVI2R(m_dst_reg, value); + if (m_sign_extend) + m_emit->SBFM(m_dst_reg, m_dst_reg, 0, sbits - 1); + } + + void LoadToRegister(int sbits, bool dont_extend) + { + switch (sbits) + { + case 8: + if (m_sign_extend && !dont_extend) + m_emit->LDRSB(INDEX_UNSIGNED, m_dst_reg, X0, 0); + else + m_emit->LDRB(INDEX_UNSIGNED, m_dst_reg, X0, 0); + break; + case 16: + if (m_sign_extend && !dont_extend) + m_emit->LDRSH(INDEX_UNSIGNED, m_dst_reg, X0, 0); + else + m_emit->LDRH(INDEX_UNSIGNED, m_dst_reg, X0, 0); + break; + case 32: + m_emit->LDR(INDEX_UNSIGNED, m_dst_reg, X0, 0); + break; + default: + _assert_msg_(DYNA_REC, false, "Unknown size %d passed to MMIOReadCodeGenerator!", sbits); + break; + } + } + + void LoadAddrMaskToReg(int sbits, const void* ptr, u32 mask) + { + m_emit->MOVI2R(X0, (u64)ptr); + + // If we do not need to mask, we can do the sign extend while loading + // from memory. If masking is required, we have to first zero extend, + // then mask, then sign extend if needed (1 instr vs. ~4). + u32 all_ones = (1ULL << sbits) - 1; + if ((all_ones & mask) == all_ones) + { + LoadToRegister(sbits, false); + } + else + { + LoadToRegister(sbits, true); + m_emit->MOVI2R(W0, mask); + m_emit->AND(m_dst_reg, m_dst_reg, W0, ArithOption(W0, ST_LSL, 0)); + if (m_sign_extend) + m_emit->SBFM(m_dst_reg, m_dst_reg, 0, sbits - 1); + } + } + + void CallLambda(int sbits, const std::function* lambda) + { + ARM64FloatEmitter float_emit(m_emit); + + m_emit->ABI_PushRegisters(m_gprs_in_use); + float_emit.ABI_PushRegisters(m_fprs_in_use); + m_emit->MOVI2R(W1, m_address); + m_emit->BLR(m_emit->ABI_SetupLambda(lambda)); + float_emit.ABI_PopRegisters(m_fprs_in_use); + m_emit->ABI_PopRegisters(m_gprs_in_use); + + if (m_sign_extend) + m_emit->SBFM(m_dst_reg, W0, 0, sbits - 1); + else + m_emit->UBFM(m_dst_reg, W0, 0, sbits - 1); + } + + ARM64XEmitter* m_emit; + BitSet32 m_gprs_in_use; + BitSet32 m_fprs_in_use; + ARM64Reg m_dst_reg; + u32 m_address; + bool m_sign_extend; +}; + +void MMIOLoadToReg(MMIO::Mapping* mmio, Arm64Gen::ARM64XEmitter* emit, + BitSet32 gprs_in_use, BitSet32 fprs_in_use, + ARM64Reg dst_reg, u32 address, u32 flags) +{ + if (flags & BackPatchInfo::FLAG_SIZE_8) + { + MMIOReadCodeGenerator gen(emit, gprs_in_use, fprs_in_use, dst_reg, + address, flags & BackPatchInfo::FLAG_EXTEND); + mmio->GetHandlerForRead(address).Visit(gen); + } + else if (flags & BackPatchInfo::FLAG_SIZE_16) + { + MMIOReadCodeGenerator gen(emit, gprs_in_use, fprs_in_use, dst_reg, + address, flags & BackPatchInfo::FLAG_EXTEND); + mmio->GetHandlerForRead(address).Visit(gen); + } + else if (flags & BackPatchInfo::FLAG_SIZE_32) + { + MMIOReadCodeGenerator gen(emit, gprs_in_use, fprs_in_use, dst_reg, + address, flags & BackPatchInfo::FLAG_EXTEND); + mmio->GetHandlerForRead(address).Visit(gen); + } +} + +void MMIOWriteRegToAddr(MMIO::Mapping* mmio, Arm64Gen::ARM64XEmitter* emit, + BitSet32 gprs_in_use, BitSet32 fprs_in_use, + ARM64Reg src_reg, u32 address, u32 flags) +{ + if (flags & BackPatchInfo::FLAG_SIZE_8) + { + MMIOWriteCodeGenerator gen(emit, gprs_in_use, fprs_in_use, src_reg, + address); + mmio->GetHandlerForWrite(address).Visit(gen); + } + else if (flags & BackPatchInfo::FLAG_SIZE_16) + { + MMIOWriteCodeGenerator gen(emit, gprs_in_use, fprs_in_use, src_reg, + address); + mmio->GetHandlerForWrite(address).Visit(gen); + } + else if (flags & BackPatchInfo::FLAG_SIZE_32) + { + MMIOWriteCodeGenerator gen(emit, gprs_in_use, fprs_in_use, src_reg, + address); + mmio->GetHandlerForWrite(address).Visit(gen); + } +} diff --git a/Source/Core/Core/PowerPC/JitArm64/Jit_Util.h b/Source/Core/Core/PowerPC/JitArm64/Jit_Util.h new file mode 100644 index 000000000000..6d5ab1d87a02 --- /dev/null +++ b/Source/Core/Core/PowerPC/JitArm64/Jit_Util.h @@ -0,0 +1,18 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include "Common/Arm64Emitter.h" +#include "Common/Common.h" + +#include "Core/HW/MMIO.h" + +void MMIOLoadToReg(MMIO::Mapping* mmio, Arm64Gen::ARM64XEmitter* emit, + BitSet32 gprs_in_use, BitSet32 fprs_in_use, + ARM64Reg dst_reg, u32 address, u32 flags); + +void MMIOWriteRegToAddr(MMIO::Mapping* mmio, Arm64Gen::ARM64XEmitter* emit, + BitSet32 gprs_in_use, BitSet32 fprs_in_use, + ARM64Reg src_reg, u32 address, u32 flags);