/* $Id$ 
 *
 * Generate intermediate code, intermediate code implementation of builtins.
 *
 * Copyright (C) 2008-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#ifndef __GC_BUILTINS_HPP_INCLUDED
#define __GC_BUILTINS_HPP_INCLUDED

#include "frontend/visitor/GenCode.hpp"
#include "frontend/misc/BuiltinSymbolTable.hpp"
#include <list>
#include "intermediate/opcodes/OpCode.hpp"
#include "intermediate/operands/RegisterFactory.hpp"
#include "intermediate/opcodes/Sub.hpp"
#include "intermediate/opcodes/Add.hpp"
#include "intermediate/opcodes/IMul.hpp"
#include "intermediate/opcodes/Div.hpp"

namespace ast {

//! abstract base class for an arbitrary builtin operator.
/** GCBuiltins is the abstract base class for all builtin operators.
 *  To generate code, the following functions must always get called:
 *
 *    reset();
 *     generate code to evaluate first operand here ...
 *    shortCutfirstOp(cc, firstOp);
 *     generate code to evaluate the remaining operands here ...
 *    emitCode(cc, operands);
 *
 *  The operands parameter of emitCode must contain *all* operands.
 */
class GCBuiltins {
public:
	//! virtual dummy d'tor.
	virtual ~GCBuiltins() {}

	//! generic abstract interface to emit code for a builtin function
	/** @param gc Code generator instance.
	 *  @param ops list of operands.
	 *  @return RegisterSet containing the result of the operands.
	 */
	virtual RegisterSet
	emitCode(GenCode &gc, std::list<AssociationElement*> ops) const = 0;
};

//! abstract base class for non-short-circuit operators
class GCBuiltinsNoShortCircuit : public GCBuiltins {
private:
	//! generic abstract interface to emit code for a builtin function
	/** @param gc Code generator instance.
	 *  @param ops list of operands.
	 *  @return RegisterSet containing the result of the operands.
	 */
	virtual RegisterSet
	emitCode(GenCode &gc, std::list<AssociationElement*> ops) const;

protected:
	//! emit code to calculate the result
	/** @param cc container to add generated code to.
	 *  @param ops RegisterSets containing the operands.
	 *  @return RegisterSet containing the result.
	 */
	virtual RegisterSet 
	calculate(
		intermediate::CodeContainer &cc, 
		std::list<RegisterSet> ops,
		std::list<enum BaseType> btl) const = 0;
};

//! abstract base class for all binary short-circuit operators.
class GCBuiltinsShortCircuit : public GCBuiltins {
private:
	//! generic abstract interface to emit code for a builtin function
	/** 
	 * res = <default>
	 * if (left == <shortCut>) {
         * 	goto out
	 * }
	 * if (right == <shortCut>) {
	 * 	goto out
	 * 	res = < 1 - default >
	 * }
	 *
	 *  @param gc Code generator instance.
	 *  @param ops list of operands.
	 *  @return RegisterSet containing the result of the operands.
	 */
	virtual RegisterSet
	emitCode(GenCode &gc, std::list<AssociationElement*> ops) const;

protected:
	/** determine the default value, which gets used if the comparison
	 *  against getShortCut is equal.
	 *  @return the default value of the short circuit operator in the
	 *          range between 0 and 1.
	 */
	virtual universal_integer getDefault(void) const = 0;

	/** determine the value to check against, if the shortcut condition
	 *  matches.
	 *  @return if the operand is equal to the return value, the default
	 *          value will be used as result.
	 */
	virtual universal_integer getShortCut(void) const = 0;
private:
	//! evaluate the result of one assocation and return it in an operand
	/** @param cc CodeContainer to add code to.
	 *  @param assoc association to evaluate. (FIXME interface ?)
	 *  @return operand containing the result */
	static intermediate::Operand *
	evaluate(intermediate::CodeContainer *cc, AssociationElement &assoc);
};

//! implementation of the "AND" short-circuit operator.
class GCBuiltinsAnd : public GCBuiltinsShortCircuit {
private:
	virtual universal_integer getDefault(void) const {
		return 0;
	}

	virtual universal_integer getShortCut(void) const {
		return 0;
	}
};

//! implementation of the "OR" short-circuit operator.
class GCBuiltinsOr : public GCBuiltinsShortCircuit {
private:
	virtual universal_integer getDefault(void) const {
		return 1;
	}

	virtual universal_integer getShortCut(void) const {
		return 1;
	}
};

