// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include <limits>
#include <vector>

#include "Common/MathUtil.h"
#include "Core/PowerPC/Jit64/Jit.h"
#include "Core/PowerPC/Jit64/JitAsm.h"
#include "Core/PowerPC/Jit64/JitRegCache.h"

using namespace Gen;

void Jit64::GenerateConstantOverflow(s64 val)
{
	GenerateConstantOverflow(val > std::numeric_limits<s32>::max() || val < std::numeric_limits<s32>::min());
}

void Jit64::GenerateConstantOverflow(bool overflow)
{
	if (overflow)
	{
		//XER[OV/SO] = 1
		MOV(8, PPCSTATE(xer_so_ov), Imm8(XER_OV_MASK | XER_SO_MASK));
	}
	else
	{
		//XER[OV] = 0
		AND(8, PPCSTATE(xer_so_ov), Imm8(~XER_OV_MASK));
	}
}

// We could do overflow branchlessly, but unlike carry it seems to be quite a bit rarer.
void Jit64::GenerateOverflow()
{
	FixupBranch jno = J_CC(CC_NO);
	//XER[OV/SO] = 1
	MOV(8, PPCSTATE(xer_so_ov), Imm8(XER_OV_MASK | XER_SO_MASK));
	FixupBranch exit = J();
	SetJumpTarget(jno);
	//XER[OV] = 0
	//We need to do this without modifying flags so as not to break stuff that assumes flags
	//aren't clobbered (carry, branch merging): speed doesn't really matter here (this is really
	//rare).
	static const u8 ovtable[4] = {0, 0, XER_SO_MASK, XER_SO_MASK};
	MOVZX(32, 8, RSCRATCH, PPCSTATE(xer_so_ov));
	MOV(8, R(RSCRATCH), MDisp(RSCRATCH, (u32)(u64)ovtable));
	MOV(8, PPCSTATE(xer_so_ov), R(RSCRATCH));
	SetJumpTarget(exit);
}

void Jit64::FinalizeCarry(CCFlags cond)
{
	js.carryFlagSet = false;
	js.carryFlagInverted = false;
	if (js.op->wantsCA)
	{
		// Not actually merging instructions, but the effect is equivalent (we can't have breakpoints/etc in between).
		if (MergeAllowedNextInstructions(1) && js.op[1].wantsCAInFlags)
		{
			if (cond == CC_C || cond == CC_NC)
			{
				js.carryFlagInverted = cond == CC_NC;
			}
			else
			{
				// convert the condition to a carry flag (is there a better way?)
				SETcc(cond, R(RSCRATCH));
				SHR(8, R(RSCRATCH), Imm8(1));
			}
			LockFlags();
			js.carryFlagSet = true;
		}
		else
		{
			JitSetCAIf(cond);
		}
	}
}

// Unconditional version
void Jit64::FinalizeCarry(bool ca)
{
	js.carryFlagSet = false;
	js.carryFlagInverted = false;
	if (js.op->wantsCA)
	{
		if (MergeAllowedNextInstructions(1) && js.op[1].wantsCAInFlags)
		{
			if (ca)
				STC();
			else
				CLC();
			LockFlags();
			js.carryFlagSet = true;
		}
		else if (ca)
		{
			JitSetCA();
		}
		else
		{
			JitClearCA();
		}
	}
}

void Jit64::FinalizeCarryOverflow(bool oe, bool inv)
{
	if (oe)
	{
		// Make sure not to lose the carry flags (not a big deal, this path is rare).
		PUSHF();
		//XER[OV] = 0
		AND(8, PPCSTATE(xer_so_ov), Imm8(~XER_OV_MASK));
		FixupBranch jno = J_CC(CC_NO);
		//XER[OV/SO] = 1
		MOV(8, PPCSTATE(xer_so_ov), Imm8(XER_SO_MASK | XER_OV_MASK));
		SetJumpTarget(jno);
		POPF();
	}
	// Do carry
	FinalizeCarry(inv ? CC_NC : CC_C);
}

// Be careful; only set needs_test to false if we can be absolutely sure flags don't need
// to be recalculated and haven't been clobbered. Keep in mind not all instructions set
// sufficient flags -- for example, the flags from SHL/SHR are *not* sufficient for LT/GT
// branches, only EQ.
void Jit64::ComputeRC(const Gen::OpArg & arg, bool needs_test, bool needs_sext)
{
	_assert_msg_(DYNA_REC, arg.IsSimpleReg() || arg.IsImm(), "Invalid ComputeRC operand");
	if (arg.IsImm())
	{
		MOV(64, PPCSTATE(cr_val[0]), Imm32(arg.SImm32()));
	}
	else if (needs_sext)
	{
		MOVSX(64, 32, RSCRATCH, arg);
		MOV(64, PPCSTATE(cr_val[0]), R(RSCRATCH));
	}
	else
	{
		MOV(64, PPCSTATE(cr_val[0]), arg);
	}
	if (CheckMergedBranch(0))
	{
		if (arg.IsImm())
		{
			DoMergedBranchImmediate(arg.SImm32());
		}
		else
		{
			if (needs_test)
			{
				TEST(32, arg, arg);
			}
			else
			{
				// If an operand to the cmp/rc op we're merging with the branch isn't used anymore, it'd be
				// better to flush it here so that we don't have to flush it on both sides of the branch.
				// We don't want to do this if a test is needed though, because it would interrupt macro-op
				// fusion.
				for (int j : ~js.op->gprInUse)
					gpr.StoreFromRegister(j);
			}
			DoMergedBranchCondition();
		}
	}
}

OpArg Jit64::ExtractFromReg(int reg, int offset)
{
	OpArg src = gpr.R(reg);
	// store to load forwarding should handle this case efficiently
	if (offset)
	{
		gpr.StoreFromRegister(reg, FLUSH_MAINTAIN_STATE);
		src = gpr.GetDefaultLocation(reg);
		src.AddMemOffset(offset);
	}
	return src;
}

// we can't do this optimization in the emitter because MOVZX and AND have different effects on flags.
void Jit64::AndWithMask(X64Reg reg, u32 mask)
{
	if (mask == 0xff)
		MOVZX(32, 8, reg, R(reg));
	else if (mask == 0xffff)
		MOVZX(32, 16, reg, R(reg));
	else
		AND(32, R(reg), Imm32(mask));
}

// Following static functions are used in conjunction with regimmop
static u32 Add(u32 a, u32 b)
{
	return a + b;
}

static u32 Or(u32 a, u32 b)
{
	return a | b;
}

static u32 And(u32 a, u32 b)
{
	return a & b;
}

static u32 Xor(u32 a, u32 b)
{
	return a ^ b;
}

void Jit64::regimmop(int d, int a, bool binary, u32 value, Operation doop, void (XEmitter::*op)(int, const Gen::OpArg&, const Gen::OpArg&), bool Rc, bool carry)
{
	bool needs_test = false;
	gpr.Lock(d, a);
	// Be careful; addic treats r0 as r0, but addi treats r0 as zero.
	if (a || binary || carry)
	{
		carry &= js.op->wantsCA;
		if (gpr.R(a).IsImm() && !carry)
		{
			gpr.SetImmediate32(d, doop(gpr.R(a).Imm32(), value));
		}
		else if (a == d)
		{
			gpr.BindToRegister(d, true);
			(this->*op)(32, gpr.R(d), Imm32(value)); //m_GPR[d] = m_GPR[_inst.RA] + _inst.SIMM_16;
		}
		else
		{
			gpr.BindToRegister(d, false);
			if (doop == Add && gpr.R(a).IsSimpleReg() && !carry)
			{
				needs_test = true;
				LEA(32, gpr.RX(d), MDisp(gpr.RX(a), value));
			}
			else
			{
				MOV(32, gpr.R(d), gpr.R(a));
				(this->*op)(32, gpr.R(d), Imm32(value)); //m_GPR[d] = m_GPR[_inst.RA] + _inst.SIMM_16;
			}
		}
		if (carry)
			FinalizeCarry(CC_C);
	}
	else if (doop == Add)
	{
		// a == 0, which for these instructions imply value = 0
		gpr.SetImmediate32(d, value);
	}
	else
	{
		_assert_msg_(DYNA_REC, 0, "WTF regimmop");
	}
	if (Rc)
		ComputeRC(gpr.R(d), needs_test, doop != And || (value & 0x80000000));
	gpr.UnlockAll();
}

