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