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