#include "RegisterAllocator.hpp"

#include "Error.hpp"

namespace SoftWire
{
	RegisterAllocator::Allocation RegisterAllocator::EAX;
	RegisterAllocator::Allocation RegisterAllocator::ECX;
	RegisterAllocator::Allocation RegisterAllocator::EDX;
	RegisterAllocator::Allocation RegisterAllocator::EBX;
	RegisterAllocator::Allocation RegisterAllocator::ESI;
	RegisterAllocator::Allocation RegisterAllocator::EDI;

	RegisterAllocator::Allocation RegisterAllocator::MM0;
	RegisterAllocator::Allocation RegisterAllocator::MM1;
	RegisterAllocator::Allocation RegisterAllocator::MM2;
	RegisterAllocator::Allocation RegisterAllocator::MM3;
	RegisterAllocator::Allocation RegisterAllocator::MM4;
	RegisterAllocator::Allocation RegisterAllocator::MM5;
	RegisterAllocator::Allocation RegisterAllocator::MM6;
	RegisterAllocator::Allocation RegisterAllocator::MM7;

	RegisterAllocator::Allocation RegisterAllocator::XMM0;
	RegisterAllocator::Allocation RegisterAllocator::XMM1;
	RegisterAllocator::Allocation RegisterAllocator::XMM2;
	RegisterAllocator::Allocation RegisterAllocator::XMM3;
	RegisterAllocator::Allocation RegisterAllocator::XMM4;
	RegisterAllocator::Allocation RegisterAllocator::XMM5;
	RegisterAllocator::Allocation RegisterAllocator::XMM6;
	RegisterAllocator::Allocation RegisterAllocator::XMM7;

	RegisterAllocator::RegisterAllocator()
	{
		freeAll();
	}

	RegisterAllocator::~RegisterAllocator()
	{
		freeAll();
	}

	const OperandREG8 RegisterAllocator::r8(const OperandREF &ref, bool copy)
	{
		OperandREG32 reg = r32(ref, copy);

		// Make sure we only have al, cl, dl or bl
		if(reg.reg == Encoding::ESI ||
		   reg.reg == Encoding::EDI)
		{
			spill(reg);

			// Need to spill one of al, cl, dl or bl
			Encoding::Reg candidate;
			unsigned int priority = 0xFFFFFFFF - 2;   // Don't spill most recently used

			if(EAX.priority < priority) {priority = EAX.priority; candidate = Encoding::EAX;}
			if(ECX.priority < priority) {priority = ECX.priority; candidate = Encoding::ECX;}
			if(EDX.priority < priority) {priority = EDX.priority; candidate = Encoding::EDX;}
			if(EBX.priority < priority) {priority = EBX.priority; candidate = Encoding::EBX;}

			spill(R32(candidate));

			return (OperandREG8)assign(R32(candidate), ref, copy, 1);
		}

		return (OperandREG8)reg;
	}

	const OperandR_M8 RegisterAllocator::m8(const OperandREF &ref)
	{
		return (OperandR_M8)m32(ref, 1);
	}

	const OperandREG16 RegisterAllocator::r16(const OperandREF &ref, bool copy)
	{
		return (OperandREG16)r32(ref, copy, 2);
	}

	const OperandR_M16 RegisterAllocator::m16(const OperandREF &ref)
	{
		return (OperandR_M16)m32(ref, 2);
	}

