/* 
   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        "gen386.h"
#include 				"diag.h"

extern SNODE *funcstmt, *funcendstmt ;
extern ENODE *thisenode ;
extern SYM *thissp;
extern TABLE *gsyms;
extern int stdaddrsize;
extern int global_flag;
extern int fsave_mask, save_mask;
extern TYP              stdfunc;
extern struct amode     push[], pop[];
extern OCODE *peep_tail, *peep_head, *peep_insert;
extern SYM *currentfunc;
extern int regs[3];
extern int prm_cplusplus,prm_profiler;
extern int firstlabel, nextlabel;
extern long lc_maxauto;
extern ENODE *block_rundown;
extern SYM *declclass;
extern int funcfloat;
extern int prm_smartframes ;
extern OCODE *frame_ins ;

int     retlab,startlab;

LIST *mpthunklist;

static int returndreg ;
static int     breaklab;
static int     contlab;
static int diddef;
static int gswitchcount ;
static LLONG_TYPE gswitchbottom ;
static LLONG_TYPE gswitchtop;
static long plabel;
static int retsize;

AMODE    *makedreg(int r)
/*
 *      make an address reference to a data register.
 */
{       AMODE    *ap;
        ap = xalloc(sizeof(AMODE));
        ap->mode = am_dreg;
        ap->preg = r;
				ap->length = 4;
        return ap;
}

AMODE    *makefreg(int r)
/*
 *      make an address reference to a data register.
 */
{       AMODE    *ap;
        ap = xalloc(sizeof(AMODE));
        ap->mode = am_freg;
        ap->preg = r;
        return ap;
}

AMODE    *make_direct(int i)
/*
 *      make a direct reference to an immediate value.
 */
{       return make_offset(makeintnode(en_icon,i));
}

AMODE    *make_strlab(char *s)
/*
 *      generate a direct reference to a string label.
 */
{       AMODE    *ap;
        ap = xalloc(sizeof(AMODE));
        ap->mode = am_direct;
        ap->offset = makenode(en_nacon,s,0);
        return ap;
}

void genwhile(SNODE *stmt)
/*
 *      generate code to evaluate a while statement.
 */
{       int     lab1, lab2, lab3;
        initstack();            /* initialize temp registers */
        lab1 = contlab;         /* save old continue label */
        contlab = nextlabel++;  /* new continue label */
        if( stmt->s1 != 0 )      /* has block */
                {
        				lab2 = breaklab;        /* save old break label */
                breaklab = nextlabel++;
								gen_codes(op_jmp,0,make_label(contlab),0);
								lab3 = nextlabel++;
								gen_label(lab3);
                genstmt(stmt->s1);
								gen_label(contlab);
								if (stmt->lst)
		 							gen_line(stmt->lst);
                initstack();
                truejp(stmt->exp,lab3);
                gen_label(breaklab);
                breaklab = lab2;        /* restore old break label */
                }
        else					        /* no loop code */
                {
								if (stmt->lst)
		 							gen_line(stmt->lst);
        				gen_label(contlab);
                initstack();
                truejp(stmt->exp,contlab);
                }
        contlab = lab1;         /* restore old continue label */
}

void gen_for(SNODE *stmt)
/*
 *      generate code to evaluate a for loop
 */
{       int     old_break, old_cont, exit_label, loop_label, start_label;
        old_break = breaklab;
        old_cont = contlab;
        loop_label = nextlabel++;
        contlab = nextlabel++;
        start_label = nextlabel++;
        exit_label = nextlabel++;
        initstack();
        if( stmt->label != 0 ) {
               	gen_void(stmt->label);
				}
				
        gen_codes(op_jmp,0,make_label(start_label),0);
        gen_label(loop_label);
        if( stmt->s1 != 0 ) {
								breaklab = exit_label;
                genstmt(stmt->s1);
				}
				if (stmt->lst)
		 			gen_line(stmt->lst);
				gen_label(contlab);
        initstack();
        if( stmt->s2 != 0 ) {
                gen_void(stmt->s2);
				}
				gen_label(start_label);
        initstack();
        if( stmt->exp != 0 )
                truejp(stmt->exp,loop_label);
				else
						gen_codes(op_jmp,0,make_label(loop_label),0);
				gen_label(exit_label);
 				breaklab = old_break;
				contlab = old_cont;
}

