#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& entryAddresses) : startAddress(startAddress), lastAddress(startAddress) { isEnterable = entryAddresses.find(startAddress) != entryAddresses.end(); } uint32 startAddress; uint32 lastAddress; // inclusive bool isEnterable{ false }; bool hasContinuedFlow{ true }; // non-branch path goes to next segment, assumed by default bool hasBranchTarget{ false }; uint32 branchTarget{}; // associated IML segments IMLSegment* firstSegment{}; // first segment in chain, used as branch target for other segments IMLSegment* appendSegment{}; // last segment in chain, additional instructions should be appended to this segment void SetInitialSegment(IMLSegment* seg) { cemu_assert_debug(!firstSegment); cemu_assert_debug(!appendSegment); firstSegment = seg; appendSegment = seg; } IMLSegment* GetFirstSegmentInChain() { return firstSegment; } IMLSegment* GetSegmentForInstructionAppend() { return appendSegment; } }; IMLInstruction* PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext_t* ppcImlGenContext) { IMLInstruction& inst = ppcImlGenContext->currentOutputSegment->imlList.emplace_back(); memset(&inst, 0x00, sizeof(IMLInstruction)); return &inst; } void PPCRecompilerImlGen_generateNewInstruction_conditional_r_s32(ppcImlGenContext_t* ppcImlGenContext, IMLInstruction* imlInstruction, uint32 operation, IMLReg registerIndex, sint32 immS32, uint32 crRegisterIndex, uint32 crBitIndex, bool bitMustBeSet) { if(imlInstruction == NULL) imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); else memset(imlInstruction, 0, sizeof(IMLInstruction)); imlInstruction->type = PPCREC_IML_TYPE_CONDITIONAL_R_S32; imlInstruction->operation = operation; // r_s32 operation imlInstruction->op_conditional_r_s32.regR = registerIndex; imlInstruction->op_conditional_r_s32.immS32 = immS32; // condition imlInstruction->op_conditional_r_s32.crRegisterIndex = crRegisterIndex; imlInstruction->op_conditional_r_s32.crBitIndex = crBitIndex; imlInstruction->op_conditional_r_s32.bitMustBeSet = bitMustBeSet; } void PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext_t* ppcImlGenContext, IMLReg registerDestination, IMLReg registerMemory1, IMLReg registerMemory2, uint32 copyWidth, bool signExtend, bool switchEndian) { cemu_assert_debug(registerMemory1.IsValid()); cemu_assert_debug(registerMemory2.IsValid()); cemu_assert_debug(registerDestination.IsValid()); IMLInstruction* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_LOAD_INDEXED; imlInstruction->operation = 0; imlInstruction->op_storeLoad.registerData = registerDestination; imlInstruction->op_storeLoad.registerMem = registerMemory1; imlInstruction->op_storeLoad.registerMem2 = registerMemory2; imlInstruction->op_storeLoad.copyWidth = copyWidth; imlInstruction->op_storeLoad.flags2.swapEndian = switchEndian; imlInstruction->op_storeLoad.flags2.signExtend = signExtend; } void PPCRecompilerImlGen_generateNewInstruction_memory_r_indexed(ppcImlGenContext_t* ppcImlGenContext, IMLReg registerDestination, IMLReg registerMemory1, IMLReg registerMemory2, uint32 copyWidth, bool signExtend, bool switchEndian) { cemu_assert_debug(registerMemory1.IsValid()); cemu_assert_debug(registerMemory2.IsValid()); cemu_assert_debug(registerDestination.IsValid()); IMLInstruction* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_STORE_INDEXED; imlInstruction->operation = 0; imlInstruction->op_storeLoad.registerData = registerDestination; imlInstruction->op_storeLoad.registerMem = registerMemory1; imlInstruction->op_storeLoad.registerMem2 = registerMemory2; imlInstruction->op_storeLoad.copyWidth = copyWidth; imlInstruction->op_storeLoad.flags2.swapEndian = switchEndian; imlInstruction->op_storeLoad.flags2.signExtend = signExtend; } // create and fill two segments (branch taken and branch not taken) as a follow up to the current segment and then merge flow afterwards template void PPCIMLGen_CreateSegmentBranchedPath(ppcImlGenContext_t& ppcImlGenContext, PPCBasicBlockInfo& basicBlockInfo, F1n genSegmentBranchTaken, F2n genSegmentBranchNotTaken) { IMLSegment* currentWriteSegment = basicBlockInfo.GetSegmentForInstructionAppend(); std::span segments = ppcImlGenContext.InsertSegments(ppcImlGenContext.GetSegmentIndex(currentWriteSegment) + 1, 3); IMLSegment* segBranchNotTaken = segments[0]; IMLSegment* segBranchTaken = segments[1]; IMLSegment* segMerge = segments[2]; // link the segments segMerge->SetLinkBranchTaken(currentWriteSegment->GetBranchTaken()); segMerge->SetLinkBranchNotTaken(currentWriteSegment->GetBranchNotTaken()); currentWriteSegment->SetLinkBranchTaken(segBranchTaken); currentWriteSegment->SetLinkBranchNotTaken(segBranchNotTaken); segBranchTaken->SetLinkBranchNotTaken(segMerge); segBranchNotTaken->SetLinkBranchTaken(segMerge); // generate code for branch taken segment ppcImlGenContext.currentOutputSegment = segBranchTaken; genSegmentBranchTaken(ppcImlGenContext); cemu_assert_debug(ppcImlGenContext.currentOutputSegment == segBranchTaken); // generate code for branch not taken segment ppcImlGenContext.currentOutputSegment = segBranchNotTaken; genSegmentBranchNotTaken(ppcImlGenContext); cemu_assert_debug(ppcImlGenContext.currentOutputSegment == segBranchNotTaken); ppcImlGenContext.emitInst().make_jump(); // make merge segment the new write segment ppcImlGenContext.currentOutputSegment = segMerge; basicBlockInfo.appendSegment = segMerge; } uint32 PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName) { 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; } IMLReg PPCRecompilerImlGen_loadRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName) { uint32 loadedRegisterIndex = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, mappedName); if (loadedRegisterIndex != PPC_REC_INVALID_REGISTER) return IMLReg(IMLRegFormat::I64, IMLRegFormat::I32, 0, loadedRegisterIndex); uint32 registerIndex = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, mappedName); return IMLReg(IMLRegFormat::I64, IMLRegFormat::I32, 0, 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 IMLReg _GetRegTemporaryS8(ppcImlGenContext_t* ppcImlGenContext, uint32 index) { cemu_assert_debug(index < 4); return PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + index); } /* * Loads a PPC fpr into any of the available IML FPU registers * If loadNew is false, it will check first if the fpr is already loaded into any IML register */ IMLReg PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName, bool loadNew) { if( loadNew == false ) { uint32 loadedRegisterIndex = PPCRecompilerImlGen_findFPRRegisterByMappedName(ppcImlGenContext, mappedName); if( loadedRegisterIndex != PPC_REC_INVALID_REGISTER ) return IMLReg(IMLRegFormat::F64, IMLRegFormat::F64, 0, loadedRegisterIndex); } uint32 registerIndex = PPCRecompilerImlGen_getAndLockFreeTemporaryFPR(ppcImlGenContext, mappedName); return IMLReg(IMLRegFormat::F64, IMLRegFormat::F64, 0, registerIndex); } /* * Checks if a PPC fpr register is already loaded into any IML register * If not, it will create a new undefined temporary IML FPU register and map the name (effectively overwriting the old ppc register) */ IMLReg PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName) { uint32 loadedRegisterIndex = PPCRecompilerImlGen_findFPRRegisterByMappedName(ppcImlGenContext, mappedName); if( loadedRegisterIndex != PPC_REC_INVALID_REGISTER ) return IMLReg(IMLRegFormat::F64, IMLRegFormat::F64, 0, loadedRegisterIndex); uint32 registerIndex = PPCRecompilerImlGen_getAndLockFreeTemporaryFPR(ppcImlGenContext, mappedName); return IMLReg(IMLRegFormat::F64, IMLRegFormat::F64, 0, 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, IMLReg regR) { IMLReg crBitRegLT = _GetRegCR(ppcImlGenContext, 0, Espresso::CR_BIT::CR_BIT_INDEX_LT); IMLReg crBitRegGT = _GetRegCR(ppcImlGenContext, 0, Espresso::CR_BIT::CR_BIT_INDEX_GT); IMLReg crBitRegEQ = _GetRegCR(ppcImlGenContext, 0, Espresso::CR_BIT::CR_BIT_INDEX_EQ); // todo - SO bit ppcImlGenContext->emitInst().make_compare_s32(regR, 0, crBitRegLT, IMLCondition::SIGNED_LT); ppcImlGenContext->emitInst().make_compare_s32(regR, 0, crBitRegGT, IMLCondition::SIGNED_GT); ppcImlGenContext->emitInst().make_compare_s32(regR, 0, crBitRegEQ, IMLCondition::EQ); //ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, crBitRegSO, 0); // todo - copy from XER //ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, registerR, registerR, 0, PPCREC_CR_MODE_LOGICAL); } void PPCRecompilerImlGen_TW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { // split before and after to make sure the macro is in an isolated segment that we can make enterable PPCIMLGen_CreateSplitSegmentAtEnd(*ppcImlGenContext, *ppcImlGenContext->currentBasicBlock); ppcImlGenContext->currentOutputSegment->SetEnterable(ppcImlGenContext->ppcAddressOfCurrentInstruction); PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext)->make_macro(PPCREC_IML_MACRO_LEAVE, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, 0, IMLREG_INVALID); IMLSegment* middleSeg = PPCIMLGen_CreateSplitSegmentAtEnd(*ppcImlGenContext, *ppcImlGenContext->currentBasicBlock); middleSeg->SetLinkBranchTaken(nullptr); middleSeg->SetLinkBranchNotTaken(nullptr); } bool PPCRecompilerImlGen_MTSPR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { uint32 rD, spr1, spr2, spr; PPC_OPC_TEMPL_XO(opcode, rD, spr1, spr2); spr = spr1 | (spr2<<5); IMLReg gprReg = _GetRegGPR(ppcImlGenContext, rD); if (spr == SPR_CTR || spr == SPR_LR) { IMLReg sprReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + spr); ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, sprReg, gprReg); } else if (spr >= SPR_UGQR0 && spr <= SPR_UGQR7) { IMLReg sprReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + spr); ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, sprReg, gprReg); ppcImlGenContext->tracking.modifiesGQR[spr - SPR_UGQR0] = true; } else return false; return true; } bool PPCRecompilerImlGen_MFSPR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { uint32 rD, spr1, spr2, spr; PPC_OPC_TEMPL_XO(opcode, rD, spr1, spr2); spr = spr1 | (spr2<<5); IMLReg gprReg = _GetRegGPR(ppcImlGenContext, rD); if (spr == SPR_LR || spr == SPR_CTR) { IMLReg sprReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + spr); ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, gprReg, sprReg); } else if (spr >= SPR_UGQR0 && spr <= SPR_UGQR7) { IMLReg sprReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + spr); ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, gprReg, sprReg); } else return false; return true; } bool PPCRecompilerImlGen_MFTB(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { printf("PPCRecompilerImlGen_MFTB(): Not supported\n"); return false; uint32 rD, spr1, spr2, spr; PPC_OPC_TEMPL_XO(opcode, rD, spr1, spr2); spr = spr1 | (spr2<<5); if (spr == 268 || spr == 269) { // TBL / TBU uint32 param2 = spr | (rD << 16); ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_MFTB, ppcImlGenContext->ppcAddressOfCurrentInstruction, param2, 0, IMLREG_INVALID); IMLSegment* middleSeg = PPCIMLGen_CreateSplitSegmentAtEnd(*ppcImlGenContext, *ppcImlGenContext->currentBasicBlock); return true; } return false; } bool PPCRecompilerImlGen_MFCR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { 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, IMLREG_INVALID); return true; } // is jump destination within recompiled function? if (ppcImlGenContext->boundaryTracker->ContainsAddress(jumpAddressDest)) ppcImlGenContext->emitInst().make_jump(); else ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_B_FAR, ppcImlGenContext->ppcAddressOfCurrentInstruction, jumpAddressDest, ppcImlGenContext->cyclesSinceLastBranch, IMLREG_INVALID); return true; } bool PPCRecompilerImlGen_BC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { PPCIMLGen_AssertIfNotLastSegmentInstruction(*ppcImlGenContext); uint32 BO, BI, BD; PPC_OPC_TEMPL_B(opcode, BO, BI, BD); // decodeOp_BC(uint32 opcode, uint32& BD, BOField& BO, uint32& BI, bool& AA, bool& LK) Espresso::BOField boField(BO); uint32 crRegister = BI/4; uint32 crBit = BI%4; uint32 jumpCondition = 0; bool conditionMustBeTrue = (BO&8)!=0; bool useDecrementer = (BO&4)==0; // bit not set -> decrement bool decrementerMustBeZero = (BO&2)!=0; // bit set -> branch if CTR = 0, bit not set -> branch if CTR != 0 bool ignoreCondition = (BO&16)!=0; IMLReg regCRBit; if (!ignoreCondition) regCRBit = _GetRegCR(ppcImlGenContext, crRegister, crBit); uint32 jumpAddressDest = BD; if( (opcode&PPC_OPC_AA) == 0 ) { jumpAddressDest = BD + (unsigned int)ppcImlGenContext->ppcAddressOfCurrentInstruction; } if( opcode&PPC_OPC_LK ) { if (useDecrementer) return false; // conditional function calls are not supported if( ignoreCondition == false ) { PPCBasicBlockInfo* currentBasicBlock = ppcImlGenContext->currentBasicBlock; IMLSegment* blSeg = PPCIMLGen_CreateNewSegmentAsBranchTarget(*ppcImlGenContext, *currentBasicBlock); ppcImlGenContext->emitInst().make_conditional_jump(regCRBit, conditionMustBeTrue); blSeg->AppendInstruction()->make_macro(PPCREC_IML_MACRO_BL, ppcImlGenContext->ppcAddressOfCurrentInstruction, jumpAddressDest, ppcImlGenContext->cyclesSinceLastBranch, IMLREG_INVALID); return true; } return false; } // generate iml instructions depending on flags if( useDecrementer ) { if( ignoreCondition == false ) return false; // not supported for the moment IMLReg ctrRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0+SPR_CTR); IMLReg tmpBoolReg = _GetRegTemporaryS8(ppcImlGenContext, 1); ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_SUB, ctrRegister, ctrRegister, 1); ppcImlGenContext->emitInst().make_compare_s32(ctrRegister, 0, tmpBoolReg, decrementerMustBeZero ? IMLCondition::EQ : IMLCondition::NEQ); ppcImlGenContext->emitInst().make_conditional_jump(tmpBoolReg, true); return true; } else { if( ignoreCondition ) { // branch always, no condition and no decrementer // not supported return false; } else { if (ppcImlGenContext->boundaryTracker->ContainsAddress(jumpAddressDest)) { // near jump ppcImlGenContext->emitInst().make_conditional_jump(regCRBit, conditionMustBeTrue); } else { // far jump debug_printf("PPCRecompilerImlGen_BC(): Far jump not supported yet"); return false; } } } return true; } // BCCTR or BCLR bool PPCRecompilerImlGen_BCSPR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, uint32 sprReg) { PPCIMLGen_AssertIfNotLastSegmentInstruction(*ppcImlGenContext); Espresso::BOField BO; uint32 BI; bool LK; Espresso::decodeOp_BCSPR(opcode, BO, BI, LK); uint32 crRegister = BI/4; uint32 crBit = BI%4; IMLReg regCRBit; if (!BO.conditionIgnore()) regCRBit = _GetRegCR(ppcImlGenContext, crRegister, crBit); IMLReg branchDestReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + sprReg); if (LK) { if (sprReg == SPR_LR) { // if the branch target is LR, then preserve it in a temporary cemu_assert_suspicious(); // this case needs testing IMLReg tmpRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY); ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, tmpRegister, branchDestReg); branchDestReg = tmpRegister; } IMLReg registerLR = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + SPR_LR); ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, registerLR, ppcImlGenContext->ppcAddressOfCurrentInstruction + 4); } if (!BO.decrementerIgnore()) { cemu_assert_unimplemented(); return false; } else if (!BO.conditionIgnore()) { // no decrementer but CR check cemu_assert_debug(ppcImlGenContext->currentBasicBlock->hasContinuedFlow); cemu_assert_debug(!ppcImlGenContext->currentBasicBlock->hasBranchTarget); PPCBasicBlockInfo* currentBasicBlock = ppcImlGenContext->currentBasicBlock; IMLSegment* bctrSeg = PPCIMLGen_CreateNewSegmentAsBranchTarget(*ppcImlGenContext, *currentBasicBlock); ppcImlGenContext->emitInst().make_conditional_jump(regCRBit, !BO.conditionInverted()); bctrSeg->AppendInstruction()->make_macro(PPCREC_IML_MACRO_B_TO_REG, 0, 0, 0, branchDestReg); } else { // branch always, no condition and no decrementer check cemu_assert_debug(!ppcImlGenContext->currentBasicBlock->hasContinuedFlow); cemu_assert_debug(!ppcImlGenContext->currentBasicBlock->hasBranchTarget); ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_B_TO_REG, 0, 0, 0, branchDestReg); } return true; } bool PPCRecompilerImlGen_ISYNC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { return true; } bool PPCRecompilerImlGen_SYNC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { return true; } bool PPCRecompilerImlGen_ADD(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_ADD, regD, regA, regB); if (opcode & PPC_OPC_RC) PPCImlGen_UpdateCR0(ppcImlGenContext, regD); return true; } bool PPCRecompilerImlGen_ADDI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); if (rA != 0) { IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_ADD, regD, regA, imm); } else { ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regD, imm); } return true; } bool PPCRecompilerImlGen_ADDIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rD, rA; uint32 imm; PPC_OPC_TEMPL_D_Shift16(opcode, rD, rA, imm); IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); if (rA != 0) { IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_ADD, regD, regA, (sint32)imm); } else { ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regD, (sint32)imm); } return true; } bool PPCRecompilerImlGen_ADDC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { // r = a + b -> update carry sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); IMLReg regRA = _GetRegGPR(ppcImlGenContext, rA); IMLReg regRB = _GetRegGPR(ppcImlGenContext, rB); IMLReg regRD = _GetRegGPR(ppcImlGenContext, rD); IMLReg regCa = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_CA); ppcImlGenContext->emitInst().make_r_r_r_carry(PPCREC_IML_OP_ADD, regRD, regRA, regRB, regCa); if (opcode & PPC_OPC_RC) PPCImlGen_UpdateCR0(ppcImlGenContext, regRD); return true; } bool PPCRecompilerImlGen_ADDIC_(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, bool updateCR0) { sint32 rD, rA; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); IMLReg regCa = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_CA); ppcImlGenContext->emitInst().make_r_r_s32_carry(PPCREC_IML_OP_ADD, regD, regA, (sint32)imm, regCa); if(updateCR0) PPCImlGen_UpdateCR0(ppcImlGenContext, regD); return true; } bool PPCRecompilerImlGen_ADDE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { // r = a + b + carry -> update carry sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); IMLReg regRA = _GetRegGPR(ppcImlGenContext, rA); IMLReg regRB = _GetRegGPR(ppcImlGenContext, rB); IMLReg regRD = _GetRegGPR(ppcImlGenContext, rD); IMLReg regCa = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_CA); ppcImlGenContext->emitInst().make_r_r_r_carry(PPCREC_IML_OP_ADD_WITH_CARRY, regRD, regRA, regRB, regCa); if (opcode & PPC_OPC_RC) PPCImlGen_UpdateCR0(ppcImlGenContext, regRD); return true; } bool PPCRecompilerImlGen_ADDZE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { // r = a + carry -> update carry sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); IMLReg regRA = _GetRegGPR(ppcImlGenContext, rA); IMLReg regRD = _GetRegGPR(ppcImlGenContext, rD); IMLReg regCa = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_CA); ppcImlGenContext->emitInst().make_r_r_s32_carry(PPCREC_IML_OP_ADD_WITH_CARRY, regRD, regRA, 0, regCa); if (opcode & PPC_OPC_RC) PPCImlGen_UpdateCR0(ppcImlGenContext, regRD); return true; } bool PPCRecompilerImlGen_ADDME(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { // r = a + 0xFFFFFFFF + carry -> update carry sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); IMLReg regRA = _GetRegGPR(ppcImlGenContext, rA); IMLReg regRD = _GetRegGPR(ppcImlGenContext, rD); IMLReg regCa = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_CA); ppcImlGenContext->emitInst().make_r_r_s32_carry(PPCREC_IML_OP_ADD_WITH_CARRY, regRD, regRA, -1, regCa); if (opcode & PPC_OPC_RC) PPCImlGen_UpdateCR0(ppcImlGenContext, regRD); return true; } bool PPCRecompilerImlGen_SUBF(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); // rD = ~rA + rB + 1 IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_SUB, regD, regB, regA); if ((opcode & PPC_OPC_RC)) PPCImlGen_UpdateCR0(ppcImlGenContext, regD); return true; } bool PPCRecompilerImlGen_SUBFE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { // d = ~a + b + ca; sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); IMLReg regTmp = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 0); IMLReg regCa = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_CA); ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, regTmp, regA); ppcImlGenContext->emitInst().make_r_r_r_carry(PPCREC_IML_OP_ADD_WITH_CARRY, regD, regTmp, regB, regCa); if (opcode & PPC_OPC_RC) PPCImlGen_UpdateCR0(ppcImlGenContext, regD); return true; } bool PPCRecompilerImlGen_SUBFZE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { // d = ~a + ca; sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); IMLReg regTmp = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 0); IMLReg regCa = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_CA); ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, regTmp, regA); ppcImlGenContext->emitInst().make_r_r_s32_carry(PPCREC_IML_OP_ADD_WITH_CARRY, regD, regTmp, 0, regCa); if (opcode & PPC_OPC_RC) PPCImlGen_UpdateCR0(ppcImlGenContext, regD); return true; } bool PPCRecompilerImlGen_SUBFC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { // d = ~a + b + 1; sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); IMLReg regTmp = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 0); IMLReg regCa = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_CA); ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, regTmp, regA); ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regCa, 1); // set input carry to simulate offset of 1 ppcImlGenContext->emitInst().make_r_r_r_carry(PPCREC_IML_OP_ADD_WITH_CARRY, regD, regTmp, regB, regCa); if ((opcode & PPC_OPC_RC)) PPCImlGen_UpdateCR0(ppcImlGenContext, regD); return true; } bool PPCRecompilerImlGen_SUBFIC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { // d = ~a + imm + 1 sint32 rD, rA; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); IMLReg regCa = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_CA); IMLReg regTmp = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 0); ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, regTmp, regA); ppcImlGenContext->emitInst().make_r_r_s32_carry(PPCREC_IML_OP_ADD, regD, regTmp, (sint32)imm + 1, regCa); return true; } bool PPCRecompilerImlGen_MULLI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rD, rA; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_MULTIPLY_SIGNED, regD, regA, (sint32)imm); return true; } bool PPCRecompilerImlGen_MULLW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); if (opcode & PPC_OPC_OE) { return false; } ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_MULTIPLY_SIGNED, regD, regA, regB); if (opcode & PPC_OPC_RC) PPCImlGen_UpdateCR0(ppcImlGenContext, regD); return true; } bool PPCRecompilerImlGen_MULHW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_MULTIPLY_HIGH_SIGNED, regD, regA, regB); if (opcode & PPC_OPC_RC) PPCImlGen_UpdateCR0(ppcImlGenContext, regD); return true; } bool PPCRecompilerImlGen_MULHWU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_MULTIPLY_HIGH_UNSIGNED, regD, regA, regB); if (opcode & PPC_OPC_RC) PPCImlGen_UpdateCR0(ppcImlGenContext, regD); return true; } bool PPCRecompilerImlGen_DIVW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); IMLReg regR = _GetRegGPR(ppcImlGenContext, rD); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_DIVIDE_SIGNED, regR, regA, regB); if (opcode & PPC_OPC_RC) PPCImlGen_UpdateCR0(ppcImlGenContext, regR); return true; } bool PPCRecompilerImlGen_DIVWU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_DIVIDE_UNSIGNED, regD, regA, regB); if (opcode & PPC_OPC_RC) PPCImlGen_UpdateCR0(ppcImlGenContext, regD); return true; } bool PPCRecompilerImlGen_RLWINM(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rS, rA, SH, MB, ME; PPC_OPC_TEMPL_M(opcode, rS, rA, SH, MB, ME); uint32 mask = ppc_mask(MB, ME); IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); IMLReg regA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); if( ME == (31-SH) && MB == 0 ) { // SLWI ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_LEFT_SHIFT, regA, regS, SH); } else if( SH == (32-MB) && ME == 31 ) { // SRWI ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_RIGHT_SHIFT_U, regA, regS, MB); } else { // general handler if (rA != rS) ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, regA, regS); if (SH != 0) ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_LEFT_ROTATE, regA, SH); if (mask != 0xFFFFFFFF) ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_AND, regA, regA, (sint32)mask); } if (opcode & PPC_OPC_RC) PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } bool PPCRecompilerImlGen_RLWIMI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rS, rA, SH, MB, ME; PPC_OPC_TEMPL_M(opcode, rS, rA, SH, MB, ME); IMLReg regS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); IMLReg regA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); // pack RLWIMI parameters into single integer uint32 vImm = MB|(ME<<8)|(SH<<16); ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_RLWIMI, regA, regS, (sint32)vImm); if (opcode & PPC_OPC_RC) PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } bool PPCRecompilerImlGen_RLWNM(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rS, rA, rB, MB, ME; PPC_OPC_TEMPL_M(opcode, rS, rA, rB, MB, ME); uint32 mask = ppc_mask(MB, ME); IMLReg regS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); IMLReg regB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); IMLReg regA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_LEFT_ROTATE, regA, regS, regB); if( mask != 0xFFFFFFFF ) ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_AND, regA, regA, (sint32)mask); if (opcode & PPC_OPC_RC) PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } bool PPCRecompilerImlGen_SRAW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { // unlike SRAWI, for SRAW the shift range is 0-63 (masked to 6 bits) // but only shifts up to register bitwidth minus one are well defined in IML so this requires special handling for shifts >= 32 sint32 rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); IMLReg regS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); IMLReg regB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); IMLReg regA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); IMLReg regCarry = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_XER_CA); IMLReg regTmpShiftAmount = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 0); IMLReg regTmpCondBool = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 1); IMLReg regTmp1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 2); IMLReg regTmp2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 3); // load masked shift factor into temporary register ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_AND, regTmpShiftAmount, regB, 0x3F); ppcImlGenContext->emitInst().make_compare_s32(regTmpShiftAmount, 32, regTmpCondBool, IMLCondition::UNSIGNED_GT); ppcImlGenContext->emitInst().make_conditional_jump(regTmpCondBool, true); PPCIMLGen_CreateSegmentBranchedPath(*ppcImlGenContext, *ppcImlGenContext->currentBasicBlock, [&](ppcImlGenContext_t& genCtx) { /* branch taken */ genCtx.emitInst().make_r_r_r(PPCREC_IML_OP_RIGHT_SHIFT_S, regA, regS, regTmpShiftAmount); genCtx.emitInst().make_compare_s32(regA, 0, regCarry, IMLCondition::NEQ); // if the sign bit is still set it also means it was shifted out and we can set carry }, [&](ppcImlGenContext_t& genCtx) { /* branch not taken, shift size below 32 */ genCtx.emitInst().make_r_r_s32(PPCREC_IML_OP_RIGHT_SHIFT_S, regTmp1, regS, 31); // signMask = input >> 31 (arithmetic shift) genCtx.emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regTmp2, 1); // shiftMask = ((1<emitInst().make_r_r_s32(PPCREC_IML_OP_RIGHT_SHIFT_S, regTmp, regS, 31); // signMask = input >> 31 (arithmetic shift) ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_AND, regTmp, regTmp, regS); // testValue = input & signMask & ((1<emitInst().make_r_r_s32(PPCREC_IML_OP_AND, regTmp, regTmp, ((1 << SH) - 1)); ppcImlGenContext->emitInst().make_compare_s32(regTmp, 0, regCarry, IMLCondition::NEQ); // ca = (testValue != 0) // do the actual shift ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_RIGHT_SHIFT_S, regA, regS, (sint32)SH); if (opcode & PPC_OPC_RC) PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } bool PPCRecompilerImlGen_SLW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_SLW, regA, regS, regB); if ((opcode & PPC_OPC_RC)) PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } bool PPCRecompilerImlGen_SRW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_SRW, regA, regS, regB); if (opcode & PPC_OPC_RC) PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } bool PPCRecompilerImlGen_EXTSH(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN_S16_TO_S32, regA, regS); if (opcode & PPC_OPC_RC) PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } bool PPCRecompilerImlGen_EXTSB(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN_S8_TO_S32, regA, regS); if ((opcode & PPC_OPC_RC)) PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } bool PPCRecompilerImlGen_CNTLZW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_CNTLZW, regA, regS); if ((opcode & PPC_OPC_RC)) PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } bool PPCRecompilerImlGen_NEG(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NEG, regD, regA); if (opcode & PPC_OPC_RC) PPCImlGen_UpdateCR0(ppcImlGenContext, regD); return true; } bool PPCRecompilerImlGen_LOAD(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, uint32 bitWidth, bool signExtend, bool isBigEndian, bool updateAddrReg) { int rA, rD; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); IMLReg regMemAddr; if (rA == 0) { if (updateAddrReg) return false; // invalid instruction form regMemAddr = _GetRegTemporary(ppcImlGenContext, 0); ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regMemAddr, 0); } else { if (updateAddrReg && rA == rD) return false; // invalid instruction form regMemAddr = _GetRegGPR(ppcImlGenContext, rA); } if (updateAddrReg) { ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_ADD, regMemAddr, regMemAddr, (sint32)imm); imm = 0; } IMLReg regDst = _GetRegGPR(ppcImlGenContext, rD); ppcImlGenContext->emitInst().make_r_memory(regDst, regMemAddr, (sint32)imm, bitWidth, signExtend, isBigEndian); return true; } 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.IsValid()) PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext, regDst, regA, regB, bitWidth, signExtend, isBigEndian); else ppcImlGenContext->emitInst().make_r_memory(regDst, regB, 0, bitWidth, signExtend, isBigEndian); return true; } bool PPCRecompilerImlGen_STORE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, uint32 bitWidth, bool isBigEndian, bool updateAddrReg) { int rA, rD; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); IMLReg regA; if (rA != 0) { regA = _GetRegGPR(ppcImlGenContext, rA); } else { if (updateAddrReg) return false; // invalid instruction form regA = _GetRegTemporary(ppcImlGenContext, 0); ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regA, 0); } IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); if (updateAddrReg) { if (rD == rA) { // make sure to keep source data intact regD = _GetRegTemporary(ppcImlGenContext, 0); ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, regD, regA); } ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_ADD, regA, regA, (sint32)imm); imm = 0; } ppcImlGenContext->emitInst().make_memory_r(regD, regA, (sint32)imm, bitWidth, isBigEndian); return true; } bool PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, uint32 bitWidth, bool isBigEndian, bool updateAddrReg) { sint32 rA, rS, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); IMLReg regA = rA != 0 ? _GetRegGPR(ppcImlGenContext, rA) : IMLREG_INVALID; IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); IMLReg regSrc = _GetRegGPR(ppcImlGenContext, rS); if (updateAddrReg) { if(rA == 0) return false; // invalid instruction form if (regSrc == regA) { // make sure to keep source data intact regSrc = _GetRegTemporary(ppcImlGenContext, 0); ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, regSrc, regA); } ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_ADD, regA, regA, regB); // use single register addressing regB = regA; regA = IMLREG_INVALID; } if (regA.IsInvalid()) ppcImlGenContext->emitInst().make_memory_r(regSrc, regB, 0, bitWidth, isBigEndian); else PPCRecompilerImlGen_generateNewInstruction_memory_r_indexed(ppcImlGenContext, regSrc, regA, regB, bitWidth, false, isBigEndian); return true; } void PPCRecompilerImlGen_LMW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); cemu_assert_debug(rA != 0); sint32 index = 0; while (rD <= 31) { IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); IMLReg regD = _GetRegGPR(ppcImlGenContext, rD); // load word ppcImlGenContext->emitInst().make_r_memory(regD, regA, (sint32)imm + index * 4, 32, false, true); // next rD++; index++; } } void PPCRecompilerImlGen_STMW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rS, rA; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rS, rA, imm); cemu_assert_debug(rA != 0); sint32 index = 0; while( rS <= 31 ) { IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); // store word ppcImlGenContext->emitInst().make_memory_r(regS, regA, (sint32)imm + index * 4, 32, true); // next rS++; index++; } } bool PPCRecompilerImlGen_LSWI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rA, rD, nb; PPC_OPC_TEMPL_X(opcode, rD, rA, nb); if( nb == 0 ) nb = 32; if (rA == 0) { cemu_assert_unimplemented(); // special form where gpr is ignored and EA is 0 return false; } // potential optimization: On x86 unaligned access is allowed and we could handle the case nb==4 with a single memory read, and nb==2 with a memory read and shift IMLReg memReg = _GetRegGPR(ppcImlGenContext, rA); IMLReg regTmp = _GetRegTemporary(ppcImlGenContext, 0); uint32 memOffset = 0; while (nb > 0) { if (rD == rA) return false; cemu_assert(rD < 32); IMLReg regDst = _GetRegGPR(ppcImlGenContext, rD); // load bytes one-by-one for (sint32 b = 0; b < 4; b++) { ppcImlGenContext->emitInst().make_r_memory(regTmp, memReg, memOffset + b, 8, false, false); sint32 shiftAmount = (3 - b) * 8; if(shiftAmount) ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_LEFT_SHIFT, regTmp, regTmp, shiftAmount); if(b == 0) ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, regDst, regTmp); else ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_OR, regDst, regDst, regTmp); nb--; if (nb == 0) break; } memOffset += 4; rD++; } return true; } bool PPCRecompilerImlGen_STSWI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rA, rS, nb; PPC_OPC_TEMPL_X(opcode, rS, rA, nb); if( nb == 0 ) nb = 32; IMLReg regMem = _GetRegGPR(ppcImlGenContext, rA); IMLReg regTmp = _GetRegTemporary(ppcImlGenContext, 0); uint32 memOffset = 0; while (nb > 0) { if (rS == rA) return false; cemu_assert(rS < 32); IMLReg regSrc = _GetRegGPR(ppcImlGenContext, rS); // store bytes one-by-one for (sint32 b = 0; b < 4; b++) { ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, regTmp, regSrc); sint32 shiftAmount = (3 - b) * 8; if (shiftAmount) ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_RIGHT_SHIFT_U, regTmp, regTmp, shiftAmount); ppcImlGenContext->emitInst().make_memory_r(regTmp, regMem, memOffset + b, 8, false); nb--; if (nb == 0) break; } memOffset += 4; rS++; } return true; } bool PPCRecompilerImlGen_LWARX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rA, rD, rB; PPC_OPC_TEMPL_X(opcode, rD, rA, rB); IMLReg regA = rA != 0 ? PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA) : IMLREG_INVALID; IMLReg regB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rB); IMLReg regD = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rD); IMLReg regMemResEA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_CPU_MEMRES_EA); IMLReg regMemResVal = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_CPU_MEMRES_VAL); // calculate EA if (regA.IsValid()) ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_ADD, regMemResEA, regA, regB); else ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, regMemResEA, regB); // load word ppcImlGenContext->emitInst().make_r_memory(regD, regMemResEA, 0, 32, false, true); ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, regMemResVal, regD); return true; } bool PPCRecompilerImlGen_STWCX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rA, rS, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); IMLReg regA = rA != 0 ? PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA) : IMLREG_INVALID; IMLReg regB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rB); IMLReg regData = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rS); IMLReg regTmpDataBE = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 2); IMLReg regTmpCompareBE = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 3); // calculate EA IMLReg regCalcEA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY); if (regA.IsValid()) ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_ADD, regCalcEA, regA, regB); else ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, regCalcEA, regB); // get CR bit regs and set LT, GT and SO immediately IMLReg regCrLT = _GetRegCR(ppcImlGenContext, 0, Espresso::CR_BIT_INDEX_LT); IMLReg regCrGT = _GetRegCR(ppcImlGenContext, 0, Espresso::CR_BIT_INDEX_GT); IMLReg regCrEQ = _GetRegCR(ppcImlGenContext, 0, Espresso::CR_BIT_INDEX_EQ); IMLReg regCrSO = _GetRegCR(ppcImlGenContext, 0, Espresso::CR_BIT_INDEX_SO); IMLReg regXerSO = _GetRegCR(ppcImlGenContext, 0, Espresso::CR_BIT_INDEX_SO); ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regCrLT, 0); ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regCrGT, 0); ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, regCrSO, regXerSO); // get regs for reservation address and value IMLReg regMemResEA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_CPU_MEMRES_EA); IMLReg regMemResVal = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_CPU_MEMRES_VAL); // compare calculated EA with reservation IMLReg regTmpBool = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY + 1); ppcImlGenContext->emitInst().make_compare(regCalcEA, regMemResEA, regTmpBool, IMLCondition::EQ); ppcImlGenContext->emitInst().make_conditional_jump(regTmpBool, true); PPCIMLGen_CreateSegmentBranchedPath(*ppcImlGenContext, *ppcImlGenContext->currentBasicBlock, [&](ppcImlGenContext_t& genCtx) { /* branch taken, EA matching */ ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ENDIAN_SWAP, regTmpDataBE, regData); ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ENDIAN_SWAP, regTmpCompareBE, regMemResVal); ppcImlGenContext->emitInst().make_atomic_cmp_store(regMemResEA, regTmpCompareBE, regTmpDataBE, regCrEQ); }, [&](ppcImlGenContext_t& genCtx) { /* branch not taken, EA mismatching */ ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regCrEQ, 0); } ); // reset reservation // I found contradictory information of whether the reservation is cleared in all cases, so unit testing would be required // Most sources state that it is cleared on successful store. They don't explicitly mention what happens on failure // "The PowerPC 600 series, part 7: Atomic memory access and cache coherency" states that it is always cleared // There may also be different behavior between individual PPC architectures ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regMemResEA, 0); ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regMemResVal, 0); return true; } bool PPCRecompilerImlGen_DCBZ(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rA, rB; rA = (opcode>>16)&0x1F; rB = (opcode>>11)&0x1F; // prepare registers IMLReg gprRegisterA = rA!=0?PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA):IMLREG_INVALID; IMLReg gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); // store if( rA != 0 ) 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_NOR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, bool complementResult) { int rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); if(rS == rB) // check for MR mnemonic ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, regA, regS); else ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_OR, regA, regS, regB); if(complementResult) ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, regA, regA); if (opcode & PPC_OPC_RC) PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } bool PPCRecompilerImlGen_ORC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); // rA = rS | ~rB; IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); IMLReg regTmp = _GetRegTemporary(ppcImlGenContext, 0); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, regTmp, regB); ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_OR, regA, regS, regTmp); if (opcode & PPC_OPC_RC) PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } bool PPCRecompilerImlGen_AND_NAND(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, bool complementResult) { int rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); if (regS == regB) ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_ASSIGN, regA, regS); else ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_AND, regA, regS, regB); if (complementResult) ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, regA, regA); if (opcode & PPC_OPC_RC) PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } bool PPCRecompilerImlGen_ANDC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); // rA = rS & ~rB; IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); IMLReg regTmp = _GetRegTemporary(ppcImlGenContext, 0); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, regTmp, regB); ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_AND, regA, regS, regTmp); if (opcode & PPC_OPC_RC) PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } bool PPCRecompilerImlGen_XOR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, bool complementResult) { sint32 rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); if( rS == rB ) { ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regA, 0); } else { IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); IMLReg regB = _GetRegGPR(ppcImlGenContext, rB); ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_XOR, regA, regS, regB); } if (complementResult) ppcImlGenContext->emitInst().make_r_r(PPCREC_IML_OP_NOT, regA, regA); if (opcode & PPC_OPC_RC) PPCImlGen_UpdateCR0(ppcImlGenContext, regA); return true; } void PPCRecompilerImlGen_ANDI_ANDIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, bool isShifted) { sint32 rS, rA; uint32 imm; if (isShifted) { PPC_OPC_TEMPL_D_Shift16(opcode, rS, rA, imm); } else { PPC_OPC_TEMPL_D_UImm(opcode, rS, rA, imm); } IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_AND, regA, regS, (sint32)imm); // ANDI/ANDIS always updates cr0 PPCImlGen_UpdateCR0(ppcImlGenContext, regA); } void PPCRecompilerImlGen_ORI_ORIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, bool isShifted) { sint32 rS, rA; uint32 imm; if (isShifted) { PPC_OPC_TEMPL_D_Shift16(opcode, rS, rA, imm); } else { PPC_OPC_TEMPL_D_UImm(opcode, rS, rA, imm); } IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_OR, regA, regS, (sint32)imm); } void PPCRecompilerImlGen_XORI_XORIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, bool isShifted) { sint32 rS, rA; uint32 imm; if (isShifted) { PPC_OPC_TEMPL_D_Shift16(opcode, rS, rA, imm); } else { PPC_OPC_TEMPL_D_UImm(opcode, rS, rA, imm); } IMLReg regS = _GetRegGPR(ppcImlGenContext, rS); IMLReg regA = _GetRegGPR(ppcImlGenContext, rA); ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_XOR, regA, regS, (sint32)imm); } bool PPCRecompilerImlGen_CROR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int crD, crA, crB; PPC_OPC_TEMPL_X(opcode, crD, crA, crB); IMLReg regCrA = _GetRegCR(ppcImlGenContext, crA); IMLReg regCrB = _GetRegCR(ppcImlGenContext, crB); IMLReg regCrR = _GetRegCR(ppcImlGenContext, crD); ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_OR, regCrR, regCrA, regCrB); return true; } bool PPCRecompilerImlGen_CRORC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int crD, crA, crB; PPC_OPC_TEMPL_X(opcode, crD, crA, crB); IMLReg regCrA = _GetRegCR(ppcImlGenContext, crA); IMLReg regCrB = _GetRegCR(ppcImlGenContext, crB); IMLReg regCrR = _GetRegCR(ppcImlGenContext, crD); IMLReg regTmp = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY); ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_XOR, regTmp, regCrB, 1); // invert crB ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_OR, regCrR, regCrA, regTmp); return true; } bool PPCRecompilerImlGen_CRAND(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int crD, crA, crB; PPC_OPC_TEMPL_X(opcode, crD, crA, crB); IMLReg regCrA = _GetRegCR(ppcImlGenContext, crA); IMLReg regCrB = _GetRegCR(ppcImlGenContext, crB); IMLReg regCrR = _GetRegCR(ppcImlGenContext, crD); ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_AND, regCrR, regCrA, regCrB); return true; } bool PPCRecompilerImlGen_CRANDC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int crD, crA, crB; PPC_OPC_TEMPL_X(opcode, crD, crA, crB); IMLReg regCrA = _GetRegCR(ppcImlGenContext, crA); IMLReg regCrB = _GetRegCR(ppcImlGenContext, crB); IMLReg regCrR = _GetRegCR(ppcImlGenContext, crD); IMLReg regTmp = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY); ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_XOR, regTmp, regCrB, 1); // invert crB ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_AND, regCrR, regCrA, regTmp); return true; } bool PPCRecompilerImlGen_CRXOR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int crD, crA, crB; PPC_OPC_TEMPL_X(opcode, crD, crA, crB); IMLReg regCrA = _GetRegCR(ppcImlGenContext, crA); IMLReg regCrB = _GetRegCR(ppcImlGenContext, crB); IMLReg regCrR = _GetRegCR(ppcImlGenContext, crD); if (regCrA == regCrB) { ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regCrR, 0); return true; } ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_XOR, regCrR, regCrA, regCrB); return true; } bool PPCRecompilerImlGen_CREQV(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int crD, crA, crB; PPC_OPC_TEMPL_X(opcode, crD, crA, crB); IMLReg regCrA = _GetRegCR(ppcImlGenContext, crA); IMLReg regCrB = _GetRegCR(ppcImlGenContext, crB); IMLReg regCrR = _GetRegCR(ppcImlGenContext, crD); if (regCrA == regCrB) { ppcImlGenContext->emitInst().make_r_s32(PPCREC_IML_OP_ASSIGN, regCrR, 1); return true; } IMLReg regTmp = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY); ppcImlGenContext->emitInst().make_r_r_s32(PPCREC_IML_OP_XOR, regTmp, regCrB, 1); // invert crB ppcImlGenContext->emitInst().make_r_r_r(PPCREC_IML_OP_XOR, regCrR, regCrA, regTmp); return true; } bool PPCRecompilerImlGen_HLE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { uint32 hleFuncId = opcode&0xFFFF; ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_HLE, ppcImlGenContext->ppcAddressOfCurrentInstruction, hleFuncId, 0, IMLREG_INVALID); return true; } uint32 PPCRecompiler_iterateCurrentInstruction(ppcImlGenContext_t* ppcImlGenContext) { uint32 v = CPU_swapEndianU32(*(ppcImlGenContext->currentInstruction)); ppcImlGenContext->currentInstruction += 1; return v; } uint32 PPCRecompiler_getCurrentInstruction(ppcImlGenContext_t* ppcImlGenContext) { uint32 v = CPU_swapEndianU32(*(ppcImlGenContext->currentInstruction)); return v; } uint32 PPCRecompiler_getPreviousInstruction(ppcImlGenContext_t* ppcImlGenContext) { uint32 v = CPU_swapEndianU32(*(ppcImlGenContext->currentInstruction-1)); return v; } void PPCRecompilerIml_setSegmentPoint(IMLSegmentPoint* segmentPoint, IMLSegment* imlSegment, sint32 index) { segmentPoint->imlSegment = imlSegment; segmentPoint->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: // ORI PPCRecompilerImlGen_ORI_ORIS(ppcImlGenContext, opcode, false); break; case 25: // ORIS PPCRecompilerImlGen_ORI_ORIS(ppcImlGenContext, opcode, true); break; case 26: // XORI PPCRecompilerImlGen_XORI_XORIS(ppcImlGenContext, opcode, false); break; case 27: // XORIS PPCRecompilerImlGen_XORI_XORIS(ppcImlGenContext, opcode, true); break; case 28: // ANDI PPCRecompilerImlGen_ANDI_ANDIS(ppcImlGenContext, opcode, false); break; case 29: // ANDIS PPCRecompilerImlGen_ANDI_ANDIS(ppcImlGenContext, opcode, true); break; case 31: // opcode category switch (PPC_getBits(opcode, 30, 10)) { case 0: PPCRecompilerImlGen_CMP(ppcImlGenContext, opcode, false); break; case 4: PPCRecompilerImlGen_TW(ppcImlGenContext, opcode); break; case 8: if (PPCRecompilerImlGen_SUBFC(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 10: if (PPCRecompilerImlGen_ADDC(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 11: if (PPCRecompilerImlGen_MULHWU(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 19: if (PPCRecompilerImlGen_MFCR(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 20: if (PPCRecompilerImlGen_LWARX(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 23: // LWZX 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: // AND if (!PPCRecompilerImlGen_AND_NAND(ppcImlGenContext, opcode, false)) unsupportedInstructionFound = true; break; case 32: PPCRecompilerImlGen_CMP(ppcImlGenContext, opcode, true); // CMPL break; case 40: if (PPCRecompilerImlGen_SUBF(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 54: // DBCST - Generates no code break; case 55: // LWZUX if (!PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 32, false, true, true)) unsupportedInstructionFound = true; break; case 60: // ANDC if (!PPCRecompilerImlGen_ANDC(ppcImlGenContext, opcode)) unsupportedInstructionFound = true; break; case 75: if (PPCRecompilerImlGen_MULHW(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 86: // DCBF -> No-Op break; case 87: // LBZX 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: // NOR if (!PPCRecompilerImlGen_OR_NOR(ppcImlGenContext, opcode, true)) unsupportedInstructionFound = true; break; case 136: if (PPCRecompilerImlGen_SUBFE(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 138: if (PPCRecompilerImlGen_ADDE(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 144: if( !PPCRecompilerImlGen_MTCRF(ppcImlGenContext, opcode)) unsupportedInstructionFound = true; break; case 150: if (!PPCRecompilerImlGen_STWCX(ppcImlGenContext, opcode)) unsupportedInstructionFound = true; break; case 151: // STWX if (!PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 32, true, false)) unsupportedInstructionFound = true; break; case 183: // STWUX if (!PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 32, true, true)) unsupportedInstructionFound = true; break; case 200: if (PPCRecompilerImlGen_SUBFZE(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 202: if (PPCRecompilerImlGen_ADDZE(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 215: // STBX if (!PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 8, true, false)) unsupportedInstructionFound = true; break; case 234: if (PPCRecompilerImlGen_ADDME(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 235: if (PPCRecompilerImlGen_MULLW(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 247: // STBUX if (!PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 8, true, true)) unsupportedInstructionFound = true; break; case 266: if (PPCRecompilerImlGen_ADD(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 279: // LHZX if (!PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 16, false, true, false)) unsupportedInstructionFound = true; break; case 284: // EQV (alias to NXOR) if (!PPCRecompilerImlGen_XOR(ppcImlGenContext, opcode, true)) unsupportedInstructionFound = true; break; case 311: // LHZUX if (!PPCRecompilerImlGen_LOAD_INDEXED(ppcImlGenContext, opcode, 16, false, true, true)) unsupportedInstructionFound = true; break; case 316: // XOR if (!PPCRecompilerImlGen_XOR(ppcImlGenContext, opcode, false)) unsupportedInstructionFound = true; break; case 339: if (PPCRecompilerImlGen_MFSPR(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 343: // 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: // OR if (!PPCRecompilerImlGen_OR_NOR(ppcImlGenContext, opcode, false)) unsupportedInstructionFound = true; break; case 459: PPCRecompilerImlGen_DIVWU(ppcImlGenContext, opcode); break; case 467: if (PPCRecompilerImlGen_MTSPR(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 476: // NAND if (!PPCRecompilerImlGen_AND_NAND(ppcImlGenContext, opcode, true)) unsupportedInstructionFound = true; break; case 491: if (PPCRecompilerImlGen_DIVW(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 534: // LWBRX 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& basicBlockList, PPCFunctionBoundaryTracker& boundaryTracker, uint32 ppcStart, uint32 ppcEnd, const std::set& combinedBranchTargets, const std::set& entryAddresses) { cemu_assert_debug(ppcStart <= ppcEnd); uint32 currentAddr = ppcStart; PPCBasicBlockInfo* curBlockInfo = &basicBlockList.emplace_back(currentAddr, entryAddresses); uint32 basicBlockStart = currentAddr; while (currentAddr <= ppcEnd) { curBlockInfo->lastAddress = currentAddr; uint32 opcode = memory_readU32(currentAddr); bool nextInstIsEnterable = false; bool hasBranchTarget = false; bool hasContinuedFlow = false; uint32 branchTarget = 0; if (PPCRecompiler_CheckIfInstructionEndsSegment(boundaryTracker, currentAddr, opcode, nextInstIsEnterable, hasContinuedFlow, hasBranchTarget, branchTarget)) { curBlockInfo->hasBranchTarget = hasBranchTarget; curBlockInfo->branchTarget = branchTarget; curBlockInfo->hasContinuedFlow = hasContinuedFlow; // start new basic block, except if this is the last instruction if (currentAddr >= ppcEnd) break; curBlockInfo = &basicBlockList.emplace_back(currentAddr + 4, entryAddresses); curBlockInfo->isEnterable = curBlockInfo->isEnterable || nextInstIsEnterable; currentAddr += 4; continue; } currentAddr += 4; if (currentAddr <= ppcEnd) { if (combinedBranchTargets.find(currentAddr) != combinedBranchTargets.end()) { // instruction is branch target, start new basic block curBlockInfo = &basicBlockList.emplace_back(currentAddr, entryAddresses); } } } } std::vector PPCRecompiler_DetermineBasicBlockRange(PPCFunctionBoundaryTracker& boundaryTracker, const std::set& entryAddresses) { cemu_assert(!entryAddresses.empty()); std::vector basicBlockList; const std::set branchTargets = boundaryTracker.GetBranchTargets(); auto funcRanges = boundaryTracker.GetRanges(); std::set combinedBranchTargets = branchTargets; combinedBranchTargets.insert(entryAddresses.begin(), entryAddresses.end()); for (auto& funcRangeIt : funcRanges) PPCRecompiler_DetermineBasicBlockRange(basicBlockList, boundaryTracker, funcRangeIt.startAddress, funcRangeIt.startAddress + funcRangeIt.length - 4, combinedBranchTargets, entryAddresses); // mark all segments that start at entryAddresses as enterable (debug code for verification, can be removed) size_t numMarkedEnterable = 0; for (auto& basicBlockIt : basicBlockList) { if (entryAddresses.find(basicBlockIt.startAddress) != entryAddresses.end()) { cemu_assert_debug(basicBlockIt.isEnterable); numMarkedEnterable++; } } cemu_assert_debug(numMarkedEnterable == entryAddresses.size()); // todo - inline BL, currently this is done in the instruction handler of BL but this will mean that instruction cycle increasing is ignored return basicBlockList; } bool PPCIMLGen_FillBasicBlock(ppcImlGenContext_t& ppcImlGenContext, PPCBasicBlockInfo& basicBlockInfo) { ppcImlGenContext.currentOutputSegment = basicBlockInfo.GetSegmentForInstructionAppend(); ppcImlGenContext.currentInstruction = (uint32*)(memory_base + basicBlockInfo.startAddress); uint32* firstCurrentInstruction = ppcImlGenContext.currentInstruction; uint32* endCurrentInstruction = (uint32*)(memory_base + basicBlockInfo.lastAddress); while (ppcImlGenContext.currentInstruction <= endCurrentInstruction) { uint32 addressOfCurrentInstruction = (uint32)((uint8*)ppcImlGenContext.currentInstruction - memory_base); ppcImlGenContext.ppcAddressOfCurrentInstruction = addressOfCurrentInstruction; if (PPCRecompiler_decodePPCInstruction(&ppcImlGenContext)) { debug_printf("Recompiler encountered unsupported instruction at 0x%08x\n", addressOfCurrentInstruction); ppcImlGenContext.currentOutputSegment = nullptr; return false; } } ppcImlGenContext.currentOutputSegment = nullptr; return true; } // returns split segment from which the continued segment is available via seg->GetBranchNotTaken() IMLSegment* PPCIMLGen_CreateSplitSegmentAtEnd(ppcImlGenContext_t& ppcImlGenContext, PPCBasicBlockInfo& basicBlockInfo) { IMLSegment* writeSegment = basicBlockInfo.GetSegmentForInstructionAppend(); IMLSegment* continuedSegment = ppcImlGenContext.InsertSegment(ppcImlGenContext.GetSegmentIndex(writeSegment) + 1); continuedSegment->SetLinkBranchTaken(writeSegment->GetBranchTaken()); continuedSegment->SetLinkBranchNotTaken(writeSegment->GetBranchNotTaken()); writeSegment->SetLinkBranchNotTaken(continuedSegment); writeSegment->SetLinkBranchTaken(nullptr); if (ppcImlGenContext.currentOutputSegment == writeSegment) ppcImlGenContext.currentOutputSegment = continuedSegment; cemu_assert_debug(basicBlockInfo.appendSegment == writeSegment); basicBlockInfo.appendSegment = continuedSegment; return writeSegment; } // generates a new segment and sets it as branch target for the current write segment. Returns the created segment IMLSegment* PPCIMLGen_CreateNewSegmentAsBranchTarget(ppcImlGenContext_t& ppcImlGenContext, PPCBasicBlockInfo& basicBlockInfo) { IMLSegment* writeSegment = basicBlockInfo.GetSegmentForInstructionAppend(); IMLSegment* branchTargetSegment = ppcImlGenContext.NewSegment(); cemu_assert_debug(!writeSegment->GetBranchTaken()); // must not have a target already writeSegment->SetLinkBranchTaken(branchTargetSegment); return branchTargetSegment; } // verify that current instruction is the last instruction of the active basic block void PPCIMLGen_AssertIfNotLastSegmentInstruction(ppcImlGenContext_t& ppcImlGenContext) { cemu_assert_debug(ppcImlGenContext.currentBasicBlock->lastAddress == ppcImlGenContext.ppcAddressOfCurrentInstruction); } void PPCRecompiler_HandleCycleCheckCount(ppcImlGenContext_t& ppcImlGenContext, PPCBasicBlockInfo& basicBlockInfo) { IMLSegment* imlSegment = basicBlockInfo.GetFirstSegmentInChain(); if (!basicBlockInfo.hasBranchTarget) return; if (basicBlockInfo.branchTarget > basicBlockInfo.startAddress) return; // exclude non-infinite tight loops if (IMLAnalyzer_IsTightFiniteLoop(imlSegment)) return; // make the segment enterable so execution can return after passing a check basicBlockInfo.GetFirstSegmentInChain()->SetEnterable(basicBlockInfo.startAddress); IMLSegment* splitSeg = PPCIMLGen_CreateSplitSegmentAtEnd(ppcImlGenContext, basicBlockInfo); splitSeg->AppendInstruction()->make_cjump_cycle_check(); IMLSegment* exitSegment = ppcImlGenContext.NewSegment(); splitSeg->SetLinkBranchTaken(exitSegment); exitSegment->AppendInstruction()->make_macro(PPCREC_IML_MACRO_LEAVE, basicBlockInfo.startAddress, 0, 0, IMLREG_INVALID); } void PPCRecompiler_SetSegmentsUncertainFlow(ppcImlGenContext_t& ppcImlGenContext) { for (IMLSegment* segIt : ppcImlGenContext.segmentList2) { bool isLastSegment = segIt == ppcImlGenContext.segmentList2.back(); // handle empty segment if (segIt->imlList.empty()) { cemu_assert_debug(segIt->GetBranchNotTaken()); continue; } // check last instruction of segment IMLInstruction* imlInstruction = segIt->GetLastInstruction(); if (imlInstruction->type == PPCREC_IML_TYPE_MACRO) { auto macroType = imlInstruction->operation; switch (macroType) { case PPCREC_IML_MACRO_B_TO_REG: case PPCREC_IML_MACRO_BL: case PPCREC_IML_MACRO_B_FAR: case PPCREC_IML_MACRO_HLE: case PPCREC_IML_MACRO_LEAVE: segIt->nextSegmentIsUncertain = true; break; case PPCREC_IML_MACRO_DEBUGBREAK: case PPCREC_IML_MACRO_COUNT_CYCLES: case PPCREC_IML_MACRO_MFTB: break; default: cemu_assert_unimplemented(); } } } } bool PPCRecompiler_GenerateIML(ppcImlGenContext_t& ppcImlGenContext, PPCFunctionBoundaryTracker& boundaryTracker, std::set& entryAddresses) { std::vector basicBlockList = PPCRecompiler_DetermineBasicBlockRange(boundaryTracker, entryAddresses); // create segments std::unordered_map addrToBB; ppcImlGenContext.segmentList2.resize(basicBlockList.size()); for (size_t i = 0; i < basicBlockList.size(); i++) { PPCBasicBlockInfo& basicBlockInfo = basicBlockList[i]; IMLSegment* seg = new IMLSegment(); seg->ppcAddress = basicBlockInfo.startAddress; if(basicBlockInfo.isEnterable) seg->SetEnterable(basicBlockInfo.startAddress); ppcImlGenContext.segmentList2[i] = seg; cemu_assert_debug(addrToBB.find(basicBlockInfo.startAddress) == addrToBB.end()); basicBlockInfo.SetInitialSegment(seg); addrToBB.emplace(basicBlockInfo.startAddress, &basicBlockInfo); } // link segments for (size_t i = 0; i < basicBlockList.size(); i++) { PPCBasicBlockInfo& bbInfo = basicBlockList[i]; cemu_assert_debug(bbInfo.GetFirstSegmentInChain() == bbInfo.GetSegmentForInstructionAppend()); IMLSegment* seg = ppcImlGenContext.segmentList2[i]; if (bbInfo.hasBranchTarget) { PPCBasicBlockInfo* targetBB = addrToBB[bbInfo.branchTarget]; cemu_assert_debug(targetBB); IMLSegment_SetLinkBranchTaken(seg, targetBB->GetFirstSegmentInChain()); } if (bbInfo.hasContinuedFlow) { PPCBasicBlockInfo* targetBB = addrToBB[bbInfo.lastAddress + 4]; if (!targetBB) { cemuLog_log(LogType::Recompiler, "Recompiler was unable to link segment [0x{:08x}-0x{:08x}] to 0x{:08x}", bbInfo.startAddress, bbInfo.lastAddress, bbInfo.lastAddress + 4); return false; } cemu_assert_debug(targetBB); IMLSegment_SetLinkBranchNotTaken(seg, targetBB->GetFirstSegmentInChain()); } } // we assume that all unreachable segments are potentially enterable // todo - mark them as such // generate cycle counters // in theory we could generate these as part of FillBasicBlock() but in the future we might use more complex logic to emit fewer operations for (size_t i = 0; i < basicBlockList.size(); i++) { PPCBasicBlockInfo& basicBlockInfo = basicBlockList[i]; IMLSegment* seg = basicBlockInfo.GetSegmentForInstructionAppend(); uint32 ppcInstructionCount = (basicBlockInfo.lastAddress - basicBlockInfo.startAddress + 4) / 4; cemu_assert_debug(ppcInstructionCount > 0); PPCRecompiler_pushBackIMLInstructions(seg, 0, 1); seg->imlList[0].type = PPCREC_IML_TYPE_MACRO; seg->imlList[0].operation = PPCREC_IML_MACRO_COUNT_CYCLES; seg->imlList[0].op_macro.param = ppcInstructionCount; } // generate cycle check instructions // note: Introduces new segments for (size_t i = 0; i < basicBlockList.size(); i++) { PPCBasicBlockInfo& basicBlockInfo = basicBlockList[i]; PPCRecompiler_HandleCycleCheckCount(ppcImlGenContext, basicBlockInfo); } // fill in all the basic blocks // note: This step introduces new segments as is necessary for some instructions for (size_t i = 0; i < basicBlockList.size(); i++) { PPCBasicBlockInfo& basicBlockInfo = basicBlockList[i]; ppcImlGenContext.currentBasicBlock = &basicBlockInfo; if (!PPCIMLGen_FillBasicBlock(ppcImlGenContext, basicBlockInfo)) return false; ppcImlGenContext.currentBasicBlock = nullptr; } // mark segments with unknown jump destination (e.g. BLR and most macros) PPCRecompiler_SetSegmentsUncertainFlow(ppcImlGenContext); // debug - check segment graph #ifdef CEMU_DEBUG_ASSERT //for (size_t i = 0; i < basicBlockList.size(); i++) //{ // IMLSegment* seg = ppcImlGenContext.segmentList2[i]; // if (seg->list_prevSegments.empty()) // { // cemu_assert_debug(seg->isEnterable); // } //} // debug - check if suffix instructions are at the end of segments and if they are present for branching segments for (size_t segIndex = 0; segIndex < ppcImlGenContext.segmentList2.size(); segIndex++) { IMLSegment* seg = ppcImlGenContext.segmentList2[segIndex]; IMLSegment* nextSeg = (segIndex+1) < ppcImlGenContext.segmentList2.size() ? ppcImlGenContext.segmentList2[segIndex + 1] : nullptr; if (seg->imlList.size() > 0) { for (size_t f = 0; f < seg->imlList.size() - 1; f++) { if (seg->imlList[f].IsSuffixInstruction()) { debug_printf("---------------- SegmentDump (Suffix instruction at wrong pos in segment 0x%x):\n", (int)segIndex); IMLDebug_Dump(&ppcImlGenContext); DEBUG_BREAK; } } } if (seg->nextSegmentBranchTaken) { if (!seg->HasSuffixInstruction()) { debug_printf("---------------- SegmentDump (NoSuffixInstruction in segment 0x%x):\n", (int)segIndex); IMLDebug_Dump(&ppcImlGenContext); DEBUG_BREAK; } } if (seg->nextSegmentBranchNotTaken) { // if branch not taken, flow must continue to next segment in sequence cemu_assert_debug(seg->nextSegmentBranchNotTaken == nextSeg); } // more detailed checks based on actual suffix instruction if (seg->imlList.size() > 0) { IMLInstruction* inst = seg->GetLastInstruction(); if (inst->type == PPCREC_IML_TYPE_MACRO && inst->op_macro.param == PPCREC_IML_MACRO_B_FAR) { cemu_assert_debug(!seg->GetBranchTaken()); cemu_assert_debug(!seg->GetBranchNotTaken()); } if (inst->type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK) { cemu_assert_debug(seg->GetBranchTaken()); cemu_assert_debug(seg->GetBranchNotTaken()); } if (inst->type == PPCREC_IML_TYPE_CONDITIONAL_JUMP) { if (!seg->GetBranchTaken() || !seg->GetBranchNotTaken()) { debug_printf("---------------- SegmentDump (Missing branch for conditional jump in segment 0x%x):\n", (int)segIndex); IMLDebug_Dump(&ppcImlGenContext); cemu_assert_error(); } } } segIndex++; } #endif // todos: // - basic block determination should look for the B(L) B(L) pattern. Or maybe just mark every bb without any input segments as an entry segment return true; } void IMLOptimizer_replaceWithConditionalMov(ppcImlGenContext_t& ppcImlGenContext) { // optimization pass - replace segments with conditional MOVs if possible //for (IMLSegment* segIt : ppcImlGenContext.segmentList2) //{ // if (segIt->nextSegmentBranchNotTaken == nullptr || segIt->nextSegmentBranchTaken == nullptr) // continue; // not a branching segment // IMLInstruction* lastInstruction = segIt->GetLastInstruction(); // if (lastInstruction->type != PPCREC_IML_TYPE_CJUMP || lastInstruction->op_conditionalJump.crRegisterIndex != 0) // continue; // IMLSegment* conditionalSegment = segIt->nextSegmentBranchNotTaken; // IMLSegment* finalSegment = segIt->nextSegmentBranchTaken; // if (segIt->nextSegmentBranchTaken != segIt->nextSegmentBranchNotTaken->nextSegmentBranchNotTaken) // continue; // if (segIt->nextSegmentBranchNotTaken->imlList.size() > 4) // continue; // if (conditionalSegment->list_prevSegments.size() != 1) // continue; // the reduced segment must not be the target of any other branch // if (conditionalSegment->isEnterable) // continue; // // check if the segment contains only iml instructions that can be turned into conditional moves (Value assignment, register assignment) // bool canReduceSegment = true; // for (sint32 f = 0; f < conditionalSegment->imlList.size(); f++) // { // IMLInstruction* imlInstruction = conditionalSegment->imlList.data() + f; // if (imlInstruction->type == PPCREC_IML_TYPE_R_S32 && imlInstruction->operation == PPCREC_IML_OP_ASSIGN) // continue; // // todo: Register to register copy // canReduceSegment = false; // break; // } // if (canReduceSegment == false) // continue; // // remove the branch instruction // uint8 branchCond_crRegisterIndex = lastInstruction->op_conditionalJump.crRegisterIndex; // uint8 branchCond_crBitIndex = lastInstruction->op_conditionalJump.crBitIndex; // bool branchCond_bitMustBeSet = lastInstruction->op_conditionalJump.bitMustBeSet; // lastInstruction->make_no_op(); // // append conditional moves based on branch condition // for (sint32 f = 0; f < conditionalSegment->imlList.size(); f++) // { // IMLInstruction* imlInstruction = conditionalSegment->imlList.data() + f; // if (imlInstruction->type == PPCREC_IML_TYPE_R_S32 && imlInstruction->operation == PPCREC_IML_OP_ASSIGN) // PPCRecompilerImlGen_generateNewInstruction_conditional_r_s32(&ppcImlGenContext, PPCRecompiler_appendInstruction(segIt), PPCREC_IML_OP_ASSIGN, imlInstruction->op_r_immS32.registerIndex, imlInstruction->op_r_immS32.immS32, branchCond_crRegisterIndex, branchCond_crBitIndex, !branchCond_bitMustBeSet); // else // assert_dbg(); // } // // update segment links // // source segment: imlSegment, conditional/removed segment: conditionalSegment, final segment: finalSegment // IMLSegment_RemoveLink(segIt, conditionalSegment); // IMLSegment_RemoveLink(segIt, finalSegment); // IMLSegment_RemoveLink(conditionalSegment, finalSegment); // IMLSegment_SetLinkBranchNotTaken(segIt, finalSegment); // // remove all instructions from conditional segment // conditionalSegment->imlList.clear(); // // if possible, merge imlSegment with finalSegment // if (finalSegment->isEnterable == false && finalSegment->list_prevSegments.size() == 1) // { // // todo: Clean this up and move into separate function PPCRecompilerIML_mergeSegments() // IMLSegment_RemoveLink(segIt, finalSegment); // if (finalSegment->nextSegmentBranchNotTaken) // { // IMLSegment* tempSegment = finalSegment->nextSegmentBranchNotTaken; // IMLSegment_RemoveLink(finalSegment, tempSegment); // IMLSegment_SetLinkBranchNotTaken(segIt, tempSegment); // } // if (finalSegment->nextSegmentBranchTaken) // { // IMLSegment* tempSegment = finalSegment->nextSegmentBranchTaken; // IMLSegment_RemoveLink(finalSegment, tempSegment); // IMLSegment_SetLinkBranchTaken(segIt, tempSegment); // } // // copy IML instructions // cemu_assert_debug(segIt != finalSegment); // for (sint32 f = 0; f < finalSegment->imlList.size(); f++) // { // memcpy(PPCRecompiler_appendInstruction(segIt), finalSegment->imlList.data() + f, sizeof(IMLInstruction)); // } // finalSegment->imlList.clear(); // } // // todo: If possible, merge with the segment following conditionalSegment (merging is only possible if the segment is not an entry point or has no other jump sources) //} } bool PPCRecompiler_generateIntermediateCode(ppcImlGenContext_t& ppcImlGenContext, PPCRecFunction_t* ppcRecFunc, std::set& entryAddresses, PPCFunctionBoundaryTracker& boundaryTracker) { ppcImlGenContext.boundaryTracker = &boundaryTracker; if (!PPCRecompiler_GenerateIML(ppcImlGenContext, boundaryTracker, entryAddresses)) return false; // IMLOptimizer_replaceWithConditionalMov(ppcImlGenContext); // set range // todo - support non-continuous functions for the range tracking? ppcRecRange_t recRange; recRange.ppcAddress = ppcRecFunc->ppcAddress; recRange.ppcSize = ppcRecFunc->ppcSize; ppcRecFunc->list_ranges.push_back(recRange); return true; }