	const OperandREG32 &RegisterAllocator::r32(const OperandREF &ref, bool copy, int partial)
	{
		if(ref == 0 && copy) throw Error("Cannot dereference 0");

		// Check if already allocated
		     if(EAX.reference == ref) {return access(eax);}
		else if(ECX.reference == ref) {return access(ecx);}
		else if(EDX.reference == ref) {return access(edx);}
		else if(EBX.reference == ref) {return access(ebx);}
		else if(ESI.reference == ref) {return access(esi);}
		else if(EDI.reference == ref) {return access(edi);}

		// Search for free registers
		     if(EAX.reference == 0 && EAX.priority == 0) return assign(eax, ref, copy, partial);
		else if(ECX.reference == 0 && ECX.priority == 0) return assign(ecx, ref, copy, partial);
		else if(EDX.reference == 0 && EDX.priority == 0) return assign(edx, ref, copy, partial);
		else if(EBX.reference == 0 && EBX.priority == 0) return assign(ebx, ref, copy, partial);
		else if(ESI.reference == 0 && ESI.priority == 0) return assign(esi, ref, copy, partial);
		else if(EDI.reference == 0 && EDI.priority == 0) return assign(edi, ref, copy, partial);

		// Need to spill one
		Encoding::Reg candidate;
		unsigned int priority = 0xFFFFFFFF - 2;   // Don't spill most recently used

		if(EAX.priority < priority) {priority = EAX.priority; candidate = Encoding::EAX;}
		if(ECX.priority < priority) {priority = ECX.priority; candidate = Encoding::ECX;}
		if(EDX.priority < priority) {priority = EDX.priority; candidate = Encoding::EDX;}
		if(EBX.priority < priority) {priority = EBX.priority; candidate = Encoding::EBX;}
		if(ESI.priority < priority) {priority = ESI.priority; candidate = Encoding::ESI;}
		if(EDI.priority < priority) {priority = EDI.priority; candidate = Encoding::EDI;}

		spill(R32(candidate));

		return assign(R32(candidate), ref, copy, partial);
	}

	const OperandR_M32 RegisterAllocator::m32(const OperandREF &ref, int partial)
	{
		if(ref == 0) throw Error("Cannot dereference 0");

		// Check if already allocated
		     if(EAX.reference == ref) return access(eax);
		else if(ECX.reference == ref) return access(ecx);
		else if(EDX.reference == ref) return access(edx);
		else if(EBX.reference == ref) return access(ebx);
		else if(ESI.reference == ref) return access(esi);
		else if(EDI.reference == ref) return access(edi);

		return (OperandR_M32)dword_ptr [ref];
	}

	const OperandREG32 &RegisterAllocator::allocate(const OperandREG32 &reg, const OperandREF &ref, int partial)
	{
		if(X32(reg).reference != 0)
		{
			throw Error("%s not available for register allocation", reg.string());
		}

		X32(reg).reference = ref;
		X32(reg).partial = partial;

		return access(reg);
	}

	const OperandREG32 &RegisterAllocator::assign(const OperandREG32 &reg, const OperandREF &ref, bool copy, int partial)
	{
		allocate(reg, ref, partial);

		if(copy)
		{
			     if(partial == 1) mov((OperandREG8)reg, byte_ptr [ref]);
			else if(partial == 2) mov((OperandREG16)reg, word_ptr [ref]); 
			else                  mov(reg, dword_ptr [ref]);
		}

		return reg;
	}

	const OperandREG32 &RegisterAllocator::access(const OperandREG32 &reg)
	{
		// Decrease priority of other registers
		if(reg.reg != Encoding::EAX && EAX.priority) EAX.priority--;
		if(reg.reg != Encoding::ECX && ECX.priority) ECX.priority--;
		if(reg.reg != Encoding::EDX && EDX.priority) EDX.priority--;
		if(reg.reg != Encoding::EBX && EBX.priority) EBX.priority--;
		if(reg.reg != Encoding::ESI && ESI.priority) ESI.priority--;
		if(reg.reg != Encoding::EDI && EDI.priority) EDI.priority--;

		// Give highest priority
		X32(reg).priority = 0xFFFFFFFF;

		return reg;
	}

	void RegisterAllocator::free(const OperandREG32 &reg)
	{
		X32(reg).free();
	}

	void RegisterAllocator::spill(const OperandREG32 &reg)
	{
		if(X32(reg).reference != 0)
		{
				if(X32(reg).partial == 1) mov(byte_ptr [X32(reg).reference], (OperandREG8)reg);
			else if(X32(reg).partial == 2) mov(word_ptr [X32(reg).reference], (OperandREG16)reg);
			else                           mov(dword_ptr [X32(reg).reference], reg);
		}
		
		free(reg);
	}

