#include "Optimizer.hpp"

namespace SoftWire
{
	bool Optimizer::copyPropagation = true;
	bool Optimizer::dropUnmodified = true;
	bool Optimizer::spillUnrelocated = false;
	bool Optimizer::loadLoadElimination = true;

	Optimizer::Optimizer()
	{
	}

	Optimizer::~Optimizer()
	{
	}

	const Optimizer::AllocationTable Optimizer::getAllocationState()
	{
		AllocationTable state;

		if(spillUnrelocated)
		{
			RegisterAllocator::spillAll();
			return state;
		}

		state.eax = EAX;
		state.ecx = ECX;
		state.edx = EDX;
		state.ebx = EBX;
		state.esi = ESI;
		state.edi = EDI;

		state.mm0 = MM0;
		state.mm1 = MM1;
		state.mm2 = MM2;
		state.mm3 = MM3;
		state.mm4 = MM4;
		state.mm5 = MM5;
		state.mm6 = MM6;
		state.mm7 = MM7;

		state.xmm0 = XMM0;
		state.xmm1 = XMM1;
		state.xmm2 = XMM2;
		state.xmm3 = XMM3;
		state.xmm4 = XMM4;
		state.xmm5 = XMM5;
		state.xmm6 = XMM6;
		state.xmm7 = XMM7;

		return state;
	}

	void Optimizer::spillAll(const AllocationTable &allocationState)
	{
		if(spillUnrelocated)
		{
			RegisterAllocator::spillAll();
			return;
		}

		if(EAX.reference != allocationState.eax.reference) spill(eax);
		if(ECX.reference != allocationState.ecx.reference) spill(ecx);
		if(EDX.reference != allocationState.edx.reference) spill(edx);
		if(EBX.reference != allocationState.ebx.reference) spill(ebx);
		if(ESI.reference != allocationState.esi.reference) spill(esi);
		if(EDI.reference != allocationState.edi.reference) spill(edi);

		if(MM0.reference != allocationState.mm0.reference) spill(mm0);
		if(MM1.reference != allocationState.mm1.reference) spill(mm1);
		if(MM2.reference != allocationState.mm2.reference) spill(mm2);
		if(MM3.reference != allocationState.mm3.reference) spill(mm3);
		if(MM4.reference != allocationState.mm4.reference) spill(mm4);
		if(MM5.reference != allocationState.mm5.reference) spill(mm5);
		if(MM6.reference != allocationState.mm6.reference) spill(mm6);
		if(MM7.reference != allocationState.mm7.reference) spill(mm7);

		if(XMM0.reference != allocationState.xmm0.reference) spill(xmm0);
		if(XMM1.reference != allocationState.xmm1.reference) spill(xmm1);
		if(XMM2.reference != allocationState.xmm2.reference) spill(xmm2);
		if(XMM3.reference != allocationState.xmm3.reference) spill(xmm3);
		if(XMM4.reference != allocationState.xmm4.reference) spill(xmm4);
		if(XMM5.reference != allocationState.xmm5.reference) spill(xmm5);
		if(XMM6.reference != allocationState.xmm6.reference) spill(xmm6);
		if(XMM7.reference != allocationState.xmm7.reference) spill(xmm7);
	}

	void Optimizer::free(const OperandREF &ref)
	{
		if(EAX.copyInstruction && EAX.copyReference == ref) {free(eax); return;}
		if(ECX.copyInstruction && ECX.copyReference == ref) {free(ecx); return;}
		if(EDX.copyInstruction && EDX.copyReference == ref) {free(edx); return;}
		if(EBX.copyInstruction && EBX.copyReference == ref) {free(ebx); return;}
		if(ESI.copyInstruction && ESI.copyReference == ref) {free(esi); return;}
		if(EDI.copyInstruction && EDI.copyReference == ref) {free(edi); return;}

		if(MM0.copyInstruction && MM0.copyReference == ref) {free(mm0); return;}
		if(MM1.copyInstruction && MM1.copyReference == ref) {free(mm1); return;}
		if(MM2.copyInstruction && MM2.copyReference == ref) {free(mm2); return;}
		if(MM3.copyInstruction && MM3.copyReference == ref) {free(mm3); return;}
		if(MM4.copyInstruction && MM4.copyReference == ref) {free(mm4); return;}
		if(MM5.copyInstruction && MM5.copyReference == ref) {free(mm5); return;}
		if(MM6.copyInstruction && MM6.copyReference == ref) {free(mm6); return;}
		if(MM7.copyInstruction && MM7.copyReference == ref) {free(mm7); return;}

		if(XMM0.copyInstruction && XMM0.copyReference == ref) {free(xmm0); return;}
		if(XMM1.copyInstruction && XMM1.copyReference == ref) {free(xmm1); return;}
		if(XMM2.copyInstruction && XMM2.copyReference == ref) {free(xmm2); return;}
		if(XMM3.copyInstruction && XMM3.copyReference == ref) {free(xmm3); return;}
		if(XMM4.copyInstruction && XMM4.copyReference == ref) {free(xmm4); return;}
		if(XMM5.copyInstruction && XMM5.copyReference == ref) {free(xmm5); return;}
		if(XMM6.copyInstruction && XMM6.copyReference == ref) {free(xmm6); return;}
		if(XMM7.copyInstruction && XMM7.copyReference == ref) {free(xmm7); return;}

		RegisterAllocator::free(ref);
	}

