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 -------------------------------------------------------------------------*/
28 static peepRule *rootRules = NULL;
29 static peepRule *currRule = NULL;
34 char name[SDCC_NAME_MAX + 1];
39 static hTab *labelHash = NULL;
47 static int hashSymbolName (const char *name);
48 static void buildLabelRefCountHash (lineNode * head);
50 static bool matchLine (char *, char *, hTab **);
52 #define FBYNAME(x) int x (hTab *vars, lineNode *currPl, lineNode *head, \
56 void peepRules2pCode(peepRule *);
59 /*-----------------------------------------------------------------*/
60 /* pcDistance - afinds a label back ward or forward */
61 /*-----------------------------------------------------------------*/
63 pcDistance (lineNode * cpos, char *lbl, bool back)
66 char buff[MAX_PATTERN_LEN];
69 sprintf (buff, "%s:", lbl);
75 pl->line[strlen (pl->line) - 1] != ':' &&
80 if (strncmp (pl->line, buff, strlen (buff)) == 0)
92 /*-----------------------------------------------------------------*/
93 /* flat24bitModeAndPortDS390 - */
94 /*-----------------------------------------------------------------*/
95 FBYNAME (flat24bitModeAndPortDS390)
97 return ((strcmp(port->target,"ds390") == 0) &&
98 (options.model == MODEL_FLAT24));
101 /*-----------------------------------------------------------------*/
102 /* portIsDS390 - return true if port is DS390 */
103 /*-----------------------------------------------------------------*/
104 FBYNAME (portIsDS390)
106 return (strcmp(port->target,"ds390") == 0);
109 /*-----------------------------------------------------------------*/
110 /* flat24bitMode - will check to see if we are in flat24 mode */
111 /*-----------------------------------------------------------------*/
112 FBYNAME (flat24bitMode)
114 return (options.model == MODEL_FLAT24);
117 /*-----------------------------------------------------------------*/
118 /* labelInRange - will check to see if label %5 is within range */
119 /*-----------------------------------------------------------------*/
120 FBYNAME (labelInRange)
122 /* assumes that %5 pattern variable has the label name */
123 char *lbl = hTabItemWithKey (vars, 5);
129 /* if the previous two instructions are "ljmp"s then don't
130 do it since it can be part of a jump table */
131 if (currPl->prev && currPl->prev->prev &&
132 strstr (currPl->prev->line, "ljmp") &&
133 strstr (currPl->prev->prev->line, "ljmp"))
136 /* calculate the label distance : the jump for reladdr can be
137 +/- 127 bytes, here Iam assuming that an average 8051
138 instruction is 2 bytes long, so if the label is more than
139 63 intructions away, the label is considered out of range
140 for a relative jump. we could get more precise this will
141 suffice for now since it catches > 90% cases */
142 dist = (pcDistance (currPl, lbl, TRUE) +
143 pcDistance (currPl, lbl, FALSE));
145 /* if (!dist || dist > 45) has produced wrong sjmp */
146 /* 07-Sep-2000 Michael Schmitt */
147 /* FIX for Peephole 132 */
148 /* switch with lots of case can lead to a sjmp with a distance */
149 /* out of the range for sjmp */
150 if (!dist || dist > 43)
156 /*-----------------------------------------------------------------*/
157 /* operandsNotSame - check if %1 & %2 are the same */
158 /*-----------------------------------------------------------------*/
159 FBYNAME (operandsNotSame)
161 char *op1 = hTabItemWithKey (vars, 1);
162 char *op2 = hTabItemWithKey (vars, 2);
164 if (strcmp (op1, op2) == 0)
172 * takes two parameters: a variable (bound to a label name)
173 * and an expected reference count.
175 * Returns TRUE if that label is defined and referenced exactly
176 * the given number of times.
178 FBYNAME (labelRefCount)
180 int varNumber, expectedRefCount;
183 /* If we don't have the label hash table yet, build it. */
186 buildLabelRefCountHash (head);
189 if (sscanf (cmdLine, "%*[ \t%]%d %d", &varNumber, &expectedRefCount) == 2)
191 char *label = hTabItemWithKey (vars, varNumber);
195 labelHashEntry *entry;
197 entry = hTabFirstItemWK (labelHash, hashSymbolName (label));
201 if (!strcmp (label, entry->name))
205 entry = hTabNextItemWK (labelHash);
211 fprintf (stderr, "labelRefCount: %s has refCount %d, want %d\n",
212 label, entry->refCount, expectedRefCount);
215 rc = (expectedRefCount == entry->refCount);
219 fprintf (stderr, "*** internal error: no label has entry for"
220 " %s in labelRefCount peephole.\n",
226 fprintf (stderr, "*** internal error: var %d not bound"
227 " in peephole labelRefCount rule.\n",
235 "*** internal error: labelRefCount peephole restriction"
236 " malformed: %s\n", cmdLine);
241 /*-----------------------------------------------------------------*/
242 /* callFuncByName - calls a function as defined in the table */
243 /*-----------------------------------------------------------------*/
245 callFuncByName (char *fname,
253 int (*func) (hTab *, lineNode *, lineNode *, const char *);
258 "labelInRange", labelInRange
262 "operandsNotSame", operandsNotSame
266 "24bitMode", flat24bitMode
270 "labelRefCount", labelRefCount
274 "portIsDS390", portIsDS390
277 "24bitModeAndPortDS390", flat24bitModeAndPortDS390
282 for (i = 0; i < ((sizeof (ftab)) / (sizeof (struct ftab))); i++)
283 if (strncmp (ftab[i].fname, fname, strlen (ftab[i].fname)) == 0)
285 return (*ftab[i].func) (vars, currPl, head,
286 fname + strlen (ftab[i].fname));
288 fprintf (stderr, "could not find named function in function table\n");
292 /*-----------------------------------------------------------------*/
293 /* printLine - prints a line chain into a given file */
294 /*-----------------------------------------------------------------*/
296 printLine (lineNode * head, FILE * of)
303 /* don't indent comments & labels */
305 (*head->line == ';' ||
306 head->line[strlen (head->line) - 1] == ':')) {
307 fprintf (of, "%s\n", head->line);
309 if (head->isInline && *head->line=='#') {
310 // comment out preprocessor directives in inline asm
313 fprintf (of, "\t%s\n", head->line);
319 /*-----------------------------------------------------------------*/
320 /* newPeepRule - creates a new peeprule and attach it to the root */
321 /*-----------------------------------------------------------------*/
323 newPeepRule (lineNode * match,
330 pr = Safe_alloc ( sizeof (peepRule));
332 pr->replace = replace;
333 pr->restart = restart;
337 pr->cond = Safe_alloc ( strlen (cond) + 1);
338 strcpy (pr->cond, cond);
343 pr->vars = newHashTable (100);
345 /* if root is empty */
347 rootRules = currRule = pr;
349 currRule = currRule->next = pr;
354 /*-----------------------------------------------------------------*/
355 /* newLineNode - creates a new peep line */
356 /*-----------------------------------------------------------------*/
358 newLineNode (char *line)
362 pl = Safe_alloc ( sizeof (lineNode));
363 pl->line = Safe_alloc ( strlen (line) + 1);
364 strcpy (pl->line, line);
368 /*-----------------------------------------------------------------*/
369 /* connectLine - connects two lines */
370 /*-----------------------------------------------------------------*/
372 connectLine (lineNode * pl1, lineNode * pl2)
376 fprintf (stderr, "trying to connect null line\n");
386 #define SKIP_SPACE(x,y) { while (*x && (isspace(*x) || *x == '\n')) x++; \
387 if (!*x) { fprintf(stderr,y); return ; } }
389 #define EXPECT_STR(x,y,z) { while (*x && strncmp(x,y,strlen(y))) x++ ; \
390 if (!*x) { fprintf(stderr,z); return ; } }
391 #define EXPECT_CHR(x,y,z) { while (*x && *x != y) x++ ; \
392 if (!*x) { fprintf(stderr,z); return ; } }
394 /*-----------------------------------------------------------------*/
395 /* getPeepLine - parses the peep lines */
396 /*-----------------------------------------------------------------*/
398 getPeepLine (lineNode ** head, char **bpp)
400 char lines[MAX_PATTERN_LEN];
403 lineNode *currL = NULL;
410 fprintf (stderr, "unexpected end of match pattern\n");
417 while (isspace (*bp) ||
428 /* read till end of line */
430 while ((*bp != '\n' && *bp != '}') && *bp)
435 *head = currL = newLineNode (lines);
437 currL = connectLine (currL, newLineNode (lines));
443 /*-----------------------------------------------------------------*/
444 /* readRules - reads the rules from a string buffer */
445 /*-----------------------------------------------------------------*/
450 char lines[MAX_PATTERN_LEN];
454 lineNode *currL = NULL;
460 /* look for the token "replace" that is the
462 while (*bp && strncmp (bp, "replace", 7))
469 /* then look for either "restart" or '{' */
470 while (strncmp (bp, "restart", 7) &&
477 fprintf (stderr, "expected 'restart' or '{'\n");
485 { /* must be restart */
487 bp += strlen ("restart");
489 EXPECT_CHR (bp, '{', "expected '{'\n");
493 /* skip thru all the blank space */
494 SKIP_SPACE (bp, "unexpected end of rule\n");
496 match = replace = currL = NULL;
497 /* we are the start of a rule */
498 getPeepLine (&match, &bp);
500 /* now look for by */
501 EXPECT_STR (bp, "by", "expected 'by'\n");
503 /* then look for a '{' */
504 EXPECT_CHR (bp, '{', "expected '{'\n");
507 SKIP_SPACE (bp, "unexpected end of rule\n");
508 getPeepLine (&replace, &bp);
510 /* look for a 'if' */
511 while ((isspace (*bp) || *bp == '\n') && *bp)
514 if (strncmp (bp, "if", 2) == 0)
517 while ((isspace (*bp) || *bp == '\n') && *bp)
521 fprintf (stderr, "expected condition name\n");
525 /* look for the condition */
527 while (*bp && (*bp != '\n'))
533 newPeepRule (match, replace, lines, restart);
536 newPeepRule (match, replace, NULL, restart);
541 /*-----------------------------------------------------------------*/
542 /* keyForVar - returns the numeric key for a var */
543 /*-----------------------------------------------------------------*/
558 /*-----------------------------------------------------------------*/
559 /* bindVar - binds a value to a variable in the given hashtable */
560 /*-----------------------------------------------------------------*/
562 bindVar (int key, char **s, hTab ** vtab)
564 char vval[MAX_PATTERN_LEN];
568 /* first get the value of the variable */
570 /* the value is ended by a ',' or space or newline or null or ) */
579 /* if we find a '(' then we need to balance it */
598 vvx = traceAlloc (&_G.values, Safe_alloc(strlen (vval) + 1));
601 hTabAddItem (vtab, key, vvx);
604 /*-----------------------------------------------------------------*/
605 /* matchLine - matches one line */
606 /*-----------------------------------------------------------------*/
608 matchLine (char *s, char *d, hTab ** vars)
617 /* skip white space in both */
623 /* if the destination is a var */
624 if (*d == '%' && isdigit (*(d + 1)))
626 char *v = hTabItemWithKey (*vars, keyForVar (d + 1));
627 /* if the variable is already bound
628 then it MUST match with dest */
636 /* variable not bound we need to
638 bindVar (keyForVar (d + 1), &s, vars);
640 /* in either case go past the variable */
646 /* they should be an exact match other wise */
659 /* get rid of the trailing spaces
660 in both source & destination */
669 /* after all this if only one of them
670 has something left over then no match */
677 /*-----------------------------------------------------------------*/
678 /* matchRule - matches a all the rule lines */
679 /*-----------------------------------------------------------------*/
681 matchRule (lineNode * pl,
686 lineNode *spl; /* source pl */
687 lineNode *rpl; /* rule peep line */
689 /* setToNull((void **) &pr->vars); */
690 /* pr->vars = newHashTable(100); */
692 /* for all the lines defined in the rule */
698 /* if the source line starts with a ';' then
699 comment line don't process or the source line
700 contains == . debugger information skip it */
702 (*spl->line == ';' || spl->isDebug))
708 if (!matchLine (spl->line, rpl->line, &pr->vars))
719 /* if this rule has additional conditions */
722 if (callFuncByName (pr->cond, pr->vars, pl, head))
740 /*-----------------------------------------------------------------*/
741 /* replaceRule - does replacement of a matching pattern */
742 /*-----------------------------------------------------------------*/
744 replaceRule (lineNode ** shead, lineNode * stail, peepRule * pr)
747 lineNode *pl = NULL, *lhead = NULL;
748 /* a long function name and long variable name can evaluate to
749 4x max pattern length e.g. "mov dptr,((fie_var>>8)<<8)+fie_var" */
750 char lb[MAX_PATTERN_LEN*4];
752 lineNode *comment = NULL;
754 /* collect all the comment lines in the source */
755 for (cl = *shead; cl != stail; cl = cl->next)
757 if (cl->line && (*cl->line == ';' || cl->isDebug))
759 pl = (pl ? connectLine (pl, newLineNode (cl->line)) :
760 (comment = newLineNode (cl->line)));
761 pl->isDebug = cl->isDebug;
766 /* for all the lines in the replacement pattern do */
767 for (pl = pr->replace; pl; pl = pl->next)
777 /* if the line contains a variable */
778 if (*l == '%' && isdigit (*(l + 1)))
780 v = hTabItemWithKey (pr->vars, keyForVar (l + 1));
783 fprintf (stderr, "used unbound variable in replacement\n");
791 while (isdigit (*l)) {
801 cl = connectLine (cl, newLineNode (lb));
803 lhead = cl = newLineNode (lb);
806 /* add the comments if any to the head of list */
809 lineNode *lc = comment;
818 /* now we need to connect / replace the original chain */
819 /* if there is a prev then change it */
822 (*shead)->prev->next = lhead;
823 lhead->prev = (*shead)->prev;
827 /* now for the tail */
828 if (stail && stail->next)
830 stail->next->prev = cl;
832 cl->next = stail->next;
836 /* Returns TRUE if this line is a label definition.
838 * If so, start will point to the start of the label name,
839 * and len will be it's length.
842 isLabelDefinition (const char *line, const char **start, int *len)
844 const char *cp = line;
846 /* This line is a label if if consists of:
847 * [optional whitespace] followed by identifier chars
848 * (alnum | $ | _ ) followed by a colon.
851 while (*cp && isspace (*cp))
863 while (isalnum (*cp) || (*cp == '$') || (*cp == '_'))
868 if ((cp == *start) || (*cp != ':'))
873 *len = (cp - (*start));
877 /* Quick & dirty string hash function. */
879 hashSymbolName (const char *name)
885 hash = (hash << 6) ^ *name;
894 return hash % HTAB_SIZE;
897 /* Build a hash of all labels in the passed set of lines
898 * and how many times they are referenced.
901 buildLabelRefCountHash (lineNode * head)
908 assert (labelHash == NULL);
909 labelHash = newHashTable (HTAB_SIZE);
911 /* First pass: locate all the labels. */
916 if (isLabelDefinition (line->line, &label, &labelLen)
917 && labelLen <= SDCC_NAME_MAX)
919 labelHashEntry *entry;
921 entry = traceAlloc (&_G.labels, Safe_alloc(sizeof (labelHashEntry)));
923 memcpy (entry->name, label, labelLen);
924 entry->name[labelLen] = 0;
925 entry->refCount = -1;
927 hTabAddItem (&labelHash, hashSymbolName (entry->name), entry);
933 /* Second pass: for each line, note all the referenced labels. */
934 /* This is ugly, O(N^2) stuff. Optimizations welcome... */
938 for (i = 0; i < HTAB_SIZE; i++)
940 labelHashEntry *thisEntry;
942 thisEntry = hTabFirstItemWK (labelHash, i);
946 if (strstr (line->line, thisEntry->name))
948 thisEntry->refCount++;
950 thisEntry = hTabNextItemWK (labelHash);
957 /* Spew the contents of the table. Debugging fun only. */
958 for (i = 0; i < HTAB_SIZE; i++)
960 labelHashEntry *thisEntry;
962 thisEntry = hTabFirstItemWK (labelHash, i);
966 fprintf (stderr, "label: %s ref %d\n",
967 thisEntry->name, thisEntry->refCount);
968 thisEntry = hTabNextItemWK (labelHash);
974 /* How does this work?
985 Where is stuff allocated?
989 /*-----------------------------------------------------------------*/
990 /* peepHole - matches & substitutes rules */
991 /*-----------------------------------------------------------------*/
993 peepHole (lineNode ** pls)
997 lineNode *mtail = NULL;
1000 assert(labelHash == NULL);
1007 for (pr = rootRules; pr; pr = pr->next)
1009 for (spl = *pls; spl; spl = spl->next)
1011 /* if inline assembler then no peep hole */
1017 /* Tidy up any data stored in the hTab */
1020 if (matchRule (spl, &mtail, pr, *pls))
1025 replaceRule (pls, mtail, pr);
1027 replaceRule (&spl, mtail, pr);
1029 /* if restart rule type then
1030 start at the top again */
1039 hTabDeleteAll (pr->vars);
1040 Safe_free (pr->vars);
1044 freeTrace (&_G.values);
1047 } while (restart == TRUE);
1051 hTabDeleteAll (labelHash);
1052 freeTrace (&_G.labels);
1058 /*-----------------------------------------------------------------*/
1059 /* readFileIntoBuffer - reads a file into a string buffer */
1060 /*-----------------------------------------------------------------*/
1062 readFileIntoBuffer (char *fname)
1068 char lb[MAX_PATTERN_LEN];
1070 if (!(f = fopen (fname, "r")))
1072 fprintf (stderr, "cannot open peep rule file\n");
1076 while ((ch = fgetc (f)) != EOF)
1080 /* if we maxed out our local buffer */
1081 if (nch >= (MAX_PATTERN_LEN - 2))
1084 /* copy it into allocated buffer */
1087 rs = Safe_realloc (rs, strlen (rs) + strlen (lb) + 1);
1092 rs = Safe_alloc ( strlen (lb) + 1);
1099 /* if some charaters left over */
1103 /* copy it into allocated buffer */
1106 rs = Safe_realloc (rs, strlen (rs) + strlen (lb) + 1);
1111 rs = Safe_alloc ( strlen (lb) + 1);
1118 /*-----------------------------------------------------------------*/
1119 /* initPeepHole - initialises the peep hole optimizer stuff */
1120 /*-----------------------------------------------------------------*/
1126 /* read in the default rules */
1127 readRules (port->peep.default_rules);
1129 /* if we have any additional file read it too */
1130 if (options.peep_file)
1132 readRules (s = readFileIntoBuffer (options.peep_file));
1133 setToNull ((void **) &s);
1137 #if !OPT_DISABLE_PIC
1138 /* Convert the peep rules into pcode.
1139 NOTE: this is only support in the PIC port (at the moment)
1141 if (TARGET_IS_PIC) {
1142 peepRules2pCode(rootRules);