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