/*	$Id$	*/
/*
 * Copyright (c) 2003 Anders Magnusson (ragge@ludd.luth.se).
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


# include "pass2.h"
# include <strings.h>

int canaddr(NODE *);

/*
 * should the assignment op p be stored,
 * given that it lies as the right operand of o
 * (or the left, if o==UNARY MUL)
 */
/*
void
stoasg(NODE *p, int o)
{
	if (x2debug)
		printf("stoasg(%p, %o)\n", p, o);
}
*/
/* should we delay the INCR or DECR operation p */
int
deltest(NODE *p)
{
	return 0;
}

/*
 * Check if p can be autoincremented.
 * XXX - nothing can be autoincremented for now.
 */
int
autoincr(NODE *p)
{
	return 0;
}

/* is it legal to make an OREG or NAME entry which has an
 * offset of off, (from a register of r), if the
 * resulting thing had type t */
int
notoff(TWORD t, int r, CONSZ off, char *cp)
{
	return(0);  /* YES */
}

/*
 * Turn a UMUL-referenced node into OREG.
 */
int
offstar(NODE *p, int shape)
{
	if (x2debug)
		printf("offstar(%p)\n", p);

	if( p->n_op == PLUS || p->n_op == MINUS ){
		if( p->n_right->n_op == ICON ){
			geninsn(p->n_left, INBREG);
			p->n_su = -1;
			return 1;
		}
	}
	geninsn(p, INBREG);
	return 0;
}

/*
 * Shape matches for UMUL.  Cooperates with offstar().
 */
int
shumul(NODE *p, int shape)
{
//	NODE *l = p->n_left;

#ifdef PCC_DEBUG
	if (x2debug) {
		printf("shumul(%p)\n", p);
		fwalk(p, e2print, 0);
	}
#endif
	/* XXX - fix */

	/* Can only generate OREG of BREGs (or FB) */
	if (p->n_op == REG && (isbreg(p->n_rval) || p->n_rval == FB))
		return SROREG;
#if 0
	if ((p->n_op == PLUS || p->n_op == MINUS) &&
	    (l->n_op == REG && (isbreg(l->n_rval) || l->n_rval == FB)) &&
	    p->n_right->n_op == ICON)
		return SOREG;
	return 0;
#else
	return SROREG;
#endif
}

/*
 * Rewrite increment/decrement operation.
 */
int
setincr(NODE *p)
{
	if (x2debug)
		printf("setincr(%p)\n", p);

	return(0);
}

/*
 * Rewrite operations on binary operators (like +, -, etc...).
 * Called as a result of table lookup.
 */
int
setbin(NODE *p)
{

	if (x2debug)
		printf("setbin(%p)\n", p);
	return 0;

}

/* setup for assignment operator */
int
setasg(NODE *p, int cookie)
{
	if (x2debug)
		printf("setasg(%p)\n", p);
	return(0);
}

/* setup for unary operator */
int
setuni(NODE *p, int cookie)
{
	return 0;
}

#if 0
/*
 * register allocation for instructions with special preferences.
 */
regcode
regalloc(NODE *p, struct optab *q, int wantreg)
{
	regcode regc;

	if (q->op == DIV || q->op == MOD) {
		/*
		 * 16-bit div.
		 */
		if (regblk[R0] & 1 || regblk[R2] & 1)
			comperr("regalloc: needed regs inuse, node %p", p);
		if (p->n_su & DORIGHT) {
			regc = alloregs(p->n_right, A0);
			if (REGNUM(regc) != A0) {
				p->n_right = movenode(p->n_right, A0);
				if ((p->n_su & RMASK) == ROREG) {
					p->n_su &= ~RMASK;
					p->n_su |= RREG;
					p->n_right->n_su &= ~LMASK;
					p->n_right->n_su |= LOREG;
				}
				freeregs(regc);
				regblk[A0] |= 1;
			}
		}
		regc = alloregs(p->n_left, R0);
		if (REGNUM(regc) != R0) {
			p->n_left = movenode(p->n_left, R0);
			freeregs(regc);
			regblk[R0] |= 1;
		}
		if ((p->n_su & RMASK) && !(p->n_su & DORIGHT)) {
			regc = alloregs(p->n_right, A0);
			if (REGNUM(regc) != A0) {
				p->n_right = movenode(p->n_right, A0);
				if ((p->n_su & RMASK) == ROREG) {
					p->n_su &= ~RMASK;
					p->n_su |= RREG;
					p->n_right->n_su &= ~LMASK;
					p->n_right->n_su |= LOREG;
				}
			}
		}
		regblk[A0] &= ~1;
		regblk[R0] &= ~1;
		regblk[R2] &= ~1;
		if (q->op == DIV) {
			MKREGC(regc, R0, 1);
			regblk[R0] |= 1;
		} else {
			MKREGC(regc, R2, 1);
			regblk[R2] |= 1;
		}
	} else
		comperr("regalloc");
	p->n_rall = REGNUM(regc);
	return regc;
}
#endif

