* src/SDCCpeeph.h,
[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
949   lineNode *currL = NULL;
950   char *bp = *bpp;
951   while (1)
952     {
953
954       if (!*bp)
955         {
956           fprintf (stderr, "unexpected end of match pattern\n");
957           return;
958         }
959
960       if (*bp == '\n')
961         {
962           bp++;
963           while (isspace (*bp) ||
964                  *bp == '\n')
965             bp++;
966         }
967
968       if (*bp == '}')
969         {
970           bp++;
971           break;
972         }
973
974       /* read till end of line */
975       lp = lines;
976       while ((*bp != '\n' && *bp != '}') && *bp)
977         *lp++ = *bp++;
978
979       *lp = '\0';
980       if (!currL)
981         *head = currL = newLineNode (lines);
982       else
983         currL = connectLine (currL, newLineNode (lines));
984
985       lp = lines;
986       while (*lp && isspace(*lp))
987         lp++;
988       if (*lp==';')
989         currL->isComment = 1;
990     }
991
992   *bpp = bp;
993 }
994
995 /*-----------------------------------------------------------------*/
996 /* readRules - reads the rules from a string buffer                */
997 /*-----------------------------------------------------------------*/
998 static void 
999 readRules (char *bp)
1000 {
1001   char restart = 0;
1002   char lines[MAX_PATTERN_LEN];
1003   char *lp;
1004   lineNode *match;
1005   lineNode *replace;
1006   lineNode *currL = NULL;
1007
1008   if (!bp)
1009     return;
1010 top:
1011   restart = 0;
1012   /* look for the token "replace" that is the
1013      start of a rule */
1014   while (*bp && strncmp (bp, "replace", 7))
1015     bp++;
1016
1017   /* if not found */
1018   if (!*bp)
1019     return;
1020
1021   /* then look for either "restart" or '{' */
1022   while (strncmp (bp, "restart", 7) &&
1023          *bp != '{' && bp)
1024     bp++;
1025
1026   /* not found */
1027   if (!*bp)
1028     {
1029       fprintf (stderr, "expected 'restart' or '{'\n");
1030       return;
1031     }
1032
1033   /* if brace */
1034   if (*bp == '{')
1035     bp++;
1036   else
1037     {                           /* must be restart */
1038       restart++;
1039       bp += strlen ("restart");
1040       /* look for '{' */
1041       EXPECT_CHR (bp, '{', "expected '{'\n");
1042       bp++;
1043     }
1044
1045   /* skip thru all the blank space */
1046   SKIP_SPACE (bp, "unexpected end of rule\n");
1047
1048   match = replace = currL = NULL;
1049   /* we are the start of a rule */
1050   getPeepLine (&match, &bp);
1051
1052   /* now look for by */
1053   EXPECT_STR (bp, "by", "expected 'by'\n");
1054
1055   /* then look for a '{' */
1056   EXPECT_CHR (bp, '{', "expected '{'\n");
1057   bp++;
1058
1059   SKIP_SPACE (bp, "unexpected end of rule\n");
1060   getPeepLine (&replace, &bp);
1061
1062   /* look for a 'if' */
1063   while ((isspace (*bp) || *bp == '\n') && *bp)
1064     bp++;
1065
1066   if (strncmp (bp, "if", 2) == 0)
1067     {
1068       bp += 2;
1069       while ((isspace (*bp) || *bp == '\n') && *bp)
1070         bp++;
1071       if (!*bp)
1072         {
1073           fprintf (stderr, "expected condition name\n");
1074           return;
1075         }
1076
1077       /* look for the condition */
1078       lp = lines;
1079       while (*bp && (*bp != '\n'))
1080         {
1081           *lp++ = *bp++;
1082         }
1083       *lp = '\0';
1084
1085       newPeepRule (match, replace, lines, restart);
1086     }
1087   else
1088     newPeepRule (match, replace, NULL, restart);
1089   goto top;
1090
1091 }
1092
1093 /*-----------------------------------------------------------------*/
1094 /* keyForVar - returns the numeric key for a var                   */
1095 /*-----------------------------------------------------------------*/
1096 static int 
1097 keyForVar (char *d)
1098 {
1099   int i = 0;
1100
1101   while (isdigit (*d))
1102     {
1103       i *= 10;
1104       i += (*d++ - '0');
1105     }
1106
1107   return i;
1108 }
1109
1110 /*-----------------------------------------------------------------*/
1111 /* bindVar - binds a value to a variable in the given hashtable    */
1112 /*-----------------------------------------------------------------*/
1113 static void 
1114 bindVar (int key, char **s, hTab ** vtab)
1115 {
1116   char vval[MAX_PATTERN_LEN];
1117   char *vvx;
1118   char *vv = vval;
1119
1120   /* first get the value of the variable */
1121   vvx = *s;
1122   /* the value is ended by a ',' or space or newline or null or ) */
1123   while (*vvx &&
1124          *vvx != ',' &&
1125          !isspace (*vvx) &&
1126          *vvx != '\n' &&
1127          *vvx != ':' &&
1128          *vvx != ')')
1129     {
1130       char ubb = 0;
1131       /* if we find a '(' then we need to balance it */
1132       if (*vvx == '(')
1133         {
1134           ubb++;
1135           while (ubb)
1136             {
1137               *vv++ = *vvx++;
1138               if (*vvx == '(')
1139                 ubb++;
1140               if (*vvx == ')')
1141                 ubb--;
1142             }
1143           // include the trailing ')'
1144           *vv++ = *vvx++;
1145         }
1146       else
1147         *vv++ = *vvx++;
1148     }
1149   *s = vvx;
1150   *vv = '\0';
1151   /* got value */
1152   vvx = traceAlloc (&_G.values, Safe_strdup(vval));
1153
1154   hTabAddItem (vtab, key, vvx);
1155 }
1156
1157 /*-----------------------------------------------------------------*/
1158 /* matchLine - matches one line                                    */
1159 /*-----------------------------------------------------------------*/
1160 static bool 
1161 matchLine (char *s, char *d, hTab ** vars)
1162 {
1163
1164   if (!s || !(*s))
1165     return FALSE;
1166
1167   while (*s && *d)
1168     {
1169
1170       /* skip white space in both */
1171       while (isspace (*s))
1172         s++;
1173       while (isspace (*d))
1174         d++;
1175
1176       /* if the destination is a var */
1177       if (*d == '%' && isdigit (*(d + 1)) && vars)
1178         {
1179           char *v = hTabItemWithKey (*vars, keyForVar (d + 1));
1180           /* if the variable is already bound
1181              then it MUST match with dest */
1182           if (v)
1183             {
1184               while (*v)
1185                 if (*v++ != *s++)
1186                   return FALSE;
1187             }
1188           else
1189             /* variable not bound we need to
1190                bind it */
1191             bindVar (keyForVar (d + 1), &s, vars);
1192
1193           /* in either case go past the variable */
1194           d++;
1195           while (isdigit (*d))
1196             d++;
1197
1198           while (isspace (*s))
1199             s++;
1200           while (isspace (*d))
1201             d++;
1202         }
1203
1204       /* they should be an exact match other wise */
1205       if (*s && *d)
1206         {
1207           if (*s++ != *d++)
1208             return FALSE;
1209         }
1210
1211     }
1212
1213   /* get rid of the trailing spaces
1214      in both source & destination */
1215   if (*s)
1216     while (isspace (*s))
1217       s++;
1218
1219   if (*d)
1220     while (isspace (*d))
1221       d++;
1222
1223   /* after all this if only one of them
1224      has something left over then no match */
1225   if (*s || *d)
1226     return FALSE;
1227
1228   return TRUE;
1229 }
1230
1231 /*-----------------------------------------------------------------*/
1232 /* matchRule - matches a all the rule lines                        */
1233 /*-----------------------------------------------------------------*/
1234 static bool 
1235 matchRule (lineNode * pl,
1236            lineNode ** mtail,
1237            peepRule * pr,
1238            lineNode * head)
1239 {
1240   lineNode *spl;                /* source pl */
1241   lineNode *rpl;                /* rule peep line */
1242
1243 /*     setToNull((void *) &pr->vars);    */
1244 /*     pr->vars = newHashTable(100); */
1245
1246   /* for all the lines defined in the rule */
1247   rpl = pr->match;
1248   spl = pl;
1249   while (spl && rpl)
1250     {
1251
1252       /* if the source line starts with a ';' then
1253          comment line don't process or the source line
1254          contains == . debugger information skip it */
1255       if (spl->line &&
1256           (*spl->line == ';' || spl->isDebug))
1257         {
1258           spl = spl->next;
1259           continue;
1260         }
1261
1262       if (!matchLine (spl->line, rpl->line, &pr->vars))
1263         return FALSE;
1264
1265       rpl = rpl->next;
1266       if (rpl)
1267         spl = spl->next;
1268     }
1269
1270   /* if rules ended */
1271   if (!rpl)
1272     {
1273       /* if this rule has additional conditions */
1274       if (pr->cond)
1275         {
1276           if (callFuncByName (pr->cond, pr->vars, pl, spl, head))
1277             {
1278               *mtail = spl;
1279               return TRUE;
1280             }
1281           else
1282             return FALSE;
1283         }
1284       else
1285         {
1286           *mtail = spl;
1287           return TRUE;
1288         }
1289     }
1290   else
1291     return FALSE;
1292 }
1293
1294 static void
1295 reassociate_ic_down (lineNode *shead, lineNode *stail,
1296                      lineNode *rhead, lineNode *rtail)
1297 {
1298   lineNode *csl;        /* current source line */
1299   lineNode *crl;        /* current replacement line */
1300
1301   csl = shead;
1302   crl = rhead;
1303   while (1)
1304     {
1305       /* skip over any comments */
1306       while (csl!=stail->next && csl->isComment)
1307         csl = csl->next;
1308       while (crl!=rtail->next && crl->isComment)
1309         crl = crl->next;
1310
1311       /* quit if we reach the end */
1312       if ((csl==stail->next) || (crl==rtail->next) || crl->ic)
1313         break;
1314
1315       if (matchLine(csl->line,crl->line,NULL))
1316         {
1317           crl->ic = csl->ic;
1318           csl = csl->next;
1319           crl = crl->next;
1320         }
1321       else
1322         break;
1323     }
1324 }
1325
1326 static void
1327 reassociate_ic_up (lineNode *shead, lineNode *stail,
1328                    lineNode *rhead, lineNode *rtail)
1329 {
1330   lineNode *csl;        /* current source line */
1331   lineNode *crl;        /* current replacement line */
1332
1333   csl = stail;
1334   crl = rtail;
1335   while (1)
1336     {
1337       /* skip over any comments */
1338       while (csl!=shead->prev && csl->isComment)
1339         csl = csl->prev;
1340       while (crl!=rhead->prev && crl->isComment)
1341         crl = crl->prev;
1342
1343       /* quit if we reach the end */
1344       if ((csl==shead->prev) || (crl==rhead->prev) || crl->ic)
1345         break;
1346
1347       if (matchLine(csl->line,crl->line,NULL))
1348         {
1349           crl->ic = csl->ic;
1350           csl = csl->prev;
1351           crl = crl->prev;
1352         }
1353       else
1354         break;
1355     }
1356 }
1357
1358 /*------------------------------------------------------------------*/
1359 /* reassociate_ic - reassociate replacement lines with origin iCode */
1360 /*------------------------------------------------------------------*/
1361 static void
1362 reassociate_ic (lineNode *shead, lineNode *stail,
1363                 lineNode *rhead, lineNode *rtail)
1364 {
1365   lineNode *csl;        /* current source line */
1366   lineNode *crl;        /* current replacement line */
1367   bool single_iCode;
1368   iCode *ic;
1369   
1370   /* Check to see if all the source lines (excluding comments) came
1371   ** for the same iCode
1372   */
1373   ic = NULL;
1374   for (csl=shead;csl!=stail->next;csl=csl->next)
1375     if (csl->ic && !csl->isComment)
1376       {
1377         ic = csl->ic;
1378         break;
1379       }
1380   single_iCode = (ic!=NULL);
1381   for (csl=shead;csl!=stail->next;csl=csl->next)
1382     if ((csl->ic != ic) && !csl->isComment)
1383       {
1384         /* More than one iCode was found. However, if it's just the
1385         ** last line with the different iCode and it was not changed
1386         ** in the replacement, everything else must be the first iCode.
1387         */
1388         if ((csl==stail) && matchLine (stail->line, rtail->line, NULL))
1389           {
1390             rtail->ic = stail->ic;
1391             for (crl=rhead;crl!=rtail;crl=crl->next)
1392               crl->ic = ic;
1393             return;
1394           }
1395             
1396         single_iCode = FALSE;
1397         break;
1398       }
1399   
1400   /* If all of the source lines came from the same iCode, then so have
1401   ** all of the replacement lines too.
1402   */
1403   if (single_iCode)
1404     {
1405       for (crl=rhead;crl!=rtail->next;crl=crl->next)
1406         crl->ic = ic;
1407       return;
1408     }
1409   
1410   /* The source lines span iCodes, so we may end up with replacement
1411   ** lines that we don't know which iCode(s) to associate with. Do the
1412   ** best we can by using the following strategies:
1413   **    1) Start at the top and scan down. As long as the source line
1414   **       matches the replacement line, they have the same iCode.
1415   **    2) Start at the bottom and scan up. As long as the source line
1416   **       matches the replacement line, they have the same iCode.
1417   **    3) For any label in the source, look for a matching label in
1418   **       the replacment. If found, they have the same iCode. From
1419   **       these matching labels, scan down for additional matching
1420   **       lines; if found, they also have the same iCode.
1421   */
1422
1423   /* Strategy #1: Start at the top and scan down for matches
1424   */
1425   reassociate_ic_down(shead,stail,rhead,rtail);
1426   
1427   /* Strategy #2: Start at the bottom and scan up for matches
1428   */
1429   reassociate_ic_up(shead,stail,rhead,rtail);
1430
1431   /* Strategy #3: Try to match labels
1432   */
1433   csl = shead;
1434   while (1)
1435     {
1436       const char *labelStart;
1437       int labelLength;
1438       
1439       /* skip over any comments */
1440       while (csl!=stail->next && csl->isComment)
1441         csl = csl->next;
1442       if (csl==stail->next)
1443         break;
1444
1445       if (isLabelDefinition(csl->line, &labelStart, &labelLength))
1446         {
1447           /* found a source line label; look for it in the replacment lines */
1448           crl = rhead;
1449           while (1)
1450             {
1451               while (crl!=rtail->next && crl->isComment)
1452                 crl = crl->next;
1453               if (crl==rtail->next)
1454                 break;
1455               if (matchLine(csl->line, crl->line, NULL))
1456                 {
1457                   reassociate_ic_down(csl,stail,crl,rtail);
1458                   break;
1459                 }
1460               else
1461                 crl = crl->next;
1462             }
1463         }
1464       csl = csl->next;
1465     }
1466   
1467   /* Try to assign a meaningful iCode to any comment that is missing
1468      one. Since they are comments, it's ok to make mistakes; we are just
1469      trying to improve continuity to simplify other tests.
1470   */
1471   ic = NULL;
1472   for (crl=rtail;crl!=rhead->prev;crl=crl->prev)
1473     {
1474       if (!crl->ic && ic && crl->isComment)
1475         crl->ic = ic;
1476       ic = crl->ic;
1477     }
1478 }
1479
1480                   
1481 /*-----------------------------------------------------------------*/
1482 /* replaceRule - does replacement of a matching pattern            */
1483 /*-----------------------------------------------------------------*/
1484 static void 
1485 replaceRule (lineNode ** shead, lineNode * stail, peepRule * pr)
1486 {
1487   lineNode *cl = NULL;
1488   lineNode *pl = NULL, *lhead = NULL;
1489   /* a long function name and long variable name can evaluate to
1490      4x max pattern length e.g. "mov dptr,((fie_var>>8)<<8)+fie_var" */
1491   char lb[MAX_PATTERN_LEN*4];
1492   char *lbp;
1493   lineNode *comment = NULL;
1494
1495   /* collect all the comment lines in the source */
1496   for (cl = *shead; cl != stail; cl = cl->next)
1497     {
1498       if (cl->line && (*cl->line == ';' || cl->isDebug))
1499         {
1500           pl = (pl ? connectLine (pl, newLineNode (cl->line)) :
1501                 (comment = newLineNode (cl->line)));
1502           pl->isDebug = cl->isDebug;
1503           pl->isComment = cl->isComment || (*cl->line == ';');
1504         }
1505     }
1506   cl = NULL;
1507
1508   /* for all the lines in the replacement pattern do */
1509   for (pl = pr->replace; pl; pl = pl->next)
1510     {
1511       char *v;
1512       char *l;
1513       lbp = lb;
1514
1515       l = pl->line;
1516
1517       while (*l)
1518         {
1519           /* if the line contains a variable */
1520           if (*l == '%' && isdigit (*(l + 1)))
1521             {
1522               v = hTabItemWithKey (pr->vars, keyForVar (l + 1));
1523               if (!v)
1524                 {
1525                   fprintf (stderr, "used unbound variable in replacement\n");
1526                   l++;
1527                   continue;
1528                 }
1529               while (*v) {
1530                 *lbp++ = *v++;
1531               }
1532               l++;
1533               while (isdigit (*l)) {
1534                 l++;
1535               }
1536               continue;
1537             }
1538           *lbp++ = *l++;
1539         }
1540
1541       *lbp = '\0';
1542       if (cl)
1543         cl = connectLine (cl, newLineNode (lb));
1544       else
1545         lhead = cl = newLineNode (lb);
1546       cl->isComment = pl->isComment;
1547     }
1548
1549   /* add the comments if any to the head of list */
1550   if (comment)
1551     {
1552       lineNode *lc = comment;
1553       while (lc->next)
1554         lc = lc->next;
1555       lc->next = lhead;
1556       if (lhead)
1557         lhead->prev = lc;
1558       lhead = comment;
1559     }
1560
1561   /* determine which iCodes the replacment lines relate to */
1562   reassociate_ic(*shead,stail,lhead,cl);
1563
1564   /* now we need to connect / replace the original chain */
1565   /* if there is a prev then change it */
1566   if ((*shead)->prev)
1567     {
1568       (*shead)->prev->next = lhead;
1569       lhead->prev = (*shead)->prev;
1570     }
1571   *shead = lhead;
1572   /* now for the tail */
1573   if (stail && stail->next)
1574     {
1575       stail->next->prev = cl;
1576       if (cl)
1577         cl->next = stail->next;
1578     }
1579 }
1580
1581 /* Returns TRUE if this line is a label definition.
1582
1583  * If so, start will point to the start of the label name,
1584  * and len will be it's length.
1585  */
1586 bool 
1587 isLabelDefinition (const char *line, const char **start, int *len)
1588 {
1589   const char *cp = line;
1590
1591   /* This line is a label if if consists of:
1592    * [optional whitespace] followed by identifier chars
1593    * (alnum | $ | _ ) followed by a colon.
1594    */
1595
1596   while (*cp && isspace (*cp))
1597     {
1598       cp++;
1599     }
1600
1601   if (!*cp)
1602     {
1603       return FALSE;
1604     }
1605
1606   *start = cp;
1607
1608   while (isalnum (*cp) || (*cp == '$') || (*cp == '_'))
1609     {
1610       cp++;
1611     }
1612
1613   if ((cp == *start) || (*cp != ':'))
1614     {
1615       return FALSE;
1616     }
1617
1618   *len = (cp - (*start));
1619   return TRUE;
1620 }
1621
1622 /* Quick & dirty string hash function. */
1623 static int 
1624 hashSymbolName (const char *name)
1625 {
1626   int hash = 0;
1627
1628   while (*name)
1629     {
1630       hash = (hash << 6) ^ *name;
1631       name++;
1632     }
1633
1634   if (hash < 0)
1635     {
1636       hash = -hash;
1637     }
1638
1639   return hash % HTAB_SIZE;
1640 }
1641
1642 /* Build a hash of all labels in the passed set of lines
1643  * and how many times they are referenced.
1644  */
1645 static void 
1646 buildLabelRefCountHash (lineNode * head)
1647 {
1648   lineNode *line;
1649   const char *label;
1650   int labelLen;
1651   int i;
1652
1653   assert (labelHash == NULL);
1654   labelHash = newHashTable (HTAB_SIZE);
1655
1656   /* First pass: locate all the labels. */
1657   line = head;
1658
1659   while (line)
1660     {
1661       if (isLabelDefinition (line->line, &label, &labelLen)
1662           && labelLen <= SDCC_NAME_MAX)
1663         {
1664           labelHashEntry *entry;
1665
1666           entry = traceAlloc (&_G.labels, Safe_alloc(sizeof (labelHashEntry)));
1667
1668           memcpy (entry->name, label, labelLen);
1669           entry->name[labelLen] = 0;
1670           entry->refCount = -1;
1671
1672           hTabAddItem (&labelHash, hashSymbolName (entry->name), entry);
1673         }
1674       line = line->next;
1675     }
1676
1677
1678   /* Second pass: for each line, note all the referenced labels. */
1679   /* This is ugly, O(N^2) stuff. Optimizations welcome... */
1680   line = head;
1681   while (line)
1682     {
1683       for (i = 0; i < HTAB_SIZE; i++)
1684         {
1685           labelHashEntry *thisEntry;
1686
1687           thisEntry = hTabFirstItemWK (labelHash, i);
1688
1689           while (thisEntry)
1690             {
1691               if (strstr (line->line, thisEntry->name))
1692                 {
1693                   thisEntry->refCount++;
1694                 }
1695               thisEntry = hTabNextItemWK (labelHash);
1696             }
1697         }
1698       line = line->next;
1699     }
1700
1701 #if 0
1702   /* Spew the contents of the table. Debugging fun only. */
1703   for (i = 0; i < HTAB_SIZE; i++)
1704     {
1705       labelHashEntry *thisEntry;
1706
1707       thisEntry = hTabFirstItemWK (labelHash, i);
1708
1709       while (thisEntry)
1710         {
1711           fprintf (stderr, "label: %s ref %d\n",
1712                    thisEntry->name, thisEntry->refCount);
1713           thisEntry = hTabNextItemWK (labelHash);
1714         }
1715     }
1716 #endif
1717 }
1718
1719 /* How does this work?
1720    peepHole
1721     For each rule,
1722      For each line,
1723       Try to match
1724       If it matches,
1725        replace and restart.
1726
1727     matchRule
1728      matchLine
1729
1730   Where is stuff allocated?
1731   
1732 */
1733
1734 /*-----------------------------------------------------------------*/
1735 /* peepHole - matches & substitutes rules                          */
1736 /*-----------------------------------------------------------------*/
1737 void 
1738 peepHole (lineNode ** pls)
1739 {
1740   lineNode *spl;
1741   peepRule *pr;
1742   lineNode *mtail = NULL;
1743   bool restart;
1744
1745 #if !OPT_DISABLE_PIC || !OPT_DISABLE_PIC16
1746   /* The PIC port uses a different peep hole optimizer based on "pCode" */
1747   if (TARGET_IS_PIC || TARGET_IS_PIC16)
1748     return;
1749 #endif
1750
1751   assert(labelHash == NULL);
1752
1753   do
1754     {
1755       restart = FALSE;
1756
1757       /* for all rules */
1758       for (pr = rootRules; pr; pr = pr->next)
1759         {
1760           for (spl = *pls; spl; spl = spl->next)
1761             {
1762               /* if inline assembler then no peep hole */
1763               if (spl->isInline)
1764                 continue;
1765
1766               /* don't waste time starting a match on debug symbol
1767               ** or comment */
1768               if (spl->isDebug || spl->isComment || *(spl->line)==';')
1769                 continue;
1770               
1771               mtail = NULL;
1772
1773               /* Tidy up any data stored in the hTab */
1774               
1775               /* if it matches */
1776               if (matchRule (spl, &mtail, pr, *pls))
1777                 {
1778                   
1779                   /* then replace */
1780                   if (spl == *pls)
1781                     replaceRule (pls, mtail, pr);
1782                   else
1783                     replaceRule (&spl, mtail, pr);
1784                   
1785                   /* if restart rule type then
1786                      start at the top again */
1787                   if (pr->restart)
1788                     {
1789                       restart = TRUE;
1790                     }
1791                 }
1792               
1793               if (pr->vars)
1794                 {
1795                   hTabDeleteAll (pr->vars);
1796                   Safe_free (pr->vars);
1797                   pr->vars = NULL;
1798                 }
1799               
1800               freeTrace (&_G.values);
1801             }
1802         }
1803     } while (restart == TRUE);
1804
1805   if (labelHash)
1806     {
1807       hTabDeleteAll (labelHash);
1808       freeTrace (&_G.labels);
1809     }
1810   labelHash = NULL;
1811 }
1812
1813
1814 /*-----------------------------------------------------------------*/
1815 /* readFileIntoBuffer - reads a file into a string buffer          */
1816 /*-----------------------------------------------------------------*/
1817 static char *
1818 readFileIntoBuffer (char *fname)
1819 {
1820   FILE *f;
1821   char *rs = NULL;
1822   int nch = 0;
1823   int ch;
1824   char lb[MAX_PATTERN_LEN];
1825
1826   if (!(f = fopen (fname, "r")))
1827     {
1828       fprintf (stderr, "cannot open peep rule file\n");
1829       return NULL;
1830     }
1831
1832   while ((ch = fgetc (f)) != EOF)
1833     {
1834       lb[nch++] = ch;
1835
1836       /* if we maxed out our local buffer */
1837       if (nch >= (MAX_PATTERN_LEN - 2))
1838         {
1839           lb[nch] = '\0';
1840           /* copy it into allocated buffer */
1841           if (rs)
1842             {
1843               rs = Safe_realloc (rs, strlen (rs) + strlen (lb) + 1);
1844               strncatz (rs, lb,  strlen (rs) + strlen (lb) + 1);
1845             }
1846           else
1847             {
1848               rs = Safe_strdup (lb);
1849             }
1850           nch = 0;
1851         }
1852     }
1853
1854   /* if some charaters left over */
1855   if (nch)
1856     {
1857       lb[nch] = '\0';
1858       /* copy it into allocated buffer */
1859       if (rs)
1860         {
1861           rs = Safe_realloc (rs, strlen (rs) + strlen (lb) + 1);
1862           strncatz (rs, lb, strlen (rs) + strlen (lb) + 1);
1863         }
1864       else
1865         {
1866           rs = Safe_strdup (lb);
1867         }
1868     }
1869   return rs;
1870 }
1871
1872 /*-----------------------------------------------------------------*/
1873 /* initPeepHole - initialises the peep hole optimizer stuff        */
1874 /*-----------------------------------------------------------------*/
1875 void 
1876 initPeepHole ()
1877 {
1878   char *s;
1879
1880   /* read in the default rules */
1881   readRules (port->peep.default_rules);
1882
1883   /* if we have any additional file read it too */
1884   if (options.peep_file)
1885     {
1886       readRules (s = readFileIntoBuffer (options.peep_file));
1887       setToNull ((void *) &s);
1888     }
1889
1890
1891 #if !OPT_DISABLE_PIC
1892   /* Convert the peep rules into pcode.
1893      NOTE: this is only support in the PIC port (at the moment)
1894   */
1895         if (TARGET_IS_PIC)
1896                 peepRules2pCode(rootRules);
1897 #endif
1898
1899 #if !OPT_DISABLE_PIC16
1900   /* Convert the peep rules into pcode.
1901      NOTE: this is only support in the PIC port (at the moment)
1902        and the PIC16 port (VR 030601)
1903   */
1904         if (TARGET_IS_PIC16)
1905                 pic16_peepRules2pCode(rootRules);
1906
1907 #endif
1908
1909 }