/*
 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 * See the copyright notice in the ACK home directory, in the file "Copyright".
 */
#ifndef NORCSID
static char rcsid[] = "$Id$";
#endif

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "param.h"
#include "set.h"
#include "reg.h"
#include "lookup.h"
#include "token.h"
#include "property.h"
#include "expr.h"
#include "regvar.h"
#include <cgg_cg.h>
#include <em_reg.h>
#include "extern.h"

extern set_t l_sets[];

int i_expr(expr_t e)
{

	if (e.ex_typ != TYPINT)
		error("Expression should be integer");
	return (e.ex_index);
}

int b_expr(expr_t e)
{
	if (e.ex_typ != TYPBOOL)
		error("Expression should be boolean");
	return (e.ex_index);
}

expr_t make_expr(int type, int operator, int op1, int op2)
{
	expr_t result;

	result.ex_typ = type;
	result.ex_index = ex_lookup(operator, op1, op2);
	return (result);
}

expr_t regno_expr(int regno)
{
	expr_t result;
	register int i;

	result.ex_typ = TYPREG;
	result.ex_index = ex_lookup(EX_REG, regno, 0);
	for (i = 0; i < SZOFSET(MAXREGS); i++)
		result.ex_regset[i] = 0;
	BIS(result.ex_regset, regno);
	return (result);
}

expr_t ident_expr(char *name)
{
	register symbol *sy_p;

	sy_p = lookup(name, symany, mustexist);
	if (sy_p->sy_type == symconst)
		return (make_expr(TYPINT, EX_CON,
				(int) (sy_p->sy_value.syv_cstval & 0xFFFF),
				(int) (sy_p->sy_value.syv_cstval >> 16)));
	else if (sy_p->sy_type == symsconst)
		return (make_expr(TYPADDR, EX_STRING, sy_p->sy_value.syv_stringno, 0));
	else if (sy_p->sy_type != symreg)
		error("Wrong type of identifier %s", name);
	return (regno_expr(sy_p->sy_value.syv_regno));
}

expr_t subreg_expr(int tokarg, int subreg)
{
	expr_t result;

	result.ex_typ = TYPREG;
	subregset(l_sets[tokpatset[tokarg > 0 ? tokarg - 1 : tokarg]].set_val,
			subreg, result.ex_regset);
	result.ex_index = ex_lookup(EX_SUBREG, tokarg, subreg);
	return (result);
}

void subregset(register short *sp, int subreg, register short *regset)
{
	register int i;
	register reginfo *rp;

	for (i = 0; i < SZOFSET(MAXREGS); i++)
		regset[i] = 0;
	for (i = 1; i < nregs; i++)
		if (BIT(sp, i))
		{
			if (subreg)
			{
				rp = &l_regs[i];
				if (rp->ri_memb[subreg - 1] == 0)
					error("Register %s in set has no member %d", rp->ri_name,
							subreg);
				BIS(regset, rp->ri_memb[subreg - 1]);
			}
			else
				BIS(regset, i);
		}
	for (; i < nregs + MAXTOKENS; i++)
		if (BIT(sp, i))
			error("Set contains %s, which is not a register",
					l_tokens[i - nregs]->tk_name);
}

int membset(int setno, char *name, short *regset, char *appearance, int restyp, int *typp)
{
	register short *sp;
	register token_p tp;
	register int i, j, k;
	int thistyp;
	int typesdiffer = 0;
	int res_j = -1;

	if (setno < 0)
		return 0;
	sp = l_sets[setno].set_val;
	for (i = 1; i < nregs; i++)
		if (BIT(sp, i))
		{
			error("Set in %s contains %s, which is not a token", appearance,
					l_regs[i].ri_name);
			break;
		}
	for (i = 0; i < SZOFSET(MAXREGS); i++)
		regset[i] = 0;
	for (i = nregs; i < nregs + MAXTOKENS; i++)
		if (BIT(sp, i))
		{
			tp = l_tokens[i - nregs];
			for (j = 0;
					j < MAXATT
							&& (tp->tk_att[j].ta_type == -3
									|| strcmp(tp->tk_att[j].ta_name, name));
					j++)
				;
			if (j == MAXATT)
				error("Token %s does not contain %s", tp->tk_name, name);
			else if (j != res_j && res_j != -1)
				typesdiffer = 1;
			else
			{
				res_j = j;
				thistyp = tp->tk_att[j].ta_type;
				if (thistyp == -2)
				{
					if (restyp != TYPADDR && restyp != 0)
						typesdiffer = 1;
					else
						restyp = TYPADDR;
				}
				else if (thistyp == -1)
				{
					if (restyp != TYPINT && restyp != 0)
						typesdiffer = 1;
					else
						restyp = TYPINT;
				}
				else
				{
					if (restyp != TYPREG && restyp != 0)
						typesdiffer = 1;
					else
					{
						restyp = TYPREG;
						for (k = 0; k < SZOFSET(MAXREGS); k++)
							regset[k] |=
									l_props[tp->tk_att[j].ta_type].pr_regset[k];
					}
				}
			}
		}
	if (typesdiffer)
		error("%s is not a valid expression; types differ in the set",
				appearance);
	*typp = restyp == 0 ? TYPINT : restyp;
	return (res_j == -1 ? 0 : res_j);
}

expr_t memb_expr(int setno, char *name, char *appearance, int tokarg)
{
	expr_t result;
	int res_j;

	res_j = membset(setno, name, result.ex_regset, appearance, 0,
			&result.ex_typ);
	result.ex_index = ex_lookup(EX_TOKFIELD, tokarg, res_j + 1);
	return (result);
}