void Jit64::reg_imm(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITIntegerOff);
	u32 d = inst.RD, a = inst.RA, s = inst.RS;
	switch (inst.OPCD)
	{
	case 14: // addi
		// occasionally used as MOV - emulate, with immediate propagation
		if (gpr.R(a).IsImm() && d != a && a != 0)
		{
			gpr.SetImmediate32(d, gpr.R(a).Imm32() + (u32)(s32)inst.SIMM_16);
		}
		else if (inst.SIMM_16 == 0 && d != a && a != 0)
		{
			gpr.Lock(a, d);
			gpr.BindToRegister(d, false, true);
			MOV(32, gpr.R(d), gpr.R(a));
			gpr.UnlockAll();
		}
		else
		{
			regimmop(d, a, false, (u32)(s32)inst.SIMM_16,  Add, &XEmitter::ADD); //addi
		}
		break;
	case 15: // addis
		regimmop(d, a, false, (u32)inst.SIMM_16 << 16, Add, &XEmitter::ADD);
		break;
	case 24: // ori
		if (a == 0 && s == 0 && inst.UIMM == 0 && !inst.Rc)  //check for nop
		{
			// Make the nop visible in the generated code. not much use but interesting if we see one.
			NOP();
			return;
		}
		regimmop(a, s, true, inst.UIMM, Or, &XEmitter::OR);
		break;
	case 25: // oris
		regimmop(a, s, true, inst.UIMM << 16, Or,  &XEmitter::OR, false);
		break;
	case 28: // andi
		regimmop(a, s, true, inst.UIMM,       And, &XEmitter::AND, true);
		break;
	case 29: // andis
		regimmop(a, s, true, inst.UIMM << 16, And, &XEmitter::AND, true);
		break;
	case 26: // xori
		regimmop(a, s, true, inst.UIMM,       Xor, &XEmitter::XOR, false);
		break;
	case 27: // xoris
		regimmop(a, s, true, inst.UIMM << 16, Xor, &XEmitter::XOR, false);
		break;
	case 12: // addic
		regimmop(d, a, false, (u32)(s32)inst.SIMM_16, Add, &XEmitter::ADD, false, true);
		break;
	case 13: // addic_rc
		regimmop(d, a, true, (u32)(s32)inst.SIMM_16, Add, &XEmitter::ADD, true, true);
		break;
	default:
		FALLBACK_IF(true);
	}
}

bool Jit64::CheckMergedBranch(int crf)
{
	if (!analyzer.HasOption(PPCAnalyst::PPCAnalyzer::OPTION_BRANCH_MERGE))
		return false;

	if (!MergeAllowedNextInstructions(1))
		return false;

	const UGeckoInstruction& next = js.op[1].inst;
	return (((next.OPCD == 16 /* bcx */) ||
	        ((next.OPCD == 19) && (next.SUBOP10 == 528) /* bcctrx */) ||
	        ((next.OPCD == 19) && (next.SUBOP10 == 16) /* bclrx */)) &&
	         (next.BO & BO_DONT_DECREMENT_FLAG) &&
	        !(next.BO & BO_DONT_CHECK_CONDITION) &&
	         (next.BI >> 2) == crf);
}

void Jit64::DoMergedBranch()
{
	// Code that handles successful PPC branching.
	const UGeckoInstruction& next = js.op[1].inst;
	const u32 nextPC = js.op[1].address;
	if (next.OPCD == 16) // bcx
	{
		if (next.LK)
			MOV(32, M(&LR), Imm32(nextPC + 4));

		u32 destination;
		if (next.AA)
			destination = SignExt16(next.BD << 2);
		else
			destination = nextPC + SignExt16(next.BD << 2);
		WriteExit(destination, next.LK, nextPC + 4);
	}
	else if ((next.OPCD == 19) && (next.SUBOP10 == 528)) // bcctrx
	{
		if (next.LK)
			MOV(32, M(&LR), Imm32(nextPC + 4));
		MOV(32, R(RSCRATCH), M(&CTR));
		AND(32, R(RSCRATCH), Imm32(0xFFFFFFFC));
		WriteExitDestInRSCRATCH(next.LK, nextPC + 4);
	}
	else if ((next.OPCD == 19) && (next.SUBOP10 == 16)) // bclrx
	{
		MOV(32, R(RSCRATCH), M(&LR));
		if (!m_enable_blr_optimization)
			AND(32, R(RSCRATCH), Imm32(0xFFFFFFFC));
		if (next.LK)
			MOV(32, M(&LR), Imm32(nextPC + 4));
		WriteBLRExit();
	}
	else
	{
		PanicAlert("WTF invalid branch");
	}
}

void Jit64::DoMergedBranchCondition()
{
	js.downcountAmount++;
	js.skipInstructions = 1;
	const UGeckoInstruction& next = js.op[1].inst;
	int test_bit = 8 >> (next.BI & 3);
	bool condition = !!(next.BO & BO_BRANCH_IF_TRUE);
	const u32 nextPC = js.op[1].address;

	gpr.UnlockAll();
	gpr.UnlockAllX();
	FixupBranch pDontBranch;
	if (test_bit & 8)
		pDontBranch = J_CC(condition ? CC_GE : CC_L, true);  // Test < 0, so jump over if >= 0.
	else if (test_bit & 4)
		pDontBranch = J_CC(condition ? CC_LE : CC_G, true);  // Test > 0, so jump over if <= 0.
	else if (test_bit & 2)
		pDontBranch = J_CC(condition ? CC_NE : CC_E, true);  // Test = 0, so jump over if != 0.
	else  // SO bit, do not branch (we don't emulate SO for cmp).
		pDontBranch = J(true);

	gpr.Flush(FLUSH_MAINTAIN_STATE);
	fpr.Flush(FLUSH_MAINTAIN_STATE);

	DoMergedBranch();

	SetJumpTarget(pDontBranch);

	if (!analyzer.HasOption(PPCAnalyst::PPCAnalyzer::OPTION_CONDITIONAL_CONTINUE))
	{
		gpr.Flush();
		fpr.Flush();
		WriteExit(nextPC + 4);
	}
}

void Jit64::DoMergedBranchImmediate(s64 val)
{
	js.downcountAmount++;
	js.skipInstructions = 1;
	const UGeckoInstruction& next = js.op[1].inst;
	int test_bit = 8 >> (next.BI & 3);
	bool condition = !!(next.BO & BO_BRANCH_IF_TRUE);
	const u32 nextPC = js.op[1].address;

	gpr.UnlockAll();
	gpr.UnlockAllX();
	bool branch;
	if (test_bit & 8)
		branch = condition ? val < 0 : val >= 0;
	else if (test_bit & 4)
		branch = condition ? val > 0 : val <= 0;
	else if (test_bit & 2)
		branch = condition ? val == 0 : val != 0;
	else  // SO bit, do not branch (we don't emulate SO for cmp).
		branch = false;

	if (branch)
	{
		gpr.Flush();
		fpr.Flush();
		DoMergedBranch();
	}
	else if (!analyzer.HasOption(PPCAnalyst::PPCAnalyzer::OPTION_CONDITIONAL_CONTINUE))
	{
		gpr.Flush();
		fpr.Flush();
		WriteExit(nextPC + 4);
	}
}

