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/SDCCopt.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/SDCCopt.c')
| -rw-r--r-- | src/SDCCopt.c | 3328 |
1 files changed, 3328 insertions, 0 deletions
diff --git a/src/SDCCopt.c b/src/SDCCopt.c new file mode 100644 index 0000000..35681e4 --- /dev/null +++ b/src/SDCCopt.c @@ -0,0 +1,3328 @@ +/*------------------------------------------------------------------------- + SDCCopt.c - calls all the optimizations routines and does some of the + hackier transformations, these include translating iCodes + to function calls and replacing local variables with their + register equivalents etc. Also contains the driver routine + for dead code elimination + + 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 <math.h> +#include "common.h" +#include "dbuf_string.h" + +/*-----------------------------------------------------------------*/ +/* global variables */ +int cseDefNum = 0; + +char flowChanged = 0; + + +/*-----------------------------------------------------------------*/ +/* printSymName - prints the symbol names */ +/*-----------------------------------------------------------------*/ +int +printSymName (void *vsym) +{ + symbol *sym = vsym; + fprintf (stdout, " %s ", sym->name); + return 0; +} + +/*-----------------------------------------------------------------*/ +/* cnvToFcall - does the actual conversion to function call */ +/*-----------------------------------------------------------------*/ +static void +cnvToFcall (iCode * ic, eBBlock * ebp) +{ + iCode *ip; + iCode *newic; + operand *left; + operand *right; + symbol *func = NULL; + char *filename = ic->filename; + int lineno = ic->lineno; + int bytesPushed=0; + + ip = ic->next; /* insertion point */ + /* remove it from the iCode */ + remiCodeFromeBBlock (ebp, ic); + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + + if (IS_SYMOP (left)) + bitVectUnSetBit (OP_USES (left), ic->key); + if (IS_SYMOP (right)) + bitVectUnSetBit (OP_USES (right), ic->key); + if (IS_SYMOP (IC_RESULT (ic))) + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + + if (IS_FLOAT (operandType (right))) + { + switch (ic->op) + { + case '+': + func = fsadd; + break; + case '-': + func = fssub; + break; + case '/': + func = fsdiv; + break; + case '*': + func = fsmul; + break; + case EQ_OP: + func = fseq; + break; + case NE_OP: + func = fsneq; + break; + case '<': + func = fslt; + break; + case '>': + { + operand *tmp = right; + right = left; + left = tmp; + func = fslt; + break; + } + } + } + else if (IS_FIXED16X16 (operandType (right))) + { + switch (ic->op) + { + case '+': + func = fps16x16_add; + break; + case '-': + func = fps16x16_sub; + break; + case '/': + func = fps16x16_div; + break; + case '*': + func = fps16x16_mul; + break; + case EQ_OP: + func = fps16x16_eq; + break; + case NE_OP: + func = fps16x16_neq; + break; + case '<': + func = fps16x16_lt; + break; + case '>': + func = fps16x16_gt; + break; + case LE_OP: + func = fps16x16_lteq; + break; + case GE_OP: + func = fps16x16_gteq; + break; + } + } + + /* if float support routines NOT compiled as reentrant */ + if (!options.float_rent) + { + /* first one */ + if (IS_REGPARM (FUNC_ARGS(func->type)->etype)) + { + newic = newiCode (SEND, left, NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->etype); + } + else + { + newic = newiCode ('=', NULL, left); + IC_RESULT (newic) = operandFromValue (FUNC_ARGS(func->type)); + } + + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (left)) + OP_USES (left) = bitVectSetBit (OP_USES (left), newic->key); + + /* second one */ + if (IS_REGPARM (FUNC_ARGS(func->type)->next->etype)) + { + newic = newiCode (SEND, right, NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->next->etype); + } + else + { + newic = newiCode ('=', NULL, right); + IC_RESULT (newic) = operandFromValue (FUNC_ARGS(func->type)->next); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (right)) + OP_USES (right) = bitVectSetBit (OP_USES (right), newic->key); + } + else + { + /* push right */ + if (IS_REGPARM (FUNC_ARGS(func->type)->next->etype)) + { + newic = newiCode (SEND, right, NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->next->etype); + } + else + { + newic = newiCode (IPUSH, right, NULL); + newic->parmPush = 1; + bytesPushed += getSize(operandType(right)); + } + + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (right)) + OP_USES (right) = bitVectSetBit (OP_USES (right), newic->key); + + /* insert push left */ + if (IS_REGPARM (FUNC_ARGS(func->type)->etype)) + { + newic = newiCode (SEND, left, NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->etype); + } + else + { + newic = newiCode (IPUSH, left, NULL); + newic->parmPush = 1; + bytesPushed += getSize(operandType(left)); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (left)) + OP_USES (left) = bitVectSetBit (OP_USES (left), newic->key); + } + /* insert the call */ + newic = newiCode (CALL, operandFromSymbol (func), NULL); + IC_RESULT (newic) = IC_RESULT (ic); + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + OP_DEFS (IC_RESULT (newic)) = bitVectSetBit (OP_DEFS (IC_RESULT (newic)), newic->key); + newic->filename = filename; + newic->lineno = lineno; + newic->parmBytes += bytesPushed; + ebp->hasFcall = 1; + if (currFunc) + FUNC_HASFCALL (currFunc->type) = 1; + + if (TARGET_PIC_LIKE) + { + /* normally these functions aren't marked external, so we can use their + * _extern field to mark as already added to symbol table */ + + if (!SPEC_EXTR(func->etype)) + { + memmap *seg = SPEC_OCLS(OP_SYMBOL(IC_LEFT(newic))->etype); + + SPEC_EXTR(func->etype) = 1; + seg = SPEC_OCLS( func->etype ); + addSet(&seg->syms, func); + } + } + + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); +} + +/*-----------------------------------------------------------------*/ +/* cnvToFloatCast - converts casts to floats to function calls */ +/*-----------------------------------------------------------------*/ +static void +cnvToFloatCast (iCode * ic, eBBlock * ebp) +{ + iCode *ip, *newic; + symbol *func = NULL; + sym_link *type = operandType (IC_RIGHT (ic)); + int linenno = ic->lineno; + int bwd, su; + int bytesPushed=0; + + ip = ic->next; + /* remove it from the iCode */ + remiCodeFromeBBlock (ebp, ic); + if (IS_SYMOP (IC_RIGHT (ic))) + bitVectUnSetBit (OP_USES (IC_RIGHT (ic)), ic->key); + if (IS_SYMOP (IC_RESULT (ic))) + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + + /* depending on the type */ + for (bwd = 0; bwd < 4; bwd++) + { + for (su = 0; su < 2; su++) + { + if (compareType (type, multypes[bwd][su]) == 1) + { + func = conv[0][bwd][su]; + goto found; + } + } + } + + if (compareType (type, fixed16x16Type) == 1) + { + func = fp16x16conv[0][4][0]; + goto found; + } + + if (IS_BOOLEAN (type)) + { + wassert(multypes[0][1] == UCHARTYPE); + func = conv[0][0][1]; + goto found; + } + + assert (0); +found: + + /* if float support routines NOT compiled as reentrant */ + if (!options.float_rent) + { + /* first one */ + if (IS_REGPARM (FUNC_ARGS(func->type)->etype)) + { + newic = newiCode (SEND, IC_RIGHT (ic), NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->etype); + } + else + { + newic = newiCode ('=', NULL, IC_RIGHT (ic)); + IC_RESULT (newic) = operandFromValue (FUNC_ARGS(func->type)); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = linenno; + if (IS_SYMOP (IC_RIGHT (ic))) + OP_USES (IC_RIGHT (ic)) = bitVectSetBit (OP_USES (IC_RIGHT (ic)), newic->key); + } + else + { + /* push the left */ + if (IS_REGPARM (FUNC_ARGS(func->type)->etype)) + { + newic = newiCode (SEND, IC_RIGHT (ic), NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->etype); + } + else + { + newic = newiCode (IPUSH, IC_RIGHT (ic), NULL); + newic->parmPush = 1; + bytesPushed += getSize(operandType(IC_RIGHT(ic))); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = linenno; + if (IS_SYMOP (IC_RIGHT (ic))) + OP_USES (IC_RIGHT (ic)) = bitVectSetBit (OP_USES (IC_RIGHT (ic)), newic->key); + } + + /* make the call */ + newic = newiCode (CALL, operandFromSymbol (func), NULL); + IC_RESULT (newic) = IC_RESULT (ic); + newic->parmBytes+=bytesPushed; + ebp->hasFcall = 1; + if (currFunc) + FUNC_HASFCALL (currFunc->type) = 1; + + if (TARGET_PIC_LIKE) + { + /* normally these functions aren't marked external, so we can use their + * _extern field to marked as already added to symbol table */ + + if (!SPEC_EXTR(func->etype)) + { + memmap *seg = SPEC_OCLS(OP_SYMBOL(IC_LEFT(newic))->etype); + + SPEC_EXTR(func->etype) = 1; + seg = SPEC_OCLS( func->etype ); + addSet(&seg->syms, func); + } + } + + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = linenno; + if (IS_SYMOP (IC_RESULT (ic))) + OP_DEFS (IC_RESULT (ic)) = bitVectSetBit (OP_DEFS (IC_RESULT (ic)), newic->key); +} + +/*----------------------------------------------------------------------*/ +/* cnvToFixed16x16Cast - converts casts to fixed16x16 to function calls */ +/*----------------------------------------------------------------------*/ +static void +cnvToFixed16x16Cast (iCode * ic, eBBlock * ebp) +{ + iCode *ip, *newic; + symbol *func = NULL; + sym_link *type = operandType (IC_RIGHT (ic)); + int linenno = ic->lineno; + int bwd, su; + int bytesPushed=0; + + ip = ic->next; + /* remove it from the iCode */ + remiCodeFromeBBlock (ebp, ic); + if (IS_SYMOP (IC_RIGHT (ic))) + bitVectUnSetBit (OP_USES (IC_RIGHT (ic)), ic->key); + if (IS_SYMOP (IC_RESULT (ic))) + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + + /* depending on the type */ + for (bwd = 0; bwd < 4; bwd++) + { + for (su = 0; su < 2; su++) + { + if (compareType (type, multypes[bwd][su]) == 1) + { + func = fp16x16conv[0][bwd][su]; + goto found; + } + } + } + assert (0); +found: + + /* if float support routines NOT compiled as reentrant */ + if (!options.float_rent) + { + /* first one */ + if (IS_REGPARM (FUNC_ARGS(func->type)->etype)) + { + newic = newiCode (SEND, IC_RIGHT (ic), NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->etype); + } + else + { + newic = newiCode ('=', NULL, IC_RIGHT (ic)); + IC_RESULT (newic) = operandFromValue (FUNC_ARGS(func->type)); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = linenno; + if (IS_SYMOP (IC_RIGHT (ic))) + OP_USES (IC_RIGHT (ic)) = bitVectSetBit (OP_USES (IC_RIGHT (ic)), newic->key); + } + else + { + /* push the left */ + if (IS_REGPARM (FUNC_ARGS(func->type)->etype)) + { + newic = newiCode (SEND, IC_RIGHT (ic), NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->etype); + } + else + { + newic = newiCode (IPUSH, IC_RIGHT (ic), NULL); + newic->parmPush = 1; + bytesPushed += getSize(operandType(IC_RIGHT(ic))); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = linenno; + if (IS_SYMOP (IC_RIGHT (ic))) + OP_USES (IC_RIGHT (ic)) = bitVectSetBit (OP_USES (IC_RIGHT (ic)), newic->key); + } + + /* make the call */ + newic = newiCode (CALL, operandFromSymbol (func), NULL); + IC_RESULT (newic) = IC_RESULT (ic); + newic->parmBytes+=bytesPushed; + ebp->hasFcall = 1; + if (currFunc) + FUNC_HASFCALL (currFunc->type) = 1; + + if (TARGET_PIC_LIKE) + { + /* normally these functions aren't marked external, so we can use their + * _extern field to marked as already added to symbol table */ + + if (!SPEC_EXTR(func->etype)) + { + memmap *seg = SPEC_OCLS(OP_SYMBOL(IC_LEFT(newic))->etype); + + SPEC_EXTR(func->etype) = 1; + seg = SPEC_OCLS( func->etype ); + addSet(&seg->syms, func); + } + } + + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = linenno; + if (IS_SYMOP (IC_RESULT (ic))) + OP_DEFS (IC_RESULT (ic)) = bitVectSetBit (OP_DEFS (IC_RESULT (ic)), newic->key); +} + +/*-----------------------------------------------------------------*/ +/* cnvFromFloatCast - converts casts From floats to function calls */ +/*-----------------------------------------------------------------*/ +static void +cnvFromFloatCast (iCode * ic, eBBlock * ebp) +{ + iCode *ip, *newic; + symbol *func = NULL; + sym_link *type = operandType (IC_LEFT (ic)); + char *filename = ic->filename; + int lineno = ic->lineno; + int bwd, su; + int bytesPushed=0; + + ip = ic->next; + /* remove it from the iCode */ + remiCodeFromeBBlock (ebp, ic); + if (IS_SYMOP (IC_RIGHT (ic))) + bitVectUnSetBit (OP_USES (IC_RIGHT (ic)), ic->key); + if (IS_SYMOP (IC_RESULT (ic))) + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + + /* depending on the type */ + for (bwd = 0; bwd < 4; bwd++) + { + for (su = 0; su < 2; su++) + { + if (compareType (type, multypes[bwd][su]) == 1) + { + func = conv[1][bwd][su]; + goto found; + } + } + } + assert (0); +found: + + /* if float support routines NOT compiled as reentrant */ + if (!options.float_rent) + { + /* first one */ + if (IS_REGPARM (FUNC_ARGS(func->type)->etype)) + { + newic = newiCode (SEND, IC_RIGHT (ic), NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->etype); + } + else + { + newic = newiCode ('=', NULL, IC_RIGHT (ic)); + IC_RESULT (newic) = operandFromValue (FUNC_ARGS(func->type)); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (IC_RIGHT (ic))) + OP_USES (IC_RIGHT (ic)) = bitVectSetBit (OP_USES (IC_RIGHT (ic)), newic->key); + } + else + { + /* push the left */ + if (IS_REGPARM (FUNC_ARGS(func->type)->etype)) + { + newic = newiCode (SEND, IC_RIGHT (ic), NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->etype); + } + else + { + newic = newiCode (IPUSH, IC_RIGHT (ic), NULL); + newic->parmPush = 1; + bytesPushed += getSize(operandType(IC_RIGHT(ic))); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (IC_RIGHT (ic))) + OP_USES (IC_RIGHT (ic)) = bitVectSetBit (OP_USES (IC_RIGHT (ic)), newic->key); + } + + /* make the call */ + newic = newiCode (CALL, operandFromSymbol (func), NULL); + IC_RESULT (newic) = IC_RESULT (ic); + newic->parmBytes+=bytesPushed; + ebp->hasFcall = 1; + if (currFunc) + FUNC_HASFCALL (currFunc->type) = 1; + + if (TARGET_PIC_LIKE) + { + /* normally these functions aren't marked external, so we can use their + * _extern field to marked as already added to symbol table */ + + if (!SPEC_EXTR(func->etype)) + { + memmap *seg = SPEC_OCLS(OP_SYMBOL(IC_LEFT(newic))->etype); + + SPEC_EXTR(func->etype) = 1; + seg = SPEC_OCLS( func->etype ); + addSet(&seg->syms, func); + } + } + + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (IC_RESULT (ic))) + OP_DEFS (IC_RESULT (ic)) = bitVectSetBit (OP_DEFS (IC_RESULT (ic)), newic->key); +} + +/*--------------------------------------------------------------------------*/ +/* cnvFromFixed16x16Cast - converts casts from fixed16x16 to function calls */ +/*--------------------------------------------------------------------------*/ +static void +cnvFromFixed16x16Cast (iCode * ic, eBBlock * ebp) +{ + iCode *ip, *newic; + symbol *func = NULL; + sym_link *type = operandType (IC_LEFT (ic)); + char *filename = ic->filename; + int lineno = ic->lineno; + int bwd, su; + int bytesPushed=0; + + ip = ic->next; + /* remove it from the iCode */ + remiCodeFromeBBlock (ebp, ic); + if (IS_SYMOP (IC_RIGHT (ic))) + bitVectUnSetBit (OP_USES (IC_RIGHT (ic)), ic->key); + if (IS_SYMOP (IC_RESULT (ic))) + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + + /* depending on the type */ + for (bwd = 0; bwd < 4; bwd++) + { + for (su = 0; su < 2; su++) + { + if (compareType (type, multypes[bwd][su]) == 1) + { + func = fp16x16conv[1][bwd][su]; + goto found; + } + } + } + + if (compareType (type, floatType) == 1) + { + func = fp16x16conv[1][4][0]; + goto found; + } + + assert (0); +found: + + /* if float support routines NOT compiled as reentrant */ + if (!options.float_rent) + { + /* first one */ + if (IS_REGPARM (FUNC_ARGS(func->type)->etype)) + { + newic = newiCode (SEND, IC_RIGHT (ic), NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->etype); + } + else + { + newic = newiCode ('=', NULL, IC_RIGHT (ic)); + IC_RESULT (newic) = operandFromValue (FUNC_ARGS(func->type)); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (IC_RIGHT (ic))) + OP_USES (IC_RIGHT (ic)) = bitVectSetBit (OP_USES (IC_RIGHT (ic)), newic->key); + } + else + { + /* push the left */ + if (IS_REGPARM (FUNC_ARGS(func->type)->etype)) + { + newic = newiCode (SEND, IC_RIGHT (ic), NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->etype); + } + else + { + newic = newiCode (IPUSH, IC_RIGHT (ic), NULL); + newic->parmPush = 1; + bytesPushed += getSize(operandType(IC_RIGHT(ic))); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (IC_RIGHT (ic))) + OP_USES (IC_RIGHT (ic)) = bitVectSetBit (OP_USES (IC_RIGHT (ic)), newic->key); + } + + /* make the call */ + newic = newiCode (CALL, operandFromSymbol (func), NULL); + IC_RESULT (newic) = IC_RESULT (ic); + newic->parmBytes+=bytesPushed; + ebp->hasFcall = 1; + if (currFunc) + FUNC_HASFCALL (currFunc->type) = 1; + + if (TARGET_PIC_LIKE) + { + /* normally these functions aren't marked external, so we can use their + * _extern field to marked as already added to symbol table */ + + if (!SPEC_EXTR(func->etype)) + { + memmap *seg = SPEC_OCLS(OP_SYMBOL(IC_LEFT(newic))->etype); + + SPEC_EXTR(func->etype) = 1; + seg = SPEC_OCLS( func->etype ); + addSet(&seg->syms, func); + } + } + + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (IC_RESULT (ic))) + OP_DEFS (IC_RESULT (ic)) = bitVectSetBit (OP_DEFS (IC_RESULT (ic)), newic->key); +} + +extern operand *geniCodeRValue (operand *, bool); + +/*-----------------------------------------------------------------*/ +/* convilong - converts int or long mults or divs to fcalls */ +/*-----------------------------------------------------------------*/ +static void +convilong (iCode * ic, eBBlock * ebp) +{ + int op = ic->op; + symbol *func = NULL; + iCode *ip = ic->next; + iCode *newic; + char *filename = ic->filename; + int lineno = ic->lineno; + int bwd; + int su; + int bytesPushed=0; + operand *left; + operand *right; + sym_link *leftType = operandType (IC_LEFT (ic)); + sym_link *rightType = operandType (IC_RIGHT (ic)); + + remiCodeFromeBBlock (ebp, ic); + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + + if (IS_SYMOP (left)) + bitVectUnSetBit (OP_USES (left), ic->key); + if (IS_SYMOP (right)) + bitVectUnSetBit (OP_USES (right), ic->key); + + if (op == '*' && (muls16tos32[0] || muls16tos32[1]) && + (IS_SYMOP (left) && bitVectnBitsOn (OP_DEFS (left)) == 1 && bitVectnBitsOn (OP_USES (left)) == 0 || IS_OP_LITERAL (left) && operandLitValue (left) < 32768 && operandLitValue (left) >= -32768) && + (IS_SYMOP (right) && bitVectnBitsOn (OP_DEFS (right)) == 1 && bitVectnBitsOn (OP_USES (right)) == 0 || IS_OP_LITERAL (right) && operandLitValue (right) < 32768 && operandLitValue (right) >= -32768) && + getSize (leftType) == 4 && getSize (rightType) == 4) + { + iCode *lic = IS_SYMOP (left) ? hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (left))) : 0; + iCode *ric = IS_SYMOP (right) ? hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (right))) : 0; + + if ((!lic || lic->op == CAST && getSize (operandType (IC_RIGHT (lic))) == 2 && SPEC_USIGN (operandType (IC_RIGHT (lic))) == SPEC_USIGN (operandType (left))) && + (!ric || ric->op == CAST && getSize (operandType (IC_RIGHT (ric))) == 2 && SPEC_USIGN (operandType (IC_RIGHT (ric))) == SPEC_USIGN (operandType (right)))) + { + func = muls16tos32[SPEC_USIGN (operandType (left))]; + + if (lic) + { + lic->op = '='; + OP_SYMBOL (left)->type = newIntLink(); + } + else + IC_LEFT (ic) = operandFromValue (valCastLiteral (newIntLink(), operandLitValue (left), operandLitValue (left))); + + if (ric) + { + ric->op = '='; + OP_SYMBOL (right)->type = newIntLink(); + } + else + IC_RIGHT (ic) = operandFromValue (valCastLiteral (newIntLink(), operandLitValue (right), operandLitValue (right))); + + if (func) + goto found; + } + } + + if (getSize (leftType) == 1 && getSize (rightType) == 1) + { + int muldivmod; + + if (op == '*') + muldivmod = 0; + else if (op == '/') + muldivmod = 1; + else if (op == '%') + muldivmod = 2; + else + muldivmod = -1; + + for (su = 0; su < 4 && muldivmod >= 0; su++) + { + if ((compareType (leftType, multypes[0][su%2]) == 1) && + (compareType (rightType, multypes[0][su/2]) == 1)) + { + func = muldiv[muldivmod][0][su]; + goto found; + } + } + } + + /* depending on the type */ + for (bwd = 0; bwd < 4; bwd++) + { + for (su = 0; su < 2; su++) + { + if (compareType (leftType, multypes[bwd][su]) == 1) + { + if ((op=='*' || op=='/' || op=='%')) + { + int ret = compareType (rightType, multypes[bwd][su]); + if (ret != 1) + { + assert(0); + } + } + if (op == '*') + func = muldiv[0][bwd][su]; + else if (op == '/') + func = muldiv[1][bwd][su]; + else if (op == '%') + func = muldiv[2][bwd][su]; + else if (op == RRC) + func = rlrr[1][bwd][su]; + else if (op == RLC) + func = rlrr[0][bwd][su]; + else if (op == RIGHT_OP) + func = rlrr[1][bwd][su]; + else if (op == LEFT_OP) + func = rlrr[0][bwd][su]; + else + assert (0); + goto found; + } + } + } + werrorfl (filename, lineno, E_INVALID_OP, ""); + return; +found: + /* if int & long support routines NOT compiled as reentrant */ + if (!options.intlong_rent) + { + /* first one */ + if (IS_REGPARM (FUNC_ARGS(func->type)->etype)) + { + newic = newiCode (SEND, IC_LEFT (ic), NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->etype); + } + else + { + newic = newiCode ('=', NULL, IC_LEFT (ic)); + IC_RESULT (newic) = operandFromValue (FUNC_ARGS(func->type)); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (left)) + OP_USES (left) = bitVectSetBit (OP_USES (left), newic->key); + + /* second one */ + if (IS_REGPARM (FUNC_ARGS(func->type)->next->etype)) + { + newic = newiCode (SEND, IC_RIGHT (ic), NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->next->etype); + } + else + { + newic = newiCode ('=', NULL, IC_RIGHT (ic)); + IC_RESULT (newic) = operandFromValue (FUNC_ARGS(func->type)->next); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (right)) + OP_USES (right) = bitVectSetBit (OP_USES (right), newic->key); + } + else + { + /* compiled as reentrant then push */ + /* push right */ + if (IS_REGPARM (FUNC_ARGS(func->type)->next->etype)) + { + newic = newiCode (SEND, IC_RIGHT (ic), NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->next->etype); + } + else + { + newic = newiCode (IPUSH, IC_RIGHT (ic), NULL); + newic->parmPush = 1; + + bytesPushed += getSize(operandType(IC_RIGHT(ic))); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (right)) + OP_USES (right) = bitVectSetBit (OP_USES (right), newic->key); + + /* insert push left */ + if (IS_REGPARM (FUNC_ARGS(func->type)->etype)) + { + newic = newiCode (SEND, IC_LEFT (ic), NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->etype); + } + else + { + newic = newiCode (IPUSH, IC_LEFT (ic), NULL); + newic->parmPush = 1; + + bytesPushed += getSize(operandType(IC_LEFT(ic))); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (left)) + OP_USES (left) = bitVectSetBit (OP_USES (left), newic->key); + } + + /* for the result */ + newic = newiCode (CALL, operandFromSymbol (func), NULL); + IC_RESULT (newic) = IC_RESULT (ic); + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + OP_DEFS (IC_RESULT (newic)) = bitVectSetBit (OP_DEFS (IC_RESULT (newic)), newic->key); + newic->filename = filename; + newic->lineno = lineno; + newic->parmBytes+=bytesPushed; // to clear the stack after the call + ebp->hasFcall = 1; + if (currFunc) + FUNC_HASFCALL (currFunc->type) = 1; + + if (TARGET_PIC_LIKE) + { + /* normally these functions aren't marked external, so we can use their + * _extern field to marked as already added to symbol table */ + + if (!SPEC_EXTR(func->etype)) + { + memmap *seg = SPEC_OCLS(OP_SYMBOL(IC_LEFT(newic))->etype); + + SPEC_EXTR(func->etype) = 1; + seg = SPEC_OCLS( func->etype ); + addSet(&seg->syms, func); + } + } + + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); +} + +/*-----------------------------------------------------------------*/ +/* convertbuiltin - maybe convert some builtins back */ +/*-----------------------------------------------------------------*/ +static void +convbuiltin (iCode *const ic, eBBlock *ebp) +{ + sym_link *ftype; + symbol *bif; + int stack; + + iCode *icc = ic, *icp = ic->prev, *ico = NULL; + iCode *lastparam = ic; + while (icc->op != CALL) + { + if (icc->op != SEND || !icc->builtinSEND) + return; + lastparam = icc; + icc = icc->next; + } + + if (!IS_SYMOP (IC_LEFT(icc))) + return; + + ftype = operandType (IC_LEFT(icc)); + if (!IFFUNC_ISBUILTIN (ftype)) + return; + + bif = OP_SYMBOL (IC_LEFT (icc)); + + /* Now we can be sure to have found a builtin function. */ + + if ((!strcmp (bif->name, "__builtin_memcpy") || !strcmp (bif->name, "__builtin_strncpy") || !strcmp (bif->name, "__builtin_memset")) && + IS_OP_LITERAL (IC_LEFT (lastparam)) && !operandLitValue (IC_LEFT (lastparam))) + { + /* We have a builtin that does nothing. */ + /* TODO: Eliminate it, convert any SEND of volatile into DUMMY_READ_VOLATILE. */ + /* For now just convert back to call to make sure any volatiles are read. */ + + strcpy(OP_SYMBOL (IC_LEFT (icc))->rname, !strcmp (bif->name, "__builtin_memcpy") ? "___memcpy" : (!strcmp (bif->name, "__builtin_strncpy") ? "_strncpy" : "_memset")); + goto convert; + } + + if ((TARGET_IS_Z80 || TARGET_IS_Z180 || TARGET_IS_RABBIT || TARGET_IS_EZ80_Z80) && (!strcmp (bif->name, "__builtin_memcpy") || !strcmp (bif->name, "__builtin_strncpy") || !strcmp (bif->name, "__builtin_memset"))) + { + /* Replace iff return value is used or last parameter is not an integer constant. */ + if (bitVectIsZero (OP_USES (IC_RESULT (icc))) && IS_OP_LITERAL (IC_LEFT (lastparam))) + return; + + strcpy(OP_SYMBOL (IC_LEFT (icc))->rname, !strcmp (bif->name, "__builtin_memcpy") ? "___memcpy" : (!strcmp (bif->name, "__builtin_strncpy") ? "_strncpy" : "_memset")); + goto convert; + } + + return; + +convert: + /* Convert parameter passings from SEND to PUSH. */ + stack = 0; + for (icc = ic; icc->op != CALL; icc = icc->next) + { + icc->builtinSEND = 0; + icc->op = IPUSH; + icc->parmPush = 1; + stack += getSize (operandType (IC_LEFT (icc))); + } + icc->parmBytes = stack; + + /* Reverse parameters. */ + for (icc = ic; icc->op != CALL; icc = icc->next) + { + if(icc->next->op != CALL) + icc->prev = icc->next; + else + icc->prev = icp; + } + if(icc != ic) + { + if(icp) + icp->next = icc->prev; + icc->prev = ic; + } + for(; icc != icp; ico = icc, icc = icc->prev) + { + if(icc->op != CALL) + icc->next = ico; + } +} + +static void +convsmallc (iCode *ic, eBBlock *ebp) +{ + iCode *icc, *icp, *ico = NULL; + + assert (ic->op == CALL || ic->op == PCALL); + + for (icc = ic->prev; icc && icc->op == IPUSH; icc = icc->prev) + ic = icc; + icp = icc; + + /* Reverse parameters. */ + for (icc = ic; icc->op != CALL && icc->op != PCALL; icc = icc->next) + { + if (icc->next->op != CALL && icc->next->op != PCALL) + icc->prev = icc->next; + else + icc->prev = icp; + } + if (icc != ic) + { + if (icp) + icp->next = icc->prev; + icc->prev = ic; + } + for (; icc != icp; ico = icc, icc = icc->prev) + { + if (icc->op != CALL && icc->op != PCALL) + icc->next = ico; + } +} + +/*-----------------------------------------------------------------*/ +/* convertToFcall - converts some operations to fcalls */ +/*-----------------------------------------------------------------*/ +static void +convertToFcall (eBBlock ** ebbs, int count) +{ + int i; + + /* for all blocks do */ + for (i = 0; i < count; i++) + { + iCode *ic; + + /* for all instructions in the block do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + /* floating point operations are + converted to function calls */ + if ((IS_CONDITIONAL (ic) || IS_ARITHMETIC_OP (ic)) && + (IS_FLOAT (operandType (IC_RIGHT (ic))) || IS_FIXED( operandType (IC_RIGHT (ic))))) + { + cnvToFcall (ic, ebbs[i]); + } + + /* casting is a little different */ + if (ic->op == CAST) + { + if (IS_FLOAT (operandType (IC_RIGHT (ic)))) + cnvFromFloatCast (ic, ebbs[i]); + else if (IS_FLOAT (operandType (IC_LEFT (ic)))) + cnvToFloatCast (ic, ebbs[i]); + if (IS_FIXED16X16 (operandType (IC_RIGHT (ic)))) + cnvFromFixed16x16Cast (ic, ebbs[i]); + else if (IS_FIXED16X16 (operandType (IC_LEFT (ic)))) + cnvToFixed16x16Cast (ic, ebbs[i]); + } + + // Easy special case which avoids function call: modulo by a literal power + // of two can be replaced by a bitwise AND. + if (ic->op == '%' && isOperandLiteral (IC_RIGHT(ic))) + { + bool us = IS_UNSIGNED (operandType (IC_LEFT(ic))); + + // Chek if left really is just an upcasted unsigned value. + if (!us && IS_SYMOP (IC_LEFT(ic)) && bitVectnBitsOn (OP_DEFS (IC_LEFT (ic))) == 1) + { + iCode *dic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (IC_LEFT (ic)))); + + if (dic && dic->op == CAST && IS_UNSIGNED (operandType (IC_RIGHT (dic))) && getSize (operandType (IC_RIGHT (dic))) < getSize (operandType (IC_RESULT (dic)))) + us = true; + } + + if (us) + { + unsigned long litVal = double2ul (operandLitValue (IC_RIGHT (ic))); + + /* modulo by 1: no remainder */ + if (litVal == 1) + { + ic->op = '='; + IC_RIGHT (ic) = operandFromLit (0); + IC_LEFT (ic) = NULL; + continue; + } + // See if literal value is a power of 2. + while (litVal && !(litVal & 1)) + { + litVal >>= 1; + } + if (litVal) + { + // discard lowest set bit. + litVal >>= 1; + } + + if (!litVal) + { + ic->op = BITWISEAND; + IC_RIGHT(ic) = operandFromLit (operandLitValue (IC_RIGHT (ic)) - 1); + continue; + } + } + } + + /* if long / int mult or divide or mod */ + if (ic->op == '*' || ic->op == '/' || ic->op == '%') + { + sym_link *leftType = operandType (IC_LEFT (ic)); + + if (IS_INTEGRAL (leftType)) + { + sym_link *rightType = operandType (IC_RIGHT (ic)); + + if (port->hasNativeMulFor != NULL && + port->hasNativeMulFor (ic, leftType, rightType)) + { + /* Leave as native */ + } + else + { + convilong (ic, ebbs[i]); + } + } + } + + if (ic->op == RRC || ic->op == RLC || ic->op == LEFT_OP || ic->op == RIGHT_OP) + { + sym_link *type = operandType (IC_LEFT (ic)); + + if (IS_INTEGRAL (type) && getSize (type) > (unsigned)port->support.shift && port->support.shift >= 0) + { + convilong (ic, ebbs[i]); + } + } + if (ic->op == SEND && ic->builtinSEND) + { + convbuiltin (ic, ebbs[i]); + } + if ((ic->op == CALL && IFFUNC_ISSMALLC (operandType (IC_LEFT (ic)))) || + (ic->op == PCALL && IFFUNC_ISSMALLC (operandType (IC_LEFT (ic))->next))) + { + convsmallc (ic, ebbs[i]); + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* isPowerOf2 - test if val is power of 2 */ +/*-----------------------------------------------------------------*/ +bool +isPowerOf2 (unsigned long val) +{ + while (val && !(val & 1)) + { + val >>= 1; + } + return val == 1; +} + +/*-----------------------------------------------------------------*/ +/* miscOpt - miscellaneous optimizations */ +/*-----------------------------------------------------------------*/ +static void +miscOpt (eBBlock ** ebbs, int count) +{ +/* Borut: disabled optimization of comparision unsigned with 2^n literal + * since it is broken; see bug #2165 Broken comparison */ +#if 0 + int i; + + /* for all blocks do */ + for (i = 0; i < count; ++i) + { + iCode *ic; + + /* for all instructions in the block do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + /* patch ID: 2702889 - Summary of all uncommitted changes I applied on "my" SDCC */ + /* MB: This seems rather incomplete. + MB: What if using <= or >= ? + Borut: Currently <= and >= are transformed to > and < on all targets. + Transformation depends on lt_nge, gt_nle, bool le_ngt, + ge_nlt, ne_neq and eq_nne members of PORT structure. + MB: Why do we need IFX in the first case and not in the second ? + Borutr: Because the result of comparision is logically negated, + so in case of IFX the jump logic is inverted for '<' and '<='. + TODO: The logical negation of the result should be implemeted + for '<' and '<=' in case when the following instruction is not IFX. + Philipp: Added the test for ifx in the second case, too: + We want 0 or 1 as a result, the bitwise and won't do, unless we add a cast to bool. + */ + switch (ic->op) + { + case '<': + case LE_OP: + case '>': + case GE_OP: + /* Only if the the right operand is literal and left operand is unsigned */ + if (isOperandLiteral (IC_RIGHT (ic)) && IS_UNSIGNED (operandType (IC_LEFT (ic)))) + { + unsigned litVal = ulFromVal (OP_VALUE (IC_RIGHT (ic))); + + /* Only if the literal value is greater than 255 and a power of 2 */ + if (litVal >= 255 && + (isPowerOf2 (litVal) && (ic->op == '<' || ic->op == GE_OP) || + isPowerOf2 (litVal + 1) && (ic->op == '>' || ic->op == LE_OP))) + { + iCode *ic_nxt = ic->next; + + switch (ic->op) + { + case LE_OP: + ++litVal; + /* fall through */ + case '<': + /* Only if the next instruction is IFX */ + if (ic_nxt && (ic_nxt->op == IFX) && (ic->eBBlockNum == ic_nxt->eBBlockNum)) + { + int AndMaskVal = 0 - litVal; + symbol *TrueLabel; + + /* set op to bitwise and */ + ic->op = BITWISEAND; + IC_RIGHT (ic) = operandFromLit (AndMaskVal); + + /* invert jump logic */ + TrueLabel = IC_TRUE (ic_nxt); + IC_TRUE (ic_nxt) = IC_FALSE (ic_nxt); + IC_FALSE (ic_nxt) = TrueLabel; + } + break; + + case '>': + ++litVal; + /* fall through */ + case GE_OP: + if (ic_nxt && (ic_nxt->op == IFX) && (ic->eBBlockNum == ic_nxt->eBBlockNum)) + { + int AndMaskVal = 0 - litVal; + + ic->op = BITWISEAND; + IC_RIGHT (ic) = operandFromLit (AndMaskVal); + } + break; + } /* switch */ + } /* if */ + } /* if */ + } /* switch */ + } /* for */ + } /* for */ +#endif +} + +/*-----------------------------------------------------------------*/ +/* separateAddressSpaces - enforce restrictions on bank switching */ +/* Operands of a single iCode must be in at most one */ +/* named address space. Use temporaries and additional assignments */ +/* to enforce the rule. */ +/*-----------------------------------------------------------------*/ +static void +separateAddressSpaces (eBBlock **ebbs, int count) +{ + int i; + + /* for all blocks do */ + for (i = 0; i < count; ++i) + { + iCode *ic; + symbol *source; + + /* Skip this block if not reachable; other routines may have */ + /* also skipped it, so these iCodes may be undercooked. */ + if (ebbs[i]->noPath) + continue; + + /* for all instructions in the block do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + iCode *iic = 0, *newic = 0; + operand *left, *right, *result; + const symbol *leftaddrspace = 0, *rightaddrspace = 0, *resultaddrspace = 0; + + /* JUMPTABLE and IFX do not have left/right/result operands. */ + /* However, they only have a single operand so they cannot */ + /* have more than one address space to worry about. */ + if (ic->op == JUMPTABLE || ic->op == IFX) + continue; + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + /*printf ("Looking at ic %d, op %d\n", ic->key, (int)(ic->op));*/ + + if (left && IS_SYMOP (left)) + { + if (POINTER_GET (ic)) + { + assert (!(IS_DECL (OP_SYMBOL (left)->type) && DCL_PTR_ADDRSPACE (OP_SYMBOL (left)->type))); + leftaddrspace = getAddrspace (OP_SYMBOL (left)->type->next); + } + else + leftaddrspace = getAddrspace (OP_SYMBOL (left)->type); + } + if (right && IS_SYMOP (right)) + rightaddrspace = getAddrspace (OP_SYMBOL (right)->type); + if (result && IS_SYMOP (result)) + { + if (POINTER_SET (ic)) + { + assert (!(IS_DECL (OP_SYMBOL (result)->type) && DCL_PTR_ADDRSPACE (OP_SYMBOL (result)->type))); + resultaddrspace = getAddrspace (OP_SYMBOL (result)->type->next); + } + else + resultaddrspace = getAddrspace (OP_SYMBOL (result)->type); + } + +#if 0 + if (leftaddrspace) + printf("ic %d (dcl? %d) leftaddrspace %s\n", ic->key, (int)(IS_DECL (OP_SYMBOL (left)->type)), leftaddrspace->name); + if (rightaddrspace) + printf("ic %d (dcl? %d) rightaddrspace %s\n", ic->key, (int)(IS_DECL (OP_SYMBOL (right)->type)), rightaddrspace->name); + if (resultaddrspace) + printf("ic %d (dcl? %d) resultaddrspace %s\n", ic->key, (int)(IS_DECL (OP_SYMBOL (result)->type)), resultaddrspace->name); +#endif + + if (ic->op == IPUSH && leftaddrspace) + { + operand *newop; + + source = OP_SYMBOL (left); + newic = newiCode ('=', 0, left); + IC_RESULT (newic) = newop = newiTempOperand (source->type, 0); + IC_LEFT (ic) = newop; + leftaddrspace = 0; + for (iic = ic; iic->prev && iic->prev->op == IPUSH; iic = iic->prev); + } + else if (leftaddrspace && rightaddrspace && leftaddrspace != rightaddrspace || + resultaddrspace && rightaddrspace && resultaddrspace != rightaddrspace || + resultaddrspace && leftaddrspace && resultaddrspace != leftaddrspace) + { + operand *newop; + + if (rightaddrspace == resultaddrspace) + source = OP_SYMBOL (left); + else + source = OP_SYMBOL (right); + newic = newiCode ('=', 0, rightaddrspace == resultaddrspace ? left : right); + IC_RESULT (newic) = newop = newiTempOperand (source->type, 0); + if (rightaddrspace == resultaddrspace) + { + IC_LEFT (ic) = newop; + leftaddrspace = 0; + } + else + { + IC_RIGHT (ic) = newop; + rightaddrspace = 0; + } + iic = ic; + } + + if (newic) + { + newic->filename = ic->filename; + newic->lineno = ic->lineno; + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebbs[i], newic, iic); + } + + assert (!leftaddrspace || !resultaddrspace || leftaddrspace == resultaddrspace); + assert (!rightaddrspace || !resultaddrspace || rightaddrspace == resultaddrspace); + } + } +} + +const symbol * +getAddrspaceiCode (const iCode *ic) +{ + operand *left, *right, *result; + const symbol *leftaddrspace = 0, *rightaddrspace = 0, *resultaddrspace = 0; + const symbol *addrspace; + + /* Not safe to use IC_LEFT, IC_RIGHT, or IC_RESULT macros on */ + /* IFX or JUMPTABLE iCodes. Handle these as a special case. */ + if (ic->op == IFX || ic->op == JUMPTABLE) + { + operand *cond; + if (ic->op == IFX) + cond = IC_COND (ic); + else + cond = IC_JTCOND (ic); + if (IS_SYMOP (cond)) + return getAddrspace (OP_SYMBOL (cond)->type); + else + return NULL; + } + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + /* Previous transformations in separateAddressSpaces() should + ensure that at most one addressspace occours in each iCode. */ + if (left && IS_SYMOP (left)) + { + if (POINTER_GET (ic)) + { + assert (!(IS_DECL (OP_SYMBOL (left)->type) && DCL_PTR_ADDRSPACE (OP_SYMBOL (left)->type))); + leftaddrspace = getAddrspace (OP_SYMBOL (left)->type->next); + } + else + leftaddrspace = getAddrspace (OP_SYMBOL (left)->type); + } + if (right && IS_SYMOP (right)) + rightaddrspace = getAddrspace (OP_SYMBOL (right)->type); + if (result && IS_SYMOP (result)) + { + if (POINTER_SET (ic)) + { + assert (!(IS_DECL (OP_SYMBOL (result)->type) && DCL_PTR_ADDRSPACE (OP_SYMBOL (result)->type))); + resultaddrspace = getAddrspace (OP_SYMBOL (result)->type->next); + } + else + resultaddrspace = getAddrspace (OP_SYMBOL (result)->type); + } + + addrspace = leftaddrspace; + if (rightaddrspace) + { + wassertl (!addrspace || addrspace == rightaddrspace, "Multiple named address spaces in icode."); + addrspace = rightaddrspace; + } + if (resultaddrspace) + { + wassertl (!addrspace || addrspace == resultaddrspace, "Multiple named address spaces in icode."); + addrspace = resultaddrspace; + } + + return (addrspace); +} + +/*-----------------------------------------------------------------*/ +/* switchAddressSpaceAt - insert a bank selection instruction */ +/*-----------------------------------------------------------------*/ +void +switchAddressSpaceAt (iCode *ic, const symbol *const addrspace) +{ + iCode *newic; + const symbol *const laddrspace = getAddrspaceiCode (ic); + wassertl(!laddrspace || laddrspace == addrspace, "Switching to invalid address space."); + + newic = newiCode (CALL, operandFromSymbol (addrspace->addressmod[0]), 0); + + IC_RESULT (newic) = newiTempOperand (newVoidLink (), 1); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + + newic->next = ic; + newic->prev = ic->prev; + if (ic->prev) + ic->prev->next = newic; + ic->prev = newic; +} + +/*-----------------------------------------------------------------*/ +/* switchAddressSpaces - insert instructions for bank switching */ +/* This is just a fallback, in case the optimal approach fails - */ +/* improbable, but possible depending on sdcc options and code. */ +/*-----------------------------------------------------------------*/ +static void +switchAddressSpaces (iCode *ic) +{ + const symbol *oldaddrspace = 0; + + for (; ic; ic = ic->next) + { + const symbol *const addrspace = getAddrspaceiCode (ic); + + if (addrspace && addrspace != oldaddrspace) + { + switchAddressSpaceAt (ic, addrspace); + + oldaddrspace = addrspace; + } + + /* Address space might not be preserved over these. */ + if (ic->op == LABEL || ic->op == CALL || ic->op == PCALL) + oldaddrspace = 0; + } +} + +/*-----------------------------------------------------------------*/ +/* isLocalWithoutDef - return 1 if sym might be used without a */ +/* defining iCode */ +/*-----------------------------------------------------------------*/ +static int +isLocalWithoutDef (symbol * sym) +{ + if (!IS_AUTO (sym)) + return 0; + + if (IS_VOLATILE (sym->type)) + return 0; + + if (sym->_isparm) + return 0; + + if (IS_AGGREGATE (sym->type)) + return 0; + + if (sym->addrtaken) + return 0; + + return !sym->defs; +} + +static void +replaceRegEqvOperand (iCode * ic, operand ** opp, int force_isaddr, int new_isaddr) +{ + operand * op = *opp; + symbol * sym = OP_SYMBOL (op); + + if (isLocalWithoutDef (sym)) + { + werrorfl (ic->filename, ic->lineno, W_LOCAL_NOINIT, sym->name); + OP_REQV (op) = NULL; + sym->allocreq = 1; + } + else if (OP_REQV (op)) + { + operand * nop; + + nop = operandFromOperand (OP_REQV (op)); + + /* Copy def/use info from true symbol to register equivalent */ + /* but only if this hasn't been done already. */ + if (!OP_DEFS (nop)) + OP_DEFS (nop) = bitVectCopy (OP_DEFS (op)); + if (!OP_USES (nop)) + OP_USES (nop) = bitVectCopy (OP_USES (op)); + + if (force_isaddr) + nop->isaddr = new_isaddr; + + *opp = nop; /* Replace true sym operand with reg equiv */ + } +} + +/*-----------------------------------------------------------------*/ +/* replaceRegEqv - replace all local variables with their reqv */ +/*-----------------------------------------------------------------*/ +static void +replaceRegEqv (ebbIndex * ebbi) +{ + eBBlock ** ebbs = ebbi->bbOrder; + int count = ebbi->count; + int i; + + /* Reset all the def/use info (Otherwise there may be stale def/use */ + /* info if a variable is also used in a previous functions) */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath) + continue; + + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (SKIP_IC2 (ic)) + continue; + + if (ic->op == IFX) + { + if (IS_TRUE_SYMOP (IC_COND (ic))) + { + OP_DEFS (IC_COND (ic)) = NULL; + OP_USES (IC_COND (ic)) = NULL; + } + continue; + } + + if (ic->op == JUMPTABLE) + { + if (IS_TRUE_SYMOP (IC_JTCOND (ic))) + { + OP_DEFS (IC_JTCOND (ic)) = NULL; + OP_USES (IC_JTCOND (ic)) = NULL; + } + continue; + } + + if (IS_TRUE_SYMOP (IC_RESULT (ic))) + { + OP_DEFS (IC_RESULT (ic)) = NULL; + OP_USES (IC_RESULT (ic)) = NULL; + } + + if (IS_TRUE_SYMOP (IC_RIGHT (ic))) + { + OP_DEFS (IC_RIGHT (ic)) = NULL; + OP_USES (IC_RIGHT (ic)) = NULL; + } + + if (IS_TRUE_SYMOP (IC_LEFT (ic))) + { + OP_DEFS (IC_LEFT (ic)) = NULL; + OP_USES (IC_LEFT (ic)) = NULL; + } + } + } + + /* Update the symbols' def bitvector so we know if there is */ + /* a defining iCode or not. Only replace a local variable */ + /* with its register equivalent if there is a defining iCode; */ + /* otherwise, the port's register allocater may choke. */ + cseAllBlocks (ebbi, TRUE); + + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath) + continue; + + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (SKIP_IC2 (ic)) + continue; + + if (ic->op == IFX) + { + if (IS_TRUE_SYMOP (IC_COND (ic))) + replaceRegEqvOperand (ic, &IC_COND (ic), 0, 0); + continue; + } + + if (ic->op == JUMPTABLE) + { + if (IS_TRUE_SYMOP (IC_JTCOND (ic))) + replaceRegEqvOperand (ic, &IC_JTCOND (ic), 0, 0); + continue; + } + + if (ic->op == RECEIVE) + { + if (OP_SYMBOL (IC_RESULT (ic))->addrtaken) + OP_SYMBOL (IC_RESULT (ic))->isspilt = 1; + } + + /* general case */ + if (IS_TRUE_SYMOP (IC_RESULT (ic))) + { + if (POINTER_SET (ic)) + replaceRegEqvOperand (ic, &IC_RESULT (ic), 1, 1); + else + replaceRegEqvOperand (ic, &IC_RESULT (ic), 0, 0); + } + if (IS_TRUE_SYMOP (IC_RIGHT (ic))) + replaceRegEqvOperand (ic, &IC_RIGHT (ic), 1, 0); + if (IS_TRUE_SYMOP (IC_LEFT (ic))) + replaceRegEqvOperand (ic, &IC_LEFT (ic), 1, 0); + } + } +} + +/*-----------------------------------------------------------------*/ +/* findReqv - search for a register equivalent */ +/*-----------------------------------------------------------------*/ +operand * +findReqv (symbol * prereqv, eBBlock ** ebbs, int count) +{ + int i; + iCode * ic; + + /* for all blocks do */ + for (i=0; i<count; i++) + { + /* for all instructions in the block do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (ic->op == IFX) + { + if (IS_ITEMP (IC_COND (ic)) + && OP_SYMBOL (IC_COND (ic))->prereqv == prereqv) + return IC_COND (ic); + } + else if (ic->op == JUMPTABLE) + { + if (IS_ITEMP (IC_JTCOND (ic)) + && OP_SYMBOL (IC_JTCOND (ic))->prereqv == prereqv) + return IC_JTCOND (ic); + } + else + { + if (IS_ITEMP (IC_LEFT (ic)) + && OP_SYMBOL (IC_LEFT (ic))->prereqv == prereqv) + return IC_LEFT (ic); + if (IS_ITEMP (IC_RIGHT (ic)) + && OP_SYMBOL (IC_RIGHT (ic))->prereqv == prereqv) + return IC_RIGHT (ic); + if (IS_ITEMP (IC_RESULT (ic)) + && OP_SYMBOL (IC_RESULT (ic))->prereqv == prereqv) + return IC_RESULT (ic); + } + } + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* killDeadCode - eliminates dead assignments */ +/*-----------------------------------------------------------------*/ +int +killDeadCode (ebbIndex * ebbi) +{ + eBBlock ** ebbs = ebbi->dfOrder; + int count = ebbi->count; + int change = 1; + int gchange = 0; + int i = 0; + + /* basic algorithm :- */ + /* first the exclusion rules :- */ + /* 1. if result is a global or volatile then skip */ + /* 2. if assignment and result is a temp & isaddr then skip */ + /* since this means array & pointer access, will be taken */ + /* care of by alias analysis. */ + /* 3. if the result is used in the remainder of the block skip */ + /* 4. if this definition does not reach the end of the block */ + /* i.e. the result is not present in the outExprs then KILL */ + /* 5. if it reaches the end of block & is used by some success */ + /* or then skip */ + /* else KILL */ + /* this whole process is carried on iteratively till no change */ + do + { + change = 0; + /* for all blocks do */ + for (i = 0; i < count; i++) + { + iCode *ic; + + /* for all instructions in the block do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + int kill, j; + kill = 0; + + if (SKIP_IC (ic) || + ic->op == IFX || + ic->op == RETURN || + ic->op == DUMMY_READ_VOLATILE || + ic->op == CRITICAL || + ic->op == ENDCRITICAL) + continue; + + /* Since both IFX & JUMPTABLE (in SKIP_IC) have been tested for */ + /* it is now safe to assume IC_LEFT, IC_RIGHT, & IC_RESULT are */ + /* valid. */ + + /* if the result is volatile then continue */ + if (IC_RESULT (ic) && isOperandVolatile (IC_RESULT (ic), FALSE)) + continue; + + /* if the result is a temp & isaddr then skip */ + if (IC_RESULT (ic) && POINTER_SET (ic)) + continue; + + /* if the results address has been taken then skip */ + if (IS_SYMOP (IC_RESULT (ic)) && OP_SYMBOL (IC_RESULT (ic))->addrtaken) + continue; + + if (POINTER_GET (ic) && IS_VOLATILE (operandType (IC_LEFT (ic))->next) + && !SPIL_LOC (IC_RESULT (ic))) + continue; + + /* if the result is used in the remainder of the */ + /* block then skip */ + if (usedInRemaining (IC_RESULT (ic), ic->next)) + continue; + + /* does this definition reach the end of the block + or the usage is zero then we can kill */ + if (!bitVectBitValue (ebbs[i]->outDefs, ic->key)) + kill = 1; /* if not we can kill it */ + else + { + /* if this is a global variable or function parameter */ + /* we cannot kill anyway */ + if (isOperandGlobal (IC_RESULT (ic)) || + (OP_SYMBOL (IC_RESULT (ic))->_isparm && + !OP_SYMBOL (IC_RESULT (ic))->ismyparm)) + continue; + + /* if we are sure there are no usages */ + if (bitVectIsZero (OP_USES (IC_RESULT (ic)))) + { + kill = 1; + goto kill; + } + + /* reset visited flag */ + for (j = 0; j < count; ebbs[j++]->visited = 0); + + /* find out if this definition is alive */ + if (applyToSet (ebbs[i]->succList, isDefAlive, ic)) + continue; + + kill = 1; + } + + kill: + /* kill this one if required */ + if (kill) + { + bool volLeft = IS_SYMOP (IC_LEFT (ic)) + && isOperandVolatile (IC_LEFT (ic), FALSE); + bool volRight = IS_SYMOP (IC_RIGHT (ic)) + && isOperandVolatile (IC_RIGHT (ic), FALSE); + + /* a dead address-of operation should die, even if volatile */ + if (ic->op == ADDRESS_OF) + volLeft = FALSE; + + if (ic->next && ic->seqPoint == ic->next->seqPoint + && (ic->next->op == '+' || ic->next->op == '-')) + { + if (isOperandEqual (IC_LEFT(ic), IC_LEFT(ic->next)) + || isOperandEqual (IC_LEFT(ic), IC_RIGHT(ic->next))) + volLeft = FALSE; + if (isOperandEqual (IC_RIGHT(ic), IC_LEFT(ic->next)) + || isOperandEqual (IC_RIGHT(ic), IC_RIGHT(ic->next))) + volRight = FALSE; + } + + if (POINTER_GET (ic) && IS_VOLATILE (operandType (IC_LEFT (ic))->next)) + { + if (SPIL_LOC (IC_RESULT (ic))) + { + IC_RESULT (ic) = newiTempFromOp (IC_RESULT (ic)); + SPIL_LOC (IC_RESULT (ic)) = NULL; + } + continue; + } + + change = 1; + gchange++; + + /* now delete from defUseSet */ + deleteItemIf (&ebbs[i]->outExprs, ifDiCodeIsX, ic); + bitVectUnSetBit (ebbs[i]->outDefs, ic->key); + + /* and defset of the block */ + bitVectUnSetBit (ebbs[i]->defSet, ic->key); + + /* If this is the last of a register equivalent, */ + /* look for a successor register equivalent. */ + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + if (IS_ITEMP (IC_RESULT (ic)) + && OP_SYMBOL (IC_RESULT (ic))->isreqv + && bitVectIsZero (OP_DEFS (IC_RESULT (ic)))) + { + symbol * resultsym = OP_SYMBOL (IC_RESULT (ic)); + symbol * prereqv = resultsym->prereqv; + + if (prereqv && prereqv->reqv && (OP_SYMBOL (prereqv->reqv) == resultsym)) + { + operand * newreqv; + + IC_RESULT (ic) = NULL; + newreqv = findReqv (prereqv, ebbs, count); + if (newreqv) + { + prereqv->reqv = newreqv; + } + } + } + + /* delete the result */ + if (IC_RESULT (ic)) + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + IC_RESULT (ic) = NULL; + + if (volLeft || volRight) + { + /* something is volatile, so keep the iCode */ + /* and change the operator instead */ + ic->op = DUMMY_READ_VOLATILE; + + /* keep only the volatile operands */ + if (!volLeft) + IC_LEFT (ic) = NULL; + if (!volRight) + IC_RIGHT (ic) = NULL; + } + else + { + /* nothing is volatile, eliminate the iCode */ + remiCodeFromeBBlock (ebbs[i], ic); + + /* for the left & right remove the usage */ + if (IS_SYMOP (IC_LEFT (ic))) + { + if (OP_SYMBOL (IC_LEFT (ic))->isstrlit) + freeStringSymbol (OP_SYMBOL (IC_LEFT (ic))); + bitVectUnSetBit (OP_USES (IC_LEFT (ic)), ic->key); + } + if (IS_SYMOP (IC_RIGHT (ic))) + bitVectUnSetBit (OP_USES (IC_RIGHT (ic)), ic->key); + } + } + } /* end of all instructions */ + + if (!ebbs[i]->sch && !ebbs[i]->noPath) + disconBBlock (ebbs[i], ebbi); + } /* end of for all blocks */ + } /* end of do */ + while (change); + + return gchange; +} + +/*-----------------------------------------------------------------*/ +/* printCyclomatic - prints the cyclomatic information */ +/*-----------------------------------------------------------------*/ +static void +printCyclomatic (eBBlock ** ebbs, int count) +{ + int nEdges = elementsInSet (graphEdges); + int i, nNodes = 0; + + for (i = 0; i < count; i++) + nNodes += (!ebbs[i]->noPath); + + /* print the information */ + werror (I_CYCLOMATIC, currFunc->name, nEdges, nNodes, nEdges - nNodes + 2); +} + +/*-----------------------------------------------------------------*/ +/* discardDeadParamReceives - remove any RECEIVE opcodes which */ +/* refer to dead variables. */ +/*-----------------------------------------------------------------*/ +static void +discardDeadParamReceives (eBBlock ** ebbs, int count) +{ + int i; + iCode *ic; + iCode dummyIcode; + + for (i = 0; i < count; i++) + { + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (ic->op == RECEIVE) + { + if (IC_RESULT (ic) && OP_SYMBOL (IC_RESULT (ic)) + && !OP_SYMBOL (IC_RESULT (ic))->used) + { +#if 0 + fprintf (stderr, "discarding dead receive for %s\n", + OP_SYMBOL (IC_RESULT (ic))->name); +#endif + dummyIcode.next = ic->next; + remiCodeFromeBBlock (ebbs[i], ic); + ic = &dummyIcode; + } + } + } + } +} + +/* Insert a cast of operand op of ic to type type */ +static void prependCast (iCode *ic, operand *op, sym_link *type, eBBlock *ebb) +{ + iCode *newic = newiCode (CAST, operandFromLink (type), op); + hTabAddItem (&iCodehTab, newic->key, newic); + + IC_RESULT (newic) = newiTempOperand (type, 0); + bitVectSetBit (OP_USES (op), newic->key); + OP_DEFS (IC_RESULT (newic)) = bitVectSetBit (OP_DEFS (IC_RESULT (newic)), newic->key); + bitVectUnSetBit (OP_USES (op), ic->key); + OP_USES (IC_RESULT (newic)) = bitVectSetBit (OP_USES (IC_RESULT (newic)), ic->key); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + + addiCodeToeBBlock (ebb, newic, ic); + + if (isOperandEqual (op, IC_LEFT (ic))) + IC_LEFT (ic) = IC_RESULT (newic); + + if (isOperandEqual (op, IC_RIGHT (ic))) + IC_RIGHT (ic) = IC_RESULT (newic); +} + +/* Insert a cast of result of ic from type type */ +static void appendCast (iCode *ic, sym_link *type, eBBlock *ebb) +{ + iCode *newic = newiCode (CAST, operandFromLink (operandType (IC_RESULT (ic))), 0); + hTabAddItem (&iCodehTab, newic->key, newic); + + IC_RESULT (newic) = IC_RESULT (ic); + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + bitVectSetBit (OP_DEFS (IC_RESULT (ic)), newic->key); + IC_RESULT (ic) = newiTempOperand (type, 0); + IC_RIGHT (newic) = operandFromOperand (IC_RESULT (ic)); + bitVectSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + bitVectSetBit (OP_USES (IC_RESULT (ic)), newic->key); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + addiCodeToeBBlock (ebb, newic, ic->next); +} + + + +/*-----------------------------------------------------------------*/ +/* optimizeOpWidth - reduce operation width. */ +/* Wide arithmetic operations where the result is cast to narrow */ +/* type can be optimized by doing the casts on the operands */ +/*-----------------------------------------------------------------*/ +static int +optimizeOpWidth (eBBlock ** ebbs, int count) +{ + int i; + int change = 0; + iCode *ic, *newic; + iCode *uic, *skipuic; + sym_link *nextresulttype; + symbol *sym; + int resultsize, nextresultsize; + + // Wide loop counter + for (i = 0; i < count; i++) + { + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + sym_link *newcountertype, *oldcountertype; + const symbol *label; + const iCode *ifx, *inc = 0, *obstacle = 0; + iCode *mul = 0; + bool found = false; + + if (ic->op != LABEL || !ic->next) + continue; + + label = IC_LABEL (ic); + ic = ic->next; + + if (ic->op != '<' || !IS_ITEMP (IC_LEFT (ic)) || bitVectnBitsOn (OP_DEFS (IC_LEFT (ic))) != 2) + continue; + + oldcountertype = operandType (IC_LEFT (ic)); + if (IS_VOLATILE (oldcountertype)) + continue; + + // Only try to narrow wide counters. + if (!IS_INTEGRAL(oldcountertype) || bitsForType (oldcountertype) <= 8 || TARGET_IS_DS390 || TARGET_IS_DS400 || (!SPEC_USIGN (oldcountertype))) // TODO: Handle signed types as well, maybe even transform int to unsigned int? + continue; + + ifx = ifxForOp (IC_RESULT (ic), ic); + + if (!ifx || IC_TRUE (ifx) || i + 1 >= count) + continue; + + /* For now we handle only loops that have no complex control flow inside them and where + the loop is entered and left through ifx only */ + for(uic = ebbs[i + 1]->sch; uic; uic = uic->next) + { + if(uic->op == GOTO && IC_LABEL (uic) == label) + break; + + if(!obstacle && + (uic->op == CALL || uic->op == PCALL || uic->op == IFX || uic->op == LABEL || + uic->op == GOTO && IC_LABEL (uic) != label || uic->op == INLINEASM)) + { + obstacle = uic; + break; + } + } + + // TODO: Proceed despite obstacle, but only consider array accesses before obstacle. + if(obstacle || !uic || uic->op != GOTO || IC_LABEL (uic) != label) + continue; + + const bitVect *uses; + int bit; + + uses = bitVectCopy (OP_USES (IC_LEFT (ic))); + for (bit = bitVectFirstBit (uses); bitVectnBitsOn (uses); bitVectUnSetBit (uses, bit), bit = bitVectFirstBit (uses)) + { + operand *prevresult = IC_LEFT(ic); + operand *mulotherop = 0; + iCode *mul_candidate = 0; + uic = hTabItemWithKey (iCodehTab, bit); + + if(uic->op == '+' && IS_OP_LITERAL (IC_RIGHT (uic)) && operandLitValue (IC_RIGHT (uic)) == 1 && isOperandEqual (IC_LEFT (uic), IC_LEFT (ic))) + { + inc = uic; + continue; + } + + if (uic->op != CAST && uic->op != '=' && uic->op != '+' && uic->op != '*' && uic->op != '-' && uic->op != LEFT_OP && uic->op != RIGHT_OP && uic->op != '<') + { + found = false; + break; + } + + if (uic && uic->op == '*') + { + mulotherop = isOperandEqual (IC_LEFT (ic), IC_LEFT (uic)) ? IC_RIGHT (uic) : IC_LEFT (uic); + if (isOperandEqual (IC_RIGHT (ic), mulotherop)) + { + mul_candidate = uic; + uic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_USES (IC_RESULT (uic)))); + } + } + + for (int i = 0; i < 8 && uic && + (uic->op == CAST && bitsForType (operandType (IC_RESULT (uic))) >= 16 || + uic->op == '=' || uic->op == '+' || uic->op == LEFT_OP || + uic->op == '*' && IS_OP_LITERAL (IC_RIGHT (uic)) && operandLitValue (IC_RIGHT (uic)) >= 1); i++) + { + prevresult = IC_RESULT (uic); + uic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_USES (IC_RESULT (uic)))); + } + + if(!uic) + continue; + + // Use as array index? + if (uic->op == GET_VALUE_AT_ADDRESS || POINTER_SET(uic) && isOperandEqual (IC_RESULT (uic), prevresult)) + { + found = true; + if (mul_candidate) + mul = mul_candidate; + } + } + + if (!found || !inc) + continue; + + /* All backends (except ds390 / ds400) have an array size limit smaller than 2^16. Thus if the loop counter ever goes outside + the range of a 16-bit type, the array access would result in undefined behaviour. We can thus replace the loop + counter by a 16-bit type. If we found a squaring multiplication, we can even use an 8-bit type*/ + if (bitsForType (oldcountertype) <= 16 && !mul) + continue; + + newcountertype = mul ? newCharLink () : newIntLink (); + SPEC_USIGN (newcountertype) = 1; + OP_SYMBOL (IC_LEFT (ic))->type = newcountertype; + OP_SYMBOL (IC_RESULT (inc))->type = newcountertype; + + uses = bitVectCopy (OP_USES (IC_LEFT (ic))); + for (bit = bitVectFirstBit (uses); bitVectnBitsOn (uses); bitVectUnSetBit (uses, bit), bit = bitVectFirstBit (uses)) + { + uic = hTabItemWithKey (iCodehTab, bit); + + if (uic == inc || uic == mul) + continue; + if (uic->op == CAST) + continue; + if (uic->key == ic->key) + continue; + if (uic->op == '=') + { + uic->op = CAST; + continue; + } + + // Need to insert cast. + prependCast (uic, IC_LEFT (ic), oldcountertype, ebbs[i + 1]); + } + + // Insert cast for comparison. + if (IS_OP_LITERAL (IC_RIGHT (ic))) + IC_RIGHT (ic) = operandFromValue (valCastLiteral (newcountertype, operandLitValue (IC_RIGHT (ic)), operandLitValue (IC_RIGHT (ic)))); + else + prependCast (ic, IC_RIGHT (ic), newcountertype, ebbs[i]); + + // Bonus: Can narrow a multiplication in the loop. + if (mul) + { + prependCast (mul, IC_LEFT (mul), newcountertype, ebbs[i + 1]); + prependCast (mul, IC_RIGHT (mul), newcountertype, ebbs[i + 1]); + nextresulttype = newIntLink(); + SPEC_USIGN (nextresulttype) = 1; + appendCast(mul, nextresulttype, ebbs[i + 1]); + } + } + } + + /* long and long long multiplications where operands are unsigned char due to bitwise and */ + for (i = 0; i < count; i++) + for (ic = ebbs[i]->sch; ic; ic = ic->next) + if (ic->op == '*' && IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic))) + { + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + sym_link *resulttype = operandType (IC_RESULT (ic)); + + if (!IS_INTEGRAL (resulttype) || bitsForType (resulttype) <= 16 || + !(IS_ITEMP (left) || IS_OP_LITERAL (left)) || + !(IS_ITEMP (right) || IS_OP_LITERAL (right))) + continue; + + if (IS_ITEMP (left) && bitVectnBitsOn (OP_DEFS (left)) != 1 || + IS_ITEMP (right) && bitVectnBitsOn (OP_DEFS (right)) != 1) + continue; + + iCode *lic = IS_ITEMP (left) ? hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (left))) : 0; + iCode *ric = IS_ITEMP (right) ? hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (right))) : 0; + + if (lic) + { + if (lic->op != BITWISEAND || !IS_OP_LITERAL (IC_LEFT (lic)) && !IS_OP_LITERAL (IC_RIGHT (lic))) + continue; + + unsigned long litval = operandLitValue (IS_OP_LITERAL (IC_LEFT (lic)) ? IC_LEFT (lic) : IC_RIGHT (lic)); + + if (litval > 0x7f) + continue; + } + else if (operandLitValue (left) > 0x7f) + continue; + + if (ric) + { + if (ric->op != BITWISEAND || !IS_OP_LITERAL (IC_LEFT (ric)) && !IS_OP_LITERAL (IC_RIGHT (ric))) + continue; + + unsigned long litval = operandLitValue (IS_OP_LITERAL (IC_LEFT (ric)) ? IC_LEFT (ric) : IC_RIGHT (ric)); + + if (litval > 0x7f) + continue; + } + else if (operandLitValue (right) > 0x7f) + continue; + + // Now replace the wide multiplication by 8x8->16 multiplication and insert casts. + + if (lic) + { + newic = newiCode (CAST, operandFromLink (newCharLink()), left); + hTabAddItem (&iCodehTab, newic->key, newic); + IC_RESULT (newic) = newiTempOperand (newCharLink(), 0); + IC_LEFT (ic) = operandFromOperand (IC_RESULT (newic)); + bitVectUnSetBit (OP_USES (left), ic->key); + bitVectSetBit (OP_USES (left), newic->key); + OP_DEFS (IC_RESULT (newic)) = bitVectSetBit (OP_DEFS (IC_RESULT (newic)), newic->key); + OP_USES (IC_RESULT (newic)) = bitVectSetBit (OP_USES (IC_RESULT (newic)), ic->key); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + addiCodeToeBBlock (ebbs[i], newic, ic); + } + else + IC_LEFT (ic) = operandFromValue (valCastLiteral (newCharLink(), operandLitValue (IC_LEFT (ic)), operandLitValue (IC_LEFT (ic)))); + + if (ric) + { + newic = newiCode (CAST, operandFromLink (newCharLink()), right); + hTabAddItem (&iCodehTab, newic->key, newic); + IC_RESULT (newic) = newiTempOperand (newCharLink(), 0); + IC_RIGHT (ic) = operandFromOperand (IC_RESULT (newic)); + bitVectUnSetBit (OP_USES (right), ic->key); + bitVectSetBit (OP_USES (right), newic->key); + OP_DEFS (IC_RESULT (newic)) = bitVectSetBit (OP_DEFS (IC_RESULT (newic)), newic->key); + OP_USES (IC_RESULT (newic)) = bitVectSetBit (OP_USES (IC_RESULT (newic)), ic->key); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + addiCodeToeBBlock (ebbs[i], newic, ic); + } + else + IC_LEFT (ic) = operandFromValue (valCastLiteral (newCharLink(), operandLitValue (IC_LEFT (ic)), operandLitValue (IC_LEFT (ic)))); + + // Insert cast on result + nextresulttype = newIntLink(); + SPEC_USIGN (nextresulttype) = 1; + appendCast(ic, nextresulttype, ebbs[i]); + } + + // Operation followed by cast + for (i = 0; i < count; i++) + { + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if ((ic->op == '+' || ic->op == '-' || ic->op == UNARYMINUS || ic->op == '*' || ic->op == LEFT_OP || ic->op == RIGHT_OP || ic->op == BITWISEAND || ic->op == '|' || ic->op == CAST) && + IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic))) + { + sym_link *resulttype = operandType (IC_RESULT (ic)); + + if (!IS_INTEGRAL (resulttype) || + ic->op != CAST && !(IS_SYMOP (IC_LEFT (ic)) || IS_OP_LITERAL (IC_LEFT (ic))) || + !(IS_SYMOP (IC_RIGHT (ic)) || IS_OP_LITERAL (IC_RIGHT (ic)) || ic->op == UNARYMINUS)) + continue; + + resultsize = bitsForType (resulttype); + + /* There must be only one use of this first result */ + if (bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) != 1 || bitVectnBitsOn (OP_USES (IC_RESULT (ic))) != 1) + continue; + + uic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_USES (IC_RESULT (ic)))); + + if(!uic) + continue; + + /* Skip over assignment */ + skipuic = NULL; + if(uic->op == '=' && IS_ITEMP (IC_RESULT (uic)) && + bitVectnBitsOn (OP_DEFS (IC_RESULT (uic))) == 1 && bitVectnBitsOn (OP_USES (IC_RESULT (ic))) == 1 && bitVectnBitsOn (OP_USES (IC_RESULT (uic))) == 1 && + compareType (operandType (IC_RESULT (ic)), operandType (IC_RESULT (uic))) == 1) + { + skipuic = uic; + uic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_USES (IC_RESULT (uic)))); + } + + /* Try to handle a few cases where the result has multiple uses */ + else if(ic->op == '*' && bitsForType (operandType (IC_RESULT (ic))) > 16 && uic->op == '=' && + bitVectnBitsOn (OP_DEFS (IC_RESULT (uic))) == 1 && bitVectnBitsOn (OP_USES (IC_RESULT (ic))) == 1 && bitVectnBitsOn (OP_USES (IC_RESULT (uic))) > 1 && + compareType (operandType (IC_RESULT (ic)), operandType (IC_RESULT (uic))) == 1) + { + bool ok = true; + const bitVect *uses; + int bit; + + uses = bitVectCopy (OP_USES (IC_RESULT (uic))); + for (bit = bitVectFirstBit(uses); bitVectnBitsOn (uses); bitVectUnSetBit(uses, bit), bit = bitVectFirstBit(uses)) + { + iCode *uuic = hTabItemWithKey (iCodehTab, bit); + if (uuic->op != CAST || bitsForType (operandType (IC_RESULT (uuic))) > 16 || IS_BOOLEAN (operandType (IC_RESULT (uuic)))) + { + ok = false; + break; + } + } + + if (!ok) + continue; + + nextresulttype = newIntLink (); + SPEC_USIGN (nextresulttype) = 1; + sym = OP_SYMBOL (IC_RESULT (uic)); + sym->type = nextresulttype; + + nextresulttype = newIntLink (); + SPEC_USIGN (nextresulttype) = 1; + goto optimize; + } + + if (uic->op != CAST && uic->op != '+' && uic->op != LEFT_OP && uic->op != RIGHT_OP) + continue; + + /* Special handling since we might need more bits in the operand than in the result */ + if (ic->op == RIGHT_OP) + { + int shiftbits, resultbits; + + if (!IS_OP_LITERAL (IC_RIGHT (ic))) + continue; + + shiftbits = (int) operandLitValue (IC_RIGHT (ic)); + resultbits = bitsForType (operandType (IC_RESULT (uic))); + + if (resultbits + shiftbits > 16) + continue; + else if (resultbits + shiftbits > 8) + nextresulttype = newIntLink (); + else + nextresulttype = newCharLink (); + SPEC_USIGN (nextresulttype) = 1; + } + /* It must be a cast to another integer type that */ + /* has fewer bits */ + else if (uic->op == LEFT_OP || uic->op == RIGHT_OP) + { + /* Since shifting by the width of an operand or more is undefined behaviour, and no type is wider than 256 bits, + we can optimize when the result is used as right operand to a shift. */ + if(!isOperandEqual (IC_RESULT (ic), IC_RIGHT (uic)) || isOperandEqual (IC_RESULT (ic), IC_LEFT (uic))) + continue; + + nextresulttype = newCharLink (); + } + else + { + nextresulttype = operandType (IC_RESULT (uic)); + if (!IS_INTEGRAL (nextresulttype) && !(IS_PTR (nextresulttype) && NEARPTRSIZE == 2)) + continue; + + if (IS_PTR (nextresulttype)) + { + nextresulttype = newIntLink (); + SPEC_USIGN (nextresulttype) = 1; + } + else + nextresulttype = copyLinkChain (nextresulttype); + } + + nextresultsize = bitsForType (nextresulttype); + if (nextresultsize >= resultsize) + continue; + /* Cast to bool and bool-like types must be preserved to ensure that all nonzero values are correctly cast to true */ + if (uic->op == CAST && IS_BOOLEAN (nextresulttype)) + continue; + +optimize: + /* Make op result narrower */ + sym = OP_SYMBOL (IC_RESULT (ic)); + sym->type = nextresulttype; + + /* Insert casts on operands */ + if (ic->op != CAST) + { + if (IS_SYMOP (IC_LEFT (ic))) + { + newic = newiCode (CAST, operandFromLink (nextresulttype), IC_LEFT (ic)); + hTabAddItem (&iCodehTab, newic->key, newic); + bitVectSetBit (OP_USES (IC_LEFT (ic)), newic->key); + IC_RESULT (newic) = newiTempOperand (nextresulttype, 0); + OP_DEFS (IC_RESULT (newic)) = bitVectSetBit (OP_DEFS (IC_RESULT (newic)), newic->key); + bitVectUnSetBit (OP_USES (IC_LEFT (ic)), ic->key); + IC_LEFT (ic) = operandFromOperand (IC_RESULT (newic)); + OP_USES (IC_LEFT (ic)) = bitVectSetBit (OP_USES (IC_LEFT (ic)), ic->key); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + addiCodeToeBBlock (ebbs[i], newic, ic); + } + else + { + wassert (IS_OP_LITERAL (IC_LEFT (ic))); + IC_LEFT (ic) = operandFromValue (valCastLiteral (nextresulttype, operandLitValue (IC_LEFT (ic)), operandLitValue (IC_LEFT (ic)))); + } + if (ic->op != LEFT_OP && IS_SYMOP (IC_RIGHT (ic))) + { + newic = newiCode (CAST, operandFromLink (nextresulttype), IC_RIGHT (ic)); + hTabAddItem (&iCodehTab, newic->key, newic); + bitVectSetBit (OP_USES (IC_RIGHT (ic)), newic->key); + IC_RESULT (newic) = newiTempOperand (nextresulttype, 0); + OP_DEFS (IC_RESULT (newic)) = bitVectSetBit (OP_DEFS (IC_RESULT (newic)), newic->key); + bitVectUnSetBit (OP_USES (IC_RIGHT (ic)), ic->key); + IC_RIGHT (ic) = operandFromOperand (IC_RESULT (newic)); + OP_USES (IC_RIGHT (ic)) = bitVectSetBit (OP_USES (IC_RIGHT (ic)), ic->key); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + addiCodeToeBBlock (ebbs[i], newic, ic); + } + else if (ic->op != LEFT_OP && ic->op != UNARYMINUS) + { + wassert (IS_OP_LITERAL (IC_RIGHT (ic))); + IC_RIGHT (ic) = operandFromValue (valCastLiteral (nextresulttype, operandLitValue (IC_RIGHT (ic)), operandLitValue (IC_RIGHT (ic)))); + } + } + if (uic->op == CAST && ic->op != RIGHT_OP) + { + uic->op = '='; + if (skipuic) + { + bitVectUnSetBit (OP_USES (IC_RIGHT (uic)), uic->key); + IC_RIGHT (uic) = IC_RIGHT (skipuic); + OP_USES (IC_RIGHT (uic)) = bitVectSetBit (OP_USES (IC_RIGHT (uic)), uic->key); + } + } + change++; + } + } + } + + return change; +} + +/*-----------------------------------------------------------------*/ +/* Go back a chain of assigments / casts to try to find a string */ +/* literal symbol that op really is. */ +/*-----------------------------------------------------------------*/ +static symbol *findStrLitDef (operand *op, iCode **def) +{ + for(;;) + { + if (!IS_ITEMP (op)) + return (0); + + if (bitVectnBitsOn (OP_DEFS (op)) != 1) + return (0); + + iCode *dic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (op))); + + wassert (dic); + + if (dic->op == ADDRESS_OF) + { + if (def) + *def = dic; + symbol *sym = OP_SYMBOL (IC_LEFT (dic)); + return (sym->isstrlit ? sym : 0); + } + + if (dic->op != '=' && dic->op != CAST) + return (0); + + op = IC_RIGHT (dic); + } +} + +/*-----------------------------------------------------------------*/ +/* optimizeStdLibCall - optimize calls to standard library. */ +/* for now we just merge adjacent calls to puts() */ +/*-----------------------------------------------------------------*/ +static void +optimizeStdLibCall (eBBlock ** ebbs, int count) +{ + iCode *ic, *nic, *ndic; + symbol *strsym, *nstrsym, *cstrsym; + sym_link *strlink, *nstrlink; + size_t replacecost; + + for (int i = 0; i < count; i++) + { + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + // Look for call to puts(). + if (ic->op != CALL || !ic->prev || ic->prev->op != IPUSH && ic->prev->op != SEND) + continue; + if (!IS_SYMOP (IC_LEFT (ic)) || !OP_SYMBOL (IC_LEFT (ic))->rname || strcmp (OP_SYMBOL (IC_LEFT (ic))->rname, "_puts")) + continue; + + // Look for following call to puts(). + for (nic = ic->next; nic; nic = nic->next) + { + if (nic->op == '=' && !POINTER_SET (ic) || nic->op == CAST) + { + if (!IS_ITEMP (IC_RESULT (nic))) + break; + if (IS_OP_VOLATILE (IC_RIGHT (nic))) + break; + } + else if (nic->op == ADDRESS_OF) + { + if (!IS_ITEMP (IC_RESULT (nic))) + break; + } + else if (nic->op == IPUSH || nic->op == SEND) + { + if (IS_OP_VOLATILE (IC_LEFT (nic))) + break; + } + else // Todo: Handle more to make the optimization more general. + break; + } + if (!nic || nic->op != CALL || nic->prev->op != IPUSH && nic->prev->op != SEND) + continue; + if (!IS_SYMOP (IC_LEFT (nic)) || !OP_SYMBOL (IC_LEFT (nic))->rname || strcmp (OP_SYMBOL (IC_LEFT (nic))->rname, "_puts")) + continue; + + // Check that the return values are unused + if (IC_RESULT (ic) && (!IS_ITEMP (IC_RESULT (ic)) || bitVectnBitsOn (OP_USES (IC_RESULT (ic))))) + continue; + if (IC_RESULT (nic) && (!IS_ITEMP (IC_RESULT (nic)) || bitVectnBitsOn (OP_USES (IC_RESULT (nic))))) + continue; + + // Chek that their parameters are string literals + strsym = findStrLitDef (IC_LEFT (ic->prev), 0); + nstrsym = findStrLitDef (IC_LEFT (nic->prev), &ndic); + if (!strsym || !nstrsym) + continue; + strlink = strsym->etype; + nstrlink = nstrsym->etype; + + // Calculate the cost of doing the replacement in bytes of string literal + replacecost = 1; // For '\n' + if (strsym->isstrlit > 1) + replacecost += strlen (SPEC_CVAL (strlink).v_char); + if (nstrsym->isstrlit > 1) + replacecost += strlen (SPEC_CVAL (nstrlink).v_char); + + // Doing the replacement saves at least 6 bytes of call overhead (assuming pointers are 16 bits). + if (replacecost > 7 - optimize.codeSize + 4 * optimize.codeSpeed) + continue; + + // Combine strings + struct dbuf_s dbuf; + dbuf_init (&dbuf, 3); + dbuf_append_str(&dbuf, SPEC_CVAL (strlink).v_char); + dbuf_append_str(&dbuf, "\n"); + dbuf_append_str(&dbuf, SPEC_CVAL (nstrlink).v_char); + cstrsym = stringToSymbol (rawStrVal (dbuf_c_str (&dbuf), dbuf_get_length (&dbuf) + 1))->sym; + freeStringSymbol (nstrsym); + dbuf_destroy (&dbuf); + + // Make second call print the combined string (which allows further optimization with subsequent calls) + IC_LEFT (ndic)->key = cstrsym->key; + IC_LEFT (ndic)->svt.symOperand = cstrsym; + + // Change unused call to assignments to self to mark it for dead-code elimination. + bitVectSetBit (OP_USES (IC_LEFT (ic->prev)), ic->key); + bitVectSetBit (OP_DEFS (IC_LEFT (ic->prev)), ic->prev->key); + ic->op = '='; + IC_RESULT (ic) = IC_LEFT (ic->prev); + IC_RIGHT (ic) = IC_LEFT (ic->prev); + IC_LEFT (ic) = 0; + ic->prev->op = '='; + IC_RESULT (ic->prev) = IC_LEFT (ic->prev); + IC_RIGHT (ic->prev) = IC_LEFT (ic->prev); + IC_LEFT (ic->prev) = 0; + } + } +} + +/*-----------------------------------------------------------------*/ +/* optimizeCastCast - remove unneeded intermediate casts. */ +/* Integer promotion may cast (un)signed char to int and then */ +/* recast the int to (un)signed long. If the signedness of the */ +/* char and long are the same, the cast can be safely performed in */ +/* a single step. */ +/*-----------------------------------------------------------------*/ +static void +optimizeCastCast (eBBlock ** ebbs, int count) +{ + int i; + iCode *ic; + iCode *uic; + sym_link *type1; + sym_link *type2; + sym_link *type3; + symbol *sym; + int size1, size2, size3; + + for (i = 0; i < count; i++) + { + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (ic->op == CAST && IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic))) + { + type1 = operandType (IC_RIGHT (ic)); + type2 = operandType (IC_RESULT (ic)); + + /* Look only for a cast from an integer type to an */ + /* integer type that has no loss of bits */ + if (!IS_INTEGRAL (type1) || !IS_INTEGRAL (type2)) + continue; + size1 = bitsForType (type1); + size2 = bitsForType (type2); + if (size2 < size1) + continue; + /* If they are the same size, they must have the same signedness */ + if (size2 == size1 && SPEC_USIGN (type2) != SPEC_USIGN (type1)) + continue; + + /* There must be only one use of this first result */ + if (bitVectnBitsOn (OP_USES (IC_RESULT (ic))) != 1 || + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) != 1) + continue; + + uic = hTabItemWithKey (iCodehTab, + bitVectFirstBit (OP_USES (IC_RESULT (ic)))); + if(!uic || (uic->op != CAST && uic->op != BITWISEAND)) + continue; + + type3 = operandType (IC_RESULT (uic)); + + /* Cast to bool must be preserved to ensure that all nonzero values are correctly cast to true */ + if (SPEC_NOUN (type2) == V_BOOL && SPEC_NOUN(type3) != V_BOOL) + continue; + + /* Special case: Second use is a bit test */ + if (uic->op == BITWISEAND && IS_OP_LITERAL (IC_RIGHT (uic)) && ifxForOp (IC_RESULT (uic), uic)) + { + unsigned long long mask = operandLitValue (IC_RIGHT (uic)); + + /* Signed cast might set bits above the width of type1 */ + if (!SPEC_USIGN (type1) && (mask >> (bitsForType (type1)))) + continue; + + IC_RIGHT (uic) = operandFromValue (valCastLiteral (type1, operandLitValue (IC_RIGHT (uic)), operandLitValue (IC_RIGHT (uic)))); + } + else if (uic->op == CAST) /* Otherwise this use must be a second cast */ + { + /* It must be a cast to another integer type that */ + /* has no loss of bits */ + type3 = operandType (IC_RESULT (uic)); + if (!IS_INTEGRAL (type3)) + continue; + size3 = bitsForType (type3); + if (size3 < size1) + continue; + /* If they are the same size, they must have the same signedness */ + if (size3 == size2 && SPEC_USIGN (type3) != SPEC_USIGN (type2)) + continue; + + /* The signedness between the first and last types must match */ + if (SPEC_USIGN (type3) != SPEC_USIGN (type1)) + continue; + } + else + continue; + + + /* Change the first cast to a simple assignment and */ + /* let the second cast do all the work */ + ic->op = '='; + IC_LEFT (ic) = NULL; + + sym = OP_SYMBOL (IC_RESULT (ic)); + sym->type = copyLinkChain (type1); + sym->etype = getSpec (sym->type); + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* optimizeNegation - remove unneeded intermediate negation */ +/*-----------------------------------------------------------------*/ +static void +optimizeNegation (eBBlock **ebbs, int count) +{ + int i; + iCode *ic; + iCode *uic; + + for (i = 0; i < count; i++) + { + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (ic->op == '!' && IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic))) + { + /* There must be only one use of this first result */ + if (bitVectnBitsOn (OP_USES (IC_RESULT (ic))) != 1) + continue; + + /* This use must be an ifx */ + uic = hTabItemWithKey (iCodehTab, + bitVectFirstBit (OP_USES (IC_RESULT (ic)))); + if (!uic) + continue; + /* Todo: Optimize case where use is another negation */ + else if(uic->op == IFX) /* Remove negation by inverting jump targets */ + { + IC_LEFT (uic) = IC_LEFT (ic); + IC_LEFT (ic) = 0; + IC_RIGHT (ic) = IC_RESULT (ic); + ic->op = '='; + + if (IC_TRUE (uic)) + { + IC_FALSE (uic) = IC_TRUE (uic); + IC_TRUE (uic) = 0; + } + else + { + IC_TRUE (uic) = IC_FALSE (uic); + IC_FALSE (uic) = 0; + } + } + } + } + } +} + +/* Fold pointer addition into offset of ADDRESS_OF. */ +static void +offsetFoldGet (eBBlock **ebbs, int count) +{ + int i; + iCode *ic; + iCode *uic; + + if (!TARGET_Z80_LIKE && !TARGET_IS_STM8) + return; + + for (i = 0; i < count; i++) + { + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (ic->op == ADDRESS_OF && IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic))) + { + /* There must be only one use of the result */ + if (bitVectnBitsOn (OP_USES (IC_RESULT (ic))) != 1) + continue; + + /* This use must be an addition / subtraction */ + uic = hTabItemWithKey (iCodehTab, + bitVectFirstBit (OP_USES (IC_RESULT (ic)))); + + if (uic->op != '+' && uic->op != '-' || !IS_OP_LITERAL (IC_RIGHT (uic))) + continue; + + /* Historically ADDRESS_OF didn't have a right operand */ + wassertl (IC_RIGHT (ic), "ADDRESS_OF without right operand"); + wassertl (IS_OP_LITERAL (IC_RIGHT (ic)), "ADDRESS_OF with non-literal right operand"); + + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (ic))->uses, uic->key); + + if (uic->op == '+') + IC_RIGHT (uic) = operandFromLit (operandLitValue (IC_RIGHT (ic)) + operandLitValue (IC_RIGHT (uic))); + else + IC_RIGHT (uic) = operandFromLit (operandLitValue (IC_RIGHT (ic)) - operandLitValue (IC_RIGHT (uic))); + IC_LEFT (uic) = operandFromOperand (IC_LEFT(ic)); + uic->op = ADDRESS_OF; + IC_LEFT (uic)->isaddr = 1; + + ic->op = '='; + IC_RIGHT (ic) = IC_RESULT (ic); + IC_LEFT (ic) = 0; + SET_ISADDR (IC_RESULT (ic), 0); + } + } + } +} + +/* Fold pointer addition into offset of GET_VALUE_AT_ADDRESS. */ +/* The hc08-related ports do a similar thing in hc08/ralloc.c, packPointerOp() */ +static void +offsetFoldUse (eBBlock **ebbs, int count) +{ + int i; + iCode *ic; + iCode *uic; + + if (!TARGET_IS_Z80 && !TARGET_IS_Z180 && !TARGET_IS_RABBIT && !TARGET_IS_STM8) + return; + + for (i = 0; i < count; i++) + { + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if ((ic->op == '+' || ic->op == '-') && IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic))) + { + if (!IS_OP_LITERAL (IC_RIGHT (ic))) + continue; + + /* There must be only one use of the result */ + if (bitVectnBitsOn (OP_USES (IC_RESULT (ic))) != 1) + continue; + + /* This use must be a GET_VALUE_AT_ADDRESS */ + uic = hTabItemWithKey (iCodehTab, + bitVectFirstBit (OP_USES (IC_RESULT (ic)))); + + if (!POINTER_GET (uic)) + continue; + + /* Historically GET_VALUE_AT_ADDRESS didn't have a right operand */ + wassertl (IC_RIGHT (uic), "GET_VALUE_AT_ADDRESS without right operand"); + wassertl (IS_OP_LITERAL (IC_RIGHT (uic)), "GET_VALUE_AT_ADDRESS with non-literal right operand"); + + if (ic->op == '+') + IC_RIGHT (uic) = operandFromLit (operandLitValue (IC_RIGHT (uic)) + operandLitValue (IC_RIGHT (ic))); + else + IC_RIGHT (uic) = operandFromLit (operandLitValue (IC_RIGHT (uic)) - operandLitValue (IC_RIGHT (ic))); + + ic->op = '='; + IC_RIGHT (ic) = IC_LEFT (ic); + IC_LEFT (ic) = 0; + SET_ISADDR (IC_RESULT (ic), 0); + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* guessCounts - Guess execution counts for iCodes */ +/* Needs ic->seq assigned (typically done by computeLiveRanges() */ +/*-----------------------------------------------------------------*/ +void guessCounts (iCode *start_ic, ebbIndex *ebbi) +{ + iCode *ic; + int i; + bool needprop; + + for (ic = start_ic; ic; ic = ic->next) + ic->count = 0; + start_ic->pcount = 1.0f; + needprop = TRUE; + + for(i = 0; needprop && i < 24; i++) // 24 is an arbitrary limit to reduce runtime at the cost of accuracy. + { + needprop = FALSE; + for (ic = start_ic; ic; ic = ic->next) + { + if(ic->pcount <= 0.01) // 0.01 is an arbitrary limit to reduce runtime at the cost of accuracy. + continue; + + ic->count += ic->pcount; + + if (ic->op == GOTO) + { + iCode *target = hTabItemWithKey (labelDef, IC_LABEL (ic)->key); + target->pcount += ic->pcount; + needprop = TRUE; + } + else if(ic->op == IFX) // Use a classic, simple branch prediction. Works well for typical loops. + { + iCode *target = hTabItemWithKey (labelDef, (IC_TRUE (ic) ? IC_TRUE (ic) : IC_FALSE (ic))->key); + if(target->seq >= ic->seq) + { + target->pcount += ic->pcount / 4; + if(ic->next) + ic->next->pcount += ic->pcount * 3 / 4; + } + else + { + target->pcount += ic->pcount * 3 / 4; + if(ic->next) + ic->next->pcount += ic->pcount / 4; + } + needprop = TRUE; + } + else if(ic->op == JUMPTABLE) + { + symbol *label; + int n = elementsInSet (IC_JTLABELS (ic)); + + for (label = setFirstItem (IC_JTLABELS (ic)); label; label = setNextItem (IC_JTLABELS (ic))) + { + iCode *target = hTabItemWithKey (labelDef, label->key); + target->pcount += ic->pcount / n; + } + needprop = TRUE; + } + else if(ic->op == CALL && IS_SYMOP (IC_LEFT (ic)) && IFFUNC_ISNORETURN (OP_SYMBOL (IC_LEFT (ic))->type)) + ; + else if (ic->next) + ic->next->pcount += ic->pcount; + ic->pcount = 0.0f; + } + } +} + +/*-----------------------------------------------------------------*/ +/* narrowRead() - Will read fewer bytes by eliminating a downcast. */ +/*-----------------------------------------------------------------*/ +static int +narrowRead (iCode *ic, operand **opp, eBBlock *ebp) +{ + iCode *dic; + operand *op = *opp; + + if (ic->op != CAST || !IS_ITEMP (op)) + return 0; + + if (bitVectnBitsOn (OP_USES (op)) != 1 || bitVectnBitsOn (OP_DEFS (op)) != 1) + return 0; + + // get the definition + if (!(dic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (op))))) + return 0; + + // found the definition now check if it is local + if (dic->seq < ebp->fSeq || dic->seq > ebp->lSeq) + return 0; + + // for now handle pointer reads only + if (dic->op != GET_VALUE_AT_ADDRESS || IS_VOLATILE (operandType (IC_LEFT (dic))->next)) + return 0; + + sym_link *resulttype = operandType (IC_RESULT (ic)); + sym_link *righttype = operandType (IC_RIGHT (ic)); + + if (IS_BOOL (resulttype) || getSize (resulttype) >= getSize (righttype)) + return 0; + + // Narrow read + if (!port->little_endian) + { + int offset = getSize (righttype) - getSize (resulttype); + IC_RIGHT (dic) = operandFromLit (operandLitValue (IC_RIGHT (dic)) + offset); + } + OP_SYMBOL (IC_RESULT (dic))->type = resulttype; + ic->op = '='; + + return 1; +} + +/*-----------------------------------------------------------------*/ +/* narrowRead() - Will read fewer bytes by eliminating downcasts. */ +/*-----------------------------------------------------------------*/ +static void +narrowReads(ebbIndex *ebbi) +{ + for (int i = 0; i < ebbi->count; i++) + { + eBBlock **ebbs = ebbi->bbOrder; + eBBlock *ebp = ebbs[i]; + + for (iCode *ic = ebp->sch; ic; ic = ic->next) + if (ic->op == CAST) + narrowRead (ic, &(IC_RIGHT (ic)), ebp); + } +} + +/*-----------------------------------------------------------------*/ +/* eBBlockFromiCode - creates extended basic blocks from iCode */ +/* will return an array of eBBlock pointers */ +/*-----------------------------------------------------------------*/ +eBBlock ** +eBBlockFromiCode (iCode *ic) +{ + ebbIndex *ebbi = NULL; + int change = 1; + int lchange = 0; + int kchange = 0; + hTab *loops; + + /* if nothing passed then return nothing */ + if (!ic) + return NULL; + + eBBNum = 0; + + /* optimize the chain for labels & gotos + this will eliminate redundant labels and + will change jump to jumps by jumps */ + ic = iCodeLabelOptimize (ic); + + /* break it down into basic blocks */ + ebbi = iCodeBreakDown (ic); + /* hash the iCode keys so that we can quickly index */ + /* them in the rest of the optimization steps */ + setToNull ((void *) &iCodehTab); + iCodehTab = newHashTable (iCodeKey); + hashiCodeKeys (ebbi->bbOrder, ebbi->count); + + /* compute the control flow */ + computeControlFlow (ebbi); + + /* dumpraw if asked for */ + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_RAW0, ebbi); + + /* replace the local variables with their + register equivalents : the liveRange computation + along with the register allocation will determine + if it finally stays in the registers */ + replaceRegEqv (ebbi); + + /* create loop regions */ + loops = createLoopRegions (ebbi); + + /* dumpraw if asked for */ + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_RAW1, ebbi); + + if (!optimize.noStdLibCall) + optimizeStdLibCall (ebbi->bbOrder, ebbi->count); + + optimizeCastCast (ebbi->bbOrder, ebbi->count); + while (optimizeOpWidth (ebbi->bbOrder, ebbi->count)) + optimizeCastCast (ebbi->bbOrder, ebbi->count); + optimizeNegation (ebbi->bbOrder, ebbi->count); + + /* Burn the corpses, so the dead may rest in peace, + safe from cse necromancy */ + computeDataFlow (ebbi); + killDeadCode (ebbi); + + /* do common subexpression elimination for each block */ + change = cseAllBlocks (ebbi, FALSE); + + /* dumpraw if asked for */ + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_CSE, ebbi); + + /* compute the data flow */ + computeDataFlow (ebbi); + + /* dumpraw if asked for */ + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_DFLOW, ebbi); + + /* global common subexpression elimination */ + if (optimize.global_cse) + { + change += cseAllBlocks (ebbi, FALSE); + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_GCSE, ebbi); + } + else + { + // compute the dataflow only + assert(cseAllBlocks (ebbi, TRUE)==0); + } + + /* kill dead code */ + kchange = killDeadCode (ebbi); + + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_DEADCODE, ebbi); + + /* do loop optimizations */ + change += (lchange = loopOptimizations (loops, ebbi)); + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_LOOP, ebbi); + + /* recompute the data flow and apply global cse again + if loops optimizations or dead code caused a change: + loops will brings out of the loop which then may be + available for use in the later blocks: dead code + elimination could potentially disconnect some blocks + conditional flow may be efected so we need to apply + subexpression once more */ + if (lchange || kchange) + { + computeDataFlow (ebbi); + change += cseAllBlocks (ebbi, FALSE); + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_LOOPG, ebbi); + + /* if loop optimizations caused a change then do + dead code elimination once more : this will + get rid of the extra assignments to the induction + variables created during loop optimizations */ + killDeadCode (ebbi); + + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_LOOPD, ebbi); + } + + offsetFoldGet (ebbi->bbOrder, ebbi->count); + + /* lospre */ + computeControlFlow (ebbi); + loops = createLoopRegions (ebbi); + computeDataFlow (ebbi); + computeLiveRanges (ebbi->bbOrder, ebbi->count, FALSE); + while (optimizeOpWidth (ebbi->bbOrder, ebbi->count)) + optimizeCastCast (ebbi->bbOrder, ebbi->count); + adjustIChain (ebbi->bbOrder, ebbi->count); + ic = iCodeLabelOptimize (iCodeFromeBBlock (ebbi->bbOrder, ebbi->count)); + shortenLiveRanges (ic, ebbi); + guessCounts (ic, ebbi); + if (optimize.lospre && (TARGET_Z80_LIKE || TARGET_HC08_LIKE || TARGET_IS_STM8)) /* For mcs51, we get a code size regression with lospre enabled, since the backend can't deal well with the added temporaries */ + { + lospre (ic, ebbi); + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_LOSPRE, ebbi); + + /* GCSE, lospre and maybe other optimizations sometimes create temporaries that have non-connected live ranges, which is bad. Split them. */ + freeeBBlockData (ebbi); + ebbi = iCodeBreakDown (ic); + computeControlFlow (ebbi); + loops = createLoopRegions (ebbi); + computeDataFlow (ebbi); + recomputeLiveRanges (ebbi->bbOrder, ebbi->count, FALSE); + adjustIChain (ebbi->bbOrder, ebbi->count); + ic = iCodeLabelOptimize (iCodeFromeBBlock (ebbi->bbOrder, ebbi->count)); + separateLiveRanges (ic, ebbi); + } + + /* Break down again and redo some steps to not confuse live range analysis later. */ + freeeBBlockData (ebbi); + ebbi = iCodeBreakDown (ic); + computeControlFlow (ebbi); + loops = createLoopRegions (ebbi); + computeDataFlow (ebbi); + + killDeadCode (ebbi); + + offsetFoldUse (ebbi->bbOrder, ebbi->count); + killDeadCode (ebbi); + + /* sort it back by block number */ + //qsort (ebbs, saveCount, sizeof (eBBlock *), bbNumCompare); + + /* enforce restrictions on acesses to named address spaces */ + separateAddressSpaces (ebbi->bbOrder, ebbi->count); + + /* insert bank switching instructions. Do it here, before the + other support routines, since we can assume that there is no + bank switching happening in those other support routines + (but assume that it can happen in other functions) */ + adjustIChain (ebbi->bbOrder, ebbi->count); + ic = iCodeLabelOptimize (iCodeFromeBBlock (ebbi->bbOrder, ebbi->count)); + if (!currFunc || switchAddressSpacesOptimally (ic, ebbi)) + switchAddressSpaces (ic); /* Fallback. Very unlikely to be triggered, unless --max-allocs-per-node is set to very small values or very weird control-flow graphs */ + + /* Break down again and redo some steps to not confuse live range analysis. */ + freeeBBlockData (ebbi); + ebbi = iCodeBreakDown (ic); + computeControlFlow (ebbi); + loops = createLoopRegions (ebbi); + computeDataFlow (ebbi); + + if (!options.lessPedantic) + { + // this is a good place to check missing return values + if (currFunc) + { + // the user is on his own with naked functions... + if (!IS_VOID(currFunc->etype) && !FUNC_ISNAKED(currFunc->type)) + { + eBBlock *bp; + // make sure all predecessors of the last block end in a return + for (bp=setFirstItem(ebbi->bbOrder[ebbi->count-1]->predList); + bp; + bp=setNextItem(ebbi->bbOrder[ebbi->count-1]->predList)) + { + if (bp->ech->op != RETURN) + { + werrorfl (bp->ech->filename, bp->ech->lineno, W_VOID_FUNC, currFunc->name); + } + } + } + } + } + + /* if cyclomatic info requested then print it */ + if (options.cyclomatic) + printCyclomatic (ebbi->bbOrder, ebbi->count); + + /* convert operations with support routines + written in C to function calls : I am doing + this at this point since I want all the + operations to be as they are for optimizations */ + convertToFcall (ebbi->bbOrder, ebbi->count); + + /* miscelaneous optimizations */ + miscOpt (ebbi->bbOrder, ebbi->count); + + /* Split any live-ranges that became non-connected in dead code elimination. */ + change = 0; + do + { + recomputeLiveRanges (ebbi->bbOrder, ebbi->count, FALSE); + adjustIChain (ebbi->bbOrder, ebbi->count); + ic = iCodeLabelOptimize (iCodeFromeBBlock (ebbi->bbOrder, ebbi->count)); + change = separateLiveRanges (ic, ebbi); + freeeBBlockData (ebbi); + ebbi = iCodeBreakDown (ic); + computeControlFlow (ebbi); + loops = createLoopRegions (ebbi); + computeDataFlow (ebbi); + } + while (change); + killDeadCode (ebbi); /* iCodeLabelOptimize() above might result in dead code, when both branches of an ifx go to the same destination. */ + + /* compute the live ranges */ + recomputeLiveRanges (ebbi->bbOrder, ebbi->count, TRUE); + + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_RANGE, ebbi); + + /* Now that we have the live ranges, discard parameter + * receives for unused parameters. + */ + discardDeadParamReceives (ebbi->bbOrder, ebbi->count); + + narrowReads(ebbi); + + /* allocate registers & generate code */ + port->assignRegisters (ebbi); + + /* throw away blocks */ + setToNull ((void *) &graphEdges); + freeeBBlockData (ebbi); + + return NULL; +} + |
