/* $Id$ 
 *
 * Code generator, which transforms the abstract syntax tree into intermediate
 * code.
 *
 * 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/GenCode.hpp"
#include <cassert>
#include <cmath>
#include "frontend/misc/RangeSet.hpp"
#include "frontend/misc/Driver.hpp"
#include "frontend/ast/VarAssignStat.hpp"
#include "frontend/ast/ConstInteger.hpp"
#include "frontend/ast/ConstReal.hpp"
#include "frontend/ast/SimpleName.hpp"
#include "frontend/ast/AttributeName.hpp"
#include "frontend/ast/Subscript.hpp"
#include "frontend/ast/Slice.hpp"
#include "frontend/ast/SelectedName.hpp"
#include "frontend/ast/IfStat.hpp"
#include "frontend/ast/RecordType.hpp"
#include "frontend/ast/RangeConstraintType.hpp"
#include "frontend/ast/UnconstrainedArrayType.hpp"
#include "frontend/ast/PhysicalType.hpp"
#include "frontend/ast/EnumerationType.hpp"
#include "frontend/ast/FunctionCall.hpp"
#include "frontend/ast/Process.hpp"
#include "frontend/ast/SigAssignStat.hpp"
#include "frontend/ast/Aggregate.hpp"
#include "frontend/ast/ReturnStat.hpp"
#include "frontend/ast/SubtypeIndication.hpp"
#include "frontend/ast/ForLoopStat.hpp"
#include "frontend/ast/WhileLoopStat.hpp"
#include "frontend/ast/NextStat.hpp"
#include "frontend/ast/ExitStat.hpp"
#include "frontend/ast/ConstantDeclaration.hpp"
#include "frontend/ast/Architecture.hpp"
#include "frontend/ast/CompInstStat.hpp"
#include "frontend/ast/Package.hpp"
#include "frontend/ast/WaitStat.hpp"
#include "frontend/ast/AssertStat.hpp"
#include "frontend/ast/Entity.hpp"
#include "frontend/ast/ConstArray.hpp"
#include "frontend/ast/ProcCallStat.hpp"
#include "frontend/ast/AttributeSpecification.hpp"
#include "frontend/ast/CaseStat.hpp"
#include "frontend/ast/Others.hpp"
#include "frontend/reporting/ErrorRegistry.hpp"
#include "frontend/reporting/CompileError.hpp"
#include "frontend/visitor/GCArrays.hpp"
#include "frontend/visitor/GCTypes.hpp"
#include "frontend/visitor/GCLoops.hpp"
#include "frontend/visitor/ResolveAggregates.hpp"
#include "frontend/visitor/GCBuiltins.hpp"
#include "frontend/visitor/ResolveTypes.hpp"
#include "frontend/visitor/LookupTypes.hpp"
#include "frontend/visitor/NormalizeAssocLists.hpp"
#include "intermediate/operands/ImmediateOperand.hpp"
#include "intermediate/operands/IndirectOperand.hpp"
#include "intermediate/operands/Reference.hpp"
#include "intermediate/operands/RegisterFactory.hpp"
#include "intermediate/opcodes/Abort.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"
#include "intermediate/opcodes/Jmp.hpp"
#include "intermediate/opcodes/IMul.hpp"
#include "intermediate/opcodes/Add.hpp"
#include "intermediate/opcodes/Sub.hpp"
#include "intermediate/opcodes/Connect.hpp"
#include "intermediate/opcodes/SetParam.hpp"
#include "intermediate/opcodes/GetParam.hpp"
#include "intermediate/opcodes/BeginTransfer.hpp"
#include "intermediate/opcodes/EndTransfer.hpp"
#include "intermediate/opcodes/Call.hpp"
#include "intermediate/opcodes/Proc.hpp"
#include "intermediate/opcodes/Return.hpp"
#include "intermediate/opcodes/Update.hpp"
#include "intermediate/opcodes/GetSig.hpp"
#include "intermediate/opcodes/GetSimTime.hpp"
#include "intermediate/opcodes/ROffset.hpp"
#include "intermediate/opcodes/Suspend.hpp"
#include "intermediate/opcodes/WakeAt.hpp"
#include "intermediate/opcodes/WakeOn.hpp"
#include "intermediate/opcodes/Log.hpp"
#include "intermediate/container/Label.hpp"
#include "intermediate/container/LabelFactory.hpp"
#include "intermediate/container/Data.hpp"
#include "intermediate/container/TypeFactory.hpp"
#include "intermediate/container/Type.hpp"
#include "intermediate/container/TypeElement.hpp"

namespace ast {

/* import some names into current namespace, just to avoid lengthy types */
using namespace intermediate;

GenCode::GenCode() : 	container(
				new intermediate::CodeContainer("top", NULL)),
			assignExpression(false),
			isTarget(false),
			sourceRegs(RegisterSet(*container)),
			destRegs(RegisterSet(*container)),
			dataOp(NULL),
			currentProcess(NULL),
			currentSubprog(NULL),
			assignOperation(ASSIGN_OPERATION_COPY)
{
}

// FIXME eventually separate expression handling from rest.
GenCode::GenCode(CodeContainer *cc) : 	container(cc),
					assignExpression(false),
					isTarget(false),
					sourceRegs(RegisterSet(*cc)),
					destRegs(RegisterSet(*cc)),
					dataOp(NULL),
					currentProcess(NULL),
					currentSubprog(NULL),
					assignOperation(ASSIGN_OPERATION_COPY)
{
}

void
GenCode::visit(Package &node)
{
	if (node.declarations != NULL) {
		this->listTraverse(*node.declarations);
	}

	if (node.body != NULL) {
		node.body->accept(*this);
	}
}

void
GenCode::visit(Architecture &node)
{
	assert(node.entity != NULL);
	assert(this->archComponents.empty());
	CodeContainer *current = this->container;
	this->container = 
		new CodeContainer(node.entity->getICName(), current);

	// handle entity specific parts.
	// FIXME: in case more than one architecture per entity is present,
	//        this won't work! (however that should actually be filtered
	//        out with configurations)
	this->container->dataMode = CodeContainer::DATA_TRANSFER;
	if (node.entity->generics != NULL) {
		this->listTraverse(*node.entity->generics);
	}
	if (node.entity->ports != NULL) {
		this->listTraverse(*node.entity->ports);
	}

	this->container->dataMode = CodeContainer::DATA_STACK;
	if (node.entity->declarations != NULL) {
		this->listTraverse(*node.entity->declarations);
	}

	if (node.declarations != NULL) {
		this->listTraverse(*node.declarations);
	}

	if (node.concurrentStats != NULL) {
		this->listTraverse(*node.concurrentStats);
	}

	this->createInitFunction(node);

	this->container = current;
	this->archComponents.clear();
}

void
GenCode::visit(Entity &node)
{
	// do nothing, handled by Architecture already
}

void
GenCode::createInitFunction(const Architecture &node)
{
	assert(node.entity != NULL);

	AttributeSpecification *foreign = node.hasAttr("foreign");
	if (foreign != NULL) {
		this->foreignInit(node, *foreign);
	}

	this->instantiateComponents(node);
	Return *ret = new Return();
	this->container->addCode(ret);
}

void
GenCode::foreignInit(
	const Architecture &node,
	const AttributeSpecification &foreign
)
{
	assert(node.entity != NULL);

	assert(foreign.init != NULL);
	ConstArray *fVal = dynamic_cast<ConstArray *>(foreign.init);
	assert(fVal != NULL);
	Reference *fRef = new Reference(fVal->unsafeMakeString());

	Reference *entName = new Reference(*node.entity->name);
	BeginTransfer *bg = new BeginTransfer(fRef, entName);
	this->container->addCode(bg);
	bg->annotate("foreign", "architecture");

	/* set generics */
	assert(node.entity->generics != NULL);
	assert(node.entity->ports != NULL);

	/* set generics: 
	 * reuse code for CompInstStat, but generate an 
	 * association list with a name simply referring back
	 * to the declaration.
	 */
	std::list<AssociationElement *> assocs;
	this->makeSelfAssociations(
				assocs, 
				*node.entity->generics, 
				node.entity->region);
	this->setGenericMap(fRef, assocs, true);
	assocs.clear();

	this->makeSelfAssociations(
				assocs, 
				*node.entity->ports,
				node.entity->region);
	this->setPortList(fRef, assocs, true);
	assocs.clear();

	Call *c = new Call(fRef);
	this->container->addCode(c);
	c->annotate("foreign", "architecture");

	EndTransfer *et = new EndTransfer(fRef, ImmediateOperand::getZero());
	this->container->addCode(et);
	et->annotate("foreign", "architecture");
}

template <typename T>
void
GenCode::makeSelfAssociations(
	std::list<AssociationElement*> &dest,
	const T &src,
	const DeclarativeRegion *lookupRegion
)
{
	for (typename T::const_iterator i = src.begin();
		i != src.end(); i++) {
		
		SimpleName *formal = 
			NormalizeAssocLists::makeNameOfVal(*i, 
							lookupRegion,
							Location("GenCode"));
		
		AssociationElement *ele = 
			new AssociationElement(formal, 
					formal,
					Location("builtin"));
		dest.push_back(ele);
	}
}

void
GenCode::instantiateComponents(const Architecture &node)
{
	for (std::list<CompInstStat*>::const_iterator i = 
		this->archComponents.begin();
		i != this->archComponents.end(); i++) {

		this->instantiateComponent(**i);
	}
}

void
GenCode::instantiateComponent(CompInstStat &node)
{	
	assert(node.entity != NULL);
	Reference *ref;

	AttributeSpecification *foreign = node.entity->hasAttr("foreign");

	if (foreign != NULL) {
		assert(foreign->init != NULL);
		ConstArray *fVal = dynamic_cast<ConstArray *>(foreign->init);
		assert(fVal != NULL);
		ref = new Reference(fVal->unsafeMakeString());
	} else {
		ref = new Reference(node.entity->getICName());
	}

	assert(node.name != NULL);
	BeginTransfer *bg = new BeginTransfer(ref, new Reference(*node.name));
	this->container->addCode(bg);

	if (node.genericMap != NULL) {
		this->setGenericMap(ref, *node.genericMap, foreign != NULL);
	}	
	if (node.portMap != NULL) {
		this->setPortList(ref, *node.portMap, foreign != NULL);
	}

	Call *call = new Call(ref);
	this->container->addCode(call);
	EndTransfer *et = new EndTransfer(ref, ImmediateOperand::getZero());
	this->container->addCode(et);

	if (foreign != NULL) {
		bg->annotate("foreign", "entity");
		call->annotate("foreign", "entity");
		et->annotate("foreign", "entity");
	}

}

void
GenCode::setGenericMap(
	Reference *cont,
	std::list<AssociationElement*> &genericMap,
	bool isForeign
)
{
	assert(cont != NULL);
	const char *foreign = NULL;
	if (isForeign) {
		foreign = "generic";
	}

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

		this->sourceRegs = RegisterSet(*this->container);

		switch ((*i)->formal->baseType) {
		case BASE_TYPE_REAL:
		case BASE_TYPE_INTEGER:
		case BASE_TYPE_ENUM:
			this->setArgByValue(cont, NULL, **i, foreign, false);
			break;

		case BASE_TYPE_RECORD:
			/* FIXME */
			assert(0);
			break;

		case BASE_TYPE_ARRAY:
			if ((*i)->hiddenFormal != NULL 
			  && ((*i)->actual != NULL)) {
				// FIXME hacky
				// not an open association, and the actual
				// is not an unconstraint array / has
				// hidden storage space.
				const SymbolDeclaration *sd = 
					(*i)->formal->getDeclaration();
				const ValDeclaration *vd =
					dynamic_cast<const ValDeclaration*>(
									sd);
				assert(vd != NULL);
				this->setArgByBasePointerToTemporary(
								cont,
								*vd,
								**i,
								foreign);
			} else {
				// open association.
				// here the hidden formal is directly
				// used via pointer.
				//
				// or it is an association with an actual which
				// is an unconstraint array, where also the
				// pointer can be used directly.
				this->setArgByBasePointer(cont, 
							NULL, 
							**i, 
							foreign);
			}
			break;

		default:
			assert(0);
		}
	}
}

