1 /*-------------------------------------------------------------------------
2 SDCCpeeph.c - The peep hole optimizer: for interpreting the
5 Written By - Sandeep Dutta . sandeep.dutta@usa.net (1999)
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
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.
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.
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 -------------------------------------------------------------------------*/
27 #include "SDCCpeeph.h"
30 peepRule *rootRules = NULL;
31 peepRule *currRule = NULL;
36 char name[SDCC_NAME_MAX + 1];
41 hTab *labelHash = NULL;
43 static int hashSymbolName (const char *name);
44 static void buildLabelRefCountHash (lineNode * head);
46 static bool matchLine (char *, char *, hTab **);
48 #define FBYNAME(x) int x (hTab *vars, lineNode *currPl, lineNode *head, \
51 /*-----------------------------------------------------------------*/
52 /* pcDistance - afinds a label back ward or forward */
53 /*-----------------------------------------------------------------*/
55 pcDistance (lineNode * cpos, char *lbl, bool back)
58 char buff[MAX_PATTERN_LEN];
61 sprintf (buff, "%s:", lbl);
67 pl->line[strlen (pl->line) - 1] != ':' &&
72 if (strncmp (pl->line, buff, strlen (buff)) == 0)
84 /*-----------------------------------------------------------------*/
85 /* flat24bitMode - will check to see if we are in flat24 mode */
86 /*-----------------------------------------------------------------*/
87 FBYNAME (flat24bitMode)
89 return (options.model == MODEL_FLAT24);
92 /*-----------------------------------------------------------------*/
93 /* labelInRange - will check to see if label %5 is within range */
94 /*-----------------------------------------------------------------*/
95 FBYNAME (labelInRange)
97 /* assumes that %5 pattern variable has the label name */
98 char *lbl = hTabItemWithKey (vars, 5);
104 /* if the previous teo instructions are "ljmp"s then don't
105 do it since it can be part of a jump table */
106 if (currPl->prev && currPl->prev->prev &&
107 strstr (currPl->prev->line, "ljmp") &&
108 strstr (currPl->prev->prev->line, "ljmp"))
111 /* calculate the label distance : the jump for reladdr can be
112 +/- 127 bytes, here Iam assuming that an average 8051
113 instruction is 2 bytes long, so if the label is more than
114 63 intructions away, the label is considered out of range
115 for a relative jump. we could get more precise this will
116 suffice for now since it catches > 90% cases */
117 dist = (pcDistance (currPl, lbl, TRUE) +
118 pcDistance (currPl, lbl, FALSE));
120 /* if (!dist || dist > 45) has produced wrong sjmp */
121 /* 07-Sep-2000 Michael Schmitt */
122 /* FIX for Peephole 132 */
123 /* switch with lots of case can lead to a sjmp with a distance */
124 /* out of the range for sjmp */
125 if (!dist || dist > 43)
131 /*-----------------------------------------------------------------*/
132 /* operandsNotSame - check if %1 & %2 are the same */
133 /*-----------------------------------------------------------------*/
134 FBYNAME (operandsNotSame)
136 char *op1 = hTabItemWithKey (vars, 1);
137 char *op2 = hTabItemWithKey (vars, 2);
139 if (strcmp (op1, op2) == 0)
147 * takes two parameters: a variable (bound to a label name)
148 * and an expected reference count.
150 * Returns TRUE if that label is defined and referenced exactly
151 * the given number of times.
153 FBYNAME (labelRefCount)
155 int varNumber, expectedRefCount;
158 /* If we don't have the label hash table yet, build it. */
161 buildLabelRefCountHash (head);
164 if (sscanf (cmdLine, "%*[ \t%]%d %d", &varNumber, &expectedRefCount) == 2)
166 char *label = hTabItemWithKey (vars, varNumber);
170 labelHashEntry *entry;
172 entry = hTabFirstItemWK (labelHash, hashSymbolName (label));
176 if (!strcmp (label, entry->name))
180 entry = hTabNextItemWK (labelHash);
186 fprintf (stderr, "labelRefCount: %s has refCount %d, want %d\n",
187 label, entry->refCount, expectedRefCount);
190 rc = (expectedRefCount == entry->refCount);
194 fprintf (stderr, "*** internal error: no label has entry for"
195 " %s in labelRefCount peephole.\n",
201 fprintf (stderr, "*** internal error: var %d not bound"
202 " in peephole labelRefCount rule.\n",
210 "*** internal error: labelRefCount peephole restriction"
211 " malformed: %s\n", cmdLine);
216 /*-----------------------------------------------------------------*/
217 /* callFuncByName - calls a function as defined in the table */
218 /*-----------------------------------------------------------------*/
220 callFuncByName (char *fname,
228 int (*func) (hTab *, lineNode *, lineNode *, const char *);
233 "labelInRange", labelInRange
237 "operandsNotSame", operandsNotSame
241 "24bitMode", flat24bitMode
245 "labelRefCount", labelRefCount
251 for (i = 0; i < ((sizeof (ftab)) / (sizeof (struct ftab))); i++)
252 if (strncmp (ftab[i].fname, fname, strlen (ftab[i].fname)) == 0)
254 return (*ftab[i].func) (vars, currPl, head,
255 fname + strlen (ftab[i].fname));
257 fprintf (stderr, "could not find named function in function table\n");
261 /*-----------------------------------------------------------------*/
262 /* printLine - prints a line chain into a given file */
263 /*-----------------------------------------------------------------*/
265 printLine (lineNode * head, FILE * of)
272 /* don't indent comments & labels */
274 (*head->line == ';' ||
275 head->line[strlen (head->line) - 1] == ':'))
276 fprintf (of, "%s\n", head->line);
278 fprintf (of, "\t%s\n", head->line);
283 /*-----------------------------------------------------------------*/
284 /* newPeepRule - creates a new peeprule and attach it to the root */
285 /*-----------------------------------------------------------------*/
287 newPeepRule (lineNode * match,
294 pr = Safe_calloc (1, sizeof (peepRule));
296 pr->replace = replace;
297 pr->restart = restart;
301 pr->cond = Safe_calloc (1, strlen (cond) + 1);
302 strcpy (pr->cond, cond);
307 pr->vars = newHashTable (100);
309 /* if root is empty */
311 rootRules = currRule = pr;
313 currRule = currRule->next = pr;
318 /*-----------------------------------------------------------------*/
319 /* newLineNode - creates a new peep line */
320 /*-----------------------------------------------------------------*/
322 newLineNode (char *line)
326 pl = Safe_calloc (1, sizeof (lineNode));
327 pl->line = Safe_calloc (1, strlen (line) + 1);
328 strcpy (pl->line, line);
332 /*-----------------------------------------------------------------*/
333 /* connectLine - connects two lines */
334 /*-----------------------------------------------------------------*/
336 connectLine (lineNode * pl1, lineNode * pl2)
340 fprintf (stderr, "trying to connect null line\n");
350 #define SKIP_SPACE(x,y) { while (*x && (isspace(*x) || *x == '\n')) x++; \
351 if (!*x) { fprintf(stderr,y); return ; } }
353 #define EXPECT_STR(x,y,z) { while (*x && strncmp(x,y,strlen(y))) x++ ; \
354 if (!*x) { fprintf(stderr,z); return ; } }
355 #define EXPECT_CHR(x,y,z) { while (*x && *x != y) x++ ; \
356 if (!*x) { fprintf(stderr,z); return ; } }
358 /*-----------------------------------------------------------------*/
359 /* getPeepLine - parses the peep lines */
360 /*-----------------------------------------------------------------*/
362 getPeepLine (lineNode ** head, char **bpp)
364 char lines[MAX_PATTERN_LEN];
367 lineNode *currL = NULL;
374 fprintf (stderr, "unexpected end of match pattern\n");
381 while (isspace (*bp) ||
392 /* read till end of line */
394 while ((*bp != '\n' && *bp != '}') && *bp)
399 *head = currL = newLineNode (lines);
401 currL = connectLine (currL, newLineNode (lines));
407 /*-----------------------------------------------------------------*/
408 /* readRules - reads the rules from a string buffer */
409 /*-----------------------------------------------------------------*/
414 char lines[MAX_PATTERN_LEN];
418 lineNode *currL = NULL;
424 /* look for the token "replace" that is the
426 while (*bp && strncmp (bp, "replace", 7))
433 /* then look for either "restart" or '{' */
434 while (strncmp (bp, "restart", 7) &&
441 fprintf (stderr, "expected 'restart' or '{'\n");
449 { /* must be restart */
451 bp += strlen ("restart");
453 EXPECT_CHR (bp, '{', "expected '{'\n");
457 /* skip thru all the blank space */
458 SKIP_SPACE (bp, "unexpected end of rule\n");
460 match = replace = currL = NULL;
461 /* we are the start of a rule */
462 getPeepLine (&match, &bp);
464 /* now look for by */
465 EXPECT_STR (bp, "by", "expected 'by'\n");
467 /* then look for a '{' */
468 EXPECT_CHR (bp, '{', "expected '{'\n");
471 SKIP_SPACE (bp, "unexpected end of rule\n");
472 getPeepLine (&replace, &bp);
474 /* look for a 'if' */
475 while ((isspace (*bp) || *bp == '\n') && *bp)
478 if (strncmp (bp, "if", 2) == 0)
481 while ((isspace (*bp) || *bp == '\n') && *bp)
485 fprintf (stderr, "expected condition name\n");
489 /* look for the condition */
491 while (*bp && (*bp != '\n'))
497 newPeepRule (match, replace, lines, restart);
500 newPeepRule (match, replace, NULL, restart);
505 /*-----------------------------------------------------------------*/
506 /* keyForVar - returns the numeric key for a var */
507 /*-----------------------------------------------------------------*/
522 /*-----------------------------------------------------------------*/
523 /* bindVar - binds a value to a variable in the given hashtable */
524 /*-----------------------------------------------------------------*/
526 bindVar (int key, char **s, hTab ** vtab)
528 char vval[MAX_PATTERN_LEN];
532 /* first get the value of the variable */
534 /* the value is ended by a ',' or space or newline or null or ) */
543 /* if we find a '(' then we need to balance it */
562 vvx = Safe_calloc (1, strlen (vval) + 1);
564 hTabAddItem (vtab, key, vvx);
568 /*-----------------------------------------------------------------*/
569 /* matchLine - matches one line */
570 /*-----------------------------------------------------------------*/
572 matchLine (char *s, char *d, hTab ** vars)
581 /* skip white space in both */
587 /* if the destination is a var */
588 if (*d == '%' && isdigit (*(d + 1)))
590 char *v = hTabItemWithKey (*vars, keyForVar (d + 1));
591 /* if the variable is already bound
592 then it MUST match with dest */
600 /* variable not bound we need to
602 bindVar (keyForVar (d + 1), &s, vars);
604 /* in either case go past the variable */
610 /* they should be an exact match other wise */
623 /* get rid of the trailing spaces
624 in both source & destination */
633 /* after all this if only one of them
634 has something left over then no match */
641 /*-----------------------------------------------------------------*/
642 /* matchRule - matches a all the rule lines */
643 /*-----------------------------------------------------------------*/
645 matchRule (lineNode * pl,
650 lineNode *spl; /* source pl */
651 lineNode *rpl; /* rule peep line */
653 hTabClearAll (pr->vars);
654 /* setToNull((void **) &pr->vars); */
655 /* pr->vars = newHashTable(100); */
657 /* for all the lines defined in the rule */
663 /* if the source line starts with a ';' then
664 comment line don't process or the source line
665 contains == . debugger information skip it */
667 (*spl->line == ';' || spl->isDebug))
673 if (!matchLine (spl->line, rpl->line, &pr->vars))
684 /* if this rule has additional conditions */
687 if (callFuncByName (pr->cond, pr->vars, pl, head))
705 /*-----------------------------------------------------------------*/
706 /* replaceRule - does replacement of a matching pattern */
707 /*-----------------------------------------------------------------*/
709 replaceRule (lineNode ** shead, lineNode * stail, peepRule * pr)
712 lineNode *pl = NULL, *lhead = NULL;
713 /* a long function name and long variable name can evaluate to
714 4x max pattern length e.g. "mov dptr,((fie_var>>8)<<8)+fie_var" */
715 char lb[MAX_PATTERN_LEN*4];
717 lineNode *comment = NULL;
719 /* collect all the comment lines in the source */
720 for (cl = *shead; cl != stail; cl = cl->next)
722 if (cl->line && (*cl->line == ';' || cl->isDebug))
724 pl = (pl ? connectLine (pl, newLineNode (cl->line)) :
725 (comment = newLineNode (cl->line)));
726 pl->isDebug = cl->isDebug;
731 /* for all the lines in the replacement pattern do */
732 for (pl = pr->replace; pl; pl = pl->next)
741 /* if the line contains a variable */
742 if (*l == '%' && isdigit (*(l + 1)))
744 v = hTabItemWithKey (pr->vars, keyForVar (l + 1));
747 fprintf (stderr, "used unbound variable in replacement\n");
755 while (isdigit (*l)) {
765 cl = connectLine (cl, newLineNode (lb));
767 lhead = cl = newLineNode (lb);
770 /* add the comments if any to the head of list */
773 lineNode *lc = comment;
782 /* now we need to connect / replace the original chain */
783 /* if there is a prev then change it */
786 (*shead)->prev->next = lhead;
787 lhead->prev = (*shead)->prev;
791 /* now for the tail */
792 if (stail && stail->next)
794 stail->next->prev = cl;
796 cl->next = stail->next;
800 /* Returns TRUE if this line is a label definition.
802 * If so, start will point to the start of the label name,
803 * and len will be it's length.
806 isLabelDefinition (const char *line, const char **start, int *len)
808 const char *cp = line;
810 /* This line is a label if if consists of:
811 * [optional whitespace] followed by identifier chars
812 * (alnum | $ | _ ) followed by a colon.
815 while (*cp && isspace (*cp))
827 while (isalnum (*cp) || (*cp == '$') || (*cp == '_'))
832 if ((cp == *start) || (*cp != ':'))
837 *len = (cp - (*start));
841 /* Quick & dirty string hash function. */
843 hashSymbolName (const char *name)
849 hash = (hash << 6) ^ *name;
858 return hash % HTAB_SIZE;
861 /* Build a hash of all labels in the passed set of lines
862 * and how many times they are referenced.
865 buildLabelRefCountHash (lineNode * head)
872 assert (labelHash == NULL);
873 labelHash = newHashTable (HTAB_SIZE);
875 /* First pass: locate all the labels. */
880 if (isLabelDefinition (line->line, &label, &labelLen)
881 && labelLen <= SDCC_NAME_MAX)
883 labelHashEntry *entry;
885 entry = Safe_calloc (1, sizeof (labelHashEntry));
887 memcpy (entry->name, label, labelLen);
888 entry->name[labelLen] = 0;
889 entry->refCount = -1;
891 hTabAddItem (&labelHash, hashSymbolName (entry->name), entry);
897 /* Second pass: for each line, note all the referenced labels. */
898 /* This is ugly, O(N^2) stuff. Optimizations welcome... */
902 for (i = 0; i < HTAB_SIZE; i++)
904 labelHashEntry *thisEntry;
906 thisEntry = hTabFirstItemWK (labelHash, i);
910 if (strstr (line->line, thisEntry->name))
912 thisEntry->refCount++;
914 thisEntry = hTabNextItemWK (labelHash);
921 /* Spew the contents of the table. Debugging fun only. */
922 for (i = 0; i < HTAB_SIZE; i++)
924 labelHashEntry *thisEntry;
926 thisEntry = hTabFirstItemWK (labelHash, i);
930 fprintf (stderr, "label: %s ref %d\n",
931 thisEntry->name, thisEntry->refCount);
932 thisEntry = hTabNextItemWK (labelHash);
938 /*-----------------------------------------------------------------*/
939 /* peepHole - matches & substitutes rules */
940 /*-----------------------------------------------------------------*/
942 peepHole (lineNode ** pls)
946 lineNode *mtail = NULL;
950 hTabDeleteAll (labelHash);
956 for (pr = rootRules; pr; pr = pr->next)
959 for (spl = *pls; spl; spl = spl->next)
961 /* if inline assembler then no peep hole */
968 if (matchRule (spl, &mtail, pr, *pls))
973 replaceRule (pls, mtail, pr);
975 replaceRule (&spl, mtail, pr);
977 /* if restart rule type then
978 start at the top again */
987 /*-----------------------------------------------------------------*/
988 /* readFileIntoBuffer - reads a file into a string buffer */
989 /*-----------------------------------------------------------------*/
991 readFileIntoBuffer (char *fname)
997 char lb[MAX_PATTERN_LEN];
999 if (!(f = fopen (fname, "r")))
1001 fprintf (stderr, "cannot open peep rule file\n");
1005 while ((ch = fgetc (f)) != EOF)
1009 /* if we maxed out our local buffer */
1010 if (nch >= (MAX_PATTERN_LEN - 2))
1013 /* copy it into allocated buffer */
1016 rs = Safe_realloc (rs, strlen (rs) + strlen (lb) + 1);
1021 rs = Safe_calloc (1, strlen (lb) + 1);
1028 /* if some charaters left over */
1032 /* copy it into allocated buffer */
1035 rs = Safe_realloc (rs, strlen (rs) + strlen (lb) + 1);
1040 rs = Safe_calloc (1, strlen (lb) + 1);
1047 /*-----------------------------------------------------------------*/
1048 /* initPeepHole - initiaises the peep hole optimizer stuff */
1049 /*-----------------------------------------------------------------*/
1055 /* read in the default rules */
1056 readRules (port->peep.default_rules);
1058 /* if we have any additional file read it too */
1059 if (options.peep_file)
1061 readRules (s = readFileIntoBuffer (options.peep_file));
1062 setToNull ((void **) &s);