/*-----------------------------------------------------------------
    SDCClabel.c - label optimizations on iCode (intermediate code)

    Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998)

    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, 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    
    In other words, you are welcome to use, share and improve this program.
    You are forbidden to forbid anyone else to use, share and improve
    what you give them.   Help stamp out software-hoarding!  
-------------------------------------------------------------------------*/

#include "common.h"

hTab *labelRef = NULL;
hTab *labelDef = NULL;

/*-----------------------------------------------------------------*/
/* buildLabelRefTable - creates an hashTable of label references   */
/*-----------------------------------------------------------------*/
void 
buildLabelRefTable (iCode * ic)
{
  iCode *lic;

  setToNull ((void *) &labelRef);
  setToNull ((void *) &labelDef);
  labelRef = newHashTable (labelKey + 1);
  labelDef = newHashTable (labelKey + 1);

  for (lic = ic; lic; lic = lic->next)
    {
      if (lic->op == GOTO)
	hTabAddItem (&labelRef, (IC_LABEL (lic))->key, lic);

      if (lic->op == JUMPTABLE)
	{
	  symbol *lbl;
	  for (lbl = setFirstItem (IC_JTLABELS (lic)); lbl;
	       lbl = setNextItem (IC_JTLABELS (lic)))
	    {
	      hTabAddItem (&labelRef, lbl->key, lic);
	    }
	}

      if (lic->op == IFX)
	{
	  if (IC_TRUE (lic))
	    hTabAddItem (&labelRef, (IC_TRUE (lic))->key, lic);
	  else
	    hTabAddItem (&labelRef, (IC_FALSE (lic))->key, lic);
	}
      if (lic->op == LABEL)
	hTabAddItem (&labelDef, (IC_LABEL (lic))->key, lic);

    }
}

/*-----------------------------------------------------------------*/
/* labelGotoNext - kills gotos to next statement                   */
/*-----------------------------------------------------------------*/
int 
labelGotoNext (iCode * ic)
{
  iCode *loop;
  int change = 0;

  for (loop = ic; loop; loop = loop->next)
    {

      if (loop->op == GOTO &&	/* if this is a goto */
	  loop->next &&		/* and we have a next one */
	  loop->next->op == LABEL &&	/* next one is a label */
	  loop->next->label->key == loop->label->key)	/* same label */
	{
	  loop->prev->next = loop->next;	/* get this out of the chain */
	  loop->next->prev = loop->prev;
	  hTabDeleteItem (&labelRef, (IC_LABEL (loop))->key, loop, DELETE_ITEM, NULL);
	  change++;
	}
    }

  return change;
}

/*-------------------------------------------------------------------*/
/* deleteIfx - delete an IFX iCode or convert to DUMMY_READ_VOLATILE */
/*-------------------------------------------------------------------*/
static void
deleteIfx (iCode * loop, int key)
{
  if (!options.lessPedantic)
    {
      werrorfl (loop->filename, loop->lineno, W_CONTROL_FLOW);
    }
  hTabDeleteItem (&labelRef, key, loop, DELETE_ITEM, NULL);
	      
  /* If the condition was volatile, convert IFX to */
  /* DUMMY_READ_VOLATILE. Otherwise just delete the */
  /* IFX iCode */
  if (IS_OP_VOLATILE (IC_COND (loop)))
    {
      IC_RIGHT (loop) = IC_COND (loop);
      IC_LEFT (loop) = NULL;
      IC_RESULT (loop) = NULL;
      loop->op = DUMMY_READ_VOLATILE;
    }
  else
    {
      loop->prev->next = loop->next;
      loop->next->prev = loop->prev;
    }
}