void Jit64::cmpXX(UGeckoInstruction inst)
{
	// USES_CR
	INSTRUCTION_START
	JITDISABLE(bJITIntegerOff);
	int a = inst.RA;
	int b = inst.RB;
	int crf = inst.CRFD;
	bool merge_branch = CheckMergedBranch(crf);

	OpArg comparand;
	bool signedCompare;
	if (inst.OPCD == 31)
	{
		// cmp / cmpl
		gpr.Lock(a, b);
		comparand = gpr.R(b);
		signedCompare = (inst.SUBOP10 == 0);
	}
	else
	{
		gpr.Lock(a);
		if (inst.OPCD == 10)
		{
			//cmpli
			comparand = Imm32((u32)inst.UIMM);
			signedCompare = false;
		}
		else if (inst.OPCD == 11)
		{
			//cmpi
			comparand = Imm32((u32)(s32)(s16)inst.UIMM);
			signedCompare = true;
		}
		else
		{
			signedCompare = false; // silence compiler warning
			PanicAlert("cmpXX");
		}
	}

	if (gpr.R(a).IsImm() && comparand.IsImm())
	{
		// Both registers contain immediate values, so we can pre-compile the compare result
		s64 compareResult = signedCompare ? (s64)gpr.R(a).SImm32() - (s64)comparand.SImm32() :
		                                    (u64)gpr.R(a).Imm32() - (u64)comparand.Imm32();
		if (compareResult == (s32)compareResult)
		{
			MOV(64, PPCSTATE(cr_val[crf]), Imm32((u32)compareResult));
		}
		else
		{
			MOV(64, R(RSCRATCH), Imm64(compareResult));
			MOV(64, PPCSTATE(cr_val[crf]), R(RSCRATCH));
		}

		if (merge_branch)
			DoMergedBranchImmediate(compareResult);
	}
	else
	{
		X64Reg input = RSCRATCH;
		if (signedCompare)
		{
			if (gpr.R(a).IsImm())
				MOV(64, R(input), Imm32(gpr.R(a).SImm32()));
			else
				MOVSX(64, 32, input, gpr.R(a));

			if (!comparand.IsImm())
			{
				MOVSX(64, 32, RSCRATCH2, comparand);
				comparand = R(RSCRATCH2);
			}
		}
		else
		{
			if (gpr.R(a).IsImm())
			{
				MOV(32, R(input), Imm32(gpr.R(a).Imm32()));
			}
			else if (comparand.IsImm() && !comparand.Imm32())
			{
				gpr.BindToRegister(a, true, false);
				input = gpr.RX(a);
			}
			else
			{
				MOVZX(64, 32, input, gpr.R(a));
			}

			if (comparand.IsImm())
			{
				// sign extension will ruin this, so store it in a register
				if (comparand.Imm32() & 0x80000000U)
				{
					MOV(32, R(RSCRATCH2), comparand);
					comparand = R(RSCRATCH2);
				}
			}
			else
			{
				gpr.BindToRegister(b, true, false);
				comparand = gpr.R(b);
			}
		}
		if (comparand.IsImm() && !comparand.Imm32())
		{
			MOV(64, PPCSTATE(cr_val[crf]), R(input));
			// Place the comparison next to the branch for macro-op fusion
			if (merge_branch)
				TEST(64, R(input), R(input));
		}
		else
		{
			SUB(64, R(input), comparand);
			MOV(64, PPCSTATE(cr_val[crf]), R(input));
		}

		if (merge_branch)
			DoMergedBranchCondition();
	}

	gpr.UnlockAll();
}

void Jit64::boolX(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITIntegerOff);
	int a = inst.RA, s = inst.RS, b = inst.RB;
	bool needs_test = false;
	_dbg_assert_msg_(DYNA_REC, inst.OPCD == 31, "Invalid boolX");

	if (gpr.R(s).IsImm() && gpr.R(b).IsImm())
	{
		const u32 rs_offset = gpr.R(s).Imm32();
		const u32 rb_offset = gpr.R(b).Imm32();

		if (inst.SUBOP10 == 28)       // andx
			gpr.SetImmediate32(a, rs_offset & rb_offset);
		else if (inst.SUBOP10 == 476) // nandx
			gpr.SetImmediate32(a, ~(rs_offset & rb_offset));
		else if (inst.SUBOP10 == 60)  // andcx
			gpr.SetImmediate32(a, rs_offset & (~rb_offset));
		else if (inst.SUBOP10 == 444) // orx
			gpr.SetImmediate32(a, rs_offset | rb_offset);
		else if (inst.SUBOP10 == 124) // norx
			gpr.SetImmediate32(a, ~(rs_offset | rb_offset));
		else if (inst.SUBOP10 == 412) // orcx
			gpr.SetImmediate32(a, rs_offset | (~rb_offset));
		else if (inst.SUBOP10 == 316) // xorx
			gpr.SetImmediate32(a, rs_offset ^ rb_offset);
		else if (inst.SUBOP10 == 284) // eqvx
			gpr.SetImmediate32(a, ~(rs_offset ^ rb_offset));
	}
	else if (s == b)
	{
		if ((inst.SUBOP10 == 28 /* andx */) || (inst.SUBOP10 == 444 /* orx */))
		{
			if (a != s)
			{
				gpr.Lock(a,s);
				gpr.BindToRegister(a, false, true);
				MOV(32, gpr.R(a), gpr.R(s));
			}
			else if (inst.Rc)
			{
				gpr.BindToRegister(a, true, false);
			}
			needs_test = true;
		}
		else if ((inst.SUBOP10 == 476 /* nandx */) || (inst.SUBOP10 == 124 /* norx */))
		{
			if (a != s)
			{
				gpr.Lock(a,s);
				gpr.BindToRegister(a, false, true);
				MOV(32, gpr.R(a), gpr.R(s));
			}
			else if (inst.Rc)
			{
				gpr.BindToRegister(a, true, true);
			}
			else
			{
				gpr.KillImmediate(a, true, true);
			}
			NOT(32, gpr.R(a));
			needs_test = true;
		}
		else if ((inst.SUBOP10 == 412 /* orcx */) || (inst.SUBOP10 == 284 /* eqvx */))
		{
			gpr.SetImmediate32(a, 0xFFFFFFFF);
		}
		else if ((inst.SUBOP10 == 60 /* andcx */) || (inst.SUBOP10 == 316 /* xorx */))
		{
			gpr.SetImmediate32(a, 0);
		}
		else
		{
			PanicAlert("WTF!");
		}
	}
	else if ((a == s) || (a == b))
	{
		gpr.Lock(a,((a == s) ? b : s));
		OpArg operand = ((a == s) ? gpr.R(b) : gpr.R(s));
		gpr.BindToRegister(a, true, true);

		if (inst.SUBOP10 == 28) // andx
		{
			AND(32, gpr.R(a), operand);
		}
		else if (inst.SUBOP10 == 476) // nandx
		{
			AND(32, gpr.R(a), operand);
			NOT(32, gpr.R(a));
			needs_test = true;
		}
		else if (inst.SUBOP10 == 60) // andcx
		{
			if (a == b)
			{
				NOT(32, gpr.R(a));
				AND(32, gpr.R(a), operand);
			}
			else
			{
				MOV(32, R(RSCRATCH), operand);
				NOT(32, R(RSCRATCH));
				AND(32, gpr.R(a), R(RSCRATCH));
			}
		}
		else if (inst.SUBOP10 == 444) // orx
		{
			OR(32, gpr.R(a), operand);
		}
		else if (inst.SUBOP10 == 124) // norx
		{
			OR(32, gpr.R(a), operand);
			NOT(32, gpr.R(a));
			needs_test = true;
		}
		else if (inst.SUBOP10 == 412) // orcx
		{
			if (a == b)
			{
				NOT(32, gpr.R(a));
				OR(32, gpr.R(a), operand);
			}
			else
			{
				MOV(32, R(RSCRATCH), operand);
				NOT(32, R(RSCRATCH));
				OR(32, gpr.R(a), R(RSCRATCH));
			}
		}
		else if (inst.SUBOP10 == 316) // xorx
		{
			XOR(32, gpr.R(a), operand);
		}
		else if (inst.SUBOP10 == 284) // eqvx
		{
			NOT(32, gpr.R(a));
			XOR(32, gpr.R(a), operand);
		}
		else
		{
			PanicAlert("WTF");
		}
	}
	else
	{
		gpr.Lock(a,s,b);
		gpr.BindToRegister(a, false, true);

		if (inst.SUBOP10 == 28) // andx
		{
			MOV(32, gpr.R(a), gpr.R(s));
			AND(32, gpr.R(a), gpr.R(b));
		}
		else if (inst.SUBOP10 == 476) // nandx
		{
			MOV(32, gpr.R(a), gpr.R(s));
			AND(32, gpr.R(a), gpr.R(b));
			NOT(32, gpr.R(a));
			needs_test = true;
		}
		else if (inst.SUBOP10 == 60) // andcx
		{
			MOV(32, gpr.R(a), gpr.R(b));
			NOT(32, gpr.R(a));
			AND(32, gpr.R(a), gpr.R(s));
		}
		else if (inst.SUBOP10 == 444) // orx
		{
			MOV(32, gpr.R(a), gpr.R(s));
			OR(32, gpr.R(a), gpr.R(b));
		}
		else if (inst.SUBOP10 == 124) // norx
		{
			MOV(32, gpr.R(a), gpr.R(s));
			OR(32, gpr.R(a), gpr.R(b));
			NOT(32, gpr.R(a));
			needs_test = true;
		}
		else if (inst.SUBOP10 == 412) // orcx
		{
			MOV(32, gpr.R(a), gpr.R(b));
			NOT(32, gpr.R(a));
			OR(32, gpr.R(a), gpr.R(s));
		}
		else if (inst.SUBOP10 == 316) // xorx
		{
			MOV(32, gpr.R(a), gpr.R(s));
			XOR(32, gpr.R(a), gpr.R(b));
		}
		else if (inst.SUBOP10 == 284) // eqvx
		{
			MOV(32, gpr.R(a), gpr.R(s));
			NOT(32, gpr.R(a));
			XOR(32, gpr.R(a), gpr.R(b));
		}
		else
		{
			PanicAlert("WTF!");
		}
	}
	if (inst.Rc)
		ComputeRC(gpr.R(a), needs_test);
	gpr.UnlockAll();
}

