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/mcs51 | |
| 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/mcs51')
| -rw-r--r-- | src/mcs51/Makefile | 7 | ||||
| -rw-r--r-- | src/mcs51/Makefile.in | 7 | ||||
| -rw-r--r-- | src/mcs51/gen.c | 12371 | ||||
| -rw-r--r-- | src/mcs51/gen.h | 88 | ||||
| -rw-r--r-- | src/mcs51/main.c | 934 | ||||
| -rw-r--r-- | src/mcs51/main.h | 45 | ||||
| -rw-r--r-- | src/mcs51/mcs51.vcxproj | 165 | ||||
| -rw-r--r-- | src/mcs51/mcs51.vcxproj.filters | 55 | ||||
| -rw-r--r-- | src/mcs51/peep.c | 773 | ||||
| -rw-r--r-- | src/mcs51/peep.h | 25 | ||||
| -rw-r--r-- | src/mcs51/peeph.def | 5092 | ||||
| -rw-r--r-- | src/mcs51/ralloc.c | 3466 | ||||
| -rw-r--r-- | src/mcs51/ralloc.h | 81 | ||||
| -rw-r--r-- | src/mcs51/rtrack.c | 1200 | ||||
| -rw-r--r-- | src/mcs51/rtrack.h | 28 |
15 files changed, 24337 insertions, 0 deletions
diff --git a/src/mcs51/Makefile b/src/mcs51/Makefile new file mode 100644 index 0000000..cb704c7 --- /dev/null +++ b/src/mcs51/Makefile @@ -0,0 +1,7 @@ + +srcdir = . +top_builddir = ../.. +top_srcdir = ../.. + +# Make all in this directory +include $(srcdir)/../port.mk diff --git a/src/mcs51/Makefile.in b/src/mcs51/Makefile.in new file mode 100644 index 0000000..dfb8a52 --- /dev/null +++ b/src/mcs51/Makefile.in @@ -0,0 +1,7 @@ +VPATH = @srcdir@ +srcdir = @srcdir@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# Make all in this directory +include $(srcdir)/../port.mk diff --git a/src/mcs51/gen.c b/src/mcs51/gen.c new file mode 100644 index 0000000..b3ebf12 --- /dev/null +++ b/src/mcs51/gen.c @@ -0,0 +1,12371 @@ +/*------------------------------------------------------------------------- + gen.c - source file for code generation for 8051 + + Copyright (C) 1998, Sandeep Dutta . sandeep.dutta@usa.net + Copyright (C) 1999, Jean-Louis VERN.jlvern@writeme.com + Bug Fixes - Wojciech Stryjewski wstryj1@tiger.lsu.edu (1999 v2.1.9a) + + 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. +-------------------------------------------------------------------*/ +/* + Notes: + 000123 mlh Moved aopLiteral to SDCCglue.c to help the split + Made everything static +*/ + +#define D(x) do if (options.verboseAsm) {x;} while(0) + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include "common.h" +#include "ralloc.h" +#include "rtrack.h" +#include "gen.h" +#include "dbuf_string.h" + +char *aopLiteralGptr (const char *name, value * val); +extern int allocInfo; + +/* this is the down and dirty file with all kinds of + kludgy & hacky stuff. This is what it is all about + CODE GENERATION for a specific MCU . some of the + routines may be reusable, will have to see */ + +static char *zero = "#0x00"; +static char *one = "#0x01"; +static char *spname; + +char *fReturn8051[] = { "dpl", "dph", "b", "a", "r4", "r5", "r6", "r7" }; + +unsigned fReturnSizeMCS51 = 4; /* shared with ralloc.c */ +char **fReturn = fReturn8051; +static char *accUse[] = { "a", "b" }; + +static short rbank = -1; + +#define REG_WITH_INDEX mcs51_regWithIdx + +#define AOP(op) op->aop +#define AOP_TYPE(op) AOP(op)->type +#define AOP_SIZE(op) AOP(op)->size +#define IS_AOP_PREG(x) (AOP(x) && (AOP_TYPE(x) == AOP_R1 || \ + AOP_TYPE(x) == AOP_R0)) + +#define AOP_NEEDSACC(x) (AOP(x) && (AOP_TYPE(x) == AOP_CRY || \ + AOP_TYPE(x) == AOP_DPTR || \ + AOP(x)->paged)) + +#define AOP_INPREG(x) (x && (x->type == AOP_REG && \ + (x->aopu.aop_reg[0] == REG_WITH_INDEX(R0_IDX) || \ + x->aopu.aop_reg[0] == REG_WITH_INDEX(R1_IDX) ))) + +#define IS_AOP_IMMEDIATE(x) (AOP(x) && (AOP_TYPE(x) == AOP_LIT || \ + AOP_TYPE(x) == AOP_IMMD || \ + AOP_TYPE(x) == AOP_STR)) + +#define SP_BP(sp, bp) (options.omitFramePtr ? sp : bp) +#define SYM_BP(sym) (SPEC_OCLS (sym->etype)->paged ? SP_BP("_spx", "_bpx") : SP_BP("sp", "_bp")) + +#define EQ(a, b) (strcmp (a, b) == 0) + +#define R0INB _G.bu.bs.r0InB +#define R1INB _G.bu.bs.r1InB +#define OPINB _G.bu.bs.OpInB +#define BITSINB _G.bu.bs.bitsInB +#define BINUSE _G.bu.BInUse + +static struct +{ + short r0Pushed; + short r1Pushed; + union + { + struct + { + short r0InB: 2; //2 so we can see it overflow + short r1InB: 2; //2 so we can see it overflow + short OpInB: 2; //2 so we can see it overflow + short bitsInB: 2; //2 so we can see it overflow + } bs; + short BInUse; + } bu; + short accInUse; + struct + { + int pushed; + int pushedregs; + int offset; + int param_offset; + int xpushed; + int xpushedregs; + int xoffset; + } stack; + set *sendSet; + symbol *currentFunc; +} +_G; + +static char *rb1regs[] = +{ + "b1_0", "b1_1", "b1_2", "b1_3", "b1_4", "b1_5", "b1_6", "b1_7", + "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7" +}; + +extern struct dbuf_s *codeOutBuf; + +#define RESULTONSTACK(x) \ + (IC_RESULT (x) && IC_RESULT (x)->aop && \ + IC_RESULT (x)->aop->type == AOP_STK ) + +#define MOVA(x) mova (x) /* use function to avoid multiple eval */ +#define MOVB(x) movb (x) + +#define CLRC emitcode ("clr","c") +#define SETC emitcode ("setb","c") + +static unsigned char SLMask[] = { 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, + 0xE0, 0xC0, 0x80, 0x00 + }; + +static unsigned char SRMask[] = { 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, + 0x07, 0x03, 0x01, 0x00 + }; + +#define MAX_REGISTER_BANKS 4 + +#define LSB 0 +#define MSB16 1 +#define MSB24 2 +#define MSB32 3 + +/*-----------------------------------------------------------------*/ +/* mcs51_emitDebuggerSymbol - associate the current code location */ +/* with a debugger symbol */ +/*-----------------------------------------------------------------*/ +void +mcs51_emitDebuggerSymbol (const char *debugSym) +{ + genLine.lineElement.isDebug = 1; + emitcode ("", "%s ==.", debugSym); + genLine.lineElement.isDebug = 0; +} + +/*-----------------------------------------------------------------*/ +/* mova - moves specified value into accumulator */ +/*-----------------------------------------------------------------*/ +static void +mova (const char *x) +{ + /* do some early peephole optimization */ + if (!strncmp (x, "a", 2) || !strncmp (x, "acc", 4)) + return; + + /* if it is a literal mov try to get it cheaper */ + if (*x == '#' && rtrackMoveALit (x)) + return; + + /* another early peephole optimization */ + if (EQ (x, "#0x00")) + { + emitcode ("clr", "a"); + return; + } + + emitcode ("mov", "a,%s", x); +} + +/*-----------------------------------------------------------------*/ +/* movb - moves specified value into register b */ +/*-----------------------------------------------------------------*/ +static void +movb (const char *x) +{ + /* do some early peephole optimization */ + if (!strncmp (x, "b", 2)) + return; + + /* if it is a literal mov try to get it cheaper */ + if (*x == '#') + { + emitcode ("mov", "b,%s", rtrackGetLit (x)); + return; + } + + emitcode ("mov", "b,%s", x); +} + +/*-----------------------------------------------------------------*/ +/* emitpush - push something on internal stack */ +/*-----------------------------------------------------------------*/ +static void +emitpush (const char *arg) +{ + char buf[] = "ar?"; + + _G.stack.pushed++; + if (!arg) + { + emitcode ("inc", "sp"); + return; + } + else if (EQ (arg, "a")) + { + arg = "acc"; + } + else if ((*arg == '@') || (*arg == '#')) + { + MOVA (arg); + arg = "acc"; + } + else if (EQ (arg, "r0") || EQ (arg, "r1") || EQ (arg, "r2") || EQ (arg, "r3") || + EQ (arg, "r4") || EQ (arg, "r5") || EQ (arg, "r6") || EQ (arg, "r7")) + { + buf[2] = arg[1]; + arg = buf; + } + emitcode ("push", arg); +} + +/*-----------------------------------------------------------------*/ +/* emitpop - pop something from internal stack */ +/*-----------------------------------------------------------------*/ +static void +emitpop (const char *arg) +{ + if (!arg) + emitcode ("dec", "sp"); + else + emitcode ("pop", arg); + _G.stack.pushed--; + wassertl (_G.stack.pushed >= 0, "stack underflow"); +} + +/*-----------------------------------------------------------------*/ +/* pushB - saves register B if necessary */ +/*-----------------------------------------------------------------*/ +static bool +pushB (void) +{ + bool pushedB = FALSE; + + if (BINUSE) + { + emitpush ("b"); +// printf("B was in use !\n"); + pushedB = TRUE; + } + else + { + OPINB++; + } + return pushedB; +} + +/*-----------------------------------------------------------------*/ +/* popB - restores value of register B if necessary */ +/*-----------------------------------------------------------------*/ +static void +popB (bool pushedB) +{ + if (pushedB) + { + emitpop ("b"); + } + else + { + OPINB--; + } +} + +/*-----------------------------------------------------------------*/ +/* pushReg - saves register */ +/*-----------------------------------------------------------------*/ +static bool +pushReg (int index, bool bits_pushed) +{ + const reg_info *reg = REG_WITH_INDEX (index); + if (reg->type == REG_BIT) + { + if (!bits_pushed) + emitpush (reg->base); + return TRUE; + } + else + emitpush (reg->dname); + return bits_pushed; +} + +/*-----------------------------------------------------------------*/ +/* popReg - restores register */ +/*-----------------------------------------------------------------*/ +static bool +popReg (int index, bool bits_popped) +{ + const reg_info *reg = REG_WITH_INDEX (index); + if (reg->type == REG_BIT) + { + if (!bits_popped) + emitpop (reg->base); + return TRUE; + } + else + emitpop (reg->dname); + return bits_popped; +} + +#if 0 +/*-----------------------------------------------------------------*/ +/* showR0R1status - helper for debugging getFreePtr failures */ +/*-----------------------------------------------------------------*/ +static void +showR0R1status(iCode * ic) +{ + bool r0iu, r1iu; + bool r0ou, r1ou; + + r0iu = bitVectBitValue (ic->rUsed, R0_IDX); + r1iu = bitVectBitValue (ic->rUsed, R1_IDX); + printf ("ic->rUsed = ["); + if (r0iu) + if (r1iu) + printf("r0,r1"); + else + printf("r0"); + else + if (r1iu) + printf("r1"); + printf("] "); + + r0ou = bitVectBitValue (ic->rMask, R0_IDX); + r1ou = bitVectBitValue (ic->rMask, R1_IDX); + printf ("ic->rMask = ["); + if (r0ou) + if (r1ou) + printf("r0,r1"); + else + printf("r0"); + else + if (r1ou) + printf("r1"); + printf("]\n"); +} +#endif + +/*-----------------------------------------------------------------*/ +/* getFreePtr - returns r0 or r1 whichever is free or can be pushed */ +/*-----------------------------------------------------------------*/ +static reg_info * +getFreePtr (iCode * ic, asmop * aop, bool result) +{ + bool r0iu, r1iu; + bool r0ou, r1ou; + + /* the logic: if r0 & r1 used in the instruction + then we are in trouble otherwise */ + + /* first check if r0 & r1 are used by this + instruction, in which case we are in trouble */ + r0iu = bitVectBitValue (ic->rUsed, R0_IDX); + r1iu = bitVectBitValue (ic->rUsed, R1_IDX); + if (r0iu && r1iu) + { + goto endOfWorld; + } + + r0ou = bitVectBitValue (ic->rMask, R0_IDX); + r1ou = bitVectBitValue (ic->rMask, R1_IDX); + + /* if no usage of r0 then return it */ + if (!r0iu && !r0ou) + { + ic->rUsed = bitVectSetBit (ic->rUsed, R0_IDX); + aop->type = AOP_R0; + + return aop->aopu.aop_ptr = REG_WITH_INDEX (R0_IDX); + } + + /* if no usage of r1 then return it */ + if (!r1iu && !r1ou) + { + ic->rUsed = bitVectSetBit (ic->rUsed, R1_IDX); + aop->type = AOP_R1; + + return aop->aopu.aop_ptr = REG_WITH_INDEX (R1_IDX); + } + + /* now we know they both have usage */ + /* if r0 not used in this instruction */ + if (!r0iu) + { + /* push it if not already pushed */ + if ((ic->op == IPUSH) || (ic->op == PCALL)) + { + MOVB (REG_WITH_INDEX (R0_IDX)->dname); + R0INB++; + } + else if (!_G.r0Pushed) + { + emitpush (REG_WITH_INDEX (R0_IDX)->dname); + _G.r0Pushed++; + } + + ic->rUsed = bitVectSetBit (ic->rUsed, R0_IDX); + aop->type = AOP_R0; + + return aop->aopu.aop_ptr = REG_WITH_INDEX (R0_IDX); + } + + /* if r1 not used then */ + + if (!r1iu) + { + /* push it if not already pushed */ + if ((ic->op == IPUSH) || (ic->op == PCALL)) + { + MOVB (REG_WITH_INDEX (R1_IDX)->dname); + R1INB++; + } + else if (!_G.r1Pushed) + { + emitpush (REG_WITH_INDEX (R1_IDX)->dname); + _G.r1Pushed++; + } + + ic->rUsed = bitVectSetBit (ic->rUsed, R1_IDX); + aop->type = AOP_R1; + return REG_WITH_INDEX (R1_IDX); + } + +endOfWorld: + /* I said end of world, but not quite end of world yet */ + /* if this is a result then we can push it on the stack */ + if (result) + { + aop->type = AOP_STK; + return NULL; + } + /* in the case that result AND left AND right needs a pointer reg + we can safely use the result's */ + if (bitVectBitValue (mcs51_rUmaskForOp (IC_RESULT (ic)), R0_IDX) && + (!OP_SYMBOL (IC_RESULT (ic)) || OP_SYMBOL (IC_RESULT (ic))->regs[getSize (operandType (IC_RESULT (ic))) - 1]->rIdx == R0_IDX)) + { + aop->type = AOP_R0; + return REG_WITH_INDEX (R0_IDX); + } + if (bitVectBitValue (mcs51_rUmaskForOp (IC_RESULT (ic)), R1_IDX) && + (!OP_SYMBOL (IC_RESULT (ic)) || OP_SYMBOL (IC_RESULT (ic))->regs[getSize (operandType (IC_RESULT (ic))) - 1]->rIdx == R1_IDX)) + { + aop->type = AOP_R1; + return REG_WITH_INDEX (R1_IDX); + } + + /* now this is REALLY the end of the world */ + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "getFreePtr should never reach here"); + exit (EXIT_FAILURE); +} + + +/*-----------------------------------------------------------------*/ +/* getTempRegs - initialize an array of pointers to GPR registers */ +/* that are not in use. Returns 1 if the requested */ +/* number of registers were available, 0 otherwise. */ +/*-----------------------------------------------------------------*/ +int +getTempRegs (reg_info ** tempRegs, int size, iCode * ic) +{ + bitVect *freeRegs; + int i; + int offset; + + if (!ic) + ic = genLine.lineElement.ic; + if (!ic) + return 0; + if (!_G.currentFunc) + return 0; + + freeRegs = newBitVect (8); + bitVectSetBit (freeRegs, R2_IDX); + bitVectSetBit (freeRegs, R3_IDX); + bitVectSetBit (freeRegs, R4_IDX); + bitVectSetBit (freeRegs, R5_IDX); + bitVectSetBit (freeRegs, R6_IDX); + bitVectSetBit (freeRegs, R7_IDX); + + if (IFFUNC_CALLEESAVES (_G.currentFunc->type)) + { + bitVect *newfreeRegs; + newfreeRegs = bitVectIntersect (freeRegs, _G.currentFunc->regsUsed); + freeBitVect (freeRegs); + freeRegs = newfreeRegs; + } + freeRegs = bitVectCplAnd (freeRegs, ic->rMask); + + offset = 0; + for (i = 0; i < freeRegs->size; i++) + { + if (bitVectBitValue (freeRegs, i)) + tempRegs[offset++] = REG_WITH_INDEX (i); + if (offset >= size) + { + freeBitVect (freeRegs); + return 1; + } + } + + freeBitVect (freeRegs); + return 0; +} + + +/*-----------------------------------------------------------------*/ +/* newAsmop - creates a new asmOp */ +/*-----------------------------------------------------------------*/ +static asmop * +newAsmop (short type) +{ + asmop *aop; + + aop = Safe_calloc (1, sizeof (asmop)); + aop->type = type; + aop->allocated = 1; + return aop; +} + +/*-----------------------------------------------------------------*/ +/* pointerCode - returns the code for a pointer type */ +/*-----------------------------------------------------------------*/ +static int +pointerCode (sym_link * etype) +{ + return PTR_TYPE (SPEC_OCLS (etype)); +} + +/*-----------------------------------------------------------------*/ +/* leftRightUseAcc - returns size of accumulator use by operands */ +/*-----------------------------------------------------------------*/ +static int +leftRightUseAcc (iCode * ic) +{ + operand *op; + int size; + int accuseSize = 0; + int accuse = 0; + + if (!ic) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "null iCode pointer"); + return 0; + } + + if (ic->op == IFX) + { + op = IC_COND (ic); + if (IS_OP_ACCUSE (op)) + { + accuse = 1; + size = getSize (OP_SYMBOL (op)->type); + if (size > accuseSize) + accuseSize = size; + } + } + else if (ic->op == JUMPTABLE) + { + op = IC_JTCOND (ic); + if (IS_OP_ACCUSE (op)) + { + accuse = 1; + size = getSize (OP_SYMBOL (op)->type); + if (size > accuseSize) + accuseSize = size; + } + } + else + { + op = IC_LEFT (ic); + if (IS_OP_ACCUSE (op)) + { + accuse = 1; + size = getSize (OP_SYMBOL (op)->type); + if (size > accuseSize) + accuseSize = size; + } + op = IC_RIGHT (ic); + if (IS_OP_ACCUSE (op)) + { + accuse = 1; + size = getSize (OP_SYMBOL (op)->type); + if (size > accuseSize) + accuseSize = size; + } + } + + if (accuseSize) + return accuseSize; + else + return accuse; +} + +/*-----------------------------------------------------------------*/ +/* stackoffset - stack offset for symbol */ +/*-----------------------------------------------------------------*/ +static int +stackoffset (symbol * sym) +{ + int offset = sym->stack; + if (options.omitFramePtr) + { + if (SPEC_OCLS (sym->etype)->paged) + offset -= _G.stack.xoffset + _G.stack.xpushed; + else + offset -= _G.stack.offset + _G.stack.pushed; + } + if (sym->stack < 0) + offset -= _G.stack.param_offset; + return offset; +} + +/*-----------------------------------------------------------------*/ +/* aopPtrForSym - pointer for symbol */ +/*-----------------------------------------------------------------*/ +static void +aopPtrForSym (symbol * sym, bool accuse, int offset, asmop * aop, iCode * ic) +{ + char *base; + struct dbuf_s tmpBuf; + dbuf_init (&tmpBuf, 1024); + if (sym->onStack) + { + dbuf_printf (&tmpBuf, "%s", SYM_BP (sym)); + } + else + { + dbuf_printf (&tmpBuf, "#%s", sym->rname); + } + base = dbuf_detach_c_str (&tmpBuf); + + offset += stackoffset (sym); + + if (abs (offset) >= 248) + werrorfl (ic->filename, ic->lineno, W_LIT_OVERFLOW); + + if ((abs (offset) < 3) || (accuse && (abs (offset) < 4))) + { + emitcode ("mov", "%s,%s", aop->aopu.aop_ptr->name, base); + while (offset < 0) + { + emitcode ("dec", aop->aopu.aop_ptr->name); + offset++; + } + while (offset > 0) + { + emitcode ("inc", aop->aopu.aop_ptr->name); + offset--; + } + } + else + { + if (accuse) + { + emitcode ("xch", "a,%s", aop->aopu.aop_ptr->name); + emitcode ("mov", "a,%s", base); + emitcode ("add", "a,#0x%02x", offset & 0xff); + emitcode ("xch", "a,%s", aop->aopu.aop_ptr->name); + } + else + { + emitcode ("mov", "a,%s", base); + emitcode ("add", "a,#0x%02x", offset & 0xff); + emitcode ("mov", "%s,a", aop->aopu.aop_ptr->name); + } + } + aop->paged = SPEC_OCLS (sym->etype)->paged; + dbuf_free (base); +} + +/*-----------------------------------------------------------------*/ +/* aopForSym - for a true symbol */ +/*-----------------------------------------------------------------*/ +static asmop * +aopForSym (iCode * ic, symbol * sym, bool result) +{ + asmop *aop; + memmap *space; + bool accuse = leftRightUseAcc (ic) || _G.accInUse; + + wassertl (ic != NULL, "Got a null iCode"); + wassertl (sym != NULL, "Got a null symbol"); + + space = SPEC_OCLS (sym->etype); + + /* if already has one */ + if (sym->aop) + { + sym->aop->allocated++; + return sym->aop; + } + + /* assign depending on the storage class */ + /* if it is on the stack or indirectly addressable */ + /* space we need to assign either r0 or r1 to it */ + if (sym->onStack || sym->iaccess) + { + sym->aop = aop = newAsmop (0); + aop->aopu.aop_ptr = getFreePtr (ic, aop, result); + aop->size = getSize (sym->type); + + /* now assign the address of the variable to + the pointer register */ + if (aop->type != AOP_STK) + { + aopPtrForSym (sym, accuse, 0, aop, ic); + } + else + { + aop->aopu.aop_sym = sym; + } + return aop; + } + + /* if in bit space */ + if (IN_BITSPACE (space)) + { + sym->aop = aop = newAsmop (AOP_CRY); + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + return aop; + } + /* if it is in direct space */ + if (IN_DIRSPACE (space)) + { + //printf("aopForSym, using AOP_DIR for %s (%x)\n", sym->name, sym); + //printTypeChainRaw(sym->type, NULL); + //printf("space = %s\n", space ? space->sname : "NULL"); + sym->aop = aop = newAsmop (AOP_DIR); + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + return aop; + } + + /* special case for a function */ + if (IS_FUNC (sym->type)) + { + sym->aop = aop = newAsmop (AOP_IMMD); + aop->aopu.aop_immd.aop_immd1 = Safe_strdup (sym->rname); + aop->size = getSize (sym->type); + return aop; + } + + /* only remaining is far space */ + /* in which case DPTR gets the address */ + sym->aop = aop = newAsmop (AOP_DPTR); + + rtrackLoadDptrWithSym (sym->rname); + + aop->size = getSize (sym->type); + + /* if it is in code space */ + if (IN_CODESPACE (space)) + aop->code = 1; + + return aop; +} + +/*-----------------------------------------------------------------*/ +/* aopForRemat - rematerializes an object */ +/*-----------------------------------------------------------------*/ +static asmop * +aopForRemat (symbol * sym) +{ + iCode *ic = sym->rematiCode; + asmop *aop = newAsmop (AOP_IMMD); + int ptr_type = 0; + int val = 0; + sym_link *from_type = NULL; + const char *from_name = NULL; + struct dbuf_s dbuf; + + for (;;) + { + if (ic->op == '+') + { + val += (int) operandLitValue (IC_RIGHT (ic)); + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + } + else if (ic->op == '-') + { + val -= (int) operandLitValue (IC_RIGHT (ic)); + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + } + else if (IS_CAST_ICODE (ic)) + { + from_type = operandType (IC_RIGHT (ic)); + from_name = IS_SYMOP (IC_RIGHT (ic)) ? OP_SYMBOL (IC_RIGHT (ic))->name : NULL; + aop->aopu.aop_immd.from_cast_remat = 1; + ic = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + } + else + { + break; + } + } + + dbuf_init (&dbuf, 128); + if (val) + { + dbuf_printf (&dbuf, "(%s %c 0x%04x)", OP_SYMBOL (IC_LEFT (ic))->rname, val >= 0 ? '+' : '-', abs (val) & 0xffff); + } + else + { + dbuf_append_str (&dbuf, OP_SYMBOL (IC_LEFT (ic))->rname); + } + + aop->aopu.aop_immd.aop_immd1 = dbuf_detach_c_str (&dbuf); + /* set immd2 field if required */ + if (aop->aopu.aop_immd.from_cast_remat) + { + ptr_type = pointerTypeToGPByte (DCL_TYPE (from_type), from_name, sym->name); + dbuf_init (&dbuf, 128); + dbuf_tprintf (&dbuf, "#!constbyte", ptr_type); + aop->aopu.aop_immd.aop_immd2 = dbuf_detach_c_str (&dbuf); + } + + return aop; +} + +/*-----------------------------------------------------------------*/ +/* regsInCommon - two operands have some registers in common */ +/*-----------------------------------------------------------------*/ +static bool +regsInCommon (operand * op1, operand * op2) +{ + symbol *sym1, *sym2; + int i; + + /* if they have registers in common */ + if (!IS_SYMOP (op1) || !IS_SYMOP (op2)) + return FALSE; + + sym1 = OP_SYMBOL (op1); + sym2 = OP_SYMBOL (op2); + + if (sym1->nRegs == 0 || sym2->nRegs == 0) + return FALSE; + + for (i = 0; i < sym1->nRegs; i++) + { + int j; + if (!sym1->regs[i]) + continue; + + for (j = 0; j < sym2->nRegs; j++) + { + if (!sym2->regs[j]) + continue; + + if (sym2->regs[j] == sym1->regs[i]) + return TRUE; + } + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* operandsEqu - equivalent */ +/*-----------------------------------------------------------------*/ +static bool +operandsEqu (operand * op1, operand * op2) +{ + symbol *sym1, *sym2; + + /* if they're not symbols */ + if (!IS_SYMOP (op1) || !IS_SYMOP (op2)) + return FALSE; + + sym1 = OP_SYMBOL (op1); + sym2 = OP_SYMBOL (op2); + + /* if both are itemps & one is spilt + and the other is not then false */ + if (IS_ITEMP (op1) && IS_ITEMP (op2) && sym1->isspilt != sym2->isspilt) + return FALSE; + + /* if they are the same */ + if (sym1 == sym2) + return TRUE; + + /* if they have the same rname */ + if (sym1->rname[0] && sym2->rname[0] && EQ (sym1->rname, sym2->rname) && !(IS_PARM (op2) && IS_ITEMP (op1))) + return TRUE; + + /* if left is a tmp & right is not */ + if (IS_ITEMP (op1) && !IS_ITEMP (op2) && sym1->isspilt && (sym1->usl.spillLoc == sym2)) + return TRUE; + + if (IS_ITEMP (op2) && !IS_ITEMP (op1) && sym2->isspilt && sym1->level > 0 && (sym2->usl.spillLoc == sym1)) + return TRUE; + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* sameByte - two asmops have the same address at given offsets */ +/*-----------------------------------------------------------------*/ +static bool +sameByte (asmop * aop1, int off1, asmop * aop2, int off2) +{ + if (aop1 == aop2 && off1 == off2) + return TRUE; + + if (aop1->type != AOP_REG && aop1->type != AOP_CRY) + return FALSE; + + if (aop1->type != aop2->type) + return FALSE; + + if (aop1->aopu.aop_reg[off1] != aop2->aopu.aop_reg[off2]) + return FALSE; + + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* sameRegs - two asmops have the same registers */ +/*-----------------------------------------------------------------*/ +static bool +sameRegs (asmop * aop1, asmop * aop2) +{ + int i; + + if (aop1 == aop2) + return TRUE; + + if (aop1->type != AOP_REG && aop1->type != AOP_CRY) + return FALSE; + + if (aop1->type != aop2->type) + return FALSE; + + if (aop1->size != aop2->size) + return FALSE; + + for (i = 0; i < aop1->size; i++) + if (aop1->aopu.aop_reg[i] != aop2->aopu.aop_reg[i]) + return FALSE; + + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* aopOp - allocates an asmop for an operand : */ +/*-----------------------------------------------------------------*/ +static void +aopOp (operand * op, iCode * ic, bool result) +{ + asmop *aop; + symbol *sym; + int i; + + if (!op) + return; + + /* if this a literal */ + if (IS_OP_LITERAL (op)) + { + op->aop = aop = newAsmop (AOP_LIT); + aop->aopu.aop_lit = OP_VALUE (op); + aop->size = getSize (operandType (op)); + return; + } + + /* if already has a asmop then continue */ + if (op->aop) + { + op->aop->allocated++; + return; + } + + /* if the underlying symbol has a aop */ + if (IS_SYMOP (op) && OP_SYMBOL (op)->aop) + { + op->aop = OP_SYMBOL (op)->aop; + op->aop->allocated++; + return; + } + + /* if this is a true symbol */ + if (IS_TRUE_SYMOP (op)) + { + op->aop = aopForSym (ic, OP_SYMBOL (op), result); + return; + } + + /* this is a temporary : this has + only five choices : + a) register + b) spillocation + c) rematerialize + d) conditional + e) can be a return use only */ + + sym = OP_SYMBOL (op); + + /* if the type is a conditional */ + if (sym->regType == REG_CND) + { + sym->aop = op->aop = aop = newAsmop (AOP_CRY); + aop->size = sym->ruonly ? 1 : 0; + return; + } + + /* if it is spilt then two situations + a) is rematerialize + b) has a spill location */ + if (sym->isspilt || sym->nRegs == 0) + { + /* rematerialize it NOW */ + if (sym->remat) + { + sym->aop = op->aop = aop = aopForRemat (sym); + aop->size = operandSize (op); + return; + } + + if (sym->accuse) + { + int i; + sym->aop = op->aop = aop = newAsmop (AOP_ACC); + aop->size = getSize (sym->type); + for (i = 0; i < 2; i++) + aop->aopu.aop_str[i] = accUse[i]; + return; + } + + if (sym->ruonly) + { + unsigned i; + + sym->aop = op->aop = aop = newAsmop (AOP_STR); + aop->size = getSize (sym->type); + for (i = 0; i < fReturnSizeMCS51; i++) + aop->aopu.aop_str[i] = fReturn[i]; + return; + } + + if (sym->isspilt && sym->usl.spillLoc) + { + asmop *oldAsmOp = NULL; + + if (getSize (sym->type) != getSize (sym->usl.spillLoc->type)) + { + /* force a new aop if sizes differ */ + oldAsmOp = sym->usl.spillLoc->aop; + sym->usl.spillLoc->aop = NULL; + } + sym->aop = op->aop = aop = aopForSym (ic, sym->usl.spillLoc, result); + if (getSize (sym->type) != getSize (sym->usl.spillLoc->type)) + { + /* Don't reuse the new aop, go with the last one */ + sym->usl.spillLoc->aop = oldAsmOp; + } + aop->size = getSize (sym->type); + return; + } + + /* else must be a dummy iTemp */ + sym->aop = op->aop = aop = newAsmop (AOP_DUMMY); + aop->size = getSize (sym->type); + return; + } + + /* if the type is a bit register */ + if (sym->regType == REG_BIT && sym->regs[0]->type == REG_BIT) + { + sym->aop = op->aop = aop = newAsmop (AOP_CRY); + aop->size = sym->nRegs; //1??? + aop->aopu.aop_reg[0] = sym->regs[0]; + aop->aopu.aop_dir = sym->regs[0]->name; + return; + } + + /* must be in a register */ + sym->aop = op->aop = aop = newAsmop (AOP_REG); + aop->size = sym->nRegs; + for (i = 0; i < sym->nRegs; i++) + aop->aopu.aop_reg[i] = sym->regs[i]; +} + +/*-----------------------------------------------------------------*/ +/* freeAsmop - free up the asmop given to an operand */ +/*-----------------------------------------------------------------*/ +static void +freeAsmop (operand * op, asmop * aaop, iCode * ic, bool pop) +{ + asmop *aop; + int sz; + symbol *sym; + + if (!op) + aop = aaop; + else + aop = op->aop; + + if (!aop) + return; + + aop->allocated--; + + if (aop->allocated) + goto dealloc; + + /* depending on the asmop type only three cases need work + AOP_R0, AOP_R1 & AOP_STK */ + switch (aop->type) + { + case AOP_R0: + if (R0INB) + { + emitcode ("mov", "r0,b"); + R0INB--; + } + else if (_G.r0Pushed) + { + if (pop) + { + emitpop ("ar0"); + _G.r0Pushed--; + } + } + bitVectUnSetBit (ic->rUsed, R0_IDX); + break; + + case AOP_R1: + if (R1INB) + { + emitcode ("mov", "r1,b"); + R1INB--; + } + else if (_G.r1Pushed) + { + if (pop) + { + emitpop ("ar1"); + _G.r1Pushed--; + } + } + bitVectUnSetBit (ic->rUsed, R1_IDX); + break; + + case AOP_STK: + sz = aop->size; + sym = aop->aopu.aop_sym; + bitVectUnSetBit (ic->rUsed, R0_IDX); + bitVectUnSetBit (ic->rUsed, R1_IDX); + + getFreePtr (ic, aop, FALSE); + + aopPtrForSym (sym, FALSE, aop->size - 1, aop, ic); + + while (sz--) + { + emitpop ("acc"); + if (aop->paged) + emitcode ("movx", "@%s,a", aop->aopu.aop_ptr->name); + else + emitcode ("mov", "@%s,a", aop->aopu.aop_ptr->name); + if (!sz) + break; + emitcode ("dec", "%s", aop->aopu.aop_ptr->name); + } + op->aop = aop; + freeAsmop (op, NULL, ic, TRUE); + if (_G.r1Pushed) + { + emitpop ("ar1"); + _G.r1Pushed--; + } + if (_G.r0Pushed) + { + emitpop ("ar0"); + _G.r0Pushed--; + } + break; + } + +dealloc: + /* all other cases just dealloc */ + if (op) + { + op->aop = NULL; + if (IS_SYMOP (op)) + { + OP_SYMBOL (op)->aop = NULL; + /* if the symbol has a spill */ + if (SPIL_LOC (op)) + SPIL_LOC (op)->aop = NULL; + } + } +} + +/*------------------------------------------------------------------*/ +/* freeForBranchAsmop - partial free up of Asmop for a branch; just */ +/* pop r0 or r1 off stack if pushed */ +/*------------------------------------------------------------------*/ +static void +freeForBranchAsmop (operand * op, iCode * ic) +{ + asmop *aop; + + if (!op) + return; + + aop = op->aop; + + if (!aop) + return; + + if (!aop->allocated) + return; + + switch (aop->type) + { + case AOP_R0: + if (R0INB) + { + emitcode ("mov", "r0,b"); + } + else if (_G.r0Pushed) + { + emitcode ("pop", "ar0"); /* without pushed-- */ + } + break; + + case AOP_R1: + if (R1INB) + { + emitcode ("mov", "r1,b"); + } + else if (_G.r1Pushed) + { + emitcode ("pop", "ar1"); /* without pushed-- */ + } + break; + + case AOP_STK: + { + int sz = aop->size; + + emitcode ("mov", "b,r0"); + aopPtrForSym (aop->aopu.aop_sym, FALSE, 0, aop, ic); + + while (sz--) + { + emitcode ("pop", "acc"); /* without pushed-- */ + if (aop->paged) + emitcode ("movx", "@r0,a"); + else + emitcode ("mov", "@r0,a"); + if (!sz) + break; + emitcode ("dec", "r0"); + } + emitcode ("mov", "r0,b"); + } + } +} + +/*------------------------------------------------------------------*/ +/* freeForBranchAsmops - partial free up of 3 Asmops for a branch; */ +/* just pop r0 or r1 off stack if pushed */ +/*------------------------------------------------------------------*/ +static void +freeForBranchAsmops (operand * op1, operand * op2, operand * op3, iCode * ic) +{ + if (op1) + freeForBranchAsmop (op1, ic); + if (op2) + freeForBranchAsmop (op2, ic); + if (op3) + freeForBranchAsmop (op3, ic); +} + +/*-----------------------------------------------------------------*/ +/* opIsGptr: returns non-zero if the passed operand is */ +/* a generic pointer type. */ +/*-----------------------------------------------------------------*/ +static int +opIsGptr (operand * op) +{ + if (op && (AOP_SIZE (op) == GPTRSIZE) && (IS_GENPTR (operandType (op)) || IFFUNC_ISBANKEDCALL (operandType (op)))) + { + return 1; + } + return 0; +} + +/*-----------------------------------------------------------------*/ +/* swapOperands - swap two operands */ +/*-----------------------------------------------------------------*/ +static void +swapOperands (operand ** left, operand ** right) +{ + operand *t = *right; + *right = *left; + *left = t; +} + +/*-----------------------------------------------------------------*/ +/* aopGetUsesAcc - indicates ahead of time whether aopGet() will */ +/* clobber the accumulator */ +/*-----------------------------------------------------------------*/ +static bool +aopGetUsesAcc (operand * oper, int offset) +{ + asmop *aop = AOP (oper); + + if (offset > (aop->size - 1)) + return FALSE; + + switch (aop->type) + { + + case AOP_R0: + case AOP_R1: + if (aop->paged) + return TRUE; + return FALSE; + case AOP_DPTR: + return TRUE; + case AOP_IMMD: + return FALSE; + case AOP_DIR: + return FALSE; + case AOP_REG: + wassert (!EQ (aop->aopu.aop_reg[offset]->name, "a")); + return FALSE; + case AOP_CRY: + return TRUE; + case AOP_ACC: + if (offset) + return FALSE; + return TRUE; + case AOP_LIT: + return FALSE; + case AOP_STR: + if (EQ (aop->aopu.aop_str[offset], "a")) + return TRUE; + return FALSE; + case AOP_DUMMY: + return FALSE; + default: + /* Error case --- will have been caught already */ + wassert (0); + return FALSE; + } +} + +/*-------------------------------------------------------------------*/ +/* aopGet - for fetching value of the aop */ +/*-------------------------------------------------------------------*/ +/* + * NOTE: function returns a pointer to a reusable dynamically allocated + * buffer, which should never be freed! + * Subsequent call to aopGet() will rewrite the result of the previous + * call, so the content of the result should be copied to an other + * location, usually using Safe_strdup(), in order to perserve it. + */ +static const char * +aopGet (operand * oper, int offset, bool bit16, bool dname) +{ + asmop *aop = AOP (oper); + static struct dbuf_s dbuf = { 0 }; + + if (dbuf_is_initialized (&dbuf)) + { + /* reuse the dynamically allocated buffer */ + dbuf_set_length (&dbuf, 0); + } + else + { + /* first time: initialize the dynamically allocated buffer */ + dbuf_init (&dbuf, 128); + } + + /* offset is greater than + size then zero */ + if (offset > (aop->size - 1) && aop->type != AOP_LIT) + { + dbuf_append_str (&dbuf, zero); + } + else + { + /* depending on type */ + switch (aop->type) + { + case AOP_DUMMY: + dbuf_append_str (&dbuf, zero); + break; + + case AOP_R0: + case AOP_R1: + /* if we need to increment it */ + while (offset > aop->coff) + { + emitcode ("inc", "%s", aop->aopu.aop_ptr->name); + aop->coff++; + } + + while (offset < aop->coff) + { + emitcode ("dec", "%s", aop->aopu.aop_ptr->name); + aop->coff--; + } + + aop->coff = offset; + if (aop->paged) + { + emitcode ("movx", "a,@%s", aop->aopu.aop_ptr->name); + dbuf_append_str (&dbuf, dname ? "acc" : "a"); + } + else + { + dbuf_printf (&dbuf, "@%s", aop->aopu.aop_ptr->name); + } + break; + + case AOP_DPTR: + if (aop->code && aop->coff == 0 && offset >= 1) + { + emitcode ("mov", "a,#0x%02x", offset); + emitcode ("movc", "a,@a+dptr"); + } + else + { + while (offset > aop->coff) + { + emitcode ("inc", "dptr"); + aop->coff++; + } + + while (offset < aop->coff) + { + emitcode ("lcall", "__decdptr"); + aop->coff--; + } + + aop->coff = offset; + if (aop->code) + { + emitcode ("clr", "a"); + emitcode ("movc", "a,@a+dptr"); + } + else + { + emitcode ("movx", "a,@dptr"); + } + } + dbuf_append_str (&dbuf, dname ? "acc" : "a"); + break; + + case AOP_IMMD: + if (aop->aopu.aop_immd.from_cast_remat && opIsGptr (oper) && offset == GPTRSIZE - 1) + { + dbuf_printf (&dbuf, "%s", aop->aopu.aop_immd.aop_immd2); + } + else if (bit16) + { + dbuf_printf (&dbuf, "#%s", aop->aopu.aop_immd.aop_immd1); + } + else if (offset) + { + dbuf_printf (&dbuf, "#(%s >> %d)", aop->aopu.aop_immd.aop_immd1, offset * 8); + } + else + { + dbuf_printf (&dbuf, "#%s", aop->aopu.aop_immd.aop_immd1); + } + break; + + case AOP_DIR: + if ((SPEC_SCLS (getSpec (operandType (oper))) == S_SFR) && (aop->size > 1)) + { + dbuf_printf (&dbuf, "((%s >> %d) & 0xFF)", aop->aopu.aop_dir, offset * 8); + } + else if (offset) + { + dbuf_printf (&dbuf, "(%s + %d)", aop->aopu.aop_dir, offset); + } + else + { + dbuf_printf (&dbuf, "%s", aop->aopu.aop_dir); + } + break; + + case AOP_REG: + dbuf_append_str (&dbuf, dname ? aop->aopu.aop_reg[offset]->dname : aop->aopu.aop_reg[offset]->name); + break; + + case AOP_CRY: + if (!IS_OP_RUONLY (oper)) + emitcode ("mov", "c,%s", aop->aopu.aop_dir); + emitcode ("clr", "a"); + emitcode ("rlc", "a"); + dbuf_append_str (&dbuf, dname ? "acc" : "a"); + break; + + case AOP_ACC: + dbuf_append_str (&dbuf, (!offset && dname) ? "acc" : aop->aopu.aop_str[offset]); + break; + + case AOP_LIT: + if (opIsGptr (oper) && IS_FUNCPTR (operandType (oper)) && offset == GPTRSIZE - 1) + { + dbuf_append_str (&dbuf, aopLiteralGptr (NULL, aop->aopu.aop_lit)); + } + else + { + int size = 1 + (bit16 ? 1 : 0); + dbuf_append_str (&dbuf, aopLiteralLong (aop->aopu.aop_lit, offset, size)); + } + break; + + case AOP_STR: + aop->coff = offset; + if (EQ (aop->aopu.aop_str[offset], "a") && dname) + dbuf_append_str (&dbuf, "acc"); + else + dbuf_append_str (&dbuf, aop->aopu.aop_str[offset]); + break; + + default: + dbuf_destroy (&dbuf); + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopget got unsupported aop->type"); + exit (EXIT_FAILURE); + } + } + return dbuf_c_str (&dbuf); +} + +/*-----------------------------------------------------------------*/ +/* aopPutUsesAcc - indicates ahead of time whether aopPut() will */ +/* clobber the accumulator */ +/*-----------------------------------------------------------------*/ +static bool +aopPutUsesAcc (operand * oper, const char *s, int offset) +{ + asmop *aop = AOP (oper); + + if (offset > (aop->size - 1)) + return FALSE; + + switch (aop->type) + { + case AOP_DUMMY: + return TRUE; + case AOP_DIR: + return FALSE; + case AOP_REG: + wassert (!EQ (aop->aopu.aop_reg[offset]->name, "a")); + return FALSE; + case AOP_DPTR: + return TRUE; + case AOP_R0: + case AOP_R1: + return ((aop->paged) || (*s == '@')); + case AOP_STK: + return (*s == '@'); + case AOP_CRY: + return (!aop->aopu.aop_dir || !EQ (s, aop->aopu.aop_dir)); + case AOP_STR: + return FALSE; + case AOP_IMMD: + return FALSE; + case AOP_ACC: + return FALSE; + default: + /* Error case --- will have been caught already */ + wassert (0); + return FALSE; + } +} + +/*-----------------------------------------------------------------*/ +/* aopPut - puts a string for a aop and indicates if acc is in use */ +/*-----------------------------------------------------------------*/ +static bool +aopPut (operand * result, const char *s, int offset) +{ + bool bvolatile = isOperandVolatile (result, FALSE); + bool accuse = FALSE; + asmop *aop = AOP (result); + const char *d = NULL; + static struct dbuf_s dbuf = { 0 }; + + if (dbuf_is_initialized (&dbuf)) + { + /* reuse the dynamically allocated buffer */ + dbuf_set_length (&dbuf, 0); + } + else + { + /* first time: initialize the dynamically allocated buffer */ + dbuf_init (&dbuf, 128); + } + + if (aop->size && offset > (aop->size - 1)) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopPut got offset > aop->size"); + exit (EXIT_FAILURE); + } + + /* will assign value to value */ + /* depending on where it is ofcourse */ + switch (aop->type) + { + case AOP_DUMMY: + MOVA (s); /* read s in case it was volatile */ + accuse = TRUE; + break; + + case AOP_DIR: + if ((SPEC_SCLS (getSpec (operandType (result))) == S_SFR) && (aop->size > 1)) + { + dbuf_printf (&dbuf, "((%s >> %d) & 0xFF)", aop->aopu.aop_dir, offset * 8); + } + else if (offset) + { + dbuf_printf (&dbuf, "(%s + %d)", aop->aopu.aop_dir, offset); + } + else + { + dbuf_append_str (&dbuf, aop->aopu.aop_dir); + } + + if (!EQ (dbuf_c_str (&dbuf), s) || bvolatile) + { + emitcode ("mov", "%s,%s", dbuf_c_str (&dbuf), s); + } + if (EQ (dbuf_c_str (&dbuf), "acc")) + { + accuse = TRUE; + } + break; + + case AOP_REG: + if (!EQ (aop->aopu.aop_reg[offset]->name, s) && !EQ (aop->aopu.aop_reg[offset]->dname, s)) + { + if (*s == '@' || + EQ (s, "r0") || EQ (s, "r1") || EQ (s, "r2") || EQ (s, "r3") || + EQ (s, "r4") || EQ (s, "r5") || EQ (s, "r6") || EQ (s, "r7")) + { + emitcode ("mov", "%s,%s", aop->aopu.aop_reg[offset]->dname, s); + } + else + { + emitcode ("mov", "%s,%s", aop->aopu.aop_reg[offset]->name, s); + } + } + break; + + case AOP_DPTR: + if (aop->code) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopPut writing to code space"); + exit (EXIT_FAILURE); + } + + /* if not in accumulator */ + MOVA (s); + + while (offset > aop->coff) + { + aop->coff++; + emitcode ("inc", "dptr"); + } + + while (offset < aop->coff) + { + aop->coff--; + emitcode ("lcall", "__decdptr"); + } + + aop->coff = offset; + + emitcode ("movx", "@dptr,a"); + break; + + case AOP_R0: + case AOP_R1: + while (offset > aop->coff) + { + aop->coff++; + emitcode ("inc", "%s", aop->aopu.aop_ptr->name); + } + while (offset < aop->coff) + { + aop->coff--; + emitcode ("dec", "%s", aop->aopu.aop_ptr->name); + } + aop->coff = offset; + + if (aop->paged) + { + MOVA (s); + emitcode ("movx", "@%s,a", aop->aopu.aop_ptr->name); + } + else if (*s == '@') + { + MOVA (s); + emitcode ("mov", "@%s,a", aop->aopu.aop_ptr->name); + } + else if (EQ (s, "r0") || EQ (s, "r1") || EQ (s, "r2") || EQ (s, "r3") || + EQ (s, "r4") || EQ (s, "r5") || EQ (s, "r6") || EQ (s, "r7")) + { + dbuf_printf (&dbuf, "a%s", s); + emitcode ("mov", "@%s,%s", aop->aopu.aop_ptr->name, dbuf_c_str (&dbuf)); + } + else + { + emitcode ("mov", "@%s,%s", aop->aopu.aop_ptr->name, s); + } + break; + + case AOP_STK: + emitpush (s); + break; + + case AOP_CRY: + // destination is carry for return-use-only + d = (IS_OP_RUONLY (result)) ? "c" : aop->aopu.aop_dir; + + // source is no literal and not in carry + if (!EQ (s, zero) && !EQ (s, one) && !EQ (s, "c")) + { + MOVA (s); + /* set C, if a >= 1 */ + emitcode ("add", "a,#!constbyte", 0xff); + s = "c"; + } + // now source is zero, one or carry + + /* if result no bit variable */ + if (!d) + { + if (EQ (s, "c")) + { + /* inefficient: move carry into A and use jz/jnz */ + emitcode ("clr", "a"); + emitcode ("rlc", "a"); + accuse = TRUE; + } + else + { + MOVA (s); + accuse = TRUE; + } + } + else if (EQ (s, zero)) + emitcode ("clr", "%s", d); + else if (EQ (s, one)) + emitcode ("setb", "%s", d); + else if (!EQ (s, d)) + emitcode ("mov", "%s,c", d); + break; + + case AOP_STR: + aop->coff = offset; + if (!EQ (aop->aopu.aop_str[offset], s) || bvolatile) + emitcode ("mov", "%s,%s", aop->aopu.aop_str[offset], s); + break; + + case AOP_ACC: + accuse = TRUE; + aop->coff = offset; + if (!offset && EQ (s, "acc") && !bvolatile) + break; + + if (!EQ (aop->aopu.aop_str[offset], s) && !bvolatile) + emitcode ("mov", "%s,%s", aop->aopu.aop_str[offset], s); + break; + + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopPut got unsupported aop->type"); + exit (EXIT_FAILURE); + } + + return accuse; +} + +/*--------------------------------------------------------------------*/ +/* loadDptrFromOperand - load dptr (and optionally B) from operand op */ +/*--------------------------------------------------------------------*/ +static void +loadDptrFromOperand (operand *op, bool loadBToo) +{ + if (AOP_TYPE (op) != AOP_STR) + { + /* if this is rematerializable */ + if (AOP_TYPE (op) == AOP_IMMD) + { + emitcode ("mov", "dptr,%s", aopGet (op, 0, TRUE, FALSE)); + if (loadBToo) + { + if (AOP (op)->aopu.aop_immd.from_cast_remat) + emitcode ("mov", "b,%s", aopGet (op, AOP_SIZE (op) - 1, FALSE, FALSE)); + else + { + wassertl (FALSE, "need pointerCode"); + emitcode (";", "mov b,???"); + /* genPointerGet and genPointerSet originally did different + ** things for this case. Both seem wrong. + ** from genPointerGet: + ** emitcode ("mov", "b,#%d", pointerCode (retype)); + ** from genPointerSet: + ** emitcode ("mov", "b,%s + 1", aopGet (result, 0, TRUE, FALSE)); + */ + } + } + } + else if (AOP_TYPE (op) == AOP_LIT) + { + emitcode ("mov", "dptr,%s", aopGet (op, 0, TRUE, FALSE)); + if (loadBToo) + emitcode ("mov", "b,%s", aopGet (op, AOP_SIZE (op) - 1, FALSE, FALSE)); + } + else if (AOP_TYPE (op) == AOP_DPTR) + { + emitpush (aopGet (op, 0, FALSE, FALSE)); + if (loadBToo) + { + emitpush (aopGet (op, 1, FALSE, FALSE)); + emitcode ("mov", "b,%s", aopGet (op, AOP_SIZE (op) - 1, FALSE, FALSE)); + emitpop ("dph"); + } + else + { + emitcode ("mov", "dph,%s", aopGet (op, 1, FALSE, FALSE)); + } + emitpop ("dpl"); + } + else + { + /* we need to get it byte by byte */ + emitcode ("mov", "dpl,%s", aopGet (op, 0, FALSE, FALSE)); + emitcode ("mov", "dph,%s", aopGet (op, 1, FALSE, FALSE)); + if (loadBToo) + emitcode ("mov", "b,%s", aopGet (op, AOP_SIZE (op) - 1, FALSE, FALSE)); + } + } +} + +/*-----------------------------------------------------------------*/ +/* reAdjustPreg - points a register back to where it should */ +/*-----------------------------------------------------------------*/ +static void +reAdjustPreg (asmop * aop) +{ + if ((aop->coff == 0) || (aop->size <= 1)) + return; + + switch (aop->type) + { + case AOP_R0: + case AOP_R1: + while (aop->coff--) + emitcode ("dec", "%s", aop->aopu.aop_ptr->name); + break; + case AOP_DPTR: + while (aop->coff--) + { + emitcode ("lcall", "__decdptr"); + } + break; + } + aop->coff = 0; +} + +/*-----------------------------------------------------------------*/ +/* getDataSize - get the operand data size */ +/*-----------------------------------------------------------------*/ +static int +getDataSize (operand * op) +{ + int size = AOP_SIZE (op); + + if (size == GPTRSIZE) + { + sym_link *type = operandType (op); + if (IS_GENPTR (type)) + { + /* generic pointer; arithmetic operations + * should ignore the high byte (pointer type). + */ + size--; + } + } + return size; +} + +/*-----------------------------------------------------------------*/ +/* outAcc - output Acc */ +/*-----------------------------------------------------------------*/ +static void +outAcc (operand * result) +{ + int size, offset; + size = getDataSize (result); + if (size) + { + aopPut (result, "a", 0); + size--; + offset = 1; + /* unsigned or positive */ + while (size--) + { + aopPut (result, zero, offset++); + } + } +} + +/*-----------------------------------------------------------------*/ +/* outBitC - output a bit C */ +/*-----------------------------------------------------------------*/ +static void +outBitC (operand * result) +{ + /* if the result is bit */ + if (AOP_TYPE (result) == AOP_CRY) + { + if (!IS_OP_RUONLY (result) && !IS_OP_ACCUSE (result)) + aopPut (result, "c", 0); + } + else if (AOP_TYPE (result) != AOP_DUMMY) + { + emitcode ("clr", "a"); + emitcode ("rlc", "a"); + outAcc (result); + } +} + +/*-----------------------------------------------------------------*/ +/* toBoolean - emit code for orl a,operator(sizeop) */ +/*-----------------------------------------------------------------*/ +static void +toBoolean (operand * oper) +{ + int size = AOP_SIZE (oper) - 1; + int offset = 1; + bool AccUsed; + sym_link *type = operandType (oper); + bool pushedB; + + /* always need B for float */ + AccUsed = IS_FLOAT (type); + + while (!AccUsed && size--) + { + AccUsed |= aopGetUsesAcc (oper, offset++); + } + + if (opIsGptr (oper)) + { + /* assumes that banks never map to address 0x0000 + so it suffices to check dptr part only and ignore b */ + size = AOP_SIZE (oper) - 2; + } + else + { + size = AOP_SIZE (oper) - 1; + } + + offset = 0; + if (size && AccUsed && (AOP (oper)->type != AOP_ACC)) + { + pushedB = pushB (); + MOVB (aopGet (oper, offset++, FALSE, FALSE)); + while (--size) + { + MOVA (aopGet (oper, offset++, FALSE, FALSE)); + emitcode ("orl", "b,a"); + } + MOVA (aopGet (oper, offset++, FALSE, FALSE)); + if (IS_FLOAT (type)) + emitcode ("anl", "a,#0x7F"); //clear sign bit + emitcode ("orl", "a,b"); + popB (pushedB); + } + else + { + MOVA (aopGet (oper, offset++, FALSE, FALSE)); + while (size--) + { + emitcode ("orl", "a,%s", aopGet (oper, offset++, FALSE, FALSE)); + } + } +} + +/*-----------------------------------------------------------------*/ +/* toCarry - make boolean and move into carry */ +/*-----------------------------------------------------------------*/ +static void +toCarry (operand *oper) +{ + /* if the operand is a literal then + we know what the value is */ + if (AOP_TYPE (oper) == AOP_LIT) + { + if ((int) operandLitValue (oper)) + SETC; + else + CLRC; + } + else if (AOP_TYPE (oper) == AOP_CRY) + { + if (!IS_OP_ACCUSE (oper)) + emitcode ("mov", "c,%s", oper->aop->aopu.aop_dir); + } + else if (IS_BOOL (operandType (oper)) || IS_BITFIELD (operandType (oper)) && SPEC_BLEN (getSpec (operandType (oper))) == 1) + { + MOVA (aopGet (oper, 0, FALSE, FALSE)); + emitcode ("rrc", "a"); + } + else + { + /* or the operand into a */ + toBoolean (oper); + /* set C, if a >= 1 */ + emitcode ("add", "a,#0xff"); + } +} + +/*-----------------------------------------------------------------*/ +/* assignBit - assign operand to bit operand */ +/*-----------------------------------------------------------------*/ +static void +assignBit (operand * result, operand * right) +{ + emitcode (";", "assignBit"); + /* if the right side is a literal then + we know what the value is */ + if (AOP_TYPE (right) == AOP_LIT) + { + if ((int) operandLitValue (right)) + aopPut (result, one, 0); + else + aopPut (result, zero, 0); + } + else + { + toCarry (right); + outBitC (result); + } +} + +/*-------------------------------------------------------------------*/ +/* xch_a_aopGet - for exchanging acc with value of the aop */ +/*-------------------------------------------------------------------*/ +static const char * +xch_a_aopGet (operand * oper, int offset, bool bit16, bool dname) +{ + const char *l; + + if (aopGetUsesAcc (oper, offset)) + { + emitcode ("mov", "b,a"); + MOVA (aopGet (oper, offset, bit16, dname)); + emitcode ("xch", "a,b"); + aopPut (oper, "a", offset); + emitcode ("xch", "a,b"); + l = "b"; + } + else + { + l = aopGet (oper, offset, bit16, dname); + emitcode ("xch", "a,%s", l); + } + return l; +} + +/*-----------------------------------------------------------------*/ +/* genNot - generate code for ! operation */ +/*-----------------------------------------------------------------*/ +static void +genNot (iCode * ic) +{ + symbol *tlbl; + + D (emitcode (";", "genNot")); + + /* assign asmOps to operand & result */ + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + + /* if in bit space then a special case */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY) + { + /* if left==result then cpl bit */ + if (sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic)))) + { + emitcode ("cpl", "%s", IC_LEFT (ic)->aop->aopu.aop_dir); + } + else + { + toCarry (IC_LEFT (ic)); + emitcode ("cpl", "c"); + outBitC (IC_RESULT (ic)); + } + goto release; + } + + toBoolean (IC_LEFT (ic)); + + /* set C, if a == 0 */ + tlbl = newiTempLabel (NULL); + emitcode ("cjne", "a,#0x01,!tlabel", labelKey2num (tlbl->key)); + emitLabel (tlbl); + outBitC (IC_RESULT (ic)); + +release: + /* release the aops */ + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? 0 : 1)); +} + +/*-----------------------------------------------------------------*/ +/* genCpl - generate code for complement */ +/*-----------------------------------------------------------------*/ +static void +genCpl (iCode * ic) +{ + int offset = 0; + int size; + symbol *tlbl; + sym_link *letype = getSpec (operandType (IC_LEFT (ic))); + + D (emitcode (";", "genCpl")); + + /* assign asmOps to operand & result */ + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + + /* special case if in bit space */ + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY) + { + const char *l; + + if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY || (SPEC_USIGN (letype) && IS_CHAR (letype))) + { + /* promotion rules are responsible for this strange result: + bit -> int -> ~int -> bit + uchar -> int -> ~int -> bit + */ + emitcode ("setb", "%s", IC_RESULT (ic)->aop->aopu.aop_dir); + goto release; + } + + tlbl = newiTempLabel (NULL); + l = aopGet (IC_LEFT (ic), offset++, FALSE, FALSE); + if ((AOP_TYPE (IC_LEFT (ic)) == AOP_ACC && offset == 0) || + AOP_TYPE (IC_LEFT (ic)) == AOP_REG || IS_AOP_PREG (IC_LEFT (ic))) + { + emitcode ("cjne", "%s,#0xFF,!tlabel", l, labelKey2num (tlbl->key)); + } + else + { + MOVA (l); + emitcode ("cjne", "a,#0xFF,!tlabel", labelKey2num (tlbl->key)); + } + emitLabel (tlbl); + outBitC (IC_RESULT (ic)); + goto release; + } + + size = AOP_SIZE (IC_RESULT (ic)); + while (size--) + { + MOVA (aopGet (IC_LEFT (ic), offset, FALSE, FALSE)); + emitcode ("cpl", "a"); + aopPut (IC_RESULT (ic), "a", offset++); + } + +release: + /* release the aops */ + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? 0 : 1)); +} + +/*-----------------------------------------------------------------*/ +/* genUminusFloat - unary minus for floating points */ +/*-----------------------------------------------------------------*/ +static void +genUminusFloat (operand * op, operand * result) +{ + int size, offset = 0; + + D (emitcode (";", "genUminusFloat")); + + /* for this we just copy and then flip the bit */ + + size = AOP_SIZE (op) - 1; + + while (size--) + { + aopPut (result, aopGet (op, offset, FALSE, FALSE), offset); + offset++; + } + + MOVA (aopGet (op, offset, FALSE, FALSE)); + + emitcode ("cpl", "acc.7"); + aopPut (result, "a", offset); +} + +/*-----------------------------------------------------------------*/ +/* genUminus - unary minus code generation */ +/*-----------------------------------------------------------------*/ +static void +genUminus (iCode * ic) +{ + int offset, size; + sym_link *optype; + + D (emitcode (";", "genUminus")); + + /* assign asmops */ + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + + /* if both in bit space then special + case */ + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY && AOP_TYPE (IC_LEFT (ic)) == AOP_CRY) + { + + emitcode ("mov", "c,%s", IC_LEFT (ic)->aop->aopu.aop_dir); + emitcode ("cpl", "c"); + emitcode ("mov", "%s,c", IC_RESULT (ic)->aop->aopu.aop_dir); + goto release; + } + + optype = operandType (IC_LEFT (ic)); + + /* if float then do float stuff */ + if (IS_FLOAT (optype)) + { + genUminusFloat (IC_LEFT (ic), IC_RESULT (ic)); + goto release; + } + + /* otherwise subtract from zero */ + size = AOP_SIZE (IC_LEFT (ic)); + offset = 0; + while (size--) + { + const char *l = aopGet (IC_LEFT (ic), offset, FALSE, FALSE); + if (EQ (l, "a")) + { + if (offset == 0) + SETC; + emitcode ("cpl", "a"); + emitcode ("addc", "a,#0x00"); + } + else + { + if (offset == 0) + CLRC; + emitcode ("clr", "a"); + emitcode ("subb", "a,%s", l); + } + aopPut (IC_RESULT (ic), "a", offset++); + } + + /* if any remaining bytes in the result */ + /* we just need to propagate the sign */ + if ((size = (AOP_SIZE (IC_RESULT (ic)) - AOP_SIZE (IC_LEFT (ic))))) + { + emitcode ("rlc", "a"); + emitcode ("subb", "a,acc"); + while (size--) + aopPut (IC_RESULT (ic), "a", offset++); + } + +release: + /* release the aops */ + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? 0 : 1)); +} + +/*-----------------------------------------------------------------*/ +/* inExcludeList - return 1 if the string is in exclude Reg list */ +/*-----------------------------------------------------------------*/ +static int +regsCmp (void *p1, void *p2) +{ + return (STRCASECMP ((char *) p1, (char *) (p2)) == 0); +} + +static bool +inExcludeList (char *s) +{ + const char *p = setFirstItem (options.excludeRegsSet); + + if (p == NULL || STRCASECMP (p, "none") == 0) + return FALSE; + + return isinSetWith (options.excludeRegsSet, s, regsCmp); +} + +/*-----------------------------------------------------------------*/ +/* xstackRegisters - create bitmask for registers on xstack */ +/*-----------------------------------------------------------------*/ +static int +xstackRegisters (bitVect * rsave, bool push, int count, char szRegs[32]) +{ + int i; + int mask = 0; + + szRegs[0] = '\0'; + + for (i = mcs51_nRegs; i >= 0; i--) + { + if (bitVectBitValue (rsave, i)) + { + reg_info *reg = REG_WITH_INDEX (i); + if (reg->type == REG_BIT) + { + mask |= 0x01; + strncat (szRegs, reg->base, 31); + } + else + { + if (i == R0_IDX) + { + mask |= 0x100; + } + else + { + //set bit(n) for Rn + mask |= (0x01 << reg->offset); + } + strncat (szRegs, reg->name, 31); + } + } + } + return mask ^ 0xFF; //invert all bits for jbc +} + +/*-----------------------------------------------------------------*/ +/* saveRegisters - will look for a call and save the registers */ +/*-----------------------------------------------------------------*/ +static void +saveRegisters (iCode * lic) +{ + int i; + iCode *ic; + bitVect *rsave; + + /* look for call */ + for (ic = lic; ic; ic = ic->next) + if (ic->op == CALL || ic->op == PCALL) + break; + + if (!ic) + { + fprintf (stderr, "found parameter push with no function call\n"); + return; + } + + /* if the registers have been saved already or don't need to be then + do nothing */ + if (ic->regsSaved) + return; + if (IS_SYMOP (IC_LEFT (ic))) + { + sym_link *type = OP_SYM_TYPE (IC_LEFT (ic)); + if (IFFUNC_ISNAKED (type) && !IFFUNC_ISBANKEDCALL (type)) + return; + if (IFFUNC_CALLEESAVES (type)) + return; + } + + if (IFFUNC_CALLEESAVES (_G.currentFunc->type)) + { + /* save all registers if the caller is callee_saves and the callee is not */ + rsave = bitVectCopy (mcs51_allBankregs ()); + if (!inExcludeList ("bits")) + { + rsave = bitVectUnion (rsave, mcs51_allBitregs ()); + BitBankUsed = 1; + } + } + else + /* save only the registers in use at this time */ + rsave = bitVectCopy (ic->rMask); + /* but skip the ones for the result */ + rsave = bitVectCplAnd (rsave, mcs51_rUmaskForOp (IC_RESULT (ic))); + + ic->regsSaved = 1; + if (options.useXstack) + { + bitVect *rsavebits = bitVectIntersect (bitVectCopy (mcs51_allBitregs ()), rsave); + int nBits = bitVectnBitsOn (rsavebits); + int count = bitVectnBitsOn (rsave); + + if (nBits != 0) + { + count = count - nBits + 1; + /* remove all but the first bits as they are pushed all at once */ + rsave = bitVectCplAnd (rsave, rsavebits); + rsave = bitVectSetBit (rsave, bitVectFirstBit (rsavebits)); + } + freeBitVect (rsavebits); + + if (count == 1) + { + reg_info *reg = REG_WITH_INDEX (bitVectFirstBit (rsave)); + emitpush (REG_WITH_INDEX (R0_IDX)->dname); + if (reg->type == REG_BIT) + { + emitcode ("mov", "a,%s", reg->base); + } + else + { + emitcode ("mov", "a,%s", reg->name); + } + emitcode ("mov", "r0,%s", spname); + emitcode ("inc", "%s", spname); // allocate before use + emitcode ("movx", "@r0,a"); + _G.stack.xpushed++; + emitpop (REG_WITH_INDEX (R0_IDX)->dname); + } + else if (count != 0) + { + if ((FUNC_REGBANK (currFunc->type) == 0) && optimize.codeSize) + { + char szRegs[32]; + int mask = xstackRegisters (rsave, TRUE, count, szRegs); + if (BINUSE) + emitpush ("b"); + emitcode ("mov", "a,#0x%02x", count); + emitcode ("mov", "b,#0x%02x", mask & 0xFF); + if (mask & 0x100) + emitcode ("lcall", "___sdcc_xpush_regs_r0\t;(%s)", szRegs); + else + emitcode ("lcall", "___sdcc_xpush_regs\t;(%s)", szRegs); + genLine.lineCurr->isInline = 1; + if (BINUSE) + emitpop ("b"); + _G.stack.xpushed += count; + } + else + { + emitpush (REG_WITH_INDEX (R0_IDX)->dname); + emitcode ("mov", "r0,%s", spname); + if (count == 2) + { + emitcode ("inc", "%s", spname); + emitcode ("inc", "%s", spname); + } + else + { + MOVA ("r0"); + emitcode ("add", "a,#0x%02x", count); + emitcode ("mov", "%s,a", spname); + } + for (i = 0; i < mcs51_nRegs; i++) + { + if (bitVectBitValue (rsave, i)) + { + reg_info *reg = REG_WITH_INDEX (i); + if (i == R0_IDX) + { + emitpop ("acc"); + emitpush ("acc"); + } + else if (reg->type == REG_BIT) + { + emitcode ("mov", "a,%s", reg->base); + } + else + { + emitcode ("mov", "a,%s", reg->name); + } + emitcode ("movx", "@r0,a"); + _G.stack.xpushed++; + if (--count) + { + emitcode ("inc", "r0"); + } + } + } + emitpop (REG_WITH_INDEX (R0_IDX)->dname); + } + } + } + else + { + bool bits_pushed = FALSE; + for (i = 0; i < mcs51_nRegs; i++) + { + if (bitVectBitValue (rsave, i)) + { + bits_pushed = pushReg (i, bits_pushed); + } + } + } + freeBitVect (rsave); +} + +/*-----------------------------------------------------------------*/ +/* unsaveRegisters - pop the pushed registers */ +/*-----------------------------------------------------------------*/ +static void +unsaveRegisters (iCode * ic) +{ + int i; + bitVect *rsave; + + if (IFFUNC_CALLEESAVES (_G.currentFunc->type)) + { + /* restore all registers if the caller is callee_saves and the callee is not */ + rsave = bitVectCopy (mcs51_allBankregs ()); + if (!inExcludeList ("bits")) + { + rsave = bitVectUnion (rsave, mcs51_allBitregs ()); + BitBankUsed = 1; + } + } + else + /* restore only the registers in use at this time */ + rsave = bitVectCopy (ic->rMask); + /* but skip the ones for the result */ + rsave = bitVectCplAnd (rsave, mcs51_rUmaskForOp (IC_RESULT (ic))); + + if (options.useXstack) + { + bitVect *rsavebits = bitVectIntersect (bitVectCopy (mcs51_allBitregs ()), rsave); + int nBits = bitVectnBitsOn (rsavebits); + int count = bitVectnBitsOn (rsave); + + if (nBits != 0) + { + count = count - nBits + 1; + /* remove all but the first bits as they are popped all at once */ + rsave = bitVectCplAnd (rsave, rsavebits); + rsave = bitVectSetBit (rsave, bitVectFirstBit (rsavebits)); + } + freeBitVect (rsavebits); + + if (count == 1) + { + reg_info *reg = REG_WITH_INDEX (bitVectFirstBit (rsave)); + emitcode ("mov", "r0,%s", spname); + emitcode ("dec", "r0"); + emitcode ("movx", "a,@r0"); + _G.stack.xpushed--; + if (reg->type == REG_BIT) + { + emitcode ("mov", "%s,a", reg->base); + } + else + { + emitcode ("mov", "%s,a", reg->name); + } + emitcode ("dec", "%s", spname); + } + else if (count != 0) + { + if ((FUNC_REGBANK (currFunc->type) == 0) && optimize.codeSize) + { + char szRegs[32]; + int mask = xstackRegisters (rsave, FALSE, count, szRegs); + emitcode ("mov", "b,#0x%02x", mask & 0xFF); + if (mask & 0x100) + emitcode ("lcall", "___sdcc_xpop_regs_r0\t;(%s)", szRegs); + else + emitcode ("lcall", "___sdcc_xpop_regs\t;(%s)", szRegs); + genLine.lineCurr->isInline = 1; + _G.stack.xpushed -= count; + } + else + { + bool resultInR0 = bitVectBitValue (mcs51_rUmaskForOp (IC_RESULT (ic)), R0_IDX); + if (resultInR0) + { + emitpush (REG_WITH_INDEX (R0_IDX)->dname); + } + emitcode ("mov", "r0,%s", spname); + for (i = mcs51_nRegs; i >= 0; i--) + { + if (bitVectBitValue (rsave, i)) + { + reg_info *reg = REG_WITH_INDEX (i); + emitcode ("dec", "r0"); + emitcode ("movx", "a,@r0"); + _G.stack.xpushed--; + if (i == R0_IDX) + { + emitpush ("acc"); + } + else if (reg->type == REG_BIT) + { + emitcode ("mov", "%s,a", reg->base); + } + else + { + emitcode ("mov", "%s,a", reg->name); + } + } + } + emitcode ("mov", "%s,r0", spname); + if (bitVectBitValue (rsave, R0_IDX) || resultInR0) + { + emitpop (REG_WITH_INDEX (R0_IDX)->dname); + } + } + } + } + else + { + bool bits_popped = FALSE; + for (i = mcs51_nRegs; i >= 0; i--) + { + if (bitVectBitValue (rsave, i)) + { + bits_popped = popReg (i, bits_popped); + } + } + } + freeBitVect (rsave); +} + + +/*-----------------------------------------------------------------*/ +/* pushSide - */ +/*-----------------------------------------------------------------*/ +static void +pushSide (operand * oper, int size, iCode * ic) +{ + int offset = 0; + int nPushed = _G.r0Pushed + _G.r1Pushed; + + aopOp (oper, ic, FALSE); + + if (nPushed != _G.r0Pushed + _G.r1Pushed) + { + while (offset < size) + { + const char *l = aopGet (oper, offset, FALSE, TRUE); + emitcode ("mov", "%s,%s", fReturn[offset++], l); + } + freeAsmop (oper, NULL, ic, TRUE); + offset = 0; + while (offset < size) + { + emitpush (fReturn[offset++]); + } + return; + } + + while (size--) + { + const char *l = aopGet (oper, offset++, FALSE, TRUE); + if (AOP_TYPE (oper) != AOP_REG && AOP_TYPE (oper) != AOP_DIR) + { + MOVA (l); + emitpush ("acc"); + } + else + { + emitpush (l); + } + } + + freeAsmop (oper, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* assignResultValue - also indicates if acc is in use afterwards */ +/*-----------------------------------------------------------------*/ +static bool +assignResultValue (operand * oper, operand * func) +{ + int offset = 0; + int size = AOP_SIZE (oper); + bool accuse = FALSE; + bool pushedA = FALSE; + + if (func && IS_BIT (getSpec (operandType (func)))) + { + outBitC (oper); + return FALSE; + } + if ((size > 3) && aopPutUsesAcc (oper, fReturn[offset], offset)) + { + emitpush ("acc"); + pushedA = TRUE; + } + while (size--) + { + if ((offset == 3) && pushedA) + emitpop ("acc"); + accuse |= aopPut (oper, fReturn[offset], offset); + offset++; + } + return accuse; +} + + +/*-----------------------------------------------------------------*/ +/* genXpush - pushes onto the external stack */ +/*-----------------------------------------------------------------*/ +static void +genXpush (iCode * ic) +{ + asmop *aop = newAsmop (0); + reg_info *r; + int size, offset = 0; + + D (emitcode (";", "genXpush")); + + aopOp (IC_LEFT (ic), ic, FALSE); + r = getFreePtr (ic, aop, FALSE); + + size = AOP_SIZE (IC_LEFT (ic)); + emitcode ("mov", "%s,%s", r->name, spname); + + // allocate space first + if (size <= 2) + { + emitcode ("inc", "%s", spname); + if (size == 2) + emitcode ("inc", "%s", spname); + } + else + { + MOVA (r->name); + emitcode ("add", "a,#0x%02x", size); + emitcode ("mov", "%s,a", spname); + } + + while (offset < size) + { + MOVA (aopGet (IC_LEFT (ic), offset++, FALSE, FALSE)); + emitcode ("movx", "@%s,a", r->name); + emitcode ("inc", "%s", r->name); + } + _G.stack.xpushed += size; + + freeAsmop (NULL, aop, ic, TRUE); + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genIpush - generate code for pushing this gets a little complex */ +/*-----------------------------------------------------------------*/ +static void +genIpush (iCode * ic) +{ + int size, offset = 0; + char *prev; + + D (emitcode (";", "genIpush")); + + /* if this is not a parm push : ie. it is spill push + and spill push is always done on the local stack */ + if (!ic->parmPush) + { + /* and the item is spilt then do nothing */ + if (OP_SYMBOL (IC_LEFT (ic))->isspilt) + return; + + aopOp (IC_LEFT (ic), ic, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + /* push it on the stack */ + while (size--) + { + emitpush (aopGet (IC_LEFT (ic), offset++, FALSE, TRUE)); + } + return; + } + + /* this is a parameter push: in this case we call + the routine to find the call and save those + registers that need to be saved */ + saveRegisters (ic); + + /* if use external stack then call the external + stack pushing routine */ + if (options.useXstack) + { + genXpush (ic); + return; + } + + /* then do the push */ + aopOp (IC_LEFT (ic), ic, FALSE); + + // pushSide(IC_LEFT(ic), AOP_SIZE(IC_LEFT(ic))); + size = AOP_SIZE (IC_LEFT (ic)); + + prev = Safe_strdup (""); + while (size--) + { + const char *l = aopGet (IC_LEFT (ic), offset++, FALSE, TRUE); + if (AOP_TYPE (IC_LEFT (ic)) != AOP_REG && AOP_TYPE (IC_LEFT (ic)) != AOP_DIR) + { + if (!EQ (l, prev) || *l == '@') + MOVA (l); + emitpush ("acc"); + } + else + { + emitpush (l); + } + Safe_free (prev); + prev = Safe_strdup (l); + } + Safe_free (prev); + + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genIpop - recover the registers: can happen only for spilling */ +/*-----------------------------------------------------------------*/ +static void +genIpop (iCode * ic) +{ + int size, offset; + + D (emitcode (";", "genIpop")); + + /* if the temp was not pushed then */ + if (OP_SYMBOL (IC_LEFT (ic))->isspilt) + return; + + aopOp (IC_LEFT (ic), ic, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + offset = size - 1; + while (size--) + { + emitpop (aopGet (IC_LEFT (ic), offset--, FALSE, TRUE)); + } + + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* popForBranch - recover the spilt registers for a branch */ +/*-----------------------------------------------------------------*/ +static void +popForBranch (iCode * ic, bool markGenerated) +{ + while (ic && ic->op == IPOP) + { + int pushed = _G.stack.pushed; + genIpop (ic); + if (markGenerated) + ic->generated = 1; /* mark the icode as generated */ + else + _G.stack.pushed = pushed; + ic = ic->next; + } +} + +/*-----------------------------------------------------------------*/ +/* emitDummyCall - emit a dummy call for --no-ret-without-call */ +/*-----------------------------------------------------------------*/ +static void +emitDummyCall(void) +{ + symbol *dummyLabel; + + if (!options.no_ret_without_call) + return; + dummyLabel = newiTempLabel (NULL); + emitcode ("lcall", "!tlabel", labelKey2num (dummyLabel->key)); + emitLabel (dummyLabel); + emitcode ("dec", "sp"); + emitcode ("dec", "sp"); +} + +/*-----------------------------------------------------------------*/ +/* saveRBank - saves an entire register bank on the stack */ +/*-----------------------------------------------------------------*/ +static void +saveRBank (int bank, iCode * ic, bool pushPsw) +{ + int i; + int count = 8 + (pushPsw ? 1 : 0); + asmop *aop = NULL; + reg_info *r = NULL; + + if (options.useXstack) + { + if (!ic) + { + /* Assume r0 is available for use. */ + r = REG_WITH_INDEX (R0_IDX); + } + else + { + aop = newAsmop (0); + r = getFreePtr (ic, aop, FALSE); + } + // allocate space first + emitcode ("mov", "%s,%s", r->name, spname); + MOVA (r->name); + emitcode ("add", "a,#!constbyte", count); + emitcode ("mov", "%s,a", spname); + } + + for (i = 0; i < 8; i++) + { + if (options.useXstack) + { + emitcode ("mov", "a,(%s+%d)", regs8051[i].base, 8 * bank + regs8051[i].offset); + emitcode ("movx", "@%s,a", r->name); + _G.stack.xpushed++; + if (--count) + emitcode ("inc", "%s", r->name); + } + else + { + char buf[16] = ""; + SNPRINTF (buf, 16, "(%s+%d)", regs8051[i].base, 8 * bank + regs8051[i].offset); + emitpush (buf); + } + } + + if (pushPsw) + { + if (options.useXstack) + { + emitcode ("mov", "a,psw"); + emitcode ("movx", "@%s,a", r->name); + _G.stack.xpushed++; + } + else + { + emitpush ("psw"); + } + + emitcode ("mov", "psw,#!constbyte", (bank << 3) & 0x00ff); + } + + if (aop) + { + freeAsmop (NULL, aop, ic, TRUE); + } + + if (ic) + { + ic->bankSaved = 1; + } +} + +/*-----------------------------------------------------------------*/ +/* unsaveRBank - restores the register bank from stack */ +/*-----------------------------------------------------------------*/ +static void +unsaveRBank (int bank, iCode * ic, bool popPsw) +{ + int i; + asmop *aop = NULL; + reg_info *r = NULL; + + if (options.useXstack) + { + if (!ic) + { + /* Assume r0 is available for use. */ + r = REG_WITH_INDEX (R0_IDX); + } + else + { + aop = newAsmop (0); + r = getFreePtr (ic, aop, FALSE); + } + emitcode ("mov", "%s,%s", r->name, spname); + } + + if (popPsw) + { + if (options.useXstack) + { + emitcode ("dec", "%s", r->name); + emitcode ("movx", "a,@%s", r->name); + emitcode ("mov", "psw,a"); + _G.stack.xpushed--; + } + else + { + emitpop ("psw"); + } + } + + for (i = 7; i >= 0; i--) + { + if (options.useXstack) + { + emitcode ("dec", "%s", r->name); + emitcode ("movx", "a,@%s", r->name); + emitcode ("mov", "(%s+%d),a", regs8051[i].base, 8 * bank + regs8051[i].offset); + _G.stack.xpushed--; + } + else + { + char buf[16] = ""; + SNPRINTF (buf, 16, "(%s+%d)", regs8051[i].base, 8 * bank + regs8051[i].offset); + emitpop (buf); + } + } + + if (options.useXstack) + { + emitcode ("mov", "%s,%s", spname, r->name); + } + + if (aop) + { + freeAsmop (NULL, aop, ic, TRUE); + } +} + +/*-----------------------------------------------------------------*/ +/* genSend - gen code for SEND */ +/*-----------------------------------------------------------------*/ +static void +genSend (set * sendSet) +{ + iCode *sic; + int bit_count = 0; + + /* first we do all bit parameters */ + for (sic = setFirstItem (sendSet); sic; sic = setNextItem (sendSet)) + { + if (sic->argreg > 12) + { + int bit = sic->argreg - 13; + + aopOp (IC_LEFT (sic), sic, FALSE); + + /* if left is a literal then + we know what the value is */ + if (AOP_TYPE (IC_LEFT (sic)) == AOP_LIT) + { + if (((int) operandLitValue (IC_LEFT (sic)))) + emitcode ("setb", "b[%d]", bit); + else + emitcode ("clr", "b[%d]", bit); + } + else + { + /* we need to or */ + toCarry (IC_LEFT (sic)); + emitcode ("mov", "b[%d],c", bit); + } + bit_count++; + BitBankUsed = 1; + + freeAsmop (IC_LEFT (sic), NULL, sic, TRUE); + } + } + + if (options.useXstack || bit_count || setFirstItem (sendSet) && operandSize (IC_LEFT ((iCode *)(setFirstItem (sendSet)))) >= 6) + { + if (bit_count) + BITSINB++; + saveRegisters (setFirstItem (sendSet)); + if (bit_count) + BITSINB--; + } + + if (bit_count) + { + emitcode ("mov", "bits,b"); + } + + /* then we do all other parameters */ + for (sic = setFirstItem (sendSet); sic; sic = setNextItem (sendSet)) + { + if (sic->argreg <= 12) + { + int size, offset = 0; + aopOp (IC_LEFT (sic), sic, FALSE); + size = AOP_SIZE (IC_LEFT (sic)); + + if (sic->argreg == 1) + { + if (AOP_TYPE (IC_LEFT (sic)) != AOP_DPTR) + { + bool pushedA = FALSE; + while (size--) + { + const char *l = aopGet (IC_LEFT (sic), offset, FALSE, FALSE); + if (!EQ (l, fReturn[offset])) + if (fReturn[offset][0] == 'r' && (AOP_TYPE (IC_LEFT (sic)) == AOP_REG || AOP_TYPE (IC_LEFT (sic)) == AOP_R0 || AOP_TYPE (IC_LEFT (sic)) == AOP_R1)) + emitcode ("mov", "a%s,%s", fReturn[offset], l); // use register's direct address instead of name + else + emitcode ("mov", "%s,%s", fReturn[offset], l); + else if (EQ (l, "a") && size != 0) + { + emitpush ("acc"); + pushedA = TRUE; + } + offset++; + } + if (pushedA) + emitpop ("acc"); + } + else /* need to load dpl, dph, etc from @dptr */ + { + while (size--) + { + MOVA (aopGet (IC_LEFT (sic), offset, FALSE, FALSE)); + emitpush ("acc"); + offset++; + } + size = AOP_SIZE (IC_LEFT (sic)); + while (size--) + { + offset--; + if (!EQ ("a", fReturn[offset])) + { + emitpop (fReturn[offset]); + } + else + { + emitpop ("acc"); + } + } + } + } + else + { + while (size--) + { + emitcode ("mov", "%s,%s", rb1regs[sic->argreg + offset - 5], aopGet (IC_LEFT (sic), offset, FALSE, FALSE)); + offset++; + } + } + freeAsmop (IC_LEFT (sic), NULL, sic, TRUE); + } + } +} + +/*-----------------------------------------------------------------*/ +/* selectRegBank - emit code to select the register bank */ +/*-----------------------------------------------------------------*/ +static void +selectRegBank (short bank, bool keepFlags) +{ + /* if f.e. result is in carry */ + if (keepFlags) + { + emitcode ("anl", "psw,#0xE7"); + if (bank) + emitcode ("orl", "psw,#0x%02x", (bank << 3) & 0xff); + } + else + { + emitcode ("mov", "psw,#0x%02x", (bank << 3) & 0xff); + } +} + +/*-----------------------------------------------------------------*/ +/* genCall - generates a call statement */ +/*-----------------------------------------------------------------*/ +static void +genCall (iCode * ic) +{ + sym_link *dtype; + sym_link *etype; + bool swapBanks = FALSE; + bool accuse = FALSE; + bool accPushed = FALSE; + bool resultInF0 = FALSE; + bool assignResultGenerated = FALSE; + + D (emitcode (";", "genCall")); + + dtype = operandType (IC_LEFT (ic)); + etype = getSpec (dtype); + /* if send set is not empty then assign */ + if (_G.sendSet) + { + if (IFFUNC_ISREENT (dtype)) + { + /* need to reverse the send set */ + genSend (reverseSet (_G.sendSet)); + } + else + { + genSend (_G.sendSet); + } + _G.sendSet = NULL; + } + + /* if we are calling a not _naked function that is not using + the same register bank then we need to save the + destination registers on the stack */ + if (currFunc && dtype && !IFFUNC_ISNAKED (dtype) && + (FUNC_REGBANK (currFunc->type) != FUNC_REGBANK (dtype)) && !IFFUNC_ISISR (dtype)) + { + swapBanks = TRUE; + } + + /* if caller saves & we have not saved then */ + if (!ic->regsSaved) + saveRegisters (ic); + + if (swapBanks) + { + emitcode ("mov", "psw,#!constbyte", ((FUNC_REGBANK (dtype)) << 3) & 0xff); + } + + /* make the call */ + if (IFFUNC_ISBANKEDCALL (dtype)) + { + if (IFFUNC_CALLEESAVES (dtype)) + { + werror (E_BANKED_WITH_CALLEESAVES); + } + else + { + if (IS_LITERAL (etype)) + { + emitcode ("mov", "r0,#%s", aopLiteralLong (OP_VALUE (IC_LEFT (ic)), 0, 1)); + emitcode ("mov", "r1,#%s", aopLiteralLong (OP_VALUE (IC_LEFT (ic)), 1, 1)); + emitcode ("mov", "r2,#%s", aopLiteralLong (OP_VALUE (IC_LEFT (ic)), 2, 1)); + } + else + { + char *name = (OP_SYMBOL (IC_LEFT (ic))->rname[0] ? + OP_SYMBOL (IC_LEFT (ic))->rname : OP_SYMBOL (IC_LEFT (ic))->name); + emitcode ("mov", "r0,#%s", name); + emitcode ("mov", "r1,#(%s >> 8)", name); + emitcode ("mov", "r2,#(%s >> 16)", name); + } + emitcode ("lcall", "__sdcc_banked_call"); + } + } + else + { + if (IS_LITERAL (etype)) + { + emitcode ("lcall", "0x%04X", ulFromVal (OP_VALUE (IC_LEFT (ic)))); + } + else + { + emitcode ("lcall", "%s", (OP_SYMBOL (IC_LEFT (ic))->rname[0] ? + OP_SYMBOL (IC_LEFT (ic))->rname : OP_SYMBOL (IC_LEFT (ic))->name)); + } + } + + if (swapBanks) + { + selectRegBank (FUNC_REGBANK (currFunc->type), IS_BIT (etype)); + } + + /* if we need assign a result value */ + if ((IS_ITEMP (IC_RESULT (ic)) && + !IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic))) && + (OP_SYMBOL (IC_RESULT (ic))->nRegs || + OP_SYMBOL (IC_RESULT (ic))->accuse || + OP_SYMBOL (IC_RESULT (ic))->spildir || IS_BIT (etype))) || IS_TRUE_SYMOP (IC_RESULT (ic))) + { + _G.accInUse++; + aopOp (IC_RESULT (ic), ic, FALSE); + _G.accInUse--; + + accuse = assignResultValue (IC_RESULT (ic), IC_LEFT (ic)); + assignResultGenerated = TRUE; + + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + + /* adjust the stack for parameters if required */ + if (ic->parmBytes) + { + int i; + if (ic->parmBytes > 3) + { + if (accuse) + { + emitpush ("acc"); + accPushed = TRUE; + } + if (IS_BIT (etype) && IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic))) && !assignResultGenerated) + { + emitcode ("mov", "F0,c"); + resultInF0 = TRUE; + } + + emitcode ("mov", "a,%s", spname); + emitcode ("add", "a,#0x%02x", (-ic->parmBytes) & 0xff); + emitcode ("mov", "%s,a", spname); + if (options.useXstack) + _G.stack.xpushed -= ic->parmBytes; + else + _G.stack.pushed -= ic->parmBytes; + + /* unsaveRegisters from xstack needs acc, but */ + /* unsaveRegisters from stack needs this popped */ + if (accPushed && !options.useXstack) + { + emitpop ("acc"); + accPushed = FALSE; + } + } + else + { + for (i = 0; i < ic->parmBytes; i++) + emitcode ("dec", "%s", spname); + if (options.useXstack) + _G.stack.xpushed -= ic->parmBytes; + else + _G.stack.pushed -= ic->parmBytes; + } + } + + /* if we had saved some registers then unsave them */ + if (ic->regsSaved && !IFFUNC_CALLEESAVES (dtype)) + { + if (accuse && !accPushed && options.useXstack) + { + /* xstack needs acc, but doesn't touch normal stack */ + emitpush ("acc"); + accPushed = TRUE; + } + unsaveRegisters (ic); + } + + if (IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic))) && !assignResultGenerated) + { + if (resultInF0) + emitcode ("mov", "c,F0"); + + aopOp (IC_RESULT (ic), ic, FALSE); + assignResultValue (IC_RESULT (ic), IC_LEFT (ic)); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + + if (accPushed) + emitpop ("acc"); +} + +/*-----------------------------------------------------------------*/ +/* genPcall - generates a call by pointer statement */ +/*-----------------------------------------------------------------*/ +static void +genPcall (iCode * ic) +{ + sym_link *dtype; + sym_link *etype; + bool swapBanks = FALSE; + bool resultInF0 = FALSE; + + D (emitcode (";", "genPcall")); + + dtype = operandType (IC_LEFT (ic))->next; + etype = getSpec (dtype); + /* if caller saves & we have not saved then */ + if (!ic->regsSaved) + saveRegisters (ic); + + /* if we are calling a not _naked function that is not using + the same register bank then we need to save the + destination registers on the stack */ + if (currFunc && dtype && !IFFUNC_ISNAKED (dtype) && + (FUNC_REGBANK (currFunc->type) != FUNC_REGBANK (dtype)) && !IFFUNC_ISISR (dtype)) + { + swapBanks = TRUE; + // need caution message to user here + } + + if (IS_LITERAL (etype)) + { + /* if send set is not empty then assign */ + if (_G.sendSet) + { + genSend (reverseSet (_G.sendSet)); + _G.sendSet = NULL; + } + + if (swapBanks) + { + emitcode ("mov", "psw,#0x%02x", ((FUNC_REGBANK (dtype)) << 3) & 0xff); + } + + if (IFFUNC_ISBANKEDCALL (dtype)) + { + if (IFFUNC_CALLEESAVES (dtype)) + { + werror (E_BANKED_WITH_CALLEESAVES); + } + else + { + emitcode ("mov", "r0,#%s", aopLiteralLong (OP_VALUE (IC_LEFT (ic)), 0, 1)); + emitcode ("mov", "r1,#%s", aopLiteralLong (OP_VALUE (IC_LEFT (ic)), 1, 1)); + emitcode ("mov", "r2,#%s", aopLiteralLong (OP_VALUE (IC_LEFT (ic)), 2, 1)); + emitcode ("lcall", "__sdcc_banked_call"); + } + } + else + { + emitcode ("lcall", "0x%04X", ulFromVal (OP_VALUE (IC_LEFT (ic)))); + } + } + else + { + if (IFFUNC_ISBANKEDCALL (dtype)) + { + if (IFFUNC_CALLEESAVES (dtype)) + { + werror (E_BANKED_WITH_CALLEESAVES); + } + else + { + aopOp (IC_LEFT (ic), ic, FALSE); + + emitpush (aopGet (IC_LEFT (ic), 0, FALSE, TRUE)); + emitpush (aopGet (IC_LEFT (ic), 1, FALSE, TRUE)); + emitpush (aopGet (IC_LEFT (ic), 2, FALSE, TRUE)); + + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); + + /* if send set is not empty then assign */ + if (_G.sendSet) + { + genSend (reverseSet (_G.sendSet)); + _G.sendSet = NULL; + } + + if (swapBanks) + { + char buf[8] = ""; + int reg = ((FUNC_REGBANK (dtype)) << 3) & 0xff; + emitcode ("mov", "psw,#0x%02x", reg); + SNPRINTF (buf, 8, "0x%02x", reg + 2); + emitpop (buf); + SNPRINTF (buf, 8, "0x%02x", reg + 1); + emitpop (buf); + SNPRINTF (buf, 8, "0x%02x", reg + 0); + emitpop (buf); + } + else + { + emitpop ("ar2"); + emitpop ("ar1"); + emitpop ("ar0"); + } + /* make the call */ + emitcode ("lcall", "__sdcc_banked_call"); + } + } + else if (_G.sendSet) /* the send set is not empty */ + { + symbol *callLabel = newiTempLabel (NULL); + symbol *returnLabel = newiTempLabel (NULL); + + /* create the return address on the stack */ + emitcode ("lcall", "!tlabel", labelKey2num (callLabel->key)); + emitcode ("ljmp", "!tlabel", labelKey2num (returnLabel->key)); + emitLabel (callLabel); + _G.stack.pushed += 2; + + emitDummyCall(); + /* now push the function address */ + pushSide (IC_LEFT (ic), FARPTRSIZE, ic); + + /* send set is not empty: assign */ + genSend (reverseSet (_G.sendSet)); + _G.sendSet = NULL; + + if (swapBanks) + { + emitcode ("mov", "psw,#0x%02x", ((FUNC_REGBANK (dtype)) << 3) & 0xff); + } + + /* make the call */ + emitcode ("ret", ""); + _G.stack.pushed -= 4; + emitLabel (returnLabel); + } + else /* the send set is empty */ + { + /* now get the called address into dptr */ + aopOp (IC_LEFT (ic), ic, FALSE); + + if (AOP_TYPE (IC_LEFT (ic)) == AOP_DPTR) + { + emitcode ("mov", "r0,%s", aopGet (IC_LEFT (ic), 0, FALSE, FALSE)); + emitcode ("mov", "dph,%s", aopGet (IC_LEFT (ic), 1, FALSE, FALSE)); + emitcode ("mov", "dpl,r0"); + } + else + { + emitcode ("mov", "dpl,%s", aopGet (IC_LEFT (ic), 0, FALSE, FALSE)); + emitcode ("mov", "dph,%s", aopGet (IC_LEFT (ic), 1, FALSE, FALSE)); + } + + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); + + if (swapBanks) + { + emitcode ("mov", "psw,#0x%02x", ((FUNC_REGBANK (dtype)) << 3) & 0xff); + } + + /* make the call */ + emitcode ("lcall", "__sdcc_call_dptr"); + } + } + if (swapBanks) + { + selectRegBank (FUNC_REGBANK (currFunc->type), IS_BIT (etype)); + } + + /* if we need assign a result value */ + if ((IS_ITEMP (IC_RESULT (ic)) && + !IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic))) && + (OP_SYMBOL (IC_RESULT (ic))->nRegs || OP_SYMBOL (IC_RESULT (ic))->accuse || OP_SYMBOL (IC_RESULT (ic))->spildir)) || IS_TRUE_SYMOP (IC_RESULT (ic))) + { + _G.accInUse++; + aopOp (IC_RESULT (ic), ic, FALSE); + _G.accInUse--; + + assignResultValue (IC_RESULT (ic), IC_LEFT (ic)); + + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + + /* adjust the stack for parameters if required */ + if (ic->parmBytes) + { + int i; + if (ic->parmBytes > 3) + { + if (IS_BIT (OP_SYM_ETYPE (IC_LEFT (ic))) && IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic)))) + { + emitcode ("mov", "F0,c"); + resultInF0 = TRUE; + } + + emitcode ("mov", "a,%s", spname); + emitcode ("add", "a,#0x%02x", (-ic->parmBytes) & 0xff); + emitcode ("mov", "%s,a", spname); + if (options.useXstack) + _G.stack.xpushed -= ic->parmBytes; + else + _G.stack.pushed -= ic->parmBytes; + } + else + { + for (i = 0; i < ic->parmBytes; i++) + emitcode ("dec", "%s", spname); + if (options.useXstack) + _G.stack.xpushed -= ic->parmBytes; + else + _G.stack.pushed -= ic->parmBytes; + } + } + + /* if we had saved some registers then unsave them */ + if (ic->regsSaved && !IFFUNC_CALLEESAVES (dtype)) + unsaveRegisters (ic); + + if (IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic)))) + { + if (resultInF0) + emitcode ("mov", "c,F0"); + + aopOp (IC_RESULT (ic), ic, FALSE); + assignResultValue (IC_RESULT (ic), IC_LEFT (ic)); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } +} + +/*-----------------------------------------------------------------*/ +/* resultRemat - result is rematerializable */ +/*-----------------------------------------------------------------*/ +static int +resultRemat (iCode * ic) +{ + if (SKIP_IC (ic) || ic->op == IFX) + return 0; + + if (IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic))) + { + symbol *sym = OP_SYMBOL (IC_RESULT (ic)); + if (sym->remat && !POINTER_SET (ic)) + return 1; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* genFunction - generated code for function entry */ +/*-----------------------------------------------------------------*/ +static void +genFunction (iCode * ic) +{ + symbol *sym = OP_SYMBOL (IC_LEFT (ic)); + sym_link *ftype = operandType (IC_LEFT (ic)); + bool switchedPSW = FALSE; + int calleesaves_saved_register = -1; + int stackAdjust = sym->stack; + int accIsFree = sym->recvSize < 4; + char *freereg = NULL; + iCode *ric = (ic->next && ic->next->op == RECEIVE) ? ic->next : NULL; + bool fReentrant = (IFFUNC_ISREENT (sym->type) || options.stackAuto); + + /* create the function header */ + emitcode (";", "-----------------------------------------"); + emitcode (";", " function %s", sym->name); + emitcode (";", "-----------------------------------------"); + + emitcode ("", "%s:", sym->rname); + genLine.lineCurr->isLabel = 1; + _G.currentFunc = sym; + + if (IFFUNC_ISNAKED (ftype)) + { + emitcode (";", "naked function: no prologue."); + return; + } + + /* here we need to generate the equates for the + register bank if required */ + if (FUNC_REGBANK (ftype) != rbank) + { + int i; + + rbank = FUNC_REGBANK (ftype); + for (i = 0; i < mcs51_nRegs; i++) + { + if (regs8051[i].type != REG_BIT) + { + if (EQ (regs8051[i].base, "0")) + emitcode ("", "%s !equ !constbyte", regs8051[i].dname, 8 * rbank + regs8051[i].offset); + else + emitcode ("", "%s !equ %s + !constbyte", regs8051[i].dname, regs8051[i].base, 8 * rbank + regs8051[i].offset); + } + } + } + + _G.stack.param_offset = 0; + _G.stack.offset = sym->stack; + _G.stack.xoffset = sym->xstack; + wassertl (_G.stack.pushed == 0, "stack over/underflow"); + wassertl (_G.stack.xpushed == 0, "xstack over/underflow"); + + /* if this is an interrupt service routine then + save acc, b, dpl, dph */ + if (IFFUNC_ISISR (ftype)) + { + bitVect *rsavebits; + + /* weird but possible, one should better use a different priority */ + /* if critical function then turn interrupts off */ + if (IFFUNC_ISCRITICAL (ftype)) + { + emitcode ("clr", "ea"); + } + + rsavebits = bitVectIntersect (bitVectCopy (mcs51_allBitregs ()), sym->regsUsed); + if (IFFUNC_HASFCALL (ftype) || !bitVectIsZero (rsavebits)) + { + if (!inExcludeList ("bits")) + { + emitpush ("bits"); + BitBankUsed = 1; + } + } + freeBitVect (rsavebits); + + if (!inExcludeList ("acc")) + emitpush ("acc"); + if (!inExcludeList ("b")) + emitpush ("b"); + if (!inExcludeList ("dpl")) + emitpush ("dpl"); + if (!inExcludeList ("dph")) + emitpush ("dph"); + /* if this isr has no bank i.e. is going to + run with bank 0 , then we need to save more + registers :-) */ + if (!FUNC_REGBANK (ftype)) + { + int i; + + /* if this function does not call any other + function then we can be economical and + save only those registers that are used */ + if (!IFFUNC_HASFCALL (ftype)) + { + /* if any registers used */ + if (!bitVectIsZero (sym->regsUsed)) + { + /* save the registers used */ + for (i = 0; i < sym->regsUsed->size; i++) + { + if (bitVectBitValue (sym->regsUsed, i)) + pushReg (i, TRUE); + } + } + } + else + { + /* this function has a function call. We cannot + determine register usage so we will have to push the + entire bank */ + saveRBank (0, ic, FALSE); + if (options.parms_in_bank1) + { + for (i = 0; i < 8; i++) + { + emitpush (rb1regs[i]); + } + } + } + } + else + { + /* This ISR uses a non-zero bank. + * + * We assume that the bank is available for our + * exclusive use. + * + * However, if this ISR calls a function which uses some + * other bank, we must save that bank entirely. + */ + unsigned long banksToSave = 0; + + if (IFFUNC_HASFCALL (ftype)) + { + iCode *i; + int ix; + + for (i = ic; i; i = i->next) + { + sym_link *dtype = NULL; + + if (i->op == ENDFUNCTION) + { + /* we got to the end OK. */ + break; + } + + if (i->op == CALL) + { + dtype = operandType (IC_LEFT (i)); + } + if (i->op == PCALL) + { + /* This is a mess; we have no idea what + * register bank the called function might + * use. + * + * The only thing I can think of to do is + * throw a warning and hope. + */ +// werror (W_FUNCPTR_IN_USING_ISR); + dtype = operandType (IC_LEFT (i))->next; + } + if (dtype && FUNC_REGBANK (dtype) != FUNC_REGBANK (ftype)) + { + /* Mark this bank for saving. */ + if (FUNC_REGBANK (dtype) >= MAX_REGISTER_BANKS) + { + werror (E_NO_SUCH_BANK, FUNC_REGBANK (dtype)); + } + else + { + banksToSave |= (1 << FUNC_REGBANK (dtype)); + } + + /* And note that we don't need to do it in + * genCall. + */ + i->bankSaved = 1; + } + } + + if (banksToSave && options.useXstack) + { + /* Since we aren't passing it an ic, + * saveRBank will assume r0 is available to abuse. + * + * So switch to our (trashable) bank now, so + * the caller's R0 isn't trashed. + */ + emitpush ("psw"); + emitcode ("mov", "psw,#!constbyte", (FUNC_REGBANK (sym->type) << 3) & 0x00ff); + switchedPSW = TRUE; + } + + for (ix = 0; ix < MAX_REGISTER_BANKS; ix++) + { + if (banksToSave & (1 << ix)) + { + saveRBank (ix, NULL, FALSE); + } + } + } + // TODO: this needs a closer look + SPEC_ISR_SAVED_BANKS (currFunc->etype) = banksToSave; + } + + /* Set the register bank to the desired value if nothing else */ + /* has done so yet. */ + if (!switchedPSW) + { + emitpush ("psw"); + emitcode ("mov", "psw,#!constbyte", (FUNC_REGBANK (ftype) << 3) & 0x00ff); + } + } + else + { + /* This is a non-ISR function. */ + + /* if critical function then turn interrupts off */ + if (IFFUNC_ISCRITICAL (ftype)) + { + symbol *tlbl = newiTempLabel (NULL); + emitcode ("setb", "c"); + emitcode ("jbc", "ea,!tlabel", labelKey2num (tlbl->key)); /* atomic test & clear */ + emitcode ("clr", "c"); + emitLabel (tlbl); + emitpush ("psw"); /* save old ea via c in psw */ + } + + /* The caller has already switched register banks if */ + /* necessary, so just handle the callee-saves option. */ + + /* if callee-save to be used for this function + then save the registers being used in this function */ + if (IFFUNC_CALLEESAVES (ftype)) + { + int i; + + /* if any registers used */ + if (sym->regsUsed) + { + bool bits_pushed = FALSE; + /* save the registers used */ + for (i = 0; i < sym->regsUsed->size; i++) + { + if (bitVectBitValue (sym->regsUsed, i)) + { + /* remember one saved register for later usage */ + if (calleesaves_saved_register < 0) + calleesaves_saved_register = i; + bits_pushed = pushReg (i, bits_pushed); + _G.stack.param_offset--; + } + } + } + } + } + + if (fReentrant && !options.omitFramePtr) + { + if (options.useXstack) + { + if (sym->xstack || FUNC_HASSTACKPARM (ftype)) + { + emitcode ("mov", "r0,%s", spname); + emitcode ("inc", "%s", spname); + emitcode ("xch", "a,_bpx"); + emitcode ("movx", "@r0,a"); + emitcode ("inc", "r0"); + emitcode ("mov", "a,r0"); + emitcode ("xch", "a,_bpx"); + } + if (sym->stack) + { + /* save the callers stack, but without pushed++ */ + emitcode ("push", "_bp"); + emitcode ("mov", "_bp,sp"); + } + } + else + { + if (sym->stack || FUNC_HASSTACKPARM (ftype)) + { + /* set up the stack */ + /* save the callers stack, but without pushed++ */ + emitcode ("push", "_bp"); + emitcode ("mov", "_bp,sp"); + } + } + } + + /* For some cases it is worthwhile to perform a RECEIVE iCode */ + /* before setting up the stack frame completely. */ + if (ric && ric->argreg == 1 && IC_RESULT (ric)) + { + symbol *rsym = OP_SYMBOL (IC_RESULT (ric)); + + if (rsym->isitmp) + { + if (rsym && rsym->regType == REG_CND) + rsym = NULL; + if (rsym && (rsym->accuse || rsym->ruonly)) + rsym = NULL; + if (rsym && (rsym->isspilt || rsym->nRegs == 0) && rsym->usl.spillLoc) + rsym = rsym->usl.spillLoc; + } + + /* If the RECEIVE operand immediately spills to the first entry on the */ + /* stack, we can push it directly (since sp = _bp + 1 at this point) */ + /* rather than the usual @r0/r1 machinations. */ + if (!options.useXstack && rsym && rsym->onStack && rsym->stack == 1) + { + int ofs; + + genLine.lineElement.ic = ric; + D (emitcode (";", "genReceive")); + for (ofs = 0; ofs < sym->recvSize; ofs++) + { + emitpush (fReturn[ofs]); + _G.stack.pushed--; /* cancel out pushed++ from emitpush()*/ + } + stackAdjust -= sym->recvSize; + if (stackAdjust < 0) + { + assert (stackAdjust >= 0); + stackAdjust = 0; + } + genLine.lineElement.ic = ic; + ric->generated = 1; + accIsFree = 1; + } + /* If the RECEIVE operand is 4 registers, we can do the moves now */ + /* to free up the accumulator. */ + else if (rsym && rsym->nRegs && sym->recvSize == 4) + { + int ofs; + + genLine.lineElement.ic = ric; + D (emitcode (";", "genReceive")); + for (ofs = 0; ofs < sym->recvSize; ofs++) + { + emitcode ("mov", "%s,%s", rsym->regs[ofs]->name, fReturn[ofs]); + } + genLine.lineElement.ic = ic; + ric->generated = 1; + accIsFree = 1; + } + } + + /* If the accumulator is not free, we will need another register */ + /* to clobber. No need to worry about a possible conflict with */ + /* the above early RECEIVE optimizations since they would have */ + /* freed the accumulator if they were generated. */ + if (IFFUNC_CALLEESAVES (ftype)) + { + /* if it's a callee-saves function we need a saved register */ + if (calleesaves_saved_register >= 0) + { + freereg = REG_WITH_INDEX (calleesaves_saved_register)->dname; + } + } + else + { + /* not callee-saves, we can clobber r0 */ + freereg = "r0"; + } + + /* adjust the stack for the function */ + if (stackAdjust) + { + int i = stackAdjust & 0xff; + if (stackAdjust > 256) + werror (W_STACK_OVERFLOW, sym->name); + + if (i > 3 && accIsFree) + { + emitcode ("mov", "a,sp"); + emitcode ("add", "a,#!constbyte", i & 0xff); + emitcode ("mov", "sp,a"); + } + else if (i > 4) + { + if (freereg) + { + emitcode ("xch", "a,%s", freereg); + emitcode ("mov", "a,sp"); + emitcode ("add", "a,#!constbyte", i & 0xff); + emitcode ("mov", "sp,a"); + emitcode ("xch", "a,%s", freereg); + } + else + { + /* do it the hard way */ + while (i--) + emitcode ("inc", "sp"); + } + } + else + { + while (i--) + emitcode ("inc", "sp"); + } + } + + if (sym->xstack) + { + int i = sym->xstack & 0xff; + if (sym->xstack > 256) + werror (W_STACK_OVERFLOW, sym->name); + + if (i > 3 && accIsFree) + { + emitcode ("mov", "a,_spx"); + emitcode ("add", "a,#!constbyte", i & 0xff); + emitcode ("mov", "_spx,a"); + } + else if (i > 4) + { + if (freereg) + emitcode ("xch", "a,%s", freereg); + else + emitpush ("acc"); + emitcode ("mov", "a,_spx"); + emitcode ("add", "a,#0x%02x", i & 0xff); + emitcode ("mov", "_spx,a"); + if (freereg) + emitcode ("xch", "a,%s", freereg); + else + emitpop ("acc"); + } + else + { + while (i--) + emitcode ("inc", "_spx"); + } + } + + _G.stack.param_offset = options.useXstack ? _G.stack.xpushed : _G.stack.pushed; + _G.stack.pushedregs = _G.stack.pushed; + _G.stack.xpushedregs = _G.stack.xpushed; + _G.stack.pushed = 0; + _G.stack.xpushed = 0; +} + +/*-----------------------------------------------------------------*/ +/* genEndFunction - generates epilogue for functions */ +/*-----------------------------------------------------------------*/ +static void +genEndFunction (iCode * ic) +{ + symbol *sym = OP_SYMBOL (IC_LEFT (ic)); + sym_link *ftype = operandType (IC_LEFT (ic)); + bool fReentrant = (IFFUNC_ISREENT (sym->type) || options.stackAuto); + lineNode *lineBodyEnd = genLine.lineCurr; + lineNode *linePrologueStart = NULL; + lineNode *lnp; + bitVect *regsUsed; + bitVect *regsUnneeded; + int idx; + + _G.currentFunc = NULL; + if (IFFUNC_ISNAKED (ftype)) + { + emitcode (";", "naked function: no epilogue."); + if (options.debug && currFunc) + debugFile->writeEndFunction (currFunc, ic, 0); + return; + } + + _G.stack.xpushed = _G.stack.xpushedregs; + _G.stack.pushed = _G.stack.pushedregs; + + if (fReentrant) + { + if (options.omitFramePtr) + { + bool cy_in_r0 = FALSE; + bool acc_in_r0 = FALSE; + + if (sym->stack > 3) + { + if (IS_BIT (OP_SYM_ETYPE (IC_LEFT (ic)))) + { + emitcode ("mov", "r0,psw"); /* save cy in r0 */ + cy_in_r0 = TRUE; + } + if (getSize (OP_SYM_ETYPE (IC_LEFT (ic))) >= 4) + { + emitcode ("xch", "a,r0"); /* save a in r0 */ + acc_in_r0 = TRUE; + } + + emitcode ("mov", "a,sp"); + emitcode ("add", "a,#!constbyte", (-sym->stack) & 0xff); + emitcode ("mov", "sp,a"); + } + else + { + int i = sym->stack; + while (i--) + emitcode ("dec", "sp"); + } + if (sym->xstack > 3) + { + if (IS_BIT (OP_SYM_ETYPE (IC_LEFT (ic)))) + { + if (!cy_in_r0) + emitcode ("mov", "r0,psw"); /* save cy in r0 */ + cy_in_r0 = TRUE; + } + if (getSize (OP_SYM_ETYPE (IC_LEFT (ic))) >= 4) + { + if (!acc_in_r0) + emitcode ("xch", "a,r0"); /* save a in r0 */ + acc_in_r0 = TRUE; + } + + emitcode ("mov", "a,_spx"); + emitcode ("add", "a,#!constbyte", (-sym->xstack) & 0xff); + emitcode ("mov", "_spx,a"); + } + else + { + int i = sym->xstack; + while (i--) + emitcode ("dec", "_spx"); + } + + if (acc_in_r0) + emitcode ("xch", "a,r0"); /* restore a from r0 */ + + if (cy_in_r0) + emitcode ("mov", "psw,r0"); /* restore c from r0 */ + } + else + { + if (options.useXstack) + { + if (sym->stack) + { + if (sym->stack == 1) + emitcode ("dec", "sp"); + else + emitcode ("mov", "sp,_bp"); + emitcode ("pop", "_bp"); /* without pushed-- */ + } + if (sym->xstack || FUNC_HASSTACKPARM (ftype)) + { + emitcode ("xch", "a,_bpx"); + emitcode ("mov", "r0,a"); + emitcode ("dec", "r0"); + emitcode ("movx", "a,@r0"); + emitcode ("xch", "a,_bpx"); + emitcode ("mov", "%s,r0", spname); //read before freeing stack space (interrupts) + } + } + else if (sym->stack || FUNC_HASSTACKPARM (ftype)) + { + if (sym->stack == 1) + emitcode ("dec", "sp"); + else if (sym->stack) + emitcode ("mov", "sp,_bp"); + emitcode ("pop", "_bp"); /* without pushed-- */ + } + } + } + + /* restore the register bank */ + if (IFFUNC_ISISR (ftype)) + { + if (!FUNC_REGBANK (ftype) || !options.useXstack) + { + /* Special case of ISR using non-zero bank with useXstack + * is handled below. + */ + emitpop ("psw"); + } + } + + if (IFFUNC_ISISR (ftype)) + { + bitVect *rsavebits; + + /* now we need to restore the registers */ + /* if this isr has no bank i.e. is going to + run with bank 0 , then we need to save more + registers :-) */ + if (!FUNC_REGBANK (ftype)) + { + int i; + /* if this function does not call any other + function then we can be economical and + save only those registers that are used */ + if (!IFFUNC_HASFCALL (ftype)) + { + /* if any registers used */ + if (!bitVectIsZero (sym->regsUsed)) + { + /* restore the registers used */ + for (i = sym->regsUsed->size; i >= 0; i--) + { + if (bitVectBitValue (sym->regsUsed, i)) + popReg (i, TRUE); + } + } + } + else + { + if (options.parms_in_bank1) + { + for (i = 7; i >= 0; i--) + { + emitpop (rb1regs[i]); + } + } + /* this function has a function call. We cannot + determine register usage so we will have to pop the + entire bank */ + unsaveRBank (0, ic, FALSE); + } + } + else + { + /* This ISR uses a non-zero bank. + * + * Restore any register banks saved by genFunction + * in reverse order. + */ + unsigned savedBanks = SPEC_ISR_SAVED_BANKS (currFunc->etype); + int ix; + + for (ix = MAX_REGISTER_BANKS - 1; ix >= 0; ix--) + { + if (savedBanks & (1 << ix)) + { + unsaveRBank (ix, NULL, FALSE); + } + } + + if (options.useXstack) + { + /* Restore bank AFTER calling unsaveRBank, + * since it can trash r0. + */ + emitpop ("psw"); + } + } + + if (!inExcludeList ("dph")) + emitpop ("dph"); + if (!inExcludeList ("dpl")) + emitpop ("dpl"); + if (!inExcludeList ("b")) + emitpop ("b"); + if (!inExcludeList ("acc")) + emitpop ("acc"); + + rsavebits = bitVectIntersect (bitVectCopy (mcs51_allBitregs ()), sym->regsUsed); + if (IFFUNC_HASFCALL (ftype) || !bitVectIsZero (rsavebits)) + { + if (!inExcludeList ("bits")) + emitpop ("bits"); + } + freeBitVect (rsavebits); + + /* weird but possible, one should better use a different priority */ + /* if critical function then turn interrupts off */ + if (IFFUNC_ISCRITICAL (ftype)) + { + emitcode ("setb", "ea"); + } + + /* if debug then send end of function */ + if (options.debug && currFunc) + { + debugFile->writeEndFunction (currFunc, ic, 1); + } + + emitcode ("reti", ""); + } + else + { + if (IFFUNC_CALLEESAVES (ftype)) + { + int i; + + /* if any registers used */ + if (sym->regsUsed) + { + /* save the registers used */ + for (i = sym->regsUsed->size; i >= 0; i--) + { + if (bitVectBitValue (sym->regsUsed, i) || (mcs51_ptrRegReq && (i == R0_IDX || i == R1_IDX))) + emitpop (REG_WITH_INDEX (i)->dname); + } + } + else if (mcs51_ptrRegReq) + { + emitpop (REG_WITH_INDEX (R1_IDX)->dname); + emitpop (REG_WITH_INDEX (R0_IDX)->dname); + } + } + + if (IFFUNC_ISCRITICAL (ftype)) + { + if (IS_BIT (OP_SYM_ETYPE (IC_LEFT (ic)))) + { + emitcode ("rlc", "a"); /* save c in a */ + emitpop ("psw"); /* restore ea via c in psw */ + emitcode ("mov", "ea,c"); + emitcode ("rrc", "a"); /* restore c from a */ + } + else + { + emitpop ("psw"); /* restore ea via c in psw */ + emitcode ("mov", "ea,c"); + } + } + + /* if debug then send end of function */ + if (options.debug && currFunc) + { + debugFile->writeEndFunction (currFunc, ic, 1); + } + + if (IFFUNC_ISBANKEDCALL (ftype)) + { + emitcode ("ljmp", "__sdcc_banked_ret"); + } + else + { + emitcode ("ret", ""); + } + } + + wassertl (_G.stack.pushed == 0, "stack over/underflow"); + wassertl (_G.stack.xpushed == 0, "xstack over/underflow"); + + if (!port->peep.getRegsRead || !port->peep.getRegsWritten || options.nopeep) + return; + + /* If this was an interrupt handler using bank 0 that called another */ + /* function, then all registers must be saved; nothing to optimize. */ + if (IFFUNC_ISISR (ftype) && IFFUNC_HASFCALL (ftype) && !FUNC_REGBANK (ftype)) + return; + + /* There are no push/pops to optimize if not callee-saves or ISR */ + if (!(FUNC_CALLEESAVES (ftype) || FUNC_ISISR (ftype))) + return; + + /* If there were stack parameters, we cannot optimize without also */ + /* fixing all of the stack offsets; this is too dificult to consider. */ + if (FUNC_HASSTACKPARM (ftype)) + return; + + /* Compute the registers actually used */ + regsUsed = newBitVect (mcs51_nRegs); + lnp = lineBodyEnd; + while (lnp) + { + /* Remove change of register bank if no registers used */ + if (lnp->ic && lnp->ic->op == FUNCTION && + !strncmp (lnp->line, "mov", 3) && + bitVectFirstBit (port->peep.getRegsWritten (lnp)) == CND_IDX && + !bitVectBitsInCommon (mcs51_allBankregs (), regsUsed) && + !IFFUNC_HASFCALL (ftype)) + { + emitcode (";", "eliminated unneeded mov psw,# (no regs used in bank)"); + connectLine (lnp->prev, lnp->next); + } + else + { + regsUsed = bitVectUnion (regsUsed, port->peep.getRegsWritten (lnp)); + } + + if (lnp->ic && lnp->ic->op == FUNCTION) + { + if (!lnp->prev || (lnp->prev->ic && lnp->prev->ic->op != FUNCTION)) + break; + } + lnp = lnp->prev; + } + linePrologueStart = lnp; + + /* If this was an interrupt handler that called another function */ + /* function, then assume A, B, DPH, & DPL may be modified by it. */ + if (IFFUNC_ISISR (ftype) && IFFUNC_HASFCALL (ftype)) + { + regsUsed = bitVectSetBit (regsUsed, DPL_IDX); + regsUsed = bitVectSetBit (regsUsed, DPH_IDX); + regsUsed = bitVectSetBit (regsUsed, B_IDX); + regsUsed = bitVectSetBit (regsUsed, A_IDX); + regsUsed = bitVectSetBit (regsUsed, CND_IDX); + } + + /* Remove the unneeded push/pops */ + regsUnneeded = newBitVect (mcs51_nRegs); + for (lnp = genLine.lineCurr; lnp != linePrologueStart; lnp = lnp->prev) + { + if (lnp->ic) + { + if (lnp->ic && (lnp->ic->op == FUNCTION) && !strncmp (lnp->line, "push", 4)) + { + idx = bitVectFirstBit (port->peep.getRegsRead (lnp)); + if (idx >= 0 && !bitVectBitValue (regsUsed, idx)) + { + connectLine (lnp->prev, lnp->next); + regsUnneeded = bitVectSetBit (regsUnneeded, idx); + } + } + if (lnp->ic && (lnp->ic->op == ENDFUNCTION) && !strncmp (lnp->line, "pop", 3)) + { + idx = bitVectFirstBit (port->peep.getRegsWritten (lnp)); + if (idx >= 0 && !bitVectBitValue (regsUsed, idx)) + { + connectLine (lnp->prev, lnp->next); + regsUnneeded = bitVectSetBit (regsUnneeded, idx); + } + } + } + } + + for (idx = 0; idx < regsUnneeded->size; idx++) + if (bitVectBitValue (regsUnneeded, idx)) + emitcode (";", "eliminated unneeded push/pop %s", REG_WITH_INDEX (idx)->dname); + + freeBitVect (regsUnneeded); + freeBitVect (regsUsed); +} + +/*-----------------------------------------------------------------*/ +/* genRet - generate code for return statement */ +/*-----------------------------------------------------------------*/ +static void +genRet (iCode * ic) +{ + int size, offset = 0, pushed = 0; + bool pushedA = FALSE; + + D (emitcode (";", "genRet")); + + /* if we have no return value then + just generate the "ret" */ + if (!IC_LEFT (ic)) + goto jumpret; + + /* we have something to return then + move the return value into place */ + aopOp (IC_LEFT (ic), ic, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + + if (IS_BIT (_G.currentFunc->etype)) + { + if (!IS_OP_RUONLY (IC_LEFT (ic))) + toCarry (IC_LEFT (ic)); + } + else + { + while (size--) + { + if (AOP_TYPE (IC_LEFT (ic)) == AOP_DPTR) + { + /* #NOCHANGE */ + emitpush (aopGet (IC_LEFT (ic), offset++, FALSE, TRUE)); + pushed++; + } + else + { + const char *l = aopGet (IC_LEFT (ic), offset, FALSE, FALSE); + if (!EQ (fReturn[offset], l)) + if (fReturn[offset][0] == 'r' && (AOP_TYPE (IC_LEFT (ic)) == AOP_REG || AOP_TYPE (IC_LEFT (ic)) == AOP_R0 || AOP_TYPE (IC_LEFT (ic)) == AOP_R1)) + emitcode ("mov", "a%s,%s", fReturn[offset], l); // use register's direct address instead of name + else + emitcode ("mov", "%s,%s", fReturn[offset], l); + if (size && !strcmp(fReturn[offset], "a") && aopGetUsesAcc (IC_LEFT (ic), offset+1)) + { + emitpush ("acc"); + pushedA = TRUE; + } + offset++; + } + } + if (pushedA) + { + emitpop ("acc"); + } + + while (pushed) + { + pushed--; + if (!EQ (fReturn[pushed], "a")) + emitpop (fReturn[pushed]); + else + emitpop ("acc"); + } + } + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); + +jumpret: + /* generate a jump to the return label + if the next is not the return statement */ + if (!(ic->next && ic->next->op == LABEL && IC_LABEL (ic->next) == returnLabel)) + { + emitcode ("ljmp", "!tlabel", labelKey2num (returnLabel->key)); + } +} + +/*-----------------------------------------------------------------*/ +/* genLabel - generates a label */ +/*-----------------------------------------------------------------*/ +static void +genLabel (iCode * ic) +{ + /* special case never generate */ + if (IC_LABEL (ic) == entryLabel) + return; + + emitLabel (IC_LABEL (ic)); +} + +/*-----------------------------------------------------------------*/ +/* genGoto - generates a ljmp */ +/*-----------------------------------------------------------------*/ +static void +genGoto (iCode * ic) +{ + emitcode ("ljmp", "!tlabel", labelKey2num (IC_LABEL (ic)->key)); +} + +/*-----------------------------------------------------------------*/ +/* genPlusIncr :- does addition with increment if possible */ +/*-----------------------------------------------------------------*/ +static bool +genPlusIncr (iCode * ic) +{ + unsigned int icount; + unsigned int size = getDataSize (IC_RESULT (ic)), offset; + + /* will try to generate an increment */ + /* if the right side is not a literal + we cannot */ + if (AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT) + return FALSE; + + icount = (unsigned int) ulFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit); + + D (emitcode (";", "genPlusIncr")); + + /* if increment >=16 bits in register or direct space */ + if ((AOP_TYPE (IC_LEFT (ic)) == AOP_REG || + AOP_TYPE (IC_LEFT (ic)) == AOP_DIR || + (IS_AOP_PREG (IC_LEFT (ic)) && !AOP_NEEDSACC (IC_LEFT (ic)))) && + sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))) && + !isOperandVolatile (IC_RESULT (ic), FALSE) && (size > 1) && (icount == 1)) + { + symbol *tlbl; + const char *l; + + tlbl = newiTempLabel (NULL); + l = aopGet (IC_RESULT (ic), LSB, FALSE, FALSE); + emitcode ("inc", "%s", l); + if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG || IS_AOP_PREG (IC_RESULT (ic))) + { + emitcode ("cjne", "%s,%s,!tlabel", l, zero, labelKey2num (tlbl->key)); + } + else + { + emitcode ("clr", "a"); + emitcode ("cjne", "a,%s,!tlabel", l, labelKey2num (tlbl->key)); + } + + l = aopGet (IC_RESULT (ic), MSB16, FALSE, FALSE); + emitcode ("inc", "%s", l); + + for(offset = 2; size > 2; size--, offset++) + { + if (EQ (l, "acc")) + { + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + } + else if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG || IS_AOP_PREG (IC_RESULT (ic))) + { + emitcode ("cjne", "%s,%s,!tlabel", l, zero, labelKey2num (tlbl->key)); + } + else + { + emitcode ("cjne", "a,%s,!tlabel", l, labelKey2num (tlbl->key)); + } + + l = aopGet (IC_RESULT (ic), offset, FALSE, FALSE); + emitcode ("inc", "%s", l); + } + + emitLabel (tlbl); + return TRUE; + } + + /* if result is dptr */ + if ((AOP_TYPE (IC_RESULT (ic)) == AOP_STR) && + (AOP_SIZE (IC_RESULT (ic)) == 2) && + !strncmp (AOP (IC_RESULT (ic))->aopu.aop_str[0], "dpl", 4) && !strncmp (AOP (IC_RESULT (ic))->aopu.aop_str[1], "dph", 4)) + { + if (aopGetUsesAcc (IC_LEFT (ic), 0)) + return FALSE; + + if (icount > 9) + return FALSE; + + if ((AOP_TYPE (IC_LEFT (ic)) != AOP_DIR) && (icount > 5)) + return FALSE; + + aopPut (IC_RESULT (ic), aopGet (IC_LEFT (ic), 0, FALSE, FALSE), 0); + aopPut (IC_RESULT (ic), aopGet (IC_LEFT (ic), 1, FALSE, FALSE), 1); + while (icount--) + emitcode ("inc", "dptr"); + + return TRUE; + } + + /* if the literal value of the right hand side + is greater than 4 then it is not worth it */ + if (icount > 4) + return FALSE; + + /* if the sizes are greater than 1 then we cannot */ + if (AOP_SIZE (IC_RESULT (ic)) > 1 || AOP_SIZE (IC_LEFT (ic)) > 1) + return FALSE; + + /* we can if the aops of the left & result match or + if they are in registers and the registers are the + same */ + if (sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic)))) + { + if (icount > 3) + { + MOVA (aopGet (IC_LEFT (ic), 0, FALSE, FALSE)); + emitcode ("add", "a,#!constbyte", ((char) icount) & 0xff); + aopPut (IC_RESULT (ic), "a", 0); + } + else + { + while (icount--) + { + emitcode ("inc", "%s", aopGet (IC_LEFT (ic), 0, FALSE, FALSE)); + } + } + + return TRUE; + } + + if (icount == 1) + { + MOVA (aopGet (IC_LEFT (ic), 0, FALSE, FALSE)); + emitcode ("inc", "a"); + aopPut (IC_RESULT (ic), "a", 0); + return TRUE; + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* outBitAcc - output a bit in acc */ +/*-----------------------------------------------------------------*/ +static void +outBitAcc (operand * result) +{ + symbol *tlbl = newiTempLabel (NULL); + /* if the result is a bit */ + if (AOP_TYPE (result) == AOP_CRY) + { + aopPut (result, "a", 0); + } + else + { + emitcode ("jz", "!tlabel", labelKey2num (tlbl->key)); + emitcode ("mov", "a,%s", one); + emitLabel (tlbl); + outAcc (result); + } +} + +/*-----------------------------------------------------------------*/ +/* genPlusBits - generates code for addition of two bits */ +/*-----------------------------------------------------------------*/ +static void +genPlusBits (iCode * ic) +{ + D (emitcode (";", "genPlusBits")); + + emitcode ("mov", "c,%s", AOP (IC_LEFT (ic))->aopu.aop_dir); + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY) + { + symbol *lbl = newiTempLabel (NULL); + emitcode ("jnb", "%s,!tlabel", AOP (IC_RIGHT (ic))->aopu.aop_dir, labelKey2num (lbl->key)); + emitcode ("cpl", "c"); + emitLabel (lbl); + outBitC (IC_RESULT (ic)); + } + else + { + emitcode ("clr", "a"); + emitcode ("rlc", "a"); + emitcode ("mov", "c,%s", AOP (IC_RIGHT (ic))->aopu.aop_dir); + emitcode ("addc", "a,%s", zero); + outAcc (IC_RESULT (ic)); + } +} + +static void +adjustArithmeticResult (iCode * ic) +{ + if (opIsGptr (IC_RESULT (ic))) + { + struct dbuf_s dbuf; + + if (opIsGptr (IC_LEFT (ic))) + { + if (!sameRegs (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic)))) + { + aopPut (IC_RESULT (ic), aopGet (IC_LEFT (ic), GPTRSIZE - 1, FALSE, FALSE), GPTRSIZE - 1); + } + return; + } + + if (opIsGptr (IC_RIGHT (ic))) + { + if (!sameRegs (AOP (IC_RESULT (ic)), AOP (IC_RIGHT (ic)))) + { + aopPut (IC_RESULT (ic), aopGet (IC_RIGHT (ic), GPTRSIZE - 1, FALSE, FALSE), GPTRSIZE - 1); + } + return; + } + + dbuf_init (&dbuf, 128); + if (IC_LEFT (ic) && AOP_SIZE (IC_LEFT (ic)) < GPTRSIZE && + IC_RIGHT (ic) && AOP_SIZE (IC_RIGHT (ic)) < GPTRSIZE && + !sameRegs (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic))) && !sameRegs (AOP (IC_RESULT (ic)), AOP (IC_RIGHT (ic)))) + { + dbuf_printf (&dbuf, "#0x%02x", pointerTypeToGPByte (pointerCode (getSpec (operandType (IC_LEFT (ic)))), NULL, NULL)); + aopPut (IC_RESULT (ic), dbuf_c_str (&dbuf), GPTRSIZE - 1); + } + else if (IC_LEFT (ic) && AOP_SIZE (IC_LEFT (ic)) < GPTRSIZE && !sameRegs (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic)))) + { + dbuf_printf (&dbuf, "#0x%02x", pointerTypeToGPByte (pointerCode (getSpec (operandType (IC_LEFT (ic)))), NULL, NULL)); + aopPut (IC_RESULT (ic), dbuf_c_str (&dbuf), GPTRSIZE - 1); + } + else if (IC_RIGHT (ic) && AOP_SIZE (IC_RIGHT (ic)) < GPTRSIZE && !sameRegs (AOP (IC_RESULT (ic)), AOP (IC_RIGHT (ic)))) + { + dbuf_printf (&dbuf, "#0x%02x", pointerTypeToGPByte (pointerCode (getSpec (operandType (IC_RIGHT (ic)))), NULL, NULL)); + aopPut (IC_RESULT (ic), dbuf_c_str (&dbuf), GPTRSIZE - 1); + } + dbuf_destroy (&dbuf); + } +} + +/*-----------------------------------------------------------------*/ +/* genPlus - generates code for addition */ +/*-----------------------------------------------------------------*/ +static void +genPlus (iCode * ic) +{ + int size, offset = 0; + int skip_bytes = 0; + char *add = "add"; + bool swappedLR = FALSE; + operand *leftOp, *rightOp; + operand *op; + + D (emitcode (";", "genPlus")); + + /* special cases :- */ + + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RIGHT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + + /* if literal, literal on the right or + if left requires ACC or right is already + in ACC */ + if ((AOP_TYPE (IC_LEFT (ic)) == AOP_LIT) || (AOP_NEEDSACC (IC_LEFT (ic))) || AOP_TYPE (IC_RIGHT (ic)) == AOP_ACC) + { + swapOperands (&IC_LEFT (ic), &IC_RIGHT (ic)); + swappedLR = TRUE; + } + + /* if both left & right are in bit space */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY && AOP_TYPE (IC_RIGHT (ic)) == AOP_CRY) + { + genPlusBits (ic); + goto release; + } + + /* if left in bit space & right literal */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY && AOP_TYPE (IC_RIGHT (ic)) == AOP_LIT) + { + emitcode ("mov", "c,%s", AOP (IC_LEFT (ic))->aopu.aop_dir); + /* if result in bit space */ + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY) + { + if (ulFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit) != 0L) + emitcode ("cpl", "c"); + outBitC (IC_RESULT (ic)); + } + else + { + size = getDataSize (IC_RESULT (ic)); + while (size--) + { + MOVA (aopGet (IC_RIGHT (ic), offset, FALSE, FALSE)); + emitcode ("addc", "a,%s", zero); + aopPut (IC_RESULT (ic), "a", offset++); + } + } + goto release; + } + + /* if I can do an increment instead + of add then GOOD for ME */ + if (genPlusIncr (ic) == TRUE) + goto release; + + size = getDataSize (IC_RESULT (ic)); + leftOp = IC_LEFT (ic); + rightOp = IC_RIGHT (ic); + op = IC_LEFT (ic); + + /* if this is an add for an array access + at a 256 byte boundary */ + if (2 == size + && AOP_TYPE (op) == AOP_IMMD + && IS_SYMOP (op) + && IS_SPEC (OP_SYM_ETYPE (op)) && SPEC_ABSA (OP_SYM_ETYPE (op)) && (SPEC_ADDR (OP_SYM_ETYPE (op)) & 0xff) == 0) + { + D (emitcode (";", "genPlus aligned array")); + aopPut (IC_RESULT (ic), aopGet (rightOp, 0, FALSE, FALSE), 0); + + if (1 == getDataSize (IC_RIGHT (ic))) + { + aopPut (IC_RESULT (ic), aopGet (leftOp, 1, FALSE, FALSE), 1); + } + else + { + MOVA (aopGet (IC_LEFT (ic), 1, FALSE, FALSE)); + emitcode ("add", "a,%s", aopGet (rightOp, 1, FALSE, FALSE)); + aopPut (IC_RESULT (ic), "a", 1); + } + goto release; + } + + /* if the lower bytes of a literal are zero skip the addition */ + if (AOP_TYPE (IC_RIGHT (ic)) == AOP_LIT) + { + while ((0 == ((unsigned int) ullFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit) & (0xff << skip_bytes * 8))) && + (skip_bytes + 1 < size)) + { + skip_bytes++; + } + if (skip_bytes) + D (emitcode (";", "genPlus shortcut")); + } + + while (size--) + { + if (offset >= skip_bytes) + { + if (aopGetUsesAcc (leftOp, offset) && aopGetUsesAcc (rightOp, offset)) + { + bool pushedB; + MOVA (aopGet (leftOp, offset, FALSE, FALSE)); + pushedB = pushB (); + emitcode ("xch", "a,b"); + MOVA (aopGet (rightOp, offset, FALSE, FALSE)); + emitcode (add, "a,b"); + popB (pushedB); + } + else if (aopGetUsesAcc (leftOp, offset)) + { + MOVA (aopGet (leftOp, offset, FALSE, FALSE)); + emitcode (add, "a,%s", aopGet (rightOp, offset, FALSE, FALSE)); + } + else + { + MOVA (aopGet (rightOp, offset, FALSE, FALSE)); + emitcode (add, "a,%s", aopGet (leftOp, offset, FALSE, FALSE)); + } + aopPut (IC_RESULT (ic), "a", offset); + add = "addc"; /* further adds must propagate carry */ + } + else + { + if (!sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))) || isOperandVolatile (IC_RESULT (ic), FALSE)) + { + /* just move */ + aopPut (IC_RESULT (ic), aopGet (leftOp, offset, FALSE, FALSE), offset); + } + } + offset++; + } + + adjustArithmeticResult (ic); + +release: + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + if (swappedLR) + swapOperands (&IC_LEFT (ic), &IC_RIGHT (ic)); + freeAsmop (IC_RIGHT (ic), NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genMinusDec :- does subtraction with decrement if possible */ +/*-----------------------------------------------------------------*/ +static bool +genMinusDec (iCode * ic) +{ + unsigned int icount; + unsigned int size = getDataSize (IC_RESULT (ic)); + + /* will try to generate a decrement */ + /* if the right side is not a literal + we cannot */ + if (AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT) + return FALSE; + + /* if the literal value of the right hand side + is greater than 4 then it is not worth it */ + if ((icount = (unsigned int) ullFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit)) > 4) + return FALSE; + + D (emitcode (";", "genMinusDec")); + + /* if decrement >=16 bits in register or direct space */ + if ((AOP_TYPE (IC_LEFT (ic)) == AOP_REG || + AOP_TYPE (IC_LEFT (ic)) == AOP_DIR || + (IS_AOP_PREG (IC_LEFT (ic)) && !AOP_NEEDSACC (IC_LEFT (ic)))) && + sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))) && (size > 1) && (icount == 1)) + { + symbol *tlbl; + const char *l; + + tlbl = newiTempLabel (NULL); + l = aopGet (IC_RESULT (ic), LSB, FALSE, FALSE); + emitcode ("dec", "%s", l); + + if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG || IS_AOP_PREG (IC_RESULT (ic))) + { + emitcode ("cjne", "%s,#!constbyte,!tlabel", l, 0xff, labelKey2num (tlbl->key)); + } + else + { + emitcode ("mov", "a,#!constbyte", 0xff); + emitcode ("cjne", "a,%s,!tlabel", l, labelKey2num (tlbl->key)); + } + l = aopGet (IC_RESULT (ic), MSB16, FALSE, FALSE); + emitcode ("dec", "%s", l); + if (size > 2) + { + if (EQ (l, "acc")) + { + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + } + else if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG || IS_AOP_PREG (IC_RESULT (ic))) + { + emitcode ("cjne", "%s,#!constbyte,!tlabel", l, 0xff, labelKey2num (tlbl->key)); + } + else + { + emitcode ("cjne", "a,%s,!tlabel", l, labelKey2num (tlbl->key)); + } + l = aopGet (IC_RESULT (ic), MSB24, FALSE, FALSE); + emitcode ("dec", "%s", l); + } + if (size > 3) + { + if (EQ (l, "acc")) + { + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + } + else if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG || IS_AOP_PREG (IC_RESULT (ic))) + { + emitcode ("cjne", "%s,#!constbyte,!tlabel", l, 0xff, labelKey2num (tlbl->key)); + } + else + { + emitcode ("cjne", "a,%s,!tlabel", l, labelKey2num (tlbl->key)); + } + emitcode ("dec", "%s", aopGet (IC_RESULT (ic), MSB32, FALSE, FALSE)); + } + emitLabel (tlbl); + return TRUE; + } + + /* if the sizes are greater than 1 then we cannot */ + if (AOP_SIZE (IC_RESULT (ic)) > 1 || AOP_SIZE (IC_LEFT (ic)) > 1) + return FALSE; + + /* we can if the aops of the left & result match or + if they are in registers and the registers are the + same */ + if (sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic)))) + { + const char *l; + + if (aopGetUsesAcc (IC_LEFT (ic), 0)) + { + MOVA (aopGet (IC_RESULT (ic), 0, FALSE, FALSE)); + l = "a"; + } + else + { + l = aopGet (IC_RESULT (ic), 0, FALSE, FALSE); + } + + while (icount--) + { + emitcode ("dec", "%s", l); + } + + if (AOP_NEEDSACC (IC_RESULT (ic))) + aopPut (IC_RESULT (ic), "a", 0); + + return TRUE; + } + + if (icount == 1) + { + MOVA (aopGet (IC_LEFT (ic), 0, FALSE, FALSE)); + emitcode ("dec", "a"); + aopPut (IC_RESULT (ic), "a", 0); + return TRUE; + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* addSign - complete with sign */ +/*-----------------------------------------------------------------*/ +static void +addSign (operand * result, int offset, int sign) +{ + int size = (getDataSize (result) - offset); + if (size > 0) + { + if (sign) + { + emitcode ("rlc", "a"); + emitcode ("subb", "a,acc"); + while (size--) + { + aopPut (result, "a", offset++); + } + } + else + { + while (size--) + { + aopPut (result, zero, offset++); + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* genMinusBits - generates code for subtraction of two bits */ +/*-----------------------------------------------------------------*/ +static void +genMinusBits (iCode * ic) +{ + symbol *lbl = newiTempLabel (NULL); + + D (emitcode (";", "genMinusBits")); + + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY) + { + emitcode ("mov", "c,%s", AOP (IC_LEFT (ic))->aopu.aop_dir); + emitcode ("jnb", "%s,!tlabel", AOP (IC_RIGHT (ic))->aopu.aop_dir, labelKey2num (lbl->key)); + emitcode ("cpl", "c"); + emitLabel (lbl); + outBitC (IC_RESULT (ic)); + } + else + { + emitcode ("mov", "c,%s", AOP (IC_RIGHT (ic))->aopu.aop_dir); + emitcode ("subb", "a,acc"); + emitcode ("jnb", "%s,!tlabel", AOP (IC_LEFT (ic))->aopu.aop_dir, labelKey2num (lbl->key)); + emitcode ("inc", "a"); + emitLabel (lbl); + aopPut (IC_RESULT (ic), "a", 0); + addSign (IC_RESULT (ic), MSB16, SPEC_USIGN (getSpec (operandType (IC_RESULT (ic))))); + } +} + +/*-----------------------------------------------------------------*/ +/* genMinus - generates code for subtraction */ +/*-----------------------------------------------------------------*/ +static void +genMinus (iCode * ic) +{ + int size, offset = 0; + + D (emitcode (";", "genMinus")); + + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RIGHT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + + /* special cases :- */ + /* if both left & right are in bit space */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY && AOP_TYPE (IC_RIGHT (ic)) == AOP_CRY) + { + genMinusBits (ic); + goto release; + } + + /* if I can do a decrement instead + of subtract then GOOD for ME */ + if (genMinusDec (ic) == TRUE) + goto release; + + size = getDataSize (IC_RESULT (ic)); + + /* if literal, add a,#-lit, else normal subb */ + if (AOP_TYPE (IC_RIGHT (ic)) == AOP_LIT) + { + unsigned long long lit = 0L; + bool useCarry = FALSE; + + lit = ullFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit); + lit = -(long long) lit; + + while (size--) + { + if (useCarry || ((lit >> (offset * 8)) & 0x0ffll)) + { + MOVA (aopGet (IC_LEFT (ic), offset, FALSE, FALSE)); + if (!offset && !size && lit == (unsigned long long) - 1) + { + emitcode ("dec", "a"); + } + else if (!useCarry) + { + /* first add without previous c */ + emitcode ("add", "a,#!constbyte", (unsigned int) ((lit >> (offset * 8)) & 0x0ffll)); + useCarry = TRUE; + } + else + { + emitcode ("addc", "a,#!constbyte", (unsigned int) ((lit >> (offset * 8)) & 0x0ffll)); + } + aopPut (IC_RESULT (ic), "a", offset++); + } + else + { + /* no need to add zeroes */ + if (!sameRegs (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic)))) + { + aopPut (IC_RESULT (ic), aopGet (IC_LEFT (ic), offset, FALSE, FALSE), offset); + } + offset++; + } + } + } + else + { + operand *leftOp, *rightOp; + + leftOp = IC_LEFT (ic); + rightOp = IC_RIGHT (ic); + + while (size--) + { + if (aopGetUsesAcc (rightOp, offset)) + { + if (aopGetUsesAcc (leftOp, offset)) + { + bool pushedB; + + MOVA (aopGet (rightOp, offset, FALSE, FALSE)); + pushedB = pushB (); + emitcode ("mov", "b,a"); + if (offset == 0) + CLRC; + MOVA (aopGet (leftOp, offset, FALSE, FALSE)); + emitcode ("subb", "a,b"); + popB (pushedB); + } + else + { + /* reverse subtraction with 2's complement */ + if (offset == 0) + emitcode ("setb", "c"); + else + emitcode ("cpl", "c"); + wassertl (!aopGetUsesAcc (leftOp, offset), "accumulator clash"); + MOVA (aopGet (rightOp, offset, FALSE, FALSE)); + emitcode ("subb", "a,%s", aopGet (leftOp, offset, FALSE, FALSE)); + emitcode ("cpl", "a"); + if (size) /* skip if last byte */ + emitcode ("cpl", "c"); + } + } + else + { + MOVA (aopGet (leftOp, offset, FALSE, FALSE)); + if (offset == 0) + CLRC; + emitcode ("subb", "a,%s", aopGet (rightOp, offset, FALSE, FALSE)); + } + + aopPut (IC_RESULT (ic), "a", offset++); + } + } + + adjustArithmeticResult (ic); + +release: + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + freeAsmop (IC_RIGHT (ic), NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + + +/*-----------------------------------------------------------------*/ +/* genMultbits :- multiplication of bits */ +/*-----------------------------------------------------------------*/ +static void +genMultbits (operand * left, operand * right, operand * result) +{ + D (emitcode (";", "genMultbits")); + + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("anl", "c,%s", AOP (right)->aopu.aop_dir); + outBitC (result); +} + +/*-----------------------------------------------------------------*/ +/* genMultOneByte : 8*8=8/16 bit multiplication */ +/*-----------------------------------------------------------------*/ +static void +genMultOneByte (operand * left, operand * right, operand * result) +{ + symbol *lbl; + int size = AOP_SIZE (result); + bool runtimeSign, compiletimeSign; + bool lUnsigned, rUnsigned, pushedB; + + D (emitcode (";", "genMultOneByte")); + + if (size < 1 || size > 2) + { + /* this should never happen */ + fprintf (stderr, "size!=1||2 (%d) in %s at line:%d \n", AOP_SIZE (result), __FILE__, lineno); + exit (EXIT_FAILURE); + } + + /* (if two literals: the value is computed before) */ + /* if one literal, literal on the right */ + if (AOP_TYPE (left) == AOP_LIT || AOP_TYPE (right) == AOP_ACC) + { + operand *t = right; + right = left; + left = t; + /* emitcode (";", "swapped left and right"); */ + } + /* if no literal, unsigned on the right: shorter code */ + if (AOP_TYPE (right) != AOP_LIT && SPEC_USIGN (getSpec (operandType (left)))) + { + operand *t = right; + right = left; + left = t; + } + + lUnsigned = SPEC_USIGN (getSpec (operandType (left))); + rUnsigned = SPEC_USIGN (getSpec (operandType (right))); + + pushedB = pushB (); + + if (size == 1 /* no, this is not a bug; with a 1 byte result there's + no need to take care about the signedness! */ + || (lUnsigned && rUnsigned)) + { + /* just an unsigned 8 * 8 = 8 multiply + or 8u * 8u = 16u */ + /* emitcode (";","unsigned"); */ + /* TODO: check for accumulator clash between left & right aops? */ + + /*if (AOP_TYPE (right) == AOP_ACC) + MOVB (aopGet (left, 0, FALSE, FALSE)); + else*/ if (AOP_TYPE (right) == AOP_LIT) + { + /* moving to accumulator first helps peepholes */ + MOVA (aopGet (left, 0, FALSE, FALSE)); + MOVB (aopGet (right, 0, FALSE, FALSE)); + } + else + { + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE)); + MOVA (aopGet (left, 0, FALSE, FALSE)); + } + + emitcode ("mul", "ab"); + aopPut (result, "a", 0); + if (size == 2) + aopPut (result, "b", 1); + + popB (pushedB); + return; + } + + /* we have to do a signed multiply */ + /* emitcode (";", "signed"); */ + + /* now sign adjust for both left & right */ + + /* let's see what's needed: */ + /* apply negative sign during runtime */ + runtimeSign = FALSE; + /* negative sign from literals */ + compiletimeSign = FALSE; + + if (!lUnsigned) + { + if (AOP_TYPE (left) == AOP_LIT) + { + /* signed literal */ + signed char val = (char) ulFromVal (AOP (left)->aopu.aop_lit); + if (val < 0) + compiletimeSign = TRUE; + } + else + /* signed but not literal */ + runtimeSign = TRUE; + } + + if (!rUnsigned) + { + if (AOP_TYPE (right) == AOP_LIT) + { + /* signed literal */ + signed char val = (char) ulFromVal (AOP (right)->aopu.aop_lit); + if (val < 0) + compiletimeSign ^= TRUE; + } + else + /* signed but not literal */ + runtimeSign = TRUE; + } + + /* initialize F0, which stores the runtime sign */ + if (runtimeSign) + { + if (compiletimeSign) + emitcode ("setb", "F0"); /* set sign flag */ + else + emitcode ("clr", "F0"); /* reset sign flag */ + } + + /* save the signs of the operands */ + if (AOP_TYPE (right) == AOP_LIT) + { + signed char val = (char) ulFromVal (AOP (right)->aopu.aop_lit); + + if (!rUnsigned && val < 0) + emitcode ("mov", "b,#!constbyte", -val); + else + emitcode ("mov", "b,#!constbyte", (unsigned char) val); + } + else /* ! literal */ + { + if (rUnsigned) /* emitcode (";", "signed"); */ + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE)); + else + { + MOVA (aopGet (right, 0, FALSE, FALSE)); + lbl = newiTempLabel (NULL); + emitcode ("jnb", "acc.7,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "F0"); /* complement sign flag */ + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + emitcode ("mov", "b,a"); + } + } + + if (AOP_TYPE (left) == AOP_LIT) + { + signed char val = (char) ulFromVal (AOP (left)->aopu.aop_lit); + + if (!lUnsigned && val < 0) + emitcode ("mov", "a,#!constbyte", -val); + else + emitcode ("mov", "a,#!constbyte", (unsigned char) val); + } + else /* ! literal */ + { + MOVA (aopGet (left, 0, FALSE, FALSE)); + + if (!lUnsigned) + { + lbl = newiTempLabel (NULL); + emitcode ("jnb", "acc.7,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "F0"); /* complement sign flag */ + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + } + } + + /* now the multiplication */ + emitcode ("mul", "ab"); + if (runtimeSign || compiletimeSign) + { + lbl = newiTempLabel (NULL); + if (runtimeSign) + emitcode ("jnb", "F0,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "a"); /* lsb 2's complement */ + if (size != 2) + emitcode ("inc", "a"); /* inc doesn't set carry flag */ + else + { + emitcode ("add", "a,#0x01"); /* this sets carry flag */ + emitcode ("xch", "a,b"); + emitcode ("cpl", "a"); /* msb 2's complement */ + emitcode ("addc", "a,#0x00"); + emitcode ("xch", "a,b"); + } + emitLabel (lbl); + } + aopPut (result, "a", 0); + if (size == 2) + aopPut (result, "b", 1); + + popB (pushedB); +} + +/*-----------------------------------------------------------------*/ +/* genMult - generates code for multiplication */ +/*-----------------------------------------------------------------*/ +static void +genMult (iCode * ic) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + operand *result = IC_RESULT (ic); + + D (emitcode (";", "genMult")); + + /* assign the asmops */ + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + /* special cases first */ + /* both are bits */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + genMultbits (left, right, result); + goto release; + } + + /* if both are of size == 1 */ +#if 0 // one of them can be a sloc shared with the result + if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1) +#else + if (getSize (operandType (left)) == 1 && getSize (operandType (right)) == 1) +#endif + { + genMultOneByte (left, right, result); + goto release; + } + + /* should have been converted to function call */ + fprintf (stderr, "left: %d right: %d\n", getSize (OP_SYMBOL (left)->type), getSize (OP_SYMBOL (right)->type)); + assert (0); + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genDivbits :- division of bits */ +/*-----------------------------------------------------------------*/ +static void +genDivbits (operand * left, operand * right, operand * result) +{ + bool pushedB; + + D (emitcode (";", "genDivbits")); + + pushedB = pushB (); + + /* the result must be bit */ + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE)); + + MOVA (aopGet (left, 0, FALSE, FALSE)); + + emitcode ("div", "ab"); + emitcode ("rrc", "a"); + + popB (pushedB); + + aopPut (result, "c", 0); +} + +/*-----------------------------------------------------------------*/ +/* genDivOneByte : 8 bit division */ +/*-----------------------------------------------------------------*/ +static void +genDivOneByte (operand * left, operand * right, operand * result) +{ + bool lUnsigned, rUnsigned, pushedB; + bool runtimeSign, compiletimeSign; + bool accuse = FALSE; + bool pushedA = FALSE; + symbol *lbl; + int size, offset; + + D (emitcode (";", "genDivOneByte")); + + /* Why is it necessary that genDivOneByte() can return an int result? + Have a look at: + + volatile unsigned char uc; + volatile signed char sc1, sc2; + volatile int i; + + uc = 255; + sc1 = -1; + i = uc / sc1; + + Or: + + sc1 = -128; + sc2 = -1; + i = sc1 / sc2; + + In all cases a one byte result would overflow, the following cast to int + would return the wrong result. + + Two possible solution: + a) cast operands to int, if ((unsigned) / (signed)) or + ((signed) / (signed)) + b) return an 16 bit signed int; this is what we're doing here! + */ + + size = AOP_SIZE (result) - 1; + offset = 1; + lUnsigned = SPEC_USIGN (getSpec (operandType (left))); + rUnsigned = SPEC_USIGN (getSpec (operandType (right))); + + pushedB = pushB (); + + /* signed or unsigned */ + if (lUnsigned && rUnsigned) + { + /* unsigned is easy */ + MOVB (aopGet (right, 0, FALSE, FALSE)); + MOVA (aopGet (left, 0, FALSE, FALSE)); + emitcode ("div", "ab"); + aopPut (result, "a", 0); + while (size--) + aopPut (result, zero, offset++); + + popB (pushedB); + return; + } + + /* signed is a little bit more difficult */ + + /* now sign adjust for both left & right */ + + /* let's see what's needed: */ + /* apply negative sign during runtime */ + runtimeSign = FALSE; + /* negative sign from literals */ + compiletimeSign = FALSE; + + if (!lUnsigned) + { + if (AOP_TYPE (left) == AOP_LIT) + { + /* signed literal */ + signed char val = (char) ulFromVal (AOP (left)->aopu.aop_lit); + if (val < 0) + compiletimeSign = TRUE; + } + else + /* signed but not literal */ + runtimeSign = TRUE; + } + + if (!rUnsigned) + { + if (AOP_TYPE (right) == AOP_LIT) + { + /* signed literal */ + signed char val = (char) ulFromVal (AOP (right)->aopu.aop_lit); + if (val < 0) + compiletimeSign ^= TRUE; + } + else + /* signed but not literal */ + runtimeSign = TRUE; + } + + /* initialize F0, which stores the runtime sign */ + if (runtimeSign) + { + if (compiletimeSign) + emitcode ("setb", "F0"); /* set sign flag */ + else + emitcode ("clr", "F0"); /* reset sign flag */ + } + + /* save the signs of the operands */ + if (AOP_TYPE (right) == AOP_LIT) + { + signed char val = (char) ulFromVal (AOP (right)->aopu.aop_lit); + + if (!rUnsigned && val < 0) + emitcode ("mov", "b,#0x%02x", -val); + else + emitcode ("mov", "b,#0x%02x", (unsigned char) val); + } + else /* ! literal */ + { + if (rUnsigned) + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE)); + else + { + MOVA (aopGet (right, 0, FALSE, FALSE)); + lbl = newiTempLabel (NULL); + emitcode ("jnb", "acc.7,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "F0"); /* complement sign flag */ + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + emitcode ("mov", "b,a"); + } + } + + if (AOP_TYPE (left) == AOP_LIT) + { + signed char val = (char) ulFromVal (AOP (left)->aopu.aop_lit); + + if (!lUnsigned && val < 0) + emitcode ("mov", "a,#0x%02x", -val); + else + emitcode ("mov", "a,#0x%02x", (unsigned char) val); + } + else /* ! literal */ + { + MOVA (aopGet (left, 0, FALSE, FALSE)); + + if (!lUnsigned) + { + lbl = newiTempLabel (NULL); + emitcode ("jnb", "acc.7,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "F0"); /* complement sign flag */ + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + } + } + + /* now the division */ + emitcode ("div", "ab"); + + if (runtimeSign || compiletimeSign) + { + lbl = newiTempLabel (NULL); + if (runtimeSign) + emitcode ("jnb", "F0,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "a"); /* lsb 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + + accuse = aopPut (result, "a", 0); + if (size > 0) + { + /* msb is 0x00 or 0xff depending on the sign */ + if (runtimeSign) + { + if (accuse) + { + emitpush ("acc"); + pushedA = TRUE; + } + emitcode ("mov", "c,F0"); + emitcode ("subb", "a,acc"); + while (size--) + aopPut (result, "a", offset++); + } + else /* compiletimeSign */ + { + if (aopPutUsesAcc (result, "#0xff", offset)) + { + emitpush ("acc"); + pushedA = TRUE; + } + while (size--) + aopPut (result, "#0xff", offset++); + } + } + } + else + { + aopPut (result, "a", 0); + while (size--) + aopPut (result, zero, offset++); + } + + if (pushedA) + emitpop ("acc"); + popB (pushedB); +} + +/*-----------------------------------------------------------------*/ +/* genDiv - generates code for division */ +/*-----------------------------------------------------------------*/ +static void +genDiv (iCode * ic) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + operand *result = IC_RESULT (ic); + + D (emitcode (";", "genDiv")); + + /* assign the asmops */ + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + /* special cases first */ + /* both are bits */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + genDivbits (left, right, result); + goto release; + } + + /* if both are of size == 1 */ + if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1) + { + genDivOneByte (left, right, result); + goto release; + } + + /* should have been converted to function call */ + assert (0); +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genModbits :- modulus of bits */ +/*-----------------------------------------------------------------*/ +static void +genModbits (operand * left, operand * right, operand * result) +{ + bool pushedB; + + D (emitcode (";", "genModbits")); + + pushedB = pushB (); + + /* the result must be bit */ + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE)); + + MOVA (aopGet (left, 0, FALSE, FALSE)); + + emitcode ("div", "ab"); + emitcode ("mov", "a,b"); + emitcode ("rrc", "a"); + + popB (pushedB); + + aopPut (result, "c", 0); +} + +/*-----------------------------------------------------------------*/ +/* genModOneByte : 8 bit modulus */ +/*-----------------------------------------------------------------*/ +static void +genModOneByte (operand * left, operand * right, operand * result) +{ + bool lUnsigned, rUnsigned, pushedB; + bool runtimeSign, compiletimeSign; + symbol *lbl; + int size, offset; + + D (emitcode (";", "genModOneByte")); + + size = AOP_SIZE (result) - 1; + offset = 1; + lUnsigned = SPEC_USIGN (getSpec (operandType (left))); + rUnsigned = SPEC_USIGN (getSpec (operandType (right))); + + /* if right is a literal, check it for 2^n */ + if (AOP_TYPE (right) == AOP_LIT) + { + unsigned char val = abs ((int) operandLitValue (right)); + symbol *lbl2 = NULL; + + switch (val) + { + case 1: /* sometimes it makes sense (on tricky code and hardware)... */ + case 2: + case 4: + case 8: + case 16: + case 32: + case 64: + case 128: + if (lUnsigned) + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "modulus of unsigned char by 2^n literal shouldn't be processed here"); + /* because iCode should have been changed to genAnd */ + /* see file "SDCCopt.c", function "convertToFcall()" */ + + MOVA (aopGet (left, 0, FALSE, FALSE)); + emitcode ("mov", "c,acc.7"); + emitcode ("anl", "a,#0x%02x", val - 1); + lbl = newiTempLabel (NULL); + emitcode ("jz", "!tlabel", labelKey2num (lbl->key)); + emitcode ("jnc", "!tlabel", labelKey2num (lbl->key)); + emitcode ("orl", "a,#0x%02x", 0xff ^ (val - 1)); + if (size) + { + int size2 = size; + int offs2 = offset; + + aopPut (result, "a", 0); + while (size2--) + aopPut (result, "#0xff", offs2++); + lbl2 = newiTempLabel (NULL); + emitcode ("sjmp", "!tlabel", labelKey2num (lbl2->key)); + } + emitLabel (lbl); + aopPut (result, "a", 0); + while (size--) + aopPut (result, zero, offset++); + if (lbl2) + { + emitLabel (lbl2); + } + return; + + default: + break; + } + } + + pushedB = pushB (); + + /* signed or unsigned */ + if (lUnsigned && rUnsigned) + { + /* unsigned is easy */ + MOVB (aopGet (right, 0, FALSE, FALSE)); + MOVA (aopGet (left, 0, FALSE, FALSE)); + emitcode ("div", "ab"); + aopPut (result, "b", 0); + while (size--) + aopPut (result, zero, offset++); + + popB (pushedB); + return; + } + + /* signed is a little bit more difficult */ + + /* now sign adjust for both left & right */ + + /* modulus: sign of the right operand has no influence on the result! */ + if (AOP_TYPE (right) == AOP_LIT) + { + signed char val = (signed char) operandLitValue (right); + + if (!rUnsigned && val < 0) + emitcode ("mov", "b,#0x%02x", -val); + else + emitcode ("mov", "b,#0x%02x", (unsigned char) val); + } + else /* not literal */ + { + if (rUnsigned) + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE)); + else + { + MOVA (aopGet (right, 0, FALSE, FALSE)); + lbl = newiTempLabel (NULL); + emitcode ("jnb", "acc.7,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + emitcode ("mov", "b,a"); + } + } + + /* let's see what's needed: */ + /* apply negative sign during runtime */ + runtimeSign = FALSE; + /* negative sign from literals */ + compiletimeSign = FALSE; + + /* sign adjust left side */ + if (AOP_TYPE (left) == AOP_LIT) + { + signed char val = (char) ulFromVal (AOP (left)->aopu.aop_lit); + + if (!lUnsigned && val < 0) + { + compiletimeSign = TRUE; /* set sign flag */ + emitcode ("mov", "a,#0x%02x", -val); + } + else + emitcode ("mov", "a,#0x%02x", (unsigned char) val); + } + else /* ! literal */ + { + MOVA (aopGet (left, 0, FALSE, FALSE)); + + if (!lUnsigned) + { + runtimeSign = TRUE; + emitcode ("clr", "F0"); /* clear sign flag */ + + lbl = newiTempLabel (NULL); + emitcode ("jnb", "acc.7,!tlabel", labelKey2num (lbl->key)); + emitcode ("setb", "F0"); /* set sign flag */ + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + } + } + + /* now the modulus */ + emitcode ("div", "ab"); + + if (runtimeSign || compiletimeSign) + { + emitcode ("mov", "a,b"); + lbl = newiTempLabel (NULL); + if (runtimeSign) + emitcode ("jnb", "F0,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + + aopPut (result, "a", 0); + if (size > 0) + { + /* msb is 0x00 or 0xff depending on the sign */ + if (runtimeSign) + { + emitcode ("mov", "c,F0"); + emitcode ("subb", "a,acc"); + while (size--) + aopPut (result, "a", offset++); + } + else /* compiletimeSign */ + while (size--) + aopPut (result, "#0xff", offset++); + } + } + else + { + aopPut (result, "b", 0); + while (size--) + aopPut (result, zero, offset++); + } + + popB (pushedB); +} + +/*-----------------------------------------------------------------*/ +/* genMod - generates code for division */ +/*-----------------------------------------------------------------*/ +static void +genMod (iCode * ic) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + operand *result = IC_RESULT (ic); + + D (emitcode (";", "genMod")); + + /* assign the asmops */ + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + /* special cases first */ + /* both are bits */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + genModbits (left, right, result); + goto release; + } + + /* if both are of size == 1 */ + if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1) + { + genModOneByte (left, right, result); + goto release; + } + + /* should have been converted to function call */ + assert (0); + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genIfxJump :- will create a jump depending on the ifx */ +/*-----------------------------------------------------------------*/ +static void +genIfxJump (iCode * ic, const char *jval, operand * left, operand * right, operand * result, iCode * popIc) +{ + symbol *jlbl; + symbol *tlbl = newiTempLabel (NULL); + char *inst; + + /* if there is something to be popped then do it first */ + popForBranch (popIc, TRUE); + + D (emitcode (";", "genIfxJump")); + + /* if true label then we jump if condition + supplied is true */ + if (IC_TRUE (ic)) + { + jlbl = IC_TRUE (ic); + inst = ((EQ (jval, "a") ? "jz" : (EQ (jval, "c") ? "jnc" : "jnb"))); + } + else + { + /* false label is present */ + jlbl = IC_FALSE (ic); + inst = ((EQ (jval, "a") ? "jnz" : (EQ (jval, "c") ? "jc" : "jb"))); + } + if (EQ (inst, "jb") || EQ (inst, "jnb")) + emitcode (inst, "%s,!tlabel", jval, labelKey2num (tlbl->key)); + else + emitcode (inst, "!tlabel", labelKey2num (tlbl->key)); + freeForBranchAsmops (result, right, left, ic); + emitcode ("ljmp", "!tlabel", labelKey2num (jlbl->key)); + emitLabel (tlbl); + + /* mark the icode as generated */ + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genCmp :- greater or less than comparison */ +/*-----------------------------------------------------------------*/ +static void +genCmp (operand * left, operand * right, operand * result, iCode * ifx, int sign, iCode * ic) +{ + int size, offset = 0; + unsigned long long lit = 0L; + bool rightInB; + + D (emitcode (";", "genCmp")); + + /* if left & right are bit variables */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + emitcode ("mov", "c,%s", AOP (right)->aopu.aop_dir); + emitcode ("anl", "c,%s", AOP (left)->aopu.aop_dir); + } + /* generic pointers require special handling since all NULL pointers must compare equal */ + else if (opIsGptr (left) || opIsGptr (right)) + { + /* push right */ + while (offset < GPTRSIZE) + { + emitpush (aopGet (right, offset++, FALSE, TRUE)); + } + loadDptrFromOperand (left, TRUE); + emitcode ("lcall", "___gptr_cmp"); + for (offset = 0; offset < GPTRSIZE; offset++) + emitpop (NULL); + } + else + { + /* subtract right from left if at the + end the carry flag is set then we know that + left is greater than right */ + size = max (AOP_SIZE (left), AOP_SIZE (right)); + + /* if unsigned char cmp with lit, do cjne left,#right,zz */ + if (size == 1 && !sign && AOP_TYPE (right) == AOP_LIT && AOP_TYPE (left) != AOP_DIR && AOP_TYPE (left) != AOP_STR) + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, FALSE)); + symbol *lbl = newiTempLabel (NULL); + emitcode ("cjne", "%s,%s,!tlabel", l, aopGet (right, offset, FALSE, FALSE), labelKey2num (lbl->key)); + Safe_free (l); + emitLabel (lbl); + } + else + { + if (AOP_TYPE (right) == AOP_LIT) + { + lit = ullFromVal (AOP (right)->aopu.aop_lit); + /* optimize if(x < 0) or if(x >= 0) */ + if (lit == 0ll) + { + if (!sign) + { + CLRC; + } + else + { + MOVA (aopGet (left, AOP_SIZE (left) - 1, FALSE, FALSE)); + if (!(AOP_TYPE (result) == AOP_CRY && AOP_SIZE (result)) && ifx) + { + genIfxJump (ifx, "acc.7", left, right, result, ic->next); + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); + + return; + } + else + { + emitcode ("rlc", "a"); + } + } + goto release; + } + else + { + //nonzero literal + int bytelit = ((lit >> (offset * 8)) & 0x0ffll); + while (size && (bytelit == 0)) + { + offset++; + bytelit = ((lit >> (offset * 8)) & 0x0ffll); + size--; + } + CLRC; + while (size--) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + if (sign && size == 0) + { + emitcode ("xrl", "a,#0x80"); + emitcode ("subb", "a,#0x%02x", 0x80 ^ (unsigned int) ((lit >> (offset * 8)) & 0x0ffll)); + } + else + { + emitcode ("subb", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + offset++; + } + goto release; + } + } + CLRC; + while (size--) + { + bool pushedB = FALSE; + rightInB = aopGetUsesAcc (right, offset); + if (rightInB) + { + pushedB = pushB (); + emitcode ("mov", "b,%s", aopGet (right, offset, FALSE, FALSE)); + } + MOVA (aopGet (left, offset, FALSE, FALSE)); + if (sign && size == 0) + { + emitcode ("xrl", "a,#0x80"); + if (!rightInB) + { + pushedB = pushB (); + rightInB++; + MOVB (aopGet (right, offset, FALSE, FALSE)); + } + emitcode ("xrl", "b,#0x80"); + emitcode ("subb", "a,b"); + } + else + { + if (rightInB) + emitcode ("subb", "a,b"); + else + emitcode ("subb", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + if (rightInB) + popB (pushedB); + offset++; + } + } + } + +release: + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + if (AOP_TYPE (result) == AOP_CRY && AOP_SIZE (result)) + { + outBitC (result); + } + else + { + /* if the result is used in the next + ifx conditional branch then generate + code a little differently */ + if (ifx) + { + genIfxJump (ifx, "c", NULL, NULL, result, ic->next); + } + else + { + outBitC (result); + } + /* leave the result in acc */ + } +} + +/*-----------------------------------------------------------------*/ +/* genCmpGt :- greater than comparison */ +/*-----------------------------------------------------------------*/ +static void +genCmpGt (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + sym_link *letype, *retype; + int sign = 0; + + D (emitcode (";", "genCmpGt")); + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + if (IS_SPEC (operandType (left)) && IS_SPEC (operandType (right))) + { + letype = getSpec (operandType (left)); + retype = getSpec (operandType (right)); + sign = !((SPEC_USIGN (letype) && !(IS_CHAR (letype) && IS_LITERAL (letype))) || + (SPEC_USIGN (retype) && !(IS_CHAR (retype) && IS_LITERAL (retype)))); + } + /* assign the asmops */ + aopOp (result, ic, TRUE); + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + + genCmp (right, left, result, ifx, sign, ic); + + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genCmpLt - less than comparisons */ +/*-----------------------------------------------------------------*/ +static void +genCmpLt (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + sym_link *letype, *retype; + int sign = 0; + + D (emitcode (";", "genCmpLt")); + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + if (IS_SPEC (operandType (left)) && IS_SPEC (operandType (right))) + { + letype = getSpec (operandType (left)); + retype = getSpec (operandType (right)); + sign = !((SPEC_USIGN (letype) && !(IS_CHAR (letype) && IS_LITERAL (letype))) || + (SPEC_USIGN (retype) && !(IS_CHAR (retype) && IS_LITERAL (retype)))); + } + /* assign the asmops */ + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + genCmp (left, right, result, ifx, sign, ic); + + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* gencjneshort - compare and jump if not equal */ +/*-----------------------------------------------------------------*/ +static void +gencjneshort (operand * left, operand * right, symbol * lbl) +{ + int size = max (AOP_SIZE (left), AOP_SIZE (right)); + int offset = 0; + + D (emitcode (";", "gencjneshort")); + + /* if the left side is a immediate/literal or + if the right is in a pointer register and left is not */ + if (IS_AOP_IMMEDIATE (left) || + (AOP_TYPE (left) == AOP_DIR && !IS_AOP_IMMEDIATE (right)) || + (IS_AOP_PREG (right) && !IS_AOP_PREG (left))) + { + operand *t = right; + right = left; + left = t; + } + + /* generic pointers require special handling since all NULL pointers must compare equal */ + if (opIsGptr (left) || opIsGptr (right)) + { + /* push right */ + while (offset < size) + { + emitpush (aopGet (right, offset++, FALSE, TRUE)); + } + loadDptrFromOperand (left, TRUE); + emitcode ("lcall", "___gptr_cmp"); + for (offset = 0; offset < GPTRSIZE; offset++) + emitpop (NULL); + emitcode ("jnz", "!tlabel", labelKey2num (lbl->key)); + } + + /* if the right side is a literal then anything goes */ + else if (IS_AOP_IMMEDIATE (right) && + AOP_TYPE (left) != AOP_DIR && !IS_AOP_IMMEDIATE (left)) + { + while (size--) + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, FALSE)); + const char *r = aopGet (right, offset, FALSE, FALSE); + if (EQ (l, "a") && EQ (r, zero)) + emitcode ("jnz", "!tlabel", labelKey2num (lbl->key)); + else + emitcode ("cjne", "%s,%s,!tlabel", l, r, labelKey2num (lbl->key)); + Safe_free (l); + offset++; + } + } + + /* if the right side is in a register or in direct space or + if the left is a pointer register & right is not */ + else if (AOP_TYPE (right) == AOP_REG || + AOP_TYPE (right) == AOP_DIR || + IS_AOP_IMMEDIATE (right) || + (IS_AOP_PREG (left) && !IS_AOP_PREG (right))) + { + if (AOP_TYPE (right) == AOP_LIT) + { + int val[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + bool chk[8] = {TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE}; + int rsize = AOP_SIZE (right); + int pidx = -1; + int lidx, cidx; + unsigned long long lit = ullFromVal (AOP (right)->aopu.aop_lit); + + switch (rsize) + { + case 8: + val[7] = (lit >> 56) & 0xff; + chk[7] = TRUE; + val[6] = (lit >> 48) & 0xff; + chk[6] = TRUE; + val[5] = (lit >> 40) & 0xff; + chk[5] = TRUE; + val[4] = (lit >> 32) & 0xff; + chk[4] = TRUE; + //fallthrough + case 4: + val[3] = (lit >> 24) & 0xff; + chk[3] = TRUE; + //fallthrough + case 3: + val[2] = (lit >> 16) & 0xff; + chk[2] = TRUE; + //fallthrough + case 2: + val[1] = (lit >> 8) & 0xff; + chk[1] = TRUE; + //fallthrough + default: + val[0] = (lit >> 0) & 0xff; + chk[0] = TRUE; + } + if (optimize.codeSize && (rsize > 1)) + { + if ((!chk[0] || val[0] == 0x00) && + (!chk[1] || val[1] == 0x00) && + (!chk[2] || val[2] == 0x00) && + (!chk[3] || val[3] == 0x00) && + (!chk[4] || val[4] == 0x00) && + (!chk[5] || val[5] == 0x00) && + (!chk[6] || val[6] == 0x00) && + (!chk[7] || val[7] == 0x00)) + { + MOVA(aopGet(left, 0, FALSE, FALSE)); + for (cidx = 1; cidx < size; cidx++) + if (chk[cidx]) + emitcode ("orl", "a,%s", aopGet(left, cidx, FALSE, FALSE)); + emitcode ("jnz", "%05d$", lbl->key + 100); + return; + } + if ((!chk[0] || val[0] == 0xFF) && + (!chk[1] || val[1] == 0xFF) && + (!chk[2] || val[2] == 0xFF) && + (!chk[3] || val[3] == 0xFF) && + (!chk[4] || val[4] == 0xFF) && + (!chk[5] || val[5] == 0xFF) && + (!chk[6] || val[6] == 0xFF) && + (!chk[7] || val[7] == 0xFF)) + { + MOVA(aopGet(left, 0, FALSE, FALSE)); + for (cidx = 1; cidx < size; cidx++) + if (chk[cidx]) + emitcode ("anl", "a,%s", aopGet(left, cidx, FALSE, FALSE)); + emitcode ("cjne", "a,#0xFF,%05d$", lbl->key + 100); + return; + } + } + + for (lidx = 0; lidx < size; lidx++) + { + if (chk[lidx] && !aopGetUsesAcc(left, lidx)) + { + if (pidx >= 0) + { + if (val[pidx] == val[lidx]) + { + chk[lidx] = FALSE; + } + if ((~val[pidx] & 0xff) == val[lidx]) + { + emitcode ("cpl", "a"); + chk[lidx] = FALSE; + } + else if (((val[pidx] + 1) & 0xff) == val[lidx]) + { + emitcode ("inc", "a"); + chk[lidx] = FALSE; + } + else if (((val[pidx] - 1) & 0xff) == val[lidx]) + { + emitcode ("dec", "a"); + chk[lidx] = FALSE; + } + } + pidx = lidx; + if (chk[lidx]) + { + MOVA(aopGet(right, lidx, FALSE, FALSE)); + chk[lidx] = FALSE; + } + emitcode ("cjne", "a,%s,%05d$", aopGet(left, lidx, FALSE, TRUE), lbl->key + 100); + + for (cidx = lidx + 1; cidx < size; cidx++) + { + if (chk[cidx] && val[lidx] == val[cidx] && !IS_AOP_PREG (left)) + { + chk[cidx] = FALSE; + emitcode ("cjne", "a,%s,%05d$", aopGet(left, cidx, FALSE, TRUE), lbl->key + 100); + } + } + } + } + return; + } + + while (size--) + { + const char *r; + MOVA (aopGet (left, offset, FALSE, FALSE)); + r = aopGet (right, offset, FALSE, TRUE); + if (EQ (r, zero)) + emitcode ("jnz", "!tlabel", labelKey2num (lbl->key)); + else + emitcode ("cjne", "a,%s,!tlabel", r, labelKey2num (lbl->key)); + offset++; + } + } + else + { + /* right is a pointer reg need both a & b */ + while (size--) + { + //if B in use: push B; mov B,left; mov A,right; clrc; subb A,B; pop B; jnz + wassertl (!BINUSE, "B was in use"); + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("cjne", "a,b,!tlabel", labelKey2num (lbl->key)); + offset++; + } + } +} + +/*-----------------------------------------------------------------*/ +/* gencjne - compare and jump if not equal */ +/*-----------------------------------------------------------------*/ +static void +gencjne (operand * left, operand * right, symbol * lbl, bool useCarry) +{ + symbol *tlbl = newiTempLabel (NULL); + + D (emitcode (";", "gencjne")); + + gencjneshort (left, right, lbl); + + if (useCarry) + SETC; + else + MOVA (one); + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl->key)); + emitLabel (lbl); + if (useCarry) + CLRC; + else + MOVA (zero); + emitLabel (tlbl); +} + +/*-----------------------------------------------------------------*/ +/* genCmpEq - generates code for equal to */ +/*-----------------------------------------------------------------*/ +static void +genCmpEq (iCode * ic, iCode * ifx) +{ + bool swappedLR = FALSE; + operand *left, *right, *result; + iCode *popIc = ic->next; + + D (emitcode (";", "genCmpEq")); + + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE); + + /* if literal, literal on the right or + if the right is in a pointer register and left + is not */ + if ((AOP_TYPE (left) == AOP_LIT) || (IS_AOP_PREG (right) && !IS_AOP_PREG (left))) + { + swapOperands (&left, &right); + swappedLR = TRUE; + } + + if (ifx && !AOP_SIZE (result)) + { + symbol *tlbl; + /* if they are both bit variables */ + if (AOP_TYPE (left) == AOP_CRY && ((AOP_TYPE (right) == AOP_CRY) || (AOP_TYPE (right) == AOP_LIT))) + { + if (AOP_TYPE (right) == AOP_LIT) + { + unsigned long lit = ulFromVal (AOP (right)->aopu.aop_lit); + if (lit == 0L) + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("cpl", "c"); + } + else if (lit == 1L) + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + emitcode ("clr", "c"); + } + /* AOP_TYPE(right) == AOP_CRY */ + } + else + { + symbol *lbl = newiTempLabel (NULL); + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("jb", "%s,!tlabel", AOP (right)->aopu.aop_dir, labelKey2num ((lbl->key))); + emitcode ("cpl", "c"); + emitLabel (lbl); + } + /* if true label then we jump if condition + supplied is true */ + tlbl = newiTempLabel (NULL); + if (IC_TRUE (ifx)) + { + emitcode ("jnc", "!tlabel", labelKey2num (tlbl->key)); + freeForBranchAsmops (result, right, left, ic); + popForBranch (popIc, FALSE); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_TRUE (ifx)->key)); + } + else + { + emitcode ("jc", "!tlabel", labelKey2num (tlbl->key)); + freeForBranchAsmops (result, right, left, ic); + popForBranch (popIc, FALSE); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_FALSE (ifx)->key)); + } + emitLabel (tlbl); + } + else + { + tlbl = newiTempLabel (NULL); + gencjneshort (left, right, tlbl); + if (IC_TRUE (ifx)) + { + freeForBranchAsmops (result, right, left, ic); + popForBranch (popIc, FALSE); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_TRUE (ifx)->key)); + emitLabel (tlbl); + } + else + { + symbol *lbl = newiTempLabel (NULL); + emitcode ("sjmp", "!tlabel", labelKey2num (lbl->key)); + emitLabel (tlbl); + freeForBranchAsmops (result, right, left, ic); + popForBranch (popIc, FALSE); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_FALSE (ifx)->key)); + emitLabel (lbl); + } + } + /* mark the icode as generated */ + ifx->generated = 1; + goto release; + } + + /* if they are both bit variables */ + if (AOP_TYPE (left) == AOP_CRY && ((AOP_TYPE (right) == AOP_CRY) || (AOP_TYPE (right) == AOP_LIT))) + { + if (AOP_TYPE (right) == AOP_LIT) + { + unsigned long lit = ulFromVal (AOP (right)->aopu.aop_lit); + if (lit == 0L) + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("cpl", "c"); + } + else if (lit == 1L) + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + emitcode ("clr", "c"); + } + /* AOP_TYPE(right) == AOP_CRY */ + } + else + { + symbol *lbl = newiTempLabel (NULL); + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("jb", "%s,!tlabel", AOP (right)->aopu.aop_dir, labelKey2num (lbl->key)); + emitcode ("cpl", "c"); + emitLabel (lbl); + } + /* c = 1 if egal */ + if (AOP_TYPE (result) == AOP_CRY && AOP_SIZE (result)) + { + outBitC (result); + goto release; + } + if (ifx) + { + genIfxJump (ifx, "c", left, right, result, popIc); + goto release; + } + /* if the result is used in an arithmetic operation + then put the result in place */ + outBitC (result); + } + else + { + bool useCarry = (AOP_TYPE (result) == AOP_CRY); + gencjne (left, right, newiTempLabel (NULL), useCarry); + if (useCarry && AOP_SIZE (result)) + { + aopPut (result, "c", 0); + goto release; + } + if (ifx) + { + genIfxJump (ifx, useCarry ? "c" : "a", left, right, result, popIc); + goto release; + } + /* if the result is used in an arithmetic operation + then put the result in place */ + if (!useCarry) + outAcc (result); + /* leave the result in acc */ + } + +release: + freeAsmop (result, NULL, ic, TRUE); + if (swappedLR) + swapOperands (&left, &right); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* hasInc - operand is incremented before any other use */ +/*-----------------------------------------------------------------*/ +static iCode * +hasInc (operand * op, iCode * ic, int osize) +{ + sym_link *type = operandType (op); + sym_link *retype = getSpec (type); + iCode *lic = ic->next; + int isize; + + /* this could from a cast, e.g.: "(char xdata *) 0x7654;" */ + if (!IS_SYMOP (op)) + return NULL; + + if (IS_BITVAR (retype) || !IS_PTR (type)) + return NULL; + if (IS_AGGREGATE (type->next)) + return NULL; + if (osize != (isize = getSize (type->next))) + return NULL; + + while (lic) + { + /* if operand of the form op = op + <sizeof *op> */ + if (lic->op == '+' && isOperandEqual (IC_LEFT (lic), op) && + isOperandEqual (IC_RESULT (lic), op) && + isOperandLiteral (IC_RIGHT (lic)) && operandLitValue (IC_RIGHT (lic)) == isize) + { + return lic; + } + /* if the operand used or deffed */ + if (bitVectBitValue (OP_USES (op), lic->key) || lic->defKey == op->key) + { + return NULL; + } + /* if GOTO or IFX */ + if (lic->op == IFX || lic->op == GOTO || lic->op == LABEL) + break; + lic = lic->next; + } + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* genAndOp - for && operation */ +/*-----------------------------------------------------------------*/ +static void +genAndOp (iCode * ic) +{ + operand *left, *right, *result; + symbol *tlbl; + + D (emitcode (";", "genAndOp")); + + /* note here that && operations that are in an + if statement are taken away by backPatchLabels + only those used in arthmetic operations remain */ + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, FALSE); + + /* if both are bit variables */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("anl", "c,%s", AOP (right)->aopu.aop_dir); + outBitC (result); + } + else + { + tlbl = newiTempLabel (NULL); + toBoolean (left); + emitcode ("jz", "!tlabel", labelKey2num (tlbl->key)); + toBoolean (right); + emitLabel (tlbl); + outBitAcc (result); + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + + +/*-----------------------------------------------------------------*/ +/* genOrOp - for || operation */ +/*-----------------------------------------------------------------*/ +static void +genOrOp (iCode * ic) +{ + operand *left, *right, *result; + symbol *tlbl; + + D (emitcode (";", "genOrOp")); + + /* note here that || operations that are in an + if statement are taken away by backPatchLabels + only those used in arthmetic operations remain */ + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, FALSE); + + /* if both are bit variables */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("orl", "c,%s", AOP (right)->aopu.aop_dir); + outBitC (result); + } + else + { + tlbl = newiTempLabel (NULL); + toBoolean (left); + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + toBoolean (right); + emitLabel (tlbl); + outBitAcc (result); + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* isLiteralBit - test if lit == 2^n */ +/*-----------------------------------------------------------------*/ +static int +isLiteralBit (unsigned long long lit) +{ + unsigned long long w = 1; + int idx; + + for (idx = 0; idx < 64; idx++, w<<=1) + if (lit == w) + return idx + 1; + return 0; +} + +/*-----------------------------------------------------------------*/ +/* continueIfTrue - */ +/*-----------------------------------------------------------------*/ +static void +continueIfTrue (iCode * ic, iCode * popIc) +{ + if (IC_TRUE (ic)) + { + popForBranch (popIc, TRUE); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_TRUE (ic)->key)); + } + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* jmpIfTrue - */ +/*-----------------------------------------------------------------*/ +static void +jumpIfTrue (iCode * ic, iCode * popIc) +{ + if (!IC_TRUE (ic)) + { + popForBranch (popIc, TRUE); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_FALSE (ic)->key)); + } + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* jmpTrueOrFalse - */ +/*-----------------------------------------------------------------*/ +static void +jmpTrueOrFalse (iCode * ic, symbol * tlbl, operand * left, operand * right, operand * result, iCode * popIc) +{ + // ugly but optimized by peephole + if (IC_TRUE (ic)) + { + symbol *nlbl = newiTempLabel (NULL); + emitcode ("sjmp", "!tlabel", labelKey2num (nlbl->key)); + emitLabel (tlbl); + popForBranch (popIc, FALSE); + freeForBranchAsmops (result, right, left, ic); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_TRUE (ic)->key)); + emitLabel (nlbl); + } + else + { + popForBranch (popIc, FALSE); + freeForBranchAsmops (result, right, left, ic); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_FALSE (ic)->key)); + emitLabel (tlbl); + } + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genAnd - code for and */ +/*-----------------------------------------------------------------*/ +static void +genAnd (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + int size, offset = 0; + unsigned long long lit = 0ull; + int bytelit = 0; + + D (emitcode (";", "genAnd")); + + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE); + +#ifdef DEBUG_TYPE + emitcode (";", "Type res[%d] = l[%d]&r[%d]", AOP_TYPE (result), AOP_TYPE (left), AOP_TYPE (right)); + emitcode (";", "Size res[%d] = l[%d]&r[%d]", AOP_SIZE (result), AOP_SIZE (left), AOP_SIZE (right)); +#endif + + /* if left is a literal & right is not then exchange them */ + if ((AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) || AOP_NEEDSACC (left)) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if result = right then exchange left and right */ + if (sameRegs (AOP (result), AOP (right))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if right is bit then exchange them */ + if (AOP_TYPE (right) == AOP_CRY && AOP_TYPE (left) != AOP_CRY) + { + operand *tmp = right; + right = left; + left = tmp; + } + if (AOP_TYPE (right) == AOP_LIT) + { + lit = ullFromVal (AOP (right)->aopu.aop_lit); + } + + size = AOP_SIZE (result); + + // if(bit & yy) + // result = bit & yy; + if (AOP_TYPE (left) == AOP_CRY) + { + if (AOP_TYPE (right) == AOP_LIT) + { + // c = bit & literal; + if (lit & 1) + { + if (size && sameRegs (AOP (result), AOP (left))) + // no change + goto release; + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + // bit(result) = 0; + if (size && (AOP_TYPE (result) == AOP_CRY)) + { + emitcode ("clr", "%s", AOP (result)->aopu.aop_dir); + goto release; + } + if ((AOP_TYPE (result) == AOP_CRY) && ifx) + { + jumpIfTrue (ifx, ic->next); + goto release; + } + emitcode ("clr", "c"); + } + } + else + { + if (AOP_TYPE (right) == AOP_CRY) + { + // c = bit & bit; + if (IS_OP_ACCUSE (left)) + { + emitcode ("anl", "c,%s", AOP (right)->aopu.aop_dir); + } + else if (IS_OP_ACCUSE (right)) + { + emitcode ("anl", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + emitcode ("mov", "c,%s", AOP (right)->aopu.aop_dir); + emitcode ("anl", "c,%s", AOP (left)->aopu.aop_dir); + } + } + else if (AOP_TYPE (right) == AOP_DIR && IS_BOOL (operandType (right)) && AOP_TYPE (left) == AOP_CRY) + { + MOVA (aopGet (right, 0, FALSE, FALSE)); + emitcode ("anl", "c,acc.0"); + } + else + { + // c = bit & val; + MOVA (aopGet (right, 0, FALSE, FALSE)); + // c = lsb + emitcode ("rrc", "a"); + emitcode ("anl", "c,%s", AOP (left)->aopu.aop_dir); + } + } + // bit = c + // val = c + if (size) + outBitC (result); + // if(bit & ...) + else if ((AOP_TYPE (result) == AOP_CRY) && ifx) + genIfxJump (ifx, "c", left, right, result, ic->next); + goto release; + } + + // if(val & 0xZZ) - size = 0, ifx != FALSE - + // bit = val & 0xZZ - size = 1, ifx = FALSE - + if ((AOP_TYPE (right) == AOP_LIT) && (AOP_TYPE (result) == AOP_CRY) && (AOP_TYPE (left) != AOP_CRY)) + { + int posbit = isLiteralBit (lit); + /* left & 2^n */ + if (posbit) + { + posbit--; + MOVA (aopGet (left, posbit >> 3, FALSE, FALSE)); + // bit = left & 2^n + if (size) + { + switch (posbit & 0x07) + { + case 0: + emitcode ("rrc", "a"); + break; + case 7: + emitcode ("rlc", "a"); + break; + default: + emitcode ("mov", "c,acc.%d", posbit & 0x07); + break; + } + } + // if(left & 2^n) + else + { + if (ifx) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "acc.%d", posbit & 0x07); + genIfxJump (ifx, dbuf_c_str (&dbuf), left, right, result, ic->next); + dbuf_destroy (&dbuf); + } + else + { + emitcode ("anl", "a,#!constbyte", 1 << (posbit & 0x07)); + } + goto release; + } + } + else + { + symbol *tlbl = newiTempLabel (NULL); + int sizel = AOP_SIZE (left); + if (size) + emitcode ("setb", "c"); + while (sizel--) + { + if ((bytelit = ((lit >> (offset * 8)) & 0x0FFL)) != 0x0L) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + // byte == 2^n ? + if ((posbit = isLiteralBit (bytelit)) != 0) + emitcode ("jb", "acc.%d,!tlabel", (posbit - 1) & 0x07, labelKey2num (tlbl->key)); + else + { + if (bytelit != 0x0FFL) + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, TRUE)); + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + } + } + offset++; + } + // bit = left & literal + if (size) + { + emitcode ("clr", "c"); + emitLabel (tlbl); + } + // if(left & literal) + else + { + if (ifx) + jmpTrueOrFalse (ifx, tlbl, left, right, result, ic->next); + else + emitLabel (tlbl); + goto release; + } + } + outBitC (result); + goto release; + } + + /* if left is same as result */ + if (sameRegs (AOP (result), AOP (left))) + { + for (; size--; offset++) + { + if (AOP_TYPE (right) == AOP_LIT) + { + bytelit = (int) ((lit >> (offset * 8)) & 0x0ffull); + if (bytelit == 0x0FF) + { + /* dummy read of volatile operand */ + if (isOperandVolatile (left, FALSE)) + MOVA (aopGet (left, offset, FALSE, FALSE)); + else + continue; + } + else if (bytelit == 0) + { + aopPut (result, zero, offset); + } + else if (IS_AOP_PREG (result)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + aopPut (result, "a", offset); + } + else if (AOP_TYPE (left) != AOP_DPTR) + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, TRUE)); + emitcode ("anl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + Safe_free (l); + } + else + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, TRUE)); + emitcode ("anl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + Safe_free (l); + aopPut (result, "a", offset); + } + } + else + { + if (AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + if (offset) + emitcode ("mov", "b,a"); + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,b"); + aopPut (result, "a", offset); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + aopPut (result, "a", offset); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + if (IS_AOP_PREG (result)) + { + emitcode ("anl", "a,%s", aopGet (left, offset, FALSE, TRUE)); + aopPut (result, "a", offset); + } + else + { + emitcode ("anl", "%s,a", aopGet (left, offset, FALSE, TRUE)); + } + } + } + } + } + else + { + // left & result in different registers + if (AOP_TYPE (result) == AOP_CRY) + { + // result = bit + // if(size), result in bit + // if(!size && ifx), conditional oper: if(left & right) + symbol *tlbl = newiTempLabel (NULL); + int sizer = min (AOP_SIZE (left), AOP_SIZE (right)); + if (size) + emitcode ("setb", "c"); + while (sizer--) + { + if ((AOP_TYPE (right) == AOP_REG || IS_AOP_PREG (right) || AOP_TYPE (right) == AOP_DIR) + && AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else if (AOP_TYPE (left) == AOP_ACC) + { + if (!offset) + { + //B contains high byte of left + emitpush ("b"); + emitcode ("mov", "b,a"); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,b"); + emitpop ("b"); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,b"); + } + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,b"); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,%s", aopGet (left, offset, FALSE, FALSE)); + } + + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + offset++; + } + if (size) + { + CLRC; + emitLabel (tlbl); + outBitC (result); + } + else if (ifx) + jmpTrueOrFalse (ifx, tlbl, left, right, result, ic->next); + else + emitLabel (tlbl); + } + else + { + for (; (size--); offset++) + { + // normal case + // result = left & right + if (AOP_TYPE (right) == AOP_LIT) + { + bytelit = (int) ((lit >> (offset * 8)) & 0x0ffull); + if (bytelit == 0x0FF) + { + aopPut (result, aopGet (left, offset, FALSE, FALSE), offset); + continue; + } + else if (bytelit == 0) + { + /* dummy read of volatile operand */ + if (isOperandVolatile (left, FALSE)) + MOVA (aopGet (left, offset, FALSE, FALSE)); + aopPut (result, zero, offset); + continue; + } + else if (AOP_TYPE (left) == AOP_ACC) + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, FALSE)); + emitcode ("anl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + aopPut (result, l, offset); + Safe_free (l); + continue; + } + } + // faster than result <- left, anl result,right + // and better if result is SFR + if ((AOP_TYPE (right) == AOP_REG || IS_AOP_PREG (right) || AOP_TYPE (right) == AOP_DIR) + && AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else if (AOP_TYPE (left) == AOP_ACC) + { + if (!offset) + { + //B contains high byte of left + emitpush ("b"); + emitcode ("mov", "b,a"); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,b"); + emitpop ("b"); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,b"); + } + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,b"); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,%s", aopGet (left, offset, FALSE, FALSE)); + } + aopPut (result, "a", offset); + } + } + } + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genOr - code for or */ +/*-----------------------------------------------------------------*/ +static void +genOr (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + int size, offset = 0; + unsigned long lit = 0L; + int bytelit = 0; + + D (emitcode (";", "genOr")); + + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE); + +#ifdef DEBUG_TYPE + emitcode (";", "Type res[%d] = l[%d]&r[%d]", AOP_TYPE (result), AOP_TYPE (left), AOP_TYPE (right)); + emitcode (";", "Size res[%d] = l[%d]&r[%d]", AOP_SIZE (result), AOP_SIZE (left), AOP_SIZE (right)); +#endif + + /* if left is a literal & right is not then exchange them */ + if ((AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) || AOP_NEEDSACC (left)) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if result = right then exchange left and right */ + if (sameRegs (AOP (result), AOP (right))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if right is bit then exchange them */ + if (AOP_TYPE (right) == AOP_CRY && AOP_TYPE (left) != AOP_CRY) + { + operand *tmp = right; + right = left; + left = tmp; + } + if (AOP_TYPE (right) == AOP_LIT) + { + lit = ulFromVal (AOP (right)->aopu.aop_lit); + } + + size = AOP_SIZE (result); + + // if(bit | yy) + // xx = bit | yy; + if (AOP_TYPE (left) == AOP_CRY) + { + if (AOP_TYPE (right) == AOP_LIT) + { + // c = bit | literal; + if (lit) + { + // lit != 0 => result = 1 + if (AOP_TYPE (result) == AOP_CRY) + { + if (size) + emitcode ("setb", "%s", AOP (result)->aopu.aop_dir); + else if (ifx) + continueIfTrue (ifx, ic->next); + goto release; + } + emitcode ("setb", "c"); + } + else + { + // lit == 0 => result = left + if (size && sameRegs (AOP (result), AOP (left))) + goto release; + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + } + else + { + if (AOP_TYPE (right) == AOP_CRY) + { + // c = bit | bit; + if (IS_OP_ACCUSE (left)) + { + emitcode ("orl", "c,%s", AOP (right)->aopu.aop_dir); + } + else if (IS_OP_ACCUSE (right)) + { + emitcode ("orl", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + emitcode ("mov", "c,%s", AOP (right)->aopu.aop_dir); + emitcode ("orl", "c,%s", AOP (left)->aopu.aop_dir); + } + } + else + { + // c = bit | val; + if ((AOP_TYPE (result) == AOP_CRY) && ifx) + { + symbol *tlbl = newiTempLabel (NULL); + emitcode ("jb", "%s,!tlabel", AOP (left)->aopu.aop_dir, labelKey2num (tlbl->key)); + toBoolean (right); + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + jmpTrueOrFalse (ifx, tlbl, left, right, result, ic->next); + goto release; + } + else + { + toCarry (right); + emitcode ("orl", "c,%s", AOP (left)->aopu.aop_dir); + } + } + } + // bit = c + // val = c + if (size) + outBitC (result); + // if(bit | ...) + else if ((AOP_TYPE (result) == AOP_CRY) && ifx) + genIfxJump (ifx, "c", left, right, result, ic->next); + goto release; + } + + // if(val | 0xZZ) - size = 0, ifx != FALSE - + // bit = val | 0xZZ - size = 1, ifx = FALSE - + if ((AOP_TYPE (right) == AOP_LIT) && (AOP_TYPE (result) == AOP_CRY) && (AOP_TYPE (left) != AOP_CRY)) + { + if (lit) + { + // result = 1 + if (size) + emitcode ("setb", "%s", AOP (result)->aopu.aop_dir); + else if (ifx) + continueIfTrue (ifx, ic->next); + goto release; + } + else + { + // lit = 0, result = boolean(left) + if (size) + SETC; + toBoolean (left); + if (size) + { + symbol *tlbl = newiTempLabel (NULL); + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + CLRC; + emitLabel (tlbl); + } + else + { + /* FIXME, thats pretty fishy, check for ifx!=0, testcase .. */ + assert (ifx); + genIfxJump (ifx, "a", left, right, result, ic->next); + goto release; + } + } + outBitC (result); + goto release; + } + + /* if left is same as result */ + if (sameRegs (AOP (result), AOP (left))) + { + for (; size--; offset++) + { + if (AOP_TYPE (right) == AOP_LIT) + { + bytelit = (int) ((lit >> (offset * 8)) & 0x0FFL); + if (bytelit == 0) + { + /* dummy read of volatile operand */ + if (isOperandVolatile (left, FALSE)) + MOVA (aopGet (left, offset, FALSE, FALSE)); + else + continue; + } + else if (bytelit == 0x0FF) + { + /* dummy read of volatile operand */ + if (isOperandVolatile (left, FALSE)) + MOVA (aopGet (left, offset, FALSE, FALSE)); + aopPut (result, "#0xff", offset); + } + else if (IS_AOP_PREG (left)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + aopPut (result, "a", offset); + } + else if (AOP_TYPE (left) != AOP_DPTR) + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, TRUE)); + emitcode ("orl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + Safe_free (l); + } + else + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, TRUE)); + emitcode ("orl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + Safe_free (l); + aopPut (result, "a", offset); + } + } + else + { + if (AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + if (offset) + emitcode ("mov", "b,a"); + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,b"); + aopPut (result, "a", offset); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + aopPut (result, "a", offset); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + if (IS_AOP_PREG (left)) + { + emitcode ("orl", "a,%s", aopGet (left, offset, FALSE, TRUE)); + aopPut (result, "a", offset); + } + else + { + emitcode ("orl", "%s,a", aopGet (left, offset, FALSE, TRUE)); + } + } + } + } + } + else + { + // left & result in different registers + if (AOP_TYPE (result) == AOP_CRY) + { + // result = bit + // if(size), result in bit + // if(!size && ifx), conditional oper: if(left | right) + symbol *tlbl = newiTempLabel (NULL); + int sizer = max (AOP_SIZE (left), AOP_SIZE (right)); + if (size) + emitcode ("setb", "c"); + while (sizer--) + { + if ((AOP_TYPE (right) == AOP_REG || IS_AOP_PREG (right) || AOP_TYPE (right) == AOP_DIR) + && AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else if (AOP_TYPE (left) == AOP_ACC) + { + if (!offset) + { + //B contains high byte of left + emitpush ("b"); + emitcode ("mov", "b,a"); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,b"); + emitpop ("b"); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,b"); + } + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,b"); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,%s", aopGet (left, offset, FALSE, FALSE)); + } + + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + offset++; + } + if (size) + { + CLRC; + emitLabel (tlbl); + outBitC (result); + } + else if (ifx) + jmpTrueOrFalse (ifx, tlbl, left, right, result, ic->next); + else + emitLabel (tlbl); + } + else + { + for (; (size--); offset++) + { + // normal case + // result = left | right + if (AOP_TYPE (right) == AOP_LIT) + { + bytelit = (int) ((lit >> (offset * 8)) & 0x0FFL); + if (bytelit == 0) + { + aopPut (result, aopGet (left, offset, FALSE, FALSE), offset); + continue; + } + else if (bytelit == 0x0FF) + { + /* dummy read of volatile operand */ + if (isOperandVolatile (left, FALSE)) + MOVA (aopGet (left, offset, FALSE, FALSE)); + aopPut (result, "#0xff", offset); + continue; + } + else if (AOP_TYPE (left) == AOP_ACC) + { + // this should be the only use of left so A,B can be overwritten + char *l = Safe_strdup (aopGet (left, offset, FALSE, FALSE)); + emitcode ("orl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + aopPut (result, l, offset); + Safe_free (l); + continue; + } + } + // faster than result <- left, orl result,right + // and better if result is SFR + if ((AOP_TYPE (right) == AOP_REG || IS_AOP_PREG (right) || AOP_TYPE (right) == AOP_DIR) + && AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else if (AOP_TYPE (left) == AOP_ACC) + { + if (!offset) + { + //B contains high byte of left + emitpush ("b"); + emitcode ("mov", "b,a"); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,b"); + emitpop ("b"); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,b"); + } + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,b"); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,%s", aopGet (left, offset, FALSE, FALSE)); + } + aopPut (result, "a", offset); + } + } + } + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genXor - code for xclusive or */ +/*-----------------------------------------------------------------*/ +static void +genXor (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + int size, offset = 0; + unsigned long lit = 0L; + int bytelit = 0; + + D (emitcode (";", "genXor")); + + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE); + +#ifdef DEBUG_TYPE + emitcode (";", "Type res[%d] = l[%d]&r[%d]", AOP_TYPE (result), AOP_TYPE (left), AOP_TYPE (right)); + emitcode (";", "Size res[%d] = l[%d]&r[%d]", AOP_SIZE (result), AOP_SIZE (left), AOP_SIZE (right)); +#endif + + /* if left is a literal & right is not || + if left needs acc & right does not */ + if ((AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) || (AOP_NEEDSACC (left) && !AOP_NEEDSACC (right))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if result = right then exchange left and right */ + if (sameRegs (AOP (result), AOP (right))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if right is bit then exchange them */ + if (AOP_TYPE (right) == AOP_CRY && AOP_TYPE (left) != AOP_CRY) + { + operand *tmp = right; + right = left; + left = tmp; + } + if (AOP_TYPE (right) == AOP_LIT) + { + lit = ulFromVal (AOP (right)->aopu.aop_lit); + } + + size = AOP_SIZE (result); + + // if(bit ^ yy) + // xx = bit ^ yy; + if (AOP_TYPE (left) == AOP_CRY) + { + if (AOP_TYPE (right) == AOP_LIT) + { + // c = bit ^ literal; + if (lit >> 1) + { + // lit>>1 != 0 => result = 1 + if (AOP_TYPE (result) == AOP_CRY) + { + if (size) + emitcode ("setb", "%s", AOP (result)->aopu.aop_dir); + else if (ifx) + continueIfTrue (ifx, ic->next); + goto release; + } + emitcode ("setb", "c"); + } + else + { + // lit == (0 or 1) + if (lit == 0) + { + // lit == 0, result = left + if (size && sameRegs (AOP (result), AOP (left))) + goto release; + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + // lit == 1, result = not(left) + if (size && sameRegs (AOP (result), AOP (left))) + { + emitcode ("cpl", "%s", AOP (result)->aopu.aop_dir); + goto release; + } + else + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("cpl", "c"); + } + } + } + } + else + { + // right != literal + symbol *tlbl = newiTempLabel (NULL); + if (AOP_TYPE (right) == AOP_CRY) + { + // c = bit ^ bit; + if (IS_OP_ACCUSE (left)) + { + // left already is in the carry + operand *tmp = right; + right = left; + left = tmp; + } + else + { + toCarry (right); + } + } + else + { + // c = bit ^ val + toCarry (right); + } + emitcode ("jnb", "%s,!tlabel", AOP (left)->aopu.aop_dir, labelKey2num (tlbl->key)); + emitcode ("cpl", "c"); + emitLabel (tlbl); + } + // bit = c + // val = c + if (size) + outBitC (result); + // if(bit ^ ...) + else if ((AOP_TYPE (result) == AOP_CRY) && ifx) + genIfxJump (ifx, "c", left, right, result, ic->next); + goto release; + } + + /* if left is same as result */ + if (sameRegs (AOP (result), AOP (left))) + { + for (; size--; offset++) + { + if (AOP_TYPE (right) == AOP_LIT) + { + bytelit = (int) ((lit >> (offset * 8)) & 0x0FFL); + if (bytelit == 0) + { + /* dummy read of volatile operand */ + if (isOperandVolatile (left, FALSE)) + MOVA (aopGet (left, offset, FALSE, FALSE)); + else + continue; + } + else if (IS_AOP_PREG (left)) + { + MOVA (aopGet (left, offset, FALSE, TRUE)); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + aopPut (result, "a", offset); + } + else if (AOP_TYPE (left) != AOP_DPTR) + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, TRUE)); + emitcode ("xrl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + Safe_free (l); + } + else + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, TRUE)); + emitcode ("xrl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + Safe_free (l); + aopPut (result, "a", offset); + } + } + else + { + if (AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + if (offset) + emitcode ("mov", "b,a"); + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,b"); + aopPut (result, "a", offset); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + aopPut (result, "a", offset); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + if (IS_AOP_PREG (left)) + { + emitcode ("xrl", "a,%s", aopGet (left, offset, FALSE, TRUE)); + aopPut (result, "a", offset); + } + else + { + emitcode ("xrl", "%s,a", aopGet (left, offset, FALSE, TRUE)); + } + } + } + } + } + else + { + // left & result in different registers + if (AOP_TYPE (result) == AOP_CRY) + { + // result = bit + // if(size), result in bit + // if(!size && ifx), conditional oper: if(left ^ right) + symbol *tlbl = newiTempLabel (NULL); + int sizer = max (AOP_SIZE (left), AOP_SIZE (right)); + + if (size) + emitcode ("setb", "c"); + while (sizer--) + { + if ((AOP_TYPE (right) == AOP_LIT) && (((lit >> (offset * 8)) & 0x0FFL) == 0x00L)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + } + else if ((AOP_TYPE (right) == AOP_REG || IS_AOP_PREG (right) || AOP_TYPE (right) == AOP_DIR) + && AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else if (AOP_TYPE (left) == AOP_ACC) + { + if (!offset) + { + //B contains high byte of left + emitpush ("b"); + emitcode ("mov", "b,a"); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,b"); + emitpop ("b"); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,b"); + } + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,b"); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,%s", aopGet (left, offset, FALSE, FALSE)); + } + + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + offset++; + } + if (size) + { + CLRC; + emitLabel (tlbl); + outBitC (result); + } + else if (ifx) + jmpTrueOrFalse (ifx, tlbl, left, right, result, ic->next); + else + emitLabel (tlbl); + } + else + { + for (; (size--); offset++) + { + // normal case + // result = left ^ right + if (AOP_TYPE (right) == AOP_LIT) + { + bytelit = (int) ((lit >> (offset * 8)) & 0x0FFL); + if (bytelit == 0) + { + aopPut (result, aopGet (left, offset, FALSE, FALSE), offset); + continue; + } + else if (AOP_TYPE (left) == AOP_ACC) + { + // this should be the only use of left so A,B can be overwritten + char *l = Safe_strdup (aopGet (left, offset, FALSE, FALSE)); + emitcode ("xrl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + aopPut (result, l, offset); + Safe_free (l); + continue; + } + } + // faster than result <- left, xrl result,right + // and better if result is SFR + if ((AOP_TYPE (right) == AOP_REG || IS_AOP_PREG (right) || AOP_TYPE (right) == AOP_DIR) + && AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else if (AOP_TYPE (left) == AOP_ACC) + { + if (!offset) + { + //B contains high byte of left + emitpush ("b"); + emitcode ("mov", "b,a"); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,b"); + emitpop ("b"); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,b"); + } + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,b"); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,%s", aopGet (left, offset, FALSE, FALSE)); + } + aopPut (result, "a", offset); + } + } + } + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genRRC - rotate right with carry */ +/*-----------------------------------------------------------------*/ +static void +genRRC (iCode * ic) +{ + operand *left, *result; + int size, offset; + + D (emitcode (";", "genRRC")); + + /* rotate right with carry */ + left = IC_LEFT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + /* move it to the result */ + size = AOP_SIZE (result); + offset = size - 1; + if (size == 1) + { + /* special case for 1 byte */ + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("rr", "a"); + goto release; + } + /* no need to clear carry, bit7 will be written later */ + while (size--) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("rrc", "a"); + if (AOP_SIZE (result) > 1) + aopPut (result, "a", offset--); + } + /* now we need to put the carry into the + highest order byte of the result */ + if (AOP_SIZE (result) > 1) + { + MOVA (aopGet (result, AOP_SIZE (result) - 1, FALSE, FALSE)); + } + emitcode ("mov", "acc.7,c"); +release: + aopPut (result, "a", AOP_SIZE (result) - 1); + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genRLC - generate code for rotate left with carry */ +/*-----------------------------------------------------------------*/ +static void +genRLC (iCode * ic) +{ + operand *left, *result; + int size, offset; + + D (emitcode (";", "genRLC")); + + /* rotate right with carry */ + left = IC_LEFT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + /* move it to the result */ + size = AOP_SIZE (result); + offset = 0; + if (size--) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + if (size == 0) + { + /* special case for 1 byte */ + emitcode ("rl", "a"); + goto release; + } + emitcode ("rlc", "a"); /* bit0 will be written later */ + if (AOP_SIZE (result) > 1) + { + aopPut (result, "a", offset++); + } + + while (size--) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("rlc", "a"); + if (AOP_SIZE (result) > 1) + aopPut (result, "a", offset++); + } + } + /* now we need to put the carry into the + highest order byte of the result */ + if (AOP_SIZE (result) > 1) + { + MOVA (aopGet (result, 0, FALSE, FALSE)); + } + emitcode ("mov", "acc.0,c"); +release: + aopPut (result, "a", 0); + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGetAbit - generates code get a single bit */ +/*-----------------------------------------------------------------*/ +static void +genGetAbit (iCode * ic) +{ + operand *left, *right, *result; + int shCount; + + D (emitcode (";", "genGetAbit")); + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, FALSE); + + shCount = (int) ulFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit); + + /* get the needed byte into a */ + MOVA (aopGet (left, shCount / 8, FALSE, FALSE)); + shCount %= 8; + if (AOP_TYPE (result) == AOP_CRY) + { + if ((shCount) == 7) + emitcode ("rlc", "a"); + else if ((shCount) == 0) + emitcode ("rrc", "a"); + else + emitcode ("mov", "c,acc[%d]", shCount); + outBitC (result); + } + else + { + switch (shCount) + { + case 2: + emitcode ("rr", "a"); + //fallthrough + case 1: + emitcode ("rr", "a"); + //fallthrough + case 0: + emitcode ("anl", "a,#0x01"); + break; + case 3: + case 5: + emitcode ("mov", "c,acc[%d]", shCount); + emitcode ("clr", "a"); + emitcode ("rlc", "a"); + break; + case 4: + emitcode ("swap", "a"); + emitcode ("anl", "a,#0x01"); + break; + case 6: + emitcode ("rl", "a"); + //fallthrough + case 7: + emitcode ("rl", "a"); + emitcode ("anl", "a,#0x01"); + break; + } + outAcc (result); + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGetByte - generates code get a single byte */ +/*-----------------------------------------------------------------*/ +static void +genGetByte (iCode * ic) +{ + operand *left, *right, *result; + int offset; + + D (emitcode (";", "genGetByte")); + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, FALSE); + + offset = (int) ulFromVal (AOP (right)->aopu.aop_lit) / 8; + aopPut (result, aopGet (left, offset, FALSE, FALSE), 0); + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGetWord - generates code get two bytes */ +/*-----------------------------------------------------------------*/ +static void +genGetWord (iCode * ic) +{ + operand *left, *right, *result; + int offset; + + D (emitcode (";", "genGetWord")); + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, FALSE); + + offset = (int) ulFromVal (AOP (right)->aopu.aop_lit) / 8; + aopPut (result, aopGet (left, offset, FALSE, FALSE), 0); + aopPut (result, aopGet (left, offset + 1, FALSE, FALSE), 1); + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genSwap - generates code to swap nibbles or bytes */ +/*-----------------------------------------------------------------*/ +static void +genSwap (iCode * ic) +{ + operand *left, *result; + + D (emitcode (";", "genSwap")); + + left = IC_LEFT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + switch (AOP_SIZE (left)) + { + case 1: /* swap nibbles in byte */ + MOVA (aopGet (left, 0, FALSE, FALSE)); + emitcode ("swap", "a"); + aopPut (result, "a", 0); + break; + case 2: /* swap bytes in word */ + if (AOP_TYPE (left) == AOP_REG && sameRegs (AOP (left), AOP (result))) + { + MOVA (aopGet (left, 0, FALSE, FALSE)); + aopPut (result, aopGet (left, 1, FALSE, FALSE), 0); + aopPut (result, "a", 1); + } + else if (operandsEqu (left, result)) + { + char *reg = "a"; + bool pushedB = FALSE, leftInB = FALSE; + + MOVA (aopGet (left, 0, FALSE, FALSE)); + if (aopGetUsesAcc (left, 1) || aopGetUsesAcc (result, 0)) + { + pushedB = pushB (); + emitcode ("mov", "b,a"); + reg = "b"; + leftInB = TRUE; + } + aopPut (result, aopGet (left, 1, FALSE, FALSE), 0); + aopPut (result, reg, 1); + + if (leftInB) + popB (pushedB); + } + else + { + aopPut (result, aopGet (left, 1, FALSE, FALSE), 0); + aopPut (result, aopGet (left, 0, FALSE, FALSE), 1); + } + break; + default: + wassertl (FALSE, "unsupported SWAP operand size"); + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* AccRol - rotate left accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccRol (int shCount) +{ + shCount &= 0x0007; // shCount : 0..7 + + switch (shCount) + { + case 0: + break; + case 1: + emitcode ("rl", "a"); + break; + case 2: + emitcode ("rl", "a"); + emitcode ("rl", "a"); + break; + case 3: + emitcode ("swap", "a"); + emitcode ("rr", "a"); + break; + case 4: + emitcode ("swap", "a"); + break; + case 5: + emitcode ("swap", "a"); + emitcode ("rl", "a"); + break; + case 6: + emitcode ("rr", "a"); + emitcode ("rr", "a"); + break; + case 7: + emitcode ("rr", "a"); + break; + } +} + +/*-----------------------------------------------------------------*/ +/* AccLsh - left shift accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccLsh (int shCount) +{ + if (shCount != 0) + { + if (shCount == 1) + emitcode ("add", "a,acc"); + else if (shCount == 2) + { + emitcode ("add", "a,acc"); + emitcode ("add", "a,acc"); + } + else + { + /* rotate left accumulator */ + AccRol (shCount); + /* and kill the lower order bits */ + emitcode ("anl", "a,#!constbyte", SLMask[shCount]); + } + } +} + +/*-----------------------------------------------------------------*/ +/* AccRsh - right shift accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccRsh (int shCount) +{ + if (shCount != 0) + { + if (shCount == 1) + { + CLRC; + emitcode ("rrc", "a"); + } + else + { + /* rotate right accumulator */ + AccRol (8 - shCount); + /* and kill the higher order bits */ + emitcode ("anl", "a,#!constbyte", SRMask[shCount]); + } + } +} + +/*-----------------------------------------------------------------*/ +/* AccSRsh - signed right shift accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccSRsh (int shCount) +{ + symbol *tlbl; + if (shCount != 0) + { + if (shCount == 1) + { + emitcode ("mov", "c,acc.7"); + emitcode ("rrc", "a"); + } + else if (shCount == 2) + { + emitcode ("mov", "c,acc.7"); + emitcode ("rrc", "a"); + emitcode ("mov", "c,acc.7"); + emitcode ("rrc", "a"); + } + else + { + tlbl = newiTempLabel (NULL); + /* rotate right accumulator */ + AccRol (8 - shCount); + /* and kill the higher order bits */ + emitcode ("anl", "a,#!constbyte", SRMask[shCount]); + emitcode ("jnb", "acc.%d,!tlabel", 7 - shCount, labelKey2num (tlbl->key)); + emitcode ("orl", "a,#!constbyte", (unsigned char) ~SRMask[shCount]); + emitLabel (tlbl); + } + } +} + +/*-----------------------------------------------------------------*/ +/* shiftR1Left2Result - shift right one byte from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftR1Left2Result (operand * left, int offl, operand * result, int offr, int shCount, int sign) +{ + MOVA (aopGet (left, offl, FALSE, FALSE)); + /* shift right accumulator */ + if (sign) + AccSRsh (shCount); + else + AccRsh (shCount); + aopPut (result, "a", offr); +} + +/*-----------------------------------------------------------------*/ +/* shiftL1Left2Result - shift left one byte from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftL1Left2Result (operand * left, int offl, operand * result, int offr, int shCount) +{ + MOVA (aopGet (left, offl, FALSE, FALSE)); + /* shift left accumulator */ + AccLsh (shCount); + aopPut (result, "a", offr); +} + +/*-----------------------------------------------------------------*/ +/* movLeft2Result - move byte from left to result */ +/*-----------------------------------------------------------------*/ +static void +movLeft2Result (operand * left, int offl, operand * result, int offr, int sign) +{ + const char *l; + if (!sameRegs (AOP (left), AOP (result)) || (offl != offr)) + { + l = aopGet (left, offl, FALSE, FALSE); + + if (*l == '@' && (IS_AOP_PREG (result))) + { + emitcode ("mov", "a,%s", l); + aopPut (result, "a", offr); + } + else + { + if (!sign) + { + aopPut (result, l, offr); + } + else + { + /* MSB sign in acc.7 ! */ + if (getDataSize (left) == offl + 1) + { + MOVA (l); + aopPut (result, "a", offr); + } + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* AccAXRrl1 - right rotate c->a:x->c by 1 */ +/*-----------------------------------------------------------------*/ +static void +AccAXRrl1 (const char *x) +{ + emitcode ("rrc", "a"); + emitcode ("xch", "a,%s", x); + emitcode ("rrc", "a"); + emitcode ("xch", "a,%s", x); +} + +/*-----------------------------------------------------------------*/ +/* AccAXLrl1 - left rotate c<-a:x<-c by 1 */ +/*-----------------------------------------------------------------*/ +static void +AccAXLrl1 (const char *x) +{ + emitcode ("xch", "a,%s", x); + emitcode ("rlc", "a"); + emitcode ("xch", "a,%s", x); + emitcode ("rlc", "a"); +} + +/*-----------------------------------------------------------------*/ +/* AccAXLsh1 - left shift a:x<-0 by 1 */ +/*-----------------------------------------------------------------*/ +static void +AccAXLsh1 (const char *x) +{ + emitcode ("xch", "a,%s", x); + emitcode ("add", "a,acc"); + emitcode ("xch", "a,%s", x); + emitcode ("rlc", "a"); +} + +/*-----------------------------------------------------------------*/ +/* AccAXLsh - left shift a:x by known count (0..7) */ +/*-----------------------------------------------------------------*/ +static void +AccAXLsh (const char *x, int shCount) +{ + unsigned char mask; + + switch (shCount) + { + case 0: + break; + case 1: + AccAXLsh1 (x); + break; + case 2: + AccAXLsh1 (x); + AccAXLsh1 (x); + break; + case 3: + case 4: + case 5: // AAAAABBB:CCCCCDDD + mask = SLMask[shCount]; + AccRol (shCount); // BBBAAAAA:CCCCCDDD + emitcode ("anl", "a,#!constbyte", mask); // BBB00000:CCCCCDDD + emitcode ("xch", "a,%s", x); // CCCCCDDD:BBB00000 + AccRol (shCount); // DDDCCCCC:BBB00000 + emitcode ("xch", "a,%s", x); // BBB00000:DDDCCCCC + emitcode ("xrl", "a,%s", x); // (BBB^DDD)CCCCC:DDDCCCCC + emitcode ("xch", "a,%s", x); // DDDCCCCC:(BBB^DDD)CCCCC + emitcode ("anl", "a,#!constbyte", mask); // DDD00000:(BBB^DDD)CCCCC + emitcode ("xch", "a,%s", x); // (BBB^DDD)CCCCC:DDD00000 + emitcode ("xrl", "a,%s", x); // BBBCCCCC:DDD00000 + break; + case 6: // AAAAAABB:CCCCCCDD + mask = SRMask[shCount]; + emitcode ("anl", "a,#!constbyte", mask); // 000000BB:CCCCCCDD + emitcode ("mov", "c,acc.0"); // c = B + emitcode ("xch", "a,%s", x); // CCCCCCDD:000000BB + emitcode ("rrc", "a"); + emitcode ("xch", "a,%s", x); + emitcode ("rrc", "a"); + emitcode ("mov", "c,acc.0"); //<< get correct bit + emitcode ("xch", "a,%s", x); + emitcode ("rrc", "a"); + emitcode ("xch", "a,%s", x); + emitcode ("rrc", "a"); + emitcode ("xch", "a,%s", x); + break; + case 7: // a:x <<= 7 + mask = SRMask[shCount]; + emitcode ("anl", "a,#!constbyte", mask); // 0000000B:CCCCCCCD + emitcode ("mov", "c,acc.0"); // c = B + emitcode ("xch", "a,%s", x); // CCCCCCCD:0000000B + AccAXRrl1 (x); // BCCCCCCC:D0000000 + break; + default: + break; + } +} + +/*-----------------------------------------------------------------*/ +/* AccAXRsh - right shift a:x known count (0..7) */ +/*-----------------------------------------------------------------*/ +static void +AccAXRsh (const char *x, int shCount) +{ + unsigned char mask = SRMask[shCount]; + + switch (shCount) + { + case 0: + break; + case 1: + CLRC; + AccAXRrl1 (x); // 0->a:x + break; + case 2: + CLRC; + AccAXRrl1 (x); // 0->a:x + CLRC; + AccAXRrl1 (x); // 0->a:x + break; + case 3: + case 4: + case 5: // AAAAABBB:CCCCCDDD = a:x + AccRol (8 - shCount); // BBBAAAAA:DDDCCCCC + emitcode ("xch", "a,%s", x); // CCCCCDDD:BBBAAAAA + AccRol (8 - shCount); // DDDCCCCC:BBBAAAAA + emitcode ("anl", "a,#!constbyte", mask); // 000CCCCC:BBBAAAAA + emitcode ("xrl", "a,%s", x); // BBB(CCCCC^AAAAA):BBBAAAAA + emitcode ("xch", "a,%s", x); // BBBAAAAA:BBB(CCCCC^AAAAA) + emitcode ("anl", "a,#!constbyte", mask); // 000AAAAA:BBB(CCCCC^AAAAA) + emitcode ("xch", "a,%s", x); // BBB(CCCCC^AAAAA):000AAAAA + emitcode ("xrl", "a,%s", x); // BBBCCCCC:000AAAAA + emitcode ("xch", "a,%s", x); // 000AAAAA:BBBCCCCC + break; + case 6: // AABBBBBB:CCDDDDDD + emitcode ("mov", "c,acc.7"); + AccAXLrl1 (x); // ABBBBBBC:CDDDDDDA + emitcode ("mov", "c,acc.7"); + AccAXLrl1 (x); // BBBBBBCC:DDDDDDAA + emitcode ("xch", "a,%s", x); // DDDDDDAA:BBBBBBCC + emitcode ("anl", "a,#!constbyte", mask); // 000000AA:BBBBBBCC + break; + case 7: // ABBBBBBB:CDDDDDDD + emitcode ("mov", "c,acc.7"); // c = A + AccAXLrl1 (x); // BBBBBBBC:DDDDDDDA + emitcode ("xch", "a,%s", x); // DDDDDDDA:BBBBBBCC + emitcode ("anl", "a,#!constbyte", mask); // 0000000A:BBBBBBBC + break; + default: + break; + } +} + +/*-----------------------------------------------------------------*/ +/* AccAXRshS - right shift signed a:x known count (0..7) */ +/*-----------------------------------------------------------------*/ +static void +AccAXRshS (const char *x, int shCount) +{ + symbol *tlbl; + unsigned char mask = SRMask[shCount]; + + switch (shCount) + { + case 0: + break; + case 1: + emitcode ("mov", "c,acc.7"); + AccAXRrl1 (x); // s->a:x + break; + case 2: + emitcode ("mov", "c,acc.7"); + AccAXRrl1 (x); // s->a:x + emitcode ("mov", "c,acc.7"); + AccAXRrl1 (x); // s->a:x + break; + case 3: + case 4: + case 5: // AAAAABBB:CCCCCDDD = a:x + tlbl = newiTempLabel (NULL); + AccRol (8 - shCount); // BBBAAAAA:CCCCCDDD + emitcode ("xch", "a,%s", x); // CCCCCDDD:BBBAAAAA + AccRol (8 - shCount); // DDDCCCCC:BBBAAAAA + emitcode ("anl", "a,#!constbyte", mask); // 000CCCCC:BBBAAAAA + emitcode ("xrl", "a,%s", x); // BBB(CCCCC^AAAAA):BBBAAAAA + emitcode ("xch", "a,%s", x); // BBBAAAAA:BBB(CCCCC^AAAAA) + emitcode ("anl", "a,#!constbyte", mask); // 000AAAAA:BBB(CCCCC^AAAAA) + emitcode ("xch", "a,%s", x); // BBB(CCCCC^AAAAA):000AAAAA + emitcode ("xrl", "a,%s", x); // BBBCCCCC:000AAAAA + emitcode ("xch", "a,%s", x); // 000SAAAA:BBBCCCCC + emitcode ("jnb", "acc.%d,!tlabel", 7 - shCount, labelKey2num (tlbl->key)); + mask = ~SRMask[shCount]; + emitcode ("orl", "a,#!constbyte", mask); // 111AAAAA:BBBCCCCC + emitLabel (tlbl); + break; // SSSSAAAA:BBBCCCCC + case 6: // AABBBBBB:CCDDDDDD + tlbl = newiTempLabel (NULL); + emitcode ("mov", "c,acc.7"); + AccAXLrl1 (x); // ABBBBBBC:CDDDDDDA + emitcode ("mov", "c,acc.7"); + AccAXLrl1 (x); // BBBBBBCC:DDDDDDAA + emitcode ("xch", "a,%s", x); // DDDDDDAA:BBBBBBCC + emitcode ("anl", "a,#!constbyte", mask); // 000000AA:BBBBBBCC + emitcode ("jnb", "acc.%d,!tlabel", 7 - shCount, labelKey2num (tlbl->key)); + mask = ~SRMask[shCount]; + emitcode ("orl", "a,#!constbyte", mask); // 111111AA:BBBBBBCC + emitLabel (tlbl); + break; + case 7: // ABBBBBBB:CDDDDDDD + tlbl = newiTempLabel (NULL); + emitcode ("mov", "c,acc.7"); // c = A + AccAXLrl1 (x); // BBBBBBBC:DDDDDDDA + emitcode ("xch", "a,%s", x); // DDDDDDDA:BBBBBBCC + emitcode ("anl", "a,#!constbyte", mask); // 0000000A:BBBBBBBC + emitcode ("jnb", "acc.%d,!tlabel", 7 - shCount, labelKey2num (tlbl->key)); + mask = ~SRMask[shCount]; + emitcode ("orl", "a,#!constbyte", mask); // 1111111A:BBBBBBBC + emitLabel (tlbl); + break; + default: + break; + } +} + +/*-----------------------------------------------------------------*/ +/* shiftL2Left2Result - shift left two bytes from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftL2Left2Result (operand * left, int offl, operand * result, int offr, int shCount) +{ + const char *x; + bool pushedB = FALSE; + bool usedB = FALSE; + + if (sameRegs (AOP (result), AOP (left)) && ((offl + MSB16) == offr)) + { + /* don't crash result[offr] */ + MOVA (aopGet (left, offl, FALSE, FALSE)); + x = xch_a_aopGet (left, offl + MSB16, FALSE, FALSE); + usedB = !strncmp (x, "b", 1); + } + else if (aopGetUsesAcc (result, offr)) + { + movLeft2Result (left, offl, result, offr, 0); + pushedB = pushB (); + usedB = TRUE; + emitcode ("mov", "b,%s", aopGet (left, offl + MSB16, FALSE, FALSE)); + MOVA (aopGet (result, offr, FALSE, FALSE)); + emitcode ("xch", "a,b"); + x = "b"; + } + else + { + movLeft2Result (left, offl, result, offr, 0); + MOVA (aopGet (left, offl + MSB16, FALSE, FALSE)); + x = aopGet (result, offr, FALSE, FALSE); + } + /* ax << shCount (x = lsb(result)) */ + AccAXLsh (x, shCount); + if (usedB) + { + emitcode ("xch", "a,b"); + aopPut (result, "a", offr); + aopPut (result, "b", offr + MSB16); + popB (pushedB); + } + else + { + aopPut (result, "a", offr + MSB16); + } +} + +/*-----------------------------------------------------------------*/ +/* shiftR2Left2Result - shift right two bytes from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftR2Left2Result (operand * left, int offl, operand * result, int offr, int shCount, int sign) +{ + const char *x; + bool pushedB = FALSE; + bool usedB = FALSE; + + if (sameRegs (AOP (result), AOP (left)) && ((offl + MSB16) == offr)) + { + /* don't crash result[offr] */ + MOVA (aopGet (left, offl, FALSE, FALSE)); + x = xch_a_aopGet (left, offl + MSB16, FALSE, FALSE); + usedB = !strncmp (x, "b", 1); + } + else if (aopGetUsesAcc (result, offr)) + { + movLeft2Result (left, offl, result, offr, 0); + pushedB = pushB (); + usedB = TRUE; + emitcode ("mov", "b,%s", aopGet (result, offr, FALSE, FALSE)); + MOVA (aopGet (left, offl + MSB16, FALSE, FALSE)); + x = "b"; + } + else + { + movLeft2Result (left, offl, result, offr, 0); + MOVA (aopGet (left, offl + MSB16, FALSE, FALSE)); + x = aopGet (result, offr, FALSE, FALSE); + } + /* a:x >> shCount (x = lsb(result)) */ + if (sign) + AccAXRshS (x, shCount); + else + AccAXRsh (x, shCount); + if (usedB) + { + emitcode ("xch", "a,b"); + aopPut (result, "a", offr); + emitcode ("xch", "a,b"); + popB (pushedB); + } + if (getDataSize (result) > 1) + aopPut (result, "a", offr + MSB16); +} + +/*------------------------------------------------------------------*/ +/* shiftLLeftOrResult - shift left one byte from left, or to result */ +/*------------------------------------------------------------------*/ +static void +shiftLLeftOrResult (operand * left, int offl, operand * result, int offr, int shCount) +{ + MOVA (aopGet (left, offl, FALSE, FALSE)); + /* shift left accumulator */ + AccLsh (shCount); + /* or with result */ + if (aopGetUsesAcc (result, offr)) + { + emitcode ("xch", "a,b"); + MOVA (aopGet (result, offr, FALSE, FALSE)); + emitcode ("orl", "a,b"); + } + else + { + emitcode ("orl", "a,%s", aopGet (result, offr, FALSE, FALSE)); + } + /* back to result */ + aopPut (result, "a", offr); +} + +/*-----------------------------------------------------------------*/ +/* shiftRLeftOrResult - shift right one byte from left,or to result */ +/*-----------------------------------------------------------------*/ +static void +shiftRLeftOrResult (operand * left, int offl, operand * result, int offr, int shCount) +{ + MOVA (aopGet (left, offl, FALSE, FALSE)); + /* shift right accumulator */ + AccRsh (shCount); + /* or with result */ + if (aopGetUsesAcc (result, offr)) + { + emitcode ("xch", "a,b"); + MOVA (aopGet (result, offr, FALSE, FALSE)); + emitcode ("orl", "a,b"); + } + else + { + emitcode ("orl", "a,%s", aopGet (result, offr, FALSE, FALSE)); + } + /* back to result */ + aopPut (result, "a", offr); +} + +/*-----------------------------------------------------------------*/ +/* shiftLLong - shift left one long from left to result */ +/* offl = LSB or MSB16 */ +/*-----------------------------------------------------------------*/ +static void +shiftLLong (operand * left, operand * result, int offr) +{ + int offl = LSB; + int size = AOP_SIZE (result); + int useXch = (sameRegs (AOP (left), AOP (result)) && size >= MSB16 + offr && offr != LSB); + + if (size > offl + offr) + { + MOVA (aopGet (left, offl, FALSE, FALSE)); + emitcode ("add", "a,acc"); + if (useXch) + xch_a_aopGet (left, offl + offr, FALSE, FALSE); + else + aopPut (result, "a", offl + offr); + } + + for (offl = LSB + 1; offl < LSB + 8; offl++) + { + if (size > offl + offr) + { + if (!useXch) + MOVA (aopGet (left, offl, FALSE, FALSE)); + emitcode ("rlc", "a"); + if (useXch) + xch_a_aopGet (left, offl + offr, FALSE, FALSE); + else + aopPut (result, "a", offl + offr); + } + } +} + +/*-----------------------------------------------------------------*/ +/* genlshFixed - shift four byte by a known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genlshFixed (operand *result, operand *left, int shCount) +{ + int size, b; + int full_bytes; + + D (emitcode (";", "genlshFixed")); + + size = AOP_SIZE (result); + + full_bytes = shCount / 8; + shCount -= full_bytes * 8; + if (shCount == 0) + { + for (b = size - 1; b > full_bytes - 1; b--) + movLeft2Result (left, b - full_bytes, result, b, 0); + } + else if ((shCount == 1) && (full_bytes < 2)) + { + shiftLLong (left, result, full_bytes); + } + else if ((shCount == 2) && (full_bytes == 0)) + { + shiftLLong (left, result, full_bytes); + shiftLLong (result, result, full_bytes); + } + else + { + int off; + for (off = size - 2; off - full_bytes >= 0; off -= 2) + { + shiftL2Left2Result (left, off - full_bytes, result, off, shCount); + if (off - full_bytes - 1 >= 0) + shiftRLeftOrResult (left, off - full_bytes - 1, result, off, 8 - shCount); + } + if (off - full_bytes == -1) + { + shiftL1Left2Result (left, LSB, result, full_bytes, shCount); + } + } + for (b = LSB; b < full_bytes; b++) + aopPut (result, zero, b); + return; +} + +/*-----------------------------------------------------------------*/ +/* genlshAny - shift any number of bytes by a known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genlshAny (operand *result, operand *left, int shCount) +{ + int size, size2, offset; + + D (emitcode (";", "genlshAny")); + + size = AOP_SIZE (result); + + if (!operandsEqu (result, left)) + for (size2 = size, offset = 0; size2 > 0; size2--, offset++) + aopPut (result, aopGet (left, offset, FALSE, FALSE), offset); + + while (shCount--) + { + MOVA (aopGet (result, LSB, FALSE, FALSE)); + emitcode ("add", "a,acc"); + aopPut (result, "a", 0); + + for(size2 = size - 1, offset = 1; size2 > 0; size2--, offset++) + { + + MOVA (aopGet (result, offset, FALSE, FALSE)); + emitcode ("rlc", "a"); + aopPut (result, "a", offset); + } + } +} + +/*-----------------------------------------------------------------*/ +/* genLeftShiftLiteral - left shifting by known count */ +/*-----------------------------------------------------------------*/ +static void +genLeftShiftLiteral (operand * left, operand * right, operand * result, iCode * ic) +{ + unsigned int shCount = (unsigned int) ulFromVal (AOP (right)->aopu.aop_lit); + unsigned int size; + + size = getSize (operandType (result)); + + D (emitcode (";", "genLeftShiftLiteral (%d), size %d", shCount, size)); + + freeAsmop (right, NULL, ic, TRUE); + + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + +#if VIEW_SIZE + emitcode ("; shift left ", "result %d, left %d", size, AOP_SIZE (left)); +#endif + + switch (size) + { + case 1: + case 2: + case 4: + case 8: + genlshFixed (result, left, shCount); + break; + + default: + genlshAny (result, left, shCount); + break; + } + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genLeftShift - generates code for left shifting */ +/*-----------------------------------------------------------------*/ +static void +genLeftShift (iCode * ic) +{ + operand *left, *right, *result; + int size, offset; + symbol *tlbl, *tlbl1; + bool pushedB; + + D (emitcode (";", "genLeftShift")); + + right = IC_RIGHT (ic); + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + aopOp (right, ic, FALSE); + + /* if the shift count is known then do it + as efficiently as possible */ + if (AOP_TYPE (right) == AOP_LIT) + { + genLeftShiftLiteral (left, right, result, ic); + return; + } + + /* shift count is unknown then we have to form + a loop get the loop count in B : Note: we take + only the lower order byte since shifting + more that 32 bits makes no sense anyway, ( the + largest size of an object can be only 32 bits ) */ + + pushedB = pushB (); + if (AOP_TYPE (right) == AOP_LIT) + { + /* Really should be handled by genLeftShiftLiteral, + * but since I'm too lazy to fix that today, at least we can make + * some small improvement. + */ + emitcode ("mov", "b,#!constbyte", ((int) ulFromVal (AOP (right)->aopu.aop_lit)) + 1); + } + else + { + MOVB (aopGet (right, 0, FALSE, FALSE)); + emitcode ("inc", "b"); + } + freeAsmop (right, NULL, ic, TRUE); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + /* now move the left to the result if they are not the same */ + if (!sameRegs (AOP (left), AOP (result)) && AOP_SIZE (result) > 1) + { + size = AOP_SIZE (result); + offset = 0; + while (size--) + { + const char *l = aopGet (left, offset, FALSE, TRUE); + if (*l == '@' && (IS_AOP_PREG (result))) + { + + emitcode ("mov", "a,%s", l); + aopPut (result, "a", offset); + } + else + aopPut (result, l, offset); + offset++; + } + } + + tlbl = newiTempLabel (NULL); + size = AOP_SIZE (result); + offset = 0; + tlbl1 = newiTempLabel (NULL); + + /* if it is only one byte then */ + if (size == 1) + { + symbol *tlbl1 = newiTempLabel (NULL); + + MOVA (aopGet (left, 0, FALSE, FALSE)); + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + emitcode ("add", "a,acc"); + emitLabel (tlbl1); + emitcode ("djnz", "b,!tlabel", labelKey2num (tlbl->key)); + popB (pushedB); + aopPut (result, "a", 0); + goto release; + } + + reAdjustPreg (AOP (result)); + + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + MOVA (aopGet (result, offset, FALSE, FALSE)); + emitcode ("add", "a,acc"); + aopPut (result, "a", offset++); + while (--size) + { + MOVA (aopGet (result, offset, FALSE, FALSE)); + emitcode ("rlc", "a"); + aopPut (result, "a", offset++); + } + reAdjustPreg (AOP (result)); + + emitLabel (tlbl1); + emitcode ("djnz", "b,!tlabel", labelKey2num (tlbl->key)); + popB (pushedB); +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genrshOne - right shift a one byte quantity by known count */ +/*-----------------------------------------------------------------*/ +static void +genrshOne (operand * result, operand * left, int shCount, int sign) +{ + D (emitcode (";", "genrshOne")); + + shiftR1Left2Result (left, LSB, result, LSB, shCount, sign); +} + +/*-----------------------------------------------------------------*/ +/* genrshTwo - right shift two bytes by known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genrshTwo (operand * result, operand * left, int shCount, int sign) +{ + D (emitcode (";", "genrshTwo")); + + /* if shCount >= 8 */ + if (shCount >= 8) + { + shCount -= 8; + if (shCount) + shiftR1Left2Result (left, MSB16, result, LSB, shCount, sign); + else + movLeft2Result (left, MSB16, result, LSB, sign); + addSign (result, MSB16, sign); + } + + /* 1 <= shCount <= 7 */ + else + { + shiftR2Left2Result (left, LSB, result, LSB, shCount, sign); + } +} + +/*-----------------------------------------------------------------*/ +/* shiftRLong - shift right one long from left to result */ +/* offl = LSB or MSB16 */ +/*-----------------------------------------------------------------*/ +static void +shiftRLong (operand * left, int offl, operand * result, int sign) +{ + bool overlapping = regsInCommon (left, result) || operandsEqu (left, result); + + if (overlapping && offl > 1) + { + // we are in big trouble, but this shouldn't happen + werror (E_INTERNAL_ERROR, __FILE__, __LINE__); + } + + MOVA (aopGet (left, MSB32, FALSE, FALSE)); + + if (offl == MSB16) + { + // shift is > 8 + if (sign) + { + emitcode ("rlc", "a"); + emitcode ("subb", "a,acc"); + if (overlapping && sameByte (AOP (left), MSB32, AOP (result), MSB32)) + { + xch_a_aopGet (left, MSB32, FALSE, FALSE); + } + else + { + aopPut (result, "a", MSB32); + MOVA (aopGet (left, MSB32, FALSE, FALSE)); + } + } + else + { + if (aopPutUsesAcc (result, zero, MSB32)) + { + emitcode ("xch", "a,b"); + aopPut (result, zero, MSB32); + emitcode ("xch", "a,b"); + } + else + { + aopPut (result, zero, MSB32); + } + } + } + + if (!sign) + { + emitcode ("clr", "c"); + } + else + { + emitcode ("mov", "c,acc.7"); + } + + emitcode ("rrc", "a"); + + if (overlapping && offl == MSB16 && sameByte (AOP (left), MSB24, AOP (result), MSB32 - offl)) + { + xch_a_aopGet (left, MSB24, FALSE, FALSE); + } + else + { + aopPut (result, "a", MSB32 - offl); + MOVA (aopGet (left, MSB24, FALSE, FALSE)); + } + + emitcode ("rrc", "a"); + if (overlapping && offl == MSB16 && sameByte (AOP (left), MSB16, AOP (result), MSB24 - offl)) + { + xch_a_aopGet (left, MSB16, FALSE, FALSE); + } + else + { + aopPut (result, "a", MSB24 - offl); + MOVA (aopGet (left, MSB16, FALSE, FALSE)); + } + + emitcode ("rrc", "a"); + if (offl != LSB) + { + aopPut (result, "a", MSB16 - offl); + } + else + { + if (overlapping && sameByte (AOP (left), LSB, AOP (result), MSB16 - offl)) + { + xch_a_aopGet (left, LSB, FALSE, FALSE); + } + else + { + aopPut (result, "a", MSB16 - offl); + MOVA (aopGet (left, LSB, FALSE, FALSE)); + } + emitcode ("rrc", "a"); + aopPut (result, "a", LSB); + } +} + +/*-----------------------------------------------------------------*/ +/* genrshFour - shift four byte by a known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genrshFour (operand *result, operand *left, int shCount, int sign) +{ + D (emitcode (";", "genrshFour")); + + /* if shifting more that 3 bytes */ + if (shCount >= 24) + { + shCount -= 24; + if (shCount) + shiftR1Left2Result (left, MSB32, result, LSB, shCount, sign); + else + movLeft2Result (left, MSB32, result, LSB, sign); + addSign (result, MSB16, sign); + } + else if (shCount >= 16) + { + shCount -= 16; + if (shCount) + shiftR2Left2Result (left, MSB24, result, LSB, shCount, sign); + else + { + movLeft2Result (left, MSB24, result, LSB, 0); + movLeft2Result (left, MSB32, result, MSB16, sign); + } + addSign (result, MSB24, sign); + } + else if (shCount >= 8) + { + shCount -= 8; + if (shCount == 1) + { + shiftRLong (left, MSB16, result, sign); + } + else if (shCount == 0) + { + movLeft2Result (left, MSB16, result, LSB, 0); + movLeft2Result (left, MSB24, result, MSB16, 0); + movLeft2Result (left, MSB32, result, MSB24, sign); + addSign (result, MSB32, sign); + } + else + { + shiftR2Left2Result (left, MSB16, result, LSB, shCount, 0); + shiftLLeftOrResult (left, MSB32, result, MSB16, 8 - shCount); + /* the last shift is signed */ + shiftR1Left2Result (left, MSB32, result, MSB24, shCount, sign); + addSign (result, MSB32, sign); + } + } + else + { + /* 1 <= shCount <= 7 */ + if (shCount <= 2) + { + shiftRLong (left, LSB, result, sign); + if (shCount == 2) + shiftRLong (result, LSB, result, sign); + } + else + { + shiftR2Left2Result (left, LSB, result, LSB, shCount, 0); + shiftLLeftOrResult (left, MSB24, result, MSB16, 8 - shCount); + shiftR2Left2Result (left, MSB24, result, MSB24, shCount, sign); + } + } +} + +/*-----------------------------------------------------------------*/ +/* genrshAny - shift any number of bytes by a known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genrshAny (operand *result, operand *left, int shCount, int sign) +{ + int size, size2, offset; + + D (emitcode (";", "genrshAny")); + + size = AOP_SIZE (result); + + if (!operandsEqu (result, left)) + for (size2 = size, offset = 0; size2 > 0; size2--, offset++) + aopPut (result, aopGet (left, offset, FALSE, FALSE), offset); + + while (shCount--) + { + MOVA (aopGet (result, size - 1, FALSE, FALSE)); + if (!sign) + emitcode ("clr", "c"); + else + emitcode ("mov", "c,acc.7"); + emitcode ("rrc", "a"); + aopPut (result, "a", size - 1); + + for(size2 = size - 1, offset = size - 2; size2 > 0; size2--, offset--) + { + + MOVA (aopGet (result, offset, FALSE, FALSE)); + emitcode ("rrc", "a"); + aopPut (result, "a", offset); + } + } +} + +/*-----------------------------------------------------------------*/ +/* genRightShiftLiteral - right shifting by known count */ +/*-----------------------------------------------------------------*/ +static void +genRightShiftLiteral (operand * left, operand * right, operand * result, iCode * ic, int sign) +{ + int shCount = (int) ulFromVal (AOP (right)->aopu.aop_lit); + int size; + + size = getSize (operandType (result)); //getDataSize (left); + + D (emitcode (";", "genRightShiftLiteral (%d), size %d", shCount, size)); + + freeAsmop (right, NULL, ic, TRUE); + + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + +#if VIEW_SIZE + emitcode ("; shift right ", "result %d, left %d", AOP_SIZE (result), AOP_SIZE (left)); +#endif + + /* test the LEFT size !!! */ + + /* I suppose that the left size >= result size */ + wassert ((int)getSize (operandType (left)) >= size); + + if (shCount == 0) + { + size = getDataSize (result); + while (size--) + movLeft2Result (left, size, result, size, 0); + } + else if (shCount >= (size * 8)) + { + if (sign) + { + /* get sign in acc.7 */ + MOVA (aopGet (left, size - 1, FALSE, FALSE)); + } + addSign (result, LSB, sign); + } + else + { + switch (size) + { + case 1: + genrshOne (result, left, shCount, sign); + break; + + case 2: + genrshTwo (result, left, shCount, sign); + break; + + case 4: + genrshFour (result, left, shCount, sign); + break; + + default: + genrshAny (result, left, shCount, sign); + break; + } + } + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genSignedRightShift - right shift of signed number */ +/*-----------------------------------------------------------------*/ +static void +genSignedRightShift (iCode * ic) +{ + operand *right, *left, *result; + int size, offset; + symbol *tlbl, *tlbl1; + bool pushedB; + + D (emitcode (";", "genSignedRightShift")); + + /* we do it the hard way put the shift count in b + and loop thru preserving the sign */ + + right = IC_RIGHT (ic); + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + aopOp (right, ic, FALSE); + + if (AOP_TYPE (right) == AOP_LIT) + { + genRightShiftLiteral (left, right, result, ic, 1); + return; + } + /* shift count is unknown then we have to form + a loop get the loop count in B : Note: we take + only the lower order byte since shifting + more that 32 bits make no sense anyway, ( the + largest size of an object can be only 32 bits ) */ + + pushedB = pushB (); + if (AOP_TYPE (right) == AOP_LIT) + { + /* Really should be handled by genRightShiftLiteral, + * but since I'm too lazy to fix that today, at least we can make + * some small improvement. + */ + emitcode ("mov", "b,#!constbyte", ((int) ulFromVal (AOP (right)->aopu.aop_lit)) + 1); + } + else + { + MOVB (aopGet (right, 0, FALSE, FALSE)); + emitcode ("inc", "b"); + } + freeAsmop (right, NULL, ic, TRUE); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + /* now move the left to the result if they are not the + same */ + if (!sameRegs (AOP (left), AOP (result)) && AOP_SIZE (result) > 1) + { + + size = AOP_SIZE (result); + offset = 0; + while (size--) + { + const char *l = aopGet (left, offset, FALSE, TRUE); + if (*l == '@' && IS_AOP_PREG (result)) + { + + emitcode ("mov", "a,%s", l); + aopPut (result, "a", offset); + } + else + aopPut (result, l, offset); + offset++; + } + } + + /* mov the highest order bit to OVR */ + tlbl = newiTempLabel (NULL); + tlbl1 = newiTempLabel (NULL); + + size = AOP_SIZE (result); + offset = size - 1; + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("rlc", "a"); + emitcode ("mov", "ov,c"); + /* if it is only one byte then */ + if (size == 1) + { + MOVA (aopGet (left, 0, FALSE, FALSE)); + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + emitcode ("mov", "c,ov"); + emitcode ("rrc", "a"); + emitLabel (tlbl1); + emitcode ("djnz", "b,!tlabel", labelKey2num (tlbl->key)); + popB (pushedB); + aopPut (result, "a", 0); + goto release; + } + + reAdjustPreg (AOP (result)); + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + emitcode ("mov", "c,ov"); + while (size--) + { + MOVA (aopGet (result, offset, FALSE, FALSE)); + emitcode ("rrc", "a"); + aopPut (result, "a", offset--); + } + reAdjustPreg (AOP (result)); + emitLabel (tlbl1); + emitcode ("djnz", "b,!tlabel", labelKey2num (tlbl->key)); + popB (pushedB); + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genRightShift - generate code for right shifting */ +/*-----------------------------------------------------------------*/ +static void +genRightShift (iCode * ic) +{ + operand *right, *left, *result; + sym_link *letype; + int size, offset; + symbol *tlbl, *tlbl1; + bool pushedB; + + D (emitcode (";", "genRightShift")); + + /* if signed then we do it the hard way preserve the + sign bit moving it inwards */ + letype = getSpec (operandType (IC_LEFT (ic))); + + if (!SPEC_USIGN (letype)) + { + genSignedRightShift (ic); + return; + } + + /* signed & unsigned types are treated the same : i.e. the + signed is NOT propagated inwards : quoting from the + ANSI - standard : "for E1 >> E2, is equivalent to division + by 2**E2 if unsigned or if it has a non-negative value, + otherwise the result is implementation defined ", MY definition + is that the sign does not get propagated */ + + right = IC_RIGHT (ic); + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + aopOp (right, ic, FALSE); + + /* if the shift count is known then do it + as efficiently as possible */ + if (AOP_TYPE (right) == AOP_LIT) + { + genRightShiftLiteral (left, right, result, ic, 0); + return; + } + + /* shift count is unknown then we have to form + a loop get the loop count in B : Note: we take + only the lower order byte since shifting + more that 32 bits make no sense anyway, ( the + largest size of an object can be only 32 bits ) */ + + pushedB = pushB (); + if (AOP_TYPE (right) == AOP_LIT) + { + /* Really should be handled by genRightShiftLiteral, + * but since I'm too lazy to fix that today, at least we can make + * some small improvement. + */ + emitcode ("mov", "b,#!constbyte", ((int) ulFromVal (AOP (right)->aopu.aop_lit)) + 1); + } + else + { + MOVB (aopGet (right, 0, FALSE, FALSE)); + emitcode ("inc", "b"); + } + freeAsmop (right, NULL, ic, TRUE); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + /* now move the left to the result if they are not the + same */ + if (!sameRegs (AOP (left), AOP (result)) && AOP_SIZE (result) > 1) + { + size = AOP_SIZE (result); + offset = 0; + while (size--) + { + const char *l = aopGet (left, offset, FALSE, TRUE); + if (*l == '@' && IS_AOP_PREG (result)) + { + + emitcode ("mov", "a,%s", l); + aopPut (result, "a", offset); + } + else + aopPut (result, l, offset); + offset++; + } + } + + tlbl = newiTempLabel (NULL); + tlbl1 = newiTempLabel (NULL); + size = AOP_SIZE (result); + offset = size - 1; + + /* if it is only one byte then */ + if (size == 1) + { + MOVA (aopGet (left, 0, FALSE, FALSE)); + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + CLRC; + emitcode ("rrc", "a"); + emitLabel (tlbl1); + emitcode ("djnz", "b,!tlabel", labelKey2num (tlbl->key)); + popB (pushedB); + aopPut (result, "a", 0); + goto release; + } + + reAdjustPreg (AOP (result)); + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + CLRC; + while (size--) + { + MOVA (aopGet (result, offset, FALSE, FALSE)); + emitcode ("rrc", "a"); + aopPut (result, "a", offset--); + } + reAdjustPreg (AOP (result)); + + emitLabel (tlbl1); + emitcode ("djnz", "b,!tlabel", labelKey2num (tlbl->key)); + popB (pushedB); + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* emitPtrByteGet - emits code to get a byte into A through a */ +/* pointer register (R0, R1, or DPTR). The */ +/* original value of A can be preserved in B. */ +/*-----------------------------------------------------------------*/ +static void +emitPtrByteGet (const char *rname, int p_type, bool preserveAinB) +{ + switch (p_type) + { + case IPOINTER: + case POINTER: + if (preserveAinB) + emitcode ("mov", "b,a"); + emitcode ("mov", "a,@%s", rname); + break; + + case PPOINTER: + if (preserveAinB) + emitcode ("mov", "b,a"); + emitcode ("movx", "a,@%s", rname); + break; + + case FPOINTER: + if (preserveAinB) + emitcode ("mov", "b,a"); + emitcode ("movx", "a,@dptr"); + break; + + case CPOINTER: + if (preserveAinB) + emitcode ("mov", "b,a"); + emitcode ("clr", "a"); + emitcode ("movc", "a,@a+dptr"); + break; + + case GPOINTER: + if (preserveAinB) + { + emitpush ("b"); + emitpush ("acc"); + } + emitcode ("lcall", "__gptrget"); + if (preserveAinB) + emitpop ("b"); + break; + } +} + +/*-----------------------------------------------------------------*/ +/* emitPtrByteSet - emits code to set a byte from src through a */ +/* pointer register (R0, R1, or DPTR). */ +/*-----------------------------------------------------------------*/ +static void +emitPtrByteSet (const char *rname, int p_type, const char *src) +{ + switch (p_type) + { + case IPOINTER: + case POINTER: + if (*src == '@') + { + MOVA (src); + emitcode ("mov", "@%s,a", rname); + } + else + emitcode ("mov", "@%s,%s", rname, src); + break; + + case PPOINTER: + MOVA (src); + emitcode ("movx", "@%s,a", rname); + break; + + case FPOINTER: + MOVA (src); + emitcode ("movx", "@dptr,a"); + break; + + case GPOINTER: + MOVA (src); + emitcode ("lcall", "__gptrput"); + break; + } +} + +/*-----------------------------------------------------------------*/ +/* genUnpackBits - generates code for unpacking bits */ +/*-----------------------------------------------------------------*/ +static char * +genUnpackBits (operand * result, const char *rname, int ptype, iCode * ifx) +{ + int offset = 0; /* result byte offset */ + int rsize; /* result size */ + int rlen = 0; /* remaining bitfield length */ + sym_link *etype; /* bitfield type information */ + unsigned blen; /* bitfield length */ + unsigned bstr; /* bitfield starting bit within byte */ + static char *const accBits[] = { "acc.0", "acc.1", "acc.2", "acc.3", + "acc.4", "acc.5", "acc.6", "acc.7" + }; + + D (emitcode (";", "genUnpackBits")); + + etype = getSpec (operandType (result)); + rsize = getSize (operandType (result)); + blen = SPEC_BLEN (etype); + bstr = SPEC_BSTR (etype); + + if (ifx && blen <= 8) + { + emitPtrByteGet (rname, ptype, FALSE); + if (blen == 1) + { + return accBits[bstr];; + } + else + { + if (blen < 8) + emitcode ("anl", "a,#!constbyte", (((unsigned char) - 1) >> (8 - blen)) << bstr); + return "a"; + } + } + wassert (!ifx); + + /* If the bitfield length is less than a byte */ + if (blen < 8) + { + emitPtrByteGet (rname, ptype, FALSE); + AccRol (8 - bstr); + emitcode ("anl", "a,#!constbyte", ((unsigned char) - 1) >> (8 - blen)); + if (!SPEC_USIGN (etype)) + { + /* signed bitfield */ + symbol *tlbl = newiTempLabel (NULL); + + emitcode ("jnb", "acc.%d,!tlabel", blen - 1, labelKey2num (tlbl->key)); + emitcode ("orl", "a,#0x%02x", (unsigned char) (0xff << blen)); + emitLabel (tlbl); + } + aopPut (result, "a", offset++); + goto finish; + } + + /* Bit field did not fit in a byte. Copy all + but the partial byte at the end. */ + for (rlen = blen; rlen >= 8; rlen -= 8) + { + emitPtrByteGet (rname, ptype, FALSE); + aopPut (result, "a", offset++); + if (rlen > 8) + emitcode ("inc", "%s", rname); + } + + /* Handle the partial byte at the end */ + if (rlen) + { + emitPtrByteGet (rname, ptype, FALSE); + emitcode ("anl", "a,#!constbyte", ((unsigned char) - 1) >> (8 - rlen)); + if (!SPEC_USIGN (etype)) + { + /* signed bitfield */ + symbol *tlbl = newiTempLabel (NULL); + + emitcode ("jnb", "acc.%d,!tlabel", rlen - 1, labelKey2num (tlbl->key)); + emitcode ("orl", "a,#0x%02x", (unsigned char) (0xff << rlen)); + emitLabel (tlbl); + } + aopPut (result, "a", offset++); + } + +finish: + if (offset < rsize) + { + char *source; + + if (SPEC_USIGN (etype)) + source = zero; + else + { + /* signed bitfield: sign extension with 0x00 or 0xff */ + emitcode ("rlc", "a"); + emitcode ("subb", "a,acc"); + + source = "a"; + } + rsize -= offset; + while (rsize--) + aopPut (result, source, offset++); + } + return NULL; +} + + +/*-----------------------------------------------------------------*/ +/* genDataPointerGet - generates code when ptr offset is known */ +/*-----------------------------------------------------------------*/ +static void +genDataPointerGet (operand * left, operand * result, iCode * ic) +{ + const char *l; + int size, offset = 0; + + D (emitcode (";", "genDataPointerGet")); + + aopOp (result, ic, TRUE); + + /* get the string representation of the name */ + l = aopGet (left, 0, FALSE, TRUE) + 1; // remove # + size = AOP_SIZE (result); + while (size--) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + if (AOP_SIZE (result) > 1) + { + dbuf_printf (&dbuf, "(%s + %d)", l, offset); + } + else + { + dbuf_append_str (&dbuf, l); + } + aopPut (result, dbuf_c_str (&dbuf), offset++); + dbuf_destroy (&dbuf); + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genNearPointerGet - emitcode for near pointer fetch */ +/*-----------------------------------------------------------------*/ +static void +genNearPointerGet (operand * left, operand * result, iCode * ic, iCode * pi, iCode * ifx) +{ + asmop *aop = NULL; + reg_info *preg = NULL; + const char *rname; + char *ifxCond = "a"; + sym_link *rtype, *retype; + sym_link *ltype = operandType (left); + + D (emitcode (";", "genNearPointerGet")); + + rtype = operandType (result); + retype = getSpec (rtype); + + aopOp (left, ic, FALSE); + + /* if left is rematerialisable and + result is not bitfield variable type and + the left is pointer to data space i.e + lower 128 bytes of space */ + if (AOP_TYPE (left) == AOP_IMMD && !IS_BITFIELD (retype) && DCL_TYPE (ltype) == POINTER) + { + genDataPointerGet (left, result, ic); + return; + } + + //aopOp (result, ic, FALSE); + aopOp (result, ic, result ? TRUE : FALSE); + + /* if the value is already in a pointer register + then don't need anything more */ + if (!AOP_INPREG (AOP (left))) + { + if (IS_AOP_PREG (left)) + { + // Aha, it is a pointer, just in disguise. + rname = aopGet (left, 0, FALSE, FALSE); + if (EQ (rname, "a")) + { + // It's in pdata or on xstack + rname = AOP (left)->aopu.aop_ptr->name; + emitcode ("mov", "%s,a", rname); + } + else if (*rname != '@') + { + fprintf (stderr, "probable internal error: unexpected rname '%s' @ %s:%d\n", rname, __FILE__, __LINE__); + } + else + { + // Expected case. + emitcode ("mov", "a%s,%s", rname + 1, rname); + rname++; // skip the '@'. + } + } + else + { + /* otherwise get a free pointer register */ + aop = newAsmop (0); + preg = getFreePtr (ic, aop, FALSE); + emitcode ("mov", "%s,%s", preg->name, aopGet (left, 0, FALSE, TRUE)); + rname = preg->name; + } + } + else + rname = aopGet (left, 0, FALSE, FALSE); + + /* if bitfield then unpack the bits */ + if (IS_BITFIELD (retype)) + ifxCond = genUnpackBits (result, rname, POINTER, ifx); + else + { + /* we can just get the values */ + int size = AOP_SIZE (result); + int offset = 0; + + while (size--) + { + if (ifx || IS_AOP_PREG (result) || AOP_TYPE (result) == AOP_STK) + { + emitcode ("mov", "a,@%s", rname); + if (!ifx) + aopPut (result, "a", offset); + } + else + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "@%s", rname); + aopPut (result, dbuf_c_str (&dbuf), offset); + dbuf_destroy (&dbuf); + } + offset++; + if (size || pi) + emitcode ("inc", "%s", rname); + } + } + + /* now some housekeeping stuff */ + if (aop) /* we had to allocate for this iCode */ + { + if (pi) + { + /* post increment present */ + aopPut (left, rname, 0); + } + freeAsmop (NULL, aop, ic, RESULTONSTACK (ic) ? FALSE : TRUE); + } + else + { + /* we did not allocate which means left + already in a pointer register, then + if size > 0 && this could be used again + we have to point it back to where it + belongs */ + if ((AOP_SIZE (result) > 1 && !OP_SYMBOL (left)->remat && (OP_SYMBOL (left)->liveTo > ic->seq || ic->depth)) && !pi) + { + int size = AOP_SIZE (result) - 1; + while (size--) + emitcode ("dec", "%s", rname); + } + } + + if (ifx && !ifx->generated) + { + genIfxJump (ifx, ifxCond, left, NULL, result, ic->next); + } + + /* done */ + freeAsmop (result, NULL, ic, RESULTONSTACK (ic) ? FALSE : TRUE); + freeAsmop (left, NULL, ic, TRUE); + if (pi) + pi->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genPagedPointerGet - emitcode for paged pointer fetch */ +/*-----------------------------------------------------------------*/ +static void +genPagedPointerGet (operand * left, operand * result, iCode * ic, iCode * pi, iCode * ifx) +{ + asmop *aop = NULL; + reg_info *preg = NULL; + const char *rname; + char *ifxCond = "a"; + sym_link *rtype, *retype; + + D (emitcode (";", "genPagedPointerGet")); + + rtype = operandType (result); + retype = getSpec (rtype); + + aopOp (left, ic, FALSE); + + /* if the value is already in a pointer register + then don't need anything more */ + if (!AOP_INPREG (AOP (left))) + { + const char *l; + /* otherwise get a free pointer register */ + aop = newAsmop (0); + preg = getFreePtr (ic, aop, FALSE); + l = aopGet (left, 0, FALSE, TRUE); + if (*l == '@') + emitcode ("mov", "a%s,%s", preg->name, l); + else + emitcode ("mov", "%s,%s", preg->name, l); + rname = preg->name; + } + else + { + rname = aopGet (left, 0, FALSE, FALSE); + } + + aopOp (result, ic, TRUE); + + /* if bitfield then unpack the bits */ + if (IS_BITFIELD (retype)) + { + ifxCond = genUnpackBits (result, rname, PPOINTER, ifx); + } + else + { + /* we can just get the values */ + int size = AOP_SIZE (result); + int offset = 0; + + while (size--) + { + emitcode ("movx", "a,@%s", rname); + if (!ifx) + aopPut (result, "a", offset); + + offset++; + + if (size || pi) + emitcode ("inc", "%s", rname); + } + } + + /* now some housekeeping stuff */ + if (aop) /* we had to allocate for this iCode */ + { + if (pi) + aopPut (left, rname, 0); + } + else + { + /* we did not allocate which means left + already in a pointer register, then + if size > 1 && this could be used again + we have to point it back to where it + belongs */ + if ((AOP_SIZE (result) > 1 && !OP_SYMBOL (left)->remat && (OP_SYMBOL (left)->liveTo > ic->seq || ic->depth)) && !pi) + { + int size = AOP_SIZE (result) - 1; + while (size--) + emitcode ("dec", "%s", rname); + } + } + + /* done */ + freeAsmop (result, NULL, ic, TRUE); + if (aop) + { + freeAsmop (NULL, aop, ic, TRUE); + } + freeAsmop (left, NULL, ic, TRUE); + + if (ifx && !ifx->generated) + { + genIfxJump (ifx, ifxCond, NULL, NULL, NULL, ic->next); + } + + if (pi) + pi->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genFarPointerGet - get value from far space */ +/*-----------------------------------------------------------------*/ +static void +genFarPointerGet (operand * left, operand * result, iCode * ic, iCode * pi, iCode * ifx) +{ + int size, offset; + char *ifxCond = "a"; + sym_link *retype = getSpec (operandType (result)); + + D (emitcode (";", "genFarPointerGet")); + + aopOp (left, ic, FALSE); + loadDptrFromOperand (left, FALSE); + + /* so dptr now contains the address */ + aopOp (result, ic, FALSE); + + /* if bit then unpack */ + if (IS_BITFIELD (retype)) + ifxCond = genUnpackBits (result, "dptr", FPOINTER, ifx); + else + { + size = AOP_SIZE (result); + offset = 0; + + while (size--) + { + emitcode ("movx", "a,@dptr"); + if (!ifx) + aopPut (result, "a", offset++); + if (size || pi) + emitcode ("inc", "dptr"); + } + } + + if (pi && AOP_TYPE (left) != AOP_IMMD && AOP_TYPE (left) != AOP_STR) + { + aopPut (left, "dpl", 0); + aopPut (left, "dph", 1); + pi->generated = 1; + } + + if (ifx && !ifx->generated) + { + genIfxJump (ifx, ifxCond, left, NULL, result, ic->next); + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genCodePointerGet - get value from code space */ +/*-----------------------------------------------------------------*/ +static void +genCodePointerGet (operand * left, operand * result, iCode * ic, iCode * pi, iCode * ifx) +{ + int size, offset; + char *ifxCond = "a"; + sym_link *retype = getSpec (operandType (result)); + + D (emitcode (";", "genCodePointerGet")); + + aopOp (left, ic, FALSE); + loadDptrFromOperand (left, FALSE); + + /* so dptr now contains the address */ + aopOp (result, ic, FALSE); + + /* if bit then unpack */ + if (IS_BITFIELD (retype)) + ifxCond = genUnpackBits (result, "dptr", CPOINTER, ifx); + else + { + size = AOP_SIZE (result); + offset = 0; + + while (size--) + { + emitcode ("clr", "a"); + emitcode ("movc", "a,@a+dptr"); + if (!ifx) + aopPut (result, "a", offset++); + if (size || pi) + emitcode ("inc", "dptr"); + } + } + + if (pi && AOP_TYPE (left) != AOP_IMMD && AOP_TYPE (left) != AOP_STR) + { + aopPut (left, "dpl", 0); + aopPut (left, "dph", 1); + pi->generated = 1; + } + + if (ifx && !ifx->generated) + { + genIfxJump (ifx, ifxCond, left, NULL, result, ic->next); + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGenPointerGet - get value from generic pointer space */ +/*-----------------------------------------------------------------*/ +static void +genGenPointerGet (operand * left, operand * result, iCode * ic, iCode * pi, iCode * ifx) +{ + int size, offset; + char *ifxCond = "a"; + sym_link *retype = getSpec (operandType (result)); + + D (emitcode (";", "genGenPointerGet")); + + aopOp (left, ic, FALSE); + loadDptrFromOperand (left, TRUE); + + /* so dptr-b now contains the address */ + aopOp (result, ic, FALSE); + + /* if bit then unpack */ + if (IS_BITFIELD (retype)) + { + ifxCond = genUnpackBits (result, "dptr", GPOINTER, ifx); + } + else + { + size = AOP_SIZE (result); + offset = 0; + + while (size--) + { + emitcode ("lcall", "__gptrget"); + if (!ifx) + aopPut (result, "a", offset++); + if (size || pi) + emitcode ("inc", "dptr"); + } + } + + if (pi && AOP_TYPE (left) != AOP_IMMD && AOP_TYPE (left) != AOP_STR) + { + aopPut (left, "dpl", 0); + aopPut (left, "dph", 1); + pi->generated = 1; + } + + if (ifx && !ifx->generated) + { + genIfxJump (ifx, ifxCond, left, NULL, result, ic->next); + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genPointerGet - generate code for pointer get */ +/*-----------------------------------------------------------------*/ +static void +genPointerGet (iCode * ic, iCode * pi, iCode * ifx) +{ + operand *left, *result; + sym_link *type, *etype; + int p_type; + + D (emitcode (";", "genPointerGet")); + + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + if (getSize (operandType (result)) > 1) + ifx = NULL; + + /* depending on the type of pointer we need to + move it to the correct pointer register */ + type = operandType (left); + etype = getSpec (type); + /* if left is of type of pointer then it is simple */ + if (IS_PTR (type) && !IS_FUNC (type->next)) + { + p_type = DCL_TYPE (type); + } + else + { + /* we have to go by the storage class */ + p_type = PTR_TYPE (SPEC_OCLS (etype)); + } + + /* special case when cast remat */ + while (IS_SYMOP (left) && OP_SYMBOL (left)->remat && IS_CAST_ICODE (OP_SYMBOL (left)->rematiCode)) + { + left = IC_RIGHT (OP_SYMBOL (left)->rematiCode); + type = operandType (left); + p_type = DCL_TYPE (type); + } + /* now that we have the pointer type we assign + the pointer values */ + switch (p_type) + { + case POINTER: + case IPOINTER: + genNearPointerGet (left, result, ic, pi, ifx); + break; + + case PPOINTER: + genPagedPointerGet (left, result, ic, pi, ifx); + break; + + case FPOINTER: + genFarPointerGet (left, result, ic, pi, ifx); + break; + + case CPOINTER: + genCodePointerGet (left, result, ic, pi, ifx); + break; + + case GPOINTER: + genGenPointerGet (left, result, ic, pi, ifx); + break; + } +} + + +/*-----------------------------------------------------------------*/ +/* genPackBits - generates code for packed bit storage */ +/*-----------------------------------------------------------------*/ +static void +genPackBits (sym_link * etype, operand * right, const char *rname, int p_type) +{ + int offset = 0; /* source byte offset */ + int rlen = 0; /* remaining bitfield length */ + unsigned blen; /* bitfield length */ + unsigned bstr; /* bitfield starting bit within byte */ + int litval; /* source literal value (if AOP_LIT) */ + unsigned char mask; /* bitmask within current byte */ + + D (emitcode (";", "genPackBits")); + + blen = SPEC_BLEN (etype); + bstr = SPEC_BSTR (etype); + + /* If the bitfield length is less than a byte */ + if (blen < 8) + { + mask = ((unsigned char) (0xFF << (blen + bstr)) | (unsigned char) (0xFF >> (8 - bstr))); + + if (AOP_TYPE (right) == AOP_LIT) + { + /* Case with a bitfield length <8 and literal source + */ + litval = (int) ulFromVal (AOP (right)->aopu.aop_lit); + litval <<= bstr; + litval &= (~mask) & 0xff; + emitPtrByteGet (rname, p_type, FALSE); + if ((mask | litval) != 0xff) + emitcode ("anl", "a,#!constbyte", mask); + if (litval) + emitcode ("orl", "a,#!constbyte", litval); + } + else + { + if ((blen == 1) && (p_type != GPOINTER)) + { + /* Case with a bitfield length == 1 and no generic pointer + */ + if (AOP_TYPE (right) == AOP_CRY) + emitcode ("mov", "c,%s", AOP (right)->aopu.aop_dir); + else + { + MOVA (aopGet (right, 0, FALSE, FALSE)); + emitcode ("rrc", "a"); + } + emitPtrByteGet (rname, p_type, FALSE); + emitcode ("mov", "acc.%d,c", bstr); + } + else + { + bool pushedB; + /* Case with a bitfield length < 8 and arbitrary source + */ + MOVA (aopGet (right, 0, FALSE, FALSE)); + /* shift and mask source value */ + AccLsh (bstr); + emitcode ("anl", "a,#!constbyte", (~mask) & 0xff); + + pushedB = pushB (); + /* transfer A to B and get next byte */ + emitPtrByteGet (rname, p_type, TRUE); + + emitcode ("anl", "a,#!constbyte", mask); + emitcode ("orl", "a,b"); + if (p_type == GPOINTER) + emitpop ("b"); + + popB (pushedB); + } + } + + emitPtrByteSet (rname, p_type, "a"); + return; + } + + /* Bit length is greater than 7 bits. In this case, copy */ + /* all except the partial byte at the end */ + for (rlen = blen; rlen >= 8; rlen -= 8) + { + emitPtrByteSet (rname, p_type, aopGet (right, offset++, FALSE, TRUE)); + if (rlen > 8) + emitcode ("inc", "%s", rname); + } + + /* If there was a partial byte at the end */ + if (rlen) + { + mask = (((unsigned char) - 1 << rlen) & 0xff); + + if (AOP_TYPE (right) == AOP_LIT) + { + /* Case with partial byte and literal source + */ + litval = (int) ulFromVal (AOP (right)->aopu.aop_lit); + litval >>= (blen - rlen); + litval &= (~mask) & 0xff; + emitPtrByteGet (rname, p_type, FALSE); + if ((mask | litval) != 0xff) + emitcode ("anl", "a,#!constbyte", mask); + if (litval) + emitcode ("orl", "a,#!constbyte", litval); + } + else + { + bool pushedB; + /* Case with partial byte and arbitrary source + */ + MOVA (aopGet (right, offset++, FALSE, FALSE)); + emitcode ("anl", "a,#!constbyte", (~mask) & 0xff); + + pushedB = pushB (); + /* transfer A to B and get next byte */ + emitPtrByteGet (rname, p_type, TRUE); + + emitcode ("anl", "a,#!constbyte", mask); + emitcode ("orl", "a,b"); + if (p_type == GPOINTER) + emitpop ("b"); + + popB (pushedB); + } + emitPtrByteSet (rname, p_type, "a"); + } +} + +/*-----------------------------------------------------------------*/ +/* genLiteralAssign - assignment of literal */ +/*-----------------------------------------------------------------*/ +static void +genLiteralAssign (operand * result, operand * right, int size, bool (*output_fn) (operand * result, const char *s, int offset)) +{ + unsigned long long lit = 0LL; + int offset; + int accumulator_value = -1; /* -1 denotes: not yet set */ + + if (!IS_FLOAT (operandType (right))) + { + lit = ullFromVal (AOP (right)->aopu.aop_lit); + } + else + { + union + { + float f; + unsigned char c[4]; + } fl; + + fl.f = (float) floatFromVal (AOP (right)->aopu.aop_lit); +#ifdef WORDS_BIGENDIAN + lit = (fl.c[3] << 0) | (fl.c[2] << 8) | (fl.c[1] << 16) | (fl.c[0] << 24); +#else + lit = (fl.c[0] << 0) | (fl.c[1] << 8) | (fl.c[2] << 16) | (fl.c[3] << 24); +#endif + } + + offset = 0; + while (size) + { + /* check whether preloading the accumulator pays off: + + mov direct,#something 3 byte, 2 cycle + mov direct,a 2 byte, 1 cycle + + mov @r0,#something 2 byte, 1 cycle + mov @r0,a 1 byte, 1 cycle + + mov rx,#something 2 byte, 1 cycle + mov rx,a 1 byte, 1 cycle + + clr a 1 byte, 1 cycle + mov a,#something 2 byte, 1 cycle + + (setting bytes in pdata and xdata need the accumulator anyway) + */ + + /* clr a needs an extra byte. If two bytes are zero it starts to pay off + to preload the accumulator */ + int clr_num_bytes_saved = -1 + /* size of clr a */ + (int) ((((lit >> 0) & 0xff) == 0) && (size >= 1)) + + (int) ((((lit >> 8) & 0xff) == 0) && (size >= 2)) + + (int) ((((lit >> 16) & 0xff) == 0) && (size >= 3)) + (int) ((((lit >> 24) & 0xff) == 0) && (size >= 4)); + + /* mov a,#something needs two extra bytes. If three bytes are identical it starts to pay off */ + int mov_num_bytes_saved = -2 + /* size of mov a,#something */ + (int) ((lit & 0xff) == ((lit >> 0) & 0xff) && (size >= 1)) + /* true */ + (int) ((lit & 0xff) == ((lit >> 8) & 0xff) && (size >= 2)) + + (int) ((lit & 0xff) == ((lit >> 16) & 0xff) && (size >= 3)) + + (int) ((lit & 0xff) == ((lit >> 24) & 0xff) && (size >= 4)); + + int num_bytes_to_save_before_using_acc_takes_effect = 1; + + if (optimize.codeSpeed && (AOP_TYPE (result) != AOP_DIR)) + { + /* require an extra byte being saved */ + num_bytes_to_save_before_using_acc_takes_effect++; + } + + /* eventually preload accumulator */ + if ((clr_num_bytes_saved >= num_bytes_to_save_before_using_acc_takes_effect) && + (clr_num_bytes_saved >= mov_num_bytes_saved) && (lit & 0xff) == 0) + { + if (0 != accumulator_value) + { + accumulator_value = 0; +// emitcode ("clr", "a"); + MOVA ("#0x00"); + } + } + else if ((mov_num_bytes_saved >= num_bytes_to_save_before_using_acc_takes_effect) && (mov_num_bytes_saved > clr_num_bytes_saved)) /* preferrably have 0 in acc */ + { + if ((lit & 0xff) != accumulator_value) + { + accumulator_value = lit & 0xff; +// emitcode ("mov", "a,%s", aopGet (right, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + } + } + + /* write byte */ + if ((lit & 0xff) == accumulator_value) + { + /* value in accumulator can be used */ + output_fn (result, "a", offset); + } + else + { + /* otherwise use the normal path that should always work */ + char *r = Safe_strdup (aopGet (right, offset, FALSE, FALSE)); + output_fn (result, r, offset); + Safe_free (r); + } + + /* advance */ + lit >>= 8; + offset++; + size--; + } +} + +/*-----------------------------------------------------------------*/ +/* dataPut - puts a string for a aop and indicates if acc is in use */ +/*-----------------------------------------------------------------*/ +static bool +litPut (operand * result, const char *s, int offset) +{ + emitcode ("mov", "(%s + %d),%s", aopGet (result, 0, FALSE, TRUE) + 1, offset, s); + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* genDataPointerSet - remat pointer to data space */ +/*-----------------------------------------------------------------*/ +static void +genDataPointerSet (operand * right, operand * result, iCode * ic) +{ + int size, offset; + + D (emitcode (";", "genDataPointerSet")); + + aopOp (right, ic, FALSE); + + size = max (AOP_SIZE (right), AOP_SIZE (result)); + if ((size > 1) && IS_OP_LITERAL (right)) + { + genLiteralAssign (result, right, size, litPut); + } + else + { + //remove # + char *l = Safe_strdup (aopGet (result, 0, FALSE, TRUE) + 1); //remove # + for (offset = 0; offset < size; offset++) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + if (size > 1) + dbuf_printf (&dbuf, "(%s + %d)", l, offset); + else + dbuf_append_str (&dbuf, l); + emitcode ("mov", "%s,%s", dbuf_c_str (&dbuf), aopGet (right, offset, FALSE, FALSE)); + dbuf_destroy (&dbuf); + } + Safe_free (l); + } + + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genNearPointerSet - emitcode for near pointer put */ +/*-----------------------------------------------------------------*/ +static void +genNearPointerSet (operand * right, operand * result, iCode * ic, iCode * pi) +{ + asmop *aop = NULL; + reg_info *preg = NULL; + const char *rname; + sym_link *retype, *letype; + sym_link *ptype = operandType (result); + + D (emitcode (";", "genNearPointerSet")); + + retype = getSpec (operandType (right)); + letype = getSpec (ptype); + + aopOp (result, ic, FALSE); + + /* if the result is rematerializable & + in data space & not a bit variable */ + if (AOP_TYPE (result) == AOP_IMMD && DCL_TYPE (ptype) == POINTER && !IS_BITVAR (retype) && !IS_BITVAR (letype)) + { + genDataPointerSet (right, result, ic); + return; + } + + /* if the value is already in a pointer register + then don't need anything more */ + if (!AOP_INPREG (AOP (result))) + { + if (IS_AOP_PREG (result)) + { + // Aha, it is a pointer, just in disguise. + rname = aopGet (result, 0, FALSE, FALSE); + if (EQ (rname, "a")) + { + // It's in pdata or on xstack + rname = AOP (result)->aopu.aop_ptr->name; + emitcode ("mov", "%s,a", rname); + } + else if (*rname != '@') + { + fprintf (stderr, "probable internal error: unexpected rname @ %s:%d\n", __FILE__, __LINE__); + } + else + { + // Expected case. + emitcode ("mov", "a%s,%s", rname + 1, rname); + rname++; // skip the '@'. + } + } + else + { + /* otherwise get a free pointer register */ + aop = newAsmop (0); + preg = getFreePtr (ic, aop, FALSE); + emitcode ("mov", "%s,%s", preg->name, aopGet (result, 0, FALSE, TRUE)); + rname = preg->name; + } + } + else + { + rname = aopGet (result, 0, FALSE, FALSE); + } + + aopOp (right, ic, FALSE); + + rname = Safe_strdup (rname); + /* if bitfield then unpack the bits */ + if (IS_BITFIELD (retype) || IS_BITFIELD (letype)) + genPackBits ((IS_BITFIELD (retype) ? retype : letype), right, rname, POINTER); + else + { + /* we can just get the values */ + int size = AOP_SIZE (right); + int offset = 0; + + while (size--) + { + const char *l = aopGet (right, offset, FALSE, TRUE); + if ((*l == '@') || (EQ (l, "acc"))) + { + MOVA (l); + emitcode ("mov", "@%s,a", rname); + } + else + emitcode ("mov", "@%s,%s", rname, l); + if (size || pi) + emitcode ("inc", "%s", rname); + offset++; + } + } + + /* now some housekeeping stuff */ + if (aop) /* we had to allocate for this iCode */ + { + if (pi) + aopPut (result, rname, 0); + freeAsmop (NULL, aop, ic, TRUE); + } + else + { + /* we did not allocate which means left + already in a pointer register, then + if size > 0 && this could be used again + we have to point it back to where it + belongs */ + if ((AOP_SIZE (right) > 1 && !OP_SYMBOL (result)->remat && (OP_SYMBOL (result)->liveTo > ic->seq || ic->depth)) && !pi) + { + int size = AOP_SIZE (right) - 1; + while (size--) + emitcode ("dec", "%s", rname); + } + } + Safe_free ((void *) rname); + + /* done */ + if (pi) + pi->generated = 1; + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genPagedPointerSet - emitcode for Paged pointer put */ +/*-----------------------------------------------------------------*/ +static void +genPagedPointerSet (operand * right, operand * result, iCode * ic, iCode * pi) +{ + asmop *aop = NULL; + reg_info *preg = NULL; + const char *rname; + sym_link *retype, *letype; + + D (emitcode (";", "genPagedPointerSet")); + + retype = getSpec (operandType (right)); + letype = getSpec (operandType (result)); + + aopOp (result, ic, FALSE); + + /* if the value is already in a pointer register + then don't need anything more */ + if (!AOP_INPREG (AOP (result))) + { + if (IS_AOP_PREG (result)) + { + // Aha, it is a pointer, just in disguise. + rname = aopGet (result, 0, FALSE, FALSE); + if (*rname != '@') + { + fprintf (stderr, "probable internal error: unexpected rname @ %s:%d\n", __FILE__, __LINE__); + } + else + { + // Expected case. + emitcode ("mov", "a%s,%s", rname + 1, rname); + rname++; // skip the '@'. + } + } + else + { + /* otherwise get a free pointer register */ + aop = newAsmop (0); + preg = getFreePtr (ic, aop, FALSE); + emitcode ("mov", "%s,%s", preg->name, aopGet (result, 0, FALSE, TRUE)); + rname = preg->name; + } + } + else + { + rname = aopGet (result, 0, FALSE, FALSE); + } + + aopOp (right, ic, FALSE); + + rname = Safe_strdup (rname); + /* if bitfield then unpack the bits */ + if (IS_BITFIELD (retype) || IS_BITFIELD (letype)) + genPackBits ((IS_BITFIELD (retype) ? retype : letype), right, rname, PPOINTER); + else + { + /* we can just get the values */ + int size = AOP_SIZE (right); + int offset = 0; + + while (size--) + { + MOVA (aopGet (right, offset, FALSE, TRUE)); + emitcode ("movx", "@%s,a", rname); + if (size || pi) + emitcode ("inc", "%s", rname); + offset++; + } + } + + /* now some housekeeping stuff */ + if (aop) /* we had to allocate for this iCode */ + { + if (pi) + aopPut (result, rname, 0); + freeAsmop (NULL, aop, ic, TRUE); + } + else + { + /* we did not allocate which means left + already in a pointer register, then + if size > 1 && this could be used again + we have to point it back to where it + belongs */ + if (AOP_SIZE (right) > 1 && !OP_SYMBOL (result)->remat && (OP_SYMBOL (result)->liveTo > ic->seq || ic->depth) && !pi) + { + int size = AOP_SIZE (right) - 1; + while (size--) + emitcode ("dec", "%s", rname); + } + } + Safe_free ((void *) rname); + + /* done */ + if (pi) + pi->generated = 1; + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genFarPointerSet - set value in far space */ +/*-----------------------------------------------------------------*/ +static void +genFarPointerSet (operand * right, operand * result, iCode * ic, iCode * pi) +{ + int size, offset; + sym_link *retype = getSpec (operandType (right)); + sym_link *letype = getSpec (operandType (result)); + + D (emitcode (";", "genFarPointerSet")); + + aopOp (result, ic, FALSE); + loadDptrFromOperand (result, FALSE); + + /* so dptr now contains the address */ + aopOp (right, ic, FALSE); + + /* if bit then unpack */ + if (IS_BITFIELD (retype) || IS_BITFIELD (letype)) + genPackBits ((IS_BITFIELD (retype) ? retype : letype), right, "dptr", FPOINTER); + else + { + size = AOP_SIZE (right); + offset = 0; + + while (size--) + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + if (offset++ > 0) + emitcode ("inc", "dptr"); + emitcode ("movx", "@dptr,a"); + } + if (pi) + emitcode ("inc", "dptr"); + } + if (pi && AOP_TYPE (result) != AOP_STR && AOP_TYPE (result) != AOP_IMMD) + { + aopPut (result, "dpl", 0); + aopPut (result, "dph", 1); + pi->generated = 1; + } + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGenPointerSet - set value from generic pointer space */ +/*-----------------------------------------------------------------*/ +static void +genGenPointerSet (operand * right, operand * result, iCode * ic, iCode * pi) +{ + int size, offset; + sym_link *retype = getSpec (operandType (right)); + sym_link *letype = getSpec (operandType (result)); + + D (emitcode (";", "genGenPointerSet")); + + aopOp (result, ic, FALSE); + loadDptrFromOperand (result, TRUE); + + /* so dptr-b now contains the address */ + aopOp (right, ic, FALSE); + + /* if bit then unpack */ + if (IS_BITFIELD (retype) || IS_BITFIELD (letype)) + { + genPackBits ((IS_BITFIELD (retype) ? retype : letype), right, "dptr", GPOINTER); + } + else + { + size = AOP_SIZE (right); + offset = 0; + + while (size--) + { + MOVA (aopGet (right, offset++, FALSE, FALSE)); + emitcode ("lcall", "__gptrput"); + if (size || pi) + emitcode ("inc", "dptr"); + } + } + + if (pi && AOP_TYPE (result) != AOP_STR && AOP_TYPE (result) != AOP_IMMD) + { + aopPut (result, "dpl", 0); + aopPut (result, "dph", 1); + pi->generated = 1; + } + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genPointerSet - stores the value into a pointer location */ +/*-----------------------------------------------------------------*/ +static void +genPointerSet (iCode * ic, iCode * pi) +{ + operand *right, *result; + sym_link *type, *etype; + int p_type; + + D (emitcode (";", "genPointerSet")); + + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + /* depending on the type of pointer we need to + move it to the correct pointer register */ + type = operandType (result); + etype = getSpec (type); + /* if left is of type of pointer then it is simple */ + if (IS_PTR (type) && !IS_FUNC (type->next)) + { + p_type = DCL_TYPE (type); + } + else + { + /* we have to go by the storage class */ + p_type = PTR_TYPE (SPEC_OCLS (etype)); + } + + /* special case when cast remat */ + while (IS_SYMOP (result) && OP_SYMBOL (result)->remat && + IS_CAST_ICODE (OP_SYMBOL (result)->rematiCode) && + !IS_BITFIELD (getSpec (operandType (result)))) + { + result = IC_RIGHT (OP_SYMBOL (result)->rematiCode); + type = operandType (result); + p_type = DCL_TYPE (type); + } + + /* now that we have the pointer type we assign + the pointer values */ + switch (p_type) + { + case POINTER: + case IPOINTER: + genNearPointerSet (right, result, ic, pi); + break; + + case PPOINTER: + genPagedPointerSet (right, result, ic, pi); + break; + + case FPOINTER: + genFarPointerSet (right, result, ic, pi); + break; + + case GPOINTER: + genGenPointerSet (right, result, ic, pi); + break; + + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "genPointerSet: illegal pointer type"); + } +} + +/*-----------------------------------------------------------------*/ +/* genIfx - generate code for Ifx statement */ +/*-----------------------------------------------------------------*/ +static void +genIfx (iCode * ic, iCode * popIc) +{ + operand *cond = IC_COND (ic); + int isbit = 0; + char *dup = NULL; + + D (emitcode (";", "genIfx")); + + aopOp (cond, ic, FALSE); + + /* get the value into acc */ + if (AOP_TYPE (cond) != AOP_CRY) + { + toBoolean (cond); + } + else + { + isbit = 1; + if (AOP (cond)->aopu.aop_dir) + dup = Safe_strdup (AOP (cond)->aopu.aop_dir); + } + + /* the result is now in the accumulator or a directly addressable bit */ + freeAsmop (cond, NULL, ic, TRUE); + + /* if the condition is a bit variable */ + if (isbit && dup) + genIfxJump (ic, dup, NULL, NULL, NULL, popIc); + else if (isbit && IS_OP_ACCUSE (cond)) + genIfxJump (ic, "c", NULL, NULL, NULL, popIc); + else if (isbit && IS_ITEMP (cond) && SPIL_LOC (cond)) + genIfxJump (ic, SPIL_LOC (cond)->rname, NULL, NULL, NULL, popIc); + else if (isbit && !IS_ITEMP (cond)) + genIfxJump (ic, OP_SYMBOL (cond)->rname, NULL, NULL, NULL, popIc); + else + genIfxJump (ic, "a", NULL, NULL, NULL, popIc); + + if (dup) + Safe_free (dup); + + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genAddrOf - generates code for address of */ +/*-----------------------------------------------------------------*/ +static void +genAddrOf (iCode * ic) +{ + symbol *sym = OP_SYMBOL (IC_LEFT (ic)); + int size, offset; + + D (emitcode (";", "genAddrOf")); + + aopOp (IC_RESULT (ic), ic, FALSE); + + /* if the operand is on the stack then we + need to get the stack offset of this + variable */ + if (sym->onStack) + { + int stack_offset = stackoffset (sym); + + /* if it has an offset then we need to compute it */ + if (stack_offset) + { + if ((abs (stack_offset) == 1) && !AOP_NEEDSACC (IC_RESULT (ic)) && !isOperandVolatile (IC_RESULT (ic), FALSE)) + { + aopPut (IC_RESULT (ic), SYM_BP (sym), 0); + if (stack_offset > 0) + emitcode ("inc", "%s", aopGet (IC_RESULT (ic), LSB, FALSE, FALSE)); + else + emitcode ("dec", "%s", aopGet (IC_RESULT (ic), LSB, FALSE, FALSE)); + } + else + { + emitcode ("mov", "a,%s", SYM_BP (sym)); + emitcode ("add", "a,#!constbyte", stack_offset & 0xff); + aopPut (IC_RESULT (ic), "a", 0); + } + } + else + { + /* we can just move _bp */ + aopPut (IC_RESULT (ic), SYM_BP (sym), 0); + } + /* fill the result with zero */ + size = AOP_SIZE (IC_RESULT (ic)) - 1; + + offset = 1; + while (size--) + { + aopPut (IC_RESULT (ic), zero, offset++); + } + goto release; + } + + /* object not on stack then we need the name */ + size = getDataSize (IC_RESULT (ic)); + offset = 0; + + while (size--) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + if (offset) + { + dbuf_printf (&dbuf, "#(%s >> %d)", sym->rname, offset * 8); + } + else + { + dbuf_printf (&dbuf, "#%s", sym->rname); + } + aopPut (IC_RESULT (ic), dbuf_c_str (&dbuf), offset++); + dbuf_destroy (&dbuf); + } + if (opIsGptr (IC_RESULT (ic))) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "#0x%02x", pointerTypeToGPByte (pointerCode (getSpec (operandType (IC_LEFT (ic)))), NULL, NULL)); + aopPut (IC_RESULT (ic), dbuf_c_str (&dbuf), GPTRSIZE - 1); + dbuf_destroy (&dbuf); + } + +release: + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genFarFarAssign - assignment when both are in far space */ +/*-----------------------------------------------------------------*/ +static void +genFarFarAssign (operand * result, operand * right, iCode * ic) +{ + int size = AOP_SIZE (right); + int offset = 0; + + D (emitcode (";", "genFarFarAssign")); + + /* first push the right side on to the stack */ + while (size--) + { + MOVA (aopGet (right, offset++, FALSE, FALSE)); + emitpush ("acc"); + } + + freeAsmop (right, NULL, ic, FALSE); + /* now assign DPTR to result */ + aopOp (result, ic, FALSE); + size = AOP_SIZE (result); + while (size--) + { + emitpop ("acc"); + aopPut (result, "a", --offset); + } + freeAsmop (result, NULL, ic, FALSE); +} + +/*-----------------------------------------------------------------*/ +/* genAssign - generate code for assignment */ +/*-----------------------------------------------------------------*/ +static void +genAssign (iCode * ic) +{ + operand *result, *right; + int size, offset; + + D (emitcode (";", "genAssign")); + + result = IC_RESULT (ic); + right = IC_RIGHT (ic); + + /* if they are the same */ + if (operandsEqu (result, right) && !isOperandVolatile (result, FALSE) && !isOperandVolatile (right, FALSE)) + return; + + aopOp (right, ic, FALSE); + + /* special case both in far space */ + if (AOP_TYPE (right) == AOP_DPTR && IS_TRUE_SYMOP (result) && isOperandInFarSpace (result)) + { + genFarFarAssign (result, right, ic); + return; + } + + aopOp (result, ic, TRUE); + + /* if they are the same registers */ + if (sameRegs (AOP (right), AOP (result)) && !isOperandVolatile (result, FALSE) && !isOperandVolatile (right, FALSE)) + goto release; + + /* if the result is a bit */ + if (AOP_TYPE (result) == AOP_CRY) + { + assignBit (result, right); + goto release; + } + + /* bit variables done */ + /* general case */ + + size = getDataSize (result); + + if ((size > 1) && (AOP_TYPE (result) != AOP_REG) && /* for registers too? (regression test passes) */ + (AOP_TYPE (right) == AOP_LIT) && !aopPutUsesAcc (result, aopGet (right, 0, FALSE, FALSE), 0)) + { + genLiteralAssign (result, right, size, aopPut); + } + else + { + offset = 0; + while (size--) + { + aopPut (result, aopGet (right, offset, FALSE, FALSE), offset); + offset++; + } + } + adjustArithmeticResult (ic); + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genJumpTab - generates code for jump table */ +/*-----------------------------------------------------------------*/ +static void +genJumpTab (iCode * ic) +{ + operand *cond = IC_JTCOND (ic); + symbol *jtab, *jtablo, *jtabhi; + unsigned int count; + const char *l; + bool useB = FALSE; + + D (emitcode (";", "genJumpTab")); + + count = elementsInSet (IC_JTLABELS (ic)); + + if ((count <= 7) || + (count <= 16 && optimize.codeSpeed) || + options.acall_ajmp) + { + /* This algorithm needs 9 cycles and 7 + 3*n bytes + if the switch argument is in a register. + (6 + 2*n bytes when options.acall_ajmp is set) */ + /* Peephole may not convert ljmp to sjmp or ret + labelIsReturnOnly & labelInRange must check + currPl->ic->op != JUMPTABLE */ + aopOp (cond, ic, FALSE); + /* get the condition into accumulator */ + l = aopGet (cond, 0, FALSE, FALSE); + MOVA (l); + /* multiply by three */ + if ((AOP_TYPE (cond) == AOP_REG) || (IS_AOP_PREG (cond) && !AOP (cond)->paged && !IS_VOLATILE (operandType (cond)))) + { + emitcode ("add", "a,%s", l); + if (options.acall_ajmp == 0) + emitcode ("add", "a,%s", l); + } + else + { + if (options.acall_ajmp == 0) + { + MOVB ("#0x03"); + emitcode ("mul", "ab"); + } + else + { + emitcode ("add", "a,acc"); + } + } + freeAsmop (cond, NULL, ic, TRUE); + + jtab = newiTempLabel (NULL); + emitcode ("mov", "dptr,#!tlabel", labelKey2num (jtab->key)); + emitcode ("jmp", "@a+dptr"); + emitLabel (jtab); + /* now generate the jump labels */ + for (jtab = setFirstItem (IC_JTLABELS (ic)); jtab; jtab = setNextItem (IC_JTLABELS (ic))) + if (options.acall_ajmp) + emitcode ("ajmp", "!tlabel", labelKey2num (jtab->key)); + else + emitcode ("ljmp", "!tlabel", labelKey2num (jtab->key)); + } + else + { + /* this algorithm needs 14 cycles and 14 + 2*n bytes + if the switch argument is in a register. + For n>7 this algorithm may be more compact */ + jtablo = newiTempLabel (NULL); + jtabhi = newiTempLabel (NULL); + + /* get the condition into accumulator. + Using b as temporary storage, if register push/pop is needed */ + aopOp (cond, ic, FALSE); + l = aopGet (cond, 0, FALSE, FALSE); + if ((AOP_TYPE (cond) == AOP_R0 && _G.r0Pushed) || + (AOP_TYPE (cond) == AOP_R1 && _G.r1Pushed) || + EQ (l, "a") || EQ (l, "acc") || + (count > 125 && EQ (l, "dpl")) || + IS_VOLATILE (operandType (cond))) + { + // (MB) what if B is in use??? + wassertl (!BINUSE, "B was in use"); + MOVB (l); + l = "b"; + useB = TRUE; + } + freeAsmop (cond, NULL, ic, TRUE); + MOVA (l); + if (count <= 125) + { + emitcode ("add", "a,#(!tlabel-3-.)", labelKey2num (jtablo->key)); + emitcode ("movc", "a,@a+pc"); + if (EQ (l, "dpl")) + { + emitcode ("xch", "a,dpl"); + } + else + { + emitcode ("mov", "dpl,a"); + MOVA (l); + } + emitcode ("add", "a,#(!tlabel-3-.)", labelKey2num (jtabhi->key)); + emitcode ("movc", "a,@a+pc"); + emitcode ("mov", "dph,a"); + } + else + { + /* this scales up to n<=255, but needs four more bytes + and changes dptr */ + emitcode ("mov", "dptr,#!tlabel", labelKey2num (jtablo->key)); + emitcode ("movc", "a,@a+dptr"); + if (useB) + { + emitcode ("xch", "a,b"); + } + else + { + emitpush ("acc"); + MOVA (l); + } + emitcode ("mov", "dptr,#!tlabel", labelKey2num (jtabhi->key)); + emitcode ("movc", "a,@a+dptr"); + emitcode ("mov", "dph,a"); + if (useB) + emitcode ("mov", "dpl,b"); + else + emitpop ("dpl"); + } + + emitcode ("clr", "a"); + emitcode ("jmp", "@a+dptr"); + + /* now generate jump table, LSB */ + emitLabel (jtablo); + for (jtab = setFirstItem (IC_JTLABELS (ic)); jtab; jtab = setNextItem (IC_JTLABELS (ic))) + emitcode (".db", "!tlabel", labelKey2num (jtab->key)); + + /* now generate jump table, MSB */ + emitLabel (jtabhi); + for (jtab = setFirstItem (IC_JTLABELS (ic)); jtab; jtab = setNextItem (IC_JTLABELS (ic))) + emitcode (".db", "!tlabel>>8", labelKey2num (jtab->key)); + } +} + +/*-----------------------------------------------------------------*/ +/* genCast - gen code for casting */ +/*-----------------------------------------------------------------*/ +static void +genCast (iCode * ic) +{ + operand *result = IC_RESULT (ic); + sym_link *ctype = operandType (IC_LEFT (ic)); + sym_link *rtype = operandType (IC_RIGHT (ic)); + operand *right = IC_RIGHT (ic); + int size, offset; + bool right_boolean; + + D (emitcode (";", "genCast")); + + /* if they are equivalent then do nothing */ + if (operandsEqu (IC_RESULT (ic), IC_RIGHT (ic))) + return; + + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + right_boolean = IS_BOOLEAN (rtype) || IS_BITFIELD (rtype) && SPEC_BLEN (getSpec (rtype)) == 1; + + /* if the result is a bit (and not a bitfield) */ + if (IS_BOOLEAN (OP_SYMBOL (result)->type) && !right_boolean) + { + assignBit (result, right); + goto release; + } + + /* if they are the same size : or less */ + if (AOP_SIZE (result) <= AOP_SIZE (right)) + { + /* if they are in the same place */ + if (sameRegs (AOP (right), AOP (result))) + goto release; + + /* if they are in different places then copy */ + size = AOP_SIZE (result); + offset = 0; + while (size--) + { + aopPut (result, aopGet (right, offset, FALSE, FALSE), offset); + offset++; + } + goto release; + } + + /* if the either is of type pointer */ + if ((IS_PTR (ctype) || IS_PTR (rtype)) && !IS_INTEGRAL (rtype)) + { + int p_type; + sym_link *type = operandType (right); + sym_link *etype = getSpec (type); + + /* pointer to generic pointer or long */ + if (AOP_SIZE (result) >= GPTRSIZE) + { + if (IS_PTR (type)) + { + p_type = DCL_TYPE (type); + } + else + { + if (SPEC_SCLS (etype) == S_REGISTER) + { + // let's assume it is a generic pointer + p_type = GPOINTER; + } + else + { + /* we have to go by the storage class */ + p_type = PTR_TYPE (SPEC_OCLS (etype)); + } + } + + /* the first two bytes are known */ + size = GPTRSIZE - 1; + offset = 0; + while (size--) + { + aopPut (result, aopGet (right, offset, FALSE, FALSE), offset); + offset++; + } + /* the third byte depending on type */ + { + int gpVal; + + /* If there will be no loss of precision, handle generic */ + /* pointer as special case to avoid generating warning */ + if (p_type == GPOINTER && AOP_SIZE (result) >= GPTRSIZE) + gpVal = -1; + else + gpVal = pointerTypeToGPByte (p_type, NULL, NULL); + + if (gpVal == -1) + { + // pointerTypeToGPByte will have warned, just copy. + aopPut (result, aopGet (right, offset, FALSE, FALSE), offset); + } + else + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "#0x%02x", gpVal); + aopPut (result, dbuf_c_str (&dbuf), GPTRSIZE - 1); + dbuf_destroy (&dbuf); + } + } + + /* 8051 uses unsigned address spaces, so no sign extension needed. */ + /* Pad the remaining bytes of the result (if any) with 0. */ + size = AOP_SIZE (result) - GPTRSIZE; + offset = GPTRSIZE; + while (size--) + { + aopPut (result, zero, offset++); + } + goto release; + } + + /* just copy the pointers */ + size = AOP_SIZE (result); + offset = 0; + while (size--) + { + aopPut (result, aopGet (right, offset, FALSE, FALSE), offset); + offset++; + } + goto release; + } + + /* so we now know that the size of destination is greater + than the size of the source */ + /* we move to result for the size of source */ + size = AOP_SIZE (right); + offset = 0; + while (size--) + { + aopPut (result, aopGet (right, offset, FALSE, FALSE), offset); + offset++; + } + + /* now depending on the sign of the source && destination */ + size = AOP_SIZE (result) - AOP_SIZE (right); + /* if unsigned or not an integral type */ + if (!IS_SPEC (rtype) || SPEC_USIGN (rtype) || AOP_TYPE (right) == AOP_CRY) + { + while (size--) + { + aopPut (result, zero, offset++); + } + } + else + { + /* we need to extend the sign :{ */ + MOVA (aopGet (right, AOP_SIZE (right) - 1, FALSE, FALSE)); + emitcode ("rlc", "a"); + emitcode ("subb", "a,acc"); + while (size--) + aopPut (result, "a", offset++); + } + + /* we are done hurray !!!! */ + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genDjnz - generate decrement & jump if not zero instruction */ +/*-----------------------------------------------------------------*/ +static int +genDjnz (iCode * ic, iCode * ifx) +{ + symbol *lbl, *lbl1; + if (!ifx) + return 0; + + /* if the if condition has a false label + then we cannot save */ + if (IC_FALSE (ifx)) + return 0; + + /* if the minus is not of the form a = a - 1 */ + if (!isOperandEqual (IC_RESULT (ic), IC_LEFT (ic)) || !IS_OP_LITERAL (IC_RIGHT (ic))) + return 0; + + if (operandLitValue (IC_RIGHT (ic)) != 1) + return 0; + + /* if the size of this greater than one then no saving */ + if (getSize (operandType (IC_RESULT (ic))) > 1) + return 0; + + /* otherwise we can save BIG */ + + popForBranch (ic->next, TRUE); + + D (emitcode (";", "genDjnz")); + + lbl = newiTempLabel (NULL); + lbl1 = newiTempLabel (NULL); + + aopOp (IC_RESULT (ic), ic, FALSE); + + if (AOP_NEEDSACC (IC_RESULT (ic))) + { + /* If the result is accessed indirectly via + * the accumulator, we must explicitly write + * it back after the decrement. + */ + const char *rByte = aopGet (IC_RESULT (ic), 0, FALSE, FALSE); + + if (!EQ (rByte, "a")) + { + /* Something is hopelessly wrong */ + fprintf (stderr, "*** warning: internal error at %s:%d\n", __FILE__, __LINE__); + /* We can just give up; the generated code will be inefficient, + * but what the hey. + */ + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + return 0; + } + emitcode ("dec", "%s", rByte); + aopPut (IC_RESULT (ic), rByte, 0); + emitcode ("jnz", "!tlabel", labelKey2num (lbl->key)); + } + else if (IS_AOP_PREG (IC_RESULT (ic))) + { + emitcode ("dec", "%s", aopGet (IC_RESULT (ic), 0, FALSE, FALSE)); + MOVA (aopGet (IC_RESULT (ic), 0, FALSE, FALSE)); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + ifx->generated = 1; + emitcode ("jnz", "!tlabel", labelKey2num (lbl->key)); + } + else + { + emitcode ("djnz", "%s,!tlabel", aopGet (IC_RESULT (ic), 0, FALSE, FALSE), labelKey2num (lbl->key)); + } + emitcode ("sjmp", "!tlabel", labelKey2num (lbl1->key)); + emitLabel (lbl); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_TRUE (ifx)->key)); + emitLabel (lbl1); + + if (!ifx->generated) + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + ifx->generated = 1; + return 1; +} + +/*-----------------------------------------------------------------*/ +/* genReceive - generate code for a receive iCode */ +/*-----------------------------------------------------------------*/ +static void +genReceive (iCode * ic) +{ + int size = getSize (operandType (IC_RESULT (ic))); + int offset = 0; + + D (emitcode (";", "genReceive")); + + if (ic->argreg == 1) + { + /* first parameter */ + if ((isOperandInFarSpace (IC_RESULT (ic)) || + isOperandInPagedSpace (IC_RESULT (ic))) && (OP_SYMBOL (IC_RESULT (ic))->isspilt || IS_TRUE_SYMOP (IC_RESULT (ic)))) + { + reg_info *tempRegs[8]; + int receivingA = 0; + int roffset = 0; + + for (offset = 0; offset < size; offset++) + if (EQ (fReturn[offset], "a")) + receivingA = 1; + + if (!receivingA) + { + if (size == 1 || getTempRegs (tempRegs, size - 1, ic)) + { + for (offset = size - 1; offset > 0; offset--) + emitcode ("mov", "%s,%s", tempRegs[roffset++]->name, fReturn[offset]); + emitcode ("mov", "a,%s", fReturn[0]); + _G.accInUse++; + aopOp (IC_RESULT (ic), ic, FALSE); + _G.accInUse--; + aopPut (IC_RESULT (ic), "a", offset); + for (offset = 1; offset < size; offset++) + aopPut (IC_RESULT (ic), tempRegs[--roffset]->name, offset); + goto release; + } + } + else + { + if (getTempRegs (tempRegs, size, ic)) + { + for (offset = 0; offset < size; offset++) + emitcode ("mov", "%s,%s", tempRegs[offset]->name, fReturn[offset]); + aopOp (IC_RESULT (ic), ic, FALSE); + for (offset = 0; offset < size; offset++) + aopPut (IC_RESULT (ic), tempRegs[offset]->name, offset); + goto release; + } + } + + offset = fReturnSizeMCS51 - size; + while (size--) + { + emitpush ((!EQ (fReturn[fReturnSizeMCS51 - offset - 1], "a") ? fReturn[fReturnSizeMCS51 - offset - 1] : "acc")); + offset++; + } + aopOp (IC_RESULT (ic), ic, FALSE); + size = AOP_SIZE (IC_RESULT (ic)); + offset = 0; + while (size--) + { + emitpop ("acc"); + aopPut (IC_RESULT (ic), "a", offset++); + } + } + else + { + _G.accInUse++; + aopOp (IC_RESULT (ic), ic, FALSE); + _G.accInUse--; + assignResultValue (IC_RESULT (ic), NULL); + } + } + else if (ic->argreg > 12) + { + /* bit parameters */ + reg_info *reg = OP_SYMBOL (IC_RESULT (ic))->regs[0]; + + BitBankUsed = 1; + if (!reg || reg->rIdx != ic->argreg - 5) + { + aopOp (IC_RESULT (ic), ic, FALSE); + emitcode ("mov", "c,%s", rb1regs[ic->argreg - 5]); + outBitC (IC_RESULT (ic)); + } + } + else + { + /* other parameters */ + int rb1off; + aopOp (IC_RESULT (ic), ic, FALSE); + rb1off = ic->argreg; + while (size--) + { + aopPut (IC_RESULT (ic), rb1regs[rb1off++ - 5], offset++); + } + } + +release: + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genDummyRead - generate code for dummy read of volatiles */ +/*-----------------------------------------------------------------*/ +static void +genDummyRead (iCode * ic) +{ + operand *op; + int size, offset; + + D (emitcode (";", "genDummyRead")); + + op = IC_RIGHT (ic); + if (op && IS_SYMOP (op)) + { + aopOp (op, ic, FALSE); + + /* if the result is a bit */ + if (AOP_TYPE (op) == AOP_CRY) + emitcode ("mov", "c,%s", AOP (op)->aopu.aop_dir); + else + { + /* bit variables done */ + /* general case */ + size = AOP_SIZE (op); + offset = 0; + while (size--) + { + MOVA (aopGet (op, offset, FALSE, FALSE)); + offset++; + } + } + + freeAsmop (op, NULL, ic, TRUE); + } + + op = IC_LEFT (ic); + if (op && IS_SYMOP (op)) + { + aopOp (op, ic, FALSE); + + /* if the result is a bit */ + if (AOP_TYPE (op) == AOP_CRY) + emitcode ("mov", "c,%s", AOP (op)->aopu.aop_dir); + else + { + /* bit variables done */ + /* general case */ + size = AOP_SIZE (op); + offset = 0; + while (size--) + { + MOVA (aopGet (op, offset, FALSE, FALSE)); + offset++; + } + } + + freeAsmop (op, NULL, ic, TRUE); + } +} + +/*-----------------------------------------------------------------*/ +/* genCritical - generate code for start of a critical sequence */ +/*-----------------------------------------------------------------*/ +static void +genCritical (iCode * ic) +{ + symbol *tlbl = newiTempLabel (NULL); + + D (emitcode (";", "genCritical")); + + if (IC_RESULT (ic)) + { + aopOp (IC_RESULT (ic), ic, TRUE); + aopPut (IC_RESULT (ic), one, 0); /* save old ea in an operand */ + emitcode ("jbc", "ea,!tlabel", labelKey2num (tlbl->key)); /* atomic test & clear */ + aopPut (IC_RESULT (ic), zero, 0); + emitLabel (tlbl); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + else + { + emitcode ("setb", "c"); + emitcode ("jbc", "ea,!tlabel", labelKey2num (tlbl->key)); /* atomic test & clear */ + emitcode ("clr", "c"); + emitLabel (tlbl); + emitpush ("psw"); /* save old ea via c in psw on top of stack */ + } +} + +/*-----------------------------------------------------------------*/ +/* genEndCritical - generate code for end of a critical sequence */ +/*-----------------------------------------------------------------*/ +static void +genEndCritical (iCode * ic) +{ + D (emitcode (";", "genEndCritical")); + + if (IC_RIGHT (ic)) + { + aopOp (IC_RIGHT (ic), ic, FALSE); + if (AOP_TYPE (IC_RIGHT (ic)) == AOP_CRY) + { + emitcode ("mov", "c,%s", IC_RIGHT (ic)->aop->aopu.aop_dir); + emitcode ("mov", "ea,c"); + } + else + { + if (AOP_TYPE (IC_RIGHT (ic)) != AOP_DUMMY) + MOVA (aopGet (IC_RIGHT (ic), 0, FALSE, FALSE)); + emitcode ("rrc", "a"); + emitcode ("mov", "ea,c"); + } + freeAsmop (IC_RIGHT (ic), NULL, ic, TRUE); + } + else + { + emitpop ("psw"); /* restore ea via c in psw on top of stack */ + emitcode ("mov", "ea,c"); + } +} + +/*-----------------------------------------------------------------*/ +/* gen51Code - generate code for 8051 based controllers */ +/*-----------------------------------------------------------------*/ +void +gen51Code (iCode * lic) +{ + iCode *ic; + int cln = 0; +#ifdef _DEBUG + int cseq = 0; +#endif + + _G.currentFunc = NULL; + + /* print the allocation information */ + if (allocInfo && currFunc) + printAllocInfo (currFunc, codeOutBuf); + /* if debug information required */ + if (options.debug && currFunc) + { + debugFile->writeFunction (currFunc, lic); + } + /* stack pointer name */ + if (options.useXstack) + spname = "_spx"; + else + spname = "sp"; + + for (ic = lic; ic; ic = ic->next) + { + initGenLineElement (); + + genLine.lineElement.ic = ic; + + if (ic->lineno && cln != ic->lineno) + { + if (options.debug) + { + debugFile->writeCLine (ic); + } + if (!options.noCcodeInAsm) + { + emitcode (";", "%s:%d: %s", ic->filename, ic->lineno, printCLine (ic->filename, ic->lineno)); + } + cln = ic->lineno; + } +#ifdef _DEBUG + if (ic->seqPoint && ic->seqPoint != cseq) + { + emitcode (";", "sequence point %d", ic->seqPoint); + cseq = ic->seqPoint; + } +#endif + if (options.iCodeInAsm) + { + char regsInUse[80]; + int i; + const char *iLine; + +#if 0 + for (i = 0; i < 8; i++) + { + sprintf (®sInUse[i], "%c", ic->riu & (1 << i) ? i + '0' : '-'); /* show riu */ + regsInUse[i] = 0; + } +#else + strcpy (regsInUse, "--------"); + for (i = 0; i < 8; i++) + { + if (bitVectBitValue (ic->rMask, i)) + { + int offset = regs8051[i].offset; + regsInUse[offset] = offset + '0'; /* show rMask */ + } + } +#endif + iLine = printILine (ic); + emitcode (";", "[%s] ic:%d: %s", regsInUse, ic->seq, iLine); + dbuf_free (iLine); + } + /* if the result is marked as + spilt and rematerializable or code for + this has already been generated then + do nothing */ + if (resultRemat (ic) || ic->generated) + continue; + + /* depending on the operation */ + switch (ic->op) + { + case '!': + genNot (ic); + break; + + case '~': + genCpl (ic); + break; + + case UNARYMINUS: + genUminus (ic); + break; + + case IPUSH: + genIpush (ic); + break; + + case IPOP: + { + iCode *ifxIc, *popIc; + bool CommonRegs = FALSE; + + /* IPOP happens only when trying to restore a + spilt live range, if there is an ifx statement + following this pop (or several) then the if statement might + be using some of the registers being popped which + would destroy the contents of the register so + we need to check for this condition and handle it */ + for (ifxIc = ic->next; ifxIc && ifxIc->op == IPOP; ifxIc = ifxIc->next); + for (popIc = ic; popIc && popIc->op == IPOP; popIc = popIc->next) + CommonRegs |= (ifxIc && ifxIc->op == IFX && !ifxIc->generated && regsInCommon (IC_LEFT (popIc), IC_COND (ifxIc))); + if (CommonRegs) + genIfx (ifxIc, ic); + else + genIpop (ic); + } + break; + + case CALL: + genCall (ic); + break; + + case PCALL: + genPcall (ic); + break; + + case FUNCTION: + genFunction (ic); + break; + + case ENDFUNCTION: + genEndFunction (ic); + break; + + case RETURN: + genRet (ic); + break; + + case LABEL: + genLabel (ic); + break; + + case GOTO: + genGoto (ic); + break; + + case '+': + genPlus (ic); + break; + + case '-': + if (!genDjnz (ic, ifxForOp (IC_RESULT (ic), ic))) + genMinus (ic); + break; + + case '*': + genMult (ic); + break; + + case '/': + genDiv (ic); + break; + + case '%': + genMod (ic); + break; + + case '>': + genCmpGt (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case '<': + genCmpLt (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case LE_OP: + case GE_OP: + case NE_OP: + + /* note these two are xlated by algebraic equivalence + in decorateType() in SDCCast.c */ + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "got '>=' or '<=' shouldn't have come here"); + break; + + case EQ_OP: + genCmpEq (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case AND_OP: + genAndOp (ic); + break; + + case OR_OP: + genOrOp (ic); + break; + + case '^': + genXor (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case '|': + genOr (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case BITWISEAND: + genAnd (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case INLINEASM: + genInline (ic); + break; + + case RRC: + genRRC (ic); + break; + + case RLC: + genRLC (ic); + break; + + case GETHBIT: + assert (0); + break; + + case GETABIT: + genGetAbit (ic); + break; + + case GETBYTE: + genGetByte (ic); + break; + + case GETWORD: + genGetWord (ic); + break; + + case LEFT_OP: + genLeftShift (ic); + break; + + case RIGHT_OP: + genRightShift (ic); + break; + + case GET_VALUE_AT_ADDRESS: + genPointerGet (ic, hasInc (IC_LEFT (ic), ic, getSize (operandType (IC_RESULT (ic)))), ifxForOp (IC_RESULT (ic), ic)); + break; + + case '=': + if (POINTER_SET (ic)) + genPointerSet (ic, hasInc (IC_RESULT (ic), ic, getSize (operandType (IC_RIGHT (ic))))); + else + genAssign (ic); + break; + + case IFX: + genIfx (ic, NULL); + break; + + case ADDRESS_OF: + genAddrOf (ic); + break; + + case JUMPTABLE: + genJumpTab (ic); + break; + + case CAST: + genCast (ic); + break; + + case RECEIVE: + genReceive (ic); + break; + + case SEND: + addSet (&_G.sendSet, ic); + break; + + case DUMMY_READ_VOLATILE: + genDummyRead (ic); + break; + + case CRITICAL: + genCritical (ic); + break; + + case ENDCRITICAL: + genEndCritical (ic); + break; + + case SWAP: + genSwap (ic); + break; + + default: + ic = ic; + } + } + + genLine.lineElement.ic = NULL; + + /* now we are ready to call the + peep hole optimizer */ + if (!options.nopeep) + peepHole (&genLine.lineHead); + + /* now do the actual printing */ + printLine (genLine.lineHead, codeOutBuf); + + /* destroy the line list */ + destroy_line_list (); +} diff --git a/src/mcs51/gen.h b/src/mcs51/gen.h new file mode 100644 index 0000000..66e3e06 --- /dev/null +++ b/src/mcs51/gen.h @@ -0,0 +1,88 @@ +/*------------------------------------------------------------------------- + gen.h - header file for code generation for 8051 + + 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! +-------------------------------------------------------------------------*/ + +#ifndef SDCCGEN51_H +#define SDCCGEN51_H + +enum +{ + AOP_LIT = 1, + AOP_REG, AOP_DIR, + AOP_DPTR, AOP_R0, AOP_R1, + AOP_STK, AOP_IMMD, AOP_STR, + AOP_CRY, AOP_ACC, AOP_DUMMY +}; + +/* type asmop : a homogenised type for + all the different spaces an operand can be + in */ +typedef struct asmop +{ + short type; + /* can have values + AOP_LIT - operand is a literal value + AOP_REG - is in registers + AOP_DIR - direct just a name + AOP_DPTR - dptr contains address of operand + AOP_R0/R1 - r0/r1 contains address of operand + AOP_STK - should be pushed on stack this + can happen only for the result + AOP_IMMD - immediate value for eg. remateriazable + AOP_CRY - carry contains the value of this + AOP_STR - array of strings + AOP_ACC - result is in the acc:b pair + AOP_DUMMY - read as 0, discard writes + */ + short coff; /* current offset */ + short size; /* total size */ + unsigned code:1; /* is in Code space */ + unsigned paged:1; /* in paged memory */ + unsigned short allocated; /* number of times allocated */ + union + { + value *aop_lit; /* if literal */ + reg_info *aop_reg[8]; /* array of registers */ + char *aop_dir; /* if direct */ + reg_info *aop_ptr; /* either -> to r0 or r1 */ + struct + { + int from_cast_remat; /* cast remat created this : immd2 field used for highest order */ + char *aop_immd1; /* if immediate others are implied */ + char *aop_immd2; /* cast remat will generate this */ + } aop_immd; + symbol *aop_sym; /* symbol when AOP_STK */ + char *aop_str[8]; /* just a string array containing the location */ + } + aopu; +} +asmop; + +void gen51Code (iCode *); +void mcs51_emitDebuggerSymbol (const char *); + +extern char *fReturn8051[]; +extern unsigned fReturnSizeMCS51; +//extern char **fReturn; + +#endif diff --git a/src/mcs51/main.c b/src/mcs51/main.c new file mode 100644 index 0000000..207356c --- /dev/null +++ b/src/mcs51/main.c @@ -0,0 +1,934 @@ +/*------------------------------------------------------------------------- + main.c - mcs51 specific general functions + + Copyright (C) 1998, Sandeep Dutta . sandeep.dutta@usa.net + Copyright (C) 1999, Jean-Louis VERN.jlvern@writeme.com + Copyright (C) 2000, Michael Hope + + 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. +-------------------------------------------------------------------*/ +/* + Note that mlh prepended _mcs51_ on the static functions. Makes + it easier to set a breakpoint using the debugger. +*/ +#include "common.h" +#include "main.h" +#include "ralloc.h" +#include "gen.h" +#include "peep.h" +#include "rtrack.h" +#include "dbuf_string.h" +#include "../SDCCutil.h" + +static char _defaultRules[] = +{ +#include "peeph.rul" +}; + +#define OPTION_SMALL_MODEL "--model-small" +#define OPTION_MEDIUM_MODEL "--model-medium" +#define OPTION_LARGE_MODEL "--model-large" +#define OPTION_HUGE_MODEL "--model-huge" +#define OPTION_STACK_SIZE "--stack-size" + +static OPTION _mcs51_options[] = + { + { 0, OPTION_SMALL_MODEL, NULL, "internal data space is used (default)"}, + { 0, OPTION_MEDIUM_MODEL, NULL, "external paged data space is used"}, + { 0, OPTION_LARGE_MODEL, NULL, "external data space is used"}, + { 0, OPTION_HUGE_MODEL, NULL, "functions are banked, data in external space"}, + { 0, OPTION_STACK_SIZE, &options.stack_size, "Tells the linker to allocate this space for stack", CLAT_INTEGER }, + { 0, "--parms-in-bank1", &options.parms_in_bank1, "use Bank1 for parameter passing"}, + { 0, "--pack-iram", NULL, "Tells the linker to pack variables in internal ram (default)"}, + { 0, "--no-pack-iram", &options.no_pack_iram, "Deprecated: Tells the linker not to pack variables in internal ram"}, + { 0, "--acall-ajmp", &options.acall_ajmp, "Use acall/ajmp instead of lcall/ljmp" }, + { 0, "--no-ret-without-call", &options.no_ret_without_call, "Do not use ret independent of acall/lcall" }, + { 0, NULL } + }; + +/* list of key words used by msc51 */ +static char *_mcs51_keywords[] = +{ + "at", + "banked", + "bit", + "code", + "critical", + "data", + "far", + "generic", + "idata", + "interrupt", + "naked", + "near", + "nonbanked", + "overlay", + "pdata", + "reentrant", + "sbit", + "sfr", + "sfr16", + "sfr32", + "using", + "xdata", + NULL +}; + + + +void mcs51_assignRegisters (ebbIndex *); + +static int regParmFlg = 0; /* determine if we can register a parameter */ +static int regBitParmFlg = 0; /* determine if we can register a bit parameter */ + +static void +_mcs51_init (void) +{ + asm_addTree (&asm_asxxxx_mapping); +} + +static void +_mcs51_reset_regparm (struct sym_link *funcType) +{ + regParmFlg = 0; + regBitParmFlg = 0; +} + +static int +_mcs51_regparm (sym_link * l, bool reentrant) +{ + if (IS_SPEC(l) && (SPEC_NOUN(l) == V_BIT)) + { + /* bit parameters go to b0 thru b7 */ + if (reentrant && (regBitParmFlg < 8)) + { + regBitParmFlg++; + return 12 + regBitParmFlg; + } + return 0; + } + if (options.parms_in_bank1 == 0) + { + /* simple can pass only the first parameter in a register */ + if (regParmFlg) + return 0; + + regParmFlg = 1; + return 1; + } + else + { + int size = getSize(l); + int remain; + + /* first one goes the usual way to DPTR */ + if (regParmFlg == 0) + { + regParmFlg += 4 ; + return 1; + } + /* second one onwards goes to RB1_0 thru RB1_7 */ + remain = regParmFlg - 4; + if (size > (8 - remain)) + { + regParmFlg = 12 ; + return 0; + } + regParmFlg += size ; + return regParmFlg - size + 1; + } +} + +static bool +_mcs51_parseOptions (int *pargc, char **argv, int *i) +{ + /* TODO: allow port-specific command line options to specify + * segment names here. + */ + return FALSE; +} + +static void +_mcs51_finaliseOptions (void) +{ + if (options.noXinitOpt) + port->genXINIT=0; + + switch (options.model) + { + case MODEL_SMALL: + port->mem.default_local_map = data; + port->mem.default_globl_map = data; + port->s.ptr_size = 3; + break; + case MODEL_MEDIUM: + port->mem.default_local_map = pdata; + port->mem.default_globl_map = pdata; + port->s.ptr_size = 3; + break; + case MODEL_LARGE: + case MODEL_HUGE: + port->mem.default_local_map = xdata; + port->mem.default_globl_map = xdata; + port->s.ptr_size = 3; + break; + default: + port->mem.default_local_map = data; + port->mem.default_globl_map = data; + break; + } + + if (options.parms_in_bank1) + addSet(&preArgvSet, Safe_strdup("-DSDCC_PARMS_IN_BANK1")); + + /* mcs51 has an assembly coded float library that's almost always reentrant */ + if (!options.useXstack) + options.float_rent = 1; + + if (options.omitFramePtr) + port->stack.reent_overhead = 0; + + /* set up external stack location if not explicitly specified */ + if (!options.xstack_loc) + options.xstack_loc = options.xdata_loc; +} + +static void +_mcs51_setDefaultOptions (void) +{ +} + +static const char * +_mcs51_getRegName (const struct reg_info *reg) +{ + if (reg) + return reg->name; + return "err"; +} + +static void +_mcs51_genAssemblerPreamble (FILE * of) +{ + if (options.parms_in_bank1) + { + int i; + for (i=0; i < 8 ; i++ ) + fprintf (of, "\tb1_%d = 0x%x \n", i, 8+i); + } +} + +/* Generate interrupt vector table. */ +static int +_mcs51_genIVT (struct dbuf_s * oBuf, symbol ** interrupts, int maxInterrupts) +{ + int i; + + dbuf_printf (oBuf, "\t%cjmp\t__sdcc_gsinit_startup\n", options.acall_ajmp?'a':'l'); + if((options.acall_ajmp)&&(maxInterrupts)) dbuf_printf (oBuf, "\t.ds\t1\n"); + + /* now for the other interrupts */ + for (i = 0; i < maxInterrupts; i++) + { + if (interrupts[i]) + { + dbuf_printf (oBuf, "\t%cjmp\t%s\n", options.acall_ajmp?'a':'l', interrupts[i]->rname); + if ( i != maxInterrupts - 1 ) + dbuf_printf (oBuf, "\t.ds\t%d\n", options.acall_ajmp?6:5); + } + else + { + dbuf_printf (oBuf, "\treti\n"); + if ( i != maxInterrupts - 1 ) + dbuf_printf (oBuf, "\t.ds\t7\n"); + } + } + return TRUE; +} + +static void +_mcs51_genExtraAreas(FILE *of, bool hasMain) +{ + tfprintf (of, "\t!area\n", HOME_NAME); + tfprintf (of, "\t!area\n", "GSINIT0 (CODE)"); + tfprintf (of, "\t!area\n", "GSINIT1 (CODE)"); + tfprintf (of, "\t!area\n", "GSINIT2 (CODE)"); + tfprintf (of, "\t!area\n", "GSINIT3 (CODE)"); + tfprintf (of, "\t!area\n", "GSINIT4 (CODE)"); + tfprintf (of, "\t!area\n", "GSINIT5 (CODE)"); + tfprintf (of, "\t!area\n", STATIC_NAME); + tfprintf (of, "\t!area\n", port->mem.post_static_name); + tfprintf (of, "\t!area\n", CODE_NAME); +} + +static void +_mcs51_genInitStartup (FILE *of) +{ + tfprintf (of, "\t!global\n", "__sdcc_gsinit_startup"); + tfprintf (of, "\t!global\n", "__sdcc_program_startup"); + tfprintf (of, "\t!global\n", "__start__stack"); + + if (options.useXstack) + { + tfprintf (of, "\t!global\n", "__sdcc_init_xstack"); + tfprintf (of, "\t!global\n", "__start__xstack"); + } + + // if the port can copy the XINIT segment to XISEG + if (port->genXINIT) + { + port->genXINIT(of); + } + + if (!getenv("SDCC_NOGENRAMCLEAR")) + tfprintf (of, "\t!global\n", "__mcs51_genRAMCLEAR"); +} + + +/* Generate code to copy XINIT to XISEG */ +static void _mcs51_genXINIT (FILE * of) +{ + tfprintf (of, "\t!global\n", "__mcs51_genXINIT"); + + if (!getenv("SDCC_NOGENRAMCLEAR")) + tfprintf (of, "\t!global\n", "__mcs51_genXRAMCLEAR"); +} + + +/* Do CSE estimation */ +static bool cseCostEstimation (iCode *ic, iCode *pdic) +{ + operand *result = IC_RESULT(ic); + sym_link *result_type = operandType(result); + + /* if it is a pointer then return ok for now */ + if (IC_RESULT(ic) && IS_PTR(result_type)) return 1; + + /* if bitwise | add & subtract then no since mcs51 is pretty good at it + so we will cse only if they are local (i.e. both ic & pdic belong to + the same basic block */ + if (IS_BITWISE_OP(ic) || ic->op == '+' || ic->op == '-') + { + /* then if they are the same Basic block then ok */ + if (ic->eBBlockNum == pdic->eBBlockNum) return 1; + else return 0; + } + + /* for others it is cheaper to do the cse */ + return 1; +} + +/* Indicate which extended bit operations this port supports */ +static bool +hasExtBitOp (int op, int size) +{ + if (op == RRC + || op == RLC + || op == GETABIT + || op == GETBYTE + || op == GETWORD + || (op == SWAP && size <= 2) + ) + return TRUE; + else + return FALSE; +} + +/* Indicate the expense of an access to an output storage class */ +static int +oclsExpense (struct memmap *oclass) +{ + if (IN_FARSPACE(oclass)) + return 1; + + return 0; +} + +static bool +_hasNativeMulFor (iCode *ic, sym_link *left, sym_link *right) +{ + return getSize (left) == 1 && getSize (right) == 1; +} + +static int +instructionSize(char *inst, char *op1, char *op2) +{ + #define ISINST(s) (strncmp(inst, (s), sizeof(s)-1) == 0) + #define IS_A(s) (*(s) == 'a' && *(s+1) == '\0') + #define IS_C(s) (*(s) == 'c' && *(s+1) == '\0') + #define IS_Rn(s) (*(s) == 'r' && *(s+1) >= '0' && *(s+1) <= '7') + #define IS_atRi(s) (*(s) == '@' && *(s+1) == 'r') + + /* Based on the current (2003-08-22) code generation for the + small library, the top instruction probability is: + + 57% mov/movx/movc + 6% push + 6% pop + 4% inc + 4% lcall + 4% add + 3% clr + 2% subb + */ + /* mov, push, & pop are the 69% of the cases. Check them first! */ + if (ISINST ("mov")) + { + if (*(inst+3)=='x') return 1; /* movx */ + if (*(inst+3)=='c') return 1; /* movc */ + if (IS_C (op1) || IS_C (op2)) return 2; + if (IS_A (op1)) + { + if (IS_Rn (op2) || IS_atRi (op2)) return 1; + return 2; + } + if (IS_Rn(op1) || IS_atRi(op1)) + { + if (IS_A(op2)) return 1; + return 2; + } + if (strcmp (op1, "dptr") == 0) return 3; + if (IS_A (op2) || IS_Rn (op2) || IS_atRi (op2)) return 2; + return 3; + } + + if (ISINST ("push")) return 2; + if (ISINST ("pop")) return 2; + + if (ISINST ("lcall")) return 3; + if (ISINST ("ret")) return 1; + if (ISINST ("ljmp")) return 3; + if (ISINST ("sjmp")) return 2; + if (ISINST ("rlc")) return 1; + if (ISINST ("rrc")) return 1; + if (ISINST ("rl")) return 1; + if (ISINST ("rr")) return 1; + if (ISINST ("swap")) return 1; + if (ISINST ("jc")) return 2; + if (ISINST ("jnc")) return 2; + if (ISINST ("jb")) return 3; + if (ISINST ("jnb")) return 3; + if (ISINST ("jbc")) return 3; + if (ISINST ("jmp")) return 1; // always jmp @a+dptr + if (ISINST ("jz")) return 2; + if (ISINST ("jnz")) return 2; + if (ISINST ("cjne")) return 3; + if (ISINST ("mul")) return 1; + if (ISINST ("div")) return 1; + if (ISINST ("da")) return 1; + if (ISINST ("xchd")) return 1; + if (ISINST ("reti")) return 1; + if (ISINST ("nop")) return 1; + if (ISINST ("acall")) return 2; + if (ISINST ("ajmp")) return 2; + + + if (ISINST ("add") || ISINST ("addc") || ISINST ("subb") || ISINST ("xch")) + { + if (IS_Rn(op2) || IS_atRi(op2)) return 1; + return 2; + } + if (ISINST ("inc") || ISINST ("dec")) + { + if (IS_A(op1) || IS_Rn(op1) || IS_atRi(op1)) return 1; + if (strcmp(op1, "dptr") == 0) return 1; + return 2; + } + if (ISINST ("anl") || ISINST ("orl") || ISINST ("xrl")) + { + if (IS_C(op1)) return 2; + if (IS_A(op1)) + { + if (IS_Rn(op2) || IS_atRi(op2)) return 1; + return 2; + } + else + { + if (IS_A(op2)) return 2; + return 3; + } + } + if (ISINST ("clr") || ISINST ("setb") || ISINST ("cpl")) + { + if (IS_A(op1) || IS_C(op1)) return 1; + return 2; + } + if (ISINST ("djnz")) + { + if (IS_Rn(op1)) return 2; + return 3; + } + + /* If the instruction is unrecognized, we shouldn't try to optimize. */ + /* Return a large value to discourage optimization. */ + return 999; +} + +static asmLineNode * +newAsmLineNode (void) +{ + asmLineNode *aln; + + aln = Safe_alloc ( sizeof (asmLineNode)); + aln->size = 0; + aln->regsRead = NULL; + aln->regsWritten = NULL; + + return aln; +} + + +typedef struct mcs51operanddata + { + char name[6]; + int regIdx1; + int regIdx2; + } +mcs51operanddata; + +static mcs51operanddata mcs51operandDataTable[] = + { + {"a", A_IDX, -1}, + {"ab", A_IDX, B_IDX}, + {"ac", CND_IDX, -1}, + {"acc", A_IDX, -1}, + {"ar0", R0_IDX, -1}, + {"ar1", R1_IDX, -1}, + {"ar2", R2_IDX, -1}, + {"ar3", R3_IDX, -1}, + {"ar4", R4_IDX, -1}, + {"ar5", R5_IDX, -1}, + {"ar6", R6_IDX, -1}, + {"ar7", R7_IDX, -1}, + {"b", B_IDX, -1}, + {"c", CND_IDX, -1}, + {"cy", CND_IDX, -1}, + {"dph", DPH_IDX, -1}, + {"dpl", DPL_IDX, -1}, + {"dptr", DPL_IDX, DPH_IDX}, + {"f0", CND_IDX, -1}, + {"f1", CND_IDX, -1}, + {"ov", CND_IDX, -1}, + {"p", CND_IDX, -1}, + {"psw", CND_IDX, -1}, + {"r0", R0_IDX, -1}, + {"r1", R1_IDX, -1}, + {"r2", R2_IDX, -1}, + {"r3", R3_IDX, -1}, + {"r4", R4_IDX, -1}, + {"r5", R5_IDX, -1}, + {"r6", R6_IDX, -1}, + {"r7", R7_IDX, -1}, + }; + +static int +mcs51operandCompare (const void *key, const void *member) +{ + return strcmp((const char *)key, ((mcs51operanddata *)member)->name); +} + +static void +updateOpRW (asmLineNode *aln, char *op, char *optype) +{ + mcs51operanddata *opdat; + char *dot; + + dot = strchr(op, '.'); + if (dot) + *dot = '\0'; + + opdat = bsearch (op, mcs51operandDataTable, + sizeof(mcs51operandDataTable)/sizeof(mcs51operanddata), + sizeof(mcs51operanddata), mcs51operandCompare); + + if (opdat && strchr(optype,'r')) + { + if (opdat->regIdx1 >= 0) + aln->regsRead = bitVectSetBit (aln->regsRead, opdat->regIdx1); + if (opdat->regIdx2 >= 0) + aln->regsRead = bitVectSetBit (aln->regsRead, opdat->regIdx2); + } + if (opdat && strchr(optype,'w')) + { + if (opdat->regIdx1 >= 0) + aln->regsWritten = bitVectSetBit (aln->regsWritten, opdat->regIdx1); + if (opdat->regIdx2 >= 0) + aln->regsWritten = bitVectSetBit (aln->regsWritten, opdat->regIdx2); + } + if (op[0] == '@') + { + if (!strcmp(op, "@r0")) + aln->regsRead = bitVectSetBit (aln->regsRead, R0_IDX); + if (!strcmp(op, "@r1")) + aln->regsRead = bitVectSetBit (aln->regsRead, R1_IDX); + if (strstr(op, "dptr")) + { + aln->regsRead = bitVectSetBit (aln->regsRead, DPL_IDX); + aln->regsRead = bitVectSetBit (aln->regsRead, DPH_IDX); + } + if (strstr(op, "a+")) + aln->regsRead = bitVectSetBit (aln->regsRead, A_IDX); + } +} + +typedef struct mcs51opcodedata + { + char name[6]; + char class[3]; + char pswtype[3]; + char op1type[3]; + char op2type[3]; + } +mcs51opcodedata; + +static mcs51opcodedata mcs51opcodeDataTable[] = + { + {"acall","j", "", "", ""}, + {"add", "", "w", "rw", "r"}, + {"addc", "", "rw", "rw", "r"}, + {"ajmp", "j", "", "", ""}, + {"anl", "", "", "rw", "r"}, + {"cjne", "j", "w", "r", "r"}, + {"clr", "", "", "w", ""}, + {"cpl", "", "", "rw", ""}, + {"da", "", "rw", "rw", ""}, + {"dec", "", "", "rw", ""}, + {"div", "", "w", "rw", ""}, + {"djnz", "j", "", "rw", ""}, + {"inc", "", "", "rw", ""}, + {"jb", "j", "", "r", ""}, + {"jbc", "j", "", "rw", ""}, + {"jc", "j", "", "", ""}, + {"jmp", "j", "", "", ""}, + {"jnb", "j", "", "r", ""}, + {"jnc", "j", "", "", ""}, + {"jnz", "j", "", "", ""}, + {"jz", "j", "", "", ""}, + {"lcall","j", "", "", ""}, + {"ljmp", "j", "", "", ""}, + {"mov", "", "", "w", "r"}, + {"movc", "", "", "w", "r"}, + {"movx", "", "", "w", "r"}, + {"mul", "", "w", "rw", ""}, + {"nop", "", "", "", ""}, + {"orl", "", "", "rw", "r"}, + {"pop", "", "", "w", ""}, + {"push", "", "", "r", ""}, + {"ret", "j", "", "", ""}, + {"reti", "j", "", "", ""}, + {"rl", "", "", "rw", ""}, + {"rlc", "", "rw", "rw", ""}, + {"rr", "", "", "rw", ""}, + {"rrc", "", "rw", "rw", ""}, + {"setb", "", "", "w", ""}, + {"sjmp", "j", "", "", ""}, + {"subb", "", "rw", "rw", "r"}, + {"swap", "", "", "rw", ""}, + {"xch", "", "", "rw", "rw"}, + {"xchd", "", "", "rw", "rw"}, + {"xrl", "", "", "rw", "r"}, + }; + +static int +mcs51opcodeCompare (const void *key, const void *member) +{ + return strcmp((const char *)key, ((mcs51opcodedata *)member)->name); +} + +static asmLineNode * +asmLineNodeFromLineNode (lineNode *ln) +{ + asmLineNode *aln = newAsmLineNode(); + char *op, op1[256], op2[256]; + int opsize; + const char *p; + char inst[8]; + mcs51opcodedata *opdat; + + p = ln->line; + + while (*p && isspace(*p)) p++; + for (op = inst, opsize=1; *p; p++) + { + if (isspace(*p) || *p == ';' || *p == ':' || *p == '=') + break; + else + if (opsize < sizeof(inst)) + *op++ = tolower(*p), opsize++; + } + *op = '\0'; + + if (*p == ';' || *p == ':' || *p == '=') + return aln; + + while (*p && isspace(*p)) p++; + if (*p == '=') + return aln; + + for (op = op1, opsize=1; *p && *p != ','; p++) + { + if (!isspace(*p) && opsize < sizeof(op1)) + *op++ = tolower(*p), opsize++; + } + *op = '\0'; + + if (*p == ',') p++; + for (op = op2, opsize=1; *p && *p != ','; p++) + { + if (!isspace(*p) && opsize < sizeof(op2)) + *op++ = tolower(*p), opsize++; + } + *op = '\0'; + + aln->size = instructionSize(inst, op1, op2); + + aln->regsRead = newBitVect (END_IDX); + aln->regsWritten = newBitVect (END_IDX); + + opdat = bsearch (inst, mcs51opcodeDataTable, + sizeof(mcs51opcodeDataTable)/sizeof(mcs51opcodedata), + sizeof(mcs51opcodedata), mcs51opcodeCompare); + + if (opdat) + { + updateOpRW (aln, op1, opdat->op1type); + updateOpRW (aln, op2, opdat->op2type); + if (strchr(opdat->pswtype,'r')) + aln->regsRead = bitVectSetBit (aln->regsRead, CND_IDX); + if (strchr(opdat->pswtype,'w')) + aln->regsWritten = bitVectSetBit (aln->regsWritten, CND_IDX); + } + + return aln; +} + +static int +getInstructionSize (lineNode *line) +{ + if (!line->aln) + line->aln = (asmLineNodeBase *) asmLineNodeFromLineNode (line); + + return line->aln->size; +} + +static bitVect * +getRegsRead (lineNode *line) +{ + if (!line->aln) + line->aln = (asmLineNodeBase *) asmLineNodeFromLineNode (line); + + return line->aln->regsRead; +} + +static bitVect * +getRegsWritten (lineNode *line) +{ + if (!line->aln) + line->aln = (asmLineNodeBase *) asmLineNodeFromLineNode (line); + + return line->aln->regsWritten; +} + +static const char * models[] = +{ + "small", "small-xstack", "small-stack-auto", "small-xstack-auto", + "medium", "medium-xstack", "medium-stack-auto", "medium-xstack-auto", + "large", "large-xstack", "large-stack-auto", "large-xstack-auto", + "huge", "huge-xstack", "huge-stack-auto", "huge-xstack-auto", +}; + +static const char * +get_model (void) +{ + int index; + + switch (options.model) + { + case MODEL_SMALL: + index = 0; + break; + case MODEL_MEDIUM: + index = 4; + break; + case MODEL_LARGE: + index = 8; + break; + case MODEL_HUGE: + index = 12; + break; + default: + werror (W_UNKNOWN_MODEL, __FILE__, __LINE__); + return "unknown"; + } + if (options.stackAuto) + index += 2; + if (options.useXstack) + index += 1; + return models[index]; +} + +/** $1 is always the basename. + $2 is always the output file. + $3 varies + $l is the list of extra options that should be there somewhere... + MUST be terminated with a NULL. +*/ +static const char *_linkCmd[] = +{ + "sdld", "-nf", "$1", NULL +}; + +/* $3 is replaced by assembler.debug_opts resp. port->assembler.plain_opts */ +static const char *_asmCmd[] = +{ + "sdas8051", "$l", "$3", "$2", "$1.asm", NULL +}; + +static const char * const _libs[] = { "mcs51", STD_LIB, STD_INT_LIB, STD_LONG_LIB, STD_FP_LIB, NULL, }; + +/* Globals */ +PORT mcs51_port = +{ + TARGET_ID_MCS51, + "mcs51", + "MCU 8051", /* Target name */ + NULL, /* Processor name */ + { + glue, + TRUE, /* glue_up_main: Emit glue around main */ + MODEL_SMALL | MODEL_MEDIUM | MODEL_LARGE | MODEL_HUGE, + MODEL_SMALL, + get_model, + }, + { /* Assembler */ + _asmCmd, + NULL, + "-plosgffwy", /* Options with debug */ + "-plosgffw", /* Options without debug */ + 0, + ".asm", + NULL /* no do_assemble function */ + }, + { /* Linker */ + _linkCmd, + NULL, + NULL, + ".rel", + 1, + NULL, /* crt */ + _libs, /* libs */ + }, + { /* Peephole optimizer */ + _defaultRules, + getInstructionSize, + getRegsRead, + getRegsWritten, + mcs51DeadMove, + NULL, + NULL, + NULL, + NULL, + }, + /* Sizes: char, short, int, long, long long, near ptr, far ptr, gptr, func ptr, banked func ptr, bit, float */ + { 1, 2, 2, 4, 8, 1, 2, 3, 2, 3, 1, 4 }, + /* tags for generic pointers */ + { 0x00, 0x40, 0x60, 0x80 }, /* far, near, xstack, code */ + { + "XSTK (PAG,XDATA)", // xstack_name + "STACK (DATA)", // istack_name + "CSEG (CODE)", // code_name + "DSEG (DATA)", // data_name + "ISEG (DATA)", // idata_name + "PSEG (PAG,XDATA)", // pdata_name + "XSEG (XDATA)", // xdata_name + "BSEG (BIT)", // bit_name + "RSEG (ABS,DATA)", // reg_name + "GSINIT (CODE)", // static_name + "OSEG (OVR,DATA)", // overlay_name + "GSFINAL (CODE)", // post_static_name + "HOME (CODE)", // home_name + "XISEG (XDATA)", // xidata_name - initialized xdata + "XINIT (CODE)", // xinit_name - a code copy of xiseg + "CONST (CODE)", // const_name - const data (code or not) + "CABS (ABS,CODE)", // cabs_name - const absolute data (code or not) + "XABS (ABS,XDATA)", // xabs_name - absolute xdata/pdata + "IABS (ABS,DATA)", // iabs_name - absolute idata/data + NULL, // name of segment for initialized variables + NULL, // name of segment for copies of initialized variables in code space + NULL, + NULL, + 1, + 1 // No fancy alignments supported. + }, + { _mcs51_genExtraAreas, NULL }, + { + +1, /* direction (+1 = stack grows up) */ + 0, /* bank_overhead (switch between register banks) */ + 4, /* isr_overhead */ + 1, /* call_overhead (2 for return address - 1 for pre-incrementing push */ + 1, /* reent_overhead */ + 1, /* banked_overhead (switch between code banks) */ + 0 /* sp points directly at last item pushed */ + }, + { -1, FALSE }, + { mcs51_emitDebuggerSymbol }, + { + 256, /* maxCount */ + 2, /* sizeofElement */ + {6,9,15}, /* sizeofMatchJump[] */ + {9,18,36}, /* sizeofRangeCompare[] */ + 4, /* sizeofSubtract */ + 6, /* sizeofDispatch */ + }, + "_", + _mcs51_init, + _mcs51_parseOptions, + _mcs51_options, + NULL, + _mcs51_finaliseOptions, + _mcs51_setDefaultOptions, + mcs51_assignRegisters, + _mcs51_getRegName, + 0, + _mcs51_rtrackUpdate, + _mcs51_keywords, + _mcs51_genAssemblerPreamble, + NULL, /* no genAssemblerEnd */ + _mcs51_genIVT, + _mcs51_genXINIT, + _mcs51_genInitStartup, + _mcs51_reset_regparm, + _mcs51_regparm, + NULL, /* process_pragma */ + NULL, /* getMangledFunctionName */ + _hasNativeMulFor, /* hasNativeMulFor */ + hasExtBitOp, /* hasExtBitOp */ + oclsExpense, /* oclsExpense */ + FALSE, /* use_dw_for_init */ + TRUE, /* little_endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + FALSE, /* No array initializer support. */ + cseCostEstimation, + NULL, /* no builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local statics allowed */ + 0, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; diff --git a/src/mcs51/main.h b/src/mcs51/main.h new file mode 100644 index 0000000..e34fc20 --- /dev/null +++ b/src/mcs51/main.h @@ -0,0 +1,45 @@ +/*------------------------------------------------------------------------- + main.c - mcs51 specific general header file + + Copyright (C) 1998, Sandeep Dutta . sandeep.dutta@usa.net + Copyright (C) 1999, Jean-Louis VERN.jlvern@writeme.com + Copyright (C) 2000, Michael Hope + + 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. +-------------------------------------------------------------------*/ + +#ifndef MAIN_INCLUDE +#define MAIN_INCLUDE + +#include "SDCCgen.h" + +typedef struct asmLineNode + { +#ifdef UNNAMED_STRUCT_TAG + struct asmLineNodeBase; +#else + /* exactly the same members as of struct asmLineNodeBase from SDCCgen.h */ + int size; + bitVect *regsRead; + bitVect *regsWritten; +#endif + } +asmLineNode; + +bool x_parseOptions (char **argv, int *pargc); +void x_setDefaultOptions (void); +void x_finaliseOptions (void); + +#endif diff --git a/src/mcs51/mcs51.vcxproj b/src/mcs51/mcs51.vcxproj new file mode 100644 index 0000000..fa9833a --- /dev/null +++ b/src/mcs51/mcs51.vcxproj @@ -0,0 +1,165 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{9FACDB81-BE66-42D0-95F5-EA2FA3B09065}</ProjectGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="..\..\SDCC.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="..\..\SDCC.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</IntDir>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</IntDir>
+ <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">port</TargetName>
+ <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">port</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <AdditionalIncludeDirectories>..;.;..\..;..\..\support\util;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;_LIB;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeaderOutputFile>.\Release/mcs51.pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\Release/</AssemblerListingLocation>
+ <ObjectFileName>.\Release/</ObjectFileName>
+ <ProgramDataBaseFileName>.\Release/</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Lib>
+ <OutputFile>$(Configuration)\$(TargetFileName)</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Lib>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Release/mcs51.bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..;.;..\..;..\..\support\util;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;_LIB;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <PrecompiledHeaderOutputFile>.\Debug/mcs51.pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\Debug/</AssemblerListingLocation>
+ <ObjectFileName>.\Debug/</ObjectFileName>
+ <ProgramDataBaseFileName>.\Debug/</ProgramDataBaseFileName>
+ <BrowseInformation>true</BrowseInformation>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Lib>
+ <OutputFile>$(Configuration)\$(TargetFileName)</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Lib>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Debug/mcs51.bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="gen.c">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="main.c">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="peep.c">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="ralloc.c">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="rtrack.c">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="gen.h" />
+ <ClInclude Include="main.h" />
+ <ClInclude Include="peep.h" />
+ <ClInclude Include="ralloc.h" />
+ <ClInclude Include="..\..\sdcc_vc.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\config.vcxproj">
+ <Project>{2f87ba6f-8ee1-48d0-9817-6ba30bddb3c1}</Project>
+ <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="peeph.def">
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">gawk -f ../SDCCpeeph.awk %(Identity) >peeph.rul</Command>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">gawk -f ../SDCCpeeph.awk %(Identity) >peeph.rul</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">peeph.rul;%(Outputs)</Outputs>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">peeph.rul;%(Outputs)</Outputs>
+ <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Generating Peephole Rule: peeph.rul</Message>
+ <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Generating Peephole Rule: peeph.rul</Message>
+ </CustomBuild>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file diff --git a/src/mcs51/mcs51.vcxproj.filters b/src/mcs51/mcs51.vcxproj.filters new file mode 100644 index 0000000..aa1f754 --- /dev/null +++ b/src/mcs51/mcs51.vcxproj.filters @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{bf0fcc72-c97b-453e-9ddb-0df03c205510}</UniqueIdentifier>
+ <Extensions>cpp;c;cxx;rc;def;r;odl;idl;hpj;bat</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{11a5d289-5894-4486-bee0-4d6dce084246}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl</Extensions>
+ </Filter>
+ <Filter Include="Custom Build">
+ <UniqueIdentifier>{fa0a3371-90ba-4525-8964-2e8712494a31}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="gen.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="peep.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ralloc.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="rtrack.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="gen.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="main.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="peep.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ralloc.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\sdcc_vc.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="peeph.def">
+ <Filter>Custom Build</Filter>
+ </CustomBuild>
+ </ItemGroup>
+</Project>
\ No newline at end of file diff --git a/src/mcs51/peep.c b/src/mcs51/peep.c new file mode 100644 index 0000000..0a8db59 --- /dev/null +++ b/src/mcs51/peep.c @@ -0,0 +1,773 @@ +/*------------------------------------------------------------------------- + peep.c - source file for peephole optimizer helper functions + + Written By - Bernhard Held + + 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 <ctype.h> +#include "common.h" +#include "ralloc.h" +#include "gen.h" + +#define D(x) x +#define DEADMOVEERROR() do {werror(E_INTERNAL_ERROR, __FILE__, __LINE__, "error in deadmove");} while(0) + +typedef enum +{ + S4O_FOUNDOPCODE, + S4O_PUSHPOP, + S4O_CONDJMP, + S4O_WR_OP, + S4O_RD_OP, + S4O_TERM, + S4O_VISITED, + S4O_ABORT, + S4O_CONTINUE +} S4O_RET; + +static struct +{ + lineNode *head; +} _G; + +/*-----------------------------------------------------------------*/ +/* univisitLines - clear "visited" flag in all lines */ +/*-----------------------------------------------------------------*/ +static void +unvisitLines (lineNode *pl) +{ + for (; pl; pl = pl->next) + pl->visited = FALSE; +} + +/*-----------------------------------------------------------------*/ +/* cleanLabelRef - clear label jump-counter and pass-flag */ +/*-----------------------------------------------------------------*/ +static void +cleanLabelRef (void) +{ + int key; + labelHashEntry *entry; + + if (!labelHash) + return; + for (entry = (labelHashEntry *) hTabFirstItem (labelHash, &key); + entry; + entry = (labelHashEntry *) hTabNextItem (labelHash, &key)) + { + entry->passedLabel = FALSE; + entry->jmpToCount = 0; + } +} + +/*-----------------------------------------------------------------*/ +/* checkLabelRef - check all entries in labelHash */ +/* The path from 'pop' to 'push' must be the only possible path. */ +/* There must not be any paths in or out of this path. */ +/* This is checked by counting the label references. */ +/*-----------------------------------------------------------------*/ +static bool +checkLabelRef (void) +{ + int key; + labelHashEntry *entry; + + if (!labelHash) + { + /* no labels at all: no problems ;-) */ + return TRUE; + } + + for (entry = (labelHashEntry *) hTabFirstItem (labelHash, &key); + entry; + entry = (labelHashEntry *) hTabNextItem (labelHash, &key)) + { + + /* In our path we passed a label, + but we didn't meet all references (jumps) to this label. + This means that the code jumps from outside into this path. */ + if (entry->passedLabel && + entry->jmpToCount != entry->refCount) + { + return FALSE; + } + + /* In our path we jumped to (referenced) a label, + but we we didn't pass it. + This means that there's a code path into our path. */ + if (!entry->passedLabel && + entry->jmpToCount != 0) + { + return FALSE; + } + } + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* setLabelRefPassedLabel - set flag "passedLabel" in entry */ +/* of the list labelHash */ +/*-----------------------------------------------------------------*/ +static bool +setLabelRefPassedLabel (const char *label) +{ + labelHashEntry *entry; + + entry = getLabelRef (label, _G.head); + if (!entry) + return FALSE; + entry->passedLabel = TRUE; + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* incLabelJmpToCount - increment counter "jmpToCount" in entry */ +/* of the list labelHash */ +/*-----------------------------------------------------------------*/ +static bool +incLabelJmpToCount (const char *label) +{ + labelHashEntry *entry; + + entry = getLabelRef (label, _G.head); + if (!entry) + return FALSE; + entry->jmpToCount++; + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* findLabel - */ +/* 1. extracts label in the opcode pl */ +/* 2. increment "label jump-to count" in labelHash */ +/* 3. search lineNode with label definition and return it */ +/*-----------------------------------------------------------------*/ +static lineNode * +findLabel (const lineNode *pl) +{ + char *p; + lineNode *cpl; + + /* 1. extract label in opcode */ + + /* In each mcs51 jumping opcode the label is at the end of the opcode */ + p = strlen (pl->line) - 1 + pl->line; + + /* scan backward until ',' or '\t' */ + for (; p > pl->line; p--) + if (*p == ',' || *p == '\t') + break; + + /* sanity check */ + if (p == pl->line) + { + DEADMOVEERROR(); + return NULL; + } + + /* skip ',' resp. '\t' */ + ++p; + + /* 2. increment "label jump-to count" */ + if (!incLabelJmpToCount (p)) + return NULL; + + /* 3. search lineNode with label definition and return it */ + for (cpl = _G.head; cpl; cpl = cpl->next) + { + if (cpl->isLabel + && strncmp (p, cpl->line, strlen(p)) == 0) + { + return cpl; + } + } + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* isFunc - returns TRUE if it's a CALL or PCALL (not _gptrget()) */ +/*-----------------------------------------------------------------*/ +static bool +isFunc (const lineNode *pl) +{ + if (pl && pl->ic) + { + if ( pl->ic->op == CALL + || pl->ic->op == PCALL) + return TRUE; + } + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* termScanAtFunc - returns S4O_TERM if it's a 'normal' function */ +/* call and it's a 'caller save'. returns S4O_CONTINUE if it's */ +/* 'callee save' or 'naked'. returns S4O_ABORT if it's 'banked' */ +/* and uses the register for the destination. */ +/*-----------------------------------------------------------------*/ +static S4O_RET +termScanAtFunc (const lineNode *pl, int rIdx) +{ + sym_link *ftype; + bool banked_reg = (rIdx == R0_IDX) || (rIdx == R1_IDX) || (rIdx == R2_IDX); + + if (!isFunc (pl)) + return S4O_CONTINUE; + // let's assume calls to literally given locations use the default + // most notably : (*(void (*)()) 0) (); see bug 1749275 + if (IS_VALOP (IC_LEFT (pl->ic))) + return (options.model == MODEL_HUGE) && banked_reg ? S4O_ABORT : options.all_callee_saves ? S4O_CONTINUE : S4O_TERM; + ftype = OP_SYM_TYPE(IC_LEFT(pl->ic)); + if (IS_FUNCPTR (ftype)) + ftype = ftype->next; + if (IFFUNC_ISBANKEDCALL(ftype) && banked_reg) + return S4O_ABORT; + if (FUNC_ARGS (ftype) && getSize (FUNC_ARGS (ftype)->type) > 4) + return S4O_ABORT; + if (FUNC_CALLEESAVES(ftype)) + return S4O_CONTINUE; + if (FUNC_ISNAKED(ftype)) + return S4O_CONTINUE; + return S4O_TERM; +} + +/*-----------------------------------------------------------------*/ +/* scan4op - "executes" and examines the assembler opcodes, */ +/* follows conditional and un-conditional jumps. */ +/* Moreover it registers all passed labels. */ +/* */ +/* Parameter: */ +/* lineNode **pl */ +/* scanning starts from pl; */ +/* pl also returns the last scanned line */ +/* const char *pReg */ +/* points to a register (e.g. "ar0"). scan4op() tests for */ +/* read or write operations with this register */ +/* const char *untilOp */ +/* points to NULL or an opcode (e.g. "push"). */ +/* scan4op() returns if it hits this opcode. */ +/* lineNode **plCond */ +/* If a conditional branch is met plCond points to the */ +/* lineNode of the conditional branch */ +/* */ +/* Returns: */ +/* S4O_ABORT */ +/* on error */ +/* S4O_VISITED */ +/* hit lineNode with "visited" flag set: scan4op() already */ +/* scanned this opcode. */ +/* S4O_FOUNDOPCODE */ +/* found opcode and operand, to which untilOp and pReg are */ +/* pointing to. */ +/* S4O_RD_OP, S4O_WR_OP */ +/* hit an opcode reading or writing from pReg */ +/* S4O_PUSHPOP */ +/* hit a "push" or "pop" opcode */ +/* S4O_CONDJMP */ +/* hit a conditional jump opcode. pl and plCond return the */ +/* two possible branches. */ +/* S4O_TERM */ +/* acall, lcall, ret and reti "terminate" a scan. */ +/*-----------------------------------------------------------------*/ +static S4O_RET +scan4op (lineNode **pl, const char *pReg, const char *untilOp, + lineNode **plCond) +{ + char *p; + int len; + bool isConditionalJump; + int rIdx; + S4O_RET ret; + bool findPushPop; + + findPushPop = untilOp && (strcmp (untilOp, "push") == 0 || strcmp (untilOp, "pop") == 0); + + /* pReg points to e.g. "ar0"..."ar7" */ + len = strlen (pReg); + + /* get index into pReg table */ + for (rIdx = 0; rIdx < mcs51_nRegs; ++rIdx) + if (strcmp (regs8051[rIdx].name, pReg + 1) == 0) + break; + + /* sanity check */ + if (rIdx >= mcs51_nRegs) + { + DEADMOVEERROR(); + return S4O_ABORT; + } + + for (; *pl; *pl = (*pl)->next) + { + if (!(*pl)->line || (*pl)->isDebug || (*pl)->isComment) + continue; + + /* don't optimize across inline assembler, + e.g. isLabel doesn't work there */ + if ((*pl)->isInline) + return S4O_ABORT; + + if ((*pl)->visited) + return S4O_VISITED; + (*pl)->visited = TRUE; + + /* found untilOp? */ + if (untilOp && strncmp ((*pl)->line, untilOp, strlen (untilOp)) == 0) + { + p = (*pl)->line + strlen (untilOp); + if (*p == '\t' && strncmp (p + 1, pReg, len) == 0) + return S4O_FOUNDOPCODE; + else + { + /* found untilOp but without our pReg */ + return S4O_ABORT; + } + } + + /* found pReg? */ + p = strchr ((*pl)->line, '\t'); + if (p) + { + /* skip '\t' */ + p++; + + /* when looking for push or pop and we find a direct access of sp: abort */ + if (findPushPop && strstr (p, "sp")) + return S4O_ABORT; + + /* course search */ + if (strstr (p, pReg + 1)) + { + /* ok, let's have a closer look */ + + /* does opcode read from pReg? */ + if (bitVectBitValue (port->peep.getRegsRead ((*pl)), rIdx)) + return S4O_RD_OP; + /* does opcode write to pReg? */ + if (bitVectBitValue (port->peep.getRegsWritten ((*pl)), rIdx)) + return S4O_WR_OP; + + /* we can get here, if the register name is + part of a variable name: ignore it */ + } + } + + /* found label? */ + if ((*pl)->isLabel) + { + const char *start; + char label[SDCC_NAME_MAX + 1]; + int len; + + if (!isLabelDefinition ((*pl)->line, &start, &len, FALSE)) + return S4O_ABORT; + memcpy (label, start, len); + label[len] = '\0'; + /* register passing this label */ + if (!setLabelRefPassedLabel (label)) + { + DEADMOVEERROR(); + return S4O_ABORT; + } + continue; + } + + /* branch or terminate? */ + isConditionalJump = FALSE; + switch ((*pl)->line[0]) + { + case 'a': + if (strncmp ("acall", (*pl)->line, 5) == 0) + { + /* for comments see 'lcall' */ + ret = termScanAtFunc (*pl, rIdx); + if (ret != S4O_CONTINUE) + return ret; + break; + } + if (strncmp ("ajmp", (*pl)->line, 4) == 0) + { + *pl = findLabel (*pl); + if (!*pl) + return S4O_ABORT; + } + break; + case 'c': + if (strncmp ("cjne", (*pl)->line, 4) == 0) + { + isConditionalJump = TRUE; + break; + } + break; + case 'd': + if (strncmp ("djnz", (*pl)->line, 4) == 0) + { + isConditionalJump = TRUE; + break; + } + break; + case 'j': + if (strncmp ("jmp", (*pl)->line, 3) == 0) + /* "jmp @a+dptr": no chance to trace execution */ + return S4O_ABORT; + if (strncmp ("jc", (*pl)->line, 2) == 0 || + strncmp ("jnc", (*pl)->line, 3) == 0 || + strncmp ("jz", (*pl)->line, 2) == 0 || + strncmp ("jnz", (*pl)->line, 3) == 0) + { + isConditionalJump = TRUE; + break; + } + if (strncmp ("jbc", (*pl)->line, 3) == 0 || + strncmp ("jb", (*pl)->line, 2) == 0 || + strncmp ("jnb", (*pl)->line, 3) == 0) + { + isConditionalJump = TRUE; + break; + } + break; + case 'l': + if (strncmp ("lcall", (*pl)->line, 5) == 0) + { + const char *p = (*pl)->line+5; + while (*p == ' ' || *p == '\t') + p++; + while (isdigit (*p)) + p++; + if (isdigit(p[-1]) && *p == '$') /* at least one digit */ + { + /* this is a temp label for a pcall */ + *pl = findLabel (*pl); + if (!*pl) + return S4O_ABORT; + break; + } + + ret = termScanAtFunc (*pl, rIdx); + /* If it's a 'normal' 'caller save' function call, all + registers have been saved until the 'lcall'. The + 'life range' of all registers end at the lcall, + and we can terminate our search. + * If the function is 'banked', the registers r0, r1 and r2 + are used to tell the trampoline the destination. After + that their 'life range' ends just like the other registers. + * If it's a 'callee save' function call, registers are saved + by the callee. We've got no information, if the register + might live beyond the lcall. Therefore we've to continue + the search. + */ + if (ret != S4O_CONTINUE) + return ret; + break; + } + if (strncmp ("ljmp", (*pl)->line, 4) == 0) + { + *pl = findLabel (*pl); + if (!*pl) + return S4O_ABORT; + } + break; + case 'p': + if (strncmp ("pop", (*pl)->line, 3) == 0 || + strncmp ("push", (*pl)->line, 4) == 0) + return S4O_PUSHPOP; + break; + case 'r': + if (strncmp ("reti", (*pl)->line, 4) == 0) + return S4O_TERM; + + if (strncmp ("ret", (*pl)->line, 3) == 0) + { + /* pcall uses 'ret' */ + if (isFunc (*pl)) + { + /* for comments see 'lcall' */ + ret = termScanAtFunc (*pl, rIdx); + if (ret != S4O_CONTINUE) + return ret; + break; + } + + /* it's a normal function return */ + if (!((*pl)->ic)) + return S4O_ABORT; /* but no ic? */ + if (!currFunc->type) + return S4O_ABORT; /* not a function? */ + if (FUNC_CALLEESAVES (currFunc->type)) + return S4O_ABORT; /* returning from callee saves function */ + if (getSize(currFunc->etype) > 4) + { + for (unsigned i = 0; i < getSize(currFunc->etype); i++) + if (strstr (pReg, fReturn8051[i])) + return S4O_ABORT; /* return value is partially in r4-r7 */ + } + return S4O_TERM; + } + break; + case 's': + if (strncmp ("sjmp", (*pl)->line, 4) == 0) + { + *pl = findLabel (*pl); + if (!*pl) + return S4O_ABORT; + } + break; + default: + break; + } /* switch ((*pl)->line[0]) */ + + if (isConditionalJump) + { + *plCond = findLabel (*pl); + if (!*plCond) + return S4O_ABORT; + return S4O_CONDJMP; + } + } /* for (; *pl; *pl = (*pl)->next) */ + return S4O_ABORT; +} + +/*-----------------------------------------------------------------*/ +/* doPushScan - scan through area 1. This small wrapper handles: */ +/* - action required on different return values */ +/* - recursion in case of conditional branches */ +/*-----------------------------------------------------------------*/ +static bool +doPushScan (lineNode **pl, const char *pReg) +{ + lineNode *plConditional, *pushPl = NULL; + + for (;; *pl = (*pl)->next) + { + switch (scan4op (pl, pReg, "push", &plConditional)) + { + case S4O_FOUNDOPCODE: + /* this is what we're looking for */ + return TRUE; + case S4O_VISITED: +#if 0 + if (!pushPl) + { + DEADMOVEERROR(); + return FALSE; + } + *pl = pushPl; + /* already checked */ + return TRUE; +#else + return FALSE; +#endif + case S4O_CONDJMP: +#if 0 + /* two possible destinations: recurse */ + { + lineNode *pushPl2 = plConditional; + + if (!doPushScan (&pushPl2, pReg)) + return FALSE; + pushPl = pushPl2; + } + continue; +#else + /* two possible destinations: give up */ + return FALSE; +#endif + default: + return FALSE; + } + } +} + +/*-----------------------------------------------------------------*/ +/* doTermScan - scan through area 2. This small wrapper handles: */ +/* - action required on different return values */ +/* - recursion in case of conditional branches */ +/*-----------------------------------------------------------------*/ +static bool +doTermScan (lineNode **pl, const char *pReg) +{ + lineNode *plConditional; + + for (;; *pl = (*pl)->next) + { + switch (scan4op (pl, pReg, NULL, &plConditional)) + { + case S4O_TERM: + case S4O_VISITED: + case S4O_WR_OP: + /* all these are terminating conditions */ + return TRUE; + case S4O_PUSHPOP: + /* don't care, go on */ + continue; + case S4O_CONDJMP: + /* two possible destinations: recurse */ + { + lineNode *pl2 = plConditional; + + if (!doTermScan (&pl2, pReg)) + return FALSE; + } + continue; + case S4O_RD_OP: + default: + /* no go */ + return FALSE; + } + } +} + +/*-----------------------------------------------------------------*/ +/* removeDeadPopPush - remove pop/push pair if possible */ +/*-----------------------------------------------------------------*/ +static bool +removeDeadPopPush (const char *pReg, lineNode *currPl, lineNode *head) +{ + lineNode *pushPl, *pl; + + /* A pop/push pair can be removed, if these criteria are met + (ar0 is just an example here, ar0...ar7 are possible): + + pop ar0 + + ; area 1 + + ; There must not be in area 1: + ; - read or write access of ar0 + ; - "acall", "lcall", "pop", "ret", "reti" or "jmp @a+dptr" opcodes + ; - "push" opcode, which doesn't push ar0 + ; - inline assembly + ; - a jump in or out of area 1 (see checkLabelRef()) + + ; area 1 must be terminated by a: + push ar0 + + ; area 2 + + ; There must not be: + ; - read access of ar0 + ; - "jmp @a+dptr" opcode + ; - inline assembly + ; - a jump in or out of area 2 (see checkLabelRef()) + + ; An "acall", "lcall" (not callee save), "ret" (not PCALL with + ; callee save), "reti" or write access of r0 terminate + ; the search, and the "pop/push ar0" can safely be removed. + */ + + /* area 1 */ + pushPl = currPl->next; + if (!doPushScan (&pushPl, pReg)) + return FALSE; + + if (!checkLabelRef()) + return FALSE; + + /* area 2 */ + pl = pushPl->next; + if (!doTermScan (&pl, pReg)) + return FALSE; + if (!checkLabelRef()) + return FALSE; + + /* Success! */ + if (options.noPeepComments) + { + /* remove pushPl from list */ + pushPl->prev->next = pushPl->next; + pushPl->next->prev = pushPl->prev; + } + else + { + /* replace 'push ar0' by comment */ + #define STR ";\tPeephole\tpush %s removed" + int size = sizeof(STR) + 2; + + pushPl->line = Safe_alloc (size); + SNPRINTF (pushPl->line, size, STR, pReg); + pushPl->isComment = TRUE; + } + + /* 'pop ar0' will be removed by peephole framework after returning TRUE */ + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* removeDeadMove - remove superflous 'mov r%1,%2' */ +/*-----------------------------------------------------------------*/ +static bool +removeDeadMove (const char *pReg, lineNode *currPl) +{ + lineNode *pl; + + /* "mov r0,a" can be removed, if these criteria are met + (r0 is just an example here, r0...r7 are possible): + + ; There must not be: + ; - read access of r0 + ; - "jmp @a+dptr" opcode + ; - inline assembly + ; - a jump in or out of this area (see checkLabelRef()) + + ; An "acall", "lcall" (not callee save), "ret" (not PCALL with + ; callee save), "reti" or write access of r0 terminate + ; the search, and the "mov r0,a" can safely be removed. + */ + pl = currPl->next; + if (!doTermScan (&pl, pReg)) + return FALSE; + + if (!checkLabelRef()) + return FALSE; + + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* mcs51DeadMove - dispatch condition deadmove between */ +/* - remove pop/push */ +/* - remove mov r%1,%2 */ +/*-----------------------------------------------------------------*/ +bool +mcs51DeadMove (const char *reg, lineNode *currPl, lineNode *head) +{ + char pReg[5] = "ar"; + + _G.head = head; + strcat (pReg, reg); + + unvisitLines (_G.head); + cleanLabelRef(); + + if (strncmp (currPl->line, "pop", 3) == 0) + return removeDeadPopPush (pReg, currPl, head); + else if ( strncmp (currPl->line, "mov", 3) == 0 + && (currPl->line[3] == ' ' || currPl->line[3] == '\t')) + return removeDeadMove (pReg, currPl); + else + { + fprintf (stderr, "Error: " + "peephole rule with condition deadMove " + "used with unknown opocde:\n" + "\t%s\n", currPl->line); + return FALSE; + } +} diff --git a/src/mcs51/peep.h b/src/mcs51/peep.h new file mode 100644 index 0000000..fec1dd8 --- /dev/null +++ b/src/mcs51/peep.h @@ -0,0 +1,25 @@ +/*------------------------------------------------------------------------- + peep.h - header file for peephole optimizer helper functions + + Written By - Bernhard Held + + 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! +-------------------------------------------------------------------------*/ + +bool mcs51DeadMove (const char *reg, lineNode *currPl, lineNode *head); diff --git a/src/mcs51/peeph.def b/src/mcs51/peeph.def new file mode 100644 index 0000000..67d46d0 --- /dev/null +++ b/src/mcs51/peeph.def @@ -0,0 +1,5092 @@ +// added by Jean Louis VERN for +// his shift stuff +replace { + xch a,%1 + xch a,%1 +} by { + ; Peephole 2.a removed redundant xch xch +} + +replace { +// saving 2 byte + mov %1,#0x00 + mov a,#0x00 +} by { + ; Peephole 3.a changed mov to clr + clr a + mov %1,a +} + +replace restart { +// saving 1 byte + mov %1,#0x00 + clr a +} by { + ; Peephole 3.b changed mov to clr + clr a + mov %1,a +} + +replace restart { +// saving 1 byte, loosing 1 cycle but maybe allowing peephole 3.b to start + mov %1,#0x00 + mov %2,#0x00 + mov a,%3 +} by { + ; Peephole 3.c changed mov to clr + clr a + mov %1,a + mov %2,a + mov a,%3 +} + +replace { + clr a + mov %1,a + mov %2,a + clr a +} by { + clr a + mov %1,a + mov %2,a + ; Peephole 3.d removed redundant clr +} + +replace { + clr a + mov %1,a + mov %2,a + mov %3,a + clr a +} by { + clr a + mov %1,a + mov %2,a + mov %3,a + ; Peephole 3.e removed redundant clr +} + +replace { + clr a + mov %1,a + mov %2,a + mov %3,a + mov %4,a + clr a +} by { + clr a + mov %1,a + mov %2,a + mov %3,a + mov %4,a + ; Peephole 3.f removed redundant clr +} + +replace { + clr a + mov %1,a + mov %2,a + mov %3,a + mov %4,a + mov %5,a + clr a +} by { + clr a + mov %1,a + mov %2,a + mov %3,a + mov %4,a + mov %5,a + ; Peephole 3.g removed redundant clr +} + +replace { + clr a + mov %1,a + mov %2,a + mov %3,#0x00 +} by { + clr a + mov %1,a + mov %2,a + ; Peephole 3.h changed mov %3,#0x00 to ...,a + mov %3,a +} + +replace { + clr a + mov %1,a + mov %2,a + mov %3,a + mov %4,#0x00 +} by { + clr a + mov %1,a + mov %2,a + mov %3,a + ; Peephole 3.i changed mov %4,#0x00 to ...,a + mov %4,a +} + +replace { + clr a + mov %1,a + mov %2,a + mov %3,a + mov %4,a + mov %5,#0x00 +} by { + clr a + mov %1,a + mov %2,a + mov %3,a + mov %4,a + ; Peephole 3.j changed mov %5,#0x00 to ...,a + mov %5,a +} + +replace { + clr a + mov %1,a + mov %2,a + mov %3,a + mov %4,a + mov %5,a + mov %6,#0x00 +} by { + clr a + mov %1,a + mov %2,a + mov %3,a + mov %4,a + mov %5,a + ; Peephole 3.k changed mov %6,#0x00 to ...,a + mov %6,a +} + +replace { + mov %1,a + mov dptr,#%2 + mov a,%1 + movx @dptr,a +} by { + mov %1,a + mov dptr,#%2 + ; Peephole 100 removed redundant mov + movx @dptr,a +} if notVolatile %1 + +// applies to f.e. lib/src/time.c (--model-large) +replace { + mov a,%1 + movx @dptr,a + inc dptr + mov a,%1 + movx @dptr,a + inc dptr + mov a,%1 + movx @dptr,a + inc dptr + mov a,%1 + movx @dptr,a +} by { + mov a,%1 + movx @dptr,a + inc dptr + ; Peephole 101.a removed redundant moves + movx @dptr,a + inc dptr + movx @dptr,a + inc dptr + movx @dptr,a +} if notVolatile %1 + +// applies to f.e. support/regression/tests/literalop.c (--model-large) +replace { + mov a,%1 + movx @dptr,a + inc dptr + mov a,%1 + movx @dptr,a + inc dptr + mov a,%1 + movx @dptr,a +} by { + mov a,%1 + movx @dptr,a + inc dptr + ; Peephole 101.b removed redundant moves + movx @dptr,a + inc dptr + movx @dptr,a +} if notVolatile %1 + +// applies to f.e. support/regression/tests/onebyte.c (--model-large) +replace { + mov a,%1 + movx @dptr,a + inc dptr + mov a,%1 + movx @dptr,a +} by { + mov a,%1 + movx @dptr,a + inc dptr + ; Peephole 101.c removed redundant mov + movx @dptr,a +} if notVolatile %1 + +replace { + mov %1,%2 + ljmp %3 +%4: + mov %1,%5 +%3: + mov dpl,%1 +%7: + mov sp,_bp + pop _bp +} by { + ; Peephole 102 removed redundant mov to %1 + mov dpl,%2 + ljmp %3 +%4: + mov dpl,%5 +%3: +%7: + mov sp,_bp + pop _bp +} if notVolatile(%1), labelRefCount(%3 1) + +replace { + mov %1,%2 + ljmp %3 +%4: + mov a%1,%5 +%3: + mov dpl,%1 +%7: + mov sp,_bp + pop _bp +} by { + ; Peephole 103 removed redundant mov to %1 + mov dpl,%2 + ljmp %3 +%4: + mov dpl,%5 +%3: +%7: + mov sp,_bp + pop _bp +} if labelRefCount(%3 1) + +// Does not seem to be triggered anymore +//replace { +// mov a,_bp +// clr c +// add a,#0x01 +// mov r%1,a +//} by { +// ; Peephole 104 optimized increment (acc not set to r%1, flags undefined) +// mov r%1,_bp +// inc r%1 +//} + +replace { + mov %1,a + mov a,%1 +} by { + mov %1,a + ; Peephole 105.a removed redundant mov +} if notVolatile %1 + +replace { + mov dptr,#%1 + movx @dptr,a + mov dptr,#%1 + movx a,@dptr +} by { + mov dptr,#%1 + movx @dptr,a + ; Peephole 105.b removed redundant movx +} if notVolatile %1 + + +replace { + mov %1,a + clr c + mov a,%1 +} by { + mov %1,a + clr c + ; Peephole 106 removed redundant mov +} if notVolatile %1 + +replace { + ljmp %1 +%1: +} by { + ; Peephole 107 removed redundant ljmp +%1: +} if labelRefCountChange(%1 -1) + +replace { + jc %1 + ljmp %5 +%1: +} by { + ; Peephole 108.a removed ljmp by inverse jump logic + jnc %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + jz %1 + ljmp %5 +%1: +} by { + ; Peephole 108.b removed ljmp by inverse jump logic + jnz %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + jnz %1 + ljmp %5 +%1: +} by { + ; Peephole 108.c removed ljmp by inverse jump logic + jz %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + jb %1,%2 + ljmp %5 +%2: +} by { + ; Peephole 108.d removed ljmp by inverse jump logic + jnb %1,%5 +%2: +} if labelInRange(%5), labelRefCountChange(%2 -1) + +replace { + jnb %1,%2 + ljmp %5 +%2: +} by { + ; Peephole 108.e removed ljmp by inverse jump logic + jb %1,%5 +%2: +} if labelInRange(%5), labelRefCountChange(%2 -1) + +replace { + ljmp %5 +%1: +} by { + ; Peephole 112.b changed ljmp to sjmp + sjmp %5 +%1: +} if labelInRange(%5) + +replace { + clr a + cjne %1,%2,%3 + cpl a +%3: + rrc a + mov %4,c +} by { + ; Peephole 113.a optimized misc sequence + clr %4 + cjne %1,%2,%3 + setb %4 +%3: +} if labelRefCount %3 1 + +replace { + clr a + cjne %1,%2,%3 + cjne %10,%11,%3 + cpl a +%3: + rrc a + mov %4,c +} by { + ; Peephole 113.b optimized misc sequence + clr %4 + cjne %1,%2,%3 + cjne %10,%11,%3 + setb %4 +%3: +} if labelRefCount %3 2 + +// parameter passing for model-large and model-huge +replace { + mov r%2,dph + mov a,dpl + mov dptr,#%1 + movx @dptr,a + inc dptr + mov a,r%2 + movx @dptr,a + mov dptr,#%1 + movx a,@dptr + mov r%2,a + inc dptr + movx a,@dptr +} by { + mov r%2,dph + mov a,dpl + mov dptr,#%1 + movx @dptr,a + inc dptr + ; Peephole 114.a optimized 16-bit parameter passing + xch a,r%2 + movx @dptr,a +} if notVolatile(%1) + +replace { + mov r%2,dpl + mov r%3,dph + mov r%4,b + mov r%5,a + mov dptr,#%1 + mov a,r%2 + movx @dptr,a + inc dptr + mov a,r%3 + movx @dptr,a + inc dptr + mov a,r%4 + movx @dptr,a + inc dptr + mov a,r%5 + movx @dptr,a + mov dptr,#%1 + movx a,@dptr + mov r%2,a + inc dptr + movx a,@dptr + mov r%3,a + inc dptr + movx a,@dptr + mov r%4,a + inc dptr + movx a,@dptr + mov r%5,a +} by { + mov r%2,dpl + mov r%3,dph + mov r%4,b + mov r%5,a + mov dptr,#%1 + mov a,r%2 + movx @dptr,a + inc dptr + mov a,r%3 + movx @dptr,a + inc dptr + mov a,r%4 + movx @dptr,a + inc dptr + mov a,r%5 + movx @dptr,a +; Peephole 114.b optimized 32-bit parameter passing +} if notVolatile(%1) + + +replace { + clr a + cjne %1,%2,%3 + cpl a +%3: + jnz %4 +} by { + ; Peephole 115.a jump optimization (acc not set) + cjne %1,%2,%3 + sjmp %4 +%3: +} if labelRefCount %3 1 + +replace { + mov %1,a + cjne %1,#0x00,%2 + sjmp %3 +%2: +} by { + mov %1,a + ; Peephole 115.b jump optimization + jz %3 +%2: +} if labelRefCountChange(%2 -1) + +replace { + clr a + cjne %1,%2,%3 + cjne %9,%10,%3 + cpl a +%3: + jnz %4 +} by { + ; Peephole 115.c jump optimization (acc not set) + cjne %1,%2,%3 + cjne %9,%10,%3 + sjmp %4 +%3: +} if labelRefCount %3 2 + +replace { + clr a + cjne %1,%2,%3 + cjne %9,%10,%3 + cjne %11,%12,%3 + cpl a +%3: + jnz %4 +} by { + ; Peephole 115.d jump optimization (acc not set) + cjne %1,%2,%3 + cjne %9,%10,%3 + cjne %11,%12,%3 + sjmp %4 +%3: +} if labelRefCount %3 3 + +replace { + clr a + cjne %1,%2,%3 + cjne %9,%10,%3 + cjne %11,%12,%3 + cjne %13,%14,%3 + cpl a +%3: + jnz %4 +} by { + ; Peephole 115.e jump optimization (acc not set) + cjne %1,%2,%3 + cjne %9,%10,%3 + cjne %11,%12,%3 + cjne %13,%14,%3 + sjmp %4 +%3: +} if labelRefCount %3 4 + +replace { + mov a,#0x01 + cjne %1,%2,%3 + clr a +%3: + jnz %4 +} by { + ; Peephole 115.f jump optimization (acc not set) + cjne %1,%2,%4 +%3: +} if labelRefCount(%3 1), labelRefCountChange(%3 -1) + +replace { + mov a,#0x01 + cjne %1,%2,%3 + cjne %10,%11,%3 + clr a +%3: + jnz %4 +} by { + ; Peephole 115.g jump optimization (acc not set) + cjne %1,%2,%4 + cjne %10,%11,%4 +%3: +} if labelRefCount(%3 2), labelRefCountChange(%3 -2), labelRefCountChange(%4 1) + +replace { + mov a,#0x01 + cjne %1,%2,%3 + cjne %10,%11,%3 + cjne %12,%13,%3 + clr a +%3: + jnz %4 +} by { + ; Peephole 115.h jump optimization (acc not set) + cjne %1,%2,%4 + cjne %10,%11,%4 + cjne %12,%13,%4 +%3: +} if labelRefCount(%3 3), labelRefCountChange(%3 -3), labelRefCountChange(%4 2) + +replace { + mov a,#0x01 + cjne %1,%2,%3 + cjne %10,%11,%3 + cjne %12,%13,%3 + cjne %14,%15,%3 + clr a +%3: + jnz %4 +} by { + ; Peephole 115.i jump optimization (acc not set) + cjne %1,%2,%4 + cjne %10,%11,%4 + cjne %12,%13,%4 + cjne %14,%15,%4 +%3: +} if labelRefCount(%3 4), labelRefCountChange(%3 -4), labelRefCountChange(%4 3) + +replace { + mov a,#0x01 + cjne %1,%2,%3 + clr a +%3: + jz %4 +} by { + ; Peephole 115.j jump optimization (acc not set) + cjne %1,%2,%3 + sjmp %4 +%3: +} if labelRefCount %3 1 + +replace { + mov a,#0x01 + cjne %1,%2,%3 + cjne %10,%11,%3 + clr a +%3: + jz %4 +} by { + ; Peephole 115.k jump optimization (acc not set) + cjne %1,%2,%3 + cjne %10,%11,%3 + sjmp %4 +%3: +} if labelRefCount %3 2 + +replace { + mov a,#0x01 + cjne %1,%2,%3 + cjne %10,%11,%3 + cjne %12,%13,%3 + clr a +%3: + jz %4 +} by { + ; Peephole 115.l jump optimization (acc not set) + cjne %1,%2,%3 + cjne %10,%11,%3 + cjne %12,%13,%3 + sjmp %4 +%3: +} if labelRefCount %3 3 + +replace { + mov a,#0x01 + cjne %1,%2,%3 + cjne %10,%11,%3 + cjne %12,%13,%3 + cjne %14,%15,%3 + clr a +%3: + jz %4 +} by { + ; Peephole 115.m jump optimization (acc not set) + cjne %1,%2,%3 + cjne %10,%11,%3 + cjne %12,%13,%3 + cjne %14,%15,%3 + sjmp %4 +%3: +} if labelRefCount %3 4 + +replace { + push psw + mov psw,%1 + push _bp + mov _bp,%2 +%3: + mov %2,_bp + pop _bp + pop psw + ret +} by { + ; Peephole 127 removed misc sequence + ret +} if labelRefCount %3 0 + +replace { + clr a + rlc a + jz %1 +} by { + ; Peephole 128 jump optimization + jnc %1 +} + +// applies to: bug-524691.c --model-large: while (uRight - uLeft > 1) +replace { + clr a + rlc a + jnz %0 +} by { + ; Peephole 129.a jump optimization + jc %0 +} + +// applies to: _fsdiv.c --xstack: if (mant1 < mant2) +replace { + clr a + rlc a + pop %1 + jnz %0 +} by { + ; Peephole 129.b optimized condition + pop %1 + jc %0 +} if notVolatile %1 + +// applies to: time.c --xstack: while((days += (LEAP_YEAR(year) ? 366 : 365)) <= epoch) +replace { + clr a + rlc a + pop %1 + pop %2 + jnz %0 +} by { + ; Peephole 129.c optimized condition + pop %1 + pop %2 + jc %0 +} if notVolatile %1 %2 + +// applies to: _memmove.c --xstack: if (((int)src < (int)dst) && ((((int)src)+acount) > (int)dst)) +replace { + clr a + rlc a + pop %1 + pop %2 + pop %3 + jnz %0 +} by { + ; Peephole 129.d optimized condition + pop %1 + pop %2 + pop %3 + jc %0 +} if notVolatile %1 %2 %3 + +replace { + mov r%1,@r%2 +} by { + ; Peephole 130 changed target address mode r%1 to ar%1 + mov ar%1,@r%2 +} + +replace { + mov a,%1 + subb a,#0x01 + mov %2,a + mov %1,%2 +} by { + ; Peephole 131 optimized decrement (not caring for c) + dec %1 + mov %2,%1 +} + +// ideally the optimizations of rules 132.x should be done in genCmpXX +replace { + clr c + mov a,#%1 + subb a,%2 + mov %3,c +} by { + ; Peephole 132.a optimized genCmpGt by inverse logic (acc differs) + mov a,%2 + add a,#0xff - %1 + mov %3,c +} if operandsLiteral(%1) + +replace { + clr c + mov a,#%1 + subb a,%2 + jnc %5 +} by { + ; Peephole 132.b optimized genCmpGt by inverse logic (acc differs) + mov a,%2 + add a,#0xff - %1 + jnc %5 +} if operandsLiteral(%1) + +replace { + clr c + mov a,#%1 + subb a,%2 + jc %5 +} by { + ; Peephole 132.c optimized genCmpGt by inverse logic (acc differs) + mov a,%2 + add a,#0xff - %1 + jc %5 +} if operandsLiteral(%1) + +// disabled. See bug1734654.c +//replace { +// clr c +// mov a,%1 +// subb a,#%2 +// mov %3,c +//} by { +// ; Peephole 132.d optimized genCmpGt by inverse logic +// mov a,#0x100 - %2 +// add a,%1 +// mov %3,c +//} if operandsNotRelated('0x00' %2), operandsLiteral(%2) + +replace { + clr c + mov a,%1 + subb a,#%2 + jnc %5 +} by { + ; Peephole 132.e optimized genCmpLt by inverse logic (carry differs) + mov a,#0x100 - %2 + add a,%1 + jc %5 +} if operandsNotRelated('0x00' %2), operandsLiteral(%2) + +replace { + clr c + mov a,%1 + subb a,#%2 + jc %5 +} by { + ; Peephole 132.f optimized genCmpLt by inverse logic (carry differs) + mov a,#0x100 - %2 + add a,%1 + jnc %5 +} if operandsNotRelated('0x00' %2), operandsLiteral(%2) + + +replace { + mov r%1,%2 + mov ar%3,@r%1 + inc r%3 + mov r%4,%2 + mov @r%4,ar%3 +} by { + mov r%1,%2 + ; Peephole 133 removed redundant moves + inc @r%1 + mov ar%3,@r%1 +} if notVolatile + +replace { + mov r%1,%2 + mov ar%3,@r%1 + dec r%3 + mov r%4,%2 + mov @r%4,ar%3 +} by { + mov r%1,%2 + ; Peephole 134 removed redundant moves + dec @r%1 + mov ar%3,@r%1 +} if notVolatile + +replace { + mov r%1,a + mov a,r%2 + orl a,r%1 +} by { + mov r%1,a + ; Peephole 135 removed redundant mov + orl a,r%2 +} + +replace { + mov %1,a + mov dpl,%2 + mov dph,%3 + mov a,%1 +} by { + mov %1,a + mov dpl,%2 + mov dph,%3 + ; Peephole 136 removed redundant mov +} if notVolatile %1 + +// WTF? Doesn't look sensible to me... +//replace { +// mov b,#0x00 +// mov a,%1 +// cjne %2,%3,%4 +// mov b,#0x01 +//%4: +// mov a,b +// jz %5 +//} by { +// ; Peephole 137 optimized misc jump sequence +// mov a,%1 +// cjne %2,%3,%5 +//%4: +//} if labelRefCount %4 1 +// +//replace { +// mov b,#0x00 +// mov a,%1 +// cjne %2,%3,%4 +// mov b,#0x01 +//%4: +// mov a,b +// jnz %5 +//} by { +// ; Peephole 138 optimized misc jump sequence +// mov a,%1 +// cjne %2,%3,%4 +// sjmp %5 +//%4: +//} if labelRefCount %4 1 + +replace { + mov r%1,a + anl ar%1,%2 + mov a,r%1 +} by { + ; Peephole 139.a removed redundant mov + anl a,%2 + mov r%1,a +} + +replace { + mov r%1,a + orl ar%1,%2 + mov a,r%1 +} by { + ; Peephole 139.b removed redundant mov + orl a,%2 + mov r%1,a +} + +replace { + mov r%1,a + xrl ar%1,%2 + mov a,r%1 +} by { + ; Peephole 139.c removed redundant mov + xrl a,%2 + mov r%1,a +} + +// applies to genlshOne +replace { + mov ar%1,@%2 + mov a,r%1 + add a,acc + mov r%1,a +} by { + ; Peephole 140 removed redundant mov + mov a,@%2 + add a,@%2 + mov r%1,a +} + +replace { + mov r%1,a + mov r%2,ar%1 + mov ar%1,@r%2 +} by { + ; Peephole 142 removed redundant mov + mov r%2,a + mov ar%1,@r%2 +} + +replace { + rlc a + mov acc.0,c +} by { + ; Peephole 143.a converted rlc to rl + rl a +} + +replace { + rrc a + mov acc.7,c +} by { + ; Peephole 143.b converted rrc to rc + rr a +} + +replace { + clr c + addc a,%1 +} by { + ; Peephole 145.a changed to add without carry + add a,%1 +} + +replace { + clr c + mov a,%1 + addc a,%2 +} by { + ; Peephole 145.b changed to add without carry + mov a,%1 + add a,%2 +} + +// 147: Fix compiler output to comply with 8051 instruction set. +replace { + orl r%1,a +} by { + ; Peephole 147.a changed target address mode r%1 to ar%1 + orl ar%1,a +} + +replace { + anl r%1,a +} by { + ; Peephole 147.b changed target address mode r%1 to ar%1 + anl ar%1,a +} + +replace { + xrl r%1,a +} by { + ; Peephole 147.c changed target address mode r%1 to ar%1 + xrl ar%1,a +} + +replace { + mov r%1,dpl + mov dpl,r%1 +%9: + ret +} by { + ; Peephole 150.a removed misc moves via dpl before return +%9: + ret +} + +replace { + mov r%1,dpl + mov r%2,dph + mov dpl,r%1 + mov dph,r%2 +%9: + ret +} by { + ; Peephole 150.b removed misc moves via dph, dpl before return +%9: + ret +} + +replace { + mov r%1,dpl + mov r%2,dph + mov dpl,r%1 +%9: + ret +} by { + ; Peephole 150.c removed misc moves via dph, dpl before return +%9: + ret +} + +replace { + mov r%1,dpl + mov r%2,dph + mov r%3,b + mov dpl,r%1 + mov dph,r%2 + mov b,r%3 +%9: + ret +} by { + ; Peephole 150.d removed misc moves via dph, dpl, b before return +%9: + ret +} + +replace { + mov r%1,dpl + mov r%2,dph + mov r%3,b + mov dpl,r%1 +%9: + ret +} by { + ; Peephole 150.e removed misc moves via dph, dpl, b before return +%9: + ret +} + +replace { + mov r%1,dpl + mov r%2,dph + mov r%3,b + mov dpl,r%1 + mov dph,r%2 +%9: + ret +} by { + ; Peephole 150.f removed misc moves via dph, dpl, b before return +%9: + ret +} + +replace { + mov r%1,dpl + mov r%2,dph + mov r%3,b + mov r%4,a + mov dpl,r%1 + mov dph,r%2 + mov b,r%3 + mov a,r%4 +%9: + ret +} by { + ; Peephole 150.g removed misc moves via dph, dpl, b, a before return +%9: + ret +} + +replace { + mov r%1,dpl + mov r%2,dph + mov r%3,b + mov r%4,a + mov dpl,r%1 + mov dph,r%2 +%9: + ret +} by { + ; Peephole 150.h removed misc moves via dph, dpl, b, a before return +%9: + ret +} + +replace { + mov r%1,dpl + mov r%2,dph + mov r%3,b + mov r%4,a + mov dpl,r%1 +%9: + ret +} by { + ; Peephole 150.i removed misc moves via dph, dpl, b, a before return +%9: + ret +} + +// peephole 213.a might revert this +replace { + mov %1,#%2 + xrl %1,#0x80 +} by { + ; Peephole 159 avoided xrl during execution + mov %1,#(%2 ^ 0x80) +} + +replace { + jnc %1 + sjmp %2 +%1: +} by { + ; Peephole 160.a removed sjmp by inverse jump logic + jc %2 +%1: +} if labelRefCountChange(%1 -1) + +replace { + jc %1 + sjmp %2 +%1: +} by { + ; Peephole 160.b removed sjmp by inverse jump logic + jnc %2 +%1: +} if labelRefCountChange(%1 -1) + +replace { + jnz %1 + sjmp %2 +%1: +} by { + ; Peephole 160.c removed sjmp by inverse jump logic + jz %2 +%1: +} if labelRefCountChange(%1 -1) + +replace { + jz %1 + sjmp %2 +%1: +} by { + ; Peephole 160.d removed sjmp by inverse jump logic + jnz %2 +%1: +} if labelRefCountChange(%1 -1) + +replace { + jnb %3,%1 + sjmp %2 +%1: +} by { + ; Peephole 160.e removed sjmp by inverse jump logic + jb %3,%2 +%1: +} if labelRefCountChange(%1 -1) + +replace { + jb %3,%1 + sjmp %2 +%1: +} by { + ; Peephole 160.f removed sjmp by inverse jump logic + jnb %3,%2 +%1: +} if labelRefCountChange(%1 -1) + +replace { + mov %1,%2 + mov %3,%1 + mov %2,%1 +} by { + mov %1,%2 + mov %3,%1 + ; Peephole 166 removed redundant mov +} if notVolatile %1 %2 + +replace { + mov c,%1 + cpl c + mov %1,c +} by { + ; Peephole 167 removed redundant bit moves (c not set to %1) + cpl %1 +} + +replace { + jnb %1,%2 + sjmp %3 +%2: +} by { + ; Peephole 168 jump optimization + jb %1,%3 +%2: +} if labelRefCountChange(%2 -1) + +replace { + jb %1,%2 + sjmp %3 +%2: +} by { + ; Peephole 169 jump optimization + jnb %1,%3 +%2: +} if labelRefCountChange(%2 -1) + +replace { + clr a + cjne %1,%2,%3 + cpl a +%3: + jz %4 +} by { + ; Peephole 170 jump optimization + cjne %1,%2,%4 +%3: +} if labelRefCount(%3 1), labelRefCountChange(%3 -1) + +replace { + clr a + cjne %1,%2,%3 + cjne %9,%10,%3 + cpl a +%3: + jz %4 +} by { + ; Peephole 171 jump optimization + cjne %1,%2,%4 + cjne %9,%10,%4 +%3: +} if labelRefCount(%3 2), labelRefCountChange(%3 -2), labelRefCountChange(%4 1) + +replace { + clr a + cjne %1,%2,%3 + cjne %9,%10,%3 + cjne %11,%12,%3 + cpl a +%3: + jz %4 +} by { + ; Peephole 172 jump optimization + cjne %1,%2,%4 + cjne %9,%10,%4 + cjne %11,%12,%4 +%3: +} if labelRefCount(%3 3), labelRefCountChange(%3 -3), labelRefCountChange(%4 2) + +replace { + clr a + cjne %1,%2,%3 + cjne %9,%10,%3 + cjne %11,%12,%3 + cjne %13,%14,%3 + cpl a +%3: + jz %4 +} by { + ; Peephole 173 jump optimization + cjne %1,%2,%4 + cjne %9,%10,%4 + cjne %11,%12,%4 + cjne %13,%14,%4 +%3: +} if labelRefCount(%3 4), labelRefCountChange(%3 -4), labelRefCountChange(%4 3) + +replace { + mov r%1,%2 + clr c + mov a,r%1 + subb a,#0x01 + mov %2,a +} by { + mov r%1,%2 + ; Peephole 174.a optimized decrement (acc not set to %2, flags undefined) + dec %2 +} + +replace { + mov r%1,%2 + mov a,r%1 + add a,#0x01 + mov %2,a +} by { + mov r%1,%2 + ; Peephole 174.b optimized increment (acc not set to %2, flags undefined) + inc %2 +} + +replace { + mov %1,@r%2 + inc %1 + mov @r%2,%1 +} by { + ; Peephole 174.c optimized increment, removed redundant mov + inc @r%2 + mov %1,@r%2 +} if notVolatile + +// this one will screw assignes to volatile/sfr's +replace { + mov %1,%2 + mov %2,%1 +} by { + mov %1,%2 + ; Peephole 177.a removed redundant mov +} if notVolatile %1 %2 + +// applies to f.e. scott-add.asm (--model-large) +replace { + mov r%1,a + mov a,ar%1 +} by { + mov r%1,a + ; Peephole 177.b removed redundant mov +} + +// applies to f.e. bug-408972.c +replace { + mov %1,%2 + mov %1,%3 +} by { + ; Peephole 177.c removed redundant mov + mov %1,%3 +} if notVolatile(%1 %2),operandsNotRelated(%1 %3) + +// applies to f.e. bug-408972.c +// not before peephole 177.c +replace restart { + mov %1,%2 + mov %3,%4 + mov %2,%1 +} by { + mov %1,%2 + mov %3,%4 + ; Peephole 177.d removed redundant mov +} if notVolatile(%1 %2),operandsNotRelated(%1 %2 %3) + +// applies to f.e. bug-607243.c +replace { + mov %1,%2 + mov a%1,%3 +} by { + ; peephole 177.e removed redundant mov %1,%2 + mov a%1,%3 +} if notVolatile(%2), operandsNotRelated(%1 %3) + +replace { + mov ar%1,%2 + mov r%1,%3 +} by { + ; peephole 177.f removed redundant mov + mov r%1,%3 +} if notVolatile %2 + +replace { + mov %1,%2 + mov a,%1 +} by { + ; peephole 177.g optimized mov sequence + mov a,%2 + mov %1,a +} if notVolatile %1 + +replace { + mov %1,%2 + mov a,%2 +} by { + ; peephole 177.h optimized mov sequence + mov a,%2 + mov %1,a +} if notVolatile(%2), notSame(%1 'dptr'), operandsNotRelated(%1 %2) + +// applies to f.e. testfwk.c +replace { + mov r%1,a + mov ar%2,r%1 +} by { + mov r%1,a + ; peephole 177.i optimized mov sequence + mov r%2,a +} + +replace { + mov r%1,%2 + mov ar%3,r%1 + mov r%1,%4 +} by { + ; peephole 177.j optimized mov sequence + mov r%3,%2 + mov r%1,%4 +} + +replace { + mov a,%1 + mov b,a + mov a,%2 +} by { + ; Peephole 178 removed redundant mov + mov b,%1 + mov a,%2 +} + +// rules 179-182 provided by : Frieder <fe@lake.iup.uni-heidelberg.de> +// saving 2 byte, 1 cycle +replace { + mov b,#0x00 + mov a,#0x00 +} by { + ; Peephole 179 changed mov to clr + clr a + mov b,a +} + +// applies to: +// volatile xdata char t; t=0x01; t=0x03; +replace { + mov dptr,%1 + mov a,%2 + movx @dptr,a + mov dptr,%1 +} by { + mov dptr,%1 + mov a,%2 + movx @dptr,a + ; Peephole 180.a removed redundant mov to dptr +} + +// volatile xdata char t; t=0x01; t=0x03; t=0x01; +replace { + mov dptr,%1 + mov a,%2 + movx @dptr,a + mov a,%3 + movx @dptr,a + mov dptr,%1 +} by { + mov dptr,%1 + mov a,%2 + movx @dptr,a + mov a,%3 + movx @dptr,a + ; Peephole 180.b removed redundant mov to dptr +} + +// saving 1 byte, 0 cycles +replace { + mov a,#0x00 +} by { + ; Peephole 181 changed mov to clr + clr a +} + +// saving 3 bytes, 2 cycles +// provided by Bernhard Held <bernhard.held@de.westinghouse.com> +replace { + mov dpl,#%1 + mov dph,#(%1 >> 8) +} by { + ; Peephole 182.a used 16 bit load of DPTR + mov dptr,#%1 +} + +// saving 3 byte, 2 cycles, return(NULL) profits here +replace { + mov dpl,#0x%1 + mov dph,#0x%2 +} by { + ; Peephole 182.b used 16 bit load of dptr + mov dptr,#0x%2%1 +} + +// saving 3 byte, 2 cycles. Probably obsoleted by 182.b +replace { + mov dpl,#%1 + mov dph,#%2 +} by { + ; Peephole 182.c used 16 bit load of dptr + mov dptr,#(((%2)<<8) + %1) +} + +// applies to return 0.0; in f.e. sincosf.c +replace { + mov dpl,#%1 + clr a + mov dph,a +} by { + ; Peephole 182.d used 16 bit load of dptr + mov dptr,#(%1&0x00ff) + clr a +} + +replace { + anl %1,#%2 + anl %1,#%3 +} by { + ; Peephole 183.a avoided anl during execution + anl %1,#(%2&%3) +} if notVolatile %1 + +replace { + orl %1,#%2 + orl %1,#%3 +} by { + ; Peephole 183.b avoided orl during execution + orl %1,#(%2|%3) +} if notVolatile %1 + +replace { + mov %1,a + cpl a + mov %1,a +} by { + ; Peephole 184 removed redundant mov + cpl a + mov %1,a +} if notVolatile %1 + +//replace { +// acc being incremented might cause problems with register tracking +// mov %1,a +// inc %1 +//} by { +// ; Peephole 185 changed order of increment (acc incremented also!) +// inc a +// mov %1,a +//} if notVolatile %1 + +// char indexed access to: long code table[] = {4,3,2,1}; +replace restart { + add a,#%1 + mov dpl,a + clr a + addc a,#(%1 >> 8) + mov dph,a + clr a + movc a,@a+dptr + mov %2,a + inc dptr + clr a + movc a,@a+dptr + mov %3,a + inc dptr + clr a + movc a,@a+dptr + mov %4,a + inc dptr + clr a + movc a,@a+dptr +} by { + ; Peephole 186.a optimized movc sequence + mov b,a + mov dptr,#%1 + movc a,@a+dptr + mov %2,a + inc dptr + mov a,b + movc a,@a+dptr + mov %3,a + inc dptr + mov a,b + movc a,@a+dptr + mov %4,a + inc dptr + mov a,b + movc a,@a+dptr +} + +// char indexed access to: void* code table[] = {4,3,2,1}; +replace restart { + add a,#%1 + mov dpl,a + clr a + addc a,#(%1 >> 8) + mov dph,a + clr a + movc a,@a+dptr + mov %2,a + inc dptr + clr a + movc a,@a+dptr + mov %3,a + inc dptr + clr a + movc a,@a+dptr +} by { + ; Peephole 186.b optimized movc sequence + mov b,a + mov dptr,#%1 + movc a,@a+dptr + mov %2,a + inc dptr + mov a,b + movc a,@a+dptr + mov %3,a + inc dptr + mov a,b + movc a,@a+dptr +} + +// char indexed access to: int code table[] = {4,3,2,1}; +replace restart { + add a,#%1 + mov dpl,a + clr a + addc a,#(%1 >> 8) + mov dph,a + clr a + movc a,@a+dptr + mov %2,a + inc dptr + clr a + movc a,@a+dptr +} by { + ; Peephole 186.c optimized movc sequence + mov %2,a + mov dptr,#%1 + movc a,@a+dptr + xch a,%2 + inc dptr + movc a,@a+dptr +} + +// char indexed access to: char code table[] = {4,3,2,1}; +replace { + add a,#%1 + mov dpl,a + clr a + addc a,#(%1 >> 8) + mov dph,a + clr a + movc a,@a+dptr +} by { + ; Peephole 186.d optimized movc sequence + mov dptr,#%1 + movc a,@a+dptr +} + +// char indexed access to: int code table[] = {4,3,2,1}; +replace { + mov b,#0x02 + mul ab + add a,#%2 + mov dpl,a + mov a,#(%2 >> 8) + addc a,b + mov dph,a + clr a + movc a,@a+dptr + mov %3,a + mov a,#0x01 + movc a,@a+dptr +} by { + ; Peephole 186.e optimized movc sequence (b, dptr differ) + add a,acc + mov b,a + mov dptr,#%2 + jnc .+3 + inc dph + movc a,@a+dptr + mov %3,a + mov a,b + inc a + movc a,@a+dptr +} + +replace { + mov r%1,%2 + anl ar%1,#%3 + mov a,r%1 +} by { + ; Peephole 187 used a instead of ar%1 for anl + mov a,%2 + anl a,#%3 + mov r%1,a +} + +replace { + mov %1,a + mov dptr,%2 + movc a,@a+dptr + mov %1,a +} by { + ; Peephole 188 removed redundant mov + mov dptr,%2 + movc a,@a+dptr + mov %1,a +} if notVolatile %1 + +replace { + anl a,#0x0f + mov %1,a + mov a,#0x0f + anl a,%1 +} by { + anl a,#0x0f + mov %1,a + ; Peephole 189 removed redundant mov and anl +} if notVolatile %1 + +// rules 190 & 191 need to be in order +replace { + mov a,%1 + lcall __gptrput + mov a,%1 +} by { + mov a,%1 + lcall __gptrput + ; Peephole 190 removed redundant mov +} if notVolatile %1 + +replace { + mov %1,a + mov dpl,%2 + mov dph,%3 + mov b,%4 + mov a,%1 +} by { + mov %1,a + mov dpl,%2 + mov dph,%3 + mov b,%4 + ; Peephole 191 removed redundant mov +} if notVolatile %1 + +// applies to f.e. regression/ports/mcs51/support.c +replace { + mov r%1,a + mov @r%2,ar%1 +} by { + mov r%1,a + ; Peephole 192.a used a instead of ar%1 as source + mov @r%2,a +} + +// applies to f.e. printf_large.c +replace { + mov ar%1,@r%2 + mov a,r%1 +} by { + ; Peephole 192.b used a instead of ar%1 as destination + mov a,@r%2 + mov r%1,a +} + +replace { + jnz %3 + mov a,%4 + jnz %3 + mov a,%9 + jnz %3 + mov a,%12 + cjne %13,%14,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 193.a optimized misc jump sequence + jnz %8 + mov a,%4 + jnz %8 + mov a,%9 + jnz %8 + mov a,%12 + cjne %13,%14,%8 + sjmp %7 +%3: +} if labelInRange(%8), labelRefCount(%3 4), labelRefCountChange(%3 -4), labelRefCountChange(%8 3) + +replace { + cjne %1,%2,%3 + mov a,%4 + cjne %5,%6,%3 + mov a,%9 + cjne %10,%11,%3 + mov a,%12 + cjne %13,%14,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 193.b optimized misc jump sequence + cjne %1,%2,%8 + mov a,%4 + cjne %5,%6,%8 + mov a,%9 + cjne %10,%11,%8 + mov a,%12 + cjne %13,%14,%8 + sjmp %7 +%3: +} if labelInRange(%8), labelRefCount(%3 4), labelRefCountChange(%3 -4), labelRefCountChange(%8 3) + +replace { + cjne @%1,%2,%3 + inc %1 + cjne @%1,%6,%3 + inc %1 + cjne @%1,%11,%3 + inc %1 + cjne @%1,%14,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 193.c optimized misc jump sequence + cjne @%1,%2,%8 + inc %1 + cjne @%1,%6,%8 + inc %1 + cjne @%1,%11,%8 + inc %1 + cjne @%1,%14,%8 + sjmp %7 +%3: +} if labelInRange(%8), labelRefCount(%3 4), labelRefCountChange(%3 -4), labelRefCountChange(%8 3) + +replace { + cjne %1,%2,%3 + cjne %5,%6,%3 + cjne %10,%11,%3 + cjne %13,%14,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 194 optimized misc jump sequence + cjne %1,%2,%8 + cjne %5,%6,%8 + cjne %10,%11,%8 + cjne %13,%14,%8 + sjmp %7 +%3: +} if labelInRange(%8), labelRefCount(%3 4), labelRefCountChange(%3 -4), labelRefCountChange(%8 3) + +replace { + jnz %3 + mov a,%4 + jnz %3 + mov a,%9 + cjne %10,%11,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 195.a optimized misc jump sequence + jnz %8 + mov a,%4 + jnz %8 + mov a,%9 + cjne %10,%11,%8 + sjmp %7 +%3: +} if labelInRange(%8), labelRefCount(%3 3), labelRefCountChange(%3 -3), labelRefCountChange(%8 2) + +replace { + cjne %1,%2,%3 + mov a,%4 + cjne %5,%6,%3 + mov a,%9 + cjne %10,%11,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 195.b optimized misc jump sequence + cjne %1,%2,%8 + mov a,%4 + cjne %5,%6,%8 + mov a,%9 + cjne %10,%11,%8 + sjmp %7 +%3: +} if labelInRange(%8), labelRefCount(%3 3), labelRefCountChange(%3 -3), labelRefCountChange(%8 2) + +replace { + cjne @%1,%2,%3 + inc %1 + cjne @%1,%6,%3 + inc %1 + cjne @%1,%11,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 195.c optimized misc jump sequence + cjne @%1,%2,%8 + inc %1 + cjne @%1,%6,%8 + inc %1 + cjne @%1,%11,%8 + sjmp %7 +%3: +} if labelInRange(%8), labelRefCount(%3 3), labelRefCountChange(%3 -3), labelRefCountChange(%8 2) + +replace { + cjne %1,%2,%3 + cjne %5,%6,%3 + cjne %10,%11,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 196 optimized misc jump sequence + cjne %1,%2,%8 + cjne %5,%6,%8 + cjne %10,%11,%8 + sjmp %7 +%3: +} if labelInRange(%8), labelRefCount(%3 3), labelRefCountChange(%3 -3), labelRefCountChange(%8 2) + +replace { + jnz %3 + mov a,%4 + cjne %5,%6,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 197.a optimized misc jump sequence + jnz %8 + mov a,%4 + cjne %5,%6,%8 + sjmp %7 +%3: +} if labelInRange(%8), labelRefCount(%3 2), labelRefCountChange(%3 -2), labelRefCountChange(%8 1) + +replace { + cjne %1,%2,%3 + mov a,%4 + cjne %5,%6,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 197.b optimized misc jump sequence + cjne %1,%2,%8 + mov a,%4 + cjne %5,%6,%8 + sjmp %7 +%3: +} if labelInRange(%8), labelRefCount(%3 2), labelRefCountChange(%3 -2), labelRefCountChange(%8 1) + +replace { + cjne @%1,%2,%3 + inc %1 + cjne @%1,%6,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 197.c optimized misc jump sequence + cjne @%1,%2,%8 + inc %1 + cjne @%1,%6,%8 + sjmp %7 +%3: +} if labelInRange(%8), labelRefCount(%3 2), labelRefCountChange(%3 -2), labelRefCountChange(%8 1) + +replace { + cjne %1,%2,%3 + cjne %5,%6,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 198.a optimized misc jump sequence + cjne %1,%2,%8 + cjne %5,%6,%8 + sjmp %7 +%3: +} if labelInRange(%8), labelRefCount(%3 2), labelRefCountChange(%3 -2), labelRefCountChange(%8 1) + +replace { + cjne %1,%2,%3 + sjmp %4 +%3: + sjmp %5 +} by { + ; Peephole 198.b optimized misc jump sequence + cjne %1,%2,%5 + sjmp %4 +%3: +} if labelInRange(%5), labelRefCount(%3 1), labelRefCountChange(%3 -1) + +replace { + sjmp %1 +%1: +} by { + ; Peephole 200.a removed redundant sjmp +%1: +} if labelRefCountChange(%1 -1) + +replace { + sjmp %1 +%2: +%1: +} by { + ; Peephole 200.b removed redundant sjmp +%2: +%1: +} if labelRefCountChange(%1 -1) + +replace { + push acc + mov dptr,%1 + pop acc +} by { + ; Peephole 202 removed redundant push pop + mov dptr,%1 +} + +replace { + mov r%1,_spx + lcall %2 + mov r%1,_spx +} by { + ; Peephole 203 removed mov r%1,_spx + lcall %2 +} + +replace { + mov %1,a + add a,acc + mov %1,a +} by { + ; Peephole 204 removed redundant mov + add a,acc + mov %1,a +} if notVolatile %1 + +replace { + djnz %1,%2 + sjmp %3 +%2: + sjmp %4 +%3: +} by { + ; Peephole 205 optimized misc jump sequence + djnz %1,%4 +%2: +%3: +} if labelRefCount(%2 1), labelRefCountChange(%2 -1), labelRefCountChange(%3 -1) + +replace { + mov %1,%1 +} by { + ; Peephole 206 removed redundant mov %1,%1 +} if notVolatile %1 + +// Does not seem to be triggered anymore +//replace { +// mov a,_bp +// add a,#0x00 +// mov %1,a +//} by { +// ; Peephole 207 removed zero add (acc not set to %1, flags undefined) +// mov %1,_bp +//} + +replace { + push acc + mov r%1,_bp + pop acc +} by { + ; Peephole 208 removed redundant push pop + mov r%1,_bp +} + +// Does not seem to be triggered anymore +//replace { +// mov a,_bp +// add a,#0x00 +// inc a +// mov %1,a +//} by { +// ; Peephole 209 optimized increment (acc not set to %1, flags undefined) +// mov %1,_bp +// inc %1 +//} + +replace { + mov dptr,#((((%1 >> 8)) <<8) + %1) +} by { + ; Peephole 210 simplified expression + mov dptr,#%1 +} + +replace { + push %1 + pop %1 +} by { + ; Peephole 211 removed redundant push %1 pop %1 +} + +// Does not seem to be triggered anymore +//replace { +// mov a,_bp +// add a,#0x01 +// mov r%1,a +//} by { +// ; Peephole 212 reduced add sequence to inc +// mov r%1,_bp +// inc r%1 +//} + +// reverts peephole 159? asx8051 cannot handle, too complex? +replace { + mov %1,#(( %2 >> 8 ) ^ 0x80) +} by { + ; Peephole 213.a inserted fix + mov %1,#(%2 >> 8) + xrl %1,#0x80 +} + +replace { + mov %1,#(( %2 + %3 >> 8 ) ^ 0x80) +} by { + ; Peephole 213.b inserted fix + mov %1,#((%2 + %3) >> 8) + xrl %1,#0x80 +} + + +replace { + mov %1,a + mov a,%2 + add a,%1 +} by { + mov %1,a + ; Peephole 214.a removed redundant mov + add a,%2 +} if notSame(%1 %2) + +replace { + mov %1,a + add a,%2 + mov %1,a +} by { + ; Peephole 214.b removed redundant mov + add a,%2 + mov %1,a +} if notSame(%1 %2) + +replace { + mov r%1,%2 + clr a + inc r%1 + mov @r%1,a + dec r%1 + mov @r%1,a +} by { + mov r%1,%2 + clr a + ; Peephole 216.a simplified clear (2 bytes) + mov @r%1,a + inc r%1 + mov @r%1,a +} + +replace { + mov r%1,%2 + clr a + inc r%1 + inc r%1 + mov @r%1,a + dec r%1 + mov @r%1,a + dec r%1 + mov @r%1,a +} by { + mov r%1,%2 + clr a + ; Peephole 216.b simplified clear (3 bytes) + mov @r%1,a + inc r%1 + mov @r%1,a + inc r%1 + mov @r%1,a +} + +replace { + mov r%1,%2 + clr a + inc r%1 + inc r%1 + inc r%1 + mov @r%1,a + dec r%1 + mov @r%1,a + dec r%1 + mov @r%1,a + dec r%1 + mov @r%1,a +} by { + mov r%1,%2 + clr a + ; Peephole 216.c simplified clear (4 bytes) + mov @r%1,a + inc r%1 + mov @r%1,a + inc r%1 + mov @r%1,a + inc r%1 + mov @r%1,a +} + +replace { + clr a + movx @dptr,a + mov dptr,%1 + clr a + movx @dptr,a +} by { + ; Peephole 219.a removed redundant clear + clr a + movx @dptr,a + mov dptr,%1 + movx @dptr,a +} + +replace { + clr a + movx @dptr,a + mov dptr,%1 + movx @dptr,a + mov dptr,%2 + clr a + movx @dptr,a +} by { + clr a + movx @dptr,a + mov dptr,%1 + movx @dptr,a + mov dptr,%2 + ; Peephole 219.b removed redundant clear + movx @dptr,a +} + +replace { + mov dps,#0x00 + mov dps,#0x01 +} by { + ; Peephole 220.a removed bogus DPS set + mov dps,#0x01 +} + +replace { + mov dps,#0x01 + mov dps,#0x00 +} by { + ; Peephole 220.b removed bogus DPS set + mov dps,#0x00 +} + +replace { + mov %1 + %2,(%2 + %1) +} by { + ; Peephole 221.a remove redundant mov +} if notVolatile + +replace { + mov (%1 + %2 + %3),((%2 + %1) + %3) +} by { + ; Peephole 221.b remove redundant mov +} if notVolatile + +replace { + dec r%1 + inc r%1 +} by { + ; Peephole 222 removed dec/inc pair +} + +replace { + mov %1,dpl + mov %2,dph + mov dpl,%1 + mov dph,%2 +} by { + mov %1,dpl + mov %2,dph + ; Peephole 223.a removed redundant dph/dpl moves +} if notVolatile %1 %2 + +replace { + mov %1,dpl + mov (%1 + 1),dph + mov dpl,%1 + mov dph,(%1 + 1) +} by { + mov %1,dpl + mov (%1 + 1),dph + ; Peephole 223.b removed redundant dph/dpl moves +} if notVolatile %1 + +replace { + mov a,%1 + movx @dptr,a + mov dpl,%2 + mov dph,%3 + mov b,%4 + mov a,%1 +} by { + mov a,%1 + movx @dptr,a + mov dpl,%2 + mov dph,%3 + mov b,%4 + ; Peephole 225 removed redundant move to acc +} if notVolatile %1 + +replace { + clr a + movx @%1,a + inc %1 + clr a +} by { + clr a + movx @%1,a + inc %1 + ; Peephole 226.a removed unnecessary clr +} + +replace { + clr a + movx @%1,a + inc %1 + movx @%1,a + inc %1 + clr a +} by { + clr a + movx @%1,a + inc %1 + movx @%1,a + inc %1 + ; Peephole 226.b removed unnecessary clr +} + +replace { + mov dptr,#%1 + clr a + inc dptr + inc dptr + inc dptr + movx @dptr,a + lcall __decdptr + movx @dptr,a + lcall __decdptr + movx @dptr,a + lcall __decdptr + movx @dptr,a +} by { + mov dptr,#%1 + clr a + ; Peephole 227.a replaced inefficient 32 bit clear + movx @dptr,a + inc dptr + movx @dptr,a + inc dptr + movx @dptr,a + inc dptr + movx @dptr,a + mov dptr,#%1 +} + +replace { + mov dptr,#%1 + clr a + inc dptr + inc dptr + inc dptr + movx @dptr,a + lcall __decdptr + movx @dptr,a + lcall __decdptr + movx @dptr,a + lcall __decdptr + mov a,#%2 + movx @dptr,a +} by { + mov dptr,#%1 + ; Peephole 227.b replaced inefficient 32 constant + mov a,#%2 + movx @dptr,a + inc dptr + clr a + movx @dptr,a + inc dptr + movx @dptr,a + inc dptr + movx @dptr,a + mov dptr,#%1 +} + +replace { + mov dptr,#%1 + clr a + inc dptr + movx @dptr,a + lcall __decdptr + movx @dptr,a +} by { + mov dptr,#%1 + clr a + ; Peephole 227.c replaced inefficient 16 bit clear + movx @dptr,a + inc dptr + movx @dptr,a + mov dptr,#%1 +} + +replace { + mov dptr,#%1 + clr a + inc dptr + movx @dptr,a + lcall __decdptr + mov a,#%2 + movx @dptr,a +} by { + mov dptr,#%1 + ; Peephole 227.d replaced inefficient 16 bit constant + mov a,#%2 + movx @dptr,a + inc dptr + clr a + movx @dptr,a + mov dptr,#%1 +} + +// this last peephole often removes the last mov from 227.a - 227.d +replace { + mov dptr,#%1 + mov dptr,#%2 +} by { + ; Peephole 227.e removed redundant mov to dptr + mov dptr,#%2 +} + +replace { + movx a,@dptr +} by { + ; Peephole 232 using movc to read xdata (--xram-movc) + clr a + movc a,@a+dptr +} if xramMovcOption + +replace { + lcall _gptrget +} by { + ; Peephole 233 using _gptrgetc instead of _gptrget (--xram-movc) + lcall _gptrgetc +} if xramMovcOption + +replace { + mov r%1,a + mov dpl,r%1 +%2: + ret +} by { + ; Peephole 234.a loading dpl directly from a(ccumulator), r%1 not set + mov dpl,a +%2: + ret +} + +replace { + mov r%1,a + mov dpl,r%2 + mov dph,r%1 +%3: + ret +} by { + ; Peephole 234.b loading dph directly from a(ccumulator), r%1 not set + mov dpl,r%2 + mov dph,a +%3: + ret +} + +// 14 rules by Fiorenzo D. Ramaglia <fd.ramaglia@tin.it> + +replace { + add a,ar%1 +} by { + ; Peephole 236.a used r%1 instead of ar%1 + add a,r%1 +} + +replace { + addc a,ar%1 +} by { + ; Peephole 236.b used r%1 instead of ar%1 + addc a,r%1 +} + +replace { + anl a,ar%1 +} by { + ; Peephole 236.c used r%1 instead of ar%1 + anl a,r%1 +} + +replace { + dec ar%1 +} by { + ; Peephole 236.d used r%1 instead of ar%1 + dec r%1 +} + +replace { + djnz ar%1,%2 +} by { + ; Peephole 236.e used r%1 instead of ar%1 + djnz r%1,%2 +} + +replace { + inc ar%1 +} by { + ; Peephole 236.f used r%1 instead of ar%1 + inc r%1 +} + +replace { + mov a,ar%1 +} by { + ; Peephole 236.g used r%1 instead of ar%1 + mov a,r%1 +} + +replace { + mov ar%1,#%2 +} by { + ; Peephole 236.h used r%1 instead of ar%1 + mov r%1,#%2 +} + +replace { + mov ar%1,a +} by { + ; Peephole 236.i used r%1 instead of ar%1 + mov r%1,a +} + +replace { + mov ar%1,ar%2 +} by { + ; Peephole 236.j used r%1 instead of ar%1 + mov r%1,ar%2 +} + +replace { + orl a,ar%1 +} by { + ; Peephole 236.k used r%1 instead of ar%1 + orl a,r%1 +} + +replace { + subb a,ar%1 +} by { + ; Peephole 236.l used r%1 instead of ar%1 + subb a,r%1 +} + +replace { + xch a,ar%1 +} by { + ; Peephole 236.m used r%1 instead of ar%1 + xch a,r%1 +} + +replace { + xrl a,ar%1 +} by { + ; Peephole 236.n used r%1 instead of ar%1 + xrl a,r%1 +} + +// obsoleted by 251.b +//replace { +// sjmp %1 +//%2: +// mov %3,%4 +//%1: +// ret +//} by { +// ; Peephole 237.a removed sjmp to ret +// ret +//%2: +// mov %3,%4 +//%1: +// ret +//} if labelRefCountChange(%1 -1) + +// obsoleted by 251.b +//replace { +// sjmp %1 +//%2: +// mov %3,%4 +// mov dpl,%5 +// mov dph,%6 +//%1: +// ret +//} by { +// ; Peephole 237.b removed sjmp to ret +// ret +//%2: +// mov %3,%4 +// mov dpl,%5 +// mov dph,%6 +//%1: +// ret +//} if labelRefCountChange(%1 -1) + +// applies to f.e. device/lib/log10f.c +replace { + mov %1,%9 + mov %2,%10 + mov %3,%11 + mov %4,%12 + + mov %5,%13 + mov %6,%14 + mov %7,%15 + mov %8,%16 + + mov %9,%1 + mov %10,%2 + mov %11,%3 + mov %12,%4 +} by { + mov %1,%9 + mov %2,%10 + mov %3,%11 + mov %4,%12 + + mov %5,%13 + mov %6,%14 + mov %7,%15 + mov %8,%16 + ; Peephole 238.a removed 4 redundant moves +} if notSame(%1 %2 %3 %4 %5 %6 %7 %8), notVolatile(%1 %2 %3 %4 %9 %10 %11 %12) + +// applies to device/lib/log10f.c +replace { + mov %1,%5 + mov %2,%6 + mov %3,%7 + mov %4,%8 + + mov %5,%1 + mov %6,%2 + mov %7,%3 +} by { + mov %1,%5 + mov %2,%6 + mov %3,%7 + mov %4,%8 + ; Peephole 238.b removed 3 redundant moves +} if notSame(%1 %2 %3 %4 %5 %6 %7), notVolatile(%1 %2 %3 %5 %6 %7) + +// applies to f.e. device/lib/time.c +replace { + mov %1,%5 + mov %2,%6 + + mov %3,%7 + mov %4,%8 + + mov %5,%1 + mov %6,%2 +} by { + mov %1,%5 + mov %2,%6 + + mov %3,%7 + mov %4,%8 + ; Peephole 238.c removed 2 redundant moves +} if notSame(%1 %2 %3 %4), notVolatile(%1 %2 %5 %6) + +// applies to f.e. support/regression/tests/bug-524209.c +replace { + mov %1,%4 + mov %2,%5 + mov %3,%6 + + mov %4,%1 + mov %5,%2 + mov %6,%3 +} by { + mov %1,%4 + mov %2,%5 + mov %3,%6 + ; Peephole 238.d removed 3 redundant moves +} if notSame(%1 %2 %3 %4 %5 %6), notVolatile(%1 %2 %3 %4 %5 %6) + +// applies to f.e. ser_ir.asm +replace { + mov r%1,acc +} by { + ; Peephole 239 used a instead of acc + mov r%1,a +} + +replace restart { + mov a,%1 + addc a,#0x00 +} by { + ; Peephole 240 use clr instead of addc a,#0 + clr a + addc a,%1 +} + +// peepholes 241.a0 to 241.d and 241.e0 to 241.h need to be in order +replace { + cjne r%2,#%3,%0 + cjne r%4,#%5,%0 + cjne r%6,#%7,%0 + cjne r%8,#%9,%0 + cjne r%10,#%11,%0 + cjne r%12,#%13,%0 + cjne r%14,#%15,%0 + cjne r%16,#%17,%0 + mov a,#0x01 + sjmp %1 +%0: + clr a +%1: +} by { + ; Peephole 241.a0 optimized compare + clr a + cjne r%2,#%3,%0 + cjne r%4,#%5,%0 + cjne r%6,#%7,%0 + cjne r%8,#%9,%0 + cjne r%10,#%11,%0 + cjne r%12,#%13,%0 + cjne r%14,#%15,%0 + cjne r%16,#%17,%0 + inc a +%0: +%1: +} if labelRefCount(%0 8), labelRefCountChange(%1 -1) + +replace { + cjne r%2,#%3,%0 + cjne r%4,#%5,%0 + cjne r%6,#%7,%0 + cjne r%8,#%9,%0 + mov a,#0x01 + sjmp %1 +%0: + clr a +%1: +} by { + ; Peephole 241.a optimized compare + clr a + cjne r%2,#%3,%0 + cjne r%4,#%5,%0 + cjne r%6,#%7,%0 + cjne r%8,#%9,%0 + inc a +%0: +%1: +} if labelRefCount(%0 4), labelRefCountChange(%1 -1) + +// applies to generic pointer compare +replace { + cjne r%2,#%3,%0 + cjne r%4,#%5,%0 + cjne r%6,#%7,%0 + mov a,#0x01 + sjmp %1 +%0: + clr a +%1: +} by { + ; Peephole 241.b optimized compare + clr a + cjne r%2,#%3,%0 + cjne r%4,#%5,%0 + cjne r%6,#%7,%0 + inc a +%0: +%1: +} if labelRefCount(%0 3), labelRefCountChange(%1 -1) + +// applies to f.e. time.c +replace { + cjne r%2,#%3,%0 + cjne r%4,#%5,%0 + mov a,#0x01 + sjmp %1 +%0: + clr a +%1: +} by { + ; Peephole 241.c optimized compare + clr a + cjne r%2,#%3,%0 + cjne r%4,#%5,%0 + inc a +%0: +%1: +} if labelRefCount(%0 2), labelRefCountChange(%1 -1) + +// applies to f.e. malloc.c +replace { + cjne r%2,#%3,%0 + mov a,#0x01 + sjmp %1 +%0: + clr a +%1: +} by { + ; Peephole 241.d optimized compare + clr a + cjne r%2,#%3,%0 + inc a +%0: +%1: +} if labelRefCount(%0 1), labelRefCountChange(%1 -1) + +// applies to f.e. j = (k!=0x1000); +// with volatile idata long long k; +replace { + cjne @r%0,#%3,%1 + inc r%0 + cjne @r%0,#%4,%1 + inc r%0 + cjne @r%0,#%5,%1 + inc r%0 + cjne @r%0,#%6,%1 + inc r%0 + cjne @r%0,#%7,%1 + inc r%0 + cjne @r%0,#%8,%1 + inc r%0 + cjne @r%0,#%9,%1 + inc r%0 + cjne @r%0,#%10,%1 + mov a,#0x01 + sjmp %2 +%1: + clr a +%2: +} by { + ; Peephole 241.e0 optimized compare + clr a + cjne @r%0,#%3,%1 + inc r%0 + cjne @r%0,#%4,%1 + inc r%0 + cjne @r%0,#%5,%1 + inc r%0 + cjne @r%0,#%6,%1 + inc r%0 + cjne @r%0,#%7,%1 + inc r%0 + cjne @r%0,#%8,%1 + inc r%0 + cjne @r%0,#%9,%1 + inc r%0 + cjne @r%0,#%10,%1 + inc a +%1: +%2: +} if labelRefCount(%1 8), labelRefCountChange(%2 -1) + +// applies to f.e. j = (k!=0x1000); +// with volatile idata long k; +replace { + cjne @r%0,#%3,%1 + inc r%0 + cjne @r%0,#%4,%1 + inc r%0 + cjne @r%0,#%5,%1 + inc r%0 + cjne @r%0,#%6,%1 + mov a,#0x01 + sjmp %2 +%1: + clr a +%2: +} by { + ; Peephole 241.e optimized compare + clr a + cjne @r%0,#%3,%1 + inc r%0 + cjne @r%0,#%4,%1 + inc r%0 + cjne @r%0,#%5,%1 + inc r%0 + cjne @r%0,#%6,%1 + inc a +%1: +%2: +} if labelRefCount(%1 4), labelRefCountChange(%2 -1) + +// applies to f.e. j = (p!=NULL); +// with volatile idata char *p; +replace { + cjne @r%0,#%3,%1 + inc r%0 + cjne @r%0,#%4,%1 + inc r%0 + cjne @r%0,#%5,%1 + mov a,#0x01 + sjmp %2 +%1: + clr a +%2: +} by { + ; Peephole 241.f optimized compare + clr a + cjne @r%0,#%3,%1 + inc r%0 + cjne @r%0,#%4,%1 + inc r%0 + cjne @r%0,#%5,%1 + inc a +%1: +%2: +} if labelRefCount(%1 3), labelRefCountChange(%2 -1) + +// applies to f.e. j = (k!=0x1000); +// with volatile idata int k; +replace { + cjne @r%0,#%3,%1 + inc r%0 + cjne @r%0,#%4,%1 + mov a,#0x01 + sjmp %2 +%1: + clr a +%2: +} by { + ; Peephole 241.g optimized compare + clr a + cjne @r%0,#%3,%1 + inc r%0 + cjne @r%0,#%4,%1 + inc a +%1: +%2: +} if labelRefCount(%1 2), labelRefCountChange(%2 -1) + +// applies to f.e. vprintf.asm (--stack-auto) +replace { + cjne @r%0,#%3,%1 + mov a,#0x01 + sjmp %2 +%1: + clr a +%2: +} by { + ; Peephole 241.h optimized compare + clr a + cjne @r%0,#%3,%1 + inc a +%1: +%2: +} if labelRefCount(%1 1), labelRefCountChange(%2 -1) + +// applies to f.e. scott-bool1.c +replace { + jnz %1 + mov %2,%3 +%1: + jz %4 +} by { + jnz %1 + mov %2,%3 + ; Peephole 242.a avoided branch jnz to jz + jz %4 +%1: +} if labelRefCount %1 1 + +// applies to f.e. scott-bool1.c +replace { + jnz %1 + mov %2,%3 + orl a,%5 +%1: + jz %4 +} by { + jnz %1 + mov %2,%3 + orl a,%5 + ; Peephole 242.b avoided branch jnz to jz + jz %4 +%1: +} if labelRefCount %1 1 + +// applies to f.e. logic.c +replace { + jnz %1 + mov %2,%3 + orl a,%5 + orl a,%6 + orl a,%7 +%1: + jz %4 +} by { + jnz %1 + mov %2,%3 + orl a,%5 + orl a,%6 + orl a,%7 + ; Peephole 242.c avoided branch jnz to jz + jz %4 +%1: +} if labelRefCount %1 1 + +// applies to f.e. vprintf.c +// this is a rare case, usually the "tail increment" is noticed earlier +replace { + cjne %1,%2,%3 + inc %4 +%3: + sjmp %5 +} by { + ; Peephole 243 avoided branch to sjmp + cjne %1,%2,%5 + inc %4 +%3: + sjmp %5 +} if labelInRange(%5), labelRefCountChange(%3 -1), labelRefCountChange(%5 1) + +// applies to f.e. simplefloat.c (saving 1 cycle) +replace { + mov r%1,dpl + mov a,r%1 +} by { + ; Peephole 244.a moving first to a instead of r%1 + mov a,dpl + mov r%1,a +} + +// applies to f.e. _itoa.c (saving 1 cycle) +replace { + mov r%1,dph + mov a,r%1 +} by { + ; Peephole 244.b moving first to a instead of r%1 + mov a,dph + mov r%1,a +} + + +// applies to f.e. bug-460010.c (saving 1 cycle) +replace { + mov r%1,a + mov dpl,r%1 +} by { + mov r%1,a + ; Peephole 244.c loading dpl from a instead of r%1 + mov dpl,a +} + +replace { + mov r%1,a + mov dph,r%1 +} by { + mov r%1,a + ; Peephole 244.d loading dph from a instead of r%1 + mov dph,a +} + +// this one is safe but disables 245.a 245.b +// please remove 245 if 245.a 245.b are found to be safe +// applies to f.e. scott-compare.c +replace { + clr a + rlc a + mov r%1,a + cjne a,#0x01,%2 +%2: + clr a + rlc a + mov r%1,a +} by { + ; Peephole 245 optimized complement (r%1 and acc set needed?) + cpl c + clr a + rlc a + mov r%1,a +} if labelRefCount(%2 1), labelRefCountChange(%2 -1) + +// this one will not be triggered if 245 is present +// please remove 245 if 245.a 245.b are found to be safe +// applies to f.e. vprintf.c +replace { + clr a + rlc a + mov r%1,a + cjne a,#0x01,%2 +%2: + clr a + rlc a + mov r%1,a + jz %3 +} by { + ; Peephole 245.a optimized conditional jump (r%1 and acc not set!) + jc %3 +} if labelRefCount(%2 1), labelRefCountChange(%2 -1) + +// this one will not be triggered if 245 is present +// please remove 245 if 245.a 245.b are found to be safe +// applies to f.e. scott-compare.c +replace { + clr a + rlc a + mov r%1,a + cjne a,#0x01,%2 +%2: + clr a + rlc a + mov r%1,a + jnz %3 +} by { + ; Peephole 245.b optimized conditional jump (r%1 and acc not set!) + jnc %3 +} if labelRefCount(%2 1), labelRefCountChange(%2 -1) + + +// rules 246.x apply to f.e. bitfields.c +replace { + mov dptr,#%1 + movx a,@dptr + anl a,#%2 + movx @dptr,a + mov dptr,#%1 + movx a,@dptr + anl a,#%3 + movx @dptr,a +} by { + mov dptr,#%1 + movx a,@dptr + ; Peephole 246.a combined clr/clr + anl a,#%2&%3 + movx @dptr,a +} if notVolatile %1 + +replace { + mov dptr,#%1 + movx a,@dptr + orl a,#%2 + movx @dptr,a + mov dptr,#%1 + movx a,@dptr + orl a,#%3 + movx @dptr,a +} by { + mov dptr,#%1 + movx a,@dptr + ; Peephole 246.b combined set/set + orl a,#%2|%3 + movx @dptr,a +} if notVolatile %1 + +replace { + mov dptr,#%1 + movx a,@dptr + orl a,#%2 + movx @dptr,a + mov dptr,#%1 + movx a,@dptr + anl a,#%3 + movx @dptr,a +} by { + mov dptr,#%1 + movx a,@dptr + orl a,#%2 + ; Peephole 246.c combined set/clr + anl a,#%3 + movx @dptr,a +} if notVolatile %1 + +replace { + mov dptr,#%1 + movx a,@dptr + anl a,#%2 + movx @dptr,a + mov dptr,#%1 + movx a,@dptr + orl a,#%3 + movx @dptr,a +} by { + mov dptr,#%1 + movx a,@dptr + anl a,#%2 + ; Peephole 246.d combined clr/set + orl a,#%3 + movx @dptr,a +} if notVolatile %1 + +replace { + mov dptr,#%1 + movx a,@dptr + orl a,#%2 + anl a,#%3 + movx @dptr,a + mov dptr,#%1 + movx a,@dptr + anl a,#%4 + movx @dptr,a +} by { + mov dptr,#%1 + movx a,@dptr + orl a,#%2 + ; Peephole 246.e combined set/clr/clr + anl a,#%3&%4 + movx @dptr,a +} if notVolatile %1 + +replace { + mov dptr,#%1 + movx a,@dptr + orl a,#%2 + anl a,#%3 + movx @dptr,a + mov dptr,#%1 + movx a,@dptr + orl a,#%4 + movx @dptr,a +} by { + mov dptr,#%1 + movx a,@dptr + orl a,#%2 + anl a,#%3 + ; Peephole 246.f combined set/clr/set + orl a,#%4 + movx @dptr,a +} if notVolatile %1 + +replace { + mov dptr,#%1 + movx a,@dptr + anl a,#%2 + orl a,#%3 + movx @dptr,a + mov dptr,#%1 + movx a,@dptr + anl a,#%4 + movx @dptr,a +} by { + mov dptr,#%1 + movx a,@dptr + anl a,#%2 + orl a,#%3 + ; Peephole 246.g combined clr/set/clr + anl a,#%4 + movx @dptr,a +} if notVolatile %1 + +replace { + mov dptr,#%1 + movx a,@dptr + anl a,#%2 + orl a,#%3 + movx @dptr,a + mov dptr,#%1 + movx a,@dptr + orl a,#%4 + movx @dptr,a +} by { + mov dptr,#%1 + movx a,@dptr + anl a,#%2 + ; Peephole 246.h combined clr/set/set + orl a,#%3|%4 + movx @dptr,a +} if notVolatile %1 + + +// rules 247.x apply to f.e. bitfields.c +replace { + mov r%5,#%1 + mov a,@r%5 + anl a,#%2 + mov @r%5,a + mov r%5,#%1 + mov a,@r%5 + anl a,#%3 + mov @r%5,a +} by { + mov r%5,#%1 + mov a,@r%5 + ; Peephole 247.a combined clr/clr + anl a,#%2&%3 + mov @r%5,a +} if notVolatile %1 + +replace { + mov r%5,#%1 + mov a,@r%5 + orl a,#%2 + mov @r%5,a + mov r%5,#%1 + mov a,@r%5 + orl a,#%3 + mov @r%5,a +} by { + mov r%5,#%1 + mov a,@r%5 + ; Peephole 247.b combined set/set + orl a,#%2|%3 + mov @r%5,a +} if notVolatile %1 + +replace { + mov r%5,#%1 + mov a,@r%5 + orl a,#%2 + mov @r%5,a + mov r%5,#%1 + mov a,@r%5 + anl a,#%3 + mov @r%5,a +} by { + mov r%5,#%1 + mov a,@r%5 + orl a,#%2 + ; Peephole 247.c combined set/clr + anl a,#%3 + mov @r%5,a +} if notVolatile %1 + +replace { + mov r%5,#%1 + mov a,@r%5 + anl a,#%2 + mov @r%5,a + mov r%5,#%1 + mov a,@r%5 + orl a,#%3 + mov @r%5,a +} by { + mov r%5,#%1 + mov a,@r%5 + anl a,#%2 + ; Peephole 247.d combined clr/set + orl a,#%3 + mov @r%5,a +} if notVolatile %1 + +replace { + mov r%5,#%1 + mov a,@r%5 + orl a,#%2 + anl a,#%3 + mov @r%5,a + mov r%5,#%1 + mov a,@r%5 + anl a,#%4 + mov @r%5,a +} by { + mov r%5,#%1 + mov a,@r%5 + orl a,#%2 + ; Peephole 247.e combined set/clr/clr + anl a,#%3&%4 + mov @r%5,a +} if notVolatile %1 + +replace { + mov r%5,#%1 + mov a,@r%5 + orl a,#%2 + anl a,#%3 + mov @r%5,a + mov r%5,#%1 + mov a,@r%5 + orl a,#%4 + mov @r%5,a +} by { + mov r%5,#%1 + mov a,@r%5 + orl a,#%2 + anl a,#%3 + ; Peephole 247.f combined set/clr/set + orl a,#%4 + mov @r%5,a +} if notVolatile %1 + +replace { + mov r%5,#%1 + mov a,@r%5 + anl a,#%2 + orl a,#%3 + mov @r%5,a + mov r%5,#%1 + mov a,@r%5 + anl a,#%4 + mov @r%5,a +} by { + mov r%5,#%1 + mov a,@r%5 + anl a,#%2 + orl a,#%3 + ; Peephole 247.g combined clr/set/clr + anl a,#%4 + mov @r%5,a +} if notVolatile %1 + +replace { + mov r%5,#%1 + mov a,@r%5 + anl a,#%2 + orl a,#%3 + mov @r%5,a + mov r%5,#%1 + mov a,@r%4 + orl a,#%4 + mov @r%5,a +} by { + mov r%5,#%1 + mov a,@r%5 + anl a,#%2 + ; Peephole 247.h combined clr/set/set + orl a,#%3|%4 + mov @r%5,a +} if notVolatile %1 + + +// Peepholes 248.x have to be compatible with the keyword volatile. +// They optimize typical accesses to memory mapped I/O devices: +// volatile xdata char t; t|=0x01; +replace { + mov dptr,%1 + movx a,@dptr + mov r%2,a + mov dptr,%1 + mov a,%3 + orl a,r%2 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + mov r%2,a + ; Peephole 248.a optimized or to xdata + orl a,%3 + movx @dptr,a +} + +// volatile xdata char t; t&=0x01; +replace { + mov dptr,%1 + movx a,@dptr + mov r%2,a + mov dptr,%1 + mov a,%3 + anl a,r%2 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + mov r%2,a + ; Peephole 248.b optimized and to xdata + anl a,%3 + movx @dptr,a +} + +// volatile xdata char t; t^=0x01; +replace { + mov dptr,%1 + movx a,@dptr + mov r%2,a + mov dptr,%1 + mov a,%3 + xrl a,r%2 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + mov r%2,a + ; Peephole 248.c optimized xor to xdata + xrl a,%3 + movx @dptr,a +} + +// volatile xdata char t; t|=0x01; t&=~0x01; t|=0x01; +replace { + mov dptr,%1 + movx a,@dptr + mov r%2,a + orl a,%3 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + mov r%2,a + anl a,%4 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + mov r%2,a + orl a,%5 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + ; Peephole 248.d optimized or/and/or to volatile xdata + orl a,%3 + movx @dptr,a + movx a,@dptr + anl a,%4 + movx @dptr,a + movx a,@dptr + mov r%2,a + orl a,%5 + movx @dptr,a +} + +// volatile xdata char t; t&=~0x01; t|=0x01; t&=~0x01; +replace { + mov dptr,%1 + movx a,@dptr + mov r%2,a + anl a,%3 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + mov r%2,a + orl a,%4 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + mov r%2,a + anl a,%5 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + ; Peephole 248.e optimized and/or/and to volatile xdata + anl a,%3 + movx @dptr,a + movx a,@dptr + orl a,%4 + movx @dptr,a + movx a,@dptr + mov r%2,a + anl a,%5 + movx @dptr,a +} + +// volatile xdata char t; t|=0x01; t&=~0x01; +replace { + mov dptr,%1 + movx a,@dptr + mov r%2,a + orl a,%3 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + mov r%2,a + anl a,%4 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + ; Peephole 248.f optimized or/and to volatile xdata + orl a,%3 + movx @dptr,a + movx a,@dptr + mov r%2,a + anl a,%4 + movx @dptr,a +} + +// volatile xdata char t; t&=~0x01; t|=0x01; +replace { + mov dptr,%1 + movx a,@dptr + mov r%2,a + anl a,%3 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + mov r%2,a + orl a,%4 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + ; Peephole 248.g optimized and/or to volatile xdata + anl a,%3 + movx @dptr,a + movx a,@dptr + mov r%2,a + orl a,%4 + movx @dptr,a +} + +// volatile xdata char t; t^=0x01; t^=0x01; +replace { + mov dptr,%1 + movx a,@dptr + mov r%2,a + xrl a,%3 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + mov r%2,a + xrl a,%4 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + ; Peephole 248.h optimized xor/xor to volatile xdata + xrl a,%3 + movx @dptr,a + movx a,@dptr + mov r%2,a + xrl a,%4 + movx @dptr,a +} + +// Peeepholes 248.i to 248.m are like 248.d to 248.h except they apply to bitfields: +// xdata struct { unsigned b0:1; unsigned b1:1; unsigned b2:1; } xport; +// xport.b0=1; xport.b0=0; xport.b0=1; +replace { + mov dptr,%1 + movx a,@dptr + orl a,%3 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + anl a,%4 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + orl a,%5 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + orl a,%3 + movx @dptr,a + ; Peephole 248.i optimized or/and/or to xdata bitfield + movx a,@dptr + anl a,%4 + movx @dptr,a + movx a,@dptr + orl a,%5 + movx @dptr,a +} + +replace { + mov dptr,%1 + movx a,@dptr + anl a,%3 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + orl a,%4 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + anl a,%5 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + anl a,%3 + movx @dptr,a + ; Peephole 248.j optimized and/or/and to xdata bitfield + movx a,@dptr + orl a,%4 + movx @dptr,a + movx a,@dptr + anl a,%5 + movx @dptr,a +} + +replace { + mov dptr,%1 + movx a,@dptr + orl a,%3 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + anl a,%4 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + orl a,%3 + movx @dptr,a + ; Peephole 248.k optimized or/and to xdata bitfield + movx a,@dptr + anl a,%4 + movx @dptr,a +} + +replace { + mov dptr,%1 + movx a,@dptr + anl a,%3 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + orl a,%4 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + anl a,%3 + movx @dptr,a + ; Peephole 248.l optimized and/or to xdata bitfield + movx a,@dptr + orl a,%4 + movx @dptr,a +} + +replace { + mov dptr,%1 + movx a,@dptr + xrl a,%3 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + xrl a,%4 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + xrl a,%3 + movx @dptr,a + ; Peephole 248.m optimized xor/xor to xdata bitfield + movx a,@dptr + xrl a,%4 + movx @dptr,a +} + +// Peeepholes 248.n to 248.p are like previous peepholes but apply to arrays +// applies to f.e. bug2686159.c +replace { + mov dptr,%1 + movx a,@dptr + mov r%2,a + orl ar%2,%3 + mov dptr,%1 + mov a,r%2 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + ; Peephole 248.n optimized or to xdata array + orl a,%3 + mov r%2,a + movx @dptr,a +} + +replace { + mov dptr,%1 + movx a,@dptr + mov r%2,a + anl ar%2,%3 + mov dptr,%1 + mov a,r%2 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + ; Peephole 248.o optimized and to xdata array + anl a,%3 + mov r%2,a + movx @dptr,a +} + +replace { + mov dptr,%1 + movx a,@dptr + mov r%2,a + xrl ar%2,%3 + mov dptr,%1 + mov a,r%2 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + ; Peephole 248.p optimized xor to xdata array + xrl a,%3 + mov r%2,a + movx @dptr,a +} + +replace { + jnz %1 +%1: +} by { + ; Peephole 249.a jump optimization +} if labelRefCount(%1 1), labelRefCountChange(%1 -1) + +replace { + jz %1 +%1: +} by { + ; Peephole 249.b jump optimization +} if labelRefCount(%1 1), labelRefCountChange(%1 -1) + + +// This allows non-interrupt and interrupt code to safely compete +// for a resource without the non-interrupt code having to disable +// interrupts: +// volatile bit resource_is_free; +// if( resource_is_free ) { +// resource_is_free=0; do_something; resource_is_free=1; +// } +replace { + jnb %1,%2 +%3: + clr %1 +} by { + ; Peephole 250.a using atomic test and clear + jbc %1,%3 + sjmp %2 +%3: +} if labelRefCount(%3 0), labelRefCountChange(%3 1) + +replace { + jb %1,%2 + ljmp %3 +%2: + clr %1 +} by { + ; Peephole 250.b using atomic test and clear + jbc %1,%2 + ljmp %3 +%2: +} if labelRefCount %2 1 + + +// not before peephole 250.b +replace { + ljmp %5 +} by { + ; Peephole 251.a replaced ljmp %5 to ret with ret + ret +} if optimizeReturn(), labelIsReturnOnly(%5), labelRefCountChange(%5 -1) + +// not before peephole 250.b +replace { + sjmp %5 +} by { + ; Peephole 251.b replaced sjmp %5 to ret with ret + ret +} if optimizeReturn(), labelIsReturnOnly(%5), labelRefCountChange(%5 -1) + +// applies to shifts.c and when accessing arrays with an unsigned integer index +// saving 1 byte, 2 cycles +replace { + mov r%1,%2 + mov a,(%2 + 1) + xch a,r%1 + add a,acc + xch a,r%1 + rlc a + mov r%3,a +} by { + ; Peephole 252 optimized left shift + mov a,%2 + add a,acc + mov r%1,a + mov a,(%2 + 1) + rlc a + mov r%3,a +} + +// unsigned char i=8; do{ } while(--i != 0); +// this applies if i is kept in a register +replace { + dec %1 + cjne %1,#0x00,%2 +} by { + ; Peephole 253.a optimized decrement with compare + djnz %1,%2 +} if notVolatile(%1) + +// unsigned char i=8; do{ } while(--i != 0); +// this applies if i is kept in data memory +// must come before 256, see bug 1721024 +replace { + dec %1 + mov a,%1 + jnz %2 +} by { + ; Peephole 253.b optimized decrement with compare + djnz %1,%2 +} if notVolatile(%1), operandsNotRelated(%1 '@r0' '@r1') + + +// applies to f.e. funptrs.c +// saves one byte if %1 is a register or @register +replace { + mov a,%1 + add a,acc +} by { + mov a,%1 + ; Peephole 254 optimized left shift + add a,%1 +} if notVolatile %1 + +// applies to f.e. switch.c +replace { + clr c + mov a,#%1 + subb a,%2 + jc %3 +%4: + mov a,%2 + add a,%2 + add a,%2 + mov dptr,%5 + jmp @a+dptr +} by { + ; Peephole 255 optimized jump table index calculation + mov a,%2 + cjne a,#(%1+0x01),.+1 + jnc %3 +%4: + add a,%2 + add a,%2 + mov dptr,%5 + jmp @a+dptr +} + +// applies to f.e. jump tables and scott-bool1.c. +// similar peepholes can be constructed for other instructions +// after which a flag or a register is known (like: djnz, cjne, jnc) +replace { + jc %1 +%2: + clr c +} by { + jc %1 +%2: + ; Peephole 256.a removed redundant clr c +} if labelRefCount %2 0 + +// applies to f.e. logf.c +replace { + jnz %1 +%2: + clr a +} by { + jnz %1 +%2: + ; Peephole 256.b removed redundant clr a +} if labelRefCount %2 0 + +// applies to f.e. bug-905492.c +replace { + jnz %1 +%2: + mov %3,#0x00 +} by { + jnz %1 +%2: + ; Peephole 256.c loading %3 with zero from a + mov %3,a +} if labelRefCount %2 0 + +// applies to f.e. malloc.c +replace { + jnz %1 +%2: + mov %4,%5 + mov %3,#0x00 +} by { + jnz %1 +%2: + mov %4,%5 + ; Peephole 256.d loading %3 with zero from a + mov %3,a +} if labelRefCount(%2 0),operandsNotRelated('a' %4) + +replace { + jnz %1 +%2: + mov %4,%5 + mov %6,%7 + mov %3,#0x00 +} by { + jnz %1 +%2: + mov %4,%5 + mov %6,%7 + ; Peephole 256.e loading %3 with zero from a + mov %3,a +} if labelRefCount(%2 0),operandsNotRelated('a' %4 %6) + +replace { + jnz %1 +%2: + mov %4,%5 + mov %6,%7 + mov %8,%9 + mov %3,#0x00 +} by { + jnz %1 +%2: + mov %4,%5 + mov %6,%7 + mov %8,%9 + ; Peephole 256.f loading %2 with zero from a + mov %3,a +} if labelRefCount(%2 0),operandsNotRelated('a' %4 %6 %8) + + +replace restart { + ljmp %5 +} by { + ljmp %6 + ; peephole 257.a jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + sjmp %5 +} by { + sjmp %6 + ; peephole 257.b jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), labelInRange(%6), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jz %5 +} by { + jz %6 + ; peephole 257.c jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), labelInRange(%6), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jnz %5 +} by { + jnz %6 + ; peephole 257.d jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), labelInRange(%6), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jc %5 +} by { + jc %6 + ; peephole 257.e jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), labelInRange(%6), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jnc %5 +} by { + jnc %6 + ; peephole 257.f jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), labelInRange(%6), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jb %1,%5 +} by { + jb %1,%6 + ; peephole 257.g jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), labelInRange(%6), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jnb %1,%5 +} by { + jnb %1,%6 + ; peephole 257.h jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), labelInRange(%6), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jbc %1,%5 +} by { + jbc %1,%6 + ; peephole 257.i jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), labelInRange(%6), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + + +// in_byte<<=1; if(in_bit) in_byte|=1; +// helps f.e. reading data on a 3-wire (SPI) bus +replace { + mov a,%1 + add a,%1 + mov %1,a + jnb %2,%3 +%4: + orl %1,#0x01 +%3: +} by { + mov a,%1 + ; Peephole 258.a optimized bitbanging + mov c,%2 + addc a,%1 + mov %1,a +%4: +%3: +} if notVolatile(%1), labelRefCountChange(%3 -1) + +// in_byte<<=1; if(in_bit) in_byte|=1; +replace { + mov a,r%1 + add a,r%1 + mov r%1,a + jnb %2,%3 +%4: + orl ar%1,#0x01 +%3: +} by { + mov a,r%1 + ; Peephole 258.b optimized bitbanging + mov c,%2 + addc a,r%1 + mov r%1,a +%4: +%3: +} if labelRefCountChange(%3 -1) + +// in_byte>>=1; if(in_bit) in_byte|=0x80; +replace { + mov a,%1 + clr c + rrc a + mov %1,a + jnb %2,%3 +%4: + orl %1,#0x80 +%3: +} by { + mov a,%1 + ; Peephole 258.c optimized bitbanging + mov c,%2 + rrc a + mov %1,a +%4: +%3: +} if notVolatile(%1), labelRefCountChange(%3 -1) + +// in_byte>>=1; if(in_bit) in_byte|=0x80; +replace { + mov a,r%1 + clr c + rrc a + mov r%1,a + jnb %2,%3 +%4: + orl ar%1,#0x80 +%3: +} by { + mov a,r%1 + ; Peephole 258.d optimized bitbanging + mov c,%2 + rrc a + mov r%1,a +%4: +%3: +} if labelRefCountChange(%3 -1) + +// out_bit=out_byte&0x80; out_byte<<=1; +// helps f.e. writing data on a 3-wire (SPI) bus +replace { + mov a,%1 + rlc a + mov %2,c + mov a,%1 + add a,%1 + mov %1,a +} by { + mov a,%1 + ; Peephole 258.e optimized bitbanging + add a,%1 + mov %2,c + mov %1,a +} if notVolatile %1 + +// out_bit=out_byte&0x01; out_byte>>=1; +replace { + mov a,%1 + rrc a + mov %2,c + mov a,%1 + clr c + rrc a + mov %1,a +} by { + mov a,%1 + ; Peephole 258.f optimized bitbanging + clr c + rrc a + mov %2,c + mov %1,a +} if notVolatile %1 + +// Peepholes 259.x rely on the correct labelRefCount. Otherwise they are +// not compatible with peepholes 250.x +// Peepholes 250.x add jumps to a previously unused label. If the +// labelRefCount is not increased, peepholes 259.x are (mistakenly) applied. +// (Mail on sdcc-devel 2004-10-25) +// +// applies to f.e. vprintf.c +replace { + sjmp %1 +%2: + ret +} by { + sjmp %1 + ; Peephole 259.a removed redundant label %2 and ret +} if optimizeReturn(), labelRefCount(%2 0) + +// applies to f.e. gets.c +replace { + ljmp %1 +%2: + ret +} by { + ljmp %1 + ; Peephole 259.b removed redundant label %2 and ret +} if optimizeReturn(), labelRefCount(%2 0) + +replace { + sjmp %1 +%2: + sjmp %3 +} by { + sjmp %1 + ; Peephole 259.c removed redundant label %2 and sjmp %3 +} if labelRefCount(%2 0) + +replace { + ljmp %1 +%2: + sjmp %3 +} by { + ljmp %1 + ; Peephole 259.d removed redundant label %2 and sjmp %3 +} if labelRefCount(%2 0) + +replace { + sjmp %1 +%2: + ljmp %3 +} by { + sjmp %1 + ; Peephole 259.e removed redundant label %2 and ljmp %3 +} if labelRefCount(%2 0) + +replace { + ljmp %1 +%2: + ljmp %3 +} by { + ljmp %1 + ; Peephole 259.f removed redundant label %2 and ljmp %3 +} if labelRefCount(%2 0) + + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 +%3: +} by { + ; Peephole 260.a used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 +%3: +} if labelJTInRange + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 + ljmp %9 +%3: +} by { + ; Peephole 260.b used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 + sjmp %9 +%3: +} if labelJTInRange + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 + ljmp %9 + ljmp %10 +%3: +} by { + ; Peephole 260.c used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 + sjmp %9 + sjmp %10 +%3: +} if labelJTInRange + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 + ljmp %9 + ljmp %10 + ljmp %11 +%3: +} by { + ; Peephole 260.d used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 + sjmp %9 + sjmp %10 + sjmp %11 +%3: +} if labelJTInRange + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 + ljmp %9 + ljmp %10 + ljmp %11 + ljmp %12 +%3: +} by { + ; Peephole 260.e used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 + sjmp %9 + sjmp %10 + sjmp %11 + sjmp %12 +%3: +} if labelJTInRange + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 + ljmp %9 + ljmp %10 + ljmp %11 + ljmp %12 + ljmp %13 +%3: +} by { + ; Peephole 260.f used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 + sjmp %9 + sjmp %10 + sjmp %11 + sjmp %12 + sjmp %13 +%3: +} if labelJTInRange + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 + ljmp %9 + ljmp %10 + ljmp %11 + ljmp %12 + ljmp %13 + ljmp %14 +%3: +} by { + ; Peephole 260.g used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 + sjmp %9 + sjmp %10 + sjmp %11 + sjmp %12 + sjmp %13 + sjmp %14 +%3: +} if labelJTInRange + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 + ljmp %9 + ljmp %10 + ljmp %11 + ljmp %12 + ljmp %13 + ljmp %14 + ljmp %15 +%3: +} by { + ; Peephole 260.h used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 + sjmp %9 + sjmp %10 + sjmp %11 + sjmp %12 + sjmp %13 + sjmp %14 + sjmp %15 +%3: +} if labelJTInRange + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 + ljmp %9 + ljmp %10 + ljmp %11 + ljmp %12 + ljmp %13 + ljmp %14 + ljmp %15 + ljmp %16 +%3: +} by { + ; Peephole 260.i used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 + sjmp %9 + sjmp %10 + sjmp %11 + sjmp %12 + sjmp %13 + sjmp %14 + sjmp %15 + sjmp %16 +%3: +} if labelJTInRange + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 + ljmp %9 + ljmp %10 + ljmp %11 + ljmp %12 + ljmp %13 + ljmp %14 + ljmp %15 + ljmp %16 + ljmp %17 +%3: +} by { + ; Peephole 260.j used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 + sjmp %9 + sjmp %10 + sjmp %11 + sjmp %12 + sjmp %13 + sjmp %14 + sjmp %15 + sjmp %16 + sjmp %17 +%3: +} if labelJTInRange + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 + ljmp %9 + ljmp %10 + ljmp %11 + ljmp %12 + ljmp %13 + ljmp %14 + ljmp %15 + ljmp %16 + ljmp %17 + ljmp %18 +%3: +} by { + ; Peephole 260.k used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 + sjmp %9 + sjmp %10 + sjmp %11 + sjmp %12 + sjmp %13 + sjmp %14 + sjmp %15 + sjmp %16 + sjmp %17 + sjmp %18 +%3: +} if labelJTInRange + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 + ljmp %9 + ljmp %10 + ljmp %11 + ljmp %12 + ljmp %13 + ljmp %14 + ljmp %15 + ljmp %16 + ljmp %17 + ljmp %18 + ljmp %19 +%3: +} by { + ; Peephole 260.l used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 + sjmp %9 + sjmp %10 + sjmp %11 + sjmp %12 + sjmp %13 + sjmp %14 + sjmp %15 + sjmp %16 + sjmp %17 + sjmp %18 + sjmp %19 +%3: +} if labelJTInRange + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 + ljmp %9 + ljmp %10 + ljmp %11 + ljmp %12 + ljmp %13 + ljmp %14 + ljmp %15 + ljmp %16 + ljmp %17 + ljmp %18 + ljmp %19 + ljmp %20 +%3: +} by { + ; Peephole 260.m used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 + sjmp %9 + sjmp %10 + sjmp %11 + sjmp %12 + sjmp %13 + sjmp %14 + sjmp %15 + sjmp %16 + sjmp %17 + sjmp %18 + sjmp %19 + sjmp %20 +%3: +} if labelJTInRange + +// applies to: a = (a << 1) | (a >> 15); +replace { + mov a,%1 + rlc a + mov %1,a + mov a,%2 + rlc a + mov %2,a + mov a,%1 + mov acc.0,c + mov %1,a +} by { + mov a,%1 + rlc a + ; Peephole 261.a optimized left rol + xch a,%2 + rlc a + xch a,%2 + mov acc.0,c + mov %1,a +} + +// applies to: a = (a << 15) | (a >> 1); +replace { + mov a,%1 + rrc a + mov %1,a + mov a,%2 + rrc a + mov %2,a + mov a,%1 + mov acc.7,c + mov %1,a +} by { + mov a,%1 + rrc a + ; Peephole 261.b optimized right rol + xch a,%2 + rrc a + xch a,%2 + mov acc.7,c + mov %1,a +} + +replace { + cpl c + cpl c +} by { + ; Peephole 262 removed redundant cpl c +} + +replace { + mov %1,#%2 + inc %1 + inc %1 + inc %1 +} by { + ; Peephole 263.a optimized loading const + mov %1,#(%2 + 3) +} if notVolatile(%1) + +replace { + mov %1,#%2 + inc %1 + inc %1 +} by { + ; Peephole 263.b optimized loading const + mov %1,#(%2 + 2) +} if notVolatile(%1) + +replace { + mov %1,#%2 + inc %1 +} by { + ; Peephole 263.c optimized loading const + mov %1,#(%2 + 1) +} if notVolatile(%1) + + +replace { + clr a + cjne %1,%2,%3 + inc a +%3: + jz %4 +} by { + ; Peephole 264 jump optimization (acc not set) + cjne %1,%2,%4 +%3: +} if labelRefCount(%3 1), labelRefCountChange(%3 -1) + + +replace { + mov %1,c + cpl %1 +} by { + ; Peephole 265 optimized mov/cpl sequence (carry differs) + cpl c + mov %1,c +} if notVolatile(%1) + +replace { + mov %1,c + jb %1,%2 +} by { + ; Peephole 266.a optimized mov/jump sequence + mov %1,c + jc %2 +} if notVolatile(%1) + +replace { + mov %1,c + jnb %1,%2 +} by { + ; Peephole 266.b optimized mov/jump sequence + mov %1,c + jnc %2 +} if notVolatile(%1) + +replace { + jnc %1 + setb %2 + sjmp %3 +%1: + clr %2 +%3: +} by { + ; Peephole 267.a optimized mov bit sequence + mov %2,c +%1: +%3: +} if labelRefCount(%1 1), labelRefCountChange(%1 -1), labelRefCountChange(%3 -1) + +replace { + jc %1 + clr %2 + sjmp %3 +%1: + setb %2 +%3: +} by { + ; Peephole 267.b optimized mov bit sequence + mov %2,c +%1: +%3: +} if labelRefCount(%1 1), labelRefCountChange(%1 -1), labelRefCountChange(%3 -1) + +replace { + mov %1,c + mov %1,c +} by { + ; Peephole 268 removed redundant mov + mov %1,c +} if notVolatile(%1) + +replace { + mov %1,c + mov c,%1 +} by { + ; Peephole 269 removed redundant mov + mov %1,c +} if notVolatile(%1) + +//accessing struct/array on stack +//replace { +// add a,#%1 +// add a,#%2 +//} by { +// ; Peephole 270 removed redundant add (carry might differ, bug 2736282) +// add a,#%1+%2 +//} + +replace { + jz %1 + mov %2,%4 + sjmp %3 +%1: + mov %2,#0x00 +%3: +} by { + jz %1 + ; Peephole 271 optimized ternary operation (acc different) + mov a,%4 +%1: + mov %2,a +%3: +} if operandsNotRelated('a' 'dptr' %2), labelRefCount(%1 1), labelRefCountChange(%3 -1) + + +// left-shifting 8 bit to 16 bit result +// http://sourceforge.net/projects/sdcc/forums/forum/1864/topic/3536144 +replace { + clr a + swap a +} by { + clr a + ; Peephole 272.a removed swap operation on zero +} + +replace { + clr a + anl a,%1 +} by { + clr a + ; Peephole 272.b removed anl operation on zero +} if notVolatile(%1) + +replace { + clr a + mov c,acc.0 +} by { + clr a + ; Peephole 272.c clearing carry directly + clr c +} + + +replace restart { + pop ar%1 +} by { + ; Peephole 300 pop ar%1 removed +} if deadMove(%1) + +replace { + mov r%1,%2 +} by { + ; Peephole 301 mov r%1,%2 removed +} if notVolatile(%2), deadMove(%1) + + +// applies to: void test( char c ) { if( c ) func1(); else func2(); } +replace { + lcall %1 + ret +} by { + ; Peephole 400.a replaced lcall/ret with ljmp + ljmp %1 +} if optimizeReturn(), notSame(%1 '_longjmp') + +// applies to: void test( char c ) { if( c ) func1(); else func2(); } +replace { + lcall %1 +%2: + ret +} by { + ; Peephole 400.b replaced lcall/ret with ljmp + ljmp %1 + ; +} if optimizeReturn(), notSame(%1 '_longjmp'), labelRefCount(%2 0) + +// applies to f.e. scott-bool1.c +replace { + lcall %1 +%2: + ret +} by { + ; Peephole 400.c replaced lcall with ljmp + ljmp %1 +%2: + ret +} if optimizeReturn(), notSame(%1 '_longjmp') + +// for programs less than 2k +replace { + lcall %1 +} by { + ; Peephole 401.a replaced lcall with acall + acall %1 +} if useAcallAjmp + +// for programs less than 2k +replace { + ljmp %1 +} by { + ; Peephole 401.b replaced ljmp with ajmp + ajmp %1 +} if useAcallAjmp + + +// should be one of the last peepholes +replace{ +%1: +} by { + ; Peephole 500 removed redundant label %1 +} if labelRefCount(%1 0) diff --git a/src/mcs51/ralloc.c b/src/mcs51/ralloc.c new file mode 100644 index 0000000..1d900ee --- /dev/null +++ b/src/mcs51/ralloc.c @@ -0,0 +1,3466 @@ +/*------------------------------------------------------------------------ + + SDCCralloc.c - source file for register allocation. (8051) 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 gen51Code (iCode *); +#define D(x) + +/* 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; + bitVect *allBitregs; /* all bit registers */ + bitVect *allBankregs; /* all bank registers */ +} +_G; + +/* Shared with gen.c */ +int mcs51_ptrRegReq; /* one byte pointer register required */ + +/* 8051 registers */ +reg_info regs8051[] = { + {REG_GPR, R7_IDX, REG_GPR, "r7", "ar7", "0", 7, 1}, + {REG_GPR, R6_IDX, REG_GPR, "r6", "ar6", "0", 6, 1}, + {REG_GPR, R5_IDX, REG_GPR, "r5", "ar5", "0", 5, 1}, + {REG_GPR, R4_IDX, REG_GPR, "r4", "ar4", "0", 4, 1}, + {REG_GPR, R3_IDX, REG_GPR, "r3", "ar3", "0", 3, 1}, + {REG_GPR, R2_IDX, REG_GPR, "r2", "ar2", "0", 2, 1}, + {REG_PTR, R1_IDX, REG_PTR, "r1", "ar1", "0", 1, 1}, + {REG_PTR, R0_IDX, REG_PTR, "r0", "ar0", "0", 0, 1}, + {REG_BIT, B0_IDX, REG_BIT, "b0", "b0", "bits", 0, 1}, + {REG_BIT, B1_IDX, REG_BIT, "b1", "b1", "bits", 1, 1}, + {REG_BIT, B2_IDX, REG_BIT, "b2", "b2", "bits", 2, 1}, + {REG_BIT, B3_IDX, REG_BIT, "b3", "b3", "bits", 3, 1}, + {REG_BIT, B4_IDX, REG_BIT, "b4", "b4", "bits", 4, 1}, + {REG_BIT, B5_IDX, REG_BIT, "b5", "b5", "bits", 5, 1}, + {REG_BIT, B6_IDX, REG_BIT, "b6", "b6", "bits", 6, 1}, + {REG_BIT, B7_IDX, REG_BIT, "b7", "b7", "bits", 7, 1}, + {REG_GPR, X8_IDX, REG_GPR, "x8", "x8", "xreg", 0, 1}, + {REG_GPR, X9_IDX, REG_GPR, "x9", "x9", "xreg", 1, 1}, + {REG_GPR, X10_IDX, REG_GPR, "x10", "x10", "xreg", 2, 1}, + {REG_GPR, X11_IDX, REG_GPR, "x11", "x11", "xreg", 3, 1}, + {REG_GPR, X12_IDX, REG_GPR, "x12", "x12", "xreg", 4, 1}, + {REG_CND, CND_IDX, REG_CND, "C", "psw", "0xd0", 0, 1}, + {0, DPL_IDX, 0, "dpl", "dpl", "0x82", 0, 0}, + {0, DPH_IDX, 0, "dph", "dph", "0x83", 0, 0}, + {0, B_IDX, 0, "b", "b", "0xf0", 0, 0}, + {0, A_IDX, 0, "a", "acc", "0xe0", 0, 0}, +}; + +int mcs51_nRegs = 16; +static void spillThis (symbol *); +static void freeAllRegs (); + +/*-----------------------------------------------------------------*/ +/* allocReg - allocates register of given type */ +/*-----------------------------------------------------------------*/ +static reg_info * +allocReg (short type) +{ + int i; + + for (i = 0; i < mcs51_nRegs; i++) + { + /* if type is given as 0 then any + free register will do */ + if (!type && regs8051[i].isFree) + { + regs8051[i].isFree = 0; + if (currFunc) + currFunc->regsUsed = bitVectSetBit (currFunc->regsUsed, i); + return ®s8051[i]; + } + /* otherwise look for specific type + of register */ + if (regs8051[i].isFree && regs8051[i].type == type) + { + regs8051[i].isFree = 0; + if (currFunc) + currFunc->regsUsed = bitVectSetBit (currFunc->regsUsed, i); + return ®s8051[i]; + } + } + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* allocThisReg - allocates a particular register (if free) */ +/*-----------------------------------------------------------------*/ +static reg_info * +allocThisReg (reg_info *reg) +{ + if (!reg->isFree) + return NULL; + + reg->isFree = 0; + if (currFunc) + currFunc->regsUsed = bitVectSetBit (currFunc->regsUsed, reg->rIdx); + + return reg; +} + + +/*-----------------------------------------------------------------*/ +/* mcs51_regWithIdx - returns pointer to register with index number*/ +/*-----------------------------------------------------------------*/ +reg_info * +mcs51_regWithIdx (int idx) +{ + int i; + + for (i = 0; i < sizeof (regs8051) / sizeof (reg_info); i++) + if (regs8051[i].rIdx == idx) + return ®s8051[i]; + + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "regWithIdx not found"); + exit (1); +} + +/*-----------------------------------------------------------------*/ +/* freeReg - frees a register */ +/*-----------------------------------------------------------------*/ +static void +freeReg (reg_info *reg) +{ + if (!reg) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "freeReg - Freeing NULL register"); + exit (1); + } + + reg->isFree = 1; +} + + +/*-----------------------------------------------------------------*/ +/* nFreeRegs - returns number of free registers */ +/*-----------------------------------------------------------------*/ +static int +nFreeRegs (int type) +{ + int i; + int nfr = 0; + + for (i = 0; i < mcs51_nRegs; i++) + if (regs8051[i].isFree && regs8051[i].type == type) + nfr++; + return nfr; +} + +/*-----------------------------------------------------------------*/ +/* nfreeRegsType - free registers with type */ +/*-----------------------------------------------------------------*/ +static int +nfreeRegsType (int type) +{ + int nfr; + if (type == REG_PTR) + { + if ((nfr = nFreeRegs (type)) == 0) + return nFreeRegs (REG_GPR); + } + + return nFreeRegs (type); +} + +/*-----------------------------------------------------------------*/ +/* useReg - marks a register as used */ +/*-----------------------------------------------------------------*/ +static void +useReg (reg_info *reg) +{ + reg->isFree = 0; +} + +/*-----------------------------------------------------------------*/ +/* computeSpillable - given a point find the spillable live ranges */ +/*-----------------------------------------------------------------*/ +static bitVect * +computeSpillable (iCode * ic) +{ + bitVect *spillable; + + /* spillable live ranges are those that are live at this + point . the following categories need to be subtracted + from this set. + a) - those that are already spilt + b) - if being used by this one + c) - defined by this one */ + + spillable = bitVectCopy (ic->rlive); + spillable = bitVectCplAnd (spillable, _G.spiltSet); /* those already spilt */ + spillable = bitVectCplAnd (spillable, ic->uses); /* used in this one */ + bitVectUnSetBit (spillable, ic->defKey); + spillable = bitVectIntersect (spillable, _G.regAssigned); + return spillable; +} + +/*-----------------------------------------------------------------*/ +/* bitType - will return 1 if the symbol has type REG_BIT */ +/*-----------------------------------------------------------------*/ +static int +bitType (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return (sym->regType == REG_BIT ? 1 : 0); +} + +/*-----------------------------------------------------------------*/ +/* noSpilLoc - return true if a variable has no spil location */ +/*-----------------------------------------------------------------*/ +static int +noSpilLoc (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return (sym->usl.spillLoc ? 0 : 1); +} + +/*-----------------------------------------------------------------*/ +/* hasSpilLoc - will return 1 if the symbol has spil location */ +/*-----------------------------------------------------------------*/ +static int +hasSpilLoc (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return (sym->usl.spillLoc ? 1 : 0); +} + +/*-----------------------------------------------------------------*/ +/* directSpilLoc - will return 1 if the spillocation is in direct */ +/*-----------------------------------------------------------------*/ +static int +directSpilLoc (symbol * sym, eBBlock * ebp, iCode * ic) +{ + if (sym->usl.spillLoc && (IN_DIRSPACE (SPEC_OCLS (sym->usl.spillLoc->etype)))) + return 1; + else + return 0; +} + +/*-----------------------------------------------------------------*/ +/* hasSpilLocnoUptr - will return 1 if the symbol has spil */ +/* location but is not used as a pointer */ +/*-----------------------------------------------------------------*/ +static int +hasSpilLocnoUptr (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return ((sym->usl.spillLoc && !sym->uptr) ? 1 : 0); +} + +/*-----------------------------------------------------------------*/ +/* rematable - will return 1 if the remat flag is set */ +/*-----------------------------------------------------------------*/ +static int +rematable (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return sym->remat; +} + +/*-----------------------------------------------------------------*/ +/* notUsedInRemaining - not used or defined in remain of the block */ +/*-----------------------------------------------------------------*/ +static int +notUsedInRemaining (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return ((usedInRemaining (operandFromSymbol (sym), ic) ? 0 : 1) && allDefsOutOfRange (sym->defs, ebp->fSeq, ebp->lSeq)); +} + +/*-----------------------------------------------------------------*/ +/* allLRs - return true for all */ +/*-----------------------------------------------------------------*/ +static int +allLRs (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return 1; +} + +/*-----------------------------------------------------------------*/ +/* liveRangesWith - applies function to a given set of live range */ +/*-----------------------------------------------------------------*/ +static set * +liveRangesWith (bitVect * lrs, int (func) (symbol *, eBBlock *, iCode *), eBBlock * ebp, iCode * ic) +{ + set *rset = NULL; + int i; + + if (!lrs || !lrs->size) + return NULL; + + for (i = 1; i < lrs->size; i++) + { + symbol *sym; + if (!bitVectBitValue (lrs, i)) + continue; + + /* if we don't find it in the live range + hash table we are in serious trouble */ + if (!(sym = hTabItemWithKey (liveRanges, i))) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "liveRangesWith could not find liveRange"); + exit (1); + } + + if (func (sym, ebp, ic) && bitVectBitValue (_G.regAssigned, sym->key)) + addSetHead (&rset, sym); + } + + return rset; +} + + +/*-----------------------------------------------------------------*/ +/* leastUsedLR - given a set determines which is the least used */ +/*-----------------------------------------------------------------*/ +static symbol * +leastUsedLR (set * sset) +{ + symbol *sym = NULL, *lsym = NULL; + + sym = lsym = setFirstItem (sset); + + if (!lsym) + return NULL; + + for (; lsym; lsym = setNextItem (sset)) + { + /* if usage is the same then prefer + to spill the smaller of the two */ + if (lsym->used == sym->used) + if (getSize (lsym->type) < getSize (sym->type)) + sym = lsym; + + /* if less usage */ + if (lsym->used < sym->used) + sym = lsym; + } + + setToNull ((void *) &sset); + sym->blockSpil = 0; + return sym; +} + +/*-----------------------------------------------------------------*/ +/* 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) + && (IS_BIT (sym->type) == IS_BIT (fsym->type))) + { + *sloc = sym; + return 1; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* spillLRWithPtrReg :- will spil those live ranges which use PTR */ +/*-----------------------------------------------------------------*/ +static void +spillLRWithPtrReg (symbol * forSym) +{ + symbol *lrsym; + reg_info *r0, *r1; + int k; + + if (!_G.regAssigned || bitVectIsZero (_G.regAssigned)) + return; + + r0 = mcs51_regWithIdx (R0_IDX); + r1 = mcs51_regWithIdx (R1_IDX); + + /* for all live ranges */ + for (lrsym = hTabFirstItem (liveRanges, &k); lrsym; lrsym = hTabNextItem (liveRanges, &k)) + { + int j; + + /* if no registers assigned to it or spilt */ + /* if it does not overlap this then + no need to spill it */ + + if (lrsym->isspilt || !lrsym->nRegs || (lrsym->liveTo < forSym->liveFrom)) + continue; + + /* go thru the registers : if it is either + r0 or r1 then spill it */ + for (j = 0; j < lrsym->nRegs; j++) + if (lrsym->regs[j] == r0 || lrsym->regs[j] == r1) + { + spillThis (lrsym); + break; + } + } +} + +/*-----------------------------------------------------------------*/ +/* createStackSpil - create a location on the stack to spil */ +/*-----------------------------------------------------------------*/ +static symbol * +createStackSpil (symbol * sym) +{ + symbol *sloc = NULL; + int useXstack, model; + + struct dbuf_s dbuf; + + /* 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); + if (!IS_BIT (sloc->etype)) + { + SPEC_SCLS (sloc->etype) = S_DATA; + } + else if (SPEC_SCLS (sloc->etype) == S_SBIT) + { + SPEC_SCLS (sloc->etype) = S_BIT; + } + 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; +} + +/*-----------------------------------------------------------------*/ +/* isSpiltOnStack - returns true if the spil location is on stack */ +/* or otherwise needs a pointer register */ +/*-----------------------------------------------------------------*/ +static bool +isSpiltOnStack (symbol * sym) +{ + sym_link *etype; + + if (!sym) + return FALSE; + + if (!sym->isspilt) + return FALSE; + +/* if (sym->_G.stackSpil) */ +/* return TRUE; */ + + if (!sym->usl.spillLoc) + return FALSE; + + if (sym->usl.spillLoc->onStack || sym->usl.spillLoc->iaccess) + return TRUE; + + etype = getSpec (sym->usl.spillLoc->type); + if (IN_STACK (etype)) + return TRUE; + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* 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]) + { + freeReg (sym->regs[i]); + sym->regs[i] = NULL; + } + } + + /* if spilt on stack then free up r0 & r1 + if they could have been assigned to some + LIVE ranges */ + if (!mcs51_ptrRegReq && isSpiltOnStack (sym)) + { + spillLRWithPtrReg (sym); + mcs51_ptrRegReq++; + } + + if (sym->usl.spillLoc && !sym->remat) + sym->usl.spillLoc->allocreq++; + return; +} + +/*-----------------------------------------------------------------*/ +/* selectSpil - select a iTemp to spil : rather a simple procedure */ +/*-----------------------------------------------------------------*/ +static symbol * +selectSpil (iCode * ic, eBBlock * ebp, symbol * forSym) +{ + bitVect *lrcs = NULL; + set *selectS; + symbol *sym; + + /* get the spillable live ranges */ + lrcs = computeSpillable (ic); + + /* remove incompatible registers */ + if ((forSym->regType == REG_PTR) || (forSym->regType == REG_GPR)) + { + selectS = liveRangesWith (lrcs, bitType, ebp, ic); + + for (sym = setFirstItem (selectS); sym; sym = setNextItem (selectS)) + { + bitVectUnSetBit (lrcs, sym->key); + } + } + + /* get all live ranges that are rematerializable */ + if ((selectS = liveRangesWith (lrcs, rematable, ebp, ic))) + { + /* return the least used of these */ + return leastUsedLR (selectS); + } + + /* get live ranges with spillLocations in direct space */ + if ((selectS = liveRangesWith (lrcs, directSpilLoc, ebp, ic))) + { + sym = leastUsedLR (selectS); + strncpyz (sym->rname, + sym->usl.spillLoc->rname[0] ? sym->usl.spillLoc->rname : sym->usl.spillLoc->name, sizeof (sym->rname)); + sym->spildir = 1; + /* mark it as allocation required */ + sym->usl.spillLoc->allocreq++; + return sym; + } + + /* if the symbol is local to the block then */ + if (forSym->liveTo < ebp->lSeq) + { + /* check if there are any live ranges allocated + to registers that are not used in this block */ + if (!_G.blockSpil && (selectS = liveRangesWith (lrcs, notUsedInBlock, ebp, ic))) + { + sym = leastUsedLR (selectS); + /* if this is not rematerializable */ + if (!sym->remat) + { + _G.blockSpil++; + sym->blockSpil = 1; + } + return sym; + } + + /* check if there are any live ranges that are + not used in the remainder of the block */ + if (!_G.blockSpil && !isiCodeInFunctionCall (ic) && (selectS = liveRangesWith (lrcs, notUsedInRemaining, ebp, ic))) + { + sym = leastUsedLR (selectS); + if (sym != forSym) + { + if (!sym->remat) + { + sym->remainSpil = 1; + _G.blockSpil++; + } + return sym; + } + } + } + + /* find live ranges with spillocation && not used as pointers */ + if ((selectS = liveRangesWith (lrcs, hasSpilLocnoUptr, ebp, ic))) + { + sym = leastUsedLR (selectS); + /* mark this as allocation required */ + sym->usl.spillLoc->allocreq++; + return sym; + } + + /* find live ranges with spillocation */ + if ((selectS = liveRangesWith (lrcs, hasSpilLoc, ebp, ic))) + { + sym = leastUsedLR (selectS); + sym->usl.spillLoc->allocreq++; + return sym; + } + + /* couldn't find then we need to create a spil + location on the stack, for which one? + the least used ofcourse */ + if ((selectS = liveRangesWith (lrcs, noSpilLoc, ebp, ic))) + { + /* return a created spil location */ + sym = createStackSpil (leastUsedLR (selectS)); + sym->usl.spillLoc->allocreq++; + return sym; + } + + /* this is an extreme situation we will spill + this one : happens very rarely but it does happen */ + spillThis (forSym); + return forSym; +} + +/*-----------------------------------------------------------------*/ +/* spilSomething - spil some variable & mark registers as free */ +/*-----------------------------------------------------------------*/ +static bool +spilSomething (iCode * ic, eBBlock * ebp, symbol * forSym) +{ + symbol *ssym; + int i; + + /* get something we can spil */ + ssym = selectSpil (ic, ebp, forSym); + + /* mark it as spilt */ + ssym->isspilt = ssym->spillA = 1; + _G.spiltSet = bitVectSetBit (_G.spiltSet, ssym->key); + + /* mark it as not register assigned & + take it away from the set */ + bitVectUnSetBit (_G.regAssigned, ssym->key); + bitVectUnSetBit (_G.totRegAssigned, ssym->key); + + /* mark the registers as free */ + for (i = 0; i < ssym->nRegs; i++) + if (ssym->regs[i]) + freeReg (ssym->regs[i]); + + /* if spilt on stack then free up r0 & r1 + if they could have been assigned to as gprs */ + if (!mcs51_ptrRegReq && isSpiltOnStack (ssym)) + { + spillLRWithPtrReg (ssym); + mcs51_ptrRegReq++; + } + + /* if this was a block level spil then insert push & pop + at the start & end of block respectively */ + if (ssym->blockSpil) + { + iCode *nic = newiCode (IPUSH, operandFromSymbol (ssym), NULL); + /* add push to the start of the block */ + addiCodeToeBBlock (ebp, nic, (ebp->sch->op == LABEL ? ebp->sch->next : ebp->sch)); + nic = newiCode (IPOP, operandFromSymbol (ssym), NULL); + /* add pop to the end of the block */ + addiCodeToeBBlock (ebp, nic, NULL); + } + + /* if spilt because not used in the remainder of the + block then add a push before this instruction and + a pop at the end of the block */ + if (ssym->remainSpil) + { + iCode *nic = newiCode (IPUSH, operandFromSymbol (ssym), NULL); + /* add push just before this instruction */ + addiCodeToeBBlock (ebp, nic, ic); + + nic = newiCode (IPOP, operandFromSymbol (ssym), NULL); + /* add pop to the end of the block */ + addiCodeToeBBlock (ebp, nic, NULL); + } + + if (ssym == forSym) + return FALSE; + else + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* getRegPtr - will try for PTR if not a GPR type if not spil */ +/*-----------------------------------------------------------------*/ +static reg_info * +getRegPtr (iCode * ic, eBBlock * ebp, symbol * sym) +{ + reg_info *reg; + int j; + +tryAgain: + /* try for a ptr type */ + if ((reg = allocReg (REG_PTR))) + return reg; + + /* try for gpr type */ + if ((reg = allocReg (REG_GPR))) + return reg; + + /* we have to spil */ + if (!spilSomething (ic, ebp, sym)) + return NULL; + + /* make sure partially assigned registers aren't reused */ + for (j = 0; j <= sym->nRegs; j++) + if (sym->regs[j]) + sym->regs[j]->isFree = 0; + + /* this looks like an infinite loop but + in reality selectSpil will abort */ + goto tryAgain; +} + +/*-----------------------------------------------------------------*/ +/* getRegGpr - will try for GPR if not spil */ +/*-----------------------------------------------------------------*/ +static reg_info * +getRegGpr (iCode * ic, eBBlock * ebp, symbol * sym) +{ + reg_info *reg; + int j; + +tryAgain: + /* try for gpr type */ + if ((reg = allocReg (REG_GPR))) + return reg; + + if (!mcs51_ptrRegReq) + if ((reg = allocReg (REG_PTR))) + return reg; + + /* we have to spil */ + if (!spilSomething (ic, ebp, sym)) + return NULL; + + /* make sure partially assigned registers aren't reused */ + for (j = 0; j <= sym->nRegs; j++) + if (sym->regs[j]) + sym->regs[j]->isFree = 0; + + /* this looks like an infinite loop but + in reality selectSpil will abort */ + goto tryAgain; +} + +/*-----------------------------------------------------------------*/ +/* getRegBit - will try for Bit if not spill this */ +/*-----------------------------------------------------------------*/ +static reg_info * +getRegBitTry (symbol * sym) +{ + reg_info *reg; + + /* try for a bit type */ + if ((reg = allocReg (REG_BIT))) + return reg; + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* getRegPtrNoSpil - get it cannot be spilt */ +/*-----------------------------------------------------------------*/ +static reg_info * +getRegPtrNoSpil () +{ + reg_info *reg; + + /* try for a ptr type */ + if ((reg = allocReg (REG_PTR))) + return reg; + + /* try for gpr type */ + if ((reg = allocReg (REG_GPR))) + return reg; + + assert (0); + + /* just to make the compiler happy */ + return 0; +} + +/*-----------------------------------------------------------------*/ +/* getRegGprNoSpil - get it cannot be spilt */ +/*-----------------------------------------------------------------*/ +static reg_info * +getRegGprNoSpil () +{ + reg_info *reg; + if ((reg = allocReg (REG_GPR))) + return reg; + + if (!mcs51_ptrRegReq) + if ((reg = allocReg (REG_PTR))) + return reg; + + assert (0); + + /* just to make the compiler happy */ + return 0; +} + +/*-----------------------------------------------------------------*/ +/* getRegBitNoSpil - get it cannot be spilt */ +/*-----------------------------------------------------------------*/ +static reg_info * +getRegBitNoSpil () +{ + reg_info *reg; + + /* try for a ptr type */ + if ((reg = allocReg (REG_BIT))) + return reg; + + /* try for gpr type */ + if ((reg = allocReg (REG_GPR))) + return reg; + + assert (0); + + /* just to make the compiler happy */ + return 0; +} + +/*-----------------------------------------------------------------*/ +/* symHasReg - symbol has a given register */ +/*-----------------------------------------------------------------*/ +static bool +symHasReg (symbol * sym, reg_info * reg) +{ + int i; + + for (i = 0; i < sym->nRegs; i++) + if (sym->regs[i] == reg) + return TRUE; + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* updateRegUsage - update the registers in use at the start of */ +/* this icode */ +/*-----------------------------------------------------------------*/ +static void +updateRegUsage (iCode * ic) +{ + int reg; + + for (reg = 0; reg < mcs51_nRegs; reg++) + { + if (regs8051[reg].isFree) + { + ic->riu &= ~(1 << regs8051[reg].offset); + } + else + { + ic->riu |= (1 << regs8051[reg].offset); + BitBankUsed |= (reg >= 8); + } + } +} + +/*-----------------------------------------------------------------*/ +/* 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; + symbol *result; + + for (sym = hTabFirstItem (liveRanges, &k); sym; sym = hTabNextItem (liveRanges, &k)) + { + symbol *psym = NULL; + /* 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; + } + + if (!bitVectBitValue (_G.regAssigned, sym->key)) + continue; + + /* special case check if this is an IFX & + the privious one was a pop and the + previous one was not spilt then keep track + of the symbol */ + if (ic->op == IFX && ic->prev && ic->prev->op == IPOP && !ic->prev->parmPush && !OP_SYMBOL (IC_LEFT (ic->prev))->isspilt) + psym = OP_SYMBOL (IC_LEFT (ic->prev)); + + if (sym->nRegs) + { + int i = 0; + + bitVectUnSetBit (_G.regAssigned, sym->key); + + /* if the result of this one needs registers + and does not have it then assign it right + away */ + if (IC_RESULT (ic) && + !( SKIP_IC2 (ic) || /* not a special icode */ + ic->op == JUMPTABLE || + ic->op == IFX || + ic->op == IPUSH || + ic->op == IPOP || + ic->op == RETURN || + POINTER_SET (ic) ) && + (result = OP_SYMBOL (IC_RESULT (ic))) && /* has a result */ + result->liveTo > ic->seq && /* and will live beyond this */ + result->liveTo <= ebp->lSeq && /* does not go beyond this block */ + result->liveFrom == ic->seq && /* does not start before here */ + result->regType == sym->regType && /* same register types */ + result->nRegs && /* which needs registers */ + !result->isspilt && /* and does not already have them */ + !result->remat && + !bitVectBitValue (_G.regAssigned, result->key) && + /* the number of free regs + number of regs in this LR + can accomodate the what result Needs */ + ((nfreeRegsType (result->regType) + sym->nRegs) >= result->nRegs)) + { + for (i = 0; i < result->nRegs; i++) + if (i < sym->nRegs) + result->regs[i] = sym->regs[i]; + else + result->regs[i] = getRegGpr (ic, ebp, result); + + _G.regAssigned = bitVectSetBit (_G.regAssigned, result->key); + _G.totRegAssigned = bitVectSetBit (_G.totRegAssigned, result->key); + } + + /* free the remaining */ + for (; i < sym->nRegs; i++) + { + if (psym) + { + if (!symHasReg (psym, sym->regs[i])) + freeReg (sym->regs[i]); + } + else + freeReg (sym->regs[i]); + } + } + } +} + + +/*-----------------------------------------------------------------*/ +/* 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; +} + +/*-----------------------------------------------------------------*/ +/* willCauseSpill - determines if allocating will cause a spill */ +/*-----------------------------------------------------------------*/ +static int +willCauseSpill (int nr, int rt) +{ + /* first check if there are any available registers + of the type required */ + if (rt == REG_PTR) + { + /* special case for pointer type + if pointer type not available then + check for type gpr */ + if (nFreeRegs (rt) >= nr) + return 0; + if (nFreeRegs (REG_GPR) >= nr) + return 0; + } + else if (rt == REG_BIT) + { + if (nFreeRegs (rt) >= nr) + return 0; + } + else + { + if (mcs51_ptrRegReq) + { + if (nFreeRegs (rt) >= nr) + return 0; + } + else + { + if (nFreeRegs (REG_PTR) + nFreeRegs (REG_GPR) >= nr) + return 0; + } + } + + /* it will cause a spil */ + return 1; +} + +/*-----------------------------------------------------------------*/ +/* positionRegs - the allocator can allocate same registers to */ +/* result and operand, if this happens make sure they are in the */ +/* same position as the operand otherwise chaos results */ +/*-----------------------------------------------------------------*/ +static int +positionRegs (symbol * result, symbol * opsym, int chOp) +{ + int count = min (result->nRegs, opsym->nRegs); + int i, j = 0, shared = 0; + int change = 0; + + /* if the result has been spilt then cannot share */ + if (opsym->isspilt) + return 0; + + for (;;) + { + shared = 0; + /* first make sure that they actually share */ + for (i = 0; i < count; i++) + for (j = 0; j < count; j++) + if (result->regs[i] == opsym->regs[j] && i != j) + { + shared = 1; + goto xchgPositions; + } +xchgPositions: + if (shared) + if (!chOp) + { + reg_info *tmp = result->regs[i]; + result->regs[i] = result->regs[j]; + result->regs[j] = tmp; + change++; + } + else + { + reg_info *tmp = opsym->regs[i]; + opsym->regs[i] = opsym->regs[j]; + opsym->regs[j] = tmp; + change++; + } + else + return change; + } +} + +/*------------------------------------------------------------------*/ +/* 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; + + 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)); + bitVect *spillable; + int willCS; + int j; + int ptrRegSet = 0; + + /* 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; + } + + /* do not try to spil bit registers as it won't work */ + if (sym->regType != REG_BIT) + { + /* 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; + } + + willCS = willCauseSpill (sym->nRegs, sym->regType); + /* if this is a bit variable then don't use precious registers + along with expensive bit-to-char conversions but just spill + it */ + if (willCS && SPEC_NOUN (sym->etype) == V_BIT) + { + spillThis (sym); + continue; + } + + /* if trying to allocate this will cause + a spill and there is nothing to spill + or this one is rematerializable then + spill this one */ + spillable = computeSpillable (ic); + if (sym->remat || (willCS && bitVectIsZero (spillable))) + { + spillThis (sym); + continue; + } + + /* If the live range preceeds the point of definition + then ideally we must take into account registers that + have been allocated after sym->liveFrom but freed + before ic->seq. This is complicated, so spill this + symbol instead and let fillGaps handle the allocation. */ + if (sym->liveFrom < ic->seq) + { + spillThis (sym); + continue; + } + + /* if it has a spillocation & is used less than + all other live ranges then spill this */ + if (willCS) + { + if (sym->usl.spillLoc) + { + symbol *leastUsed = leastUsedLR (liveRangesWith (spillable, + allLRs, ebbs[i], ic)); + if (leastUsed && leastUsed->used > sym->used) + { + spillThis (sym); + continue; + } + } + else + { + /* if none of the liveRanges have a spillLocation then better + to spill this one than anything else already assigned to registers */ + if (liveRangesWith (spillable, noSpilLoc, ebbs[i], ic)) + { + /* if this is local to this block then we might find a block spil */ + if (!(sym->liveFrom >= ebbs[i]->fSeq && sym->liveTo <= ebbs[i]->lSeq)) + { + spillThis (sym); + continue; + } + } + } + } + } + /* if we need ptr regs for the right side + then mark it */ + if (POINTER_GET (ic) && IS_SYMOP (IC_LEFT (ic)) + && getSize (OP_SYMBOL (IC_LEFT (ic))->type) <= (unsigned int) NEARPTRSIZE) + { + mcs51_ptrRegReq++; + ptrRegSet = 1; + } + if (IC_LEFT (ic) && IS_SYMOP (IC_LEFT (ic)) && SPEC_OCLS (OP_SYMBOL (IC_LEFT (ic))->etype) == idata) + { + mcs51_ptrRegReq++; + ptrRegSet = 1; + } + if (IC_RIGHT (ic) && IS_SYMOP (IC_RIGHT (ic)) && SPEC_OCLS (OP_SYMBOL (IC_RIGHT (ic))->etype) == idata) + { + mcs51_ptrRegReq++; + ptrRegSet = 1; + } + + /* else we assign registers to it */ + _G.regAssigned = bitVectSetBit (_G.regAssigned, sym->key); + _G.totRegAssigned = bitVectSetBit (_G.totRegAssigned, sym->key); + + for (j = 0; j < sym->nRegs; j++) + { + sym->regs[j] = NULL; + if (sym->regType == REG_PTR) + sym->regs[j] = getRegPtr (ic, ebbs[i], sym); + else + { + if (sym->regType == REG_BIT) /* Try to allocate to bit register if possible */ + sym->regs[j] = getRegBitTry (sym); + if (ic->op == CAST && IS_SYMOP (IC_RIGHT (ic))) + { + symbol *right = OP_SYMBOL (IC_RIGHT (ic)); + + if (right->regs[j] && (right->regType != REG_BIT)) + sym->regs[j] = allocThisReg (right->regs[j]); + } + if (!sym->regs[j]) + sym->regs[j] = getRegGpr (ic, ebbs[i], sym); + } + + /* if the allocation failed which means + this was spilt then break */ + if (!sym->regs[j]) + { + int i; + for (i = 0; i < sym->nRegs; i++) + sym->regs[i] = NULL; + break; + } + } + + /* for debugging prefer to keep the sym in ascending + registers so sort them by address */ + if (sym->regs[0]) + { + for (j = 0; j < sym->nRegs - 1; j++) + { + int k; + for (k=j+1; k<sym->nRegs; k++) + { + if (sym->regs[j]->offset > sym->regs[k]->offset) + { + reg_info *tmp = sym->regs[j]; + sym->regs[j] = sym->regs[k]; + sym->regs[k] = tmp; + } + } + } + } + + if (!POINTER_SET (ic) && !POINTER_GET (ic)) + { + /* if it shares registers with operands make sure + that they are in the same position */ + if (IC_LEFT (ic) && IS_SYMOP (IC_LEFT (ic)) && OP_SYMBOL (IC_LEFT (ic))->nRegs) + { + positionRegs (OP_SYMBOL (IC_RESULT (ic)), OP_SYMBOL (IC_LEFT (ic)), 0); + } + /* do the same for the right operand */ + if (IC_RIGHT (ic) && IS_SYMOP (IC_RIGHT (ic)) && OP_SYMBOL (IC_RIGHT (ic))->nRegs) + { + positionRegs (OP_SYMBOL (IC_RESULT (ic)), OP_SYMBOL (IC_RIGHT (ic)), 0); + } + } + + if (ptrRegSet) + { + mcs51_ptrRegReq--; + ptrRegSet = 0; + } + } + } + } + + /* 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); + } + } +} + +/*-----------------------------------------------------------------*/ +/* fillGaps - Try to fill in the Gaps left by Pass1 */ +/*-----------------------------------------------------------------*/ +static void +fillGaps (void) +{ + symbol *sym = NULL; + int key = 0; + int pass; + iCode *ic = NULL; + + if (getenv ("DISABLE_FILL_GAPS")) + return; + + /* look for liveranges that were spilt by the allocator */ + for (sym = hTabFirstItem (liveRanges, &key); sym; sym = hTabNextItem (liveRanges, &key)) + { + int i; + int pdone = 0; + + if (!sym->spillA || !sym->clashes || sym->remat) + continue; + + /* if spilt in direct space the original rname is lost */ + if (sym->usl.spillLoc && (IN_DIRSPACE (SPEC_OCLS (sym->usl.spillLoc->etype)))) + continue; + + /* find the liveRanges this one clashes with, that are + still assigned to registers & mark the registers as used */ + for (i = 0; i < sym->clashes->size; i++) + { + int k; + symbol *clr; + + if (bitVectBitValue (sym->clashes, i) == 0 || /* those that clash with this */ + bitVectBitValue (_G.totRegAssigned, i) == 0) /* and are still assigned to registers */ + continue; + + clr = hTabItemWithKey (liveRanges, i); + assert (clr); + + /* mark these registers as used */ + for (k = 0; k < clr->nRegs; k++) + useReg (clr->regs[k]); + } + + if (willCauseSpill (sym->nRegs, sym->regType)) + { + /* NOPE :( clear all registers & and continue */ + freeAllRegs (); + continue; + } + + ic = NULL; + for (i = 0; i < sym->defs->size; i++) + { + if (bitVectBitValue (sym->defs, i)) + { + if (!(ic = hTabItemWithKey (iCodehTab, i))) + continue; + if (ic->op == CAST) + break; + } + } + + D (printf ("Attempting fillGaps on %s: [", sym->name)); + /* THERE IS HOPE !!!! */ + for (i = 0; i < sym->nRegs; i++) + { + if (sym->regType == REG_PTR) + sym->regs[i] = getRegPtrNoSpil (); + else if (sym->regType == REG_BIT) + sym->regs[i] = getRegBitNoSpil (); + else + { + sym->regs[i] = NULL; + if (ic && ic->op == CAST && IS_SYMOP (IC_RIGHT (ic))) + { + symbol *right = OP_SYMBOL (IC_RIGHT (ic)); + + if (right->regs[i]) + sym->regs[i] = allocThisReg (right->regs[i]); + } + if (!sym->regs[i]) + sym->regs[i] = getRegGprNoSpil (); + } + D (printf ("%s ", sym->regs[i]->name)); + } + D (printf ("]\n")); + + /* For all its definitions check if the registers + allocated needs positioning NOTE: we can position + only ONCE if more than One positioning required + then give up. + We may need to perform the checks twice; once to + position the registers as needed, the second to + verify any register repositioning is still + compatible. + */ + sym->isspilt = 0; + for (pass = 0; pass < 2; pass++) + { + D (printf (" checking definitions\n")); + for (i = 0; i < sym->defs->size; i++) + { + if (bitVectBitValue (sym->defs, i)) + { + if (!(ic = hTabItemWithKey (iCodehTab, i))) + continue; + D (printf (" ic->seq = %d\n", ic->seq)); + if (SKIP_IC (ic)) + continue; + assert (isSymbolEqual (sym, OP_SYMBOL (IC_RESULT (ic)))); /* just making sure */ + /* if left is assigned to registers */ + if (IS_SYMOP (IC_LEFT (ic))) + { + D (printf (" left = ")); + D (printOperand (IC_LEFT (ic), NULL)); + } + if (IS_SYMOP (IC_LEFT (ic)) && bitVectBitValue (_G.totRegAssigned, OP_SYMBOL (IC_LEFT (ic))->key)) + { + pdone += (positionRegs (sym, OP_SYMBOL (IC_LEFT (ic)), 0) > 0); + } + if (IS_SYMOP (IC_RIGHT (ic))) + { + D (printf (" right = ")); + D (printOperand (IC_RIGHT (ic), NULL)); + } + if (IS_SYMOP (IC_RIGHT (ic)) && bitVectBitValue (_G.totRegAssigned, OP_SYMBOL (IC_RIGHT (ic))->key)) + { + pdone += (positionRegs (sym, OP_SYMBOL (IC_RIGHT (ic)), 0) > 0); + } + D (printf (" pdone = %d\n", pdone)); + if (pdone > 1) + break; + } + } + D (printf (" checking uses\n")); + for (i = 0; i < sym->uses->size; i++) + { + if (bitVectBitValue (sym->uses, i)) + { + iCode *ic; + if (!(ic = hTabItemWithKey (iCodehTab, i))) + continue; + D (printf (" ic->seq = %d\n", ic->seq)); + if (SKIP_IC (ic)) + continue; + if (POINTER_SET (ic) || POINTER_GET (ic)) + continue; + + /* if result is assigned to registers */ + if (IS_SYMOP (IC_RESULT (ic))) + { + D (printf (" result = ")); + D (printOperand (IC_RESULT (ic), NULL)); + } + if (IS_SYMOP (IC_RESULT (ic)) && bitVectBitValue (_G.totRegAssigned, OP_SYMBOL (IC_RESULT (ic))->key)) + { + pdone += (positionRegs (sym, OP_SYMBOL (IC_RESULT (ic)), 0) > 0); + } + D (printf (" pdone = %d\n", pdone)); + if (pdone > 1) + break; + } + } + if (pdone == 0) + break; /* second pass only if regs repositioned */ + if (pdone > 1) + break; + } + D (printf (" sym->regs = [")); + for (i = 0; i < sym->nRegs; i++) + D (printf ("%s ", sym->regs[i]->name)); + D (printf ("]\n")); + /* had to position more than once GIVE UP */ + if (pdone > 1) + { + /* UNDO all the changes we made to try this */ + sym->isspilt = 1; + for (i = 0; i < sym->nRegs; i++) + { + sym->regs[i] = NULL; + } + freeAllRegs (); + D (printf + ("Fill Gap gave up due to positioning for %s in function %s\n", sym->name, currFunc ? currFunc->name : "UNKNOWN")); + continue; + } + D (printf ("FILLED GAP for %s in function %s\n", sym->name, currFunc ? currFunc->name : "UNKNOWN")); + + _G.totRegAssigned = bitVectSetBit (_G.totRegAssigned, sym->key); + sym->isspilt = sym->spillA = 0; + sym->usl.spillLoc->allocreq--; + freeAllRegs (); + } +} + +/*-----------------------------------------------------------------*/ +/* findAllBitregs :- returns bit vector of all bit registers */ +/*-----------------------------------------------------------------*/ +static bitVect * +findAllBitregs (void) +{ + bitVect *rmask = newBitVect (mcs51_nRegs); + int j; + + for (j = 0; j < mcs51_nRegs; j++) + { + if (regs8051[j].type == REG_BIT) + rmask = bitVectSetBit (rmask, regs8051[j].rIdx); + } + + return rmask; +} + +/*-----------------------------------------------------------------*/ +/* mcs51_allBitregs :- returns bit vector of all bit registers */ +/*-----------------------------------------------------------------*/ +bitVect * +mcs51_allBitregs (void) +{ + return _G.allBitregs; +} + +/*-----------------------------------------------------------------*/ +/* findAllBankregs :- returns bit vector of all bank registers */ +/*-----------------------------------------------------------------*/ +static bitVect * +findAllBankregs (void) +{ + bitVect *rmask = newBitVect (mcs51_nRegs); + int j; + + for (j = 0; j < mcs51_nRegs; j++) + { + if ((regs8051[j].type == REG_GPR) || (regs8051[j].type == REG_PTR)) + rmask = bitVectSetBit (rmask, regs8051[j].rIdx); + } + + return rmask; +} + +/*-----------------------------------------------------------------*/ +/* mcs51_allBankregs :- returns bit vector of all bank registers */ +/*-----------------------------------------------------------------*/ +bitVect * +mcs51_allBankregs (void) +{ + return _G.allBankregs; +} + +/*-----------------------------------------------------------------*/ +/* rUmaskForOp :- returns register mask for an operand */ +/*-----------------------------------------------------------------*/ +bitVect * +mcs51_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 (mcs51_nRegs); + + for (j = 0; j < sym->nRegs; j++) + { + if (sym->regs[j]) /* EEP - debug */ + 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 (mcs51_nRegs); + + /* do the special cases first */ + if (ic->op == IFX) + { + rmask = bitVectUnion (rmask, mcs51_rUmaskForOp (IC_COND (ic))); + goto ret; + } + + /* for the jumptable */ + if (ic->op == JUMPTABLE) + { + rmask = bitVectUnion (rmask, mcs51_rUmaskForOp (IC_JTCOND (ic))); + goto ret; + } + + /* of all other cases */ + if (IC_LEFT (ic)) + rmask = bitVectUnion (rmask, mcs51_rUmaskForOp (IC_LEFT (ic))); + + if (IC_RIGHT (ic)) + rmask = bitVectUnion (rmask, mcs51_rUmaskForOp (IC_RIGHT (ic))); + + if (IC_RESULT (ic)) + rmask = bitVectUnion (rmask, mcs51_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->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 (mcs51_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]) + ic->rMask = bitVectSetBit (ic->rMask, sym->regs[k]->rIdx); + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* rematStr - returns the rematerialized string for a remat var */ +/*-----------------------------------------------------------------*/ +static char * +rematStr (symbol * sym) +{ + iCode *ic = sym->rematiCode; + int offset = 0; + struct dbuf_s dbuf; + + while (1) + { + /* if plus adjust offset to right hand side */ + if (ic->op == '+') + { + offset += (int) operandLitValue (IC_RIGHT (ic)); + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + continue; + } + + /* if minus adjust offset to right hand side */ + if (ic->op == '-') + { + offset -= (int) operandLitValue (IC_RIGHT (ic)); + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + continue; + } + + /* cast then continue */ + if (IS_CAST_ICODE (ic)) + { + ic = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + continue; + } + /* we reached the end */ + break; + } + + dbuf_init (&dbuf, 128); + if (offset) + { + dbuf_printf (&dbuf, "(%s %c 0x%04x)", OP_SYMBOL (IC_LEFT (ic))->rname, offset >= 0 ? '+' : '-', abs (offset) & 0xffff); + } + else + { + dbuf_append_str (&dbuf, OP_SYMBOL (IC_LEFT (ic))->rname); + } + return dbuf_detach_c_str (&dbuf); +} + +/*------------------------------------------------------------------*/ +/* isBitVar - returns true if sym is a good candiate for allocation */ +/* to a bit */ +/*------------------------------------------------------------------*/ +static bool isFlagVar (symbol *sym) +{ + if (IS_BIT (sym->type)) + return (true); + + if (!(IS_SPEC(sym->type) && SPEC_NOUN (sym->type) == V_BOOL && !sym->addrtaken)) + return (false); + + bitVect *defs = bitVectCopy (sym->defs); + bitVect *uses = bitVectCopy (sym->uses); + int key; + unsigned int gooduses = 0; + unsigned int baduses = 0; + + for (key = bitVectFirstBit (defs); key >= 0; key = bitVectFirstBit (defs)) + { + bitVectUnSetBit (defs, key); + + iCode *ic = hTabItemWithKey (iCodehTab, key); + + if (ic->op == AND_OP || ic->op == OR_OP || ic->op == EQ_OP || ic->op == '<' || ic->op == '>' || ic->op == CAST || ic->op == '!') + gooduses++; + else if (ic->op == '=' && + (IS_OP_LITERAL (IC_RIGHT (ic)) || IS_SYMOP (IC_RIGHT (ic)) && IS_BIT (OP_SYMBOL (IC_RIGHT (ic))->type))) + gooduses++; + else + baduses++; + } + + for (key = bitVectFirstBit (uses); key >= 0; key = bitVectFirstBit (uses)) + { + bitVectUnSetBit (uses, key); + + iCode *ic = hTabItemWithKey (iCodehTab, key); + + if (!ic) /* Shouldn't happen, but does */ + continue; + + if (ic->op == IFX || ic->op == '!' || ic->op == AND_OP || ic->op == OR_OP) + gooduses++; + else if (ic->op == BITWISEAND || ic->op == '|' || ic->op == '^') + gooduses++; + else if (ic->op == '=' && !POINTER_SET (ic) && IS_SYMOP (IC_RESULT (ic)) && IS_BIT (OP_SYMBOL (IC_RESULT (ic))->type)) + gooduses++; + else + baduses++; + } + + freeBitVect (defs); + freeBitVect (uses); + + return (gooduses >= baduses * 2); +} + +/*-----------------------------------------------------------------*/ +/* regTypeNum - computes the type & number of registers required */ +/*-----------------------------------------------------------------*/ +static void +regTypeNum (eBBlock * ebbs) +{ + symbol *sym; + int k; + iCode *ic; + + /* 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); + else if (IS_BIT (sym->type)) + sym->regType = REG_CND; + continue; + } + + /* if the symbol has only one definition & + that definition is a get_pointer */ + if (bitVectnBitsOn (sym->defs) == 1 && + (ic = hTabItemWithKey (iCodehTab, bitVectFirstBit (sym->defs))) && + POINTER_GET (ic) && !IS_BITVAR (sym->etype) && (aggrToPtrDclType (operandType (IC_LEFT (ic)), FALSE) == POINTER)) + { + if (ptrPseudoSymSafe (sym, ic)) + { + char *s = rematStr (OP_SYMBOL (IC_LEFT (ic))); + ptrPseudoSymConvert (sym, ic, s); + Safe_free (s); + continue; + } + + /* if in data space or idata space then try to + allocate pointer register */ + } + + /* 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 or 0 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 if (isFlagVar (sym)) + sym->regType = REG_BIT; + 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 < mcs51_nRegs; i++) + regs8051[i].isFree = 1; +} + +/*-----------------------------------------------------------------*/ +/* deallocStackSpil - this will set the stack pointer back */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (deallocStackSpil) +{ + symbol *sym = item; + + deallocLocal (sym); + return 0; +} + +/*-----------------------------------------------------------------*/ +/* farSpacePackable - returns the packable icode for far variables */ +/*-----------------------------------------------------------------*/ +static iCode * +farSpacePackable (iCode * ic) +{ + iCode *dic; + + /* go thru till we find a definition for the + symbol on the right */ + for (dic = ic->prev; dic; dic = dic->prev) + { + /* if the definition is a call then no */ + if ((dic->op == CALL || dic->op == PCALL) && IC_RESULT (dic)->key == IC_RIGHT (ic)->key) + { + return NULL; + } + + /* if shift by unknown amount then not */ + if ((dic->op == LEFT_OP || dic->op == RIGHT_OP) && IC_RESULT (dic)->key == IC_RIGHT (ic)->key) + return NULL; + + /* if pointer get and size > 1 */ + if (POINTER_GET (dic) && getSize (aggrToPtr (operandType (IC_LEFT (dic)), FALSE)) > 1) + return NULL; + + if (POINTER_SET (dic) && getSize (aggrToPtr (operandType (IC_RESULT (dic)), FALSE)) > 1) + return NULL; + + if (dic->op == IFX) + { + if (IC_COND (dic) && IS_TRUE_SYMOP (IC_COND (dic)) && isOperandInFarSpace (IC_COND (dic))) + return NULL; + } + else if (dic->op == JUMPTABLE) + { + if (IC_JTCOND (dic) && IS_TRUE_SYMOP (IC_JTCOND (dic)) && isOperandInFarSpace (IC_JTCOND (dic))) + return NULL; + } + else + { + /* if any tree is a true symbol in far space */ + if (IC_RESULT (dic) && IS_TRUE_SYMOP (IC_RESULT (dic)) && isOperandInFarSpace (IC_RESULT (dic))) + return NULL; + + if (IC_RIGHT (dic) && + IS_TRUE_SYMOP (IC_RIGHT (dic)) && + isOperandInFarSpace (IC_RIGHT (dic)) && !isOperandEqual (IC_RIGHT (dic), IC_RESULT (ic))) + return NULL; + + if (IC_LEFT (dic) && + IS_TRUE_SYMOP (IC_LEFT (dic)) && + isOperandInFarSpace (IC_LEFT (dic)) && !isOperandEqual (IC_LEFT (dic), IC_RESULT (ic))) + return NULL; + } + + if (isOperandEqual (IC_RIGHT (ic), IC_RESULT (dic))) + { + if ((dic->op == LEFT_OP || dic->op == RIGHT_OP || dic->op == '-') && IS_OP_LITERAL (IC_RIGHT (dic))) + return NULL; + else + return dic; + } + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* 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 (isOperandInFarSpace (IC_RESULT (ic)) && !farSpacePackable (ic)) + { + return 0; + } + + /* 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; + } + } + + if (dic->op == INLINEASM) + { + dic = NULL; + break; + } + + /* 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 (IS_SYMOP (IC_RESULT (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; + } + } + } + + /* if the result is on stack or iaccess then it must be + the same atleast one of the operands */ + if (OP_SYMBOL (IC_RESULT (ic))->onStack || OP_SYMBOL (IC_RESULT (ic))->iaccess) + { + /* the operation has only one symbol + operator then we can pack */ + if ((IC_LEFT (dic) && !IS_SYMOP (IC_LEFT (dic))) || (IC_RIGHT (dic) && !IS_SYMOP (IC_RIGHT (dic)))) + goto pack; + + if (!((IC_LEFT (dic) && + IC_RESULT (ic)->key == IC_LEFT (dic)->key) || (IC_RIGHT (dic) && IC_RESULT (ic)->key == IC_RIGHT (dic)->key))) + return 0; + } +pack: + /* 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 definition by assignment */ + 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 (dic->op == IFX) + { + if (IC_COND (dic) && IC_COND (dic)->key == op->key) + return NULL; + } + else if (dic->op == JUMPTABLE) + { + if (IC_JTCOND (dic) && IC_JTCOND (dic)->key == op->key) + return NULL; + } + else + { + 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 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; + + /* for + & - operations make sure that + if it is on the stack it is the same + as one of the three operands */ + if ((ic->op == '+' || ic->op == '-') && OP_SYMBOL (IC_RIGHT (dic))->onStack) + { + if (IC_RESULT (ic)->key != IC_RIGHT (dic)->key && + IC_LEFT (ic)->key != IC_RIGHT (dic)->key && IC_RIGHT (ic)->key != IC_RIGHT (dic)->key) + 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; + OP_SYMBOL (op)->accuse = 0; + + /* 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; + + /* 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)); + return 1; + } + } + + /* 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) + { + /* if this is a subtraction & the result + is a true symbol in far space then don't pack */ + if (ic->op == '-' && IS_TRUE_SYMOP (IC_RESULT (dic))) + { + sym_link *etype = getSpec (operandType (IC_RESULT (dic))); + if (IN_FARSPACE (SPEC_OCLS (etype))) + return 0; + } + /* found it we need to remove it from the block */ + reassignAliasedSym (ebp, dic, ic, IC_RIGHT (ic)); + + return 1; + } + } + + return 0; +} + + +/*-----------------------------------------------------------------*/ +/* packRegsForOneuse : - will reduce some registers for single Use */ +/*-----------------------------------------------------------------*/ +static iCode * +packRegsForOneuse (iCode * ic, operand * op, eBBlock * ebp) +{ + iCode *dic, *sic; + sym_link *type; + int usingCarry=0; + + /* if returning a literal then do nothing */ + if (!IS_ITEMP (op)) + return NULL; + + /* if rematerializable or already return use then do nothing */ + if (OP_SYMBOL (op)->remat || OP_SYMBOL (op)->ruonly) + return NULL; + + /* only upto 2 bytes since we cannot predict + the usage of b, & acc */ + type = operandType (op); + if (getSize (type) > (fReturnSizeMCS51 - 2)) + return NULL; + usingCarry = IS_BIT(type); + + if (ic->op != 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 */ + if (bitVectnBitsOn (OP_USES (op)) > 1) + 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; + } + } + else + { + /* 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 (POINTER_SET (dic) && !IS_SMALL_PTR (aggrToPtr (operandType (IC_RESULT (dic)), FALSE))) + return NULL; + + if (POINTER_GET (dic) && !IS_SMALL_PTR (aggrToPtr (operandType (IC_LEFT (dic)), FALSE))) + return NULL; + } + + /* Make sure no overlapping liverange is already assigned to DPTR */ + if (OP_SYMBOL (op)->clashes) + { + symbol *sym; + int i; + + for (i = 0; i < OP_SYMBOL (op)->clashes->size; i++) + { + if (bitVectBitValue (OP_SYMBOL (op)->clashes, i)) + { + sym = hTabItemWithKey (liveRanges, i); + if (sym->ruonly) + return NULL; + } + } + } + + sic = dic; + + if (ic->op == SEND) + { + /* look for the call to extend following + far space search to include all parameters. + see bug 3004918 */ + for (; ic; ic = ic->next) + if (ic->op == CALL || ic->op == PCALL) + break; + if (!ic) /* not found */ + return NULL; + } + + if (ic->op == PCALL && !IS_SMALL_PTR(aggrToPtr(operandType(IC_LEFT(ic)), FALSE))) + return NULL; + + /* 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 (POINTER_SET (dic) && !IS_SMALL_PTR (aggrToPtr (operandType (IC_RESULT (dic)), FALSE))) + return NULL; + + if (POINTER_GET (dic) && !IS_SMALL_PTR (aggrToPtr (operandType (IC_LEFT (dic)), FALSE))) + return NULL; + + /* 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 ((dic->op == '%' || dic->op == '/' || dic->op == '*') && getSize (operandType (op)) >= 3) + return NULL; + + /* 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; + } + if (usingCarry) + { + if (isOperandInBitSpace (IC_LEFT (dic)) || + isOperandInBitSpace (IC_RIGHT (dic)) || + isOperandInBitSpace (IC_RESULT (dic))) + { + return NULL; + } + if (dic->op != SEND || dic->op != IPUSH || dic->op != '=') + { + return NULL; + } + } + } + + OP_SYMBOL (op)->ruonly = 1; + return sic; +} + +/*-----------------------------------------------------------------*/ +/* 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 +isCommutativeOp (unsigned int op) +{ + if (op == '+' || op == '*' || op == EQ_OP || op == '^' || op == '|' || op == BITWISEAND) + return TRUE; + else + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* operandUsesAcc - determines whether the code generated for this */ +/* operand will have to use the accumulator */ +/*-----------------------------------------------------------------*/ +bool +operandUsesAcc (operand * op, bool allowBitspace) +{ + if (!op) + return FALSE; + + if (IS_SYMOP (op)) + { + symbol *sym = OP_SYMBOL (op); + memmap *symspace; + + if (sym->accuse) + return TRUE; /* duh! */ + + if (IN_STACK (sym->etype) || sym->onStack || (SPIL_LOC (op) && SPIL_LOC (op)->onStack)) + return TRUE; /* acc is used to calc stack offset */ + + 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 (sym->iaccess && symspace->paged) + return TRUE; /* must fetch paged indirect sym via accumulator */ + + if (!allowBitspace && 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; +} + +/*-----------------------------------------------------------------*/ +/* packRegsForAccUse - pack registers for acc use */ +/*-----------------------------------------------------------------*/ +static void +packRegsForAccUse (iCode * ic) +{ + iCode *uic; + + /* if this is an aggregate, e.g. a one byte char array */ + if (IS_AGGREGATE (operandType (IC_RESULT (ic)))) + return; + + /* if we are calling a reentrant function that has stack parameters */ + if (ic->op == CALL && IFFUNC_ISREENT (operandType (IC_LEFT (ic))) && FUNC_HASSTACKPARM (operandType (IC_LEFT (ic)))) + return; + + if (ic->op == PCALL && + IFFUNC_ISREENT (operandType (IC_LEFT (ic))->next) && FUNC_HASSTACKPARM (operandType (IC_LEFT (ic))->next)) + return; + + /* if + or - then it has to be one byte result */ + if ((ic->op == '+' || ic->op == '-') && getSize (operandType (IC_RESULT (ic))) > 1) + return; + + /* if shift operation make sure right side is not a literal */ + if (ic->op == RIGHT_OP && (isOperandLiteral (IC_RIGHT (ic)) || getSize (operandType (IC_RESULT (ic))) > 1)) + return; + + if (ic->op == LEFT_OP && (isOperandLiteral (IC_RIGHT (ic)) || getSize (operandType (IC_RESULT (ic))) > 1)) + return; + + if (IS_BITWISE_OP (ic) && getSize (operandType (IC_RESULT (ic))) > 1) + return; + + /* has only one definition */ + if (bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) > 1) + return; + + /* has only one use */ + if (bitVectnBitsOn (OP_USES (IC_RESULT (ic))) > 1) + return; + + /* and the usage immediately follows this iCode */ + if (!(uic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_USES (IC_RESULT (ic)))))) + return; + + if (ic->next != uic) + return; + + /* if it is a conditional branch then we definitely can */ + if (uic->op == IFX) + goto accuse; + + if (uic->op == JUMPTABLE) + return; + + if (POINTER_SET (uic) && getSize (aggrToPtr (operandType (IC_RESULT (uic)), FALSE)) > 1) + return; + + /* if the usage is not an assignment + or an arithmetic / bitwise / shift operation then not */ + if (uic->op != '=' && !IS_ARITHMETIC_OP (uic) && !IS_BITWISE_OP (uic) && uic->op != LEFT_OP && uic->op != RIGHT_OP) + return; + + /* if shift operation make sure right side is not a literal */ + /* WIML: Why is this? */ + if (uic->op == RIGHT_OP && (isOperandLiteral (IC_RIGHT (uic)) || getSize (operandType (IC_RESULT (uic))) > 1)) + return; + if (uic->op == LEFT_OP && (isOperandLiteral (IC_RIGHT (uic)) || getSize (operandType (IC_RESULT (uic))) > 1)) + return; + + /* make sure that the result of this icode is not on the + stack, since acc is used to compute stack offset */ +#if 0 + if (IS_TRUE_SYMOP (IC_RESULT (uic)) && OP_SYMBOL (IC_RESULT (uic))->onStack) + return; +#else + if (isOperandOnStack (IC_RESULT (uic))) + return; +#endif + + /* if the usage has only one operand then we can */ + if (IC_LEFT (uic) == NULL || IC_RIGHT (uic) == NULL) + goto accuse; + + /* if the other operand uses the accumulator then we cannot */ + if ((IC_LEFT (uic)->key == IC_RESULT (ic)->key && + operandUsesAcc (IC_RIGHT (uic), IS_BIT (operandType (IC_LEFT (uic))))) || + (IC_RIGHT (uic)->key == IC_RESULT (ic)->key && operandUsesAcc (IC_LEFT (uic), IS_BIT (operandType (IC_RIGHT (uic)))))) + return; + + /* make sure this is on the left side if not commutative */ + /* except for '-', which has been written to be able to + handle reversed operands */ + if (!(isCommutativeOp (ic->op) || ic->op == '-') && IC_LEFT (uic)->key != IC_RESULT (ic)->key) + return; + + /* Sign handling will overwrite a */ + if (uic->op == '*' && getSize (operandType (IC_RESULT (uic))) > 1 && + (!SPEC_USIGN (getSpec (operandType (IC_LEFT (uic)))) || !SPEC_USIGN (getSpec (operandType (IC_RIGHT (uic)))))) + return; + if ((uic->op == '/' || uic->op == '%') && + (!SPEC_USIGN (getSpec (operandType (IC_LEFT (uic)))) || !SPEC_USIGN (getSpec (operandType (IC_RIGHT (uic)))))) + return; + +#if 0 + // this is too dangerous and need further restrictions + // see bug #447547 + + /* if one of them is a literal then we can */ + if ((IC_LEFT (uic) && IS_OP_LITERAL (IC_LEFT (uic))) || (IC_RIGHT (uic) && IS_OP_LITERAL (IC_RIGHT (uic)))) + { + OP_SYMBOL (IC_RESULT (ic))->accuse = 1; + return; + } +#endif + +accuse: + OP_SYMBOL (IC_RESULT (ic))->accuse = 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 || !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; + + /* The changes in SDCCopt.c #7741 should correct the use info, making */ + /* this extra test redundant. */ + if (ic->parmPush) + {// find Send or other Push for this func call + for (lic = ic->next; lic && lic->op != CALL; lic = lic->next) + { + if ((lic->op == IPUSH || lic->op == SEND) && IS_ITEMP (IC_LEFT (lic))) + {// and check parameter is not passed again + symbol * parm = OP_SYMBOL (IC_LEFT (ic)); + symbol * other = OP_SYMBOL (IC_LEFT (lic)); + if (other == parm) + 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); + + /* 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); +} + +/*-----------------------------------------------------------------*/ +/* 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) + { + /* Fix for bug #979599: */ + /* P0 &= ~1; */ + + /* Look for two subsequent iCodes with */ + /* iTemp := _c; */ + /* _c = iTemp & op; */ + /* and replace them by */ + /* iTemp := _c; */ + /* _c = _c & op; */ + if ((ic->op == BITWISEAND || ic->op == '|' || ic->op == '^') && + ic->prev && + ic->prev->op == '=' && + (IS_ITEMP (IC_LEFT (ic)) && isOperandEqual (IC_LEFT (ic), IC_RESULT (ic->prev)) && isOperandEqual (IC_RESULT (ic), IC_RIGHT (ic->prev)) || + IS_ITEMP (IC_RIGHT (ic)) && isOperandEqual (IC_RIGHT (ic), IC_RESULT (ic->prev)) && isOperandEqual (IC_RESULT (ic), IC_RIGHT (ic->prev)))) + { + bool left = IS_ITEMP (IC_LEFT (ic)) && isOperandEqual (IC_LEFT (ic), IC_RESULT (ic->prev)) && isOperandEqual (IC_RESULT (ic), IC_RIGHT (ic->prev)); + + iCode *ic_prev = ic->prev; + symbol *prev_result_sym = OP_SYMBOL (IC_RESULT (ic_prev)); + + ReplaceOpWithCheaperOp (left ? &IC_LEFT (ic) : &IC_RIGHT (ic), IC_RESULT (ic)); + if (IC_RESULT (ic_prev) != (left ? IC_RIGHT (ic) : IC_LEFT (ic))) + { + bitVectUnSetBit (OP_USES (IC_RESULT (ic_prev)), ic->key); + if ( /*IS_ITEMP (IC_RESULT (ic_prev)) && */ + prev_result_sym->liveTo == ic->seq) + { + prev_result_sym->liveTo = ic_prev->seq; + } + } + bitVectSetBit (OP_USES (IC_RESULT (ic)), ic->key); + + bitVectSetBit (ic->rlive, IC_RESULT (ic)->key); + + if (bitVectIsZero (OP_USES (IC_RESULT (ic_prev)))) + { + bitVectUnSetBit (ic->rlive, IC_RESULT (ic)->key); + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic_prev)), ic_prev->key); + remiCodeFromeBBlock (ebp, ic_prev); + hTabDeleteItem (&iCodehTab, ic_prev->key, ic_prev, DELETE_ITEM, NULL); + } + } + + /* 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 && + !IS_CAST_ICODE (OP_SYMBOL (IC_RIGHT (ic))->rematiCode) && + !isOperandGlobal (IC_RESULT (ic)) && /* due to bug 1618050 */ + bitVectnBitsOn (OP_SYMBOL (IC_RESULT (ic))->defs) <= 1 && + !OP_SYMBOL (IC_RESULT (ic))->addrtaken) + { + 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))->addrtaken) + { + 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 rematerializable + 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; + + if (!SKIP_IC2 (ic)) + { + /* if we are using a symbol on the stack + then we should say mcs51_ptrRegReq */ + if (options.useXstack && ic->parmPush && (ic->op == IPUSH || ic->op == IPOP)) + mcs51_ptrRegReq++; + if (ic->op == IFX && IS_SYMOP (IC_COND (ic))) + mcs51_ptrRegReq += ((OP_SYMBOL (IC_COND (ic))->onStack || + OP_SYMBOL (IC_COND (ic))->iaccess || + SPEC_OCLS (OP_SYMBOL (IC_COND (ic))->etype) == idata) ? 1 : 0); + else if (ic->op == JUMPTABLE && IS_SYMOP (IC_JTCOND (ic))) + mcs51_ptrRegReq += ((OP_SYMBOL (IC_JTCOND (ic))->onStack || + OP_SYMBOL (IC_JTCOND (ic))->iaccess || + SPEC_OCLS (OP_SYMBOL (IC_JTCOND (ic))->etype) == idata) ? 1 : 0); + else + { + if (IS_SYMOP (IC_LEFT (ic))) + mcs51_ptrRegReq += ((OP_SYMBOL (IC_LEFT (ic))->onStack || + OP_SYMBOL (IC_LEFT (ic))->iaccess || + SPEC_OCLS (OP_SYMBOL (IC_LEFT (ic))->etype) == idata) ? 1 : 0); + if (IS_SYMOP (IC_RIGHT (ic))) + mcs51_ptrRegReq += ((OP_SYMBOL (IC_RIGHT (ic))->onStack || + OP_SYMBOL (IC_RIGHT (ic))->iaccess || + SPEC_OCLS (OP_SYMBOL (IC_RIGHT (ic))->etype) == idata) ? 1 : 0); + if (IS_SYMOP (IC_RESULT (ic))) + mcs51_ptrRegReq += ((OP_SYMBOL (IC_RESULT (ic))->onStack || + OP_SYMBOL (IC_RESULT (ic))->iaccess || + SPEC_OCLS (OP_SYMBOL (IC_RESULT (ic))->etype) == idata) ? 1 : 0); + if (POINTER_GET (ic) && IS_SYMOP (IC_LEFT (ic)) + && getSize (OP_SYMBOL (IC_LEFT (ic))->type) <= (unsigned int) NEARPTRSIZE) + mcs51_ptrRegReq++; + if (POINTER_SET (ic) && IS_SYMOP (IC_RESULT (ic)) + && getSize (OP_SYMBOL (IC_RESULT (ic))->type) <= (unsigned int) NEARPTRSIZE) + mcs51_ptrRegReq++; + } + } + + /* 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; + } + + /* if the condition of an if instruction + is defined in the previous GET_POINTER instruction and + this is the only usage then + mark the itemp as accumulator use */ + if ((POINTER_GET (ic) && getSize (operandType (IC_RESULT (ic))) <= 1) && + 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))->accuse = 1; + continue; + } + + /* reduce for support function calls */ + if (ic->supportRtn || ic->op == '+' || ic->op == '-') + packRegsForSupport (ic, ebp); + + /* some cases the redundant moves can + can be eliminated for return statements */ + if ((ic->op == RETURN || (ic->op == SEND && ic->argreg == 1)) && + !isOperandInFarSpace (IC_LEFT (ic)) && (options.model == MODEL_SMALL || options.model == MODEL_MEDIUM)) + { + packRegsForOneuse (ic, IC_LEFT (ic), ebp); + } + + /* if pointer set & left has a size more than + one and right is not in far space */ + if (POINTER_SET (ic) && + IS_SYMOP (IC_RESULT (ic)) && + !isOperandInFarSpace (IC_RIGHT (ic)) && + !OP_SYMBOL (IC_RESULT (ic))->remat && + !IS_OP_RUONLY (IC_RIGHT (ic)) && getSize (aggrToPtr (operandType (IC_RESULT (ic)), FALSE)) > 1) + { + packRegsForOneuse (ic, IC_RESULT (ic), ebp); + } + + /* if pointer get */ + if (POINTER_GET (ic) && + IS_SYMOP (IC_LEFT (ic)) && + !isOperandInFarSpace (IC_RESULT (ic)) && + !OP_SYMBOL (IC_LEFT (ic))->remat && + !IS_OP_RUONLY (IC_RESULT (ic)) && + getSize (aggrToPtr (operandType (IC_LEFT (ic)), FALSE)) > 1) + { + packRegsForOneuse (ic, IC_LEFT (ic), ebp); + } + + /* if this is a cast for integral promotion then + check if it's the only use of the definition of the + operand being casted/ if yes then replace + the result of that arithmetic operation with + this result and get rid of the cast */ + if (ic->op == CAST) + { + sym_link *fromType = operandType (IC_RIGHT (ic)); + sym_link *toType = operandType (IC_LEFT (ic)); + + if (IS_INTEGRAL (fromType) && IS_INTEGRAL (toType) && + getSize (fromType) != getSize (toType) && SPEC_USIGN (fromType) == SPEC_USIGN (toType)) + { + iCode *dic = packRegsForOneuse (ic, IC_RIGHT (ic), ebp); + if (dic) + { + if (IS_ARITHMETIC_OP (dic)) + { + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (dic))->defs, dic->key); + ReplaceOpWithCheaperOp (&IC_RESULT (dic), IC_RESULT (ic)); + remiCodeFromeBBlock (ebp, ic); + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (ic))->defs, ic->key); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + OP_DEFS (IC_RESULT (dic)) = bitVectSetBit (OP_DEFS (IC_RESULT (dic)), dic->key); + ic = ic->prev; + } + else + { + OP_SYMBOL (IC_RIGHT (ic))->ruonly = 0; + } + } + } + else + { + /* if the type from and type to are the same + then if this is the only use then pack it */ + if (compareType (operandType (IC_RIGHT (ic)), operandType (IC_LEFT (ic))) == 1) + { + iCode *dic = packRegsForOneuse (ic, IC_RIGHT (ic), ebp); + if (dic) + { + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (dic))->defs, dic->key); + ReplaceOpWithCheaperOp (&IC_RESULT (dic), IC_RESULT (ic)); + remiCodeFromeBBlock (ebp, ic); + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (ic))->defs, ic->key); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + OP_DEFS (IC_RESULT (dic)) = bitVectSetBit (OP_DEFS (IC_RESULT (dic)), dic->key); + ic = ic->prev; + } + } + } + } + + /* pack for PUSH + iTempNN := (some variable in farspace) V1 + push iTempNN ; + ------------- + push V1 + */ + if (ic->op == IPUSH) + { + packForPush (ic, ebpp, blockno); + } + + /* pack registers for accumulator use, when the + result of an arithmetic or bit wise operation + has only one use, that use is immediately following + the definition and the using iCode has only one + operand or has two operands but one is literal & + the result of that operation is not on stack then + we can leave the result of this operation in acc:b + combination */ + if ((IS_ARITHMETIC_OP (ic) + || IS_CONDITIONAL (ic) + || IS_BITWISE_OP (ic) + || ic->op == LEFT_OP || ic->op == RIGHT_OP || ic->op == CALL + || ic->op == '=' && !POINTER_SET(ic) && getSize (operandType (IC_RESULT (ic))) < 2 + || (ic->op == ADDRESS_OF && isOperandOnStack (IC_LEFT (ic)))) && + IS_ITEMP (IC_RESULT (ic)) && getSize (operandType (IC_RESULT (ic))) <= 2) + { + packRegsForAccUse (ic); + } + } +} + +/*------------------------------------------------------------------------*/ +/* positionRegsReverse - positioning registers from end to begin to avoid */ +/* conflict among result, left and right operands in some extrem cases */ +/*------------------------------------------------------------------------*/ +static void +positionRegsReverse (eBBlock ** ebbs, int count) +{ + int i; + iCode *ic; + + for (i = count - 1; i >= 0; i--) + for (ic = ebbs[i]->ech; ic; ic = ic->prev) + { + if (IC_LEFT (ic) && IS_SYMOP (IC_LEFT (ic)) && OP_SYMBOL (IC_LEFT (ic))->nRegs && + IC_RESULT (ic) && IS_SYMOP (IC_RESULT (ic)) && OP_SYMBOL (IC_RESULT (ic))->nRegs) + { + positionRegs (OP_SYMBOL (IC_RESULT (ic)), OP_SYMBOL (IC_LEFT (ic)), 1); + } + if (IC_RIGHT (ic) && IS_SYMOP (IC_RIGHT (ic)) && OP_SYMBOL (IC_RIGHT (ic))->nRegs && + IC_RESULT (ic) && IS_SYMOP (IC_RESULT (ic)) && OP_SYMBOL (IC_RESULT (ic))->nRegs) + { + positionRegs (OP_SYMBOL (IC_RESULT (ic)), OP_SYMBOL (IC_RIGHT (ic)), 1); + } + } +} + +/*-----------------------------------------------------------------*/ +/* assignRegisters - assigns registers to each live range as need */ +/*-----------------------------------------------------------------*/ +void +mcs51_assignRegisters (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); + mcs51_ptrRegReq = _G.stackExtend = _G.dataExtend = 0; + if ((currFunc && IFFUNC_ISREENT (currFunc->type)) || options.stackAuto) + { + mcs51_nRegs = 16; + } + else + { + mcs51_nRegs = 8; + } + _G.allBitregs = findAllBitregs (); + _G.allBankregs = findAllBankregs (); + + /* 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); + fillGaps (); + + /* do positionRegs() for all ICs from end to begin */ + positionRegsReverse (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; + } + + /* after that create the register mask + for each of the instruction */ + createRegMask (ebbs, count); + + /* redo that offsets for stacked automatic variables */ + if (currFunc) + { + redoStackOffsets (); + } + + /* make sure r0 & r1 are flagged as used if they might be used */ + /* as pointers */ + if (currFunc && mcs51_ptrRegReq) + { + currFunc->regsUsed = bitVectSetBit (currFunc->regsUsed, R0_IDX); + currFunc->regsUsed = bitVectSetBit (currFunc->regsUsed, R1_IDX); + } + + 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)); + + gen51Code (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; +} diff --git a/src/mcs51/ralloc.h b/src/mcs51/ralloc.h new file mode 100644 index 0000000..10b86e1 --- /dev/null +++ b/src/mcs51/ralloc.h @@ -0,0 +1,81 @@ +/*------------------------------------------------------------------------- + + SDCCralloc.h - header file register allocation + + 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 "SDCCicode.h" +#include "SDCCBBlock.h" +#ifndef SDCCRALLOC_H +#define SDCCRALLOC_H 1 + +enum +{ + R7_IDX = 0, R6_IDX, R5_IDX, R4_IDX, + R3_IDX, R2_IDX, R1_IDX, R0_IDX, + B0_IDX, B1_IDX, B2_IDX, B3_IDX, + B4_IDX, B5_IDX, B6_IDX, B7_IDX, + X8_IDX, X9_IDX, X10_IDX, X11_IDX, + X12_IDX, CND_IDX, + DPL_IDX, DPH_IDX, B_IDX, A_IDX, + END_IDX +}; + + +#define REG_PTR 0x01 +#define REG_GPR 0x02 +#define REG_CND 0x04 +#define REG_BIT 0x08 +/* definition for the registers */ +typedef struct reg_info +{ + short type; /* can have value + REG_GPR, REG_BIT, REG_PTR or REG_CND */ + short rIdx; /* index into register table */ + short otype; + char *name; /* name */ + char *dname; /* name when direct access needed */ + char *base; /* base address */ + short offset; /* offset from the base */ + unsigned isFree:1; /* is currently unassigned */ + + struct + { + unsigned valueKnown:1; + unsigned char value; /* only valid when valueKnown is set */ + char *symbol; /* holds symbol if value is known by symbol */ + } + rtrack; +} +reg_info; + +extern reg_info regs8051[]; + +reg_info *mcs51_regWithIdx (int); + +bitVect *mcs51_rUmaskForOp (operand * op); +bitVect *mcs51_allBitregs (void); +bitVect *mcs51_allBankregs (void); + +extern int mcs51_ptrRegReq; +extern int mcs51_nRegs; + +#endif diff --git a/src/mcs51/rtrack.c b/src/mcs51/rtrack.c new file mode 100644 index 0000000..13fc5b2 --- /dev/null +++ b/src/mcs51/rtrack.c @@ -0,0 +1,1200 @@ +/*------------------------------------------------------------------------- + rtrack.c - tracking content of registers on an mcs51 + + Copyright 2007 Frieder Ferlemann (Frieder Ferlemann AT web.de) + + 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 of the License, 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +-------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------- + Status: + - passes regression test suite, still bugs are likely + - only active if environment variable SDCC_REGTRACK is set + + Missed opportunities: + - does not track offsets to symbols as in "mov dptr,#(_my_int + 2)" + - only used for moves to acc or dptr so chances to use: + "inc r2", "mov r2,a" would not be taken + - a label causes loss of tracking (no handling of information of blocks + known to follow/preceed the current block) + - not used in aopGet or genRet + - SFRX (__xdata volatile unsigned char __at(addr)) not handled as value + - does not track which registers are known to be unchanged within + a function (would not have to be saved when calling the function) +-------------------------------------------------------------------------*/ + + +#include <stdio.h> +#include <string.h> +#include "SDCCglobl.h" + +#include "common.h" +#include "ralloc.h" +#include "gen.h" +#include "rtrack.h" + + +#define D(x) do if (options.verboseAsm) {x;} while(0) +#define DD(x) do if (options.verboseAsm && enableextraverbose) {x;} while(0) + + +/* move this (or rtrackGetLit() and rtrackMoveALit() + elsewhere? stealing emitcode from gen.c */ +void emitcode (const char *inst, const char *fmt,...); + + +static int enable = +1; +static int enableextraverbose = -1; + + +static unsigned int rx_num_to_idx (const unsigned int num) +{ + const unsigned int regidx[8] = + { R7_IDX, R6_IDX, R5_IDX, R4_IDX, R3_IDX, R2_IDX, R1_IDX, R0_IDX }; + + assert( 7 >= num ); + + return regidx [num & 0x7]; +} + + +static void rtrack_data_unset (const unsigned int idx) +{ + assert (idx >= 0); + assert (idx < END_IDX); + + if (regs8051[idx].rtrack.symbol || regs8051[idx].rtrack.valueKnown) + { + DD(emitcode (";", "\t%s=?", regs8051[idx].name);); + } + + if (regs8051[idx].rtrack.symbol) + { + Safe_free (regs8051[idx].rtrack.symbol); + } + + memset (®s8051[idx].rtrack, 0, sizeof regs8051[idx].rtrack); +} + + +static void rtrack_data_set_val (const unsigned int idx, const unsigned char value) +{ + assert (idx >= 0); + assert (idx < END_IDX); + + regs8051[idx].rtrack.value = value; + regs8051[idx].rtrack.valueKnown = 1; + + /* in case it was set by symbol, unset symbol */ + if (regs8051[idx].rtrack.symbol) + { + Safe_free (regs8051[idx].rtrack.symbol); + regs8051[idx].rtrack.symbol = NULL; + } + + DD(emitcode (";", "\t%s=#0x%02x", + regs8051[idx].name, + regs8051[idx].rtrack.value);); +} + + +static void rtrack_data_set_symbol (const unsigned int idx, const char * const symbol) +{ + assert (idx >= 0); + assert (idx < END_IDX); + + /* in case it was set by value, unset value */ + regs8051[idx].rtrack.value = 0; + regs8051[idx].rtrack.valueKnown = 0; + + /* eventually free a previous symbol */ + if (regs8051[idx].rtrack.symbol) + { + Safe_free (regs8051[idx].rtrack.symbol); + } + regs8051[idx].rtrack.symbol = Safe_strdup(symbol); + + DD(emitcode (";", "\t%s=#%s", + regs8051[idx].name, + regs8051[idx].rtrack.symbol);); +} + + +static int rtrack_data_is_same (const unsigned int idxdst, const unsigned int idxsrc) +{ + return ((regs8051[idxdst].rtrack.valueKnown && regs8051[idxsrc].rtrack.valueKnown) && + (regs8051[idxdst].rtrack.value == regs8051[idxsrc].rtrack.value)) || + ((regs8051[idxdst].rtrack.symbol && regs8051[idxsrc].rtrack.symbol) && + !strcmp (regs8051[idxdst].rtrack.symbol, regs8051[idxsrc].rtrack.symbol)); +} + + +static void rtrack_data_copy_dst_src (const unsigned int idxdst, const unsigned int idxsrc) +{ + assert (idxdst >= 0); + assert (idxdst < END_IDX); + assert (idxsrc >= 0); + assert (idxsrc < END_IDX); + + DD + ( + if ((NULL != regs8051[idxsrc].rtrack.symbol) || regs8051[idxsrc].rtrack.valueKnown) + { + emitcode (";", "\t%s=%s", regs8051[idxdst].name, regs8051[idxsrc].name); + } + else if (regs8051[idxdst].rtrack.symbol || regs8051[idxdst].rtrack.valueKnown) + { + emitcode (";", "\t%s=*",regs8051[idxdst].name); + } + + if (rtrack_data_is_same (idxdst, idxsrc)) + { + emitcode (";", "genFromRTrack redundant?"); + } + ); + + /* mov a, acc */ + if (idxsrc == idxdst) + return; + + regs8051[idxdst].rtrack.valueKnown = regs8051[idxsrc].rtrack.valueKnown; + regs8051[idxdst].rtrack.value = regs8051[idxsrc].rtrack.value; + + if (regs8051[idxdst].rtrack.symbol) + { + Safe_free (regs8051[idxdst].rtrack.symbol); + regs8051[idxdst].rtrack.symbol = NULL; + } + + memcpy (®s8051[idxdst].rtrack, ®s8051[idxsrc].rtrack, sizeof regs8051[idxdst].rtrack); + + if (regs8051[idxsrc].rtrack.symbol) + { + regs8051[idxdst].rtrack.symbol = Safe_strdup(regs8051[idxsrc].rtrack.symbol); + } +} + + +static void dumpAll() +{ + DD + ( + unsigned int i; + unsigned int column = 0; + char s[512]; + + s[0] = 0; + for (i = 0; i < END_IDX; i++) + { + if (regs8051[i].rtrack.valueKnown) + { + column += sprintf(s + column, "%s%s:#0x%02x", + column?" ":"", regs8051[i].name, regs8051[i].rtrack.value); + } + if (NULL != regs8051[i].rtrack.symbol) + { + column += sprintf(s + column, "%s%s:#%s", + column?" ":"", regs8051[i].name, regs8051[i].rtrack.symbol); + } + if (column>160) + { + strcpy (&s[157], "..."); + break; + } + } + emitcode (";", "\t%s", s); + ); +} + + +static void invalidateAllRx() +{ + unsigned int i; + for (i = 0; i <= 7; i++) + { + rtrack_data_unset (rx_num_to_idx (i)); + } +} + + +static void invalidateAll() +{ + invalidateAllRx(); + + rtrack_data_unset (DPL_IDX); + rtrack_data_unset (DPH_IDX); + rtrack_data_unset (B_IDX); + rtrack_data_unset (A_IDX); +} + + +static int regidxfromregname (const char* const s) +{ + unsigned int i; + + for (i = 0; i < END_IDX; i++) + { + if (regs8051[i].name) + if (!strncmp (s, regs8051[i].name, strlen(regs8051[i].name))) + return i; + + if (regs8051[i].dname) + if (!strncmp (s, regs8051[i].dname, strlen(regs8051[i].dname))) + return i; + } + + return -1; +} + + +static int valuefromliteral (const char* const s) +{ + char* tmp = NULL; + int value; + + if (strncmp (s, "0x", 2)) + return -1; + + value = strtol (s + 2, &tmp, 16); + if (s != tmp) + return value; + + return -1; +} + + +/* tracking values within registers by looking + at the line passed to the assembler. + Tries to keep regs8051[] up to date */ +bool _mcs51_rtrackUpdate (const char *line) +{ + bool modified = false; + + if (enable == -1) + enable = (NULL != getenv("SDCC_REGTRACK")); + + if (enableextraverbose == -1) + enableextraverbose = (NULL != getenv("SDCC_REGTRACK_VERBOSE")); + + if (!enable || + *line == ';' || /* comment */ + (NULL != strstr( line, "==."))) /* dirty check for _G.debugLine */ + return false; /* nothing to do */ + + dumpAll (); + + if (!strncmp (line, "mov", 3)) + { + if (!strncmp (line, "movc\ta", 6) || + !strncmp (line, "movx\ta", 6)) + { + rtrack_data_unset (A_IDX); + return false; + } + + /* mov to register (r0..r7, dpl, dph, a, b)*/ + if (!strncmp (line, "mov\t", 4)) + { + int regIdx = regidxfromregname (line + 4); + + if (0 <= regIdx) + { + char *argument = strstr (line, ",") + 1; + char *s; + int value; + + value = strtol (argument + 1, &s, 16); + + /* check literal mov to register */ + if ((s != argument + 1) && !strncmp (argument, "#0x", 3)) + { + D + ( + if (regs8051[regIdx].rtrack.valueKnown && (value == regs8051[regIdx].rtrack.value)) + { + emitcode (";", "genFromRTrack removed\t%s", line); + modified = true; + } + if (regs8051[A_IDX].rtrack.valueKnown && (value == regs8051[A_IDX].rtrack.value) && + (regIdx != A_IDX) && (regIdx != DPL_IDX) && (regIdx != DPH_IDX)) + /* ignore DPL/DPH for now as peephole rule for MOV DPTR is much better */ + { + emitcode (";", "genFromRTrack replaced\t%s", line); + emitcode ("mov", "%s,a", regs8051[regIdx].dname); + modified = true; + } + else if (regs8051[regIdx].rtrack.valueKnown && (value == regs8051[regIdx].rtrack.value + 1) && + ((regIdx != A_IDX) || (0xff != regs8051[regIdx].rtrack.value))) + { + /* does not occur in regression test mcs51-small */ + emitcode (";", "genFromRTrack replaced\t%s", line); + emitcode ("inc", "%s", regs8051[regIdx].name); + modified = true; + } + else if (regs8051[regIdx].rtrack.valueKnown && (value == regs8051[regIdx].rtrack.value - 1) && + ((regIdx != A_IDX) || (0x01 != regs8051[regIdx].rtrack.value))) + { + /* does not occur in regression test mcs51-small */ + emitcode (";", "genFromRTrack replaced\t%s", line); + emitcode ("dec", "%s", regs8051[regIdx].name); + modified = true; + } + ); + + rtrack_data_set_val (regIdx, (unsigned char) value); + } + /* check literal mov of symbol to register */ + else if (!strncmp (argument, "#", 1)) + { + rtrack_data_set_symbol (regIdx, argument + 1); + } + /* check mov from register to register */ + else if (0 <= regidxfromregname (argument)) + { + rtrack_data_copy_dst_src (regIdx, regidxfromregname (argument)); + } + else + { + /* mov acc.7,c and the likes */ + rtrack_data_unset (regIdx); + } + return modified; + } + } + + /* mov to psw can change register bank */ + if (!strncmp (line, "mov\tpsw,", 8)) + { + invalidateAllRx (); + return false; + } + + /* tracking dptr */ + /* literal number 16 bit */ + if (!strncmp (line, "mov\tdptr,#0x", 12)) + { + char* s; + int value = strtol (line + 10, &s, 16); + if( s != line + 10 ) + { + if (options.verboseAsm) + { + bool foundshortcut = 0; + + if ( regs8051[DPH_IDX].rtrack.valueKnown && + regs8051[DPL_IDX].rtrack.valueKnown && + (regs8051[DPH_IDX].rtrack.value == (value >> 8)) && + (regs8051[DPL_IDX].rtrack.value == (value & 0xff))) + { + emitcode (";", "genFromRTrack removed\t%s", line); + foundshortcut = 1; + modified = true; + } + + if (!foundshortcut && + regs8051[DPH_IDX].rtrack.valueKnown && + regs8051[DPL_IDX].rtrack.valueKnown) + { + /* some instructions are shorter than mov dptr,#0xabcd */ + const struct + { + int offset; + const char* inst; + const char* parm; + } reachable[6] = + { + { 1, "inc", "dptr"}, + { 256, "inc", "dph"}, + {-256, "dec", "dph"}, + {-255, "inc", "dpl"}, /* if overflow */ + { -1, "dec", "dpl"}, /* if no overflow */ + { 255, "dec", "dpl"} /* if overflow */ + }; + + unsigned int dptr = (regs8051[DPH_IDX].rtrack.value << 8 ) | + regs8051[DPL_IDX].rtrack.value; + unsigned int i; + + for (i = 0; i < 6; i++) + { + if (dptr + reachable[i].offset == value) + { + /* check if an overflow would occur */ + if ((i == 3) && ((dptr & 0xff) != 0xff)) continue; + if ((i == 4) && ((dptr & 0xff) == 0x00)) continue; + if ((i == 5) && ((dptr & 0xff) != 0x00)) continue; + + /* does not occur in regression test mcs51-small */ + emitcode (";", "genFromRTrack replaced\t%s", line); + emitcode (reachable[i].inst, "%s", reachable[i].parm); + modified = true; + foundshortcut = 1; + + break; + } + }; + } + + if (!foundshortcut && + regs8051[DPH_IDX].rtrack.valueKnown && + (regs8051[DPH_IDX].rtrack.value == (value >> 8))) + { + char s[32]; + sprintf (s, "#0x%02x", value & 0xff); + + if (s != rtrackGetLit(s)) + { + /* does not occur in regression test mcs51-small */ + emitcode (";", "genFromRTrack replaced\t%s", line); + emitcode ("mov", "dpl,%s", rtrackGetLit (s)); + modified = true; + foundshortcut = 1; + } + } + if (!foundshortcut && + regs8051[DPL_IDX].rtrack.valueKnown && + (regs8051[DPL_IDX].rtrack.value == (value & 0xff))) + { + char s[32]; + sprintf (s, "#0x%02x", value >> 8); + + if (s != rtrackGetLit (s)) + { + /* does not occur in regression test mcs51-small */ + emitcode (";", "genFromRTrack replaced\t%s", line); + emitcode ("mov", "dph,%s", rtrackGetLit (s)); + modified = true; + foundshortcut = 1; + } + } + } + + rtrack_data_set_val (DPH_IDX, (unsigned char) (value >> 8)); + rtrack_data_set_val (DPL_IDX, (unsigned char) value); + return modified; + } + } + /* literal symbol 16 bit */ + else if (!strncmp (line, "mov\tdptr,#", 10)) + { + char* s = Safe_alloc (strlen (line) + strlen ("( >> 8)")); + + strcat (s, "("); + strcat (s, &line[10]); + strcat (s, " >> 8)"); + + rtrack_data_set_symbol (DPH_IDX, s); + rtrack_data_set_symbol (DPL_IDX, &line[10]); + + Safe_free (s); + return false; + } + else if (!strncmp (line, "mov\tdptr", 8)) + { + /* unidentified */ + rtrack_data_unset (DPH_IDX); + rtrack_data_unset (DPL_IDX); + return false; + } + + /* move direct to symbol */ + if (!strncmp (line, "mov\t_", 5) || + !strncmp (line, "mov\t(", 5)) + { + char* argument = strstr (line, ",") + 1; + + if (argument && !strncmp (argument, "#0x", 3)) + { + char s[8] = {0}; + + strncpy ((void *)&s, argument, strlen ("#0xab")); + + /* could we get it from a, r0..r7? */ + if (s != rtrackGetLit (s)) + { + int lengthuptoargument = argument - (line + 4); + emitcode (";", "1-genFromRTrack replaced\t%s", line); + emitcode ("mov", "%.*s%s", + lengthuptoargument, + line + 4, + rtrackGetLit (s)); + modified = true; + } + } + return modified; + } + + /* no tracking of SP, so we do not care */ + if (!strncmp (line, "mov\tsp,", 7)) + return false; + + /* mov to xdata or pdata memory does not change registers */ + if (!strncmp (line, "movx\t@", 6)) + return false; + + /* mov to idata memory might change registers r0..r7 + but unless there is a stack problem + compiler generated code does not do idata + writes to 0x00..0x1f? */ + if (!strncmp (line, "mov\t@", 5)) + { + /* a little too paranoid? */ + invalidateAllRx (); + return false; + } + } + + /* no tracking of SP */ + if (!strncmp (line, "push", 4)) + return false; + + if (!strncmp (line, "pop\t", 4)) + { + int regIdx = regidxfromregname (line + 4); + if (0 <= regIdx) + { + rtrack_data_unset (regIdx); + } + return false; + } + + if (!strncmp (line, "inc", 3)) + { + if (!strcmp (line, "inc\tdptr")) + { + if (regs8051[DPH_IDX].rtrack.valueKnown && + regs8051[DPL_IDX].rtrack.valueKnown) + { + int val = (regs8051[DPH_IDX].rtrack.value << 8) | regs8051[DPL_IDX].rtrack.value; + val += 1; + rtrack_data_set_val (DPL_IDX, (unsigned char) val); + rtrack_data_set_val (DPH_IDX, (unsigned char) (val >> 8)); + } + else + { + /* not yet handling offset to a symbol. Invalidating. So no inc dptr for: + __xdata char array[4]; array[0] = 0; array[1] = 0; array[2] = 0; + (If an offset to the respective linker segment would be + available then additionally + __xdata int a = 123; __xdata int b = 456; __xdata c= 'a'; + could be 4 bytes shorter) */ + rtrack_data_unset (DPL_IDX); + rtrack_data_unset (DPH_IDX); + } + return false; + } + if (!strncmp (line, "inc\t", 4)) + { + int regIdx = regidxfromregname (line + 4); + if (0 <= regIdx) + { + if (regs8051[regIdx].rtrack.valueKnown) + rtrack_data_set_val (regIdx, (unsigned char) (regs8051[regIdx].rtrack.value + 1)); + else + /* explicitely unsetting (could be known by symbol). + not yet handling offset to a symbol. (idata/pdata) */ + rtrack_data_unset (regIdx); + + return false; + } + } + return false; + } + + /* some bit in acc is cleared + MB: I'm too lazy to find out which right now */ + if (!strncmp (line, "jbc\tacc", 7)) + { + rtrack_data_unset (A_IDX); + return false; + } + + /* unfortunately the label typically following these + will cause loss of tracking */ + if (!strncmp (line, "jc\t", 3) || + !strncmp (line, "jnc\t", 4) || + !strncmp (line, "jb\t", 3) || + !strncmp (line, "jnb\t", 4) || + !strncmp (line, "jbc\t", 4)) + return false; + + /* if branch not taken in "cjne r2,#0x08,somewhere" + r2 is known to be 8 */ + if (!strncmp (line, "cjne\t", 5)) + { + int regIdx = regidxfromregname (line + 5); + if (0 <= regIdx) + { + char *argument = strstr (line, ",") + 1; + char *s; + int value; + + value = strtol (argument + 1, &s, 16); + + /* check literal compare to register */ + if ((s != argument + 1) && !strncmp (argument, "#0x", 3)) + { + rtrack_data_set_val (regIdx, (unsigned char) value); + return false; + } + rtrack_data_unset (regIdx); + } + return false; + } + + /* acc eventually known to be zero */ + if (!strncmp (line, "jz\t", 3)) + return false; + + /* acc eventually known to be zero */ + if (!strncmp (line, "jnz\t", 4)) + { + rtrack_data_set_val (A_IDX, 0x00); // branch not taken + return false; + } + + if (!strncmp (line, "djnz\t", 5)) + { + int regIdx = regidxfromregname (line + 5); + if (0 <= regIdx) + { + rtrack_data_set_val (regIdx, 0x00); // branch not taken + return false; + } + } + + /* only carry bit, so we do not care */ + if (!strncmp (line, "setb\tc", 6) || + !strncmp (line, "clr\tc", 5) || + !strncmp (line, "cpl\tc", 5)) + return false; + + /* operations on acc which depend on PSW */ + if (!strncmp (line, "addc\ta,", 7)|| + !strncmp (line, "subb\ta,", 7)|| + !strncmp (line, "da\ta", 4) || + !strncmp (line, "rlc\ta", 5) || + !strncmp (line, "rrc\ta", 5)) + { + rtrack_data_unset (A_IDX); + return false; + } + + /* bitwise operations on acc */ + if (!strncmp (line, "setb\ta", 6) || + !strncmp (line, "clrb\ta", 6)) + { + rtrack_data_unset (A_IDX); + return false; + } + + /* other operations on acc that can be tracked */ + if (!strncmp (line, "add\ta,", 6) || + !strncmp (line, "anl\ta,", 6) || + !strncmp (line, "orl\ta,", 6) || + !strncmp (line, "xrl\ta,", 6) || + !strcmp (line, "cpl\ta")) + { + if (regs8051[A_IDX].rtrack.valueKnown) + { + if (!strncmp (line, "add\ta,", 6)) + { + int regIdx = regidxfromregname (line + 6); + + if (0 <= regIdx && regs8051[regIdx].rtrack.valueKnown) + { + rtrack_data_set_val (A_IDX, (unsigned char) (regs8051[A_IDX].rtrack.value + regs8051[regIdx].rtrack.value)); + return false; + } + else if (('#' == line[6]) && (0 <= valuefromliteral (line + 7))) + { + rtrack_data_set_val (A_IDX, (unsigned char) (regs8051[A_IDX].rtrack.value + valuefromliteral (line + 7))); + return false; + } + } + + if (!strncmp (line, "anl\ta,", 6)) + { + int regIdx = regidxfromregname (line + 6); + + if (0 <= regIdx && regs8051[regIdx].rtrack.valueKnown) + { + rtrack_data_set_val (A_IDX, (unsigned char) (regs8051[A_IDX].rtrack.value & regs8051[regIdx].rtrack.value)); + return false; + } + else if (('#' == line[6]) && (0 <= valuefromliteral (line + 7))) + { + rtrack_data_set_val (A_IDX, (unsigned char) (regs8051[A_IDX].rtrack.value & valuefromliteral (line + 7))); + return false; + } + } + + if (!strncmp (line, "orl\ta,", 6)) + { + int regIdx = regidxfromregname (line + 6); + + if (0 <= regIdx && regs8051[regIdx].rtrack.valueKnown) + { + rtrack_data_set_val (A_IDX, (unsigned char) (regs8051[A_IDX].rtrack.value | regs8051[regIdx].rtrack.value)); + return false; + } + else if (('#' == line[6]) && (0 <= valuefromliteral (line + 7))) + { + rtrack_data_set_val (A_IDX, (unsigned char) (regs8051[A_IDX].rtrack.value | valuefromliteral (line + 7))); + return false; + } + } + + if (!strncmp (line, "xrl\ta,", 6)) + { + int regIdx = regidxfromregname (line + 6); + + if (0 <= regIdx && regs8051[regIdx].rtrack.valueKnown) + { + rtrack_data_set_val (A_IDX, (unsigned char) (regs8051[A_IDX].rtrack.value ^ regs8051[regIdx].rtrack.value)); + return false; + } + else if (('#' == line[6]) && (0 <= valuefromliteral (line + 7))) + { + rtrack_data_set_val (A_IDX, (unsigned char) (regs8051[A_IDX].rtrack.value ^ valuefromliteral (line + 7))); + return false; + } + } + + if (!strcmp (line, "cpl\ta")) + { + rtrack_data_set_val (A_IDX, (unsigned char) (regs8051[A_IDX].rtrack.value ^ 0xff)); + return false; + } + + rtrack_data_unset (A_IDX); + return false; + } + else + { + rtrack_data_unset (A_IDX); + return false; + } + } + + if (!strncmp (line, "dec\t", 4)) + { + int regIdx = regidxfromregname (line + 4); + if (0 <= regIdx) + { + if (regs8051[regIdx].rtrack.valueKnown) + rtrack_data_set_val (regIdx, (unsigned char) (regs8051[regIdx].rtrack.value - 1)); + + /* not handling offset to a symbol. invalidating if needed */ + if (NULL != regs8051[regIdx].rtrack.symbol) + rtrack_data_unset (regIdx); + + return false; + } + return false; + } + + if (!strcmp (line, "clr\ta")) + { + if (regs8051[A_IDX].rtrack.valueKnown && (0 == regs8051[A_IDX].rtrack.value)) + { + emitcode (";", "genFromRTrack removed\t%s", line); + modified = true; + } + rtrack_data_set_val (A_IDX, 0); + return modified; + } + + if (!strcmp (line, "cpl\ta")) + { + if (regs8051[A_IDX].rtrack.valueKnown) + rtrack_data_set_val (A_IDX, (unsigned char) (~regs8051[A_IDX].rtrack.value)); + else + /* in case a holds a symbol */ + rtrack_data_unset (A_IDX); + return false; + } + if (!strcmp (line, "rl\ta")) + { + if (regs8051[A_IDX].rtrack.valueKnown) + rtrack_data_set_val (A_IDX, (unsigned char) ((regs8051[A_IDX].rtrack.value<<1) | + (regs8051[A_IDX].rtrack.value>>7))); + else + rtrack_data_unset (A_IDX); + return false; + } + if (!strcmp (line, "rr\ta")) + { + if (regs8051[A_IDX].rtrack.valueKnown) + rtrack_data_set_val (A_IDX, (unsigned char) ((regs8051[A_IDX].rtrack.value>>1) | + (regs8051[A_IDX].rtrack.value<<7))); + else + rtrack_data_unset (A_IDX); + return false; + } + if (!strcmp (line, "swap\ta")) + { + if (regs8051[A_IDX].rtrack.valueKnown) + rtrack_data_set_val (A_IDX, (unsigned char) ((regs8051[A_IDX].rtrack.value>>4) | + (regs8051[A_IDX].rtrack.value<<4))); + else + rtrack_data_unset (A_IDX); + return false; + } + + if (!strncmp (line, "mul\t", 4)) + { + if (regs8051[A_IDX].rtrack.valueKnown && regs8051[B_IDX].rtrack.valueKnown) + { + unsigned int value = (unsigned int)regs8051[A_IDX].rtrack.value * + (unsigned int)regs8051[B_IDX].rtrack.value; + + rtrack_data_set_val (A_IDX, (unsigned char) value); + rtrack_data_set_val (B_IDX, (unsigned char) (value >> 8)); + } + else + { + rtrack_data_unset (A_IDX); + rtrack_data_unset (B_IDX); + } + return false; + } + + if (!strncmp (line, "div\t", 4)) + { + if (regs8051[A_IDX].rtrack.valueKnown && regs8051[B_IDX].rtrack.valueKnown) + { + rtrack_data_set_val (A_IDX, (unsigned char) (regs8051[A_IDX].rtrack.value / regs8051[B_IDX].rtrack.value)); + rtrack_data_set_val (B_IDX, (unsigned char) (regs8051[A_IDX].rtrack.value % regs8051[B_IDX].rtrack.value)); + } + else + { + rtrack_data_unset (A_IDX); + rtrack_data_unset (B_IDX); + } + return false; + } + + /* assuming these library functions have no side-effects */ + if (!strncmp (line, "lcall", 5)) + { + if (!strcmp (line, "lcall\t__gptrput")) + { + /* invalidate R0..R7 because they might have been changed */ + /* MB: too paranoid ? */ + //invalidateAllRx(); + return false; + } + if (!strcmp (line, "lcall\t__gptrget")) + { + rtrack_data_unset (A_IDX); + return false; + } + if (!strcmp (line, "lcall\t__decdptr")) + { + if (regs8051[DPH_IDX].rtrack.valueKnown && + regs8051[DPL_IDX].rtrack.valueKnown) + { + int val = (regs8051[DPH_IDX].rtrack.value << 8) | regs8051[DPL_IDX].rtrack.value; + val -= 1; + rtrack_data_set_val (DPL_IDX, (unsigned char) val); + rtrack_data_set_val (DPH_IDX, (unsigned char) (val >> 8)); + } + else + { + rtrack_data_unset (DPL_IDX); + rtrack_data_unset (DPH_IDX); + } + return false; + } + /* if callee_saves */ + } + + if (!strncmp (line, "xch\ta,", 6)) + { + /* handle xch from register (r0..r7, dpl, dph, b) */ + int regIdx = regidxfromregname (line + 6); + if (0 <= regIdx) + { + void* swap = Safe_malloc (sizeof regs8051[A_IDX].rtrack); + + memcpy (swap, ®s8051[A_IDX].rtrack, sizeof regs8051[A_IDX].rtrack); + memcpy (®s8051[A_IDX ].rtrack, ®s8051[regIdx].rtrack, sizeof regs8051[A_IDX].rtrack); + memcpy (®s8051[regIdx].rtrack, swap, sizeof regs8051[A_IDX].rtrack); + + Safe_free (swap); + return false; + } + } + + /* all others unrecognized, invalidate */ + invalidateAll(); + return false; +} + + +/* expects f.e. "#0x01" and returns either "#0x01" + if the value is not known to be within registers + or "a" or "r0".."r7". + (mov a,r7 or add a,r7 need one byte whereas + mov a,#0x01 or add a,#0x01 would take two + */ +char * rtrackGetLit(const char *x) +{ + unsigned int i; + + char *s; + + if (enable != 1) + return (char *)x; + + /* was it a numerical literal? */ + if (*x == '#') + { + int val = strtol (x+1, &s, 16); + if (x+1 != s) + { + /* try to get from acc */ + reg_info *r = ®s8051[A_IDX]; + if (r->rtrack.valueKnown && + r->rtrack.value == val) + { + D(emitcode (";", "genFromRTrack 0x%02x==%s", val, r->name)); + return r->name; + } + /* try to get from register R0..R7 */ + for (i = 0; i < 8; i++) + { + reg_info *r = ®s8051[rx_num_to_idx(i)]; + if (r->rtrack.valueKnown && + r->rtrack.value == val) + { + D(emitcode (";", "genFromRTrack 0x%02x==%s", val, r->name)); + return r->name; + } + } + } + else + { + /* probably a symbolic literal as in "mov r3,#(_i+1)", + not handled... */ + } + } + + return (char *)x; +} + +/* Similar to the above function + As the destination is the accumulator try harder yet and + try to generate the result with arithmetic operations */ +int rtrackMoveALit (const char *x) +{ + + if (enable != 1) + return 0; + + /* if it is a literal mov try to get it cheaper */ + if ( *x == '#' ) + { + reg_info *a = ®s8051[A_IDX]; + + char *s; + int val = strtol (x+1, &s, 16); + + /* was it a numerical literal? */ + if (x+1 != s) + { + /* prefer mov a,#0x00 */ + if (val == 0 && + ((a->rtrack.valueKnown && a->rtrack.value != 0) || + !a->rtrack.valueKnown)) + { + /* peepholes convert to clr a */ + /* (regression test suite is slightly larger if "clr a" is used here) */ + emitcode ("mov", "a,#0x00"); + return 1; + } + + if (a->rtrack.valueKnown) + { + /* already there? */ + if (val == a->rtrack.value) + { + D(emitcode (";", "genFromRTrack acc==0x%02x", a->rtrack.value)); + return 1; + } + + /* can be calculated with an instruction + that does not change flags from acc itself? */ + if (val == ((a->rtrack.value+1) & 0xff) ) + { + D(emitcode (";", "genFromRTrack 0x%02x==0x%02x+1", val, a->rtrack.value)); + emitcode ("inc", "a"); + return 1; + } + if (val == ((a->rtrack.value-1) & 0xff) ) + { + D(emitcode (";", "genFromRTrack 0x%02x==0x%02x-1", val, a->rtrack.value)); + emitcode ("dec", "a"); + return 1; + } + if (val == ((~a->rtrack.value) & 0xff) ) + { + D(emitcode (";", "genFromRTrack 0x%02x==~0x%02x", val, a->rtrack.value)); + emitcode ("cpl", "a"); + return 1; + } + if (val == (((a->rtrack.value>>1) | + (a->rtrack.value<<7)) & 0xff)) + { + D(emitcode (";", "genFromRTrack 0x%02x==rr(0x%02x)", val, a->rtrack.value)); + emitcode ("rr", "a"); + return 1; + } + if (val == (((a->rtrack.value<<1) | + (a->rtrack.value>>7)) & 0xff )) + { + D(emitcode (";", "genFromRTrack 0x%02x==rl(0x%02x)", val, a->rtrack.value)); + emitcode ("rl", "a"); + return 1; + } + if (val == ( ((a->rtrack.value & 0x0f)<<4) | + ((a->rtrack.value & 0xf0)>>4) )) + { + D(emitcode (";", "genFromRTrack 0x%02x==swap(0x%02x)", val, a->rtrack.value)); + emitcode ("swap", "a"); + return 1; + } + /* Decimal Adjust Accumulator (da a) changes flags so not used */ + } + + + { + unsigned int i; + char *ptr= rtrackGetLit(x); + + if (x != ptr) + { + /* could get from register, fine */ + emitcode ("mov", "a,%s", ptr); + return 1; + } + + /* not yet giving up - try to calculate from register R0..R7 */ + for (i = 0; i < 8; i++) + { + reg_info *r = ®s8051[rx_num_to_idx(i)]; + + if (a->rtrack.valueKnown && r->rtrack.valueKnown) + { + /* calculate with a single byte instruction from R0..R7? */ + if (val == (a->rtrack.value | r->rtrack.value)) + { + D(emitcode (";", "genFromRTrack 0x%02x==0x%02x|0x%02x", + val, a->rtrack.value, r->rtrack.value)); + emitcode ("orl", "a,%s",r->name); + return 1; + } + if (val == (a->rtrack.value & r->rtrack.value)) + { + D(emitcode (";", "genFromRTrack 0x%02x==0x%02x&0x%02x", + val, a->rtrack.value, r->rtrack.value)); + emitcode ("anl", "a,%s", r->name); + return 1; + } + if (val == (a->rtrack.value ^ r->rtrack.value)) + { + D(emitcode (";", "genFromRTrack 0x%02x==0x%02x^0x%02x", + val, a->rtrack.value, r->rtrack.value)); + emitcode ("xrl", "a,%s", r->name); + return 1; + } + /* changes flags (does that matter?) + if (val == (a->rtrack.value + r->rtrack.value)) + { + D(emitcode (";", "genFromRTrack 0x%02x=0x%02x+%0x02x", + val, a->rtrack.value, r->rtrack.value)); + emitcode ("add", "a,%s",r->name); + return 1; + } + so not used */ + } + } + } + } + } + + return 0; +} + + +/* Loads dptr with symbol (if needed) + */ +void rtrackLoadDptrWithSym (const char *x) +{ + if (enable != 1) + { + emitcode ("mov", "dptr,#%s", x); + return; + } + + if (regs8051[DPL_IDX].rtrack.symbol && + regs8051[DPH_IDX].rtrack.symbol) + { + /* rtrack.symbol for dph should look like "(something >> 8)" */ + if ((!strcmp (x, regs8051[DPL_IDX].rtrack.symbol) && + !strncmp (x, regs8051[DPH_IDX].rtrack.symbol + 1, strlen (x) ) && + !strncmp (" >> 8)", regs8051[DPH_IDX].rtrack.symbol + 1 + strlen (x), 6))) + { + /* dptr already holds the symbol */ + D(emitcode (";", "genFromRTrack dptr==#%s",x)); + return; + } + } + + emitcode ("mov", "dptr,#%s", x); +} + + +#if 0 +/* Loads index registers R0, R1 with symbol (if needed) + * + * R0, R1 index registers are already handled in gen.c (see AOP_INPREG) + */ +void rtrackLoadR0R1WithSym (const char *reg, const char *x) +{ + int regNum, regIdx; + + if (enable != 1) + { + emitcode ("mov", "%s,#%s", reg, x ); + return; + } + + regNum = reg[1] - '0'; + if (regNum == 0 || regNum == 1) + { + regIdx = rx_num_to_idx(regNum); + if ((NULL != regs8051[regIdx].rtrack.symbol) && !strcmp (x, regs8051[regIdx].rtrack.symbol)) + { + /* register already holds the symbol */ + D(emitcode (";", "genFromRTrack %s=#%s",reg,x)); + return; + } + } + + emitcode ("mov", "%s,#%s", reg, x ); +} +#endif diff --git a/src/mcs51/rtrack.h b/src/mcs51/rtrack.h new file mode 100644 index 0000000..dfad2e0 --- /dev/null +++ b/src/mcs51/rtrack.h @@ -0,0 +1,28 @@ +/*------------------------------------------------------------------------- + rtrack.h - header file for tracking content of registers on an mcs51 + + Copyright 2007 Frieder Ferlemann (Frieder Ferlemann AT web.de) + + 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 of the License, 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +-------------------------------------------------------------------------*/ + +bool _mcs51_rtrackUpdate (const char *line); + +char * rtrackGetLit(const char *x); + +int rtrackMoveALit (const char *x); + +void rtrackLoadDptrWithSym (const char *x); +void rtrackLoadR0R1WithSym (const char *reg, const char *x); |
