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