Cemu/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlGen.cpp
2025-04-26 00:24:43 +02:00

3265 lines
117 KiB
C++

#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 "IML/IML.h"
#include "IML/IMLRegisterAllocatorRanges.h"
#include "PPCFunctionBoundaryTracker.h"
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext);
struct PPCBasicBlockInfo
{
PPCBasicBlockInfo(uint32 startAddress, const std::set<uint32>& entryAddresses) : startAddress(startAddress), lastAddress(startAddress)
{
isEnterable = entryAddresses.find(startAddress) != entryAddresses.end();
}
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)
{
IMLInstruction& inst = ppcImlGenContext->currentOutputSegment->imlList.emplace_back();
memset(&inst, 0x00, sizeof(IMLInstruction));
return &inst;
}
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(IMLInstruction));
imlInstruction->type = PPCREC_IML_TYPE_CONDITIONAL_R_S32;
imlInstruction->operation = operation;
// r_s32 operation
imlInstruction->op_conditional_r_s32.regR = registerIndex;
imlInstruction->op_conditional_r_s32.immS32 = immS32;
// condition
imlInstruction->op_conditional_r_s32.crRegisterIndex = crRegisterIndex;
imlInstruction->op_conditional_r_s32.crBitIndex = crBitIndex;
imlInstruction->op_conditional_r_s32.bitMustBeSet = bitMustBeSet;
}
void PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext_t* ppcImlGenContext, IMLReg registerDestination, IMLReg registerMemory1, IMLReg registerMemory2, uint32 copyWidth, bool signExtend, bool switchEndian)
{
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->op_storeLoad.registerData = registerDestination;
imlInstruction->op_storeLoad.registerMem = registerMemory1;
imlInstruction->op_storeLoad.registerMem2 = registerMemory2;
imlInstruction->op_storeLoad.copyWidth = copyWidth;
imlInstruction->op_storeLoad.flags2.swapEndian = switchEndian;
imlInstruction->op_storeLoad.flags2.signExtend = signExtend;
}
void PPCRecompilerImlGen_generateNewInstruction_memory_r_indexed(ppcImlGenContext_t* ppcImlGenContext, IMLReg registerDestination, IMLReg registerMemory1, IMLReg registerMemory2, uint32 copyWidth, bool signExtend, bool switchEndian)
{
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->op_storeLoad.registerData = registerDestination;
imlInstruction->op_storeLoad.registerMem = registerMemory1;
imlInstruction->op_storeLoad.registerMem2 = registerMemory2;
imlInstruction->op_storeLoad.copyWidth = copyWidth;
imlInstruction->op_storeLoad.flags2.swapEndian = switchEndian;
imlInstruction->op_storeLoad.flags2.signExtend = signExtend;
}
// 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<typename F1n, typename F2n>
void PPCIMLGen_CreateSegmentBranchedPath(ppcImlGenContext_t& ppcImlGenContext, PPCBasicBlockInfo& basicBlockInfo, F1n genSegmentBranchTaken, F2n genSegmentBranchNotTaken)
{
IMLSegment* currentWriteSegment = basicBlockInfo.GetSegmentForInstructionAppend();
std::span<IMLSegment*> 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;
}
IMLReg PPCRecompilerImlGen_LookupReg(ppcImlGenContext_t* ppcImlGenContext, IMLName mappedName, IMLRegFormat regFormat)
{
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
{
cemu_assert_suspicious();
}
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 (it.second.GetRegID() == reg.GetRegID())
return it.first;
}
cemu_assert(false);
return 0;
}
uint32 PPCRecompilerImlGen_getAndLockFreeTemporaryFPR(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName)
{
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)
{
DEBUG_BREAK;
//for(uint32 i=0; i<255; i++)
//{
// if( ppcImlGenContext->mappedFPRRegister[i] == mappedName )
// {
// return i;
// }
//}
return PPC_REC_INVALID_REGISTER;
}
IMLReg PPCRecompilerImlGen_loadRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName)
{
return PPCRecompilerImlGen_LookupReg(ppcImlGenContext, mappedName, IMLRegFormat::I32);
}
IMLReg _GetRegGPR(ppcImlGenContext_t* ppcImlGenContext, uint32 index)
{
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
*/
IMLReg PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName, bool loadNew)
{
return PPCRecompilerImlGen_LookupReg(ppcImlGenContext, mappedName, IMLRegFormat::F64);
}
/*
* Checks if a PPC fpr register is already loaded into any IML register
* If not, it will create a new undefined temporary IML FPU register and map the name (effectively overwriting the old ppc register)
*/
IMLReg PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName)
{
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);
switch ((opcode >> 26))
{
case 14: // ADDI
case 15: // ADDIS
continue;
case 19: // opcode category 19
switch (PPC_getBits(opcode, 30, 10))
{
case 16:
if (opcode == 0x4E800020)
{
*functionInstructionCount = i;
return true; // BLR
}
return false;
}
return false;
case 32: // LWZ
case 33: // LWZU
case 34: // LBZ
case 35: // LBZU
case 36: // STW
case 37: // STWU
case 38: // STB
case 39: // STBU
case 40: // LHZ
case 41: // LHZU
case 42: // LHA
case 43: // LHAU
case 44: // STH
case 45: // STHU
case 46: // LMW
case 47: // STMW
case 48: // LFS
case 49: // LFSU
case 50: // LFD
case 51: // LFDU
case 52: // STFS
case 53: // STFSU
case 54: // STFD
case 55: // STFDU
continue;
default:
return false;
}
}
return false;
}
void PPCRecompiler_generateInlinedCode(ppcImlGenContext_t* ppcImlGenContext, uint32 startAddress, sint32 instructionCount)
{
for (sint32 i = 0; i < instructionCount; i++)
{
ppcImlGenContext->ppcAddressOfCurrentInstruction = startAddress + i * 4;
ppcImlGenContext->cyclesSinceLastBranch++;
if (PPCRecompiler_decodePPCInstruction(ppcImlGenContext))
{
cemu_assert_suspicious();
}
}
// add range
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;
}
ATTR_MS_ABI uint32 PPCRecompiler_GetTBL()
{
return (uint32)coreinit::coreinit_getTimerTick();
}
ATTR_MS_ABI uint32 PPCRecompiler_GetTBU()
{
return (uint32)(coreinit::coreinit_getTimerTick() >> 32);
}
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 == SPR_TBL || spr == SPR_TBU )
{
IMLReg resultReg = _GetRegGPR(ppcImlGenContext, rD);
ppcImlGenContext->emitInst().make_call_imm(spr == SPR_TBL ? (uintptr_t)PPCRecompiler_GetTBL : (uintptr_t)PPCRecompiler_GetTBU, IMLREG_INVALID, IMLREG_INVALID, IMLREG_INVALID, resultReg);
return true;
}
return false;
}
void PPCRecompilerImlGen_MCRF(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
uint32 crD, crS, b;
PPC_OPC_TEMPL_X(opcode, crD, crS, b);
cemu_assert_debug((crD&3) == 0);
cemu_assert_debug((crS&3) == 0);
crD >>= 2;
crS >>= 2;
for (sint32 i = 0; i<4; i++)
{
IMLReg regCrSrcBit = _GetRegCR(ppcImlGenContext, crS * 4 + i);
IMLReg regCrDstBit = _GetRegCR(ppcImlGenContext, crD * 4 + i);
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, regCrDstBit, regCrSrcBit);
}
}
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)
{
uint32 li;
PPC_OPC_TEMPL_I(opcode, li);
uint32 jumpAddressDest = li;
if( (opcode&PPC_OPC_AA) == 0 )
{
jumpAddressDest = li + (unsigned int)ppcImlGenContext->ppcAddressOfCurrentInstruction;
}
if( opcode&PPC_OPC_LK )
{
// function call
ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_BL, ppcImlGenContext->ppcAddressOfCurrentInstruction, jumpAddressDest, ppcImlGenContext->cyclesSinceLastBranch, IMLREG_INVALID);
return true;
}
// is jump destination within recompiled function?
if (ppcImlGenContext->boundaryTracker->ContainsAddress(jumpAddressDest))
ppcImlGenContext->emitInst().make_jump();
else
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;
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;
IMLReg regCRBit;
if (!ignoreCondition)
regCRBit = _GetRegCR(ppcImlGenContext, crRegister, crBit);
uint32 jumpAddressDest = BD;
if( (opcode&PPC_OPC_AA) == 0 )
{
jumpAddressDest = BD + (unsigned int)ppcImlGenContext->ppcAddressOfCurrentInstruction;
}
if( opcode&PPC_OPC_LK )
{
if (useDecrementer)
return false;
// conditional function calls are not supported
if( ignoreCondition == false )
{
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;
}
// generate iml instructions depending on flags
if( useDecrementer )
{
if( ignoreCondition == false )
return false; // not supported for the moment
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
{
if( ignoreCondition )
{
// branch always, no condition and no decrementer
// not supported
return false;
}
else
{
if (ppcImlGenContext->boundaryTracker->ContainsAddress(jumpAddressDest))
{
// near jump
ppcImlGenContext->emitInst().make_conditional_jump(regCRBit, conditionMustBeTrue);
}
else
{
// far jump
debug_printf("PPCRecompilerImlGen_BC(): Far jump not supported yet");
return false;
}
}
}
return true;
}
// BCCTR or BCLR
bool PPCRecompilerImlGen_BCSPR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, uint32 sprReg)
{
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;
IMLReg regCRBit;
if (!BO.conditionIgnore())
regCRBit = _GetRegCR(ppcImlGenContext, crRegister, crBit);
IMLReg branchDestReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + sprReg);
if (LK)
{
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
{
// 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)
{
return true;
}
bool PPCRecompilerImlGen_SYNC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
return true;
}
bool PPCRecompilerImlGen_ADD(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rD, rA, rB;
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
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;
}
bool PPCRecompilerImlGen_ADDI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rD, rA;
uint32 imm;
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
IMLReg regD = _GetRegGPR(ppcImlGenContext, rD);
if (rA != 0)
{
IMLReg regA = _GetRegGPR(ppcImlGenContext, rA);
ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_ADD, regD, regA, imm);
}
else
{
ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regD, imm);
}
return true;
}
bool PPCRecompilerImlGen_ADDIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
int rD, rA;
uint32 imm;
PPC_OPC_TEMPL_D_Shift16(opcode, rD, rA, imm);
IMLReg regD = _GetRegGPR(ppcImlGenContext, rD);
if (rA != 0)
{
IMLReg regA = _GetRegGPR(ppcImlGenContext, rA);
ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_ADD, regD, regA, (sint32)imm);
}
else
{
ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regD, (sint32)imm);
}
return true;
}
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);
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_ADDE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
// 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;
}
bool PPCRecompilerImlGen_SUBF(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rD, rA, rB;
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
// 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);
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);
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);
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);
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;
}
bool PPCRecompilerImlGen_MULLI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
int rD, rA;
uint32 imm;
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, 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;
}
bool PPCRecompilerImlGen_MULLW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rD, rA, rB;
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
IMLReg regD = _GetRegGPR(ppcImlGenContext, rD);
IMLReg regA = _GetRegGPR(ppcImlGenContext, rA);
IMLReg regB = _GetRegGPR(ppcImlGenContext, rB);
if (opcode & PPC_OPC_OE)
{
return false;
}
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;
}
bool PPCRecompilerImlGen_MULHW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rD, rA, rB;
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
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;
}
bool PPCRecompilerImlGen_MULHWU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rD, rA, rB;
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
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;
}
bool PPCRecompilerImlGen_DIVW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rD, rA, rB;
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
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)
PPCImlGen_UpdateCR0(ppcImlGenContext, regR);
return true;
}
bool PPCRecompilerImlGen_DIVWU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rD, rA, rB;
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
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)
PPCImlGen_UpdateCR0(ppcImlGenContext, regD);
return true;
}
bool PPCRecompilerImlGen_RLWINM(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
int rS, rA, SH, MB, ME;
PPC_OPC_TEMPL_M(opcode, rS, rA, SH, MB, ME);
uint32 mask = ppc_mask(MB, ME);
IMLReg regS = _GetRegGPR(ppcImlGenContext, rS);
IMLReg regA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
if( ME == (31-SH) && MB == 0 )
{
// SLWI
ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_LEFT_SHIFT, regA, regS, SH);
}
else if( SH == (32-MB) && ME == 31 )
{
// SRWI
ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_RIGHT_SHIFT_U, regA, regS, MB);
}
else
{
// 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;
}
bool PPCRecompilerImlGen_RLWIMI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
int rS, rA, SH, MB, ME;
PPC_OPC_TEMPL_M(opcode, rS, rA, SH, MB, ME);
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);
ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_RLWIMI, regA, regS, (sint32)vImm);
if (opcode & PPC_OPC_RC)
PPCImlGen_UpdateCR0(ppcImlGenContext, regA);
return true;
}
bool PPCRecompilerImlGen_RLWNM(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rS, rA, rB, MB, ME;
PPC_OPC_TEMPL_M(opcode, rS, rA, rB, MB, ME);
uint32 mask = ppc_mask(MB, ME);
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)
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);
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);
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<<SH)-1)
genCtx.emitInst().make_r_r_r(PPCREC_IML_OP_LEFT_SHIFT, regTmp2, regTmp2, regTmpShiftAmount);
genCtx.emitInst().make_r_r_s32(PPCREC_IML_OP_SUB, regTmp2, regTmp2, 1);
genCtx.emitInst().make_r_r_r(PPCREC_IML_OP_AND, regTmp1, regTmp1, regTmp2); // signMask & shiftMask & input
genCtx.emitInst().make_r_r_r(PPCREC_IML_OP_AND, regTmp1, regTmp1, regS);
genCtx.emitInst().make_compare_s32(regTmp1, 0, regCarry, IMLCondition::NEQ);
genCtx.emitInst().make_r_r_r(PPCREC_IML_OP_RIGHT_SHIFT_S, regA, regS, regTmpShiftAmount);
}
);
return true;
}
bool PPCRecompilerImlGen_SRAWI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
int rS, rA;
uint32 SH;
PPC_OPC_TEMPL_X(opcode, rS, rA, SH);
cemu_assert_debug(SH < 32);
if (SH == 0)
return false; // becomes a no-op (unless RC bit is set) but also sets ca bit to 0?
IMLReg regS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rS);
IMLReg regA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA);
IMLReg regCarry = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_CA);
IMLReg regTmp = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 0);
// calculate CA first
ppcImlGenContext->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<<SH)-1)
ppcImlGenContext->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;
}
bool PPCRecompilerImlGen_SLW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
int rS, rA, rB;
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
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;
}
bool PPCRecompilerImlGen_SRW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
int rS, rA, rB;
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
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)
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);
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;
}
bool PPCRecompilerImlGen_EXTSB(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rS, rA, rB;
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
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;
}
bool PPCRecompilerImlGen_CNTLZW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rS, rA, rB;
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
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;
}
bool PPCRecompilerImlGen_NEG(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rD, rA, rB;
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
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;
}
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);
IMLReg regMemAddr;
if (rA == 0)
{
if (updateAddrReg)
return false; // invalid instruction form
regMemAddr = _GetRegTemporary(ppcImlGenContext, 0);
ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regMemAddr, 0);
}
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;
}
void PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, uint32 bitWidth, bool signExtend, bool isBigEndian, bool updateAddrReg)
{
// if rA == rD, then the EA wont be stored to rA. We could set updateAddrReg to false in such cases but the end result is the same since the loaded value would overwrite rA
sint32 rA, rD, rB;
PPC_OPC_TEMPL_X(opcode, rD, rA, rB);
updateAddrReg = updateAddrReg && (rA != 0);
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
ppcImlGenContext->emitInst().make_r_memory(regDst, regB, 0, bitWidth, signExtend, isBigEndian);
}
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;
}
void PPCRecompilerImlGen_LMW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rD, rA;
uint32 imm;
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
cemu_assert_debug(rA != 0);
sint32 index = 0;
while (rD <= 31)
{
IMLReg regA = _GetRegGPR(ppcImlGenContext, rA);
IMLReg regD = _GetRegGPR(ppcImlGenContext, rD);
// load word
ppcImlGenContext->emitInst().make_r_memory(regD, regA, (sint32)imm + index * 4, 32, false, true);
// next
rD++;
index++;
}
}
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 )
{
IMLReg regA = _GetRegGPR(ppcImlGenContext, rA);
IMLReg regS = _GetRegGPR(ppcImlGenContext, rS);
// store word
ppcImlGenContext->emitInst().make_memory_r(regS, regA, (sint32)imm + index * 4, 32, true);
// next
rS++;
index++;
}
}
bool PPCRecompilerImlGen_LSWI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
int rA, rD, nb;
PPC_OPC_TEMPL_X(opcode, rD, rA, nb);
if( nb == 0 )
nb = 32;
if (rA == 0)
{
cemu_assert_unimplemented(); // special form where gpr is ignored and EA is 0
return false;
}
// 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 (rD == rA)
return false;
cemu_assert(rD < 32);
IMLReg regDst = _GetRegGPR(ppcImlGenContext, rD);
// load bytes one-by-one
for (sint32 b = 0; b < 4; b++)
{
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;
}
memOffset += 4;
rD++;
}
return true;
}
bool PPCRecompilerImlGen_STSWI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
int rA, rS, nb;
PPC_OPC_TEMPL_X(opcode, rS, rA, nb);
if( nb == 0 )
nb = 32;
IMLReg regMem = _GetRegGPR(ppcImlGenContext, rA);
IMLReg regTmp = _GetRegTemporary(ppcImlGenContext, 0);
uint32 memOffset = 0;
while (nb > 0)
{
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++;
}
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)
{
sint32 rA, rB;
rA = (opcode>>16)&0x1F;
rB = (opcode>>11)&0x1F;
// prepare registers
IMLReg regA = rA!=0?PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA):IMLREG_INVALID;
IMLReg regB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB);
// load zero into a temporary register
IMLReg regZero = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 0);
ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regZero, 0);
// prepare EA and align it to cacheline
IMLReg regMemResEA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 1);
if(rA != 0)
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);
ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_AND, regMemResEA, ~31);
// zero out the cacheline
for(sint32 i = 0; i < 32; i += 4)
ppcImlGenContext->emitInst().make_memory_r(regZero, regMemResEA, i, 32, false);
return true;
}
bool PPCRecompilerImlGen_OR_NOR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, bool complementResult)
{
int rS, rA, rB;
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
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
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;
}
bool PPCRecompilerImlGen_ORC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rS, rA, rB;
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
// 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_AND_NAND(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, bool complementResult)
{
int rS, rA, rB;
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
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
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;
}
bool PPCRecompilerImlGen_ANDC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rS, rA, rB;
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
// 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;
}
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 )
{
ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regA, 0);
}
else
{
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;
}
void PPCRecompilerImlGen_ANDI_ANDIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, bool isShifted)
{
sint32 rS, rA;
uint32 imm;
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_AND, regA, regS, (sint32)imm);
// ANDI/ANDIS always updates cr0
PPCImlGen_UpdateCR0(ppcImlGenContext, regA);
}
void PPCRecompilerImlGen_ORI_ORIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, bool isShifted)
{
sint32 rS, rA;
uint32 imm;
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_XORI_XORIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, bool isShifted)
{
sint32 rS, rA;
uint32 imm;
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);
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;
}
bool PPCRecompilerImlGen_CRORC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
int crD, crA, crB;
PPC_OPC_TEMPL_X(opcode, 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;
}
bool PPCRecompilerImlGen_CRAND(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
int crD, crA, crB;
PPC_OPC_TEMPL_X(opcode, 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;
}
bool PPCRecompilerImlGen_CRANDC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
int crD, crA, crB;
PPC_OPC_TEMPL_X(opcode, 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;
}
bool PPCRecompilerImlGen_CRXOR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
int crD, crA, crB;
PPC_OPC_TEMPL_X(opcode, crD, crA, crB);
IMLReg regCrA = _GetRegCR(ppcImlGenContext, crA);
IMLReg regCrB = _GetRegCR(ppcImlGenContext, crB);
IMLReg regCrR = _GetRegCR(ppcImlGenContext, crD);
if (regCrA == regCrB)
{
ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regCrR, 0);
return true;
}
ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_XOR, regCrR, regCrA, regCrB);
return true;
}
bool PPCRecompilerImlGen_CREQV(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
int crD, crA, crB;
PPC_OPC_TEMPL_X(opcode, crD, crA, crB);
IMLReg regCrA = _GetRegCR(ppcImlGenContext, crA);
IMLReg regCrB = _GetRegCR(ppcImlGenContext, crB);
IMLReg regCrR = _GetRegCR(ppcImlGenContext, crD);
if (regCrA == regCrB)
{
ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regCrR, 1);
return true;
}
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;
ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_HLE, ppcImlGenContext->ppcAddressOfCurrentInstruction, hleFuncId, 0, IMLREG_INVALID);
return true;
}
uint32 PPCRecompiler_iterateCurrentInstruction(ppcImlGenContext_t* ppcImlGenContext)
{
uint32 v = CPU_swapEndianU32(*(ppcImlGenContext->currentInstruction));
ppcImlGenContext->currentInstruction += 1;
return v;
}
uint32 PPCRecompiler_getCurrentInstruction(ppcImlGenContext_t* ppcImlGenContext)
{
uint32 v = CPU_swapEndianU32(*(ppcImlGenContext->currentInstruction));
return v;
}
uint32 PPCRecompiler_getPreviousInstruction(ppcImlGenContext_t* ppcImlGenContext)
{
uint32 v = CPU_swapEndianU32(*(ppcImlGenContext->currentInstruction-1));
return v;
}
void PPCRecompilerIml_setSegmentPoint(IMLSegmentPoint* segmentPoint, IMLSegment* imlSegment, sint32 index)
{
segmentPoint->imlSegment = imlSegment;
segmentPoint->SetInstructionIndex(index);
if (imlSegment->segmentPointList)
imlSegment->segmentPointList->prev = segmentPoint;
segmentPoint->prev = nullptr;
segmentPoint->next = imlSegment->segmentPointList;
imlSegment->segmentPointList = segmentPoint;
}
void PPCRecompilerIml_removeSegmentPoint(IMLSegmentPoint* segmentPoint)
{
if (segmentPoint->prev)
segmentPoint->prev->next = segmentPoint->next;
else
segmentPoint->imlSegment->segmentPointList = segmentPoint->next;
if (segmentPoint->next)
segmentPoint->next->prev = segmentPoint->prev;
}
/*
* Insert multiple no-op instructions
* Warning: Can invalidate any previous instruction pointers from the same segment
*/
void PPCRecompiler_pushBackIMLInstructions(IMLSegment* imlSegment, sint32 index, sint32 shiftBackCount)
{
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);
// fill empty space with NOP instructions
for (sint32 i = 0; i < shiftBackCount; i++)
{
imlSegment->imlList[index + i].type = PPCREC_IML_TYPE_NONE;
}
// update position of segment points
if (imlSegment->segmentPointList)
{
IMLSegmentPoint* segmentPoint = imlSegment->segmentPointList;
while (segmentPoint)
{
segmentPoint->ShiftIfAfter(index, shiftBackCount);
segmentPoint = segmentPoint->next;
}
}
}
IMLInstruction* PPCRecompiler_insertInstruction(IMLSegment* imlSegment, sint32 index)
{
PPCRecompiler_pushBackIMLInstructions(imlSegment, index, 1);
return imlSegment->imlList.data() + index;
}
IMLInstruction* PPCRecompiler_appendInstruction(IMLSegment* imlSegment)
{
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)
{
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)
{
bool unsupportedInstructionFound = false;
uint32 opcode = PPCRecompiler_iterateCurrentInstruction(ppcImlGenContext);
switch ((opcode >> 26))
{
case 1:
if (PPCRecompilerImlGen_HLE(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 4: // opcode category - paired single
switch (PPC_getBits(opcode, 30, 5))
{
case 0: // subcategory compare
switch (PPC_getBits(opcode, 25, 5))
{
case 0:
if( !PPCRecompilerImlGen_PS_CMPU0(ppcImlGenContext, opcode) )
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 1:
if( !PPCRecompilerImlGen_PS_CMPO0(ppcImlGenContext, opcode) )
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 2:
if( !PPCRecompilerImlGen_PS_CMPU1(ppcImlGenContext, opcode) )
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
default:
unsupportedInstructionFound = true;
break;
}
break;
case 8: //Sub category - move/negate
switch (PPC_getBits(opcode, 25, 5))
{
case 1: // PS negate
if (PPCRecompilerImlGen_PS_NEG(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 2: // PS move register
if (PPCRecompilerImlGen_PS_MR(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 8: // PS abs
if (PPCRecompilerImlGen_PS_ABS(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
default:
unsupportedInstructionFound = true;
break;
}
break;
case 10:
if (PPCRecompilerImlGen_PS_SUM0(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 11:
if (PPCRecompilerImlGen_PS_SUM1(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 12: // multiply scalar
if (PPCRecompilerImlGen_PS_MULS0(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 13: // multiply scalar
if (PPCRecompilerImlGen_PS_MULS1(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 14: // multiply add scalar
if (PPCRecompilerImlGen_PS_MADDS0(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 15: // multiply add scalar
if (PPCRecompilerImlGen_PS_MADDS1(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 16: // sub category - merge
switch (PPC_getBits(opcode, 25, 5))
{
case 16:
if (PPCRecompilerImlGen_PS_MERGE00(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 17:
if (PPCRecompilerImlGen_PS_MERGE01(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 18:
if (PPCRecompilerImlGen_PS_MERGE10(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 19:
if (PPCRecompilerImlGen_PS_MERGE11(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
default:
unsupportedInstructionFound = true;
break;
}
break;
case 18: // divide paired
if (PPCRecompilerImlGen_PS_DIV(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 20: // sub paired
if (PPCRecompilerImlGen_PS_SUB(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 21: // add paired
if (PPCRecompilerImlGen_PS_ADD(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 23: // select paired
if (PPCRecompilerImlGen_PS_SEL(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 25: // multiply paired
if (PPCRecompilerImlGen_PS_MUL(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 24: // reciprocal paired
if (PPCRecompilerImlGen_PS_RES(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 26: // reciprocal squareroot paired
if (PPCRecompilerImlGen_PS_RSQRTE(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 28: // multiply sub paired
if (PPCRecompilerImlGen_PS_MSUB(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 29: // multiply add paired
if (PPCRecompilerImlGen_PS_MADD(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 30: // negative multiply sub paired
if (PPCRecompilerImlGen_PS_NMSUB(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 31: // negative multiply add paired
if (PPCRecompilerImlGen_PS_NMADD(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
default:
unsupportedInstructionFound = true;
break;
}
break;
case 7: // MULLI
PPCRecompilerImlGen_MULLI(ppcImlGenContext, opcode);
break;
case 8: // SUBFIC
if (!PPCRecompilerImlGen_SUBFIC(ppcImlGenContext, opcode))
unsupportedInstructionFound = true;
break;
case 10: // CMPLI
if (!PPCRecompilerImlGen_CMPI(ppcImlGenContext, opcode, true))
unsupportedInstructionFound = true;
break;
case 11: // CMPI
if (!PPCRecompilerImlGen_CMPI(ppcImlGenContext, opcode, false))
unsupportedInstructionFound = true;
break;
case 12: // ADDIC
if (PPCRecompilerImlGen_ADDIC_(ppcImlGenContext, opcode, false) == false)
unsupportedInstructionFound = true;
break;
case 13: // ADDIC.
if (PPCRecompilerImlGen_ADDIC_(ppcImlGenContext, opcode, true) == false)
unsupportedInstructionFound = true;
break;
case 14: // ADDI
if (PPCRecompilerImlGen_ADDI(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 15: // ADDIS
if (PPCRecompilerImlGen_ADDIS(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 16: // BC
if (PPCRecompilerImlGen_BC(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 17:
if (PPC_getBits(opcode, 30, 1) == 1)
{
// SC -> no-op
}
else
{
unsupportedInstructionFound = true;
}
break;
case 18: // B
if (PPCRecompilerImlGen_B(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 19: // opcode category 19
switch (PPC_getBits(opcode, 30, 10))
{
case 0:
PPCRecompilerImlGen_MCRF(ppcImlGenContext, opcode);
break;
case 16: // BCLR
if (PPCRecompilerImlGen_BCSPR(ppcImlGenContext, opcode, SPR_LR) == false)
unsupportedInstructionFound = true;
break;
case 129:
if (PPCRecompilerImlGen_CRANDC(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 150:
if (PPCRecompilerImlGen_ISYNC(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 193:
if (PPCRecompilerImlGen_CRXOR(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 257:
if (PPCRecompilerImlGen_CRAND(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 289:
if (PPCRecompilerImlGen_CREQV(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 417:
if (PPCRecompilerImlGen_CRORC(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 449:
if (PPCRecompilerImlGen_CROR(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 528: // BCCTR
if (PPCRecompilerImlGen_BCSPR(ppcImlGenContext, opcode, SPR_CTR) == false)
unsupportedInstructionFound = true;
break;
default:
unsupportedInstructionFound = true;
break;
}
break;
case 20:
if (PPCRecompilerImlGen_RLWIMI(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 21:
if (PPCRecompilerImlGen_RLWINM(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 23:
if (PPCRecompilerImlGen_RLWNM(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 24: // ORI
PPCRecompilerImlGen_ORI_ORIS(ppcImlGenContext, opcode, false);
break;
case 25: // ORIS
PPCRecompilerImlGen_ORI_ORIS(ppcImlGenContext, opcode, true);
break;
case 26: // XORI
PPCRecompilerImlGen_XORI_XORIS(ppcImlGenContext, opcode, false);
break;
case 27: // XORIS
PPCRecompilerImlGen_XORI_XORIS(ppcImlGenContext, opcode, true);
break;
case 28: // ANDI
PPCRecompilerImlGen_ANDI_ANDIS(ppcImlGenContext, opcode, false);
break;
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, false);
break;
case 4:
PPCRecompilerImlGen_TW(ppcImlGenContext, opcode);
break;
case 8:
if (PPCRecompilerImlGen_SUBFC(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 10:
if (PPCRecompilerImlGen_ADDC(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 11:
if (PPCRecompilerImlGen_MULHWU(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 19:
if (PPCRecompilerImlGen_MFCR(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 20:
if (PPCRecompilerImlGen_LWARX(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 23: // LWZX
PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 32, false, true, false);
break;
case 24:
if (PPCRecompilerImlGen_SLW(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 26:
if (PPCRecompilerImlGen_CNTLZW(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 28: // AND
if (!PPCRecompilerImlGen_AND_NAND(ppcImlGenContext, opcode, false))
unsupportedInstructionFound = true;
break;
case 32:
PPCRecompilerImlGen_CMP(ppcImlGenContext, opcode, true); // CMPL
break;
case 40:
if (PPCRecompilerImlGen_SUBF(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 54:
// DBCST - Generates no code
break;
case 55: // LWZUX
PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 32, false, true, true);
break;
case 60: // ANDC
if (!PPCRecompilerImlGen_ANDC(ppcImlGenContext, opcode))
unsupportedInstructionFound = true;
break;
case 75:
if (PPCRecompilerImlGen_MULHW(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 86:
// DCBF -> No-Op
break;
case 87: // LBZX
PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 8, false, true, false);
break;
case 104:
if (PPCRecompilerImlGen_NEG(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 119: // LBZUX
PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 8, false, true, true);
break;
case 124: // NOR
if (!PPCRecompilerImlGen_OR_NOR(ppcImlGenContext, opcode, true))
unsupportedInstructionFound = true;
break;
case 136:
if (PPCRecompilerImlGen_SUBFE(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 138:
if (PPCRecompilerImlGen_ADDE(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 144:
if( !PPCRecompilerImlGen_MTCRF(ppcImlGenContext, opcode))
unsupportedInstructionFound = true;
break;
case 150:
if (!PPCRecompilerImlGen_STWCX(ppcImlGenContext, opcode))
unsupportedInstructionFound = true;
break;
case 151: // STWX
if (!PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 32, true, false))
unsupportedInstructionFound = true;
break;
case 183: // STWUX
if (!PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 32, true, true))
unsupportedInstructionFound = true;
break;
case 200:
if (PPCRecompilerImlGen_SUBFZE(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 202:
if (PPCRecompilerImlGen_ADDZE(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 215: // STBX
if (!PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 8, true, false))
unsupportedInstructionFound = true;
break;
case 234:
if (PPCRecompilerImlGen_ADDME(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 235:
if (PPCRecompilerImlGen_MULLW(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
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: // LHZX
PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 16, false, true, false);
break;
case 284: // EQV (alias to NXOR)
if (!PPCRecompilerImlGen_XOR(ppcImlGenContext, opcode, true))
unsupportedInstructionFound = true;
break;
case 311: // LHZUX
PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 16, false, true, 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: // LHAX
PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 16, true, true, false);
break;
case 371:
if (PPCRecompilerImlGen_MFTB(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 375: // LHAUX
PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 16, true, true, true);
break;
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: // STHUX
if (!PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 16, true, true))
unsupportedInstructionFound = true;
break;
case 444: // OR
if (!PPCRecompilerImlGen_OR_NOR(ppcImlGenContext, opcode, false))
unsupportedInstructionFound = true;
break;
case 459:
PPCRecompilerImlGen_DIVWU(ppcImlGenContext, opcode);
break;
case 467:
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: // LWBRX
PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 32, false, false, false);
break;
case 535:
if (PPCRecompilerImlGen_LFSX(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 536:
if (PPCRecompilerImlGen_SRW(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 567:
if (PPCRecompilerImlGen_LFSUX(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 597:
if (PPCRecompilerImlGen_LSWI(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 598:
PPCRecompilerImlGen_SYNC(ppcImlGenContext, opcode);
break;
case 599:
if (PPCRecompilerImlGen_LFDX(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 631:
if (PPCRecompilerImlGen_LFDUX(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 662: // STWBRX
if (!PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 32, false, false))
unsupportedInstructionFound = true;
break;
case 663:
if (PPCRecompilerImlGen_STFSX(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 695:
if (PPCRecompilerImlGen_STFSUX(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 725:
if (PPCRecompilerImlGen_STSWI(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 727:
if (PPCRecompilerImlGen_STFDX(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 790: // LHBRX
PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 16, false, false, false);
break;
case 792:
if (PPCRecompilerImlGen_SRAW(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 824:
if (PPCRecompilerImlGen_SRAWI(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 918: // STHBRX
if (!PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 16, false, true))
unsupportedInstructionFound = true;
break;
case 922:
if (PPCRecompilerImlGen_EXTSH(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 954:
if (PPCRecompilerImlGen_EXTSB(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 983:
if (PPCRecompilerImlGen_STFIWX(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 1014:
if (PPCRecompilerImlGen_DCBZ(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
default:
unsupportedInstructionFound = true;
break;
}
break;
case 32: // LWZ
if(!PPCRecompilerImlGen_LOAD(ppcImlGenContext, opcode, 32, false, true, false))
unsupportedInstructionFound = true;
break;
case 33: // LWZU
if (!PPCRecompilerImlGen_LOAD(ppcImlGenContext, opcode, 32, false, true, true))
unsupportedInstructionFound = true;
break;
case 34: // LBZ
if (!PPCRecompilerImlGen_LOAD(ppcImlGenContext, opcode, 8, false, true, false))
unsupportedInstructionFound = true;
break;
case 35: // LBZU
if (!PPCRecompilerImlGen_LOAD(ppcImlGenContext, opcode, 8, false, true, true))
unsupportedInstructionFound = true;
break;
case 36: // STW
if(!PPCRecompilerImlGen_STORE(ppcImlGenContext, opcode, 32, true, false))
unsupportedInstructionFound = true;
break;
case 37: // STWU
if (!PPCRecompilerImlGen_STORE(ppcImlGenContext, opcode, 32, true, true))
unsupportedInstructionFound = true;
break;
case 38: // STB
if (!PPCRecompilerImlGen_STORE(ppcImlGenContext, opcode, 8, true, false))
unsupportedInstructionFound = true;
break;
case 39: // STBU
if (!PPCRecompilerImlGen_STORE(ppcImlGenContext, opcode, 8, true, true))
unsupportedInstructionFound = true;
break;
case 40: // LHZ
if (!PPCRecompilerImlGen_LOAD(ppcImlGenContext, opcode, 16, false, true, false))
unsupportedInstructionFound = true;
break;
case 41: // LHZU
if (!PPCRecompilerImlGen_LOAD(ppcImlGenContext, opcode, 16, false, true, true))
unsupportedInstructionFound = true;
break;
case 42: // LHA
if (!PPCRecompilerImlGen_LOAD(ppcImlGenContext, opcode, 16, true, true, false))
unsupportedInstructionFound = true;
break;
case 43: // LHAU
if (!PPCRecompilerImlGen_LOAD(ppcImlGenContext, opcode, 16, true, true, true))
unsupportedInstructionFound = true;
break;
case 44: // STH
if (!PPCRecompilerImlGen_STORE(ppcImlGenContext, opcode, 16, true, false))
unsupportedInstructionFound = true;
break;
case 45: // STHU
if (!PPCRecompilerImlGen_STORE(ppcImlGenContext, opcode, 16, true, true))
unsupportedInstructionFound = true;
break;
case 46:
PPCRecompilerImlGen_LMW(ppcImlGenContext, opcode);
break;
case 47:
PPCRecompilerImlGen_STMW(ppcImlGenContext, opcode);
break;
case 48:
if (PPCRecompilerImlGen_LFS(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 49:
if (PPCRecompilerImlGen_LFSU(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 50:
if (PPCRecompilerImlGen_LFD(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 51:
if (PPCRecompilerImlGen_LFDU(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 52:
if (PPCRecompilerImlGen_STFS(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 53:
if (PPCRecompilerImlGen_STFSU(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 54:
if (PPCRecompilerImlGen_STFD(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 55:
if (PPCRecompilerImlGen_STFDU(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 56:
if (PPCRecompilerImlGen_PSQ_L(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 57:
if (PPCRecompilerImlGen_PSQ_LU(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 59: // opcode category
switch (PPC_getBits(opcode, 30, 5))
{
case 18:
if (PPCRecompilerImlGen_FDIVS(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 20:
if (PPCRecompilerImlGen_FSUBS(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 21:
if (PPCRecompilerImlGen_FADDS(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 24:
if (PPCRecompilerImlGen_FRES(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 25:
if (PPCRecompilerImlGen_FMULS(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 28:
if (PPCRecompilerImlGen_FMSUBS(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 29:
if (PPCRecompilerImlGen_FMADDS(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 30:
if (PPCRecompilerImlGen_FNMSUBS(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
default:
unsupportedInstructionFound = true;
break;
}
break;
case 60:
if (PPCRecompilerImlGen_PSQ_ST(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 61:
if (PPCRecompilerImlGen_PSQ_STU(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 63: // opcode category
switch (PPC_getBits(opcode, 30, 5))
{
case 0:
if (PPCRecompilerImlGen_FCMPU(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 12:
if (PPCRecompilerImlGen_FRSP(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 15:
if (PPCRecompilerImlGen_FCTIWZ(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 18:
if (PPCRecompilerImlGen_FDIV(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 20:
if (PPCRecompilerImlGen_FSUB(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 21:
if (PPCRecompilerImlGen_FADD(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 23:
if (PPCRecompilerImlGen_FSEL(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 25:
if (PPCRecompilerImlGen_FMUL(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 26:
if (PPCRecompilerImlGen_FRSQRTE(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 28:
if (PPCRecompilerImlGen_FMSUB(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 29:
if (PPCRecompilerImlGen_FMADD(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 30:
if (PPCRecompilerImlGen_FNMSUB(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
default:
switch (PPC_getBits(opcode, 30, 10))
{
case 32:
if (PPCRecompilerImlGen_FCMPO(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 40:
if (PPCRecompilerImlGen_FNEG(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 72:
if (PPCRecompilerImlGen_FMR(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 136:
if (PPCRecompilerImlGen_FNABS(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
case 264:
if (PPCRecompilerImlGen_FABS(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
ppcImlGenContext->hasFPUInstruction = true;
break;
default:
unsupportedInstructionFound = true;
break;
}
break;
}
break;
default:
unsupportedInstructionFound = true;
break;
}
return unsupportedInstructionFound;
}
// 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)
{
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<PPCBasicBlockInfo>& basicBlockList, PPCFunctionBoundaryTracker& boundaryTracker, uint32 ppcStart, uint32 ppcEnd, const std::set<uint32>& combinedBranchTargets, const std::set<uint32>& 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<PPCBasicBlockInfo> PPCRecompiler_DetermineBasicBlockRange(PPCFunctionBoundaryTracker& boundaryTracker, const std::set<uint32>& entryAddresses)
{
cemu_assert(!entryAddresses.empty());
std::vector<PPCBasicBlockInfo> basicBlockList;
const std::set<uint32> branchTargets = boundaryTracker.GetBranchTargets();
auto funcRanges = boundaryTracker.GetRanges();
std::set<uint32> 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))
{
cemuLog_logDebug(LogType::Force, "PPCRecompiler: Unsupported instruction at 0x{:08x}", 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);
cemu_assert_debug(splitSeg->nextSegmentBranchNotTaken);
// let the IML optimizer and RA know that the original segment should be used during analysis for dead code elimination
exitSegment->SetNextSegmentForOverwriteHints(splitSeg->nextSegmentBranchNotTaken);
}
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<uint32>& entryAddresses)
{
std::vector<PPCBasicBlockInfo> basicBlockList = PPCRecompiler_DetermineBasicBlockRange(boundaryTracker, entryAddresses);
// create segments
std::unordered_map<uint32, PPCBasicBlockInfo*> 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<uint32>& 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);
return true;
}