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