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

import dspl.SymbolTable;
import dspl.backend.AssemblyfileGenerator;
import dspl.backend.BranchInstructionProcessor;
import dspl.backend.InstructionProcessor;
import dspl.backend.x86.operands.X86Operand;
import dspl.backend.x86.operands.X86Register;
import dspl.intermediate.Comparison;
import dspl.intermediate.instructions.BranchInstruction;
import dspl.intermediate.variable.Variable;

/**
 * x86 implmentation of the BranchInstructionProcessor
 * @author David
 */
public class X86BranchInstructionProcessor extends BranchInstructionProcessor {

	/**
	 * Returns the x86 ASM instruction corresponding to the appriopriate conditional jump
	 * for the specific conditionType
	 * @param conditionType
	 * @return
	 */
	public static String getConditionalJumpInstruction(int conditionType, Variable var)  {
		//note: Float is actually signed, but in term sof the proper jump instruction, based on Intel v9.1 compiler, the comisd instruction is followe dby above/below rather than greather/less
		boolean isSigned = (var.getDataType()==Variable.DATA_TYPE_INT || var.getDataType()==Variable.DATA_TYPE_BYTE); 
		switch (conditionType) {
		//negative versions, useful if we want to jump only if the condition isn't true
		//the reason for inverting them is so that when generating asm for if (expres) { iftrue... } else { ifwasn'ttrue }
		//when operating on the above, using the positive versions of jumps, we would be jumping iftrue, thus the else code
		//would actually come before the if code, making debugging the output more confusing. by inverting it gurantees
		//that the order of instructions is maintained with if's expressions coming before else's expressions
		case Comparison.CONDITION_EQUAL:
			return "jne";
		case Comparison.CONDITION_GE:
			if (isSigned) {
				return "jl";
			}
			else {
				return "jb"; 
			}
		case Comparison.CONDITION_GREATER:
			if (isSigned) {
				return "jle";
			}
			else {
				return "jbe";
			}
		case Comparison.CONDITION_LE:
			if (isSigned) {
				return "jg";
			}
			else {
				return "ja";
			}
		case Comparison.CONDITION_LESS:
			if (isSigned) {
				return "jge";
			}
			else {
				return "jae";
			}
		case Comparison.CONDITION_NEQUAL:
			return "je";
			
		// the positive versions, when one actually does jump based on the condition
		/*
		case Comparison.CONDITION_EQUAL:
			return "je";
		case Comparison.CONDITION_GE:
			return "jae";
		case Comparison.CONDITION_GREATER:
			return "ja";
		case Comparison.CONDITION_LE:
			return "jbe";
		case Comparison.CONDITION_LESS:
			return "jb";
		case Comparison.CONDITION_NEQUAL:
			return "jne";
			*/ 
		default:
			throw new Error("Unknown BranchInstruction conditionType: " + conditionType); 
		}
	}
	
	/**
	 * Convert a BranchInstruction into x86 asm instructions
	 */
	public void processBranchInstruction(BranchInstruction inst, InstructionProcessor ip, SymbolTable st, AssemblyfileGenerator afg) {
		Variable arg1 = inst.comp.arg1; 
		Variable arg2 = inst.comp.arg2;
		
		//TODO: (Dave) branch instructions involving comparisons between arrays, or redefine LRM to elminate comparisons between arrays
		if (arg1.isArray() || arg2.isArray()) { 
			throw new Error("Comparisons of arrays is not supported"); 
		}
		
		else {
			String elseBranchLabel  = afg.getBranchLabel(); //the label to branch to if the comparison evaluates false
			String afterBranchLabel = afg.getBranchLabel(); 
			
			X86Operand argOp1, argOp2; 
			
			//if arg2 is constant, can utilize immediate comparison
			argOp1 = X86VariableManager.prepareArg1Variable(arg1, X86Operand.OPERAND_TYPE_REGISTER, afg); 
			argOp2 = X86VariableManager.prepareArg2Variable(arg2, X86Operand.OPERAND_CLASS_ANY, afg); 
			afg.writeAsmInstr("cmp", argOp2.toString(), argOp1.toString()); //ORDER OF OPERANDS VERY IMPORTANT!!! 
			
		
			//based on Intel v9.1 Compiler with P4 architecture enabled, with some modifications
			if (inst.comp.arg1.getDataType()==Variable.DATA_TYPE_FLOAT) {
				X86Register xmm0 = X86VariableManager.getXmm0();
				X86Register xmm1 = X86VariableManager.getXmm1();
				
				argOp1 = X86VariableManager.prepareArg1Variable(arg1, X86Operand.OPERAND_TYPE_INDEXED_MEMORY | X86Operand.OPERAND_TYPE_MEMORY, afg); 
				argOp2 = X86VariableManager.prepareArg2Variable(arg2, X86Operand.OPERAND_TYPE_INDEXED_MEMORY | X86Operand.OPERAND_TYPE_MEMORY, afg); 
				if (arg1.isValueSet()) {//if constant, mov from memory (its a double)
					afg.writeAsmInstr("movsd", argOp1.toString(), xmm0.toString());
				}
				else {
					afg.writeAsmInstr("cvtss2sd", argOp1.toString(), xmm0.toString());
				}
				
				if (arg2.isValueSet()) { //if its a floating point constant stored in symbol table, then its actually a double
					afg.writeAsmInstr("comisd", argOp2.toString(), xmm0.toString()); 
				}
				else {
					afg.writeAsmInstr("cvtss2sd", argOp2.toString(), xmm1.toString());
					afg.writeAsmInstr("comisd",   xmm1.toString(),   xmm0.toString()); 
				}
			}
			else { //else for byte/ubyte/int/uint
				//if arg2 is constant, can utilize immediate comparison
				argOp1 = X86VariableManager.prepareArg1Variable(arg1, X86Operand.OPERAND_TYPE_REGISTER, afg); 
				argOp2 = X86VariableManager.prepareArg2Variable(arg2, X86Operand.OPERAND_CLASS_ANY, afg); 
				afg.writeAsmInstr("cmp", argOp2.toString(), argOp1.toString()); //ORDER OF OPERANDS VERY IMPORTANT!!! 
			}
			
			//write the appriopriate branch instruction which occurs if arg1 *operation* arg2 is false
			afg.writeAsmInstr(getConditionalJumpInstruction(inst.comp.condition, inst.comp.arg1), elseBranchLabel); 
			
			
			//write out all of the if's instructions
			ip.processInstruction(inst.ifInst); 
			
			//at the end of if, now jmp to the afterBranchLabel, skipping ovr the else portion
			afg.writeAsmInstr("jmp", afterBranchLabel); 
			
			//write the else label so that if the branch does occur, it will jump here
			afg.writeLabel(elseBranchLabel); 
		
			//write out all of the else condition's instructions
			ip.processInstruction(inst.elseInst);  
			
			//write the afterBranchLabel which if will jump to after it has evalutead all of its instructions
			afg.writeLabel(afterBranchLabel);  
		}
	}
}
