Applied patch #2762516
[fw/sdcc] / src / SDCCpeeph.c
index b06c6f9e6e52f500754936bca8fbd13f89939c22..5a7447fff503ce66a2099c3a4ccd4d7da8c5a905 100644 (file)
 -------------------------------------------------------------------------*/
 
 #include "common.h"
-#include "SDCCpeeph.h"
+#include "dbuf_string.h"
 
-peepRule *rootRules = NULL;
-peepRule *currRule  = NULL;
+#define ISCHARDIGIT(c) isdigit((unsigned char)c)
+#define ISCHARSPACE(c) isspace((unsigned char)c)
+#define ISCHARALNUM(c) isalnum((unsigned char)c)
+
+static peepRule *rootRules = NULL;
+static peepRule *currRule = NULL;
 
 #define HTAB_SIZE 53
-typedef struct
-{
-    char name[SDCC_NAME_MAX + 1];
-    int refCount;
-} labelHashEntry;
 
 hTab *labelHash = NULL;
 
-static int hashSymbolName(const char *name);
-static void buildLabelRefCountHash(lineNode *head);
+static struct
+{
+  allocTrace values;
+  allocTrace labels;
+} _G;
+
+static int hashSymbolName (const char *name);
+static void buildLabelRefCountHash (lineNode * head);
+static void bindVar (int key, char **s, hTab ** vtab);
 
 static bool matchLine (char *, char *, hTab **);
 
-#define FBYNAME(x) int x (hTab *vars, lineNode *currPl, lineNode *head, \
-                         const char *cmdLine)
+#define FBYNAME(x) static int x (hTab *vars, lineNode *currPl, lineNode *endPl, \
+        lineNode *head, char *cmdLine)
+
+#if !OPT_DISABLE_PIC
+void peepRules2pCode(peepRule *);
+#endif
+
+#if !OPT_DISABLE_PIC16
+void pic16_peepRules2pCode(peepRule *);
+#endif
 
 /*-----------------------------------------------------------------*/
-/* pcDistance - afinds a label back ward or forward                */
+/* getPatternVar - finds a pattern variable                        */
 /*-----------------------------------------------------------------*/
-int pcDistance (lineNode *cpos, char *lbl, bool back)
+
+static char*
+getPatternVar (hTab *vars, char **cmdLine)
 {
-    lineNode *pl = cpos;
-    char buff[MAX_PATTERN_LEN];
-    int dist = 0 ;
+  int varNumber;
+  char *digitend;
+
+  if (!cmdLine || !*cmdLine || !**cmdLine)
+    return NULL; /* no parameters given */
+
+  while (**cmdLine && ISCHARSPACE(**cmdLine))
+    (*cmdLine)++; /* skip whitespace */
+
+  if (**cmdLine != '%')
+    goto error;
+  (*cmdLine)++;
+  if (!ISCHARDIGIT (**cmdLine))
+    goto error;
+  varNumber = strtol (*cmdLine, &digitend, 10);
+  *cmdLine = digitend;
+  return hTabItemWithKey (vars, varNumber);
+
+error:
+  fprintf (stderr,
+           "*** internal error: peephole restriction malformed: %s\n", *cmdLine);
+  return NULL;
+}
 
-    sprintf(buff,"%s:",lbl);
-    while (pl) {
+/*-----------------------------------------------------------------*/
+/* pcDistance - finds a label backward or forward                  */
+/*-----------------------------------------------------------------*/
 
-    if (pl->line &&
-        *pl->line != ';' &&
-        pl->line[strlen(pl->line)-1] != ':' &&
-        !pl->isDebug)
+static int
+pcDistance (lineNode * cpos, char *lbl, bool back)
+{
+  lineNode *pl = cpos;
+  char buff[MAX_PATTERN_LEN];
+  int dist = 0;
 
-        dist ++;
+  SNPRINTF (buff, sizeof(buff), "%s:", lbl);
+  while (pl)
+    {
+      if (pl->line &&
+          !pl->isComment &&
+          !pl->isLabel &&
+          !pl->isDebug)
+        {
+          if (port->peep.getSize)
+            {
+              dist += port->peep.getSize(pl);
+            }
+          else
+            {
+              dist += 3;
+            }
+        }
 
-    if (strncmp(pl->line,buff,strlen(buff)) == 0)
+      if (strncmp (pl->line, buff, strlen (buff)) == 0)
         return dist;
 
-    if (back)
+      if (back)
         pl = pl->prev;
-    else
+      else
         pl = pl->next;
 
     }
-    return 0;
+  return 0;
+}
+
+/*-----------------------------------------------------------------*/
+/* portIsDS390 - return true if port is DS390                      */
+/*-----------------------------------------------------------------*/
+FBYNAME (portIsDS390)
+{
+    return ((strcmp(port->target,"ds390") == 0) ||
+            (strcmp(port->target,"ds400") == 0));
 }
 
 /*-----------------------------------------------------------------*/
 /* flat24bitMode - will check to see if we are in flat24 mode      */
 /*-----------------------------------------------------------------*/
-FBYNAME(flat24bitMode)
+FBYNAME (flat24bitMode)
 {
-    return (options.model == MODEL_FLAT24);
+  return (options.model == MODEL_FLAT24);
+}
+
+/*-----------------------------------------------------------------*/
+/* xramMovcOption - check if using movc to read xram               */
+/*-----------------------------------------------------------------*/
+FBYNAME (xramMovcOption)
+{
+  return (options.xram_movc && (strcmp(port->target,"mcs51") == 0));
+}
+
+/*-----------------------------------------------------------------*/
+/* useAcallAjmp - Enable replacement of lcall/ljmp with acall/ajmp */
+/*-----------------------------------------------------------------*/
+FBYNAME (useAcallAjmp)
+{
+  return (options.acall_ajmp && (strcmp(port->target,"mcs51") == 0));
+}
+
+/*-----------------------------------------------------------------*/
+/* labelInRange - will check to see if label is within range       */
+/*-----------------------------------------------------------------*/
+FBYNAME (labelInRange)
+{
+  int dist = 0;
+  char *lbl = getPatternVar (vars, &cmdLine);
+
+  if (!lbl)
+    {
+      /* If no parameters given, assume that %5 pattern variable
+         has the label name for backward compatibility */
+      lbl = hTabItemWithKey (vars, 5);
+    }
+
+  if (!lbl)
+    return FALSE;
+
+  do
+    {
+      /* Don't optimize jumps in a jump table; a more generic test */
+      if (currPl->ic && currPl->ic->op == JUMPTABLE)
+        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 I am 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));
+
+      /* changed to 127, now that pcDistance return actual number of bytes */
+      if (!dist || dist > 127)
+        return FALSE;
+
+      lbl = getPatternVar (vars, &cmdLine);
+    }
+  while (lbl);
+
+  return TRUE;
 }
 
 /*-----------------------------------------------------------------*/
-/* labelInRange - will check to see if label %5 is within range    */
+/* labelJTInRange - will check to see if label %5 and up are       */
+/* within range.                                                   */
+/* Specifically meant to optimize long (3-byte) jumps to short     */
+/* (2-byte) jumps in jumptables                                    */
 /*-----------------------------------------------------------------*/
-FBYNAME(labelInRange)
+FBYNAME (labelJTInRange)
 {
-    /* assumes that %5 pattern variable has the label name */
-    char *lbl = hTabItemWithKey(vars,5);
-    int dist = 0 ;
+  char *lbl;
+  int dist, count, i;
+
+  if (!getenv("SDCC_SJMP_JUMPTABLE"))
+    return FALSE;
+
+  /* Only optimize within a jump table */
+  if (currPl->ic && currPl->ic->op != JUMPTABLE)
+    return FALSE;
+
+  count = elementsInSet( IC_JTLABELS (currPl->ic) );
+
+  /* check all labels (this is needed if the case statements are unsorted) */
+  for (i=0; i<count; i++)
+    {
+      /* assumes that the %5 pattern variable has the first ljmp label */
+      lbl = hTabItemWithKey (vars, 5+i);
+      if (!lbl)
+        return FALSE;
 
-    if (!lbl)
+      dist = pcDistance (currPl, lbl, FALSE);
+
+      /* three terms used to calculate allowable distance */
+// printf("\nlabel %s %i dist %i cdist 0x%02x 0x%02x\n", lbl, i, dist, dist -(count-i-1)-(7+3*i), 127+(count-i-1)+(7+3*i) - dist);
+      if (!dist ||
+          dist > 127+           /* range of sjmp */
+                 (7+3*i)+       /* offset between this jump and currPl,
+                                   should use pcDistance instead? */
+                 (count-i-1)    /* if peephole applies distance is shortened */
+         )
+        return FALSE;
+    }
+  return TRUE;
+}
+
+/*-----------------------------------------------------------------*/
+/* labelIsReturnOnly - Check if label %5 is followed by RET        */
+/*-----------------------------------------------------------------*/
+FBYNAME (labelIsReturnOnly)
+{
+  /* assumes that %5 pattern variable has the label name */
+  const char *label, *p;
+  const lineNode *pl;
+  int len;
+  char * retInst;
+
+  /* Don't optimize jumps in a jump table; a more generic test */
+  if (currPl->ic && currPl->ic->op == JUMPTABLE)
     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) 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)