void
GenCode::setPortList(
	Reference *cont,
	const std::list<AssociationElement*> &portList,
	bool isForeign
)
{
	assert(cont != NULL);
	// this method assumes that the port list has already been 
	// normalized, so that it just contains single port elements.
	// FIXME this won't work with conversion functions.
	this->isTarget = false;

	const char *foreign;
	if (isForeign) {
		foreign = "port";
	} else {
		foreign = NULL;
	}

	for (std::list<AssociationElement*>::const_reverse_iterator i = 
		portList.rbegin(); i != portList.rend(); i++) {
		
		this->sourceRegs = RegisterSet(*this->container);

		assert((*i)->formal != NULL);
		switch ((*i)->formal->baseType) {
		case BASE_TYPE_REAL:
		case BASE_TYPE_INTEGER:
		case BASE_TYPE_ENUM:
			this->setArgByPointer(cont, NULL, **i, foreign);
			break;

		case BASE_TYPE_ARRAY:
		case BASE_TYPE_RECORD:
			this->setArgByBasePointer(cont, NULL, **i, foreign);
			break;

		default:
			// must not happen
			assert(0);
		}
	}
}

void
GenCode::createInitFunction(const Process &node)
{
	for (std::list<Driver*>::const_iterator i = node.drivers.begin();
		i != node.drivers.end(); i++) {

		this->sourceRegs = RegisterSet(*this->container);
		// even though the signal and the driver are known,
		// the way how to access these is not (pointer, or
		// base-pointer?). (ok, driver of a Process should
		// always be directly via the reference, as it
		// cannot be a parameter!)
		// reuse the SimpleName stored in the driver to
		// find out.

		// first pick up destination driver (the 
		// SimpleName is used to obtain the signal
		// *and* the driver depending on isTarget)
		this->assignOperation = ASSIGN_OPERATION_CONNECT;
		this->isTarget = true;
		this->assignExpression = false;
		assert((*i)->n != NULL);
		(*i)->n->accept(*this);

		// then pick up the source signal.
		this->destRegs = this->sourceRegs;
		this->sourceRegs = RegisterSet(*this->container);
		this->isTarget = false;
		this->assignExpression = true;
		(*i)->n->accept(*this);
	}

	this->assignExpression = false;
	this->assignOperation = ASSIGN_OPERATION_COPY;
	this->sourceRegs = RegisterSet(*this->container);
	this->destRegs = RegisterSet(*this->container);
	
	Reference *ref = new Reference(node.getICName());
	BeginTransfer *bg = new BeginTransfer(ref, new Reference(""));
	this->container->addCode(bg);

	Proc *proc = new Proc(ref);
	this->container->addCode(proc);

	EndTransfer *et = new EndTransfer(ref, ImmediateOperand::getZero());
	this->container->addCode(et);
}

void
GenCode::visit(CompInstStat &node)
{
	assert(node.entity != NULL);
	assert(node.name != NULL);

	this->archComponents.push_back(&node);
	// the real instantiation is done via the init-function so no need for
	// anything else here.
}

void
GenCode::visit(VarAssignStat &node)
{
	this->sourceRegs = RegisterSet(*this->container);
	this->destRegs = RegisterSet(*this->container);

	// traverse to target, storing result in reg
	this->assignExpression = false;
	this->isTarget = true;
	node.target->accept(*this);

	this->destRegs = this->sourceRegs;

	// traverse to source, storing result in reg
	this->assignExpression = true;
	this->isTarget = false;
	node.source->accept(*this);

	// FIXME aggregate assignments (i.e. with an aggregate as target)
	//       are currently not supported.
}

void
GenCode::visit(SigAssignStat &node)
{
	this->sourceRegs = RegisterSet(*this->container);
	this->destRegs = RegisterSet(*this->container);

	assert(node.target);
	assert(node.waveForm);

	this->isTarget = true;
	this->assignExpression = false;
	node.target->accept(*this);
	this->destRegs = this->sourceRegs;

	this->isTarget = false;

	//traverse to waveform elements
	for (std::list<WaveFormElem*>::const_iterator i = 
		node.waveForm->begin(); i != node.waveForm->end(); i++) {
	
		this->sourceRegs = RegisterSet(*this->container);
		if ((*i)->delay != NULL) {
			(*i)->accept(*this);
			this->dataOp = 
				this->sourceRegs.getValue(
						(*i)->delay->baseType);
			this->sourceRegs = RegisterSet(*this->container);
		} else {
			this->dataOp = ImmediateOperand::getZero();
		}
		this->assignExpression = true;
		assert((*i)->value != NULL);
		(*i)->value->accept(*this);	
	}

	this->dataOp = NULL;
}

void
GenCode::visit(SimpleName &node)
{
	assert(node.candidates.size() == 1);
	Symbol *sym = node.candidates.front();

	bool refPointer = false;

	// sanity check only
	switch(sym->type) {
	case SYMBOL_VARIABLE:
		break;
	case SYMBOL_SIGNAL:
		break;

	case SYMBOL_PARAMETER:
	switch (node.baseType) {
		case BASE_TYPE_ARRAY:
		case BASE_TYPE_RECORD:
			refPointer = true;
			break;

		default:
			break;
		}
		break;

	case SYMBOL_PORT:
		switch (node.baseType) {
		case BASE_TYPE_ARRAY:
		case BASE_TYPE_RECORD:
			if (! this->isTarget) {
				// the ports means that the 
				// driver is *always* locally stored.
				refPointer = true;
			}
			break;

		default:
			break;
		}
		break;

	default:
		std::cerr << "At " << node.location
			<< ": unexpected symbol found: " << *sym << std::endl;
		assert(false);
	}

	ProcessStackedExpression pse = ProcessStackedExpression(*this, node);

	this->sourceRegs = RegisterSet(*this->container);

	Reference *ref;
	if (this->isTarget && node.isSignal()) {
		assert(node.driver != NULL);
		ref = new Reference(node.driver->getICName());
	} else {
		ref = new Reference(node.getName());
	}

	Operand *op = ref;

	if (refPointer) {
		Register *ptr = 
			this->container->createRegister(OP_TYPE_POINTER);
		Mov *m = new Mov(ref, ptr);
		this->container->addCode(m);
		op = new IndirectOperand(ptr, OP_TYPE_POINTER);
	} 

	// check for unconstraint arrays.
	const ValDeclaration *decl = 
		dynamic_cast<const ValDeclaration*>(
					node.getDeclaration());
	assert(decl != NULL);
	if (decl->isUnconstraint()) {
		this->getConstraints(*decl);
	}

	this->sourceRegs.setPointer(op);
	this->sourceRegs.isSignal = node.isSignal();
}

void
GenCode::visit(ConstInteger &node)
{
	ProcessStackedExpression pse = ProcessStackedExpression(*this, node);
	this->processConst(node);
}

void
GenCode::visit(ConstReal &node)
{
	ProcessStackedExpression pse = ProcessStackedExpression(*this, node);
	this->processConst(node);
}

void
GenCode::visit(IfStat &node)
{
	this->assignExpression = false;
	// generate code for condition
	node.condition->accept(*this);

	ImmediateOperand *trueOp = new ImmediateOperand(
				static_cast<universal_integer>(VHDL_TRUE));

	Label *thenLabel = LabelFactory::getLabel("then");

	Operand *condOp = 
		this->sourceRegs.getValue(node.condition->baseType);

	Je *condJmp = new Je(condOp, trueOp, thenLabel);
	this->container->addCode(condJmp);

	Label *endIf = LabelFactory::getLabel("endif");
	Label *elseLbl = NULL;
	if (node.elseStats != NULL) {
		elseLbl = LabelFactory::getLabel("else");
		Jmp *ej = new Jmp(elseLbl);
		this->container->addCode(ej);
	} else {
		Jmp *out = new Jmp(endIf);
		this->container->addCode(out);
	}

	assert(node.thenStats != NULL);
	this->container->addCode(thenLabel);
	this->listTraverse(*node.thenStats);
	
	if (node.elseStats != NULL) {
		Jmp *out = new Jmp(endIf);
		this->container->addCode(out);
		this->container->addCode(elseLbl);
		this->listTraverse(*node.elseStats);
	}

	this->container->addCode(endIf);
}

void
GenCode::visit(Process &node)
{
	CodeContainer *current = this->container;
	this->container = new CodeContainer(node.getICName(), current);

	if (node.declarations != NULL) {
		this->listTraverse(*node.declarations);
	}

	this->currentProcess = &node;

	std::string runS = node.getICName() + "_run";
	Label *runL = LabelFactory::getFixedLabel(runS);

	// FIXME how to handle implicit variables?

	this->container->addCode(runL);
	if (node.seqStats != NULL) {
		this->listTraverse(*node.seqStats);
	}

	Jmp *loop = new Jmp(runL);
	this->container->addCode(loop);

	this->currentProcess = NULL;

	this->container = current;
	// register drivers in *architecture*
	this->registerDrivers(node);
	// also add init function code to *architecture*
	this->createInitFunction(node);
}

void
GenCode::registerDrivers(const Process &node)
{
	for (std::list<Driver*>::const_iterator i = node.drivers.begin(); 
		i != node.drivers.end(); i++) {

		this->registerDriver(**i);
	}
}

void
GenCode::registerDriver(Driver &drv)
{
	std::string name = drv.getICName();
	GCTypes::GenTypeElements gte = 
		GCTypes::GenTypeElements(true, "FIXME", drv.signal, NULL);

	assert(drv.signal.subtypeIndic != NULL);
	drv.signal.subtypeIndic->accept(gte);
	assert(gte.composite.size() == 1);
	TypeElement *te = gte.composite.front();

	Data *d = new Data(name, STORAGE_TYPE_DRIVER, te, NULL);
	this->container->addData(d);
}

void
GenCode::visit(ForLoopStat &node)
{
	class LoopImpl : public ForLoopIterate {
	public:
		LoopImpl(
			intermediate::CodeContainer &container,
			intermediate::Operand &counter,
			intermediate::Operand &initializer,
			intermediate::Operand &rightBound,
			bool isAscending,
			GenCode &genCode,
			ForLoopStat &nd
			) : 	ForLoopIterate(
					container,
					counter,
					initializer,
					rightBound,
					isAscending),
				gc(genCode),
				loopNode(nd) {}
	private:
		// cppcheck-suppress unusedPrivateFunction (false positive)
		virtual void loopBody(void) {
			gc.listTraverse(*this->loopNode.loopStats);
		}


		GenCode &gc;
		ForLoopStat &loopNode;
	};

	assert(node.loopStats != NULL);
	assert(node.range != NULL);
	assert(node.loopVariable != NULL);
	assert(node.loopVariable->subtypeIndic != NULL);
	assert(node.loopVariable->subtypeIndic->baseType 
	       == BASE_TYPE_INTEGER);

	// add declaration for loop variable
	node.loopVariable->accept(*this);

	bool asc = node.range->direction == DiscreteRange::DIRECTION_UP;
	this->assignExpression = false;
	
	this->sourceRegs = RegisterSet(*this->container);
	node.range->accept(*this);
	assert(this->sourceRegs.sliceBegin != NULL);
	assert(this->sourceRegs.sliceEnd != NULL);

	Reference *ref = new Reference(node.loopVariable->getICName());
	Register *loopVarP = this->container->createRegister(OP_TYPE_POINTER);
	Mov *mov = new Mov(ref, loopVarP);
	this->container->addCode(mov);

	Operand *cnt = new IndirectOperand(loopVarP, OP_TYPE_INTEGER);

	Operand *left = this->sourceRegs.sliceBegin;
	Operand *right = this->sourceRegs.sliceEnd;
	this->sourceRegs = RegisterSet(*this->container);
	
	LoopImpl li = LoopImpl(
			*this->container,
			*cnt,
			*left,
			*right,
			asc,
			*this,
			node);
	this->loopRegistry.rememberLoop(&node, &li);

	li.addIteration();

	this->loopRegistry.forgetLoop(&node);
}

