/*	$Id: local.c,v 1.15 2008/12/14 21:16:58 ragge Exp $	*/
/*
 * 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 "pass1.h"

/*	this file contains code which is dependent on the target machine */

NODE *
clocal(NODE *p)
{
	/* this is called to do local transformations on
	   an expression tree preparitory to its being
	   written out in intermediate code.
	*/

	/* the major essential job is rewriting the
	   automatic variables and arguments in terms of
	   REG and OREG nodes */
	/* conversion ops which are not necessary are also clobbered here */
	/* in addition, any special features (such as rewriting
	   exclusive or) are easily handled here as well */

	struct symtab *q;
	NODE *l, *r;
	int o;
	TWORD ml;

	switch( o = p->n_op ){

	case NAME:
		if ((q = p->n_sp) == NULL)
			return p; /* Nothing to care about */

		switch (q->sclass) {

		case PARAM:
		case AUTO:
			/* fake up a structure reference */
			r = block(REG, NIL, NIL, PTR+STRTY, 0, 0);
			r->n_lval = 0;
			r->n_rval = FPREG;
			p = stref(block(STREF, r, p, 0, 0, 0));
			break;

		case STATIC:
			if (q->slevel == 0)
				break;
			p->n_lval = 0;
			p->n_sp = q;
			break;

		case REGISTER:
			p->n_op = REG;
			p->n_lval = 0;
			p->n_rval = q->soffset;
			break;

			}
		break;

	case PMCONV:
	case PVCONV:
		if( p->n_right->n_op != ICON ) cerror( "bad conversion", 0);
		nfree(p);
		return(buildtree(o==PMCONV?MUL:DIV, p->n_left, p->n_right));
	
	case PCONV:
		ml = p->n_left->n_type;
		l = p->n_left;
		if ((ml == CHAR || ml == UCHAR) && l->n_op != ICON)
			break;
		l->n_type = p->n_type;
		l->n_qual = p->n_qual;
		l->n_df = p->n_df;
		l->n_sue = p->n_sue;
		nfree(p);
		p = l;
		break;

	case SCONV:
		l = p->n_left;
		if (DEUNSIGN(p->n_type) == INT && DEUNSIGN(l->n_type) == INT) {
			nfree(p);
			return l;
		}
		if (l->n_op == ICON) {
			CONSZ val = l->n_lval;
			switch (p->n_type) {
			case CHAR:
				l->n_lval = (char)val;
				break;
			case UCHAR:
				l->n_lval = val & 0377;
				break;
			case SHORT:
			case INT:
				l->n_lval = (short)val;
				break;
			case USHORT:
			case UNSIGNED:
				l->n_lval = val & 0177777;
				break;
			case ULONG:
			case ULONGLONG:
				l->n_lval = val & 0xffffffff;
				break;
			case LONG:
			case LONGLONG:
				l->n_lval = (int)val;
				break;
			case VOID:
				break;
			case LDOUBLE:
			case DOUBLE:
			case FLOAT:
				l->n_op = FCON;
				l->n_dcon = val;
				break;
			default:
				cerror("unknown type %d", p->n_type);
			}
			l->n_type = p->n_type;
			nfree(p);
			return l;
		}
		break;
		

	}

	return(p);
}

/*ARGSUSED*/
int
andable(NODE *p)
{
	return(1);  /* all names can have & taken on them */
}

/*
 * at the end of the arguments of a ftn, set the automatic offset
 */
void
cendarg()
{
	autooff = AUTOINIT;
}

/*
 * is an automatic variable of type t OK for a register variable
 */
int
cisreg(TWORD t)
{
	if (t == INT || t == UNSIGNED || t == CHAR || t == UCHAR ||
		ISPTR(t))
		return(1);
	return 0; /* XXX - fix reg assignment in pftn.c */
}

/*
 * return a node, for structure references, which is suitable for
 * being added to a pointer of type t, in order to be off bits offset
 * into a structure
 * t, d, and s are the type, dimension offset, and sizeoffset
 * For pdp10, return the type-specific index number which calculation
 * is based on its size. For example, short a[3] would return 3.
 * Be careful about only handling first-level pointers, the following
 * indirections must be fullword.
 */
