* src/SDCCpeeph.c (replaceRule): support empty replacement peephole
[fw/sdcc] / src / SDCCpeeph.c
1 /*-------------------------------------------------------------------------
2   SDCCpeeph.c - The peep hole optimizer: for interpreting the
3                 peep hole rules
4
5              Written By -  Sandeep Dutta . sandeep.dutta@usa.net (1999)
6
7    This program is free software; you can redistribute it and/or modify it
8    under the terms of the GNU General Public License as published by the
9    Free Software Foundation; either version 2, or (at your option) any
10    later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
21    In other words, you are welcome to use, share and improve this program.
22    You are forbidden to forbid anyone else to use, share and improve
23    what you give them.   Help stamp out software-hoarding!
24 -------------------------------------------------------------------------*/
25
26 #include "common.h"
27
28 static peepRule *rootRules = NULL;
29 static peepRule *currRule = NULL;
30
31 #define HTAB_SIZE 53
32 typedef struct
33   {
34     char name[SDCC_NAME_MAX + 1];
35     int refCount;
36   }
37 labelHashEntry;
38
39 static hTab *labelHash = NULL;
40
41 static struct
42 {
43   allocTrace values;
44   allocTrace labels;
45 } _G;
46
47 static int hashSymbolName (const char *name);
48 static void buildLabelRefCountHash (lineNode * head);
49
50 static bool matchLine (char *, char *, hTab **);
51 bool isLabelDefinition (const char *line, const char **start, int *len);
52
53 #define FBYNAME(x) int x (hTab *vars, lineNode *currPl, lineNode *endPl, \
54         lineNode *head, const char *cmdLine)
55
56 #if !OPT_DISABLE_PIC
57 void peepRules2pCode(peepRule *);
58 #endif
59
60 #if !OPT_DISABLE_PIC16
61 void pic16_peepRules2pCode(peepRule *);
62 #endif
63
64 /*-----------------------------------------------------------------*/
65 /* pcDistance - afinds a label back ward or forward                */
66 /*-----------------------------------------------------------------*/
67
68 int 
69 pcDistance (lineNode * cpos, char *lbl, bool back)
70 {
71   lineNode *pl = cpos;
72   char buff[MAX_PATTERN_LEN];
73   int dist = 0;
74
75   SNPRINTF (buff, sizeof(buff), "%s:", lbl);
76   while (pl)
77     {
78
79       if (pl->line &&
80           *pl->line != ';' &&
81           pl->line[strlen (pl->line) - 1] != ':' &&
82           !pl->isDebug) {
83                 if (port->peep.getSize) {
84                         dist += port->peep.getSize(pl);
85                 } else {
86                         dist += 3;
87                 }
88         }
89
90       if (strncmp (pl->line, buff, strlen (buff)) == 0)
91         return dist;
92
93       if (back)
94         pl = pl->prev;
95       else
96         pl = pl->next;
97
98     }
99   return 0;
100 }
101
102 /*-----------------------------------------------------------------*/
103 /* flat24bitModeAndPortDS390 -                                     */
104 /*-----------------------------------------------------------------*/
105 FBYNAME (flat24bitModeAndPortDS390)
106 {
107     return (((strcmp(port->target,"ds390") == 0) ||
108              (strcmp(port->target,"ds400") == 0)) && 
109             (options.model == MODEL_FLAT24));
110 }
111
112 /*-----------------------------------------------------------------*/
113 /* portIsDS390 - return true if port is DS390                      */
114 /*-----------------------------------------------------------------*/
115 FBYNAME (portIsDS390)
116 {
117     return ((strcmp(port->target,"ds390") == 0) ||
118             (strcmp(port->target,"ds400") == 0));
119 }
120
121 /*-----------------------------------------------------------------*/
122 /* flat24bitMode - will check to see if we are in flat24 mode      */
123 /*-----------------------------------------------------------------*/
124 FBYNAME (flat24bitMode)
125 {
126   return (options.model == MODEL_FLAT24);
127 }
128
129 /*-----------------------------------------------------------------*/
130 /* xramMovcOption - check if using movc to read xram               */
131 /*-----------------------------------------------------------------*/
132 FBYNAME (xramMovcOption)
133 {
134   return (options.xram_movc && (strcmp(port->target,"mcs51") == 0));
135 }
136
137
138
139
140
141
142 /*-----------------------------------------------------------------*/
143 /* labelInRange - will check to see if label %5 is within range    */
144 /*-----------------------------------------------------------------*/
145 FBYNAME (labelInRange)
146 {
147   /* assumes that %5 pattern variable has the label name */
148   char *lbl = hTabItemWithKey (vars, 5);
149   int dist = 0;
150
151   if (!lbl)
152     return FALSE;
153
154   /* Don't optimize jumps in a jump table; a more generic test */
155   if (currPl->ic && currPl->ic->op == JUMPTABLE)
156     return FALSE;
157     
158   /* if the previous two instructions are "ljmp"s then don't
159      do it since it can be part of a jump table */
160   if (currPl->prev && currPl->prev->prev &&
161       strstr (currPl->prev->line, "ljmp") &&
162       strstr (currPl->prev->prev->line, "ljmp"))
163     return FALSE;
164
165   /* calculate the label distance : the jump for reladdr can be
166      +/- 127 bytes, here Iam assuming that an average 8051
167      instruction is 2 bytes long, so if the label is more than
168      63 intructions away, the label is considered out of range
169      for a relative jump. we could get more precise this will
170      suffice for now since it catches > 90% cases */
171   dist = (pcDistance (currPl, lbl, TRUE) +
172           pcDistance (currPl, lbl, FALSE));
173
174 /*    changed to 127, now that pcDistance return actual number of bytes */
175   if (!dist || dist > 127)
176     return FALSE;
177
178   return TRUE;
179 }
180
181 /*-----------------------------------------------------------------*/
182 /* labelIsReturnOnly - Check if label %5 is followed by RET        */
183 /*-----------------------------------------------------------------*/
184 FBYNAME (labelIsReturnOnly)
185 {
186   /* assumes that %5 pattern variable has the label name */
187   const char *label, *p;
188   const lineNode *pl;
189   int len;
190
191   label = hTabItemWithKey (vars, 5);
192   if (!label) return FALSE;
193   len = strlen(label);
194
195   for(pl = currPl; pl; pl = pl->next) {
196         if (pl->line && !pl->isDebug &&
197           pl->line[strlen(pl->line)-1] == ':') {
198                 if (strncmp(pl->line, label, len) == 0) break; /* Found Label */
199                 if (strlen(pl->line) != 7 || !isdigit(*(pl->line)) ||
200                   !isdigit(*(pl->line+1)) || !isdigit(*(pl->line+2)) ||
201                   !isdigit(*(pl->line+3)) || !isdigit(*(pl->line+4)) ||
202                   *(pl->line+5) != '$') {
203                         return FALSE; /* non-local label encountered */
204                 }
205         }
206   }
207   if (!pl) return FALSE; /* did not find the label */
208   pl = pl->next;
209   if (!pl || !pl->line || pl->isDebug) return FALSE; /* next line not valid */
210   p = pl->line;
211   for (p = pl->line; *p && isspace(*p); p++)
212           ;
213   if (strcmp(p, "ret") == 0) return TRUE;
214   return FALSE;
215 }
216
217
218 /*-----------------------------------------------------------------*/
219 /* okToRemoveSLOC - Check if label %1 is a SLOC and not other      */
220 /* usage of it in the code depends on a value from this section    */
221 /*-----------------------------------------------------------------*/
222 FBYNAME (okToRemoveSLOC)
223 {
224   const lineNode *pl;
225   const char *sloc, *p;
226   int dummy1, dummy2, dummy3;
227
228   /* assumes that %1 as the SLOC name */
229   sloc = hTabItemWithKey (vars, 1);
230   if (sloc == NULL) return FALSE;
231   p = strstr(sloc, "sloc");
232   if (p == NULL) return FALSE;
233   p += 4;
234   if (sscanf(p, "%d_%d_%d", &dummy1, &dummy2, &dummy3) != 3) return FALSE;
235   /*TODO: ultra-paranoid: get funtion name from "head" and check that */
236   /* the sloc name begins with that.  Probably not really necessary */
237
238   /* Look for any occurance of this SLOC before the peephole match */
239   for (pl = currPl->prev; pl; pl = pl->prev) {
240         if (pl->line && !pl->isDebug && !pl->isComment
241           && *pl->line != ';' && strstr(pl->line, sloc))
242                 return FALSE;
243   }
244   /* Look for any occurance of this SLOC after the peephole match */
245   for (pl = endPl->next; pl; pl = pl->next) {
246         if (pl->line && !pl->isDebug && !pl->isComment
247           && *pl->line != ';' && strstr(pl->line, sloc))
248                 return FALSE;
249   }
250   return TRUE; /* safe for a peephole to remove it :) */
251 }
252
253
254 /*-----------------------------------------------------------------*/
255 /* operandsNotSame - check if %1 & %2 are the same                 */
256 /*-----------------------------------------------------------------*/
257 FBYNAME (operandsNotSame)
258 {
259   char *op1 = hTabItemWithKey (vars, 1);
260   char *op2 = hTabItemWithKey (vars, 2);
261
262   if (strcmp (op1, op2) == 0)
263     return FALSE;
264   else
265     return TRUE;
266 }
267
268 /*-----------------------------------------------------------------*/
269 /* operandsNotSame3- check if any pair of %1,%2,%3 are the same    */
270 /*-----------------------------------------------------------------*/
271 FBYNAME (operandsNotSame3)
272 {
273   char *op1 = hTabItemWithKey (vars, 1);
274   char *op2 = hTabItemWithKey (vars, 2);
275   char *op3 = hTabItemWithKey (vars, 3);
276
277   if ( (strcmp (op1, op2) == 0) ||
278        (strcmp (op1, op3) == 0) ||
279        (strcmp (op2, op3) == 0) )
280     return FALSE;
281   else
282     return TRUE;
283 }
284
285 /*-----------------------------------------------------------------*/
286 /* operandsNotSame4- check if any pair of %1,%2,%3,.. are the same */
287 /*-----------------------------------------------------------------*/
288 FBYNAME (operandsNotSame4)
289 {
290   char *op1 = hTabItemWithKey (vars, 1);
291   char *op2 = hTabItemWithKey (vars, 2);
292   char *op3 = hTabItemWithKey (vars, 3);
293   char *op4 = hTabItemWithKey (vars, 4);
294
295   if ( (strcmp (op1, op2) == 0) ||
296        (strcmp (op1, op3) == 0) ||
297        (strcmp (op1, op4) == 0) ||
298        (strcmp (op2, op3) == 0) ||
299        (strcmp (op2, op4) == 0) ||
300        (strcmp (op3, op4) == 0) )
301     return FALSE;
302   else
303     return TRUE;
304 }
305
306 /*-----------------------------------------------------------------*/
307 /* operandsNotSame5- check if any pair of %1,%2,%3,.. are the same */
308 /*-----------------------------------------------------------------*/
309 FBYNAME (operandsNotSame5)
310 {
311   char *op1 = hTabItemWithKey (vars, 1);
312   char *op2 = hTabItemWithKey (vars, 2);
313   char *op3 = hTabItemWithKey (vars, 3);
314   char *op4 = hTabItemWithKey (vars, 4);
315   char *op5 = hTabItemWithKey (vars, 5);
316
317   if ( (strcmp (op1, op2) == 0) ||
318        (strcmp (op1, op3) == 0) ||
319        (strcmp (op1, op4) == 0) ||
320        (strcmp (op1, op5) == 0) ||
321        (strcmp (op2, op3) == 0) ||
322        (strcmp (op2, op4) == 0) ||
323        (strcmp (op2, op5) == 0) ||
324        (strcmp (op3, op4) == 0) ||
325        (strcmp (op3, op5) == 0) ||
326        (strcmp (op4, op5) == 0) )
327     return FALSE;
328   else
329     return TRUE;
330 }
331
332 /*-----------------------------------------------------------------*/
333 /* operandsNotSame6- check if any pair of %1,%2,%3,.. are the same */
334 /*-----------------------------------------------------------------*/
335 FBYNAME (operandsNotSame6)
336 {
337   char *op1 = hTabItemWithKey (vars, 1);
338   char *op2 = hTabItemWithKey (vars, 2);
339   char *op3 = hTabItemWithKey (vars, 3);
340   char *op4 = hTabItemWithKey (vars, 4);
341   char *op5 = hTabItemWithKey (vars, 5);
342   char *op6 = hTabItemWithKey (vars, 6);
343
344   if ( (strcmp (op1, op2) == 0) ||
345        (strcmp (op1, op3) == 0) ||
346        (strcmp (op1, op4) == 0) ||
347        (strcmp (op1, op5) == 0) ||
348        (strcmp (op1, op6) == 0) ||
349        (strcmp (op2, op3) == 0) ||
350        (strcmp (op2, op4) == 0) ||
351        (strcmp (op2, op5) == 0) ||
352        (strcmp (op2, op6) == 0) ||
353        (strcmp (op3, op4) == 0) ||
354        (strcmp (op3, op5) == 0) ||
355        (strcmp (op3, op6) == 0) ||
356        (strcmp (op4, op5) == 0) ||
357        (strcmp (op4, op6) == 0) ||
358        (strcmp (op5, op6) == 0) )
359     return FALSE;
360   else
361     return TRUE;
362 }
363
364
365 /*-----------------------------------------------------------------*/
366 /* operandsNotSame7- check if any pair of %1,%2,%3,.. are the same */
367 /*-----------------------------------------------------------------*/
368 FBYNAME (operandsNotSame7)
369 {
370   char *op1 = hTabItemWithKey (vars, 1);
371   char *op2 = hTabItemWithKey (vars, 2);
372   char *op3 = hTabItemWithKey (vars, 3);
373   char *op4 = hTabItemWithKey (vars, 4);
374   char *op5 = hTabItemWithKey (vars, 5);
375   char *op6 = hTabItemWithKey (vars, 6);
376   char *op7 = hTabItemWithKey (vars, 7);
377
378   if ( (strcmp (op1, op2) == 0) ||
379        (strcmp (op1, op3) == 0) ||
380        (strcmp (op1, op4) == 0) ||
381        (strcmp (op1, op5) == 0) ||
382        (strcmp (op1, op6) == 0) ||
383        (strcmp (op1, op7) == 0) ||
384        (strcmp (op2, op3) == 0) ||
385        (strcmp (op2, op4) == 0) ||
386        (strcmp (op2, op5) == 0) ||
387        (strcmp (op2, op6) == 0) ||
388        (strcmp (op2, op7) == 0) ||
389        (strcmp (op3, op4) == 0) ||
390        (strcmp (op3, op5) == 0) ||
391        (strcmp (op3, op6) == 0) ||
392        (strcmp (op3, op7) == 0) ||
393        (strcmp (op4, op5) == 0) ||
394        (strcmp (op4, op6) == 0) ||
395        (strcmp (op4, op7) == 0) ||
396        (strcmp (op5, op6) == 0) ||
397        (strcmp (op5, op7) == 0) ||
398        (strcmp (op6, op7) == 0) )
399     return FALSE;
400   else
401     return TRUE;
402 }
403
404 /*-----------------------------------------------------------------*/
405 /* operandsNotSame8- check if any pair of %1,%2,%3,.. are the same */
406 /*-----------------------------------------------------------------*/
407 FBYNAME (operandsNotSame8)
408 {
409   char *op1 = hTabItemWithKey (vars, 1);
410   char *op2 = hTabItemWithKey (vars, 2);
411   char *op3 = hTabItemWithKey (vars, 3);
412   char *op4 = hTabItemWithKey (vars, 4);
413   char *op5 = hTabItemWithKey (vars, 5);
414   char *op6 = hTabItemWithKey (vars, 6);
415   char *op7 = hTabItemWithKey (vars, 7);
416   char *op8 = hTabItemWithKey (vars, 8);
417
418   if ( (strcmp (op1, op2) == 0) ||
419        (strcmp (op1, op3) == 0) ||
420        (strcmp (op1, op4) == 0) ||
421        (strcmp (op1, op5) == 0) ||
422        (strcmp (op1, op6) == 0) ||
423        (strcmp (op1, op7) == 0) ||
424        (strcmp (op1, op8) == 0) ||
425        (strcmp (op2, op3) == 0) ||
426        (strcmp (op2, op4) == 0) ||
427        (strcmp (op2, op5) == 0) ||
428        (strcmp (op2, op6) == 0) ||
429        (strcmp (op2, op7) == 0) ||
430        (strcmp (op2, op8) == 0) ||
431        (strcmp (op3, op4) == 0) ||
432        (strcmp (op3, op5) == 0) ||
433        (strcmp (op3, op6) == 0) ||
434        (strcmp (op3, op7) == 0) ||
435        (strcmp (op3, op8) == 0) ||
436        (strcmp (op4, op5) == 0) ||
437        (strcmp (op4, op6) == 0) ||
438        (strcmp (op4, op7) == 0) ||
439        (strcmp (op4, op8) == 0) ||
440        (strcmp (op5, op6) == 0) ||
441        (strcmp (op5, op7) == 0) ||
442        (strcmp (op5, op8) == 0) ||
443        (strcmp (op6, op7) == 0) ||
444        (strcmp (op6, op8) == 0) ||
445        (strcmp (op7, op8) == 0) )
446     return FALSE;
447   else
448     return TRUE;
449 }
450
451
452 /* labelRefCount:
453
454  * takes two parameters: a variable (bound to a label name)
455  * and an expected reference count.
456  *
457  * Returns TRUE if that label is defined and referenced exactly
458  * the given number of times.
459  */
460 FBYNAME (labelRefCount)
461 {
462   int varNumber, expectedRefCount;
463   bool rc = FALSE;
464
465   /* If we don't have the label hash table yet, build it. */
466   if (!labelHash)
467     {
468       buildLabelRefCountHash (head);
469     }
470
471   if (sscanf (cmdLine, "%*[ \t%]%d %d", &varNumber, &expectedRefCount) == 2)
472     {
473       char *label = hTabItemWithKey (vars, varNumber);
474
475       if (label)
476         {
477           labelHashEntry *entry;
478
479           entry = hTabFirstItemWK (labelHash, hashSymbolName (label));
480
481           while (entry)
482             {
483               if (!strcmp (label, entry->name))
484                 {
485                   break;
486                 }
487               entry = hTabNextItemWK (labelHash);
488             }
489           if (entry)
490             {
491 #if 0
492               /* debug spew. */
493               fprintf (stderr, "labelRefCount: %s has refCount %d, want %d\n",
494                        label, entry->refCount, expectedRefCount);
495 #endif
496
497               rc = (expectedRefCount == entry->refCount);
498             }
499           else
500             {
501               fprintf (stderr, "*** internal error: no label has entry for"
502                        " %s in labelRefCount peephole.\n",
503                        label);
504             }
505         }
506       else
507         {
508           fprintf (stderr, "*** internal error: var %d not bound"
509                    " in peephole labelRefCount rule.\n",
510                    varNumber);
511         }
512
513     }
514   else
515     {
516       fprintf (stderr,
517                "*** internal error: labelRefCount peephole restriction"
518                " malformed: %s\n", cmdLine);
519     }
520   return rc;
521 }
522
523 /* Within the context of the lines currPl through endPl, determine
524 ** if the variable var contains a symbol that is volatile. Returns
525 ** TRUE only if it is certain that this was not volatile (the symbol
526 ** was found and not volatile, or var was a constant or CPU register).
527 ** Returns FALSE if the symbol was found and volatile, the symbol was
528 ** not found, or var was a indirect/pointer addressing mode.
529 */
530 static bool
531 notVolatileVariable(char *var, lineNode *currPl, lineNode *endPl)
532 {
533   char symname[SDCC_NAME_MAX + 1];
534   char *p = symname;
535   char *vp = var;
536   lineNode *cl;
537   operand *op;
538   iCode *last_ic;
539
540   /* Can't tell if indirect accesses are volatile or not, so
541   ** assume they are, just to be safe.
542   */
543   if (TARGET_IS_MCS51 || TARGET_IS_DS390 || TARGET_IS_DS400)
544     {
545       if (*var=='@')
546         return FALSE;
547     }
548   if (TARGET_IS_Z80 || TARGET_IS_GBZ80)
549     {
550       if (strstr(var,"(bc)"))
551         return FALSE;
552       if (strstr(var,"(de)"))
553         return FALSE;
554       if (strstr(var,"(hl)"))
555         return FALSE;
556       if (strstr(var,"(ix"))
557         return FALSE;
558       if (strstr(var,"(iy"))
559         return FALSE;
560     }
561
562   /* Extract a symbol name from the variable */
563   while (*vp && (*vp!='_'))
564     vp++;
565   while (*vp && (isalnum(*vp) || *vp=='_'))
566     *p++ = *vp++;
567   *p='\0';
568
569   if (!symname[0])
570     {
571       /* Nothing resembling a symbol name was found, so it can't
572          be volatile
573       */
574       return TRUE;
575     }
576
577   last_ic = NULL;
578   for (cl = currPl; cl!=endPl->next; cl = cl->next)
579   {
580     if (cl->ic && (cl->ic!=last_ic))
581       {
582         last_ic = cl->ic;
583         switch (cl->ic->op)
584           {
585           case IFX:
586             op = IC_COND (cl->ic);
587             if (IS_SYMOP (op) && !strcmp(OP_SYMBOL (op)->rname,symname) )
588               return !op->isvolatile;
589           case JUMPTABLE:
590             op = IC_JTCOND (cl->ic);
591             if (IS_SYMOP (op) && !strcmp(OP_SYMBOL (op)->rname,symname) )
592               return !op->isvolatile;
593           default:
594             op = IC_LEFT (cl->ic);
595             if (IS_SYMOP (op) && !strcmp(OP_SYMBOL (op)->rname,symname) )
596               return !op->isvolatile;
597             op = IC_RIGHT (cl->ic);
598             if (IS_SYMOP (op) && !strcmp(OP_SYMBOL (op)->rname,symname) )
599               return !op->isvolatile;
600             op = IC_RESULT (cl->ic);
601             if (IS_SYMOP (op) && !strcmp(OP_SYMBOL (op)->rname,symname) )
602               return !op->isvolatile;
603           }
604       }
605   }
606   
607   /* Couldn't find the symbol for some reason. Assume volatile. */
608   return FALSE;
609 }
610
611 /*  notVolatile:
612  *
613  *  This rule restriction has two different behaviours depending on
614  *  the number of parameters given.
615  *
616  *    if notVolatile                 (no parameters given)
617  *       The rule is applied only if none of the iCodes originating
618  *       the matched pattern reference a volatile operand.
619  *
620  *    if notVolatile %1 ...          (one or more parameters given)
621  *       The rule is applied if the parameters are not expressions
622  *       containing volatile symbols and are not pointer accesses.
623  *
624  */
625 FBYNAME (notVolatile)
626 {
627   int varNumber;
628   char *var;
629   bool notvol;
630   char *digitend;
631   lineNode *cl;
632   operand *op;
633
634   if (!cmdLine)
635     {
636       /* If no parameters given, just scan the iCodes for volatile operands */
637       for (cl = currPl; cl!=endPl->next; cl = cl->next)
638       {
639         if (cl->ic)
640           {
641             switch (cl->ic->op)
642               {
643               case IFX:
644                 op = IC_COND (cl->ic);
645                 if (IS_SYMOP (op) && op->isvolatile)
646                   return FALSE;
647               case JUMPTABLE:
648                 op = IC_JTCOND (cl->ic);
649                 if (IS_SYMOP (op) && op->isvolatile)
650                   return FALSE;
651               default:
652                 op = IC_LEFT (cl->ic);
653                 if (IS_SYMOP (op) && op->isvolatile)
654                   return FALSE;
655                 op = IC_RIGHT (cl->ic);
656                 if (IS_SYMOP (op) && op->isvolatile)
657                   return FALSE;
658                 op = IC_RESULT (cl->ic);
659                 if (IS_SYMOP (op) && op->isvolatile)
660                   return FALSE;
661               }
662           }
663       }
664       return TRUE;
665     }
666
667   /* There were parameters; check the volatility of each */
668   while (*cmdLine && isspace(*cmdLine))
669     cmdLine++;
670   while (*cmdLine)
671     {
672       if (*cmdLine!='%')
673         goto error;
674       cmdLine++;
675       if (!isdigit(*cmdLine))
676         goto error;
677       varNumber = strtol(cmdLine, &digitend, 10);
678       cmdLine = digitend;
679       while (*cmdLine && isspace(*cmdLine))
680         cmdLine++;
681
682       var = hTabItemWithKey (vars, varNumber);
683
684       if (var)
685         {
686           notvol = notVolatileVariable (var, currPl, endPl);
687           if (!notvol)
688             return FALSE;
689         }
690       else
691         {
692           fprintf (stderr, "*** internal error: var %d not bound"
693                    " in peephole notVolatile rule.\n",
694                    varNumber);
695           return FALSE;
696         }
697     }
698
699   return TRUE;
700     
701     
702 error:
703   fprintf (stderr,
704            "*** internal error: notVolatile peephole restriction"
705            " malformed: %s\n", cmdLine);
706   return FALSE;
707 }
708     
709
710 /*-----------------------------------------------------------------*/
711 /* callFuncByName - calls a function as defined in the table       */
712 /*-----------------------------------------------------------------*/
713 int 
714 callFuncByName (char *fname,
715                 hTab * vars,
716                 lineNode * currPl,
717                 lineNode * endPl,
718                 lineNode * head)
719 {
720   struct ftab
721   {
722     char *fname;
723     int (*func) (hTab *, lineNode *, lineNode *, lineNode *, const char *);
724   }
725   ftab[] =
726   {
727     {
728       "labelInRange", labelInRange
729     }
730     ,
731     {
732       "operandsNotSame", operandsNotSame
733     }
734     ,
735     {
736       "operandsNotSame3", operandsNotSame3
737     }
738     ,
739     {
740       "operandsNotSame4", operandsNotSame4
741     }
742     ,
743     {
744       "operandsNotSame5", operandsNotSame5
745     }
746     ,
747     {
748       "operandsNotSame6", operandsNotSame6
749     }
750     ,
751     {
752       "operandsNotSame7", operandsNotSame7
753     }
754     ,
755     {
756       "operandsNotSame8", operandsNotSame8
757     }
758     ,     
759     {
760       "24bitMode", flat24bitMode
761     }
762     ,
763     {
764       "xramMovcOption", xramMovcOption
765     }
766     ,
767     {
768       "labelRefCount", labelRefCount
769     }
770     ,
771     {
772       "portIsDS390", portIsDS390
773     },
774     {
775       "labelIsReturnOnly", labelIsReturnOnly
776     },
777     {
778       "okToRemoveSLOC", okToRemoveSLOC
779     },
780     {
781       "24bitModeAndPortDS390", flat24bitModeAndPortDS390
782     },
783     {
784       "notVolatile", notVolatile
785     }
786   };
787   int   i;
788   char  *cmdCopy, *funcName, *funcArgs;
789   int   rc = -1;
790     
791   /* Isolate the function name part (we are passed the full condition 
792    * string including arguments) 
793    */
794   cmdCopy = Safe_strdup(fname);
795   funcName = strtok(cmdCopy, " \t");
796   funcArgs = strtok(NULL, "");
797
798     for (i = 0; i < ((sizeof (ftab)) / (sizeof (struct ftab))); i++)
799     {
800         if (strcmp (ftab[i].fname, funcName) == 0)
801         {
802             rc = (*ftab[i].func) (vars, currPl, endPl, head,
803                                   funcArgs);
804         }
805     }
806     
807     if (rc == -1)
808     {
809         fprintf (stderr, 
810                  "could not find named function \"%s\" in "
811                  "peephole function table\n",
812                  funcName);
813         // If the function couldn't be found, let's assume it's
814         // a bad rule and refuse it.
815         rc = FALSE;
816     }
817
818     Safe_free(cmdCopy);
819     
820   return rc;
821 }
822
823 /*-----------------------------------------------------------------*/
824 /* printLine - prints a line chain into a given file               */
825 /*-----------------------------------------------------------------*/
826 void 
827 printLine (lineNode * head, FILE * of)
828 {
829   iCode *last_ic = NULL;
830   bool debug_iCode_tracking = (getenv("DEBUG_ICODE_TRACKING")!=NULL);
831   
832   if (!of)
833     of = stdout;
834
835   while (head)
836     {
837       if (head->ic!=last_ic)
838         {
839           last_ic = head->ic;
840           if (debug_iCode_tracking)
841             {
842               if (head->ic)
843                 fprintf (of, "; block = %d, seq = %d\n",
844                          head->ic->block, head->ic->seq);
845               else
846                 fprintf (of, "; iCode lost\n");
847             }
848         }
849         
850       /* don't indent comments & labels */
851       if (head->line &&
852           (*head->line == ';' ||
853            head->line[strlen (head->line) - 1] == ':')) {
854         fprintf (of, "%s\n", head->line);
855       } else {
856         if (head->isInline && *head->line=='#') {
857           // comment out preprocessor directives in inline asm
858           fprintf (of, ";");
859         }
860         fprintf (of, "\t%s\n", head->line);
861       }
862       head = head->next;
863     }
864 }
865
866 /*-----------------------------------------------------------------*/
867 /* newPeepRule - creates a new peeprule and attach it to the root  */
868 /*-----------------------------------------------------------------*/
869 peepRule *
870 newPeepRule (lineNode * match,
871              lineNode * replace,
872              char *cond,
873              int restart)
874 {
875   peepRule *pr;
876
877   pr = Safe_alloc ( sizeof (peepRule));
878   pr->match = match;
879   pr->replace = replace;
880   pr->restart = restart;
881
882   if (cond && *cond)
883     {
884       pr->cond = Safe_strdup (cond);
885     }
886   else
887     pr->cond = NULL;
888
889   pr->vars = newHashTable (100);
890
891   /* if root is empty */
892   if (!rootRules)
893     rootRules = currRule = pr;
894   else
895     currRule = currRule->next = pr;
896
897   return pr;
898 }
899
900 /*-----------------------------------------------------------------*/
901 /* newLineNode - creates a new peep line                           */
902 /*-----------------------------------------------------------------*/
903 lineNode *
904 newLineNode (char *line)
905 {
906   lineNode *pl;
907
908   pl = Safe_alloc ( sizeof (lineNode));
909   pl->line = Safe_strdup (line);
910   pl->ic = NULL;
911   return pl;
912 }
913
914 /*-----------------------------------------------------------------*/
915 /* connectLine - connects two lines                                */
916 /*-----------------------------------------------------------------*/
917 lineNode *
918 connectLine (lineNode * pl1, lineNode * pl2)
919 {
920   if (!pl1 || !pl2)
921     {
922       fprintf (stderr, "trying to connect null line\n");
923       return NULL;
924     }
925
926   pl2->prev = pl1;
927   pl1->next = pl2;
928
929   return pl2;
930 }
931
932 #define SKIP_SPACE(x,y) { while (*x && (isspace(*x) || *x == '\n')) x++; \
933                          if (!*x) { fprintf(stderr,y); return ; } }
934
935 #define EXPECT_STR(x,y,z) { while (*x && strncmp(x,y,strlen(y))) x++ ;   \
936                            if (!*x) { fprintf(stderr,z); return ; } }
937 #define EXPECT_CHR(x,y,z) { while (*x && *x != y) x++ ;   \
938                            if (!*x) { fprintf(stderr,z); return ; } }
939
940 /*-----------------------------------------------------------------*/
941 /* getPeepLine - parses the peep lines                             */
942 /*-----------------------------------------------------------------*/
943 static void 
944 getPeepLine (lineNode ** head, char **bpp)
945 {
946   char lines[MAX_PATTERN_LEN];
947   char *lp;
948   int isComment;
949
950   lineNode *currL = NULL;
951   char *bp = *bpp;
952   while (1)
953     {
954
955       if (!*bp)
956         {
957           fprintf (stderr, "unexpected end of match pattern\n");
958           return;
959         }
960
961       if (*bp == '\n')
962         {
963           bp++;
964           while (isspace (*bp) ||
965                  *bp == '\n')
966             bp++;
967         }
968
969       if (*bp == '}')
970         {
971           bp++;
972           break;
973         }
974
975       /* read till end of line */
976       lp = lines;
977       while ((*bp != '\n' && *bp != '}') && *bp)
978         *lp++ = *bp++;
979       *lp = '\0';
980       
981       lp = lines;
982       while (*lp && isspace(*lp))
983         lp++;
984       isComment = (*lp == ';');
985         
986       if (!isComment || (isComment && !options.noPeepComments))
987         {
988           if (!currL)
989             *head = currL = newLineNode (lines);
990           else
991             currL = connectLine (currL, newLineNode (lines));
992           currL->isComment = isComment;
993         }
994
995     }
996
997   *bpp = bp;
998 }
999
1000 /*-----------------------------------------------------------------*/
1001 /* readRules - reads the rules from a string buffer                */
1002 /*-----------------------------------------------------------------*/
1003 static void 
1004 readRules (char *bp)
1005 {
1006   char restart = 0;
1007   char lines[MAX_PATTERN_LEN];
1008   char *lp;
1009   lineNode *match;
1010   lineNode *replace;
1011   lineNode *currL = NULL;
1012
1013   if (!bp)
1014     return;
1015 top:
1016   restart = 0;
1017   /* look for the token "replace" that is the
1018      start of a rule */
1019   while (*bp && strncmp (bp, "replace", 7))
1020     bp++;
1021
1022   /* if not found */
1023   if (!*bp)
1024     return;
1025
1026   /* then look for either "restart" or '{' */
1027   while (strncmp (bp, "restart", 7) &&
1028          *bp != '{' && bp)
1029     bp++;
1030
1031   /* not found */
1032   if (!*bp)
1033     {
1034       fprintf (stderr, "expected 'restart' or '{'\n");
1035       return;
1036     }
1037
1038   /* if brace */
1039   if (*bp == '{')
1040     bp++;
1041   else
1042     {                           /* must be restart */
1043       restart++;
1044       bp += strlen ("restart");
1045       /* look for '{' */
1046       EXPECT_CHR (bp, '{', "expected '{'\n");
1047       bp++;
1048     }
1049
1050   /* skip thru all the blank space */
1051   SKIP_SPACE (bp, "unexpected end of rule\n");
1052
1053   match = replace = currL = NULL;
1054   /* we are the start of a rule */
1055   getPeepLine (&match, &bp);
1056
1057   /* now look for by */
1058   EXPECT_STR (bp, "by", "expected 'by'\n");
1059
1060   /* then look for a '{' */
1061   EXPECT_CHR (bp, '{', "expected '{'\n");
1062   bp++;
1063
1064   SKIP_SPACE (bp, "unexpected end of rule\n");
1065   getPeepLine (&replace, &bp);
1066
1067   /* look for a 'if' */
1068   while ((isspace (*bp) || *bp == '\n') && *bp)
1069     bp++;
1070
1071   if (strncmp (bp, "if", 2) == 0)
1072     {
1073       bp += 2;
1074       while ((isspace (*bp) || *bp == '\n') && *bp)
1075         bp++;
1076       if (!*bp)
1077         {
1078           fprintf (stderr, "expected condition name\n");
1079           return;
1080         }
1081
1082       /* look for the condition */
1083       lp = lines;
1084       while (*bp && (*bp != '\n'))
1085         {
1086           *lp++ = *bp++;
1087         }
1088       *lp = '\0';
1089
1090       newPeepRule (match, replace, lines, restart);
1091     }
1092   else
1093     newPeepRule (match, replace, NULL, restart);
1094   goto top;
1095
1096 }
1097
1098 /*-----------------------------------------------------------------*/
1099 /* keyForVar - returns the numeric key for a var                   */
1100 /*-----------------------------------------------------------------*/
1101 static int 
1102 keyForVar (char *d)
1103 {
1104   int i = 0;
1105
1106   while (isdigit (*d))
1107     {
1108       i *= 10;
1109       i += (*d++ - '0');
1110     }
1111
1112   return i;
1113 }
1114
1115 /*-----------------------------------------------------------------*/
1116 /* bindVar - binds a value to a variable in the given hashtable    */
1117 /*-----------------------------------------------------------------*/
1118 static void 
1119 bindVar (int key, char **s, hTab ** vtab)
1120 {
1121   char vval[MAX_PATTERN_LEN];
1122   char *vvx;
1123   char *vv = vval;
1124
1125   /* first get the value of the variable */
1126   vvx = *s;
1127   /* the value is ended by a ',' or space or newline or null or ) */
1128   while (*vvx &&
1129          *vvx != ',' &&
1130          !isspace (*vvx) &&
1131          *vvx != '\n' &&
1132          *vvx != ':' &&
1133          *vvx != ')')
1134     {
1135       char ubb = 0;
1136       /* if we find a '(' then we need to balance it */
1137       if (*vvx == '(')
1138         {
1139           ubb++;
1140           while (ubb)
1141             {
1142               *vv++ = *vvx++;
1143               if (*vvx == '(')
1144                 ubb++;
1145               if (*vvx == ')')
1146                 ubb--;
1147             }
1148           // include the trailing ')'
1149           *vv++ = *vvx++;
1150         }
1151       else
1152         *vv++ = *vvx++;
1153     }
1154   *s = vvx;
1155   *vv = '\0';
1156   /* got value */
1157   vvx = traceAlloc (&_G.values, Safe_strdup(vval));
1158
1159   hTabAddItem (vtab, key, vvx);
1160 }
1161
1162 /*-----------------------------------------------------------------*/
1163 /* matchLine - matches one line                                    */
1164 /*-----------------------------------------------------------------*/
1165 static bool 
1166 matchLine (char *s, char *d, hTab ** vars)
1167 {
1168
1169   if (!s || !(*s))
1170     return FALSE;
1171
1172   while (*s && *d)
1173     {
1174
1175       /* skip white space in both */
1176       while (isspace (*s))
1177         s++;
1178       while (isspace (*d))
1179         d++;
1180
1181       /* if the destination is a var */
1182       if (*d == '%' && isdigit (*(d + 1)) && vars)
1183         {
1184           char *v = hTabItemWithKey (*vars, keyForVar (d + 1));
1185           /* if the variable is already bound
1186              then it MUST match with dest */
1187           if (v)
1188             {
1189               while (*v)
1190                 if (*v++ != *s++)
1191                   return FALSE;
1192             }
1193           else
1194             /* variable not bound we need to
1195                bind it */
1196             bindVar (keyForVar (d + 1), &s, vars);
1197
1198           /* in either case go past the variable */
1199           d++;
1200           while (isdigit (*d))
1201             d++;
1202
1203           while (isspace (*s))
1204             s++;
1205           while (isspace (*d))
1206             d++;
1207         }
1208
1209       /* they should be an exact match other wise */
1210       if (*s && *d)
1211         {
1212           if (*s++ != *d++)
1213             return FALSE;
1214         }
1215
1216     }
1217
1218   /* get rid of the trailing spaces
1219      in both source & destination */
1220   if (*s)
1221     while (isspace (*s))
1222       s++;
1223
1224   if (*d)
1225     while (isspace (*d))
1226       d++;
1227
1228   /* after all this if only one of them
1229      has something left over then no match */
1230   if (*s || *d)
1231     return FALSE;
1232
1233   return TRUE;
1234 }
1235
1236 /*-----------------------------------------------------------------*/
1237 /* matchRule - matches a all the rule lines                        */
1238 /*-----------------------------------------------------------------*/
1239 static bool 
1240 matchRule (lineNode * pl,
1241            lineNode ** mtail,
1242            peepRule * pr,
1243            lineNode * head)
1244 {
1245   lineNode *spl;                /* source pl */
1246   lineNode *rpl;                /* rule peep line */
1247
1248 /*     setToNull((void *) &pr->vars);    */
1249 /*     pr->vars = newHashTable(100); */
1250
1251   /* for all the lines defined in the rule */
1252   rpl = pr->match;
1253   spl = pl;
1254   while (spl && rpl)
1255     {
1256
1257       /* if the source line starts with a ';' then
1258          comment line don't process or the source line
1259          contains == . debugger information skip it */
1260       if (spl->line &&
1261           (*spl->line == ';' || spl->isDebug))
1262         {
1263           spl = spl->next;
1264           continue;
1265         }
1266
1267       if (!matchLine (spl->line, rpl->line, &pr->vars))
1268         return FALSE;
1269
1270       rpl = rpl->next;
1271       if (rpl)
1272         spl = spl->next;
1273     }
1274
1275   /* if rules ended */
1276   if (!rpl)
1277     {
1278       /* if this rule has additional conditions */
1279       if (pr->cond)
1280         {
1281           if (callFuncByName (pr->cond, pr->vars, pl, spl, head))
1282             {
1283               *mtail = spl;
1284               return TRUE;
1285             }
1286           else
1287             return FALSE;
1288         }
1289       else
1290         {
1291           *mtail = spl;
1292           return TRUE;
1293         }
1294     }
1295   else
1296     return FALSE;
1297 }
1298
1299 static void
1300 reassociate_ic_down (lineNode *shead, lineNode *stail,
1301                      lineNode *rhead, lineNode *rtail)
1302 {
1303   lineNode *csl;        /* current source line */
1304   lineNode *crl;        /* current replacement line */
1305
1306   csl = shead;
1307   crl = rhead;
1308   while (1)
1309     {
1310       /* skip over any comments */
1311       while (csl!=stail->next && csl->isComment)
1312         csl = csl->next;
1313       while (crl!=rtail->next && crl->isComment)
1314         crl = crl->next;
1315
1316       /* quit if we reach the end */
1317       if ((csl==stail->next) || (crl==rtail->next) || crl->ic)
1318         break;
1319
1320       if (matchLine(csl->line,crl->line,NULL))
1321         {
1322           crl->ic = csl->ic;
1323           csl = csl->next;
1324           crl = crl->next;
1325         }
1326       else
1327         break;
1328     }
1329 }
1330
1331 static void
1332 reassociate_ic_up (lineNode *shead, lineNode *stail,
1333                    lineNode *rhead, lineNode *rtail)
1334 {
1335   lineNode *csl;        /* current source line */
1336   lineNode *crl;        /* current replacement line */
1337
1338   csl = stail;
1339   crl = rtail;
1340   while (1)
1341     {
1342       /* skip over any comments */
1343       while (csl!=shead->prev && csl->isComment)
1344         csl = csl->prev;
1345       while (crl!=rhead->prev && crl->isComment)
1346         crl = crl->prev;
1347
1348       /* quit if we reach the end */
1349       if ((csl==shead->prev) || (crl==rhead->prev) || crl->ic)
1350         break;
1351
1352       if (matchLine(csl->line,crl->line,NULL))
1353         {
1354           crl->ic = csl->ic;
1355           csl = csl->prev;
1356           crl = crl->prev;
1357         }
1358       else
1359         break;
1360     }
1361 }
1362
1363 /*------------------------------------------------------------------*/
1364 /* reassociate_ic - reassociate replacement lines with origin iCode */
1365 /*------------------------------------------------------------------*/
1366 static void
1367 reassociate_ic (lineNode *shead, lineNode *stail,
1368                 lineNode *rhead, lineNode *rtail)
1369 {
1370   lineNode *csl;        /* current source line */
1371   lineNode *crl;        /* current replacement line */
1372   bool single_iCode;
1373   iCode *ic;
1374   
1375   /* Check to see if all the source lines (excluding comments) came
1376   ** for the same iCode
1377   */
1378   ic = NULL;
1379   for (csl=shead;csl!=stail->next;csl=csl->next)
1380     if (csl->ic && !csl->isComment)
1381       {
1382         ic = csl->ic;
1383         break;
1384       }
1385   single_iCode = (ic!=NULL);
1386   for (csl=shead;csl!=stail->next;csl=csl->next)
1387     if ((csl->ic != ic) && !csl->isComment)
1388       {
1389         /* More than one iCode was found. However, if it's just the
1390         ** last line with the different iCode and it was not changed
1391         ** in the replacement, everything else must be the first iCode.
1392         */
1393         if ((csl==stail) && matchLine (stail->line, rtail->line, NULL))
1394           {
1395             rtail->ic = stail->ic;
1396             for (crl=rhead;crl!=rtail;crl=crl->next)
1397               crl->ic = ic;
1398             return;
1399           }
1400             
1401         single_iCode = FALSE;
1402         break;
1403       }
1404   
1405   /* If all of the source lines came from the same iCode, then so have
1406   ** all of the replacement lines too.
1407   */
1408   if (single_iCode)
1409     {
1410       for (crl=rhead;crl!=rtail->next;crl=crl->next)
1411         crl->ic = ic;
1412       return;
1413     }
1414   
1415   /* The source lines span iCodes, so we may end up with replacement
1416   ** lines that we don't know which iCode(s) to associate with. Do the
1417   ** best we can by using the following strategies:
1418   **    1) Start at the top and scan down. As long as the source line
1419   **       matches the replacement line, they have the same iCode.
1420   **    2) Start at the bottom and scan up. As long as the source line
1421   **       matches the replacement line, they have the same iCode.
1422   **    3) For any label in the source, look for a matching label in
1423   **       the replacment. If found, they have the same iCode. From
1424   **       these matching labels, scan down for additional matching
1425   **       lines; if found, they also have the same iCode.
1426   */
1427
1428   /* Strategy #1: Start at the top and scan down for matches
1429   */
1430   reassociate_ic_down(shead,stail,rhead,rtail);
1431   
1432   /* Strategy #2: Start at the bottom and scan up for matches
1433   */
1434   reassociate_ic_up(shead,stail,rhead,rtail);
1435
1436   /* Strategy #3: Try to match labels
1437   */
1438   csl = shead;
1439   while (1)
1440     {
1441       const char *labelStart;
1442       int labelLength;
1443       
1444       /* skip over any comments */
1445       while (csl!=stail->next && csl->isComment)
1446         csl = csl->next;
1447       if (csl==stail->next)
1448         break;
1449
1450       if (isLabelDefinition(csl->line, &labelStart, &labelLength))
1451         {
1452           /* found a source line label; look for it in the replacment lines */
1453           crl = rhead;
1454           while (1)
1455             {
1456               while (crl!=rtail->next && crl->isComment)
1457                 crl = crl->next;
1458               if (crl==rtail->next)
1459                 break;
1460               if (matchLine(csl->line, crl->line, NULL))
1461                 {
1462                   reassociate_ic_down(csl,stail,crl,rtail);
1463                   break;
1464                 }
1465               else
1466                 crl = crl->next;
1467             }
1468         }
1469       csl = csl->next;
1470     }
1471   
1472   /* Try to assign a meaningful iCode to any comment that is missing
1473      one. Since they are comments, it's ok to make mistakes; we are just
1474      trying to improve continuity to simplify other tests.
1475   */
1476   ic = NULL;
1477   for (crl=rtail;crl!=rhead->prev;crl=crl->prev)
1478     {
1479       if (!crl->ic && ic && crl->isComment)
1480         crl->ic = ic;
1481       ic = crl->ic;
1482     }
1483 }
1484
1485                   
1486 /*-----------------------------------------------------------------*/
1487 /* replaceRule - does replacement of a matching pattern            */
1488 /*-----------------------------------------------------------------*/
1489 static void 
1490 replaceRule (lineNode ** shead, lineNode * stail, peepRule * pr)
1491 {
1492   lineNode *cl = NULL;
1493   lineNode *pl = NULL, *lhead = NULL;
1494   /* a long function name and long variable name can evaluate to
1495      4x max pattern length e.g. "mov dptr,((fie_var>>8)<<8)+fie_var" */
1496   char lb[MAX_PATTERN_LEN*4];
1497   char *lbp;
1498   lineNode *comment = NULL;
1499
1500   /* collect all the comment lines in the source */
1501   for (cl = *shead; cl != stail; cl = cl->next)
1502     {
1503       if (cl->line && (*cl->line == ';' || cl->isDebug))
1504         {
1505           pl = (pl ? connectLine (pl, newLineNode (cl->line)) :
1506                 (comment = newLineNode (cl->line)));
1507           pl->isDebug = cl->isDebug;
1508           pl->isComment = cl->isComment || (*cl->line == ';');
1509         }
1510     }
1511   cl = NULL;
1512
1513   /* for all the lines in the replacement pattern do */
1514   for (pl = pr->replace; pl; pl = pl->next)
1515     {
1516       char *v;
1517       char *l;
1518       lbp = lb;
1519
1520       l = pl->line;
1521
1522       while (*l)
1523         {
1524           /* if the line contains a variable */
1525           if (*l == '%' && isdigit (*(l + 1)))
1526             {
1527               v = hTabItemWithKey (pr->vars, keyForVar (l + 1));
1528               if (!v)
1529                 {
1530                   fprintf (stderr, "used unbound variable in replacement\n");
1531                   l++;
1532                   continue;
1533                 }
1534               while (*v) {
1535                 *lbp++ = *v++;
1536               }
1537               l++;
1538               while (isdigit (*l)) {
1539                 l++;
1540               }
1541               continue;
1542             }
1543           *lbp++ = *l++;
1544         }
1545
1546       *lbp = '\0';
1547       if (cl)
1548         cl = connectLine (cl, newLineNode (lb));
1549       else
1550         lhead = cl = newLineNode (lb);
1551       cl->isComment = pl->isComment;
1552     }
1553
1554   /* add the comments if any to the head of list */
1555   if (comment)
1556     {
1557       lineNode *lc = comment;
1558       while (lc->next)
1559         lc = lc->next;
1560       lc->next = lhead;
1561       if (lhead)
1562         lhead->prev = lc;
1563       lhead = comment;
1564     }
1565
1566   if (lhead)
1567     {
1568       /* determine which iCodes the replacment lines relate to */
1569       reassociate_ic(*shead,stail,lhead,cl);
1570
1571       /* now we need to connect / replace the original chain */
1572       /* if there is a prev then change it */
1573       if ((*shead)->prev)
1574         {
1575           (*shead)->prev->next = lhead;
1576           lhead->prev = (*shead)->prev;
1577         }
1578       *shead = lhead;
1579       /* now for the tail */
1580       if (stail && stail->next)
1581         {
1582           stail->next->prev = cl;
1583           if (cl)
1584             cl->next = stail->next;
1585         }
1586     }
1587   else
1588     {
1589       /* the replacement is empty - delete the source lines */
1590       if ((*shead)->prev)
1591         (*shead)->prev->next = stail->next;
1592       if (stail->next)
1593         stail->next->prev = (*shead)->prev;
1594       *shead = stail->next;
1595     }
1596 }
1597
1598 /* Returns TRUE if this line is a label definition.
1599
1600  * If so, start will point to the start of the label name,
1601  * and len will be it's length.
1602  */
1603 bool 
1604 isLabelDefinition (const char *line, const char **start, int *len)
1605 {
1606   const char *cp = line;
1607
1608   /* This line is a label if if consists of:
1609    * [optional whitespace] followed by identifier chars
1610    * (alnum | $ | _ ) followed by a colon.
1611    */
1612
1613   while (*cp && isspace (*cp))
1614     {
1615       cp++;
1616     }
1617
1618   if (!*cp)
1619     {
1620       return FALSE;
1621     }
1622
1623   *start = cp;
1624
1625   while (isalnum (*cp) || (*cp == '$') || (*cp == '_'))
1626     {
1627       cp++;
1628     }
1629
1630   if ((cp == *start) || (*cp != ':'))
1631     {
1632       return FALSE;
1633     }
1634
1635   *len = (cp - (*start));
1636   return TRUE;
1637 }
1638
1639 /* Quick & dirty string hash function. */
1640 static int 
1641 hashSymbolName (const char *name)
1642 {
1643   int hash = 0;
1644
1645   while (*name)
1646     {
1647       hash = (hash << 6) ^ *name;
1648       name++;
1649     }
1650
1651   if (hash < 0)
1652     {
1653       hash = -hash;
1654     }
1655
1656   return hash % HTAB_SIZE;
1657 }
1658
1659 /* Build a hash of all labels in the passed set of lines
1660  * and how many times they are referenced.
1661  */
1662 static void 
1663 buildLabelRefCountHash (lineNode * head)
1664 {
1665   lineNode *line;
1666   const char *label;
1667   int labelLen;
1668   int i;
1669
1670   assert (labelHash == NULL);
1671   labelHash = newHashTable (HTAB_SIZE);
1672
1673   /* First pass: locate all the labels. */
1674   line = head;
1675
1676   while (line)
1677     {
1678       if (isLabelDefinition (line->line, &label, &labelLen)
1679           && labelLen <= SDCC_NAME_MAX)
1680         {
1681           labelHashEntry *entry;
1682
1683           entry = traceAlloc (&_G.labels, Safe_alloc(sizeof (labelHashEntry)));
1684
1685           memcpy (entry->name, label, labelLen);
1686           entry->name[labelLen] = 0;
1687           entry->refCount = -1;
1688
1689           hTabAddItem (&labelHash, hashSymbolName (entry->name), entry);
1690         }
1691       line = line->next;
1692     }
1693
1694
1695   /* Second pass: for each line, note all the referenced labels. */
1696   /* This is ugly, O(N^2) stuff. Optimizations welcome... */
1697   line = head;
1698   while (line)
1699     {
1700       for (i = 0; i < HTAB_SIZE; i++)
1701         {
1702           labelHashEntry *thisEntry;
1703
1704           thisEntry = hTabFirstItemWK (labelHash, i);
1705
1706           while (thisEntry)
1707             {
1708               if (strstr (line->line, thisEntry->name))
1709                 {
1710                   thisEntry->refCount++;
1711                 }
1712               thisEntry = hTabNextItemWK (labelHash);
1713             }
1714         }
1715       line = line->next;
1716     }
1717
1718 #if 0
1719   /* Spew the contents of the table. Debugging fun only. */
1720   for (i = 0; i < HTAB_SIZE; i++)
1721     {
1722       labelHashEntry *thisEntry;
1723
1724       thisEntry = hTabFirstItemWK (labelHash, i);
1725
1726       while (thisEntry)
1727         {
1728           fprintf (stderr, "label: %s ref %d\n",
1729                    thisEntry->name, thisEntry->refCount);
1730           thisEntry = hTabNextItemWK (labelHash);
1731         }
1732     }
1733 #endif
1734 }
1735
1736 /* How does this work?
1737    peepHole
1738     For each rule,
1739      For each line,
1740       Try to match
1741       If it matches,
1742        replace and restart.
1743
1744     matchRule
1745      matchLine
1746
1747   Where is stuff allocated?
1748   
1749 */
1750
1751 /*-----------------------------------------------------------------*/
1752 /* peepHole - matches & substitutes rules                          */
1753 /*-----------------------------------------------------------------*/
1754 void 
1755 peepHole (lineNode ** pls)
1756 {
1757   lineNode *spl;
1758   peepRule *pr;
1759   lineNode *mtail = NULL;
1760   bool restart;
1761
1762 #if !OPT_DISABLE_PIC || !OPT_DISABLE_PIC16
1763   /* The PIC port uses a different peep hole optimizer based on "pCode" */
1764   if (TARGET_IS_PIC || TARGET_IS_PIC16)
1765     return;
1766 #endif
1767
1768   assert(labelHash == NULL);
1769
1770   do
1771     {
1772       restart = FALSE;
1773
1774       /* for all rules */
1775       for (pr = rootRules; pr; pr = pr->next)
1776         {
1777           for (spl = *pls; spl; spl = spl->next)
1778             {
1779               /* if inline assembler then no peep hole */
1780               if (spl->isInline)
1781                 continue;
1782
1783               /* don't waste time starting a match on debug symbol
1784               ** or comment */
1785               if (spl->isDebug || spl->isComment || *(spl->line)==';')
1786                 continue;
1787               
1788               mtail = NULL;
1789
1790               /* Tidy up any data stored in the hTab */
1791               
1792               /* if it matches */
1793               if (matchRule (spl, &mtail, pr, *pls))
1794                 {
1795                   
1796                   /* then replace */
1797                   if (spl == *pls)
1798                     replaceRule (pls, mtail, pr);
1799                   else
1800                     replaceRule (&spl, mtail, pr);
1801                   
1802                   /* if restart rule type then
1803                      start at the top again */
1804                   if (pr->restart)
1805                     {
1806                       restart = TRUE;
1807                     }
1808                 }
1809               
1810               if (pr->vars)
1811                 {
1812                   hTabDeleteAll (pr->vars);
1813                   Safe_free (pr->vars);
1814                   pr->vars = NULL;
1815                 }
1816               
1817               freeTrace (&_G.values);
1818             }
1819         }
1820     } while (restart == TRUE);
1821
1822   if (labelHash)
1823     {
1824       hTabDeleteAll (labelHash);
1825       freeTrace (&_G.labels);
1826     }
1827   labelHash = NULL;
1828 }
1829
1830
1831 /*-----------------------------------------------------------------*/
1832 /* readFileIntoBuffer - reads a file into a string buffer          */
1833 /*-----------------------------------------------------------------*/
1834 static char *
1835 readFileIntoBuffer (char *fname)
1836 {
1837   FILE *f;
1838   char *rs = NULL;
1839   int nch = 0;
1840   int ch;
1841   char lb[MAX_PATTERN_LEN];
1842
1843   if (!(f = fopen (fname, "r")))
1844     {
1845       fprintf (stderr, "cannot open peep rule file\n");
1846       return NULL;
1847     }
1848
1849   while ((ch = fgetc (f)) != EOF)
1850     {
1851       lb[nch++] = ch;
1852
1853       /* if we maxed out our local buffer */
1854       if (nch >= (MAX_PATTERN_LEN - 2))
1855         {
1856           lb[nch] = '\0';
1857           /* copy it into allocated buffer */
1858           if (rs)
1859             {
1860               rs = Safe_realloc (rs, strlen (rs) + strlen (lb) + 1);
1861               strncatz (rs, lb,  strlen (rs) + strlen (lb) + 1);
1862             }
1863           else
1864             {
1865               rs = Safe_strdup (lb);
1866             }
1867           nch = 0;
1868         }
1869     }
1870
1871   /* if some charaters left over */
1872   if (nch)
1873     {
1874       lb[nch] = '\0';
1875       /* copy it into allocated buffer */
1876       if (rs)
1877         {
1878           rs = Safe_realloc (rs, strlen (rs) + strlen (lb) + 1);
1879           strncatz (rs, lb, strlen (rs) + strlen (lb) + 1);
1880         }
1881       else
1882         {
1883           rs = Safe_strdup (lb);
1884         }
1885     }
1886   return rs;
1887 }
1888
1889 /*-----------------------------------------------------------------*/
1890 /* initPeepHole - initialises the peep hole optimizer stuff        */
1891 /*-----------------------------------------------------------------*/
1892 void 
1893 initPeepHole ()
1894 {
1895   char *s;
1896
1897   /* read in the default rules */
1898   readRules (port->peep.default_rules);
1899
1900   /* if we have any additional file read it too */
1901   if (options.peep_file)
1902     {
1903       readRules (s = readFileIntoBuffer (options.peep_file));
1904       setToNull ((void *) &s);
1905     }
1906
1907
1908 #if !OPT_DISABLE_PIC
1909   /* Convert the peep rules into pcode.
1910      NOTE: this is only support in the PIC port (at the moment)
1911   */
1912         if (TARGET_IS_PIC)
1913                 peepRules2pCode(rootRules);
1914 #endif
1915
1916 #if !OPT_DISABLE_PIC16
1917   /* Convert the peep rules into pcode.
1918      NOTE: this is only support in the PIC port (at the moment)
1919        and the PIC16 port (VR 030601)
1920   */
1921         if (TARGET_IS_PIC16)
1922                 pic16_peepRules2pCode(rootRules);
1923
1924 #endif
1925
1926 }