diff --git a/src/Cafe/CMakeLists.txt b/src/Cafe/CMakeLists.txt index 59b6aa42..46060154 100644 --- a/src/Cafe/CMakeLists.txt +++ b/src/Cafe/CMakeLists.txt @@ -62,24 +62,31 @@ add_library(CemuCafe HW/Espresso/Recompiler/PPCFunctionBoundaryTracker.h HW/Espresso/Recompiler/PPCRecompiler.cpp HW/Espresso/Recompiler/PPCRecompiler.h - HW/Espresso/Recompiler/PPCRecompilerImlAnalyzer.cpp + HW/Espresso/Recompiler/IML/IML.h + HW/Espresso/Recompiler/IML/IMLSegment.cpp + HW/Espresso/Recompiler/IML/IMLSegment.h + HW/Espresso/Recompiler/IML/IMLInstruction.cpp + HW/Espresso/Recompiler/IML/IMLInstruction.h + HW/Espresso/Recompiler/IML/IMLDebug.cpp + HW/Espresso/Recompiler/IML/IMLAnalyzer.cpp + HW/Espresso/Recompiler/IML/IMLOptimizer.cpp + HW/Espresso/Recompiler/IML/IMLRegisterAllocator.cpp + HW/Espresso/Recompiler/IML/IMLRegisterAllocator.h + HW/Espresso/Recompiler/IML/IMLRegisterAllocatorRanges.cpp + HW/Espresso/Recompiler/IML/IMLRegisterAllocatorRanges.h HW/Espresso/Recompiler/PPCRecompilerImlGen.cpp HW/Espresso/Recompiler/PPCRecompilerImlGenFPU.cpp HW/Espresso/Recompiler/PPCRecompilerIml.h - HW/Espresso/Recompiler/PPCRecompilerImlOptimizer.cpp - HW/Espresso/Recompiler/PPCRecompilerImlRanges.cpp - HW/Espresso/Recompiler/PPCRecompilerImlRanges.h - HW/Espresso/Recompiler/PPCRecompilerImlRegisterAllocator2.cpp - HW/Espresso/Recompiler/PPCRecompilerImlRegisterAllocator.cpp HW/Espresso/Recompiler/PPCRecompilerIntermediate.cpp - HW/Espresso/Recompiler/PPCRecompilerX64AVX.cpp - HW/Espresso/Recompiler/PPCRecompilerX64BMI.cpp - HW/Espresso/Recompiler/PPCRecompilerX64.cpp - HW/Espresso/Recompiler/PPCRecompilerX64FPU.cpp - HW/Espresso/Recompiler/PPCRecompilerX64Gen.cpp - HW/Espresso/Recompiler/PPCRecompilerX64GenFPU.cpp - HW/Espresso/Recompiler/PPCRecompilerX64.h - HW/Espresso/Recompiler/x64Emit.hpp + HW/Espresso/Recompiler/BackendX64/BackendX64AVX.cpp + HW/Espresso/Recompiler/BackendX64/BackendX64BMI.cpp + HW/Espresso/Recompiler/BackendX64/BackendX64.cpp + HW/Espresso/Recompiler/BackendX64/BackendX64FPU.cpp + HW/Espresso/Recompiler/BackendX64/BackendX64Gen.cpp + HW/Espresso/Recompiler/BackendX64/BackendX64GenFPU.cpp + HW/Espresso/Recompiler/BackendX64/BackendX64.h + HW/Espresso/Recompiler/BackendX64/X64Emit.hpp + HW/Espresso/Recompiler/BackendX64/x86Emitter.h HW/Latte/Common/RegisterSerializer.cpp HW/Latte/Common/RegisterSerializer.h HW/Latte/Common/ShaderSerializer.cpp diff --git a/src/Cafe/HW/Espresso/EspressoISA.h b/src/Cafe/HW/Espresso/EspressoISA.h index b3ae45c3..e66e1424 100644 --- a/src/Cafe/HW/Espresso/EspressoISA.h +++ b/src/Cafe/HW/Espresso/EspressoISA.h @@ -91,13 +91,15 @@ namespace Espresso BCCTR = 528 }; - enum class OPCODE_31 + enum class Opcode31 { - + TW = 4, + MFTB = 371, }; inline PrimaryOpcode GetPrimaryOpcode(uint32 opcode) { return (PrimaryOpcode)(opcode >> 26); }; inline Opcode19 GetGroup19Opcode(uint32 opcode) { return (Opcode19)((opcode >> 1) & 0x3FF); }; + inline Opcode31 GetGroup31Opcode(uint32 opcode) { return (Opcode31)((opcode >> 1) & 0x3FF); }; struct BOField { @@ -132,6 +134,12 @@ namespace Espresso uint8 bo; }; + // returns true if LK bit is set, only valid for branch instructions + inline bool DecodeLK(uint32 opcode) + { + return (opcode & 1) != 0; + } + inline void _decodeForm_I(uint32 opcode, uint32& LI, bool& AA, bool& LK) { LI = opcode & 0x3fffffc; @@ -183,13 +191,7 @@ namespace Espresso _decodeForm_D_branch(opcode, BD, BO, BI, AA, LK); } - inline void decodeOp_BCLR(uint32 opcode, BOField& BO, uint32& BI, bool& LK) - { - // form XL (with BD field expected to be zero) - _decodeForm_XL(opcode, BO, BI, LK); - } - - inline void decodeOp_BCCTR(uint32 opcode, BOField& BO, uint32& BI, bool& LK) + inline void decodeOp_BCSPR(uint32 opcode, BOField& BO, uint32& BI, bool& LK) // BCLR and BCSPR { // form XL (with BD field expected to be zero) _decodeForm_XL(opcode, BO, BI, LK); diff --git a/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterALU.hpp b/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterALU.hpp index ed97288d..aec5dc0a 100644 --- a/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterALU.hpp +++ b/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterALU.hpp @@ -3,12 +3,12 @@ static void PPCInterpreter_setXerOV(PPCInterpreter_t* hCPU, bool hasOverflow) { if (hasOverflow) { - hCPU->spr.XER |= XER_SO; - hCPU->spr.XER |= XER_OV; + hCPU->xer_so = 1; + hCPU->xer_ov = 1; } else { - hCPU->spr.XER &= ~XER_OV; + hCPU->xer_ov = 0; } } @@ -245,7 +245,7 @@ static void PPCInterpreter_SUBFCO(PPCInterpreter_t* hCPU, uint32 opcode) uint32 a = hCPU->gpr[rA]; uint32 b = hCPU->gpr[rB]; hCPU->gpr[rD] = ~a + b + 1; - // update xer + // update carry if (ppc_carry_3(~a, b, 1)) hCPU->xer_ca = 1; else @@ -847,8 +847,7 @@ static void PPCInterpreter_CMP(PPCInterpreter_t* hCPU, uint32 opcode) hCPU->cr[cr * 4 + CR_BIT_GT] = 1; else hCPU->cr[cr * 4 + CR_BIT_EQ] = 1; - if ((hCPU->spr.XER & XER_SO) != 0) - hCPU->cr[cr * 4 + CR_BIT_SO] = 1; + hCPU->cr[cr * 4 + CR_BIT_SO] = hCPU->xer_so; PPCInterpreter_nextInstruction(hCPU); } @@ -870,8 +869,7 @@ static void PPCInterpreter_CMPL(PPCInterpreter_t* hCPU, uint32 opcode) hCPU->cr[cr * 4 + CR_BIT_GT] = 1; else hCPU->cr[cr * 4 + CR_BIT_EQ] = 1; - if ((hCPU->spr.XER & XER_SO) != 0) - hCPU->cr[cr * 4 + CR_BIT_SO] = 1; + hCPU->cr[cr * 4 + CR_BIT_SO] = hCPU->xer_so; PPCInterpreter_nextInstruction(hCPU); } @@ -894,8 +892,7 @@ static void PPCInterpreter_CMPI(PPCInterpreter_t* hCPU, uint32 opcode) hCPU->cr[cr * 4 + CR_BIT_GT] = 1; else hCPU->cr[cr * 4 + CR_BIT_EQ] = 1; - if (hCPU->spr.XER & XER_SO) - hCPU->cr[cr * 4 + CR_BIT_SO] = 1; + hCPU->cr[cr * 4 + CR_BIT_SO] = hCPU->xer_so; PPCInterpreter_nextInstruction(hCPU); } @@ -918,8 +915,7 @@ static void PPCInterpreter_CMPLI(PPCInterpreter_t* hCPU, uint32 opcode) hCPU->cr[cr * 4 + CR_BIT_GT] = 1; else hCPU->cr[cr * 4 + CR_BIT_EQ] = 1; - if (hCPU->spr.XER & XER_SO) - hCPU->cr[cr * 4 + CR_BIT_SO] = 1; + hCPU->cr[cr * 4 + CR_BIT_SO] = hCPU->xer_so; PPCInterpreter_nextInstruction(hCPU); } diff --git a/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterInternal.h b/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterInternal.h index bc8458d9..bac253c4 100644 --- a/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterInternal.h +++ b/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterInternal.h @@ -50,9 +50,9 @@ #define CR_BIT_EQ 2 #define CR_BIT_SO 3 -#define XER_SO (1<<31) // summary overflow bit -#define XER_OV (1<<30) // overflow bit #define XER_BIT_CA (29) // carry bit index. To accelerate frequent access, this bit is stored as a separate uint8 +#define XER_BIT_SO (31) // summary overflow, counterpart to CR SO +#define XER_BIT_OV (30) // FPSCR #define FPSCR_VXSNAN (1<<24) @@ -118,7 +118,8 @@ static inline void ppc_update_cr0(PPCInterpreter_t* hCPU, uint32 r) { - hCPU->cr[CR_BIT_SO] = (hCPU->spr.XER&XER_SO) ? 1 : 0; + cemu_assert_debug(hCPU->xer_so <= 1); + hCPU->cr[CR_BIT_SO] = hCPU->xer_so; hCPU->cr[CR_BIT_LT] = ((r != 0) ? 1 : 0) & ((r & 0x80000000) ? 1 : 0); hCPU->cr[CR_BIT_EQ] = (r == 0); hCPU->cr[CR_BIT_GT] = hCPU->cr[CR_BIT_EQ] ^ hCPU->cr[CR_BIT_LT] ^ 1; // this works because EQ and LT can never be set at the same time. So the only case where GT becomes 1 is when LT=0 and EQ=0 diff --git a/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterLoadStore.hpp b/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterLoadStore.hpp index 694e05e6..26467458 100644 --- a/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterLoadStore.hpp +++ b/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterLoadStore.hpp @@ -85,7 +85,8 @@ static void PPCInterpreter_STWCX(PPCInterpreter_t* hCPU, uint32 Opcode) ppc_setCRBit(hCPU, CR_BIT_GT, 0); ppc_setCRBit(hCPU, CR_BIT_EQ, 1); } - ppc_setCRBit(hCPU, CR_BIT_SO, (hCPU->spr.XER&XER_SO) != 0 ? 1 : 0); + cemu_assert_debug(hCPU->xer_so <= 1); + ppc_setCRBit(hCPU, CR_BIT_SO, hCPU->xer_so); // remove reservation hCPU->reservedMemAddr = 0; hCPU->reservedMemValue = 0; diff --git a/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterMain.cpp b/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterMain.cpp index 2d808fef..295865f5 100644 --- a/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterMain.cpp +++ b/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterMain.cpp @@ -59,16 +59,24 @@ void PPCInterpreter_setDEC(PPCInterpreter_t* hCPU, uint32 newValue) uint32 PPCInterpreter_getXER(PPCInterpreter_t* hCPU) { uint32 xerValue = hCPU->spr.XER; - xerValue &= ~(1<xer_ca ) - xerValue |= (1<xer_ca) + xerValue |= (1 << XER_BIT_CA); + if (hCPU->xer_so) + xerValue |= (1 << XER_BIT_SO); + if (hCPU->xer_ov) + xerValue |= (1 << XER_BIT_OV); return xerValue; } void PPCInterpreter_setXER(PPCInterpreter_t* hCPU, uint32 v) { hCPU->spr.XER = v; - hCPU->xer_ca = (v>>XER_BIT_CA)&1; + hCPU->xer_ca = (v >> XER_BIT_CA) & 1; + hCPU->xer_so = (v >> XER_BIT_SO) & 1; + hCPU->xer_ov = (v >> XER_BIT_OV) & 1; } uint32 PPCInterpreter_getCoreIndex(PPCInterpreter_t* hCPU) diff --git a/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterOPC.cpp b/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterOPC.cpp index 12f86427..d6b643ee 100644 --- a/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterOPC.cpp +++ b/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterOPC.cpp @@ -5,7 +5,6 @@ #include "Cafe/OS/libs/coreinit/coreinit_CodeGen.h" #include "../Recompiler/PPCRecompiler.h" -#include "../Recompiler/PPCRecompilerX64.h" #include #include "Cafe/HW/Latte/Core/LatteBufferCache.h" diff --git a/src/Cafe/HW/Espresso/PPCState.h b/src/Cafe/HW/Espresso/PPCState.h index 2b30326b..b14e33c6 100644 --- a/src/Cafe/HW/Espresso/PPCState.h +++ b/src/Cafe/HW/Espresso/PPCState.h @@ -49,6 +49,8 @@ struct PPCInterpreter_t uint32 fpscr; uint8 cr[32]; // 0 -> bit not set, 1 -> bit set (upper 7 bits of each byte must always be zero) (cr0 starts at index 0, cr1 at index 4 ..) uint8 xer_ca; // carry from xer + uint8 xer_so; + uint8 xer_ov; uint8 LSQE; uint8 PSE; // thread remaining cycles @@ -67,7 +69,8 @@ struct PPCInterpreter_t uint32 reservedMemValue; // temporary storage for recompiler FPR_t temporaryFPR[8]; - uint32 temporaryGPR[4]; + uint32 temporaryGPR[4]; // deprecated, refactor backend dependency on this away + uint32 temporaryGPR_reg[4]; // values below this are not used by Cafe OS usermode struct { diff --git a/src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64.cpp b/src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64.cpp new file mode 100644 index 00000000..e32d1ea8 --- /dev/null +++ b/src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64.cpp @@ -0,0 +1,1777 @@ +#include "Cafe/HW/Espresso/PPCState.h" +#include "Cafe/HW/Espresso/Interpreter/PPCInterpreterInternal.h" +#include "Cafe/HW/Espresso/Interpreter/PPCInterpreterHelper.h" +#include "../PPCRecompiler.h" +#include "../PPCRecompilerIml.h" +#include "BackendX64.h" +#include "Cafe/OS/libs/coreinit/coreinit_Time.h" +#include "util/MemMapper/MemMapper.h" +#include "Common/cpu_features.h" + +static x86Assembler64::GPR32 _reg32(IMLReg physReg) +{ + cemu_assert_debug(physReg.GetRegFormat() == IMLRegFormat::I32); + IMLRegID regId = physReg.GetRegID(); + cemu_assert_debug(regId < 16); + return (x86Assembler64::GPR32)regId; +} + +static uint32 _reg64(IMLReg physReg) +{ + cemu_assert_debug(physReg.GetRegFormat() == IMLRegFormat::I64); + IMLRegID regId = physReg.GetRegID(); + cemu_assert_debug(regId < 16); + return regId; +} + +uint32 _regF64(IMLReg physReg) +{ + cemu_assert_debug(physReg.GetRegFormat() == IMLRegFormat::F64); + IMLRegID regId = physReg.GetRegID(); + cemu_assert_debug(regId >= IMLArchX86::PHYSREG_FPR_BASE && regId < IMLArchX86::PHYSREG_FPR_BASE+16); + regId -= IMLArchX86::PHYSREG_FPR_BASE; + return regId; +} + +static x86Assembler64::GPR8_REX _reg8(IMLReg physReg) +{ + cemu_assert_debug(physReg.GetRegFormat() == IMLRegFormat::I32); // for now these are represented as 32bit + return (x86Assembler64::GPR8_REX)physReg.GetRegID(); +} + +static x86Assembler64::GPR32 _reg32_from_reg8(x86Assembler64::GPR8_REX regId) +{ + return (x86Assembler64::GPR32)regId; +} + +static x86Assembler64::GPR8_REX _reg8_from_reg32(x86Assembler64::GPR32 regId) +{ + return (x86Assembler64::GPR8_REX)regId; +} + +static x86Assembler64::GPR8_REX _reg8_from_reg64(uint32 regId) +{ + return (x86Assembler64::GPR8_REX)regId; +} + +static x86Assembler64::GPR64 _reg64_from_reg32(x86Assembler64::GPR32 regId) +{ + return (x86Assembler64::GPR64)regId; +} + +X86Cond _x86Cond(IMLCondition imlCond) +{ + switch (imlCond) + { + case IMLCondition::EQ: + return X86_CONDITION_Z; + case IMLCondition::NEQ: + return X86_CONDITION_NZ; + case IMLCondition::UNSIGNED_GT: + return X86_CONDITION_NBE; + case IMLCondition::UNSIGNED_LT: + return X86_CONDITION_B; + case IMLCondition::SIGNED_GT: + return X86_CONDITION_NLE; + case IMLCondition::SIGNED_LT: + return X86_CONDITION_L; + default: + break; + } + cemu_assert_suspicious(); + return X86_CONDITION_Z; +} + +/* +* Remember current instruction output offset for reloc +* The instruction generated after this method has been called will be adjusted +*/ +void PPCRecompilerX64Gen_rememberRelocatableOffset(x64GenContext_t* x64GenContext, void* extraInfo = nullptr) +{ + x64GenContext->relocateOffsetTable2.emplace_back(x64GenContext->emitter->GetWriteIndex(), extraInfo); +} + +void PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext_t* x64GenContext, sint32 jumpInstructionOffset, sint32 destinationOffset) +{ + uint8* instructionData = x64GenContext->emitter->GetBufferPtr() + jumpInstructionOffset; + if (instructionData[0] == 0x0F && (instructionData[1] >= 0x80 && instructionData[1] <= 0x8F)) + { + // far conditional jump + *(uint32*)(instructionData + 2) = (destinationOffset - (jumpInstructionOffset + 6)); + } + else if (instructionData[0] >= 0x70 && instructionData[0] <= 0x7F) + { + // short conditional jump + sint32 distance = (sint32)((destinationOffset - (jumpInstructionOffset + 2))); + cemu_assert_debug(distance >= -128 && distance <= 127); + *(uint8*)(instructionData + 1) = (uint8)distance; + } + else if (instructionData[0] == 0xE9) + { + *(uint32*)(instructionData + 1) = (destinationOffset - (jumpInstructionOffset + 5)); + } + else if (instructionData[0] == 0xEB) + { + sint32 distance = (sint32)((destinationOffset - (jumpInstructionOffset + 2))); + cemu_assert_debug(distance >= -128 && distance <= 127); + *(uint8*)(instructionData + 1) = (uint8)distance; + } + else + { + assert_dbg(); + } +} + +void* ATTR_MS_ABI PPCRecompiler_virtualHLE(PPCInterpreter_t* hCPU, uint32 hleFuncId) +{ + void* prevRSPTemp = hCPU->rspTemp; + if( hleFuncId == 0xFFD0 ) + { + hCPU->remainingCycles -= 500; // let subtract about 500 cycles for each HLE call + hCPU->gpr[3] = 0; + PPCInterpreter_nextInstruction(hCPU); + return ppcInterpreterCurrentInstance; + } + else + { + auto hleCall = PPCInterpreter_getHLECall(hleFuncId); + cemu_assert(hleCall != nullptr); + hleCall(hCPU); + } + hCPU->rspTemp = prevRSPTemp; + return ppcInterpreterCurrentInstance; +} + +void ATTR_MS_ABI PPCRecompiler_getTBL(PPCInterpreter_t* hCPU, uint32 gprIndex) +{ + uint64 coreTime = coreinit::coreinit_getTimerTick(); + hCPU->gpr[gprIndex] = (uint32)(coreTime&0xFFFFFFFF); +} + +void ATTR_MS_ABI PPCRecompiler_getTBU(PPCInterpreter_t* hCPU, uint32 gprIndex) +{ + uint64 coreTime = coreinit::coreinit_getTimerTick(); + hCPU->gpr[gprIndex] = (uint32)((coreTime>>32)&0xFFFFFFFF); +} + +bool PPCRecompilerX64Gen_imlInstruction_macro(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction) +{ + if (imlInstruction->operation == PPCREC_IML_MACRO_B_TO_REG) + { + uint32 branchDstReg = _reg32(imlInstruction->op_macro.paramReg); + if(X86_REG_RDX != branchDstReg) + x64Gen_mov_reg64_reg64(x64GenContext, X86_REG_RDX, branchDstReg); + // potential optimization: Use branchDstReg directly if possible instead of moving to RDX/EDX + // JMP [offset+RDX*(8/4)+R15] + x64Gen_writeU8(x64GenContext, 0x41); + x64Gen_writeU8(x64GenContext, 0xFF); + x64Gen_writeU8(x64GenContext, 0xA4); + x64Gen_writeU8(x64GenContext, 0x57); + x64Gen_writeU32(x64GenContext, (uint32)offsetof(PPCRecompilerInstanceData_t, ppcRecompilerDirectJumpTable)); + return true; + } + else if( imlInstruction->operation == PPCREC_IML_MACRO_BL ) + { + // MOV DWORD [SPR_LinkRegister], newLR + uint32 newLR = imlInstruction->op_macro.param + 4; + x64Gen_mov_mem32Reg64_imm32(x64GenContext, X86_REG_RSP, offsetof(PPCInterpreter_t, spr.LR), newLR); + // remember new instruction pointer in RDX + uint32 newIP = imlInstruction->op_macro.param2; + x64Gen_mov_reg64Low32_imm32(x64GenContext, X86_REG_RDX, newIP); + // since RDX is constant we can use JMP [R15+const_offset] if jumpTableOffset+RDX*2 does not exceed the 2GB boundary + uint64 lookupOffset = (uint64)offsetof(PPCRecompilerInstanceData_t, ppcRecompilerDirectJumpTable) + (uint64)newIP * 2ULL; + if (lookupOffset >= 0x80000000ULL) + { + // JMP [offset+RDX*(8/4)+R15] + x64Gen_writeU8(x64GenContext, 0x41); + x64Gen_writeU8(x64GenContext, 0xFF); + x64Gen_writeU8(x64GenContext, 0xA4); + x64Gen_writeU8(x64GenContext, 0x57); + x64Gen_writeU32(x64GenContext, (uint32)offsetof(PPCRecompilerInstanceData_t, ppcRecompilerDirectJumpTable)); + } + else + { + x64Gen_writeU8(x64GenContext, 0x41); + x64Gen_writeU8(x64GenContext, 0xFF); + x64Gen_writeU8(x64GenContext, 0xA7); + x64Gen_writeU32(x64GenContext, (uint32)lookupOffset); + } + return true; + } + else if( imlInstruction->operation == PPCREC_IML_MACRO_B_FAR ) + { + // remember new instruction pointer in RDX + uint32 newIP = imlInstruction->op_macro.param2; + x64Gen_mov_reg64Low32_imm32(x64GenContext, X86_REG_RDX, newIP); + // Since RDX is constant we can use JMP [R15+const_offset] if jumpTableOffset+RDX*2 does not exceed the 2GB boundary + uint64 lookupOffset = (uint64)offsetof(PPCRecompilerInstanceData_t, ppcRecompilerDirectJumpTable) + (uint64)newIP * 2ULL; + if (lookupOffset >= 0x80000000ULL) + { + // JMP [offset+RDX*(8/4)+R15] + x64Gen_writeU8(x64GenContext, 0x41); + x64Gen_writeU8(x64GenContext, 0xFF); + x64Gen_writeU8(x64GenContext, 0xA4); + x64Gen_writeU8(x64GenContext, 0x57); + x64Gen_writeU32(x64GenContext, (uint32)offsetof(PPCRecompilerInstanceData_t, ppcRecompilerDirectJumpTable)); + } + else + { + x64Gen_writeU8(x64GenContext, 0x41); + x64Gen_writeU8(x64GenContext, 0xFF); + x64Gen_writeU8(x64GenContext, 0xA7); + x64Gen_writeU32(x64GenContext, (uint32)lookupOffset); + } + return true; + } + else if( imlInstruction->operation == PPCREC_IML_MACRO_LEAVE ) + { + uint32 currentInstructionAddress = imlInstruction->op_macro.param; + // remember PC value in REG_EDX + x64Gen_mov_reg64Low32_imm32(x64GenContext, X86_REG_RDX, currentInstructionAddress); + + uint32 newIP = 0; // special value for recompiler exit + uint64 lookupOffset = (uint64)&(((PPCRecompilerInstanceData_t*)NULL)->ppcRecompilerDirectJumpTable) + (uint64)newIP * 2ULL; + // JMP [R15+offset] + x64Gen_writeU8(x64GenContext, 0x41); + x64Gen_writeU8(x64GenContext, 0xFF); + x64Gen_writeU8(x64GenContext, 0xA7); + x64Gen_writeU32(x64GenContext, (uint32)lookupOffset); + return true; + } + else if( imlInstruction->operation == PPCREC_IML_MACRO_DEBUGBREAK ) + { + x64Gen_mov_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, imlInstruction->op_macro.param2); + x64Gen_int3(x64GenContext); + return true; + } + else if( imlInstruction->operation == PPCREC_IML_MACRO_COUNT_CYCLES ) + { + uint32 cycleCount = imlInstruction->op_macro.param; + x64Gen_sub_mem32reg64_imm32(x64GenContext, X86_REG_RSP, offsetof(PPCInterpreter_t, remainingCycles), cycleCount); + return true; + } + else if( imlInstruction->operation == PPCREC_IML_MACRO_HLE ) + { + uint32 ppcAddress = imlInstruction->op_macro.param; + uint32 funcId = imlInstruction->op_macro.param2; + //x64Gen_int3(x64GenContext); + // update instruction pointer + x64Gen_mov_mem32Reg64_imm32(x64GenContext, X86_REG_RSP, offsetof(PPCInterpreter_t, instructionPointer), ppcAddress); + //// save hCPU (RSP) + //x64Gen_mov_reg64_imm64(x64GenContext, REG_RESV_TEMP, (uint64)&ppcRecompilerX64_hCPUTemp); + //x64Emit_mov_mem64_reg64(x64GenContext, REG_RESV_TEMP, 0, REG_RSP); + // set parameters + x64Gen_mov_reg64_reg64(x64GenContext, X86_REG_RCX, X86_REG_RSP); + x64Gen_mov_reg64_imm64(x64GenContext, X86_REG_RDX, funcId); + // restore stackpointer from executionContext/hCPU->rspTemp + x64Emit_mov_reg64_mem64(x64GenContext, X86_REG_RSP, REG_RESV_HCPU, offsetof(PPCInterpreter_t, rspTemp)); + //x64Emit_mov_reg64_mem64(x64GenContext, REG_RSP, REG_R14, 0); + //x64Gen_int3(x64GenContext); + // reserve space on stack for call parameters + x64Gen_sub_reg64_imm32(x64GenContext, X86_REG_RSP, 8*11); // must be uneven number in order to retain stack 0x10 alignment + x64Gen_mov_reg64_imm64(x64GenContext, X86_REG_RBP, 0); + // call HLE function + x64Gen_mov_reg64_imm64(x64GenContext, X86_REG_RAX, (uint64)PPCRecompiler_virtualHLE); + x64Gen_call_reg64(x64GenContext, X86_REG_RAX); + // restore RSP to hCPU (from RAX, result of PPCRecompiler_virtualHLE) + //x64Gen_mov_reg64_imm64(x64GenContext, REG_RESV_TEMP, (uint64)&ppcRecompilerX64_hCPUTemp); + //x64Emit_mov_reg64_mem64Reg64(x64GenContext, REG_RSP, REG_RESV_TEMP, 0); + x64Gen_mov_reg64_reg64(x64GenContext, X86_REG_RSP, X86_REG_RAX); + // MOV R15, ppcRecompilerInstanceData + x64Gen_mov_reg64_imm64(x64GenContext, X86_REG_R15, (uint64)ppcRecompilerInstanceData); + // MOV R13, memory_base + x64Gen_mov_reg64_imm64(x64GenContext, X86_REG_R13, (uint64)memory_base); + // check if cycles where decreased beyond zero, if yes -> leave recompiler + x64Gen_bt_mem8(x64GenContext, X86_REG_RSP, offsetof(PPCInterpreter_t, remainingCycles), 31); // check if negative + sint32 jumpInstructionOffset1 = x64GenContext->emitter->GetWriteIndex(); + x64Gen_jmpc_near(x64GenContext, X86_CONDITION_NOT_CARRY, 0); + //x64Gen_int3(x64GenContext); + //x64Gen_mov_reg64Low32_imm32(x64GenContext, REG_RDX, ppcAddress); + + x64Emit_mov_reg64_mem32(x64GenContext, X86_REG_RDX, X86_REG_RSP, offsetof(PPCInterpreter_t, instructionPointer)); + // set EAX to 0 (we assume that ppcRecompilerDirectJumpTable[0] will be a recompiler escape function) + x64Gen_xor_reg32_reg32(x64GenContext, X86_REG_RAX, X86_REG_RAX); + // ADD RAX, R15 (R15 -> Pointer to ppcRecompilerInstanceData + x64Gen_add_reg64_reg64(x64GenContext, X86_REG_RAX, X86_REG_R15); + //// JMP [recompilerCallTable+EAX/4*8] + //x64Gen_int3(x64GenContext); + x64Gen_jmp_memReg64(x64GenContext, X86_REG_RAX, (uint32)offsetof(PPCRecompilerInstanceData_t, ppcRecompilerDirectJumpTable)); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset1, x64GenContext->emitter->GetWriteIndex()); + // check if instruction pointer was changed + // assign new instruction pointer to EAX + x64Emit_mov_reg64_mem32(x64GenContext, X86_REG_RAX, X86_REG_RSP, offsetof(PPCInterpreter_t, instructionPointer)); + // remember instruction pointer in REG_EDX + x64Gen_mov_reg64_reg64(x64GenContext, X86_REG_RDX, X86_REG_RAX); + // EAX *= 2 + x64Gen_add_reg64_reg64(x64GenContext, X86_REG_RAX, X86_REG_RAX); + // ADD RAX, R15 (R15 -> Pointer to ppcRecompilerInstanceData + x64Gen_add_reg64_reg64(x64GenContext, X86_REG_RAX, X86_REG_R15); + // JMP [ppcRecompilerDirectJumpTable+RAX/4*8] + x64Gen_jmp_memReg64(x64GenContext, X86_REG_RAX, (uint32)offsetof(PPCRecompilerInstanceData_t, ppcRecompilerDirectJumpTable)); + return true; + } + else if( imlInstruction->operation == PPCREC_IML_MACRO_MFTB ) + { + // according to MS ABI the caller needs to save: + // RAX, RCX, RDX, R8, R9, R10, R11 + + uint32 ppcAddress = imlInstruction->op_macro.param; + uint32 sprId = imlInstruction->op_macro.param2&0xFFFF; + uint32 gprIndex = (imlInstruction->op_macro.param2>>16)&0x1F; + // update instruction pointer + x64Gen_mov_mem32Reg64_imm32(x64GenContext, X86_REG_RSP, offsetof(PPCInterpreter_t, instructionPointer), ppcAddress); + // set parameters + x64Gen_mov_reg64_reg64(x64GenContext, X86_REG_RCX, X86_REG_RSP); + x64Gen_mov_reg64_imm64(x64GenContext, X86_REG_RDX, gprIndex); + // restore stackpointer to original RSP + x64Emit_mov_reg64_mem64(x64GenContext, X86_REG_RSP, REG_RESV_HCPU, offsetof(PPCInterpreter_t, rspTemp)); + // push hCPU on stack + x64Gen_push_reg64(x64GenContext, X86_REG_RCX); + // reserve space on stack for call parameters + x64Gen_sub_reg64_imm32(x64GenContext, X86_REG_RSP, 8*11 + 8); + x64Gen_mov_reg64_imm64(x64GenContext, X86_REG_RBP, 0); + // call function + if( sprId == SPR_TBL ) + x64Gen_mov_reg64_imm64(x64GenContext, X86_REG_RAX, (uint64)PPCRecompiler_getTBL); + else if( sprId == SPR_TBU ) + x64Gen_mov_reg64_imm64(x64GenContext, X86_REG_RAX, (uint64)PPCRecompiler_getTBU); + else + assert_dbg(); + x64Gen_call_reg64(x64GenContext, X86_REG_RAX); + // restore hCPU from stack + x64Gen_add_reg64_imm32(x64GenContext, X86_REG_RSP, 8 * 11 + 8); + x64Gen_pop_reg64(x64GenContext, X86_REG_RSP); + // MOV R15, ppcRecompilerInstanceData + x64Gen_mov_reg64_imm64(x64GenContext, X86_REG_R15, (uint64)ppcRecompilerInstanceData); + // MOV R13, memory_base + x64Gen_mov_reg64_imm64(x64GenContext, X86_REG_R13, (uint64)memory_base); + return true; + } + else + { + debug_printf("Unknown recompiler macro operation %d\n", imlInstruction->operation); + assert_dbg(); + } + return false; +} + +/* +* Load from memory +*/ +bool PPCRecompilerX64Gen_imlInstruction_load(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction, bool indexed) +{ + cemu_assert_debug(imlInstruction->op_storeLoad.registerData.GetRegFormat() == IMLRegFormat::I32); + cemu_assert_debug(imlInstruction->op_storeLoad.registerMem.GetRegFormat() == IMLRegFormat::I32); + if (indexed) + cemu_assert_debug(imlInstruction->op_storeLoad.registerMem2.GetRegFormat() == IMLRegFormat::I32); + + IMLRegID realRegisterData = imlInstruction->op_storeLoad.registerData.GetRegID(); + IMLRegID realRegisterMem = imlInstruction->op_storeLoad.registerMem.GetRegID(); + IMLRegID realRegisterMem2 = PPC_REC_INVALID_REGISTER; + if( indexed ) + realRegisterMem2 = imlInstruction->op_storeLoad.registerMem2.GetRegID(); + if( indexed && realRegisterMem == realRegisterMem2 ) + { + return false; + } + if( indexed && realRegisterData == realRegisterMem2 ) + { + // for indexed memory access realRegisterData must not be the same register as the second memory register, + // this can easily be fixed by swapping the logic of realRegisterMem and realRegisterMem2 + sint32 temp = realRegisterMem; + realRegisterMem = realRegisterMem2; + realRegisterMem2 = temp; + } + + bool signExtend = imlInstruction->op_storeLoad.flags2.signExtend; + bool switchEndian = imlInstruction->op_storeLoad.flags2.swapEndian; + if( imlInstruction->op_storeLoad.copyWidth == 32 ) + { + //if( indexed ) + // PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); + if (indexed) + { + x64Gen_lea_reg64Low32_reg64Low32PlusReg64Low32(x64GenContext, REG_RESV_TEMP, realRegisterMem, realRegisterMem2); + } + if( g_CPUFeatures.x86.movbe && switchEndian ) + { + if (indexed) + { + x64Gen_movBEZeroExtend_reg64_mem32Reg64PlusReg64(x64GenContext, realRegisterData, X86_REG_R13, REG_RESV_TEMP, imlInstruction->op_storeLoad.immS32); + //if (indexed && realRegisterMem != realRegisterData) + // x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); + } + else + { + x64Gen_movBEZeroExtend_reg64_mem32Reg64PlusReg64(x64GenContext, realRegisterData, X86_REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32); + } + } + else + { + if (indexed) + { + x64Emit_mov_reg32_mem32(x64GenContext, realRegisterData, X86_REG_R13, REG_RESV_TEMP, imlInstruction->op_storeLoad.immS32); + //if (realRegisterMem != realRegisterData) + // x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); + if (switchEndian) + x64Gen_bswap_reg64Lower32bit(x64GenContext, realRegisterData); + } + else + { + x64Emit_mov_reg32_mem32(x64GenContext, realRegisterData, X86_REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32); + if (switchEndian) + x64Gen_bswap_reg64Lower32bit(x64GenContext, realRegisterData); + } + } + } + else if( imlInstruction->op_storeLoad.copyWidth == 16 ) + { + if (indexed) + { + x64Gen_add_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); + } + if(g_CPUFeatures.x86.movbe && switchEndian ) + { + x64Gen_movBEZeroExtend_reg64Low16_mem16Reg64PlusReg64(x64GenContext, realRegisterData, X86_REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32); + if( indexed && realRegisterMem != realRegisterData ) + x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); + } + else + { + x64Gen_movZeroExtend_reg64Low16_mem16Reg64PlusReg64(x64GenContext, realRegisterData, X86_REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32); + if( indexed && realRegisterMem != realRegisterData ) + x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); + if( switchEndian ) + x64Gen_rol_reg64Low16_imm8(x64GenContext, realRegisterData, 8); + } + if( signExtend ) + x64Gen_movSignExtend_reg64Low32_reg64Low16(x64GenContext, realRegisterData, realRegisterData); + else + x64Gen_movZeroExtend_reg64Low32_reg64Low16(x64GenContext, realRegisterData, realRegisterData); + } + else if( imlInstruction->op_storeLoad.copyWidth == 8 ) + { + if( indexed ) + x64Gen_add_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); + if( signExtend ) + x64Gen_movSignExtend_reg64Low32_mem8Reg64PlusReg64(x64GenContext, realRegisterData, X86_REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32); + else + x64Emit_movZX_reg32_mem8(x64GenContext, realRegisterData, X86_REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32); + if( indexed && realRegisterMem != realRegisterData ) + x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); + } + else + return false; + return true; +} + +/* +* Write to memory +*/ +bool PPCRecompilerX64Gen_imlInstruction_store(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction, bool indexed) +{ + cemu_assert_debug(imlInstruction->op_storeLoad.registerData.GetRegFormat() == IMLRegFormat::I32); + cemu_assert_debug(imlInstruction->op_storeLoad.registerMem.GetRegFormat() == IMLRegFormat::I32); + if (indexed) + cemu_assert_debug(imlInstruction->op_storeLoad.registerMem2.GetRegFormat() == IMLRegFormat::I32); + + IMLRegID realRegisterData = imlInstruction->op_storeLoad.registerData.GetRegID(); + IMLRegID realRegisterMem = imlInstruction->op_storeLoad.registerMem.GetRegID(); + IMLRegID realRegisterMem2 = PPC_REC_INVALID_REGISTER; + if (indexed) + realRegisterMem2 = imlInstruction->op_storeLoad.registerMem2.GetRegID(); + + if (indexed && realRegisterMem == realRegisterMem2) + { + return false; + } + if (indexed && realRegisterData == realRegisterMem2) + { + // for indexed memory access realRegisterData must not be the same register as the second memory register, + // this can easily be fixed by swapping the logic of realRegisterMem and realRegisterMem2 + sint32 temp = realRegisterMem; + realRegisterMem = realRegisterMem2; + realRegisterMem2 = temp; + } + + bool signExtend = imlInstruction->op_storeLoad.flags2.signExtend; + bool swapEndian = imlInstruction->op_storeLoad.flags2.swapEndian; + if (imlInstruction->op_storeLoad.copyWidth == 32) + { + uint32 valueRegister; + if ((swapEndian == false || g_CPUFeatures.x86.movbe) && realRegisterMem != realRegisterData) + { + valueRegister = realRegisterData; + } + else + { + x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, realRegisterData); + valueRegister = REG_RESV_TEMP; + } + if (!g_CPUFeatures.x86.movbe && swapEndian) + x64Gen_bswap_reg64Lower32bit(x64GenContext, valueRegister); + if (indexed) + x64Gen_add_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); + if (g_CPUFeatures.x86.movbe && swapEndian) + x64Gen_movBETruncate_mem32Reg64PlusReg64_reg64(x64GenContext, X86_REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32, valueRegister); + else + x64Gen_movTruncate_mem32Reg64PlusReg64_reg64(x64GenContext, X86_REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32, valueRegister); + if (indexed) + x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); + } + else if (imlInstruction->op_storeLoad.copyWidth == 16) + { + x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, realRegisterData); + if (swapEndian) + x64Gen_rol_reg64Low16_imm8(x64GenContext, REG_RESV_TEMP, 8); + if (indexed) + x64Gen_add_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); + x64Gen_movTruncate_mem16Reg64PlusReg64_reg64(x64GenContext, X86_REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32, REG_RESV_TEMP); + if (indexed) + x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); + // todo: Optimize this, e.g. by using MOVBE + } + else if (imlInstruction->op_storeLoad.copyWidth == 8) + { + if (indexed && realRegisterMem == realRegisterData) + { + x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, realRegisterData); + realRegisterData = REG_RESV_TEMP; + } + if (indexed) + x64Gen_add_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); + x64Gen_movTruncate_mem8Reg64PlusReg64_reg64(x64GenContext, REG_RESV_MEMBASE, realRegisterMem, imlInstruction->op_storeLoad.immS32, realRegisterData); + if (indexed) + x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); + } + else + return false; + return true; +} + +bool PPCRecompilerX64Gen_imlInstruction_atomic_cmp_store(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction) +{ + auto regBoolOut = _reg32_from_reg8(_reg8(imlInstruction->op_atomic_compare_store.regBoolOut)); + auto regEA = _reg32(imlInstruction->op_atomic_compare_store.regEA); + auto regVal = _reg32(imlInstruction->op_atomic_compare_store.regWriteValue); + auto regCmp = _reg32(imlInstruction->op_atomic_compare_store.regCompareValue); + + // make sure non of the regs are in EAX + if (regEA == X86_REG_EAX || + regBoolOut == X86_REG_EAX || + regVal == X86_REG_EAX || + regCmp == X86_REG_EAX) + { + printf("x86: atomic_cmp_store cannot emit due to EAX already being in use\n"); + return false; + } + + x64GenContext->emitter->XCHG_qq(REG_RESV_TEMP, X86_REG_RAX); + x64GenContext->emitter->MOV_dd(X86_REG_EAX, regCmp); + x64GenContext->emitter->XOR_dd(_reg32_from_reg8(regBoolOut), _reg32_from_reg8(regBoolOut)); // zero bytes unaffected by SETcc + x64GenContext->emitter->LockPrefix(); + x64GenContext->emitter->CMPXCHG_dd_l(REG_RESV_MEMBASE, 0, _reg64_from_reg32(regEA), 1, regVal); + x64GenContext->emitter->SETcc_b(X86Cond::X86_CONDITION_Z, regBoolOut); + x64GenContext->emitter->XCHG_qq(REG_RESV_TEMP, X86_REG_RAX); + return true; +} + +bool PPCRecompilerX64Gen_imlInstruction_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction) +{ + auto regR = _reg32(imlInstruction->op_r_r.regR); + auto regA = _reg32(imlInstruction->op_r_r.regA); + + if (imlInstruction->operation == PPCREC_IML_OP_ASSIGN) + { + // registerResult = registerA + if (regR != regA) + x64Gen_mov_reg64Low32_reg64Low32(x64GenContext, regR, regA); + } + else if (imlInstruction->operation == PPCREC_IML_OP_ENDIAN_SWAP) + { + if (regA != regR) + x64Gen_mov_reg64Low32_reg64Low32(x64GenContext, regR, regA); // if movbe is available we can move and swap in a single instruction? + x64Gen_bswap_reg64Lower32bit(x64GenContext, regR); + } + else if( imlInstruction->operation == PPCREC_IML_OP_ASSIGN_S8_TO_S32 ) + { + x64Gen_movSignExtend_reg64Low32_reg64Low8(x64GenContext, regR, regA); + } + else if (imlInstruction->operation == PPCREC_IML_OP_ASSIGN_S16_TO_S32) + { + x64Gen_movSignExtend_reg64Low32_reg64Low16(x64GenContext, regR, reg32ToReg16(regA)); + } + else if( imlInstruction->operation == PPCREC_IML_OP_NOT ) + { + // copy register content if different registers + if( regR != regA ) + x64Gen_mov_reg64_reg64(x64GenContext, regR, regA); + x64Gen_not_reg64Low32(x64GenContext, regR); + } + else if (imlInstruction->operation == PPCREC_IML_OP_NEG) + { + // copy register content if different registers + if (regR != regA) + x64Gen_mov_reg64_reg64(x64GenContext, regR, regA); + x64Gen_neg_reg64Low32(x64GenContext, regR); + } + else if( imlInstruction->operation == PPCREC_IML_OP_CNTLZW ) + { + // count leading zeros + // LZCNT instruction (part of SSE4, CPUID.80000001H:ECX.ABM[Bit 5]) + if(g_CPUFeatures.x86.lzcnt) + { + x64Gen_lzcnt_reg64Low32_reg64Low32(x64GenContext, regR, regA); + } + else + { + x64Gen_test_reg64Low32_reg64Low32(x64GenContext, regA, regA); + sint32 jumpInstructionOffset1 = x64GenContext->emitter->GetWriteIndex(); + x64Gen_jmpc_near(x64GenContext, X86_CONDITION_EQUAL, 0); + x64Gen_bsr_reg64Low32_reg64Low32(x64GenContext, regR, regA); + x64Gen_neg_reg64Low32(x64GenContext, regR); + x64Gen_add_reg64Low32_imm32(x64GenContext, regR, 32-1); + sint32 jumpInstructionOffset2 = x64GenContext->emitter->GetWriteIndex(); + x64Gen_jmpc_near(x64GenContext, X86_CONDITION_NONE, 0); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset1, x64GenContext->emitter->GetWriteIndex()); + x64Gen_mov_reg64Low32_imm32(x64GenContext, regR, 32); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset2, x64GenContext->emitter->GetWriteIndex()); + } + } + else if( imlInstruction->operation == PPCREC_IML_OP_DCBZ ) + { + if( regR != regA ) + { + x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, regA); + x64Gen_add_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, regR); + x64Gen_and_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, ~0x1F); + x64Gen_add_reg64_reg64(x64GenContext, REG_RESV_TEMP, REG_RESV_MEMBASE); + for(sint32 f=0; f<0x20; f+=8) + x64Gen_mov_mem64Reg64_imm32(x64GenContext, REG_RESV_TEMP, f, 0); + } + else + { + // calculate effective address + x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, regA); + x64Gen_and_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, ~0x1F); + x64Gen_add_reg64_reg64(x64GenContext, REG_RESV_TEMP, REG_RESV_MEMBASE); + for(sint32 f=0; f<0x20; f+=8) + x64Gen_mov_mem64Reg64_imm32(x64GenContext, REG_RESV_TEMP, f, 0); + } + } + else + { + debug_printf("PPCRecompilerX64Gen_imlInstruction_r_r(): Unsupported operation 0x%x\n", imlInstruction->operation); + return false; + } + return true; +} + +bool PPCRecompilerX64Gen_imlInstruction_r_s32(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction) +{ + auto regR = _reg32(imlInstruction->op_r_immS32.regR); + + if( imlInstruction->operation == PPCREC_IML_OP_ASSIGN ) + { + x64Gen_mov_reg64Low32_imm32(x64GenContext, regR, (uint32)imlInstruction->op_r_immS32.immS32); + } + else if( imlInstruction->operation == PPCREC_IML_OP_LEFT_ROTATE ) + { + cemu_assert_debug((imlInstruction->op_r_immS32.immS32 & 0x80) == 0); + x64Gen_rol_reg64Low32_imm8(x64GenContext, regR, (uint8)imlInstruction->op_r_immS32.immS32); + } + else + { + debug_printf("PPCRecompilerX64Gen_imlInstruction_r_s32(): Unsupported operation 0x%x\n", imlInstruction->operation); + return false; + } + return true; +} + +bool PPCRecompilerX64Gen_imlInstruction_conditional_r_s32(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction) +{ + cemu_assert_unimplemented(); + //if (imlInstruction->operation == PPCREC_IML_OP_ASSIGN) + //{ + // // registerResult = immS32 (conditional) + // if (imlInstruction->crRegister != PPC_REC_INVALID_REGISTER) + // { + // assert_dbg(); + // } + + // x64Gen_mov_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, (uint32)imlInstruction->op_conditional_r_s32.immS32); + // uint8 crBitIndex = imlInstruction->op_conditional_r_s32.crRegisterIndex * 4 + imlInstruction->op_conditional_r_s32.crBitIndex; + // x64Gen_bt_mem8(x64GenContext, X86_REG_RSP, offsetof(PPCInterpreter_t, cr) + crBitIndex * sizeof(uint8), 0); + // if (imlInstruction->op_conditional_r_s32.bitMustBeSet) + // x64Gen_cmovcc_reg64Low32_reg64Low32(x64GenContext, X86_CONDITION_CARRY, imlInstruction->op_conditional_r_s32.registerIndex, REG_RESV_TEMP); + // else + // x64Gen_cmovcc_reg64Low32_reg64Low32(x64GenContext, X86_CONDITION_NOT_CARRY, imlInstruction->op_conditional_r_s32.registerIndex, REG_RESV_TEMP); + // return true; + //} + return false; +} + +bool PPCRecompilerX64Gen_imlInstruction_r_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction) +{ + auto rRegResult = _reg32(imlInstruction->op_r_r_r.regR); + auto rRegOperand1 = _reg32(imlInstruction->op_r_r_r.regA); + auto rRegOperand2 = _reg32(imlInstruction->op_r_r_r.regB); + + if (imlInstruction->operation == PPCREC_IML_OP_ADD) + { + // registerResult = registerOperand1 + registerOperand2 + if( (rRegResult == rRegOperand1) || (rRegResult == rRegOperand2) ) + { + // be careful not to overwrite the operand before we use it + if( rRegResult == rRegOperand1 ) + x64Gen_add_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand2); + else + x64Gen_add_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand1); + } + else + { + // copy operand1 to destination register before doing addition + x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, rRegOperand1); + x64Gen_add_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand2); + } + } + else if( imlInstruction->operation == PPCREC_IML_OP_SUB ) + { + if( rRegOperand1 == rRegOperand2 ) + { + // result = operand1 - operand1 -> 0 + x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegResult); + } + else if( rRegResult == rRegOperand1 ) + { + // result = result - operand2 + x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand2); + } + else if ( rRegResult == rRegOperand2 ) + { + // result = operand1 - result + x64Gen_neg_reg64Low32(x64GenContext, rRegResult); + x64Gen_add_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand1); + } + else + { + x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, rRegOperand1); + x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand2); + } + } + else if (imlInstruction->operation == PPCREC_IML_OP_OR || imlInstruction->operation == PPCREC_IML_OP_AND || imlInstruction->operation == PPCREC_IML_OP_XOR) + { + if (rRegResult == rRegOperand2) + std::swap(rRegOperand1, rRegOperand2); + + if (rRegResult != rRegOperand1) + x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, rRegOperand1); + + if (imlInstruction->operation == PPCREC_IML_OP_OR) + x64Gen_or_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand2); + else if (imlInstruction->operation == PPCREC_IML_OP_AND) + x64Gen_and_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand2); + else + x64Gen_xor_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand2); + } + else if( imlInstruction->operation == PPCREC_IML_OP_MULTIPLY_SIGNED ) + { + // registerResult = registerOperand1 * registerOperand2 + if( (rRegResult == rRegOperand1) || (rRegResult == rRegOperand2) ) + { + // be careful not to overwrite the operand before we use it + if( rRegResult == rRegOperand1 ) + x64Gen_imul_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand2); + else + x64Gen_imul_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand1); + } + else + { + // copy operand1 to destination register before doing multiplication + x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, rRegOperand1); + // add operand2 + x64Gen_imul_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand2); + } + } + else if( imlInstruction->operation == PPCREC_IML_OP_SLW || imlInstruction->operation == PPCREC_IML_OP_SRW ) + { + // registerResult = registerOperand1(rA) >> registerOperand2(rB) (up to 63 bits) + + if (g_CPUFeatures.x86.bmi2 && imlInstruction->operation == PPCREC_IML_OP_SRW) + { + // use BMI2 SHRX if available + x64Gen_shrx_reg64_reg64_reg64(x64GenContext, rRegResult, rRegOperand1, rRegOperand2); + } + else if (g_CPUFeatures.x86.bmi2 && imlInstruction->operation == PPCREC_IML_OP_SLW) + { + // use BMI2 SHLX if available + x64Gen_shlx_reg64_reg64_reg64(x64GenContext, rRegResult, rRegOperand1, rRegOperand2); + x64Gen_and_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegResult); // trim result to 32bit + } + else + { + // lazy and slow way to do shift by register without relying on ECX/CL or BMI2 + x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, rRegOperand1); + for (sint32 b = 0; b < 6; b++) + { + x64Gen_test_reg64Low32_imm32(x64GenContext, rRegOperand2, (1 << b)); + sint32 jumpInstructionOffset = x64GenContext->emitter->GetWriteIndex(); + x64Gen_jmpc_near(x64GenContext, X86_CONDITION_EQUAL, 0); // jump if bit not set + if (b == 5) + { + x64Gen_xor_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, REG_RESV_TEMP); + } + else + { + if (imlInstruction->operation == PPCREC_IML_OP_SLW) + x64Gen_shl_reg64Low32_imm8(x64GenContext, REG_RESV_TEMP, (1 << b)); + else + x64Gen_shr_reg64Low32_imm8(x64GenContext, REG_RESV_TEMP, (1 << b)); + } + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset, x64GenContext->emitter->GetWriteIndex()); + } + x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, REG_RESV_TEMP); + } + } + else if( imlInstruction->operation == PPCREC_IML_OP_LEFT_ROTATE ) + { + // todo: Use BMI2 rotate if available + // check if CL/ECX/RCX is available + if( rRegResult != X86_REG_RCX && rRegOperand1 != X86_REG_RCX && rRegOperand2 != X86_REG_RCX ) + { + // swap operand 2 with RCX + x64Gen_xchg_reg64_reg64(x64GenContext, X86_REG_RCX, rRegOperand2); + // move operand 1 to temp register + x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, rRegOperand1); + // rotate + x64Gen_rol_reg64Low32_cl(x64GenContext, REG_RESV_TEMP); + // undo swap operand 2 with RCX + x64Gen_xchg_reg64_reg64(x64GenContext, X86_REG_RCX, rRegOperand2); + // copy to result register + x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, REG_RESV_TEMP); + } + else + { + x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, rRegOperand1); + // lazy and slow way to do shift by register without relying on ECX/CL + for(sint32 b=0; b<5; b++) + { + x64Gen_test_reg64Low32_imm32(x64GenContext, rRegOperand2, (1<emitter->GetWriteIndex(); + x64Gen_jmpc_near(x64GenContext, X86_CONDITION_EQUAL, 0); // jump if bit not set + x64Gen_rol_reg64Low32_imm8(x64GenContext, REG_RESV_TEMP, (1<emitter->GetWriteIndex()); + } + x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, REG_RESV_TEMP); + } + } + else if (imlInstruction->operation == PPCREC_IML_OP_RIGHT_SHIFT_S || + imlInstruction->operation == PPCREC_IML_OP_RIGHT_SHIFT_U || + imlInstruction->operation == PPCREC_IML_OP_LEFT_SHIFT) + { + // x86's shift and rotate instruction have the shift amount hardwired to the CL register + // since our register allocator doesn't support instruction based fixed phys registers yet + // we'll instead have to temporarily shuffle registers around + + // we use BMI2's shift instructions until the RA can assign fixed registers + if (imlInstruction->operation == PPCREC_IML_OP_RIGHT_SHIFT_S) + { + x64Gen_sarx_reg32_reg32_reg32(x64GenContext, rRegResult, rRegOperand1, rRegOperand2); + } + else if (imlInstruction->operation == PPCREC_IML_OP_RIGHT_SHIFT_U) + { + x64Gen_shrx_reg32_reg32_reg32(x64GenContext, rRegResult, rRegOperand1, rRegOperand2); + } + else if (imlInstruction->operation == PPCREC_IML_OP_LEFT_SHIFT) + { + x64Gen_shlx_reg32_reg32_reg32(x64GenContext, rRegResult, rRegOperand1, rRegOperand2); + } + + //auto rResult = _reg32(rRegResult); + //auto rOp2 = _reg8_from_reg32(_reg32(rRegOperand2)); + + //if (rRegResult == rRegOperand2) + //{ + // if (rRegResult != rRegOperand1) + // DEBUG_BREAK; // cannot handle yet (we use rRegResult as a temporary reg, but its not possible if it is shared with op2) + //} + + //if(rRegOperand1 != rRegResult) + // x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, rRegOperand1); + + //cemu_assert_debug(rRegOperand1 != X86_REG_ECX); + + //if (rRegOperand2 == X86_REG_ECX) + //{ + // if (imlInstruction->operation == PPCREC_IML_OP_RIGHT_SHIFT_S) + // x64GenContext->emitter->SAR_d_CL(rResult); + // else if (imlInstruction->operation == PPCREC_IML_OP_RIGHT_SHIFT_U) + // x64GenContext->emitter->SHR_d_CL(rResult); + // else if (imlInstruction->operation == PPCREC_IML_OP_LEFT_SHIFT) + // x64GenContext->emitter->SHL_d_CL(rResult); + // else + // cemu_assert_unimplemented(); + //} + //else + //{ + // auto rRegResultOrg = rRegResult; + // if (rRegResult == X86_REG_ECX) + // { + // x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, rRegResult); + // rRegResult = REG_RESV_TEMP; + // rResult = _reg32(rRegResult); + // } + // + // x64Gen_xchg_reg64_reg64(x64GenContext, X86_REG_RCX, rRegOperand2); + // + // if (imlInstruction->operation == PPCREC_IML_OP_RIGHT_SHIFT_S) + // x64GenContext->emitter->SAR_d_CL(rResult); + // else if (imlInstruction->operation == PPCREC_IML_OP_RIGHT_SHIFT_U) + // x64GenContext->emitter->SHR_d_CL(rResult); + // else if (imlInstruction->operation == PPCREC_IML_OP_LEFT_SHIFT) + // x64GenContext->emitter->SHL_d_CL(rResult); + // else + // cemu_assert_unimplemented(); + + // x64Gen_xchg_reg64_reg64(x64GenContext, X86_REG_RCX, rRegOperand2); + + // // move result back if it was in ECX + // if (rRegResultOrg == X86_REG_ECX) + // { + // x64Gen_mov_reg64_reg64(x64GenContext, rRegResultOrg, REG_RESV_TEMP); + // } + //} + } + else if( imlInstruction->operation == PPCREC_IML_OP_DIVIDE_SIGNED || imlInstruction->operation == PPCREC_IML_OP_DIVIDE_UNSIGNED ) + { + x64Emit_mov_mem32_reg32(x64GenContext, X86_REG_RSP, (uint32)offsetof(PPCInterpreter_t, temporaryGPR[0]), X86_REG_EAX); + x64Emit_mov_mem32_reg32(x64GenContext, X86_REG_RSP, (uint32)offsetof(PPCInterpreter_t, temporaryGPR[1]), X86_REG_EDX); + // mov operand 2 to temp register + x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, rRegOperand2); + // mov operand1 to EAX + x64Gen_mov_reg64Low32_reg64Low32(x64GenContext, X86_REG_EAX, rRegOperand1); + // sign or zero extend EAX to EDX:EAX based on division sign mode + if( imlInstruction->operation == PPCREC_IML_OP_DIVIDE_SIGNED ) + x64Gen_cdq(x64GenContext); + else + x64Gen_xor_reg64Low32_reg64Low32(x64GenContext, X86_REG_EDX, X86_REG_EDX); + // make sure we avoid division by zero + x64Gen_test_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, REG_RESV_TEMP); + x64Gen_jmpc_near(x64GenContext, X86_CONDITION_EQUAL, 3); + // divide + if( imlInstruction->operation == PPCREC_IML_OP_DIVIDE_SIGNED ) + x64Gen_idiv_reg64Low32(x64GenContext, REG_RESV_TEMP); + else + x64Gen_div_reg64Low32(x64GenContext, REG_RESV_TEMP); + // result of division is now stored in EAX, move it to result register + if( rRegResult != X86_REG_EAX ) + x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, X86_REG_EAX); + // restore EAX / EDX + if( rRegResult != X86_REG_RAX ) + x64Emit_mov_reg64_mem32(x64GenContext, X86_REG_EAX, X86_REG_RSP, (uint32)offsetof(PPCInterpreter_t, temporaryGPR[0])); + if( rRegResult != X86_REG_RDX ) + x64Emit_mov_reg64_mem32(x64GenContext, X86_REG_EDX, X86_REG_RSP, (uint32)offsetof(PPCInterpreter_t, temporaryGPR[1])); + } + else if( imlInstruction->operation == PPCREC_IML_OP_MULTIPLY_HIGH_SIGNED || imlInstruction->operation == PPCREC_IML_OP_MULTIPLY_HIGH_UNSIGNED ) + { + x64Emit_mov_mem32_reg32(x64GenContext, X86_REG_RSP, (uint32)offsetof(PPCInterpreter_t, temporaryGPR[0]), X86_REG_EAX); + x64Emit_mov_mem32_reg32(x64GenContext, X86_REG_RSP, (uint32)offsetof(PPCInterpreter_t, temporaryGPR[1]), X86_REG_EDX); + // mov operand 2 to temp register + x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, rRegOperand2); + // mov operand1 to EAX + x64Gen_mov_reg64Low32_reg64Low32(x64GenContext, X86_REG_EAX, rRegOperand1); + if( imlInstruction->operation == PPCREC_IML_OP_MULTIPLY_HIGH_SIGNED ) + { + // zero extend EAX to EDX:EAX + x64Gen_xor_reg64Low32_reg64Low32(x64GenContext, X86_REG_EDX, X86_REG_EDX); + } + else + { + // sign extend EAX to EDX:EAX + x64Gen_cdq(x64GenContext); + } + // multiply + if( imlInstruction->operation == PPCREC_IML_OP_MULTIPLY_HIGH_SIGNED ) + x64Gen_imul_reg64Low32(x64GenContext, REG_RESV_TEMP); + else + x64Gen_mul_reg64Low32(x64GenContext, REG_RESV_TEMP); + // result of multiplication is now stored in EDX:EAX, move it to result register + if( rRegResult != X86_REG_EDX ) + x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, X86_REG_EDX); + // restore EAX / EDX + if( rRegResult != X86_REG_RAX ) + x64Emit_mov_reg64_mem32(x64GenContext, X86_REG_EAX, X86_REG_RSP, (uint32)offsetof(PPCInterpreter_t, temporaryGPR[0])); + if( rRegResult != X86_REG_RDX ) + x64Emit_mov_reg64_mem32(x64GenContext, X86_REG_EDX, X86_REG_RSP, (uint32)offsetof(PPCInterpreter_t, temporaryGPR[1])); + } + else + { + debug_printf("PPCRecompilerX64Gen_imlInstruction_r_r_r(): Unsupported operation 0x%x\n", imlInstruction->operation); + return false; + } + return true; +} + +bool PPCRecompilerX64Gen_imlInstruction_r_r_r_carry(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction) +{ + auto regR = _reg32(imlInstruction->op_r_r_r_carry.regR); + auto regA = _reg32(imlInstruction->op_r_r_r_carry.regA); + auto regB = _reg32(imlInstruction->op_r_r_r_carry.regB); + auto regCarry = _reg32(imlInstruction->op_r_r_r_carry.regCarry); + cemu_assert_debug(regCarry != regR && regCarry != regA); + + switch (imlInstruction->operation) + { + case PPCREC_IML_OP_ADD: + if (regB == regR) + std::swap(regB, regA); + if (regR != regA) + x64GenContext->emitter->MOV_dd(regR, regA); + x64GenContext->emitter->XOR_dd(regCarry, regCarry); + x64GenContext->emitter->ADD_dd(regR, regB); + x64GenContext->emitter->SETcc_b(X86_CONDITION_B, _reg8_from_reg32(regCarry)); // below condition checks carry flag + break; + case PPCREC_IML_OP_ADD_WITH_CARRY: + // assumes that carry is already correctly initialized as 0 or 1 + if (regB == regR) + std::swap(regB, regA); + if (regR != regA) + x64GenContext->emitter->MOV_dd(regR, regA); + x64GenContext->emitter->BT_du8(regCarry, 0); // copy carry register to x86 carry flag + x64GenContext->emitter->ADC_dd(regR, regB); + x64GenContext->emitter->SETcc_b(X86_CONDITION_B, _reg8_from_reg32(regCarry)); + break; + default: + cemu_assert_unimplemented(); + return false; + } + return true; +} + +bool PPCRecompilerX64Gen_imlInstruction_compare(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction) +{ + auto regR = _reg8(imlInstruction->op_compare.regR); + auto regA = _reg32(imlInstruction->op_compare.regA); + auto regB = _reg32(imlInstruction->op_compare.regB); + X86Cond cond = _x86Cond(imlInstruction->op_compare.cond); + x64GenContext->emitter->XOR_dd(_reg32_from_reg8(regR), _reg32_from_reg8(regR)); // zero bytes unaffected by SETcc + x64GenContext->emitter->CMP_dd(regA, regB); + x64GenContext->emitter->SETcc_b(cond, regR); + return true; +} + +bool PPCRecompilerX64Gen_imlInstruction_compare_s32(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction) +{ + auto regR = _reg8(imlInstruction->op_compare_s32.regR); + auto regA = _reg32(imlInstruction->op_compare_s32.regA); + sint32 imm = imlInstruction->op_compare_s32.immS32; + X86Cond cond = _x86Cond(imlInstruction->op_compare_s32.cond); + x64GenContext->emitter->XOR_dd(_reg32_from_reg8(regR), _reg32_from_reg8(regR)); // zero bytes unaffected by SETcc + x64GenContext->emitter->CMP_di32(regA, imm); + x64GenContext->emitter->SETcc_b(cond, regR); + return true; +} + +bool PPCRecompilerX64Gen_imlInstruction_cjump2(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction, IMLSegment* imlSegment) +{ + auto regBool = _reg8(imlInstruction->op_conditional_jump.registerBool); + bool mustBeTrue = imlInstruction->op_conditional_jump.mustBeTrue; + x64GenContext->emitter->TEST_bb(regBool, regBool); + PPCRecompilerX64Gen_rememberRelocatableOffset(x64GenContext, imlSegment->nextSegmentBranchTaken); + x64GenContext->emitter->Jcc_j32(mustBeTrue ? X86_CONDITION_NZ : X86_CONDITION_Z, 0); + return true; +} + +bool PPCRecompilerX64Gen_imlInstruction_jump2(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction, IMLSegment* imlSegment) +{ + PPCRecompilerX64Gen_rememberRelocatableOffset(x64GenContext, imlSegment->nextSegmentBranchTaken); + x64GenContext->emitter->JMP_j32(0); + return true; +} + +bool PPCRecompilerX64Gen_imlInstruction_r_r_s32(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction) +{ + auto regR = _reg32(imlInstruction->op_r_r_s32.regR); + auto regA = _reg32(imlInstruction->op_r_r_s32.regA); + uint32 immS32 = imlInstruction->op_r_r_s32.immS32; + + if( imlInstruction->operation == PPCREC_IML_OP_ADD ) + { + uint32 immU32 = (uint32)imlInstruction->op_r_r_s32.immS32; + if(regR != regA) + x64Gen_mov_reg64_reg64(x64GenContext, regR, regA); + x64Gen_add_reg64Low32_imm32(x64GenContext, regR, (uint32)immU32); + } + else if (imlInstruction->operation == PPCREC_IML_OP_SUB) + { + if (regR != regA) + x64Gen_mov_reg64_reg64(x64GenContext, regR, regA); + x64Gen_sub_reg64Low32_imm32(x64GenContext, regR, immS32); + } + else if (imlInstruction->operation == PPCREC_IML_OP_AND || + imlInstruction->operation == PPCREC_IML_OP_OR || + imlInstruction->operation == PPCREC_IML_OP_XOR) + { + if (regR != regA) + x64Gen_mov_reg64_reg64(x64GenContext, regR, regA); + if (imlInstruction->operation == PPCREC_IML_OP_AND) + x64Gen_and_reg64Low32_imm32(x64GenContext, regR, immS32); + else if (imlInstruction->operation == PPCREC_IML_OP_OR) + x64Gen_or_reg64Low32_imm32(x64GenContext, regR, immS32); + else // XOR + x64Gen_xor_reg64Low32_imm32(x64GenContext, regR, immS32); + } + else if( imlInstruction->operation == PPCREC_IML_OP_RLWIMI ) + { + // registerResult = ((registerResult<<op_r_r_s32.immS32; + uint32 mb = (vImm>>0)&0xFF; + uint32 me = (vImm>>8)&0xFF; + uint32 sh = (vImm>>16)&0xFF; + uint32 mask = ppc_mask(mb, me); + // copy rS to temporary register + x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, regA); + // rotate destination register + if( sh ) + x64Gen_rol_reg64Low32_imm8(x64GenContext, REG_RESV_TEMP, (uint8)sh&0x1F); + // AND destination register with inverted mask + x64Gen_and_reg64Low32_imm32(x64GenContext, regR, ~mask); + // AND temporary rS register with mask + x64Gen_and_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, mask); + // OR result with temporary + x64Gen_or_reg64Low32_reg64Low32(x64GenContext, regR, REG_RESV_TEMP); + } + else if( imlInstruction->operation == PPCREC_IML_OP_MULTIPLY_SIGNED ) + { + // registerResult = registerOperand * immS32 + sint32 immS32 = (uint32)imlInstruction->op_r_r_s32.immS32; + x64Gen_mov_reg64_imm64(x64GenContext, REG_RESV_TEMP, (sint64)immS32); // todo: Optimize + if( regR != regA ) + x64Gen_mov_reg64_reg64(x64GenContext, regR, regA); + x64Gen_imul_reg64Low32_reg64Low32(x64GenContext, regR, REG_RESV_TEMP); + } + else if (imlInstruction->operation == PPCREC_IML_OP_LEFT_SHIFT || + imlInstruction->operation == PPCREC_IML_OP_RIGHT_SHIFT_U || + imlInstruction->operation == PPCREC_IML_OP_RIGHT_SHIFT_S) + { + if( regA != regR ) + x64Gen_mov_reg64_reg64(x64GenContext, regR, regA); + + if (imlInstruction->operation == PPCREC_IML_OP_LEFT_SHIFT) + x64Gen_shl_reg64Low32_imm8(x64GenContext, regR, imlInstruction->op_r_r_s32.immS32); + else if (imlInstruction->operation == PPCREC_IML_OP_RIGHT_SHIFT_U) + x64Gen_shr_reg64Low32_imm8(x64GenContext, regR, imlInstruction->op_r_r_s32.immS32); + else // RIGHT_SHIFT_S + x64Gen_sar_reg64Low32_imm8(x64GenContext, regR, imlInstruction->op_r_r_s32.immS32); + } + else + { + debug_printf("PPCRecompilerX64Gen_imlInstruction_r_r_s32(): Unsupported operation 0x%x\n", imlInstruction->operation); + return false; + } + return true; +} + +bool PPCRecompilerX64Gen_imlInstruction_r_r_s32_carry(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction) +{ + auto regR = _reg32(imlInstruction->op_r_r_s32_carry.regR); + auto regA = _reg32(imlInstruction->op_r_r_s32_carry.regA); + sint32 immS32 = imlInstruction->op_r_r_s32_carry.immS32; + auto regCarry = _reg32(imlInstruction->op_r_r_s32_carry.regCarry); + cemu_assert_debug(regCarry != regR && regCarry != regA); + + switch (imlInstruction->operation) + { + case PPCREC_IML_OP_ADD: + x64GenContext->emitter->XOR_dd(regCarry, regCarry); + if (regR != regA) + x64GenContext->emitter->MOV_dd(regR, regA); + x64GenContext->emitter->ADD_di32(regR, immS32); + x64GenContext->emitter->SETcc_b(X86_CONDITION_B, _reg8_from_reg32(regCarry)); + break; + case PPCREC_IML_OP_ADD_WITH_CARRY: + // assumes that carry is already correctly initialized as 0 or 1 + if (regR != regA) + x64GenContext->emitter->MOV_dd(regR, regA); + x64GenContext->emitter->BT_du8(regCarry, 0); // copy carry register to x86 carry flag + x64GenContext->emitter->ADC_di32(regR, immS32); + x64GenContext->emitter->SETcc_b(X86_CONDITION_B, _reg8_from_reg32(regCarry)); + break; + default: + cemu_assert_unimplemented(); + return false; + } + return true; +} + +bool PPCRecompilerX64Gen_imlInstruction_conditionalJumpCycleCheck(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction) +{ + // some tests (all performed on a i7-4790K) + // 1) DEC [mem] + JNS has significantly worse performance than BT + JNC (probably due to additional memory write and direct dependency) + // 2) CMP [mem], 0 + JG has about equal (or slightly worse) performance than BT + JNC + + // BT + x64Gen_bt_mem8(x64GenContext, X86_REG_RSP, offsetof(PPCInterpreter_t, remainingCycles), 31); // check if negative + cemu_assert_debug(x64GenContext->currentSegment->GetBranchTaken()); + PPCRecompilerX64Gen_rememberRelocatableOffset(x64GenContext, x64GenContext->currentSegment->GetBranchTaken()); + x64Gen_jmpc_far(x64GenContext, X86_CONDITION_CARRY, 0); + return true; +} + +void PPCRecompilerX64Gen_imlInstruction_r_name(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction) +{ + uint32 name = imlInstruction->op_r_name.name; + if (imlInstruction->op_r_name.regR.GetBaseFormat() == IMLRegFormat::I64) + { + auto regR = _reg64(imlInstruction->op_r_name.regR); + if (name >= PPCREC_NAME_R0 && name < PPCREC_NAME_R0 + 32) + { + x64Emit_mov_reg64_mem32(x64GenContext, regR, X86_REG_RSP, offsetof(PPCInterpreter_t, gpr) + sizeof(uint32) * (name - PPCREC_NAME_R0)); + } + else if (name >= PPCREC_NAME_SPR0 && name < PPCREC_NAME_SPR0 + 999) + { + sint32 sprIndex = (name - PPCREC_NAME_SPR0); + if (sprIndex == SPR_LR) + x64Emit_mov_reg64_mem32(x64GenContext, regR, X86_REG_RSP, offsetof(PPCInterpreter_t, spr.LR)); + else if (sprIndex == SPR_CTR) + x64Emit_mov_reg64_mem32(x64GenContext, regR, X86_REG_RSP, offsetof(PPCInterpreter_t, spr.CTR)); + else if (sprIndex == SPR_XER) + x64Emit_mov_reg64_mem32(x64GenContext, regR, X86_REG_RSP, offsetof(PPCInterpreter_t, spr.XER)); + else if (sprIndex >= SPR_UGQR0 && sprIndex <= SPR_UGQR7) + { + sint32 memOffset = offsetof(PPCInterpreter_t, spr.UGQR) + sizeof(PPCInterpreter_t::spr.UGQR[0]) * (sprIndex - SPR_UGQR0); + x64Emit_mov_reg64_mem32(x64GenContext, regR, X86_REG_RSP, memOffset); + } + else + assert_dbg(); + } + else if (name >= PPCREC_NAME_TEMPORARY && name < PPCREC_NAME_TEMPORARY + 4) + { + x64Emit_mov_reg64_mem32(x64GenContext, regR, X86_REG_RSP, offsetof(PPCInterpreter_t, temporaryGPR_reg) + sizeof(uint32) * (name - PPCREC_NAME_TEMPORARY)); + } + else if (name == PPCREC_NAME_XER_CA) + { + x64Emit_movZX_reg64_mem8(x64GenContext, regR, X86_REG_RSP, offsetof(PPCInterpreter_t, xer_ca)); + } + else if (name == PPCREC_NAME_XER_SO) + { + x64Emit_movZX_reg64_mem8(x64GenContext, regR, X86_REG_RSP, offsetof(PPCInterpreter_t, xer_so)); + } + else if (name >= PPCREC_NAME_CR && name <= PPCREC_NAME_CR_LAST) + { + x64Emit_movZX_reg64_mem8(x64GenContext, regR, X86_REG_RSP, offsetof(PPCInterpreter_t, cr) + (name - PPCREC_NAME_CR)); + } + else if (name == PPCREC_NAME_CPU_MEMRES_EA) + { + x64Emit_mov_reg64_mem32(x64GenContext, regR, X86_REG_RSP, offsetof(PPCInterpreter_t, reservedMemAddr)); + } + else if (name == PPCREC_NAME_CPU_MEMRES_VAL) + { + x64Emit_mov_reg64_mem32(x64GenContext, regR, X86_REG_RSP, offsetof(PPCInterpreter_t, reservedMemValue)); + } + else + assert_dbg(); + } + else if (imlInstruction->op_r_name.regR.GetBaseFormat() == IMLRegFormat::F64) + { + auto regR = _regF64(imlInstruction->op_r_name.regR); + if (name >= PPCREC_NAME_FPR0 && name < (PPCREC_NAME_FPR0 + 32)) + { + x64Gen_movupd_xmmReg_memReg128(x64GenContext, regR, X86_REG_ESP, offsetof(PPCInterpreter_t, fpr) + sizeof(FPR_t) * (name - PPCREC_NAME_FPR0)); + } + else if (name >= PPCREC_NAME_TEMPORARY_FPR0 || name < (PPCREC_NAME_TEMPORARY_FPR0 + 8)) + { + x64Gen_movupd_xmmReg_memReg128(x64GenContext, regR, X86_REG_ESP, offsetof(PPCInterpreter_t, temporaryFPR) + sizeof(FPR_t) * (name - PPCREC_NAME_TEMPORARY_FPR0)); + } + else + { + cemu_assert_debug(false); + } + } + else + DEBUG_BREAK; + +} + +void PPCRecompilerX64Gen_imlInstruction_name_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction) +{ + uint32 name = imlInstruction->op_r_name.name; + + if (imlInstruction->op_r_name.regR.GetBaseFormat() == IMLRegFormat::I64) + { + auto regR = _reg64(imlInstruction->op_r_name.regR); + if (name >= PPCREC_NAME_R0 && name < PPCREC_NAME_R0 + 32) + { + x64Emit_mov_mem32_reg64(x64GenContext, X86_REG_RSP, offsetof(PPCInterpreter_t, gpr) + sizeof(uint32) * (name - PPCREC_NAME_R0), regR); + } + else if (name >= PPCREC_NAME_SPR0 && name < PPCREC_NAME_SPR0 + 999) + { + uint32 sprIndex = (name - PPCREC_NAME_SPR0); + if (sprIndex == SPR_LR) + x64Emit_mov_mem32_reg64(x64GenContext, X86_REG_RSP, offsetof(PPCInterpreter_t, spr.LR), regR); + else if (sprIndex == SPR_CTR) + x64Emit_mov_mem32_reg64(x64GenContext, X86_REG_RSP, offsetof(PPCInterpreter_t, spr.CTR), regR); + else if (sprIndex == SPR_XER) + x64Emit_mov_mem32_reg64(x64GenContext, X86_REG_RSP, offsetof(PPCInterpreter_t, spr.XER), regR); + else if (sprIndex >= SPR_UGQR0 && sprIndex <= SPR_UGQR7) + { + sint32 memOffset = offsetof(PPCInterpreter_t, spr.UGQR) + sizeof(PPCInterpreter_t::spr.UGQR[0]) * (sprIndex - SPR_UGQR0); + x64Emit_mov_mem32_reg64(x64GenContext, X86_REG_RSP, memOffset, regR); + } + else + assert_dbg(); + } + else if (name >= PPCREC_NAME_TEMPORARY && name < PPCREC_NAME_TEMPORARY + 4) + { + x64Emit_mov_mem32_reg64(x64GenContext, X86_REG_RSP, offsetof(PPCInterpreter_t, temporaryGPR_reg) + sizeof(uint32) * (name - PPCREC_NAME_TEMPORARY), regR); + } + else if (name == PPCREC_NAME_XER_CA) + { + x64GenContext->emitter->MOV_bb_l(X86_REG_RSP, offsetof(PPCInterpreter_t, xer_ca), X86_REG_NONE, 0, _reg8_from_reg64(regR)); + } + else if (name == PPCREC_NAME_XER_SO) + { + x64GenContext->emitter->MOV_bb_l(X86_REG_RSP, offsetof(PPCInterpreter_t, xer_so), X86_REG_NONE, 0, _reg8_from_reg64(regR)); + } + else if (name >= PPCREC_NAME_CR && name <= PPCREC_NAME_CR_LAST) + { + x64GenContext->emitter->MOV_bb_l(X86_REG_RSP, offsetof(PPCInterpreter_t, cr) + (name - PPCREC_NAME_CR), X86_REG_NONE, 0, _reg8_from_reg64(regR)); + } + else if (name == PPCREC_NAME_CPU_MEMRES_EA) + { + x64Emit_mov_mem32_reg64(x64GenContext, X86_REG_RSP, offsetof(PPCInterpreter_t, reservedMemAddr), regR); + } + else if (name == PPCREC_NAME_CPU_MEMRES_VAL) + { + x64Emit_mov_mem32_reg64(x64GenContext, X86_REG_RSP, offsetof(PPCInterpreter_t, reservedMemValue), regR); + } + else + assert_dbg(); + } + else if (imlInstruction->op_r_name.regR.GetBaseFormat() == IMLRegFormat::F64) + { + auto regR = _regF64(imlInstruction->op_r_name.regR); + uint32 name = imlInstruction->op_r_name.name; + if (name >= PPCREC_NAME_FPR0 && name < (PPCREC_NAME_FPR0 + 32)) + { + x64Gen_movupd_memReg128_xmmReg(x64GenContext, regR, X86_REG_ESP, offsetof(PPCInterpreter_t, fpr) + sizeof(FPR_t) * (name - PPCREC_NAME_FPR0)); + } + else if (name >= PPCREC_NAME_TEMPORARY_FPR0 && name < (PPCREC_NAME_TEMPORARY_FPR0 + 8)) + { + x64Gen_movupd_memReg128_xmmReg(x64GenContext, regR, X86_REG_ESP, offsetof(PPCInterpreter_t, temporaryFPR) + sizeof(FPR_t) * (name - PPCREC_NAME_TEMPORARY_FPR0)); + } + else + { + cemu_assert_debug(false); + } + } + else + DEBUG_BREAK; + + +} + +//void PPCRecompilerX64Gen_imlInstruction_fpr_r_name(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction) +//{ +// uint32 name = imlInstruction->op_r_name.name; +// uint32 fprReg = _regF64(imlInstruction->op_r_name.regR); +// if (name >= PPCREC_NAME_FPR0 && name < (PPCREC_NAME_FPR0 + 32)) +// { +// x64Gen_movupd_xmmReg_memReg128(x64GenContext, fprReg, X86_REG_ESP, offsetof(PPCInterpreter_t, fpr) + sizeof(FPR_t) * (name - PPCREC_NAME_FPR0)); +// } +// else if (name >= PPCREC_NAME_TEMPORARY_FPR0 || name < (PPCREC_NAME_TEMPORARY_FPR0 + 8)) +// { +// x64Gen_movupd_xmmReg_memReg128(x64GenContext, fprReg, X86_REG_ESP, offsetof(PPCInterpreter_t, temporaryFPR) + sizeof(FPR_t) * (name - PPCREC_NAME_TEMPORARY_FPR0)); +// } +// else +// { +// cemu_assert_debug(false); +// } +//} +// +//void PPCRecompilerX64Gen_imlInstruction_fpr_name_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction) +//{ +// uint32 name = imlInstruction->op_r_name.name; +// uint32 fprReg = _regF64(imlInstruction->op_r_name.regR); +// if (name >= PPCREC_NAME_FPR0 && name < (PPCREC_NAME_FPR0 + 32)) +// { +// x64Gen_movupd_memReg128_xmmReg(x64GenContext, fprReg, X86_REG_ESP, offsetof(PPCInterpreter_t, fpr) + sizeof(FPR_t) * (name - PPCREC_NAME_FPR0)); +// } +// else if (name >= PPCREC_NAME_TEMPORARY_FPR0 && name < (PPCREC_NAME_TEMPORARY_FPR0 + 8)) +// { +// x64Gen_movupd_memReg128_xmmReg(x64GenContext, fprReg, X86_REG_ESP, offsetof(PPCInterpreter_t, temporaryFPR) + sizeof(FPR_t) * (name - PPCREC_NAME_TEMPORARY_FPR0)); +// } +// else +// { +// cemu_assert_debug(false); +// } +//} + +uint8* codeMemoryBlock = nullptr; +sint32 codeMemoryBlockIndex = 0; +sint32 codeMemoryBlockSize = 0; + +std::mutex mtx_allocExecutableMemory; + +uint8* PPCRecompilerX86_allocateExecutableMemory(sint32 size) +{ + std::lock_guard lck(mtx_allocExecutableMemory); + if( codeMemoryBlockIndex+size > codeMemoryBlockSize ) + { + // allocate new block + codeMemoryBlockSize = std::max(1024*1024*4, size+1024); // 4MB (or more if the function is larger than 4MB) + codeMemoryBlockIndex = 0; + codeMemoryBlock = (uint8*)MemMapper::AllocateMemory(nullptr, codeMemoryBlockSize, MemMapper::PAGE_PERMISSION::P_RWX); + } + uint8* codeMem = codeMemoryBlock + codeMemoryBlockIndex; + codeMemoryBlockIndex += size; + // pad to 4 byte alignment + while (codeMemoryBlockIndex & 3) + { + codeMemoryBlock[codeMemoryBlockIndex] = 0x90; + codeMemoryBlockIndex++; + } + return codeMem; +} + +bool PPCRecompiler_generateX64Code(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext) +{ + x64GenContext_t x64GenContext{}; + + // generate iml instruction code + bool codeGenerationFailed = false; + for (IMLSegment* segIt : ppcImlGenContext->segmentList2) + { + x64GenContext.currentSegment = segIt; + segIt->x64Offset = x64GenContext.emitter->GetWriteIndex(); + for(size_t i=0; iimlList.size(); i++) + { + IMLInstruction* imlInstruction = segIt->imlList.data() + i; + + if( imlInstruction->type == PPCREC_IML_TYPE_R_NAME ) + { + PPCRecompilerX64Gen_imlInstruction_r_name(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction); + } + else if( imlInstruction->type == PPCREC_IML_TYPE_NAME_R ) + { + PPCRecompilerX64Gen_imlInstruction_name_r(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction); + } + else if( imlInstruction->type == PPCREC_IML_TYPE_R_R ) + { + if( PPCRecompilerX64Gen_imlInstruction_r_r(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction) == false ) + codeGenerationFailed = true; + } + else if (imlInstruction->type == PPCREC_IML_TYPE_R_S32) + { + if (PPCRecompilerX64Gen_imlInstruction_r_s32(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction) == false) + codeGenerationFailed = true; + } + else if (imlInstruction->type == PPCREC_IML_TYPE_CONDITIONAL_R_S32) + { + if (PPCRecompilerX64Gen_imlInstruction_conditional_r_s32(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction) == false) + codeGenerationFailed = true; + } + else if (imlInstruction->type == PPCREC_IML_TYPE_R_R_S32) + { + if (PPCRecompilerX64Gen_imlInstruction_r_r_s32(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction) == false) + codeGenerationFailed = true; + } + else if (imlInstruction->type == PPCREC_IML_TYPE_R_R_S32_CARRY) + { + if (PPCRecompilerX64Gen_imlInstruction_r_r_s32_carry(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction) == false) + codeGenerationFailed = true; + } + else if (imlInstruction->type == PPCREC_IML_TYPE_R_R_R) + { + if (PPCRecompilerX64Gen_imlInstruction_r_r_r(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction) == false) + codeGenerationFailed = true; + } + else if (imlInstruction->type == PPCREC_IML_TYPE_R_R_R_CARRY) + { + if (PPCRecompilerX64Gen_imlInstruction_r_r_r_carry(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction) == false) + codeGenerationFailed = true; + } + else if (imlInstruction->type == PPCREC_IML_TYPE_COMPARE) + { + PPCRecompilerX64Gen_imlInstruction_compare(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction); + } + else if (imlInstruction->type == PPCREC_IML_TYPE_COMPARE_S32) + { + PPCRecompilerX64Gen_imlInstruction_compare_s32(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction); + } + else if (imlInstruction->type == PPCREC_IML_TYPE_CONDITIONAL_JUMP) + { + if (PPCRecompilerX64Gen_imlInstruction_cjump2(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction, segIt) == false) + codeGenerationFailed = true; + } + else if (imlInstruction->type == PPCREC_IML_TYPE_JUMP) + { + if (PPCRecompilerX64Gen_imlInstruction_jump2(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction, segIt) == false) + codeGenerationFailed = true; + } + else if( imlInstruction->type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK ) + { + PPCRecompilerX64Gen_imlInstruction_conditionalJumpCycleCheck(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction); + } + else if( imlInstruction->type == PPCREC_IML_TYPE_MACRO ) + { + if( PPCRecompilerX64Gen_imlInstruction_macro(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction) == false ) + { + codeGenerationFailed = true; + } + } + else if( imlInstruction->type == PPCREC_IML_TYPE_LOAD ) + { + if( PPCRecompilerX64Gen_imlInstruction_load(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction, false) == false ) + { + codeGenerationFailed = true; + } + } + else if( imlInstruction->type == PPCREC_IML_TYPE_LOAD_INDEXED ) + { + if( PPCRecompilerX64Gen_imlInstruction_load(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction, true) == false ) + { + codeGenerationFailed = true; + } + } + else if( imlInstruction->type == PPCREC_IML_TYPE_STORE ) + { + if( PPCRecompilerX64Gen_imlInstruction_store(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction, false) == false ) + { + codeGenerationFailed = true; + } + } + else if( imlInstruction->type == PPCREC_IML_TYPE_STORE_INDEXED ) + { + if( PPCRecompilerX64Gen_imlInstruction_store(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction, true) == false ) + { + codeGenerationFailed = true; + } + } + else if (imlInstruction->type == PPCREC_IML_TYPE_ATOMIC_CMP_STORE) + { + if (!PPCRecompilerX64Gen_imlInstruction_atomic_cmp_store(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction)) + codeGenerationFailed = true; + } + else if( imlInstruction->type == PPCREC_IML_TYPE_NO_OP ) + { + // no op + } + else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_LOAD ) + { + if( PPCRecompilerX64Gen_imlInstruction_fpr_load(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction, false) == false ) + { + codeGenerationFailed = true; + } + } + else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_LOAD_INDEXED ) + { + if( PPCRecompilerX64Gen_imlInstruction_fpr_load(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction, true) == false ) + { + codeGenerationFailed = true; + } + } + else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_STORE ) + { + if( PPCRecompilerX64Gen_imlInstruction_fpr_store(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction, false) == false ) + { + codeGenerationFailed = true; + } + } + else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_STORE_INDEXED ) + { + if( PPCRecompilerX64Gen_imlInstruction_fpr_store(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction, true) == false ) + { + codeGenerationFailed = true; + } + } + else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_R_R ) + { + PPCRecompilerX64Gen_imlInstruction_fpr_r_r(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction); + } + else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_R_R_R ) + { + PPCRecompilerX64Gen_imlInstruction_fpr_r_r_r(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction); + } + else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_R_R_R_R ) + { + PPCRecompilerX64Gen_imlInstruction_fpr_r_r_r_r(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction); + } + else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_R ) + { + PPCRecompilerX64Gen_imlInstruction_fpr_r(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction); + } + else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_COMPARE) + { + PPCRecompilerX64Gen_imlInstruction_fpr_compare(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction); + } + else + { + debug_printf("PPCRecompiler_generateX64Code(): Unsupported iml type 0x%x\n", imlInstruction->type); + assert_dbg(); + } + } + } + // handle failed code generation + if( codeGenerationFailed ) + { + return false; + } + // allocate executable memory + uint8* executableMemory = PPCRecompilerX86_allocateExecutableMemory(x64GenContext.emitter->GetBuffer().size_bytes()); + size_t baseAddress = (size_t)executableMemory; + // fix relocs + for(auto& relocIt : x64GenContext.relocateOffsetTable2) + { + // search for segment that starts with this offset + uint32 ppcOffset = (uint32)(size_t)relocIt.extraInfo; + uint32 x64Offset = 0xFFFFFFFF; + + IMLSegment* destSegment = (IMLSegment*)relocIt.extraInfo; + x64Offset = destSegment->x64Offset; + + uint32 relocBase = relocIt.offset; + uint8* relocInstruction = x64GenContext.emitter->GetBufferPtr()+relocBase; + if( relocInstruction[0] == 0x0F && (relocInstruction[1] >= 0x80 && relocInstruction[1] <= 0x8F) ) + { + // Jcc relativeImm32 + sint32 distanceNearJump = (sint32)((baseAddress + x64Offset) - (baseAddress + relocBase + 2)); + if (distanceNearJump >= -128 && distanceNearJump < 127) // disabled + { + // convert to near Jcc + *(uint8*)(relocInstruction + 0) = (uint8)(relocInstruction[1]-0x80 + 0x70); + // patch offset + *(uint8*)(relocInstruction + 1) = (uint8)distanceNearJump; + // replace unused 4 bytes with NOP instruction + relocInstruction[2] = 0x0F; + relocInstruction[3] = 0x1F; + relocInstruction[4] = 0x40; + relocInstruction[5] = 0x00; + } + else + { + // patch offset + *(uint32*)(relocInstruction + 2) = (uint32)((baseAddress + x64Offset) - (baseAddress + relocBase + 6)); + } + } + else if( relocInstruction[0] == 0xE9 ) + { + // JMP relativeImm32 + *(uint32*)(relocInstruction+1) = (uint32)((baseAddress+x64Offset)-(baseAddress+relocBase+5)); + } + else + assert_dbg(); + } + + // copy code to executable memory + std::span codeBuffer = x64GenContext.emitter->GetBuffer(); + memcpy(executableMemory, codeBuffer.data(), codeBuffer.size_bytes()); + // set code + PPCRecFunction->x86Code = executableMemory; + PPCRecFunction->x86Size = codeBuffer.size_bytes(); + return true; +} + +void PPCRecompilerX64Gen_generateEnterRecompilerCode() +{ + x64GenContext_t x64GenContext{}; + + // start of recompiler entry function + x64Gen_push_reg64(&x64GenContext, X86_REG_RAX); + x64Gen_push_reg64(&x64GenContext, X86_REG_RCX); + x64Gen_push_reg64(&x64GenContext, X86_REG_RDX); + x64Gen_push_reg64(&x64GenContext, X86_REG_RBX); + x64Gen_push_reg64(&x64GenContext, X86_REG_RBP); + x64Gen_push_reg64(&x64GenContext, X86_REG_RDI); + x64Gen_push_reg64(&x64GenContext, X86_REG_RSI); + x64Gen_push_reg64(&x64GenContext, X86_REG_R8); + x64Gen_push_reg64(&x64GenContext, X86_REG_R9); + x64Gen_push_reg64(&x64GenContext, X86_REG_R10); + x64Gen_push_reg64(&x64GenContext, X86_REG_R11); + x64Gen_push_reg64(&x64GenContext, X86_REG_R12); + x64Gen_push_reg64(&x64GenContext, X86_REG_R13); + x64Gen_push_reg64(&x64GenContext, X86_REG_R14); + x64Gen_push_reg64(&x64GenContext, X86_REG_R15); + + // 000000007775EF04 | E8 00 00 00 00 call +0x00 + x64Gen_writeU8(&x64GenContext, 0xE8); + x64Gen_writeU8(&x64GenContext, 0x00); + x64Gen_writeU8(&x64GenContext, 0x00); + x64Gen_writeU8(&x64GenContext, 0x00); + x64Gen_writeU8(&x64GenContext, 0x00); + //000000007775EF09 | 48 83 04 24 05 add qword ptr ss:[rsp],5 + x64Gen_writeU8(&x64GenContext, 0x48); + x64Gen_writeU8(&x64GenContext, 0x83); + x64Gen_writeU8(&x64GenContext, 0x04); + x64Gen_writeU8(&x64GenContext, 0x24); + uint32 jmpPatchOffset = x64GenContext.emitter->GetWriteIndex(); + x64Gen_writeU8(&x64GenContext, 0); // skip the distance until after the JMP + x64Emit_mov_mem64_reg64(&x64GenContext, X86_REG_RDX, offsetof(PPCInterpreter_t, rspTemp), X86_REG_RSP); + + + // MOV RSP, RDX (ppc interpreter instance) + x64Gen_mov_reg64_reg64(&x64GenContext, X86_REG_RSP, X86_REG_RDX); + // MOV R15, ppcRecompilerInstanceData + x64Gen_mov_reg64_imm64(&x64GenContext, X86_REG_R15, (uint64)ppcRecompilerInstanceData); + // MOV R13, memory_base + x64Gen_mov_reg64_imm64(&x64GenContext, X86_REG_R13, (uint64)memory_base); + + //JMP recFunc + x64Gen_jmp_reg64(&x64GenContext, X86_REG_RCX); // call argument 1 + + x64GenContext.emitter->GetBuffer()[jmpPatchOffset] = (x64GenContext.emitter->GetWriteIndex() -(jmpPatchOffset-4)); + + //recompilerExit1: + x64Gen_pop_reg64(&x64GenContext, X86_REG_R15); + x64Gen_pop_reg64(&x64GenContext, X86_REG_R14); + x64Gen_pop_reg64(&x64GenContext, X86_REG_R13); + x64Gen_pop_reg64(&x64GenContext, X86_REG_R12); + x64Gen_pop_reg64(&x64GenContext, X86_REG_R11); + x64Gen_pop_reg64(&x64GenContext, X86_REG_R10); + x64Gen_pop_reg64(&x64GenContext, X86_REG_R9); + x64Gen_pop_reg64(&x64GenContext, X86_REG_R8); + x64Gen_pop_reg64(&x64GenContext, X86_REG_RSI); + x64Gen_pop_reg64(&x64GenContext, X86_REG_RDI); + x64Gen_pop_reg64(&x64GenContext, X86_REG_RBP); + x64Gen_pop_reg64(&x64GenContext, X86_REG_RBX); + x64Gen_pop_reg64(&x64GenContext, X86_REG_RDX); + x64Gen_pop_reg64(&x64GenContext, X86_REG_RCX); + x64Gen_pop_reg64(&x64GenContext, X86_REG_RAX); + // RET + x64Gen_ret(&x64GenContext); + + uint8* executableMemory = PPCRecompilerX86_allocateExecutableMemory(x64GenContext.emitter->GetBuffer().size_bytes()); + // copy code to executable memory + memcpy(executableMemory, x64GenContext.emitter->GetBuffer().data(), x64GenContext.emitter->GetBuffer().size_bytes()); + PPCRecompiler_enterRecompilerCode = (void ATTR_MS_ABI (*)(uint64,uint64))executableMemory; +} + + +void* PPCRecompilerX64Gen_generateLeaveRecompilerCode() +{ + x64GenContext_t x64GenContext{}; + + // update instruction pointer + // LR is in EDX + x64Emit_mov_mem32_reg32(&x64GenContext, X86_REG_RSP, offsetof(PPCInterpreter_t, instructionPointer), X86_REG_EDX); + + // MOV RSP, [ppcRecompilerX64_rspTemp] + x64Emit_mov_reg64_mem64(&x64GenContext, X86_REG_RSP, REG_RESV_HCPU, offsetof(PPCInterpreter_t, rspTemp)); + + // RET + x64Gen_ret(&x64GenContext); + + uint8* executableMemory = PPCRecompilerX86_allocateExecutableMemory(x64GenContext.emitter->GetBuffer().size_bytes()); + // copy code to executable memory + memcpy(executableMemory, x64GenContext.emitter->GetBuffer().data(), x64GenContext.emitter->GetBuffer().size_bytes()); + return executableMemory; +} + +void PPCRecompilerX64Gen_generateRecompilerInterfaceFunctions() +{ + PPCRecompilerX64Gen_generateEnterRecompilerCode(); + PPCRecompiler_leaveRecompilerCode_unvisited = (void ATTR_MS_ABI (*)())PPCRecompilerX64Gen_generateLeaveRecompilerCode(); + PPCRecompiler_leaveRecompilerCode_visited = (void ATTR_MS_ABI (*)())PPCRecompilerX64Gen_generateLeaveRecompilerCode(); + cemu_assert_debug(PPCRecompiler_leaveRecompilerCode_unvisited != PPCRecompiler_leaveRecompilerCode_visited); +} + diff --git a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64.h b/src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64.h similarity index 81% rename from src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64.h rename to src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64.h index 1d37a77e..1a0fffec 100644 --- a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64.h +++ b/src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64.h @@ -1,104 +1,47 @@ -typedef struct +#include "../PPCRecompiler.h" // todo - get rid of dependency + +#include "x86Emitter.h" + +struct x64RelocEntry_t { + x64RelocEntry_t(uint32 offset, void* extraInfo) : offset(offset), extraInfo(extraInfo) {}; + uint32 offset; - uint8 type; void* extraInfo; -}x64RelocEntry_t; +}; -typedef struct +struct x64GenContext_t { - uint8* codeBuffer; - sint32 codeBufferIndex; - sint32 codeBufferSize; - // cr state - sint32 activeCRRegister; // current x86 condition flags reflect this cr* register - sint32 activeCRState; // describes the way in which x86 flags map to the cr register (signed / unsigned) + IMLSegment* currentSegment{}; + x86Assembler64* emitter; + + x64GenContext_t() + { + emitter = new x86Assembler64(); + } + + ~x64GenContext_t() + { + delete emitter; + } + // relocate offsets - x64RelocEntry_t* relocateOffsetTable; - sint32 relocateOffsetTableSize; - sint32 relocateOffsetTableCount; -}x64GenContext_t; - -// Some of these are defined by winnt.h and gnu headers -#undef REG_EAX -#undef REG_ECX -#undef REG_EDX -#undef REG_EBX -#undef REG_ESP -#undef REG_EBP -#undef REG_ESI -#undef REG_EDI -#undef REG_NONE -#undef REG_RAX -#undef REG_RCX -#undef REG_RDX -#undef REG_RBX -#undef REG_RSP -#undef REG_RBP -#undef REG_RSI -#undef REG_RDI -#undef REG_R8 -#undef REG_R9 -#undef REG_R10 -#undef REG_R11 -#undef REG_R12 -#undef REG_R13 -#undef REG_R14 -#undef REG_R15 - -#define REG_EAX 0 -#define REG_ECX 1 -#define REG_EDX 2 -#define REG_EBX 3 -#define REG_ESP 4 // reserved for low half of hCPU pointer -#define REG_EBP 5 -#define REG_ESI 6 -#define REG_EDI 7 -#define REG_NONE -1 - -#define REG_RAX 0 -#define REG_RCX 1 -#define REG_RDX 2 -#define REG_RBX 3 -#define REG_RSP 4 // reserved for hCPU pointer -#define REG_RBP 5 -#define REG_RSI 6 -#define REG_RDI 7 -#define REG_R8 8 -#define REG_R9 9 -#define REG_R10 10 -#define REG_R11 11 -#define REG_R12 12 -#define REG_R13 13 // reserved to hold pointer to memory base? (Not decided yet) -#define REG_R14 14 // reserved as temporary register -#define REG_R15 15 // reserved for pointer to ppcRecompilerInstanceData - -#define REG_AL 0 -#define REG_CL 1 -#define REG_DL 2 -#define REG_BL 3 -#define REG_AH 4 -#define REG_CH 5 -#define REG_DH 6 -#define REG_BH 7 + std::vector relocateOffsetTable2; +}; // reserved registers -#define REG_RESV_TEMP (REG_R14) -#define REG_RESV_HCPU (REG_RSP) -#define REG_RESV_MEMBASE (REG_R13) -#define REG_RESV_RECDATA (REG_R15) +#define REG_RESV_TEMP (X86_REG_R14) +#define REG_RESV_HCPU (X86_REG_RSP) +#define REG_RESV_MEMBASE (X86_REG_R13) +#define REG_RESV_RECDATA (X86_REG_R15) // reserved floating-point registers #define REG_RESV_FPR_TEMP (15) +#define reg32ToReg16(__x) (__x) // deprecated -extern sint32 x64Gen_registerMap[12]; - -#define tempToRealRegister(__x) (x64Gen_registerMap[__x]) -#define tempToRealFPRRegister(__x) (__x) -#define reg32ToReg16(__x) (__x) - +// deprecated condition flags enum { X86_CONDITION_EQUAL, // or zero @@ -119,36 +62,26 @@ enum X86_CONDITION_NONE, // no condition, jump always }; -#define PPCREC_CR_TEMPORARY (8) // never stored -#define PPCREC_CR_STATE_TYPE_UNSIGNED_ARITHMETIC (0) // for signed arithmetic operations (ADD, CMPI) -#define PPCREC_CR_STATE_TYPE_SIGNED_ARITHMETIC (1) // for unsigned arithmetic operations (ADD, CMPI) -#define PPCREC_CR_STATE_TYPE_LOGICAL (2) // for unsigned operations (CMPLI) - -#define X86_RELOC_MAKE_RELATIVE (0) // make code imm relative to instruction -#define X64_RELOC_LINK_TO_PPC (1) // translate from ppc address to x86 offset -#define X64_RELOC_LINK_TO_SEGMENT (2) // link to beginning of segment - #define PPC_X64_GPR_USABLE_REGISTERS (16-4) #define PPC_X64_FPR_USABLE_REGISTERS (16-1) // Use XMM0 - XMM14, XMM15 is the temp register - -bool PPCRecompiler_generateX64Code(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext); - -void PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext); +bool PPCRecompiler_generateX64Code(struct PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext); void PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext_t* x64GenContext, sint32 jumpInstructionOffset, sint32 destinationOffset); void PPCRecompilerX64Gen_generateRecompilerInterfaceFunctions(); -void PPCRecompilerX64Gen_imlInstruction_fpr_r_name(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction); -void PPCRecompilerX64Gen_imlInstruction_fpr_name_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction); -bool PPCRecompilerX64Gen_imlInstruction_fpr_load(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction, bool indexed); -bool PPCRecompilerX64Gen_imlInstruction_fpr_store(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction, bool indexed); +void PPCRecompilerX64Gen_imlInstruction_fpr_r_name(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction); +void PPCRecompilerX64Gen_imlInstruction_fpr_name_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction); +bool PPCRecompilerX64Gen_imlInstruction_fpr_load(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction, bool indexed); +bool PPCRecompilerX64Gen_imlInstruction_fpr_store(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction, bool indexed); -void PPCRecompilerX64Gen_imlInstruction_fpr_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction); -void PPCRecompilerX64Gen_imlInstruction_fpr_r_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction); -void PPCRecompilerX64Gen_imlInstruction_fpr_r_r_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction); -void PPCRecompilerX64Gen_imlInstruction_fpr_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction); +void PPCRecompilerX64Gen_imlInstruction_fpr_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction); +void PPCRecompilerX64Gen_imlInstruction_fpr_r_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction); +void PPCRecompilerX64Gen_imlInstruction_fpr_r_r_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction); +void PPCRecompilerX64Gen_imlInstruction_fpr_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction); + +void PPCRecompilerX64Gen_imlInstruction_fpr_compare(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction); // ASM gen void x64Gen_writeU8(x64GenContext_t* x64GenContext, uint8 v); @@ -196,9 +129,6 @@ void x64Gen_or_reg64Low8_mem8Reg64(x64GenContext_t* x64GenContext, sint32 dstReg void x64Gen_and_reg64Low8_mem8Reg64(x64GenContext_t* x64GenContext, sint32 dstRegister, sint32 memRegister64, sint32 memImmS32); void x64Gen_mov_mem8Reg64_reg64Low8(x64GenContext_t* x64GenContext, sint32 dstRegister, sint32 memRegister64, sint32 memImmS32); -void x64Gen_lock_cmpxchg_mem32Reg64PlusReg64_reg64(x64GenContext_t* x64GenContext, sint32 memRegisterA64, sint32 memRegisterB64, sint32 memImmS32, sint32 srcRegister); -void x64Gen_lock_cmpxchg_mem32Reg64_reg64(x64GenContext_t* x64GenContext, sint32 memRegister64, sint32 memImmS32, sint32 srcRegister); - void x64Gen_add_reg64_reg64(x64GenContext_t* x64GenContext, sint32 destRegister, sint32 srcRegister); void x64Gen_add_reg64Low32_reg64Low32(x64GenContext_t* x64GenContext, sint32 destRegister, sint32 srcRegister); void x64Gen_add_reg64_imm32(x64GenContext_t* x64GenContext, sint32 srcRegister, uint32 immU32); @@ -207,9 +137,6 @@ void x64Gen_sub_reg64Low32_reg64Low32(x64GenContext_t* x64GenContext, sint32 des void x64Gen_sub_reg64Low32_imm32(x64GenContext_t* x64GenContext, sint32 srcRegister, uint32 immU32); void x64Gen_sub_reg64_imm32(x64GenContext_t* x64GenContext, sint32 srcRegister, uint32 immU32); void x64Gen_sub_mem32reg64_imm32(x64GenContext_t* x64GenContext, sint32 memRegister, sint32 memImmS32, uint64 immU32); -void x64Gen_sbb_reg64Low32_reg64Low32(x64GenContext_t* x64GenContext, sint32 destRegister, sint32 srcRegister); -void x64Gen_adc_reg64Low32_reg64Low32(x64GenContext_t* x64GenContext, sint32 destRegister, sint32 srcRegister); -void x64Gen_adc_reg64Low32_imm32(x64GenContext_t* x64GenContext, sint32 srcRegister, uint32 immU32); void x64Gen_dec_mem32(x64GenContext_t* x64GenContext, sint32 memoryRegister, uint32 memoryImmU32); void x64Gen_imul_reg64Low32_reg64Low32(x64GenContext_t* x64GenContext, sint32 destRegister, sint32 operandRegister); void x64Gen_idiv_reg64Low32(x64GenContext_t* x64GenContext, sint32 operandRegister); @@ -241,9 +168,7 @@ void x64Gen_not_reg64Low32(x64GenContext_t* x64GenContext, sint32 destRegister); void x64Gen_neg_reg64Low32(x64GenContext_t* x64GenContext, sint32 destRegister); void x64Gen_cdq(x64GenContext_t* x64GenContext); -void x64Gen_bswap_reg64(x64GenContext_t* x64GenContext, sint32 destRegister); void x64Gen_bswap_reg64Lower32bit(x64GenContext_t* x64GenContext, sint32 destRegister); -void x64Gen_bswap_reg64Lower16bit(x64GenContext_t* x64GenContext, sint32 destRegister); void x64Gen_lzcnt_reg64Low32_reg64Low32(x64GenContext_t* x64GenContext, sint32 destRegister, sint32 srcRegister); void x64Gen_bsr_reg64Low32_reg64Low32(x64GenContext_t* x64GenContext, sint32 destRegister, sint32 srcRegister); @@ -329,4 +254,8 @@ void x64Gen_movBEZeroExtend_reg64Low16_mem16Reg64PlusReg64(x64GenContext_t* x64G void x64Gen_movBETruncate_mem32Reg64PlusReg64_reg64(x64GenContext_t* x64GenContext, sint32 memRegisterA64, sint32 memRegisterB64, sint32 memImmS32, sint32 srcRegister); void x64Gen_shrx_reg64_reg64_reg64(x64GenContext_t* x64GenContext, sint32 registerDst, sint32 registerA, sint32 registerB); -void x64Gen_shlx_reg64_reg64_reg64(x64GenContext_t* x64GenContext, sint32 registerDst, sint32 registerA, sint32 registerB); \ No newline at end of file +void x64Gen_shrx_reg32_reg32_reg32(x64GenContext_t* x64GenContext, sint32 registerDst, sint32 registerA, sint32 registerB); +void x64Gen_sarx_reg64_reg64_reg64(x64GenContext_t* x64GenContext, sint32 registerDst, sint32 registerA, sint32 registerB); +void x64Gen_sarx_reg32_reg32_reg32(x64GenContext_t* x64GenContext, sint32 registerDst, sint32 registerA, sint32 registerB); +void x64Gen_shlx_reg64_reg64_reg64(x64GenContext_t* x64GenContext, sint32 registerDst, sint32 registerA, sint32 registerB); +void x64Gen_shlx_reg32_reg32_reg32(x64GenContext_t* x64GenContext, sint32 registerDst, sint32 registerA, sint32 registerB); \ No newline at end of file diff --git a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64AVX.cpp b/src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64AVX.cpp similarity index 92% rename from src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64AVX.cpp rename to src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64AVX.cpp index 619c3985..b0ef8640 100644 --- a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64AVX.cpp +++ b/src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64AVX.cpp @@ -1,5 +1,4 @@ -#include "PPCRecompiler.h" -#include "PPCRecompilerX64.h" +#include "BackendX64.h" void _x64Gen_writeMODRMDeprecated(x64GenContext_t* x64GenContext, sint32 dataRegister, sint32 memRegisterA64, sint32 memRegisterB64, sint32 memImmS32); @@ -21,11 +20,10 @@ void _x64Gen_vex128_nds(x64GenContext_t* x64GenContext, uint8 opcodeMap, uint8 a x64Gen_writeU8(x64GenContext, opcode); } -#define VEX_PP_0F 0 // guessed +#define VEX_PP_0F 0 #define VEX_PP_66_0F 1 -#define VEX_PP_F3_0F 2 // guessed -#define VEX_PP_F2_0F 3 // guessed - +#define VEX_PP_F3_0F 2 +#define VEX_PP_F2_0F 3 void x64Gen_avx_VPUNPCKHQDQ_xmm_xmm_xmm(x64GenContext_t* x64GenContext, sint32 dstRegister, sint32 srcRegisterA, sint32 srcRegisterB) { diff --git a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64BMI.cpp b/src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64BMI.cpp similarity index 67% rename from src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64BMI.cpp rename to src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64BMI.cpp index 5a71e93d..bbb707e0 100644 --- a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64BMI.cpp +++ b/src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64BMI.cpp @@ -1,5 +1,4 @@ -#include "PPCRecompiler.h" -#include "PPCRecompilerX64.h" +#include "BackendX64.h" void _x64Gen_writeMODRMDeprecated(x64GenContext_t* x64GenContext, sint32 dataRegister, sint32 memRegisterA64, sint32 memRegisterB64, sint32 memImmS32); @@ -69,6 +68,34 @@ void x64Gen_shrx_reg64_reg64_reg64(x64GenContext_t* x64GenContext, sint32 regist x64Gen_writeU8(x64GenContext, 0xC0 + (registerDst & 7) * 8 + (registerA & 7)); } +void x64Gen_shrx_reg32_reg32_reg32(x64GenContext_t* x64GenContext, sint32 registerDst, sint32 registerA, sint32 registerB) +{ + x64Gen_writeU8(x64GenContext, 0xC4); + x64Gen_writeU8(x64GenContext, 0xE2 - ((registerDst >= 8) ? 0x80 : 0) - ((registerA >= 8) ? 0x20 : 0)); + x64Gen_writeU8(x64GenContext, 0x7B - registerB * 8); + x64Gen_writeU8(x64GenContext, 0xF7); + x64Gen_writeU8(x64GenContext, 0xC0 + (registerDst & 7) * 8 + (registerA & 7)); +} + +void x64Gen_sarx_reg64_reg64_reg64(x64GenContext_t* x64GenContext, sint32 registerDst, sint32 registerA, sint32 registerB) +{ + // SARX reg64, reg64, reg64 + x64Gen_writeU8(x64GenContext, 0xC4); + x64Gen_writeU8(x64GenContext, 0xE2 - ((registerDst >= 8) ? 0x80 : 0) - ((registerA >= 8) ? 0x20 : 0)); + x64Gen_writeU8(x64GenContext, 0xFA - registerB * 8); + x64Gen_writeU8(x64GenContext, 0xF7); + x64Gen_writeU8(x64GenContext, 0xC0 + (registerDst & 7) * 8 + (registerA & 7)); +} + +void x64Gen_sarx_reg32_reg32_reg32(x64GenContext_t* x64GenContext, sint32 registerDst, sint32 registerA, sint32 registerB) +{ + x64Gen_writeU8(x64GenContext, 0xC4); + x64Gen_writeU8(x64GenContext, 0xE2 - ((registerDst >= 8) ? 0x80 : 0) - ((registerA >= 8) ? 0x20 : 0)); + x64Gen_writeU8(x64GenContext, 0x7A - registerB * 8); + x64Gen_writeU8(x64GenContext, 0xF7); + x64Gen_writeU8(x64GenContext, 0xC0 + (registerDst & 7) * 8 + (registerA & 7)); +} + void x64Gen_shlx_reg64_reg64_reg64(x64GenContext_t* x64GenContext, sint32 registerDst, sint32 registerA, sint32 registerB) { // SHLX reg64, reg64, reg64 @@ -77,4 +104,13 @@ void x64Gen_shlx_reg64_reg64_reg64(x64GenContext_t* x64GenContext, sint32 regist x64Gen_writeU8(x64GenContext, 0xF9 - registerB * 8); x64Gen_writeU8(x64GenContext, 0xF7); x64Gen_writeU8(x64GenContext, 0xC0 + (registerDst & 7) * 8 + (registerA & 7)); +} + +void x64Gen_shlx_reg32_reg32_reg32(x64GenContext_t* x64GenContext, sint32 registerDst, sint32 registerA, sint32 registerB) +{ + x64Gen_writeU8(x64GenContext, 0xC4); + x64Gen_writeU8(x64GenContext, 0xE2 - ((registerDst >= 8) ? 0x80 : 0) - ((registerA >= 8) ? 0x20 : 0)); + x64Gen_writeU8(x64GenContext, 0x79 - registerB * 8); + x64Gen_writeU8(x64GenContext, 0xF7); + x64Gen_writeU8(x64GenContext, 0xC0 + (registerDst & 7) * 8 + (registerA & 7)); } \ No newline at end of file diff --git a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64FPU.cpp b/src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64FPU.cpp similarity index 51% rename from src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64FPU.cpp rename to src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64FPU.cpp index d83f67de..cff46a2d 100644 --- a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64FPU.cpp +++ b/src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64FPU.cpp @@ -1,47 +1,43 @@ -#include "PPCRecompiler.h" -#include "PPCRecompilerIml.h" -#include "PPCRecompilerX64.h" -#include "asm/x64util.h" +#include "../PPCRecompiler.h" +#include "../IML/IML.h" +#include "BackendX64.h" #include "Common/cpu_features.h" -void PPCRecompilerX64Gen_imlInstruction_fpr_r_name(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction) +#include "asm/x64util.h" // for recompiler_fres / frsqrte + +uint32 _regF64(IMLReg physReg); + +uint32 _regI32(IMLReg r) { - uint32 name = imlInstruction->op_r_name.name; - if( name >= PPCREC_NAME_FPR0 && name < (PPCREC_NAME_FPR0+32) ) - { - x64Gen_movupd_xmmReg_memReg128(x64GenContext, tempToRealFPRRegister(imlInstruction->op_r_name.registerIndex), REG_ESP, offsetof(PPCInterpreter_t, fpr)+sizeof(FPR_t)*(name-PPCREC_NAME_FPR0)); - } - else if( name >= PPCREC_NAME_TEMPORARY_FPR0 || name < (PPCREC_NAME_TEMPORARY_FPR0+8) ) - { - x64Gen_movupd_xmmReg_memReg128(x64GenContext, tempToRealFPRRegister(imlInstruction->op_r_name.registerIndex), REG_ESP, offsetof(PPCInterpreter_t, temporaryFPR)+sizeof(FPR_t)*(name-PPCREC_NAME_TEMPORARY_FPR0)); - } - else - { - cemu_assert_debug(false); - } + cemu_assert_debug(r.GetRegFormat() == IMLRegFormat::I32); + return (uint32)r.GetRegID(); } -void PPCRecompilerX64Gen_imlInstruction_fpr_name_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction) +static x86Assembler64::GPR32 _reg32(sint8 physRegId) { - uint32 name = imlInstruction->op_r_name.name; - if( name >= PPCREC_NAME_FPR0 && name < (PPCREC_NAME_FPR0+32) ) - { - x64Gen_movupd_memReg128_xmmReg(x64GenContext, tempToRealFPRRegister(imlInstruction->op_r_name.registerIndex), REG_ESP, offsetof(PPCInterpreter_t, fpr)+sizeof(FPR_t)*(name-PPCREC_NAME_FPR0)); - } - else if( name >= PPCREC_NAME_TEMPORARY_FPR0 && name < (PPCREC_NAME_TEMPORARY_FPR0+8) ) - { - x64Gen_movupd_memReg128_xmmReg(x64GenContext, tempToRealFPRRegister(imlInstruction->op_r_name.registerIndex), REG_ESP, offsetof(PPCInterpreter_t, temporaryFPR)+sizeof(FPR_t)*(name-PPCREC_NAME_TEMPORARY_FPR0)); - } - else - { - cemu_assert_debug(false); - } + return (x86Assembler64::GPR32)physRegId; } -void PPCRecompilerX64Gen_imlInstr_gqr_generateScaleCode(ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, sint32 registerXMM, bool isLoad, bool scalePS1, sint32 registerGQR) +static x86Assembler64::GPR8_REX _reg8(IMLReg r) +{ + cemu_assert_debug(r.GetRegFormat() == IMLRegFormat::I32); // currently bool regs are implemented as 32bit registers + return (x86Assembler64::GPR8_REX)r.GetRegID(); +} + +static x86Assembler64::GPR32 _reg32_from_reg8(x86Assembler64::GPR8_REX regId) +{ + return (x86Assembler64::GPR32)regId; +} + +static x86Assembler64::GPR8_REX _reg8_from_reg32(x86Assembler64::GPR32 regId) +{ + return (x86Assembler64::GPR8_REX)regId; +} + +void PPCRecompilerX64Gen_imlInstr_gqr_generateScaleCode(ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, sint32 registerXMM, bool isLoad, bool scalePS1, IMLReg registerGQR) { // load GQR - x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, registerGQR); + x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, _regI32(registerGQR)); // extract scale field and multiply by 16 to get array offset x64Gen_shr_reg64Low32_imm8(x64GenContext, REG_RESV_TEMP, (isLoad?16:0)+8-4); x64Gen_and_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, (0x3F<<4)); @@ -65,7 +61,7 @@ void PPCRecompilerX64Gen_imlInstr_gqr_generateScaleCode(ppcImlGenContext_t* ppcI // generate code for PSQ load for a particular type // if scaleGQR is -1 then a scale of 1.0 is assumed (no scale) -void PPCRecompilerX64Gen_imlInstr_psq_load(ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, uint8 mode, sint32 registerXMM, sint32 memReg, sint32 memRegEx, sint32 memImmS32, bool indexed, sint32 registerGQR = -1) +void PPCRecompilerX64Gen_imlInstr_psq_load(ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, uint8 mode, sint32 registerXMM, sint32 memReg, sint32 memRegEx, sint32 memImmS32, bool indexed, IMLReg registerGQR = IMLREG_INVALID) { if (mode == PPCREC_FPR_LD_MODE_PSQ_FLOAT_PS0_PS1) { @@ -74,8 +70,8 @@ void PPCRecompilerX64Gen_imlInstr_psq_load(ppcImlGenContext_t* ppcImlGenContext, assert_dbg(); } // optimized code for ps float load - x64Emit_mov_reg64_mem64(x64GenContext, REG_RESV_TEMP, REG_R13, memReg, memImmS32); - x64Gen_bswap_reg64(x64GenContext, REG_RESV_TEMP); + x64Emit_mov_reg64_mem64(x64GenContext, REG_RESV_TEMP, X86_REG_R13, memReg, memImmS32); + x64GenContext->emitter->BSWAP_q(REG_RESV_TEMP); x64Gen_rol_reg64_imm8(x64GenContext, REG_RESV_TEMP, 32); // swap upper and lower DWORD x64Gen_movq_xmmReg_reg64(x64GenContext, registerXMM, REG_RESV_TEMP); x64Gen_cvtps2pd_xmmReg_xmmReg(x64GenContext, registerXMM, registerXMM); @@ -115,8 +111,8 @@ void PPCRecompilerX64Gen_imlInstr_psq_load(ppcImlGenContext_t* ppcImlGenContext, } else { - x64Emit_mov_mem32_reg64(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR), REG_RESV_TEMP); - x64Gen_movddup_xmmReg_memReg64(x64GenContext, REG_RESV_FPR_TEMP, REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)); + x64Emit_mov_mem32_reg64(x64GenContext, X86_REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR), REG_RESV_TEMP); + x64Gen_movddup_xmmReg_memReg64(x64GenContext, REG_RESV_FPR_TEMP, X86_REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)); } x64Gen_cvtss2sd_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, REG_RESV_FPR_TEMP); // load constant 1.0 into lower half and upper half of temp register @@ -178,7 +174,7 @@ void PPCRecompilerX64Gen_imlInstr_psq_load(ppcImlGenContext_t* ppcImlGenContext, if (readSize == 16) { // half word - x64Gen_movZeroExtend_reg64Low16_mem16Reg64PlusReg64(x64GenContext, REG_RESV_TEMP, REG_R13, memReg, memOffset); + x64Gen_movZeroExtend_reg64Low16_mem16Reg64PlusReg64(x64GenContext, REG_RESV_TEMP, X86_REG_R13, memReg, memOffset); x64Gen_rol_reg64Low16_imm8(x64GenContext, REG_RESV_TEMP, 8); // endian swap if (isSigned) x64Gen_movSignExtend_reg64Low32_reg64Low16(x64GenContext, REG_RESV_TEMP, REG_RESV_TEMP); @@ -188,7 +184,7 @@ void PPCRecompilerX64Gen_imlInstr_psq_load(ppcImlGenContext_t* ppcImlGenContext, else if (readSize == 8) { // byte - x64Emit_mov_reg64b_mem8(x64GenContext, REG_RESV_TEMP, REG_R13, memReg, memOffset); + x64Emit_mov_reg64b_mem8(x64GenContext, REG_RESV_TEMP, X86_REG_R13, memReg, memOffset); if (isSigned) x64Gen_movSignExtend_reg64Low32_reg64Low8(x64GenContext, REG_RESV_TEMP, REG_RESV_TEMP); else @@ -201,31 +197,31 @@ void PPCRecompilerX64Gen_imlInstr_psq_load(ppcImlGenContext_t* ppcImlGenContext, // convert the two integers to doubles x64Gen_cvtpi2pd_xmmReg_mem64Reg64(x64GenContext, registerXMM, REG_RESV_HCPU, offsetof(PPCInterpreter_t, temporaryGPR)); // scale - if (registerGQR >= 0) + if (registerGQR.IsValid()) PPCRecompilerX64Gen_imlInstr_gqr_generateScaleCode(ppcImlGenContext, x64GenContext, registerXMM, true, loadPS1, registerGQR); } } -void PPCRecompilerX64Gen_imlInstr_psq_load_generic(ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, uint8 mode, sint32 registerXMM, sint32 memReg, sint32 memRegEx, sint32 memImmS32, bool indexed, sint32 registerGQR) +void PPCRecompilerX64Gen_imlInstr_psq_load_generic(ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, uint8 mode, sint32 registerXMM, sint32 memReg, sint32 memRegEx, sint32 memImmS32, bool indexed, IMLReg registerGQR) { bool loadPS1 = (mode == PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0_PS1); // load GQR - x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, registerGQR); + x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, _regI32(registerGQR)); // extract load type field x64Gen_shr_reg64Low32_imm8(x64GenContext, REG_RESV_TEMP, 16); x64Gen_and_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, 7); // jump cases x64Gen_cmp_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, 4); // type 4 -> u8 - sint32 jumpOffset_caseU8 = x64GenContext->codeBufferIndex; + sint32 jumpOffset_caseU8 = x64GenContext->emitter->GetWriteIndex(); x64Gen_jmpc_far(x64GenContext, X86_CONDITION_EQUAL, 0); x64Gen_cmp_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, 5); // type 5 -> u16 - sint32 jumpOffset_caseU16 = x64GenContext->codeBufferIndex; + sint32 jumpOffset_caseU16 = x64GenContext->emitter->GetWriteIndex(); x64Gen_jmpc_far(x64GenContext, X86_CONDITION_EQUAL, 0); x64Gen_cmp_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, 6); // type 4 -> s8 - sint32 jumpOffset_caseS8 = x64GenContext->codeBufferIndex; + sint32 jumpOffset_caseS8 = x64GenContext->emitter->GetWriteIndex(); x64Gen_jmpc_far(x64GenContext, X86_CONDITION_EQUAL, 0); x64Gen_cmp_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, 7); // type 5 -> s16 - sint32 jumpOffset_caseS16 = x64GenContext->codeBufferIndex; + sint32 jumpOffset_caseS16 = x64GenContext->emitter->GetWriteIndex(); x64Gen_jmpc_far(x64GenContext, X86_CONDITION_EQUAL, 0); // default case -> float @@ -236,42 +232,41 @@ void PPCRecompilerX64Gen_imlInstr_psq_load_generic(ppcImlGenContext_t* ppcImlGen uint32 jumpOffset_endOfS8; PPCRecompilerX64Gen_imlInstr_psq_load(ppcImlGenContext, x64GenContext, loadPS1 ? PPCREC_FPR_LD_MODE_PSQ_FLOAT_PS0_PS1 : PPCREC_FPR_LD_MODE_PSQ_FLOAT_PS0, registerXMM, memReg, memRegEx, memImmS32, indexed, registerGQR); - jumpOffset_endOfFloat = x64GenContext->codeBufferIndex; + jumpOffset_endOfFloat = x64GenContext->emitter->GetWriteIndex(); x64Gen_jmp_imm32(x64GenContext, 0); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_caseU16, x64GenContext->codeBufferIndex); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_caseU16, x64GenContext->emitter->GetWriteIndex()); PPCRecompilerX64Gen_imlInstr_psq_load(ppcImlGenContext, x64GenContext, loadPS1 ? PPCREC_FPR_LD_MODE_PSQ_U16_PS0_PS1 : PPCREC_FPR_LD_MODE_PSQ_U16_PS0, registerXMM, memReg, memRegEx, memImmS32, indexed, registerGQR); - jumpOffset_endOfU8 = x64GenContext->codeBufferIndex; + jumpOffset_endOfU8 = x64GenContext->emitter->GetWriteIndex(); x64Gen_jmp_imm32(x64GenContext, 0); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_caseS16, x64GenContext->codeBufferIndex); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_caseS16, x64GenContext->emitter->GetWriteIndex()); PPCRecompilerX64Gen_imlInstr_psq_load(ppcImlGenContext, x64GenContext, loadPS1 ? PPCREC_FPR_LD_MODE_PSQ_S16_PS0_PS1 : PPCREC_FPR_LD_MODE_PSQ_S16_PS0, registerXMM, memReg, memRegEx, memImmS32, indexed, registerGQR); - jumpOffset_endOfU16 = x64GenContext->codeBufferIndex; + jumpOffset_endOfU16 = x64GenContext->emitter->GetWriteIndex(); x64Gen_jmp_imm32(x64GenContext, 0); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_caseU8, x64GenContext->codeBufferIndex); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_caseU8, x64GenContext->emitter->GetWriteIndex()); PPCRecompilerX64Gen_imlInstr_psq_load(ppcImlGenContext, x64GenContext, loadPS1 ? PPCREC_FPR_LD_MODE_PSQ_U8_PS0_PS1 : PPCREC_FPR_LD_MODE_PSQ_U8_PS0, registerXMM, memReg, memRegEx, memImmS32, indexed, registerGQR); - jumpOffset_endOfS8 = x64GenContext->codeBufferIndex; + jumpOffset_endOfS8 = x64GenContext->emitter->GetWriteIndex(); x64Gen_jmp_imm32(x64GenContext, 0); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_caseS8, x64GenContext->codeBufferIndex); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_caseS8, x64GenContext->emitter->GetWriteIndex()); PPCRecompilerX64Gen_imlInstr_psq_load(ppcImlGenContext, x64GenContext, loadPS1 ? PPCREC_FPR_LD_MODE_PSQ_S8_PS0_PS1 : PPCREC_FPR_LD_MODE_PSQ_S8_PS0, registerXMM, memReg, memRegEx, memImmS32, indexed, registerGQR); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_endOfFloat, x64GenContext->codeBufferIndex); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_endOfU8, x64GenContext->codeBufferIndex); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_endOfU16, x64GenContext->codeBufferIndex); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_endOfS8, x64GenContext->codeBufferIndex); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_endOfFloat, x64GenContext->emitter->GetWriteIndex()); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_endOfU8, x64GenContext->emitter->GetWriteIndex()); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_endOfU16, x64GenContext->emitter->GetWriteIndex()); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_endOfS8, x64GenContext->emitter->GetWriteIndex()); } // load from memory -bool PPCRecompilerX64Gen_imlInstruction_fpr_load(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction, bool indexed) +bool PPCRecompilerX64Gen_imlInstruction_fpr_load(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction, bool indexed) { - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - sint32 realRegisterXMM = tempToRealFPRRegister(imlInstruction->op_storeLoad.registerData); - sint32 realRegisterMem = tempToRealRegister(imlInstruction->op_storeLoad.registerMem); + sint32 realRegisterXMM = _regF64(imlInstruction->op_storeLoad.registerData); + sint32 realRegisterMem = _regI32(imlInstruction->op_storeLoad.registerMem); sint32 realRegisterMem2 = PPC_REC_INVALID_REGISTER; if( indexed ) - realRegisterMem2 = tempToRealRegister(imlInstruction->op_storeLoad.registerMem2); + realRegisterMem2 = _regI32(imlInstruction->op_storeLoad.registerMem2); uint8 mode = imlInstruction->op_storeLoad.mode; if( mode == PPCREC_FPR_LD_MODE_SINGLE_INTO_PS0_PS1 ) @@ -281,29 +276,21 @@ bool PPCRecompilerX64Gen_imlInstruction_fpr_load(PPCRecFunction_t* PPCRecFunctio { x64Gen_mov_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, realRegisterMem2); x64Gen_add_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, realRegisterMem); - if( g_CPUFeatures.x86.movbe ) + if(g_CPUFeatures.x86.movbe) x64Gen_movBEZeroExtend_reg64_mem32Reg64PlusReg64(x64GenContext, REG_RESV_TEMP, REG_RESV_MEMBASE, REG_RESV_TEMP, imlInstruction->op_storeLoad.immS32); else x64Emit_mov_reg32_mem32(x64GenContext, REG_RESV_TEMP, REG_RESV_MEMBASE, REG_RESV_TEMP, imlInstruction->op_storeLoad.immS32); } else { - if( g_CPUFeatures.x86.movbe ) + if(g_CPUFeatures.x86.movbe) x64Gen_movBEZeroExtend_reg64_mem32Reg64PlusReg64(x64GenContext, REG_RESV_TEMP, REG_RESV_MEMBASE, realRegisterMem, imlInstruction->op_storeLoad.immS32); else x64Emit_mov_reg32_mem32(x64GenContext, REG_RESV_TEMP, REG_RESV_MEMBASE, realRegisterMem, imlInstruction->op_storeLoad.immS32); } - if( g_CPUFeatures.x86.movbe == false ) + if(g_CPUFeatures.x86.movbe == false ) x64Gen_bswap_reg64Lower32bit(x64GenContext, REG_RESV_TEMP); - if( g_CPUFeatures.x86.avx ) - { - x64Gen_movd_xmmReg_reg64Low32(x64GenContext, realRegisterXMM, REG_RESV_TEMP); - } - else - { - x64Emit_mov_mem32_reg64(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR), REG_RESV_TEMP); - x64Gen_movddup_xmmReg_memReg64(x64GenContext, realRegisterXMM, REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)); - } + x64Gen_movd_xmmReg_reg64Low32(x64GenContext, realRegisterXMM, REG_RESV_TEMP); if (imlInstruction->op_storeLoad.flags2.notExpanded) { @@ -325,15 +312,15 @@ bool PPCRecompilerX64Gen_imlInstruction_fpr_load(PPCRecFunction_t* PPCRecFunctio x64Gen_mov_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, realRegisterMem); x64Gen_add_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, realRegisterMem2); // load value - x64Emit_mov_reg64_mem64(x64GenContext, REG_RESV_TEMP, REG_R13, REG_RESV_TEMP, imlInstruction->op_storeLoad.immS32+0); - x64Gen_bswap_reg64(x64GenContext, REG_RESV_TEMP); + x64Emit_mov_reg64_mem64(x64GenContext, REG_RESV_TEMP, X86_REG_R13, REG_RESV_TEMP, imlInstruction->op_storeLoad.immS32+0); + x64GenContext->emitter->BSWAP_q(REG_RESV_TEMP); x64Gen_movq_xmmReg_reg64(x64GenContext, REG_RESV_FPR_TEMP, REG_RESV_TEMP); x64Gen_movsd_xmmReg_xmmReg(x64GenContext, realRegisterXMM, REG_RESV_FPR_TEMP); } else { - x64Emit_mov_reg64_mem64(x64GenContext, REG_RESV_TEMP, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32+0); - x64Gen_bswap_reg64(x64GenContext, REG_RESV_TEMP); + x64Emit_mov_reg64_mem64(x64GenContext, REG_RESV_TEMP, X86_REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32+0); + x64GenContext->emitter->BSWAP_q(REG_RESV_TEMP); x64Gen_movq_xmmReg_reg64(x64GenContext, REG_RESV_FPR_TEMP, REG_RESV_TEMP); x64Gen_movsd_xmmReg_xmmReg(x64GenContext, realRegisterXMM, REG_RESV_FPR_TEMP); } @@ -346,31 +333,31 @@ bool PPCRecompilerX64Gen_imlInstruction_fpr_load(PPCRecFunction_t* PPCRecFunctio x64Gen_mov_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, realRegisterMem); x64Gen_add_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, realRegisterMem2); // load double low part to temporaryFPR - x64Emit_mov_reg32_mem32(x64GenContext, REG_RESV_TEMP, REG_R13, REG_RESV_TEMP, imlInstruction->op_storeLoad.immS32+0); + x64Emit_mov_reg32_mem32(x64GenContext, REG_RESV_TEMP, X86_REG_R13, REG_RESV_TEMP, imlInstruction->op_storeLoad.immS32+0); x64Gen_bswap_reg64Lower32bit(x64GenContext, REG_RESV_TEMP); - x64Emit_mov_mem32_reg64(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)+4, REG_RESV_TEMP); + x64Emit_mov_mem32_reg64(x64GenContext, X86_REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)+4, REG_RESV_TEMP); // calculate offset again x64Gen_mov_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, realRegisterMem); x64Gen_add_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, realRegisterMem2); // load double high part to temporaryFPR - x64Emit_mov_reg32_mem32(x64GenContext, REG_RESV_TEMP, REG_R13, REG_RESV_TEMP, imlInstruction->op_storeLoad.immS32+4); + x64Emit_mov_reg32_mem32(x64GenContext, REG_RESV_TEMP, X86_REG_R13, REG_RESV_TEMP, imlInstruction->op_storeLoad.immS32+4); x64Gen_bswap_reg64Lower32bit(x64GenContext, REG_RESV_TEMP); - x64Emit_mov_mem32_reg64(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)+0, REG_RESV_TEMP); + x64Emit_mov_mem32_reg64(x64GenContext, X86_REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)+0, REG_RESV_TEMP); // load double from temporaryFPR - x64Gen_movlpd_xmmReg_memReg64(x64GenContext, realRegisterXMM, REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)); + x64Gen_movlpd_xmmReg_memReg64(x64GenContext, realRegisterXMM, X86_REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)); } else { // load double low part to temporaryFPR - x64Emit_mov_reg32_mem32(x64GenContext, REG_RESV_TEMP, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32+0); + x64Emit_mov_reg32_mem32(x64GenContext, REG_RESV_TEMP, X86_REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32+0); x64Gen_bswap_reg64Lower32bit(x64GenContext, REG_RESV_TEMP); - x64Emit_mov_mem32_reg64(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)+4, REG_RESV_TEMP); + x64Emit_mov_mem32_reg64(x64GenContext, X86_REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)+4, REG_RESV_TEMP); // load double high part to temporaryFPR - x64Emit_mov_reg32_mem32(x64GenContext, REG_RESV_TEMP, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32+4); + x64Emit_mov_reg32_mem32(x64GenContext, REG_RESV_TEMP, X86_REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32+4); x64Gen_bswap_reg64Lower32bit(x64GenContext, REG_RESV_TEMP); - x64Emit_mov_mem32_reg64(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)+0, REG_RESV_TEMP); + x64Emit_mov_mem32_reg64(x64GenContext, X86_REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)+0, REG_RESV_TEMP); // load double from temporaryFPR - x64Gen_movlpd_xmmReg_memReg64(x64GenContext, realRegisterXMM, REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)); + x64Gen_movlpd_xmmReg_memReg64(x64GenContext, realRegisterXMM, X86_REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)); } } } @@ -391,7 +378,7 @@ bool PPCRecompilerX64Gen_imlInstruction_fpr_load(PPCRecFunction_t* PPCRecFunctio else if (mode == PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0_PS1 || mode == PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0) { - PPCRecompilerX64Gen_imlInstr_psq_load_generic(ppcImlGenContext, x64GenContext, mode, realRegisterXMM, realRegisterMem, realRegisterMem2, imlInstruction->op_storeLoad.immS32, indexed, tempToRealRegister(imlInstruction->op_storeLoad.registerGQR)); + PPCRecompilerX64Gen_imlInstr_psq_load_generic(ppcImlGenContext, x64GenContext, mode, realRegisterXMM, realRegisterMem, realRegisterMem2, imlInstruction->op_storeLoad.immS32, indexed, imlInstruction->op_storeLoad.registerGQR); } else { @@ -400,7 +387,7 @@ bool PPCRecompilerX64Gen_imlInstruction_fpr_load(PPCRecFunction_t* PPCRecFunctio return true; } -void PPCRecompilerX64Gen_imlInstr_psq_store(ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, uint8 mode, sint32 registerXMM, sint32 memReg, sint32 memRegEx, sint32 memImmS32, bool indexed, sint32 registerGQR = -1) +void PPCRecompilerX64Gen_imlInstr_psq_store(ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, uint8 mode, sint32 registerXMM, sint32 memReg, sint32 memRegEx, sint32 memImmS32, bool indexed, IMLReg registerGQR = IMLREG_INVALID) { bool storePS1 = (mode == PPCREC_FPR_ST_MODE_PSQ_FLOAT_PS0_PS1 || mode == PPCREC_FPR_ST_MODE_PSQ_S8_PS0_PS1 || @@ -408,7 +395,7 @@ void PPCRecompilerX64Gen_imlInstr_psq_store(ppcImlGenContext_t* ppcImlGenContext mode == PPCREC_FPR_ST_MODE_PSQ_U16_PS0_PS1 || mode == PPCREC_FPR_ST_MODE_PSQ_S16_PS0_PS1); bool isFloat = mode == PPCREC_FPR_ST_MODE_PSQ_FLOAT_PS0 || mode == PPCREC_FPR_ST_MODE_PSQ_FLOAT_PS0_PS1; - if (registerGQR >= 0) + if (registerGQR.IsValid()) { // move to temporary xmm and update registerXMM x64Gen_movaps_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, registerXMM); @@ -420,15 +407,7 @@ void PPCRecompilerX64Gen_imlInstr_psq_store(ppcImlGenContext_t* ppcImlGenContext if (mode == PPCREC_FPR_ST_MODE_PSQ_FLOAT_PS0) { x64Gen_cvtsd2ss_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, registerXMM); - if (g_CPUFeatures.x86.avx) - { - x64Gen_movd_reg64Low32_xmmReg(x64GenContext, REG_RESV_TEMP, REG_RESV_FPR_TEMP); - } - else - { - x64Gen_movsd_memReg64_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)); - x64Emit_mov_reg64_mem32(x64GenContext, REG_RESV_TEMP, REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)); - } + x64Gen_movd_reg64Low32_xmmReg(x64GenContext, REG_RESV_TEMP, REG_RESV_FPR_TEMP); if (g_CPUFeatures.x86.movbe == false) x64Gen_bswap_reg64Lower32bit(x64GenContext, REG_RESV_TEMP); if (indexed) @@ -437,9 +416,9 @@ void PPCRecompilerX64Gen_imlInstr_psq_store(ppcImlGenContext_t* ppcImlGenContext x64Gen_add_reg64Low32_reg64Low32(x64GenContext, memReg, memRegEx); } if (g_CPUFeatures.x86.movbe) - x64Gen_movBETruncate_mem32Reg64PlusReg64_reg64(x64GenContext, REG_R13, memReg, memImmS32, REG_RESV_TEMP); + x64Gen_movBETruncate_mem32Reg64PlusReg64_reg64(x64GenContext, X86_REG_R13, memReg, memImmS32, REG_RESV_TEMP); else - x64Gen_movTruncate_mem32Reg64PlusReg64_reg64(x64GenContext, REG_R13, memReg, memImmS32, REG_RESV_TEMP); + x64Gen_movTruncate_mem32Reg64PlusReg64_reg64(x64GenContext, X86_REG_R13, memReg, memImmS32, REG_RESV_TEMP); if (indexed) { x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, memReg, memRegEx); @@ -453,8 +432,8 @@ void PPCRecompilerX64Gen_imlInstr_psq_store(ppcImlGenContext_t* ppcImlGenContext x64Gen_cvtpd2ps_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, registerXMM); x64Gen_movq_reg64_xmmReg(x64GenContext, REG_RESV_TEMP, REG_RESV_FPR_TEMP); x64Gen_rol_reg64_imm8(x64GenContext, REG_RESV_TEMP, 32); // swap upper and lower DWORD - x64Gen_bswap_reg64(x64GenContext, REG_RESV_TEMP); - x64Gen_mov_mem64Reg64PlusReg64_reg64(x64GenContext, REG_RESV_TEMP, REG_R13, memReg, memImmS32); + x64GenContext->emitter->BSWAP_q(REG_RESV_TEMP); + x64Gen_mov_mem64Reg64PlusReg64_reg64(x64GenContext, REG_RESV_TEMP, X86_REG_R13, memReg, memImmS32); return; } // store as integer @@ -510,16 +489,16 @@ void PPCRecompilerX64Gen_imlInstr_psq_store(ppcImlGenContext_t* ppcImlGenContext } // max(i, -clampMin) x64Gen_cmp_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, clampMin); - sint32 jumpInstructionOffset1 = x64GenContext->codeBufferIndex; + sint32 jumpInstructionOffset1 = x64GenContext->emitter->GetWriteIndex(); x64Gen_jmpc_near(x64GenContext, X86_CONDITION_SIGNED_GREATER_EQUAL, 0); x64Gen_mov_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, clampMin); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset1, x64GenContext->codeBufferIndex); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset1, x64GenContext->emitter->GetWriteIndex()); // min(i, clampMax) x64Gen_cmp_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, clampMax); - sint32 jumpInstructionOffset2 = x64GenContext->codeBufferIndex; + sint32 jumpInstructionOffset2 = x64GenContext->emitter->GetWriteIndex(); x64Gen_jmpc_near(x64GenContext, X86_CONDITION_SIGNED_LESS_EQUAL, 0); x64Gen_mov_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, clampMax); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset2, x64GenContext->codeBufferIndex); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset2, x64GenContext->emitter->GetWriteIndex()); // endian swap if( bitWriteSize == 16) x64Gen_rol_reg64Low16_imm8(x64GenContext, REG_RESV_TEMP, 8); @@ -534,25 +513,25 @@ void PPCRecompilerX64Gen_imlInstr_psq_store(ppcImlGenContext_t* ppcImlGenContext } } -void PPCRecompilerX64Gen_imlInstr_psq_store_generic(ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, uint8 mode, sint32 registerXMM, sint32 memReg, sint32 memRegEx, sint32 memImmS32, bool indexed, sint32 registerGQR) +void PPCRecompilerX64Gen_imlInstr_psq_store_generic(ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, uint8 mode, sint32 registerXMM, sint32 memReg, sint32 memRegEx, sint32 memImmS32, bool indexed, IMLReg registerGQR) { bool storePS1 = (mode == PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0_PS1); // load GQR - x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, registerGQR); + x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, _regI32(registerGQR)); // extract store type field x64Gen_and_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, 7); // jump cases x64Gen_cmp_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, 4); // type 4 -> u8 - sint32 jumpOffset_caseU8 = x64GenContext->codeBufferIndex; + sint32 jumpOffset_caseU8 = x64GenContext->emitter->GetWriteIndex(); x64Gen_jmpc_far(x64GenContext, X86_CONDITION_EQUAL, 0); x64Gen_cmp_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, 5); // type 5 -> u16 - sint32 jumpOffset_caseU16 = x64GenContext->codeBufferIndex; + sint32 jumpOffset_caseU16 = x64GenContext->emitter->GetWriteIndex(); x64Gen_jmpc_far(x64GenContext, X86_CONDITION_EQUAL, 0); x64Gen_cmp_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, 6); // type 4 -> s8 - sint32 jumpOffset_caseS8 = x64GenContext->codeBufferIndex; + sint32 jumpOffset_caseS8 = x64GenContext->emitter->GetWriteIndex(); x64Gen_jmpc_far(x64GenContext, X86_CONDITION_EQUAL, 0); x64Gen_cmp_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, 7); // type 5 -> s16 - sint32 jumpOffset_caseS16 = x64GenContext->codeBufferIndex; + sint32 jumpOffset_caseS16 = x64GenContext->emitter->GetWriteIndex(); x64Gen_jmpc_far(x64GenContext, X86_CONDITION_EQUAL, 0); // default case -> float @@ -563,72 +542,55 @@ void PPCRecompilerX64Gen_imlInstr_psq_store_generic(ppcImlGenContext_t* ppcImlGe uint32 jumpOffset_endOfS8; PPCRecompilerX64Gen_imlInstr_psq_store(ppcImlGenContext, x64GenContext, storePS1 ? PPCREC_FPR_ST_MODE_PSQ_FLOAT_PS0_PS1 : PPCREC_FPR_ST_MODE_PSQ_FLOAT_PS0, registerXMM, memReg, memRegEx, memImmS32, indexed, registerGQR); - jumpOffset_endOfFloat = x64GenContext->codeBufferIndex; + jumpOffset_endOfFloat = x64GenContext->emitter->GetWriteIndex(); x64Gen_jmp_imm32(x64GenContext, 0); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_caseU16, x64GenContext->codeBufferIndex); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_caseU16, x64GenContext->emitter->GetWriteIndex()); PPCRecompilerX64Gen_imlInstr_psq_store(ppcImlGenContext, x64GenContext, storePS1 ? PPCREC_FPR_ST_MODE_PSQ_U16_PS0_PS1 : PPCREC_FPR_ST_MODE_PSQ_U16_PS0, registerXMM, memReg, memRegEx, memImmS32, indexed, registerGQR); - jumpOffset_endOfU8 = x64GenContext->codeBufferIndex; + jumpOffset_endOfU8 = x64GenContext->emitter->GetWriteIndex(); x64Gen_jmp_imm32(x64GenContext, 0); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_caseS16, x64GenContext->codeBufferIndex); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_caseS16, x64GenContext->emitter->GetWriteIndex()); PPCRecompilerX64Gen_imlInstr_psq_store(ppcImlGenContext, x64GenContext, storePS1 ? PPCREC_FPR_ST_MODE_PSQ_S16_PS0_PS1 : PPCREC_FPR_ST_MODE_PSQ_S16_PS0, registerXMM, memReg, memRegEx, memImmS32, indexed, registerGQR); - jumpOffset_endOfU16 = x64GenContext->codeBufferIndex; + jumpOffset_endOfU16 = x64GenContext->emitter->GetWriteIndex(); x64Gen_jmp_imm32(x64GenContext, 0); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_caseU8, x64GenContext->codeBufferIndex); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_caseU8, x64GenContext->emitter->GetWriteIndex()); PPCRecompilerX64Gen_imlInstr_psq_store(ppcImlGenContext, x64GenContext, storePS1 ? PPCREC_FPR_ST_MODE_PSQ_U8_PS0_PS1 : PPCREC_FPR_ST_MODE_PSQ_U8_PS0, registerXMM, memReg, memRegEx, memImmS32, indexed, registerGQR); - jumpOffset_endOfS8 = x64GenContext->codeBufferIndex; + jumpOffset_endOfS8 = x64GenContext->emitter->GetWriteIndex(); x64Gen_jmp_imm32(x64GenContext, 0); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_caseS8, x64GenContext->codeBufferIndex); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_caseS8, x64GenContext->emitter->GetWriteIndex()); PPCRecompilerX64Gen_imlInstr_psq_store(ppcImlGenContext, x64GenContext, storePS1 ? PPCREC_FPR_ST_MODE_PSQ_S8_PS0_PS1 : PPCREC_FPR_ST_MODE_PSQ_S8_PS0, registerXMM, memReg, memRegEx, memImmS32, indexed, registerGQR); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_endOfFloat, x64GenContext->codeBufferIndex); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_endOfU8, x64GenContext->codeBufferIndex); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_endOfU16, x64GenContext->codeBufferIndex); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_endOfS8, x64GenContext->codeBufferIndex); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_endOfFloat, x64GenContext->emitter->GetWriteIndex()); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_endOfU8, x64GenContext->emitter->GetWriteIndex()); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_endOfU16, x64GenContext->emitter->GetWriteIndex()); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpOffset_endOfS8, x64GenContext->emitter->GetWriteIndex()); } // store to memory -bool PPCRecompilerX64Gen_imlInstruction_fpr_store(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction, bool indexed) +bool PPCRecompilerX64Gen_imlInstruction_fpr_store(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction, bool indexed) { - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - sint32 realRegisterXMM = tempToRealFPRRegister(imlInstruction->op_storeLoad.registerData); - sint32 realRegisterMem = tempToRealRegister(imlInstruction->op_storeLoad.registerMem); + sint32 realRegisterXMM = _regF64(imlInstruction->op_storeLoad.registerData); + sint32 realRegisterMem = _regI32(imlInstruction->op_storeLoad.registerMem); sint32 realRegisterMem2 = PPC_REC_INVALID_REGISTER; if( indexed ) - realRegisterMem2 = tempToRealRegister(imlInstruction->op_storeLoad.registerMem2); + realRegisterMem2 = _regI32(imlInstruction->op_storeLoad.registerMem2); uint8 mode = imlInstruction->op_storeLoad.mode; if( mode == PPCREC_FPR_ST_MODE_SINGLE_FROM_PS0 ) { if (imlInstruction->op_storeLoad.flags2.notExpanded) { // value is already in single format - if (g_CPUFeatures.x86.avx) - { - x64Gen_movd_reg64Low32_xmmReg(x64GenContext, REG_RESV_TEMP, realRegisterXMM); - } - else - { - x64Gen_movsd_memReg64_xmmReg(x64GenContext, realRegisterXMM, REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)); - x64Emit_mov_reg64_mem32(x64GenContext, REG_RESV_TEMP, REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)); - } + x64Gen_movd_reg64Low32_xmmReg(x64GenContext, REG_RESV_TEMP, realRegisterXMM); } else { x64Gen_cvtsd2ss_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, realRegisterXMM); - if (g_CPUFeatures.x86.avx) - { - x64Gen_movd_reg64Low32_xmmReg(x64GenContext, REG_RESV_TEMP, REG_RESV_FPR_TEMP); - } - else - { - x64Gen_movsd_memReg64_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)); - x64Emit_mov_reg64_mem32(x64GenContext, REG_RESV_TEMP, REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)); - } + x64Gen_movd_reg64Low32_xmmReg(x64GenContext, REG_RESV_TEMP, REG_RESV_FPR_TEMP); } - if( g_CPUFeatures.x86.movbe == false ) + if(g_CPUFeatures.x86.movbe == false ) x64Gen_bswap_reg64Lower32bit(x64GenContext, REG_RESV_TEMP); if( indexed ) { @@ -636,10 +598,10 @@ bool PPCRecompilerX64Gen_imlInstruction_fpr_store(PPCRecFunction_t* PPCRecFuncti assert_dbg(); x64Gen_add_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); } - if( g_CPUFeatures.x86.movbe ) - x64Gen_movBETruncate_mem32Reg64PlusReg64_reg64(x64GenContext, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32, REG_RESV_TEMP); + if(g_CPUFeatures.x86.movbe) + x64Gen_movBETruncate_mem32Reg64PlusReg64_reg64(x64GenContext, X86_REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32, REG_RESV_TEMP); else - x64Gen_movTruncate_mem32Reg64PlusReg64_reg64(x64GenContext, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32, REG_RESV_TEMP); + x64Gen_movTruncate_mem32Reg64PlusReg64_reg64(x64GenContext, X86_REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32, REG_RESV_TEMP); if( indexed ) { x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); @@ -653,15 +615,15 @@ bool PPCRecompilerX64Gen_imlInstruction_fpr_store(PPCRecFunction_t* PPCRecFuncti assert_dbg(); x64Gen_add_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); } - x64Gen_movsd_memReg64_xmmReg(x64GenContext, realRegisterXMM, REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)); - // store double low part - x64Emit_mov_reg64_mem32(x64GenContext, REG_RESV_TEMP, REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)+0); + x64Gen_movsd_memReg64_xmmReg(x64GenContext, realRegisterXMM, X86_REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)); + // store double low part + x64Emit_mov_reg64_mem32(x64GenContext, REG_RESV_TEMP, X86_REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)+0); x64Gen_bswap_reg64Lower32bit(x64GenContext, REG_RESV_TEMP); - x64Gen_movTruncate_mem32Reg64PlusReg64_reg64(x64GenContext, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32+4, REG_RESV_TEMP); - // store double high part - x64Emit_mov_reg64_mem32(x64GenContext, REG_RESV_TEMP, REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)+4); + x64Gen_movTruncate_mem32Reg64PlusReg64_reg64(x64GenContext, X86_REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32+4, REG_RESV_TEMP); + // store double high part + x64Emit_mov_reg64_mem32(x64GenContext, REG_RESV_TEMP, X86_REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)+4); x64Gen_bswap_reg64Lower32bit(x64GenContext, REG_RESV_TEMP); - x64Gen_movTruncate_mem32Reg64PlusReg64_reg64(x64GenContext, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32+0, REG_RESV_TEMP); + x64Gen_movTruncate_mem32Reg64PlusReg64_reg64(x64GenContext, X86_REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32+0, REG_RESV_TEMP); if( indexed ) { x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); @@ -669,27 +631,19 @@ bool PPCRecompilerX64Gen_imlInstruction_fpr_store(PPCRecFunction_t* PPCRecFuncti } else if( mode == PPCREC_FPR_ST_MODE_UI32_FROM_PS0 ) { - if( g_CPUFeatures.x86.avx ) - { - x64Gen_movd_reg64Low32_xmmReg(x64GenContext, REG_RESV_TEMP, realRegisterXMM); - } - else - { - x64Gen_movsd_memReg64_xmmReg(x64GenContext, realRegisterXMM, REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)); - x64Emit_mov_reg64_mem32(x64GenContext, REG_RESV_TEMP, REG_RSP, offsetof(PPCInterpreter_t, temporaryFPR)); - } + x64Gen_movd_reg64Low32_xmmReg(x64GenContext, REG_RESV_TEMP, realRegisterXMM); x64Gen_bswap_reg64Lower32bit(x64GenContext, REG_RESV_TEMP); if( indexed ) { if( realRegisterMem == realRegisterMem2 ) assert_dbg(); x64Gen_add_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); - x64Gen_movTruncate_mem32Reg64PlusReg64_reg64(x64GenContext, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32, REG_RESV_TEMP); + x64Gen_movTruncate_mem32Reg64PlusReg64_reg64(x64GenContext, X86_REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32, REG_RESV_TEMP); x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); } else { - x64Gen_movTruncate_mem32Reg64PlusReg64_reg64(x64GenContext, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32, REG_RESV_TEMP); + x64Gen_movTruncate_mem32Reg64PlusReg64_reg64(x64GenContext, X86_REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32, REG_RESV_TEMP); } } else if(mode == PPCREC_FPR_ST_MODE_PSQ_FLOAT_PS0_PS1 || @@ -709,7 +663,7 @@ bool PPCRecompilerX64Gen_imlInstruction_fpr_store(PPCRecFunction_t* PPCRecFuncti else if (mode == PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0_PS1 || mode == PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0) { - PPCRecompilerX64Gen_imlInstr_psq_store_generic(ppcImlGenContext, x64GenContext, mode, realRegisterXMM, realRegisterMem, realRegisterMem2, imlInstruction->op_storeLoad.immS32, indexed, tempToRealRegister(imlInstruction->op_storeLoad.registerGQR)); + PPCRecompilerX64Gen_imlInstr_psq_store_generic(ppcImlGenContext, x64GenContext, mode, realRegisterXMM, realRegisterMem, realRegisterMem2, imlInstruction->op_storeLoad.immS32, indexed, imlInstruction->op_storeLoad.registerGQR); } else { @@ -727,275 +681,168 @@ void _swapPS0PS1(x64GenContext_t* x64GenContext, sint32 xmmReg) } // FPR op FPR -void PPCRecompilerX64Gen_imlInstruction_fpr_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction) +void PPCRecompilerX64Gen_imlInstruction_fpr_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction) { - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); + uint32 regR = _regF64(imlInstruction->op_fpr_r_r.regR); + uint32 regA = _regF64(imlInstruction->op_fpr_r_r.regA); + if( imlInstruction->operation == PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_BOTTOM_AND_TOP ) { - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - assert_dbg(); - } - x64Gen_movddup_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerOperand); + x64Gen_movddup_xmmReg_xmmReg(x64GenContext, regR, regA); } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_COPY_TOP_TO_BOTTOM_AND_TOP ) { - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - assert_dbg(); - } // VPUNPCKHQDQ - if (imlInstruction->op_fpr_r_r.registerResult == imlInstruction->op_fpr_r_r.registerOperand) + if (regR == regA) { // unpack top to bottom and top - x64Gen_unpckhpd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerOperand); + x64Gen_unpckhpd_xmmReg_xmmReg(x64GenContext, regR, regA); } - //else if ( g_CPUFeatures.x86.avx ) + //else if ( hasAVXSupport ) //{ // // unpack top to bottom and top with non-destructive destination // // update: On Ivy Bridge this causes weird stalls? - // x64Gen_avx_VUNPCKHPD_xmm_xmm_xmm(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerOperand, imlInstruction->op_fpr_r_r.registerOperand); + // x64Gen_avx_VUNPCKHPD_xmm_xmm_xmm(x64GenContext, registerResult, registerOperand, registerOperand); //} else { // move top to bottom - x64Gen_movhlps_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerOperand); + x64Gen_movhlps_xmmReg_xmmReg(x64GenContext, regR, regA); // duplicate bottom - x64Gen_movddup_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerResult); + x64Gen_movddup_xmmReg_xmmReg(x64GenContext, regR, regR); } } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_BOTTOM ) { - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); - x64Gen_movsd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerOperand); + x64Gen_movsd_xmmReg_xmmReg(x64GenContext, regR, regA); } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_TOP ) { - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); - x64Gen_unpcklpd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerOperand); + x64Gen_unpcklpd_xmmReg_xmmReg(x64GenContext, regR, regA); } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_COPY_BOTTOM_AND_TOP_SWAPPED ) { - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); - if( imlInstruction->op_fpr_r_r.registerResult != imlInstruction->op_fpr_r_r.registerOperand ) - x64Gen_movaps_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerOperand); - _swapPS0PS1(x64GenContext, imlInstruction->op_fpr_r_r.registerResult); + if( regR != regA ) + x64Gen_movaps_xmmReg_xmmReg(x64GenContext, regR, regA); + _swapPS0PS1(x64GenContext, regR); } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_COPY_TOP_TO_TOP ) { - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); - x64Gen_shufpd_xmmReg_xmmReg_imm8(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerOperand, 2); + x64Gen_shufpd_xmmReg_xmmReg_imm8(x64GenContext, regR, regA, 2); } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_COPY_TOP_TO_BOTTOM ) { - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); // use unpckhpd here? - x64Gen_shufpd_xmmReg_xmmReg_imm8(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerOperand, 3); - _swapPS0PS1(x64GenContext, imlInstruction->op_fpr_r_r.registerResult); + x64Gen_shufpd_xmmReg_xmmReg_imm8(x64GenContext, regR, regA, 3); + _swapPS0PS1(x64GenContext, regR); } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_MULTIPLY_BOTTOM ) { - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - assert_dbg(); - } - x64Gen_mulsd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerOperand); + x64Gen_mulsd_xmmReg_xmmReg(x64GenContext, regR, regA); } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_MULTIPLY_PAIR ) { - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - assert_dbg(); - } - x64Gen_mulpd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerOperand); + x64Gen_mulpd_xmmReg_xmmReg(x64GenContext, regR, regA); } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_DIVIDE_BOTTOM ) { - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - assert_dbg(); - } - x64Gen_divsd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerOperand); + x64Gen_divsd_xmmReg_xmmReg(x64GenContext, regR, regA); } else if (imlInstruction->operation == PPCREC_IML_OP_FPR_DIVIDE_PAIR) { - if (imlInstruction->crRegister != PPC_REC_INVALID_REGISTER) - { - assert_dbg(); - } - x64Gen_divpd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerOperand); + x64Gen_divpd_xmmReg_xmmReg(x64GenContext, regR, regA); } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_ADD_BOTTOM ) { - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - assert_dbg(); - } - x64Gen_addsd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerOperand); + x64Gen_addsd_xmmReg_xmmReg(x64GenContext, regR, regA); } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_ADD_PAIR ) { - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - assert_dbg(); - } - x64Gen_addpd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerOperand); + x64Gen_addpd_xmmReg_xmmReg(x64GenContext, regR, regA); } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_SUB_PAIR ) { - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - assert_dbg(); - } - x64Gen_subpd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerOperand); + x64Gen_subpd_xmmReg_xmmReg(x64GenContext, regR, regA); } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_SUB_BOTTOM ) { - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - assert_dbg(); - } - x64Gen_subsd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerOperand); + x64Gen_subsd_xmmReg_xmmReg(x64GenContext, regR, regA); } else if( imlInstruction->operation == PPCREC_IML_OP_ASSIGN ) { - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - assert_dbg(); - } - x64Gen_movaps_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerOperand); + x64Gen_movaps_xmmReg_xmmReg(x64GenContext, regR, regA); } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_BOTTOM_FCTIWZ ) { - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - assert_dbg(); - } - x64Gen_cvttsd2si_xmmReg_xmmReg(x64GenContext, REG_RESV_TEMP, imlInstruction->op_fpr_r_r.registerOperand); + x64Gen_cvttsd2si_xmmReg_xmmReg(x64GenContext, REG_RESV_TEMP, regA); x64Gen_mov_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, REG_RESV_TEMP); // move to FPR register - x64Gen_movq_xmmReg_reg64(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, REG_RESV_TEMP); - } - else if(imlInstruction->operation == PPCREC_IML_OP_FPR_FCMPU_BOTTOM || - imlInstruction->operation == PPCREC_IML_OP_FPR_FCMPU_TOP || - imlInstruction->operation == PPCREC_IML_OP_FPR_FCMPO_BOTTOM ) - { - if( imlInstruction->crRegister == PPC_REC_INVALID_REGISTER ) - { - assert_dbg(); - } - if (imlInstruction->operation == PPCREC_IML_OP_FPR_FCMPU_BOTTOM) - x64Gen_ucomisd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerOperand); - else if (imlInstruction->operation == PPCREC_IML_OP_FPR_FCMPU_TOP) - { - // temporarily switch top/bottom of both operands and compare - if (imlInstruction->op_fpr_r_r.registerResult == imlInstruction->op_fpr_r_r.registerOperand) - { - _swapPS0PS1(x64GenContext, imlInstruction->op_fpr_r_r.registerResult); - x64Gen_ucomisd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerOperand); - _swapPS0PS1(x64GenContext, imlInstruction->op_fpr_r_r.registerResult); - } - else - { - _swapPS0PS1(x64GenContext, imlInstruction->op_fpr_r_r.registerResult); - _swapPS0PS1(x64GenContext, imlInstruction->op_fpr_r_r.registerOperand); - x64Gen_ucomisd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerOperand); - _swapPS0PS1(x64GenContext, imlInstruction->op_fpr_r_r.registerResult); - _swapPS0PS1(x64GenContext, imlInstruction->op_fpr_r_r.registerOperand); - } - } - else - x64Gen_comisd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerOperand); - // todo: handle FPSCR updates - // update cr - sint32 crRegister = imlInstruction->crRegister; - // if the parity bit is set (NaN) we need to manually set CR LT, GT and EQ to 0 (comisd/ucomisd sets the respective flags to 1 in case of NaN) - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_PARITY, REG_RSP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_SO)); // unordered - sint32 jumpInstructionOffset1 = x64GenContext->codeBufferIndex; - x64Gen_jmpc_near(x64GenContext, X86_CONDITION_PARITY, 0); - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_UNSIGNED_BELOW, REG_RSP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_LT)); // same as X64_CONDITION_CARRY - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_UNSIGNED_ABOVE, REG_RSP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_GT)); - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_EQUAL, REG_RSP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_EQ)); - sint32 jumpInstructionOffset2 = x64GenContext->codeBufferIndex; - x64Gen_jmpc_near(x64GenContext, X86_CONDITION_NONE, 0); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset1, x64GenContext->codeBufferIndex); - x64Gen_mov_mem8Reg64_imm8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_LT), 0); - x64Gen_mov_mem8Reg64_imm8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_GT), 0); - x64Gen_mov_mem8Reg64_imm8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_EQ), 0); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset2, x64GenContext->codeBufferIndex); + x64Gen_movq_xmmReg_reg64(x64GenContext, regR, REG_RESV_TEMP); } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_BOTTOM_FRES_TO_BOTTOM_AND_TOP ) { - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - assert_dbg(); - } // move register to XMM15 - x64Gen_movsd_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, imlInstruction->op_fpr_r_r.registerOperand); + x64Gen_movsd_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, regA); // call assembly routine to calculate accurate FRES result in XMM15 x64Gen_mov_reg64_imm64(x64GenContext, REG_RESV_TEMP, (uint64)recompiler_fres); x64Gen_call_reg64(x64GenContext, REG_RESV_TEMP); // copy result to bottom and top half of result register - x64Gen_movddup_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, REG_RESV_FPR_TEMP); + x64Gen_movddup_xmmReg_xmmReg(x64GenContext, regR, REG_RESV_FPR_TEMP); } else if (imlInstruction->operation == PPCREC_IML_OP_FPR_BOTTOM_RECIPROCAL_SQRT) { - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); // move register to XMM15 - x64Gen_movsd_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, imlInstruction->op_fpr_r_r.registerOperand); + x64Gen_movsd_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, regA); // call assembly routine to calculate accurate FRSQRTE result in XMM15 x64Gen_mov_reg64_imm64(x64GenContext, REG_RESV_TEMP, (uint64)recompiler_frsqrte); x64Gen_call_reg64(x64GenContext, REG_RESV_TEMP); // copy result to bottom of result register - x64Gen_movsd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, REG_RESV_FPR_TEMP); + x64Gen_movsd_xmmReg_xmmReg(x64GenContext, regR, REG_RESV_FPR_TEMP); } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_NEGATE_PAIR ) { - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); // copy register - if( imlInstruction->op_fpr_r_r.registerResult != imlInstruction->op_fpr_r_r.registerOperand ) + if( regR != regA ) { - x64Gen_movaps_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerOperand); + x64Gen_movaps_xmmReg_xmmReg(x64GenContext, regR, regA); } // toggle sign bits - x64Gen_xorps_xmmReg_mem128Reg64(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, REG_RESV_RECDATA, offsetof(PPCRecompilerInstanceData_t, _x64XMM_xorNegateMaskPair)); + x64Gen_xorps_xmmReg_mem128Reg64(x64GenContext, regR, REG_RESV_RECDATA, offsetof(PPCRecompilerInstanceData_t, _x64XMM_xorNegateMaskPair)); } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_ABS_PAIR ) { - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); // copy register - if( imlInstruction->op_fpr_r_r.registerResult != imlInstruction->op_fpr_r_r.registerOperand ) + if( regR != regA ) { - x64Gen_movaps_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, imlInstruction->op_fpr_r_r.registerOperand); + x64Gen_movaps_xmmReg_xmmReg(x64GenContext, regR, regA); } // set sign bit to 0 - x64Gen_andps_xmmReg_mem128Reg64(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, REG_RESV_RECDATA, offsetof(PPCRecompilerInstanceData_t, _x64XMM_andAbsMaskPair)); + x64Gen_andps_xmmReg_mem128Reg64(x64GenContext, regR, REG_RESV_RECDATA, offsetof(PPCRecompilerInstanceData_t, _x64XMM_andAbsMaskPair)); } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_FRES_PAIR || imlInstruction->operation == PPCREC_IML_OP_FPR_FRSQRTE_PAIR) { - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); // calculate bottom half of result - x64Gen_movsd_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, imlInstruction->op_fpr_r_r.registerOperand); + x64Gen_movsd_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, regA); if(imlInstruction->operation == PPCREC_IML_OP_FPR_FRES_PAIR) x64Gen_mov_reg64_imm64(x64GenContext, REG_RESV_TEMP, (uint64)recompiler_fres); else x64Gen_mov_reg64_imm64(x64GenContext, REG_RESV_TEMP, (uint64)recompiler_frsqrte); x64Gen_call_reg64(x64GenContext, REG_RESV_TEMP); // calculate fres result in xmm15 - x64Gen_movsd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, REG_RESV_FPR_TEMP); + x64Gen_movsd_xmmReg_xmmReg(x64GenContext, regR, REG_RESV_FPR_TEMP); // calculate top half of result // todo - this top to bottom copy can be optimized? - x64Gen_shufpd_xmmReg_xmmReg_imm8(x64GenContext, REG_RESV_FPR_TEMP, imlInstruction->op_fpr_r_r.registerOperand, 3); + x64Gen_shufpd_xmmReg_xmmReg_imm8(x64GenContext, REG_RESV_FPR_TEMP, regA, 3); x64Gen_shufpd_xmmReg_xmmReg_imm8(x64GenContext, REG_RESV_FPR_TEMP, REG_RESV_FPR_TEMP, 1); // swap top and bottom x64Gen_call_reg64(x64GenContext, REG_RESV_TEMP); // calculate fres result in xmm15 - x64Gen_unpcklpd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r.registerResult, REG_RESV_FPR_TEMP); // copy bottom to top + x64Gen_unpcklpd_xmmReg_xmmReg(x64GenContext, regR, REG_RESV_FPR_TEMP); // copy bottom to top } else { @@ -1006,90 +853,84 @@ void PPCRecompilerX64Gen_imlInstruction_fpr_r_r(PPCRecFunction_t* PPCRecFunction /* * FPR = op (fprA, fprB) */ -void PPCRecompilerX64Gen_imlInstruction_fpr_r_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction) +void PPCRecompilerX64Gen_imlInstruction_fpr_r_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction) { - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); + uint32 regR = _regF64(imlInstruction->op_fpr_r_r_r.regR); + uint32 regA = _regF64(imlInstruction->op_fpr_r_r_r.regA); + uint32 regB = _regF64(imlInstruction->op_fpr_r_r_r.regB); if (imlInstruction->operation == PPCREC_IML_OP_FPR_MULTIPLY_BOTTOM) { - if (imlInstruction->crRegister != PPC_REC_INVALID_REGISTER) + if (regR == regA) { - assert_dbg(); + x64Gen_mulsd_xmmReg_xmmReg(x64GenContext, regR, regB); } - if (imlInstruction->op_fpr_r_r_r.registerResult == imlInstruction->op_fpr_r_r_r.registerOperandA) + else if (regR == regB) { - x64Gen_mulsd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r_r.registerResult, imlInstruction->op_fpr_r_r_r.registerOperandB); - } - else if (imlInstruction->op_fpr_r_r_r.registerResult == imlInstruction->op_fpr_r_r_r.registerOperandB) - { - x64Gen_mulsd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r_r.registerResult, imlInstruction->op_fpr_r_r_r.registerOperandA); + x64Gen_mulsd_xmmReg_xmmReg(x64GenContext, regR, regA); } else { - x64Gen_movsd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r_r.registerResult, imlInstruction->op_fpr_r_r_r.registerOperandA); - x64Gen_mulsd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r_r.registerResult, imlInstruction->op_fpr_r_r_r.registerOperandB); + x64Gen_movsd_xmmReg_xmmReg(x64GenContext, regR, regA); + x64Gen_mulsd_xmmReg_xmmReg(x64GenContext, regR, regB); } } else if (imlInstruction->operation == PPCREC_IML_OP_FPR_ADD_BOTTOM) { - // registerResult(fp0) = registerOperandA(fp0) + registerOperandB(fp0) - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); // todo: Use AVX 3-operand VADDSD if available - if (imlInstruction->op_fpr_r_r_r.registerResult == imlInstruction->op_fpr_r_r_r.registerOperandA) + if (regR == regA) { - x64Gen_addsd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r_r.registerResult, imlInstruction->op_fpr_r_r_r.registerOperandB); + x64Gen_addsd_xmmReg_xmmReg(x64GenContext, regR, regB); } - else if (imlInstruction->op_fpr_r_r_r.registerResult == imlInstruction->op_fpr_r_r_r.registerOperandB) + else if (regR == regB) { - x64Gen_addsd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r_r.registerResult, imlInstruction->op_fpr_r_r_r.registerOperandA); + x64Gen_addsd_xmmReg_xmmReg(x64GenContext, regR, regA); } else { - x64Gen_movaps_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r_r.registerResult, imlInstruction->op_fpr_r_r_r.registerOperandA); - x64Gen_addsd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r_r.registerResult, imlInstruction->op_fpr_r_r_r.registerOperandB); + x64Gen_movaps_xmmReg_xmmReg(x64GenContext, regR, regA); + x64Gen_addsd_xmmReg_xmmReg(x64GenContext, regR, regB); } } else if (imlInstruction->operation == PPCREC_IML_OP_FPR_SUB_PAIR) { // registerResult = registerOperandA - registerOperandB - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); - if( imlInstruction->op_fpr_r_r_r.registerResult == imlInstruction->op_fpr_r_r_r.registerOperandA ) + if( regR == regA ) { - x64Gen_subpd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r_r.registerResult, imlInstruction->op_fpr_r_r_r.registerOperandB); + x64Gen_subpd_xmmReg_xmmReg(x64GenContext, regR, regB); } else if (g_CPUFeatures.x86.avx) { - x64Gen_avx_VSUBPD_xmm_xmm_xmm(x64GenContext, imlInstruction->op_fpr_r_r_r.registerResult, imlInstruction->op_fpr_r_r_r.registerOperandA, imlInstruction->op_fpr_r_r_r.registerOperandB); + x64Gen_avx_VSUBPD_xmm_xmm_xmm(x64GenContext, regR, regA, regB); } - else if( imlInstruction->op_fpr_r_r_r.registerResult == imlInstruction->op_fpr_r_r_r.registerOperandB ) + else if( regR == regB ) { - x64Gen_movaps_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, imlInstruction->op_fpr_r_r_r.registerOperandA); - x64Gen_subpd_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, imlInstruction->op_fpr_r_r_r.registerOperandB); - x64Gen_movaps_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r_r.registerResult, REG_RESV_FPR_TEMP); + x64Gen_movaps_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, regA); + x64Gen_subpd_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, regB); + x64Gen_movaps_xmmReg_xmmReg(x64GenContext, regR, REG_RESV_FPR_TEMP); } else { - x64Gen_movaps_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r_r.registerResult, imlInstruction->op_fpr_r_r_r.registerOperandA); - x64Gen_subpd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r_r.registerResult, imlInstruction->op_fpr_r_r_r.registerOperandB); + x64Gen_movaps_xmmReg_xmmReg(x64GenContext, regR, regA); + x64Gen_subpd_xmmReg_xmmReg(x64GenContext, regR, regB); } } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_SUB_BOTTOM ) { - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); - if( imlInstruction->op_fpr_r_r_r.registerResult == imlInstruction->op_fpr_r_r_r.registerOperandA ) + if( regR == regA ) { - x64Gen_subsd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r_r.registerResult, imlInstruction->op_fpr_r_r_r.registerOperandB); + x64Gen_subsd_xmmReg_xmmReg(x64GenContext, regR, regB); } - else if( imlInstruction->op_fpr_r_r_r.registerResult == imlInstruction->op_fpr_r_r_r.registerOperandB ) + else if( regR == regB ) { - x64Gen_movsd_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, imlInstruction->op_fpr_r_r_r.registerOperandA); - x64Gen_subsd_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, imlInstruction->op_fpr_r_r_r.registerOperandB); - x64Gen_movsd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r_r.registerResult, REG_RESV_FPR_TEMP); + x64Gen_movsd_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, regA); + x64Gen_subsd_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, regB); + x64Gen_movsd_xmmReg_xmmReg(x64GenContext, regR, REG_RESV_FPR_TEMP); } else { - x64Gen_movsd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r_r.registerResult, imlInstruction->op_fpr_r_r_r.registerOperandA); - x64Gen_subsd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r_r.registerResult, imlInstruction->op_fpr_r_r_r.registerOperandB); + x64Gen_movsd_xmmReg_xmmReg(x64GenContext, regR, regA); + x64Gen_subsd_xmmReg_xmmReg(x64GenContext, regR, regB); } } else @@ -1099,38 +940,39 @@ void PPCRecompilerX64Gen_imlInstruction_fpr_r_r_r(PPCRecFunction_t* PPCRecFuncti /* * FPR = op (fprA, fprB, fprC) */ -void PPCRecompilerX64Gen_imlInstruction_fpr_r_r_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction) +void PPCRecompilerX64Gen_imlInstruction_fpr_r_r_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction) { - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); + uint32 regR = _regF64(imlInstruction->op_fpr_r_r_r_r.regR); + uint32 regA = _regF64(imlInstruction->op_fpr_r_r_r_r.regA); + uint32 regB = _regF64(imlInstruction->op_fpr_r_r_r_r.regB); + uint32 regC = _regF64(imlInstruction->op_fpr_r_r_r_r.regC); + if( imlInstruction->operation == PPCREC_IML_OP_FPR_SUM0 ) { - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); - // todo: Investigate if there are other optimizations possible if the operand registers overlap // generic case // 1) move frA bottom to frTemp bottom and top - x64Gen_movddup_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, imlInstruction->op_fpr_r_r_r_r.registerOperandA); + x64Gen_movddup_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, regA); // 2) add frB (both halfs, lower half is overwritten in the next step) - x64Gen_addpd_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, imlInstruction->op_fpr_r_r_r_r.registerOperandB); + x64Gen_addpd_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, regB); // 3) Interleave top of frTemp and frC - x64Gen_unpckhpd_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, imlInstruction->op_fpr_r_r_r_r.registerOperandC); + x64Gen_unpckhpd_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, regC); // todo: We can optimize the REG_RESV_FPR_TEMP -> resultReg copy operation away when the result register does not overlap with any of the operand registers - x64Gen_movaps_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r_r_r.registerResult, REG_RESV_FPR_TEMP); + x64Gen_movaps_xmmReg_xmmReg(x64GenContext, regR, REG_RESV_FPR_TEMP); } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_SUM1 ) { - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); // todo: Investigate if there are other optimizations possible if the operand registers overlap // 1) move frA bottom to frTemp bottom and top - x64Gen_movddup_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, imlInstruction->op_fpr_r_r_r_r.registerOperandA); + x64Gen_movddup_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, regA); // 2) add frB (both halfs, lower half is overwritten in the next step) - x64Gen_addpd_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, imlInstruction->op_fpr_r_r_r_r.registerOperandB); + x64Gen_addpd_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, regB); // 3) Copy bottom from frC - x64Gen_movsd_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, imlInstruction->op_fpr_r_r_r_r.registerOperandC); + x64Gen_movsd_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, regC); //// 4) Swap bottom and top half //x64Gen_shufpd_xmmReg_xmmReg_imm8(x64GenContext, REG_RESV_FPR_TEMP, REG_RESV_FPR_TEMP, 1); // todo: We can optimize the REG_RESV_FPR_TEMP -> resultReg copy operation away when the result register does not overlap with any of the operand registers - x64Gen_movaps_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r_r_r.registerResult, REG_RESV_FPR_TEMP); + x64Gen_movaps_xmmReg_xmmReg(x64GenContext, regR, REG_RESV_FPR_TEMP); //float s0 = (float)hCPU->fpr[frC].fp0; //float s1 = (float)(hCPU->fpr[frA].fp0 + hCPU->fpr[frB].fp1); @@ -1139,107 +981,135 @@ void PPCRecompilerX64Gen_imlInstruction_fpr_r_r_r_r(PPCRecFunction_t* PPCRecFunc } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_SELECT_BOTTOM ) { - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); - x64Gen_comisd_xmmReg_mem64Reg64(x64GenContext, imlInstruction->op_fpr_r_r_r_r.registerOperandA, REG_RESV_RECDATA, offsetof(PPCRecompilerInstanceData_t, _x64XMM_constDouble0_0)); - sint32 jumpInstructionOffset1 = x64GenContext->codeBufferIndex; + x64Gen_comisd_xmmReg_mem64Reg64(x64GenContext, regA, REG_RESV_RECDATA, offsetof(PPCRecompilerInstanceData_t, _x64XMM_constDouble0_0)); + sint32 jumpInstructionOffset1 = x64GenContext->emitter->GetWriteIndex(); x64Gen_jmpc_near(x64GenContext, X86_CONDITION_UNSIGNED_BELOW, 0); // select C - x64Gen_movsd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r_r_r.registerResult, imlInstruction->op_fpr_r_r_r_r.registerOperandC); - sint32 jumpInstructionOffset2 = x64GenContext->codeBufferIndex; + x64Gen_movsd_xmmReg_xmmReg(x64GenContext, regR, regC); + sint32 jumpInstructionOffset2 = x64GenContext->emitter->GetWriteIndex(); x64Gen_jmpc_near(x64GenContext, X86_CONDITION_NONE, 0); // select B - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset1, x64GenContext->codeBufferIndex); - x64Gen_movsd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r_r_r.registerResult, imlInstruction->op_fpr_r_r_r_r.registerOperandB); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset1, x64GenContext->emitter->GetWriteIndex()); + x64Gen_movsd_xmmReg_xmmReg(x64GenContext, regR, regB); // end - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset2, x64GenContext->codeBufferIndex); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset2, x64GenContext->emitter->GetWriteIndex()); } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_SELECT_PAIR ) { - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); // select bottom - x64Gen_comisd_xmmReg_mem64Reg64(x64GenContext, imlInstruction->op_fpr_r_r_r_r.registerOperandA, REG_RESV_RECDATA, offsetof(PPCRecompilerInstanceData_t, _x64XMM_constDouble0_0)); - sint32 jumpInstructionOffset1_bottom = x64GenContext->codeBufferIndex; + x64Gen_comisd_xmmReg_mem64Reg64(x64GenContext, regA, REG_RESV_RECDATA, offsetof(PPCRecompilerInstanceData_t, _x64XMM_constDouble0_0)); + sint32 jumpInstructionOffset1_bottom = x64GenContext->emitter->GetWriteIndex(); x64Gen_jmpc_near(x64GenContext, X86_CONDITION_UNSIGNED_BELOW, 0); // select C bottom - x64Gen_movsd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r_r_r.registerResult, imlInstruction->op_fpr_r_r_r_r.registerOperandC); - sint32 jumpInstructionOffset2_bottom = x64GenContext->codeBufferIndex; + x64Gen_movsd_xmmReg_xmmReg(x64GenContext, regR, regC); + sint32 jumpInstructionOffset2_bottom = x64GenContext->emitter->GetWriteIndex(); x64Gen_jmpc_near(x64GenContext, X86_CONDITION_NONE, 0); // select B bottom - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset1_bottom, x64GenContext->codeBufferIndex); - x64Gen_movsd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r_r_r.registerResult, imlInstruction->op_fpr_r_r_r_r.registerOperandB); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset1_bottom, x64GenContext->emitter->GetWriteIndex()); + x64Gen_movsd_xmmReg_xmmReg(x64GenContext, regR, regB); // end - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset2_bottom, x64GenContext->codeBufferIndex); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset2_bottom, x64GenContext->emitter->GetWriteIndex()); // select top - x64Gen_movhlps_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, imlInstruction->op_fpr_r_r_r_r.registerOperandA); // copy top to bottom (todo: May cause stall?) + x64Gen_movhlps_xmmReg_xmmReg(x64GenContext, REG_RESV_FPR_TEMP, regA); // copy top to bottom (todo: May cause stall?) x64Gen_comisd_xmmReg_mem64Reg64(x64GenContext, REG_RESV_FPR_TEMP, REG_RESV_RECDATA, offsetof(PPCRecompilerInstanceData_t, _x64XMM_constDouble0_0)); - sint32 jumpInstructionOffset1_top = x64GenContext->codeBufferIndex; + sint32 jumpInstructionOffset1_top = x64GenContext->emitter->GetWriteIndex(); x64Gen_jmpc_near(x64GenContext, X86_CONDITION_UNSIGNED_BELOW, 0); // select C top - //x64Gen_movsd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r_r_r.registerResult, imlInstruction->op_fpr_r_r_r_r.registerOperandC); - x64Gen_shufpd_xmmReg_xmmReg_imm8(x64GenContext, imlInstruction->op_fpr_r_r_r_r.registerResult, imlInstruction->op_fpr_r_r_r_r.registerOperandC, 2); - sint32 jumpInstructionOffset2_top = x64GenContext->codeBufferIndex; + //x64Gen_movsd_xmmReg_xmmReg(x64GenContext, registerResult, registerOperandC); + x64Gen_shufpd_xmmReg_xmmReg_imm8(x64GenContext, regR, regC, 2); + sint32 jumpInstructionOffset2_top = x64GenContext->emitter->GetWriteIndex(); x64Gen_jmpc_near(x64GenContext, X86_CONDITION_NONE, 0); // select B top - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset1_top, x64GenContext->codeBufferIndex); - //x64Gen_movsd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r_r_r_r.registerResult, imlInstruction->op_fpr_r_r_r_r.registerOperandB); - x64Gen_shufpd_xmmReg_xmmReg_imm8(x64GenContext, imlInstruction->op_fpr_r_r_r_r.registerResult, imlInstruction->op_fpr_r_r_r_r.registerOperandB, 2); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset1_top, x64GenContext->emitter->GetWriteIndex()); + //x64Gen_movsd_xmmReg_xmmReg(x64GenContext, registerResult, registerOperandB); + x64Gen_shufpd_xmmReg_xmmReg_imm8(x64GenContext, regR, regB, 2); // end - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset2_top, x64GenContext->codeBufferIndex); + PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset2_top, x64GenContext->emitter->GetWriteIndex()); } else assert_dbg(); } -/* - * Single FPR operation - */ -void PPCRecompilerX64Gen_imlInstruction_fpr_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction) +void PPCRecompilerX64Gen_imlInstruction_fpr_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction) { - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); + uint32 regR = _regF64(imlInstruction->op_fpr_r.regR); + if( imlInstruction->operation == PPCREC_IML_OP_FPR_NEGATE_BOTTOM ) { - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); - // toggle sign bit - x64Gen_xorps_xmmReg_mem128Reg64(x64GenContext, imlInstruction->op_fpr_r.registerResult, REG_RESV_RECDATA, offsetof(PPCRecompilerInstanceData_t, _x64XMM_xorNegateMaskBottom)); + x64Gen_xorps_xmmReg_mem128Reg64(x64GenContext, regR, REG_RESV_RECDATA, offsetof(PPCRecompilerInstanceData_t, _x64XMM_xorNegateMaskBottom)); } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_ABS_BOTTOM ) { - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); - // mask out sign bit - x64Gen_andps_xmmReg_mem128Reg64(x64GenContext, imlInstruction->op_fpr_r.registerResult, REG_RESV_RECDATA, offsetof(PPCRecompilerInstanceData_t, _x64XMM_andAbsMaskBottom)); + x64Gen_andps_xmmReg_mem128Reg64(x64GenContext, regR, REG_RESV_RECDATA, offsetof(PPCRecompilerInstanceData_t, _x64XMM_andAbsMaskBottom)); } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_NEGATIVE_ABS_BOTTOM ) { - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); - // set sign bit - x64Gen_orps_xmmReg_mem128Reg64(x64GenContext, imlInstruction->op_fpr_r.registerResult, REG_RESV_RECDATA, offsetof(PPCRecompilerInstanceData_t, _x64XMM_xorNegateMaskBottom)); + x64Gen_orps_xmmReg_mem128Reg64(x64GenContext, regR, REG_RESV_RECDATA, offsetof(PPCRecompilerInstanceData_t, _x64XMM_xorNegateMaskBottom)); } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_ROUND_TO_SINGLE_PRECISION_BOTTOM ) { - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); // convert to 32bit single - x64Gen_cvtsd2ss_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r.registerResult, imlInstruction->op_fpr_r.registerResult); + x64Gen_cvtsd2ss_xmmReg_xmmReg(x64GenContext, regR, regR); // convert back to 64bit double - x64Gen_cvtss2sd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r.registerResult, imlInstruction->op_fpr_r.registerResult); + x64Gen_cvtss2sd_xmmReg_xmmReg(x64GenContext, regR, regR); } else if( imlInstruction->operation == PPCREC_IML_OP_FPR_ROUND_TO_SINGLE_PRECISION_PAIR ) { - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); // convert to 32bit singles - x64Gen_cvtpd2ps_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r.registerResult, imlInstruction->op_fpr_r.registerResult); + x64Gen_cvtpd2ps_xmmReg_xmmReg(x64GenContext, regR, regR); // convert back to 64bit doubles - x64Gen_cvtps2pd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r.registerResult, imlInstruction->op_fpr_r.registerResult); + x64Gen_cvtps2pd_xmmReg_xmmReg(x64GenContext, regR, regR); } else if (imlInstruction->operation == PPCREC_IML_OP_FPR_EXPAND_BOTTOM32_TO_BOTTOM64_AND_TOP64) { - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); // convert bottom to 64bit double - x64Gen_cvtss2sd_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r.registerResult, imlInstruction->op_fpr_r.registerResult); + x64Gen_cvtss2sd_xmmReg_xmmReg(x64GenContext, regR, regR); // copy to top half - x64Gen_movddup_xmmReg_xmmReg(x64GenContext, imlInstruction->op_fpr_r.registerResult, imlInstruction->op_fpr_r.registerResult); + x64Gen_movddup_xmmReg_xmmReg(x64GenContext, regR, regR); } else { cemu_assert_unimplemented(); } } + +void PPCRecompilerX64Gen_imlInstruction_fpr_compare(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction) +{ + auto regR = _reg8(imlInstruction->op_fpr_compare.regR); + auto regA = _regF64(imlInstruction->op_fpr_compare.regA); + auto regB = _regF64(imlInstruction->op_fpr_compare.regB); + + x64GenContext->emitter->XOR_dd(_reg32_from_reg8(regR), _reg32_from_reg8(regR)); + x64Gen_ucomisd_xmmReg_xmmReg(x64GenContext, regA, regB); + + if (imlInstruction->op_fpr_compare.cond == IMLCondition::UNORDERED_GT) + { + // GT case can be covered with a single SETnbe which checks CF==0 && ZF==0 (unordered sets both) + x64GenContext->emitter->SETcc_b(X86Cond::X86_CONDITION_NBE, regR); + return; + } + else if (imlInstruction->op_fpr_compare.cond == IMLCondition::UNORDERED_U) + { + // unordered case can be checked via PF + x64GenContext->emitter->SETcc_b(X86Cond::X86_CONDITION_PE, regR); + return; + } + + // remember unordered state + auto regTmp = _reg32_from_reg8(_reg32(REG_RESV_TEMP)); + x64GenContext->emitter->SETcc_b(X86Cond::X86_CONDITION_PO, regTmp); // by reversing the parity we can avoid having to XOR the value for masking the LT/EQ conditions + + X86Cond x86Cond; + switch (imlInstruction->op_fpr_compare.cond) + { + case IMLCondition::UNORDERED_LT: + x64GenContext->emitter->SETcc_b(X86Cond::X86_CONDITION_B, regR); + break; + case IMLCondition::UNORDERED_EQ: + x64GenContext->emitter->SETcc_b(X86Cond::X86_CONDITION_Z, regR); + break; + default: + cemu_assert_unimplemented(); + } + x64GenContext->emitter->AND_bb(_reg8_from_reg32(regR), _reg8_from_reg32(regTmp)); // if unordered (PF=1) then force LT/GT/EQ to zero +} \ No newline at end of file diff --git a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64Gen.cpp b/src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64Gen.cpp similarity index 90% rename from src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64Gen.cpp rename to src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64Gen.cpp index 19327f46..efe929d0 100644 --- a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64Gen.cpp +++ b/src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64Gen.cpp @@ -1,62 +1,31 @@ -#include "PPCRecompiler.h" -#include "PPCRecompilerIml.h" -#include "PPCRecompilerX64.h" +#include "BackendX64.h" // x86/x64 extension opcodes that could be useful: // ANDN // mulx, rorx, sarx, shlx, shrx // PDEP, PEXT -void x64Gen_checkBuffer(x64GenContext_t* x64GenContext) -{ - // todo -} - void x64Gen_writeU8(x64GenContext_t* x64GenContext, uint8 v) { - if( x64GenContext->codeBufferIndex+1 > x64GenContext->codeBufferSize ) - { - x64GenContext->codeBufferSize *= 2; - x64GenContext->codeBuffer = (uint8*)realloc(x64GenContext->codeBuffer, x64GenContext->codeBufferSize); - } - *(uint8*)(x64GenContext->codeBuffer+x64GenContext->codeBufferIndex) = v; - x64GenContext->codeBufferIndex++; + x64GenContext->emitter->_emitU8(v); } void x64Gen_writeU16(x64GenContext_t* x64GenContext, uint32 v) { - if( x64GenContext->codeBufferIndex+2 > x64GenContext->codeBufferSize ) - { - x64GenContext->codeBufferSize *= 2; - x64GenContext->codeBuffer = (uint8*)realloc(x64GenContext->codeBuffer, x64GenContext->codeBufferSize); - } - *(uint16*)(x64GenContext->codeBuffer+x64GenContext->codeBufferIndex) = v; - x64GenContext->codeBufferIndex += 2; + x64GenContext->emitter->_emitU16(v); } void x64Gen_writeU32(x64GenContext_t* x64GenContext, uint32 v) { - if( x64GenContext->codeBufferIndex+4 > x64GenContext->codeBufferSize ) - { - x64GenContext->codeBufferSize *= 2; - x64GenContext->codeBuffer = (uint8*)realloc(x64GenContext->codeBuffer, x64GenContext->codeBufferSize); - } - *(uint32*)(x64GenContext->codeBuffer+x64GenContext->codeBufferIndex) = v; - x64GenContext->codeBufferIndex += 4; + x64GenContext->emitter->_emitU32(v); } void x64Gen_writeU64(x64GenContext_t* x64GenContext, uint64 v) { - if( x64GenContext->codeBufferIndex+8 > x64GenContext->codeBufferSize ) - { - x64GenContext->codeBufferSize *= 2; - x64GenContext->codeBuffer = (uint8*)realloc(x64GenContext->codeBuffer, x64GenContext->codeBufferSize); - } - *(uint64*)(x64GenContext->codeBuffer+x64GenContext->codeBufferIndex) = v; - x64GenContext->codeBufferIndex += 8; + x64GenContext->emitter->_emitU64(v); } -#include "x64Emit.hpp" +#include "X64Emit.hpp" void _x64Gen_writeMODRMDeprecated(x64GenContext_t* x64GenContext, sint32 dataRegister, sint32 memRegisterA64, sint32 memRegisterB64, sint32 memImmS32) { @@ -67,7 +36,7 @@ void _x64Gen_writeMODRMDeprecated(x64GenContext_t* x64GenContext, sint32 dataReg forceUseOffset = true; } - if (memRegisterB64 == REG_NONE) + if (memRegisterB64 == X86_REG_NONE) { // memRegisterA64 + memImmS32 uint8 modRM = (dataRegister & 7) * 8 + (memRegisterA64 & 7); @@ -352,7 +321,7 @@ void x64Gen_mov_mem32Reg64_imm32(x64GenContext_t* x64GenContext, sint32 memRegis void x64Gen_mov_mem64Reg64_imm32(x64GenContext_t* x64GenContext, sint32 memRegister, uint32 memImmU32, uint32 dataImmU32) { // MOV QWORD [+], dataImmU32 - if( memRegister == REG_R14 ) + if( memRegister == X86_REG_R14 ) { sint32 memImmS32 = (sint32)memImmU32; if( memImmS32 == 0 ) @@ -384,7 +353,7 @@ void x64Gen_mov_mem64Reg64_imm32(x64GenContext_t* x64GenContext, sint32 memRegis void x64Gen_mov_mem8Reg64_imm8(x64GenContext_t* x64GenContext, sint32 memRegister, uint32 memImmU32, uint8 dataImmU8) { // MOV BYTE [+], dataImmU8 - if( memRegister == REG_RSP ) + if( memRegister == X86_REG_RSP ) { sint32 memImmS32 = (sint32)memImmU32; if( memImmS32 >= -128 && memImmS32 <= 127 ) @@ -625,7 +594,7 @@ void _x64_op_reg64Low_mem8Reg64(x64GenContext_t* x64GenContext, sint32 dstRegist if (memRegister64 >= 8) x64Gen_writeU8(x64GenContext, 0x41); x64Gen_writeU8(x64GenContext, opByte); - _x64Gen_writeMODRMDeprecated(x64GenContext, dstRegister, memRegister64, REG_NONE, memImmS32); + _x64Gen_writeMODRMDeprecated(x64GenContext, dstRegister, memRegister64, X86_REG_NONE, memImmS32); } void x64Gen_or_reg64Low8_mem8Reg64(x64GenContext_t* x64GenContext, sint32 dstRegister, sint32 memRegister64, sint32 memImmS32) @@ -643,40 +612,6 @@ void x64Gen_mov_mem8Reg64_reg64Low8(x64GenContext_t* x64GenContext, sint32 dstRe _x64_op_reg64Low_mem8Reg64(x64GenContext, dstRegister, memRegister64, memImmS32, 0x88); } -void x64Gen_lock_cmpxchg_mem32Reg64PlusReg64_reg64(x64GenContext_t* x64GenContext, sint32 memRegisterA64, sint32 memRegisterB64, sint32 memImmS32, sint32 srcRegister) -{ - // LOCK CMPXCHG DWORD [ + + ], (low dword) - x64Gen_writeU8(x64GenContext, 0xF0); // LOCK prefix - - if( srcRegister >= 8 || memRegisterA64 >= 8|| memRegisterB64 >= 8 ) - x64Gen_writeU8(x64GenContext, 0x40+((srcRegister>=8)?4:0)+((memRegisterA64>=8)?1:0)+((memRegisterB64>=8)?2:0)); - - x64Gen_writeU8(x64GenContext, 0x0F); - x64Gen_writeU8(x64GenContext, 0xB1); - - _x64Gen_writeMODRMDeprecated(x64GenContext, srcRegister, memRegisterA64, memRegisterB64, memImmS32); -} - -void x64Gen_lock_cmpxchg_mem32Reg64_reg64(x64GenContext_t* x64GenContext, sint32 memRegister64, sint32 memImmS32, sint32 srcRegister) -{ - // LOCK CMPXCHG DWORD [ + ], (low dword) - x64Gen_writeU8(x64GenContext, 0xF0); // LOCK prefix - - if( srcRegister >= 8 || memRegister64 >= 8 ) - x64Gen_writeU8(x64GenContext, 0x40+((srcRegister>=8)?4:0)+((memRegister64>=8)?1:0)); - - x64Gen_writeU8(x64GenContext, 0x0F); - x64Gen_writeU8(x64GenContext, 0xB1); - - if( memImmS32 == 0 ) - { - x64Gen_writeU8(x64GenContext, 0x45+(srcRegister&7)*8); - x64Gen_writeU8(x64GenContext, 0x00); - } - else - assert_dbg(); -} - void x64Gen_add_reg64_reg64(x64GenContext_t* x64GenContext, sint32 destRegister, sint32 srcRegister) { // ADD , @@ -732,7 +667,7 @@ void x64Gen_add_reg64Low32_imm32(x64GenContext_t* x64GenContext, sint32 srcRegis } else { - if( srcRegister == REG_RAX ) + if( srcRegister == X86_REG_RAX ) { // special EAX short form x64Gen_writeU8(x64GenContext, 0x05); @@ -772,7 +707,7 @@ void x64Gen_sub_reg64Low32_imm32(x64GenContext_t* x64GenContext, sint32 srcRegis } else { - if( srcRegister == REG_RAX ) + if( srcRegister == X86_REG_RAX ) { // special EAX short form x64Gen_writeU8(x64GenContext, 0x2D); @@ -811,7 +746,7 @@ void x64Gen_sub_mem32reg64_imm32(x64GenContext_t* x64GenContext, sint32 memRegis { // SUB , sint32 immS32 = (sint32)immU32; - if( memRegister == REG_RSP ) + if( memRegister == X86_REG_RSP ) { if( memImmS32 >= 128 ) { @@ -843,64 +778,11 @@ void x64Gen_sub_mem32reg64_imm32(x64GenContext_t* x64GenContext, sint32 memRegis } } -void x64Gen_sbb_reg64Low32_reg64Low32(x64GenContext_t* x64GenContext, sint32 destRegister, sint32 srcRegister) -{ - // SBB , - if( destRegister >= 8 && srcRegister >= 8 ) - x64Gen_writeU8(x64GenContext, 0x45); - else if( srcRegister >= 8 ) - x64Gen_writeU8(x64GenContext, 0x44); - else if( destRegister >= 8 ) - x64Gen_writeU8(x64GenContext, 0x41); - x64Gen_writeU8(x64GenContext, 0x19); - x64Gen_writeU8(x64GenContext, 0xC0+(srcRegister&7)*8+(destRegister&7)); -} - -void x64Gen_adc_reg64Low32_reg64Low32(x64GenContext_t* x64GenContext, sint32 destRegister, sint32 srcRegister) -{ - // ADC , - if( destRegister >= 8 && srcRegister >= 8 ) - x64Gen_writeU8(x64GenContext, 0x45); - else if( srcRegister >= 8 ) - x64Gen_writeU8(x64GenContext, 0x44); - else if( destRegister >= 8 ) - x64Gen_writeU8(x64GenContext, 0x41); - x64Gen_writeU8(x64GenContext, 0x11); - x64Gen_writeU8(x64GenContext, 0xC0+(srcRegister&7)*8+(destRegister&7)); -} - -void x64Gen_adc_reg64Low32_imm32(x64GenContext_t* x64GenContext, sint32 srcRegister, uint32 immU32) -{ - sint32 immS32 = (sint32)immU32; - if( srcRegister >= 8 ) - x64Gen_writeU8(x64GenContext, 0x41); - if( immS32 >= -128 && immS32 <= 127 ) - { - x64Gen_writeU8(x64GenContext, 0x83); - x64Gen_writeU8(x64GenContext, 0xD0+(srcRegister&7)); - x64Gen_writeU8(x64GenContext, (uint8)immS32); - } - else - { - if( srcRegister == REG_RAX ) - { - // special EAX short form - x64Gen_writeU8(x64GenContext, 0x15); - } - else - { - x64Gen_writeU8(x64GenContext, 0x81); - x64Gen_writeU8(x64GenContext, 0xD0+(srcRegister&7)); - } - x64Gen_writeU32(x64GenContext, immU32); - } -} - void x64Gen_dec_mem32(x64GenContext_t* x64GenContext, sint32 memoryRegister, uint32 memoryImmU32) { // DEC dword [+imm] sint32 memoryImmS32 = (sint32)memoryImmU32; - if (memoryRegister != REG_RSP) + if (memoryRegister != X86_REG_RSP) assert_dbg(); // not supported yet if (memoryImmS32 >= -128 && memoryImmS32 <= 127) { @@ -981,7 +863,7 @@ void x64Gen_and_reg64Low32_imm32(x64GenContext_t* x64GenContext, sint32 srcRegis } else { - if( srcRegister == REG_RAX ) + if( srcRegister == X86_REG_RAX ) { // special EAX short form x64Gen_writeU8(x64GenContext, 0x25); @@ -1026,7 +908,7 @@ void x64Gen_test_reg64Low32_imm32(x64GenContext_t* x64GenContext, sint32 srcRegi sint32 immS32 = (sint32)immU32; if( srcRegister >= 8 ) x64Gen_writeU8(x64GenContext, 0x41); - if( srcRegister == REG_RAX ) + if( srcRegister == X86_REG_RAX ) { // special EAX short form x64Gen_writeU8(x64GenContext, 0xA9); @@ -1052,7 +934,7 @@ void x64Gen_cmp_reg64Low32_imm32(x64GenContext_t* x64GenContext, sint32 srcRegis } else { - if( srcRegister == REG_RAX ) + if( srcRegister == X86_REG_RAX ) { // special RAX short form x64Gen_writeU8(x64GenContext, 0x3D); @@ -1082,7 +964,7 @@ void x64Gen_cmp_reg64Low32_reg64Low32(x64GenContext_t* x64GenContext, sint32 des void x64Gen_cmp_reg64Low32_mem32reg64(x64GenContext_t* x64GenContext, sint32 destRegister, sint32 memRegister, sint32 memImmS32) { // CMP , DWORD [+] - if( memRegister == REG_RSP ) + if( memRegister == X86_REG_RSP ) { if( memImmS32 >= -128 && memImmS32 <= 127 ) assert_dbg(); // todo -> Shorter instruction form @@ -1112,7 +994,7 @@ void x64Gen_or_reg64Low32_imm32(x64GenContext_t* x64GenContext, sint32 srcRegist } else { - if( srcRegister == REG_RAX ) + if( srcRegister == X86_REG_RAX ) { // special EAX short form x64Gen_writeU8(x64GenContext, 0x0D); @@ -1172,7 +1054,7 @@ void x64Gen_xor_reg64Low32_imm32(x64GenContext_t* x64GenContext, sint32 srcRegis } else { - if( srcRegister == REG_RAX ) + if( srcRegister == X86_REG_RAX ) { // special EAX short form x64Gen_writeU8(x64GenContext, 0x35); @@ -1326,16 +1208,6 @@ void x64Gen_cdq(x64GenContext_t* x64GenContext) x64Gen_writeU8(x64GenContext, 0x99); } -void x64Gen_bswap_reg64(x64GenContext_t* x64GenContext, sint32 destRegister) -{ - if( destRegister >= 8 ) - x64Gen_writeU8(x64GenContext, 0x41|8); - else - x64Gen_writeU8(x64GenContext, 0x40|8); - x64Gen_writeU8(x64GenContext, 0x0F); - x64Gen_writeU8(x64GenContext, 0xC8+(destRegister&7)); -} - void x64Gen_bswap_reg64Lower32bit(x64GenContext_t* x64GenContext, sint32 destRegister) { if( destRegister >= 8 ) @@ -1344,16 +1216,6 @@ void x64Gen_bswap_reg64Lower32bit(x64GenContext_t* x64GenContext, sint32 destReg x64Gen_writeU8(x64GenContext, 0xC8+(destRegister&7)); } -void x64Gen_bswap_reg64Lower16bit(x64GenContext_t* x64GenContext, sint32 destRegister) -{ - assert_dbg(); // do not use this instruction, it's result is always undefined. Instead use ROL , 8 - //x64Gen_writeU8(x64GenContext, 0x66); - //if( destRegister >= 8 ) - // x64Gen_writeU8(x64GenContext, 0x41); - //x64Gen_writeU8(x64GenContext, 0x0F); - //x64Gen_writeU8(x64GenContext, 0xC8+(destRegister&7)); -} - void x64Gen_lzcnt_reg64Low32_reg64Low32(x64GenContext_t* x64GenContext, sint32 destRegister, sint32 srcRegister) { // SSE4 @@ -1388,7 +1250,7 @@ void x64Gen_setcc_mem8(x64GenContext_t* x64GenContext, sint32 conditionType, sin { // SETcc [+imm] sint32 memoryImmS32 = (sint32)memoryImmU32; - if( memoryRegister != REG_RSP ) + if( memoryRegister != X86_REG_RSP ) assert_dbg(); // not supported if( memoryRegister >= 8 ) assert_dbg(); // not supported @@ -1627,7 +1489,7 @@ void x64Gen_bt_mem8(x64GenContext_t* x64GenContext, sint32 memoryRegister, uint3 { // BT [+imm], bitIndex (bit test) sint32 memoryImmS32 = (sint32)memoryImmU32; - if( memoryRegister != REG_RSP ) + if( memoryRegister != X86_REG_RSP ) assert_dbg(); // not supported yet if( memoryImmS32 >= -128 && memoryImmS32 <= 127 ) { @@ -1662,7 +1524,7 @@ void x64Gen_jmp_imm32(x64GenContext_t* x64GenContext, uint32 destImm32) void x64Gen_jmp_memReg64(x64GenContext_t* x64GenContext, sint32 memRegister, uint32 immU32) { - if( memRegister == REG_NONE ) + if( memRegister == X86_REG_NONE ) { assert_dbg(); } diff --git a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64GenFPU.cpp b/src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64GenFPU.cpp similarity index 97% rename from src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64GenFPU.cpp rename to src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64GenFPU.cpp index 92289d68..882820e2 100644 --- a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64GenFPU.cpp +++ b/src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64GenFPU.cpp @@ -1,6 +1,4 @@ -#include "PPCRecompiler.h" -#include "PPCRecompilerIml.h" -#include "PPCRecompilerX64.h" +#include "BackendX64.h" void x64Gen_genSSEVEXPrefix2(x64GenContext_t* x64GenContext, sint32 xmmRegister1, sint32 xmmRegister2, bool use64BitMode) { @@ -44,7 +42,7 @@ void x64Gen_movupd_xmmReg_memReg128(x64GenContext_t* x64GenContext, sint32 xmmRe // SSE2 // move two doubles from memory into xmm register // MOVUPD , [+] - if( memRegister == REG_ESP ) + if( memRegister == X86_REG_ESP ) { // todo: Short form of instruction if memImmU32 is 0 or in -128 to 127 range // 66 0F 10 84 E4 23 01 00 00 @@ -56,7 +54,7 @@ void x64Gen_movupd_xmmReg_memReg128(x64GenContext_t* x64GenContext, sint32 xmmRe x64Gen_writeU8(x64GenContext, 0xE4); x64Gen_writeU32(x64GenContext, memImmU32); } - else if( memRegister == REG_NONE ) + else if( memRegister == X86_REG_NONE ) { assert_dbg(); //x64Gen_writeU8(x64GenContext, 0x66); @@ -76,7 +74,7 @@ void x64Gen_movupd_memReg128_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRe // SSE2 // move two doubles from memory into xmm register // MOVUPD [+], - if( memRegister == REG_ESP ) + if( memRegister == X86_REG_ESP ) { // todo: Short form of instruction if memImmU32 is 0 or in -128 to 127 range x64Gen_writeU8(x64GenContext, 0x66); @@ -87,7 +85,7 @@ void x64Gen_movupd_memReg128_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRe x64Gen_writeU8(x64GenContext, 0xE4); x64Gen_writeU32(x64GenContext, memImmU32); } - else if( memRegister == REG_NONE ) + else if( memRegister == X86_REG_NONE ) { assert_dbg(); //x64Gen_writeU8(x64GenContext, 0x66); @@ -106,7 +104,7 @@ void x64Gen_movddup_xmmReg_memReg64(x64GenContext_t* x64GenContext, sint32 xmmRe { // SSE3 // move one double from memory into lower and upper half of a xmm register - if( memRegister == REG_RSP ) + if( memRegister == X86_REG_RSP ) { // MOVDDUP , [+] // todo: Short form of instruction if memImmU32 is 0 or in -128 to 127 range @@ -119,7 +117,7 @@ void x64Gen_movddup_xmmReg_memReg64(x64GenContext_t* x64GenContext, sint32 xmmRe x64Gen_writeU8(x64GenContext, 0xE4); x64Gen_writeU32(x64GenContext, memImmU32); } - else if( memRegister == REG_R15 ) + else if( memRegister == X86_REG_R15 ) { // MOVDDUP , [+] // todo: Short form of instruction if memImmU32 is 0 or in -128 to 127 range @@ -131,7 +129,7 @@ void x64Gen_movddup_xmmReg_memReg64(x64GenContext_t* x64GenContext, sint32 xmmRe x64Gen_writeU8(x64GenContext, 0x87+(xmmRegister&7)*8); x64Gen_writeU32(x64GenContext, memImmU32); } - else if( memRegister == REG_NONE ) + else if( memRegister == X86_REG_NONE ) { // MOVDDUP , [] // 36 F2 0F 12 05 - 00 00 00 00 @@ -185,7 +183,7 @@ void x64Gen_movsd_memReg64_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRegi { // SSE2 // move lower 64bits (double) of xmm register to memory location - if( memRegister == REG_NONE ) + if( memRegister == X86_REG_NONE ) { // MOVSD [], // F2 0F 11 05 - 45 23 01 00 @@ -197,7 +195,7 @@ void x64Gen_movsd_memReg64_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRegi //x64Gen_writeU8(x64GenContext, 0x05+xmmRegister*8); //x64Gen_writeU32(x64GenContext, memImmU32); } - else if( memRegister == REG_RSP ) + else if( memRegister == X86_REG_RSP ) { // MOVSD [RSP+], // F2 0F 11 84 24 - 33 22 11 00 @@ -219,7 +217,7 @@ void x64Gen_movlpd_xmmReg_memReg64(x64GenContext_t* x64GenContext, sint32 xmmReg { // SSE3 // move one double from memory into lower half of a xmm register, leave upper half unchanged(?) - if( memRegister == REG_NONE ) + if( memRegister == X86_REG_NONE ) { // MOVLPD , [] //x64Gen_writeU8(x64GenContext, 0x66); @@ -229,7 +227,7 @@ void x64Gen_movlpd_xmmReg_memReg64(x64GenContext_t* x64GenContext, sint32 xmmReg //x64Gen_writeU32(x64GenContext, memImmU32); assert_dbg(); } - else if( memRegister == REG_RSP ) + else if( memRegister == X86_REG_RSP ) { // MOVLPD , [+] // 66 0F 12 84 24 - 33 22 11 00 @@ -348,11 +346,11 @@ void x64Gen_mulpd_xmmReg_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRegist void x64Gen_mulpd_xmmReg_memReg128(x64GenContext_t* x64GenContext, sint32 xmmRegister, sint32 memRegister, uint32 memImmU32) { // SSE2 - if (memRegister == REG_NONE) + if (memRegister == X86_REG_NONE) { assert_dbg(); } - else if (memRegister == REG_R14) + else if (memRegister == X86_REG_R14) { x64Gen_writeU8(x64GenContext, 0x66); x64Gen_writeU8(x64GenContext, (xmmRegister < 8) ? 0x41 : 0x45); @@ -404,7 +402,7 @@ void x64Gen_comisd_xmmReg_mem64Reg64(x64GenContext_t* x64GenContext, sint32 xmmR { // SSE2 // compare bottom double with double from memory location - if( memoryReg == REG_R15 ) + if( memoryReg == X86_REG_R15 ) { x64Gen_writeU8(x64GenContext, 0x66); x64Gen_genSSEVEXPrefix1(x64GenContext, xmmRegisterDest, true); @@ -432,7 +430,7 @@ void x64Gen_comiss_xmmReg_mem64Reg64(x64GenContext_t* x64GenContext, sint32 xmmR { // SSE2 // compare bottom float with float from memory location - if (memoryReg == REG_R15) + if (memoryReg == X86_REG_R15) { x64Gen_genSSEVEXPrefix1(x64GenContext, xmmRegisterDest, true); x64Gen_writeU8(x64GenContext, 0x0F); @@ -448,7 +446,7 @@ void x64Gen_orps_xmmReg_mem128Reg64(x64GenContext_t* x64GenContext, sint32 xmmRe { // SSE2 // and xmm register with 128 bit value from memory - if( memReg == REG_R15 ) + if( memReg == X86_REG_R15 ) { x64Gen_genSSEVEXPrefix2(x64GenContext, memReg, xmmRegisterDest, false); x64Gen_writeU8(x64GenContext, 0x0F); @@ -464,7 +462,7 @@ void x64Gen_xorps_xmmReg_mem128Reg64(x64GenContext_t* x64GenContext, sint32 xmmR { // SSE2 // xor xmm register with 128 bit value from memory - if( memReg == REG_R15 ) + if( memReg == X86_REG_R15 ) { x64Gen_genSSEVEXPrefix1(x64GenContext, xmmRegisterDest, true); // todo: should be x64Gen_genSSEVEXPrefix2() with memReg? x64Gen_writeU8(x64GenContext, 0x0F); @@ -479,11 +477,11 @@ void x64Gen_xorps_xmmReg_mem128Reg64(x64GenContext_t* x64GenContext, sint32 xmmR void x64Gen_andpd_xmmReg_memReg128(x64GenContext_t* x64GenContext, sint32 xmmRegister, sint32 memRegister, uint32 memImmU32) { // SSE2 - if (memRegister == REG_NONE) + if (memRegister == X86_REG_NONE) { assert_dbg(); } - else if (memRegister == REG_R14) + else if (memRegister == X86_REG_R14) { x64Gen_writeU8(x64GenContext, 0x66); x64Gen_writeU8(x64GenContext, (xmmRegister < 8) ? 0x41 : 0x45); @@ -502,7 +500,7 @@ void x64Gen_andps_xmmReg_mem128Reg64(x64GenContext_t* x64GenContext, sint32 xmmR { // SSE2 // and xmm register with 128 bit value from memory - if( memReg == REG_R15 ) + if( memReg == X86_REG_R15 ) { x64Gen_genSSEVEXPrefix1(x64GenContext, xmmRegisterDest, true); // todo: should be x64Gen_genSSEVEXPrefix2() with memReg? x64Gen_writeU8(x64GenContext, 0x0F); @@ -528,7 +526,7 @@ void x64Gen_pcmpeqd_xmmReg_mem128Reg64(x64GenContext_t* x64GenContext, sint32 xm { // SSE2 // doubleword integer compare - if( memReg == REG_R15 ) + if( memReg == X86_REG_R15 ) { x64Gen_writeU8(x64GenContext, 0x66); x64Gen_genSSEVEXPrefix1(x64GenContext, xmmRegisterDest, true); @@ -610,7 +608,7 @@ void x64Gen_cvtpi2pd_xmmReg_mem64Reg64(x64GenContext_t* x64GenContext, sint32 xm { // SSE2 // converts two signed 32bit integers to two doubles - if( memReg == REG_RSP ) + if( memReg == X86_REG_RSP ) { x64Gen_writeU8(x64GenContext, 0x66); x64Gen_genSSEVEXPrefix1(x64GenContext, xmmRegisterDest, false); @@ -684,7 +682,7 @@ void x64Gen_rcpss_xmmReg_xmmReg(x64GenContext_t* x64GenContext, sint32 xmmRegist void x64Gen_mulss_xmmReg_memReg64(x64GenContext_t* x64GenContext, sint32 xmmRegister, sint32 memRegister, uint32 memImmU32) { // SSE2 - if( memRegister == REG_NONE ) + if( memRegister == X86_REG_NONE ) { assert_dbg(); } diff --git a/src/Cafe/HW/Espresso/Recompiler/x64Emit.hpp b/src/Cafe/HW/Espresso/Recompiler/BackendX64/X64Emit.hpp similarity index 99% rename from src/Cafe/HW/Espresso/Recompiler/x64Emit.hpp rename to src/Cafe/HW/Espresso/Recompiler/BackendX64/X64Emit.hpp index e936f1d8..b4021931 100644 --- a/src/Cafe/HW/Espresso/Recompiler/x64Emit.hpp +++ b/src/Cafe/HW/Espresso/Recompiler/BackendX64/X64Emit.hpp @@ -203,7 +203,6 @@ template void _x64Gen_writeMODRM_internal(x64GenContext_t* x64GenContext, TA opA, TB opB) { static_assert(TA::getType() == MODRM_OPR_TYPE::REG); - x64Gen_checkBuffer(x64GenContext); // REX prefix // 0100 WRXB if constexpr (TA::getType() == MODRM_OPR_TYPE::REG && TB::getType() == MODRM_OPR_TYPE::REG) diff --git a/src/Cafe/HW/Espresso/Recompiler/BackendX64/x86Emitter.h b/src/Cafe/HW/Espresso/Recompiler/BackendX64/x86Emitter.h new file mode 100644 index 00000000..eae3835d --- /dev/null +++ b/src/Cafe/HW/Espresso/Recompiler/BackendX64/x86Emitter.h @@ -0,0 +1,4335 @@ +#pragma once + +// x86-64 assembler/emitter +// auto generated. Do not edit this file manually + +typedef unsigned long long u64; +typedef unsigned int u32; +typedef unsigned short u16; +typedef unsigned char u8; +typedef signed long long s64; +typedef signed int s32; +typedef signed short s16; +typedef signed char s8; + +enum X86Reg : sint8 +{ + X86_REG_NONE = -1, + X86_REG_EAX = 0, + X86_REG_ECX = 1, + X86_REG_EDX = 2, + X86_REG_EBX = 3, + X86_REG_ESP = 4, + X86_REG_EBP = 5, + X86_REG_ESI = 6, + X86_REG_EDI = 7, + X86_REG_R8D = 8, + X86_REG_R9D = 9, + X86_REG_R10D = 10, + X86_REG_R11D = 11, + X86_REG_R12D = 12, + X86_REG_R13D = 13, + X86_REG_R14D = 14, + X86_REG_R15D = 15, + X86_REG_RAX = 0, + X86_REG_RCX = 1, + X86_REG_RDX = 2, + X86_REG_RBX = 3, + X86_REG_RSP = 4, + X86_REG_RBP = 5, + X86_REG_RSI = 6, + X86_REG_RDI = 7, + X86_REG_R8 = 8, + X86_REG_R9 = 9, + X86_REG_R10 = 10, + X86_REG_R11 = 11, + X86_REG_R12 = 12, + X86_REG_R13 = 13, + X86_REG_R14 = 14, + X86_REG_R15 = 15 +}; + +enum X86Cond : u8 +{ + X86_CONDITION_O = 0, + X86_CONDITION_NO = 1, + X86_CONDITION_B = 2, + X86_CONDITION_NB = 3, + X86_CONDITION_Z = 4, + X86_CONDITION_NZ = 5, + X86_CONDITION_BE = 6, + X86_CONDITION_NBE = 7, + X86_CONDITION_S = 8, + X86_CONDITION_NS = 9, + X86_CONDITION_PE = 10, + X86_CONDITION_PO = 11, + X86_CONDITION_L = 12, + X86_CONDITION_NL = 13, + X86_CONDITION_LE = 14, + X86_CONDITION_NLE = 15 +}; +class x86Assembler64 +{ +private: + std::vector m_buffer; + +public: + u8* GetBufferPtr() { return m_buffer.data(); }; + std::span GetBuffer() { return m_buffer; }; + u32 GetWriteIndex() { return (u32)m_buffer.size(); }; + void _emitU8(u8 v) { m_buffer.emplace_back(v); }; + void _emitU16(u16 v) { size_t writeIdx = m_buffer.size(); m_buffer.resize(writeIdx + 2); *(u16*)(m_buffer.data() + writeIdx) = v; }; + void _emitU32(u32 v) { size_t writeIdx = m_buffer.size(); m_buffer.resize(writeIdx + 4); *(u32*)(m_buffer.data() + writeIdx) = v; }; + void _emitU64(u64 v) { size_t writeIdx = m_buffer.size(); m_buffer.resize(writeIdx + 8); *(u64*)(m_buffer.data() + writeIdx) = v; }; + using GPR64 = X86Reg; + using GPR32 = X86Reg; + using GPR8_REX = X86Reg; + void LockPrefix() { _emitU8(0xF0); }; + void ADD_bb(GPR8_REX dst, GPR8_REX src) + { + if ((src >= 4) || (dst >= 4)) + { + _emitU8(0x40 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + } + _emitU8(0x00); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void ADD_bb_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR8_REX src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((src >= 4) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((src >= 4) || (memReg & 8)) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x00); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void ADD_bb_r(GPR8_REX dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((dst >= 4) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((dst >= 4) || (memReg & 8)) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x02); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void ADD_dd(GPR32 dst, GPR32 src) + { + if (((src & 8) != 0) || ((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + } + _emitU8(0x01); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void ADD_qq(GPR64 dst, GPR64 src) + { + _emitU8(0x48 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + _emitU8(0x01); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void ADD_dd_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR32 src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((src & 8) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((src & 8) || (memReg & 8)) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x01); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void ADD_qq_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR64 src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x01); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void ADD_dd_r(GPR32 dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((dst & 8) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((dst & 8) || (memReg & 8)) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x03); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void ADD_qq_r(GPR64 dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x03); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void OR_bb(GPR8_REX dst, GPR8_REX src) + { + if ((src >= 4) || (dst >= 4)) + { + _emitU8(0x40 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + } + _emitU8(0x08); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void OR_bb_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR8_REX src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((src >= 4) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((src >= 4) || (memReg & 8)) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x08); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void OR_bb_r(GPR8_REX dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((dst >= 4) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((dst >= 4) || (memReg & 8)) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x0a); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void OR_dd(GPR32 dst, GPR32 src) + { + if (((src & 8) != 0) || ((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + } + _emitU8(0x09); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void OR_qq(GPR64 dst, GPR64 src) + { + _emitU8(0x48 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + _emitU8(0x09); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void OR_dd_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR32 src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((src & 8) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((src & 8) || (memReg & 8)) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x09); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void OR_qq_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR64 src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x09); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void OR_dd_r(GPR32 dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((dst & 8) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((dst & 8) || (memReg & 8)) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x0b); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void OR_qq_r(GPR64 dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x0b); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void ADC_bb(GPR8_REX dst, GPR8_REX src) + { + if ((src >= 4) || (dst >= 4)) + { + _emitU8(0x40 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + } + _emitU8(0x10); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void ADC_bb_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR8_REX src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((src >= 4) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((src >= 4) || (memReg & 8)) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x10); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void ADC_bb_r(GPR8_REX dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((dst >= 4) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((dst >= 4) || (memReg & 8)) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x12); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void ADC_dd(GPR32 dst, GPR32 src) + { + if (((src & 8) != 0) || ((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + } + _emitU8(0x11); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void ADC_qq(GPR64 dst, GPR64 src) + { + _emitU8(0x48 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + _emitU8(0x11); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void ADC_dd_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR32 src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((src & 8) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((src & 8) || (memReg & 8)) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x11); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void ADC_qq_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR64 src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x11); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void ADC_dd_r(GPR32 dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((dst & 8) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((dst & 8) || (memReg & 8)) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x13); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void ADC_qq_r(GPR64 dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x13); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void SBB_bb(GPR8_REX dst, GPR8_REX src) + { + if ((src >= 4) || (dst >= 4)) + { + _emitU8(0x40 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + } + _emitU8(0x18); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void SBB_bb_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR8_REX src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((src >= 4) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((src >= 4) || (memReg & 8)) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x18); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void SBB_bb_r(GPR8_REX dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((dst >= 4) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((dst >= 4) || (memReg & 8)) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x1a); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void SBB_dd(GPR32 dst, GPR32 src) + { + if (((src & 8) != 0) || ((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + } + _emitU8(0x19); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void SBB_qq(GPR64 dst, GPR64 src) + { + _emitU8(0x48 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + _emitU8(0x19); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void SBB_dd_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR32 src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((src & 8) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((src & 8) || (memReg & 8)) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x19); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void SBB_qq_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR64 src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x19); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void SBB_dd_r(GPR32 dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((dst & 8) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((dst & 8) || (memReg & 8)) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x1b); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void SBB_qq_r(GPR64 dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x1b); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void AND_bb(GPR8_REX dst, GPR8_REX src) + { + if ((src >= 4) || (dst >= 4)) + { + _emitU8(0x40 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + } + _emitU8(0x20); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void AND_bb_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR8_REX src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((src >= 4) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((src >= 4) || (memReg & 8)) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x20); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void AND_bb_r(GPR8_REX dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((dst >= 4) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((dst >= 4) || (memReg & 8)) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x22); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void AND_dd(GPR32 dst, GPR32 src) + { + if (((src & 8) != 0) || ((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + } + _emitU8(0x21); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void AND_qq(GPR64 dst, GPR64 src) + { + _emitU8(0x48 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + _emitU8(0x21); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void AND_dd_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR32 src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((src & 8) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((src & 8) || (memReg & 8)) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x21); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void AND_qq_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR64 src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x21); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void AND_dd_r(GPR32 dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((dst & 8) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((dst & 8) || (memReg & 8)) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x23); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void AND_qq_r(GPR64 dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x23); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void SUB_bb(GPR8_REX dst, GPR8_REX src) + { + if ((src >= 4) || (dst >= 4)) + { + _emitU8(0x40 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + } + _emitU8(0x28); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void SUB_bb_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR8_REX src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((src >= 4) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((src >= 4) || (memReg & 8)) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x28); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void SUB_bb_r(GPR8_REX dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((dst >= 4) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((dst >= 4) || (memReg & 8)) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x2a); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void SUB_dd(GPR32 dst, GPR32 src) + { + if (((src & 8) != 0) || ((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + } + _emitU8(0x29); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void SUB_qq(GPR64 dst, GPR64 src) + { + _emitU8(0x48 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + _emitU8(0x29); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void SUB_dd_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR32 src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((src & 8) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((src & 8) || (memReg & 8)) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x29); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void SUB_qq_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR64 src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x29); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void SUB_dd_r(GPR32 dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((dst & 8) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((dst & 8) || (memReg & 8)) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x2b); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void SUB_qq_r(GPR64 dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x2b); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void XOR_bb(GPR8_REX dst, GPR8_REX src) + { + if ((src >= 4) || (dst >= 4)) + { + _emitU8(0x40 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + } + _emitU8(0x30); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void XOR_bb_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR8_REX src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((src >= 4) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((src >= 4) || (memReg & 8)) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x30); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void XOR_bb_r(GPR8_REX dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((dst >= 4) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((dst >= 4) || (memReg & 8)) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x32); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void XOR_dd(GPR32 dst, GPR32 src) + { + if (((src & 8) != 0) || ((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + } + _emitU8(0x31); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void XOR_qq(GPR64 dst, GPR64 src) + { + _emitU8(0x48 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + _emitU8(0x31); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void XOR_dd_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR32 src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((src & 8) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((src & 8) || (memReg & 8)) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x31); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void XOR_qq_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR64 src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x31); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void XOR_dd_r(GPR32 dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((dst & 8) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((dst & 8) || (memReg & 8)) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x33); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void XOR_qq_r(GPR64 dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x33); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void CMP_bb(GPR8_REX dst, GPR8_REX src) + { + if ((src >= 4) || (dst >= 4)) + { + _emitU8(0x40 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + } + _emitU8(0x38); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void CMP_bb_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR8_REX src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((src >= 4) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((src >= 4) || (memReg & 8)) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x38); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void CMP_bb_r(GPR8_REX dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((dst >= 4) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((dst >= 4) || (memReg & 8)) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x3a); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void CMP_dd(GPR32 dst, GPR32 src) + { + if (((src & 8) != 0) || ((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + } + _emitU8(0x39); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void CMP_qq(GPR64 dst, GPR64 src) + { + _emitU8(0x48 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + _emitU8(0x39); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void CMP_dd_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR32 src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((src & 8) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((src & 8) || (memReg & 8)) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x39); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void CMP_qq_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR64 src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x39); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void CMP_dd_r(GPR32 dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((dst & 8) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((dst & 8) || (memReg & 8)) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x3b); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void CMP_qq_r(GPR64 dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x3b); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void ADD_di32(GPR32 dst, s32 imm) + { + if (((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0x81); + _emitU8((3 << 6) | ((0 & 7) << 3) | (dst & 7)); + _emitU32((u32)imm); + } + void ADD_qi32(GPR64 dst, s32 imm) + { + _emitU8(0x48 | ((dst & 8) >> 3)); + _emitU8(0x81); + _emitU8((3 << 6) | ((0 & 7) << 3) | (dst & 7)); + _emitU32((u32)imm); + } + void ADD_di32_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s32 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0x81); + _emitU8((mod << 6) | ((0 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU32((u32)imm); + } + void ADD_qi32_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s32 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x81); + _emitU8((mod << 6) | ((0 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU32((u32)imm); + } + void OR_di32(GPR32 dst, s32 imm) + { + if (((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0x81); + _emitU8((3 << 6) | ((1 & 7) << 3) | (dst & 7)); + _emitU32((u32)imm); + } + void OR_qi32(GPR64 dst, s32 imm) + { + _emitU8(0x48 | ((dst & 8) >> 3)); + _emitU8(0x81); + _emitU8((3 << 6) | ((1 & 7) << 3) | (dst & 7)); + _emitU32((u32)imm); + } + void OR_di32_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s32 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0x81); + _emitU8((mod << 6) | ((1 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU32((u32)imm); + } + void OR_qi32_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s32 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x81); + _emitU8((mod << 6) | ((1 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU32((u32)imm); + } + void ADC_di32(GPR32 dst, s32 imm) + { + if (((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0x81); + _emitU8((3 << 6) | ((2 & 7) << 3) | (dst & 7)); + _emitU32((u32)imm); + } + void ADC_qi32(GPR64 dst, s32 imm) + { + _emitU8(0x48 | ((dst & 8) >> 3)); + _emitU8(0x81); + _emitU8((3 << 6) | ((2 & 7) << 3) | (dst & 7)); + _emitU32((u32)imm); + } + void ADC_di32_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s32 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0x81); + _emitU8((mod << 6) | ((2 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU32((u32)imm); + } + void ADC_qi32_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s32 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x81); + _emitU8((mod << 6) | ((2 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU32((u32)imm); + } + void SBB_di32(GPR32 dst, s32 imm) + { + if (((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0x81); + _emitU8((3 << 6) | ((3 & 7) << 3) | (dst & 7)); + _emitU32((u32)imm); + } + void SBB_qi32(GPR64 dst, s32 imm) + { + _emitU8(0x48 | ((dst & 8) >> 3)); + _emitU8(0x81); + _emitU8((3 << 6) | ((3 & 7) << 3) | (dst & 7)); + _emitU32((u32)imm); + } + void SBB_di32_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s32 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0x81); + _emitU8((mod << 6) | ((3 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU32((u32)imm); + } + void SBB_qi32_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s32 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x81); + _emitU8((mod << 6) | ((3 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU32((u32)imm); + } + void AND_di32(GPR32 dst, s32 imm) + { + if (((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0x81); + _emitU8((3 << 6) | ((4 & 7) << 3) | (dst & 7)); + _emitU32((u32)imm); + } + void AND_qi32(GPR64 dst, s32 imm) + { + _emitU8(0x48 | ((dst & 8) >> 3)); + _emitU8(0x81); + _emitU8((3 << 6) | ((4 & 7) << 3) | (dst & 7)); + _emitU32((u32)imm); + } + void AND_di32_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s32 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0x81); + _emitU8((mod << 6) | ((4 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU32((u32)imm); + } + void AND_qi32_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s32 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x81); + _emitU8((mod << 6) | ((4 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU32((u32)imm); + } + void SUB_di32(GPR32 dst, s32 imm) + { + if (((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0x81); + _emitU8((3 << 6) | ((5 & 7) << 3) | (dst & 7)); + _emitU32((u32)imm); + } + void SUB_qi32(GPR64 dst, s32 imm) + { + _emitU8(0x48 | ((dst & 8) >> 3)); + _emitU8(0x81); + _emitU8((3 << 6) | ((5 & 7) << 3) | (dst & 7)); + _emitU32((u32)imm); + } + void SUB_di32_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s32 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0x81); + _emitU8((mod << 6) | ((5 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU32((u32)imm); + } + void SUB_qi32_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s32 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x81); + _emitU8((mod << 6) | ((5 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU32((u32)imm); + } + void XOR_di32(GPR32 dst, s32 imm) + { + if (((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0x81); + _emitU8((3 << 6) | ((6 & 7) << 3) | (dst & 7)); + _emitU32((u32)imm); + } + void XOR_qi32(GPR64 dst, s32 imm) + { + _emitU8(0x48 | ((dst & 8) >> 3)); + _emitU8(0x81); + _emitU8((3 << 6) | ((6 & 7) << 3) | (dst & 7)); + _emitU32((u32)imm); + } + void XOR_di32_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s32 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0x81); + _emitU8((mod << 6) | ((6 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU32((u32)imm); + } + void XOR_qi32_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s32 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x81); + _emitU8((mod << 6) | ((6 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU32((u32)imm); + } + void CMP_di32(GPR32 dst, s32 imm) + { + if (((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0x81); + _emitU8((3 << 6) | ((7 & 7) << 3) | (dst & 7)); + _emitU32((u32)imm); + } + void CMP_qi32(GPR64 dst, s32 imm) + { + _emitU8(0x48 | ((dst & 8) >> 3)); + _emitU8(0x81); + _emitU8((3 << 6) | ((7 & 7) << 3) | (dst & 7)); + _emitU32((u32)imm); + } + void CMP_di32_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s32 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0x81); + _emitU8((mod << 6) | ((7 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU32((u32)imm); + } + void CMP_qi32_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s32 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x81); + _emitU8((mod << 6) | ((7 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU32((u32)imm); + } + void ADD_di8(GPR32 dst, s8 imm) + { + if (((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0x83); + _emitU8((3 << 6) | ((0 & 7) << 3) | (dst & 7)); + _emitU8((u8)imm); + } + void ADD_qi8(GPR64 dst, s8 imm) + { + _emitU8(0x48 | ((dst & 8) >> 3)); + _emitU8(0x83); + _emitU8((3 << 6) | ((0 & 7) << 3) | (dst & 7)); + _emitU8((u8)imm); + } + void ADD_di8_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s8 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0x83); + _emitU8((mod << 6) | ((0 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU8((u8)imm); + } + void ADD_qi8_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s8 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x83); + _emitU8((mod << 6) | ((0 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU8((u8)imm); + } + void OR_di8(GPR32 dst, s8 imm) + { + if (((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0x83); + _emitU8((3 << 6) | ((1 & 7) << 3) | (dst & 7)); + _emitU8((u8)imm); + } + void OR_qi8(GPR64 dst, s8 imm) + { + _emitU8(0x48 | ((dst & 8) >> 3)); + _emitU8(0x83); + _emitU8((3 << 6) | ((1 & 7) << 3) | (dst & 7)); + _emitU8((u8)imm); + } + void OR_di8_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s8 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0x83); + _emitU8((mod << 6) | ((1 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU8((u8)imm); + } + void OR_qi8_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s8 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x83); + _emitU8((mod << 6) | ((1 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU8((u8)imm); + } + void ADC_di8(GPR32 dst, s8 imm) + { + if (((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0x83); + _emitU8((3 << 6) | ((2 & 7) << 3) | (dst & 7)); + _emitU8((u8)imm); + } + void ADC_qi8(GPR64 dst, s8 imm) + { + _emitU8(0x48 | ((dst & 8) >> 3)); + _emitU8(0x83); + _emitU8((3 << 6) | ((2 & 7) << 3) | (dst & 7)); + _emitU8((u8)imm); + } + void ADC_di8_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s8 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0x83); + _emitU8((mod << 6) | ((2 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU8((u8)imm); + } + void ADC_qi8_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s8 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x83); + _emitU8((mod << 6) | ((2 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU8((u8)imm); + } + void SBB_di8(GPR32 dst, s8 imm) + { + if (((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0x83); + _emitU8((3 << 6) | ((3 & 7) << 3) | (dst & 7)); + _emitU8((u8)imm); + } + void SBB_qi8(GPR64 dst, s8 imm) + { + _emitU8(0x48 | ((dst & 8) >> 3)); + _emitU8(0x83); + _emitU8((3 << 6) | ((3 & 7) << 3) | (dst & 7)); + _emitU8((u8)imm); + } + void SBB_di8_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s8 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0x83); + _emitU8((mod << 6) | ((3 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU8((u8)imm); + } + void SBB_qi8_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s8 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x83); + _emitU8((mod << 6) | ((3 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU8((u8)imm); + } + void AND_di8(GPR32 dst, s8 imm) + { + if (((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0x83); + _emitU8((3 << 6) | ((4 & 7) << 3) | (dst & 7)); + _emitU8((u8)imm); + } + void AND_qi8(GPR64 dst, s8 imm) + { + _emitU8(0x48 | ((dst & 8) >> 3)); + _emitU8(0x83); + _emitU8((3 << 6) | ((4 & 7) << 3) | (dst & 7)); + _emitU8((u8)imm); + } + void AND_di8_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s8 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0x83); + _emitU8((mod << 6) | ((4 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU8((u8)imm); + } + void AND_qi8_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s8 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x83); + _emitU8((mod << 6) | ((4 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU8((u8)imm); + } + void SUB_di8(GPR32 dst, s8 imm) + { + if (((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0x83); + _emitU8((3 << 6) | ((5 & 7) << 3) | (dst & 7)); + _emitU8((u8)imm); + } + void SUB_qi8(GPR64 dst, s8 imm) + { + _emitU8(0x48 | ((dst & 8) >> 3)); + _emitU8(0x83); + _emitU8((3 << 6) | ((5 & 7) << 3) | (dst & 7)); + _emitU8((u8)imm); + } + void SUB_di8_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s8 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0x83); + _emitU8((mod << 6) | ((5 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU8((u8)imm); + } + void SUB_qi8_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s8 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x83); + _emitU8((mod << 6) | ((5 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU8((u8)imm); + } + void XOR_di8(GPR32 dst, s8 imm) + { + if (((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0x83); + _emitU8((3 << 6) | ((6 & 7) << 3) | (dst & 7)); + _emitU8((u8)imm); + } + void XOR_qi8(GPR64 dst, s8 imm) + { + _emitU8(0x48 | ((dst & 8) >> 3)); + _emitU8(0x83); + _emitU8((3 << 6) | ((6 & 7) << 3) | (dst & 7)); + _emitU8((u8)imm); + } + void XOR_di8_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s8 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0x83); + _emitU8((mod << 6) | ((6 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU8((u8)imm); + } + void XOR_qi8_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s8 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x83); + _emitU8((mod << 6) | ((6 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU8((u8)imm); + } + void CMP_di8(GPR32 dst, s8 imm) + { + if (((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0x83); + _emitU8((3 << 6) | ((7 & 7) << 3) | (dst & 7)); + _emitU8((u8)imm); + } + void CMP_qi8(GPR64 dst, s8 imm) + { + _emitU8(0x48 | ((dst & 8) >> 3)); + _emitU8(0x83); + _emitU8((3 << 6) | ((7 & 7) << 3) | (dst & 7)); + _emitU8((u8)imm); + } + void CMP_di8_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s8 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0x83); + _emitU8((mod << 6) | ((7 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU8((u8)imm); + } + void CMP_qi8_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s8 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x83); + _emitU8((mod << 6) | ((7 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU8((u8)imm); + } + void TEST_bb(GPR8_REX dst, GPR8_REX src) + { + if ((src >= 4) || (dst >= 4)) + { + _emitU8(0x40 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + } + _emitU8(0x84); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void TEST_bb_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR8_REX src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((src >= 4) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((src >= 4) || (memReg & 8)) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x84); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void TEST_dd(GPR32 dst, GPR32 src) + { + if (((src & 8) != 0) || ((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + } + _emitU8(0x85); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void TEST_qq(GPR64 dst, GPR64 src) + { + _emitU8(0x48 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + _emitU8(0x85); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void TEST_dd_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR32 src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((src & 8) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((src & 8) || (memReg & 8)) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x85); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void TEST_qq_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR64 src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x85); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void XCHG_bb(GPR8_REX dst, GPR8_REX src) + { + if ((dst >= 4) || (src >= 4)) + { + _emitU8(0x40 | ((src & 8) >> 3) | ((dst & 8) >> 1)); + } + _emitU8(0x86); + _emitU8((3 << 6) | ((dst & 7) << 3) | (src & 7)); + } + void XCHG_bb_r(GPR8_REX dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((dst >= 4) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((dst >= 4) || (memReg & 8)) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x86); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void XCHG_dd(GPR32 dst, GPR32 src) + { + if (((dst & 8) != 0) || ((src & 8) != 0)) + { + _emitU8(0x40 | ((src & 8) >> 3) | ((dst & 8) >> 1)); + } + _emitU8(0x87); + _emitU8((3 << 6) | ((dst & 7) << 3) | (src & 7)); + } + void XCHG_qq(GPR64 dst, GPR64 src) + { + _emitU8(0x48 | ((src & 8) >> 3) | ((dst & 8) >> 1)); + _emitU8(0x87); + _emitU8((3 << 6) | ((dst & 7) << 3) | (src & 7)); + } + void XCHG_dd_r(GPR32 dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((dst & 8) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((dst & 8) || (memReg & 8)) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x87); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void XCHG_qq_r(GPR64 dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x87); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void MOV_bb(GPR8_REX dst, GPR8_REX src) + { + if ((src >= 4) || (dst >= 4)) + { + _emitU8(0x40 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + } + _emitU8(0x88); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void MOV_bb_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR8_REX src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((src >= 4) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((src >= 4) || (memReg & 8)) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x88); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void MOV_bb_r(GPR8_REX dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((dst >= 4) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((dst >= 4) || (memReg & 8)) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x8a); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void MOV_dd(GPR32 dst, GPR32 src) + { + if (((src & 8) != 0) || ((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + } + _emitU8(0x89); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void MOV_qq(GPR64 dst, GPR64 src) + { + _emitU8(0x48 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + _emitU8(0x89); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void MOV_dd_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR32 src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((src & 8) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((src & 8) || (memReg & 8)) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x89); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void MOV_qq_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR64 src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x89); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void MOV_dd_r(GPR32 dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((dst & 8) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((dst & 8) || (memReg & 8)) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x8b); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void MOV_qq_r(GPR64 dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x8b); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void MOV_di32(GPR32 dst, s32 imm) + { + if (((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0xb8 | ((dst) & 7)); + _emitU32((u32)imm); + } + void MOV_qi64(GPR64 dst, s64 imm) + { + _emitU8(0x48 | ((dst & 8) >> 3)); + _emitU8(0xb8 | ((dst) & 7)); + _emitU64((u64)imm); + } + void CALL_q(GPR64 dst) + { + if (((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0xff); + _emitU8((3 << 6) | ((2 & 7) << 3) | (dst & 7)); + } + void CALL_q_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0xff); + _emitU8((mod << 6) | ((2 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void IMUL_ddi32(GPR32 dst, GPR32 src, s32 imm) + { + if (((dst & 8) != 0) || ((src & 8) != 0)) + { + _emitU8(0x40 | ((src & 8) >> 3) | ((dst & 8) >> 1)); + } + _emitU8(0x69); + _emitU8((3 << 6) | ((dst & 7) << 3) | (src & 7)); + _emitU32((u32)imm); + } + void IMUL_qqi32(GPR64 dst, GPR64 src, s32 imm) + { + _emitU8(0x48 | ((src & 8) >> 3) | ((dst & 8) >> 1)); + _emitU8(0x69); + _emitU8((3 << 6) | ((dst & 7) << 3) | (src & 7)); + _emitU32((u32)imm); + } + void IMUL_ddi32_r(GPR32 dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s32 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((dst & 8) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((dst & 8) || (memReg & 8)) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x69); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU32((u32)imm); + } + void IMUL_qqi32_r(GPR64 dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s32 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x69); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU32((u32)imm); + } + void IMUL_ddi8(GPR32 dst, GPR32 src, s8 imm) + { + if (((dst & 8) != 0) || ((src & 8) != 0)) + { + _emitU8(0x40 | ((src & 8) >> 3) | ((dst & 8) >> 1)); + } + _emitU8(0x6b); + _emitU8((3 << 6) | ((dst & 7) << 3) | (src & 7)); + _emitU8((u8)imm); + } + void IMUL_qqi8(GPR64 dst, GPR64 src, s8 imm) + { + _emitU8(0x48 | ((src & 8) >> 3) | ((dst & 8) >> 1)); + _emitU8(0x6b); + _emitU8((3 << 6) | ((dst & 7) << 3) | (src & 7)); + _emitU8((u8)imm); + } + void IMUL_ddi8_r(GPR32 dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s8 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((dst & 8) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((dst & 8) || (memReg & 8)) + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x6b); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU8((u8)imm); + } + void IMUL_qqi8_r(GPR64 dst, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, s8 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((dst & 8) >> 1) | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x6b); + _emitU8((mod << 6) | ((dst & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU8((u8)imm); + } + void SHL_b_CL(GPR8_REX dst) + { + if ((dst >= 4)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0xd2); + _emitU8((3 << 6) | ((4 & 7) << 3) | (dst & 7)); + } + void SHL_b_CL_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0xd2); + _emitU8((mod << 6) | ((4 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void SHR_b_CL(GPR8_REX dst) + { + if ((dst >= 4)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0xd2); + _emitU8((3 << 6) | ((5 & 7) << 3) | (dst & 7)); + } + void SHR_b_CL_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0xd2); + _emitU8((mod << 6) | ((5 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void SAR_b_CL(GPR8_REX dst) + { + if ((dst >= 4)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0xd2); + _emitU8((3 << 6) | ((7 & 7) << 3) | (dst & 7)); + } + void SAR_b_CL_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0xd2); + _emitU8((mod << 6) | ((7 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void SHL_d_CL(GPR32 dst) + { + if (((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0xd3); + _emitU8((3 << 6) | ((4 & 7) << 3) | (dst & 7)); + } + void SHL_q_CL(GPR64 dst) + { + _emitU8(0x48 | ((dst & 8) >> 3)); + _emitU8(0xd3); + _emitU8((3 << 6) | ((4 & 7) << 3) | (dst & 7)); + } + void SHL_d_CL_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0xd3); + _emitU8((mod << 6) | ((4 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void SHL_q_CL_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0xd3); + _emitU8((mod << 6) | ((4 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void SHR_d_CL(GPR32 dst) + { + if (((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0xd3); + _emitU8((3 << 6) | ((5 & 7) << 3) | (dst & 7)); + } + void SHR_q_CL(GPR64 dst) + { + _emitU8(0x48 | ((dst & 8) >> 3)); + _emitU8(0xd3); + _emitU8((3 << 6) | ((5 & 7) << 3) | (dst & 7)); + } + void SHR_d_CL_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0xd3); + _emitU8((mod << 6) | ((5 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void SHR_q_CL_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0xd3); + _emitU8((mod << 6) | ((5 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void SAR_d_CL(GPR32 dst) + { + if (((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0xd3); + _emitU8((3 << 6) | ((7 & 7) << 3) | (dst & 7)); + } + void SAR_q_CL(GPR64 dst) + { + _emitU8(0x48 | ((dst & 8) >> 3)); + _emitU8(0xd3); + _emitU8((3 << 6) | ((7 & 7) << 3) | (dst & 7)); + } + void SAR_d_CL_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0xd3); + _emitU8((mod << 6) | ((7 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void SAR_q_CL_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0xd3); + _emitU8((mod << 6) | ((7 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void JMP_j32(s32 imm) + { + _emitU8(0xe9); + _emitU32((u32)imm); + } + void Jcc_j32(X86Cond cond, s32 imm) + { + _emitU8(0x0f); + _emitU8(0x80 | (u8)cond); + _emitU32((u32)imm); + } + void SETcc_b(X86Cond cond, GPR8_REX dst) + { + if ((dst >= 4)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0x0f); + _emitU8(0x90 | (u8)cond); + _emitU8((3 << 6) | (dst & 7)); + } + void SETcc_b_l(X86Cond cond, GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0x0f); + _emitU8(0x90); + _emitU8((mod << 6) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void CMPXCHG_dd(GPR32 dst, GPR32 src) + { + if (((src & 8) != 0) || ((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + } + _emitU8(0x0f); + _emitU8(0xb1); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void CMPXCHG_qq(GPR64 dst, GPR64 src) + { + _emitU8(0x48 | ((dst & 8) >> 3) | ((src & 8) >> 1)); + _emitU8(0x0f); + _emitU8(0xb1); + _emitU8((3 << 6) | ((src & 7) << 3) | (dst & 7)); + } + void CMPXCHG_dd_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR32 src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((src & 8) || (memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((src & 8) || (memReg & 8)) + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1)); + } + _emitU8(0x0f); + _emitU8(0xb1); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void CMPXCHG_qq_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, GPR64 src) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((src & 8) >> 1) | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x0f); + _emitU8(0xb1); + _emitU8((mod << 6) | ((src & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + } + void BSWAP_d(GPR32 dst) + { + if (((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0x0f); + _emitU8(0xc8 | ((dst) & 7)); + } + void BSWAP_q(GPR64 dst) + { + _emitU8(0x48 | ((dst & 8) >> 3)); + _emitU8(0x0f); + _emitU8(0xc8 | ((dst) & 7)); + } + void BT_du8(GPR32 dst, u8 imm) + { + if (((dst & 8) != 0)) + { + _emitU8(0x40 | ((dst & 8) >> 3)); + } + _emitU8(0x0f); + _emitU8(0xba); + _emitU8((3 << 6) | ((4 & 7) << 3) | (dst & 7)); + _emitU8((u8)imm); + } + void BT_qu8(GPR64 dst, u8 imm) + { + _emitU8(0x48 | ((dst & 8) >> 3)); + _emitU8(0x0f); + _emitU8(0xba); + _emitU8((3 << 6) | ((4 & 7) << 3) | (dst & 7)); + _emitU8((u8)imm); + } + void BT_du8_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, u8 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + if ((memReg & 8) || ((index != X86_REG_NONE) && (index & 8))) + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2)); + } + else + { + if ((memReg & 8)) + _emitU8(0x40 | ((memReg & 8) >> 1)); + } + _emitU8(0x0f); + _emitU8(0xba); + _emitU8((mod << 6) | ((4 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU8((u8)imm); + } + void BT_qu8_l(GPR64 memReg, sint32 offset, GPR64 index, uint8 scaler, u8 imm) + { + uint8 mod; + if (offset == 0 && (memReg & 7) != 5) mod = 0; + else if (offset == (s32)(s8)offset) mod = 1; + else mod = 2; + bool sib_use = (scaler != 0 && index != X86_REG_NONE); + if ((memReg & 7) == 4) + { + cemu_assert_debug(index == X86_REG_NONE); + index = memReg; + sib_use = true; + } + if (sib_use) + { + _emitU8(0x40 | ((memReg & 8) >> 3) | ((index & 8) >> 2) | 0x08); + } + else + { + _emitU8(0x40 | ((memReg & 8) >> 1) | 0x08); + } + _emitU8(0x0f); + _emitU8(0xba); + _emitU8((mod << 6) | ((4 & 7) << 3) | (sib_use ? 4 : (memReg & 7))); + if (sib_use) + { + _emitU8((0 << 6) | ((memReg & 7)) | ((index & 7) << 3)); + } + if (mod == 1) _emitU8((u8)offset); + else if (mod == 2) _emitU32((u32)offset); + _emitU8((u8)imm); + } +}; diff --git a/src/Cafe/HW/Espresso/Recompiler/IML/IML.h b/src/Cafe/HW/Espresso/Recompiler/IML/IML.h new file mode 100644 index 00000000..b58fdfa8 --- /dev/null +++ b/src/Cafe/HW/Espresso/Recompiler/IML/IML.h @@ -0,0 +1,16 @@ +#pragma once + +#include "IMLInstruction.h" +#include "IMLSegment.h" + +// analyzer +bool IMLAnalyzer_IsTightFiniteLoop(IMLSegment* imlSegment); + +// optimizer passes +void IMLOptimizer_OptimizeDirectFloatCopies(struct ppcImlGenContext_t* ppcImlGenContext); +void IMLOptimizer_OptimizeDirectIntegerCopies(struct ppcImlGenContext_t* ppcImlGenContext); +void PPCRecompiler_optimizePSQLoadAndStore(struct ppcImlGenContext_t* ppcImlGenContext); + +// debug +void IMLDebug_DumpSegment(struct ppcImlGenContext_t* ctx, IMLSegment* imlSegment, bool printLivenessRangeInfo = false); +void IMLDebug_Dump(struct ppcImlGenContext_t* ppcImlGenContext, bool printLivenessRangeInfo = false); diff --git a/src/Cafe/HW/Espresso/Recompiler/IML/IMLAnalyzer.cpp b/src/Cafe/HW/Espresso/Recompiler/IML/IMLAnalyzer.cpp new file mode 100644 index 00000000..77403e1b --- /dev/null +++ b/src/Cafe/HW/Espresso/Recompiler/IML/IMLAnalyzer.cpp @@ -0,0 +1,55 @@ +#include "IML.h" +//#include "PPCRecompilerIml.h" +#include "util/helpers/fixedSizeList.h" + +#include "Cafe/HW/Espresso/Interpreter/PPCInterpreterInternal.h" + +/* + * Analyzes a single segment and returns true if it is a finite loop + */ +bool IMLAnalyzer_IsTightFiniteLoop(IMLSegment* imlSegment) +{ + return false; // !!! DISABLED !!! + + bool isTightFiniteLoop = false; + // base criteria, must jump to beginning of same segment + if (imlSegment->nextSegmentBranchTaken != imlSegment) + return false; + // loops using BDNZ are assumed to always be finite + for(const IMLInstruction& instIt : imlSegment->imlList) + { + if (instIt.type == PPCREC_IML_TYPE_R_S32 && instIt.operation == PPCREC_IML_OP_SUB) + { + return true; + } + } + // for non-BDNZ loops, check for common patterns + // risky approach, look for ADD/SUB operations and assume that potential overflow means finite (does not include r_r_s32 ADD/SUB) + // this catches most loops with load-update and store-update instructions, but also those with decrementing counters + FixedSizeList list_modifiedRegisters; + for (const IMLInstruction& instIt : imlSegment->imlList) + { + if (instIt.type == PPCREC_IML_TYPE_R_S32 && (instIt.operation == PPCREC_IML_OP_ADD || instIt.operation == PPCREC_IML_OP_SUB) ) + { + list_modifiedRegisters.addUnique(instIt.op_r_immS32.regR); + } + } + if (list_modifiedRegisters.count > 0) + { + // remove all registers from the list that are modified by non-ADD/SUB instructions + // todo: We should also cover the case where ADD+SUB on the same register cancel the effect out + IMLUsedRegisters registersUsed; + for (const IMLInstruction& instIt : imlSegment->imlList) + { + if (instIt.type == PPCREC_IML_TYPE_R_S32 && (instIt.operation == PPCREC_IML_OP_ADD || instIt.operation == PPCREC_IML_OP_SUB)) + continue; + instIt.CheckRegisterUsage(®istersUsed); + registersUsed.ForEachWrittenGPR([&](IMLReg r) { list_modifiedRegisters.remove(r); }); + } + if (list_modifiedRegisters.count > 0) + { + return true; + } + } + return false; +} \ No newline at end of file diff --git a/src/Cafe/HW/Espresso/Recompiler/IML/IMLDebug.cpp b/src/Cafe/HW/Espresso/Recompiler/IML/IMLDebug.cpp new file mode 100644 index 00000000..d295f0aa --- /dev/null +++ b/src/Cafe/HW/Espresso/Recompiler/IML/IMLDebug.cpp @@ -0,0 +1,534 @@ +#include "IML.h" +#include "IMLInstruction.h" +#include "IMLSegment.h" +#include "IMLRegisterAllocatorRanges.h" +#include "util/helpers/StringBuf.h" + +#include "../PPCRecompiler.h" + +const char* IMLDebug_GetOpcodeName(const IMLInstruction* iml) +{ + static char _tempOpcodename[32]; + uint32 op = iml->operation; + if (op == PPCREC_IML_OP_ASSIGN) + return "MOV"; + else if (op == PPCREC_IML_OP_ADD) + return "ADD"; + else if (op == PPCREC_IML_OP_ADD_WITH_CARRY) + return "ADC"; + else if (op == PPCREC_IML_OP_SUB) + return "SUB"; + else if (op == PPCREC_IML_OP_OR) + return "OR"; + else if (op == PPCREC_IML_OP_AND) + return "AND"; + else if (op == PPCREC_IML_OP_XOR) + return "XOR"; + else if (op == PPCREC_IML_OP_LEFT_SHIFT) + return "LSH"; + else if (op == PPCREC_IML_OP_RIGHT_SHIFT_U) + return "RSH"; + else if (op == PPCREC_IML_OP_RIGHT_SHIFT_S) + return "ARSH"; + else if (op == PPCREC_IML_OP_LEFT_ROTATE) + return "LROT"; + else if (op == PPCREC_IML_OP_MULTIPLY_SIGNED) + return "MULS"; + else if (op == PPCREC_IML_OP_DIVIDE_SIGNED) + return "DIVS"; + + sprintf(_tempOpcodename, "OP0%02x_T%d", iml->operation, iml->type); + return _tempOpcodename; +} + +std::string IMLDebug_GetRegName(IMLReg r) +{ + std::string regName; + uint32 regId = r.GetRegID(); + switch (r.GetRegFormat()) + { + case IMLRegFormat::F32: + regName.append("f"); + break; + case IMLRegFormat::F64: + regName.append("fd"); + break; + case IMLRegFormat::I32: + regName.append("i"); + break; + case IMLRegFormat::I64: + regName.append("r"); + break; + default: + DEBUG_BREAK; + } + regName.append(fmt::format("{}", regId)); + return regName; +} + +void IMLDebug_AppendRegisterParam(StringBuf& strOutput, IMLReg virtualRegister, bool isLast = false) +{ + strOutput.add(IMLDebug_GetRegName(virtualRegister)); + if (!isLast) + strOutput.add(", "); +} + +void IMLDebug_AppendS32Param(StringBuf& strOutput, sint32 val, bool isLast = false) +{ + if (isLast) + { + strOutput.addFmt("0x{:08x}", val); + return; + } + strOutput.addFmt("0x{:08x}, ", val); +} + +void IMLDebug_PrintLivenessRangeInfo(StringBuf& currentLineText, IMLSegment* imlSegment, sint32 offset) +{ + // pad to 70 characters + sint32 index = currentLineText.getLen(); + while (index < 70) + { + debug_printf(" "); + index++; + } + raLivenessSubrange_t* subrangeItr = imlSegment->raInfo.linkedList_allSubranges; + while (subrangeItr) + { + if (offset == subrangeItr->start.index) + { + if (false)//subrange->isDirtied && i == subrange->becomesDirtyAtIndex.index) + { + debug_printf("*%-2d", subrangeItr->range->virtualRegister); + } + else + { + debug_printf("|%-2d", subrangeItr->range->virtualRegister); + } + } + else if (false)//subrange->isDirtied && i == subrange->becomesDirtyAtIndex.index ) + { + debug_printf("* "); + } + else if (offset >= subrangeItr->start.index && offset < subrangeItr->end.index) + { + debug_printf("| "); + } + else + { + debug_printf(" "); + } + index += 3; + // next + subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; + } +} + +std::string IMLDebug_GetSegmentName(ppcImlGenContext_t* ctx, IMLSegment* seg) +{ + if (!ctx) + { + return ""; + } + // find segment index + for (size_t i = 0; i < ctx->segmentList2.size(); i++) + { + if (ctx->segmentList2[i] == seg) + { + return fmt::format("Seg{:04x}", i); + } + } + return ""; +} + +std::string IMLDebug_GetConditionName(IMLCondition cond) +{ + switch (cond) + { + case IMLCondition::EQ: + return "EQ"; + case IMLCondition::NEQ: + return "NEQ"; + case IMLCondition::UNSIGNED_GT: + return "UGT"; + case IMLCondition::UNSIGNED_LT: + return "ULT"; + case IMLCondition::SIGNED_GT: + return "SGT"; + case IMLCondition::SIGNED_LT: + return "SLT"; + default: + cemu_assert_unimplemented(); + } + return "ukn"; +} + +void IMLDebug_DumpSegment(ppcImlGenContext_t* ctx, IMLSegment* imlSegment, bool printLivenessRangeInfo) +{ + StringBuf strOutput(1024); + + strOutput.addFmt("SEGMENT {} | PPC=0x{:08x} Loop-depth {}", IMLDebug_GetSegmentName(ctx, imlSegment), imlSegment->ppcAddress, imlSegment->loopDepth); + if (imlSegment->isEnterable) + { + strOutput.addFmt(" ENTERABLE (0x{:08x})", imlSegment->enterPPCAddress); + } + //else if (imlSegment->isJumpDestination) + //{ + // strOutput.addFmt(" JUMP-DEST (0x{:08x})", imlSegment->jumpDestinationPPCAddress); + //} + + debug_printf("%s\n", strOutput.c_str()); + + //strOutput.reset(); + //strOutput.addFmt("SEGMENT NAME 0x{:016x}", (uintptr_t)imlSegment); + //debug_printf("%s", strOutput.c_str()); + + if (printLivenessRangeInfo) + { + strOutput.reset(); + IMLDebug_PrintLivenessRangeInfo(strOutput, imlSegment, RA_INTER_RANGE_START); + debug_printf("%s\n", strOutput.c_str()); + } + //debug_printf("\n"); + strOutput.reset(); + + sint32 lineOffsetParameters = 18; + + for (sint32 i = 0; i < imlSegment->imlList.size(); i++) + { + const IMLInstruction& inst = imlSegment->imlList[i]; + // don't log NOP instructions + if (inst.type == PPCREC_IML_TYPE_NO_OP) + continue; + strOutput.reset(); + strOutput.addFmt("{:02x} ", i); + if (inst.type == PPCREC_IML_TYPE_R_NAME || inst.type == PPCREC_IML_TYPE_NAME_R) + { + if (inst.type == PPCREC_IML_TYPE_R_NAME) + strOutput.add("R_NAME"); + else + strOutput.add("NAME_R"); + while ((sint32)strOutput.getLen() < lineOffsetParameters) + strOutput.add(" "); + + if(inst.type == PPCREC_IML_TYPE_R_NAME) + IMLDebug_AppendRegisterParam(strOutput, inst.op_r_name.regR); + + strOutput.add("name_"); + if (inst.op_r_name.name >= PPCREC_NAME_R0 && inst.op_r_name.name < (PPCREC_NAME_R0 + 999)) + { + strOutput.addFmt("r{}", inst.op_r_name.name - PPCREC_NAME_R0); + } + else if (inst.op_r_name.name >= PPCREC_NAME_FPR0 && inst.op_r_name.name < (PPCREC_NAME_FPR0 + 999)) + { + strOutput.addFmt("f{}", inst.op_r_name.name - PPCREC_NAME_FPR0); + } + else if (inst.op_r_name.name >= PPCREC_NAME_SPR0 && inst.op_r_name.name < (PPCREC_NAME_SPR0 + 999)) + { + strOutput.addFmt("spr{}", inst.op_r_name.name - PPCREC_NAME_SPR0); + } + else if (inst.op_r_name.name >= PPCREC_NAME_CR && inst.op_r_name.name <= PPCREC_NAME_CR_LAST) + strOutput.addFmt("cr{}", inst.op_r_name.name - PPCREC_NAME_CR); + else if (inst.op_r_name.name == PPCREC_NAME_XER_CA) + strOutput.add("xer.ca"); + else if (inst.op_r_name.name == PPCREC_NAME_XER_SO) + strOutput.add("xer.so"); + else if (inst.op_r_name.name == PPCREC_NAME_XER_OV) + strOutput.add("xer.ov"); + else if (inst.op_r_name.name == PPCREC_NAME_CPU_MEMRES_EA) + strOutput.add("cpuReservation.ea"); + else if (inst.op_r_name.name == PPCREC_NAME_CPU_MEMRES_VAL) + strOutput.add("cpuReservation.value"); + else + { + strOutput.addFmt("name_ukn{}", inst.op_r_name.name); + } + if (inst.type != PPCREC_IML_TYPE_R_NAME) + { + strOutput.add(", "); + IMLDebug_AppendRegisterParam(strOutput, inst.op_r_name.regR, true); + } + + } + else if (inst.type == PPCREC_IML_TYPE_R_R) + { + strOutput.addFmt("{}", IMLDebug_GetOpcodeName(&inst)); + while ((sint32)strOutput.getLen() < lineOffsetParameters) + strOutput.add(" "); + IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r.regR); + IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r.regA, true); + } + else if (inst.type == PPCREC_IML_TYPE_R_R_R) + { + strOutput.addFmt("{}", IMLDebug_GetOpcodeName(&inst)); + while ((sint32)strOutput.getLen() < lineOffsetParameters) + strOutput.add(" "); + IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r_r.regR); + IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r_r.regA); + IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r_r.regB, true); + } + else if (inst.type == PPCREC_IML_TYPE_R_R_R_CARRY) + { + strOutput.addFmt("{}", IMLDebug_GetOpcodeName(&inst)); + while ((sint32)strOutput.getLen() < lineOffsetParameters) + strOutput.add(" "); + IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r_r_carry.regR); + IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r_r_carry.regA); + IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r_r_carry.regB); + IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r_r_carry.regCarry, true); + } + else if (inst.type == PPCREC_IML_TYPE_COMPARE) + { + strOutput.add("CMP "); + while ((sint32)strOutput.getLen() < lineOffsetParameters) + strOutput.add(" "); + IMLDebug_AppendRegisterParam(strOutput, inst.op_compare.regA); + IMLDebug_AppendRegisterParam(strOutput, inst.op_compare.regB); + strOutput.addFmt(", {}", IMLDebug_GetConditionName(inst.op_compare.cond)); + strOutput.add(" -> "); + IMLDebug_AppendRegisterParam(strOutput, inst.op_compare.regR, true); + } + else if (inst.type == PPCREC_IML_TYPE_COMPARE_S32) + { + strOutput.add("CMP "); + while ((sint32)strOutput.getLen() < lineOffsetParameters) + strOutput.add(" "); + IMLDebug_AppendRegisterParam(strOutput, inst.op_compare_s32.regA); + strOutput.addFmt("{}", inst.op_compare_s32.immS32); + strOutput.addFmt(", {}", IMLDebug_GetConditionName(inst.op_compare_s32.cond)); + strOutput.add(" -> "); + IMLDebug_AppendRegisterParam(strOutput, inst.op_compare_s32.regR, true); + } + else if (inst.type == PPCREC_IML_TYPE_CONDITIONAL_JUMP) + { + strOutput.add("CJUMP "); + while ((sint32)strOutput.getLen() < lineOffsetParameters) + strOutput.add(" "); + IMLDebug_AppendRegisterParam(strOutput, inst.op_conditional_jump.registerBool, true); + if (!inst.op_conditional_jump.mustBeTrue) + strOutput.add("(inverted)"); + } + else if (inst.type == PPCREC_IML_TYPE_JUMP) + { + strOutput.add("JUMP"); + } + else if (inst.type == PPCREC_IML_TYPE_R_R_S32) + { + strOutput.addFmt("{}", IMLDebug_GetOpcodeName(&inst)); + while ((sint32)strOutput.getLen() < lineOffsetParameters) + strOutput.add(" "); + + IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r_s32.regR); + IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r_s32.regA); + IMLDebug_AppendS32Param(strOutput, inst.op_r_r_s32.immS32, true); + } + else if (inst.type == PPCREC_IML_TYPE_R_R_S32_CARRY) + { + strOutput.addFmt("{}", IMLDebug_GetOpcodeName(&inst)); + while ((sint32)strOutput.getLen() < lineOffsetParameters) + strOutput.add(" "); + + IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r_s32_carry.regR); + IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r_s32_carry.regA); + IMLDebug_AppendS32Param(strOutput, inst.op_r_r_s32_carry.immS32); + IMLDebug_AppendRegisterParam(strOutput, inst.op_r_r_s32_carry.regCarry, true); + } + else if (inst.type == PPCREC_IML_TYPE_R_S32) + { + strOutput.addFmt("{}", IMLDebug_GetOpcodeName(&inst)); + while ((sint32)strOutput.getLen() < lineOffsetParameters) + strOutput.add(" "); + + IMLDebug_AppendRegisterParam(strOutput, inst.op_r_immS32.regR); + IMLDebug_AppendS32Param(strOutput, inst.op_r_immS32.immS32, true); + } + else if (inst.type == PPCREC_IML_TYPE_LOAD || inst.type == PPCREC_IML_TYPE_STORE || + inst.type == PPCREC_IML_TYPE_LOAD_INDEXED || inst.type == PPCREC_IML_TYPE_STORE_INDEXED) + { + if (inst.type == PPCREC_IML_TYPE_LOAD || inst.type == PPCREC_IML_TYPE_LOAD_INDEXED) + strOutput.add("LD_"); + else + strOutput.add("ST_"); + + if (inst.op_storeLoad.flags2.signExtend) + strOutput.add("S"); + else + strOutput.add("U"); + strOutput.addFmt("{}", inst.op_storeLoad.copyWidth); + + while ((sint32)strOutput.getLen() < lineOffsetParameters) + strOutput.add(" "); + + IMLDebug_AppendRegisterParam(strOutput, inst.op_storeLoad.registerData); + + if (inst.type == PPCREC_IML_TYPE_LOAD_INDEXED || inst.type == PPCREC_IML_TYPE_STORE_INDEXED) + strOutput.addFmt("[{}+{}]", IMLDebug_GetRegName(inst.op_storeLoad.registerMem), IMLDebug_GetRegName(inst.op_storeLoad.registerMem2)); + else + strOutput.addFmt("[{}+{}]", IMLDebug_GetRegName(inst.op_storeLoad.registerMem), inst.op_storeLoad.immS32); + } + else if (inst.type == PPCREC_IML_TYPE_ATOMIC_CMP_STORE) + { + strOutput.add("ATOMIC_ST_U32"); + + while ((sint32)strOutput.getLen() < lineOffsetParameters) + strOutput.add(" "); + + IMLDebug_AppendRegisterParam(strOutput, inst.op_atomic_compare_store.regEA); + IMLDebug_AppendRegisterParam(strOutput, inst.op_atomic_compare_store.regCompareValue); + IMLDebug_AppendRegisterParam(strOutput, inst.op_atomic_compare_store.regWriteValue); + IMLDebug_AppendRegisterParam(strOutput, inst.op_atomic_compare_store.regBoolOut, true); + } + else if (inst.type == PPCREC_IML_TYPE_NO_OP) + { + strOutput.add("NOP"); + } + else if (inst.type == PPCREC_IML_TYPE_MACRO) + { + if (inst.operation == PPCREC_IML_MACRO_B_TO_REG) + { + strOutput.addFmt("MACRO B_TO_REG {}", IMLDebug_GetRegName(inst.op_macro.paramReg)); + } + else if (inst.operation == PPCREC_IML_MACRO_BL) + { + strOutput.addFmt("MACRO BL 0x{:08x} -> 0x{:08x} cycles (depr): {}", inst.op_macro.param, inst.op_macro.param2, (sint32)inst.op_macro.paramU16); + } + else if (inst.operation == PPCREC_IML_MACRO_B_FAR) + { + strOutput.addFmt("MACRO B_FAR 0x{:08x} -> 0x{:08x} cycles (depr): {}", inst.op_macro.param, inst.op_macro.param2, (sint32)inst.op_macro.paramU16); + } + else if (inst.operation == PPCREC_IML_MACRO_LEAVE) + { + strOutput.addFmt("MACRO LEAVE ppc: 0x{:08x}", inst.op_macro.param); + } + else if (inst.operation == PPCREC_IML_MACRO_HLE) + { + strOutput.addFmt("MACRO HLE ppcAddr: 0x{:08x} funcId: 0x{:08x}", inst.op_macro.param, inst.op_macro.param2); + } + else if (inst.operation == PPCREC_IML_MACRO_MFTB) + { + strOutput.addFmt("MACRO MFTB ppcAddr: 0x{:08x} sprId: 0x{:08x}", inst.op_macro.param, inst.op_macro.param2); + } + else if (inst.operation == PPCREC_IML_MACRO_COUNT_CYCLES) + { + strOutput.addFmt("MACRO COUNT_CYCLES cycles: {}", inst.op_macro.param); + } + else + { + strOutput.addFmt("MACRO ukn operation {}", inst.operation); + } + } + else if (inst.type == PPCREC_IML_TYPE_FPR_LOAD) + { + strOutput.addFmt("{} = ", IMLDebug_GetRegName(inst.op_storeLoad.registerData)); + if (inst.op_storeLoad.flags2.signExtend) + strOutput.add("S"); + else + strOutput.add("U"); + strOutput.addFmt("{} [{}+{}] mode {}", inst.op_storeLoad.copyWidth / 8, IMLDebug_GetRegName(inst.op_storeLoad.registerMem), inst.op_storeLoad.immS32, inst.op_storeLoad.mode); + if (inst.op_storeLoad.flags2.notExpanded) + { + strOutput.addFmt(" "); + } + } + else if (inst.type == PPCREC_IML_TYPE_FPR_STORE) + { + if (inst.op_storeLoad.flags2.signExtend) + strOutput.add("S"); + else + strOutput.add("U"); + strOutput.addFmt("{} [t{}+{}]", inst.op_storeLoad.copyWidth / 8, inst.op_storeLoad.registerMem.GetRegID(), inst.op_storeLoad.immS32); + strOutput.addFmt(" = {} mode {}", IMLDebug_GetRegName(inst.op_storeLoad.registerData), inst.op_storeLoad.mode); + } + else if (inst.type == PPCREC_IML_TYPE_FPR_R_R) + { + strOutput.addFmt("{:>6} ", IMLDebug_GetOpcodeName(&inst)); + strOutput.addFmt("{}, {}", IMLDebug_GetRegName(inst.op_fpr_r_r.regR), IMLDebug_GetRegName(inst.op_fpr_r_r.regA)); + } + else if (inst.type == PPCREC_IML_TYPE_FPR_R_R_R_R) + { + strOutput.addFmt("{:>6} ", IMLDebug_GetOpcodeName(&inst)); + strOutput.addFmt("{}, {}, {}, {}", IMLDebug_GetRegName(inst.op_fpr_r_r_r_r.regR), IMLDebug_GetRegName(inst.op_fpr_r_r_r_r.regA), IMLDebug_GetRegName(inst.op_fpr_r_r_r_r.regB), IMLDebug_GetRegName(inst.op_fpr_r_r_r_r.regC)); + } + else if (inst.type == PPCREC_IML_TYPE_FPR_R_R_R) + { + strOutput.addFmt("{:>6} ", IMLDebug_GetOpcodeName(&inst)); + strOutput.addFmt("{}, {}, {}", IMLDebug_GetRegName(inst.op_fpr_r_r_r.regR), IMLDebug_GetRegName(inst.op_fpr_r_r_r.regA), IMLDebug_GetRegName(inst.op_fpr_r_r_r.regB)); + } + else if (inst.type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK) + { + strOutput.addFmt("CYCLE_CHECK"); + } + else if (inst.type == PPCREC_IML_TYPE_CONDITIONAL_R_S32) + { + strOutput.addFmt("{} ", IMLDebug_GetRegName(inst.op_conditional_r_s32.regR)); + bool displayAsHex = false; + if (inst.operation == PPCREC_IML_OP_ASSIGN) + { + displayAsHex = true; + strOutput.add("="); + } + else + strOutput.addFmt("(unknown operation CONDITIONAL_R_S32 {})", inst.operation); + if (displayAsHex) + strOutput.addFmt(" 0x{:x}", inst.op_conditional_r_s32.immS32); + else + strOutput.addFmt(" {}", inst.op_conditional_r_s32.immS32); + strOutput.add(" (conditional)"); + } + else + { + strOutput.addFmt("Unknown iml type {}", inst.type); + } + debug_printf("%s", strOutput.c_str()); + if (printLivenessRangeInfo) + { + IMLDebug_PrintLivenessRangeInfo(strOutput, imlSegment, i); + } + debug_printf("\n"); + } + // all ranges + if (printLivenessRangeInfo) + { + debug_printf("Ranges-VirtReg "); + raLivenessSubrange_t* subrangeItr = imlSegment->raInfo.linkedList_allSubranges; + while (subrangeItr) + { + debug_printf("v%-2d", subrangeItr->range->virtualRegister); + subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; + } + debug_printf("\n"); + debug_printf("Ranges-PhysReg "); + subrangeItr = imlSegment->raInfo.linkedList_allSubranges; + while (subrangeItr) + { + debug_printf("p%-2d", subrangeItr->range->physicalRegister); + subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; + } + debug_printf("\n"); + } + // branch info + debug_printf("Links from: "); + for (sint32 i = 0; i < imlSegment->list_prevSegments.size(); i++) + { + if (i) + debug_printf(", "); + debug_printf("%s", IMLDebug_GetSegmentName(ctx, imlSegment->list_prevSegments[i]).c_str()); + } + debug_printf("\n"); + if (imlSegment->nextSegmentBranchNotTaken) + debug_printf("BranchNotTaken: %s\n", IMLDebug_GetSegmentName(ctx, imlSegment->nextSegmentBranchNotTaken).c_str()); + if (imlSegment->nextSegmentBranchTaken) + debug_printf("BranchTaken: %s\n", IMLDebug_GetSegmentName(ctx, imlSegment->nextSegmentBranchTaken).c_str()); + if (imlSegment->nextSegmentIsUncertain) + debug_printf("Dynamic target\n"); + debug_printf("\n"); +} + +void IMLDebug_Dump(ppcImlGenContext_t* ppcImlGenContext, bool printLivenessRangeInfo) +{ + for (size_t i = 0; i < ppcImlGenContext->segmentList2.size(); i++) + { + IMLDebug_DumpSegment(ppcImlGenContext, ppcImlGenContext->segmentList2[i], printLivenessRangeInfo); + debug_printf("\n"); + } +} diff --git a/src/Cafe/HW/Espresso/Recompiler/IML/IMLInstruction.cpp b/src/Cafe/HW/Espresso/Recompiler/IML/IMLInstruction.cpp new file mode 100644 index 00000000..f2476e61 --- /dev/null +++ b/src/Cafe/HW/Espresso/Recompiler/IML/IMLInstruction.cpp @@ -0,0 +1,899 @@ +#include "IMLInstruction.h" +#include "IML.h" + +#include "../PPCRecompiler.h" +#include "../PPCRecompilerIml.h" + +void IMLInstruction::CheckRegisterUsage(IMLUsedRegisters* registersUsed) const +{ + registersUsed->readGPR1 = IMLREG_INVALID; + registersUsed->readGPR2 = IMLREG_INVALID; + registersUsed->readGPR3 = IMLREG_INVALID; + registersUsed->writtenGPR1 = IMLREG_INVALID; + registersUsed->writtenGPR2 = IMLREG_INVALID; + registersUsed->readFPR1 = IMLREG_INVALID; + registersUsed->readFPR2 = IMLREG_INVALID; + registersUsed->readFPR3 = IMLREG_INVALID; + registersUsed->readFPR4 = IMLREG_INVALID; + registersUsed->writtenFPR1 = IMLREG_INVALID; + if (type == PPCREC_IML_TYPE_R_NAME) + { + registersUsed->writtenGPR1 = op_r_name.regR; + } + else if (type == PPCREC_IML_TYPE_NAME_R) + { + registersUsed->readGPR1 = op_r_name.regR; + } + else if (type == PPCREC_IML_TYPE_R_R) + { + if (operation == PPCREC_IML_OP_DCBZ) + { + // both operands are read only + registersUsed->readGPR1 = op_r_r.regR; + registersUsed->readGPR2 = op_r_r.regA; + } + else if ( + operation == PPCREC_IML_OP_ASSIGN || + operation == PPCREC_IML_OP_ENDIAN_SWAP || + operation == PPCREC_IML_OP_CNTLZW || + operation == PPCREC_IML_OP_NOT || + operation == PPCREC_IML_OP_NEG || + operation == PPCREC_IML_OP_ASSIGN_S16_TO_S32 || + operation == PPCREC_IML_OP_ASSIGN_S8_TO_S32) + { + // result is written, operand is read + registersUsed->writtenGPR1 = op_r_r.regR; + registersUsed->readGPR1 = op_r_r.regA; + } + else + cemu_assert_unimplemented(); + } + else if (type == PPCREC_IML_TYPE_R_S32) + { + cemu_assert_debug(operation != PPCREC_IML_OP_ADD && + operation != PPCREC_IML_OP_SUB && + operation != PPCREC_IML_OP_AND && + operation != PPCREC_IML_OP_OR && + operation != PPCREC_IML_OP_XOR); // deprecated, use r_r_s32 for these + + if (operation == PPCREC_IML_OP_LEFT_ROTATE) + { + // operand register is read and write + registersUsed->readGPR1 = op_r_immS32.regR; + registersUsed->writtenGPR1 = op_r_immS32.regR; + } + else + { + // operand register is write only + // todo - use explicit lists, avoid default cases + registersUsed->writtenGPR1 = op_r_immS32.regR; + } + } + else if (type == PPCREC_IML_TYPE_CONDITIONAL_R_S32) + { + if (operation == PPCREC_IML_OP_ASSIGN) + { + // result is written, but also considered read (in case the condition is false the input is preserved) + registersUsed->readGPR1 = op_conditional_r_s32.regR; + registersUsed->writtenGPR1 = op_conditional_r_s32.regR; + } + else + cemu_assert_unimplemented(); + } + else if (type == PPCREC_IML_TYPE_R_R_S32) + { + if (operation == PPCREC_IML_OP_RLWIMI) + { + // result and operand register are both read, result is written + registersUsed->writtenGPR1 = op_r_r_s32.regR; + registersUsed->readGPR1 = op_r_r_s32.regR; + registersUsed->readGPR2 = op_r_r_s32.regA; + } + else + { + // result is write only and operand is read only + registersUsed->writtenGPR1 = op_r_r_s32.regR; + registersUsed->readGPR1 = op_r_r_s32.regA; + } + } + else if (type == PPCREC_IML_TYPE_R_R_S32_CARRY) + { + registersUsed->writtenGPR1 = op_r_r_s32_carry.regR; + registersUsed->readGPR1 = op_r_r_s32_carry.regA; + // some operations read carry + switch (operation) + { + case PPCREC_IML_OP_ADD_WITH_CARRY: + registersUsed->readGPR2 = op_r_r_s32_carry.regCarry; + break; + case PPCREC_IML_OP_ADD: + break; + default: + cemu_assert_unimplemented(); + } + // carry is always written + registersUsed->writtenGPR2 = op_r_r_s32_carry.regCarry; + } + else if (type == PPCREC_IML_TYPE_R_R_R) + { + // in all cases result is written and other operands are read only + registersUsed->writtenGPR1 = op_r_r_r.regR; + registersUsed->readGPR1 = op_r_r_r.regA; + registersUsed->readGPR2 = op_r_r_r.regB; + } + else if (type == PPCREC_IML_TYPE_R_R_R_CARRY) + { + registersUsed->writtenGPR1 = op_r_r_r_carry.regR; + registersUsed->readGPR1 = op_r_r_r_carry.regA; + registersUsed->readGPR2 = op_r_r_r_carry.regB; + // some operations read carry + switch (operation) + { + case PPCREC_IML_OP_ADD_WITH_CARRY: + registersUsed->readGPR3 = op_r_r_r_carry.regCarry; + break; + case PPCREC_IML_OP_ADD: + break; + default: + cemu_assert_unimplemented(); + } + // carry is always written + registersUsed->writtenGPR2 = op_r_r_r_carry.regCarry; + } + else if (type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK) + { + // no effect on registers + } + else if (type == PPCREC_IML_TYPE_NO_OP) + { + // no effect on registers + } + else if (type == PPCREC_IML_TYPE_MACRO) + { + if (operation == PPCREC_IML_MACRO_BL || operation == PPCREC_IML_MACRO_B_FAR || operation == PPCREC_IML_MACRO_LEAVE || operation == PPCREC_IML_MACRO_DEBUGBREAK || operation == PPCREC_IML_MACRO_COUNT_CYCLES || operation == PPCREC_IML_MACRO_HLE || operation == PPCREC_IML_MACRO_MFTB) + { + // no effect on registers + } + else if (operation == PPCREC_IML_MACRO_B_TO_REG) + { + cemu_assert_debug(op_macro.paramReg.IsValid()); + registersUsed->readGPR1 = op_macro.paramReg; + } + else + cemu_assert_unimplemented(); + } + else if (type == PPCREC_IML_TYPE_COMPARE) + { + registersUsed->readGPR1 = op_compare.regA; + registersUsed->readGPR2 = op_compare.regB; + registersUsed->writtenGPR1 = op_compare.regR; + } + else if (type == PPCREC_IML_TYPE_COMPARE_S32) + { + registersUsed->readGPR1 = op_compare_s32.regA; + registersUsed->writtenGPR1 = op_compare_s32.regR; + } + else if (type == PPCREC_IML_TYPE_CONDITIONAL_JUMP) + { + registersUsed->readGPR1 = op_conditional_jump.registerBool; + } + else if (type == PPCREC_IML_TYPE_JUMP) + { + // no registers affected + } + else if (type == PPCREC_IML_TYPE_LOAD) + { + registersUsed->writtenGPR1 = op_storeLoad.registerData; + if (op_storeLoad.registerMem.IsValid()) + registersUsed->readGPR1 = op_storeLoad.registerMem; + } + else if (type == PPCREC_IML_TYPE_LOAD_INDEXED) + { + registersUsed->writtenGPR1 = op_storeLoad.registerData; + if (op_storeLoad.registerMem.IsValid()) + registersUsed->readGPR1 = op_storeLoad.registerMem; + if (op_storeLoad.registerMem2.IsValid()) + registersUsed->readGPR2 = op_storeLoad.registerMem2; + } + else if (type == PPCREC_IML_TYPE_STORE) + { + registersUsed->readGPR1 = op_storeLoad.registerData; + if (op_storeLoad.registerMem.IsValid()) + registersUsed->readGPR2 = op_storeLoad.registerMem; + } + else if (type == PPCREC_IML_TYPE_STORE_INDEXED) + { + registersUsed->readGPR1 = op_storeLoad.registerData; + if (op_storeLoad.registerMem.IsValid()) + registersUsed->readGPR2 = op_storeLoad.registerMem; + if (op_storeLoad.registerMem2.IsValid()) + registersUsed->readGPR3 = op_storeLoad.registerMem2; + } + else if (type == PPCREC_IML_TYPE_ATOMIC_CMP_STORE) + { + registersUsed->readGPR1 = op_atomic_compare_store.regEA; + registersUsed->readGPR2 = op_atomic_compare_store.regCompareValue; + registersUsed->readGPR3 = op_atomic_compare_store.regWriteValue; + registersUsed->writtenGPR1 = op_atomic_compare_store.regBoolOut; + } + else if (type == PPCREC_IML_TYPE_FPR_LOAD) + { + // fpr load operation + registersUsed->writtenFPR1 = op_storeLoad.registerData; + // address is in gpr register + if (op_storeLoad.registerMem.IsValid()) + registersUsed->readGPR1 = op_storeLoad.registerMem; + // determine partially written result + switch (op_storeLoad.mode) + { + case PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0: + case PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0_PS1: + cemu_assert_debug(op_storeLoad.registerGQR.IsValid()); + registersUsed->readGPR2 = op_storeLoad.registerGQR; + break; + case PPCREC_FPR_LD_MODE_DOUBLE_INTO_PS0: + // PS1 remains the same + registersUsed->readFPR4 = op_storeLoad.registerData; + cemu_assert_debug(op_storeLoad.registerGQR.IsInvalid()); + break; + case PPCREC_FPR_LD_MODE_SINGLE_INTO_PS0_PS1: + case PPCREC_FPR_LD_MODE_PSQ_FLOAT_PS0_PS1: + case PPCREC_FPR_LD_MODE_PSQ_FLOAT_PS0: + case PPCREC_FPR_LD_MODE_PSQ_S16_PS0: + case PPCREC_FPR_LD_MODE_PSQ_S16_PS0_PS1: + case PPCREC_FPR_LD_MODE_PSQ_U16_PS0_PS1: + case PPCREC_FPR_LD_MODE_PSQ_U16_PS0: + case PPCREC_FPR_LD_MODE_PSQ_S8_PS0_PS1: + case PPCREC_FPR_LD_MODE_PSQ_U8_PS0_PS1: + case PPCREC_FPR_LD_MODE_PSQ_U8_PS0: + case PPCREC_FPR_LD_MODE_PSQ_S8_PS0: + cemu_assert_debug(op_storeLoad.registerGQR.IsInvalid()); + break; + default: + cemu_assert_unimplemented(); + } + } + else if (type == PPCREC_IML_TYPE_FPR_LOAD_INDEXED) + { + // fpr load operation + registersUsed->writtenFPR1 = op_storeLoad.registerData; + // address is in gpr registers + if (op_storeLoad.registerMem.IsValid()) + registersUsed->readGPR1 = op_storeLoad.registerMem; + if (op_storeLoad.registerMem2.IsValid()) + registersUsed->readGPR2 = op_storeLoad.registerMem2; + // determine partially written result + switch (op_storeLoad.mode) + { + case PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0: + case PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0_PS1: + cemu_assert_debug(op_storeLoad.registerGQR.IsValid()); + registersUsed->readGPR3 = op_storeLoad.registerGQR; + break; + case PPCREC_FPR_LD_MODE_DOUBLE_INTO_PS0: + // PS1 remains the same + cemu_assert_debug(op_storeLoad.registerGQR.IsInvalid()); + registersUsed->readFPR4 = op_storeLoad.registerData; + break; + case PPCREC_FPR_LD_MODE_SINGLE_INTO_PS0_PS1: + case PPCREC_FPR_LD_MODE_PSQ_FLOAT_PS0_PS1: + case PPCREC_FPR_LD_MODE_PSQ_FLOAT_PS0: + case PPCREC_FPR_LD_MODE_PSQ_S16_PS0: + case PPCREC_FPR_LD_MODE_PSQ_S16_PS0_PS1: + case PPCREC_FPR_LD_MODE_PSQ_U16_PS0_PS1: + case PPCREC_FPR_LD_MODE_PSQ_U16_PS0: + case PPCREC_FPR_LD_MODE_PSQ_S8_PS0_PS1: + case PPCREC_FPR_LD_MODE_PSQ_U8_PS0_PS1: + case PPCREC_FPR_LD_MODE_PSQ_U8_PS0: + cemu_assert_debug(op_storeLoad.registerGQR.IsInvalid()); + break; + default: + cemu_assert_unimplemented(); + } + } + else if (type == PPCREC_IML_TYPE_FPR_STORE) + { + // fpr store operation + registersUsed->readFPR1 = op_storeLoad.registerData; + if (op_storeLoad.registerMem.IsValid()) + registersUsed->readGPR1 = op_storeLoad.registerMem; + // PSQ generic stores also access GQR + switch (op_storeLoad.mode) + { + case PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0: + case PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0_PS1: + cemu_assert_debug(op_storeLoad.registerGQR.IsValid()); + registersUsed->readGPR2 = op_storeLoad.registerGQR; + break; + default: + cemu_assert_debug(op_storeLoad.registerGQR.IsInvalid()); + break; + } + } + else if (type == PPCREC_IML_TYPE_FPR_STORE_INDEXED) + { + // fpr store operation + registersUsed->readFPR1 = op_storeLoad.registerData; + // address is in gpr registers + if (op_storeLoad.registerMem.IsValid()) + registersUsed->readGPR1 = op_storeLoad.registerMem; + if (op_storeLoad.registerMem2.IsValid()) + registersUsed->readGPR2 = op_storeLoad.registerMem2; + // PSQ generic stores also access GQR + switch (op_storeLoad.mode) + { + case PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0: + case PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0_PS1: + cemu_assert_debug(op_storeLoad.registerGQR.IsValid()); + registersUsed->readGPR3 = op_storeLoad.registerGQR; + break; + default: + cemu_assert_debug(op_storeLoad.registerGQR.IsInvalid()); + break; + } + } + else if (type == PPCREC_IML_TYPE_FPR_R_R) + { + // fpr operation + if (operation == PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_BOTTOM_AND_TOP || + operation == PPCREC_IML_OP_FPR_COPY_TOP_TO_BOTTOM_AND_TOP || + operation == PPCREC_IML_OP_FPR_COPY_BOTTOM_AND_TOP_SWAPPED || + operation == PPCREC_IML_OP_ASSIGN || + operation == PPCREC_IML_OP_FPR_BOTTOM_FRES_TO_BOTTOM_AND_TOP || + operation == PPCREC_IML_OP_FPR_NEGATE_PAIR || + operation == PPCREC_IML_OP_FPR_ABS_PAIR || + operation == PPCREC_IML_OP_FPR_FRES_PAIR || + operation == PPCREC_IML_OP_FPR_FRSQRTE_PAIR) + { + // operand read, result written + registersUsed->readFPR1 = op_fpr_r_r.regA; + registersUsed->writtenFPR1 = op_fpr_r_r.regR; + } + else if ( + operation == PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_BOTTOM || + operation == PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_TOP || + operation == PPCREC_IML_OP_FPR_COPY_TOP_TO_TOP || + operation == PPCREC_IML_OP_FPR_COPY_TOP_TO_BOTTOM || + operation == PPCREC_IML_OP_FPR_EXPAND_BOTTOM32_TO_BOTTOM64_AND_TOP64 || + operation == PPCREC_IML_OP_FPR_BOTTOM_FCTIWZ || + operation == PPCREC_IML_OP_FPR_BOTTOM_RECIPROCAL_SQRT + ) + { + // operand read, result read and (partially) written + registersUsed->readFPR1 = op_fpr_r_r.regA; + registersUsed->readFPR4 = op_fpr_r_r.regR; + registersUsed->writtenFPR1 = op_fpr_r_r.regR; + } + else if (operation == PPCREC_IML_OP_FPR_MULTIPLY_BOTTOM || + operation == PPCREC_IML_OP_FPR_MULTIPLY_PAIR || + operation == PPCREC_IML_OP_FPR_DIVIDE_BOTTOM || + operation == PPCREC_IML_OP_FPR_DIVIDE_PAIR || + operation == PPCREC_IML_OP_FPR_ADD_BOTTOM || + operation == PPCREC_IML_OP_FPR_ADD_PAIR || + operation == PPCREC_IML_OP_FPR_SUB_PAIR || + operation == PPCREC_IML_OP_FPR_SUB_BOTTOM) + { + // operand read, result read and written + registersUsed->readFPR1 = op_fpr_r_r.regA; + registersUsed->readFPR2 = op_fpr_r_r.regR; + registersUsed->writtenFPR1 = op_fpr_r_r.regR; + + } + else if (operation == PPCREC_IML_OP_FPR_FCMPU_BOTTOM || + operation == PPCREC_IML_OP_FPR_FCMPU_TOP || + operation == PPCREC_IML_OP_FPR_FCMPO_BOTTOM) + { + // operand read, result read + registersUsed->readFPR1 = op_fpr_r_r.regA; + registersUsed->readFPR2 = op_fpr_r_r.regR; + } + else + cemu_assert_unimplemented(); + } + else if (type == PPCREC_IML_TYPE_FPR_R_R_R) + { + // fpr operation + registersUsed->readFPR1 = op_fpr_r_r_r.regA; + registersUsed->readFPR2 = op_fpr_r_r_r.regB; + registersUsed->writtenFPR1 = op_fpr_r_r_r.regR; + // handle partially written result + switch (operation) + { + case PPCREC_IML_OP_FPR_MULTIPLY_BOTTOM: + case PPCREC_IML_OP_FPR_ADD_BOTTOM: + case PPCREC_IML_OP_FPR_SUB_BOTTOM: + registersUsed->readFPR4 = op_fpr_r_r_r.regR; + break; + case PPCREC_IML_OP_FPR_SUB_PAIR: + break; + default: + cemu_assert_unimplemented(); + } + } + else if (type == PPCREC_IML_TYPE_FPR_R_R_R_R) + { + // fpr operation + registersUsed->readFPR1 = op_fpr_r_r_r_r.regA; + registersUsed->readFPR2 = op_fpr_r_r_r_r.regB; + registersUsed->readFPR3 = op_fpr_r_r_r_r.regC; + registersUsed->writtenFPR1 = op_fpr_r_r_r_r.regR; + // handle partially written result + switch (operation) + { + case PPCREC_IML_OP_FPR_SELECT_BOTTOM: + registersUsed->readFPR4 = op_fpr_r_r_r_r.regR; + break; + case PPCREC_IML_OP_FPR_SUM0: + case PPCREC_IML_OP_FPR_SUM1: + case PPCREC_IML_OP_FPR_SELECT_PAIR: + break; + default: + cemu_assert_unimplemented(); + } + } + else if (type == PPCREC_IML_TYPE_FPR_R) + { + // fpr operation + if (operation == PPCREC_IML_OP_FPR_NEGATE_BOTTOM || + operation == PPCREC_IML_OP_FPR_ABS_BOTTOM || + operation == PPCREC_IML_OP_FPR_NEGATIVE_ABS_BOTTOM || + operation == PPCREC_IML_OP_FPR_EXPAND_BOTTOM32_TO_BOTTOM64_AND_TOP64 || + operation == PPCREC_IML_OP_FPR_ROUND_TO_SINGLE_PRECISION_BOTTOM || + operation == PPCREC_IML_OP_FPR_ROUND_TO_SINGLE_PRECISION_PAIR) + { + registersUsed->readFPR1 = op_fpr_r.regR; + registersUsed->writtenFPR1 = op_fpr_r.regR; + } + else + cemu_assert_unimplemented(); + } + else if (type == PPCREC_IML_TYPE_FPR_COMPARE) + { + registersUsed->writtenGPR1 = op_fpr_compare.regR; + registersUsed->readFPR1 = op_fpr_compare.regA; + registersUsed->readFPR2 = op_fpr_compare.regB; + } + else + { + cemu_assert_unimplemented(); + } +} + +//#define replaceRegister(__x,__r,__n) (((__x)==(__r))?(__n):(__x)) +IMLReg replaceRegisterId(IMLReg reg, IMLRegID oldId, IMLRegID newId) +{ + if (reg.GetRegID() != oldId) + return reg; + reg.SetRegID(newId); + return reg; +} + +IMLReg replaceRegisterIdMultiple(IMLReg reg, const std::unordered_map& translationTable) +{ + if (reg.IsInvalid()) + return reg; + const auto& it = translationTable.find(reg.GetRegID()); + cemu_assert_debug(it != translationTable.cend()); + IMLReg alteredReg = reg; + alteredReg.SetRegID(it->second); + return alteredReg; +} + +IMLReg replaceRegisterIdMultiple(IMLReg reg, IMLReg match[4], IMLReg replaced[4]) +{ + // deprecated but still used for FPRs + for (sint32 i = 0; i < 4; i++) + { + if (match[i].IsInvalid()) + continue; + if (reg.GetRegID() == match[i].GetRegID()) + { + cemu_assert_debug(reg.GetBaseFormat() == match[i].GetBaseFormat()); + cemu_assert_debug(reg.GetRegFormat() == match[i].GetRegFormat()); + cemu_assert_debug(reg.GetBaseFormat() == replaced[i].GetBaseFormat()); + cemu_assert_debug(reg.GetRegFormat() == replaced[i].GetRegFormat()); + + return replaced[i]; + } + } + return reg; +} + +void IMLInstruction::RewriteGPR(const std::unordered_map& translationTable) +{ + if (type == PPCREC_IML_TYPE_R_NAME) + { + op_r_name.regR = replaceRegisterIdMultiple(op_r_name.regR, translationTable); + } + else if (type == PPCREC_IML_TYPE_NAME_R) + { + op_r_name.regR = replaceRegisterIdMultiple(op_r_name.regR, translationTable); + } + else if (type == PPCREC_IML_TYPE_R_R) + { + op_r_r.regR = replaceRegisterIdMultiple(op_r_r.regR, translationTable); + op_r_r.regA = replaceRegisterIdMultiple(op_r_r.regA, translationTable); + } + else if (type == PPCREC_IML_TYPE_R_S32) + { + op_r_immS32.regR = replaceRegisterIdMultiple(op_r_immS32.regR, translationTable); + } + else if (type == PPCREC_IML_TYPE_CONDITIONAL_R_S32) + { + op_conditional_r_s32.regR = replaceRegisterIdMultiple(op_conditional_r_s32.regR, translationTable); + } + else if (type == PPCREC_IML_TYPE_R_R_S32) + { + op_r_r_s32.regR = replaceRegisterIdMultiple(op_r_r_s32.regR, translationTable); + op_r_r_s32.regA = replaceRegisterIdMultiple(op_r_r_s32.regA, translationTable); + } + else if (type == PPCREC_IML_TYPE_R_R_S32_CARRY) + { + op_r_r_s32_carry.regR = replaceRegisterIdMultiple(op_r_r_s32_carry.regR, translationTable); + op_r_r_s32_carry.regA = replaceRegisterIdMultiple(op_r_r_s32_carry.regA, translationTable); + op_r_r_s32_carry.regCarry = replaceRegisterIdMultiple(op_r_r_s32_carry.regCarry, translationTable); + } + else if (type == PPCREC_IML_TYPE_R_R_R) + { + op_r_r_r.regR = replaceRegisterIdMultiple(op_r_r_r.regR, translationTable); + op_r_r_r.regA = replaceRegisterIdMultiple(op_r_r_r.regA, translationTable); + op_r_r_r.regB = replaceRegisterIdMultiple(op_r_r_r.regB, translationTable); + } + else if (type == PPCREC_IML_TYPE_R_R_R_CARRY) + { + op_r_r_r_carry.regR = replaceRegisterIdMultiple(op_r_r_r_carry.regR, translationTable); + op_r_r_r_carry.regA = replaceRegisterIdMultiple(op_r_r_r_carry.regA, translationTable); + op_r_r_r_carry.regB = replaceRegisterIdMultiple(op_r_r_r_carry.regB, translationTable); + op_r_r_r_carry.regCarry = replaceRegisterIdMultiple(op_r_r_r_carry.regCarry, translationTable); + } + else if (type == PPCREC_IML_TYPE_COMPARE) + { + op_compare.regR = replaceRegisterIdMultiple(op_compare.regR, translationTable); + op_compare.regA = replaceRegisterIdMultiple(op_compare.regA, translationTable); + op_compare.regB = replaceRegisterIdMultiple(op_compare.regB, translationTable); + } + else if (type == PPCREC_IML_TYPE_COMPARE_S32) + { + op_compare_s32.regR = replaceRegisterIdMultiple(op_compare_s32.regR, translationTable); + op_compare_s32.regA = replaceRegisterIdMultiple(op_compare_s32.regA, translationTable); + } + else if (type == PPCREC_IML_TYPE_CONDITIONAL_JUMP) + { + op_conditional_jump.registerBool = replaceRegisterIdMultiple(op_conditional_jump.registerBool, translationTable); + } + else if (type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK || type == PPCREC_IML_TYPE_JUMP) + { + // no effect on registers + } + else if (type == PPCREC_IML_TYPE_NO_OP) + { + // no effect on registers + } + else if (type == PPCREC_IML_TYPE_MACRO) + { + if (operation == PPCREC_IML_MACRO_BL || operation == PPCREC_IML_MACRO_B_FAR || operation == PPCREC_IML_MACRO_LEAVE || operation == PPCREC_IML_MACRO_DEBUGBREAK || operation == PPCREC_IML_MACRO_HLE || operation == PPCREC_IML_MACRO_MFTB || operation == PPCREC_IML_MACRO_COUNT_CYCLES) + { + // no effect on registers + } + else if (operation == PPCREC_IML_MACRO_B_TO_REG) + { + op_macro.paramReg = replaceRegisterIdMultiple(op_macro.paramReg, translationTable); + } + else + { + cemu_assert_unimplemented(); + } + } + else if (type == PPCREC_IML_TYPE_LOAD) + { + op_storeLoad.registerData = replaceRegisterIdMultiple(op_storeLoad.registerData, translationTable); + if (op_storeLoad.registerMem.IsValid()) + { + op_storeLoad.registerMem = replaceRegisterIdMultiple(op_storeLoad.registerMem, translationTable); + } + } + else if (type == PPCREC_IML_TYPE_LOAD_INDEXED) + { + op_storeLoad.registerData = replaceRegisterIdMultiple(op_storeLoad.registerData, translationTable); + if (op_storeLoad.registerMem.IsValid()) + op_storeLoad.registerMem = replaceRegisterIdMultiple(op_storeLoad.registerMem, translationTable); + if (op_storeLoad.registerMem2.IsValid()) + op_storeLoad.registerMem2 = replaceRegisterIdMultiple(op_storeLoad.registerMem2, translationTable); + } + else if (type == PPCREC_IML_TYPE_STORE) + { + op_storeLoad.registerData = replaceRegisterIdMultiple(op_storeLoad.registerData, translationTable); + if (op_storeLoad.registerMem.IsValid()) + op_storeLoad.registerMem = replaceRegisterIdMultiple(op_storeLoad.registerMem, translationTable); + } + else if (type == PPCREC_IML_TYPE_STORE_INDEXED) + { + op_storeLoad.registerData = replaceRegisterIdMultiple(op_storeLoad.registerData, translationTable); + if (op_storeLoad.registerMem.IsValid()) + op_storeLoad.registerMem = replaceRegisterIdMultiple(op_storeLoad.registerMem, translationTable); + if (op_storeLoad.registerMem2.IsValid()) + op_storeLoad.registerMem2 = replaceRegisterIdMultiple(op_storeLoad.registerMem2, translationTable); + } + else if (type == PPCREC_IML_TYPE_ATOMIC_CMP_STORE) + { + op_atomic_compare_store.regEA = replaceRegisterIdMultiple(op_atomic_compare_store.regEA, translationTable); + op_atomic_compare_store.regCompareValue = replaceRegisterIdMultiple(op_atomic_compare_store.regCompareValue, translationTable); + op_atomic_compare_store.regWriteValue = replaceRegisterIdMultiple(op_atomic_compare_store.regWriteValue, translationTable); + op_atomic_compare_store.regBoolOut = replaceRegisterIdMultiple(op_atomic_compare_store.regBoolOut, translationTable); + } + else if (type == PPCREC_IML_TYPE_FPR_LOAD) + { + op_storeLoad.registerData = replaceRegisterIdMultiple(op_storeLoad.registerData, translationTable); + op_storeLoad.registerMem = replaceRegisterIdMultiple(op_storeLoad.registerMem, translationTable); + op_storeLoad.registerGQR = replaceRegisterIdMultiple(op_storeLoad.registerGQR, translationTable); + } + else if (type == PPCREC_IML_TYPE_FPR_LOAD_INDEXED) + { + op_storeLoad.registerData = replaceRegisterIdMultiple(op_storeLoad.registerData, translationTable); + op_storeLoad.registerMem = replaceRegisterIdMultiple(op_storeLoad.registerMem, translationTable); + op_storeLoad.registerMem2 = replaceRegisterIdMultiple(op_storeLoad.registerMem2, translationTable); + op_storeLoad.registerGQR = replaceRegisterIdMultiple(op_storeLoad.registerGQR, translationTable); + } + else if (type == PPCREC_IML_TYPE_FPR_STORE) + { + op_storeLoad.registerData = replaceRegisterIdMultiple(op_storeLoad.registerData, translationTable); + op_storeLoad.registerMem = replaceRegisterIdMultiple(op_storeLoad.registerMem, translationTable); + op_storeLoad.registerGQR = replaceRegisterIdMultiple(op_storeLoad.registerGQR, translationTable); + } + else if (type == PPCREC_IML_TYPE_FPR_STORE_INDEXED) + { + op_storeLoad.registerData = replaceRegisterIdMultiple(op_storeLoad.registerData, translationTable); + op_storeLoad.registerMem = replaceRegisterIdMultiple(op_storeLoad.registerMem, translationTable); + op_storeLoad.registerMem2 = replaceRegisterIdMultiple(op_storeLoad.registerMem2, translationTable); + op_storeLoad.registerGQR = replaceRegisterIdMultiple(op_storeLoad.registerGQR, translationTable); + } + else if (type == PPCREC_IML_TYPE_FPR_R) + { + op_fpr_r.regR = replaceRegisterIdMultiple(op_fpr_r.regR, translationTable); + } + else if (type == PPCREC_IML_TYPE_FPR_R_R) + { + op_fpr_r_r.regR = replaceRegisterIdMultiple(op_fpr_r_r.regR, translationTable); + op_fpr_r_r.regA = replaceRegisterIdMultiple(op_fpr_r_r.regA, translationTable); + } + else if (type == PPCREC_IML_TYPE_FPR_R_R_R) + { + op_fpr_r_r_r.regR = replaceRegisterIdMultiple(op_fpr_r_r_r.regR, translationTable); + op_fpr_r_r_r.regA = replaceRegisterIdMultiple(op_fpr_r_r_r.regA, translationTable); + op_fpr_r_r_r.regB = replaceRegisterIdMultiple(op_fpr_r_r_r.regB, translationTable); + } + else if (type == PPCREC_IML_TYPE_FPR_R_R_R_R) + { + op_fpr_r_r_r_r.regR = replaceRegisterIdMultiple(op_fpr_r_r_r_r.regR, translationTable); + op_fpr_r_r_r_r.regA = replaceRegisterIdMultiple(op_fpr_r_r_r_r.regA, translationTable); + op_fpr_r_r_r_r.regB = replaceRegisterIdMultiple(op_fpr_r_r_r_r.regB, translationTable); + op_fpr_r_r_r_r.regC = replaceRegisterIdMultiple(op_fpr_r_r_r_r.regC, translationTable); + } + else if (type == PPCREC_IML_TYPE_FPR_COMPARE) + { + op_fpr_compare.regA = replaceRegisterIdMultiple(op_fpr_compare.regA, translationTable); + op_fpr_compare.regB = replaceRegisterIdMultiple(op_fpr_compare.regB, translationTable); + op_fpr_compare.regR = replaceRegisterIdMultiple(op_fpr_compare.regR, translationTable); + } + else + { + cemu_assert_unimplemented(); + } +} + +void IMLInstruction::ReplaceFPRs(IMLReg fprRegisterSearched[4], IMLReg fprRegisterReplaced[4]) +{ + if (type == PPCREC_IML_TYPE_R_NAME) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_NAME_R) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_R_R) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_R_S32) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_R_R_S32) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_R_R_R) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_COMPARE || type == PPCREC_IML_TYPE_COMPARE_S32 || type == PPCREC_IML_TYPE_CONDITIONAL_JUMP || type == PPCREC_IML_TYPE_JUMP) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_NO_OP) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_MACRO) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_LOAD) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_LOAD_INDEXED) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_STORE) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_STORE_INDEXED) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_ATOMIC_CMP_STORE) + { + ; + } + else if (type == PPCREC_IML_TYPE_FPR_LOAD) + { + op_storeLoad.registerData = replaceRegisterIdMultiple(op_storeLoad.registerData, fprRegisterSearched, fprRegisterReplaced); + } + else if (type == PPCREC_IML_TYPE_FPR_LOAD_INDEXED) + { + op_storeLoad.registerData = replaceRegisterIdMultiple(op_storeLoad.registerData, fprRegisterSearched, fprRegisterReplaced); + } + else if (type == PPCREC_IML_TYPE_FPR_STORE) + { + op_storeLoad.registerData = replaceRegisterIdMultiple(op_storeLoad.registerData, fprRegisterSearched, fprRegisterReplaced); + } + else if (type == PPCREC_IML_TYPE_FPR_STORE_INDEXED) + { + op_storeLoad.registerData = replaceRegisterIdMultiple(op_storeLoad.registerData, fprRegisterSearched, fprRegisterReplaced); + } + else if (type == PPCREC_IML_TYPE_FPR_R_R) + { + op_fpr_r_r.regR = replaceRegisterIdMultiple(op_fpr_r_r.regR, fprRegisterSearched, fprRegisterReplaced); + op_fpr_r_r.regA = replaceRegisterIdMultiple(op_fpr_r_r.regA, fprRegisterSearched, fprRegisterReplaced); + } + else if (type == PPCREC_IML_TYPE_FPR_R_R_R) + { + op_fpr_r_r_r.regR = replaceRegisterIdMultiple(op_fpr_r_r_r.regR, fprRegisterSearched, fprRegisterReplaced); + op_fpr_r_r_r.regA = replaceRegisterIdMultiple(op_fpr_r_r_r.regA, fprRegisterSearched, fprRegisterReplaced); + op_fpr_r_r_r.regB = replaceRegisterIdMultiple(op_fpr_r_r_r.regB, fprRegisterSearched, fprRegisterReplaced); + } + else if (type == PPCREC_IML_TYPE_FPR_R_R_R_R) + { + op_fpr_r_r_r_r.regR = replaceRegisterIdMultiple(op_fpr_r_r_r_r.regR, fprRegisterSearched, fprRegisterReplaced); + op_fpr_r_r_r_r.regA = replaceRegisterIdMultiple(op_fpr_r_r_r_r.regA, fprRegisterSearched, fprRegisterReplaced); + op_fpr_r_r_r_r.regB = replaceRegisterIdMultiple(op_fpr_r_r_r_r.regB, fprRegisterSearched, fprRegisterReplaced); + op_fpr_r_r_r_r.regC = replaceRegisterIdMultiple(op_fpr_r_r_r_r.regC, fprRegisterSearched, fprRegisterReplaced); + } + else if (type == PPCREC_IML_TYPE_FPR_R) + { + op_fpr_r.regR = replaceRegisterIdMultiple(op_fpr_r.regR, fprRegisterSearched, fprRegisterReplaced); + } + else if (type == PPCREC_IML_TYPE_FPR_COMPARE) + { + op_fpr_compare.regA = replaceRegisterIdMultiple(op_fpr_compare.regA, fprRegisterSearched, fprRegisterReplaced); + op_fpr_compare.regB = replaceRegisterIdMultiple(op_fpr_compare.regB, fprRegisterSearched, fprRegisterReplaced); + } + else + { + cemu_assert_unimplemented(); + } +} + +void IMLInstruction::ReplaceFPR(IMLRegID fprRegisterSearched, IMLRegID fprRegisterReplaced) +{ + if (type == PPCREC_IML_TYPE_R_NAME) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_NAME_R) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_R_R) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_R_S32) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_R_R_S32 || type == PPCREC_IML_TYPE_R_R_S32_CARRY) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_R_R_R || type == PPCREC_IML_TYPE_R_R_R_CARRY) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_COMPARE || type == PPCREC_IML_TYPE_COMPARE_S32 || type == PPCREC_IML_TYPE_CONDITIONAL_JUMP || type == PPCREC_IML_TYPE_JUMP) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_NO_OP) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_MACRO) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_LOAD) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_LOAD_INDEXED) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_STORE) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_STORE_INDEXED) + { + // not affected + } + else if (type == PPCREC_IML_TYPE_ATOMIC_CMP_STORE) + { + ; + } + else if (type == PPCREC_IML_TYPE_FPR_LOAD) + { + op_storeLoad.registerData = replaceRegisterId(op_storeLoad.registerData, fprRegisterSearched, fprRegisterReplaced); + } + else if (type == PPCREC_IML_TYPE_FPR_LOAD_INDEXED) + { + op_storeLoad.registerData = replaceRegisterId(op_storeLoad.registerData, fprRegisterSearched, fprRegisterReplaced); + } + else if (type == PPCREC_IML_TYPE_FPR_STORE) + { + op_storeLoad.registerData = replaceRegisterId(op_storeLoad.registerData, fprRegisterSearched, fprRegisterReplaced); + } + else if (type == PPCREC_IML_TYPE_FPR_STORE_INDEXED) + { + op_storeLoad.registerData = replaceRegisterId(op_storeLoad.registerData, fprRegisterSearched, fprRegisterReplaced); + } + else if (type == PPCREC_IML_TYPE_FPR_R_R) + { + op_fpr_r_r.regR = replaceRegisterId(op_fpr_r_r.regR, fprRegisterSearched, fprRegisterReplaced); + op_fpr_r_r.regA = replaceRegisterId(op_fpr_r_r.regA, fprRegisterSearched, fprRegisterReplaced); + } + else if (type == PPCREC_IML_TYPE_FPR_R_R_R) + { + op_fpr_r_r_r.regR = replaceRegisterId(op_fpr_r_r_r.regR, fprRegisterSearched, fprRegisterReplaced); + op_fpr_r_r_r.regA = replaceRegisterId(op_fpr_r_r_r.regA, fprRegisterSearched, fprRegisterReplaced); + op_fpr_r_r_r.regB = replaceRegisterId(op_fpr_r_r_r.regB, fprRegisterSearched, fprRegisterReplaced); + } + else if (type == PPCREC_IML_TYPE_FPR_R_R_R_R) + { + op_fpr_r_r_r_r.regR = replaceRegisterId(op_fpr_r_r_r_r.regR, fprRegisterSearched, fprRegisterReplaced); + op_fpr_r_r_r_r.regA = replaceRegisterId(op_fpr_r_r_r_r.regA, fprRegisterSearched, fprRegisterReplaced); + op_fpr_r_r_r_r.regB = replaceRegisterId(op_fpr_r_r_r_r.regB, fprRegisterSearched, fprRegisterReplaced); + op_fpr_r_r_r_r.regC = replaceRegisterId(op_fpr_r_r_r_r.regC, fprRegisterSearched, fprRegisterReplaced); + } + else if (type == PPCREC_IML_TYPE_FPR_R) + { + op_fpr_r.regR = replaceRegisterId(op_fpr_r.regR, fprRegisterSearched, fprRegisterReplaced); + } + else + { + cemu_assert_unimplemented(); + } +} diff --git a/src/Cafe/HW/Espresso/Recompiler/IML/IMLInstruction.h b/src/Cafe/HW/Espresso/Recompiler/IML/IMLInstruction.h new file mode 100644 index 00000000..817fef19 --- /dev/null +++ b/src/Cafe/HW/Espresso/Recompiler/IML/IMLInstruction.h @@ -0,0 +1,769 @@ +#pragma once + +using IMLRegID = uint16; // 16 bit ID + +// format of IMLReg: +// 0-15 (16 bit) IMLRegID +// 19-23 (5 bit) Offset In elements, for SIMD registers +// 24-27 (4 bit) IMLRegFormat RegFormat +// 28-31 (4 bit) IMLRegFormat BaseFormat + +enum class IMLRegFormat : uint8 +{ + INVALID_FORMAT, + I64, + I32, + I16, + I8, + // I1 ? + F64, + F32, + TYPE_COUNT, +}; + +class IMLReg +{ +public: + IMLReg() + { + m_raw = 0; // 0 is invalid + } + + IMLReg(IMLRegFormat baseRegFormat, IMLRegFormat regFormat, uint8 viewOffset, IMLRegID regId) + { + m_raw = 0; + m_raw |= ((uint8)baseRegFormat << 28); + m_raw |= ((uint8)regFormat << 24); + m_raw |= (uint32)regId; + } + + IMLReg(IMLReg&& baseReg, IMLRegFormat viewFormat, uint8 viewOffset, IMLRegID regId) + { + DEBUG_BREAK; + //m_raw = 0; + //m_raw |= ((uint8)baseRegFormat << 28); + //m_raw |= ((uint8)viewFormat << 24); + //m_raw |= (uint32)regId; + } + + IMLReg(const IMLReg& other) : m_raw(other.m_raw) {} + + IMLRegFormat GetBaseFormat() const + { + return (IMLRegFormat)((m_raw >> 28) & 0xF); + } + + IMLRegFormat GetRegFormat() const + { + return (IMLRegFormat)((m_raw >> 24) & 0xF); + } + + IMLRegID GetRegID() const + { + cemu_assert_debug(GetBaseFormat() != IMLRegFormat::INVALID_FORMAT); + cemu_assert_debug(GetRegFormat() != IMLRegFormat::INVALID_FORMAT); + return (IMLRegID)(m_raw & 0xFFFF); + } + + void SetRegID(IMLRegID regId) + { + cemu_assert_debug(regId <= 0xFFFF); + m_raw &= ~0xFFFF; + m_raw |= (uint32)regId; + } + + bool IsInvalid() const + { + return GetBaseFormat() == IMLRegFormat::INVALID_FORMAT; + } + + bool IsValid() const + { + return GetBaseFormat() != IMLRegFormat::INVALID_FORMAT; + } + + bool IsValidAndSameRegID(IMLRegID regId) const + { + return IsValid() && GetRegID() == regId; + } + + // compare all fields + bool operator==(const IMLReg& other) const + { + return m_raw == other.m_raw; + } + +private: + uint32 m_raw; +}; + +static const IMLReg IMLREG_INVALID(IMLRegFormat::INVALID_FORMAT, IMLRegFormat::INVALID_FORMAT, 0, 0); + +using IMLName = uint32; + +enum +{ + PPCREC_IML_OP_ASSIGN, // '=' operator + PPCREC_IML_OP_ENDIAN_SWAP, // '=' operator with 32bit endian swap + PPCREC_IML_OP_MULTIPLY_SIGNED, // '*' operator (signed multiply) + PPCREC_IML_OP_MULTIPLY_HIGH_UNSIGNED, // unsigned 64bit multiply, store only high 32bit-word of result + PPCREC_IML_OP_MULTIPLY_HIGH_SIGNED, // signed 64bit multiply, store only high 32bit-word of result + PPCREC_IML_OP_DIVIDE_SIGNED, // '/' operator (signed divide) + PPCREC_IML_OP_DIVIDE_UNSIGNED, // '/' operator (unsigned divide) + + // binary operation + PPCREC_IML_OP_OR, // '|' operator + PPCREC_IML_OP_AND, // '&' operator + PPCREC_IML_OP_XOR, // '^' operator + PPCREC_IML_OP_LEFT_ROTATE, // left rotate operator + PPCREC_IML_OP_LEFT_SHIFT, // shift left operator + PPCREC_IML_OP_RIGHT_SHIFT_U, // right shift operator (unsigned) + PPCREC_IML_OP_RIGHT_SHIFT_S, // right shift operator (signed) + // ppc + PPCREC_IML_OP_RLWIMI, // RLWIMI instruction (rotate, merge based on mask) + PPCREC_IML_OP_SLW, // SLW (shift based on register by up to 63 bits) + PPCREC_IML_OP_SRW, // SRW (shift based on register by up to 63 bits) + PPCREC_IML_OP_CNTLZW, + PPCREC_IML_OP_DCBZ, // clear 32 bytes aligned to 0x20 + // FPU + PPCREC_IML_OP_FPR_ADD_BOTTOM, + PPCREC_IML_OP_FPR_ADD_PAIR, + PPCREC_IML_OP_FPR_SUB_PAIR, + PPCREC_IML_OP_FPR_SUB_BOTTOM, + PPCREC_IML_OP_FPR_MULTIPLY_BOTTOM, + PPCREC_IML_OP_FPR_MULTIPLY_PAIR, + PPCREC_IML_OP_FPR_DIVIDE_BOTTOM, + PPCREC_IML_OP_FPR_DIVIDE_PAIR, + PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_BOTTOM_AND_TOP, + PPCREC_IML_OP_FPR_COPY_TOP_TO_BOTTOM_AND_TOP, + PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_BOTTOM, + PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_TOP, // leave bottom of destination untouched + PPCREC_IML_OP_FPR_COPY_TOP_TO_TOP, // leave bottom of destination untouched + PPCREC_IML_OP_FPR_COPY_TOP_TO_BOTTOM, // leave top of destination untouched + PPCREC_IML_OP_FPR_COPY_BOTTOM_AND_TOP_SWAPPED, + PPCREC_IML_OP_FPR_EXPAND_BOTTOM32_TO_BOTTOM64_AND_TOP64, // expand bottom f32 to f64 in bottom and top half + PPCREC_IML_OP_FPR_BOTTOM_FRES_TO_BOTTOM_AND_TOP, // calculate reciprocal with Espresso accuracy of source bottom half and write result to destination bottom and top half + PPCREC_IML_OP_FPR_FCMPO_BOTTOM, // deprecated + PPCREC_IML_OP_FPR_FCMPU_BOTTOM, // deprecated + PPCREC_IML_OP_FPR_FCMPU_TOP, // deprecated + PPCREC_IML_OP_FPR_NEGATE_BOTTOM, + PPCREC_IML_OP_FPR_NEGATE_PAIR, + PPCREC_IML_OP_FPR_ABS_BOTTOM, // abs(fp0) + PPCREC_IML_OP_FPR_ABS_PAIR, + PPCREC_IML_OP_FPR_FRES_PAIR, // 1.0/fp approx (Espresso accuracy) + PPCREC_IML_OP_FPR_FRSQRTE_PAIR, // 1.0/sqrt(fp) approx (Espresso accuracy) + PPCREC_IML_OP_FPR_NEGATIVE_ABS_BOTTOM, // -abs(fp0) + PPCREC_IML_OP_FPR_ROUND_TO_SINGLE_PRECISION_BOTTOM, // round 64bit double to 64bit double with 32bit float precision (in bottom half of xmm register) + PPCREC_IML_OP_FPR_ROUND_TO_SINGLE_PRECISION_PAIR, // round two 64bit doubles to 64bit double with 32bit float precision + PPCREC_IML_OP_FPR_BOTTOM_RECIPROCAL_SQRT, + PPCREC_IML_OP_FPR_BOTTOM_FCTIWZ, + PPCREC_IML_OP_FPR_SELECT_BOTTOM, // selectively copy bottom value from operand B or C based on value in operand A + PPCREC_IML_OP_FPR_SELECT_PAIR, // selectively copy top/bottom from operand B or C based on value in top/bottom of operand A + // PS + PPCREC_IML_OP_FPR_SUM0, + PPCREC_IML_OP_FPR_SUM1, + + + // R_R_R only + + // R_R_S32 only + + // R_R_R + R_R_S32 + PPCREC_IML_OP_ADD, // also R_R_R_CARRY + PPCREC_IML_OP_SUB, + + // R_R only + PPCREC_IML_OP_NOT, + PPCREC_IML_OP_NEG, + PPCREC_IML_OP_ASSIGN_S16_TO_S32, + PPCREC_IML_OP_ASSIGN_S8_TO_S32, + + // R_R_R_carry + PPCREC_IML_OP_ADD_WITH_CARRY, // similar to ADD but also adds carry bit (0 or 1) +}; + +#define PPCREC_IML_OP_FPR_COPY_PAIR (PPCREC_IML_OP_ASSIGN) + +enum +{ + PPCREC_IML_MACRO_B_TO_REG, // branch to PPC address in register (used for BCCTR, BCLR) + + PPCREC_IML_MACRO_BL, // call to different function (can be within same function) + PPCREC_IML_MACRO_B_FAR, // branch to different function + PPCREC_IML_MACRO_COUNT_CYCLES, // decrease current remaining thread cycles by a certain amount + PPCREC_IML_MACRO_HLE, // HLE function call + PPCREC_IML_MACRO_MFTB, // get TB register value (low or high) + PPCREC_IML_MACRO_LEAVE, // leaves recompiler and switches to interpeter + // debugging + PPCREC_IML_MACRO_DEBUGBREAK, // throws a debugbreak +}; + +enum class IMLCondition : uint8 +{ + EQ, + NEQ, + SIGNED_GT, + SIGNED_LT, + UNSIGNED_GT, + UNSIGNED_LT, + + // floating point conditions + UNORDERED_GT, // a > b, false if either is NaN + UNORDERED_LT, // a < b, false if either is NaN + UNORDERED_EQ, // a == b, false if either is NaN + UNORDERED_U, // unordered (true if either operand is NaN) + + ORDERED_GT, + ORDERED_LT, + ORDERED_EQ, + ORDERED_U +}; + +enum +{ + PPCREC_IML_TYPE_NONE, + PPCREC_IML_TYPE_NO_OP, // no-op instruction + PPCREC_IML_TYPE_R_R, // r* = (op) *r (can also be r* (op) *r) + PPCREC_IML_TYPE_R_R_R, // r* = r* (op) r* + PPCREC_IML_TYPE_R_R_R_CARRY, // r* = r* (op) r* (reads and/or updates carry) + PPCREC_IML_TYPE_R_R_S32, // r* = r* (op) s32* + PPCREC_IML_TYPE_R_R_S32_CARRY, // r* = r* (op) s32* (reads and/or updates carry) + PPCREC_IML_TYPE_LOAD, // r* = [r*+s32*] + PPCREC_IML_TYPE_LOAD_INDEXED, // r* = [r*+r*] + PPCREC_IML_TYPE_STORE, // [r*+s32*] = r* + PPCREC_IML_TYPE_STORE_INDEXED, // [r*+r*] = r* + PPCREC_IML_TYPE_R_NAME, // r* = name + PPCREC_IML_TYPE_NAME_R, // name* = r* + PPCREC_IML_TYPE_R_S32, // r* (op) imm + PPCREC_IML_TYPE_MACRO, + PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK, // jumps only if remaining thread cycles < 0 + + // conditions and branches + PPCREC_IML_TYPE_COMPARE, // r* = r* CMP[cond] r* + PPCREC_IML_TYPE_COMPARE_S32, // r* = r* CMP[cond] imm + PPCREC_IML_TYPE_JUMP, // jump always + PPCREC_IML_TYPE_CONDITIONAL_JUMP, // jump conditionally based on boolean value in register + + // atomic + PPCREC_IML_TYPE_ATOMIC_CMP_STORE, + + // conditional (legacy) + PPCREC_IML_TYPE_CONDITIONAL_R_S32, + + // FPR + PPCREC_IML_TYPE_FPR_LOAD, // r* = (bitdepth) [r*+s32*] (single or paired single mode) + PPCREC_IML_TYPE_FPR_LOAD_INDEXED, // r* = (bitdepth) [r*+r*] (single or paired single mode) + PPCREC_IML_TYPE_FPR_STORE, // (bitdepth) [r*+s32*] = r* (single or paired single mode) + PPCREC_IML_TYPE_FPR_STORE_INDEXED, // (bitdepth) [r*+r*] = r* (single or paired single mode) + PPCREC_IML_TYPE_FPR_R_R, + PPCREC_IML_TYPE_FPR_R_R_R, + PPCREC_IML_TYPE_FPR_R_R_R_R, + PPCREC_IML_TYPE_FPR_R, + + PPCREC_IML_TYPE_FPR_COMPARE, // r* = r* CMP[cond] r* +}; + +enum // IMLName +{ + PPCREC_NAME_NONE, + PPCREC_NAME_TEMPORARY = 1000, + PPCREC_NAME_R0 = 2000, + PPCREC_NAME_SPR0 = 3000, + PPCREC_NAME_FPR0 = 4000, + PPCREC_NAME_TEMPORARY_FPR0 = 5000, // 0 to 7 + PPCREC_NAME_XER_CA = 6000, // carry bit from XER + PPCREC_NAME_XER_OV = 6001, // overflow bit from XER + PPCREC_NAME_XER_SO = 6002, // summary overflow bit from XER + PPCREC_NAME_CR = 7000, // CR register bits (31 to 0) + PPCREC_NAME_CR_LAST = PPCREC_NAME_CR+31, + PPCREC_NAME_CPU_MEMRES_EA = 8000, + PPCREC_NAME_CPU_MEMRES_VAL = 8001 +}; + +#define PPC_REC_INVALID_REGISTER 0xFF // deprecated. Use IMLREG_INVALID instead + +enum +{ + // fpr load + PPCREC_FPR_LD_MODE_SINGLE_INTO_PS0, + PPCREC_FPR_LD_MODE_SINGLE_INTO_PS0_PS1, + PPCREC_FPR_LD_MODE_DOUBLE_INTO_PS0, + PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0, + PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0_PS1, + PPCREC_FPR_LD_MODE_PSQ_FLOAT_PS0, + PPCREC_FPR_LD_MODE_PSQ_FLOAT_PS0_PS1, + PPCREC_FPR_LD_MODE_PSQ_S16_PS0, + PPCREC_FPR_LD_MODE_PSQ_S16_PS0_PS1, + PPCREC_FPR_LD_MODE_PSQ_U16_PS0, + PPCREC_FPR_LD_MODE_PSQ_U16_PS0_PS1, + PPCREC_FPR_LD_MODE_PSQ_S8_PS0, + PPCREC_FPR_LD_MODE_PSQ_S8_PS0_PS1, + PPCREC_FPR_LD_MODE_PSQ_U8_PS0, + PPCREC_FPR_LD_MODE_PSQ_U8_PS0_PS1, + // fpr store + PPCREC_FPR_ST_MODE_SINGLE_FROM_PS0, // store 1 single precision float from ps0 + PPCREC_FPR_ST_MODE_DOUBLE_FROM_PS0, // store 1 double precision float from ps0 + + PPCREC_FPR_ST_MODE_UI32_FROM_PS0, // store raw low-32bit of PS0 + + PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0_PS1, + PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0, + PPCREC_FPR_ST_MODE_PSQ_FLOAT_PS0_PS1, + PPCREC_FPR_ST_MODE_PSQ_FLOAT_PS0, + PPCREC_FPR_ST_MODE_PSQ_S8_PS0, + PPCREC_FPR_ST_MODE_PSQ_S8_PS0_PS1, + PPCREC_FPR_ST_MODE_PSQ_U8_PS0, + PPCREC_FPR_ST_MODE_PSQ_U8_PS0_PS1, + PPCREC_FPR_ST_MODE_PSQ_U16_PS0, + PPCREC_FPR_ST_MODE_PSQ_U16_PS0_PS1, + PPCREC_FPR_ST_MODE_PSQ_S16_PS0, + PPCREC_FPR_ST_MODE_PSQ_S16_PS0_PS1, +}; + +struct IMLUsedRegisters +{ + IMLUsedRegisters() {}; + + // GPR + union + { + struct + { + IMLReg readGPR1; + IMLReg readGPR2; + IMLReg readGPR3; + IMLReg writtenGPR1; + IMLReg writtenGPR2; + }; + }; + // FPR + union + { + struct + { + // note: If destination operand is not fully written (PS0 and PS1) it will be added to the read registers + IMLReg readFPR1; + IMLReg readFPR2; + IMLReg readFPR3; + IMLReg readFPR4; + IMLReg writtenFPR1; + }; + }; + + bool IsBaseGPRWritten(IMLReg imlReg) const + { + cemu_assert_debug(imlReg.IsValid()); + auto regId = imlReg.GetRegID(); + if (writtenGPR1.IsValid() && writtenGPR1.GetRegID() == regId) + return true; + if (writtenGPR2.IsValid() && writtenGPR2.GetRegID() == regId) + return true; + return false; + } + + template + void ForEachWrittenGPR(Fn F) const + { + if (writtenGPR1.IsValid()) + F(writtenGPR1); + if (writtenGPR2.IsValid()) + F(writtenGPR2); + } + + template + void ForEachReadGPR(Fn F) const + { + if (readGPR1.IsValid()) + F(readGPR1); + if (readGPR2.IsValid()) + F(readGPR2); + if (readGPR3.IsValid()) + F(readGPR3); + } + + template + void ForEachAccessedGPR(Fn F) const + { + // GPRs + if (readGPR1.IsValid()) + F(readGPR1, false); + if (readGPR2.IsValid()) + F(readGPR2, false); + if (readGPR3.IsValid()) + F(readGPR3, false); + if (writtenGPR1.IsValid()) + F(writtenGPR1, true); + if (writtenGPR2.IsValid()) + F(writtenGPR2, true); + // FPRs + if (readFPR1.IsValid()) + F(readFPR1, false); + if (readFPR2.IsValid()) + F(readFPR2, false); + if (readFPR3.IsValid()) + F(readFPR3, false); + if (readFPR4.IsValid()) + F(readFPR4, false); + if (writtenFPR1.IsValid()) + F(writtenFPR1, true); + } + +}; + +struct IMLInstruction +{ + IMLInstruction() {} + IMLInstruction(const IMLInstruction& other) + { + memcpy(this, &other, sizeof(IMLInstruction)); + } + + uint8 type; + uint8 operation; + union + { + struct + { + uint8 _padding[7]; + }padding; + struct + { + IMLReg regR; + IMLReg regA; + }op_r_r; + struct + { + IMLReg regR; + IMLReg regA; + IMLReg regB; + }op_r_r_r; + struct + { + IMLReg regR; + IMLReg regA; + IMLReg regB; + IMLReg regCarry; + }op_r_r_r_carry; + struct + { + IMLReg regR; + IMLReg regA; + sint32 immS32; + }op_r_r_s32; + struct + { + IMLReg regR; + IMLReg regA; + IMLReg regCarry; + sint32 immS32; + }op_r_r_s32_carry; + struct + { + IMLReg regR; + IMLName name; + }op_r_name; // alias op_name_r + struct + { + IMLReg regR; + sint32 immS32; + }op_r_immS32; + struct + { + uint32 param; + uint32 param2; + uint16 paramU16; + IMLReg paramReg; + }op_macro; + struct + { + IMLReg registerData; + IMLReg registerMem; + IMLReg registerMem2; + IMLReg registerGQR; + uint8 copyWidth; + struct + { + bool swapEndian : 1; + bool signExtend : 1; + bool notExpanded : 1; // for floats + }flags2; + uint8 mode; // transfer mode (copy width, ps0/ps1 behavior) + sint32 immS32; + }op_storeLoad; + struct + { + IMLReg regR; + IMLReg regA; + }op_fpr_r_r; + struct + { + IMLReg regR; + IMLReg regA; + IMLReg regB; + }op_fpr_r_r_r; + struct + { + IMLReg regR; + IMLReg regA; + IMLReg regB; + IMLReg regC; + }op_fpr_r_r_r_r; + struct + { + IMLReg regR; + }op_fpr_r; + struct + { + IMLReg regR; // stores the boolean result of the comparison + IMLReg regA; + IMLReg regB; + IMLCondition cond; + }op_fpr_compare; + struct + { + IMLReg regR; // stores the boolean result of the comparison + IMLReg regA; + IMLReg regB; + IMLCondition cond; + }op_compare; + struct + { + IMLReg regR; // stores the boolean result of the comparison + IMLReg regA; + sint32 immS32; + IMLCondition cond; + }op_compare_s32; + struct + { + IMLReg registerBool; + bool mustBeTrue; + }op_conditional_jump; + struct + { + IMLReg regEA; + IMLReg regCompareValue; + IMLReg regWriteValue; + IMLReg regBoolOut; + }op_atomic_compare_store; + // conditional operations (emitted if supported by target platform) + struct + { + // r_s32 + IMLReg regR; + sint32 immS32; + // condition + uint8 crRegisterIndex; + uint8 crBitIndex; + bool bitMustBeSet; + }op_conditional_r_s32; + }; + + bool IsSuffixInstruction() const + { + if (type == PPCREC_IML_TYPE_MACRO && operation == PPCREC_IML_MACRO_BL || + type == PPCREC_IML_TYPE_MACRO && operation == PPCREC_IML_MACRO_B_FAR || + type == PPCREC_IML_TYPE_MACRO && operation == PPCREC_IML_MACRO_B_TO_REG || + type == PPCREC_IML_TYPE_MACRO && operation == PPCREC_IML_MACRO_LEAVE || + type == PPCREC_IML_TYPE_MACRO && operation == PPCREC_IML_MACRO_HLE || + type == PPCREC_IML_TYPE_MACRO && operation == PPCREC_IML_MACRO_MFTB || + type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK || + type == PPCREC_IML_TYPE_JUMP || + type == PPCREC_IML_TYPE_CONDITIONAL_JUMP) + return true; + return false; + } + + // instruction setters + void make_no_op() + { + type = PPCREC_IML_TYPE_NO_OP; + operation = 0; + } + + void make_r_name(IMLReg regR, IMLName name) + { + cemu_assert_debug(regR.GetBaseFormat() == regR.GetRegFormat()); // for name load/store instructions the register must match the base format + type = PPCREC_IML_TYPE_R_NAME; + operation = PPCREC_IML_OP_ASSIGN; + op_r_name.regR = regR; + op_r_name.name = name; + } + + void make_name_r(IMLName name, IMLReg regR) + { + cemu_assert_debug(regR.GetBaseFormat() == regR.GetRegFormat()); // for name load/store instructions the register must match the base format + type = PPCREC_IML_TYPE_NAME_R; + operation = PPCREC_IML_OP_ASSIGN; + op_r_name.regR = regR; + op_r_name.name = name; + } + + void make_debugbreak(uint32 currentPPCAddress = 0) + { + make_macro(PPCREC_IML_MACRO_DEBUGBREAK, 0, currentPPCAddress, 0, IMLREG_INVALID); + } + + void make_macro(uint32 macroId, uint32 param, uint32 param2, uint16 paramU16, IMLReg regParam) + { + this->type = PPCREC_IML_TYPE_MACRO; + this->operation = macroId; + this->op_macro.param = param; + this->op_macro.param2 = param2; + this->op_macro.paramU16 = paramU16; + this->op_macro.paramReg = regParam; + } + + void make_cjump_cycle_check() + { + this->type = PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK; + this->operation = 0; + } + + void make_r_r(uint32 operation, IMLReg regR, IMLReg regA) + { + this->type = PPCREC_IML_TYPE_R_R; + this->operation = operation; + this->op_r_r.regR = regR; + this->op_r_r.regA = regA; + } + + void make_r_s32(uint32 operation, IMLReg regR, sint32 immS32) + { + this->type = PPCREC_IML_TYPE_R_S32; + this->operation = operation; + this->op_r_immS32.regR = regR; + this->op_r_immS32.immS32 = immS32; + } + + void make_r_r_r(uint32 operation, IMLReg regR, IMLReg regA, IMLReg regB) + { + this->type = PPCREC_IML_TYPE_R_R_R; + this->operation = operation; + this->op_r_r_r.regR = regR; + this->op_r_r_r.regA = regA; + this->op_r_r_r.regB = regB; + } + + void make_r_r_r_carry(uint32 operation, IMLReg regR, IMLReg regA, IMLReg regB, IMLReg regCarry) + { + this->type = PPCREC_IML_TYPE_R_R_R_CARRY; + this->operation = operation; + this->op_r_r_r_carry.regR = regR; + this->op_r_r_r_carry.regA = regA; + this->op_r_r_r_carry.regB = regB; + this->op_r_r_r_carry.regCarry = regCarry; + } + + void make_r_r_s32(uint32 operation, IMLReg regR, IMLReg regA, sint32 immS32) + { + this->type = PPCREC_IML_TYPE_R_R_S32; + this->operation = operation; + this->op_r_r_s32.regR = regR; + this->op_r_r_s32.regA = regA; + this->op_r_r_s32.immS32 = immS32; + } + + void make_r_r_s32_carry(uint32 operation, IMLReg regR, IMLReg regA, sint32 immS32, IMLReg regCarry) + { + this->type = PPCREC_IML_TYPE_R_R_S32_CARRY; + this->operation = operation; + this->op_r_r_s32_carry.regR = regR; + this->op_r_r_s32_carry.regA = regA; + this->op_r_r_s32_carry.immS32 = immS32; + this->op_r_r_s32_carry.regCarry = regCarry; + } + + void make_compare(IMLReg regA, IMLReg regB, IMLReg regR, IMLCondition cond) + { + this->type = PPCREC_IML_TYPE_COMPARE; + this->operation = -999; + this->op_compare.regR = regR; + this->op_compare.regA = regA; + this->op_compare.regB = regB; + this->op_compare.cond = cond; + } + + void make_compare_s32(IMLReg regA, sint32 immS32, IMLReg regR, IMLCondition cond) + { + this->type = PPCREC_IML_TYPE_COMPARE_S32; + this->operation = -999; + this->op_compare_s32.regR = regR; + this->op_compare_s32.regA = regA; + this->op_compare_s32.immS32 = immS32; + this->op_compare_s32.cond = cond; + } + + void make_conditional_jump(IMLReg regBool, bool mustBeTrue) + { + this->type = PPCREC_IML_TYPE_CONDITIONAL_JUMP; + this->operation = -999; + this->op_conditional_jump.registerBool = regBool; + this->op_conditional_jump.mustBeTrue = mustBeTrue; + } + + void make_jump() + { + this->type = PPCREC_IML_TYPE_JUMP; + this->operation = -999; + } + + // load from memory + void make_r_memory(IMLReg regD, IMLReg regMem, sint32 immS32, uint32 copyWidth, bool signExtend, bool switchEndian) + { + this->type = PPCREC_IML_TYPE_LOAD; + this->operation = 0; + this->op_storeLoad.registerData = regD; + this->op_storeLoad.registerMem = regMem; + this->op_storeLoad.immS32 = immS32; + this->op_storeLoad.copyWidth = copyWidth; + this->op_storeLoad.flags2.swapEndian = switchEndian; + this->op_storeLoad.flags2.signExtend = signExtend; + } + + // store to memory + void make_memory_r(IMLReg regS, IMLReg regMem, sint32 immS32, uint32 copyWidth, bool switchEndian) + { + this->type = PPCREC_IML_TYPE_STORE; + this->operation = 0; + this->op_storeLoad.registerData = regS; + this->op_storeLoad.registerMem = regMem; + this->op_storeLoad.immS32 = immS32; + this->op_storeLoad.copyWidth = copyWidth; + this->op_storeLoad.flags2.swapEndian = switchEndian; + this->op_storeLoad.flags2.signExtend = false; + } + + void make_atomic_cmp_store(IMLReg regEA, IMLReg regCompareValue, IMLReg regWriteValue, IMLReg regSuccessOutput) + { + this->type = PPCREC_IML_TYPE_ATOMIC_CMP_STORE; + this->operation = 0; + this->op_atomic_compare_store.regEA = regEA; + this->op_atomic_compare_store.regCompareValue = regCompareValue; + this->op_atomic_compare_store.regWriteValue = regWriteValue; + this->op_atomic_compare_store.regBoolOut = regSuccessOutput; + } + + void make_fpr_compare(IMLReg regA, IMLReg regB, IMLReg regR, IMLCondition cond) + { + this->type = PPCREC_IML_TYPE_FPR_COMPARE; + this->operation = -999; + this->op_fpr_compare.regR = regR; + this->op_fpr_compare.regA = regA; + this->op_fpr_compare.regB = regB; + this->op_fpr_compare.cond = cond; + } + + void CheckRegisterUsage(IMLUsedRegisters* registersUsed) const; + + void RewriteGPR(const std::unordered_map& translationTable); + void ReplaceFPRs(IMLReg fprRegisterSearched[4], IMLReg fprRegisterReplaced[4]); + void ReplaceFPR(IMLRegID fprRegisterSearched, IMLRegID fprRegisterReplaced); + +}; + +// architecture specific constants +namespace IMLArchX86 +{ + static constexpr int PHYSREG_GPR_BASE = 0; + static constexpr int PHYSREG_FPR_BASE = 16; +}; \ No newline at end of file diff --git a/src/Cafe/HW/Espresso/Recompiler/IML/IMLOptimizer.cpp b/src/Cafe/HW/Espresso/Recompiler/IML/IMLOptimizer.cpp new file mode 100644 index 00000000..cdf922ce --- /dev/null +++ b/src/Cafe/HW/Espresso/Recompiler/IML/IMLOptimizer.cpp @@ -0,0 +1,330 @@ +#include "Cafe/HW/Espresso/Interpreter/PPCInterpreterInternal.h" +#include "Cafe/HW/Espresso/Recompiler/IML/IML.h" +#include "Cafe/HW/Espresso/Recompiler/IML/IMLInstruction.h" + +#include "../PPCRecompiler.h" +#include "../PPCRecompilerIml.h" +#include "../BackendX64/BackendX64.h" + +IMLReg _FPRRegFromID(IMLRegID regId) +{ + return IMLReg(IMLRegFormat::F64, IMLRegFormat::F64, 0, regId); +} + +void PPCRecompiler_optimizeDirectFloatCopiesScanForward(ppcImlGenContext_t* ppcImlGenContext, IMLSegment* imlSegment, sint32 imlIndexLoad, IMLReg fprReg) +{ + IMLRegID fprIndex = fprReg.GetRegID(); + + IMLInstruction* imlInstructionLoad = imlSegment->imlList.data() + imlIndexLoad; + if (imlInstructionLoad->op_storeLoad.flags2.notExpanded) + return; + + IMLUsedRegisters registersUsed; + sint32 scanRangeEnd = std::min(imlIndexLoad + 25, imlSegment->imlList.size()); // don't scan too far (saves performance and also the chances we can merge the load+store become low at high distances) + bool foundMatch = false; + sint32 lastStore = -1; + for (sint32 i = imlIndexLoad + 1; i < scanRangeEnd; i++) + { + IMLInstruction* imlInstruction = imlSegment->imlList.data() + i; + if (imlInstruction->IsSuffixInstruction()) + break; + // check if FPR is stored + if ((imlInstruction->type == PPCREC_IML_TYPE_FPR_STORE && imlInstruction->op_storeLoad.mode == PPCREC_FPR_ST_MODE_SINGLE_FROM_PS0) || + (imlInstruction->type == PPCREC_IML_TYPE_FPR_STORE_INDEXED && imlInstruction->op_storeLoad.mode == PPCREC_FPR_ST_MODE_SINGLE_FROM_PS0)) + { + if (imlInstruction->op_storeLoad.registerData.GetRegID() == fprIndex) + { + if (foundMatch == false) + { + // flag the load-single instruction as "don't expand" (leave single value as-is) + imlInstructionLoad->op_storeLoad.flags2.notExpanded = true; + } + // also set the flag for the store instruction + IMLInstruction* imlInstructionStore = imlInstruction; + imlInstructionStore->op_storeLoad.flags2.notExpanded = true; + + foundMatch = true; + lastStore = i + 1; + + continue; + } + } + + // check if FPR is overwritten (we can actually ignore read operations?) + imlInstruction->CheckRegisterUsage(®istersUsed); + if (registersUsed.writtenFPR1.IsValidAndSameRegID(fprIndex)) + break; + if (registersUsed.readFPR1.IsValidAndSameRegID(fprIndex)) + break; + if (registersUsed.readFPR2.IsValidAndSameRegID(fprIndex)) + break; + if (registersUsed.readFPR3.IsValidAndSameRegID(fprIndex)) + break; + if (registersUsed.readFPR4.IsValidAndSameRegID(fprIndex)) + break; + } + + if (foundMatch) + { + // insert expand instruction after store + IMLInstruction* newExpand = PPCRecompiler_insertInstruction(imlSegment, lastStore); + PPCRecompilerImlGen_generateNewInstruction_fpr_r(ppcImlGenContext, newExpand, PPCREC_IML_OP_FPR_EXPAND_BOTTOM32_TO_BOTTOM64_AND_TOP64, _FPRRegFromID(fprIndex)); + } +} + +/* +* Scans for patterns: +* +* +* +* For these patterns the store and load is modified to work with un-extended values (float remains as float, no double conversion) +* The float->double extension is then executed later +* Advantages: +* Keeps denormals and other special float values intact +* Slightly improves performance +*/ +void IMLOptimizer_OptimizeDirectFloatCopies(ppcImlGenContext_t* ppcImlGenContext) +{ + for (IMLSegment* segIt : ppcImlGenContext->segmentList2) + { + for (sint32 i = 0; i < segIt->imlList.size(); i++) + { + IMLInstruction* imlInstruction = segIt->imlList.data() + i; + if (imlInstruction->type == PPCREC_IML_TYPE_FPR_LOAD && imlInstruction->op_storeLoad.mode == PPCREC_FPR_LD_MODE_SINGLE_INTO_PS0_PS1) + { + PPCRecompiler_optimizeDirectFloatCopiesScanForward(ppcImlGenContext, segIt, i, imlInstruction->op_storeLoad.registerData); + } + else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_LOAD_INDEXED && imlInstruction->op_storeLoad.mode == PPCREC_FPR_LD_MODE_SINGLE_INTO_PS0_PS1) + { + PPCRecompiler_optimizeDirectFloatCopiesScanForward(ppcImlGenContext, segIt, i, imlInstruction->op_storeLoad.registerData); + } + } + } +} + +void PPCRecompiler_optimizeDirectIntegerCopiesScanForward(ppcImlGenContext_t* ppcImlGenContext, IMLSegment* imlSegment, sint32 imlIndexLoad, IMLReg gprReg) +{ + cemu_assert_debug(gprReg.GetBaseFormat() == IMLRegFormat::I64); // todo - proper handling required for non-standard sizes + cemu_assert_debug(gprReg.GetRegFormat() == IMLRegFormat::I32); + + IMLRegID gprIndex = gprReg.GetRegID(); + IMLInstruction* imlInstructionLoad = imlSegment->imlList.data() + imlIndexLoad; + if ( imlInstructionLoad->op_storeLoad.flags2.swapEndian == false ) + return; + bool foundMatch = false; + IMLUsedRegisters registersUsed; + sint32 scanRangeEnd = std::min(imlIndexLoad + 25, imlSegment->imlList.size()); // don't scan too far (saves performance and also the chances we can merge the load+store become low at high distances) + sint32 i = imlIndexLoad + 1; + for (; i < scanRangeEnd; i++) + { + IMLInstruction* imlInstruction = imlSegment->imlList.data() + i; + if (imlInstruction->IsSuffixInstruction()) + break; + // check if GPR is stored + if ((imlInstruction->type == PPCREC_IML_TYPE_STORE && imlInstruction->op_storeLoad.copyWidth == 32 ) ) + { + if (imlInstruction->op_storeLoad.registerMem.GetRegID() == gprIndex) + break; + if (imlInstruction->op_storeLoad.registerData.GetRegID() == gprIndex) + { + IMLInstruction* imlInstructionStore = imlInstruction; + if (foundMatch == false) + { + // switch the endian swap flag for the load instruction + imlInstructionLoad->op_storeLoad.flags2.swapEndian = !imlInstructionLoad->op_storeLoad.flags2.swapEndian; + foundMatch = true; + } + // switch the endian swap flag for the store instruction + imlInstructionStore->op_storeLoad.flags2.swapEndian = !imlInstructionStore->op_storeLoad.flags2.swapEndian; + // keep scanning + continue; + } + } + // check if GPR is accessed + imlInstruction->CheckRegisterUsage(®istersUsed); + if (registersUsed.readGPR1.IsValidAndSameRegID(gprIndex) || + registersUsed.readGPR2.IsValidAndSameRegID(gprIndex) || + registersUsed.readGPR3.IsValidAndSameRegID(gprIndex)) + { + break; + } + if (registersUsed.IsBaseGPRWritten(gprReg)) + return; // GPR overwritten, we don't need to byte swap anymore + } + if (foundMatch) + { + PPCRecompiler_insertInstruction(imlSegment, i)->make_r_r(PPCREC_IML_OP_ENDIAN_SWAP, gprReg, gprReg); + } +} + +/* +* Scans for patterns: +* +* +* +* For these patterns the store and load is modified to work with non-swapped values +* The big_endian->little_endian conversion is then executed later +* Advantages: +* Slightly improves performance +*/ +void IMLOptimizer_OptimizeDirectIntegerCopies(ppcImlGenContext_t* ppcImlGenContext) +{ + for (IMLSegment* segIt : ppcImlGenContext->segmentList2) + { + for (sint32 i = 0; i < segIt->imlList.size(); i++) + { + IMLInstruction* imlInstruction = segIt->imlList.data() + i; + if (imlInstruction->type == PPCREC_IML_TYPE_LOAD && imlInstruction->op_storeLoad.copyWidth == 32 && imlInstruction->op_storeLoad.flags2.swapEndian ) + { + PPCRecompiler_optimizeDirectIntegerCopiesScanForward(ppcImlGenContext, segIt, i, imlInstruction->op_storeLoad.registerData); + } + } + } +} + +IMLName PPCRecompilerImlGen_GetRegName(ppcImlGenContext_t* ppcImlGenContext, IMLReg reg); + +sint32 _getGQRIndexFromRegister(ppcImlGenContext_t* ppcImlGenContext, IMLReg gqrReg) +{ + if (gqrReg.IsInvalid()) + return -1; + sint32 namedReg = PPCRecompilerImlGen_GetRegName(ppcImlGenContext, gqrReg); + if (namedReg >= (PPCREC_NAME_SPR0 + SPR_UGQR0) && namedReg <= (PPCREC_NAME_SPR0 + SPR_UGQR7)) + { + return namedReg - (PPCREC_NAME_SPR0 + SPR_UGQR0); + } + else + { + cemu_assert_suspicious(); + } + return -1; +} + +bool PPCRecompiler_isUGQRValueKnown(ppcImlGenContext_t* ppcImlGenContext, sint32 gqrIndex, uint32& gqrValue) +{ + // UGQR 2 to 7 are initialized by the OS and we assume that games won't ever permanently touch those + // todo - hack - replace with more accurate solution + if (gqrIndex == 2) + gqrValue = 0x00040004; + else if (gqrIndex == 3) + gqrValue = 0x00050005; + else if (gqrIndex == 4) + gqrValue = 0x00060006; + else if (gqrIndex == 5) + gqrValue = 0x00070007; + else + return false; + return true; +} + +/* + * If value of GQR can be predicted for a given PSQ load or store instruction then replace it with an optimized version + */ +void PPCRecompiler_optimizePSQLoadAndStore(ppcImlGenContext_t* ppcImlGenContext) +{ + for (IMLSegment* segIt : ppcImlGenContext->segmentList2) + { + for(IMLInstruction& instIt : segIt->imlList) + { + if (instIt.type == PPCREC_IML_TYPE_FPR_LOAD || instIt.type == PPCREC_IML_TYPE_FPR_LOAD_INDEXED) + { + if(instIt.op_storeLoad.mode != PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0 && + instIt.op_storeLoad.mode != PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0_PS1 ) + continue; + // get GQR value + cemu_assert_debug(instIt.op_storeLoad.registerGQR.IsValid()); + sint32 gqrIndex = _getGQRIndexFromRegister(ppcImlGenContext, instIt.op_storeLoad.registerGQR); + cemu_assert(gqrIndex >= 0); + if (ppcImlGenContext->tracking.modifiesGQR[gqrIndex]) + continue; + uint32 gqrValue; + if (!PPCRecompiler_isUGQRValueKnown(ppcImlGenContext, gqrIndex, gqrValue)) + continue; + + uint32 formatType = (gqrValue >> 16) & 7; + uint32 scale = (gqrValue >> 24) & 0x3F; + if (scale != 0) + continue; // only generic handler supports scale + if (instIt.op_storeLoad.mode == PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0) + { + if (formatType == 0) + instIt.op_storeLoad.mode = PPCREC_FPR_LD_MODE_PSQ_FLOAT_PS0; + else if (formatType == 4) + instIt.op_storeLoad.mode = PPCREC_FPR_LD_MODE_PSQ_U8_PS0; + else if (formatType == 5) + instIt.op_storeLoad.mode = PPCREC_FPR_LD_MODE_PSQ_U16_PS0; + else if (formatType == 6) + instIt.op_storeLoad.mode = PPCREC_FPR_LD_MODE_PSQ_S8_PS0; + else if (formatType == 7) + instIt.op_storeLoad.mode = PPCREC_FPR_LD_MODE_PSQ_S16_PS0; + if (instIt.op_storeLoad.mode != PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0) + instIt.op_storeLoad.registerGQR = IMLREG_INVALID; + } + else if (instIt.op_storeLoad.mode == PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0_PS1) + { + if (formatType == 0) + instIt.op_storeLoad.mode = PPCREC_FPR_LD_MODE_PSQ_FLOAT_PS0_PS1; + else if (formatType == 4) + instIt.op_storeLoad.mode = PPCREC_FPR_LD_MODE_PSQ_U8_PS0_PS1; + else if (formatType == 5) + instIt.op_storeLoad.mode = PPCREC_FPR_LD_MODE_PSQ_U16_PS0_PS1; + else if (formatType == 6) + instIt.op_storeLoad.mode = PPCREC_FPR_LD_MODE_PSQ_S8_PS0_PS1; + else if (formatType == 7) + instIt.op_storeLoad.mode = PPCREC_FPR_LD_MODE_PSQ_S16_PS0_PS1; + if (instIt.op_storeLoad.mode != PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0_PS1) + instIt.op_storeLoad.registerGQR = IMLREG_INVALID; + } + } + else if (instIt.type == PPCREC_IML_TYPE_FPR_STORE || instIt.type == PPCREC_IML_TYPE_FPR_STORE_INDEXED) + { + if(instIt.op_storeLoad.mode != PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0 && + instIt.op_storeLoad.mode != PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0_PS1) + continue; + // get GQR value + cemu_assert_debug(instIt.op_storeLoad.registerGQR.IsValid()); + sint32 gqrIndex = _getGQRIndexFromRegister(ppcImlGenContext, instIt.op_storeLoad.registerGQR); + cemu_assert(gqrIndex >= 0 && gqrIndex < 8); + if (ppcImlGenContext->tracking.modifiesGQR[gqrIndex]) + continue; + uint32 gqrValue; + if(!PPCRecompiler_isUGQRValueKnown(ppcImlGenContext, gqrIndex, gqrValue)) + continue; + uint32 formatType = (gqrValue >> 16) & 7; + uint32 scale = (gqrValue >> 24) & 0x3F; + if (scale != 0) + continue; // only generic handler supports scale + if (instIt.op_storeLoad.mode == PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0) + { + if (formatType == 0) + instIt.op_storeLoad.mode = PPCREC_FPR_ST_MODE_PSQ_FLOAT_PS0; + else if (formatType == 4) + instIt.op_storeLoad.mode = PPCREC_FPR_ST_MODE_PSQ_U8_PS0; + else if (formatType == 5) + instIt.op_storeLoad.mode = PPCREC_FPR_ST_MODE_PSQ_U16_PS0; + else if (formatType == 6) + instIt.op_storeLoad.mode = PPCREC_FPR_ST_MODE_PSQ_S8_PS0; + else if (formatType == 7) + instIt.op_storeLoad.mode = PPCREC_FPR_ST_MODE_PSQ_S16_PS0; + if (instIt.op_storeLoad.mode != PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0) + instIt.op_storeLoad.registerGQR = IMLREG_INVALID; + } + else if (instIt.op_storeLoad.mode == PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0_PS1) + { + if (formatType == 0) + instIt.op_storeLoad.mode = PPCREC_FPR_ST_MODE_PSQ_FLOAT_PS0_PS1; + else if (formatType == 4) + instIt.op_storeLoad.mode = PPCREC_FPR_ST_MODE_PSQ_U8_PS0_PS1; + else if (formatType == 5) + instIt.op_storeLoad.mode = PPCREC_FPR_ST_MODE_PSQ_U16_PS0_PS1; + else if (formatType == 6) + instIt.op_storeLoad.mode = PPCREC_FPR_ST_MODE_PSQ_S8_PS0_PS1; + else if (formatType == 7) + instIt.op_storeLoad.mode = PPCREC_FPR_ST_MODE_PSQ_S16_PS0_PS1; + if (instIt.op_storeLoad.mode != PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0_PS1) + instIt.op_storeLoad.registerGQR = IMLREG_INVALID; + } + } + } + } +} diff --git a/src/Cafe/HW/Espresso/Recompiler/IML/IMLRegisterAllocator.cpp b/src/Cafe/HW/Espresso/Recompiler/IML/IMLRegisterAllocator.cpp new file mode 100644 index 00000000..077a0516 --- /dev/null +++ b/src/Cafe/HW/Espresso/Recompiler/IML/IMLRegisterAllocator.cpp @@ -0,0 +1,1422 @@ +#include "IML.h" + +#include "../PPCRecompiler.h" +#include "../PPCRecompilerIml.h" +#include "IMLRegisterAllocator.h" +#include "IMLRegisterAllocatorRanges.h" + +#include "../BackendX64/BackendX64.h" + +#include + +struct IMLRARegAbstractLiveness // preliminary liveness info. One entry per register and segment +{ + IMLRARegAbstractLiveness(IMLRegFormat regBaseFormat, sint32 usageStart, sint32 usageEnd) : regBaseFormat(regBaseFormat), usageStart(usageStart), usageEnd(usageEnd) {}; + + void TrackInstruction(sint32 index) + { + usageStart = std::min(usageStart, index); + usageEnd = std::max(usageEnd, index + 1); // exclusive index + } + + sint32 usageStart; + sint32 usageEnd; + bool isProcessed{false}; + IMLRegFormat regBaseFormat; +}; + +struct IMLRegisterAllocatorContext +{ + IMLRegisterAllocatorParameters* raParam; + ppcImlGenContext_t* deprGenContext; // deprecated. Try to decouple IMLRA from other parts of IML/PPCRec + + std::unordered_map regIdToBaseFormat; // a vector would be more efficient but it also means that reg ids have to be continuous and not completely arbitrary + // first pass + std::vector> perSegmentAbstractRanges; + // second pass + + // helper methods + inline std::unordered_map& GetSegmentAbstractRangeMap(IMLSegment* imlSegment) + { + return perSegmentAbstractRanges[imlSegment->momentaryIndex]; + } + + inline IMLRegFormat GetBaseFormatByRegId(IMLRegID regId) const + { + auto it = regIdToBaseFormat.find(regId); + cemu_assert_debug(it != regIdToBaseFormat.cend()); + return it->second; + } + +}; + +uint32 recRACurrentIterationIndex = 0; + +uint32 PPCRecRA_getNextIterationIndex() +{ + recRACurrentIterationIndex++; + return recRACurrentIterationIndex; +} + +bool _detectLoop(IMLSegment* currentSegment, sint32 depth, uint32 iterationIndex, IMLSegment* imlSegmentLoopBase) +{ + if (currentSegment == imlSegmentLoopBase) + return true; + if (currentSegment->raInfo.lastIterationIndex == iterationIndex) + return currentSegment->raInfo.isPartOfProcessedLoop; + if (depth >= 9) + return false; + currentSegment->raInfo.lastIterationIndex = iterationIndex; + currentSegment->raInfo.isPartOfProcessedLoop = false; + + if (currentSegment->nextSegmentIsUncertain) + return false; + if (currentSegment->nextSegmentBranchNotTaken) + { + if (currentSegment->nextSegmentBranchNotTaken->momentaryIndex > currentSegment->momentaryIndex) + { + currentSegment->raInfo.isPartOfProcessedLoop = _detectLoop(currentSegment->nextSegmentBranchNotTaken, depth + 1, iterationIndex, imlSegmentLoopBase); + } + } + if (currentSegment->nextSegmentBranchTaken) + { + if (currentSegment->nextSegmentBranchTaken->momentaryIndex > currentSegment->momentaryIndex) + { + currentSegment->raInfo.isPartOfProcessedLoop = _detectLoop(currentSegment->nextSegmentBranchTaken, depth + 1, iterationIndex, imlSegmentLoopBase); + } + } + if (currentSegment->raInfo.isPartOfProcessedLoop) + currentSegment->loopDepth++; + return currentSegment->raInfo.isPartOfProcessedLoop; +} + +void PPCRecRA_detectLoop(ppcImlGenContext_t* ppcImlGenContext, IMLSegment* imlSegmentLoopBase) +{ + uint32 iterationIndex = PPCRecRA_getNextIterationIndex(); + imlSegmentLoopBase->raInfo.lastIterationIndex = iterationIndex; + if (_detectLoop(imlSegmentLoopBase->nextSegmentBranchTaken, 0, iterationIndex, imlSegmentLoopBase)) + { + imlSegmentLoopBase->loopDepth++; + } +} + +void PPCRecRA_identifyLoop(ppcImlGenContext_t* ppcImlGenContext, IMLSegment* imlSegment) +{ + if (imlSegment->nextSegmentIsUncertain) + return; + // check if this segment has a branch that links to itself (tight loop) + if (imlSegment->nextSegmentBranchTaken == imlSegment) + { + // segment loops over itself + imlSegment->loopDepth++; + return; + } + // check if this segment has a branch that goes backwards (potential complex loop) + if (imlSegment->nextSegmentBranchTaken && imlSegment->nextSegmentBranchTaken->momentaryIndex < imlSegment->momentaryIndex) + { + PPCRecRA_detectLoop(ppcImlGenContext, imlSegment); + } +} + +#define SUBRANGE_LIST_SIZE (128) + +sint32 PPCRecRA_countInstructionsUntilNextUse(raLivenessSubrange_t* subrange, sint32 startIndex) +{ + for (sint32 i = 0; i < subrange->list_locations.size(); i++) + { + if (subrange->list_locations.data()[i].index >= startIndex) + return subrange->list_locations.data()[i].index - startIndex; + } + return INT_MAX; +} + +// count how many instructions there are until physRegister is used by any subrange (returns 0 if register is in use at startIndex, and INT_MAX if not used for the remainder of the segment) +sint32 PPCRecRA_countInstructionsUntilNextLocalPhysRegisterUse(IMLSegment* imlSegment, sint32 startIndex, sint32 physRegister) +{ + sint32 minDistance = INT_MAX; + // next + raLivenessSubrange_t* subrangeItr = imlSegment->raInfo.linkedList_allSubranges; + while(subrangeItr) + { + if (subrangeItr->range->physicalRegister != physRegister) + { + subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; + continue; + } + if (startIndex >= subrangeItr->start.index && startIndex < subrangeItr->end.index) + return 0; + if (subrangeItr->start.index >= startIndex) + { + minDistance = std::min(minDistance, (subrangeItr->start.index - startIndex)); + } + subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; + } + return minDistance; +} + +struct IMLRALivenessTimeline +{ +// IMLRALivenessTimeline(raLivenessSubrange_t* subrangeChain) +// { +//#ifdef CEMU_DEBUG_ASSERT +// raLivenessSubrange_t* it = subrangeChain; +// raLivenessSubrange_t* prevIt = it; +// while (it) +// { +// cemu_assert_debug(prevIt->start.index <= it->start.index); +// prevIt = it; +// it = it->link_segmentSubrangesGPR.next; +// } +//#endif +// } + + IMLRALivenessTimeline() + { + } + + // manually add an active range + void AddActiveRange(raLivenessSubrange_t* subrange) + { + activeRanges.emplace_back(subrange); + } + + // remove all ranges from activeRanges with end <= instructionIndex + void ExpireRanges(sint32 instructionIndex) + { + expiredRanges.clear(); + size_t count = activeRanges.size(); + for (size_t f = 0; f < count; f++) + { + raLivenessSubrange_t* liverange = activeRanges[f]; + if (liverange->end.index <= instructionIndex) + { +#ifdef CEMU_DEBUG_ASSERT + if (instructionIndex != RA_INTER_RANGE_END && (liverange->subrangeBranchTaken || liverange->subrangeBranchNotTaken)) + assert_dbg(); // infinite subranges should not expire +#endif + expiredRanges.emplace_back(liverange); + // remove entry + activeRanges[f] = activeRanges[count-1]; + f--; + count--; + } + } + if(count != activeRanges.size()) + activeRanges.resize(count); + } + + std::span GetExpiredRanges() + { + return { expiredRanges.data(), expiredRanges.size() }; + } + + boost::container::small_vector activeRanges; + +private: + boost::container::small_vector expiredRanges; +}; + +bool IsRangeOverlapping(raLivenessSubrange_t* rangeA, raLivenessSubrange_t* rangeB) +{ + if (rangeA->start.index < rangeB->end.index && rangeA->end.index > rangeB->start.index) + return true; + if ((rangeA->start.index == RA_INTER_RANGE_START && rangeA->start.index == rangeB->start.index)) + return true; + if (rangeA->end.index == RA_INTER_RANGE_END && rangeA->end.index == rangeB->end.index) + return true; + return false; +} + +// mark occupied registers by any overlapping range as unavailable in physRegSet +void PPCRecRA_MaskOverlappingPhysRegForGlobalRange(raLivenessRange_t* range, IMLPhysRegisterSet& physRegSet) +{ + for (auto& subrange : range->list_subranges) + { + IMLSegment* imlSegment = subrange->imlSegment; + raLivenessSubrange_t* subrangeItr = imlSegment->raInfo.linkedList_allSubranges; + while(subrangeItr) + { + if (subrange == subrangeItr) + { + // next + subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; + continue; + } + if(IsRangeOverlapping(subrange, subrangeItr)) + { + if (subrangeItr->range->physicalRegister >= 0) + physRegSet.SetReserved(subrangeItr->range->physicalRegister); + } + // next + subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; + } + } +} + +bool _livenessRangeStartCompare(raLivenessSubrange_t* lhs, raLivenessSubrange_t* rhs) { return lhs->start.index < rhs->start.index; } + +void _sortSegmentAllSubrangesLinkedList(IMLSegment* imlSegment) +{ + raLivenessSubrange_t* subrangeList[4096+1]; + sint32 count = 0; + // disassemble linked list + raLivenessSubrange_t* subrangeItr = imlSegment->raInfo.linkedList_allSubranges; + while (subrangeItr) + { + if (count >= 4096) + assert_dbg(); + subrangeList[count] = subrangeItr; + count++; + // next + subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; + } + if (count == 0) + { + imlSegment->raInfo.linkedList_allSubranges = nullptr; + return; + } + // sort + std::sort(subrangeList, subrangeList + count, _livenessRangeStartCompare); + // reassemble linked list + subrangeList[count] = nullptr; + imlSegment->raInfo.linkedList_allSubranges = subrangeList[0]; + subrangeList[0]->link_segmentSubrangesGPR.prev = nullptr; + subrangeList[0]->link_segmentSubrangesGPR.next = subrangeList[1]; + for (sint32 i = 1; i < count; i++) + { + subrangeList[i]->link_segmentSubrangesGPR.prev = subrangeList[i - 1]; + subrangeList[i]->link_segmentSubrangesGPR.next = subrangeList[i + 1]; + } + // validate list +#ifdef CEMU_DEBUG_ASSERT + sint32 count2 = 0; + subrangeItr = imlSegment->raInfo.linkedList_allSubranges; + sint32 currentStartIndex = RA_INTER_RANGE_START; + while (subrangeItr) + { + count2++; + if (subrangeItr->start.index < currentStartIndex) + assert_dbg(); + currentStartIndex = subrangeItr->start.index; + // next + subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; + } + if (count != count2) + assert_dbg(); +#endif +} + +std::unordered_map& IMLRA_GetSubrangeMap(IMLSegment* imlSegment) +{ + return imlSegment->raInfo.linkedList_perVirtualGPR2; +} + +raLivenessSubrange_t* IMLRA_GetSubrange(IMLSegment* imlSegment, IMLRegID regId) +{ + auto it = imlSegment->raInfo.linkedList_perVirtualGPR2.find(regId); + if (it == imlSegment->raInfo.linkedList_perVirtualGPR2.end()) + return nullptr; + return it->second; +} + +raLivenessSubrange_t* _GetSubrangeByInstructionIndexAndVirtualReg(IMLSegment* imlSegment, IMLReg regToSearch, sint32 instructionIndex) +{ + uint32 regId = regToSearch.GetRegID(); + raLivenessSubrange_t* subrangeItr = IMLRA_GetSubrange(imlSegment, regId); + while (subrangeItr) + { + if (subrangeItr->start.index <= instructionIndex && subrangeItr->end.index > instructionIndex) + return subrangeItr; + subrangeItr = subrangeItr->link_sameVirtualRegisterGPR.next; + } + return nullptr; +} + +void IMLRA_IsolateRangeOnInstruction(ppcImlGenContext_t* ppcImlGenContext, IMLSegment* imlSegment, raLivenessSubrange_t* subrange, sint32 instructionIndex) +{ + DEBUG_BREAK; +} + +void IMLRA_HandleFixedRegisters(ppcImlGenContext_t* ppcImlGenContext, IMLSegment* imlSegment) +{ + // this works as a pre-pass to actual register allocation. Assigning registers in advance based on fixed requirements (e.g. calling conventions and operations with fixed-reg input/output like x86 DIV/MUL) + // algorithm goes as follows: + // 1) Iterate all instructions from beginning to end and keep a list of covering ranges + // 2) If we encounter an instruction with a fixed register we: + // 2.0) Check if there are any other ranges already using the same fixed-register and if yes, we split them and unassign the register for any follow-up instructions just prior to the current instruction + // 2.1) For inputs: Split the range that needs to be assigned a phys reg on the current instruction. Basically creating a 1-instruction long subrange that we can assign the physical register. RA will then schedule register allocation around that and avoid moves + // 2.2) For outputs: Split the range that needs to be assigned a phys reg on the current instruction + // Q: What if a specific fixed-register is used both for input and output and thus is destructive? A: Create temporary range + // Q: What if we have 3 different inputs that are all the same virtual register? A: Create temporary range + // Q: Assuming the above is implemented, do we even support overlapping two ranges of separate virtual regs on the same phys register? In theory the RA shouldn't care + + // experimental code + //for (size_t i = 0; i < imlSegment->imlList.size(); i++) + //{ + // IMLInstruction& inst = imlSegment->imlList[i]; + // if (inst.type == PPCREC_IML_TYPE_R_R_R) + // { + // if (inst.operation == PPCREC_IML_OP_LEFT_SHIFT) + // { + // // get the virtual reg which needs to be assigned a fixed register + // //IMLUsedRegisters usedReg; + // //inst.CheckRegisterUsage(&usedReg); + // IMLReg rB = inst.op_r_r_r.regB; + // // rB needs to use RCX/ECX + // raLivenessSubrange_t* subrange = _GetSubrangeByInstructionIndexAndVirtualReg(imlSegment, rB, i); + // cemu_assert_debug(subrange->range->physicalRegister < 0); // already has a phys reg assigned + // // make sure RCX/ECX is free + // // split before (if needed) and after instruction so that we get a new 1-instruction long range for which we can assign the physical register + // raLivenessSubrange_t* instructionRange = subrange->start.index < i ? PPCRecRA_splitLocalSubrange(ppcImlGenContext, subrange, i, false) : subrange; + // raLivenessSubrange_t* tailRange = PPCRecRA_splitLocalSubrange(ppcImlGenContext, instructionRange, i+1, false); + + // } + // } + //} +} + +bool IMLRA_AssignSegmentRegisters(IMLRegisterAllocatorContext& ctx, ppcImlGenContext_t* ppcImlGenContext, IMLSegment* imlSegment) +{ + // sort subranges ascending by start index + _sortSegmentAllSubrangesLinkedList(imlSegment); + + IMLRALivenessTimeline livenessTimeline; + raLivenessSubrange_t* subrangeItr = imlSegment->raInfo.linkedList_allSubranges; + while(subrangeItr) + { + sint32 currentIndex = subrangeItr->start.index; + PPCRecRA_debugValidateSubrange(subrangeItr); + livenessTimeline.ExpireRanges(std::min(currentIndex, RA_INTER_RANGE_END-1)); // expire up to currentIndex (inclusive), but exclude infinite ranges + // if subrange already has register assigned then add it to the active list and continue + if (subrangeItr->range->physicalRegister >= 0) + { + // verify if register is actually available +#ifdef CEMU_DEBUG_ASSERT + for (auto& liverangeItr : livenessTimeline.activeRanges) + { + // check for register mismatch + cemu_assert_debug(liverangeItr->range->physicalRegister != subrangeItr->range->physicalRegister); + } +#endif + livenessTimeline.AddActiveRange(subrangeItr); + subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; + continue; + } + // find free register for current subrangeItr and segment + IMLRegFormat regBaseFormat = ctx.GetBaseFormatByRegId(subrangeItr->range->virtualRegister); + IMLPhysRegisterSet physRegSet = ctx.raParam->GetPhysRegPool(regBaseFormat); + cemu_assert_debug(physRegSet.HasAnyAvailable()); // register uses type with no valid pool + for (auto& liverangeItr : livenessTimeline.activeRanges) + { + cemu_assert_debug(liverangeItr->range->physicalRegister >= 0); + physRegSet.SetReserved(liverangeItr->range->physicalRegister); + } + // check intersections with other ranges and determine allowed registers + IMLPhysRegisterSet localAvailableRegsMask = physRegSet; // mask of registers that are currently not used (does not include range checks in other segments) + if(physRegSet.HasAnyAvailable()) + { + // check globally in all segments + PPCRecRA_MaskOverlappingPhysRegForGlobalRange(subrangeItr->range, physRegSet); + } + if (!physRegSet.HasAnyAvailable()) + { + struct + { + // estimated costs and chosen candidates for the different spill strategies + // hole cutting into a local range + struct + { + sint32 distance; + raLivenessSubrange_t* largestHoleSubrange; + sint32 cost; // additional cost of choosing this candidate + }localRangeHoleCutting; + // split current range (this is generally only a good choice when the current range is long but rarely used) + struct + { + sint32 cost; + sint32 physRegister; + sint32 distance; // size of hole + }availableRegisterHole; + // explode a inter-segment range (prefer ranges that are not read/written in this segment) + struct + { + raLivenessRange_t* range; + sint32 cost; + sint32 distance; // size of hole + // note: If we explode a range, we still have to check the size of the hole that becomes available, if too small then we need to add cost of splitting local subrange + }explodeRange; + // todo - add more strategies, make cost estimation smarter (for example, in some cases splitting can have reduced or no cost if read/store can be avoided due to data flow) + }spillStrategies; + // cant assign register + // there might be registers available, we just can't use them due to range conflicts + if (subrangeItr->end.index != RA_INTER_RANGE_END) + { + // range ends in current segment + + // Current algo looks like this: + // 1) Get the size of the largest possible hole that we can cut into any of the live local subranges + // 1.1) Check if the hole is large enough to hold the current subrange + // 2) If yes, cut hole and return false (full retry) + // 3) If no, try to reuse free register (need to determine how large the region is we can use) + // 4) If there is no free register or the range is extremely short go back to step 1+2 but additionally split the current subrange at where the hole ends + + cemu_assert_debug(currentIndex == subrangeItr->start.index); + + sint32 requiredSize = subrangeItr->end.index - subrangeItr->start.index; + // evaluate strategy: Cut hole into local subrange + spillStrategies.localRangeHoleCutting.distance = -1; + spillStrategies.localRangeHoleCutting.largestHoleSubrange = nullptr; + spillStrategies.localRangeHoleCutting.cost = INT_MAX; + if (currentIndex >= 0) + { + for (auto candidate : livenessTimeline.activeRanges) + { + if (candidate->end.index == RA_INTER_RANGE_END) + continue; + sint32 distance = PPCRecRA_countInstructionsUntilNextUse(candidate, currentIndex); + if (distance < 2) + continue; // not even worth the consideration + // calculate split cost of candidate + sint32 cost = PPCRecRARange_estimateAdditionalCostAfterSplit(candidate, currentIndex + distance); + // calculate additional split cost of currentRange if hole is not large enough + if (distance < requiredSize) + { + cost += PPCRecRARange_estimateAdditionalCostAfterSplit(subrangeItr, currentIndex + distance); + // we also slightly increase cost in relation to the remaining length (in order to make the algorithm prefer larger holes) + cost += (requiredSize - distance) / 10; + } + // compare cost with previous candidates + if (cost < spillStrategies.localRangeHoleCutting.cost) + { + spillStrategies.localRangeHoleCutting.cost = cost; + spillStrategies.localRangeHoleCutting.distance = distance; + spillStrategies.localRangeHoleCutting.largestHoleSubrange = candidate; + } + } + } + // evaluate strategy: Split current range to fit in available holes + // todo - are checks required to avoid splitting on the suffix instruction? + spillStrategies.availableRegisterHole.cost = INT_MAX; + spillStrategies.availableRegisterHole.distance = -1; + spillStrategies.availableRegisterHole.physRegister = -1; + if (currentIndex >= 0) + { + if (localAvailableRegsMask.HasAnyAvailable()) + { + sint32 physRegItr = -1; + while (true) + { + physRegItr = localAvailableRegsMask.GetNextAvailableReg(physRegItr + 1); + if (physRegItr < 0) + break; + // get size of potential hole for this register + sint32 distance = PPCRecRA_countInstructionsUntilNextLocalPhysRegisterUse(imlSegment, currentIndex, physRegItr); + if (distance < 2) + continue; // not worth consideration + // calculate additional cost due to split + if (distance >= requiredSize) + assert_dbg(); // should not happen or else we would have selected this register + sint32 cost = PPCRecRARange_estimateAdditionalCostAfterSplit(subrangeItr, currentIndex + distance); + // add small additional cost for the remaining range (prefer larger holes) + cost += (requiredSize - distance) / 10; + if (cost < spillStrategies.availableRegisterHole.cost) + { + spillStrategies.availableRegisterHole.cost = cost; + spillStrategies.availableRegisterHole.distance = distance; + spillStrategies.availableRegisterHole.physRegister = physRegItr; + } + } + } + } + // evaluate strategy: Explode inter-segment ranges + spillStrategies.explodeRange.cost = INT_MAX; + spillStrategies.explodeRange.range = nullptr; + spillStrategies.explodeRange.distance = -1; + for (auto candidate : livenessTimeline.activeRanges) + { + if (candidate->end.index != RA_INTER_RANGE_END) + continue; + sint32 distance = PPCRecRA_countInstructionsUntilNextUse(candidate, currentIndex); + if( distance < 2) + continue; + sint32 cost; + cost = PPCRecRARange_estimateAdditionalCostAfterRangeExplode(candidate->range); + // if the hole is not large enough, add cost of splitting current subrange + if (distance < requiredSize) + { + cost += PPCRecRARange_estimateAdditionalCostAfterSplit(subrangeItr, currentIndex + distance); + // add small additional cost for the remaining range (prefer larger holes) + cost += (requiredSize - distance) / 10; + } + // compare with current best candidate for this strategy + if (cost < spillStrategies.explodeRange.cost) + { + spillStrategies.explodeRange.cost = cost; + spillStrategies.explodeRange.distance = distance; + spillStrategies.explodeRange.range = candidate->range; + } + } + // choose strategy + if (spillStrategies.explodeRange.cost != INT_MAX && spillStrategies.explodeRange.cost <= spillStrategies.localRangeHoleCutting.cost && spillStrategies.explodeRange.cost <= spillStrategies.availableRegisterHole.cost) + { + // explode range + PPCRecRA_explodeRange(ppcImlGenContext, spillStrategies.explodeRange.range); + // split current subrange if necessary + if( requiredSize > spillStrategies.explodeRange.distance) + PPCRecRA_splitLocalSubrange(ppcImlGenContext, subrangeItr, currentIndex+spillStrategies.explodeRange.distance, true); + } + else if (spillStrategies.availableRegisterHole.cost != INT_MAX && spillStrategies.availableRegisterHole.cost <= spillStrategies.explodeRange.cost && spillStrategies.availableRegisterHole.cost <= spillStrategies.localRangeHoleCutting.cost) + { + // use available register + PPCRecRA_splitLocalSubrange(ppcImlGenContext, subrangeItr, currentIndex + spillStrategies.availableRegisterHole.distance, true); + } + else if (spillStrategies.localRangeHoleCutting.cost != INT_MAX && spillStrategies.localRangeHoleCutting.cost <= spillStrategies.explodeRange.cost && spillStrategies.localRangeHoleCutting.cost <= spillStrategies.availableRegisterHole.cost) + { + // cut hole + PPCRecRA_splitLocalSubrange(ppcImlGenContext, spillStrategies.localRangeHoleCutting.largestHoleSubrange, currentIndex + spillStrategies.localRangeHoleCutting.distance, true); + // split current subrange if necessary + if (requiredSize > spillStrategies.localRangeHoleCutting.distance) + PPCRecRA_splitLocalSubrange(ppcImlGenContext, subrangeItr, currentIndex + spillStrategies.localRangeHoleCutting.distance, true); + } + else if (subrangeItr->start.index == RA_INTER_RANGE_START) + { + // alternative strategy if we have no other choice: explode current range + PPCRecRA_explodeRange(ppcImlGenContext, subrangeItr->range); + } + else + assert_dbg(); + + return false; + } + else + { + // range exceeds segment border + // simple but bad solution -> explode the entire range (no longer allow it to cross segment boundaries) + // better solutions: 1) Depending on the situation, we can explode other ranges to resolve the conflict. Thus we should explode the range with the lowest extra cost + // 2) Or we explode the range only partially + // explode the range with the least cost + spillStrategies.explodeRange.cost = INT_MAX; + spillStrategies.explodeRange.range = nullptr; + spillStrategies.explodeRange.distance = -1; + for(auto candidate : livenessTimeline.activeRanges) + { + if (candidate->end.index != RA_INTER_RANGE_END) + continue; + // only select candidates that clash with current subrange + if (candidate->range->physicalRegister < 0 && candidate != subrangeItr) + continue; + + sint32 cost; + cost = PPCRecRARange_estimateAdditionalCostAfterRangeExplode(candidate->range); + // compare with current best candidate for this strategy + if (cost < spillStrategies.explodeRange.cost) + { + spillStrategies.explodeRange.cost = cost; + spillStrategies.explodeRange.distance = INT_MAX; + spillStrategies.explodeRange.range = candidate->range; + } + } + // add current range as a candidate too + sint32 ownCost; + ownCost = PPCRecRARange_estimateAdditionalCostAfterRangeExplode(subrangeItr->range); + if (ownCost < spillStrategies.explodeRange.cost) + { + spillStrategies.explodeRange.cost = ownCost; + spillStrategies.explodeRange.distance = INT_MAX; + spillStrategies.explodeRange.range = subrangeItr->range; + } + if (spillStrategies.explodeRange.cost == INT_MAX) + assert_dbg(); // should not happen + PPCRecRA_explodeRange(ppcImlGenContext, spillStrategies.explodeRange.range); + } + return false; + } + // assign register to range + subrangeItr->range->physicalRegister = physRegSet.GetFirstAvailableReg(); + livenessTimeline.AddActiveRange(subrangeItr); + // next + subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; + } + return true; +} + +void IMLRA_AssignRegisters(IMLRegisterAllocatorContext& ctx, ppcImlGenContext_t* ppcImlGenContext) +{ + // start with frequently executed segments first + sint32 maxLoopDepth = 0; + for (IMLSegment* segIt : ppcImlGenContext->segmentList2) + { + maxLoopDepth = std::max(maxLoopDepth, segIt->loopDepth); + } + // assign fixed registers first + for (IMLSegment* segIt : ppcImlGenContext->segmentList2) + IMLRA_HandleFixedRegisters(ppcImlGenContext, segIt); + + while (true) + { + bool done = false; + for (sint32 d = maxLoopDepth; d >= 0; d--) + { + for (IMLSegment* segIt : ppcImlGenContext->segmentList2) + { + if (segIt->loopDepth != d) + continue; + done = IMLRA_AssignSegmentRegisters(ctx, ppcImlGenContext, segIt); + if (done == false) + break; + } + if (done == false) + break; + } + if (done) + break; + } +} + +struct subrangeEndingInfo_t +{ + //boost::container::small_vector subrangeList2; + raLivenessSubrange_t* subrangeList[SUBRANGE_LIST_SIZE]; + sint32 subrangeCount; + + bool hasUndefinedEndings; +}; + +void _findSubrangeWriteEndings(raLivenessSubrange_t* subrange, uint32 iterationIndex, sint32 depth, subrangeEndingInfo_t* info) +{ + if (depth >= 30) + { + info->hasUndefinedEndings = true; + return; + } + if (subrange->lastIterationIndex == iterationIndex) + return; // already processed + subrange->lastIterationIndex = iterationIndex; + if (subrange->hasStoreDelayed) + return; // no need to traverse this subrange + IMLSegment* imlSegment = subrange->imlSegment; + if (subrange->end.index != RA_INTER_RANGE_END) + { + // ending segment + if (info->subrangeCount >= SUBRANGE_LIST_SIZE) + { + info->hasUndefinedEndings = true; + return; + } + else + { + info->subrangeList[info->subrangeCount] = subrange; + info->subrangeCount++; + } + return; + } + + // traverse next subranges in flow + if (imlSegment->nextSegmentBranchNotTaken) + { + if (subrange->subrangeBranchNotTaken == nullptr) + { + info->hasUndefinedEndings = true; + } + else + { + _findSubrangeWriteEndings(subrange->subrangeBranchNotTaken, iterationIndex, depth + 1, info); + } + } + if (imlSegment->nextSegmentBranchTaken) + { + if (subrange->subrangeBranchTaken == nullptr) + { + info->hasUndefinedEndings = true; + } + else + { + _findSubrangeWriteEndings(subrange->subrangeBranchTaken, iterationIndex, depth + 1, info); + } + } +} + +void _analyzeRangeDataFlow(raLivenessSubrange_t* subrange) +{ + if (subrange->end.index != RA_INTER_RANGE_END) + return; + // analyze data flow across segments (if this segment has writes) + if (subrange->hasStore) + { + subrangeEndingInfo_t writeEndingInfo; + writeEndingInfo.subrangeCount = 0; + writeEndingInfo.hasUndefinedEndings = false; + _findSubrangeWriteEndings(subrange, PPCRecRA_getNextIterationIndex(), 0, &writeEndingInfo); + if (writeEndingInfo.hasUndefinedEndings == false) + { + // get cost of delaying store into endings + sint32 delayStoreCost = 0; + bool alreadyStoredInAllEndings = true; + for (sint32 i = 0; i < writeEndingInfo.subrangeCount; i++) + { + raLivenessSubrange_t* subrangeItr = writeEndingInfo.subrangeList[i]; + if( subrangeItr->hasStore ) + continue; // this ending already stores, no extra cost + alreadyStoredInAllEndings = false; + sint32 storeCost = PPCRecRARange_getReadWriteCost(subrangeItr->imlSegment); + delayStoreCost = std::max(storeCost, delayStoreCost); + } + if (alreadyStoredInAllEndings) + { + subrange->hasStore = false; + subrange->hasStoreDelayed = true; + } + else if (delayStoreCost <= PPCRecRARange_getReadWriteCost(subrange->imlSegment)) + { + subrange->hasStore = false; + subrange->hasStoreDelayed = true; + for (sint32 i = 0; i < writeEndingInfo.subrangeCount; i++) + { + raLivenessSubrange_t* subrangeItr = writeEndingInfo.subrangeList[i]; + subrangeItr->hasStore = true; + } + } + } + } +} + +inline IMLReg _MakeNativeReg(IMLRegFormat baseFormat, IMLRegID regId) +{ + return IMLReg(baseFormat, baseFormat, 0, regId); +} + +void PPCRecRA_insertGPRLoadInstructions(IMLRegisterAllocatorContext& ctx, IMLSegment* imlSegment, sint32 insertIndex, std::span loadList) +{ + PPCRecompiler_pushBackIMLInstructions(imlSegment, insertIndex, loadList.size()); + for (sint32 i = 0; i < loadList.size(); i++) + { + IMLRegFormat baseFormat = ctx.regIdToBaseFormat[loadList[i]->range->virtualRegister]; + cemu_assert_debug(baseFormat != IMLRegFormat::INVALID_FORMAT); + imlSegment->imlList[insertIndex + i].make_r_name(_MakeNativeReg(baseFormat, loadList[i]->range->physicalRegister), loadList[i]->range->name); + } +} + +void PPCRecRA_insertGPRStoreInstructions(IMLRegisterAllocatorContext& ctx, IMLSegment* imlSegment, sint32 insertIndex, std::span storeList) +{ + PPCRecompiler_pushBackIMLInstructions(imlSegment, insertIndex, storeList.size()); + for (size_t i = 0; i < storeList.size(); i++) + { + IMLRegFormat baseFormat = ctx.regIdToBaseFormat[storeList[i]->range->virtualRegister]; + cemu_assert_debug(baseFormat != IMLRegFormat::INVALID_FORMAT); + imlSegment->imlList[insertIndex + i].make_name_r(storeList[i]->range->name, _MakeNativeReg(baseFormat, storeList[i]->range->physicalRegister)); + } +} + +void IMLRA_GenerateSegmentMoveInstructions(IMLRegisterAllocatorContext& ctx, IMLSegment* imlSegment) +{ + std::unordered_map virtId2PhysRegIdMap; // key = virtual register, value = physical register + IMLRALivenessTimeline livenessTimeline; + sint32 index = 0; + sint32 suffixInstructionCount = imlSegment->HasSuffixInstruction() ? 1 : 0; + // load register ranges that are supplied from previous segments + raLivenessSubrange_t* subrangeItr = imlSegment->raInfo.linkedList_allSubranges; + while(subrangeItr) + { + if (subrangeItr->start.index == RA_INTER_RANGE_START) + { + livenessTimeline.AddActiveRange(subrangeItr); +#ifdef CEMU_DEBUG_ASSERT + // load GPR + if (subrangeItr->_noLoad == false) + { + assert_dbg(); + } + // update translation table + cemu_assert_debug(!virtId2PhysRegIdMap.contains(subrangeItr->range->virtualRegister)); +#endif + virtId2PhysRegIdMap.try_emplace(subrangeItr->range->virtualRegister, subrangeItr->range->physicalRegister); + } + // next + subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; + } + // process instructions + while(index < imlSegment->imlList.size() + 1) + { + // expire ranges + livenessTimeline.ExpireRanges(index); + for (auto& expiredRange : livenessTimeline.GetExpiredRanges()) + { + // update translation table + virtId2PhysRegIdMap.erase(expiredRange->range->virtualRegister); + // store GPR if required + // special care has to be taken to execute any stores before the suffix instruction since trailing instructions may not get executed + if (expiredRange->hasStore) + { + PPCRecRA_insertGPRStoreInstructions(ctx, imlSegment, std::min(index, imlSegment->imlList.size() - suffixInstructionCount), {&expiredRange, 1}); + index++; + } + } + + // load new ranges + subrangeItr = imlSegment->raInfo.linkedList_allSubranges; + while(subrangeItr) + { + if (subrangeItr->start.index == index) + { + livenessTimeline.AddActiveRange(subrangeItr); + // load GPR + // similar to stores, any loads for the next segment need to happen before the suffix instruction + // however, ranges that exit the segment at the end but do not cover the suffix instruction are illegal (e.g. RA_INTER_RANGE_END to RA_INTER_RANGE_END subrange) + // this is to prevent the RA from inserting store/load instructions after the suffix instruction + if (imlSegment->HasSuffixInstruction()) + { + cemu_assert_debug(subrangeItr->start.index <= imlSegment->GetSuffixInstructionIndex()); + } + if (subrangeItr->_noLoad == false) + { + PPCRecRA_insertGPRLoadInstructions(ctx, imlSegment, std::min(index, imlSegment->imlList.size() - suffixInstructionCount), {&subrangeItr , 1}); + index++; + subrangeItr->start.index--; + } + // update translation table + virtId2PhysRegIdMap.insert_or_assign(subrangeItr->range->virtualRegister, subrangeItr->range->physicalRegister); + } + subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; + } + // rewrite registers + if (index < imlSegment->imlList.size()) + imlSegment->imlList[index].RewriteGPR(virtId2PhysRegIdMap); + // next iml instruction + index++; + } + // expire infinite subranges (subranges which cross the segment border) + std::vector loadStoreList; + livenessTimeline.ExpireRanges(RA_INTER_RANGE_END); + for (auto liverange : livenessTimeline.GetExpiredRanges()) + { + // update translation table + virtId2PhysRegIdMap.erase(liverange->range->virtualRegister); + // store GPR + if (liverange->hasStore) + loadStoreList.emplace_back(liverange); + } + cemu_assert_debug(livenessTimeline.activeRanges.empty()); + if (!loadStoreList.empty()) + PPCRecRA_insertGPRStoreInstructions(ctx, imlSegment, imlSegment->imlList.size() - suffixInstructionCount, loadStoreList); + // load subranges for next segments + subrangeItr = imlSegment->raInfo.linkedList_allSubranges; + loadStoreList.clear(); + while(subrangeItr) + { + if (subrangeItr->start.index == RA_INTER_RANGE_END) + { + livenessTimeline.AddActiveRange(subrangeItr); + // load GPR + if (subrangeItr->_noLoad == false) + loadStoreList.emplace_back(subrangeItr); + // update translation table + virtId2PhysRegIdMap.try_emplace(subrangeItr->range->virtualRegister, subrangeItr->range->physicalRegister); + } + // next + subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; + } + if (!loadStoreList.empty()) + PPCRecRA_insertGPRLoadInstructions(ctx, imlSegment, imlSegment->imlList.size() - suffixInstructionCount, loadStoreList); +} + +void IMLRA_GenerateMoveInstructions(IMLRegisterAllocatorContext& ctx) +{ + for (size_t s = 0; s < ctx.deprGenContext->segmentList2.size(); s++) + { + IMLSegment* imlSegment = ctx.deprGenContext->segmentList2[s]; + IMLRA_GenerateSegmentMoveInstructions(ctx, imlSegment); + } +} + +void IMLRA_ReshapeForRegisterAllocation(ppcImlGenContext_t* ppcImlGenContext) +{ + // insert empty segments after every non-taken branch if the linked segment has more than one input + // this gives the register allocator more room to create efficient spill code + size_t segmentIndex = 0; + while (segmentIndex < ppcImlGenContext->segmentList2.size()) + { + IMLSegment* imlSegment = ppcImlGenContext->segmentList2[segmentIndex]; + if (imlSegment->nextSegmentIsUncertain) + { + segmentIndex++; + continue; + } + if (imlSegment->nextSegmentBranchTaken == nullptr || imlSegment->nextSegmentBranchNotTaken == nullptr) + { + segmentIndex++; + continue; + } + if (imlSegment->nextSegmentBranchNotTaken->list_prevSegments.size() <= 1) + { + segmentIndex++; + continue; + } + if (imlSegment->nextSegmentBranchNotTaken->isEnterable) + { + segmentIndex++; + continue; + } + PPCRecompilerIml_insertSegments(ppcImlGenContext, segmentIndex + 1, 1); + IMLSegment* imlSegmentP0 = ppcImlGenContext->segmentList2[segmentIndex + 0]; + IMLSegment* imlSegmentP1 = ppcImlGenContext->segmentList2[segmentIndex + 1]; + IMLSegment* nextSegment = imlSegment->nextSegmentBranchNotTaken; + IMLSegment_RemoveLink(imlSegmentP0, nextSegment); + IMLSegment_SetLinkBranchNotTaken(imlSegmentP1, nextSegment); + IMLSegment_SetLinkBranchNotTaken(imlSegmentP0, imlSegmentP1); + segmentIndex++; + } + // detect loops + for (size_t s = 0; s < ppcImlGenContext->segmentList2.size(); s++) + { + IMLSegment* imlSegment = ppcImlGenContext->segmentList2[s]; + imlSegment->momentaryIndex = s; + } + for (size_t s = 0; s < ppcImlGenContext->segmentList2.size(); s++) + { + IMLSegment* imlSegment = ppcImlGenContext->segmentList2[s]; + PPCRecRA_identifyLoop(ppcImlGenContext, imlSegment); + } +} + +IMLRARegAbstractLiveness* _GetAbstractRange(IMLRegisterAllocatorContext& ctx, IMLSegment* imlSegment, IMLRegID regId) +{ + auto& segMap = ctx.GetSegmentAbstractRangeMap(imlSegment); + auto it = segMap.find(regId); + return it != segMap.end() ? &it->second : nullptr; +} + +// scan instructions and establish register usage range for segment +void IMLRA_CalculateSegmentMinMaxAbstractRanges(IMLRegisterAllocatorContext& ctx, IMLSegment* imlSegment) +{ + size_t instructionIndex = 0; + IMLUsedRegisters gprTracking; + auto& segDistMap = ctx.GetSegmentAbstractRangeMap(imlSegment); + while (instructionIndex < imlSegment->imlList.size()) + { + imlSegment->imlList[instructionIndex].CheckRegisterUsage(&gprTracking); + gprTracking.ForEachAccessedGPR([&](IMLReg gprReg, bool isWritten) { + IMLRegID gprId = gprReg.GetRegID(); + auto it = segDistMap.find(gprId); + if (it == segDistMap.end()) + { + segDistMap.try_emplace(gprId, gprReg.GetBaseFormat(), (sint32)instructionIndex, (sint32)instructionIndex + 1); + ctx.regIdToBaseFormat.try_emplace(gprId, gprReg.GetBaseFormat()); + } + else + { + it->second.TrackInstruction(instructionIndex); +#ifdef CEMU_DEBUG_ASSERT + cemu_assert_debug(ctx.regIdToBaseFormat[gprId] == gprReg.GetBaseFormat()); // the base type per register always has to be the same +#endif + } + }); + instructionIndex++; + } +} + +void IMLRA_CalculateLivenessRanges(IMLRegisterAllocatorContext& ctx) +{ + // for each register calculate min/max index of usage range within each segment + size_t dbgIndex = 0; + for (IMLSegment* segIt : ctx.deprGenContext->segmentList2) + { + cemu_assert_debug(segIt->momentaryIndex == dbgIndex); + IMLRA_CalculateSegmentMinMaxAbstractRanges(ctx, segIt); + dbgIndex++; + } +} + +raLivenessSubrange_t* PPCRecRA_convertToMappedRanges(IMLRegisterAllocatorContext& ctx, IMLSegment* imlSegment, sint32 vGPR, raLivenessRange_t* range) +{ + IMLRARegAbstractLiveness* abstractRange = _GetAbstractRange(ctx, imlSegment, vGPR); + if (!abstractRange) + return nullptr; + if (abstractRange->isProcessed) + { + // return already existing segment + raLivenessSubrange_t* existingRange = IMLRA_GetSubrange(imlSegment, vGPR); + cemu_assert_debug(existingRange); + return existingRange; + } + abstractRange->isProcessed = true; + // create subrange +#ifdef CEMU_DEBUG_ASSERT + cemu_assert_debug(IMLRA_GetSubrange(imlSegment, vGPR) == nullptr); +#endif + raLivenessSubrange_t* subrange = PPCRecRA_createSubrange(ctx.deprGenContext, range, imlSegment, abstractRange->usageStart, abstractRange->usageEnd); + // traverse forward + if (abstractRange->usageEnd == RA_INTER_RANGE_END) + { + if (imlSegment->nextSegmentBranchTaken) + { + IMLRARegAbstractLiveness* branchTakenRange = _GetAbstractRange(ctx, imlSegment->nextSegmentBranchTaken, vGPR); + if (branchTakenRange && branchTakenRange->usageStart == RA_INTER_RANGE_START) + { + subrange->subrangeBranchTaken = PPCRecRA_convertToMappedRanges(ctx, imlSegment->nextSegmentBranchTaken, vGPR, range); + cemu_assert_debug(subrange->subrangeBranchTaken->start.index == RA_INTER_RANGE_START); + } + } + if (imlSegment->nextSegmentBranchNotTaken) + { + IMLRARegAbstractLiveness* branchNotTakenRange = _GetAbstractRange(ctx, imlSegment->nextSegmentBranchNotTaken, vGPR); + if (branchNotTakenRange && branchNotTakenRange->usageStart == RA_INTER_RANGE_START) + { + subrange->subrangeBranchNotTaken = PPCRecRA_convertToMappedRanges(ctx, imlSegment->nextSegmentBranchNotTaken, vGPR, range); + cemu_assert_debug(subrange->subrangeBranchNotTaken->start.index == RA_INTER_RANGE_START); + } + } + } + // traverse backward + if (abstractRange->usageStart == RA_INTER_RANGE_START) + { + for (auto& it : imlSegment->list_prevSegments) + { + IMLRARegAbstractLiveness* prevRange = _GetAbstractRange(ctx, it, vGPR); + if(!prevRange) + continue; + if (prevRange->usageEnd == RA_INTER_RANGE_END) + PPCRecRA_convertToMappedRanges(ctx, it, vGPR, range); + } + } + // for subranges which exit the segment at the end there is a hard requirement that they cover the suffix instruction + // this is due to range load instructions being inserted before the suffix instruction + if (subrange->end.index == RA_INTER_RANGE_END) + { + if (imlSegment->HasSuffixInstruction()) + { + cemu_assert_debug(subrange->start.index <= imlSegment->GetSuffixInstructionIndex()); + } + } + return subrange; +} + +// take abstract range data and create LivenessRanges +void IMLRA_ConvertAbstractToLivenessRanges(IMLRegisterAllocatorContext& ctx, IMLSegment* imlSegment) +{ + // convert abstract min-max ranges to liveness range objects + auto& segMap = ctx.GetSegmentAbstractRangeMap(imlSegment); + for (auto& it : segMap) + { + if(it.second.isProcessed) + continue; + IMLRegID regId = it.first; + raLivenessRange_t* range = PPCRecRA_createRangeBase(ctx.deprGenContext, regId, ctx.raParam->regIdToName.find(regId)->second); + PPCRecRA_convertToMappedRanges(ctx, imlSegment, regId, range); + } + // fill created ranges with read/write location indices + // note that at this point there is only one range per register per segment + // and the algorithm below relies on this + const std::unordered_map& regToSubrange = IMLRA_GetSubrangeMap(imlSegment); + size_t index = 0; + IMLUsedRegisters gprTracking; + while (index < imlSegment->imlList.size()) + { + imlSegment->imlList[index].CheckRegisterUsage(&gprTracking); + gprTracking.ForEachAccessedGPR([&](IMLReg gprReg, bool isWritten) { + IMLRegID gprId = gprReg.GetRegID(); + raLivenessSubrange_t* subrange = regToSubrange.find(gprId)->second; + PPCRecRA_updateOrAddSubrangeLocation(subrange, index, !isWritten, isWritten); +#ifdef CEMU_DEBUG_ASSERT + if ((sint32)index < subrange->start.index) + { + IMLRARegAbstractLiveness* dbgAbstractRange = _GetAbstractRange(ctx, imlSegment, gprId); + assert_dbg(); + } + if ((sint32)index + 1 > subrange->end.index) + assert_dbg(); +#endif + }); + index++; + } +} + +void IMLRA_extendAbstractRangeToEndOfSegment(IMLRegisterAllocatorContext& ctx, IMLSegment* imlSegment, IMLRegID regId) +{ + auto& segDistMap = ctx.GetSegmentAbstractRangeMap(imlSegment); + auto it = segDistMap.find(regId); + if (it == segDistMap.end()) + { + sint32 startIndex; + if(imlSegment->HasSuffixInstruction()) + startIndex = imlSegment->GetSuffixInstructionIndex(); + else + startIndex = RA_INTER_RANGE_END; + segDistMap.try_emplace((IMLRegID)regId, IMLRegFormat::INVALID_FORMAT, startIndex, RA_INTER_RANGE_END); + } + else + { + it->second.usageEnd = RA_INTER_RANGE_END; + } +} + +void IMLRA_extendAbstractRangeToBeginningOfSegment(IMLRegisterAllocatorContext& ctx, IMLSegment* imlSegment, IMLRegID regId) +{ + auto& segDistMap = ctx.GetSegmentAbstractRangeMap(imlSegment); + auto it = segDistMap.find(regId); + if (it == segDistMap.end()) + { + segDistMap.try_emplace((IMLRegID)regId, IMLRegFormat::INVALID_FORMAT, RA_INTER_RANGE_START, RA_INTER_RANGE_START); + } + else + { + it->second.usageStart = RA_INTER_RANGE_START; + } + // propagate backwards + for (auto& it : imlSegment->list_prevSegments) + { + IMLRA_extendAbstractRangeToEndOfSegment(ctx, it, regId); + } +} + +void IMLRA_connectAbstractRanges(IMLRegisterAllocatorContext& ctx, IMLRegID regId, IMLSegment** route, sint32 routeDepth) +{ +#ifdef CEMU_DEBUG_ASSERT + if (routeDepth < 2) + assert_dbg(); +#endif + // extend starting range to end of segment + IMLRA_extendAbstractRangeToEndOfSegment(ctx, route[0], regId); + // extend all the connecting segments in both directions + for (sint32 i = 1; i < (routeDepth - 1); i++) + { + IMLRA_extendAbstractRangeToEndOfSegment(ctx, route[i], regId); + IMLRA_extendAbstractRangeToBeginningOfSegment(ctx, route[i], regId); + } + // extend the final segment towards the beginning + IMLRA_extendAbstractRangeToBeginningOfSegment(ctx, route[routeDepth - 1], regId); +} + +void _IMLRA_checkAndTryExtendRange(IMLRegisterAllocatorContext& ctx, IMLSegment* currentSegment, IMLRegID regID, sint32 distanceLeft, IMLSegment** route, sint32 routeDepth) +{ + if (routeDepth >= 64) + { + cemuLog_log(LogType::Recompiler, "Recompiler RA route maximum depth exceeded\n"); + return; + } + route[routeDepth] = currentSegment; + + IMLRARegAbstractLiveness* range = _GetAbstractRange(ctx, currentSegment, regID); + + if (!range) + { + // measure distance over entire segment + distanceLeft -= (sint32)currentSegment->imlList.size(); + if (distanceLeft > 0) + { + if (currentSegment->nextSegmentBranchNotTaken) + _IMLRA_checkAndTryExtendRange(ctx, currentSegment->nextSegmentBranchNotTaken, regID, distanceLeft, route, routeDepth + 1); + if (currentSegment->nextSegmentBranchTaken) + _IMLRA_checkAndTryExtendRange(ctx, currentSegment->nextSegmentBranchTaken, regID, distanceLeft, route, routeDepth + 1); + } + return; + } + else + { + // measure distance to range + if (range->usageStart == RA_INTER_RANGE_END) + { + if (distanceLeft < (sint32)currentSegment->imlList.size()) + return; // range too far away + } + else if (range->usageStart != RA_INTER_RANGE_START && range->usageStart > distanceLeft) + return; // out of range + // found close range -> connect ranges + IMLRA_connectAbstractRanges(ctx, regID, route, routeDepth + 1); + } +} + +void PPCRecRA_checkAndTryExtendRange(IMLRegisterAllocatorContext& ctx, IMLSegment* currentSegment, IMLRARegAbstractLiveness* range, IMLRegID regID) +{ + cemu_assert_debug(range->usageEnd >= 0); + // count instructions to end of initial segment + sint32 instructionsUntilEndOfSeg; + if (range->usageEnd == RA_INTER_RANGE_END) + instructionsUntilEndOfSeg = 0; + else + instructionsUntilEndOfSeg = (sint32)currentSegment->imlList.size() - range->usageEnd; + cemu_assert_debug(instructionsUntilEndOfSeg >= 0); + sint32 remainingScanDist = 45 - instructionsUntilEndOfSeg; + if (remainingScanDist <= 0) + return; // can't reach end + + IMLSegment* route[64]; + route[0] = currentSegment; + if (currentSegment->nextSegmentBranchNotTaken) + _IMLRA_checkAndTryExtendRange(ctx, currentSegment->nextSegmentBranchNotTaken, regID, remainingScanDist, route, 1); + if (currentSegment->nextSegmentBranchTaken) + _IMLRA_checkAndTryExtendRange(ctx, currentSegment->nextSegmentBranchTaken, regID, remainingScanDist, route, 1); +} + +void PPCRecRA_mergeCloseRangesForSegmentV2(IMLRegisterAllocatorContext& ctx, IMLSegment* imlSegment) +{ + auto& segMap = ctx.GetSegmentAbstractRangeMap(imlSegment); + for (auto& it : segMap) + { + PPCRecRA_checkAndTryExtendRange(ctx, imlSegment, &(it.second), it.first); + } +#ifdef CEMU_DEBUG_ASSERT + if (imlSegment->list_prevSegments.empty() == false && imlSegment->isEnterable) + assert_dbg(); + if ((imlSegment->nextSegmentBranchNotTaken != nullptr || imlSegment->nextSegmentBranchTaken != nullptr) && imlSegment->nextSegmentIsUncertain) + assert_dbg(); +#endif +} + +void PPCRecRA_followFlowAndExtendRanges(IMLRegisterAllocatorContext& ctx, IMLSegment* imlSegment) +{ + std::vector list_segments; + std::vector list_processedSegment; + size_t segmentCount = ctx.deprGenContext->segmentList2.size(); + list_segments.reserve(segmentCount+1); + list_processedSegment.resize(segmentCount); + + auto markSegProcessed = [&list_processedSegment](IMLSegment* seg) {list_processedSegment[seg->momentaryIndex] = true; }; + auto isSegProcessed = [&list_processedSegment](IMLSegment* seg) -> bool { return list_processedSegment[seg->momentaryIndex]; }; + markSegProcessed(imlSegment); + + sint32 index = 0; + list_segments.push_back(imlSegment); + while (index < list_segments.size()) + { + IMLSegment* currentSegment = list_segments[index]; + PPCRecRA_mergeCloseRangesForSegmentV2(ctx, currentSegment); + // follow flow + if (currentSegment->nextSegmentBranchNotTaken && !isSegProcessed(currentSegment->nextSegmentBranchNotTaken)) + { + markSegProcessed(currentSegment->nextSegmentBranchNotTaken); + list_segments.push_back(currentSegment->nextSegmentBranchNotTaken); + } + if (currentSegment->nextSegmentBranchTaken && !isSegProcessed(currentSegment->nextSegmentBranchTaken)) + { + markSegProcessed(currentSegment->nextSegmentBranchTaken); + list_segments.push_back(currentSegment->nextSegmentBranchTaken); + } + index++; + } +} + +void IMLRA_mergeCloseAbstractRanges(IMLRegisterAllocatorContext& ctx) +{ + for (size_t s = 0; s < ctx.deprGenContext->segmentList2.size(); s++) + { + IMLSegment* imlSegment = ctx.deprGenContext->segmentList2[s]; + if (!imlSegment->list_prevSegments.empty()) + continue; // not an entry/standalone segment + PPCRecRA_followFlowAndExtendRanges(ctx, imlSegment); + } +} + +void IMLRA_extendAbstracRangesOutOfLoops(IMLRegisterAllocatorContext& ctx) +{ + for (size_t s = 0; s < ctx.deprGenContext->segmentList2.size(); s++) + { + IMLSegment* imlSegment = ctx.deprGenContext->segmentList2[s]; + auto localLoopDepth = imlSegment->loopDepth; + if (localLoopDepth <= 0) + continue; // not inside a loop + // look for loop exit + bool hasLoopExit = false; + if (imlSegment->nextSegmentBranchTaken && imlSegment->nextSegmentBranchTaken->loopDepth < localLoopDepth) + { + hasLoopExit = true; + } + if (imlSegment->nextSegmentBranchNotTaken && imlSegment->nextSegmentBranchNotTaken->loopDepth < localLoopDepth) + { + hasLoopExit = true; + } + if (hasLoopExit == false) + continue; + + // extend looping ranges into all exits (this allows the data flow analyzer to move stores out of the loop) + auto& segMap = ctx.GetSegmentAbstractRangeMap(imlSegment); + for (auto& it : segMap) + { + if(it.second.usageEnd != RA_INTER_RANGE_END) + continue; + if (imlSegment->nextSegmentBranchTaken) + IMLRA_extendAbstractRangeToBeginningOfSegment(ctx, imlSegment->nextSegmentBranchTaken, it.first); + if (imlSegment->nextSegmentBranchNotTaken) + IMLRA_extendAbstractRangeToBeginningOfSegment(ctx, imlSegment->nextSegmentBranchNotTaken, it.first); + } + } +} + +void IMLRA_ProcessFlowAndCalculateLivenessRanges(IMLRegisterAllocatorContext& ctx) +{ + IMLRA_mergeCloseAbstractRanges(ctx); + // extra pass to move register stores out of loops + IMLRA_extendAbstracRangesOutOfLoops(ctx); + // calculate liveness ranges + for (auto& segIt : ctx.deprGenContext->segmentList2) + IMLRA_ConvertAbstractToLivenessRanges(ctx, segIt); +} + +void PPCRecRA_analyzeSubrangeDataDependencyV2(raLivenessSubrange_t* subrange) +{ + bool isRead = false; + bool isWritten = false; + bool isOverwritten = false; + for (auto& location : subrange->list_locations) + { + if (location.isRead) + { + isRead = true; + } + if (location.isWrite) + { + if (isRead == false) + isOverwritten = true; + isWritten = true; + } + } + subrange->_noLoad = isOverwritten; + subrange->hasStore = isWritten; + + if (subrange->start.index == RA_INTER_RANGE_START) + subrange->_noLoad = true; +} + +void IMLRA_AnalyzeRangeDataFlow(ppcImlGenContext_t* ppcImlGenContext) +{ + // this function is called after _assignRegisters(), which means that all ranges are already final and wont change anymore + // first do a per-subrange pass + for (auto& range : ppcImlGenContext->raInfo.list_ranges) + { + for (auto& subrange : range->list_subranges) + { + PPCRecRA_analyzeSubrangeDataDependencyV2(subrange); + } + } + // then do a second pass where we scan along subrange flow + for (auto& range : ppcImlGenContext->raInfo.list_ranges) + { + for (auto& subrange : range->list_subranges) // todo - traversing this backwards should be faster and yield better results due to the nature of the algorithm + { + _analyzeRangeDataFlow(subrange); + } + } +} + +void IMLRegisterAllocator_AllocateRegisters(ppcImlGenContext_t* ppcImlGenContext, IMLRegisterAllocatorParameters& raParam) +{ + IMLRegisterAllocatorContext ctx; + ctx.raParam = &raParam; + ctx.deprGenContext = ppcImlGenContext; + + IMLRA_ReshapeForRegisterAllocation(ppcImlGenContext); + + ppcImlGenContext->UpdateSegmentIndices(); // update momentaryIndex of each segment + + ppcImlGenContext->raInfo.list_ranges = std::vector(); + + ctx.perSegmentAbstractRanges.resize(ppcImlGenContext->segmentList2.size()); + + IMLRA_CalculateLivenessRanges(ctx); + IMLRA_ProcessFlowAndCalculateLivenessRanges(ctx); + IMLRA_AssignRegisters(ctx, ppcImlGenContext); + + IMLRA_AnalyzeRangeDataFlow(ppcImlGenContext); + IMLRA_GenerateMoveInstructions(ctx); + + PPCRecRA_deleteAllRanges(ppcImlGenContext); +} diff --git a/src/Cafe/HW/Espresso/Recompiler/IML/IMLRegisterAllocator.h b/src/Cafe/HW/Espresso/Recompiler/IML/IMLRegisterAllocator.h new file mode 100644 index 00000000..52b20397 --- /dev/null +++ b/src/Cafe/HW/Espresso/Recompiler/IML/IMLRegisterAllocator.h @@ -0,0 +1,100 @@ + +// container for storing a set of register indices +// specifically optimized towards storing physical register indices (expected to be below 64) +class IMLPhysRegisterSet +{ +public: + void SetAvailable(uint32 index) + { + cemu_assert_debug(index < 64); + m_regBitmask |= ((uint64)1 << index); + } + + void SetReserved(uint32 index) + { + cemu_assert_debug(index < 64); + m_regBitmask &= ~((uint64)1 << index); + } + + bool IsAvailable(uint32 index) const + { + return (m_regBitmask & (1 << index)) != 0; + } + + IMLPhysRegisterSet& operator&=(const IMLPhysRegisterSet& other) + { + this->m_regBitmask &= other.m_regBitmask; + return *this; + } + + IMLPhysRegisterSet& operator=(const IMLPhysRegisterSet& other) + { + this->m_regBitmask = other.m_regBitmask; + return *this; + } + + bool HasAnyAvailable() const + { + return m_regBitmask != 0; + } + + // returns index of first available register. Do not call when HasAnyAvailable() == false + uint32 GetFirstAvailableReg() + { + cemu_assert_debug(m_regBitmask != 0); + uint32 regIndex = 0; + auto tmp = m_regBitmask; + while ((tmp & 0xFF) == 0) + { + regIndex += 8; + tmp >>= 8; + } + while ((tmp & 0x1) == 0) + { + regIndex++; + tmp >>= 1; + } + return regIndex; + } + + // returns index of next available register (search includes any register index >= startIndex) + // returns -1 if there is no more register + sint32 GetNextAvailableReg(sint32 startIndex) + { + if (startIndex >= 64) + return -1; + uint32 regIndex = startIndex; + auto tmp = m_regBitmask; + tmp >>= regIndex; + if (!tmp) + return -1; + while ((tmp & 0xFF) == 0) + { + regIndex += 8; + tmp >>= 8; + } + while ((tmp & 0x1) == 0) + { + regIndex++; + tmp >>= 1; + } + return regIndex; + } + +private: + uint64 m_regBitmask{ 0 }; +}; + + +struct IMLRegisterAllocatorParameters +{ + inline IMLPhysRegisterSet& GetPhysRegPool(IMLRegFormat regFormat) + { + return perTypePhysPool[stdx::to_underlying(regFormat)]; + } + + IMLPhysRegisterSet perTypePhysPool[stdx::to_underlying(IMLRegFormat::TYPE_COUNT)]; + std::unordered_map regIdToName; +}; + +void IMLRegisterAllocator_AllocateRegisters(ppcImlGenContext_t* ppcImlGenContext, IMLRegisterAllocatorParameters& raParam); \ No newline at end of file diff --git a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlRanges.cpp b/src/Cafe/HW/Espresso/Recompiler/IML/IMLRegisterAllocatorRanges.cpp similarity index 84% rename from src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlRanges.cpp rename to src/Cafe/HW/Espresso/Recompiler/IML/IMLRegisterAllocatorRanges.cpp index d31c02d4..f722e7ca 100644 --- a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlRanges.cpp +++ b/src/Cafe/HW/Espresso/Recompiler/IML/IMLRegisterAllocatorRanges.cpp @@ -1,20 +1,26 @@ -#include "PPCRecompiler.h" -#include "PPCRecompilerIml.h" -#include "PPCRecompilerX64.h" -#include "PPCRecompilerImlRanges.h" +#include "../PPCRecompiler.h" +#include "../PPCRecompilerIml.h" +#include "IMLRegisterAllocatorRanges.h" #include "util/helpers/MemoryPool.h" -void PPCRecRARange_addLink_perVirtualGPR(raLivenessSubrange_t** root, raLivenessSubrange_t* subrange) +void PPCRecRARange_addLink_perVirtualGPR(std::unordered_map& root, raLivenessSubrange_t* subrange) { -#ifdef CEMU_DEBUG_ASSERT - if ((*root) && (*root)->range->virtualRegister != subrange->range->virtualRegister) - assert_dbg(); -#endif - subrange->link_sameVirtualRegisterGPR.next = *root; - if (*root) - (*root)->link_sameVirtualRegisterGPR.prev = subrange; - subrange->link_sameVirtualRegisterGPR.prev = nullptr; - *root = subrange; + IMLRegID regId = subrange->range->virtualRegister; + auto it = root.find(regId); + if (it == root.end()) + { + // new single element + root.try_emplace(regId, subrange); + subrange->link_sameVirtualRegisterGPR.prev = nullptr; + subrange->link_sameVirtualRegisterGPR.next = nullptr; + } + else + { + // insert in first position + subrange->link_sameVirtualRegisterGPR.next = it->second; + it->second = subrange; + subrange->link_sameVirtualRegisterGPR.prev = subrange; + } } void PPCRecRARange_addLink_allSubrangesGPR(raLivenessSubrange_t** root, raLivenessSubrange_t* subrange) @@ -26,15 +32,28 @@ void PPCRecRARange_addLink_allSubrangesGPR(raLivenessSubrange_t** root, raLivene *root = subrange; } -void PPCRecRARange_removeLink_perVirtualGPR(raLivenessSubrange_t** root, raLivenessSubrange_t* subrange) +void PPCRecRARange_removeLink_perVirtualGPR(std::unordered_map& root, raLivenessSubrange_t* subrange) { - raLivenessSubrange_t* tempPrev = subrange->link_sameVirtualRegisterGPR.prev; - if (subrange->link_sameVirtualRegisterGPR.prev) - subrange->link_sameVirtualRegisterGPR.prev->link_sameVirtualRegisterGPR.next = subrange->link_sameVirtualRegisterGPR.next; - else - (*root) = subrange->link_sameVirtualRegisterGPR.next; - if (subrange->link_sameVirtualRegisterGPR.next) - subrange->link_sameVirtualRegisterGPR.next->link_sameVirtualRegisterGPR.prev = tempPrev; + IMLRegID regId = subrange->range->virtualRegister; + raLivenessSubrange_t* nextRange = subrange->link_sameVirtualRegisterGPR.next; + raLivenessSubrange_t* prevRange = subrange->link_sameVirtualRegisterGPR.prev; + raLivenessSubrange_t* newBase = prevRange ? prevRange : nextRange; + if (prevRange) + prevRange->link_sameVirtualRegisterGPR.next = subrange->link_sameVirtualRegisterGPR.next; + if (nextRange) + nextRange->link_sameVirtualRegisterGPR.prev = subrange->link_sameVirtualRegisterGPR.prev; + + if (!prevRange) + { + if (nextRange) + { + root.find(regId)->second = nextRange; + } + else + { + root.erase(regId); + } + } #ifdef CEMU_DEBUG_ASSERT subrange->link_sameVirtualRegisterGPR.prev = (raLivenessSubrange_t*)1; subrange->link_sameVirtualRegisterGPR.next = (raLivenessSubrange_t*)1; @@ -70,7 +89,7 @@ raLivenessRange_t* PPCRecRA_createRangeBase(ppcImlGenContext_t* ppcImlGenContext return livenessRange; } -raLivenessSubrange_t* PPCRecRA_createSubrange(ppcImlGenContext_t* ppcImlGenContext, raLivenessRange_t* range, PPCRecImlSegment_t* imlSegment, sint32 startIndex, sint32 endIndex) +raLivenessSubrange_t* PPCRecRA_createSubrange(ppcImlGenContext_t* ppcImlGenContext, raLivenessRange_t* range, IMLSegment* imlSegment, sint32 startIndex, sint32 endIndex) { raLivenessSubrange_t* livenessSubrange = memPool_livenessSubrange.acquireObj(); livenessSubrange->list_locations.resize(0); @@ -88,15 +107,15 @@ raLivenessSubrange_t* PPCRecRA_createSubrange(ppcImlGenContext_t* ppcImlGenConte // add to range range->list_subranges.push_back(livenessSubrange); // add to segment - PPCRecRARange_addLink_perVirtualGPR(&(imlSegment->raInfo.linkedList_perVirtualGPR[range->virtualRegister]), livenessSubrange); + PPCRecRARange_addLink_perVirtualGPR(imlSegment->raInfo.linkedList_perVirtualGPR2, livenessSubrange); PPCRecRARange_addLink_allSubrangesGPR(&imlSegment->raInfo.linkedList_allSubranges, livenessSubrange); return livenessSubrange; } void _unlinkSubrange(raLivenessSubrange_t* subrange) { - PPCRecImlSegment_t* imlSegment = subrange->imlSegment; - PPCRecRARange_removeLink_perVirtualGPR(&imlSegment->raInfo.linkedList_perVirtualGPR[subrange->range->virtualRegister], subrange); + IMLSegment* imlSegment = subrange->imlSegment; + PPCRecRARange_removeLink_perVirtualGPR(imlSegment->raInfo.linkedList_perVirtualGPR2, subrange); PPCRecRARange_removeLink_allSubrangesGPR(&imlSegment->raInfo.linkedList_allSubranges, subrange); } @@ -226,9 +245,9 @@ void PPCRecRA_debugValidateSubrange(raLivenessSubrange_t* subrange) {} #endif // split subrange at the given index -// After the split there will be two ranges/subranges: -// head -> subrange is shortned to end at splitIndex -// tail -> a new subrange that reaches from splitIndex to the end of the original subrange +// After the split there will be two ranges and subranges: +// head -> subrange is shortened to end at splitIndex (exclusive) +// tail -> a new subrange that ranges from splitIndex (inclusive) to the end of the original subrange // if head has a physical register assigned it will not carry over to tail // The return value is the tail subrange // If trimToHole is true, the end of the head subrange and the start of the tail subrange will be moved to fit the locations @@ -237,7 +256,9 @@ raLivenessSubrange_t* PPCRecRA_splitLocalSubrange(ppcImlGenContext_t* ppcImlGenC { // validation #ifdef CEMU_DEBUG_ASSERT - if (subrange->end.index == RA_INTER_RANGE_END || subrange->end.index == RA_INTER_RANGE_START) + //if (subrange->end.index == RA_INTER_RANGE_END || subrange->end.index == RA_INTER_RANGE_START) + // assert_dbg(); + if (subrange->start.index == RA_INTER_RANGE_END || subrange->end.index == RA_INTER_RANGE_START) assert_dbg(); if (subrange->start.index >= splitIndex) assert_dbg(); @@ -283,6 +304,11 @@ raLivenessSubrange_t* PPCRecRA_splitLocalSubrange(ppcImlGenContext_t* ppcImlGenC tailSubrange->start.index = tailSubrange->list_locations.front().index; } } + else + { + // set head range to end at split index + subrange->end.index = splitIndex; + } return tailSubrange; } @@ -306,7 +332,7 @@ void PPCRecRA_updateOrAddSubrangeLocation(raLivenessSubrange_t* subrange, sint32 subrange->list_locations.emplace_back(index, isRead, isWrite); } -sint32 PPCRecRARange_getReadWriteCost(PPCRecImlSegment_t* imlSegment) +sint32 PPCRecRARange_getReadWriteCost(IMLSegment* imlSegment) { sint32 v = imlSegment->loopDepth + 1; v *= 5; @@ -397,3 +423,4 @@ sint32 PPCRecRARange_estimateAdditionalCostAfterSplit(raLivenessSubrange_t* subr return cost; } + diff --git a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlRanges.h b/src/Cafe/HW/Espresso/Recompiler/IML/IMLRegisterAllocatorRanges.h similarity index 89% rename from src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlRanges.h rename to src/Cafe/HW/Espresso/Recompiler/IML/IMLRegisterAllocatorRanges.h index 01970bbf..28fbe906 100644 --- a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlRanges.h +++ b/src/Cafe/HW/Espresso/Recompiler/IML/IMLRegisterAllocatorRanges.h @@ -1,7 +1,7 @@ #pragma once raLivenessRange_t* PPCRecRA_createRangeBase(ppcImlGenContext_t* ppcImlGenContext, uint32 virtualRegister, uint32 name); -raLivenessSubrange_t* PPCRecRA_createSubrange(ppcImlGenContext_t* ppcImlGenContext, raLivenessRange_t* range, PPCRecImlSegment_t* imlSegment, sint32 startIndex, sint32 endIndex); +raLivenessSubrange_t* PPCRecRA_createSubrange(ppcImlGenContext_t* ppcImlGenContext, raLivenessRange_t* range, IMLSegment* imlSegment, sint32 startIndex, sint32 endIndex); void PPCRecRA_deleteSubrange(ppcImlGenContext_t* ppcImlGenContext, raLivenessSubrange_t* subrange); void PPCRecRA_deleteRange(ppcImlGenContext_t* ppcImlGenContext, raLivenessRange_t* range); void PPCRecRA_deleteAllRanges(ppcImlGenContext_t* ppcImlGenContext); @@ -17,7 +17,7 @@ void PPCRecRA_updateOrAddSubrangeLocation(raLivenessSubrange_t* subrange, sint32 void PPCRecRA_debugValidateSubrange(raLivenessSubrange_t* subrange); // cost estimation -sint32 PPCRecRARange_getReadWriteCost(PPCRecImlSegment_t* imlSegment); +sint32 PPCRecRARange_getReadWriteCost(IMLSegment* imlSegment); sint32 PPCRecRARange_estimateCost(raLivenessRange_t* range); sint32 PPCRecRARange_estimateAdditionalCostAfterRangeExplode(raLivenessRange_t* range); sint32 PPCRecRARange_estimateAdditionalCostAfterSplit(raLivenessSubrange_t* subrange, sint32 splitIndex); diff --git a/src/Cafe/HW/Espresso/Recompiler/IML/IMLSegment.cpp b/src/Cafe/HW/Espresso/Recompiler/IML/IMLSegment.cpp new file mode 100644 index 00000000..f3b6834f --- /dev/null +++ b/src/Cafe/HW/Espresso/Recompiler/IML/IMLSegment.cpp @@ -0,0 +1,133 @@ +#include "IMLInstruction.h" +#include "IMLSegment.h" + +void IMLSegment::SetEnterable(uint32 enterAddress) +{ + cemu_assert_debug(!isEnterable || enterPPCAddress == enterAddress); + isEnterable = true; + enterPPCAddress = enterAddress; +} + +bool IMLSegment::HasSuffixInstruction() const +{ + if (imlList.empty()) + return false; + const IMLInstruction& imlInstruction = imlList.back(); + return imlInstruction.IsSuffixInstruction(); +} + +sint32 IMLSegment::GetSuffixInstructionIndex() const +{ + cemu_assert_debug(HasSuffixInstruction()); + return (sint32)(imlList.size() - 1); +} + +IMLInstruction* IMLSegment::GetLastInstruction() +{ + if (imlList.empty()) + return nullptr; + return &imlList.back(); +} + +void IMLSegment::SetLinkBranchNotTaken(IMLSegment* imlSegmentDst) +{ + if (nextSegmentBranchNotTaken) + nextSegmentBranchNotTaken->list_prevSegments.erase(std::find(nextSegmentBranchNotTaken->list_prevSegments.begin(), nextSegmentBranchNotTaken->list_prevSegments.end(), this)); + nextSegmentBranchNotTaken = imlSegmentDst; + if(imlSegmentDst) + imlSegmentDst->list_prevSegments.push_back(this); +} + +void IMLSegment::SetLinkBranchTaken(IMLSegment* imlSegmentDst) +{ + if (nextSegmentBranchTaken) + nextSegmentBranchTaken->list_prevSegments.erase(std::find(nextSegmentBranchTaken->list_prevSegments.begin(), nextSegmentBranchTaken->list_prevSegments.end(), this)); + nextSegmentBranchTaken = imlSegmentDst; + if (imlSegmentDst) + imlSegmentDst->list_prevSegments.push_back(this); +} + +IMLInstruction* IMLSegment::AppendInstruction() +{ + IMLInstruction& inst = imlList.emplace_back(); + memset(&inst, 0, sizeof(IMLInstruction)); + return &inst; +} + +void IMLSegment_SetLinkBranchNotTaken(IMLSegment* imlSegmentSrc, IMLSegment* imlSegmentDst) +{ + // make sure segments aren't already linked + if (imlSegmentSrc->nextSegmentBranchNotTaken == imlSegmentDst) + return; + // add as next segment for source + if (imlSegmentSrc->nextSegmentBranchNotTaken != nullptr) + assert_dbg(); + imlSegmentSrc->nextSegmentBranchNotTaken = imlSegmentDst; + // add as previous segment for destination + imlSegmentDst->list_prevSegments.push_back(imlSegmentSrc); +} + +void IMLSegment_SetLinkBranchTaken(IMLSegment* imlSegmentSrc, IMLSegment* imlSegmentDst) +{ + // make sure segments aren't already linked + if (imlSegmentSrc->nextSegmentBranchTaken == imlSegmentDst) + return; + // add as next segment for source + if (imlSegmentSrc->nextSegmentBranchTaken != nullptr) + assert_dbg(); + imlSegmentSrc->nextSegmentBranchTaken = imlSegmentDst; + // add as previous segment for destination + imlSegmentDst->list_prevSegments.push_back(imlSegmentSrc); +} + +void IMLSegment_RemoveLink(IMLSegment* imlSegmentSrc, IMLSegment* imlSegmentDst) +{ + if (imlSegmentSrc->nextSegmentBranchNotTaken == imlSegmentDst) + { + imlSegmentSrc->nextSegmentBranchNotTaken = nullptr; + } + else if (imlSegmentSrc->nextSegmentBranchTaken == imlSegmentDst) + { + imlSegmentSrc->nextSegmentBranchTaken = nullptr; + } + else + assert_dbg(); + + bool matchFound = false; + for (sint32 i = 0; i < imlSegmentDst->list_prevSegments.size(); i++) + { + if (imlSegmentDst->list_prevSegments[i] == imlSegmentSrc) + { + imlSegmentDst->list_prevSegments.erase(imlSegmentDst->list_prevSegments.begin() + i); + matchFound = true; + break; + } + } + if (matchFound == false) + assert_dbg(); +} + +/* + * Replaces all links to segment orig with linkts to segment new + */ +void IMLSegment_RelinkInputSegment(IMLSegment* imlSegmentOrig, IMLSegment* imlSegmentNew) +{ + while (imlSegmentOrig->list_prevSegments.size() != 0) + { + IMLSegment* prevSegment = imlSegmentOrig->list_prevSegments[0]; + if (prevSegment->nextSegmentBranchNotTaken == imlSegmentOrig) + { + IMLSegment_RemoveLink(prevSegment, imlSegmentOrig); + IMLSegment_SetLinkBranchNotTaken(prevSegment, imlSegmentNew); + } + else if (prevSegment->nextSegmentBranchTaken == imlSegmentOrig) + { + IMLSegment_RemoveLink(prevSegment, imlSegmentOrig); + IMLSegment_SetLinkBranchTaken(prevSegment, imlSegmentNew); + } + else + { + assert_dbg(); + } + } +} diff --git a/src/Cafe/HW/Espresso/Recompiler/IML/IMLSegment.h b/src/Cafe/HW/Espresso/Recompiler/IML/IMLSegment.h new file mode 100644 index 00000000..bf1868cf --- /dev/null +++ b/src/Cafe/HW/Espresso/Recompiler/IML/IMLSegment.h @@ -0,0 +1,119 @@ +#pragma once +#include "IMLInstruction.h" + +struct IMLSegmentPoint +{ + sint32 index; + struct IMLSegment* imlSegment; + IMLSegmentPoint* next; + IMLSegmentPoint* prev; +}; + +struct raLivenessLocation_t +{ + sint32 index; + bool isRead; + bool isWrite; + + raLivenessLocation_t() = default; + + raLivenessLocation_t(sint32 index, bool isRead, bool isWrite) + : index(index), isRead(isRead), isWrite(isWrite) {}; +}; + +struct raLivenessSubrangeLink_t +{ + struct raLivenessSubrange_t* prev; + struct raLivenessSubrange_t* next; +}; + +struct raLivenessSubrange_t +{ + struct raLivenessRange_t* range; + IMLSegment* imlSegment; + IMLSegmentPoint start; + IMLSegmentPoint end; + // dirty state tracking + bool _noLoad; + bool hasStore; + bool hasStoreDelayed; + // next + raLivenessSubrange_t* subrangeBranchTaken; + raLivenessSubrange_t* subrangeBranchNotTaken; + // processing + uint32 lastIterationIndex; + // instruction locations + std::vector list_locations; + // linked list (subranges with same GPR virtual register) + raLivenessSubrangeLink_t link_sameVirtualRegisterGPR; + // linked list (all subranges for this segment) + raLivenessSubrangeLink_t link_segmentSubrangesGPR; +}; + +struct raLivenessRange_t +{ + IMLRegID virtualRegister; + sint32 physicalRegister; + IMLName name; + std::vector list_subranges; +}; + +struct PPCSegmentRegisterAllocatorInfo_t +{ + // used during loop detection + bool isPartOfProcessedLoop{}; + sint32 lastIterationIndex{}; + // linked lists + raLivenessSubrange_t* linkedList_allSubranges{}; + std::unordered_map linkedList_perVirtualGPR2; +}; + +struct IMLSegment +{ + sint32 momentaryIndex{}; // index in segment list, generally not kept up to date except if needed (necessary for loop detection) + sint32 loopDepth{}; + uint32 ppcAddress{}; // ppc address (0xFFFFFFFF if not associated with an address) + uint32 x64Offset{}; // x64 code offset of segment start + // list of intermediate instructions in this segment + std::vector imlList; + // segment link + IMLSegment* nextSegmentBranchNotTaken{}; // this is also the default for segments where there is no branch + IMLSegment* nextSegmentBranchTaken{}; + bool nextSegmentIsUncertain{}; + std::vector list_prevSegments{}; + // enterable segments + bool isEnterable{}; // this segment can be entered from outside the recompiler (no preloaded registers necessary) + uint32 enterPPCAddress{}; // used if isEnterable is true + // register allocator info + PPCSegmentRegisterAllocatorInfo_t raInfo{}; + // segment state API + void SetEnterable(uint32 enterAddress); + void SetLinkBranchNotTaken(IMLSegment* imlSegmentDst); + void SetLinkBranchTaken(IMLSegment* imlSegmentDst); + + IMLSegment* GetBranchTaken() + { + return nextSegmentBranchTaken; + } + + IMLSegment* GetBranchNotTaken() + { + return nextSegmentBranchNotTaken; + } + + // instruction API + IMLInstruction* AppendInstruction(); + + bool HasSuffixInstruction() const; + sint32 GetSuffixInstructionIndex() const; + IMLInstruction* GetLastInstruction(); + + // segment points + IMLSegmentPoint* segmentPointList{}; +}; + + +void IMLSegment_SetLinkBranchNotTaken(IMLSegment* imlSegmentSrc, IMLSegment* imlSegmentDst); +void IMLSegment_SetLinkBranchTaken(IMLSegment* imlSegmentSrc, IMLSegment* imlSegmentDst); +void IMLSegment_RelinkInputSegment(IMLSegment* imlSegmentOrig, IMLSegment* imlSegmentNew); +void IMLSegment_RemoveLink(IMLSegment* imlSegmentSrc, IMLSegment* imlSegmentDst); diff --git a/src/Cafe/HW/Espresso/Recompiler/PPCFunctionBoundaryTracker.h b/src/Cafe/HW/Espresso/Recompiler/PPCFunctionBoundaryTracker.h index e558292b..96b5143e 100644 --- a/src/Cafe/HW/Espresso/Recompiler/PPCFunctionBoundaryTracker.h +++ b/src/Cafe/HW/Espresso/Recompiler/PPCFunctionBoundaryTracker.h @@ -21,6 +21,16 @@ public: }; public: + ~PPCFunctionBoundaryTracker() + { + while (!map_ranges.empty()) + { + PPCRange_t* range = *map_ranges.begin(); + delete range; + map_ranges.erase(map_ranges.begin()); + } + } + void trackStartPoint(MPTR startAddress) { processRange(startAddress, nullptr, nullptr); @@ -40,10 +50,34 @@ public: return false; } + std::vector GetRanges() + { + std::vector r; + for (auto& it : map_ranges) + r.emplace_back(*it); + return r; + } + + bool ContainsAddress(uint32 addr) const + { + for (auto& it : map_ranges) + { + if (addr >= it->startAddress && addr < it->getEndAddress()) + return true; + } + return false; + } + + const std::set& GetBranchTargets() const + { + return map_branchTargetsAll; + } + private: void addBranchDestination(PPCRange_t* sourceRange, MPTR address) { - map_branchTargets.emplace(address); + map_queuedBranchTargets.emplace(address); + map_branchTargetsAll.emplace(address); } // process flow of instruction @@ -114,7 +148,7 @@ private: Espresso::BOField BO; uint32 BI; bool LK; - Espresso::decodeOp_BCLR(opcode, BO, BI, LK); + Espresso::decodeOp_BCSPR(opcode, BO, BI, LK); if (BO.branchAlways() && !LK) { // unconditional BLR @@ -218,7 +252,7 @@ private: auto rangeItr = map_ranges.begin(); PPCRange_t* previousRange = nullptr; - for (std::set::const_iterator targetItr = map_branchTargets.begin() ; targetItr != map_branchTargets.end(); ) + for (std::set::const_iterator targetItr = map_queuedBranchTargets.begin() ; targetItr != map_queuedBranchTargets.end(); ) { while (rangeItr != map_ranges.end() && ((*rangeItr)->startAddress + (*rangeItr)->length) <= (*targetItr)) { @@ -239,7 +273,7 @@ private: (*targetItr) < ((*rangeItr)->startAddress + (*rangeItr)->length)) { // delete visited targets - targetItr = map_branchTargets.erase(targetItr); + targetItr = map_queuedBranchTargets.erase(targetItr); continue; } @@ -289,5 +323,6 @@ private: }; std::set map_ranges; - std::set map_branchTargets; + std::set map_queuedBranchTargets; + std::set map_branchTargetsAll; }; \ No newline at end of file diff --git a/src/Cafe/HW/Espresso/Recompiler/PPCRecompiler.cpp b/src/Cafe/HW/Espresso/Recompiler/PPCRecompiler.cpp index 6b830563..a094ff4c 100644 --- a/src/Cafe/HW/Espresso/Recompiler/PPCRecompiler.cpp +++ b/src/Cafe/HW/Espresso/Recompiler/PPCRecompiler.cpp @@ -2,7 +2,6 @@ #include "PPCFunctionBoundaryTracker.h" #include "PPCRecompiler.h" #include "PPCRecompilerIml.h" -#include "PPCRecompilerX64.h" #include "Cafe/OS/RPL/rpl.h" #include "util/containers/RangeStore.h" #include "Cafe/OS/libs/coreinit/coreinit_CodeGen.h" @@ -14,6 +13,10 @@ #include "util/helpers/helpers.h" #include "util/MemMapper/MemMapper.h" +#include "IML/IML.h" +#include "IML/IMLRegisterAllocator.h" +#include "BackendX64/BackendX64.h" + struct PPCInvalidationRange { MPTR startAddress; @@ -127,8 +130,9 @@ void PPCRecompiler_attemptEnter(PPCInterpreter_t* hCPU, uint32 enterAddress) PPCRecompiler_enter(hCPU, funcPtr); } } +bool PPCRecompiler_ApplyIMLPasses(ppcImlGenContext_t& ppcImlGenContext); -PPCRecFunction_t* PPCRecompiler_recompileFunction(PPCFunctionBoundaryTracker::PPCRange_t range, std::set& entryAddresses, std::vector>& entryPointsOut) +PPCRecFunction_t* PPCRecompiler_recompileFunction(PPCFunctionBoundaryTracker::PPCRange_t range, std::set& entryAddresses, std::vector>& entryPointsOut, PPCFunctionBoundaryTracker& boundaryTracker) { if (range.startAddress >= PPC_REC_CODE_AREA_END) { @@ -153,29 +157,80 @@ PPCRecFunction_t* PPCRecompiler_recompileFunction(PPCFunctionBoundaryTracker::PP PPCRecFunction_t* ppcRecFunc = new PPCRecFunction_t(); ppcRecFunc->ppcAddress = range.startAddress; ppcRecFunc->ppcSize = range.length; + // generate intermediate code ppcImlGenContext_t ppcImlGenContext = { 0 }; - bool compiledSuccessfully = PPCRecompiler_generateIntermediateCode(ppcImlGenContext, ppcRecFunc, entryAddresses); + bool compiledSuccessfully = PPCRecompiler_generateIntermediateCode(ppcImlGenContext, ppcRecFunc, entryAddresses, boundaryTracker); if (compiledSuccessfully == false) { - // todo: Free everything - PPCRecompiler_freeContext(&ppcImlGenContext); delete ppcRecFunc; - return NULL; + return nullptr; } + + uint32 ppcRecLowerAddr = LaunchSettings::GetPPCRecLowerAddr(); + uint32 ppcRecUpperAddr = LaunchSettings::GetPPCRecUpperAddr(); + + if (ppcRecLowerAddr != 0 && ppcRecUpperAddr != 0) + { + if (ppcRecFunc->ppcAddress < ppcRecLowerAddr || ppcRecFunc->ppcAddress > ppcRecUpperAddr) + { + delete ppcRecFunc; + return nullptr; + } + } + + // apply passes + if (!PPCRecompiler_ApplyIMLPasses(ppcImlGenContext)) + { + delete ppcRecFunc; + return nullptr; + } + + //if (ppcRecFunc->ppcAddress == 0x30DF5F8) + //{ + // debug_printf("----------------------------------------\n"); + // IMLDebug_Dump(&ppcImlGenContext); + // __debugbreak(); + //} + + + //if (ppcRecFunc->ppcAddress == 0x11223344) + //{ + // //debug_printf("----------------------------------------\n"); + // //IMLDebug_Dump(&ppcImlGenContext); + // //__debugbreak(); + //} + //else + //{ + // delete ppcRecFunc; + // return nullptr; + //} + + //if (ppcRecFunc->ppcAddress == 0x03C26844) + //{ + // __debugbreak(); + // IMLDebug_Dump(&ppcImlGenContext); + // __debugbreak(); + //} + // 31A8778 + + // Functions for testing (botw): + // 3B4049C (large with switch case) + // 30BF118 (has a bndz copy loop + some float instructions at the end) + + + // emit x64 code bool x64GenerationSuccess = PPCRecompiler_generateX64Code(ppcRecFunc, &ppcImlGenContext); if (x64GenerationSuccess == false) { - PPCRecompiler_freeContext(&ppcImlGenContext); return nullptr; } // collect list of PPC-->x64 entry points entryPointsOut.clear(); - for (sint32 s = 0; s < ppcImlGenContext.segmentListCount; s++) + for(IMLSegment* imlSegment : ppcImlGenContext.segmentList2) { - PPCRecImlSegment_t* imlSegment = ppcImlGenContext.segmentList[s]; if (imlSegment->isEnterable == false) continue; @@ -185,10 +240,75 @@ PPCRecFunction_t* PPCRecompiler_recompileFunction(PPCFunctionBoundaryTracker::PP entryPointsOut.emplace_back(ppcEnterOffset, x64Offset); } - PPCRecompiler_freeContext(&ppcImlGenContext); + cemuLog_log(LogType::Force, "[Recompiler] Successfully compiled {:08x} - {:08x} Segments: {} Entrypoints: {}", ppcRecFunc->ppcAddress, ppcRecFunc->ppcAddress + ppcRecFunc->ppcSize, ppcImlGenContext.segmentList2.size(), entryPointsOut.size()); + return ppcRecFunc; } +void PPCRecompiler_NativeRegisterAllocatorPass(ppcImlGenContext_t& ppcImlGenContext) +{ + IMLRegisterAllocatorParameters raParam; + + for (auto& it : ppcImlGenContext.mappedRegs) + raParam.regIdToName.try_emplace(it.second.GetRegID(), it.first); + + auto& gprPhysPool = raParam.GetPhysRegPool(IMLRegFormat::I64); + gprPhysPool.SetAvailable(IMLArchX86::PHYSREG_GPR_BASE + X86_REG_RAX); + gprPhysPool.SetAvailable(IMLArchX86::PHYSREG_GPR_BASE + X86_REG_RDX); + gprPhysPool.SetAvailable(IMLArchX86::PHYSREG_GPR_BASE + X86_REG_RBX); + gprPhysPool.SetAvailable(IMLArchX86::PHYSREG_GPR_BASE + X86_REG_RBP); + gprPhysPool.SetAvailable(IMLArchX86::PHYSREG_GPR_BASE + X86_REG_RSI); + gprPhysPool.SetAvailable(IMLArchX86::PHYSREG_GPR_BASE + X86_REG_RDI); + gprPhysPool.SetAvailable(IMLArchX86::PHYSREG_GPR_BASE + X86_REG_R8); + gprPhysPool.SetAvailable(IMLArchX86::PHYSREG_GPR_BASE + X86_REG_R9); + gprPhysPool.SetAvailable(IMLArchX86::PHYSREG_GPR_BASE + X86_REG_R10); + gprPhysPool.SetAvailable(IMLArchX86::PHYSREG_GPR_BASE + X86_REG_R11); + gprPhysPool.SetAvailable(IMLArchX86::PHYSREG_GPR_BASE + X86_REG_R12); + gprPhysPool.SetAvailable(IMLArchX86::PHYSREG_GPR_BASE + X86_REG_RCX); + + // add XMM registers, except XMM15 which is the temporary register + auto& fprPhysPool = raParam.GetPhysRegPool(IMLRegFormat::F64); + fprPhysPool.SetAvailable(IMLArchX86::PHYSREG_FPR_BASE + 0); + fprPhysPool.SetAvailable(IMLArchX86::PHYSREG_FPR_BASE + 1); + fprPhysPool.SetAvailable(IMLArchX86::PHYSREG_FPR_BASE + 2); + fprPhysPool.SetAvailable(IMLArchX86::PHYSREG_FPR_BASE + 3); + fprPhysPool.SetAvailable(IMLArchX86::PHYSREG_FPR_BASE + 4); + fprPhysPool.SetAvailable(IMLArchX86::PHYSREG_FPR_BASE + 5); + fprPhysPool.SetAvailable(IMLArchX86::PHYSREG_FPR_BASE + 6); + fprPhysPool.SetAvailable(IMLArchX86::PHYSREG_FPR_BASE + 7); + fprPhysPool.SetAvailable(IMLArchX86::PHYSREG_FPR_BASE + 8); + fprPhysPool.SetAvailable(IMLArchX86::PHYSREG_FPR_BASE + 9); + fprPhysPool.SetAvailable(IMLArchX86::PHYSREG_FPR_BASE + 10); + fprPhysPool.SetAvailable(IMLArchX86::PHYSREG_FPR_BASE + 11); + fprPhysPool.SetAvailable(IMLArchX86::PHYSREG_FPR_BASE + 12); + fprPhysPool.SetAvailable(IMLArchX86::PHYSREG_FPR_BASE + 13); + fprPhysPool.SetAvailable(IMLArchX86::PHYSREG_FPR_BASE + 14); + + IMLRegisterAllocator_AllocateRegisters(&ppcImlGenContext, raParam); +} + +bool PPCRecompiler_ApplyIMLPasses(ppcImlGenContext_t& ppcImlGenContext) +{ + // isolate entry points from function flow (enterable segments must not be the target of any other segment) + // this simplifies logic during register allocation + PPCRecompilerIML_isolateEnterableSegments(&ppcImlGenContext); + + // if GQRs can be predicted, optimize PSQ load/stores + PPCRecompiler_optimizePSQLoadAndStore(&ppcImlGenContext); + + // merge certain float load+store patterns (must happen before FPR register remapping) + IMLOptimizer_OptimizeDirectFloatCopies(&ppcImlGenContext); + // delay byte swapping for certain load+store patterns + IMLOptimizer_OptimizeDirectIntegerCopies(&ppcImlGenContext); + + PPCRecompiler_NativeRegisterAllocatorPass(ppcImlGenContext); + + //PPCRecompiler_reorderConditionModifyInstructions(&ppcImlGenContext); + //PPCRecompiler_removeRedundantCRUpdates(&ppcImlGenContext); + + return true; +} + bool PPCRecompiler_makeRecompiledFunctionActive(uint32 initialEntryPoint, PPCFunctionBoundaryTracker::PPCRange_t& range, PPCRecFunction_t* ppcRecFunc, std::vector>& entryPoints) { // update jump table @@ -202,7 +322,7 @@ bool PPCRecompiler_makeRecompiledFunctionActive(uint32 initialEntryPoint, PPCFun return false; } - // check if the current range got invalidated in the time it took to recompile it + // check if the current range got invalidated during the time it took to recompile it bool isInvalidated = false; for (auto& invRange : PPCRecompilerState.invalidationRanges) { @@ -280,7 +400,7 @@ void PPCRecompiler_recompileAtAddress(uint32 address) PPCRecompilerState.recompilerSpinlock.unlock(); std::vector> functionEntryPoints; - auto func = PPCRecompiler_recompileFunction(range, entryAddresses, functionEntryPoints); + auto func = PPCRecompiler_recompileFunction(range, entryAddresses, functionEntryPoints, funcBoundaries); if (!func) { @@ -473,6 +593,41 @@ void PPCRecompiler_invalidateRange(uint32 startAddr, uint32 endAddr) #if defined(ARCH_X86_64) void PPCRecompiler_initPlatform() { + ppcRecompilerInstanceData->_x64XMM_xorNegateMaskBottom[0] = 1ULL << 63ULL; + ppcRecompilerInstanceData->_x64XMM_xorNegateMaskBottom[1] = 0ULL; + ppcRecompilerInstanceData->_x64XMM_xorNegateMaskPair[0] = 1ULL << 63ULL; + ppcRecompilerInstanceData->_x64XMM_xorNegateMaskPair[1] = 1ULL << 63ULL; + ppcRecompilerInstanceData->_x64XMM_xorNOTMask[0] = 0xFFFFFFFFFFFFFFFFULL; + ppcRecompilerInstanceData->_x64XMM_xorNOTMask[1] = 0xFFFFFFFFFFFFFFFFULL; + ppcRecompilerInstanceData->_x64XMM_andAbsMaskBottom[0] = ~(1ULL << 63ULL); + ppcRecompilerInstanceData->_x64XMM_andAbsMaskBottom[1] = ~0ULL; + ppcRecompilerInstanceData->_x64XMM_andAbsMaskPair[0] = ~(1ULL << 63ULL); + ppcRecompilerInstanceData->_x64XMM_andAbsMaskPair[1] = ~(1ULL << 63ULL); + ppcRecompilerInstanceData->_x64XMM_andFloatAbsMaskBottom[0] = ~(1 << 31); + ppcRecompilerInstanceData->_x64XMM_andFloatAbsMaskBottom[1] = 0xFFFFFFFF; + ppcRecompilerInstanceData->_x64XMM_andFloatAbsMaskBottom[2] = 0xFFFFFFFF; + ppcRecompilerInstanceData->_x64XMM_andFloatAbsMaskBottom[3] = 0xFFFFFFFF; + ppcRecompilerInstanceData->_x64XMM_singleWordMask[0] = 0xFFFFFFFFULL; + ppcRecompilerInstanceData->_x64XMM_singleWordMask[1] = 0ULL; + ppcRecompilerInstanceData->_x64XMM_constDouble1_1[0] = 1.0; + ppcRecompilerInstanceData->_x64XMM_constDouble1_1[1] = 1.0; + ppcRecompilerInstanceData->_x64XMM_constDouble0_0[0] = 0.0; + ppcRecompilerInstanceData->_x64XMM_constDouble0_0[1] = 0.0; + ppcRecompilerInstanceData->_x64XMM_constFloat0_0[0] = 0.0f; + ppcRecompilerInstanceData->_x64XMM_constFloat0_0[1] = 0.0f; + ppcRecompilerInstanceData->_x64XMM_constFloat1_1[0] = 1.0f; + ppcRecompilerInstanceData->_x64XMM_constFloat1_1[1] = 1.0f; + *(uint32*)&ppcRecompilerInstanceData->_x64XMM_constFloatMin[0] = 0x00800000; + *(uint32*)&ppcRecompilerInstanceData->_x64XMM_constFloatMin[1] = 0x00800000; + ppcRecompilerInstanceData->_x64XMM_flushDenormalMask1[0] = 0x7F800000; + ppcRecompilerInstanceData->_x64XMM_flushDenormalMask1[1] = 0x7F800000; + ppcRecompilerInstanceData->_x64XMM_flushDenormalMask1[2] = 0x7F800000; + ppcRecompilerInstanceData->_x64XMM_flushDenormalMask1[3] = 0x7F800000; + ppcRecompilerInstanceData->_x64XMM_flushDenormalMaskResetSignBits[0] = ~0x80000000; + ppcRecompilerInstanceData->_x64XMM_flushDenormalMaskResetSignBits[1] = ~0x80000000; + ppcRecompilerInstanceData->_x64XMM_flushDenormalMaskResetSignBits[2] = ~0x80000000; + ppcRecompilerInstanceData->_x64XMM_flushDenormalMaskResetSignBits[3] = ~0x80000000; + // mxcsr ppcRecompilerInstanceData->_x64XMM_mxCsr_ftzOn = 0x1F80 | 0x8000; ppcRecompilerInstanceData->_x64XMM_mxCsr_ftzOff = 0x1F80; @@ -510,42 +665,6 @@ void PPCRecompiler_init() PPCRecompiler_allocateRange(mmuRange_TRAMPOLINE_AREA.getBase(), mmuRange_TRAMPOLINE_AREA.getSize()); PPCRecompiler_allocateRange(mmuRange_CODECAVE.getBase(), mmuRange_CODECAVE.getSize()); - // init x64 recompiler instance data - ppcRecompilerInstanceData->_x64XMM_xorNegateMaskBottom[0] = 1ULL << 63ULL; - ppcRecompilerInstanceData->_x64XMM_xorNegateMaskBottom[1] = 0ULL; - ppcRecompilerInstanceData->_x64XMM_xorNegateMaskPair[0] = 1ULL << 63ULL; - ppcRecompilerInstanceData->_x64XMM_xorNegateMaskPair[1] = 1ULL << 63ULL; - ppcRecompilerInstanceData->_x64XMM_xorNOTMask[0] = 0xFFFFFFFFFFFFFFFFULL; - ppcRecompilerInstanceData->_x64XMM_xorNOTMask[1] = 0xFFFFFFFFFFFFFFFFULL; - ppcRecompilerInstanceData->_x64XMM_andAbsMaskBottom[0] = ~(1ULL << 63ULL); - ppcRecompilerInstanceData->_x64XMM_andAbsMaskBottom[1] = ~0ULL; - ppcRecompilerInstanceData->_x64XMM_andAbsMaskPair[0] = ~(1ULL << 63ULL); - ppcRecompilerInstanceData->_x64XMM_andAbsMaskPair[1] = ~(1ULL << 63ULL); - ppcRecompilerInstanceData->_x64XMM_andFloatAbsMaskBottom[0] = ~(1 << 31); - ppcRecompilerInstanceData->_x64XMM_andFloatAbsMaskBottom[1] = 0xFFFFFFFF; - ppcRecompilerInstanceData->_x64XMM_andFloatAbsMaskBottom[2] = 0xFFFFFFFF; - ppcRecompilerInstanceData->_x64XMM_andFloatAbsMaskBottom[3] = 0xFFFFFFFF; - ppcRecompilerInstanceData->_x64XMM_singleWordMask[0] = 0xFFFFFFFFULL; - ppcRecompilerInstanceData->_x64XMM_singleWordMask[1] = 0ULL; - ppcRecompilerInstanceData->_x64XMM_constDouble1_1[0] = 1.0; - ppcRecompilerInstanceData->_x64XMM_constDouble1_1[1] = 1.0; - ppcRecompilerInstanceData->_x64XMM_constDouble0_0[0] = 0.0; - ppcRecompilerInstanceData->_x64XMM_constDouble0_0[1] = 0.0; - ppcRecompilerInstanceData->_x64XMM_constFloat0_0[0] = 0.0f; - ppcRecompilerInstanceData->_x64XMM_constFloat0_0[1] = 0.0f; - ppcRecompilerInstanceData->_x64XMM_constFloat1_1[0] = 1.0f; - ppcRecompilerInstanceData->_x64XMM_constFloat1_1[1] = 1.0f; - *(uint32*)&ppcRecompilerInstanceData->_x64XMM_constFloatMin[0] = 0x00800000; - *(uint32*)&ppcRecompilerInstanceData->_x64XMM_constFloatMin[1] = 0x00800000; - ppcRecompilerInstanceData->_x64XMM_flushDenormalMask1[0] = 0x7F800000; - ppcRecompilerInstanceData->_x64XMM_flushDenormalMask1[1] = 0x7F800000; - ppcRecompilerInstanceData->_x64XMM_flushDenormalMask1[2] = 0x7F800000; - ppcRecompilerInstanceData->_x64XMM_flushDenormalMask1[3] = 0x7F800000; - ppcRecompilerInstanceData->_x64XMM_flushDenormalMaskResetSignBits[0] = ~0x80000000; - ppcRecompilerInstanceData->_x64XMM_flushDenormalMaskResetSignBits[1] = ~0x80000000; - ppcRecompilerInstanceData->_x64XMM_flushDenormalMaskResetSignBits[2] = ~0x80000000; - ppcRecompilerInstanceData->_x64XMM_flushDenormalMaskResetSignBits[3] = ~0x80000000; - // setup GQR scale tables for (uint32 i = 0; i < 32; i++) diff --git a/src/Cafe/HW/Espresso/Recompiler/PPCRecompiler.h b/src/Cafe/HW/Espresso/Recompiler/PPCRecompiler.h index 2e40f19d..080ce2fa 100644 --- a/src/Cafe/HW/Espresso/Recompiler/PPCRecompiler.h +++ b/src/Cafe/HW/Espresso/Recompiler/PPCRecompiler.h @@ -1,4 +1,4 @@ -#include +#pragma once #define PPC_REC_CODE_AREA_START (0x00000000) // lower bound of executable memory area. Recompiler expects this address to be 0 #define PPC_REC_CODE_AREA_END (0x10000000) // upper bound of executable memory area @@ -6,324 +6,50 @@ #define PPC_REC_ALIGN_TO_4MB(__v) (((__v)+4*1024*1024-1)&~(4*1024*1024-1)) -#define PPC_REC_MAX_VIRTUAL_GPR (40) // enough to store 32 GPRs + a few SPRs + temp registers (usually only 1-2) +#define PPC_REC_MAX_VIRTUAL_GPR (40 + 32) // enough to store 32 GPRs + a few SPRs + temp registers (usually only 1-2) -typedef struct +struct ppcRecRange_t { uint32 ppcAddress; uint32 ppcSize; - //void* x86Start; - //size_t x86Size; void* storedRange; -}ppcRecRange_t; +}; -typedef struct +struct PPCRecFunction_t { uint32 ppcAddress; uint32 ppcSize; // ppc code size of function void* x86Code; // pointer to x86 code size_t x86Size; std::vector list_ranges; -}PPCRecFunction_t; - -#define PPCREC_IML_OP_FLAG_SIGNEXTEND (1<<0) -#define PPCREC_IML_OP_FLAG_SWITCHENDIAN (1<<1) -#define PPCREC_IML_OP_FLAG_NOT_EXPANDED (1<<2) // set single-precision load instructions to indicate that the value should not be rounded to double-precision -#define PPCREC_IML_OP_FLAG_UNUSED (1<<7) // used to mark instructions that are not used - -typedef struct -{ - uint8 type; - uint8 operation; - uint8 crRegister; // set to 0xFF if not set, not all IML instruction types support cr. - uint8 crMode; // only used when crRegister is valid, used to differentiate between various forms of condition flag set/clear behavior - uint32 crIgnoreMask; // bit set for every respective CR bit that doesn't need to be updated - uint32 associatedPPCAddress; // ppc address that is associated with this instruction - union - { - struct - { - uint8 _padding[7]; - }padding; - struct - { - // R (op) A [update cr* in mode *] - uint8 registerResult; - uint8 registerA; - }op_r_r; - struct - { - // R = A (op) B [update cr* in mode *] - uint8 registerResult; - uint8 registerA; - uint8 registerB; - }op_r_r_r; - struct - { - // R = A (op) immS32 [update cr* in mode *] - uint8 registerResult; - uint8 registerA; - sint32 immS32; - }op_r_r_s32; - struct - { - // R/F = NAME or NAME = R/F - uint8 registerIndex; - uint8 copyWidth; - uint32 name; - uint8 flags; - }op_r_name; - struct - { - // R (op) s32 [update cr* in mode *] - uint8 registerIndex; - sint32 immS32; - }op_r_immS32; - struct - { - uint32 address; - uint8 flags; - }op_jumpmark; - struct - { - uint32 param; - uint32 param2; - uint16 paramU16; - }op_macro; - struct - { - uint32 jumpmarkAddress; - bool jumpAccordingToSegment; //PPCRecImlSegment_t* destinationSegment; // if set, this replaces jumpmarkAddress - uint8 condition; // only used when crRegisterIndex is 8 or above (update: Apparently only used to mark jumps without a condition? -> Cleanup) - uint8 crRegisterIndex; - uint8 crBitIndex; - bool bitMustBeSet; - }op_conditionalJump; - struct - { - uint8 registerData; - uint8 registerMem; - uint8 registerMem2; - uint8 registerGQR; - uint8 copyWidth; - //uint8 flags; - struct - { - bool swapEndian : 1; - bool signExtend : 1; - bool notExpanded : 1; // for floats - }flags2; - uint8 mode; // transfer mode (copy width, ps0/ps1 behavior) - sint32 immS32; - }op_storeLoad; - struct - { - struct - { - uint8 registerMem; - sint32 immS32; - }src; - struct - { - uint8 registerMem; - sint32 immS32; - }dst; - uint8 copyWidth; - }op_mem2mem; - struct - { - uint8 registerResult; - uint8 registerOperand; - uint8 flags; - }op_fpr_r_r; - struct - { - uint8 registerResult; - uint8 registerOperandA; - uint8 registerOperandB; - uint8 flags; - }op_fpr_r_r_r; - struct - { - uint8 registerResult; - uint8 registerOperandA; - uint8 registerOperandB; - uint8 registerOperandC; - uint8 flags; - }op_fpr_r_r_r_r; - struct - { - uint8 registerResult; - //uint8 flags; - }op_fpr_r; - struct - { - uint32 ppcAddress; - uint32 x64Offset; - }op_ppcEnter; - struct - { - uint8 crD; // crBitIndex (result) - uint8 crA; // crBitIndex - uint8 crB; // crBitIndex - }op_cr; - // conditional operations (emitted if supported by target platform) - struct - { - // r_s32 - uint8 registerIndex; - sint32 immS32; - // condition - uint8 crRegisterIndex; - uint8 crBitIndex; - bool bitMustBeSet; - }op_conditional_r_s32; - }; -}PPCRecImlInstruction_t; - -typedef struct _PPCRecImlSegment_t PPCRecImlSegment_t; - -typedef struct _ppcRecompilerSegmentPoint_t -{ - sint32 index; - PPCRecImlSegment_t* imlSegment; - _ppcRecompilerSegmentPoint_t* next; - _ppcRecompilerSegmentPoint_t* prev; -}ppcRecompilerSegmentPoint_t; - -struct raLivenessLocation_t -{ - sint32 index; - bool isRead; - bool isWrite; - - raLivenessLocation_t() = default; - - raLivenessLocation_t(sint32 index, bool isRead, bool isWrite) - : index(index), isRead(isRead), isWrite(isWrite) {}; }; -struct raLivenessSubrangeLink_t -{ - struct raLivenessSubrange_t* prev; - struct raLivenessSubrange_t* next; -}; +#include "Cafe/HW/Espresso/Recompiler/IML/IMLInstruction.h" +#include "Cafe/HW/Espresso/Recompiler/IML/IMLSegment.h" -struct raLivenessSubrange_t -{ - struct raLivenessRange_t* range; - PPCRecImlSegment_t* imlSegment; - ppcRecompilerSegmentPoint_t start; - ppcRecompilerSegmentPoint_t end; - // dirty state tracking - bool _noLoad; - bool hasStore; - bool hasStoreDelayed; - // next - raLivenessSubrange_t* subrangeBranchTaken; - raLivenessSubrange_t* subrangeBranchNotTaken; - // processing - uint32 lastIterationIndex; - // instruction locations - std::vector list_locations; - // linked list (subranges with same GPR virtual register) - raLivenessSubrangeLink_t link_sameVirtualRegisterGPR; - // linked list (all subranges for this segment) - raLivenessSubrangeLink_t link_segmentSubrangesGPR; -}; - -struct raLivenessRange_t -{ - sint32 virtualRegister; - sint32 physicalRegister; - sint32 name; - std::vector list_subranges; -}; - -struct PPCSegmentRegisterAllocatorInfo_t -{ - // analyzer stage - bool isPartOfProcessedLoop{}; // used during loop detection - sint32 lastIterationIndex{}; - // linked lists - raLivenessSubrange_t* linkedList_allSubranges{}; - raLivenessSubrange_t* linkedList_perVirtualGPR[PPC_REC_MAX_VIRTUAL_GPR]{}; -}; - -struct PPCRecVGPRDistances_t -{ - struct _RegArrayEntry - { - sint32 usageStart{}; - sint32 usageEnd{}; - }reg[PPC_REC_MAX_VIRTUAL_GPR]; - bool isProcessed[PPC_REC_MAX_VIRTUAL_GPR]{}; -}; - -typedef struct _PPCRecImlSegment_t -{ - sint32 momentaryIndex{}; // index in segment list, generally not kept up to date except if needed (necessary for loop detection) - sint32 startOffset{}; // offset to first instruction in iml instruction list - sint32 count{}; // number of instructions in segment - uint32 ppcAddress{}; // ppc address (0xFFFFFFFF if not associated with an address) - uint32 x64Offset{}; // x64 code offset of segment start - uint32 cycleCount{}; // number of PPC cycles required to execute this segment (roughly) - // list of intermediate instructions in this segment - PPCRecImlInstruction_t* imlList{}; - sint32 imlListSize{}; - sint32 imlListCount{}; - // segment link - _PPCRecImlSegment_t* nextSegmentBranchNotTaken{}; // this is also the default for segments where there is no branch - _PPCRecImlSegment_t* nextSegmentBranchTaken{}; - bool nextSegmentIsUncertain{}; - sint32 loopDepth{}; - //sList_t* list_prevSegments; - std::vector<_PPCRecImlSegment_t*> list_prevSegments{}; - // PPC range of segment - uint32 ppcAddrMin{}; - uint32 ppcAddrMax{}; - // enterable segments - bool isEnterable{}; // this segment can be entered from outside the recompiler (no preloaded registers necessary) - uint32 enterPPCAddress{}; // used if isEnterable is true - // jump destination segments - bool isJumpDestination{}; // segment is a destination for one or more (conditional) jumps - uint32 jumpDestinationPPCAddress{}; - // PPC FPR use mask - bool ppcFPRUsed[32]{}; // same as ppcGPRUsed, but for FPR - // CR use mask - uint32 crBitsInput{}; // bits that are expected to be set from the previous segment (read in this segment but not overwritten) - uint32 crBitsRead{}; // all bits that are read in this segment - uint32 crBitsWritten{}; // bits that are written in this segment - // register allocator info - PPCSegmentRegisterAllocatorInfo_t raInfo{}; - PPCRecVGPRDistances_t raDistances{}; - bool raRangeExtendProcessed{}; - // segment points - ppcRecompilerSegmentPoint_t* segmentPointList{}; -}PPCRecImlSegment_t; +struct IMLInstruction* PPCRecompilerImlGen_generateNewEmptyInstruction(struct ppcImlGenContext_t* ppcImlGenContext); struct ppcImlGenContext_t { - PPCRecFunction_t* functionRef; + class PPCFunctionBoundaryTracker* boundaryTracker; uint32* currentInstruction; uint32 ppcAddressOfCurrentInstruction; + IMLSegment* currentOutputSegment; + struct PPCBasicBlockInfo* currentBasicBlock{}; // fpr mode bool LSQE{ true }; bool PSE{ true }; // cycle counter uint32 cyclesSinceLastBranch; // used to track ppc cycles // temporary general purpose registers - uint32 mappedRegister[PPC_REC_MAX_VIRTUAL_GPR]; + //uint32 mappedRegister[PPC_REC_MAX_VIRTUAL_GPR]; // temporary floating point registers (single and double precision) - uint32 mappedFPRRegister[256]; - // list of intermediate instructions - PPCRecImlInstruction_t* imlList; - sint32 imlListSize; - sint32 imlListCount; + //uint32 mappedFPRRegister[256]; + + std::unordered_map mappedRegs; + // list of segments - PPCRecImlSegment_t** segmentList; - sint32 segmentListSize; - sint32 segmentListCount; + std::vector segmentList2; // code generation control bool hasFPUInstruction; // if true, PPCEnter macro will create FP_UNAVAIL checks -> Not needed in user mode // register allocator info @@ -336,6 +62,58 @@ struct ppcImlGenContext_t { bool modifiesGQR[8]; }tracking; + + ~ppcImlGenContext_t() + { + for (IMLSegment* imlSegment : segmentList2) + delete imlSegment; + segmentList2.clear(); + } + + // append raw instruction + IMLInstruction& emitInst() + { + return *PPCRecompilerImlGen_generateNewEmptyInstruction(this); + } + + IMLSegment* NewSegment() + { + IMLSegment* seg = new IMLSegment(); + segmentList2.emplace_back(seg); + return seg; + } + + size_t GetSegmentIndex(IMLSegment* seg) + { + for (size_t i = 0; i < segmentList2.size(); i++) + { + if (segmentList2[i] == seg) + return i; + } + cemu_assert_error(); + return 0; + } + + IMLSegment* InsertSegment(size_t index) + { + IMLSegment* newSeg = new IMLSegment(); + segmentList2.insert(segmentList2.begin() + index, 1, newSeg); + return newSeg; + } + + std::span InsertSegments(size_t index, size_t count) + { + segmentList2.insert(segmentList2.begin() + index, count, {}); + for (size_t i = index; i < (index + count); i++) + segmentList2[i] = new IMLSegment(); + return { segmentList2.data() + index, count}; + } + + void UpdateSegmentIndices() + { + for (size_t i = 0; i < segmentList2.size(); i++) + segmentList2[i]->momentaryIndex = (sint32)i; + } }; typedef void ATTR_MS_ABI (*PPCREC_JUMP_ENTRY)(); @@ -385,8 +163,6 @@ extern void ATTR_MS_ABI (*PPCRecompiler_leaveRecompilerCode_unvisited)(); #define PPC_REC_INVALID_FUNCTION ((PPCRecFunction_t*)-1) -// todo - move some of the stuff above into PPCRecompilerInternal.h - // recompiler interface void PPCRecompiler_recompileIfUnvisited(uint32 enterAddress); diff --git a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerIml.h b/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerIml.h index 86af33b2..5d30267d 100644 --- a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerIml.h +++ b/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerIml.h @@ -1,275 +1,29 @@ +bool PPCRecompiler_generateIntermediateCode(ppcImlGenContext_t& ppcImlGenContext, PPCRecFunction_t* PPCRecFunction, std::set& entryAddresses, class PPCFunctionBoundaryTracker& boundaryTracker); -#define PPCREC_CR_REG_TEMP 8 // there are only 8 cr registers (0-7) we use the 8th as temporary cr register that is never stored (BDNZ instruction for example) +IMLSegment* PPCIMLGen_CreateSplitSegmentAtEnd(ppcImlGenContext_t& ppcImlGenContext, PPCBasicBlockInfo& basicBlockInfo); +IMLSegment* PPCIMLGen_CreateNewSegmentAsBranchTarget(ppcImlGenContext_t& ppcImlGenContext, PPCBasicBlockInfo& basicBlockInfo); -enum -{ - PPCREC_IML_OP_ASSIGN, // '=' operator - PPCREC_IML_OP_ENDIAN_SWAP, // '=' operator with 32bit endian swap - PPCREC_IML_OP_ADD, // '+' operator - PPCREC_IML_OP_SUB, // '-' operator - PPCREC_IML_OP_SUB_CARRY_UPDATE_CARRY, // complex operation, result = operand + ~operand2 + carry bit, updates carry bit - PPCREC_IML_OP_COMPARE_SIGNED, // arithmetic/signed comparison operator (updates cr) - PPCREC_IML_OP_COMPARE_UNSIGNED, // logical/unsigned comparison operator (updates cr) - PPCREC_IML_OP_MULTIPLY_SIGNED, // '*' operator (signed multiply) - PPCREC_IML_OP_MULTIPLY_HIGH_UNSIGNED, // unsigned 64bit multiply, store only high 32bit-word of result - PPCREC_IML_OP_MULTIPLY_HIGH_SIGNED, // signed 64bit multiply, store only high 32bit-word of result - PPCREC_IML_OP_DIVIDE_SIGNED, // '/' operator (signed divide) - PPCREC_IML_OP_DIVIDE_UNSIGNED, // '/' operator (unsigned divide) - PPCREC_IML_OP_ADD_CARRY, // complex operation, result = operand + carry bit, updates carry bit - PPCREC_IML_OP_ADD_CARRY_ME, // complex operation, result = operand + carry bit + (-1), updates carry bit - PPCREC_IML_OP_ADD_UPDATE_CARRY, // '+' operator but also updates carry flag - PPCREC_IML_OP_ADD_CARRY_UPDATE_CARRY, // '+' operator and also adds carry, updates carry flag - // assign operators with cast - PPCREC_IML_OP_ASSIGN_S16_TO_S32, // copy 16bit and sign extend - PPCREC_IML_OP_ASSIGN_S8_TO_S32, // copy 8bit and sign extend - // binary operation - PPCREC_IML_OP_OR, // '|' operator - PPCREC_IML_OP_ORC, // '|' operator, second operand is complemented first - PPCREC_IML_OP_AND, // '&' operator - PPCREC_IML_OP_XOR, // '^' operator - PPCREC_IML_OP_LEFT_ROTATE, // left rotate operator - PPCREC_IML_OP_LEFT_SHIFT, // shift left operator - PPCREC_IML_OP_RIGHT_SHIFT, // right shift operator (unsigned) - PPCREC_IML_OP_NOT, // complement each bit - PPCREC_IML_OP_NEG, // negate - // ppc - PPCREC_IML_OP_RLWIMI, // RLWIMI instruction (rotate, merge based on mask) - PPCREC_IML_OP_SRAW, // SRAWI/SRAW instruction (algebraic shift right, sets ca flag) - PPCREC_IML_OP_SLW, // SLW (shift based on register by up to 63 bits) - PPCREC_IML_OP_SRW, // SRW (shift based on register by up to 63 bits) - PPCREC_IML_OP_CNTLZW, - PPCREC_IML_OP_SUBFC, // SUBFC and SUBFIC (subtract from and set carry) - PPCREC_IML_OP_DCBZ, // clear 32 bytes aligned to 0x20 - PPCREC_IML_OP_MFCR, // copy cr to gpr - PPCREC_IML_OP_MTCRF, // copy gpr to cr (with mask) - // condition register - PPCREC_IML_OP_CR_CLEAR, // clear cr bit - PPCREC_IML_OP_CR_SET, // set cr bit - PPCREC_IML_OP_CR_OR, // OR cr bits - PPCREC_IML_OP_CR_ORC, // OR cr bits, complement second input operand bit first - PPCREC_IML_OP_CR_AND, // AND cr bits - PPCREC_IML_OP_CR_ANDC, // AND cr bits, complement second input operand bit first - // FPU - PPCREC_IML_OP_FPR_ADD_BOTTOM, - PPCREC_IML_OP_FPR_ADD_PAIR, - PPCREC_IML_OP_FPR_SUB_PAIR, - PPCREC_IML_OP_FPR_SUB_BOTTOM, - PPCREC_IML_OP_FPR_MULTIPLY_BOTTOM, - PPCREC_IML_OP_FPR_MULTIPLY_PAIR, - PPCREC_IML_OP_FPR_DIVIDE_BOTTOM, - PPCREC_IML_OP_FPR_DIVIDE_PAIR, - PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_BOTTOM_AND_TOP, - PPCREC_IML_OP_FPR_COPY_TOP_TO_BOTTOM_AND_TOP, - PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_BOTTOM, - PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_TOP, // leave bottom of destination untouched - PPCREC_IML_OP_FPR_COPY_TOP_TO_TOP, // leave bottom of destination untouched - PPCREC_IML_OP_FPR_COPY_TOP_TO_BOTTOM, // leave top of destination untouched - PPCREC_IML_OP_FPR_COPY_BOTTOM_AND_TOP_SWAPPED, - PPCREC_IML_OP_FPR_EXPAND_BOTTOM32_TO_BOTTOM64_AND_TOP64, // expand bottom f32 to f64 in bottom and top half - PPCREC_IML_OP_FPR_BOTTOM_FRES_TO_BOTTOM_AND_TOP, // calculate reciprocal with Espresso accuracy of source bottom half and write result to destination bottom and top half - PPCREC_IML_OP_FPR_FCMPO_BOTTOM, - PPCREC_IML_OP_FPR_FCMPU_BOTTOM, - PPCREC_IML_OP_FPR_FCMPU_TOP, - PPCREC_IML_OP_FPR_NEGATE_BOTTOM, - PPCREC_IML_OP_FPR_NEGATE_PAIR, - PPCREC_IML_OP_FPR_ABS_BOTTOM, // abs(fp0) - PPCREC_IML_OP_FPR_ABS_PAIR, - PPCREC_IML_OP_FPR_FRES_PAIR, // 1.0/fp approx (Espresso accuracy) - PPCREC_IML_OP_FPR_FRSQRTE_PAIR, // 1.0/sqrt(fp) approx (Espresso accuracy) - PPCREC_IML_OP_FPR_NEGATIVE_ABS_BOTTOM, // -abs(fp0) - PPCREC_IML_OP_FPR_ROUND_TO_SINGLE_PRECISION_BOTTOM, // round 64bit double to 64bit double with 32bit float precision (in bottom half of xmm register) - PPCREC_IML_OP_FPR_ROUND_TO_SINGLE_PRECISION_PAIR, // round two 64bit doubles to 64bit double with 32bit float precision - PPCREC_IML_OP_FPR_BOTTOM_RECIPROCAL_SQRT, - PPCREC_IML_OP_FPR_BOTTOM_FCTIWZ, - PPCREC_IML_OP_FPR_SELECT_BOTTOM, // selectively copy bottom value from operand B or C based on value in operand A - PPCREC_IML_OP_FPR_SELECT_PAIR, // selectively copy top/bottom from operand B or C based on value in top/bottom of operand A - // PS - PPCREC_IML_OP_FPR_SUM0, - PPCREC_IML_OP_FPR_SUM1, -}; +void PPCIMLGen_AssertIfNotLastSegmentInstruction(ppcImlGenContext_t& ppcImlGenContext); -#define PPCREC_IML_OP_FPR_COPY_PAIR (PPCREC_IML_OP_ASSIGN) - -enum -{ - PPCREC_IML_MACRO_BLR, // macro for BLR instruction code - PPCREC_IML_MACRO_BLRL, // macro for BLRL instruction code - PPCREC_IML_MACRO_BCTR, // macro for BCTR instruction code - PPCREC_IML_MACRO_BCTRL, // macro for BCTRL instruction code - PPCREC_IML_MACRO_BL, // call to different function (can be within same function) - PPCREC_IML_MACRO_B_FAR, // branch to different function - PPCREC_IML_MACRO_COUNT_CYCLES, // decrease current remaining thread cycles by a certain amount - PPCREC_IML_MACRO_HLE, // HLE function call - PPCREC_IML_MACRO_MFTB, // get TB register value (low or high) - PPCREC_IML_MACRO_LEAVE, // leaves recompiler and switches to interpeter - // debugging - PPCREC_IML_MACRO_DEBUGBREAK, // throws a debugbreak -}; - -enum -{ - PPCREC_JUMP_CONDITION_NONE, - PPCREC_JUMP_CONDITION_E, // equal / zero - PPCREC_JUMP_CONDITION_NE, // not equal / not zero - PPCREC_JUMP_CONDITION_LE, // less or equal - PPCREC_JUMP_CONDITION_L, // less - PPCREC_JUMP_CONDITION_GE, // greater or equal - PPCREC_JUMP_CONDITION_G, // greater - // special case: - PPCREC_JUMP_CONDITION_SUMMARYOVERFLOW, // needs special handling - PPCREC_JUMP_CONDITION_NSUMMARYOVERFLOW, // not summaryoverflow - -}; - -enum -{ - PPCREC_CR_MODE_COMPARE_SIGNED, - PPCREC_CR_MODE_COMPARE_UNSIGNED, // alias logic compare - // others: PPCREC_CR_MODE_ARITHMETIC, - PPCREC_CR_MODE_ARITHMETIC, // arithmetic use (for use with add/sub instructions without generating extra code) - PPCREC_CR_MODE_LOGICAL, -}; - -enum -{ - PPCREC_IML_TYPE_NONE, - PPCREC_IML_TYPE_NO_OP, // no-op instruction - PPCREC_IML_TYPE_JUMPMARK, // possible jump destination (generated before each ppc instruction) - PPCREC_IML_TYPE_R_R, // r* (op) *r - PPCREC_IML_TYPE_R_R_R, // r* = r* (op) r* - PPCREC_IML_TYPE_R_R_S32, // r* = r* (op) s32* - PPCREC_IML_TYPE_LOAD, // r* = [r*+s32*] - PPCREC_IML_TYPE_LOAD_INDEXED, // r* = [r*+r*] - PPCREC_IML_TYPE_STORE, // [r*+s32*] = r* - PPCREC_IML_TYPE_STORE_INDEXED, // [r*+r*] = r* - PPCREC_IML_TYPE_R_NAME, // r* = name - PPCREC_IML_TYPE_NAME_R, // name* = r* - PPCREC_IML_TYPE_R_S32, // r* (op) imm - PPCREC_IML_TYPE_MACRO, - PPCREC_IML_TYPE_CJUMP, // conditional jump - PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK, // jumps only if remaining thread cycles >= 0 - PPCREC_IML_TYPE_PPC_ENTER, // used to mark locations that should be written to recompilerCallTable - PPCREC_IML_TYPE_CR, // condition register specific operations (one or more operands) - // conditional - PPCREC_IML_TYPE_CONDITIONAL_R_S32, - // FPR - PPCREC_IML_TYPE_FPR_R_NAME, // name = f* - PPCREC_IML_TYPE_FPR_NAME_R, // f* = name - PPCREC_IML_TYPE_FPR_LOAD, // r* = (bitdepth) [r*+s32*] (single or paired single mode) - PPCREC_IML_TYPE_FPR_LOAD_INDEXED, // r* = (bitdepth) [r*+r*] (single or paired single mode) - PPCREC_IML_TYPE_FPR_STORE, // (bitdepth) [r*+s32*] = r* (single or paired single mode) - PPCREC_IML_TYPE_FPR_STORE_INDEXED, // (bitdepth) [r*+r*] = r* (single or paired single mode) - PPCREC_IML_TYPE_FPR_R_R, - PPCREC_IML_TYPE_FPR_R_R_R, - PPCREC_IML_TYPE_FPR_R_R_R_R, - PPCREC_IML_TYPE_FPR_R, - // special - PPCREC_IML_TYPE_MEM2MEM, // memory to memory copy (deprecated) - -}; - -enum -{ - PPCREC_NAME_NONE, - PPCREC_NAME_TEMPORARY, - PPCREC_NAME_R0 = 1000, - PPCREC_NAME_SPR0 = 2000, - PPCREC_NAME_FPR0 = 3000, - PPCREC_NAME_TEMPORARY_FPR0 = 4000, // 0 to 7 - //PPCREC_NAME_CR0 = 3000, // value mapped condition register (usually it isn't needed and can be optimized away) -}; - -// special cases for LOAD/STORE -#define PPC_REC_LOAD_LWARX_MARKER (100) // lwarx instruction (similar to LWZX but sets reserved address/value) -#define PPC_REC_STORE_STWCX_MARKER (100) // stwcx instruction (similar to STWX but writes only if reservation from LWARX is valid) -#define PPC_REC_STORE_STSWI_1 (200) // stswi nb = 1 -#define PPC_REC_STORE_STSWI_2 (201) // stswi nb = 2 -#define PPC_REC_STORE_STSWI_3 (202) // stswi nb = 3 -#define PPC_REC_STORE_LSWI_1 (200) // lswi nb = 1 -#define PPC_REC_STORE_LSWI_2 (201) // lswi nb = 2 -#define PPC_REC_STORE_LSWI_3 (202) // lswi nb = 3 - -#define PPC_REC_INVALID_REGISTER 0xFF - -#define PPCREC_CR_BIT_LT 0 -#define PPCREC_CR_BIT_GT 1 -#define PPCREC_CR_BIT_EQ 2 -#define PPCREC_CR_BIT_SO 3 - -enum -{ - // fpr load - PPCREC_FPR_LD_MODE_SINGLE_INTO_PS0, - PPCREC_FPR_LD_MODE_SINGLE_INTO_PS0_PS1, - PPCREC_FPR_LD_MODE_DOUBLE_INTO_PS0, - PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0, - PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0_PS1, - PPCREC_FPR_LD_MODE_PSQ_FLOAT_PS0, - PPCREC_FPR_LD_MODE_PSQ_FLOAT_PS0_PS1, - PPCREC_FPR_LD_MODE_PSQ_S16_PS0, - PPCREC_FPR_LD_MODE_PSQ_S16_PS0_PS1, - PPCREC_FPR_LD_MODE_PSQ_U16_PS0, - PPCREC_FPR_LD_MODE_PSQ_U16_PS0_PS1, - PPCREC_FPR_LD_MODE_PSQ_S8_PS0, - PPCREC_FPR_LD_MODE_PSQ_S8_PS0_PS1, - PPCREC_FPR_LD_MODE_PSQ_U8_PS0, - PPCREC_FPR_LD_MODE_PSQ_U8_PS0_PS1, - // fpr store - PPCREC_FPR_ST_MODE_SINGLE_FROM_PS0, // store 1 single precision float from ps0 - PPCREC_FPR_ST_MODE_DOUBLE_FROM_PS0, // store 1 double precision float from ps0 - - PPCREC_FPR_ST_MODE_UI32_FROM_PS0, // store raw low-32bit of PS0 - - PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0_PS1, - PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0, - PPCREC_FPR_ST_MODE_PSQ_FLOAT_PS0_PS1, - PPCREC_FPR_ST_MODE_PSQ_FLOAT_PS0, - PPCREC_FPR_ST_MODE_PSQ_S8_PS0, - PPCREC_FPR_ST_MODE_PSQ_S8_PS0_PS1, - PPCREC_FPR_ST_MODE_PSQ_U8_PS0, - PPCREC_FPR_ST_MODE_PSQ_U8_PS0_PS1, - PPCREC_FPR_ST_MODE_PSQ_U16_PS0, - PPCREC_FPR_ST_MODE_PSQ_U16_PS0_PS1, - PPCREC_FPR_ST_MODE_PSQ_S16_PS0, - PPCREC_FPR_ST_MODE_PSQ_S16_PS0_PS1, -}; - -bool PPCRecompiler_generateIntermediateCode(ppcImlGenContext_t& ppcImlGenContext, PPCRecFunction_t* PPCRecFunction, std::set& entryAddresses); -void PPCRecompiler_freeContext(ppcImlGenContext_t* ppcImlGenContext); // todo - move to destructor - -PPCRecImlInstruction_t* PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext_t* ppcImlGenContext); -void PPCRecompiler_pushBackIMLInstructions(PPCRecImlSegment_t* imlSegment, sint32 index, sint32 shiftBackCount); -PPCRecImlInstruction_t* PPCRecompiler_insertInstruction(PPCRecImlSegment_t* imlSegment, sint32 index); +IMLInstruction* PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext_t* ppcImlGenContext); +void PPCRecompiler_pushBackIMLInstructions(IMLSegment* imlSegment, sint32 index, sint32 shiftBackCount); +IMLInstruction* PPCRecompiler_insertInstruction(IMLSegment* imlSegment, sint32 index); void PPCRecompilerIml_insertSegments(ppcImlGenContext_t* ppcImlGenContext, sint32 index, sint32 count); -void PPCRecompilerIml_setSegmentPoint(ppcRecompilerSegmentPoint_t* segmentPoint, PPCRecImlSegment_t* imlSegment, sint32 index); -void PPCRecompilerIml_removeSegmentPoint(ppcRecompilerSegmentPoint_t* segmentPoint); +void PPCRecompilerIml_setSegmentPoint(IMLSegmentPoint* segmentPoint, IMLSegment* imlSegment, sint32 index); +void PPCRecompilerIml_removeSegmentPoint(IMLSegmentPoint* segmentPoint); // GPR register management -uint32 PPCRecompilerImlGen_loadRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName, bool loadNew = false); -uint32 PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName); +IMLReg PPCRecompilerImlGen_loadRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName); // FPR register management -uint32 PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName, bool loadNew = false); -uint32 PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName); +IMLReg PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName, bool loadNew = false); +IMLReg PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName); // IML instruction generation -void PPCRecompilerImlGen_generateNewInstruction_jump(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction, uint32 jumpmarkAddress); -void PPCRecompilerImlGen_generateNewInstruction_jumpSegment(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction); - -void PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext_t* ppcImlGenContext, uint32 operation, uint8 registerIndex, sint32 immS32, uint32 copyWidth, bool signExtend, bool bigEndian, uint8 crRegister, uint32 crMode); -void PPCRecompilerImlGen_generateNewInstruction_conditional_r_s32(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction, uint32 operation, uint8 registerIndex, sint32 immS32, uint32 crRegisterIndex, uint32 crBitIndex, bool bitMustBeSet); -void PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction, uint32 operation, uint8 registerResult, uint8 registerA, uint8 crRegister = PPC_REC_INVALID_REGISTER, uint8 crMode = 0); - - - -// IML instruction generation (new style, can generate new instructions but also overwrite existing ones) - -void PPCRecompilerImlGen_generateNewInstruction_noOp(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction); -void PPCRecompilerImlGen_generateNewInstruction_memory_memory(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction, uint8 srcMemReg, sint32 srcImmS32, uint8 dstMemReg, sint32 dstImmS32, uint8 copyWidth); - -void PPCRecompilerImlGen_generateNewInstruction_fpr_r(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction, sint32 operation, uint8 registerResult, sint32 crRegister = PPC_REC_INVALID_REGISTER); +void PPCRecompilerImlGen_generateNewInstruction_conditional_r_s32(ppcImlGenContext_t* ppcImlGenContext, IMLInstruction* imlInstruction, uint32 operation, IMLReg registerIndex, sint32 immS32, uint32 crRegisterIndex, uint32 crBitIndex, bool bitMustBeSet); +void PPCRecompilerImlGen_generateNewInstruction_fpr_r(ppcImlGenContext_t* ppcImlGenContext, IMLInstruction* imlInstruction, sint32 operation, IMLReg registerResult); // IML generation - FPU bool PPCRecompilerImlGen_LFS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode); @@ -347,76 +101,4 @@ bool PPCRecompilerImlGen_PS_CMPU1(ppcImlGenContext_t* ppcImlGenContext, uint32 o // IML general -bool PPCRecompiler_isSuffixInstruction(PPCRecImlInstruction_t* iml); -void PPCRecompilerIML_linkSegments(ppcImlGenContext_t* ppcImlGenContext); -void PPCRecompilerIml_setLinkBranchNotTaken(PPCRecImlSegment_t* imlSegmentSrc, PPCRecImlSegment_t* imlSegmentDst); -void PPCRecompilerIml_setLinkBranchTaken(PPCRecImlSegment_t* imlSegmentSrc, PPCRecImlSegment_t* imlSegmentDst); -void PPCRecompilerIML_relinkInputSegment(PPCRecImlSegment_t* imlSegmentOrig, PPCRecImlSegment_t* imlSegmentNew); -void PPCRecompilerIML_removeLink(PPCRecImlSegment_t* imlSegmentSrc, PPCRecImlSegment_t* imlSegmentDst); void PPCRecompilerIML_isolateEnterableSegments(ppcImlGenContext_t* ppcImlGenContext); - -PPCRecImlInstruction_t* PPCRecompilerIML_getLastInstruction(PPCRecImlSegment_t* imlSegment); - -// IML analyzer -typedef struct -{ - uint32 readCRBits; - uint32 writtenCRBits; -}PPCRecCRTracking_t; - -bool PPCRecompilerImlAnalyzer_isTightFiniteLoop(PPCRecImlSegment_t* imlSegment); -bool PPCRecompilerImlAnalyzer_canTypeWriteCR(PPCRecImlInstruction_t* imlInstruction); -void PPCRecompilerImlAnalyzer_getCRTracking(PPCRecImlInstruction_t* imlInstruction, PPCRecCRTracking_t* crTracking); - -// IML optimizer -bool PPCRecompiler_reduceNumberOfFPRRegisters(ppcImlGenContext_t* ppcImlGenContext); - -bool PPCRecompiler_manageFPRRegisters(ppcImlGenContext_t* ppcImlGenContext); - -void PPCRecompiler_removeRedundantCRUpdates(ppcImlGenContext_t* ppcImlGenContext); -void PPCRecompiler_optimizeDirectFloatCopies(ppcImlGenContext_t* ppcImlGenContext); -void PPCRecompiler_optimizeDirectIntegerCopies(ppcImlGenContext_t* ppcImlGenContext); - -void PPCRecompiler_optimizePSQLoadAndStore(ppcImlGenContext_t* ppcImlGenContext); - -// IML register allocator -void PPCRecompilerImm_allocateRegisters(ppcImlGenContext_t* ppcImlGenContext); - -// late optimizations -void PPCRecompiler_reorderConditionModifyInstructions(ppcImlGenContext_t* ppcImlGenContext); - -// debug - -void PPCRecompiler_dumpIMLSegment(PPCRecImlSegment_t* imlSegment, sint32 segmentIndex, bool printLivenessRangeInfo = false); - - -typedef struct -{ - union - { - struct - { - sint16 readNamedReg1; - sint16 readNamedReg2; - sint16 readNamedReg3; - sint16 writtenNamedReg1; - }; - sint16 gpr[4]; // 3 read + 1 write - }; - // FPR - union - { - struct - { - // note: If destination operand is not fully written, it will be added as a read FPR as well - sint16 readFPR1; - sint16 readFPR2; - sint16 readFPR3; - sint16 readFPR4; // usually this is set to the result FPR if only partially overwritten - sint16 writtenFPR1; - }; - sint16 fpr[4]; - }; -}PPCImlOptimizerUsedRegisters_t; - -void PPCRecompiler_checkRegisterUsage(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction, PPCImlOptimizerUsedRegisters_t* registersUsed); diff --git a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlAnalyzer.cpp b/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlAnalyzer.cpp deleted file mode 100644 index 4962d30d..00000000 --- a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlAnalyzer.cpp +++ /dev/null @@ -1,137 +0,0 @@ -#include "PPCRecompiler.h" -#include "PPCRecompilerIml.h" -#include "util/helpers/fixedSizeList.h" -#include "Cafe/HW/Espresso/Interpreter/PPCInterpreterInternal.h" - -/* - * Initializes a single segment and returns true if it is a finite loop - */ -bool PPCRecompilerImlAnalyzer_isTightFiniteLoop(PPCRecImlSegment_t* imlSegment) -{ - bool isTightFiniteLoop = false; - // base criteria, must jump to beginning of same segment - if (imlSegment->nextSegmentBranchTaken != imlSegment) - return false; - // loops using BDNZ are assumed to always be finite - for (sint32 t = 0; t < imlSegment->imlListCount; t++) - { - if (imlSegment->imlList[t].type == PPCREC_IML_TYPE_R_S32 && imlSegment->imlList[t].operation == PPCREC_IML_OP_SUB && imlSegment->imlList[t].crRegister == 8) - { - return true; - } - } - // for non-BDNZ loops, check for common patterns - // risky approach, look for ADD/SUB operations and assume that potential overflow means finite (does not include r_r_s32 ADD/SUB) - // this catches most loops with load-update and store-update instructions, but also those with decrementing counters - FixedSizeList list_modifiedRegisters; - for (sint32 t = 0; t < imlSegment->imlListCount; t++) - { - if (imlSegment->imlList[t].type == PPCREC_IML_TYPE_R_S32 && (imlSegment->imlList[t].operation == PPCREC_IML_OP_ADD || imlSegment->imlList[t].operation == PPCREC_IML_OP_SUB) ) - { - list_modifiedRegisters.addUnique(imlSegment->imlList[t].op_r_immS32.registerIndex); - } - } - if (list_modifiedRegisters.count > 0) - { - // remove all registers from the list that are modified by non-ADD/SUB instructions - // todo: We should also cover the case where ADD+SUB on the same register cancel the effect out - PPCImlOptimizerUsedRegisters_t registersUsed; - for (sint32 t = 0; t < imlSegment->imlListCount; t++) - { - if (imlSegment->imlList[t].type == PPCREC_IML_TYPE_R_S32 && (imlSegment->imlList[t].operation == PPCREC_IML_OP_ADD || imlSegment->imlList[t].operation == PPCREC_IML_OP_SUB)) - continue; - PPCRecompiler_checkRegisterUsage(NULL, imlSegment->imlList + t, ®istersUsed); - if(registersUsed.writtenNamedReg1 < 0) - continue; - list_modifiedRegisters.remove(registersUsed.writtenNamedReg1); - } - if (list_modifiedRegisters.count > 0) - { - return true; - } - } - return false; -} - -/* -* Returns true if the imlInstruction can overwrite CR (depending on value of ->crRegister) -*/ -bool PPCRecompilerImlAnalyzer_canTypeWriteCR(PPCRecImlInstruction_t* imlInstruction) -{ - if (imlInstruction->type == PPCREC_IML_TYPE_R_R) - return true; - if (imlInstruction->type == PPCREC_IML_TYPE_R_R_R) - return true; - if (imlInstruction->type == PPCREC_IML_TYPE_R_R_S32) - return true; - if (imlInstruction->type == PPCREC_IML_TYPE_R_S32) - return true; - if (imlInstruction->type == PPCREC_IML_TYPE_FPR_R_R) - return true; - if (imlInstruction->type == PPCREC_IML_TYPE_FPR_R_R_R) - return true; - if (imlInstruction->type == PPCREC_IML_TYPE_FPR_R_R_R_R) - return true; - if (imlInstruction->type == PPCREC_IML_TYPE_FPR_R) - return true; - return false; -} - -void PPCRecompilerImlAnalyzer_getCRTracking(PPCRecImlInstruction_t* imlInstruction, PPCRecCRTracking_t* crTracking) -{ - crTracking->readCRBits = 0; - crTracking->writtenCRBits = 0; - if (imlInstruction->type == PPCREC_IML_TYPE_CJUMP) - { - if (imlInstruction->op_conditionalJump.condition != PPCREC_JUMP_CONDITION_NONE) - { - uint32 crBitFlag = 1 << (imlInstruction->op_conditionalJump.crRegisterIndex * 4 + imlInstruction->op_conditionalJump.crBitIndex); - crTracking->readCRBits = (crBitFlag); - } - } - else if (imlInstruction->type == PPCREC_IML_TYPE_CONDITIONAL_R_S32) - { - uint32 crBitFlag = 1 << (imlInstruction->op_conditional_r_s32.crRegisterIndex * 4 + imlInstruction->op_conditional_r_s32.crBitIndex); - crTracking->readCRBits = crBitFlag; - } - else if (imlInstruction->type == PPCREC_IML_TYPE_R_S32 && imlInstruction->operation == PPCREC_IML_OP_MFCR) - { - crTracking->readCRBits = 0xFFFFFFFF; - } - else if (imlInstruction->type == PPCREC_IML_TYPE_R_S32 && imlInstruction->operation == PPCREC_IML_OP_MTCRF) - { - crTracking->writtenCRBits |= ppc_MTCRFMaskToCRBitMask((uint32)imlInstruction->op_r_immS32.immS32); - } - else if (imlInstruction->type == PPCREC_IML_TYPE_CR) - { - if (imlInstruction->operation == PPCREC_IML_OP_CR_CLEAR || - imlInstruction->operation == PPCREC_IML_OP_CR_SET) - { - uint32 crBitFlag = 1 << (imlInstruction->op_cr.crD); - crTracking->writtenCRBits = crBitFlag; - } - else if (imlInstruction->operation == PPCREC_IML_OP_CR_OR || - imlInstruction->operation == PPCREC_IML_OP_CR_ORC || - imlInstruction->operation == PPCREC_IML_OP_CR_AND || - imlInstruction->operation == PPCREC_IML_OP_CR_ANDC) - { - uint32 crBitFlag = 1 << (imlInstruction->op_cr.crD); - crTracking->writtenCRBits = crBitFlag; - crBitFlag = 1 << (imlInstruction->op_cr.crA); - crTracking->readCRBits = crBitFlag; - crBitFlag = 1 << (imlInstruction->op_cr.crB); - crTracking->readCRBits |= crBitFlag; - } - else - assert_dbg(); - } - else if (PPCRecompilerImlAnalyzer_canTypeWriteCR(imlInstruction) && imlInstruction->crRegister >= 0 && imlInstruction->crRegister <= 7) - { - crTracking->writtenCRBits |= (0xF << (imlInstruction->crRegister * 4)); - } - else if ((imlInstruction->type == PPCREC_IML_TYPE_STORE || imlInstruction->type == PPCREC_IML_TYPE_STORE_INDEXED) && imlInstruction->op_storeLoad.copyWidth == PPC_REC_STORE_STWCX_MARKER) - { - // overwrites CR0 - crTracking->writtenCRBits |= (0xF << 0); - } -} diff --git a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlGen.cpp b/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlGen.cpp index b9685488..6c8374d4 100644 --- a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlGen.cpp +++ b/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlGen.cpp @@ -1,141 +1,68 @@ #include "Cafe/HW/Espresso/Interpreter/PPCInterpreterInternal.h" #include "Cafe/HW/Espresso/Interpreter/PPCInterpreterHelper.h" +#include "Cafe/HW/Espresso/EspressoISA.h" #include "PPCRecompiler.h" #include "PPCRecompilerIml.h" -#include "PPCRecompilerX64.h" -#include "PPCRecompilerImlRanges.h" -#include "util/helpers/StringBuf.h" +#include "IML/IML.h" +#include "IML/IMLRegisterAllocatorRanges.h" +#include "PPCFunctionBoundaryTracker.h" bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext); -uint32 PPCRecompiler_iterateCurrentInstruction(ppcImlGenContext_t* ppcImlGenContext); -uint32 PPCRecompiler_getInstructionByOffset(ppcImlGenContext_t* ppcImlGenContext, uint32 offset); -PPCRecImlInstruction_t* PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext_t* ppcImlGenContext) +struct PPCBasicBlockInfo { - if( ppcImlGenContext->imlListCount+1 > ppcImlGenContext->imlListSize ) + PPCBasicBlockInfo(uint32 startAddress, const std::set& entryAddresses) : startAddress(startAddress), lastAddress(startAddress) { - sint32 newSize = ppcImlGenContext->imlListCount*2 + 2; - ppcImlGenContext->imlList = (PPCRecImlInstruction_t*)realloc(ppcImlGenContext->imlList, sizeof(PPCRecImlInstruction_t)*newSize); - ppcImlGenContext->imlListSize = newSize; + isEnterable = entryAddresses.find(startAddress) != entryAddresses.end(); } - PPCRecImlInstruction_t* imlInstruction = ppcImlGenContext->imlList+ppcImlGenContext->imlListCount; - memset(imlInstruction, 0x00, sizeof(PPCRecImlInstruction_t)); - imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; // dont update any cr register by default - imlInstruction->associatedPPCAddress = ppcImlGenContext->ppcAddressOfCurrentInstruction; - ppcImlGenContext->imlListCount++; - return imlInstruction; -} -void PPCRecompilerImlGen_generateNewInstruction_jumpmark(ppcImlGenContext_t* ppcImlGenContext, uint32 address) + uint32 startAddress; + uint32 lastAddress; // inclusive + bool isEnterable{ false }; + bool hasContinuedFlow{ true }; // non-branch path goes to next segment, assumed by default + bool hasBranchTarget{ false }; + uint32 branchTarget{}; + + // associated IML segments + IMLSegment* firstSegment{}; // first segment in chain, used as branch target for other segments + IMLSegment* appendSegment{}; // last segment in chain, additional instructions should be appended to this segment + + void SetInitialSegment(IMLSegment* seg) + { + cemu_assert_debug(!firstSegment); + cemu_assert_debug(!appendSegment); + firstSegment = seg; + appendSegment = seg; + } + + IMLSegment* GetFirstSegmentInChain() + { + return firstSegment; + } + + IMLSegment* GetSegmentForInstructionAppend() + { + return appendSegment; + } +}; + +IMLInstruction* PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext_t* ppcImlGenContext) { - // no-op that indicates possible destination of a jump - PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); - imlInstruction->type = PPCREC_IML_TYPE_JUMPMARK; - imlInstruction->op_jumpmark.address = address; + IMLInstruction& inst = ppcImlGenContext->currentOutputSegment->imlList.emplace_back(); + memset(&inst, 0x00, sizeof(IMLInstruction)); + return &inst; } -void PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext_t* ppcImlGenContext, uint32 macroId, uint32 param, uint32 param2, uint16 paramU16) -{ - // no-op that indicates possible destination of a jump - PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); - imlInstruction->type = PPCREC_IML_TYPE_MACRO; - imlInstruction->operation = macroId; - imlInstruction->op_macro.param = param; - imlInstruction->op_macro.param2 = param2; - imlInstruction->op_macro.paramU16 = paramU16; -} - -/* - * Generates a marker for Interpreter -> Recompiler entrypoints - * PPC_ENTER iml instructions have no associated PPC address but the instruction itself has one - */ -void PPCRecompilerImlGen_generateNewInstruction_ppcEnter(ppcImlGenContext_t* ppcImlGenContext, uint32 ppcAddress) -{ - // no-op that indicates possible destination of a jump - PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); - imlInstruction->type = PPCREC_IML_TYPE_PPC_ENTER; - imlInstruction->operation = 0; - imlInstruction->op_ppcEnter.ppcAddress = ppcAddress; - imlInstruction->op_ppcEnter.x64Offset = 0; - imlInstruction->associatedPPCAddress = 0; -} - -void PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction, uint32 operation, uint8 registerResult, uint8 registerA, uint8 crRegister, uint8 crMode) -{ - // operation with two register operands (e.g. "t0 = t1") - if(imlInstruction == NULL) - imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); - imlInstruction->type = PPCREC_IML_TYPE_R_R; - imlInstruction->operation = operation; - imlInstruction->crRegister = crRegister; - imlInstruction->crMode = crMode; - imlInstruction->op_r_r.registerResult = registerResult; - imlInstruction->op_r_r.registerA = registerA; -} - -void PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext_t* ppcImlGenContext, uint32 operation, uint8 registerResult, uint8 registerA, uint8 registerB, uint8 crRegister=PPC_REC_INVALID_REGISTER, uint8 crMode=0) -{ - // operation with three register operands (e.g. "t0 = t1 + t4") - PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); - imlInstruction->type = PPCREC_IML_TYPE_R_R_R; - imlInstruction->operation = operation; - imlInstruction->crRegister = crRegister; - imlInstruction->crMode = crMode; - imlInstruction->op_r_r_r.registerResult = registerResult; - imlInstruction->op_r_r_r.registerA = registerA; - imlInstruction->op_r_r_r.registerB = registerB; -} - -void PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext_t* ppcImlGenContext, uint32 operation, uint8 registerResult, uint8 registerA, sint32 immS32, uint8 crRegister=PPC_REC_INVALID_REGISTER, uint8 crMode=0) -{ - // operation with two register operands and one signed immediate (e.g. "t0 = t1 + 1234") - PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); - imlInstruction->type = PPCREC_IML_TYPE_R_R_S32; - imlInstruction->operation = operation; - imlInstruction->crRegister = crRegister; - imlInstruction->crMode = crMode; - imlInstruction->op_r_r_s32.registerResult = registerResult; - imlInstruction->op_r_r_s32.registerA = registerA; - imlInstruction->op_r_r_s32.immS32 = immS32; -} - -void PPCRecompilerImlGen_generateNewInstruction_name_r(ppcImlGenContext_t* ppcImlGenContext, uint32 operation, uint8 registerIndex, uint32 name, uint32 copyWidth, bool signExtend, bool bigEndian) -{ - // Store name (e.g. "'r3' = t0" which translates to MOV [ESP+offset_r3], reg32) - PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); - imlInstruction->type = PPCREC_IML_TYPE_NAME_R; - imlInstruction->operation = operation; - imlInstruction->op_r_name.registerIndex = registerIndex; - imlInstruction->op_r_name.name = name; - imlInstruction->op_r_name.copyWidth = copyWidth; - imlInstruction->op_r_name.flags = (signExtend?PPCREC_IML_OP_FLAG_SIGNEXTEND:0)|(bigEndian?PPCREC_IML_OP_FLAG_SWITCHENDIAN:0); -} - -void PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext_t* ppcImlGenContext, uint32 operation, uint8 registerIndex, sint32 immS32, uint32 copyWidth, bool signExtend, bool bigEndian, uint8 crRegister, uint32 crMode) -{ - // two variations: - // operation without store (e.g. "'r3' < 123" which has no effect other than updating a condition flags register) - // operation with store (e.g. "'r3' = 123") - PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); - imlInstruction->type = PPCREC_IML_TYPE_R_S32; - imlInstruction->operation = operation; - imlInstruction->crRegister = crRegister; - imlInstruction->crMode = crMode; - imlInstruction->op_r_immS32.registerIndex = registerIndex; - imlInstruction->op_r_immS32.immS32 = immS32; -} - -void PPCRecompilerImlGen_generateNewInstruction_conditional_r_s32(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction, uint32 operation, uint8 registerIndex, sint32 immS32, uint32 crRegisterIndex, uint32 crBitIndex, bool bitMustBeSet) +void PPCRecompilerImlGen_generateNewInstruction_conditional_r_s32(ppcImlGenContext_t* ppcImlGenContext, IMLInstruction* imlInstruction, uint32 operation, IMLReg registerIndex, sint32 immS32, uint32 crRegisterIndex, uint32 crBitIndex, bool bitMustBeSet) { if(imlInstruction == NULL) imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); else - memset(imlInstruction, 0, sizeof(PPCRecImlInstruction_t)); + memset(imlInstruction, 0, sizeof(IMLInstruction)); imlInstruction->type = PPCREC_IML_TYPE_CONDITIONAL_R_S32; imlInstruction->operation = operation; - imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; // r_s32 operation - imlInstruction->op_conditional_r_s32.registerIndex = registerIndex; + imlInstruction->op_conditional_r_s32.regR = registerIndex; imlInstruction->op_conditional_r_s32.immS32 = immS32; // condition imlInstruction->op_conditional_r_s32.crRegisterIndex = crRegisterIndex; @@ -143,421 +70,195 @@ void PPCRecompilerImlGen_generateNewInstruction_conditional_r_s32(ppcImlGenConte imlInstruction->op_conditional_r_s32.bitMustBeSet = bitMustBeSet; } - -void PPCRecompilerImlGen_generateNewInstruction_jump(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction, uint32 jumpmarkAddress) +void PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext_t* ppcImlGenContext, IMLReg registerDestination, IMLReg registerMemory1, IMLReg registerMemory2, uint32 copyWidth, bool signExtend, bool switchEndian) { - // jump - if (imlInstruction == NULL) - imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); - else - memset(imlInstruction, 0, sizeof(PPCRecImlInstruction_t)); - imlInstruction->type = PPCREC_IML_TYPE_CJUMP; - imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; - imlInstruction->op_conditionalJump.jumpmarkAddress = jumpmarkAddress; - imlInstruction->op_conditionalJump.jumpAccordingToSegment = false; - imlInstruction->op_conditionalJump.condition = PPCREC_JUMP_CONDITION_NONE; - imlInstruction->op_conditionalJump.crRegisterIndex = 0; - imlInstruction->op_conditionalJump.crBitIndex = 0; - imlInstruction->op_conditionalJump.bitMustBeSet = false; -} - -// jump based on segment branches -void PPCRecompilerImlGen_generateNewInstruction_jumpSegment(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction) -{ - // jump - if (imlInstruction == NULL) - imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); - imlInstruction->associatedPPCAddress = 0; - imlInstruction->type = PPCREC_IML_TYPE_CJUMP; - imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; - imlInstruction->op_conditionalJump.jumpmarkAddress = 0; - imlInstruction->op_conditionalJump.jumpAccordingToSegment = true; - imlInstruction->op_conditionalJump.condition = PPCREC_JUMP_CONDITION_NONE; - imlInstruction->op_conditionalJump.crRegisterIndex = 0; - imlInstruction->op_conditionalJump.crBitIndex = 0; - imlInstruction->op_conditionalJump.bitMustBeSet = false; -} - -void PPCRecompilerImlGen_generateNewInstruction_noOp(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction) -{ - if (imlInstruction == NULL) - imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); - imlInstruction->type = PPCREC_IML_TYPE_NO_OP; - imlInstruction->operation = 0; - imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; - imlInstruction->crMode = 0; -} - -void PPCRecompilerImlGen_generateNewInstruction_cr(ppcImlGenContext_t* ppcImlGenContext, uint32 operation, uint8 crD, uint8 crA, uint8 crB) -{ - // multiple variations: - // operation involving only one cr bit (like clear crD bit) - // operation involving three cr bits (like crD = crA or crB) - PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); - imlInstruction->type = PPCREC_IML_TYPE_CR; - imlInstruction->operation = operation; - imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; - imlInstruction->crMode = 0; - imlInstruction->op_cr.crD = crD; - imlInstruction->op_cr.crA = crA; - imlInstruction->op_cr.crB = crB; -} - -void PPCRecompilerImlGen_generateNewInstruction_conditionalJump(ppcImlGenContext_t* ppcImlGenContext, uint32 jumpmarkAddress, uint32 jumpCondition, uint32 crRegisterIndex, uint32 crBitIndex, bool bitMustBeSet) -{ - // conditional jump - PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); - imlInstruction->type = PPCREC_IML_TYPE_CJUMP; - imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; - imlInstruction->op_conditionalJump.jumpmarkAddress = jumpmarkAddress; - imlInstruction->op_conditionalJump.condition = jumpCondition; - imlInstruction->op_conditionalJump.crRegisterIndex = crRegisterIndex; - imlInstruction->op_conditionalJump.crBitIndex = crBitIndex; - imlInstruction->op_conditionalJump.bitMustBeSet = bitMustBeSet; -} - -void PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext_t* ppcImlGenContext, uint8 registerDestination, uint8 registerMemory, sint32 immS32, uint32 copyWidth, bool signExtend, bool switchEndian) -{ - // load from memory - PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); - imlInstruction->type = PPCREC_IML_TYPE_LOAD; - imlInstruction->operation = 0; - imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; - imlInstruction->op_storeLoad.registerData = registerDestination; - imlInstruction->op_storeLoad.registerMem = registerMemory; - imlInstruction->op_storeLoad.immS32 = immS32; - imlInstruction->op_storeLoad.copyWidth = copyWidth; - //imlInstruction->op_storeLoad.flags = (signExtend ? PPCREC_IML_OP_FLAG_SIGNEXTEND : 0) | (switchEndian ? PPCREC_IML_OP_FLAG_SWITCHENDIAN : 0); - imlInstruction->op_storeLoad.flags2.swapEndian = switchEndian; - imlInstruction->op_storeLoad.flags2.signExtend = signExtend; -} - -void PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext_t* ppcImlGenContext, uint8 registerDestination, uint8 registerMemory1, uint8 registerMemory2, uint32 copyWidth, bool signExtend, bool switchEndian) -{ - // load from memory - PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); + cemu_assert_debug(registerMemory1.IsValid()); + cemu_assert_debug(registerMemory2.IsValid()); + cemu_assert_debug(registerDestination.IsValid()); + IMLInstruction* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_LOAD_INDEXED; imlInstruction->operation = 0; - imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; imlInstruction->op_storeLoad.registerData = registerDestination; imlInstruction->op_storeLoad.registerMem = registerMemory1; imlInstruction->op_storeLoad.registerMem2 = registerMemory2; imlInstruction->op_storeLoad.copyWidth = copyWidth; - //imlInstruction->op_storeLoad.flags = (signExtend?PPCREC_IML_OP_FLAG_SIGNEXTEND:0)|(switchEndian?PPCREC_IML_OP_FLAG_SWITCHENDIAN:0); imlInstruction->op_storeLoad.flags2.swapEndian = switchEndian; imlInstruction->op_storeLoad.flags2.signExtend = signExtend; } -void PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext_t* ppcImlGenContext, uint8 registerSource, uint8 registerMemory, sint32 immS32, uint32 copyWidth, bool switchEndian) +void PPCRecompilerImlGen_generateNewInstruction_memory_r_indexed(ppcImlGenContext_t* ppcImlGenContext, IMLReg registerDestination, IMLReg registerMemory1, IMLReg registerMemory2, uint32 copyWidth, bool signExtend, bool switchEndian) { - // load from memory - PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); - imlInstruction->type = PPCREC_IML_TYPE_STORE; - imlInstruction->operation = 0; - imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; - imlInstruction->op_storeLoad.registerData = registerSource; - imlInstruction->op_storeLoad.registerMem = registerMemory; - imlInstruction->op_storeLoad.immS32 = immS32; - imlInstruction->op_storeLoad.copyWidth = copyWidth; - //imlInstruction->op_storeLoad.flags = (switchEndian?PPCREC_IML_OP_FLAG_SWITCHENDIAN:0); - imlInstruction->op_storeLoad.flags2.swapEndian = switchEndian; - imlInstruction->op_storeLoad.flags2.signExtend = false; -} - -void PPCRecompilerImlGen_generateNewInstruction_memory_r_indexed(ppcImlGenContext_t* ppcImlGenContext, uint8 registerDestination, uint8 registerMemory1, uint8 registerMemory2, uint32 copyWidth, bool signExtend, bool switchEndian) -{ - // load from memory - PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); + cemu_assert_debug(registerMemory1.IsValid()); + cemu_assert_debug(registerMemory2.IsValid()); + cemu_assert_debug(registerDestination.IsValid()); + IMLInstruction* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_STORE_INDEXED; imlInstruction->operation = 0; - imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; imlInstruction->op_storeLoad.registerData = registerDestination; imlInstruction->op_storeLoad.registerMem = registerMemory1; imlInstruction->op_storeLoad.registerMem2 = registerMemory2; imlInstruction->op_storeLoad.copyWidth = copyWidth; - //imlInstruction->op_storeLoad.flags = (signExtend?PPCREC_IML_OP_FLAG_SIGNEXTEND:0)|(switchEndian?PPCREC_IML_OP_FLAG_SWITCHENDIAN:0); imlInstruction->op_storeLoad.flags2.swapEndian = switchEndian; imlInstruction->op_storeLoad.flags2.signExtend = signExtend; } -void PPCRecompilerImlGen_generateNewInstruction_memory_memory(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction, uint8 srcMemReg, sint32 srcImmS32, uint8 dstMemReg, sint32 dstImmS32, uint8 copyWidth) +// create and fill two segments (branch taken and branch not taken) as a follow up to the current segment and then merge flow afterwards +template +void PPCIMLGen_CreateSegmentBranchedPath(ppcImlGenContext_t& ppcImlGenContext, PPCBasicBlockInfo& basicBlockInfo, F1n genSegmentBranchTaken, F2n genSegmentBranchNotTaken) { - // copy from memory to memory - if(imlInstruction == NULL) - imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); - imlInstruction->type = PPCREC_IML_TYPE_MEM2MEM; - imlInstruction->operation = 0; - imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; - imlInstruction->op_mem2mem.src.registerMem = srcMemReg; - imlInstruction->op_mem2mem.src.immS32 = srcImmS32; - imlInstruction->op_mem2mem.dst.registerMem = dstMemReg; - imlInstruction->op_mem2mem.dst.immS32 = dstImmS32; - imlInstruction->op_mem2mem.copyWidth = copyWidth; + IMLSegment* currentWriteSegment = basicBlockInfo.GetSegmentForInstructionAppend(); + + std::span segments = ppcImlGenContext.InsertSegments(ppcImlGenContext.GetSegmentIndex(currentWriteSegment) + 1, 3); + IMLSegment* segBranchNotTaken = segments[0]; + IMLSegment* segBranchTaken = segments[1]; + IMLSegment* segMerge = segments[2]; + + // link the segments + segMerge->SetLinkBranchTaken(currentWriteSegment->GetBranchTaken()); + segMerge->SetLinkBranchNotTaken(currentWriteSegment->GetBranchNotTaken()); + currentWriteSegment->SetLinkBranchTaken(segBranchTaken); + currentWriteSegment->SetLinkBranchNotTaken(segBranchNotTaken); + segBranchTaken->SetLinkBranchNotTaken(segMerge); + segBranchNotTaken->SetLinkBranchTaken(segMerge); + // generate code for branch taken segment + ppcImlGenContext.currentOutputSegment = segBranchTaken; + genSegmentBranchTaken(ppcImlGenContext); + cemu_assert_debug(ppcImlGenContext.currentOutputSegment == segBranchTaken); + // generate code for branch not taken segment + ppcImlGenContext.currentOutputSegment = segBranchNotTaken; + genSegmentBranchNotTaken(ppcImlGenContext); + cemu_assert_debug(ppcImlGenContext.currentOutputSegment == segBranchNotTaken); + ppcImlGenContext.emitInst().make_jump(); + // make merge segment the new write segment + ppcImlGenContext.currentOutputSegment = segMerge; + basicBlockInfo.appendSegment = segMerge; } -uint32 PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName) +IMLReg PPCRecompilerImlGen_LookupReg(ppcImlGenContext_t* ppcImlGenContext, IMLName mappedName, IMLRegFormat regFormat) { - if( mappedName == PPCREC_NAME_NONE ) + auto it = ppcImlGenContext->mappedRegs.find(mappedName); + if (it != ppcImlGenContext->mappedRegs.end()) + return it->second; + // create new reg entry + IMLRegFormat baseFormat; + if (regFormat == IMLRegFormat::F64) + baseFormat = IMLRegFormat::F64; + else if (regFormat == IMLRegFormat::I32) + baseFormat = IMLRegFormat::I64; + else { - debug_printf("PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(): Invalid mappedName parameter\n"); - return PPC_REC_INVALID_REGISTER; + cemu_assert_suspicious(); } - for(uint32 i=0; i<(PPC_REC_MAX_VIRTUAL_GPR-1); i++) + IMLRegID newRegId = ppcImlGenContext->mappedRegs.size(); + IMLReg newReg(baseFormat, regFormat, 0, newRegId); + ppcImlGenContext->mappedRegs.try_emplace(mappedName, newReg); + return newReg; +} + +IMLName PPCRecompilerImlGen_GetRegName(ppcImlGenContext_t* ppcImlGenContext, IMLReg reg) +{ + for (auto& it : ppcImlGenContext->mappedRegs) { - if( ppcImlGenContext->mappedRegister[i] == PPCREC_NAME_NONE ) - { - ppcImlGenContext->mappedRegister[i] = mappedName; - return i; - } + if (it.second.GetRegID() == reg.GetRegID()) + return it.first; } + cemu_assert(false); return 0; } -uint32 PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName) -{ - for(uint32 i=0; i< PPC_REC_MAX_VIRTUAL_GPR; i++) - { - if( ppcImlGenContext->mappedRegister[i] == mappedName ) - { - return i; - } - } - return PPC_REC_INVALID_REGISTER; -} - uint32 PPCRecompilerImlGen_getAndLockFreeTemporaryFPR(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName) { - if( mappedName == PPCREC_NAME_NONE ) - { - debug_printf("PPCRecompilerImlGen_getAndLockFreeTemporaryFPR(): Invalid mappedName parameter\n"); - return PPC_REC_INVALID_REGISTER; - } - for(uint32 i=0; i<255; i++) - { - if( ppcImlGenContext->mappedFPRRegister[i] == PPCREC_NAME_NONE ) - { - ppcImlGenContext->mappedFPRRegister[i] = mappedName; - return i; - } - } + DEBUG_BREAK; + //if( mappedName == PPCREC_NAME_NONE ) + //{ + // debug_printf("PPCRecompilerImlGen_getAndLockFreeTemporaryFPR(): Invalid mappedName parameter\n"); + // return PPC_REC_INVALID_REGISTER; + //} + //for(uint32 i=0; i<255; i++) + //{ + // if( ppcImlGenContext->mappedFPRRegister[i] == PPCREC_NAME_NONE ) + // { + // ppcImlGenContext->mappedFPRRegister[i] = mappedName; + // return i; + // } + //} return 0; } uint32 PPCRecompilerImlGen_findFPRRegisterByMappedName(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName) { - for(uint32 i=0; i<255; i++) - { - if( ppcImlGenContext->mappedFPRRegister[i] == mappedName ) - { - return i; - } - } + DEBUG_BREAK; + //for(uint32 i=0; i<255; i++) + //{ + // if( ppcImlGenContext->mappedFPRRegister[i] == mappedName ) + // { + // return i; + // } + //} return PPC_REC_INVALID_REGISTER; } -/* - * Loads a PPC gpr into any of the available IML registers - * If loadNew is false, it will reuse already loaded instances - */ -uint32 PPCRecompilerImlGen_loadRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName, bool loadNew) +IMLReg PPCRecompilerImlGen_loadRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName) { - if( loadNew == false ) - { - uint32 loadedRegisterIndex = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, mappedName); - if( loadedRegisterIndex != PPC_REC_INVALID_REGISTER ) - return loadedRegisterIndex; - } - uint32 registerIndex = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, mappedName); - return registerIndex; + return PPCRecompilerImlGen_LookupReg(ppcImlGenContext, mappedName, IMLRegFormat::I32); } -/* - * Reuse already loaded register if present - * Otherwise create new IML register and map the name. The register contents will be undefined - */ -uint32 PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName) +IMLReg _GetRegGPR(ppcImlGenContext_t* ppcImlGenContext, uint32 index) { - uint32 loadedRegisterIndex = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, mappedName); - if( loadedRegisterIndex != PPC_REC_INVALID_REGISTER ) - return loadedRegisterIndex; - uint32 registerIndex = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, mappedName); - return registerIndex; + cemu_assert_debug(index < 32); + return PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + index); +} + +IMLReg _GetRegCR(ppcImlGenContext_t* ppcImlGenContext, uint32 index) +{ + cemu_assert_debug(index < 32); + return PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_CR + index); +} + +IMLReg _GetRegCR(ppcImlGenContext_t* ppcImlGenContext, uint8 crReg, uint8 crBit) +{ + cemu_assert_debug(crReg < 8); + cemu_assert_debug(crBit < 4); + return _GetRegCR(ppcImlGenContext, (crReg * 4) + crBit); +} + +IMLReg _GetRegTemporary(ppcImlGenContext_t* ppcImlGenContext, uint32 index) +{ + cemu_assert_debug(index < 4); + return PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + index); +} + +// get throw-away register. Only valid for the scope of a single translated instruction +// be careful to not collide with manually loaded temporary register +IMLReg _GetRegTemporaryS8(ppcImlGenContext_t* ppcImlGenContext, uint32 index) +{ + cemu_assert_debug(index < 4); + return PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + index); } /* * Loads a PPC fpr into any of the available IML FPU registers * If loadNew is false, it will check first if the fpr is already loaded into any IML register */ -uint32 PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName, bool loadNew) +IMLReg PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName, bool loadNew) { - if( loadNew == false ) - { - uint32 loadedRegisterIndex = PPCRecompilerImlGen_findFPRRegisterByMappedName(ppcImlGenContext, mappedName); - if( loadedRegisterIndex != PPC_REC_INVALID_REGISTER ) - return loadedRegisterIndex; - } - uint32 registerIndex = PPCRecompilerImlGen_getAndLockFreeTemporaryFPR(ppcImlGenContext, mappedName); - return registerIndex; + return PPCRecompilerImlGen_LookupReg(ppcImlGenContext, mappedName, IMLRegFormat::F64); } /* * Checks if a PPC fpr register is already loaded into any IML register - * If no, it will create a new undefined temporary IML FPU register and map the name (effectively overwriting the old ppc register) + * If not, it will create a new undefined temporary IML FPU register and map the name (effectively overwriting the old ppc register) */ -uint32 PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName) +IMLReg PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName) { - uint32 loadedRegisterIndex = PPCRecompilerImlGen_findFPRRegisterByMappedName(ppcImlGenContext, mappedName); - if( loadedRegisterIndex != PPC_REC_INVALID_REGISTER ) - return loadedRegisterIndex; - uint32 registerIndex = PPCRecompilerImlGen_getAndLockFreeTemporaryFPR(ppcImlGenContext, mappedName); - return registerIndex; -} - -void PPCRecompilerImlGen_TW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ -//#ifdef CEMU_DEBUG_ASSERT -// PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, 0); -//#endif - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_LEAVE, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, 0); -} - -bool PPCRecompilerImlGen_MTSPR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - uint32 rD, spr1, spr2, spr; - PPC_OPC_TEMPL_XO(opcode, rD, spr1, spr2); - spr = spr1 | (spr2<<5); - if (spr == SPR_CTR || spr == SPR_LR) - { - uint32 gprReg = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0 + rD); - if (gprReg == PPC_REC_INVALID_REGISTER) - gprReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rD); - uint32 sprReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + spr); - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, sprReg, gprReg); - } - else if (spr >= SPR_UGQR0 && spr <= SPR_UGQR7) - { - uint32 gprReg = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0 + rD); - if (gprReg == PPC_REC_INVALID_REGISTER) - gprReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rD); - uint32 sprReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + spr); - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, sprReg, gprReg); - ppcImlGenContext->tracking.modifiesGQR[spr - SPR_UGQR0] = true; - } - else - return false; - return true; -} - -bool PPCRecompilerImlGen_MFSPR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - uint32 rD, spr1, spr2, spr; - PPC_OPC_TEMPL_XO(opcode, rD, spr1, spr2); - spr = spr1 | (spr2<<5); - if (spr == SPR_LR || spr == SPR_CTR) - { - uint32 sprReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + spr); - uint32 gprReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0 + rD); - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprReg, sprReg); - } - else if (spr >= SPR_UGQR0 && spr <= SPR_UGQR7) - { - uint32 sprReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + spr); - uint32 gprReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0 + rD); - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprReg, sprReg); - } - else - return false; - return true; -} - -bool PPCRecompilerImlGen_MFTB(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - uint32 rD, spr1, spr2, spr; - PPC_OPC_TEMPL_XO(opcode, rD, spr1, spr2); - spr = spr1 | (spr2<<5); - - if (spr == 268 || spr == 269) - { - // TBL / TBU - uint32 param2 = spr | (rD << 16); - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_MFTB, ppcImlGenContext->ppcAddressOfCurrentInstruction, param2, 0); - return true; - } - return false; -} - -bool PPCRecompilerImlGen_MFCR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rD, rA, rB; - PPC_OPC_TEMPL_X(opcode, rD, rA, rB); - uint32 gprReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0 + rD); - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_MFCR, gprReg, 0, 0, false, false, PPC_REC_INVALID_REGISTER, 0); - return true; -} - -bool PPCRecompilerImlGen_MTCRF(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - uint32 rS; - uint32 crMask; - PPC_OPC_TEMPL_XFX(opcode, rS, crMask); - uint32 gprReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0 + rS); - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_MTCRF, gprReg, crMask, 0, false, false, PPC_REC_INVALID_REGISTER, 0); - return true; -} - -void PPCRecompilerImlGen_CMP(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - uint32 cr; - int rA, rB; - PPC_OPC_TEMPL_X(opcode, cr, rA, rB); - cr >>= 2; - uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_COMPARE_SIGNED, gprRegisterA, gprRegisterB, cr, PPCREC_CR_MODE_COMPARE_SIGNED); -} - -void PPCRecompilerImlGen_CMPL(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - uint32 cr; - int rA, rB; - PPC_OPC_TEMPL_X(opcode, cr, rA, rB); - cr >>= 2; - uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_COMPARE_UNSIGNED, gprRegisterA, gprRegisterB, cr, PPCREC_CR_MODE_COMPARE_UNSIGNED); -} - -void PPCRecompilerImlGen_CMPI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - uint32 cr; - int rA; - uint32 imm; - PPC_OPC_TEMPL_D_SImm(opcode, cr, rA, imm); - cr >>= 2; - sint32 b = imm; - // load gpr into register - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_COMPARE_SIGNED, gprRegister, b, 0, false, false, cr, PPCREC_CR_MODE_COMPARE_SIGNED); -} - -void PPCRecompilerImlGen_CMPLI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - uint32 cr; - int rA; - uint32 imm; - PPC_OPC_TEMPL_D_UImm(opcode, cr, rA, imm); - cr >>= 2; - uint32 b = imm; - // load gpr into register - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_COMPARE_UNSIGNED, gprRegister, (sint32)b, 0, false, false, cr, PPCREC_CR_MODE_COMPARE_UNSIGNED); + return PPCRecompilerImlGen_LookupReg(ppcImlGenContext, mappedName, IMLRegFormat::F64); } bool PPCRecompiler_canInlineFunction(MPTR functionPtr, sint32* functionInstructionCount) { for (sint32 i = 0; i < 6; i++) { - uint32 opcode = memory_readU32(functionPtr+i*4); + uint32 opcode = memory_readU32(functionPtr + i * 4); switch ((opcode >> 26)) { case 14: // ADDI @@ -611,18 +312,200 @@ void PPCRecompiler_generateInlinedCode(ppcImlGenContext_t* ppcImlGenContext, uin { for (sint32 i = 0; i < instructionCount; i++) { - ppcImlGenContext->ppcAddressOfCurrentInstruction = startAddress + i*4; + ppcImlGenContext->ppcAddressOfCurrentInstruction = startAddress + i * 4; ppcImlGenContext->cyclesSinceLastBranch++; if (PPCRecompiler_decodePPCInstruction(ppcImlGenContext)) { - assert_dbg(); + cemu_assert_suspicious(); } } // add range - ppcRecRange_t recRange; - recRange.ppcAddress = startAddress; - recRange.ppcSize = instructionCount*4 + 4; // + 4 because we have to include the BLR - ppcImlGenContext->functionRef->list_ranges.push_back(recRange); + cemu_assert_unimplemented(); + //ppcRecRange_t recRange; + //recRange.ppcAddress = startAddress; + //recRange.ppcSize = instructionCount*4 + 4; // + 4 because we have to include the BLR + //ppcImlGenContext->functionRef->list_ranges.push_back(recRange); +} + +// for handling RC bit of many instructions +void PPCImlGen_UpdateCR0(ppcImlGenContext_t* ppcImlGenContext, IMLReg regR) +{ + IMLReg crBitRegLT = _GetRegCR(ppcImlGenContext, 0, Espresso::CR_BIT::CR_BIT_INDEX_LT); + IMLReg crBitRegGT = _GetRegCR(ppcImlGenContext, 0, Espresso::CR_BIT::CR_BIT_INDEX_GT); + IMLReg crBitRegEQ = _GetRegCR(ppcImlGenContext, 0, Espresso::CR_BIT::CR_BIT_INDEX_EQ); + // todo - SO bit + + ppcImlGenContext->emitInst().make_compare_s32(regR, 0, crBitRegLT, IMLCondition::SIGNED_LT); + ppcImlGenContext->emitInst().make_compare_s32(regR, 0, crBitRegGT, IMLCondition::SIGNED_GT); + ppcImlGenContext->emitInst().make_compare_s32(regR, 0, crBitRegEQ, IMLCondition::EQ); + + //ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, crBitRegSO, 0); // todo - copy from XER + + //ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, registerR, registerR, 0, PPCREC_CR_MODE_LOGICAL); +} + +void PPCRecompilerImlGen_TW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) +{ + // split before and after to make sure the macro is in an isolated segment that we can make enterable + PPCIMLGen_CreateSplitSegmentAtEnd(*ppcImlGenContext, *ppcImlGenContext->currentBasicBlock); + ppcImlGenContext->currentOutputSegment->SetEnterable(ppcImlGenContext->ppcAddressOfCurrentInstruction); + PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext)->make_macro(PPCREC_IML_MACRO_LEAVE, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, 0, IMLREG_INVALID); + IMLSegment* middleSeg = PPCIMLGen_CreateSplitSegmentAtEnd(*ppcImlGenContext, *ppcImlGenContext->currentBasicBlock); + middleSeg->SetLinkBranchTaken(nullptr); + middleSeg->SetLinkBranchNotTaken(nullptr); +} + +bool PPCRecompilerImlGen_MTSPR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) +{ + uint32 rD, spr1, spr2, spr; + PPC_OPC_TEMPL_XO(opcode, rD, spr1, spr2); + spr = spr1 | (spr2<<5); + IMLReg gprReg = _GetRegGPR(ppcImlGenContext, rD); + if (spr == SPR_CTR || spr == SPR_LR) + { + IMLReg sprReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + spr); + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, sprReg, gprReg); + } + else if (spr >= SPR_UGQR0 && spr <= SPR_UGQR7) + { + IMLReg sprReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + spr); + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, sprReg, gprReg); + ppcImlGenContext->tracking.modifiesGQR[spr - SPR_UGQR0] = true; + } + else + return false; + return true; +} + +bool PPCRecompilerImlGen_MFSPR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) +{ + uint32 rD, spr1, spr2, spr; + PPC_OPC_TEMPL_XO(opcode, rD, spr1, spr2); + spr = spr1 | (spr2<<5); + IMLReg gprReg = _GetRegGPR(ppcImlGenContext, rD); + if (spr == SPR_LR || spr == SPR_CTR) + { + IMLReg sprReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + spr); + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, gprReg, sprReg); + } + else if (spr >= SPR_UGQR0 && spr <= SPR_UGQR7) + { + IMLReg sprReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + spr); + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, gprReg, sprReg); + } + else + return false; + return true; +} + +bool PPCRecompilerImlGen_MFTB(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) +{ + printf("PPCRecompilerImlGen_MFTB(): Not supported\n"); + return false; + + uint32 rD, spr1, spr2, spr; + PPC_OPC_TEMPL_XO(opcode, rD, spr1, spr2); + spr = spr1 | (spr2<<5); + + if (spr == 268 || spr == 269) + { + // TBL / TBU + uint32 param2 = spr | (rD << 16); + ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_MFTB, ppcImlGenContext->ppcAddressOfCurrentInstruction, param2, 0, IMLREG_INVALID); + IMLSegment* middleSeg = PPCIMLGen_CreateSplitSegmentAtEnd(*ppcImlGenContext, *ppcImlGenContext->currentBasicBlock); + + return true; + } + return false; +} + +bool PPCRecompilerImlGen_MFCR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) +{ + sint32 rD, rA, rB; + PPC_OPC_TEMPL_X(opcode, rD, rA, rB); + IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); + ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regD, 0); + for (sint32 i = 0; i < 32; i++) + { + IMLReg regCrBit = _GetRegCR(ppcImlGenContext, i); + cemu_assert_debug(regCrBit.GetRegFormat() == IMLRegFormat::I32); // addition is only allowed between same-format regs + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_LEFT_SHIFT, regD, regD, 1); + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_ADD, regD, regD, regCrBit); + } + return true; +} + +bool PPCRecompilerImlGen_MTCRF(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) +{ + uint32 rS; + uint32 crMask; + PPC_OPC_TEMPL_XFX(opcode, rS, crMask); + IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); + IMLReg regTmp = _GetRegTemporary(ppcImlGenContext, 0); + uint32 crBitMask = ppc_MTCRFMaskToCRBitMask(crMask); + for (sint32 f = 0; f < 32; f++) + { + if(((crBitMask >> f) & 1) == 0) + continue; + IMLReg regCrBit = _GetRegCR(ppcImlGenContext, f); + cemu_assert_debug(regCrBit.GetRegFormat() == IMLRegFormat::I32); + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_RIGHT_SHIFT_U, regTmp, regS, (31-f)); + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_AND, regCrBit, regTmp, 1); + } + return true; +} + +void PPCRecompilerImlGen_CMP(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, bool isUnsigned) +{ + uint32 cr; + int rA, rB; + PPC_OPC_TEMPL_X(opcode, cr, rA, rB); + cr >>= 2; + + IMLReg gprRegisterA = _GetRegGPR(ppcImlGenContext, rA); + IMLReg gprRegisterB = _GetRegGPR(ppcImlGenContext, rB); + IMLReg regXerSO = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_SO); + + IMLReg crBitRegLT = _GetRegCR(ppcImlGenContext, cr, Espresso::CR_BIT::CR_BIT_INDEX_LT); + IMLReg crBitRegGT = _GetRegCR(ppcImlGenContext, cr, Espresso::CR_BIT::CR_BIT_INDEX_GT); + IMLReg crBitRegEQ = _GetRegCR(ppcImlGenContext, cr, Espresso::CR_BIT::CR_BIT_INDEX_EQ); + IMLReg crBitRegSO = _GetRegCR(ppcImlGenContext, cr, Espresso::CR_BIT::CR_BIT_INDEX_SO); + + ppcImlGenContext->emitInst().make_compare(gprRegisterA, gprRegisterB, crBitRegLT, isUnsigned ? IMLCondition::UNSIGNED_LT : IMLCondition::SIGNED_LT); + ppcImlGenContext->emitInst().make_compare(gprRegisterA, gprRegisterB, crBitRegGT, isUnsigned ? IMLCondition::UNSIGNED_GT : IMLCondition::SIGNED_GT); + ppcImlGenContext->emitInst().make_compare(gprRegisterA, gprRegisterB, crBitRegEQ, IMLCondition::EQ); + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, crBitRegSO, regXerSO); +} + +bool PPCRecompilerImlGen_CMPI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, bool isUnsigned) +{ + uint32 cr; + int rA; + uint32 imm; + if (isUnsigned) + { + PPC_OPC_TEMPL_D_UImm(opcode, cr, rA, imm); + } + else + { + PPC_OPC_TEMPL_D_SImm(opcode, cr, rA, imm); + } + cr >>= 2; + + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + IMLReg regXerSO = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_SO); + + IMLReg crBitRegLT = _GetRegCR(ppcImlGenContext, cr, Espresso::CR_BIT::CR_BIT_INDEX_LT); + IMLReg crBitRegGT = _GetRegCR(ppcImlGenContext, cr, Espresso::CR_BIT::CR_BIT_INDEX_GT); + IMLReg crBitRegEQ = _GetRegCR(ppcImlGenContext, cr, Espresso::CR_BIT::CR_BIT_INDEX_EQ); + IMLReg crBitRegSO = _GetRegCR(ppcImlGenContext, cr, Espresso::CR_BIT::CR_BIT_INDEX_SO); + + ppcImlGenContext->emitInst().make_compare_s32(regA, (sint32)imm, crBitRegLT, isUnsigned ? IMLCondition::UNSIGNED_LT : IMLCondition::SIGNED_LT); + ppcImlGenContext->emitInst().make_compare_s32(regA, (sint32)imm, crBitRegGT, isUnsigned ? IMLCondition::UNSIGNED_GT : IMLCondition::SIGNED_GT); + ppcImlGenContext->emitInst().make_compare_s32(regA, (sint32)imm, crBitRegEQ, IMLCondition::EQ); + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, crBitRegSO, regXerSO); + + return true; } bool PPCRecompilerImlGen_B(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) @@ -637,43 +520,28 @@ bool PPCRecompilerImlGen_B(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) if( opcode&PPC_OPC_LK ) { // function call - // check if function can be inlined - sint32 inlineFuncInstructionCount = 0; - if (PPCRecompiler_canInlineFunction(jumpAddressDest, &inlineFuncInstructionCount)) - { - // generate NOP iml instead of BL macro (this assures that segment PPC range remains intact) - PPCRecompilerImlGen_generateNewInstruction_noOp(ppcImlGenContext, NULL); + ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_BL, ppcImlGenContext->ppcAddressOfCurrentInstruction, jumpAddressDest, ppcImlGenContext->cyclesSinceLastBranch, IMLREG_INVALID); //cemuLog_log(LogType::Force, "Inline func 0x{:08x} at {:08x}", jumpAddressDest, ppcImlGenContext->ppcAddressOfCurrentInstruction); - uint32* prevInstructionPtr = ppcImlGenContext->currentInstruction; - ppcImlGenContext->currentInstruction = (uint32*)memory_getPointerFromVirtualOffset(jumpAddressDest); - PPCRecompiler_generateInlinedCode(ppcImlGenContext, jumpAddressDest, inlineFuncInstructionCount); - ppcImlGenContext->currentInstruction = prevInstructionPtr; - return true; - } - // generate funtion call instructions - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_BL, ppcImlGenContext->ppcAddressOfCurrentInstruction, jumpAddressDest, ppcImlGenContext->cyclesSinceLastBranch); - PPCRecompilerImlGen_generateNewInstruction_ppcEnter(ppcImlGenContext, ppcImlGenContext->ppcAddressOfCurrentInstruction+4); return true; } // is jump destination within recompiled function? - if( jumpAddressDest >= ppcImlGenContext->functionRef->ppcAddress && jumpAddressDest < (ppcImlGenContext->functionRef->ppcAddress + ppcImlGenContext->functionRef->ppcSize) ) - { - // generate instruction - PPCRecompilerImlGen_generateNewInstruction_jump(ppcImlGenContext, NULL, jumpAddressDest); - } + if (ppcImlGenContext->boundaryTracker->ContainsAddress(jumpAddressDest)) + ppcImlGenContext->emitInst().make_jump(); else - { - // todo: Inline this jump destination if possible (in many cases it's a bunch of GPR/FPR store instructions + BLR) - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_B_FAR, ppcImlGenContext->ppcAddressOfCurrentInstruction, jumpAddressDest, ppcImlGenContext->cyclesSinceLastBranch); - } + ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_B_FAR, ppcImlGenContext->ppcAddressOfCurrentInstruction, jumpAddressDest, ppcImlGenContext->cyclesSinceLastBranch, IMLREG_INVALID); return true; } bool PPCRecompilerImlGen_BC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { + PPCIMLGen_AssertIfNotLastSegmentInstruction(*ppcImlGenContext); + uint32 BO, BI, BD; PPC_OPC_TEMPL_B(opcode, BO, BI, BD); + // decodeOp_BC(uint32 opcode, uint32& BD, BOField& BO, uint32& BI, bool& AA, bool& LK) + Espresso::BOField boField(BO); + uint32 crRegister = BI/4; uint32 crBit = BI%4; uint32 jumpCondition = 0; @@ -682,6 +550,10 @@ bool PPCRecompilerImlGen_BC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) bool decrementerMustBeZero = (BO&2)!=0; // bit set -> branch if CTR = 0, bit not set -> branch if CTR != 0 bool ignoreCondition = (BO&16)!=0; + IMLReg regCRBit; + if (!ignoreCondition) + regCRBit = _GetRegCR(ppcImlGenContext, crRegister, crBit); + uint32 jumpAddressDest = BD; if( (opcode&PPC_OPC_AA) == 0 ) { @@ -690,37 +562,15 @@ bool PPCRecompilerImlGen_BC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) if( opcode&PPC_OPC_LK ) { + if (useDecrementer) + return false; // conditional function calls are not supported if( ignoreCondition == false ) { - // generate jump condition - if( conditionMustBeTrue ) - { - if( crBit == 0 ) - jumpCondition = PPCREC_JUMP_CONDITION_GE; - else if( crBit == 1 ) - jumpCondition = PPCREC_JUMP_CONDITION_LE; - else if( crBit == 2 ) - jumpCondition = PPCREC_JUMP_CONDITION_NE; - else if( crBit == 3 ) - jumpCondition = PPCREC_JUMP_CONDITION_NSUMMARYOVERFLOW; - } - else - { - if( crBit == 0 ) - jumpCondition = PPCREC_JUMP_CONDITION_L; - else if( crBit == 1 ) - jumpCondition = PPCREC_JUMP_CONDITION_G; - else if( crBit == 2 ) - jumpCondition = PPCREC_JUMP_CONDITION_E; - else if( crBit == 3 ) - jumpCondition = PPCREC_JUMP_CONDITION_SUMMARYOVERFLOW; - } - // generate instruction - //PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, 0); - PPCRecompilerImlGen_generateNewInstruction_conditionalJump(ppcImlGenContext, ppcImlGenContext->ppcAddressOfCurrentInstruction+4, jumpCondition, crRegister, crBit, !conditionMustBeTrue); - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_BL, ppcImlGenContext->ppcAddressOfCurrentInstruction, jumpAddressDest, ppcImlGenContext->cyclesSinceLastBranch); - PPCRecompilerImlGen_generateNewInstruction_ppcEnter(ppcImlGenContext, ppcImlGenContext->ppcAddressOfCurrentInstruction+4); + PPCBasicBlockInfo* currentBasicBlock = ppcImlGenContext->currentBasicBlock; + IMLSegment* blSeg = PPCIMLGen_CreateNewSegmentAsBranchTarget(*ppcImlGenContext, *currentBasicBlock); + ppcImlGenContext->emitInst().make_conditional_jump(regCRBit, conditionMustBeTrue); + blSeg->AppendInstruction()->make_macro(PPCREC_IML_MACRO_BL, ppcImlGenContext->ppcAddressOfCurrentInstruction, jumpAddressDest, ppcImlGenContext->cyclesSinceLastBranch, IMLREG_INVALID); return true; } return false; @@ -730,12 +580,11 @@ bool PPCRecompilerImlGen_BC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { if( ignoreCondition == false ) return false; // not supported for the moment - uint32 ctrRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0+SPR_CTR, false); - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_SUB, ctrRegister, 1, 0, false, false, PPCREC_CR_REG_TEMP, PPCREC_CR_MODE_ARITHMETIC); - if( decrementerMustBeZero ) - PPCRecompilerImlGen_generateNewInstruction_conditionalJump(ppcImlGenContext, jumpAddressDest, PPCREC_JUMP_CONDITION_E, PPCREC_CR_REG_TEMP, 0, false); - else - PPCRecompilerImlGen_generateNewInstruction_conditionalJump(ppcImlGenContext, jumpAddressDest, PPCREC_JUMP_CONDITION_NE, PPCREC_CR_REG_TEMP, 0, false); + IMLReg ctrRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0+SPR_CTR); + IMLReg tmpBoolReg = _GetRegTemporaryS8(ppcImlGenContext, 1); + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_SUB, ctrRegister, ctrRegister, 1); + ppcImlGenContext->emitInst().make_compare_s32(ctrRegister, 0, tmpBoolReg, decrementerMustBeZero ? IMLCondition::EQ : IMLCondition::NEQ); + ppcImlGenContext->emitInst().make_conditional_jump(tmpBoolReg, true); return true; } else @@ -743,219 +592,90 @@ bool PPCRecompilerImlGen_BC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) if( ignoreCondition ) { // branch always, no condition and no decrementer - debugBreakpoint(); - crRegister = PPC_REC_INVALID_REGISTER; // not necessary but lets optimizer know we dont care for cr register on this instruction + // not supported + return false; } else { - // generate jump condition - if( conditionMustBeTrue ) - { - if( crBit == 0 ) - jumpCondition = PPCREC_JUMP_CONDITION_GE; - else if( crBit == 1 ) - jumpCondition = PPCREC_JUMP_CONDITION_LE; - else if( crBit == 2 ) - jumpCondition = PPCREC_JUMP_CONDITION_NE; - else if( crBit == 3 ) - jumpCondition = PPCREC_JUMP_CONDITION_NSUMMARYOVERFLOW; - } - else - { - if( crBit == 0 ) - jumpCondition = PPCREC_JUMP_CONDITION_L; - else if( crBit == 1 ) - jumpCondition = PPCREC_JUMP_CONDITION_G; - else if( crBit == 2 ) - jumpCondition = PPCREC_JUMP_CONDITION_E; - else if( crBit == 3 ) - jumpCondition = PPCREC_JUMP_CONDITION_SUMMARYOVERFLOW; - } - - if (jumpAddressDest >= ppcImlGenContext->functionRef->ppcAddress && jumpAddressDest < (ppcImlGenContext->functionRef->ppcAddress + ppcImlGenContext->functionRef->ppcSize)) + if (ppcImlGenContext->boundaryTracker->ContainsAddress(jumpAddressDest)) { // near jump - PPCRecompilerImlGen_generateNewInstruction_conditionalJump(ppcImlGenContext, jumpAddressDest, jumpCondition, crRegister, crBit, conditionMustBeTrue); + ppcImlGenContext->emitInst().make_conditional_jump(regCRBit, conditionMustBeTrue); } else { // far jump - PPCRecompilerImlGen_generateNewInstruction_conditionalJump(ppcImlGenContext, ppcImlGenContext->ppcAddressOfCurrentInstruction + 4, jumpCondition, crRegister, crBit, !conditionMustBeTrue); - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_B_FAR, ppcImlGenContext->ppcAddressOfCurrentInstruction, jumpAddressDest, ppcImlGenContext->cyclesSinceLastBranch); - PPCRecompilerImlGen_generateNewInstruction_ppcEnter(ppcImlGenContext, ppcImlGenContext->ppcAddressOfCurrentInstruction + 4); + debug_printf("PPCRecompilerImlGen_BC(): Far jump not supported yet"); + return false; } } } return true; } -bool PPCRecompilerImlGen_BCLR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) +// BCCTR or BCLR +bool PPCRecompilerImlGen_BCSPR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, uint32 sprReg) { - uint32 BO, BI, BD; - PPC_OPC_TEMPL_XL(opcode, BO, BI, BD); + PPCIMLGen_AssertIfNotLastSegmentInstruction(*ppcImlGenContext); + Espresso::BOField BO; + uint32 BI; + bool LK; + Espresso::decodeOp_BCSPR(opcode, BO, BI, LK); uint32 crRegister = BI/4; uint32 crBit = BI%4; - uint32 jumpCondition = 0; + IMLReg regCRBit; + if (!BO.conditionIgnore()) + regCRBit = _GetRegCR(ppcImlGenContext, crRegister, crBit); - bool conditionMustBeTrue = (BO&8)!=0; - bool useDecrementer = (BO&4)==0; // bit not set -> decrement - bool decrementerMustBeZero = (BO&2)!=0; // bit set -> branch if CTR = 0, bit not set -> branch if CTR != 0 - bool ignoreCondition = (BO&16)!=0; - bool saveLR = (opcode&PPC_OPC_LK)!=0; - // since we skip this instruction if the condition is true, we need to invert the logic - bool invertedConditionMustBeTrue = !conditionMustBeTrue; - if( useDecrementer ) + IMLReg branchDestReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + sprReg); + if (LK) { - cemu_assert_debug(false); - return false; // unsupported + if (sprReg == SPR_LR) + { + // if the branch target is LR, then preserve it in a temporary + cemu_assert_suspicious(); // this case needs testing + IMLReg tmpRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY); + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, tmpRegister, branchDestReg); + branchDestReg = tmpRegister; + } + IMLReg registerLR = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + SPR_LR); + ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, registerLR, ppcImlGenContext->ppcAddressOfCurrentInstruction + 4); + } + + if (!BO.decrementerIgnore()) + { + cemu_assert_unimplemented(); + return false; + } + else if (!BO.conditionIgnore()) + { + // no decrementer but CR check + cemu_assert_debug(ppcImlGenContext->currentBasicBlock->hasContinuedFlow); + cemu_assert_debug(!ppcImlGenContext->currentBasicBlock->hasBranchTarget); + PPCBasicBlockInfo* currentBasicBlock = ppcImlGenContext->currentBasicBlock; + IMLSegment* bctrSeg = PPCIMLGen_CreateNewSegmentAsBranchTarget(*ppcImlGenContext, *currentBasicBlock); + ppcImlGenContext->emitInst().make_conditional_jump(regCRBit, !BO.conditionInverted()); + bctrSeg->AppendInstruction()->make_macro(PPCREC_IML_MACRO_B_TO_REG, 0, 0, 0, branchDestReg); } else { - if( ignoreCondition ) - { - // store LR - if( saveLR ) - { - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_BLRL, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, ppcImlGenContext->cyclesSinceLastBranch); - PPCRecompilerImlGen_generateNewInstruction_ppcEnter(ppcImlGenContext, ppcImlGenContext->ppcAddressOfCurrentInstruction+4); - } - else - { - // branch always, no condition and no decrementer - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_BLR, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, ppcImlGenContext->cyclesSinceLastBranch); - } - } - else - { - // store LR - if( saveLR ) - { - uint32 registerLR = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_SPR0+SPR_LR); - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, registerLR, (ppcImlGenContext->ppcAddressOfCurrentInstruction+4)&0x7FFFFFFF, 0, false, false, PPC_REC_INVALID_REGISTER, 0); - } - // generate jump condition - if( invertedConditionMustBeTrue ) - { - if( crBit == 0 ) - jumpCondition = PPCREC_JUMP_CONDITION_L; - else if( crBit == 1 ) - jumpCondition = PPCREC_JUMP_CONDITION_G; - else if( crBit == 2 ) - jumpCondition = PPCREC_JUMP_CONDITION_E; - else if( crBit == 3 ) - jumpCondition = PPCREC_JUMP_CONDITION_SUMMARYOVERFLOW; - } - else - { - if( crBit == 0 ) - jumpCondition = PPCREC_JUMP_CONDITION_GE; - else if( crBit == 1 ) - jumpCondition = PPCREC_JUMP_CONDITION_LE; - else if( crBit == 2 ) - jumpCondition = PPCREC_JUMP_CONDITION_NE; - else if( crBit == 3 ) - jumpCondition = PPCREC_JUMP_CONDITION_NSUMMARYOVERFLOW; - } - // jump if BCLR condition NOT met (jump to jumpmark of next instruction, essentially skipping current instruction) - PPCRecompilerImlGen_generateNewInstruction_conditionalJump(ppcImlGenContext, ppcImlGenContext->ppcAddressOfCurrentInstruction+4, jumpCondition, crRegister, crBit, invertedConditionMustBeTrue); - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_BLR, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, ppcImlGenContext->cyclesSinceLastBranch); - } - } - return true; -} - -bool PPCRecompilerImlGen_BCCTR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - uint32 BO, BI, BD; - PPC_OPC_TEMPL_XL(opcode, BO, BI, BD); - - uint32 crRegister = BI/4; - uint32 crBit = BI%4; - - uint32 jumpCondition = 0; - - bool conditionMustBeTrue = (BO&8)!=0; - bool useDecrementer = (BO&4)==0; // bit not set -> decrement - bool decrementerMustBeZero = (BO&2)!=0; // bit set -> branch if CTR = 0, bit not set -> branch if CTR != 0 - bool ignoreCondition = (BO&16)!=0; - bool saveLR = (opcode&PPC_OPC_LK)!=0; - // since we skip this instruction if the condition is true, we need to invert the logic - bool invertedConditionMustBeTrue = !conditionMustBeTrue; - if( useDecrementer ) - { - assert_dbg(); - // if added, dont forget inverted logic - debug_printf("Rec: BCLR unsupported decrementer\n"); - return false; // unsupported - } - else - { - if( ignoreCondition ) - { - // store LR - if( saveLR ) - { - uint32 registerLR = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_SPR0+SPR_LR); - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, registerLR, (ppcImlGenContext->ppcAddressOfCurrentInstruction+4)&0x7FFFFFFF, 0, false, false, PPC_REC_INVALID_REGISTER, 0); - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_BCTRL, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, ppcImlGenContext->cyclesSinceLastBranch); - PPCRecompilerImlGen_generateNewInstruction_ppcEnter(ppcImlGenContext, ppcImlGenContext->ppcAddressOfCurrentInstruction+4); - } - else - { - // branch always, no condition and no decrementer - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_BCTR, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, ppcImlGenContext->cyclesSinceLastBranch); - } - } - else - { - // store LR - if( saveLR ) - { - uint32 registerLR = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_SPR0+SPR_LR); - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, registerLR, (ppcImlGenContext->ppcAddressOfCurrentInstruction+4)&0x7FFFFFFF, 0, false, false, PPC_REC_INVALID_REGISTER, 0); - } - // generate jump condition - if( invertedConditionMustBeTrue ) - { - if( crBit == 0 ) - jumpCondition = PPCREC_JUMP_CONDITION_L; - else if( crBit == 1 ) - jumpCondition = PPCREC_JUMP_CONDITION_G; - else if( crBit == 2 ) - jumpCondition = PPCREC_JUMP_CONDITION_E; - else if( crBit == 3 ) - jumpCondition = PPCREC_JUMP_CONDITION_SUMMARYOVERFLOW; - } - else - { - if( crBit == 0 ) - jumpCondition = PPCREC_JUMP_CONDITION_GE; - else if( crBit == 1 ) - jumpCondition = PPCREC_JUMP_CONDITION_LE; - else if( crBit == 2 ) - jumpCondition = PPCREC_JUMP_CONDITION_NE; - else if( crBit == 3 ) - jumpCondition = PPCREC_JUMP_CONDITION_NSUMMARYOVERFLOW; - } - // jump if BCLR condition NOT met (jump to jumpmark of next instruction, essentially skipping current instruction) - PPCRecompilerImlGen_generateNewInstruction_conditionalJump(ppcImlGenContext, ppcImlGenContext->ppcAddressOfCurrentInstruction+4, jumpCondition, crRegister, crBit, invertedConditionMustBeTrue); - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_BCTR, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, ppcImlGenContext->cyclesSinceLastBranch); - } + // branch always, no condition and no decrementer check + cemu_assert_debug(!ppcImlGenContext->currentBasicBlock->hasContinuedFlow); + cemu_assert_debug(!ppcImlGenContext->currentBasicBlock->hasBranchTarget); + ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_B_TO_REG, 0, 0, 0, branchDestReg); } return true; } bool PPCRecompilerImlGen_ISYNC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { - // does not need to be translated return true; } bool PPCRecompilerImlGen_SYNC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { - // does not need to be translated return true; } @@ -963,102 +683,12 @@ bool PPCRecompilerImlGen_ADD(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); - //hCPU->gpr[rD] = (int)hCPU->gpr[rA] + (int)hCPU->gpr[rB]; - uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( opcode&PPC_OPC_RC ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ADD, registerRD, registerRA, registerRB, 0, PPCREC_CR_MODE_LOGICAL); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ADD, registerRD, registerRA, registerRB); - } - return true; -} - -bool PPCRecompilerImlGen_ADDC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rD, rA, rB; - PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); - //hCPU->gpr[rD] = (int)hCPU->gpr[rA] + (int)hCPU->gpr[rB]; -> Update carry - uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( opcode&PPC_OPC_RC ) - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ADD_UPDATE_CARRY, registerRD, registerRA, registerRB, 0, PPCREC_CR_MODE_LOGICAL); - else - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ADD_UPDATE_CARRY, registerRD, registerRA, registerRB); - return true; -} - -bool PPCRecompilerImlGen_ADDE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rD, rA, rB; - PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); - // hCPU->gpr[rD] = hCPU->gpr[rA] + hCPU->gpr[rB] + ca; - uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( opcode&PPC_OPC_RC ) - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ADD_CARRY_UPDATE_CARRY, registerRD, registerRB, registerRA, 0, PPCREC_CR_MODE_LOGICAL); - else - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ADD_CARRY_UPDATE_CARRY, registerRD, registerRB, registerRA); - return true; -} - -bool PPCRecompilerImlGen_ADDZE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rD, rA, rB; - PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); - PPC_ASSERT(rB == 0); - //uint32 a = hCPU->gpr[rA]; - //uint32 ca = hCPU->xer_ca; - //hCPU->gpr[rD] = a + ca; - - uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); - // move rA to rD - if( registerRA != registerRD ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, registerRD, registerRA); - } - if( opcode&PPC_OPC_RC ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD_CARRY, registerRD, registerRD, 0, PPCREC_CR_MODE_LOGICAL); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD_CARRY, registerRD, registerRD); - } - return true; -} - -bool PPCRecompilerImlGen_ADDME(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rD, rA, rB; - PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); - PPC_ASSERT(rB == 0); - //uint32 a = hCPU->gpr[rA]; - //uint32 ca = hCPU->xer_ca; - //hCPU->gpr[rD] = a + ca + -1; - - uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); - // move rA to rD - if( registerRA != registerRD ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, registerRD, registerRA); - } - if( opcode&PPC_OPC_RC ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD_CARRY_ME, registerRD, registerRD, 0, PPCREC_CR_MODE_LOGICAL); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD_CARRY_ME, registerRD, registerRD); - } + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); + IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_ADD, regD, regA, regB); + if (opcode & PPC_OPC_RC) + PPCImlGen_UpdateCR0(ppcImlGenContext, regD); return true; } @@ -1067,22 +697,16 @@ bool PPCRecompilerImlGen_ADDI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcod sint32 rD, rA; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); - //hCPU->gpr[rD] = (rA ? (int)hCPU->gpr[rA] : 0) + (int)imm; - if( rA != 0 ) + IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); + if (rA != 0) { - uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // check if rD is already loaded, else use new temporary register - uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); - PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, registerRD, registerRA, imm); + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_ADD, regD, regA, imm); } else { - // rA not used, instruction is value assignment - // rD = imm - uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, registerRD, imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); + ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regD, imm); } - // never updates any cr return true; } @@ -1091,49 +715,88 @@ bool PPCRecompilerImlGen_ADDIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opco int rD, rA; uint32 imm; PPC_OPC_TEMPL_D_Shift16(opcode, rD, rA, imm); - if( rA != 0 ) + IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); + if (rA != 0) { - uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // check if rD is already loaded, else use new temporary register - uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); - PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, registerRD, registerRA, (sint32)imm); + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_ADD, regD, regA, (sint32)imm); } else { - // rA not used, instruction turns into simple value assignment - // rD = imm - uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, registerRD, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); + ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regD, (sint32)imm); } - // never updates any cr return true; } -bool PPCRecompilerImlGen_ADDIC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) +bool PPCRecompilerImlGen_ADDC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) +{ + // r = a + b -> update carry + sint32 rD, rA, rB; + PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); + IMLReg regRA = _GetRegGPR(ppcImlGenContext, rA); + IMLReg regRB = _GetRegGPR(ppcImlGenContext, rB); + IMLReg regRD = _GetRegGPR(ppcImlGenContext, rD); + IMLReg regCa = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_CA); + ppcImlGenContext->emitInst().make_r_r_r_carry(PPCREC_IML_OP_ADD, regRD, regRA, regRB, regCa); + if (opcode & PPC_OPC_RC) + PPCImlGen_UpdateCR0(ppcImlGenContext, regRD); + return true; +} + +bool PPCRecompilerImlGen_ADDIC_(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, bool updateCR0) { sint32 rD, rA; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); - // rD = rA + imm; - uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // check if rD is already loaded, else use new temporary register - uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); - PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD_UPDATE_CARRY, registerRD, registerRA, imm); - // never updates any cr + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); + IMLReg regCa = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_CA); + ppcImlGenContext->emitInst().make_r_r_s32_carry(PPCREC_IML_OP_ADD, regD, regA, (sint32)imm, regCa); + if(updateCR0) + PPCImlGen_UpdateCR0(ppcImlGenContext, regD); return true; } -bool PPCRecompilerImlGen_ADDIC_(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) +bool PPCRecompilerImlGen_ADDE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { - // this opcode is identical to ADDIC but additionally it updates CR0 - sint32 rD, rA; - uint32 imm; - PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); - // rD = rA + imm; - uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // check if rD is already loaded, else use new temporary register - uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); - PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD_UPDATE_CARRY, registerRD, registerRA, imm, 0, PPCREC_CR_MODE_LOGICAL); + // r = a + b + carry -> update carry + sint32 rD, rA, rB; + PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); + IMLReg regRA = _GetRegGPR(ppcImlGenContext, rA); + IMLReg regRB = _GetRegGPR(ppcImlGenContext, rB); + IMLReg regRD = _GetRegGPR(ppcImlGenContext, rD); + IMLReg regCa = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_CA); + ppcImlGenContext->emitInst().make_r_r_r_carry(PPCREC_IML_OP_ADD_WITH_CARRY, regRD, regRA, regRB, regCa); + if (opcode & PPC_OPC_RC) + PPCImlGen_UpdateCR0(ppcImlGenContext, regRD); + return true; +} + +bool PPCRecompilerImlGen_ADDZE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) +{ + // r = a + carry -> update carry + sint32 rD, rA, rB; + PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); + IMLReg regRA = _GetRegGPR(ppcImlGenContext, rA); + IMLReg regRD = _GetRegGPR(ppcImlGenContext, rD); + IMLReg regCa = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_CA); + ppcImlGenContext->emitInst().make_r_r_s32_carry(PPCREC_IML_OP_ADD_WITH_CARRY, regRD, regRA, 0, regCa); + if (opcode & PPC_OPC_RC) + PPCImlGen_UpdateCR0(ppcImlGenContext, regRD); + return true; +} + +bool PPCRecompilerImlGen_ADDME(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) +{ + // r = a + 0xFFFFFFFF + carry -> update carry + sint32 rD, rA, rB; + PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); + IMLReg regRA = _GetRegGPR(ppcImlGenContext, rA); + IMLReg regRD = _GetRegGPR(ppcImlGenContext, rD); + IMLReg regCa = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_CA); + ppcImlGenContext->emitInst().make_r_r_s32_carry(PPCREC_IML_OP_ADD_WITH_CARRY, regRD, regRA, -1, regCa); + if (opcode & PPC_OPC_RC) + PPCImlGen_UpdateCR0(ppcImlGenContext, regRD); return true; } @@ -1141,74 +804,79 @@ bool PPCRecompilerImlGen_SUBF(ppcImlGenContext_t* ppcImlGenContext, uint32 opcod { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); - // hCPU->gpr[rD] = ~hCPU->gpr[rA] + hCPU->gpr[rB] + 1; - // rD = rB - rA - uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( opcode&PPC_OPC_RC ) - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SUB, registerRD, registerRB, registerRA, 0, PPCREC_CR_MODE_LOGICAL); - else - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SUB, registerRD, registerRB, registerRA); + // rD = ~rA + rB + 1 + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); + IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_SUB, regD, regB, regA); + if ((opcode & PPC_OPC_RC)) + PPCImlGen_UpdateCR0(ppcImlGenContext, regD); return true; } bool PPCRecompilerImlGen_SUBFE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { + // d = ~a + b + ca; sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); - // hCPU->gpr[rD] = ~hCPU->gpr[rA] + hCPU->gpr[rB] + ca; - uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( opcode&PPC_OPC_RC ) - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SUB_CARRY_UPDATE_CARRY, registerRD, registerRB, registerRA, 0, PPCREC_CR_MODE_LOGICAL); - else - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SUB_CARRY_UPDATE_CARRY, registerRD, registerRB, registerRA); + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); + IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); + IMLReg regTmp = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 0); + IMLReg regCa = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_CA); + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, regTmp, regA); + ppcImlGenContext->emitInst().make_r_r_r_carry(PPCREC_IML_OP_ADD_WITH_CARRY, regD, regTmp, regB, regCa); + if (opcode & PPC_OPC_RC) + PPCImlGen_UpdateCR0(ppcImlGenContext, regD); return true; } bool PPCRecompilerImlGen_SUBFZE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { + // d = ~a + ca; sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); - if( rB != 0 ) - debugBreakpoint(); - uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( opcode&PPC_OPC_RC ) - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_SUB_CARRY_UPDATE_CARRY, registerRD, registerRA, 0, PPCREC_CR_MODE_LOGICAL); - else - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_SUB_CARRY_UPDATE_CARRY, registerRD, registerRA); + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); + IMLReg regTmp = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 0); + IMLReg regCa = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_CA); + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, regTmp, regA); + ppcImlGenContext->emitInst().make_r_r_s32_carry(PPCREC_IML_OP_ADD_WITH_CARRY, regD, regTmp, 0, regCa); + if (opcode & PPC_OPC_RC) + PPCImlGen_UpdateCR0(ppcImlGenContext, regD); return true; } bool PPCRecompilerImlGen_SUBFC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { + // d = ~a + b + 1; sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); - // hCPU->gpr[rD] = ~hCPU->gpr[rA] + hCPU->gpr[rB] + 1; - // rD = rB - rA - uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SUBFC, registerRD, registerRA, registerRB); - if (opcode & PPC_OPC_RC) - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, registerRD, registerRD, 0, PPCREC_CR_MODE_LOGICAL); + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); + IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); + IMLReg regTmp = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 0); + IMLReg regCa = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_CA); + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, regTmp, regA); + ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regCa, 1); // set input carry to simulate offset of 1 + ppcImlGenContext->emitInst().make_r_r_r_carry(PPCREC_IML_OP_ADD_WITH_CARRY, regD, regTmp, regB, regCa); + if ((opcode & PPC_OPC_RC)) + PPCImlGen_UpdateCR0(ppcImlGenContext, regD); return true; } bool PPCRecompilerImlGen_SUBFIC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { + // d = ~a + imm + 1 sint32 rD, rA; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); - //uint32 a = hCPU->gpr[rA]; - //hCPU->gpr[rD] = ~a + imm + 1; - // cr0 is never affected - uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); - PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_SUBFC, registerRD, registerRA, imm); + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); + IMLReg regCa = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_CA); + IMLReg regTmp = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 0); + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, regTmp, regA); + ppcImlGenContext->emitInst().make_r_r_s32_carry(PPCREC_IML_OP_ADD, regD, regTmp, (sint32)imm + 1, regCa); return true; } @@ -1217,10 +885,9 @@ bool PPCRecompilerImlGen_MULLI(ppcImlGenContext_t* ppcImlGenContext, uint32 opco int rD, rA; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); - // mulli instruction does not modify any flags - uint32 registerResult = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); - uint32 registerOperand = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_SIGNED, registerResult, registerOperand, (sint32)imm); + IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_MULTIPLY_SIGNED, regD, regA, (sint32)imm); return true; } @@ -1228,18 +895,16 @@ bool PPCRecompilerImlGen_MULLW(ppcImlGenContext_t* ppcImlGenContext, uint32 opco { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); - //hCPU->gpr[rD] = hCPU->gpr[rA] * hCPU->gpr[rB]; - uint32 registerResult = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); - uint32 registerOperand1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 registerOperand2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); + IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); if (opcode & PPC_OPC_OE) { return false; } - if( opcode&PPC_OPC_RC ) - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_SIGNED, registerResult, registerOperand1, registerOperand2, 0, PPCREC_CR_MODE_LOGICAL); - else - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_SIGNED, registerResult, registerOperand1, registerOperand2); + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_MULTIPLY_SIGNED, regD, regA, regB); + if (opcode & PPC_OPC_RC) + PPCImlGen_UpdateCR0(ppcImlGenContext, regD); return true; } @@ -1247,14 +912,12 @@ bool PPCRecompilerImlGen_MULHW(ppcImlGenContext_t* ppcImlGenContext, uint32 opco { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); - //hCPU->gpr[rD] = ((sint64)(sint32)hCPU->gpr[rA] * (sint64)(sint32)hCPU->gpr[rB])>>32; - uint32 registerResult = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); - uint32 registerOperand1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 registerOperand2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - if( opcode&PPC_OPC_RC ) - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_HIGH_SIGNED, registerResult, registerOperand1, registerOperand2, 0, PPCREC_CR_MODE_LOGICAL); - else - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_HIGH_SIGNED, registerResult, registerOperand1, registerOperand2); + IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_MULTIPLY_HIGH_SIGNED, regD, regA, regB); + if (opcode & PPC_OPC_RC) + PPCImlGen_UpdateCR0(ppcImlGenContext, regD); return true; } @@ -1262,14 +925,12 @@ bool PPCRecompilerImlGen_MULHWU(ppcImlGenContext_t* ppcImlGenContext, uint32 opc { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); - //hCPU->gpr[rD] = (hCPU->gpr[rA] * hCPU->gpr[rB])>>32; - uint32 registerResult = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); - uint32 registerOperand1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 registerOperand2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - if( opcode&PPC_OPC_RC ) - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_HIGH_UNSIGNED, registerResult, registerOperand1, registerOperand2, 0, PPCREC_CR_MODE_LOGICAL); - else - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_HIGH_UNSIGNED, registerResult, registerOperand1, registerOperand2); + IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_MULTIPLY_HIGH_UNSIGNED, regD, regA, regB); + if (opcode & PPC_OPC_RC) + PPCImlGen_UpdateCR0(ppcImlGenContext, regD); return true; } @@ -1277,18 +938,12 @@ bool PPCRecompilerImlGen_DIVW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcod { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); - // hCPU->gpr[rD] = (sint32)a / (sint32)b; - uint32 registerResult = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); - uint32 registerOperand1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 registerOperand2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); + IMLReg regR = _GetRegGPR(ppcImlGenContext, rD); + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_DIVIDE_SIGNED, regR, regA, regB); if (opcode & PPC_OPC_RC) - { - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_DIVIDE_SIGNED, registerResult, registerOperand1, registerOperand2, 0, PPCREC_CR_MODE_ARITHMETIC); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_DIVIDE_SIGNED, registerResult, registerOperand1, registerOperand2); - } + PPCImlGen_UpdateCR0(ppcImlGenContext, regR); return true; } @@ -1296,18 +951,12 @@ bool PPCRecompilerImlGen_DIVWU(ppcImlGenContext_t* ppcImlGenContext, uint32 opco { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); - // hCPU->gpr[rD] = (uint32)a / (uint32)b; - uint32 registerResult = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); - uint32 registerOperand1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 registerOperand2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); + IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_DIVIDE_UNSIGNED, regD, regA, regB); if (opcode & PPC_OPC_RC) - { - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_DIVIDE_UNSIGNED, registerResult, registerOperand1, registerOperand2, 0, PPCREC_CR_MODE_ARITHMETIC); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_DIVIDE_UNSIGNED, registerResult, registerOperand1, registerOperand2); - } + PPCImlGen_UpdateCR0(ppcImlGenContext, regD); return true; } @@ -1316,49 +965,31 @@ bool PPCRecompilerImlGen_RLWINM(ppcImlGenContext_t* ppcImlGenContext, uint32 opc int rS, rA, SH, MB, ME; PPC_OPC_TEMPL_M(opcode, rS, rA, SH, MB, ME); uint32 mask = ppc_mask(MB, ME); - //uint32 v = ppc_word_rotl(hCPU->gpr[rS], SH); - //hCPU->gpr[rA] = v & mask; - uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); - uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - // handle special forms of RLWINM - if( SH == 0 && SH == (ME-SH) && MB == 0 ) - { - // CLRRWI - // todo - } - else if( ME == (31-SH) && MB == 0 ) + IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); + IMLReg regA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); + if( ME == (31-SH) && MB == 0 ) { // SLWI - if(opcode&PPC_OPC_RC) - PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_LEFT_SHIFT, registerRA, registerRS, SH, 0, PPCREC_CR_MODE_LOGICAL); - else - PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_LEFT_SHIFT, registerRA, registerRS, SH); - return true; + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_LEFT_SHIFT, regA, regS, SH); } else if( SH == (32-MB) && ME == 31 ) { // SRWI - if(opcode&PPC_OPC_RC) - PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_RIGHT_SHIFT, registerRA, registerRS, MB, 0, PPCREC_CR_MODE_LOGICAL); - else - PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_RIGHT_SHIFT, registerRA, registerRS, MB); - return true; - } - // general handler - if( registerRA != registerRS ) - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, registerRA, registerRS); - if( SH != 0 ) - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_LEFT_ROTATE, registerRA, SH, 0, false, false, PPC_REC_INVALID_REGISTER, 0); - if(opcode&PPC_OPC_RC) - { - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_AND, registerRA, (sint32)mask, 0, false, false, 0, PPCREC_CR_MODE_LOGICAL); + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_RIGHT_SHIFT_U, regA, regS, MB); } else { - if( mask != 0xFFFFFFFF ) - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_AND, registerRA, (sint32)mask, 0, false, false, PPC_REC_INVALID_REGISTER, 0); + // general handler + if (rA != rS) + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, regA, regS); + if (SH != 0) + ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_LEFT_ROTATE, regA, SH); + if (mask != 0xFFFFFFFF) + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_AND, regA, regA, (sint32)mask); } + if (opcode & PPC_OPC_RC) + PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } @@ -1367,13 +998,13 @@ bool PPCRecompilerImlGen_RLWIMI(ppcImlGenContext_t* ppcImlGenContext, uint32 opc int rS, rA, SH, MB, ME; PPC_OPC_TEMPL_M(opcode, rS, rA, SH, MB, ME); - uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); - uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); + IMLReg regS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); + IMLReg regA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); // pack RLWIMI parameters into single integer uint32 vImm = MB|(ME<<8)|(SH<<16); - PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_RLWIMI, registerRA, registerRS, (sint32)vImm, PPC_REC_INVALID_REGISTER, 0); + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_RLWIMI, regA, regS, (sint32)vImm); if (opcode & PPC_OPC_RC) - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, registerRA, registerRA, 0, PPCREC_CR_MODE_LOGICAL); + PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } @@ -1381,61 +1012,59 @@ bool PPCRecompilerImlGen_RLWNM(ppcImlGenContext_t* ppcImlGenContext, uint32 opco { sint32 rS, rA, rB, MB, ME; PPC_OPC_TEMPL_M(opcode, rS, rA, rB, MB, ME); - // uint32 v = ppc_word_rotl(hCPU->gpr[rS], hCPU->gpr[rB]); uint32 mask = ppc_mask(MB, ME); - // uint32 v = ppc_word_rotl(hCPU->gpr[rS], hCPU->gpr[rB]); - // hCPU->gpr[rA] = v & mask; - uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); - uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_LEFT_ROTATE, registerRA, registerRS, registerRB); + IMLReg regS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); + IMLReg regB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); + IMLReg regA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_LEFT_ROTATE, regA, regS, regB); + if( mask != 0xFFFFFFFF ) + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_AND, regA, regA, (sint32)mask); if (opcode & PPC_OPC_RC) - { - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_AND, registerRA, (sint32)mask, 32, false, false, 0, PPCREC_CR_MODE_LOGICAL); - } - else - { - if( mask != 0xFFFFFFFF ) - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_AND, registerRA, (sint32)mask, 32, false, false, PPC_REC_INVALID_REGISTER, 0); - } + PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } bool PPCRecompilerImlGen_SRAW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { + // unlike SRAWI, for SRAW the shift range is 0-63 (masked to 6 bits) + // but only shifts up to register bitwidth minus one are well defined in IML so this requires special handling for shifts >= 32 sint32 rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); - //uint32 SH = hCPU->gpr[rB] & 0x3f; - //hCPU->gpr[rA] = hCPU->gpr[rS]; - //hCPU->xer_ca = 0; - //if (hCPU->gpr[rA] & 0x80000000) { - // uint32 ca = 0; - // for (uint32 i=0; i < SH; i++) { - // if (hCPU->gpr[rA] & 1) ca = 1; - // hCPU->gpr[rA] >>= 1; - // hCPU->gpr[rA] |= 0x80000000; - // } - // if (ca) hCPU->xer_ca = 1; - //} else { - // if (SH > 31) { - // hCPU->gpr[rA] = 0; - // } else { - // hCPU->gpr[rA] >>= SH; - // } - //} - //if (Opcode & PPC_OPC_RC) { - // // update cr0 flags - // ppc_update_cr0(hCPU, hCPU->gpr[rA]); - //} + IMLReg regS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); + IMLReg regB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); + IMLReg regA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); + IMLReg regCarry = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_CA); - uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); - uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - if( (opcode&PPC_OPC_RC) != 0 ) - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SRAW, registerRA, registerRS, registerRB, 0, PPCREC_CR_MODE_LOGICAL); - else - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SRAW, registerRA, registerRS, registerRB); + IMLReg regTmpShiftAmount = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 0); + IMLReg regTmpCondBool = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 1); + IMLReg regTmp1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 2); + IMLReg regTmp2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 3); + + // load masked shift factor into temporary register + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_AND, regTmpShiftAmount, regB, 0x3F); + ppcImlGenContext->emitInst().make_compare_s32(regTmpShiftAmount, 32, regTmpCondBool, IMLCondition::UNSIGNED_GT); + ppcImlGenContext->emitInst().make_conditional_jump(regTmpCondBool, true); + + PPCIMLGen_CreateSegmentBranchedPath(*ppcImlGenContext, *ppcImlGenContext->currentBasicBlock, + [&](ppcImlGenContext_t& genCtx) + { + /* branch taken */ + genCtx.emitInst().make_r_r_r(PPCREC_IML_OP_RIGHT_SHIFT_S, regA, regS, regTmpShiftAmount); + genCtx.emitInst().make_compare_s32(regA, 0, regCarry, IMLCondition::NEQ); // if the sign bit is still set it also means it was shifted out and we can set carry + }, + [&](ppcImlGenContext_t& genCtx) + { + /* branch not taken, shift size below 32 */ + genCtx.emitInst().make_r_r_s32(PPCREC_IML_OP_RIGHT_SHIFT_S, regTmp1, regS, 31); // signMask = input >> 31 (arithmetic shift) + genCtx.emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regTmp2, 1); // shiftMask = ((1<emitInst().make_r_r_s32(PPCREC_IML_OP_RIGHT_SHIFT_S, regTmp, regS, 31); // signMask = input >> 31 (arithmetic shift) + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_AND, regTmp, regTmp, regS); // testValue = input & signMask & ((1<emitInst().make_r_r_s32(PPCREC_IML_OP_AND, regTmp, regTmp, ((1 << SH) - 1)); + ppcImlGenContext->emitInst().make_compare_s32(regTmp, 0, regCarry, IMLCondition::NEQ); // ca = (testValue != 0) + // do the actual shift + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_RIGHT_SHIFT_S, regA, regS, (sint32)SH); + if (opcode & PPC_OPC_RC) + PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } @@ -1459,17 +1097,12 @@ bool PPCRecompilerImlGen_SLW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode int rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); - uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); - uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - if (opcode & PPC_OPC_RC) - { - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SLW, registerRA, registerRS, registerRB, 0, PPCREC_CR_MODE_LOGICAL); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SLW, registerRA, registerRS, registerRB, PPC_REC_INVALID_REGISTER, 0); - } + IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); + IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_SLW, regA, regS, regB); + if ((opcode & PPC_OPC_RC)) + PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } @@ -1477,37 +1110,24 @@ bool PPCRecompilerImlGen_SRW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode { int rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); - - uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); - uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); + IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); + IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_SRW, regA, regS, regB); if (opcode & PPC_OPC_RC) - { - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SRW, registerRA, registerRS, registerRB, 0, PPCREC_CR_MODE_LOGICAL); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SRW, registerRA, registerRS, registerRB, PPC_REC_INVALID_REGISTER, 0); - } + PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } - bool PPCRecompilerImlGen_EXTSH(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); - PPC_ASSERT(rB==0); - uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); - uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - if ( opcode&PPC_OPC_RC ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN_S16_TO_S32, registerRA, registerRS, 0, PPCREC_CR_MODE_ARITHMETIC); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN_S16_TO_S32, registerRA, registerRS); - } + IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN_S16_TO_S32, regA, regS); + if (opcode & PPC_OPC_RC) + PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } @@ -1515,16 +1135,11 @@ bool PPCRecompilerImlGen_EXTSB(ppcImlGenContext_t* ppcImlGenContext, uint32 opco { sint32 rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); - uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); - uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - if ( opcode&PPC_OPC_RC ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN_S8_TO_S32, registerRA, registerRS, 0, PPCREC_CR_MODE_ARITHMETIC); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN_S8_TO_S32, registerRA, registerRS); - } + IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN_S8_TO_S32, regA, regS); + if ((opcode & PPC_OPC_RC)) + PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } @@ -1532,30 +1147,11 @@ bool PPCRecompilerImlGen_CNTLZW(ppcImlGenContext_t* ppcImlGenContext, uint32 opc { sint32 rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); - PPC_ASSERT(rB==0); - if( opcode&PPC_OPC_RC ) - { - return false; - } - uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); - uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_CNTLZW, registerRA, registerRS); - - //uint32 n=0; - //uint32 x=0x80000000; - //uint32 v=hCPU->gpr[rS]; - //while (!(v & x)) { - // n++; - // if (n==32) break; - // x>>=1; - //} - //hCPU->gpr[rA] = n; - //if (Opcode & PPC_OPC_RC) { - // // update cr0 flags - // ppc_update_cr0(hCPU, hCPU->gpr[rA]); - //} - - + IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_CNTLZW, regA, regS); + if ((opcode & PPC_OPC_RC)) + PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } @@ -1563,438 +1159,125 @@ bool PPCRecompilerImlGen_NEG(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); - PPC_ASSERT(rB == 0); - //hCPU->gpr[rD] = -((signed int)hCPU->gpr[rA]); - //if (Opcode & PPC_OPC_RC) { - // // update cr0 flags - // ppc_update_cr0(hCPU, hCPU->gpr[rD]); - //} - uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( opcode&PPC_OPC_RC ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NEG, registerRD, registerRA, 0, PPCREC_CR_MODE_ARITHMETIC); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NEG, registerRD, registerRA); - } + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NEG, regD, regA); + if (opcode & PPC_OPC_RC) + PPCImlGen_UpdateCR0(ppcImlGenContext, regD); return true; } -void PPCRecompilerImlGen_LWZ(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) +bool PPCRecompilerImlGen_LOAD(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, uint32 bitWidth, bool signExtend, bool isBigEndian, bool updateAddrReg) { int rA, rD; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); - if( rA == 0 ) - { - // special form where gpr is ignored and only imm is used - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); - return; - } - // load memory gpr into register - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // check if destination register is already loaded - uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( destinationRegister == PPC_REC_INVALID_REGISTER ) - destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register - // load half - PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, imm, 32, false, true); -} - -void PPCRecompilerImlGen_LWZU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - int rA, rD; - uint32 imm; - PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); - if( rA == 0 ) - { - // special form where gpr is ignored and only imm is used - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); - return; - } - // load memory gpr into register - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // add imm to memory register - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); - // check if destination register is already loaded - uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( destinationRegister == PPC_REC_INVALID_REGISTER ) - destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register - // load half - PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, 0, 32, false, true); -} - -void PPCRecompilerImlGen_LHA(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - int rA, rD; - uint32 imm; - PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); - if( rA == 0 ) - { - // special form where gpr is ignored and only imm is used - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); - return; - } - // load memory gpr into register - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // check if destination register is already loaded - uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( destinationRegister == PPC_REC_INVALID_REGISTER ) - destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new temporary register - // load half - PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, imm, 16, true, true); -} - -void PPCRecompilerImlGen_LHAU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rA, rD; - uint32 imm; - PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); - if( rA == 0 ) - { - // special form where gpr is ignored and only imm is used - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); - return; - } - // load memory gpr into register - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // add imm to memory register - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); - // check if destination register is already loaded - uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( destinationRegister == PPC_REC_INVALID_REGISTER ) - destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new temporary register - // load half - PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, 0, 16, true, true); -} - -void PPCRecompilerImlGen_LHZ(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rA, rD; - uint32 imm; - PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); - if( rA == 0 ) - { - // special form where gpr is ignored and only imm is used - // note: Darksiders 2 has this instruction form but it is never executed. - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); - return; - } - // load memory gpr into register - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // check if destination register is already loaded - uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( destinationRegister == PPC_REC_INVALID_REGISTER ) - destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new temporary register - // load half - PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, imm, 16, false, true); -} - -void PPCRecompilerImlGen_LHZU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rA, rD; - uint32 imm; - PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); - if( rA == 0 ) - { - // special form where gpr is ignored and only imm is used - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); - return; - } - // load memory gpr into register - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // add imm to memory register - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); - // check if destination register is already loaded - uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( destinationRegister == PPC_REC_INVALID_REGISTER ) - destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new temporary register - // load half - PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, 0, 16, false, true); -} - -void PPCRecompilerImlGen_LBZ(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - int rA, rD; - uint32 imm; - PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); - if( rA == 0 ) - { - // special form where gpr is ignored and only imm is used - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); - return; - } - // load memory gpr into register - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // check if destination register is already loaded - uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( destinationRegister == PPC_REC_INVALID_REGISTER ) - destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register - // load byte - PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, imm, 8, false, true); -} - -void PPCRecompilerImlGen_LBZU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - int rA, rD; - uint32 imm; - PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); - if( rA == 0 ) - { - // special form where gpr is ignored and only imm is used - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); - return; - } - // load memory gpr into register - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // add imm to memory register - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); - // check if destination register is already loaded - uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( destinationRegister == PPC_REC_INVALID_REGISTER ) - destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register - // load byte - PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, 0, 8, false, true); -} - -bool PPCRecompilerImlGen_LWZX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rA, rD, rB; - PPC_OPC_TEMPL_X(opcode, rD, rA, rB); - if( rA == 0 ) - { - return false; - } - // hCPU->gpr[rD] = memory_readU8((rA?hCPU->gpr[rA]:0)+hCPU->gpr[rB]); - // load memory rA and rB into register - uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - // check if destination register is already loaded - uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( destinationRegister == PPC_REC_INVALID_REGISTER ) - destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register - // load word - PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, 32, false, true); - return true; -} - -bool PPCRecompilerImlGen_LWZUX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rA, rD, rB; - PPC_OPC_TEMPL_X(opcode, rD, rA, rB); - if( rA == 0 ) - { - return false; - } - // load memory rA and rB into register - uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - // check if destination register is already loaded - uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( destinationRegister == PPC_REC_INVALID_REGISTER ) - destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register - // add rB to rA - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD, gprRegisterA, gprRegisterB); - // load word - PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegisterA, 0, 32, false, true); - return true; -} - -bool PPCRecompilerImlGen_LWBRX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rA, rD, rB; - PPC_OPC_TEMPL_X(opcode, rD, rA, rB); - // load memory rA and rB into register - uint32 gprRegisterA = 0; - if( rA ) - gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA, false); - uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rB, false); - // check if destination register is already loaded - uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0 + rD); - if (destinationRegister == PPC_REC_INVALID_REGISTER) - destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0 + rD); // else just create new register - // load word - if( rA ) - PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, 32, false, false); - else - PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegisterB, 0, 32, false, false); - return true; -} - -bool PPCRecompilerImlGen_LHAX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rA, rD, rB; - PPC_OPC_TEMPL_X(opcode, rD, rA, rB); - if( rA == 0 ) - { - // special form where gpr is ignored and only imm is used - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); - return true; - } - // load memory rA and rB into register - uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - // check if destination register is already loaded - uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( destinationRegister == PPC_REC_INVALID_REGISTER ) - destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register - // load half word - PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, 16, true, true); - return true; -} - -bool PPCRecompilerImlGen_LHAUX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rA, rD, rB; - PPC_OPC_TEMPL_X(opcode, rD, rA, rB); - if( rA == 0 ) - { - // special form where gpr is ignored and only imm is used - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); - return true; - } - // load memory rA and rB into register - uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - // check if destination register is already loaded - uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( destinationRegister == PPC_REC_INVALID_REGISTER ) - destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register - // add rB to rA - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD, gprRegisterA, gprRegisterB); - // load half word - PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegisterA, 0, 16, true, true); - return true; -} - -bool PPCRecompilerImlGen_LHZX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rA, rD, rB; - PPC_OPC_TEMPL_X(opcode, rD, rA, rB); - if( rA == 0 ) - { - // special form where gpr is ignored and only imm is used - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); - return true; - } - // load memory rA and rB into register - uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - // check if destination register is already loaded - uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( destinationRegister == PPC_REC_INVALID_REGISTER ) - destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register - // load half word - PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, 16, false, true); - return true; -} - -bool PPCRecompilerImlGen_LHZUX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rA, rD, rB; - PPC_OPC_TEMPL_X(opcode, rD, rA, rB); - if( rA == 0 ) - { - // special form where gpr is ignored and only imm is used - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); - return true; - } - // load memory rA and rB into register - uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - // check if destination register is already loaded - uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( destinationRegister == PPC_REC_INVALID_REGISTER ) - destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register - // add rB to rA - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD, gprRegisterA, gprRegisterB); - // load hald word - PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegisterA, 0, 16, false, true); - return true; -} - -void PPCRecompilerImlGen_LHBRX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rA, rD, rB; - PPC_OPC_TEMPL_X(opcode, rD, rA, rB); - // load memory rA and rB into register - uint32 gprRegisterA = rA != 0 ? PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA, false) : 0; - uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rB, false); - // check if destination register is already loaded - uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0 + rD); - if (destinationRegister == PPC_REC_INVALID_REGISTER) - destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0 + rD); // else just create new register - // load half word (little-endian) - if (rA == 0) - PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegisterB, 0, 16, false, false); - else - PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, 16, false, false); -} - -bool PPCRecompilerImlGen_LBZX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rA, rD, rB; - PPC_OPC_TEMPL_X(opcode, rD, rA, rB); - if( rA == 0 ) - { - // special case where rA is ignored and only rB is used - return false; - } - // hCPU->gpr[rD] = memory_readU8((rA?hCPU->gpr[rA]:0)+hCPU->gpr[rB]); - // load memory rA and rB into register - uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - // check if destination register is already loaded - uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( destinationRegister == PPC_REC_INVALID_REGISTER ) - destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register - // load byte - PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, 8, false, true); - return true; -} - -bool PPCRecompilerImlGen_LBZUX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rA, rD, rB; - PPC_OPC_TEMPL_X(opcode, rD, rA, rB); + IMLReg regMemAddr; if (rA == 0) { - // special form where gpr is ignored and only imm is used - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); - return true; + if (updateAddrReg) + return false; // invalid instruction form + regMemAddr = _GetRegTemporary(ppcImlGenContext, 0); + ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regMemAddr, 0); } - // load memory rA and rB into register - uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA, false); - uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rB, false); - // check if destination register is already loaded - uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0 + rD); - if (destinationRegister == PPC_REC_INVALID_REGISTER) - destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0 + rD); // else just create new register - // add rB to rA - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD, gprRegisterA, gprRegisterB); - // load byte - PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegisterA, 0, 8, false, true); + else + { + if (updateAddrReg && rA == rD) + return false; // invalid instruction form + regMemAddr = _GetRegGPR(ppcImlGenContext, rA); + } + if (updateAddrReg) + { + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_ADD, regMemAddr, regMemAddr, (sint32)imm); + imm = 0; + } + IMLReg regDst = _GetRegGPR(ppcImlGenContext, rD); + ppcImlGenContext->emitInst().make_r_memory(regDst, regMemAddr, (sint32)imm, bitWidth, signExtend, isBigEndian); return true; } -bool PPCRecompilerImlGen_LWARX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) +bool PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, uint32 bitWidth, bool signExtend, bool isBigEndian, bool updateAddrReg) { sint32 rA, rD, rB; PPC_OPC_TEMPL_X(opcode, rD, rA, rB); - // load memory rA and rB into register - uint32 gprRegisterA = rA != 0?PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false):0; - uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - // check if destination register is already loaded - uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( destinationRegister == PPC_REC_INVALID_REGISTER ) - destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register - // load word - if( rA != 0 ) - PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, PPC_REC_LOAD_LWARX_MARKER, false, true); + if (updateAddrReg && (rA == 0 || rD == rB)) + return false; // invalid instruction form + IMLReg regA = rA != 0 ? _GetRegGPR(ppcImlGenContext, rA) : IMLREG_INVALID; + IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); + IMLReg regDst = _GetRegGPR(ppcImlGenContext, rD); + if (updateAddrReg) + { + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_ADD, regA, regA, regB); + // use single register addressing + regB = regA; + regA = IMLREG_INVALID; + } + if(regA.IsValid()) + PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext, regDst, regA, regB, bitWidth, signExtend, isBigEndian); else - PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegisterB, 0, PPC_REC_LOAD_LWARX_MARKER, false, true); + ppcImlGenContext->emitInst().make_r_memory(regDst, regB, 0, bitWidth, signExtend, isBigEndian); + return true; +} + +bool PPCRecompilerImlGen_STORE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, uint32 bitWidth, bool isBigEndian, bool updateAddrReg) +{ + int rA, rD; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); + IMLReg regA; + if (rA != 0) + { + regA = _GetRegGPR(ppcImlGenContext, rA); + } + else + { + if (updateAddrReg) + return false; // invalid instruction form + regA = _GetRegTemporary(ppcImlGenContext, 0); + ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regA, 0); + } + IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); + if (updateAddrReg) + { + if (rD == rA) + { + // make sure to keep source data intact + regD = _GetRegTemporary(ppcImlGenContext, 0); + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, regD, regA); + } + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_ADD, regA, regA, (sint32)imm); + imm = 0; + } + ppcImlGenContext->emitInst().make_memory_r(regD, regA, (sint32)imm, bitWidth, isBigEndian); + return true; +} + +bool PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, uint32 bitWidth, bool isBigEndian, bool updateAddrReg) +{ + sint32 rA, rS, rB; + PPC_OPC_TEMPL_X(opcode, rS, rA, rB); + IMLReg regA = rA != 0 ? _GetRegGPR(ppcImlGenContext, rA) : IMLREG_INVALID; + IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); + IMLReg regSrc = _GetRegGPR(ppcImlGenContext, rS); + if (updateAddrReg) + { + if(rA == 0) + return false; // invalid instruction form + if (regSrc == regA) + { + // make sure to keep source data intact + regSrc = _GetRegTemporary(ppcImlGenContext, 0); + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, regSrc, regA); + } + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_ADD, regA, regA, regB); + // use single register addressing + regB = regA; + regA = IMLREG_INVALID; + } + if (regA.IsInvalid()) + ppcImlGenContext->emitInst().make_memory_r(regSrc, regB, 0, bitWidth, isBigEndian); + else + PPCRecompilerImlGen_generateNewInstruction_memory_r_indexed(ppcImlGenContext, regSrc, regA, regB, bitWidth, false, isBigEndian); return true; } @@ -2003,257 +1286,33 @@ void PPCRecompilerImlGen_LMW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode sint32 rD, rA; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); - //uint32 ea = (rA ? hCPU->gpr[rA] : 0) + imm; + cemu_assert_debug(rA != 0); sint32 index = 0; - while( rD <= 31 ) + while (rD <= 31) { - // load memory gpr into register - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // check if destination register is already loaded - uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( destinationRegister == PPC_REC_INVALID_REGISTER ) - destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); // load word - PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, imm+index*4, 32, false, true); + ppcImlGenContext->emitInst().make_r_memory(regD, regA, (sint32)imm + index * 4, 32, false, true); // next rD++; index++; } } -void PPCRecompilerImlGen_STW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - int rA, rD; - uint32 imm; - PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); - if( rA == 0 ) - { - // special form where gpr is ignored and only imm is used - // note: Darksiders 2 has this instruction form but it is never executed. - //PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); - return; - } - // load memory gpr into register - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // load source register - uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); // can be the same as gprRegister - // store word - PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, imm, 32, true); -} - -void PPCRecompilerImlGen_STWU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - int rA, rD; - uint32 imm; - PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); - if( rA == 0 ) - { - // special form where gpr is ignored and only imm is used - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); - return; - } - // store&update instructions where rD==rA store the register contents without added imm, therefore we need to handle it differently - // get memory gpr register - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // get source register - uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); // can be the same as gprRegister - // add imm to memory register early if possible - if( rD != rA ) - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); - // store word - PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, (rD==rA)?imm:0, 32, true); - // add imm to memory register late if we couldn't do it early - if( rD == rA ) - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); -} - -void PPCRecompilerImlGen_STH(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - int rA, rD; - uint32 imm; - PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); - if( rA == 0 ) - { - // special form where gpr is ignored and only imm is used - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); - return; - } - // load memory gpr into register - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // load source register - uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); // can be the same as gprRegister - // load half - PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, imm, 16, true); -} - -void PPCRecompilerImlGen_STHU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - int rA, rD; - uint32 imm; - PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); - if( rA == 0 ) - { - // special form where gpr is ignored and only imm is used - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); - return; - } - // get memory gpr register - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // get source register - uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); // can be the same as gprRegister - // add imm to memory register early if possible - if( rD != rA ) - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); - // store word - PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, (rD==rA)?imm:0, 16, true); - // add imm to memory register late if we couldn't do it early - if( rD == rA ) - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); -} - -void PPCRecompilerImlGen_STB(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - int rA, rS; - uint32 imm; - PPC_OPC_TEMPL_D_SImm(opcode, rS, rA, imm); - if( rA == 0 ) - { - // special form where gpr is ignored and only imm is used - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); - return; - } - // load memory gpr into register - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // load source register - uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); // can be the same as gprRegister - // store byte - PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, imm, 8, true); -} - -void PPCRecompilerImlGen_STBU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - int rA, rD; - uint32 imm; - PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); - if( rA == 0 ) - { - // special form where gpr is ignored and only imm is used - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); - return; - } - // get memory gpr register - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // get source register - uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); // can be the same as gprRegister - // add imm to memory register early if possible - if( rD != rA ) - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); - // store byte - PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, (rD==rA)?imm:0, 8, true); - // add imm to memory register late if we couldn't do it early - if( rD == rA ) - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); -} - -// generic indexed store (STWX, STHX, STBX, STWUX. If bitReversed == true -> STHBRX) -bool PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, uint32 storeBitWidth, bool byteReversed = false) -{ - sint32 rA, rS, rB; - PPC_OPC_TEMPL_X(opcode, rS, rA, rB); - // prepare registers - uint32 gprRegisterA; - if(rA != 0) - gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA, false); - uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - uint32 destinationRegister = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); - // store word - if (rA == 0) - { - PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, destinationRegister, gprRegisterB, 0, storeBitWidth, !byteReversed); - } - else - PPCRecompilerImlGen_generateNewInstruction_memory_r_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, storeBitWidth, false, !byteReversed); - return true; -} - -bool PPCRecompilerImlGen_STORE_INDEXED_UPDATE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, uint32 storeBitWidth) -{ - sint32 rA, rS, rB; - PPC_OPC_TEMPL_X(opcode, rS, rA, rB); - if( rA == 0 ) - { - // not supported - return false; - } - if( rS == rA || rS == rB ) - { - // prepare registers - uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - uint32 destinationRegister = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); - // store word - PPCRecompilerImlGen_generateNewInstruction_memory_r_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, storeBitWidth, false, true); - // update EA after store - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD, gprRegisterA, gprRegisterB); - return true; - } - // prepare registers - uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - uint32 sourceRegister = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); - // update EA - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD, gprRegisterA, gprRegisterB); - // store word - PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegisterA, 0, storeBitWidth, true); - return true; -} - -bool PPCRecompilerImlGen_STWCX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rA, rS, rB; - PPC_OPC_TEMPL_X(opcode, rS, rA, rB); - // prepare registers - uint32 gprRegisterA = rA!=0?PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false):0; - uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - uint32 destinationRegister = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); - // store word - if( rA != 0 ) - PPCRecompilerImlGen_generateNewInstruction_memory_r_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, PPC_REC_STORE_STWCX_MARKER, false, true); - else - PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, destinationRegister, gprRegisterB, 0, PPC_REC_STORE_STWCX_MARKER, true); - return true; -} - -bool PPCRecompilerImlGen_STWBRX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rA, rS, rB; - PPC_OPC_TEMPL_X(opcode, rS, rA, rB); - // prepare registers - uint32 gprRegisterA = rA!=0?PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false):0; - uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - uint32 destinationRegister = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); - // store word - if( rA != 0 ) - PPCRecompilerImlGen_generateNewInstruction_memory_r_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, 32, false, false); - else - PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, destinationRegister, gprRegisterB, 0, 32, false); - return true; -} - void PPCRecompilerImlGen_STMW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rS, rA; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rS, rA, imm); + cemu_assert_debug(rA != 0); sint32 index = 0; while( rS <= 31 ) { - // load memory gpr into register - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // load source register - uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); // can be the same as gprRegister + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); // store word - PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, imm+index*4, 32, true); + ppcImlGenContext->emitInst().make_memory_r(regS, regA, (sint32)imm + index * 4, 32, true); // next rS++; index++; @@ -2266,70 +1325,43 @@ bool PPCRecompilerImlGen_LSWI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcod PPC_OPC_TEMPL_X(opcode, rD, rA, nb); if( nb == 0 ) nb = 32; - if( nb == 4 ) + + if (rA == 0) { - // if nb == 4 this instruction immitates LWZ - if( rA == 0 ) - { -#ifdef CEMU_DEBUG_ASSERT - assert_dbg(); // special form where gpr is ignored and only imm is used -#endif - return false; - } - // load memory gpr into register - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // check if destination register is already loaded - uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( destinationRegister == PPC_REC_INVALID_REGISTER ) - destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register - // load half - PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, 0, 32, false, true); - return true; + cemu_assert_unimplemented(); // special form where gpr is ignored and EA is 0 + return false; } - else if( nb == 2 ) + + // potential optimization: On x86 unaligned access is allowed and we could handle the case nb==4 with a single memory read, and nb==2 with a memory read and shift + + IMLReg memReg = _GetRegGPR(ppcImlGenContext, rA); + IMLReg regTmp = _GetRegTemporary(ppcImlGenContext, 0); + uint32 memOffset = 0; + while (nb > 0) { - // if nb == 2 this instruction immitates a LHZ but the result is shifted left by 16 bits - if( rA == 0 ) - { -#ifdef CEMU_DEBUG_ASSERT - assert_dbg(); // special form where gpr is ignored and only imm is used -#endif + if (rD == rA) return false; - } - // load memory gpr into register - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // check if destination register is already loaded - uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( destinationRegister == PPC_REC_INVALID_REGISTER ) - destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register - // load half - PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, 0, 16, false, true); - // shift - PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_LEFT_SHIFT, destinationRegister, destinationRegister, 16); - return true; - } - else if( nb == 3 ) - { - // if nb == 3 this instruction loads a 3-byte big-endian and the result is shifted left by 8 bits - if( rA == 0 ) + cemu_assert(rD < 32); + IMLReg regDst = _GetRegGPR(ppcImlGenContext, rD); + // load bytes one-by-one + for (sint32 b = 0; b < 4; b++) { -#ifdef CEMU_DEBUG_ASSERT - assert_dbg(); // special form where gpr is ignored and only imm is used -#endif - return false; + ppcImlGenContext->emitInst().make_r_memory(regTmp, memReg, memOffset + b, 8, false, false); + sint32 shiftAmount = (3 - b) * 8; + if(shiftAmount) + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_LEFT_SHIFT, regTmp, regTmp, shiftAmount); + if(b == 0) + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, regDst, regTmp); + else + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_OR, regDst, regDst, regTmp); + nb--; + if (nb == 0) + break; } - // load memory gpr into register - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // check if destination register is already loaded - uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); - if( destinationRegister == PPC_REC_INVALID_REGISTER ) - destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register - // load half - PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, 0, PPC_REC_STORE_LSWI_3, false, true); - return true; + memOffset += 4; + rD++; } - debug_printf("PPCRecompilerImlGen_LSWI(): Unsupported nb value %d\n", nb); - return false; + return true; } bool PPCRecompilerImlGen_STSWI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) @@ -2338,38 +1370,111 @@ bool PPCRecompilerImlGen_STSWI(ppcImlGenContext_t* ppcImlGenContext, uint32 opco PPC_OPC_TEMPL_X(opcode, rS, rA, nb); if( nb == 0 ) nb = 32; - if( nb == 4 ) + + IMLReg regMem = _GetRegGPR(ppcImlGenContext, rA); + IMLReg regTmp = _GetRegTemporary(ppcImlGenContext, 0); + uint32 memOffset = 0; + while (nb > 0) { - // load memory gpr into register - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // load source register - uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); // can be the same as gprRegister - // store word - PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, 0, 32, true); - return true; + if (rS == rA) + return false; + cemu_assert(rS < 32); + IMLReg regSrc = _GetRegGPR(ppcImlGenContext, rS); + // store bytes one-by-one + for (sint32 b = 0; b < 4; b++) + { + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, regTmp, regSrc); + sint32 shiftAmount = (3 - b) * 8; + if (shiftAmount) + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_RIGHT_SHIFT_U, regTmp, regTmp, shiftAmount); + ppcImlGenContext->emitInst().make_memory_r(regTmp, regMem, memOffset + b, 8, false); + nb--; + if (nb == 0) + break; + } + memOffset += 4; + rS++; } - else if( nb == 2 ) - { - // load memory gpr into register - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // load source register - uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); // can be the same as gprRegister - // store half-word (shifted << 16) - PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, 0, PPC_REC_STORE_STSWI_2, false); - return true; - } - else if( nb == 3 ) - { - // load memory gpr into register - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // load source register - uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); // can be the same as gprRegister - // store 3-byte-word (shifted << 8) - PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, 0, PPC_REC_STORE_STSWI_3, false); - return true; - } - debug_printf("PPCRecompilerImlGen_STSWI(): Unsupported nb value %d\n", nb); - return false; + return true; +} + +bool PPCRecompilerImlGen_LWARX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) +{ + sint32 rA, rD, rB; + PPC_OPC_TEMPL_X(opcode, rD, rA, rB); + + IMLReg regA = rA != 0 ? PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA) : IMLREG_INVALID; + IMLReg regB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rB); + IMLReg regD = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rD); + IMLReg regMemResEA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_CPU_MEMRES_EA); + IMLReg regMemResVal = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_CPU_MEMRES_VAL); + // calculate EA + if (regA.IsValid()) + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_ADD, regMemResEA, regA, regB); + else + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, regMemResEA, regB); + // load word + ppcImlGenContext->emitInst().make_r_memory(regD, regMemResEA, 0, 32, false, true); + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, regMemResVal, regD); + return true; +} + +bool PPCRecompilerImlGen_STWCX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) +{ + sint32 rA, rS, rB; + PPC_OPC_TEMPL_X(opcode, rS, rA, rB); + IMLReg regA = rA != 0 ? PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA) : IMLREG_INVALID; + IMLReg regB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rB); + IMLReg regData = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rS); + IMLReg regTmpDataBE = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 2); + IMLReg regTmpCompareBE = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 3); + // calculate EA + IMLReg regCalcEA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY); + if (regA.IsValid()) + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_ADD, regCalcEA, regA, regB); + else + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, regCalcEA, regB); + // get CR bit regs and set LT, GT and SO immediately + IMLReg regCrLT = _GetRegCR(ppcImlGenContext, 0, Espresso::CR_BIT_INDEX_LT); + IMLReg regCrGT = _GetRegCR(ppcImlGenContext, 0, Espresso::CR_BIT_INDEX_GT); + IMLReg regCrEQ = _GetRegCR(ppcImlGenContext, 0, Espresso::CR_BIT_INDEX_EQ); + IMLReg regCrSO = _GetRegCR(ppcImlGenContext, 0, Espresso::CR_BIT_INDEX_SO); + IMLReg regXerSO = _GetRegCR(ppcImlGenContext, 0, Espresso::CR_BIT_INDEX_SO); + ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regCrLT, 0); + ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regCrGT, 0); + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, regCrSO, regXerSO); + // get regs for reservation address and value + IMLReg regMemResEA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_CPU_MEMRES_EA); + IMLReg regMemResVal = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_CPU_MEMRES_VAL); + // compare calculated EA with reservation + IMLReg regTmpBool = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 1); + ppcImlGenContext->emitInst().make_compare(regCalcEA, regMemResEA, regTmpBool, IMLCondition::EQ); + ppcImlGenContext->emitInst().make_conditional_jump(regTmpBool, true); + + PPCIMLGen_CreateSegmentBranchedPath(*ppcImlGenContext, *ppcImlGenContext->currentBasicBlock, + [&](ppcImlGenContext_t& genCtx) + { + /* branch taken, EA matching */ + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ENDIAN_SWAP, regTmpDataBE, regData); + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ENDIAN_SWAP, regTmpCompareBE, regMemResVal); + ppcImlGenContext->emitInst().make_atomic_cmp_store(regMemResEA, regTmpCompareBE, regTmpDataBE, regCrEQ); + }, + [&](ppcImlGenContext_t& genCtx) + { + /* branch not taken, EA mismatching */ + ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regCrEQ, 0); + } + ); + + // reset reservation + // I found contradictory information of whether the reservation is cleared in all cases, so unit testing would be required + // Most sources state that it is cleared on successful store. They don't explicitly mention what happens on failure + // "The PowerPC 600 series, part 7: Atomic memory access and cache coherency" states that it is always cleared + // There may also be different behavior between individual PPC architectures + ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regMemResEA, 0); + ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regMemResVal, 0); + + return true; } bool PPCRecompilerImlGen_DCBZ(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) @@ -2378,92 +1483,31 @@ bool PPCRecompilerImlGen_DCBZ(ppcImlGenContext_t* ppcImlGenContext, uint32 opcod rA = (opcode>>16)&0x1F; rB = (opcode>>11)&0x1F; // prepare registers - uint32 gprRegisterA = rA!=0?PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false):0; - uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); + IMLReg gprRegisterA = rA!=0?PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA):IMLREG_INVALID; + IMLReg gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); // store if( rA != 0 ) - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_DCBZ, gprRegisterA, gprRegisterB); + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_DCBZ, gprRegisterA, gprRegisterB); else - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_DCBZ, gprRegisterB, gprRegisterB); + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_DCBZ, gprRegisterB, gprRegisterB); return true; } -bool PPCRecompilerImlGen_OR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) +bool PPCRecompilerImlGen_OR_NOR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, bool complementResult) { int rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); - // check for MR mnemonic - if( rS == rB ) - { - // simple register copy - if( rA != rS ) // check if no-op - { - sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); - sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - if( opcode&PPC_OPC_RC ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg, 0, PPCREC_CR_MODE_LOGICAL); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg); - } - } - else - { - if( opcode&PPC_OPC_RC ) - { - // no effect but CR is updated - sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprSourceReg, gprSourceReg, 0, PPCREC_CR_MODE_LOGICAL); - } - else - { - // no-op - } - } - } + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); + IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); + if(rS == rB) // check for MR mnemonic + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, regA, regS); else - { - // rA = rS | rA - sint32 gprSource1Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); - sint32 gprSource2Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); - sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - if( gprSource1Reg == gprDestReg || gprSource2Reg == gprDestReg ) - { - // make sure we don't overwrite rS or rA - if( gprSource1Reg == gprDestReg ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_OR, gprDestReg, gprSource2Reg); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_OR, gprDestReg, gprSource1Reg); - } - if( opcode&PPC_OPC_RC ) - { - // fixme: merge CR update into OR instruction above - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_LOGICAL); - } - } - else - { - // rA = rS - if( gprDestReg != gprSource1Reg ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSource1Reg); - } - // rA |= rB - if( opcode&PPC_OPC_RC ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_OR, gprDestReg, gprSource2Reg, 0, PPCREC_CR_MODE_LOGICAL); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_OR, gprDestReg, gprSource2Reg); - } - } - } + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_OR, regA, regS, regB); + if(complementResult) + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, regA, regA); + if (opcode & PPC_OPC_RC) + PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } @@ -2471,151 +1515,33 @@ bool PPCRecompilerImlGen_ORC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode { sint32 rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); - // hCPU->gpr[rA] = hCPU->gpr[rS] | ~hCPU->gpr[rB]; - sint32 gprSource1Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); - sint32 gprSource2Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); - sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - if( opcode&PPC_OPC_RC ) - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ORC, gprDestReg, gprSource1Reg, gprSource2Reg, 0, PPCREC_CR_MODE_LOGICAL); - else - PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ORC, gprDestReg, gprSource1Reg, gprSource2Reg); + // rA = rS | ~rB; + IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); + IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); + IMLReg regTmp = _GetRegTemporary(ppcImlGenContext, 0); + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, regTmp, regB); + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_OR, regA, regS, regTmp); + if (opcode & PPC_OPC_RC) + PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } -bool PPCRecompilerImlGen_NOR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) +bool PPCRecompilerImlGen_AND_NAND(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, bool complementResult) { int rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); - //hCPU->gpr[rA] = ~(hCPU->gpr[rS] | hCPU->gpr[rB]); - // check for NOT mnemonic - if( rS == rB ) - { - // simple register copy with NOT - sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); - sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - if( gprDestReg != gprSourceReg ) - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg); - if( opcode&PPC_OPC_RC ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_ARITHMETIC); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg); - } - } + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); + IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); + if (regS == regB) + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, regA, regS); else - { - // rA = rS | rA - sint32 gprSource1Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); - sint32 gprSource2Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); - sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - if( gprSource1Reg == gprDestReg || gprSource2Reg == gprDestReg ) - { - // make sure we don't overwrite rS or rA - if( gprSource1Reg == gprDestReg ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_OR, gprDestReg, gprSource2Reg); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_OR, gprDestReg, gprSource1Reg); - } - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg); - if( opcode&PPC_OPC_RC ) - { - // fixme: merge CR update into OR instruction above - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_LOGICAL); - } - } - else - { - // rA = rS - if( gprDestReg != gprSource1Reg ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSource1Reg); - } - // rA |= rB - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_OR, gprDestReg, gprSource2Reg); - if( opcode&PPC_OPC_RC ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_ARITHMETIC); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg); - } - } - } - return true; -} - -bool PPCRecompilerImlGen_AND(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rS, rA, rB; - PPC_OPC_TEMPL_X(opcode, rS, rA, rB); - // check for MR mnemonic - if( rS == rB ) - { - // simple register copy - if( rA != rS ) // check if no-op - { - sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); - sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - if( opcode&PPC_OPC_RC ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg, 0, PPCREC_CR_MODE_LOGICAL); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg); - } - } - else - { - cemu_assert_unimplemented(); // no-op -> verify this case - } - } - else - { - // rA = rS & rA - sint32 gprSource1Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); - sint32 gprSource2Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); - sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - if( gprSource1Reg == gprDestReg || gprSource2Reg == gprDestReg ) - { - // make sure we don't overwrite rS or rA - if( gprSource1Reg == gprDestReg ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprDestReg, gprSource2Reg); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprDestReg, gprSource1Reg); - } - if( opcode&PPC_OPC_RC ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_LOGICAL); - } - } - else - { - // rA = rS - if( gprDestReg != gprSource1Reg ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSource1Reg); - } - // rA &= rB - if( opcode&PPC_OPC_RC ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprDestReg, gprSource2Reg, 0, PPCREC_CR_MODE_LOGICAL); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprDestReg, gprSource2Reg); - } - } - } + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_AND, regA, regS, regB); + if (complementResult) + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, regA, regA); + if (opcode & PPC_OPC_RC) + PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } @@ -2623,277 +1549,101 @@ bool PPCRecompilerImlGen_ANDC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcod { sint32 rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); - //hCPU->gpr[rA] = hCPU->gpr[rS] & ~hCPU->gpr[rB]; - //if (Opcode & PPC_OPC_RC) { - if( rS == rB ) - { - // result is always 0 -> replace with XOR rA,rA - sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - if( opcode&PPC_OPC_RC ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_LOGICAL); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprDestReg); - } - } - else if( rA == rB ) - { - // rB already in rA, therefore we complement rA first and then AND it with rS - sint32 gprRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); - sint32 gprRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - // rA = ~rA - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprRA, gprRA); - // rA &= rS - if( opcode&PPC_OPC_RC ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprRA, gprRS, 0, PPCREC_CR_MODE_LOGICAL); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprRA, gprRS); - } - } - else - { - // a & (~b) is the same as ~((~a) | b) - sint32 gprRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - sint32 gprRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); - sint32 gprRS = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); - // move rS to rA (if required) - if( gprRA != gprRS ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprRA, gprRS); - } - // rS already in rA, therefore we complement rS first and then OR it with rB - // rA = ~rA - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprRA, gprRA); - // rA |= rB - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_OR, gprRA, gprRB); - // rA = ~rA - if( opcode&PPC_OPC_RC ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprRA, gprRA, 0, PPCREC_CR_MODE_LOGICAL); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprRA, gprRA); - } - } + // rA = rS & ~rB; + IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); + IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); + IMLReg regTmp = _GetRegTemporary(ppcImlGenContext, 0); + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, regTmp, regB); + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_AND, regA, regS, regTmp); + if (opcode & PPC_OPC_RC) + PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } -void PPCRecompilerImlGen_ANDI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rS, rA; - uint32 imm; - PPC_OPC_TEMPL_D_UImm(opcode, rS, rA, imm); - // ANDI. always sets cr0 flags - sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); - sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - // rA = rS - if( gprDestReg != gprSourceReg ) - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg); - // rA &= imm32 - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_AND, gprDestReg, (sint32)imm, 0, false, false, 0, PPCREC_CR_MODE_LOGICAL); -} - -void PPCRecompilerImlGen_ANDIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rS, rA; - uint32 imm; - PPC_OPC_TEMPL_D_Shift16(opcode, rS, rA, imm); - // ANDI. always sets cr0 flags - sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); - sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - // rA = rS - if( gprDestReg != gprSourceReg ) - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg); - // rA &= imm32 - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_AND, gprDestReg, (sint32)imm, 0, false, false, 0, PPCREC_CR_MODE_LOGICAL); -} - -bool PPCRecompilerImlGen_XOR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) +bool PPCRecompilerImlGen_XOR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, bool complementResult) { sint32 rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); if( rS == rB ) { - // xor register with itself - sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - if( opcode&PPC_OPC_RC ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_LOGICAL); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprDestReg); - } + ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regA, 0); } else { - // rA = rS ^ rA - sint32 gprSource1Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); - sint32 gprSource2Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); - sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - if( gprSource1Reg == gprDestReg || gprSource2Reg == gprDestReg ) - { - // make sure we don't overwrite rS or rA - if( gprSource1Reg == gprDestReg ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprSource2Reg); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprSource1Reg); - } - if( opcode&PPC_OPC_RC ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_LOGICAL); - } - } - else - { - // rA = rS - if( gprDestReg != gprSource1Reg ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSource1Reg); - } - // rA ^= rB - if( opcode&PPC_OPC_RC ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprSource2Reg, 0, PPCREC_CR_MODE_LOGICAL); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprSource2Reg); - } - } + IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); + IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_XOR, regA, regS, regB); } + if (complementResult) + ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, regA, regA); + if (opcode & PPC_OPC_RC) + PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } - -bool PPCRecompilerImlGen_EQV(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) +void PPCRecompilerImlGen_ANDI_ANDIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, bool isShifted) { - sint32 rS, rA, rB; - PPC_OPC_TEMPL_X(opcode, rS, rA, rB); - if( rS == rB ) + sint32 rS, rA; + uint32 imm; + if (isShifted) { - // xor register with itself, then invert - sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprDestReg); - if( opcode&PPC_OPC_RC ) - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_LOGICAL); - else - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg); + PPC_OPC_TEMPL_D_Shift16(opcode, rS, rA, imm); } else { - // rA = ~(rS ^ rA) - sint32 gprSource1Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); - sint32 gprSource2Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); - sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - if( gprSource1Reg == gprDestReg || gprSource2Reg == gprDestReg ) - { - // make sure we don't overwrite rS or rA - if( gprSource1Reg == gprDestReg ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprSource2Reg); - } - else - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprSource1Reg); - } - } - else - { - // rA = rS - if( gprDestReg != gprSource1Reg ) - { - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSource1Reg); - } - // rA ^= rB - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprSource2Reg); - } - if( opcode&PPC_OPC_RC ) - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_LOGICAL); - else - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg); + PPC_OPC_TEMPL_D_UImm(opcode, rS, rA, imm); } - return true; + IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_AND, regA, regS, (sint32)imm); + // ANDI/ANDIS always updates cr0 + PPCImlGen_UpdateCR0(ppcImlGenContext, regA); } -void PPCRecompilerImlGen_ORI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) +void PPCRecompilerImlGen_ORI_ORIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, bool isShifted) { sint32 rS, rA; uint32 imm; - PPC_OPC_TEMPL_D_UImm(opcode, rS, rA, imm); - // ORI does not set cr0 flags - //hCPU->gpr[rA] = hCPU->gpr[rS] | imm; - sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); - sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - // rA = rS - if( gprDestReg != gprSourceReg ) - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg); - // rA |= imm32 - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_OR, gprDestReg, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); + if (isShifted) + { + PPC_OPC_TEMPL_D_Shift16(opcode, rS, rA, imm); + } + else + { + PPC_OPC_TEMPL_D_UImm(opcode, rS, rA, imm); + } + IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_OR, regA, regS, (sint32)imm); } -void PPCRecompilerImlGen_ORIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) +void PPCRecompilerImlGen_XORI_XORIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, bool isShifted) { sint32 rS, rA; uint32 imm; - PPC_OPC_TEMPL_D_Shift16(opcode, rS, rA, imm); - // ORI does not set cr0 flags - //hCPU->gpr[rA] = hCPU->gpr[rS] | imm; - sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); - sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - // rA = rS - if( gprDestReg != gprSourceReg ) - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg); - // rA |= imm32 - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_OR, gprDestReg, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); -} - -void PPCRecompilerImlGen_XORI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rS, rA; - uint32 imm; - PPC_OPC_TEMPL_D_UImm(opcode, rS, rA, imm); - //hCPU->gpr[rA] = hCPU->gpr[rS] ^ imm; - // XORI does not set cr0 flags - sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); - sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - // rA = rS - if( gprDestReg != gprSourceReg ) - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg); - // rA |= imm32 - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_XOR, gprDestReg, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); -} - -void PPCRecompilerImlGen_XORIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) -{ - sint32 rS, rA; - uint32 imm; - PPC_OPC_TEMPL_D_Shift16(opcode, rS, rA, imm); - //hCPU->gpr[rA] = hCPU->gpr[rS] ^ imm; - // XORIS does not set cr0 flags - sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); - sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); - // rA = rS - if( gprDestReg != gprSourceReg ) - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg); - // rA |= imm32 - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_XOR, gprDestReg, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); + if (isShifted) + { + PPC_OPC_TEMPL_D_Shift16(opcode, rS, rA, imm); + } + else + { + PPC_OPC_TEMPL_D_UImm(opcode, rS, rA, imm); + } + IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); + IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_XOR, regA, regS, (sint32)imm); } bool PPCRecompilerImlGen_CROR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int crD, crA, crB; PPC_OPC_TEMPL_X(opcode, crD, crA, crB); - PPCRecompilerImlGen_generateNewInstruction_cr(ppcImlGenContext, PPCREC_IML_OP_CR_OR, crD, crA, crB); + IMLReg regCrA = _GetRegCR(ppcImlGenContext, crA); + IMLReg regCrB = _GetRegCR(ppcImlGenContext, crB); + IMLReg regCrR = _GetRegCR(ppcImlGenContext, crD); + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_OR, regCrR, regCrA, regCrB); return true; } @@ -2901,7 +1651,12 @@ bool PPCRecompilerImlGen_CRORC(ppcImlGenContext_t* ppcImlGenContext, uint32 opco { int crD, crA, crB; PPC_OPC_TEMPL_X(opcode, crD, crA, crB); - PPCRecompilerImlGen_generateNewInstruction_cr(ppcImlGenContext, PPCREC_IML_OP_CR_ORC, crD, crA, crB); + IMLReg regCrA = _GetRegCR(ppcImlGenContext, crA); + IMLReg regCrB = _GetRegCR(ppcImlGenContext, crB); + IMLReg regCrR = _GetRegCR(ppcImlGenContext, crD); + IMLReg regTmp = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY); + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_XOR, regTmp, regCrB, 1); // invert crB + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_OR, regCrR, regCrA, regTmp); return true; } @@ -2909,7 +1664,10 @@ bool PPCRecompilerImlGen_CRAND(ppcImlGenContext_t* ppcImlGenContext, uint32 opco { int crD, crA, crB; PPC_OPC_TEMPL_X(opcode, crD, crA, crB); - PPCRecompilerImlGen_generateNewInstruction_cr(ppcImlGenContext, PPCREC_IML_OP_CR_AND, crD, crA, crB); + IMLReg regCrA = _GetRegCR(ppcImlGenContext, crA); + IMLReg regCrB = _GetRegCR(ppcImlGenContext, crB); + IMLReg regCrR = _GetRegCR(ppcImlGenContext, crD); + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_AND, regCrR, regCrA, regCrB); return true; } @@ -2917,7 +1675,12 @@ bool PPCRecompilerImlGen_CRANDC(ppcImlGenContext_t* ppcImlGenContext, uint32 opc { int crD, crA, crB; PPC_OPC_TEMPL_X(opcode, crD, crA, crB); - PPCRecompilerImlGen_generateNewInstruction_cr(ppcImlGenContext, PPCREC_IML_OP_CR_ANDC, crD, crA, crB); + IMLReg regCrA = _GetRegCR(ppcImlGenContext, crA); + IMLReg regCrB = _GetRegCR(ppcImlGenContext, crB); + IMLReg regCrR = _GetRegCR(ppcImlGenContext, crD); + IMLReg regTmp = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY); + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_XOR, regTmp, regCrB, 1); // invert crB + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_AND, regCrR, regCrA, regTmp); return true; } @@ -2925,17 +1688,15 @@ bool PPCRecompilerImlGen_CRXOR(ppcImlGenContext_t* ppcImlGenContext, uint32 opco { int crD, crA, crB; PPC_OPC_TEMPL_X(opcode, crD, crA, crB); - if (crA == crB) + IMLReg regCrA = _GetRegCR(ppcImlGenContext, crA); + IMLReg regCrB = _GetRegCR(ppcImlGenContext, crB); + IMLReg regCrR = _GetRegCR(ppcImlGenContext, crD); + if (regCrA == regCrB) { - // both operands equal, clear bit in crD - // PPC's assert() uses this to pass a parameter to OSPanic - PPCRecompilerImlGen_generateNewInstruction_cr(ppcImlGenContext, PPCREC_IML_OP_CR_CLEAR, crD, 0, 0); + ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regCrR, 0); return true; } - else - { - return false; - } + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_XOR, regCrR, regCrA, regCrB); return true; } @@ -2943,23 +1704,24 @@ bool PPCRecompilerImlGen_CREQV(ppcImlGenContext_t* ppcImlGenContext, uint32 opco { int crD, crA, crB; PPC_OPC_TEMPL_X(opcode, crD, crA, crB); - if (crA == crB) + IMLReg regCrA = _GetRegCR(ppcImlGenContext, crA); + IMLReg regCrB = _GetRegCR(ppcImlGenContext, crB); + IMLReg regCrR = _GetRegCR(ppcImlGenContext, crD); + if (regCrA == regCrB) { - // both operands equal, set bit in crD - PPCRecompilerImlGen_generateNewInstruction_cr(ppcImlGenContext, PPCREC_IML_OP_CR_SET, crD, 0, 0); + ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regCrR, 1); return true; } - else - { - return false; - } + IMLReg regTmp = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY); + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_XOR, regTmp, regCrB, 1); // invert crB + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_XOR, regCrR, regCrA, regTmp); return true; } bool PPCRecompilerImlGen_HLE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { uint32 hleFuncId = opcode&0xFFFF; - PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_HLE, ppcImlGenContext->ppcAddressOfCurrentInstruction, hleFuncId, 0); + ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_HLE, ppcImlGenContext->ppcAddressOfCurrentInstruction, hleFuncId, 0, IMLREG_INVALID); return true; } @@ -2970,12 +1732,6 @@ uint32 PPCRecompiler_iterateCurrentInstruction(ppcImlGenContext_t* ppcImlGenCont return v; } -uint32 PPCRecompiler_getInstructionByOffset(ppcImlGenContext_t* ppcImlGenContext, uint32 offset) -{ - uint32 v = CPU_swapEndianU32(*(ppcImlGenContext->currentInstruction + offset/4)); - return v; -} - uint32 PPCRecompiler_getCurrentInstruction(ppcImlGenContext_t* ppcImlGenContext) { uint32 v = CPU_swapEndianU32(*(ppcImlGenContext->currentInstruction)); @@ -2988,477 +1744,7 @@ uint32 PPCRecompiler_getPreviousInstruction(ppcImlGenContext_t* ppcImlGenContext return v; } -char _tempOpcodename[32]; - -const char* PPCRecompiler_getOpcodeDebugName(PPCRecImlInstruction_t* iml) -{ - uint32 op = iml->operation; - if (op == PPCREC_IML_OP_ASSIGN) - return "MOV"; - else if (op == PPCREC_IML_OP_ADD) - return "ADD"; - else if (op == PPCREC_IML_OP_SUB) - return "SUB"; - else if (op == PPCREC_IML_OP_ADD_CARRY_UPDATE_CARRY) - return "ADDCSC"; - else if (op == PPCREC_IML_OP_OR) - return "OR"; - else if (op == PPCREC_IML_OP_AND) - return "AND"; - else if (op == PPCREC_IML_OP_XOR) - return "XOR"; - else if (op == PPCREC_IML_OP_LEFT_SHIFT) - return "LSH"; - else if (op == PPCREC_IML_OP_RIGHT_SHIFT) - return "RSH"; - else if (op == PPCREC_IML_OP_MULTIPLY_SIGNED) - return "MULS"; - else if (op == PPCREC_IML_OP_DIVIDE_SIGNED) - return "DIVS"; - - sprintf(_tempOpcodename, "OP0%02x_T%d", iml->operation, iml->type); - return _tempOpcodename; -} - -void PPCRecDebug_addRegisterParam(StringBuf& strOutput, sint32 virtualRegister, bool isLast = false) -{ - if (isLast) - { - if (virtualRegister < 10) - strOutput.addFmt("t{} ", virtualRegister); - else - strOutput.addFmt("t{}", virtualRegister); - return; - } - if (virtualRegister < 10) - strOutput.addFmt("t{} , ", virtualRegister); - else - strOutput.addFmt("t{}, ", virtualRegister); -} - -void PPCRecDebug_addS32Param(StringBuf& strOutput, sint32 val, bool isLast = false) -{ - if (isLast) - { - strOutput.addFmt("0x{:08x}", val); - return; - } - strOutput.addFmt("0x{:08x}, ", val); -} - -void PPCRecompilerDebug_printLivenessRangeInfo(StringBuf& currentLineText, PPCRecImlSegment_t* imlSegment, sint32 offset) -{ - // pad to 70 characters - sint32 index = currentLineText.getLen(); - while (index < 70) - { - debug_printf(" "); - index++; - } - raLivenessSubrange_t* subrangeItr = imlSegment->raInfo.linkedList_allSubranges; - while (subrangeItr) - { - if (offset == subrangeItr->start.index) - { - if (false)//subrange->isDirtied && i == subrange->becomesDirtyAtIndex.index) - { - debug_printf("*%-2d", subrangeItr->range->virtualRegister); - } - else - { - debug_printf("|%-2d", subrangeItr->range->virtualRegister); - } - } - else if (false)//subrange->isDirtied && i == subrange->becomesDirtyAtIndex.index ) - { - debug_printf("* "); - } - else if (offset >= subrangeItr->start.index && offset < subrangeItr->end.index) - { - debug_printf("| "); - } - else - { - debug_printf(" "); - } - index += 3; - // next - subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; - } -} - -void PPCRecompiler_dumpIMLSegment(PPCRecImlSegment_t* imlSegment, sint32 segmentIndex, bool printLivenessRangeInfo) -{ - StringBuf strOutput(1024); - - strOutput.addFmt("SEGMENT 0x{:04x} 0x{:08x} PPC 0x{:08x} - 0x{:08x} Loop-depth {}", segmentIndex, imlSegment->ppcAddress, imlSegment->ppcAddrMin, imlSegment->ppcAddrMax, imlSegment->loopDepth); - if (imlSegment->isEnterable) - { - strOutput.addFmt(" ENTERABLE (0x{:08x})", imlSegment->enterPPCAddress); - } - else if( imlSegment->isJumpDestination ) - { - strOutput.addFmt(" JUMP-DEST (0x{:08x})", imlSegment->jumpDestinationPPCAddress); - } - - debug_printf("%s\n", strOutput.c_str()); - - strOutput.reset(); - strOutput.addFmt("SEGMENT NAME 0x{:016x}", (uintptr_t)imlSegment); - debug_printf("%s", strOutput.c_str()); - - if (printLivenessRangeInfo) - { - PPCRecompilerDebug_printLivenessRangeInfo(strOutput, imlSegment, RA_INTER_RANGE_START); - } - debug_printf("\n"); - - sint32 lineOffsetParameters = 18; - - for(sint32 i=0; iimlListCount; i++) - { - // don't log NOP instructions unless they have an associated PPC address - if(imlSegment->imlList[i].type == PPCREC_IML_TYPE_NO_OP && imlSegment->imlList[i].associatedPPCAddress == MPTR_NULL) - continue; - strOutput.reset(); - strOutput.addFmt("{:08x} ", imlSegment->imlList[i].associatedPPCAddress); - if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_R_NAME || imlSegment->imlList[i].type == PPCREC_IML_TYPE_NAME_R) - { - if(imlSegment->imlList[i].type == PPCREC_IML_TYPE_R_NAME) - strOutput.add("LD_NAME"); - else - strOutput.add("ST_NAME"); - while ((sint32)strOutput.getLen() < lineOffsetParameters) - strOutput.add(" "); - - PPCRecDebug_addRegisterParam(strOutput, imlSegment->imlList[i].op_r_name.registerIndex); - - strOutput.addFmt("name_{} (", imlSegment->imlList[i].op_r_name.registerIndex, imlSegment->imlList[i].op_r_name.name); - if( imlSegment->imlList[i].op_r_name.name >= PPCREC_NAME_R0 && imlSegment->imlList[i].op_r_name.name < (PPCREC_NAME_R0+999) ) - { - strOutput.addFmt("r{}", imlSegment->imlList[i].op_r_name.name-PPCREC_NAME_R0); - } - else if( imlSegment->imlList[i].op_r_name.name >= PPCREC_NAME_SPR0 && imlSegment->imlList[i].op_r_name.name < (PPCREC_NAME_SPR0+999) ) - { - strOutput.addFmt("spr{}", imlSegment->imlList[i].op_r_name.name-PPCREC_NAME_SPR0); - } - else - strOutput.add("ukn"); - strOutput.add(")"); - } - else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_R_R ) - { - strOutput.addFmt("{}", PPCRecompiler_getOpcodeDebugName(imlSegment->imlList+i)); - while ((sint32)strOutput.getLen() < lineOffsetParameters) - strOutput.add(" "); - PPCRecDebug_addRegisterParam(strOutput, imlSegment->imlList[i].op_r_r.registerResult); - PPCRecDebug_addRegisterParam(strOutput, imlSegment->imlList[i].op_r_r.registerA, true); - - if( imlSegment->imlList[i].crRegister != PPC_REC_INVALID_REGISTER ) - { - strOutput.addFmt(" -> CR{}", imlSegment->imlList[i].crRegister); - } - } - else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_R_R_R ) - { - strOutput.addFmt("{}", PPCRecompiler_getOpcodeDebugName(imlSegment->imlList + i)); - while ((sint32)strOutput.getLen() < lineOffsetParameters) - strOutput.add(" "); - PPCRecDebug_addRegisterParam(strOutput, imlSegment->imlList[i].op_r_r_r.registerResult); - PPCRecDebug_addRegisterParam(strOutput, imlSegment->imlList[i].op_r_r_r.registerA); - PPCRecDebug_addRegisterParam(strOutput, imlSegment->imlList[i].op_r_r_r.registerB, true); - if( imlSegment->imlList[i].crRegister != PPC_REC_INVALID_REGISTER ) - { - strOutput.addFmt(" -> CR{}", imlSegment->imlList[i].crRegister); - } - } - else if (imlSegment->imlList[i].type == PPCREC_IML_TYPE_R_R_S32) - { - strOutput.addFmt("{}", PPCRecompiler_getOpcodeDebugName(imlSegment->imlList + i)); - while ((sint32)strOutput.getLen() < lineOffsetParameters) - strOutput.add(" "); - - PPCRecDebug_addRegisterParam(strOutput, imlSegment->imlList[i].op_r_r_s32.registerResult); - PPCRecDebug_addRegisterParam(strOutput, imlSegment->imlList[i].op_r_r_s32.registerA); - PPCRecDebug_addS32Param(strOutput, imlSegment->imlList[i].op_r_r_s32.immS32, true); - - if (imlSegment->imlList[i].crRegister != PPC_REC_INVALID_REGISTER) - { - strOutput.addFmt(" -> CR{}", imlSegment->imlList[i].crRegister); - } - } - else if (imlSegment->imlList[i].type == PPCREC_IML_TYPE_R_S32) - { - strOutput.addFmt("{}", PPCRecompiler_getOpcodeDebugName(imlSegment->imlList + i)); - while ((sint32)strOutput.getLen() < lineOffsetParameters) - strOutput.add(" "); - - PPCRecDebug_addRegisterParam(strOutput, imlSegment->imlList[i].op_r_immS32.registerIndex); - PPCRecDebug_addS32Param(strOutput, imlSegment->imlList[i].op_r_immS32.immS32, true); - - if (imlSegment->imlList[i].crRegister != PPC_REC_INVALID_REGISTER) - { - strOutput.addFmt(" -> CR{}", imlSegment->imlList[i].crRegister); - } - } - else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_JUMPMARK ) - { - strOutput.addFmt("jm_{:08x}:", imlSegment->imlList[i].op_jumpmark.address); - } - else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_PPC_ENTER ) - { - strOutput.addFmt("ppcEnter_{:08x}:", imlSegment->imlList[i].op_ppcEnter.ppcAddress); - } - else if(imlSegment->imlList[i].type == PPCREC_IML_TYPE_LOAD || imlSegment->imlList[i].type == PPCREC_IML_TYPE_STORE || - imlSegment->imlList[i].type == PPCREC_IML_TYPE_LOAD_INDEXED || imlSegment->imlList[i].type == PPCREC_IML_TYPE_STORE_INDEXED ) - { - if(imlSegment->imlList[i].type == PPCREC_IML_TYPE_LOAD || imlSegment->imlList[i].type == PPCREC_IML_TYPE_LOAD_INDEXED) - strOutput.add("LD_"); - else - strOutput.add("ST_"); - - if (imlSegment->imlList[i].op_storeLoad.flags2.signExtend) - strOutput.add("S"); - else - strOutput.add("U"); - strOutput.addFmt("{}", imlSegment->imlList[i].op_storeLoad.copyWidth); - - while ((sint32)strOutput.getLen() < lineOffsetParameters) - strOutput.add(" "); - - PPCRecDebug_addRegisterParam(strOutput, imlSegment->imlList[i].op_storeLoad.registerData); - - if(imlSegment->imlList[i].type == PPCREC_IML_TYPE_LOAD_INDEXED || imlSegment->imlList[i].type == PPCREC_IML_TYPE_STORE_INDEXED) - strOutput.addFmt("[t{}+t{}]", imlSegment->imlList[i].op_storeLoad.registerMem, imlSegment->imlList[i].op_storeLoad.registerMem2); - else - strOutput.addFmt("[t{}+{}]", imlSegment->imlList[i].op_storeLoad.registerMem, imlSegment->imlList[i].op_storeLoad.immS32); - } - else if (imlSegment->imlList[i].type == PPCREC_IML_TYPE_MEM2MEM) - { - strOutput.addFmt("{} [t{}+{}] = [t{}+{}]", imlSegment->imlList[i].op_mem2mem.copyWidth, imlSegment->imlList[i].op_mem2mem.dst.registerMem, imlSegment->imlList[i].op_mem2mem.dst.immS32, imlSegment->imlList[i].op_mem2mem.src.registerMem, imlSegment->imlList[i].op_mem2mem.src.immS32); - } - else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_CJUMP ) - { - if (imlSegment->imlList[i].op_conditionalJump.condition == PPCREC_JUMP_CONDITION_E) - strOutput.add("JE"); - else if (imlSegment->imlList[i].op_conditionalJump.condition == PPCREC_JUMP_CONDITION_NE) - strOutput.add("JNE"); - else if (imlSegment->imlList[i].op_conditionalJump.condition == PPCREC_JUMP_CONDITION_G) - strOutput.add("JG"); - else if (imlSegment->imlList[i].op_conditionalJump.condition == PPCREC_JUMP_CONDITION_GE) - strOutput.add("JGE"); - else if (imlSegment->imlList[i].op_conditionalJump.condition == PPCREC_JUMP_CONDITION_L) - strOutput.add("JL"); - else if (imlSegment->imlList[i].op_conditionalJump.condition == PPCREC_JUMP_CONDITION_LE) - strOutput.add("JLE"); - else if (imlSegment->imlList[i].op_conditionalJump.condition == PPCREC_JUMP_CONDITION_NONE) - strOutput.add("JALW"); // jump always - else - cemu_assert_unimplemented(); - strOutput.addFmt(" jm_{:08x} (cr{})", imlSegment->imlList[i].op_conditionalJump.jumpmarkAddress, imlSegment->imlList[i].crRegister); - } - else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_NO_OP ) - { - strOutput.add("NOP"); - } - else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_MACRO ) - { - if( imlSegment->imlList[i].operation == PPCREC_IML_MACRO_BLR ) - { - strOutput.addFmt("MACRO BLR 0x{:08x} cycles (depr): {}", imlSegment->imlList[i].op_macro.param, (sint32)imlSegment->imlList[i].op_macro.paramU16); - } - else if( imlSegment->imlList[i].operation == PPCREC_IML_MACRO_BLRL ) - { - strOutput.addFmt("MACRO BLRL 0x{:08x} cycles (depr): {}", imlSegment->imlList[i].op_macro.param, (sint32)imlSegment->imlList[i].op_macro.paramU16); - } - else if( imlSegment->imlList[i].operation == PPCREC_IML_MACRO_BCTR ) - { - strOutput.addFmt("MACRO BCTR 0x{:08x} cycles (depr): {}", imlSegment->imlList[i].op_macro.param, (sint32)imlSegment->imlList[i].op_macro.paramU16); - } - else if( imlSegment->imlList[i].operation == PPCREC_IML_MACRO_BCTRL ) - { - strOutput.addFmt("MACRO BCTRL 0x{:08x} cycles (depr): {}", imlSegment->imlList[i].op_macro.param, (sint32)imlSegment->imlList[i].op_macro.paramU16); - } - else if( imlSegment->imlList[i].operation == PPCREC_IML_MACRO_BL ) - { - strOutput.addFmt("MACRO BL 0x{:08x} -> 0x{:08x} cycles (depr): {}", imlSegment->imlList[i].op_macro.param, imlSegment->imlList[i].op_macro.param2, (sint32)imlSegment->imlList[i].op_macro.paramU16); - } - else if( imlSegment->imlList[i].operation == PPCREC_IML_MACRO_B_FAR ) - { - strOutput.addFmt("MACRO B_FAR 0x{:08x} -> 0x{:08x} cycles (depr): {}", imlSegment->imlList[i].op_macro.param, imlSegment->imlList[i].op_macro.param2, (sint32)imlSegment->imlList[i].op_macro.paramU16); - } - else if( imlSegment->imlList[i].operation == PPCREC_IML_MACRO_LEAVE ) - { - strOutput.addFmt("MACRO LEAVE ppc: 0x{:08x}", imlSegment->imlList[i].op_macro.param); - } - else if( imlSegment->imlList[i].operation == PPCREC_IML_MACRO_HLE ) - { - strOutput.addFmt("MACRO HLE ppcAddr: 0x{:08x} funcId: 0x{:08x}", imlSegment->imlList[i].op_macro.param, imlSegment->imlList[i].op_macro.param2); - } - else if( imlSegment->imlList[i].operation == PPCREC_IML_MACRO_MFTB ) - { - strOutput.addFmt("MACRO MFTB ppcAddr: 0x{:08x} sprId: 0x{:08x}", imlSegment->imlList[i].op_macro.param, imlSegment->imlList[i].op_macro.param2); - } - else if( imlSegment->imlList[i].operation == PPCREC_IML_MACRO_COUNT_CYCLES ) - { - strOutput.addFmt("MACRO COUNT_CYCLES cycles: {}", imlSegment->imlList[i].op_macro.param); - } - else - { - strOutput.addFmt("MACRO ukn operation {}", imlSegment->imlList[i].operation); - } - } - else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_FPR_R_NAME ) - { - strOutput.addFmt("fpr_t{} = name_{} (", imlSegment->imlList[i].op_r_name.registerIndex, imlSegment->imlList[i].op_r_name.name); - if( imlSegment->imlList[i].op_r_name.name >= PPCREC_NAME_FPR0 && imlSegment->imlList[i].op_r_name.name < (PPCREC_NAME_FPR0+999) ) - { - strOutput.addFmt("fpr{}", imlSegment->imlList[i].op_r_name.name-PPCREC_NAME_FPR0); - } - else if( imlSegment->imlList[i].op_r_name.name >= PPCREC_NAME_TEMPORARY_FPR0 && imlSegment->imlList[i].op_r_name.name < (PPCREC_NAME_TEMPORARY_FPR0+999) ) - { - strOutput.addFmt("tempFpr{}", imlSegment->imlList[i].op_r_name.name-PPCREC_NAME_TEMPORARY_FPR0); - } - else - strOutput.add("ukn"); - strOutput.add(")"); - } - else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_FPR_NAME_R ) - { - strOutput.addFmt("name_{} (", imlSegment->imlList[i].op_r_name.name); - if( imlSegment->imlList[i].op_r_name.name >= PPCREC_NAME_FPR0 && imlSegment->imlList[i].op_r_name.name < (PPCREC_NAME_FPR0+999) ) - { - strOutput.addFmt("fpr{}", imlSegment->imlList[i].op_r_name.name-PPCREC_NAME_FPR0); - } - else if( imlSegment->imlList[i].op_r_name.name >= PPCREC_NAME_TEMPORARY_FPR0 && imlSegment->imlList[i].op_r_name.name < (PPCREC_NAME_TEMPORARY_FPR0+999) ) - { - strOutput.addFmt("tempFpr{}", imlSegment->imlList[i].op_r_name.name-PPCREC_NAME_TEMPORARY_FPR0); - } - else - strOutput.add("ukn"); - strOutput.addFmt(") = fpr_t{}", imlSegment->imlList[i].op_r_name.registerIndex); - } - else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_FPR_LOAD ) - { - strOutput.addFmt("fpr_t{} = ", imlSegment->imlList[i].op_storeLoad.registerData); - if( imlSegment->imlList[i].op_storeLoad.flags2.signExtend ) - strOutput.add("S"); - else - strOutput.add("U"); - strOutput.addFmt("{} [t{}+{}] mode {}", imlSegment->imlList[i].op_storeLoad.copyWidth / 8, imlSegment->imlList[i].op_storeLoad.registerMem, imlSegment->imlList[i].op_storeLoad.immS32, imlSegment->imlList[i].op_storeLoad.mode); - if (imlSegment->imlList[i].op_storeLoad.flags2.notExpanded) - { - strOutput.addFmt(" "); - } - } - else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_FPR_STORE ) - { - if( imlSegment->imlList[i].op_storeLoad.flags2.signExtend ) - strOutput.add("S"); - else - strOutput.add("U"); - strOutput.addFmt("{} [t{}+{}]", imlSegment->imlList[i].op_storeLoad.copyWidth/8, imlSegment->imlList[i].op_storeLoad.registerMem, imlSegment->imlList[i].op_storeLoad.immS32); - strOutput.addFmt("= fpr_t{} mode {}\n", imlSegment->imlList[i].op_storeLoad.registerData, imlSegment->imlList[i].op_storeLoad.mode); - } - else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_FPR_R_R ) - { - strOutput.addFmt("{:-6} ", PPCRecompiler_getOpcodeDebugName(&imlSegment->imlList[i])); - strOutput.addFmt("fpr{:02d}, fpr{:02d}", imlSegment->imlList[i].op_fpr_r_r.registerResult, imlSegment->imlList[i].op_fpr_r_r.registerOperand); - } - else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_FPR_R_R_R_R ) - { - strOutput.addFmt("{:-6} ", PPCRecompiler_getOpcodeDebugName(&imlSegment->imlList[i])); - strOutput.addFmt("fpr{:02d}, fpr{:02d}, fpr{:02d}, fpr{:02d}", imlSegment->imlList[i].op_fpr_r_r_r_r.registerResult, imlSegment->imlList[i].op_fpr_r_r_r_r.registerOperandA, imlSegment->imlList[i].op_fpr_r_r_r_r.registerOperandB, imlSegment->imlList[i].op_fpr_r_r_r_r.registerOperandC); - } - else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_FPR_R_R_R ) - { - strOutput.addFmt("{:-6} ", PPCRecompiler_getOpcodeDebugName(&imlSegment->imlList[i])); - strOutput.addFmt("fpr{:02d}, fpr{:02d}, fpr{:02d}", imlSegment->imlList[i].op_fpr_r_r_r.registerResult, imlSegment->imlList[i].op_fpr_r_r_r.registerOperandA, imlSegment->imlList[i].op_fpr_r_r_r.registerOperandB); - } - else if (imlSegment->imlList[i].type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK) - { - strOutput.addFmt("CYCLE_CHECK jm_{:08x}\n", imlSegment->imlList[i].op_conditionalJump.jumpmarkAddress); - } - else if (imlSegment->imlList[i].type == PPCREC_IML_TYPE_CONDITIONAL_R_S32) - { - strOutput.addFmt("t{} ", imlSegment->imlList[i].op_conditional_r_s32.registerIndex); - bool displayAsHex = false; - if (imlSegment->imlList[i].operation == PPCREC_IML_OP_ASSIGN) - { - displayAsHex = true; - strOutput.add("="); - } - else - strOutput.addFmt("(unknown operation CONDITIONAL_R_S32 {})", imlSegment->imlList[i].operation); - if (displayAsHex) - strOutput.addFmt(" 0x{:x}", imlSegment->imlList[i].op_conditional_r_s32.immS32); - else - strOutput.addFmt(" {}", imlSegment->imlList[i].op_conditional_r_s32.immS32); - strOutput.add(" (conditional)"); - if (imlSegment->imlList[i].crRegister != PPC_REC_INVALID_REGISTER) - { - strOutput.addFmt(" -> and update CR{}", imlSegment->imlList[i].crRegister); - } - } - else - { - strOutput.addFmt("Unknown iml type {}", imlSegment->imlList[i].type); - } - debug_printf("%s", strOutput.c_str()); - if (printLivenessRangeInfo) - { - PPCRecompilerDebug_printLivenessRangeInfo(strOutput, imlSegment, i); - } - debug_printf("\n"); - } - // all ranges - if (printLivenessRangeInfo) - { - debug_printf("Ranges-VirtReg "); - raLivenessSubrange_t* subrangeItr = imlSegment->raInfo.linkedList_allSubranges; - while(subrangeItr) - { - debug_printf("v%-2d", subrangeItr->range->virtualRegister); - subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; - } - debug_printf("\n"); - debug_printf("Ranges-PhysReg "); - subrangeItr = imlSegment->raInfo.linkedList_allSubranges; - while (subrangeItr) - { - debug_printf("p%-2d", subrangeItr->range->physicalRegister); - subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; - } - debug_printf("\n"); - } - // branch info - debug_printf("Links from: "); - for (sint32 i = 0; i < imlSegment->list_prevSegments.size(); i++) - { - if (i) - debug_printf(", "); - debug_printf("%p", (void*)imlSegment->list_prevSegments[i]); - } - debug_printf("\n"); - debug_printf("Links to: "); - if (imlSegment->nextSegmentBranchNotTaken) - debug_printf("%p (no branch), ", (void*)imlSegment->nextSegmentBranchNotTaken); - if (imlSegment->nextSegmentBranchTaken) - debug_printf("%p (branch)", (void*)imlSegment->nextSegmentBranchTaken); - debug_printf("\n"); -} - -void PPCRecompiler_dumpIML(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext) -{ - for(sint32 f=0; fsegmentListCount; f++) - { - PPCRecImlSegment_t* imlSegment = ppcImlGenContext->segmentList[f]; - PPCRecompiler_dumpIMLSegment(imlSegment, f); - debug_printf("\n"); - } -} - -void PPCRecompilerIml_setSegmentPoint(ppcRecompilerSegmentPoint_t* segmentPoint, PPCRecImlSegment_t* imlSegment, sint32 index) +void PPCRecompilerIml_setSegmentPoint(IMLSegmentPoint* segmentPoint, IMLSegment* imlSegment, sint32 index) { segmentPoint->imlSegment = imlSegment; segmentPoint->index = index; @@ -3469,7 +1755,7 @@ void PPCRecompilerIml_setSegmentPoint(ppcRecompilerSegmentPoint_t* segmentPoint, imlSegment->segmentPointList = segmentPoint; } -void PPCRecompilerIml_removeSegmentPoint(ppcRecompilerSegmentPoint_t* segmentPoint) +void PPCRecompilerIml_removeSegmentPoint(IMLSegmentPoint* segmentPoint) { if (segmentPoint->prev) segmentPoint->prev->next = segmentPoint->next; @@ -3483,30 +1769,24 @@ void PPCRecompilerIml_removeSegmentPoint(ppcRecompilerSegmentPoint_t* segmentPoi * Insert multiple no-op instructions * Warning: Can invalidate any previous instruction structs from the same segment */ -void PPCRecompiler_pushBackIMLInstructions(PPCRecImlSegment_t* imlSegment, sint32 index, sint32 shiftBackCount) +void PPCRecompiler_pushBackIMLInstructions(IMLSegment* imlSegment, sint32 index, sint32 shiftBackCount) { - cemu_assert(index >= 0 && index <= imlSegment->imlListCount); + cemu_assert_debug(index >= 0 && index <= imlSegment->imlList.size()); + + imlSegment->imlList.insert(imlSegment->imlList.begin() + index, shiftBackCount, {}); + + memset(imlSegment->imlList.data() + index, 0, sizeof(IMLInstruction) * shiftBackCount); - if (imlSegment->imlListCount + shiftBackCount > imlSegment->imlListSize) - { - sint32 newSize = imlSegment->imlListCount + shiftBackCount + std::max(2, imlSegment->imlListSize/2); - imlSegment->imlList = (PPCRecImlInstruction_t*)realloc(imlSegment->imlList, sizeof(PPCRecImlInstruction_t)*newSize); - imlSegment->imlListSize = newSize; - } - for (sint32 i = (sint32)imlSegment->imlListCount - 1; i >= index; i--) - { - memcpy(imlSegment->imlList + (i + shiftBackCount), imlSegment->imlList + i, sizeof(PPCRecImlInstruction_t)); - } // fill empty space with NOP instructions for (sint32 i = 0; i < shiftBackCount; i++) { imlSegment->imlList[index + i].type = PPCREC_IML_TYPE_NONE; } - imlSegment->imlListCount += shiftBackCount; + // update position of segment points if (imlSegment->segmentPointList) { - ppcRecompilerSegmentPoint_t* segmentPoint = imlSegment->segmentPointList; + IMLSegmentPoint* segmentPoint = imlSegment->segmentPointList; while (segmentPoint) { if (segmentPoint->index != RA_INTER_RANGE_START && segmentPoint->index != RA_INTER_RANGE_END) @@ -3520,108 +1800,32 @@ void PPCRecompiler_pushBackIMLInstructions(PPCRecImlSegment_t* imlSegment, sint3 } } -/* -* Insert and return new instruction at index -* Warning: Can invalidate any previous instruction structs from the same segment -*/ -PPCRecImlInstruction_t* PPCRecompiler_insertInstruction(PPCRecImlSegment_t* imlSegment, sint32 index) +IMLInstruction* PPCRecompiler_insertInstruction(IMLSegment* imlSegment, sint32 index) { PPCRecompiler_pushBackIMLInstructions(imlSegment, index, 1); - return imlSegment->imlList + index; + return imlSegment->imlList.data() + index; } -/* -* Append and return new instruction at the end of the segment -* Warning: Can invalidate any previous instruction structs from the same segment -*/ -PPCRecImlInstruction_t* PPCRecompiler_appendInstruction(PPCRecImlSegment_t* imlSegment) +IMLInstruction* PPCRecompiler_appendInstruction(IMLSegment* imlSegment) { - sint32 index = imlSegment->imlListCount; - if (index >= imlSegment->imlListSize) - { - sint32 newSize = index+1; - imlSegment->imlList = (PPCRecImlInstruction_t*)realloc(imlSegment->imlList, sizeof(PPCRecImlInstruction_t)*newSize); - imlSegment->imlListSize = newSize; - } - imlSegment->imlListCount++; - memset(imlSegment->imlList + index, 0, sizeof(PPCRecImlInstruction_t)); - return imlSegment->imlList + index; + size_t index = imlSegment->imlList.size(); + imlSegment->imlList.emplace_back(); + memset(imlSegment->imlList.data() + index, 0, sizeof(IMLInstruction)); + return imlSegment->imlList.data() + index; +} + +IMLSegment* PPCRecompilerIml_appendSegment(ppcImlGenContext_t* ppcImlGenContext) +{ + IMLSegment* segment = new IMLSegment(); + ppcImlGenContext->segmentList2.emplace_back(segment); + return segment; } void PPCRecompilerIml_insertSegments(ppcImlGenContext_t* ppcImlGenContext, sint32 index, sint32 count) { - if( (ppcImlGenContext->segmentListCount+count) > ppcImlGenContext->segmentListSize ) - { - // allocate space for more segments - ppcImlGenContext->segmentListSize += count; - ppcImlGenContext->segmentList = (PPCRecImlSegment_t**)realloc(ppcImlGenContext->segmentList, ppcImlGenContext->segmentListSize*sizeof(PPCRecImlSegment_t*)); - } - for(sint32 i=(sint32)ppcImlGenContext->segmentListCount-1; i>=index; i--) - { - memcpy(ppcImlGenContext->segmentList+(i+count), ppcImlGenContext->segmentList+i, sizeof(PPCRecImlSegment_t*)); - } - ppcImlGenContext->segmentListCount += count; - for(sint32 i=0; isegmentList+index+i, 0x00, sizeof(PPCRecImlSegment_t*)); - ppcImlGenContext->segmentList[index+i] = (PPCRecImlSegment_t*)malloc(sizeof(PPCRecImlSegment_t)); - memset(ppcImlGenContext->segmentList[index+i], 0x00, sizeof(PPCRecImlSegment_t)); - ppcImlGenContext->segmentList[index + i]->list_prevSegments = std::vector(); - } -} - -/* - * Allocate and init a new iml instruction segment - */ -PPCRecImlSegment_t* PPCRecompiler_generateImlSegment(ppcImlGenContext_t* ppcImlGenContext) -{ - if( ppcImlGenContext->segmentListCount >= ppcImlGenContext->segmentListSize ) - { - // allocate space for more segments - ppcImlGenContext->segmentListSize *= 2; - ppcImlGenContext->segmentList = (PPCRecImlSegment_t**)realloc(ppcImlGenContext->segmentList, ppcImlGenContext->segmentListSize*sizeof(PPCRecImlSegment_t*)); - } - PPCRecImlSegment_t* ppcRecSegment = new PPCRecImlSegment_t(); - ppcImlGenContext->segmentList[ppcImlGenContext->segmentListCount] = ppcRecSegment; - ppcImlGenContext->segmentListCount++; - return ppcRecSegment; -} - -void PPCRecompiler_freeContext(ppcImlGenContext_t* ppcImlGenContext) -{ - if (ppcImlGenContext->imlList) - { - free(ppcImlGenContext->imlList); - ppcImlGenContext->imlList = nullptr; - } - for(sint32 i=0; isegmentListCount; i++) - { - free(ppcImlGenContext->segmentList[i]->imlList); - delete ppcImlGenContext->segmentList[i]; - } - ppcImlGenContext->segmentListCount = 0; - if (ppcImlGenContext->segmentList) - { - free(ppcImlGenContext->segmentList); - ppcImlGenContext->segmentList = nullptr; - } -} - -bool PPCRecompiler_isSuffixInstruction(PPCRecImlInstruction_t* iml) -{ - if (iml->type == PPCREC_IML_TYPE_MACRO && (iml->operation == PPCREC_IML_MACRO_BLR || iml->operation == PPCREC_IML_MACRO_BCTR) || - iml->type == PPCREC_IML_TYPE_MACRO && iml->operation == PPCREC_IML_MACRO_BL || - iml->type == PPCREC_IML_TYPE_MACRO && iml->operation == PPCREC_IML_MACRO_B_FAR || - iml->type == PPCREC_IML_TYPE_MACRO && iml->operation == PPCREC_IML_MACRO_BLRL || - iml->type == PPCREC_IML_TYPE_MACRO && iml->operation == PPCREC_IML_MACRO_BCTRL || - iml->type == PPCREC_IML_TYPE_MACRO && iml->operation == PPCREC_IML_MACRO_LEAVE || - iml->type == PPCREC_IML_TYPE_MACRO && iml->operation == PPCREC_IML_MACRO_HLE || - iml->type == PPCREC_IML_TYPE_MACRO && iml->operation == PPCREC_IML_MACRO_MFTB || - iml->type == PPCREC_IML_TYPE_PPC_ENTER || - iml->type == PPCREC_IML_TYPE_CJUMP || - iml->type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK) - return true; - return false; + ppcImlGenContext->segmentList2.insert(ppcImlGenContext->segmentList2.begin() + index, count, nullptr); + for (sint32 i = 0; i < count; i++) + ppcImlGenContext->segmentList2[index + i] = new IMLSegment(); } bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext) @@ -3643,15 +1847,18 @@ bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext) switch (PPC_getBits(opcode, 25, 5)) { case 0: - PPCRecompilerImlGen_PS_CMPU0(ppcImlGenContext, opcode); + if( !PPCRecompilerImlGen_PS_CMPU0(ppcImlGenContext, opcode) ) + unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 1: - PPCRecompilerImlGen_PS_CMPO0(ppcImlGenContext, opcode); + if( !PPCRecompilerImlGen_PS_CMPO0(ppcImlGenContext, opcode) ) + unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 2: - PPCRecompilerImlGen_PS_CMPU1(ppcImlGenContext, opcode); + if( !PPCRecompilerImlGen_PS_CMPU1(ppcImlGenContext, opcode) ) + unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; default: @@ -3804,20 +2011,23 @@ bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext) PPCRecompilerImlGen_MULLI(ppcImlGenContext, opcode); break; case 8: // SUBFIC - PPCRecompilerImlGen_SUBFIC(ppcImlGenContext, opcode); + if (!PPCRecompilerImlGen_SUBFIC(ppcImlGenContext, opcode)) + unsupportedInstructionFound = true; break; case 10: // CMPLI - PPCRecompilerImlGen_CMPLI(ppcImlGenContext, opcode); + if (!PPCRecompilerImlGen_CMPI(ppcImlGenContext, opcode, true)) + unsupportedInstructionFound = true; break; case 11: // CMPI - PPCRecompilerImlGen_CMPI(ppcImlGenContext, opcode); + if (!PPCRecompilerImlGen_CMPI(ppcImlGenContext, opcode, false)) + unsupportedInstructionFound = true; break; case 12: // ADDIC - if (PPCRecompilerImlGen_ADDIC(ppcImlGenContext, opcode) == false) + if (PPCRecompilerImlGen_ADDIC_(ppcImlGenContext, opcode, false) == false) unsupportedInstructionFound = true; break; case 13: // ADDIC. - if (PPCRecompilerImlGen_ADDIC_(ppcImlGenContext, opcode) == false) + if (PPCRecompilerImlGen_ADDIC_(ppcImlGenContext, opcode, true) == false) unsupportedInstructionFound = true; break; case 14: // ADDI @@ -3849,8 +2059,8 @@ bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext) case 19: // opcode category 19 switch (PPC_getBits(opcode, 30, 10)) { - case 16: - if (PPCRecompilerImlGen_BCLR(ppcImlGenContext, opcode) == false) + case 16: // BCLR + if (PPCRecompilerImlGen_BCSPR(ppcImlGenContext, opcode, SPR_LR) == false) unsupportedInstructionFound = true; break; case 129: @@ -3881,8 +2091,8 @@ bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext) if (PPCRecompilerImlGen_CROR(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; - case 528: - if (PPCRecompilerImlGen_BCCTR(ppcImlGenContext, opcode) == false) + case 528: // BCCTR + if (PPCRecompilerImlGen_BCSPR(ppcImlGenContext, opcode, SPR_CTR) == false) unsupportedInstructionFound = true; break; default: @@ -3902,37 +2112,34 @@ bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext) if (PPCRecompilerImlGen_RLWNM(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; - case 24: - PPCRecompilerImlGen_ORI(ppcImlGenContext, opcode); + case 24: // ORI + PPCRecompilerImlGen_ORI_ORIS(ppcImlGenContext, opcode, false); break; - case 25: - PPCRecompilerImlGen_ORIS(ppcImlGenContext, opcode); + case 25: // ORIS + PPCRecompilerImlGen_ORI_ORIS(ppcImlGenContext, opcode, true); break; - case 26: - PPCRecompilerImlGen_XORI(ppcImlGenContext, opcode); + case 26: // XORI + PPCRecompilerImlGen_XORI_XORIS(ppcImlGenContext, opcode, false); break; - case 27: - PPCRecompilerImlGen_XORIS(ppcImlGenContext, opcode); + case 27: // XORIS + PPCRecompilerImlGen_XORI_XORIS(ppcImlGenContext, opcode, true); break; - case 28: - PPCRecompilerImlGen_ANDI(ppcImlGenContext, opcode); + case 28: // ANDI + PPCRecompilerImlGen_ANDI_ANDIS(ppcImlGenContext, opcode, false); break; - case 29: - PPCRecompilerImlGen_ANDIS(ppcImlGenContext, opcode); + case 29: // ANDIS + PPCRecompilerImlGen_ANDI_ANDIS(ppcImlGenContext, opcode, true); break; case 31: // opcode category switch (PPC_getBits(opcode, 30, 10)) { case 0: - PPCRecompilerImlGen_CMP(ppcImlGenContext, opcode); + PPCRecompilerImlGen_CMP(ppcImlGenContext, opcode, false); break; case 4: PPCRecompilerImlGen_TW(ppcImlGenContext, opcode); break; case 8: - // todo: Check if we can optimize this pattern: - // SUBFC + SUBFE - // SUBFC if (PPCRecompilerImlGen_SUBFC(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; @@ -3952,8 +2159,8 @@ bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext) if (PPCRecompilerImlGen_LWARX(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; - case 23: - if (PPCRecompilerImlGen_LWZX(ppcImlGenContext, opcode) == false) + case 23: // LWZX + if (!PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 32, false, true, false)) unsupportedInstructionFound = true; break; case 24: @@ -3964,12 +2171,12 @@ bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext) if (PPCRecompilerImlGen_CNTLZW(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; - case 28: - if (PPCRecompilerImlGen_AND(ppcImlGenContext, opcode) == false) + case 28: // AND + if (!PPCRecompilerImlGen_AND_NAND(ppcImlGenContext, opcode, false)) unsupportedInstructionFound = true; break; case 32: - PPCRecompilerImlGen_CMPL(ppcImlGenContext, opcode); + PPCRecompilerImlGen_CMP(ppcImlGenContext, opcode, true); // CMPL break; case 40: if (PPCRecompilerImlGen_SUBF(ppcImlGenContext, opcode) == false) @@ -3978,12 +2185,12 @@ bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext) case 54: // DBCST - Generates no code break; - case 55: - if (PPCRecompilerImlGen_LWZUX(ppcImlGenContext, opcode) == false) + case 55: // LWZUX + if (!PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 32, false, true, true)) unsupportedInstructionFound = true; break; - case 60: - if (PPCRecompilerImlGen_ANDC(ppcImlGenContext, opcode) == false) + case 60: // ANDC + if (!PPCRecompilerImlGen_ANDC(ppcImlGenContext, opcode)) unsupportedInstructionFound = true; break; case 75: @@ -3993,20 +2200,20 @@ bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext) case 86: // DCBF -> No-Op break; - case 87: - if (PPCRecompilerImlGen_LBZX(ppcImlGenContext, opcode) == false) + case 87: // LBZX + if (!PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 8, false, true, false)) unsupportedInstructionFound = true; break; case 104: if (PPCRecompilerImlGen_NEG(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; - case 119: - if (PPCRecompilerImlGen_LBZUX(ppcImlGenContext, opcode) == false) + case 119: // LBZUX + if (!PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 8, false, true, true)) unsupportedInstructionFound = true; break; - case 124: - if (PPCRecompilerImlGen_NOR(ppcImlGenContext, opcode) == false) + case 124: // NOR + if (!PPCRecompilerImlGen_OR_NOR(ppcImlGenContext, opcode, true)) unsupportedInstructionFound = true; break; case 136: @@ -4018,19 +2225,20 @@ bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext) unsupportedInstructionFound = true; break; case 144: - PPCRecompilerImlGen_MTCRF(ppcImlGenContext, opcode); + if( !PPCRecompilerImlGen_MTCRF(ppcImlGenContext, opcode)) + unsupportedInstructionFound = true; break; case 150: - if (PPCRecompilerImlGen_STWCX(ppcImlGenContext, opcode) == false) + if (!PPCRecompilerImlGen_STWCX(ppcImlGenContext, opcode)) unsupportedInstructionFound = true; break; - case 151: - if (PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 32) == false) + case 151: // STWX + if (!PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 32, true, false)) unsupportedInstructionFound = true; break; - case 183: - if (PPCRecompilerImlGen_STORE_INDEXED_UPDATE(ppcImlGenContext, opcode, 32) == false) - unsupportedInstructionFound = true; + case 183: // STWUX + if (!PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 32, true, true)) + unsupportedInstructionFound = true; break; case 200: if (PPCRecompilerImlGen_SUBFZE(ppcImlGenContext, opcode) == false) @@ -4040,8 +2248,8 @@ bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext) if (PPCRecompilerImlGen_ADDZE(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; - case 215: - if (PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 8) == false) + case 215: // STBX + if (!PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 8, true, false)) unsupportedInstructionFound = true; break; case 234: @@ -4052,59 +2260,60 @@ bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext) if (PPCRecompilerImlGen_MULLW(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; - case 247: - if (PPCRecompilerImlGen_STORE_INDEXED_UPDATE(ppcImlGenContext, opcode, 8) == false) + case 247: // STBUX + if (!PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 8, true, true)) unsupportedInstructionFound = true; break; case 266: if (PPCRecompilerImlGen_ADD(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; - case 279: - if (PPCRecompilerImlGen_LHZX(ppcImlGenContext, opcode) == false) + case 279: // LHZX + if (!PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 16, false, true, false)) unsupportedInstructionFound = true; break; - case 284: - PPCRecompilerImlGen_EQV(ppcImlGenContext, opcode); - break; - case 311: - if (PPCRecompilerImlGen_LHZUX(ppcImlGenContext, opcode) == false) + case 284: // EQV (alias to NXOR) + if (!PPCRecompilerImlGen_XOR(ppcImlGenContext, opcode, true)) unsupportedInstructionFound = true; break; - case 316: - if (PPCRecompilerImlGen_XOR(ppcImlGenContext, opcode) == false) + case 311: // LHZUX + if (!PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 16, false, true, true)) + unsupportedInstructionFound = true; + break; + case 316: // XOR + if (!PPCRecompilerImlGen_XOR(ppcImlGenContext, opcode, false)) unsupportedInstructionFound = true; break; case 339: if (PPCRecompilerImlGen_MFSPR(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; - case 343: - if (PPCRecompilerImlGen_LHAX(ppcImlGenContext, opcode) == false) + case 343: // LHAX + if (!PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 16, true, true, false)) unsupportedInstructionFound = true; break; case 371: if (PPCRecompilerImlGen_MFTB(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; - case 375: - if (PPCRecompilerImlGen_LHAUX(ppcImlGenContext, opcode) == false) + case 375: // LHAUX + if (!PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 16, true, true, true)) unsupportedInstructionFound = true; break; - case 407: - if (PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 16) == false) + case 407: // STHX + if (!PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 16, true, false)) unsupportedInstructionFound = true; break; case 412: if (PPCRecompilerImlGen_ORC(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; - case 439: - if (PPCRecompilerImlGen_STORE_INDEXED_UPDATE(ppcImlGenContext, opcode, 16) == false) + case 439: // STHUX + if (!PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 16, true, true)) unsupportedInstructionFound = true; break; - case 444: - if (PPCRecompilerImlGen_OR(ppcImlGenContext, opcode) == false) + case 444: // OR + if (!PPCRecompilerImlGen_OR_NOR(ppcImlGenContext, opcode, false)) unsupportedInstructionFound = true; break; case 459: @@ -4114,14 +2323,17 @@ bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext) if (PPCRecompilerImlGen_MTSPR(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; + case 476: // NAND + if (!PPCRecompilerImlGen_AND_NAND(ppcImlGenContext, opcode, true)) + unsupportedInstructionFound = true; + break; case 491: if (PPCRecompilerImlGen_DIVW(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; - case 534: - if (PPCRecompilerImlGen_LWBRX(ppcImlGenContext, opcode) == false) + case 534: // LWBRX + if (!PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 32, false, false, false)) unsupportedInstructionFound = true; - ppcImlGenContext->hasFPUInstruction = true; break; case 535: if (PPCRecompilerImlGen_LFSX(ppcImlGenContext, opcode) == false) @@ -4154,8 +2366,8 @@ bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; - case 662: - if (PPCRecompilerImlGen_STWBRX(ppcImlGenContext, opcode) == false) + case 662: // STWBRX + if (!PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 32, false, false)) unsupportedInstructionFound = true; break; case 663: @@ -4174,8 +2386,9 @@ bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext) if (PPCRecompilerImlGen_STFDX(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; - case 790: - PPCRecompilerImlGen_LHBRX(ppcImlGenContext, opcode); + case 790: // LHBRX + if (!PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 16, false, false, false)) + unsupportedInstructionFound = true; break; case 792: if (PPCRecompilerImlGen_SRAW(ppcImlGenContext, opcode) == false) @@ -4186,7 +2399,7 @@ bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext) unsupportedInstructionFound = true; break; case 918: // STHBRX - if (PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 16, true) == false) + if (!PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 16, false, true)) unsupportedInstructionFound = true; break; case 922: @@ -4210,47 +2423,61 @@ bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext) break; } break; - case 32: - PPCRecompilerImlGen_LWZ(ppcImlGenContext, opcode); + case 32: // LWZ + if(!PPCRecompilerImlGen_LOAD(ppcImlGenContext, opcode, 32, false, true, false)) + unsupportedInstructionFound = true; break; - case 33: - PPCRecompilerImlGen_LWZU(ppcImlGenContext, opcode); + case 33: // LWZU + if (!PPCRecompilerImlGen_LOAD(ppcImlGenContext, opcode, 32, false, true, true)) + unsupportedInstructionFound = true; break; - case 34: - PPCRecompilerImlGen_LBZ(ppcImlGenContext, opcode); + case 34: // LBZ + if (!PPCRecompilerImlGen_LOAD(ppcImlGenContext, opcode, 8, false, true, false)) + unsupportedInstructionFound = true; break; - case 35: - PPCRecompilerImlGen_LBZU(ppcImlGenContext, opcode); + case 35: // LBZU + if (!PPCRecompilerImlGen_LOAD(ppcImlGenContext, opcode, 8, false, true, true)) + unsupportedInstructionFound = true; break; - case 36: - PPCRecompilerImlGen_STW(ppcImlGenContext, opcode); + case 36: // STW + if(!PPCRecompilerImlGen_STORE(ppcImlGenContext, opcode, 32, true, false)) + unsupportedInstructionFound = true; break; - case 37: - PPCRecompilerImlGen_STWU(ppcImlGenContext, opcode); + case 37: // STWU + if (!PPCRecompilerImlGen_STORE(ppcImlGenContext, opcode, 32, true, true)) + unsupportedInstructionFound = true; break; - case 38: - PPCRecompilerImlGen_STB(ppcImlGenContext, opcode); + case 38: // STB + if (!PPCRecompilerImlGen_STORE(ppcImlGenContext, opcode, 8, true, false)) + unsupportedInstructionFound = true; break; - case 39: - PPCRecompilerImlGen_STBU(ppcImlGenContext, opcode); + case 39: // STBU + if (!PPCRecompilerImlGen_STORE(ppcImlGenContext, opcode, 8, true, true)) + unsupportedInstructionFound = true; break; - case 40: - PPCRecompilerImlGen_LHZ(ppcImlGenContext, opcode); + case 40: // LHZ + if (!PPCRecompilerImlGen_LOAD(ppcImlGenContext, opcode, 16, false, true, false)) + unsupportedInstructionFound = true; break; - case 41: - PPCRecompilerImlGen_LHZU(ppcImlGenContext, opcode); + case 41: // LHZU + if (!PPCRecompilerImlGen_LOAD(ppcImlGenContext, opcode, 16, false, true, true)) + unsupportedInstructionFound = true; break; - case 42: - PPCRecompilerImlGen_LHA(ppcImlGenContext, opcode); + case 42: // LHA + if (!PPCRecompilerImlGen_LOAD(ppcImlGenContext, opcode, 16, true, true, false)) + unsupportedInstructionFound = true; break; - case 43: - PPCRecompilerImlGen_LHAU(ppcImlGenContext, opcode); + case 43: // LHAU + if (!PPCRecompilerImlGen_LOAD(ppcImlGenContext, opcode, 16, true, true, true)) + unsupportedInstructionFound = true; break; - case 44: - PPCRecompilerImlGen_STH(ppcImlGenContext, opcode); + case 44: // STH + if (!PPCRecompilerImlGen_STORE(ppcImlGenContext, opcode, 16, true, false)) + unsupportedInstructionFound = true; break; - case 45: - PPCRecompilerImlGen_STHU(ppcImlGenContext, opcode); + case 45: // STHU + if (!PPCRecompilerImlGen_STORE(ppcImlGenContext, opcode, 16, true, true)) + unsupportedInstructionFound = true; break; case 46: PPCRecompilerImlGen_LMW(ppcImlGenContext, opcode); @@ -4471,556 +2698,548 @@ bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext) return unsupportedInstructionFound; } -bool PPCRecompiler_generateIntermediateCode(ppcImlGenContext_t& ppcImlGenContext, PPCRecFunction_t* ppcRecFunc, std::set& entryAddresses) +// returns false if code flow is not interrupted +// continueDefaultPath: Controls if +bool PPCRecompiler_CheckIfInstructionEndsSegment(PPCFunctionBoundaryTracker& boundaryTracker, uint32 instructionAddress, uint32 opcode, bool& makeNextInstEnterable, bool& continueDefaultPath, bool& hasBranchTarget, uint32& branchTarget) { - //ppcImlGenContext_t ppcImlGenContext = { 0 }; - ppcImlGenContext.functionRef = ppcRecFunc; - // add entire range + hasBranchTarget = false; + branchTarget = 0xFFFFFFFF; + makeNextInstEnterable = false; + continueDefaultPath = false; + switch (Espresso::GetPrimaryOpcode(opcode)) + { + case Espresso::PrimaryOpcode::VIRTUAL_HLE: + { + makeNextInstEnterable = true; + hasBranchTarget = false; + continueDefaultPath = false; + return true; + } + case Espresso::PrimaryOpcode::BC: + { + uint32 BD, BI; + Espresso::BOField BO; + bool AA, LK; + Espresso::decodeOp_BC(opcode, BD, BO, BI, AA, LK); + if (!LK) + { + hasBranchTarget = true; + branchTarget = (AA ? BD : BD) + instructionAddress; + if (!boundaryTracker.ContainsAddress(branchTarget)) + hasBranchTarget = false; // far jump + } + makeNextInstEnterable = LK; + continueDefaultPath = true; + return true; + } + case Espresso::PrimaryOpcode::B: + { + uint32 LI; + bool AA, LK; + Espresso::decodeOp_B(opcode, LI, AA, LK); + if (!LK) + { + hasBranchTarget = true; + branchTarget = AA ? LI : LI + instructionAddress; + if (!boundaryTracker.ContainsAddress(branchTarget)) + hasBranchTarget = false; // far jump + } + makeNextInstEnterable = LK; + continueDefaultPath = false; + return true; + } + case Espresso::PrimaryOpcode::GROUP_19: + switch (Espresso::GetGroup19Opcode(opcode)) + { + case Espresso::Opcode19::BCLR: + case Espresso::Opcode19::BCCTR: + { + Espresso::BOField BO; + uint32 BI; + bool LK; + Espresso::decodeOp_BCSPR(opcode, BO, BI, LK); + continueDefaultPath = !BO.conditionIgnore() || !BO.decrementerIgnore(); // if branch is always taken then there is no continued path + makeNextInstEnterable = Espresso::DecodeLK(opcode); + return true; + } + default: + break; + } + break; + case Espresso::PrimaryOpcode::GROUP_31: + switch (Espresso::GetGroup31Opcode(opcode)) + { + default: + break; + } + break; + default: + break; + } + return false; +} + +void PPCRecompiler_DetermineBasicBlockRange(std::vector& basicBlockList, PPCFunctionBoundaryTracker& boundaryTracker, uint32 ppcStart, uint32 ppcEnd, const std::set& combinedBranchTargets, const std::set& entryAddresses) +{ + cemu_assert_debug(ppcStart <= ppcEnd); + + uint32 currentAddr = ppcStart; + + PPCBasicBlockInfo* curBlockInfo = &basicBlockList.emplace_back(currentAddr, entryAddresses); + + uint32 basicBlockStart = currentAddr; + while (currentAddr <= ppcEnd) + { + curBlockInfo->lastAddress = currentAddr; + uint32 opcode = memory_readU32(currentAddr); + bool nextInstIsEnterable = false; + bool hasBranchTarget = false; + bool hasContinuedFlow = false; + uint32 branchTarget = 0; + if (PPCRecompiler_CheckIfInstructionEndsSegment(boundaryTracker, currentAddr, opcode, nextInstIsEnterable, hasContinuedFlow, hasBranchTarget, branchTarget)) + { + curBlockInfo->hasBranchTarget = hasBranchTarget; + curBlockInfo->branchTarget = branchTarget; + curBlockInfo->hasContinuedFlow = hasContinuedFlow; + // start new basic block, except if this is the last instruction + if (currentAddr >= ppcEnd) + break; + curBlockInfo = &basicBlockList.emplace_back(currentAddr + 4, entryAddresses); + curBlockInfo->isEnterable = curBlockInfo->isEnterable || nextInstIsEnterable; + currentAddr += 4; + continue; + } + currentAddr += 4; + if (currentAddr <= ppcEnd) + { + if (combinedBranchTargets.find(currentAddr) != combinedBranchTargets.end()) + { + // instruction is branch target, start new basic block + curBlockInfo = &basicBlockList.emplace_back(currentAddr, entryAddresses); + } + } + + } +} + +std::vector PPCRecompiler_DetermineBasicBlockRange(PPCFunctionBoundaryTracker& boundaryTracker, const std::set& entryAddresses) +{ + cemu_assert(!entryAddresses.empty()); + std::vector basicBlockList; + + const std::set branchTargets = boundaryTracker.GetBranchTargets(); + auto funcRanges = boundaryTracker.GetRanges(); + + std::set combinedBranchTargets = branchTargets; + combinedBranchTargets.insert(entryAddresses.begin(), entryAddresses.end()); + + for (auto& funcRangeIt : funcRanges) + PPCRecompiler_DetermineBasicBlockRange(basicBlockList, boundaryTracker, funcRangeIt.startAddress, funcRangeIt.startAddress + funcRangeIt.length - 4, combinedBranchTargets, entryAddresses); + + // mark all segments that start at entryAddresses as enterable (debug code for verification, can be removed) + size_t numMarkedEnterable = 0; + for (auto& basicBlockIt : basicBlockList) + { + if (entryAddresses.find(basicBlockIt.startAddress) != entryAddresses.end()) + { + cemu_assert_debug(basicBlockIt.isEnterable); + numMarkedEnterable++; + } + } + cemu_assert_debug(numMarkedEnterable == entryAddresses.size()); + + // todo - inline BL, currently this is done in the instruction handler of BL but this will mean that instruction cycle increasing is ignored + + return basicBlockList; +} + +bool PPCIMLGen_FillBasicBlock(ppcImlGenContext_t& ppcImlGenContext, PPCBasicBlockInfo& basicBlockInfo) +{ + ppcImlGenContext.currentOutputSegment = basicBlockInfo.GetSegmentForInstructionAppend(); + ppcImlGenContext.currentInstruction = (uint32*)(memory_base + basicBlockInfo.startAddress); + + uint32* firstCurrentInstruction = ppcImlGenContext.currentInstruction; + uint32* endCurrentInstruction = (uint32*)(memory_base + basicBlockInfo.lastAddress); + + while (ppcImlGenContext.currentInstruction <= endCurrentInstruction) + { + uint32 addressOfCurrentInstruction = (uint32)((uint8*)ppcImlGenContext.currentInstruction - memory_base); + ppcImlGenContext.ppcAddressOfCurrentInstruction = addressOfCurrentInstruction; + if (PPCRecompiler_decodePPCInstruction(&ppcImlGenContext)) + { + debug_printf("Recompiler encountered unsupported instruction at 0x%08x\n", addressOfCurrentInstruction); + ppcImlGenContext.currentOutputSegment = nullptr; + return false; + } + } + ppcImlGenContext.currentOutputSegment = nullptr; + return true; +} + +// returns split segment from which the continued segment is available via seg->GetBranchNotTaken() +IMLSegment* PPCIMLGen_CreateSplitSegmentAtEnd(ppcImlGenContext_t& ppcImlGenContext, PPCBasicBlockInfo& basicBlockInfo) +{ + IMLSegment* writeSegment = basicBlockInfo.GetSegmentForInstructionAppend(); + + IMLSegment* continuedSegment = ppcImlGenContext.InsertSegment(ppcImlGenContext.GetSegmentIndex(writeSegment) + 1); + + continuedSegment->SetLinkBranchTaken(writeSegment->GetBranchTaken()); + continuedSegment->SetLinkBranchNotTaken(writeSegment->GetBranchNotTaken()); + + writeSegment->SetLinkBranchNotTaken(continuedSegment); + writeSegment->SetLinkBranchTaken(nullptr); + + if (ppcImlGenContext.currentOutputSegment == writeSegment) + ppcImlGenContext.currentOutputSegment = continuedSegment; + + cemu_assert_debug(basicBlockInfo.appendSegment == writeSegment); + basicBlockInfo.appendSegment = continuedSegment; + + return writeSegment; +} + +// generates a new segment and sets it as branch target for the current write segment. Returns the created segment +IMLSegment* PPCIMLGen_CreateNewSegmentAsBranchTarget(ppcImlGenContext_t& ppcImlGenContext, PPCBasicBlockInfo& basicBlockInfo) +{ + IMLSegment* writeSegment = basicBlockInfo.GetSegmentForInstructionAppend(); + IMLSegment* branchTargetSegment = ppcImlGenContext.NewSegment(); + cemu_assert_debug(!writeSegment->GetBranchTaken()); // must not have a target already + writeSegment->SetLinkBranchTaken(branchTargetSegment); + return branchTargetSegment; +} + +// verify that current instruction is the last instruction of the active basic block +void PPCIMLGen_AssertIfNotLastSegmentInstruction(ppcImlGenContext_t& ppcImlGenContext) +{ + cemu_assert_debug(ppcImlGenContext.currentBasicBlock->lastAddress == ppcImlGenContext.ppcAddressOfCurrentInstruction); +} + +void PPCRecompiler_HandleCycleCheckCount(ppcImlGenContext_t& ppcImlGenContext, PPCBasicBlockInfo& basicBlockInfo) +{ + IMLSegment* imlSegment = basicBlockInfo.GetFirstSegmentInChain(); + if (!basicBlockInfo.hasBranchTarget) + return; + if (basicBlockInfo.branchTarget > basicBlockInfo.startAddress) + return; + + // exclude non-infinite tight loops + if (IMLAnalyzer_IsTightFiniteLoop(imlSegment)) + return; + + // make the segment enterable so execution can return after passing a check + basicBlockInfo.GetFirstSegmentInChain()->SetEnterable(basicBlockInfo.startAddress); + + IMLSegment* splitSeg = PPCIMLGen_CreateSplitSegmentAtEnd(ppcImlGenContext, basicBlockInfo); + splitSeg->AppendInstruction()->make_cjump_cycle_check(); + + IMLSegment* exitSegment = ppcImlGenContext.NewSegment(); + splitSeg->SetLinkBranchTaken(exitSegment); + + exitSegment->AppendInstruction()->make_macro(PPCREC_IML_MACRO_LEAVE, basicBlockInfo.startAddress, 0, 0, IMLREG_INVALID); +} + +void PPCRecompiler_SetSegmentsUncertainFlow(ppcImlGenContext_t& ppcImlGenContext) +{ + for (IMLSegment* segIt : ppcImlGenContext.segmentList2) + { + bool isLastSegment = segIt == ppcImlGenContext.segmentList2.back(); + // handle empty segment + if (segIt->imlList.empty()) + { + cemu_assert_debug(segIt->GetBranchNotTaken()); + continue; + } + // check last instruction of segment + IMLInstruction* imlInstruction = segIt->GetLastInstruction(); + if (imlInstruction->type == PPCREC_IML_TYPE_MACRO) + { + auto macroType = imlInstruction->operation; + switch (macroType) + { + case PPCREC_IML_MACRO_B_TO_REG: + case PPCREC_IML_MACRO_BL: + case PPCREC_IML_MACRO_B_FAR: + case PPCREC_IML_MACRO_HLE: + case PPCREC_IML_MACRO_LEAVE: + segIt->nextSegmentIsUncertain = true; + break; + case PPCREC_IML_MACRO_DEBUGBREAK: + case PPCREC_IML_MACRO_COUNT_CYCLES: + case PPCREC_IML_MACRO_MFTB: + break; + default: + cemu_assert_unimplemented(); + } + } + } +} + +bool PPCRecompiler_GenerateIML(ppcImlGenContext_t& ppcImlGenContext, PPCFunctionBoundaryTracker& boundaryTracker, std::set& entryAddresses) +{ + std::vector basicBlockList = PPCRecompiler_DetermineBasicBlockRange(boundaryTracker, entryAddresses); + + // create segments + std::unordered_map addrToBB; + ppcImlGenContext.segmentList2.resize(basicBlockList.size()); + for (size_t i = 0; i < basicBlockList.size(); i++) + { + PPCBasicBlockInfo& basicBlockInfo = basicBlockList[i]; + IMLSegment* seg = new IMLSegment(); + seg->ppcAddress = basicBlockInfo.startAddress; + if(basicBlockInfo.isEnterable) + seg->SetEnterable(basicBlockInfo.startAddress); + ppcImlGenContext.segmentList2[i] = seg; + cemu_assert_debug(addrToBB.find(basicBlockInfo.startAddress) == addrToBB.end()); + basicBlockInfo.SetInitialSegment(seg); + addrToBB.emplace(basicBlockInfo.startAddress, &basicBlockInfo); + } + // link segments + for (size_t i = 0; i < basicBlockList.size(); i++) + { + PPCBasicBlockInfo& bbInfo = basicBlockList[i]; + cemu_assert_debug(bbInfo.GetFirstSegmentInChain() == bbInfo.GetSegmentForInstructionAppend()); + IMLSegment* seg = ppcImlGenContext.segmentList2[i]; + if (bbInfo.hasBranchTarget) + { + PPCBasicBlockInfo* targetBB = addrToBB[bbInfo.branchTarget]; + cemu_assert_debug(targetBB); + IMLSegment_SetLinkBranchTaken(seg, targetBB->GetFirstSegmentInChain()); + } + if (bbInfo.hasContinuedFlow) + { + PPCBasicBlockInfo* targetBB = addrToBB[bbInfo.lastAddress + 4]; + if (!targetBB) + { + cemuLog_log(LogType::Recompiler, "Recompiler was unable to link segment [0x{:08x}-0x{:08x}] to 0x{:08x}", bbInfo.startAddress, bbInfo.lastAddress, bbInfo.lastAddress + 4); + return false; + } + cemu_assert_debug(targetBB); + IMLSegment_SetLinkBranchNotTaken(seg, targetBB->GetFirstSegmentInChain()); + } + } + // we assume that all unreachable segments are potentially enterable + // todo - mark them as such + + + // generate cycle counters + // in theory we could generate these as part of FillBasicBlock() but in the future we might use more complex logic to emit fewer operations + for (size_t i = 0; i < basicBlockList.size(); i++) + { + PPCBasicBlockInfo& basicBlockInfo = basicBlockList[i]; + IMLSegment* seg = basicBlockInfo.GetSegmentForInstructionAppend(); + + uint32 ppcInstructionCount = (basicBlockInfo.lastAddress - basicBlockInfo.startAddress + 4) / 4; + cemu_assert_debug(ppcInstructionCount > 0); + + PPCRecompiler_pushBackIMLInstructions(seg, 0, 1); + seg->imlList[0].type = PPCREC_IML_TYPE_MACRO; + seg->imlList[0].operation = PPCREC_IML_MACRO_COUNT_CYCLES; + seg->imlList[0].op_macro.param = ppcInstructionCount; + } + + // generate cycle check instructions + // note: Introduces new segments + for (size_t i = 0; i < basicBlockList.size(); i++) + { + PPCBasicBlockInfo& basicBlockInfo = basicBlockList[i]; + PPCRecompiler_HandleCycleCheckCount(ppcImlGenContext, basicBlockInfo); + } + + // fill in all the basic blocks + // note: This step introduces new segments as is necessary for some instructions + for (size_t i = 0; i < basicBlockList.size(); i++) + { + PPCBasicBlockInfo& basicBlockInfo = basicBlockList[i]; + ppcImlGenContext.currentBasicBlock = &basicBlockInfo; + if (!PPCIMLGen_FillBasicBlock(ppcImlGenContext, basicBlockInfo)) + return false; + ppcImlGenContext.currentBasicBlock = nullptr; + } + + // mark segments with unknown jump destination (e.g. BLR and most macros) + PPCRecompiler_SetSegmentsUncertainFlow(ppcImlGenContext); + + // debug - check segment graph +#ifdef CEMU_DEBUG_ASSERT + //for (size_t i = 0; i < basicBlockList.size(); i++) + //{ + // IMLSegment* seg = ppcImlGenContext.segmentList2[i]; + // if (seg->list_prevSegments.empty()) + // { + // cemu_assert_debug(seg->isEnterable); + // } + //} + // debug - check if suffix instructions are at the end of segments and if they are present for branching segments + for (size_t segIndex = 0; segIndex < ppcImlGenContext.segmentList2.size(); segIndex++) + { + IMLSegment* seg = ppcImlGenContext.segmentList2[segIndex]; + IMLSegment* nextSeg = (segIndex+1) < ppcImlGenContext.segmentList2.size() ? ppcImlGenContext.segmentList2[segIndex + 1] : nullptr; + + if (seg->imlList.size() > 0) + { + for (size_t f = 0; f < seg->imlList.size() - 1; f++) + { + if (seg->imlList[f].IsSuffixInstruction()) + { + debug_printf("---------------- SegmentDump (Suffix instruction at wrong pos in segment 0x%x):\n", (int)segIndex); + IMLDebug_Dump(&ppcImlGenContext); + DEBUG_BREAK; + } + } + } + if (seg->nextSegmentBranchTaken) + { + if (!seg->HasSuffixInstruction()) + { + debug_printf("---------------- SegmentDump (NoSuffixInstruction in segment 0x%x):\n", (int)segIndex); + IMLDebug_Dump(&ppcImlGenContext); + DEBUG_BREAK; + } + } + if (seg->nextSegmentBranchNotTaken) + { + // if branch not taken, flow must continue to next segment in sequence + cemu_assert_debug(seg->nextSegmentBranchNotTaken == nextSeg); + } + // more detailed checks based on actual suffix instruction + if (seg->imlList.size() > 0) + { + IMLInstruction* inst = seg->GetLastInstruction(); + if (inst->type == PPCREC_IML_TYPE_MACRO && inst->op_macro.param == PPCREC_IML_MACRO_B_FAR) + { + cemu_assert_debug(!seg->GetBranchTaken()); + cemu_assert_debug(!seg->GetBranchNotTaken()); + } + if (inst->type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK) + { + cemu_assert_debug(seg->GetBranchTaken()); + cemu_assert_debug(seg->GetBranchNotTaken()); + } + if (inst->type == PPCREC_IML_TYPE_CONDITIONAL_JUMP) + { + if (!seg->GetBranchTaken() || !seg->GetBranchNotTaken()) + { + debug_printf("---------------- SegmentDump (Missing branch for conditional jump in segment 0x%x):\n", (int)segIndex); + IMLDebug_Dump(&ppcImlGenContext); + cemu_assert_error(); + } + } + } + segIndex++; + } +#endif + + + // todos: + // - basic block determination should look for the B(L) B(L) pattern. Or maybe just mark every bb without any input segments as an entry segment + + return true; +} + +void IMLOptimizer_replaceWithConditionalMov(ppcImlGenContext_t& ppcImlGenContext) +{ + // optimization pass - replace segments with conditional MOVs if possible + //for (IMLSegment* segIt : ppcImlGenContext.segmentList2) + //{ + // if (segIt->nextSegmentBranchNotTaken == nullptr || segIt->nextSegmentBranchTaken == nullptr) + // continue; // not a branching segment + // IMLInstruction* lastInstruction = segIt->GetLastInstruction(); + // if (lastInstruction->type != PPCREC_IML_TYPE_CJUMP || lastInstruction->op_conditionalJump.crRegisterIndex != 0) + // continue; + // IMLSegment* conditionalSegment = segIt->nextSegmentBranchNotTaken; + // IMLSegment* finalSegment = segIt->nextSegmentBranchTaken; + // if (segIt->nextSegmentBranchTaken != segIt->nextSegmentBranchNotTaken->nextSegmentBranchNotTaken) + // continue; + // if (segIt->nextSegmentBranchNotTaken->imlList.size() > 4) + // continue; + // if (conditionalSegment->list_prevSegments.size() != 1) + // continue; // the reduced segment must not be the target of any other branch + // if (conditionalSegment->isEnterable) + // continue; + // // check if the segment contains only iml instructions that can be turned into conditional moves (Value assignment, register assignment) + // bool canReduceSegment = true; + // for (sint32 f = 0; f < conditionalSegment->imlList.size(); f++) + // { + // IMLInstruction* imlInstruction = conditionalSegment->imlList.data() + f; + // if (imlInstruction->type == PPCREC_IML_TYPE_R_S32 && imlInstruction->operation == PPCREC_IML_OP_ASSIGN) + // continue; + // // todo: Register to register copy + // canReduceSegment = false; + // break; + // } + + // if (canReduceSegment == false) + // continue; + + // // remove the branch instruction + // uint8 branchCond_crRegisterIndex = lastInstruction->op_conditionalJump.crRegisterIndex; + // uint8 branchCond_crBitIndex = lastInstruction->op_conditionalJump.crBitIndex; + // bool branchCond_bitMustBeSet = lastInstruction->op_conditionalJump.bitMustBeSet; + // lastInstruction->make_no_op(); + + // // append conditional moves based on branch condition + // for (sint32 f = 0; f < conditionalSegment->imlList.size(); f++) + // { + // IMLInstruction* imlInstruction = conditionalSegment->imlList.data() + f; + // if (imlInstruction->type == PPCREC_IML_TYPE_R_S32 && imlInstruction->operation == PPCREC_IML_OP_ASSIGN) + // PPCRecompilerImlGen_generateNewInstruction_conditional_r_s32(&ppcImlGenContext, PPCRecompiler_appendInstruction(segIt), PPCREC_IML_OP_ASSIGN, imlInstruction->op_r_immS32.registerIndex, imlInstruction->op_r_immS32.immS32, branchCond_crRegisterIndex, branchCond_crBitIndex, !branchCond_bitMustBeSet); + // else + // assert_dbg(); + // } + // // update segment links + // // source segment: imlSegment, conditional/removed segment: conditionalSegment, final segment: finalSegment + // IMLSegment_RemoveLink(segIt, conditionalSegment); + // IMLSegment_RemoveLink(segIt, finalSegment); + // IMLSegment_RemoveLink(conditionalSegment, finalSegment); + // IMLSegment_SetLinkBranchNotTaken(segIt, finalSegment); + // // remove all instructions from conditional segment + // conditionalSegment->imlList.clear(); + + // // if possible, merge imlSegment with finalSegment + // if (finalSegment->isEnterable == false && finalSegment->list_prevSegments.size() == 1) + // { + // // todo: Clean this up and move into separate function PPCRecompilerIML_mergeSegments() + // IMLSegment_RemoveLink(segIt, finalSegment); + // if (finalSegment->nextSegmentBranchNotTaken) + // { + // IMLSegment* tempSegment = finalSegment->nextSegmentBranchNotTaken; + // IMLSegment_RemoveLink(finalSegment, tempSegment); + // IMLSegment_SetLinkBranchNotTaken(segIt, tempSegment); + // } + // if (finalSegment->nextSegmentBranchTaken) + // { + // IMLSegment* tempSegment = finalSegment->nextSegmentBranchTaken; + // IMLSegment_RemoveLink(finalSegment, tempSegment); + // IMLSegment_SetLinkBranchTaken(segIt, tempSegment); + // } + // // copy IML instructions + // cemu_assert_debug(segIt != finalSegment); + // for (sint32 f = 0; f < finalSegment->imlList.size(); f++) + // { + // memcpy(PPCRecompiler_appendInstruction(segIt), finalSegment->imlList.data() + f, sizeof(IMLInstruction)); + // } + // finalSegment->imlList.clear(); + // } + + // // todo: If possible, merge with the segment following conditionalSegment (merging is only possible if the segment is not an entry point or has no other jump sources) + //} +} + +bool PPCRecompiler_generateIntermediateCode(ppcImlGenContext_t& ppcImlGenContext, PPCRecFunction_t* ppcRecFunc, std::set& entryAddresses, PPCFunctionBoundaryTracker& boundaryTracker) +{ + ppcImlGenContext.boundaryTracker = &boundaryTracker; + if (!PPCRecompiler_GenerateIML(ppcImlGenContext, boundaryTracker, entryAddresses)) + return false; + + // IMLOptimizer_replaceWithConditionalMov(ppcImlGenContext); + + // set range + // todo - support non-continuous functions for the range tracking? ppcRecRange_t recRange; recRange.ppcAddress = ppcRecFunc->ppcAddress; recRange.ppcSize = ppcRecFunc->ppcSize; ppcRecFunc->list_ranges.push_back(recRange); - // process ppc instructions - ppcImlGenContext.currentInstruction = (uint32*)memory_getPointerFromVirtualOffset(ppcRecFunc->ppcAddress); - bool unsupportedInstructionFound = false; - sint32 numPPCInstructions = ppcRecFunc->ppcSize/4; - sint32 unsupportedInstructionCount = 0; - uint32 unsupportedInstructionLastOffset = 0; - uint32* firstCurrentInstruction = ppcImlGenContext.currentInstruction; - uint32* endCurrentInstruction = ppcImlGenContext.currentInstruction + numPPCInstructions; + - while(ppcImlGenContext.currentInstruction < endCurrentInstruction) - { - uint32 addressOfCurrentInstruction = (uint32)((uint8*)ppcImlGenContext.currentInstruction - memory_base); - ppcImlGenContext.ppcAddressOfCurrentInstruction = addressOfCurrentInstruction; - ppcImlGenContext.cyclesSinceLastBranch++; - PPCRecompilerImlGen_generateNewInstruction_jumpmark(&ppcImlGenContext, addressOfCurrentInstruction); - - if (entryAddresses.find(addressOfCurrentInstruction) != entryAddresses.end()) - { - // add PPCEnter for addresses that are in entryAddresses - PPCRecompilerImlGen_generateNewInstruction_ppcEnter(&ppcImlGenContext, addressOfCurrentInstruction); - } - else if(ppcImlGenContext.currentInstruction != firstCurrentInstruction) - { - // add PPCEnter mark if code is seemingly unreachable (for example if between two unconditional jump instructions without jump goal) - uint32 opcodeCurrent = PPCRecompiler_getCurrentInstruction(&ppcImlGenContext); - uint32 opcodePrevious = PPCRecompiler_getPreviousInstruction(&ppcImlGenContext); - if( ((opcodePrevious>>26) == 18) && ((opcodeCurrent>>26) == 18) ) - { - // between two B(L) instructions - // todo: for BL only if they are not inlineable - - bool canInlineFunction = false; - if ((opcodePrevious & PPC_OPC_LK) && (opcodePrevious & PPC_OPC_AA) == 0) - { - uint32 li; - PPC_OPC_TEMPL_I(opcodePrevious, li); - sint32 inlineSize = 0; - if (PPCRecompiler_canInlineFunction(li + addressOfCurrentInstruction - 4, &inlineSize)) - canInlineFunction = true; - } - if( canInlineFunction == false && (opcodePrevious & PPC_OPC_LK) == false) - PPCRecompilerImlGen_generateNewInstruction_ppcEnter(&ppcImlGenContext, addressOfCurrentInstruction); - } - if( ((opcodePrevious>>26) == 19) && PPC_getBits(opcodePrevious, 30, 10) == 528 ) - { - uint32 BO, BI, BD; - PPC_OPC_TEMPL_XL(opcodePrevious, BO, BI, BD); - if( (BO & 16) && (opcodePrevious&PPC_OPC_LK) == 0 ) - { - // after unconditional BCTR instruction - PPCRecompilerImlGen_generateNewInstruction_ppcEnter(&ppcImlGenContext, addressOfCurrentInstruction); - } - } - } - - unsupportedInstructionFound = PPCRecompiler_decodePPCInstruction(&ppcImlGenContext); - if( unsupportedInstructionFound ) - { - unsupportedInstructionCount++; - unsupportedInstructionLastOffset = ppcImlGenContext.ppcAddressOfCurrentInstruction; - unsupportedInstructionFound = false; - //break; - } - } - ppcImlGenContext.ppcAddressOfCurrentInstruction = 0; // reset current instruction offset (any future generated IML instruction will be assigned to ppc address 0) - if( unsupportedInstructionCount > 0 || unsupportedInstructionFound ) - { - // could not compile function - debug_printf("Failed recompile due to unknown instruction at 0x%08x\n", unsupportedInstructionLastOffset); - PPCRecompiler_freeContext(&ppcImlGenContext); - return false; - } - // optimize unused jumpmarks away - // first, flag all jumpmarks as unused - std::map map_jumpMarks; - for(sint32 i=0; isecond->op_jumpmark.flags &= ~PPCREC_IML_OP_FLAG_UNUSED; - } - } - // lastly, remove jumpmarks that still have the unused flag set - sint32 currentImlIndex = 0; - for(sint32 i=0; i end of segment after current instruction - // If we encounter a jumpmark -> end of segment before current instruction - // If we encounter ppc_enter -> end of segment before current instruction - if( ppcImlGenContext.imlList[segmentImlIndex].type == PPCREC_IML_TYPE_CJUMP || - (ppcImlGenContext.imlList[segmentImlIndex].type == PPCREC_IML_TYPE_MACRO && (ppcImlGenContext.imlList[segmentImlIndex].operation == PPCREC_IML_MACRO_BLR || ppcImlGenContext.imlList[segmentImlIndex].operation == PPCREC_IML_MACRO_BLRL || ppcImlGenContext.imlList[segmentImlIndex].operation == PPCREC_IML_MACRO_BCTR || ppcImlGenContext.imlList[segmentImlIndex].operation == PPCREC_IML_MACRO_BCTRL)) || - (ppcImlGenContext.imlList[segmentImlIndex].type == PPCREC_IML_TYPE_MACRO && (ppcImlGenContext.imlList[segmentImlIndex].operation == PPCREC_IML_MACRO_BL)) || - (ppcImlGenContext.imlList[segmentImlIndex].type == PPCREC_IML_TYPE_MACRO && (ppcImlGenContext.imlList[segmentImlIndex].operation == PPCREC_IML_MACRO_B_FAR)) || - (ppcImlGenContext.imlList[segmentImlIndex].type == PPCREC_IML_TYPE_MACRO && (ppcImlGenContext.imlList[segmentImlIndex].operation == PPCREC_IML_MACRO_LEAVE)) || - (ppcImlGenContext.imlList[segmentImlIndex].type == PPCREC_IML_TYPE_MACRO && (ppcImlGenContext.imlList[segmentImlIndex].operation == PPCREC_IML_MACRO_HLE)) || - (ppcImlGenContext.imlList[segmentImlIndex].type == PPCREC_IML_TYPE_MACRO && (ppcImlGenContext.imlList[segmentImlIndex].operation == PPCREC_IML_MACRO_MFTB)) ) - { - // segment ends after current instruction - PPCRecImlSegment_t* ppcRecSegment = PPCRecompiler_generateImlSegment(&ppcImlGenContext); - ppcRecSegment->startOffset = segmentStart; - ppcRecSegment->count = segmentImlIndex-segmentStart+1; - ppcRecSegment->ppcAddress = 0xFFFFFFFF; - segmentStart = segmentImlIndex+1; - } - else if( ppcImlGenContext.imlList[segmentImlIndex].type == PPCREC_IML_TYPE_JUMPMARK || - ppcImlGenContext.imlList[segmentImlIndex].type == PPCREC_IML_TYPE_PPC_ENTER ) - { - // segment ends before current instruction - if( segmentImlIndex > segmentStart ) - { - PPCRecImlSegment_t* ppcRecSegment = PPCRecompiler_generateImlSegment(&ppcImlGenContext); - ppcRecSegment->startOffset = segmentStart; - ppcRecSegment->count = segmentImlIndex-segmentStart; - ppcRecSegment->ppcAddress = 0xFFFFFFFF; - segmentStart = segmentImlIndex; - } - } - segmentImlIndex++; - } - if( segmentImlIndex != segmentStart ) - { - // final segment - PPCRecImlSegment_t* ppcRecSegment = PPCRecompiler_generateImlSegment(&ppcImlGenContext); - ppcRecSegment->startOffset = segmentStart; - ppcRecSegment->count = segmentImlIndex-segmentStart; - ppcRecSegment->ppcAddress = 0xFFFFFFFF; - segmentStart = segmentImlIndex; - } - // move iml instructions into the segments - for(sint32 s=0; sstartOffset; - uint32 imlCount = ppcImlGenContext.segmentList[s]->count; - if( imlCount > 0 ) - { - ppcImlGenContext.segmentList[s]->imlListSize = imlCount + 4; - ppcImlGenContext.segmentList[s]->imlList = (PPCRecImlInstruction_t*)malloc(sizeof(PPCRecImlInstruction_t)*ppcImlGenContext.segmentList[s]->imlListSize); - ppcImlGenContext.segmentList[s]->imlListCount = imlCount; - memcpy(ppcImlGenContext.segmentList[s]->imlList, ppcImlGenContext.imlList+imlStartIndex, sizeof(PPCRecImlInstruction_t)*imlCount); - } - else - { - // empty segments are allowed so we can handle multiple PPC entry addresses pointing to the same code - ppcImlGenContext.segmentList[s]->imlList = NULL; - ppcImlGenContext.segmentList[s]->imlListSize = 0; - ppcImlGenContext.segmentList[s]->imlListCount = 0; - } - ppcImlGenContext.segmentList[s]->startOffset = 9999999; - ppcImlGenContext.segmentList[s]->count = 9999999; - } - // clear segment-independent iml list - free(ppcImlGenContext.imlList); - ppcImlGenContext.imlList = NULL; - ppcImlGenContext.imlListCount = 999999; // set to high number to force crash in case old code still uses ppcImlGenContext.imlList - // calculate PPC address of each segment based on iml instructions inside that segment (we need this info to calculate how many cpu cycles each segment takes) - for(sint32 s=0; simlListCount; i++) - { - if( ppcImlGenContext.segmentList[s]->imlList[i].associatedPPCAddress == 0 ) - continue; - //if( ppcImlGenContext.segmentList[s]->imlList[i].type == PPCREC_IML_TYPE_JUMPMARK || ppcImlGenContext.segmentList[s]->imlList[i].type == PPCREC_IML_TYPE_NO_OP ) - // continue; // jumpmarks and no-op instructions must not affect segment ppc address range - segmentPPCAddrMin = std::min(ppcImlGenContext.segmentList[s]->imlList[i].associatedPPCAddress, segmentPPCAddrMin); - segmentPPCAddrMax = std::max(ppcImlGenContext.segmentList[s]->imlList[i].associatedPPCAddress, segmentPPCAddrMax); - } - if( segmentPPCAddrMin != 0xFFFFFFFF ) - { - ppcImlGenContext.segmentList[s]->ppcAddrMin = segmentPPCAddrMin; - ppcImlGenContext.segmentList[s]->ppcAddrMax = segmentPPCAddrMax; - } - else - { - ppcImlGenContext.segmentList[s]->ppcAddrMin = 0; - ppcImlGenContext.segmentList[s]->ppcAddrMax = 0; - } - } - // certain instructions can change the segment state - // ppcEnter instruction marks a segment as enterable (BL, BCTR, etc. instructions can enter at this location from outside) - // jumpmarks mark the segment as a jump destination (within the same function) - for(sint32 s=0; simlListCount > 0 ) - { - if( ppcImlGenContext.segmentList[s]->imlList[0].type == PPCREC_IML_TYPE_PPC_ENTER ) - { - // mark segment as enterable - if( ppcImlGenContext.segmentList[s]->isEnterable ) - assert_dbg(); // should not happen? - ppcImlGenContext.segmentList[s]->isEnterable = true; - ppcImlGenContext.segmentList[s]->enterPPCAddress = ppcImlGenContext.segmentList[s]->imlList[0].op_ppcEnter.ppcAddress; - // remove ppc_enter instruction - ppcImlGenContext.segmentList[s]->imlList[0].type = PPCREC_IML_TYPE_NO_OP; - ppcImlGenContext.segmentList[s]->imlList[0].crRegister = PPC_REC_INVALID_REGISTER; - ppcImlGenContext.segmentList[s]->imlList[0].associatedPPCAddress = 0; - } - else if( ppcImlGenContext.segmentList[s]->imlList[0].type == PPCREC_IML_TYPE_JUMPMARK ) - { - // mark segment as jump destination - if( ppcImlGenContext.segmentList[s]->isJumpDestination ) - assert_dbg(); // should not happen? - ppcImlGenContext.segmentList[s]->isJumpDestination = true; - ppcImlGenContext.segmentList[s]->jumpDestinationPPCAddress = ppcImlGenContext.segmentList[s]->imlList[0].op_jumpmark.address; - // remove jumpmark instruction - ppcImlGenContext.segmentList[s]->imlList[0].type = PPCREC_IML_TYPE_NO_OP; - ppcImlGenContext.segmentList[s]->imlList[0].crRegister = PPC_REC_INVALID_REGISTER; - ppcImlGenContext.segmentList[s]->imlList[0].associatedPPCAddress = 0; - } - else - break; - } - } - // the first segment is always enterable as the recompiled functions entrypoint - ppcImlGenContext.segmentList[0]->isEnterable = true; - ppcImlGenContext.segmentList[0]->enterPPCAddress = ppcImlGenContext.functionRef->ppcAddress; - - // link segments for further inter-segment optimization - PPCRecompilerIML_linkSegments(&ppcImlGenContext); - - // optimization pass - replace segments with conditional MOVs if possible - for (sint32 s = 0; s < ppcImlGenContext.segmentListCount; s++) - { - PPCRecImlSegment_t* imlSegment = ppcImlGenContext.segmentList[s]; - if (imlSegment->nextSegmentBranchNotTaken == NULL || imlSegment->nextSegmentBranchTaken == NULL) - continue; // not a branching segment - PPCRecImlInstruction_t* lastInstruction = PPCRecompilerIML_getLastInstruction(imlSegment); - if (lastInstruction->type != PPCREC_IML_TYPE_CJUMP || lastInstruction->op_conditionalJump.crRegisterIndex != 0) - continue; - PPCRecImlSegment_t* conditionalSegment = imlSegment->nextSegmentBranchNotTaken; - PPCRecImlSegment_t* finalSegment = imlSegment->nextSegmentBranchTaken; - if(imlSegment->nextSegmentBranchTaken != imlSegment->nextSegmentBranchNotTaken->nextSegmentBranchNotTaken) - continue; - if (imlSegment->nextSegmentBranchNotTaken->imlListCount > 4) - continue; - if(conditionalSegment->list_prevSegments.size() != 1) - continue; // the reduced segment must not be the target of any other branch - if(conditionalSegment->isEnterable) - continue; - // check if the segment contains only iml instructions that can be turned into conditional moves (Value assignment, register assignment) - bool canReduceSegment = true; - for (sint32 f = 0; f < conditionalSegment->imlListCount; f++) - { - PPCRecImlInstruction_t* imlInstruction = conditionalSegment->imlList+f; - if( imlInstruction->type == PPCREC_IML_TYPE_R_S32 && imlInstruction->operation == PPCREC_IML_OP_ASSIGN) - continue; - // todo: Register to register copy - canReduceSegment = false; - break; - } - - if( canReduceSegment == false ) - continue; - - // remove the branch instruction - uint8 branchCond_crRegisterIndex = lastInstruction->op_conditionalJump.crRegisterIndex; - uint8 branchCond_crBitIndex = lastInstruction->op_conditionalJump.crBitIndex; - bool branchCond_bitMustBeSet = lastInstruction->op_conditionalJump.bitMustBeSet; - - PPCRecompilerImlGen_generateNewInstruction_noOp(&ppcImlGenContext, lastInstruction); - - // append conditional moves based on branch condition - for (sint32 f = 0; f < conditionalSegment->imlListCount; f++) - { - PPCRecImlInstruction_t* imlInstruction = conditionalSegment->imlList + f; - if (imlInstruction->type == PPCREC_IML_TYPE_R_S32 && imlInstruction->operation == PPCREC_IML_OP_ASSIGN) - PPCRecompilerImlGen_generateNewInstruction_conditional_r_s32(&ppcImlGenContext, PPCRecompiler_appendInstruction(imlSegment), PPCREC_IML_OP_ASSIGN, imlInstruction->op_r_immS32.registerIndex, imlInstruction->op_r_immS32.immS32, branchCond_crRegisterIndex, branchCond_crBitIndex, !branchCond_bitMustBeSet); - else - assert_dbg(); - } - // update segment links - // source segment: imlSegment, conditional/removed segment: conditionalSegment, final segment: finalSegment - PPCRecompilerIML_removeLink(imlSegment, conditionalSegment); - PPCRecompilerIML_removeLink(imlSegment, finalSegment); - PPCRecompilerIML_removeLink(conditionalSegment, finalSegment); - PPCRecompilerIml_setLinkBranchNotTaken(imlSegment, finalSegment); - // remove all instructions from conditional segment - conditionalSegment->imlListCount = 0; - - // if possible, merge imlSegment with finalSegment - if (finalSegment->isEnterable == false && finalSegment->list_prevSegments.size() == 1) - { - // todo: Clean this up and move into separate function PPCRecompilerIML_mergeSegments() - PPCRecompilerIML_removeLink(imlSegment, finalSegment); - if (finalSegment->nextSegmentBranchNotTaken) - { - PPCRecImlSegment_t* tempSegment = finalSegment->nextSegmentBranchNotTaken; - PPCRecompilerIML_removeLink(finalSegment, tempSegment); - PPCRecompilerIml_setLinkBranchNotTaken(imlSegment, tempSegment); - } - if (finalSegment->nextSegmentBranchTaken) - { - PPCRecImlSegment_t* tempSegment = finalSegment->nextSegmentBranchTaken; - PPCRecompilerIML_removeLink(finalSegment, tempSegment); - PPCRecompilerIml_setLinkBranchTaken(imlSegment, tempSegment); - } - // copy IML instructions - for (sint32 f = 0; f < finalSegment->imlListCount; f++) - { - memcpy(PPCRecompiler_appendInstruction(imlSegment), finalSegment->imlList + f, sizeof(PPCRecImlInstruction_t)); - } - finalSegment->imlListCount = 0; - - //PPCRecompiler_dumpIML(ppcRecFunc, &ppcImlGenContext); - } - - // todo: If possible, merge with the segment following conditionalSegment (merging is only possible if the segment is not an entry point or has no other jump sources) - } - - // insert cycle counter instruction in every segment that has a cycle count greater zero - for(sint32 s=0; sppcAddrMin == 0 ) - continue; - // count number of PPC instructions in segment - // note: This algorithm correctly counts inlined functions but it doesn't count NO-OP instructions like ISYNC - uint32 lastPPCInstAddr = 0; - uint32 ppcCount2 = 0; - for (sint32 i = 0; i < imlSegment->imlListCount; i++) - { - if (imlSegment->imlList[i].associatedPPCAddress == 0) - continue; - if (imlSegment->imlList[i].associatedPPCAddress == lastPPCInstAddr) - continue; - lastPPCInstAddr = imlSegment->imlList[i].associatedPPCAddress; - ppcCount2++; - } - //uint32 ppcCount = imlSegment->ppcAddrMax-imlSegment->ppcAddrMin+4; -> No longer works with inlined functions - uint32 cycleCount = ppcCount2;// ppcCount / 4; - if( cycleCount > 0 ) - { - PPCRecompiler_pushBackIMLInstructions(imlSegment, 0, 1); - imlSegment->imlList[0].type = PPCREC_IML_TYPE_MACRO; - imlSegment->imlList[0].crRegister = PPC_REC_INVALID_REGISTER; - imlSegment->imlList[0].operation = PPCREC_IML_MACRO_COUNT_CYCLES; - imlSegment->imlList[0].op_macro.param = cycleCount; - } - } - - // find segments that have a (conditional) jump instruction that points in reverse direction of code flow - // for these segments there is a risk that the recompiler could get trapped in an infinite busy loop. - // todo: We should do a loop-detection prepass where we flag segments that are actually in a loop. We can then use this information below to avoid generating the scheduler-exit code for segments that aren't actually in a loop despite them referencing an earlier segment (which could be an exit segment for example) - uint32 currentLoopEscapeJumpMarker = 0xFF000000; // start in an area where no valid code can be located - for(sint32 s=0; sppcAddrMin which isn't really reliable. (We already had a problem where function inlining would generate falsified segment ranges by omitting the branch instruction). Find a better solution (use jumpmark/enterable offsets?) - PPCRecImlSegment_t* imlSegment = ppcImlGenContext.segmentList[s]; - if( imlSegment->imlListCount == 0 ) - continue; - if (imlSegment->imlList[imlSegment->imlListCount - 1].type != PPCREC_IML_TYPE_CJUMP || imlSegment->imlList[imlSegment->imlListCount - 1].op_conditionalJump.jumpmarkAddress > imlSegment->ppcAddrMin) - continue; - if (imlSegment->imlList[imlSegment->imlListCount - 1].type != PPCREC_IML_TYPE_CJUMP || imlSegment->imlList[imlSegment->imlListCount - 1].op_conditionalJump.jumpAccordingToSegment) - continue; - // exclude non-infinite tight loops - if (PPCRecompilerImlAnalyzer_isTightFiniteLoop(imlSegment)) - continue; - // potential loop segment found, split this segment into four: - // P0: This segment checks if the remaining cycles counter is still above zero. If yes, it jumps to segment P2 (it's also the jump destination for other segments) - // P1: This segment consists only of a single ppc_leave instruction and is usually skipped. Register unload instructions are later inserted here. - // P2: This segment contains the iml instructions of the original segment - // PEntry: This segment is used to enter the function, it jumps to P0 - // All segments are considered to be part of the same PPC instruction range - // The first segment also retains the jump destination and enterable properties from the original segment. - //debug_printf("--- Insert cycle counter check ---\n"); - //PPCRecompiler_dumpIML(ppcRecFunc, &ppcImlGenContext); - - PPCRecompilerIml_insertSegments(&ppcImlGenContext, s, 2); - imlSegment = NULL; - PPCRecImlSegment_t* imlSegmentP0 = ppcImlGenContext.segmentList[s+0]; - PPCRecImlSegment_t* imlSegmentP1 = ppcImlGenContext.segmentList[s+1]; - PPCRecImlSegment_t* imlSegmentP2 = ppcImlGenContext.segmentList[s+2]; - // create entry point segment - PPCRecompilerIml_insertSegments(&ppcImlGenContext, ppcImlGenContext.segmentListCount, 1); - PPCRecImlSegment_t* imlSegmentPEntry = ppcImlGenContext.segmentList[ppcImlGenContext.segmentListCount-1]; - // relink segments - PPCRecompilerIML_relinkInputSegment(imlSegmentP2, imlSegmentP0); - PPCRecompilerIml_setLinkBranchNotTaken(imlSegmentP0, imlSegmentP1); - PPCRecompilerIml_setLinkBranchTaken(imlSegmentP0, imlSegmentP2); - PPCRecompilerIml_setLinkBranchTaken(imlSegmentPEntry, imlSegmentP0); - // update segments - uint32 enterPPCAddress = imlSegmentP2->ppcAddrMin; - if (imlSegmentP2->isEnterable) - enterPPCAddress = imlSegmentP2->enterPPCAddress; - imlSegmentP0->ppcAddress = 0xFFFFFFFF; - imlSegmentP1->ppcAddress = 0xFFFFFFFF; - imlSegmentP2->ppcAddress = 0xFFFFFFFF; - cemu_assert_debug(imlSegmentP2->ppcAddrMin != 0); - // move segment properties from segment P2 to segment P0 - imlSegmentP0->isJumpDestination = imlSegmentP2->isJumpDestination; - imlSegmentP0->jumpDestinationPPCAddress = imlSegmentP2->jumpDestinationPPCAddress; - imlSegmentP0->isEnterable = false; - //imlSegmentP0->enterPPCAddress = imlSegmentP2->enterPPCAddress; - imlSegmentP0->ppcAddrMin = imlSegmentP2->ppcAddrMin; - imlSegmentP0->ppcAddrMax = imlSegmentP2->ppcAddrMax; - imlSegmentP2->isJumpDestination = false; - imlSegmentP2->jumpDestinationPPCAddress = 0; - imlSegmentP2->isEnterable = false; - imlSegmentP2->enterPPCAddress = 0; - imlSegmentP2->ppcAddrMin = 0; - imlSegmentP2->ppcAddrMax = 0; - // setup enterable segment - if( enterPPCAddress != 0 && enterPPCAddress != 0xFFFFFFFF ) - { - imlSegmentPEntry->isEnterable = true; - imlSegmentPEntry->ppcAddress = enterPPCAddress; - imlSegmentPEntry->enterPPCAddress = enterPPCAddress; - } - // assign new jumpmark to segment P2 - imlSegmentP2->isJumpDestination = true; - imlSegmentP2->jumpDestinationPPCAddress = currentLoopEscapeJumpMarker; - currentLoopEscapeJumpMarker++; - // create ppc_leave instruction in segment P1 - PPCRecompiler_pushBackIMLInstructions(imlSegmentP1, 0, 1); - imlSegmentP1->imlList[0].type = PPCREC_IML_TYPE_MACRO; - imlSegmentP1->imlList[0].operation = PPCREC_IML_MACRO_LEAVE; - imlSegmentP1->imlList[0].crRegister = PPC_REC_INVALID_REGISTER; - imlSegmentP1->imlList[0].op_macro.param = imlSegmentP0->ppcAddrMin; - imlSegmentP1->imlList[0].associatedPPCAddress = imlSegmentP0->ppcAddrMin; - // create cycle-based conditional instruction in segment P0 - PPCRecompiler_pushBackIMLInstructions(imlSegmentP0, 0, 1); - imlSegmentP0->imlList[0].type = PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK; - imlSegmentP0->imlList[0].operation = 0; - imlSegmentP0->imlList[0].crRegister = PPC_REC_INVALID_REGISTER; - imlSegmentP0->imlList[0].op_conditionalJump.jumpmarkAddress = imlSegmentP2->jumpDestinationPPCAddress; - imlSegmentP0->imlList[0].associatedPPCAddress = imlSegmentP0->ppcAddrMin; - // jump instruction for PEntry - PPCRecompiler_pushBackIMLInstructions(imlSegmentPEntry, 0, 1); - PPCRecompilerImlGen_generateNewInstruction_jumpSegment(&ppcImlGenContext, imlSegmentPEntry->imlList + 0); - - // skip the newly created segments - s += 2; - } - - // isolate entry points from function flow (enterable segments must not be the target of any other segment) - // this simplifies logic during register allocation - PPCRecompilerIML_isolateEnterableSegments(&ppcImlGenContext); - - // if GQRs can be predicted, optimize PSQ load/stores - PPCRecompiler_optimizePSQLoadAndStore(&ppcImlGenContext); - - // count number of used registers - uint32 numLoadedFPRRegisters = 0; - for(uint32 i=0; i<255; i++) - { - if( ppcImlGenContext.mappedFPRRegister[i] ) - numLoadedFPRRegisters++; - } - - // insert name store instructions at the end of each segment but before branch instructions - for(sint32 s=0; simlListCount == 0 ) - continue; // ignore empty segments - // analyze segment for register usage - PPCImlOptimizerUsedRegisters_t registersUsed; - for(sint32 i=0; iimlListCount; i++) - { - PPCRecompiler_checkRegisterUsage(&ppcImlGenContext, imlSegment->imlList+i, ®istersUsed); - //PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, registersUsed.readGPR1); - sint32 accessedTempReg[5]; - // intermediate FPRs - accessedTempReg[0] = registersUsed.readFPR1; - accessedTempReg[1] = registersUsed.readFPR2; - accessedTempReg[2] = registersUsed.readFPR3; - accessedTempReg[3] = registersUsed.readFPR4; - accessedTempReg[4] = registersUsed.writtenFPR1; - for(sint32 f=0; f<5; f++) - { - if( accessedTempReg[f] == -1 ) - continue; - uint32 regName = ppcImlGenContext.mappedFPRRegister[accessedTempReg[f]]; - if( regName >= PPCREC_NAME_FPR0 && regName < PPCREC_NAME_FPR0+32 ) - { - imlSegment->ppcFPRUsed[regName - PPCREC_NAME_FPR0] = true; - } - } - } - } - - // merge certain float load+store patterns (must happen before FPR register remapping) - PPCRecompiler_optimizeDirectFloatCopies(&ppcImlGenContext); - // delay byte swapping for certain load+store patterns - PPCRecompiler_optimizeDirectIntegerCopies(&ppcImlGenContext); - - if (numLoadedFPRRegisters > 0) - { - if (PPCRecompiler_manageFPRRegisters(&ppcImlGenContext) == false) - { - PPCRecompiler_freeContext(&ppcImlGenContext); - return false; - } - } - - PPCRecompilerImm_allocateRegisters(&ppcImlGenContext); - - // remove redundant name load and store instructions - PPCRecompiler_reorderConditionModifyInstructions(&ppcImlGenContext); - PPCRecompiler_removeRedundantCRUpdates(&ppcImlGenContext); return true; } diff --git a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlGenFPU.cpp b/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlGenFPU.cpp index 1efc41b8..ffee73ea 100644 --- a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlGenFPU.cpp +++ b/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlGenFPU.cpp @@ -1,14 +1,16 @@ +#include "Cafe/HW/Espresso/EspressoISA.h" #include "../Interpreter/PPCInterpreterInternal.h" #include "PPCRecompiler.h" #include "PPCRecompilerIml.h" #include "Cafe/GameProfile/GameProfile.h" -void PPCRecompilerImlGen_generateNewInstruction_fpr_r_memory(ppcImlGenContext_t* ppcImlGenContext, uint8 registerDestination, uint8 registerMemory, sint32 immS32, uint32 mode, bool switchEndian, uint8 registerGQR = PPC_REC_INVALID_REGISTER) +IMLReg _GetRegCR(ppcImlGenContext_t* ppcImlGenContext, uint8 crReg, uint8 crBit); + +void PPCRecompilerImlGen_generateNewInstruction_fpr_r_memory(ppcImlGenContext_t* ppcImlGenContext, IMLReg registerDestination, IMLReg registerMemory, sint32 immS32, uint32 mode, bool switchEndian, IMLReg registerGQR = IMLREG_INVALID) { // load from memory - PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); + IMLInstruction* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_FPR_LOAD; - imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; imlInstruction->operation = 0; imlInstruction->op_storeLoad.registerData = registerDestination; imlInstruction->op_storeLoad.registerMem = registerMemory; @@ -18,12 +20,11 @@ void PPCRecompilerImlGen_generateNewInstruction_fpr_r_memory(ppcImlGenContext_t* imlInstruction->op_storeLoad.flags2.swapEndian = switchEndian; } -void PPCRecompilerImlGen_generateNewInstruction_fpr_r_memory_indexed(ppcImlGenContext_t* ppcImlGenContext, uint8 registerDestination, uint8 registerMemory1, uint8 registerMemory2, uint32 mode, bool switchEndian, uint8 registerGQR = PPC_REC_INVALID_REGISTER) +void PPCRecompilerImlGen_generateNewInstruction_fpr_r_memory_indexed(ppcImlGenContext_t* ppcImlGenContext, IMLReg registerDestination, IMLReg registerMemory1, IMLReg registerMemory2, uint32 mode, bool switchEndian, IMLReg registerGQR = IMLREG_INVALID) { // load from memory - PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); + IMLInstruction* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_FPR_LOAD_INDEXED; - imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; imlInstruction->operation = 0; imlInstruction->op_storeLoad.registerData = registerDestination; imlInstruction->op_storeLoad.registerMem = registerMemory1; @@ -34,12 +35,11 @@ void PPCRecompilerImlGen_generateNewInstruction_fpr_r_memory_indexed(ppcImlGenCo imlInstruction->op_storeLoad.flags2.swapEndian = switchEndian; } -void PPCRecompilerImlGen_generateNewInstruction_fpr_memory_r(ppcImlGenContext_t* ppcImlGenContext, uint8 registerSource, uint8 registerMemory, sint32 immS32, uint32 mode, bool switchEndian, uint8 registerGQR = PPC_REC_INVALID_REGISTER) +void PPCRecompilerImlGen_generateNewInstruction_fpr_memory_r(ppcImlGenContext_t* ppcImlGenContext, IMLReg registerSource, IMLReg registerMemory, sint32 immS32, uint32 mode, bool switchEndian, IMLReg registerGQR = IMLREG_INVALID) { // store to memory - PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); + IMLInstruction* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_FPR_STORE; - imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; imlInstruction->operation = 0; imlInstruction->op_storeLoad.registerData = registerSource; imlInstruction->op_storeLoad.registerMem = registerMemory; @@ -49,12 +49,11 @@ void PPCRecompilerImlGen_generateNewInstruction_fpr_memory_r(ppcImlGenContext_t* imlInstruction->op_storeLoad.flags2.swapEndian = switchEndian; } -void PPCRecompilerImlGen_generateNewInstruction_fpr_memory_r_indexed(ppcImlGenContext_t* ppcImlGenContext, uint8 registerSource, uint8 registerMemory1, uint8 registerMemory2, sint32 immS32, uint32 mode, bool switchEndian, uint8 registerGQR = 0) +void PPCRecompilerImlGen_generateNewInstruction_fpr_memory_r_indexed(ppcImlGenContext_t* ppcImlGenContext, IMLReg registerSource, IMLReg registerMemory1, IMLReg registerMemory2, sint32 immS32, uint32 mode, bool switchEndian, IMLReg registerGQR = IMLREG_INVALID) { // store to memory - PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); + IMLInstruction* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_FPR_STORE_INDEXED; - imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; imlInstruction->operation = 0; imlInstruction->op_storeLoad.registerData = registerSource; imlInstruction->op_storeLoad.registerMem = registerMemory1; @@ -65,60 +64,53 @@ void PPCRecompilerImlGen_generateNewInstruction_fpr_memory_r_indexed(ppcImlGenCo imlInstruction->op_storeLoad.flags2.swapEndian = switchEndian; } -void PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext_t* ppcImlGenContext, sint32 operation, uint8 registerResult, uint8 registerOperand, sint32 crRegister=PPC_REC_INVALID_REGISTER) +void PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext_t* ppcImlGenContext, sint32 operation, IMLReg registerResult, IMLReg registerOperand, sint32 crRegister=PPC_REC_INVALID_REGISTER) { // fpr OP fpr - PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); + IMLInstruction* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_FPR_R_R; imlInstruction->operation = operation; - imlInstruction->op_fpr_r_r.registerResult = registerResult; - imlInstruction->op_fpr_r_r.registerOperand = registerOperand; - imlInstruction->crRegister = crRegister; - imlInstruction->op_fpr_r_r.flags = 0; + imlInstruction->op_fpr_r_r.regR = registerResult; + imlInstruction->op_fpr_r_r.regA = registerOperand; } -void PPCRecompilerImlGen_generateNewInstruction_fpr_r_r_r(ppcImlGenContext_t* ppcImlGenContext, sint32 operation, uint8 registerResult, uint8 registerOperand1, uint8 registerOperand2, sint32 crRegister=PPC_REC_INVALID_REGISTER) +void PPCRecompilerImlGen_generateNewInstruction_fpr_r_r_r(ppcImlGenContext_t* ppcImlGenContext, sint32 operation, IMLReg registerResult, IMLReg registerOperand1, IMLReg registerOperand2, sint32 crRegister=PPC_REC_INVALID_REGISTER) { // fpr = OP (fpr,fpr) - PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); + IMLInstruction* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_FPR_R_R_R; imlInstruction->operation = operation; - imlInstruction->op_fpr_r_r_r.registerResult = registerResult; - imlInstruction->op_fpr_r_r_r.registerOperandA = registerOperand1; - imlInstruction->op_fpr_r_r_r.registerOperandB = registerOperand2; - imlInstruction->crRegister = crRegister; - imlInstruction->op_fpr_r_r_r.flags = 0; + imlInstruction->op_fpr_r_r_r.regR = registerResult; + imlInstruction->op_fpr_r_r_r.regA = registerOperand1; + imlInstruction->op_fpr_r_r_r.regB = registerOperand2; } -void PPCRecompilerImlGen_generateNewInstruction_fpr_r_r_r_r(ppcImlGenContext_t* ppcImlGenContext, sint32 operation, uint8 registerResult, uint8 registerOperandA, uint8 registerOperandB, uint8 registerOperandC, sint32 crRegister=PPC_REC_INVALID_REGISTER) +void PPCRecompilerImlGen_generateNewInstruction_fpr_r_r_r_r(ppcImlGenContext_t* ppcImlGenContext, sint32 operation, IMLReg registerResult, IMLReg registerOperandA, IMLReg registerOperandB, IMLReg registerOperandC, sint32 crRegister=PPC_REC_INVALID_REGISTER) { // fpr = OP (fpr,fpr,fpr) - PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); + IMLInstruction* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_FPR_R_R_R_R; imlInstruction->operation = operation; - imlInstruction->op_fpr_r_r_r_r.registerResult = registerResult; - imlInstruction->op_fpr_r_r_r_r.registerOperandA = registerOperandA; - imlInstruction->op_fpr_r_r_r_r.registerOperandB = registerOperandB; - imlInstruction->op_fpr_r_r_r_r.registerOperandC = registerOperandC; - imlInstruction->crRegister = crRegister; - imlInstruction->op_fpr_r_r_r_r.flags = 0; + imlInstruction->op_fpr_r_r_r_r.regR = registerResult; + imlInstruction->op_fpr_r_r_r_r.regA = registerOperandA; + imlInstruction->op_fpr_r_r_r_r.regB = registerOperandB; + imlInstruction->op_fpr_r_r_r_r.regC = registerOperandC; } -void PPCRecompilerImlGen_generateNewInstruction_fpr_r(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction, sint32 operation, uint8 registerResult, sint32 crRegister) +void PPCRecompilerImlGen_generateNewInstruction_fpr_r(ppcImlGenContext_t* ppcImlGenContext, IMLInstruction* imlInstruction, sint32 operation, IMLReg registerResult) { // OP (fpr) if(imlInstruction == NULL) imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_FPR_R; imlInstruction->operation = operation; - imlInstruction->op_fpr_r.registerResult = registerResult; - imlInstruction->crRegister = crRegister; + imlInstruction->op_fpr_r.regR = registerResult; } /* * Rounds the bottom double to single precision (if single precision accuracy is emulated) */ -void PPRecompilerImmGen_optionalRoundBottomFPRToSinglePrecision(ppcImlGenContext_t* ppcImlGenContext, uint32 fprRegister, bool flushDenormals=false) +void PPRecompilerImmGen_optionalRoundBottomFPRToSinglePrecision(ppcImlGenContext_t* ppcImlGenContext, IMLReg fprRegister, bool flushDenormals=false) { PPCRecompilerImlGen_generateNewInstruction_fpr_r(ppcImlGenContext, NULL, PPCREC_IML_OP_FPR_ROUND_TO_SINGLE_PRECISION_BOTTOM, fprRegister); if( flushDenormals ) @@ -128,7 +120,7 @@ void PPRecompilerImmGen_optionalRoundBottomFPRToSinglePrecision(ppcImlGenContext /* * Rounds pair of doubles to single precision (if single precision accuracy is emulated) */ -void PPRecompilerImmGen_optionalRoundPairFPRToSinglePrecision(ppcImlGenContext_t* ppcImlGenContext, uint32 fprRegister, bool flushDenormals=false) +void PPRecompilerImmGen_optionalRoundPairFPRToSinglePrecision(ppcImlGenContext_t* ppcImlGenContext, IMLReg fprRegister, bool flushDenormals=false) { PPCRecompilerImlGen_generateNewInstruction_fpr_r(ppcImlGenContext, NULL, PPCREC_IML_OP_FPR_ROUND_TO_SINGLE_PRECISION_PAIR, fprRegister); if( flushDenormals ) @@ -141,9 +133,9 @@ bool PPCRecompilerImlGen_LFS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, frD, rA, imm); // get memory gpr register index - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); + IMLReg gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); // get fpr register index - uint32 fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); if( ppcImlGenContext->LSQE ) { PPCRecompilerImlGen_generateNewInstruction_fpr_r_memory(ppcImlGenContext, fprRegister, gprRegister, imm, PPCREC_FPR_LD_MODE_SINGLE_INTO_PS0_PS1, true); @@ -161,11 +153,11 @@ bool PPCRecompilerImlGen_LFSU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcod uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, frD, rA, imm); // get memory gpr register index - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); + IMLReg gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); // add imm to memory register - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_ADD, gprRegister, gprRegister, (sint32)imm); // get fpr register index - uint32 fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); if( ppcImlGenContext->LSQE ) { PPCRecompilerImlGen_generateNewInstruction_fpr_r_memory(ppcImlGenContext, fprRegister, gprRegister, 0, PPCREC_FPR_LD_MODE_SINGLE_INTO_PS0_PS1, true); @@ -187,10 +179,10 @@ bool PPCRecompilerImlGen_LFSX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcod return false; } // get memory gpr registers - uint32 gprRegister1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 gprRegister2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); + IMLReg gprRegister1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); + IMLReg gprRegister2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); // get fpr register index - uint32 fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); if( ppcImlGenContext->LSQE ) { PPCRecompilerImlGen_generateNewInstruction_fpr_r_memory_indexed(ppcImlGenContext, fprRegister, gprRegister1, gprRegister2, PPCREC_FPR_LD_MODE_SINGLE_INTO_PS0_PS1, true); @@ -212,12 +204,12 @@ bool PPCRecompilerImlGen_LFSUX(ppcImlGenContext_t* ppcImlGenContext, uint32 opco return false; } // get memory gpr registers - uint32 gprRegister1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 gprRegister2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); + IMLReg gprRegister1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); + IMLReg gprRegister2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); // add rB to rA (if rA != 0) - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD, gprRegister1, gprRegister2); + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_ADD, gprRegister1, gprRegister1, gprRegister2); // get fpr register index - uint32 fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); if( ppcImlGenContext->LSQE ) { PPCRecompilerImlGen_generateNewInstruction_fpr_r_memory(ppcImlGenContext, fprRegister, gprRegister1, 0, PPCREC_FPR_LD_MODE_SINGLE_INTO_PS0_PS1, true); @@ -239,9 +231,9 @@ bool PPCRecompilerImlGen_LFD(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode assert_dbg(); } // get memory gpr register index - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); + IMLReg gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); // get fpr register index - uint32 fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); PPCRecompilerImlGen_generateNewInstruction_fpr_r_memory(ppcImlGenContext, fprRegister, gprRegister, imm, PPCREC_FPR_LD_MODE_DOUBLE_INTO_PS0, true); return true; } @@ -256,11 +248,11 @@ bool PPCRecompilerImlGen_LFDU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcod assert_dbg(); } // get memory gpr register index - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); + IMLReg gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); // add imm to memory register - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_ADD, gprRegister, gprRegister, (sint32)imm); // get fpr register index - uint32 fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); // emit load iml PPCRecompilerImlGen_generateNewInstruction_fpr_r_memory(ppcImlGenContext, fprRegister, gprRegister, 0, PPCREC_FPR_LD_MODE_DOUBLE_INTO_PS0, true); return true; @@ -276,10 +268,10 @@ bool PPCRecompilerImlGen_LFDX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcod return false; } // get memory gpr registers - uint32 gprRegister1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 gprRegister2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); + IMLReg gprRegister1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); + IMLReg gprRegister2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); // get fpr register index - uint32 fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); PPCRecompilerImlGen_generateNewInstruction_fpr_r_memory_indexed(ppcImlGenContext, fprRegister, gprRegister1, gprRegister2, PPCREC_FPR_LD_MODE_DOUBLE_INTO_PS0, true); return true; } @@ -293,13 +285,11 @@ bool PPCRecompilerImlGen_LFDUX(ppcImlGenContext_t* ppcImlGenContext, uint32 opco debugBreakpoint(); return false; } - // get memory gpr registers - uint32 gprRegister1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 gprRegister2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); + IMLReg gprRegister1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); + IMLReg gprRegister2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); // add rB to rA (if rA != 0) - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD, gprRegister1, gprRegister2); - // get fpr register index - uint32 fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_ADD, gprRegister1, gprRegister1, gprRegister2); + IMLReg fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); PPCRecompilerImlGen_generateNewInstruction_fpr_r_memory(ppcImlGenContext, fprRegister, gprRegister1, 0, PPCREC_FPR_LD_MODE_DOUBLE_INTO_PS0, true); return true; } @@ -309,10 +299,8 @@ bool PPCRecompilerImlGen_STFS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcod sint32 rA, frD; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, frD, rA, imm); - // get memory gpr register index - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - // get fpr register index - uint32 fprRegister = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); + IMLReg fprRegister = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); PPCRecompilerImlGen_generateNewInstruction_fpr_memory_r(ppcImlGenContext, fprRegister, gprRegister, imm, PPCREC_FPR_ST_MODE_SINGLE_FROM_PS0, true); return true; @@ -324,11 +312,11 @@ bool PPCRecompilerImlGen_STFSU(ppcImlGenContext_t* ppcImlGenContext, uint32 opco uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, frD, rA, imm); // get memory gpr register index - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); + IMLReg gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); // add imm to memory register - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_ADD, gprRegister, gprRegister, (sint32)imm); // get fpr register index - uint32 fprRegister = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegister = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); PPCRecompilerImlGen_generateNewInstruction_fpr_memory_r(ppcImlGenContext, fprRegister, gprRegister, 0, PPCREC_FPR_ST_MODE_SINGLE_FROM_PS0, true); return true; @@ -344,10 +332,10 @@ bool PPCRecompilerImlGen_STFSX(ppcImlGenContext_t* ppcImlGenContext, uint32 opco return false; } // get memory gpr registers - uint32 gprRegister1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 gprRegister2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); + IMLReg gprRegister1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); + IMLReg gprRegister2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); // get fpr register index - uint32 fprRegister = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frS); + IMLReg fprRegister = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frS); if( ppcImlGenContext->LSQE ) { PPCRecompilerImlGen_generateNewInstruction_fpr_memory_r_indexed(ppcImlGenContext, fprRegister, gprRegister1, gprRegister2, 0, PPCREC_FPR_ST_MODE_SINGLE_FROM_PS0, true); @@ -370,12 +358,12 @@ bool PPCRecompilerImlGen_STFSUX(ppcImlGenContext_t* ppcImlGenContext, uint32 opc return false; } // get memory gpr registers - uint32 gprRegister1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 gprRegister2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); + IMLReg gprRegister1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); + IMLReg gprRegister2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); // get fpr register index - uint32 fprRegister = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frS); + IMLReg fprRegister = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frS); // calculate EA in rA - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD, gprRegister1, gprRegister2); + ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_ADD, gprRegister1, gprRegister1, gprRegister2); PPCRecompilerImlGen_generateNewInstruction_fpr_memory_r(ppcImlGenContext, fprRegister, gprRegister1, 0, PPCREC_FPR_ST_MODE_SINGLE_FROM_PS0, true); return true; @@ -392,9 +380,9 @@ bool PPCRecompilerImlGen_STFD(ppcImlGenContext_t* ppcImlGenContext, uint32 opcod return false; } // get memory gpr register index - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); + IMLReg gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); // get fpr register index - uint32 fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); PPCRecompilerImlGen_generateNewInstruction_fpr_memory_r(ppcImlGenContext, fprRegister, gprRegister, imm, PPCREC_FPR_ST_MODE_DOUBLE_FROM_PS0, true); return true; } @@ -410,11 +398,11 @@ bool PPCRecompilerImlGen_STFDU(ppcImlGenContext_t* ppcImlGenContext, uint32 opco return false; } // get memory gpr register index - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); + IMLReg gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); // add imm to memory register - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_ADD, gprRegister, gprRegister, (sint32)imm); // get fpr register index - uint32 fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); PPCRecompilerImlGen_generateNewInstruction_fpr_memory_r(ppcImlGenContext, fprRegister, gprRegister, 0, PPCREC_FPR_ST_MODE_DOUBLE_FROM_PS0, true); return true; @@ -430,10 +418,10 @@ bool PPCRecompilerImlGen_STFDX(ppcImlGenContext_t* ppcImlGenContext, uint32 opco return false; } // get memory gpr registers - uint32 gprRegister1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - uint32 gprRegister2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); + IMLReg gprRegister1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); + IMLReg gprRegister2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); // get fpr register index - uint32 fprRegister = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frS); + IMLReg fprRegister = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frS); if( ppcImlGenContext->LSQE ) { PPCRecompilerImlGen_generateNewInstruction_fpr_memory_r_indexed(ppcImlGenContext, fprRegister, gprRegister1, gprRegister2, 0, PPCREC_FPR_ST_MODE_DOUBLE_FROM_PS0, true); @@ -450,21 +438,21 @@ bool PPCRecompilerImlGen_STFIWX(ppcImlGenContext_t* ppcImlGenContext, uint32 opc sint32 rA, frS, rB; PPC_OPC_TEMPL_X(opcode, frS, rA, rB); // get memory gpr registers - uint32 gprRegister1; - uint32 gprRegister2; + IMLReg gprRegister1; + IMLReg gprRegister2; if( rA != 0 ) { - gprRegister1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); - gprRegister2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); + gprRegister1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); + gprRegister2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); } else { // rA is not used - gprRegister1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); - gprRegister2 = 0; + gprRegister1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); + gprRegister2 = IMLREG_INVALID; } // get fpr register index - uint32 fprRegister = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frS); + IMLReg fprRegister = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frS); if( rA != 0 ) PPCRecompilerImlGen_generateNewInstruction_fpr_memory_r_indexed(ppcImlGenContext, fprRegister, gprRegister1, gprRegister2, 0, PPCREC_FPR_ST_MODE_UI32_FROM_PS0, true); else @@ -479,9 +467,9 @@ bool PPCRecompilerImlGen_FADD(ppcImlGenContext_t* ppcImlGenContext, uint32 opcod PPC_ASSERT(frC==0); // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_ADD_BOTTOM, fprRegisterD, fprRegisterA, fprRegisterB); return true; @@ -494,9 +482,9 @@ bool PPCRecompilerImlGen_FSUB(ppcImlGenContext_t* ppcImlGenContext, uint32 opcod PPC_ASSERT(frC==0); // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); // subtract bottom double of frB from bottom double of frD PPCRecompilerImlGen_generateNewInstruction_fpr_r_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_SUB_BOTTOM, fprRegisterD, fprRegisterA, fprRegisterB); return true; @@ -514,9 +502,9 @@ bool PPCRecompilerImlGen_FMUL(ppcImlGenContext_t* ppcImlGenContext, uint32 opcod frC = temp; } // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); // move frA to frD (if different register) if( fprRegisterD != fprRegisterA ) PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, fprRegisterD, fprRegisterA); // always copy ps0 and ps1 @@ -531,13 +519,13 @@ bool PPCRecompilerImlGen_FDIV(ppcImlGenContext_t* ppcImlGenContext, uint32 opcod PPC_OPC_TEMPL_A(opcode, frD, frA, frB, frC_unused); PPC_ASSERT(frB==0); // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frD); if( frB == frD && frA != frB ) { - uint32 fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0+0); + IMLReg fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0+0); // move frA to temporary register PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, fprRegisterTemp, fprRegisterA); // divide bottom double of temporary register by bottom double of frB @@ -559,14 +547,14 @@ bool PPCRecompilerImlGen_FMADD(ppcImlGenContext_t* ppcImlGenContext, uint32 opco sint32 frD, frA, frB, frC; PPC_OPC_TEMPL_A(opcode, frD, frA, frB, frC); // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); // if frB is already in frD we need a temporary register to store the product of frA*frC if( frB == frD ) { - uint32 fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0+0); + IMLReg fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0+0); // move frA to temporary register PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, fprRegisterTemp, fprRegisterA); // multiply bottom double of temporary register with bottom double of frC @@ -579,7 +567,7 @@ bool PPCRecompilerImlGen_FMADD(ppcImlGenContext_t* ppcImlGenContext, uint32 opco if( fprRegisterD == fprRegisterC ) { // swap frA and frC - sint32 temp = fprRegisterA; + IMLReg temp = fprRegisterA; fprRegisterA = fprRegisterC; fprRegisterC = temp; } @@ -598,10 +586,10 @@ bool PPCRecompilerImlGen_FMSUB(ppcImlGenContext_t* ppcImlGenContext, uint32 opco sint32 frD, frA, frB, frC; PPC_OPC_TEMPL_A(opcode, frD, frA, frB, frC); // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); // if frB is already in frD we need a temporary register to store the product of frA*frC if( frB == frD ) { @@ -612,7 +600,7 @@ bool PPCRecompilerImlGen_FMSUB(ppcImlGenContext_t* ppcImlGenContext, uint32 opco if( fprRegisterD == fprRegisterC ) { // swap frA and frC - sint32 temp = fprRegisterA; + IMLReg temp = fprRegisterA; fprRegisterA = fprRegisterC; fprRegisterC = temp; } @@ -632,15 +620,15 @@ bool PPCRecompilerImlGen_FNMSUB(ppcImlGenContext_t* ppcImlGenContext, uint32 opc PPC_OPC_TEMPL_A(opcode, frD, frA, frB, frC); // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); // if frB is already in frD we need a temporary register to store the product of frA*frC if( frB == frD ) { // hCPU->fpr[frD].fpr = -(hCPU->fpr[frA].fpr * hCPU->fpr[frC].fpr - hCPU->fpr[frD].fpr); - uint32 fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0+0); + IMLReg fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0+0); //// negate frB/frD //PPCRecompilerImlGen_generateNewInstruction_fpr_r(ppcImlGenContext, NULL,PPCREC_IML_OP_FPR_NEGATE_BOTTOM, fprRegisterD, true); // move frA to temporary register @@ -659,7 +647,7 @@ bool PPCRecompilerImlGen_FNMSUB(ppcImlGenContext_t* ppcImlGenContext, uint32 opc if( fprRegisterD == fprRegisterC ) { // swap frA and frC - sint32 temp = fprRegisterA; + IMLReg temp = fprRegisterA; fprRegisterA = fprRegisterC; fprRegisterC = temp; } @@ -688,9 +676,9 @@ bool PPCRecompilerImlGen_FMULS(ppcImlGenContext_t* ppcImlGenContext, uint32 opco frC = temp; } // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); // move frA to frD (if different register) if( fprRegisterD != fprRegisterA ) PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, fprRegisterD, fprRegisterA); // always copy ps0 and ps1 @@ -717,13 +705,13 @@ bool PPCRecompilerImlGen_FDIVS(ppcImlGenContext_t* ppcImlGenContext, uint32 opco if( hCPU->PSE ) hCPU->fpr[frD].fp1 = hCPU->fpr[frD].fp0;*/ // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); if( frB == frD && frA != frB ) { - uint32 fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0+0); + IMLReg fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0+0); // move frA to temporary register PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, fprRegisterTemp, fprRegisterA); // divide bottom double of temporary register by bottom double of frB @@ -767,9 +755,9 @@ bool PPCRecompilerImlGen_FADDS(ppcImlGenContext_t* ppcImlGenContext, uint32 opco frB = temp; } // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); // move frA to frD (if different register) if( fprRegisterD != fprRegisterA ) PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, fprRegisterD, fprRegisterA); // always copy ps0 and ps1 @@ -792,9 +780,9 @@ bool PPCRecompilerImlGen_FSUBS(ppcImlGenContext_t* ppcImlGenContext, uint32 opco PPC_ASSERT(frB==0); // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); // subtract bottom PPCRecompilerImlGen_generateNewInstruction_fpr_r_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_SUB_BOTTOM, fprRegisterD, fprRegisterA, fprRegisterB); // adjust accuracy @@ -816,11 +804,11 @@ bool PPCRecompilerImlGen_FMADDS(ppcImlGenContext_t* ppcImlGenContext, uint32 opc //if( hCPU->PSE ) // hCPU->fpr[frD].fp1 = hCPU->fpr[frD].fp0; // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); - uint32 fprRegisterTemp; + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterTemp; // if none of the operand registers overlap with the result register then we can avoid the usage of a temporary register if( fprRegisterD != fprRegisterA && fprRegisterD != fprRegisterB && fprRegisterD != fprRegisterC ) fprRegisterTemp = fprRegisterD; @@ -850,11 +838,11 @@ bool PPCRecompilerImlGen_FMSUBS(ppcImlGenContext_t* ppcImlGenContext, uint32 opc //if( hCPU->PSE ) // hCPU->fpr[frD].fp1 = hCPU->fpr[frD].fp0; // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); - uint32 fprRegisterTemp; + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterTemp; // if none of the operand registers overlap with the result register then we can avoid the usage of a temporary register if( fprRegisterD != fprRegisterA && fprRegisterD != fprRegisterB && fprRegisterD != fprRegisterC ) fprRegisterTemp = fprRegisterD; @@ -887,11 +875,11 @@ bool PPCRecompilerImlGen_FNMSUBS(ppcImlGenContext_t* ppcImlGenContext, uint32 op // hCPU->fpr[frD].fp1 = hCPU->fpr[frD].fp0; // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); - uint32 fprRegisterTemp; + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterTemp; // if none of the operand registers overlap with the result register then we can avoid the usage of a temporary register if( fprRegisterD != fprRegisterA && fprRegisterD != fprRegisterB && fprRegisterD != fprRegisterC ) fprRegisterTemp = fprRegisterD; @@ -916,12 +904,33 @@ bool PPCRecompilerImlGen_FNMSUBS(ppcImlGenContext_t* ppcImlGenContext, uint32 op bool PPCRecompilerImlGen_FCMPO(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { - sint32 crfD, frA, frB; - PPC_OPC_TEMPL_X(opcode, crfD, frA, frB); - crfD >>= 2; - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_FCMPO_BOTTOM, fprRegisterA, fprRegisterB, crfD); + printf("FCMPO: Not implemented\n"); + return false; + + //sint32 crfD, frA, frB; + //PPC_OPC_TEMPL_X(opcode, crfD, frA, frB); + //crfD >>= 2; + //IMLReg regFprA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frA); + //IMLReg regFprB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frB); + + //IMLReg crBitRegLT = _GetCRReg(ppcImlGenContext, crfD, Espresso::CR_BIT::CR_BIT_INDEX_LT); + //IMLReg crBitRegGT = _GetCRReg(ppcImlGenContext, crfD, Espresso::CR_BIT::CR_BIT_INDEX_GT); + //IMLReg crBitRegEQ = _GetCRReg(ppcImlGenContext, crfD, Espresso::CR_BIT::CR_BIT_INDEX_EQ); + //IMLReg crBitRegSO = _GetCRReg(ppcImlGenContext, crfD, Espresso::CR_BIT::CR_BIT_INDEX_SO); + + //ppcImlGenContext->emitInst().make_fpr_compare(regFprA, regFprB, crBitRegLT, IMLCondition::UNORDERED_LT); + //ppcImlGenContext->emitInst().make_fpr_compare(regFprA, regFprB, crBitRegGT, IMLCondition::UNORDERED_GT); + //ppcImlGenContext->emitInst().make_fpr_compare(regFprA, regFprB, crBitRegEQ, IMLCondition::UNORDERED_EQ); + //ppcImlGenContext->emitInst().make_fpr_compare(regFprA, regFprB, crBitRegSO, IMLCondition::UNORDERED_U); + + // todo - set fpscr + + //sint32 crfD, frA, frB; + //PPC_OPC_TEMPL_X(opcode, crfD, frA, frB); + //crfD >>= 2; + //uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + //uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + //PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_FCMPO_BOTTOM, fprRegisterA, fprRegisterB, crfD); return true; } @@ -930,9 +939,21 @@ bool PPCRecompilerImlGen_FCMPU(ppcImlGenContext_t* ppcImlGenContext, uint32 opco sint32 crfD, frA, frB; PPC_OPC_TEMPL_X(opcode, crfD, frA, frB); crfD >>= 2; - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_FCMPU_BOTTOM, fprRegisterA, fprRegisterB, crfD); + IMLReg regFprA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frA); + IMLReg regFprB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frB); + + IMLReg crBitRegLT = _GetRegCR(ppcImlGenContext, crfD, Espresso::CR_BIT::CR_BIT_INDEX_LT); + IMLReg crBitRegGT = _GetRegCR(ppcImlGenContext, crfD, Espresso::CR_BIT::CR_BIT_INDEX_GT); + IMLReg crBitRegEQ = _GetRegCR(ppcImlGenContext, crfD, Espresso::CR_BIT::CR_BIT_INDEX_EQ); + IMLReg crBitRegSO = _GetRegCR(ppcImlGenContext, crfD, Espresso::CR_BIT::CR_BIT_INDEX_SO); + + ppcImlGenContext->emitInst().make_fpr_compare(regFprA, regFprB, crBitRegLT, IMLCondition::UNORDERED_LT); + ppcImlGenContext->emitInst().make_fpr_compare(regFprA, regFprB, crBitRegGT, IMLCondition::UNORDERED_GT); + ppcImlGenContext->emitInst().make_fpr_compare(regFprA, regFprB, crBitRegEQ, IMLCondition::UNORDERED_EQ); + ppcImlGenContext->emitInst().make_fpr_compare(regFprA, regFprB, crBitRegSO, IMLCondition::UNORDERED_U); + + // todo: set fpscr + return true; } @@ -940,8 +961,8 @@ bool PPCRecompilerImlGen_FMR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode { sint32 frD, rA, frB; PPC_OPC_TEMPL_X(opcode, frD, rA, frB); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_BOTTOM, fprRegisterD, fprRegisterB); return true; } @@ -952,8 +973,8 @@ bool PPCRecompilerImlGen_FABS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcod PPC_OPC_TEMPL_X(opcode, frD, frA, frB); PPC_ASSERT(frA==0); // load registers - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); // move frB to frD (if different register) if( fprRegisterD != fprRegisterB ) PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_BOTTOM, fprRegisterD, fprRegisterB); @@ -968,8 +989,8 @@ bool PPCRecompilerImlGen_FNABS(ppcImlGenContext_t* ppcImlGenContext, uint32 opco PPC_OPC_TEMPL_X(opcode, frD, frA, frB); PPC_ASSERT(frA==0); // load registers - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); // move frB to frD (if different register) if( fprRegisterD != fprRegisterB ) PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_BOTTOM, fprRegisterD, fprRegisterB); @@ -984,8 +1005,8 @@ bool PPCRecompilerImlGen_FRES(ppcImlGenContext_t* ppcImlGenContext, uint32 opcod PPC_OPC_TEMPL_X(opcode, frD, frA, frB); PPC_ASSERT(frA==0); // load registers - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_BOTTOM_FRES_TO_BOTTOM_AND_TOP, fprRegisterD, fprRegisterB); // adjust accuracy PPRecompilerImmGen_optionalRoundBottomFPRToSinglePrecision(ppcImlGenContext, fprRegisterD); @@ -997,8 +1018,8 @@ bool PPCRecompilerImlGen_FRSP(ppcImlGenContext_t* ppcImlGenContext, uint32 opcod sint32 frD, frA, frB; PPC_OPC_TEMPL_X(opcode, frD, frA, frB); PPC_ASSERT(frA==0); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); if( fprRegisterD != fprRegisterB ) { PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_BOTTOM, fprRegisterD, fprRegisterB); @@ -1021,8 +1042,8 @@ bool PPCRecompilerImlGen_FNEG(ppcImlGenContext_t* ppcImlGenContext, uint32 opcod return false; } // load registers - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); // move frB to frD (if different register) if( fprRegisterD != fprRegisterB ) PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_BOTTOM, fprRegisterD, fprRegisterB); @@ -1039,10 +1060,10 @@ bool PPCRecompilerImlGen_FSEL(ppcImlGenContext_t* ppcImlGenContext, uint32 opcod { return false; } - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_SELECT_BOTTOM, fprRegisterD, fprRegisterA, fprRegisterB, fprRegisterC); return true; } @@ -1052,8 +1073,8 @@ bool PPCRecompilerImlGen_FRSQRTE(ppcImlGenContext_t* ppcImlGenContext, uint32 op sint32 frD, frA, frB, frC; PPC_OPC_TEMPL_A(opcode, frD, frA, frB, frC); // hCPU->fpr[frD].fpr = 1.0 / sqrt(hCPU->fpr[frB].fpr); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_BOTTOM_RECIPROCAL_SQRT, fprRegisterD, fprRegisterB); // adjust accuracy PPRecompilerImmGen_optionalRoundBottomFPRToSinglePrecision(ppcImlGenContext, fprRegisterD); @@ -1064,8 +1085,8 @@ bool PPCRecompilerImlGen_FCTIWZ(ppcImlGenContext_t* ppcImlGenContext, uint32 opc { sint32 frD, frA, frB; PPC_OPC_TEMPL_X(opcode, frD, frA, frB); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_BOTTOM_FCTIWZ, fprRegisterD, fprRegisterB); return true; } @@ -1083,12 +1104,9 @@ bool PPCRecompilerImlGen_PSQ_L(ppcImlGenContext_t* ppcImlGenContext, uint32 opco bool readPS1 = (opcode & 0x8000) == false; - // get gqr register - uint32 gqrRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + SPR_UGQR0 + gqrIndex, false); - // get memory gpr register index - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA, false); - // get fpr register index - uint32 fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frD); + IMLReg gqrRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + SPR_UGQR0 + gqrIndex); + IMLReg gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA); + IMLReg fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frD); // psq load PPCRecompilerImlGen_generateNewInstruction_fpr_r_memory(ppcImlGenContext, fprRegister, gprRegister, imm, readPS1 ? PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0_PS1 : PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0, true, gqrRegister); return true; @@ -1109,14 +1127,12 @@ bool PPCRecompilerImlGen_PSQ_LU(ppcImlGenContext_t* ppcImlGenContext, uint32 opc bool readPS1 = (opcode & 0x8000) == false; - // get gqr register - uint32 gqrRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + SPR_UGQR0 + gqrIndex, false); - // get memory gpr register index - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA, false); - // add imm to memory register - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); - // get fpr register index - uint32 fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frD); + IMLReg gqrRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + SPR_UGQR0 + gqrIndex); + IMLReg gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA); + + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_ADD, gprRegister, gprRegister, (sint32)imm); + + IMLReg fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frD); // paired load PPCRecompilerImlGen_generateNewInstruction_fpr_r_memory(ppcImlGenContext, fprRegister, gprRegister, 0, readPS1 ? PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0_PS1 : PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0, true, gqrRegister); return true; @@ -1134,12 +1150,9 @@ bool PPCRecompilerImlGen_PSQ_ST(ppcImlGenContext_t* ppcImlGenContext, uint32 opc bool storePS1 = (opcode & 0x8000) == false; - // get gqr register - uint32 gqrRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + SPR_UGQR0 + gqrIndex, false); - // get memory gpr register index - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA, false); - // get fpr register index - uint32 fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frD); + IMLReg gqrRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + SPR_UGQR0 + gqrIndex); + IMLReg gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA); + IMLReg fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frD); // paired store PPCRecompilerImlGen_generateNewInstruction_fpr_memory_r(ppcImlGenContext, fprRegister, gprRegister, imm, storePS1 ? PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0_PS1 : PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0, true, gqrRegister); return true; @@ -1160,14 +1173,11 @@ bool PPCRecompilerImlGen_PSQ_STU(ppcImlGenContext_t* ppcImlGenContext, uint32 op bool storePS1 = (opcode & 0x8000) == false; - // get gqr register - uint32 gqrRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + SPR_UGQR0 + gqrIndex, false); - // get memory gpr register index - uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA, false); - // add imm to memory register - PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); - // get fpr register index - uint32 fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frD); + IMLReg gqrRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + SPR_UGQR0 + gqrIndex); + IMLReg gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA); + ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_ADD, gprRegister, gprRegister, (sint32)imm); + + IMLReg fprRegister = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frD); // paired store PPCRecompilerImlGen_generateNewInstruction_fpr_memory_r(ppcImlGenContext, fprRegister, gprRegister, 0, storePS1 ? PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0_PS1 : PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0, true, gqrRegister); return true; @@ -1180,11 +1190,11 @@ bool PPCRecompilerImlGen_PS_MULS0(ppcImlGenContext_t* ppcImlGenContext, uint32 o frA = (opcode>>16)&0x1F; frD = (opcode>>21)&0x1F; // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); // we need a temporary register to store frC.fp0 in low and high half - uint32 fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0+0); + IMLReg fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0+0); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_BOTTOM_AND_TOP, fprRegisterTemp, fprRegisterC); // if frD == frA we can multiply frD immediately and safe a copy instruction if( frD == frA ) @@ -1210,11 +1220,11 @@ bool PPCRecompilerImlGen_PS_MULS1(ppcImlGenContext_t* ppcImlGenContext, uint32 o frA = (opcode>>16)&0x1F; frD = (opcode>>21)&0x1F; // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); // we need a temporary register to store frC.fp0 in low and high half - uint32 fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0+0); + IMLReg fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0+0); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_COPY_TOP_TO_BOTTOM_AND_TOP, fprRegisterTemp, fprRegisterC); // if frD == frA we can multiply frD immediately and safe a copy instruction if( frD == frA ) @@ -1243,12 +1253,12 @@ bool PPCRecompilerImlGen_PS_MADDS0(ppcImlGenContext_t* ppcImlGenContext, uint32 //float s0 = (float)(hCPU->fpr[frA].fp0 * hCPU->fpr[frC].fp0 + hCPU->fpr[frB].fp0); //float s1 = (float)(hCPU->fpr[frA].fp1 * hCPU->fpr[frC].fp0 + hCPU->fpr[frB].fp1); // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); // we need a temporary register to store frC.fp0 in low and high half - uint32 fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0+0); + IMLReg fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0+0); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_BOTTOM_AND_TOP, fprRegisterTemp, fprRegisterC); // if frD == frA and frD != frB we can multiply frD immediately and safe a copy instruction if( frD == frA && frD != frB ) @@ -1281,12 +1291,12 @@ bool PPCRecompilerImlGen_PS_MADDS1(ppcImlGenContext_t* ppcImlGenContext, uint32 //float s0 = (float)(hCPU->fpr[frA].fp0 * hCPU->fpr[frC].fp1 + hCPU->fpr[frB].fp0); //float s1 = (float)(hCPU->fpr[frA].fp1 * hCPU->fpr[frC].fp1 + hCPU->fpr[frB].fp1); // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); // we need a temporary register to store frC.fp1 in bottom and top half - uint32 fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0+0); + IMLReg fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0+0); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_COPY_TOP_TO_BOTTOM_AND_TOP, fprRegisterTemp, fprRegisterC); // if frD == frA and frD != frB we can multiply frD immediately and safe a copy instruction if( frD == frA && frD != frB ) @@ -1318,9 +1328,9 @@ bool PPCRecompilerImlGen_PS_ADD(ppcImlGenContext_t* ppcImlGenContext, uint32 opc //hCPU->fpr[frD].fp0 = hCPU->fpr[frA].fp0 + hCPU->fpr[frB].fp0; //hCPU->fpr[frD].fp1 = hCPU->fpr[frA].fp1 + hCPU->fpr[frB].fp1; // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); if( frD == frA ) { PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_ADD_PAIR, fprRegisterD, fprRegisterB); @@ -1348,9 +1358,9 @@ bool PPCRecompilerImlGen_PS_SUB(ppcImlGenContext_t* ppcImlGenContext, uint32 opc //hCPU->fpr[frD].fp0 = hCPU->fpr[frA].fp0 - hCPU->fpr[frB].fp0; //hCPU->fpr[frD].fp1 = hCPU->fpr[frA].fp1 - hCPU->fpr[frB].fp1; // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_SUB_PAIR, fprRegisterD, fprRegisterA, fprRegisterB); // adjust accuracy PPRecompilerImmGen_optionalRoundPairFPRToSinglePrecision(ppcImlGenContext, fprRegisterD); @@ -1364,11 +1374,11 @@ bool PPCRecompilerImlGen_PS_MUL(ppcImlGenContext_t* ppcImlGenContext, uint32 opc frA = (opcode >> 16) & 0x1F; frD = (opcode >> 21) & 0x1F; // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frA); - uint32 fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frC); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frA); + IMLReg fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frC); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frD); // we need a temporary register - uint32 fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0 + 0); + IMLReg fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0 + 0); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_COPY_PAIR, fprRegisterTemp, fprRegisterC); // todo-optimize: This instruction can be optimized so that it doesn't always use a temporary register // if frD == frA we can multiply frD immediately and safe a copy instruction @@ -1397,9 +1407,9 @@ bool PPCRecompilerImlGen_PS_DIV(ppcImlGenContext_t* ppcImlGenContext, uint32 opc //hCPU->fpr[frD].fp0 = hCPU->fpr[frA].fp0 / hCPU->fpr[frB].fp0; //hCPU->fpr[frD].fp1 = hCPU->fpr[frA].fp1 / hCPU->fpr[frB].fp1; // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frD); // todo-optimize: This instruction can be optimized so that it doesn't always use a temporary register // if frD == frA we can divide frD immediately and safe a copy instruction if (frD == frA) @@ -1409,7 +1419,7 @@ bool PPCRecompilerImlGen_PS_DIV(ppcImlGenContext_t* ppcImlGenContext, uint32 opc else { // we need a temporary register - uint32 fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0 + 0); + IMLReg fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0 + 0); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_COPY_PAIR, fprRegisterTemp, fprRegisterA); // we divide temporary by frB PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_DIVIDE_PAIR, fprRegisterTemp, fprRegisterB); @@ -1432,12 +1442,12 @@ bool PPCRecompilerImlGen_PS_MADD(ppcImlGenContext_t* ppcImlGenContext, uint32 op //float s1 = (float)(hCPU->fpr[frA].fp1 * hCPU->fpr[frC].fp1 + hCPU->fpr[frB].fp1); // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); // we need a temporary register to store frC.fp0 in low and high half - uint32 fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0+0); + IMLReg fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0+0); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_COPY_PAIR, fprRegisterTemp, fprRegisterC); // todo-optimize: This instruction can be optimized so that it doesn't always use a temporary register // if frD == frA and frD != frB we can multiply frD immediately and save a copy instruction @@ -1470,12 +1480,12 @@ bool PPCRecompilerImlGen_PS_NMADD(ppcImlGenContext_t* ppcImlGenContext, uint32 o frD = (opcode>>21)&0x1F; // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); // we need a temporary register to store frC.fp0 in low and high half - uint32 fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0+0); + IMLReg fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0+0); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_COPY_PAIR, fprRegisterTemp, fprRegisterC); // todo-optimize: This instruction can be optimized so that it doesn't always use a temporary register // if frD == frA and frD != frB we can multiply frD immediately and safe a copy instruction @@ -1514,12 +1524,12 @@ bool PPCRecompilerImlGen_PS_MSUB(ppcImlGenContext_t* ppcImlGenContext, uint32 op //hCPU->fpr[frD].fp1 = (hCPU->fpr[frA].fp1 * hCPU->fpr[frC].fp1 - hCPU->fpr[frB].fp1); // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); // we need a temporary register to store frC.fp0 in low and high half - uint32 fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0+0); + IMLReg fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0+0); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_COPY_PAIR, fprRegisterTemp, fprRegisterC); // todo-optimize: This instruction can be optimized so that it doesn't always use a temporary register // if frD == frA and frD != frB we can multiply frD immediately and safe a copy instruction @@ -1552,12 +1562,12 @@ bool PPCRecompilerImlGen_PS_NMSUB(ppcImlGenContext_t* ppcImlGenContext, uint32 o frD = (opcode>>21)&0x1F; // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); // we need a temporary register to store frC.fp0 in low and high half - uint32 fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0+0); + IMLReg fprRegisterTemp = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY_FPR0+0); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_COPY_PAIR, fprRegisterTemp, fprRegisterC); // todo-optimize: This instruction can be optimized so that it doesn't always use a temporary register // if frD == frA and frD != frB we can multiply frD immediately and safe a copy instruction @@ -1595,10 +1605,10 @@ bool PPCRecompilerImlGen_PS_SUM0(ppcImlGenContext_t* ppcImlGenContext, uint32 op //hCPU->fpr[frD].fp0 = s0; //hCPU->fpr[frD].fp1 = s1; // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_SUM0, fprRegisterD, fprRegisterA, fprRegisterB, fprRegisterC); // adjust accuracy PPRecompilerImmGen_optionalRoundPairFPRToSinglePrecision(ppcImlGenContext, fprRegisterD); @@ -1617,10 +1627,10 @@ bool PPCRecompilerImlGen_PS_SUM1(ppcImlGenContext_t* ppcImlGenContext, uint32 op //hCPU->fpr[frD].fp0 = s0; //hCPU->fpr[frD].fp1 = s1; // load registers - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_SUM1, fprRegisterD, fprRegisterA, fprRegisterB, fprRegisterC); // adjust accuracy PPRecompilerImmGen_optionalRoundPairFPRToSinglePrecision(ppcImlGenContext, fprRegisterD); @@ -1635,8 +1645,8 @@ bool PPCRecompilerImlGen_PS_NEG(ppcImlGenContext_t* ppcImlGenContext, uint32 opc //hCPU->fpr[frD].fp0 = -hCPU->fpr[frB].fp0; //hCPU->fpr[frD].fp1 = -hCPU->fpr[frB].fp1; // load registers - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_NEGATE_PAIR, fprRegisterD, fprRegisterB); return true; } @@ -1647,8 +1657,8 @@ bool PPCRecompilerImlGen_PS_ABS(ppcImlGenContext_t* ppcImlGenContext, uint32 opc frB = (opcode>>11)&0x1F; frD = (opcode>>21)&0x1F; // load registers - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_ABS_PAIR, fprRegisterD, fprRegisterB); return true; } @@ -1662,8 +1672,8 @@ bool PPCRecompilerImlGen_PS_RES(ppcImlGenContext_t* ppcImlGenContext, uint32 opc //hCPU->fpr[frD].fp1 = (float)(1.0f / (float)hCPU->fpr[frB].fp1); // load registers - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_FRES_PAIR, fprRegisterD, fprRegisterB); return true; @@ -1678,8 +1688,8 @@ bool PPCRecompilerImlGen_PS_RSQRTE(ppcImlGenContext_t* ppcImlGenContext, uint32 //hCPU->fpr[frD].fp1 = (float)(1.0f / (float)sqrt(hCPU->fpr[frB].fp1)); // load registers - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_FRSQRTE_PAIR, fprRegisterD, fprRegisterB); return true; } @@ -1694,8 +1704,8 @@ bool PPCRecompilerImlGen_PS_MR(ppcImlGenContext_t* ppcImlGenContext, uint32 opco // load registers if( frB != frD ) { - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_COPY_PAIR, fprRegisterD, fprRegisterB); } return true; @@ -1709,10 +1719,10 @@ bool PPCRecompilerImlGen_PS_SEL(ppcImlGenContext_t* ppcImlGenContext, uint32 opc frA = (opcode>>16)&0x1F; frD = (opcode>>21)&0x1F; - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterC = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frC); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_SELECT_PAIR, fprRegisterD, fprRegisterA, fprRegisterB, fprRegisterC); return true; } @@ -1727,10 +1737,10 @@ bool PPCRecompilerImlGen_PS_MERGE00(ppcImlGenContext_t* ppcImlGenContext, uint32 //float s1 = (float)hCPU->fpr[frB].fp0; //hCPU->fpr[frD].fp0 = s0; //hCPU->fpr[frD].fp1 = s1; - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); - // unpcklpd + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + if( frA == frB ) { // simply duplicate bottom into bottom and top of destination register @@ -1754,9 +1764,9 @@ bool PPCRecompilerImlGen_PS_MERGE01(ppcImlGenContext_t* ppcImlGenContext, uint32 frD = (opcode>>21)&0x1F; // hCPU->fpr[frD].fp0 = hCPU->fpr[frA].fp0; // hCPU->fpr[frD].fp1 = hCPU->fpr[frB].fp1; - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); if( fprRegisterD != fprRegisterB ) PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_COPY_TOP_TO_TOP, fprRegisterD, fprRegisterB); @@ -1773,9 +1783,9 @@ bool PPCRecompilerImlGen_PS_MERGE10(ppcImlGenContext_t* ppcImlGenContext, uint32 frA = (opcode>>16)&0x1F; frD = (opcode>>21)&0x1F; - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); if( frA == frB ) { // swap bottom and top @@ -1811,9 +1821,9 @@ bool PPCRecompilerImlGen_PS_MERGE11(ppcImlGenContext_t* ppcImlGenContext, uint32 frA = (opcode>>16)&0x1F; frD = (opcode>>21)&0x1F; - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); - uint32 fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterD = PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frD); if( fprRegisterA == fprRegisterB ) { PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_COPY_TOP_TO_BOTTOM_AND_TOP, fprRegisterD, fprRegisterA); @@ -1837,38 +1847,47 @@ bool PPCRecompilerImlGen_PS_MERGE11(ppcImlGenContext_t* ppcImlGenContext, uint32 bool PPCRecompilerImlGen_PS_CMPO0(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { + printf("PS_CMPO0: Not implemented\n"); + return false; + sint32 crfD, frA, frB; uint32 c=0; frB = (opcode>>11)&0x1F; frA = (opcode>>16)&0x1F; crfD = (opcode>>23)&0x7; - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0+frB); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_FCMPO_BOTTOM, fprRegisterA, fprRegisterB, crfD); return true; } bool PPCRecompilerImlGen_PS_CMPU0(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { + printf("PS_CMPU0: Not implemented\n"); + return false; + sint32 crfD, frA, frB; frB = (opcode >> 11) & 0x1F; frA = (opcode >> 16) & 0x1F; crfD = (opcode >> 23) & 0x7; - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frB); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frB); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_FCMPU_BOTTOM, fprRegisterA, fprRegisterB, crfD); return true; } bool PPCRecompilerImlGen_PS_CMPU1(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { + printf("PS_CMPU1: Not implemented\n"); + return false; + sint32 crfD, frA, frB; frB = (opcode >> 11) & 0x1F; frA = (opcode >> 16) & 0x1F; crfD = (opcode >> 23) & 0x7; - uint32 fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frA); - uint32 fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frB); + IMLReg fprRegisterA = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frA); + IMLReg fprRegisterB = PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext, PPCREC_NAME_FPR0 + frB); PPCRecompilerImlGen_generateNewInstruction_fpr_r_r(ppcImlGenContext, PPCREC_IML_OP_FPR_FCMPU_TOP, fprRegisterA, fprRegisterB, crfD); return true; } \ No newline at end of file diff --git a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlOptimizer.cpp b/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlOptimizer.cpp deleted file mode 100644 index 45e27664..00000000 --- a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlOptimizer.cpp +++ /dev/null @@ -1,2175 +0,0 @@ -#include "../Interpreter/PPCInterpreterInternal.h" -#include "PPCRecompiler.h" -#include "PPCRecompilerIml.h" -#include "PPCRecompilerX64.h" - -void PPCRecompiler_checkRegisterUsage(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction, PPCImlOptimizerUsedRegisters_t* registersUsed) -{ - registersUsed->readNamedReg1 = -1; - registersUsed->readNamedReg2 = -1; - registersUsed->readNamedReg3 = -1; - registersUsed->writtenNamedReg1 = -1; - registersUsed->readFPR1 = -1; - registersUsed->readFPR2 = -1; - registersUsed->readFPR3 = -1; - registersUsed->readFPR4 = -1; - registersUsed->writtenFPR1 = -1; - if( imlInstruction->type == PPCREC_IML_TYPE_R_NAME ) - { - registersUsed->writtenNamedReg1 = imlInstruction->op_r_name.registerIndex; - } - else if( imlInstruction->type == PPCREC_IML_TYPE_NAME_R ) - { - registersUsed->readNamedReg1 = imlInstruction->op_r_name.registerIndex; - } - else if( imlInstruction->type == PPCREC_IML_TYPE_R_R ) - { - if (imlInstruction->operation == PPCREC_IML_OP_COMPARE_SIGNED || imlInstruction->operation == PPCREC_IML_OP_COMPARE_UNSIGNED || imlInstruction->operation == PPCREC_IML_OP_DCBZ) - { - // both operands are read only - registersUsed->readNamedReg1 = imlInstruction->op_r_r.registerResult; - registersUsed->readNamedReg2 = imlInstruction->op_r_r.registerA; - } - else if ( - imlInstruction->operation == PPCREC_IML_OP_OR || - imlInstruction->operation == PPCREC_IML_OP_AND || - imlInstruction->operation == PPCREC_IML_OP_XOR || - imlInstruction->operation == PPCREC_IML_OP_ADD || - imlInstruction->operation == PPCREC_IML_OP_ADD_CARRY || - imlInstruction->operation == PPCREC_IML_OP_ADD_CARRY_ME || - imlInstruction->operation == PPCREC_IML_OP_SUB_CARRY_UPDATE_CARRY) - { - // result is read and written, operand is read - registersUsed->writtenNamedReg1 = imlInstruction->op_r_r.registerResult; - registersUsed->readNamedReg1 = imlInstruction->op_r_r.registerResult; - registersUsed->readNamedReg2 = imlInstruction->op_r_r.registerA; - } - else if ( - imlInstruction->operation == PPCREC_IML_OP_ASSIGN || - imlInstruction->operation == PPCREC_IML_OP_ENDIAN_SWAP || - imlInstruction->operation == PPCREC_IML_OP_CNTLZW || - imlInstruction->operation == PPCREC_IML_OP_NOT || - imlInstruction->operation == PPCREC_IML_OP_NEG || - imlInstruction->operation == PPCREC_IML_OP_ASSIGN_S16_TO_S32 || - imlInstruction->operation == PPCREC_IML_OP_ASSIGN_S8_TO_S32) - { - // result is written, operand is read - registersUsed->writtenNamedReg1 = imlInstruction->op_r_r.registerResult; - registersUsed->readNamedReg1 = imlInstruction->op_r_r.registerA; - } - else - cemu_assert_unimplemented(); - } - else if (imlInstruction->type == PPCREC_IML_TYPE_R_S32) - { - if (imlInstruction->operation == PPCREC_IML_OP_COMPARE_SIGNED || imlInstruction->operation == PPCREC_IML_OP_COMPARE_UNSIGNED || imlInstruction->operation == PPCREC_IML_OP_MTCRF) - { - // operand register is read only - registersUsed->readNamedReg1 = imlInstruction->op_r_immS32.registerIndex; - } - else if (imlInstruction->operation == PPCREC_IML_OP_ADD || - imlInstruction->operation == PPCREC_IML_OP_SUB || - imlInstruction->operation == PPCREC_IML_OP_AND || - imlInstruction->operation == PPCREC_IML_OP_OR || - imlInstruction->operation == PPCREC_IML_OP_XOR || - imlInstruction->operation == PPCREC_IML_OP_LEFT_ROTATE) - { - // operand register is read and write - registersUsed->readNamedReg1 = imlInstruction->op_r_immS32.registerIndex; - registersUsed->writtenNamedReg1 = imlInstruction->op_r_immS32.registerIndex; - } - else - { - // operand register is write only - // todo - use explicit lists, avoid default cases - registersUsed->writtenNamedReg1 = imlInstruction->op_r_immS32.registerIndex; - } - } - else if (imlInstruction->type == PPCREC_IML_TYPE_CONDITIONAL_R_S32) - { - if (imlInstruction->operation == PPCREC_IML_OP_ASSIGN) - { - // result is written, but also considered read (in case the condition fails) - registersUsed->readNamedReg1 = imlInstruction->op_conditional_r_s32.registerIndex; - registersUsed->writtenNamedReg1 = imlInstruction->op_conditional_r_s32.registerIndex; - } - else - cemu_assert_unimplemented(); - } - else if( imlInstruction->type == PPCREC_IML_TYPE_R_R_S32 ) - { - if( imlInstruction->operation == PPCREC_IML_OP_RLWIMI ) - { - // result and operand register are both read, result is written - registersUsed->writtenNamedReg1 = imlInstruction->op_r_r_s32.registerResult; - registersUsed->readNamedReg1 = imlInstruction->op_r_r_s32.registerResult; - registersUsed->readNamedReg2 = imlInstruction->op_r_r_s32.registerA; - } - else - { - // result is write only and operand is read only - registersUsed->writtenNamedReg1 = imlInstruction->op_r_r_s32.registerResult; - registersUsed->readNamedReg1 = imlInstruction->op_r_r_s32.registerA; - } - } - else if( imlInstruction->type == PPCREC_IML_TYPE_R_R_R ) - { - // in all cases result is written and other operands are read only - registersUsed->writtenNamedReg1 = imlInstruction->op_r_r_r.registerResult; - registersUsed->readNamedReg1 = imlInstruction->op_r_r_r.registerA; - registersUsed->readNamedReg2 = imlInstruction->op_r_r_r.registerB; - } - else if( imlInstruction->type == PPCREC_IML_TYPE_CJUMP || imlInstruction->type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK ) - { - // no effect on registers - } - else if( imlInstruction->type == PPCREC_IML_TYPE_NO_OP ) - { - // no effect on registers - } - else if( imlInstruction->type == PPCREC_IML_TYPE_MACRO ) - { - if( imlInstruction->operation == PPCREC_IML_MACRO_BL || imlInstruction->operation == PPCREC_IML_MACRO_B_FAR || imlInstruction->operation == PPCREC_IML_MACRO_BLR || imlInstruction->operation == PPCREC_IML_MACRO_BLRL || imlInstruction->operation == PPCREC_IML_MACRO_BCTR || imlInstruction->operation == PPCREC_IML_MACRO_BCTRL || imlInstruction->operation == PPCREC_IML_MACRO_LEAVE || imlInstruction->operation == PPCREC_IML_MACRO_DEBUGBREAK || imlInstruction->operation == PPCREC_IML_MACRO_COUNT_CYCLES || imlInstruction->operation == PPCREC_IML_MACRO_HLE || imlInstruction->operation == PPCREC_IML_MACRO_MFTB ) - { - // no effect on registers - } - else - cemu_assert_unimplemented(); - } - else if (imlInstruction->type == PPCREC_IML_TYPE_LOAD) - { - registersUsed->writtenNamedReg1 = imlInstruction->op_storeLoad.registerData; - if (imlInstruction->op_storeLoad.registerMem != PPC_REC_INVALID_REGISTER) - registersUsed->readNamedReg1 = imlInstruction->op_storeLoad.registerMem; - } - else if (imlInstruction->type == PPCREC_IML_TYPE_MEM2MEM) - { - registersUsed->readNamedReg1 = imlInstruction->op_mem2mem.src.registerMem; - registersUsed->readNamedReg2 = imlInstruction->op_mem2mem.dst.registerMem; - } - else if( imlInstruction->type == PPCREC_IML_TYPE_LOAD_INDEXED ) - { - registersUsed->writtenNamedReg1 = imlInstruction->op_storeLoad.registerData; - if( imlInstruction->op_storeLoad.registerMem != PPC_REC_INVALID_REGISTER ) - registersUsed->readNamedReg1 = imlInstruction->op_storeLoad.registerMem; - if( imlInstruction->op_storeLoad.registerMem2 != PPC_REC_INVALID_REGISTER ) - registersUsed->readNamedReg2 = imlInstruction->op_storeLoad.registerMem2; - } - else if( imlInstruction->type == PPCREC_IML_TYPE_STORE ) - { - registersUsed->readNamedReg1 = imlInstruction->op_storeLoad.registerData; - if( imlInstruction->op_storeLoad.registerMem != PPC_REC_INVALID_REGISTER ) - registersUsed->readNamedReg2 = imlInstruction->op_storeLoad.registerMem; - } - else if( imlInstruction->type == PPCREC_IML_TYPE_STORE_INDEXED ) - { - registersUsed->readNamedReg1 = imlInstruction->op_storeLoad.registerData; - if( imlInstruction->op_storeLoad.registerMem != PPC_REC_INVALID_REGISTER ) - registersUsed->readNamedReg2 = imlInstruction->op_storeLoad.registerMem; - if( imlInstruction->op_storeLoad.registerMem2 != PPC_REC_INVALID_REGISTER ) - registersUsed->readNamedReg3 = imlInstruction->op_storeLoad.registerMem2; - } - else if( imlInstruction->type == PPCREC_IML_TYPE_CR ) - { - // only affects cr register - } - else if( imlInstruction->type == PPCREC_IML_TYPE_JUMPMARK ) - { - // no effect on registers - } - else if( imlInstruction->type == PPCREC_IML_TYPE_PPC_ENTER ) - { - // no op - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_R_NAME ) - { - // fpr operation - registersUsed->writtenFPR1 = imlInstruction->op_r_name.registerIndex; - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_NAME_R ) - { - // fpr operation - registersUsed->readFPR1 = imlInstruction->op_r_name.registerIndex; - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_LOAD ) - { - // fpr load operation - registersUsed->writtenFPR1 = imlInstruction->op_storeLoad.registerData; - // address is in gpr register - if (imlInstruction->op_storeLoad.registerMem != PPC_REC_INVALID_REGISTER) - registersUsed->readNamedReg1 = imlInstruction->op_storeLoad.registerMem; - // determine partially written result - switch (imlInstruction->op_storeLoad.mode) - { - case PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0: - case PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0_PS1: - cemu_assert_debug(imlInstruction->op_storeLoad.registerGQR != PPC_REC_INVALID_REGISTER); - registersUsed->readNamedReg2 = imlInstruction->op_storeLoad.registerGQR; - break; - case PPCREC_FPR_LD_MODE_DOUBLE_INTO_PS0: - // PS1 remains the same - registersUsed->readFPR4 = imlInstruction->op_storeLoad.registerData; - break; - case PPCREC_FPR_LD_MODE_SINGLE_INTO_PS0_PS1: - case PPCREC_FPR_LD_MODE_PSQ_FLOAT_PS0_PS1: - case PPCREC_FPR_LD_MODE_PSQ_FLOAT_PS0: - case PPCREC_FPR_LD_MODE_PSQ_S16_PS0: - case PPCREC_FPR_LD_MODE_PSQ_S16_PS0_PS1: - case PPCREC_FPR_LD_MODE_PSQ_U16_PS0_PS1: - case PPCREC_FPR_LD_MODE_PSQ_U16_PS0: - case PPCREC_FPR_LD_MODE_PSQ_S8_PS0_PS1: - case PPCREC_FPR_LD_MODE_PSQ_U8_PS0_PS1: - case PPCREC_FPR_LD_MODE_PSQ_U8_PS0: - case PPCREC_FPR_LD_MODE_PSQ_S8_PS0: - break; - default: - cemu_assert_unimplemented(); - } - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_LOAD_INDEXED ) - { - // fpr load operation - registersUsed->writtenFPR1 = imlInstruction->op_storeLoad.registerData; - // address is in gpr registers - if (imlInstruction->op_storeLoad.registerMem != PPC_REC_INVALID_REGISTER) - registersUsed->readNamedReg1 = imlInstruction->op_storeLoad.registerMem; - if (imlInstruction->op_storeLoad.registerMem2 != PPC_REC_INVALID_REGISTER) - registersUsed->readNamedReg2 = imlInstruction->op_storeLoad.registerMem2; - // determine partially written result - switch (imlInstruction->op_storeLoad.mode) - { - case PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0: - case PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0_PS1: - cemu_assert_debug(imlInstruction->op_storeLoad.registerGQR != PPC_REC_INVALID_REGISTER); - registersUsed->readNamedReg3 = imlInstruction->op_storeLoad.registerGQR; - break; - case PPCREC_FPR_LD_MODE_DOUBLE_INTO_PS0: - // PS1 remains the same - registersUsed->readFPR4 = imlInstruction->op_storeLoad.registerData; - break; - case PPCREC_FPR_LD_MODE_SINGLE_INTO_PS0_PS1: - case PPCREC_FPR_LD_MODE_PSQ_FLOAT_PS0_PS1: - case PPCREC_FPR_LD_MODE_PSQ_FLOAT_PS0: - case PPCREC_FPR_LD_MODE_PSQ_S16_PS0: - case PPCREC_FPR_LD_MODE_PSQ_S16_PS0_PS1: - case PPCREC_FPR_LD_MODE_PSQ_U16_PS0_PS1: - case PPCREC_FPR_LD_MODE_PSQ_U16_PS0: - case PPCREC_FPR_LD_MODE_PSQ_S8_PS0_PS1: - case PPCREC_FPR_LD_MODE_PSQ_U8_PS0_PS1: - case PPCREC_FPR_LD_MODE_PSQ_U8_PS0: - break; - default: - cemu_assert_unimplemented(); - } - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_STORE ) - { - // fpr store operation - registersUsed->readFPR1 = imlInstruction->op_storeLoad.registerData; - if( imlInstruction->op_storeLoad.registerMem != PPC_REC_INVALID_REGISTER ) - registersUsed->readNamedReg1 = imlInstruction->op_storeLoad.registerMem; - // PSQ generic stores also access GQR - switch (imlInstruction->op_storeLoad.mode) - { - case PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0: - case PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0_PS1: - cemu_assert_debug(imlInstruction->op_storeLoad.registerGQR != PPC_REC_INVALID_REGISTER); - registersUsed->readNamedReg2 = imlInstruction->op_storeLoad.registerGQR; - break; - default: - break; - } - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_STORE_INDEXED ) - { - // fpr store operation - registersUsed->readFPR1 = imlInstruction->op_storeLoad.registerData; - // address is in gpr registers - if( imlInstruction->op_storeLoad.registerMem != PPC_REC_INVALID_REGISTER ) - registersUsed->readNamedReg1 = imlInstruction->op_storeLoad.registerMem; - if( imlInstruction->op_storeLoad.registerMem2 != PPC_REC_INVALID_REGISTER ) - registersUsed->readNamedReg2 = imlInstruction->op_storeLoad.registerMem2; - // PSQ generic stores also access GQR - switch (imlInstruction->op_storeLoad.mode) - { - case PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0: - case PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0_PS1: - cemu_assert_debug(imlInstruction->op_storeLoad.registerGQR != PPC_REC_INVALID_REGISTER); - registersUsed->readNamedReg3 = imlInstruction->op_storeLoad.registerGQR; - break; - default: - break; - } - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_R_R ) - { - // fpr operation - if( imlInstruction->operation == PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_BOTTOM_AND_TOP || - imlInstruction->operation == PPCREC_IML_OP_FPR_COPY_TOP_TO_BOTTOM_AND_TOP || - imlInstruction->operation == PPCREC_IML_OP_FPR_COPY_BOTTOM_AND_TOP_SWAPPED || - imlInstruction->operation == PPCREC_IML_OP_ASSIGN || - imlInstruction->operation == PPCREC_IML_OP_FPR_BOTTOM_FRES_TO_BOTTOM_AND_TOP || - imlInstruction->operation == PPCREC_IML_OP_FPR_NEGATE_PAIR || - imlInstruction->operation == PPCREC_IML_OP_FPR_ABS_PAIR || - imlInstruction->operation == PPCREC_IML_OP_FPR_FRES_PAIR || - imlInstruction->operation == PPCREC_IML_OP_FPR_FRSQRTE_PAIR ) - { - // operand read, result written - registersUsed->readFPR1 = imlInstruction->op_fpr_r_r.registerOperand; - registersUsed->writtenFPR1 = imlInstruction->op_fpr_r_r.registerResult; - } - else if( - imlInstruction->operation == PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_BOTTOM || - imlInstruction->operation == PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_TOP || - imlInstruction->operation == PPCREC_IML_OP_FPR_COPY_TOP_TO_TOP || - imlInstruction->operation == PPCREC_IML_OP_FPR_COPY_TOP_TO_BOTTOM || - imlInstruction->operation == PPCREC_IML_OP_FPR_EXPAND_BOTTOM32_TO_BOTTOM64_AND_TOP64 || - imlInstruction->operation == PPCREC_IML_OP_FPR_BOTTOM_FCTIWZ || - imlInstruction->operation == PPCREC_IML_OP_FPR_BOTTOM_RECIPROCAL_SQRT - ) - { - // operand read, result read and (partially) written - registersUsed->readFPR1 = imlInstruction->op_fpr_r_r.registerOperand; - registersUsed->readFPR4 = imlInstruction->op_fpr_r_r.registerResult; - registersUsed->writtenFPR1 = imlInstruction->op_fpr_r_r.registerResult; - } - else if( imlInstruction->operation == PPCREC_IML_OP_FPR_MULTIPLY_BOTTOM || - imlInstruction->operation == PPCREC_IML_OP_FPR_MULTIPLY_PAIR || - imlInstruction->operation == PPCREC_IML_OP_FPR_DIVIDE_BOTTOM || - imlInstruction->operation == PPCREC_IML_OP_FPR_DIVIDE_PAIR || - imlInstruction->operation == PPCREC_IML_OP_FPR_ADD_BOTTOM || - imlInstruction->operation == PPCREC_IML_OP_FPR_ADD_PAIR || - imlInstruction->operation == PPCREC_IML_OP_FPR_SUB_PAIR || - imlInstruction->operation == PPCREC_IML_OP_FPR_SUB_BOTTOM ) - { - // operand read, result read and written - registersUsed->readFPR1 = imlInstruction->op_fpr_r_r.registerOperand; - registersUsed->readFPR2 = imlInstruction->op_fpr_r_r.registerResult; - registersUsed->writtenFPR1 = imlInstruction->op_fpr_r_r.registerResult; - - } - else if(imlInstruction->operation == PPCREC_IML_OP_FPR_FCMPU_BOTTOM || - imlInstruction->operation == PPCREC_IML_OP_FPR_FCMPU_TOP || - imlInstruction->operation == PPCREC_IML_OP_FPR_FCMPO_BOTTOM) - { - // operand read, result read - registersUsed->readFPR1 = imlInstruction->op_fpr_r_r.registerOperand; - registersUsed->readFPR2 = imlInstruction->op_fpr_r_r.registerResult; - } - else - cemu_assert_unimplemented(); - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_R_R_R ) - { - // fpr operation - registersUsed->readFPR1 = imlInstruction->op_fpr_r_r_r.registerOperandA; - registersUsed->readFPR2 = imlInstruction->op_fpr_r_r_r.registerOperandB; - registersUsed->writtenFPR1 = imlInstruction->op_fpr_r_r_r.registerResult; - // handle partially written result - switch (imlInstruction->operation) - { - case PPCREC_IML_OP_FPR_MULTIPLY_BOTTOM: - case PPCREC_IML_OP_FPR_ADD_BOTTOM: - case PPCREC_IML_OP_FPR_SUB_BOTTOM: - registersUsed->readFPR4 = imlInstruction->op_fpr_r_r_r.registerResult; - break; - case PPCREC_IML_OP_FPR_SUB_PAIR: - break; - default: - cemu_assert_unimplemented(); - } - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_R_R_R_R ) - { - // fpr operation - registersUsed->readFPR1 = imlInstruction->op_fpr_r_r_r_r.registerOperandA; - registersUsed->readFPR2 = imlInstruction->op_fpr_r_r_r_r.registerOperandB; - registersUsed->readFPR3 = imlInstruction->op_fpr_r_r_r_r.registerOperandC; - registersUsed->writtenFPR1 = imlInstruction->op_fpr_r_r_r_r.registerResult; - // handle partially written result - switch (imlInstruction->operation) - { - case PPCREC_IML_OP_FPR_SELECT_BOTTOM: - registersUsed->readFPR4 = imlInstruction->op_fpr_r_r_r_r.registerResult; - break; - case PPCREC_IML_OP_FPR_SUM0: - case PPCREC_IML_OP_FPR_SUM1: - case PPCREC_IML_OP_FPR_SELECT_PAIR: - break; - default: - cemu_assert_unimplemented(); - } - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_R ) - { - // fpr operation - if( imlInstruction->operation == PPCREC_IML_OP_FPR_NEGATE_BOTTOM || - imlInstruction->operation == PPCREC_IML_OP_FPR_ABS_BOTTOM || - imlInstruction->operation == PPCREC_IML_OP_FPR_NEGATIVE_ABS_BOTTOM || - imlInstruction->operation == PPCREC_IML_OP_FPR_EXPAND_BOTTOM32_TO_BOTTOM64_AND_TOP64 || - imlInstruction->operation == PPCREC_IML_OP_FPR_ROUND_TO_SINGLE_PRECISION_BOTTOM || - imlInstruction->operation == PPCREC_IML_OP_FPR_ROUND_TO_SINGLE_PRECISION_PAIR ) - { - registersUsed->readFPR1 = imlInstruction->op_fpr_r.registerResult; - registersUsed->writtenFPR1 = imlInstruction->op_fpr_r.registerResult; - } - else - cemu_assert_unimplemented(); - } - else - { - cemu_assert_unimplemented(); - } -} - -#define replaceRegister(__x,__r,__n) (((__x)==(__r))?(__n):(__x)) - -sint32 replaceRegisterMultiple(sint32 reg, sint32 match[4], sint32 replaced[4]) -{ - for (sint32 i = 0; i < 4; i++) - { - if(match[i] < 0) - continue; - if (reg == match[i]) - { - return replaced[i]; - } - } - return reg; -} - -void PPCRecompiler_replaceGPRRegisterUsageMultiple(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction, sint32 gprRegisterSearched[4], sint32 gprRegisterReplaced[4]) -{ - if (imlInstruction->type == PPCREC_IML_TYPE_R_NAME) - { - imlInstruction->op_r_name.registerIndex = replaceRegisterMultiple(imlInstruction->op_r_name.registerIndex, gprRegisterSearched, gprRegisterReplaced); - } - else if (imlInstruction->type == PPCREC_IML_TYPE_NAME_R) - { - imlInstruction->op_r_name.registerIndex = replaceRegisterMultiple(imlInstruction->op_r_name.registerIndex, gprRegisterSearched, gprRegisterReplaced); - } - else if (imlInstruction->type == PPCREC_IML_TYPE_R_R) - { - imlInstruction->op_r_r.registerResult = replaceRegisterMultiple(imlInstruction->op_r_r.registerResult, gprRegisterSearched, gprRegisterReplaced); - imlInstruction->op_r_r.registerA = replaceRegisterMultiple(imlInstruction->op_r_r.registerA, gprRegisterSearched, gprRegisterReplaced); - } - else if (imlInstruction->type == PPCREC_IML_TYPE_R_S32) - { - imlInstruction->op_r_immS32.registerIndex = replaceRegisterMultiple(imlInstruction->op_r_immS32.registerIndex, gprRegisterSearched, gprRegisterReplaced); - } - else if (imlInstruction->type == PPCREC_IML_TYPE_CONDITIONAL_R_S32) - { - imlInstruction->op_conditional_r_s32.registerIndex = replaceRegisterMultiple(imlInstruction->op_conditional_r_s32.registerIndex, gprRegisterSearched, gprRegisterReplaced); - } - else if (imlInstruction->type == PPCREC_IML_TYPE_R_R_S32) - { - // in all cases result is written and other operand is read only - imlInstruction->op_r_r_s32.registerResult = replaceRegisterMultiple(imlInstruction->op_r_r_s32.registerResult, gprRegisterSearched, gprRegisterReplaced); - imlInstruction->op_r_r_s32.registerA = replaceRegisterMultiple(imlInstruction->op_r_r_s32.registerA, gprRegisterSearched, gprRegisterReplaced); - } - else if (imlInstruction->type == PPCREC_IML_TYPE_R_R_R) - { - // in all cases result is written and other operands are read only - imlInstruction->op_r_r_r.registerResult = replaceRegisterMultiple(imlInstruction->op_r_r_r.registerResult, gprRegisterSearched, gprRegisterReplaced); - imlInstruction->op_r_r_r.registerA = replaceRegisterMultiple(imlInstruction->op_r_r_r.registerA, gprRegisterSearched, gprRegisterReplaced); - imlInstruction->op_r_r_r.registerB = replaceRegisterMultiple(imlInstruction->op_r_r_r.registerB, gprRegisterSearched, gprRegisterReplaced); - } - else if (imlInstruction->type == PPCREC_IML_TYPE_CJUMP || imlInstruction->type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK) - { - // no effect on registers - } - else if (imlInstruction->type == PPCREC_IML_TYPE_NO_OP) - { - // no effect on registers - } - else if (imlInstruction->type == PPCREC_IML_TYPE_MACRO) - { - if (imlInstruction->operation == PPCREC_IML_MACRO_BL || imlInstruction->operation == PPCREC_IML_MACRO_B_FAR || imlInstruction->operation == PPCREC_IML_MACRO_BLR || imlInstruction->operation == PPCREC_IML_MACRO_BLRL || imlInstruction->operation == PPCREC_IML_MACRO_BCTR || imlInstruction->operation == PPCREC_IML_MACRO_BCTRL || imlInstruction->operation == PPCREC_IML_MACRO_LEAVE || imlInstruction->operation == PPCREC_IML_MACRO_DEBUGBREAK || imlInstruction->operation == PPCREC_IML_MACRO_HLE || imlInstruction->operation == PPCREC_IML_MACRO_MFTB || imlInstruction->operation == PPCREC_IML_MACRO_COUNT_CYCLES ) - { - // no effect on registers - } - else - { - cemu_assert_unimplemented(); - } - } - else if (imlInstruction->type == PPCREC_IML_TYPE_LOAD) - { - imlInstruction->op_storeLoad.registerData = replaceRegisterMultiple(imlInstruction->op_storeLoad.registerData, gprRegisterSearched, gprRegisterReplaced); - if (imlInstruction->op_storeLoad.registerMem != PPC_REC_INVALID_REGISTER) - { - imlInstruction->op_storeLoad.registerMem = replaceRegisterMultiple(imlInstruction->op_storeLoad.registerMem, gprRegisterSearched, gprRegisterReplaced); - } - } - else if (imlInstruction->type == PPCREC_IML_TYPE_LOAD_INDEXED) - { - imlInstruction->op_storeLoad.registerData = replaceRegisterMultiple(imlInstruction->op_storeLoad.registerData, gprRegisterSearched, gprRegisterReplaced); - if (imlInstruction->op_storeLoad.registerMem != PPC_REC_INVALID_REGISTER) - imlInstruction->op_storeLoad.registerMem = replaceRegisterMultiple(imlInstruction->op_storeLoad.registerMem, gprRegisterSearched, gprRegisterReplaced); - if (imlInstruction->op_storeLoad.registerMem2 != PPC_REC_INVALID_REGISTER) - imlInstruction->op_storeLoad.registerMem2 = replaceRegisterMultiple(imlInstruction->op_storeLoad.registerMem2, gprRegisterSearched, gprRegisterReplaced); - } - else if (imlInstruction->type == PPCREC_IML_TYPE_STORE) - { - imlInstruction->op_storeLoad.registerData = replaceRegisterMultiple(imlInstruction->op_storeLoad.registerData, gprRegisterSearched, gprRegisterReplaced); - if (imlInstruction->op_storeLoad.registerMem != PPC_REC_INVALID_REGISTER) - imlInstruction->op_storeLoad.registerMem = replaceRegisterMultiple(imlInstruction->op_storeLoad.registerMem, gprRegisterSearched, gprRegisterReplaced); - } - else if (imlInstruction->type == PPCREC_IML_TYPE_STORE_INDEXED) - { - imlInstruction->op_storeLoad.registerData = replaceRegisterMultiple(imlInstruction->op_storeLoad.registerData, gprRegisterSearched, gprRegisterReplaced); - if (imlInstruction->op_storeLoad.registerMem != PPC_REC_INVALID_REGISTER) - imlInstruction->op_storeLoad.registerMem = replaceRegisterMultiple(imlInstruction->op_storeLoad.registerMem, gprRegisterSearched, gprRegisterReplaced); - if (imlInstruction->op_storeLoad.registerMem2 != PPC_REC_INVALID_REGISTER) - imlInstruction->op_storeLoad.registerMem2 = replaceRegisterMultiple(imlInstruction->op_storeLoad.registerMem2, gprRegisterSearched, gprRegisterReplaced); - } - else if (imlInstruction->type == PPCREC_IML_TYPE_CR) - { - // only affects cr register - } - else if (imlInstruction->type == PPCREC_IML_TYPE_JUMPMARK) - { - // no effect on registers - } - else if (imlInstruction->type == PPCREC_IML_TYPE_PPC_ENTER) - { - // no op - } - else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_R_NAME) - { - - } - else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_NAME_R) - { - - } - else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_LOAD) - { - if (imlInstruction->op_storeLoad.registerMem != PPC_REC_INVALID_REGISTER) - { - imlInstruction->op_storeLoad.registerMem = replaceRegisterMultiple(imlInstruction->op_storeLoad.registerMem, gprRegisterSearched, gprRegisterReplaced); - } - if (imlInstruction->op_storeLoad.registerGQR != PPC_REC_INVALID_REGISTER) - { - imlInstruction->op_storeLoad.registerGQR = replaceRegisterMultiple(imlInstruction->op_storeLoad.registerGQR, gprRegisterSearched, gprRegisterReplaced); - } - } - else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_LOAD_INDEXED) - { - if (imlInstruction->op_storeLoad.registerMem != PPC_REC_INVALID_REGISTER) - { - imlInstruction->op_storeLoad.registerMem = replaceRegisterMultiple(imlInstruction->op_storeLoad.registerMem, gprRegisterSearched, gprRegisterReplaced); - } - if (imlInstruction->op_storeLoad.registerMem2 != PPC_REC_INVALID_REGISTER) - { - imlInstruction->op_storeLoad.registerMem2 = replaceRegisterMultiple(imlInstruction->op_storeLoad.registerMem2, gprRegisterSearched, gprRegisterReplaced); - } - if (imlInstruction->op_storeLoad.registerGQR != PPC_REC_INVALID_REGISTER) - { - imlInstruction->op_storeLoad.registerGQR = replaceRegisterMultiple(imlInstruction->op_storeLoad.registerGQR, gprRegisterSearched, gprRegisterReplaced); - } - } - else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_STORE) - { - if (imlInstruction->op_storeLoad.registerMem != PPC_REC_INVALID_REGISTER) - { - imlInstruction->op_storeLoad.registerMem = replaceRegisterMultiple(imlInstruction->op_storeLoad.registerMem, gprRegisterSearched, gprRegisterReplaced); - } - if (imlInstruction->op_storeLoad.registerGQR != PPC_REC_INVALID_REGISTER) - { - imlInstruction->op_storeLoad.registerGQR = replaceRegisterMultiple(imlInstruction->op_storeLoad.registerGQR, gprRegisterSearched, gprRegisterReplaced); - } - } - else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_STORE_INDEXED) - { - if (imlInstruction->op_storeLoad.registerMem != PPC_REC_INVALID_REGISTER) - { - imlInstruction->op_storeLoad.registerMem = replaceRegisterMultiple(imlInstruction->op_storeLoad.registerMem, gprRegisterSearched, gprRegisterReplaced); - } - if (imlInstruction->op_storeLoad.registerMem2 != PPC_REC_INVALID_REGISTER) - { - imlInstruction->op_storeLoad.registerMem2 = replaceRegisterMultiple(imlInstruction->op_storeLoad.registerMem2, gprRegisterSearched, gprRegisterReplaced); - } - if (imlInstruction->op_storeLoad.registerGQR != PPC_REC_INVALID_REGISTER) - { - imlInstruction->op_storeLoad.registerGQR = replaceRegisterMultiple(imlInstruction->op_storeLoad.registerGQR, gprRegisterSearched, gprRegisterReplaced); - } - } - else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_R_R) - { - } - else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_R_R_R) - { - } - else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_R_R_R_R) - { - } - else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_R) - { - } - else - { - cemu_assert_unimplemented(); - } -} - -void PPCRecompiler_replaceFPRRegisterUsageMultiple(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction, sint32 fprRegisterSearched[4], sint32 fprRegisterReplaced[4]) -{ - if (imlInstruction->type == PPCREC_IML_TYPE_R_NAME) - { - // not affected - } - else if (imlInstruction->type == PPCREC_IML_TYPE_NAME_R) - { - // not affected - } - else if (imlInstruction->type == PPCREC_IML_TYPE_R_R) - { - // not affected - } - else if (imlInstruction->type == PPCREC_IML_TYPE_R_S32) - { - // not affected - } - else if (imlInstruction->type == PPCREC_IML_TYPE_R_R_S32) - { - // not affected - } - else if (imlInstruction->type == PPCREC_IML_TYPE_R_R_R) - { - // not affected - } - else if (imlInstruction->type == PPCREC_IML_TYPE_CJUMP || imlInstruction->type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK) - { - // no effect on registers - } - else if (imlInstruction->type == PPCREC_IML_TYPE_NO_OP) - { - // no effect on registers - } - else if (imlInstruction->type == PPCREC_IML_TYPE_MACRO) - { - // not affected - } - else if (imlInstruction->type == PPCREC_IML_TYPE_LOAD) - { - // not affected - } - else if (imlInstruction->type == PPCREC_IML_TYPE_MEM2MEM) - { - // not affected - } - else if (imlInstruction->type == PPCREC_IML_TYPE_LOAD_INDEXED) - { - // not affected - } - else if (imlInstruction->type == PPCREC_IML_TYPE_STORE) - { - // not affected - } - else if (imlInstruction->type == PPCREC_IML_TYPE_STORE_INDEXED) - { - // not affected - } - else if (imlInstruction->type == PPCREC_IML_TYPE_CR) - { - // only affects cr register - } - else if (imlInstruction->type == PPCREC_IML_TYPE_JUMPMARK) - { - // no effect on registers - } - else if (imlInstruction->type == PPCREC_IML_TYPE_PPC_ENTER) - { - // no op - } - else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_R_NAME) - { - imlInstruction->op_r_name.registerIndex = replaceRegisterMultiple(imlInstruction->op_r_name.registerIndex, fprRegisterSearched, fprRegisterReplaced); - } - else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_NAME_R) - { - imlInstruction->op_r_name.registerIndex = replaceRegisterMultiple(imlInstruction->op_r_name.registerIndex, fprRegisterSearched, fprRegisterReplaced); - } - else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_LOAD) - { - imlInstruction->op_storeLoad.registerData = replaceRegisterMultiple(imlInstruction->op_storeLoad.registerData, fprRegisterSearched, fprRegisterReplaced); - } - else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_LOAD_INDEXED) - { - imlInstruction->op_storeLoad.registerData = replaceRegisterMultiple(imlInstruction->op_storeLoad.registerData, fprRegisterSearched, fprRegisterReplaced); - } - else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_STORE) - { - imlInstruction->op_storeLoad.registerData = replaceRegisterMultiple(imlInstruction->op_storeLoad.registerData, fprRegisterSearched, fprRegisterReplaced); - } - else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_STORE_INDEXED) - { - imlInstruction->op_storeLoad.registerData = replaceRegisterMultiple(imlInstruction->op_storeLoad.registerData, fprRegisterSearched, fprRegisterReplaced); - } - else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_R_R) - { - imlInstruction->op_fpr_r_r.registerResult = replaceRegisterMultiple(imlInstruction->op_fpr_r_r.registerResult, fprRegisterSearched, fprRegisterReplaced); - imlInstruction->op_fpr_r_r.registerOperand = replaceRegisterMultiple(imlInstruction->op_fpr_r_r.registerOperand, fprRegisterSearched, fprRegisterReplaced); - } - else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_R_R_R) - { - imlInstruction->op_fpr_r_r_r.registerResult = replaceRegisterMultiple(imlInstruction->op_fpr_r_r_r.registerResult, fprRegisterSearched, fprRegisterReplaced); - imlInstruction->op_fpr_r_r_r.registerOperandA = replaceRegisterMultiple(imlInstruction->op_fpr_r_r_r.registerOperandA, fprRegisterSearched, fprRegisterReplaced); - imlInstruction->op_fpr_r_r_r.registerOperandB = replaceRegisterMultiple(imlInstruction->op_fpr_r_r_r.registerOperandB, fprRegisterSearched, fprRegisterReplaced); - } - else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_R_R_R_R) - { - imlInstruction->op_fpr_r_r_r_r.registerResult = replaceRegisterMultiple(imlInstruction->op_fpr_r_r_r_r.registerResult, fprRegisterSearched, fprRegisterReplaced); - imlInstruction->op_fpr_r_r_r_r.registerOperandA = replaceRegisterMultiple(imlInstruction->op_fpr_r_r_r_r.registerOperandA, fprRegisterSearched, fprRegisterReplaced); - imlInstruction->op_fpr_r_r_r_r.registerOperandB = replaceRegisterMultiple(imlInstruction->op_fpr_r_r_r_r.registerOperandB, fprRegisterSearched, fprRegisterReplaced); - imlInstruction->op_fpr_r_r_r_r.registerOperandC = replaceRegisterMultiple(imlInstruction->op_fpr_r_r_r_r.registerOperandC, fprRegisterSearched, fprRegisterReplaced); - } - else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_R) - { - imlInstruction->op_fpr_r.registerResult = replaceRegisterMultiple(imlInstruction->op_fpr_r.registerResult, fprRegisterSearched, fprRegisterReplaced); - } - else - { - cemu_assert_unimplemented(); - } -} - -void PPCRecompiler_replaceFPRRegisterUsage(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction, sint32 fprRegisterSearched, sint32 fprRegisterReplaced) -{ - if( imlInstruction->type == PPCREC_IML_TYPE_R_NAME ) - { - // not affected - } - else if( imlInstruction->type == PPCREC_IML_TYPE_NAME_R ) - { - // not affected - } - else if( imlInstruction->type == PPCREC_IML_TYPE_R_R ) - { - // not affected - } - else if( imlInstruction->type == PPCREC_IML_TYPE_R_S32 ) - { - // not affected - } - else if( imlInstruction->type == PPCREC_IML_TYPE_R_R_S32 ) - { - // not affected - } - else if( imlInstruction->type == PPCREC_IML_TYPE_R_R_R ) - { - // not affected - } - else if( imlInstruction->type == PPCREC_IML_TYPE_CJUMP || imlInstruction->type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK ) - { - // no effect on registers - } - else if( imlInstruction->type == PPCREC_IML_TYPE_NO_OP ) - { - // no effect on registers - } - else if( imlInstruction->type == PPCREC_IML_TYPE_MACRO ) - { - // not affected - } - else if( imlInstruction->type == PPCREC_IML_TYPE_LOAD ) - { - // not affected - } - else if (imlInstruction->type == PPCREC_IML_TYPE_MEM2MEM) - { - // not affected - } - else if( imlInstruction->type == PPCREC_IML_TYPE_LOAD_INDEXED ) - { - // not affected - } - else if( imlInstruction->type == PPCREC_IML_TYPE_STORE ) - { - // not affected - } - else if( imlInstruction->type == PPCREC_IML_TYPE_STORE_INDEXED ) - { - // not affected - } - else if( imlInstruction->type == PPCREC_IML_TYPE_CR ) - { - // only affects cr register - } - else if( imlInstruction->type == PPCREC_IML_TYPE_JUMPMARK ) - { - // no effect on registers - } - else if( imlInstruction->type == PPCREC_IML_TYPE_PPC_ENTER ) - { - // no op - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_R_NAME ) - { - imlInstruction->op_r_name.registerIndex = replaceRegister(imlInstruction->op_r_name.registerIndex, fprRegisterSearched, fprRegisterReplaced); - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_NAME_R ) - { - imlInstruction->op_r_name.registerIndex = replaceRegister(imlInstruction->op_r_name.registerIndex, fprRegisterSearched, fprRegisterReplaced); - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_LOAD ) - { - imlInstruction->op_storeLoad.registerData = replaceRegister(imlInstruction->op_storeLoad.registerData, fprRegisterSearched, fprRegisterReplaced); - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_LOAD_INDEXED ) - { - imlInstruction->op_storeLoad.registerData = replaceRegister(imlInstruction->op_storeLoad.registerData, fprRegisterSearched, fprRegisterReplaced); - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_STORE ) - { - imlInstruction->op_storeLoad.registerData = replaceRegister(imlInstruction->op_storeLoad.registerData, fprRegisterSearched, fprRegisterReplaced); - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_STORE_INDEXED ) - { - imlInstruction->op_storeLoad.registerData = replaceRegister(imlInstruction->op_storeLoad.registerData, fprRegisterSearched, fprRegisterReplaced); - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_R_R ) - { - imlInstruction->op_fpr_r_r.registerResult = replaceRegister(imlInstruction->op_fpr_r_r.registerResult, fprRegisterSearched, fprRegisterReplaced); - imlInstruction->op_fpr_r_r.registerOperand = replaceRegister(imlInstruction->op_fpr_r_r.registerOperand, fprRegisterSearched, fprRegisterReplaced); - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_R_R_R ) - { - imlInstruction->op_fpr_r_r_r.registerResult = replaceRegister(imlInstruction->op_fpr_r_r_r.registerResult, fprRegisterSearched, fprRegisterReplaced); - imlInstruction->op_fpr_r_r_r.registerOperandA = replaceRegister(imlInstruction->op_fpr_r_r_r.registerOperandA, fprRegisterSearched, fprRegisterReplaced); - imlInstruction->op_fpr_r_r_r.registerOperandB = replaceRegister(imlInstruction->op_fpr_r_r_r.registerOperandB, fprRegisterSearched, fprRegisterReplaced); - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_R_R_R_R ) - { - imlInstruction->op_fpr_r_r_r_r.registerResult = replaceRegister(imlInstruction->op_fpr_r_r_r_r.registerResult, fprRegisterSearched, fprRegisterReplaced); - imlInstruction->op_fpr_r_r_r_r.registerOperandA = replaceRegister(imlInstruction->op_fpr_r_r_r_r.registerOperandA, fprRegisterSearched, fprRegisterReplaced); - imlInstruction->op_fpr_r_r_r_r.registerOperandB = replaceRegister(imlInstruction->op_fpr_r_r_r_r.registerOperandB, fprRegisterSearched, fprRegisterReplaced); - imlInstruction->op_fpr_r_r_r_r.registerOperandC = replaceRegister(imlInstruction->op_fpr_r_r_r_r.registerOperandC, fprRegisterSearched, fprRegisterReplaced); - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_R ) - { - imlInstruction->op_fpr_r.registerResult = replaceRegister(imlInstruction->op_fpr_r.registerResult, fprRegisterSearched, fprRegisterReplaced); - } - else - { - cemu_assert_unimplemented(); - } -} - -typedef struct -{ - struct - { - sint32 instructionIndex; - sint32 registerPreviousName; - sint32 registerNewName; - sint32 index; // new index - sint32 previousIndex; // previous index (always out of range) - bool nameMustBeMaintained; // must be stored before replacement and loaded after replacement ends - }replacedRegisterEntry[PPC_X64_GPR_USABLE_REGISTERS]; - sint32 count; -}replacedRegisterTracker_t; - -bool PPCRecompiler_checkIfGPRRegisterIsAccessed(PPCImlOptimizerUsedRegisters_t* registersUsed, sint32 gprRegister) -{ - if( registersUsed->readNamedReg1 == gprRegister ) - return true; - if( registersUsed->readNamedReg2 == gprRegister ) - return true; - if( registersUsed->readNamedReg3 == gprRegister ) - return true; - if( registersUsed->writtenNamedReg1 == gprRegister ) - return true; - return false; -} - -/* - * Returns index of register to replace - * If no register needs to be replaced, -1 is returned - */ -sint32 PPCRecompiler_getNextRegisterToReplace(PPCImlOptimizerUsedRegisters_t* registersUsed) -{ - // get index of register to replace - sint32 gprToReplace = -1; - if( registersUsed->readNamedReg1 >= PPC_X64_GPR_USABLE_REGISTERS ) - gprToReplace = registersUsed->readNamedReg1; - else if( registersUsed->readNamedReg2 >= PPC_X64_GPR_USABLE_REGISTERS ) - gprToReplace = registersUsed->readNamedReg2; - else if( registersUsed->readNamedReg3 >= PPC_X64_GPR_USABLE_REGISTERS ) - gprToReplace = registersUsed->readNamedReg3; - else if( registersUsed->writtenNamedReg1 >= PPC_X64_GPR_USABLE_REGISTERS ) - gprToReplace = registersUsed->writtenNamedReg1; - // return - return gprToReplace; -} - -bool PPCRecompiler_findAvailableRegisterDepr(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* imlSegment, sint32 imlIndexStart, replacedRegisterTracker_t* replacedRegisterTracker, sint32* registerIndex, sint32* registerName, bool* isUsed) -{ - PPCImlOptimizerUsedRegisters_t registersUsed; - PPCRecompiler_checkRegisterUsage(ppcImlGenContext, imlSegment->imlList+imlIndexStart, ®istersUsed); - // mask all registers used by this instruction - uint32 instructionReservedRegisterMask = 0;//(1<<(PPC_X64_GPR_USABLE_REGISTERS+1))-1; - if( registersUsed.readNamedReg1 != -1 ) - instructionReservedRegisterMask |= (1<<(registersUsed.readNamedReg1)); - if( registersUsed.readNamedReg2 != -1 ) - instructionReservedRegisterMask |= (1<<(registersUsed.readNamedReg2)); - if( registersUsed.readNamedReg3 != -1 ) - instructionReservedRegisterMask |= (1<<(registersUsed.readNamedReg3)); - if( registersUsed.writtenNamedReg1 != -1 ) - instructionReservedRegisterMask |= (1<<(registersUsed.writtenNamedReg1)); - // mask all registers that are reserved for other replacements - uint32 replacementReservedRegisterMask = 0; - for(sint32 i=0; icount; i++) - { - replacementReservedRegisterMask |= (1<replacedRegisterEntry[i].index); - } - - // potential improvement: Scan ahead a few instructions and look for registers that are the least used (or ideally never used) - - // pick available register - const uint32 allRegisterMask = (1<<(PPC_X64_GPR_USABLE_REGISTERS+1))-1; // mask with set bit for every register - uint32 reservedRegisterMask = instructionReservedRegisterMask | replacementReservedRegisterMask; - cemu_assert(instructionReservedRegisterMask != allRegisterMask); // no usable register! (Need to store a register from the replacedRegisterTracker) - sint32 usedRegisterIndex = -1; - for(sint32 i=0; imappedRegister[i] != -1 ) - { - // register is reserved by segment -> In use - *isUsed = true; - *registerName = ppcImlGenContext->mappedRegister[i]; - } - else - { - *isUsed = false; - *registerName = -1; - } - *registerIndex = i; - return true; - } - } - return false; - -} - -bool PPCRecompiler_hasSuffixInstruction(PPCRecImlSegment_t* imlSegment) -{ - if( imlSegment->imlListCount == 0 ) - return false; - PPCRecImlInstruction_t* imlInstruction = imlSegment->imlList+imlSegment->imlListCount-1; - if( imlInstruction->type == PPCREC_IML_TYPE_MACRO && (imlInstruction->operation == PPCREC_IML_MACRO_BLR || imlInstruction->operation == PPCREC_IML_MACRO_BCTR) || - imlInstruction->type == PPCREC_IML_TYPE_MACRO && imlInstruction->operation == PPCREC_IML_MACRO_BL || - imlInstruction->type == PPCREC_IML_TYPE_MACRO && imlInstruction->operation == PPCREC_IML_MACRO_B_FAR || - imlInstruction->type == PPCREC_IML_TYPE_MACRO && imlInstruction->operation == PPCREC_IML_MACRO_BLRL || - imlInstruction->type == PPCREC_IML_TYPE_MACRO && imlInstruction->operation == PPCREC_IML_MACRO_BCTRL || - imlInstruction->type == PPCREC_IML_TYPE_MACRO && imlInstruction->operation == PPCREC_IML_MACRO_LEAVE || - imlInstruction->type == PPCREC_IML_TYPE_MACRO && imlInstruction->operation == PPCREC_IML_MACRO_HLE || - imlInstruction->type == PPCREC_IML_TYPE_MACRO && imlInstruction->operation == PPCREC_IML_MACRO_MFTB || - imlInstruction->type == PPCREC_IML_TYPE_PPC_ENTER || - imlInstruction->type == PPCREC_IML_TYPE_CJUMP || - imlInstruction->type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK ) - return true; - return false; -} - -void PPCRecompiler_storeReplacedRegister(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* imlSegment, replacedRegisterTracker_t* replacedRegisterTracker, sint32 registerTrackerIndex, sint32* imlIndex) -{ - // store register - sint32 imlIndexEdit = *imlIndex; - PPCRecompiler_pushBackIMLInstructions(imlSegment, imlIndexEdit, 1); - // name_unusedRegister = unusedRegister - PPCRecImlInstruction_t* imlInstructionItr = imlSegment->imlList+(imlIndexEdit+0); - memset(imlInstructionItr, 0x00, sizeof(PPCRecImlInstruction_t)); - imlInstructionItr->type = PPCREC_IML_TYPE_NAME_R; - imlInstructionItr->crRegister = PPC_REC_INVALID_REGISTER; - imlInstructionItr->operation = PPCREC_IML_OP_ASSIGN; - imlInstructionItr->op_r_name.registerIndex = replacedRegisterTracker->replacedRegisterEntry[registerTrackerIndex].index; - imlInstructionItr->op_r_name.name = replacedRegisterTracker->replacedRegisterEntry[registerTrackerIndex].registerNewName; - imlInstructionItr->op_r_name.copyWidth = 32; - imlInstructionItr->op_r_name.flags = 0; - imlIndexEdit++; - // load new register if required - if( replacedRegisterTracker->replacedRegisterEntry[registerTrackerIndex].nameMustBeMaintained ) - { - PPCRecompiler_pushBackIMLInstructions(imlSegment, imlIndexEdit, 1); - PPCRecImlInstruction_t* imlInstructionItr = imlSegment->imlList+(imlIndexEdit+0); - memset(imlInstructionItr, 0x00, sizeof(PPCRecImlInstruction_t)); - imlInstructionItr->type = PPCREC_IML_TYPE_R_NAME; - imlInstructionItr->crRegister = PPC_REC_INVALID_REGISTER; - imlInstructionItr->operation = PPCREC_IML_OP_ASSIGN; - imlInstructionItr->op_r_name.registerIndex = replacedRegisterTracker->replacedRegisterEntry[registerTrackerIndex].index; - imlInstructionItr->op_r_name.name = replacedRegisterTracker->replacedRegisterEntry[registerTrackerIndex].registerPreviousName;//ppcImlGenContext->mappedRegister[replacedRegisterTracker.replacedRegisterEntry[i].index]; - imlInstructionItr->op_r_name.copyWidth = 32; - imlInstructionItr->op_r_name.flags = 0; - imlIndexEdit += 1; - } - // move last entry to current one - memcpy(replacedRegisterTracker->replacedRegisterEntry+registerTrackerIndex, replacedRegisterTracker->replacedRegisterEntry+replacedRegisterTracker->count-1, sizeof(replacedRegisterTracker->replacedRegisterEntry[0])); - replacedRegisterTracker->count--; - *imlIndex = imlIndexEdit; -} - -bool PPCRecompiler_reduceNumberOfFPRRegisters(ppcImlGenContext_t* ppcImlGenContext) -{ - // only xmm0 to xmm14 may be used, xmm15 is reserved - // this method will reduce the number of fpr registers used - // inefficient algorithm for optimizing away excess registers - // we simply load, use and store excess registers into other unused registers when we need to - // first we remove all name load and store instructions that involve out-of-bounds registers - for(sint32 s=0; ssegmentListCount; s++) - { - PPCRecImlSegment_t* imlSegment = ppcImlGenContext->segmentList[s]; - sint32 imlIndex = 0; - while( imlIndex < imlSegment->imlListCount ) - { - PPCRecImlInstruction_t* imlInstructionItr = imlSegment->imlList+imlIndex; - if( imlInstructionItr->type == PPCREC_IML_TYPE_FPR_R_NAME || imlInstructionItr->type == PPCREC_IML_TYPE_FPR_NAME_R ) - { - if( imlInstructionItr->op_r_name.registerIndex >= PPC_X64_FPR_USABLE_REGISTERS ) - { - // convert to NO-OP instruction - imlInstructionItr->type = PPCREC_IML_TYPE_NO_OP; - imlInstructionItr->associatedPPCAddress = 0; - } - } - imlIndex++; - } - } - // replace registers - for(sint32 s=0; ssegmentListCount; s++) - { - PPCRecImlSegment_t* imlSegment = ppcImlGenContext->segmentList[s]; - sint32 imlIndex = 0; - while( imlIndex < imlSegment->imlListCount ) - { - PPCImlOptimizerUsedRegisters_t registersUsed; - while( true ) - { - PPCRecompiler_checkRegisterUsage(ppcImlGenContext, imlSegment->imlList+imlIndex, ®istersUsed); - if( registersUsed.readFPR1 >= PPC_X64_FPR_USABLE_REGISTERS || registersUsed.readFPR2 >= PPC_X64_FPR_USABLE_REGISTERS || registersUsed.readFPR3 >= PPC_X64_FPR_USABLE_REGISTERS || registersUsed.readFPR4 >= PPC_X64_FPR_USABLE_REGISTERS || registersUsed.writtenFPR1 >= PPC_X64_FPR_USABLE_REGISTERS ) - { - // get index of register to replace - sint32 fprToReplace = -1; - if( registersUsed.readFPR1 >= PPC_X64_FPR_USABLE_REGISTERS ) - fprToReplace = registersUsed.readFPR1; - else if( registersUsed.readFPR2 >= PPC_X64_FPR_USABLE_REGISTERS ) - fprToReplace = registersUsed.readFPR2; - else if (registersUsed.readFPR3 >= PPC_X64_FPR_USABLE_REGISTERS) - fprToReplace = registersUsed.readFPR3; - else if (registersUsed.readFPR4 >= PPC_X64_FPR_USABLE_REGISTERS) - fprToReplace = registersUsed.readFPR4; - else if( registersUsed.writtenFPR1 >= PPC_X64_FPR_USABLE_REGISTERS ) - fprToReplace = registersUsed.writtenFPR1; - // generate mask of useable registers - uint8 useableRegisterMask = 0x7F; // lowest bit is fpr register 0 - if( registersUsed.readFPR1 != -1 ) - useableRegisterMask &= ~(1<<(registersUsed.readFPR1)); - if( registersUsed.readFPR2 != -1 ) - useableRegisterMask &= ~(1<<(registersUsed.readFPR2)); - if (registersUsed.readFPR3 != -1) - useableRegisterMask &= ~(1 << (registersUsed.readFPR3)); - if (registersUsed.readFPR4 != -1) - useableRegisterMask &= ~(1 << (registersUsed.readFPR4)); - if( registersUsed.writtenFPR1 != -1 ) - useableRegisterMask &= ~(1<<(registersUsed.writtenFPR1)); - // get highest unused register index (0-6 range) - sint32 unusedRegisterIndex = -1; - for(sint32 f=0; fmappedFPRRegister[unusedRegisterIndex]; - bool replacedRegisterIsUsed = true; - if( unusedRegisterName >= PPCREC_NAME_FPR0 && unusedRegisterName < (PPCREC_NAME_FPR0+32) ) - { - replacedRegisterIsUsed = imlSegment->ppcFPRUsed[unusedRegisterName-PPCREC_NAME_FPR0]; - } - // replace registers that are out of range - PPCRecompiler_replaceFPRRegisterUsage(ppcImlGenContext, imlSegment->imlList+imlIndex, fprToReplace, unusedRegisterIndex); - // add load/store name after instruction - PPCRecompiler_pushBackIMLInstructions(imlSegment, imlIndex+1, 2); - // add load/store before current instruction - PPCRecompiler_pushBackIMLInstructions(imlSegment, imlIndex, 2); - // name_unusedRegister = unusedRegister - PPCRecImlInstruction_t* imlInstructionItr = imlSegment->imlList+(imlIndex+0); - memset(imlInstructionItr, 0x00, sizeof(PPCRecImlInstruction_t)); - if( replacedRegisterIsUsed ) - { - imlInstructionItr->type = PPCREC_IML_TYPE_FPR_NAME_R; - imlInstructionItr->operation = PPCREC_IML_OP_ASSIGN; - imlInstructionItr->op_r_name.registerIndex = unusedRegisterIndex; - imlInstructionItr->op_r_name.name = ppcImlGenContext->mappedFPRRegister[unusedRegisterIndex]; - imlInstructionItr->op_r_name.copyWidth = 32; - imlInstructionItr->op_r_name.flags = 0; - } - else - imlInstructionItr->type = PPCREC_IML_TYPE_NO_OP; - imlInstructionItr = imlSegment->imlList+(imlIndex+1); - memset(imlInstructionItr, 0x00, sizeof(PPCRecImlInstruction_t)); - imlInstructionItr->type = PPCREC_IML_TYPE_FPR_R_NAME; - imlInstructionItr->operation = PPCREC_IML_OP_ASSIGN; - imlInstructionItr->op_r_name.registerIndex = unusedRegisterIndex; - imlInstructionItr->op_r_name.name = ppcImlGenContext->mappedFPRRegister[fprToReplace]; - imlInstructionItr->op_r_name.copyWidth = 32; - imlInstructionItr->op_r_name.flags = 0; - // name_gprToReplace = unusedRegister - imlInstructionItr = imlSegment->imlList+(imlIndex+3); - memset(imlInstructionItr, 0x00, sizeof(PPCRecImlInstruction_t)); - imlInstructionItr->type = PPCREC_IML_TYPE_FPR_NAME_R; - imlInstructionItr->operation = PPCREC_IML_OP_ASSIGN; - imlInstructionItr->op_r_name.registerIndex = unusedRegisterIndex; - imlInstructionItr->op_r_name.name = ppcImlGenContext->mappedFPRRegister[fprToReplace]; - imlInstructionItr->op_r_name.copyWidth = 32; - imlInstructionItr->op_r_name.flags = 0; - // unusedRegister = name_unusedRegister - imlInstructionItr = imlSegment->imlList+(imlIndex+4); - memset(imlInstructionItr, 0x00, sizeof(PPCRecImlInstruction_t)); - if( replacedRegisterIsUsed ) - { - imlInstructionItr->type = PPCREC_IML_TYPE_FPR_R_NAME; - imlInstructionItr->operation = PPCREC_IML_OP_ASSIGN; - imlInstructionItr->op_r_name.registerIndex = unusedRegisterIndex; - imlInstructionItr->op_r_name.name = ppcImlGenContext->mappedFPRRegister[unusedRegisterIndex]; - imlInstructionItr->op_r_name.copyWidth = 32; - imlInstructionItr->op_r_name.flags = 0; - } - else - imlInstructionItr->type = PPCREC_IML_TYPE_NO_OP; - } - else - break; - } - imlIndex++; - } - } - return true; -} - -typedef struct -{ - bool isActive; - uint32 virtualReg; - sint32 lastUseIndex; -}ppcRecRegisterMapping_t; - -typedef struct -{ - ppcRecRegisterMapping_t currentMapping[PPC_X64_FPR_USABLE_REGISTERS]; - sint32 ppcRegToMapping[64]; - sint32 currentUseIndex; -}ppcRecManageRegisters_t; - -ppcRecRegisterMapping_t* PPCRecompiler_findAvailableRegisterDepr(ppcRecManageRegisters_t* rCtx, PPCImlOptimizerUsedRegisters_t* instructionUsedRegisters) -{ - // find free register - for (sint32 i = 0; i < PPC_X64_FPR_USABLE_REGISTERS; i++) - { - if (rCtx->currentMapping[i].isActive == false) - { - rCtx->currentMapping[i].isActive = true; - rCtx->currentMapping[i].virtualReg = -1; - rCtx->currentMapping[i].lastUseIndex = rCtx->currentUseIndex; - return rCtx->currentMapping + i; - } - } - // all registers are used - return nullptr; -} - -ppcRecRegisterMapping_t* PPCRecompiler_findUnloadableRegister(ppcRecManageRegisters_t* rCtx, PPCImlOptimizerUsedRegisters_t* instructionUsedRegisters, uint32 unloadLockedMask) -{ - // find unloadable register (with lowest lastUseIndex) - sint32 unloadIndex = -1; - sint32 unloadIndexLastUse = 0x7FFFFFFF; - for (sint32 i = 0; i < PPC_X64_FPR_USABLE_REGISTERS; i++) - { - if (rCtx->currentMapping[i].isActive == false) - continue; - if( (unloadLockedMask&(1<currentMapping[i].virtualReg; - bool isReserved = false; - for (sint32 f = 0; f < 4; f++) - { - if (virtualReg == (sint32)instructionUsedRegisters->fpr[f]) - { - isReserved = true; - break; - } - } - if (isReserved) - continue; - if (rCtx->currentMapping[i].lastUseIndex < unloadIndexLastUse) - { - unloadIndexLastUse = rCtx->currentMapping[i].lastUseIndex; - unloadIndex = i; - } - } - cemu_assert(unloadIndex != -1); - return rCtx->currentMapping + unloadIndex; -} - -bool PPCRecompiler_manageFPRRegistersForSegment(ppcImlGenContext_t* ppcImlGenContext, sint32 segmentIndex) -{ - ppcRecManageRegisters_t rCtx = { 0 }; - for (sint32 i = 0; i < 64; i++) - rCtx.ppcRegToMapping[i] = -1; - PPCRecImlSegment_t* imlSegment = ppcImlGenContext->segmentList[segmentIndex]; - sint32 idx = 0; - sint32 currentUseIndex = 0; - PPCImlOptimizerUsedRegisters_t registersUsed; - while (idx < imlSegment->imlListCount) - { - if ( PPCRecompiler_isSuffixInstruction(imlSegment->imlList + idx) ) - break; - PPCRecompiler_checkRegisterUsage(ppcImlGenContext, imlSegment->imlList + idx, ®istersUsed); - sint32 fprMatch[4]; - sint32 fprReplace[4]; - fprMatch[0] = -1; - fprMatch[1] = -1; - fprMatch[2] = -1; - fprMatch[3] = -1; - fprReplace[0] = -1; - fprReplace[1] = -1; - fprReplace[2] = -1; - fprReplace[3] = -1; - // generate a mask of registers that we may not free - sint32 numReplacedOperands = 0; - uint32 unloadLockedMask = 0; - for (sint32 f = 0; f < 5; f++) - { - sint32 virtualFpr; - if (f == 0) - virtualFpr = registersUsed.readFPR1; - else if (f == 1) - virtualFpr = registersUsed.readFPR2; - else if (f == 2) - virtualFpr = registersUsed.readFPR3; - else if (f == 3) - virtualFpr = registersUsed.readFPR4; - else if (f == 4) - virtualFpr = registersUsed.writtenFPR1; - if( virtualFpr < 0 ) - continue; - cemu_assert_debug(virtualFpr < 64); - // check if this virtual FPR is already loaded in any real register - ppcRecRegisterMapping_t* regMapping; - if (rCtx.ppcRegToMapping[virtualFpr] == -1) - { - // not loaded - // find available register - while (true) - { - regMapping = PPCRecompiler_findAvailableRegisterDepr(&rCtx, ®istersUsed); - if (regMapping == NULL) - { - // unload least recently used register and try again - ppcRecRegisterMapping_t* unloadRegMapping = PPCRecompiler_findUnloadableRegister(&rCtx, ®istersUsed, unloadLockedMask); - // mark as locked - unloadLockedMask |= (1<<(unloadRegMapping- rCtx.currentMapping)); - // create unload instruction - PPCRecompiler_pushBackIMLInstructions(imlSegment, idx, 1); - PPCRecImlInstruction_t* imlInstructionTemp = imlSegment->imlList + idx; - memset(imlInstructionTemp, 0x00, sizeof(PPCRecImlInstruction_t)); - imlInstructionTemp->type = PPCREC_IML_TYPE_FPR_NAME_R; - imlInstructionTemp->operation = PPCREC_IML_OP_ASSIGN; - imlInstructionTemp->op_r_name.registerIndex = (uint8)(unloadRegMapping - rCtx.currentMapping); - imlInstructionTemp->op_r_name.name = ppcImlGenContext->mappedFPRRegister[unloadRegMapping->virtualReg]; - imlInstructionTemp->op_r_name.copyWidth = 32; - imlInstructionTemp->op_r_name.flags = 0; - idx++; - // update mapping - unloadRegMapping->isActive = false; - rCtx.ppcRegToMapping[unloadRegMapping->virtualReg] = -1; - } - else - break; - } - // create load instruction - PPCRecompiler_pushBackIMLInstructions(imlSegment, idx, 1); - PPCRecImlInstruction_t* imlInstructionTemp = imlSegment->imlList + idx; - memset(imlInstructionTemp, 0x00, sizeof(PPCRecImlInstruction_t)); - imlInstructionTemp->type = PPCREC_IML_TYPE_FPR_R_NAME; - imlInstructionTemp->operation = PPCREC_IML_OP_ASSIGN; - imlInstructionTemp->op_r_name.registerIndex = (uint8)(regMapping-rCtx.currentMapping); - imlInstructionTemp->op_r_name.name = ppcImlGenContext->mappedFPRRegister[virtualFpr]; - imlInstructionTemp->op_r_name.copyWidth = 32; - imlInstructionTemp->op_r_name.flags = 0; - idx++; - // update mapping - regMapping->virtualReg = virtualFpr; - rCtx.ppcRegToMapping[virtualFpr] = (sint32)(regMapping - rCtx.currentMapping); - regMapping->lastUseIndex = rCtx.currentUseIndex; - rCtx.currentUseIndex++; - } - else - { - regMapping = rCtx.currentMapping + rCtx.ppcRegToMapping[virtualFpr]; - regMapping->lastUseIndex = rCtx.currentUseIndex; - rCtx.currentUseIndex++; - } - // replace FPR - bool entryFound = false; - for (sint32 t = 0; t < numReplacedOperands; t++) - { - if (fprMatch[t] == virtualFpr) - { - cemu_assert_debug(fprReplace[t] == (regMapping - rCtx.currentMapping)); - entryFound = true; - break; - } - } - if (entryFound == false) - { - cemu_assert_debug(numReplacedOperands != 4); - fprMatch[numReplacedOperands] = virtualFpr; - fprReplace[numReplacedOperands] = (sint32)(regMapping - rCtx.currentMapping); - numReplacedOperands++; - } - } - if (numReplacedOperands > 0) - { - PPCRecompiler_replaceFPRRegisterUsageMultiple(ppcImlGenContext, imlSegment->imlList + idx, fprMatch, fprReplace); - } - // next - idx++; - } - // count loaded registers - sint32 numLoadedRegisters = 0; - for (sint32 i = 0; i < PPC_X64_FPR_USABLE_REGISTERS; i++) - { - if (rCtx.currentMapping[i].isActive) - numLoadedRegisters++; - } - // store all loaded registers - if (numLoadedRegisters > 0) - { - PPCRecompiler_pushBackIMLInstructions(imlSegment, idx, numLoadedRegisters); - for (sint32 i = 0; i < PPC_X64_FPR_USABLE_REGISTERS; i++) - { - if (rCtx.currentMapping[i].isActive == false) - continue; - PPCRecImlInstruction_t* imlInstructionTemp = imlSegment->imlList + idx; - memset(imlInstructionTemp, 0x00, sizeof(PPCRecImlInstruction_t)); - imlInstructionTemp->type = PPCREC_IML_TYPE_FPR_NAME_R; - imlInstructionTemp->operation = PPCREC_IML_OP_ASSIGN; - imlInstructionTemp->op_r_name.registerIndex = i; - imlInstructionTemp->op_r_name.name = ppcImlGenContext->mappedFPRRegister[rCtx.currentMapping[i].virtualReg]; - imlInstructionTemp->op_r_name.copyWidth = 32; - imlInstructionTemp->op_r_name.flags = 0; - idx++; - } - } - return true; -} - -bool PPCRecompiler_manageFPRRegisters(ppcImlGenContext_t* ppcImlGenContext) -{ - for (sint32 s = 0; s < ppcImlGenContext->segmentListCount; s++) - { - if (PPCRecompiler_manageFPRRegistersForSegment(ppcImlGenContext, s) == false) - return false; - } - return true; -} - - -/* - * Returns true if the loaded value is guaranteed to be overwritten - */ -bool PPCRecompiler_trackRedundantNameLoadInstruction(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* imlSegment, sint32 startIndex, PPCRecImlInstruction_t* nameStoreInstruction, sint32 scanDepth) -{ - sint16 registerIndex = nameStoreInstruction->op_r_name.registerIndex; - for(sint32 i=startIndex; iimlListCount; i++) - { - PPCRecImlInstruction_t* imlInstruction = imlSegment->imlList+i; - //nameStoreInstruction->op_r_name.registerIndex - PPCImlOptimizerUsedRegisters_t registersUsed; - PPCRecompiler_checkRegisterUsage(ppcImlGenContext, imlSegment->imlList+i, ®istersUsed); - if( registersUsed.readNamedReg1 == registerIndex || registersUsed.readNamedReg2 == registerIndex || registersUsed.readNamedReg3 == registerIndex ) - return false; - if( registersUsed.writtenNamedReg1 == registerIndex ) - return true; - } - // todo: Scan next segment(s) - return false; -} - -/* - * Returns true if the loaded value is guaranteed to be overwritten - */ -bool PPCRecompiler_trackRedundantFPRNameLoadInstruction(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* imlSegment, sint32 startIndex, PPCRecImlInstruction_t* nameStoreInstruction, sint32 scanDepth) -{ - sint16 registerIndex = nameStoreInstruction->op_r_name.registerIndex; - for(sint32 i=startIndex; iimlListCount; i++) - { - PPCRecImlInstruction_t* imlInstruction = imlSegment->imlList+i; - PPCImlOptimizerUsedRegisters_t registersUsed; - PPCRecompiler_checkRegisterUsage(ppcImlGenContext, imlSegment->imlList+i, ®istersUsed); - if( registersUsed.readFPR1 == registerIndex || registersUsed.readFPR2 == registerIndex || registersUsed.readFPR3 == registerIndex || registersUsed.readFPR4 == registerIndex) - return false; - if( registersUsed.writtenFPR1 == registerIndex ) - return true; - } - // todo: Scan next segment(s) - return false; -} - -/* - * Returns true if the loaded name is never changed - */ -bool PPCRecompiler_trackRedundantNameStoreInstruction(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* imlSegment, sint32 startIndex, PPCRecImlInstruction_t* nameStoreInstruction, sint32 scanDepth) -{ - sint16 registerIndex = nameStoreInstruction->op_r_name.registerIndex; - for(sint32 i=startIndex; i>=0; i--) - { - PPCRecImlInstruction_t* imlInstruction = imlSegment->imlList+i; - PPCImlOptimizerUsedRegisters_t registersUsed; - PPCRecompiler_checkRegisterUsage(ppcImlGenContext, imlSegment->imlList+i, ®istersUsed); - if( registersUsed.writtenNamedReg1 == registerIndex ) - { - if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_R_NAME ) - return true; - return false; - } - } - return false; -} - -sint32 debugCallCounter1 = 0; - -/* - * Returns true if the name is overwritten in the current or any following segments - */ -bool PPCRecompiler_trackOverwrittenNameStoreInstruction(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* imlSegment, sint32 startIndex, PPCRecImlInstruction_t* nameStoreInstruction, sint32 scanDepth) -{ - //sint16 registerIndex = nameStoreInstruction->op_r_name.registerIndex; - uint32 name = nameStoreInstruction->op_r_name.name; - for(sint32 i=startIndex; iimlListCount; i++) - { - PPCRecImlInstruction_t* imlInstruction = imlSegment->imlList+i; - if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_R_NAME ) - { - // name is loaded before being written - if( imlSegment->imlList[i].op_r_name.name == name ) - return false; - } - else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_NAME_R ) - { - // name is written before being loaded - if( imlSegment->imlList[i].op_r_name.name == name ) - return true; - } - } - if( scanDepth >= 2 ) - return false; - if( imlSegment->nextSegmentIsUncertain ) - return false; - if( imlSegment->nextSegmentBranchTaken && PPCRecompiler_trackOverwrittenNameStoreInstruction(ppcImlGenContext, imlSegment->nextSegmentBranchTaken, 0, nameStoreInstruction, scanDepth+1) == false ) - return false; - if( imlSegment->nextSegmentBranchNotTaken && PPCRecompiler_trackOverwrittenNameStoreInstruction(ppcImlGenContext, imlSegment->nextSegmentBranchNotTaken, 0, nameStoreInstruction, scanDepth+1) == false ) - return false; - if( imlSegment->nextSegmentBranchTaken == NULL && imlSegment->nextSegmentBranchNotTaken == NULL ) - return false; - - return true; -} - -/* - * Returns true if the loaded FPR name is never changed - */ -bool PPCRecompiler_trackRedundantFPRNameStoreInstruction(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* imlSegment, sint32 startIndex, PPCRecImlInstruction_t* nameStoreInstruction, sint32 scanDepth) -{ - sint16 registerIndex = nameStoreInstruction->op_r_name.registerIndex; - for(sint32 i=startIndex; i>=0; i--) - { - PPCRecImlInstruction_t* imlInstruction = imlSegment->imlList+i; - PPCImlOptimizerUsedRegisters_t registersUsed; - PPCRecompiler_checkRegisterUsage(ppcImlGenContext, imlSegment->imlList+i, ®istersUsed); - if( registersUsed.writtenFPR1 == registerIndex ) - { - if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_FPR_R_NAME ) - return true; - return false; - } - } - // todo: Scan next segment(s) - return false; -} - -uint32 _PPCRecompiler_getCROverwriteMask(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* imlSegment, uint32 currentOverwriteMask, uint32 currentReadMask, uint32 scanDepth) -{ - // is any bit overwritten but not read? - uint32 overwriteMask = imlSegment->crBitsWritten&~imlSegment->crBitsInput; - currentOverwriteMask |= overwriteMask; - // next segment - if( imlSegment->nextSegmentIsUncertain == false && scanDepth < 3 ) - { - uint32 nextSegmentOverwriteMask = 0; - if( imlSegment->nextSegmentBranchTaken && imlSegment->nextSegmentBranchNotTaken ) - { - uint32 mask0 = _PPCRecompiler_getCROverwriteMask(ppcImlGenContext, imlSegment->nextSegmentBranchTaken, 0, 0, scanDepth+1); - uint32 mask1 = _PPCRecompiler_getCROverwriteMask(ppcImlGenContext, imlSegment->nextSegmentBranchNotTaken, 0, 0, scanDepth+1); - nextSegmentOverwriteMask = mask0&mask1; - } - else if( imlSegment->nextSegmentBranchNotTaken) - { - nextSegmentOverwriteMask = _PPCRecompiler_getCROverwriteMask(ppcImlGenContext, imlSegment->nextSegmentBranchNotTaken, 0, 0, scanDepth+1); - } - nextSegmentOverwriteMask &= ~imlSegment->crBitsRead; - currentOverwriteMask |= nextSegmentOverwriteMask; - } - else if (imlSegment->nextSegmentIsUncertain) - { - if (ppcImlGenContext->segmentListCount >= 5) - { - return 7; // for more complex functions we assume that CR is not passed on - } - } - return currentOverwriteMask; -} - -/* - * Returns a mask of all CR bits that are overwritten (written but not read) in the segment and all it's following segments - * If the write state of a CR bit cannot be determined, it is returned as 0 (not overwritten) - */ -uint32 PPCRecompiler_getCROverwriteMask(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* imlSegment) -{ - if (imlSegment->nextSegmentIsUncertain) - { - return 0; - } - if( imlSegment->nextSegmentBranchTaken && imlSegment->nextSegmentBranchNotTaken ) - { - uint32 mask0 = _PPCRecompiler_getCROverwriteMask(ppcImlGenContext, imlSegment->nextSegmentBranchTaken, 0, 0, 0); - uint32 mask1 = _PPCRecompiler_getCROverwriteMask(ppcImlGenContext, imlSegment->nextSegmentBranchNotTaken, 0, 0, 0); - return mask0&mask1; // only return bits that are overwritten in both branches - } - else if( imlSegment->nextSegmentBranchNotTaken ) - { - uint32 mask = _PPCRecompiler_getCROverwriteMask(ppcImlGenContext, imlSegment->nextSegmentBranchNotTaken, 0, 0, 0); - return mask; - } - else - { - // not implemented - } - return 0; -} - -void PPCRecompiler_removeRedundantCRUpdates(ppcImlGenContext_t* ppcImlGenContext) -{ - for(sint32 s=0; ssegmentListCount; s++) - { - PPCRecImlSegment_t* imlSegment = ppcImlGenContext->segmentList[s]; - - for(sint32 i=0; iimlListCount; i++) - { - PPCRecImlInstruction_t* imlInstruction = imlSegment->imlList+i; - if (imlInstruction->type == PPCREC_IML_TYPE_CJUMP) - { - if (imlInstruction->op_conditionalJump.condition != PPCREC_JUMP_CONDITION_NONE) - { - uint32 crBitFlag = 1 << (imlInstruction->op_conditionalJump.crRegisterIndex * 4 + imlInstruction->op_conditionalJump.crBitIndex); - imlSegment->crBitsInput |= (crBitFlag&~imlSegment->crBitsWritten); // flag bits that have not already been written - imlSegment->crBitsRead |= (crBitFlag); - } - } - else if (imlInstruction->type == PPCREC_IML_TYPE_CONDITIONAL_R_S32) - { - uint32 crBitFlag = 1 << (imlInstruction->op_conditional_r_s32.crRegisterIndex * 4 + imlInstruction->op_conditional_r_s32.crBitIndex); - imlSegment->crBitsInput |= (crBitFlag&~imlSegment->crBitsWritten); // flag bits that have not already been written - imlSegment->crBitsRead |= (crBitFlag); - } - else if (imlInstruction->type == PPCREC_IML_TYPE_R_S32 && imlInstruction->operation == PPCREC_IML_OP_MFCR) - { - imlSegment->crBitsRead |= 0xFFFFFFFF; - } - else if (imlInstruction->type == PPCREC_IML_TYPE_R_S32 && imlInstruction->operation == PPCREC_IML_OP_MTCRF) - { - imlSegment->crBitsWritten |= ppc_MTCRFMaskToCRBitMask((uint32)imlInstruction->op_r_immS32.immS32); - } - else if( imlInstruction->type == PPCREC_IML_TYPE_CR ) - { - if (imlInstruction->operation == PPCREC_IML_OP_CR_CLEAR || - imlInstruction->operation == PPCREC_IML_OP_CR_SET) - { - uint32 crBitFlag = 1 << (imlInstruction->op_cr.crD); - imlSegment->crBitsWritten |= (crBitFlag & ~imlSegment->crBitsWritten); - } - else if (imlInstruction->operation == PPCREC_IML_OP_CR_OR || - imlInstruction->operation == PPCREC_IML_OP_CR_ORC || - imlInstruction->operation == PPCREC_IML_OP_CR_AND || - imlInstruction->operation == PPCREC_IML_OP_CR_ANDC) - { - uint32 crBitFlag = 1 << (imlInstruction->op_cr.crD); - imlSegment->crBitsWritten |= (crBitFlag & ~imlSegment->crBitsWritten); - crBitFlag = 1 << (imlInstruction->op_cr.crA); - imlSegment->crBitsRead |= (crBitFlag & ~imlSegment->crBitsRead); - crBitFlag = 1 << (imlInstruction->op_cr.crB); - imlSegment->crBitsRead |= (crBitFlag & ~imlSegment->crBitsRead); - } - else - cemu_assert_unimplemented(); - } - else if( PPCRecompilerImlAnalyzer_canTypeWriteCR(imlInstruction) && imlInstruction->crRegister >= 0 && imlInstruction->crRegister <= 7 ) - { - imlSegment->crBitsWritten |= (0xF<<(imlInstruction->crRegister*4)); - } - else if( (imlInstruction->type == PPCREC_IML_TYPE_STORE || imlInstruction->type == PPCREC_IML_TYPE_STORE_INDEXED) && imlInstruction->op_storeLoad.copyWidth == PPC_REC_STORE_STWCX_MARKER ) - { - // overwrites CR0 - imlSegment->crBitsWritten |= (0xF<<0); - } - } - } - // flag instructions that write to CR where we can ignore individual CR bits - for(sint32 s=0; ssegmentListCount; s++) - { - PPCRecImlSegment_t* imlSegment = ppcImlGenContext->segmentList[s]; - for(sint32 i=0; iimlListCount; i++) - { - PPCRecImlInstruction_t* imlInstruction = imlSegment->imlList+i; - if( PPCRecompilerImlAnalyzer_canTypeWriteCR(imlInstruction) && imlInstruction->crRegister >= 0 && imlInstruction->crRegister <= 7 ) - { - uint32 crBitFlags = 0xF<<((uint32)imlInstruction->crRegister*4); - uint32 crOverwriteMask = PPCRecompiler_getCROverwriteMask(ppcImlGenContext, imlSegment); - uint32 crIgnoreMask = crOverwriteMask & ~imlSegment->crBitsRead; - imlInstruction->crIgnoreMask = crIgnoreMask; - } - } - } -} - -bool PPCRecompiler_checkIfGPRIsModifiedInRange(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* imlSegment, sint32 startIndex, sint32 endIndex, sint32 vreg) -{ - PPCImlOptimizerUsedRegisters_t registersUsed; - for (sint32 i = startIndex; i <= endIndex; i++) - { - PPCRecImlInstruction_t* imlInstruction = imlSegment->imlList + i; - PPCRecompiler_checkRegisterUsage(ppcImlGenContext, imlInstruction, ®istersUsed); - if (registersUsed.writtenNamedReg1 == vreg) - return true; - } - return false; -} - -sint32 PPCRecompiler_scanBackwardsForReusableRegister(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* startSegment, sint32 startIndex, sint32 name) -{ - // current segment - sint32 currentIndex = startIndex; - PPCRecImlSegment_t* currentSegment = startSegment; - sint32 segmentIterateCount = 0; - sint32 foundRegister = -1; - while (true) - { - // stop scanning if segment is enterable - if (currentSegment->isEnterable) - return -1; - while (currentIndex >= 0) - { - if (currentSegment->imlList[currentIndex].type == PPCREC_IML_TYPE_NAME_R && currentSegment->imlList[currentIndex].op_r_name.name == name) - { - foundRegister = currentSegment->imlList[currentIndex].op_r_name.registerIndex; - break; - } - // previous instruction - currentIndex--; - } - if (foundRegister >= 0) - break; - // continue at previous segment (if there is only one) - if (segmentIterateCount >= 1) - return -1; - if (currentSegment->list_prevSegments.size() != 1) - return -1; - currentSegment = currentSegment->list_prevSegments[0]; - currentIndex = currentSegment->imlListCount - 1; - segmentIterateCount++; - } - // scan again to make sure the register is not modified inbetween - currentIndex = startIndex; - currentSegment = startSegment; - segmentIterateCount = 0; - PPCImlOptimizerUsedRegisters_t registersUsed; - while (true) - { - while (currentIndex >= 0) - { - // check if register is modified - PPCRecompiler_checkRegisterUsage(ppcImlGenContext, currentSegment->imlList+currentIndex, ®istersUsed); - if (registersUsed.writtenNamedReg1 == foundRegister) - return -1; - // check if end of scan reached - if (currentSegment->imlList[currentIndex].type == PPCREC_IML_TYPE_NAME_R && currentSegment->imlList[currentIndex].op_r_name.name == name) - { - //foundRegister = currentSegment->imlList[currentIndex].op_r_name.registerIndex; - return foundRegister; - } - // previous instruction - currentIndex--; - } - // continue at previous segment (if there is only one) - if (segmentIterateCount >= 1) - return -1; - if (currentSegment->list_prevSegments.size() != 1) - return -1; - currentSegment = currentSegment->list_prevSegments[0]; - currentIndex = currentSegment->imlListCount - 1; - segmentIterateCount++; - } - return -1; -} - -void PPCRecompiler_optimizeDirectFloatCopiesScanForward(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* imlSegment, sint32 imlIndexLoad, sint32 fprIndex) -{ - PPCRecImlInstruction_t* imlInstructionLoad = imlSegment->imlList + imlIndexLoad; - if (imlInstructionLoad->op_storeLoad.flags2.notExpanded) - return; - - PPCImlOptimizerUsedRegisters_t registersUsed; - sint32 scanRangeEnd = std::min(imlIndexLoad + 25, imlSegment->imlListCount); // don't scan too far (saves performance and also the chances we can merge the load+store become low at high distances) - bool foundMatch = false; - sint32 lastStore = -1; - for (sint32 i = imlIndexLoad + 1; i < scanRangeEnd; i++) - { - PPCRecImlInstruction_t* imlInstruction = imlSegment->imlList + i; - if (PPCRecompiler_isSuffixInstruction(imlInstruction)) - { - break; - } - - // check if FPR is stored - if ((imlInstruction->type == PPCREC_IML_TYPE_FPR_STORE && imlInstruction->op_storeLoad.mode == PPCREC_FPR_ST_MODE_SINGLE_FROM_PS0) || - (imlInstruction->type == PPCREC_IML_TYPE_FPR_STORE_INDEXED && imlInstruction->op_storeLoad.mode == PPCREC_FPR_ST_MODE_SINGLE_FROM_PS0)) - { - if (imlInstruction->op_storeLoad.registerData == fprIndex) - { - if (foundMatch == false) - { - // flag the load-single instruction as "don't expand" (leave single value as-is) - imlInstructionLoad->op_storeLoad.flags2.notExpanded = true; - } - // also set the flag for the store instruction - PPCRecImlInstruction_t* imlInstructionStore = imlInstruction; - imlInstructionStore->op_storeLoad.flags2.notExpanded = true; - - foundMatch = true; - lastStore = i + 1; - - continue; - } - } - - // check if FPR is overwritten (we can actually ignore read operations?) - PPCRecompiler_checkRegisterUsage(ppcImlGenContext, imlInstruction, ®istersUsed); - if (registersUsed.writtenFPR1 == fprIndex) - break; - if (registersUsed.readFPR1 == fprIndex) - break; - if (registersUsed.readFPR2 == fprIndex) - break; - if (registersUsed.readFPR3 == fprIndex) - break; - if (registersUsed.readFPR4 == fprIndex) - break; - } - - if (foundMatch) - { - // insert expand instruction after store - PPCRecImlInstruction_t* newExpand = PPCRecompiler_insertInstruction(imlSegment, lastStore); - PPCRecompilerImlGen_generateNewInstruction_fpr_r(ppcImlGenContext, newExpand, PPCREC_IML_OP_FPR_EXPAND_BOTTOM32_TO_BOTTOM64_AND_TOP64, fprIndex); - } -} - -/* -* Scans for patterns: -* -* -* -* For these patterns the store and load is modified to work with un-extended values (float remains as float, no double conversion) -* The float->double extension is then executed later -* Advantages: -* Keeps denormals and other special float values intact -* Slightly improves performance -*/ -void PPCRecompiler_optimizeDirectFloatCopies(ppcImlGenContext_t* ppcImlGenContext) -{ - for (sint32 s = 0; s < ppcImlGenContext->segmentListCount; s++) - { - PPCRecImlSegment_t* imlSegment = ppcImlGenContext->segmentList[s]; - - for (sint32 i = 0; i < imlSegment->imlListCount; i++) - { - PPCRecImlInstruction_t* imlInstruction = imlSegment->imlList + i; - if (imlInstruction->type == PPCREC_IML_TYPE_FPR_LOAD && imlInstruction->op_storeLoad.mode == PPCREC_FPR_LD_MODE_SINGLE_INTO_PS0_PS1) - { - PPCRecompiler_optimizeDirectFloatCopiesScanForward(ppcImlGenContext, imlSegment, i, imlInstruction->op_storeLoad.registerData); - } - else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_LOAD_INDEXED && imlInstruction->op_storeLoad.mode == PPCREC_FPR_LD_MODE_SINGLE_INTO_PS0_PS1) - { - PPCRecompiler_optimizeDirectFloatCopiesScanForward(ppcImlGenContext, imlSegment, i, imlInstruction->op_storeLoad.registerData); - } - } - } -} - -void PPCRecompiler_optimizeDirectIntegerCopiesScanForward(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* imlSegment, sint32 imlIndexLoad, sint32 gprIndex) -{ - PPCRecImlInstruction_t* imlInstructionLoad = imlSegment->imlList + imlIndexLoad; - if ( imlInstructionLoad->op_storeLoad.flags2.swapEndian == false ) - return; - bool foundMatch = false; - PPCImlOptimizerUsedRegisters_t registersUsed; - sint32 scanRangeEnd = std::min(imlIndexLoad + 25, imlSegment->imlListCount); // don't scan too far (saves performance and also the chances we can merge the load+store become low at high distances) - sint32 i = imlIndexLoad + 1; - for (; i < scanRangeEnd; i++) - { - PPCRecImlInstruction_t* imlInstruction = imlSegment->imlList + i; - if (PPCRecompiler_isSuffixInstruction(imlInstruction)) - { - break; - } - // check if GPR is stored - if ((imlInstruction->type == PPCREC_IML_TYPE_STORE && imlInstruction->op_storeLoad.copyWidth == 32 ) ) - { - if (imlInstruction->op_storeLoad.registerMem == gprIndex) - break; - if (imlInstruction->op_storeLoad.registerData == gprIndex) - { - PPCRecImlInstruction_t* imlInstructionStore = imlInstruction; - if (foundMatch == false) - { - // switch the endian swap flag for the load instruction - imlInstructionLoad->op_storeLoad.flags2.swapEndian = !imlInstructionLoad->op_storeLoad.flags2.swapEndian; - foundMatch = true; - } - // switch the endian swap flag for the store instruction - imlInstructionStore->op_storeLoad.flags2.swapEndian = !imlInstructionStore->op_storeLoad.flags2.swapEndian; - // keep scanning - continue; - } - } - // check if GPR is accessed - PPCRecompiler_checkRegisterUsage(ppcImlGenContext, imlInstruction, ®istersUsed); - if (registersUsed.readNamedReg1 == gprIndex || - registersUsed.readNamedReg2 == gprIndex || - registersUsed.readNamedReg3 == gprIndex) - { - break; - } - if (registersUsed.writtenNamedReg1 == gprIndex) - return; // GPR overwritten, we don't need to byte swap anymore - } - if (foundMatch) - { - // insert expand instruction - PPCRecImlInstruction_t* newExpand = PPCRecompiler_insertInstruction(imlSegment, i); - PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, newExpand, PPCREC_IML_OP_ENDIAN_SWAP, gprIndex, gprIndex); - } -} - -/* -* Scans for patterns: -* -* -* -* For these patterns the store and load is modified to work with non-swapped values -* The big_endian->little_endian conversion is then executed later -* Advantages: -* Slightly improves performance -*/ -void PPCRecompiler_optimizeDirectIntegerCopies(ppcImlGenContext_t* ppcImlGenContext) -{ - for (sint32 s = 0; s < ppcImlGenContext->segmentListCount; s++) - { - PPCRecImlSegment_t* imlSegment = ppcImlGenContext->segmentList[s]; - - for (sint32 i = 0; i < imlSegment->imlListCount; i++) - { - PPCRecImlInstruction_t* imlInstruction = imlSegment->imlList + i; - if (imlInstruction->type == PPCREC_IML_TYPE_LOAD && imlInstruction->op_storeLoad.copyWidth == 32 && imlInstruction->op_storeLoad.flags2.swapEndian ) - { - PPCRecompiler_optimizeDirectIntegerCopiesScanForward(ppcImlGenContext, imlSegment, i, imlInstruction->op_storeLoad.registerData); - } - } - } -} - -sint32 _getGQRIndexFromRegister(ppcImlGenContext_t* ppcImlGenContext, sint32 registerIndex) -{ - if (registerIndex == PPC_REC_INVALID_REGISTER) - return -1; - sint32 namedReg = ppcImlGenContext->mappedRegister[registerIndex]; - if (namedReg >= (PPCREC_NAME_SPR0 + SPR_UGQR0) && namedReg <= (PPCREC_NAME_SPR0 + SPR_UGQR7)) - { - return namedReg - (PPCREC_NAME_SPR0 + SPR_UGQR0); - } - return -1; -} - -bool PPCRecompiler_isUGQRValueKnown(ppcImlGenContext_t* ppcImlGenContext, sint32 gqrIndex, uint32& gqrValue) -{ - // UGQR 2 to 7 are initialized by the OS and we assume that games won't ever permanently touch those - // todo - hack - replace with more accurate solution - if (gqrIndex == 2) - gqrValue = 0x00040004; - else if (gqrIndex == 3) - gqrValue = 0x00050005; - else if (gqrIndex == 4) - gqrValue = 0x00060006; - else if (gqrIndex == 5) - gqrValue = 0x00070007; - else - return false; - return true; -} - -/* - * If value of GQR can be predicted for a given PSQ load or store instruction then replace it with an optimized version - */ -void PPCRecompiler_optimizePSQLoadAndStore(ppcImlGenContext_t* ppcImlGenContext) -{ - for (sint32 s = 0; s < ppcImlGenContext->segmentListCount; s++) - { - PPCRecImlSegment_t* imlSegment = ppcImlGenContext->segmentList[s]; - for (sint32 i = 0; i < imlSegment->imlListCount; i++) - { - PPCRecImlInstruction_t* imlInstruction = imlSegment->imlList + i; - if (imlInstruction->type == PPCREC_IML_TYPE_FPR_LOAD || imlInstruction->type == PPCREC_IML_TYPE_FPR_LOAD_INDEXED) - { - if(imlInstruction->op_storeLoad.mode != PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0 && - imlInstruction->op_storeLoad.mode != PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0_PS1 ) - continue; - // get GQR value - cemu_assert_debug(imlInstruction->op_storeLoad.registerGQR != PPC_REC_INVALID_REGISTER); - sint32 gqrIndex = _getGQRIndexFromRegister(ppcImlGenContext, imlInstruction->op_storeLoad.registerGQR); - cemu_assert(gqrIndex >= 0); - if (ppcImlGenContext->tracking.modifiesGQR[gqrIndex]) - continue; - //uint32 gqrValue = ppcInterpreterCurrentInstance->sprNew.UGQR[gqrIndex]; - uint32 gqrValue; - if (!PPCRecompiler_isUGQRValueKnown(ppcImlGenContext, gqrIndex, gqrValue)) - continue; - - uint32 formatType = (gqrValue >> 16) & 7; - uint32 scale = (gqrValue >> 24) & 0x3F; - if (scale != 0) - continue; // only generic handler supports scale - if (imlInstruction->op_storeLoad.mode == PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0) - { - if (formatType == 0) - imlInstruction->op_storeLoad.mode = PPCREC_FPR_LD_MODE_PSQ_FLOAT_PS0; - else if (formatType == 4) - imlInstruction->op_storeLoad.mode = PPCREC_FPR_LD_MODE_PSQ_U8_PS0; - else if (formatType == 5) - imlInstruction->op_storeLoad.mode = PPCREC_FPR_LD_MODE_PSQ_U16_PS0; - else if (formatType == 6) - imlInstruction->op_storeLoad.mode = PPCREC_FPR_LD_MODE_PSQ_S8_PS0; - else if (formatType == 7) - imlInstruction->op_storeLoad.mode = PPCREC_FPR_LD_MODE_PSQ_S16_PS0; - } - else if (imlInstruction->op_storeLoad.mode == PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0_PS1) - { - if (formatType == 0) - imlInstruction->op_storeLoad.mode = PPCREC_FPR_LD_MODE_PSQ_FLOAT_PS0_PS1; - else if (formatType == 4) - imlInstruction->op_storeLoad.mode = PPCREC_FPR_LD_MODE_PSQ_U8_PS0_PS1; - else if (formatType == 5) - imlInstruction->op_storeLoad.mode = PPCREC_FPR_LD_MODE_PSQ_U16_PS0_PS1; - else if (formatType == 6) - imlInstruction->op_storeLoad.mode = PPCREC_FPR_LD_MODE_PSQ_S8_PS0_PS1; - else if (formatType == 7) - imlInstruction->op_storeLoad.mode = PPCREC_FPR_LD_MODE_PSQ_S16_PS0_PS1; - } - } - else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_STORE || imlInstruction->type == PPCREC_IML_TYPE_FPR_STORE_INDEXED) - { - if(imlInstruction->op_storeLoad.mode != PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0 && - imlInstruction->op_storeLoad.mode != PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0_PS1) - continue; - // get GQR value - cemu_assert_debug(imlInstruction->op_storeLoad.registerGQR != PPC_REC_INVALID_REGISTER); - sint32 gqrIndex = _getGQRIndexFromRegister(ppcImlGenContext, imlInstruction->op_storeLoad.registerGQR); - cemu_assert(gqrIndex >= 0); - if (ppcImlGenContext->tracking.modifiesGQR[gqrIndex]) - continue; - uint32 gqrValue; - if(!PPCRecompiler_isUGQRValueKnown(ppcImlGenContext, gqrIndex, gqrValue)) - continue; - uint32 formatType = (gqrValue >> 16) & 7; - uint32 scale = (gqrValue >> 24) & 0x3F; - if (scale != 0) - continue; // only generic handler supports scale - if (imlInstruction->op_storeLoad.mode == PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0) - { - if (formatType == 0) - imlInstruction->op_storeLoad.mode = PPCREC_FPR_ST_MODE_PSQ_FLOAT_PS0; - else if (formatType == 4) - imlInstruction->op_storeLoad.mode = PPCREC_FPR_ST_MODE_PSQ_U8_PS0; - else if (formatType == 5) - imlInstruction->op_storeLoad.mode = PPCREC_FPR_ST_MODE_PSQ_U16_PS0; - else if (formatType == 6) - imlInstruction->op_storeLoad.mode = PPCREC_FPR_ST_MODE_PSQ_S8_PS0; - else if (formatType == 7) - imlInstruction->op_storeLoad.mode = PPCREC_FPR_ST_MODE_PSQ_S16_PS0; - } - else if (imlInstruction->op_storeLoad.mode == PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0_PS1) - { - if (formatType == 0) - imlInstruction->op_storeLoad.mode = PPCREC_FPR_ST_MODE_PSQ_FLOAT_PS0_PS1; - else if (formatType == 4) - imlInstruction->op_storeLoad.mode = PPCREC_FPR_ST_MODE_PSQ_U8_PS0_PS1; - else if (formatType == 5) - imlInstruction->op_storeLoad.mode = PPCREC_FPR_ST_MODE_PSQ_U16_PS0_PS1; - else if (formatType == 6) - imlInstruction->op_storeLoad.mode = PPCREC_FPR_ST_MODE_PSQ_S8_PS0_PS1; - else if (formatType == 7) - imlInstruction->op_storeLoad.mode = PPCREC_FPR_ST_MODE_PSQ_S16_PS0_PS1; - } - } - } - } -} - -/* - * Returns true if registerWrite overwrites any of the registers read by registerRead - */ -bool PPCRecompilerAnalyzer_checkForGPROverwrite(PPCImlOptimizerUsedRegisters_t* registerRead, PPCImlOptimizerUsedRegisters_t* registerWrite) -{ - if (registerWrite->writtenNamedReg1 < 0) - return false; - - if (registerWrite->writtenNamedReg1 == registerRead->readNamedReg1) - return true; - if (registerWrite->writtenNamedReg1 == registerRead->readNamedReg2) - return true; - if (registerWrite->writtenNamedReg1 == registerRead->readNamedReg3) - return true; - return false; -} - -void _reorderConditionModifyInstructions(PPCRecImlSegment_t* imlSegment) -{ - PPCRecImlInstruction_t* lastInstruction = PPCRecompilerIML_getLastInstruction(imlSegment); - // last instruction a conditional branch? - if (lastInstruction == nullptr || lastInstruction->type != PPCREC_IML_TYPE_CJUMP) - return; - if (lastInstruction->op_conditionalJump.crRegisterIndex >= 8) - return; - // get CR bitmask of bit required for conditional jump - PPCRecCRTracking_t crTracking; - PPCRecompilerImlAnalyzer_getCRTracking(lastInstruction, &crTracking); - uint32 requiredCRBits = crTracking.readCRBits; - - // scan backwards until we find the instruction that sets the CR - sint32 crSetterInstructionIndex = -1; - sint32 unsafeInstructionIndex = -1; - for (sint32 i = imlSegment->imlListCount-2; i >= 0; i--) - { - PPCRecImlInstruction_t* imlInstruction = imlSegment->imlList + i; - PPCRecompilerImlAnalyzer_getCRTracking(imlInstruction, &crTracking); - if (crTracking.readCRBits != 0) - return; // dont handle complex cases for now - if (crTracking.writtenCRBits != 0) - { - if ((crTracking.writtenCRBits&requiredCRBits) != 0) - { - crSetterInstructionIndex = i; - break; - } - else - { - return; // other CR bits overwritten (dont handle complex cases) - } - } - // is safe? (no risk of overwriting x64 eflags) - if ((imlInstruction->type == PPCREC_IML_TYPE_NAME_R || imlInstruction->type == PPCREC_IML_TYPE_R_NAME || imlInstruction->type == PPCREC_IML_TYPE_NO_OP) || - (imlInstruction->type == PPCREC_IML_TYPE_FPR_NAME_R || imlInstruction->type == PPCREC_IML_TYPE_FPR_R_NAME) || - (imlInstruction->type == PPCREC_IML_TYPE_R_S32 && (imlInstruction->operation == PPCREC_IML_OP_ASSIGN)) || - (imlInstruction->type == PPCREC_IML_TYPE_R_R && (imlInstruction->operation == PPCREC_IML_OP_ASSIGN)) ) - continue; - // not safe - //hasUnsafeInstructions = true; - if (unsafeInstructionIndex == -1) - unsafeInstructionIndex = i; - } - if (crSetterInstructionIndex < 0) - return; - if (unsafeInstructionIndex < 0) - return; // no danger of overwriting eflags, don't reorder - // check if we can move the CR setter instruction to after unsafeInstructionIndex - PPCRecCRTracking_t crTrackingSetter = crTracking; - PPCImlOptimizerUsedRegisters_t regTrackingCRSetter; - PPCRecompiler_checkRegisterUsage(NULL, imlSegment->imlList+crSetterInstructionIndex, ®TrackingCRSetter); - if (regTrackingCRSetter.writtenFPR1 >= 0 || regTrackingCRSetter.readFPR1 >= 0 || regTrackingCRSetter.readFPR2 >= 0 || regTrackingCRSetter.readFPR3 >= 0 || regTrackingCRSetter.readFPR4 >= 0) - return; // we don't handle FPR dependency yet so just ignore FPR instructions - PPCImlOptimizerUsedRegisters_t registerTracking; - if (regTrackingCRSetter.writtenNamedReg1 >= 0) - { - // CR setter does write GPR - for (sint32 i = crSetterInstructionIndex + 1; i <= unsafeInstructionIndex; i++) - { - PPCRecompiler_checkRegisterUsage(NULL, imlSegment->imlList + i, ®isterTracking); - // reads register written by CR setter? - if (PPCRecompilerAnalyzer_checkForGPROverwrite(®isterTracking, ®TrackingCRSetter)) - { - return; // cant move CR setter because of dependency - } - // writes register read by CR setter? - if (PPCRecompilerAnalyzer_checkForGPROverwrite(®TrackingCRSetter, ®isterTracking)) - { - return; // cant move CR setter because of dependency - } - // overwrites register written by CR setter? - if (regTrackingCRSetter.writtenNamedReg1 == registerTracking.writtenNamedReg1) - return; - } - } - else - { - // CR setter does not write GPR - for (sint32 i = crSetterInstructionIndex + 1; i <= unsafeInstructionIndex; i++) - { - PPCRecompiler_checkRegisterUsage(NULL, imlSegment->imlList + i, ®isterTracking); - // writes register read by CR setter? - if (PPCRecompilerAnalyzer_checkForGPROverwrite(®TrackingCRSetter, ®isterTracking)) - { - return; // cant move CR setter because of dependency - } - } - } - - // move CR setter instruction -#ifdef CEMU_DEBUG_ASSERT - if ((unsafeInstructionIndex + 1) <= crSetterInstructionIndex) - assert_dbg(); -#endif - PPCRecImlInstruction_t* newCRSetterInstruction = PPCRecompiler_insertInstruction(imlSegment, unsafeInstructionIndex+1); - memcpy(newCRSetterInstruction, imlSegment->imlList + crSetterInstructionIndex, sizeof(PPCRecImlInstruction_t)); - PPCRecompilerImlGen_generateNewInstruction_noOp(NULL, imlSegment->imlList + crSetterInstructionIndex); -} - -/* - * Move instructions which update the condition flags closer to the instruction that consumes them - * On x64 this improves performance since we often can avoid storing CR in memory - */ -void PPCRecompiler_reorderConditionModifyInstructions(ppcImlGenContext_t* ppcImlGenContext) -{ - // check if this segment has a conditional branch - for (sint32 s = 0; s < ppcImlGenContext->segmentListCount; s++) - { - PPCRecImlSegment_t* imlSegment = ppcImlGenContext->segmentList[s]; - _reorderConditionModifyInstructions(imlSegment); - } -} diff --git a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlRegisterAllocator.cpp b/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlRegisterAllocator.cpp deleted file mode 100644 index 88d387e6..00000000 --- a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlRegisterAllocator.cpp +++ /dev/null @@ -1,1012 +0,0 @@ -#include "PPCRecompiler.h" -#include "PPCRecompilerIml.h" -#include "PPCRecompilerX64.h" -#include "PPCRecompilerImlRanges.h" - -void PPCRecompiler_replaceGPRRegisterUsageMultiple(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction, sint32 gprRegisterSearched[4], sint32 gprRegisterReplaced[4]); - -bool PPCRecompiler_isSuffixInstruction(PPCRecImlInstruction_t* iml); - -uint32 recRACurrentIterationIndex = 0; - -uint32 PPCRecRA_getNextIterationIndex() -{ - recRACurrentIterationIndex++; - return recRACurrentIterationIndex; -} - -bool _detectLoop(PPCRecImlSegment_t* currentSegment, sint32 depth, uint32 iterationIndex, PPCRecImlSegment_t* imlSegmentLoopBase) -{ - if (currentSegment == imlSegmentLoopBase) - return true; - if (currentSegment->raInfo.lastIterationIndex == iterationIndex) - return currentSegment->raInfo.isPartOfProcessedLoop; - if (depth >= 9) - return false; - currentSegment->raInfo.lastIterationIndex = iterationIndex; - currentSegment->raInfo.isPartOfProcessedLoop = false; - - if (currentSegment->nextSegmentIsUncertain) - return false; - if (currentSegment->nextSegmentBranchNotTaken) - { - if (currentSegment->nextSegmentBranchNotTaken->momentaryIndex > currentSegment->momentaryIndex) - { - currentSegment->raInfo.isPartOfProcessedLoop = _detectLoop(currentSegment->nextSegmentBranchNotTaken, depth + 1, iterationIndex, imlSegmentLoopBase); - } - } - if (currentSegment->nextSegmentBranchTaken) - { - if (currentSegment->nextSegmentBranchTaken->momentaryIndex > currentSegment->momentaryIndex) - { - currentSegment->raInfo.isPartOfProcessedLoop = _detectLoop(currentSegment->nextSegmentBranchTaken, depth + 1, iterationIndex, imlSegmentLoopBase); - } - } - if (currentSegment->raInfo.isPartOfProcessedLoop) - currentSegment->loopDepth++; - return currentSegment->raInfo.isPartOfProcessedLoop; -} - -void PPCRecRA_detectLoop(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* imlSegmentLoopBase) -{ - uint32 iterationIndex = PPCRecRA_getNextIterationIndex(); - imlSegmentLoopBase->raInfo.lastIterationIndex = iterationIndex; - if (_detectLoop(imlSegmentLoopBase->nextSegmentBranchTaken, 0, iterationIndex, imlSegmentLoopBase)) - { - imlSegmentLoopBase->loopDepth++; - } -} - -void PPCRecRA_identifyLoop(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* imlSegment) -{ - if (imlSegment->nextSegmentIsUncertain) - return; - // check if this segment has a branch that links to itself (tight loop) - if (imlSegment->nextSegmentBranchTaken == imlSegment) - { - // segment loops over itself - imlSegment->loopDepth++; - return; - } - // check if this segment has a branch that goes backwards (potential complex loop) - if (imlSegment->nextSegmentBranchTaken && imlSegment->nextSegmentBranchTaken->momentaryIndex < imlSegment->momentaryIndex) - { - PPCRecRA_detectLoop(ppcImlGenContext, imlSegment); - } -} - -typedef struct -{ - sint32 name; - sint32 virtualRegister; - sint32 physicalRegister; - bool isDirty; -}raRegisterState_t; - -const sint32 _raInfo_physicalGPRCount = PPC_X64_GPR_USABLE_REGISTERS; - -raRegisterState_t* PPCRecRA_getRegisterState(raRegisterState_t* regState, sint32 virtualRegister) -{ - for (sint32 i = 0; i < _raInfo_physicalGPRCount; i++) - { - if (regState[i].virtualRegister == virtualRegister) - { -#ifdef CEMU_DEBUG_ASSERT - if (regState[i].physicalRegister < 0) - assert_dbg(); -#endif - return regState + i; - } - } - return nullptr; -} - -raRegisterState_t* PPCRecRA_getFreePhysicalRegister(raRegisterState_t* regState) -{ - for (sint32 i = 0; i < _raInfo_physicalGPRCount; i++) - { - if (regState[i].physicalRegister < 0) - { - regState[i].physicalRegister = i; - return regState + i; - } - } - return nullptr; -} - -typedef struct -{ - uint16 registerIndex; - uint16 registerName; -}raLoadStoreInfo_t; - -void PPCRecRA_insertGPRLoadInstruction(PPCRecImlSegment_t* imlSegment, sint32 insertIndex, sint32 registerIndex, sint32 registerName) -{ - PPCRecompiler_pushBackIMLInstructions(imlSegment, insertIndex, 1); - PPCRecImlInstruction_t* imlInstructionItr = imlSegment->imlList + (insertIndex + 0); - memset(imlInstructionItr, 0x00, sizeof(PPCRecImlInstruction_t)); - imlInstructionItr->type = PPCREC_IML_TYPE_R_NAME; - imlInstructionItr->operation = PPCREC_IML_OP_ASSIGN; - imlInstructionItr->op_r_name.registerIndex = registerIndex; - imlInstructionItr->op_r_name.name = registerName; - imlInstructionItr->op_r_name.copyWidth = 32; - imlInstructionItr->op_r_name.flags = 0; -} - -void PPCRecRA_insertGPRLoadInstructions(PPCRecImlSegment_t* imlSegment, sint32 insertIndex, raLoadStoreInfo_t* loadList, sint32 loadCount) -{ - PPCRecompiler_pushBackIMLInstructions(imlSegment, insertIndex, loadCount); - memset(imlSegment->imlList + (insertIndex + 0), 0x00, sizeof(PPCRecImlInstruction_t)*loadCount); - for (sint32 i = 0; i < loadCount; i++) - { - PPCRecImlInstruction_t* imlInstructionItr = imlSegment->imlList + (insertIndex + i); - imlInstructionItr->type = PPCREC_IML_TYPE_R_NAME; - imlInstructionItr->operation = PPCREC_IML_OP_ASSIGN; - imlInstructionItr->op_r_name.registerIndex = (uint8)loadList[i].registerIndex; - imlInstructionItr->op_r_name.name = (uint32)loadList[i].registerName; - imlInstructionItr->op_r_name.copyWidth = 32; - imlInstructionItr->op_r_name.flags = 0; - } -} - -void PPCRecRA_insertGPRStoreInstruction(PPCRecImlSegment_t* imlSegment, sint32 insertIndex, sint32 registerIndex, sint32 registerName) -{ - PPCRecompiler_pushBackIMLInstructions(imlSegment, insertIndex, 1); - PPCRecImlInstruction_t* imlInstructionItr = imlSegment->imlList + (insertIndex + 0); - memset(imlInstructionItr, 0x00, sizeof(PPCRecImlInstruction_t)); - imlInstructionItr->type = PPCREC_IML_TYPE_NAME_R; - imlInstructionItr->operation = PPCREC_IML_OP_ASSIGN; - imlInstructionItr->op_r_name.registerIndex = registerIndex; - imlInstructionItr->op_r_name.name = registerName; - imlInstructionItr->op_r_name.copyWidth = 32; - imlInstructionItr->op_r_name.flags = 0; -} - -void PPCRecRA_insertGPRStoreInstructions(PPCRecImlSegment_t* imlSegment, sint32 insertIndex, raLoadStoreInfo_t* storeList, sint32 storeCount) -{ - PPCRecompiler_pushBackIMLInstructions(imlSegment, insertIndex, storeCount); - memset(imlSegment->imlList + (insertIndex + 0), 0x00, sizeof(PPCRecImlInstruction_t)*storeCount); - for (sint32 i = 0; i < storeCount; i++) - { - PPCRecImlInstruction_t* imlInstructionItr = imlSegment->imlList + (insertIndex + i); - memset(imlInstructionItr, 0x00, sizeof(PPCRecImlInstruction_t)); - imlInstructionItr->type = PPCREC_IML_TYPE_NAME_R; - imlInstructionItr->operation = PPCREC_IML_OP_ASSIGN; - imlInstructionItr->op_r_name.registerIndex = (uint8)storeList[i].registerIndex; - imlInstructionItr->op_r_name.name = (uint32)storeList[i].registerName; - imlInstructionItr->op_r_name.copyWidth = 32; - imlInstructionItr->op_r_name.flags = 0; - } -} - -#define SUBRANGE_LIST_SIZE (128) - -sint32 PPCRecRA_countInstructionsUntilNextUse(raLivenessSubrange_t* subrange, sint32 startIndex) -{ - for (sint32 i = 0; i < subrange->list_locations.size(); i++) - { - if (subrange->list_locations.data()[i].index >= startIndex) - return subrange->list_locations.data()[i].index - startIndex; - } - return INT_MAX; -} - -// count how many instructions there are until physRegister is used by any subrange (returns 0 if register is in use at startIndex, and INT_MAX if not used for the remainder of the segment) -sint32 PPCRecRA_countInstructionsUntilNextLocalPhysRegisterUse(PPCRecImlSegment_t* imlSegment, sint32 startIndex, sint32 physRegister) -{ - sint32 minDistance = INT_MAX; - // next - raLivenessSubrange_t* subrangeItr = imlSegment->raInfo.linkedList_allSubranges; - while(subrangeItr) - { - if (subrangeItr->range->physicalRegister != physRegister) - { - subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; - continue; - } - if (startIndex >= subrangeItr->start.index && startIndex < subrangeItr->end.index) - return 0; - if (subrangeItr->start.index >= startIndex) - { - minDistance = std::min(minDistance, (subrangeItr->start.index - startIndex)); - } - subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; - } - return minDistance; -} - -typedef struct -{ - raLivenessSubrange_t* liveRangeList[64]; - sint32 liveRangesCount; -}raLiveRangeInfo_t; - -// return a bitmask that contains only registers that are not used by any colliding range -uint32 PPCRecRA_getAllowedRegisterMaskForFullRange(raLivenessRange_t* range) -{ - uint32 physRegisterMask = (1 << PPC_X64_GPR_USABLE_REGISTERS) - 1; - for (auto& subrange : range->list_subranges) - { - PPCRecImlSegment_t* imlSegment = subrange->imlSegment; - raLivenessSubrange_t* subrangeItr = imlSegment->raInfo.linkedList_allSubranges; - while(subrangeItr) - { - if (subrange == subrangeItr) - { - // next - subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; - continue; - } - - if (subrange->start.index < subrangeItr->end.index && subrange->end.index > subrangeItr->start.index || - (subrange->start.index == RA_INTER_RANGE_START && subrange->start.index == subrangeItr->start.index) || - (subrange->end.index == RA_INTER_RANGE_END && subrange->end.index == subrangeItr->end.index) ) - { - if(subrangeItr->range->physicalRegister >= 0) - physRegisterMask &= ~(1<<(subrangeItr->range->physicalRegister)); - } - // next - subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; - } - } - return physRegisterMask; -} - -bool _livenessRangeStartCompare(raLivenessSubrange_t* lhs, raLivenessSubrange_t* rhs) { return lhs->start.index < rhs->start.index; } - -void _sortSegmentAllSubrangesLinkedList(PPCRecImlSegment_t* imlSegment) -{ - raLivenessSubrange_t* subrangeList[4096+1]; - sint32 count = 0; - // disassemble linked list - raLivenessSubrange_t* subrangeItr = imlSegment->raInfo.linkedList_allSubranges; - while (subrangeItr) - { - if (count >= 4096) - assert_dbg(); - subrangeList[count] = subrangeItr; - count++; - // next - subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; - } - if (count == 0) - { - imlSegment->raInfo.linkedList_allSubranges = nullptr; - return; - } - // sort - std::sort(subrangeList, subrangeList + count, _livenessRangeStartCompare); - //for (sint32 i1 = 0; i1 < count; i1++) - //{ - // for (sint32 i2 = i1+1; i2 < count; i2++) - // { - // if (subrangeList[i1]->start.index > subrangeList[i2]->start.index) - // { - // // swap - // raLivenessSubrange_t* temp = subrangeList[i1]; - // subrangeList[i1] = subrangeList[i2]; - // subrangeList[i2] = temp; - // } - // } - //} - // reassemble linked list - subrangeList[count] = nullptr; - imlSegment->raInfo.linkedList_allSubranges = subrangeList[0]; - subrangeList[0]->link_segmentSubrangesGPR.prev = nullptr; - subrangeList[0]->link_segmentSubrangesGPR.next = subrangeList[1]; - for (sint32 i = 1; i < count; i++) - { - subrangeList[i]->link_segmentSubrangesGPR.prev = subrangeList[i - 1]; - subrangeList[i]->link_segmentSubrangesGPR.next = subrangeList[i + 1]; - } - // validate list -#ifdef CEMU_DEBUG_ASSERT - sint32 count2 = 0; - subrangeItr = imlSegment->raInfo.linkedList_allSubranges; - sint32 currentStartIndex = RA_INTER_RANGE_START; - while (subrangeItr) - { - count2++; - if (subrangeItr->start.index < currentStartIndex) - assert_dbg(); - currentStartIndex = subrangeItr->start.index; - // next - subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; - } - if (count != count2) - assert_dbg(); -#endif -} - -bool PPCRecRA_assignSegmentRegisters(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* imlSegment) -{ - - // sort subranges ascending by start index - - //std::sort(imlSegment->raInfo.list_subranges.begin(), imlSegment->raInfo.list_subranges.end(), _sortSubrangesByStartIndexDepr); - _sortSegmentAllSubrangesLinkedList(imlSegment); - - raLiveRangeInfo_t liveInfo; - liveInfo.liveRangesCount = 0; - //sint32 subrangeIndex = 0; - //for (auto& subrange : imlSegment->raInfo.list_subranges) - raLivenessSubrange_t* subrangeItr = imlSegment->raInfo.linkedList_allSubranges; - while(subrangeItr) - { - sint32 currentIndex = subrangeItr->start.index; - // validate subrange - PPCRecRA_debugValidateSubrange(subrangeItr); - // expire ranges - for (sint32 f = 0; f < liveInfo.liveRangesCount; f++) - { - raLivenessSubrange_t* liverange = liveInfo.liveRangeList[f]; - if (liverange->end.index <= currentIndex && liverange->end.index != RA_INTER_RANGE_END) - { -#ifdef CEMU_DEBUG_ASSERT - if (liverange->subrangeBranchTaken || liverange->subrangeBranchNotTaken) - assert_dbg(); // infinite subranges should not expire -#endif - // remove entry - liveInfo.liveRangesCount--; - liveInfo.liveRangeList[f] = liveInfo.liveRangeList[liveInfo.liveRangesCount]; - f--; - } - } - // check if subrange already has register assigned - if (subrangeItr->range->physicalRegister >= 0) - { - // verify if register is actually available -#ifdef CEMU_DEBUG_ASSERT - for (sint32 f = 0; f < liveInfo.liveRangesCount; f++) - { - raLivenessSubrange_t* liverangeItr = liveInfo.liveRangeList[f]; - if (liverangeItr->range->physicalRegister == subrangeItr->range->physicalRegister) - { - // this should never happen because we try to preventively avoid register conflicts - assert_dbg(); - } - } -#endif - // add to live ranges - liveInfo.liveRangeList[liveInfo.liveRangesCount] = subrangeItr; - liveInfo.liveRangesCount++; - // next - subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; - continue; - } - // find free register - uint32 physRegisterMask = (1<range->physicalRegister < 0) - assert_dbg(); - physRegisterMask &= ~(1<range->physicalRegister); - } - // check intersections with other ranges and determine allowed registers - uint32 allowedPhysRegisterMask = 0; - uint32 unusedRegisterMask = physRegisterMask; // mask of registers that are currently not used (does not include range checks) - if (physRegisterMask != 0) - { - allowedPhysRegisterMask = PPCRecRA_getAllowedRegisterMaskForFullRange(subrangeItr->range); - physRegisterMask &= allowedPhysRegisterMask; - } - if (physRegisterMask == 0) - { - struct - { - // estimated costs and chosen candidates for the different spill strategies - // hole cutting into a local range - struct - { - sint32 distance; - raLivenessSubrange_t* largestHoleSubrange; - sint32 cost; // additional cost of choosing this candidate - }localRangeHoleCutting; - // split current range (this is generally only a good choice when the current range is long but rarely used) - struct - { - sint32 cost; - sint32 physRegister; - sint32 distance; // size of hole - }availableRegisterHole; - // explode a inter-segment range (prefer ranges that are not read/written in this segment) - struct - { - raLivenessRange_t* range; - sint32 cost; - sint32 distance; // size of hole - // note: If we explode a range, we still have to check the size of the hole that becomes available, if too small then we need to add cost of splitting local subrange - }explodeRange; - // todo - add more strategies, make cost estimation smarter (for example, in some cases splitting can have reduced or no cost if read/store can be avoided due to data flow) - }spillStrategies; - // cant assign register - // there might be registers available, we just can't use them due to range conflicts - if (subrangeItr->end.index != RA_INTER_RANGE_END) - { - // range ends in current segment - - // Current algo looks like this: - // 1) Get the size of the largest possible hole that we can cut into any of the live local subranges - // 1.1) Check if the hole is large enough to hold the current subrange - // 2) If yes, cut hole and return false (full retry) - // 3) If no, try to reuse free register (need to determine how large the region is we can use) - // 4) If there is no free register or the range is extremely short go back to step 1+2 but additionally split the current subrange at where the hole ends - - cemu_assert_debug(currentIndex == subrangeItr->start.index); - - sint32 requiredSize = subrangeItr->end.index - subrangeItr->start.index; - // evaluate strategy: Cut hole into local subrange - spillStrategies.localRangeHoleCutting.distance = -1; - spillStrategies.localRangeHoleCutting.largestHoleSubrange = nullptr; - spillStrategies.localRangeHoleCutting.cost = INT_MAX; - if (currentIndex >= 0) - { - for (sint32 f = 0; f < liveInfo.liveRangesCount; f++) - { - raLivenessSubrange_t* candidate = liveInfo.liveRangeList[f]; - if (candidate->end.index == RA_INTER_RANGE_END) - continue; - sint32 distance = PPCRecRA_countInstructionsUntilNextUse(candidate, currentIndex); - if (distance < 2) - continue; // not even worth the consideration - // calculate split cost of candidate - sint32 cost = PPCRecRARange_estimateAdditionalCostAfterSplit(candidate, currentIndex + distance); - // calculate additional split cost of currentRange if hole is not large enough - if (distance < requiredSize) - { - cost += PPCRecRARange_estimateAdditionalCostAfterSplit(subrangeItr, currentIndex + distance); - // we also slightly increase cost in relation to the remaining length (in order to make the algorithm prefer larger holes) - cost += (requiredSize - distance) / 10; - } - // compare cost with previous candidates - if (cost < spillStrategies.localRangeHoleCutting.cost) - { - spillStrategies.localRangeHoleCutting.cost = cost; - spillStrategies.localRangeHoleCutting.distance = distance; - spillStrategies.localRangeHoleCutting.largestHoleSubrange = candidate; - } - } - } - // evaluate strategy: Split current range to fit in available holes - spillStrategies.availableRegisterHole.cost = INT_MAX; - spillStrategies.availableRegisterHole.distance = -1; - spillStrategies.availableRegisterHole.physRegister = -1; - if (currentIndex >= 0) - { - if (unusedRegisterMask != 0) - { - for (sint32 t = 0; t < PPC_X64_GPR_USABLE_REGISTERS; t++) - { - if ((unusedRegisterMask&(1 << t)) == 0) - continue; - // get size of potential hole for this register - sint32 distance = PPCRecRA_countInstructionsUntilNextLocalPhysRegisterUse(imlSegment, currentIndex, t); - if (distance < 2) - continue; // not worth consideration - // calculate additional cost due to split - if (distance >= requiredSize) - assert_dbg(); // should not happen or else we would have selected this register - sint32 cost = PPCRecRARange_estimateAdditionalCostAfterSplit(subrangeItr, currentIndex + distance); - // add small additional cost for the remaining range (prefer larger holes) - cost += (requiredSize - distance) / 10; - if (cost < spillStrategies.availableRegisterHole.cost) - { - spillStrategies.availableRegisterHole.cost = cost; - spillStrategies.availableRegisterHole.distance = distance; - spillStrategies.availableRegisterHole.physRegister = t; - } - } - } - } - // evaluate strategy: Explode inter-segment ranges - spillStrategies.explodeRange.cost = INT_MAX; - spillStrategies.explodeRange.range = nullptr; - spillStrategies.explodeRange.distance = -1; - for (sint32 f = 0; f < liveInfo.liveRangesCount; f++) - { - raLivenessSubrange_t* candidate = liveInfo.liveRangeList[f]; - if (candidate->end.index != RA_INTER_RANGE_END) - continue; - sint32 distance = PPCRecRA_countInstructionsUntilNextUse(liveInfo.liveRangeList[f], currentIndex); - if( distance < 2) - continue; - sint32 cost; - cost = PPCRecRARange_estimateAdditionalCostAfterRangeExplode(candidate->range); - // if the hole is not large enough, add cost of splitting current subrange - if (distance < requiredSize) - { - cost += PPCRecRARange_estimateAdditionalCostAfterSplit(subrangeItr, currentIndex + distance); - // add small additional cost for the remaining range (prefer larger holes) - cost += (requiredSize - distance) / 10; - } - // compare with current best candidate for this strategy - if (cost < spillStrategies.explodeRange.cost) - { - spillStrategies.explodeRange.cost = cost; - spillStrategies.explodeRange.distance = distance; - spillStrategies.explodeRange.range = candidate->range; - } - } - // choose strategy - if (spillStrategies.explodeRange.cost != INT_MAX && spillStrategies.explodeRange.cost <= spillStrategies.localRangeHoleCutting.cost && spillStrategies.explodeRange.cost <= spillStrategies.availableRegisterHole.cost) - { - // explode range - PPCRecRA_explodeRange(ppcImlGenContext, spillStrategies.explodeRange.range); - // split current subrange if necessary - if( requiredSize > spillStrategies.explodeRange.distance) - PPCRecRA_splitLocalSubrange(ppcImlGenContext, subrangeItr, currentIndex+spillStrategies.explodeRange.distance, true); - } - else if (spillStrategies.availableRegisterHole.cost != INT_MAX && spillStrategies.availableRegisterHole.cost <= spillStrategies.explodeRange.cost && spillStrategies.availableRegisterHole.cost <= spillStrategies.localRangeHoleCutting.cost) - { - // use available register - PPCRecRA_splitLocalSubrange(ppcImlGenContext, subrangeItr, currentIndex + spillStrategies.availableRegisterHole.distance, true); - } - else if (spillStrategies.localRangeHoleCutting.cost != INT_MAX && spillStrategies.localRangeHoleCutting.cost <= spillStrategies.explodeRange.cost && spillStrategies.localRangeHoleCutting.cost <= spillStrategies.availableRegisterHole.cost) - { - // cut hole - PPCRecRA_splitLocalSubrange(ppcImlGenContext, spillStrategies.localRangeHoleCutting.largestHoleSubrange, currentIndex + spillStrategies.localRangeHoleCutting.distance, true); - // split current subrange if necessary - if (requiredSize > spillStrategies.localRangeHoleCutting.distance) - PPCRecRA_splitLocalSubrange(ppcImlGenContext, subrangeItr, currentIndex + spillStrategies.localRangeHoleCutting.distance, true); - } - else if (subrangeItr->start.index == RA_INTER_RANGE_START) - { - // alternative strategy if we have no other choice: explode current range - PPCRecRA_explodeRange(ppcImlGenContext, subrangeItr->range); - } - else - assert_dbg(); - - return false; - } - else - { - // range exceeds segment border - // simple but bad solution -> explode the entire range (no longer allow it to cross segment boundaries) - // better solutions: 1) Depending on the situation, we can explode other ranges to resolve the conflict. Thus we should explode the range with the lowest extra cost - // 2) Or we explode the range only partially - // explode the range with the least cost - spillStrategies.explodeRange.cost = INT_MAX; - spillStrategies.explodeRange.range = nullptr; - spillStrategies.explodeRange.distance = -1; - for (sint32 f = 0; f < liveInfo.liveRangesCount; f++) - { - raLivenessSubrange_t* candidate = liveInfo.liveRangeList[f]; - if (candidate->end.index != RA_INTER_RANGE_END) - continue; - // only select candidates that clash with current subrange - if (candidate->range->physicalRegister < 0 && candidate != subrangeItr) - continue; - - sint32 cost; - cost = PPCRecRARange_estimateAdditionalCostAfterRangeExplode(candidate->range); - // compare with current best candidate for this strategy - if (cost < spillStrategies.explodeRange.cost) - { - spillStrategies.explodeRange.cost = cost; - spillStrategies.explodeRange.distance = INT_MAX; - spillStrategies.explodeRange.range = candidate->range; - } - } - // add current range as a candidate too - sint32 ownCost; - ownCost = PPCRecRARange_estimateAdditionalCostAfterRangeExplode(subrangeItr->range); - if (ownCost < spillStrategies.explodeRange.cost) - { - spillStrategies.explodeRange.cost = ownCost; - spillStrategies.explodeRange.distance = INT_MAX; - spillStrategies.explodeRange.range = subrangeItr->range; - } - if (spillStrategies.explodeRange.cost == INT_MAX) - assert_dbg(); // should not happen - PPCRecRA_explodeRange(ppcImlGenContext, spillStrategies.explodeRange.range); - } - return false; - } - // assign register to range - sint32 registerIndex = -1; - for (sint32 f = 0; f < PPC_X64_GPR_USABLE_REGISTERS; f++) - { - if ((physRegisterMask&(1 << f)) != 0) - { - registerIndex = f; - break; - } - } - subrangeItr->range->physicalRegister = registerIndex; - // add to live ranges - liveInfo.liveRangeList[liveInfo.liveRangesCount] = subrangeItr; - liveInfo.liveRangesCount++; - // next - subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; - } - return true; -} - -void PPCRecRA_assignRegisters(ppcImlGenContext_t* ppcImlGenContext) -{ - // start with frequently executed segments first - sint32 maxLoopDepth = 0; - for (sint32 i = 0; i < ppcImlGenContext->segmentListCount; i++) - { - maxLoopDepth = std::max(maxLoopDepth, ppcImlGenContext->segmentList[i]->loopDepth); - } - while (true) - { - bool done = false; - for (sint32 d = maxLoopDepth; d >= 0; d--) - { - for (sint32 i = 0; i < ppcImlGenContext->segmentListCount; i++) - { - PPCRecImlSegment_t* imlSegment = ppcImlGenContext->segmentList[i]; - if (imlSegment->loopDepth != d) - continue; - done = PPCRecRA_assignSegmentRegisters(ppcImlGenContext, imlSegment); - if (done == false) - break; - } - if (done == false) - break; - } - if (done) - break; - } -} - -typedef struct -{ - raLivenessSubrange_t* subrangeList[SUBRANGE_LIST_SIZE]; - sint32 subrangeCount; - bool hasUndefinedEndings; -}subrangeEndingInfo_t; - -void _findSubrangeWriteEndings(raLivenessSubrange_t* subrange, uint32 iterationIndex, sint32 depth, subrangeEndingInfo_t* info) -{ - if (depth >= 30) - { - info->hasUndefinedEndings = true; - return; - } - if (subrange->lastIterationIndex == iterationIndex) - return; // already processed - subrange->lastIterationIndex = iterationIndex; - if (subrange->hasStoreDelayed) - return; // no need to traverse this subrange - PPCRecImlSegment_t* imlSegment = subrange->imlSegment; - if (subrange->end.index != RA_INTER_RANGE_END) - { - // ending segment - if (info->subrangeCount >= SUBRANGE_LIST_SIZE) - { - info->hasUndefinedEndings = true; - return; - } - else - { - info->subrangeList[info->subrangeCount] = subrange; - info->subrangeCount++; - } - return; - } - - // traverse next subranges in flow - if (imlSegment->nextSegmentBranchNotTaken) - { - if (subrange->subrangeBranchNotTaken == nullptr) - { - info->hasUndefinedEndings = true; - } - else - { - _findSubrangeWriteEndings(subrange->subrangeBranchNotTaken, iterationIndex, depth + 1, info); - } - } - if (imlSegment->nextSegmentBranchTaken) - { - if (subrange->subrangeBranchTaken == nullptr) - { - info->hasUndefinedEndings = true; - } - else - { - _findSubrangeWriteEndings(subrange->subrangeBranchTaken, iterationIndex, depth + 1, info); - } - } -} - -void _analyzeRangeDataFlow(raLivenessSubrange_t* subrange) -{ - if (subrange->end.index != RA_INTER_RANGE_END) - return; - // analyze data flow across segments (if this segment has writes) - if (subrange->hasStore) - { - subrangeEndingInfo_t writeEndingInfo; - writeEndingInfo.subrangeCount = 0; - writeEndingInfo.hasUndefinedEndings = false; - _findSubrangeWriteEndings(subrange, PPCRecRA_getNextIterationIndex(), 0, &writeEndingInfo); - if (writeEndingInfo.hasUndefinedEndings == false) - { - // get cost of delaying store into endings - sint32 delayStoreCost = 0; - bool alreadyStoredInAllEndings = true; - for (sint32 i = 0; i < writeEndingInfo.subrangeCount; i++) - { - raLivenessSubrange_t* subrangeItr = writeEndingInfo.subrangeList[i]; - if( subrangeItr->hasStore ) - continue; // this ending already stores, no extra cost - alreadyStoredInAllEndings = false; - sint32 storeCost = PPCRecRARange_getReadWriteCost(subrangeItr->imlSegment); - delayStoreCost = std::max(storeCost, delayStoreCost); - } - if (alreadyStoredInAllEndings) - { - subrange->hasStore = false; - subrange->hasStoreDelayed = true; - } - else if (delayStoreCost <= PPCRecRARange_getReadWriteCost(subrange->imlSegment)) - { - subrange->hasStore = false; - subrange->hasStoreDelayed = true; - for (sint32 i = 0; i < writeEndingInfo.subrangeCount; i++) - { - raLivenessSubrange_t* subrangeItr = writeEndingInfo.subrangeList[i]; - subrangeItr->hasStore = true; - } - } - } - } -} - -void PPCRecRA_generateSegmentInstructions(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* imlSegment) -{ - sint16 virtualReg2PhysReg[PPC_REC_MAX_VIRTUAL_GPR]; - for (sint32 i = 0; i < PPC_REC_MAX_VIRTUAL_GPR; i++) - virtualReg2PhysReg[i] = -1; - - raLiveRangeInfo_t liveInfo; - liveInfo.liveRangesCount = 0; - sint32 index = 0; - sint32 suffixInstructionCount = (imlSegment->imlListCount > 0 && PPCRecompiler_isSuffixInstruction(imlSegment->imlList + imlSegment->imlListCount - 1)) ? 1 : 0; - // load register ranges that are supplied from previous segments - raLivenessSubrange_t* subrangeItr = imlSegment->raInfo.linkedList_allSubranges; - //for (auto& subrange : imlSegment->raInfo.list_subranges) - while(subrangeItr) - { - if (subrangeItr->start.index == RA_INTER_RANGE_START) - { - liveInfo.liveRangeList[liveInfo.liveRangesCount] = subrangeItr; - liveInfo.liveRangesCount++; -#ifdef CEMU_DEBUG_ASSERT - // load GPR - if (subrangeItr->_noLoad == false) - { - assert_dbg(); - } - // update translation table - if (virtualReg2PhysReg[subrangeItr->range->virtualRegister] != -1) - assert_dbg(); -#endif - virtualReg2PhysReg[subrangeItr->range->virtualRegister] = subrangeItr->range->physicalRegister; - } - // next - subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; - } - // process instructions - while(index < imlSegment->imlListCount+1) - { - // expire ranges - for (sint32 f = 0; f < liveInfo.liveRangesCount; f++) - { - raLivenessSubrange_t* liverange = liveInfo.liveRangeList[f]; - if (liverange->end.index <= index) - { - // update translation table - if (virtualReg2PhysReg[liverange->range->virtualRegister] == -1) - assert_dbg(); - virtualReg2PhysReg[liverange->range->virtualRegister] = -1; - // store GPR - if (liverange->hasStore) - { - PPCRecRA_insertGPRStoreInstruction(imlSegment, std::min(index, imlSegment->imlListCount - suffixInstructionCount), liverange->range->physicalRegister, liverange->range->name); - index++; - } - // remove entry - liveInfo.liveRangesCount--; - liveInfo.liveRangeList[f] = liveInfo.liveRangeList[liveInfo.liveRangesCount]; - f--; - } - } - // load new ranges - subrangeItr = imlSegment->raInfo.linkedList_allSubranges; - while(subrangeItr) - { - if (subrangeItr->start.index == index) - { - liveInfo.liveRangeList[liveInfo.liveRangesCount] = subrangeItr; - liveInfo.liveRangesCount++; - // load GPR - if (subrangeItr->_noLoad == false) - { - PPCRecRA_insertGPRLoadInstruction(imlSegment, std::min(index, imlSegment->imlListCount - suffixInstructionCount), subrangeItr->range->physicalRegister, subrangeItr->range->name); - index++; - subrangeItr->start.index--; - } - // update translation table - cemu_assert_debug(virtualReg2PhysReg[subrangeItr->range->virtualRegister] == -1); - virtualReg2PhysReg[subrangeItr->range->virtualRegister] = subrangeItr->range->physicalRegister; - } - subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; - } - // replace registers - if (index < imlSegment->imlListCount) - { - PPCImlOptimizerUsedRegisters_t gprTracking; - PPCRecompiler_checkRegisterUsage(NULL, imlSegment->imlList + index, &gprTracking); - - sint32 inputGpr[4]; - inputGpr[0] = gprTracking.gpr[0]; - inputGpr[1] = gprTracking.gpr[1]; - inputGpr[2] = gprTracking.gpr[2]; - inputGpr[3] = gprTracking.gpr[3]; - sint32 replaceGpr[4]; - for (sint32 f = 0; f < 4; f++) - { - sint32 virtualRegister = gprTracking.gpr[f]; - if (virtualRegister < 0) - { - replaceGpr[f] = -1; - continue; - } - if (virtualRegister >= PPC_REC_MAX_VIRTUAL_GPR) - assert_dbg(); - replaceGpr[f] = virtualReg2PhysReg[virtualRegister]; - cemu_assert_debug(replaceGpr[f] >= 0); - } - PPCRecompiler_replaceGPRRegisterUsageMultiple(ppcImlGenContext, imlSegment->imlList + index, inputGpr, replaceGpr); - } - // next iml instruction - index++; - } - // expire infinite subranges (subranges that cross the segment border) - sint32 storeLoadListLength = 0; - raLoadStoreInfo_t loadStoreList[PPC_REC_MAX_VIRTUAL_GPR]; - for (sint32 f = 0; f < liveInfo.liveRangesCount; f++) - { - raLivenessSubrange_t* liverange = liveInfo.liveRangeList[f]; - if (liverange->end.index == RA_INTER_RANGE_END) - { - // update translation table - cemu_assert_debug(virtualReg2PhysReg[liverange->range->virtualRegister] != -1); - virtualReg2PhysReg[liverange->range->virtualRegister] = -1; - // store GPR - if (liverange->hasStore) - { - loadStoreList[storeLoadListLength].registerIndex = liverange->range->physicalRegister; - loadStoreList[storeLoadListLength].registerName = liverange->range->name; - storeLoadListLength++; - } - // remove entry - liveInfo.liveRangesCount--; - liveInfo.liveRangeList[f] = liveInfo.liveRangeList[liveInfo.liveRangesCount]; - f--; - } - else - { - cemu_assert_suspicious(); - } - } - if (storeLoadListLength > 0) - { - PPCRecRA_insertGPRStoreInstructions(imlSegment, imlSegment->imlListCount - suffixInstructionCount, loadStoreList, storeLoadListLength); - } - // load subranges for next segments - subrangeItr = imlSegment->raInfo.linkedList_allSubranges; - storeLoadListLength = 0; - while(subrangeItr) - { - if (subrangeItr->start.index == RA_INTER_RANGE_END) - { - liveInfo.liveRangeList[liveInfo.liveRangesCount] = subrangeItr; - liveInfo.liveRangesCount++; - // load GPR - if (subrangeItr->_noLoad == false) - { - loadStoreList[storeLoadListLength].registerIndex = subrangeItr->range->physicalRegister; - loadStoreList[storeLoadListLength].registerName = subrangeItr->range->name; - storeLoadListLength++; - } - // update translation table - cemu_assert_debug(virtualReg2PhysReg[subrangeItr->range->virtualRegister] == -1); - virtualReg2PhysReg[subrangeItr->range->virtualRegister] = subrangeItr->range->physicalRegister; - } - // next - subrangeItr = subrangeItr->link_segmentSubrangesGPR.next; - } - if (storeLoadListLength > 0) - { - PPCRecRA_insertGPRLoadInstructions(imlSegment, imlSegment->imlListCount - suffixInstructionCount, loadStoreList, storeLoadListLength); - } -} - -void PPCRecRA_generateMoveInstructions(ppcImlGenContext_t* ppcImlGenContext) -{ - for (sint32 s = 0; s < ppcImlGenContext->segmentListCount; s++) - { - PPCRecImlSegment_t* imlSegment = ppcImlGenContext->segmentList[s]; - PPCRecRA_generateSegmentInstructions(ppcImlGenContext, imlSegment); - } -} - -void PPCRecRA_calculateLivenessRangesV2(ppcImlGenContext_t* ppcImlGenContext); -void PPCRecRA_processFlowAndCalculateLivenessRangesV2(ppcImlGenContext_t* ppcImlGenContext); -void PPCRecRA_analyzeRangeDataFlowV2(ppcImlGenContext_t* ppcImlGenContext); - -void PPCRecompilerImm_prepareForRegisterAllocation(ppcImlGenContext_t* ppcImlGenContext) -{ - // insert empty segments after every non-taken branch if the linked segment has more than one input - // this gives the register allocator more room to create efficient spill code - sint32 segmentIndex = 0; - while (segmentIndex < ppcImlGenContext->segmentListCount) - { - PPCRecImlSegment_t* imlSegment = ppcImlGenContext->segmentList[segmentIndex]; - if (imlSegment->nextSegmentIsUncertain) - { - segmentIndex++; - continue; - } - if (imlSegment->nextSegmentBranchTaken == nullptr || imlSegment->nextSegmentBranchNotTaken == nullptr) - { - segmentIndex++; - continue; - } - if (imlSegment->nextSegmentBranchNotTaken->list_prevSegments.size() <= 1) - { - segmentIndex++; - continue; - } - if (imlSegment->nextSegmentBranchNotTaken->isEnterable) - { - segmentIndex++; - continue; - } - PPCRecompilerIml_insertSegments(ppcImlGenContext, segmentIndex + 1, 1); - PPCRecImlSegment_t* imlSegmentP0 = ppcImlGenContext->segmentList[segmentIndex + 0]; - PPCRecImlSegment_t* imlSegmentP1 = ppcImlGenContext->segmentList[segmentIndex + 1]; - PPCRecImlSegment_t* nextSegment = imlSegment->nextSegmentBranchNotTaken; - PPCRecompilerIML_removeLink(imlSegmentP0, nextSegment); - PPCRecompilerIml_setLinkBranchNotTaken(imlSegmentP1, nextSegment); - PPCRecompilerIml_setLinkBranchNotTaken(imlSegmentP0, imlSegmentP1); - segmentIndex++; - } - // detect loops - for (sint32 s = 0; s < ppcImlGenContext->segmentListCount; s++) - { - PPCRecImlSegment_t* imlSegment = ppcImlGenContext->segmentList[s]; - imlSegment->momentaryIndex = s; - } - for (sint32 s = 0; s < ppcImlGenContext->segmentListCount; s++) - { - PPCRecImlSegment_t* imlSegment = ppcImlGenContext->segmentList[s]; - PPCRecRA_identifyLoop(ppcImlGenContext, imlSegment); - } -} - -void PPCRecompilerImm_allocateRegisters(ppcImlGenContext_t* ppcImlGenContext) -{ - PPCRecompilerImm_prepareForRegisterAllocation(ppcImlGenContext); - - ppcImlGenContext->raInfo.list_ranges = std::vector(); - - // calculate liveness - PPCRecRA_calculateLivenessRangesV2(ppcImlGenContext); - PPCRecRA_processFlowAndCalculateLivenessRangesV2(ppcImlGenContext); - - PPCRecRA_assignRegisters(ppcImlGenContext); - - PPCRecRA_analyzeRangeDataFlowV2(ppcImlGenContext); - PPCRecRA_generateMoveInstructions(ppcImlGenContext); - - PPCRecRA_deleteAllRanges(ppcImlGenContext); -} \ No newline at end of file diff --git a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlRegisterAllocator2.cpp b/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlRegisterAllocator2.cpp deleted file mode 100644 index abb47e92..00000000 --- a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlRegisterAllocator2.cpp +++ /dev/null @@ -1,414 +0,0 @@ -#include "PPCRecompiler.h" -#include "PPCRecompilerIml.h" -#include "PPCRecompilerX64.h" -#include "PPCRecompilerImlRanges.h" -#include - -bool _isRangeDefined(PPCRecImlSegment_t* imlSegment, sint32 vGPR) -{ - return (imlSegment->raDistances.reg[vGPR].usageStart != INT_MAX); -} - -void PPCRecRA_calculateSegmentMinMaxRanges(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* imlSegment) -{ - for (sint32 i = 0; i < PPC_REC_MAX_VIRTUAL_GPR; i++) - { - imlSegment->raDistances.reg[i].usageStart = INT_MAX; - imlSegment->raDistances.reg[i].usageEnd = INT_MIN; - } - // scan instructions for usage range - sint32 index = 0; - PPCImlOptimizerUsedRegisters_t gprTracking; - while (index < imlSegment->imlListCount) - { - // end loop at suffix instruction - if (PPCRecompiler_isSuffixInstruction(imlSegment->imlList + index)) - break; - // get accessed GPRs - PPCRecompiler_checkRegisterUsage(NULL, imlSegment->imlList + index, &gprTracking); - for (sint32 t = 0; t < 4; t++) - { - sint32 virtualRegister = gprTracking.gpr[t]; - if (virtualRegister < 0) - continue; - cemu_assert_debug(virtualRegister < PPC_REC_MAX_VIRTUAL_GPR); - imlSegment->raDistances.reg[virtualRegister].usageStart = std::min(imlSegment->raDistances.reg[virtualRegister].usageStart, index); // index before/at instruction - imlSegment->raDistances.reg[virtualRegister].usageEnd = std::max(imlSegment->raDistances.reg[virtualRegister].usageEnd, index+1); // index after instruction - } - // next instruction - index++; - } -} - -void PPCRecRA_calculateLivenessRangesV2(ppcImlGenContext_t* ppcImlGenContext) -{ - // for each register calculate min/max index of usage range within each segment - for (sint32 s = 0; s < ppcImlGenContext->segmentListCount; s++) - { - PPCRecRA_calculateSegmentMinMaxRanges(ppcImlGenContext, ppcImlGenContext->segmentList[s]); - } -} - -raLivenessSubrange_t* PPCRecRA_convertToMappedRanges(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* imlSegment, sint32 vGPR, raLivenessRange_t* range) -{ - if (imlSegment->raDistances.isProcessed[vGPR]) - { - // return already existing segment - return imlSegment->raInfo.linkedList_perVirtualGPR[vGPR]; - } - imlSegment->raDistances.isProcessed[vGPR] = true; - if (_isRangeDefined(imlSegment, vGPR) == false) - return nullptr; - // create subrange - cemu_assert_debug(imlSegment->raInfo.linkedList_perVirtualGPR[vGPR] == nullptr); - raLivenessSubrange_t* subrange = PPCRecRA_createSubrange(ppcImlGenContext, range, imlSegment, imlSegment->raDistances.reg[vGPR].usageStart, imlSegment->raDistances.reg[vGPR].usageEnd); - // traverse forward - if (imlSegment->raDistances.reg[vGPR].usageEnd == RA_INTER_RANGE_END) - { - if (imlSegment->nextSegmentBranchTaken && imlSegment->nextSegmentBranchTaken->raDistances.reg[vGPR].usageStart == RA_INTER_RANGE_START) - { - subrange->subrangeBranchTaken = PPCRecRA_convertToMappedRanges(ppcImlGenContext, imlSegment->nextSegmentBranchTaken, vGPR, range); - cemu_assert_debug(subrange->subrangeBranchTaken->start.index == RA_INTER_RANGE_START); - } - if (imlSegment->nextSegmentBranchNotTaken && imlSegment->nextSegmentBranchNotTaken->raDistances.reg[vGPR].usageStart == RA_INTER_RANGE_START) - { - subrange->subrangeBranchNotTaken = PPCRecRA_convertToMappedRanges(ppcImlGenContext, imlSegment->nextSegmentBranchNotTaken, vGPR, range); - cemu_assert_debug(subrange->subrangeBranchNotTaken->start.index == RA_INTER_RANGE_START); - } - } - // traverse backward - if (imlSegment->raDistances.reg[vGPR].usageStart == RA_INTER_RANGE_START) - { - for (auto& it : imlSegment->list_prevSegments) - { - if (it->raDistances.reg[vGPR].usageEnd == RA_INTER_RANGE_END) - PPCRecRA_convertToMappedRanges(ppcImlGenContext, it, vGPR, range); - } - } - // return subrange - return subrange; -} - -void PPCRecRA_createSegmentLivenessRanges(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* imlSegment) -{ - for (sint32 i = 0; i < PPC_REC_MAX_VIRTUAL_GPR; i++) - { - if( _isRangeDefined(imlSegment, i) == false ) - continue; - if( imlSegment->raDistances.isProcessed[i]) - continue; - raLivenessRange_t* range = PPCRecRA_createRangeBase(ppcImlGenContext, i, ppcImlGenContext->mappedRegister[i]); - PPCRecRA_convertToMappedRanges(ppcImlGenContext, imlSegment, i, range); - } - // create lookup table of ranges - raLivenessSubrange_t* vGPR2Subrange[PPC_REC_MAX_VIRTUAL_GPR]; - for (sint32 i = 0; i < PPC_REC_MAX_VIRTUAL_GPR; i++) - { - vGPR2Subrange[i] = imlSegment->raInfo.linkedList_perVirtualGPR[i]; -#ifdef CEMU_DEBUG_ASSERT - if (vGPR2Subrange[i] && vGPR2Subrange[i]->link_sameVirtualRegisterGPR.next != nullptr) - assert_dbg(); -#endif - } - // parse instructions and convert to locations - sint32 index = 0; - PPCImlOptimizerUsedRegisters_t gprTracking; - while (index < imlSegment->imlListCount) - { - // end loop at suffix instruction - if (PPCRecompiler_isSuffixInstruction(imlSegment->imlList + index)) - break; - // get accessed GPRs - PPCRecompiler_checkRegisterUsage(NULL, imlSegment->imlList + index, &gprTracking); - // handle accessed GPR - for (sint32 t = 0; t < 4; t++) - { - sint32 virtualRegister = gprTracking.gpr[t]; - if (virtualRegister < 0) - continue; - bool isWrite = (t == 3); - // add location - PPCRecRA_updateOrAddSubrangeLocation(vGPR2Subrange[virtualRegister], index, isWrite == false, isWrite); -#ifdef CEMU_DEBUG_ASSERT - if (index < vGPR2Subrange[virtualRegister]->start.index) - assert_dbg(); - if (index+1 > vGPR2Subrange[virtualRegister]->end.index) - assert_dbg(); -#endif - } - // next instruction - index++; - } -} - -void PPCRecRA_extendRangeToEndOfSegment(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* imlSegment, sint32 vGPR) -{ - if (_isRangeDefined(imlSegment, vGPR) == false) - { - imlSegment->raDistances.reg[vGPR].usageStart = RA_INTER_RANGE_END; - imlSegment->raDistances.reg[vGPR].usageEnd = RA_INTER_RANGE_END; - return; - } - imlSegment->raDistances.reg[vGPR].usageEnd = RA_INTER_RANGE_END; -} - -void PPCRecRA_extendRangeToBeginningOfSegment(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* imlSegment, sint32 vGPR) -{ - if (_isRangeDefined(imlSegment, vGPR) == false) - { - imlSegment->raDistances.reg[vGPR].usageStart = RA_INTER_RANGE_START; - imlSegment->raDistances.reg[vGPR].usageEnd = RA_INTER_RANGE_START; - } - else - { - imlSegment->raDistances.reg[vGPR].usageStart = RA_INTER_RANGE_START; - } - // propagate backwards - for (auto& it : imlSegment->list_prevSegments) - { - PPCRecRA_extendRangeToEndOfSegment(ppcImlGenContext, it, vGPR); - } -} - -void _PPCRecRA_connectRanges(ppcImlGenContext_t* ppcImlGenContext, sint32 vGPR, PPCRecImlSegment_t** route, sint32 routeDepth) -{ -#ifdef CEMU_DEBUG_ASSERT - if (routeDepth < 2) - assert_dbg(); -#endif - // extend starting range to end of segment - PPCRecRA_extendRangeToEndOfSegment(ppcImlGenContext, route[0], vGPR); - // extend all the connecting segments in both directions - for (sint32 i = 1; i < (routeDepth - 1); i++) - { - PPCRecRA_extendRangeToEndOfSegment(ppcImlGenContext, route[i], vGPR); - PPCRecRA_extendRangeToBeginningOfSegment(ppcImlGenContext, route[i], vGPR); - } - // extend the final segment towards the beginning - PPCRecRA_extendRangeToBeginningOfSegment(ppcImlGenContext, route[routeDepth-1], vGPR); -} - -void _PPCRecRA_checkAndTryExtendRange(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* currentSegment, sint32 vGPR, sint32 distanceLeft, PPCRecImlSegment_t** route, sint32 routeDepth) -{ - if (routeDepth >= 64) - { - cemuLog_logDebug(LogType::Force, "Recompiler RA route maximum depth exceeded for function 0x{:08x}", ppcImlGenContext->functionRef->ppcAddress); - return; - } - route[routeDepth] = currentSegment; - if (currentSegment->raDistances.reg[vGPR].usageStart == INT_MAX) - { - // measure distance to end of segment - distanceLeft -= currentSegment->imlListCount; - if (distanceLeft > 0) - { - if (currentSegment->nextSegmentBranchNotTaken) - _PPCRecRA_checkAndTryExtendRange(ppcImlGenContext, currentSegment->nextSegmentBranchNotTaken, vGPR, distanceLeft, route, routeDepth + 1); - if (currentSegment->nextSegmentBranchTaken) - _PPCRecRA_checkAndTryExtendRange(ppcImlGenContext, currentSegment->nextSegmentBranchTaken, vGPR, distanceLeft, route, routeDepth + 1); - } - return; - } - else - { - // measure distance to range - if (currentSegment->raDistances.reg[vGPR].usageStart == RA_INTER_RANGE_END) - { - if (distanceLeft < currentSegment->imlListCount) - return; // range too far away - } - else if (currentSegment->raDistances.reg[vGPR].usageStart != RA_INTER_RANGE_START && currentSegment->raDistances.reg[vGPR].usageStart > distanceLeft) - return; // out of range - // found close range -> connect ranges - _PPCRecRA_connectRanges(ppcImlGenContext, vGPR, route, routeDepth + 1); - } -} - -void PPCRecRA_checkAndTryExtendRange(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* currentSegment, sint32 vGPR) -{ -#ifdef CEMU_DEBUG_ASSERT - if (currentSegment->raDistances.reg[vGPR].usageEnd < 0) - assert_dbg(); -#endif - // count instructions to end of initial segment - if (currentSegment->raDistances.reg[vGPR].usageEnd == RA_INTER_RANGE_START) - assert_dbg(); - sint32 instructionsUntilEndOfSeg; - if (currentSegment->raDistances.reg[vGPR].usageEnd == RA_INTER_RANGE_END) - instructionsUntilEndOfSeg = 0; - else - instructionsUntilEndOfSeg = currentSegment->imlListCount - currentSegment->raDistances.reg[vGPR].usageEnd; - -#ifdef CEMU_DEBUG_ASSERT - if (instructionsUntilEndOfSeg < 0) - assert_dbg(); -#endif - sint32 remainingScanDist = 45 - instructionsUntilEndOfSeg; - if (remainingScanDist <= 0) - return; // can't reach end - - // also dont forget: Extending is easier if we allow 'non symetric' branches. E.g. register range one enters one branch - PPCRecImlSegment_t* route[64]; - route[0] = currentSegment; - if (currentSegment->nextSegmentBranchNotTaken) - { - _PPCRecRA_checkAndTryExtendRange(ppcImlGenContext, currentSegment->nextSegmentBranchNotTaken, vGPR, remainingScanDist, route, 1); - } - if (currentSegment->nextSegmentBranchTaken) - { - _PPCRecRA_checkAndTryExtendRange(ppcImlGenContext, currentSegment->nextSegmentBranchTaken, vGPR, remainingScanDist, route, 1); - } -} - -void PPCRecRA_mergeCloseRangesForSegmentV2(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* imlSegment) -{ - for (sint32 i = 0; i < PPC_REC_MAX_VIRTUAL_GPR; i++) // todo: Use dynamic maximum or list of used vGPRs so we can avoid parsing empty entries - { - if(imlSegment->raDistances.reg[i].usageStart == INT_MAX) - continue; // not used - // check and extend if possible - PPCRecRA_checkAndTryExtendRange(ppcImlGenContext, imlSegment, i); - } -#ifdef CEMU_DEBUG_ASSERT - if (imlSegment->list_prevSegments.empty() == false && imlSegment->isEnterable) - assert_dbg(); - if ((imlSegment->nextSegmentBranchNotTaken != nullptr || imlSegment->nextSegmentBranchTaken != nullptr) && imlSegment->nextSegmentIsUncertain) - assert_dbg(); -#endif -} - -void PPCRecRA_followFlowAndExtendRanges(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlSegment_t* imlSegment) -{ - std::vector list_segments; - list_segments.reserve(1000); - sint32 index = 0; - imlSegment->raRangeExtendProcessed = true; - list_segments.push_back(imlSegment); - while (index < list_segments.size()) - { - PPCRecImlSegment_t* currentSegment = list_segments[index]; - PPCRecRA_mergeCloseRangesForSegmentV2(ppcImlGenContext, currentSegment); - // follow flow - if (currentSegment->nextSegmentBranchNotTaken && currentSegment->nextSegmentBranchNotTaken->raRangeExtendProcessed == false) - { - currentSegment->nextSegmentBranchNotTaken->raRangeExtendProcessed = true; - list_segments.push_back(currentSegment->nextSegmentBranchNotTaken); - } - if (currentSegment->nextSegmentBranchTaken && currentSegment->nextSegmentBranchTaken->raRangeExtendProcessed == false) - { - currentSegment->nextSegmentBranchTaken->raRangeExtendProcessed = true; - list_segments.push_back(currentSegment->nextSegmentBranchTaken); - } - index++; - } -} - -void PPCRecRA_mergeCloseRangesV2(ppcImlGenContext_t* ppcImlGenContext) -{ - for (sint32 s = 0; s < ppcImlGenContext->segmentListCount; s++) - { - PPCRecImlSegment_t* imlSegment = ppcImlGenContext->segmentList[s]; - if (imlSegment->list_prevSegments.empty()) - { - if (imlSegment->raRangeExtendProcessed) - assert_dbg(); // should not happen - PPCRecRA_followFlowAndExtendRanges(ppcImlGenContext, imlSegment); - } - } -} - -void PPCRecRA_extendRangesOutOfLoopsV2(ppcImlGenContext_t* ppcImlGenContext) -{ - for (sint32 s = 0; s < ppcImlGenContext->segmentListCount; s++) - { - PPCRecImlSegment_t* imlSegment = ppcImlGenContext->segmentList[s]; - auto localLoopDepth = imlSegment->loopDepth; - if( localLoopDepth <= 0 ) - continue; // not inside a loop - // look for loop exit - bool hasLoopExit = false; - if (imlSegment->nextSegmentBranchTaken && imlSegment->nextSegmentBranchTaken->loopDepth < localLoopDepth) - { - hasLoopExit = true; - } - if (imlSegment->nextSegmentBranchNotTaken && imlSegment->nextSegmentBranchNotTaken->loopDepth < localLoopDepth) - { - hasLoopExit = true; - } - if(hasLoopExit == false) - continue; - - // extend looping ranges into all exits (this allows the data flow analyzer to move stores out of the loop) - for (sint32 i = 0; i < PPC_REC_MAX_VIRTUAL_GPR; i++) // todo: Use dynamic maximum or list of used vGPRs so we can avoid parsing empty entries - { - if (imlSegment->raDistances.reg[i].usageEnd != RA_INTER_RANGE_END) - continue; // range not set or does not reach end of segment - if(imlSegment->nextSegmentBranchTaken) - PPCRecRA_extendRangeToBeginningOfSegment(ppcImlGenContext, imlSegment->nextSegmentBranchTaken, i); - if(imlSegment->nextSegmentBranchNotTaken) - PPCRecRA_extendRangeToBeginningOfSegment(ppcImlGenContext, imlSegment->nextSegmentBranchNotTaken, i); - } - } -} - -void PPCRecRA_processFlowAndCalculateLivenessRangesV2(ppcImlGenContext_t* ppcImlGenContext) -{ - // merge close ranges - PPCRecRA_mergeCloseRangesV2(ppcImlGenContext); - // extra pass to move register stores out of loops - PPCRecRA_extendRangesOutOfLoopsV2(ppcImlGenContext); - // calculate liveness ranges - for (sint32 s = 0; s < ppcImlGenContext->segmentListCount; s++) - { - PPCRecImlSegment_t* imlSegment = ppcImlGenContext->segmentList[s]; - PPCRecRA_createSegmentLivenessRanges(ppcImlGenContext, imlSegment); - } -} - -void PPCRecRA_analyzeSubrangeDataDependencyV2(raLivenessSubrange_t* subrange) -{ - bool isRead = false; - bool isWritten = false; - bool isOverwritten = false; - for (auto& location : subrange->list_locations) - { - if (location.isRead) - { - isRead = true; - } - if (location.isWrite) - { - if (isRead == false) - isOverwritten = true; - isWritten = true; - } - } - subrange->_noLoad = isOverwritten; - subrange->hasStore = isWritten; - - if (subrange->start.index == RA_INTER_RANGE_START) - subrange->_noLoad = true; -} - -void _analyzeRangeDataFlow(raLivenessSubrange_t* subrange); - -void PPCRecRA_analyzeRangeDataFlowV2(ppcImlGenContext_t* ppcImlGenContext) -{ - // this function is called after _assignRegisters(), which means that all ranges are already final and wont change anymore - // first do a per-subrange pass - for (auto& range : ppcImlGenContext->raInfo.list_ranges) - { - for (auto& subrange : range->list_subranges) - { - PPCRecRA_analyzeSubrangeDataDependencyV2(subrange); - } - } - // then do a second pass where we scan along subrange flow - for (auto& range : ppcImlGenContext->raInfo.list_ranges) - { - for (auto& subrange : range->list_subranges) // todo - traversing this backwards should be faster and yield better results due to the nature of the algorithm - { - _analyzeRangeDataFlow(subrange); - } - } -} diff --git a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerIntermediate.cpp b/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerIntermediate.cpp index fcbe64be..468af5b2 100644 --- a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerIntermediate.cpp +++ b/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerIntermediate.cpp @@ -1,173 +1,26 @@ #include "PPCRecompiler.h" #include "PPCRecompilerIml.h" -PPCRecImlSegment_t* PPCRecompiler_getSegmentByPPCJumpAddress(ppcImlGenContext_t* ppcImlGenContext, uint32 ppcOffset) -{ - for(sint32 s=0; ssegmentListCount; s++) - { - if( ppcImlGenContext->segmentList[s]->isJumpDestination && ppcImlGenContext->segmentList[s]->jumpDestinationPPCAddress == ppcOffset ) - { - return ppcImlGenContext->segmentList[s]; - } - } - debug_printf("PPCRecompiler_getSegmentByPPCJumpAddress(): Unable to find segment (ppcOffset 0x%08x)\n", ppcOffset); - return NULL; -} - -void PPCRecompilerIml_setLinkBranchNotTaken(PPCRecImlSegment_t* imlSegmentSrc, PPCRecImlSegment_t* imlSegmentDst) -{ - // make sure segments aren't already linked - if (imlSegmentSrc->nextSegmentBranchNotTaken == imlSegmentDst) - return; - // add as next segment for source - if (imlSegmentSrc->nextSegmentBranchNotTaken != NULL) - assert_dbg(); - imlSegmentSrc->nextSegmentBranchNotTaken = imlSegmentDst; - // add as previous segment for destination - imlSegmentDst->list_prevSegments.push_back(imlSegmentSrc); -} - -void PPCRecompilerIml_setLinkBranchTaken(PPCRecImlSegment_t* imlSegmentSrc, PPCRecImlSegment_t* imlSegmentDst) -{ - // make sure segments aren't already linked - if (imlSegmentSrc->nextSegmentBranchTaken == imlSegmentDst) - return; - // add as next segment for source - if (imlSegmentSrc->nextSegmentBranchTaken != NULL) - assert_dbg(); - imlSegmentSrc->nextSegmentBranchTaken = imlSegmentDst; - // add as previous segment for destination - imlSegmentDst->list_prevSegments.push_back(imlSegmentSrc); -} - -void PPCRecompilerIML_removeLink(PPCRecImlSegment_t* imlSegmentSrc, PPCRecImlSegment_t* imlSegmentDst) -{ - if (imlSegmentSrc->nextSegmentBranchNotTaken == imlSegmentDst) - { - imlSegmentSrc->nextSegmentBranchNotTaken = NULL; - } - else if (imlSegmentSrc->nextSegmentBranchTaken == imlSegmentDst) - { - imlSegmentSrc->nextSegmentBranchTaken = NULL; - } - else - assert_dbg(); - - bool matchFound = false; - for (sint32 i = 0; i < imlSegmentDst->list_prevSegments.size(); i++) - { - if (imlSegmentDst->list_prevSegments[i] == imlSegmentSrc) - { - imlSegmentDst->list_prevSegments.erase(imlSegmentDst->list_prevSegments.begin()+i); - matchFound = true; - break; - } - } - if (matchFound == false) - assert_dbg(); -} - -/* - * Replaces all links to segment orig with linkts to segment new - */ -void PPCRecompilerIML_relinkInputSegment(PPCRecImlSegment_t* imlSegmentOrig, PPCRecImlSegment_t* imlSegmentNew) -{ - while (imlSegmentOrig->list_prevSegments.size() != 0) - { - PPCRecImlSegment_t* prevSegment = imlSegmentOrig->list_prevSegments[0]; - if (prevSegment->nextSegmentBranchNotTaken == imlSegmentOrig) - { - PPCRecompilerIML_removeLink(prevSegment, imlSegmentOrig); - PPCRecompilerIml_setLinkBranchNotTaken(prevSegment, imlSegmentNew); - } - else if (prevSegment->nextSegmentBranchTaken == imlSegmentOrig) - { - PPCRecompilerIML_removeLink(prevSegment, imlSegmentOrig); - PPCRecompilerIml_setLinkBranchTaken(prevSegment, imlSegmentNew); - } - else - { - assert_dbg(); - } - } -} - -void PPCRecompilerIML_linkSegments(ppcImlGenContext_t* ppcImlGenContext) -{ - for(sint32 s=0; ssegmentListCount; s++) - { - PPCRecImlSegment_t* imlSegment = ppcImlGenContext->segmentList[s]; - - bool isLastSegment = (s+1)>=ppcImlGenContext->segmentListCount; - PPCRecImlSegment_t* nextSegment = isLastSegment?NULL:ppcImlGenContext->segmentList[s+1]; - // handle empty segment - if( imlSegment->imlListCount == 0 ) - { - if (isLastSegment == false) - PPCRecompilerIml_setLinkBranchNotTaken(imlSegment, ppcImlGenContext->segmentList[s+1]); // continue execution to next segment - else - imlSegment->nextSegmentIsUncertain = true; - continue; - } - // check last instruction of segment - PPCRecImlInstruction_t* imlInstruction = imlSegment->imlList+(imlSegment->imlListCount-1); - if( imlInstruction->type == PPCREC_IML_TYPE_CJUMP || imlInstruction->type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK ) - { - // find destination segment by ppc jump address - PPCRecImlSegment_t* jumpDestSegment = PPCRecompiler_getSegmentByPPCJumpAddress(ppcImlGenContext, imlInstruction->op_conditionalJump.jumpmarkAddress); - if( jumpDestSegment ) - { - if (imlInstruction->op_conditionalJump.condition != PPCREC_JUMP_CONDITION_NONE) - PPCRecompilerIml_setLinkBranchNotTaken(imlSegment, nextSegment); - PPCRecompilerIml_setLinkBranchTaken(imlSegment, jumpDestSegment); - } - else - { - imlSegment->nextSegmentIsUncertain = true; - } - } - else if( imlInstruction->type == PPCREC_IML_TYPE_MACRO ) - { - // currently we assume that the next segment is unknown for all macros - imlSegment->nextSegmentIsUncertain = true; - } - else - { - // all other instruction types do not branch - //imlSegment->nextSegment[0] = nextSegment; - PPCRecompilerIml_setLinkBranchNotTaken(imlSegment, nextSegment); - //imlSegment->nextSegmentIsUncertain = true; - } - } -} - void PPCRecompilerIML_isolateEnterableSegments(ppcImlGenContext_t* ppcImlGenContext) { - sint32 initialSegmentCount = ppcImlGenContext->segmentListCount; - for (sint32 i = 0; i < ppcImlGenContext->segmentListCount; i++) + size_t initialSegmentCount = ppcImlGenContext->segmentList2.size(); + for (size_t i = 0; i < initialSegmentCount; i++) { - PPCRecImlSegment_t* imlSegment = ppcImlGenContext->segmentList[i]; + IMLSegment* imlSegment = ppcImlGenContext->segmentList2[i]; if (imlSegment->list_prevSegments.empty() == false && imlSegment->isEnterable) { // spawn new segment at end - PPCRecompilerIml_insertSegments(ppcImlGenContext, ppcImlGenContext->segmentListCount, 1); - PPCRecImlSegment_t* entrySegment = ppcImlGenContext->segmentList[ppcImlGenContext->segmentListCount-1]; + PPCRecompilerIml_insertSegments(ppcImlGenContext, ppcImlGenContext->segmentList2.size(), 1); + IMLSegment* entrySegment = ppcImlGenContext->segmentList2[ppcImlGenContext->segmentList2.size()-1]; entrySegment->isEnterable = true; entrySegment->enterPPCAddress = imlSegment->enterPPCAddress; // create jump instruction PPCRecompiler_pushBackIMLInstructions(entrySegment, 0, 1); - PPCRecompilerImlGen_generateNewInstruction_jumpSegment(ppcImlGenContext, entrySegment->imlList + 0); - PPCRecompilerIml_setLinkBranchTaken(entrySegment, imlSegment); + entrySegment->imlList.data()[0].make_jump(); + IMLSegment_SetLinkBranchTaken(entrySegment, imlSegment); // remove enterable flag from original segment imlSegment->isEnterable = false; imlSegment->enterPPCAddress = 0; } } -} - -PPCRecImlInstruction_t* PPCRecompilerIML_getLastInstruction(PPCRecImlSegment_t* imlSegment) -{ - if (imlSegment->imlListCount == 0) - return nullptr; - return imlSegment->imlList + (imlSegment->imlListCount - 1); -} +} \ No newline at end of file diff --git a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64.cpp b/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64.cpp deleted file mode 100644 index 14d2febb..00000000 --- a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64.cpp +++ /dev/null @@ -1,2687 +0,0 @@ -#include "Cafe/HW/Espresso/PPCState.h" -#include "Cafe/HW/Espresso/Interpreter/PPCInterpreterInternal.h" -#include "Cafe/HW/Espresso/Interpreter/PPCInterpreterHelper.h" -#include "PPCRecompiler.h" -#include "PPCRecompilerIml.h" -#include "PPCRecompilerX64.h" -#include "Cafe/OS/libs/coreinit/coreinit_Time.h" -#include "util/MemMapper/MemMapper.h" -#include "Common/cpu_features.h" - -sint32 x64Gen_registerMap[12] = // virtual GPR to x64 register mapping -{ - REG_RAX, REG_RDX, REG_RBX, REG_RBP, REG_RSI, REG_RDI, REG_R8, REG_R9, REG_R10, REG_R11, REG_R12, REG_RCX -}; - -/* -* Remember current instruction output offset for reloc -* The instruction generated after this method has been called will be adjusted -*/ -void PPCRecompilerX64Gen_rememberRelocatableOffset(x64GenContext_t* x64GenContext, uint8 type, void* extraInfo = nullptr) -{ - if( x64GenContext->relocateOffsetTableCount >= x64GenContext->relocateOffsetTableSize ) - { - x64GenContext->relocateOffsetTableSize = std::max(4, x64GenContext->relocateOffsetTableSize*2); - x64GenContext->relocateOffsetTable = (x64RelocEntry_t*)realloc(x64GenContext->relocateOffsetTable, sizeof(x64RelocEntry_t)*x64GenContext->relocateOffsetTableSize); - } - x64GenContext->relocateOffsetTable[x64GenContext->relocateOffsetTableCount].offset = x64GenContext->codeBufferIndex; - x64GenContext->relocateOffsetTable[x64GenContext->relocateOffsetTableCount].type = type; - x64GenContext->relocateOffsetTable[x64GenContext->relocateOffsetTableCount].extraInfo = extraInfo; - x64GenContext->relocateOffsetTableCount++; -} - -/* -* Overwrites the currently cached (in x64 cf) cr* register -* Should be called before each x64 instruction which overwrites the current status flags (with mappedCRRegister set to PPCREC_CR_TEMPORARY unless explicitly set by PPC instruction) -*/ -void PPCRecompilerX64Gen_crConditionFlags_set(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, sint32 mappedCRRegister, sint32 crState) -{ - x64GenContext->activeCRRegister = mappedCRRegister; - x64GenContext->activeCRState = crState; -} - -/* -* Reset cached cr* register without storing it first -*/ -void PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext) -{ - x64GenContext->activeCRRegister = PPC_REC_INVALID_REGISTER; -} - -void PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext_t* x64GenContext, sint32 jumpInstructionOffset, sint32 destinationOffset) -{ - uint8* instructionData = x64GenContext->codeBuffer + jumpInstructionOffset; - if (instructionData[0] == 0x0F && (instructionData[1] >= 0x80 && instructionData[1] <= 0x8F)) - { - // far conditional jump - *(uint32*)(instructionData + 2) = (destinationOffset - (jumpInstructionOffset + 6)); - } - else if (instructionData[0] >= 0x70 && instructionData[0] <= 0x7F) - { - // short conditional jump - sint32 distance = (sint32)((destinationOffset - (jumpInstructionOffset + 2))); - cemu_assert_debug(distance >= -128 && distance <= 127); - *(uint8*)(instructionData + 1) = (uint8)distance; - } - else if (instructionData[0] == 0xE9) - { - *(uint32*)(instructionData + 1) = (destinationOffset - (jumpInstructionOffset + 5)); - } - else if (instructionData[0] == 0xEB) - { - sint32 distance = (sint32)((destinationOffset - (jumpInstructionOffset + 2))); - cemu_assert_debug(distance >= -128 && distance <= 127); - *(uint8*)(instructionData + 1) = (uint8)distance; - } - else - { - assert_dbg(); - } -} - -void PPCRecompilerX64Gen_updateCRLogical(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction) -{ - sint32 crRegister = imlInstruction->crRegister; - if( (imlInstruction->crIgnoreMask&(1<<(crRegister*4+PPCREC_CR_BIT_LT))) == 0 ) - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_SIGN, REG_RSP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_LT)); // check for sign instead of _BELOW (CF) which is not set by TEST - if( (imlInstruction->crIgnoreMask&(1<<(crRegister*4+PPCREC_CR_BIT_GT))) == 0 ) - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_SIGNED_GREATER, REG_RSP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_GT)); - if( (imlInstruction->crIgnoreMask&(1<<(crRegister*4+PPCREC_CR_BIT_EQ))) == 0 ) - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_EQUAL, REG_RSP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_EQ)); - // todo: Set CR SO if XER SO bit is set - PPCRecompilerX64Gen_crConditionFlags_set(PPCRecFunction, ppcImlGenContext, x64GenContext, crRegister, PPCREC_CR_STATE_TYPE_LOGICAL); -} - -void* ATTR_MS_ABI PPCRecompiler_virtualHLE(PPCInterpreter_t* hCPU, uint32 hleFuncId) -{ - void* prevRSPTemp = hCPU->rspTemp; - if( hleFuncId == 0xFFD0 ) - { - hCPU->remainingCycles -= 500; // let subtract about 500 cycles for each HLE call - hCPU->gpr[3] = 0; - PPCInterpreter_nextInstruction(hCPU); - return ppcInterpreterCurrentInstance; - } - else - { - auto hleCall = PPCInterpreter_getHLECall(hleFuncId); - cemu_assert(hleCall != nullptr); - hleCall(hCPU); - } - hCPU->rspTemp = prevRSPTemp; - return ppcInterpreterCurrentInstance; -} - -void ATTR_MS_ABI PPCRecompiler_getTBL(PPCInterpreter_t* hCPU, uint32 gprIndex) -{ - uint64 coreTime = coreinit::coreinit_getTimerTick(); - hCPU->gpr[gprIndex] = (uint32)(coreTime&0xFFFFFFFF); -} - -void ATTR_MS_ABI PPCRecompiler_getTBU(PPCInterpreter_t* hCPU, uint32 gprIndex) -{ - uint64 coreTime = coreinit::coreinit_getTimerTick(); - hCPU->gpr[gprIndex] = (uint32)((coreTime>>32)&0xFFFFFFFF); -} - -bool PPCRecompilerX64Gen_imlInstruction_macro(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction) -{ - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - if( imlInstruction->operation == PPCREC_IML_MACRO_BLR || imlInstruction->operation == PPCREC_IML_MACRO_BLRL ) - { - uint32 currentInstructionAddress = imlInstruction->op_macro.param; - // MOV EDX, [SPR_LR] - x64Emit_mov_reg64_mem32(x64GenContext, REG_RDX, REG_RSP, offsetof(PPCInterpreter_t, spr.LR)); - // if BLRL, then update SPR LR - if (imlInstruction->operation == PPCREC_IML_MACRO_BLRL) - x64Gen_mov_mem32Reg64_imm32(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, spr.LR), currentInstructionAddress + 4); - // JMP [offset+RDX*(8/4)+R15] - x64Gen_writeU8(x64GenContext, 0x41); - x64Gen_writeU8(x64GenContext, 0xFF); - x64Gen_writeU8(x64GenContext, 0xA4); - x64Gen_writeU8(x64GenContext, 0x57); - x64Gen_writeU32(x64GenContext, (uint32)offsetof(PPCRecompilerInstanceData_t, ppcRecompilerDirectJumpTable)); - return true; - } - else if( imlInstruction->operation == PPCREC_IML_MACRO_BCTR || imlInstruction->operation == PPCREC_IML_MACRO_BCTRL ) - { - uint32 currentInstructionAddress = imlInstruction->op_macro.param; - // MOV EDX, [SPR_CTR] - x64Emit_mov_reg64_mem32(x64GenContext, REG_RDX, REG_RSP, offsetof(PPCInterpreter_t, spr.CTR)); - // if BCTRL, then update SPR LR - if (imlInstruction->operation == PPCREC_IML_MACRO_BCTRL) - x64Gen_mov_mem32Reg64_imm32(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, spr.LR), currentInstructionAddress + 4); - // JMP [offset+RDX*(8/4)+R15] - x64Gen_writeU8(x64GenContext, 0x41); - x64Gen_writeU8(x64GenContext, 0xFF); - x64Gen_writeU8(x64GenContext, 0xA4); - x64Gen_writeU8(x64GenContext, 0x57); - x64Gen_writeU32(x64GenContext, (uint32)offsetof(PPCRecompilerInstanceData_t, ppcRecompilerDirectJumpTable)); - return true; - } - else if( imlInstruction->operation == PPCREC_IML_MACRO_BL ) - { - // MOV DWORD [SPR_LinkRegister], newLR - uint32 newLR = imlInstruction->op_macro.param + 4; - x64Gen_mov_mem32Reg64_imm32(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, spr.LR), newLR); - // remember new instruction pointer in RDX - uint32 newIP = imlInstruction->op_macro.param2; - x64Gen_mov_reg64Low32_imm32(x64GenContext, REG_RDX, newIP); - // since RDX is constant we can use JMP [R15+const_offset] if jumpTableOffset+RDX*2 does not exceed the 2GB boundary - uint64 lookupOffset = (uint64)offsetof(PPCRecompilerInstanceData_t, ppcRecompilerDirectJumpTable) + (uint64)newIP * 2ULL; - if (lookupOffset >= 0x80000000ULL) - { - // JMP [offset+RDX*(8/4)+R15] - x64Gen_writeU8(x64GenContext, 0x41); - x64Gen_writeU8(x64GenContext, 0xFF); - x64Gen_writeU8(x64GenContext, 0xA4); - x64Gen_writeU8(x64GenContext, 0x57); - x64Gen_writeU32(x64GenContext, (uint32)offsetof(PPCRecompilerInstanceData_t, ppcRecompilerDirectJumpTable)); - } - else - { - x64Gen_writeU8(x64GenContext, 0x41); - x64Gen_writeU8(x64GenContext, 0xFF); - x64Gen_writeU8(x64GenContext, 0xA7); - x64Gen_writeU32(x64GenContext, (uint32)lookupOffset); - } - return true; - } - else if( imlInstruction->operation == PPCREC_IML_MACRO_B_FAR ) - { - // remember new instruction pointer in RDX - uint32 newIP = imlInstruction->op_macro.param2; - x64Gen_mov_reg64Low32_imm32(x64GenContext, REG_RDX, newIP); - // Since RDX is constant we can use JMP [R15+const_offset] if jumpTableOffset+RDX*2 does not exceed the 2GB boundary - uint64 lookupOffset = (uint64)offsetof(PPCRecompilerInstanceData_t, ppcRecompilerDirectJumpTable) + (uint64)newIP * 2ULL; - if (lookupOffset >= 0x80000000ULL) - { - // JMP [offset+RDX*(8/4)+R15] - x64Gen_writeU8(x64GenContext, 0x41); - x64Gen_writeU8(x64GenContext, 0xFF); - x64Gen_writeU8(x64GenContext, 0xA4); - x64Gen_writeU8(x64GenContext, 0x57); - x64Gen_writeU32(x64GenContext, (uint32)offsetof(PPCRecompilerInstanceData_t, ppcRecompilerDirectJumpTable)); - } - else - { - x64Gen_writeU8(x64GenContext, 0x41); - x64Gen_writeU8(x64GenContext, 0xFF); - x64Gen_writeU8(x64GenContext, 0xA7); - x64Gen_writeU32(x64GenContext, (uint32)lookupOffset); - } - return true; - } - else if( imlInstruction->operation == PPCREC_IML_MACRO_LEAVE ) - { - uint32 currentInstructionAddress = imlInstruction->op_macro.param; - // remember PC value in REG_EDX - x64Gen_mov_reg64Low32_imm32(x64GenContext, REG_RDX, currentInstructionAddress); - - uint32 newIP = 0; // special value for recompiler exit - uint64 lookupOffset = (uint64)&(((PPCRecompilerInstanceData_t*)NULL)->ppcRecompilerDirectJumpTable) + (uint64)newIP * 2ULL; - // JMP [R15+offset] - x64Gen_writeU8(x64GenContext, 0x41); - x64Gen_writeU8(x64GenContext, 0xFF); - x64Gen_writeU8(x64GenContext, 0xA7); - x64Gen_writeU32(x64GenContext, (uint32)lookupOffset); - return true; - } - else if( imlInstruction->operation == PPCREC_IML_MACRO_DEBUGBREAK ) - { - x64Gen_mov_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, imlInstruction->op_macro.param2); - x64Gen_int3(x64GenContext); - return true; - } - else if( imlInstruction->operation == PPCREC_IML_MACRO_COUNT_CYCLES ) - { - uint32 cycleCount = imlInstruction->op_macro.param; - x64Gen_sub_mem32reg64_imm32(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, remainingCycles), cycleCount); - return true; - } - else if( imlInstruction->operation == PPCREC_IML_MACRO_HLE ) - { - uint32 ppcAddress = imlInstruction->op_macro.param; - uint32 funcId = imlInstruction->op_macro.param2; - //x64Gen_int3(x64GenContext); - // update instruction pointer - x64Gen_mov_mem32Reg64_imm32(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, instructionPointer), ppcAddress); - //// save hCPU (RSP) - //x64Gen_mov_reg64_imm64(x64GenContext, REG_RESV_TEMP, (uint64)&ppcRecompilerX64_hCPUTemp); - //x64Emit_mov_mem64_reg64(x64GenContext, REG_RESV_TEMP, 0, REG_RSP); - // set parameters - x64Gen_mov_reg64_reg64(x64GenContext, REG_RCX, REG_RSP); - x64Gen_mov_reg64_imm64(x64GenContext, REG_RDX, funcId); - // restore stackpointer from executionContext/hCPU->rspTemp - x64Emit_mov_reg64_mem64(x64GenContext, REG_RSP, REG_RESV_HCPU, offsetof(PPCInterpreter_t, rspTemp)); - //x64Emit_mov_reg64_mem64(x64GenContext, REG_RSP, REG_R14, 0); - //x64Gen_int3(x64GenContext); - // reserve space on stack for call parameters - x64Gen_sub_reg64_imm32(x64GenContext, REG_RSP, 8*11); // must be uneven number in order to retain stack 0x10 alignment - x64Gen_mov_reg64_imm64(x64GenContext, REG_RBP, 0); - // call HLE function - x64Gen_mov_reg64_imm64(x64GenContext, REG_RAX, (uint64)PPCRecompiler_virtualHLE); - x64Gen_call_reg64(x64GenContext, REG_RAX); - // restore RSP to hCPU (from RAX, result of PPCRecompiler_virtualHLE) - //x64Gen_mov_reg64_imm64(x64GenContext, REG_RESV_TEMP, (uint64)&ppcRecompilerX64_hCPUTemp); - //x64Emit_mov_reg64_mem64Reg64(x64GenContext, REG_RSP, REG_RESV_TEMP, 0); - x64Gen_mov_reg64_reg64(x64GenContext, REG_RSP, REG_RAX); - // MOV R15, ppcRecompilerInstanceData - x64Gen_mov_reg64_imm64(x64GenContext, REG_R15, (uint64)ppcRecompilerInstanceData); - // MOV R13, memory_base - x64Gen_mov_reg64_imm64(x64GenContext, REG_R13, (uint64)memory_base); - // check if cycles where decreased beyond zero, if yes -> leave recompiler - x64Gen_bt_mem8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, remainingCycles), 31); // check if negative - sint32 jumpInstructionOffset1 = x64GenContext->codeBufferIndex; - x64Gen_jmpc_near(x64GenContext, X86_CONDITION_NOT_CARRY, 0); - //x64Gen_int3(x64GenContext); - //x64Gen_mov_reg64Low32_imm32(x64GenContext, REG_RDX, ppcAddress); - - x64Emit_mov_reg64_mem32(x64GenContext, REG_RDX, REG_RSP, offsetof(PPCInterpreter_t, instructionPointer)); - // set EAX to 0 (we assume that ppcRecompilerDirectJumpTable[0] will be a recompiler escape function) - x64Gen_xor_reg32_reg32(x64GenContext, REG_RAX, REG_RAX); - // ADD RAX, R15 (R15 -> Pointer to ppcRecompilerInstanceData - x64Gen_add_reg64_reg64(x64GenContext, REG_RAX, REG_R15); - //// JMP [recompilerCallTable+EAX/4*8] - //x64Gen_int3(x64GenContext); - x64Gen_jmp_memReg64(x64GenContext, REG_RAX, (uint32)offsetof(PPCRecompilerInstanceData_t, ppcRecompilerDirectJumpTable)); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset1, x64GenContext->codeBufferIndex); - // check if instruction pointer was changed - // assign new instruction pointer to EAX - x64Emit_mov_reg64_mem32(x64GenContext, REG_RAX, REG_RSP, offsetof(PPCInterpreter_t, instructionPointer)); - // remember instruction pointer in REG_EDX - x64Gen_mov_reg64_reg64(x64GenContext, REG_RDX, REG_RAX); - // EAX *= 2 - x64Gen_add_reg64_reg64(x64GenContext, REG_RAX, REG_RAX); - // ADD RAX, R15 (R15 -> Pointer to ppcRecompilerInstanceData - x64Gen_add_reg64_reg64(x64GenContext, REG_RAX, REG_R15); - // JMP [ppcRecompilerDirectJumpTable+RAX/4*8] - x64Gen_jmp_memReg64(x64GenContext, REG_RAX, (uint32)offsetof(PPCRecompilerInstanceData_t, ppcRecompilerDirectJumpTable)); - return true; - } - else if( imlInstruction->operation == PPCREC_IML_MACRO_MFTB ) - { - uint32 ppcAddress = imlInstruction->op_macro.param; - uint32 sprId = imlInstruction->op_macro.param2&0xFFFF; - uint32 gprIndex = (imlInstruction->op_macro.param2>>16)&0x1F; - // update instruction pointer - x64Gen_mov_mem32Reg64_imm32(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, instructionPointer), ppcAddress); - // set parameters - x64Gen_mov_reg64_reg64(x64GenContext, REG_RCX, REG_RSP); - x64Gen_mov_reg64_imm64(x64GenContext, REG_RDX, gprIndex); - // restore stackpointer to original RSP - x64Emit_mov_reg64_mem64(x64GenContext, REG_RSP, REG_RESV_HCPU, offsetof(PPCInterpreter_t, rspTemp)); - // push hCPU on stack - x64Gen_push_reg64(x64GenContext, REG_RCX); - // reserve space on stack for call parameters - x64Gen_sub_reg64_imm32(x64GenContext, REG_RSP, 8*11 + 8); - x64Gen_mov_reg64_imm64(x64GenContext, REG_RBP, 0); - // call HLE function - if( sprId == SPR_TBL ) - x64Gen_mov_reg64_imm64(x64GenContext, REG_RAX, (uint64)PPCRecompiler_getTBL); - else if( sprId == SPR_TBU ) - x64Gen_mov_reg64_imm64(x64GenContext, REG_RAX, (uint64)PPCRecompiler_getTBU); - else - assert_dbg(); - x64Gen_call_reg64(x64GenContext, REG_RAX); - // restore hCPU from stack - x64Gen_add_reg64_imm32(x64GenContext, REG_RSP, 8 * 11 + 8); - x64Gen_pop_reg64(x64GenContext, REG_RSP); - // MOV R15, ppcRecompilerInstanceData - x64Gen_mov_reg64_imm64(x64GenContext, REG_R15, (uint64)ppcRecompilerInstanceData); - // MOV R13, memory_base - x64Gen_mov_reg64_imm64(x64GenContext, REG_R13, (uint64)memory_base); - return true; - } - else - { - debug_printf("Unknown recompiler macro operation %d\n", imlInstruction->operation); - assert_dbg(); - } - return false; -} - -/* -* Load from memory -*/ -bool PPCRecompilerX64Gen_imlInstruction_load(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction, bool indexed) -{ - sint32 realRegisterData = tempToRealRegister(imlInstruction->op_storeLoad.registerData); - sint32 realRegisterMem = tempToRealRegister(imlInstruction->op_storeLoad.registerMem); - sint32 realRegisterMem2 = PPC_REC_INVALID_REGISTER; - if( indexed ) - realRegisterMem2 = tempToRealRegister(imlInstruction->op_storeLoad.registerMem2); - if( false )//imlInstruction->op_storeLoad.flags & PPCREC_IML_OP_FLAG_FASTMEMACCESS ) - { - // load u8/u16/u32 via direct memory access + optional sign extend - assert_dbg(); // todo - } - else - { - if( indexed && realRegisterMem == realRegisterMem2 ) - { - return false; - } - if( indexed && realRegisterData == realRegisterMem2 ) - { - // for indexed memory access realRegisterData must not be the same register as the second memory register, - // this can easily be fixed by swapping the logic of realRegisterMem and realRegisterMem2 - sint32 temp = realRegisterMem; - realRegisterMem = realRegisterMem2; - realRegisterMem2 = temp; - } - - bool signExtend = imlInstruction->op_storeLoad.flags2.signExtend; - bool switchEndian = imlInstruction->op_storeLoad.flags2.swapEndian; - if( imlInstruction->op_storeLoad.copyWidth == 32 ) - { - //if( indexed ) - // PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - if (indexed) - { - x64Gen_lea_reg64Low32_reg64Low32PlusReg64Low32(x64GenContext, REG_RESV_TEMP, realRegisterMem, realRegisterMem2); - } - if( g_CPUFeatures.x86.movbe && switchEndian ) - { - if (indexed) - { - x64Gen_movBEZeroExtend_reg64_mem32Reg64PlusReg64(x64GenContext, realRegisterData, REG_R13, REG_RESV_TEMP, imlInstruction->op_storeLoad.immS32); - //if (indexed && realRegisterMem != realRegisterData) - // x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); - } - else - { - x64Gen_movBEZeroExtend_reg64_mem32Reg64PlusReg64(x64GenContext, realRegisterData, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32); - } - } - else - { - if (indexed) - { - x64Emit_mov_reg32_mem32(x64GenContext, realRegisterData, REG_R13, REG_RESV_TEMP, imlInstruction->op_storeLoad.immS32); - //if (realRegisterMem != realRegisterData) - // x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); - if (switchEndian) - x64Gen_bswap_reg64Lower32bit(x64GenContext, realRegisterData); - } - else - { - x64Emit_mov_reg32_mem32(x64GenContext, realRegisterData, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32); - if (switchEndian) - x64Gen_bswap_reg64Lower32bit(x64GenContext, realRegisterData); - } - } - } - else if( imlInstruction->op_storeLoad.copyWidth == 16 ) - { - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); // todo: We can avoid this if MOVBE is available - if (indexed) - { - x64Gen_add_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); - } - if( g_CPUFeatures.x86.movbe && switchEndian ) - { - x64Gen_movBEZeroExtend_reg64Low16_mem16Reg64PlusReg64(x64GenContext, realRegisterData, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32); - if( indexed && realRegisterMem != realRegisterData ) - x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); - } - else - { - x64Gen_movZeroExtend_reg64Low16_mem16Reg64PlusReg64(x64GenContext, realRegisterData, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32); - if( indexed && realRegisterMem != realRegisterData ) - x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); - if( switchEndian ) - x64Gen_rol_reg64Low16_imm8(x64GenContext, realRegisterData, 8); - } - if( signExtend ) - x64Gen_movSignExtend_reg64Low32_reg64Low16(x64GenContext, realRegisterData, realRegisterData); - else - x64Gen_movZeroExtend_reg64Low32_reg64Low16(x64GenContext, realRegisterData, realRegisterData); - } - else if( imlInstruction->op_storeLoad.copyWidth == 8 ) - { - if( indexed ) - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - // todo: Optimize by using only MOVZX/MOVSX - if( indexed ) - x64Gen_add_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); - // todo: Use sign extend move from memory instead of separate sign-extend? - if( signExtend ) - x64Gen_movSignExtend_reg64Low32_mem8Reg64PlusReg64(x64GenContext, realRegisterData, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32); - else - x64Emit_movZX_reg32_mem8(x64GenContext, realRegisterData, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32); - if( indexed && realRegisterMem != realRegisterData ) - x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); - } - else if( imlInstruction->op_storeLoad.copyWidth == PPC_REC_LOAD_LWARX_MARKER ) - { - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - if( imlInstruction->op_storeLoad.immS32 != 0 ) - assert_dbg(); // not supported - if( indexed ) - x64Gen_add_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); - x64Emit_mov_mem32_reg32(x64GenContext, REG_RSP, (uint32)offsetof(PPCInterpreter_t, reservedMemAddr), realRegisterMem); // remember EA for reservation - x64Emit_mov_reg32_mem32(x64GenContext, realRegisterData, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32); - if( indexed && realRegisterMem != realRegisterData ) - x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); - if( switchEndian ) - x64Gen_bswap_reg64Lower32bit(x64GenContext, realRegisterData); - x64Emit_mov_mem32_reg32(x64GenContext, REG_RSP, (uint32)offsetof(PPCInterpreter_t, reservedMemValue), realRegisterData); // remember value for reservation - // LWARX instruction costs extra cycles (this speeds up busy loops) - x64Gen_sub_mem32reg64_imm32(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, remainingCycles), 20); - } - else if( imlInstruction->op_storeLoad.copyWidth == PPC_REC_STORE_LSWI_3 ) - { - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - if( switchEndian == false ) - assert_dbg(); - if( indexed ) - x64Gen_add_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); // can be replaced with LEA temp, [memReg1+memReg2] (this way we can avoid the SUB instruction after the move) - if( g_CPUFeatures.x86.movbe ) - { - x64Gen_movBEZeroExtend_reg64_mem32Reg64PlusReg64(x64GenContext, realRegisterData, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32); - if( indexed && realRegisterMem != realRegisterData ) - x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); - } - else - { - x64Emit_mov_reg32_mem32(x64GenContext, realRegisterData, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32); - if( indexed && realRegisterMem != realRegisterData ) - x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); - x64Gen_bswap_reg64Lower32bit(x64GenContext, realRegisterData); - } - x64Gen_and_reg64Low32_imm32(x64GenContext, realRegisterData, 0xFFFFFF00); - } - else - return false; - return true; - } - return false; -} - -/* -* Write to memory -*/ -bool PPCRecompilerX64Gen_imlInstruction_store(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction, bool indexed) -{ - sint32 realRegisterData = tempToRealRegister(imlInstruction->op_storeLoad.registerData); - sint32 realRegisterMem = tempToRealRegister(imlInstruction->op_storeLoad.registerMem); - sint32 realRegisterMem2 = PPC_REC_INVALID_REGISTER; - if (indexed) - realRegisterMem2 = tempToRealRegister(imlInstruction->op_storeLoad.registerMem2); - - if (false)//imlInstruction->op_storeLoad.flags & PPCREC_IML_OP_FLAG_FASTMEMACCESS ) - { - // load u8/u16/u32 via direct memory access + optional sign extend - assert_dbg(); // todo - } - else - { - if (indexed && realRegisterMem == realRegisterMem2) - { - return false; - } - if (indexed && realRegisterData == realRegisterMem2) - { - // for indexed memory access realRegisterData must not be the same register as the second memory register, - // this can easily be fixed by swapping the logic of realRegisterMem and realRegisterMem2 - sint32 temp = realRegisterMem; - realRegisterMem = realRegisterMem2; - realRegisterMem2 = temp; - } - - bool signExtend = imlInstruction->op_storeLoad.flags2.signExtend; - bool swapEndian = imlInstruction->op_storeLoad.flags2.swapEndian; - if (imlInstruction->op_storeLoad.copyWidth == 32) - { - if (indexed) - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - uint32 valueRegister; - if ((swapEndian == false || g_CPUFeatures.x86.movbe) && realRegisterMem != realRegisterData) - { - valueRegister = realRegisterData; - } - else - { - x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, realRegisterData); - valueRegister = REG_RESV_TEMP; - } - if (g_CPUFeatures.x86.movbe == false && swapEndian) - x64Gen_bswap_reg64Lower32bit(x64GenContext, valueRegister); - if (indexed) - x64Gen_add_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); - if (g_CPUFeatures.x86.movbe && swapEndian) - x64Gen_movBETruncate_mem32Reg64PlusReg64_reg64(x64GenContext, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32, valueRegister); - else - x64Gen_movTruncate_mem32Reg64PlusReg64_reg64(x64GenContext, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32, valueRegister); - if (indexed) - x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); - } - else if (imlInstruction->op_storeLoad.copyWidth == 16) - { - if (indexed || swapEndian) - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, realRegisterData); - if (swapEndian) - x64Gen_rol_reg64Low16_imm8(x64GenContext, REG_RESV_TEMP, 8); - if (indexed) - x64Gen_add_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); - x64Gen_movTruncate_mem16Reg64PlusReg64_reg64(x64GenContext, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32, REG_RESV_TEMP); - if (indexed) - x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); - // todo: Optimize this, e.g. by using MOVBE - } - else if (imlInstruction->op_storeLoad.copyWidth == 8) - { - if (indexed) - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - if (indexed && realRegisterMem == realRegisterData) - { - x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, realRegisterData); - realRegisterData = REG_RESV_TEMP; - } - if (indexed) - x64Gen_add_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); - x64Gen_movTruncate_mem8Reg64PlusReg64_reg64(x64GenContext, REG_RESV_MEMBASE, realRegisterMem, imlInstruction->op_storeLoad.immS32, realRegisterData); - if (indexed) - x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); - } - else if (imlInstruction->op_storeLoad.copyWidth == PPC_REC_STORE_STWCX_MARKER) - { - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - if (imlInstruction->op_storeLoad.immS32 != 0) - assert_dbg(); // todo - // reset cr0 LT, GT and EQ - sint32 crRegister = 0; - x64Gen_mov_mem8Reg64_imm8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, cr) + sizeof(uint8)*(crRegister * 4 + PPCREC_CR_BIT_LT), 0); - x64Gen_mov_mem8Reg64_imm8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, cr) + sizeof(uint8)*(crRegister * 4 + PPCREC_CR_BIT_GT), 0); - x64Gen_mov_mem8Reg64_imm8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, cr) + sizeof(uint8)*(crRegister * 4 + PPCREC_CR_BIT_EQ), 0); - // calculate effective address - x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, realRegisterData); - if (swapEndian) - x64Gen_bswap_reg64Lower32bit(x64GenContext, REG_RESV_TEMP); - if (indexed) - x64Gen_add_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); - // realRegisterMem now holds EA - x64Gen_cmp_reg64Low32_mem32reg64(x64GenContext, realRegisterMem, REG_RESV_HCPU, offsetof(PPCInterpreter_t, reservedMemAddr)); - sint32 jumpInstructionOffsetJumpToEnd = x64GenContext->codeBufferIndex; - x64Gen_jmpc_near(x64GenContext, X86_CONDITION_NOT_EQUAL, 0); - // EA matches reservation - // backup EAX (since it's an explicit operand of CMPXCHG and will be overwritten) - x64Emit_mov_mem32_reg32(x64GenContext, REG_RSP, (uint32)offsetof(PPCInterpreter_t, temporaryGPR[0]), REG_EAX); - // backup REG_RESV_MEMBASE - x64Emit_mov_mem64_reg64(x64GenContext, REG_RESV_HCPU, (uint32)offsetof(PPCInterpreter_t, temporaryGPR[2]), REG_RESV_MEMBASE); - // add mem register to REG_RESV_MEMBASE - x64Gen_add_reg64_reg64(x64GenContext, REG_RESV_MEMBASE, realRegisterMem); - // load reserved value in EAX - x64Emit_mov_reg64_mem32(x64GenContext, REG_EAX, REG_RESV_HCPU, offsetof(PPCInterpreter_t, reservedMemValue)); - // bswap EAX - x64Gen_bswap_reg64Lower32bit(x64GenContext, REG_EAX); - - //x64Gen_lock_cmpxchg_mem32Reg64PlusReg64_reg64(x64GenContext, REG_RESV_MEMBASE, realRegisterMem, 0, REG_RESV_TEMP); - x64Gen_lock_cmpxchg_mem32Reg64_reg64(x64GenContext, REG_RESV_MEMBASE, 0, REG_RESV_TEMP); - - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_EQUAL, REG_RSP, offsetof(PPCInterpreter_t, cr) + sizeof(uint8)*(crRegister * 4 + PPCREC_CR_BIT_EQ)); - - // reset reservation - x64Gen_mov_mem32Reg64_imm32(x64GenContext, REG_RESV_HCPU, (uint32)offsetof(PPCInterpreter_t, reservedMemAddr), 0); - x64Gen_mov_mem32Reg64_imm32(x64GenContext, REG_RESV_HCPU, (uint32)offsetof(PPCInterpreter_t, reservedMemValue), 0); - - // restore EAX - x64Emit_mov_reg64_mem32(x64GenContext, REG_EAX, REG_RESV_HCPU, (uint32)offsetof(PPCInterpreter_t, temporaryGPR[0])); - // restore REG_RESV_MEMBASE - x64Emit_mov_reg64_mem64(x64GenContext, REG_RESV_MEMBASE, REG_RESV_HCPU, (uint32)offsetof(PPCInterpreter_t, temporaryGPR[2])); - - // copy XER SO to CR0 SO - x64Gen_bt_mem8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, spr.XER), 31); - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_CARRY, REG_RESV_HCPU, offsetof(PPCInterpreter_t, cr) + sizeof(uint8)*(crRegister * 4 + PPCREC_CR_BIT_SO)); - // end - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffsetJumpToEnd, x64GenContext->codeBufferIndex); - } - else if (imlInstruction->op_storeLoad.copyWidth == PPC_REC_STORE_STSWI_2) - { - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, realRegisterData); - x64Gen_shr_reg64Low32_imm8(x64GenContext, REG_RESV_TEMP, 16); // store upper 2 bytes .. - x64Gen_rol_reg64Low16_imm8(x64GenContext, REG_RESV_TEMP, 8); // .. as big-endian - if (indexed) - x64Gen_add_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); - - x64Gen_movTruncate_mem16Reg64PlusReg64_reg64(x64GenContext, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32, REG_RESV_TEMP); - if (indexed) - x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); - } - else if (imlInstruction->op_storeLoad.copyWidth == PPC_REC_STORE_STSWI_3) - { - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, realRegisterData); - if (indexed) - x64Gen_add_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); - - x64Gen_shr_reg64Low32_imm8(x64GenContext, REG_RESV_TEMP, 8); - x64Gen_movTruncate_mem8Reg64PlusReg64_reg64(x64GenContext, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32 + 2, REG_RESV_TEMP); - x64Gen_shr_reg64Low32_imm8(x64GenContext, REG_RESV_TEMP, 8); - x64Gen_movTruncate_mem8Reg64PlusReg64_reg64(x64GenContext, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32 + 1, REG_RESV_TEMP); - x64Gen_shr_reg64Low32_imm8(x64GenContext, REG_RESV_TEMP, 8); - x64Gen_movTruncate_mem8Reg64PlusReg64_reg64(x64GenContext, REG_R13, realRegisterMem, imlInstruction->op_storeLoad.immS32 + 0, REG_RESV_TEMP); - - if (indexed) - x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, realRegisterMem, realRegisterMem2); - } - else - return false; - return true; - } - return false; -} - -/* - * Copy byte/word/dword from memory to memory - */ -void PPCRecompilerX64Gen_imlInstruction_mem2mem(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction) -{ - sint32 realSrcMemReg = tempToRealRegister(imlInstruction->op_mem2mem.src.registerMem); - sint32 realSrcMemImm = imlInstruction->op_mem2mem.src.immS32; - sint32 realDstMemReg = tempToRealRegister(imlInstruction->op_mem2mem.dst.registerMem); - sint32 realDstMemImm = imlInstruction->op_mem2mem.dst.immS32; - // PPCRecompilerX64Gen_crConditionFlags_forget() is not needed here, since MOVs don't affect eflags - if (imlInstruction->op_mem2mem.copyWidth == 32) - { - x64Emit_mov_reg32_mem32(x64GenContext, REG_RESV_TEMP, REG_R13, realSrcMemReg, realSrcMemImm); - x64Gen_movTruncate_mem32Reg64PlusReg64_reg64(x64GenContext, REG_R13, realDstMemReg, realDstMemImm, REG_RESV_TEMP); - } - else - { - assert_dbg(); - } -} - -bool PPCRecompilerX64Gen_imlInstruction_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction) -{ - if (imlInstruction->operation == PPCREC_IML_OP_ASSIGN) - { - // registerResult = registerA - if (imlInstruction->crRegister != PPC_REC_INVALID_REGISTER) - { - x64Gen_mov_reg64_reg64(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult), tempToRealRegister(imlInstruction->op_r_r.registerA)); - if (imlInstruction->crMode == PPCREC_CR_MODE_LOGICAL) - { - // since MOV doesn't set eflags we need another test instruction - x64Gen_test_reg64Low32_reg64Low32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult), tempToRealRegister(imlInstruction->op_r_r.registerResult)); - // set cr bits - PPCRecompilerX64Gen_updateCRLogical(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction); - } - else - { - assert_dbg(); - } - } - else - { - x64Gen_mov_reg64Low32_reg64Low32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult), tempToRealRegister(imlInstruction->op_r_r.registerA)); - } - } - else if (imlInstruction->operation == PPCREC_IML_OP_ENDIAN_SWAP) - { - // registerResult = endianSwap32(registerA) - if (imlInstruction->op_r_r.registerA != imlInstruction->op_r_r.registerResult) - assert_dbg(); - x64Gen_bswap_reg64Lower32bit(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult)); - } - else if( imlInstruction->operation == PPCREC_IML_OP_ADD ) - { - // registerResult += registerA - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); - x64Gen_add_reg64Low32_reg64Low32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult), tempToRealRegister(imlInstruction->op_r_r.registerA)); - } - else if( imlInstruction->operation == PPCREC_IML_OP_ASSIGN_S8_TO_S32 ) - { - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - x64Gen_movSignExtend_reg64Low32_reg64Low8(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult), tempToRealRegister(imlInstruction->op_r_r.registerA)); - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - if( imlInstruction->crMode == PPCREC_CR_MODE_ARITHMETIC ) - { - x64Gen_test_reg64Low32_reg64Low32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult), tempToRealRegister(imlInstruction->op_r_r.registerResult)); - // set cr bits - PPCRecompilerX64Gen_updateCRLogical(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction); - } - else - { - debug_printf("PPCRecompilerX64Gen_imlInstruction_r_r(): Unsupported operation\n"); - assert_dbg(); - } - } - } - else if( imlInstruction->operation == PPCREC_IML_OP_OR || imlInstruction->operation == PPCREC_IML_OP_AND || imlInstruction->operation == PPCREC_IML_OP_XOR ) - { - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - if( imlInstruction->operation == PPCREC_IML_OP_OR ) - { - // registerResult |= registerA - x64Gen_or_reg64Low32_reg64Low32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult), tempToRealRegister(imlInstruction->op_r_r.registerA)); - } - else if( imlInstruction->operation == PPCREC_IML_OP_AND ) - { - // registerResult &= registerA - x64Gen_and_reg64Low32_reg64Low32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult), tempToRealRegister(imlInstruction->op_r_r.registerA)); - } - else - { - // registerResult ^= registerA - x64Gen_xor_reg64Low32_reg64Low32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult), tempToRealRegister(imlInstruction->op_r_r.registerA)); - } - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - // set cr bits - PPCRecompilerX64Gen_updateCRLogical(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction); - } - } - else if( imlInstruction->operation == PPCREC_IML_OP_NOT ) - { - // copy register content if different registers - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - if( imlInstruction->op_r_r.registerResult != imlInstruction->op_r_r.registerA ) - { - x64Gen_mov_reg64_reg64(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult), tempToRealRegister(imlInstruction->op_r_r.registerA)); - } - // NOT destination register - x64Gen_not_reg64Low32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult)); - // update cr bits - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - // NOT instruction does not update flags, so we have to generate an additional TEST instruction - x64Gen_test_reg64Low32_reg64Low32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult), tempToRealRegister(imlInstruction->op_r_r.registerResult)); - // set cr bits - PPCRecompilerX64Gen_updateCRLogical(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction); - } - } - else if( imlInstruction->operation == PPCREC_IML_OP_CNTLZW ) - { - // count leading zeros - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); - if( g_CPUFeatures.x86.lzcnt ) - { - x64Gen_lzcnt_reg64Low32_reg64Low32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult), tempToRealRegister(imlInstruction->op_r_r.registerA)); - } - else - { - x64Gen_test_reg64Low32_reg64Low32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerA), tempToRealRegister(imlInstruction->op_r_r.registerA)); - sint32 jumpInstructionOffset1 = x64GenContext->codeBufferIndex; - x64Gen_jmpc_near(x64GenContext, X86_CONDITION_EQUAL, 0); - x64Gen_bsr_reg64Low32_reg64Low32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult), tempToRealRegister(imlInstruction->op_r_r.registerA)); - x64Gen_neg_reg64Low32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult)); - x64Gen_add_reg64Low32_imm32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult), 32-1); - sint32 jumpInstructionOffset2 = x64GenContext->codeBufferIndex; - x64Gen_jmpc_near(x64GenContext, X86_CONDITION_NONE, 0); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset1, x64GenContext->codeBufferIndex); - x64Gen_mov_reg64Low32_imm32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult), 32); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset2, x64GenContext->codeBufferIndex); - } - } - else if( imlInstruction->operation == PPCREC_IML_OP_COMPARE_SIGNED || imlInstruction->operation == PPCREC_IML_OP_COMPARE_UNSIGNED ) - { - // registerA CMP registerB (arithmetic compare) - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - if( imlInstruction->crRegister == PPC_REC_INVALID_REGISTER ) - { - return false; // a NO-OP instruction - } - if( imlInstruction->crRegister >= 8 ) - { - return false; - } - // update state of cr register - if( imlInstruction->operation == PPCREC_IML_OP_COMPARE_SIGNED ) - PPCRecompilerX64Gen_crConditionFlags_set(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction->crRegister, PPCREC_CR_STATE_TYPE_SIGNED_ARITHMETIC); - else - PPCRecompilerX64Gen_crConditionFlags_set(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction->crRegister, PPCREC_CR_STATE_TYPE_UNSIGNED_ARITHMETIC); - // create compare instruction - x64Gen_cmp_reg64Low32_reg64Low32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult), tempToRealRegister(imlInstruction->op_r_r.registerA)); - // set cr bits - sint32 crRegister = imlInstruction->crRegister; - if( imlInstruction->operation == PPCREC_IML_OP_COMPARE_SIGNED ) - { - if( (imlInstruction->crIgnoreMask&(1<<(crRegister*4+PPCREC_CR_BIT_LT))) == 0 ) - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_SIGNED_LESS, REG_ESP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_LT)); - if( (imlInstruction->crIgnoreMask&(1<<(crRegister*4+PPCREC_CR_BIT_GT))) == 0 ) - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_SIGNED_GREATER, REG_ESP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_GT)); - if( (imlInstruction->crIgnoreMask&(1<<(crRegister*4+PPCREC_CR_BIT_EQ))) == 0 ) - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_EQUAL, REG_ESP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_EQ)); - // todo: Also set summary overflow if xer bit is set - } - else if( imlInstruction->operation == PPCREC_IML_OP_COMPARE_UNSIGNED ) - { - if( (imlInstruction->crIgnoreMask&(1<<(crRegister*4+PPCREC_CR_BIT_LT))) == 0 ) - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_UNSIGNED_BELOW, REG_ESP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_LT)); - if( (imlInstruction->crIgnoreMask&(1<<(crRegister*4+PPCREC_CR_BIT_GT))) == 0 ) - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_UNSIGNED_ABOVE, REG_ESP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_GT)); - if( (imlInstruction->crIgnoreMask&(1<<(crRegister*4+PPCREC_CR_BIT_EQ))) == 0 ) - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_EQUAL, REG_ESP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_EQ)); - // todo: Also set summary overflow if xer bit is set - } - else - assert_dbg(); - } - else if( imlInstruction->operation == PPCREC_IML_OP_NEG ) - { - // copy register content if different registers - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - if( imlInstruction->op_r_r.registerResult != imlInstruction->op_r_r.registerA ) - { - x64Gen_mov_reg64_reg64(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult), tempToRealRegister(imlInstruction->op_r_r.registerA)); - } - // NEG destination register - x64Gen_neg_reg64Low32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult)); - // update cr bits - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - // set cr bits - PPCRecompilerX64Gen_updateCRLogical(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction); - } - } - else if( imlInstruction->operation == PPCREC_IML_OP_ADD_CARRY ) - { - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - // copy operand to result if different registers - if( imlInstruction->op_r_r.registerResult != imlInstruction->op_r_r.registerA ) - { - x64Gen_mov_reg64_reg64(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult), tempToRealRegister(imlInstruction->op_r_r.registerA)); - } - // copy xer_ca to eflags carry - x64Gen_bt_mem8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, xer_ca), 0); - // add carry bit - x64Gen_adc_reg64Low32_imm32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult), 0); - // update xer carry - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_CARRY, REG_RSP, offsetof(PPCInterpreter_t, xer_ca)); - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - // set cr bits - sint32 crRegister = imlInstruction->crRegister; - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_SIGN, REG_RSP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_LT)); // check for sign instead of _BELOW (CF) which is not set by AND/OR - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_UNSIGNED_ABOVE, REG_RSP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_GT)); - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_EQUAL, REG_RSP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_EQ)); - // todo: Use different version of PPCRecompilerX64Gen_updateCRLogical(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction) - // todo: Also set summary overflow if xer bit is set - } - } - else if( imlInstruction->operation == PPCREC_IML_OP_ADD_CARRY_ME ) - { - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - // copy operand to result if different registers - if( imlInstruction->op_r_r.registerResult != imlInstruction->op_r_r.registerA ) - { - x64Gen_mov_reg64_reg64(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult), tempToRealRegister(imlInstruction->op_r_r.registerA)); - } - // copy xer_ca to eflags carry - x64Gen_bt_mem8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, xer_ca), 0); - // add carry bit - x64Gen_adc_reg64Low32_imm32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult), (uint32)-1); - // update xer carry - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_CARRY, REG_RSP, offsetof(PPCInterpreter_t, xer_ca)); - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - // set cr bits - sint32 crRegister = imlInstruction->crRegister; - x64Gen_test_reg64Low32_reg64Low32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult), tempToRealRegister(imlInstruction->op_r_r.registerResult)); - PPCRecompilerX64Gen_updateCRLogical(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction); - } - } - else if( imlInstruction->operation == PPCREC_IML_OP_SUB_CARRY_UPDATE_CARRY ) - { - // registerResult = ~registerOperand1 + carry - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - sint32 rRegResult = tempToRealRegister(imlInstruction->op_r_r.registerResult); - sint32 rRegOperand1 = tempToRealRegister(imlInstruction->op_r_r.registerA); - // copy operand to result register - x64Gen_mov_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand1); - // execute NOT on result - x64Gen_not_reg64Low32(x64GenContext, rRegResult); - // copy xer_ca to eflags carry - x64Gen_bt_mem8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, xer_ca), 0); - // add carry - x64Gen_adc_reg64Low32_imm32(x64GenContext, rRegResult, 0); - // update carry - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_CARRY, REG_RSP, offsetof(PPCInterpreter_t, xer_ca)); - // update cr if requested - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - if( imlInstruction->crMode == PPCREC_CR_MODE_LOGICAL ) - { - x64Gen_test_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegResult); - // set cr bits - PPCRecompilerX64Gen_updateCRLogical(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction); - } - else - { - assert_dbg(); - } - } - } - else if( imlInstruction->operation == PPCREC_IML_OP_ASSIGN_S16_TO_S32 ) - { - // registerResult = (uint32)(sint32)(sint16)registerA - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - x64Gen_movSignExtend_reg64Low32_reg64Low16(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult), reg32ToReg16(tempToRealRegister(imlInstruction->op_r_r.registerA))); - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - if( imlInstruction->crMode == PPCREC_CR_MODE_ARITHMETIC ) - { - x64Gen_test_reg64Low32_reg64Low32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r.registerResult), tempToRealRegister(imlInstruction->op_r_r.registerResult)); - // set cr bits - PPCRecompilerX64Gen_updateCRLogical(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction); - } - else - { - debug_printf("PPCRecompilerX64Gen_imlInstruction_r_r(): Unsupported operation\n"); - assert_dbg(); - } - } - } - else if( imlInstruction->operation == PPCREC_IML_OP_DCBZ ) - { - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - if( imlInstruction->op_r_r.registerResult != imlInstruction->op_r_r.registerA ) - { - x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, tempToRealRegister(imlInstruction->op_r_r.registerA)); - x64Gen_add_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, tempToRealRegister(imlInstruction->op_r_r.registerResult)); - x64Gen_and_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, ~0x1F); - x64Gen_add_reg64_reg64(x64GenContext, REG_RESV_TEMP, REG_RESV_MEMBASE); - for(sint32 f=0; f<0x20; f+=8) - x64Gen_mov_mem64Reg64_imm32(x64GenContext, REG_RESV_TEMP, f, 0); - } - else - { - // calculate effective address - x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, tempToRealRegister(imlInstruction->op_r_r.registerA)); - x64Gen_and_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, ~0x1F); - x64Gen_add_reg64_reg64(x64GenContext, REG_RESV_TEMP, REG_RESV_MEMBASE); - for(sint32 f=0; f<0x20; f+=8) - x64Gen_mov_mem64Reg64_imm32(x64GenContext, REG_RESV_TEMP, f, 0); - } - } - else - { - debug_printf("PPCRecompilerX64Gen_imlInstruction_r_r(): Unsupported operation 0x%x\n", imlInstruction->operation); - return false; - } - return true; -} - -bool PPCRecompilerX64Gen_imlInstruction_r_s32(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction) -{ - if( imlInstruction->operation == PPCREC_IML_OP_ASSIGN ) - { - // registerResult = immS32 - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); - x64Gen_mov_reg64Low32_imm32(x64GenContext, tempToRealRegister(imlInstruction->op_r_immS32.registerIndex), (uint32)imlInstruction->op_r_immS32.immS32); - } - else if( imlInstruction->operation == PPCREC_IML_OP_ADD ) - { - // registerResult += immS32 - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - assert_dbg(); - } - x64Gen_add_reg64Low32_imm32(x64GenContext, tempToRealRegister(imlInstruction->op_r_immS32.registerIndex), (uint32)imlInstruction->op_r_immS32.immS32); - } - else if( imlInstruction->operation == PPCREC_IML_OP_SUB ) - { - // registerResult -= immS32 - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - if (imlInstruction->crRegister == PPCREC_CR_REG_TEMP) - { - // do nothing -> SUB is for BDNZ instruction - } - else if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - // update cr register - assert_dbg(); - } - x64Gen_sub_reg64Low32_imm32(x64GenContext, tempToRealRegister(imlInstruction->op_r_immS32.registerIndex), (uint32)imlInstruction->op_r_immS32.immS32); - } - else if( imlInstruction->operation == PPCREC_IML_OP_AND ) - { - // registerResult &= immS32 - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - x64Gen_and_reg64Low32_imm32(x64GenContext, tempToRealRegister(imlInstruction->op_r_immS32.registerIndex), (uint32)imlInstruction->op_r_immS32.immS32); - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - if( imlInstruction->crMode != PPCREC_CR_MODE_LOGICAL ) - { - assert_dbg(); - } - // set cr bits - sint32 crRegister = imlInstruction->crRegister; - PPCRecompilerX64Gen_updateCRLogical(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction); - // todo: Set CR SO if XER SO bit is set - } - } - else if( imlInstruction->operation == PPCREC_IML_OP_OR ) - { - // registerResult |= immS32 - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); - x64Gen_or_reg64Low32_imm32(x64GenContext, tempToRealRegister(imlInstruction->op_r_immS32.registerIndex), (uint32)imlInstruction->op_r_immS32.immS32); - } - else if( imlInstruction->operation == PPCREC_IML_OP_XOR ) - { - // registerResult ^= immS32 - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); - x64Gen_xor_reg64Low32_imm32(x64GenContext, tempToRealRegister(imlInstruction->op_r_immS32.registerIndex), (uint32)imlInstruction->op_r_immS32.immS32); - } - else if( imlInstruction->operation == PPCREC_IML_OP_LEFT_ROTATE ) - { - // registerResult <<<= immS32 - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); - if( (imlInstruction->op_r_immS32.immS32&0x80) ) - assert_dbg(); // should not happen - x64Gen_rol_reg64Low32_imm8(x64GenContext, tempToRealRegister(imlInstruction->op_r_immS32.registerIndex), (uint8)imlInstruction->op_r_immS32.immS32); - } - else if( imlInstruction->operation == PPCREC_IML_OP_COMPARE_SIGNED || imlInstruction->operation == PPCREC_IML_OP_COMPARE_UNSIGNED ) - { - // registerResult CMP immS32 (arithmetic compare) - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - if( imlInstruction->crRegister == PPC_REC_INVALID_REGISTER ) - { - debug_printf("PPCRecompilerX64Gen_imlInstruction_r_s32(): No-Op CMP found\n"); - return true; // a NO-OP instruction - } - if( imlInstruction->crRegister >= 8 ) - { - debug_printf("PPCRecompilerX64Gen_imlInstruction_r_s32(): Unsupported CMP with crRegister = 8\n"); - return false; - } - // update state of cr register - if( imlInstruction->operation == PPCREC_IML_OP_COMPARE_SIGNED ) - PPCRecompilerX64Gen_crConditionFlags_set(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction->crRegister, PPCREC_CR_STATE_TYPE_SIGNED_ARITHMETIC); - else - PPCRecompilerX64Gen_crConditionFlags_set(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction->crRegister, PPCREC_CR_STATE_TYPE_UNSIGNED_ARITHMETIC); - // create compare instruction - x64Gen_cmp_reg64Low32_imm32(x64GenContext, tempToRealRegister(imlInstruction->op_r_immS32.registerIndex), imlInstruction->op_r_immS32.immS32); - // set cr bits - uint32 crRegister = imlInstruction->crRegister; - if( imlInstruction->operation == PPCREC_IML_OP_COMPARE_SIGNED ) - { - if( (imlInstruction->crIgnoreMask&(1<<(crRegister*4+PPCREC_CR_BIT_LT))) == 0 ) - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_SIGNED_LESS, REG_ESP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_LT)); - if( (imlInstruction->crIgnoreMask&(1<<(crRegister*4+PPCREC_CR_BIT_GT))) == 0 ) - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_SIGNED_GREATER, REG_ESP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_GT)); - if( (imlInstruction->crIgnoreMask&(1<<(crRegister*4+PPCREC_CR_BIT_EQ))) == 0 ) - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_EQUAL, REG_ESP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_EQ)); - } - else if( imlInstruction->operation == PPCREC_IML_OP_COMPARE_UNSIGNED ) - { - if( (imlInstruction->crIgnoreMask&(1<<(crRegister*4+PPCREC_CR_BIT_LT))) == 0 ) - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_UNSIGNED_BELOW, REG_ESP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_LT)); - if( (imlInstruction->crIgnoreMask&(1<<(crRegister*4+PPCREC_CR_BIT_GT))) == 0 ) - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_UNSIGNED_ABOVE, REG_ESP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_GT)); - if( (imlInstruction->crIgnoreMask&(1<<(crRegister*4+PPCREC_CR_BIT_EQ))) == 0 ) - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_EQUAL, REG_ESP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_EQ)); - } - else - assert_dbg(); - // todo: Also set summary overflow if xer bit is set? - } - else if( imlInstruction->operation == PPCREC_IML_OP_MFCR ) - { - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - uint32 destRegister = tempToRealRegister(imlInstruction->op_r_immS32.registerIndex); - x64Gen_xor_reg64Low32_reg64Low32(x64GenContext, destRegister, destRegister); - for(sint32 f=0; f<32; f++) - { - x64Gen_bt_mem8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, cr)+f, 0); - x64Gen_adc_reg64Low32_reg64Low32(x64GenContext, destRegister, destRegister); - } - } - else if (imlInstruction->operation == PPCREC_IML_OP_MTCRF) - { - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - uint32 srcRegister = tempToRealRegister(imlInstruction->op_r_immS32.registerIndex); - uint32 crBitMask = ppc_MTCRFMaskToCRBitMask((uint32)imlInstruction->op_r_immS32.immS32); - for (sint32 f = 0; f < 32; f++) - { - if(((crBitMask >> f) & 1) == 0) - continue; - x64Gen_mov_mem8Reg64_imm8(x64GenContext, REG_ESP, offsetof(PPCInterpreter_t, cr) + sizeof(uint8) * (f), 0); - x64Gen_test_reg64Low32_imm32(x64GenContext, srcRegister, 0x80000000>>f); - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_NOT_EQUAL, REG_ESP, offsetof(PPCInterpreter_t, cr) + sizeof(uint8) * (f)); - } - } - else - { - debug_printf("PPCRecompilerX64Gen_imlInstruction_r_s32(): Unsupported operation 0x%x\n", imlInstruction->operation); - return false; - } - return true; -} - -bool PPCRecompilerX64Gen_imlInstruction_conditional_r_s32(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction) -{ - if (imlInstruction->operation == PPCREC_IML_OP_ASSIGN) - { - // registerResult = immS32 (conditional) - if (imlInstruction->crRegister != PPC_REC_INVALID_REGISTER) - { - assert_dbg(); - } - - x64Gen_mov_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, (uint32)imlInstruction->op_conditional_r_s32.immS32); - - uint8 crBitIndex = imlInstruction->op_conditional_r_s32.crRegisterIndex * 4 + imlInstruction->op_conditional_r_s32.crBitIndex; - if (imlInstruction->op_conditional_r_s32.crRegisterIndex == x64GenContext->activeCRRegister) - { - if (x64GenContext->activeCRState == PPCREC_CR_STATE_TYPE_UNSIGNED_ARITHMETIC) - { - if (imlInstruction->op_conditional_r_s32.crBitIndex == CR_BIT_LT) - { - x64Gen_cmovcc_reg64Low32_reg64Low32(x64GenContext, imlInstruction->op_conditional_r_s32.bitMustBeSet ? X86_CONDITION_CARRY : X86_CONDITION_NOT_CARRY, tempToRealRegister(imlInstruction->op_conditional_r_s32.registerIndex), REG_RESV_TEMP); - return true; - } - else if (imlInstruction->op_conditional_r_s32.crBitIndex == CR_BIT_EQ) - { - x64Gen_cmovcc_reg64Low32_reg64Low32(x64GenContext, imlInstruction->op_conditional_r_s32.bitMustBeSet ? X86_CONDITION_EQUAL : X86_CONDITION_NOT_EQUAL, tempToRealRegister(imlInstruction->op_conditional_r_s32.registerIndex), REG_RESV_TEMP); - return true; - } - else if (imlInstruction->op_conditional_r_s32.crBitIndex == CR_BIT_GT) - { - x64Gen_cmovcc_reg64Low32_reg64Low32(x64GenContext, imlInstruction->op_conditional_r_s32.bitMustBeSet ? X86_CONDITION_UNSIGNED_ABOVE : X86_CONDITION_UNSIGNED_BELOW_EQUAL, tempToRealRegister(imlInstruction->op_conditional_r_s32.registerIndex), REG_RESV_TEMP); - return true; - } - } - else if (x64GenContext->activeCRState == PPCREC_CR_STATE_TYPE_SIGNED_ARITHMETIC) - { - if (imlInstruction->op_conditional_r_s32.crBitIndex == CR_BIT_LT) - { - x64Gen_cmovcc_reg64Low32_reg64Low32(x64GenContext, imlInstruction->op_conditional_r_s32.bitMustBeSet ? X86_CONDITION_SIGNED_LESS : X86_CONDITION_SIGNED_GREATER_EQUAL, tempToRealRegister(imlInstruction->op_conditional_r_s32.registerIndex), REG_RESV_TEMP); - return true; - } - else if (imlInstruction->op_conditional_r_s32.crBitIndex == CR_BIT_EQ) - { - x64Gen_cmovcc_reg64Low32_reg64Low32(x64GenContext, imlInstruction->op_conditional_r_s32.bitMustBeSet ? X86_CONDITION_EQUAL : X86_CONDITION_NOT_EQUAL, tempToRealRegister(imlInstruction->op_conditional_r_s32.registerIndex), REG_RESV_TEMP); - return true; - } - else if (imlInstruction->op_conditional_r_s32.crBitIndex == CR_BIT_GT) - { - x64Gen_cmovcc_reg64Low32_reg64Low32(x64GenContext, imlInstruction->op_conditional_r_s32.bitMustBeSet ? X86_CONDITION_SIGNED_GREATER : X86_CONDITION_SIGNED_LESS_EQUAL, tempToRealRegister(imlInstruction->op_conditional_r_s32.registerIndex), REG_RESV_TEMP); - return true; - } - } - else if (x64GenContext->activeCRState == PPCREC_CR_STATE_TYPE_LOGICAL) - { - if (imlInstruction->op_conditional_r_s32.crBitIndex == CR_BIT_LT) - { - x64Gen_cmovcc_reg64Low32_reg64Low32(x64GenContext, imlInstruction->op_conditional_r_s32.bitMustBeSet ? X86_CONDITION_SIGN : X86_CONDITION_NOT_SIGN, tempToRealRegister(imlInstruction->op_conditional_r_s32.registerIndex), REG_RESV_TEMP); - return true; - } - else if (imlInstruction->op_conditional_r_s32.crBitIndex == CR_BIT_EQ) - { - x64Gen_cmovcc_reg64Low32_reg64Low32(x64GenContext, imlInstruction->op_conditional_r_s32.bitMustBeSet ? X86_CONDITION_EQUAL : X86_CONDITION_NOT_EQUAL, tempToRealRegister(imlInstruction->op_conditional_r_s32.registerIndex), REG_RESV_TEMP); - return true; - } - else if (imlInstruction->op_conditional_r_s32.crBitIndex == CR_BIT_GT) - { - x64Gen_cmovcc_reg64Low32_reg64Low32(x64GenContext, imlInstruction->op_conditional_r_s32.bitMustBeSet ? X86_CONDITION_SIGNED_GREATER : X86_CONDITION_SIGNED_LESS_EQUAL, tempToRealRegister(imlInstruction->op_conditional_r_s32.registerIndex), REG_RESV_TEMP); - return true; - } - } - } - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - x64Gen_bt_mem8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, cr) + crBitIndex * sizeof(uint8), 0); - if (imlInstruction->op_conditional_r_s32.bitMustBeSet) - x64Gen_cmovcc_reg64Low32_reg64Low32(x64GenContext, X86_CONDITION_CARRY, tempToRealRegister(imlInstruction->op_conditional_r_s32.registerIndex), REG_RESV_TEMP); - else - x64Gen_cmovcc_reg64Low32_reg64Low32(x64GenContext, X86_CONDITION_NOT_CARRY, tempToRealRegister(imlInstruction->op_conditional_r_s32.registerIndex), REG_RESV_TEMP); - return true; - } - return false; -} - -bool PPCRecompilerX64Gen_imlInstruction_r_r_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction) -{ - if( imlInstruction->operation == PPCREC_IML_OP_ADD || imlInstruction->operation == PPCREC_IML_OP_ADD_UPDATE_CARRY || imlInstruction->operation == PPCREC_IML_OP_ADD_CARRY_UPDATE_CARRY ) - { - // registerResult = registerOperand1 + registerOperand2 - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - sint32 rRegResult = tempToRealRegister(imlInstruction->op_r_r_r.registerResult); - sint32 rRegOperand1 = tempToRealRegister(imlInstruction->op_r_r_r.registerA); - sint32 rRegOperand2 = tempToRealRegister(imlInstruction->op_r_r_r.registerB); - - bool addCarry = imlInstruction->operation == PPCREC_IML_OP_ADD_CARRY_UPDATE_CARRY; - if( (rRegResult == rRegOperand1) || (rRegResult == rRegOperand2) ) - { - // be careful not to overwrite the operand before we use it - if( rRegResult == rRegOperand1 ) - { - if( addCarry ) - { - x64Gen_bt_mem8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, xer_ca), 0); - x64Gen_adc_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand2); - } - else - x64Gen_add_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand2); - } - else - { - if( addCarry ) - { - x64Gen_bt_mem8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, xer_ca), 0); - x64Gen_adc_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand1); - } - else - x64Gen_add_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand1); - } - } - else - { - // copy operand1 to destination register before doing addition - x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, rRegOperand1); - // add operand2 - if( addCarry ) - { - x64Gen_bt_mem8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, xer_ca), 0); - x64Gen_adc_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand2); - } - else - x64Gen_add_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand2); - } - // update carry - if( imlInstruction->operation == PPCREC_IML_OP_ADD_UPDATE_CARRY || imlInstruction->operation == PPCREC_IML_OP_ADD_CARRY_UPDATE_CARRY ) - { - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_CARRY, REG_RSP, offsetof(PPCInterpreter_t, xer_ca)); - } - // set cr bits if enabled - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - if( imlInstruction->crMode != PPCREC_CR_MODE_LOGICAL ) - { - assert_dbg(); - } - sint32 crRegister = imlInstruction->crRegister; - PPCRecompilerX64Gen_updateCRLogical(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction); - return true; - } - } - else if( imlInstruction->operation == PPCREC_IML_OP_SUB ) - { - // registerResult = registerOperand1 - registerOperand2 - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - sint32 rRegResult = tempToRealRegister(imlInstruction->op_r_r_r.registerResult); - sint32 rRegOperand1 = tempToRealRegister(imlInstruction->op_r_r_r.registerA); - sint32 rRegOperand2 = tempToRealRegister(imlInstruction->op_r_r_r.registerB); - if( rRegOperand1 == rRegOperand2 ) - { - // result = operand1 - operand1 -> 0 - x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegResult); - } - else if( rRegResult == rRegOperand1 ) - { - // result = result - operand2 - x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand2); - } - else if ( rRegResult == rRegOperand2 ) - { - // result = operand1 - result - // NEG result - x64Gen_neg_reg64Low32(x64GenContext, rRegResult); - // ADD result, operand1 - x64Gen_add_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand1); - } - else - { - // copy operand1 to destination register before doing addition - x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, rRegOperand1); - // sub operand2 - x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand2); - } - // set cr bits if enabled - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - if( imlInstruction->crMode != PPCREC_CR_MODE_LOGICAL ) - { - assert_dbg(); - } - sint32 crRegister = imlInstruction->crRegister; - PPCRecompilerX64Gen_updateCRLogical(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction); - return true; - } - } - else if( imlInstruction->operation == PPCREC_IML_OP_SUB_CARRY_UPDATE_CARRY ) - { - // registerResult = registerOperand1 - registerOperand2 + carry - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - sint32 rRegResult = tempToRealRegister(imlInstruction->op_r_r_r.registerResult); - sint32 rRegOperand1 = tempToRealRegister(imlInstruction->op_r_r_r.registerA); - sint32 rRegOperand2 = tempToRealRegister(imlInstruction->op_r_r_r.registerB); - if( rRegOperand1 == rRegOperand2 ) - { - // copy xer_ca to eflags carry - x64Gen_bt_mem8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, xer_ca), 0); - x64Gen_cmc(x64GenContext); - // result = operand1 - operand1 -> 0 - x64Gen_sbb_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegResult); - } - else if( rRegResult == rRegOperand1 ) - { - // copy inverted xer_ca to eflags carry - x64Gen_bt_mem8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, xer_ca), 0); - x64Gen_cmc(x64GenContext); - // result = result - operand2 - x64Gen_sbb_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand2); - } - else if ( rRegResult == rRegOperand2 ) - { - // result = operand1 - result - // NOT result - x64Gen_not_reg64Low32(x64GenContext, rRegResult); - // copy xer_ca to eflags carry - x64Gen_bt_mem8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, xer_ca), 0); - // ADC result, operand1 - x64Gen_adc_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand1); - } - else - { - // copy operand1 to destination register before doing addition - x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, rRegOperand1); - // copy xer_ca to eflags carry - x64Gen_bt_mem8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, xer_ca), 0); - x64Gen_cmc(x64GenContext); - // sub operand2 - x64Gen_sbb_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand2); - } - // update carry flag (todo: is this actually correct in all cases?) - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_CARRY, REG_RSP, offsetof(PPCInterpreter_t, xer_ca)); - // update cr0 if requested - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - if( imlInstruction->crMode != PPCREC_CR_MODE_LOGICAL ) - assert_dbg(); - x64Gen_test_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegResult); - PPCRecompilerX64Gen_updateCRLogical(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction); - } - } - else if( imlInstruction->operation == PPCREC_IML_OP_MULTIPLY_SIGNED ) - { - // registerResult = registerOperand1 * registerOperand2 - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - sint32 rRegResult = tempToRealRegister(imlInstruction->op_r_r_r.registerResult); - sint32 rRegOperand1 = tempToRealRegister(imlInstruction->op_r_r_r.registerA); - sint32 rRegOperand2 = tempToRealRegister(imlInstruction->op_r_r_r.registerB); - if( (rRegResult == rRegOperand1) || (rRegResult == rRegOperand2) ) - { - // be careful not to overwrite the operand before we use it - if( rRegResult == rRegOperand1 ) - x64Gen_imul_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand2); - else - x64Gen_imul_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand1); - } - else - { - // copy operand1 to destination register before doing multiplication - x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, rRegOperand1); - // add operand2 - x64Gen_imul_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand2); - } - // set cr bits if enabled - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - if( imlInstruction->crMode != PPCREC_CR_MODE_LOGICAL ) - { - assert_dbg(); - } - // since IMUL instruction leaves relevant flags undefined, we have to use another TEST instruction to get the correct results - x64Gen_test_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegResult); - PPCRecompilerX64Gen_updateCRLogical(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction); - } - } - else if( imlInstruction->operation == PPCREC_IML_OP_SUBFC ) - { - // registerResult = registerOperand2(rB) - registerOperand1(rA) - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - // updates carry flag - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - return false; - } - sint32 rRegResult = tempToRealRegister(imlInstruction->op_r_r_r.registerResult); - sint32 rRegOperandA = tempToRealRegister(imlInstruction->op_r_r_r.registerA); - sint32 rRegOperandB = tempToRealRegister(imlInstruction->op_r_r_r.registerB); - // update carry flag - // carry flag is detected this way: - //if ((~a+b) < a) { - // return true; - //} - //if ((~a+b+1) < 1) { - // return true; - //} - // set carry to zero - x64Gen_mov_mem8Reg64_imm8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, xer_ca), 0); - // ((~a+b)<~a) == true -> ca = 1 - x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, rRegOperandA); - x64Gen_not_reg64Low32(x64GenContext, REG_RESV_TEMP); - x64Gen_add_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, rRegOperandB); - x64Gen_not_reg64Low32(x64GenContext, rRegOperandA); - x64Gen_cmp_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, rRegOperandA); - x64Gen_not_reg64Low32(x64GenContext, rRegOperandA); - sint32 jumpInstructionOffset1 = x64GenContext->codeBufferIndex; - x64Gen_jmpc_near(x64GenContext, X86_CONDITION_UNSIGNED_ABOVE_EQUAL, 0); - // reset carry flag + jump destination afterwards - x64Gen_mov_mem8Reg64_imm8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, xer_ca), 1); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset1, x64GenContext->codeBufferIndex); - // OR ((~a+b+1)<1) == true -> ca = 1 - x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, rRegOperandA); - // todo: Optimize by reusing result in REG_RESV_TEMP from above and only add 1 - x64Gen_not_reg64Low32(x64GenContext, REG_RESV_TEMP); - x64Gen_add_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, rRegOperandB); - x64Gen_add_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, 1); - x64Gen_cmp_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, 1); - sint32 jumpInstructionOffset2 = x64GenContext->codeBufferIndex; - x64Gen_jmpc_near(x64GenContext, X86_CONDITION_UNSIGNED_ABOVE_EQUAL, 0); - // reset carry flag + jump destination afterwards - x64Gen_mov_mem8Reg64_imm8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, xer_ca), 1); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset2, x64GenContext->codeBufferIndex); - // do subtraction - if( rRegOperandB == rRegOperandA ) - { - // result = operandA - operandA -> 0 - x64Gen_xor_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegResult); - } - else if( rRegResult == rRegOperandB ) - { - // result = result - operandA - x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperandA); - } - else if ( rRegResult == rRegOperandA ) - { - // result = operandB - result - // NEG result - x64Gen_neg_reg64Low32(x64GenContext, rRegResult); - // ADD result, operandB - x64Gen_add_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperandB); - } - else - { - // copy operand1 to destination register before doing addition - x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, rRegOperandB); - // sub operand2 - x64Gen_sub_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperandA); - } - } - else if( imlInstruction->operation == PPCREC_IML_OP_SLW || imlInstruction->operation == PPCREC_IML_OP_SRW ) - { - // registerResult = registerOperand1(rA) >> registerOperand2(rB) (up to 63 bits) - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - sint32 rRegResult = tempToRealRegister(imlInstruction->op_r_r_r.registerResult); - sint32 rRegOperand1 = tempToRealRegister(imlInstruction->op_r_r_r.registerA); - sint32 rRegOperand2 = tempToRealRegister(imlInstruction->op_r_r_r.registerB); - - if (g_CPUFeatures.x86.bmi2 && imlInstruction->operation == PPCREC_IML_OP_SRW) - { - // use BMI2 SHRX if available - x64Gen_shrx_reg64_reg64_reg64(x64GenContext, rRegResult, rRegOperand1, rRegOperand2); - } - else if (g_CPUFeatures.x86.bmi2 && imlInstruction->operation == PPCREC_IML_OP_SLW) - { - // use BMI2 SHLX if available - x64Gen_shlx_reg64_reg64_reg64(x64GenContext, rRegResult, rRegOperand1, rRegOperand2); - x64Gen_and_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegResult); // trim result to 32bit - } - else - { - // lazy and slow way to do shift by register without relying on ECX/CL or BMI2 - x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, rRegOperand1); - for (sint32 b = 0; b < 6; b++) - { - x64Gen_test_reg64Low32_imm32(x64GenContext, rRegOperand2, (1 << b)); - sint32 jumpInstructionOffset = x64GenContext->codeBufferIndex; - x64Gen_jmpc_near(x64GenContext, X86_CONDITION_EQUAL, 0); // jump if bit not set - if (b == 5) - { - x64Gen_xor_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, REG_RESV_TEMP); - } - else - { - if (imlInstruction->operation == PPCREC_IML_OP_SLW) - x64Gen_shl_reg64Low32_imm8(x64GenContext, REG_RESV_TEMP, (1 << b)); - else - x64Gen_shr_reg64Low32_imm8(x64GenContext, REG_RESV_TEMP, (1 << b)); - } - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset, x64GenContext->codeBufferIndex); - } - x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, REG_RESV_TEMP); - } - // set cr bits if enabled - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - if( imlInstruction->crMode != PPCREC_CR_MODE_LOGICAL ) - { - assert_dbg(); - } - x64Gen_test_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegResult); - PPCRecompilerX64Gen_updateCRLogical(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction); - } - } - else if( imlInstruction->operation == PPCREC_IML_OP_LEFT_ROTATE ) - { - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - sint32 rRegResult = tempToRealRegister(imlInstruction->op_r_r_r.registerResult); - sint32 rRegOperand1 = tempToRealRegister(imlInstruction->op_r_r_r.registerA); - sint32 rRegOperand2 = tempToRealRegister(imlInstruction->op_r_r_r.registerB); - // todo: Use BMI2 rotate if available - // check if CL/ECX/RCX is available - if( rRegResult != REG_RCX && rRegOperand1 != REG_RCX && rRegOperand2 != REG_RCX ) - { - // swap operand 2 with RCX - x64Gen_xchg_reg64_reg64(x64GenContext, REG_RCX, rRegOperand2); - // move operand 1 to temp register - x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, rRegOperand1); - // rotate - x64Gen_rol_reg64Low32_cl(x64GenContext, REG_RESV_TEMP); - // undo swap operand 2 with RCX - x64Gen_xchg_reg64_reg64(x64GenContext, REG_RCX, rRegOperand2); - // copy to result register - x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, REG_RESV_TEMP); - } - else - { - x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, rRegOperand1); - // lazy and slow way to do shift by register without relying on ECX/CL - for(sint32 b=0; b<5; b++) - { - x64Gen_test_reg64Low32_imm32(x64GenContext, rRegOperand2, (1<codeBufferIndex; - x64Gen_jmpc_near(x64GenContext, X86_CONDITION_EQUAL, 0); // jump if bit not set - x64Gen_rol_reg64Low32_imm8(x64GenContext, REG_RESV_TEMP, (1<codeBufferIndex); - } - x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, REG_RESV_TEMP); - } - // set cr bits if enabled - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - if( imlInstruction->crMode != PPCREC_CR_MODE_LOGICAL ) - { - assert_dbg(); - } - x64Gen_test_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegResult); - PPCRecompilerX64Gen_updateCRLogical(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction); - } - } - else if( imlInstruction->operation == PPCREC_IML_OP_SRAW ) - { - // registerResult = (sint32)registerOperand1(rA) >> (sint32)registerOperand2(rB) (up to 63 bits) - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - sint32 rRegResult = tempToRealRegister(imlInstruction->op_r_r_r.registerResult); - sint32 rRegOperand1 = tempToRealRegister(imlInstruction->op_r_r_r.registerA); - sint32 rRegOperand2 = tempToRealRegister(imlInstruction->op_r_r_r.registerB); - // save cr - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - return false; - } - // todo: Use BMI instructions if available? - // MOV registerResult, registerOperand (if different) - x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, rRegOperand1); - // reset carry - x64Gen_mov_mem8Reg64_imm8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, xer_ca), 0); - // we use the same shift by register approach as in SLW/SRW, but we have to differentiate by signed/unsigned shift since it influences how the carry flag is set - x64Gen_test_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, 0x80000000); - sint32 jumpInstructionJumpToSignedShift = x64GenContext->codeBufferIndex; - x64Gen_jmpc_far(x64GenContext, X86_CONDITION_NOT_EQUAL, 0); - //sint32 jumpInstructionJumpToEnd = x64GenContext->codeBufferIndex; - //x64Gen_jmpc(x64GenContext, X86_CONDITION_EQUAL, 0); - // unsigned shift (MSB of input register is not set) - for(sint32 b=0; b<6; b++) - { - x64Gen_test_reg64Low32_imm32(x64GenContext, rRegOperand2, (1<codeBufferIndex; - x64Gen_jmpc_near(x64GenContext, X86_CONDITION_EQUAL, 0); // jump if bit not set - if( b == 5 ) - { - x64Gen_sar_reg64Low32_imm8(x64GenContext, REG_RESV_TEMP, (1<codeBufferIndex); - } - sint32 jumpInstructionJumpToEnd = x64GenContext->codeBufferIndex; - x64Gen_jmpc_far(x64GenContext, X86_CONDITION_NONE, 0); - // signed shift - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionJumpToSignedShift, x64GenContext->codeBufferIndex); - for(sint32 b=0; b<6; b++) - { - // check if we need to shift by (1<codeBufferIndex; - x64Gen_jmpc_near(x64GenContext, X86_CONDITION_EQUAL, 0); // jump if bit not set - // set ca if any non-zero bit is shifted out - x64Gen_test_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, (1<<(1<codeBufferIndex; - x64Gen_jmpc_near(x64GenContext, X86_CONDITION_EQUAL, 0); // jump if no bit is set - x64Gen_mov_mem8Reg64_imm8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, xer_ca), 1); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionJumpToAfterCa, x64GenContext->codeBufferIndex); - // arithmetic shift - if( b == 5 ) - { - // copy sign bit into all bits - x64Gen_sar_reg64Low32_imm8(x64GenContext, REG_RESV_TEMP, (1<codeBufferIndex); - } - // end - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionJumpToEnd, x64GenContext->codeBufferIndex); - x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, REG_RESV_TEMP); - // update CR if requested - // todo - } - else if( imlInstruction->operation == PPCREC_IML_OP_DIVIDE_SIGNED || imlInstruction->operation == PPCREC_IML_OP_DIVIDE_UNSIGNED ) - { - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - sint32 rRegResult = tempToRealRegister(imlInstruction->op_r_r_r.registerResult); - sint32 rRegOperand1 = tempToRealRegister(imlInstruction->op_r_r_r.registerA); - sint32 rRegOperand2 = tempToRealRegister(imlInstruction->op_r_r_r.registerB); - - x64Emit_mov_mem32_reg32(x64GenContext, REG_RSP, (uint32)offsetof(PPCInterpreter_t, temporaryGPR[0]), REG_EAX); - x64Emit_mov_mem32_reg32(x64GenContext, REG_RSP, (uint32)offsetof(PPCInterpreter_t, temporaryGPR[1]), REG_EDX); - // mov operand 2 to temp register - x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, rRegOperand2); - // mov operand1 to EAX - x64Gen_mov_reg64Low32_reg64Low32(x64GenContext, REG_EAX, rRegOperand1); - // sign or zero extend EAX to EDX:EAX based on division sign mode - if( imlInstruction->operation == PPCREC_IML_OP_DIVIDE_SIGNED ) - x64Gen_cdq(x64GenContext); - else - x64Gen_xor_reg64Low32_reg64Low32(x64GenContext, REG_EDX, REG_EDX); - // make sure we avoid division by zero - x64Gen_test_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, REG_RESV_TEMP); - x64Gen_jmpc_near(x64GenContext, X86_CONDITION_EQUAL, 3); - // divide - if( imlInstruction->operation == PPCREC_IML_OP_DIVIDE_SIGNED ) - x64Gen_idiv_reg64Low32(x64GenContext, REG_RESV_TEMP); - else - x64Gen_div_reg64Low32(x64GenContext, REG_RESV_TEMP); - // result of division is now stored in EAX, move it to result register - if( rRegResult != REG_EAX ) - x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, REG_EAX); - // restore EAX / EDX - if( rRegResult != REG_RAX ) - x64Emit_mov_reg64_mem32(x64GenContext, REG_EAX, REG_RSP, (uint32)offsetof(PPCInterpreter_t, temporaryGPR[0])); - if( rRegResult != REG_RDX ) - x64Emit_mov_reg64_mem32(x64GenContext, REG_EDX, REG_RSP, (uint32)offsetof(PPCInterpreter_t, temporaryGPR[1])); - // set cr bits if requested - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - if( imlInstruction->crMode != PPCREC_CR_MODE_ARITHMETIC ) - { - assert_dbg(); - } - x64Gen_test_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegResult); - PPCRecompilerX64Gen_updateCRLogical(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction); - } - } - else if( imlInstruction->operation == PPCREC_IML_OP_MULTIPLY_HIGH_SIGNED || imlInstruction->operation == PPCREC_IML_OP_MULTIPLY_HIGH_UNSIGNED ) - { - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - sint32 rRegResult = tempToRealRegister(imlInstruction->op_r_r_r.registerResult); - sint32 rRegOperand1 = tempToRealRegister(imlInstruction->op_r_r_r.registerA); - sint32 rRegOperand2 = tempToRealRegister(imlInstruction->op_r_r_r.registerB); - - x64Emit_mov_mem32_reg32(x64GenContext, REG_RSP, (uint32)offsetof(PPCInterpreter_t, temporaryGPR[0]), REG_EAX); - x64Emit_mov_mem32_reg32(x64GenContext, REG_RSP, (uint32)offsetof(PPCInterpreter_t, temporaryGPR[1]), REG_EDX); - // mov operand 2 to temp register - x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, rRegOperand2); - // mov operand1 to EAX - x64Gen_mov_reg64Low32_reg64Low32(x64GenContext, REG_EAX, rRegOperand1); - if( imlInstruction->operation == PPCREC_IML_OP_MULTIPLY_HIGH_SIGNED ) - { - // zero extend EAX to EDX:EAX - x64Gen_xor_reg64Low32_reg64Low32(x64GenContext, REG_EDX, REG_EDX); - } - else - { - // sign extend EAX to EDX:EAX - x64Gen_cdq(x64GenContext); - } - // multiply - if( imlInstruction->operation == PPCREC_IML_OP_MULTIPLY_HIGH_SIGNED ) - x64Gen_imul_reg64Low32(x64GenContext, REG_RESV_TEMP); - else - x64Gen_mul_reg64Low32(x64GenContext, REG_RESV_TEMP); - // result of multiplication is now stored in EDX:EAX, move it to result register - if( rRegResult != REG_EDX ) - x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, REG_EDX); - // restore EAX / EDX - if( rRegResult != REG_RAX ) - x64Emit_mov_reg64_mem32(x64GenContext, REG_EAX, REG_RSP, (uint32)offsetof(PPCInterpreter_t, temporaryGPR[0])); - if( rRegResult != REG_RDX ) - x64Emit_mov_reg64_mem32(x64GenContext, REG_EDX, REG_RSP, (uint32)offsetof(PPCInterpreter_t, temporaryGPR[1])); - // set cr bits if requested - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - if( imlInstruction->crMode != PPCREC_CR_MODE_LOGICAL ) - { - assert_dbg(); - } - x64Gen_test_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegResult); - PPCRecompilerX64Gen_updateCRLogical(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction); - } - } - else if( imlInstruction->operation == PPCREC_IML_OP_ORC ) - { - // registerResult = registerOperand1 | ~registerOperand2 - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - sint32 rRegResult = tempToRealRegister(imlInstruction->op_r_r_r.registerResult); - sint32 rRegOperand1 = tempToRealRegister(imlInstruction->op_r_r_r.registerA); - sint32 rRegOperand2 = tempToRealRegister(imlInstruction->op_r_r_r.registerB); - - x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, rRegOperand2); - x64Gen_not_reg64Low32(x64GenContext, REG_RESV_TEMP); - if( rRegResult != rRegOperand1 ) - x64Gen_mov_reg64Low32_reg64Low32(x64GenContext, rRegResult, rRegOperand1); - x64Gen_or_reg64Low32_reg64Low32(x64GenContext, rRegResult, REG_RESV_TEMP); - - // set cr bits if enabled - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - if( imlInstruction->crMode != PPCREC_CR_MODE_LOGICAL ) - { - assert_dbg(); - } - PPCRecompilerX64Gen_updateCRLogical(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction); - return true; - } - } - else - { - debug_printf("PPCRecompilerX64Gen_imlInstruction_r_r_r(): Unsupported operation 0x%x\n", imlInstruction->operation); - return false; - } - return true; -} - -bool PPCRecompilerX64Gen_imlInstruction_r_r_s32(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction) -{ - if( imlInstruction->operation == PPCREC_IML_OP_ADD ) - { - // registerResult = registerOperand + immS32 - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); - sint32 rRegResult = tempToRealRegister(imlInstruction->op_r_r_s32.registerResult); - sint32 rRegOperand = tempToRealRegister(imlInstruction->op_r_r_s32.registerA); - uint32 immU32 = (uint32)imlInstruction->op_r_r_s32.immS32; - if( rRegResult != rRegOperand ) - { - // copy value to destination register before doing addition - x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, rRegOperand); - } - x64Gen_add_reg64Low32_imm32(x64GenContext, rRegResult, (uint32)immU32); - } - else if( imlInstruction->operation == PPCREC_IML_OP_ADD_UPDATE_CARRY ) - { - // registerResult = registerOperand + immS32 - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - sint32 rRegResult = tempToRealRegister(imlInstruction->op_r_r_s32.registerResult); - sint32 rRegOperand = tempToRealRegister(imlInstruction->op_r_r_s32.registerA); - uint32 immU32 = (uint32)imlInstruction->op_r_r_s32.immS32; - if( rRegResult != rRegOperand ) - { - // copy value to destination register before doing addition - x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, rRegOperand); - } - x64Gen_add_reg64Low32_imm32(x64GenContext, rRegResult, (uint32)immU32); - // update carry flag - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_CARRY, REG_RSP, offsetof(PPCInterpreter_t, xer_ca)); - // set cr bits if enabled - if( imlInstruction->crRegister != PPC_REC_INVALID_REGISTER ) - { - if( imlInstruction->crMode != PPCREC_CR_MODE_LOGICAL ) - { - assert_dbg(); - } - sint32 crRegister = imlInstruction->crRegister; - //x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_SIGN, REG_RSP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_LT)); - //x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_SIGNED_GREATER, REG_RSP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_GT)); - //x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_EQUAL, REG_RSP, offsetof(PPCInterpreter_t, cr)+sizeof(uint8)*(crRegister*4+PPCREC_CR_BIT_EQ)); - PPCRecompilerX64Gen_updateCRLogical(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction); - } - } - else if( imlInstruction->operation == PPCREC_IML_OP_SUBFC ) - { - // registerResult = immS32 - registerOperand - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); - sint32 rRegResult = tempToRealRegister(imlInstruction->op_r_r_s32.registerResult); - sint32 rRegOperand = tempToRealRegister(imlInstruction->op_r_r_s32.registerA); - sint32 immS32 = (sint32)imlInstruction->op_r_r_s32.immS32; - if( rRegResult != rRegOperand ) - { - // copy value to destination register before doing addition - x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, rRegOperand); - } - // set carry to zero - x64Gen_mov_mem8Reg64_imm8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, xer_ca), 0); - // ((~a+b)<~a) == true -> ca = 1 - x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, rRegOperand); - x64Gen_not_reg64Low32(x64GenContext, REG_RESV_TEMP); - x64Gen_add_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, (uint32)immS32); - x64Gen_not_reg64Low32(x64GenContext, rRegOperand); - x64Gen_cmp_reg64Low32_reg64Low32(x64GenContext, REG_RESV_TEMP, rRegOperand); - x64Gen_not_reg64Low32(x64GenContext, rRegOperand); - sint32 jumpInstructionOffset1 = x64GenContext->codeBufferIndex; - x64Gen_jmpc_far(x64GenContext, X86_CONDITION_UNSIGNED_ABOVE_EQUAL, 0); - // reset carry flag + jump destination afterwards - x64Gen_mov_mem8Reg64_imm8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, xer_ca), 1); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset1, x64GenContext->codeBufferIndex); - // OR ((~a+b+1)<1) == true -> ca = 1 - x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, rRegOperand); - // todo: Optimize by reusing result in REG_RESV_TEMP from above and only add 1 - x64Gen_not_reg64Low32(x64GenContext, REG_RESV_TEMP); - x64Gen_add_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, (uint32)immS32); - x64Gen_add_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, 1); - x64Gen_cmp_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, 1); - sint32 jumpInstructionOffset2 = x64GenContext->codeBufferIndex; - x64Gen_jmpc_far(x64GenContext, X86_CONDITION_UNSIGNED_ABOVE_EQUAL, 0); - // reset carry flag + jump destination afterwards - x64Gen_mov_mem8Reg64_imm8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, xer_ca), 1); - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset2, x64GenContext->codeBufferIndex); - // do actual computation of value, note: a - b is equivalent to a + ~b + 1 - x64Gen_not_reg64Low32(x64GenContext, rRegResult); - x64Gen_add_reg64Low32_imm32(x64GenContext, rRegResult, (uint32)immS32 + 1); - } - else if( imlInstruction->operation == PPCREC_IML_OP_RLWIMI ) - { - // registerResult = ((registerResult<<op_r_r_s32.immS32; - uint32 mb = (vImm>>0)&0xFF; - uint32 me = (vImm>>8)&0xFF; - uint32 sh = (vImm>>16)&0xFF; - uint32 mask = ppc_mask(mb, me); - // save cr - cemu_assert_debug(imlInstruction->crRegister == PPC_REC_INVALID_REGISTER); - // copy rS to temporary register - x64Gen_mov_reg64_reg64(x64GenContext, REG_RESV_TEMP, tempToRealRegister(imlInstruction->op_r_r_s32.registerA)); - // rotate destination register - if( sh ) - x64Gen_rol_reg64Low32_imm8(x64GenContext, REG_RESV_TEMP, (uint8)sh&0x1F); - // AND destination register with inverted mask - x64Gen_and_reg64Low32_imm32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r_s32.registerResult), ~mask); - // AND temporary rS register with mask - x64Gen_and_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, mask); - // OR result with temporary - x64Gen_or_reg64Low32_reg64Low32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r_s32.registerResult), REG_RESV_TEMP); - } - else if( imlInstruction->operation == PPCREC_IML_OP_MULTIPLY_SIGNED ) - { - // registerResult = registerOperand * immS32 - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - sint32 rRegResult = tempToRealRegister(imlInstruction->op_r_r_s32.registerResult); - sint32 rRegOperand = tempToRealRegister(imlInstruction->op_r_r_s32.registerA); - sint32 immS32 = (uint32)imlInstruction->op_r_r_s32.immS32; - x64Gen_mov_reg64_imm64(x64GenContext, REG_RESV_TEMP, (sint64)immS32); // todo: Optimize - if( rRegResult != rRegOperand ) - x64Gen_mov_reg64_reg64(x64GenContext, rRegResult, rRegOperand); - x64Gen_imul_reg64Low32_reg64Low32(x64GenContext, rRegResult, REG_RESV_TEMP); - } - else if( imlInstruction->operation == PPCREC_IML_OP_SRAW ) - { - // registerResult = registerOperand>>SH and set xer ca flag - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - uint32 sh = (uint32)imlInstruction->op_r_r_s32.immS32; - // MOV registerResult, registerOperand (if different) - if( imlInstruction->op_r_r_s32.registerA != imlInstruction->op_r_r_s32.registerResult ) - x64Gen_mov_reg64_reg64(x64GenContext, tempToRealRegister(imlInstruction->op_r_r_s32.registerResult), tempToRealRegister(imlInstruction->op_r_r_s32.registerA)); - // todo: Detect if we don't need to update carry - // generic case - // TEST registerResult, (1<<(SH+1))-1 - uint32 caTestMask = 0; - if (sh >= 31) - caTestMask = 0x7FFFFFFF; - else - caTestMask = (1 << (sh)) - 1; - x64Gen_test_reg64Low32_imm32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r_s32.registerResult), caTestMask); - // SETNE/NZ [ESP+XER_CA] - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_NOT_EQUAL, REG_RSP, offsetof(PPCInterpreter_t, xer_ca)); - // SAR registerResult, SH - x64Gen_sar_reg64Low32_imm8(x64GenContext, tempToRealRegister(imlInstruction->op_r_r_s32.registerResult), sh); - // JNS (if sign not set) - sint32 jumpInstructionOffset = x64GenContext->codeBufferIndex; - x64Gen_jmpc_near(x64GenContext, X86_CONDITION_SIGN, 0); // todo: Can use 2-byte form of jump instruction here - // MOV BYTE [ESP+xer_ca], 0 - x64Gen_mov_mem8Reg64_imm8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, xer_ca), 0); - // jump destination - PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset, x64GenContext->codeBufferIndex); - // CR update - if (imlInstruction->crRegister != PPC_REC_INVALID_REGISTER) - { - sint32 crRegister = imlInstruction->crRegister; - x64Gen_test_reg64Low32_reg64Low32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r_s32.registerResult), tempToRealRegister(imlInstruction->op_r_r_s32.registerResult)); - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_SIGN, REG_RSP, offsetof(PPCInterpreter_t, cr) + sizeof(uint8)*(crRegister * 4 + PPCREC_CR_BIT_LT)); - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_SIGNED_GREATER, REG_RSP, offsetof(PPCInterpreter_t, cr) + sizeof(uint8)*(crRegister * 4 + PPCREC_CR_BIT_GT)); - x64Gen_setcc_mem8(x64GenContext, X86_CONDITION_EQUAL, REG_RSP, offsetof(PPCInterpreter_t, cr) + sizeof(uint8)*(crRegister * 4 + PPCREC_CR_BIT_EQ)); - } - } - else if( imlInstruction->operation == PPCREC_IML_OP_LEFT_SHIFT || - imlInstruction->operation == PPCREC_IML_OP_RIGHT_SHIFT ) - { - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - // MOV registerResult, registerOperand (if different) - if( imlInstruction->op_r_r_s32.registerA != imlInstruction->op_r_r_s32.registerResult ) - x64Gen_mov_reg64_reg64(x64GenContext, tempToRealRegister(imlInstruction->op_r_r_s32.registerResult), tempToRealRegister(imlInstruction->op_r_r_s32.registerA)); - // Shift - if( imlInstruction->operation == PPCREC_IML_OP_LEFT_SHIFT ) - x64Gen_shl_reg64Low32_imm8(x64GenContext, tempToRealRegister(imlInstruction->op_r_r_s32.registerResult), imlInstruction->op_r_r_s32.immS32); - else - x64Gen_shr_reg64Low32_imm8(x64GenContext, tempToRealRegister(imlInstruction->op_r_r_s32.registerResult), imlInstruction->op_r_r_s32.immS32); - // CR update - if (imlInstruction->crRegister != PPC_REC_INVALID_REGISTER) - { - // since SHL/SHR only modifies the OF flag we need another TEST reg,reg here - x64Gen_test_reg64Low32_reg64Low32(x64GenContext, tempToRealRegister(imlInstruction->op_r_r_s32.registerResult), tempToRealRegister(imlInstruction->op_r_r_s32.registerResult)); - PPCRecompilerX64Gen_updateCRLogical(PPCRecFunction, ppcImlGenContext, x64GenContext, imlInstruction); - } - } - else - { - debug_printf("PPCRecompilerX64Gen_imlInstruction_r_r_s32(): Unsupported operation 0x%x\n", imlInstruction->operation); - return false; - } - return true; -} - -bool PPCRecompilerX64Gen_imlInstruction_conditionalJump(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlSegment_t* imlSegment, PPCRecImlInstruction_t* imlInstruction) -{ - if( imlInstruction->op_conditionalJump.condition == PPCREC_JUMP_CONDITION_NONE ) - { - // jump always - if (imlInstruction->op_conditionalJump.jumpAccordingToSegment) - { - // jump to segment - if (imlSegment->nextSegmentBranchTaken == nullptr) - assert_dbg(); - PPCRecompilerX64Gen_rememberRelocatableOffset(x64GenContext, X64_RELOC_LINK_TO_SEGMENT, imlSegment->nextSegmentBranchTaken); - x64Gen_jmp_imm32(x64GenContext, 0); - } - else - { - // deprecated (jump to jumpmark) - PPCRecompilerX64Gen_rememberRelocatableOffset(x64GenContext, X64_RELOC_LINK_TO_PPC, (void*)(size_t)imlInstruction->op_conditionalJump.jumpmarkAddress); - x64Gen_jmp_imm32(x64GenContext, 0); - } - } - else - { - if (imlInstruction->op_conditionalJump.jumpAccordingToSegment) - assert_dbg(); - // generate jump update marker - if( imlInstruction->op_conditionalJump.crRegisterIndex == PPCREC_CR_TEMPORARY || imlInstruction->op_conditionalJump.crRegisterIndex >= 8 ) - { - // temporary cr is used, which means we use the currently active eflags - PPCRecompilerX64Gen_rememberRelocatableOffset(x64GenContext, X64_RELOC_LINK_TO_PPC, (void*)(size_t)imlInstruction->op_conditionalJump.jumpmarkAddress); - sint32 condition = imlInstruction->op_conditionalJump.condition; - if( condition == PPCREC_JUMP_CONDITION_E ) - x64Gen_jmpc_far(x64GenContext, X86_CONDITION_EQUAL, 0); - else if( condition == PPCREC_JUMP_CONDITION_NE ) - x64Gen_jmpc_far(x64GenContext, X86_CONDITION_NOT_EQUAL, 0); - else - assert_dbg(); - } - else - { - uint8 crBitIndex = imlInstruction->op_conditionalJump.crRegisterIndex*4 + imlInstruction->op_conditionalJump.crBitIndex; - if (imlInstruction->op_conditionalJump.crRegisterIndex == x64GenContext->activeCRRegister ) - { - if (x64GenContext->activeCRState == PPCREC_CR_STATE_TYPE_UNSIGNED_ARITHMETIC) - { - if (imlInstruction->op_conditionalJump.crBitIndex == CR_BIT_LT) - { - PPCRecompilerX64Gen_rememberRelocatableOffset(x64GenContext, X64_RELOC_LINK_TO_PPC, (void*)(size_t)imlInstruction->op_conditionalJump.jumpmarkAddress); - x64Gen_jmpc_far(x64GenContext, imlInstruction->op_conditionalJump.bitMustBeSet ? X86_CONDITION_CARRY : X86_CONDITION_NOT_CARRY, 0); - return true; - } - else if (imlInstruction->op_conditionalJump.crBitIndex == CR_BIT_EQ) - { - PPCRecompilerX64Gen_rememberRelocatableOffset(x64GenContext, X64_RELOC_LINK_TO_PPC, (void*)(size_t)imlInstruction->op_conditionalJump.jumpmarkAddress); - x64Gen_jmpc_far(x64GenContext, imlInstruction->op_conditionalJump.bitMustBeSet ? X86_CONDITION_EQUAL : X86_CONDITION_NOT_EQUAL, 0); - return true; - } - else if (imlInstruction->op_conditionalJump.crBitIndex == CR_BIT_GT) - { - PPCRecompilerX64Gen_rememberRelocatableOffset(x64GenContext, X64_RELOC_LINK_TO_PPC, (void*)(size_t)imlInstruction->op_conditionalJump.jumpmarkAddress); - x64Gen_jmpc_far(x64GenContext, imlInstruction->op_conditionalJump.bitMustBeSet ? X86_CONDITION_UNSIGNED_ABOVE : X86_CONDITION_UNSIGNED_BELOW_EQUAL, 0); - return true; - } - } - else if (x64GenContext->activeCRState == PPCREC_CR_STATE_TYPE_SIGNED_ARITHMETIC) - { - if (imlInstruction->op_conditionalJump.crBitIndex == CR_BIT_LT) - { - PPCRecompilerX64Gen_rememberRelocatableOffset(x64GenContext, X64_RELOC_LINK_TO_PPC, (void*)(size_t)imlInstruction->op_conditionalJump.jumpmarkAddress); - x64Gen_jmpc_far(x64GenContext, imlInstruction->op_conditionalJump.bitMustBeSet ? X86_CONDITION_SIGNED_LESS : X86_CONDITION_SIGNED_GREATER_EQUAL, 0); - return true; - } - else if (imlInstruction->op_conditionalJump.crBitIndex == CR_BIT_EQ) - { - PPCRecompilerX64Gen_rememberRelocatableOffset(x64GenContext, X64_RELOC_LINK_TO_PPC, (void*)(size_t)imlInstruction->op_conditionalJump.jumpmarkAddress); - x64Gen_jmpc_far(x64GenContext, imlInstruction->op_conditionalJump.bitMustBeSet ? X86_CONDITION_EQUAL : X86_CONDITION_NOT_EQUAL, 0); - return true; - } - else if (imlInstruction->op_conditionalJump.crBitIndex == CR_BIT_GT) - { - PPCRecompilerX64Gen_rememberRelocatableOffset(x64GenContext, X64_RELOC_LINK_TO_PPC, (void*)(size_t)imlInstruction->op_conditionalJump.jumpmarkAddress); - x64Gen_jmpc_far(x64GenContext, imlInstruction->op_conditionalJump.bitMustBeSet ? X86_CONDITION_SIGNED_GREATER : X86_CONDITION_SIGNED_LESS_EQUAL, 0); - return true; - } - } - else if (x64GenContext->activeCRState == PPCREC_CR_STATE_TYPE_LOGICAL) - { - if (imlInstruction->op_conditionalJump.crBitIndex == CR_BIT_LT) - { - PPCRecompilerX64Gen_rememberRelocatableOffset(x64GenContext, X64_RELOC_LINK_TO_PPC, (void*)(size_t)imlInstruction->op_conditionalJump.jumpmarkAddress); - x64Gen_jmpc_far(x64GenContext, imlInstruction->op_conditionalJump.bitMustBeSet ? X86_CONDITION_SIGN : X86_CONDITION_NOT_SIGN, 0); - return true; - } - else if (imlInstruction->op_conditionalJump.crBitIndex == CR_BIT_EQ) - { - PPCRecompilerX64Gen_rememberRelocatableOffset(x64GenContext, X64_RELOC_LINK_TO_PPC, (void*)(size_t)imlInstruction->op_conditionalJump.jumpmarkAddress); - x64Gen_jmpc_far(x64GenContext, imlInstruction->op_conditionalJump.bitMustBeSet ? X86_CONDITION_EQUAL : X86_CONDITION_NOT_EQUAL, 0); - return true; - } - else if (imlInstruction->op_conditionalJump.crBitIndex == CR_BIT_GT) - { - PPCRecompilerX64Gen_rememberRelocatableOffset(x64GenContext, X64_RELOC_LINK_TO_PPC, (void*)(size_t)imlInstruction->op_conditionalJump.jumpmarkAddress); - x64Gen_jmpc_far(x64GenContext, imlInstruction->op_conditionalJump.bitMustBeSet ? X86_CONDITION_SIGNED_GREATER : X86_CONDITION_SIGNED_LESS_EQUAL, 0); - return true; - } - } - } - x64Gen_bt_mem8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, cr) + crBitIndex * sizeof(uint8), 0); - PPCRecompilerX64Gen_rememberRelocatableOffset(x64GenContext, X64_RELOC_LINK_TO_PPC, (void*)(size_t)imlInstruction->op_conditionalJump.jumpmarkAddress); - if( imlInstruction->op_conditionalJump.bitMustBeSet ) - { - x64Gen_jmpc_far(x64GenContext, X86_CONDITION_CARRY, 0); - } - else - { - x64Gen_jmpc_far(x64GenContext, X86_CONDITION_NOT_CARRY, 0); - } - } - } - return true; -} - -bool PPCRecompilerX64Gen_imlInstruction_conditionalJumpCycleCheck(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction) -{ - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); - // some tests (all performed on a i7-4790K) - // 1) DEC [mem] + JNS has significantly worse performance than BT + JNC (probably due to additional memory write) - // 2) CMP [mem], 0 + JG has about equal (or slightly worse) performance than BT + JNC - - // BT - x64Gen_bt_mem8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, remainingCycles), 31); // check if negative - PPCRecompilerX64Gen_rememberRelocatableOffset(x64GenContext, X64_RELOC_LINK_TO_PPC, (void*)(size_t)imlInstruction->op_conditionalJump.jumpmarkAddress); - x64Gen_jmpc_far(x64GenContext, X86_CONDITION_NOT_CARRY, 0); - return true; -} - -/* -* PPC condition register operation -*/ -bool PPCRecompilerX64Gen_imlInstruction_cr(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction) -{ - PPCRecompilerX64Gen_crConditionFlags_forget(PPCRecFunction, ppcImlGenContext, x64GenContext); // while these instruction do not directly affect eflags, they change the CR bit - if (imlInstruction->operation == PPCREC_IML_OP_CR_CLEAR) - { - // clear cr bit - x64Gen_mov_mem8Reg64_imm8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, cr) + sizeof(uint8)*imlInstruction->op_cr.crD, 0); - return true; - } - else if (imlInstruction->operation == PPCREC_IML_OP_CR_SET) - { - // set cr bit - x64Gen_mov_mem8Reg64_imm8(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, cr) + sizeof(uint8)*imlInstruction->op_cr.crD, 1); - return true; - } - else if(imlInstruction->operation == PPCREC_IML_OP_CR_OR || imlInstruction->operation == PPCREC_IML_OP_CR_ORC || - imlInstruction->operation == PPCREC_IML_OP_CR_AND || imlInstruction->operation == PPCREC_IML_OP_CR_ANDC ) - { - x64Emit_movZX_reg64_mem8(x64GenContext, REG_RESV_TEMP, REG_RSP, offsetof(PPCInterpreter_t, cr) + sizeof(uint8)*imlInstruction->op_cr.crB); - if (imlInstruction->operation == PPCREC_IML_OP_CR_ORC || imlInstruction->operation == PPCREC_IML_OP_CR_ANDC) - { - return false; // untested - x64Gen_int3(x64GenContext); - x64Gen_xor_reg64Low32_imm32(x64GenContext, REG_RESV_TEMP, 1); // complement - } - if(imlInstruction->operation == PPCREC_IML_OP_CR_OR || imlInstruction->operation == PPCREC_IML_OP_CR_ORC) - x64Gen_or_reg64Low8_mem8Reg64(x64GenContext, REG_RESV_TEMP, REG_RSP, offsetof(PPCInterpreter_t, cr) + sizeof(uint8)*imlInstruction->op_cr.crA); - else - x64Gen_and_reg64Low8_mem8Reg64(x64GenContext, REG_RESV_TEMP, REG_RSP, offsetof(PPCInterpreter_t, cr) + sizeof(uint8)*imlInstruction->op_cr.crA); - - x64Gen_mov_mem8Reg64_reg64Low8(x64GenContext, REG_RESV_TEMP, REG_RSP, offsetof(PPCInterpreter_t, cr) + sizeof(uint8)*imlInstruction->op_cr.crD); - - return true; - } - else - { - assert_dbg(); - } - return false; -} - - -void PPCRecompilerX64Gen_imlInstruction_ppcEnter(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction) -{ - imlInstruction->op_ppcEnter.x64Offset = x64GenContext->codeBufferIndex; - // generate code - if( ppcImlGenContext->hasFPUInstruction ) - { - // old FPU unavailable code - //PPCRecompilerX86_crConditionFlags_saveBeforeOverwrite(PPCRecFunction, ppcImlGenContext, x64GenContext); - //// skip if FP bit in MSR is set - //// #define MSR_FP (1<<13) - //x64Gen_bt_mem8(x64GenContext, REG_ESP, offsetof(PPCInterpreter_t, msr), 13); - //uint32 jmpCodeOffset = x64GenContext->codeBufferIndex; - //x64Gen_jmpc(x64GenContext, X86_CONDITION_CARRY, 0); - //x64Gen_mov_reg32_imm32(x64GenContext, REG_EAX, imlInstruction->op_ppcEnter.ppcAddress&0x7FFFFFFF); - //PPCRecompilerX64Gen_rememberRelocatableOffset(x64GenContext, X86_RELOC_MAKE_RELATIVE); - //x64Gen_jmp_imm32(x64GenContext, (uint32)PPCRecompiler_recompilerCallEscapeAndCallFPUUnavailable); - //// patch jump - //*(uint32*)(x64GenContext->codeBuffer+jmpCodeOffset+2) = x64GenContext->codeBufferIndex-jmpCodeOffset-6; - } -} - -void PPCRecompilerX64Gen_imlInstruction_r_name(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction) -{ - uint32 name = imlInstruction->op_r_name.name; - if( name >= PPCREC_NAME_R0 && name < PPCREC_NAME_R0+32 ) - { - x64Emit_mov_reg64_mem32(x64GenContext, tempToRealRegister(imlInstruction->op_r_name.registerIndex), REG_RSP, offsetof(PPCInterpreter_t, gpr)+sizeof(uint32)*(name-PPCREC_NAME_R0)); - } - else if( name >= PPCREC_NAME_SPR0 && name < PPCREC_NAME_SPR0+999 ) - { - sint32 sprIndex = (name - PPCREC_NAME_SPR0); - if (sprIndex == SPR_LR) - x64Emit_mov_reg64_mem32(x64GenContext, tempToRealRegister(imlInstruction->op_r_name.registerIndex), REG_RSP, offsetof(PPCInterpreter_t, spr.LR)); - else if (sprIndex == SPR_CTR) - x64Emit_mov_reg64_mem32(x64GenContext, tempToRealRegister(imlInstruction->op_r_name.registerIndex), REG_RSP, offsetof(PPCInterpreter_t, spr.CTR)); - else if (sprIndex == SPR_XER) - x64Emit_mov_reg64_mem32(x64GenContext, tempToRealRegister(imlInstruction->op_r_name.registerIndex), REG_RSP, offsetof(PPCInterpreter_t, spr.XER)); - else if (sprIndex >= SPR_UGQR0 && sprIndex <= SPR_UGQR7) - { - sint32 memOffset = offsetof(PPCInterpreter_t, spr.UGQR) + sizeof(PPCInterpreter_t::spr.UGQR[0]) * (sprIndex - SPR_UGQR0); - x64Emit_mov_reg64_mem32(x64GenContext, tempToRealRegister(imlInstruction->op_r_name.registerIndex), REG_RSP, memOffset); - } - else - assert_dbg(); - //x64Emit_mov_reg64_mem32(x64GenContext, tempToRealRegister(imlInstruction->op_r_name.registerIndex), REG_RSP, offsetof(PPCInterpreter_t, spr)+sizeof(uint32)*(name-PPCREC_NAME_SPR0)); - } - else - assert_dbg(); -} - -void PPCRecompilerX64Gen_imlInstruction_name_r(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, PPCRecImlInstruction_t* imlInstruction) -{ - uint32 name = imlInstruction->op_r_name.name; - if( name >= PPCREC_NAME_R0 && name < PPCREC_NAME_R0+32 ) - { - x64Emit_mov_mem32_reg64(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, gpr)+sizeof(uint32)*(name-PPCREC_NAME_R0), tempToRealRegister(imlInstruction->op_r_name.registerIndex)); - } - else if( name >= PPCREC_NAME_SPR0 && name < PPCREC_NAME_SPR0+999 ) - { - uint32 sprIndex = (name - PPCREC_NAME_SPR0); - if (sprIndex == SPR_LR) - x64Emit_mov_mem32_reg64(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, spr.LR), tempToRealRegister(imlInstruction->op_r_name.registerIndex)); - else if (sprIndex == SPR_CTR) - x64Emit_mov_mem32_reg64(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, spr.CTR), tempToRealRegister(imlInstruction->op_r_name.registerIndex)); - else if (sprIndex == SPR_XER) - x64Emit_mov_mem32_reg64(x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, spr.XER), tempToRealRegister(imlInstruction->op_r_name.registerIndex)); - else if (sprIndex >= SPR_UGQR0 && sprIndex <= SPR_UGQR7) - { - sint32 memOffset = offsetof(PPCInterpreter_t, spr.UGQR) + sizeof(PPCInterpreter_t::spr.UGQR[0]) * (sprIndex - SPR_UGQR0); - x64Emit_mov_mem32_reg64(x64GenContext, REG_RSP, memOffset, tempToRealRegister(imlInstruction->op_r_name.registerIndex)); - } - else - assert_dbg(); - } - else - assert_dbg(); -} - -uint8* codeMemoryBlock = nullptr; -sint32 codeMemoryBlockIndex = 0; -sint32 codeMemoryBlockSize = 0; - -std::mutex mtx_allocExecutableMemory; - -uint8* PPCRecompilerX86_allocateExecutableMemory(sint32 size) -{ - std::lock_guard lck(mtx_allocExecutableMemory); - if( codeMemoryBlockIndex+size > codeMemoryBlockSize ) - { - // allocate new block - codeMemoryBlockSize = std::max(1024*1024*4, size+1024); // 4MB (or more if the function is larger than 4MB) - codeMemoryBlockIndex = 0; - codeMemoryBlock = (uint8*)MemMapper::AllocateMemory(nullptr, codeMemoryBlockSize, MemMapper::PAGE_PERMISSION::P_RWX); - } - uint8* codeMem = codeMemoryBlock + codeMemoryBlockIndex; - codeMemoryBlockIndex += size; - // pad to 4 byte alignment - while (codeMemoryBlockIndex & 3) - { - codeMemoryBlock[codeMemoryBlockIndex] = 0x90; - codeMemoryBlockIndex++; - } - return codeMem; -} - -void PPCRecompiler_dumpIML(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext); - -bool PPCRecompiler_generateX64Code(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext) -{ - x64GenContext_t x64GenContext = {0}; - x64GenContext.codeBufferSize = 1024; - x64GenContext.codeBuffer = (uint8*)malloc(x64GenContext.codeBufferSize); - x64GenContext.codeBufferIndex = 0; - x64GenContext.activeCRRegister = PPC_REC_INVALID_REGISTER; - - // generate iml instruction code - bool codeGenerationFailed = false; - for(sint32 s=0; ssegmentListCount; s++) - { - PPCRecImlSegment_t* imlSegment = ppcImlGenContext->segmentList[s]; - ppcImlGenContext->segmentList[s]->x64Offset = x64GenContext.codeBufferIndex; - for(sint32 i=0; iimlListCount; i++) - { - PPCRecImlInstruction_t* imlInstruction = imlSegment->imlList+i; - - if( imlInstruction->type == PPCREC_IML_TYPE_R_NAME ) - { - PPCRecompilerX64Gen_imlInstruction_r_name(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction); - } - else if( imlInstruction->type == PPCREC_IML_TYPE_NAME_R ) - { - PPCRecompilerX64Gen_imlInstruction_name_r(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction); - } - else if( imlInstruction->type == PPCREC_IML_TYPE_R_R ) - { - if( PPCRecompilerX64Gen_imlInstruction_r_r(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction) == false ) - { - codeGenerationFailed = true; - } - } - else if (imlInstruction->type == PPCREC_IML_TYPE_R_S32) - { - if (PPCRecompilerX64Gen_imlInstruction_r_s32(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction) == false) - { - codeGenerationFailed = true; - } - } - else if (imlInstruction->type == PPCREC_IML_TYPE_CONDITIONAL_R_S32) - { - if (PPCRecompilerX64Gen_imlInstruction_conditional_r_s32(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction) == false) - { - codeGenerationFailed = true; - } - } - else if( imlInstruction->type == PPCREC_IML_TYPE_R_R_S32 ) - { - if( PPCRecompilerX64Gen_imlInstruction_r_r_s32(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction) == false ) - { - codeGenerationFailed = true; - } - } - else if( imlInstruction->type == PPCREC_IML_TYPE_R_R_R ) - { - if( PPCRecompilerX64Gen_imlInstruction_r_r_r(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction) == false ) - { - codeGenerationFailed = true; - } - } - else if( imlInstruction->type == PPCREC_IML_TYPE_CJUMP ) - { - if( PPCRecompilerX64Gen_imlInstruction_conditionalJump(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlSegment, imlInstruction) == false ) - { - codeGenerationFailed = true; - } - } - else if( imlInstruction->type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK ) - { - PPCRecompilerX64Gen_imlInstruction_conditionalJumpCycleCheck(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction); - } - else if( imlInstruction->type == PPCREC_IML_TYPE_MACRO ) - { - if( PPCRecompilerX64Gen_imlInstruction_macro(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction) == false ) - { - codeGenerationFailed = true; - } - } - else if( imlInstruction->type == PPCREC_IML_TYPE_LOAD ) - { - if( PPCRecompilerX64Gen_imlInstruction_load(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction, false) == false ) - { - codeGenerationFailed = true; - } - } - else if( imlInstruction->type == PPCREC_IML_TYPE_LOAD_INDEXED ) - { - if( PPCRecompilerX64Gen_imlInstruction_load(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction, true) == false ) - { - codeGenerationFailed = true; - } - } - else if( imlInstruction->type == PPCREC_IML_TYPE_STORE ) - { - if( PPCRecompilerX64Gen_imlInstruction_store(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction, false) == false ) - { - codeGenerationFailed = true; - } - } - else if( imlInstruction->type == PPCREC_IML_TYPE_STORE_INDEXED ) - { - if( PPCRecompilerX64Gen_imlInstruction_store(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction, true) == false ) - { - codeGenerationFailed = true; - } - } - else if (imlInstruction->type == PPCREC_IML_TYPE_MEM2MEM) - { - PPCRecompilerX64Gen_imlInstruction_mem2mem(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction); - } - else if( imlInstruction->type == PPCREC_IML_TYPE_CR ) - { - if( PPCRecompilerX64Gen_imlInstruction_cr(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction) == false ) - { - codeGenerationFailed = true; - } - } - else if( imlInstruction->type == PPCREC_IML_TYPE_JUMPMARK ) - { - // no op - } - else if( imlInstruction->type == PPCREC_IML_TYPE_NO_OP ) - { - // no op - } - else if( imlInstruction->type == PPCREC_IML_TYPE_PPC_ENTER ) - { - PPCRecompilerX64Gen_imlInstruction_ppcEnter(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction); - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_R_NAME ) - { - PPCRecompilerX64Gen_imlInstruction_fpr_r_name(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction); - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_NAME_R ) - { - PPCRecompilerX64Gen_imlInstruction_fpr_name_r(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction); - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_LOAD ) - { - if( PPCRecompilerX64Gen_imlInstruction_fpr_load(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction, false) == false ) - { - codeGenerationFailed = true; - } - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_LOAD_INDEXED ) - { - if( PPCRecompilerX64Gen_imlInstruction_fpr_load(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction, true) == false ) - { - codeGenerationFailed = true; - } - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_STORE ) - { - if( PPCRecompilerX64Gen_imlInstruction_fpr_store(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction, false) == false ) - { - codeGenerationFailed = true; - } - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_STORE_INDEXED ) - { - if( PPCRecompilerX64Gen_imlInstruction_fpr_store(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction, true) == false ) - { - codeGenerationFailed = true; - } - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_R_R ) - { - PPCRecompilerX64Gen_imlInstruction_fpr_r_r(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction); - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_R_R_R ) - { - PPCRecompilerX64Gen_imlInstruction_fpr_r_r_r(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction); - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_R_R_R_R ) - { - PPCRecompilerX64Gen_imlInstruction_fpr_r_r_r_r(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction); - } - else if( imlInstruction->type == PPCREC_IML_TYPE_FPR_R ) - { - PPCRecompilerX64Gen_imlInstruction_fpr_r(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction); - } - else - { - debug_printf("PPCRecompiler_generateX64Code(): Unsupported iml type 0x%x\n", imlInstruction->type); - assert_dbg(); - } - } - } - // handle failed code generation - if( codeGenerationFailed ) - { - free(x64GenContext.codeBuffer); - if (x64GenContext.relocateOffsetTable) - free(x64GenContext.relocateOffsetTable); - return false; - } - // allocate executable memory - uint8* executableMemory = PPCRecompilerX86_allocateExecutableMemory(x64GenContext.codeBufferIndex); - size_t baseAddress = (size_t)executableMemory; - // fix relocs - for(sint32 i=0; isegmentListCount; s++) - { - if (ppcImlGenContext->segmentList[s]->isJumpDestination && ppcImlGenContext->segmentList[s]->jumpDestinationPPCAddress == ppcOffset) - { - x64Offset = ppcImlGenContext->segmentList[s]->x64Offset; - break; - } - } - if (x64Offset == 0xFFFFFFFF) - { - debug_printf("Recompiler could not resolve jump (function at 0x%08x)\n", PPCRecFunction->ppcAddress); - // todo: Cleanup - return false; - } - } - else - { - PPCRecImlSegment_t* destSegment = (PPCRecImlSegment_t*)x64GenContext.relocateOffsetTable[i].extraInfo; - x64Offset = destSegment->x64Offset; - } - uint32 relocBase = x64GenContext.relocateOffsetTable[i].offset; - uint8* relocInstruction = x64GenContext.codeBuffer+relocBase; - if( relocInstruction[0] == 0x0F && (relocInstruction[1] >= 0x80 && relocInstruction[1] <= 0x8F) ) - { - // Jcc relativeImm32 - sint32 distanceNearJump = (sint32)((baseAddress + x64Offset) - (baseAddress + relocBase + 2)); - if (distanceNearJump >= -128 && distanceNearJump < 127) // disabled - { - // convert to near Jcc - *(uint8*)(relocInstruction + 0) = (uint8)(relocInstruction[1]-0x80 + 0x70); - // patch offset - *(uint8*)(relocInstruction + 1) = (uint8)distanceNearJump; - // replace unused 4 bytes with NOP instruction - relocInstruction[2] = 0x0F; - relocInstruction[3] = 0x1F; - relocInstruction[4] = 0x40; - relocInstruction[5] = 0x00; - } - else - { - // patch offset - *(uint32*)(relocInstruction + 2) = (uint32)((baseAddress + x64Offset) - (baseAddress + relocBase + 6)); - } - } - else if( relocInstruction[0] == 0xE9 ) - { - // JMP relativeImm32 - *(uint32*)(relocInstruction+1) = (uint32)((baseAddress+x64Offset)-(baseAddress+relocBase+5)); - } - else - assert_dbg(); - } - else - { - assert_dbg(); - } - } - - // copy code to executable memory - memcpy(executableMemory, x64GenContext.codeBuffer, x64GenContext.codeBufferIndex); - free(x64GenContext.codeBuffer); - x64GenContext.codeBuffer = nullptr; - if (x64GenContext.relocateOffsetTable) - free(x64GenContext.relocateOffsetTable); - // set code - PPCRecFunction->x86Code = executableMemory; - PPCRecFunction->x86Size = x64GenContext.codeBufferIndex; - return true; -} - -void PPCRecompilerX64Gen_generateEnterRecompilerCode() -{ - x64GenContext_t x64GenContext = {0}; - x64GenContext.codeBufferSize = 1024; - x64GenContext.codeBuffer = (uint8*)malloc(x64GenContext.codeBufferSize); - x64GenContext.codeBufferIndex = 0; - x64GenContext.activeCRRegister = PPC_REC_INVALID_REGISTER; - - // start of recompiler entry function - x64Gen_push_reg64(&x64GenContext, REG_RAX); - x64Gen_push_reg64(&x64GenContext, REG_RCX); - x64Gen_push_reg64(&x64GenContext, REG_RDX); - x64Gen_push_reg64(&x64GenContext, REG_RBX); - x64Gen_push_reg64(&x64GenContext, REG_RBP); - x64Gen_push_reg64(&x64GenContext, REG_RDI); - x64Gen_push_reg64(&x64GenContext, REG_RSI); - x64Gen_push_reg64(&x64GenContext, REG_R8); - x64Gen_push_reg64(&x64GenContext, REG_R9); - x64Gen_push_reg64(&x64GenContext, REG_R10); - x64Gen_push_reg64(&x64GenContext, REG_R11); - x64Gen_push_reg64(&x64GenContext, REG_R12); - x64Gen_push_reg64(&x64GenContext, REG_R13); - x64Gen_push_reg64(&x64GenContext, REG_R14); - x64Gen_push_reg64(&x64GenContext, REG_R15); - - // 000000007775EF04 | E8 00 00 00 00 call +0x00 - x64Gen_writeU8(&x64GenContext, 0xE8); - x64Gen_writeU8(&x64GenContext, 0x00); - x64Gen_writeU8(&x64GenContext, 0x00); - x64Gen_writeU8(&x64GenContext, 0x00); - x64Gen_writeU8(&x64GenContext, 0x00); - //000000007775EF09 | 48 83 04 24 05 add qword ptr ss:[rsp],5 - x64Gen_writeU8(&x64GenContext, 0x48); - x64Gen_writeU8(&x64GenContext, 0x83); - x64Gen_writeU8(&x64GenContext, 0x04); - x64Gen_writeU8(&x64GenContext, 0x24); - uint32 jmpPatchOffset = x64GenContext.codeBufferIndex; - x64Gen_writeU8(&x64GenContext, 0); // skip the distance until after the JMP - x64Emit_mov_mem64_reg64(&x64GenContext, REG_RDX, offsetof(PPCInterpreter_t, rspTemp), REG_RSP); - - - // MOV RSP, RDX (ppc interpreter instance) - x64Gen_mov_reg64_reg64(&x64GenContext, REG_RSP, REG_RDX); - // MOV R15, ppcRecompilerInstanceData - x64Gen_mov_reg64_imm64(&x64GenContext, REG_R15, (uint64)ppcRecompilerInstanceData); - // MOV R13, memory_base - x64Gen_mov_reg64_imm64(&x64GenContext, REG_R13, (uint64)memory_base); - - //JMP recFunc - x64Gen_jmp_reg64(&x64GenContext, REG_RCX); // call argument 1 - - x64GenContext.codeBuffer[jmpPatchOffset] = (x64GenContext.codeBufferIndex-(jmpPatchOffset-4)); - - //recompilerExit1: - x64Gen_pop_reg64(&x64GenContext, REG_R15); - x64Gen_pop_reg64(&x64GenContext, REG_R14); - x64Gen_pop_reg64(&x64GenContext, REG_R13); - x64Gen_pop_reg64(&x64GenContext, REG_R12); - x64Gen_pop_reg64(&x64GenContext, REG_R11); - x64Gen_pop_reg64(&x64GenContext, REG_R10); - x64Gen_pop_reg64(&x64GenContext, REG_R9); - x64Gen_pop_reg64(&x64GenContext, REG_R8); - x64Gen_pop_reg64(&x64GenContext, REG_RSI); - x64Gen_pop_reg64(&x64GenContext, REG_RDI); - x64Gen_pop_reg64(&x64GenContext, REG_RBP); - x64Gen_pop_reg64(&x64GenContext, REG_RBX); - x64Gen_pop_reg64(&x64GenContext, REG_RDX); - x64Gen_pop_reg64(&x64GenContext, REG_RCX); - x64Gen_pop_reg64(&x64GenContext, REG_RAX); - // RET - x64Gen_ret(&x64GenContext); - - uint8* executableMemory = PPCRecompilerX86_allocateExecutableMemory(x64GenContext.codeBufferIndex); - // copy code to executable memory - memcpy(executableMemory, x64GenContext.codeBuffer, x64GenContext.codeBufferIndex); - free(x64GenContext.codeBuffer); - PPCRecompiler_enterRecompilerCode = (void ATTR_MS_ABI (*)(uint64,uint64))executableMemory; -} - - -void* PPCRecompilerX64Gen_generateLeaveRecompilerCode() -{ - x64GenContext_t x64GenContext = {0}; - x64GenContext.codeBufferSize = 128; - x64GenContext.codeBuffer = (uint8*)malloc(x64GenContext.codeBufferSize); - x64GenContext.codeBufferIndex = 0; - x64GenContext.activeCRRegister = PPC_REC_INVALID_REGISTER; - - // update instruction pointer - // LR is in EDX - x64Emit_mov_mem32_reg32(&x64GenContext, REG_RSP, offsetof(PPCInterpreter_t, instructionPointer), REG_EDX); - - // MOV RSP, [ppcRecompilerX64_rspTemp] - x64Emit_mov_reg64_mem64(&x64GenContext, REG_RSP, REG_RESV_HCPU, offsetof(PPCInterpreter_t, rspTemp)); - - // RET - x64Gen_ret(&x64GenContext); - - uint8* executableMemory = PPCRecompilerX86_allocateExecutableMemory(x64GenContext.codeBufferIndex); - // copy code to executable memory - memcpy(executableMemory, x64GenContext.codeBuffer, x64GenContext.codeBufferIndex); - free(x64GenContext.codeBuffer); - return executableMemory; -} - -void PPCRecompilerX64Gen_generateRecompilerInterfaceFunctions() -{ - PPCRecompilerX64Gen_generateEnterRecompilerCode(); - PPCRecompiler_leaveRecompilerCode_unvisited = (void ATTR_MS_ABI (*)())PPCRecompilerX64Gen_generateLeaveRecompilerCode(); - PPCRecompiler_leaveRecompilerCode_visited = (void ATTR_MS_ABI (*)())PPCRecompilerX64Gen_generateLeaveRecompilerCode(); - cemu_assert_debug(PPCRecompiler_leaveRecompilerCode_unvisited != PPCRecompiler_leaveRecompilerCode_visited); -} \ No newline at end of file diff --git a/src/Cafe/HW/Latte/Core/LatteThread.cpp b/src/Cafe/HW/Latte/Core/LatteThread.cpp index 897f769c..e6fe29c8 100644 --- a/src/Cafe/HW/Latte/Core/LatteThread.cpp +++ b/src/Cafe/HW/Latte/Core/LatteThread.cpp @@ -226,6 +226,8 @@ void Latte_Start() void Latte_Stop() { std::unique_lock _lock(sLatteThreadStateMutex); + if (!sLatteThreadRunning) + return; sLatteThreadRunning = false; _lock.unlock(); sLatteThread.join(); diff --git a/src/Cemu/Logging/CemuLogging.h b/src/Cemu/Logging/CemuLogging.h index d55256c9..d5d0a4ea 100644 --- a/src/Cemu/Logging/CemuLogging.h +++ b/src/Cemu/Logging/CemuLogging.h @@ -35,9 +35,8 @@ enum class LogType : sint32 ProcUi = 40, + Recompiler = 60, APIErrors = 0, // alias for Force. Logs bad parameters or other API errors in OS libs - - }; template <> diff --git a/src/config/LaunchSettings.cpp b/src/config/LaunchSettings.cpp index 92289edd..8bed69f1 100644 --- a/src/config/LaunchSettings.cpp +++ b/src/config/LaunchSettings.cpp @@ -13,6 +13,7 @@ #include "util/crypto/aes128.h" #include "Cafe/Filesystem/FST/FST.h" +#include "util/helpers/StringHelpers.h" void requireConsole(); @@ -78,7 +79,9 @@ bool LaunchSettings::HandleCommandline(const std::vector& args) po::options_description hidden{ "Hidden options" }; hidden.add_options() ("nsight", po::value()->implicit_value(true), "NSight debugging options") - ("legacy", po::value()->implicit_value(true), "Intel legacy graphic mode"); + ("legacy", po::value()->implicit_value(true), "Intel legacy graphic mode") + ("ppcrec-lower-addr", po::value(), "For debugging: Lower address allowed for PPC recompilation") + ("ppcrec-upper-addr", po::value(), "For debugging: Upper address allowed for PPC recompilation"); po::options_description extractor{ "Extractor tool" }; extractor.add_options() @@ -202,6 +205,21 @@ bool LaunchSettings::HandleCommandline(const std::vector& args) if (vm.count("ecs-url")) serviceURL_ECS = vm["ecs-url"].as(); + // recompiler range limit for debugging + if (vm.count("ppcrec-lower-addr")) + { + uint32 addr = (uint32)StringHelpers::ToInt64(vm["ppcrec-lower-addr"].as()); + ppcRec_limitLowerAddr = addr; + } + if (vm.count("ppcrec-upper-addr")) + { + uint32 addr = (uint32)StringHelpers::ToInt64(vm["ppcrec-upper-addr"].as()); + ppcRec_limitUpperAddr = addr; + } + if(ppcRec_limitLowerAddr != 0 && ppcRec_limitUpperAddr != 0) + cemuLog_log(LogType::Force, "PPCRec range limited to 0x{:08x}-0x{:08x}", ppcRec_limitLowerAddr, ppcRec_limitUpperAddr); + + if(!extract_path.empty()) { ExtractorTool(extract_path, output_path, log_path); diff --git a/src/config/LaunchSettings.h b/src/config/LaunchSettings.h index f87dc609..ac054ab1 100644 --- a/src/config/LaunchSettings.h +++ b/src/config/LaunchSettings.h @@ -34,6 +34,9 @@ public: static std::string GetServiceURL_ecs() { return serviceURL_ECS; } static void ChangeNetworkServiceURL(int ID); + static uint32 GetPPCRecLowerAddr() { return ppcRec_limitLowerAddr; }; + static uint32 GetPPCRecUpperAddr() { return ppcRec_limitUpperAddr; }; + private: inline static std::optional s_load_game_file{}; inline static std::optional s_load_title_id{}; @@ -55,6 +58,9 @@ private: inline static std::string serviceURL_ECS; // todo - npts and other boss urls + // for recompiler debugging + inline static uint32 ppcRec_limitLowerAddr{}; + inline static uint32 ppcRec_limitUpperAddr{}; static bool ExtractorTool(std::wstring_view wud_path, std::string_view output_path, std::wstring_view log_path); }; diff --git a/src/util/helpers/StringBuf.h b/src/util/helpers/StringBuf.h index 432fa7a1..e2f723d3 100644 --- a/src/util/helpers/StringBuf.h +++ b/src/util/helpers/StringBuf.h @@ -80,6 +80,13 @@ public: } private: + void _reserve(uint32 newLimit) + { + cemu_assert_debug(newLimit > length); + this->str = (uint8*)realloc(this->str, newLimit + 4); + this->limit = newLimit; + } + uint8* str; uint32 length; /* in bytes */ uint32 limit; /* in bytes */