2001-10-21 Michael Hope <michaelh@juju.net.nz>
[fw/sdcc] / src / SDCCpeeph.c
index aa923fbbe1ef100bed2c1270c71e3df5527851e5..8797ce6c41301380fdb8ef2cafc979a7cdde3f5d 100644 (file)
@@ -1,5 +1,5 @@
 /*-------------------------------------------------------------------------
-  SDCCpeeph.c - The peep hole optimizer: for interpreting the 
+  SDCCpeeph.c - The peep hole optimizer: for interpreting the
                 peep hole rules
 
              Written By -  Sandeep Dutta . sandeep.dutta@usa.net (1999)
    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!  
+   what you give them.   Help stamp out software-hoarding!
 -------------------------------------------------------------------------*/
 
 #include "common.h"
-#include "SDCCpeeph.h"
 
-peepRule *rootRules = NULL;
-peepRule *currRule  = NULL;
+static peepRule *rootRules = NULL;
+static peepRule *currRule = NULL;
+
+#define HTAB_SIZE 53
+typedef struct
+  {
+    char name[SDCC_NAME_MAX + 1];
+    int refCount;
+  }
+labelHashEntry;
+
+static hTab *labelHash = NULL;
+
+static struct
+{
+  allocTrace values;
+  allocTrace labels;
+} _G;
+
+static int hashSymbolName (const char *name);
+static void buildLabelRefCountHash (lineNode * head);
 
 static bool matchLine (char *, char *, hTab **);
 
-#define FBYNAME(x) int x (hTab *vars, lineNode *currPl, lineNode *head)
+#define FBYNAME(x) int x (hTab *vars, lineNode *currPl, lineNode *head, \
+        const char *cmdLine)
+
+#if !OPT_DISABLE_PIC
+void  peepRules2pCode(peepRule *);
+#endif
 
 /*-----------------------------------------------------------------*/
 /* pcDistance - afinds a label back ward or forward                */
 /*-----------------------------------------------------------------*/
