arm64jit: Handle branch/jump in branch delay slots.

This commit is contained in:
Unknown W. Brackets committed 2022-09-03 21:04:54 -07:00
1 parent bac36df453
commit f75dadd1d6
3 files changed
+176 -87

No files matched your search

+84 -40
View File
@@ -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;
}
+87 -43
View File
@@ -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;
}
+5 -4
View File
@@ -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);