	const OperandMMREG &RegisterAllocator::r64(const OperandREF &ref, bool copy)
	{
		if(ref == 0 && copy) throw Error("Cannot dereference 0");

		// Check if already allocated
		     if(MM0.reference == ref) {return access(mm0);}
		else if(MM1.reference == ref) {return access(mm1);}
		else if(MM2.reference == ref) {return access(mm2);}
		else if(MM3.reference == ref) {return access(mm3);}
		else if(MM4.reference == ref) {return access(mm4);}
		else if(MM5.reference == ref) {return access(mm5);}
		else if(MM6.reference == ref) {return access(mm6);}
		else if(MM7.reference == ref) {return access(mm7);}

		// Search for free registers
		     if(MM0.reference == 0 && MM0.priority == 0) return assign(mm0, ref, copy);
		else if(MM1.reference == 0 && MM1.priority == 0) return assign(mm1, ref, copy);
		else if(MM2.reference == 0 && MM2.priority == 0) return assign(mm2, ref, copy);
		else if(MM3.reference == 0 && MM3.priority == 0) return assign(mm3, ref, copy);
		else if(MM4.reference == 0 && MM4.priority == 0) return assign(mm4, ref, copy);
		else if(MM5.reference == 0 && MM5.priority == 0) return assign(mm5, ref, copy);
		else if(MM6.reference == 0 && MM6.priority == 0) return assign(mm6, ref, copy);
		else if(MM7.reference == 0 && MM7.priority == 0) return assign(mm7, ref, copy);

		// Need to spill one
		Encoding::Reg candidate;
		unsigned int priority = 0xFFFFFFFF - 2;   // Don't spill most recently used

		if(MM0.priority < priority) {priority = MM0.priority; candidate = Encoding::MM0;}
		if(MM1.priority < priority) {priority = MM1.priority; candidate = Encoding::MM1;}
		if(MM2.priority < priority) {priority = MM2.priority; candidate = Encoding::MM2;}
		if(MM3.priority < priority) {priority = MM3.priority; candidate = Encoding::MM3;}
		if(MM4.priority < priority) {priority = MM4.priority; candidate = Encoding::MM4;}
		if(MM5.priority < priority) {priority = MM5.priority; candidate = Encoding::MM5;}
		if(MM6.priority < priority) {priority = MM6.priority; candidate = Encoding::MM6;}
		if(MM7.priority < priority) {priority = MM7.priority; candidate = Encoding::MM7;}

		spill(R64(candidate));

		return assign(R64(candidate), ref, copy);
	}

	const OperandR_M64 RegisterAllocator::m64(const OperandREF &ref)
	{
		if(ref == 0) throw Error("Cannot dereference 0");

		// Check if already allocated
		     if(MM0.reference == ref) return access(mm0);
		else if(MM1.reference == ref) return access(mm1);
		else if(MM2.reference == ref) return access(mm2);
		else if(MM3.reference == ref) return access(mm3);
		else if(MM4.reference == ref) return access(mm4);
		else if(MM5.reference == ref) return access(mm5);
		else if(MM6.reference == ref) return access(mm6);
		else if(MM7.reference == ref) return access(mm7);

		return (OperandR_M64)qword_ptr [ref];
	}

	const OperandMMREG &RegisterAllocator::allocate(const OperandMMREG &reg, const OperandREF &ref)
	{
		if(X64(reg).reference != 0)
		{
			throw Error("%s not available for register allocation", reg.string());
		}

		X64(reg).reference = ref;

		return access(reg);
	}

	const OperandMMREG &RegisterAllocator::assign(const OperandMMREG &reg, const OperandREF &ref, bool copy)
	{
		allocate(reg, ref);

		if(copy)
		{
			movq(reg, qword_ptr [ref]);
		}

		return reg;
	}