/*
 * Special handling of some instruction register allocation.
 * - left is the register that left node wants.
 * - right is the register that right node wants.
 * - res is in which register the result will end up.
 * - mask is registers that will be clobbered.
 *
 *  XXX - Fix this function
 */
struct rspecial *
nspecial(struct optab *q)
{
    switch (q->op) {

    case DIV:
    case MOD:
	if(q->ltype & (TINT|TSHORT)){
	    static struct rspecial s[] = {
		{ NRES, R0 }, { NRES, R2}, { 0 } };
	    return s;
	}
	/*
	else if(q->ltype & TCHAR) {
	    static struct rspecial s[] = {
		{ NRES, R0L }, { NRES, R0H}, { 0 } };
	    return s;
	    }*/
	break;

    case MUL:
	/*
	if(q->ltype & (TINT|TSHORT)){
	    static struct rspecial s[] = {
		{ NRES, R0 }, { NRES, R2}, { 0 } };
	    return s;
	    }*/
	comperr("multiplication not implemented");
	break;
	
    default:
	break;
    }
    comperr("nspecial entry %d", q - table);
    return 0; /* XXX gcc */
}


/*
 * Splitup a function call and give away its arguments first.
 * Calling convention used ("normal" in IAR syntax) is:
 * - 1-byte parameters in R0L if possible, otherwise in R0H.
 * - 2-byte pointers in A0.
 * - 2-byte non-pointers in R0 if no byte-size arguments are found in
 *   in the first 6 bytes of parameters, otherwise R2 or at last A0.
 * - 4-byte parameters in R2R0.
 */