+  label = hTabItemWithKey (vars, 5);
+  if (!label)
     return FALSE;
+  len = strlen(label);
 
+  for(pl = currPl; pl; pl = pl->next)
+    {
+      if (pl->line && !pl->isDebug && !pl->isComment && pl->isLabel)
+        {
+          if (strncmp(pl->line, label, len) == 0)
+            break; /* Found Label */
+          if (strlen(pl->line) != 7       || !ISCHARDIGIT(*(pl->line))   ||
+              !ISCHARDIGIT(*(pl->line+1)) || !ISCHARDIGIT(*(pl->line+2)) ||
+              !ISCHARDIGIT(*(pl->line+3)) || !ISCHARDIGIT(*(pl->line+4)) ||
+              *(pl->line+5) != '$')
+            {
+              return FALSE; /* non-local label encountered */
+            }
+        }
+    }
+  if (!pl)
+    return FALSE; /* did not find the label */
+  pl = pl->next;
+  while (pl && (pl->isDebug || pl->isComment))
+    pl = pl->next;
+  if (!pl || !pl->line || pl->isDebug)
+    return FALSE; /* next line not valid */
+  p = pl->line;
+  for (p = pl->line; *p && ISCHARSPACE(*p); p++)
+    ;
+
+  retInst = "ret";
+  if (TARGET_IS_HC08)
+    retInst = "rts";
+  if (strcmp(p, retInst) == 0)
     return TRUE;
