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