	const OperandMMREG &RegisterAllocator::access(const OperandMMREG &reg)
	{
		// Decrease priority of other registers
		if(reg.reg != Encoding::MM0 && MM0.priority) MM0.priority--;
		if(reg.reg != Encoding::MM1 && MM1.priority) MM1.priority--;
		if(reg.reg != Encoding::MM2 && MM2.priority) MM2.priority--;
		if(reg.reg != Encoding::MM3 && MM3.priority) MM3.priority--;
		if(reg.reg != Encoding::MM4 && MM4.priority) MM4.priority--;
		if(reg.reg != Encoding::MM5 && MM5.priority) MM5.priority--;
		if(reg.reg != Encoding::MM6 && MM6.priority) MM6.priority--;
		if(reg.reg != Encoding::MM7 && MM7.priority) MM7.priority--;

		// Give highest priority
		X64(reg).priority = 0xFFFFFFFF;

		return reg;
	}

	void RegisterAllocator::free(const OperandMMREG &reg)
	{
		bool free = (X64(reg).priority != 0);

		X64(reg).free();

		if(free)
		{
			if(!MM0.priority &&
			   !MM1.priority &&
			   !MM2.priority &&
			   !MM3.priority &&
			   !MM4.priority &&
			   !MM5.priority &&
			   !MM6.priority &&
			   !MM7.priority)
			{
				emms();
			}
		}
	}

	void RegisterAllocator::spill(const OperandMMREG &reg)
	{
		if(X64(reg).reference != 0)
		{
			movq(qword_ptr [X64(reg).reference], reg);
		}

		free(reg);
	}

	const OperandXMMREG &RegisterAllocator::r128(const OperandREF &ref, bool copy, bool ss)
	{
		if(ref == 0 && copy) throw Error("Cannot dereference 0");

		// Check if already allocated
		     if(XMM0.reference == ref) {return access(xmm0);}
		else if(XMM1.reference == ref) {return access(xmm1);}
		else if(XMM2.reference == ref) {return access(xmm2);}
		else if(XMM3.reference == ref) {return access(xmm3);}
		else if(XMM4.reference == ref) {return access(xmm4);}
		else if(XMM5.reference == ref) {return access(xmm5);}
		else if(XMM6.reference == ref) {return access(xmm6);}
		else if(XMM7.reference == ref) {return access(xmm7);}

		// Search for free registers
		     if(XMM0.reference == 0 && XMM0.priority == 0) return assign(xmm0, ref, copy, ss);
		else if(XMM1.reference == 0 && XMM1.priority == 0) return assign(xmm1, ref, copy, ss);
		else if(XMM2.reference == 0 && XMM2.priority == 0) return assign(xmm2, ref, copy, ss);
		else if(XMM3.reference == 0 && XMM3.priority == 0) return assign(xmm3, ref, copy, ss);
		else if(XMM4.reference == 0 && XMM4.priority == 0) return assign(xmm4, ref, copy, ss);
		else if(XMM5.reference == 0 && XMM5.priority == 0) return assign(xmm5, ref, copy, ss);
		else if(XMM6.reference == 0 && XMM6.priority == 0) return assign(xmm6, ref, copy, ss);
		else if(XMM7.reference == 0 && XMM7.priority == 0) return assign(xmm7, ref, copy, ss);

		// Need to spill one
		Encoding::Reg candidate;
		unsigned int priority = 0xFFFFFFFF - 2;   // Don't spill most recently used

		if(XMM0.priority < priority) {priority = XMM0.priority; candidate = Encoding::XMM0;}
		if(XMM1.priority < priority) {priority = XMM1.priority; candidate = Encoding::XMM1;}
		if(XMM2.priority < priority) {priority = XMM2.priority; candidate = Encoding::XMM2;}
		if(XMM3.priority < priority) {priority = XMM3.priority; candidate = Encoding::XMM3;}
		if(XMM4.priority < priority) {priority = XMM4.priority; candidate = Encoding::XMM4;}
		if(XMM5.priority < priority) {priority = XMM5.priority; candidate = Encoding::XMM5;}
		if(XMM6.priority < priority) {priority = XMM6.priority; candidate = Encoding::XMM6;}
		if(XMM7.priority < priority) {priority = XMM7.priority; candidate = Encoding::XMM7;}

		spill(R128(candidate));

		return assign(R128(candidate), ref, copy, ss);
	}

