diff options
| author | Xavier ASUS <xavi92psx@gmail.com> | 2019-10-18 00:31:54 +0200 |
|---|---|---|
| committer | Xavier ASUS <xavi92psx@gmail.com> | 2019-10-18 00:31:54 +0200 |
| commit | 268a53de823a6750d6256ee1fb1e7707b4b45740 (patch) | |
| tree | 42c1799a9a82b2f7d9790ee9fe181d72a7274751 /src/SDCCpeeph.c | |
| download | sdcc-gas-268a53de823a6750d6256ee1fb1e7707b4b45740.tar.gz | |
sdcc-3.9.0 fork implementing GNU assembler syntax
This fork aims to provide better support for stm8-binutils
Diffstat (limited to 'src/SDCCpeeph.c')
| -rw-r--r-- | src/SDCCpeeph.c | 2893 |
1 files changed, 2893 insertions, 0 deletions
diff --git a/src/SDCCpeeph.c b/src/SDCCpeeph.c new file mode 100644 index 0000000..d607675 --- /dev/null +++ b/src/SDCCpeeph.c @@ -0,0 +1,2893 @@ +/*------------------------------------------------------------------------- + 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) ; +} |