NODE *
offcon(OFFSZ off, TWORD t, union dimfun *d, struct suedef *sue)
{
	register NODE *p;

	if (xdebug)
		printf("offcon: OFFSZ %lld type %x dim %p siz %d\n",
		    off, t, d, sue->suesize);

	p = bcon(0);
	p->n_lval = off/SZCHAR; /* Default */
	return(p);
}

/*
 * Allocate off bits on the stack.  p is a tree that when evaluated
 * is the multiply count for off, t is a NAME node where to write
 * the allocated address.
 */
void
spalloc(NODE *t, NODE *p, OFFSZ off)
{
	NODE *sp;

	if ((off % SZINT) == 0)
		p =  buildtree(MUL, p, bcon(off/SZINT));
	else if ((off % SZSHORT) == 0) {
		p = buildtree(MUL, p, bcon(off/SZSHORT));
		p = buildtree(PLUS, p, bcon(1));
		p = buildtree(RS, p, bcon(1));
	} else if ((off % SZCHAR) == 0) {
		p = buildtree(MUL, p, bcon(off/SZCHAR));
		p = buildtree(PLUS, p, bcon(3));
		p = buildtree(RS, p, bcon(2));
	} else
		cerror("roundsp");

	/* save the address of sp */
	sp = block(REG, NIL, NIL, PTR+INT, t->n_df, t->n_sue);
	sp->n_lval = 0;
	sp->n_rval = STKREG;
	t->n_type = sp->n_type;
	ecomp(buildtree(ASSIGN, t, sp)); /* Emit! */

	/* add the size to sp */
	sp = block(REG, NIL, NIL, p->n_type, 0, 0);
	sp->n_lval = 0;
	sp->n_rval = STKREG;
	ecomp(buildtree(PLUSEQ, sp, p));
}

/*
 * print out a constant node
 * mat be associated with a label
 */
void
ninval(NODE *p)
{
	struct symtab *q;
	TWORD t;

	p = p->n_left;
	t = p->n_type;
	if (t > BTMASK)
		t = INT; /* pointer */

	switch (t) {
	case LONGLONG:
	case ULONGLONG:
		inval(p->n_lval & 0xffffffff);
		inval(p->n_lval >> 32);
		break;
	case LONG:
	case ULONG:
	case INT:
	case UNSIGNED:
		printf("\t.long 0x%x", (int)p->n_lval);
		if ((q = p->n_sp) != NULL) {
			if ((q->sclass == STATIC && q->slevel > 0)) {
				printf("+" LABFMT, q->soffset);
			} else
				printf("+%s", exname(q->soname));
		}
		printf("\n");
		break;
	default:
		fwalk(p, eprint, 0);
		cerror("ninval");
	}
}

/*
 * print out an integer.
 */
void
inval(CONSZ word)
{
	word &= 0xffffffff;
	printf("	.long 0x%llx\n", word);
}

/* output code to initialize a floating point value */
/* the proper alignment has been obtained */
void
finval(NODE *p)
{
	switch (p->n_type) {
	case LDOUBLE:
		printf("\t.tfloat\t0t%.20Le\n", p->n_dcon);
		break;
	case DOUBLE:
		printf("\t.dfloat\t0d%.20e\n", (double)p->n_dcon);
		break;
	case FLOAT:
		printf("\t.ffloat\t0f%.20e\n", (float)p->n_dcon);
		break;
	}
}

/* make a name look like an external name in the local machine */
char *
exname(char *p)
{
	if (p == NULL)
		return "";
	return p;
}

/*
 * map types which are not defined on the local machine
 */