	const OperandR_M128 RegisterAllocator::m128(const OperandREF &ref, bool ss)
	{
		if(ref == 0) throw Error("Cannot dereference 0");

		// Check if already allocated
		     if(XMM0.reference == ref) return access(xmm0);
		else if(XMM1.reference == ref) return access(xmm1);
		else if(XMM2.reference == ref) return access(xmm2);
		else if(XMM3.reference == ref) return access(xmm3);
		else if(XMM4.reference == ref) return access(xmm4);
		else if(XMM5.reference == ref) return access(xmm5);
		else if(XMM6.reference == ref) return access(xmm6);
		else if(XMM7.reference == ref) return access(xmm7);

		return (OperandR_M128)xword_ptr [ref];
	}

	const OperandXMMREG &RegisterAllocator::allocate(const OperandXMMREG &reg, const OperandREF &ref, bool ss)
	{
		if(X128(reg).reference != 0)
		{
			throw Error("%s not available for register allocation", reg.string());
		}

		X128(reg).reference = ref;
		X128(reg).partial = ss ? 4 : 0;

		return access(reg);
	}

	const OperandXMMREG &RegisterAllocator::assign(const OperandXMMREG &reg, const OperandREF &ref, bool copy, bool ss)
	{
		allocate(reg, ref, ss);

		if(copy)
		{
			if(ss) movss(reg, dword_ptr [ref]);
			else   movaps(reg, xword_ptr [ref]);
		}

		return reg;
	}

	const OperandXMMREG &RegisterAllocator::access(const OperandXMMREG &reg)
	{
		// Decrease priority of other registers
		if(reg.reg != Encoding::XMM0 && XMM0.priority) XMM0.priority--;
		if(reg.reg != Encoding::XMM1 && XMM1.priority) XMM1.priority--;
		if(reg.reg != Encoding::XMM2 && XMM2.priority) XMM2.priority--;
		if(reg.reg != Encoding::XMM3 && XMM3.priority) XMM3.priority--;
		if(reg.reg != Encoding::XMM4 && XMM4.priority) XMM4.priority--;
		if(reg.reg != Encoding::XMM5 && XMM5.priority) XMM5.priority--;
		if(reg.reg != Encoding::XMM6 && XMM6.priority) XMM6.priority--;
		if(reg.reg != Encoding::XMM7 && XMM7.priority) XMM7.priority--;

		// Give highest priority
		X128(reg).priority = 0xFFFFFFFF;

		return reg;
	}

	void RegisterAllocator::free(const OperandXMMREG &reg)
	{
		X128(reg).free();
	}

	void RegisterAllocator::spill(const OperandXMMREG &reg)
	{
		if(X128(reg).reference != 0)
		{
			if(X128(reg).partial) movss(dword_ptr [X128(reg).reference], reg);
			else                  movaps(xword_ptr [X128(reg).reference], reg);
		}
		
		free(reg);
	}

	const OperandXMMREG &RegisterAllocator::rSS(const OperandREF &ref, bool copy, bool ss)
	{
		return r128(ref, copy, ss);
	}

	const OperandXMM32 RegisterAllocator::mSS(const OperandREF &ref, bool ss)
	{
		return (OperandXMM32)m128(ref, ss);
	}

	void RegisterAllocator::free(const OperandREF &ref)
	{
		     if(EAX.reference == ref) free(eax);
		else if(ECX.reference == ref) free(ecx);
		else if(EDX.reference == ref) free(edx);
		else if(EBX.reference == ref) free(ebx);
		else if(ESI.reference == ref) free(esi);
		else if(EDI.reference == ref) free(edi);

		     if(MM0.reference == ref) free(mm0);
		else if(MM1.reference == ref) free(mm1);
		else if(MM2.reference == ref) free(mm2);
		else if(MM3.reference == ref) free(mm3);
		else if(MM4.reference == ref) free(mm4);
		else if(MM5.reference == ref) free(mm5);
		else if(MM6.reference == ref) free(mm6);
		else if(MM7.reference == ref) free(mm7);

		     if(XMM0.reference == ref) free(xmm0);
		else if(XMM1.reference == ref) free(xmm1);
		else if(XMM2.reference == ref) free(xmm2);
		else if(XMM3.reference == ref) free(xmm3);
		else if(XMM4.reference == ref) free(xmm4);
		else if(XMM5.reference == ref) free(xmm5);
		else if(XMM6.reference == ref) free(xmm6);
		else if(XMM7.reference == ref) free(xmm7);
	}