	void Optimizer::free(const OperandREG32 &reg)
	{
		if(X32(reg).loadInstruction && !X32(reg).referenced)
		{
			X32(reg).loadInstruction->reserve();
			X32(reg).loadInstruction = 0;
		}

		RegisterAllocator::free(reg);
	}

	void Optimizer::free(const OperandMMREG &reg)
	{
		if(X64(reg).loadInstruction && !X64(reg).referenced)
		{
			X64(reg).loadInstruction->reserve();
			X64(reg).loadInstruction = 0;
		}

		RegisterAllocator::free(reg);
	}

	void Optimizer::free(const OperandXMMREG &reg)
	{
		if(X128(reg).loadInstruction && !X128(reg).referenced)
		{
			X128(reg).loadInstruction->reserve();
			X128(reg).loadInstruction = 0;
		}

		RegisterAllocator::free(reg);
	}

	void Optimizer::spill(const OperandREF &ref)
	{
		if(EAX.copyInstruction && EAX.copyReference == ref) {spill(eax); return;}
		if(ECX.copyInstruction && ECX.copyReference == ref) {spill(ecx); return;}
		if(EDX.copyInstruction && EDX.copyReference == ref) {spill(edx); return;}
		if(EBX.copyInstruction && EBX.copyReference == ref) {spill(ebx); return;}
		if(ESI.copyInstruction && ESI.copyReference == ref) {spill(esi); return;}
		if(EDI.copyInstruction && EDI.copyReference == ref) {spill(edi); return;}

		if(MM0.copyInstruction && MM0.copyReference == ref) {spill(mm0); return;}
		if(MM1.copyInstruction && MM1.copyReference == ref) {spill(mm1); return;}
		if(MM2.copyInstruction && MM2.copyReference == ref) {spill(mm2); return;}
		if(MM3.copyInstruction && MM3.copyReference == ref) {spill(mm3); return;}
		if(MM4.copyInstruction && MM4.copyReference == ref) {spill(mm4); return;}
		if(MM5.copyInstruction && MM5.copyReference == ref) {spill(mm5); return;}
		if(MM6.copyInstruction && MM6.copyReference == ref) {spill(mm6); return;}
		if(MM7.copyInstruction && MM7.copyReference == ref) {spill(mm7); return;}

		if(XMM0.copyInstruction && XMM0.copyReference == ref) {spill(xmm0); return;}
		if(XMM1.copyInstruction && XMM1.copyReference == ref) {spill(xmm1); return;}
		if(XMM2.copyInstruction && XMM2.copyReference == ref) {spill(xmm2); return;}
		if(XMM3.copyInstruction && XMM3.copyReference == ref) {spill(xmm3); return;}
		if(XMM4.copyInstruction && XMM4.copyReference == ref) {spill(xmm4); return;}
		if(XMM5.copyInstruction && XMM5.copyReference == ref) {spill(xmm5); return;}
		if(XMM6.copyInstruction && XMM6.copyReference == ref) {spill(xmm6); return;}
		if(XMM7.copyInstruction && XMM7.copyReference == ref) {spill(xmm7); return;}

		RegisterAllocator::free(ref);
	}

	void Optimizer::restoreForward(const AllocationTable &allocationState)
	{
		if(spillUnrelocated)
		{
			RegisterAllocator::spillAll();
			return;
		}

		spillAll(allocationState);
	}

