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