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