	void Optimizer::restoreBackward(const AllocationTable &allocationState)
	{
		if(spillUnrelocated)
		{
			RegisterAllocator::spillAll();
			return;
		}
		
		if(EAX.reference != allocationState.eax.reference) {spill(eax); assign(eax, allocationState.eax.reference, true, allocationState.eax.partial);}
		if(ECX.reference != allocationState.ecx.reference) {spill(ecx); assign(ecx, allocationState.ecx.reference, true, allocationState.ecx.partial);}
		if(EDX.reference != allocationState.edx.reference) {spill(edx); assign(edx, allocationState.edx.reference, true, allocationState.edx.partial);}
		if(EBX.reference != allocationState.ebx.reference) {spill(ebx); assign(ebx, allocationState.ebx.reference, true, allocationState.ebx.partial);}
		if(ESI.reference != allocationState.esi.reference) {spill(esi); assign(esi, allocationState.esi.reference, true, allocationState.esi.partial);}
		if(EDI.reference != allocationState.edi.reference) {spill(edi); assign(edi, allocationState.edi.reference, true, allocationState.edi.partial);}

		if(MM0.reference != allocationState.mm0.reference) {spill(mm0); assign(mm0, allocationState.mm0.reference, true);}
		if(MM1.reference != allocationState.mm1.reference) {spill(mm1); assign(mm1, allocationState.mm1.reference, true);}
		if(MM2.reference != allocationState.mm2.reference) {spill(mm2); assign(mm2, allocationState.mm2.reference, true);}
		if(MM3.reference != allocationState.mm3.reference) {spill(mm3); assign(mm3, allocationState.mm3.reference, true);}
		if(MM4.reference != allocationState.mm4.reference) {spill(mm4); assign(mm4, allocationState.mm4.reference, true);}
		if(MM5.reference != allocationState.mm5.reference) {spill(mm5); assign(mm5, allocationState.mm5.reference, true);}
		if(MM6.reference != allocationState.mm6.reference) {spill(mm6); assign(mm6, allocationState.mm6.reference, true);}
		if(MM7.reference != allocationState.mm7.reference) {spill(mm7); assign(mm7, allocationState.mm7.reference, true);}

		if(XMM0.reference != allocationState.xmm0.reference) {spill(xmm0); assign(xmm0, allocationState.xmm0.reference, true, allocationState.xmm0.partial != 0);}
		if(XMM1.reference != allocationState.xmm1.reference) {spill(xmm1); assign(xmm1, allocationState.xmm1.reference, true, allocationState.xmm1.partial != 0);}
		if(XMM2.reference != allocationState.xmm2.reference) {spill(xmm2); assign(xmm2, allocationState.xmm2.reference, true, allocationState.xmm2.partial != 0);}
		if(XMM3.reference != allocationState.xmm3.reference) {spill(xmm3); assign(xmm3, allocationState.xmm3.reference, true, allocationState.xmm3.partial != 0);}
		if(XMM4.reference != allocationState.xmm4.reference) {spill(xmm4); assign(xmm4, allocationState.xmm4.reference, true, allocationState.xmm4.partial != 0);}
		if(XMM5.reference != allocationState.xmm5.reference) {spill(xmm5); assign(xmm5, allocationState.xmm5.reference, true, allocationState.xmm5.partial != 0);}
		if(XMM6.reference != allocationState.xmm6.reference) {spill(xmm6); assign(xmm6, allocationState.xmm6.reference, true, allocationState.xmm6.partial != 0);}
		if(XMM7.reference != allocationState.xmm7.reference) {spill(xmm7); assign(xmm7, allocationState.xmm7.reference, true, allocationState.xmm7.partial != 0);}
	}

	void Optimizer::enableCopyPropagation()
	{
		copyPropagation = true;
	}

	void Optimizer::disableCopyPropagation()
	{
		copyPropagation = false;
	}

	void Optimizer::enableDropUnmodified()
	{
		dropUnmodified = true;
	}

	void Optimizer::disableDropUnmodified()
	{
		dropUnmodified = false;
	}

	void Optimizer::enableSpillUnrelocated()
	{
		spillUnrelocated = true;
	}

	void Optimizer::disableSpillUnrelocated()
	{
		spillUnrelocated = false;
	}

	void Optimizer::enableLoadLoadElimination()
	{
		loadLoadElimination = true;
	}

	void Optimizer::disableLoadLoadElimination()
	{
		loadLoadElimination = false;
	}

	Encoding *Optimizer::cmpxchg(OperandR_M8 r_m8, OperandREG8 r8)
	{
		markModified(r_m8);
		markModified(al);

		return RegisterAllocator::cmpxchg(r_m8, r8);
	}