-int pcDistance (lineNode *cpos, char *lbl, bool back)
+int 
+pcDistance (lineNode * cpos, char *lbl, bool back)
 {
-    lineNode *pl = cpos;
-    char buff[MAX_PATTERN_LEN];
-    int dist = 0 ;
+  lineNode *pl = cpos;
+  char buff[MAX_PATTERN_LEN];
+  int dist = 0;
+
+  sprintf (buff, "%s:", lbl);
+  while (pl)
+    {
 
-    sprintf(buff,"%s:",lbl);
-    while (pl) {
+      if (pl->line &&
+         *pl->line != ';' &&
+         pl->line[strlen (pl->line) - 1] != ':' &&
+         !pl->isDebug)
 
-       if (pl->line && 
-           *pl->line != ';' &&
-           pl->line[strlen(pl->line)-1] != ':' &&
-           !pl->isDebug)
-            
-           dist ++;
+       dist++;
 
-       if (strncmp(pl->line,buff,strlen(buff)) == 0)
-           return dist;
+      if (strncmp (pl->line, buff, strlen (buff)) == 0)
+       return dist;
 
-       if (back)
-           pl = pl->prev;
-       else
-           pl = pl->next;
+      if (back)
+       pl = pl->prev;
+      else
+       pl = pl->next;
 
     }
-    return 0;
+  return 0;
 }
 
 /*-----------------------------------------------------------------*/
-/* labelInRange - will check to see if label %5 is within range    */
+/* flat24bitMode - will check to see if we are in flat24 mode      */
 /*-----------------------------------------------------------------*/
-FBYNAME(labelInRange)
+FBYNAME (flat24bitMode)
 {
-    /* assumes that %5 pattern variable has the label name */
-    char *lbl = hTabItemWithKey(vars,5);
-    int dist = 0 ;
-
-    if (!lbl)
-       return FALSE;
-
-    /* if the previous teo instructions are "ljmp"s then don't
-       do it since it can be part of a jump table */
-    if (currPl->prev && currPl->prev->prev &&
-       strstr(currPl->prev->line,"ljmp") &&
-       strstr(currPl->prev->prev->line,"ljmp"))
-       return FALSE ;
-
-    /* calculate the label distance : the jump for reladdr can be
-       +/- 127 bytes, here Iam assuming that an average 8051
-       instruction is 2 bytes long, so if the label is more than
-       63 intructions away, the label is considered out of range
-       for a relative jump. we could get more precise this will
-       suffice for now since it catches > 90% cases */
-    dist = (pcDistance(currPl,lbl,TRUE) +
-           pcDistance(currPl,lbl,FALSE)) ;
-        
-    if (!dist || dist > 45)
-       return FALSE;
+  return (options.model == MODEL_FLAT24);
+}
 
-    return TRUE;
+/*-----------------------------------------------------------------*/
+/* labelInRange - will check to see if label %5 is within range    */
+/*-----------------------------------------------------------------*/
+FBYNAME (labelInRange)
+{
+  /* assumes that %5 pattern variable has the label name */
+  char *lbl = hTabItemWithKey (vars, 5);
+  int dist = 0;
+
+  if (!lbl)
+    return FALSE;
+
+  /* if the previous two instructions are "ljmp"s then don't
+     do it since it can be part of a jump table */
+  if (currPl->prev && currPl->prev->prev &&
+      strstr (currPl->prev->line, "ljmp") &&
+      strstr (currPl->prev->prev->line, "ljmp"))
+    return FALSE;
+
+  /* calculate the label distance : the jump for reladdr can be
+     +/- 127 bytes, here Iam assuming that an average 8051
+     instruction is 2 bytes long, so if the label is more than
+     63 intructions away, the label is considered out of range
+     for a relative jump. we could get more precise this will
+     suffice for now since it catches > 90% cases */
+  dist = (pcDistance (currPl, lbl, TRUE) +
+         pcDistance (currPl, lbl, FALSE));
+
+/*    if (!dist || dist > 45) has produced wrong sjmp */
+  /* 07-Sep-2000 Michael Schmitt */
+  /* FIX for Peephole 132 */
+  /* switch with lots of case can lead to a sjmp with a distance */
+  /* out of the range for sjmp */
+  if (!dist || dist > 43)
+    return FALSE;
+
+  return TRUE;
 }
 
 /*-----------------------------------------------------------------*/
 /* operandsNotSame - check if %1 & %2 are the same                 */
 /*-----------------------------------------------------------------*/
-FBYNAME(operandsNotSame)
+FBYNAME (operandsNotSame)
 {
-    char *op1 = hTabItemWithKey(vars,1);
-    char *op2 = hTabItemWithKey(vars,2);
+  char *op1 = hTabItemWithKey (vars, 1);
+  char *op2 = hTabItemWithKey (vars, 2);
 
-    if (strcmp(op1,op2) == 0)
-       return FALSE;
-    else
-       return TRUE;
+  if (strcmp (op1, op2) == 0)
+    return FALSE;
+  else
+    return TRUE;
+}
+
+/* labelRefCount:
+
+ * takes two parameters: a variable (bound to a label name)
+ * and an expected reference count.
+ *
+ * Returns TRUE if that label is defined and referenced exactly
+ * the given number of times.
+ */
+FBYNAME (labelRefCount)
+{
+  int varNumber, expectedRefCount;
+  bool rc = FALSE;
+
+  /* If we don't have the label hash table yet, build it. */
+  if (!labelHash)
+    {
+      buildLabelRefCountHash (head);
+    }
+
+  if (sscanf (cmdLine, "%*[ \t%]%d %d", &varNumber, &expectedRefCount) == 2)
+    {
+      char *label = hTabItemWithKey (vars, varNumber);
+
+      if (label)
+       {
+         labelHashEntry *entry;
+
+         entry = hTabFirstItemWK (labelHash, hashSymbolName (label));
+
+         while (entry)
+           {
+             if (!strcmp (label, entry->name))
+               {
+                 break;
+               }
+             entry = hTabNextItemWK (labelHash);
+           }
+         if (entry)
+           {
+#if 0
+             /* debug spew. */
+             fprintf (stderr, "labelRefCount: %s has refCount %d, want %d\n",
+                      label, entry->refCount, expectedRefCount);
+#endif
+
+             rc = (expectedRefCount == entry->refCount);
+           }
+         else
+           {
+             fprintf (stderr, "*** internal error: no label has entry for"
+                      " %s in labelRefCount peephole.\n",
+                      label);
+           }
+       }
+      else
+       {
+         fprintf (stderr, "*** internal error: var %d not bound"
+                  " in peephole labelRefCount rule.\n",
+                  varNumber);
+       }
+
+    }
+  else
+    {
+      fprintf (stderr,
+              "*** internal error: labelRefCount peephole restriction"
+              " malformed: %s\n", cmdLine);
+    }
+  return rc;
 }
 
 /*-----------------------------------------------------------------*/
 /* callFuncByName - calls a function as defined in the table       */
 /*-----------------------------------------------------------------*/
-int callFuncByName ( char *fname, 
-                    hTab *vars, 
-                    lineNode *currPl, 
-                    lineNode *head) 
+int 
+callFuncByName (char *fname,
+               hTab * vars,
+               lineNode * currPl,
+               lineNode * head)
 {
-    struct ftab {
-       char *fname ;
-       int (*func)(hTab *,lineNode *,lineNode *) ; 
-    }  ftab[] = { 
-       {"labelInRange",   labelInRange },
-       {"operandsNotSame", operandsNotSame }
-    };
-    int i;
-
-    for ( i = 0 ; i < ((sizeof (ftab))/(sizeof(struct ftab))); i++) 
-       if (strcmp(ftab[i].fname,fname) == 0)
-           return (*ftab[i].func)(vars,currPl,head);
-    fprintf(stderr,"could not find named function in function table\n");
-    return TRUE;    
+  struct ftab
+  {
+    char *fname;
+    int (*func) (hTab *, lineNode *, lineNode *, const char *);
+  }
+  ftab[] =
+  {
+    {
+      "labelInRange", labelInRange
+    }
+    ,
+    {
+      "operandsNotSame", operandsNotSame
+    }
+    ,
+    {
+      "24bitMode", flat24bitMode
+    }
+    ,
+    {
+      "labelRefCount", labelRefCount
+    }
+    ,
+  };
+  int i;
+
+  for (i = 0; i < ((sizeof (ftab)) / (sizeof (struct ftab))); i++)
+    if (strncmp (ftab[i].fname, fname, strlen (ftab[i].fname)) == 0)
+      {
+       return (*ftab[i].func) (vars, currPl, head,
+                               fname + strlen (ftab[i].fname));
+      }
+  fprintf (stderr, "could not find named function in function table\n");
+  return TRUE;
 }
 
 /*-----------------------------------------------------------------*/
 /* printLine - prints a line chain into a given file               */
 /*-----------------------------------------------------------------*/
-void printLine (lineNode *head, FILE *of)
+void 
+printLine (lineNode * head, FILE * of)
 {
-    if (!of)
-       of = stdout ;
-
-    while (head) {
-       /* don't indent comments & labels */
-       if (head->line && 
-           ( *head->line == ';' ||
-             head->line[strlen(head->line)-1] == ':'))
-           fprintf(of,"%s\n",head->line);
-       else
-           fprintf(of,"\t%s\n",head->line);
-       head = head->next;
+  if (!of)
+    of = stdout;
+
+  while (head)
+    {
+      /* don't indent comments & labels */
+      if (head->line &&
+         (*head->line == ';' ||
+          head->line[strlen (head->line) - 1] == ':')) {
+       fprintf (of, "%s\n", head->line);
+      } else {
+       if (head->isInline && *head->line=='#') {
+         // comment out preprocessor directives in inline asm
+         fprintf (of, ";");
+       }
+       fprintf (of, "\t%s\n", head->line);
+      }
+      head = head->next;
     }
 }
 
 /*-----------------------------------------------------------------*/
 /* newPeepRule - creates a new peeprule and attach it to the root  */
 /*-----------------------------------------------------------------*/
-peepRule *newPeepRule (lineNode *match  , 
-                      lineNode *replace, 
-                      char *cond       ,
-                      int restart)
+peepRule *
+newPeepRule (lineNode * match,
+            lineNode * replace,
+            char *cond,
+            int restart)
 {
-    peepRule *pr ;
-
-    ALLOC(pr,sizeof(peepRule));
-    pr->match = match;
-    pr->replace= replace;
-    pr->restart = restart;   
-    
-    if (cond && *cond) {
-       ALLOC_ATOMIC(pr->cond,strlen(cond)+1);
-       strcpy(pr->cond,cond);
-    } else
-       pr->cond = NULL ;
-
-    pr->vars = newHashTable(100);
-
-    /* if root is empty */
-    if (!rootRules) 
-       rootRules = currRule = pr;
-    else
-       currRule = currRule->next = pr;
-
-    return pr;
+  peepRule *pr;
+
+  pr = Safe_alloc ( sizeof (peepRule));
+  pr->match = match;
+  pr->replace = replace;
+  pr->restart = restart;
+
+  if (cond && *cond)
+    {
+      pr->cond = Safe_alloc ( strlen (cond) + 1);
+      strcpy (pr->cond, cond);
+    }
+  else
+    pr->cond = NULL;
+
+  pr->vars = newHashTable (100);
+
+  /* if root is empty */
+  if (!rootRules)
+    rootRules = currRule = pr;
+  else
+    currRule = currRule->next = pr;
+
+  return pr;
 }
 
 /*-----------------------------------------------------------------*/
 /* newLineNode - creates a new peep line                           */
 /*-----------------------------------------------------------------*/
-lineNode *newLineNode (char *line)
+lineNode *
+newLineNode (char *line)
 {
-    lineNode *pl;
+  lineNode *pl;
 
-    ALLOC(pl,sizeof(lineNode));
-    ALLOC_ATOMIC(pl->line,strlen(line)+1);
-    strcpy(pl->line,line);   
-    return pl;
+  pl = Safe_alloc ( sizeof (lineNode));
+  pl->line = Safe_alloc ( strlen (line) + 1);
+  strcpy (pl->line, line);
+  return pl;
 }
 
 /*-----------------------------------------------------------------*/
 /* connectLine - connects two lines                                */
 /*-----------------------------------------------------------------*/
-lineNode *connectLine (lineNode *pl1, lineNode *pl2)
+lineNode *
+connectLine (lineNode * pl1, lineNode * pl2)
 {
-    if (!pl1 || !pl2) {
-       fprintf (stderr,"trying to connect null line\n");
-       return NULL ;
+  if (!pl1 || !pl2)
+    {
+      fprintf (stderr, "trying to connect null line\n");
+      return NULL;
     }
 
-    pl2->prev = pl1;
-    pl1->next = pl2;
+  pl2->prev = pl1;
+  pl1->next = pl2;
 
-    return pl2;
+  return pl2;
 }
 
 #define SKIP_SPACE(x,y) { while (*x && (isspace(*x) || *x == '\n')) x++; \
@@ -228,483 +371,753 @@ lineNode *connectLine (lineNode *pl1, lineNode *pl2)
 /*-----------------------------------------------------------------*/
 /* getPeepLine - parses the peep lines                             */
 /*-----------------------------------------------------------------*/
-static void getPeepLine (lineNode **head, char **bpp)
+static void 
+getPeepLine (lineNode ** head, char **bpp)
 {
-    char lines[MAX_PATTERN_LEN];
-    char *lp;
-
-    lineNode *currL = NULL ;
-    char *bp = *bpp;
-    while (1) {
-       
-       if (!*bp) {
-           fprintf(stderr,"unexpected end of match pattern\n");
-           return ;
+  char lines[MAX_PATTERN_LEN];
+  char *lp;
+
+  lineNode *currL = NULL;
+  char *bp = *bpp;
+  while (1)
+    {
+
+      if (!*bp)
+       {
+         fprintf (stderr, "unexpected end of match pattern\n");
+         return;
        }
-       
-       if (*bp == '\n') {
-           bp++ ;
-           while (isspace(*bp) ||
-                  *bp == '\n') bp++;
+
+      if (*bp == '\n')
+       {
+         bp++;
+         while (isspace (*bp) ||
+                *bp == '\n')
+           bp++;
        }
-       
-       if (*bp == '}') {
-           bp++ ;
-           break;
+
+      if (*bp == '}')
+       {
+         bp++;
+         break;
        }
-       
-       /* read till end of line */
-       lp = lines ;
-       while ((*bp != '\n' && *bp != '}' ) && *bp)
-           *lp++ = *bp++ ;
-       
-       *lp = '\0';
-       if (!currL) 
-           *head = currL = newLineNode (lines);
-       else 
-           currL = connectLine(currL,newLineNode(lines));
-    }  
-
-    *bpp = bp;
+
+      /* read till end of line */
+      lp = lines;
+      while ((*bp != '\n' && *bp != '}') && *bp)
+       *lp++ = *bp++;
+
+      *lp = '\0';
+      if (!currL)
+       *head = currL = newLineNode (lines);
+      else
+       currL = connectLine (currL, newLineNode (lines));
+    }
+
+  *bpp = bp;
 }
 
 /*-----------------------------------------------------------------*/
 /* readRules - reads the rules from a string buffer                */
 /*-----------------------------------------------------------------*/
-static void readRules (char *bp)
+static void 
+readRules (char *bp)
 {
-    char restart = 0 ;
-    char lines[MAX_PATTERN_LEN];
-    char *lp;
-    lineNode *match;
-    lineNode *replace;
-    lineNode *currL = NULL;
-    
-    if (!bp)
-       return;
- top:
-    restart = 0;
-    /* look for the token "replace" that is the
-       start of a rule */
-    while (*bp && strncmp(bp,"replace",7)) bp++;
-    
-    /* if not found */
-    if (!*bp)        
-       return ;    
-
-    /* then look for either "restart" or '{' */
-    while (strncmp(bp,"restart",7) &&
-          *bp != '{'  && bp ) bp++ ;
-
-    /* not found */
-    if (!*bp) {
-       fprintf(stderr,"expected 'restart' or '{'\n");
-       return ;
-    }
-
-    /* if brace */
-    if (*bp == '{')
-       bp++ ;
-    else { /* must be restart */
-       restart++;
-       bp += strlen("restart");
-       /* look for '{' */
-       EXPECT_CHR(bp,'{',"expected '{'\n");    
-       bp++;
+  char restart = 0;
+  char lines[MAX_PATTERN_LEN];
+  char *lp;
+  lineNode *match;
+  lineNode *replace;
+  lineNode *currL = NULL;
+
+  if (!bp)
+    return;
+top:
+  restart = 0;
+  /* look for the token "replace" that is the
+     start of a rule */
+  while (*bp && strncmp (bp, "replace", 7))
+    bp++;
+
+  /* if not found */
+  if (!*bp)
+    return;
+
+  /* then look for either "restart" or '{' */
+  while (strncmp (bp, "restart", 7) &&
+        *bp != '{' && bp)
+    bp++;
+
+  /* not found */
+  if (!*bp)
+    {
+      fprintf (stderr, "expected 'restart' or '{'\n");
+      return;
     }
 
-    /* skip thru all the blank space */
-    SKIP_SPACE(bp,"unexpected end of rule\n");
-
-    match = replace = currL = NULL ;
-    /* we are the start of a rule */
-    getPeepLine(&match, &bp);
-
-    /* now look for by */
-    EXPECT_STR(bp,"by","expected 'by'\n");
-
-    /* then look for a '{' */
-    EXPECT_CHR(bp,'{',"expected '{'\n");
-    bp++ ;
-    
-    SKIP_SPACE(bp,"unexpected end of rule\n");
-    getPeepLine (&replace, &bp);
-   
-    /* look for a 'if' */    
-    while ((isspace(*bp) || *bp == '\n') && *bp) bp++;
-    
-    if (strncmp(bp,"if",2) == 0) {
-       bp += 2;
-       while ((isspace(*bp) || *bp == '\n') && *bp) bp++;
-       if (!*bp) {
-           fprintf(stderr,"expected condition name\n");
-           return;
+  /* if brace */
+  if (*bp == '{')
+    bp++;
+  else
+    {                          /* must be restart */
+      restart++;
+      bp += strlen ("restart");
+      /* look for '{' */
+      EXPECT_CHR (bp, '{', "expected '{'\n");
+      bp++;
+    }
+
+  /* skip thru all the blank space */
+  SKIP_SPACE (bp, "unexpected end of rule\n");
+
+  match = replace = currL = NULL;
+  /* we are the start of a rule */
+  getPeepLine (&match, &bp);
+
+  /* now look for by */
+  EXPECT_STR (bp, "by", "expected 'by'\n");
+
+  /* then look for a '{' */
+  EXPECT_CHR (bp, '{', "expected '{'\n");
+  bp++;
+
+  SKIP_SPACE (bp, "unexpected end of rule\n");
+  getPeepLine (&replace, &bp);
+
+  /* look for a 'if' */
+  while ((isspace (*bp) || *bp == '\n') && *bp)
+    bp++;
+
+  if (strncmp (bp, "if", 2) == 0)
+    {
+      bp += 2;
+      while ((isspace (*bp) || *bp == '\n') && *bp)
+       bp++;
+      if (!*bp)
+       {
+         fprintf (stderr, "expected condition name\n");
+         return;
        }
-           
-       /* look for the condition */
-       lp = lines;
-       while (isalnum(*bp)) {
-           *lp++ = *bp++;
+
+      /* look for the condition */
+      lp = lines;
+      while (*bp && (*bp != '\n'))
+       {
+         *lp++ = *bp++;
        }
-       *lp = '\0';
-       
-       newPeepRule(match,replace,lines,restart);
-    } else
-       newPeepRule(match,replace,NULL,restart);
-    goto top;
+      *lp = '\0';
+
+      newPeepRule (match, replace, lines, restart);
+    }
+  else
+    newPeepRule (match, replace, NULL, restart);
+  goto top;
 
 }
 
 /*-----------------------------------------------------------------*/
 /* keyForVar - returns the numeric key for a var                   */
 /*-----------------------------------------------------------------*/
-static int keyForVar (char *d) 
+static int 
+keyForVar (char *d)
 {
-    int i = 0;
+  int i = 0;
 
-    while (isdigit(*d)) {
-       i *= 10 ;
-       i += (*d++ - '0') ;
+  while (isdigit (*d))
+    {
+      i *= 10;
+      i += (*d++ - '0');
     }
 
-    return i;
+  return i;
 }
 
 /*-----------------------------------------------------------------*/
 /* bindVar - binds a value to a variable in the given hashtable    */
 /*-----------------------------------------------------------------*/
-static void bindVar (int key, char **s, hTab **vtab)
+static void 
+bindVar (int key, char **s, hTab ** vtab)
 {
-    char vval[MAX_PATTERN_LEN];
-    char *vvx;
-    char *vv = vval;
-    
-    /* first get the value of the variable */
-    vvx = *s;
-    /* the value is ended by a ',' or space or newline or null */
-    while (*vvx           && 
-          *vvx != ','    && 
-          !isspace(*vvx) && 
-          *vvx != '\n'   &&
-          *vvx != ':' ) {
-       char ubb = 0 ;
-       /* if we find a '(' then we need to balance it */
-       if (*vvx == '(') {
-           ubb++ ;
-           while (ubb) {
-               *vv++ = *vvx++ ;
-               if (*vvx == '(') ubb++;
-               if (*vvx == ')') ubb--;
+  char vval[MAX_PATTERN_LEN];
+  char *vvx;
+  char *vv = vval;
+
+  /* first get the value of the variable */
+  vvx = *s;
+  /* the value is ended by a ',' or space or newline or null or ) */
+  while (*vvx &&
+        *vvx != ',' &&
+        !isspace (*vvx) &&
+        *vvx != '\n' &&
+        *vvx != ':' &&
+        *vvx != ')')
+    {
+      char ubb = 0;
+      /* if we find a '(' then we need to balance it */
+      if (*vvx == '(')
+       {
+         ubb++;
+         while (ubb)
+           {
+             *vv++ = *vvx++;
+             if (*vvx == '(')
+               ubb++;
+             if (*vvx == ')')
+               ubb--;
            }
-       } else
-           *vv++ = *vvx++ ;
-    }
-    *s = vvx ;
-    *vv = '\0';
-    /* got value */
-    ALLOC_ATOMIC(vvx,strlen(vval)+1);
-    strcpy(vvx,vval);
-    hTabAddItem(vtab,key,vvx);
-    
+       }
+      else
+       *vv++ = *vvx++;
+    }
+  *s = vvx;
+  *vv = '\0';
+  /* got value */
+  vvx = traceAlloc (&_G.values, Safe_alloc(strlen (vval) + 1));
+  strcpy (vvx, vval);
+
+  hTabAddItem (vtab, key, vvx);
 }
 
 /*-----------------------------------------------------------------*/
 /* matchLine - matches one line                                    */
 /*-----------------------------------------------------------------*/
-static bool matchLine (char *s, char *d, hTab **vars)
-{    
-
-    if (!s || !(*s))
-       return FALSE;
+static bool 
+matchLine (char *s, char *d, hTab ** vars)
+{
 
-    while (*s && *d) {
-
-       /* skip white space in both */
-       while (isspace(*s)) s++;
-       while (isspace(*d)) d++;
-       
-       /* if the destination is a var */
-       if (*d == '%' && isdigit(*(d+1))) {         
-           char *v = hTabItemWithKey(*vars,keyForVar(d+1));
-           /* if the variable is already bound
-              then it MUST match with dest */
-           if (v) {
-               while (*v)
-                   if (*v++ != *s++) return FALSE;
-           } else 
-               /* variable not bound we need to
-                  bind it */
-               bindVar (keyForVar(d+1),&s,vars);
-           
-           /* in either case go past the variable */
-           d++ ;
-           while (isdigit(*d)) d++;
+  if (!s || !(*s))
+    return FALSE;
+
+  while (*s && *d)
+    {
+
+      /* skip white space in both */
+      while (isspace (*s))
+       s++;
+      while (isspace (*d))
+       d++;
+
+      /* if the destination is a var */
+      if (*d == '%' && isdigit (*(d + 1)))
+       {
+         char *v = hTabItemWithKey (*vars, keyForVar (d + 1));
+         /* if the variable is already bound
+            then it MUST match with dest */
+         if (v)
+           {
+             while (*v)
+               if (*v++ != *s++)
+                 return FALSE;
+           }
+         else
+           /* variable not bound we need to
+              bind it */
+           bindVar (keyForVar (d + 1), &s, vars);
+
+         /* in either case go past the variable */
+         d++;
+         while (isdigit (*d))
+           d++;
        }
 
-       /* they should be an exact match other wise */
-       if (*s && *d) {
-           while (isspace(*s))s++;
-           while (isspace(*d))d++;
-           if (*s++ != *d++)
-               return FALSE;
+      /* they should be an exact match other wise */
+      if (*s && *d)
+       {
+         while (isspace (*s))
+           s++;
+         while (isspace (*d))
+           d++;
+         if (*s++ != *d++)
+           return FALSE;
        }
 
     }
 
-    /* get rid of the trailing spaces 
-       in both source & destination */
-    if (*s) 
-       while (isspace(*s)) s++;
+  /* get rid of the trailing spaces
+     in both source & destination */
+  if (*s)
+    while (isspace (*s))
+      s++;
+
+  if (*d)
+    while (isspace (*d))
+      d++;
 
-    if (*d)
-       while (isspace(*d)) d++;
+  /* after all this if only one of them
+     has something left over then no match */
+  if (*s || *d)
+    return FALSE;
 
-    /* after all this if only one of them
-       has something left over then no match */
-    if (*s || *d)
-       return FALSE ;
-    
-    return TRUE ;
+  return TRUE;
 }
 
 /*-----------------------------------------------------------------*/
 /* matchRule - matches a all the rule lines                        */
 /*-----------------------------------------------------------------*/
-static bool matchRule (lineNode *pl, 
-                      lineNode **mtail, 
-                      peepRule *pr, 
-                      lineNode *head)
+static bool 
+matchRule (lineNode * pl,
+          lineNode ** mtail,
+          peepRule * pr,
+          lineNode * head)
 {
-    lineNode *spl ; /* source pl */
-    lineNode *rpl ; /* rule peep line */
-    
-    hTabClearAll(pr->vars); 
+  lineNode *spl;               /* source pl */
+  lineNode *rpl;               /* rule peep line */
+
 /*     setToNull((void **) &pr->vars);    */
 /*     pr->vars = newHashTable(100); */
 
-    /* for all the lines defined in the rule */
-    rpl = pr->match;
-    spl = pl ; 
-    while (spl && rpl) {
-
-       /* if the source line starts with a ';' then
-          comment line don't process or the source line
-          contains == . debugger information skip it */
-       if (spl->line && 
-           (*spl->line == ';' || spl->isDebug)) {
-           spl = spl->next;
-           continue;
+  /* for all the lines defined in the rule */
+  rpl = pr->match;
+  spl = pl;
+  while (spl && rpl)
+    {
+
+      /* if the source line starts with a ';' then
+         comment line don't process or the source line
+         contains == . debugger information skip it */
+      if (spl->line &&
+         (*spl->line == ';' || spl->isDebug))
+       {
+         spl = spl->next;
+         continue;
        }
 
-       if (!matchLine(spl->line,rpl->line,&pr->vars))
-           return FALSE ;
-       
-       rpl = rpl->next ;
-       if (rpl)
-           spl = spl->next ;
-    }
-
-    /* if rules ended */
-    if (!rpl) {
-       /* if this rule has additional conditions */
-       if ( pr->cond) {
-           if (callFuncByName (pr->cond, pr->vars,pl,head) ) {
-               *mtail = spl;
-               return TRUE;  
-           } else
-               return FALSE;
-       } else {
-           *mtail = spl;
-           return TRUE;
+      if (!matchLine (spl->line, rpl->line, &pr->vars))
+       return FALSE;
+
+      rpl = rpl->next;
+      if (rpl)
+       spl = spl->next;
+    }
+
+  /* if rules ended */
+  if (!rpl)
+    {
+      /* if this rule has additional conditions */
+      if (pr->cond)
+       {
+         if (callFuncByName (pr->cond, pr->vars, pl, head))
+           {
+             *mtail = spl;
+             return TRUE;
+           }
+         else
+           return FALSE;
+       }
+      else
+       {
+         *mtail = spl;
+         return TRUE;
        }
     }
-    else
-       return FALSE;
+  else
+    return FALSE;
 }
 
 /*-----------------------------------------------------------------*/
 /* replaceRule - does replacement of a matching pattern            */
 /*-----------------------------------------------------------------*/
-static void replaceRule (lineNode **shead, lineNode *stail, peepRule *pr)
+static void 
+replaceRule (lineNode ** shead, lineNode * stail, peepRule * pr)
 {
-    lineNode *cl = NULL;
-    lineNode *pl = NULL , *lhead = NULL;    
-    char lb[MAX_PATTERN_LEN];
-    char *lbp;
-    lineNode *comment = NULL;
-
-    /* collect all the comment lines in the source */
-    for (cl = *shead ; cl != stail ; cl = cl->next) {
-       if (cl->line && ( *cl->line == ';' || cl->isDebug)) {
-           pl = (pl ? connectLine (pl,newLineNode(cl->line)) :
-                 (comment = newLineNode(cl->line)));
-           pl->isDebug = cl->isDebug;
+  lineNode *cl = NULL;
+  lineNode *pl = NULL, *lhead = NULL;
+  /* a long function name and long variable name can evaluate to
+     4x max pattern length e.g. "mov dptr,((fie_var>>8)<<8)+fie_var" */
+  char lb[MAX_PATTERN_LEN*4];
+  char *lbp;
+  lineNode *comment = NULL;
+
+  /* collect all the comment lines in the source */
+  for (cl = *shead; cl != stail; cl = cl->next)
+    {
+      if (cl->line && (*cl->line == ';' || cl->isDebug))
+       {
+         pl = (pl ? connectLine (pl, newLineNode (cl->line)) :
+               (comment = newLineNode (cl->line)));
+         pl->isDebug = cl->isDebug;
        }
     }
-    cl = NULL;
-    
-    /* for all the lines in the replacement pattern do */
-    for ( pl = pr->replace ; pl ; pl = pl->next ) {
-       char *v;
-       char *l;
-       lbp = lb;
-
-       l = pl->line;
-       while (*l) {
-           /* if the line contains a variable */
-           if (*l == '%' && isdigit(*(l+1))) {
-               v = hTabItemWithKey(pr->vars,keyForVar(l+1));
-               if (!v) {
-                   fprintf(stderr,"used unbound variable in replacement\n");
-                   l++;
-                   continue;
+  cl = NULL;
+
+  /* for all the lines in the replacement pattern do */
+  for (pl = pr->replace; pl; pl = pl->next)
+    {
+      char *v;
+      char *l;
+      lbp = lb;
+
+      l = pl->line;
+
+      while (*l)
+       {
+         /* if the line contains a variable */
+         if (*l == '%' && isdigit (*(l + 1)))
+           {
+             v = hTabItemWithKey (pr->vars, keyForVar (l + 1));
+             if (!v)
+               {
+                 fprintf (stderr, "used unbound variable in replacement\n");
+                 l++;
+                 continue;
                }
-               while (*v)
-                   *lbp++ = *v++;
+             while (*v) {
+               *lbp++ = *v++;
+             }
+             l++;
+             while (isdigit (*l)) {
                l++;
-               while (isdigit(*l)) l++;
-               continue ;
+             }
+             continue;
+           }
+         *lbp++ = *l++;
+       }
+
+      *lbp = '\0';
+      if (cl)
+       cl = connectLine (cl, newLineNode (lb));
+      else
+       lhead = cl = newLineNode (lb);
+    }
+
+  /* add the comments if any to the head of list */
+  if (comment)
+    {
+      lineNode *lc = comment;
+      while (lc->next)
+       lc = lc->next;
+      lc->next = lhead;
+      if (lhead)
+       lhead->prev = lc;
+      lhead = comment;
+    }
+
+  /* now we need to connect / replace the original chain */
+  /* if there is a prev then change it */
+  if ((*shead)->prev)
+    {
+      (*shead)->prev->next = lhead;
+      lhead->prev = (*shead)->prev;
+    }
+  else
+    *shead = lhead;
+  /* now for the tail */
+  if (stail && stail->next)
+    {
+      stail->next->prev = cl;
+      if (cl)
+       cl->next = stail->next;
+    }
+}
+
+/* Returns TRUE if this line is a label definition.
+
+ * If so, start will point to the start of the label name,
+ * and len will be it's length.
+ */
+bool 
+isLabelDefinition (const char *line, const char **start, int *len)
+{
+  const char *cp = line;
+
+  /* This line is a label if if consists of:
+   * [optional whitespace] followed by identifier chars
+   * (alnum | $ | _ ) followed by a colon.
+   */
+
+  while (*cp && isspace (*cp))
+    {
+      cp++;
+    }
+
+  if (!*cp)
+    {
+      return FALSE;
+    }
+
+  *start = cp;
+
+  while (isalnum (*cp) || (*cp == '$') || (*cp == '_'))
+    {
+      cp++;
+    }
+
+  if ((cp == *start) || (*cp != ':'))
+    {
+      return FALSE;
+    }
+
+  *len = (cp - (*start));
+  return TRUE;
+}
+
+/* Quick & dirty string hash function. */
+static int 
+hashSymbolName (const char *name)
+{
+  int hash = 0;
+
+  while (*name)
+    {
+      hash = (hash << 6) ^ *name;
+      name++;
+    }
+
+  if (hash < 0)
+    {
+      hash = -hash;
+    }
+
+  return hash % HTAB_SIZE;
+}
+
+/* Build a hash of all labels in the passed set of lines
+ * and how many times they are referenced.
+ */
+static void 
+buildLabelRefCountHash (lineNode * head)
+{
+  lineNode *line;
+  const char *label;
+  int labelLen;
+  int i;
+
+  assert (labelHash == NULL);
+  labelHash = newHashTable (HTAB_SIZE);
+
+  /* First pass: locate all the labels. */
+  line = head;
+
+  while (line)
+    {
+      if (isLabelDefinition (line->line, &label, &labelLen)
+         && labelLen <= SDCC_NAME_MAX)
+       {
+         labelHashEntry *entry;
+
+         entry = traceAlloc (&_G.labels, Safe_alloc(sizeof (labelHashEntry)));
+
+         memcpy (entry->name, label, labelLen);
+         entry->name[labelLen] = 0;
+         entry->refCount = -1;
+
+         hTabAddItem (&labelHash, hashSymbolName (entry->name), entry);
+       }
+      line = line->next;
+    }
+
+
+  /* Second pass: for each line, note all the referenced labels. */
+  /* This is ugly, O(N^2) stuff. Optimizations welcome... */
+  line = head;
+  while (line)
+    {
+      for (i = 0; i < HTAB_SIZE; i++)
+       {
+         labelHashEntry *thisEntry;
+
+         thisEntry = hTabFirstItemWK (labelHash, i);
+
+         while (thisEntry)
+           {
+             if (strstr (line->line, thisEntry->name))
+               {
+                 thisEntry->refCount++;
+               }
+             thisEntry = hTabNextItemWK (labelHash);
            }
-           *lbp++ = *l++;
        }
+      line = line->next;
+    }
+
+#if 0
+  /* Spew the contents of the table. Debugging fun only. */
+  for (i = 0; i < HTAB_SIZE; i++)
+    {
+      labelHashEntry *thisEntry;
+
+      thisEntry = hTabFirstItemWK (labelHash, i);
 
-       *lbp = '\0';
-       if (cl)
-           cl = connectLine(cl,newLineNode(lb));
-       else
-           lhead = cl = newLineNode(lb);
-    }
-
-    /* add the comments if any to the head of list */
-    if (comment) {
-       lineNode *lc = comment;
-       while (lc->next) lc = lc->next;
-       lc->next = lhead;
-       if (lhead)
-           lhead->prev = lc;
-       lhead = comment;
-    }
-
-    /* now we need to connect / replace the original chain */
-    /* if there is a prev then change it */
-    if ((*shead)->prev) {
-       (*shead)->prev->next = lhead;
-       lhead->prev = (*shead)->prev;
-    } else 
-       *shead = lhead;
-    /* now for the tail */
-    if (stail && stail->next) {
-       stail->next->prev = cl;
-       if (cl)
-           cl->next = stail->next;
-    }    
+      while (thisEntry)
+       {
+         fprintf (stderr, "label: %s ref %d\n",
+                  thisEntry->name, thisEntry->refCount);
+         thisEntry = hTabNextItemWK (labelHash);
+       }
+    }
+#endif
 }
 
+/* How does this work?
+   peepHole
+    For each rule,
+     For each line,
+      Try to match
+      If it matches,
+       replace and restart.
+
+    matchRule
+     matchLine
+
+  Where is stuff allocated?
+  
+*/
+
 /*-----------------------------------------------------------------*/
 /* peepHole - matches & substitutes rules                          */
 /*-----------------------------------------------------------------*/
-void peepHole (lineNode **pls )
+void 
+peepHole (lineNode ** pls)
 {
-    lineNode *spl ; 
-    peepRule *pr ;
-    lineNode *mtail = NULL;
-
- top:   
-    /* for all rules */
-    for (pr = rootRules ; pr ; pr = pr->next ) {
-       for (spl = *pls ; spl ; spl = spl->next ) {
-                   
-           /* if inline assembler then no peep hole */
-           if (spl->isInline)
-               continue ;
-
-           mtail = NULL ;          
-               
-           /* if it matches */
-           if (matchRule (spl,&mtail,pr, *pls)) {
-               
-               /* then replace */ 
-               if (spl == *pls)
-                   replaceRule(pls, mtail, pr);
-               else
-                   replaceRule (&spl, mtail,pr);
-               
-               /* if it was the start then replace
-                  the start */
-               
-               /* if restart rule type then
-                  start at the top again */
-               if (pr->restart)
-                   goto top;
-           }
-       }
+  lineNode *spl;
+  peepRule *pr;
+  lineNode *mtail = NULL;
+  bool restart;
+
+  assert(labelHash == NULL);
+
+  do
+    {
+      restart = FALSE;
+
+      /* for all rules */
+      for (pr = rootRules; pr; pr = pr->next)
+        {
+          for (spl = *pls; spl; spl = spl->next)
+            {
+              /* if inline assembler then no peep hole */
+              if (spl->isInline)
+                continue;
+              
+              mtail = NULL;
+
+              /* Tidy up any data stored in the hTab */
+              
+              /* if it matches */
+              if (matchRule (spl, &mtail, pr, *pls))
+                {
+                  
+                  /* then replace */
+                  if (spl == *pls)
+                    replaceRule (pls, mtail, pr);
+                  else
+                    replaceRule (&spl, mtail, pr);
+                  
+                  /* if restart rule type then
+                     start at the top again */
+                  if (pr->restart)
+                    {
+                      restart = TRUE;
+                    }
+                }
+              
+              if (pr->vars)
+                {
+                  hTabDeleteAll (pr->vars);
+                  Safe_free (pr->vars);
+                  pr->vars = NULL;
+                }
+              
+              freeTrace (&_G.values);
+            }
+        }
+    } while (restart == TRUE);
+
+  if (labelHash)
+    {
+      hTabDeleteAll (labelHash);
+      freeTrace (&_G.labels);
     }
+  labelHash = NULL;
 }
 
 
 /*-----------------------------------------------------------------*/
 /* readFileIntoBuffer - reads a file into a string buffer          */
 /*-----------------------------------------------------------------*/
-static char *readFileIntoBuffer (char *fname)
+static char *
+readFileIntoBuffer (char *fname)
 {
-    FILE *f;
-    char *rs = NULL;       
-    int nch = 0 ;
-    int ch;
-    char lb[MAX_PATTERN_LEN];
-
-    if (!(f = fopen(fname,"r"))) {
-       fprintf(stderr,"cannot open peep rule file\n");
-       return NULL;
-    }
-
-    while ((ch = fgetc(f)) != EOF) {
-       lb[nch++] = ch;
-
-       /* if we maxed out our local buffer */
-       if (nch >= (MAX_PATTERN_LEN - 2)) {
-           lb[nch] = '\0';
-           /* copy it into allocated buffer */
-           if (rs) {
-               rs = GC_realloc(rs,strlen(rs)+strlen(lb)+1);
-               strcat(rs,lb);
-           } else {
-               ALLOC_ATOMIC(rs,strlen(lb)+1);
-               strcpy(rs,lb);
+  FILE *f;
+  char *rs = NULL;
+  int nch = 0;
+  int ch;
+  char lb[MAX_PATTERN_LEN];
+
+  if (!(f = fopen (fname, "r")))
+    {
+      fprintf (stderr, "cannot open peep rule file\n");
+      return NULL;
+    }
+
+  while ((ch = fgetc (f)) != EOF)
+    {
+      lb[nch++] = ch;
+
+      /* if we maxed out our local buffer */
+      if (nch >= (MAX_PATTERN_LEN - 2))
+       {
+         lb[nch] = '\0';
+         /* copy it into allocated buffer */
+         if (rs)
+           {
+             rs = Safe_realloc (rs, strlen (rs) + strlen (lb) + 1);
+             strcat (rs, lb);
+           }
+         else
+           {
+             rs = Safe_alloc ( strlen (lb) + 1);
+             strcpy (rs, lb);
            }
-           nch = 0 ;           
+         nch = 0;
        }
     }
 
-    /* if some charaters left over */
-    if (nch) {
-       lb[nch] = '\0';
-       /* copy it into allocated buffer */
-       if (rs) {
-           rs = GC_realloc(rs,strlen(rs)+strlen(lb)+1);
-           strcat(rs,lb);
-       } else {
-           ALLOC_ATOMIC(rs,strlen(lb)+1);
-           strcpy(rs,lb);
+  /* if some charaters left over */
+  if (nch)
+    {
+      lb[nch] = '\0';
+      /* copy it into allocated buffer */
+      if (rs)
+       {
+         rs = Safe_realloc (rs, strlen (rs) + strlen (lb) + 1);
+         strcat (rs, lb);
+       }
+      else
+       {
+         rs = Safe_alloc ( strlen (lb) + 1);
+         strcpy (rs, lb);
        }
     }
-    return rs;
+  return rs;
 }
 
 /*-----------------------------------------------------------------*/
-/* initPeepHole - initiaises the peep hole optimizer stuff         */
+/* initPeepHole - initialises the peep hole optimizer stuff        */
 /*-----------------------------------------------------------------*/
-void initPeepHole ()
+void 
+initPeepHole ()
 {
-    char *s;
+  char *s;
 
-    /* read in the default rules */
-    readRules(port->peep.default_rules);
+  /* read in the default rules */
+  readRules (port->peep.default_rules);
 
-    /* if we have any additional file read it too */
-    if (options.peep_file) {
-       readRules(s=readFileIntoBuffer(options.peep_file));
-       setToNull((void **) &s);
+  /* if we have any additional file read it too */
+  if (options.peep_file)
+    {
+      readRules (s = readFileIntoBuffer (options.peep_file));
+      setToNull ((void **) &s);
     }
+
+
+#if !OPT_DISABLE_PIC
+  /* Convert the peep rules into pcode.
+     NOTE: this is only support in the PIC port (at the moment)
+  */
+  if (TARGET_IS_PIC) {
+    peepRules2pCode(rootRules);
+  }
+#endif
+
 }