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"
29 peepRule *rootRules = NULL;
30 peepRule *currRule = NULL;
35 char name[SDCC_NAME_MAX + 1];
39 hTab *labelHash = NULL;
41 static int hashSymbolName(const char *name);
42 static void buildLabelRefCountHash(lineNode *head);
44 static bool matchLine (char *, char *, hTab **);
46 #define FBYNAME(x) int x (hTab *vars, lineNode *currPl, lineNode *head, \
49 /*-----------------------------------------------------------------*/
50 /* pcDistance - afinds a label back ward or forward */
51 /*-----------------------------------------------------------------*/
52 int pcDistance (lineNode *cpos, char *lbl, bool back)
55 char buff[MAX_PATTERN_LEN];
58 sprintf(buff,"%s:",lbl);
63 pl->line[strlen(pl->line)-1] != ':' &&
68 if (strncmp(pl->line,buff,strlen(buff)) == 0)
80 /*-----------------------------------------------------------------*/
81 /* flat24bitMode - will check to see if we are in flat24 mode */
82 /*-----------------------------------------------------------------*/
83 FBYNAME(flat24bitMode)
85 return (options.model == MODEL_FLAT24);
88 /*-----------------------------------------------------------------*/
89 /* labelInRange - will check to see if label %5 is within range */
90 /*-----------------------------------------------------------------*/
93 /* assumes that %5 pattern variable has the label name */
94 char *lbl = hTabItemWithKey(vars,5);
100 /* if the previous teo instructions are "ljmp"s then don't
101 do it since it can be part of a jump table */
102 if (currPl->prev && currPl->prev->prev &&
103 strstr(currPl->prev->line,"ljmp") &&
104 strstr(currPl->prev->prev->line,"ljmp"))
107 /* calculate the label distance : the jump for reladdr can be
108 +/- 127 bytes, here Iam assuming that an average 8051
109 instruction is 2 bytes long, so if the label is more than
110 63 intructions away, the label is considered out of range
111 for a relative jump. we could get more precise this will
112 suffice for now since it catches > 90% cases */
113 dist = (pcDistance(currPl,lbl,TRUE) +
114 pcDistance(currPl,lbl,FALSE)) ;
116 /* if (!dist || dist > 45) has produced wrong sjmp */
117 /* 07-Sep-2000 Michael Schmitt */
118 /* FIX for Peephole 132 */
119 /* switch with lots of case can lead to a sjmp with a distance */
120 /* out of the range for sjmp */
121 if (!dist || dist > 43)
127 /*-----------------------------------------------------------------*/
128 /* operandsNotSame - check if %1 & %2 are the same */
129 /*-----------------------------------------------------------------*/
130 FBYNAME(operandsNotSame)
132 char *op1 = hTabItemWithKey(vars,1);
133 char *op2 = hTabItemWithKey(vars,2);
135 if (strcmp(op1,op2) == 0)
143 * takes two parameters: a variable (bound to a label name)
144 * and an expected reference count.
146 * Returns TRUE if that label is defined and referenced exactly
147 * the given number of times.
149 FBYNAME(labelRefCount)
151 int varNumber, expectedRefCount;
154 /* If we don't have the label hash table yet, build it. */
157 buildLabelRefCountHash(head);
160 if (sscanf(cmdLine, "%*[ \t%]%d %d", &varNumber, &expectedRefCount) == 2)
162 char *label = hTabItemWithKey(vars, varNumber);
166 labelHashEntry *entry;
168 entry = hTabFirstItemWK(labelHash, hashSymbolName(label));
172 if (!strcmp(label, entry->name))
176 entry = hTabNextItemWK(labelHash);
182 fprintf(stderr, "labelRefCount: %s has refCount %d, want %d\n",
183 label, entry->refCount, expectedRefCount);
186 rc = (expectedRefCount == entry->refCount);
190 fprintf(stderr, "*** internal error: no label has entry for"
191 " %s in labelRefCount peephole.\n",
197 fprintf(stderr, "*** internal error: var %d not bound"
198 " in peephole labelRefCount rule.\n",
206 "*** internal error: labelRefCount peephole restriction"
207 " malformed: %s\n", cmdLine);
212 /*-----------------------------------------------------------------*/
213 /* callFuncByName - calls a function as defined in the table */
214 /*-----------------------------------------------------------------*/
215 int callFuncByName ( char *fname,
223 int (*func)(hTab *,lineNode *,lineNode *,const char *) ;
226 {"labelInRange", labelInRange },
227 {"operandsNotSame", operandsNotSame },
228 {"24bitMode", flat24bitMode },
229 {"labelRefCount", labelRefCount },
233 for ( i = 0 ; i < ((sizeof (ftab))/(sizeof(struct ftab))); i++)
234 if (strncmp(ftab[i].fname,fname,strlen(ftab[i].fname)) == 0)
236 return (*ftab[i].func)(vars,currPl,head,
237 fname + strlen(ftab[i].fname));
239 fprintf(stderr,"could not find named function in function table\n");
243 /*-----------------------------------------------------------------*/
244 /* printLine - prints a line chain into a given file */
245 /*-----------------------------------------------------------------*/
246 void printLine (lineNode *head, FILE *of)
252 /* don't indent comments & labels */
254 ( *head->line == ';' ||
255 head->line[strlen(head->line)-1] == ':'))
256 fprintf(of,"%s\n",head->line);
258 fprintf(of,"\t%s\n",head->line);
263 /*-----------------------------------------------------------------*/
264 /* newPeepRule - creates a new peeprule and attach it to the root */
265 /*-----------------------------------------------------------------*/
266 peepRule *newPeepRule (lineNode *match ,
273 ALLOC(pr,sizeof(peepRule));
275 pr->replace= replace;
276 pr->restart = restart;
279 ALLOC(pr->cond,strlen(cond)+1);
280 strcpy(pr->cond,cond);
284 pr->vars = newHashTable(100);
286 /* if root is empty */
288 rootRules = currRule = pr;
290 currRule = currRule->next = pr;
295 /*-----------------------------------------------------------------*/
296 /* newLineNode - creates a new peep line */
297 /*-----------------------------------------------------------------*/
298 lineNode *newLineNode (char *line)
302 ALLOC(pl,sizeof(lineNode));
303 ALLOC(pl->line,strlen(line)+1);
304 strcpy(pl->line,line);
308 /*-----------------------------------------------------------------*/
309 /* connectLine - connects two lines */
310 /*-----------------------------------------------------------------*/
311 lineNode *connectLine (lineNode *pl1, lineNode *pl2)
314 fprintf (stderr,"trying to connect null line\n");
324 #define SKIP_SPACE(x,y) { while (*x && (isspace(*x) || *x == '\n')) x++; \
325 if (!*x) { fprintf(stderr,y); return ; } }
327 #define EXPECT_STR(x,y,z) { while (*x && strncmp(x,y,strlen(y))) x++ ; \
328 if (!*x) { fprintf(stderr,z); return ; } }
329 #define EXPECT_CHR(x,y,z) { while (*x && *x != y) x++ ; \
330 if (!*x) { fprintf(stderr,z); return ; } }
332 /*-----------------------------------------------------------------*/
333 /* getPeepLine - parses the peep lines */
334 /*-----------------------------------------------------------------*/
335 static void getPeepLine (lineNode **head, char **bpp)
337 char lines[MAX_PATTERN_LEN];
340 lineNode *currL = NULL ;
345 fprintf(stderr,"unexpected end of match pattern\n");
351 while (isspace(*bp) ||
360 /* read till end of line */
362 while ((*bp != '\n' && *bp != '}' ) && *bp)
367 *head = currL = newLineNode (lines);
369 currL = connectLine(currL,newLineNode(lines));
375 /*-----------------------------------------------------------------*/
376 /* readRules - reads the rules from a string buffer */
377 /*-----------------------------------------------------------------*/
378 static void readRules (char *bp)
381 char lines[MAX_PATTERN_LEN];
385 lineNode *currL = NULL;
391 /* look for the token "replace" that is the
393 while (*bp && strncmp(bp,"replace",7)) bp++;
399 /* then look for either "restart" or '{' */
400 while (strncmp(bp,"restart",7) &&
401 *bp != '{' && bp ) bp++ ;
405 fprintf(stderr,"expected 'restart' or '{'\n");
412 else { /* must be restart */
414 bp += strlen("restart");
416 EXPECT_CHR(bp,'{',"expected '{'\n");
420 /* skip thru all the blank space */
421 SKIP_SPACE(bp,"unexpected end of rule\n");
423 match = replace = currL = NULL ;
424 /* we are the start of a rule */
425 getPeepLine(&match, &bp);
427 /* now look for by */
428 EXPECT_STR(bp,"by","expected 'by'\n");
430 /* then look for a '{' */
431 EXPECT_CHR(bp,'{',"expected '{'\n");
434 SKIP_SPACE(bp,"unexpected end of rule\n");
435 getPeepLine (&replace, &bp);
437 /* look for a 'if' */
438 while ((isspace(*bp) || *bp == '\n') && *bp) bp++;
440 if (strncmp(bp,"if",2) == 0) {
442 while ((isspace(*bp) || *bp == '\n') && *bp) bp++;
444 fprintf(stderr,"expected condition name\n");
448 /* look for the condition */
450 while (*bp && (*bp != '\n')) {
455 newPeepRule(match,replace,lines,restart);
457 newPeepRule(match,replace,NULL,restart);
462 /*-----------------------------------------------------------------*/
463 /* keyForVar - returns the numeric key for a var */
464 /*-----------------------------------------------------------------*/
465 static int keyForVar (char *d)
469 while (isdigit(*d)) {
477 /*-----------------------------------------------------------------*/
478 /* bindVar - binds a value to a variable in the given hashtable */
479 /*-----------------------------------------------------------------*/
480 static void bindVar (int key, char **s, hTab **vtab)
482 char vval[MAX_PATTERN_LEN];
486 /* first get the value of the variable */
488 /* the value is ended by a ',' or space or newline or null */
495 /* if we find a '(' then we need to balance it */
500 if (*vvx == '(') ubb++;
501 if (*vvx == ')') ubb--;
509 ALLOC(vvx,strlen(vval)+1);
511 hTabAddItem(vtab,key,vvx);
515 /*-----------------------------------------------------------------*/
516 /* matchLine - matches one line */
517 /*-----------------------------------------------------------------*/
518 static bool matchLine (char *s, char *d, hTab **vars)
526 /* skip white space in both */
527 while (isspace(*s)) s++;
528 while (isspace(*d)) d++;
530 /* if the destination is a var */
531 if (*d == '%' && isdigit(*(d+1))) {
532 char *v = hTabItemWithKey(*vars,keyForVar(d+1));
533 /* if the variable is already bound
534 then it MUST match with dest */
537 if (*v++ != *s++) return FALSE;
539 /* variable not bound we need to
541 bindVar (keyForVar(d+1),&s,vars);
543 /* in either case go past the variable */
545 while (isdigit(*d)) d++;
548 /* they should be an exact match other wise */
550 while (isspace(*s))s++;
551 while (isspace(*d))d++;
558 /* get rid of the trailing spaces
559 in both source & destination */
561 while (isspace(*s)) s++;
564 while (isspace(*d)) d++;
566 /* after all this if only one of them
567 has something left over then no match */
574 /*-----------------------------------------------------------------*/
575 /* matchRule - matches a all the rule lines */
576 /*-----------------------------------------------------------------*/
577 static bool matchRule (lineNode *pl,
582 lineNode *spl ; /* source pl */
583 lineNode *rpl ; /* rule peep line */
585 hTabClearAll(pr->vars);
586 /* setToNull((void **) &pr->vars); */
587 /* pr->vars = newHashTable(100); */
589 /* for all the lines defined in the rule */
594 /* if the source line starts with a ';' then
595 comment line don't process or the source line
596 contains == . debugger information skip it */
598 (*spl->line == ';' || spl->isDebug)) {
603 if (!matchLine(spl->line,rpl->line,&pr->vars))
613 /* if this rule has additional conditions */
615 if (callFuncByName (pr->cond, pr->vars,pl,head) ) {
629 /*-----------------------------------------------------------------*/
630 /* replaceRule - does replacement of a matching pattern */
631 /*-----------------------------------------------------------------*/
632 static void replaceRule (lineNode **shead, lineNode *stail, peepRule *pr)
635 lineNode *pl = NULL , *lhead = NULL;
636 char lb[MAX_PATTERN_LEN];
638 lineNode *comment = NULL;
640 /* collect all the comment lines in the source */
641 for (cl = *shead ; cl != stail ; cl = cl->next) {
642 if (cl->line && ( *cl->line == ';' || cl->isDebug)) {
643 pl = (pl ? connectLine (pl,newLineNode(cl->line)) :
644 (comment = newLineNode(cl->line)));
645 pl->isDebug = cl->isDebug;
650 /* for all the lines in the replacement pattern do */
651 for ( pl = pr->replace ; pl ; pl = pl->next ) {
658 /* if the line contains a variable */
659 if (*l == '%' && isdigit(*(l+1))) {
660 v = hTabItemWithKey(pr->vars,keyForVar(l+1));
662 fprintf(stderr,"used unbound variable in replacement\n");
669 while (isdigit(*l)) l++;
677 cl = connectLine(cl,newLineNode(lb));
679 lhead = cl = newLineNode(lb);
682 /* add the comments if any to the head of list */
684 lineNode *lc = comment;
685 while (lc->next) lc = lc->next;
692 /* now we need to connect / replace the original chain */
693 /* if there is a prev then change it */
694 if ((*shead)->prev) {
695 (*shead)->prev->next = lhead;
696 lhead->prev = (*shead)->prev;
699 /* now for the tail */
700 if (stail && stail->next) {
701 stail->next->prev = cl;
703 cl->next = stail->next;
707 /* Returns TRUE if this line is a label definition.
709 * If so, start will point to the start of the label name,
710 * and len will be it's length.
712 bool isLabelDefinition(const char *line, const char **start, int *len)
714 const char *cp = line;
716 /* This line is a label if if consists of:
717 * [optional whitespace] followed by identifier chars
718 * (alnum | $ | _ ) followed by a colon.
721 while (*cp && isspace(*cp))
733 while (isalnum(*cp) || (*cp == '$') || (*cp == '_'))
738 if ((cp == *start) || (*cp != ':'))
743 *len = (cp - (*start));
747 /* Quick & dirty string hash function. */
748 static int hashSymbolName(const char *name)
754 hash = (hash << 6) ^ *name;
763 return hash % HTAB_SIZE;
766 /* Build a hash of all labels in the passed set of lines
767 * and how many times they are referenced.
769 static void buildLabelRefCountHash(lineNode *head)
776 assert(labelHash == NULL);
777 labelHash = newHashTable(HTAB_SIZE);
779 /* First pass: locate all the labels. */
784 if (isLabelDefinition(line->line, &label, &labelLen)
785 && labelLen <= SDCC_NAME_MAX)
787 labelHashEntry *entry;
789 ALLOC(entry, sizeof(labelHashEntry));
791 assert(entry != NULL);
793 memcpy(entry->name, label, labelLen);
794 entry->name[labelLen] = 0;
795 entry->refCount = -1;
797 hTabAddItem(&labelHash, hashSymbolName(entry->name), entry);
803 /* Second pass: for each line, note all the referenced labels. */
804 /* This is ugly, O(N^2) stuff. Optimizations welcome... */
808 for (i = 0; i < HTAB_SIZE; i++)
810 labelHashEntry *thisEntry;
812 thisEntry = hTabFirstItemWK(labelHash, i);
816 if (strstr(line->line, thisEntry->name))
818 thisEntry->refCount++;
820 thisEntry = hTabNextItemWK(labelHash);
827 /* Spew the contents of the table. Debugging fun only. */
828 for (i = 0; i < HTAB_SIZE; i++)
830 labelHashEntry *thisEntry;
832 thisEntry = hTabFirstItemWK(labelHash, i);
836 fprintf(stderr, "label: %s ref %d\n",
837 thisEntry->name, thisEntry->refCount);
838 thisEntry = hTabNextItemWK(labelHash);
844 /*-----------------------------------------------------------------*/
845 /* peepHole - matches & substitutes rules */
846 /*-----------------------------------------------------------------*/
847 void peepHole (lineNode **pls )
851 lineNode *mtail = NULL;
855 hTabDeleteAll(labelHash);
861 for (pr = rootRules ; pr ; pr = pr->next ) {
863 for (spl = *pls ; spl ; spl = spl->next ) {
865 /* if inline assembler then no peep hole */
872 if (matchRule (spl,&mtail,pr, *pls)) {
876 replaceRule(pls, mtail, pr);
878 replaceRule (&spl, mtail,pr);
880 /* if restart rule type then
881 start at the top again */
890 /*-----------------------------------------------------------------*/
891 /* readFileIntoBuffer - reads a file into a string buffer */
892 /*-----------------------------------------------------------------*/
893 static char *readFileIntoBuffer (char *fname)
899 char lb[MAX_PATTERN_LEN];
901 if (!(f = fopen(fname,"r"))) {
902 fprintf(stderr,"cannot open peep rule file\n");
906 while ((ch = fgetc(f)) != EOF) {
909 /* if we maxed out our local buffer */
910 if (nch >= (MAX_PATTERN_LEN - 2)) {
912 /* copy it into allocated buffer */
914 rs = realloc(rs,strlen(rs)+strlen(lb)+1);
917 ALLOC(rs,strlen(lb)+1);
924 /* if some charaters left over */
927 /* copy it into allocated buffer */
929 rs = realloc(rs,strlen(rs)+strlen(lb)+1);
932 ALLOC(rs,strlen(lb)+1);
939 /*-----------------------------------------------------------------*/
940 /* initPeepHole - initiaises the peep hole optimizer stuff */
941 /*-----------------------------------------------------------------*/
946 /* read in the default rules */
947 readRules(port->peep.default_rules);
949 /* if we have any additional file read it too */
950 if (options.peep_file) {
951 readRules(s=readFileIntoBuffer(options.peep_file));
952 setToNull((void **) &s);