	Encoding *Optimizer::cmpxchg(OperandR_M16 r_m16, OperandREG16 r16)
	{
		markModified(r_m16);
		markModified(ax);

		return RegisterAllocator::cmpxchg(r_m16, r16);
	}

	Encoding *Optimizer::cmpxchg(OperandR_M32 r_m32, OperandREG32 r32)
	{
		markModified(r_m32);
		markModified(eax);

		return RegisterAllocator::cmpxchg(r_m32, r32);
	}

	Encoding *Optimizer::lock_cmpxchg(OperandMEM8 m8, OperandREG8 r8)
	{
		markModified(m8);
		markModified(al);

		return RegisterAllocator::lock_cmpxchg(m8, r8);
	}

	Encoding *Optimizer::lock_cmpxchg(OperandMEM16 m16, OperandREG16 r16)
	{
		markModified(m16);
		markModified(ax);

		return RegisterAllocator::lock_cmpxchg(m16, r16);
	}

	Encoding *Optimizer::lock_cmpxchg(OperandMEM32 m32, OperandREG32 r32)
	{
		markModified(m32);
		markModified(eax);

		return RegisterAllocator::lock_cmpxchg(m32, r32);
	}

	Encoding *Optimizer::mov(OperandREG32 r32i, OperandREG32 r32j)
	{
		if(r32i == r32j) return 0;

		if(r32i.reg == Encoding::ESP || r32i.reg == Encoding::EBP ||
		   r32j.reg == Encoding::ESP || r32j.reg == Encoding::EBP)
		{
			return RegisterAllocator::mov(r32i, r32j);
		}

		if(loadLoadElimination && !X32(r32i).referenced && X32(r32i).loadInstruction)
		{
			X32(r32i).loadInstruction->reserve();
			X32(r32i).loadInstruction = 0;
		}

		if(copyPropagation && X32(r32i).copyInstruction)
		{
			X32(r32i).copyInstruction->reserve();
			X32(r32i).copyInstruction = 0;
		}

		Encoding *mov = RegisterAllocator::mov(r32i, r32j);

		X32(r32i).loadInstruction = mov;
		X32(r32i).referenced = false;
		
		if(copyPropagation)
		{
			// Return if not in allocation table
			if(X32(r32i).reference == 0)
			{
				return mov;
			}

			// Attempt copy propagation
			mov->reserve();
			swapAllocation(&X32(r32i), &X32(r32j));
			X32(r32i).copyInstruction = mov;
		}
		
		return mov;
	}

	Encoding *Optimizer::mov(OperandREG32 r32, OperandMEM32 m32)
	{
		if(r32.reg == Encoding::ESP || r32.reg == Encoding::EBP)
		{
			return RegisterAllocator::mov(r32, m32);
		}

		if(loadLoadElimination && !X32(r32).referenced && X32(r32).loadInstruction)
		{
			X32(r32).loadInstruction->reserve();
			X32(r32).loadInstruction = 0;
		}

		if(copyPropagation && X32(r32).copyInstruction)
		{
			X32(r32).copyInstruction->reserve();
			X32(r32).copyInstruction = 0;
		}

		Encoding *mov = RegisterAllocator::mov(r32, m32);

		X32(r32).loadInstruction = mov;
		X32(r32).referenced = false;

		// Register has been allocated here
		if(X32(r32).reference == (OperandREF&)m32)
		{
			X32(r32).modified = false;
		}

		return mov;
	}

	Encoding *Optimizer::mov(OperandREG32 r32, OperandR_M32 r_m32)
	{
		if(Operand::isReg(r_m32)) return mov(r32, (OperandREG32)r_m32);
		else                      return mov(r32, (OperandMEM32)r_m32);
	}

	Encoding *Optimizer::mov(OperandMEM32 m32, OperandREG32 r32)
	{
		if(dropUnmodified && !X32(r32).modified && X32(r32).reference == (OperandREF&)m32)
		{
			return 0;
		}

		return RegisterAllocator::mov(m32, r32);
	}

	Encoding *Optimizer::mov(OperandR_M32 r_m32, OperandREG32 r32)
	{
		if(Operand::isReg(r_m32)) return mov((OperandREG32)r_m32, r32);
		else                      return mov((OperandMEM32)r_m32, r32);
	}