void Jit64::extsXx(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITIntegerOff);
	int a = inst.RA, s = inst.RS;
	int size = inst.SUBOP10 == 922 ? 16 : 8;

	if (gpr.R(s).IsImm())
	{
		gpr.SetImmediate32(a, (u32)(s32)(size == 16 ? (s16)gpr.R(s).Imm32() : (s8)gpr.R(s).Imm32()));
	}
	else
	{
		gpr.Lock(a, s);
		gpr.BindToRegister(a, a == s, true);
		MOVSX(32, size, gpr.RX(a), gpr.R(s));
	}
	if (inst.Rc)
		ComputeRC(gpr.R(a));
	gpr.UnlockAll();
}

void Jit64::subfic(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITIntegerOff);
	int a = inst.RA, d = inst.RD;
	gpr.Lock(a, d);
	gpr.BindToRegister(d, a == d, true);
	int imm = inst.SIMM_16;
	if (d == a)
	{
		if (imm == 0)
		{
			// Flags act exactly like subtracting from 0
			NEG(32, gpr.R(d));
			// Output carry is inverted
			FinalizeCarry(CC_NC);
		}
		else if (imm == -1)
		{
			NOT(32, gpr.R(d));
			// CA is always set in this case
			FinalizeCarry(true);
		}
		else
		{
			NOT(32, gpr.R(d));
			ADD(32, gpr.R(d), Imm32(imm+1));
			// Output carry is normal
			FinalizeCarry(CC_C);
		}
	}
	else
	{
		MOV(32, gpr.R(d), Imm32(imm));
		SUB(32, gpr.R(d), gpr.R(a));
		// Output carry is inverted
		FinalizeCarry(CC_NC);
	}
	gpr.UnlockAll();
	// This instruction has no RC flag
}

void Jit64::subfx(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITIntegerOff);
	int a = inst.RA, b = inst.RB, d = inst.RD;

	if (gpr.R(a).IsImm() && gpr.R(b).IsImm())
	{
		s32 i = gpr.R(b).SImm32(), j = gpr.R(a).SImm32();
		gpr.SetImmediate32(d, i - j);
		if (inst.OE)
			GenerateConstantOverflow((s64)i - (s64)j);
	}
	else
	{
		gpr.Lock(a, b, d);
		gpr.BindToRegister(d, (d == a || d == b), true);
		if (d == b)
		{
			SUB(32, gpr.R(d), gpr.R(a));
		}
		else if (d == a)
		{
			MOV(32, R(RSCRATCH), gpr.R(a));
			MOV(32, gpr.R(d), gpr.R(b));
			SUB(32, gpr.R(d), R(RSCRATCH));
		}
		else
		{
			MOV(32, gpr.R(d), gpr.R(b));
			SUB(32, gpr.R(d), gpr.R(a));
		}
		if (inst.OE)
			GenerateOverflow();
	}
	if (inst.Rc)
		ComputeRC(gpr.R(d), false);
	gpr.UnlockAll();
}

void Jit64::MultiplyImmediate(u32 imm, int a, int d, bool overflow)
{
	// simplest cases first
	if (imm == 0)
	{
		XOR(32, gpr.R(d), gpr.R(d));
		return;
	}

	if (imm == (u32)-1)
	{
		if (d != a)
			MOV(32, gpr.R(d), gpr.R(a));
		NEG(32, gpr.R(d));
		return;
	}

	// skip these if we need to check overflow flag
	if (!overflow)
	{
		// power of 2; just a shift
		if (IsPow2(imm))
		{
			u32 shift = IntLog2(imm);
			// use LEA if it saves an op
			if (d != a && shift <= 3 && shift >= 1 && gpr.R(a).IsSimpleReg())
			{
				LEA(32, gpr.RX(d), MScaled(gpr.RX(a), SCALE_1 << shift, 0));
			}
			else
			{
				if (d != a)
					MOV(32, gpr.R(d), gpr.R(a));
				if (shift)
					SHL(32, gpr.R(d), Imm8(shift));
			}
			return;
		}

		// We could handle factors of 2^N*3, 2^N*5, and 2^N*9 using lea+shl, but testing shows
		// it seems to be slower overall.
		static u8 lea_scales[3] = { 3, 5, 9 };
		for (int i = 0; i < 3; i++)
		{
			if (imm == lea_scales[i])
			{
				if (d != a)
					gpr.BindToRegister(a, true, false);
				LEA(32, gpr.RX(d), MComplex(gpr.RX(a), gpr.RX(a), SCALE_2 << i, 0));
				return;
			}
		}
	}

	// if we didn't find any better options
	IMUL(32, gpr.RX(d), gpr.R(a), Imm32(imm));
}

