* support/regression/fwk/lib/testfwk.c: printn is recursive and thus needs
[fw/sdcc] / src / SDCCpeeph.c
index 37e349c6853d1ae6ec8b841d96818e1617507cc3..2c493e2528aaabffc331b7da307ded3e7ae01cb4 100644 (file)
@@ -48,17 +48,151 @@ static int hashSymbolName (const char *name);
 static void buildLabelRefCountHash (lineNode * head);
 
 static bool matchLine (char *, char *, hTab **);
+bool isLabelDefinition (const char *line, const char **start, int *len);
 
-#define FBYNAME(x) int x (hTab *vars, lineNode *currPl, lineNode *head, \
-        const char *cmdLine)
+#define FBYNAME(x) int x (hTab *vars, lineNode *currPl, lineNode *endPl, \
+       lineNode *head, const char *cmdLine)
 
 #if !OPT_DISABLE_PIC
-void  peepRules2pCode(peepRule *);
+void peepRules2pCode(peepRule *);
+#endif
+
+#if !OPT_DISABLE_PIC16
+void pic16_peepRules2pCode(peepRule *);
 #endif
 
 /*-----------------------------------------------------------------*/
 /* pcDistance - afinds a label back ward or forward                */
 /*-----------------------------------------------------------------*/
+int
+mcs51_instruction_size(const char *inst)
+{
+       char *op, op1[256], op2[256];
+       int opsize;
+       const char *p;
+
+       while (*inst && isspace(*inst)) inst++;
+
+       #define ISINST(s) (strncmp(inst, (s), sizeof(s)-1) == 0)
+
+       /* Based on the current (2003-08-22) code generation for the
+          small library, the top instruction probability is:
+          
+            57% mov/movx/movc
+             6% push
+             6% pop
+             4% inc
+             4% lcall
+             4% add
+             3% clr
+             2% subb
+       */
+       /* mov, push, & pop are the 69% of the cases. Check them first! */
+       if (ISINST("mov"))
+         {
+           if (*(inst+3)=='x') return 1; /* movx */
+           if (*(inst+3)=='c') return 1; /* movc */
+           goto checkoperands;           /* mov  */
+         }
+       if (ISINST("push")) return 2;
+       if (ISINST("pop")) return 2;
+
+       if (ISINST("lcall")) return 3;
+       if (ISINST("ret")) return 1;
+       if (ISINST("ljmp")) return 3;
+       if (ISINST("sjmp")) return 2;
+       if (ISINST("rlc")) return 1;
+       if (ISINST("rrc")) return 1;
+       if (ISINST("rl")) return 1;
+       if (ISINST("rr")) return 1;
+       if (ISINST("swap")) return 1;
+       if (ISINST("jc")) return 2;
+       if (ISINST("jnc")) return 2;
+       if (ISINST("jb")) return 3;
+       if (ISINST("jnb")) return 3;
+       if (ISINST("jbc")) return 3;
+       if (ISINST("jmp")) return 1;    // always jmp @a+dptr
+       if (ISINST("jz")) return 2;
+       if (ISINST("jnz")) return 2;
+       if (ISINST("cjne")) return 3;
+       if (ISINST("mul")) return 1;
+       if (ISINST("div")) return 1;
+       if (ISINST("da")) return 1;
+       if (ISINST("xchd")) return 1;
+       if (ISINST("reti")) return 1;
+       if (ISINST("nop")) return 1;
+       if (ISINST("acall")) return 1;
+       if (ISINST("ajmp")) return 2;
+
+checkoperands:
+       p = inst;
+       while (*p && isalnum(*p)) p++;
+       for (op = op1, opsize=0; *p && *p != ',' && opsize < sizeof(op1); p++) {
+               if (!isspace(*p)) *op++ = *p, opsize++;
+       }
+       *op = '\0';
+       if (*p == ',') p++;
+       for (op = op2, opsize=0; *p && *p != ',' && opsize < sizeof(op2); p++) {
+               if (!isspace(*p)) *op++ = *p, opsize++;
+       }
+       *op = '\0';
+
+       #define IS_A(s) (*(s) == 'a' && *(s+1) == '\0')
+       #define IS_C(s) (*(s) == 'c' && *(s+1) == '\0')
+       #define IS_Rn(s) (*(s) == 'r' && *(s+1) >= '0' && *(s+1) <= '7')
+       #define IS_atRi(s) (*(s) == '@' && *(s+1) == 'r')
+
+       if (ISINST("mov")) {
+               if (IS_C(op1) || IS_C(op2)) return 2;
+               if (IS_A(op1)) {
+                       if (IS_Rn(op2) || IS_atRi(op2)) return 1;
+                       return 2;
+               }
+               if (IS_Rn(op1) || IS_atRi(op1)) {
+                       if (IS_A(op2)) return 1;
+                       return 2;
+               }
+               if (strcmp(op1, "dptr") == 0) return 3;
+               if (IS_A(op2) || IS_Rn(op2) || IS_atRi(op2)) return 2;
+               return 3;
+       }
+       if (ISINST("add") || ISINST("addc") || ISINST("subb") || ISINST("xch")) {
+               if (IS_Rn(op2) || IS_atRi(op2)) return 1;
+               return 2;
+       }
+       if (ISINST("inc") || ISINST("dec")) {
+               if (IS_A(op1) || IS_Rn(op1) || IS_atRi(op1)) return 1;
+               if (strcmp(op1, "dptr") == 0) return 1;
+               return 2;
+       }
+       if (ISINST("anl") || ISINST("orl") || ISINST("xrl")) {
+               if (IS_C(op1)) return 2;
+               if (IS_A(op1)) {
+                       if (IS_Rn(op2) || IS_atRi(op2)) return 1;
+                       return 2;
+               } else {
+                       if (IS_A(op2)) return 2;
+                       return 3;
+               }
+       }
+       if (ISINST("clr") || ISINST("setb") || ISINST("cpl")) {
+               if (IS_A(op1) || IS_C(op1)) return 1;
+               return 2;
+       }
+       if (ISINST("djnz")) {
+               if (IS_Rn(op1)) return 2;
+               return 3;
+       }
+
+       if (*inst == 'a' && *(inst+1) == 'r' && *(inst+2) >= '0' && *(inst+2) <= '7' && op1[0] == '=') {
+               /* ignore ar0 = 0x00 type definitions */
+               return 0;
+       }
+
+       fprintf(stderr, "Warning, peephole unrecognized instruction: %s\n", inst);
+       return 3;
+}
+
 int 
 pcDistance (lineNode * cpos, char *lbl, bool back)
 {
@@ -73,9 +207,13 @@ pcDistance (lineNode * cpos, char *lbl, bool back)
       if (pl->line &&
          *pl->line != ';' &&
          pl->line[strlen (pl->line) - 1] != ':' &&
-         !pl->isDebug)
-
-       dist++;
+         !pl->isDebug) {
+               if (TARGET_IS_MCS51) {
+                       dist += mcs51_instruction_size(pl->line);
+               } else {
+                       dist += 3;
+               }
+       }
 
       if (strncmp (pl->line, buff, strlen (buff)) == 0)
        return dist;
