/*
 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 * See the copyright notice in the ACK home directory, in the file "Copyright".
 *
 * Author: Hans van Staveren
 */
#include <assert.h>
#include "param.h"
#include "types.h"
#include "tes.h"
#include <em_spec.h>
#include <em_pseu.h>
#include "alloc.h"
#include "util.h"
#include "putline.h"
#include "line.h"
#include "reg.h"
#include "lookup.h"
#include "proinf.h"
#include "ext.h"

/* External definitions */
extern void flow(void);
extern void backward(void);
extern int peephole(void);

static void relabel(void)
{
	register num_p *npp, np, tp;
	register num_p repl, ttp;

	/*
	 * For each label find its final destination after crossjumping.
	 * Care has to be taken to prevent a loop in the program to
	 * cause same in the optimizer.
	 */

	for (npp = curpro.numhash; npp < &curpro.numhash[NNUMHASH]; npp++)
		for (np = *npp; np != (num_p) 0; np = np->n_next)
		{
			assert(
					! np->n_line || ((np->n_line->l_instr&BMASK) == op_lab && np->n_line->l_a.la_np == np));
			for (tp = np; (tp->n_flags & (NUMKNOWN | NUMMARK)) == 0;
					tp = tp->n_repl)
				tp->n_flags |= NUMMARK;
			repl = tp->n_repl;
			for (tp = np; tp->n_flags & NUMMARK; tp = ttp)
			{
				ttp = tp->n_repl;
				tp->n_repl = repl;
				tp->n_flags &= ~ NUMMARK;
				tp->n_flags |= NUMKNOWN;
			}
		}
	for (npp = curpro.numhash; npp < &curpro.numhash[NNUMHASH]; npp++)
		for (np = *npp; np != (num_p) 0; np = np->n_next)
		{
			np->n_flags &= ~(NUMKNOWN | NUMSCAN | NUMREACH);
			np->n_jumps = 0;
		}
}

static void symknown(void)
{
	register sym_p *spp, sp;

	for (spp = symhash; spp < &symhash[NSYMHASH]; spp++)
		for (sp = *spp; sp != (sym_p) 0; sp = sp->s_next)
			if (sp->s_flags & SYMSEEN)
				sp->s_flags |= SYMKNOWN;
}

static void cleanlocals(void)
{
	register num_p *npp, np, tp;

	for (npp = curpro.numhash; npp < &curpro.numhash[NNUMHASH]; npp++)
	{
		np = *npp;
		while (np != (num_p) 0)
		{
			tp = np->n_next;
			oldnum(np);
			np = tp;
		}
		*npp = (num_p) 0;
	}
}

static void checklocs(void)
{
	register num_p *npp, np;

	for (npp = curpro.numhash; npp < &curpro.numhash[NNUMHASH]; npp++)
		for (np = *npp; np != (num_p) 0; np = np->n_next)
			if (np->n_line == (line_p) 0)
				error("local label %u undefined", (unsigned) np->n_number);
}

static offset align(offset count, offset alignment)
{

	assert(alignment == 1 || alignment == 2 || alignment == 4);
	return ((count + alignment - 1) & ~(alignment - 1));
}

static void symvalue(void)
{
	register line_p lp;
	register sym_p sp;
	register arg_p ap;
	register argb_p abp;
	short curfrag = 0;
	offset count;

	for (lp = pseudos; lp != (line_p) 0; lp = lp->l_next)
		switch (lp->l_instr & BMASK)
		{
			default:
				assert(FALSE);
				break;
			case ps_sym:
				sp = lp->l_a.la_sp;
				if (sp->s_frag != curfrag)
				{
					count = 0;
					curfrag = sp->s_frag;
				}
				count = align(count, wordsize);
				sp->s_value = count;
				break;
			case ps_bss:
			case ps_hol:
				/* nothing to do, all bss pseudos are in diff frags */
			case ps_mes:
				break;
			case ps_con:
			case ps_rom:
				for (ap = lp->l_a.la_arg; ap != (arg_p) 0; ap = ap->a_next)
					switch (ap->a_typ)
					{
						default:
							assert(FALSE);
						case ARGOFF:
							count = align(count, wordsize) + wordsize;
							break;
						case ARGNUM:
						case ARGSYM:
						case ARGVAL:
							count = align(count, wordsize) + pointersize;
							break;
						case ARGICN:
						case ARGUCN:
						case ARGFCN:
							if (ap->a_a.a_con.ac_length < wordsize)
								count = align(count,
										(offset) ap->a_a.a_con.ac_length);
							else
								count = align(count, wordsize);
							count += ap->a_a.a_con.ac_length;
							break;
						case ARGSTR:
							for (abp = &ap->a_a.a_string; abp != (argb_p) 0;
									abp = abp->ab_next)
								count += abp->ab_index;
							break;
					}
		}
}

static void do_tes(void)
{
	register line_p insptr = instrs, oldlin = NULL, oldlin2 = NULL;

	init_state();
	tes_pseudos();
	while (insptr != NULL)
	{
		tes_instr(insptr, oldlin, oldlin2);
		oldlin2 = oldlin;
		oldlin = insptr;
		insptr = insptr->l_next;
	}
}

void process(void)
{

	if (wordsize == 0 || pointersize == 0)
		error("No MES EMX encountered");
	backward(); /* reverse and cleanup list */
	symknown(); /* symbol scope is now known */
	if (!nflag)
		symvalue(); /* give symbols value */
	if (prodepth != 0)
	{
		if (!nflag)
		{
			int npasses = 0;
			bool madeopt;

			checklocs(); /* check definition of locals */
			do
			{
				madeopt = peephole(); /* local optimization */
				relabel(); /* relabel local labels */
				flow(); /* throw away unreachable code */
			} while (madeopt && ++npasses < 5000);
			assert(!madeopt);
		}
		do_tes(); /* top elt. size computation phase */
		outpro(); /* generate PRO pseudo */
		outregs(); /* generate MES ms_reg pseudos */
		outtes(); /* generate MES ms_tes pseudos */
	}
	putlines(pseudos); /* pseudos first */
	if (prodepth != 0)
	{
		putlines(instrs); /* instructions next */
		outend(); /* generate END pseudo */
		cleanlocals(); /* forget instruction labels */
	}
	else if (instrs != (line_p) 0)
		error("instructions outside procedure");
#ifdef COREDEBUG
	coreverbose();
#endif
}