+  return FALSE;
+}
+
+/*-----------------------------------------------------------------*/
+/* labelIsUncondJump - Check if label %5 is followed by an         */
+/* unconditional jump and put the destination of that jump in %6   */
+/*-----------------------------------------------------------------*/
+FBYNAME (labelIsUncondJump)
+{
+  /* assumes that %5 pattern variable has the label name */
+  const char *label;
+  char *p, *q;
+  const lineNode *pl;
+  int len;
+  char * jpInst = NULL;
+
+  /* Don't optimize jumps in a jump table; a more generic test */
+  if (currPl->ic && currPl->ic->op == JUMPTABLE)
+    return FALSE;
+
+  label = hTabItemWithKey (vars, 5);
+  if (!label)
+    return FALSE;
+  len = strlen(label);
+
+  for (pl = currPl; pl; pl = pl->next)
+    {
+      if (pl->line && !pl->isDebug && !pl->isComment && pl->isLabel)
+        {
+          if (strncmp(pl->line, label, len) == 0)
+            break; /* Found Label */
+          if (strlen(pl->line) != 7       || !ISCHARDIGIT(*(pl->line))   ||
+              !ISCHARDIGIT(*(pl->line+1)) || !ISCHARDIGIT(*(pl->line+2)) ||
+              !ISCHARDIGIT(*(pl->line+3)) || !ISCHARDIGIT(*(pl->line+4)) ||
+              *(pl->line+5) != '$')
+            {
+              return FALSE; /* non-local label encountered */
+            }
+        }
+    }
+  if (!pl)
+    return FALSE; /* did not find the label */
+  pl = pl->next;
+  while (pl && (pl->isDebug || pl->isComment))
+    pl = pl->next;
+  if (!pl || !pl->line)
+    return FALSE; /* next line not valid */
+  p = pl->line;
+  while (*p && ISCHARSPACE(*p))
+    p++;
+
+  if (TARGET_MCS51_LIKE)
+    jpInst = "ljmp";
+  if (TARGET_IS_HC08)
+    jpInst = "jmp";
+  if (TARGET_Z80_LIKE)
+    jpInst = "jp";
+  len = strlen(jpInst);
+  if (strncmp(p, jpInst, len) != 0)
+    return FALSE; /* next line is no jump */
+  p += len;
+  while (*p && ISCHARSPACE(*p))
+    p++;
+
+  q = p;
+  while (*q && *q!=';')
+    q++;
+  while (q>p && ISCHARSPACE(*q))
+    q--;
+  len = q-p;
+  if (len == 0)
+    return FALSE; /* no destination? */
+  if (TARGET_Z80_LIKE)
+    {
+      while (q>p && *q!=',')
+        q--;
+      if (*q==',')
+        return FALSE; /* conditional jump */
+    }
+  if (strcmp(p, q) == 0)
+    return FALSE; /* labels are equal */
+  /* now put the destination in %6 */
+  bindVar (6, &p, &vars);
+  return TRUE;
+}
+
+/*-----------------------------------------------------------------*/
+/* okToRemoveSLOC - Check if label %1 is a SLOC and not other      */
+/* usage of it in the code depends on a value from this section    */
+/*-----------------------------------------------------------------*/
+FBYNAME (okToRemoveSLOC)
+{
+  const lineNode *pl;
+  const char *sloc, *p;
+  int dummy1, dummy2, dummy3;
+
+  /* assumes that %1 as the SLOC name */
+  sloc = hTabItemWithKey (vars, 1);
+  if (sloc == NULL) return FALSE;
+  p = strstr(sloc, "sloc");
+  if (p == NULL) return FALSE;
+  p += 4;
+  if (sscanf(p, "%d_%d_%d", &dummy1, &dummy2, &dummy3) != 3) return FALSE;
+  /*TODO: ultra-paranoid: get funtion name from "head" and check that */
+  /* the sloc name begins with that.  Probably not really necessary */
+
+  /* Look for any occurance of this SLOC before the peephole match */
+  for (pl = currPl->prev; pl; pl = pl->prev) {
+        if (pl->line && !pl->isDebug && !pl->isComment
+          && *pl->line != ';' && strstr(pl->line, sloc))
+                return FALSE;
+  }
+  /* Look for any occurance of this SLOC after the peephole match */
+  for (pl = endPl->next; pl; pl = pl->next) {
+        if (pl->line && !pl->isDebug && !pl->isComment
+          && *pl->line != ';' && strstr(pl->line, sloc))
+                return FALSE;
+  }
+  return TRUE; /* safe for a peephole to remove it :) */
+}
+
+/*-----------------------------------------------------------------*/
+/* deadMove - Check, if a pop/push pair can be removed             */
+/*-----------------------------------------------------------------*/
+FBYNAME (deadMove)
+{
+  const char *reg = hTabItemWithKey (vars, 1);
+
+  if (port->peep.deadMove)
+    return port->peep.deadMove (reg, currPl, head);
+
+  fprintf (stderr, "Function deadMove not initialized in port structure\n");
+  return FALSE;
+}
+
+/*-----------------------------------------------------------------*/
+/* notUsed - Check, if value in register is not read again         */
+/*-----------------------------------------------------------------*/
+FBYNAME (notUsed)
+{
+  const char *what;
+
+  if(cmdLine[0] != '\'')
+    what = hTabItemWithKey (vars, 1);
+  else
+  {
+    cmdLine[strlen(cmdLine) - 1] = 0;
+    what = cmdLine + 1;
+  }
+
+  if (port->peep.notUsed)
+    return port->peep.notUsed (what, endPl, head);
+
+  fprintf (stderr, "Function notUsed not initialized in port structure\n");
+  return FALSE;
 }
 
 /*-----------------------------------------------------------------*/
 /* 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)
+  if (strcmp (op1, op2) == 0)
     return FALSE;
-    else
+  else
     return TRUE;
 }
 
-/* labelRefCount: 
- *
+/*-----------------------------------------------------------------*/
+/* operandsNotSame3- check if any pair of %1,%2,%3 are the same    */
+/*-----------------------------------------------------------------*/
+FBYNAME (operandsNotSame3)
+{
+  char *op1 = hTabItemWithKey (vars, 1);
+  char *op2 = hTabItemWithKey (vars, 2);
+  char *op3 = hTabItemWithKey (vars, 3);
+
+  if ( (strcmp (op1, op2) == 0) ||
+       (strcmp (op1, op3) == 0) ||
+       (strcmp (op2, op3) == 0) )
+    return FALSE;
+  else
+    return TRUE;
+}
+
+/*-----------------------------------------------------------------*/
+/* operandsNotSame4- check if any pair of %1,%2,%3,.. are the same */
+/*-----------------------------------------------------------------*/
+FBYNAME (operandsNotSame4)
+{
+  char *op1 = hTabItemWithKey (vars, 1);
+  char *op2 = hTabItemWithKey (vars, 2);
+  char *op3 = hTabItemWithKey (vars, 3);
+  char *op4 = hTabItemWithKey (vars, 4);
+
+  if ( (strcmp (op1, op2) == 0) ||
+       (strcmp (op1, op3) == 0) ||
+       (strcmp (op1, op4) == 0) ||
+       (strcmp (op2, op3) == 0) ||
+       (strcmp (op2, op4) == 0) ||
+       (strcmp (op3, op4) == 0) )
+    return FALSE;
+  else
+    return TRUE;
+}
+
+/*-----------------------------------------------------------------*/
+/* operandsNotSame5- check if any pair of %1,%2,%3,.. are the same */
+/*-----------------------------------------------------------------*/
+FBYNAME (operandsNotSame5)
+{
+  char *op1 = hTabItemWithKey (vars, 1);
+  char *op2 = hTabItemWithKey (vars, 2);
+  char *op3 = hTabItemWithKey (vars, 3);
+  char *op4 = hTabItemWithKey (vars, 4);
+  char *op5 = hTabItemWithKey (vars, 5);
+
+  if ( (strcmp (op1, op2) == 0) ||
+       (strcmp (op1, op3) == 0) ||
+       (strcmp (op1, op4) == 0) ||
+       (strcmp (op1, op5) == 0) ||
+       (strcmp (op2, op3) == 0) ||
+       (strcmp (op2, op4) == 0) ||
+       (strcmp (op2, op5) == 0) ||
+       (strcmp (op3, op4) == 0) ||
+       (strcmp (op3, op5) == 0) ||
+       (strcmp (op4, op5) == 0) )
+    return FALSE;
+  else
+    return TRUE;
+}
+
+/*-----------------------------------------------------------------*/
+/* operandsNotSame6- check if any pair of %1,%2,%3,.. are the same */
+/*-----------------------------------------------------------------*/
+FBYNAME (operandsNotSame6)
+{
+  char *op1 = hTabItemWithKey (vars, 1);
+  char *op2 = hTabItemWithKey (vars, 2);
+  char *op3 = hTabItemWithKey (vars, 3);
+  char *op4 = hTabItemWithKey (vars, 4);
+  char *op5 = hTabItemWithKey (vars, 5);
+  char *op6 = hTabItemWithKey (vars, 6);
+
+  if ( (strcmp (op1, op2) == 0) ||
+       (strcmp (op1, op3) == 0) ||
+       (strcmp (op1, op4) == 0) ||
+       (strcmp (op1, op5) == 0) ||
+       (strcmp (op1, op6) == 0) ||
+       (strcmp (op2, op3) == 0) ||
+       (strcmp (op2, op4) == 0) ||
+       (strcmp (op2, op5) == 0) ||
+       (strcmp (op2, op6) == 0) ||
+       (strcmp (op3, op4) == 0) ||
+       (strcmp (op3, op5) == 0) ||
+       (strcmp (op3, op6) == 0) ||
+       (strcmp (op4, op5) == 0) ||
+       (strcmp (op4, op6) == 0) ||
+       (strcmp (op5, op6) == 0) )
+    return FALSE;
+  else
+    return TRUE;
+}
+
+/*-----------------------------------------------------------------*/
+/* operandsNotSame7- check if any pair of %1,%2,%3,.. are the same */
+/*-----------------------------------------------------------------*/
+FBYNAME (operandsNotSame7)
+{
+  char *op1 = hTabItemWithKey (vars, 1);
+  char *op2 = hTabItemWithKey (vars, 2);
+  char *op3 = hTabItemWithKey (vars, 3);
+  char *op4 = hTabItemWithKey (vars, 4);
+  char *op5 = hTabItemWithKey (vars, 5);
+  char *op6 = hTabItemWithKey (vars, 6);
+  char *op7 = hTabItemWithKey (vars, 7);
+
+  if ( (strcmp (op1, op2) == 0) ||
+       (strcmp (op1, op3) == 0) ||
+       (strcmp (op1, op4) == 0) ||
+       (strcmp (op1, op5) == 0) ||
+       (strcmp (op1, op6) == 0) ||
+       (strcmp (op1, op7) == 0) ||
+       (strcmp (op2, op3) == 0) ||
+       (strcmp (op2, op4) == 0) ||
+       (strcmp (op2, op5) == 0) ||
+       (strcmp (op2, op6) == 0) ||
+       (strcmp (op2, op7) == 0) ||
+       (strcmp (op3, op4) == 0) ||
+       (strcmp (op3, op5) == 0) ||
+       (strcmp (op3, op6) == 0) ||
+       (strcmp (op3, op7) == 0) ||
+       (strcmp (op4, op5) == 0) ||
+       (strcmp (op4, op6) == 0) ||
+       (strcmp (op4, op7) == 0) ||
+       (strcmp (op5, op6) == 0) ||
+       (strcmp (op5, op7) == 0) ||
+       (strcmp (op6, op7) == 0) )
+    return FALSE;
+  else
+    return TRUE;
+}
+
+/*-----------------------------------------------------------------*/
+/* operandsNotSame8- check if any pair of %1,%2,%3,.. are the same */
+/*-----------------------------------------------------------------*/
+FBYNAME (operandsNotSame8)
+{
+  char *op1 = hTabItemWithKey (vars, 1);
+  char *op2 = hTabItemWithKey (vars, 2);
+  char *op3 = hTabItemWithKey (vars, 3);
+  char *op4 = hTabItemWithKey (vars, 4);
+  char *op5 = hTabItemWithKey (vars, 5);
+  char *op6 = hTabItemWithKey (vars, 6);
+  char *op7 = hTabItemWithKey (vars, 7);
+  char *op8 = hTabItemWithKey (vars, 8);
+
+  if ( (strcmp (op1, op2) == 0) ||
+       (strcmp (op1, op3) == 0) ||
+       (strcmp (op1, op4) == 0) ||
+       (strcmp (op1, op5) == 0) ||
+       (strcmp (op1, op6) == 0) ||
+       (strcmp (op1, op7) == 0) ||
+       (strcmp (op1, op8) == 0) ||
+       (strcmp (op2, op3) == 0) ||
+       (strcmp (op2, op4) == 0) ||
+       (strcmp (op2, op5) == 0) ||
+       (strcmp (op2, op6) == 0) ||
+       (strcmp (op2, op7) == 0) ||
+       (strcmp (op2, op8) == 0) ||
+       (strcmp (op3, op4) == 0) ||
+       (strcmp (op3, op5) == 0) ||
+       (strcmp (op3, op6) == 0) ||
+       (strcmp (op3, op7) == 0) ||
+       (strcmp (op3, op8) == 0) ||
+       (strcmp (op4, op5) == 0) ||
+       (strcmp (op4, op6) == 0) ||
+       (strcmp (op4, op7) == 0) ||
+       (strcmp (op4, op8) == 0) ||
+       (strcmp (op5, op6) == 0) ||
+       (strcmp (op5, op7) == 0) ||
+       (strcmp (op5, op8) == 0) ||
+       (strcmp (op6, op7) == 0) ||
+       (strcmp (op6, op8) == 0) ||
+       (strcmp (op7, op8) == 0) )
+    return FALSE;
+  else
+    return TRUE;
+}
+
+/*-----------------------------------------------------------------*/
+/* labelHashEntry- searches for a label in the list labelHash      */
+/* Builds labelHash, if it does not yet exist.                     */
+/* Returns the labelHashEntry or NULL                              */
+/*-----------------------------------------------------------------*/
+labelHashEntry *
+getLabelRef (const char *label, lineNode *head)
+{
+  labelHashEntry *entry;
+
+  /* If we don't have the label hash table yet, build it. */
+  if (!labelHash)
+    {
+      buildLabelRefCountHash (head);
+    }
+
+  entry = hTabFirstItemWK (labelHash, hashSymbolName (label));
+
+  while (entry)
+    {
+      if (!strcmp (label, entry->name))
+        {
+          break;
+        }
+      entry = hTabNextItemWK (labelHash);
+    }
+  return entry;
+}
+
+/* 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)
-           {
+ */
+FBYNAME (labelRefCount)
+{
+  int varNumber, expectedRefCount;
+  bool rc = FALSE;
+
+  if (sscanf (cmdLine, "%*[ \t%]%d %d", &varNumber, &expectedRefCount) == 2)
+    {
+      char *label = hTabItemWithKey (vars, varNumber);
+
+      if (label)
+        {
+          labelHashEntry *entry = getLabelRef (label, head);
+
+          if (entry)
+            {
 #if 0
-               /* debug spew. */              
-               fprintf(stderr, "labelRefCount: %s has refCount %d, want %d\n",
-                       label, entry->refCount, expectedRefCount);
+              /* 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);
-           }
+
+              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;
+}
+
+/* labelRefCountChange:
+ * takes two parameters: a variable (bound to a label name)
+ * and a signed int for changing the reference count.
+ *
+ * Please note, this function is not a conditional. It unconditionally
+ * changes the label. It should be passed as the 'last' function
+ * so it only is applied if all other conditions have been met.
+ *
+ * should always return TRUE
+ */
+FBYNAME (labelRefCountChange)
+{
+  int varNumber, RefCountDelta;
+  bool rc = FALSE;
+
+  /* If we don't have the label hash table yet, build it. */
+  if (!labelHash)
+    {
+      buildLabelRefCountHash (head);
+    }
+
+  if (sscanf (cmdLine, "%*[ \t%]%d %i", &varNumber, &RefCountDelta) == 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 <= entry->refCount + RefCountDelta)
+                {
+                  entry->refCount += RefCountDelta;
+                  rc = TRUE;
+                }
+              else
+                {
+                  fprintf (stderr, "*** internal error: label %s may not get"
+                          " negative refCount in %s peephole.\n",
+                           label, __FUNCTION__);
+                }
+            }
+            else
+            {
+              fprintf (stderr, "*** internal error: no label has entry for"
+                       " %s in %s peephole.\n",
+                       label, __FUNCTION__);
+            }
+        }
+      else
+        {
+          fprintf (stderr, "*** internal error: var %d not bound"
+                   " in peephole %s rule.\n",
+                   varNumber, __FUNCTION__);
+        }
+    }
+  else
+    {
+      fprintf (stderr,
+               "*** internal error: labelRefCount peephole restriction"
+               " malformed: %s\n", cmdLine);
+    }
+  return rc;
+}
+
+/* Within the context of the lines currPl through endPl, determine
+** if the variable var contains a symbol that is volatile. Returns
+** TRUE only if it is certain that this was not volatile (the symbol
+** was found and not volatile, or var was a constant or CPU register).
+** Returns FALSE if the symbol was found and volatile, the symbol was
+** not found, or var was a indirect/pointer addressing mode.
+*/
+static bool
+notVolatileVariable(char *var, lineNode *currPl, lineNode *endPl)
+{
+  char symname[SDCC_NAME_MAX + 1];
+  char *p = symname;
+  char *vp = var;
+  lineNode *cl;
+  operand *op;
+  iCode *last_ic;
+
+  /* Can't tell if indirect accesses are volatile or not, so
+  ** assume they are, just to be safe.
+  */
+  if (TARGET_IS_MCS51 || TARGET_IS_DS390 || TARGET_IS_DS400)
+    {
+      if (*var=='@')
+        return FALSE;
+    }
+  if (TARGET_IS_Z80 || TARGET_IS_GBZ80)
+    {
+      if (strstr(var,"(bc)"))
+        return FALSE;
+      if (strstr(var,"(de)"))
+        return FALSE;
+      if (strstr(var,"(hl)"))
+        return FALSE;
+      if (strstr(var,"(ix"))
+        return FALSE;
+      if (strstr(var,"(iy"))
+        return FALSE;
+    }
+
+  /* Extract a symbol name from the variable */
+  while (*vp && (*vp!='_'))
+    vp++;
+  while (*vp && (ISCHARALNUM(*vp) || *vp=='_'))
+    *p++ = *vp++;
+  *p='\0';
+
+  if (!symname[0])
+    {
+      /* Nothing resembling a symbol name was found, so it can't
+         be volatile
+      */
+      return TRUE;
+    }
+
+  last_ic = NULL;
+  for (cl = currPl; cl!=endPl->next; cl = cl->next)
+  {
+    if (cl->ic && (cl->ic!=last_ic))
+      {
+        last_ic = cl->ic;
+        switch (cl->ic->op)
+          {
+          case IFX:
+            op = IC_COND (cl->ic);
+            if (IS_SYMOP (op) &&
+                ( !strcmp(OP_SYMBOL (op)->rname, symname) ||
+                  (OP_SYMBOL (op)->isspilt &&
+                   SPIL_LOC (op) &&
+                   !strcmp(SPIL_LOC (op)->rname, symname)) ))
+              {
+                return !op->isvolatile;
+              }
+          case JUMPTABLE:
+            op = IC_JTCOND (cl->ic);
+            if (IS_SYMOP (op) &&
+                ( !strcmp(OP_SYMBOL (op)->rname, symname) ||
+                  (OP_SYMBOL (op)->isspilt &&
+                   SPIL_LOC (op) &&
+                   !strcmp(SPIL_LOC (op)->rname, symname)) ))
+              {
+                return !op->isvolatile;
+              }
+          default:
+            op = IC_LEFT (cl->ic);
+            if (IS_SYMOP (op) &&
+                ( !strcmp(OP_SYMBOL (op)->rname, symname) ||
+                  (OP_SYMBOL (op)->isspilt &&
+                   SPIL_LOC (op) &&
+                   !strcmp(SPIL_LOC (op)->rname, symname)) ))
+              {
+                return !op->isvolatile;
+              }
+            op = IC_RIGHT (cl->ic);
+            if (IS_SYMOP (op) &&
+                ( !strcmp(OP_SYMBOL (op)->rname, symname) ||
+                  (OP_SYMBOL (op)->isspilt &&
+                   SPIL_LOC (op) &&
+                   !strcmp(SPIL_LOC (op)->rname, symname)) ))
+              {
+                return !op->isvolatile;
+              }
+            op = IC_RESULT (cl->ic);
+            if (IS_SYMOP (op) &&
+                ( !strcmp(OP_SYMBOL (op)->rname, symname) ||
+                  (OP_SYMBOL (op)->isspilt &&
+                   SPIL_LOC (op) &&
+                   !strcmp(SPIL_LOC (op)->rname, symname)) ))
+              {
+                return !op->isvolatile;
+              }
+          }
+      }
+  }
+
+  /* Couldn't find the symbol for some reason. Assume volatile. */
+  return FALSE;
+}
+
+/*  notVolatile:
+ *
+ *  This rule restriction has two different behaviours depending on
+ *  the number of parameters given.
+ *
+ *    if notVolatile                 (no parameters given)
+ *       The rule is applied only if none of the iCodes originating
+ *       the matched pattern reference a volatile operand.
+ *
+ *    if notVolatile %1 ...          (one or more parameters given)
+ *       The rule is applied if the parameters are not expressions
+ *       containing volatile symbols and are not pointer accesses.
+ *
+ */
+FBYNAME (notVolatile)
+{
+  int varNumber;
+  char *var;
+  bool notvol;
+  char *digitend;
+  lineNode *cl;
+  operand *op;
+
+  if (!cmdLine)
+    {
+      /* If no parameters given, just scan the iCodes for volatile operands */
+      for (cl = currPl; cl!=endPl->next; cl = cl->next)
+      {
+        if (cl->ic)
+          {
+            switch (cl->ic->op)
+              {
+              case IFX:
+                op = IC_COND (cl->ic);
+                if (IS_SYMOP (op) && op->isvolatile)
+                  return FALSE;
+              case JUMPTABLE:
+                op = IC_JTCOND (cl->ic);
+                if (IS_SYMOP (op) && op->isvolatile)
+                  return FALSE;
+              default:
+                op = IC_LEFT (cl->ic);
+                if (IS_SYMOP (op) && op->isvolatile)
+                  return FALSE;
+                op = IC_RIGHT (cl->ic);
+                if (IS_SYMOP (op) && op->isvolatile)
+                  return FALSE;
+                op = IC_RESULT (cl->ic);
+                if (IS_SYMOP (op) && op->isvolatile)
+                  return FALSE;
+              }
+          }
+      }
+      return TRUE;
+    }
+
+  /* There were parameters; check the volatility of each */
+  while (*cmdLine && ISCHARSPACE(*cmdLine))
+    cmdLine++;
+  while (*cmdLine)
+    {
+      if (*cmdLine!='%')
+        goto error;
+      cmdLine++;
+      if (!ISCHARDIGIT(*cmdLine))
+        goto error;
+      varNumber = strtol(cmdLine, &digitend, 10);
+      cmdLine = digitend;
+      while (*cmdLine && ISCHARSPACE(*cmdLine))
+        cmdLine++;
+
+      var = hTabItemWithKey (vars, varNumber);
+
+      if (var)
+        {
+          notvol = notVolatileVariable (var, currPl, endPl);
+          if (!notvol)
+            return FALSE;
+        }
+      else
+        {
+          fprintf (stderr, "*** internal error: var %d not bound"
+                   " in peephole notVolatile rule.\n",
+                   varNumber);
+          return FALSE;
+        }
+    }
+
+  return TRUE;
+
+error:
+  fprintf (stderr,
+           "*** internal error: notVolatile peephole restriction"
+           " malformed: %s\n", cmdLine);
+  return FALSE;
+}
+
+/*------------------------------------------------------------------*/
+/* setFromConditionArgs - parse a peephole condition's arguments    */
+/* to produce a set of strings, one per argument. Variables %x will */
+/* be replaced with their values. String literals (in single quotes)*/
+/* are accepted and return in unquoted form.                         */
+/*------------------------------------------------------------------*/
+static set *
+setFromConditionArgs (char *cmdLine, hTab * vars)
+{
+  int varNumber;
+  char *var;
+  char *digitend;
+  set *operands = NULL;
+
+  if (!cmdLine)
+    return NULL;
+
+  while (*cmdLine && ISCHARSPACE(*cmdLine))
+    cmdLine++;
+
+  while (*cmdLine)
+    {
+      if (*cmdLine == '%')
+        {
+          cmdLine++;
+          if (!ISCHARDIGIT(*cmdLine))
+            goto error;
+          varNumber = strtol(cmdLine, &digitend, 10);
+          cmdLine = digitend;
+
+          var = hTabItemWithKey (vars, varNumber);
+
+          if (var)
+            {
+              addSetHead (&operands, var);
+            }
+          else
+            goto error;
+        }
+      else if (*cmdLine == '\'' )
+        {
+          char quote = *cmdLine;
+
+          var = ++cmdLine;
+          while (*cmdLine && *cmdLine != quote)
+            cmdLine++;
+          if (*cmdLine == quote)
+            *cmdLine++ = '\0';
+          else
+            goto error;
+          addSetHead (&operands, var);
+        }
+      else
+        goto error;
+
+      while (*cmdLine && ISCHARSPACE(*cmdLine))
+        cmdLine++;
+    }
+
+  return operands;
+
+error:
+  deleteSet (&operands);
+  return NULL;
+}
+
+static const char *
+operandBaseName (const char *op)
+{
+  if (TARGET_IS_MCS51 || TARGET_IS_DS390 || TARGET_IS_DS400)
+    {
+      if (!strcmp (op, "acc") || !strncmp (op, "acc.", 4))
+        return "a";
+      if (!strncmp (op, "ar", 2) && ISCHARDIGIT(*(op+2)) && !*(op+3))
+        return op+1;
+      // bug 1739475, temp fix
+      if (op[0] == '@')
+        return operandBaseName(op+1);
+    }
+
+  return op;
+}
+
+/*-------------------------------------------------------------------*/
+/* operandsNotRelated - returns true if the condition's operands are */
+/* not related (taking into account register name aliases). N-way    */
+/* comparison performed between all operands.                        */
+/*-------------------------------------------------------------------*/
+FBYNAME (operandsNotRelated)
+{
+  set *operands;
+  const char *op1, *op2;
+
+  operands = setFromConditionArgs (cmdLine, vars);
+
+  if (!operands)
+    {
+      fprintf (stderr,
+               "*** internal error: operandsNotRelated peephole restriction"
+               " malformed: %s\n", cmdLine);
+      return FALSE;
+    }
+
+  while ((op1 = setFirstItem (operands)))
+    {
+      deleteSetItem (&operands, (void*)op1);
+      op1 = operandBaseName (op1);
+
+      for (op2 = setFirstItem (operands); op2; op2 = setNextItem (operands))
+        {
+          op2 = operandBaseName (op2);
+          if (strcmp (op1, op2) == 0)
+            {
+              deleteSet (&operands);
+              return FALSE;
+            }
+        }
+    }
+
+  deleteSet (&operands);
+  return TRUE;
+}
+
+/*-------------------------------------------------------------------*/
+/* operandsLiteral - returns true of the condition's operands are    */
+/* literals.                                                         */
+/*-------------------------------------------------------------------*/
+FBYNAME (operandsLiteral)
+{
+  set *operands;
+  const char *op;
+
+  operands = setFromConditionArgs (cmdLine, vars);
+
+  if (!operands)
+    {
+      fprintf (stderr,
+               "*** internal error: operandsLiteral peephole restriction"
+               " malformed: %s\n", cmdLine);
+      return FALSE;
+    }
+
+  for (op = setFirstItem (operands); op; op = setNextItem (operands))
+    {
+      if (!isdigit(*op))
+        {
+          deleteSet (&operands);
+          return FALSE;
+        }
+    }
+
+  deleteSet (&operands);
+  return TRUE;
+}
+
+static const struct ftab
+{
+  char *fname;
+  int (*func) (hTab *, lineNode *, lineNode *, lineNode *, char *);
+}
+ftab[] =                                // sorted on the number of times used
+{                                       // in the peephole rules on 2007-10-29
+  {
+    "labelRefCount", labelRefCount                  //105
+  },
+  {
+    "notVolatile", notVolatile                      //85
+  },
+  {
+    "labelRefCountChange", labelRefCountChange      //74
+  },
+  {
+    "labelInRange", labelInRange                    //37
+  },
+  {
+    "labelJTInRange", labelJTInRange                //13
+  },
+  {
+    "operandsNotRelated", operandsNotRelated        //9
+  },
+  {
+    "24bitMode", flat24bitMode                      //9
+  },
+  {
+    "operandsNotSame", operandsNotSame              //8
+  },
+  {
+    "operandsNotSame3", operandsNotSame3
+  },
+  {
+    "operandsNotSame4", operandsNotSame4
+  },
+  {
+    "operandsNotSame5", operandsNotSame5
+  },
+  {
+    "operandsNotSame6", operandsNotSame6
+  },
+  {
+    "operandsNotSame7", operandsNotSame7
+  },
+  {
+    "operandsNotSame8", operandsNotSame8
+  },
+  {
+    "xramMovcOption", xramMovcOption
+  },
+  {
+    "portIsDS390", portIsDS390
+  },
+  {
+    "labelIsReturnOnly", labelIsReturnOnly
+  },
+  {
+    "labelIsUncondJump", labelIsUncondJump
+  },
+  {
+    "okToRemoveSLOC", okToRemoveSLOC
+  },
+  {
+    "deadMove", deadMove
+  },
+  {
+    "operandsLiteral", operandsLiteral
+  },
+  {
+    "useAcallAjmp", useAcallAjmp
+  },
+  {
+    "notUsed", notUsed
+  }
+};
+/*-----------------------------------------------------------------*/
+/* callFuncByName - calls a function as defined in the table       */
+/*-----------------------------------------------------------------*/
+static int
+callFuncByName (char *fname,
+                hTab * vars,
+                lineNode * currPl, /* first source line matched */
+                lineNode * endPl,  /* last source line matched */
+                lineNode * head)
+{
+  int   i;
+  char  *cmdCopy, *funcName, *funcArgs, *cmdTerm;
+  char  c;
+  int   rc;
+
+  /* Isolate the function name part (we are passed the full condition
+   * string including arguments)
+   */
+  cmdTerm = cmdCopy = Safe_strdup(fname);
+
+  do
+    {
+      funcArgs = funcName = cmdTerm;
+      while ((c = *funcArgs) && c != ' ' && c != '\t' && c != '(')
+        funcArgs++;
+      *funcArgs = '\0';  /* terminate the function name */
+      if (c)
+        funcArgs++;
+
+      /* Find the start of the arguments */
+      if (c == ' ' || c == '\t')
+        while ((c = *funcArgs) && (c == ' ' || c == '\t'))
+          funcArgs++;
+
+      /* If the arguments started with an opening parenthesis,  */
+      /* use the closing parenthesis for the end of the         */
+      /* arguments and look for the start of another condition  */
+      /* that can optionally follow. If there was no opening    */
+      /* parethesis, then everything that follows are arguments */
+      /* and there can be no additional conditions.             */
+      if (c == '(')
+        {
+
+          int num_parenthesis = 0;
+          cmdTerm = funcArgs;          
+
+          while ((c = *cmdTerm) && (c != ')' || num_parenthesis))
+            {
+              if (c == '(')
+                num_parenthesis++;
+              else if (c == ')')
+                num_parenthesis--;
+              cmdTerm++;
+            }
+          *cmdTerm = '\0';  /* terminate the arguments */
+          if (c == ')')
+            {
+              cmdTerm++;
+              while ((c = *cmdTerm) && (c == ' ' || c == '\t' || c == ','))
+                cmdTerm++;
+              if (!*cmdTerm)
+                cmdTerm = NULL;
+            }
+          else
+            cmdTerm = NULL; /* closing parenthesis missing */
         }
-        else
+      else
+        cmdTerm = NULL;
+
+      if (!*funcArgs)
+        funcArgs = NULL;
+
+      rc = -1;
+      for (i = 0; i < ((sizeof (ftab)) / (sizeof (struct ftab))); i++)
         {
-            fprintf(stderr, "*** internal error: var %d not bound"
-                           " in peephole labelRefCount rule.\n",
-                           varNumber);
+          if (strcmp (ftab[i].fname, funcName) == 0)
+            {
+              rc = (*ftab[i].func) (vars, currPl, endPl, head, funcArgs);
+              break;
+            }
+        }
+
+      if (rc == -1)
+        {
+          fprintf (stderr,
+                   "could not find named function \"%s\" in "
+                   "peephole function table\n",
+                   funcName);
+          // If the function couldn't be found, let's assume it's
+          // a bad rule and refuse it.
+          rc = FALSE;
+          break;
         }
-        
-    }
-    else
-    {
-         fprintf(stderr, 
-                 "*** internal error: labelRefCount peephole restriction"
-                 " malformed: %s\n", cmdLine);
     }
-    return rc;
-}
+  while (rc && cmdTerm);
 
-/*-----------------------------------------------------------------*/
-/* callFuncByName - calls a function as defined in the table       */
-/*-----------------------------------------------------------------*/
-int callFuncByName ( char *fname,
-             hTab *vars,
-             lineNode *currPl,
-             lineNode *head)
-{
-    struct ftab 
-    {
-       char *fname ;
-       int (*func)(hTab *,lineNode *,lineNode *,const char *) ;
-    }  ftab[] = 
-    {
-       {"labelInRange",   labelInRange },
-       {"operandsNotSame", operandsNotSame },
-       {"24bitMode", flat24bitMode },
-       {"labelRefCount", labelRefCount },
-    };
-    int i;
+  Safe_free(cmdCopy);
 
-    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;
+  return rc;
 }
 
 /*-----------------------------------------------------------------*/
 /* printLine - prints a line chain into a given file               */
 /*-----------------------------------------------------------------*/
-void printLine (lineNode *head, FILE *of)
+void
+printLine (lineNode * head, struct dbuf_s * oBuf)
 {
-    if (!of)
-    of = stdout ;
+  iCode *last_ic = NULL;
+  bool debug_iCode_tracking = (getenv("DEBUG_ICODE_TRACKING")!=NULL);
+
+  while (head)
+    {
+      if (head->ic!=last_ic)
+        {
+          last_ic = head->ic;
+          if (debug_iCode_tracking)
+            {
+              if (head->ic)
+                dbuf_printf (oBuf, "; block = %d, seq = %d\n",
+                         head->ic->block, head->ic->seq);
+              else
+                dbuf_append_str (oBuf, "; iCode lost\n");
+            }
+        }
 
-    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;
+      /* don't indent comments & labels */
+      if (head->line &&
+          (head->isComment || head->isLabel)) {
+        dbuf_printf (oBuf, "%s\n", head->line);
+      } else {
+        if (head->isInline && *head->line=='#') {
+          // comment out preprocessor directives in inline asm
+          dbuf_append_char (oBuf, ';');
+        }
+        dbuf_printf (oBuf, "\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)
+static peepRule *
+newPeepRule (lineNode * match,
+             lineNode * replace,
+             char *cond,
+             int restart)
 {
-    peepRule *pr ;
+  peepRule *pr;
 
-    ALLOC(pr,sizeof(peepRule));
-    pr->match = match;
-    pr->replace= replace;
-    pr->restart = restart;
+  pr = Safe_alloc ( 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 ;
+  if (cond && *cond)
+    {
+      pr->cond = Safe_strdup (cond);
+    }
+  else
+    pr->cond = NULL;
 
-    pr->vars = newHashTable(100);
+  pr->vars = newHashTable (100);
 
-    /* if root is empty */
-    if (!rootRules)
+  /* if root is empty */
+  if (!rootRules)
     rootRules = currRule = pr;
-    else
+  else
     currRule = currRule->next = pr;
 
-    return pr;
+  return pr;
 }
 
 /*-----------------------------------------------------------------*/
 /* newLineNode - creates a new peep line                           */
 /*-----------------------------------------------------------------*/
-lineNode *newLineNode (char *line)
+lineNode *
+newLineNode (const 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_strdup (line);
+  pl->ic = NULL;
+  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++; \
+#define SKIP_SPACE(x,y) { while (*x && (ISCHARSPACE(*x) || *x == '\n')) x++; \
                          if (!*x) { fprintf(stderr,y); return ; } }
 
 #define EXPECT_STR(x,y,z) { while (*x && strncmp(x,y,strlen(y))) x++ ;   \
@@ -332,623 +1487,1041 @@ 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;
+  char lines[MAX_PATTERN_LEN];
+  char *lp;
+  int isComment;
 
-    lineNode *currL = NULL ;
-    char *bp = *bpp;
-    while (1) {
+  lineNode *currL = NULL;
+  char *bp = *bpp;
+  while (1)
+    {
 
-    if (!*bp) {
-        fprintf(stderr,"unexpected end of match pattern\n");
-        return ;
-    }
+      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 (ISCHARSPACE (*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';
+
+      lp = lines;
+      while (*lp && ISCHARSPACE(*lp))
+        lp++;
+      isComment = (*lp == ';');
 
-    /* read till end of line */
-    lp = lines ;
-    while ((*bp != '\n' && *bp != '}' ) && *bp)
-        *lp++ = *bp++ ;
+      if (!isComment || (isComment && !options.noPeepComments))
+        {
+          const char *dummy1;
+          int dummy2;
+
+          if (!currL)
+            *head = currL = newLineNode (lines);
+          else
+            currL = connectLine (currL, newLineNode (lines));
+          currL->isComment = isComment;
+          currL->isLabel = isLabelDefinition (currL->line, &dummy1, &dummy2,
+                                              TRUE);
+        }
 
-    *lp = '\0';
-    if (!currL)
-        *head = currL = newLineNode (lines);
-    else
-        currL = connectLine(currL,newLineNode(lines));
     }
 
-    *bpp = bp;
+  *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;
+  char restart = 0;
+  char lines[MAX_PATTERN_LEN];
+  char *lp, *rp;
+  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 (!bp)
+  /* if not found */
+  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");
+
+  /* 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");
+  /* if brace */
+  if (*bp == '{')
+    bp++;
+  else
+    {                           /* must be restart */
+      restart++;
+      bp += strlen ("restart");
+      /* look for '{' */
+      EXPECT_CHR (bp, '{', "expected '{'\n");
+      bp++;
+    }
 
-    match = replace = currL = NULL ;
-    /* we are the start of a rule */
-    getPeepLine(&match, &bp);
+  /* skip thru all the blank space */
+  SKIP_SPACE (bp, "unexpected end of rule\n");
 
-    /* now look for by */
-    EXPECT_STR(bp,"by","expected 'by'\n");
+  match = replace = currL = NULL;
+  /* we are the start of a rule */
+  getPeepLine (&match, &bp);
 
-    /* then look for a '{' */
-    EXPECT_CHR(bp,'{',"expected '{'\n");
-    bp++ ;
+  /* now look for by */
+  EXPECT_STR (bp, "by", "expected 'by'\n");
 
-    SKIP_SPACE(bp,"unexpected end of rule\n");
-    getPeepLine (&replace, &bp);
+  /* then look for a '{' */
+  EXPECT_CHR (bp, '{', "expected '{'\n");
+  bp++;
 
-    /* look for a 'if' */
-    while ((isspace(*bp) || *bp == '\n') && *bp) bp++;
+  /* save char position (needed for generating error msg) */
+  rp = 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;
-    }
+  SKIP_SPACE (bp, "unexpected end of rule\n");
+  getPeepLine (&replace, &bp);
 
-    /* look for the condition */
-    lp = lines;
-    while (*bp && (*bp != '\n')) {
-        *lp++ = *bp++;
+  /* look for a 'if' */
+  while ((ISCHARSPACE (*bp) || *bp == '\n') && *bp)
+    bp++;
+
+  if (strncmp (bp, "if", 2) == 0)
+    {
+      bp += 2;
+      while ((ISCHARSPACE (*bp) || *bp == '\n') && *bp)
+        bp++;
+      if (!*bp)
+        {
+          fprintf (stderr, "expected condition name\n");
+          return;
+        }
+
+      /* look for the condition */
+      lp = lines;
+      while (*bp && (*bp != '\n'))
+        {
+          *lp++ = *bp++;
+        }
+      *lp = '\0';
+
+      newPeepRule (match, replace, lines, restart);
     }
-    *lp = '\0';
+  else
+    {
+      if (*bp && strncmp (bp, "replace", 7))
+        {
+          /* not the start of a new peeprule, so "if" should be here */
+
+          char strbuff[1000];
+          char *cp;
+
+          /* go to the start of the line following "{" of the "by" token */
+          while (*rp && (*rp == '\n'))
+            rp++;
 
-    newPeepRule(match,replace,lines,restart);
-    } else
-    newPeepRule(match,replace,NULL,restart);
-    goto top;
+          /* copy text of rule starting with line after "by {" */
+          cp = strbuff;
+          while (*rp && (rp < bp) && ((cp - strbuff) < sizeof(strbuff)))
+              *cp++ = *rp++;
+
+          /* and now the rest of the line */
+          while (*rp && (*rp != '\n') && ((cp - strbuff) < sizeof(strbuff)))
+            *cp++ = *rp++;
+
+          *cp = '\0';
+          fprintf (stderr, "%s\nexpected '} if ...'\n", strbuff);
+          return;
+        }
+      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 (ISCHARDIGIT (*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 != ',' &&
+         !ISCHARSPACE (*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--;
+            }
+          // include the trailing ')'
+          *vv++ = *vvx++;
         }
-    } else
-        *vv++ = *vvx++ ;
+      else
+        *vv++ = *vvx++;
     }
-    *s = vvx ;
-    *vv = '\0';
-    /* got value */
-    ALLOC_ATOMIC(vvx,strlen(vval)+1);
-    strcpy(vvx,vval);
-    hTabAddItem(vtab,key,vvx);
+  *s = vvx;
+  *vv = '\0';
+  /* got value */
+  vvx = traceAlloc (&_G.values, Safe_strdup(vval));
 
+  hTabAddItem (vtab, key, vvx);
 }
 
 /*-----------------------------------------------------------------*/
 /* matchLine - matches one line                                    */
 /*-----------------------------------------------------------------*/
-static bool matchLine (char *s, char *d, hTab **vars)
+static bool
+matchLine (char *s, char *d, hTab ** vars)
 {
-
-    if (!s || !(*s))
+  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;
-    }
+  while (*s && *d)
+    {
+      /* skip white space in both */
+      while (ISCHARSPACE (*s))
+        s++;
+      while (ISCHARSPACE (*d))
+        d++;
+
+      /* if the destination is a var */
+      if (*d == '%' && ISCHARDIGIT (*(d + 1)) && vars)
+        {
+          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 (ISCHARDIGIT (*d))
+            d++;
+
+          while (ISCHARSPACE (*s))
+            s++;
+          while (ISCHARSPACE (*d))
+            d++;
+        }
+
+      /* they should be an exact match other wise */
+      if (*s && *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 (ISCHARSPACE (*s))
+      s++;
 
-    if (*d)
-    while (isspace(*d)) d++;
+  if (*d)
+    while (ISCHARSPACE (*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 */
+  lineNode *spl;                /* source pl */
+  lineNode *rpl;                /* rule peep line */
 
-    hTabClearAll(pr->vars);
-/*     setToNull((void **) &pr->vars);    */
+/*     setToNull((void *) &pr->vars);    */
 /*     pr->vars = newHashTable(100); */
 
-    /* for all the lines defined in the rule */
-    rpl = pr->match;
-    spl = pl ;
-    while (spl && rpl) {
+  /* 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;
 
-    /* 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)) {
+      rpl = rpl->next;
+      if (rpl)
         spl = spl->next;
-        continue;
     }
 
-    if (!matchLine(spl->line,rpl->line,&pr->vars))
-        return FALSE ;
+  /* if rules ended */
+  if (!rpl)
+    {
+      /* if this rule has additional conditions */
+      if (pr->cond)
+        {
+          if (callFuncByName (pr->cond, pr->vars, pl, spl, head))
+            {
+              *mtail = spl;
+              return TRUE;
+            }
+          else
+            return FALSE;
+        }
+      else
+        {
+          *mtail = spl;
+          return TRUE;
+        }
+    }
+  else
+    return FALSE;
+}
+
+static void
+reassociate_ic_down (lineNode *shead, lineNode *stail,
+                     lineNode *rhead, lineNode *rtail)
+{
+  lineNode *csl;        /* current source line */
+  lineNode *crl;        /* current replacement line */
+
+  csl = shead;
+  crl = rhead;
+  while (1)
+    {
+      /* skip over any comments */
+      while (csl!=stail->next && csl->isComment)
+        csl = csl->next;
+      while (crl!=rtail->next && crl->isComment)
+        crl = crl->next;
+
+      /* quit if we reach the end */
+      if ((csl==stail->next) || (crl==rtail->next) || crl->ic)
+        break;
+
+      if (matchLine(csl->line,crl->line,NULL))
+        {
+          crl->ic = csl->ic;
+          csl = csl->next;
+          crl = crl->next;
+        }
+      else
+        break;
+    }
+}
+
+static void
+reassociate_ic_up (lineNode *shead, lineNode *stail,
+                   lineNode *rhead, lineNode *rtail)
+{
+  lineNode *csl;        /* current source line */
+  lineNode *crl;        /* current replacement line */
+
+  csl = stail;
+  crl = rtail;
+  while (1)
+    {
+      /* skip over any comments */
+      while (csl!=shead->prev && csl->isComment)
+        csl = csl->prev;
+      while (crl!=rhead->prev && crl->isComment)
+        crl = crl->prev;
+
+      /* quit if we reach the end */
+      if ((csl==shead->prev) || (crl==rhead->prev) || crl->ic)
+        break;
 
-    rpl = rpl->next ;
-    if (rpl)
-        spl = spl->next ;
+      if (matchLine(csl->line,crl->line,NULL))
+        {
+          crl->ic = csl->ic;
+          csl = csl->prev;
+          crl = crl->prev;
+        }
+      else
+        break;
     }
+}
 
-    /* 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;
+/*------------------------------------------------------------------*/
+/* reassociate_ic - reassociate replacement lines with origin iCode */
+/*------------------------------------------------------------------*/
+static void
+reassociate_ic (lineNode *shead, lineNode *stail,
+                lineNode *rhead, lineNode *rtail)
+{
+  lineNode *csl;        /* current source line */
+  lineNode *crl;        /* current replacement line */
+  bool single_iCode;
+  iCode *ic;
+
+  /* Check to see if all the source lines (excluding comments) came
+  ** for the same iCode
+  */
+  ic = NULL;
+  for (csl=shead;csl!=stail->next;csl=csl->next)
+    if (csl->ic && !csl->isComment)
+      {
+        ic = csl->ic;
+        break;
+      }
+  single_iCode = (ic!=NULL);
+  for (csl=shead;csl!=stail->next;csl=csl->next)
+    if ((csl->ic != ic) && !csl->isComment)
+      {
+        /* More than one iCode was found. However, if it's just the
+        ** last line with the different iCode and it was not changed
+        ** in the replacement, everything else must be the first iCode.
+        */
+        if ((csl==stail) && matchLine (stail->line, rtail->line, NULL))
+          {
+            rtail->ic = stail->ic;
+            for (crl=rhead;crl!=rtail;crl=crl->next)
+              crl->ic = ic;
+            return;
+          }
+
+        single_iCode = FALSE;
+        break;
+      }
+
+  /* If all of the source lines came from the same iCode, then so have
+  ** all of the replacement lines too.
+  */
+  if (single_iCode)
+    {
+      for (crl=rhead;crl!=rtail->next;crl=crl->next)
+        crl->ic = ic;
+      return;
     }
+
+  /* The source lines span iCodes, so we may end up with replacement
+  ** lines that we don't know which iCode(s) to associate with. Do the
+  ** best we can by using the following strategies:
+  **    1) Start at the top and scan down. As long as the source line
+  **       matches the replacement line, they have the same iCode.
+  **    2) Start at the bottom and scan up. As long as the source line
+  **       matches the replacement line, they have the same iCode.
+  **    3) For any label in the source, look for a matching label in
+  **       the replacment. If found, they have the same iCode. From
+  **       these matching labels, scan down for additional matching
+  **       lines; if found, they also have the same iCode.
+  */
+
+  /* Strategy #1: Start at the top and scan down for matches
+  */
+  reassociate_ic_down(shead,stail,rhead,rtail);
+
+  /* Strategy #2: Start at the bottom and scan up for matches
+  */
+  reassociate_ic_up(shead,stail,rhead,rtail);
+
+  /* Strategy #3: Try to match labels
+  */
+  csl = shead;
+  while (1)
+    {
+      /* skip over any comments */
+      while (csl!=stail->next && csl->isComment)
+        csl = csl->next;
+      if (csl==stail->next)
+        break;
+
+      if (csl->isLabel)
+        {
+          /* found a source line label; look for it in the replacment lines */
+          crl = rhead;
+          while (1)
+            {
+              while (crl!=rtail->next && crl->isComment)
+                crl = crl->next;
+              if (crl==rtail->next)
+                break;
+              if (matchLine(csl->line, crl->line, NULL))
+                {
+                  reassociate_ic_down(csl,stail,crl,rtail);
+                  break;
+                }
+              else
+                crl = crl->next;
+            }
+        }
+      csl = csl->next;
+    }
+
+  /* Try to assign a meaningful iCode to any comment that is missing
+     one. Since they are comments, it's ok to make mistakes; we are just
+     trying to improve continuity to simplify other tests.
+  */
+  ic = NULL;
+  for (crl=rtail;crl!=rhead->prev;crl=crl->prev)
+    {
+      if (!crl->ic && ic && crl->isComment)
+        crl->ic = ic;
+      ic = crl->ic;
     }
-    else
-    return FALSE;
 }
 
+
 /*-----------------------------------------------------------------*/
 /* replaceRule - does replacement of a matching pattern            */
 /*-----------------------------------------------------------------*/
-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;
-    }
-    }
-    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++;
-        l++;
-        while (isdigit(*l)) l++;
-        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)
+static void
+replaceRule (lineNode ** shead, lineNode * stail, peepRule * pr)
+{
+  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;
+          pl->isComment = cl->isComment || (*cl->line == ';');
+        }
+    }
+  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 == '%' && ISCHARDIGIT (*(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++;
+              }
+              l++;
+              while (ISCHARDIGIT (*l)) {
+                l++;
+              }
+              continue;
+            }
+          *lbp++ = *l++;
+        }
+
+      *lbp = '\0';
+      if (cl)
+        cl = connectLine (cl, newLineNode (lb));
+      else
+        lhead = cl = newLineNode (lb);
+      cl->isComment = pl->isComment;
+      cl->isLabel   = pl->isLabel;
+    }
+
+  /* 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;
+      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;
+  if (lhead)
+    {
+      /* determine which iCodes the replacment lines relate to */
+      reassociate_ic(*shead,stail,lhead,cl);
+
+      /* 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;
+        }
+      *shead = lhead;
+      /* now for the tail */
+      if (stail && stail->next)
+        {
+          stail->next->prev = cl;
+          if (cl)
+            cl->next = stail->next;
+        }
+    }
+  else
+    {
+      /* the replacement is empty - delete the source lines */
+      if ((*shead)->prev)
+        (*shead)->prev->next = stail->next;
+      if (stail->next)
+        stail->next->prev = (*shead)->prev;
+      *shead = 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)
+bool
+isLabelDefinition (const char *line, const char **start, int *len,
+                   bool isPeepRule)
 {
-    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))
+  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 && ISCHARSPACE (*cp))
     {
-        cp++;
-    } 
-    
-    if (!*cp)
+      cp++;
+    }
+
+  if (!*cp)
     {
-       return FALSE;
+      return FALSE;
     }
-    
-    *start = cp;
-    
-    while (isalnum(*cp) || (*cp == '$') || (*cp == '_'))
+
+  *start = cp;
+
+  while (ISCHARALNUM (*cp) || (*cp == '$') || (*cp == '_') ||
+         (isPeepRule && (*cp == '%')))
     {
-       cp++;
+      cp++;
     }
-    
-    if ((cp == *start) || (*cp != ':'))
+
+  if ((cp == *start) || (*cp != ':'))
     {
-        return FALSE;
+      return FALSE;
     }
-    
-    *len = (cp - (*start));
-    return TRUE;
+
+  *len = (cp - (*start));
+  return TRUE;
 }
 
 /* Quick & dirty string hash function. */
