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