void
GenCode::visit(WhileLoopStat &node)
{
	class WLImpl : public WhileIterate {
	public:
		WLImpl(
			CodeContainer &container,
			GenCode &genCode,
			WhileLoopStat &n
			) : 	WhileIterate(container),
				gc(genCode),
				nd(n) {}

	private:
		// cppcheck-suppress unusedPrivateFunction (false positive)
		virtual void initializeCounter(void) {
			// do nothing.
		}

		// cppcheck-suppress unusedPrivateFunction (false positive)
		virtual void checkCondition(void) {
			this->gc.sourceRegs = 
				RegisterSet(*this->gc.container);
			this->gc.destRegs = 
				RegisterSet(*this->gc.container);
			this->gc.assignExpression = false;
			assert(this->nd.condition != NULL);

			this->nd.condition->accept(this->gc);
			Operand *condOp = this->gc.sourceRegs.getValue(
						this->nd.condition->baseType);

			ImmediateOperand *trueOp = 
				new ImmediateOperand(
					static_cast<universal_integer>(
								VHDL_TRUE));
			Jne *jne = new Jne(
					trueOp, 
					condOp,
					this->loopDone);
			this->cc.addCode(jne);
			this->gc.sourceRegs = RegisterSet(*this->gc.container);
		}

		// cppcheck-suppress unusedPrivateFunction (false positive)
		virtual void incCounter(void) {
			// nothing to do
		}

		// cppcheck-suppress unusedPrivateFunction (false positive)
		virtual void loopBody(void) {
			if (this->nd.loopStats == NULL) {
				return;
			}

			this->gc.listTraverse(*nd.loopStats);
		}

		GenCode &gc;
		WhileLoopStat &nd;
	};

	WLImpl loop = WLImpl(*this->container, *this, node);

	this->loopRegistry.rememberLoop(&node, &loop);
	loop.addIteration();
	this->loopRegistry.forgetLoop(&node);
}

void
GenCode::visit(NextStat &node)
{
	assert(node.referredLoop != NULL);
	WhileIterate *wl = this->loopRegistry.lookup(node.referredLoop);
	assert(wl->loopInc != NULL);

	this->processCFLoopStat(node.condition, wl->loopInc);
}

void
GenCode::visit(ExitStat &node)
{
	assert(node.referredLoop != NULL);
	WhileIterate *wl = this->loopRegistry.lookup(node.referredLoop);
	assert(wl->loopDone != NULL);

	this->processCFLoopStat(node.condition, wl->loopDone);
}

void
GenCode::visit(WaitStat &node)
{
	this->sourceRegs = RegisterSet(*this->container);
	this->assignExpression = false;
	this->isTarget = false;

	Label *loopLbl = NULL;
	Label *exitWait = NULL;
	Register *resumeAt = NULL;

	// semantics of a wait:
	// time := now() + timeout
	// loop
	//      trigger(self, time)
	//      trigger_sig(self, signal_list)
	//      suspend()
	//      time1 := now()
	//      exit if time1 >= time -- timeout
	//      if !condition goto loop -- condition met
	// end loop


	// time := now() + timeout
	if (node.timeout != NULL) {
		node.timeout->accept(*this);
		Operand *timeout = 
			this->sourceRegs.getValue(node.timeout->baseType);

		assert(timeout != NULL);
		// now := now();
		Register *now = 
			this->container->createRegister(OP_TYPE_INTEGER);
		GetSimTime *gst = new GetSimTime(now);
		this->container->addCode(gst);
		resumeAt = this->container->createRegister(OP_TYPE_INTEGER);
		Add *cra = new Add(now, timeout, resumeAt);
		this->container->addCode(cra);
	}

	// loop ...
	if (node.condition != NULL) {
		loopLbl = LabelFactory::getLabel("wait_until_loop");
		this->container->addCode(loopLbl);
		exitWait = LabelFactory::getLabel("wait_done");
	}

	// trigger(self, time)
	if (node.timeout != NULL) {
		WakeAt *wa = new WakeAt(resumeAt);
		this->container->addCode(wa);
	}

	// trigger_sig(self, signal_list)
	// sensitivities must have been set by WaitConditions visitor.
	assert(node.sensitivities != NULL);
	for (std::list<Name*>::iterator i = node.sensitivities->begin();
		i != node.sensitivities->end(); i++) {

		this->sourceRegs = RegisterSet(*this->container);
		this->assignOperation = ASSIGN_OPERATION_WAKEON;
		this->assignExpression = true;

		(*i)->accept(*this);
	}
	this->assignExpression = false;
	this->assignOperation = ASSIGN_OPERATION_COPY;

	//suspend
	Suspend *suspend = new Suspend();
	this->container->addCode(suspend);

	// exit if now() >= time -- timeout
	if ((node.timeout != NULL) && (node.condition != NULL)) {
		Register *now = 
			this->container->createRegister(OP_TYPE_INTEGER);
		GetSimTime *gst = new GetSimTime(now);
		this->container->addCode(gst);
		Jbe *exitTimout = new Jbe(resumeAt, now, exitWait);
		this->container->addCode(exitTimout);
	}

	// if !condition goto loop
	if (node.condition != NULL) {
		this->sourceRegs = RegisterSet(*this->container);
		node.condition->accept(*this);
		Operand *cond = this->sourceRegs.getValue(BASE_TYPE_INTEGER);
		Jne *goLoop = 
			new Jne(cond, ImmediateOperand::getOne(), loopLbl);
		this->container->addCode(goLoop);
		this->sourceRegs = RegisterSet(*this->container);

		this->container->addCode(exitWait);
	}
}

void
GenCode::visit(AssertStat &node)
{
	assert(node.condition != NULL);

	this->assignExpression = false;
	this->sourceRegs = RegisterSet(*this->container);
	this->destRegs = RegisterSet(*this->container);

	node.condition->accept(*this);

	//evaluate condition 
	Label *assertSuccL = LabelFactory::getLabel("assert_succ");
	Operand *cond = 
		this->sourceRegs.getValue(node.condition->baseType);
	Je *je = new Je(cond, 
			new ImmediateOperand(
				static_cast<universal_integer>(VHDL_TRUE)),
			assertSuccL);
	this->container->addCode(je);

	// severity present? 
	if (node.severity != NULL) {
		this->sourceRegs = RegisterSet(*this->container);
		node.severity->accept(*this);
		this->dataOp = this->sourceRegs.getValue(BASE_TYPE_INTEGER);
	} else {
		// no severity -> error (lrm 8.2)
		this->dataOp = new ImmediateOperand(
					static_cast<universal_integer>(2));
	}
	Operand *severityOp = this->dataOp;

	// log VHDL position
	std::string loc = util::MiscUtil::toString(node.location);
	loc += ": ";
	for (std::string::const_iterator i = loc.begin(); i != loc.end(); 
		i++) {

		ImmediateOperand *charVal = 
			new ImmediateOperand(
				static_cast<universal_integer>(*i));

		Log *l = new Log(this->dataOp, charVal);

		this->dataOp = 
			new ImmediateOperand(
				static_cast<universal_integer>(-1));
		this->container->addCode(l);
	}

	if (node.report != NULL) {
		this->assignExpression = true;
		this->assignOperation = ASSIGN_OPERATION_LOG;
		node.report->accept(*this);
		this->assignOperation = ASSIGN_OPERATION_COPY;
		this->assignExpression = false;

		// add a newline
		universal_integer newline = '\n';
		universal_integer mo = -1;
		Log *l = new Log(new ImmediateOperand(mo),
				new ImmediateOperand(newline));
		this->container->addCode(l);

		// abort if severity is FAILURE
		universal_integer sevError = 3;
		Jb *jb = new Jb(severityOp, new ImmediateOperand(sevError), 
				assertSuccL);
		Abort *a = new Abort();
		this->container->addCode(jb);
		this->container->addCode(a);

	} else {
		// TODO Log "Assertion violation." 
	}

	this->container->addCode(assertSuccL);
	this->dataOp = NULL;
}

void
GenCode::visit(ProcCallStat &node)
{
	this->sourceRegs = RegisterSet(*this->container);
	this->destRegs = RegisterSet(*this->container);

	assert(node.definition != NULL);
	AttributeSpecification *foreign = node.definition->hasAttr("foreign");
	const char *fattr = NULL;
	if (foreign != NULL) {
		fattr = "procedure";
	}

	this->callSubprog(node, fattr);
}

void
GenCode::visit(ConstArray &node)
{
	assert(this->assignExpression);

	switch (this->assignOperation) {
	case ASSIGN_OPERATION_COPY:
		if (this->destRegs.isSignal) {
			this->processArrayUpdate(node);
		} else {
			this->processArrayCopy(node);
		}
		break;

	case ASSIGN_OPERATION_LOG:
		// only the first character should get logged with
		// level in dataOp, the rest with -1 (so that
		// level will only be printed for the first
		// character)
		for (std::vector<ConstInteger *>::iterator i = 
			node.elements->begin();
			i != node.elements->end(); i++) {

			(*i)->accept(*this);
			if (i == node.elements->begin()) {
				universal_integer mo = -1;
				this->dataOp = new ImmediateOperand(mo);
			}
		}
		break;

	case ASSIGN_OPERATION_CONNECT:
		// mustn't happen
		assert(false);
		break;
	
	case ASSIGN_OPERATION_WAKEON:
		for (std::vector<ConstInteger *>::iterator i = 
			node.elements->begin();
			i != node.elements->end(); i++) {
				(*i)->accept(*this);
		}

		break;
	};
}

void
GenCode::processArrayCopy(ConstArray &node)
{
	class ConstArrayAssign : public StaticArrayIterate {
	public:
		ConstArrayAssign(
			TypeDeclaration *at,
			Operand *b,
			CodeContainer &container,
			ConstArray &nod
			) :	StaticArrayIterate(at, b, container),
				ca(nod) {}
	private:
		ConstArray &ca;

		// cppcheck-suppress unusedPrivateFunction (false positive)
		virtual void iterateBody(
			Register *element,
			std::list<universal_integer> indics) {

			universal_integer i = indics.front();
			i = ArrayHandling::transform_idx(this->arrayType, i);
			unsigned int ri = static_cast<unsigned int>(i);
			assert(static_cast<universal_integer>(ri) == i);

			assert(ri < this->ca.elements->size());
			ConstInteger *ci = (*this->ca.elements)[ri];
			universal_integer value = ci->value;
			IndirectOperand *valOp = 
				new IndirectOperand(element, OP_TYPE_INTEGER);
			Mov *m = new Mov(new ImmediateOperand(value), valOp);
			this->cc.addCode(m);
		}

	};

	ConstArrayAssign caa = 
		ConstArrayAssign(node.type, 
				this->destRegs.getPointer(),
				*this->container,
				node);
	caa.iterate();
}