/*-----------------------------------------------------------------*/
/* labelIfx - special case Ifx elimination                         */
/*-----------------------------------------------------------------*/
int 
labelIfx (iCode * ic)
{
  iCode *loop;
  int change = 0;


  for (loop = ic; loop; loop = loop->next)
    {
      /*   if  condition  goto label */
      /*   goto label                */
      /* i.e. the flow is going to the same location 
         regardless of the condition in this case the 
         condition can be eliminated with a WARNING ofcource */
      if (loop->op == IFX &&
	  loop->next &&
	  loop->next->op == GOTO)
	{
	  if (IC_TRUE (loop) &&
	      IC_TRUE (loop)->key == IC_LABEL (loop->next)->key)
	    {
	      deleteIfx (loop, IC_TRUE (loop)->key);
	      change++;
              continue;
	    }
	  else
	    {
	      if (IC_FALSE (loop) &&
		  IC_FALSE (loop)->key == IC_LABEL (loop->next)->key)
		{
		  deleteIfx (loop, IC_FALSE (loop)->key);
		  change++;
		  continue;
		}
	    }
	}
      /* same as above but with a twist */
      /* if condition goto label */
      /* label:                  */
      if (loop->op == IFX &&
	  loop->next &&
	  loop->next->op == LABEL &&
	  ((IC_TRUE (loop) && IC_TRUE (loop)->key == IC_LABEL (loop->next)->key) ||
	   (IC_FALSE (loop) && IC_FALSE (loop)->key == IC_LABEL (loop->next)->key)))
	{
	  deleteIfx (loop, IC_LABEL (loop->next)->key);
	  change++;
	  continue;
	}

      /* we will eliminate certain special case situations */
      /* of the conditional statement :-                  */
      /*       if cond != 0 goto _trueLabel               */
      /*       goto _falseLabel                           */
      /* _trueLabel :                                     */
      /*       ...                                        */
      /* in these cases , if this is the only reference   */
      /* to the _trueLabel, we can change it to :-        */
      /*       if cond == 0 goto _falseLabel              */
      /*       ...                                        */
      /* similarly if we have a situation like :-         */
      /*       if cond == 0 goto _falseLabel              */
      /*       goto _someLabel                            */
      /* _falseLabel :                                    */
      /* we can change this to                            */
      /*       if cond != 0 goto _someLabel               */
      /*       ...                                        */
      if (loop->op == IFX &&
	  loop->next &&
	  loop->next->op == GOTO &&
	  loop->next->next &&
	  loop->next->next->op == LABEL)
	{


	  /* now check that the last label is the */
	  /* same as the _trueLabel of this       */
	  if (IC_TRUE (loop))
	    if ((IC_TRUE (loop))->key != (IC_LABEL (loop->next->next))->key)
	      continue;
	    else;
	  else if ((IC_FALSE (loop))->key != (IC_LABEL (loop->next->next))->key)
	    continue;

	  /* now make sure that this is the only */
	  /* referenece to the _trueLabel        */
	  if (IC_TRUE (loop) && hTabItemWithKey (labelRef, (IC_TRUE (loop))->key))
	    {

	      /* we just change the falseLabel */
	      /* to the next goto statement    */
	      /* unreferenced label will take  */
	      /* care of removing the label    */
	      /* delete reference to the true label */

	      hTabDeleteItem (&labelRef, (IC_TRUE (loop))->key, loop, DELETE_ITEM, NULL);
	      IC_TRUE (loop) = NULL;
	      IC_FALSE (loop) = IC_LABEL (loop->next);
	      /* add reference to the LABEL */
	      hTabAddItem (&labelRef, (IC_FALSE (loop))->key, loop);
	      /* next remove the goto */
	      hTabDeleteItem (&labelRef,
	       (IC_LABEL (loop->next))->key, loop->next, DELETE_ITEM, NULL);
	      loop->next = loop->next->next;
	      loop->next->prev = loop;
	      change++;
	      continue;
	    }

	  /* now do the same with the false labels */
	  if (IC_FALSE (loop) &&
	      hTabItemWithKey (labelRef, (IC_FALSE (loop))->key))
	    {

	      hTabDeleteItem (&labelRef, (IC_FALSE (loop))->key, loop, DELETE_ITEM, NULL);
	      IC_FALSE (loop) = NULL;
	      IC_TRUE (loop) = IC_LABEL (loop->next);
	      hTabAddItem (&labelRef, (IC_TRUE (loop))->key, loop);
	      hTabDeleteItem (&labelRef, (IC_LABEL (loop->next))->key, loop->next, DELETE_ITEM, NULL);
	      loop->next = loop->next->next;
	      loop->next->prev = loop;
	      change++;
	      continue;
	    }
	}
    }

  return change;
}

/*-----------------------------------------------------------------*/
/* labelGotoGoto - target of a goto is a goto                      */
/*-----------------------------------------------------------------*/
int 
labelGotoGoto (iCode * ic)
{
  iCode *loop;
  int change = 0;

  for (loop = ic; loop; loop = loop->next)
    {
      iCode *stat;
      symbol *sLabel = NULL;
      stat = NULL;
      switch (loop->op)
	{
	case GOTO:		/* for a goto statement */
	  stat = hTabItemWithKey (labelDef, (sLabel = IC_LABEL (loop))->key);
	  break;
	case IFX:		/* for a conditional jump */
	  if (IC_TRUE (loop))
	    stat = hTabItemWithKey (labelDef, (sLabel = IC_TRUE (loop))->key);
	  else
	    stat = hTabItemWithKey (labelDef, (sLabel = IC_FALSE (loop))->key);
	}

      /* if we have a target statement then check if the next */
      /* one is a goto: this means target of goto is a goto   */
      if (stat && stat->next &&
	  (stat->next->op == GOTO ||
	   stat->next->op == LABEL) &&
	  stat->next != loop)
	{

	  symbol *repLabel = stat->next->label;	/* replace with label */

	  /* if they are the same then continue */
	  if (repLabel->key == sLabel->key)
	    continue;

	  /* replacement depends on the statement type */
	  switch (loop->op)
	    {

	    case GOTO:		/* for a goto statement */

	      hTabDeleteItem (&labelRef, (IC_LABEL (loop))->key, loop, DELETE_ITEM, NULL);
	      loop->label = repLabel;
	      hTabAddItem (&labelRef, repLabel->key, loop);
	      break;

	    case IFX:		/* for a conditional jump */
	      if (IC_TRUE (loop))
		{

		  hTabDeleteItem (&labelRef, (IC_TRUE (loop))->key, loop, DELETE_ITEM, NULL);
		  IC_TRUE (loop) = repLabel;
		}
	      else
		{

		  hTabDeleteItem (&labelRef, (IC_FALSE (loop))->key, loop, DELETE_ITEM, NULL);
		  IC_FALSE (loop) = repLabel;
		}
	      hTabAddItem (&labelRef, repLabel->key, loop);

	    }
	  change++;
	}
    }

  return change;
}

