/* $Header:lab.c 12.0$ */
/* $ACIS:lab.c 12.0$ */
/* $Source: /ibm/acis/usr/src/lib/c2_ca/RCS/lab.c,v $ */

#ifndef lint
static char *rcsid = "$Header:lab.c 12.0$";
#endif

/* LAB.C. -- Build and maintain table of labels and references

   External routines:
      MakeLabRef()     Scan whole program for labels; build table.
      ListLabels()     List label table (debug)
      DropLabRef()     Delete whole label table
      LabCmp()         Compare two labels or references to labels
      DeleteLab()      Delete a label definition node
      LabUnRef()       Undo a label reference
      LabNewRef()      Add a new label reference
      BrFwd()          Is this node a branch forward?
      BrBkwd()         Is this node a branch backward?
*/

#include "stdio.h"
#include "opt.h"
#include "inst.h"
#include "instps.h"
#include "error.h"


static struct label *Lroot = NULL;


/* Scan whole program for labels.
   When found enter into label table.
*/
MakeLabRef()
{
   register struct snode *sn;
   register struct label *lab;
   struct label *labelRef();
   struct label *labelDef();

   sn = Root;
   Lroot = NULL;
   while ( (sn = sn->next) != NULL)  {
      lab = NULL;
      if( HASLABEL(sn) )   lab = labelDef(sn);      else
      if( BTLAB(sn) )      lab = labelRef(sn);
      sn->lablock = lab;
      }
}


/* Found the definition of a label.
   enter it into the table  */
static struct label *labelDef(sn)
register struct snode *sn;
{
   register struct label *lab;
   if( !islet(*(sn->labels)) )
      return NULL;
   lab = LookupLab(sn->labels);
   lab->ldef = sn;
   lab->lname = sn->labels;
   return lab;
}

/* found a reference to a label;    Record it */
static struct label *labelRef(sn)
struct snode *sn;
{
   register struct label *lab;
   register char *s;

   if( !BTLAB(sn) )   return NULL;

   if( sn->op2a == NULL )           s=sn->op1;  /*.long, br */
   else if( OPNUMBER(sn)==i_using ) s=sn->op1;  /* .using   */
   else                             s=sn->op2a; /* br-on-bit*/

   /* if label doesn't start with a letter it is a function name
      or other special label we don't want to look at */
   if( !islet(*s) && *s != '$' )      return NULL;

   if ( *s == '$' ) s++;
   lab = LookupLab(s);
   if(lab->lname == NULL)
      lab->lname = s;
   lab->refcount += 1;
   sn->labsame = lab->lchain;
   lab->lchain = sn;
   return lab;
}

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

/* utility to list table of labels for debugging */
ListLabels()
{
   register struct label *lab;
   fprintf(stderr,"\nBlock       Label Refs Line ldef lchain\n");
   for( lab=Lroot; lab!=NULL; lab=lab->lnext )
      fprintf(stderr," %8x %11s %4d %4d %8x %8x\n",
         lab, lab->lname, lab->refcount,
         lab->ldef->lineno, lab->ldef, lab->lchain);
}

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

/* Delete whole label table */
DropLabRef()
{
/* register struct label *lab, *next;                  */
/* for( lab=Lroot; lab!=NULL; lab=next )   {           */
/*    next = lab->lnext;  /*get it before it's gone*/
/*    free(lab);                                       */
/*    }                                                */
   Lroot = NULL;
}

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

/* Look up a label in the table;
   Return a pointer to the table entry if found;
   NULL if not
*/
struct label *LookupLab(nm)
register char *nm;
{
   register struct label *lab;

   for( lab=Lroot; lab!=NULL; lab=lab->lnext )
      if( Labcmp(lab->lname, nm) ) {
         if(DEBUG2) fprintf(stderr,
                    "Found:  %s  ref: %d  line: %d\n",
                    lab->lname, lab->refcount,
                    lab->ldef ? lab->ldef->lineno : 0 );
         return lab;
         }
   lab = (struct label *) Myalloc( sizeof(struct label) );
   if( lab==NULL ) {Error(E_STORAGE, "in LookupLab", NULL); exit(); }
   lab->lnext = Lroot;
   Lroot = lab;
   lab->refcount = 0;
   lab->ldef = NULL;
   lab->lchain = NULL;
   lab->lname = NULL;
   return lab;
}

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

