/* $Id$ 
 *
 * Generate intermediate code, loop specific parts.
 *
 * 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/GCLoops.hpp"
#include "intermediate/opcodes/Jmp.hpp"
#include "intermediate/opcodes/Jbe.hpp"
#include "intermediate/opcodes/Jb.hpp"
#include "intermediate/opcodes/Mov.hpp"
#include "intermediate/opcodes/Add.hpp"
#include "intermediate/opcodes/Sub.hpp"
#include "intermediate/operands/ImmediateOperand.hpp"
#include "intermediate/operands/RegisterFactory.hpp"
#include "intermediate/container/LabelFactory.hpp"

namespace ast {

using namespace intermediate;

/*
 * ===================== WHILE ITERATE ========================
 */
WhileIterate::WhileIterate(
	CodeContainer &container
) :  		cc(container),
		loopInc(LabelFactory::getLabel("while_iter_inc")),
		loopDone(LabelFactory::getLabel("while_iter_done")),
		loopCheck(LabelFactory::getLabel("while_iter_check"))
{
}

WhileIterate::~WhileIterate()
{
}

void
WhileIterate::addIteration(void)
{
	this->genIterateCode();
}

void
WhileIterate::genIterateCode(void)
{
	this->initializeCounter();

	this->cc.addCode(loopCheck);
	this->checkCondition();

	this->loopBody();

	this->cc.addCode(loopInc);
	this->incCounter();

	Jmp *jmp = new Jmp(this->loopCheck);
	this->cc.addCode(jmp);
	this->cc.addCode(loopDone);
}

/*
 * ===================== FOR LOOP ITERATE =====================
 */
void
ForLoopIterate::initializeCounter(void)
{

	Mov *mov = new Mov(&this->init, &this->cnt);
	this->cc.addCode(mov);
}

void
ForLoopIterate::checkCondition(void)
{
	// FIXME this won't work if rbound is equal to one of the bounds
	//       of the underlying integer type (endless loop).
	//
	// The following construct would be one possible solution:
	//
	// counter = init;
	// if (init > bound) goto out;
	// while (true) {
	// 	loopBody();
	// 	if init == bound goto out;
	// 	loopInc();
	// }
	// out:


	Node *goOut;
	if (this->isAsc) {
		goOut = new Jb(&this->rbound, &this->cnt, this->loopDone);
	} else {
		goOut = new Jb(&this->cnt, &this->rbound, this->loopDone);
	}
	this->cc.addCode(goOut);
}

void
ForLoopIterate::incCounter(void)
{

	Register *tmp = this->cc.createRegister(OP_TYPE_INTEGER);

	Node *inc;
	if (this->isAsc) {
		inc = new Add(&this->cnt, ImmediateOperand::getOne(), tmp);
	} else {
		inc = new Sub(&this->cnt, ImmediateOperand::getOne(), tmp);
	}

	Mov *mov = new Mov(tmp, &this->cnt);

	this->cc.addCode(inc);
	this->cc.addCode(mov);
}

}; /* namespace ast */
