Cemu/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlGen.cpp

3435 lines
124 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"
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, uint8 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.registerIndex = 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, uint8 registerDestination, uint8 registerMemory1, uint8 registerMemory2, uint32 copyWidth, bool signExtend, bool switchEndian)
{
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, uint8 registerDestination, uint8 registerMemory1, uint8 registerMemory2, uint32 copyWidth, bool signExtend, bool switchEndian)
{
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;
}
uint32 PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName)
{
if( mappedName == PPCREC_NAME_NONE )
{
debug_printf("PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(): Invalid mappedName parameter\n");
return PPC_REC_INVALID_REGISTER;
}
for(uint32 i=0; i<(PPC_REC_MAX_VIRTUAL_GPR-1); i++)
{
if( ppcImlGenContext->mappedRegister[i] == PPCREC_NAME_NONE )
{
ppcImlGenContext->mappedRegister[i] = mappedName;
return i;
}
}
return 0;
}
uint32 PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName)
{
for(uint32 i=0; i< PPC_REC_MAX_VIRTUAL_GPR; i++)
{
if( ppcImlGenContext->mappedRegister[i] == mappedName )
{
return i;
}
}
return PPC_REC_INVALID_REGISTER;
}
uint32 PPCRecompilerImlGen_getAndLockFreeTemporaryFPR(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName)
{
if( mappedName == PPCREC_NAME_NONE )
{
debug_printf("PPCRecompilerImlGen_getAndLockFreeTemporaryFPR(): Invalid mappedName parameter\n");
return PPC_REC_INVALID_REGISTER;
}
for(uint32 i=0; i<255; i++)
{
if( ppcImlGenContext->mappedFPRRegister[i] == PPCREC_NAME_NONE )
{
ppcImlGenContext->mappedFPRRegister[i] = mappedName;
return i;
}
}
return 0;
}
uint32 PPCRecompilerImlGen_findFPRRegisterByMappedName(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName)
{
for(uint32 i=0; i<255; i++)
{
if( ppcImlGenContext->mappedFPRRegister[i] == mappedName )
{
return i;
}
}
return PPC_REC_INVALID_REGISTER;
}
uint32 PPCRecompilerImlGen_loadRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName)
{
uint32 loadedRegisterIndex = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, mappedName);
if (loadedRegisterIndex != PPC_REC_INVALID_REGISTER)
return loadedRegisterIndex;
uint32 registerIndex = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, mappedName);
return registerIndex;
}
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
uint32 _GetRegTemporaryS8(ppcImlGenContext_t* ppcImlGenContext, uint32 index)
{
cemu_assert_debug(index < 4);
return PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + index);
}
/*
* Loads a PPC fpr into any of the available IML FPU registers
* If loadNew is false, it will check first if the fpr is already loaded into any IML register
*/
uint32 PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName, bool loadNew)
{
if( loadNew == false )
{
uint32 loadedRegisterIndex = PPCRecompilerImlGen_findFPRRegisterByMappedName(ppcImlGenContext, mappedName);
if( loadedRegisterIndex != PPC_REC_INVALID_REGISTER )
return loadedRegisterIndex;
}
uint32 registerIndex = PPCRecompilerImlGen_getAndLockFreeTemporaryFPR(ppcImlGenContext, mappedName);
return registerIndex;
}
/*
* Checks if a PPC fpr register is already loaded into any IML register
* If no, it will create a new undefined temporary IML FPU register and map the name (effectively overwriting the old ppc register)
*/
uint32 PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName)
{
uint32 loadedRegisterIndex = PPCRecompilerImlGen_findFPRRegisterByMappedName(ppcImlGenContext, mappedName);
if( loadedRegisterIndex != PPC_REC_INVALID_REGISTER )
return loadedRegisterIndex;
uint32 registerIndex = PPCRecompilerImlGen_getAndLockFreeTemporaryFPR(ppcImlGenContext, mappedName);
return registerIndex;
}
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, uint32 registerR)
{
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(registerR, 0, crBitRegLT, IMLCondition::SIGNED_LT);
ppcImlGenContext->emitInst().make_compare_s32(registerR, 0, crBitRegGT, IMLCondition::SIGNED_GT);
ppcImlGenContext->emitInst().make_compare_s32(registerR, 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);
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)
{
uint32 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)
{
uint32 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)
{
uint32 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)
{
uint32 sprReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + spr);
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, gprReg, sprReg);
}
else
return false;
return true;
}
bool PPCRecompilerImlGen_MFTB(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
printf("PPCRecompilerImlGen_MFTB(): Not supported\n");
return false;
uint32 rD, spr1, spr2, spr;
PPC_OPC_TEMPL_XO(opcode, rD, spr1, spr2);
spr = spr1 | (spr2<<5);
if (spr == 268 || spr == 269)
{
// TBL / TBU
uint32 param2 = spr | (rD << 16);
ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_MFTB, ppcImlGenContext->ppcAddressOfCurrentInstruction, param2, 0);
IMLSegment* middleSeg = PPCIMLGen_CreateSplitSegmentAtEnd(*ppcImlGenContext, *ppcImlGenContext->currentBasicBlock);
return true;
}
return false;
}
bool PPCRecompilerImlGen_MFCR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
printf("MFCR: Not implemented\n");
return false;
//sint32 rD, rA, rB;
//PPC_OPC_TEMPL_X(opcode, rD, rA, rB);
//uint32 gprReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0 + rD);
//ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_MFCR, gprReg, 0);
//return true;
}
bool PPCRecompilerImlGen_MTCRF(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
printf("MTCRF: Not implemented\n");
return false;
//uint32 rS;
//uint32 crMask;
//PPC_OPC_TEMPL_XFX(opcode, rS, crMask);
//uint32 gprReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0 + rS);
//ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_MTCRF, gprReg, crMask);
//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);
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);
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);
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);
uint32 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
uint32 tmpRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY);
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, tmpRegister, branchDestReg);
branchDestReg = tmpRegister;
}
uint32 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, branchDestReg, 0, 0);
}
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, branchDestReg, 0, 0);
}
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);
uint32 registerResult = _GetRegGPR(ppcImlGenContext, rD);
uint32 registerOperand = _GetRegGPR(ppcImlGenContext, rA);
ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_MULTIPLY_SIGNED, registerResult, registerOperand, (sint32)imm);
return true;
}
bool PPCRecompilerImlGen_MULLW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rD, rA, rB;
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
//hCPU->gpr[rD] = hCPU->gpr[rA] * hCPU->gpr[rB];
uint32 registerResult = _GetRegGPR(ppcImlGenContext, rD);
uint32 registerOperand1 = _GetRegGPR(ppcImlGenContext, rA);
uint32 registerOperand2 = _GetRegGPR(ppcImlGenContext, rB);
if (opcode & PPC_OPC_OE)
{
return false;
}
ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_MULTIPLY_SIGNED, registerResult, registerOperand1, registerOperand2);
if (opcode & PPC_OPC_RC)
PPCImlGen_UpdateCR0(ppcImlGenContext, registerResult);
return true;
}
bool PPCRecompilerImlGen_MULHW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rD, rA, rB;
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
uint32 registerResult = _GetRegGPR(ppcImlGenContext, rD);
uint32 registerOperand1 = _GetRegGPR(ppcImlGenContext, rA);
uint32 registerOperand2 = _GetRegGPR(ppcImlGenContext, rB);
ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_MULTIPLY_HIGH_SIGNED, registerResult, registerOperand1, registerOperand2);
if (opcode & PPC_OPC_RC)
PPCImlGen_UpdateCR0(ppcImlGenContext, registerResult);
return true;
}
bool PPCRecompilerImlGen_MULHWU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rD, rA, rB;
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
uint32 registerResult = _GetRegGPR(ppcImlGenContext, rD);
uint32 registerOperand1 = _GetRegGPR(ppcImlGenContext, rA);
uint32 registerOperand2 = _GetRegGPR(ppcImlGenContext, rB);
ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_MULTIPLY_HIGH_UNSIGNED, registerResult, registerOperand1, registerOperand2);
if (opcode & PPC_OPC_RC)
PPCImlGen_UpdateCR0(ppcImlGenContext, registerResult);
return true;
}
bool PPCRecompilerImlGen_DIVW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rD, rA, rB;
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
uint32 registerResult = _GetRegGPR(ppcImlGenContext, rD);
uint32 registerOperand1 = _GetRegGPR(ppcImlGenContext, rA);
uint32 registerOperand2 = _GetRegGPR(ppcImlGenContext, rB);
ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_DIVIDE_SIGNED, registerResult, registerOperand1, registerOperand2);
if (opcode & PPC_OPC_RC)
PPCImlGen_UpdateCR0(ppcImlGenContext, registerResult);
return true;
}
bool PPCRecompilerImlGen_DIVWU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rD, rA, rB;
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
// hCPU->gpr[rD] = (uint32)a / (uint32)b;
uint32 registerResult = _GetRegGPR(ppcImlGenContext, rD);
uint32 registerOperand1 = _GetRegGPR(ppcImlGenContext, rA);
uint32 registerOperand2 = _GetRegGPR(ppcImlGenContext, rB);
ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_DIVIDE_UNSIGNED, registerResult, registerOperand1, registerOperand2);
if (opcode & PPC_OPC_RC)
PPCImlGen_UpdateCR0(ppcImlGenContext, registerResult);
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);
uint32 registerRS = _GetRegGPR(ppcImlGenContext, rS);
uint32 registerRA = 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, registerRA, registerRS, SH);
}
else if( SH == (32-MB) && ME == 31 )
{
// SRWI
ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_RIGHT_SHIFT_U, registerRA, registerRS, MB);
}
else
{
// general handler
if (registerRA != registerRS)
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, registerRA, registerRS);
if (SH != 0)
ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_LEFT_ROTATE, registerRA, SH);
if (mask != 0xFFFFFFFF)
ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_AND, registerRA, (sint32)mask);
}
if (opcode & PPC_OPC_RC)
PPCImlGen_UpdateCR0(ppcImlGenContext, registerRA);
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);
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
uint32 registerRA = 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, registerRA, registerRS, (sint32)vImm);
if (opcode & PPC_OPC_RC)
PPCImlGen_UpdateCR0(ppcImlGenContext, registerRA);
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);
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB);
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_LEFT_ROTATE, registerRA, registerRS, registerRB);
if( mask != 0xFFFFFFFF )
ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_AND, registerRA, (sint32)mask);
if (opcode & PPC_OPC_RC)
PPCImlGen_UpdateCR0(ppcImlGenContext, registerRA);
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-1 are well defined in IML so this requires special handling for shifts >= 32
sint32 rS, rA, rB;
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB);
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
uint32 registerCarry = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_CA);
uint32 registerTmpShiftAmount = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 0);
uint32 registerTmpCondBool = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 1);
uint32 registerTmp1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 2);
uint32 registerTmp2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 3);
// load masked shift factor into temporary register
ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_AND, registerTmpShiftAmount, registerRB, 0x3F);
ppcImlGenContext->emitInst().make_compare_s32(registerTmpShiftAmount, 32, registerTmpCondBool, IMLCondition::UNSIGNED_GT);
ppcImlGenContext->emitInst().make_conditional_jump(registerTmpCondBool, true);
PPCIMLGen_CreateSegmentBranchedPath(*ppcImlGenContext, *ppcImlGenContext->currentBasicBlock,
[&](ppcImlGenContext_t& genCtx)
{
/* branch taken */
genCtx.emitInst().make_r_r_r(PPCREC_IML_OP_RIGHT_SHIFT_S, registerRA, registerRS, registerTmpShiftAmount);
genCtx.emitInst().make_compare_s32(registerRA, 0, registerCarry, 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, registerTmp1, registerRS, 31); // signMask = input >> 31 (arithmetic shift)
genCtx.emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, registerTmp2, 1); // shiftMask = ((1<<SH)-1)
genCtx.emitInst().make_r_r_r(PPCREC_IML_OP_LEFT_SHIFT, registerTmp2, registerTmp2, registerTmpShiftAmount);
genCtx.emitInst().make_r_r_s32(PPCREC_IML_OP_SUB, registerTmp2, registerTmp2, 1);
genCtx.emitInst().make_r_r_r(PPCREC_IML_OP_AND, registerTmp1, registerTmp1, registerTmp2); // signMask & shiftMask & input
genCtx.emitInst().make_r_r_r(PPCREC_IML_OP_AND, registerTmp1, registerTmp1, registerRS);
genCtx.emitInst().make_compare_s32(registerTmp1, 0, registerCarry, IMLCondition::NEQ);
genCtx.emitInst().make_r_r_r(PPCREC_IML_OP_RIGHT_SHIFT_S, registerRA, registerRS, registerTmpShiftAmount);
}
);
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?
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rS);
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA);
uint32 registerCarry = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_CA);
uint32 registerTmp = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 0);
// calculate CA first
ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_RIGHT_SHIFT_S, registerTmp, registerRS, 31); // signMask = input >> 31 (arithmetic shift)
ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_AND, registerTmp, registerTmp, registerRS); // testValue = input & signMask & ((1<<SH)-1)
ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_AND, registerTmp, registerTmp, ((1 << SH) - 1));
ppcImlGenContext->emitInst().make_compare_s32(registerTmp, 0, registerCarry, IMLCondition::NEQ); // ca = (testValue != 0)
// do the actual shift
ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_RIGHT_SHIFT_S, registerRA, registerRS, (sint32)SH);
if (opcode & PPC_OPC_RC)
PPCImlGen_UpdateCR0(ppcImlGenContext, registerRA);
return true;
}
bool PPCRecompilerImlGen_SLW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
int rS, rA, rB;
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB);
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_SLW, registerRA, registerRS, registerRB);
if ((opcode & PPC_OPC_RC))
PPCImlGen_UpdateCR0(ppcImlGenContext, registerRA);
return true;
}
bool PPCRecompilerImlGen_SRW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
int rS, rA, rB;
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB);
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_SRW, registerRA, registerRS, registerRB);
if ((opcode & PPC_OPC_RC))
PPCImlGen_UpdateCR0(ppcImlGenContext, registerRA);
return true;
}
bool PPCRecompilerImlGen_EXTSH(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
int rS, rA, rB;
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
PPC_ASSERT(rB==0);
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN_S16_TO_S32, registerRA, registerRS);
if ((opcode & PPC_OPC_RC))
PPCImlGen_UpdateCR0(ppcImlGenContext, registerRA);
return true;
}
bool PPCRecompilerImlGen_EXTSB(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rS, rA, rB;
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN_S8_TO_S32, registerRA, registerRS);
if ((opcode & PPC_OPC_RC))
PPCImlGen_UpdateCR0(ppcImlGenContext, registerRA);
return true;
}
bool PPCRecompilerImlGen_CNTLZW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rS, rA, rB;
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
PPC_ASSERT(rB==0);
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_CNTLZW, registerRA, registerRS);
if ((opcode & PPC_OPC_RC))
PPCImlGen_UpdateCR0(ppcImlGenContext, registerRA);
return true;
}
bool PPCRecompilerImlGen_NEG(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rD, rA, rB;
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
PPC_ASSERT(rB == 0);
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
uint32 registerRD = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NEG, registerRD, registerRA);
if (opcode & PPC_OPC_RC)
PPCImlGen_UpdateCR0(ppcImlGenContext, registerRD);
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;
}
bool PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, uint32 bitWidth, bool signExtend, bool isBigEndian, bool updateAddrReg)
{
sint32 rA, rD, rB;
PPC_OPC_TEMPL_X(opcode, rD, rA, rB);
if (updateAddrReg && (rA == 0 || rD == rB))
return false; // invalid instruction form
IMLReg regA = rA != 0 ? _GetRegGPR(ppcImlGenContext, rA) : IMLREG_INVALID;
IMLReg regB = _GetRegGPR(ppcImlGenContext, rB);
IMLReg regDst = _GetRegGPR(ppcImlGenContext, rD);
if (updateAddrReg)
{
ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_ADD, regA, regA, regB);
// use single register addressing
regB = regA;
regA = IMLREG_INVALID;
}
if(regA != IMLREG_INVALID)
PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext, regDst, regA, regB, bitWidth, signExtend, isBigEndian);
else
ppcImlGenContext->emitInst().make_r_memory(regDst, regB, 0, bitWidth, signExtend, isBigEndian);
return true;
}
bool PPCRecompilerImlGen_STORE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, uint32 bitWidth, bool isBigEndian, bool updateAddrReg)
{
int rA, rD;
uint32 imm;
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
IMLReg regA;
if (rA != 0)
{
regA = _GetRegGPR(ppcImlGenContext, rA);
}
else
{
if (updateAddrReg)
return false; // invalid instruction form
regA = _GetRegTemporary(ppcImlGenContext, 0);
ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regA, 0);
}
IMLReg regD = _GetRegGPR(ppcImlGenContext, rD);
if (updateAddrReg)
{
if (regD == regA)
{
// 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 == IMLREG_INVALID)
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 tmpReg = _GetRegTemporary(ppcImlGenContext, 0);
uint32 memOffset = 0;
while (nb > 0)
{
if (rD == rA)
return false;
cemu_assert(rD < 32);
IMLReg destinationRegister = _GetRegGPR(ppcImlGenContext, rD);
// load bytes one-by-one
for (sint32 b = 0; b < 4; b++)
{
ppcImlGenContext->emitInst().make_r_memory(tmpReg, memReg, memOffset + b, 8, false, false);
sint32 shiftAmount = (3 - b) * 8;
if(shiftAmount)
ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_LEFT_SHIFT, tmpReg, tmpReg, shiftAmount);
ppcImlGenContext->emitInst().make_r_r(b == 0 ? PPCREC_IML_OP_ASSIGN : PPCREC_IML_OP_OR, destinationRegister, tmpReg);
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 memReg = _GetRegGPR(ppcImlGenContext, rA);
IMLReg tmpReg = _GetRegTemporary(ppcImlGenContext, 0);
uint32 memOffset = 0;
while (nb > 0)
{
if (rS == rA)
return false;
cemu_assert(rS < 32);
IMLReg dataRegister = _GetRegGPR(ppcImlGenContext, rS);
// store bytes one-by-one
for (sint32 b = 0; b < 4; b++)
{
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, tmpReg, dataRegister);
sint32 shiftAmount = (3 - b) * 8;
if (shiftAmount)
ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_RIGHT_SHIFT_U, tmpReg, tmpReg, shiftAmount);
ppcImlGenContext->emitInst().make_memory_r(tmpReg, memReg, 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 != IMLREG_INVALID)
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 != IMLREG_INVALID)
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 generations
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
uint32 gprRegisterA = rA!=0?PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA):0;
uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB);
// store
if( rA != 0 )
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_DCBZ, gprRegisterA, gprRegisterB);
else
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_DCBZ, gprRegisterB, gprRegisterB);
return true;
}
bool PPCRecompilerImlGen_OR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
int rS, rA, rB;
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
// check for MR mnemonic
if( rS == rB )
{
// simple register copy
if( rA != rS ) // check if no-op
{
sint32 gprSourceReg = _GetRegGPR(ppcImlGenContext, rS);
sint32 gprDestReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg);
}
if ((opcode & PPC_OPC_RC))
{
sint32 gprDestReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA);
PPCImlGen_UpdateCR0(ppcImlGenContext, gprDestReg);
}
}
else
{
// rA = rS | rA
sint32 gprSource1Reg = _GetRegGPR(ppcImlGenContext, rS);
sint32 gprSource2Reg = _GetRegGPR(ppcImlGenContext, rB);
sint32 gprDestReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
if( gprSource1Reg == gprDestReg || gprSource2Reg == gprDestReg )
{
// make sure we don't overwrite rS or rA
if( gprSource1Reg == gprDestReg )
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_OR, gprDestReg, gprSource2Reg);
else
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_OR, gprDestReg, gprSource1Reg);
}
else
{
// rA = rS
if( gprDestReg != gprSource1Reg )
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, gprDestReg, gprSource1Reg);
// rA |= rB
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_OR, gprDestReg, gprSource2Reg);
}
if ((opcode & PPC_OPC_RC))
PPCImlGen_UpdateCR0(ppcImlGenContext, gprDestReg);
}
return true;
}
bool PPCRecompilerImlGen_NOR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
int rS, rA, rB;
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
//hCPU->gpr[rA] = ~(hCPU->gpr[rS] | hCPU->gpr[rB]);
// check for NOT mnemonic
if (rS == rB)
{
// simple register copy with NOT
sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rS);
sint32 gprDestReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA);
if (gprDestReg != gprSourceReg)
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg);
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, gprDestReg, gprDestReg);
if ((opcode & PPC_OPC_RC))
PPCImlGen_UpdateCR0(ppcImlGenContext, gprDestReg);
}
else
{
// rA = rS | rA
sint32 gprSource1Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rS);
sint32 gprSource2Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rB);
sint32 gprDestReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA);
if (gprSource1Reg == gprDestReg || gprSource2Reg == gprDestReg)
{
// make sure we don't overwrite rS or rA
if (gprSource1Reg == gprDestReg)
{
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_OR, gprDestReg, gprSource2Reg);
}
else
{
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_OR, gprDestReg, gprSource1Reg);
}
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, gprDestReg, gprDestReg);
if ((opcode & PPC_OPC_RC))
PPCImlGen_UpdateCR0(ppcImlGenContext, gprDestReg);
}
else
{
// rA = rS
if (gprDestReg != gprSource1Reg)
{
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, gprDestReg, gprSource1Reg);
}
// rA |= rB
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_OR, gprDestReg, gprSource2Reg);
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, gprDestReg, gprDestReg);
if ((opcode & PPC_OPC_RC))
PPCImlGen_UpdateCR0(ppcImlGenContext, gprDestReg);
}
}
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);
sint32 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(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rS, rA, rB;
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
// check for MR mnemonic
if( rS == rB )
{
// simple register copy
if( rA != rS ) // check if no-op
{
sint32 gprSourceReg = _GetRegGPR(ppcImlGenContext, rS);
sint32 gprDestReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg);
if ((opcode & PPC_OPC_RC))
PPCImlGen_UpdateCR0(ppcImlGenContext, gprDestReg);
}
else
{
cemu_assert_unimplemented(); // no-op -> verify this case
}
}
else
{
// rA = rS & rA
sint32 gprSource1Reg = _GetRegGPR(ppcImlGenContext, rS);
sint32 gprSource2Reg = _GetRegGPR(ppcImlGenContext, rB);
sint32 gprDestReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
if( gprSource1Reg == gprDestReg || gprSource2Reg == gprDestReg )
{
// make sure we don't overwrite rS or rA
if( gprSource1Reg == gprDestReg )
{
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_AND, gprDestReg, gprSource2Reg);
}
else
{
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_AND, gprDestReg, gprSource1Reg);
}
if ((opcode & PPC_OPC_RC))
PPCImlGen_UpdateCR0(ppcImlGenContext, gprDestReg);
}
else
{
// rA = rS
if( gprDestReg != gprSource1Reg )
{
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, gprDestReg, gprSource1Reg);
}
// rA &= rB
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_AND, gprDestReg, gprSource2Reg);
if ((opcode & PPC_OPC_RC))
PPCImlGen_UpdateCR0(ppcImlGenContext, gprDestReg);
}
}
return true;
}
bool PPCRecompilerImlGen_ANDC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rS, rA, rB;
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
//hCPU->gpr[rA] = hCPU->gpr[rS] & ~hCPU->gpr[rB];
//if (Opcode & PPC_OPC_RC) {
if( rS == rB )
{
// result is always 0 -> replace with XOR rA,rA
sint32 gprDestReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_XOR, gprDestReg, gprDestReg);
if ((opcode & PPC_OPC_RC))
PPCImlGen_UpdateCR0(ppcImlGenContext, gprDestReg);
}
else if( rA == rB )
{
// rB already in rA, therefore we complement rA first and then AND it with rS
sint32 gprRS = _GetRegGPR(ppcImlGenContext, rS);
sint32 gprDestReg = _GetRegGPR(ppcImlGenContext, rA);
// rA = ~rA
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, gprDestReg, gprDestReg);
// rA &= rS
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_AND, gprDestReg, gprRS);
if ((opcode & PPC_OPC_RC))
PPCImlGen_UpdateCR0(ppcImlGenContext, gprDestReg);
}
else
{
// a & (~b) is the same as ~((~a) | b)
sint32 gprDestReg = _GetRegGPR(ppcImlGenContext, rA);
sint32 gprRB = _GetRegGPR(ppcImlGenContext, rB);
sint32 gprRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
// move rS to rA (if required)
if( gprDestReg != gprRS )
{
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, gprDestReg, gprRS);
}
// rS already in rA, therefore we complement rS first and then OR it with rB
// rA = ~rA
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, gprDestReg, gprDestReg);
// rA |= rB
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_OR, gprDestReg, gprRB);
// rA = ~rA
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, gprDestReg, gprDestReg);
if ((opcode & PPC_OPC_RC))
PPCImlGen_UpdateCR0(ppcImlGenContext, gprDestReg);
}
return true;
}
void PPCRecompilerImlGen_ANDI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rS, rA;
uint32 imm;
PPC_OPC_TEMPL_D_UImm(opcode, rS, rA, imm);
sint32 gprSourceReg = _GetRegGPR(ppcImlGenContext, rS);
sint32 gprDestReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
// rA = rS
if( gprDestReg != gprSourceReg )
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg);
// rA &= imm32
ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_AND, gprDestReg, (sint32)imm);
// ANDI. always sets cr0
PPCImlGen_UpdateCR0(ppcImlGenContext, gprDestReg);
}
void PPCRecompilerImlGen_ANDIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rS, rA;
uint32 imm;
PPC_OPC_TEMPL_D_Shift16(opcode, rS, rA, imm);
IMLReg gprSourceReg = _GetRegGPR(ppcImlGenContext, rS);
sint32 gprDestReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
// rA = rS
if( gprDestReg != gprSourceReg )
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg);
// rA &= imm32
ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_AND, gprDestReg, (sint32)imm);
// ANDIS. always sets cr0
PPCImlGen_UpdateCR0(ppcImlGenContext, gprDestReg);
}
bool PPCRecompilerImlGen_XOR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
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 (opcode & PPC_OPC_RC)
PPCImlGen_UpdateCR0(ppcImlGenContext, regA);
return true;
}
bool PPCRecompilerImlGen_EQV(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
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, -1);
}
else
{
// rA = ~(rS ^ rB)
IMLReg regS = _GetRegGPR(ppcImlGenContext, rS);
IMLReg regB = _GetRegGPR(ppcImlGenContext, rB);
ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_XOR, regA, regS, regB);
ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, regA, regA);
}
if (opcode & PPC_OPC_RC)
PPCImlGen_UpdateCR0(ppcImlGenContext, regA);
return true;
}
void PPCRecompilerImlGen_ORI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rS, rA;
uint32 imm;
PPC_OPC_TEMPL_D_UImm(opcode, rS, rA, imm);
IMLReg regS = _GetRegGPR(ppcImlGenContext, rS);
IMLReg regA = _GetRegGPR(ppcImlGenContext, rA);
ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_OR, regA, regS, (sint32)imm);
}
void PPCRecompilerImlGen_ORIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rS, rA;
uint32 imm;
PPC_OPC_TEMPL_D_Shift16(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(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rS, rA;
uint32 imm;
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);
}
void PPCRecompilerImlGen_XORIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
{
sint32 rS, rA;
uint32 imm;
PPC_OPC_TEMPL_D_Shift16(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);
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->index = 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 structs 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)
{
if (segmentPoint->index != RA_INTER_RANGE_START && segmentPoint->index != RA_INTER_RANGE_END)
{
if (segmentPoint->index >= index)
segmentPoint->index += shiftBackCount;
}
// next
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 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:
PPCRecompilerImlGen_ORI(ppcImlGenContext, opcode);
break;
case 25:
PPCRecompilerImlGen_ORIS(ppcImlGenContext, opcode);
break;
case 26:
PPCRecompilerImlGen_XORI(ppcImlGenContext, opcode);
break;
case 27:
PPCRecompilerImlGen_XORIS(ppcImlGenContext, opcode);
break;
case 28:
PPCRecompilerImlGen_ANDI(ppcImlGenContext, opcode);
break;
case 29:
PPCRecompilerImlGen_ANDIS(ppcImlGenContext, opcode);
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
if (!PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 32, false, true, false))
unsupportedInstructionFound = true;
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:
if (PPCRecompilerImlGen_AND(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
if (!PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 32, false, true, true))
unsupportedInstructionFound = true;
break;
case 60:
if (PPCRecompilerImlGen_ANDC(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 75:
if (PPCRecompilerImlGen_MULHW(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 86:
// DCBF -> No-Op
break;
case 87: // LBZX
if (!PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 8, false, true, false))
unsupportedInstructionFound = true;
break;
case 104:
if (PPCRecompilerImlGen_NEG(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 119: // LBZUX
if (!PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 8, false, true, true))
unsupportedInstructionFound = true;
break;
case 124:
if (PPCRecompilerImlGen_NOR(ppcImlGenContext, opcode) == false)
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:
PPCRecompilerImlGen_MTCRF(ppcImlGenContext, opcode);
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
if (!PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 16, false, true, false))
unsupportedInstructionFound = true;
break;
case 284:
PPCRecompilerImlGen_EQV(ppcImlGenContext, opcode);
break;
case 311: // LHZUX
if (!PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 16, false, true, true))
unsupportedInstructionFound = true;
break;
case 316:
if (PPCRecompilerImlGen_XOR(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 339:
if (PPCRecompilerImlGen_MFSPR(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 343: // LHAX
if (!PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 16, true, true, false))
unsupportedInstructionFound = true;
break;
case 371:
if (PPCRecompilerImlGen_MFTB(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 375: // LHAUX
if (!PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 16, true, true, true))
unsupportedInstructionFound = 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:
if (PPCRecompilerImlGen_OR(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 491:
if (PPCRecompilerImlGen_DIVW(ppcImlGenContext, opcode) == false)
unsupportedInstructionFound = true;
break;
case 534: // LWBRX
if (!PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 32, false, false, false))
unsupportedInstructionFound = true;
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
if (!PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 16, false, false, false))
unsupportedInstructionFound = true;
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))
{
debug_printf("Recompiler encountered unsupported instruction at 0x%08x\n", addressOfCurrentInstruction);
ppcImlGenContext.currentOutputSegment = nullptr;
return false;
}
}
ppcImlGenContext.currentOutputSegment = nullptr;
return true;
}
// returns split segment from which the continued segment is available via seg->GetBranchNotTaken()
IMLSegment* PPCIMLGen_CreateSplitSegmentAtEnd(ppcImlGenContext_t& ppcImlGenContext, PPCBasicBlockInfo& basicBlockInfo)
{
IMLSegment* writeSegment = basicBlockInfo.GetSegmentForInstructionAppend();
IMLSegment* continuedSegment = ppcImlGenContext.InsertSegment(ppcImlGenContext.GetSegmentIndex(writeSegment) + 1);
continuedSegment->SetLinkBranchTaken(writeSegment->GetBranchTaken());
continuedSegment->SetLinkBranchNotTaken(writeSegment->GetBranchNotTaken());
writeSegment->SetLinkBranchNotTaken(continuedSegment);
writeSegment->SetLinkBranchTaken(nullptr);
if (ppcImlGenContext.currentOutputSegment == writeSegment)
ppcImlGenContext.currentOutputSegment = continuedSegment;
cemu_assert_debug(basicBlockInfo.appendSegment == writeSegment);
basicBlockInfo.appendSegment = continuedSegment;
return writeSegment;
}
// generates a new segment and sets it as branch target for the current write segment. Returns the created segment
IMLSegment* PPCIMLGen_CreateNewSegmentAsBranchTarget(ppcImlGenContext_t& ppcImlGenContext, PPCBasicBlockInfo& basicBlockInfo)
{
IMLSegment* writeSegment = basicBlockInfo.GetSegmentForInstructionAppend();
IMLSegment* branchTargetSegment = ppcImlGenContext.NewSegment();
cemu_assert_debug(!writeSegment->GetBranchTaken()); // must not have a target already
writeSegment->SetLinkBranchTaken(branchTargetSegment);
return branchTargetSegment;
}
// verify that current instruction is the last instruction of the active basic block
void PPCIMLGen_AssertIfNotLastSegmentInstruction(ppcImlGenContext_t& ppcImlGenContext)
{
cemu_assert_debug(ppcImlGenContext.currentBasicBlock->lastAddress == ppcImlGenContext.ppcAddressOfCurrentInstruction);
}
void PPCRecompiler_HandleCycleCheckCount(ppcImlGenContext_t& ppcImlGenContext, PPCBasicBlockInfo& basicBlockInfo)
{
IMLSegment* imlSegment = basicBlockInfo.GetFirstSegmentInChain();
if (!basicBlockInfo.hasBranchTarget)
return;
if (basicBlockInfo.branchTarget > basicBlockInfo.startAddress)
return;
// exclude non-infinite tight loops
if (IMLAnalyzer_IsTightFiniteLoop(imlSegment))
return;
// make the segment enterable so execution can return after passing a check
basicBlockInfo.GetFirstSegmentInChain()->SetEnterable(basicBlockInfo.startAddress);
IMLSegment* splitSeg = PPCIMLGen_CreateSplitSegmentAtEnd(ppcImlGenContext, basicBlockInfo);
splitSeg->AppendInstruction()->make_cjump_cycle_check();
IMLSegment* exitSegment = ppcImlGenContext.NewSegment();
splitSeg->SetLinkBranchTaken(exitSegment);
exitSegment->AppendInstruction()->make_macro(PPCREC_IML_MACRO_LEAVE, basicBlockInfo.startAddress, 0, 0);
}
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);
__debugbreak();
}
}
}
if (seg->nextSegmentBranchTaken)
{
if (!seg->HasSuffixInstruction())
{
debug_printf("---------------- SegmentDump (NoSuffixInstruction in segment 0x%x):\n", (int)segIndex);
IMLDebug_Dump(&ppcImlGenContext);
__debugbreak();
}
}
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;
}