void
GenCode::processArrayUpdate(ConstArray &node)
{
	class ConstArrayAssign : public StaticArrayIterate {
	public:
		ConstArrayAssign(
			TypeDeclaration *at,
			Operand *b,
			CodeContainer &container,
			ConstArray &nod,
			Operand *delay
			) :	StaticArrayIterate(at, b, container),
				ca(nod),
				delayOp(delay) {}
	private:
		ConstArray &ca;
		Operand *delayOp;

		// cppcheck-suppress unusedPrivateFunction (false positive)
		virtual void iterateBody(
			Register *element,
			std::list<universal_integer> indics) {

			universal_integer i = indics.front();

			i = ArrayHandling::transform_idx(this->arrayType, i);

			unsigned int ri = static_cast<unsigned int>(i);
			assert(static_cast<universal_integer>(ri) == i);

			assert(ri < this->ca.elements->size());
			ConstInteger *ci = (*this->ca.elements)[ri];
			universal_integer value = ci->value;

			IndirectOperand *drvPtr = 
				new IndirectOperand(element, OP_TYPE_POINTER);

			Update *up = new Update(new ImmediateOperand(value), 
						this->delayOp,
						drvPtr);
			this->cc.addCode(up);
		}

	};

	ConstArrayAssign caa = 
		ConstArrayAssign(node.type, 
				this->destRegs.getPointer(),
				*this->container,
				node,
				this->dataOp);
	caa.iterate();
}

void
GenCode::processCFLoopStat(Expression *optCond, Label *target)
{
	assert(target != NULL);

	if (optCond == NULL) {
		Jmp *jmp = new Jmp(target);
		this->container->addCode(jmp);
		return;
	}

	// optCond != NULL
	this->sourceRegs = RegisterSet(*this->container);
	this->assignExpression = false;
	optCond->accept(*this);

	Operand *cond = this->sourceRegs.getValue(optCond->baseType);

	ImmediateOperand *trueOp = 
		new ImmediateOperand(
			static_cast<universal_integer>(VHDL_TRUE));

	Je *jcond = new Je(cond, trueOp, target);
	this->container->addCode(jcond);
	this->sourceRegs = RegisterSet(*this->container);
}

template <typename T>
void
GenCode::processConst(T &node)
{
	this->sourceRegs = RegisterSet(*this->container);
	this->sourceRegs.setValue(new ImmediateOperand(node.value));
}

void
GenCode::visit(Subscript &node)
{
	ProcessStackedExpression pse = ProcessStackedExpression(*this, node);

	assert(node.indices != NULL);
	assert(node.source != NULL);
	node.source->accept(*this);
	assert(! node.indices->empty());

	this->processSubscription(node, *node.indices);
}

void
GenCode::processSubscription(Subscript &node, std::list<Expression*> &indices)
{
	std::list<Operand*> idcs = std::list<Operand*>();

	for (std::list<Expression*>::iterator i = indices.begin(); 
		i != indices.end(); i++) {
		Operand *value = this->getSubscriptIndex(*i);
		idcs.push_back(value);
	}

	this->sourceRegs.subscribe(node.source->type, idcs);
}

intermediate::Operand *
GenCode::getSubscriptIndex(Expression *index)
{
	bool backupIsTarget = this->isTarget;
	RegisterSet backupRegs = this->sourceRegs;
	this->sourceRegs = RegisterSet(*this->container);

	this->isTarget = false;
	index->accept(*this);
	this->isTarget = backupIsTarget;
	RegisterSet ret = this->sourceRegs;
	this->sourceRegs = backupRegs;

	return ret.getValue(index->baseType);
}

void
GenCode::visit(Slice &node)
{
	ProcessStackedExpression pse = ProcessStackedExpression(*this, node);

	assert(node.source != NULL);
	// traverse to source
	node.source->accept(*this);

	// backup transfer registers
	RegisterSet backupRegs = this->sourceRegs;
	this->sourceRegs = RegisterSet(*this->container);

	bool backupIsTarget = this->isTarget;
	this->isTarget = false;
	// traverse to range
	assert(node.range != NULL);
	node.range->accept(*this);

	// FIXME did the slice narrow/boundary check.
	backupRegs.sliceBegin = this->sourceRegs.sliceBegin;
	backupRegs.sliceEnd = this->sourceRegs.sliceEnd;
	this->sourceRegs = backupRegs;
	this->isTarget = backupIsTarget;
}

void
GenCode::visit(DiscreteRange &node)
{
	if (node.from != NULL) {
		assert(node.to != NULL);
		this->processDRByBounds(node);
		return;
	}

	if (node.rangeName != NULL) {
		node.rangeName->accept(*this);
		return;
	}

	assert(false);
}

void
GenCode::processDRByBounds(DiscreteRange &node)
{
	assert(node.from != NULL);
	assert(node.to != NULL);

	node.from->accept(*this);
	Operand *sliceFrom = 
		this->sourceRegs.getValue(node.from->baseType);
	this->sourceRegs = RegisterSet(*this->container);

	node.to->accept(*this);
	Operand *sliceTo = 
		this->sourceRegs.getValue(node.to->baseType);
	this->sourceRegs = RegisterSet(*this->container);

	switch (node.direction) {
	case DiscreteRange::DIRECTION_UP:
		this->sourceRegs.sliceBegin = sliceFrom;
		this->sourceRegs.sliceEnd = sliceTo;
		break;

	case DiscreteRange::DIRECTION_DOWN:
		this->sourceRegs.sliceBegin = sliceTo;
		this->sourceRegs.sliceEnd = sliceFrom;
		break;

	default:
		// must not happen
		assert(false);
	}
}

void
GenCode::visit(AttributeName &node)
{
	if ((*node.name) == std::string("range")) {
		this->processRangeAttr(node);
		return;
	}

	if ((*node.name) == std::string("event")) {
		// FIXME needs a new opcode to evaluate 'event
		// (maybe combine with 'active etc.?)
		//
		// currently just say "yes" for 'event. lalala
		this->sourceRegs = RegisterSet(*this->container);
		this->sourceRegs.setValue(ImmediateOperand::getOne());
		return;
	}

	// TODO
	assert(0);
}

void
GenCode::processRangeAttr(AttributeName &node)
{
	// FIXME maybe the static part should go to constant propagation?
	std::list<DiscreteRange *> staticResolution;

	ResolveTypes::pickupIndexConstraint(node.type, staticResolution);
	if (! staticResolution.empty()) {
		DiscreteRange *dr = staticResolution.front();
		dr->accept(*this);
		return;
	}

	// need to resolve at run time (unconstraint arrays)
	assert(this->assignExpression == false);
	assert(! this->isTarget);

	node.prefix->accept(*this);
	
	std::list<Operand *> lbs;
	std::list<Operand *> ubs;
	std::list<Operand *> dirs;
	this->sourceRegs.getConstraints(lbs, ubs, dirs);
	assert(! lbs.empty());
	assert(! ubs.empty());
	assert(! dirs.empty());

	// TODO direction
	this->sourceRegs.sliceBegin = lbs.front();
	this->sourceRegs.sliceEnd = ubs.front();

	lbs.clear();
	ubs.clear();
	dirs.clear();
	// clear unconstraint state
	this->sourceRegs.setUnconstraintBounds(lbs, ubs, dirs);
}

void
GenCode::visit(SelectedName &node)
{
	ProcessStackedExpression pse = ProcessStackedExpression(*this, node);

	assert(node.prefix);
	//traverse to prefix
	node.prefix->accept(*this);

	assert(this->sourceRegs.sliceBegin == NULL);
	assert(this->sourceRegs.sliceEnd == NULL);
	assert(node.candidates.size() == 1);
	Symbol *sym = node.candidates.front();

	// symbol must be a RecordTypeElement
	RecordTypeElement *elem = 
		dynamic_cast<RecordTypeElement*>(&sym->declaration);
	assert(elem != NULL);

	ImmediateOperand *off = new ImmediateOperand(elem->offset);
	Type *rt = TypeFactory::lookupType(elem->parent->getICName());
	assert(rt != NULL);

	Register *r = this->container->createRegister(OP_TYPE_POINTER);
	ROffset *selection = 
		new ROffset(this->sourceRegs.getPointer(),
					off,
					rt,
					r);

	this->sourceRegs.setPointer(r);
	this->container->addCode(selection);
}

void
GenCode::visit(FunctionCall &node)
{
	ProcessStackedExpression pse = ProcessStackedExpression(*this, node);
	assert(! this->isTarget);

	if (node.definition->isBuiltin) {
		if (node.definition->gcBuiltin == NULL) {
			std::cerr << "WARNING: builtin, but then again not a "
				<< "builtin: "
				<< *node.definition->name
				<< std::endl;
		}
		assert(node.definition->gcBuiltin != NULL);
		assert(node.arguments != NULL);
		this->sourceRegs = 
			node.definition->gcBuiltin->emitCode(
							*this, 
							*node.arguments);
		return;
	}

	// not a builtin.
	AttributeSpecification *foreign = node.definition->hasAttr("foreign");
	const char *fattr = NULL;
	if (foreign != NULL) {
		fattr = "function";
	}

	Register *ret = this->callSubprog(node, fattr);
	enum BaseType bt = node.definition->returnType->baseType;

	switch(bt) {
	case BASE_TYPE_INTEGER: 
	case BASE_TYPE_REAL:
	case BASE_TYPE_ENUM:
		this->sourceRegs.setValue(ret);
		break;

	case BASE_TYPE_RECORD:
	case BASE_TYPE_ARRAY:
		this->handleCompositeReturn(node);
		break;

	default:
		assert(false);
	}
}

void
GenCode::visit(Aggregate &node)
{
	switch(node.baseType) {
	case BASE_TYPE_ARRAY:
		this->processArrayAggregate(node);
		break;

	case BASE_TYPE_RECORD:
		this->processRecordAggregate(node);
		break;

	default:
		std::cerr << "strange Aggregate at " << node.location
			<< ": " << node << std::endl;
		// not valid for an Aggregate
		assert(false);
	}
}

void
GenCode::processArrayAggregate(Aggregate &node)
{
	// hints from lrm, p.109:
	// if a choice is present, it must be locally static
	// the only exception is, if there is
	// - just one associaton *and*
	// - this one has only one choice

	assert(this->assignExpression);
	// FIXME
	assert(node.associations != NULL);

	// determine index constraint.
	std::list<DiscreteRange*> idcs = std::list<DiscreteRange*>();
	// TODO? return value is the base type.
	ResolveTypes::pickupIndexConstraint(node.type, idcs);

	assert(idcs.size() > 0);
	DiscreteRange *neededIndex = idcs.front();

	// resolve arrray ranges
	ResolveAggregates ra = 
		ResolveAggregates(*neededIndex);
	node.accept(ra);

	assert(node.associations != NULL);
	for (std::list<ElementAssociation*>::iterator i = 
		node.associations->begin(); 
		i != node.associations->end(); i++) {

		this->processArrayAssoc(**i, node.type);
	}
}

