/* optimize.c */
/* author: Edward A. Green */
/* optimizes code */

#include "proj3.h"


void remove_useless(BLOCK *blocks)
{
	MOD *lcl_tmp;  /* list of temporaries with local scope */
	MOD *lcl_fnd;  /* pointer to this list */
	CODE *code;    /* code under consideration */
	CODE *hold;    /* pointer for code manipulation */
	BLOCK *blk;    /* block under codsideration */
	MOD *find_local_temps(BLOCK *blocks);
	void dispose_code(CODE *code);
	MOD *find_mod(MOD *mod, char *s);

	lcl_tmp=find_local_temps(blocks);

	/* ELIMINATE USELESS VARIABLES */
	blocks->last->next=NULL;
	for (blk=blocks; blk!=NULL; blk=blk->next)
	  {
	  blk->lead->last->next=NULL;
	  for (code=blk->lead; code!=NULL; code=code->next)
	    {

	    /* eliminate useless variables */
	    if (  code->flds[4][0]=='T' && strcmp(code->flds[1],"[]=")
	       && code->next!=NULL	&& code->mod!=NULL )
	      {
	      lcl_fnd=find_mod(lcl_tmp,code->flds[4]);
	      if (lcl_fnd!=NULL)
		{
		if (!strcmp(code->next->flds[1],"MOV") &&
		    !strcmp(code->next->flds[2],code->flds[4]))
		  {
		  hold=code->next;
		  free(code->flds[4]);
		  code->flds[4]=hold->flds[4];
		  hold->flds[4]=NULL;
		  code->next=hold->next;
		  code->next->last=code;
		  dispose_code(hold);
		  }

		else if (code->mod->next_line==-1)
		  {
		  hold=code;
		  code=code->last;
		  hold->next->last=hold->last;
		  hold->last->next=hold->next;
		  dispose_code(hold);
		  }
		}
	      }
	    }
	  blk->lead->last->next=blk->lead;
	  }

	/* ELIMINATE POINTLESS NOPs */
	for (blk=blocks; blk!=NULL; blk=blk->next)
	  {
	  blk->lead->last->next=NULL;
	  for (code=blk->lead; code!=NULL; code=code->next)
	    {

	    /* eliminate pointless NOPs */
	    if ((code->flds[1][0]=='N') && (code->next!=NULL))
	      {
	      if ((strlen(code->next->flds[0])==0) &&
		  (code->basic==code->next->basic))
		{
		free(code->next->flds[0]);
		code->next->flds[0]=code->flds[0];
		code->flds[0]=NULL;
		hold=code;
		code=code->next;
		if (hold->last->next!=NULL) hold->last->next=code;
		if (blk->lead==hold) blk->lead=code;
		code->last=hold->last;
		dispose_code(hold);
		}
	      }
	    }
	  blk->lead->last->next=blk->lead;
	  }
	blocks->last->next=blocks;

	return;
}


