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/hc08/ralloc.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/hc08/ralloc.c')
| -rw-r--r-- | src/hc08/ralloc.c | 2423 |
1 files changed, 2423 insertions, 0 deletions
diff --git a/src/hc08/ralloc.c b/src/hc08/ralloc.c new file mode 100644 index 0000000..d6f9573 --- /dev/null +++ b/src/hc08/ralloc.c @@ -0,0 +1,2423 @@ +/*------------------------------------------------------------------------ + + SDCCralloc.c - source file for register allocation. 68HC08 specific + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "common.h" +#include "ralloc.h" +#include "gen.h" +#include "dbuf_string.h" + +/*-----------------------------------------------------------------*/ +/* At this point we start getting processor specific although */ +/* some routines are non-processor specific & can be reused when */ +/* targetting other processors. The decision for this will have */ +/* to be made on a routine by routine basis */ +/* routines used to pack registers are most definitely not reusable */ +/* since the pack the registers depending strictly on the MCU */ +/*-----------------------------------------------------------------*/ + +extern void genhc08Code (iCode *); + +#define D(x) + +// Build the old allocator. It can be used by command-line options +#define OLDRALLOC 1 + +/* Global data */ +static struct + { + bitVect *spiltSet; + set *stackSpil; + bitVect *regAssigned; + bitVect *totRegAssigned; /* final set of LRs that got into registers */ + short blockSpil; + int slocNum; + bitVect *funcrUsed; /* registers used in a function */ + int stackExtend; + int dataExtend; + } +_G; + +/* Shared with gen.c */ +int hc08_ptrRegReq; /* one byte pointer register required */ + +/* 6808 registers */ +reg_info regshc08[] = +{ + + {REG_GPR, A_IDX, "a", HC08MASK_A, NULL, 0, 1}, + {REG_GPR, X_IDX, "x", HC08MASK_X, NULL, 0, 1}, + {REG_GPR, H_IDX, "h", HC08MASK_H, NULL, 0, 1}, + {REG_PTR, HX_IDX, "hx", HC08MASK_HX, NULL, 0, 1}, + {REG_GPR, XA_IDX, "xa", HC08MASK_XA, NULL, 0, 1}, + + {REG_CND, CND_IDX, "C", 0, NULL, 0, 1}, + {0, SP_IDX, "sp", 0, NULL, 0, 1}, +}; +int hc08_nRegs = 7; + +reg_info *hc08_reg_a; +reg_info *hc08_reg_x; +reg_info *hc08_reg_h; +reg_info *hc08_reg_hx; +reg_info *hc08_reg_xa; +reg_info *hc08_reg_sp; + +static void spillThis (symbol *); +static void freeAllRegs (); + +/*-----------------------------------------------------------------*/ +/* hc08_regWithIdx - returns pointer to register with index number */ +/*-----------------------------------------------------------------*/ +reg_info * +hc08_regWithIdx (int idx) +{ + int i; + + for (i = 0; i < hc08_nRegs; i++) + if (regshc08[i].rIdx == idx) + return ®shc08[i]; + + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "regWithIdx not found"); + exit (1); +} + +/*-----------------------------------------------------------------*/ +/* hc08_freeReg - frees a register */ +/*-----------------------------------------------------------------*/ +void +hc08_freeReg (reg_info * reg) +{ + if (!reg) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "hc08_freeReg - Freeing NULL register"); + exit (1); + } + + reg->isFree = 1; + + switch (reg->rIdx) + { + case A_IDX: + if (hc08_reg_x->isFree) + hc08_reg_xa->isFree = 1; + break; + case X_IDX: + if (hc08_reg_a->isFree) + hc08_reg_xa->isFree = 1; + if (hc08_reg_h->isFree) + hc08_reg_hx->isFree = 1; + break; + case H_IDX: + if (hc08_reg_x->isFree) + hc08_reg_hx->isFree = 1; + break; + case HX_IDX: + hc08_reg_h->isFree = 1; + hc08_reg_x->isFree = 1; + if (hc08_reg_a->isFree) + hc08_reg_xa->isFree = 1; + break; + case XA_IDX: + hc08_reg_x->isFree = 1; + hc08_reg_a->isFree = 1; + if (hc08_reg_h->isFree) + hc08_reg_hx->isFree = 1; + break; + default: + break; + } +} + + +/*-----------------------------------------------------------------*/ +/* hc08_useReg - marks a register as used */ +/*-----------------------------------------------------------------*/ +void +hc08_useReg (reg_info * reg) +{ + reg->isFree = 0; + + switch (reg->rIdx) + { + case A_IDX: + hc08_reg_xa->aop = NULL; + hc08_reg_xa->isFree = 0; + break; + case X_IDX: + hc08_reg_xa->aop = NULL; + hc08_reg_xa->isFree = 0; + hc08_reg_hx->aop = NULL; + hc08_reg_hx->isFree = 0; + break; + case H_IDX: + hc08_reg_hx->aop = NULL; + hc08_reg_hx->isFree = 0; + break; + case HX_IDX: + hc08_reg_h->aop = NULL; + hc08_reg_h->isFree = 0; + hc08_reg_x->aop = NULL; + hc08_reg_x->isFree = 0; + break; + case XA_IDX: + hc08_reg_x->aop = NULL; + hc08_reg_x->isFree = 0; + hc08_reg_a->aop = NULL; + hc08_reg_a->isFree = 0; + break; + default: + break; + } +} + +/*-----------------------------------------------------------------*/ +/* hc08_dirtyReg - marks a register as dirty */ +/*-----------------------------------------------------------------*/ +void +hc08_dirtyReg (reg_info * reg, bool freereg) +{ + reg->aop = NULL; + + switch (reg->rIdx) + { + case A_IDX: + hc08_reg_xa->aop = NULL; + hc08_reg_xa->isLitConst = 0; + hc08_reg_a->aop = NULL; + hc08_reg_a->isLitConst = 0; + break; + case X_IDX: + hc08_reg_xa->aop = NULL; + hc08_reg_xa->isLitConst = 0; + hc08_reg_hx->aop = NULL; + hc08_reg_hx->isLitConst = 0; + hc08_reg_x->aop = NULL; + hc08_reg_x->isLitConst = 0; + break; + case H_IDX: + hc08_reg_hx->aop = NULL; + hc08_reg_hx->isLitConst = 0; + hc08_reg_h->aop = NULL; + hc08_reg_h->isLitConst = 0; + break; + case HX_IDX: + hc08_reg_hx->aop = NULL; + hc08_reg_hx->isLitConst = 0; + hc08_reg_h->aop = NULL; + hc08_reg_h->isLitConst = 0; + hc08_reg_x->aop = NULL; + hc08_reg_x->isLitConst = 0; + break; + case XA_IDX: + hc08_reg_xa->aop = NULL; + hc08_reg_xa->isLitConst = 0; + hc08_reg_x->aop = NULL; + hc08_reg_x->isLitConst = 0; + hc08_reg_a->aop = NULL; + hc08_reg_a->isLitConst = 0; + break; + default: + break; + } + if (freereg) + hc08_freeReg(reg); +} + +/*-----------------------------------------------------------------*/ +/* noOverLap - will iterate through the list looking for over lap */ +/*-----------------------------------------------------------------*/ +static int +noOverLap (set * itmpStack, symbol * fsym) +{ + symbol *sym; + + for (sym = setFirstItem (itmpStack); sym; + sym = setNextItem (itmpStack)) + { + if (bitVectBitValue(sym->clashes,fsym->key)) return 0; + } + return 1; +} + +/*-----------------------------------------------------------------*/ +/* isFree - will return 1 if the a free spil location is found */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (isFree) +{ + symbol *sym = item; + V_ARG (symbol **, sloc); + V_ARG (symbol *, fsym); + + /* if already found */ + if (*sloc) + return 0; + + /* if it is free && and the itmp assigned to + this does not have any overlapping live ranges + with the one currently being assigned and + the size can be accomodated */ + if (sym->isFree && + noOverLap (sym->usl.itmpStack, fsym) && + getSize (sym->type) >= getSize (fsym->type)) + { + *sloc = sym; + return 1; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* createStackSpil - create a location on the stack to spil */ +/*-----------------------------------------------------------------*/ +static symbol * +createStackSpil (symbol * sym) +{ + symbol *sloc = NULL; + struct dbuf_s dbuf; + int useXstack, model; + + /* first go try and find a free one that is already + existing on the stack */ + if (applyToSet (_G.stackSpil, isFree, &sloc, sym)) + { + /* found a free one : just update & return */ + sym->usl.spillLoc = sloc; + sym->stackSpil = 1; + sloc->isFree = 0; + addSetHead (&sloc->usl.itmpStack, sym); + return sym; + } + + /* could not then have to create one , this is the hard part + we need to allocate this on the stack : this is really a + hack!! but cannot think of anything better at this time */ + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "sloc%d", _G.slocNum++); + sloc = newiTemp (dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + + /* set the type to the spilling symbol */ + sloc->type = copyLinkChain (sym->type); + sloc->etype = getSpec (sloc->type); + SPEC_SCLS (sloc->etype) = S_DATA; + SPEC_EXTR (sloc->etype) = 0; + SPEC_STAT (sloc->etype) = 0; + SPEC_VOLATILE(sloc->etype) = 0; + SPEC_ABSA(sloc->etype) = 0; + + /* we don't allow it to be allocated + onto the external stack since : so we + temporarily turn it off ; we also + turn off memory model to prevent + the spil from going to the external storage + */ + + useXstack = options.useXstack; + model = options.model; +/* noOverlay = options.noOverlay; */ +/* options.noOverlay = 1; */ + options.model = options.useXstack = 0; + + allocLocal (sloc); + + options.useXstack = useXstack; + options.model = model; +/* options.noOverlay = noOverlay; */ + sloc->isref = 1; /* to prevent compiler warning */ + + /* if it is on the stack then update the stack */ + if (IN_STACK (sloc->etype)) + { + currFunc->stack += getSize (sloc->type); + _G.stackExtend += getSize (sloc->type); + } + else + _G.dataExtend += getSize (sloc->type); + + /* add it to the _G.stackSpil set */ + addSetHead (&_G.stackSpil, sloc); + sym->usl.spillLoc = sloc; + sym->stackSpil = 1; + + /* add it to the set of itempStack set + of the spill location */ + addSetHead (&sloc->usl.itmpStack, sym); + return sym; +} + +/*-----------------------------------------------------------------*/ +/* spillThis - spils a specific operand */ +/*-----------------------------------------------------------------*/ +static void +spillThis (symbol * sym) +{ + int i; + /* if this is rematerializable or has a spillLocation + we are okay, else we need to create a spillLocation + for it */ + if (!(sym->remat || sym->usl.spillLoc)) + createStackSpil (sym); + + /* mark it as spilt & put it in the spilt set */ + sym->isspilt = sym->spillA = 1; + _G.spiltSet = bitVectSetBit (_G.spiltSet, sym->key); + + bitVectUnSetBit (_G.regAssigned, sym->key); + bitVectUnSetBit (_G.totRegAssigned, sym->key); + + for (i = 0; i < sym->nRegs; i++) + { + if (sym->regs[i]) + { + hc08_freeReg (sym->regs[i]); + sym->regs[i] = NULL; + } + } + + if (sym->usl.spillLoc && !sym->remat) + sym->usl.spillLoc->allocreq++; + return; +} + +/*-----------------------------------------------------------------*/ +/* updateRegUsage - update the registers in use at the start of */ +/* this icode */ +/*-----------------------------------------------------------------*/ +static void +updateRegUsage (iCode * ic) +{ + int reg; + + // update the registers in use at the start of this icode + for (reg=0; reg<hc08_nRegs; reg++) + { + if (regshc08[reg].isFree) + { + ic->riu &= ~(regshc08[reg].mask); + } + else + { + ic->riu |= (regshc08[reg].mask); + } + } +} + +/*-----------------------------------------------------------------*/ +/* deassignLRs - check the live to and if they have registers & are */ +/* not spilt then free up the registers */ +/*-----------------------------------------------------------------*/ +static void +deassignLRs (iCode * ic, eBBlock * ebp) +{ + symbol *sym; + int k; + + for (sym = hTabFirstItem (liveRanges, &k); sym; + sym = hTabNextItem (liveRanges, &k)) + { + /* if it does not end here */ + if (sym->liveTo > ic->seq) + continue; + + /* if it was spilt on stack then we can + mark the stack spil location as free */ + if (sym->isspilt) + { + if (sym->stackSpil) + { + sym->usl.spillLoc->isFree = 1; + sym->stackSpil = 0; + } + continue; + } + } +} + + +/*-----------------------------------------------------------------*/ +/* reassignLR - reassign this to registers */ +/*-----------------------------------------------------------------*/ +static void +reassignLR (operand * op) +{ + symbol *sym = OP_SYMBOL (op); + int i; + + /* not spilt any more */ + sym->isspilt = sym->spillA = sym->blockSpil = sym->remainSpil = 0; + bitVectUnSetBit (_G.spiltSet, sym->key); + + _G.regAssigned = bitVectSetBit (_G.regAssigned, sym->key); + _G.totRegAssigned = bitVectSetBit (_G.totRegAssigned, sym->key); + + _G.blockSpil--; + + for (i = 0; i < sym->nRegs; i++) + sym->regs[i]->isFree = 0; +} + +/*------------------------------------------------------------------*/ +/* verifyRegsAssigned - make sure an iTemp is properly initialized; */ +/* it should either have registers or have beed spilled. Otherwise, */ +/* there was an uninitialized variable, so just spill this to get */ +/* the operand in a valid state. */ +/*------------------------------------------------------------------*/ +static void +verifyRegsAssigned (operand *op, iCode * ic) +{ + symbol * sym; + + if (!op) return; + if (!IS_ITEMP (op)) return; + + sym = OP_SYMBOL (op); + if (sym->isspilt) return; + if (!sym->nRegs) return; + if (sym->regs[0]) return; + + /* Don't warn for new allocator, since this is not used by default (until Thoruop is implemented for spillocation compaction). */ + /*if (z80_opts.oldralloc) + werrorfl (ic->filename, ic->lineno, W_LOCAL_NOINIT, sym->prereqv ? sym->prereqv->name : sym->name);*/ + + spillThis (sym); +} + + +/*-----------------------------------------------------------------*/ +/* serialRegAssign - serially allocate registers to the variables */ +/*-----------------------------------------------------------------*/ +static void +serialRegAssign (eBBlock ** ebbs, int count) +{ + int i; + + /* for all blocks */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && + (ebbs[i]->entryLabel != entryLabel && + ebbs[i]->entryLabel != returnLabel)) + continue; + + /* for all instructions do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + updateRegUsage(ic); + + /* if this is an ipop that means some live + range will have to be assigned again */ + if (ic->op == IPOP) + reassignLR (IC_LEFT (ic)); + + /* if result is present && is a true symbol */ + if (IC_RESULT (ic) && ic->op != IFX && + IS_TRUE_SYMOP (IC_RESULT (ic))) + { + OP_SYMBOL (IC_RESULT (ic))->allocreq++; + } + + /* take away registers from live + ranges that end at this instruction */ + deassignLRs (ic, ebbs[i]); + + /* some don't need registers */ + if (SKIP_IC2 (ic) || + ic->op == JUMPTABLE || + ic->op == IFX || + ic->op == IPUSH || + ic->op == IPOP || + (IC_RESULT (ic) && POINTER_SET (ic))) + { + continue; + } + + /* now we need to allocate registers only for the result */ + if (IC_RESULT (ic)) + { + symbol *sym = OP_SYMBOL (IC_RESULT (ic)); + + /* Make sure any spill location is definitely allocated */ + if (sym->isspilt && !sym->remat && sym->usl.spillLoc && + !sym->usl.spillLoc->allocreq) + { + sym->usl.spillLoc->allocreq++; + } + + /* if it does not need or is spilt + or is already assigned to registers + or will not live beyond this instructions */ + if (!sym->nRegs || + sym->isspilt || + bitVectBitValue (_G.regAssigned, sym->key) || + sym->liveTo <= ic->seq) + { + continue; + } + + spillThis (sym); + } + } + } + + /* Check for and fix any problems with uninitialized operands */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && + (ebbs[i]->entryLabel != entryLabel && + ebbs[i]->entryLabel != returnLabel)) + { + continue; + } + + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (SKIP_IC2 (ic)) + continue; + + if (ic->op == IFX) + { + verifyRegsAssigned (IC_COND (ic), ic); + continue; + } + + if (ic->op == JUMPTABLE) + { + verifyRegsAssigned (IC_JTCOND (ic), ic); + continue; + } + + verifyRegsAssigned (IC_RESULT (ic), ic); + verifyRegsAssigned (IC_LEFT (ic), ic); + verifyRegsAssigned (IC_RIGHT (ic), ic); + } + } +} + +/*-----------------------------------------------------------------*/ +/* rUmaskForOp :- returns register mask for an operand */ +/*-----------------------------------------------------------------*/ +bitVect * +hc08_rUmaskForOp (operand * op) +{ + bitVect *rumask; + symbol *sym; + int j; + + /* only temporaries are assigned registers */ + if (!IS_ITEMP (op)) + return NULL; + + sym = OP_SYMBOL (op); + + /* if spilt or no registers assigned to it + then nothing */ + if (sym->isspilt || !sym->nRegs) + return NULL; + + rumask = newBitVect (hc08_nRegs); + + for (j = 0; j < sym->nRegs; j++) + { + rumask = bitVectSetBit (rumask, sym->regs[j]->rIdx); + } + + return rumask; +} + +/*-----------------------------------------------------------------*/ +/* regsUsedIniCode :- returns bit vector of registers used in iCode */ +/*-----------------------------------------------------------------*/ +static bitVect * +regsUsedIniCode (iCode * ic) +{ + bitVect *rmask = newBitVect (hc08_nRegs); + + /* do the special cases first */ + if (ic->op == IFX) + { + rmask = bitVectUnion (rmask, hc08_rUmaskForOp (IC_COND (ic))); + goto ret; + } + + /* for the jumptable */ + if (ic->op == JUMPTABLE) + { + rmask = bitVectUnion (rmask, hc08_rUmaskForOp (IC_JTCOND (ic))); + goto ret; + } + + /* of all other cases */ + if (IC_LEFT (ic)) + rmask = bitVectUnion (rmask, hc08_rUmaskForOp (IC_LEFT (ic))); + + if (IC_RIGHT (ic)) + rmask = bitVectUnion (rmask, hc08_rUmaskForOp (IC_RIGHT (ic))); + + if (IC_RESULT (ic)) + rmask = bitVectUnion (rmask, hc08_rUmaskForOp (IC_RESULT (ic))); + +ret: + return rmask; +} + +/*-----------------------------------------------------------------*/ +/* createRegMask - for each instruction will determine the regsUsed */ +/*-----------------------------------------------------------------*/ +static void +createRegMask (eBBlock ** ebbs, int count) +{ + int i; + + /* for all blocks */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && + (ebbs[i]->entryLabel != entryLabel && + ebbs[i]->entryLabel != returnLabel)) + continue; + + /* for all instructions */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + int j; + + if (SKIP_IC2 (ic) || !ic->rlive) + continue; + + /* first mark the registers used in this + instruction */ + ic->rSurv = newBitVect(port->num_regs); + ic->rUsed = regsUsedIniCode (ic); + _G.funcrUsed = bitVectUnion (_G.funcrUsed, ic->rUsed); + + /* now create the register mask for those + registers that are in use : this is a + super set of ic->rUsed */ + ic->rMask = newBitVect (hc08_nRegs + 1); + + /* for all live Ranges alive at this point */ + for (j = 1; j < ic->rlive->size; j++) + { + symbol *sym; + int k; + + /* if not alive then continue */ + if (!bitVectBitValue (ic->rlive, j)) + continue; + + /* find the live range we are interested in */ + if (!(sym = hTabItemWithKey (liveRanges, j))) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "createRegMask cannot find live range"); + fprintf(stderr, "\tmissing live range: key=%d\n", j); + exit (0); + } + + /* if no register assigned to it */ + if (!sym->nRegs || sym->isspilt) + continue; + + /* for all the registers allocated to it */ + for (k = 0; k < sym->nRegs; k++) + { + if (!sym->regs[k]) + continue; + ic->rMask = bitVectSetBit (ic->rMask, sym->regs[k]->rIdx); + if (sym->liveTo != ic->key) + ic->rSurv = bitVectSetBit (ic->rSurv, sym->regs[k]->rIdx); + } + } + } + } +} + + +/*-----------------------------------------------------------------*/ +/* regTypeNum - computes the type & number of registers required */ +/*-----------------------------------------------------------------*/ +static void +regTypeNum (eBBlock *ebbs) +{ + symbol *sym; + int k; + + /* for each live range do */ + for (sym = hTabFirstItem (liveRanges, &k); sym; + sym = hTabNextItem (liveRanges, &k)) + { + /* if used zero times then no registers needed */ + if ((sym->liveTo - sym->liveFrom) == 0) + continue; + + /* if the live range is a temporary */ + if (sym->isitmp) + { + /* if the type is marked as a conditional */ + if (sym->regType == REG_CND) + continue; + + /* if used in return only then we don't + need registers */ + if (sym->ruonly || sym->accuse) + { + if (IS_AGGREGATE (sym->type) || sym->isptr) + sym->type = aggrToPtr (sym->type, FALSE); + continue; + } + + /* if not then we require registers */ + sym->nRegs = ((IS_AGGREGATE (sym->type) || sym->isptr) ? + getSize (sym->type = aggrToPtr (sym->type, FALSE)) : + getSize (sym->type)); + + if (sym->nRegs > 8) + { + fprintf (stderr, "allocated more than 8 registers for type "); + printTypeChain (sym->type, stderr); + fprintf (stderr, "\n"); + } + + /* determine the type of register required */ + if (sym->nRegs == 1 && IS_PTR (sym->type) && sym->uptr) + sym->regType = REG_PTR; + else + sym->regType = REG_GPR; + } + else + /* for the first run we don't provide */ + /* registers for true symbols we will */ + /* see how things go */ + sym->nRegs = 0; + } +} + +/*-----------------------------------------------------------------*/ +/* freeAllRegs - mark all registers as free */ +/*-----------------------------------------------------------------*/ +static void +freeAllRegs () +{ + int i; + + for (i = 0; i < hc08_nRegs; i++) + { + regshc08[i].isFree = 1; + regshc08[i].aop = NULL; + } +} + +/*-----------------------------------------------------------------*/ +/* deallocStackSpil - this will set the stack pointer back */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (deallocStackSpil) +{ + symbol *sym = item; + + deallocLocal (sym); + return 0; +} + + +#if 0 +static void +packRegsForLiteral (iCode * ic) +{ + int k; + iCode *uic; + + if (ic->op != '=') + return; + if (POINTER_SET (ic)) + return; + if (!IS_LITERAL (getSpec (operandType (IC_RIGHT (ic))))) + return; + if (bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) > 1) + return; + + for (k=0; k< OP_USES (IC_RESULT (ic))->size; k++) + if (bitVectBitValue (OP_USES (IC_RESULT (ic)), k)) + { + uic = hTabItemWithKey (iCodehTab, k); + if (!uic) continue; + + if (uic->op != IFX && uic->op != JUMPTABLE) + { + if (IC_LEFT (uic) && IC_LEFT (uic)->key == IC_RESULT (ic)->key) + ReplaceOpWithCheaperOp(&IC_LEFT(uic), IC_RIGHT(ic)); + if (IC_RIGHT (uic) && IC_RIGHT (uic)->key == IC_RESULT (ic)->key) + ReplaceOpWithCheaperOp(&IC_RIGHT(uic), IC_RIGHT(ic)); + if (IC_RESULT (uic) && IC_RESULT (uic)->key == IC_RESULT (ic)->key) + ReplaceOpWithCheaperOp(&IC_RESULT(uic), IC_RIGHT(ic)); + } + } + +} +#endif + + +/*-----------------------------------------------------------------*/ +/* packRegsForAssign - register reduction for assignment */ +/*-----------------------------------------------------------------*/ +static int +packRegsForAssign (iCode * ic, eBBlock * ebp) +{ + iCode *dic, *sic; + + if (!IS_ITEMP (IC_RIGHT (ic)) || + OP_SYMBOL (IC_RIGHT (ic))->isind || + OP_LIVETO (IC_RIGHT (ic)) > ic->seq) + { + return 0; + } + + /* if the true symbol is defined in far space or on stack + then we should not since this will increase register pressure */ +#if 0 + if (isOperandInFarSpace(IC_RESULT(ic)) && !farSpacePackable(ic)) + { + return 0; + } +#endif + + /* find the definition of iTempNN scanning backwards if we find + a use of the true symbol before we find the definition then + we cannot */ + for (dic = ic->prev; dic; dic = dic->prev) + { + int crossedCall = 0; + + /* We can pack across a function call only if it's a local */ + /* variable or our parameter. Never pack global variables */ + /* or parameters to a function we call. */ + if ((dic->op == CALL || dic->op == PCALL)) + { + if (!OP_SYMBOL (IC_RESULT (ic))->ismyparm + && !OP_SYMBOL (IC_RESULT (ic))->islocal) + { + crossedCall = 1; + } + } + + /* Don't move an assignment out of a critical block */ + if (dic->op == CRITICAL) + { + dic = NULL; + break; + } + + if (SKIP_IC2 (dic)) + continue; + + if (dic->op == IFX) + { + if (IS_SYMOP (IC_COND (dic)) && + (IC_COND (dic)->key == IC_RESULT (ic)->key || + IC_COND (dic)->key == IC_RIGHT (ic)->key)) + { + dic = NULL; + break; + } + } + else + { + if (IS_TRUE_SYMOP (IC_RESULT (dic)) && + IS_OP_VOLATILE (IC_RESULT (dic))) + { + dic = NULL; + break; + } + + if (IS_SYMOP (IC_RESULT (dic)) && + IC_RESULT (dic)->key == IC_RIGHT (ic)->key) + { + if (POINTER_SET (dic)) + dic = NULL; + break; + } + + if (IS_SYMOP (IC_RIGHT (dic)) && + (IC_RIGHT (dic)->key == IC_RESULT (ic)->key || + IC_RIGHT (dic)->key == IC_RIGHT (ic)->key)) + { + dic = NULL; + break; + } + + if (IS_SYMOP (IC_LEFT (dic)) && + (IC_LEFT (dic)->key == IC_RESULT (ic)->key || + IC_LEFT (dic)->key == IC_RIGHT (ic)->key)) + { + dic = NULL; + break; + } + + if (POINTER_SET (dic) && + IC_RESULT (dic)->key == IC_RESULT (ic)->key) + { + dic = NULL; + break; + } + + if (crossedCall) + { + dic = NULL; + break; + } + } + } + + if (!dic) + return 0; /* did not find */ + + /* if assignment then check that right is not a bit */ + if (ASSIGNMENT (ic) && !POINTER_SET (ic)) + { + sym_link *etype = operandType (IC_RESULT (dic)); + if (IS_BITFIELD (etype)) + { + /* if result is a bit too then it's ok */ + etype = operandType (IC_RESULT (ic)); + if (!IS_BITFIELD (etype)) + { + return 0; + } + } + } + + /* found the definition */ + + /* delete from liverange table also + delete from all the points inbetween and the new + one */ + for (sic = dic; sic != ic; sic = sic->next) + { + bitVectUnSetBit (sic->rlive, IC_RESULT (ic)->key); + if (IS_ITEMP (IC_RESULT (dic))) + bitVectSetBit (sic->rlive, IC_RESULT (dic)->key); + } + + /* replace the result with the result of */ + /* this assignment and remove this assignment */ + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (dic))->defs, dic->key); + ReplaceOpWithCheaperOp (&IC_RESULT (dic), IC_RESULT (ic)); + + if (IS_ITEMP (IC_RESULT (dic)) && OP_SYMBOL (IC_RESULT (dic))->liveFrom > dic->seq) + { + OP_SYMBOL (IC_RESULT (dic))->liveFrom = dic->seq; + } + // TODO: and the otherway around? + + remiCodeFromeBBlock (ebp, ic); + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + OP_DEFS (IC_RESULT (dic)) = bitVectSetBit (OP_DEFS (IC_RESULT (dic)), dic->key); + return 1; +} + +/*------------------------------------------------------------------*/ +/* findAssignToSym : scanning backwards looks for first assig found */ +/*------------------------------------------------------------------*/ +static iCode * +findAssignToSym (operand * op, iCode * ic) +{ + iCode *dic; + + /* This routine is used to find sequences like + iTempAA = FOO; + ...; (intervening ops don't use iTempAA or modify FOO) + blah = blah + iTempAA; + + and eliminate the use of iTempAA, freeing up its register for + other uses. + */ + for (dic = ic->prev; dic; dic = dic->prev) + { + if (dic->op == '=' && + !POINTER_SET (dic) && + IC_RESULT (dic)->key == op->key + && IS_TRUE_SYMOP(IC_RIGHT(dic)) + ) + break; /* found where this temp was defined */ + + /* if we find an usage then we cannot delete it */ + if (IC_LEFT (dic) && IC_LEFT (dic)->key == op->key) + return NULL; + + if (IC_RIGHT (dic) && IC_RIGHT (dic)->key == op->key) + return NULL; + + if (POINTER_SET (dic) && IC_RESULT (dic)->key == op->key) + return NULL; + } + + if (!dic) + return NULL; /* didn't find any assignment to op */ + /* we are interested only if defined in far space */ + /* or in stack space in case of + & - */ + + /* if assigned to a non-symbol then don't repack regs */ + if (!IS_SYMOP (IC_RIGHT (dic))) + return NULL; + + /* if the symbol's address has been taken, there might be a */ + /* non-obvious assignment to it, and so we should not */ + if (OP_SYMBOL (IC_RIGHT (dic))->addrtaken) + return NULL; + + /* if the symbol is volatile then we should not */ + if (isOperandVolatile (IC_RIGHT (dic), TRUE)) + return NULL; + /* XXX TODO --- should we be passing FALSE to isOperandVolatile()? + What does it mean for an iTemp to be volatile, anyway? Passing + TRUE is more cautious but may prevent possible optimizations */ + + /* if the symbol is in far space then we should not */ + /* if (isOperandInFarSpace (IC_RIGHT (dic))) + return NULL; */ + + + /* now make sure that the right side of dic + is not defined between ic & dic */ + if (dic) + { + iCode *sic = dic->next; + + for (; sic != ic; sic = sic->next) + if (IC_RESULT (sic) && + IC_RESULT (sic)->key == IC_RIGHT (dic)->key) + return NULL; + } + + return dic; +} + +/*-----------------------------------------------------------------*/ +/* reassignAliasedSym - used by packRegsForSupport to replace */ +/* redundant iTemp with equivalent symbol */ +/*-----------------------------------------------------------------*/ +static void +reassignAliasedSym (eBBlock *ebp, iCode *assignment, iCode *use, operand *op) +{ + iCode *ic; + unsigned oldSymKey, newSymKey; + + oldSymKey = op->key; + newSymKey = IC_RIGHT(assignment)->key; + + /* only track live ranges of compiler-generated temporaries */ + if (!IS_ITEMP(IC_RIGHT(assignment))) + newSymKey = 0; + + /* update the live-value bitmaps */ + for (ic = assignment; ic != use; ic = ic->next) { + bitVectUnSetBit (ic->rlive, oldSymKey); + if (newSymKey != 0) + ic->rlive = bitVectSetBit (ic->rlive, newSymKey); + } + + /* update the sym of the used operand */ + OP_SYMBOL(op) = OP_SYMBOL(IC_RIGHT(assignment)); + op->key = OP_SYMBOL(op)->key; + + /* update the sym's liverange */ + if ( OP_LIVETO(op) < ic->seq ) + setToRange(op, ic->seq, FALSE); + + /* remove the assignment iCode now that its result is unused */ + remiCodeFromeBBlock (ebp, assignment); + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(assignment))->defs, assignment->key); + hTabDeleteItem (&iCodehTab, assignment->key, assignment, DELETE_ITEM, NULL); +} + + +/*-----------------------------------------------------------------*/ +/* packRegsForSupport :- reduce some registers for support calls */ +/*-----------------------------------------------------------------*/ +static int +packRegsForSupport (iCode * ic, eBBlock * ebp) +{ + iCode *dic; + int changes = 0; + + /* for the left & right operand :- look to see if the + left was assigned a true symbol in far space in that + case replace them */ + + if (IS_ITEMP (IC_LEFT (ic)) && + OP_SYMBOL (IC_LEFT (ic))->liveTo <= ic->seq) + { + dic = findAssignToSym (IC_LEFT (ic), ic); + + if (dic) + { + /* found it we need to remove it from the block */ + reassignAliasedSym (ebp, dic, ic, IC_LEFT(ic)); + changes++; + } + } + + /* do the same for the right operand */ + if (IS_ITEMP (IC_RIGHT (ic)) && + OP_SYMBOL (IC_RIGHT (ic))->liveTo <= ic->seq) + { + iCode *dic = findAssignToSym (IC_RIGHT (ic), ic); + + if (dic) + { + /* found it we need to remove it from the block */ + reassignAliasedSym (ebp, dic, ic, IC_RIGHT(ic)); + changes++; + } + } + + return changes; +} + + +#if 0 +/*-----------------------------------------------------------------*/ +/* packRegsForOneuse : - will reduce some registers for single Use */ +/*-----------------------------------------------------------------*/ +static iCode * +packRegsForOneuse (iCode * ic, operand * op, eBBlock * ebp) +{ + bitVect *uses; + iCode *dic, *sic; + + /* if returning a literal then do nothing */ + if (!IS_SYMOP (op)) + return NULL; + + /* only up to 2 bytes */ + if (getSize (operandType (op)) > (fReturnSizeHC08 - 2)) + return NULL; + + return NULL; + + if (ic->op != SEND && //RETURN + ic->op != SEND && + !POINTER_SET (ic) && + !POINTER_GET (ic)) + return NULL; + + if (ic->op == SEND && ic->argreg != 1) + return NULL; + + /* this routine will mark the symbol as used in one + instruction use only && if the definition is local + (ie. within the basic block) && has only one definition && + that definition is either a return value from a + function or does not contain any variables in + far space */ + uses = bitVectCopy (OP_USES (op)); + bitVectUnSetBit (uses, ic->key); /* take away this iCode */ + if (!bitVectIsZero (uses)) /* has other uses */ + return NULL; + + /* if it has only one definition */ + if (bitVectnBitsOn (OP_DEFS (op)) > 1) + return NULL; /* has more than one definition */ + + /* get that definition */ + if (!(dic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (op))))) + return NULL; + + /* if that only usage is a cast */ + if (dic->op == CAST) + { + /* to a bigger type */ + if (getSize(OP_SYM_TYPE(IC_RESULT(dic))) > getSize(OP_SYM_TYPE(IC_RIGHT(dic)))) + { + /* then we can not, since we cannot predict the usage of b & acc */ + return NULL; + } + } + + /* found the definition now check if it is local */ + if (dic->seq < ebp->fSeq || dic->seq > ebp->lSeq) + return NULL; /* non-local */ + + /* now check if it is the return from a function call */ + if (dic->op == CALL || dic->op == PCALL) + { + if (ic->op != SEND && ic->op != RETURN && + !POINTER_SET(ic) && !POINTER_GET(ic)) + { + OP_SYMBOL (op)->ruonly = 1; + return dic; + } + dic = dic->next; + } + + +// /* otherwise check that the definition does +// not contain any symbols in far space */ +// if (isOperandInFarSpace (IC_LEFT (dic)) || +// isOperandInFarSpace (IC_RIGHT (dic)) || +// IS_OP_RUONLY (IC_LEFT (ic)) || +// IS_OP_RUONLY (IC_RIGHT (ic))) +// { +// return NULL; +// } + + /* if pointer set then make sure the pointer + is one byte */ +#if 0 + if (POINTER_SET (dic) && + !IS_DATA_PTR (aggrToPtr (operandType (IC_RESULT (dic)), FALSE))) + return NULL; + + if (POINTER_GET (dic) && + !IS_DATA_PTR (aggrToPtr (operandType (IC_LEFT (dic)), FALSE))) + return NULL; +#endif + + sic = dic; + + /* make sure the intervening instructions + don't have anything in far space */ + for (dic = dic->next; dic && dic != ic && sic != ic; dic = dic->next) + { + /* if there is an intervening function call then no */ + if (dic->op == CALL || dic->op == PCALL) + return NULL; + /* if pointer set then make sure the pointer + is one byte */ +#if 0 + if (POINTER_SET (dic) && + !IS_DATA_PTR (aggrToPtr (operandType (IC_RESULT (dic)), FALSE))) + return NULL; + + if (POINTER_GET (dic) && + !IS_DATA_PTR (aggrToPtr (operandType (IC_LEFT (dic)), FALSE))) + return NULL; +#endif + /* if address of & the result is remat then okay */ + if (dic->op == ADDRESS_OF && + OP_SYMBOL (IC_RESULT (dic))->remat) + continue; + + /* if operand has size of three or more & this + operation is a '*','/' or '%' then 'b' may + cause a problem */ +#if 0 + if ((dic->op == '%' || dic->op == '/' || dic->op == '*') && + getSize (operandType (op)) >= 3) + return NULL; +#endif + + /* if left or right or result is in far space */ +// if (isOperandInFarSpace (IC_LEFT (dic)) || +// isOperandInFarSpace (IC_RIGHT (dic)) || +// isOperandInFarSpace (IC_RESULT (dic)) || +// IS_OP_RUONLY (IC_LEFT (dic)) || +// IS_OP_RUONLY (IC_RIGHT (dic)) || +// IS_OP_RUONLY (IC_RESULT (dic))) +// { +// return NULL; +// } +// /* if left or right or result is on stack */ +// if (isOperandOnStack(IC_LEFT(dic)) || +// isOperandOnStack(IC_RIGHT(dic)) || +// isOperandOnStack(IC_RESULT(dic))) { +// return NULL; +// } + } + + OP_SYMBOL (op)->ruonly = 1; + return sic; +} +#endif + +/*-----------------------------------------------------------------*/ +/* isBitwiseOptimizable - requirements of JEAN LOUIS VERN */ +/*-----------------------------------------------------------------*/ +static bool +isBitwiseOptimizable (iCode * ic) +{ + sym_link *ltype = getSpec (operandType (IC_LEFT (ic))); + sym_link *rtype = getSpec (operandType (IC_RIGHT (ic))); + + /* bitwise operations are considered optimizable + under the following conditions (Jean-Louis VERN) + + x & lit + bit & bit + bit & x + bit ^ bit + bit ^ x + x ^ lit + x | lit + bit | bit + bit | x + */ + if (IS_LITERAL(rtype) || + (IS_BITVAR (ltype) && IN_BITSPACE (SPEC_OCLS (ltype)))) + return TRUE; + else + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* isCommutativeOp - tests whether this op cares what order its */ +/* operands are in */ +/*-----------------------------------------------------------------*/ +bool isCommutativeOp2(unsigned int op) +{ + if (op == '+' || op == '*' || op == EQ_OP || + op == '^' || op == '|' || op == BITWISEAND) + return TRUE; + else + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* operandUsesAcc2 - determines whether the code generated for this */ +/* operand will have to use the accumulator */ +/*-----------------------------------------------------------------*/ +bool operandUsesAcc2(operand *op) +{ + if (!op) + return FALSE; + + if (IS_SYMOP(op)) { + symbol *sym = OP_SYMBOL(op); + memmap *symspace; + + if (sym->accuse) + return TRUE; /* duh! */ + + if (IS_ITEMP(op)) + { + if (SPIL_LOC(op)) { + sym = SPIL_LOC(op); /* if spilled, look at spill location */ + } else { + return FALSE; /* more checks? */ + } + } + + symspace = SPEC_OCLS(sym->etype); + + if (IN_BITSPACE(symspace)) + return TRUE; /* fetching bit vars uses the accumulator */ + + if (IN_FARSPACE(symspace) || IN_CODESPACE(symspace)) + return TRUE; /* fetched via accumulator and dptr */ + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* canDefAccResult - return 1 if the iCode can generate a result */ +/* in A or XA */ +/*-----------------------------------------------------------------*/ +static int +canDefAccResult (iCode * ic) +{ + int size; + + if (ic->op == IFX || ic->op == JUMPTABLE) /* these iCodes have no result */ + return 0; + + if (POINTER_SET (ic)) + return 0; + + if (!IC_RESULT (ic)) + return 0; + + if (!IS_ITEMP (IC_RESULT (ic))) + return 0; + + /* I don't think an iTemp can be an aggregate, but just in case */ + if (IS_AGGREGATE(operandType(IC_RESULT(ic)))) + return 0; + + size = getSize (operandType (IC_RESULT (ic))); + + if (size == 1) + { + /* All 1 byte operations should safely generate an accumulator result */ + return 1; + } + else if (size == 2) + { + switch (ic->op) + { + case LEFT_OP: + case RIGHT_OP: + return isOperandLiteral (IC_RIGHT (ic)) + && SPEC_USIGN (operandType (IC_RESULT (ic))); + case CALL: + case PCALL: + case '*': + case RECEIVE: + case '=': /* assignment, since POINTER_SET is already ruled out */ + return 1; + + default: + return 0; + } + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* canUseAccOperand - return 1 if the iCode can use the operand */ +/* when passed in A or XA */ +/*-----------------------------------------------------------------*/ +static int +canUseAccOperand (iCode * ic, operand * op) +{ + int size; + + if (ic->op == IFX) + { + if (isOperandEqual (op, IC_COND (ic))) + return 1; + else + return 0; + } + + if (ic->op == JUMPTABLE) + { + if (isOperandEqual (op, IC_JTCOND (ic))) + return 1; + else + return 0; + } + + if (POINTER_SET (ic) && isOperandEqual (op, IC_RESULT (ic))) + return 1; + + if (!isOperandEqual (op, IC_LEFT (ic)) && !isOperandEqual (op, IC_RIGHT (ic))) + return 0; + + /* Generation of SEND is deferred until CALL; not safe */ + /* if there are intermediate iCodes */ + if (ic->op == SEND && ic->next && ic->next->op != CALL) + return 0; + + size = getSize (operandType (op)); + if (size == 1) + { + /* All 1 byte operations should safely use an accumulator operand */ + return 1; + } + else if (size == 2) + { + switch (ic->op) + { + case LEFT_OP: + case RIGHT_OP: + return isOperandLiteral (IC_RIGHT (ic)); + case SEND: + return 1; + default: + return 0; + } + } + + return 0; +} + + +/*-----------------------------------------------------------------*/ +/* packRegsForAccUse - pack registers for acc use */ +/*-----------------------------------------------------------------*/ +static int +packRegsForAccUse (iCode * ic) +{ + iCode * uic; + operand * op; + + if (!canDefAccResult (ic)) + return 0; + + op = IC_RESULT (ic); + + /* has only one definition */ + if (bitVectnBitsOn (OP_DEFS (op)) > 1) + return 0; + + /* has only one use */ + if (bitVectnBitsOn (OP_USES (op)) > 1) + return 0; + + uic = ic->next; + if (!uic) + return 0; + + if (!canUseAccOperand (uic, op)) + return 0; + + #if 0 + if ((POINTER_GET(uic)) + || (ic->op == ADDRESS_OF && uic->op == '+' && IS_OP_LITERAL (IC_RIGHT (uic)))) + { + OP_SYMBOL (IC_RESULT (ic))->accuse = ACCUSE_HX; + return; + } + #endif + + OP_SYMBOL (IC_RESULT (ic))->accuse = ACCUSE_XA; + return 1; +} + +/*-----------------------------------------------------------------*/ +/* packForPush - heuristics to reduce iCode for pushing */ +/*-----------------------------------------------------------------*/ +static void +packForPush (iCode * ic, eBBlock ** ebpp, int blockno) +{ + iCode *dic, *lic; + bitVect *dbv; + struct eBBlock * ebp=ebpp[blockno]; + int disallowHiddenAssignment = 0; + + if ((ic->op != IPUSH && ic->op != SEND) || !IS_ITEMP (IC_LEFT (ic))) + return; + + /* must have only definition & one usage */ + if (bitVectnBitsOn (OP_DEFS (IC_LEFT (ic))) != 1 || + bitVectnBitsOn (OP_USES (IC_LEFT (ic))) != 1) + return; + + /* find the definition */ + if (!(dic = hTabItemWithKey (iCodehTab, + bitVectFirstBit (OP_DEFS (IC_LEFT (ic)))))) + return; + + if (dic->op != '=' || POINTER_SET (dic)) + return; + + if (dic->seq < ebp->fSeq) // Evelyn did this + { + int i; + for (i=0; i<blockno; i++) + { + if (dic->seq >= ebpp[i]->fSeq && dic->seq <= ebpp[i]->lSeq) + { + ebp=ebpp[i]; + break; + } + } + wassert (i!=blockno); // no way to recover from here + } + + if (IS_SYMOP(IC_RIGHT(dic))) + { + if (IC_RIGHT (dic)->isvolatile) + return; + + if (OP_SYMBOL (IC_RIGHT (dic))->addrtaken || isOperandGlobal (IC_RIGHT (dic))) + disallowHiddenAssignment = 1; + + /* make sure the right side does not have any definitions + inbetween */ + dbv = OP_DEFS(IC_RIGHT(dic)); + for (lic = ic; lic && lic != dic ; lic = lic->prev) + { + if (bitVectBitValue(dbv,lic->key)) + return ; + if (disallowHiddenAssignment && (lic->op == CALL || lic->op == PCALL || POINTER_SET (lic))) + return; + } + /* make sure they have the same type */ + if (IS_SPEC(operandType(IC_LEFT(ic)))) + { + sym_link *itype=operandType(IC_LEFT(ic)); + sym_link *ditype=operandType(IC_RIGHT(dic)); + + if (SPEC_USIGN(itype)!=SPEC_USIGN(ditype) || + SPEC_LONG(itype)!=SPEC_LONG(ditype)) + return; + } + /* extend the live range of replaced operand if needed */ + if (OP_SYMBOL(IC_RIGHT(dic))->liveTo < ic->seq) + { + OP_SYMBOL(IC_RIGHT(dic))->liveTo = ic->seq; + } + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(dic))->defs,dic->key); + } + if (IS_ITEMP (IC_RIGHT (dic))) + OP_USES (IC_RIGHT (dic)) = bitVectSetBit (OP_USES (IC_RIGHT (dic)), ic->key); + + /* we now we know that it has one & only one def & use + and the that the definition is an assignment */ + ReplaceOpWithCheaperOp(&IC_LEFT (ic), IC_RIGHT (dic)); + remiCodeFromeBBlock (ebp, dic); + hTabDeleteItem (&iCodehTab, dic->key, dic, DELETE_ITEM, NULL); +} + +/*------------------------------------------------------------------*/ +/* moveSendToCall - move SEND to immediately precede its CALL/PCALL */ +/*------------------------------------------------------------------*/ +static iCode * +moveSendToCall (iCode *sic, eBBlock *ebp) +{ + iCode * prev = sic->prev; + iCode * sic2 = NULL; + iCode * cic; + + /* Go find the CALL/PCALL */ + cic = sic; + while (cic && cic->op != CALL && cic->op != PCALL) + cic = cic->next; + if (!cic) + return sic; + + /* Is there a second SEND? If so, we'll need to move it too. */ + if (sic->next->op == SEND) + sic2 = sic->next; + + /* relocate the SEND(s) */ + remiCodeFromeBBlock (ebp, sic); + addiCodeToeBBlock (ebp, sic, cic); + if (sic2) + { + remiCodeFromeBBlock (ebp, sic2); + addiCodeToeBBlock (ebp, sic2, cic); + } + + /* Return the iCode to continue processing at. */ + if (prev) + return prev->next; + else + return ebp->sch; +} + + +/*---------------------------------------------------------------------*/ +/* packPointerOp - see if we can move an offset from addition iCode */ +/* to the pointer iCode to used indexed addr mode */ +/* The z80-related ports do a similar thing in SDCCopt.c, offsetFold() */ +/*---------------------------------------------------------------------*/ +static void +packPointerOp (iCode * ic, eBBlock ** ebpp) +{ + operand * pointer; + operand * offsetOp; + operand * nonOffsetOp; + iCode * dic; + iCode * uic; + int key; + + if (POINTER_SET (ic)) + { + pointer = IC_RESULT (ic); + offsetOp = IC_LEFT (ic); + } + else if (POINTER_GET (ic)) + { + pointer = IC_LEFT (ic); + offsetOp = IC_RIGHT (ic); + } + else + return; + + if (!IS_ITEMP (pointer)) + return; + + /* If the pointer is rematerializable, it's already fully optimized */ + if (OP_SYMBOL (pointer)->remat) + return; + + if (offsetOp && IS_OP_LITERAL (offsetOp) && operandLitValue (offsetOp) != 0) + return; + if (offsetOp && IS_SYMOP (offsetOp)) + return; + + /* There must be only one definition */ + if (bitVectnBitsOn (OP_DEFS (pointer)) != 1) + return; + /* find the definition */ + if (!(dic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (pointer))))) + return; + + if (dic->op == '+' && (IS_OP_LITERAL (IC_RIGHT (dic)) || + (IS_ITEMP (IC_RIGHT (dic)) && OP_SYMBOL (IC_RIGHT (dic))->remat))) + { + nonOffsetOp = IC_LEFT (dic); + offsetOp = IC_RIGHT (dic); + } + else if (dic->op == '+' && IS_ITEMP (IC_LEFT (dic)) && OP_SYMBOL (IC_LEFT (dic))->remat) + { + nonOffsetOp = IC_RIGHT (dic); + offsetOp = IC_LEFT (dic); + } + else + return; + + + /* Now check all of the uses to make sure they are all get/set pointer */ + /* and don't already have a non-zero offset operand */ + for (key=0; key<OP_USES (pointer)->size; key++) + { + if (bitVectBitValue (OP_USES (pointer), key)) + { + uic = hTabItemWithKey (iCodehTab, key); + if (POINTER_GET (uic)) + { + if (IC_RIGHT (uic) && IS_OP_LITERAL (IC_RIGHT (uic)) && operandLitValue (IC_RIGHT (uic)) != 0) + return; + if (IC_RIGHT (uic) && IS_SYMOP (IC_RIGHT (uic))) + return; + } + else if (POINTER_SET (uic)) + { + if (IC_LEFT (uic) && IS_OP_LITERAL (IC_LEFT (uic)) && operandLitValue (IC_LEFT (uic)) != 0) + return; + if (IC_LEFT (uic) && IS_SYMOP (IC_LEFT (uic))) + return; + } + else + return; + } + } + + /* Everything checks out. Move the literal or rematerializable offset */ + /* to the pointer get/set iCodes */ + for (key=0; key<OP_USES (pointer)->size; key++) + { + if (bitVectBitValue (OP_USES (pointer), key)) + { + uic = hTabItemWithKey (iCodehTab, key); + if (POINTER_GET (uic)) + { + IC_RIGHT (uic) = offsetOp; + if (IS_SYMOP (offsetOp)) + OP_USES (offsetOp) = bitVectSetBit (OP_USES (offsetOp), key); + } + else if (POINTER_SET (uic)) + { + IC_LEFT (uic) = offsetOp; + if (IS_SYMOP (offsetOp)) + OP_USES (offsetOp) = bitVectSetBit (OP_USES (offsetOp), key); + } + else + return; + } + } + + /* Put the remaining operand on the right and convert to assignment */ + if (IS_SYMOP (offsetOp)) + bitVectUnSetBit (OP_USES (offsetOp), dic->key); + IC_RIGHT (dic) = nonOffsetOp; + IC_LEFT (dic) = NULL; + SET_ISADDR (IC_RESULT (dic), 0); + dic->op = '='; +} + +/*-----------------------------------------------------------------*/ +/* packRegisters - does some transformations to reduce register */ +/* pressure */ +/*-----------------------------------------------------------------*/ +static void +packRegisters (eBBlock ** ebpp, int blockno) +{ + iCode *ic; + int change = 0; + eBBlock *ebp = ebpp[blockno]; + + do + { + change = 0; + + /* look for assignments of the form */ + /* iTempNN = TrueSym (someoperation) SomeOperand */ + /* .... */ + /* TrueSym := iTempNN:1 */ + for (ic = ebp->sch; ic; ic = ic->next) + { + /* find assignment of the form TrueSym := iTempNN:1 */ + if (ic->op == '=' && !POINTER_SET (ic)) + change += packRegsForAssign (ic, ebp); + } + } + while (change); + + for (ic = ebp->sch; ic; ic = ic->next) + { + //packRegsForLiteral (ic); + + /* move SEND to immediately precede its CALL/PCALL */ + if (ic->op == SEND && ic->next && + ic->next->op != CALL && ic->next->op != PCALL) + { + ic = moveSendToCall (ic, ebp); + } + + /* if this is an itemp & result of an address of a true sym + then mark this as rematerialisable */ + if (ic->op == ADDRESS_OF && + IS_ITEMP (IC_RESULT (ic)) && + IS_TRUE_SYMOP (IC_LEFT (ic)) && + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && + !OP_SYMBOL (IC_LEFT (ic))->onStack) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + + /* if straight assignment then carry remat flag if + this is the only definition */ + if (ic->op == '=' && + !POINTER_SET (ic) && + IS_SYMOP (IC_RIGHT (ic)) && + OP_SYMBOL (IC_RIGHT (ic))->remat && + bitVectnBitsOn (OP_SYMBOL (IC_RESULT (ic))->defs) <= 1 && + !OP_SYMBOL (IC_RESULT (ic))->_isparm && + !OP_SYMBOL (IC_RESULT (ic))->addrtaken && + !isOperandGlobal (IC_RESULT (ic))) + { + OP_SYMBOL (IC_RESULT (ic))->remat = OP_SYMBOL (IC_RIGHT (ic))->remat; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + } + + /* if cast to a generic pointer & the pointer being + cast is remat, then we can remat this cast as well */ + if (ic->op == CAST && + IS_SYMOP(IC_RIGHT(ic)) && + OP_SYMBOL(IC_RIGHT(ic))->remat && + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && + !OP_SYMBOL (IC_RESULT (ic))->_isparm && + !OP_SYMBOL (IC_RESULT (ic))->addrtaken && + !isOperandGlobal (IC_RESULT (ic))) + { + sym_link *to_type = operandType(IC_LEFT(ic)); + sym_link *from_type = operandType(IC_RIGHT(ic)); + if (IS_PTR(to_type) && IS_PTR(from_type)) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + } + + /* if this is a +/- operation with a rematerizable + then mark this as rematerializable as well */ + if ((ic->op == '+' || ic->op == '-') && + (IS_SYMOP (IC_LEFT (ic)) && + IS_ITEMP (IC_RESULT (ic)) && + IS_OP_LITERAL (IC_RIGHT (ic))) && + OP_SYMBOL (IC_LEFT (ic))->remat && + (!IS_SYMOP (IC_RIGHT (ic)) || !IS_CAST_ICODE(OP_SYMBOL (IC_RIGHT (ic))->rematiCode)) && + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + /* mark the pointer usages */ + if (POINTER_SET (ic) && IS_SYMOP (IC_RESULT (ic))) + OP_SYMBOL (IC_RESULT (ic))->uptr = 1; + + if (POINTER_GET (ic) && IS_SYMOP (IC_LEFT (ic))) + OP_SYMBOL (IC_LEFT (ic))->uptr = 1; + + /* reduce for support function calls */ + if (ic->supportRtn || (ic->op != IFX && ic->op != JUMPTABLE)) + packRegsForSupport (ic, ebp); + + /* if the condition of an if instruction + is defined in the previous instruction and + this is the only usage then + mark the itemp as a conditional */ + if ((IS_CONDITIONAL (ic) || + (IS_BITWISE_OP(ic) && isBitwiseOptimizable (ic))) && + ic->next && ic->next->op == IFX && + bitVectnBitsOn (OP_USES(IC_RESULT(ic)))==1 && + isOperandEqual (IC_RESULT (ic), IC_COND (ic->next)) && + OP_SYMBOL (IC_RESULT (ic))->liveTo <= ic->next->seq) + { + OP_SYMBOL (IC_RESULT (ic))->regType = REG_CND; + continue; + } + + /* pack for PUSH + iTempNN := (some variable in farspace) V1 + push iTempNN ; + ------------- + push V1 + */ + if (ic->op == IPUSH || ic->op == SEND) + { + packForPush (ic, ebpp, blockno); + } + + if (POINTER_SET (ic) || POINTER_GET (ic)) + packPointerOp (ic, ebpp); + + if (options.oldralloc) + packRegsForAccUse (ic); + } +} + +static void +RegFix (eBBlock ** ebbs, int count) +{ + int i; + + /* Check for and fix any problems with uninitialized operands */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && (ebbs[i]->entryLabel != entryLabel && ebbs[i]->entryLabel != returnLabel)) + continue; + + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + deassignLRs (ic, ebbs[i]); + + if (SKIP_IC2 (ic)) + continue; + + if (ic->op == IFX) + { + verifyRegsAssigned (IC_COND (ic), ic); + continue; + } + + if (ic->op == JUMPTABLE) + { + verifyRegsAssigned (IC_JTCOND (ic), ic); + continue; + } + + verifyRegsAssigned (IC_RESULT (ic), ic); + verifyRegsAssigned (IC_LEFT (ic), ic); + verifyRegsAssigned (IC_RIGHT (ic), ic); + } + } +} + +static void +replaceAccuseOperand (operand * op) +{ + symbol * sym; + + if (!IS_ITEMP (op)) + return; + + sym = OP_SYMBOL (op); + if (sym->remat || !sym->accuse) + return; + + sym->nRegs = getSize (operandType (op)); + wassert ((sym->accuse == ACCUSE_XA) || (sym->accuse == ACCUSE_HX)); + wassert (sym->nRegs <= 2); + + if (sym->accuse == ACCUSE_XA) + { + sym->regs[0] = hc08_reg_a; + if (sym->nRegs > 1) + sym->regs[1] = hc08_reg_x; + } + else /* must be sym->accuse == ACCUSE_HX */ + { + sym->regs[0] = hc08_reg_x; + if (sym->nRegs > 1) + sym->regs[1] = hc08_reg_h; + } + sym->accuse = 0; +} + + +/* Replace all uses of the sym->accuse flag with initialized */ +/* sym->regs[] and sym->nRegs so the current code generator */ +/* can work with the old register allocator. */ +static void +replaceAccuse (eBBlock ** ebbs, int count) +{ + int i; + + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && (ebbs[i]->entryLabel != entryLabel && ebbs[i]->entryLabel != returnLabel)) + continue; + + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (SKIP_IC2 (ic)) + continue; + + if (ic->op == IFX) + { + replaceAccuseOperand (IC_COND (ic)); + continue; + } + + if (ic->op == JUMPTABLE) + { + replaceAccuseOperand (IC_JTCOND (ic)); + continue; + } + + replaceAccuseOperand (IC_RESULT (ic)); + replaceAccuseOperand (IC_LEFT (ic)); + replaceAccuseOperand (IC_RIGHT (ic)); + } + } +} + +#ifdef OLDRALLOC +/*-----------------------------------------------------------------*/ +/* Old, obsolete register allocator */ +/*-----------------------------------------------------------------*/ +void +hc08_oldralloc (ebbIndex * ebbi) +{ + eBBlock ** ebbs = ebbi->bbOrder; + int count = ebbi->count; + iCode *ic; + int i; + + setToNull ((void *) &_G.funcrUsed); + setToNull ((void *) &_G.regAssigned); + setToNull ((void *) &_G.totRegAssigned); + hc08_ptrRegReq = _G.stackExtend = _G.dataExtend = 0; + hc08_nRegs = 7; + hc08_reg_a = hc08_regWithIdx(A_IDX); + hc08_reg_x = hc08_regWithIdx(X_IDX); + hc08_reg_h = hc08_regWithIdx(H_IDX); + hc08_reg_hx = hc08_regWithIdx(HX_IDX); + hc08_reg_xa = hc08_regWithIdx(XA_IDX); + hc08_reg_sp = hc08_regWithIdx(SP_IDX); + hc08_nRegs = 5; + + /* change assignments this will remove some + live ranges reducing some register pressure */ + + for (i = 0; i < count; i++) + packRegisters (ebbs, i); + + /* liveranges probably changed by register packing + so we compute them again */ + recomputeLiveRanges (ebbs, count, FALSE); + + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_PACK, ebbi); + + /* first determine for each live range the number of + registers & the type of registers required for each */ + regTypeNum (*ebbs); + + /* and serially allocate registers */ + serialRegAssign (ebbs, count); + + freeAllRegs (); + //setToNull ((void *) &_G.regAssigned); + //setToNull ((void *) &_G.totRegAssigned); +#if 0 + fillGaps(); +#endif + + /* if stack was extended then tell the user */ + if (_G.stackExtend) + { +/* werror(W_TOOMANY_SPILS,"stack", */ +/* _G.stackExtend,currFunc->name,""); */ + _G.stackExtend = 0; + } + + if (_G.dataExtend) + { +/* werror(W_TOOMANY_SPILS,"data space", */ +/* _G.dataExtend,currFunc->name,""); */ + _G.dataExtend = 0; + } + + /* after that create the register mask + for each of the instruction */ + createRegMask (ebbs, count); + + /* Convert the old sym->accuse flag into normal register assignments */ + replaceAccuse (ebbs, count); + + /* redo that offsets for stacked automatic variables */ + if (currFunc) + { + redoStackOffsets (); + } + + if (options.dump_i_code) + { + dumpEbbsToFileExt (DUMP_RASSGN, ebbi); + dumpLiveRanges (DUMP_LRANGE, liveRanges); + } + + /* do the overlaysegment stuff SDCCmem.c */ + doOverlays (ebbs, count); + + /* now get back the chain */ + ic = iCodeLabelOptimize (iCodeFromeBBlock (ebbs, count)); + + genhc08Code (ic); + + /* free up any _G.stackSpil locations allocated */ + applyToSet (_G.stackSpil, deallocStackSpil); + _G.slocNum = 0; + setToNull ((void *) &_G.stackSpil); + setToNull ((void *) &_G.spiltSet); + /* mark all registers as free */ + freeAllRegs (); + + return; +} +#endif + +/** Serially allocate registers to the variables. + This was the main register allocation function. It is called after + packing. + In the new register allocator it only serves to mark variables for the new register allocator. + */ +static void +serialRegMark (eBBlock ** ebbs, int count) +{ + int i; + short int max_alloc_bytes = SHRT_MAX; // Byte limit. Set this to a low value to pass only few variables to the register allocator. This can be useful for debugging. + + /* for all blocks */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && + (ebbs[i]->entryLabel != entryLabel && + ebbs[i]->entryLabel != returnLabel)) + continue; + + /* for all instructions do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + updateRegUsage(ic); + + /* if this is an ipop that means some live + range will have to be assigned again */ + if (ic->op == IPOP) + reassignLR (IC_LEFT (ic)); + + /* if result is present && is a true symbol */ + if (IC_RESULT (ic) && ic->op != IFX && + IS_TRUE_SYMOP (IC_RESULT (ic))) + { + OP_SYMBOL (IC_RESULT (ic))->allocreq++; + } + + /* take away registers from live + ranges that end at this instruction */ + deassignLRs (ic, ebbs[i]); + + /* some don't need registers */ + if (SKIP_IC2 (ic) || + ic->op == JUMPTABLE || + ic->op == IFX || + ic->op == IPUSH || + ic->op == IPOP || + (IC_RESULT (ic) && POINTER_SET (ic))) + { + continue; + } + + /* now we need to allocate registers only for the result */ + if (IC_RESULT (ic)) + { + symbol *sym = OP_SYMBOL (IC_RESULT (ic)); + + /* Make sure any spill location is definitely allocated */ + if (sym->isspilt && !sym->remat && sym->usl.spillLoc && + !sym->usl.spillLoc->allocreq) + { + sym->usl.spillLoc->allocreq++; + } + + /* if it does not need or is spilt + or is already assigned to registers + or will not live beyond this instructions */ + if (!sym->nRegs || + sym->isspilt || + bitVectBitValue (_G.regAssigned, sym->key) || + sym->liveTo <= ic->seq) + { + continue; + } + + /* if some liverange has been spilt at the block level + and this one live beyond this block then spil this + to be safe */ + if (_G.blockSpil && sym->liveTo > ebbs[i]->lSeq) + { + spillThis (sym); + continue; + } + + if (sym->remat) + { + spillThis (sym); + continue; + } + + if (max_alloc_bytes >= sym->nRegs) + { + sym->for_newralloc = 1; + max_alloc_bytes -= sym->nRegs; + } + else if (!sym->for_newralloc) + { + spillThis (sym); + printf ("Spilt %s due to byte limit.\n", sym->name); + } + } + } + } +} +/*-----------------------------------------------------------------*/ +/* New register allocator */ +/*-----------------------------------------------------------------*/ +void +hc08_ralloc (ebbIndex * ebbi) +{ + eBBlock ** ebbs = ebbi->bbOrder; + int count = ebbi->count; + iCode *ic; + int i; + + setToNull ((void *) &_G.funcrUsed); + setToNull ((void *) &_G.regAssigned); + setToNull ((void *) &_G.totRegAssigned); + hc08_ptrRegReq = _G.stackExtend = _G.dataExtend = 0; + hc08_nRegs = 7; + hc08_reg_a = hc08_regWithIdx(A_IDX); + hc08_reg_x = hc08_regWithIdx(X_IDX); + hc08_reg_h = hc08_regWithIdx(H_IDX); + hc08_reg_hx = hc08_regWithIdx(HX_IDX); + hc08_reg_xa = hc08_regWithIdx(XA_IDX); + hc08_reg_sp = hc08_regWithIdx(SP_IDX); + hc08_nRegs = 5; + + /* change assignments this will remove some + live ranges reducing some register pressure */ + + for (i = 0; i < count; i++) + packRegisters (ebbs, i); + + /* liveranges probably changed by register packing + so we compute them again */ + recomputeLiveRanges (ebbs, count, FALSE); + + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_PACK, ebbi); + + /* first determine for each live range the number of + registers & the type of registers required for each */ + regTypeNum (*ebbs); + + /* and serially allocate registers */ + serialRegMark (ebbs, count); + + /* The new register allocator invokes its magic */ + ic = hc08_ralloc2_cc (ebbi); + + RegFix (ebbs, count); + + /* if stack was extended then tell the user */ + if (_G.stackExtend) + { +/* werror(W_TOOMANY_SPILS,"stack", */ +/* _G.stackExtend,currFunc->name,""); */ + _G.stackExtend = 0; + } + + if (_G.dataExtend) + { +/* werror(W_TOOMANY_SPILS,"data space", */ +/* _G.dataExtend,currFunc->name,""); */ + _G.dataExtend = 0; + } + + /* redo that offsets for stacked automatic variables */ + if (currFunc) + { + redoStackOffsets (); + } + + if (options.dump_i_code) + { + dumpEbbsToFileExt (DUMP_RASSGN, ebbi); + dumpLiveRanges (DUMP_LRANGE, liveRanges); + } + + /* do the overlaysegment stuff SDCCmem.c */ + doOverlays (ebbs, count); + + /* now get back the chain */ + ic = iCodeLabelOptimize (iCodeFromeBBlock (ebbs, count)); + + genhc08Code (ic); + + /* free up any _G.stackSpil locations allocated */ + applyToSet (_G.stackSpil, deallocStackSpil); + _G.slocNum = 0; + setToNull ((void *) &_G.stackSpil); + setToNull ((void *) &_G.spiltSet); + /* mark all registers as free */ + freeAllRegs (); + + return; +} + +/*-----------------------------------------------------------------*/ +/* assignRegisters - assigns registers to each live range as need */ +/*-----------------------------------------------------------------*/ +void +hc08_assignRegisters (ebbIndex * ebbi) +{ +#ifdef OLDRALLOC + if (options.oldralloc) + hc08_oldralloc (ebbi); + else +#endif + hc08_ralloc (ebbi); +} + |