//! implementation of the "NAND" short-circuit operator.
class GCBuiltinsNand : public GCBuiltinsShortCircuit {
private:
	virtual universal_integer getDefault(void) const {
		return 1;
	}

	virtual universal_integer getShortCut(void) const {
		return 0;
	}
};

//! implementation of the "NOR" short-circuit operator.
class GCBuiltinsNor : public GCBuiltinsShortCircuit {
private:
	virtual universal_integer getDefault(void) const {
		return 0;
	}

	virtual universal_integer getShortCut(void) const {
		return 1;
	}
};

//! no-short circuit unary operators for non-composite types.
class GCBuiltinsUnop : public GCBuiltinsNoShortCircuit {
private:
	//! emit code to calculate the result
	/** @param ops list of operands
	 *  @return RegisterSet containing the result.
	 */
	virtual RegisterSet 
	calculate(
		intermediate::CodeContainer &cc, 
		std::list<RegisterSet> ops,
		std::list<enum BaseType> btl) const;

protected:
	//! calculate the result of the unary operator
	/** @param cc container to add generated code to.
	 *  @param op operand
	 *  @return Register containing the result.
	 */
	virtual intermediate::Register *
	calcUnOp(
		intermediate::CodeContainer &cc,
		intermediate::Operand *op) const = 0;
};

//! no-short circuit binary opartors for non-composite types.
class GCBuiltinsBinOp : public GCBuiltinsNoShortCircuit {
private:
	//! emit code to calculate the result
	/** @param ops list of operands
	 *  @return RegisterSet containing the result.
	 */
	virtual RegisterSet 
	calculate(
		intermediate::CodeContainer &cc, 
		std::list<RegisterSet> ops,
		std::list<enum BaseType> btl) const;

protected:
	//! calculate the result of the binary operator
	/** @param cc container to add generated code to.
	 *  @param left left operand
	 *  @param right right operand.
	 *  @return Register containing the result.
	 */
	virtual intermediate::Register *
	calcBinOp(
		intermediate::CodeContainer &cc,
		intermediate::Operand *left,
		intermediate::Operand *right) const = 0;
};


//! unary not for boolean and bit.
class GCBuiltinsNot : public GCBuiltinsUnop {
private:
	//! emit code for an unary not (bit, boolean)
	virtual intermediate::Register *
	calcUnOp(
		intermediate::CodeContainer &cc, 
		intermediate::Operand *op) const;
};

//! xor for boolean and bit.
class GCBuiltinsXor : public GCBuiltinsBinOp {
protected:
	//! calculate the result of XOR
	/** @param cc container to add generated code to.
	 *  @param left left operand
	 *  @param right right operand.
	 *  @return Register containing the result.
	 */
	virtual intermediate::Register *
	calcBinOp(
		intermediate::CodeContainer &cc,
		intermediate::Operand *left,
		intermediate::Operand *right) const;

};

//! xnor for boolean and bit.
class GCBuiltinsXnor : public GCBuiltinsXor {
private:
	//! calculate the result of XOR
	/** @param cc container to add generated code to.
	 *  @param left left operand
	 *  @param right right operand.
	 *  @return Register containing the result.
	 */
	virtual intermediate::Register *
	calcBinOp(
		intermediate::CodeContainer &cc,
		intermediate::Operand *left,
		intermediate::Operand *right) const;

};

//! base class for comparison operators for non-composite types.
class GCBuiltinsCompare : public GCBuiltinsBinOp {
private:
	//! calculate the result of XOR
	/** @param cc container to add generated code to.
	 *  @param left left operand
	 *  @param right right operand.
	 *  @return Register containing the result.
	 */
	virtual intermediate::Register *
	calcBinOp(
		intermediate::CodeContainer &cc,
		intermediate::Operand *left,
		intermediate::Operand *right) const;

protected:
	//! emit the comparison operation branch opcode.
	/** If the branch is not taken, 1-default value will be the result.
	 *  If the branch is taken, default value will be the result.
	 *  @param left left operand to compare
	 *  @param right right operand to compare
	 *  @param out branch target that uses the default value.
	 *  @return generated branch opcode.
	 */
	virtual intermediate::OpCode *
	emitBranch(
		intermediate::Operand *left, 
		intermediate::Operand *right, 
		intermediate::Label *out) const = 0;
	
	//! get the default value of the comparison operator.
	/** @return default value of the comparison operator.
	 */
	virtual universal_integer getDefaultValue(void) const = 0;
};