	Encoding *Optimizer::mov(OperandREG32 r32, int imm)
	{
		if(r32.reg == Encoding::ESP || r32.reg == Encoding::EBP)
		{
			return RegisterAllocator::mov(r32, imm);
		}

		if(loadLoadElimination && !X32(r32).referenced && X32(r32).loadInstruction)
		{
			X32(r32).loadInstruction->reserve();
			X32(r32).loadInstruction = 0;
		}

		if(copyPropagation && X32(r32).copyInstruction)
		{
			X32(r32).copyInstruction->reserve();
			X32(r32).copyInstruction = 0;
		}

		Encoding *mov = RegisterAllocator::mov(r32, imm);

		X32(r32).loadInstruction = mov;
		X32(r32).referenced = false;

		return mov;
	}

	Encoding *Optimizer::movaps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(xmmi == xmmj) return 0;

		if(loadLoadElimination && !X128(xmmi).referenced && X128(xmmi).loadInstruction)
		{
			X128(xmmi).loadInstruction->reserve();
			X128(xmmi).loadInstruction = 0;
		}

		if(copyPropagation && X128(xmmi).copyInstruction)
		{
			X128(xmmi).copyInstruction->reserve();
			X128(xmmi).copyInstruction = 0;
		}

		Encoding *movaps = RegisterAllocator::movaps(xmmi, xmmj);

		X128(xmmi).loadInstruction = movaps;
		X128(xmmi).referenced = false;

		if(copyPropagation)
		{
			// Return if not in allocation table
			if(X128(xmmi).reference == 0)
			{
				return movaps;
			}

			// Attempt copy propagation
			movaps->reserve();
			swapAllocation(&X128(xmmi), &X128(xmmj));
			X128(xmmi).copyInstruction = movaps;
		}