void
GenCode::processArrayAssoc(ElementAssociation &node, TypeDeclaration *aType)
{
	if (node.range == NULL) {
		// error occured beforehand, reported already.
		return;
	}

	// lrm 7.3.2: reinterpeted:
	// a <= (1 to 3 | 5 => '1', others => '0')
	// means that each array element of a, that falls in the choices
	// 1 to 3 | 5 gets '1' assigned, and each element that falls in 
	// the choices others gets '0' assigned.
	//
	// In particular, this means to do a subscript for each element of a,
	// and put the result of the actual in there.
	//
	// One possible approach is to iterate over each element of the ranges
	// and assign the first result to the first element of a and reuse 
	// that space as a cache for the remaining elements of a choice.

	// empty Range? -> do nothing. (might happen in case of "Others" 
	// referring to an empty Range).
	RangeSet rs = *node.range;
	if (rs.empty()) {
		return;
	}

	// subscribe to first element of choices, and let the actual do the
	// assignment in the first place.
	RegisterSet dRegs = this->destRegs;
	// FIXME think about slices?
	this->destRegs = RegisterSet(this->destRegs);

	assert(node.actual != NULL);
	std::list<Operand *> indices = std::list<Operand *>();
	universal_integer i = rs.getLowerBound();
	ImmediateOperand *idx = new ImmediateOperand(i);
	indices.push_back(idx);

	this->destRegs.subscribe(aType, indices);

	// determine value of actual (and also assign it to the first 
	// destination offset)
	node.actual->accept(*this);
	bool ret = rs.minus(i, i);
	assert(ret);
	// prepare to handle remaining elements. use the evaluated element
	// that was just assigned as source.
	// FIXME this only works for variables, not for signals!
	this->sourceRegs = this->destRegs;
	assert(! this->sourceRegs.isSignal);

	while (! rs.empty()) {
		// determine new destination index for the next element
		indices.clear();
		i = rs.getLowerBound();
		indices.push_back(new ImmediateOperand(i));

		this->destRegs = RegisterSet(dRegs);
		this->destRegs.subscribe(aType, indices);
		// assign it
		this->doAssignment(*node.actual->type);

		// remove the next index from the set.
		ret = rs.minus(i, i);
		assert(ret);
	}

	this->destRegs = dRegs;
}

void
GenCode::processRecordAggregate(Aggregate &node)
{
	// TODO
	assert(false);
}

void
GenCode::visit(ReturnStat &node)
{
	// TODO see comment for FunctionDeclaration's visit method

	if (node.result != NULL) {
		/* FIXME composite results... there needs to be
		 *       an assignment! */
		this->destRegs = RegisterSet(*this->container);
		this->sourceRegs = RegisterSet(*this->container);

		Reference *retRef = new Reference("__return__");
		this->destRegs.setPointer(retRef);

		this->assignExpression = true;
		node.result->accept(*this);
		this->assignExpression = false;
		this->destRegs = RegisterSet(*this->container);

		switch(node.result->baseType) {
		case BASE_TYPE_ENUM:
		case BASE_TYPE_INTEGER:
		case BASE_TYPE_REAL:
			break;

		case BASE_TYPE_ARRAY:
		case BASE_TYPE_RECORD:
			/* TODO allocate space on stack, and have a pointer
			 * return register point to it */
			assert(false);
			break;

		default:
			/* must not happen */
			assert(false);
		}

	}

	Return *ret = new Return();
	this->container->addCode(ret);
}

void
GenCode::visit(SignalDeclaration &node)
{
	// FIXME for procedures, skip OUT signals (only driver needed)
	Data *d = this->processValDecl(node, STORAGE_TYPE_SIGNAL);

	if (d == NULL) {
		return;
	}

	// annotate foreign signals
	if ((node.usage & ValDeclaration::USAGE_FOREIGN) == 0) {
		// not foreign
		return;
	}

	// lookup base type
	// FIXME this doesn't work well for composite types and needs further
	//       thoughts.
	//
	//       array -> element type?
	//       or
	//       array -> array type? (might also be a mapping?)
	//
	//       record -> ???

	assert(node.subtypeIndic != NULL);
	LookupTypes lat = LookupTypes(true, true);
	node.subtypeIndic->accept(lat);
	assert(lat.declaration != NULL);
	assert(lat.declaration->name != NULL);

	d->annotate("foreign", *lat.declaration->name);

	if ((node.usage & ValDeclaration::USAGE_READ) != 0) {
		d->annotate("read", 1);
	}
	// don't care about writes, Driver must handle this.
}

Data *
GenCode::processValDecl(ValDeclaration &node, enum StorageType st)
{
	// skip unused declarations (but *not* signals, as these might be
	// ports.
	// FIXME this shouldn't be done in GenCode, but rather beforehand.
	// Ports are a little bit problematic though. If unused, 
	// NormalizeAssocs will add unassociated ports as association
	// elements.
	// If used in a port map statement, the port is signal is 
	// "kind of" used, i.e. it shouldn't get optimized out --
	// unless the association is *also* removed.

#if 0 /* FIXME deficiency in WarnUnused */
	if (   (node.usage == ValDeclaration::USAGE_NONE)
	    && (st != STORAGE_TYPE_SIGNAL)) {
		return NULL;
	}
#endif

	bool usePointer = false;

	// for transfer segments:
	// * signals/drivers will always be passed as pointers
	// * records/arrays will always be passed as pointers
	switch (this->container->dataMode) {
	case CodeContainer::DATA_TRANSFER:
		switch (st) {
		case STORAGE_TYPE_SIGNAL:
		case STORAGE_TYPE_DRIVER:
			usePointer = true;
			break;

		default:
			switch (node.subtypeIndic->baseType) {
			case BASE_TYPE_RECORD:
			case BASE_TYPE_ARRAY:
				usePointer = true;
				break;

			default:
				break;
			}
		}
	default:
		break;
	}

		
	if (usePointer) {
		std::list<ImmediateOperand*> initial 
			= std::list<ImmediateOperand*>();
		initial.push_back(ImmediateOperand::getZero());

		TypeElement *typeE = 
			new TypeElement("__pointer__", 
					initial,
					1);
		Data *d = new Data(node.getICName(), 
					STORAGE_TYPE_VARIABLE,
					typeE,
					NULL);
		this->container->addData(d);
		if (node.isUnconstraint()) {
			this->addUnconstraintParams(node);
		}
		return d;
	}

	GCTypes::GenTypeElements gte = 
		GCTypes::GenTypeElements(true, 
					"Cannot instantiate a data type "
					"of an unconstraint array.",
					node,
					node.init);
	assert(node.subtypeIndic != NULL);
	node.subtypeIndic->accept(gte);
	if (gte.composite.size() != 1) {
		//error (already reported, hopefully), e.g.
		//instantiating an unconstraint array.
		return NULL;
	}

	TypeElement *te = gte.composite.front();

	std::string *resolver = NULL;

	switch (st) {
	case STORAGE_TYPE_SIGNAL:
		resolver = node.subtypeIndic->getResolver();
		break;

	default:
		break;
	}

	Data *d = new Data(node.getICName(), st, te, resolver);
	this->container->addData(d);
	GenCode::annotateDataSize(*d, node.subtypeIndic);

	return d;
}

void
GenCode::visit(VarDeclaration &node)
{
	this->processValDecl(node, STORAGE_TYPE_VARIABLE);
}

void
GenCode::visit(ConstantDeclaration &node)
{
	this->processValDecl(node, STORAGE_TYPE_VARIABLE);
}

void
GenCode::visit(SubtypeIndication &node)
{
	this->process(node);
}

void
GenCode::visit(FunctionDeclaration &node)
{
	CodeContainer *child = this->processCallable(node);
	if (child == NULL) {
		// error found (and reported) or foreign subprogram.
		return;
	}

	// TODO better idea would be to remap function calls to
	//      procedure calls and add an out variable 
	//      that contains the return value.
	//      This is a little bit tricky to get right though.
	//      (cf. NormalizeAssocs, GatherImplicits)
	child->dataMode = CodeContainer::DATA_TRANSFER;

	// add transfer element for the return type.
	switch (node.returnType->baseType) {
	case BASE_TYPE_ENUM:
	case BASE_TYPE_INTEGER: {
		ImmediateOperand *retInit = ImmediateOperand::getZero();
		std::string btn = TypeFactory::getTypeName(
						node.returnType->baseType);

		std::list<ImmediateOperand*> initVs =
					std::list<ImmediateOperand*>();
		initVs.push_back(retInit);
		Data *d = new Data("__return__",
				STORAGE_TYPE_VARIABLE,
				new TypeElement(btn, initVs),
				NULL);
		child->addData(d);
		break;
	    }
	case BASE_TYPE_REAL: {
		ImmediateOperand *retInit = new ImmediateOperand(0.0);
		std::string btn = TypeFactory::getTypeName(
						node.returnType->baseType);

		std::list<ImmediateOperand*> initVs =
					std::list<ImmediateOperand*>();
		initVs.push_back(retInit);
		Data *d = new Data("__return__",
				STORAGE_TYPE_VARIABLE,
				new TypeElement(btn, initVs), 
				NULL);
		child->addData(d);

		break;
	    }
	case BASE_TYPE_ARRAY:
	case BASE_TYPE_RECORD:
		// TODO
		assert(false);
		break;

	default:
		assert(false);
		break;
	}
	child->dataMode = CodeContainer::DATA_STACK;
}

void
GenCode::visit(ProcedureDeclaration &node)
{
	this->processCallable(node);
}

intermediate::CodeContainer *
GenCode::processCallable(Callable &node)
{
	if (node.hasAttr("foreign") != NULL) {
		// foreign subprogram
		if (node.definition != NULL) {
			CompileError *ce = 
				new CompileError(node,
					"Implementation of foreign "
					"subprogram found.");
			ErrorRegistry::addError(ce);
		}
		return NULL;
	}

	if (node.definition == NULL) {
		CompileError *ce = 
			new CompileError(node, 
				"Subprogram declaration w.o. specification.");
			ErrorRegistry::addError(ce);
		return NULL;
	}

	CodeContainer *current = this->container;
	CodeContainer *spc = new CodeContainer(node.getICName(), current);
	this->container = spc;
	
	this->container->dataMode = CodeContainer::DATA_TRANSFER;
	if (node.arguments != NULL) {
		this->listTraverse(*node.arguments);
	}

	for (std::list<Driver*>::const_iterator i = node.drivers.begin(); 
		i != node.drivers.end(); i++) {
		// only the pointer to the driver is transferred, so 
		// it's a variable of type pointer.
		std::list<ImmediateOperand*> initVs 
			= std::list<ImmediateOperand*>();

		initVs.push_back(ImmediateOperand::getZero());
		TypeElement *te = 
			new TypeElement("__pointer__", 
					initVs);
		Data *d = new Data((*i)->getICName(), 
					STORAGE_TYPE_VARIABLE,
					te,
					NULL);
		this->container->addData(d);
	}
	this->container->dataMode = CodeContainer::DATA_STACK;

	this->currentSubprog = &node;
	node.definition->accept(*this);
	this->currentSubprog = NULL;
	Return *ret = new Return();
	this->container->addCode(ret);

	this->container = current;

	return spc;
}

void
GenCode::process(TypeDeclaration &node)
{
	if (node.name == NULL) {
		return;
	}

	GCTypes::GenTypes gt = GCTypes::GenTypes();
	node.accept(gt);
	if (gt.type != NULL) {
		this->container->addType(gt.type);
	}
}

void
GenCode::processExpression(Expression &node)
{
	if (! this->assignExpression) {
		return;
	}

	assert(node.type != NULL);
	this->doAssignment(*node.type);
}

void
GenCode::doAssignment(TypeDeclaration &type)
{
	RegisterSet *dest = NULL;

	switch (this->assignOperation) {
	case ASSIGN_OPERATION_CONNECT:
	case ASSIGN_OPERATION_COPY:
		dest = &this->destRegs;
		break;

	case ASSIGN_OPERATION_LOG:
	case ASSIGN_OPERATION_WAKEON:
		// only needs one RegisterSet
		break;
	}
	AssignVisitor av = AssignVisitor(
					&this->sourceRegs,
					dest,
					*this->container,
					this->dataOp,
					this->assignOperation);
	type.accept(av);
}