	void RegisterAllocator::spill(const OperandREF &ref)
	{
		     if(EAX.reference == ref) spill(eax);
		else if(ECX.reference == ref) spill(ecx);
		else if(EDX.reference == ref) spill(edx);
		else if(EBX.reference == ref) spill(ebx);
		else if(ESI.reference == ref) spill(esi);
		else if(EDI.reference == ref) spill(edi);

		     if(MM0.reference == ref) spill(mm0);
		else if(MM1.reference == ref) spill(mm1);
		else if(MM2.reference == ref) spill(mm2);
		else if(MM3.reference == ref) spill(mm3);
		else if(MM4.reference == ref) spill(mm4);
		else if(MM5.reference == ref) spill(mm5);
		else if(MM6.reference == ref) spill(mm6);
		else if(MM7.reference == ref) spill(mm7);

		     if(XMM0.reference == ref) spill(xmm0);
		else if(XMM1.reference == ref) spill(xmm1);
		else if(XMM2.reference == ref) spill(xmm2);
		else if(XMM3.reference == ref) spill(xmm3);
		else if(XMM4.reference == ref) spill(xmm4);
		else if(XMM5.reference == ref) spill(xmm5);
		else if(XMM6.reference == ref) spill(xmm6);
		else if(XMM7.reference == ref) spill(xmm7);

		free(ref);
	}

	void RegisterAllocator::freeAll()
	{
		free(eax);
		free(ecx);
		free(edx);
		free(ebx);
		free(esi);
		free(edi);

		free(mm0);
		free(mm1);
		free(mm2);
		free(mm3);
		free(mm4);
		free(mm5);
		free(mm6);
		free(mm7);

		free(xmm0);
		free(xmm1);
		free(xmm2);
		free(xmm3);
		free(xmm4);
		free(xmm5);
		free(xmm6);
		free(xmm7);
	}

	void RegisterAllocator::spillAll()
	{
		spill(eax);
		spill(ecx);
		spill(edx);
		spill(ebx);
		spill(esi);
		spill(edi);

		spill(mm0);
		spill(mm1);
		spill(mm2);
		spill(mm3);
		spill(mm4);
		spill(mm5);
		spill(mm6);
		spill(mm7);

		spill(xmm0);
		spill(xmm1);
		spill(xmm2);
		spill(xmm3);
		spill(xmm4);
		spill(xmm5);
		spill(xmm6);
		spill(xmm7);
	}

	void RegisterAllocator::spillMMX()
	{
		spill(mm0);
		spill(mm1);
		spill(mm2);
		spill(mm3);
		spill(mm4);
		spill(mm5);
		spill(mm6);
		spill(mm7);
	}

	void RegisterAllocator::spillMMXcept(const OperandMMREG &reg)
	{
		if(reg.reg != Encoding::MM0) spill(mm0);
		if(reg.reg != Encoding::MM1) spill(mm1);
		if(reg.reg != Encoding::MM2) spill(mm2);
		if(reg.reg != Encoding::MM3) spill(mm3);
		if(reg.reg != Encoding::MM4) spill(mm4);
		if(reg.reg != Encoding::MM5) spill(mm5);
		if(reg.reg != Encoding::MM6) spill(mm6);
		if(reg.reg != Encoding::MM7) spill(mm7);

		emms();
	}

	RegisterAllocator::Allocation &RegisterAllocator::X32(const OperandREG32 &r32)
	{
		return X32(r32.reg);
	}