void Jit64::mulli(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITIntegerOff);
	int a = inst.RA, d = inst.RD;
	u32 imm = inst.SIMM_16;

	if (gpr.R(a).IsImm())
	{
		gpr.SetImmediate32(d, gpr.R(a).Imm32() * imm);
	}
	else
	{
		gpr.Lock(a, d);
		gpr.BindToRegister(d, (d == a), true);
		MultiplyImmediate(imm, a, d, false);
		gpr.UnlockAll();
	}
}

void Jit64::mullwx(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITIntegerOff);
	int a = inst.RA, b = inst.RB, d = inst.RD;

	if (gpr.R(a).IsImm() && gpr.R(b).IsImm())
	{
		s32 i = gpr.R(a).SImm32(), j = gpr.R(b).SImm32();
		gpr.SetImmediate32(d, i * j);
		if (inst.OE)
			GenerateConstantOverflow((s64)i * (s64)j);
	}
	else
	{
		gpr.Lock(a, b, d);
		gpr.BindToRegister(d, (d == a || d == b), true);
		if (gpr.R(a).IsImm() || gpr.R(b).IsImm())
		{
			u32 imm = gpr.R(a).IsImm() ? gpr.R(a).Imm32() : gpr.R(b).Imm32();
			int src = gpr.R(a).IsImm() ? b : a;
			MultiplyImmediate(imm, src, d, inst.OE);
		}
		else if (d == a)
		{
			IMUL(32, gpr.RX(d), gpr.R(b));
		}
		else if (d == b)
		{
			IMUL(32, gpr.RX(d), gpr.R(a));
		}
		else
		{
			MOV(32, gpr.R(d), gpr.R(b));
			IMUL(32, gpr.RX(d), gpr.R(a));
		}
		if (inst.OE)
			GenerateOverflow();
	}
	if (inst.Rc)
		ComputeRC(gpr.R(d));
	gpr.UnlockAll();
}

void Jit64::mulhwXx(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITIntegerOff);
	int a = inst.RA, b = inst.RB, d = inst.RD;
	bool sign = inst.SUBOP10 == 75;

	if (gpr.R(a).IsImm() && gpr.R(b).IsImm())
	{
		if (sign)
			gpr.SetImmediate32(d, (u32)((u64)(((s64)gpr.R(a).SImm32() * (s64)gpr.R(b).SImm32())) >> 32));
		else
			gpr.SetImmediate32(d, (u32)(((u64)gpr.R(a).Imm32() * (u64)gpr.R(b).Imm32()) >> 32));
	}
	else if (sign)
	{
		gpr.Lock(a, b, d);
		// no register choice
		gpr.FlushLockX(EDX, EAX);
		gpr.BindToRegister(d, d == a || d == b, true);
		MOV(32, R(EAX), gpr.R(a));
		gpr.KillImmediate(b, true, false);
		IMUL(32, gpr.R(b));
		MOV(32, gpr.R(d), R(EDX));
	}
	else
	{
		// Not faster for signed because we'd need two movsx.
		gpr.Lock(a, b, d);
		// We need to bind everything to registers since the top 32 bits need to be zero.
		int src = d == b ? a : b;
		gpr.BindToRegister(d, d == a || d == b, true);
		gpr.BindToRegister(src, true, false);
		if (d != a && d != b)
			MOV(32, gpr.R(d), gpr.R(a));
		IMUL(64, gpr.RX(d), gpr.R(src));
		SHR(64, gpr.R(d), Imm8(32));
	}
	if (inst.Rc)
		ComputeRC(gpr.R(d));
	gpr.UnlockAll();
	gpr.UnlockAllX();
}

void Jit64::divwux(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITIntegerOff);
	int a = inst.RA, b = inst.RB, d = inst.RD;

	if (gpr.R(a).IsImm() && gpr.R(b).IsImm())
	{
		if (gpr.R(b).Imm32() == 0)
		{
			gpr.SetImmediate32(d, 0);
			if (inst.OE)
				GenerateConstantOverflow(true);
		}
		else
		{
			gpr.SetImmediate32(d, gpr.R(a).Imm32() / gpr.R(b).Imm32());
			if (inst.OE)
				GenerateConstantOverflow(false);
		}
	}
	else if (gpr.R(b).IsImm())
	{
		u32 divisor = gpr.R(b).Imm32();
		if (divisor == 0)
		{
			gpr.SetImmediate32(d, 0);
			if (inst.OE)
				GenerateConstantOverflow(true);
		}
		else
		{
			u32 shift = 31;
			while (!(divisor & (1 << shift)))
				shift--;

			if (divisor == (u32)(1 << shift))
			{
				gpr.Lock(a, b, d);
				gpr.BindToRegister(d, d == a, true);
				if (d != a)
					MOV(32, gpr.R(d), gpr.R(a));
				if (shift)
					SHR(32, gpr.R(d), Imm8(shift));
			}
			else
			{
				u64 magic_dividend = 0x100000000ULL << shift;
				u32 magic = (u32)(magic_dividend / divisor);
				u32 max_quotient = magic >> shift;

				// Test for failure in round-up method
				if (((u64)(magic+1) * (max_quotient*divisor-1)) >> (shift + 32) != max_quotient-1)
				{
					// If failed, use slower round-down method
					gpr.Lock(a, b, d);
					gpr.BindToRegister(d, d == a, true);
					MOV(32, R(RSCRATCH), Imm32(magic));
					if (d != a)
						MOV(32, gpr.R(d), gpr.R(a));
					IMUL(64, gpr.RX(d), R(RSCRATCH));
					ADD(64, gpr.R(d), R(RSCRATCH));
					SHR(64, gpr.R(d), Imm8(shift+32));
				}
				else
				{
					// If success, use faster round-up method
					gpr.Lock(a, b, d);
					gpr.BindToRegister(a, true, false);
					gpr.BindToRegister(d, false, true);
					if (d == a)
					{
						MOV(32, R(RSCRATCH), Imm32(magic+1));
						IMUL(64, gpr.RX(d), R(RSCRATCH));
					}
					else
					{
						MOV(32, gpr.R(d), Imm32(magic+1));
						IMUL(64, gpr.RX(d), gpr.R(a));
					}
					SHR(64, gpr.R(d), Imm8(shift+32));
				}
			}
			if (inst.OE)
				GenerateConstantOverflow(false);
		}
	}
	else
	{
		gpr.Lock(a, b, d);
		// no register choice (do we need to do this?)
		gpr.FlushLockX(EAX, EDX);
		gpr.BindToRegister(d, (d == a || d == b), true);
		MOV(32, R(EAX), gpr.R(a));
		XOR(32, R(EDX), R(EDX));
		gpr.KillImmediate(b, true, false);
		CMP_or_TEST(32, gpr.R(b), Imm32(0));
		FixupBranch not_div_by_zero = J_CC(CC_NZ);
		MOV(32, gpr.R(d), R(EDX));
		if (inst.OE)
		{
			GenerateConstantOverflow(true);
		}
		FixupBranch end = J();
		SetJumpTarget(not_div_by_zero);
		DIV(32, gpr.R(b));
		MOV(32, gpr.R(d), R(EAX));
		if (inst.OE)
		{
			GenerateConstantOverflow(false);
		}
		SetJumpTarget(end);
	}
	if (inst.Rc)
		ComputeRC(gpr.R(d));
	gpr.UnlockAll();
	gpr.UnlockAllX();
}