@@ -94,7 +232,8 @@ pcDistance (lineNode * cpos, char *lbl, bool back)
 /*-----------------------------------------------------------------*/
 FBYNAME (flat24bitModeAndPortDS390)
 {
-    return ((strcmp(port->target,"ds390") == 0) && 
+    return (((strcmp(port->target,"ds390") == 0) ||
+            (strcmp(port->target,"ds400") == 0)) && 
            (options.model == MODEL_FLAT24));
 }
 
@@ -103,7 +242,8 @@ FBYNAME (flat24bitModeAndPortDS390)
 /*-----------------------------------------------------------------*/
 FBYNAME (portIsDS390)
 {
-    return (strcmp(port->target,"ds390") == 0);
+    return ((strcmp(port->target,"ds390") == 0) ||
+           (strcmp(port->target,"ds400") == 0));
 }
 
 /*-----------------------------------------------------------------*/
@@ -122,6 +262,11 @@ FBYNAME (xramMovcOption)
   return (options.xram_movc && (strcmp(port->target,"mcs51") == 0));
 }
 
+
+
+
+
+
 /*-----------------------------------------------------------------*/
 /* labelInRange - will check to see if label %5 is within range    */
 /*-----------------------------------------------------------------*/
@@ -134,6 +279,10 @@ FBYNAME (labelInRange)
   if (!lbl)
     return FALSE;
 
+  /* 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 &&
@@ -150,17 +299,86 @@ FBYNAME (labelInRange)
   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)
+/*    changed to 127, now that pcDistance return actual number of bytes */
+  if (!dist || dist > 127)
     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;
+
+  label = hTabItemWithKey (vars, 5);
+  if (!label) return FALSE;
+  len = strlen(label);
+
+  for(pl = currPl; pl; pl = pl->next) {
+       if (pl->line && !pl->isDebug &&
+         pl->line[strlen(pl->line)-1] == ':') {
+               if (strncmp(pl->line, label, len) == 0) break; /* Found Label */
+               if (strlen(pl->line) != 7 || !isdigit(*(pl->line)) ||
+                 !isdigit(*(pl->line+1)) || !isdigit(*(pl->line+2)) ||
+                 !isdigit(*(pl->line+3)) || !isdigit(*(pl->line+4)) ||
+                 *(pl->line+5) != '$') {
+                       return FALSE; /* non-local label encountered */
+               }
+       }
+  }
+  if (!pl) return FALSE; /* did not find the label */
+  pl = pl->next;
+  if (!pl || !pl->line || pl->isDebug) return FALSE; /* next line not valid */
+  p = pl->line;
+  for (p = pl->line; *p && isspace(*p); p++)
+         ;
+  if (strcmp(p, "ret") == 0) return TRUE;
+  return FALSE;
+}
+
+
+/*-----------------------------------------------------------------*/
+/* 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 :) */
+}
+
+
 /*-----------------------------------------------------------------*/
 /* operandsNotSame - check if %1 & %2 are the same                 */
 /*-----------------------------------------------------------------*/