template <typename T>
Register *
GenCode::callSubprog(T &node, const char *foreign)
{
	// FIXME This violates the semantics of the intermediate code:
	// Doing it like this can result in
	//     begintransfer
	//     ...
	//     begintransfer (from an argument)
	//     call
	//     endtransfer
	//     ...
	//     endtransfer
	//
	//     TODO: write a test-case for this.
	assert(node.definition != NULL);
	std::list<OpCode *> copyBackFormals = std::list<OpCode *>();
	
	// FIXME make use of foreign.
	Reference *subprog;
	if (foreign != NULL) {
		subprog = new Reference(*node.definition->name);
	} else {
		subprog = new Reference(node.definition->getICName());
	}
	BeginTransfer *bg = new BeginTransfer(subprog, new Reference(""));
	this->container->addCode(bg);

	if (foreign != NULL) {
		bg->annotate("foreign", foreign);
	}
	
	if (node.arguments != NULL) {
		RegisterSet backup = this->destRegs;
		bool assignExp = this->assignExpression;
		this->assignExpression = false;

		copyBackFormals = this->setArgList(node, foreign);
		this->destRegs = backup;
		this->assignExpression = assignExp;
	}

	Call *c = new Call(subprog);
	this->container->addCode(c);
	if (foreign != NULL) {
		c->annotate("foreign", foreign);
	}

	// copy back formals...
	for (std::list<OpCode *>::const_iterator i = copyBackFormals.begin();
		i != copyBackFormals.end(); i++) {
		
		this->container->addCode(*i);
	}

	Register *ret = getReturnValue(node, subprog);

	ImmediateOperand *trueOp = ImmediateOperand::getOne();
	EndTransfer *et = new EndTransfer(subprog, trueOp);
	if (foreign != NULL) {
		et->annotate("foreign", foreign);
	}
	this->container->addCode(et);

	return ret;
}

template <>
Register *
GenCode::getReturnValue(ProcCallStat &node, Reference *callee)
{
	// no return value for procedures.
	return NULL;
}

template <>
Register *
GenCode::getReturnValue(FunctionCall &node, Reference *callee)
{
	Reference *ref = new Reference("__return__");
	Register *ret =	
		this->container->createRegister(
			toOpType(node.definition->returnType->baseType));
	GetParam *gp = new GetParam(ref, callee, ret);
	// TODO foreign returns
	this->container->addCode(gp);

	return ret;
}


template <typename T>
std::list<OpCode *>
GenCode::setArgList(T &node, const char *foreign)
{
	std::list<OpCode *> ret = std::list<OpCode *>();
	OpCode *op;

	assert(node.arguments != NULL);
	assert(node.definition != NULL);
	assert(node.definition->arguments != NULL);

	std::list<ValDeclaration*>::iterator j =
			node.definition->arguments->begin();

	for (std::list<AssociationElement*>::iterator i = 
		node.arguments->begin(); i != node.arguments->end(); 
		i++, j++) {

		// FIXME individual association

		op = this->setArg(*node.definition, **j, **i, foreign);
		if (op) {
			ret.push_back(op);
		}
	}

	return ret;
}

intermediate::OpCode *
GenCode::setArg(
	Callable &c,
	const ValDeclaration &vd, 
	AssociationElement &element,
	const char *foreign
)
{
	//FIXME individiual association?
	// imo this *should* have been cleared somewhere else. (but is not)
	
	assert(element.formal == NULL);
	assert(element.actual != NULL);
	assert(! this->isTarget);
	assert(! this->assignExpression);
	
	Reference *callee = NULL;
	if (foreign != NULL) {
		callee = new Reference(*c.name);
	} else {
		callee = new Reference(c.getICName());
	}
	OpCode *op = NULL;

	this->sourceRegs = RegisterSet(*this->container);
	this->destRegs = RegisterSet(*this->container);

	switch (vd.storageClass) {
	case ValDeclaration::OBJ_CLASS_CONSTANT:
		switch (element.actual->baseType) {
		case BASE_TYPE_INTEGER:
		case BASE_TYPE_ENUM:
		case BASE_TYPE_REAL:
			this->setArgByValue(callee, &vd, element, foreign, 
					false);
			break;

		case BASE_TYPE_RECORD:
		case BASE_TYPE_ARRAY:
			this->setArgByBasePointerToTemporary(
							callee, 
							vd, 
							element,
							foreign);
			break;

		default:
			// must not happen
			assert(false);
		}
		break;

	case ValDeclaration::OBJ_CLASS_VARIABLE:
		switch (element.actual->baseType) {
		case BASE_TYPE_INTEGER:
		case BASE_TYPE_ENUM:
		case BASE_TYPE_REAL:
			switch (vd.mode) {
			case ValDeclaration::MODE_IN:
				op = this->setArgByValue(
							callee, 
							&vd, 
							element, 
							foreign,
							false); 
				assert(op == NULL);
				break;

			case ValDeclaration::MODE_INOUT:
			case ValDeclaration::MODE_OUT:
				// need to copy back to op.
				op = this->setArgByValue(
							callee,
							&vd, 
							element,
							foreign,
							true);
				assert(op != NULL);
				break;
			}

			break;

		case BASE_TYPE_RECORD:
		case BASE_TYPE_ARRAY:
			this->setArgByBasePointer(
						callee,
						&vd,
						element,
						foreign);
			break;

		default:
			// must not happen
			assert(false);
		}
		break;

	case ValDeclaration::OBJ_CLASS_SIGNAL:
		switch (element.actual->baseType) {
		case BASE_TYPE_ARRAY:
		case BASE_TYPE_RECORD:
			switch (vd.mode) {
			case ValDeclaration::MODE_IN:
				this->setArgByBasePointer(
						callee,
						&vd,
						element,
						foreign);
				break;

			case ValDeclaration::MODE_INOUT:
				this->setArgByBasePointer(
						callee,
						&vd,
						element,
						foreign);
				/* fall through */

			case ValDeclaration::MODE_OUT:
				this->setArgByDriver(
						callee,
						c.drivers,
						vd,
						element,
						foreign,
						true);
				break;
			}
			break;

		case BASE_TYPE_INTEGER:
		case BASE_TYPE_ENUM:
		case BASE_TYPE_REAL:
			switch (vd.mode) {
			case ValDeclaration::MODE_IN:
				this->setArgByPointer(
						callee,
						&vd,
						element,
						foreign);
				break;

			case ValDeclaration::MODE_INOUT:
				this->setArgByPointer(
						callee,
						&vd,
						element,
						foreign);
				/* fall through */

			case ValDeclaration::MODE_OUT:
				this->setArgByDriver(
						callee,
						c.drivers,
						vd,
						element,
						foreign,
						false);
				break;
			}
			break;

		default:
			assert(0);
		}
		break;

	default:
		// must not happen
		assert(false);
	}

	this->sourceRegs = RegisterSet(*this->container);
	this->destRegs = RegisterSet(*this->container);
	return op;
}


OpCode *
GenCode::setArgByValue(
	Reference *callee,
	const ValDeclaration *vd,
	AssociationElement &element,
	const char *foreign,
	bool copyBack
)
{
	Expression *act = element.actual;
	if (act == NULL) {
		act = element.hiddenFormal;
	}
	assert(act != NULL);

	// sanity check
	switch (act->baseType) {
	case BASE_TYPE_INTEGER:
	case BASE_TYPE_REAL:
	case BASE_TYPE_ENUM:
		break;

	default:
		// composites must be called by reference!
		std::cerr 
			<< "hm....? act=" << act 
			<< ", formal=" << element.formal
			<< std::endl;
		assert(false);
	case BASE_TYPE_UNSET:
		assert(false);
	}

	if (vd != NULL) {
		assert(element.formal == NULL); // FIXME
	} else {
		// FIXME individual association
		SimpleName *sn = dynamic_cast<SimpleName*>(element.formal);
		assert(sn != NULL);
		vd = dynamic_cast<ValDeclaration*>(sn->getDeclaration());
		assert(vd != NULL);
	}

	act->accept(*this);

	Reference *formal;
	if (foreign != NULL) {
		formal = new Reference(*vd->name);
	} else {
		formal = new Reference(vd->getICName());
	}

	Operand *value = this->sourceRegs.getValue(act->baseType);
	SetParam *sp = new SetParam(value, callee, formal);
	this->container->addCode(sp);
	if (foreign != NULL) {
		sp->annotate("foreign", foreign);
		GenCode::annotateSetParamType(*sp, *vd);
	}

	if (copyBack) {
		Operand *dest = this->sourceRegs.getDestination(
							act->baseType);
		GetParam *gp = new GetParam(formal, callee, dest);
		return gp;
	}
	return NULL;
}

void
GenCode::setArgByBasePointer(
	Reference *callee,
	const ValDeclaration *vd,
	AssociationElement &element,
	const char *foreign
)
{
	if (vd == NULL) {
		assert(element.formal != NULL);
		// FIXME
		SimpleName *sn = dynamic_cast<SimpleName*>(element.formal);
		assert(sn != NULL);
		vd = dynamic_cast<ValDeclaration*>(sn->getDeclaration());
		assert(vd != NULL);
	}

	Expression *act = element.actual;
	if (act == NULL) {
		act = element.hiddenFormal;
	}
	assert(act != NULL);
	act->accept(*this);

	std::string formal_name;
	if (foreign != NULL) {
		formal_name = *vd->name;
	} else {
		formal_name = vd->getICName();
	}
	Reference *formal = new Reference(formal_name);

	Operand *pointer = this->sourceRegs.getPointer();
	SetParam *sp = new SetParam(pointer, callee, formal);
	this->container->addCode(sp);

	if (foreign != NULL) {
		sp->annotate("foreign", foreign);
		GenCode::annotateSetParamType(*sp, *vd);
	}

	if (vd->isUnconstraint()) {
		if (this->sourceRegs.isUnconstraint()) {
			// either constraints are paremeters already
			this->setConstraintsByRS(callee, 
						formal_name, 
						foreign);
		} else {
			// or the array is constraint
			this->setConstraintsByType(callee,
					formal_name, 
					act->type,
					foreign);
		}
	}
}

void
GenCode::setArgByBasePointerToTemporary(
	Reference *callee,
	const ValDeclaration &vd,
	AssociationElement &element,
	const char *foreign
)
{
	assert(element.hiddenFormal != NULL);

	// assign the constant to the hidden formal
	this->assignExpression = false;
	this->isTarget = true;
	element.hiddenFormal->accept(*this);
	this->destRegs = this->sourceRegs;
	this->sourceRegs = RegisterSet(*this->container);

	this->isTarget = false;
	this->assignExpression = true;
	element.actual->accept(*this);
	this->assignExpression = false;
	
	// get a grip on the hidden formal
	this->sourceRegs = RegisterSet(*this->container);
	element.hiddenFormal->accept(*this);

	std::string formal_name;
	if (foreign != NULL) {
		formal_name = *vd.name;
	} else {
		formal_name = vd.getICName();
	}
	Reference *formal = new Reference(formal_name);

	SetParam *sp = 
		new SetParam(this->sourceRegs.getPointer(), callee, formal);
	this->container->addCode(sp);
	if (foreign != NULL) {
		sp->annotate("foreign", foreign);
		GenCode::annotateSetParamType(*sp, vd);
	}

	if (vd.isUnconstraint()) {
		if (this->sourceRegs.isUnconstraint()) {
			// either constraints are paremeters already
			this->setConstraintsByRS(callee, formal_name, 
						foreign);
		} else {
			// or the array is constraint
			this->setConstraintsByType(callee,
					formal_name, 
					element.actual->type,
					foreign);
		}
	}
}

void
GenCode::setArgByPointer(
	Reference *callee,
	const ValDeclaration *vd,
	AssociationElement &element,
	const char *foreign
)
{
	if (vd == NULL) {
		assert(element.formal != NULL);
		// FIXME
		SimpleName *sn = dynamic_cast<SimpleName*>(element.formal);
		assert(sn != NULL);
		vd = dynamic_cast<ValDeclaration*>(sn->getDeclaration());
		assert(vd != NULL);
	}

	Expression *act = element.actual;
	if (act == NULL) {
		act = element.hiddenFormal;
	}
	assert(act != NULL);
	act->accept(*this);

	Reference *formal;
	if (foreign != NULL) {
		formal = new Reference(*vd->name);
	} else {
		formal = new Reference(vd->getICName());
	}

	Operand *pointer = this->sourceRegs.getDestination(act->baseType);
	SetParam *sp = new SetParam(pointer, callee, formal);
	this->container->addCode(sp);

	if (foreign != NULL) {
		sp->annotate("foreign", foreign);
		GenCode::annotateSetParamType(*sp, *vd);
	}
}