/*-----------------------------------------------------------------*/
/* labelUnrefLabel - remove unreferenced labels                    */
/*-----------------------------------------------------------------*/
int 
labelUnrefLabel (iCode * ic)
{
  iCode *loop;
  int change = 0;

  for (loop = ic; loop; loop = loop->next)
    {

      /* if this is a label */
      if (loop->op == LABEL)
	{
	  if (((IC_LABEL (loop))->key == returnLabel->key) ||
	      ((IC_LABEL (loop))->key == entryLabel->key))
	    continue;

	  if (hTabItemWithKey (labelRef, (IC_LABEL (loop))->key))
	    continue;

	  /* else eliminitate this one */
	  loop->prev->next = loop->next;	/* get this out of the chain */
	  loop->next->prev = loop->prev;
	  change++;
	}
    }

  return change;
}

/*-----------------------------------------------------------------*/
/* labelUnreach - remove unreachable code                          */
/*-----------------------------------------------------------------*/
int 
labelUnreach (iCode * ic)
{
  iCode *loop;
  iCode *tic;
  int change = 0;

  /* if we hit a return statement or a goto statement */
  /* remove all statements till we hit the next label */
  for (loop = ic; loop; loop = loop->next)
    {
      iCode *loop2;

      /* found a goto || return && the next */
      /* statement is not a label           */
      if (loop->op == GOTO || loop->op == RETURN)
	{
	  if (loop->next &&
	      (loop->next->op == LABEL ||
	       loop->next->op == ENDFUNCTION))
	    continue;

	  /* loop till we find a label */
	  loop2 = loop->next;
	  while (loop2 && loop2->op != LABEL)
	    loop2 = loop2->next;

	  /* throw away those in between */
	  for (tic = loop->next; tic && tic != loop2; tic = tic->next)
	    {
	      /* remove label references if any */
	      switch (tic->op)
		{
		case GOTO:
		  hTabDeleteItem (&labelRef, IC_LABEL (tic)->key, tic, DELETE_ITEM, NULL);
		  break;
		case IFX:
		  werrorfl (tic->filename, tic->lineno, W_CODE_UNREACH);
		  if (IC_TRUE (tic))
		    hTabDeleteItem (&labelRef, IC_TRUE (tic)->key, tic, DELETE_ITEM, NULL);
		  else
		    hTabDeleteItem (&labelRef, IC_FALSE (tic)->key, tic, DELETE_ITEM, NULL);
		  break;
		default:
		  werrorfl (tic->filename, tic->lineno, W_CODE_UNREACH);
		}
	    }

	  /* now set up the pointers */
	  loop->next = loop2;
	  if (loop2)
	    loop2->prev = loop;
	  change++;
	}
    }
  return change;
}

/*-----------------------------------------------------------------*/
/* iCodeLabelOptimize - some obvious & general optimizations       */
/*-----------------------------------------------------------------*/
iCode *
iCodeLabelOptimize (iCode * ic)
{
  if (!optimize.label1 &&
      !optimize.label2 &&
      !optimize.label3 &&
      !optimize.label4)
    return ic;

  /* build labelreferences */
  buildLabelRefTable (ic);

  /* the following transformations need to ne done */
  /* repeatedly till a fixed point is reached      */
  while (1)
    {
      int change;
      change = 0;

      /* first eliminate any goto statement */
      /* that goes to the next statement    */
      if (optimize.label1)
	change += labelGotoNext (ic);

      if (optimize.label2)
	change += labelIfx (ic);

      /* target of a goto is a goto then rename this goto */
      if (optimize.label3)
	change += labelGotoGoto (ic);

      /* remove unreference labels */
      if (optimize.label4)
	change += labelUnrefLabel (ic);

      /* remove unreachable code */
      change += labelUnreach (ic);

      if (!change)		/* fixed point reached */
	break;
    }

  return ic;
}