void
gencall(NODE *p, NODE *prev)
{
	NODE *n = 0; /* XXX gcc */
	static int storearg(NODE *);
	int o = p->n_op;
	int ty = optype(o);

	if (ty == LTYPE)
		return;

	switch (o) {
	case CALL:
		/* swap arguments on some hardop-converted insns */
		/* Normal call, just push args and be done with it */
		p->n_op = UCALL;
//printf("call\n");
		/* Check if left can be evaluated directly */
		if (p->n_left->n_op == UMUL) {
			TWORD t = p->n_left->n_type;
			int k = (freetemp(szty(t)));
			NODE *n = mklnode(OREG, k, FB, t);
			NODE *q = tcopy(n);
			pass2_compile(ipnode(mkbinode(ASSIGN, n, p->n_left,t)));
			p->n_left = q;
		}
		gencall(p->n_left, p);
		p->n_rval = storearg(p->n_right);
//printf("end call\n");
		break;

	case UFORTCALL:
	case FORTCALL:
		comperr("FORTCALL");

	case USTCALL:
	case STCALL:
		/*
		 * Structure return.  Look at the node above
		 * to decide about buffer address:
		 * - FUNARG, allocate space on stack, don't remove.
		 * - nothing, allocate space on stack and remove.
		 * - STASG, get the address of the left side as arg.
		 * - FORCE, this ends up in a return, get supplied addr.
		 * (this is not pretty, but what to do?)
		 */
		if (prev == NULL || prev->n_op == FUNARG) {
			/* Create nodes to generate stack space */
			n = mkbinode(ASSIGN, mklnode(REG, 0, STKREG, INT),
			    mkbinode(MINUS, mklnode(REG, 0, STKREG, INT),
			    mklnode(ICON, p->n_stsize, 0, INT), INT), INT);
//printf("stsize %d\n", p->n_stsize);
			pass2_compile(ipnode(n));
		} else if (prev->n_op == STASG) {
			n = prev->n_left;
			if (n->n_op == UMUL)
				n = nfree(n);
			else if (n->n_op == NAME) {
				n->n_op = ICON; /* Constant reference */
				n->n_type = INCREF(n->n_type);
			} else
				comperr("gencall stasg");
		} else if (prev->n_op == FORCE) {
			; /* do nothing here */
		} else {
			comperr("gencall bad op %d", prev->n_op);
		}

		/* Deal with standard arguments */
		gencall(p->n_left, p);
		if (o == STCALL) {
			p->n_op = USTCALL;
			p->n_rval = storearg(p->n_right);
		} else
			p->n_rval = 0;
		/* push return struct address */
		if (prev == NULL || prev->n_op == FUNARG) {
			n = mklnode(REG, 0, STKREG, INT);
			if (p->n_rval)
				n = mkbinode(PLUS, n,
				    mklnode(ICON, p->n_rval, 0, INT), INT);
			pass2_compile(ipnode(mkunode(FUNARG, n, 0, INT)));
			if (prev == NULL)
				p->n_rval += p->n_stsize/4;
		} else if (prev->n_op == FORCE) {
			/* return value for this function */
			n = mklnode(OREG, 8, FPREG, INT);
			pass2_compile(ipnode(mkunode(FUNARG, n, 0, INT)));
			p->n_rval++;
		} else {
			pass2_compile(ipnode(mkunode(FUNARG, n, 0, INT)));
			n = p;
			*prev = *p;
			nfree(n);
		}
//printf("end stcall\n");
		break;

	default:
		if (ty != UTYPE)
			gencall(p->n_right, p);
		gencall(p->n_left, p);
		break;
	}
}

/*
 * Create separate node trees for function arguments.
 * This is partly ticky, the strange calling convention 
 * may cause a bunch of code reorganization here.
 */