/* OPTIMIZE CODE IN BLOCK STRUCTURE VIA CONSTANT PROPAGATION */
void propagate(BLOCK *blocks)
{
	MOD *prop_list;  /* prop. list: see "constant list", below */
	MOD *hold_mod;   /* holder for prop. list manipulation */
	CODE *code;      /* pointer to code being analyzed */
	BLOCK *blk;      /* block holder for block manipulation */
	char  s[30];     /* string to receive output of math ops */
	char *op_nd1;    /* pointers to operand values */
	char *op_nd2;    /* " */
	int  op_code;    /* an index into prop-ops arrays */
	int i;           /* working index into prop-ops arrays */

	/* operators which may receive a propagated constant */
	char *prop_ops0[] = {"ADD", "DI",  "DIV", "JGZ", "JZ",
			     "MOD", "MOV", "MUL",  "SUB", ""};
	/* operators which may generate a propagated value */
	char *prop_ops1[] = {"ADD", "DI",  "DIV", "MOD", "MOV",
			     "MUL",  "SUB", ""};

	/* SUBROUTINES */
	char *constant (char *varname, MOD *list);
	MOD *insert_const_list (char *varname, char *value, MOD *list);
	MOD *remove_const_list(char *varname, MOD *list);
	char *strsave(char *s);



	prop_list=NULL;

	blocks->last->next=NULL;
	for (blk=blocks; blk!=NULL; blk=blk->next)
	  {
	  blk->lead->last->next=NULL;
	  for (code=blk->lead; code!=NULL; code=code->next)
	    {

	    /* 0) check for block boundry; remove list if new block */
	    if (code->basic!=code->last->basic || code==blk->lead)
	      {
	      while (prop_list!=NULL)
		{
		hold_mod=prop_list;
		prop_list=prop_list->next;
		free(hold_mod->var);
		free(hold_mod);
		}
	      }

	    /* 0)  detect if the statement has an op code for which */
	    /*     we can substitute values */

	    op_code=-1;
	    for (i=0; strlen(prop_ops0[i])>0; i++)
	      if (!strcmp(prop_ops0[i],code->flds[1])) op_code=i;

	    if (op_code!=-1)
	      {
	      /* replace opcodes with values if possible */

	      /* 1) search for operand values and replace if found */
	      op_nd1=constant(code->flds[2],prop_list);
	      op_nd2=constant(code->flds[3],prop_list);

	      if (op_nd1!=NULL)
		{
		free(code->flds[2]);
		code->flds[2]=strsave(op_nd1);
		}
	      if (op_nd2!=NULL)
		{
		free(code->flds[3]);
		code->flds[3]=strsave(op_nd2);
		}

	      /* 2)  see if we are generating a new constant value */

	      /* 2a) detect if its an opcode which changes a variable */
	      op_code=-1;
	      for (i=0; strlen(prop_ops1[i])>0; i++)
		if (!strcmp(prop_ops1[i],code->flds[1])) op_code=i;

	      if (op_code>=0)
		{

		/* 2) see if all operands are constants  */
		op_nd1=code->flds[2];
		op_nd2=code->flds[3];

		if (   (   (op_nd1[0]>='0' && op_nd1[0]<='9')
			 || op_nd1[0]=='-' || op_nd1[0]=='+'
			 || op_nd1[0]=='.')
		    &&
		       (   (op_nd2[0]>='0' && op_nd2[0]<='9')
			 || op_nd2[0]=='-' || op_nd2[0]=='+'
			 || op_nd2[0]=='.'
			 || op_code==4)
		   )
		  {
		  /* 2a) evaluate and place result in string s */
		  switch (op_code)
		    {
		    case 0:
		      {
		      float i;
		      i = atof(op_nd1) + atof(op_nd2);
		      sprintf(s,"%f",i);
		      }
		      break;
		    case 1:
		      {
		      int i;
		      i = atoi(op_nd1) / atoi(op_nd2);
		      sprintf(s,"%f",i);
		      }
		      break;
		    case 2:
		      {
		      float i;
		      i = atof(op_nd1) / atof(op_nd2);
		      sprintf(s,"%f",i);
		      }
		      break;
		    case 3:
		      {
		      int i;
		      i = atoi(op_nd1) % atoi(op_nd2);
		      sprintf(s,"%f",i);
		      }
		      break;
		    case 4:
		      {
		      sprintf(s,"%s",op_nd1);
		      }
		      break;
		    case 5:
		      {
		      float i;
		      i = atof(op_nd1) * atof(op_nd2);
		      sprintf(s,"%f",i);
		      }
		      break;
		    case 6:
		      {
		      float i;
		      i = atof(op_nd1) - atof(op_nd2);
		      sprintf(s,"%f",i);
		      }
		    }

		  /* keep only significant digits in s after radix pt. */
		  if (NULL!=strpbrk(s,"."))
		    {
		    for (i=strlen(s)-1; s[i]=='0'; i--);
		    if (s[i]=='.') s[i]='\0';
		    else           s[i+1]='\0';
		    }

		  /* 3) replace command with a move command */
		  free(code->flds[1]);
		  free(code->flds[2]);
		  free(code->flds[3]);
		  code->flds[1]=strsave("MOV");
		  code->flds[2]=strsave(s);
		  code->flds[3]=strsave("");

		  /* 4) insert variable and value into list */
		  prop_list=insert_const_list(code->flds[4],s,prop_list);
		  }

		else
		  /* 2b) not all operands are constants, */
		  /*     we can no longer propagate this variable */
		  prop_list=remove_const_list(code->flds[4],prop_list);

		}  /* end if we can generate a new constant */
	      } /* end if we can substitute variables */

	    }
	  blk->lead->last->next=blk->lead;
	  }
	blocks->last->next=blocks;
}


/*************************************************/


/* SCAN THE PROGRAM, FINDING TEMPORATIES WHICH ARE ISOLATED TO 1 BLK */
MOD *find_local_temps(BLOCK *blocks)
{
	MOD *ret_val;   /* pointer to a table of temps */
	MOD *wrk_lst1;  /* variables for manipulating table */
	MOD *wrk_lst2;  /* " */
	CODE *code;     /* pointer ot code and block under const. */
	BLOCK *blk;     /* " */
	int i;          /* index into quad fields */

	MOD *next_use_find(MOD *next_use_list, char *s);

	ret_val=NULL;
	wrk_lst1=NULL;
	wrk_lst2=NULL;

	/* find all temps in the code */
	blocks->last->next=NULL;
	for (blk=blocks; blk!=NULL; blk=blk->next)
	  {
	  blk->lead->last->next=NULL;
	  for (code=blk->lead; code!=NULL; code=code->next)
	    {
	    for (i=2; i<5; i++)
	      {
	      if (code->flds[i][0]=='T')
		{
		/* look up or add the temp variable */
		ret_val=next_use_find(ret_val,code->flds[i]);
		/* if created, put it's block num in the val field */
		if (ret_val->next_line==-1)
		    ret_val->next_line=code->basic;
		/* if found, check that the block number is the same */
		else if (ret_val->next_line!=code->basic)
		    ret_val->next_line=-2;
		}
	      }
	    }
	  blk->lead->last->next=blk->lead;
	  }
	blocks->last->next=blocks;
 
	/* delete the temps that span basic blocks from the list */
	if (ret_val!=NULL)
	  {
	  for (wrk_lst1=ret_val->next, wrk_lst2=ret_val;
	     wrk_lst1!=NULL;
	     wrk_lst2=wrk_lst1, wrk_lst1=wrk_lst1->next)
	     {
	     if (wrk_lst1->next_line==-2)
	       {
	       wrk_lst2->next=wrk_lst1->next;
	       free(wrk_lst1->var);
	       free(wrk_lst1);
	       wrk_lst1=wrk_lst2;
	       }
	     }
	  if (ret_val->next_line==-2)
	    {
	    wrk_lst1=ret_val;
	    ret_val=ret_val->next;
	    free(wrk_lst1->var);
	    free(wrk_lst1);
	    }
	  }
	return(ret_val);
}


/************************************************/

/* SEARCH A LINKED MOD LIST FOR AN ENTRY, RETURN THE POINTER */
MOD *find_mod(MOD *mod, char *s)
{
	while (mod!=NULL)
	  {
	  if (!strcmp(s,mod->var)) return(mod);
	  mod=mod->next;
	  }
	return(NULL);
}

/**************************************/