-static int hashSymbolName(const char *name)
+static int
+hashSymbolName (const char *name)
 {
-    int hash = 0;
-    
-    while (*name)
+  int hash = 0;
+
+  while (*name)
     {
-       hash = (hash << 6) ^ *name;
-       name++;
+      hash = (hash << 6) ^ *name;
+      name++;
     }
-    
-    if (hash < 0)
+
+  if (hash < 0)
     {
-       hash = -hash;
+      hash = -hash;
     }
-    
-    return hash % HTAB_SIZE;
+
+  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;
-           
-           ALLOC(entry, sizeof(labelHashEntry));
-
-           assert(entry != NULL);
-           
-           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)
+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. */
+  for (line = head; line; line = line->next)
+    {
+      if (line->isLabel  ||
+          line->isInline)
+        {
+          /* run isLabelDefinition to:
+             - look for labels in inline assembler
+             - calculate labelLen
+          */
+          if (isLabelDefinition (line->line, &label, &labelLen, FALSE) &&
+              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;
+
+              /* Assume function entry points are referenced somewhere,   */
+              /* even if we can't find a reference (might be from outside */
+              /* the function) */
+              if (line->ic && (line->ic->op == FUNCTION))
+                entry->refCount++;
+
+              hTabAddItem (&labelHash, hashSymbolName (entry->name), entry);
+            }
+        }
+    }
+
+
+  /* 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))
+              if (strstr (line->line, thisEntry->name))
                 {
-                    thisEntry->refCount++;
+                  thisEntry->refCount++;
                 }
-               thisEntry = hTabNextItemWK(labelHash);
+              thisEntry = hTabNextItemWK (labelHash);
             }
-        }    
-        line = line->next;
+        }
+      line = line->next;
     }
 
 #if 0
-    /* Spew the contents of the table. Debugging fun only. */    
-    for (i = 0; i < HTAB_SIZE; i++)
+  /* Spew the contents of the table. Debugging fun only. */
+  for (i = 0; i < HTAB_SIZE; i++)
     {
-        labelHashEntry *thisEntry;
-        
-        thisEntry = hTabFirstItemWK(labelHash, i);
-        
-        while (thisEntry)
+      labelHashEntry *thisEntry;
+
+      thisEntry = hTabFirstItemWK (labelHash, i);
+
+      while (thisEntry)
         {
-            fprintf(stderr, "label: %s ref %d\n", 
-                    thisEntry->name, thisEntry->refCount);
-            thisEntry = hTabNextItemWK(labelHash);
+          fprintf (stderr, "label: %s ref %d\n",
+                   thisEntry->name, thisEntry->refCount);
+          thisEntry = hTabNextItemWK (labelHash);
         }
     }
