/**
 * 
 */
package dspl.backend.x86;

import dspl.SymbolTable;
import dspl.backend.ArithmaticInstructionProcessor;
import dspl.backend.AssemblyfileGenerator;
import dspl.backend.x86.operands.X86IndexMemoryOperand;
import dspl.backend.x86.operands.X86Operand;
import dspl.backend.x86.operands.X86Register;
import dspl.errorhandling.ArgsChecker;
import dspl.intermediate.instructions.ArithmaticInstruction;
import dspl.intermediate.variable.Variable;

/**
 * x86 Implementation of the ArithmaticInstructionProcessor
 * @author David
 */
public class X86ArithmaticInstructionrProcessor extends 
		ArithmaticInstructionProcessor {
	
	/**
	 * Code Overview:
	 * Example: "iC = iA + iB;" where all three are integer arrays of same length.
	 * Generated code:<Br>
	 * mov $iC, %edx #edx holds offset of dest
	 * mov $iA, %eax #eax holds offset of arg1
	 * mov $iB, %ebx #ebx holds offset of arg2
	 * loop:
	 * movdqa (%eax, %ecx, 4), %xmm0
	 * paddd (%ebx, %ecx, 4), %xmm0
	 * mov %xmm0, (%edx, %ecx, 4)
	 * -------------------
	 * Above is the hopeful aligned version. right now due to alignment not working (seg faults) resulting in less efficient unaligned version
	 * @param instr
	 * @param st
	 * @param afg
	 * @param sseOp
	 */
	private void processVectorInstruction(ArithmaticInstruction instr, SymbolTable st, AssemblyfileGenerator afg, String sseOp) {
		boolean isFloat = (instr.dest.getDataType()==Variable.DATA_TYPE_FLOAT); //differnet move instructions for float/naturals
		X86Register eaxReg = X86VariableManager.getEax(); 
		X86Register ebxReg = X86VariableManager.getEbx(); 
		X86Register ecxReg = X86VariableManager.getEcx(); 
		X86Register edxReg = X86VariableManager.getEdx();
		X86Register xmm0   = X86VariableManager.getXmm0(); 
		X86Register xmm1   = X86VariableManager.getXmm1();
		/**
		 * Example : assume int_array of 32 elements. Will do four elements per loop, so 32/4 = 8, or (32 * 4) / 16 
		 * Example2: uchar_array of 32 elements. Will do sixteen elements per loop, so (32 * 16) / 16 = 2 iterations.
		 */
		//int numLoopIterations = (instr.arg1.getArraySize() * instr.arg1.getDataTypeByteSize() / 16); 
		/**
		 * Example: for an int_array of 32 elements, contains 4 * 32 = 128 bytes, therefore iterate only so long as %ecx < 128
		 */
		int terminatingCondition = instr.arg1.getArraySize() * instr.arg1.getDataTypeByteSize(); 
		eaxReg = X86VariableManager.prepareArrayLocAsReg(instr.arg1, eaxReg, afg); //mov $iA, %eax
		ebxReg = X86VariableManager.prepareArrayLocAsReg(instr.arg2, ebxReg, afg); //mov $iB, %eax
		edxReg = X86VariableManager.prepareArrayLocAsReg(instr.dest, edxReg, afg); //mov $iC, %edx
		afg.writeAsmInstr("movl","$0","%ecx");	  		   	   //mov $0, %ecx
		
		X86Operand destOp, argOp1, argOp2; 
		argOp1 = new X86IndexMemoryOperand("0", eaxReg, ecxReg, 1); // "0(%eax, %ecx, 1)"
		argOp2 = new X86IndexMemoryOperand("0", ebxReg, ecxReg, 1); // "0(%ebx, %ecx, 1)"
		destOp = new X86IndexMemoryOperand("0", edxReg, ecxReg, 1); // "0(%edx, %ecx, 1)"
		
		String loopLabel = afg.getBranchLabel(); 
		afg.writeLabel(loopLabel);  // "loop: "
		if (isFloat) {
			afg.writeAsmInstr("movups", argOp1.toString(),xmm0.toString());   	
		}
		else { 
			X86VariableManager.writeVectorMoveInstruction(argOp1, xmm0, afg, false); //"movdqu (%eax, %ecx, 1), %xmm0
		}
		/*afg.writeAsmInstr(												     //"'sse op' (%ebx, %ecx, 1), %xmm0
				sseOp, 
				argOp2.toString(),
				xmm0.toString());
				*/
		if (isFloat) {
			afg.writeAsmInstr("movups", argOp2.toString(),xmm1.toString());   	
		}
		else { 
			X86VariableManager.writeVectorMoveInstruction(argOp2, xmm1, afg, false); //"movdqu (%ebx, %ecx, 1), %xmm1
		}
		afg.writeAsmInstr(												  		 //"'sse op' %xmm1, %xmm0
				sseOp, 
				xmm1.toString(),
				xmm0.toString());
		if (isFloat) {
			afg.writeAsmInstr("movups", xmm0.toString(), destOp.toString());   	
		}
		else { 
			X86VariableManager.writeVectorMoveInstruction(xmm0, destOp, afg, false); //movdqu %xmm0, (%edx, %ecx, 1)
		}
		afg.writeAsmInstr("addl", "$16",ecxReg.toString());				  //addl $16, %ecx ---> %ecx = %ecx + 16
		afg.writeAsmInstr("cmp", "$" + Integer.toString(terminatingCondition-1), "%ecx");
		afg.writeAsmInstr("jle", loopLabel); 
	}
	
	private void processVectorFPDiv(ArithmaticInstruction instr, SymbolTable st, AssemblyfileGenerator afg) { 
		X86Register eaxReg = X86VariableManager.getEax(); 
		X86Register ebxReg = X86VariableManager.getEbx(); 
		X86Register ecxReg = X86VariableManager.getEcx(); 
		X86Register edxReg = X86VariableManager.getEdx();
		X86Register xmm0   = X86VariableManager.getXmm0(); 
		X86Register xmm1   = X86VariableManager.getXmm1();
		X86Register xmm3   = X86VariableManager.getXmm3();
		
		/**
		 * Example : assume int_array of 32 elements. Will do four elements per loop, so 32/4 = 8, or (32 * 4) / 16 
		 * Example2: uchar_array of 32 elements. Will do sixteen elements per loop, so (32 * 16) / 16 = 2 iterations.
		 */
		//int numLoopIterations = (instr.arg1.getArraySize() * instr.arg1.getDataTypeByteSize() / 16); 
		/**
		 * Example: for an int_array of 32 elements, contains 4 * 32 = 128 bytes, therefore iterate only so long as %ecx < 128
		 */
		int terminatingCondition = instr.arg1.getArraySize() * instr.arg1.getDataTypeByteSize(); 
		eaxReg = X86VariableManager.prepareArrayLocAsReg(instr.arg1, eaxReg, afg); //mov $iA, %eax
		ebxReg = X86VariableManager.prepareArrayLocAsReg(instr.arg2, ebxReg, afg); //mov $iB, %eax
		edxReg = X86VariableManager.prepareArrayLocAsReg(instr.dest, edxReg, afg); //mov $iC, %edx
		afg.writeAsmInstr("movl","$0","%ecx");	  		   	   //mov $0, %ecx
		
		X86Operand destOp, argOp1, argOp2; 
		argOp1 = new X86IndexMemoryOperand("0", eaxReg, ecxReg, 1); // "0(%eax, %ecx, 1)"
		argOp2 = new X86IndexMemoryOperand("0", ebxReg, ecxReg, 1); // "0(%ebx, %ecx, 1)"
		destOp = new X86IndexMemoryOperand("0", edxReg, ecxReg, 1); // "0(%edx, %ecx, 1)"
		
		String loopLabel = afg.getBranchLabel(); 
		afg.writeLabel(loopLabel);  // "loop: "
		/*
		X86VariableManager.writeVectorMoveInstruction(argOp1, xmm0, afg, false); //"movdqu (%eax, %ecx, 1), %xmm0
		X86VariableManager.writeVectorMoveInstruction(argOp2, xmm1, afg, false); //"movdqu (%ebx, %ecx, 1), %xmm1
		afg.writeAsmInstr(												  		 //"'sse op' %xmm1, %xmm0
				sseOp, 
				xmm1.toString(),
				xmm0.toString());
		
		X86VariableManager.writeVectorMoveInstruction(xmm0, destOp, afg, false); //movdqu %xmm0, (%edx, %ecx, 1)
		*/
		/**
		 * 	based on Intel Compiler v9.1 w/ autovectorization
		 movups floatConst2, %xmm0
		 rcpps %xmm0, %xmm1
		 mulps %xmm1, %xmm0
		 mulps %xmm1, %xmm0
		 addps %xmm1, %xmm1
		 subps %xmm0, %xmm1
		 movups floatConst1, %xmm3
		 mulps %xmm3, %xmm1
		 */
		afg.writeAsmInstr("movups", argOp2.toString(),xmm0.toString());   	// move divisor to xmm0
		afg.writeAsmInstr("rcpps", xmm0.toString(), xmm1.toString());   	// move reciprocal of arg2 to xmm1
		afg.writeAsmInstr("mulps", xmm1.toString(), xmm0.toString());   	// xmm0 = xmm0 * xmm1 = ~ 1
		afg.writeAsmInstr("mulps", xmm1.toString(), xmm0.toString());   	// xmm0 = xmm0 * xmm1 = reciprocal of arg2 with slightly different fp precision
		afg.writeAsmInstr("addps", xmm1.toString(), xmm1.toString());   	// xmm1= 2 * reciprocal
		afg.writeAsmInstr("subps", xmm0.toString(), xmm1.toString());   	// xmm0 = (2 * reciprocal) - reciprocal
		afg.writeAsmInstr("movups", argOp1.toString(), xmm3.toString());   	// xmm3 = dividend
		afg.writeAsmInstr("mulps", xmm3.toString(), xmm1.toString());   	// xmm1 = dividend * reciprocal
		afg.writeAsmInstr("movups", xmm1.toString(), destOp.toString());   	// destOp = dividend * reciprocal
		
		afg.writeAsmInstr("addl", "$16",ecxReg.toString());				  //addl $16, %ecx ---> %ecx = %ecx + 16
		afg.writeAsmInstr("cmp", "$" + Integer.toString(terminatingCondition-1), "%ecx");
		afg.writeAsmInstr("jle", loopLabel); 
	}
	
	private void processAtomicInstruction(ArithmaticInstruction instr, SymbolTable st, AssemblyfileGenerator afg, String atomicOp, boolean explicitlyStateEax) { 
		X86Operand destOp, argOp1, argOp2; 
		destOp = X86VariableManager.prepareDestVariable(instr.dest, X86Operand.OPERAND_TYPE_INDEXED_MEMORY | X86Operand.OPERAND_TYPE_MEMORY, afg);
		argOp1 = X86VariableManager.prepareArg1Variable(instr.arg1, X86Operand.OPERAND_TYPE_REGISTER, afg); 
		argOp2 = X86VariableManager.prepareArg2Variable(instr.arg2, X86Operand.OPERAND_TYPE_REGISTER | X86Operand.OPERAND_TYPE_MEMORY, afg); //no immediates, since some ops don't support immediate args (namely division at least), no indexed memory since it'll use %edx, which is an implicit operand in mul/div
		
		//set arg2 (which is a register) = arg1 + arg2
		/*
		afg.writeAsmInstr(
				atomicOp,
				argOp1.toString(),
				argOp2.toString()); 
		*/
		//TODO (Dave) only set %edx=0 on mul/div instructions which implicitly use it 
		afg.writeAsmInstr("movl","$0",X86VariableManager.getEdx().toString()); //mov $0, %edx, since %edx is implicit operand
		
		if (explicitlyStateEax) {
			afg.writeAsmInstr(											  //perform "operation" on %eax (argOp1) by argOp2, storing result in %eax
					atomicOp,
					argOp2.toString(),
					argOp1.toString());
		}
		else { //if it is a one operand instruction (like division), then it is assumed that the source/dest is %edx:%eax and only one operand is needed
			afg.writeAsmInstr(											  //"operation" %eax by argOp2, storing result in %eax (argOp1)
					atomicOp,
					argOp2.toString());
		}
		
		//now move arg1 (eax) (containing arg1 "operation" arg2) into dest
		afg.writeAsmInstr(
				X86AsmFormatter.getMoveInstruction(instr.dest),
				argOp1.toString(),
				destOp.toString()); 
		
	}
	/*
	 * gcc output for float x2; x2 = 2.0; x2 = x2 + 3.0; 
	flds	_x2
	flds	LC12
	faddp	%st, %st(1)
	this was breaking (most likely the double vs single with constants) 
	*/
	private void processAtomicFPInstruction(ArithmaticInstruction instr, SymbolTable st, AssemblyfileGenerator afg, String fpOp) {
		/*
		X86VariableManager.pushFloatingPointVariable(instr.arg1, afg);
		X86Operand op = X86VariableManager.prepareArg2Variable(instr.arg2, X86Operand.OPERAND_TYPE_INDEXED_MEMORY | X86Operand.OPERAND_TYPE_MEMORY, afg); 
		afg.writeAsmInstr(fpOp, op.toString()); 
		X86VariableManager.writeTopOfFPStack(instr.dest, afg); 
		*/
		X86VariableManager.pushFloatingPointVariable(instr.arg1, afg);
		X86VariableManager.pushFloatingPointVariable(instr.arg2, afg);
		
		//X86Operand op = X86VariableManager.prepareArg2Variable(instr.arg2, X86Operand.OPERAND_TYPE_INDEXED_MEMORY | X86Operand.OPERAND_TYPE_MEMORY, afg); 
		afg.writeAsmInstr(fpOp, "%st", "%st(1)"); 
		X86VariableManager.writeTopOfFPStack(instr.dest, afg); 
	}
		
	
	/**
	 * Helper function to process arithmatic instructions on arrays which cannot be performed with vectorized instructions and thus require a loop-based single value computation for each element in the arrays
	 * @param instr
	 * @param st
	 * @param afg
	 * @param instruction
	 * @param explicitlyStateEax For some instructions (for division its mandatory, for mul its optional), it is implicit and forced that %eax is used as the source for the op, for others subtraction, both source and dest must be explicitly stated
	 */
	private void processNonVectorizedArrayArithmatic(ArithmaticInstruction instr, SymbolTable st, AssemblyfileGenerator afg, String arithInst, boolean explicitlyStateEax) {
		int arrayTypeSize = instr.dest.getDataTypeByteSize(); 		//should be 1, 2, or 4. 
		X86Register eaxReg = X86VariableManager.getEax(); 		//used to hold first arg, and then is operated on, holds result, and then is moved into final dest
		X86Register ebxReg = X86VariableManager.getEbx(); 		//base location of second arg array
		X86Register ecxReg = X86VariableManager.getEcx(); 		//index into array (index NOT actual byte offset, will be multiplied by size)
		X86Register esiReg = X86VariableManager.getEsi();		//base location of dest array arg
		X86Register ediReg = X86VariableManager.getEdi(); 		//base location of first arg array
		X86Register edxReg = X86VariableManager.getEdx(); 		//not used, by set to zer0 in the loop for the mul/div which implicitly use it and we won't so keep it zero
		int terminatingCondition = (instr.arg1.getArraySize()-1); //iterate the total size of the arrays
		
		ediReg = X86VariableManager.prepareArrayLocAsReg(instr.arg1, ediReg, afg); //mov $iA, %edi
		ebxReg = X86VariableManager.prepareArrayLocAsReg(instr.arg2, ebxReg, afg); //mov $iB, %ebx
		esiReg = X86VariableManager.prepareArrayLocAsReg(instr.dest, esiReg, afg); //mov $iC, %esi
		
		afg.writeAsmInstr("movl","$0",ecxReg.toString());	  		   	   //mov $0, %ecx, where %ecx will be the counter for the array
		
		X86Operand destOp, argOp1, argOp2; 
		argOp1 = new X86IndexMemoryOperand("0", ediReg, ecxReg, arrayTypeSize); // "0(%edi, %ecx, size)"
		argOp2 = new X86IndexMemoryOperand("0", ebxReg, ecxReg, arrayTypeSize); // "0(%ebx, %ecx, size)"
		destOp = new X86IndexMemoryOperand("0", esiReg, ecxReg, arrayTypeSize); // "0(%esi, %ecx, size)"
		
		String loopLabel = afg.getBranchLabel(); 
		afg.writeLabel(loopLabel);  // "loop: "
		
		eaxReg.setRegisterContentsSize(instr.arg1); 
		
		
		//TODO (Dave) only set %edx=0 on mul/div instructions which implicitly use it 
		afg.writeAsmInstr("movl","$0",edxReg.toString());	  		   	   //mov $0, %edx, because it is implicitly used as upper bytes in mult/div
		
		//DO ACTUAL COMPUTATION
		//%eax = arg1 * arg2; //remember that above "mov mov1, %eax" happened implciitly in the preparation of arg1. 
		X86VariableManager.writeMoveInstruction(argOp1, eaxReg, afg); //mov argOp1 into eax
		
		if (explicitlyStateEax) {
			afg.writeAsmInstr(											  //perform "operation" on %eax by argOp2, storing result in %eax
					arithInst,
					argOp2.toString(),
					eaxReg.toString());
		}
		else { //if it is a one operand instruction (like division), then it is assumed that the source/dest is %edx:%eax and only one operand is needed
			afg.writeAsmInstr(											  //multiply %eax by argOp2, storing result in %eax
					arithInst,
					argOp2.toString());
		}
		//now move arg2 (containing arg1 + arg2) into dest
		/*
		afg.writeAsmInstr(
				X86AsmFormatter.getMoveInstruction(instr.dest),
				eaxReg.toString(), 
				destOp.toString()); 
				*/
		X86VariableManager.writeMoveInstruction(eaxReg, destOp, afg); //must use this version instead of above incase we're using bytes and require.. stuff *i don't remember its 4am and i'm tired now ;)
		//END OF COMPUTATION
		
		
		afg.writeAsmInstr("addl", "$1",ecxReg.toString());				  
		afg.writeAsmInstr("cmp", "$" + Integer.toString(terminatingCondition), ecxReg.toString());
		afg.writeAsmInstr("jle", loopLabel); 
	}
	
	
	/**
	 * Convert an addition operation into the necessary x86 asm instruction(s)
	 */
	public void processAddition(ArithmaticInstruction instr, SymbolTable st, AssemblyfileGenerator afg) {
		Variable dest, arg1, arg2; 
		dest = instr.dest;
		arg1 = instr.arg1;
		arg2 = instr.arg2; 
		
		/**
		 * Many different scenarios for array operations that must be handled
		 */
		if (instr.dest.isArray() && instr.arg1.isArray() && instr.arg2.isArray()) { 
			//TODO: (Varun w/ help of dave) Complicated when using saturation vs. not
			if (arg1.isArray() && arg2.isArray() && dest.isArray()) {
				//dest.getDataType()==Variable.DATA_TYPE_INT  && 
				switch (instr.arg1.getDataType()) {
				case Variable.DATA_TYPE_BYTE:
					processVectorInstruction(instr, st, afg, "paddsb"); //packed-add signed bytes with saturation
					return; 
				case Variable.DATA_TYPE_INT:
					processVectorInstruction(instr, st, afg, "paddd"); //packed-add quadword unsigned integer values
					return; 
				case Variable.DATA_TYPE_UBYTE:
					processVectorInstruction(instr, st, afg, "paddusb"); //packed-add unsigned bytes with saturation
					return; 
				case Variable.DATA_TYPE_UINT:
					processVectorInstruction(instr, st, afg, "paddd"); //packed-add quadword unsigned integer values
					return;
				case Variable.DATA_TYPE_FLOAT:
					processVectorInstruction(instr, st, afg, "addps"); //SIMD add of four packed single precision floating point values
					return; 
				default:
					throw new Error("Error: processAddition not defined for given data type"); 
				}
			}
		}
		else if (instr.dest.isNaturalNum() && instr.arg1.isNaturalNum() && instr.arg2.isNaturalNum()) {
			processAtomicInstruction(instr, st, afg, X86AsmFormatter.getAddInstruction(instr.dest), true); 
			return; 
		}
		else if (instr.dest.getDataType()==Variable.DATA_TYPE_FLOAT && instr.arg1.getDataType()==Variable.DATA_TYPE_FLOAT && instr.arg2.getDataType()==Variable.DATA_TYPE_FLOAT) {
			this.processAtomicFPInstruction(instr, st, afg, "faddp"); 
			return; 
		}
		throw new Error("not implemetned"); 
	}
	
	/**
	 * x86 assignment. Utilizes the move instruction<p>
	 * Many seperate cases that need to be handled, based on dest's and arg's element size (array or atomic), <br>
	 * argument type (constant or another variable).
	 */
	public void processAssignment(ArithmaticInstruction instr, SymbolTable st, AssemblyfileGenerator afg) {
		//handle arrays later
		//TODO: (Dave) handle array-based arithmatic assignment
		if (instr.dest.isArray()) { 
			throw new Error("Array assignment has not been implemented yet"); 
		}
		
		//handle atomic natural num assignment
		else if (instr.dest.isNaturalNum()) { 
			
			//else { 
				X86Operand destOp, argOp1;
				destOp = X86VariableManager.prepareDestVariable(instr.dest, X86Operand.OPERAND_TYPE_INDEXED_MEMORY | X86Operand.OPERAND_TYPE_MEMORY, afg);
				argOp1 = X86VariableManager.prepareArg1Variable(instr.arg1, X86Operand.OPERAND_TYPE_REGISTER | X86Operand.OPERAND_TYPE_IMMEDIATE, afg); //must be either register or immediate since it will be moved into a memory-based operand
				
				//move arg1 into dest
				afg.writeAsmInstr(
						X86AsmFormatter.getMoveInstruction(instr.dest),
						argOp1.toString(),
						destOp.toString()); 
				return; 
			//}
		}
		else if (instr.dest.getDataType()==Variable.DATA_TYPE_FLOAT) {
			//throw new Error("floating point atomic assignment not yet implemented"); 
			X86VariableManager.pushFloatingPointVariable(instr.arg1, afg); //push the src onto the fpu stack
			X86VariableManager.writeTopOfFPStack(instr.dest, afg); 		   //write top of stack to dest
			return; 
		}
		
		throw new Error("not implemented"); //can't be reached as code is above
	}

	/**
	 * Conversion of natural numbers (ubyte,byte,int,uint) is based upon gcc output... <br>
	 * <i>Note that char = byte, uchar = ubyte; =)<br>
	 * 
	movzbl	_charA, %eax <br>
	movb	%al, _ucharA  <br>
	movl	_uintA, %eax  <br>
	movb	%al, _ucharA <br>
	movl	_intA, %eax <br>
	movb	%al, _ucharA <br>
	movzbl	_ucharA, %eax <br>
	movb	%al, _charA <br>
	movl	_uintA, %eax <br>
	movb	%al, _charA <br>
	movl	_intA, %eax <br>
	movb	%al, _charA <br>
	movzbl	_ucharA, %eax <br>
	movl	%eax, _intA <br>
	movsbl	_charA,%eax <br>
	movl	%eax, _intA <br>
	movl	_uintA, %eax <br>
	movl	%eax, _intA <br>
	movzbl	_ucharA, %eax <br>
	movl	%eax, _uintA <br>
	movsbl	_charA,%eax <br>
	movl	%eax, _uintA <br>
	movl	_intA, %eax <br>
	movl	%eax, _uintA <br>
	 */
	public void processConversion(ArithmaticInstruction instr, SymbolTable st, AssemblyfileGenerator afg) {
		
		Variable src, dest;
		src = instr.arg1;
		dest = instr.dest; 
		int srcType = src.getDataType(); //will use/write these a lot, might as well cache :) //note to self, increased usage of smily faces in comments as sleep deprivation kicks in... hehe
		int destType = dest.getDataType(); 
		/*
		if (srcType==Variable.DATA_TYPE_FLOAT || destType==Variable.DATA_TYPE_FLOAT) {
			throw new Error("float conversions not supported yet"); 
		}
		*/
		if (srcType==destType) { 
			throw new Error("Conversions on same datatype invalid");
		}
		if (srcType==Variable.DATA_TYPE_COMPLEX || destType==Variable.DATA_TYPE_COMPLEX) {
			throw new Error("complex conversions aren't defined/allowed"); 
		}
		if (srcType==Variable.DATA_TYPE_STRING || destType==Variable.DATA_TYPE_STRING) {
			throw new Error("strings conversions aren't defined/allowed"); 
		}
		
		//X86Register ecxReg = X86VariableManager.prepareArg2Variable(src, X86Operand.OPERAND_TYPE_REGISTER); //move raw value into register %ecx
		//natural number conversions were written before floating point, so they are stored here, although this could probably be rewritten to be more general to include FP (especialy the movsbl/movzbl logic
		if (srcType!=Variable.DATA_TYPE_FLOAT && destType !=Variable.DATA_TYPE_FLOAT) {
			X86Register eax = X86VariableManager.getEax();
			X86Operand srcOperand; 
			if ((destType==Variable.DATA_TYPE_INT || destType==Variable.DATA_TYPE_UINT) && srcType==Variable.DATA_TYPE_BYTE) { //don't ask why, see above
				eax = X86VariableManager.prepareRegVariableWithExplicitMove(src, "movsbl", afg); //only use movsbl when going from char-->uint/int
				srcOperand = eax; 
			}
			else if ((destType==Variable.DATA_TYPE_UBYTE && srcType==Variable.DATA_TYPE_BYTE) || srcType==Variable.DATA_TYPE_UBYTE) {
				eax = X86VariableManager.prepareRegVariableWithExplicitMove(src, "movzbl", afg); 
				srcOperand = eax;
			}
			else {
				srcOperand = X86VariableManager.prepareArg1Variable(src, X86Operand.OPERAND_TYPE_REGISTER, afg); 
			}
			X86Operand destOperand = X86VariableManager.prepareDestVariable(dest, X86Operand.OPERAND_CLASS_ANY, afg); 
			eax.setRegisterContentsSize(dest); //move instruction should be based upon the dest
			X86VariableManager.writeMoveInstruction(eax, destOperand, afg); 
			return; 
		}
		//convert from some natural num type to float
		/**
		 * Note: all code from this section was derived directly from what GCC does for the following:
		 * floatA = ucharA;
		 * floatA = charA;
		 * floatA = uintA;
		 * floatA = intA;
		 * --------------
		 * 
		 */
		if (destType==Variable.DATA_TYPE_FLOAT) {
			/**
			 * Based on gcc's handling of conversions
			 movzbw	_ucharA, %ax    #uchar --> float
			 pushw	%ax
			 filds	(%esp)
			 leal	2(%esp), %esp
			 fstps	_floatA
			 movsbw	_charA,%ax      #char  --> float
			 pushw	%ax
			 filds	(%esp)
			 leal	2(%esp), %esp
			 fstps	_floatA
			 */
			if (srcType==Variable.DATA_TYPE_BYTE || srcType==Variable.DATA_TYPE_UBYTE) {
				X86Operand srcOperand = X86VariableManager.prepareArg2Variable(src, X86Operand.OPERAND_TYPE_IMMEDIATE | X86Operand.OPERAND_TYPE_INDEXED_MEMORY | X86Operand.OPERAND_TYPE_MEMORY, afg); 
				X86Operand destOperand = X86VariableManager.prepareDestVariable(dest, X86Operand.OPERAND_TYPE_INDEXED_MEMORY | X86Operand.OPERAND_TYPE_MEMORY, afg); 
				
				if (srcType==Variable.DATA_TYPE_UBYTE) {
					afg.writeAsmInstr("movzbw", srcOperand.toString(), "%ax"); 
				}
				else { //DATA_TYPE_BYTE
					afg.writeAsmInstr("movsbw", srcOperand.toString(), "%ax"); 
				}
				afg.writeAsmInstr("pushw", "%ax");  					//push byte/ubyte onto stack
				afg.writeAsmInstr("filds", "(%esp)"); 					//load an "integer" from stack (using the proper move instructions from b4 will zero the proper stuff)
				afg.writeAsmInstr("leal", "2(%esp)", "%esp"); 		//does same thing as popping the byte/ubyte off the stack. Since GCC is using it, I'll trust it and presume its more efficient than using pop or just adding
				afg.writeAsmInstr("fstps", destOperand.toString()); 	//store the top of the FPU stack containing the converted byte to FPU. 
				return; 
			}
			/**
			 * 
			 Based on GCC's output
			 movl	_uintA, %eax
			 movl	$0, %edx
			 pushl	%edx
			 pushl	%eax
			 fildq	(%esp)
			 leal	8(%esp), %esp
			 fstps	_floatA
			 */
			else if (srcType==Variable.DATA_TYPE_UINT) {
				X86Operand srcOperand = X86VariableManager.prepareArg2Variable(src, X86Operand.OPERAND_TYPE_IMMEDIATE | X86Operand.OPERAND_TYPE_INDEXED_MEMORY | X86Operand.OPERAND_TYPE_MEMORY, afg); 
				X86Operand destOperand = X86VariableManager.prepareDestVariable(dest, X86Operand.OPERAND_TYPE_INDEXED_MEMORY | X86Operand.OPERAND_TYPE_MEMORY, afg); 
				
				afg.writeAsmInstr("movl", srcOperand.toString(), "%eax"); //move uint to eax
				afg.writeAsmInstr("movl", "$0", "%edx"); 				  //making 8byte long, zero the top 4 bytes, will need this
				afg.writeAsmInstr("pushl", "%edx");  					//push 0 onto stack
				afg.writeAsmInstr("pushl", "%eax");  					//push long onto stack
				afg.writeAsmInstr("fildq", "(%esp)"); 					//load an a long onto the FPU stack
				afg.writeAsmInstr("leal", "8(%esp)", "%esp"); 		//does same thing as popping the long off the stack. Since GCC is using it, I'll trust it and presume its more efficient than using pop or just adding
				afg.writeAsmInstr("fstps", destOperand.toString()); 	//store the top of the FPU stack containing the converted uint to FPU. 
				return; 
			}
			/**
			 * Based on GCC. This one is simple (the one time we can just use a single proper FPU instruction to load directly, no twiddling)
			 fildl	_intA
			 fstps	_floatA
			 */
			else if (srcType==Variable.DATA_TYPE_INT) {
				X86Operand srcOperand = X86VariableManager.prepareArg2Variable(src, X86Operand.OPERAND_TYPE_IMMEDIATE | X86Operand.OPERAND_TYPE_INDEXED_MEMORY | X86Operand.OPERAND_TYPE_MEMORY, afg); 
				X86Operand destOperand = X86VariableManager.prepareDestVariable(dest, X86Operand.OPERAND_TYPE_INDEXED_MEMORY | X86Operand.OPERAND_TYPE_MEMORY, afg); 
				
				afg.writeAsmInstr("fildl", srcOperand.toString()); 		//load int
				afg.writeAsmInstr("fstps", destOperand.toString()); 	//store the top of the FPU stack containing the converted uint to FPU. 
				return; 
			}
		}
		/**
		 * All output is based on GCC's output:
		 ucharA = floatA;
		 charA  = floatA;
		 uintA = floatA;
		 intA = floatA;
		 ---------------
		 * NOTE: ABSOLUTE NECESSITY: as is, the implementation does not put any variables on the stack, all are declared globally in bss or data sections
		 * the below conversions utilize the stack, assuming that space has been allocated (as will be done in the AsmGenerator).
		 * If the implementation changes
		 */
		if (srcType==Variable.DATA_TYPE_FLOAT) {
			/**
			 subl	$8, %esp
			 flds	_floatA
			 fnstcw	-2(%ebp)
			 movzwl	-2(%ebp), %eax
			 orw	$3072, %ax
			 movw	%ax, -4(%ebp)
			 fldcw	-4(%ebp)
			 fistps	-6(%ebp)
			 fldcw	-2(%ebp)
			 movzwl	-6(%ebp), %eax
			 movb	%al, _ucharA
			 */
			if (destType==Variable.DATA_TYPE_UBYTE) {
				X86Operand srcOperand = X86VariableManager.prepareArg2Variable(src, X86Operand.OPERAND_TYPE_INDEXED_MEMORY | X86Operand.OPERAND_TYPE_MEMORY, afg);
				X86Operand destOperand = X86VariableManager.prepareDestVariable(dest,X86Operand.OPERAND_TYPE_INDEXED_MEMORY | X86Operand.OPERAND_TYPE_MEMORY, afg); 
				afg.writeAsmInstr("subl", "$8", "%esp"); //make some space for temp variables we'll need. Remember to add back at the end of conversion
				afg.writeAsmInstr("flds", srcOperand.toString()); //load onto fpu stack
				afg.writeAsmInstr("fnstcw", "-2(%ebp)");		  //save the fpu control word
				afg.writeAsmInstr("movzwl", "-2(%ebp)", "%eax");  //copy fpu control word to eax
				afg.writeAsmInstr("orw", "$3072", "%ax");		  //or it (presumably changing the fpu control mode
				afg.writeAsmInstr("movw", "%ax", "-4(%ebp)");	  //save the new version of the control word on stack
				afg.writeAsmInstr("fldcw", "-4(%ebp)");					
				afg.writeAsmInstr("fistps", "-6(%ebp)");
				afg.writeAsmInstr("fldcw", "-2(%ebp)");
				afg.writeAsmInstr("movzwl", "-6(%ebp)", "%eax");
				afg.writeAsmInstr("movb", "%al", destOperand.toString());
				afg.writeAsmInstr("addl", "$8", "%esp"); 		  //remove space made for temp variables
				return; 
			}
			
			/**
			 * Based on GCC output:
			 subl	$16, %esp
			 flds	_floatA
			 fnstcw	-2(%ebp)
			 movzwl	-2(%ebp), %eax
			 orw	$3072, %ax
			 movw	%ax, -4(%ebp)
			 fldcw	-4(%ebp)
			 fistpq	-16(%ebp)
			 fldcw	-2(%ebp)
			 movl	-16(%ebp), %eax
			 movl	-12(%ebp), %edx   #unnecessary... bad GCC code re-use. this would only use this if we needed to write a long, not an int
			 movl	%eax, _uintA
			 */
			if (destType==Variable.DATA_TYPE_UINT) {
				X86Operand srcOperand = X86VariableManager.prepareArg2Variable(src, X86Operand.OPERAND_TYPE_INDEXED_MEMORY | X86Operand.OPERAND_TYPE_MEMORY, afg);
				X86Operand destOperand = X86VariableManager.prepareDestVariable(dest,X86Operand.OPERAND_TYPE_INDEXED_MEMORY | X86Operand.OPERAND_TYPE_MEMORY, afg); 
				afg.writeAsmInstr("subl", "$16", "%esp"); //make some space for temp variables we'll need. Remember to add back at the end of conversion
				afg.writeAsmInstr("flds", srcOperand.toString()); //load onto fpu stack
				afg.writeAsmInstr("fnstcw", "-2(%ebp)");		  //save the fpu control word
				afg.writeAsmInstr("movzwl", "-2(%ebp)", "%eax");  //copy fpu control word to eax
				afg.writeAsmInstr("orw", "$3072", "%ax");		  //or it (presumably changing the fpu control mode
				afg.writeAsmInstr("movw", "%ax", "-4(%ebp)");	  //save the new version of the control word on stack
				afg.writeAsmInstr("fldcw", "-4(%ebp)");					
				afg.writeAsmInstr("fistpq", "-16(%ebp)");
				afg.writeAsmInstr("fldcw", "-2(%ebp)");
				afg.writeAsmInstr("movl", "-16(%ebp)", "%eax");
				afg.writeAsmInstr("movl", "%eax", destOperand.toString());
				afg.writeAsmInstr("addl", "$16", "%esp"); 		  //remove space made for temp variables
				return; 
			}
			
			/**
			 * Output based on Intel v9.1 Compiler with options "/QxW /O2 /Qvec-report3".
			 * Utilizes newer instruction "cvttss2si"
			 cvttss2si eax, DWORD PTR _floatA                        ;52.9
			 mov       DWORD PTR _intA, eax                          ;52.2
			 
			 
			 --------
			 movss     xmm0, DWORD PTR _2il0floatpacket$11           ;110.5
			 cvttss2si eax, xmm0                                     ;111.8
			 mov       ebp, 1325400064                               ;110.5
			 mov       DWORD PTR _f, ebp                             ;110.1
			 mov       DWORD PTR _intA, eax  
			 * -----
			 * Remember, above is in intel syntax, not att, so switch dest/src and append postfix to instruction :)
			 * Note: will throw exception if the fp value is larger than the max for a 32bit integer
			 */
			if (destType==Variable.DATA_TYPE_INT || destType==Variable.DATA_TYPE_BYTE) {
				X86Operand srcOperand = X86VariableManager.prepareArg2Variable(src, X86Operand.OPERAND_TYPE_INDEXED_MEMORY | X86Operand.OPERAND_TYPE_MEMORY, afg);
				X86Operand destOperand = X86VariableManager.prepareDestVariable(dest,X86Operand.OPERAND_TYPE_INDEXED_MEMORY | X86Operand.OPERAND_TYPE_MEMORY, afg); 
				afg.writeAsmInstr("cvttss2si", srcOperand.toString(), "%eax"); //convert single precision floating point value to signed doubleword integer
				afg.writeAsmInstr(X86AsmFormatter.getMoveInstruction(src), "%eax", destOperand.toString()); //movl or movb %eax into dest
				return; 
			}
		}
		
		throw new Error("not implemented");
	}

	public void processConvolution(ArithmaticInstruction instr, SymbolTable st, AssemblyfileGenerator afg) {
		// TODO Auto-generated method stub
		throw new Error("not implemented");
	}

	//for reference: crazy Intel Compiler v9.1 generated code for SSE division!
/*
	movups floatConst2, %xmm0
	rcpps %xmm0, %xmm1
	mulps %xmm1, %xmm0
	mulps %xmm1, %xmm0
	addps %xmm1, %xmm1
	subps %xmm0, %xmm1
	movups floatConst1, %xmm3
	mulps %xmm3, %xmm1
	*/
	
	public void processDivision(ArithmaticInstruction instr, SymbolTable st, AssemblyfileGenerator afg) {
		if (instr.dest.isArray() && instr.arg1.isArray() && instr.arg2.isArray()) {
			switch (instr.arg1.getDataType()) {
			case Variable.DATA_TYPE_BYTE:
			case Variable.DATA_TYPE_INT:
			case Variable.DATA_TYPE_UBYTE:
			case Variable.DATA_TYPE_UINT:
				processNonVectorizedArrayArithmatic(instr, st, afg, X86AsmFormatter.getDivideInstruction(instr.arg1), false); 
				return;
			case Variable.DATA_TYPE_FLOAT:
				processVectorFPDiv(instr, st, afg); 
				return; 
			default:
				throw new Error("Error: processDivision not defined for given data type"); 
			}
		}
		else if (instr.dest.isNaturalNum() && instr.arg1.isNaturalNum() && instr.arg2.isNaturalNum()) {
			processAtomicInstruction(instr, st, afg, X86AsmFormatter.getDivideInstruction(instr.dest), false); 
			return; 
		}
		
		else if (instr.dest.getDataType()==Variable.DATA_TYPE_FLOAT && instr.arg1.getDataType()==Variable.DATA_TYPE_FLOAT && instr.arg2.getDataType()==Variable.DATA_TYPE_FLOAT) {
			this.processAtomicFPInstruction(instr, st, afg, "fdivrp"); 
			return; 
		}
		
		throw new Error("Error: processDivision not defined for given data type"); 
	}

	public void processMultiplication(ArithmaticInstruction instr, SymbolTable st, AssemblyfileGenerator afg) {
		Variable dest, arg1, arg2; 
		dest = instr.dest;
		arg1 = instr.arg1;
		arg2 = instr.arg2; 
		
		/**
		 * Many different scenarios for array operations that must be handled
		 */
		if (dest.isArray() && instr.arg1.isArray() && instr.arg2.isArray()) { 
			switch (instr.arg1.getDataType()) {
			case Variable.DATA_TYPE_BYTE:
			case Variable.DATA_TYPE_INT:
			case Variable.DATA_TYPE_UBYTE:
			case Variable.DATA_TYPE_UINT:
				processNonVectorizedArrayArithmatic(instr, st, afg, X86AsmFormatter.getMultiplyInstruction(instr.arg1), false); 
				return;
			case Variable.DATA_TYPE_FLOAT:
				processVectorInstruction(instr, st, afg, "mulps"); //SIMD multiplication of four packed single precision floating point values
				return; 
			default:
				throw new Error("Error: processMultiplication not defined for given data type"); 
			}
		}
		else if (instr.dest.isNaturalNum() && instr.arg1.isNaturalNum() && instr.arg2.isNaturalNum()) {
			processAtomicInstruction(instr, st, afg, X86AsmFormatter.getMultiplyInstruction(instr.dest), false); 
			return; 
		}
		else if (instr.dest.getDataType()==Variable.DATA_TYPE_FLOAT && instr.arg1.getDataType()==Variable.DATA_TYPE_FLOAT && instr.arg2.getDataType()==Variable.DATA_TYPE_FLOAT) {
			this.processAtomicFPInstruction(instr, st, afg, "fmulp"); 
			return; 
		}
		throw new Error("unimplemented arg combo");
	}

	public void processSubtraction(ArithmaticInstruction instr, SymbolTable st, AssemblyfileGenerator afg) {
		if (instr.dest.isArray() && instr.arg1.isArray() && instr.arg2.isArray()) { 
			//TODO: (Varun w/ help of dave) Complicated when using saturation vs. not.. *commence playing of Avril Lavigne - Complicated.mp3 here ;)*
			if (instr.arg1.isArray() && instr.arg2.isArray() && instr.dest.isArray()) {
				//dest.getDataType()==Variable.DATA_TYPE_INT  && 
				switch (instr.arg1.getDataType()) {
				case Variable.DATA_TYPE_BYTE:
					processVectorInstruction(instr, st, afg, "psubsb"); //packed-add signed bytes with saturation
					return; 
				case Variable.DATA_TYPE_INT:
					processVectorInstruction(instr, st, afg, "psubd"); //packed-add quadword unsigned integer values
					return; 
				case Variable.DATA_TYPE_UBYTE:
					processVectorInstruction(instr, st, afg, "psubusb"); //packed-add unsigned bytes with saturation
					return; 
				case Variable.DATA_TYPE_UINT:
					processVectorInstruction(instr, st, afg, "psubd"); //packed-add quadword unsigned integer values
					return;
				case Variable.DATA_TYPE_FLOAT:
					processVectorInstruction(instr, st, afg, "subps"); //SIMD subtraction of four packed single precision floating point values
					return; 
				default:
					throw new Error("Error: processSubtraction not defined for given data type"); 
				}
			}
		}
		else if (instr.dest.isNaturalNum() && instr.arg1.isNaturalNum() && instr.arg2.isNaturalNum()) {
			processAtomicInstruction(instr, st, afg, X86AsmFormatter.getSubtractInstruction(instr.dest), true); 
			return; 
		}
		else if (instr.dest.getDataType()==Variable.DATA_TYPE_FLOAT && instr.arg1.getDataType()==Variable.DATA_TYPE_FLOAT && instr.arg2.getDataType()==Variable.DATA_TYPE_FLOAT) {
			this.processAtomicFPInstruction(instr, st, afg, "fsubrp"); //notice we use reverse, since it does the pushing of args onto FPU stack in reverse order
			return; 
		}
		throw new Error("subtraction with specified operand types not implemented yet"); 
	}

}