void Jit64::divwx(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITIntegerOff);
	int a = inst.RA, b = inst.RB, d = inst.RD;

	if (gpr.R(a).IsImm() && gpr.R(b).IsImm())
	{
		s32 i = gpr.R(a).SImm32(), j = gpr.R(b).SImm32();
		if (j == 0 || (i == (s32)0x80000000 && j == -1))
		{
			gpr.SetImmediate32(d, (i >> 31) ^ j);
			if (inst.OE)
				GenerateConstantOverflow(true);
		}
		else
		{
			gpr.SetImmediate32(d, i / j);
			if (inst.OE)
				GenerateConstantOverflow(false);
		}
	}
	else
	{
		gpr.Lock(a, b, d);
		// no register choice
		gpr.FlushLockX(EAX, EDX);
		gpr.BindToRegister(d, (d == a || d == b), true);
		MOV(32, R(EAX), gpr.R(a));
		CDQ();
		gpr.BindToRegister(b, true, false);
		TEST(32, gpr.R(b), gpr.R(b));
		FixupBranch not_div_by_zero = J_CC(CC_NZ);
		MOV(32, gpr.R(d), R(EDX));
		if (inst.OE)
		{
			GenerateConstantOverflow(true);
		}
		FixupBranch end1 = J();
		SetJumpTarget(not_div_by_zero);
		CMP(32, gpr.R(b), R(EDX));
		FixupBranch not_div_by_neg_one = J_CC(CC_NZ);
		MOV(32, gpr.R(d), R(EAX));
		NEG(32, gpr.R(d));
		FixupBranch no_overflow = J_CC(CC_NO);
		XOR(32, gpr.R(d), gpr.R(d));
		if (inst.OE)
		{
			GenerateConstantOverflow(true);
		}
		FixupBranch end2 = J();
		SetJumpTarget(not_div_by_neg_one);
		IDIV(32, gpr.R(b));
		MOV(32, gpr.R(d), R(EAX));
		SetJumpTarget(no_overflow);
		if (inst.OE)
		{
			GenerateConstantOverflow(false);
		}
		SetJumpTarget(end1);
		SetJumpTarget(end2);
	}
	if (inst.Rc)
		ComputeRC(gpr.R(d));
	gpr.UnlockAll();
	gpr.UnlockAllX();
}

void Jit64::addx(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITIntegerOff);
	int a = inst.RA, b = inst.RB, d = inst.RD;
	bool needs_test = false;

	if (gpr.R(a).IsImm() && gpr.R(b).IsImm())
	{
		s32 i = gpr.R(a).SImm32(), j = gpr.R(b).SImm32();
		gpr.SetImmediate32(d, i + j);
		if (inst.OE)
			GenerateConstantOverflow((s64)i + (s64)j);
	}
	else if ((d == a) || (d == b))
	{
		int operand = ((d == a) ? b : a);
		gpr.Lock(a, b, d);
		gpr.BindToRegister(d, true);
		ADD(32, gpr.R(d), gpr.R(operand));
		if (inst.OE)
			GenerateOverflow();
	}
	else if (gpr.R(a).IsSimpleReg() && gpr.R(b).IsSimpleReg() && !inst.OE)
	{
		gpr.Lock(a, b, d);
		gpr.BindToRegister(d, false);
		LEA(32, gpr.RX(d), MRegSum(gpr.RX(a), gpr.RX(b)));
		needs_test = true;
	}
	else
	{
		gpr.Lock(a, b, d);
		gpr.BindToRegister(d, false);
		MOV(32, gpr.R(d), gpr.R(a));
		ADD(32, gpr.R(d), gpr.R(b));
		if (inst.OE)
			GenerateOverflow();
	}
	if (inst.Rc)
		ComputeRC(gpr.R(d), needs_test);
	gpr.UnlockAll();
}

void Jit64::arithXex(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITIntegerOff);
	bool regsource = !(inst.SUBOP10 & 64); // addex or subfex
	bool mex = !!(inst.SUBOP10 & 32);      // addmex/subfmex or addzex/subfzex
	bool add = !!(inst.SUBOP10 & 2);       // add or sub
	int a = inst.RA;
	int b = regsource ? inst.RB : a;
	int d = inst.RD;
	bool same_input_sub = !add && regsource && a == b;

	gpr.Lock(a, b, d);
	gpr.BindToRegister(d, !same_input_sub && (d == a || d == b));
	if (!js.carryFlagSet)
		JitGetAndClearCAOV(inst.OE);
	else
		UnlockFlags();

	bool invertedCarry = false;
	// Special case: subfe A, B, B is a common compiler idiom
	if (same_input_sub)
	{
		// Convert carry to borrow
		if (!js.carryFlagInverted)
			CMC();
		SBB(32, gpr.R(d), gpr.R(d));
		invertedCarry = true;
	}
	else if (!add && regsource && d == b)
	{
		if (!js.carryFlagInverted)
			CMC();
		if (d != b)
			MOV(32, gpr.R(d), gpr.R(b));
		SBB(32, gpr.R(d), gpr.R(a));
		invertedCarry = true;
	}
	else
	{
		OpArg source = regsource ? gpr.R(d == b ? a : b) : Imm32(mex ? 0xFFFFFFFF : 0);
		if (d != a && d != b)
			MOV(32, gpr.R(d), gpr.R(a));
		if (!add)
			NOT(32, gpr.R(d));
		// if the source is an immediate, we can invert carry by going from add -> sub and doing src = -1 - src
		if (js.carryFlagInverted && source.IsImm())
		{
			source = Imm32(-1 - source.SImm32());
			SBB(32, gpr.R(d), source);
			invertedCarry = true;
		}
		else
		{
			if (js.carryFlagInverted)
				CMC();
			ADC(32, gpr.R(d), source);
		}
	}
	FinalizeCarryOverflow(inst.OE, invertedCarry);
	if (inst.Rc)
		ComputeRC(gpr.R(d), false);
	gpr.UnlockAll();
}

void Jit64::arithcx(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITIntegerOff);
	bool add = !!(inst.SUBOP10 & 2); // add or sub
	int a = inst.RA, b = inst.RB, d = inst.RD;
	gpr.Lock(a, b, d);
	gpr.BindToRegister(d, d == a || d == b, true);

	if (d == a && d != b)
	{
		if (add)
		{
			ADD(32, gpr.R(d), gpr.R(b));
		}
		else
		{
			// special case, because sub isn't reversible
			MOV(32, R(RSCRATCH), gpr.R(a));
			MOV(32, gpr.R(d), gpr.R(b));
			SUB(32, gpr.R(d), R(RSCRATCH));
		}
	}
	else
	{
		if (d != b)
			MOV(32, gpr.R(d), gpr.R(b));
		if (add)
			ADD(32, gpr.R(d), gpr.R(a));
		else
			SUB(32, gpr.R(d), gpr.R(a));
	}

	FinalizeCarryOverflow(inst.OE, !add);
	if (inst.Rc)
		ComputeRC(gpr.R(d), false);
	gpr.UnlockAll();
}

