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