void
GenCode::setArgByDriver(
	Reference *callee,
	const std::list<Driver *> &drivers,
	const ValDeclaration &vd,
	AssociationElement &element,
	const char *foreign,
	bool usePointer
)
{
	assert(element.formal == NULL);
	// need a driver
	this->isTarget = true;
	element.actual->accept(*this);
	this->isTarget = false;

	std::string name;
	bool found = false;

	for (std::list<Driver *>::const_iterator i = drivers.begin();
		i != drivers.end(); i++) {
		
		if (&(*i)->signal == &vd) {
			if (foreign != NULL) {
				name = (*i)->getForeignName();
			} else {
				name = (*i)->getICName();
			}
			found = true;
			break;
		}
	}

	assert(found);

	Reference *formal = new Reference(name);
	Operand *drv;
	if (usePointer) {
		drv = this->sourceRegs.getPointer();
	} else {
		drv = this->sourceRegs.getDestination(
					element.actual->baseType);
	}
	SetParam *sp = new SetParam(drv, callee, formal);
	this->container->addCode(sp);
	if (foreign != NULL) {
		sp->annotate("foreign", foreign);
	}
}

void
GenCode::annotateSetParamType(
	SetParam &sp,
	const ValDeclaration &vd
)
{
	const TypeDeclaration *t = 
		ResolveTypes::findBaseType(vd.subtypeIndic);
	
	switch (vd.subtypeIndic->baseType) {
	case BASE_TYPE_INTEGER:
	case BASE_TYPE_REAL:
	case BASE_TYPE_RECORD:
	case BASE_TYPE_ENUM:
		// cannot be an anonymous type, and doesn't need constraints
		assert(t->name != NULL);
		sp.annotate("type", *t->name);
		sp.annotate("array", 0);
		return;

	case BASE_TYPE_ARRAY:
		sp.annotate("array", 1);
		break;

	default:
		// must not happen
		assert(false);
	}

	// case: array
	const UnconstrainedArrayType *ua = 
		dynamic_cast<const UnconstrainedArrayType*>(t);
	assert(ua != NULL);
	assert(ua->elementType != NULL);
	// FIXME doesn't exactly match with GCTypes...
	t = ResolveTypes::findBaseType(ua->elementType);
	sp.annotate("type", *t->name);
	
	// only valid for foreign SetParams!
	if (vd.isUnconstraint()) {
		sp.annotate("unconstraint", 1);
	} else {
		sp.annotate("unconstraint", 0);
		// FIXME bounds somewhere?
	}
}


void
GenCode::setConstraintsByType(
	Reference *cRef,
	const std::string &icPrefix, 
	const TypeDeclaration *t,
	const char *foreign
)
{
	std::list<DiscreteRange *> ic = std::list<DiscreteRange *>();

	ResolveTypes::pickupIndexConstraint(t, ic);
	assert(! ic.empty());

	size_t dim = 0;
	for (std::list<DiscreteRange *>::const_iterator i = ic.begin();
		i != ic.end(); i++, dim++) {

		ImmediateOperand *lb = 
			new ImmediateOperand((*i)->getLeftBound());
		ImmediateOperand *rb = 
			new ImmediateOperand((*i)->getRightBound());
		ImmediateOperand *db = 
			new ImmediateOperand((*i)->getDirection());
		Reference *lname = new Reference(icPrefix + "_lb_" 
					+ util::MiscUtil::toString(dim));
		Reference *rname = new Reference(icPrefix + "_rb_" 
					+ util::MiscUtil::toString(dim));
		Reference *dirname = new Reference(icPrefix + "_dir_"
					+ util::MiscUtil::toString(dim));
		SetParam *sp1 = new SetParam(lb, cRef, lname);
		SetParam *sp2 = new SetParam(rb, cRef, rname);
		SetParam *sp3 = new SetParam(db, cRef, dirname);
		this->container->addCode(sp1);
		this->container->addCode(sp2);
		this->container->addCode(sp3);
		if (foreign != NULL) {
			sp1->annotate("foreign", foreign);
			sp2->annotate("foreign", foreign);
			sp3->annotate("foreign", foreign);
			sp1->annotate("bounds", "l");
			sp2->annotate("bounds", "r");
			sp3->annotate("bounds", "dir");
			sp1->annotate("array", 1);
			sp2->annotate("array", 1);
			sp3->annotate("array", 1);
			sp1->annotate("unconstraint", 1);
			sp2->annotate("unconstraint", 1);
			sp3->annotate("unconstraint", 1);
		}
	}
}

void
GenCode::setConstraintsByRS(
	Reference *cRef, 
	const std::string &icPrefix,
	const char *foreign
)
{
	std::list<Operand *> lb;
	std::list<Operand *> rb;
	std::list<Operand *> dirs;

	this->sourceRegs.getConstraints(lb, rb, dirs);
	std::list<Operand *>::const_iterator li = lb.begin();
	std::list<Operand *>::const_iterator ri = rb.begin();
	std::list<Operand *>::const_iterator di = dirs.begin();
	assert(lb.size() == dirs.size());

	for (size_t dim = 0; li != lb.end(); li++, ri++, di++, dim++) {
		Reference *lname = new Reference(icPrefix + "_lb_" 
					+ util::MiscUtil::toString(dim));
		Reference *rname = new Reference(icPrefix + "_rb_" 
					+ util::MiscUtil::toString(dim));
		Reference *dirname = new Reference(icPrefix + "_dir_"
					+ util::MiscUtil::toString(dim));

		SetParam *sp1 = new SetParam(*li, cRef, lname);
		SetParam *sp2 = new SetParam(*ri, cRef, rname);
		SetParam *sp3 = new SetParam(*di, cRef, dirname);

		this->container->addCode(sp1);
		this->container->addCode(sp2);
		this->container->addCode(sp3);
		if (foreign != NULL) {
			sp1->annotate("foreign", foreign);
			sp1->annotate("bounds", "l");
			sp2->annotate("foreign", foreign);
			sp2->annotate("bounds", "r");
			sp3->annotate("foreign", foreign);
			sp3->annotate("bounds", "dir");
			sp1->annotate("array", 1);
			sp2->annotate("array", 1);
			sp3->annotate("array", 1);
			sp1->annotate("unconstraint", 1);
			sp2->annotate("unconstraint", 1);
			sp3->annotate("unconstraint", 1);
		}
	}
}

void
GenCode::addUnconstraintParams(const ValDeclaration &node)
{
	const UnconstrainedArrayType *ua = 
		dynamic_cast<const UnconstrainedArrayType *>(
			ResolveTypes::findBaseType(node.subtypeIndic));
	assert(ua != NULL);

	std::string prefix = node.getICName();

	for (size_t dim = 0; dim < ua->numIndices; dim++) {
		// make a NULL range the default
		std::list<ImmediateOperand*> lIv 
			= std::list<ImmediateOperand*>();
		std::list<ImmediateOperand*> rIv
			= std::list<ImmediateOperand*>();
		std::list<ImmediateOperand*> dIv
			= std::list<ImmediateOperand*>();
		lIv.push_back(ImmediateOperand::getOne());
		rIv.push_back(ImmediateOperand::getZero());
		dIv.push_back(ImmediateOperand::getOne());

		TypeElement *ltypeE = 
			new TypeElement("universal_integer", lIv);
		TypeElement *rtypeE = 
			new TypeElement("universal_integer", rIv);
		TypeElement *dtypeE = 
			new TypeElement("universal_integer", dIv);
		std::string lname = prefix + "_lb_" 
			 	  + util::MiscUtil::toString(dim);
		std::string rname = prefix + "_rb_" 
			 	  + util::MiscUtil::toString(dim);
		std::string dirname = prefix + "_dir_"
				  + util::MiscUtil::toString(dim);

		Data *ld = new Data(lname, STORAGE_TYPE_VARIABLE, ltypeE, NULL);
		Data *rd = new Data(rname, STORAGE_TYPE_VARIABLE, rtypeE, NULL);
		Data *dd = new Data(dirname, 
					STORAGE_TYPE_VARIABLE, 
					dtypeE, 
					NULL);
		this->container->addData(ld);
		this->container->addData(rd);
		this->container->addData(dd);
	}
}

void
GenCode::getConstraints(const ValDeclaration &node)
{
	const UnconstrainedArrayType *ua = 
		dynamic_cast<const UnconstrainedArrayType *>(
			ResolveTypes::findBaseType(node.subtypeIndic));
	assert(ua != NULL);

	std::string prefix = node.getICName();

	std::list<Operand *> lbs;
	std::list<Operand *> rbs;
	std::list<Operand *> dirs;

	for (size_t dim = 0; dim < ua->numIndices; dim++) {
		std::string lname = prefix + "_lb_" 
			 	  + util::MiscUtil::toString(dim);
		std::string rname = prefix + "_rb_" 
			 	  + util::MiscUtil::toString(dim);
		std::string dirname = prefix + "_dir_"
				  + util::MiscUtil::toString(dim);

		Register *lb =
			this->container->createRegister(OP_TYPE_POINTER);
		Register *rb = 
			this->container->createRegister(OP_TYPE_POINTER);
		Register *dir = 
			this->container->createRegister(OP_TYPE_POINTER);

		Mov *lm = new Mov(new Reference(lname), lb);
		Mov *rm = new Mov(new Reference(rname), rb);
		Mov *dm = new Mov(new Reference(dirname), dir);

		this->container->addCode(lm);
		this->container->addCode(rm);
		this->container->addCode(dm);

		IndirectOperand *lbv = 
			new IndirectOperand(lb, OP_TYPE_INTEGER);
		IndirectOperand *rbv = 
			new IndirectOperand(rb, OP_TYPE_INTEGER);
		IndirectOperand *dbv = 
			new IndirectOperand(dir, OP_TYPE_INTEGER);

		lbs.push_back(lbv);
		rbs.push_back(rbv);
		dirs.push_back(dbv);
	}

	this->sourceRegs.setUnconstraintBounds(lbs, rbs, dirs);
}

void
GenCode::handleCompositeReturn(FunctionCall &node)
{
	assert(false);
	/* TODO */
}

void
GenCode::annotateDataSize(Data &data, const SubtypeIndication *type)
{
	const TypeDeclaration *t = ResolveTypes::findBaseType(type);
	const EnumerationType *et = dynamic_cast<const EnumerationType*>(t);
	// consider only enumeration types
	if (et == NULL) {
		// also consider one-dimensional arrays
		const UnconstrainedArrayType *ua = 
			dynamic_cast<const UnconstrainedArrayType *>(t);

		if (ua == NULL) {
			return;
		}

		t = ResolveTypes::findBaseType(ua->elementType);
		et = dynamic_cast<const EnumerationType*>(t);
		if (et == NULL) {
			return;
		}
	}

	assert(et->elements != NULL);
	size_t sz = et->elements->size();
	double bit_count = log(static_cast<double>(sz)) / log(2.0);
	int cnt = static_cast<int>(ceil(bit_count));

	data.annotate("display_size", cnt);
	data.annotate("display", *et->name);
}

void
GenCode::visit(AttributeSpecification &node)
{
	//TODO
}