void Jit64::rlwinmx(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITIntegerOff);
	int a = inst.RA;
	int s = inst.RS;

	if (gpr.R(s).IsImm())
	{
		u32 result = gpr.R(s).Imm32();
		if (inst.SH != 0)
			result = _rotl(result, inst.SH);
		result &= Helper_Mask(inst.MB, inst.ME);
		gpr.SetImmediate32(a, result);
		if (inst.Rc)
			ComputeRC(gpr.R(a));
	}
	else
	{
		bool left_shift = inst.SH && inst.MB == 0 && inst.ME == 31 - inst.SH;
		bool right_shift = inst.SH && inst.ME == 31 && inst.MB == 32 - inst.SH;
		u32 mask = Helper_Mask(inst.MB, inst.ME);
		bool simple_mask = mask == 0xff || mask == 0xffff;
		// In case of a merged branch, track whether or not we've set flags.
		// If not, we need to do a test later to get them.
		bool needs_test = true;
		// If we know the high bit can't be set, we can avoid doing a sign extend for flag storage.
		bool needs_sext = true;
		int mask_size = inst.ME - inst.MB + 1;

		gpr.Lock(a, s);
		gpr.BindToRegister(a, a == s);
		if (a != s && left_shift && gpr.R(s).IsSimpleReg() && inst.SH <= 3)
		{
			LEA(32, gpr.RX(a), MScaled(gpr.RX(s), SCALE_1 << inst.SH, 0));
		}
		// common optimized case: byte/word extract
		else if (simple_mask && !(inst.SH & (mask_size - 1)))
		{
			MOVZX(32, mask_size, gpr.RX(a), ExtractFromReg(s, inst.SH ? (32 - inst.SH) >> 3 : 0));
			needs_sext = false;
		}
		// another optimized special case: byte/word extract plus shift
		else if (((mask >> inst.SH) << inst.SH) == mask && !left_shift &&
		         ((mask >> inst.SH) == 0xff || (mask >> inst.SH) == 0xffff))
		{
			MOVZX(32, mask_size, gpr.RX(a), gpr.R(s));
			SHL(32, gpr.R(a), Imm8(inst.SH));
			needs_sext = inst.SH + mask_size >= 32;
		}
		else
		{
			if (a != s)
				MOV(32, gpr.R(a), gpr.R(s));

			if (left_shift)
			{
				SHL(32, gpr.R(a), Imm8(inst.SH));
			}
			else if (right_shift)
			{
				SHR(32, gpr.R(a), Imm8(inst.MB));
				needs_sext = false;
			}
			else
			{
				if (inst.SH != 0)
					ROL(32, gpr.R(a), Imm8(inst.SH));
				if (!(inst.MB == 0 && inst.ME == 31))
				{
					// we need flags if we're merging the branch
					if (inst.Rc && CheckMergedBranch(0))
						AND(32, gpr.R(a), Imm32(mask));
					else
						AndWithMask(gpr.RX(a), mask);
					needs_sext = inst.MB == 0;
					needs_test = false;
				}
			}
		}
		if (inst.Rc)
			ComputeRC(gpr.R(a), needs_test, needs_sext);
		gpr.UnlockAll();
	}
}


void Jit64::rlwimix(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITIntegerOff);
	int a = inst.RA;
	int s = inst.RS;

	if (gpr.R(a).IsImm() && gpr.R(s).IsImm())
	{
		u32 mask = Helper_Mask(inst.MB,inst.ME);
		gpr.SetImmediate32(a, (gpr.R(a).Imm32() & ~mask) | (_rotl(gpr.R(s).Imm32(),inst.SH) & mask));
		if (inst.Rc)
			ComputeRC(gpr.R(a));
	}
	else
	{
		gpr.Lock(a, s);
		u32 mask = Helper_Mask(inst.MB, inst.ME);
		bool needs_test = false;
		if (mask == 0 || (a == s && inst.SH == 0))
		{
			needs_test = true;
		}
		else if (mask == 0xFFFFFFFF)
		{
			gpr.BindToRegister(a, a == s, true);
			if (a != s)
				MOV(32, gpr.R(a), gpr.R(s));
			if (inst.SH)
				ROL(32, gpr.R(a), Imm8(inst.SH));
			needs_test = true;
		}
		else if(gpr.R(s).IsImm())
		{
			gpr.BindToRegister(a, true, true);
			AndWithMask(gpr.RX(a), ~mask);
			OR(32, gpr.R(a), Imm32(_rotl(gpr.R(s).Imm32(), inst.SH) & mask));
		}
		else if (inst.SH)
		{
			bool isLeftShift = mask == 0U - (1U << inst.SH);
			bool isRightShift = mask == (1U << inst.SH) - 1;
			if (gpr.R(a).IsImm())
			{
				u32 maskA = gpr.R(a).Imm32() & ~mask;
				gpr.BindToRegister(a, false, true);
				MOV(32, gpr.R(a), gpr.R(s));
				if (isLeftShift)
				{
					SHL(32, gpr.R(a), Imm8(inst.SH));
				}
				else if (isRightShift)
				{
					SHR(32, gpr.R(a), Imm8(32 - inst.SH));
				}
				else
				{
					ROL(32, gpr.R(a), Imm8(inst.SH));
					AND(32, gpr.R(a), Imm32(mask));
				}
				OR(32, gpr.R(a), Imm32(maskA));
			}
			else
			{
				// TODO: common cases of this might be faster with pinsrb or abuse of AH
				gpr.BindToRegister(a, true, true);
				MOV(32, R(RSCRATCH), gpr.R(s));
				if (isLeftShift)
				{
					SHL(32, R(RSCRATCH), Imm8(inst.SH));
					AndWithMask(gpr.RX(a), ~mask);
					OR(32, gpr.R(a), R(RSCRATCH));
				}
				else if (isRightShift)
				{
					SHR(32, R(RSCRATCH), Imm8(32 - inst.SH));
					AndWithMask(gpr.RX(a), ~mask);
					OR(32, gpr.R(a), R(RSCRATCH));
				}
				else
				{
					ROL(32, R(RSCRATCH), Imm8(inst.SH));
					XOR(32, R(RSCRATCH), gpr.R(a));
					AndWithMask(RSCRATCH, mask);
					XOR(32, gpr.R(a), R(RSCRATCH));
				}
			}
		}
		else
		{
			gpr.BindToRegister(a, true, true);
			XOR(32, gpr.R(a), gpr.R(s));
			AndWithMask(gpr.RX(a), ~mask);
			XOR(32, gpr.R(a), gpr.R(s));
		}
		if (inst.Rc)
			ComputeRC(gpr.R(a), needs_test);
		gpr.UnlockAll();
	}
}

void Jit64::rlwnmx(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITIntegerOff);
	int a = inst.RA, b = inst.RB, s = inst.RS;

	u32 mask = Helper_Mask(inst.MB, inst.ME);
	if (gpr.R(b).IsImm() && gpr.R(s).IsImm())
	{
		gpr.SetImmediate32(a, _rotl(gpr.R(s).Imm32(), gpr.R(b).Imm32() & 0x1F) & mask);
	}
	else
	{
		// no register choice
		gpr.FlushLockX(ECX);
		gpr.Lock(a, b, s);
		MOV(32, R(ECX), gpr.R(b));
		gpr.BindToRegister(a, (a == s), true);
		if (a != s)
		{
			MOV(32, gpr.R(a), gpr.R(s));
		}
		ROL(32, gpr.R(a), R(ECX));
		// we need flags if we're merging the branch
		if (inst.Rc && CheckMergedBranch(0))
			AND(32, gpr.R(a), Imm32(mask));
		else
			AndWithMask(gpr.RX(a), mask);
	}
	if (inst.Rc)
		ComputeRC(gpr.R(a), false);
	gpr.UnlockAll();
	gpr.UnlockAllX();
}

void Jit64::negx(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITIntegerOff);
	int a = inst.RA;
	int d = inst.RD;

	if (gpr.R(a).IsImm())
	{
		gpr.SetImmediate32(d, ~(gpr.R(a).Imm32()) + 1);
		if (inst.OE)
			GenerateConstantOverflow(gpr.R(d).Imm32() == 0x80000000);
	}
	else
	{
		gpr.Lock(a, d);
		gpr.BindToRegister(d, a == d, true);
		if (a != d)
			MOV(32, gpr.R(d), gpr.R(a));
		NEG(32, gpr.R(d));
		if (inst.OE)
			GenerateOverflow();
	}
	if (inst.Rc)
		ComputeRC(gpr.R(d), false);
	gpr.UnlockAll();
}

