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