void
GenCode::processAlternative(
	CaseAlternative &node,
	Operand *cmpVal,
	Label *caseNext, 
	Label *caseOut
)
{
	assert(node.isVals != NULL);
	assert(node.thenStats != NULL);

	Label *altFound = LabelFactory::getLabel("alternative_execute");

	for (std::list<Expression *>::const_iterator i = 
		node.isVals->begin(); i != node.isVals->end(); i++) {

		const Others *others = dynamic_cast<const Others*>(*i);
		if (others != NULL) {
			Jmp *jmp = new Jmp(altFound);
			this->container->addCode(jmp);
			break;
		}

		this->assignExpression = false;
		this->isTarget = false;
		this->sourceRegs = RegisterSet(*this->container);
		(*i)->accept(*this);

		Operand *choice = this->sourceRegs.getValue((*i)->baseType);
		Je *je = new Je(cmpVal, choice, altFound);
		this->container->addCode(je);
	}

	Jmp *jmp = new Jmp(caseNext);
	this->container->addCode(jmp);
	this->container->addCode(altFound);

	this->sourceRegs = RegisterSet(*this->container);
	this->listTraverse(*node.thenStats);
	Jmp *jmp2 = new Jmp(caseOut);
	this->container->addCode(jmp2);
}

void
GenCode::visit(CaseStat &node)
{
	// TODO: there are a number of restrictions on 
	//       select and alternatives, which aren't checked yet
	//       see lrm 8.8 for details.
	//       However these restrictions should be checked elsewhere.
	//
	// TODO 2: This is a pretty poor implementation, as all
	//       expression choices should be locally static.
	//       A good implementation would choose the best algorithm
	//       for the alternatives, including
	//       - a jump lookup table
	//       - if/then/else constructs (that's done here)
	//       - a combination thereof
	//       - ...

	assert(node.select != NULL);
	assert(node.alternatives != NULL);

	this->assignExpression = false;
	this->isTarget = false;
	this->sourceRegs = RegisterSet(*this->container);
	this->destRegs = RegisterSet(*this->container);

	switch (node.select->baseType) {
	case BASE_TYPE_RECORD:
	case BASE_TYPE_ARRAY:
		// FIXME not handled yet: this needs to 
		// 1) assign the result of the expression to a temporary
		// 2) compare the temporary with the choices
		assert(false);
		break;

	case BASE_TYPE_UNSET:
		assert(false);
		break;

	default:
		break;
	}

	node.select->accept(*this);
	Operand *cmpVal = this->sourceRegs.getValue(node.select->baseType);
	this->sourceRegs = RegisterSet(*this->container);
	
	Label *caseOut = LabelFactory::getLabel("case_out");

	// handle each alternative
	for (std::list<CaseAlternative *>::const_iterator i = 
		node.alternatives->begin();
		i != node.alternatives->end(); /* nothing */) {

		CaseAlternative *current = *i;
		Label *caseNext = caseOut;
		i++;

		// use caseOut as last next label.
		if (i != node.alternatives->end()) {
			caseNext = LabelFactory::getLabel("case_next");
		}

		this->processAlternative(*current, cmpVal, caseNext, caseOut);

		if (i != node.alternatives->end()) {
			this->container->addCode(caseNext);
		}
	}


	this->container->addCode(caseOut);
}

/*
 * ================== ASSIGN VISITOR ====================
 */

void
GenCode::AssignVisitor::visit(SubtypeIndication &node)
{
	// traverse to the defining type.
	assert(node.declaration != NULL);

	switch (node.baseType) {
	case BASE_TYPE_ENUM:
	case BASE_TYPE_INTEGER:
	case BASE_TYPE_REAL: {
		/* traverse to declaration */
		TypeDeclaration *parent = 
			const_cast<TypeDeclaration*>(node.declaration);
			parent->accept(*this);
		return;
	   }
	case BASE_TYPE_RECORD:
		/* subtype of a record cannot happen */
		assert(false);
		break;

	case BASE_TYPE_ARRAY:
		this->processArraySubtype(node);
		break;

	default:
		/* must not happen */
		assert(false);
		break;
	}
}

void
GenCode::AssignVisitor::processArraySubtype(SubtypeIndication &node)
{
	// FIXME slices
	// FIXME unconstraint lbounds/ubounds
	class ArrayAssign : public ArrayIterate {
	public:
		ArrayAssign(
			TypeDeclaration *at,
			intermediate::CodeContainer &container,
			AssignVisitor &instance,
			std::list<Operand *> unclbounds,
			std::list<Operand *> uncubounds,
			std::list<Operand *> undirects
			) : 	ArrayIterate(at, 
						instance.src->getPointer(),
						container,
						unclbounds,
						uncubounds,
						undirects),
				av(instance) {}

	private:
		// cppcheck-suppress unusedPrivateFunction (false positive)
		virtual void iterateBody(
			Register *element, 
			std::list<Register *> idcs
		) 
		{
			RegisterSet *old_src = this->av.src;
			RegisterSet *old_dst = this->av.dst;
			this->av.src = new RegisterSet(this->cc);

			if (this->av.dst != NULL) {	
				this->av.dst = new RegisterSet(*this->av.dst);
				this->setDest(idcs);
			}
			this->av.src->isSignal = old_src->isSignal;
			this->av.src->setPointer(element);

			switch (this->elementType->baseType) {
			case BASE_TYPE_RECORD:
				this->elementType->accept(this->av);
				break;

			case BASE_TYPE_INTEGER:
			case BASE_TYPE_REAL:
			case BASE_TYPE_ENUM:
				this->av.performOperation(
					this->elementType->baseType);
				break;

			default:
				/* must not happen */
				assert(false);
				break;
			}

			delete this->av.src;

			if (this->av.dst != NULL) {
				delete this->av.dst;
			}
			this->av.src = old_src;
			this->av.dst = old_dst;
		}

		void setDest(std::list<Register *> idcs) {
			assert(this->av.dst != NULL);

			std::list<Operand*> idx = std::list<Operand*>();
			for (std::list<Register *>::const_iterator i =
				idcs.begin(); i != idcs.end(); i++) {
				
				idx.push_back(*i);
			}

			this->av.dst->subscribe(this->arrayType, idx);
		}

		AssignVisitor &av;
	};

	std::list<Operand *> lb;
	std::list<Operand *> ub;
	std::list<Operand *> ds;

	this->src->getConstraints(lb, ub, ds);
	if (this->dst != NULL) {
		// perform subtype conversion on the fly for unconstraint
		// arrays.
		// FIXME where bounds are statically known, subtype conversion
		// must have taken place beforehand!
		//
		// FIXME this still leaves the problem of unconstraint <->
		// constraint
		//
		// FIXME actually src should take over the constraints of
		//       dest, but that doesn't matter that much imho
		if (this->dst->isUnconstraint()) {
			if (this->src->isUnconstraint()) {
				this->dst->setUnconstraintBounds(lb, ub, ds);
			} else {
				assert(false);
			}
		}
	}
	ArrayAssign aa = ArrayAssign(&node, 
					this->container, 
					*this,
					lb,
					ub,
					ds);
	aa.iterate();
}

void
GenCode::AssignVisitor::visit(UnconstrainedArrayType &node)
{
	// mustn't traverse to UnconstrainedArrayType, should be handled from
	// elsewhere already
	// FIXME: eventually unconstrained arrays as parameters of subprograms
	//        might bring us here.
	assert(false);
}

void
GenCode::AssignVisitor::visit(RecordType &node)
{
	universal_integer relement = 0;

	for (std::list<RecordTypeElement *>::const_iterator i = 
		node.elements->begin(); i != node.elements->end(); i++) {
		(*i)->accept(*this);

		Operand *src_composite = this->src->getPointer();
		Operand *dst_composite = NULL;
		if (this->dst != NULL) {
			dst_composite = this->dst->getPointer();
		}

		// calculate new record offset of source
		relement++;
		Register *nb1 = 
			this->container.createRegister(OP_TYPE_POINTER);
		ImmediateOperand *off1 = new ImmediateOperand(relement);
		Type *rt1 = TypeFactory::lookupType(node.getICName());
		assert(rt1 != NULL);
		ROffset *roffOp1 = 
			new ROffset(this->src->getPointer(), off1, rt1, nb1);
		this->container.addCode(roffOp1);

		this->src->setPointer(nb1);

		// and for destination
		if (this->dst != NULL) {
			Register *nb2 = 
				this->container.createRegister(
							OP_TYPE_POINTER);
			ImmediateOperand *off2 = 
				new ImmediateOperand(relement);
			Type *rt2 = TypeFactory::lookupType(node.getICName());
			assert(rt2 != NULL);
			ROffset *roffOp2 = 
				new ROffset(
					this->dst->getPointer(), 
					off2, 
					rt2, 
					nb2);
			this->container.addCode(roffOp2);

			this->dst->setPointer(nb2);
		}

		// traverse to element
		(*i)->accept(*this);
		// restore old base pointers
		this->src->setPointer(src_composite);
		if (this->dst != NULL) {
			this->dst->setPointer(dst_composite);
		}
	}
}

void
GenCode::AssignVisitor::visit(RecordTypeElement &node)
{
	node.subtype->accept(*this);
}

void
GenCode::AssignVisitor::visit(RangeConstraintType &node)
{
	this->performOperation(node.baseType);
}

void
GenCode::AssignVisitor::visit(PhysicalType &node)
{
	this->performOperation(BASE_TYPE_INTEGER);
}

void
GenCode::AssignVisitor::visit(EnumerationType &node)
{
	this->performOperation(BASE_TYPE_INTEGER);
}

void
GenCode::AssignVisitor::performOperation(enum BaseType t)
{
	switch (this->operation) {
	case ASSIGN_OPERATION_COPY:
		this->makeAssignment(t);
		break;

	case ASSIGN_OPERATION_LOG:
		this->logValue();
		break;

	case ASSIGN_OPERATION_CONNECT:
		this->connect();
		break;
	
	case ASSIGN_OPERATION_WAKEON:
		this->wakeon(t);
		break;
	}
}

void
GenCode::AssignVisitor::makeAssignment(enum BaseType t)
{
	Operand *so = this->src->getValue(t);
	assert(this->dst != NULL);
	Operand *dsto = this->dst->getDestination(t);

	OpCode *assign;

	if (this->dst->isSignal) {
		assert(this->dataOp != NULL);
		assign = new Update(so, this->dataOp, dsto);
	} else {
		assign = new Mov(so, dsto);
	}
	this->container.addCode(assign);
}

void
GenCode::AssignVisitor::logValue(void)
{
	assert(this->dataOp != NULL);

	Operand *c = this->src->getValue(BASE_TYPE_INTEGER);
	Log *l = new Log(this->dataOp, c);

	this->container.addCode(l);
}

void
GenCode::AssignVisitor::wakeon(enum BaseType t)
{
	Operand *sigPtr = this->src->getDestination(t);
	WakeOn *wo = new WakeOn(sigPtr);

	this->container.addCode(wo);
}

void
GenCode::AssignVisitor::connect(void)
{
	assert(this->dataOp == NULL);
	assert(this->dst->isSignal);
	// hm... getDestination ignores the base type if is a signal.
	Connect *c = new Connect(this->dst->getDestination(BASE_TYPE_REAL),
				this->src->getDestination(BASE_TYPE_REAL));

	this->container.addCode(c);
}

/*
 * ================== PROCESS STACKED EXPRESSION ===============
 */

GenCode::ProcessStackedExpression::ProcessStackedExpression(
	GenCode &gc,
	Expression &n
):		genCode(gc),
		node(n),
		stackedAssignExpression(gc.assignExpression)
{
	gc.assignExpression = false;
}

GenCode::ProcessStackedExpression::~ProcessStackedExpression()
{
	this->genCode.assignExpression = this->stackedAssignExpression;

	if (this->stackedAssignExpression) {
		this->genCode.processExpression(node);
	}
}

}; /* namespace ast */