//! equality for non-composite types.
class GCBuiltinsEqual : public GCBuiltinsCompare {
private:
	//! emit a je.
	virtual intermediate::OpCode *
	emitBranch(
		intermediate::Operand *left, 
		intermediate::Operand *right, 
		intermediate::Label *out) const;

	virtual universal_integer getDefaultValue(void) const {
		return VHDL_TRUE;
	}
};

//! inequality for non-composite types.
class GCBuiltinsInEqual : public GCBuiltinsCompare {
private:
	//! emit a jne
	virtual intermediate::OpCode *
	emitBranch(
		intermediate::Operand *left, 
		intermediate::Operand *right, 
		intermediate::Label *out) const;

	virtual universal_integer getDefaultValue(void) const {
		return VHDL_TRUE;
	}
};

//! less than for non-composite types.
class GCBuiltinsLess : public GCBuiltinsCompare {
private:
	//! emit a jb
	virtual intermediate::OpCode *
	emitBranch(
		intermediate::Operand *left, 
		intermediate::Operand *right, 
		intermediate::Label *out) const;

	virtual universal_integer getDefaultValue(void) const {
		return VHDL_TRUE;
	}
};

//! less or equal for non-composite types.
class GCBuiltinsLessEqual : public GCBuiltinsCompare {
private:
	//! emit a jbe
	virtual intermediate::OpCode *
	emitBranch(
		intermediate::Operand *left, 
		intermediate::Operand *right, 
		intermediate::Label *out) const;

	virtual universal_integer getDefaultValue(void) const {
		return VHDL_TRUE;
	}
};

//! greater than for non-composite types.
class GCBuiltinsGreater : public GCBuiltinsCompare {
private:
	//! emit a jbe
	virtual intermediate::OpCode *
	emitBranch(
		intermediate::Operand *left, 
		intermediate::Operand *right, 
		intermediate::Label *out) const;

	virtual universal_integer getDefaultValue(void) const {
		return VHDL_FALSE;
	}
};

//! greater or equal for non-composite types.
class GCBuiltinsGreaterEqual : public GCBuiltinsCompare {
private:
	//! emit a jb
	virtual intermediate::OpCode *
	emitBranch(
		intermediate::Operand *left, 
		intermediate::Operand *right, 
		intermediate::Label *out) const;

	virtual universal_integer getDefaultValue(void) const {
		return VHDL_FALSE;
	}
};

//! unary - (negation) for range constraint/physical types.
class GCBuiltinsUnaryMinus : public GCBuiltinsUnop {
private:
	//! calculate the result of the unary -.
	/** @param cc container to add generated code to.
	 *  @param op operand
	 *  @return Register containing the result.
	 */
	virtual intermediate::Register *
	calcUnOp(
		intermediate::CodeContainer &cc,
		intermediate::Operand *op) const;
};

//! identity (unary plus)
class GCBuiltinsIdentity : public GCBuiltins {
private:
	virtual RegisterSet
	emitCode(GenCode &gc, std::list<AssociationElement*> ops) const;
};

//! unary abs for range constraint/physical types.
class GCBuiltinsAbs : public GCBuiltinsUnop {
private:
	//! calculate absolute value.
	virtual intermediate::Register *
	calcUnOp(
		intermediate::CodeContainer &cc,
		intermediate::Operand *op) const;
};

//! template class for binary mathematical builtins
/** template class for binary mathematical builtins that can use 
 *  a direct intermediate opcode for evaluation.
 */
template <typename T>
class GCBuiltinsBinaryMath : public GCBuiltinsBinOp {
private:
	//calculate left + right
	virtual intermediate::Register *
	calcBinOp(
		intermediate::CodeContainer &cc,
		intermediate::Operand *left,
		intermediate::Operand *right) const {
		
		assert(left->type == right->type);
		intermediate::Register *result = 
			cc.createRegister(left->type);

		T *operation = new T(left, right, result);
		cc.addCode(operation);

		return result;
	};
};

//! binary plus.
class GCBuiltinsPlus : public GCBuiltinsBinaryMath<intermediate::Add> {
};

//! binary minus.
class GCBuiltinsMinus : public GCBuiltinsBinaryMath<intermediate::Sub> {
};

//! binary multiplication.
class GCBuiltinsMult : public GCBuiltinsBinaryMath<intermediate::IMul> {
};

//! binary division.
class GCBuiltinsDiv : public GCBuiltinsBinaryMath<intermediate::Div> {
};




}; /* namespace ast */

#endif /* __GC_BUILTINS_HPP_INCLUDED */