/* Compare two strings which are labels or a label reference.
   Trailing semicolons are ignored on either string;
   I.E.  "abc:" matches "abc" or "abc:"
*/
int Labcmp(a,b)
register char *a, *b;
{
   if(a==NULL)  return FALSE;
   if(b==NULL)  return FALSE;
   while( *a!=0 && *a==*b )    { a++; b++; }
   if( *a == ':' )  a++;
   if( *b == ':' )  b++;
   return *a == 0 && *b == 0;
}


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


/* Given a statement node with a label,
   delete the label if there are no more references to it.
   Otherwise, do nothing
*/
DeleteLabel(sn)
register struct snode *sn;
{
   register struct label *lab;

   lab = sn->lablock;
   if(lab==NULL) {
      fprintf(stderr,"Label not found in table: %s", sn->labels);
      return FALSE;
      }

   /* delete label if refcount is zero */
   if( lab->refcount == 0 )  {
      PrintShortNode(sn, "DeleteLabel: ");
      MarkDeleted(sn);
      return TRUE;
      }
   return FALSE;
}

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

/* given a statement node which refers to a label,
   Uncount the label reference and unhook node from
   the label chain.
*/
LabUnRef(sn)
register struct snode *sn;
{
   register struct label *lab;
   register struct snode *back, *pn;

   if(sn==NULL)  return;  /* shouldn't ever be null */

   lab = sn->lablock;
   if( lab==NULL )  return;

   /* decrement refcount */
   if( lab->refcount > 0 )
      lab->refcount -= 1;
   if(DEBUG1)  fprintf(stderr,"%s refcount now %d\n",
                      lab->lname, lab->refcount);

   /* unhook node from label chain */
   pn = lab->lchain;
   back = NULL;
   while( pn!=NULL && pn!=sn )   {
      back = pn;
      pn = pn->labsame;
      }
   if(pn==NULL)  {
      fprintf(stderr,"label block for %s not found in LabUnRef\n",
              lab->lname);
      return;
      }
   if(back==NULL)   lab->lchain  = sn->labsame;
   else            back->labsame = sn->labsame;
   sn->labsame = NULL;
   if(DEBUG1)  PrintShortNode(sn, "Unhooked from label chain: ");
}



/* given a statement node which refers to a label,
   add the label to the label reference chain.
   (Used in conjunction with LabUnRef, above, to
   change which label a node refers to
*/
LabNewRef(sn)
register struct snode *sn;
{
   sn->lablock = labelRef(sn);
   if(DEBUG1)  PrintShortNode(sn, "Added to label chain: ");
}


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


/* see if a given node is a branch instruction to a later node */
BrFwd(sn)
register struct snode *sn;
{
   if( !BRANCH(sn) )                return FALSE;
   if( sn->lablock == NULL )        return FALSE;
   if( sn->lablock->ldef == NULL )  return FALSE;
   if( SeqnoWrong )                 resequence();
   if( sn->lablock->ldef->seqno > sn->seqno )   {
      PrintShortNode( sn, "BrFwd is TRUE:" );
      return TRUE;
      }
   PrintShortNode( sn, "BrFwd is FALSE:" );
   return FALSE;
}


/* see if a given node is a branch instruction to an earlier node.
*/
BrBkwd(sn)
register struct snode *sn;
{
   register int rc;
   if( SeqnoWrong )                 resequence();

   if( !BRANCH(sn) )                           rc=FALSE;  else
   if( sn->lablock       == NULL )             rc=FALSE;  else
   if( sn->lablock->ldef == NULL )             rc=FALSE;  else
   if( sn->lablock->ldef->seqno < sn->seqno )  rc=TRUE;   else
      rc = FALSE;

   if(DEBUG1) {
      fprintf( stderr, "BrBkwd is %s", rc?"TRUE":"FALSE" );
      PrintShortNode( sn, "  in:" );
      }
   return rc;
}


/* resequence all nodes (i.e. reset sequence number)  */
static resequence()
{
   register struct snode *sn;
   register int i;

   for( sn=Root, i=1;  sn!=NULL;  sn=sn->next, i++ )
      sn->seqno = i;
}
