diff --git a/Source/Core/Common/x64Analyzer.cpp b/Source/Core/Common/x64Analyzer.cpp index c7db683f1e..5be91ea585 100644 --- a/Source/Core/Common/x64Analyzer.cpp +++ b/Source/Core/Common/x64Analyzer.cpp @@ -136,6 +136,7 @@ bool DisassembleMov(const unsigned char *codePtr, InstructionInfo *info) info->isMemoryWrite = true; info->hasImmediate = true; info->immediate = *codePtr; + info->operandSize = 1; codePtr++; break; diff --git a/Source/Core/Core/PowerPC/Jit64/Jit_LoadStore.cpp b/Source/Core/Core/PowerPC/Jit64/Jit_LoadStore.cpp index 1dd12b0971..7e01afb1de 100644 --- a/Source/Core/Core/PowerPC/Jit64/Jit_LoadStore.cpp +++ b/Source/Core/Core/PowerPC/Jit64/Jit_LoadStore.cpp @@ -399,18 +399,25 @@ void Jit64::stX(UGeckoInstruction inst) gpr.Lock(a, s); gpr.BindToRegister(a, true, false); - X64Reg reg_value; - if (WriteClobbersRegValue(accessSize, /* swap */ true)) + if (gpr.R(s).IsImm()) { - MOV(32, R(RSCRATCH2), gpr.R(s)); - reg_value = RSCRATCH2; + SafeWriteRegToReg(gpr.R(s), gpr.RX(a), accessSize, offset, CallerSavedRegistersInUse(), SAFE_LOADSTORE_CLOBBER_RSCRATCH_INSTEAD_OF_ADDR); } else { - gpr.BindToRegister(s, true, false); - reg_value = gpr.RX(s); + X64Reg reg_value; + if (WriteClobbersRegValue(accessSize, /* swap */ true)) + { + MOV(32, R(RSCRATCH2), gpr.R(s)); + reg_value = RSCRATCH2; + } + else + { + gpr.BindToRegister(s, true, false); + reg_value = gpr.RX(s); + } + SafeWriteRegToReg(reg_value, gpr.RX(a), accessSize, offset, CallerSavedRegistersInUse(), SAFE_LOADSTORE_CLOBBER_RSCRATCH_INSTEAD_OF_ADDR); } - SafeWriteRegToReg(reg_value, gpr.RX(a), accessSize, offset, CallerSavedRegistersInUse(), SAFE_LOADSTORE_CLOBBER_RSCRATCH_INSTEAD_OF_ADDR); if (update && offset) { @@ -474,18 +481,25 @@ void Jit64::stXx(UGeckoInstruction inst) break; } - X64Reg reg_value; - if (WriteClobbersRegValue(accessSize, /* swap */ true)) + if (gpr.R(s).IsImm()) { - MOV(32, R(RSCRATCH), gpr.R(s)); - reg_value = RSCRATCH; + SafeWriteRegToReg(gpr.R(s), RSCRATCH2, accessSize, 0, CallerSavedRegistersInUse()); } else { - gpr.BindToRegister(s, true, false); - reg_value = gpr.RX(s); + X64Reg reg_value; + if (WriteClobbersRegValue(accessSize, /* swap */ true)) + { + MOV(32, R(RSCRATCH), gpr.R(s)); + reg_value = RSCRATCH; + } + else + { + gpr.BindToRegister(s, true, false); + reg_value = gpr.RX(s); + } + SafeWriteRegToReg(reg_value, RSCRATCH2, accessSize, 0, CallerSavedRegistersInUse()); } - SafeWriteRegToReg(reg_value, RSCRATCH2, accessSize, 0, CallerSavedRegistersInUse()); if (update && js.memcheck) { @@ -530,8 +544,15 @@ void Jit64::stmw(UGeckoInstruction inst) MOV(32, R(RSCRATCH), gpr.R(inst.RA)); else XOR(32, R(RSCRATCH), R(RSCRATCH)); - MOV(32, R(RSCRATCH2), gpr.R(i)); - SafeWriteRegToReg(RSCRATCH2, RSCRATCH, 32, (i - inst.RD) * 4 + (u32)(s32)inst.SIMM_16, CallerSavedRegistersInUse()); + if (gpr.R(i).IsImm()) + { + SafeWriteRegToReg(gpr.R(i), RSCRATCH, 32, (i - inst.RD) * 4 + (u32)(s32)inst.SIMM_16, CallerSavedRegistersInUse()); + } + else + { + MOV(32, R(RSCRATCH2), gpr.R(i)); + SafeWriteRegToReg(RSCRATCH2, RSCRATCH, 32, (i - inst.RD) * 4 + (u32)(s32)inst.SIMM_16, CallerSavedRegistersInUse()); + } } gpr.UnlockAllX(); } diff --git a/Source/Core/Core/PowerPC/JitCommon/JitBackpatch.cpp b/Source/Core/Core/PowerPC/JitCommon/JitBackpatch.cpp index 9a7fdc7989..6e89ae32eb 100644 --- a/Source/Core/Core/PowerPC/JitCommon/JitBackpatch.cpp +++ b/Source/Core/Core/PowerPC/JitCommon/JitBackpatch.cpp @@ -115,8 +115,31 @@ const u8 *TrampolineCache::GetWriteTrampoline(const InstructionInfo &info, u32 r ABI_PushRegistersAndAdjustStack(registersInUse, 8); - MOVTwo(64, ABI_PARAM1, dataReg, ABI_PARAM2, addrReg); - + if (info.hasImmediate) + { + if (addrReg != ABI_PARAM2) + MOV(64, R(ABI_PARAM2), R(addrReg)); + // we have to swap back the immediate to pass it to the write functions + switch (info.operandSize) + { + case 8: + PanicAlert("Invalid 64-bit immediate!"); + break; + case 4: + MOV(32, R(ABI_PARAM1), Imm32(Common::swap32((u32)info.immediate))); + break; + case 2: + MOV(16, R(ABI_PARAM1), Imm16(Common::swap16((u16)info.immediate))); + break; + case 1: + MOV(8, R(ABI_PARAM1), Imm8((u8)info.immediate)); + break; + } + } + else + { + MOVTwo(64, ABI_PARAM1, dataReg, ABI_PARAM2, addrReg); + } if (info.displacement) { ADD(32, R(ABI_PARAM2), Imm32(info.displacement)); @@ -220,7 +243,7 @@ const u8 *Jitx86Base::BackPatch(u8 *codePtr, u32 emAddress, void *ctx_void) u32 pc = it->second; u8 *start; - if (info.byteSwap) + if (info.byteSwap || info.hasImmediate) { // The instruction is a MOVBE but it failed so the value is still in little-endian byte order. start = codePtr; diff --git a/Source/Core/Core/PowerPC/JitCommon/Jit_Util.cpp b/Source/Core/Core/PowerPC/JitCommon/Jit_Util.cpp index 2ded60e859..f2313579fe 100644 --- a/Source/Core/Core/PowerPC/JitCommon/Jit_Util.cpp +++ b/Source/Core/Core/PowerPC/JitCommon/Jit_Util.cpp @@ -250,10 +250,11 @@ void EmuCodeBlock::MMIOLoadToReg(MMIO::Mapping* mmio, Gen::X64Reg reg_value, } } -FixupBranch EmuCodeBlock::CheckIfSafeAddress(X64Reg reg_value, X64Reg reg_addr, u32 registers_in_use, u32 mem_mask) +FixupBranch EmuCodeBlock::CheckIfSafeAddress(OpArg reg_value, X64Reg reg_addr, u32 registers_in_use, u32 mem_mask) { registers_in_use |= (1 << reg_addr); - registers_in_use |= (1 << reg_value); + if (reg_value.IsSimpleReg()) + registers_in_use |= (1 << reg_value.GetSimpleReg()); // Get ourselves a free register; try to pick one that doesn't involve pushing, if we can. X64Reg scratch = RSCRATCH; @@ -381,7 +382,7 @@ void EmuCodeBlock::SafeLoadToReg(X64Reg reg_value, const Gen::OpArg & opAddress, } FixupBranch slow, exit; - slow = CheckIfSafeAddress(reg_value, reg_addr, registersInUse, mem_mask); + slow = CheckIfSafeAddress(R(reg_value), reg_addr, registersInUse, mem_mask); UnsafeLoadToReg(reg_value, R(reg_addr), accessSize, 0, signExtend); if (farcode.Enabled()) SwitchToFarCode(); @@ -429,34 +430,55 @@ void EmuCodeBlock::SafeLoadToReg(X64Reg reg_value, const Gen::OpArg & opAddress, } } -u8 *EmuCodeBlock::UnsafeWriteRegToReg(X64Reg reg_value, X64Reg reg_addr, int accessSize, s32 offset, bool swap) +u8 *EmuCodeBlock::UnsafeWriteRegToReg(OpArg reg_value, X64Reg reg_addr, int accessSize, s32 offset, bool swap) { u8* result = GetWritableCodePtr(); OpArg dest = MComplex(RMEM, reg_addr, SCALE_1, offset); - if (swap) + if (reg_value.IsImm()) + { + if (swap) + { + if (accessSize == 32) + reg_value = Imm32(Common::swap32((u32)reg_value.offset)); + else if (accessSize == 16) + reg_value = Imm16(Common::swap16((u16)reg_value.offset)); + else + reg_value = Imm8((u8)reg_value.offset); + } + MOV(accessSize, dest, reg_value); + } + else if (swap) { if (cpu_info.bMOVBE) { - MOVBE(accessSize, dest, R(reg_value)); + MOVBE(accessSize, dest, reg_value); } else { if (accessSize > 8) - BSWAP(accessSize, reg_value); + BSWAP(accessSize, reg_value.GetSimpleReg()); result = GetWritableCodePtr(); - MOV(accessSize, dest, R(reg_value)); + MOV(accessSize, dest, reg_value); } } else { - MOV(accessSize, dest, R(reg_value)); + MOV(accessSize, dest, reg_value); } return result; } -void EmuCodeBlock::SafeWriteRegToReg(X64Reg reg_value, X64Reg reg_addr, int accessSize, s32 offset, u32 registersInUse, int flags) +void EmuCodeBlock::SafeWriteRegToReg(OpArg reg_value, X64Reg reg_addr, int accessSize, s32 offset, u32 registersInUse, int flags) { + // set the correct immediate format + if (reg_value.IsImm()) + { + reg_value = accessSize == 32 ? Imm32((u32)reg_value.offset) : + accessSize == 16 ? Imm16((u16)reg_value.offset) : + Imm8((u8)reg_value.offset); + } + if (!SConfig::GetInstance().m_LocalCoreStartupParameter.bMMU && SConfig::GetInstance().m_LocalCoreStartupParameter.bFastmem && !(flags & (SAFE_LOADSTORE_NO_SWAP | SAFE_LOADSTORE_NO_FASTMEM)) @@ -515,24 +537,38 @@ void EmuCodeBlock::SafeWriteRegToReg(X64Reg reg_value, X64Reg reg_addr, int acce else exit = J(true); SetJumpTarget(slow); + // PC is used by memory watchpoints (if enabled) or to print accurate PC locations in debug logs MOV(32, PPCSTATE(pc), Imm32(jit->js.compilerPC)); size_t rsp_alignment = (flags & SAFE_LOADSTORE_NO_PROLOG) ? 8 : 0; ABI_PushRegistersAndAdjustStack(registersInUse, rsp_alignment); + + // If the input is an immediate, we need to put it in a register. + X64Reg reg; + if (reg_value.IsImm()) + { + reg = reg_addr == ABI_PARAM1 ? RSCRATCH : ABI_PARAM1; + MOV(accessSize, R(reg), reg_value); + } + else + { + reg = reg_value.GetSimpleReg(); + } + switch (accessSize) { case 64: - ABI_CallFunctionRR(swap ? ((void *)&Memory::Write_U64) : ((void *)&Memory::Write_U64_Swap), reg_value, reg_addr); + ABI_CallFunctionRR(swap ? ((void *)&Memory::Write_U64) : ((void *)&Memory::Write_U64_Swap), reg, reg_addr); break; case 32: - ABI_CallFunctionRR(swap ? ((void *)&Memory::Write_U32) : ((void *)&Memory::Write_U32_Swap), reg_value, reg_addr); + ABI_CallFunctionRR(swap ? ((void *)&Memory::Write_U32) : ((void *)&Memory::Write_U32_Swap), reg, reg_addr); break; case 16: - ABI_CallFunctionRR(swap ? ((void *)&Memory::Write_U16) : ((void *)&Memory::Write_U16_Swap), reg_value, reg_addr); + ABI_CallFunctionRR(swap ? ((void *)&Memory::Write_U16) : ((void *)&Memory::Write_U16_Swap), reg, reg_addr); break; case 8: - ABI_CallFunctionRR((void *)&Memory::Write_U8, reg_value, reg_addr); + ABI_CallFunctionRR((void *)&Memory::Write_U8, reg, reg_addr); break; } ABI_PopRegistersAndAdjustStack(registersInUse, rsp_alignment); diff --git a/Source/Core/Core/PowerPC/JitCommon/Jit_Util.h b/Source/Core/Core/PowerPC/JitCommon/Jit_Util.h index 7a2051f742..21a25d7213 100644 --- a/Source/Core/Core/PowerPC/JitCommon/Jit_Util.h +++ b/Source/Core/Core/PowerPC/JitCommon/Jit_Util.h @@ -76,11 +76,15 @@ public: void LoadAndSwap(int size, Gen::X64Reg dst, const Gen::OpArg& src); void SwapAndStore(int size, const Gen::OpArg& dst, Gen::X64Reg src); - Gen::FixupBranch CheckIfSafeAddress(Gen::X64Reg reg_value, Gen::X64Reg reg_addr, u32 registers_in_use, u32 mem_mask); + Gen::FixupBranch CheckIfSafeAddress(Gen::OpArg reg_value, Gen::X64Reg reg_addr, u32 registers_in_use, u32 mem_mask); void UnsafeLoadRegToReg(Gen::X64Reg reg_addr, Gen::X64Reg reg_value, int accessSize, s32 offset = 0, bool signExtend = false); void UnsafeLoadRegToRegNoSwap(Gen::X64Reg reg_addr, Gen::X64Reg reg_value, int accessSize, s32 offset, bool signExtend = false); // these return the address of the MOV, for backpatching - u8 *UnsafeWriteRegToReg(Gen::X64Reg reg_value, Gen::X64Reg reg_addr, int accessSize, s32 offset = 0, bool swap = true); + u8 *UnsafeWriteRegToReg(Gen::OpArg reg_value, Gen::X64Reg reg_addr, int accessSize, s32 offset = 0, bool swap = true); + u8 *UnsafeWriteRegToReg(Gen::X64Reg reg_value, Gen::X64Reg reg_addr, int accessSize, s32 offset = 0, bool swap = true) + { + UnsafeWriteRegToReg(R(reg_value), reg_addr, accessSize, offset, swap); + } u8 *UnsafeLoadToReg(Gen::X64Reg reg_value, Gen::OpArg opAddress, int accessSize, s32 offset, bool signExtend); // Generate a load/write from the MMIO handler for a given address. Only @@ -98,7 +102,12 @@ public: void SafeLoadToReg(Gen::X64Reg reg_value, const Gen::OpArg & opAddress, int accessSize, s32 offset, u32 registersInUse, bool signExtend, int flags = 0); // Clobbers RSCRATCH or reg_addr depending on the relevant flag. Preserves // reg_value if the load fails and js.memcheck is enabled. - void SafeWriteRegToReg(Gen::X64Reg reg_value, Gen::X64Reg reg_addr, int accessSize, s32 offset, u32 registersInUse, int flags = 0); + // Works with immediate inputs and simple registers only. + void SafeWriteRegToReg(Gen::OpArg reg_value, Gen::X64Reg reg_addr, int accessSize, s32 offset, u32 registersInUse, int flags = 0); + void SafeWriteRegToReg(Gen::X64Reg reg_value, Gen::X64Reg reg_addr, int accessSize, s32 offset, u32 registersInUse, int flags = 0) + { + SafeWriteRegToReg(R(reg_value), reg_addr, accessSize, offset, registersInUse, flags); + } // applies to safe and unsafe WriteRegToReg bool WriteClobbersRegValue(int accessSize, bool swap)