/* $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.
 */

#include "frontend/visitor/GCBuiltins.hpp"
#include <cassert>
#include "frontend/ast/AssociationElement.hpp"
#include "frontend/misc/BuiltinSymbolTable.hpp"
#include "intermediate/container/LabelFactory.hpp"
#include "intermediate/operands/ImmediateOperand.hpp"
#include "intermediate/opcodes/Mov.hpp"
#include "intermediate/opcodes/Je.hpp"
#include "intermediate/opcodes/Jne.hpp"
#include "intermediate/opcodes/Jb.hpp"
#include "intermediate/opcodes/Jbe.hpp"

namespace ast {

using namespace intermediate;

RegisterSet
GCBuiltinsNoShortCircuit::emitCode(
	GenCode &gc, 
	std::list<AssociationElement*> ops
) const
{
	std::list<RegisterSet> cOps;
	std::list<enum BaseType> btls;

	for (std::list<AssociationElement*>::iterator i = ops.begin(); 
		i != ops.end(); i++) {

		GenCode lgc = GenCode(gc.container);
		assert((*i)->formal == NULL); // FIXME
		assert((*i)->actual != NULL);

		(*i)->actual->accept(lgc);
		cOps.push_back(lgc.sourceRegs);
		btls.push_back((*i)->actual->baseType);
	}

	return this->calculate(*gc.container, cOps, btls);
}

RegisterSet
GCBuiltinsShortCircuit::emitCode(
	GenCode &gc,
	std::list<AssociationElement*> ops
) const
{
	// FIXME this doesn't work for composite types, e.g. bit-vectors.
	assert(ops.size() == 2);

	std::list<AssociationElement*>::iterator i = ops.begin();
	Label *out = LabelFactory::getLabel("builtin_shortcut_result");

	// result = <default>
	Register *result = gc.container->createRegister(OP_TYPE_INTEGER);
	Mov *def = new Mov(new ImmediateOperand(this->getDefault()), result);
	gc.container->addCode(def);

	// leftop = (..)
	Operand *leftOp = this->evaluate(gc.container, **i);
	i++;
	
	// if leftop == <shortCut> goto out
	Je *shortCut = 
		new Je(leftOp, 
			new ImmediateOperand(this->getShortCut()), 
			out);
	gc.container->addCode(shortCut);

	// rightop = (..)
	Operand *rightOp = this->evaluate(gc.container, **i);

	// if rightop == <shortCut> goto out
	Je *shortCut2 = 
		new Je(rightOp, 
			new ImmediateOperand(this->getShortCut()),
			out);
	gc.container->addCode(shortCut2);
	
	// result = 1 - <default>
	Mov *altRes = 
		new Mov(new ImmediateOperand(1 - this->getDefault()), result);
	gc.container->addCode(altRes);
	
	// out:
	gc.container->addCode(out);

	RegisterSet ret = RegisterSet(*gc.container);
	ret.setValue(result);

	return ret;
}

Operand *
GCBuiltinsShortCircuit::evaluate(
	CodeContainer *cc,
	AssociationElement &assoc
)
{
	GenCode gc = GenCode(cc);
	// FIXME composites
	assert(assoc.formal == NULL);
	assert(assoc.actual != NULL);
	assoc.actual->accept(gc);

	Operand *ret = 
		gc.sourceRegs.getValue(assoc.actual->baseType);
	assert(ret != NULL);
	return ret;
}

RegisterSet
GCBuiltinsUnop::calculate(
	CodeContainer &cc,
	std::list<RegisterSet> ops,
	std::list<enum BaseType> btl
) const
{
	assert(ops.size() == 1);
	RegisterSet r = *ops.begin();

	Operand *value = r.getValue(btl.front());

	Register *result = this->calcUnOp(cc, value);
	RegisterSet rset = RegisterSet(cc);
	rset.setValue(result);
	return rset;
}

RegisterSet
GCBuiltinsBinOp::calculate(
	CodeContainer &cc,
	std::list<RegisterSet> ops,
	std::list<enum BaseType> btl
) const
{
	assert(ops.size() == 2);
	std::list<RegisterSet>::const_iterator i = ops.begin();
	std::list<enum BaseType>::const_iterator j = btl.begin();

	RegisterSet left = *i;
	enum BaseType leftBT = *j;

	i++; j++;
	RegisterSet right = *i;
	enum BaseType rightBT = *j;

	Operand *leftOp = left.getValue(leftBT);
	Operand *rightOp = right.getValue(rightBT);

	assert(leftOp != NULL);
	assert(rightOp != NULL);

	Register *result = this->calcBinOp(cc, leftOp, rightOp);
	RegisterSet rset = RegisterSet(cc);
	rset.setValue(result);

	return rset;
}


/* ********************** implementation of builtins ******************* */

Register *
GCBuiltinsNot::calcUnOp(CodeContainer &cc, Operand *op) const
{
	// not for bit, boolean: 1 - op
	// (which is only valid if '1'/true == 1 and '0'/false == 0)
	assert(VHDL_TRUE == 1);
	assert(VHDL_FALSE == 0);

	Register *result = cc.createRegister(OP_TYPE_INTEGER);
	Sub *sub = new Sub(ImmediateOperand::getOne(), op, result);
	cc.addCode(sub);

	return result;
}

Register *
GCBuiltinsXor::calcBinOp(
	CodeContainer &cc, 
	Operand *left, 
	Operand *right
) const
{
	//t = left + right.
	//Xor = t == 2 ? 0 : t;
	Register *lsumr = cc.createRegister(OP_TYPE_INTEGER);
	Add *add = new Add(left, right, lsumr);
	cc.addCode(add);

	ImmediateOperand *two = 
		new ImmediateOperand(static_cast<universal_integer>(2));

	Label *out = LabelFactory::getLabel("xor_out");

	Jb *jb = new Jb(lsumr, two, out);
	cc.addCode(jb);

	Mov *mov = new Mov(ImmediateOperand::getZero(), lsumr);
	cc.addCode(mov);

	cc.addCode(out);

	return lsumr;
}

Register *
GCBuiltinsXnor::calcBinOp(
	CodeContainer &cc, 
	Operand *left, 
	Operand *right
) const
{
	//xnor : not xor -> 1 - xor(left, right)
	Register *ret = GCBuiltinsXor::calcBinOp(cc, left, right);
	Register *res = cc.createRegister(OP_TYPE_INTEGER);

	Sub *sub = new Sub(ImmediateOperand::getOne(), ret, res);
	cc.addCode(sub);

	return res;
}

Register *
GCBuiltinsCompare::calcBinOp(
	CodeContainer &cc,
	Operand *left,
	Operand *right
) const
{
	Register *result = cc.createRegister(OP_TYPE_INTEGER);
	ImmediateOperand *default_op = 
		new ImmediateOperand(this->getDefaultValue());

	Mov *mov = new Mov(default_op, result);
	cc.addCode(mov);

	Label *out = LabelFactory::getLabel("compare_out");
	OpCode *branch = this->emitBranch(left, right, out);
	cc.addCode(branch);

	ImmediateOperand *non_default_op = 
		new ImmediateOperand(1 - this->getDefaultValue());
	Mov *mov2 = new Mov(non_default_op, result);
	cc.addCode(mov2);

	cc.addCode(out);

	return result;
}

OpCode *
GCBuiltinsEqual::emitBranch(Operand *left, Operand *right, Label *out) const
{
	return new Je(left, right, out);
}

OpCode *
GCBuiltinsInEqual::emitBranch(Operand *left, Operand *right, Label *out) const
{
	return new Jne(left, right, out);
}

OpCode *
GCBuiltinsLess::emitBranch(Operand *left, Operand *right, Label *out) const
{
	return new Jb(left, right, out);
}

OpCode *
GCBuiltinsLessEqual::emitBranch(
	Operand *left, 
	Operand *right, 
	Label *out
) const
{
	return new Jbe(left, right, out);
}

OpCode *
GCBuiltinsGreater::emitBranch(
	Operand *left, 
	Operand *right, 
	Label *out
) const
{
	return new Jbe(left, right, out);
}

OpCode *
GCBuiltinsGreaterEqual::emitBranch(
	Operand *left, 
	Operand *right, 
	Label *out
) const
{
	return new Jb(left, right, out);
}

Register *
GCBuiltinsUnaryMinus::calcUnOp(CodeContainer &cc, Operand *op) const
{
	// unary minus: 0 - <value>
	Register *result = cc.createRegister(op->type);
	Sub *sub = new Sub(ImmediateOperand::getZero(), op, result);
	cc.addCode(sub);

	return result;
}

RegisterSet
GCBuiltinsIdentity::emitCode(
	GenCode &gc, 
	std::list<AssociationElement*> ops
) const
{
	assert(ops.size() == 1);
	AssociationElement *elem = ops.front();

	assert(elem->formal == NULL);
	assert(elem->actual != NULL);

	GenCode lgc = GenCode(gc.container);
	elem->actual->accept(lgc);

	// just pass on the RegisterSet of the argument.
	return lgc.sourceRegs;
}

Register *
GCBuiltinsAbs::calcUnOp(CodeContainer &cc, Operand *op) const
{

	//    res = op
	//    if 0 < res goto out
	//    res = 1 - res
	// out:
	//
	Register *result = cc.createRegister(op->type);
	Mov *ir = new Mov(op, result);
	cc.addCode(ir);

	Label *absOut = LabelFactory::getLabel("abs_out");
	Jb *goOut = new Jb(ImmediateOperand::getZero(), op, absOut);
	cc.addCode(goOut);

	Sub *invert = new Sub(ImmediateOperand::getZero(), op, result);
	cc.addCode(invert);

	cc.addCode(absOut);

	return result;
}

}; /* namespace ast */