expr_t tokm_expr(int tokarg, char *name)
{
	char app[100];
	int tokarg1 = tokarg > 0 ? tokarg : 1;

	sprintf(app, "%%%d.%s", tokarg1, name);
	return (memb_expr(tokpatset[tokarg1 - 1], name, app, tokarg));
}

expr_t perc_ident_expr(char *name)
{
	char app[100];

	sprintf(app, "%%%s", name);
	return (memb_expr(cursetno, name, app, 0));
}

expr_t all_expr(int all_no, int subreg)
{
	set_t localset;
	register int i;
	register short *sp;
	expr_t result;

	sp = l_props[allreg[all_no]].pr_regset;
	for (i = 0; i < SETSIZE; i++)
		localset.set_val[i] = i < SZOFSET(MAXREGS) ? sp[i] : 0;
	subregset(localset.set_val, subreg, result.ex_regset);
	result.ex_typ = TYPREG;
	result.ex_index = ex_lookup(EX_ALLREG, all_no + 1, subreg);
	return (result);
}

int eq2expr(expr_t e1, expr_t e2)
{

	if (e1.ex_typ != e2.ex_typ)
		error("Expressions around == should have equal type");
	switch (e1.ex_typ)
	{
		default:
			assert(0);
		case TYPBOOL:
			error("== on booleans not implemented");
		case TYPINT:
			return (EX_NCPEQ);
		case TYPADDR:
			return (EX_SCPEQ);
		case TYPREG:
			return (EX_RCPEQ);
	}
}

int ne2expr(expr_t e1, expr_t e2)
{

	if (e1.ex_typ != e2.ex_typ)
		error("Expressions around != should have equal type");
	switch (e1.ex_typ)
	{
		default:
			assert(0);
		case TYPBOOL:
			error("!= on booleans not implemented");
		case TYPINT:
			return (EX_NCPNE);
		case TYPADDR:
			return (EX_SCPNE);
		case TYPREG:
			return (EX_RCPNE);
	}
}

expr_t sum_expr(expr_t e1, expr_t e2)
{
	int operator, op1, op2;
	expr_t result;

	operator = EX_CAT;
	op1 = e1.ex_index;
	op2 = e2.ex_index;
	if (e1.ex_typ == e2.ex_typ)
	{
		result.ex_typ = e1.ex_typ;
		if (e1.ex_typ == TYPINT)
			operator = EX_PLUS;
		else if (e1.ex_typ != TYPADDR)
			error("+ is not implemented on this type");
	}
	else
	{
		result.ex_typ = TYPADDR;
		if (e1.ex_typ != TYPADDR)
		{
			if (e1.ex_typ != TYPINT)
				error("Wrong left operand of +");
			op1 = ex_lookup(EX_TOSTRING, op1, 0);
		}
		if (e2.ex_typ != TYPADDR)
		{
			if (e2.ex_typ != TYPINT)
				error("Wrong right operand of +");
			op2 = ex_lookup(EX_TOSTRING, op2, 0);
		}
	}
	result.ex_index = ex_lookup(operator, op1, op2);
	return (result);
}

expr_t iextoaddr(expr_t e)
{
	expr_t result;

	result.ex_typ = TYPADDR;
	result.ex_index = ex_lookup(EX_TOSTRING, e.ex_index, 0);
	return (result);
}

expr_t regvar_expr(expr_t e, int regtyp, int regsiz)
{
	expr_t result;
	int i, regno, s, two_sizes;

	two_sizes = (regtyp == reg_float && reglap != 0);
	if (regsiz == -1)
	{
		/* reglap: Can't guess between 2 sizes of reg_float. */
		if (two_sizes && regsiz == -1)
			error("Must use regvar_w() or regvar_d()");
		else
			regsiz = rvsize[regtyp];
	}

	result = make_expr(TYPREG, EX_REGVAR, i_expr(e), regsiz);
	for (i = 0; i < SZOFSET(MAXREGS); i++)
		result.ex_regset[i] = 0;

	/* s = the size of the registers in rvnumbers[regtyp] */
	s = two_sizes ? reglap : rvsize[regtyp];
	if (s == regsiz)
	{
		for (i = 0; i < nregvar[regtyp]; i++)
			BIS(result.ex_regset, rvnumbers[regtyp][i]);
	}
	/* reglap: Also check the 2nd size of reg_float. */
	if (two_sizes && rvsize[regtyp] == regsiz)
	{
		for (i = 0; i < nregvar[regtyp]; i++)
		{
			/* regno = first subregister */
			regno = l_regs[rvnumbers[regtyp][i]].ri_memb[0];
			if (regno != 0)
				BIS(result.ex_regset, regno);
		}
	}
	return (result);
}

/*
 * Node table lookup part
 */

node_t nodes[MAXNODES];
int nnodes = 0;

void initnodes(void)
{

	nodes[0].ex_operator = EX_CON;
	nodes[0].ex_lnode = 0;
	nodes[0].ex_rnode = 0;
	nnodes++;
}

int ex_lookup(int operator, int lnode, int rnode)
{
	register node_p p;

	for (p = nodes + 1; p < &nodes[nnodes]; p++)
	{
		if (p->ex_operator != operator)
			continue;
		if (p->ex_lnode != lnode)
			continue;
		if (p->ex_rnode != rnode)
			continue;
		return (p - nodes);
	}
	NEXT(nnodes, MAXNODES, "Node");
	p->ex_operator = operator;
	p->ex_lnode = lnode;
	p->ex_rnode = rnode;
	return (p - nodes);
}