@@ -430,6 +648,193 @@ FBYNAME (labelRefCount)
   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 && (isalnum(*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) )
+              return !op->isvolatile;
+          case JUMPTABLE:
+            op = IC_JTCOND (cl->ic);
+            if (IS_SYMOP (op) && !strcmp(OP_SYMBOL (op)->rname,symname) )
+              return !op->isvolatile;
+          default:
+            op = IC_LEFT (cl->ic);
+            if (IS_SYMOP (op) && !strcmp(OP_SYMBOL (op)->rname,symname) )
+              return !op->isvolatile;
+            op = IC_RIGHT (cl->ic);
+            if (IS_SYMOP (op) && !strcmp(OP_SYMBOL (op)->rname,symname) )
+              return !op->isvolatile;
+            op = IC_RESULT (cl->ic);
+            if (IS_SYMOP (op) && !strcmp(OP_SYMBOL (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 && isspace(*cmdLine))
+    cmdLine++;
+  while (*cmdLine)
+    {
+      if (*cmdLine!='%')
+        goto error;
+      cmdLine++;
+      if (!isdigit(*cmdLine))
+        goto error;
+      varNumber = strtol(cmdLine, &digitend, 10);
+      cmdLine = digitend;
+      while (*cmdLine && isspace(*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;
+}
+    
+
 /*-----------------------------------------------------------------*/
 /* callFuncByName - calls a function as defined in the table       */
 /*-----------------------------------------------------------------*/
@@ -437,12 +842,13 @@ int
 callFuncByName (char *fname,
                hTab * vars,
                lineNode * currPl,
+               lineNode * endPl,
                lineNode * head)
 {
   struct ftab
   {
     char *fname;
-    int (*func) (hTab *, lineNode *, lineNode *, const char *);
+    int (*func) (hTab *, lineNode *, lineNode *, lineNode *, const char *);
   }
   ftab[] =
   {
@@ -493,8 +899,17 @@ callFuncByName (char *fname,
     {
       "portIsDS390", portIsDS390
     },
+    {
+      "labelIsReturnOnly", labelIsReturnOnly
+    },
+    {
+      "okToRemoveSLOC", okToRemoveSLOC
+    },
     {
       "24bitModeAndPortDS390", flat24bitModeAndPortDS390
+    },
+    {
+      "notVolatile", notVolatile
     }
   };
   int  i;
@@ -512,13 +927,11 @@ callFuncByName (char *fname,
     {
        if (strcmp (ftab[i].fname, funcName) == 0)
        {
-           rc = (*ftab[i].func) (vars, currPl, head,
+           rc = (*ftab[i].func) (vars, currPl, endPl, head,
                                  funcArgs);
        }
     }
     
-    Safe_free(cmdCopy);
-    
     if (rc == -1)
     {
        fprintf (stderr, 
@@ -529,6 +942,8 @@ callFuncByName (char *fname,
        // a bad rule and refuse it.
        rc = FALSE;
     }
+
+    Safe_free(cmdCopy);
     
   return rc;
 }
@@ -539,11 +954,27 @@ callFuncByName (char *fname,
 void 
 printLine (lineNode * head, FILE * of)
 {
+  iCode *last_ic = NULL;
+  bool debug_iCode_tracking = (getenv("DEBUG_ICODE_TRACKING")!=NULL);
+  
   if (!of)
     of = stdout;
 
   while (head)
     {
+      if (head->ic!=last_ic)
+        {
+          last_ic = head->ic;
+          if (debug_iCode_tracking)
+            {
+              if (head->ic)
+                fprintf (of, "; block = %d, seq = %d\n",
+                         head->ic->block, head->ic->seq);
+              else
+                fprintf (of, "; iCode lost\n");
+            }
+        }
+        
       /* don't indent comments & labels */
       if (head->line &&
          (*head->line == ';' ||
@@ -604,6 +1035,7 @@ newLineNode (char *line)
 
   pl = Safe_alloc ( sizeof (lineNode));
   pl->line = Safe_strdup (line);
+  pl->ic = NULL;
   return pl;
 }
 
@@ -677,6 +1109,12 @@ getPeepLine (lineNode ** head, char **bpp)
        *head = currL = newLineNode (lines);
       else
        currL = connectLine (currL, newLineNode (lines));
+
+      lp = lines;
+      while (*lp && isspace(*lp))
+        lp++;
+      if (*lp==';')
+        currL->isComment = 1;
     }
 
   *bpp = bp;
@@ -864,7 +1302,7 @@ matchLine (char *s, char *d, hTab ** vars)
        d++;
 
       /* if the destination is a var */
-      if (*d == '%' && isdigit (*(d + 1)))
+      if (*d == '%' && isdigit (*(d + 1)) && vars)
        {
          char *v = hTabItemWithKey (*vars, keyForVar (d + 1));
          /* if the variable is already bound
@@ -884,15 +1322,16 @@ matchLine (char *s, char *d, hTab ** vars)
          d++;
          while (isdigit (*d))
            d++;
-       }
 
-      /* they should be an exact match other wise */
-      if (*s && *d)
-       {
          while (isspace (*s))
            s++;
          while (isspace (*d))
            d++;
+       }
+
+      /* they should be an exact match other wise */
+      if (*s && *d)
+       {
          if (*s++ != *d++)
            return FALSE;
        }
@@ -929,7 +1368,7 @@ matchRule (lineNode * pl,
   lineNode *spl;               /* source pl */
   lineNode *rpl;               /* rule peep line */
 
-/*     setToNull((void **) &pr->vars);    */
+/*     setToNull((void *) &pr->vars);    */
 /*     pr->vars = newHashTable(100); */
 
   /* for all the lines defined in the rule */
@@ -962,7 +1401,7 @@ matchRule (lineNode * pl,
       /* if this rule has additional conditions */
       if (pr->cond)
        {
-         if (callFuncByName (pr->cond, pr->vars, pl, head))
+         if (callFuncByName (pr->cond, pr->vars, pl, spl, head))
            {
              *mtail = spl;
              return TRUE;
@@ -980,6 +1419,193 @@ matchRule (lineNode * pl,
     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;
+
+      if (matchLine(csl->line,crl->line,NULL))
+        {
+          crl->ic = csl->ic;
+          csl = csl->prev;
+          crl = crl->prev;
+        }
+      else
+        break;
+    }
+}
+
+/*------------------------------------------------------------------*/
+/* 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)
+    {
+      const char *labelStart;
+      int labelLength;
+      
+      /* skip over any comments */
+      while (csl!=stail->next && csl->isComment)
+        csl = csl->next;
+      if (csl==stail->next)
+        break;
+
+      if (isLabelDefinition(csl->line, &labelStart, &labelLength))
+        {
+          /* 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;
+    }
+}
+
+                  
 /*-----------------------------------------------------------------*/
 /* replaceRule - does replacement of a matching pattern            */
 /*-----------------------------------------------------------------*/
@@ -1002,6 +1628,7 @@ replaceRule (lineNode ** shead, lineNode * stail, peepRule * pr)
          pl = (pl ? connectLine (pl, newLineNode (cl->line)) :
                (comment = newLineNode (cl->line)));
          pl->isDebug = cl->isDebug;
+         pl->isComment = cl->isComment || (*cl->line == ';');
        }
     }
   cl = NULL;
@@ -1044,6 +1671,7 @@ replaceRule (lineNode ** shead, lineNode * stail, peepRule * pr)
        cl = connectLine (cl, newLineNode (lb));
       else
        lhead = cl = newLineNode (lb);
+      cl->isComment = pl->isComment;
     }
 
   /* add the comments if any to the head of list */
@@ -1058,6 +1686,9 @@ replaceRule (lineNode ** shead, lineNode * stail, peepRule * pr)
       lhead = comment;
     }
 
+  /* 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)
@@ -1239,9 +1870,9 @@ peepHole (lineNode ** pls)
   lineNode *mtail = NULL;
   bool restart;
 
-#if !OPT_DISABLE_PIC
+#if !OPT_DISABLE_PIC || !OPT_DISABLE_PIC16
   /* The PIC port uses a different peep hole optimizer based on "pCode" */
-  if (TARGET_IS_PIC)
+  if (TARGET_IS_PIC || TARGET_IS_PIC16)
     return;
 #endif
 
@@ -1259,6 +1890,11 @@ peepHole (lineNode ** pls)
               /* 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;
 
@@ -1376,7 +2012,7 @@ initPeepHole ()
   if (options.peep_file)
     {
       readRules (s = readFileIntoBuffer (options.peep_file));
-      setToNull ((void **) &s);
+      setToNull ((void *) &s);
     }
 
 
@@ -1384,9 +2020,18 @@ initPeepHole ()
   /* Convert the peep rules into pcode.
      NOTE: this is only support in the PIC port (at the moment)
   */
-  if (TARGET_IS_PIC) {
-    peepRules2pCode(rootRules);
-  }
+       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
 
 }