void Jit64::srwx(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITIntegerOff);
	int a = inst.RA;
	int b = inst.RB;
	int s = inst.RS;

	if (gpr.R(b).IsImm() && gpr.R(s).IsImm())
	{
		u32 amount = gpr.R(b).Imm32();
		gpr.SetImmediate32(a, (amount & 0x20) ? 0 : (gpr.R(s).Imm32() >> (amount & 0x1f)));
	}
	else
	{
		// no register choice
		gpr.FlushLockX(ECX);
		gpr.Lock(a, b, s);
		MOV(32, R(ECX), gpr.R(b));
		gpr.BindToRegister(a, a == s, true);
		if (a != s)
		{
			MOV(32, gpr.R(a), gpr.R(s));
		}
		SHR(64, gpr.R(a), R(ECX));
	}
	// Shift of 0 doesn't update flags, so we need to test just in case
	if (inst.Rc)
		ComputeRC(gpr.R(a));
	gpr.UnlockAll();
	gpr.UnlockAllX();
}

void Jit64::slwx(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITIntegerOff);
	int a = inst.RA;
	int b = inst.RB;
	int s = inst.RS;

	if (gpr.R(b).IsImm() && gpr.R(s).IsImm())
	{
		u32 amount = gpr.R(b).Imm32();
		gpr.SetImmediate32(a, (amount & 0x20) ? 0 : gpr.R(s).Imm32() << (amount & 0x1f));
		if (inst.Rc)
			ComputeRC(gpr.R(a));
	}
	else
	{
		// no register choice
		gpr.FlushLockX(ECX);
		gpr.Lock(a, b, s);
		MOV(32, R(ECX), gpr.R(b));
		gpr.BindToRegister(a, a == s, true);
		if (a != s)
			MOV(32, gpr.R(a), gpr.R(s));
		SHL(64, gpr.R(a), R(ECX));
		if (inst.Rc)
		{
			AND(32, gpr.R(a), gpr.R(a));
			ComputeRC(gpr.R(a), false);
		}
		else
		{
			MOVZX(64, 32, gpr.RX(a), gpr.R(a));
		}
		gpr.UnlockAll();
		gpr.UnlockAllX();
	}
}

void Jit64::srawx(UGeckoInstruction inst)
{
	// USES_XER
	INSTRUCTION_START
	JITDISABLE(bJITIntegerOff);
	int a = inst.RA;
	int b = inst.RB;
	int s = inst.RS;

	gpr.FlushLockX(ECX);
	gpr.Lock(a, s, b);
	gpr.BindToRegister(a, (a == s || a == b), true);
	MOV(32, R(ECX), gpr.R(b));
	if (a != s)
		MOV(32, gpr.R(a), gpr.R(s));
	SHL(64, gpr.R(a), Imm8(32));
	SAR(64, gpr.R(a), R(ECX));
	if (js.op->wantsCA)
	{
		MOV(32, R(RSCRATCH), gpr.R(a));
		SHR(64, gpr.R(a), Imm8(32));
		TEST(32, gpr.R(a), R(RSCRATCH));
	}
	else
	{
		SHR(64, gpr.R(a), Imm8(32));
	}
	FinalizeCarry(CC_NZ);
	if (inst.Rc)
		ComputeRC(gpr.R(a));
	gpr.UnlockAll();
	gpr.UnlockAllX();
}

void Jit64::srawix(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITIntegerOff);
	int a = inst.RA;
	int s = inst.RS;
	int amount = inst.SH;

	if (amount != 0)
	{
		gpr.Lock(a, s);
		gpr.BindToRegister(a, a == s, true);
		if (!js.op->wantsCA)
		{
			if (a != s)
				MOV(32, gpr.R(a), gpr.R(s));
			SAR(32, gpr.R(a), Imm8(amount));
		}
		else
		{
			MOV(32, R(RSCRATCH), gpr.R(s));
			if (a != s)
				MOV(32, gpr.R(a), R(RSCRATCH));
			// some optimized common cases that can be done in slightly fewer ops
			if (amount == 1)
			{
				SHR(32, R(RSCRATCH), Imm8(31));         // sign
				AND(32, R(RSCRATCH), gpr.R(a));         // (sign && carry)
				SAR(32, gpr.R(a), Imm8(1));
				MOV(8, PPCSTATE(xer_ca), R(RSCRATCH));  // XER.CA = sign && carry, aka (input&0x80000001) == 0x80000001
			}
			else
			{
				SAR(32, gpr.R(a), Imm8(amount));
				SHL(32, R(RSCRATCH), Imm8(32 - amount));
				TEST(32, R(RSCRATCH), gpr.R(a));
				FinalizeCarry(CC_NZ);
			}
		}
	}
	else
	{
		gpr.Lock(a, s);
		FinalizeCarry(false);
		gpr.BindToRegister(a, a == s, true);

		if (a != s)
			MOV(32, gpr.R(a), gpr.R(s));
	}
	if (inst.Rc)
		ComputeRC(gpr.R(a));
	gpr.UnlockAll();
}

// count leading zeroes
void Jit64::cntlzwx(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITIntegerOff);
	int a = inst.RA;
	int s = inst.RS;
	bool needs_test = false;

	if (gpr.R(s).IsImm())
	{
		u32 mask = 0x80000000;
		u32 i = 0;
		for (; i < 32; i++, mask >>= 1)
		{
			if (gpr.R(s).Imm32() & mask)
				break;
		}
		gpr.SetImmediate32(a, i);
	}
	else
	{
		gpr.Lock(a, s);
		gpr.BindToRegister(a, a == s, true);
		if (cpu_info.bLZCNT)
		{
			LZCNT(32, gpr.RX(a), gpr.R(s));
			needs_test = true;
		}
		else
		{
			BSR(32, gpr.RX(a), gpr.R(s));
			FixupBranch gotone = J_CC(CC_NZ);
			MOV(32, gpr.R(a), Imm32(63));
			SetJumpTarget(gotone);
			XOR(32, gpr.R(a), Imm8(0x1f));  // flip order
		}
	}

	if (inst.Rc)
		ComputeRC(gpr.R(a), needs_test, false);
	gpr.UnlockAll();
}

void Jit64::twX(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITIntegerOff);

	s32 a = inst.RA;

	if (inst.OPCD == 3) // twi
	{
		gpr.KillImmediate(a, true, false);
		CMP(32, gpr.R(a), Imm32((s32)(s16)inst.SIMM_16));
	}
	else // tw
	{
		gpr.BindToRegister(a, true, false);
		CMP(32, gpr.R(a), gpr.R(inst.RB));
	}

	std::vector<FixupBranch> fixups;
	CCFlags conditions[] = { CC_A, CC_B, CC_E, CC_G, CC_L };

	for (int i = 0; i < 5; i++)
	{
		if (inst.TO & (1 << i))
		{
			FixupBranch f = J_CC(conditions[i], true);
			fixups.push_back(f);
		}
	}
	FixupBranch dont_trap = J();

	for (const FixupBranch& fixup : fixups)
	{
		SetJumpTarget(fixup);
	}
	LOCK();
	OR(32, PPCSTATE(Exceptions), Imm32(EXCEPTION_PROGRAM));

	gpr.Flush(FLUSH_MAINTAIN_STATE);
	fpr.Flush(FLUSH_MAINTAIN_STATE);

	WriteExceptionExit();

	SetJumpTarget(dont_trap);

	if (!analyzer.HasOption(PPCAnalyst::PPCAnalyzer::OPTION_CONDITIONAL_CONTINUE))
	{
		gpr.Flush();
		fpr.Flush();
		WriteExit(js.compilerPC + 4);
	}
}