void genif(SNODE *stmt)
/*
 *      generate code to evaluate an if statement.
 */
{       int     lab1, lab2;
        lab1 = nextlabel++;     /* else label */
        lab2 = nextlabel++;     /* exit label */
        initstack();            /* clear temps */
        falsejp(stmt->exp,lab1);
        genstmt(stmt->s1);
        if( stmt->s2 != 0 )             /* else part exists */
                {
                gen_codes(op_jmp,0,make_label(lab2),0);
                gen_label(lab1);
                genstmt(stmt->s2);
                gen_label(lab2);
                }
        else                            /* no else code */
                gen_label(lab1);
}

void gendo(SNODE *stmt)
/*
 *      generate code for a do - while loop.
 */
{       int     oldcont, oldbreak, looplab;
        oldcont = contlab;
        oldbreak = breaklab;
        looplab = nextlabel++;
        contlab = nextlabel++;
        breaklab = nextlabel++;
        gen_label(looplab);
        genstmt(stmt->s1);      /* generate body */
        gen_label(contlab);
        initstack();
        truejp(stmt->exp,looplab);
        gen_label(breaklab);
        breaklab = oldbreak;
        contlab = oldcont;
}

AMODE *set_symbol(char *name,int flag)
{
	SYM *sp;
	AMODE *ap;
        sp = gsearch(name);
        if( sp == 0 )
                {
                ++global_flag;
                sp = xalloc(sizeof(SYM));
                sp->tp = &stdfunc;
                sp->name = name;
								if (flag)
									sp->storage_class = sc_externalfunc;
								else
                	sp->storage_class = sc_external;
								sp->extflag = 1;
                insert(sp,gsyms);
                --global_flag;
                }
				ap = xalloc(sizeof(AMODE));
				ap->mode = am_immed;
				ap->length = 4;
				ap->offset = makenode(en_nacon,sp,0);
	return ap;
}
void call_library(char *lib_name)
/*
 *      generate a call to a library routine.
 */
{
				AMODE *ap;
				ap = set_symbol(lib_name,1);
        gen_codes(op_call,0,ap,0);
}