	RegisterAllocator::Allocation &RegisterAllocator::X64(const OperandMMREG &r64)
	{
		return X64(r64.reg);
	}

	RegisterAllocator::Allocation &RegisterAllocator::X128(const OperandXMMREG &r128)
	{
		return X128(r128.reg);
	}

	RegisterAllocator::Allocation &RegisterAllocator::X32(Encoding::Reg r32)
	{
		switch(r32)
		{
		case Encoding::EAX: return EAX;
		case Encoding::ECX: return ECX;
		case Encoding::EDX: return EDX;
		case Encoding::EBX: return EBX;
		case Encoding::ESI: return ESI;
		case Encoding::EDI: return EDI;
		default: throw INTERNAL_ERROR;
		}
	}

	RegisterAllocator::Allocation &RegisterAllocator::X64(Encoding::Reg r64)
	{
		switch(r64)
		{
		case Encoding::MM0: return MM0;
		case Encoding::MM1: return MM1;
		case Encoding::MM2: return MM2;
		case Encoding::MM3: return MM3;
		case Encoding::MM4: return MM4;
		case Encoding::MM5: return MM5;
		case Encoding::MM6: return MM6;
		case Encoding::MM7: return MM7;
		default: throw INTERNAL_ERROR;
		}
	}

	RegisterAllocator::Allocation &RegisterAllocator::X128(Encoding::Reg r128)
	{
		switch(r128)
		{
		case Encoding::XMM0: return XMM0;
		case Encoding::XMM1: return XMM1;
		case Encoding::XMM2: return XMM2;
		case Encoding::XMM3: return XMM3;
		case Encoding::XMM4: return XMM4;
		case Encoding::XMM5: return XMM5;
		case Encoding::XMM6: return XMM6;
		case Encoding::XMM7: return XMM7;
		default: throw INTERNAL_ERROR;
		}
	}

	const OperandREG8 &RegisterAllocator::R8(Encoding::Reg r8)
	{
		switch(r8)
		{
		case Encoding::AL: return al;
		case Encoding::CL: return cl;
		case Encoding::DL: return dl;
		case Encoding::BL: return bl;
		default: throw INTERNAL_ERROR;
		}
	}

	const OperandREG16 &RegisterAllocator::R16(Encoding::Reg r16)
	{
		switch(r16)
		{
		case Encoding::AX: return ax;
		case Encoding::CX: return cx;
		case Encoding::DX: return dx;
		case Encoding::BX: return bx;
		case Encoding::SI: return si;
		case Encoding::DI: return di;
		default: throw INTERNAL_ERROR;
		}
	}

	const OperandREG32 &RegisterAllocator::R32(Encoding::Reg r32)
	{
		switch(r32)
		{
		case Encoding::EAX: return eax;
		case Encoding::ECX: return ecx;
		case Encoding::EDX: return edx;
		case Encoding::EBX: return ebx;
		case Encoding::ESI: return esi;
		case Encoding::EDI: return edi;
		default: throw INTERNAL_ERROR;
		}
	}

	const OperandMMREG &RegisterAllocator::R64(Encoding::Reg r64)
	{
		switch(r64)
		{
		case Encoding::MM0: return mm0;
		case Encoding::MM1: return mm1;
		case Encoding::MM2: return mm2;
		case Encoding::MM3: return mm3;
		case Encoding::MM4: return mm4;
		case Encoding::MM5: return mm5;
		case Encoding::MM6: return mm6;
		case Encoding::MM7: return mm7;
		default: throw INTERNAL_ERROR;
		}
	}

	const OperandXMMREG &RegisterAllocator::R128(Encoding::Reg r128)
	{
		switch(r128)
		{
		case Encoding::XMM0: return xmm0;
		case Encoding::XMM1: return xmm1;
		case Encoding::XMM2: return xmm2;
		case Encoding::XMM3: return xmm3;
		case Encoding::XMM4: return xmm4;
		case Encoding::XMM5: return xmm5;
		case Encoding::XMM6: return xmm6;
		case Encoding::XMM7: return xmm7;
		default: throw INTERNAL_ERROR;
		}
	}
}
