Merge pull request #746 from FioraAeterna/fastermtcrf

Improve performance of CR functions in JIT64
This commit is contained in:
Dolphin Bot 2014-08-19 11:10:18 +02:00
commit 961c1db116
2 changed files with 100 additions and 197 deletions

View File

@ -105,8 +105,8 @@ public:
// Reads a given bit of a given CR register part. Clobbers ABI_PARAM1, // Reads a given bit of a given CR register part. Clobbers ABI_PARAM1,
// don't forget to xlock it before. // don't forget to xlock it before.
void GetCRFieldBit(int field, int bit, Gen::X64Reg out); void GetCRFieldBit(int field, int bit, Gen::X64Reg out, bool negate = false);
// Clobbers ABI_PARAM1 and ABI_PARAM2, xlock them before. // Clobbers ABI_PARAM1, xlock it before.
void SetCRFieldBit(int field, int bit, Gen::X64Reg in); void SetCRFieldBit(int field, int bit, Gen::X64Reg in);
// Generates a branch that will check if a given bit of a CR register part // Generates a branch that will check if a given bit of a CR register part

View File

@ -11,31 +11,28 @@
using namespace Gen; using namespace Gen;
void Jit64::GetCRFieldBit(int field, int bit, Gen::X64Reg out) void Jit64::GetCRFieldBit(int field, int bit, Gen::X64Reg out, bool negate)
{ {
switch (bit) switch (bit)
{ {
case CR_SO_BIT: // check bit 61 set case CR_SO_BIT: // check bit 61 set
MOV(64, R(ABI_PARAM1), Imm64(1ull << 61)); BT(64, M(&PowerPC::ppcState.cr_val[field]), Imm8(61));
TEST(64, M(&PowerPC::ppcState.cr_val[field]), R(ABI_PARAM1)); SETcc(negate ? CC_NC : CC_C, R(out));
SETcc(CC_NZ, R(out));
break; break;
case CR_EQ_BIT: // check bits 31-0 == 0 case CR_EQ_BIT: // check bits 31-0 == 0
CMP(32, M(&PowerPC::ppcState.cr_val[field]), Imm32(0)); CMP(32, M(&PowerPC::ppcState.cr_val[field]), Imm8(0));
SETcc(CC_Z, R(out)); SETcc(negate ? CC_NZ : CC_Z, R(out));
break; break;
case CR_GT_BIT: // check val > 0 case CR_GT_BIT: // check val > 0
MOV(64, R(ABI_PARAM1), M(&PowerPC::ppcState.cr_val[field])); CMP(64, M(&PowerPC::ppcState.cr_val[field]), Imm8(0));
TEST(64, R(ABI_PARAM1), R(ABI_PARAM1)); SETcc(negate ? CC_NG : CC_G, R(out));
SETcc(CC_G, R(out));
break; break;
case CR_LT_BIT: // check bit 62 set case CR_LT_BIT: // check bit 62 set
MOV(64, R(ABI_PARAM1), Imm64(1ull << 62)); BT(64, M(&PowerPC::ppcState.cr_val[field]), Imm8(62));
TEST(64, M(&PowerPC::ppcState.cr_val[field]), R(ABI_PARAM1)); SETcc(negate ? CC_NC : CC_C, R(out));
SETcc(CC_NZ, R(out));
break; break;
default: default:
@ -45,63 +42,40 @@ void Jit64::GetCRFieldBit(int field, int bit, Gen::X64Reg out)
void Jit64::SetCRFieldBit(int field, int bit, Gen::X64Reg in) void Jit64::SetCRFieldBit(int field, int bit, Gen::X64Reg in)
{ {
MOV(64, R(ABI_PARAM2), M(&PowerPC::ppcState.cr_val[field])); MOV(64, R(ABI_PARAM1), M(&PowerPC::ppcState.cr_val[field]));
TEST(8, R(in), Imm8(1)); MOVZX(32, 8, in, R(in));
FixupBranch input_is_set = J_CC(CC_NZ, false);
// New value is 0.
switch (bit)
{
case CR_SO_BIT: // unset bit 61
MOV(64, R(ABI_PARAM1), Imm64(~(1ull << 61)));
AND(64, R(ABI_PARAM2), R(ABI_PARAM1));
break;
case CR_EQ_BIT: // set bit 0 to 1
OR(8, R(ABI_PARAM2), Imm8(1));
break;
case CR_GT_BIT: // !GT, set bit 63
MOV(64, R(ABI_PARAM1), Imm64(1ull << 63));
OR(64, R(ABI_PARAM2), R(ABI_PARAM1));
break;
case CR_LT_BIT: // !LT, unset bit 62
MOV(64, R(ABI_PARAM1), Imm64(~(1ull << 62)));
AND(64, R(ABI_PARAM2), R(ABI_PARAM1));
break;
}
FixupBranch end = J();
SetJumpTarget(input_is_set);
switch (bit) switch (bit)
{ {
case CR_SO_BIT: // set bit 61 case CR_SO_BIT: // set bit 61 to input
MOV(64, R(ABI_PARAM1), Imm64(1ull << 61)); BTR(64, R(ABI_PARAM1), Imm8(61));
OR(64, R(ABI_PARAM2), R(ABI_PARAM1)); SHL(64, R(in), Imm8(61));
OR(64, R(ABI_PARAM1), R(in));
break; break;
case CR_EQ_BIT: // set bits 31-0 to 0 case CR_EQ_BIT: // clear low 32 bits, set bit 0 to !input
MOV(64, R(ABI_PARAM1), Imm64(0xFFFFFFFF00000000)); SHR(64, R(ABI_PARAM1), Imm8(32));
AND(64, R(ABI_PARAM2), R(ABI_PARAM1)); SHL(64, R(ABI_PARAM1), Imm8(32));
XOR(32, R(in), Imm8(1));
OR(64, R(ABI_PARAM1), R(in));
break; break;
case CR_GT_BIT: // unset bit 63 case CR_GT_BIT: // set bit 63 to !input
MOV(64, R(ABI_PARAM1), Imm64(~(1ull << 63))); BTR(64, R(ABI_PARAM1), Imm8(63));
AND(64, R(ABI_PARAM2), R(ABI_PARAM1)); NOT(32, R(in));
SHL(64, R(in), Imm8(63));
OR(64, R(ABI_PARAM1), R(in));
break; break;
case CR_LT_BIT: // set bit 62 case CR_LT_BIT: // set bit 62 to input
MOV(64, R(ABI_PARAM1), Imm64(1ull << 62)); BTR(64, R(ABI_PARAM1), Imm8(62));
OR(64, R(ABI_PARAM2), R(ABI_PARAM1)); SHL(64, R(in), Imm8(62));
OR(64, R(ABI_PARAM1), R(in));
break; break;
} }
SetJumpTarget(end); BTS(64, R(ABI_PARAM1), Imm8(32));
MOV(64, R(ABI_PARAM1), Imm64(1ull << 32)); MOV(64, M(&PowerPC::ppcState.cr_val[field]), R(ABI_PARAM1));
OR(64, R(ABI_PARAM2), R(ABI_PARAM1));
MOV(64, M(&PowerPC::ppcState.cr_val[field]), R(ABI_PARAM2));
} }
FixupBranch Jit64::JumpIfCRFieldBit(int field, int bit, bool jump_if_set) FixupBranch Jit64::JumpIfCRFieldBit(int field, int bit, bool jump_if_set)
@ -109,23 +83,20 @@ FixupBranch Jit64::JumpIfCRFieldBit(int field, int bit, bool jump_if_set)
switch (bit) switch (bit)
{ {
case CR_SO_BIT: // check bit 61 set case CR_SO_BIT: // check bit 61 set
MOV(64, R(RAX), Imm64(1ull << 61)); BT(64, M(&PowerPC::ppcState.cr_val[field]), Imm8(61));
TEST(64, M(&PowerPC::ppcState.cr_val[field]), R(RAX)); return J_CC(jump_if_set ? CC_C : CC_NC, true);
return J_CC(jump_if_set ? CC_NZ : CC_Z, true);
case CR_EQ_BIT: // check bits 31-0 == 0 case CR_EQ_BIT: // check bits 31-0 == 0
CMP(32, M(&PowerPC::ppcState.cr_val[field]), Imm32(0)); CMP(32, M(&PowerPC::ppcState.cr_val[field]), Imm8(0));
return J_CC(jump_if_set ? CC_Z : CC_NZ, true); return J_CC(jump_if_set ? CC_Z : CC_NZ, true);
case CR_GT_BIT: // check val > 0 case CR_GT_BIT: // check val > 0
MOV(64, R(RAX), M(&PowerPC::ppcState.cr_val[field])); CMP(64, M(&PowerPC::ppcState.cr_val[field]), Imm8(0));
TEST(64, R(RAX), R(RAX));
return J_CC(jump_if_set ? CC_G : CC_LE, true); return J_CC(jump_if_set ? CC_G : CC_LE, true);
case CR_LT_BIT: // check bit 62 set case CR_LT_BIT: // check bit 62 set
MOV(64, R(RAX), Imm64(1ull << 62)); BT(64, M(&PowerPC::ppcState.cr_val[field]), Imm8(62));
TEST(64, M(&PowerPC::ppcState.cr_val[field]), R(RAX)); return J_CC(jump_if_set ? CC_C : CC_NC, true);
return J_CC(jump_if_set ? CC_NZ : CC_Z, true);
default: default:
_assert_msg_(DYNA_REC, false, "Invalid CR bit"); _assert_msg_(DYNA_REC, false, "Invalid CR bit");
@ -277,51 +248,51 @@ void Jit64::mfcr(UGeckoInstruction inst)
JITDISABLE(bJITSystemRegistersOff); JITDISABLE(bJITSystemRegistersOff);
// USES_CR // USES_CR
int d = inst.RD; int d = inst.RD;
gpr.Lock(d); gpr.BindToRegister(d, false, true);
gpr.KillImmediate(d, false, true); XOR(32, gpr.R(d), gpr.R(d));
XOR(32, R(EAX), R(EAX));
gpr.FlushLockX(ABI_PARAM1, ABI_PARAM2); gpr.FlushLockX(ABI_PARAM1);
X64Reg cr_val = ABI_PARAM1; X64Reg cr_val = ABI_PARAM1;
X64Reg tmp = ABI_PARAM2; // we only need to zero the high bits of EAX once
XOR(32, R(EAX), R(EAX));
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
{ {
static const u8 m_flagTable[8] = {0x0,0x1,0x8,0x9,0x0,0x1,0x8,0x9};
if (i != 0) if (i != 0)
SHL(32, R(EAX), Imm8(4)); SHL(32, gpr.R(d), Imm8(4));
MOV(64, R(cr_val), M(&PowerPC::ppcState.cr_val[i])); MOV(64, R(cr_val), M(&PowerPC::ppcState.cr_val[i]));
// SO: Bit 61 set. // EQ: Bits 31-0 == 0; set flag bit 1
MOV(64, R(tmp), R(cr_val));
SHR(64, R(tmp), Imm8(61));
AND(32, R(tmp), Imm8(1));
OR(32, R(EAX), R(tmp));
// EQ: Bits 31-0 == 0.
XOR(32, R(tmp), R(tmp));
TEST(32, R(cr_val), R(cr_val)); TEST(32, R(cr_val), R(cr_val));
SETcc(CC_Z, R(tmp)); SETcc(CC_Z, R(EAX));
SHL(32, R(tmp), Imm8(1)); LEA(32, gpr.RX(d), MComplex(gpr.RX(d), EAX, SCALE_2, 0));
OR(32, R(EAX), R(tmp));
// GT: Value > 0. // GT: Value > 0; set flag bit 2
TEST(64, R(cr_val), R(cr_val)); TEST(64, R(cr_val), R(cr_val));
SETcc(CC_G, R(tmp)); SETcc(CC_G, R(EAX));
SHL(32, R(tmp), Imm8(2)); LEA(32, gpr.RX(d), MComplex(gpr.RX(d), EAX, SCALE_4, 0));
OR(32, R(EAX), R(tmp));
// LT: Bit 62 set. // SO: Bit 61 set; set flag bit 0
MOV(64, R(tmp), R(cr_val)); // LT: Bit 62 set; set flag bit 3
SHR(64, R(tmp), Imm8(62 - 3)); SHR(64, R(cr_val), Imm8(61));
AND(32, R(tmp), Imm8(0x8)); MOVZX(32, 8, EAX, MDisp(cr_val, (u32)(u64)m_flagTable));
OR(32, R(EAX), R(tmp)); OR(32, gpr.R(d), R(EAX));
} }
MOV(32, gpr.R(d), R(EAX));
gpr.UnlockAll(); gpr.UnlockAll();
gpr.UnlockAllX(); gpr.UnlockAllX();
} }
// convert flags into 64-bit CR values with a lookup table
static const u64 m_crTable[16] =
{
PPCCRToInternal(0x0), PPCCRToInternal(0x1), PPCCRToInternal(0x2), PPCCRToInternal(0x3),
PPCCRToInternal(0x4), PPCCRToInternal(0x5), PPCCRToInternal(0x6), PPCCRToInternal(0x7),
PPCCRToInternal(0x8), PPCCRToInternal(0x9), PPCCRToInternal(0xA), PPCCRToInternal(0xB),
PPCCRToInternal(0xC), PPCCRToInternal(0xD), PPCCRToInternal(0xE), PPCCRToInternal(0xF),
};
void Jit64::mtcrf(UGeckoInstruction inst) void Jit64::mtcrf(UGeckoInstruction inst)
{ {
INSTRUCTION_START INSTRUCTION_START
@ -338,8 +309,16 @@ void Jit64::mtcrf(UGeckoInstruction inst)
if ((crm & (0x80 >> i)) != 0) if ((crm & (0x80 >> i)) != 0)
{ {
u8 newcr = (gpr.R(inst.RS).offset >> (28 - (i * 4))) & 0xF; u8 newcr = (gpr.R(inst.RS).offset >> (28 - (i * 4))) & 0xF;
MOV(64, R(RAX), Imm64(PPCCRToInternal(newcr))); u64 newcrval = PPCCRToInternal(newcr);
MOV(64, M(&PowerPC::ppcState.cr_val[i]), R(RAX)); if ((s64)newcrval == (s32)newcrval)
{
MOV(64, M(&PowerPC::ppcState.cr_val[i]), Imm32(newcrval));
}
else
{
MOV(64, R(RAX), Imm64(newcrval));
MOV(64, M(&PowerPC::ppcState.cr_val[i]), R(RAX));
}
} }
} }
} }
@ -347,50 +326,20 @@ void Jit64::mtcrf(UGeckoInstruction inst)
{ {
gpr.Lock(inst.RS); gpr.Lock(inst.RS);
gpr.BindToRegister(inst.RS, true, false); gpr.BindToRegister(inst.RS, true, false);
gpr.FlushLockX(ABI_PARAM1, ABI_PARAM2);
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
{ {
if ((crm & (0x80 >> i)) != 0) if ((crm & (0x80 >> i)) != 0)
{ {
MOVZX(64, 32, EAX, gpr.R(inst.RS)); MOV(32, R(EAX), gpr.R(inst.RS));
SHR(64, R(EAX), Imm8(28 - (i * 4))); if (i != 7)
AND(64, R(EAX), Imm32(0xF)); SHR(32, R(EAX), Imm8(28 - (i * 4)));
if (i != 0)
X64Reg cr_val = ABI_PARAM1; AND(32, R(EAX), Imm8(0xF));
X64Reg tmp = ABI_PARAM2; MOV(64, R(EAX), MScaled(EAX, SCALE_8, (u32)(u64)m_crTable));
MOV(64, M(&PowerPC::ppcState.cr_val[i]), R(EAX));
MOV(64, R(cr_val), Imm64(1ull << 32));
// SO
MOV(64, R(tmp), R(EAX));
SHL(64, R(tmp), Imm8(63));
SHR(64, R(tmp), Imm8(63 - 61));
OR(64, R(cr_val), R(tmp));
// EQ
MOV(64, R(tmp), R(EAX));
NOT(64, R(tmp));
AND(64, R(tmp), Imm8(CR_EQ));
OR(64, R(cr_val), R(tmp));
// GT
MOV(64, R(tmp), R(EAX));
NOT(64, R(tmp));
AND(64, R(tmp), Imm8(CR_GT));
SHL(64, R(tmp), Imm8(63 - 2));
OR(64, R(cr_val), R(tmp));
// LT
MOV(64, R(tmp), R(EAX));
AND(64, R(tmp), Imm8(CR_LT));
SHL(64, R(tmp), Imm8(62 - 3));
OR(64, R(cr_val), R(tmp));
MOV(64, M(&PowerPC::ppcState.cr_val[i]), R(cr_val));
} }
} }
gpr.UnlockAll(); gpr.UnlockAll();
gpr.UnlockAllX();
} }
} }
} }
@ -416,41 +365,11 @@ void Jit64::mcrxr(UGeckoInstruction inst)
// USES_CR // USES_CR
// Copy XER[0-3] into CR[inst.CRFD] // Copy XER[0-3] into CR[inst.CRFD]
MOVZX(64, 32, EAX, M(&PowerPC::ppcState.spr[SPR_XER])); MOV(32, R(EAX), M(&PowerPC::ppcState.spr[SPR_XER]));
SHR(64, R(EAX), Imm8(28)); SHR(32, R(EAX), Imm8(28));
gpr.FlushLockX(ABI_PARAM1, ABI_PARAM2); MOV(64, R(EAX), MScaled(EAX, SCALE_8, (u32)(u64)m_crTable));
X64Reg cr_val = ABI_PARAM1; MOV(64, M(&PowerPC::ppcState.cr_val[inst.CRFD]), R(EAX));
X64Reg tmp = ABI_PARAM2;
MOV(64, R(cr_val), Imm64(1ull << 32));
// SO
MOV(64, R(tmp), R(EAX));
SHL(64, R(tmp), Imm8(63));
SHR(64, R(tmp), Imm8(63 - 61));
OR(64, R(cr_val), R(tmp));
// EQ
MOV(64, R(tmp), R(EAX));
AND(64, R(tmp), Imm8(0x2));
OR(64, R(cr_val), R(tmp));
// GT
MOV(64, R(tmp), R(EAX));
NOT(64, R(tmp));
AND(64, R(tmp), Imm8(0x4));
SHL(64, R(tmp), Imm8(63 - 2));
OR(64, R(cr_val), R(tmp));
// LT
MOV(64, R(tmp), R(EAX));
AND(64, R(tmp), Imm8(0x8));
SHL(64, R(tmp), Imm8(62 - 3));
OR(64, R(cr_val), R(tmp));
MOV(64, M(&PowerPC::ppcState.cr_val[inst.CRFD]), R(cr_val));
gpr.UnlockAllX();
// Clear XER[0-3] // Clear XER[0-3]
AND(32, M(&PowerPC::ppcState.spr[SPR_XER]), Imm32(0x0FFFFFFF)); AND(32, M(&PowerPC::ppcState.spr[SPR_XER]), Imm32(0x0FFFFFFF));
@ -467,49 +386,33 @@ void Jit64::crXXX(UGeckoInstruction inst)
// not required. // not required.
// USES_CR // USES_CR
// crandc or crorc or creqv or crnand or crnor
bool negateA = inst.SUBOP10 == 129 || inst.SUBOP10 == 417 || inst.SUBOP10 == 289 || inst.SUBOP10 == 225 || inst.SUBOP10 == 33;
// crnand or crnor
bool negateB = inst.SUBOP10 == 225 || inst.SUBOP10 == 33;
gpr.FlushLockX(ABI_PARAM1, ABI_PARAM2); gpr.FlushLockX(ABI_PARAM1);
GetCRFieldBit(inst.CRBA >> 2, 3 - (inst.CRBA & 3), ABI_PARAM2); GetCRFieldBit(inst.CRBA >> 2, 3 - (inst.CRBA & 3), ABI_PARAM1, negateA);
GetCRFieldBit(inst.CRBB >> 2, 3 - (inst.CRBB & 3), EAX); GetCRFieldBit(inst.CRBB >> 2, 3 - (inst.CRBB & 3), EAX, negateB);
// Compute combined bit // Compute combined bit
switch (inst.SUBOP10) switch (inst.SUBOP10)
{ {
case 33: // crnor case 33: // crnor: ~(A || B) == (~A && ~B)
OR(8, R(EAX), R(ABI_PARAM2));
NOT(8, R(EAX));
break;
case 129: // crandc case 129: // crandc
NOT(8, R(ABI_PARAM2)); case 257: // crand
AND(8, R(EAX), R(ABI_PARAM2)); AND(8, R(EAX), R(ABI_PARAM1));
break; break;
case 193: // crxor case 193: // crxor
XOR(8, R(EAX), R(ABI_PARAM2));
break;
case 225: // crnand
AND(8, R(EAX), R(ABI_PARAM2));
NOT(8, R(EAX));
break;
case 257: // crand
AND(8, R(EAX), R(ABI_PARAM2));
break;
case 289: // creqv case 289: // creqv
XOR(8, R(EAX), R(ABI_PARAM2)); XOR(8, R(EAX), R(ABI_PARAM1));
NOT(8, R(EAX));
break; break;
case 225: // crnand: ~(A && B) == (~A || ~B)
case 417: // crorc case 417: // crorc
NOT(8, R(ABI_PARAM2));
OR(8, R(EAX), R(ABI_PARAM2));
break;
case 449: // cror case 449: // cror
OR(8, R(EAX), R(ABI_PARAM2)); OR(8, R(EAX), R(ABI_PARAM1));
break; break;
} }