gbdk-releases/sdcc/src/SDCCpeeph.c
2015-01-10 16:25:09 +01:00

1124 lines
25 KiB
C

/*-------------------------------------------------------------------------
SDCCpeeph.c - The peep hole optimizer: for interpreting the
peep hole rules
Written By - Sandeep Dutta . sandeep.dutta@usa.net (1999)
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them. Help stamp out software-hoarding!
-------------------------------------------------------------------------*/
#include "common.h"
static peepRule *rootRules = NULL;
static peepRule *currRule = NULL;
#define HTAB_SIZE 53
typedef struct
{
char name[SDCC_NAME_MAX + 1];
int refCount;
}
labelHashEntry;
static hTab *labelHash = NULL;
static struct
{
allocTrace values;
allocTrace labels;
} _G;
static int hashSymbolName (const char *name);
static void buildLabelRefCountHash (lineNode * head);
static bool matchLine (char *, char *, hTab **);
#define FBYNAME(x) int x (hTab *vars, lineNode *currPl, lineNode *head, \
const char *cmdLine)
#if !OPT_DISABLE_PIC
void peepRules2pCode(peepRule *);
#endif
/*-----------------------------------------------------------------*/
/* pcDistance - afinds a label back ward or forward */
/*-----------------------------------------------------------------*/
int
pcDistance (lineNode * cpos, char *lbl, bool back)
{
lineNode *pl = cpos;
char buff[MAX_PATTERN_LEN];
int dist = 0;
sprintf (buff, "%s:", lbl);
while (pl)
{
if (pl->line &&
*pl->line != ';' &&
pl->line[strlen (pl->line) - 1] != ':' &&
!pl->isDebug)
dist++;
if (strncmp (pl->line, buff, strlen (buff)) == 0)
return dist;
if (back)
pl = pl->prev;
else
pl = pl->next;
}
return 0;
}
/*-----------------------------------------------------------------*/
/* flat24bitMode - will check to see if we are in flat24 mode */
/*-----------------------------------------------------------------*/
FBYNAME (flat24bitMode)
{
return (options.model == MODEL_FLAT24);
}
/*-----------------------------------------------------------------*/
/* labelInRange - will check to see if label %5 is within range */
/*-----------------------------------------------------------------*/
FBYNAME (labelInRange)
{
/* assumes that %5 pattern variable has the label name */
char *lbl = hTabItemWithKey (vars, 5);
int dist = 0;
if (!lbl)
return FALSE;
/* if the previous two instructions are "ljmp"s then don't
do it since it can be part of a jump table */
if (currPl->prev && currPl->prev->prev &&
strstr (currPl->prev->line, "ljmp") &&
strstr (currPl->prev->prev->line, "ljmp"))
return FALSE;
/* calculate the label distance : the jump for reladdr can be
+/- 127 bytes, here Iam assuming that an average 8051
instruction is 2 bytes long, so if the label is more than
63 intructions away, the label is considered out of range
for a relative jump. we could get more precise this will
suffice for now since it catches > 90% cases */
dist = (pcDistance (currPl, lbl, TRUE) +
pcDistance (currPl, lbl, FALSE));
/* if (!dist || dist > 45) has produced wrong sjmp */
/* 07-Sep-2000 Michael Schmitt */
/* FIX for Peephole 132 */
/* switch with lots of case can lead to a sjmp with a distance */
/* out of the range for sjmp */
if (!dist || dist > 43)
return FALSE;
return TRUE;
}
/*-----------------------------------------------------------------*/
/* operandsNotSame - check if %1 & %2 are the same */
/*-----------------------------------------------------------------*/
FBYNAME (operandsNotSame)
{
char *op1 = hTabItemWithKey (vars, 1);
char *op2 = hTabItemWithKey (vars, 2);
if (strcmp (op1, op2) == 0)
return FALSE;
else
return TRUE;
}
/* labelRefCount:
* takes two parameters: a variable (bound to a label name)
* and an expected reference count.
*
* Returns TRUE if that label is defined and referenced exactly
* the given number of times.
*/
FBYNAME (labelRefCount)
{
int varNumber, expectedRefCount;
bool rc = FALSE;
/* If we don't have the label hash table yet, build it. */
if (!labelHash)
{
buildLabelRefCountHash (head);
}
if (sscanf (cmdLine, "%*[ \t%]%d %d", &varNumber, &expectedRefCount) == 2)
{
char *label = hTabItemWithKey (vars, varNumber);
if (label)
{
labelHashEntry *entry;
entry = hTabFirstItemWK (labelHash, hashSymbolName (label));
while (entry)
{
if (!strcmp (label, entry->name))
{
break;
}
entry = hTabNextItemWK (labelHash);
}
if (entry)
{
#if 0
/* debug spew. */
fprintf (stderr, "labelRefCount: %s has refCount %d, want %d\n",
label, entry->refCount, expectedRefCount);
#endif
rc = (expectedRefCount == entry->refCount);
}
else
{
fprintf (stderr, "*** internal error: no label has entry for"
" %s in labelRefCount peephole.\n",
label);
}
}
else
{
fprintf (stderr, "*** internal error: var %d not bound"
" in peephole labelRefCount rule.\n",
varNumber);
}
}
else
{
fprintf (stderr,
"*** internal error: labelRefCount peephole restriction"
" malformed: %s\n", cmdLine);
}
return rc;
}
/*-----------------------------------------------------------------*/
/* callFuncByName - calls a function as defined in the table */
/*-----------------------------------------------------------------*/
int
callFuncByName (char *fname,
hTab * vars,
lineNode * currPl,
lineNode * head)
{
struct ftab
{
char *fname;
int (*func) (hTab *, lineNode *, lineNode *, const char *);
}
ftab[] =
{
{
"labelInRange", labelInRange
}
,
{
"operandsNotSame", operandsNotSame
}
,
{
"24bitMode", flat24bitMode
}
,
{
"labelRefCount", labelRefCount
}
,
};
int i;
for (i = 0; i < ((sizeof (ftab)) / (sizeof (struct ftab))); i++)
if (strncmp (ftab[i].fname, fname, strlen (ftab[i].fname)) == 0)
{
return (*ftab[i].func) (vars, currPl, head,
fname + strlen (ftab[i].fname));
}
fprintf (stderr, "could not find named function in function table\n");
return TRUE;
}
/*-----------------------------------------------------------------*/
/* printLine - prints a line chain into a given file */
/*-----------------------------------------------------------------*/
void
printLine (lineNode * head, FILE * of)
{
if (!of)
of = stdout;
while (head)
{
/* don't indent comments & labels */
if (head->line &&
(*head->line == ';' ||
head->line[strlen (head->line) - 1] == ':')) {
fprintf (of, "%s\n", head->line);
} else {
if (head->isInline && *head->line=='#') {
// comment out preprocessor directives in inline asm
fprintf (of, ";");
}
fprintf (of, "\t%s\n", head->line);
}
head = head->next;
}
}
/*-----------------------------------------------------------------*/
/* newPeepRule - creates a new peeprule and attach it to the root */
/*-----------------------------------------------------------------*/
peepRule *
newPeepRule (lineNode * match,
lineNode * replace,
char *cond,
int restart)
{
peepRule *pr;
pr = Safe_alloc ( sizeof (peepRule));
pr->match = match;
pr->replace = replace;
pr->restart = restart;
if (cond && *cond)
{
pr->cond = Safe_alloc ( strlen (cond) + 1);
strcpy (pr->cond, cond);
}
else
pr->cond = NULL;
pr->vars = newHashTable (100);
/* if root is empty */
if (!rootRules)
rootRules = currRule = pr;
else
currRule = currRule->next = pr;
return pr;
}
/*-----------------------------------------------------------------*/
/* newLineNode - creates a new peep line */
/*-----------------------------------------------------------------*/
lineNode *
newLineNode (char *line)
{
lineNode *pl;
pl = Safe_alloc ( sizeof (lineNode));
pl->line = Safe_alloc ( strlen (line) + 1);
strcpy (pl->line, line);
return pl;
}
/*-----------------------------------------------------------------*/
/* connectLine - connects two lines */
/*-----------------------------------------------------------------*/
lineNode *
connectLine (lineNode * pl1, lineNode * pl2)
{
if (!pl1 || !pl2)
{
fprintf (stderr, "trying to connect null line\n");
return NULL;
}
pl2->prev = pl1;
pl1->next = pl2;
return pl2;
}
#define SKIP_SPACE(x,y) { while (*x && (isspace(*x) || *x == '\n')) x++; \
if (!*x) { fprintf(stderr,y); return ; } }
#define EXPECT_STR(x,y,z) { while (*x && strncmp(x,y,strlen(y))) x++ ; \
if (!*x) { fprintf(stderr,z); return ; } }
#define EXPECT_CHR(x,y,z) { while (*x && *x != y) x++ ; \
if (!*x) { fprintf(stderr,z); return ; } }
/*-----------------------------------------------------------------*/
/* getPeepLine - parses the peep lines */
/*-----------------------------------------------------------------*/
static void
getPeepLine (lineNode ** head, char **bpp)
{
char lines[MAX_PATTERN_LEN];
char *lp;
lineNode *currL = NULL;
char *bp = *bpp;
while (1)
{
if (!*bp)
{
fprintf (stderr, "unexpected end of match pattern\n");
return;
}
if (*bp == '\n')
{
bp++;
while (isspace (*bp) ||
*bp == '\n')
bp++;
}
if (*bp == '}')
{
bp++;
break;
}
/* read till end of line */
lp = lines;
while ((*bp != '\n' && *bp != '}') && *bp)
*lp++ = *bp++;
*lp = '\0';
if (!currL)
*head = currL = newLineNode (lines);
else
currL = connectLine (currL, newLineNode (lines));
}
*bpp = bp;
}
/*-----------------------------------------------------------------*/
/* readRules - reads the rules from a string buffer */
/*-----------------------------------------------------------------*/
static void
readRules (char *bp)
{
char restart = 0;
char lines[MAX_PATTERN_LEN];
char *lp;
lineNode *match;
lineNode *replace;
lineNode *currL = NULL;
if (!bp)
return;
top:
restart = 0;
/* look for the token "replace" that is the
start of a rule */
while (*bp && strncmp (bp, "replace", 7))
bp++;
/* if not found */
if (!*bp)
return;
/* then look for either "restart" or '{' */
while (strncmp (bp, "restart", 7) &&
*bp != '{' && bp)
bp++;
/* not found */
if (!*bp)
{
fprintf (stderr, "expected 'restart' or '{'\n");
return;
}
/* if brace */
if (*bp == '{')
bp++;
else
{ /* must be restart */
restart++;
bp += strlen ("restart");
/* look for '{' */
EXPECT_CHR (bp, '{', "expected '{'\n");
bp++;
}
/* skip thru all the blank space */
SKIP_SPACE (bp, "unexpected end of rule\n");
match = replace = currL = NULL;
/* we are the start of a rule */
getPeepLine (&match, &bp);
/* now look for by */
EXPECT_STR (bp, "by", "expected 'by'\n");
/* then look for a '{' */
EXPECT_CHR (bp, '{', "expected '{'\n");
bp++;
SKIP_SPACE (bp, "unexpected end of rule\n");
getPeepLine (&replace, &bp);
/* look for a 'if' */
while ((isspace (*bp) || *bp == '\n') && *bp)
bp++;
if (strncmp (bp, "if", 2) == 0)
{
bp += 2;
while ((isspace (*bp) || *bp == '\n') && *bp)
bp++;
if (!*bp)
{
fprintf (stderr, "expected condition name\n");
return;
}
/* look for the condition */
lp = lines;
while (*bp && (*bp != '\n'))
{
*lp++ = *bp++;
}
*lp = '\0';
newPeepRule (match, replace, lines, restart);
}
else
newPeepRule (match, replace, NULL, restart);
goto top;
}
/*-----------------------------------------------------------------*/
/* keyForVar - returns the numeric key for a var */
/*-----------------------------------------------------------------*/
static int
keyForVar (char *d)
{
int i = 0;
while (isdigit (*d))
{
i *= 10;
i += (*d++ - '0');
}
return i;
}
/*-----------------------------------------------------------------*/
/* bindVar - binds a value to a variable in the given hashtable */
/*-----------------------------------------------------------------*/
static void
bindVar (int key, char **s, hTab ** vtab)
{
char vval[MAX_PATTERN_LEN];
char *vvx;
char *vv = vval;
/* first get the value of the variable */
vvx = *s;
/* the value is ended by a ',' or space or newline or null or ) */
while (*vvx &&
*vvx != ',' &&
!isspace (*vvx) &&
*vvx != '\n' &&
*vvx != ':' &&
*vvx != ')')
{
char ubb = 0;
/* if we find a '(' then we need to balance it */
if (*vvx == '(')
{
ubb++;
while (ubb)
{
*vv++ = *vvx++;
if (*vvx == '(')
ubb++;
if (*vvx == ')')
ubb--;
}
}
else
*vv++ = *vvx++;
}
*s = vvx;
*vv = '\0';
/* got value */
vvx = traceAlloc (&_G.values, Safe_alloc(strlen (vval) + 1));
strcpy (vvx, vval);
hTabAddItem (vtab, key, vvx);
}
/*-----------------------------------------------------------------*/
/* matchLine - matches one line */
/*-----------------------------------------------------------------*/
static bool
matchLine (char *s, char *d, hTab ** vars)
{
if (!s || !(*s))
return FALSE;
while (*s && *d)
{
/* skip white space in both */
while (isspace (*s))
s++;
while (isspace (*d))
d++;
/* if the destination is a var */
if (*d == '%' && isdigit (*(d + 1)))
{
char *v = hTabItemWithKey (*vars, keyForVar (d + 1));
/* if the variable is already bound
then it MUST match with dest */
if (v)
{
while (*v)
if (*v++ != *s++)
return FALSE;
}
else
/* variable not bound we need to
bind it */
bindVar (keyForVar (d + 1), &s, vars);
/* in either case go past the variable */
d++;
while (isdigit (*d))
d++;
}
/* they should be an exact match other wise */
if (*s && *d)
{
while (isspace (*s))
s++;
while (isspace (*d))
d++;
if (*s++ != *d++)
return FALSE;
}
}
/* get rid of the trailing spaces
in both source & destination */
if (*s)
while (isspace (*s))
s++;
if (*d)
while (isspace (*d))
d++;
/* after all this if only one of them
has something left over then no match */
if (*s || *d)
return FALSE;
return TRUE;
}
/*-----------------------------------------------------------------*/
/* matchRule - matches a all the rule lines */
/*-----------------------------------------------------------------*/
static bool
matchRule (lineNode * pl,
lineNode ** mtail,
peepRule * pr,
lineNode * head)
{
lineNode *spl; /* source pl */
lineNode *rpl; /* rule peep line */
/* setToNull((void **) &pr->vars); */
/* pr->vars = newHashTable(100); */
/* for all the lines defined in the rule */
rpl = pr->match;
spl = pl;
while (spl && rpl)
{
/* if the source line starts with a ';' then
comment line don't process or the source line
contains == . debugger information skip it */
if (spl->line &&
(*spl->line == ';' || spl->isDebug))
{
spl = spl->next;
continue;
}
if (!matchLine (spl->line, rpl->line, &pr->vars))
return FALSE;
rpl = rpl->next;
if (rpl)
spl = spl->next;
}
/* if rules ended */
if (!rpl)
{
/* if this rule has additional conditions */
if (pr->cond)
{
if (callFuncByName (pr->cond, pr->vars, pl, head))
{
*mtail = spl;
return TRUE;
}
else
return FALSE;
}
else
{
*mtail = spl;
return TRUE;
}
}
else
return FALSE;
}
/*-----------------------------------------------------------------*/
/* replaceRule - does replacement of a matching pattern */
/*-----------------------------------------------------------------*/
static void
replaceRule (lineNode ** shead, lineNode * stail, peepRule * pr)
{
lineNode *cl = NULL;
lineNode *pl = NULL, *lhead = NULL;
/* a long function name and long variable name can evaluate to
4x max pattern length e.g. "mov dptr,((fie_var>>8)<<8)+fie_var" */
char lb[MAX_PATTERN_LEN*4];
char *lbp;
lineNode *comment = NULL;
/* collect all the comment lines in the source */
for (cl = *shead; cl != stail; cl = cl->next)
{
if (cl->line && (*cl->line == ';' || cl->isDebug))
{
pl = (pl ? connectLine (pl, newLineNode (cl->line)) :
(comment = newLineNode (cl->line)));
pl->isDebug = cl->isDebug;
}
}
cl = NULL;
/* for all the lines in the replacement pattern do */
for (pl = pr->replace; pl; pl = pl->next)
{
char *v;
char *l;
lbp = lb;
l = pl->line;
while (*l)
{
/* if the line contains a variable */
if (*l == '%' && isdigit (*(l + 1)))
{
v = hTabItemWithKey (pr->vars, keyForVar (l + 1));
if (!v)
{
fprintf (stderr, "used unbound variable in replacement\n");
l++;
continue;
}
while (*v) {
*lbp++ = *v++;
}
l++;
while (isdigit (*l)) {
l++;
}
continue;
}
*lbp++ = *l++;
}
*lbp = '\0';
if (cl)
cl = connectLine (cl, newLineNode (lb));
else
lhead = cl = newLineNode (lb);
}
/* add the comments if any to the head of list */
if (comment)
{
lineNode *lc = comment;
while (lc->next)
lc = lc->next;
lc->next = lhead;
if (lhead)
lhead->prev = lc;
lhead = comment;
}
/* now we need to connect / replace the original chain */
/* if there is a prev then change it */
if ((*shead)->prev)
{
(*shead)->prev->next = lhead;
lhead->prev = (*shead)->prev;
}
else
*shead = lhead;
/* now for the tail */
if (stail && stail->next)
{
stail->next->prev = cl;
if (cl)
cl->next = stail->next;
}
}
/* Returns TRUE if this line is a label definition.
* If so, start will point to the start of the label name,
* and len will be it's length.
*/
bool
isLabelDefinition (const char *line, const char **start, int *len)
{
const char *cp = line;
/* This line is a label if if consists of:
* [optional whitespace] followed by identifier chars
* (alnum | $ | _ ) followed by a colon.
*/
while (*cp && isspace (*cp))
{
cp++;
}
if (!*cp)
{
return FALSE;
}
*start = cp;
while (isalnum (*cp) || (*cp == '$') || (*cp == '_'))
{
cp++;
}
if ((cp == *start) || (*cp != ':'))
{
return FALSE;
}
*len = (cp - (*start));
return TRUE;
}
/* Quick & dirty string hash function. */
static int
hashSymbolName (const char *name)
{
int hash = 0;
while (*name)
{
hash = (hash << 6) ^ *name;
name++;
}
if (hash < 0)
{
hash = -hash;
}
return hash % HTAB_SIZE;
}
/* Build a hash of all labels in the passed set of lines
* and how many times they are referenced.
*/
static void
buildLabelRefCountHash (lineNode * head)
{
lineNode *line;
const char *label;
int labelLen;
int i;
assert (labelHash == NULL);
labelHash = newHashTable (HTAB_SIZE);
/* First pass: locate all the labels. */
line = head;
while (line)
{
if (isLabelDefinition (line->line, &label, &labelLen)
&& labelLen <= SDCC_NAME_MAX)
{
labelHashEntry *entry;
entry = traceAlloc (&_G.labels, Safe_alloc(sizeof (labelHashEntry)));
memcpy (entry->name, label, labelLen);
entry->name[labelLen] = 0;
entry->refCount = -1;
hTabAddItem (&labelHash, hashSymbolName (entry->name), entry);
}
line = line->next;
}
/* Second pass: for each line, note all the referenced labels. */
/* This is ugly, O(N^2) stuff. Optimizations welcome... */
line = head;
while (line)
{
for (i = 0; i < HTAB_SIZE; i++)
{
labelHashEntry *thisEntry;
thisEntry = hTabFirstItemWK (labelHash, i);
while (thisEntry)
{
if (strstr (line->line, thisEntry->name))
{
thisEntry->refCount++;
}
thisEntry = hTabNextItemWK (labelHash);
}
}
line = line->next;
}
#if 0
/* Spew the contents of the table. Debugging fun only. */
for (i = 0; i < HTAB_SIZE; i++)
{
labelHashEntry *thisEntry;
thisEntry = hTabFirstItemWK (labelHash, i);
while (thisEntry)
{
fprintf (stderr, "label: %s ref %d\n",
thisEntry->name, thisEntry->refCount);
thisEntry = hTabNextItemWK (labelHash);
}
}
#endif
}
/* How does this work?
peepHole
For each rule,
For each line,
Try to match
If it matches,
replace and restart.
matchRule
matchLine
Where is stuff allocated?
*/
/*-----------------------------------------------------------------*/
/* peepHole - matches & substitutes rules */
/*-----------------------------------------------------------------*/
void
peepHole (lineNode ** pls)
{
lineNode *spl;
peepRule *pr;
lineNode *mtail = NULL;
bool restart;
assert(labelHash == NULL);
do
{
restart = FALSE;
/* for all rules */
for (pr = rootRules; pr; pr = pr->next)
{
for (spl = *pls; spl; spl = spl->next)
{
/* if inline assembler then no peep hole */
if (spl->isInline)
continue;
mtail = NULL;
/* Tidy up any data stored in the hTab */
/* if it matches */
if (matchRule (spl, &mtail, pr, *pls))
{
/* then replace */
if (spl == *pls)
replaceRule (pls, mtail, pr);
else
replaceRule (&spl, mtail, pr);
/* if restart rule type then
start at the top again */
if (pr->restart)
{
restart = TRUE;
}
}
if (pr->vars)
{
hTabDeleteAll (pr->vars);
Safe_free (pr->vars);
pr->vars = NULL;
}
freeTrace (&_G.values);
}
}
} while (restart == TRUE);
if (labelHash)
{
hTabDeleteAll (labelHash);
freeTrace (&_G.labels);
}
labelHash = NULL;
}
/*-----------------------------------------------------------------*/
/* readFileIntoBuffer - reads a file into a string buffer */
/*-----------------------------------------------------------------*/
static char *
readFileIntoBuffer (char *fname)
{
FILE *f;
char *rs = NULL;
int nch = 0;
int ch;
char lb[MAX_PATTERN_LEN];
if (!(f = fopen (fname, "r")))
{
fprintf (stderr, "cannot open peep rule file\n");
return NULL;
}
while ((ch = fgetc (f)) != EOF)
{
lb[nch++] = ch;
/* if we maxed out our local buffer */
if (nch >= (MAX_PATTERN_LEN - 2))
{
lb[nch] = '\0';
/* copy it into allocated buffer */
if (rs)
{
rs = Safe_realloc (rs, strlen (rs) + strlen (lb) + 1);
strcat (rs, lb);
}
else
{
rs = Safe_alloc ( strlen (lb) + 1);
strcpy (rs, lb);
}
nch = 0;
}
}
/* if some charaters left over */
if (nch)
{
lb[nch] = '\0';
/* copy it into allocated buffer */
if (rs)
{
rs = Safe_realloc (rs, strlen (rs) + strlen (lb) + 1);
strcat (rs, lb);
}
else
{
rs = Safe_alloc ( strlen (lb) + 1);
strcpy (rs, lb);
}
}
return rs;
}
/*-----------------------------------------------------------------*/
/* initPeepHole - initialises the peep hole optimizer stuff */
/*-----------------------------------------------------------------*/
void
initPeepHole ()
{
char *s;
/* read in the default rules */
readRules (port->peep.default_rules);
/* if we have any additional file read it too */
if (options.peep_file)
{
readRules (s = readFileIntoBuffer (options.peep_file));
setToNull ((void **) &s);
}
#if !OPT_DISABLE_PIC
/* Convert the peep rules into pcode.
NOTE: this is only support in the PIC port (at the moment)
*/
if (TARGET_IS_PIC) {
peepRules2pCode(rootRules);
}
#endif
}