-#endif    
+#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;
+  lineNode *spl;
+  peepRule *pr;
+  lineNode *mtail = NULL;
+  bool restart, replaced;
+
+#if !OPT_DISABLE_PIC || !OPT_DISABLE_PIC16
+  /* The PIC port uses a different peep hole optimizer based on "pCode" */
+  if (TARGET_IS_PIC || TARGET_IS_PIC16)
+    return;
+#endif
 
-    if (labelHash)
+  assert(labelHash == NULL);
+
+  do
     {
-       hTabDeleteAll(labelHash);
-    }
-    labelHash = NULL;
+      restart = FALSE;
 
- top:
-    /* for all rules */
-    for (pr = rootRules ; pr ; pr = pr->next ) {
+      /* for all rules */
+      for (pr = rootRules; pr; pr = pr->next)
+        {
+          for (spl = *pls; spl; spl = replaced ? spl : spl->next)
+            {
+              replaced = FALSE;
 
-    for (spl = *pls ; spl ; spl = spl->next ) {
+              /* if inline assembler then no peep hole */
+              if (spl->isInline)
+                continue;
 
-        /* if inline assembler then no peep hole */
-        if (spl->isInline)
-        continue ;
+              /* don't waste time starting a match on debug symbol
+              ** or comment */
+              if (spl->isDebug || spl->isComment || *(spl->line)==';')
+                continue;
 
-        mtail = NULL ;
+              mtail = NULL;
 
-        /* if it matches */
-        if (matchRule (spl,&mtail,pr, *pls)) {
+              /* Tidy up any data stored in the hTab */
+
+              /* if it matches */
+              if (matchRule (spl, &mtail, pr, *pls))
+                {
+                  /* restart at the replaced line */
+                  replaced = TRUE;
+
+                  /* then replace */
+                  if (spl == *pls)
+                    {
+                      replaceRule (pls, mtail, pr);
+                      spl = *pls;
+                    }
+                  else
+                    replaceRule (&spl, mtail, pr);
+
+                  /* if restart rule type then
+                     start at the top again */
+                  if (pr->restart)
+                    {
+                      restart = TRUE;
+                    }
+                }
 
-        /* then replace */
-        if (spl == *pls)
-            replaceRule(pls, mtail, pr);
-        else
-            replaceRule (&spl, mtail,pr);
+              if (pr->vars)
+                {
+                  hTabDeleteAll (pr->vars);
+                  Safe_free (pr->vars);
+                  pr->vars = NULL;
+                }
 
-        /* if restart rule type then
-           start at the top again */
-        if (pr->restart)
-            goto top;
+              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];
+  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;
+  if (!(f = fopen (fname, "r")))
+    {
+      fprintf (stderr, "cannot open peep rule file\n");
+      return NULL;
     }
 
-    while ((ch = fgetc(f)) != EOF) {
-    lb[nch++] = ch;
+  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);
+      /* 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);
+              strncatz (rs, lb,  strlen (rs) + strlen (lb) + 1);
+            }
+          else
+            {
+              rs = Safe_strdup (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 characters left over */
+  if (nch)
+    {
+      lb[nch] = '\0';
+      /* copy it into allocated buffer */
+      if (rs)
+        {
+          rs = Safe_realloc (rs, strlen (rs) + strlen (lb) + 1);
+          strncatz (rs, lb, strlen (rs) + strlen (lb) + 1);
+        }
+      else
+        {
+          rs = Safe_strdup (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 */
+  if (!options.nopeep)
+    {
+      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);
+      /* override nopeep setting, default rules have not been read */
+      options.nopeep = 0;
     }
+
+
+#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
+
+#if !OPT_DISABLE_PIC16
+  /* Convert the peep rules into pcode.
+     NOTE: this is only support in the PIC port (at the moment)
+       and the PIC16 port (VR 030601)
+  */
+        if (TARGET_IS_PIC16)
+                pic16_peepRules2pCode(rootRules);
+
+#endif
+
 }