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