mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-02-13 07:49:19 +01:00
Merge pull request #9593 from JosJuice/jitarm64-constant-carry
JitArm64: Constant carry flag optimizations
This commit is contained in:
commit
bcd572a820
@ -673,6 +673,7 @@ public:
|
|||||||
CSINV(Rd, zr, zr, (CCFlags)((u32)cond ^ 1));
|
CSINV(Rd, zr, zr, (CCFlags)((u32)cond ^ 1));
|
||||||
}
|
}
|
||||||
void NEG(ARM64Reg Rd, ARM64Reg Rs) { SUB(Rd, Is64Bit(Rd) ? ARM64Reg::ZR : ARM64Reg::WZR, Rs); }
|
void NEG(ARM64Reg Rd, ARM64Reg Rs) { SUB(Rd, Is64Bit(Rd) ? ARM64Reg::ZR : ARM64Reg::WZR, Rs); }
|
||||||
|
void NEGS(ARM64Reg Rd, ARM64Reg Rs) { SUBS(Rd, Is64Bit(Rd) ? ARM64Reg::ZR : ARM64Reg::WZR, Rs); }
|
||||||
// Data-Processing 1 source
|
// Data-Processing 1 source
|
||||||
void RBIT(ARM64Reg Rd, ARM64Reg Rn);
|
void RBIT(ARM64Reg Rd, ARM64Reg Rn);
|
||||||
void REV16(ARM64Reg Rd, ARM64Reg Rn);
|
void REV16(ARM64Reg Rd, ARM64Reg Rn);
|
||||||
|
@ -926,8 +926,7 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
|
|||||||
|
|
||||||
js.downcountAmount = 0;
|
js.downcountAmount = 0;
|
||||||
js.skipInstructions = 0;
|
js.skipInstructions = 0;
|
||||||
js.carryFlagSet = false;
|
js.carryFlag = CarryFlag::InPPCState;
|
||||||
js.carryFlagInverted = false;
|
|
||||||
js.constantGqr.clear();
|
js.constantGqr.clear();
|
||||||
|
|
||||||
// Assume that GQR values don't change often at runtime. Many paired-heavy games use largely float
|
// Assume that GQR values don't change often at runtime. Many paired-heavy games use largely float
|
||||||
|
@ -63,26 +63,29 @@ void Jit64::GenerateOverflow()
|
|||||||
|
|
||||||
void Jit64::FinalizeCarry(CCFlags cond)
|
void Jit64::FinalizeCarry(CCFlags cond)
|
||||||
{
|
{
|
||||||
js.carryFlagSet = false;
|
js.carryFlag = CarryFlag::InPPCState;
|
||||||
js.carryFlagInverted = false;
|
|
||||||
if (js.op->wantsCA)
|
if (js.op->wantsCA)
|
||||||
{
|
{
|
||||||
// Not actually merging instructions, but the effect is equivalent (we can't have
|
// Not actually merging instructions, but the effect is equivalent (we can't have
|
||||||
// breakpoints/etc in between).
|
// breakpoints/etc in between).
|
||||||
if (CanMergeNextInstructions(1) && js.op[1].wantsCAInFlags)
|
if (CanMergeNextInstructions(1) && js.op[1].wantsCAInFlags)
|
||||||
{
|
{
|
||||||
if (cond == CC_C || cond == CC_NC)
|
if (cond == CC_C)
|
||||||
{
|
{
|
||||||
js.carryFlagInverted = cond == CC_NC;
|
js.carryFlag = CarryFlag::InHostCarry;
|
||||||
|
}
|
||||||
|
else if (cond == CC_NC)
|
||||||
|
{
|
||||||
|
js.carryFlag = CarryFlag::InHostCarryInverted;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// convert the condition to a carry flag (is there a better way?)
|
// convert the condition to a carry flag (is there a better way?)
|
||||||
SETcc(cond, R(RSCRATCH));
|
SETcc(cond, R(RSCRATCH));
|
||||||
SHR(8, R(RSCRATCH), Imm8(1));
|
SHR(8, R(RSCRATCH), Imm8(1));
|
||||||
|
js.carryFlag = CarryFlag::InHostCarry;
|
||||||
}
|
}
|
||||||
LockFlags();
|
LockFlags();
|
||||||
js.carryFlagSet = true;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -94,8 +97,7 @@ void Jit64::FinalizeCarry(CCFlags cond)
|
|||||||
// Unconditional version
|
// Unconditional version
|
||||||
void Jit64::FinalizeCarry(bool ca)
|
void Jit64::FinalizeCarry(bool ca)
|
||||||
{
|
{
|
||||||
js.carryFlagSet = false;
|
js.carryFlag = CarryFlag::InPPCState;
|
||||||
js.carryFlagInverted = false;
|
|
||||||
if (js.op->wantsCA)
|
if (js.op->wantsCA)
|
||||||
{
|
{
|
||||||
if (CanMergeNextInstructions(1) && js.op[1].wantsCAInFlags)
|
if (CanMergeNextInstructions(1) && js.op[1].wantsCAInFlags)
|
||||||
@ -105,7 +107,7 @@ void Jit64::FinalizeCarry(bool ca)
|
|||||||
else
|
else
|
||||||
CLC();
|
CLC();
|
||||||
LockFlags();
|
LockFlags();
|
||||||
js.carryFlagSet = true;
|
js.carryFlag = CarryFlag::InHostCarry;
|
||||||
}
|
}
|
||||||
else if (ca)
|
else if (ca)
|
||||||
{
|
{
|
||||||
@ -1475,7 +1477,7 @@ void Jit64::arithXex(UGeckoInstruction inst)
|
|||||||
int d = inst.RD;
|
int d = inst.RD;
|
||||||
bool same_input_sub = !add && regsource && a == b;
|
bool same_input_sub = !add && regsource && a == b;
|
||||||
|
|
||||||
if (!js.carryFlagSet)
|
if (js.carryFlag == CarryFlag::InPPCState)
|
||||||
JitGetAndClearCAOV(inst.OE);
|
JitGetAndClearCAOV(inst.OE);
|
||||||
else
|
else
|
||||||
UnlockFlags();
|
UnlockFlags();
|
||||||
@ -1488,7 +1490,7 @@ void Jit64::arithXex(UGeckoInstruction inst)
|
|||||||
RegCache::Realize(Rd);
|
RegCache::Realize(Rd);
|
||||||
|
|
||||||
// Convert carry to borrow
|
// Convert carry to borrow
|
||||||
if (!js.carryFlagInverted)
|
if (js.carryFlag != CarryFlag::InHostCarryInverted)
|
||||||
CMC();
|
CMC();
|
||||||
SBB(32, Rd, Rd);
|
SBB(32, Rd, Rd);
|
||||||
invertedCarry = true;
|
invertedCarry = true;
|
||||||
@ -1499,7 +1501,7 @@ void Jit64::arithXex(UGeckoInstruction inst)
|
|||||||
RCX64Reg Rd = gpr.Bind(d, RCMode::ReadWrite);
|
RCX64Reg Rd = gpr.Bind(d, RCMode::ReadWrite);
|
||||||
RegCache::Realize(Ra, Rd);
|
RegCache::Realize(Ra, Rd);
|
||||||
|
|
||||||
if (!js.carryFlagInverted)
|
if (js.carryFlag != CarryFlag::InHostCarryInverted)
|
||||||
CMC();
|
CMC();
|
||||||
SBB(32, Rd, Ra);
|
SBB(32, Rd, Ra);
|
||||||
invertedCarry = true;
|
invertedCarry = true;
|
||||||
@ -1519,14 +1521,14 @@ void Jit64::arithXex(UGeckoInstruction inst)
|
|||||||
NOT(32, Rd);
|
NOT(32, Rd);
|
||||||
// if the source is an immediate, we can invert carry by going from add -> sub and doing src =
|
// if the source is an immediate, we can invert carry by going from add -> sub and doing src =
|
||||||
// -1 - src
|
// -1 - src
|
||||||
if (js.carryFlagInverted && source.IsImm())
|
if (js.carryFlag == CarryFlag::InHostCarryInverted && source.IsImm())
|
||||||
{
|
{
|
||||||
SBB(32, Rd, Imm32(-1 - source.SImm32()));
|
SBB(32, Rd, Imm32(-1 - source.SImm32()));
|
||||||
invertedCarry = true;
|
invertedCarry = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (js.carryFlagInverted)
|
if (js.carryFlag == CarryFlag::InHostCarryInverted)
|
||||||
CMC();
|
CMC();
|
||||||
ADC(32, Rd, source);
|
ADC(32, Rd, source);
|
||||||
}
|
}
|
||||||
|
@ -638,7 +638,7 @@ void JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
|
|||||||
js.downcountAmount = 0;
|
js.downcountAmount = 0;
|
||||||
js.skipInstructions = 0;
|
js.skipInstructions = 0;
|
||||||
js.curBlock = b;
|
js.curBlock = b;
|
||||||
js.carryFlagSet = false;
|
js.carryFlag = CarryFlag::InPPCState;
|
||||||
js.numLoadStoreInst = 0;
|
js.numLoadStoreInst = 0;
|
||||||
js.numFloatingPointInst = 0;
|
js.numFloatingPointInst = 0;
|
||||||
|
|
||||||
|
@ -233,7 +233,7 @@ private:
|
|||||||
void ComputeRC0(Arm64Gen::ARM64Reg reg);
|
void ComputeRC0(Arm64Gen::ARM64Reg reg);
|
||||||
void ComputeRC0(u64 imm);
|
void ComputeRC0(u64 imm);
|
||||||
void ComputeCarry(Arm64Gen::ARM64Reg reg); // reg must contain 0 or 1
|
void ComputeCarry(Arm64Gen::ARM64Reg reg); // reg must contain 0 or 1
|
||||||
void ComputeCarry(bool Carry);
|
void ComputeCarry(bool carry);
|
||||||
void ComputeCarry();
|
void ComputeCarry();
|
||||||
void FlushCarry();
|
void FlushCarry();
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ void JitArm64::ComputeRC0(u64 imm)
|
|||||||
|
|
||||||
void JitArm64::ComputeCarry(ARM64Reg reg)
|
void JitArm64::ComputeCarry(ARM64Reg reg)
|
||||||
{
|
{
|
||||||
js.carryFlagSet = false;
|
js.carryFlag = CarryFlag::InPPCState;
|
||||||
|
|
||||||
if (!js.op->wantsCA)
|
if (!js.op->wantsCA)
|
||||||
return;
|
return;
|
||||||
@ -40,7 +40,7 @@ void JitArm64::ComputeCarry(ARM64Reg reg)
|
|||||||
if (CanMergeNextInstructions(1) && js.op[1].wantsCAInFlags)
|
if (CanMergeNextInstructions(1) && js.op[1].wantsCAInFlags)
|
||||||
{
|
{
|
||||||
CMP(reg, 1);
|
CMP(reg, 1);
|
||||||
js.carryFlagSet = true;
|
js.carryFlag = CarryFlag::InHostCarry;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -48,52 +48,57 @@ void JitArm64::ComputeCarry(ARM64Reg reg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void JitArm64::ComputeCarry(bool Carry)
|
void JitArm64::ComputeCarry(bool carry)
|
||||||
{
|
{
|
||||||
js.carryFlagSet = false;
|
js.carryFlag = carry ? CarryFlag::ConstantTrue : CarryFlag::ConstantFalse;
|
||||||
|
|
||||||
if (!js.op->wantsCA)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (Carry)
|
|
||||||
{
|
|
||||||
ARM64Reg WA = gpr.GetReg();
|
|
||||||
MOVI2R(WA, 1);
|
|
||||||
STRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
|
||||||
gpr.Unlock(WA);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
STRB(IndexType::Unsigned, ARM64Reg::WSP, PPC_REG, PPCSTATE_OFF(xer_ca));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void JitArm64::ComputeCarry()
|
void JitArm64::ComputeCarry()
|
||||||
{
|
{
|
||||||
js.carryFlagSet = false;
|
js.carryFlag = CarryFlag::InPPCState;
|
||||||
|
|
||||||
if (!js.op->wantsCA)
|
if (!js.op->wantsCA)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
js.carryFlagSet = true;
|
js.carryFlag = CarryFlag::InHostCarry;
|
||||||
if (CanMergeNextInstructions(1) && js.op[1].opinfo->type == ::OpType::Integer)
|
if (CanMergeNextInstructions(1) && js.op[1].opinfo->type == ::OpType::Integer)
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
FlushCarry();
|
FlushCarry();
|
||||||
}
|
}
|
||||||
|
|
||||||
void JitArm64::FlushCarry()
|
void JitArm64::FlushCarry()
|
||||||
{
|
{
|
||||||
if (!js.carryFlagSet)
|
switch (js.carryFlag)
|
||||||
return;
|
{
|
||||||
|
case CarryFlag::InPPCState:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CarryFlag::InHostCarry:
|
||||||
|
{
|
||||||
ARM64Reg WA = gpr.GetReg();
|
ARM64Reg WA = gpr.GetReg();
|
||||||
CSINC(WA, ARM64Reg::WSP, ARM64Reg::WSP, CC_CC);
|
CSET(WA, CC_CS);
|
||||||
STRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
STRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
||||||
gpr.Unlock(WA);
|
gpr.Unlock(WA);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CarryFlag::ConstantTrue:
|
||||||
|
{
|
||||||
|
ARM64Reg WA = gpr.GetReg();
|
||||||
|
MOVI2R(WA, 1);
|
||||||
|
STRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
||||||
|
gpr.Unlock(WA);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CarryFlag::ConstantFalse:
|
||||||
|
{
|
||||||
|
STRB(IndexType::Unsigned, ARM64Reg::WZR, PPC_REG, PPCSTATE_OFF(xer_ca));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
js.carryFlagSet = false;
|
js.carryFlag = CarryFlag::InPPCState;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JitArm64::reg_imm(u32 d, u32 a, u32 value, u32 (*do_op)(u32, u32),
|
void JitArm64::reg_imm(u32 d, u32 a, u32 value, u32 (*do_op)(u32, u32),
|
||||||
@ -690,7 +695,7 @@ void JitArm64::srawix(UGeckoInstruction inst)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
CSINC(WA, ARM64Reg::WSP, ARM64Reg::WSP, CC_EQ);
|
CSINC(WA, ARM64Reg::WSP, ARM64Reg::WSP, CC_EQ);
|
||||||
STRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
ComputeCarry(WA);
|
||||||
}
|
}
|
||||||
gpr.Unlock(WA);
|
gpr.Unlock(WA);
|
||||||
}
|
}
|
||||||
@ -841,27 +846,49 @@ void JitArm64::addzex(UGeckoInstruction inst)
|
|||||||
|
|
||||||
int a = inst.RA, d = inst.RD;
|
int a = inst.RA, d = inst.RD;
|
||||||
|
|
||||||
if (js.carryFlagSet)
|
switch (js.carryFlag)
|
||||||
|
{
|
||||||
|
case CarryFlag::InPPCState:
|
||||||
|
{
|
||||||
|
gpr.BindToRegister(d, d == a);
|
||||||
|
ARM64Reg WA = d == a ? gpr.GetReg() : gpr.R(d);
|
||||||
|
|
||||||
|
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
||||||
|
ADDS(gpr.R(d), gpr.R(a), WA);
|
||||||
|
ComputeCarry();
|
||||||
|
|
||||||
|
if (d == a)
|
||||||
|
gpr.Unlock(WA);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CarryFlag::InHostCarry:
|
||||||
{
|
{
|
||||||
gpr.BindToRegister(d, d == a);
|
gpr.BindToRegister(d, d == a);
|
||||||
ADCS(gpr.R(d), gpr.R(a), ARM64Reg::WZR);
|
ADCS(gpr.R(d), gpr.R(a), ARM64Reg::WZR);
|
||||||
|
ComputeCarry();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else if (d == a)
|
case CarryFlag::ConstantTrue:
|
||||||
{
|
{
|
||||||
gpr.BindToRegister(d, true);
|
gpr.BindToRegister(d, d == a);
|
||||||
ARM64Reg WA = gpr.GetReg();
|
ADDS(gpr.R(d), gpr.R(a), 1);
|
||||||
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
ComputeCarry();
|
||||||
ADDS(gpr.R(d), gpr.R(a), WA);
|
break;
|
||||||
gpr.Unlock(WA);
|
|
||||||
}
|
}
|
||||||
else
|
case CarryFlag::ConstantFalse:
|
||||||
|
{
|
||||||
|
if (d != a)
|
||||||
{
|
{
|
||||||
gpr.BindToRegister(d, false);
|
gpr.BindToRegister(d, false);
|
||||||
LDRB(IndexType::Unsigned, gpr.R(d), PPC_REG, PPCSTATE_OFF(xer_ca));
|
MOV(gpr.R(d), gpr.R(a));
|
||||||
ADDS(gpr.R(d), gpr.R(a), gpr.R(d));
|
}
|
||||||
|
|
||||||
|
ComputeCarry(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ComputeCarry();
|
|
||||||
if (inst.Rc)
|
if (inst.Rc)
|
||||||
ComputeRC0(gpr.R(d));
|
ComputeRC0(gpr.R(d));
|
||||||
}
|
}
|
||||||
@ -909,21 +936,39 @@ void JitArm64::subfex(UGeckoInstruction inst)
|
|||||||
u32 i = gpr.GetImm(a), j = gpr.GetImm(b);
|
u32 i = gpr.GetImm(a), j = gpr.GetImm(b);
|
||||||
|
|
||||||
gpr.BindToRegister(d, false);
|
gpr.BindToRegister(d, false);
|
||||||
|
|
||||||
|
switch (js.carryFlag)
|
||||||
|
{
|
||||||
|
case CarryFlag::InPPCState:
|
||||||
|
{
|
||||||
ARM64Reg WA = gpr.GetReg();
|
ARM64Reg WA = gpr.GetReg();
|
||||||
if (js.carryFlagSet)
|
|
||||||
{
|
|
||||||
MOVI2R(WA, ~i + j);
|
|
||||||
ADC(gpr.R(d), WA, ARM64Reg::WZR);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
||||||
ADDI2R(gpr.R(d), WA, ~i + j, gpr.R(d));
|
ADDI2R(gpr.R(d), WA, ~i + j, gpr.R(d));
|
||||||
}
|
|
||||||
gpr.Unlock(WA);
|
gpr.Unlock(WA);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CarryFlag::InHostCarry:
|
||||||
|
{
|
||||||
|
ARM64Reg WA = gpr.GetReg();
|
||||||
|
MOVI2R(WA, ~i + j);
|
||||||
|
ADC(gpr.R(d), WA, ARM64Reg::WZR);
|
||||||
|
gpr.Unlock(WA);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CarryFlag::ConstantTrue:
|
||||||
|
{
|
||||||
|
gpr.SetImmediate(d, ~i + j + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CarryFlag::ConstantFalse:
|
||||||
|
{
|
||||||
|
gpr.SetImmediate(d, ~i + j);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool must_have_carry = Interpreter::Helper_Carry(~i, j);
|
const bool must_have_carry = Interpreter::Helper_Carry(~i, j);
|
||||||
bool might_have_carry = (~i + j) == 0xFFFFFFFF;
|
const bool might_have_carry = (~i + j) == 0xFFFFFFFF;
|
||||||
|
|
||||||
if (must_have_carry)
|
if (must_have_carry)
|
||||||
{
|
{
|
||||||
@ -931,7 +976,7 @@ void JitArm64::subfex(UGeckoInstruction inst)
|
|||||||
}
|
}
|
||||||
else if (might_have_carry)
|
else if (might_have_carry)
|
||||||
{
|
{
|
||||||
// carry stay as it is
|
// carry stays as it is
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -940,26 +985,50 @@ void JitArm64::subfex(UGeckoInstruction inst)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ARM64Reg WA = gpr.GetReg();
|
ARM64Reg WA = js.carryFlag != CarryFlag::ConstantTrue ? gpr.GetReg() : ARM64Reg::INVALID_REG;
|
||||||
gpr.BindToRegister(d, d == a || d == b);
|
gpr.BindToRegister(d, d == a || d == b);
|
||||||
|
|
||||||
// upload the carry state
|
switch (js.carryFlag)
|
||||||
if (!js.carryFlagSet)
|
|
||||||
{
|
{
|
||||||
|
case CarryFlag::InPPCState:
|
||||||
|
{
|
||||||
|
// upload the carry state
|
||||||
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
||||||
CMP(WA, 1);
|
CMP(WA, 1);
|
||||||
|
[[fallthrough]];
|
||||||
}
|
}
|
||||||
|
case CarryFlag::InHostCarry:
|
||||||
// d = ~a + b + carry;
|
{
|
||||||
if (gpr.IsImm(a))
|
if (gpr.IsImm(a))
|
||||||
MOVI2R(WA, u32(~gpr.GetImm(a)));
|
MOVI2R(WA, u32(~gpr.GetImm(a)));
|
||||||
else
|
else
|
||||||
MVN(WA, gpr.R(a));
|
MVN(WA, gpr.R(a));
|
||||||
|
|
||||||
ADCS(gpr.R(d), WA, gpr.R(b));
|
ADCS(gpr.R(d), WA, gpr.R(b));
|
||||||
|
|
||||||
gpr.Unlock(WA);
|
|
||||||
|
|
||||||
ComputeCarry();
|
ComputeCarry();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CarryFlag::ConstantTrue:
|
||||||
|
{
|
||||||
|
SUBS(gpr.R(d), gpr.R(b), gpr.R(a));
|
||||||
|
ComputeCarry();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CarryFlag::ConstantFalse:
|
||||||
|
{
|
||||||
|
if (gpr.IsImm(a))
|
||||||
|
MOVI2R(WA, u32(~gpr.GetImm(a)));
|
||||||
|
else
|
||||||
|
MVN(WA, gpr.R(a));
|
||||||
|
|
||||||
|
ADDS(gpr.R(d), WA, gpr.R(b));
|
||||||
|
ComputeCarry();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WA != ARM64Reg::INVALID_REG)
|
||||||
|
gpr.Unlock(WA);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inst.Rc)
|
if (inst.Rc)
|
||||||
@ -1008,21 +1077,38 @@ void JitArm64::subfzex(UGeckoInstruction inst)
|
|||||||
|
|
||||||
gpr.BindToRegister(d, d == a);
|
gpr.BindToRegister(d, d == a);
|
||||||
|
|
||||||
if (js.carryFlagSet)
|
switch (js.carryFlag)
|
||||||
{
|
{
|
||||||
MVN(gpr.R(d), gpr.R(a));
|
case CarryFlag::InPPCState:
|
||||||
ADCS(gpr.R(d), gpr.R(d), ARM64Reg::WZR);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
ARM64Reg WA = gpr.GetReg();
|
ARM64Reg WA = gpr.GetReg();
|
||||||
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
||||||
MVN(gpr.R(d), gpr.R(a));
|
MVN(gpr.R(d), gpr.R(a));
|
||||||
ADDS(gpr.R(d), gpr.R(d), WA);
|
ADDS(gpr.R(d), gpr.R(d), WA);
|
||||||
gpr.Unlock(WA);
|
|
||||||
}
|
|
||||||
|
|
||||||
ComputeCarry();
|
ComputeCarry();
|
||||||
|
gpr.Unlock(WA);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CarryFlag::InHostCarry:
|
||||||
|
{
|
||||||
|
MVN(gpr.R(d), gpr.R(a));
|
||||||
|
ADCS(gpr.R(d), gpr.R(d), ARM64Reg::WZR);
|
||||||
|
ComputeCarry();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CarryFlag::ConstantTrue:
|
||||||
|
{
|
||||||
|
NEGS(gpr.R(d), gpr.R(a));
|
||||||
|
ComputeCarry();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CarryFlag::ConstantFalse:
|
||||||
|
{
|
||||||
|
MVN(gpr.R(d), gpr.R(a));
|
||||||
|
ComputeCarry(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (inst.Rc)
|
if (inst.Rc)
|
||||||
ComputeRC0(gpr.R(d));
|
ComputeRC0(gpr.R(d));
|
||||||
@ -1070,21 +1156,39 @@ void JitArm64::addex(UGeckoInstruction inst)
|
|||||||
u32 i = gpr.GetImm(a), j = gpr.GetImm(b);
|
u32 i = gpr.GetImm(a), j = gpr.GetImm(b);
|
||||||
|
|
||||||
gpr.BindToRegister(d, false);
|
gpr.BindToRegister(d, false);
|
||||||
|
|
||||||
|
switch (js.carryFlag)
|
||||||
|
{
|
||||||
|
case CarryFlag::InPPCState:
|
||||||
|
{
|
||||||
ARM64Reg WA = gpr.GetReg();
|
ARM64Reg WA = gpr.GetReg();
|
||||||
if (js.carryFlagSet)
|
|
||||||
{
|
|
||||||
MOVI2R(WA, i + j);
|
|
||||||
ADC(gpr.R(d), WA, ARM64Reg::WZR);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
||||||
ADDI2R(gpr.R(d), WA, i + j, gpr.R(d));
|
ADDI2R(gpr.R(d), WA, i + j, gpr.R(d));
|
||||||
}
|
|
||||||
gpr.Unlock(WA);
|
gpr.Unlock(WA);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CarryFlag::InHostCarry:
|
||||||
|
{
|
||||||
|
ARM64Reg WA = gpr.GetReg();
|
||||||
|
MOVI2R(WA, i + j);
|
||||||
|
ADC(gpr.R(d), WA, ARM64Reg::WZR);
|
||||||
|
gpr.Unlock(WA);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CarryFlag::ConstantTrue:
|
||||||
|
{
|
||||||
|
gpr.SetImmediate(d, i + j + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CarryFlag::ConstantFalse:
|
||||||
|
{
|
||||||
|
gpr.SetImmediate(d, i + j);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool must_have_carry = Interpreter::Helper_Carry(i, j);
|
const bool must_have_carry = Interpreter::Helper_Carry(i, j);
|
||||||
bool might_have_carry = (i + j) == 0xFFFFFFFF;
|
const bool might_have_carry = (i + j) == 0xFFFFFFFF;
|
||||||
|
|
||||||
if (must_have_carry)
|
if (must_have_carry)
|
||||||
{
|
{
|
||||||
@ -1092,7 +1196,7 @@ void JitArm64::addex(UGeckoInstruction inst)
|
|||||||
}
|
}
|
||||||
else if (might_have_carry)
|
else if (might_have_carry)
|
||||||
{
|
{
|
||||||
// carry stay as it is
|
// carry stays as it is
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1103,19 +1207,60 @@ void JitArm64::addex(UGeckoInstruction inst)
|
|||||||
{
|
{
|
||||||
gpr.BindToRegister(d, d == a || d == b);
|
gpr.BindToRegister(d, d == a || d == b);
|
||||||
|
|
||||||
// upload the carry state
|
if (js.carryFlag == CarryFlag::ConstantTrue && !gpr.IsImm(a) && !gpr.IsImm(b))
|
||||||
if (!js.carryFlagSet)
|
|
||||||
{
|
{
|
||||||
|
CMP(ARM64Reg::WZR, ARM64Reg::WZR);
|
||||||
|
js.carryFlag = CarryFlag::InHostCarry;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (js.carryFlag)
|
||||||
|
{
|
||||||
|
case CarryFlag::InPPCState:
|
||||||
|
{
|
||||||
|
// upload the carry state
|
||||||
ARM64Reg WA = gpr.GetReg();
|
ARM64Reg WA = gpr.GetReg();
|
||||||
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
||||||
CMP(WA, 1);
|
CMP(WA, 1);
|
||||||
gpr.Unlock(WA);
|
gpr.Unlock(WA);
|
||||||
|
[[fallthrough]];
|
||||||
}
|
}
|
||||||
|
case CarryFlag::InHostCarry:
|
||||||
// d = a + b + carry;
|
{
|
||||||
ADCS(gpr.R(d), gpr.R(a), gpr.R(b));
|
ADCS(gpr.R(d), gpr.R(a), gpr.R(b));
|
||||||
|
|
||||||
ComputeCarry();
|
ComputeCarry();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CarryFlag::ConstantTrue:
|
||||||
|
{
|
||||||
|
if (!gpr.IsImm(b))
|
||||||
|
std::swap(a, b);
|
||||||
|
ASSERT(gpr.IsImm(b));
|
||||||
|
|
||||||
|
ARM64Reg WA = gpr.GetReg();
|
||||||
|
const u32 imm = gpr.GetImm(b) + 1;
|
||||||
|
if (imm == 0)
|
||||||
|
{
|
||||||
|
if (d != a)
|
||||||
|
MOV(gpr.R(d), gpr.R(a));
|
||||||
|
|
||||||
|
ComputeCarry(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ADDSI2R(gpr.R(d), gpr.R(a), imm, WA);
|
||||||
|
ComputeCarry();
|
||||||
|
}
|
||||||
|
gpr.Unlock(WA);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CarryFlag::ConstantFalse:
|
||||||
|
{
|
||||||
|
ADDS(gpr.R(d), gpr.R(a), gpr.R(b));
|
||||||
|
ComputeCarry();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inst.Rc)
|
if (inst.Rc)
|
||||||
|
@ -42,6 +42,19 @@
|
|||||||
class JitBase : public CPUCoreBase
|
class JitBase : public CPUCoreBase
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
|
enum class CarryFlag
|
||||||
|
{
|
||||||
|
InPPCState,
|
||||||
|
InHostCarry,
|
||||||
|
#ifdef _M_X86_64
|
||||||
|
InHostCarryInverted,
|
||||||
|
#endif
|
||||||
|
#ifdef _M_ARM_64
|
||||||
|
ConstantTrue,
|
||||||
|
ConstantFalse,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
struct JitOptions
|
struct JitOptions
|
||||||
{
|
{
|
||||||
bool enableBlocklink;
|
bool enableBlocklink;
|
||||||
@ -73,8 +86,7 @@ protected:
|
|||||||
bool firstFPInstructionFound;
|
bool firstFPInstructionFound;
|
||||||
bool isLastInstruction;
|
bool isLastInstruction;
|
||||||
int skipInstructions;
|
int skipInstructions;
|
||||||
bool carryFlagSet;
|
CarryFlag carryFlag;
|
||||||
bool carryFlagInverted;
|
|
||||||
|
|
||||||
bool generatingTrampoline = false;
|
bool generatingTrampoline = false;
|
||||||
u8* trampolineExceptionHandler;
|
u8* trampolineExceptionHandler;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user