#include "ppsspp_config.h"
#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)

#include "Core/MIPS/x86/IRToX86.h"

namespace MIPSComp {

// Initial attempt at converting IR directly to x86.
// This is intended to be an easy way to benefit from the IR with the current infrastructure.
// Later tries may go across multiple blocks and a different representation.

struct GPRMapping {
	Gen::OpArg dest;
	Gen::OpArg src1;
	Gen::OpArg src2;
};

struct FPRMapping {
	Gen::OpArg dest;
	Gen::OpArg src1;
	Gen::OpArg src2;
};


class GreedyRegallocGPR {
public:
	GPRMapping Map(IRInst inst, const IRMeta &meta);

private:
	
};


GPRMapping GreedyRegallocGPR::Map(IRInst inst, const IRMeta &meta) {
	GPRMapping mapping;
	if (meta.types[0] == 'G') {

	}
	// etc..
	return mapping;
}

// Every 4 registers can also be mapped into an SSE register.
// When changing from single to vec4 mapping, we'll just flush, for now.
class GreedyRegallocFPR {
public:
	FPRMapping Map(IRInst inst, const IRMeta &meta);
};

FPRMapping GreedyRegallocFPR::Map(IRInst inst, const IRMeta &meta) {
	FPRMapping mapping;

	return mapping;
}


// This requires that ThreeOpToTwoOp has been run as the last pass.
void IRToX86::ConvertIRToNative(const IRInst *instructions, int count, const u32 *constants) {
	// Set up regcaches
	using namespace Gen;

	GreedyRegallocGPR gprAlloc;
	GreedyRegallocFPR fprAlloc;

	// Loop through all the instructions, emitting code as we go.
	// Note that we do need to implement them all - fallbacks are not permitted.
	for (int i = 0; i < count; i++) {
		const IRInst *inst = &instructions[i];
		const IRMeta &meta = *GetIRMeta(inst->op);
		GPRMapping gpr = gprAlloc.Map(*inst, meta);
		FPRMapping fpr = fprAlloc.Map(*inst, meta);

		bool symmetric = false;
		switch (inst->op) {
		case IROp::Nop:
			_assert_(false);
			break;

			// Output-only
		case IROp::SetConst:
			code_->MOV(32, gpr.dest, Imm32(constants[inst->src1]));
			break;
		case IROp::SetConstF:
			code_->MOV(32, gpr.dest, Imm32(constants[inst->src1]));
			break;

			// Add gets to be special cased because we have LEA.
		case IROp::Add:
			if (gpr.dest.IsSimpleReg() && gpr.src1.IsSimpleReg() && gpr.src2.IsSimpleReg()) {
				code_->LEA(32, gpr.dest.GetSimpleReg(), MRegSum(gpr.src1.GetSimpleReg(), gpr.src2.GetSimpleReg()));
				break;
			}
			// Else fall through.
			// 3-op arithmetic that directly corresponds to x86
			// (often 2-op in practice if src1 == dst). x86 only does 2-op though so some of these will need splitting.
		case IROp::Sub:
		case IROp::And:
		case IROp::Or:
		case IROp::Xor:
			if (inst->src1 != inst->dest) {
				code_->MOV(32, gpr.dest, gpr.src1);
			}
			// Emit directly
			switch (inst->op) {
			case IROp::Add: code_->ADD(32, gpr.dest, gpr.src2); break;
			case IROp::Sub: code_->SUB(32, gpr.dest, gpr.src2); break;
			case IROp::And: code_->AND(32, gpr.dest, gpr.src2); break;
			case IROp::Or: code_->OR(32, gpr.dest, gpr.src2); break;
			case IROp::Xor: code_->XOR(32, gpr.dest, gpr.src2); break;
			}
			break;

			// Variable shifts.
		case IROp::Shl:
		case IROp::Shr:
		case IROp::Sar:
		case IROp::Ror:

		case IROp::Slt:
		case IROp::SltU:
		case IROp::MovZ:
		case IROp::MovNZ:
		case IROp::Max:
		case IROp::Min:
			break;

			// 2-op arithmetic with constant
		case IROp::AddConst:
		case IROp::SubConst:
		case IROp::AndConst:
		case IROp::OrConst:
		case IROp::XorConst:
		case IROp::SltConst:
		case IROp::SltUConst:

			// 2-op arithmetic with immediate
		case IROp::ShlImm:
		case IROp::ShrImm:
		case IROp::SarImm:
		case IROp::RorImm:

			// 2-op arithmetic
		case IROp::Mov:
			code_->MOV(32, gpr.dest, gpr.src1);
			break;

		case IROp::Neg:
		case IROp::Not:
		case IROp::Ext8to32:
		case IROp::Ext16to32:
		case IROp::ReverseBits:
		case IROp::BSwap16:
		case IROp::BSwap32:
		case IROp::Clz:
			if (inst->dest != inst->src1) {
				code_->NEG(32, gpr.dest); break;
			}
			break;
				// Multiplier control
		case IROp::MtLo:
		case IROp::MtHi:
		case IROp::MfLo:
		case IROp::MfHi:
		case IROp::Mult:
		case IROp::MultU:
		case IROp::Madd:
		case IROp::MaddU:
		case IROp::Msub:
		case IROp::MsubU:
		case IROp::Div:
		case IROp::DivU:

			// Memory access
		case IROp::Load8:
		case IROp::Load8Ext:
		case IROp::Load16:
		case IROp::Load16Ext:
		case IROp::Load32:
		case IROp::LoadFloat:
		case IROp::Store8:
		case IROp::Store16:
		case IROp::Store32:
		case IROp::StoreFloat:
		case IROp::LoadVec4:
		case IROp::StoreVec4:

			// Output-only SIMD functions
		case IROp::Vec4Init:
		case IROp::Vec4Shuffle:

			// 2-op SIMD functions
		case IROp::Vec4Mov:
			code_->MOVAPS(fpr.dest.GetSimpleReg(), fpr.src1);
			break;
		case IROp::Vec4Neg:
		case IROp::Vec4Abs:
			break;
		case IROp::Vec4ClampToZero:
			code_->PXOR(XMM0, R(XMM0));
			code_->PMAXSW(XMM0, fpr.src1);
			code_->MOVAPD(fpr.dest, XMM0);
			break;
		case IROp::Vec4DuplicateUpperBitsAndShift1:
		case IROp::Vec2ClampToZero:

			// 3-op SIMD functions
		case IROp::Vec4Add:
		case IROp::Vec4Sub:
		case IROp::Vec4Mul:
		case IROp::Vec4Div:

		case IROp::Vec4Scale:
		case IROp::Vec4Dot:

			// Pack-unpack
		case IROp::Vec2Unpack16To31:
		case IROp::Vec2Unpack16To32:
		case IROp::Vec4Unpack8To32:
		case IROp::Vec2Pack32To16:
		case IROp::Vec2Pack31To16:
		case IROp::Vec4Pack32To8:
		case IROp::Vec4Pack31To8:

		case IROp::FCmpVfpuBit:
		case IROp::FCmpVfpuAggregate:
		case IROp::FCmovVfpuCC:

			// Trancendental functions (non-simd)
		case IROp::FSin:
		case IROp::FCos:
		case IROp::FRSqrt:
		case IROp::FRecip:
		case IROp::FAsin:

			// 3-Op FP
		case IROp::FAdd:
		case IROp::FSub:
		case IROp::FMul:
		case IROp::FDiv:
		case IROp::FMin:
		case IROp::FMax:

			// 2-Op FP
		case IROp::FMov:
		case IROp::FAbs:
		case IROp::FSqrt:
		case IROp::FNeg:
		case IROp::FSat0_1:
		case IROp::FSatMinus1_1:
		case IROp::FSign:
		case IROp::FCeil:
		case IROp::FFloor:
		case IROp::FCmp:
		case IROp::FCvtSW:
		case IROp::FCvtWS:
		case IROp::FRound:
		case IROp::FTrunc:

			// Cross moves
		case IROp::FMovFromGPR:
		case IROp::FMovToGPR:
		case IROp::FpCondToReg:
		case IROp::VfpuCtrlToReg:

			// VFPU flag/control
		case IROp::SetCtrlVFPU:
		case IROp::SetCtrlVFPUReg:
		case IROp::SetCtrlVFPUFReg:
		case IROp::ZeroFpCond:

			// Block Exits
		case IROp::ExitToConst:
		case IROp::ExitToReg:
		case IROp::ExitToConstIfEq:
		case IROp::ExitToConstIfNeq:
		case IROp::ExitToConstIfGtZ:
		case IROp::ExitToConstIfGeZ:
		case IROp::ExitToConstIfLtZ:
		case IROp::ExitToConstIfLeZ:
		case IROp::ExitToPC:

			// Utilities
		case IROp::Downcount:
		case IROp::SetPC:
		case IROp::SetPCConst:
		case IROp::Syscall:
		case IROp::Interpret:  // SLOW fallback. Can be made faster.
		case IROp::CallReplacement:
		case IROp::Break:
		default:
			break;
			}
		}
	}


}  // namespace

#endif // PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