int analyzeswitch(SNODE *stmt)
{
   int size = natural_size(stmt->exp) ;
	gswitchbottom = 0x7fffffff;
	gswitchtop = 0x80000000;
	gswitchcount = 0;
	stmt = stmt->s1;
	while (stmt) {
		if (!stmt->s2) {
			gswitchcount++;
			if ((long)stmt->label < gswitchbottom)
				gswitchbottom = (int)stmt->label;
			if ((long)stmt->label > gswitchtop)
				gswitchtop = (int)stmt->label;
		}
		stmt = stmt->next;
	}
	gswitchtop++;
	if (gswitchcount == 0)
		return(0);
	if (gswitchcount < 5)
		return 3;
	if (gswitchcount*10/(gswitchtop-gswitchbottom) >= 8)
		return(1);
   // do a simple switch instead of a binary if it is a long long
   if (size == 6 || size == -6)
      return 3 ;
	return(2);
}
void bingen(int lower, int avg, int higher,AMODE *ap1, int deflab, long * switchids,int * switchlabels,int *switchbinlabels)
{
	AMODE *ap2 = make_immed(switchids[avg]);
	AMODE *ap3 = make_label(switchlabels[avg]);
	if (switchbinlabels[avg] != -1)
		gen_label(switchbinlabels[avg]);
	gen_coden(op_cmp,ap1->length,ap1,ap2);
	gen_code(op_je,ap3,0);
	if (avg == lower) {
		ap3 = make_label(deflab);
		gen_code(op_jmp,ap3,0);
	}
	else {
		int avg1 = (lower + avg)/2;
		int avg2 = (higher + avg+1)/2;
		if (avg+1 < higher) 
			ap3 = make_label(switchbinlabels[avg2]=nextlabel++);
		else
			ap3 = make_label(deflab);
		if (ap1->length < 0)
			gen_code(op_jg,ap3,0);
		else
			gen_code(op_ja,ap3,0);
		bingen(lower,avg1,avg,ap1,deflab,switchids,switchlabels,switchbinlabels);
		if (avg+1 < higher)
			bingen(avg+1,avg2,higher,ap1,deflab,switchids,switchlabels,switchbinlabels);
	}
}
void genbinaryswitch(SNODE *stmt, int deflab)
{
	int curlab, i=0,j;
	AMODE *ap1;
  long switchbottom=gswitchbottom, switchcount=gswitchcount;
  long switchtop=gswitchtop;
  int *switchlabels=0;
  long *switchids=0;
  int *switchbinlabels=0;
	curlab = nextlabel++;
  initstack();
  ap1 = gen_expr(stmt->exp,FALSE,FALSE,4);
	do_extend(ap1,ap1->length,F_DREG);
	global_flag++;
		switchlabels = xalloc((switchcount) * sizeof(int));
		switchbinlabels = xalloc((switchcount) * sizeof(int));
		switchids = xalloc((switchcount) * sizeof(long));
	global_flag--;
	stmt = stmt->s1;
	while (stmt) {
                if( stmt->s2 )          /* default case ? */
                        {
                        stmt->label = (SNODE *) deflab;
												diddef = TRUE;
                        }
                else
                        {
												switchlabels[i] = curlab;
												switchbinlabels[i] = -1;
												switchids[i++] = (int)stmt->label;
												stmt->label = (SNODE *)curlab;
                        }
                if( stmt->next != 0 )
                        curlab = nextlabel++;
                stmt = stmt->next;
                }
	for (i=0; i < switchcount; i++)
		for (j=i+1; j < switchcount; j++)
			if (switchids[j] < switchids[i]) {
				int temp = switchids[i];
				switchids[i] = switchids[j];
				switchids[j] = temp;
				temp = switchlabels[i];
				switchlabels[i] = switchlabels[j];
				switchlabels[j] = temp;
			}
	bingen(0,(switchcount)/2,switchcount,ap1,deflab,switchids,switchlabels,switchbinlabels);
	freeop(ap1);
}
void gencompactswitch(SNODE *stmt, int deflab)
{       int             tablab,curlab,size;
        AMODE    *ap,*ap2;
        LLONG_TYPE i ;
  LLONG_TYPE switchbottom=gswitchbottom;
  int  switchcount=gswitchcount;
  LLONG_TYPE switchtop=gswitchtop;
  int *switchlabels=0;
				tablab = nextlabel++;
        curlab = nextlabel++;
        initstack();
        ap = gen_expr(stmt->exp,FALSE,FALSE,4);
				size = ap->length;
            if (size == 6 || size == -6)
               do_extend(ap,6,F_AXDX);
            else
               do_extend(ap,4,F_DREG | F_VOL);
				initstack();
            if (size == 6  || size == -6) {
               if (switchbottom) {
                  gen_codes(op_sub,4,makedreg(EAX),make_immed(switchbottom));
#if sizeof(LLONG_TYPE) == 4
                  gen_codes(op_sbb,4,makedreg(EDX), make_immed(switchbottom < 0 ? -1 : 0)) ;
#else
                  gen_codes(op_sbb,4,makedreg(EDX), make_immed(switchbottom >> 32)) ;
#endif
                  if (size < 0)
                     gen_codes(op_jl,0,make_label(deflab),0);
                  else {
                     peep_tail->noopt = TRUE;
                     gen_codes(op_jb,0,make_label(deflab),0);
                  }
               }
               else
                  if (size < 0) {
                    gen_codes(op_test,4,makedreg(EDX),makedreg(EDX));
                     gen_codes(op_jl,0,make_label(deflab),0);
                  }
               gen_codes(op_cmp,4,makedreg(EAX),make_immed(switchtop-switchbottom));
            } else {
               if (switchbottom) {
                  gen_codes(op_sub,4,ap,make_immed(switchbottom));
                  if (size < 0)
                     gen_codes(op_jl,0,make_label(deflab),0);
                  else {
                     peep_tail->noopt = TRUE;
                     gen_codes(op_jb,0,make_label(deflab),0);
                  }
               }
               else
                  if (size < 0) {
                    gen_codes(op_test,4,ap,ap);
                     gen_codes(op_jl,0,make_label(deflab),0);
                  }
               gen_codes(op_cmp,4,ap,make_immed(switchtop-switchbottom));
            }
            if (size < 0)
               gen_codes(op_jge,0,make_label(deflab),0);
            else
               gen_codes(op_jnc,0,make_label(deflab),0);
				ap->mode = am_indispscale;
				ap->sreg = ap->preg;
				ap->preg = -1;
				ap->scale = 2;
            ap->offset = makeintnode(en_labcon,tablab);
				gen_codes(op_jmp,4,ap,0);

				initstack();
				align(4);
				gen_label(tablab);
		switchlabels = xalloc((switchtop-switchbottom) * sizeof(int));
   for (i=switchbottom; i < switchtop; i++)
		switchlabels[i-switchbottom] = deflab;

	stmt = stmt->s1;
	while (stmt) {
                if( stmt->s2 )          /* default case ? */
                        {
                        stmt->label = (SNODE *)deflab;
												diddef = TRUE;
                        }
                else
                        {
												switchlabels[(int)stmt->label-switchbottom] = curlab;
												stmt->label = (SNODE *)curlab;
                        }
                if(stmt->next != 0 )
                        curlab = nextlabel++;
                stmt = stmt->next;
                }
   for (i=0; i < switchtop-switchbottom; i++)
		gen_codes(op_dd,4,make_label(switchlabels[i]),0);
}
void gensimpleswitch(SNODE *stmt, int deflab)
{
	int curlab, i=0,j;
   AMODE *ap1, *ap2, *ap3, *ap4;
   int size = natural_size(stmt->exp) ;
  LLONG_TYPE switchbottom=gswitchbottom;
  int switchcount=gswitchcount;
  LLONG_TYPE switchtop=gswitchtop;
  int *switchlabels=0;
  LLONG_TYPE *switchids=0;
  int *switchbinlabels=0;
	curlab = nextlabel++;
  initstack();
  ap1 = gen_expr(stmt->exp,FALSE,FALSE,4);
  if (size == 6 || size == -6)
   do_extend(ap1,ap1->length,F_AXDX);
  else
   do_extend(ap1,ap1->length,F_DREG);
	global_flag++;
		switchlabels = xalloc((switchcount) * sizeof(int));
		switchbinlabels = xalloc((switchcount) * sizeof(int));
      switchids = xalloc((switchcount) * sizeof(LLONG_TYPE));
	global_flag--;
	stmt = stmt->s1;
	while (stmt) {
                if( stmt->s2 )          /* default case ? */
                        {
                        stmt->label = (SNODE *) deflab;
												diddef = TRUE;
                        }
                else
                        {
												switchlabels[i] = curlab;
												switchbinlabels[i] = -1;
												switchids[i++] = (int)stmt->label;
												stmt->label = (SNODE *)curlab;
                        }
                if( stmt->next != 0 )
                        curlab = nextlabel++;
                stmt = stmt->next;
   }
	for (i=0; i < switchcount; i++)
		for (j=i+1; j < switchcount; j++)
			if (switchids[j] < switchids[i]) {
				int temp = switchids[i];
				switchids[i] = switchids[j];
				switchids[j] = temp;
				temp = switchlabels[i];
				switchlabels[i] = switchlabels[j];
				switchlabels[j] = temp;
			}
	for (i=0; i < switchcount; i++) {
      if (size == 6 || size == -6) {
         int lab = nextlabel++ ;
         ap2 = make_immed(switchids[i]);
#if sizeof(LLONG_TYPE) == 4
         ap4 = make_immed(switchids[i] < 0 ? 0xffffffff : 0) ;
#else
         ap4 = make_immed(switchids[i] >> 32) ;
#endif
         ap3 = make_label(switchlabels[i]);
         gen_coden(op_cmp,4,makedreg(EAX),ap2) ;
         gen_code(op_jne, make_label(lab),0) ;
         gen_coden(op_cmp,4,makedreg(EDX),ap4) ;
         gen_code(op_je, ap3,0) ;
         gen_label(lab) ;
      } else {
         ap2 = make_immed(switchids[i]);
         ap3 = make_label(switchlabels[i]);
         gen_coden(op_cmp,ap1->length,ap1,ap2);
         gen_code(op_je,ap3,0);
      }
	}
	gen_codes(op_jmp,0,make_label(deflab),0);
	freeop(ap1);
}