static int
storearg(NODE *p)
{
	NODE *n, *q, **narry;
	int nch, k, i, nn, rary[4];
	int r0l, r0h, r2, a0, stk, sz;
	TWORD t;
	int maxrargs = 0;

	if (p->n_op == CM)
		maxrargs = p->n_stalign;

	/* count the arguments */
	for (i = 1, q = p; q->n_op == CM; q = q->n_left)
		i++;
	nn = i;

	/* allocate array to store arguments */
	narry = tmpalloc(sizeof(NODE *)*nn);

	/* enter nodes into array */
	for (q = p; q->n_op == CM; q = q->n_left)
		narry[--i] = q->n_right;
	narry[--i] = q;

	/* free CM nodes */
	for (q = p; q->n_op == CM; ) {
		n = q->n_left;
		nfree(q);
		q = n;
	}

	/* count char args */
	r0l = r0h = r2 = a0 = 0;
	for (sz = nch = i = 0; i < nn && i < 6; i++) {
		TWORD t = narry[i]->n_type;
		if (sz >= 6)
			break;
		if (t == CHAR || t == UCHAR) {
			nch++;
			sz++;
		} else if ((t >= SHORT && t <= UNSIGNED) ||
		    t > BTMASK || t == FLOAT) {
			sz += 2;
		} else /* long, double */
			sz += 4;
			
	}

	/*
	 * Now the tricky part. The parameters that should be on stack
	 * must be found and pushed first, then the register parameters.
	 * For the latter, be sure that evaluating them do not use any
	 * registers where argument values already are inserted.
	 * XXX - function pointers?
	 * XXX foo(long a, char b) ???
	 */
	for (stk = 0; stk < 4; stk++) {
		TWORD t;

		if (stk == nn)
			break;
		t = narry[stk]->n_type;
		if (ISFTN(DECREF(t)))
			t = LONG;
		switch (t) {
		case CHAR: case UCHAR:
			if (r0l) {
				if (r0h)
					break;
				rary[stk] = R2; /* char talk for 'R0H' */
				r0h = 1;
			} else {
				rary[stk] = R0;
				r0l = 1;
			}
			continue;

		case INT: case UNSIGNED:
			if (r0l || nch) {
				if (r2) {
					if (a0)
						break;
					rary[stk] = A0;
					a0 = 1;
				} else {
					rary[stk] = R2;
					r2 = 1;
				}
			} else {
				rary[stk] = R0;
				r0l = r0h = 1;
			}
			continue;

		case LONG: case ULONG:
			if (r0l || r2)
				break;
			rary[stk] = R0;
			r0l = r0h = r2 = 1;
			continue;

		default:
			if (ISPTR(narry[stk]->n_type) &&
			    !ISFTN(DECREF(narry[stk]->n_type))) {
				if (a0) {
					if (r0l || nch) {
						if (r2)
							break;
						rary[stk] = R2;
						r2 = 1;
					} else {
						rary[stk] = R0;
						r0l = r0h = 1;
					}
				} else {
					rary[stk] = A0;
					a0 = 1;
				}
				continue;
			}
			break;
		}
		break;
	}

	/*
	 * The arguments that must be on stack are stk->nn args.
	 * Argument 0->stk-1 should be put in the rary[] register.
	 */
	for (sz = 0, i = nn-1; i >= stk; i--) { /* first stack args */
		NODE nod;
		pass2_compile(ipnode(mkunode(FUNARG,
		    narry[i], 0, narry[i]->n_type)));
		nod.n_type = narry[i]->n_type;
		sz += tlen(&nod);
	}
	/* if param cannot be addressed directly, evaluate and put on stack */
	for (i = 0; i < stk; i++) {

		if (canaddr(narry[i]))
			continue;
		t = narry[i]->n_type;
		k = (freetemp(szty(t)));
		n = mklnode(OREG, k, FB, t);
		q = tcopy(n);
		pass2_compile(ipnode(mkbinode(ASSIGN, n, narry[i], t)));
		narry[i] = q;
	}
	/* move args to registers */
	for (i = 0; i < stk; i++) {
		t = narry[i]->n_type;
		pass2_compile(ipnode(mkbinode(ASSIGN, 
		    mklnode(REG, 0, rary[i], t), narry[i], t)));
	}
	return sz;
}

/*
 * Tell if a register can hold a specific datatype.
 */
#if 0
int
mayuse(int reg, TWORD type)
{
	return 1;  /* Everything is OK */
}
#endif

#ifdef TAILCALL
void
mktailopt(struct interpass *ip1, struct interpass *ip2)
{
	extern int earlylab;
	extern char *cftname;
	char *fn;
	NODE *p;

	p = ip1->ip_node->n_left->n_left;
	if (p->n_op == ICON) {
		fn = p->n_name;
		/* calling ourselves */
		p = ip1->ip_node->n_left;
		if (p->n_op == CALL) {
			if (storearg(p->n_right))
				comperr("too many args: fix mktailopt");
			p->n_op = UCALL;
		}
		tfree(ip1->ip_node);
		p = ip2->ip_node->n_left;
		if (strcmp(fn, cftname)) {
			/* Not us, must generate fake prologue */
			ip1->type = IP_ASM;
			ip1->ip_asm = "\tmov.w FB,SP\n\tpop.w FB\n";
			pass2_compile(ip1);
			p->n_lval = p->n_rval = 0;
			p->n_name = fn;
		} else
			p->n_lval = earlylab;
	} else {
		pass2_compile(ip1);
	}
	pass2_compile(ip2);
}
#endif
/*
 * Set registers "live" at function calls (like arguments in registers).
 * This is for liveness analysis of registers.
 */
int *
livecall(NODE *p)
{
	static int r[1] = { -1 }; /* Terminate with -1 */

	return &r[0];
}

/*
 * Signal whether the instruction is acceptable for this target.
 */
int
acceptable(struct optab *op)
{
	return 1;
}
