Z80 peephole improvements - Implemented RFE #1919415, #1861376, #1880202, #1914434...
[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 #include "dbuf_string.h"
28
29 #define ISCHARDIGIT(c) isdigit((unsigned char)c)
30 #define ISCHARSPACE(c) isspace((unsigned char)c)
31 #define ISCHARALNUM(c) isalnum((unsigned char)c)
32
33 static peepRule *rootRules = NULL;
34 static peepRule *currRule = NULL;
35
36 #define HTAB_SIZE 53
37
38 hTab *labelHash = NULL;
39
40 static struct
41 {
42   allocTrace values;
43   allocTrace labels;
44 } _G;
45
46 static int hashSymbolName (const char *name);
47 static void buildLabelRefCountHash (lineNode * head);
48 static void bindVar (int key, char **s, hTab ** vtab);
49
50 static bool matchLine (char *, char *, hTab **);
51
52 #define FBYNAME(x) static int x (hTab *vars, lineNode *currPl, lineNode *endPl, \
53         lineNode *head, char *cmdLine)
54
55 #if !OPT_DISABLE_PIC
56 void peepRules2pCode(peepRule *);
57 #endif
58
59 #if !OPT_DISABLE_PIC16
60 void pic16_peepRules2pCode(peepRule *);
61 #endif
62
63 /*-----------------------------------------------------------------*/
64 /* pcDistance - finds a label backward or forward                  */
65 /*-----------------------------------------------------------------*/
66
67 static int
68 pcDistance (lineNode * cpos, char *lbl, bool back)
69 {
70   lineNode *pl = cpos;
71   char buff[MAX_PATTERN_LEN];
72   int dist = 0;
73
74   SNPRINTF (buff, sizeof(buff), "%s:", lbl);
75   while (pl)
76     {
77       if (pl->line &&
78           !pl->isComment &&
79           !pl->isLabel &&
80           !pl->isDebug)
81         {
82           if (port->peep.getSize)
83             {
84               dist += port->peep.getSize(pl);
85             }
86           else
87             {
88               dist += 3;
89             }
90         }
91
92       if (strncmp (pl->line, buff, strlen (buff)) == 0)
93         return dist;
94
95       if (back)
96         pl = pl->prev;
97       else
98         pl = pl->next;
99
100     }
101   return 0;
102 }
103
104 /*-----------------------------------------------------------------*/
105 /* portIsDS390 - return true if port is DS390                      */
106 /*-----------------------------------------------------------------*/
107 FBYNAME (portIsDS390)
108 {
109     return ((strcmp(port->target,"ds390") == 0) ||
110             (strcmp(port->target,"ds400") == 0));
111 }
112
113 /*-----------------------------------------------------------------*/
114 /* flat24bitMode - will check to see if we are in flat24 mode      */
115 /*-----------------------------------------------------------------*/
116 FBYNAME (flat24bitMode)
117 {
118   return (options.model == MODEL_FLAT24);
119 }
120
121 /*-----------------------------------------------------------------*/
122 /* xramMovcOption - check if using movc to read xram               */
123 /*-----------------------------------------------------------------*/
124 FBYNAME (xramMovcOption)
125 {
126   return (options.xram_movc && (strcmp(port->target,"mcs51") == 0));
127 }
128
129 /*-----------------------------------------------------------------*/
130 /* useAcallAjmp - Enable replacement of lcall/ljmp with acall/ajmp */
131 /*-----------------------------------------------------------------*/
132 FBYNAME (useAcallAjmp)
133 {
134   return (options.acall_ajmp && (strcmp(port->target,"mcs51") == 0));
135 }
136
137 /*-----------------------------------------------------------------*/
138 /* labelInRange - will check to see if label %5 is within range    */
139 /*-----------------------------------------------------------------*/
140 FBYNAME (labelInRange)
141 {
142   /* assumes that %5 pattern variable has the label name */
143   char *lbl = hTabItemWithKey (vars, 5);
144   int dist = 0;
145
146   if (!lbl)
147     return FALSE;
148
149   /* Don't optimize jumps in a jump table; a more generic test */
150   if (currPl->ic && currPl->ic->op == JUMPTABLE)
151     return FALSE;
152
153   /* if the previous two instructions are "ljmp"s then don't
154      do it since it can be part of a jump table */
155   if (currPl->prev && currPl->prev->prev &&
156       strstr (currPl->prev->line, "ljmp") &&
157       strstr (currPl->prev->prev->line, "ljmp"))
158     return FALSE;
159
160   /* calculate the label distance : the jump for reladdr can be
161      +/- 127 bytes, here I am assuming that an average 8051
162      instruction is 2 bytes long, so if the label is more than
163      63 intructions away, the label is considered out of range
164      for a relative jump. we could get more precise this will
165      suffice for now since it catches > 90% cases */
166   dist = (pcDistance (currPl, lbl, TRUE) +
167           pcDistance (currPl, lbl, FALSE));
168
169 /*    changed to 127, now that pcDistance return actual number of bytes */
170   if (!dist || dist > 127)
171     return FALSE;
172
173   return TRUE;
174 }
175
176 /*-----------------------------------------------------------------*/
177 /* labelJTInRange - will check to see if label %5 and up are       */
178 /* within range.                                                   */
179 /* Specifically meant to optimize long (3-byte) jumps to short     */
180 /* (2-byte) jumps in jumptables                                    */
181 /*-----------------------------------------------------------------*/
182 FBYNAME (labelJTInRange)
183 {
184   char *lbl;
185   int dist, count, i;
186
187   if (!getenv("SDCC_SJMP_JUMPTABLE"))
188     return FALSE;
189
190   /* Only optimize within a jump table */
191   if (currPl->ic && currPl->ic->op != JUMPTABLE)
192     return FALSE;
193
194   count = elementsInSet( IC_JTLABELS (currPl->ic) );
195
196   /* check all labels (this is needed if the case statements are unsorted) */
197   for (i=0; i<count; i++)
198     {
199       /* assumes that the %5 pattern variable has the first ljmp label */
200       lbl = hTabItemWithKey (vars, 5+i);
201       if (!lbl)
202         return FALSE;
203
204       dist = pcDistance (currPl, lbl, FALSE);
205
206       /* three terms used to calculate allowable distance */
207 // printf("\nlabel %s %i dist %i cdist 0x%02x 0x%02x\n", lbl, i, dist, dist -(count-i-1)-(7+3*i), 127+(count-i-1)+(7+3*i) - dist);
208       if (!dist ||
209           dist > 127+           /* range of sjmp */
210                  (7+3*i)+       /* offset between this jump and currPl,
211                                    should use pcDistance instead? */
212                  (count-i-1)    /* if peephole applies distance is shortened */
213          )
214         return FALSE;
215     }
216   return TRUE;
217 }
218
219 /*-----------------------------------------------------------------*/
220 /* labelIsReturnOnly - Check if label %5 is followed by RET        */
221 /*-----------------------------------------------------------------*/
222 FBYNAME (labelIsReturnOnly)
223 {
224   /* assumes that %5 pattern variable has the label name */
225   const char *label, *p;
226   const lineNode *pl;
227   int len;
228   char * retInst;
229
230   /* Don't optimize jumps in a jump table; a more generic test */
231   if (currPl->ic && currPl->ic->op == JUMPTABLE)
232     return FALSE;
233
234   label = hTabItemWithKey (vars, 5);
235   if (!label)
236     return FALSE;
237   len = strlen(label);
238
239   for(pl = currPl; pl; pl = pl->next)
240     {
241       if (pl->line && !pl->isDebug && !pl->isComment && pl->isLabel)
242         {
243           if (strncmp(pl->line, label, len) == 0)
244             break; /* Found Label */
245           if (strlen(pl->line) != 7       || !ISCHARDIGIT(*(pl->line))   ||
246               !ISCHARDIGIT(*(pl->line+1)) || !ISCHARDIGIT(*(pl->line+2)) ||
247               !ISCHARDIGIT(*(pl->line+3)) || !ISCHARDIGIT(*(pl->line+4)) ||
248               *(pl->line+5) != '$')
249             {
250               return FALSE; /* non-local label encountered */
251             }
252         }
253     }
254   if (!pl)
255     return FALSE; /* did not find the label */
256   pl = pl->next;
257   while (pl && (pl->isDebug || pl->isComment))
258     pl = pl->next;
259   if (!pl || !pl->line || pl->isDebug)
260     return FALSE; /* next line not valid */
261   p = pl->line;
262   for (p = pl->line; *p && ISCHARSPACE(*p); p++)
263     ;
264
265   retInst = "ret";
266   if (TARGET_IS_HC08)
267     retInst = "rts";
268   if (strcmp(p, retInst) == 0)
269     return TRUE;
270   return FALSE;
271 }
272
273 /*-----------------------------------------------------------------*/
274 /* labelIsUncondJump - Check if label %5 is followed by an         */
275 /* unconditional jump and put the destination of that jump in %6   */
276 /*-----------------------------------------------------------------*/
277 FBYNAME (labelIsUncondJump)
278 {
279   /* assumes that %5 pattern variable has the label name */
280   const char *label;
281   char *p, *q;
282   const lineNode *pl;
283   int len;
284   char * jpInst = NULL;
285
286   /* Don't optimize jumps in a jump table; a more generic test */
287   if (currPl->ic && currPl->ic->op == JUMPTABLE)
288     return FALSE;
289
290   label = hTabItemWithKey (vars, 5);
291   if (!label)
292     return FALSE;
293   len = strlen(label);
294
295   for (pl = currPl; pl; pl = pl->next)
296     {
297       if (pl->line && !pl->isDebug && !pl->isComment && pl->isLabel)
298         {
299           if (strncmp(pl->line, label, len) == 0)
300             break; /* Found Label */
301           if (strlen(pl->line) != 7       || !ISCHARDIGIT(*(pl->line))   ||
302               !ISCHARDIGIT(*(pl->line+1)) || !ISCHARDIGIT(*(pl->line+2)) ||
303               !ISCHARDIGIT(*(pl->line+3)) || !ISCHARDIGIT(*(pl->line+4)) ||
304               *(pl->line+5) != '$')
305             {
306               return FALSE; /* non-local label encountered */
307             }
308         }
309     }
310   if (!pl)
311     return FALSE; /* did not find the label */
312   pl = pl->next;
313   while (pl && (pl->isDebug || pl->isComment))
314     pl = pl->next;
315   if (!pl || !pl->line)
316     return FALSE; /* next line not valid */
317   p = pl->line;
318   while (*p && ISCHARSPACE(*p))
319     p++;
320
321   if (TARGET_MCS51_LIKE)
322     jpInst = "ljmp";
323   if (TARGET_IS_HC08)
324     jpInst = "jmp";
325   if (TARGET_Z80_LIKE)
326     jpInst = "jp";
327   len = strlen(jpInst);
328   if (strncmp(p, jpInst, len) != 0)
329     return FALSE; /* next line is no jump */
330   p += len;
331   while (*p && ISCHARSPACE(*p))
332     p++;
333
334   q = p;
335   while (*q && *q!=';')
336     q++;
337   while (q>p && ISCHARSPACE(*q))
338     q--;
339   len = q-p;
340   if (len == 0)
341     return FALSE; /* no destination? */
342   if (TARGET_Z80_LIKE)
343     {
344       while (q>p && *q!=',')
345         q--;
346       if (*q==',')
347         return FALSE; /* conditional jump */
348     }
349   if (strcmp(p, q) == 0)
350     return FALSE; /* labels are equal */
351   /* now put the destination in %6 */
352   bindVar (6, &p, &vars);
353   return TRUE;
354 }
355
356 /*-----------------------------------------------------------------*/
357 /* okToRemoveSLOC - Check if label %1 is a SLOC and not other      */
358 /* usage of it in the code depends on a value from this section    */
359 /*-----------------------------------------------------------------*/
360 FBYNAME (okToRemoveSLOC)
361 {
362   const lineNode *pl;
363   const char *sloc, *p;
364   int dummy1, dummy2, dummy3;
365
366   /* assumes that %1 as the SLOC name */
367   sloc = hTabItemWithKey (vars, 1);
368   if (sloc == NULL) return FALSE;
369   p = strstr(sloc, "sloc");
370   if (p == NULL) return FALSE;
371   p += 4;
372   if (sscanf(p, "%d_%d_%d", &dummy1, &dummy2, &dummy3) != 3) return FALSE;
373   /*TODO: ultra-paranoid: get funtion name from "head" and check that */
374   /* the sloc name begins with that.  Probably not really necessary */
375
376   /* Look for any occurance of this SLOC before the peephole match */
377   for (pl = currPl->prev; pl; pl = pl->prev) {
378         if (pl->line && !pl->isDebug && !pl->isComment
379           && *pl->line != ';' && strstr(pl->line, sloc))
380                 return FALSE;
381   }
382   /* Look for any occurance of this SLOC after the peephole match */
383   for (pl = endPl->next; pl; pl = pl->next) {
384         if (pl->line && !pl->isDebug && !pl->isComment
385           && *pl->line != ';' && strstr(pl->line, sloc))
386                 return FALSE;
387   }
388   return TRUE; /* safe for a peephole to remove it :) */
389 }
390
391 /*-----------------------------------------------------------------*/
392 /* deadMove - Check, if a pop/push pair can be removed             */
393 /*-----------------------------------------------------------------*/
394 FBYNAME (deadMove)
395 {
396   const char *reg = hTabItemWithKey (vars, 1);
397
398   if (port->peep.deadMove)
399     return port->peep.deadMove (reg, currPl, head);
400
401   fprintf (stderr, "Function deadMove not initialized in port structure\n");
402   return FALSE;
403 }
404
405 /*-----------------------------------------------------------------*/
406 /* notUsed - Check, if value in register is not read again         */
407 /*-----------------------------------------------------------------*/
408 FBYNAME (notUsed)
409 {
410   const char *what;
411
412   if(cmdLine[0] != '\'')
413     what = hTabItemWithKey (vars, 1);
414   else
415   {
416     cmdLine[strlen(cmdLine) - 1] = 0;
417     what = cmdLine + 1;
418   }
419
420   if (port->peep.notUsed)
421     return port->peep.notUsed (what, endPl, head);
422
423   fprintf (stderr, "Function notUsed not initialized in port structure\n");
424   return FALSE;
425 }
426
427 /*-----------------------------------------------------------------*/
428 /* operandsNotSame - check if %1 & %2 are the same                 */
429 /*-----------------------------------------------------------------*/
430 FBYNAME (operandsNotSame)
431 {
432   char *op1 = hTabItemWithKey (vars, 1);
433   char *op2 = hTabItemWithKey (vars, 2);
434
435   if (strcmp (op1, op2) == 0)
436     return FALSE;
437   else
438     return TRUE;
439 }
440
441 /*-----------------------------------------------------------------*/
442 /* operandsNotSame3- check if any pair of %1,%2,%3 are the same    */
443 /*-----------------------------------------------------------------*/
444 FBYNAME (operandsNotSame3)
445 {
446   char *op1 = hTabItemWithKey (vars, 1);
447   char *op2 = hTabItemWithKey (vars, 2);
448   char *op3 = hTabItemWithKey (vars, 3);
449
450   if ( (strcmp (op1, op2) == 0) ||
451        (strcmp (op1, op3) == 0) ||
452        (strcmp (op2, op3) == 0) )
453     return FALSE;
454   else
455     return TRUE;
456 }
457
458 /*-----------------------------------------------------------------*/
459 /* operandsNotSame4- check if any pair of %1,%2,%3,.. are the same */
460 /*-----------------------------------------------------------------*/
461 FBYNAME (operandsNotSame4)
462 {
463   char *op1 = hTabItemWithKey (vars, 1);
464   char *op2 = hTabItemWithKey (vars, 2);
465   char *op3 = hTabItemWithKey (vars, 3);
466   char *op4 = hTabItemWithKey (vars, 4);
467
468   if ( (strcmp (op1, op2) == 0) ||
469        (strcmp (op1, op3) == 0) ||
470        (strcmp (op1, op4) == 0) ||
471        (strcmp (op2, op3) == 0) ||
472        (strcmp (op2, op4) == 0) ||
473        (strcmp (op3, op4) == 0) )
474     return FALSE;
475   else
476     return TRUE;
477 }
478
479 /*-----------------------------------------------------------------*/
480 /* operandsNotSame5- check if any pair of %1,%2,%3,.. are the same */
481 /*-----------------------------------------------------------------*/
482 FBYNAME (operandsNotSame5)
483 {
484   char *op1 = hTabItemWithKey (vars, 1);
485   char *op2 = hTabItemWithKey (vars, 2);
486   char *op3 = hTabItemWithKey (vars, 3);
487   char *op4 = hTabItemWithKey (vars, 4);
488   char *op5 = hTabItemWithKey (vars, 5);
489
490   if ( (strcmp (op1, op2) == 0) ||
491        (strcmp (op1, op3) == 0) ||
492        (strcmp (op1, op4) == 0) ||
493        (strcmp (op1, op5) == 0) ||
494        (strcmp (op2, op3) == 0) ||
495        (strcmp (op2, op4) == 0) ||
496        (strcmp (op2, op5) == 0) ||
497        (strcmp (op3, op4) == 0) ||
498        (strcmp (op3, op5) == 0) ||
499        (strcmp (op4, op5) == 0) )
500     return FALSE;
501   else
502     return TRUE;
503 }
504
505 /*-----------------------------------------------------------------*/
506 /* operandsNotSame6- check if any pair of %1,%2,%3,.. are the same */
507 /*-----------------------------------------------------------------*/
508 FBYNAME (operandsNotSame6)
509 {
510   char *op1 = hTabItemWithKey (vars, 1);
511   char *op2 = hTabItemWithKey (vars, 2);
512   char *op3 = hTabItemWithKey (vars, 3);
513   char *op4 = hTabItemWithKey (vars, 4);
514   char *op5 = hTabItemWithKey (vars, 5);
515   char *op6 = hTabItemWithKey (vars, 6);
516
517   if ( (strcmp (op1, op2) == 0) ||
518        (strcmp (op1, op3) == 0) ||
519        (strcmp (op1, op4) == 0) ||
520        (strcmp (op1, op5) == 0) ||
521        (strcmp (op1, op6) == 0) ||
522        (strcmp (op2, op3) == 0) ||
523        (strcmp (op2, op4) == 0) ||
524        (strcmp (op2, op5) == 0) ||
525        (strcmp (op2, op6) == 0) ||
526        (strcmp (op3, op4) == 0) ||
527        (strcmp (op3, op5) == 0) ||
528        (strcmp (op3, op6) == 0) ||
529        (strcmp (op4, op5) == 0) ||
530        (strcmp (op4, op6) == 0) ||
531        (strcmp (op5, op6) == 0) )
532     return FALSE;
533   else
534     return TRUE;
535 }
536
537 /*-----------------------------------------------------------------*/
538 /* operandsNotSame7- check if any pair of %1,%2,%3,.. are the same */
539 /*-----------------------------------------------------------------*/
540 FBYNAME (operandsNotSame7)
541 {
542   char *op1 = hTabItemWithKey (vars, 1);
543   char *op2 = hTabItemWithKey (vars, 2);
544   char *op3 = hTabItemWithKey (vars, 3);
545   char *op4 = hTabItemWithKey (vars, 4);
546   char *op5 = hTabItemWithKey (vars, 5);
547   char *op6 = hTabItemWithKey (vars, 6);
548   char *op7 = hTabItemWithKey (vars, 7);
549
550   if ( (strcmp (op1, op2) == 0) ||
551        (strcmp (op1, op3) == 0) ||
552        (strcmp (op1, op4) == 0) ||
553        (strcmp (op1, op5) == 0) ||
554        (strcmp (op1, op6) == 0) ||
555        (strcmp (op1, op7) == 0) ||
556        (strcmp (op2, op3) == 0) ||
557        (strcmp (op2, op4) == 0) ||
558        (strcmp (op2, op5) == 0) ||
559        (strcmp (op2, op6) == 0) ||
560        (strcmp (op2, op7) == 0) ||
561        (strcmp (op3, op4) == 0) ||
562        (strcmp (op3, op5) == 0) ||
563        (strcmp (op3, op6) == 0) ||
564        (strcmp (op3, op7) == 0) ||
565        (strcmp (op4, op5) == 0) ||
566        (strcmp (op4, op6) == 0) ||
567        (strcmp (op4, op7) == 0) ||
568        (strcmp (op5, op6) == 0) ||
569        (strcmp (op5, op7) == 0) ||
570        (strcmp (op6, op7) == 0) )
571     return FALSE;
572   else
573     return TRUE;
574 }
575
576 /*-----------------------------------------------------------------*/
577 /* operandsNotSame8- check if any pair of %1,%2,%3,.. are the same */
578 /*-----------------------------------------------------------------*/
579 FBYNAME (operandsNotSame8)
580 {
581   char *op1 = hTabItemWithKey (vars, 1);
582   char *op2 = hTabItemWithKey (vars, 2);
583   char *op3 = hTabItemWithKey (vars, 3);
584   char *op4 = hTabItemWithKey (vars, 4);
585   char *op5 = hTabItemWithKey (vars, 5);
586   char *op6 = hTabItemWithKey (vars, 6);
587   char *op7 = hTabItemWithKey (vars, 7);
588   char *op8 = hTabItemWithKey (vars, 8);
589
590   if ( (strcmp (op1, op2) == 0) ||
591        (strcmp (op1, op3) == 0) ||
592        (strcmp (op1, op4) == 0) ||
593        (strcmp (op1, op5) == 0) ||
594        (strcmp (op1, op6) == 0) ||
595        (strcmp (op1, op7) == 0) ||
596        (strcmp (op1, op8) == 0) ||
597        (strcmp (op2, op3) == 0) ||
598        (strcmp (op2, op4) == 0) ||
599        (strcmp (op2, op5) == 0) ||
600        (strcmp (op2, op6) == 0) ||
601        (strcmp (op2, op7) == 0) ||
602        (strcmp (op2, op8) == 0) ||
603        (strcmp (op3, op4) == 0) ||
604        (strcmp (op3, op5) == 0) ||
605        (strcmp (op3, op6) == 0) ||
606        (strcmp (op3, op7) == 0) ||
607        (strcmp (op3, op8) == 0) ||
608        (strcmp (op4, op5) == 0) ||
609        (strcmp (op4, op6) == 0) ||
610        (strcmp (op4, op7) == 0) ||
611        (strcmp (op4, op8) == 0) ||
612        (strcmp (op5, op6) == 0) ||
613        (strcmp (op5, op7) == 0) ||
614        (strcmp (op5, op8) == 0) ||
615        (strcmp (op6, op7) == 0) ||
616        (strcmp (op6, op8) == 0) ||
617        (strcmp (op7, op8) == 0) )
618     return FALSE;
619   else
620     return TRUE;
621 }
622
623 /*-----------------------------------------------------------------*/
624 /* labelHashEntry- searches for a label in the list labelHash      */
625 /* Builds labelHash, if it does not yet exist.                     */
626 /* Returns the labelHashEntry or NULL                              */
627 /*-----------------------------------------------------------------*/
628 labelHashEntry *
629 getLabelRef (const char *label, lineNode *head)
630 {
631   labelHashEntry *entry;
632
633   /* If we don't have the label hash table yet, build it. */
634   if (!labelHash)
635     {
636       buildLabelRefCountHash (head);
637     }
638
639   entry = hTabFirstItemWK (labelHash, hashSymbolName (label));
640
641   while (entry)
642     {
643       if (!strcmp (label, entry->name))
644         {
645           break;
646         }
647       entry = hTabNextItemWK (labelHash);
648     }
649   return entry;
650 }
651
652 /* labelRefCount:
653
654  * takes two parameters: a variable (bound to a label name)
655  * and an expected reference count.
656  *
657  * Returns TRUE if that label is defined and referenced exactly
658  * the given number of times.
659  */
660 FBYNAME (labelRefCount)
661 {
662   int varNumber, expectedRefCount;
663   bool rc = FALSE;
664
665   if (sscanf (cmdLine, "%*[ \t%]%d %d", &varNumber, &expectedRefCount) == 2)
666     {
667       char *label = hTabItemWithKey (vars, varNumber);
668
669       if (label)
670         {
671           labelHashEntry *entry = getLabelRef (label, head);
672
673           if (entry)
674             {
675 #if 0
676               /* debug spew. */
677               fprintf (stderr, "labelRefCount: %s has refCount %d, want %d\n",
678                        label, entry->refCount, expectedRefCount);
679 #endif
680
681               rc = (expectedRefCount == entry->refCount);
682             }
683           else
684             {
685               fprintf (stderr, "*** internal error: no label has entry for"
686                        " %s in labelRefCount peephole.\n",
687                        label);
688             }
689         }
690       else
691         {
692           fprintf (stderr, "*** internal error: var %d not bound"
693                    " in peephole labelRefCount rule.\n",
694                    varNumber);
695         }
696
697     }
698   else
699     {
700       fprintf (stderr,
701                "*** internal error: labelRefCount peephole restriction"
702                " malformed: %s\n", cmdLine);
703     }
704   return rc;
705 }
706
707 /* labelRefCountChange:
708  * takes two parameters: a variable (bound to a label name)
709  * and a signed int for changing the reference count.
710  *
711  * Please note, this function is not a conditional. It unconditionally
712  * changes the label. It should be passed as the 'last' function
713  * so it only is applied if all other conditions have been met.
714  *
715  * should always return TRUE
716  */
717 FBYNAME (labelRefCountChange)
718 {
719   int varNumber, RefCountDelta;
720   bool rc = FALSE;
721
722   /* If we don't have the label hash table yet, build it. */
723   if (!labelHash)
724     {
725       buildLabelRefCountHash (head);
726     }
727
728   if (sscanf (cmdLine, "%*[ \t%]%d %i", &varNumber, &RefCountDelta) == 2)
729     {
730       char *label = hTabItemWithKey (vars, varNumber);
731
732       if (label)
733         {
734           labelHashEntry *entry;
735
736           entry = hTabFirstItemWK (labelHash, hashSymbolName (label));
737
738           while (entry)
739             {
740               if (!strcmp (label, entry->name))
741                 {
742                   break;
743                 }
744               entry = hTabNextItemWK (labelHash);
745             }
746           if (entry)
747             {
748               if (0 <= entry->refCount + RefCountDelta)
749                 {
750                   entry->refCount += RefCountDelta;
751                   rc = TRUE;
752                 }
753               else
754                 {
755                   fprintf (stderr, "*** internal error: label %s may not get"
756                           " negative refCount in %s peephole.\n",
757                            label, __FUNCTION__);
758                 }
759             }
760             else
761             {
762               fprintf (stderr, "*** internal error: no label has entry for"
763                        " %s in %s peephole.\n",
764                        label, __FUNCTION__);
765             }
766         }
767       else
768         {
769           fprintf (stderr, "*** internal error: var %d not bound"
770                    " in peephole %s rule.\n",
771                    varNumber, __FUNCTION__);
772         }
773     }
774   else
775     {
776       fprintf (stderr,
777                "*** internal error: labelRefCount peephole restriction"
778                " malformed: %s\n", cmdLine);
779     }
780   return rc;
781 }
782
783 /* Within the context of the lines currPl through endPl, determine
784 ** if the variable var contains a symbol that is volatile. Returns
785 ** TRUE only if it is certain that this was not volatile (the symbol
786 ** was found and not volatile, or var was a constant or CPU register).
787 ** Returns FALSE if the symbol was found and volatile, the symbol was
788 ** not found, or var was a indirect/pointer addressing mode.
789 */
790 static bool
791 notVolatileVariable(char *var, lineNode *currPl, lineNode *endPl)
792 {
793   char symname[SDCC_NAME_MAX + 1];
794   char *p = symname;
795   char *vp = var;
796   lineNode *cl;
797   operand *op;
798   iCode *last_ic;
799
800   /* Can't tell if indirect accesses are volatile or not, so
801   ** assume they are, just to be safe.
802   */
803   if (TARGET_IS_MCS51 || TARGET_IS_DS390 || TARGET_IS_DS400)
804     {
805       if (*var=='@')
806         return FALSE;
807     }
808   if (TARGET_IS_Z80 || TARGET_IS_GBZ80)
809     {
810       if (strstr(var,"(bc)"))
811         return FALSE;
812       if (strstr(var,"(de)"))
813         return FALSE;
814       if (strstr(var,"(hl)"))
815         return FALSE;
816       if (strstr(var,"(ix"))
817         return FALSE;
818       if (strstr(var,"(iy"))
819         return FALSE;
820     }
821
822   /* Extract a symbol name from the variable */
823   while (*vp && (*vp!='_'))
824     vp++;
825   while (*vp && (ISCHARALNUM(*vp) || *vp=='_'))
826     *p++ = *vp++;
827   *p='\0';
828
829   if (!symname[0])
830     {
831       /* Nothing resembling a symbol name was found, so it can't
832          be volatile
833       */
834       return TRUE;
835     }
836
837   last_ic = NULL;
838   for (cl = currPl; cl!=endPl->next; cl = cl->next)
839   {
840     if (cl->ic && (cl->ic!=last_ic))
841       {
842         last_ic = cl->ic;
843         switch (cl->ic->op)
844           {
845           case IFX:
846             op = IC_COND (cl->ic);
847             if (IS_SYMOP (op) &&
848                 ( !strcmp(OP_SYMBOL (op)->rname, symname) ||
849                   (OP_SYMBOL (op)->isspilt &&
850                    SPIL_LOC (op) &&
851                    !strcmp(SPIL_LOC (op)->rname, symname)) ))
852               {
853                 return !op->isvolatile;
854               }
855           case JUMPTABLE:
856             op = IC_JTCOND (cl->ic);
857             if (IS_SYMOP (op) &&
858                 ( !strcmp(OP_SYMBOL (op)->rname, symname) ||
859                   (OP_SYMBOL (op)->isspilt &&
860                    SPIL_LOC (op) &&
861                    !strcmp(SPIL_LOC (op)->rname, symname)) ))
862               {
863                 return !op->isvolatile;
864               }
865           default:
866             op = IC_LEFT (cl->ic);
867             if (IS_SYMOP (op) &&
868                 ( !strcmp(OP_SYMBOL (op)->rname, symname) ||
869                   (OP_SYMBOL (op)->isspilt &&
870                    SPIL_LOC (op) &&
871                    !strcmp(SPIL_LOC (op)->rname, symname)) ))
872               {
873                 return !op->isvolatile;
874               }
875             op = IC_RIGHT (cl->ic);
876             if (IS_SYMOP (op) &&
877                 ( !strcmp(OP_SYMBOL (op)->rname, symname) ||
878                   (OP_SYMBOL (op)->isspilt &&
879                    SPIL_LOC (op) &&
880                    !strcmp(SPIL_LOC (op)->rname, symname)) ))
881               {
882                 return !op->isvolatile;
883               }
884             op = IC_RESULT (cl->ic);
885             if (IS_SYMOP (op) &&
886                 ( !strcmp(OP_SYMBOL (op)->rname, symname) ||
887                   (OP_SYMBOL (op)->isspilt &&
888                    SPIL_LOC (op) &&
889                    !strcmp(SPIL_LOC (op)->rname, symname)) ))
890               {
891                 return !op->isvolatile;
892               }
893           }
894       }
895   }
896
897   /* Couldn't find the symbol for some reason. Assume volatile. */
898   return FALSE;
899 }
900
901 /*  notVolatile:
902  *
903  *  This rule restriction has two different behaviours depending on
904  *  the number of parameters given.
905  *
906  *    if notVolatile                 (no parameters given)
907  *       The rule is applied only if none of the iCodes originating
908  *       the matched pattern reference a volatile operand.
909  *
910  *    if notVolatile %1 ...          (one or more parameters given)
911  *       The rule is applied if the parameters are not expressions
912  *       containing volatile symbols and are not pointer accesses.
913  *
914  */
915 FBYNAME (notVolatile)
916 {
917   int varNumber;
918   char *var;
919   bool notvol;
920   char *digitend;
921   lineNode *cl;
922   operand *op;
923
924   if (!cmdLine)
925     {
926       /* If no parameters given, just scan the iCodes for volatile operands */
927       for (cl = currPl; cl!=endPl->next; cl = cl->next)
928       {
929         if (cl->ic)
930           {
931             switch (cl->ic->op)
932               {
933               case IFX:
934                 op = IC_COND (cl->ic);
935                 if (IS_SYMOP (op) && op->isvolatile)
936                   return FALSE;
937               case JUMPTABLE:
938                 op = IC_JTCOND (cl->ic);
939                 if (IS_SYMOP (op) && op->isvolatile)
940                   return FALSE;
941               default:
942                 op = IC_LEFT (cl->ic);
943                 if (IS_SYMOP (op) && op->isvolatile)
944                   return FALSE;
945                 op = IC_RIGHT (cl->ic);
946                 if (IS_SYMOP (op) && op->isvolatile)
947                   return FALSE;
948                 op = IC_RESULT (cl->ic);
949                 if (IS_SYMOP (op) && op->isvolatile)
950                   return FALSE;
951               }
952           }
953       }
954       return TRUE;
955     }
956
957   /* There were parameters; check the volatility of each */
958   while (*cmdLine && ISCHARSPACE(*cmdLine))
959     cmdLine++;
960   while (*cmdLine)
961     {
962       if (*cmdLine!='%')
963         goto error;
964       cmdLine++;
965       if (!ISCHARDIGIT(*cmdLine))
966         goto error;
967       varNumber = strtol(cmdLine, &digitend, 10);
968       cmdLine = digitend;
969       while (*cmdLine && ISCHARSPACE(*cmdLine))
970         cmdLine++;
971
972       var = hTabItemWithKey (vars, varNumber);
973
974       if (var)
975         {
976           notvol = notVolatileVariable (var, currPl, endPl);
977           if (!notvol)
978             return FALSE;
979         }
980       else
981         {
982           fprintf (stderr, "*** internal error: var %d not bound"
983                    " in peephole notVolatile rule.\n",
984                    varNumber);
985           return FALSE;
986         }
987     }
988
989   return TRUE;
990
991 error:
992   fprintf (stderr,
993            "*** internal error: notVolatile peephole restriction"
994            " malformed: %s\n", cmdLine);
995   return FALSE;
996 }
997
998 /*------------------------------------------------------------------*/
999 /* setFromConditionArgs - parse a peephole condition's arguments    */
1000 /* to produce a set of strings, one per argument. Variables %x will */
1001 /* be replaced with their values. String literals (in single quotes)*/
1002 /* are accepted and return in unquoted form.                         */
1003 /*------------------------------------------------------------------*/
1004 static set *
1005 setFromConditionArgs (char *cmdLine, hTab * vars)
1006 {
1007   int varNumber;
1008   char *var;
1009   char *digitend;
1010   set *operands = NULL;
1011
1012   if (!cmdLine)
1013     return NULL;
1014
1015   while (*cmdLine && ISCHARSPACE(*cmdLine))
1016     cmdLine++;
1017
1018   while (*cmdLine)
1019     {
1020       if (*cmdLine == '%')
1021         {
1022           cmdLine++;
1023           if (!ISCHARDIGIT(*cmdLine))
1024             goto error;
1025           varNumber = strtol(cmdLine, &digitend, 10);
1026           cmdLine = digitend;
1027
1028           var = hTabItemWithKey (vars, varNumber);
1029
1030           if (var)
1031             {
1032               addSetHead (&operands, var);
1033             }
1034           else
1035             goto error;
1036         }
1037       else if (*cmdLine == '\'' )
1038         {
1039           char quote = *cmdLine;
1040
1041           var = ++cmdLine;
1042           while (*cmdLine && *cmdLine != quote)
1043             cmdLine++;
1044           if (*cmdLine == quote)
1045             *cmdLine++ = '\0';
1046           else
1047             goto error;
1048           addSetHead (&operands, var);
1049         }
1050       else
1051         goto error;
1052
1053       while (*cmdLine && ISCHARSPACE(*cmdLine))
1054         cmdLine++;
1055     }
1056
1057   return operands;
1058
1059 error:
1060   deleteSet (&operands);
1061   return NULL;
1062 }
1063
1064 static const char *
1065 operandBaseName (const char *op)
1066 {
1067   if (TARGET_IS_MCS51 || TARGET_IS_DS390 || TARGET_IS_DS400)
1068     {
1069       if (!strcmp (op, "acc") || !strncmp (op, "acc.", 4))
1070         return "a";
1071       if (!strncmp (op, "ar", 2) && ISCHARDIGIT(*(op+2)) && !*(op+3))
1072         return op+1;
1073       // bug 1739475, temp fix
1074       if (op[0] == '@')
1075         return operandBaseName(op+1);
1076     }
1077
1078   return op;
1079 }
1080
1081 /*-------------------------------------------------------------------*/
1082 /* operandsNotRelated - returns true if the condition's operands are */
1083 /* not related (taking into account register name aliases). N-way    */
1084 /* comparison performed between all operands.                        */
1085 /*-------------------------------------------------------------------*/
1086 FBYNAME (operandsNotRelated)
1087 {
1088   set *operands;
1089   const char *op1, *op2;
1090
1091   operands = setFromConditionArgs (cmdLine, vars);
1092
1093   if (!operands)
1094     {
1095       fprintf (stderr,
1096                "*** internal error: operandsNotRelated peephole restriction"
1097                " malformed: %s\n", cmdLine);
1098       return FALSE;
1099     }
1100
1101   while ((op1 = setFirstItem (operands)))
1102     {
1103       deleteSetItem (&operands, (void*)op1);
1104       op1 = operandBaseName (op1);
1105
1106       for (op2 = setFirstItem (operands); op2; op2 = setNextItem (operands))
1107         {
1108           op2 = operandBaseName (op2);
1109           if (strcmp (op1, op2) == 0)
1110             {
1111               deleteSet (&operands);
1112               return FALSE;
1113             }
1114         }
1115     }
1116
1117   deleteSet (&operands);
1118   return TRUE;
1119 }
1120
1121 /*-------------------------------------------------------------------*/
1122 /* operandsLiteral - returns true of the condition's operands are    */
1123 /* literals.                                                         */
1124 /*-------------------------------------------------------------------*/
1125 FBYNAME (operandsLiteral)
1126 {
1127   set *operands;
1128   const char *op;
1129
1130   operands = setFromConditionArgs (cmdLine, vars);
1131
1132   if (!operands)
1133     {
1134       fprintf (stderr,
1135                "*** internal error: operandsLiteral peephole restriction"
1136                " malformed: %s\n", cmdLine);
1137       return FALSE;
1138     }
1139
1140   for (op = setFirstItem (operands); op; op = setNextItem (operands))
1141     {
1142       if (!isdigit(*op))
1143         {
1144           deleteSet (&operands);
1145           return FALSE;
1146         }
1147     }
1148
1149   deleteSet (&operands);
1150   return TRUE;
1151 }
1152
1153 static const struct ftab
1154 {
1155   char *fname;
1156   int (*func) (hTab *, lineNode *, lineNode *, lineNode *, char *);
1157 }
1158 ftab[] =                                // sorted on the number of times used
1159 {                                       // in the peephole rules on 2007-10-29
1160   {
1161     "labelRefCount", labelRefCount                  //105
1162   },
1163   {
1164     "notVolatile", notVolatile                      //85
1165   },
1166   {
1167     "labelRefCountChange", labelRefCountChange      //74
1168   },
1169   {
1170     "labelInRange", labelInRange                    //37
1171   },
1172   {
1173     "labelJTInRange", labelJTInRange                //13
1174   },
1175   {
1176     "operandsNotRelated", operandsNotRelated        //9
1177   },
1178   {
1179     "24bitMode", flat24bitMode                      //9
1180   },
1181   {
1182     "operandsNotSame", operandsNotSame              //8
1183   },
1184   {
1185     "operandsNotSame3", operandsNotSame3
1186   },
1187   {
1188     "operandsNotSame4", operandsNotSame4
1189   },
1190   {
1191     "operandsNotSame5", operandsNotSame5
1192   },
1193   {
1194     "operandsNotSame6", operandsNotSame6
1195   },
1196   {
1197     "operandsNotSame7", operandsNotSame7
1198   },
1199   {
1200     "operandsNotSame8", operandsNotSame8
1201   },
1202   {
1203     "xramMovcOption", xramMovcOption
1204   },
1205   {
1206     "portIsDS390", portIsDS390
1207   },
1208   {
1209     "labelIsReturnOnly", labelIsReturnOnly
1210   },
1211   {
1212     "labelIsUncondJump", labelIsUncondJump
1213   },
1214   {
1215     "okToRemoveSLOC", okToRemoveSLOC
1216   },
1217   {
1218     "deadMove", deadMove
1219   },
1220   {
1221     "operandsLiteral", operandsLiteral
1222   },
1223   {
1224     "useAcallAjmp", useAcallAjmp
1225   },
1226   {
1227     "notUsed", notUsed
1228   }
1229 };
1230 /*-----------------------------------------------------------------*/
1231 /* callFuncByName - calls a function as defined in the table       */
1232 /*-----------------------------------------------------------------*/
1233 static int
1234 callFuncByName (char *fname,
1235                 hTab * vars,
1236                 lineNode * currPl, /* first source line matched */
1237                 lineNode * endPl,  /* last source line matched */
1238                 lineNode * head)
1239 {
1240   int   i;
1241   char  *cmdCopy, *funcName, *funcArgs, *cmdTerm;
1242   char  c;
1243   int   rc;
1244
1245   /* Isolate the function name part (we are passed the full condition
1246    * string including arguments)
1247    */
1248   cmdTerm = cmdCopy = Safe_strdup(fname);
1249
1250   do
1251     {
1252       funcArgs = funcName = cmdTerm;
1253       while ((c = *funcArgs) && c != ' ' && c != '\t' && c != '(')
1254         funcArgs++;
1255       *funcArgs = '\0';  /* terminate the function name */
1256       if (c)
1257         funcArgs++;
1258
1259       /* Find the start of the arguments */
1260       if (c == ' ' || c == '\t')
1261         while ((c = *funcArgs) && (c == ' ' || c == '\t'))
1262           funcArgs++;
1263
1264       /* If the arguments started with an opening parenthesis,  */
1265       /* use the closing parenthesis for the end of the         */
1266       /* arguments and look for the start of another condition  */
1267       /* that can optionally follow. If there was no opening    */
1268       /* parethesis, then everything that follows are arguments */
1269       /* and there can be no additional conditions.             */
1270       if (c == '(')
1271         {
1272
1273           int num_parenthesis = 0;
1274           cmdTerm = funcArgs;          
1275
1276           while ((c = *cmdTerm) && (c != ')' || num_parenthesis))
1277             {
1278               if (c == '(')
1279                 num_parenthesis++;
1280               else if (c == ')')
1281                 num_parenthesis--;
1282               cmdTerm++;
1283             }
1284           *cmdTerm = '\0';  /* terminate the arguments */
1285           if (c == ')')
1286             {
1287               cmdTerm++;
1288               while ((c = *cmdTerm) && (c == ' ' || c == '\t' || c == ','))
1289                 cmdTerm++;
1290               if (!*cmdTerm)
1291                 cmdTerm = NULL;
1292             }
1293           else
1294             cmdTerm = NULL; /* closing parenthesis missing */
1295         }
1296       else
1297         cmdTerm = NULL;
1298
1299       if (!*funcArgs)
1300         funcArgs = NULL;
1301
1302       rc = -1;
1303       for (i = 0; i < ((sizeof (ftab)) / (sizeof (struct ftab))); i++)
1304         {
1305           if (strcmp (ftab[i].fname, funcName) == 0)
1306             {
1307               rc = (*ftab[i].func) (vars, currPl, endPl, head, funcArgs);
1308               break;
1309             }
1310         }
1311
1312       if (rc == -1)
1313         {
1314           fprintf (stderr,
1315                    "could not find named function \"%s\" in "
1316                    "peephole function table\n",
1317                    funcName);
1318           // If the function couldn't be found, let's assume it's
1319           // a bad rule and refuse it.
1320           rc = FALSE;
1321           break;
1322         }
1323     }
1324   while (rc && cmdTerm);
1325
1326   Safe_free(cmdCopy);
1327
1328   return rc;
1329 }
1330
1331 /*-----------------------------------------------------------------*/
1332 /* printLine - prints a line chain into a given file               */
1333 /*-----------------------------------------------------------------*/
1334 void
1335 printLine (lineNode * head, struct dbuf_s * oBuf)
1336 {
1337   iCode *last_ic = NULL;
1338   bool debug_iCode_tracking = (getenv("DEBUG_ICODE_TRACKING")!=NULL);
1339
1340   while (head)
1341     {
1342       if (head->ic!=last_ic)
1343         {
1344           last_ic = head->ic;
1345           if (debug_iCode_tracking)
1346             {
1347               if (head->ic)
1348                 dbuf_printf (oBuf, "; block = %d, seq = %d\n",
1349                          head->ic->block, head->ic->seq);
1350               else
1351                 dbuf_append_str (oBuf, "; iCode lost\n");
1352             }
1353         }
1354
1355       /* don't indent comments & labels */
1356       if (head->line &&
1357           (head->isComment || head->isLabel)) {
1358         dbuf_printf (oBuf, "%s\n", head->line);
1359       } else {
1360         if (head->isInline && *head->line=='#') {
1361           // comment out preprocessor directives in inline asm
1362           dbuf_append_char (oBuf, ';');
1363         }
1364         dbuf_printf (oBuf, "\t%s\n", head->line);
1365       }
1366       head = head->next;
1367     }
1368 }
1369
1370 /*-----------------------------------------------------------------*/
1371 /* newPeepRule - creates a new peeprule and attach it to the root  */
1372 /*-----------------------------------------------------------------*/
1373 static peepRule *
1374 newPeepRule (lineNode * match,
1375              lineNode * replace,
1376              char *cond,
1377              int restart)
1378 {
1379   peepRule *pr;
1380
1381   pr = Safe_alloc ( sizeof (peepRule));
1382   pr->match = match;
1383   pr->replace = replace;
1384   pr->restart = restart;
1385
1386   if (cond && *cond)
1387     {
1388       pr->cond = Safe_strdup (cond);
1389     }
1390   else
1391     pr->cond = NULL;
1392
1393   pr->vars = newHashTable (100);
1394
1395   /* if root is empty */
1396   if (!rootRules)
1397     rootRules = currRule = pr;
1398   else
1399     currRule = currRule->next = pr;
1400
1401   return pr;
1402 }
1403
1404 /*-----------------------------------------------------------------*/
1405 /* newLineNode - creates a new peep line                           */
1406 /*-----------------------------------------------------------------*/
1407 lineNode *
1408 newLineNode (const char *line)
1409 {
1410   lineNode *pl;
1411
1412   pl = Safe_alloc ( sizeof (lineNode));
1413   pl->line = Safe_strdup (line);
1414   pl->ic = NULL;
1415   return pl;
1416 }
1417
1418 /*-----------------------------------------------------------------*/
1419 /* connectLine - connects two lines                                */
1420 /*-----------------------------------------------------------------*/
1421 lineNode *
1422 connectLine (lineNode * pl1, lineNode * pl2)
1423 {
1424   if (!pl1 || !pl2)
1425     {
1426       fprintf (stderr, "trying to connect null line\n");
1427       return NULL;
1428     }
1429
1430   pl2->prev = pl1;
1431   pl1->next = pl2;
1432
1433   return pl2;
1434 }
1435
1436 #define SKIP_SPACE(x,y) { while (*x && (ISCHARSPACE(*x) || *x == '\n')) x++; \
1437                          if (!*x) { fprintf(stderr,y); return ; } }
1438
1439 #define EXPECT_STR(x,y,z) { while (*x && strncmp(x,y,strlen(y))) x++ ;   \
1440                            if (!*x) { fprintf(stderr,z); return ; } }
1441 #define EXPECT_CHR(x,y,z) { while (*x && *x != y) x++ ;   \
1442                            if (!*x) { fprintf(stderr,z); return ; } }
1443
1444 /*-----------------------------------------------------------------*/
1445 /* getPeepLine - parses the peep lines                             */
1446 /*-----------------------------------------------------------------*/
1447 static void
1448 getPeepLine (lineNode ** head, char **bpp)
1449 {
1450   char lines[MAX_PATTERN_LEN];
1451   char *lp;
1452   int isComment;
1453
1454   lineNode *currL = NULL;
1455   char *bp = *bpp;
1456   while (1)
1457     {
1458
1459       if (!*bp)
1460         {
1461           fprintf (stderr, "unexpected end of match pattern\n");
1462           return;
1463         }
1464
1465       if (*bp == '\n')
1466         {
1467           bp++;
1468           while (ISCHARSPACE (*bp) ||
1469                  *bp == '\n')
1470             bp++;
1471         }
1472
1473       if (*bp == '}')
1474         {
1475           bp++;
1476           break;
1477         }
1478
1479       /* read till end of line */
1480       lp = lines;
1481       while ((*bp != '\n' && *bp != '}') && *bp)
1482         *lp++ = *bp++;
1483       *lp = '\0';
1484
1485       lp = lines;
1486       while (*lp && ISCHARSPACE(*lp))
1487         lp++;
1488       isComment = (*lp == ';');
1489
1490       if (!isComment || (isComment && !options.noPeepComments))
1491         {
1492           const char *dummy1;
1493           int dummy2;
1494
1495           if (!currL)
1496             *head = currL = newLineNode (lines);
1497           else
1498             currL = connectLine (currL, newLineNode (lines));
1499           currL->isComment = isComment;
1500           currL->isLabel = isLabelDefinition (currL->line, &dummy1, &dummy2,
1501                                               TRUE);
1502         }
1503
1504     }
1505
1506   *bpp = bp;
1507 }
1508
1509 /*-----------------------------------------------------------------*/
1510 /* readRules - reads the rules from a string buffer                */
1511 /*-----------------------------------------------------------------*/
1512 static void
1513 readRules (char *bp)
1514 {
1515   char restart = 0;
1516   char lines[MAX_PATTERN_LEN];
1517   char *lp, *rp;
1518   lineNode *match;
1519   lineNode *replace;
1520   lineNode *currL = NULL;
1521
1522   if (!bp)
1523     return;
1524 top:
1525   restart = 0;
1526   /* look for the token "replace" that is the
1527      start of a rule */
1528   while (*bp && strncmp (bp, "replace", 7))
1529     bp++;
1530
1531   /* if not found */
1532   if (!*bp)
1533     return;
1534
1535   /* then look for either "restart" or '{' */
1536   while (strncmp (bp, "restart", 7) &&
1537          *bp != '{' && bp)
1538     bp++;
1539
1540   /* not found */
1541   if (!*bp)
1542     {
1543       fprintf (stderr, "expected 'restart' or '{'\n");
1544       return;
1545     }
1546
1547   /* if brace */
1548   if (*bp == '{')
1549     bp++;
1550   else
1551     {                           /* must be restart */
1552       restart++;
1553       bp += strlen ("restart");
1554       /* look for '{' */
1555       EXPECT_CHR (bp, '{', "expected '{'\n");
1556       bp++;
1557     }
1558
1559   /* skip thru all the blank space */
1560   SKIP_SPACE (bp, "unexpected end of rule\n");
1561
1562   match = replace = currL = NULL;
1563   /* we are the start of a rule */
1564   getPeepLine (&match, &bp);
1565
1566   /* now look for by */
1567   EXPECT_STR (bp, "by", "expected 'by'\n");
1568
1569   /* then look for a '{' */
1570   EXPECT_CHR (bp, '{', "expected '{'\n");
1571   bp++;
1572
1573   /* save char position (needed for generating error msg) */
1574   rp = bp;
1575
1576   SKIP_SPACE (bp, "unexpected end of rule\n");
1577   getPeepLine (&replace, &bp);
1578
1579   /* look for a 'if' */
1580   while ((ISCHARSPACE (*bp) || *bp == '\n') && *bp)
1581     bp++;
1582
1583   if (strncmp (bp, "if", 2) == 0)
1584     {
1585       bp += 2;
1586       while ((ISCHARSPACE (*bp) || *bp == '\n') && *bp)
1587         bp++;
1588       if (!*bp)
1589         {
1590           fprintf (stderr, "expected condition name\n");
1591           return;
1592         }
1593
1594       /* look for the condition */
1595       lp = lines;
1596       while (*bp && (*bp != '\n'))
1597         {
1598           *lp++ = *bp++;
1599         }
1600       *lp = '\0';
1601
1602       newPeepRule (match, replace, lines, restart);
1603     }
1604   else
1605     {
1606       if (*bp && strncmp (bp, "replace", 7))
1607         {
1608           /* not the start of a new peeprule, so "if" should be here */
1609
1610           char strbuff[1000];
1611           char *cp;
1612
1613           /* go to the start of the line following "{" of the "by" token */
1614           while (*rp && (*rp == '\n'))
1615             rp++;
1616
1617           /* copy text of rule starting with line after "by {" */
1618           cp = strbuff;
1619           while (*rp && (rp < bp) && ((cp - strbuff) < sizeof(strbuff)))
1620               *cp++ = *rp++;
1621
1622           /* and now the rest of the line */
1623           while (*rp && (*rp != '\n') && ((cp - strbuff) < sizeof(strbuff)))
1624             *cp++ = *rp++;
1625
1626           *cp = '\0';
1627           fprintf (stderr, "%s\nexpected '} if ...'\n", strbuff);
1628           return;
1629         }
1630       newPeepRule (match, replace, NULL, restart);
1631     }
1632   goto top;
1633
1634 }
1635
1636 /*-----------------------------------------------------------------*/
1637 /* keyForVar - returns the numeric key for a var                   */
1638 /*-----------------------------------------------------------------*/
1639 static int
1640 keyForVar (char *d)
1641 {
1642   int i = 0;
1643
1644   while (ISCHARDIGIT (*d))
1645     {
1646       i *= 10;
1647       i += (*d++ - '0');
1648     }
1649
1650   return i;
1651 }
1652
1653 /*-----------------------------------------------------------------*/
1654 /* bindVar - binds a value to a variable in the given hashtable    */
1655 /*-----------------------------------------------------------------*/
1656 static void
1657 bindVar (int key, char **s, hTab ** vtab)
1658 {
1659   char vval[MAX_PATTERN_LEN];
1660   char *vvx;
1661   char *vv = vval;
1662
1663   /* first get the value of the variable */
1664   vvx = *s;
1665   /* the value is ended by a ',' or space or newline or null or ) */
1666   while (*vvx &&
1667          *vvx != ',' &&
1668          !ISCHARSPACE (*vvx) &&
1669          *vvx != '\n' &&
1670          *vvx != ':' &&
1671          *vvx != ')')
1672     {
1673       char ubb = 0;
1674       /* if we find a '(' then we need to balance it */
1675       if (*vvx == '(')
1676         {
1677           ubb++;
1678           while (ubb)
1679             {
1680               *vv++ = *vvx++;
1681               if (*vvx == '(')
1682                 ubb++;
1683               if (*vvx == ')')
1684                 ubb--;
1685             }
1686           // include the trailing ')'
1687           *vv++ = *vvx++;
1688         }
1689       else
1690         *vv++ = *vvx++;
1691     }
1692   *s = vvx;
1693   *vv = '\0';
1694   /* got value */
1695   vvx = traceAlloc (&_G.values, Safe_strdup(vval));
1696
1697   hTabAddItem (vtab, key, vvx);
1698 }
1699
1700 /*-----------------------------------------------------------------*/
1701 /* matchLine - matches one line                                    */
1702 /*-----------------------------------------------------------------*/
1703 static bool
1704 matchLine (char *s, char *d, hTab ** vars)
1705 {
1706
1707   if (!s || !(*s))
1708     return FALSE;
1709
1710   while (*s && *d)
1711     {
1712
1713       /* skip white space in both */
1714       while (ISCHARSPACE (*s))
1715         s++;
1716       while (ISCHARSPACE (*d))
1717         d++;
1718
1719       /* if the destination is a var */
1720       if (*d == '%' && ISCHARDIGIT (*(d + 1)) && vars)
1721         {
1722           char *v = hTabItemWithKey (*vars, keyForVar (d + 1));
1723           /* if the variable is already bound
1724              then it MUST match with dest */
1725           if (v)
1726             {
1727               while (*v)
1728                 if (*v++ != *s++)
1729                   return FALSE;
1730             }
1731           else
1732             /* variable not bound we need to bind it */
1733             bindVar (keyForVar (d + 1), &s, vars);
1734
1735           /* in either case go past the variable */
1736           d++;
1737           while (ISCHARDIGIT (*d))
1738             d++;
1739
1740           while (ISCHARSPACE (*s))
1741             s++;
1742           while (ISCHARSPACE (*d))
1743             d++;
1744         }
1745
1746       /* they should be an exact match other wise */
1747       if (*s && *d)
1748         {
1749           if (*s++ != *d++)
1750             return FALSE;
1751         }
1752
1753     }
1754
1755   /* get rid of the trailing spaces
1756      in both source & destination */
1757   if (*s)
1758     while (ISCHARSPACE (*s))
1759       s++;
1760
1761   if (*d)
1762     while (ISCHARSPACE (*d))
1763       d++;
1764
1765   /* after all this if only one of them
1766      has something left over then no match */
1767   if (*s || *d)
1768     return FALSE;
1769
1770   return TRUE;
1771 }
1772
1773 /*-----------------------------------------------------------------*/
1774 /* matchRule - matches a all the rule lines                        */
1775 /*-----------------------------------------------------------------*/
1776 static bool
1777 matchRule (lineNode * pl,
1778            lineNode ** mtail,
1779            peepRule * pr,
1780            lineNode * head)
1781 {
1782   lineNode *spl;                /* source pl */
1783   lineNode *rpl;                /* rule peep line */
1784
1785 /*     setToNull((void *) &pr->vars);    */
1786 /*     pr->vars = newHashTable(100); */
1787
1788   /* for all the lines defined in the rule */
1789   rpl = pr->match;
1790   spl = pl;
1791   while (spl && rpl)
1792     {
1793
1794       /* if the source line starts with a ';' then
1795          comment line don't process or the source line
1796          contains == . debugger information skip it */
1797       if (spl->line &&
1798           (*spl->line == ';' || spl->isDebug))
1799         {
1800           spl = spl->next;
1801           continue;
1802         }
1803
1804       if (!matchLine (spl->line, rpl->line, &pr->vars))
1805         return FALSE;
1806
1807       rpl = rpl->next;
1808       if (rpl)
1809         spl = spl->next;
1810     }
1811
1812   /* if rules ended */
1813   if (!rpl)
1814     {
1815       /* if this rule has additional conditions */
1816       if (pr->cond)
1817         {
1818           if (callFuncByName (pr->cond, pr->vars, pl, spl, head))
1819             {
1820               *mtail = spl;
1821               return TRUE;
1822             }
1823           else
1824             return FALSE;
1825         }
1826       else
1827         {
1828           *mtail = spl;
1829           return TRUE;
1830         }
1831     }
1832   else
1833     return FALSE;
1834 }
1835
1836 static void
1837 reassociate_ic_down (lineNode *shead, lineNode *stail,
1838                      lineNode *rhead, lineNode *rtail)
1839 {
1840   lineNode *csl;        /* current source line */
1841   lineNode *crl;        /* current replacement line */
1842
1843   csl = shead;
1844   crl = rhead;
1845   while (1)
1846     {
1847       /* skip over any comments */
1848       while (csl!=stail->next && csl->isComment)
1849         csl = csl->next;
1850       while (crl!=rtail->next && crl->isComment)
1851         crl = crl->next;
1852
1853       /* quit if we reach the end */
1854       if ((csl==stail->next) || (crl==rtail->next) || crl->ic)
1855         break;
1856
1857       if (matchLine(csl->line,crl->line,NULL))
1858         {
1859           crl->ic = csl->ic;
1860           csl = csl->next;
1861           crl = crl->next;
1862         }
1863       else
1864         break;
1865     }
1866 }
1867
1868 static void
1869 reassociate_ic_up (lineNode *shead, lineNode *stail,
1870                    lineNode *rhead, lineNode *rtail)
1871 {
1872   lineNode *csl;        /* current source line */
1873   lineNode *crl;        /* current replacement line */
1874
1875   csl = stail;
1876   crl = rtail;
1877   while (1)
1878     {
1879       /* skip over any comments */
1880       while (csl!=shead->prev && csl->isComment)
1881         csl = csl->prev;
1882       while (crl!=rhead->prev && crl->isComment)
1883         crl = crl->prev;
1884
1885       /* quit if we reach the end */
1886       if ((csl==shead->prev) || (crl==rhead->prev) || crl->ic)
1887         break;
1888
1889       if (matchLine(csl->line,crl->line,NULL))
1890         {
1891           crl->ic = csl->ic;
1892           csl = csl->prev;
1893           crl = crl->prev;
1894         }
1895       else
1896         break;
1897     }
1898 }
1899
1900 /*------------------------------------------------------------------*/
1901 /* reassociate_ic - reassociate replacement lines with origin iCode */
1902 /*------------------------------------------------------------------*/
1903 static void
1904 reassociate_ic (lineNode *shead, lineNode *stail,
1905                 lineNode *rhead, lineNode *rtail)
1906 {
1907   lineNode *csl;        /* current source line */
1908   lineNode *crl;        /* current replacement line */
1909   bool single_iCode;
1910   iCode *ic;
1911
1912   /* Check to see if all the source lines (excluding comments) came
1913   ** for the same iCode
1914   */
1915   ic = NULL;
1916   for (csl=shead;csl!=stail->next;csl=csl->next)
1917     if (csl->ic && !csl->isComment)
1918       {
1919         ic = csl->ic;
1920         break;
1921       }
1922   single_iCode = (ic!=NULL);
1923   for (csl=shead;csl!=stail->next;csl=csl->next)
1924     if ((csl->ic != ic) && !csl->isComment)
1925       {
1926         /* More than one iCode was found. However, if it's just the
1927         ** last line with the different iCode and it was not changed
1928         ** in the replacement, everything else must be the first iCode.
1929         */
1930         if ((csl==stail) && matchLine (stail->line, rtail->line, NULL))
1931           {
1932             rtail->ic = stail->ic;
1933             for (crl=rhead;crl!=rtail;crl=crl->next)
1934               crl->ic = ic;
1935             return;
1936           }
1937
1938         single_iCode = FALSE;
1939         break;
1940       }
1941
1942   /* If all of the source lines came from the same iCode, then so have
1943   ** all of the replacement lines too.
1944   */
1945   if (single_iCode)
1946     {
1947       for (crl=rhead;crl!=rtail->next;crl=crl->next)
1948         crl->ic = ic;
1949       return;
1950     }
1951
1952   /* The source lines span iCodes, so we may end up with replacement
1953   ** lines that we don't know which iCode(s) to associate with. Do the
1954   ** best we can by using the following strategies:
1955   **    1) Start at the top and scan down. As long as the source line
1956   **       matches the replacement line, they have the same iCode.
1957   **    2) Start at the bottom and scan up. As long as the source line
1958   **       matches the replacement line, they have the same iCode.
1959   **    3) For any label in the source, look for a matching label in
1960   **       the replacment. If found, they have the same iCode. From
1961   **       these matching labels, scan down for additional matching
1962   **       lines; if found, they also have the same iCode.
1963   */
1964
1965   /* Strategy #1: Start at the top and scan down for matches
1966   */
1967   reassociate_ic_down(shead,stail,rhead,rtail);
1968
1969   /* Strategy #2: Start at the bottom and scan up for matches
1970   */
1971   reassociate_ic_up(shead,stail,rhead,rtail);
1972
1973   /* Strategy #3: Try to match labels
1974   */
1975   csl = shead;
1976   while (1)
1977     {
1978       /* skip over any comments */
1979       while (csl!=stail->next && csl->isComment)
1980         csl = csl->next;
1981       if (csl==stail->next)
1982         break;
1983
1984       if (csl->isLabel)
1985         {
1986           /* found a source line label; look for it in the replacment lines */
1987           crl = rhead;
1988           while (1)
1989             {
1990               while (crl!=rtail->next && crl->isComment)
1991                 crl = crl->next;
1992               if (crl==rtail->next)
1993                 break;
1994               if (matchLine(csl->line, crl->line, NULL))
1995                 {
1996                   reassociate_ic_down(csl,stail,crl,rtail);
1997                   break;
1998                 }
1999               else
2000                 crl = crl->next;
2001             }
2002         }
2003       csl = csl->next;
2004     }
2005
2006   /* Try to assign a meaningful iCode to any comment that is missing
2007      one. Since they are comments, it's ok to make mistakes; we are just
2008      trying to improve continuity to simplify other tests.
2009   */
2010   ic = NULL;
2011   for (crl=rtail;crl!=rhead->prev;crl=crl->prev)
2012     {
2013       if (!crl->ic && ic && crl->isComment)
2014         crl->ic = ic;
2015       ic = crl->ic;
2016     }
2017 }
2018
2019
2020 /*-----------------------------------------------------------------*/
2021 /* replaceRule - does replacement of a matching pattern            */
2022 /*-----------------------------------------------------------------*/
2023 static void
2024 replaceRule (lineNode ** shead, lineNode * stail, peepRule * pr)
2025 {
2026   lineNode *cl = NULL;
2027   lineNode *pl = NULL, *lhead = NULL;
2028   /* a long function name and long variable name can evaluate to
2029      4x max pattern length e.g. "mov dptr,((fie_var>>8)<<8)+fie_var" */
2030   char lb[MAX_PATTERN_LEN*4];
2031   char *lbp;
2032   lineNode *comment = NULL;
2033
2034   /* collect all the comment lines in the source */
2035   for (cl = *shead; cl != stail; cl = cl->next)
2036     {
2037       if (cl->line && (*cl->line == ';' || cl->isDebug))
2038         {
2039           pl = (pl ? connectLine (pl, newLineNode (cl->line)) :
2040                 (comment = newLineNode (cl->line)));
2041           pl->isDebug = cl->isDebug;
2042           pl->isComment = cl->isComment || (*cl->line == ';');
2043         }
2044     }
2045   cl = NULL;
2046
2047   /* for all the lines in the replacement pattern do */
2048   for (pl = pr->replace; pl; pl = pl->next)
2049     {
2050       char *v;
2051       char *l;
2052       lbp = lb;
2053
2054       l = pl->line;
2055
2056       while (*l)
2057         {
2058           /* if the line contains a variable */
2059           if (*l == '%' && ISCHARDIGIT (*(l + 1)))
2060             {
2061               v = hTabItemWithKey (pr->vars, keyForVar (l + 1));
2062               if (!v)
2063                 {
2064                   fprintf (stderr, "used unbound variable in replacement\n");
2065                   l++;
2066                   continue;
2067                 }
2068               while (*v) {
2069                 *lbp++ = *v++;
2070               }
2071               l++;
2072               while (ISCHARDIGIT (*l)) {
2073                 l++;
2074               }
2075               continue;
2076             }
2077           *lbp++ = *l++;
2078         }
2079
2080       *lbp = '\0';
2081       if (cl)
2082         cl = connectLine (cl, newLineNode (lb));
2083       else
2084         lhead = cl = newLineNode (lb);
2085       cl->isComment = pl->isComment;
2086       cl->isLabel   = pl->isLabel;
2087     }
2088
2089   /* add the comments if any to the head of list */
2090   if (comment)
2091     {
2092       lineNode *lc = comment;
2093       while (lc->next)
2094         lc = lc->next;
2095       lc->next = lhead;
2096       if (lhead)
2097         lhead->prev = lc;
2098       lhead = comment;
2099     }
2100
2101   if (lhead)
2102     {
2103       /* determine which iCodes the replacment lines relate to */
2104       reassociate_ic(*shead,stail,lhead,cl);
2105
2106       /* now we need to connect / replace the original chain */
2107       /* if there is a prev then change it */
2108       if ((*shead)->prev)
2109         {
2110           (*shead)->prev->next = lhead;
2111           lhead->prev = (*shead)->prev;
2112         }
2113       *shead = lhead;
2114       /* now for the tail */
2115       if (stail && stail->next)
2116         {
2117           stail->next->prev = cl;
2118           if (cl)
2119             cl->next = stail->next;
2120         }
2121     }
2122   else
2123     {
2124       /* the replacement is empty - delete the source lines */
2125       if ((*shead)->prev)
2126         (*shead)->prev->next = stail->next;
2127       if (stail->next)
2128         stail->next->prev = (*shead)->prev;
2129       *shead = stail->next;
2130     }
2131 }
2132
2133 /* Returns TRUE if this line is a label definition.
2134
2135  * If so, start will point to the start of the label name,
2136  * and len will be it's length.
2137  */
2138 bool
2139 isLabelDefinition (const char *line, const char **start, int *len,
2140                    bool isPeepRule)
2141 {
2142   const char *cp = line;
2143
2144   /* This line is a label if if consists of:
2145    * [optional whitespace] followed by identifier chars
2146    * (alnum | $ | _ ) followed by a colon.
2147    */
2148
2149   while (*cp && ISCHARSPACE (*cp))
2150     {
2151       cp++;
2152     }
2153
2154   if (!*cp)
2155     {
2156       return FALSE;
2157     }
2158
2159   *start = cp;
2160
2161   while (ISCHARALNUM (*cp) || (*cp == '$') || (*cp == '_') ||
2162          (isPeepRule && (*cp == '%')))
2163     {
2164       cp++;
2165     }
2166
2167   if ((cp == *start) || (*cp != ':'))
2168     {
2169       return FALSE;
2170     }
2171
2172   *len = (cp - (*start));
2173   return TRUE;
2174 }
2175
2176 /* Quick & dirty string hash function. */
2177 static int
2178 hashSymbolName (const char *name)
2179 {
2180   int hash = 0;
2181
2182   while (*name)
2183     {
2184       hash = (hash << 6) ^ *name;
2185       name++;
2186     }
2187
2188   if (hash < 0)
2189     {
2190       hash = -hash;
2191     }
2192
2193   return hash % HTAB_SIZE;
2194 }
2195
2196 /* Build a hash of all labels in the passed set of lines
2197  * and how many times they are referenced.
2198  */
2199 static void
2200 buildLabelRefCountHash (lineNode * head)
2201 {
2202   lineNode *line;
2203   const char *label;
2204   int labelLen;
2205   int i;
2206
2207   assert (labelHash == NULL);
2208   labelHash = newHashTable (HTAB_SIZE);
2209
2210   /* First pass: locate all the labels. */
2211   for (line = head; line; line = line->next)
2212     {
2213       if (line->isLabel  ||
2214           line->isInline)
2215         {
2216           /* run isLabelDefinition to:
2217              - look for labels in inline assembler
2218              - calculate labelLen
2219           */
2220           if (isLabelDefinition (line->line, &label, &labelLen, FALSE) &&
2221               labelLen <= SDCC_NAME_MAX)
2222             {
2223               labelHashEntry *entry;
2224
2225               entry = traceAlloc (&_G.labels, Safe_alloc(sizeof (labelHashEntry)));
2226
2227               memcpy (entry->name, label, labelLen);
2228               entry->name[labelLen] = 0;
2229               entry->refCount = -1;
2230
2231               /* Assume function entry points are referenced somewhere,   */
2232               /* even if we can't find a reference (might be from outside */
2233               /* the function) */
2234               if (line->ic && (line->ic->op == FUNCTION))
2235                 entry->refCount++;
2236
2237               hTabAddItem (&labelHash, hashSymbolName (entry->name), entry);
2238             }
2239         }
2240     }
2241
2242
2243   /* Second pass: for each line, note all the referenced labels. */
2244   /* This is ugly, O(N^2) stuff. Optimizations welcome... */
2245   line = head;
2246   while (line)
2247     {
2248       for (i = 0; i < HTAB_SIZE; i++)
2249         {
2250           labelHashEntry *thisEntry;
2251
2252           thisEntry = hTabFirstItemWK (labelHash, i);
2253
2254           while (thisEntry)
2255             {
2256               if (strstr (line->line, thisEntry->name))
2257                 {
2258                   thisEntry->refCount++;
2259                 }
2260               thisEntry = hTabNextItemWK (labelHash);
2261             }
2262         }
2263       line = line->next;
2264     }
2265
2266 #if 0
2267   /* Spew the contents of the table. Debugging fun only. */
2268   for (i = 0; i < HTAB_SIZE; i++)
2269     {
2270       labelHashEntry *thisEntry;
2271
2272       thisEntry = hTabFirstItemWK (labelHash, i);
2273
2274       while (thisEntry)
2275         {
2276           fprintf (stderr, "label: %s ref %d\n",
2277                    thisEntry->name, thisEntry->refCount);
2278           thisEntry = hTabNextItemWK (labelHash);
2279         }
2280     }
2281 #endif
2282 }
2283
2284 /* How does this work?
2285    peepHole
2286     For each rule,
2287      For each line,
2288       Try to match
2289       If it matches,
2290        replace and restart.
2291
2292     matchRule
2293      matchLine
2294
2295   Where is stuff allocated?
2296
2297 */
2298
2299 /*-----------------------------------------------------------------*/
2300 /* peepHole - matches & substitutes rules                          */
2301 /*-----------------------------------------------------------------*/
2302 void
2303 peepHole (lineNode ** pls)
2304 {
2305   lineNode *spl;
2306   peepRule *pr;
2307   lineNode *mtail = NULL;
2308   bool restart, replaced;
2309
2310 #if !OPT_DISABLE_PIC || !OPT_DISABLE_PIC16
2311   /* The PIC port uses a different peep hole optimizer based on "pCode" */
2312   if (TARGET_IS_PIC || TARGET_IS_PIC16)
2313     return;
2314 #endif
2315
2316   assert(labelHash == NULL);
2317
2318   do
2319     {
2320       restart = FALSE;
2321
2322       /* for all rules */
2323       for (pr = rootRules; pr; pr = pr->next)
2324         {
2325           for (spl = *pls; spl; spl = replaced ? spl : spl->next)
2326             {
2327               replaced = FALSE;
2328
2329               /* if inline assembler then no peep hole */
2330               if (spl->isInline)
2331                 continue;
2332
2333               /* don't waste time starting a match on debug symbol
2334               ** or comment */
2335               if (spl->isDebug || spl->isComment || *(spl->line)==';')
2336                 continue;
2337
2338               mtail = NULL;
2339
2340               /* Tidy up any data stored in the hTab */
2341
2342               /* if it matches */
2343               if (matchRule (spl, &mtail, pr, *pls))
2344                 {
2345                   /* restart at the replaced line */
2346                   replaced = TRUE;
2347
2348                   /* then replace */
2349                   if (spl == *pls)
2350                     {
2351                       replaceRule (pls, mtail, pr);
2352                       spl = *pls;
2353                     }
2354                   else
2355                     replaceRule (&spl, mtail, pr);
2356
2357                   /* if restart rule type then
2358                      start at the top again */
2359                   if (pr->restart)
2360                     {
2361                       restart = TRUE;
2362                     }
2363                 }
2364
2365               if (pr->vars)
2366                 {
2367                   hTabDeleteAll (pr->vars);
2368                   Safe_free (pr->vars);
2369                   pr->vars = NULL;
2370                 }
2371
2372               freeTrace (&_G.values);
2373             }
2374         }
2375     } while (restart == TRUE);
2376
2377   if (labelHash)
2378     {
2379       hTabDeleteAll (labelHash);
2380       freeTrace (&_G.labels);
2381     }
2382   labelHash = NULL;
2383 }
2384
2385
2386 /*-----------------------------------------------------------------*/
2387 /* readFileIntoBuffer - reads a file into a string buffer          */
2388 /*-----------------------------------------------------------------*/
2389 static char *
2390 readFileIntoBuffer (char *fname)
2391 {
2392   FILE *f;
2393   char *rs = NULL;
2394   int nch = 0;
2395   int ch;
2396   char lb[MAX_PATTERN_LEN];
2397
2398   if (!(f = fopen (fname, "r")))
2399     {
2400       fprintf (stderr, "cannot open peep rule file\n");
2401       return NULL;
2402     }
2403
2404   while ((ch = fgetc (f)) != EOF)
2405     {
2406       lb[nch++] = ch;
2407
2408       /* if we maxed out our local buffer */
2409       if (nch >= (MAX_PATTERN_LEN - 2))
2410         {
2411           lb[nch] = '\0';
2412           /* copy it into allocated buffer */
2413           if (rs)
2414             {
2415               rs = Safe_realloc (rs, strlen (rs) + strlen (lb) + 1);
2416               strncatz (rs, lb,  strlen (rs) + strlen (lb) + 1);
2417             }
2418           else
2419             {
2420               rs = Safe_strdup (lb);
2421             }
2422           nch = 0;
2423         }
2424     }
2425
2426   /* if some charaters left over */
2427   if (nch)
2428     {
2429       lb[nch] = '\0';
2430       /* copy it into allocated buffer */
2431       if (rs)
2432         {
2433           rs = Safe_realloc (rs, strlen (rs) + strlen (lb) + 1);
2434           strncatz (rs, lb, strlen (rs) + strlen (lb) + 1);
2435         }
2436       else
2437         {
2438           rs = Safe_strdup (lb);
2439         }
2440     }
2441   return rs;
2442 }
2443
2444 /*-----------------------------------------------------------------*/
2445 /* initPeepHole - initialises the peep hole optimizer stuff        */
2446 /*-----------------------------------------------------------------*/
2447 void
2448 initPeepHole ()
2449 {
2450   char *s;
2451
2452   /* read in the default rules */
2453   if (!options.nopeep)
2454     {
2455       readRules (port->peep.default_rules);
2456     }
2457
2458   /* if we have any additional file read it too */
2459   if (options.peep_file)
2460     {
2461       readRules (s = readFileIntoBuffer (options.peep_file));
2462       setToNull ((void *) &s);
2463       /* override nopeep setting, default rules have not been read */
2464       options.nopeep = 0;
2465     }
2466
2467
2468 #if !OPT_DISABLE_PIC
2469   /* Convert the peep rules into pcode.
2470      NOTE: this is only support in the PIC port (at the moment)
2471   */
2472         if (TARGET_IS_PIC)
2473                 peepRules2pCode(rootRules);
2474 #endif
2475
2476 #if !OPT_DISABLE_PIC16
2477   /* Convert the peep rules into pcode.
2478      NOTE: this is only support in the PIC port (at the moment)
2479        and the PIC16 port (VR 030601)
2480   */
2481         if (TARGET_IS_PIC16)
2482                 pic16_peepRules2pCode(rootRules);
2483
2484 #endif
2485
2486 }