void gencase(SNODE *stmt)
/*
 *      generate all cases for a switch statement.
 */
{       while( stmt != 0 )
                {
                gen_label((int)stmt->label);
                if( stmt->s1 != 0 )
                        {
                        genstmt(stmt->s1);
                        }

                stmt = stmt->next;
                }
}


void genxswitch(SNODE *stmt)
/*
 *      analyze and generate best switch statement.
 */
{       int     oldbreak;
	int olddiddef = diddef;
	int deflab = nextlabel++;
        oldbreak = breaklab;
        breaklab = nextlabel++;
				diddef = FALSE;
				switch (analyzeswitch(stmt)) {
					case 3:
						gensimpleswitch(stmt,deflab);
						break;
  				case 2:
						genbinaryswitch(stmt,deflab);
						break;
					case 1:
						gencompactswitch(stmt,deflab);
						break;
					case 0:
						if (stmt->s1)
							stmt->s1->label = (SNODE *) nextlabel++;
						break;
				}
        gencase(stmt->s1);
				if (!diddef)
					gen_label(deflab);
        gen_label(breaklab);
        breaklab = oldbreak;
	diddef = olddiddef;
}
void AddProfilerData(void)
{
	char pname[256];
	if (prm_profiler) {
		sprintf(pname,"%s",currentfunc->name);
		plabel = stringlit(pname,FALSE,strlen(pname+1));
		gen_codes(op_push,4,make_label(plabel),0);
		call_library("__profile_in");
		gen_codes(op_pop,4,makedreg(ECX),0);
	}
}
void SubProfilerData(void)
{
	if (prm_profiler) {
		gen_codes(op_push,4,make_label(plabel),0);
		call_library("__profile_out");
		gen_codes(op_pop,4,makedreg(ECX),0);
	}
}
OCODE * ThunkAddPeep(OCODE *after, int op, AMODE *ap1, AMODE *ap2) 
{       OCODE    *new;
        new = xalloc(sizeof(OCODE));
        new->opcode = op;
        if (op == op_label) {
          new->oper1 = ap1 ;
          new->oper2 = 0 ;
        } else {
          new->oper1 = copy_addr(ap1);
          new->oper2 = copy_addr(ap2);
        }
//        fixlen(new->oper1);
//        fixlen(new->oper2);
        new->fwd = after->fwd ;
        after->fwd->back = new ;
        new->back = after ;
        after->fwd = new ;
        return new ;
}
void ThunkUncommittedStack(void)
{
   if (lc_maxauto >= 4088) {
      AMODE *ecx ;
      int val = (lc_maxauto + 4095) / 4096, label = nextlabel++ ;
      OCODE *after = frame_ins->back ;
      remove_peep_entry(frame_ins) ;
      after = ThunkAddPeep(after,op_mov,ecx = makedreg(ECX),make_immed(val)) ;
      after = ThunkAddPeep(after,op_push,ecx,0) ;
      after = ThunkAddPeep(after,op_label,(AMODE *)label,0) ;
      after = ThunkAddPeep(after,op_sub,makedreg(ESP),make_immed(4092)) ;
      after = ThunkAddPeep(after,op_push,ecx,0) ;
      ThunkAddPeep(after,op_loop,make_label(label),0) ;
   }
}
AMODE *genreturn(SNODE *stmt, int flag)
/*
 *      generate a return statement.
 */
{       AMODE    *ap=0,*ap1,*ap2;
				ENODE ep;
				int size,esize,t,q;
				SubProfilerData();
            ThunkUncommittedStack() ;
        if( stmt != 0 && stmt->exp != 0 ) {
                initstack();
								if (currentfunc->tp->btp && isstructured(currentfunc->tp->btp)) {
									size = currentfunc->tp->btp->size;
									ep.nodetype = en_ul_ref;
									ep.v.p[0] = stmt->exp;
									ap = gen_expr(&ep,FALSE,FALSE,0);
									noids(ap);
										ap1 = xalloc(sizeof(AMODE));
										ap1->preg = EBP;
										ap1->mode = am_indisp;
										if ((currentfunc->pascaldefn || currentfunc->isstdcall )&& currentfunc->tp->lst.head && currentfunc->tp->lst.head != (SYM *) -1) 
                                 ap1->offset = makeintnode(en_icon,(currentfunc->tp->lst.head->value.i+((currentfunc->tp->lst.head->tp->size+3) &0xFFFFFFFCL)));
										else
#ifdef CPLUSPLUS
											if (declclass && !(currentfunc->value.classdata.cppflags & PF_STATIC))
                                    ap1->offset = makeintnode(en_icon,12);
											else
#endif
                                    ap1->offset = makeintnode(en_icon,8);
									gen_code(op_mov,ap2 = temp_data(),ap1);
									ap2->mode = am_indisp;
									ap2->offset = xalloc(sizeof(ENODE));
									ap2->offset->nodetype = en_icon;
									amode_moveblock(ap2,ap,size);
									freeop(ap);
									gen_code(op_mov,makedreg(EAX),ap1);
								}
								else {
									size = currentfunc->tp->btp->size;
                           if (size == 4 && currentfunc->tp->btp->type == bt_float)
                              size = 7 ;
                           if (currentfunc->tp->btp->type == bt_long || currentfunc->tp->btp->type == bt_unsignedlong) {
                              size = currentfunc->tp->btp->type == bt_long ? -6 : 6 ;
                              ap = gen_expr(stmt->exp,FALSE,FALSE,size);
                              do_extend(ap,size, F_AXDX);
                           } else if (size > 6) {
										retsize = 10;
                              ap = gen_expr(stmt->exp,FALSE,FALSE,size);
										do_extend(ap,size, F_FREG);
									}
                           else {              
                                 returndreg = TRUE ;
											retsize = 4;

                                 ap = gen_expr(stmt->exp,FALSE,FALSE,size);
											do_extend(ap,size, F_DREG | F_VOL);
                                 if( ap->preg != 0 ) {
                                       gen_codes(op_mov,size,makedreg(0),ap);
													ap = makedreg(EAX);
											}
									}
                }
								freeop(ap);
				}
				if (flag & 1) {
               if (retlab != -1)
                  gen_label(retlab);
					if (flag & 2) {
						return ap;
					}
               if (currentfunc->value.classdata.eofdest) {
                  if (returndreg) {
                     if (currentfunc->tp->btp->type == bt_long || currentfunc->tp->btp->type == bt_unsignedlong)
                        gen_codes(op_push,4,makedreg(2),0) ;
                     gen_codes(op_push,4,makedreg(0),0) ;
                        
                  }
                  gen_expr(currentfunc->value.classdata.eofdest, TRUE,FALSE,0) ;
                  if (returndreg) {
                     gen_codes(op_pop,4,makedreg(0),0) ;
                     if (currentfunc->tp->btp->type == bt_long || currentfunc->tp->btp->type == bt_unsignedlong)
                        gen_codes(op_pop,4,makedreg(2),0) ;
                  }

               }
          if( fsave_mask != 0 )
						diag("Float restore in return");
					if (currentfunc->loadds&& !currentfunc->intflag)
						gen_code(op_pop,makesegreg(DS),0);
          if( save_mask != 0 )
							popregs(save_mask);
					if ((funcfloat || lc_maxauto || currentfunc->tp->lst.head && currentfunc->tp->lst.head != (SYM *)-1)
#ifdef CPLUSPLUS
							|| declclass && !(currentfunc->value.classdata.cppflags & PF_STATIC)
#endif
                     || !prm_smartframes
						) {
						/* LEAVE is a little faster in simple tests, but since this version
						 * can be paired with other instructions on later processors we
						 * do it this way.  Also don't reload ESP if no local vars...
						 */
						if (funcfloat || lc_maxauto)
							gen_code(op_mov,makedreg(ESP),makedreg(EBP));
						else
							initstack();
						if (!currentfunc->intflag)
							gen_code(op_pop,makedreg(EBP),0);
					}
					if (currentfunc->intflag) {
           	gen_codes(op_popad,0,0,0);
					}
					if (currentfunc->loadds&& currentfunc->intflag)
						gen_code(op_pop,makesegreg(DS),0);
					if (currentfunc->intflag) {
           	gen_codes(op_iretd,0,0,0);
					}
					else {
						int retop = op_ret;
						if (currentfunc->farproc)
							retop = op_retf;
						if (currentfunc->pascaldefn || currentfunc->isstdcall) {
							long retsize = 0;
							if (currentfunc->tp->lst.head && currentfunc->tp->lst.head != (SYM *)-1) {
								SYM *sp = currentfunc->tp->lst.head;
								if (currentfunc->isstdcall)
									while (sp->next)
										sp = sp->next;
								retsize = sp->value.i+((sp->tp->size + 3) & 0xfffffffcL);
									retsize -= 8;
							}		
							if (currentfunc->tp->btp && isstructured(currentfunc->tp->btp))
								retsize += 4;
							if (retsize) {
            		gen_codes(retop,0,make_immed(retsize),0);
								return 0;
							}
						}		
            gen_codes(retop,0,0,0);
					}
				}
        else {
          gen_codes(op_jmp,0,make_label(retlab),0);
				}
	return 0;
}

