sdcc-gas/src/SDCCpeeph.c

2894 lines
76 KiB
C

/*-------------------------------------------------------------------------
SDCCpeeph.c - The peep hole optimizer: for interpreting the
peep hole rules
Copyright (C) 1999, Sandeep Dutta . sandeep.dutta@usa.net
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.
-------------------------------------------------------------------------*/
#include "common.h"
#include "dbuf_string.h"
#define ISCHARDIGIT(c) isdigit((unsigned char)c)
#define ISCHARSPACE(c) isspace((unsigned char)c)
#define ISCHARALNUM(c) isalnum((unsigned char)c)
static peepRule *rootRules = NULL;
static peepRule *currRule = NULL;
#define HTAB_SIZE 53
hTab *labelHash = NULL;
static struct
{
allocTrace values;
allocTrace labels;
} _G;
static int hashSymbolName (const char *name);
static void buildLabelRefCountHash (lineNode * head);
static void bindVar (int key, char **s, hTab ** vtab);
static bool matchLine (char *, const char *, hTab **);
#define FBYNAME(x) static int x (hTab *vars, lineNode *currPl, lineNode *endPl, \
lineNode *head, char *cmdLine)
#if !OPT_DISABLE_PIC14
void peepRules2pCode(peepRule *);
#endif
#if !OPT_DISABLE_PIC16
void pic16_peepRules2pCode(peepRule *);
#endif
/*-----------------------------------------------------------------*/
/* getPatternVar - finds a pattern variable */
/*-----------------------------------------------------------------*/
static char*
getPatternVar (hTab *vars, char **cmdLine)
{
int varNumber;
char *digitend;
if (!cmdLine || !*cmdLine || !**cmdLine)
return NULL; /* no parameters given */
while (**cmdLine && ISCHARSPACE(**cmdLine))
(*cmdLine)++; /* skip whitespace */
if (**cmdLine != '%')
goto error;
(*cmdLine)++;
if (!ISCHARDIGIT (**cmdLine))
goto error;
varNumber = strtol (*cmdLine, &digitend, 10);
*cmdLine = digitend;
return hTabItemWithKey (vars, varNumber);
error:
fprintf (stderr,
"*** internal error: peephole restriction malformed: %s\n", *cmdLine);
return NULL;
}
/*-----------------------------------------------------------------*/
/* pcDistance - finds a label backward or forward */
/*-----------------------------------------------------------------*/
static int
pcDistance (lineNode *cpos, char *lbl, bool back)
{
lineNode *pl = cpos;
char buff[MAX_PATTERN_LEN];
int dist = 0;
SNPRINTF (buff, sizeof(buff) - 1, "%s:", lbl);
while (pl)
{
if (pl->line &&
!pl->isComment &&
!pl->isLabel &&
!pl->isDebug)
{
if (port->peep.getSize)
{
dist += port->peep.getSize(pl);
#if 0
fprintf(stderr, "Line: %s, dist: %i, total: %i\n", pl->line, port->peep.getSize(pl), dist);
#endif
}
else
{
dist += 4; // maximum instruction size
}
}
if (strncmp (pl->line, buff, strlen (buff)) == 0)
return dist;
if (back)
pl = pl->prev;
else
pl = pl->next;
}
return 0;
}
/*-----------------------------------------------------------------*/
/* portIsDS390 - return true if port is DS390 */
/*-----------------------------------------------------------------*/
FBYNAME (portIsDS390)
{
return ((strcmp(port->target,"ds390") == 0) ||
(strcmp(port->target,"ds400") == 0));
}
/*-----------------------------------------------------------------*/
/* flat24bitMode - will check to see if we are in flat24 mode */
/*-----------------------------------------------------------------*/
FBYNAME (flat24bitMode)
{
return (options.model == MODEL_FLAT24);
}
/*-----------------------------------------------------------------*/
/* xramMovcOption - check if using movc to read xram */
/*-----------------------------------------------------------------*/
FBYNAME (xramMovcOption)
{
return (options.xram_movc && (strcmp(port->target,"mcs51") == 0));
}
/*-----------------------------------------------------------------*/
/* useAcallAjmp - Enable replacement of lcall/ljmp with acall/ajmp */
/*-----------------------------------------------------------------*/
FBYNAME (useAcallAjmp)
{
return (options.acall_ajmp && (strcmp(port->target,"mcs51") == 0));
}
/*-----------------------------------------------------------------*/
/* labelInRange - will check to see if label is within range */
/*-----------------------------------------------------------------*/
FBYNAME (labelInRange)
{
int dist = 0;
char *lbl = getPatternVar (vars, &cmdLine);
if (!lbl)
{
fprintf (stderr,
"*** internal error: labelInRange peephole restriction"
" malformed: %s\n", cmdLine);
/* If no parameters given, assume that %5 pattern variable
has the label name for backward compatibility */
lbl = hTabItemWithKey (vars, 5);
}
if (!lbl)
return FALSE;
do
{
/* Don't optimize jumps in a jump table; a more generic test */
if (currPl->ic && currPl->ic->op == JUMPTABLE)
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. For mcs51 the jump can be
-127 to + 127 bytes, for Z80 -126 to +129 bytes.*/
dist = (pcDistance (currPl, lbl, TRUE) +
pcDistance (currPl, lbl, FALSE));
/* Use 125 for now. Could be made more exact using port and
exact jump location instead of currPl. */
if (!dist || dist > 127)
return FALSE;
lbl = getPatternVar (vars, &cmdLine);
}
while (lbl);
return TRUE;
}
/*-----------------------------------------------------------------*/
/* labelJTInRange - will check to see if label %5 and up are */
/* within range. */
/* Specifically meant to optimize long (3-byte) jumps to short */
/* (2-byte) jumps in jumptables */
/*-----------------------------------------------------------------*/
FBYNAME (labelJTInRange)
{
char *lbl;
int dist, count, i;
/* Only optimize within a jump table */
if (currPl->ic && currPl->ic->op != JUMPTABLE)
return FALSE;
count = elementsInSet( IC_JTLABELS (currPl->ic) );
/* check all labels (this is needed if the case statements are unsorted) */
for (i=0; i<count; i++)
{
/* assumes that the %5 pattern variable has the first ljmp label */
lbl = hTabItemWithKey (vars, 5+i);
if (!lbl)
return FALSE;
dist = (pcDistance (currPl, lbl, TRUE) +
pcDistance (currPl, lbl, FALSE));
/* three terms used to calculate allowable distance */
/* Could be made more exact and port-specific. */
if (!dist ||
dist > 127+ /* range of sjmp */
(3+3*i)+ /* offset between this jump and currPl,
should use pcDistance instead? */
(count-i-1) /* if peephole applies distance is shortened */
)
return FALSE;
}
return TRUE;
}
/*-----------------------------------------------------------------*/
/* optimizeReturn - is it allowed to optimize RET instructions */
/*-----------------------------------------------------------------*/
FBYNAME (optimizeReturn)
{
return (options.peepReturn >= 0);
}
/*-----------------------------------------------------------------*/
/* labelIsReturnOnly - Check if label is followed by ret */
/*-----------------------------------------------------------------*/
FBYNAME (labelIsReturnOnly)
{
/* assumes that %5 pattern variable has the label name */
const char *label, *p;
const lineNode *pl;
int len;
char * retInst;
/* Don't optimize jumps in a jump table; a more generic test */
if (currPl->ic && currPl->ic->op == JUMPTABLE)
return FALSE;
if (!(label = getPatternVar (vars, &cmdLine)))
{
fprintf (stderr,
"*** internal error: labelIsReturnOnly peephole restriction"
" malformed: %s\n", cmdLine);
/* If no parameters given, assume that %5 pattern variable
has the label name for backward compatibility */
label = hTabItemWithKey (vars, 5);
}
if (!label)
return FALSE;
len = strlen(label);
for(pl = currPl; pl; pl = pl->next)
{
if (pl->line && !pl->isDebug && !pl->isComment && pl->isLabel)
{
if (strncmp(pl->line, label, len) == 0)
break; /* Found Label */
if (strlen(pl->line) != 7 || !ISCHARDIGIT(*(pl->line)) ||
!ISCHARDIGIT(*(pl->line+1)) || !ISCHARDIGIT(*(pl->line+2)) ||
!ISCHARDIGIT(*(pl->line+3)) || !ISCHARDIGIT(*(pl->line+4)) ||
*(pl->line+5) != '$')
{
return FALSE; /* non-local label encountered */
}
}
}
if (!pl)
return FALSE; /* did not find the label */
pl = pl->next;
while (pl && (pl->isDebug || pl->isComment || pl->isLabel))
pl = pl->next;
if (!pl || !pl->line || pl->isDebug)
return FALSE; /* next line not valid */
p = pl->line;
for (p = pl->line; *p && ISCHARSPACE(*p); p++)
;
retInst = "ret";
if (TARGET_HC08_LIKE)
retInst = "rts";
if (strcmp(p, retInst) == 0)
return TRUE;
return FALSE;
}
/*-----------------------------------------------------------------*/
/* labelIsUncondJump - Check if label %5 is followed by an */
/* unconditional jump and put the destination of that jump in %6 */
/*-----------------------------------------------------------------*/
FBYNAME (labelIsUncondJump)
{
/* assumes that %5 pattern variable has the label name */
const char *label;
char *p, *q;
const lineNode *pl;
bool found = FALSE;
int len;
char * jpInst = NULL;
char * jpInst2 = NULL;
label = hTabItemWithKey (vars, 5);
if (!label)
return FALSE;
len = strlen(label);
for (pl = currPl; pl; pl = pl->prev)
{
if (pl->line && !pl->isDebug && !pl->isComment && pl->isLabel)
{
if (strncmp(pl->line, label, len) == 0)
{
found = TRUE;
break; /* Found Label */
}
if (strlen(pl->line) != 7 || !ISCHARDIGIT(*(pl->line)) ||
!ISCHARDIGIT(*(pl->line+1)) || !ISCHARDIGIT(*(pl->line+2)) ||
!ISCHARDIGIT(*(pl->line+3)) || !ISCHARDIGIT(*(pl->line+4)) ||
*(pl->line+5) != '$')
{
break; /* non-local label encountered */
}
}
}
if (!found)
{
for (pl = currPl; pl; pl = pl->next)
{
if (pl->line && !pl->isDebug && !pl->isComment && pl->isLabel)
{
if (strncmp(pl->line, label, len) == 0)
{
found = TRUE;
break; /* Found Label */
}
if (strlen(pl->line) != 7 || !ISCHARDIGIT(*(pl->line)) ||
!ISCHARDIGIT(*(pl->line+1)) || !ISCHARDIGIT(*(pl->line+2)) ||
!ISCHARDIGIT(*(pl->line+3)) || !ISCHARDIGIT(*(pl->line+4)) ||
*(pl->line+5) != '$')
{
return FALSE; /* non-local label encountered */
}
}
}
}
if (!pl || !found)
return FALSE; /* did not find the label */
pl = pl->next;
while (pl && (pl->isDebug || pl->isComment))
pl = pl->next;
if (!pl || !pl->line)
return FALSE; /* next line not valid */
p = pl->line;
while (*p && ISCHARSPACE(*p))
p++;
if (TARGET_MCS51_LIKE)
{
jpInst = "ljmp";
jpInst2 = "sjmp";
}
else if (TARGET_HC08_LIKE)
{
jpInst = "jmp";
jpInst2 = "bra";
}
else if (TARGET_Z80_LIKE)
{
jpInst = "jp";
jpInst2 = "jr";
}
else if (TARGET_IS_STM8)
{
jpInst = (options.model == MODEL_LARGE ? "jpf" : "jp");
jpInst2 = "jra";
}
else if (TARGET_PDK_LIKE)
{
jpInst = "goto";
}
len = strlen(jpInst);
if (strncmp(p, jpInst, len))
{
len = jpInst2 ? strlen(jpInst2) : 0;
if(!jpInst2 || strncmp(p, jpInst2, len))
return FALSE; /* next line is no jump */
}
p += len;
while (*p && ISCHARSPACE(*p))
p++;
q = p;
while (*q && *q!=';')
q++;
while (q>p && ISCHARSPACE(*q))
q--;
len = q-p;
if (len == 0)
return FALSE; /* no destination? */
if (TARGET_Z80_LIKE)
{
while (q>p && *q!=',')
q--;
if (*q==',')
return FALSE; /* conditional jump */
}
/* now put the destination in %6 */
bindVar (6, &p, &vars);
return TRUE;
}
/*-----------------------------------------------------------------*/
/* okToRemoveSLOC - Check if label %1 is a SLOC and not other */
/* usage of it in the code depends on a value from this section */
/*-----------------------------------------------------------------*/
FBYNAME (okToRemoveSLOC)
{
const lineNode *pl;
const char *sloc, *p;
int dummy1, dummy2, dummy3;
/* assumes that %1 as the SLOC name */
sloc = hTabItemWithKey (vars, 1);
if (sloc == NULL) return FALSE;
p = strstr(sloc, "sloc");
if (p == NULL) return FALSE;
p += 4;
if (sscanf(p, "%d_%d_%d", &dummy1, &dummy2, &dummy3) != 3) return FALSE;
/*TODO: ultra-paranoid: get funtion name from "head" and check that */
/* the sloc name begins with that. Probably not really necessary */
/* Look for any occurance of this SLOC before the peephole match */
for (pl = currPl->prev; pl; pl = pl->prev) {
if (pl->line && !pl->isDebug && !pl->isComment
&& *pl->line != ';' && strstr(pl->line, sloc))
return FALSE;
}
/* Look for any occurance of this SLOC after the peephole match */
for (pl = endPl->next; pl; pl = pl->next) {
if (pl->line && !pl->isDebug && !pl->isComment
&& *pl->line != ';' && strstr(pl->line, sloc))
return FALSE;
}
return TRUE; /* safe for a peephole to remove it :) */
}
/*-----------------------------------------------------------------*/
/* deadMove - Check, if a pop/push pair can be removed */
/*-----------------------------------------------------------------*/
FBYNAME (deadMove)
{
const char *reg = hTabItemWithKey (vars, 1);
if (port->peep.deadMove)
return port->peep.deadMove (reg, currPl, head);
fprintf (stderr, "Function deadMove not initialized in port structure\n");
return FALSE;
}
/*-----------------------------------------------------------------*/
/* labelHashEntry- searches for a label in the list labelHash */
/* Builds labelHash, if it does not yet exist. */
/* Returns the labelHashEntry or NULL */
/*-----------------------------------------------------------------*/
labelHashEntry *
getLabelRef (const char *label, lineNode *head)
{
labelHashEntry *entry;
/* If we don't have the label hash table yet, build it. */
if (!labelHash)
{
buildLabelRefCountHash (head);
}
entry = hTabFirstItemWK (labelHash, hashSymbolName (label));
while (entry)
{
if (!strcmp (label, entry->name))
{
break;
}
entry = hTabNextItemWK (labelHash);
}
return entry;
}
/* 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 (sscanf (cmdLine, "%*[ \t%]%d %d", &varNumber, &expectedRefCount) == 2)
{
char *label = hTabItemWithKey (vars, varNumber);
if (label)
{
labelHashEntry *entry = getLabelRef (label, head);
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
{
// Not a local label. We do not know how often it might be referenced.
rc = FALSE;
}
}
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;
}
/* labelRefCountChange:
* takes two parameters: a variable (bound to a label name)
* and a signed int for changing the reference count.
*
* Please note, this function is not a conditional. It unconditionally
* changes the label. It should be passed as the 'last' function
* so it only is applied if all other conditions have been met.
*
* should always return TRUE
*/
FBYNAME (labelRefCountChange)
{
int varNumber, RefCountDelta;
bool rc = FALSE;
/* If we don't have the label hash table yet, build it. */
if (!labelHash)
{
buildLabelRefCountHash (head);
}
if (sscanf (cmdLine, "%*[ \t%]%d %i", &varNumber, &RefCountDelta) == 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 <= entry->refCount + RefCountDelta)
{
entry->refCount += RefCountDelta;
rc = TRUE;
}
else
{
fprintf (stderr, "*** internal error: label %s may not get"
" negative refCount in %s peephole.\n",
label, __func__);
}
}
else
{
// Not a local label. We do not know how often it might be referenced.
return TRUE;
}
}
else
{
fprintf (stderr, "*** internal error: var %d not bound"
" in peephole %s rule.\n",
varNumber, __func__);
}
}
else
{
fprintf (stderr,
"*** internal error: labelRefCountChange peephole restriction"
" malformed: %s\n", cmdLine);
}
return rc;
}
/* Within the context of the lines currPl through endPl, determine
** if the variable var contains a symbol that is volatile. Returns
** TRUE only if it is certain that this was not volatile (the symbol
** was found and not volatile, or var was a constant or CPU register).
** Returns FALSE if the symbol was found and volatile, the symbol was
** not found, or var was a indirect/pointer addressing mode.
*/
static bool
notVolatileVariable(const char *var, lineNode *currPl, lineNode *endPl)
{
char symname[SDCC_NAME_MAX + 1];
char *p = symname;
const char *vp = var;
lineNode *cl;
operand *op;
iCode *last_ic;
const bool global_not_volatile = currFunc ? !currFunc->funcUsesVolatile : false;
/* Can't tell if indirect accesses are volatile or not, so
** assume they are (if there is a volatile access in the function at all), just to be safe.
*/
if (TARGET_IS_MCS51 || TARGET_IS_DS390 || TARGET_IS_DS400)
{
if (*var=='@')
return global_not_volatile;
}
if (TARGET_Z80_LIKE)
{
if (var[0] == '#')
return true;
if (var[0] == '(')
return global_not_volatile;
if (strstr (var, "(bc)"))
return global_not_volatile;
if (strstr (var, "(de)"))
return global_not_volatile;
if (strstr (var, "(hl)"))
return global_not_volatile;
if (strstr (var, "(ix"))
return global_not_volatile;
if (strstr (var, "(iy"))
return global_not_volatile;
}
if (TARGET_IS_STM8)
{
if (var[0] == '#')
return true;
if (var[0] == '(')
return global_not_volatile;
if (strstr (var, "(x)"))
return global_not_volatile;
if (strstr (var, "(y)"))
return global_not_volatile;
if (strstr (var, ", x)"))
return global_not_volatile;
if (strstr (var, ", y)"))
return global_not_volatile;
if (strstr (var, ", sp)"))
return global_not_volatile;
if (strchr (var, '[') && strchr (var, ']'))
return global_not_volatile;
if (strstr(var, "0x") || strstr(var, "0X") || isdigit(var[0]))
return global_not_volatile;
}
if (TARGET_PDK_LIKE)
{
if (var[0] == '#')
return true;
if (!strcmp (var, "p"))
return true;
}
/* Extract a symbol name from the variable */
while (*vp && (*vp!='_'))
vp++;
while (*vp && (ISCHARALNUM(*vp) || *vp=='_'))
*p++ = *vp++;
*p='\0';
if (!symname[0])
{
/* Nothing resembling a symbol name was found, so it can't
be volatile
*/
return true;
}
last_ic = NULL;
for (cl = currPl; cl!=endPl->next; cl = cl->next)
{
if (cl->ic && (cl->ic!=last_ic))
{
last_ic = cl->ic;
switch (cl->ic->op)
{
case IFX:
op = IC_COND (cl->ic);
if (IS_SYMOP (op) &&
( !strcmp(OP_SYMBOL (op)->rname, symname) ||
(OP_SYMBOL (op)->isspilt &&
SPIL_LOC (op) &&
!strcmp(SPIL_LOC (op)->rname, symname)) ))
{
return !op->isvolatile;
}
case JUMPTABLE:
op = IC_JTCOND (cl->ic);
if (IS_SYMOP (op) &&
( !strcmp(OP_SYMBOL (op)->rname, symname) ||
(OP_SYMBOL (op)->isspilt &&
SPIL_LOC (op) &&
!strcmp(SPIL_LOC (op)->rname, symname)) ))
{
return !op->isvolatile;
}
default:
op = IC_LEFT (cl->ic);
if (IS_SYMOP (op) &&
( !strcmp(OP_SYMBOL (op)->rname, symname) ||
(OP_SYMBOL (op)->isspilt &&
SPIL_LOC (op) &&
!strcmp(SPIL_LOC (op)->rname, symname)) ))
{
return !op->isvolatile;
}
op = IC_RIGHT (cl->ic);
if (IS_SYMOP (op) &&
( !strcmp(OP_SYMBOL (op)->rname, symname) ||
(OP_SYMBOL (op)->isspilt &&
SPIL_LOC (op) &&
!strcmp(SPIL_LOC (op)->rname, symname)) ))
{
return !op->isvolatile;
}
op = IC_RESULT (cl->ic);
if (IS_SYMOP (op) &&
( !strcmp(OP_SYMBOL (op)->rname, symname) ||
(OP_SYMBOL (op)->isspilt &&
SPIL_LOC (op) &&
!strcmp(SPIL_LOC (op)->rname, symname)) ))
{
return !op->isvolatile;
}
}
}
}
/* Couldn't find the symbol for some reason. Assume volatile if the current function touches anything volatile. */
return global_not_volatile;
}
/* notVolatile:
*
* This rule restriction has two different behaviours depending on
* the number of parameters given.
*
* if notVolatile (no parameters given)
* The rule is applied only if none of the iCodes originating
* the matched pattern reference a volatile operand.
*
* if notVolatile %1 ... (one or more parameters given)
* The rule is applied if the parameters are not expressions
* containing volatile symbols and are not pointer accesses.
*
*/
FBYNAME (notVolatile)
{
int varNumber;
char *var;
char *digitend;
lineNode *cl;
operand *op;
if (!cmdLine)
{
/* If no parameters given, just scan the iCodes for volatile operands */
for (cl = currPl; cl!=endPl->next; cl = cl->next)
{
if (cl->ic)
{
switch (cl->ic->op)
{
case IFX:
op = IC_COND (cl->ic);
if (IS_SYMOP (op) && op->isvolatile)
return FALSE;
case JUMPTABLE:
op = IC_JTCOND (cl->ic);
if (IS_SYMOP (op) && op->isvolatile)
return FALSE;
default:
op = IC_LEFT (cl->ic);
if (IS_SYMOP (op) && op->isvolatile)
return FALSE;
op = IC_RIGHT (cl->ic);
if (IS_SYMOP (op) && op->isvolatile)
return FALSE;
op = IC_RESULT (cl->ic);
if (IS_SYMOP (op) && op->isvolatile)
return FALSE;
}
}
}
return TRUE;
}
/* There were parameters; check the volatility of each */
while (*cmdLine && ISCHARSPACE(*cmdLine))
cmdLine++;
while (*cmdLine)
{
if (*cmdLine!='%')
goto error;
cmdLine++;
if (!ISCHARDIGIT(*cmdLine))
goto error;
varNumber = strtol(cmdLine, &digitend, 10);
cmdLine = digitend;
while (*cmdLine && ISCHARSPACE(*cmdLine))
cmdLine++;
var = hTabItemWithKey (vars, varNumber);
if (var)
{
if (!notVolatileVariable (var, currPl, endPl))
return false;
}
else
{
fprintf (stderr, "*** internal error: var %d not bound"
" in peephole notVolatile rule.\n",
varNumber);
return FALSE;
}
}
return TRUE;
error:
fprintf (stderr,
"*** internal error: notVolatile peephole restriction"
" malformed: %s\n", cmdLine);
return FALSE;
}
/*------------------------------------------------------------------*/
/* setFromConditionArgs - parse a peephole condition's arguments */
/* to produce a set of strings, one per argument. Variables %x will */
/* be replaced with their values. String literals (in single quotes)*/
/* are accepted and return in unquoted form. */
/*------------------------------------------------------------------*/
static set *
setFromConditionArgs (char *cmdLine, hTab * vars)
{
int varNumber;
char *var;
char *digitend;
set *operands = NULL;
if (!cmdLine)
return NULL;
while (*cmdLine && ISCHARSPACE(*cmdLine))
cmdLine++;
while (*cmdLine)
{
if (*cmdLine == '%')
{
cmdLine++;
if (!ISCHARDIGIT(*cmdLine))
goto error;
varNumber = strtol(cmdLine, &digitend, 10);
cmdLine = digitend;
var = hTabItemWithKey (vars, varNumber);
if (var)
{
addSetHead (&operands, var);
}
else
goto error;
}
else if (*cmdLine == '\'' )
{
char quote = *cmdLine;
var = ++cmdLine;
while (*cmdLine && *cmdLine != quote)
cmdLine++;
if (*cmdLine == quote)
*cmdLine++ = '\0';
else
goto error;
addSetHead (&operands, var);
}
else
goto error;
while (*cmdLine && ISCHARSPACE(*cmdLine))
cmdLine++;
}
return operands;
error:
deleteSet (&operands);
return NULL;
}
static const char *
operandBaseName (const char *op)
{
if (TARGET_IS_MCS51 || TARGET_IS_DS390 || TARGET_IS_DS400)
{
if (!strcmp (op, "acc") || !strncmp (op, "acc.", 4))
return "a";
if (!strncmp (op, "ar", 2) && ISCHARDIGIT(*(op+2)) && !*(op+3))
return op+1;
// bug 1739475, temp fix
if (op[0] == '@')
return operandBaseName(op+1);
}
if (TARGET_Z80_LIKE)
{
if (!strcmp (op, "d") || !strcmp (op, "e") || !strcmp (op, "(de)"))
return "de";
if (!strcmp (op, "b") || !strcmp (op, "c") || !strcmp (op, "(bc)"))
return "bc";
if (!strcmp (op, "h") || !strcmp (op, "l") || !strcmp (op, "(hl)") || !strcmp (op, "(hl+)") || !strcmp (op, "(hl-)"))
return "hl";
if (!strcmp (op, "iyh") || !strcmp (op, "iyl") || strstr (op, "iy"))
return "iy";
if (!strcmp (op, "ixh") || !strcmp (op, "ixl") || strstr (op, "ix"))
return "ix";
if (!strcmp (op, "a"))
return "af";
}
return op;
}
/*-----------------------------------------------------------------*/
/* notUsed - Check, if value in register is not read again */
/*-----------------------------------------------------------------*/
FBYNAME (notUsed)
{
const char *what;
bool ret;
set *operands = setFromConditionArgs (cmdLine, vars);
if (!operands || elementsInSet(operands) != 1)
{
fprintf (stderr,
"*** internal error: notUsed peephole restriction"
" malformed: %s\n", cmdLine);
return FALSE;
}
what = setFirstItem (operands);
if (!port->peep.notUsed)
{
fprintf (stderr, "Function notUsed not initialized in port structure\n");
return FALSE;
}
ret = port->peep.notUsed (what, endPl, head);
deleteSet(&operands);
return (ret);
}
/*-----------------------------------------------------------------*/
/* notUsed - Check, if value in register is not read again */
/* starting from label */
/*-----------------------------------------------------------------*/
FBYNAME (notUsedFrom)
{
const char *what, *label;
set *operands = setFromConditionArgs (cmdLine, vars);
if (!operands || elementsInSet(operands) != 2)
{
fprintf (stderr,
"*** internal error: notUsedFrom peephole restriction"
" malformed: %s\n", cmdLine);
return FALSE;
}
what = setFirstItem (operands);
label = setNextItem (operands);
if (port->peep.notUsedFrom)
return port->peep.notUsedFrom (what, label, head);
fprintf (stderr, "Function notUsed not initialized in port structure\n");
return FALSE;
}
/*-----------------------------------------------------------------*/
/* canAssign - Check, if we can do ld dst, src. */
/*-----------------------------------------------------------------*/
FBYNAME (canAssign)
{
set *operands;
const char *dst, *src, *exotic;
operands = setFromConditionArgs (cmdLine, vars);
if (!operands || elementsInSet(operands) < 2 || elementsInSet(operands) > 3)
{
fprintf (stderr,
"*** internal error: canAssign peephole restriction"
" malformed: %s\n", cmdLine);
return FALSE;
}
if(elementsInSet(operands) == 3)
{
exotic = setFirstItem (operands);
src = setNextItem (operands);
dst = setNextItem (operands);
}
else
{
exotic = 0;
src = setFirstItem (operands);
dst = setNextItem (operands);
}
if (port->peep.canAssign)
{
bool ret = port->peep.canAssign (dst, src, exotic);
deleteSet (&operands);
return (ret);
}
deleteSet (&operands);
fprintf (stderr, "Function canAssign not initialized in port structure\n");
return FALSE;
}
/*-----------------------------------------------------------------*/
/* operandsNotRelated - returns true if the condition's operands */
/* are not related (taking into account register name aliases). */
/* N-way comparison performed between all operands. */
/*-----------------------------------------------------------------*/
FBYNAME (operandsNotRelated)
{
set *operands;
const char *op1, *op2;
operands = setFromConditionArgs (cmdLine, vars);
if (!operands)
{
fprintf (stderr,
"*** internal error: operandsNotRelated peephole restriction"
" malformed: %s\n", cmdLine);
return FALSE;
}
while ((op1 = setFirstItem (operands)))
{
deleteSetItem (&operands, (void*)op1);
op1 = operandBaseName (op1);
for (op2 = setFirstItem (operands); op2; op2 = setNextItem (operands))
{
op2 = operandBaseName (op2);
if (strcmp (op1, op2) == 0)
{
deleteSet (&operands);
return FALSE;
}
}
}
deleteSet (&operands);
return TRUE;
}
/*-----------------------------------------------------------------*/
/* notSimilar - Check, if one is another's substring */
/*-----------------------------------------------------------------*/
FBYNAME (notSimilar)
{
set *operands;
const char *op1, *op2;
operands = setFromConditionArgs (cmdLine, vars);
if (!operands)
{
fprintf (stderr,
"*** internal error: notSimilar peephole restriction"
" malformed: %s\n", cmdLine);
return FALSE;
}
while ((op1 = setFirstItem (operands)))
{
deleteSetItem (&operands, (void*)op1);
for (op2 = setFirstItem (operands); op2; op2 = setNextItem (operands))
{
if ((strstr (op1, op2) || strstr (op2, op1)) && strcmp (op1, op2) == 0)
{
deleteSet (&operands);
return FALSE;
}
}
}
deleteSet (&operands);
return TRUE;
}
/*-----------------------------------------------------------------*/
/* symmParmStack - Caller readjusts stack by the number of bytes
that were pushed in all calls to this function */
/*-----------------------------------------------------------------*/
FBYNAME (symmParmStack)
{
if (port->peep.symmParmStack)
return port->peep.symmParmStack();
return FALSE;
}
/*-----------------------------------------------------------------*/
/* notSame - Check, that arguments are pairwise not the same */
/*-----------------------------------------------------------------*/
FBYNAME (notSame)
{
set *operands;
const char *op1, *op2;
operands = setFromConditionArgs (cmdLine, vars);
if (!operands)
{
fprintf (stderr,
"*** internal error: notSame peephole restriction"
" malformed: %s\n", cmdLine);
return FALSE;
}
while ((op1 = setFirstItem (operands)))
{
deleteSetItem (&operands, (void*)op1);
for (op2 = setFirstItem (operands); op2; op2 = setNextItem (operands))
{
if (strcmp (op1, op2) == 0)
{
deleteSet (&operands);
return FALSE;
}
}
}
deleteSet (&operands);
return TRUE;
}
/*-----------------------------------------------------------------*/
/* same - Check if first operand matches any of the remaining */
/*-----------------------------------------------------------------*/
FBYNAME (same)
{
set *operands;
const char *match, *op;
operands = setFromConditionArgs(cmdLine, vars);
if (!operands)
{
fprintf(stderr,
"*** internal error: same peephole restriction"
" malformed: %s\n", cmdLine);
return FALSE;
}
operands = reverseSet(operands);
match = setFirstItem(operands);
for (op = setNextItem(operands); op; op = setNextItem(operands))
{
if (strcmp(match, op) == 0)
{
deleteSet(&operands);
return TRUE;
}
}
deleteSet(&operands);
return FALSE;
}
/*-----------------------------------------------------------------*/
/* operandsLiteral - returns true if the condition's operands are */
/* literals. */
/*-----------------------------------------------------------------*/
FBYNAME (operandsLiteral)
{
set *operands;
const char *op;
operands = setFromConditionArgs (cmdLine, vars);
if (!operands)
{
fprintf (stderr,
"*** internal error: operandsLiteral peephole restriction"
" malformed: %s\n", cmdLine);
return FALSE;
}
for (op = setFirstItem (operands); op; op = setNextItem (operands))
{
if (!isdigit( (unsigned char)(*op) ))
{
deleteSet (&operands);
return FALSE;
}
}
deleteSet (&operands);
return TRUE;
}
static long *
immdGet (const char *pc, long *pl)
{
long s = 1;
if (!pc || !pl)
return NULL;
// omit space
for (; ISCHARSPACE (*pc); pc++);
// parse sign
for (; !ISCHARDIGIT (*pc); pc++)
if (*pc == '-')
s *= -1;
else if (*pc == '+')
s *= +1;
else
return NULL;
if (pc[0] == '0' && (pc[1] == 'x' || pc[1] == 'X'))
{
if (sscanf (pc + 2, "%lx", pl) != 1)
return NULL;
}
else
{
if (sscanf (pc, "%ld", pl) != 1)
return NULL;
}
*pl *= s;
return pl;
}
static bool
immdError (const char *info, const char *param, const char *cmd)
{
fprintf (stderr, "*** internal error: immdInRange gets "
"%s: \"%s\" in \"%s\"\n", info, param, cmd);
return FALSE;
}
/*-----------------------------------------------------------------*/
/* immdInRange - returns true if the sum or difference of two */
/* immediates is in a give range. */
/*-----------------------------------------------------------------*/
FBYNAME (immdInRange)
{
char r[64], operator[8];
const char *op;
long i, j, k, h, low, high, left_l, right_l, order;
const char *padd[] = {"+", "'+'", "\"+\"", "add", "'add'", "\"add\""};
const char *psub[] = {"-", "'-'", "\"-\"", "sub", "'sub'", "\"sub\""};
for (i = order = 0; order < 6;)
{
// pick up one parameter in the temp buffer r[64]
for (; ISCHARSPACE (cmdLine[i]) && cmdLine[i]; i++);
for (j = i; !ISCHARSPACE (cmdLine[j]) && cmdLine[j]; j++);
if (!cmdLine[i]) // unexpected end
return immdError ("no enough input", "", cmdLine);
else
{
for (k = i; k < j; k++)
r[k - i] = cmdLine[k];
r[j - i] = 0;
}
// parse the string by order
switch (order)
{
case 0: // lower bound
if (!immdGet (r, &low))
return immdError ("bad lower bound", r, cmdLine);
break;
case 1: // upper bound
if (!immdGet (r, &high))
return immdError ("bad upper bound", r, cmdLine);
break;
case 2: // operator
if (sscanf (r, "%s", operator) != 1)
return immdError ("bad operator", r, cmdLine);
break;
case 3: // left operand
if (immdGet (r, &left_l)) // the left operand is given directly
{
}
else if (r[0] == '%') // the left operand is passed via pattern match
{
if (!immdGet (r + 1, &k) || !(op = hTabItemWithKey (vars, (int) k)))
return immdError ("bad left operand", r, cmdLine);
else if (!immdGet (op, &left_l))
return immdError ("bad left operand", op, r);
}
else
return immdError ("bad left operand", r, cmdLine);
break;
case 4: // right operand
if (immdGet (r, &right_l)) // the right operand is given directly
{
}
else if (r[0] == '%') // the right operand is passed via pattern match
{
if (!immdGet (r + 1, &k) || !(op = hTabItemWithKey (vars, (int) k)))
return immdError ("bad right operand", r, cmdLine);
else if (!immdGet (op, &right_l))
return immdError ("bad right operand", op, r);
}
else
return immdError ("bad right operand", r, cmdLine);
break;
case 5: // result
if (r[0] != '%' || !immdGet (r + 1, &h))
return immdError ("bad result container", r, cmdLine);
break;
default: // should not reach
return immdError ("unexpected input", "", cmdLine);
break;
}
order++;
i = j;
}
// calculate
for (j = k = 0; k < sizeof (padd) / sizeof (padd[0]); k++) // add
if (strcmp (operator, padd[k]) == 0)
{
i = left_l + right_l;
j = 1;
break;
}
if (!j)
for (k = 0; k < sizeof (psub) / sizeof (psub[0]); k++) // sub
if (strcmp (operator, psub[k]) == 0)
{
i = left_l - right_l;
j = 1;
break;
}
if (!j)
return immdError ("bad operator", operator, cmdLine);
// bind the result
if ((low <= i && i <= high) || (high <= i && i <= low))
{
char *p[] = {r, NULL};
sprintf (r, "%ld", i);
bindVar ((int) h, p, &vars);
return TRUE;
}
else
{
return FALSE;
}
}
/*-----------------------------------------------------------------*/
/* inSequence - Check that numerical constants are in sequence */
/*-----------------------------------------------------------------*/
FBYNAME (inSequence)
{
set *operands;
const char *op;
long seq, val, stride;
if ((operands = setFromConditionArgs(cmdLine, vars)) == NULL)
{
fprintf (stderr,
"*** internal error: inSequence peephole restriction"
" malformed: %s\n", cmdLine);
return FALSE;
}
operands = reverseSet(operands);
op = setFirstItem(operands);
if ((immdGet(op, &stride) == NULL) || ((op = setNextItem(operands)) == NULL))
{
fprintf (stderr,
"*** internal error: inSequence peephole restriction"
" malformed: %s\n", cmdLine);
return FALSE;
}
for (seq = LONG_MIN; op; op = setNextItem(operands))
{
if ((immdGet(op, &val) == NULL) || ((seq != LONG_MIN) && (val != seq+stride)))
{
deleteSet(&operands);
return FALSE;
}
seq = val;
}
deleteSet(&operands);
return TRUE;
}
static const struct ftab
{
char *fname;
int (*func) (hTab *, lineNode *, lineNode *, lineNode *, char *);
}
ftab[] = // sorted on the number of times used
{ // in the peephole rules on 2010-06-12
{
"labelRefCount", labelRefCount // 161
},
{
"notVolatile", notVolatile // 118
},
{
"notUsed", notUsed // 96
},
{
"labelRefCountChange", labelRefCountChange // 86
},
{
"labelInRange", labelInRange // 51
},
{
"notSame", notSame // 28
},
{
"operandsNotRelated", operandsNotRelated // 28
},
{
"same", same // z88dk z80
},
{
"labelJTInRange", labelJTInRange // 13
},
{
"24bitMode", flat24bitMode // 9
},
{
"canAssign", canAssign // 8
},
{
"inSequence", inSequence // z88dk z80
},
{
"optimizeReturn", optimizeReturn // ? just a guess
},
{
"notUsedFrom", notUsedFrom // ? just a guess
},
{
"labelIsReturnOnly", labelIsReturnOnly // 6
},
{
"operandsLiteral", operandsLiteral // 6
},
{
"portIsDS390", portIsDS390 // 5
},
{
"labelIsUncondJump", labelIsUncondJump // 4
},
{
"deadMove", deadMove // 2
},
{
"useAcallAjmp", useAcallAjmp // 2
},
{
"xramMovcOption", xramMovcOption // 2
},
{
"okToRemoveSLOC", okToRemoveSLOC // 0
},
{
"immdInRange", immdInRange
},
{
"notSimilar", notSimilar
},
{
"symmParmStack", symmParmStack
},
};
/*-----------------------------------------------------------------*/
/* callFuncByName - calls a function as defined in the table */
/*-----------------------------------------------------------------*/
static int
callFuncByName (char *fname,
hTab *vars,
lineNode *currPl, /* first source line matched */
lineNode *endPl, /* last source line matched */
lineNode *head)
{
int i;
char *cmdCopy, *funcName, *funcArgs, *cmdTerm;
char c;
int rc;
/* Isolate the function name part (we are passed the full condition
* string including arguments)
*/
cmdTerm = cmdCopy = Safe_strdup(fname);
do
{
funcArgs = funcName = cmdTerm;
while ((c = *funcArgs) && c != ' ' && c != '\t' && c != '(')
funcArgs++;
*funcArgs = '\0'; /* terminate the function name */
if (c)
funcArgs++;
/* Find the start of the arguments */
if (c == ' ' || c == '\t')
while ((c = *funcArgs) && (c == ' ' || c == '\t'))
funcArgs++;
/* If the arguments started with an opening parenthesis, */
/* use the closing parenthesis for the end of the */
/* arguments and look for the start of another condition */
/* that can optionally follow. If there was no opening */
/* parethesis, then everything that follows are arguments */
/* and there can be no additional conditions. */
if (c == '(')
{
int num_parenthesis = 0;
cmdTerm = funcArgs;
while ((c = *cmdTerm) && (c != ')' || num_parenthesis))
{
if (c == '(')
num_parenthesis++;
else if (c == ')')
num_parenthesis--;
cmdTerm++;
}
*cmdTerm = '\0'; /* terminate the arguments */
if (c == ')')
{
cmdTerm++;
while ((c = *cmdTerm) && (c == ' ' || c == '\t' || c == ','))
cmdTerm++;
if (!*cmdTerm)
cmdTerm = NULL;
}
else
cmdTerm = NULL; /* closing parenthesis missing */
}
else
cmdTerm = NULL;
if (!*funcArgs)
funcArgs = NULL;
rc = -1;
for (i = 0; i < ((sizeof (ftab)) / (sizeof (struct ftab))); i++)
{
if (strcmp (ftab[i].fname, funcName) == 0)
{
rc = (*ftab[i].func) (vars, currPl, endPl, head, funcArgs);
break;
}
}
if (rc == -1)
{
fprintf (stderr,
"could not find named function \"%s\" in "
"peephole function table\n",
funcName);
// If the function couldn't be found, let's assume it's
// a bad rule and refuse it.
rc = FALSE;
break;
}
}
while (rc && cmdTerm);
Safe_free(cmdCopy);
return rc;
}
/*-----------------------------------------------------------------*/
/* newPeepRule - creates a new peeprule and attach it to the root */
/*-----------------------------------------------------------------*/
static peepRule *
newPeepRule (lineNode * match,
lineNode * replace,
char *cond,
int restart,
int barrier)
{
peepRule *pr;
pr = Safe_alloc ( sizeof (peepRule));
pr->match = match;
pr->replace = replace;
pr->restart = restart;
pr->barrier = barrier;
if (cond && *cond)
{
pr->cond = Safe_strdup (cond);
}
else
pr->cond = NULL;
pr->vars = newHashTable (16);
/* if root is empty */
if (!rootRules)
rootRules = currRule = pr;
else
currRule = currRule->next = pr;
return pr;
}
#define SKIP_SPACE(x,y) { while (*x && (ISCHARSPACE(*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, const char **bpp)
{
char lines[MAX_PATTERN_LEN];
char *lp;
int isComment;
lineNode *currL = NULL;
const char *bp = *bpp;
while (1)
{
if (!*bp)
{
fprintf (stderr, "unexpected end of match pattern\n");
return;
}
if (*bp == '\n')
{
bp++;
while (ISCHARSPACE (*bp) ||
*bp == '\n')
bp++;
}
if (*bp == '}')
{
bp++;
break;
}
/* read till end of line */
lp = lines;
while ((*bp != '\n' && *bp != '}') && *bp)
*lp++ = *bp++;
*lp = '\0';
lp = lines;
while (*lp && ISCHARSPACE(*lp))
lp++;
isComment = (*lp == ';');
if (!isComment || (isComment && !options.noPeepComments))
{
const char *dummy1;
int dummy2;
if (!currL)
*head = currL = newLineNode (lines);
else
currL = connectLine (currL, newLineNode (lines));
currL->isComment = isComment;
currL->isLabel = isLabelDefinition (currL->line, &dummy1, &dummy2,
TRUE);
}
}
*bpp = bp;
}
/*-----------------------------------------------------------------*/
/* readRules - reads the rules from a string buffer */
/*-----------------------------------------------------------------*/
static void
readRules (const char *bp)
{
char restart = 0, barrier = 0;
char lines[MAX_PATTERN_LEN];
size_t safetycounter;
char *lp;
const char *rp;
lineNode *match;
lineNode *replace;
lineNode *currL = NULL;
if (!bp)
return;
top:
restart = 0;
barrier = 0;
/* look for the token "replace" that is the
start of a rule */
while (*bp && strncmp (bp, "replace", 7))
{
if (!strncmp (bp, "barrier", 7))
barrier = 1;
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++;
/* save char position (needed for generating error msg) */
rp = bp;
SKIP_SPACE (bp, "unexpected end of rule\n");
getPeepLine (&replace, &bp);
/* look for a 'if' */
while ((ISCHARSPACE (*bp) || *bp == '\n' || (*bp == '/' && *(bp+1) == '/')) && *bp)
{
++bp;
if (*bp == '/') while (*bp && *bp != '\n') ++bp;
}
if (strncmp (bp, "if", 2) == 0)
{
bp += 2;
while ((ISCHARSPACE (*bp) || *bp == '\n' || (*bp == '/' && *(bp+1) == '/')) && *bp)
{
bp++;
if (*bp == '/')
while (*bp && *bp != '\n')
bp++;
}
if (!*bp)
{
fprintf (stderr, "expected condition name\n");
return;
}
/* look for the condition */
lp = lines;
for (safetycounter = 0; *bp && (*bp != '\n'); safetycounter++)
{
wassertl(safetycounter < MAX_PATTERN_LEN, "Peephole line too long.\n");
*lp++ = *bp++;
}
*lp = '\0';
newPeepRule (match, replace, lines, restart, barrier);
}
else
{
if (*bp && strncmp (bp, "replace", 7) && strncmp (bp, "barrier", 7))
{
/* not the start of a new peeprule, so "if" should be here */
char strbuff[1000];
char *cp;
/* go to the start of the line following "{" of the "by" token */
while (*rp && (*rp == '\n'))
rp++;
/* copy text of rule starting with line after "by {" */
cp = strbuff;
while (*rp && (rp < bp) && ((cp - strbuff) < sizeof(strbuff)))
*cp++ = *rp++;
/* and now the rest of the line */
while (*rp && (*rp != '\n') && ((cp - strbuff) < sizeof(strbuff)))
*cp++ = *rp++;
*cp = '\0';
fprintf (stderr, "%s\nexpected '} if ...'\n", strbuff);
return;
}
newPeepRule (match, replace, NULL, restart, barrier);
}
goto top;
}
/*-----------------------------------------------------------------*/
/* keyForVar - returns the numeric key for a var */
/*-----------------------------------------------------------------*/
static int
keyForVar (const char *d)
{
int i = 0;
while (ISCHARDIGIT (*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 != ',' &&
!ISCHARSPACE (*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--;
}
// include the trailing ')'
*vv++ = *vvx++;
}
else
*vv++ = *vvx++;
}
*s = vvx;
*vv = '\0';
/* got value */
vvx = traceAlloc (&_G.values, Safe_strdup(vval));
hTabAddItem (vtab, key, vvx);
}
/*-----------------------------------------------------------------*/
/* matchLine - matches one line */
/*-----------------------------------------------------------------*/
static bool
matchLine (char *s, const char *d, hTab ** vars)
{
if (!s || !(*s))
return FALSE;
/* skip leading white spaces */
while (ISCHARSPACE (*s))
s++;
while (ISCHARSPACE (*d))
d++;
while (*s && *d)
{
/* skip white space in both */
while (ISCHARSPACE(*s))
s++;
while (ISCHARSPACE(*d))
d++;
/* if the destination is a var */
if (*d == '%' && ISCHARDIGIT (*(d + 1)) && vars)
{
const 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 (ISCHARDIGIT (*d))
d++;
}
else if (ISCHARSPACE (*s) && ISCHARSPACE (*d)) /* whitespace sequences match any whitespace sequences */
{
while (ISCHARSPACE (*s))
s++;
while (ISCHARSPACE (*d))
d++;
}
else if (*s == ',' && *d == ',') /* Allow comman to match comma followed by whitespace */
{
s++, d++;
while (ISCHARSPACE (*s))
s++;
while (ISCHARSPACE (*d))
d++;
}
else if (*s && *d) /* they should be an exact match otherwise */
{
if (*s++ != *d++)
return FALSE;
}
}
/* skip trailing whitespaces */
if (*s)
while (ISCHARSPACE (*s))
s++;
if (*d)
while (ISCHARSPACE (*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, spl, head))
{
*mtail = spl;
return TRUE;
}
else
return FALSE;
}
else
{
*mtail = spl;
return TRUE;
}
}
else
return FALSE;
}
static void
reassociate_ic_down (lineNode *shead, lineNode *stail,
lineNode *rhead, lineNode *rtail)
{
lineNode *csl; /* current source line */
lineNode *crl; /* current replacement line */
csl = shead;
crl = rhead;
while (1)
{
/* skip over any comments */
while (csl!=stail->next && csl->isComment)
csl = csl->next;
while (crl!=rtail->next && crl->isComment)
crl = crl->next;
/* quit if we reach the end */
if ((csl==stail->next) || (crl==rtail->next) || crl->ic)
break;
if (matchLine(csl->line,crl->line,NULL))
{
crl->ic = csl->ic;
csl = csl->next;
crl = crl->next;
}
else
break;
}
}
static void
reassociate_ic_up (lineNode *shead, lineNode *stail,
lineNode *rhead, lineNode *rtail)
{
lineNode *csl; /* current source line */
lineNode *crl; /* current replacement line */
csl = stail;
crl = rtail;
while (1)
{
/* skip over any comments */
while (csl!=shead->prev && csl->isComment)
csl = csl->prev;
while (crl!=rhead->prev && crl->isComment)
crl = crl->prev;
/* quit if we reach the end */
if ((csl==shead->prev) || (crl==rhead->prev) || crl->ic)
break;
if (matchLine(csl->line,crl->line,NULL))
{
crl->ic = csl->ic;
csl = csl->prev;
crl = crl->prev;
}
else
break;
}
}
/*------------------------------------------------------------------*/
/* reassociate_ic - reassociate replacement lines with origin iCode */
/*------------------------------------------------------------------*/
static void
reassociate_ic (lineNode *shead, lineNode *stail,
lineNode *rhead, lineNode *rtail)
{
lineNode *csl; /* current source line */
lineNode *crl; /* current replacement line */
bool single_iCode;
iCode *ic;
/* Check to see if all the source lines (excluding comments) came
** for the same iCode
*/
ic = NULL;
for (csl=shead;csl!=stail->next;csl=csl->next)
if (csl->ic && !csl->isComment)
{
ic = csl->ic;
break;
}
single_iCode = (ic!=NULL);
for (csl=shead;csl!=stail->next;csl=csl->next)
if ((csl->ic != ic) && !csl->isComment)
{
/* More than one iCode was found. However, if it's just the
** last line with the different iCode and it was not changed
** in the replacement, everything else must be the first iCode.
*/
if ((csl==stail) && matchLine (stail->line, rtail->line, NULL))
{
rtail->ic = stail->ic;
for (crl=rhead;crl!=rtail;crl=crl->next)
crl->ic = ic;
return;
}
single_iCode = FALSE;
break;
}
/* If all of the source lines came from the same iCode, then so have
** all of the replacement lines too.
*/
if (single_iCode)
{
for (crl=rhead;crl!=rtail->next;crl=crl->next)
crl->ic = ic;
return;
}
/* The source lines span iCodes, so we may end up with replacement
** lines that we don't know which iCode(s) to associate with. Do the
** best we can by using the following strategies:
** 1) Start at the top and scan down. As long as the source line
** matches the replacement line, they have the same iCode.
** 2) Start at the bottom and scan up. As long as the source line
** matches the replacement line, they have the same iCode.
** 3) For any label in the source, look for a matching label in
** the replacment. If found, they have the same iCode. From
** these matching labels, scan down for additional matching
** lines; if found, they also have the same iCode.
*/
/* Strategy #1: Start at the top and scan down for matches
*/
reassociate_ic_down(shead,stail,rhead,rtail);
/* Strategy #2: Start at the bottom and scan up for matches
*/
reassociate_ic_up(shead,stail,rhead,rtail);
/* Strategy #3: Try to match labels
*/
csl = shead;
while (1)
{
/* skip over any comments */
while (csl!=stail->next && csl->isComment)
csl = csl->next;
if (csl==stail->next)
break;
if (csl->isLabel)
{
/* found a source line label; look for it in the replacment lines */
crl = rhead;
while (1)
{
while (crl!=rtail->next && crl->isComment)
crl = crl->next;
if (crl==rtail->next)
break;
if (matchLine(csl->line, crl->line, NULL))
{
reassociate_ic_down(csl,stail,crl,rtail);
break;
}
else
crl = crl->next;
}
}
csl = csl->next;
}
/* Try to assign a meaningful iCode to any comment that is missing
one. Since they are comments, it's ok to make mistakes; we are just
trying to improve continuity to simplify other tests.
*/
ic = NULL;
for (crl=rtail;crl!=rhead->prev;crl=crl->prev)
{
if (!crl->ic && ic && crl->isComment)
crl->ic = ic;
ic = crl->ic;
}
}
/*-----------------------------------------------------------------*/
/* 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;
pl->isComment = cl->isComment || (*cl->line == ';');
}
}
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 == '%' && ISCHARDIGIT (*(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 (ISCHARDIGIT (*l)) {
l++;
}
continue;
}
*lbp++ = *l++;
}
*lbp = '\0';
if (cl)
cl = connectLine (cl, newLineNode (lb));
else
lhead = cl = newLineNode (lb);
cl->isComment = pl->isComment;
cl->isLabel = pl->isLabel;
}
/* 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;
}
if (lhead && cl)
{
/* determine which iCodes the replacment lines relate to */
reassociate_ic(*shead,stail,lhead,cl);
/* 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;
}
*shead = lhead;
/* now for the tail */
if (stail && stail->next)
{
stail->next->prev = cl;
if (cl)
cl->next = stail->next;
}
}
else
{
/* the replacement is empty - delete the source lines */
if ((*shead)->prev)
(*shead)->prev->next = stail->next;
if (stail->next)
stail->next->prev = (*shead)->prev;
*shead = 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, bool isPeepRule)
{
const char *cp = line;
/* This line is a label if it consists of:
* [optional whitespace] followed by identifier chars
* (alnum | $ | _ ) followed by a colon.
*/
while (*cp && ISCHARSPACE (*cp))
{
cp++;
}
if (!*cp)
{
return FALSE;
}
*start = cp;
while (ISCHARALNUM (*cp) || (*cp == '$') || (*cp == '_') ||
(isPeepRule && (*cp == '%')))
{
cp++;
}
if ((cp == *start) || (*cp != ':'))
{
return FALSE;
}
*len = (cp - (*start));
return TRUE;
}
/* Not perfect, will not find all references yet.
Will however find references in call on Z80, which is sufficient to fix #2970351. */
bool
isLabelReference (const char *line, const char **start, int *len)
{
const char *s, *e;
if (!TARGET_Z80_LIKE && !TARGET_IS_STM8 && !TARGET_PDK_LIKE)
return FALSE;
s = line;
while (ISCHARSPACE (*s))
++s;
if(strncmp(s, "call", 4))
return FALSE;
s += 4;
while (ISCHARSPACE (*s))
++s;
/* Skip condition in conditional call */
if (strchr(s, ','))
s = strchr(s, ',') + 1;
e = s, *len = 0;
while(*e && !ISCHARSPACE (*e) && *e != ';')
++e, ++(*len);
*start = s;
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. */
for (line = head; line; line = line->next)
{
bool ref = FALSE;
/* run isLabelDefinition to:
- look for labels in inline assembler
- calculate labelLen
*/
if ((line->isLabel || line->isInline) && isLabelDefinition (line->line, &label, &labelLen, FALSE) ||
(ref = TRUE) && isLabelReference (line->line, &label, &labelLen))
{
labelHashEntry *entry, *e;
assert (labelLen <= SDCC_NAME_MAX);
entry = traceAlloc (&_G.labels, Safe_alloc(sizeof (labelHashEntry)));
memcpy (entry->name, label, labelLen);
entry->name[labelLen] = 0;
entry->refCount = -1;
for (e = hTabFirstItemWK (labelHash, hashSymbolName (entry->name)); e; e = hTabNextItemWK (labelHash))
if (!strcmp (entry->name, e->name))
goto c;
/* Assume function entry points are referenced somewhere, */
/* even if we can't find a reference (might be from outside */
/* the function) */
if (line->ic && (line->ic->op == FUNCTION) || ref)
entry->refCount++;
hTabAddItem (&labelHash, hashSymbolName (entry->name), entry);
}
c:;
}
/* Second pass: for each line, note all the referenced labels. */
/* This is ugly, O(N^2) stuff. Optimizations welcome... */
for (line = head; line; line = line->next)
{
if (line->isComment)
continue;
/* Padauk skip instructions */
if (TARGET_PDK_LIKE &&
(!strncmp(line->line, "ceqsn", 5) || !strncmp(line->line, "cneqsn", 6) ||
!strncmp(line->line, "t0sn", 4) || !strncmp(line->line, "t1sn", 4) ||
!strncmp(line->line, "izsn", 4) || !strncmp(line->line, "dzsn", 4)))
{
const lineNode *const l = line->next->next;
wassert (l);
if (l->isLabel && isLabelDefinition (l->line, &label, &labelLen, false))
{
char name[SDCC_NAME_MAX];
strcpy(name, label);
name[labelLen] = 0;
labelHashEntry *e = hTabFirstItemWK (labelHash, hashSymbolName (name));
if (e)
e->refCount++;
}
}
for (i = 0; i < HTAB_SIZE; i++)
{
labelHashEntry *thisEntry;
thisEntry = hTabFirstItemWK (labelHash, i);
while (thisEntry)
{
const char *s;
if ((s = strstr (line->line, thisEntry->name)) && !ISCHARALNUM (*(s + strlen (thisEntry->name))) && (s == line->line || !ISCHARALNUM (*(s - 1))))
thisEntry->refCount++;
thisEntry = hTabNextItemWK (labelHash);
}
}
}
#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, replaced;
#if !OPT_DISABLE_PIC14 || !OPT_DISABLE_PIC16
/* The PIC port uses a different peep hole optimizer based on "pCode" */
if (TARGET_PIC_LIKE)
return;
#endif
assert(labelHash == NULL);
do
{
restart = FALSE;
/* for all rules */
for (pr = rootRules; pr; pr = pr->next)
{
if (restart && pr->barrier)
break;
for (spl = *pls; spl; spl = replaced ? spl : spl->next)
{
replaced = FALSE;
/* if inline assembler then no peep hole */
if (spl->isInline)
continue;
/* don't waste time starting a match on debug symbol
** or comment */
if (spl->isDebug || spl->isComment || *(spl->line)==';')
continue;
mtail = NULL;
/* Tidy up any data stored in the hTab */
/* if it matches */
if (matchRule (spl, &mtail, pr, *pls))
{
/* restart at the replaced line */
replaced = TRUE;
/* then replace */
if (spl == *pls)
{
replaceRule (pls, mtail, pr);
spl = *pls;
}
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);
strncatz (rs, lb, strlen (rs) + strlen (lb) + 1);
}
else
{
rs = Safe_strdup (lb);
}
nch = 0;
}
}
fclose (f);
/* if some characters left over */
if (nch)
{
lb[nch] = '\0';
/* copy it into allocated buffer */
if (rs)
{
rs = Safe_realloc (rs, strlen (rs) + strlen (lb) + 1);
strncatz (rs, lb, strlen (rs) + strlen (lb) + 1);
}
else
{
rs = Safe_strdup (lb);
}
}
return rs;
}
/*-----------------------------------------------------------------*/
/* initPeepHole - initialises the peep hole optimizer stuff */
/*-----------------------------------------------------------------*/
void
initPeepHole (void)
{
char *s;
/* read in the default rules */
if (!options.nopeep)
{
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);
/* override nopeep setting, default rules have not been read */
options.nopeep = 0;
}
#if !OPT_DISABLE_PIC14
/* Convert the peep rules into pcode.
NOTE: this is only support in the PIC port (at the moment)
*/
if (TARGET_IS_PIC14)
peepRules2pCode (rootRules);
#endif
#if !OPT_DISABLE_PIC16
/* Convert the peep rules into pcode.
NOTE: this is only support in the PIC port (at the moment)
and the PIC16 port (VR 030601)
*/
if (TARGET_IS_PIC16)
pic16_peepRules2pCode (rootRules);
#endif
}
/*-----------------------------------------------------------------*/
/* StrStr - case-insensitive strstr implementation */
/*-----------------------------------------------------------------*/
const char * StrStr (const char * str1, const char * str2)
{
const char * cp = str1;
const char * s1;
const char * s2;
if ( !*str2 )
return str1;
while (*cp)
{
s1 = cp;
s2 = str2;
while ( *s1 && *s2 && !(tolower(*s1)-tolower(*s2)) )
s1++, s2++;
if (!*s2)
return( cp );
cp++;
}
return (NULL) ;
}