421 lines
13 KiB
C
421 lines
13 KiB
C
/*-------------------------------------------------------------------------
|
|
|
|
SDCCptropt.c - source file for pointer arithmetic Optimizations
|
|
|
|
Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998)
|
|
|
|
This program is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU General Public License as published by the
|
|
Free Software Foundation; either version 2, or (at your option) any
|
|
later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
In other words, you are welcome to use, share and improve this program.
|
|
You are forbidden to forbid anyone else to use, share and improve
|
|
what you give them. Help stamp out software-hoarding!
|
|
-------------------------------------------------------------------------*/
|
|
|
|
#include "common.h"
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/* findPointerGetSet - find the pointer get or set for a operand */
|
|
/*-----------------------------------------------------------------------*/
|
|
static iCode *
|
|
findPointerGetSet (iCode * sic, operand * op)
|
|
{
|
|
iCode *ic = sic;
|
|
|
|
for (; ic; ic = ic->next)
|
|
{
|
|
if ((POINTER_SET (ic) && isOperandEqual (op, IC_RESULT (ic))) || (POINTER_GET (ic) && isOperandEqual (op, IC_LEFT (ic))))
|
|
return ic;
|
|
|
|
/* if we find any other usage or definition of op null */
|
|
if (IC_RESULT (ic) && isOperandEqual (IC_RESULT (ic), op))
|
|
return NULL;
|
|
|
|
if (IC_RIGHT (ic) && isOperandEqual (IC_RIGHT (ic), op))
|
|
return NULL;
|
|
|
|
if (IC_LEFT (ic) && isOperandEqual (IC_LEFT (ic), op))
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
pattern1 (iCode * sic, eBBlock * ebb)
|
|
{
|
|
/* this is what we do. look for sequences like
|
|
|
|
iTempX := _SOME_POINTER_;
|
|
iTempY := _SOME_POINTER_ + nn ; nn = sizeof (pointed to object) sic->next
|
|
_SOME_POINTER_ := iTempY; sic->next->next
|
|
either
|
|
iTempZ := @[iTempX]; sic->next->next->next
|
|
or
|
|
*(iTempX) := ..something.. sic->next->next->next
|
|
if we find this then transform this to
|
|
iTempX := _SOME_POINTER_;
|
|
either
|
|
iTempZ := @[iTempX];
|
|
or
|
|
*(iTempX) := ..something..
|
|
iTempY := _SOME_POINTER_ + nn ; nn = sizeof (pointed to object)
|
|
_SOME_POINTER_ := iTempY; */
|
|
|
|
/* sounds simple enough so let's start , here I use negative
|
|
tests all the way to return if any test fails */
|
|
iCode *pgs, *sh, *st;
|
|
|
|
if (!(sic->next && sic->next->next && sic->next->next->next))
|
|
return 0;
|
|
if (sic->next->op != '+' && sic->next->op != '-')
|
|
return 0;
|
|
if (!(sic->next->next->op == '=' && !POINTER_SET (sic->next->next)))
|
|
return 0;
|
|
if (!isOperandEqual (IC_LEFT (sic->next), IC_RIGHT (sic)) || !IS_OP_LITERAL (IC_RIGHT (sic->next)))
|
|
return 0;
|
|
if (operandLitValue (IC_RIGHT (sic->next)) != getSize (operandType (IC_RIGHT (sic))->next))
|
|
return 0;
|
|
if (!isOperandEqual (IC_RESULT (sic->next->next), IC_RIGHT (sic)))
|
|
return 0;
|
|
if (!isOperandEqual (IC_RESULT (sic->next), IC_RIGHT (sic->next->next)))
|
|
return 0;
|
|
if (!(pgs = findPointerGetSet (sic->next->next, IC_RESULT (sic))))
|
|
return 0;
|
|
|
|
/* found the pattern .. now do the transformation */
|
|
sh = sic->next;
|
|
st = sic->next->next;
|
|
|
|
/* take the two out of the chain */
|
|
sic->next = st->next;
|
|
st->next->prev = sic;
|
|
|
|
/* and put them after the pointer get/set icode */
|
|
if ((st->next = pgs->next))
|
|
st->next->prev = st;
|
|
if (!st->next)
|
|
ebb->ech = st;
|
|
pgs->next = sh;
|
|
sh->prev = pgs;
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
pattern2 (iCode * sic, eBBlock * ebb)
|
|
{
|
|
/* this is what we do. look for sequences like
|
|
|
|
iTempX := _SOME_POINTER_;
|
|
iTempY := _SOME_POINTER_ + nn ; nn = sizeof (pointed to object) sic->next
|
|
iTempK := iTempY; sic->next->next
|
|
_SOME_POINTER_ := iTempK; sic->next->next->next
|
|
either
|
|
iTempZ := @[iTempX]; sic->next->next->next->next
|
|
or
|
|
*(iTempX) := ..something.. sic->next->next->next->next
|
|
if we find this then transform this to
|
|
iTempX := _SOME_POINTER_;
|
|
either
|
|
iTempZ := @[iTempX];
|
|
or
|
|
*(iTempX) := ..something..
|
|
iTempY := _SOME_POINTER_ + nn ; nn = sizeof (pointed to object)
|
|
iTempK := iTempY;
|
|
_SOME_POINTER_ := iTempK; */
|
|
|
|
/* sounds simple enough so let's start , here I use negative
|
|
tests all the way to return if any test fails */
|
|
iCode *pgs, *sh, *st;
|
|
|
|
if (!(sic->next && sic->next->next && sic->next->next->next && sic->next->next->next->next))
|
|
return 0;
|
|
|
|
/* yes I can OR them together and make one large if... but I have
|
|
simple mind and like to keep things simple & readable */
|
|
if (!(sic->next->op == '+' || sic->next->op == '-'))
|
|
return 0;
|
|
if (!isOperandEqual (IC_RIGHT (sic), IC_LEFT (sic->next)))
|
|
return 0;
|
|
if (!IS_OP_LITERAL (IC_RIGHT (sic->next)))
|
|
return 0;
|
|
if (operandLitValue (IC_RIGHT (sic->next)) != getSize (operandType (IC_RIGHT (sic))->next))
|
|
return 0;
|
|
if (!IS_ASSIGN_ICODE (sic->next->next))
|
|
return 0;
|
|
if (!isOperandEqual (IC_RIGHT (sic->next->next), IC_RESULT (sic->next)))
|
|
return 0;
|
|
if (!IS_ASSIGN_ICODE (sic->next->next->next))
|
|
return 0;
|
|
if (!isOperandEqual (IC_RIGHT (sic->next->next->next), IC_RESULT (sic->next->next)))
|
|
return 0;
|
|
if (!isOperandEqual (IC_RESULT (sic->next->next->next), IC_LEFT (sic->next)))
|
|
return 0;
|
|
if (!(pgs = findPointerGetSet (sic->next->next->next, IC_RESULT (sic))))
|
|
return 0;
|
|
|
|
/* found the pattern .. now do the transformation */
|
|
sh = sic->next;
|
|
st = sic->next->next->next;
|
|
|
|
/* take the three out of the chain */
|
|
sic->next = st->next;
|
|
st->next->prev = sic;
|
|
|
|
/* and put them after the pointer get/set icode */
|
|
if ((st->next = pgs->next))
|
|
st->next->prev = st;
|
|
if (!st->next)
|
|
ebb->ech = st;
|
|
pgs->next = sh;
|
|
sh->prev = pgs;
|
|
return 1;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/* ptrPostIncDecOpts - will do some pointer post increment optimizations */
|
|
/* this will help register allocation amongst others */
|
|
/*-----------------------------------------------------------------------*/
|
|
void
|
|
ptrPostIncDecOpt (iCode * sic, eBBlock * ebb)
|
|
{
|
|
if (pattern1 (sic, ebb))
|
|
return;
|
|
pattern2 (sic, ebb);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/* addPattern1 - transform addition to pointer of variables */
|
|
/*-----------------------------------------------------------------------*/
|
|
static int
|
|
addPattern1 (iCode * ic)
|
|
{
|
|
iCode *dic;
|
|
operand *tmp;
|
|
/* transform :
|
|
iTempAA = iTempBB + iTempCC
|
|
iTempDD = iTempAA + CONST
|
|
to
|
|
iTempAA = iTempBB + CONST
|
|
iTempDD = iTempAA + iTempCC
|
|
*/
|
|
if (!isOperandLiteral (IC_RIGHT (ic)))
|
|
return 0;
|
|
if ((dic = findBackwardDef (IC_LEFT (ic), ic->prev)) == NULL)
|
|
return 0;
|
|
if (bitVectnBitsOn (OP_SYMBOL (IC_RESULT (dic))->uses) > 1)
|
|
return 0;
|
|
if (dic->op != '+')
|
|
return 0;
|
|
tmp = IC_RIGHT (ic);
|
|
IC_RIGHT (ic) = IC_RIGHT (dic);
|
|
IC_RIGHT (dic) = tmp;
|
|
return 1;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/* ptrAddition - optimize pointer additions */
|
|
/*-----------------------------------------------------------------------*/
|
|
int
|
|
ptrAddition (iCode * sic)
|
|
{
|
|
if (addPattern1 (sic))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/* ptrBaseRematSym - find the base symbol of a remat. pointer */
|
|
/*--------------------------------------------------------------------*/
|
|
symbol *
|
|
ptrBaseRematSym (symbol * ptrsym)
|
|
{
|
|
iCode *ric;
|
|
|
|
if (!ptrsym->remat)
|
|
return NULL;
|
|
|
|
ric = ptrsym->rematiCode;
|
|
while (ric)
|
|
{
|
|
if (ric->op == '+' || ric->op == '-')
|
|
ric = OP_SYMBOL (IC_LEFT (ric))->rematiCode;
|
|
else if (IS_CAST_ICODE (ric))
|
|
ric = OP_SYMBOL (IC_RIGHT (ric))->rematiCode;
|
|
else
|
|
break;
|
|
}
|
|
|
|
if (ric && IS_SYMOP (IC_LEFT (ric)))
|
|
return OP_SYMBOL (IC_LEFT (ric));
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/* ptrPseudoSymSafe - check to see if the conversion of the result of */
|
|
/* a pointerGet of a rematerializable pointer to a pseudo symbol is */
|
|
/* safe. Returns true if safe, or false if hazards were detected. */
|
|
/*--------------------------------------------------------------------*/
|
|
int
|
|
ptrPseudoSymSafe (symbol * sym, iCode * dic)
|
|
{
|
|
symbol *ptrsym;
|
|
symbol *basesym;
|
|
iCode *ric;
|
|
iCode *ic;
|
|
int ptrsymDclType;
|
|
//int isGlobal;
|
|
|
|
assert (POINTER_GET (dic));
|
|
|
|
/* Can't if spills to this symbol are prohibited */
|
|
if (sym->noSpilLoc)
|
|
return 0;
|
|
|
|
/* Get the pointer */
|
|
if (!IS_SYMOP (IC_LEFT (dic)))
|
|
return 0;
|
|
ptrsym = OP_SYMBOL (IC_LEFT (dic));
|
|
|
|
/* Must be a rematerializable pointer */
|
|
if (!ptrsym->remat)
|
|
return 0;
|
|
|
|
/* The pointer type must be uncasted */
|
|
if (IS_CAST_ICODE (ptrsym->rematiCode))
|
|
return 0;
|
|
|
|
/* The symbol's live range must not preceed its definition */
|
|
if (dic->seq > sym->liveFrom)
|
|
return 0;
|
|
|
|
/* Ok, this is a good candidate for a pseudo symbol. */
|
|
/* However, we must check for two hazards: */
|
|
/* 1) The symbol's live range must not include a CALL */
|
|
/* or PCALL iCode. */
|
|
/* 2) The symbol's live range must not include any */
|
|
/* writes to the variable the pointer rematerializes */
|
|
/* within (to avoid aliasing problems) */
|
|
|
|
/* Find the base symbol the rematerialization is based on */
|
|
ric = ptrsym->rematiCode;
|
|
while (ric->op == '+' || ric->op == '-')
|
|
ric = OP_SYMBOL (IC_LEFT (ric))->rematiCode;
|
|
if (IS_CAST_ICODE (ric))
|
|
return 0;
|
|
basesym = OP_SYMBOL (IC_LEFT (ric));
|
|
|
|
//isGlobal = !basesym->islocal && !basesym->ismyparm;
|
|
ptrsymDclType = aggrToPtrDclType (ptrsym->type, FALSE);
|
|
|
|
ic = dic->next;
|
|
while (ic && ic->seq <= sym->liveTo)
|
|
{
|
|
if (!(SKIP_IC3 (ic) || ic->op == IFX))
|
|
{
|
|
/* Check for hazard #1 */
|
|
if ((ic->op == CALL || ic->op == PCALL) /* && isGlobal */ )
|
|
{
|
|
if (ic->seq <= sym->liveTo)
|
|
return 0;
|
|
}
|
|
/* Check for hazard #2 */
|
|
else if (POINTER_SET (ic))
|
|
{
|
|
symbol *ptrsym2;
|
|
|
|
if (!IS_SYMOP (IC_RESULT (ic)))
|
|
return 0;
|
|
|
|
ptrsym2 = OP_SYMBOL (IC_RESULT (ic));
|
|
|
|
if (ptrsym2->remat)
|
|
{
|
|
/* Must not be the same base symbol */
|
|
if (basesym == ptrBaseRematSym (ptrsym2))
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
int ptrsym2DclType = aggrToPtrDclType (ptrsym2->type, FALSE);
|
|
|
|
/* Pointer must have no memory space in common */
|
|
if (ptrsym2DclType == ptrsymDclType || ptrsym2DclType == GPOINTER || ptrsymDclType == GPOINTER)
|
|
return 0;
|
|
}
|
|
}
|
|
else if (IC_RESULT (ic))
|
|
{
|
|
symbol *rsym;
|
|
|
|
if (!IS_SYMOP (IC_RESULT (ic)))
|
|
return 0;
|
|
|
|
rsym = OP_SYMBOL (IC_RESULT (ic));
|
|
|
|
/* Make sure there is no conflict with another pseudo symbol */
|
|
if (rsym->psbase == basesym)
|
|
return 0;
|
|
if (rsym->isspilt && rsym->usl.spillLoc)
|
|
rsym = rsym->usl.spillLoc;
|
|
if (rsym->psbase == basesym)
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (ic->seq == sym->liveTo)
|
|
break;
|
|
ic = ic->next;
|
|
}
|
|
|
|
/* If the live range went past the end of the defining basic */
|
|
/* block, then a full analysis is too complicated to attempt */
|
|
/* here. To be safe, we must assume the worst. */
|
|
if (!ic)
|
|
return 0;
|
|
|
|
/* Ok, looks safe */
|
|
return 1;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/* ptrPseudoSymConvert - convert the result of a pointerGet to a */
|
|
/* pseudo symbol. The pointer must be rematerializable. */
|
|
/*--------------------------------------------------------------------*/
|
|
void
|
|
ptrPseudoSymConvert (symbol * sym, iCode * dic, const char *name)
|
|
{
|
|
symbol *psym = newSymbol (name, 1);
|
|
psym->psbase = ptrBaseRematSym (OP_SYMBOL (IC_LEFT (dic)));
|
|
psym->type = sym->type;
|
|
psym->etype = psym->psbase->etype;
|
|
|
|
strcpy (psym->rname, psym->name);
|
|
sym->isspilt = 1;
|
|
sym->usl.spillLoc = psym;
|
|
#if 0 // an alternative fix for bug #480076
|
|
/* now this is a useless assignment to itself */
|
|
remiCodeFromeBBlock (ebbs, dic);
|
|
#else
|
|
/* now this really is an assignment to itself, make it so;
|
|
it will be optimized out later */
|
|
dic->op = '=';
|
|
ReplaceOpWithCheaperOp (&IC_RIGHT (dic), IC_RESULT (dic));
|
|
IC_LEFT (dic) = NULL;
|
|
#endif
|
|
}
|