void genstmt(SNODE *stmt)
/*
 *      genstmt will generate a statement and follow the next pointer
 *      until the block is generated.
 */
{
		   while( stmt != 0 )
                {
                OCODE *result ;
                switch( stmt->stype )
                        {
												case st_block:
                                                result = gen_codes(op_blockstart,0,0,0) ;
                                                result->blocknum = (int)stmt->label ;
																genstmt(stmt->exp);
                                                result = gen_codes(op_blockend,0,0,0) ;
                                                result->blocknum = (int)stmt->label ;
																break;
                        case st_label:
                                gen_label((int)stmt->label);
                                break;
                        case st_goto:
                                gen_codes(op_jmp,0,make_label((int)stmt->label),0);
                                break;
                        case st_expr:
                                initstack();
                                gen_void(stmt->exp);
                                break;
                        case st_return:
                                genreturn(stmt,0);
                                break;
                        case st_if:
                                genif(stmt);
                                break;
                        case st_while:
                                genwhile(stmt);
                                break;
                        case st_do:
                                gendo(stmt);
                                break;
                        case st_for:
                                gen_for(stmt);
                                break;
                        case st_continue:
                                gen_codes(op_jmp,0,make_label(contlab),0);
                                break;
                        case st_break:
                                gen_codes(op_jmp,0,make_label(breaklab),0);
                                break;
                        case st_switch:
                                genxswitch(stmt);
                                break;
												case st_line:
		 														gen_line(stmt);
																break;
												case st_asm:
																if (stmt->exp)
																	add_peep(stmt->exp);
																break;
												case st__genword:
																gen_codes(op_genword,0,make_immed((int)stmt->exp),0);
																break;
                        default:

                                DIAG("unknown statement.");
                                break;
                        }
                stmt = stmt->next;
                }
}
#ifdef CPLUSPLUS
void scppinit(void)
/*
 * code in MAIN to execute C++ startup routines
 * it is safe to do it in MAIN since you cannot call main or take its address
 *
 */
{
#ifdef OLD
	if (!strcmp(currentfunc->name,"_main")) {
		AMODE *ap1,*ap2,*ap3,*ap4;
		int lbl = nextlabel++;
		initstack();
		ap1 = makedreg(ESI);
		ap4 = makedreg(EAX);
		ap2 = set_symbol("cppistart",0);
		ap3 = set_symbol("cppiend",0);
		gen_code(op_push,ap1,0);
		gen_code(op_mov,ap1,ap2);
		gen_label(lbl);
		gen_code(op_lodsd,0,0);
		gen_code(op_call,ap4,0);
		gen_codes(op_cmp,4,ap1,ap3);
		gen_code(op_jb,make_label(lbl),0);
		gen_code(op_pop,ap1,0);
		freeop(ap1);
	}
#endif
}
SYM *gen_mp_virtual_thunk(SYM *vsp)
{
	LIST *v = mpthunklist;
	SYM *sp;
	AMODE *ap1,*ap2 ;
	char buf[100];
	while (v) {
		sp = (SYM *)v->data;
		if (sp->value.i == vsp->value.classdata.vtabindex)
			return sp;
		v = v->link;
	}
	global_flag ++;
	sp = xalloc(sizeof(SYM));
	sp->value.i = vsp->value.classdata.vtabindex;
	sprintf(buf,"@$mpt$%d",sp->value.i);
	sp->name = litlate(buf) ;
	sp->storage_class = sc_static;
  sp->staticlabel=FALSE;
	v = xalloc(sizeof(LIST));
	v->data = sp;
	v->link = mpthunklist;
	mpthunklist = v;
	global_flag--;
   cseg();
   gen_codelab(sp) ;
//   gen_virtual(sp) ;
	ap1 = makedreg(ESP) ;
	ap1->mode = am_indisp ;
   ap1->offset = makeintnode(en_icon,4) ;
	gen_codes(op_mov,4,makedreg(EAX),ap1) ;
	ap1->preg = 0 ;
   if (sp->value.classdata.vtaboffs)
      ap1->offset = makeintnode(en_icon,sp->value.classdata.vtaboffs) ;
   else
      ap1->offset = makeintnode(en_icon,0) ;
	gen_codes(op_mov,4,makedreg(EAX),ap1) ;
   ap1->offset = makeintnode(en_icon,sp->value.i) ;
	gen_codes(op_jmp,4,ap1,0) ;
	flush_peep() ;
//   gen_endvirtual(sp) ;
	return sp;
}
#endif
void genfunc(SNODE *stmt)
/*
 *      generate a function body.
 */
{           //int isconst = FALSE;
            returndreg = FALSE ;
				retsize = 0;
            contlab = breaklab = -1;
            retlab = nextlabel++ ;
            startlab = nextlabel++ ;
				funcfloat = 0;
            if (funcstmt)
               gen_line(funcstmt) ;
            funcstmt = 0 ;
#ifdef XXXXX
				if (stmt->stype == st_line) {
					gen_line(stmt);
					stmt = stmt->next;
				}
				if (!stmt) {
					gen_codelab(currentfunc);
          gen_codes(op_ret,0,0,0);
					flush_peep();
					return;
				}
#endif
#ifdef CPLUSPLUS					
#ifdef XXXXX
				if (currentfunc->value.classdata.cppflags & PF_CONSTRUCTOR) {
					isconst = TRUE;
				}
#endif
				if (currentfunc->value.classdata.cppflags & (PF_HEADERFUNC | PF_INSTANTIATED))
					gen_virtual(currentfunc);
				else
#endif
					gen_codelab(currentfunc);  /* name of function */
					
        opt1(stmt);			/* push args & also subtracts SP */
#ifdef CPLUSPLUS
#ifdef XXXXX
				if (isconst) {
					SYM *psp = currentfunc->parentclass ;
					if (currentfunc->value.classdata.basecons)
						gen_void(currentfunc->value.classdata.basecons);
               if (psp->value.classdata.baseclass->vtabsp) {
                     CLASSLIST *l = psp->value.classdata.baseclass ;
							ENODE *thisn = thisenode ;
                     ENODE *ts = makenode(en_nacon,psp->value.classdata.baseclass->vtabsp, 0) ;
                     ENODE *exp1, *exp2 ;
							thisn = makenode(en_l_ref,thisn,0);
                     while (l) {
                        if (l->isvtab) {
                           exp1 = makenode(en_addstruc,ts,makeintnode(en_icon,l->vtaboffs)) ;
                           exp2 = makenode(en_addstruc,thisn,makeintnode(en_icon,l->offset)) ;
                           exp2 = makenode(en_l_ref,exp2,0);
                           exp2 = makenode(en_assign,exp2,exp1) ;
                           initstack() ;
                           gen_expr(exp2,FALSE,TRUE,4) ;
                        }
                        l = l->link ;
                     }
					}
				}
#endif
				if (prm_cplusplus)
					scppinit();
#endif
        genstmt(stmt);
#ifdef CPLUSPLUS
				if (block_rundown)
					if (retsize)
						if (retsize > 4) {
							gen_code(op_fstp,floatconvpos(),0);
							gen_void(block_rundown);
							gen_code(op_fld,floatconvpos(),0);
						}
						else {
							gen_code(op_push,makedreg(EAX),0);
							gen_void(block_rundown);
							gen_code(op_pop,makedreg(EAX),0);
						}
					else
						gen_void(block_rundown);
#endif
         if (funcendstmt)
				gen_line(funcendstmt) ;
         funcendstmt = 0 ;
        genreturn(0,1);
#ifdef CPLUSPLUS
				thissp->inreg = FALSE;
				thissp->value.i = 4;
#endif
        flush_peep();
#ifdef CPLUSPLUS
				if (currentfunc->value.classdata.cppflags & (PF_HEADERFUNC | PF_INSTANTIATED))
					gen_endvirtual(currentfunc);
#endif
}