added peephole conditional labelJTInRange. For now you need to set the environment...
[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, const 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 /* callFuncByName - calls a function as defined in the table       */
764 /*-----------------------------------------------------------------*/
765 int 
766 callFuncByName (char *fname,
767                 hTab * vars,
768                 lineNode * currPl,
769                 lineNode * endPl,
770                 lineNode * head)
771 {
772   struct ftab
773   {
774     char *fname;
775     int (*func) (hTab *, lineNode *, lineNode *, lineNode *, const char *);
776   }
777   ftab[] =
778   {
779     {
780       "labelInRange", labelInRange
781     }
782     ,
783     {
784       "labelJTInRange", labelJTInRange
785     }
786     ,
787     {
788       "operandsNotSame", operandsNotSame
789     }
790     ,
791     {
792       "operandsNotSame3", operandsNotSame3
793     }
794     ,
795     {
796       "operandsNotSame4", operandsNotSame4
797     }
798     ,
799     {
800       "operandsNotSame5", operandsNotSame5
801     }
802     ,
803     {
804       "operandsNotSame6", operandsNotSame6
805     }
806     ,
807     {
808       "operandsNotSame7", operandsNotSame7
809     }
810     ,
811     {
812       "operandsNotSame8", operandsNotSame8
813     }
814     ,     
815     {
816       "24bitMode", flat24bitMode
817     }
818     ,
819     {
820       "xramMovcOption", xramMovcOption
821     }
822     ,
823     {
824       "labelRefCount", labelRefCount
825     }
826     ,
827     {
828       "portIsDS390", portIsDS390
829     },
830     {
831       "labelIsReturnOnly", labelIsReturnOnly
832     },
833     {
834       "okToRemoveSLOC", okToRemoveSLOC
835     },
836     {
837       "24bitModeAndPortDS390", flat24bitModeAndPortDS390
838     },
839     {
840       "notVolatile", notVolatile
841     }
842   };
843   int   i;
844   char  *cmdCopy, *funcName, *funcArgs;
845   int   rc = -1;
846     
847   /* Isolate the function name part (we are passed the full condition 
848    * string including arguments) 
849    */
850   cmdCopy = Safe_strdup(fname);
851   funcName = strtok(cmdCopy, " \t");
852   funcArgs = strtok(NULL, "");
853
854     for (i = 0; i < ((sizeof (ftab)) / (sizeof (struct ftab))); i++)
855     {
856         if (strcmp (ftab[i].fname, funcName) == 0)
857         {
858             rc = (*ftab[i].func) (vars, currPl, endPl, head,
859                                   funcArgs);
860         }
861     }
862     
863     if (rc == -1)
864     {
865         fprintf (stderr, 
866                  "could not find named function \"%s\" in "
867                  "peephole function table\n",
868                  funcName);
869         // If the function couldn't be found, let's assume it's
870         // a bad rule and refuse it.
871         rc = FALSE;
872     }
873
874     Safe_free(cmdCopy);
875     
876   return rc;
877 }
878
879 /*-----------------------------------------------------------------*/
880 /* printLine - prints a line chain into a given file               */
881 /*-----------------------------------------------------------------*/
882 void 
883 printLine (lineNode * head, FILE * of)
884 {
885   iCode *last_ic = NULL;
886   bool debug_iCode_tracking = (getenv("DEBUG_ICODE_TRACKING")!=NULL);
887   
888   if (!of)
889     of = stdout;
890
891   while (head)
892     {
893       if (head->ic!=last_ic)
894         {
895           last_ic = head->ic;
896           if (debug_iCode_tracking)
897             {
898               if (head->ic)
899                 fprintf (of, "; block = %d, seq = %d\n",
900                          head->ic->block, head->ic->seq);
901               else
902                 fprintf (of, "; iCode lost\n");
903             }
904         }
905         
906       /* don't indent comments & labels */
907       if (head->line &&
908           (*head->line == ';' ||
909            head->line[strlen (head->line) - 1] == ':')) {
910         fprintf (of, "%s\n", head->line);
911       } else {
912         if (head->isInline && *head->line=='#') {
913           // comment out preprocessor directives in inline asm
914           fprintf (of, ";");
915         }
916         fprintf (of, "\t%s\n", head->line);
917       }
918       head = head->next;
919     }
920 }
921
922 /*-----------------------------------------------------------------*/
923 /* newPeepRule - creates a new peeprule and attach it to the root  */
924 /*-----------------------------------------------------------------*/
925 peepRule *
926 newPeepRule (lineNode * match,
927              lineNode * replace,
928              char *cond,
929              int restart)
930 {
931   peepRule *pr;
932
933   pr = Safe_alloc ( sizeof (peepRule));
934   pr->match = match;
935   pr->replace = replace;
936   pr->restart = restart;
937
938   if (cond && *cond)
939     {
940       pr->cond = Safe_strdup (cond);
941     }
942   else
943     pr->cond = NULL;
944
945   pr->vars = newHashTable (100);
946
947   /* if root is empty */
948   if (!rootRules)
949     rootRules = currRule = pr;
950   else
951     currRule = currRule->next = pr;
952
953   return pr;
954 }
955
956 /*-----------------------------------------------------------------*/
957 /* newLineNode - creates a new peep line                           */
958 /*-----------------------------------------------------------------*/
959 lineNode *
960 newLineNode (char *line)
961 {
962   lineNode *pl;
963
964   pl = Safe_alloc ( sizeof (lineNode));
965   pl->line = Safe_strdup (line);
966   pl->ic = NULL;
967   return pl;
968 }
969
970 /*-----------------------------------------------------------------*/
971 /* connectLine - connects two lines                                */
972 /*-----------------------------------------------------------------*/
973 lineNode *
974 connectLine (lineNode * pl1, lineNode * pl2)
975 {
976   if (!pl1 || !pl2)
977     {
978       fprintf (stderr, "trying to connect null line\n");
979       return NULL;
980     }
981
982   pl2->prev = pl1;
983   pl1->next = pl2;
984
985   return pl2;
986 }
987
988 #define SKIP_SPACE(x,y) { while (*x && (isspace(*x) || *x == '\n')) x++; \
989                          if (!*x) { fprintf(stderr,y); return ; } }
990
991 #define EXPECT_STR(x,y,z) { while (*x && strncmp(x,y,strlen(y))) x++ ;   \
992                            if (!*x) { fprintf(stderr,z); return ; } }
993 #define EXPECT_CHR(x,y,z) { while (*x && *x != y) x++ ;   \
994                            if (!*x) { fprintf(stderr,z); return ; } }
995
996 /*-----------------------------------------------------------------*/
997 /* getPeepLine - parses the peep lines                             */
998 /*-----------------------------------------------------------------*/
999 static void 
1000 getPeepLine (lineNode ** head, char **bpp)
1001 {
1002   char lines[MAX_PATTERN_LEN];
1003   char *lp;
1004   int isComment;
1005
1006   lineNode *currL = NULL;
1007   char *bp = *bpp;
1008   while (1)
1009     {
1010
1011       if (!*bp)
1012         {
1013           fprintf (stderr, "unexpected end of match pattern\n");
1014           return;
1015         }
1016
1017       if (*bp == '\n')
1018         {
1019           bp++;
1020           while (isspace (*bp) ||
1021                  *bp == '\n')
1022             bp++;
1023         }
1024
1025       if (*bp == '}')
1026         {
1027           bp++;
1028           break;
1029         }
1030
1031       /* read till end of line */
1032       lp = lines;
1033       while ((*bp != '\n' && *bp != '}') && *bp)
1034         *lp++ = *bp++;
1035       *lp = '\0';
1036       
1037       lp = lines;
1038       while (*lp && isspace(*lp))
1039         lp++;
1040       isComment = (*lp == ';');
1041         
1042       if (!isComment || (isComment && !options.noPeepComments))
1043         {
1044           if (!currL)
1045             *head = currL = newLineNode (lines);
1046           else
1047             currL = connectLine (currL, newLineNode (lines));
1048           currL->isComment = isComment;
1049         }
1050
1051     }
1052
1053   *bpp = bp;
1054 }
1055
1056 /*-----------------------------------------------------------------*/
1057 /* readRules - reads the rules from a string buffer                */
1058 /*-----------------------------------------------------------------*/
1059 static void 
1060 readRules (char *bp)
1061 {
1062   char restart = 0;
1063   char lines[MAX_PATTERN_LEN];
1064   char *lp;
1065   lineNode *match;
1066   lineNode *replace;
1067   lineNode *currL = NULL;
1068
1069   if (!bp)
1070     return;
1071 top:
1072   restart = 0;
1073   /* look for the token "replace" that is the
1074      start of a rule */
1075   while (*bp && strncmp (bp, "replace", 7))
1076     bp++;
1077
1078   /* if not found */
1079   if (!*bp)
1080     return;
1081
1082   /* then look for either "restart" or '{' */
1083   while (strncmp (bp, "restart", 7) &&
1084          *bp != '{' && bp)
1085     bp++;
1086
1087   /* not found */
1088   if (!*bp)
1089     {
1090       fprintf (stderr, "expected 'restart' or '{'\n");
1091       return;
1092     }
1093
1094   /* if brace */
1095   if (*bp == '{')
1096     bp++;
1097   else
1098     {                           /* must be restart */
1099       restart++;
1100       bp += strlen ("restart");
1101       /* look for '{' */
1102       EXPECT_CHR (bp, '{', "expected '{'\n");
1103       bp++;
1104     }
1105
1106   /* skip thru all the blank space */
1107   SKIP_SPACE (bp, "unexpected end of rule\n");
1108
1109   match = replace = currL = NULL;
1110   /* we are the start of a rule */
1111   getPeepLine (&match, &bp);
1112
1113   /* now look for by */
1114   EXPECT_STR (bp, "by", "expected 'by'\n");
1115
1116   /* then look for a '{' */
1117   EXPECT_CHR (bp, '{', "expected '{'\n");
1118   bp++;
1119
1120   SKIP_SPACE (bp, "unexpected end of rule\n");
1121   getPeepLine (&replace, &bp);
1122
1123   /* look for a 'if' */
1124   while ((isspace (*bp) || *bp == '\n') && *bp)
1125     bp++;
1126
1127   if (strncmp (bp, "if", 2) == 0)
1128     {
1129       bp += 2;
1130       while ((isspace (*bp) || *bp == '\n') && *bp)
1131         bp++;
1132       if (!*bp)
1133         {
1134           fprintf (stderr, "expected condition name\n");
1135           return;
1136         }
1137
1138       /* look for the condition */
1139       lp = lines;
1140       while (*bp && (*bp != '\n'))
1141         {
1142           *lp++ = *bp++;
1143         }
1144       *lp = '\0';
1145
1146       newPeepRule (match, replace, lines, restart);
1147     }
1148   else
1149     newPeepRule (match, replace, NULL, restart);
1150   goto top;
1151
1152 }
1153
1154 /*-----------------------------------------------------------------*/
1155 /* keyForVar - returns the numeric key for a var                   */
1156 /*-----------------------------------------------------------------*/
1157 static int 
1158 keyForVar (char *d)
1159 {
1160   int i = 0;
1161
1162   while (isdigit (*d))
1163     {
1164       i *= 10;
1165       i += (*d++ - '0');
1166     }
1167
1168   return i;
1169 }
1170
1171 /*-----------------------------------------------------------------*/
1172 /* bindVar - binds a value to a variable in the given hashtable    */
1173 /*-----------------------------------------------------------------*/
1174 static void 
1175 bindVar (int key, char **s, hTab ** vtab)
1176 {
1177   char vval[MAX_PATTERN_LEN];
1178   char *vvx;
1179   char *vv = vval;
1180
1181   /* first get the value of the variable */
1182   vvx = *s;
1183   /* the value is ended by a ',' or space or newline or null or ) */
1184   while (*vvx &&
1185          *vvx != ',' &&
1186          !isspace (*vvx) &&
1187          *vvx != '\n' &&
1188          *vvx != ':' &&
1189          *vvx != ')')
1190     {
1191       char ubb = 0;
1192       /* if we find a '(' then we need to balance it */
1193       if (*vvx == '(')
1194         {
1195           ubb++;
1196           while (ubb)
1197             {
1198               *vv++ = *vvx++;
1199               if (*vvx == '(')
1200                 ubb++;
1201               if (*vvx == ')')
1202                 ubb--;
1203             }
1204           // include the trailing ')'
1205           *vv++ = *vvx++;
1206         }
1207       else
1208         *vv++ = *vvx++;
1209     }
1210   *s = vvx;
1211   *vv = '\0';
1212   /* got value */
1213   vvx = traceAlloc (&_G.values, Safe_strdup(vval));
1214
1215   hTabAddItem (vtab, key, vvx);
1216 }
1217
1218 /*-----------------------------------------------------------------*/
1219 /* matchLine - matches one line                                    */
1220 /*-----------------------------------------------------------------*/
1221 static bool 
1222 matchLine (char *s, char *d, hTab ** vars)
1223 {
1224
1225   if (!s || !(*s))
1226     return FALSE;
1227
1228   while (*s && *d)
1229     {
1230
1231       /* skip white space in both */
1232       while (isspace (*s))
1233         s++;
1234       while (isspace (*d))
1235         d++;
1236
1237       /* if the destination is a var */
1238       if (*d == '%' && isdigit (*(d + 1)) && vars)
1239         {
1240           char *v = hTabItemWithKey (*vars, keyForVar (d + 1));
1241           /* if the variable is already bound
1242              then it MUST match with dest */
1243           if (v)
1244             {
1245               while (*v)
1246                 if (*v++ != *s++)
1247                   return FALSE;
1248             }
1249           else
1250             /* variable not bound we need to
1251                bind it */
1252             bindVar (keyForVar (d + 1), &s, vars);
1253
1254           /* in either case go past the variable */
1255           d++;
1256           while (isdigit (*d))
1257             d++;
1258
1259           while (isspace (*s))
1260             s++;
1261           while (isspace (*d))
1262             d++;
1263         }
1264
1265       /* they should be an exact match other wise */
1266       if (*s && *d)
1267         {
1268           if (*s++ != *d++)
1269             return FALSE;
1270         }
1271
1272     }
1273
1274   /* get rid of the trailing spaces
1275      in both source & destination */
1276   if (*s)
1277     while (isspace (*s))
1278       s++;
1279
1280   if (*d)
1281     while (isspace (*d))
1282       d++;
1283
1284   /* after all this if only one of them
1285      has something left over then no match */
1286   if (*s || *d)
1287     return FALSE;
1288
1289   return TRUE;
1290 }
1291
1292 /*-----------------------------------------------------------------*/
1293 /* matchRule - matches a all the rule lines                        */
1294 /*-----------------------------------------------------------------*/
1295 static bool 
1296 matchRule (lineNode * pl,
1297            lineNode ** mtail,
1298            peepRule * pr,
1299            lineNode * head)
1300 {
1301   lineNode *spl;                /* source pl */
1302   lineNode *rpl;                /* rule peep line */
1303
1304 /*     setToNull((void *) &pr->vars);    */
1305 /*     pr->vars = newHashTable(100); */
1306
1307   /* for all the lines defined in the rule */
1308   rpl = pr->match;
1309   spl = pl;
1310   while (spl && rpl)
1311     {
1312
1313       /* if the source line starts with a ';' then
1314          comment line don't process or the source line
1315          contains == . debugger information skip it */
1316       if (spl->line &&
1317           (*spl->line == ';' || spl->isDebug))
1318         {
1319           spl = spl->next;
1320           continue;
1321         }
1322
1323       if (!matchLine (spl->line, rpl->line, &pr->vars))
1324         return FALSE;
1325
1326       rpl = rpl->next;
1327       if (rpl)
1328         spl = spl->next;
1329     }
1330
1331   /* if rules ended */
1332   if (!rpl)
1333     {
1334       /* if this rule has additional conditions */
1335       if (pr->cond)
1336         {
1337           if (callFuncByName (pr->cond, pr->vars, pl, spl, head))
1338             {
1339               *mtail = spl;
1340               return TRUE;
1341             }
1342           else
1343             return FALSE;
1344         }
1345       else
1346         {
1347           *mtail = spl;
1348           return TRUE;
1349         }
1350     }
1351   else
1352     return FALSE;
1353 }
1354
1355 static void
1356 reassociate_ic_down (lineNode *shead, lineNode *stail,
1357                      lineNode *rhead, lineNode *rtail)
1358 {
1359   lineNode *csl;        /* current source line */
1360   lineNode *crl;        /* current replacement line */
1361
1362   csl = shead;
1363   crl = rhead;
1364   while (1)
1365     {
1366       /* skip over any comments */
1367       while (csl!=stail->next && csl->isComment)
1368         csl = csl->next;
1369       while (crl!=rtail->next && crl->isComment)
1370         crl = crl->next;
1371
1372       /* quit if we reach the end */
1373       if ((csl==stail->next) || (crl==rtail->next) || crl->ic)
1374         break;
1375
1376       if (matchLine(csl->line,crl->line,NULL))
1377         {
1378           crl->ic = csl->ic;
1379           csl = csl->next;
1380           crl = crl->next;
1381         }
1382       else
1383         break;
1384     }
1385 }
1386
1387 static void
1388 reassociate_ic_up (lineNode *shead, lineNode *stail,
1389                    lineNode *rhead, lineNode *rtail)
1390 {
1391   lineNode *csl;        /* current source line */
1392   lineNode *crl;        /* current replacement line */
1393
1394   csl = stail;
1395   crl = rtail;
1396   while (1)
1397     {
1398       /* skip over any comments */
1399       while (csl!=shead->prev && csl->isComment)
1400         csl = csl->prev;
1401       while (crl!=rhead->prev && crl->isComment)
1402         crl = crl->prev;
1403
1404       /* quit if we reach the end */
1405       if ((csl==shead->prev) || (crl==rhead->prev) || crl->ic)
1406         break;
1407
1408       if (matchLine(csl->line,crl->line,NULL))
1409         {
1410           crl->ic = csl->ic;
1411           csl = csl->prev;
1412           crl = crl->prev;
1413         }
1414       else
1415         break;
1416     }
1417 }
1418
1419 /*------------------------------------------------------------------*/
1420 /* reassociate_ic - reassociate replacement lines with origin iCode */
1421 /*------------------------------------------------------------------*/
1422 static void
1423 reassociate_ic (lineNode *shead, lineNode *stail,
1424                 lineNode *rhead, lineNode *rtail)
1425 {
1426   lineNode *csl;        /* current source line */
1427   lineNode *crl;        /* current replacement line */
1428   bool single_iCode;
1429   iCode *ic;
1430   
1431   /* Check to see if all the source lines (excluding comments) came
1432   ** for the same iCode
1433   */
1434   ic = NULL;
1435   for (csl=shead;csl!=stail->next;csl=csl->next)
1436     if (csl->ic && !csl->isComment)
1437       {
1438         ic = csl->ic;
1439         break;
1440       }
1441   single_iCode = (ic!=NULL);
1442   for (csl=shead;csl!=stail->next;csl=csl->next)
1443     if ((csl->ic != ic) && !csl->isComment)
1444       {
1445         /* More than one iCode was found. However, if it's just the
1446         ** last line with the different iCode and it was not changed
1447         ** in the replacement, everything else must be the first iCode.
1448         */
1449         if ((csl==stail) && matchLine (stail->line, rtail->line, NULL))
1450           {
1451             rtail->ic = stail->ic;
1452             for (crl=rhead;crl!=rtail;crl=crl->next)
1453               crl->ic = ic;
1454             return;
1455           }
1456             
1457         single_iCode = FALSE;
1458         break;
1459       }
1460   
1461   /* If all of the source lines came from the same iCode, then so have
1462   ** all of the replacement lines too.
1463   */
1464   if (single_iCode)
1465     {
1466       for (crl=rhead;crl!=rtail->next;crl=crl->next)
1467         crl->ic = ic;
1468       return;
1469     }
1470   
1471   /* The source lines span iCodes, so we may end up with replacement
1472   ** lines that we don't know which iCode(s) to associate with. Do the
1473   ** best we can by using the following strategies:
1474   **    1) Start at the top and scan down. As long as the source line
1475   **       matches the replacement line, they have the same iCode.
1476   **    2) Start at the bottom and scan up. As long as the source line
1477   **       matches the replacement line, they have the same iCode.
1478   **    3) For any label in the source, look for a matching label in
1479   **       the replacment. If found, they have the same iCode. From
1480   **       these matching labels, scan down for additional matching
1481   **       lines; if found, they also have the same iCode.
1482   */
1483
1484   /* Strategy #1: Start at the top and scan down for matches
1485   */
1486   reassociate_ic_down(shead,stail,rhead,rtail);
1487   
1488   /* Strategy #2: Start at the bottom and scan up for matches
1489   */
1490   reassociate_ic_up(shead,stail,rhead,rtail);
1491
1492   /* Strategy #3: Try to match labels
1493   */
1494   csl = shead;
1495   while (1)
1496     {
1497       const char *labelStart;
1498       int labelLength;
1499       
1500       /* skip over any comments */
1501       while (csl!=stail->next && csl->isComment)
1502         csl = csl->next;
1503       if (csl==stail->next)
1504         break;
1505
1506       if (isLabelDefinition(csl->line, &labelStart, &labelLength))
1507         {
1508           /* found a source line label; look for it in the replacment lines */
1509           crl = rhead;
1510           while (1)
1511             {
1512               while (crl!=rtail->next && crl->isComment)
1513                 crl = crl->next;
1514               if (crl==rtail->next)
1515                 break;
1516               if (matchLine(csl->line, crl->line, NULL))
1517                 {
1518                   reassociate_ic_down(csl,stail,crl,rtail);
1519                   break;
1520                 }
1521               else
1522                 crl = crl->next;
1523             }
1524         }
1525       csl = csl->next;
1526     }
1527   
1528   /* Try to assign a meaningful iCode to any comment that is missing
1529      one. Since they are comments, it's ok to make mistakes; we are just
1530      trying to improve continuity to simplify other tests.
1531   */
1532   ic = NULL;
1533   for (crl=rtail;crl!=rhead->prev;crl=crl->prev)
1534     {
1535       if (!crl->ic && ic && crl->isComment)
1536         crl->ic = ic;
1537       ic = crl->ic;
1538     }
1539 }
1540
1541                   
1542 /*-----------------------------------------------------------------*/
1543 /* replaceRule - does replacement of a matching pattern            */
1544 /*-----------------------------------------------------------------*/
1545 static void 
1546 replaceRule (lineNode ** shead, lineNode * stail, peepRule * pr)
1547 {
1548   lineNode *cl = NULL;
1549   lineNode *pl = NULL, *lhead = NULL;
1550   /* a long function name and long variable name can evaluate to
1551      4x max pattern length e.g. "mov dptr,((fie_var>>8)<<8)+fie_var" */
1552   char lb[MAX_PATTERN_LEN*4];
1553   char *lbp;
1554   lineNode *comment = NULL;
1555
1556   /* collect all the comment lines in the source */
1557   for (cl = *shead; cl != stail; cl = cl->next)
1558     {
1559       if (cl->line && (*cl->line == ';' || cl->isDebug))
1560         {
1561           pl = (pl ? connectLine (pl, newLineNode (cl->line)) :
1562                 (comment = newLineNode (cl->line)));
1563           pl->isDebug = cl->isDebug;
1564           pl->isComment = cl->isComment || (*cl->line == ';');
1565         }
1566     }
1567   cl = NULL;
1568
1569   /* for all the lines in the replacement pattern do */
1570   for (pl = pr->replace; pl; pl = pl->next)
1571     {
1572       char *v;
1573       char *l;
1574       lbp = lb;
1575
1576       l = pl->line;
1577
1578       while (*l)
1579         {
1580           /* if the line contains a variable */
1581           if (*l == '%' && isdigit (*(l + 1)))
1582             {
1583               v = hTabItemWithKey (pr->vars, keyForVar (l + 1));
1584               if (!v)
1585                 {
1586                   fprintf (stderr, "used unbound variable in replacement\n");
1587                   l++;
1588                   continue;
1589                 }
1590               while (*v) {
1591                 *lbp++ = *v++;
1592               }
1593               l++;
1594               while (isdigit (*l)) {
1595                 l++;
1596               }
1597               continue;
1598             }
1599           *lbp++ = *l++;
1600         }
1601
1602       *lbp = '\0';
1603       if (cl)
1604         cl = connectLine (cl, newLineNode (lb));
1605       else
1606         lhead = cl = newLineNode (lb);
1607       cl->isComment = pl->isComment;
1608     }
1609
1610   /* add the comments if any to the head of list */
1611   if (comment)
1612     {
1613       lineNode *lc = comment;
1614       while (lc->next)
1615         lc = lc->next;
1616       lc->next = lhead;
1617       if (lhead)
1618         lhead->prev = lc;
1619       lhead = comment;
1620     }
1621
1622   if (lhead)
1623     {
1624       /* determine which iCodes the replacment lines relate to */
1625       reassociate_ic(*shead,stail,lhead,cl);
1626
1627       /* now we need to connect / replace the original chain */
1628       /* if there is a prev then change it */
1629       if ((*shead)->prev)
1630         {
1631           (*shead)->prev->next = lhead;
1632           lhead->prev = (*shead)->prev;
1633         }
1634       *shead = lhead;
1635       /* now for the tail */
1636       if (stail && stail->next)
1637         {
1638           stail->next->prev = cl;
1639           if (cl)
1640             cl->next = stail->next;
1641         }
1642     }
1643   else
1644     {
1645       /* the replacement is empty - delete the source lines */
1646       if ((*shead)->prev)
1647         (*shead)->prev->next = stail->next;
1648       if (stail->next)
1649         stail->next->prev = (*shead)->prev;
1650       *shead = stail->next;
1651     }
1652 }
1653
1654 /* Returns TRUE if this line is a label definition.
1655
1656  * If so, start will point to the start of the label name,
1657  * and len will be it's length.
1658  */
1659 bool 
1660 isLabelDefinition (const char *line, const char **start, int *len)
1661 {
1662   const char *cp = line;
1663
1664   /* This line is a label if if consists of:
1665    * [optional whitespace] followed by identifier chars
1666    * (alnum | $ | _ ) followed by a colon.
1667    */
1668
1669   while (*cp && isspace (*cp))
1670     {
1671       cp++;
1672     }
1673
1674   if (!*cp)
1675     {
1676       return FALSE;
1677     }
1678
1679   *start = cp;
1680
1681   while (isalnum (*cp) || (*cp == '$') || (*cp == '_'))
1682     {
1683       cp++;
1684     }
1685
1686   if ((cp == *start) || (*cp != ':'))
1687     {
1688       return FALSE;
1689     }
1690
1691   *len = (cp - (*start));
1692   return TRUE;
1693 }
1694
1695 /* Quick & dirty string hash function. */
1696 static int 
1697 hashSymbolName (const char *name)
1698 {
1699   int hash = 0;
1700
1701   while (*name)
1702     {
1703       hash = (hash << 6) ^ *name;
1704       name++;
1705     }
1706
1707   if (hash < 0)
1708     {
1709       hash = -hash;
1710     }
1711
1712   return hash % HTAB_SIZE;
1713 }
1714
1715 /* Build a hash of all labels in the passed set of lines
1716  * and how many times they are referenced.
1717  */
1718 static void 
1719 buildLabelRefCountHash (lineNode * head)
1720 {
1721   lineNode *line;
1722   const char *label;
1723   int labelLen;
1724   int i;
1725
1726   assert (labelHash == NULL);
1727   labelHash = newHashTable (HTAB_SIZE);
1728
1729   /* First pass: locate all the labels. */
1730   line = head;
1731
1732   while (line)
1733     {
1734       if (isLabelDefinition (line->line, &label, &labelLen)
1735           && labelLen <= SDCC_NAME_MAX)
1736         {
1737           labelHashEntry *entry;
1738
1739           entry = traceAlloc (&_G.labels, Safe_alloc(sizeof (labelHashEntry)));
1740
1741           memcpy (entry->name, label, labelLen);
1742           entry->name[labelLen] = 0;
1743           entry->refCount = -1;
1744           
1745           /* Assume function entry points are referenced somewhere,   */
1746           /* even if we can't find a reference (might be from outside */
1747           /* the function) */
1748           if (line->ic && (line->ic->op == FUNCTION))
1749             entry->refCount++;
1750
1751           hTabAddItem (&labelHash, hashSymbolName (entry->name), entry);
1752         }
1753       line = line->next;
1754     }
1755
1756
1757   /* Second pass: for each line, note all the referenced labels. */
1758   /* This is ugly, O(N^2) stuff. Optimizations welcome... */
1759   line = head;
1760   while (line)
1761     {
1762       for (i = 0; i < HTAB_SIZE; i++)
1763         {
1764           labelHashEntry *thisEntry;
1765
1766           thisEntry = hTabFirstItemWK (labelHash, i);
1767
1768           while (thisEntry)
1769             {
1770               if (strstr (line->line, thisEntry->name))
1771                 {
1772                   thisEntry->refCount++;
1773                 }
1774               thisEntry = hTabNextItemWK (labelHash);
1775             }
1776         }
1777       line = line->next;
1778     }
1779
1780 #if 0
1781   /* Spew the contents of the table. Debugging fun only. */
1782   for (i = 0; i < HTAB_SIZE; i++)
1783     {
1784       labelHashEntry *thisEntry;
1785
1786       thisEntry = hTabFirstItemWK (labelHash, i);
1787
1788       while (thisEntry)
1789         {
1790           fprintf (stderr, "label: %s ref %d\n",
1791                    thisEntry->name, thisEntry->refCount);
1792           thisEntry = hTabNextItemWK (labelHash);
1793         }
1794     }
1795 #endif
1796 }
1797
1798 /* How does this work?
1799    peepHole
1800     For each rule,
1801      For each line,
1802       Try to match
1803       If it matches,
1804        replace and restart.
1805
1806     matchRule
1807      matchLine
1808
1809   Where is stuff allocated?
1810   
1811 */
1812
1813 /*-----------------------------------------------------------------*/
1814 /* peepHole - matches & substitutes rules                          */
1815 /*-----------------------------------------------------------------*/
1816 void 
1817 peepHole (lineNode ** pls)
1818 {
1819   lineNode *spl;
1820   peepRule *pr;
1821   lineNode *mtail = NULL;
1822   bool restart;
1823
1824 #if !OPT_DISABLE_PIC || !OPT_DISABLE_PIC16
1825   /* The PIC port uses a different peep hole optimizer based on "pCode" */
1826   if (TARGET_IS_PIC || TARGET_IS_PIC16)
1827     return;
1828 #endif
1829
1830   assert(labelHash == NULL);
1831
1832   do
1833     {
1834       restart = FALSE;
1835
1836       /* for all rules */
1837       for (pr = rootRules; pr; pr = pr->next)
1838         {
1839           for (spl = *pls; spl; spl = spl->next)
1840             {
1841               /* if inline assembler then no peep hole */
1842               if (spl->isInline)
1843                 continue;
1844
1845               /* don't waste time starting a match on debug symbol
1846               ** or comment */
1847               if (spl->isDebug || spl->isComment || *(spl->line)==';')
1848                 continue;
1849               
1850               mtail = NULL;
1851
1852               /* Tidy up any data stored in the hTab */
1853               
1854               /* if it matches */
1855               if (matchRule (spl, &mtail, pr, *pls))
1856                 {
1857                   
1858                   /* then replace */
1859                   if (spl == *pls)
1860                     replaceRule (pls, mtail, pr);
1861                   else
1862                     replaceRule (&spl, mtail, pr);
1863                   
1864                   /* if restart rule type then
1865                      start at the top again */
1866                   if (pr->restart)
1867                     {
1868                       restart = TRUE;
1869                     }
1870                 }
1871               
1872               if (pr->vars)
1873                 {
1874                   hTabDeleteAll (pr->vars);
1875                   Safe_free (pr->vars);
1876                   pr->vars = NULL;
1877                 }
1878               
1879               freeTrace (&_G.values);
1880             }
1881         }
1882     } while (restart == TRUE);
1883
1884   if (labelHash)
1885     {
1886       hTabDeleteAll (labelHash);
1887       freeTrace (&_G.labels);
1888     }
1889   labelHash = NULL;
1890 }
1891
1892
1893 /*-----------------------------------------------------------------*/
1894 /* readFileIntoBuffer - reads a file into a string buffer          */
1895 /*-----------------------------------------------------------------*/
1896 static char *
1897 readFileIntoBuffer (char *fname)
1898 {
1899   FILE *f;
1900   char *rs = NULL;
1901   int nch = 0;
1902   int ch;
1903   char lb[MAX_PATTERN_LEN];
1904
1905   if (!(f = fopen (fname, "r")))
1906     {
1907       fprintf (stderr, "cannot open peep rule file\n");
1908       return NULL;
1909     }
1910
1911   while ((ch = fgetc (f)) != EOF)
1912     {
1913       lb[nch++] = ch;
1914
1915       /* if we maxed out our local buffer */
1916       if (nch >= (MAX_PATTERN_LEN - 2))
1917         {
1918           lb[nch] = '\0';
1919           /* copy it into allocated buffer */
1920           if (rs)
1921             {
1922               rs = Safe_realloc (rs, strlen (rs) + strlen (lb) + 1);
1923               strncatz (rs, lb,  strlen (rs) + strlen (lb) + 1);
1924             }
1925           else
1926             {
1927               rs = Safe_strdup (lb);
1928             }
1929           nch = 0;
1930         }
1931     }
1932
1933   /* if some charaters left over */
1934   if (nch)
1935     {
1936       lb[nch] = '\0';
1937       /* copy it into allocated buffer */
1938       if (rs)
1939         {
1940           rs = Safe_realloc (rs, strlen (rs) + strlen (lb) + 1);
1941           strncatz (rs, lb, strlen (rs) + strlen (lb) + 1);
1942         }
1943       else
1944         {
1945           rs = Safe_strdup (lb);
1946         }
1947     }
1948   return rs;
1949 }
1950
1951 /*-----------------------------------------------------------------*/
1952 /* initPeepHole - initialises the peep hole optimizer stuff        */
1953 /*-----------------------------------------------------------------*/
1954 void 
1955 initPeepHole ()
1956 {
1957   char *s;
1958
1959   /* read in the default rules */
1960   readRules (port->peep.default_rules);
1961
1962   /* if we have any additional file read it too */
1963   if (options.peep_file)
1964     {
1965       readRules (s = readFileIntoBuffer (options.peep_file));
1966       setToNull ((void *) &s);
1967     }
1968
1969
1970 #if !OPT_DISABLE_PIC
1971   /* Convert the peep rules into pcode.
1972      NOTE: this is only support in the PIC port (at the moment)
1973   */
1974         if (TARGET_IS_PIC)
1975                 peepRules2pCode(rootRules);
1976 #endif
1977
1978 #if !OPT_DISABLE_PIC16
1979   /* Convert the peep rules into pcode.
1980      NOTE: this is only support in the PIC port (at the moment)
1981        and the PIC16 port (VR 030601)
1982   */
1983         if (TARGET_IS_PIC16)
1984                 pic16_peepRules2pCode(rootRules);
1985
1986 #endif
1987
1988 }