mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-13 09:39:33 +01:00
Merge pull request #4516 from unknownbrackets/armjit-minor
Improve spilling regs on ARM
This commit is contained in:
commit
e111034b53
4 files changed
+171
-294
No files matched your search
@@ -15,9 +15,11 @@
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "ArmRegCache.h"
|
||||
#include "ArmEmitter.h"
|
||||
#include "ArmJit.h"
|
||||
#include "Core/MIPS/ARM/ArmRegCache.h"
|
||||
#include "Core/MIPS/ARM/ArmJit.h"
|
||||
#include "Core/MIPS/MIPSAnalyst.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Common/ArmEmitter.h"
|
||||
|
||||
#if defined(MAEMO)
|
||||
#include "stddef.h"
|
||||
@@ -185,6 +187,38 @@ void ArmRegCache::MapRegTo(ARMReg reg, MIPSGPReg mipsReg, int mapFlags) {
|
||||
mr[mipsReg].reg = reg;
|
||||
}
|
||||
|
||||
ARMReg ArmRegCache::FindBestToSpill(bool unusedOnly) {
|
||||
int allocCount;
|
||||
const ARMReg *allocOrder = GetMIPSAllocationOrder(allocCount);
|
||||
|
||||
static const int UNUSED_LOOKAHEAD_OPS = 3;
|
||||
|
||||
for (int i = 0; i < allocCount; i++) {
|
||||
ARMReg reg = allocOrder[i];
|
||||
if (ar[reg].mipsReg != MIPS_REG_INVALID && mr[ar[reg].mipsReg].spillLock)
|
||||
continue;
|
||||
|
||||
if (unusedOnly) {
|
||||
bool unused = true;
|
||||
for (int ahead = 1; ahead <= UNUSED_LOOKAHEAD_OPS; ++ahead) {
|
||||
MIPSOpcode laterOp = Memory::Read_Instruction(compilerPC_ + ahead * sizeof(u32));
|
||||
// If read, it might need to be mapped again. If output, it might not need to be stored.
|
||||
if (MIPSAnalyst::ReadsFromGPReg(laterOp, ar[reg].mipsReg) || MIPSAnalyst::GetOutGPReg(laterOp) == ar[reg].mipsReg) {
|
||||
unused = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!unused) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return reg;
|
||||
}
|
||||
|
||||
return INVALID_REG;
|
||||
}
|
||||
|
||||
// TODO: Somewhat smarter spilling - currently simply spills the first available, should do
|
||||
// round robin or FIFO or something.
|
||||
ARMReg ArmRegCache::MapReg(MIPSGPReg mipsReg, int mapFlags) {
|
||||
@@ -194,7 +228,7 @@ ARMReg ArmRegCache::MapReg(MIPSGPReg mipsReg, int mapFlags) {
|
||||
if (mr[mipsReg].loc == ML_ARMREG || mr[mipsReg].loc == ML_ARMREG_IMM) {
|
||||
ARMReg armReg = mr[mipsReg].reg;
|
||||
if (ar[armReg].mipsReg != mipsReg) {
|
||||
ERROR_LOG(JIT, "Register mapping out of sync! %i", mipsReg);
|
||||
ERROR_LOG_REPORT(JIT, "Register mapping out of sync! %i", mipsReg);
|
||||
}
|
||||
if (mapFlags & MAP_DIRTY) {
|
||||
// Mapping dirty means the old imm value is invalid.
|
||||
@@ -250,13 +284,9 @@ allocate:
|
||||
// Still nothing. Let's spill a reg and goto 10.
|
||||
// TODO: Use age or something to choose which register to spill?
|
||||
// TODO: Spill dirty regs first? or opposite?
|
||||
ARMReg bestToSpill = INVALID_REG;
|
||||
for (int i = 0; i < allocCount; i++) {
|
||||
ARMReg reg = allocOrder[i];
|
||||
if (ar[reg].mipsReg != MIPS_REG_INVALID && mr[ar[reg].mipsReg].spillLock)
|
||||
continue;
|
||||
bestToSpill = reg;
|
||||
break;
|
||||
ARMReg bestToSpill = FindBestToSpill(true);
|
||||
if (bestToSpill == INVALID_REG) {
|
||||
bestToSpill = FindBestToSpill(false);
|
||||
}
|
||||
|
||||
if (bestToSpill != INVALID_REG) {
|
||||
@@ -266,7 +296,7 @@ allocate:
|
||||
}
|
||||
|
||||
// Uh oh, we have all them spilllocked....
|
||||
ERROR_LOG(JIT, "Out of spillable registers at PC %08x!!!", mips_->pc);
|
||||
ERROR_LOG_REPORT(JIT, "Out of spillable registers at PC %08x!!!", mips_->pc);
|
||||
return INVALID_REG;
|
||||
}
|
||||
|
||||
@@ -318,17 +348,24 @@ void ArmRegCache::MapDirtyDirtyInIn(MIPSGPReg rd1, MIPSGPReg rd2, MIPSGPReg rs,
|
||||
void ArmRegCache::FlushArmReg(ARMReg r) {
|
||||
if (ar[r].mipsReg == MIPS_REG_INVALID) {
|
||||
// Nothing to do, reg not mapped.
|
||||
if (ar[r].isDirty) {
|
||||
ERROR_LOG_REPORT(JIT, "Dirty but no mipsreg?");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (ar[r].mipsReg != MIPS_REG_INVALID) {
|
||||
if (ar[r].isDirty && (mr[ar[r].mipsReg].loc == ML_ARMREG || mr[ar[r].mipsReg].loc == ML_ARMREG_IMM))
|
||||
emit_->STR(r, CTXREG, GetMipsRegOffset(ar[r].mipsReg));
|
||||
// IMMs won't be in an ARM reg.
|
||||
mr[ar[r].mipsReg].loc = ML_MEM;
|
||||
mr[ar[r].mipsReg].reg = INVALID_REG;
|
||||
mr[ar[r].mipsReg].imm = 0;
|
||||
} else {
|
||||
ERROR_LOG(JIT, "Dirty but no mipsreg?");
|
||||
auto &mreg = mr[ar[r].mipsReg];
|
||||
if (mreg.loc == ML_ARMREG_IMM) {
|
||||
// We know it's immedate value, no need to STR now.
|
||||
mreg.loc = ML_IMM;
|
||||
mreg.reg = INVALID_REG;
|
||||
} else {
|
||||
if (ar[r].isDirty && mreg.loc == ML_ARMREG)
|
||||
emit_->STR(r, CTXREG, GetMipsRegOffset(ar[r].mipsReg));
|
||||
mreg.loc = ML_MEM;
|
||||
mreg.reg = INVALID_REG;
|
||||
mreg.imm = 0;
|
||||
}
|
||||
}
|
||||
ar[r].isDirty = false;
|
||||
ar[r].mipsReg = MIPS_REG_INVALID;
|
||||
@@ -359,7 +396,7 @@ void ArmRegCache::FlushR(MIPSGPReg r) {
|
||||
case ML_ARMREG:
|
||||
case ML_ARMREG_IMM:
|
||||
if (mr[r].reg == INVALID_REG) {
|
||||
ERROR_LOG(JIT, "FlushR: MipsReg %d had bad ArmReg", r);
|
||||
ERROR_LOG_REPORT(JIT, "FlushR: MipsReg %d had bad ArmReg", r);
|
||||
}
|
||||
if (ar[mr[r].reg].isDirty) {
|
||||
if (r != MIPS_REG_ZERO) {
|
||||
@@ -373,7 +410,7 @@ void ArmRegCache::FlushR(MIPSGPReg r) {
|
||||
case ML_ARMREG_AS_PTR:
|
||||
// Never dirty.
|
||||
if (ar[mr[r].reg].isDirty) {
|
||||
ERROR_LOG(JIT, "ARMREG_AS_PTR cannot be dirty (yet)");
|
||||
ERROR_LOG_REPORT(JIT, "ARMREG_AS_PTR cannot be dirty (yet)");
|
||||
}
|
||||
ar[mr[r].reg].mipsReg = MIPS_REG_INVALID;
|
||||
break;
|
||||
@@ -383,7 +420,7 @@ void ArmRegCache::FlushR(MIPSGPReg r) {
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_LOG(JIT, "FlushR: MipsReg %d with invalid location %d", r, mr[r].loc);
|
||||
ERROR_LOG_REPORT(JIT, "FlushR: MipsReg %d with invalid location %d", r, mr[r].loc);
|
||||
break;
|
||||
}
|
||||
mr[r].loc = ML_MEM;
|
||||
@@ -488,7 +525,7 @@ void ArmRegCache::FlushAll() {
|
||||
// Sanity check
|
||||
for (int i = 0; i < NUM_ARMREG; i++) {
|
||||
if (ar[i].mipsReg != MIPS_REG_INVALID) {
|
||||
ERROR_LOG(JIT, "Flush fail: ar[%i].mipsReg=%i", i, ar[i].mipsReg);
|
||||
ERROR_LOG_REPORT(JIT, "Flush fail: ar[%i].mipsReg=%i", i, ar[i].mipsReg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -519,7 +556,7 @@ bool ArmRegCache::IsImm(MIPSGPReg r) const {
|
||||
u32 ArmRegCache::GetImm(MIPSGPReg r) const {
|
||||
if (r == MIPS_REG_ZERO) return 0;
|
||||
if (mr[r].loc != ML_IMM && mr[r].loc != ML_ARMREG_IMM) {
|
||||
ERROR_LOG(JIT, "Trying to get imm from non-imm register %i", r);
|
||||
ERROR_LOG_REPORT(JIT, "Trying to get imm from non-imm register %i", r);
|
||||
}
|
||||
return mr[r].imm;
|
||||
}
|
||||
@@ -533,7 +570,7 @@ int ArmRegCache::GetMipsRegOffset(MIPSGPReg r) {
|
||||
case MIPS_REG_LO:
|
||||
return offsetof(MIPSState, lo);
|
||||
default:
|
||||
ERROR_LOG(JIT, "bad mips register %i", r);
|
||||
ERROR_LOG_REPORT(JIT, "bad mips register %i", r);
|
||||
return 0; // or what?
|
||||
}
|
||||
}
|
||||
@@ -559,7 +596,7 @@ ARMReg ArmRegCache::R(MIPSGPReg mipsReg) {
|
||||
if (mr[mipsReg].loc == ML_ARMREG || mr[mipsReg].loc == ML_ARMREG_IMM) {
|
||||
return (ARMReg)mr[mipsReg].reg;
|
||||
} else {
|
||||
ERROR_LOG(JIT, "Reg %i not in arm reg. compilerPC = %08x", mipsReg, compilerPC_);
|
||||
ERROR_LOG_REPORT(JIT, "Reg %i not in arm reg. compilerPC = %08x", mipsReg, compilerPC_);
|
||||
return INVALID_REG; // BAAAD
|
||||
}
|
||||
}
|
||||
@@ -568,7 +605,7 @@ ARMReg ArmRegCache::RPtr(MIPSGPReg mipsReg) {
|
||||
if (mr[mipsReg].loc == ML_ARMREG_AS_PTR) {
|
||||
return (ARMReg)mr[mipsReg].reg;
|
||||
} else {
|
||||
ERROR_LOG(JIT, "Reg %i not in arm reg as pointer. compilerPC = %08x", mipsReg, compilerPC_);
|
||||
ERROR_LOG_REPORT(JIT, "Reg %i not in arm reg as pointer. compilerPC = %08x", mipsReg, compilerPC_);
|
||||
return INVALID_REG; // BAAAD
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,6 +124,7 @@ private:
|
||||
const ARMReg *GetMIPSAllocationOrder(int &count);
|
||||
void MapRegTo(ARMReg reg, MIPSGPReg mipsReg, int mapFlags);
|
||||
int FlushGetSequential(MIPSGPReg startMipsReg, bool allowFlushImm);
|
||||
ARMReg FindBestToSpill(bool unusedOnly);
|
||||
|
||||
MIPSState *mips_;
|
||||
MIPSComp::ArmJitOptions *options_;
|
||||
|
||||
+104
-266
@@ -165,6 +165,106 @@ bool Jit::PredictTakeBranch(u32 targetAddr, bool likely) {
|
||||
return targetAddr > js.compilerPC;
|
||||
}
|
||||
|
||||
void Jit::CompBranchExits(CCFlags cc, u32 targetAddr, u32 notTakenAddr, bool delaySlotIsNice, bool likely, bool andLink) {
|
||||
// We may want to try to continue along this branch a little while, to reduce reg flushing.
|
||||
if (CanContinueBranch())
|
||||
{
|
||||
bool predictTakeBranch = PredictTakeBranch(targetAddr, likely);
|
||||
if (predictTakeBranch)
|
||||
cc = FlipCCFlag(cc);
|
||||
|
||||
Gen::FixupBranch ptr;
|
||||
RegCacheState state;
|
||||
if (!likely)
|
||||
{
|
||||
if (!delaySlotIsNice)
|
||||
CompileDelaySlot(DELAYSLOT_SAFE);
|
||||
ptr = J_CC(cc, true);
|
||||
GetStateAndFlushAll(state);
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = J_CC(cc, true);
|
||||
if (predictTakeBranch)
|
||||
GetStateAndFlushAll(state);
|
||||
else
|
||||
CompileDelaySlot(DELAYSLOT_FLUSH);
|
||||
}
|
||||
|
||||
if (predictTakeBranch)
|
||||
{
|
||||
// We flipped the cc, the not taken case is first.
|
||||
CONDITIONAL_LOG_EXIT(notTakenAddr);
|
||||
WriteExit(notTakenAddr, js.nextExit++);
|
||||
|
||||
// Now our taken path. Bring the regs back, we didn't flush 'em after all.
|
||||
SetJumpTarget(ptr);
|
||||
RestoreState(state);
|
||||
CONDITIONAL_LOG_EXIT(targetAddr);
|
||||
|
||||
if (andLink)
|
||||
gpr.SetImm(MIPS_REG_RA, js.compilerPC + 8);
|
||||
|
||||
// Don't forget to run the delay slot if likely.
|
||||
if (likely)
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
|
||||
// Account for the increment in the loop.
|
||||
js.compilerPC = targetAddr - 4;
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Take the branch
|
||||
if (andLink)
|
||||
MOV(32, M(&mips_->r[MIPS_REG_RA]), Imm32(js.compilerPC + 8));
|
||||
CONDITIONAL_LOG_EXIT(targetAddr);
|
||||
WriteExit(targetAddr, js.nextExit++);
|
||||
|
||||
// Not taken
|
||||
SetJumpTarget(ptr);
|
||||
RestoreState(state);
|
||||
CONDITIONAL_LOG_EXIT(notTakenAddr);
|
||||
|
||||
// Account for the delay slot.
|
||||
js.compilerPC += 4;
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Gen::FixupBranch ptr;
|
||||
if (!likely)
|
||||
{
|
||||
if (!delaySlotIsNice)
|
||||
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
|
||||
else
|
||||
FlushAll();
|
||||
ptr = J_CC(cc, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
FlushAll();
|
||||
ptr = J_CC(cc, true);
|
||||
CompileDelaySlot(DELAYSLOT_FLUSH);
|
||||
}
|
||||
|
||||
// Take the branch
|
||||
if (andLink)
|
||||
MOV(32, M(&mips_->r[MIPS_REG_RA]), Imm32(js.compilerPC + 8));
|
||||
CONDITIONAL_LOG_EXIT(targetAddr);
|
||||
WriteExit(targetAddr, js.nextExit++);
|
||||
|
||||
// Not taken
|
||||
SetJumpTarget(ptr);
|
||||
CONDITIONAL_LOG_EXIT(notTakenAddr);
|
||||
WriteExit(notTakenAddr, js.nextExit++);
|
||||
js.compiling = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Jit::BranchRSRTComp(MIPSOpcode op, Gen::CCFlags cc, bool likely)
|
||||
{
|
||||
CONDITIONAL_LOG;
|
||||
@@ -225,71 +325,7 @@ void Jit::BranchRSRTComp(MIPSOpcode op, Gen::CCFlags cc, bool likely)
|
||||
CMP(32, gpr.R(rs), gpr.R(rt));
|
||||
}
|
||||
|
||||
// We may want to try to continue along this branch a little while, to reduce reg flushing.
|
||||
bool predictTakeBranch = PredictTakeBranch(targetAddr, likely);
|
||||
bool continueBranch = false;
|
||||
// Likely delay slots may change regs, can't take those branches inline safely.
|
||||
if (CanContinueBranch() && (!likely || !predictTakeBranch))
|
||||
{
|
||||
continueBranch = true;
|
||||
if (predictTakeBranch)
|
||||
cc = FlipCCFlag(cc);
|
||||
}
|
||||
|
||||
Gen::FixupBranch ptr;
|
||||
RegCacheState state;
|
||||
if (!likely)
|
||||
{
|
||||
if (!delaySlotIsNice)
|
||||
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH, state);
|
||||
else
|
||||
GetStateAndFlushAll(state);
|
||||
ptr = J_CC(cc, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetStateAndFlushAll(state);
|
||||
ptr = J_CC(cc, true);
|
||||
CompileDelaySlot(DELAYSLOT_FLUSH);
|
||||
}
|
||||
|
||||
if (continueBranch && predictTakeBranch)
|
||||
{
|
||||
// We flipped the cc, the not taken case is first.
|
||||
CONDITIONAL_LOG_EXIT(js.compilerPC + 8);
|
||||
WriteExit(js.compilerPC + 8, js.nextExit++);
|
||||
|
||||
// Now our taken path.
|
||||
SetJumpTarget(ptr);
|
||||
CONDITIONAL_LOG_EXIT(targetAddr);
|
||||
// Account for the increment in the loop.
|
||||
js.compilerPC = targetAddr - 4;
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Take the branch
|
||||
CONDITIONAL_LOG_EXIT(targetAddr);
|
||||
WriteExit(targetAddr, js.nextExit++);
|
||||
|
||||
// Not taken
|
||||
SetJumpTarget(ptr);
|
||||
CONDITIONAL_LOG_EXIT(js.compilerPC + 8);
|
||||
|
||||
if (continueBranch && !predictTakeBranch)
|
||||
{
|
||||
// Account for the delay slot.
|
||||
js.compilerPC += 4;
|
||||
RestoreState(state);
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteExit(js.compilerPC + 8, js.nextExit++);
|
||||
js.compiling = false;
|
||||
}
|
||||
CompBranchExits(cc, targetAddr, js.compilerPC + 8, delaySlotIsNice, likely, false);
|
||||
}
|
||||
|
||||
void Jit::BranchRSZeroComp(MIPSOpcode op, Gen::CCFlags cc, bool andLink, bool likely)
|
||||
@@ -347,75 +383,7 @@ void Jit::BranchRSZeroComp(MIPSOpcode op, Gen::CCFlags cc, bool andLink, bool li
|
||||
gpr.MapReg(rs, true, false);
|
||||
CMP(32, gpr.R(rs), Imm32(0));
|
||||
|
||||
// We may want to try to continue along this branch a little while, to reduce reg flushing.
|
||||
bool predictTakeBranch = PredictTakeBranch(targetAddr, likely);
|
||||
bool continueBranch = false;
|
||||
// Likely delay slots may change regs, can't take those branches inline safely.
|
||||
if (CanContinueBranch() && (!likely || !predictTakeBranch))
|
||||
{
|
||||
continueBranch = true;
|
||||
if (predictTakeBranch)
|
||||
cc = FlipCCFlag(cc);
|
||||
}
|
||||
|
||||
Gen::FixupBranch ptr;
|
||||
RegCacheState state;
|
||||
if (!likely)
|
||||
{
|
||||
if (!delaySlotIsNice)
|
||||
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH, state);
|
||||
else
|
||||
GetStateAndFlushAll(state);
|
||||
ptr = J_CC(cc, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetStateAndFlushAll(state);
|
||||
ptr = J_CC(cc, true);
|
||||
CompileDelaySlot(DELAYSLOT_FLUSH);
|
||||
}
|
||||
|
||||
if (continueBranch && predictTakeBranch)
|
||||
{
|
||||
// We flipped the cc, the not taken case is first.
|
||||
CONDITIONAL_LOG_EXIT(js.compilerPC + 8);
|
||||
WriteExit(js.compilerPC + 8, js.nextExit++);
|
||||
|
||||
// Now our taken path.
|
||||
SetJumpTarget(ptr);
|
||||
if (andLink)
|
||||
gpr.SetImm(MIPS_REG_RA, js.compilerPC + 8);
|
||||
CONDITIONAL_LOG_EXIT(targetAddr);
|
||||
// Account for the increment in the loop.
|
||||
js.compilerPC = targetAddr - 4;
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Take the branch
|
||||
if (andLink)
|
||||
MOV(32, M(&mips_->r[MIPS_REG_RA]), Imm32(js.compilerPC + 8));
|
||||
CONDITIONAL_LOG_EXIT(targetAddr);
|
||||
WriteExit(targetAddr, js.nextExit++);
|
||||
|
||||
// Not taken
|
||||
SetJumpTarget(ptr);
|
||||
CONDITIONAL_LOG_EXIT(js.compilerPC + 8);
|
||||
|
||||
if (continueBranch && !predictTakeBranch)
|
||||
{
|
||||
// Account for the delay slot.
|
||||
js.compilerPC += 4;
|
||||
RestoreState(state);
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteExit(js.compilerPC + 8, js.nextExit++);
|
||||
js.compiling = false;
|
||||
}
|
||||
CompBranchExits(cc, targetAddr, js.compilerPC + 8, delaySlotIsNice, likely, andLink);
|
||||
}
|
||||
|
||||
|
||||
@@ -477,72 +445,9 @@ void Jit::BranchFPFlag(MIPSOpcode op, Gen::CCFlags cc, bool likely)
|
||||
if (!likely && delaySlotIsNice)
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
|
||||
// We may want to try to continue along this branch a little while, to reduce reg flushing.
|
||||
bool predictTakeBranch = PredictTakeBranch(targetAddr, likely);
|
||||
bool continueBranch = false;
|
||||
// Likely delay slots may change regs, can't take those branches inline safely.
|
||||
if (CanContinueBranch() && (!likely || !predictTakeBranch))
|
||||
{
|
||||
continueBranch = true;
|
||||
if (predictTakeBranch)
|
||||
cc = FlipCCFlag(cc);
|
||||
}
|
||||
|
||||
TEST(32, M((void *)&(mips_->fpcond)), Imm32(1));
|
||||
Gen::FixupBranch ptr;
|
||||
RegCacheState state;
|
||||
if (!likely)
|
||||
{
|
||||
if (!delaySlotIsNice)
|
||||
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH, state);
|
||||
else
|
||||
GetStateAndFlushAll(state);
|
||||
ptr = J_CC(cc, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetStateAndFlushAll(state);
|
||||
ptr = J_CC(cc, true);
|
||||
CompileDelaySlot(DELAYSLOT_FLUSH);
|
||||
}
|
||||
|
||||
if (continueBranch && predictTakeBranch)
|
||||
{
|
||||
// We flipped the cc, the not taken case is first.
|
||||
CONDITIONAL_LOG_EXIT(js.compilerPC + 8);
|
||||
WriteExit(js.compilerPC + 8, js.nextExit++);
|
||||
|
||||
// Now our taken path.
|
||||
SetJumpTarget(ptr);
|
||||
CONDITIONAL_LOG_EXIT(targetAddr);
|
||||
// Account for the increment in the loop.
|
||||
js.compilerPC = targetAddr - 4;
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Take the branch
|
||||
CONDITIONAL_LOG_EXIT(targetAddr);
|
||||
WriteExit(targetAddr, js.nextExit++);
|
||||
|
||||
// Not taken
|
||||
SetJumpTarget(ptr);
|
||||
CONDITIONAL_LOG_EXIT(js.compilerPC + 8);
|
||||
|
||||
if (continueBranch && !predictTakeBranch)
|
||||
{
|
||||
// Account for the delay slot.
|
||||
js.compilerPC += 4;
|
||||
RestoreState(state);
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteExit(js.compilerPC + 8, js.nextExit++);
|
||||
js.compiling = false;
|
||||
}
|
||||
CompBranchExits(cc, targetAddr, js.compilerPC + 8, delaySlotIsNice, likely, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -584,81 +489,14 @@ void Jit::BranchVFPUFlag(MIPSOpcode op, Gen::CCFlags cc, bool likely)
|
||||
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 %d / %d", js.compilerPC, (signed short)(delaySlotOp & 0xFFFF), (signed short)(op & 0xFFFF) - 1);
|
||||
|
||||
// We may want to try to continue along this branch a little while, to reduce reg flushing.
|
||||
bool predictTakeBranch = PredictTakeBranch(targetAddr, likely);
|
||||
bool continueBranch = false;
|
||||
// Likely delay slots may change regs, can't take those branches inline safely.
|
||||
// TODO: Maybe delaySlotIsBranch could work, but let's play it safe.
|
||||
if (CanContinueBranch() && (!likely || !predictTakeBranch) && !delaySlotIsBranch)
|
||||
{
|
||||
continueBranch = true;
|
||||
if (predictTakeBranch)
|
||||
cc = FlipCCFlag(cc);
|
||||
}
|
||||
|
||||
// THE CONDITION
|
||||
int imm3 = (op >> 18) & 7;
|
||||
|
||||
//int val = (mips_->vfpuCtrl[VFPU_CTRL_CC] >> imm3) & 1;
|
||||
TEST(32, M((void *)&(mips_->vfpuCtrl[VFPU_CTRL_CC])), Imm32(1 << imm3));
|
||||
Gen::FixupBranch ptr;
|
||||
RegCacheState state;
|
||||
if (!likely)
|
||||
{
|
||||
if (!delaySlotIsNice && !delaySlotIsBranch)
|
||||
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH, state);
|
||||
else
|
||||
GetStateAndFlushAll(state);
|
||||
ptr = J_CC(cc, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetStateAndFlushAll(state);
|
||||
ptr = J_CC(cc, true);
|
||||
if (!delaySlotIsBranch)
|
||||
CompileDelaySlot(DELAYSLOT_FLUSH);
|
||||
}
|
||||
|
||||
u32 notTakenTarget = js.compilerPC + (delaySlotIsBranch ? 4 : 8);
|
||||
|
||||
if (continueBranch && predictTakeBranch)
|
||||
{
|
||||
// We flipped the cc, the not taken case is first.
|
||||
CONDITIONAL_LOG_EXIT(notTakenTarget);
|
||||
WriteExit(notTakenTarget, js.nextExit++);
|
||||
|
||||
// Now our taken path.
|
||||
SetJumpTarget(ptr);
|
||||
CONDITIONAL_LOG_EXIT(targetAddr);
|
||||
// Account for the increment in the loop.
|
||||
js.compilerPC = targetAddr - 4;
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Take the branch
|
||||
CONDITIONAL_LOG_EXIT(targetAddr);
|
||||
WriteExit(targetAddr, js.nextExit++);
|
||||
|
||||
// Not taken
|
||||
SetJumpTarget(ptr);
|
||||
CONDITIONAL_LOG_EXIT(notTakenTarget);
|
||||
|
||||
if (continueBranch && !predictTakeBranch)
|
||||
{
|
||||
// Account for the delay slot.
|
||||
if (!delaySlotIsBranch)
|
||||
js.compilerPC += 4;
|
||||
RestoreState(state);
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteExit(notTakenTarget, js.nextExit++);
|
||||
js.compiling = false;
|
||||
}
|
||||
CompBranchExits(cc, targetAddr, notTakenTarget, delaySlotIsNice, likely, false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -196,6 +196,7 @@ private:
|
||||
void CompITypeMemWrite(MIPSOpcode op, u32 bits, void *safeFunc);
|
||||
void CompITypeMemUnpairedLR(MIPSOpcode op, bool isStore);
|
||||
void CompITypeMemUnpairedLRInner(MIPSOpcode op, X64Reg shiftReg);
|
||||
void CompBranchExits(CCFlags cc, u32 targetAddr, u32 notTakenAddr, bool delaySlotIsNice, bool likely, bool andLink);
|
||||
|
||||
void CompFPTriArith(MIPSOpcode op, void (XEmitter::*arith)(X64Reg reg, OpArg), bool orderMatters);
|
||||
void CompFPComp(int lhs, int rhs, u8 compare, bool allowNaN = false);
|
||||
|
||||
Reference in new issue
Block a user