TWORD
ctype(TWORD type)
{
	switch (BTYPE(type)) {
	case SHORT:
		MODTYPE(type,INT);
		break;

	case USHORT:
		MODTYPE(type,UNSIGNED);
		break;

	case LONGLONG:
		MODTYPE(type,LONG);
		break;

	case ULONGLONG:
		MODTYPE(type,ULONG);
		break;

	case LDOUBLE:
		MODTYPE(type,DOUBLE);
		break;
	}
	return (type);
}

/* curid is a variable which is defined but
 * is not initialized (and not a function );
 * This routine returns the storage class for an uninitialized declaration
 */
int
noinit()
{
	return(EXTERN);
}

/*
 * Extern variable not necessary common.
 */
void
extdec(struct symtab *q)
{
	extern void addsym(struct symtab *);
	addsym(q);
}

/*
 * Call to a function
 */
void
calldec(NODE *p, NODE *r)
{
	struct symtab *q = p->n_sp;
	extern void addsym(struct symtab *);
	addsym(q);
}

/* make a common declaration for id, if reasonable */
void
commdec(struct symtab *q)
{
	int off;
	char *c = q->soname;

	off = tsize(q->stype, q->sdf, q->ssue);
	off = (off+(SZCHAR-1))/SZCHAR;

	printf("	PUBLIC %s\n", c);
	/* XXX - NOROOT??? */
	printf("	RSEG DATA16_Z:NEARDATA:SORT:NOROOT(1)\n");
	printf("%s:\n", c);
	printf("	DS8 %d\n", off);
	printf("	REQUIRE __data16_zero\n");
}

/* make a local common declaration for id, if reasonable */
void
lcommdec(struct symtab *q)
{
	int off;

	off = tsize(q->stype, q->sdf, q->ssue);
	off = (off+(SZCHAR-1))/SZCHAR;
	if (q->slevel == 0)
		printf("	.lcomm %s,0%o\n", exname(q->soname), off);
	else
		printf("	.lcomm " LABFMT ",0%o\n", q->soffset, off);
}

/*
 * print a (non-prog) label.
 */
void
deflab1(int label)
{
	printf(LABFMT ":\n", label);
}

void
setloc1(int locc)
{
	if (locc == lastloc)
		return;
	lastloc = locc;
}

/*
 * special handling before tree is written out.
 */
void
myp2tree(NODE *p)
{
	struct symtab *sp;
	union dimfun *df;
	union arglist *al;
	NODE *q;
	int i;

	switch (p->n_op) {
	case MOD:
	case DIV:
		if (p->n_type == LONG || p->n_type == ULONG) {
			/* Swap arguments for hardops() later */
			q = p->n_left;
			p->n_left = p->n_right;
			p->n_right = q;
		}
		break;

	case CALL:
	case STCALL:
		/*
		 * inform pass2 about varargs.
		 * store first variadic argument number in n_stalign
		 * in the CM node.
		 */
		if (p->n_right->n_op != CM)
			break; /* nothing to care about */
		df = p->n_left->n_df;
		if (df && (al = df->dfun)) {
			for (i = 0; i < 6; i++, al++) {
				if (al->type == TELLIPSIS || al->type == TNULL)
					break;
			}
			p->n_right->n_stalign = al->type == TELLIPSIS ? i : 0;
		} else
			p->n_right->n_stalign = 0;
		break;

	case FCON:
		/* Write float constants to memory */
		sp = inlalloc(sizeof(struct symtab));
		sp->sclass = STATIC;
		sp->ssue = MKSUE(p->n_type);
		sp->slevel = 1; /* fake numeric label */
		sp->soffset = getlab();
		sp->sflags = 0;
		sp->stype = p->n_type;
		sp->squal = (CON >> TSHIFT);

		defloc(sp);
		ninval(0, sp->ssue->suesize, p);

		p->n_op = NAME;
		p->n_lval = 0;
		p->n_sp = sp;
		break;
	}

}
/*
 * Give target the opportunity of handling pragmas.
 */
int
mypragma(char **ary)
{
	return 0; }

/*
 * Called when a identifier has been declared, to give target last word.
 */
void
fixdef(struct symtab *sp)
{
}

void
pass1_lastchance(struct interpass *ip)
{
}
