/* 
   Copyright 1994-2003 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.  

   You may contact the author at:

   mailto::camille@bluegrass.net

   or by snail mail at:

   David Lindauer
   850 Washburn Ave Apt 99
   Louisville, KY 40222
*/
#include        <stdio.h>
#include				<string.h>
#include				"lists.h"
#include        "expr.h"
#include        "c.h"
#include        "errors.h"

extern int block_nesting;
extern SYM *currentfunc;
extern TABLE lsyms;

static int namenumber;
static TABLE xsyms;
static SNODE *argstmt,**argnext;
static ENODE *thisn ;

ENODE *inlineexpr(ENODE *node)
{
/*
 * routine takes an enode tree and replaces it with a copy of itself.
 * Used because we have to munge the block_nesting field (value.i) of each
 * sp in an inline function to force allocation of the variables
 */
				ENODE *temp,*temp1;
        if( node == 0 )
                return 0;
				temp = xalloc(sizeof(ENODE));
				memcpy(temp,node,sizeof(ENODE));
				switch (temp->nodetype) {
                case en_autoreg:
                case en_autocon:
											if (!strcmp(temp->v.sp->name,"**THIS**")) {
												temp = thisn ;
											} else {
												/* this should never fail */
    										temp->v.sp = search(node->v.sp->name,&xsyms);
												if (temp->v.sp->funcparm)
    											temp = temp->v.sp->value.classdata.inlinefunc;
											}
											break;
                case en_llcon: case en_llucon:
                case en_rcon: case en_lrcon: case en_fcon:
                case en_rcomplexcon: case en_lrcomplexcon: case en_fcomplexcon:
                case en_rimaginarycon: case en_lrimaginarycon: case en_fimaginarycon:
                case en_icon:
								case en_lcon:
								case en_iucon:
								case en_lucon:
								case en_ccon:
											break ;
                case en_nacon:
								case en_absacon:
                case en_napccon:
											break;
								case en_bits:
								case en_floatref:
								case en_doubleref:
								case en_longdoubleref:
               case en_fimaginaryref:
               case en_rimaginaryref:
               case en_lrimaginaryref:
               case en_fcomplexref:
               case en_rcomplexref:
               case en_lrcomplexref:
                case en_b_ref:
                case en_w_ref:
                case en_ul_ref:
                case en_l_ref:
                case en_ub_ref:
                case en_uw_ref:
                case en_bool_ref:
                case en_ll_ref:
                case en_ull_ref:
												temp1 = temp->v.p[0] = inlineexpr(node->v.p[0]);
					              while (castvalue(temp1))
													temp1 = temp1->v.p[0];
												if (isintconst(temp1->nodetype) || temp1 == thisn)
													temp = temp->v.p[0];
												break;
                case en_uminus: case en_asuminus: case en_ascompl:
                case en_compl:  case en_ainc:
                case en_adec:   case en_not:
                        case en_cf: case en_cd: case en_cld: 
                        case en_cfi: case en_cri: case en_clri: 
                        case en_cfc: case en_crc: case en_clrc: 
                        case en_cll: case en_cull:
                case en_cb: case en_cub: case en_cbool:
								case en_cw: case en_cuw:
								case en_cl: case en_cul: case en_cp:
                case en_trapcall: case en_cl_reg:
												temp->v.p[0] = inlineexpr(node->v.p[0]);
												break;
                case en_asadd:  case en_assub:
                case en_add:    case en_sub: case en_addstruc:
								case en_asalsh: case en_asarsh: case en_alsh: case en_arsh: case en_arshd: case en_asarshd:
                case en_asmul:  case en_asdiv:
                case en_asmod:  case en_aslsh:
								case en_asumod: case en_asudiv: case en_asumul:
                case en_asrsh:  case en_asand:
                case en_assign: case en_refassign: case en_lassign:
								case en_asor:  case en_asxor:
								case en_callblock:
								case en_pcallblock: case en_scallblock:
								case en_void:
								case en_pmul:		case en_pdiv:
                case en_mul:    case en_div:
                case en_umul:    case en_udiv: case en_umod:
                case en_lsh:    case en_rsh:
                case en_mod:    case en_and:
                case en_or:     case en_xor:
                case en_lor:    case en_land:
                case en_eq:     case en_ne:
                case en_gt:     case en_ge:
                case en_lt:     case en_le:
								case en_ugt:	case en_uge: case en_ult: case en_ule:
                case en_cond:  case en_intcall:
								case en_moveblock: case en_stackblock: 
                case en_fcall: case en_fcallb:
								case en_pfcall: case en_pfcallb:
								case en_sfcall: case en_sfcallb:
								case en_thiscall:
								case en_repcons:
												temp->v.p[0] = inlineexpr(node->v.p[0]);
												temp->v.p[1] = inlineexpr(node->v.p[1]);
												break;
        }
	return temp;
}
ENODE *asm_inlinestmt(ENODE *e)
{
	return e;
}
SNODE *inlinestmt(SNODE *block)
/*
 *      scan will gather all optimizable expressions into the expression
 *      list for a block of statements.
 */
{
			 SNODE *out=0,**outptr = &out;
       while( block != 0 ) {
								*outptr = xalloc(sizeof(SNODE));
								memcpy(*outptr,block,sizeof(SNODE));
								(*outptr)->next = NULL;
                switch( block->stype ) {
                        case st_return:
                        case st_expr:
                                (*outptr)->exp = inlineexpr(block->exp);
                                break;
                        case st_while:
                        case st_do:
                                (*outptr)->exp = inlineexpr(block->exp);
                                (*outptr)->s1 = inlinestmt(block->s1);
                                break;
                        case st_for:
                                (*outptr)->label = inlineexpr(block->label);
                                (*outptr)->exp = inlineexpr(block->exp);
                                (*outptr)->s1 = inlinestmt(block->s1);
                                (*outptr)->s2 = inlineexpr(block->s2);
                                break;
                        case st_if:
                                (*outptr)->exp = inlineexpr(block->exp);
                                (*outptr)->s1 = inlinestmt(block->s1);
                                (*outptr)->s2 = inlinestmt(block->s2);
                                break;
                        case st_switch:
                                (*outptr)->exp = inlineexpr(block->exp);
                                (*outptr)->s1 = inlinestmt(block->s1);
                                break;
                        case st_case:
                                (*outptr)->s1 = inlinestmt(block->s1);
                                break;
												case st_block:
																(*outptr)->exp = inlinestmt(block->exp);
																break;
												case st_asm:
																if (block->exp)
																	(*outptr)->exp = asm_inlinestmt(block->exp);
																break;
                        }
                block = block->next;
								outptr = &(*outptr)->next;
                }
	return out;
}
ENODE *inlinefuncargs(ENODE *node,SYM *sp)
{
	ENODE *xnode,*tnode ;
	if (!node)
		return node ;
	xnode = node->v.p[0];
	if (node->v.p[1])
		inlinefuncargs(node->v.p[1],sp->next);

	if (sp->tp->uflags & UF_ALTERED)
		goto dodef;
	tnode = xnode;
	while (castvalue(tnode))
		tnode = tnode->v.p[0];
	if (lvalue(tnode)) {
		switch(tnode->v.p[0]->nodetype) {
			case en_nacon:
			case en_napccon:
			case en_autocon:
			case en_autoreg:
			case en_labcon:
				xnode = tnode->v.p[0];
				break;
			default:
dodef:
				xnode = makenode(en_autocon,sp,0);
				sp->funcparm = FALSE;
				sp->value.i = block_nesting + 1;
				tnode = xnode;
				deref(&tnode,sp->tp);
				tnode = makenode(en_assign,tnode,node->v.p[0]);
				*argnext = xalloc(sizeof(SNODE));
				(*argnext)->stype = st_expr;
				(*argnext)->exp = tnode;
				argnext = &(*argnext)->next;
				break;
		}
	}
	else {
		if (isintconst(tnode->nodetype))
			xnode = node->v.p[0] ;
		else
			goto dodef;
	}
	sp->value.classdata.inlinefunc = xnode;
	return node;
}
void inlinereblock(SYM *sp)
/* Copy all the func args into the xsyms table.
 * This copies the function parameters twice...
 */
{
	SYM *head = sp->tp->lst.head;
	xsyms.head = xsyms.tail = 0;
	if (head != (SYM *) -1)
		while (head) {
			SYM *v = copysym(head) ;
			if (!v->funcparm)
				v->value.i += block_nesting+2;
			insert(v,&xsyms);
			head = head->next ;
		}
	head = sp->value.classdata.inlinefunc->syms.head;
	if (head != (SYM *) -1)
		while (head) {
			if (!search(head->name,&xsyms)) {
				SYM *v = copysym(head) ;
				if (!v->funcparm)
					v->value.i += block_nesting+2;
				insert(v,&xsyms);
			}
			head = head->next ;
		}
}
void inlinerename(TABLE *xsyms)
{
	SYM *head = xsyms->head;
	char buf[100];
	while (head) {
		sprintf(buf,"$TEMPARG%d",namenumber++);
		head->name = litlate(buf);
		head = head->next;
	}
}
ENODE *doinline(ENODE *node)
{
	ENODE *out,*xnode;
	SYM *sp,*newsp;
	argstmt = 0; 
	argnext = &argstmt;
	thisn = 0 ;
	if (node->nodetype == en_thiscall) {
		thisn = node->v.p[0];
		xnode = node->v.p[1];
		if (!xnode)
			return node ;
	} else
		xnode = node;
	if (xnode->nodetype != en_pfcall && xnode->nodetype != en_fcall && 
			xnode->nodetype != en_sfcall)
		return node;
	if (xnode->v.p[1]->v.p[0]->nodetype != en_nacon && xnode->v.p[1]->v.p[0]->nodetype != en_napccon)
		return node ;
	sp = xnode->v.p[1]->v.p[0]->v.sp;
   if (!(sp->value.classdata.cppflags & PF_INLINE) || (sp->value.classdata.cppflags & PF_INSTANTIATED))
		return node;
	inlinereblock(sp);
	newsp = copysym(sp) ;
	out = inlinefuncargs(xnode->v.p[1]->v.p[1]->v.p[0],xsyms.head);
	newsp->value.classdata.inlinefunc = xalloc(sizeof(INLINEFUNC));
	newsp->value.classdata.inlinefunc->stmt = inlinestmt(sp->value.classdata.inlinefunc->stmt);
	out = makenode(en_void,xnode->v.p[1]->v.p[0],0);
	out = makenode(xnode->nodetype,inlineexpr(xnode->v.p[0]),out);
	out->v.p[1]->v.p[0]->v.sp = newsp;
	if (argstmt) {
		*argnext = newsp->value.classdata.inlinefunc->stmt;
		newsp->value.classdata.inlinefunc->stmt = argstmt;
	}
	if (thisn) 
		out = makenode(en_thiscall,thisn,out); 
	inlinerename(&xsyms);
	addblocklist(xsyms.head);
	/* quick check against nested inlines */
	if (currentfunc)
		currentfunc->value.classdata.cppflags &= ~PF_INLINE;
	return out;
}