mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-13 09:39:33 +01:00
arm64jit: Handle branch/jump in branch delay slots.
This commit is contained in:
1 parent
bac36df453
commit
f75dadd1d6
3 files changed
+176
-87
No files matched your search
@@ -55,7 +55,7 @@
|
||||
#define LOOPOPTIMIZATION 0
|
||||
|
||||
// We can disable nice delay slots.
|
||||
// #define CONDITIONAL_NICE_DELAYSLOT delaySlotIsNice = false;
|
||||
// #define CONDITIONAL_NICE_DELAYSLOT branchInfo.delaySlotIsNice = false;
|
||||
#define CONDITIONAL_NICE_DELAYSLOT ;
|
||||
|
||||
using namespace MIPSAnalyst;
|
||||
@@ -76,9 +76,13 @@ void ArmJit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
|
||||
MIPSGPReg rs = _RS;
|
||||
u32 targetAddr = GetCompilerPC() + offset + 4;
|
||||
|
||||
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
|
||||
branchInfo.delaySlotIsNice = IsDelaySlotNiceReg(op, branchInfo.delaySlotOp, rt, rs);
|
||||
CONDITIONAL_NICE_DELAYSLOT;
|
||||
|
||||
bool immBranch = false;
|
||||
bool immBranchTaken = false;
|
||||
if (gpr.IsImm(rs) && gpr.IsImm(rt)) {
|
||||
if (gpr.IsImm(rs) && gpr.IsImm(rt) && !branchInfo.delaySlotIsBranch) {
|
||||
// The cc flags are opposites: when NOT to take the branch.
|
||||
bool immBranchNotTaken;
|
||||
s32 rsImm = (s32)gpr.GetImm(rs);
|
||||
@@ -112,11 +116,9 @@ void ArmJit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
|
||||
return;
|
||||
}
|
||||
|
||||
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
|
||||
bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rt, rs);
|
||||
CONDITIONAL_NICE_DELAYSLOT;
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
|
||||
|
||||
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
|
||||
if (immBranch) {
|
||||
// Continuing is handled above, this is just static jumping.
|
||||
if (immBranchTaken || !likely)
|
||||
@@ -124,10 +126,10 @@ void ArmJit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
|
||||
else
|
||||
FlushAll();
|
||||
|
||||
const u32 destAddr = immBranchTaken ? targetAddr : GetCompilerPC() + 8;
|
||||
const u32 destAddr = immBranchTaken ? targetAddr : notTakenTarget;
|
||||
WriteExit(destAddr, js.nextExit++);
|
||||
} else {
|
||||
if (!likely && delaySlotIsNice)
|
||||
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
|
||||
// We might be able to flip the condition (EQ/NEQ are easy.)
|
||||
@@ -156,7 +158,7 @@ void ArmJit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
|
||||
|
||||
ArmGen::FixupBranch ptr;
|
||||
if (!likely) {
|
||||
if (!delaySlotIsNice)
|
||||
if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
|
||||
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
|
||||
else
|
||||
FlushAll();
|
||||
@@ -164,7 +166,18 @@ void ArmJit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
|
||||
} else {
|
||||
FlushAll();
|
||||
ptr = B_CC(cc);
|
||||
CompileDelaySlot(DELAYSLOT_FLUSH);
|
||||
if (!branchInfo.delaySlotIsBranch)
|
||||
CompileDelaySlot(DELAYSLOT_FLUSH);
|
||||
}
|
||||
|
||||
if (branchInfo.delaySlotIsBranch) {
|
||||
// We still link when the branch is taken (targetAddr case.)
|
||||
// Remember, it's from the perspective of the delay slot, so +12.
|
||||
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
|
||||
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
|
||||
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
|
||||
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
|
||||
FlushAll();
|
||||
}
|
||||
|
||||
// Take the branch
|
||||
@@ -172,7 +185,7 @@ void ArmJit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
|
||||
|
||||
SetJumpTarget(ptr);
|
||||
// Not taken
|
||||
WriteExit(GetCompilerPC() + 8, js.nextExit++);
|
||||
WriteExit(notTakenTarget, js.nextExit++);
|
||||
}
|
||||
|
||||
js.compiling = false;
|
||||
@@ -189,9 +202,13 @@ void ArmJit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool like
|
||||
MIPSGPReg rs = _RS;
|
||||
u32 targetAddr = GetCompilerPC() + offset + 4;
|
||||
|
||||
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), andLink, likely);
|
||||
branchInfo.delaySlotIsNice = IsDelaySlotNiceReg(op, branchInfo.delaySlotOp, rs);
|
||||
CONDITIONAL_NICE_DELAYSLOT;
|
||||
|
||||
bool immBranch = false;
|
||||
bool immBranchTaken = false;
|
||||
if (gpr.IsImm(rs)) {
|
||||
if (gpr.IsImm(rs) && !branchInfo.delaySlotIsBranch) {
|
||||
// The cc flags are opposites: when NOT to take the branch.
|
||||
bool immBranchNotTaken;
|
||||
s32 imm = (s32)gpr.GetImm(rs);
|
||||
@@ -231,11 +248,9 @@ void ArmJit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool like
|
||||
return;
|
||||
}
|
||||
|
||||
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
|
||||
bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs);
|
||||
CONDITIONAL_NICE_DELAYSLOT;
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
|
||||
|
||||
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
|
||||
if (immBranch) {
|
||||
// Continuing is handled above, this is just static jumping.
|
||||
if (andLink)
|
||||
@@ -245,10 +260,10 @@ void ArmJit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool like
|
||||
else
|
||||
FlushAll();
|
||||
|
||||
const u32 destAddr = immBranchTaken ? targetAddr : GetCompilerPC() + 8;
|
||||
const u32 destAddr = immBranchTaken ? targetAddr : notTakenTarget;
|
||||
WriteExit(destAddr, js.nextExit++);
|
||||
} else {
|
||||
if (!likely && delaySlotIsNice)
|
||||
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
|
||||
gpr.MapReg(rs);
|
||||
@@ -260,7 +275,7 @@ void ArmJit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool like
|
||||
ArmGen::FixupBranch ptr;
|
||||
if (!likely)
|
||||
{
|
||||
if (!delaySlotIsNice)
|
||||
if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
|
||||
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
|
||||
else
|
||||
FlushAll();
|
||||
@@ -270,7 +285,18 @@ void ArmJit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool like
|
||||
{
|
||||
FlushAll();
|
||||
ptr = B_CC(cc);
|
||||
CompileDelaySlot(DELAYSLOT_FLUSH);
|
||||
if (!branchInfo.delaySlotIsBranch)
|
||||
CompileDelaySlot(DELAYSLOT_FLUSH);
|
||||
}
|
||||
|
||||
if (branchInfo.delaySlotIsBranch) {
|
||||
// We still link when the branch is taken (targetAddr case.)
|
||||
// Remember, it's from the perspective of the delay slot, so +12.
|
||||
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
|
||||
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
|
||||
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
|
||||
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
|
||||
FlushAll();
|
||||
}
|
||||
|
||||
// Take the branch
|
||||
@@ -278,7 +304,7 @@ void ArmJit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool like
|
||||
|
||||
SetJumpTarget(ptr);
|
||||
// Not taken
|
||||
WriteExit(GetCompilerPC() + 8, js.nextExit++);
|
||||
WriteExit(notTakenTarget, js.nextExit++);
|
||||
}
|
||||
js.compiling = false;
|
||||
}
|
||||
@@ -335,11 +361,12 @@ void ArmJit::BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely)
|
||||
int offset = TARGET16;
|
||||
u32 targetAddr = GetCompilerPC() + offset + 4;
|
||||
|
||||
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
|
||||
bool delaySlotIsNice = IsDelaySlotNiceFPU(op, delaySlotOp);
|
||||
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
|
||||
branchInfo.delaySlotIsNice = IsDelaySlotNiceFPU(op, branchInfo.delaySlotOp);
|
||||
CONDITIONAL_NICE_DELAYSLOT;
|
||||
if (!likely && delaySlotIsNice)
|
||||
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
|
||||
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
|
||||
gpr.MapReg(MIPS_REG_FPCOND);
|
||||
@@ -348,7 +375,7 @@ void ArmJit::BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely)
|
||||
ArmGen::FixupBranch ptr;
|
||||
if (!likely)
|
||||
{
|
||||
if (!delaySlotIsNice)
|
||||
if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
|
||||
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
|
||||
else
|
||||
FlushAll();
|
||||
@@ -358,7 +385,18 @@ void ArmJit::BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely)
|
||||
{
|
||||
FlushAll();
|
||||
ptr = B_CC(cc);
|
||||
CompileDelaySlot(DELAYSLOT_FLUSH);
|
||||
if (!branchInfo.delaySlotIsBranch)
|
||||
CompileDelaySlot(DELAYSLOT_FLUSH);
|
||||
}
|
||||
|
||||
if (branchInfo.delaySlotIsBranch) {
|
||||
// We still link when the branch is taken (targetAddr case.)
|
||||
// Remember, it's from the perspective of the delay slot, so +12.
|
||||
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
|
||||
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
|
||||
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
|
||||
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
|
||||
FlushAll();
|
||||
}
|
||||
|
||||
// Take the branch
|
||||
@@ -366,7 +404,7 @@ void ArmJit::BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely)
|
||||
|
||||
SetJumpTarget(ptr);
|
||||
// Not taken
|
||||
WriteExit(GetCompilerPC() + 8, js.nextExit++);
|
||||
WriteExit(ResolveNotTakenTarget(branchInfo), js.nextExit++);
|
||||
js.compiling = false;
|
||||
}
|
||||
|
||||
@@ -394,19 +432,16 @@ void ArmJit::BranchVFPUFlag(MIPSOpcode op, CCFlags cc, bool likely)
|
||||
int offset = TARGET16;
|
||||
u32 targetAddr = GetCompilerPC() + offset + 4;
|
||||
|
||||
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
|
||||
|
||||
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
|
||||
// Sometimes there's a VFPU branch in a delay slot (Disgaea 2: Dark Hero Days, Zettai Hero Project, La Pucelle)
|
||||
// The behavior is undefined - the CPU may take the second branch even if the first one passes.
|
||||
// However, it does consistently try each branch, which these games seem to expect.
|
||||
bool delaySlotIsBranch = MIPSCodeUtils::IsVFPUBranch(delaySlotOp);
|
||||
bool delaySlotIsNice = !delaySlotIsBranch && IsDelaySlotNiceVFPU(op, delaySlotOp);
|
||||
branchInfo.delaySlotIsNice = IsDelaySlotNiceVFPU(op, branchInfo.delaySlotOp);
|
||||
CONDITIONAL_NICE_DELAYSLOT;
|
||||
if (!likely && delaySlotIsNice)
|
||||
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
|
||||
if (!likely && branchInfo.delaySlotIsNice)
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
if (delaySlotIsBranch && (signed short)(delaySlotOp & 0xFFFF) != (signed short)(op & 0xFFFF) - 1)
|
||||
ERROR_LOG_REPORT(JIT, "VFPU branch in VFPU delay slot at %08x with different target", GetCompilerPC());
|
||||
|
||||
int imm3 = (op >> 18) & 7;
|
||||
|
||||
@@ -417,7 +452,7 @@ void ArmJit::BranchVFPUFlag(MIPSOpcode op, CCFlags cc, bool likely)
|
||||
js.inDelaySlot = true;
|
||||
if (!likely)
|
||||
{
|
||||
if (!delaySlotIsNice && !delaySlotIsBranch)
|
||||
if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
|
||||
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
|
||||
else
|
||||
FlushAll();
|
||||
@@ -427,18 +462,27 @@ void ArmJit::BranchVFPUFlag(MIPSOpcode op, CCFlags cc, bool likely)
|
||||
{
|
||||
FlushAll();
|
||||
ptr = B_CC(cc);
|
||||
if (!delaySlotIsBranch)
|
||||
if (!branchInfo.delaySlotIsBranch)
|
||||
CompileDelaySlot(DELAYSLOT_FLUSH);
|
||||
}
|
||||
js.inDelaySlot = false;
|
||||
|
||||
if (branchInfo.delaySlotIsBranch) {
|
||||
// We still link when the branch is taken (targetAddr case.)
|
||||
// Remember, it's from the perspective of the delay slot, so +12.
|
||||
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
|
||||
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
|
||||
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
|
||||
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
|
||||
FlushAll();
|
||||
}
|
||||
|
||||
// Take the branch
|
||||
WriteExit(targetAddr, js.nextExit++);
|
||||
|
||||
SetJumpTarget(ptr);
|
||||
// Not taken
|
||||
u32 notTakenTarget = GetCompilerPC() + (delaySlotIsBranch ? 4 : 8);
|
||||
WriteExit(notTakenTarget, js.nextExit++);
|
||||
WriteExit(ResolveNotTakenTarget(branchInfo), js.nextExit++);
|
||||
js.compiling = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
#define LOOPOPTIMIZATION 0
|
||||
|
||||
// We can disable nice delay slots.
|
||||
// #define CONDITIONAL_NICE_DELAYSLOT delaySlotIsNice = false;
|
||||
// #define CONDITIONAL_NICE_DELAYSLOT branchInfo.delaySlotIsNice = false;
|
||||
#define CONDITIONAL_NICE_DELAYSLOT ;
|
||||
|
||||
using namespace MIPSAnalyst;
|
||||
@@ -76,9 +76,13 @@ void Arm64Jit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
|
||||
MIPSGPReg rs = _RS;
|
||||
u32 targetAddr = GetCompilerPC() + offset + 4;
|
||||
|
||||
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
|
||||
branchInfo.delaySlotIsNice = IsDelaySlotNiceReg(op, branchInfo.delaySlotOp, rt, rs);
|
||||
CONDITIONAL_NICE_DELAYSLOT;
|
||||
|
||||
bool immBranch = false;
|
||||
bool immBranchTaken = false;
|
||||
if (gpr.IsImm(rs) && gpr.IsImm(rt)) {
|
||||
if (gpr.IsImm(rs) && gpr.IsImm(rt) && !branchInfo.delaySlotIsBranch) {
|
||||
// The cc flags are opposites: when NOT to take the branch.
|
||||
bool immBranchNotTaken;
|
||||
s32 rsImm = (s32)gpr.GetImm(rs);
|
||||
@@ -112,11 +116,9 @@ void Arm64Jit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
|
||||
return;
|
||||
}
|
||||
|
||||
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
|
||||
bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rt, rs);
|
||||
CONDITIONAL_NICE_DELAYSLOT;
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
|
||||
|
||||
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
|
||||
if (immBranch) {
|
||||
// Continuing is handled above, this is just static jumping.
|
||||
if (immBranchTaken || !likely)
|
||||
@@ -124,10 +126,10 @@ void Arm64Jit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
|
||||
else
|
||||
FlushAll();
|
||||
|
||||
const u32 destAddr = immBranchTaken ? targetAddr : GetCompilerPC() + 8;
|
||||
const u32 destAddr = immBranchTaken ? targetAddr : notTakenTarget;
|
||||
WriteExit(destAddr, js.nextExit++);
|
||||
} else {
|
||||
if (!likely && delaySlotIsNice)
|
||||
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
|
||||
// We might be able to flip the condition (EQ/NEQ are easy.)
|
||||
@@ -136,7 +138,7 @@ void Arm64Jit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
|
||||
const bool rtIsZero = gpr.IsImm(rt) && gpr.GetImm(rt) == 0;
|
||||
|
||||
Arm64Gen::FixupBranch ptr;
|
||||
if ((likely || delaySlotIsNice) && (rsIsZero || rtIsZero) && canFlip) {
|
||||
if ((likely || branchInfo.delaySlotIsNice) && (rsIsZero || rtIsZero) && canFlip) {
|
||||
// Special case, we can just use CBZ/CBNZ directly.
|
||||
MIPSGPReg r = rsIsZero ? rt : rs;
|
||||
gpr.MapReg(r);
|
||||
@@ -169,7 +171,7 @@ void Arm64Jit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
|
||||
}
|
||||
|
||||
if (!likely) {
|
||||
if (!delaySlotIsNice)
|
||||
if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
|
||||
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
|
||||
else
|
||||
FlushAll();
|
||||
@@ -180,17 +182,27 @@ void Arm64Jit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
|
||||
}
|
||||
}
|
||||
|
||||
if (likely) {
|
||||
if (likely && !branchInfo.delaySlotIsBranch) {
|
||||
// Only executed when taking the branch.
|
||||
CompileDelaySlot(DELAYSLOT_FLUSH);
|
||||
}
|
||||
|
||||
if (branchInfo.delaySlotIsBranch) {
|
||||
// We still link when the branch is taken (targetAddr case.)
|
||||
// Remember, it's from the perspective of the delay slot, so +12.
|
||||
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
|
||||
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
|
||||
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
|
||||
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
|
||||
FlushAll();
|
||||
}
|
||||
|
||||
// Take the branch
|
||||
WriteExit(targetAddr, js.nextExit++);
|
||||
|
||||
SetJumpTarget(ptr);
|
||||
// Not taken
|
||||
WriteExit(GetCompilerPC() + 8, js.nextExit++);
|
||||
WriteExit(notTakenTarget, js.nextExit++);
|
||||
}
|
||||
|
||||
js.compiling = false;
|
||||
@@ -207,9 +219,13 @@ void Arm64Jit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool li
|
||||
MIPSGPReg rs = _RS;
|
||||
u32 targetAddr = GetCompilerPC() + offset + 4;
|
||||
|
||||
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), andLink, likely);
|
||||
branchInfo.delaySlotIsNice = IsDelaySlotNiceReg(op, branchInfo.delaySlotOp, rs);
|
||||
CONDITIONAL_NICE_DELAYSLOT;
|
||||
|
||||
bool immBranch = false;
|
||||
bool immBranchTaken = false;
|
||||
if (gpr.IsImm(rs)) {
|
||||
if (gpr.IsImm(rs) && !branchInfo.delaySlotIsBranch) {
|
||||
// The cc flags are opposites: when NOT to take the branch.
|
||||
bool immBranchNotTaken;
|
||||
s32 imm = (s32)gpr.GetImm(rs);
|
||||
@@ -249,11 +265,9 @@ void Arm64Jit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool li
|
||||
return;
|
||||
}
|
||||
|
||||
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
|
||||
bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs);
|
||||
CONDITIONAL_NICE_DELAYSLOT;
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
|
||||
|
||||
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
|
||||
if (immBranch) {
|
||||
// Continuing is handled above, this is just static jumping.
|
||||
if (andLink)
|
||||
@@ -263,10 +277,10 @@ void Arm64Jit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool li
|
||||
else
|
||||
FlushAll();
|
||||
|
||||
const u32 destAddr = immBranchTaken ? targetAddr : GetCompilerPC() + 8;
|
||||
const u32 destAddr = immBranchTaken ? targetAddr : notTakenTarget;
|
||||
WriteExit(destAddr, js.nextExit++);
|
||||
} else {
|
||||
if (!likely && delaySlotIsNice)
|
||||
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
|
||||
gpr.MapReg(rs);
|
||||
@@ -278,7 +292,7 @@ void Arm64Jit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool li
|
||||
Arm64Gen::FixupBranch ptr;
|
||||
if (!likely)
|
||||
{
|
||||
if (!delaySlotIsNice)
|
||||
if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
|
||||
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
|
||||
else
|
||||
FlushAll();
|
||||
@@ -288,7 +302,18 @@ void Arm64Jit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool li
|
||||
{
|
||||
FlushAll();
|
||||
ptr = B(cc);
|
||||
CompileDelaySlot(DELAYSLOT_FLUSH);
|
||||
if (!branchInfo.delaySlotIsBranch)
|
||||
CompileDelaySlot(DELAYSLOT_FLUSH);
|
||||
}
|
||||
|
||||
if (branchInfo.delaySlotIsBranch) {
|
||||
// We still link when the branch is taken (targetAddr case.)
|
||||
// Remember, it's from the perspective of the delay slot, so +12.
|
||||
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
|
||||
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
|
||||
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
|
||||
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
|
||||
FlushAll();
|
||||
}
|
||||
|
||||
// Take the branch
|
||||
@@ -296,7 +321,7 @@ void Arm64Jit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool li
|
||||
|
||||
SetJumpTarget(ptr);
|
||||
// Not taken
|
||||
WriteExit(GetCompilerPC() + 8, js.nextExit++);
|
||||
WriteExit(notTakenTarget, js.nextExit++);
|
||||
}
|
||||
js.compiling = false;
|
||||
}
|
||||
@@ -352,16 +377,17 @@ void Arm64Jit::BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely) {
|
||||
int offset = TARGET16;
|
||||
u32 targetAddr = GetCompilerPC() + offset + 4;
|
||||
|
||||
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
|
||||
bool delaySlotIsNice = IsDelaySlotNiceFPU(op, delaySlotOp);
|
||||
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
|
||||
branchInfo.delaySlotIsNice = IsDelaySlotNiceFPU(op, branchInfo.delaySlotOp);
|
||||
CONDITIONAL_NICE_DELAYSLOT;
|
||||
if (!likely && delaySlotIsNice)
|
||||
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
|
||||
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
|
||||
gpr.MapReg(MIPS_REG_FPCOND);
|
||||
Arm64Gen::FixupBranch ptr;
|
||||
if (likely || delaySlotIsNice) {
|
||||
if (likely || branchInfo.delaySlotIsNice) {
|
||||
// FlushAll() won't actually change the reg.
|
||||
ARM64Reg ar = gpr.R(MIPS_REG_FPCOND);
|
||||
FlushAll();
|
||||
@@ -372,20 +398,31 @@ void Arm64Jit::BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely) {
|
||||
}
|
||||
} else {
|
||||
TSTI2R(gpr.R(MIPS_REG_FPCOND), 1, SCRATCH1);
|
||||
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
|
||||
if (!branchInfo.delaySlotIsBranch)
|
||||
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
|
||||
ptr = B(cc);
|
||||
}
|
||||
|
||||
if (likely) {
|
||||
if (likely && !branchInfo.delaySlotIsBranch) {
|
||||
CompileDelaySlot(DELAYSLOT_FLUSH);
|
||||
}
|
||||
|
||||
if (branchInfo.delaySlotIsBranch) {
|
||||
// We still link when the branch is taken (targetAddr case.)
|
||||
// Remember, it's from the perspective of the delay slot, so +12.
|
||||
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
|
||||
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
|
||||
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
|
||||
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
|
||||
FlushAll();
|
||||
}
|
||||
|
||||
// Take the branch
|
||||
WriteExit(targetAddr, js.nextExit++);
|
||||
|
||||
SetJumpTarget(ptr);
|
||||
// Not taken
|
||||
WriteExit(GetCompilerPC() + 8, js.nextExit++);
|
||||
WriteExit(ResolveNotTakenTarget(branchInfo), js.nextExit++);
|
||||
js.compiling = false;
|
||||
}
|
||||
|
||||
@@ -410,25 +447,22 @@ void Arm64Jit::BranchVFPUFlag(MIPSOpcode op, CCFlags cc, bool likely) {
|
||||
int offset = TARGET16;
|
||||
u32 targetAddr = GetCompilerPC() + offset + 4;
|
||||
|
||||
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
|
||||
|
||||
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
|
||||
// Sometimes there's a VFPU branch in a delay slot (Disgaea 2: Dark Hero Days, Zettai Hero Project, La Pucelle)
|
||||
// The behavior is undefined - the CPU may take the second branch even if the first one passes.
|
||||
// However, it does consistently try each branch, which these games seem to expect.
|
||||
bool delaySlotIsBranch = MIPSCodeUtils::IsVFPUBranch(delaySlotOp);
|
||||
bool delaySlotIsNice = !delaySlotIsBranch && IsDelaySlotNiceVFPU(op, delaySlotOp);
|
||||
branchInfo.delaySlotIsNice = IsDelaySlotNiceVFPU(op, branchInfo.delaySlotOp);
|
||||
CONDITIONAL_NICE_DELAYSLOT;
|
||||
if (!likely && delaySlotIsNice)
|
||||
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
|
||||
if (!likely && branchInfo.delaySlotIsNice)
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
if (delaySlotIsBranch && (signed short)(delaySlotOp & 0xFFFF) != (signed short)(op & 0xFFFF) - 1)
|
||||
ERROR_LOG_REPORT(JIT, "VFPU branch in VFPU delay slot at %08x with different target", GetCompilerPC());
|
||||
|
||||
int imm3 = (op >> 18) & 7;
|
||||
|
||||
gpr.MapReg(MIPS_REG_VFPUCC);
|
||||
Arm64Gen::FixupBranch ptr;
|
||||
if (likely || delaySlotIsNice || delaySlotIsBranch) {
|
||||
if (likely || branchInfo.delaySlotIsNice || branchInfo.delaySlotIsBranch) {
|
||||
// FlushAll() won't actually change the reg.
|
||||
ARM64Reg ar = gpr.R(MIPS_REG_VFPUCC);
|
||||
FlushAll();
|
||||
@@ -439,21 +473,31 @@ void Arm64Jit::BranchVFPUFlag(MIPSOpcode op, CCFlags cc, bool likely) {
|
||||
}
|
||||
} else {
|
||||
TSTI2R(gpr.R(MIPS_REG_VFPUCC), 1ULL << imm3, SCRATCH1);
|
||||
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
|
||||
if (!branchInfo.delaySlotIsBranch)
|
||||
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
|
||||
ptr = B(cc);
|
||||
}
|
||||
|
||||
if (likely && !delaySlotIsBranch) {
|
||||
if (likely && !branchInfo.delaySlotIsBranch) {
|
||||
CompileDelaySlot(DELAYSLOT_FLUSH);
|
||||
}
|
||||
|
||||
if (branchInfo.delaySlotIsBranch) {
|
||||
// We still link when the branch is taken (targetAddr case.)
|
||||
// Remember, it's from the perspective of the delay slot, so +12.
|
||||
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
|
||||
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
|
||||
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
|
||||
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
|
||||
FlushAll();
|
||||
}
|
||||
|
||||
// Take the branch
|
||||
WriteExit(targetAddr, js.nextExit++);
|
||||
|
||||
SetJumpTarget(ptr);
|
||||
// Not taken
|
||||
u32 notTakenTarget = GetCompilerPC() + (delaySlotIsBranch ? 4 : 8);
|
||||
WriteExit(notTakenTarget, js.nextExit++);
|
||||
WriteExit(ResolveNotTakenTarget(branchInfo), js.nextExit++);
|
||||
js.compiling = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -548,8 +548,9 @@ void Jit::BranchFPFlag(MIPSOpcode op, Gen::CCFlags cc, bool likely)
|
||||
|
||||
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
|
||||
branchInfo.delaySlotIsNice = IsDelaySlotNiceFPU(op, branchInfo.delaySlotOp);
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
|
||||
CONDITIONAL_NICE_DELAYSLOT;
|
||||
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
|
||||
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
|
||||
@@ -592,10 +593,10 @@ void Jit::BranchVFPUFlag(MIPSOpcode op, Gen::CCFlags cc, bool likely)
|
||||
// Sometimes there's a VFPU branch in a delay slot (Disgaea 2: Dark Hero Days, Zettai Hero Project, La Pucelle)
|
||||
// The behavior is undefined - the CPU may take the second branch even if the first one passes.
|
||||
// However, it does consistently try each branch, which these games seem to expect.
|
||||
branchInfo.delaySlotIsNice = !branchInfo.delaySlotIsBranch && IsDelaySlotNiceVFPU(op, branchInfo.delaySlotOp);
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
|
||||
|
||||
branchInfo.delaySlotIsNice = IsDelaySlotNiceVFPU(op, branchInfo.delaySlotOp);
|
||||
CONDITIONAL_NICE_DELAYSLOT;
|
||||
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
|
||||
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
|
||||
|
||||
Reference in new issue
Block a user