		return movaps;
	}

	Encoding *Optimizer::movaps(OperandXMMREG xmm, OperandMEM128 m128)
	{
		if(loadLoadElimination && !X128(xmm).referenced && X128(xmm).loadInstruction)
		{
			X128(xmm).loadInstruction->reserve();
			X128(xmm).loadInstruction = 0;
		}

		if(copyPropagation && X128(xmm).copyInstruction)
		{
			X128(xmm).copyInstruction->reserve();
			X128(xmm).copyInstruction = 0;
		}

		Encoding *movaps = RegisterAllocator::movaps(xmm, m128);

		X128(xmm).loadInstruction = movaps;
		X128(xmm).referenced = false;

		// Register has been allocated here
		if(X128(xmm).reference == (OperandREF&)m128)
		{
			X128(xmm).modified = false;
		}

		return movaps;
	}

	Encoding *Optimizer::movaps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		if(r_m128.type == Operand::OPERAND_XMMREG) return movaps(xmm, (OperandXMMREG)r_m128);
		else                                       return movaps(xmm, (OperandMEM128)r_m128);
	}

	Encoding *Optimizer::movaps(OperandMEM128 m128, OperandXMMREG xmm)
	{
		return RegisterAllocator::movaps(m128, xmm);
	}

	Encoding *Optimizer::movaps(OperandR_M128 r_m128, OperandXMMREG xmm)
	{
		if(r_m128.type == Operand::OPERAND_XMMREG) return movaps((OperandXMMREG)r_m128, xmm);
		else                                       return movaps((OperandMEM128)r_m128, xmm);
	}

	Encoding *Optimizer::movq(OperandMMREG mmi, OperandMMREG mmj)
	{
		if(mmi == mmj) return 0;
		
		if(loadLoadElimination && !X64(mmi).referenced && X64(mmi).loadInstruction)
		{
			X64(mmi).loadInstruction->reserve();
			X64(mmi).loadInstruction = 0;
		}

		if(copyPropagation && X64(mmi).copyInstruction)
		{
			X64(mmi).copyInstruction->reserve();
			X64(mmi).copyInstruction = 0;
		}

		Encoding *movq = RegisterAllocator::movq(mmi, mmj);

		X64(mmi).loadInstruction = movq;
		X64(mmi).referenced = false;

		if(copyPropagation)
		{
			// Return if not in allocation table
			if(X64(mmi).reference == 0)
			{
				return movq;
			}

			// Attempt copy propagation
			movq->reserve();
			swapAllocation(&X64(mmi), &X64(mmj));
			X64(mmi).copyInstruction = movq;
		}

		return movq;
	}

	Encoding *Optimizer::movq(OperandMMREG mm, OperandMEM64 mem64)
	{
		if(loadLoadElimination && !X64(mm).referenced && X64(mm).loadInstruction)
		{
			X64(mm).loadInstruction->reserve();
			X64(mm).loadInstruction =0;
		}

		if(copyPropagation && X64(mm).copyInstruction)
		{
			X64(mm).copyInstruction->reserve();
			X64(mm).copyInstruction = 0;
		}

		Encoding *movq = RegisterAllocator::movq(mm, mem64);

		X64(mm).loadInstruction = movq;
		X64(mm).referenced = false;

		// Register has been allocated here
		if(X64(mm).reference == (OperandREF&)mem64)
		{
			X64(mm).modified = false;
		}

		return movq;
	}

	Encoding *Optimizer::movq(OperandMMREG mm, OperandR_M64 r_m64)
	{
		if(r_m64.type == Operand::OPERAND_MMREG) return movq(mm, (OperandMMREG)r_m64);
		else                                     return movq(mm, (OperandMEM64)r_m64);
	}

	Encoding *Optimizer::movq(OperandMEM64 mem64, OperandMMREG mm)
	{
		return RegisterAllocator::movq(mem64, mm);
	}

	Encoding *Optimizer::movq(OperandR_M64 r_m64, OperandMMREG mm)
	{
		if(r_m64 == mm) return 0;

		return RegisterAllocator::movq(r_m64, mm);
	}

	Encoding *Optimizer::movups(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(xmmi == xmmj) return 0;

		if(loadLoadElimination && !X128(xmmi).referenced && X128(xmmi).loadInstruction)
		{
			X128(xmmi).loadInstruction->reserve();
			X128(xmmi).loadInstruction = 0;
		}

		if(copyPropagation && X128(xmmi).copyInstruction)
		{
			X128(xmmi).copyInstruction->reserve();
			X128(xmmi).copyInstruction = 0;
		}

		Encoding *movups = RegisterAllocator::movups(xmmi, xmmj);

		X128(xmmi).loadInstruction = movups;
		X128(xmmi).referenced = false;

		if(copyPropagation)
		{
			// Return if not in allocation table
			if(X128(xmmi).reference == 0)
			{
				return movups;
			}

			// Attempt copy propagation
			movups->reserve();
			swapAllocation(&X128(xmmi), &X128(xmmj));
			X128(xmmi).copyInstruction = movups;
		}

		return movups;
	}

	Encoding *Optimizer::movups(OperandXMMREG xmm, OperandMEM128 m128)
	{
		if(loadLoadElimination && !X128(xmm).referenced && X128(xmm).loadInstruction)
		{
			X128(xmm).loadInstruction->reserve();
			X128(xmm).loadInstruction = 0;
		}

		if(copyPropagation && X128(xmm).copyInstruction)
		{
			X128(xmm).copyInstruction->reserve();
			X128(xmm).copyInstruction = 0;
		}

		Encoding *movups = RegisterAllocator::movaps(xmm, m128);

		X128(xmm).loadInstruction = movups;
		X128(xmm).referenced = false;

		// Register has been allocated here
		if(X128(xmm).reference == (OperandREF&)m128)
		{
			X128(xmm).modified = false;
		}

		return movups;
	}

	Encoding *Optimizer::movups(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		if(r_m128.type == Operand::OPERAND_XMMREG) return movups(xmm, (OperandXMMREG)r_m128);
		else                                       return movups(xmm, (OperandMEM128)r_m128);
	}

	Encoding *Optimizer::pshufw(OperandMMREG mmi, OperandMMREG mmj, unsigned char c)
	{
		if(c == 0xE4)
		{
			if(mmi == mmj) return 0;
			else return movq(mmi, mmj);
		}

		return RegisterAllocator::pshufw(mmi, mmj, c);
	}

	Encoding *Optimizer::shufps(OperandXMMREG xmmi, OperandXMMREG xmmj, unsigned char c)
	{
		if(c == 0xE4)
		{
			if(xmmi == xmmj) return 0;
			else return movaps(xmmi, xmmj);
		}

		return RegisterAllocator::shufps(xmmi, xmmj, c);
	}

	Encoding *Optimizer::xadd(OperandREG8 r8i, OperandREG8 r8j)
	{
		markModified(r8i);
		markModified(r8j);

		return RegisterAllocator::xadd(r8i, r8j);
	}

	Encoding *Optimizer::xadd(OperandREG16 r16i, OperandREG16 r16j)
	{
		markModified(r16i);
		markModified(r16j);

		return RegisterAllocator::xadd(r16i, r16j);
	}

	Encoding *Optimizer::xadd(OperandREG32 r32i, OperandREG32 r32j)
	{
		markModified(r32i);
		markModified(r32j);

		return RegisterAllocator::xadd(r32i, r32j);
	}

	Encoding *Optimizer::xadd(OperandR_M8 r_m8, OperandREG8 r8)
	{
		markModified(r_m8);
		markModified(r8);

		return RegisterAllocator::xadd(r_m8, r8);
	}

	Encoding *Optimizer::xadd(OperandR_M16 r_m16, OperandREG16 r16)
	{
		markModified(r_m16);
		markModified(r16);

		return RegisterAllocator::xadd(r_m16, r16);
	}

	Encoding *Optimizer::xadd(OperandR_M32 r_m32, OperandREG32 r32)
	{
		markModified(r_m32);
		markModified(r32);

		return RegisterAllocator::xadd(r_m32, r32);
	}

	Encoding *Optimizer::xchg(OperandREG8 r8i, OperandREG8 r8j)
	{
		markModified(r8i);
		markModified(r8j);

		return RegisterAllocator::xchg(r8i, r8j);
	}

	Encoding *Optimizer::xchg(OperandREG16 r16i, OperandREG16 r16j)
	{
		markModified(r16i);
		markModified(r16j);

		return RegisterAllocator::xchg(r16i, r16j);
	}

	Encoding *Optimizer::xchg(OperandREG32 r32i, OperandREG32 r32j)
	{
		markModified(r32i);
		markModified(r32j);

		return RegisterAllocator::xchg(r32i, r32j);
	}

	Encoding *Optimizer::xchg(OperandR_M8 r_m8, OperandREG8 r8)
	{
		markModified(r_m8);
		markModified(r8);

		return RegisterAllocator::xchg(r_m8, r8);
	}

	Encoding *Optimizer::xchg(OperandR_M16 r_m16, OperandREG16 r16)
	{
		markModified(r_m16);
		markModified(r16);

		return RegisterAllocator::xchg(r_m16, r16);
	}

	Encoding *Optimizer::xchg(OperandR_M32 r_m32, OperandREG32 r32)
	{
		markModified(r_m32);
		markModified(r32);

		return RegisterAllocator::xchg(r_m32, r32);
	}

	Encoding *Optimizer::xchg(OperandREG8 r8, OperandR_M8 r_m8)
	{
		markModified(r8);
		markModified(r_m8);

		return RegisterAllocator::xchg(r8, r_m8);
	}

	Encoding *Optimizer::xchg(OperandREG16 r16, OperandR_M16 r_m16)
	{
		markModified(r16);
		markModified(r_m16);

		return RegisterAllocator::xchg(r16, r_m16);
	}

	Encoding *Optimizer::xchg(OperandREG32 r32, OperandR_M32 r_m32)
	{
		markModified(r32);
		markModified(r_m32);

		return RegisterAllocator::xchg(r32, r_m32);
	}

	Encoding *Optimizer::lock_xadd(OperandMEM8 m8, OperandREG8 r8)
	{
		markModified(m8);
		markModified(r8);

		return RegisterAllocator::lock_xadd(m8, r8);
	}

	Encoding *Optimizer::lock_xadd(OperandMEM16 m16, OperandREG16 r16)
	{
		markModified(m16);
		markModified(r16);

		return RegisterAllocator::lock_xadd(m16, r16);
	}

	Encoding *Optimizer::lock_xadd(OperandMEM32 m32, OperandREG32 r32)
	{
		markModified(m32);
		markModified(r32);

		return RegisterAllocator::lock_xadd(m32, r32);
	}

	Encoding *Optimizer::lock_xchg(OperandMEM8 m8, OperandREG8 r8)
	{
		markModified(m8);
		markModified(r8);

		return RegisterAllocator::lock_xadd(m8, r8);
	}

	Encoding *Optimizer::lock_xchg(OperandMEM16 m16, OperandREG16 r16)
	{
		markModified(m16);
		markModified(r16);

		return RegisterAllocator::lock_xadd(m16, r16);
	}

	Encoding *Optimizer::lock_xchg(OperandMEM32 m32, OperandREG32 r32)
	{
		markModified(m32);
		markModified(r32);

		return RegisterAllocator::lock_xadd(m32, r32);
	}

	Encoding *Optimizer::x86(int instructionID, const Operand &firstOperand, const Operand &secondOperand, const Operand &thirdOperand)
	{
		markModified(firstOperand);
		markReferenced(secondOperand);

		return RegisterAllocator::x86(instructionID, firstOperand, secondOperand, thirdOperand);
	}

	void Optimizer::markModified(const Operand &op)
	{
		if(Operand::isReg(op))
		{
			if(op.type == Operand::OPERAND_REG32 ||
			   op.type == Operand::OPERAND_REG16 ||
			   op.type == Operand::OPERAND_REG8 ||
			   op.type == Operand::OPERAND_EAX ||
			   op.type == Operand::OPERAND_ECX ||
			   op.type == Operand::OPERAND_AX ||
			   op.type == Operand::OPERAND_DX ||
			   op.type == Operand::OPERAND_CX ||
			   op.type == Operand::OPERAND_AL ||
			   op.type == Operand::OPERAND_CL)
			{
				if(op.reg == Encoding::ESP || op.reg == Encoding::EBP)
				{
					return;
				}

				if(X32(op.reg).copyInstruction)
				{
					X32(op.reg).copyInstruction->retain();
					X32(op.reg).copyInstruction = 0;
				}

				X32(op.reg).modified = true;
			}
			else if(op.type == Operand::OPERAND_MMREG)
			{
				if(X64(op.reg).copyInstruction)
				{
					X64(op.reg).copyInstruction->retain();
					X64(op.reg).copyInstruction = 0;
				}

				X64(op.reg).modified = true;
			}
			else if(op.type == Operand::OPERAND_XMMREG)
			{
				if(X128(op.reg).copyInstruction)
				{
					X128(op.reg).copyInstruction->retain();
					X128(op.reg).copyInstruction = 0;
				}

				X128(op.reg).modified = true;
			}
			else if(op.isSubtypeOf(Operand::OPERAND_FPUREG))
			{
			}
			else
			{
				throw INTERNAL_ERROR;
			}
		}
	}

	void Optimizer::markReferenced(const Operand &op)
	{
		if(Operand::isReg(op))
		{
			if(op.type == Operand::OPERAND_REG32 ||
			   op.type == Operand::OPERAND_REG16 ||
			   op.type == Operand::OPERAND_REG8 ||
			   op.type == Operand::OPERAND_EAX ||
			   op.type == Operand::OPERAND_ECX ||
			   op.type == Operand::OPERAND_AX ||
			   op.type == Operand::OPERAND_DX ||
			   op.type == Operand::OPERAND_CX ||
			   op.type == Operand::OPERAND_AL ||
			   op.type == Operand::OPERAND_CL)
			{
				if(op.reg == Encoding::ESP || op.reg == Encoding::EBP)
				{
					return;
				}

				if(X32(op.reg).copyInstruction)
				{
					X32(op.reg).copyInstruction->retain();
					X32(op.reg).copyInstruction = 0;
				}

				X32(op.reg).referenced = true;
			}
			else if(op.type == Operand::OPERAND_MMREG)
			{
				if(X64(op.reg).copyInstruction)
				{
					X64(op.reg).copyInstruction->retain();
					X64(op.reg).copyInstruction = 0;
				}

				X64(op.reg).referenced = true;
			}
			else if(op.type == Operand::OPERAND_XMMREG)
			{
				if(X128(op.reg).copyInstruction)
				{
					X128(op.reg).copyInstruction->retain();
					X128(op.reg).copyInstruction = 0;
				}

				X128(op.reg).referenced = true;
			}
			else if(op.isSubtypeOf(Operand::OPERAND_FPUREG))
			{
			}
			else
			{
				throw INTERNAL_ERROR;
			}
		}
		else if(Operand::isMem(op))
		{
			if(op.baseReg != Encoding::REG_UNKNOWN)
			{
				markReferenced(OperandREG32(op.baseReg));
			}

			if(op.indexReg != Encoding::REG_UNKNOWN)
			{
				markReferenced(OperandREG32(op.indexReg));
			}
		}
	}

	void Optimizer::swapAllocation(Allocation *destination, Allocation *source)
	{
		// Store original reference, used by free()
		source->copyReference = source->reference;
		destination->copyReference = destination->reference;

		// Swap references, priorities, etc.
		OperandREF swapRef = source->reference;
		source->reference = destination->reference;
		destination->reference = swapRef;

		int swapPriority = source->priority;
		source->priority = destination->priority;
		destination->priority = swapPriority;

		int swapPartial = source->partial;
		source->partial = destination->partial;
		destination->partial = swapPartial;

		bool swapModified = source->modified;
		source->modified = destination->modified;
		destination->modified = swapModified;
	}
}