mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-07-05 22:41:18 +12:00
PPCRec: Dead code elimination + reintroduce pre-rework optimizations
This commit is contained in:
parent
c419bfc451
commit
f55b842773
11 changed files with 930 additions and 316 deletions
|
@ -82,6 +82,36 @@ X86Cond _x86Cond(IMLCondition imlCond)
|
||||||
return X86_CONDITION_Z;
|
return X86_CONDITION_Z;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
X86Cond _x86CondInverted(IMLCondition imlCond)
|
||||||
|
{
|
||||||
|
switch (imlCond)
|
||||||
|
{
|
||||||
|
case IMLCondition::EQ:
|
||||||
|
return X86_CONDITION_NZ;
|
||||||
|
case IMLCondition::NEQ:
|
||||||
|
return X86_CONDITION_Z;
|
||||||
|
case IMLCondition::UNSIGNED_GT:
|
||||||
|
return X86_CONDITION_BE;
|
||||||
|
case IMLCondition::UNSIGNED_LT:
|
||||||
|
return X86_CONDITION_NB;
|
||||||
|
case IMLCondition::SIGNED_GT:
|
||||||
|
return X86_CONDITION_LE;
|
||||||
|
case IMLCondition::SIGNED_LT:
|
||||||
|
return X86_CONDITION_NL;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cemu_assert_suspicious();
|
||||||
|
return X86_CONDITION_Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
X86Cond _x86Cond(IMLCondition imlCond, bool condIsInverted)
|
||||||
|
{
|
||||||
|
if (condIsInverted)
|
||||||
|
return _x86CondInverted(imlCond);
|
||||||
|
return _x86Cond(imlCond);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Remember current instruction output offset for reloc
|
* Remember current instruction output offset for reloc
|
||||||
* The instruction generated after this method has been called will be adjusted
|
* The instruction generated after this method has been called will be adjusted
|
||||||
|
@ -638,6 +668,10 @@ bool PPCRecompilerX64Gen_imlInstruction_r_r(PPCRecFunction_t* PPCRecFunction, pp
|
||||||
PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset2, x64GenContext->emitter->GetWriteIndex());
|
PPCRecompilerX64Gen_redirectRelativeJump(x64GenContext, jumpInstructionOffset2, x64GenContext->emitter->GetWriteIndex());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if( imlInstruction->operation == PPCREC_IML_OP_X86_CMP)
|
||||||
|
{
|
||||||
|
x64GenContext->emitter->CMP_dd(regR, regA);
|
||||||
|
}
|
||||||
else if( imlInstruction->operation == PPCREC_IML_OP_DCBZ )
|
else if( imlInstruction->operation == PPCREC_IML_OP_DCBZ )
|
||||||
{
|
{
|
||||||
if( regR != regA )
|
if( regR != regA )
|
||||||
|
@ -680,6 +714,11 @@ bool PPCRecompilerX64Gen_imlInstruction_r_s32(PPCRecFunction_t* PPCRecFunction,
|
||||||
cemu_assert_debug((imlInstruction->op_r_immS32.immS32 & 0x80) == 0);
|
cemu_assert_debug((imlInstruction->op_r_immS32.immS32 & 0x80) == 0);
|
||||||
x64Gen_rol_reg64Low32_imm8(x64GenContext, regR, (uint8)imlInstruction->op_r_immS32.immS32);
|
x64Gen_rol_reg64Low32_imm8(x64GenContext, regR, (uint8)imlInstruction->op_r_immS32.immS32);
|
||||||
}
|
}
|
||||||
|
else if( imlInstruction->operation == PPCREC_IML_OP_X86_CMP)
|
||||||
|
{
|
||||||
|
sint32 imm = imlInstruction->op_r_immS32.immS32;
|
||||||
|
x64GenContext->emitter->CMP_di32(regR, imm);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
debug_printf("PPCRecompilerX64Gen_imlInstruction_r_s32(): Unsupported operation 0x%x\n", imlInstruction->operation);
|
debug_printf("PPCRecompilerX64Gen_imlInstruction_r_s32(): Unsupported operation 0x%x\n", imlInstruction->operation);
|
||||||
|
@ -1082,6 +1121,13 @@ bool PPCRecompilerX64Gen_imlInstruction_cjump2(PPCRecFunction_t* PPCRecFunction,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PPCRecompilerX64Gen_imlInstruction_x86_eflags_jcc(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction, IMLSegment* imlSegment)
|
||||||
|
{
|
||||||
|
X86Cond cond = _x86Cond(imlInstruction->op_x86_eflags_jcc.cond, imlInstruction->op_x86_eflags_jcc.invertedCondition);
|
||||||
|
PPCRecompilerX64Gen_rememberRelocatableOffset(x64GenContext, imlSegment->nextSegmentBranchTaken);
|
||||||
|
x64GenContext->emitter->Jcc_j32(cond, 0);
|
||||||
|
}
|
||||||
|
|
||||||
bool PPCRecompilerX64Gen_imlInstruction_jump2(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction, IMLSegment* imlSegment)
|
bool PPCRecompilerX64Gen_imlInstruction_jump2(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext, x64GenContext_t* x64GenContext, IMLInstruction* imlInstruction, IMLSegment* imlSegment)
|
||||||
{
|
{
|
||||||
PPCRecompilerX64Gen_rememberRelocatableOffset(x64GenContext, imlSegment->nextSegmentBranchTaken);
|
PPCRecompilerX64Gen_rememberRelocatableOffset(x64GenContext, imlSegment->nextSegmentBranchTaken);
|
||||||
|
@ -1504,6 +1550,10 @@ bool PPCRecompiler_generateX64Code(PPCRecFunction_t* PPCRecFunction, ppcImlGenCo
|
||||||
if (PPCRecompilerX64Gen_imlInstruction_cjump2(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction, segIt) == false)
|
if (PPCRecompilerX64Gen_imlInstruction_cjump2(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction, segIt) == false)
|
||||||
codeGenerationFailed = true;
|
codeGenerationFailed = true;
|
||||||
}
|
}
|
||||||
|
else if(imlInstruction->type == PPCREC_IML_TYPE_X86_EFLAGS_JCC)
|
||||||
|
{
|
||||||
|
PPCRecompilerX64Gen_imlInstruction_x86_eflags_jcc(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction, segIt);
|
||||||
|
}
|
||||||
else if (imlInstruction->type == PPCREC_IML_TYPE_JUMP)
|
else if (imlInstruction->type == PPCREC_IML_TYPE_JUMP)
|
||||||
{
|
{
|
||||||
if (PPCRecompilerX64Gen_imlInstruction_jump2(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction, segIt) == false)
|
if (PPCRecompilerX64Gen_imlInstruction_jump2(PPCRecFunction, ppcImlGenContext, &x64GenContext, imlInstruction, segIt) == false)
|
||||||
|
|
|
@ -11,6 +11,9 @@ void IMLOptimizer_OptimizeDirectFloatCopies(struct ppcImlGenContext_t* ppcImlGen
|
||||||
void IMLOptimizer_OptimizeDirectIntegerCopies(struct ppcImlGenContext_t* ppcImlGenContext);
|
void IMLOptimizer_OptimizeDirectIntegerCopies(struct ppcImlGenContext_t* ppcImlGenContext);
|
||||||
void PPCRecompiler_optimizePSQLoadAndStore(struct ppcImlGenContext_t* ppcImlGenContext);
|
void PPCRecompiler_optimizePSQLoadAndStore(struct ppcImlGenContext_t* ppcImlGenContext);
|
||||||
|
|
||||||
|
void IMLOptimizer_StandardOptimizationPass(ppcImlGenContext_t& ppcImlGenContext);
|
||||||
|
|
||||||
// debug
|
// debug
|
||||||
|
void IMLDebug_DisassembleInstruction(const IMLInstruction& inst, std::string& disassemblyLineOut);
|
||||||
void IMLDebug_DumpSegment(struct ppcImlGenContext_t* ctx, IMLSegment* imlSegment, bool printLivenessRangeInfo = false);
|
void IMLDebug_DumpSegment(struct ppcImlGenContext_t* ctx, IMLSegment* imlSegment, bool printLivenessRangeInfo = false);
|
||||||
void IMLDebug_Dump(struct ppcImlGenContext_t* ppcImlGenContext, bool printLivenessRangeInfo = false);
|
void IMLDebug_Dump(struct ppcImlGenContext_t* ppcImlGenContext, bool printLivenessRangeInfo = false);
|
||||||
|
|
|
@ -75,12 +75,14 @@ void IMLDebug_AppendRegisterParam(StringBuf& strOutput, IMLReg virtualRegister,
|
||||||
|
|
||||||
void IMLDebug_AppendS32Param(StringBuf& strOutput, sint32 val, bool isLast = false)
|
void IMLDebug_AppendS32Param(StringBuf& strOutput, sint32 val, bool isLast = false)
|
||||||
{
|
{
|
||||||
if (isLast)
|
if (val < 0)
|
||||||
{
|
{
|
||||||
strOutput.addFmt("0x{:08x}", val);
|
strOutput.add("-");
|
||||||
return;
|
val = -val;
|
||||||
}
|
}
|
||||||
strOutput.addFmt("0x{:08x}, ", val);
|
strOutput.addFmt("0x{:08x}", val);
|
||||||
|
if (!isLast)
|
||||||
|
strOutput.add(", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
void IMLDebug_PrintLivenessRangeInfo(StringBuf& currentLineText, IMLSegment* imlSegment, sint32 offset)
|
void IMLDebug_PrintLivenessRangeInfo(StringBuf& currentLineText, IMLSegment* imlSegment, sint32 offset)
|
||||||
|
@ -163,45 +165,12 @@ std::string IMLDebug_GetConditionName(IMLCondition cond)
|
||||||
return "ukn";
|
return "ukn";
|
||||||
}
|
}
|
||||||
|
|
||||||
void IMLDebug_DumpSegment(ppcImlGenContext_t* ctx, IMLSegment* imlSegment, bool printLivenessRangeInfo)
|
void IMLDebug_DisassembleInstruction(const IMLInstruction& inst, std::string& disassemblyLineOut)
|
||||||
{
|
{
|
||||||
|
const sint32 lineOffsetParameters = 10;//18;
|
||||||
|
|
||||||
StringBuf strOutput(1024);
|
StringBuf strOutput(1024);
|
||||||
|
|
||||||
strOutput.addFmt("SEGMENT {} | PPC=0x{:08x} Loop-depth {}", IMLDebug_GetSegmentName(ctx, imlSegment), imlSegment->ppcAddress, imlSegment->loopDepth);
|
|
||||||
if (imlSegment->isEnterable)
|
|
||||||
{
|
|
||||||
strOutput.addFmt(" ENTERABLE (0x{:08x})", imlSegment->enterPPCAddress);
|
|
||||||
}
|
|
||||||
//else if (imlSegment->isJumpDestination)
|
|
||||||
//{
|
|
||||||
// strOutput.addFmt(" JUMP-DEST (0x{:08x})", imlSegment->jumpDestinationPPCAddress);
|
|
||||||
//}
|
|
||||||
|
|
||||||
debug_printf("%s\n", strOutput.c_str());
|
|
||||||
|
|
||||||
//strOutput.reset();
|
|
||||||
//strOutput.addFmt("SEGMENT NAME 0x{:016x}", (uintptr_t)imlSegment);
|
|
||||||
//debug_printf("%s", strOutput.c_str());
|
|
||||||
|
|
||||||
if (printLivenessRangeInfo)
|
|
||||||
{
|
|
||||||
strOutput.reset();
|
strOutput.reset();
|
||||||
IMLDebug_PrintLivenessRangeInfo(strOutput, imlSegment, RA_INTER_RANGE_START);
|
|
||||||
debug_printf("%s\n", strOutput.c_str());
|
|
||||||
}
|
|
||||||
//debug_printf("\n");
|
|
||||||
strOutput.reset();
|
|
||||||
|
|
||||||
sint32 lineOffsetParameters = 18;
|
|
||||||
|
|
||||||
for (sint32 i = 0; i < imlSegment->imlList.size(); i++)
|
|
||||||
{
|
|
||||||
const IMLInstruction& inst = imlSegment->imlList[i];
|
|
||||||
// don't log NOP instructions
|
|
||||||
if (inst.type == PPCREC_IML_TYPE_NO_OP)
|
|
||||||
continue;
|
|
||||||
strOutput.reset();
|
|
||||||
strOutput.addFmt("{:02x} ", i);
|
|
||||||
if (inst.type == PPCREC_IML_TYPE_R_NAME || inst.type == PPCREC_IML_TYPE_NAME_R)
|
if (inst.type == PPCREC_IML_TYPE_R_NAME || inst.type == PPCREC_IML_TYPE_NAME_R)
|
||||||
{
|
{
|
||||||
if (inst.type == PPCREC_IML_TYPE_R_NAME)
|
if (inst.type == PPCREC_IML_TYPE_R_NAME)
|
||||||
|
@ -284,7 +253,7 @@ void IMLDebug_DumpSegment(ppcImlGenContext_t* ctx, IMLSegment* imlSegment, bool
|
||||||
strOutput.add(" ");
|
strOutput.add(" ");
|
||||||
IMLDebug_AppendRegisterParam(strOutput, inst.op_compare.regA);
|
IMLDebug_AppendRegisterParam(strOutput, inst.op_compare.regA);
|
||||||
IMLDebug_AppendRegisterParam(strOutput, inst.op_compare.regB);
|
IMLDebug_AppendRegisterParam(strOutput, inst.op_compare.regB);
|
||||||
strOutput.addFmt(", {}", IMLDebug_GetConditionName(inst.op_compare.cond));
|
strOutput.addFmt("{}", IMLDebug_GetConditionName(inst.op_compare.cond));
|
||||||
strOutput.add(" -> ");
|
strOutput.add(" -> ");
|
||||||
IMLDebug_AppendRegisterParam(strOutput, inst.op_compare.regR, true);
|
IMLDebug_AppendRegisterParam(strOutput, inst.op_compare.regR, true);
|
||||||
}
|
}
|
||||||
|
@ -475,11 +444,53 @@ void IMLDebug_DumpSegment(ppcImlGenContext_t* ctx, IMLSegment* imlSegment, bool
|
||||||
strOutput.addFmt(" {}", inst.op_conditional_r_s32.immS32);
|
strOutput.addFmt(" {}", inst.op_conditional_r_s32.immS32);
|
||||||
strOutput.add(" (conditional)");
|
strOutput.add(" (conditional)");
|
||||||
}
|
}
|
||||||
|
else if (inst.type == PPCREC_IML_TYPE_X86_EFLAGS_JCC)
|
||||||
|
{
|
||||||
|
strOutput.addFmt("X86_JCC {}", IMLDebug_GetConditionName(inst.op_x86_eflags_jcc.cond));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
strOutput.addFmt("Unknown iml type {}", inst.type);
|
strOutput.addFmt("Unknown iml type {}", inst.type);
|
||||||
}
|
}
|
||||||
debug_printf("%s", strOutput.c_str());
|
disassemblyLineOut.assign(strOutput.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void IMLDebug_DumpSegment(ppcImlGenContext_t* ctx, IMLSegment* imlSegment, bool printLivenessRangeInfo)
|
||||||
|
{
|
||||||
|
StringBuf strOutput(1024);
|
||||||
|
|
||||||
|
strOutput.addFmt("SEGMENT {} | PPC=0x{:08x} Loop-depth {}", IMLDebug_GetSegmentName(ctx, imlSegment), imlSegment->ppcAddress, imlSegment->loopDepth);
|
||||||
|
if (imlSegment->isEnterable)
|
||||||
|
{
|
||||||
|
strOutput.addFmt(" ENTERABLE (0x{:08x})", imlSegment->enterPPCAddress);
|
||||||
|
}
|
||||||
|
if (imlSegment->deadCodeEliminationHintSeg)
|
||||||
|
{
|
||||||
|
strOutput.addFmt(" InheritOverwrite: {}", IMLDebug_GetSegmentName(ctx, imlSegment->deadCodeEliminationHintSeg));
|
||||||
|
}
|
||||||
|
debug_printf("%s\n", strOutput.c_str());
|
||||||
|
|
||||||
|
if (printLivenessRangeInfo)
|
||||||
|
{
|
||||||
|
strOutput.reset();
|
||||||
|
IMLDebug_PrintLivenessRangeInfo(strOutput, imlSegment, RA_INTER_RANGE_START);
|
||||||
|
debug_printf("%s\n", strOutput.c_str());
|
||||||
|
}
|
||||||
|
//debug_printf("\n");
|
||||||
|
strOutput.reset();
|
||||||
|
|
||||||
|
std::string disassemblyLine;
|
||||||
|
for (sint32 i = 0; i < imlSegment->imlList.size(); i++)
|
||||||
|
{
|
||||||
|
const IMLInstruction& inst = imlSegment->imlList[i];
|
||||||
|
// don't log NOP instructions
|
||||||
|
if (inst.type == PPCREC_IML_TYPE_NO_OP)
|
||||||
|
continue;
|
||||||
|
//strOutput.addFmt("{:02x} ", i);
|
||||||
|
debug_printf(fmt::format("{:02x} ", i).c_str());
|
||||||
|
disassemblyLine.clear();
|
||||||
|
IMLDebug_DisassembleInstruction(inst, disassemblyLine);
|
||||||
|
debug_printf("%s", disassemblyLine.c_str());
|
||||||
if (printLivenessRangeInfo)
|
if (printLivenessRangeInfo)
|
||||||
{
|
{
|
||||||
IMLDebug_PrintLivenessRangeInfo(strOutput, imlSegment, i);
|
IMLDebug_PrintLivenessRangeInfo(strOutput, imlSegment, i);
|
||||||
|
|
|
@ -26,7 +26,8 @@ void IMLInstruction::CheckRegisterUsage(IMLUsedRegisters* registersUsed) const
|
||||||
}
|
}
|
||||||
else if (type == PPCREC_IML_TYPE_R_R)
|
else if (type == PPCREC_IML_TYPE_R_R)
|
||||||
{
|
{
|
||||||
if (operation == PPCREC_IML_OP_DCBZ)
|
if (operation == PPCREC_IML_OP_DCBZ ||
|
||||||
|
operation == PPCREC_IML_OP_X86_CMP)
|
||||||
{
|
{
|
||||||
// both operands are read only
|
// both operands are read only
|
||||||
registersUsed->readGPR1 = op_r_r.regR;
|
registersUsed->readGPR1 = op_r_r.regR;
|
||||||
|
@ -58,13 +59,18 @@ void IMLInstruction::CheckRegisterUsage(IMLUsedRegisters* registersUsed) const
|
||||||
|
|
||||||
if (operation == PPCREC_IML_OP_LEFT_ROTATE)
|
if (operation == PPCREC_IML_OP_LEFT_ROTATE)
|
||||||
{
|
{
|
||||||
// operand register is read and write
|
// register operand is read and write
|
||||||
registersUsed->readGPR1 = op_r_immS32.regR;
|
registersUsed->readGPR1 = op_r_immS32.regR;
|
||||||
registersUsed->writtenGPR1 = op_r_immS32.regR;
|
registersUsed->writtenGPR1 = op_r_immS32.regR;
|
||||||
}
|
}
|
||||||
|
else if (operation == PPCREC_IML_OP_X86_CMP)
|
||||||
|
{
|
||||||
|
// register operand is read only
|
||||||
|
registersUsed->readGPR1 = op_r_immS32.regR;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// operand register is write only
|
// register operand is write only
|
||||||
// todo - use explicit lists, avoid default cases
|
// todo - use explicit lists, avoid default cases
|
||||||
registersUsed->writtenGPR1 = op_r_immS32.regR;
|
registersUsed->writtenGPR1 = op_r_immS32.regR;
|
||||||
}
|
}
|
||||||
|
@ -453,6 +459,10 @@ void IMLInstruction::CheckRegisterUsage(IMLUsedRegisters* registersUsed) const
|
||||||
registersUsed->readFPR1 = op_fpr_compare.regA;
|
registersUsed->readFPR1 = op_fpr_compare.regA;
|
||||||
registersUsed->readFPR2 = op_fpr_compare.regB;
|
registersUsed->readFPR2 = op_fpr_compare.regB;
|
||||||
}
|
}
|
||||||
|
else if (type == PPCREC_IML_TYPE_X86_EFLAGS_JCC)
|
||||||
|
{
|
||||||
|
// no registers read or written (except for the implicit eflags)
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cemu_assert_unimplemented();
|
cemu_assert_unimplemented();
|
||||||
|
@ -675,6 +685,10 @@ void IMLInstruction::RewriteGPR(const std::unordered_map<IMLRegID, IMLRegID>& tr
|
||||||
op_fpr_compare.regB = replaceRegisterIdMultiple(op_fpr_compare.regB, translationTable);
|
op_fpr_compare.regB = replaceRegisterIdMultiple(op_fpr_compare.regB, translationTable);
|
||||||
op_fpr_compare.regR = replaceRegisterIdMultiple(op_fpr_compare.regR, translationTable);
|
op_fpr_compare.regR = replaceRegisterIdMultiple(op_fpr_compare.regR, translationTable);
|
||||||
}
|
}
|
||||||
|
else if (type == PPCREC_IML_TYPE_X86_EFLAGS_JCC)
|
||||||
|
{
|
||||||
|
// no registers read or written (except for the implicit eflags)
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cemu_assert_unimplemented();
|
cemu_assert_unimplemented();
|
||||||
|
|
|
@ -180,6 +180,11 @@ enum
|
||||||
|
|
||||||
// R_R_R_carry
|
// R_R_R_carry
|
||||||
PPCREC_IML_OP_ADD_WITH_CARRY, // similar to ADD but also adds carry bit (0 or 1)
|
PPCREC_IML_OP_ADD_WITH_CARRY, // similar to ADD but also adds carry bit (0 or 1)
|
||||||
|
|
||||||
|
// X86 extension
|
||||||
|
PPCREC_IML_OP_X86_CMP, // R_R and R_S32
|
||||||
|
|
||||||
|
PPCREC_IML_OP_INVALID
|
||||||
};
|
};
|
||||||
|
|
||||||
#define PPCREC_IML_OP_FPR_COPY_PAIR (PPCREC_IML_OP_ASSIGN)
|
#define PPCREC_IML_OP_FPR_COPY_PAIR (PPCREC_IML_OP_ASSIGN)
|
||||||
|
@ -261,6 +266,9 @@ enum
|
||||||
PPCREC_IML_TYPE_FPR_R,
|
PPCREC_IML_TYPE_FPR_R,
|
||||||
|
|
||||||
PPCREC_IML_TYPE_FPR_COMPARE, // r* = r* CMP[cond] r*
|
PPCREC_IML_TYPE_FPR_COMPARE, // r* = r* CMP[cond] r*
|
||||||
|
|
||||||
|
// X86 specific
|
||||||
|
PPCREC_IML_TYPE_X86_EFLAGS_JCC,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum // IMLName
|
enum // IMLName
|
||||||
|
@ -350,13 +358,29 @@ struct IMLUsedRegisters
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool IsWrittenByRegId(IMLRegID regId) const
|
||||||
|
{
|
||||||
|
if (writtenGPR1.IsValid() && writtenGPR1.GetRegID() == regId)
|
||||||
|
return true;
|
||||||
|
if (writtenGPR2.IsValid() && writtenGPR2.GetRegID() == regId)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool IsBaseGPRWritten(IMLReg imlReg) const
|
bool IsBaseGPRWritten(IMLReg imlReg) const
|
||||||
{
|
{
|
||||||
cemu_assert_debug(imlReg.IsValid());
|
cemu_assert_debug(imlReg.IsValid());
|
||||||
auto regId = imlReg.GetRegID();
|
auto regId = imlReg.GetRegID();
|
||||||
if (writtenGPR1.IsValid() && writtenGPR1.GetRegID() == regId)
|
return IsWrittenByRegId(regId);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsRegIdRead(IMLRegID regId) const
|
||||||
|
{
|
||||||
|
if (readGPR1.IsValid() && readGPR1.GetRegID() == regId)
|
||||||
return true;
|
return true;
|
||||||
if (writtenGPR2.IsValid() && writtenGPR2.GetRegID() == regId)
|
if (readGPR2.IsValid() && readGPR2.GetRegID() == regId)
|
||||||
|
return true;
|
||||||
|
if (readGPR3.IsValid() && readGPR3.GetRegID() == regId)
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -556,6 +580,12 @@ struct IMLInstruction
|
||||||
uint8 crBitIndex;
|
uint8 crBitIndex;
|
||||||
bool bitMustBeSet;
|
bool bitMustBeSet;
|
||||||
}op_conditional_r_s32;
|
}op_conditional_r_s32;
|
||||||
|
// X86 specific
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
IMLCondition cond;
|
||||||
|
bool invertedCondition;
|
||||||
|
}op_x86_eflags_jcc;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool IsSuffixInstruction() const
|
bool IsSuffixInstruction() const
|
||||||
|
@ -568,7 +598,8 @@ struct IMLInstruction
|
||||||
type == PPCREC_IML_TYPE_MACRO && operation == PPCREC_IML_MACRO_MFTB ||
|
type == PPCREC_IML_TYPE_MACRO && operation == PPCREC_IML_MACRO_MFTB ||
|
||||||
type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK ||
|
type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK ||
|
||||||
type == PPCREC_IML_TYPE_JUMP ||
|
type == PPCREC_IML_TYPE_JUMP ||
|
||||||
type == PPCREC_IML_TYPE_CONDITIONAL_JUMP)
|
type == PPCREC_IML_TYPE_CONDITIONAL_JUMP ||
|
||||||
|
type == PPCREC_IML_TYPE_X86_EFLAGS_JCC)
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -676,7 +707,7 @@ struct IMLInstruction
|
||||||
void make_compare(IMLReg regA, IMLReg regB, IMLReg regR, IMLCondition cond)
|
void make_compare(IMLReg regA, IMLReg regB, IMLReg regR, IMLCondition cond)
|
||||||
{
|
{
|
||||||
this->type = PPCREC_IML_TYPE_COMPARE;
|
this->type = PPCREC_IML_TYPE_COMPARE;
|
||||||
this->operation = -999;
|
this->operation = PPCREC_IML_OP_INVALID;
|
||||||
this->op_compare.regR = regR;
|
this->op_compare.regR = regR;
|
||||||
this->op_compare.regA = regA;
|
this->op_compare.regA = regA;
|
||||||
this->op_compare.regB = regB;
|
this->op_compare.regB = regB;
|
||||||
|
@ -686,7 +717,7 @@ struct IMLInstruction
|
||||||
void make_compare_s32(IMLReg regA, sint32 immS32, IMLReg regR, IMLCondition cond)
|
void make_compare_s32(IMLReg regA, sint32 immS32, IMLReg regR, IMLCondition cond)
|
||||||
{
|
{
|
||||||
this->type = PPCREC_IML_TYPE_COMPARE_S32;
|
this->type = PPCREC_IML_TYPE_COMPARE_S32;
|
||||||
this->operation = -999;
|
this->operation = PPCREC_IML_OP_INVALID;
|
||||||
this->op_compare_s32.regR = regR;
|
this->op_compare_s32.regR = regR;
|
||||||
this->op_compare_s32.regA = regA;
|
this->op_compare_s32.regA = regA;
|
||||||
this->op_compare_s32.immS32 = immS32;
|
this->op_compare_s32.immS32 = immS32;
|
||||||
|
@ -696,7 +727,7 @@ struct IMLInstruction
|
||||||
void make_conditional_jump(IMLReg regBool, bool mustBeTrue)
|
void make_conditional_jump(IMLReg regBool, bool mustBeTrue)
|
||||||
{
|
{
|
||||||
this->type = PPCREC_IML_TYPE_CONDITIONAL_JUMP;
|
this->type = PPCREC_IML_TYPE_CONDITIONAL_JUMP;
|
||||||
this->operation = -999;
|
this->operation = PPCREC_IML_OP_INVALID;
|
||||||
this->op_conditional_jump.registerBool = regBool;
|
this->op_conditional_jump.registerBool = regBool;
|
||||||
this->op_conditional_jump.mustBeTrue = mustBeTrue;
|
this->op_conditional_jump.mustBeTrue = mustBeTrue;
|
||||||
}
|
}
|
||||||
|
@ -704,7 +735,7 @@ struct IMLInstruction
|
||||||
void make_jump()
|
void make_jump()
|
||||||
{
|
{
|
||||||
this->type = PPCREC_IML_TYPE_JUMP;
|
this->type = PPCREC_IML_TYPE_JUMP;
|
||||||
this->operation = -999;
|
this->operation = PPCREC_IML_OP_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
// load from memory
|
// load from memory
|
||||||
|
@ -753,6 +784,15 @@ struct IMLInstruction
|
||||||
this->op_fpr_compare.cond = cond;
|
this->op_fpr_compare.cond = cond;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* X86 specific */
|
||||||
|
void make_x86_eflags_jcc(IMLCondition cond, bool invertedCondition)
|
||||||
|
{
|
||||||
|
this->type = PPCREC_IML_TYPE_X86_EFLAGS_JCC;
|
||||||
|
this->operation = -999;
|
||||||
|
this->op_x86_eflags_jcc.cond = cond;
|
||||||
|
this->op_x86_eflags_jcc.invertedCondition = invertedCondition;
|
||||||
|
}
|
||||||
|
|
||||||
void CheckRegisterUsage(IMLUsedRegisters* registersUsed) const;
|
void CheckRegisterUsage(IMLUsedRegisters* registersUsed) const;
|
||||||
|
|
||||||
void RewriteGPR(const std::unordered_map<IMLRegID, IMLRegID>& translationTable);
|
void RewriteGPR(const std::unordered_map<IMLRegID, IMLRegID>& translationTable);
|
||||||
|
|
|
@ -6,6 +6,11 @@
|
||||||
#include "../PPCRecompilerIml.h"
|
#include "../PPCRecompilerIml.h"
|
||||||
#include "../BackendX64/BackendX64.h"
|
#include "../BackendX64/BackendX64.h"
|
||||||
|
|
||||||
|
#include "Common/FileStream.h"
|
||||||
|
|
||||||
|
#include <boost/container/static_vector.hpp>
|
||||||
|
#include <boost/container/small_vector.hpp>
|
||||||
|
|
||||||
IMLReg _FPRRegFromID(IMLRegID regId)
|
IMLReg _FPRRegFromID(IMLRegID regId)
|
||||||
{
|
{
|
||||||
return IMLReg(IMLRegFormat::F64, IMLRegFormat::F64, 0, regId);
|
return IMLReg(IMLRegFormat::F64, IMLRegFormat::F64, 0, regId);
|
||||||
|
@ -328,3 +333,464 @@ void PPCRecompiler_optimizePSQLoadAndStore(ppcImlGenContext_t* ppcImlGenContext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// analyses register dependencies across the entire function
|
||||||
|
// per segment this will generate information about which registers need to be preserved and which ones don't (e.g. are overwritten)
|
||||||
|
class IMLOptimizerRegIOAnalysis
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// constructor with segment pointer list as span
|
||||||
|
IMLOptimizerRegIOAnalysis(std::span<IMLSegment*> segmentList, uint32 maxRegId) : m_segmentList(segmentList), m_maxRegId(maxRegId)
|
||||||
|
{
|
||||||
|
m_segRegisterInOutList.resize(segmentList.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IMLSegmentRegisterInOut
|
||||||
|
{
|
||||||
|
// todo - since our register ID range is usually pretty small (<64) we could use integer bitmasks to accelerate this? There is a helper class used in RA code already
|
||||||
|
std::unordered_set<IMLRegID> regWritten; // registers which are modified in this segment
|
||||||
|
std::unordered_set<IMLRegID> regImported; // registers which are read in this segment before they are written (importing value from previous segments)
|
||||||
|
std::unordered_set<IMLRegID> regForward; // registers which are not read or written in this segment, but are imported into a later segment (propagated info)
|
||||||
|
};
|
||||||
|
|
||||||
|
// calculate which registers are imported (read-before-written) and forwarded (read-before-written by a later segment) per segment
|
||||||
|
// then in a second step propagate the dependencies across linked segments
|
||||||
|
void ComputeDepedencies()
|
||||||
|
{
|
||||||
|
std::vector<IMLSegmentRegisterInOut>& segRegisterInOutList = m_segRegisterInOutList;
|
||||||
|
IMLSegmentRegisterInOut* segIO = segRegisterInOutList.data();
|
||||||
|
uint32 index = 0;
|
||||||
|
for(auto& seg : m_segmentList)
|
||||||
|
{
|
||||||
|
seg->momentaryIndex = index;
|
||||||
|
index++;
|
||||||
|
for(auto& instr : seg->imlList)
|
||||||
|
{
|
||||||
|
IMLUsedRegisters registerUsage;
|
||||||
|
instr.CheckRegisterUsage(®isterUsage);
|
||||||
|
// registers are considered imported if they are read before being written in this seg
|
||||||
|
registerUsage.ForEachReadGPR([&](IMLReg gprReg) {
|
||||||
|
IMLRegID gprId = gprReg.GetRegID();
|
||||||
|
if (!segIO->regWritten.contains(gprId))
|
||||||
|
{
|
||||||
|
segIO->regImported.insert(gprId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
registerUsage.ForEachWrittenGPR([&](IMLReg gprReg) {
|
||||||
|
IMLRegID gprId = gprReg.GetRegID();
|
||||||
|
segIO->regWritten.insert(gprId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
segIO++;
|
||||||
|
}
|
||||||
|
// for every exit segment, import all registers
|
||||||
|
for(auto& seg : m_segmentList)
|
||||||
|
{
|
||||||
|
if (!seg->nextSegmentIsUncertain)
|
||||||
|
continue;
|
||||||
|
if(seg->deadCodeEliminationHintSeg)
|
||||||
|
continue;
|
||||||
|
IMLSegmentRegisterInOut& segIO = segRegisterInOutList[seg->momentaryIndex];
|
||||||
|
for(uint32 i=0; i<=m_maxRegId; i++)
|
||||||
|
{
|
||||||
|
segIO.regImported.insert((IMLRegID)i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// broadcast dependencies across segment chains
|
||||||
|
std::unordered_set<uint32> segIdsWhichNeedUpdate;
|
||||||
|
for (uint32 i = 0; i < m_segmentList.size(); i++)
|
||||||
|
{
|
||||||
|
segIdsWhichNeedUpdate.insert(i);
|
||||||
|
}
|
||||||
|
while(!segIdsWhichNeedUpdate.empty())
|
||||||
|
{
|
||||||
|
auto firstIt = segIdsWhichNeedUpdate.begin();
|
||||||
|
uint32 segId = *firstIt;
|
||||||
|
segIdsWhichNeedUpdate.erase(firstIt);
|
||||||
|
// forward regImported and regForward to earlier segments into their regForward, unless the register is written
|
||||||
|
auto& curSeg = m_segmentList[segId];
|
||||||
|
IMLSegmentRegisterInOut& curSegIO = segRegisterInOutList[segId];
|
||||||
|
for(auto& prevSeg : curSeg->list_prevSegments)
|
||||||
|
{
|
||||||
|
IMLSegmentRegisterInOut& prevSegIO = segRegisterInOutList[prevSeg->momentaryIndex];
|
||||||
|
bool prevSegChanged = false;
|
||||||
|
for(auto& regId : curSegIO.regImported)
|
||||||
|
{
|
||||||
|
if (!prevSegIO.regWritten.contains(regId))
|
||||||
|
prevSegChanged |= prevSegIO.regForward.insert(regId).second;
|
||||||
|
}
|
||||||
|
for(auto& regId : curSegIO.regForward)
|
||||||
|
{
|
||||||
|
if (!prevSegIO.regWritten.contains(regId))
|
||||||
|
prevSegChanged |= prevSegIO.regForward.insert(regId).second;
|
||||||
|
}
|
||||||
|
if(prevSegChanged)
|
||||||
|
segIdsWhichNeedUpdate.insert(prevSeg->momentaryIndex);
|
||||||
|
}
|
||||||
|
// same for hint links
|
||||||
|
for(auto& prevSeg : curSeg->list_deadCodeHintBy)
|
||||||
|
{
|
||||||
|
IMLSegmentRegisterInOut& prevSegIO = segRegisterInOutList[prevSeg->momentaryIndex];
|
||||||
|
bool prevSegChanged = false;
|
||||||
|
for(auto& regId : curSegIO.regImported)
|
||||||
|
{
|
||||||
|
if (!prevSegIO.regWritten.contains(regId))
|
||||||
|
prevSegChanged |= prevSegIO.regForward.insert(regId).second;
|
||||||
|
}
|
||||||
|
for(auto& regId : curSegIO.regForward)
|
||||||
|
{
|
||||||
|
if (!prevSegIO.regWritten.contains(regId))
|
||||||
|
prevSegChanged |= prevSegIO.regForward.insert(regId).second;
|
||||||
|
}
|
||||||
|
if(prevSegChanged)
|
||||||
|
segIdsWhichNeedUpdate.insert(prevSeg->momentaryIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_set<IMLRegID> GetRegistersNeededAtEndOfSegment(IMLSegment& seg)
|
||||||
|
{
|
||||||
|
std::unordered_set<IMLRegID> regsNeeded;
|
||||||
|
if(seg.nextSegmentIsUncertain)
|
||||||
|
{
|
||||||
|
if(seg.deadCodeEliminationHintSeg)
|
||||||
|
{
|
||||||
|
auto& nextSegIO = m_segRegisterInOutList[seg.deadCodeEliminationHintSeg->momentaryIndex];
|
||||||
|
regsNeeded.insert(nextSegIO.regImported.begin(), nextSegIO.regImported.end());
|
||||||
|
regsNeeded.insert(nextSegIO.regForward.begin(), nextSegIO.regForward.end());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// add all regs
|
||||||
|
for(uint32 i = 0; i <= m_maxRegId; i++)
|
||||||
|
regsNeeded.insert(i);
|
||||||
|
}
|
||||||
|
return regsNeeded;
|
||||||
|
}
|
||||||
|
if(seg.nextSegmentBranchTaken)
|
||||||
|
{
|
||||||
|
auto& nextSegIO = m_segRegisterInOutList[seg.nextSegmentBranchTaken->momentaryIndex];
|
||||||
|
regsNeeded.insert(nextSegIO.regImported.begin(), nextSegIO.regImported.end());
|
||||||
|
regsNeeded.insert(nextSegIO.regForward.begin(), nextSegIO.regForward.end());
|
||||||
|
}
|
||||||
|
if(seg.nextSegmentBranchNotTaken)
|
||||||
|
{
|
||||||
|
auto& nextSegIO = m_segRegisterInOutList[seg.nextSegmentBranchNotTaken->momentaryIndex];
|
||||||
|
regsNeeded.insert(nextSegIO.regImported.begin(), nextSegIO.regImported.end());
|
||||||
|
regsNeeded.insert(nextSegIO.regForward.begin(), nextSegIO.regForward.end());
|
||||||
|
}
|
||||||
|
return regsNeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsRegisterNeededAtEndOfSegment(IMLSegment& seg, IMLRegID regId)
|
||||||
|
{
|
||||||
|
if(seg.nextSegmentIsUncertain)
|
||||||
|
{
|
||||||
|
if(!seg.deadCodeEliminationHintSeg)
|
||||||
|
return true;
|
||||||
|
auto& nextSegIO = m_segRegisterInOutList[seg.deadCodeEliminationHintSeg->momentaryIndex];
|
||||||
|
if(nextSegIO.regImported.contains(regId))
|
||||||
|
return true;
|
||||||
|
if(nextSegIO.regForward.contains(regId))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(seg.nextSegmentBranchTaken)
|
||||||
|
{
|
||||||
|
auto& nextSegIO = m_segRegisterInOutList[seg.nextSegmentBranchTaken->momentaryIndex];
|
||||||
|
if(nextSegIO.regImported.contains(regId))
|
||||||
|
return true;
|
||||||
|
if(nextSegIO.regForward.contains(regId))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(seg.nextSegmentBranchNotTaken)
|
||||||
|
{
|
||||||
|
auto& nextSegIO = m_segRegisterInOutList[seg.nextSegmentBranchNotTaken->momentaryIndex];
|
||||||
|
if(nextSegIO.regImported.contains(regId))
|
||||||
|
return true;
|
||||||
|
if(nextSegIO.regForward.contains(regId))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::span<IMLSegment*> m_segmentList;
|
||||||
|
uint32 m_maxRegId;
|
||||||
|
|
||||||
|
std::vector<IMLSegmentRegisterInOut> m_segRegisterInOutList;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// scan backwards starting from index and return the index of the first found instruction which writes to the given register (by id)
|
||||||
|
sint32 IMLUtil_FindInstructionWhichWritesRegister(IMLSegment& seg, sint32 startIndex, IMLReg reg, sint32 maxScanDistance = -1)
|
||||||
|
{
|
||||||
|
sint32 endIndex = std::max<sint32>(startIndex - maxScanDistance, 0);
|
||||||
|
for (sint32 i = startIndex; i >= endIndex; i--)
|
||||||
|
{
|
||||||
|
IMLInstruction& imlInstruction = seg.imlList[i];
|
||||||
|
IMLUsedRegisters registersUsed;
|
||||||
|
imlInstruction.CheckRegisterUsage(®istersUsed);
|
||||||
|
if (registersUsed.IsBaseGPRWritten(reg))
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns true if the instruction can safely be moved while keeping ordering constraints and data dependencies intact
|
||||||
|
// initialIndex is inclusive, targetIndex is exclusive
|
||||||
|
bool IMLUtil_CanMoveInstructionTo(IMLSegment& seg, sint32 initialIndex, sint32 targetIndex)
|
||||||
|
{
|
||||||
|
boost::container::static_vector<IMLRegID, 8> regsWritten;
|
||||||
|
boost::container::static_vector<IMLRegID, 8> regsRead;
|
||||||
|
// get list of read and written registers
|
||||||
|
IMLUsedRegisters registersUsed;
|
||||||
|
seg.imlList[initialIndex].CheckRegisterUsage(®istersUsed);
|
||||||
|
registersUsed.ForEachAccessedGPR([&](IMLReg reg, bool isWritten) {
|
||||||
|
if (isWritten)
|
||||||
|
regsWritten.push_back(reg.GetRegID());
|
||||||
|
else
|
||||||
|
regsRead.push_back(reg.GetRegID());
|
||||||
|
});
|
||||||
|
// check all the instructions inbetween
|
||||||
|
if(initialIndex < targetIndex)
|
||||||
|
{
|
||||||
|
sint32 scanStartIndex = initialIndex+1; // +1 to skip the moving instruction itself
|
||||||
|
sint32 scanEndIndex = targetIndex;
|
||||||
|
for (sint32 i = scanStartIndex; i < scanEndIndex; i++)
|
||||||
|
{
|
||||||
|
IMLUsedRegisters registersUsed;
|
||||||
|
seg.imlList[i].CheckRegisterUsage(®istersUsed);
|
||||||
|
// in order to be able to move an instruction past another instruction, any of the read registers must not be modified (written)
|
||||||
|
// and any of it's written registers must not be read
|
||||||
|
bool canMove = true;
|
||||||
|
registersUsed.ForEachAccessedGPR([&](IMLReg reg, bool isWritten) {
|
||||||
|
IMLRegID regId = reg.GetRegID();
|
||||||
|
if (!isWritten)
|
||||||
|
canMove = canMove && std::find(regsWritten.begin(), regsWritten.end(), regId) == regsWritten.end();
|
||||||
|
else
|
||||||
|
canMove = canMove && std::find(regsRead.begin(), regsRead.end(), regId) == regsRead.end();
|
||||||
|
});
|
||||||
|
if(!canMove)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cemu_assert_unimplemented(); // backwards scan is todo
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
sint32 IMLUtil_CountRegisterReadsInRange(IMLSegment& seg, sint32 scanStartIndex, sint32 scanEndIndex, IMLRegID regId)
|
||||||
|
{
|
||||||
|
cemu_assert_debug(scanStartIndex <= scanEndIndex);
|
||||||
|
cemu_assert_debug(scanEndIndex < seg.imlList.size());
|
||||||
|
sint32 count = 0;
|
||||||
|
for (sint32 i = scanStartIndex; i <= scanEndIndex; i++)
|
||||||
|
{
|
||||||
|
IMLUsedRegisters registersUsed;
|
||||||
|
seg.imlList[i].CheckRegisterUsage(®istersUsed);
|
||||||
|
registersUsed.ForEachReadGPR([&](IMLReg reg) {
|
||||||
|
if (reg.GetRegID() == regId)
|
||||||
|
count++;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// move instruction from one index to another
|
||||||
|
// instruction will be inserted before the instruction at targetIndex
|
||||||
|
// returns the new instruction index of the moved instruction
|
||||||
|
sint32 IMLUtil_MoveInstructionTo(IMLSegment& seg, sint32 initialIndex, sint32 targetIndex)
|
||||||
|
{
|
||||||
|
cemu_assert_debug(initialIndex != targetIndex);
|
||||||
|
IMLInstruction temp = seg.imlList[initialIndex];
|
||||||
|
if (initialIndex < targetIndex)
|
||||||
|
{
|
||||||
|
cemu_assert_debug(targetIndex > 0);
|
||||||
|
targetIndex--;
|
||||||
|
std::copy_backward(seg.imlList.begin() + initialIndex + 1, seg.imlList.begin() + targetIndex + 1, seg.imlList.begin() + targetIndex);
|
||||||
|
seg.imlList[targetIndex] = temp;
|
||||||
|
return targetIndex;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cemu_assert_unimplemented(); // testing needed
|
||||||
|
std::copy(seg.imlList.begin() + targetIndex, seg.imlList.begin() + initialIndex, seg.imlList.begin() + targetIndex + 1);
|
||||||
|
seg.imlList[targetIndex] = temp;
|
||||||
|
return targetIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// x86 specific
|
||||||
|
bool IMLOptimizerX86_ModifiesEFlags(IMLInstruction& inst)
|
||||||
|
{
|
||||||
|
// this is a very conservative implementation. There are more cases but this is good enough for now
|
||||||
|
if(inst.type == PPCREC_IML_TYPE_NAME_R || inst.type == PPCREC_IML_TYPE_R_NAME)
|
||||||
|
return false;
|
||||||
|
if((inst.type == PPCREC_IML_TYPE_R_R || inst.type == PPCREC_IML_TYPE_R_S32) && inst.operation == PPCREC_IML_OP_ASSIGN)
|
||||||
|
return false;
|
||||||
|
return true; // if we dont know for sure, assume it does
|
||||||
|
}
|
||||||
|
|
||||||
|
void IMLOptimizer_DebugPrintSeg(ppcImlGenContext_t& ppcImlGenContext, IMLSegment& seg)
|
||||||
|
{
|
||||||
|
printf("----------------\n");
|
||||||
|
IMLDebug_DumpSegment(&ppcImlGenContext, &seg);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IMLOptimizer_RemoveDeadCodeFromSegment(IMLOptimizerRegIOAnalysis& regIoAnalysis, IMLSegment& seg)
|
||||||
|
{
|
||||||
|
// algorithm works like this:
|
||||||
|
// Calculate which registers need to be preserved at the end of each segment
|
||||||
|
// Then for each segment:
|
||||||
|
// - Iterate instructions backwards
|
||||||
|
// - Maintain a list of registers which are read at a later point (initially this is the list from the first step)
|
||||||
|
// - If an instruction only modifies registers which are not in the read list, then it is dead code and can be replaced with a no-op
|
||||||
|
|
||||||
|
std::unordered_set<IMLRegID> regsNeeded = regIoAnalysis.GetRegistersNeededAtEndOfSegment(seg);
|
||||||
|
|
||||||
|
// start with suffix instruction
|
||||||
|
if(seg.HasSuffixInstruction())
|
||||||
|
{
|
||||||
|
IMLInstruction& imlInstruction = seg.imlList[seg.GetSuffixInstructionIndex()];
|
||||||
|
IMLUsedRegisters registersUsed;
|
||||||
|
imlInstruction.CheckRegisterUsage(®istersUsed);
|
||||||
|
registersUsed.ForEachWrittenGPR([&](IMLReg reg) {
|
||||||
|
regsNeeded.erase(reg.GetRegID());
|
||||||
|
});
|
||||||
|
registersUsed.ForEachReadGPR([&](IMLReg reg) {
|
||||||
|
regsNeeded.insert(reg.GetRegID());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// iterate instructions backwards
|
||||||
|
for (sint32 i = seg.imlList.size() - (seg.HasSuffixInstruction() ? 2:1); i >= 0; i--)
|
||||||
|
{
|
||||||
|
IMLInstruction& imlInstruction = seg.imlList[i];
|
||||||
|
IMLUsedRegisters registersUsed;
|
||||||
|
imlInstruction.CheckRegisterUsage(®istersUsed);
|
||||||
|
// register read -> remove from overwritten list
|
||||||
|
// register written -> add to overwritten list
|
||||||
|
|
||||||
|
// check if this instruction only writes registers which will never be read
|
||||||
|
bool onlyWritesRedundantRegisters = true;
|
||||||
|
registersUsed.ForEachWrittenGPR([&](IMLReg reg) {
|
||||||
|
if (regsNeeded.contains(reg.GetRegID()))
|
||||||
|
onlyWritesRedundantRegisters = false;
|
||||||
|
});
|
||||||
|
// check if any of the written registers are read after this point
|
||||||
|
registersUsed.ForEachWrittenGPR([&](IMLReg reg) {
|
||||||
|
regsNeeded.erase(reg.GetRegID());
|
||||||
|
});
|
||||||
|
registersUsed.ForEachReadGPR([&](IMLReg reg) {
|
||||||
|
regsNeeded.insert(reg.GetRegID());
|
||||||
|
});
|
||||||
|
// for now we only allow some instruction types to be deleted, eventually we should find a safer way to identify side effects that can't be judged by register usage alone
|
||||||
|
if(imlInstruction.type != PPCREC_IML_TYPE_R_R && imlInstruction.type != PPCREC_IML_TYPE_R_R_S32 && imlInstruction.type != PPCREC_IML_TYPE_COMPARE && imlInstruction.type != PPCREC_IML_TYPE_COMPARE_S32)
|
||||||
|
continue;
|
||||||
|
if(onlyWritesRedundantRegisters)
|
||||||
|
{
|
||||||
|
imlInstruction.make_no_op();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IMLOptimizerX86_SubstituteCJumpForEflagsJump(IMLOptimizerRegIOAnalysis& regIoAnalysis, IMLSegment& seg)
|
||||||
|
{
|
||||||
|
// convert and optimize bool condition jumps to eflags condition jumps
|
||||||
|
// - Moves eflag setter (e.g. cmp) closer to eflags consumer (conditional jump) if necessary. If not possible but required then exit early
|
||||||
|
// - Since we only rely on eflags, the boolean register can be optimized out if DCE considers it unused
|
||||||
|
// - Further detect and optimize patterns like DEC + CMP + JCC into fused ops (todo)
|
||||||
|
|
||||||
|
// check if this segment ends with a conditional jump
|
||||||
|
if(!seg.HasSuffixInstruction())
|
||||||
|
return;
|
||||||
|
sint32 cjmpInstIndex = seg.GetSuffixInstructionIndex();
|
||||||
|
if(cjmpInstIndex < 0)
|
||||||
|
return;
|
||||||
|
IMLInstruction& cjumpInstr = seg.imlList[cjmpInstIndex];
|
||||||
|
if( cjumpInstr.type != PPCREC_IML_TYPE_CONDITIONAL_JUMP )
|
||||||
|
return;
|
||||||
|
IMLReg regCondBool = cjumpInstr.op_conditional_jump.registerBool;
|
||||||
|
bool invertedCondition = !cjumpInstr.op_conditional_jump.mustBeTrue;
|
||||||
|
// find the instruction which sets the bool
|
||||||
|
sint32 cmpInstrIndex = IMLUtil_FindInstructionWhichWritesRegister(seg, cjmpInstIndex-1, regCondBool, 20);
|
||||||
|
if(cmpInstrIndex < 0)
|
||||||
|
return;
|
||||||
|
// check if its an instruction combo which can be optimized (currently only cmp + cjump) and get the condition
|
||||||
|
IMLInstruction& condSetterInstr = seg.imlList[cmpInstrIndex];
|
||||||
|
IMLCondition cond;
|
||||||
|
if(condSetterInstr.type == PPCREC_IML_TYPE_COMPARE)
|
||||||
|
cond = condSetterInstr.op_compare.cond;
|
||||||
|
else if(condSetterInstr.type == PPCREC_IML_TYPE_COMPARE_S32)
|
||||||
|
cond = condSetterInstr.op_compare_s32.cond;
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
// check if instructions inbetween modify eflags
|
||||||
|
sint32 indexEflagsSafeStart = -1; // index of the first instruction which does not modify eflags up to cjump
|
||||||
|
for(sint32 i = cjmpInstIndex-1; i > cmpInstrIndex; i--)
|
||||||
|
{
|
||||||
|
if(IMLOptimizerX86_ModifiesEFlags(seg.imlList[i]))
|
||||||
|
{
|
||||||
|
indexEflagsSafeStart = i+1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(indexEflagsSafeStart >= 0)
|
||||||
|
{
|
||||||
|
cemu_assert(indexEflagsSafeStart > 0);
|
||||||
|
// there are eflags-modifying instructions inbetween the bool setter and cjump
|
||||||
|
// try to move the eflags setter close enough to the cjump (to indexEflagsSafeStart)
|
||||||
|
bool canMove = IMLUtil_CanMoveInstructionTo(seg, cmpInstrIndex, indexEflagsSafeStart);
|
||||||
|
if(!canMove)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cmpInstrIndex = IMLUtil_MoveInstructionTo(seg, cmpInstrIndex, indexEflagsSafeStart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// we can turn the jump into an eflags jump
|
||||||
|
cjumpInstr.make_x86_eflags_jcc(cond, invertedCondition);
|
||||||
|
|
||||||
|
if (IMLUtil_CountRegisterReadsInRange(seg, cmpInstrIndex, cjmpInstIndex, regCondBool.GetRegID()) > 1 || regIoAnalysis.IsRegisterNeededAtEndOfSegment(seg, regCondBool.GetRegID()))
|
||||||
|
return; // bool register is used beyond the CMP, we can't drop it
|
||||||
|
|
||||||
|
auto& cmpInstr = seg.imlList[cmpInstrIndex];
|
||||||
|
cemu_assert_debug(cmpInstr.type == PPCREC_IML_TYPE_COMPARE || cmpInstr.type == PPCREC_IML_TYPE_COMPARE_S32);
|
||||||
|
if(cmpInstr.type == PPCREC_IML_TYPE_COMPARE)
|
||||||
|
{
|
||||||
|
IMLReg regA = cmpInstr.op_compare.regA;
|
||||||
|
IMLReg regB = cmpInstr.op_compare.regB;
|
||||||
|
seg.imlList[cmpInstrIndex].make_r_r(PPCREC_IML_OP_X86_CMP, regA, regB);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IMLReg regA = cmpInstr.op_compare_s32.regA;
|
||||||
|
sint32 val = cmpInstr.op_compare_s32.immS32;
|
||||||
|
seg.imlList[cmpInstrIndex].make_r_s32(PPCREC_IML_OP_X86_CMP, regA, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void IMLOptimizer_StandardOptimizationPassForSegment(IMLOptimizerRegIOAnalysis& regIoAnalysis, IMLSegment& seg)
|
||||||
|
{
|
||||||
|
IMLOptimizer_RemoveDeadCodeFromSegment(regIoAnalysis, seg);
|
||||||
|
|
||||||
|
// x86 specific optimizations
|
||||||
|
IMLOptimizerX86_SubstituteCJumpForEflagsJump(regIoAnalysis, seg); // this pass should be applied late since it creates invisible eflags dependencies (which would break further register dependency analysis)
|
||||||
|
}
|
||||||
|
|
||||||
|
void IMLOptimizer_StandardOptimizationPass(ppcImlGenContext_t& ppcImlGenContext)
|
||||||
|
{
|
||||||
|
IMLOptimizerRegIOAnalysis regIoAnalysis(ppcImlGenContext.segmentList2, ppcImlGenContext.GetMaxRegId());
|
||||||
|
regIoAnalysis.ComputeDepedencies();
|
||||||
|
for (IMLSegment* segIt : ppcImlGenContext.segmentList2)
|
||||||
|
{
|
||||||
|
IMLOptimizer_StandardOptimizationPassForSegment(regIoAnalysis, *segIt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -75,14 +75,14 @@ bool _detectLoop(IMLSegment* currentSegment, sint32 depth, uint32 iterationIndex
|
||||||
{
|
{
|
||||||
if (currentSegment->nextSegmentBranchNotTaken->momentaryIndex > currentSegment->momentaryIndex)
|
if (currentSegment->nextSegmentBranchNotTaken->momentaryIndex > currentSegment->momentaryIndex)
|
||||||
{
|
{
|
||||||
currentSegment->raInfo.isPartOfProcessedLoop = _detectLoop(currentSegment->nextSegmentBranchNotTaken, depth + 1, iterationIndex, imlSegmentLoopBase);
|
currentSegment->raInfo.isPartOfProcessedLoop |= _detectLoop(currentSegment->nextSegmentBranchNotTaken, depth + 1, iterationIndex, imlSegmentLoopBase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (currentSegment->nextSegmentBranchTaken)
|
if (currentSegment->nextSegmentBranchTaken)
|
||||||
{
|
{
|
||||||
if (currentSegment->nextSegmentBranchTaken->momentaryIndex > currentSegment->momentaryIndex)
|
if (currentSegment->nextSegmentBranchTaken->momentaryIndex > currentSegment->momentaryIndex)
|
||||||
{
|
{
|
||||||
currentSegment->raInfo.isPartOfProcessedLoop = _detectLoop(currentSegment->nextSegmentBranchTaken, depth + 1, iterationIndex, imlSegmentLoopBase);
|
currentSegment->raInfo.isPartOfProcessedLoop |= _detectLoop(currentSegment->nextSegmentBranchTaken, depth + 1, iterationIndex, imlSegmentLoopBase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (currentSegment->raInfo.isPartOfProcessedLoop)
|
if (currentSegment->raInfo.isPartOfProcessedLoop)
|
||||||
|
@ -341,8 +341,8 @@ void IMLRA_HandleFixedRegisters(ppcImlGenContext_t* ppcImlGenContext, IMLSegment
|
||||||
{
|
{
|
||||||
// this works as a pre-pass to actual register allocation. Assigning registers in advance based on fixed requirements (e.g. calling conventions and operations with fixed-reg input/output like x86 DIV/MUL)
|
// this works as a pre-pass to actual register allocation. Assigning registers in advance based on fixed requirements (e.g. calling conventions and operations with fixed-reg input/output like x86 DIV/MUL)
|
||||||
// algorithm goes as follows:
|
// algorithm goes as follows:
|
||||||
// 1) Iterate all instructions from beginning to end and keep a list of covering ranges
|
// 1) Iterate all instructions in the function from beginning to end and keep a list of active ranges for the currently iterated instruction
|
||||||
// 2) If we encounter an instruction with a fixed register we:
|
// 2) If we encounter an instruction with a fixed register requirement we:
|
||||||
// 2.0) Check if there are any other ranges already using the same fixed-register and if yes, we split them and unassign the register for any follow-up instructions just prior to the current instruction
|
// 2.0) Check if there are any other ranges already using the same fixed-register and if yes, we split them and unassign the register for any follow-up instructions just prior to the current instruction
|
||||||
// 2.1) For inputs: Split the range that needs to be assigned a phys reg on the current instruction. Basically creating a 1-instruction long subrange that we can assign the physical register. RA will then schedule register allocation around that and avoid moves
|
// 2.1) For inputs: Split the range that needs to be assigned a phys reg on the current instruction. Basically creating a 1-instruction long subrange that we can assign the physical register. RA will then schedule register allocation around that and avoid moves
|
||||||
// 2.2) For outputs: Split the range that needs to be assigned a phys reg on the current instruction
|
// 2.2) For outputs: Split the range that needs to be assigned a phys reg on the current instruction
|
||||||
|
|
|
@ -81,6 +81,10 @@ struct IMLSegment
|
||||||
IMLSegment* nextSegmentBranchTaken{};
|
IMLSegment* nextSegmentBranchTaken{};
|
||||||
bool nextSegmentIsUncertain{};
|
bool nextSegmentIsUncertain{};
|
||||||
std::vector<IMLSegment*> list_prevSegments{};
|
std::vector<IMLSegment*> list_prevSegments{};
|
||||||
|
// source for overwrite analysis (if nextSegmentIsUncertain is true)
|
||||||
|
// sometimes a segment is marked as an exit point, but for the purposes of dead code elimination we know the next segment
|
||||||
|
IMLSegment* deadCodeEliminationHintSeg{};
|
||||||
|
std::vector<IMLSegment*> list_deadCodeHintBy{};
|
||||||
// enterable segments
|
// enterable segments
|
||||||
bool isEnterable{}; // this segment can be entered from outside the recompiler (no preloaded registers necessary)
|
bool isEnterable{}; // this segment can be entered from outside the recompiler (no preloaded registers necessary)
|
||||||
uint32 enterPPCAddress{}; // used if isEnterable is true
|
uint32 enterPPCAddress{}; // used if isEnterable is true
|
||||||
|
@ -101,6 +105,14 @@ struct IMLSegment
|
||||||
return nextSegmentBranchNotTaken;
|
return nextSegmentBranchNotTaken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetNextSegmentForOverwriteHints(IMLSegment* seg)
|
||||||
|
{
|
||||||
|
cemu_assert_debug(!deadCodeEliminationHintSeg);
|
||||||
|
deadCodeEliminationHintSeg = seg;
|
||||||
|
if (seg)
|
||||||
|
seg->list_deadCodeHintBy.push_back(this);
|
||||||
|
}
|
||||||
|
|
||||||
// instruction API
|
// instruction API
|
||||||
IMLInstruction* AppendInstruction();
|
IMLInstruction* AppendInstruction();
|
||||||
|
|
||||||
|
|
|
@ -139,7 +139,6 @@ PPCRecFunction_t* PPCRecompiler_recompileFunction(PPCFunctionBoundaryTracker::PP
|
||||||
cemuLog_log(LogType::Force, "Attempting to recompile function outside of allowed code area");
|
cemuLog_log(LogType::Force, "Attempting to recompile function outside of allowed code area");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 codeGenRangeStart;
|
uint32 codeGenRangeStart;
|
||||||
uint32 codeGenRangeSize = 0;
|
uint32 codeGenRangeSize = 0;
|
||||||
coreinit::OSGetCodegenVirtAddrRangeInternal(codeGenRangeStart, codeGenRangeSize);
|
coreinit::OSGetCodegenVirtAddrRangeInternal(codeGenRangeStart, codeGenRangeSize);
|
||||||
|
@ -160,6 +159,7 @@ PPCRecFunction_t* PPCRecompiler_recompileFunction(PPCFunctionBoundaryTracker::PP
|
||||||
|
|
||||||
// generate intermediate code
|
// generate intermediate code
|
||||||
ppcImlGenContext_t ppcImlGenContext = { 0 };
|
ppcImlGenContext_t ppcImlGenContext = { 0 };
|
||||||
|
ppcImlGenContext.debug_entryPPCAddress = range.startAddress;
|
||||||
bool compiledSuccessfully = PPCRecompiler_generateIntermediateCode(ppcImlGenContext, ppcRecFunc, entryAddresses, boundaryTracker);
|
bool compiledSuccessfully = PPCRecompiler_generateIntermediateCode(ppcImlGenContext, ppcRecFunc, entryAddresses, boundaryTracker);
|
||||||
if (compiledSuccessfully == false)
|
if (compiledSuccessfully == false)
|
||||||
{
|
{
|
||||||
|
@ -240,7 +240,9 @@ PPCRecFunction_t* PPCRecompiler_recompileFunction(PPCFunctionBoundaryTracker::PP
|
||||||
entryPointsOut.emplace_back(ppcEnterOffset, x64Offset);
|
entryPointsOut.emplace_back(ppcEnterOffset, x64Offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
cemuLog_log(LogType::Force, "[Recompiler] Successfully compiled {:08x} - {:08x} Segments: {} Entrypoints: {}", ppcRecFunc->ppcAddress, ppcRecFunc->ppcAddress + ppcRecFunc->ppcSize, ppcImlGenContext.segmentList2.size(), entryPointsOut.size());
|
//cemuLog_log(LogType::Force, "[Recompiler] Successfully compiled {:08x} - {:08x} Segments: {} Entrypoints: {}", ppcRecFunc->ppcAddress, ppcRecFunc->ppcAddress + ppcRecFunc->ppcSize, ppcImlGenContext.segmentList2.size(), entryPointsOut.size());
|
||||||
|
|
||||||
|
cemuLog_logDebug(LogType::Force, "[Recompiler] PPC 0x{:08x} -> x64: 0x{:x}", (uint32)ppcRecFunc->ppcAddress, (uint64)(uintptr_t)ppcRecFunc->x86Code);
|
||||||
|
|
||||||
return ppcRecFunc;
|
return ppcRecFunc;
|
||||||
}
|
}
|
||||||
|
@ -301,11 +303,19 @@ bool PPCRecompiler_ApplyIMLPasses(ppcImlGenContext_t& ppcImlGenContext)
|
||||||
// delay byte swapping for certain load+store patterns
|
// delay byte swapping for certain load+store patterns
|
||||||
IMLOptimizer_OptimizeDirectIntegerCopies(&ppcImlGenContext);
|
IMLOptimizer_OptimizeDirectIntegerCopies(&ppcImlGenContext);
|
||||||
|
|
||||||
|
IMLOptimizer_StandardOptimizationPass(ppcImlGenContext);
|
||||||
|
|
||||||
PPCRecompiler_NativeRegisterAllocatorPass(ppcImlGenContext);
|
PPCRecompiler_NativeRegisterAllocatorPass(ppcImlGenContext);
|
||||||
|
|
||||||
//PPCRecompiler_reorderConditionModifyInstructions(&ppcImlGenContext);
|
//PPCRecompiler_reorderConditionModifyInstructions(&ppcImlGenContext);
|
||||||
//PPCRecompiler_removeRedundantCRUpdates(&ppcImlGenContext);
|
//PPCRecompiler_removeRedundantCRUpdates(&ppcImlGenContext);
|
||||||
|
|
||||||
|
// if(ppcImlGenContext.debug_entryPPCAddress == 0x0200E1E8)
|
||||||
|
// {
|
||||||
|
// IMLDebug_Dump(&ppcImlGenContext);
|
||||||
|
// __debugbreak();
|
||||||
|
// }
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,13 +41,15 @@ struct ppcImlGenContext_t
|
||||||
bool PSE{ true };
|
bool PSE{ true };
|
||||||
// cycle counter
|
// cycle counter
|
||||||
uint32 cyclesSinceLastBranch; // used to track ppc cycles
|
uint32 cyclesSinceLastBranch; // used to track ppc cycles
|
||||||
// temporary general purpose registers
|
|
||||||
//uint32 mappedRegister[PPC_REC_MAX_VIRTUAL_GPR];
|
|
||||||
// temporary floating point registers (single and double precision)
|
|
||||||
//uint32 mappedFPRRegister[256];
|
|
||||||
|
|
||||||
std::unordered_map<IMLName, IMLReg> mappedRegs;
|
std::unordered_map<IMLName, IMLReg> mappedRegs;
|
||||||
|
|
||||||
|
uint32 GetMaxRegId() const
|
||||||
|
{
|
||||||
|
if (mappedRegs.empty())
|
||||||
|
return 0;
|
||||||
|
return mappedRegs.size()-1;
|
||||||
|
}
|
||||||
|
|
||||||
// list of segments
|
// list of segments
|
||||||
std::vector<IMLSegment*> segmentList2;
|
std::vector<IMLSegment*> segmentList2;
|
||||||
// code generation control
|
// code generation control
|
||||||
|
@ -62,6 +64,8 @@ struct ppcImlGenContext_t
|
||||||
{
|
{
|
||||||
bool modifiesGQR[8];
|
bool modifiesGQR[8];
|
||||||
}tracking;
|
}tracking;
|
||||||
|
// debug helpers
|
||||||
|
uint32 debug_entryPPCAddress{0};
|
||||||
|
|
||||||
~ppcImlGenContext_t()
|
~ppcImlGenContext_t()
|
||||||
{
|
{
|
||||||
|
|
|
@ -2935,6 +2935,10 @@ void PPCRecompiler_HandleCycleCheckCount(ppcImlGenContext_t& ppcImlGenContext, P
|
||||||
splitSeg->SetLinkBranchTaken(exitSegment);
|
splitSeg->SetLinkBranchTaken(exitSegment);
|
||||||
|
|
||||||
exitSegment->AppendInstruction()->make_macro(PPCREC_IML_MACRO_LEAVE, basicBlockInfo.startAddress, 0, 0, IMLREG_INVALID);
|
exitSegment->AppendInstruction()->make_macro(PPCREC_IML_MACRO_LEAVE, basicBlockInfo.startAddress, 0, 0, IMLREG_INVALID);
|
||||||
|
|
||||||
|
cemu_assert_debug(splitSeg->nextSegmentBranchNotTaken);
|
||||||
|
// let the IML optimizer and RA know that the original segment should be used during analysis for dead code elimination
|
||||||
|
exitSegment->SetNextSegmentForOverwriteHints(splitSeg->nextSegmentBranchNotTaken);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPCRecompiler_SetSegmentsUncertainFlow(ppcImlGenContext_t& ppcImlGenContext)
|
void PPCRecompiler_SetSegmentsUncertainFlow(ppcImlGenContext_t& ppcImlGenContext)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue