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/pdk | |
| 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/pdk')
| -rw-r--r-- | src/pdk/Makefile | 7 | ||||
| -rw-r--r-- | src/pdk/Makefile.dep | 45 | ||||
| -rw-r--r-- | src/pdk/Makefile.in | 7 | ||||
| -rw-r--r-- | src/pdk/gen.c | 4244 | ||||
| -rw-r--r-- | src/pdk/gen.h | 90 | ||||
| -rw-r--r-- | src/pdk/gen.o | bin | 0 -> 538800 bytes | |||
| -rw-r--r-- | src/pdk/main.c | 714 | ||||
| -rw-r--r-- | src/pdk/main.o | bin | 0 -> 85464 bytes | |||
| -rw-r--r-- | src/pdk/peep.c | 521 | ||||
| -rw-r--r-- | src/pdk/peep.h | 6 | ||||
| -rw-r--r-- | src/pdk/peep.o | bin | 0 -> 117680 bytes | |||
| -rw-r--r-- | src/pdk/peeph.def | 56 | ||||
| -rw-r--r-- | src/pdk/peeph.rul | 58 | ||||
| -rw-r--r-- | src/pdk/port.a | bin | 0 -> 13086816 bytes | |||
| -rw-r--r-- | src/pdk/ralloc.c | 747 | ||||
| -rw-r--r-- | src/pdk/ralloc.h | 64 | ||||
| -rw-r--r-- | src/pdk/ralloc.o | bin | 0 -> 128192 bytes | |||
| -rw-r--r-- | src/pdk/ralloc2.cc | 692 | ||||
| -rw-r--r-- | src/pdk/ralloc2.o | bin | 0 -> 12156320 bytes |
19 files changed, 7251 insertions, 0 deletions
diff --git a/src/pdk/Makefile b/src/pdk/Makefile new file mode 100644 index 0000000..cb704c7 --- /dev/null +++ b/src/pdk/Makefile @@ -0,0 +1,7 @@ + +srcdir = . +top_builddir = ../.. +top_srcdir = ../.. + +# Make all in this directory +include $(srcdir)/../port.mk diff --git a/src/pdk/Makefile.dep b/src/pdk/Makefile.dep new file mode 100644 index 0000000..8edde56 --- /dev/null +++ b/src/pdk/Makefile.dep @@ -0,0 +1,45 @@ +gen.o: gen.c ralloc.h ../common.h ../SDCCglobl.h ../SDCCset.h \ + ../../sdccconf.h ../SDCCerr.h ../SDCCmem.h ../../support/util/dbuf.h \ + ../SDCCast.h ../SDCCsymt.h ../SDCChasht.h ../SDCCval.h ../SDCCy.h \ + ../SDCCbitv.h ../SDCCicode.h ../SDCClabel.h ../SDCCBBlock.h \ + ../SDCCloop.h ../SDCCcse.h ../SDCCcflow.h ../SDCCdflow.h ../SDCClrange.h \ + ../SDCCptropt.h ../SDCCopt.h ../SDCCglue.h ../SDCCpeeph.h ../SDCCgen.h \ + ../SDCCicode.h ../SDCCdebug.h ../SDCCutil.h ../SDCCasm.h ../SDCCsystem.h \ + ../port.h ../SDCCargs.h ../../support/util/newalloc.h gen.h +main.o: main.c ../common.h ../SDCCglobl.h ../SDCCset.h ../../sdccconf.h \ + ../SDCCerr.h ../SDCCmem.h ../../support/util/dbuf.h ../SDCCast.h \ + ../SDCCsymt.h ../SDCChasht.h ../SDCCval.h ../SDCCy.h ../SDCCbitv.h \ + ../SDCCicode.h ../SDCClabel.h ../SDCCBBlock.h ../SDCCloop.h ../SDCCcse.h \ + ../SDCCcflow.h ../SDCCdflow.h ../SDCClrange.h ../SDCCptropt.h \ + ../SDCCopt.h ../SDCCglue.h ../SDCCpeeph.h ../SDCCgen.h ../SDCCicode.h \ + ../SDCCdebug.h ../SDCCutil.h ../SDCCasm.h ../SDCCsystem.h ../port.h \ + ../SDCCargs.h ../../support/util/newalloc.h \ + ../../support/util/dbuf_string.h ../../support/util/dbuf.h ralloc.h \ + peep.h peeph.rul +peep.o: peep.c ../common.h ../SDCCglobl.h ../SDCCset.h ../../sdccconf.h \ + ../SDCCerr.h ../SDCCmem.h ../../support/util/dbuf.h ../SDCCast.h \ + ../SDCCsymt.h ../SDCChasht.h ../SDCCval.h ../SDCCy.h ../SDCCbitv.h \ + ../SDCCicode.h ../SDCClabel.h ../SDCCBBlock.h ../SDCCloop.h ../SDCCcse.h \ + ../SDCCcflow.h ../SDCCdflow.h ../SDCClrange.h ../SDCCptropt.h \ + ../SDCCopt.h ../SDCCglue.h ../SDCCpeeph.h ../SDCCgen.h ../SDCCicode.h \ + ../SDCCdebug.h ../SDCCutil.h ../SDCCasm.h ../SDCCsystem.h ../port.h \ + ../SDCCargs.h ../../support/util/newalloc.h ../SDCCgen.h peep.h +ralloc.o: ralloc.c ralloc.h ../common.h ../SDCCglobl.h ../SDCCset.h \ + ../../sdccconf.h ../SDCCerr.h ../SDCCmem.h ../../support/util/dbuf.h \ + ../SDCCast.h ../SDCCsymt.h ../SDCChasht.h ../SDCCval.h ../SDCCy.h \ + ../SDCCbitv.h ../SDCCicode.h ../SDCClabel.h ../SDCCBBlock.h \ + ../SDCCloop.h ../SDCCcse.h ../SDCCcflow.h ../SDCCdflow.h ../SDCClrange.h \ + ../SDCCptropt.h ../SDCCopt.h ../SDCCglue.h ../SDCCpeeph.h ../SDCCgen.h \ + ../SDCCicode.h ../SDCCdebug.h ../SDCCutil.h ../SDCCasm.h ../SDCCsystem.h \ + ../port.h ../SDCCargs.h ../../support/util/newalloc.h gen.h \ + ../../support/util/dbuf_string.h ../../support/util/dbuf.h +ralloc2.o: ralloc2.cc ../SDCCralloc.hpp ../common.h ../SDCCglobl.h \ + ../SDCCset.h ../../sdccconf.h ../SDCCerr.h ../SDCCmem.h \ + ../../support/util/dbuf.h ../SDCCast.h ../SDCCsymt.h ../SDCChasht.h \ + ../SDCCval.h ../SDCCy.h ../SDCCbitv.h ../SDCCicode.h ../SDCClabel.h \ + ../SDCCBBlock.h ../SDCCloop.h ../SDCCcse.h ../SDCCcflow.h ../SDCCdflow.h \ + ../SDCClrange.h ../SDCCptropt.h ../SDCCopt.h ../SDCCglue.h \ + ../SDCCpeeph.h ../SDCCgen.h ../SDCCicode.h ../SDCCdebug.h ../SDCCutil.h \ + ../SDCCasm.h ../SDCCsystem.h ../port.h ../SDCCargs.h \ + ../../support/util/newalloc.h ../SDCCbtree.h ../SDCCtree_dec.hpp \ + ../SDCCsalloc.hpp ralloc.h ../common.h gen.h diff --git a/src/pdk/Makefile.in b/src/pdk/Makefile.in new file mode 100644 index 0000000..dfb8a52 --- /dev/null +++ b/src/pdk/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/pdk/gen.c b/src/pdk/gen.c new file mode 100644 index 0000000..576c69d --- /dev/null +++ b/src/pdk/gen.c @@ -0,0 +1,4244 @@ +/*------------------------------------------------------------------------- + gen.c - code generator for Padauk. + + Copyright (C) 2018, Philipp Klaus Krause pkk@spth.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, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#include "ralloc.h" +#include "gen.h" + +/* Use the D macro for basic (unobtrusive) debugging messages */ +#define D(x) do if (options.verboseAsm) { x; } while (0) + +static bool regalloc_dry_run; +static unsigned int regalloc_dry_run_cost_words; +static float regalloc_dry_run_cost_cycles; +static unsigned int regalloc_dry_run_cycle_scale = 1; + +static struct +{ + short debugLine; + struct + { + int pushed; + int size; + int param_offset; + } stack; + bool saved; + + /* Track content of p */ + struct + { + AOP_TYPE type; + const char *base; + int offset; + } p; +} +G; + +static struct asmop asmop_a, asmop_p, asmop_pa, asmop_ap, asmop_zero, asmop_one, asmop_sp; +static struct asmop *const ASMOP_A = &asmop_a; +static struct asmop *const ASMOP_P = &asmop_p; +static struct asmop *const ASMOP_PA = &asmop_pa; +static struct asmop *const ASMOP_AP = &asmop_ap; +static struct asmop *const ASMOP_ZERO = &asmop_zero; +static struct asmop *const ASMOP_ONE = &asmop_one; +static struct asmop *const ASMOP_SP = &asmop_sp; + +void +pdk_init_asmops (void) +{ + asmop_a.type = AOP_REG; + asmop_a.size = 1; + asmop_a.aopu.bytes[0].in_reg = true; + asmop_a.aopu.bytes[0].byteu.reg = pdk_regs + A_IDX; + + asmop_p.type = AOP_REG; + asmop_p.size = 1; + asmop_p.aopu.bytes[0].in_reg = true; + asmop_p.aopu.bytes[0].byteu.reg = pdk_regs + P_IDX; + + asmop_ap.type = AOP_REG; + asmop_ap.size = 2; + asmop_ap.aopu.bytes[0].in_reg = true; + asmop_ap.aopu.bytes[0].byteu.reg = pdk_regs + A_IDX; + asmop_ap.aopu.bytes[1].in_reg = true; + asmop_ap.aopu.bytes[1].byteu.reg = pdk_regs + P_IDX; + + asmop_pa.type = AOP_REG; + asmop_pa.size = 2; + asmop_pa.aopu.bytes[0].in_reg = true; + asmop_pa.aopu.bytes[0].byteu.reg = pdk_regs + P_IDX; + asmop_pa.aopu.bytes[1].in_reg = true; + asmop_pa.aopu.bytes[1].byteu.reg = pdk_regs + A_IDX; + + asmop_zero.type = AOP_LIT; + asmop_zero.size = 1; + asmop_zero.aopu.aop_lit = constVal ("0"); + + asmop_one.type = AOP_LIT; + asmop_one.size = 1; + asmop_one.aopu.aop_lit = constVal ("1"); + + asmop_sp.type = AOP_SFR; + asmop_sp.aopu.aop_dir = "sp"; + asmop_sp.size = 1; +} + +static void +emit2 (const char *inst, const char *fmt, ...) +{ + if (!regalloc_dry_run) + { + va_list ap; + + va_start (ap, fmt); + va_emitcode (inst, fmt, ap); + va_end (ap); + } +} + +static void +cost(unsigned int words, float cycles) +{ + regalloc_dry_run_cost_words += words; + regalloc_dry_run_cost_cycles += cycles * regalloc_dry_run_cycle_scale; +} + +static void +emitJP(const symbol *target, float probability) +{ + if (!regalloc_dry_run) + emit2 ("goto", "%05d$", labelKey2num (target->key)); + cost (1, 2 * probability); +} + +static bool +regDead (int idx, const iCode *ic) +{ + wassert (idx == A_IDX || idx == P_IDX); + + return (!bitVectBitValue (ic->rSurv, idx)); +} + +/*-----------------------------------------------------------------*/ +/* aopInReg - asmop from offset in the register */ +/*-----------------------------------------------------------------*/ +static bool +aopInReg (const asmop *aop, int offset, short rIdx) +{ + if (aop->type != AOP_REG) + return (false); + + return (aop->aopu.bytes[offset].in_reg && aop->aopu.bytes[offset].byteu.reg->rIdx == rIdx); +} + +/*-----------------------------------------------------------------*/ +/* aopSame - are two asmops in the same location? */ +/*-----------------------------------------------------------------*/ +static bool +aopSame (const asmop *aop1, int offset1, const asmop *aop2, int offset2, int size) +{ + for(; size; size--, offset1++, offset2++) + { + if (aop1->type == AOP_REG && aop2->type == AOP_REG && // Same register + aop1->aopu.bytes[offset1].in_reg && aop2->aopu.bytes[offset2].in_reg && + aop1->aopu.bytes[offset1].byteu.reg == aop2->aopu.bytes[offset2].byteu.reg) + continue; + + if (aop1->type == AOP_LIT && aop2->type == AOP_LIT && + byteOfVal (aop1->aopu.aop_lit, offset1) == byteOfVal (aop2->aopu.aop_lit, offset2)) + continue; + + if (aop1->type == AOP_DIR && aop2->type == AOP_DIR && aop1->aopu.immd_off + offset1 == aop2->aopu.immd_off + offset2 && + !strcmp(aop1->aopu.aop_dir, aop2->aopu.aop_dir)) + return (true); + + if (aop1->type == AOP_SFR && aop2->type == AOP_SFR && offset1 == offset2 && + aop1->aopu.aop_dir && aop2->aopu.aop_dir && !strcmp(aop1->aopu.aop_dir, aop2->aopu.aop_dir)) + return (true); + + return (false); + } + + return (true); +} + +/*-----------------------------------------------------------------*/ +/* aopIsLitVal - asmop from offset is val */ +/*-----------------------------------------------------------------*/ +static bool +aopIsLitVal (const asmop *aop, int offset, int size, unsigned long long int val) +{ + wassert_bt (size <= sizeof (unsigned long long int)); // Make sure we are not testing outside of argument val. + + for(; size; size--, offset++) + { + unsigned char b = val & 0xff; + val >>= 8; + + // Leading zeroes + if (aop->size <= offset && !b) + continue; + + if (aop->type == AOP_IMMD && offset > (aop->aopu.code ? 1 : 0) && !b) + continue; + + if (aop->size <= offset) + return (false); + + if (aop->type != AOP_LIT) + return (false); + + if (byteOfVal (aop->aopu.aop_lit, offset) != b) + return (false); + } + + return (true); +} + +static const char * +aopGet(const asmop *aop, int offset) +{ + static char buffer[256]; + + if (offset >= aop->size) + return ("#0x00"); + + if (aop->type == AOP_LIT) + { + SNPRINTF (buffer, sizeof(buffer), "#0x%02x", byteOfVal (aop->aopu.aop_lit, offset)); + return (buffer); + } + + if (aop->type == AOP_REG) + return (aop->aopu.bytes[offset].byteu.reg->name); + + if (aop->type == AOP_IMMD) + { + if (offset == 0 && aop->aopu.code) + SNPRINTF (buffer, sizeof(buffer), "#<(%s + %d)", aop->aopu.immd, aop->aopu.immd_off); + else if (offset == 1 && aop->aopu.func) + SNPRINTF (buffer, sizeof(buffer), "#>(%s + %d)", aop->aopu.immd, aop->aopu.immd_off); + else if (offset == 1 && aop->aopu.code) + SNPRINTF (buffer, sizeof(buffer), "#>(%s + 0x8000 + %d)", aop->aopu.immd, aop->aopu.immd_off); + else if (offset == 0) + SNPRINTF (buffer, sizeof(buffer), "#(%s + %d)", aop->aopu.immd, aop->aopu.immd_off); + else + SNPRINTF (buffer, sizeof(buffer), "#0", aop->aopu.immd, aop->aopu.immd_off); + return (buffer); + } + + if (aop->type == AOP_DIR) + { + SNPRINTF (buffer, sizeof(buffer), "%s+%d", aop->aopu.aop_dir, offset); + return (buffer); + } + else if (aop->type == AOP_SFR) + { + wassert (aop->size == 1); + SNPRINTF (buffer, sizeof(buffer), "%s", aop->aopu.aop_dir); + return (buffer); + } + + wassert_bt (0); + return ("dummy"); +} + +/*-----------------------------------------------------------------*/ +/* newAsmop - creates a new asmOp */ +/*-----------------------------------------------------------------*/ +static asmop * +newAsmop (short type) +{ + asmop *aop; + + aop = Safe_calloc (1, sizeof (asmop)); + aop->type = type; + + return (aop); +} + +/*-----------------------------------------------------------------*/ +/* freeAsmop - free up the asmop given to an operand */ +/*----------------------------------------------------------------*/ +static void +freeAsmop (operand *op) +{ + asmop *aop; + + wassert_bt (op); + + aop = op->aop; + + if (!aop) + return; + + Safe_free (aop); + + op->aop = 0; + if (IS_SYMOP (op) && SPIL_LOC (op)) + SPIL_LOC (op)->aop = 0; +} + +/*-----------------------------------------------------------------*/ +/* aopForSym - for a true symbol */ +/*-----------------------------------------------------------------*/ +static asmop * +aopForSym (const iCode *ic, symbol *sym) +{ + asmop *aop; + + wassert_bt (ic); + wassert_bt (regalloc_dry_run || sym); + wassert_bt (regalloc_dry_run || sym->etype); + + // Unlike some other backends we really free asmops; to avoid a double-free, we need to support multiple asmops for the same symbol. + + if (sym && IS_FUNC (sym->type)) + { + aop = newAsmop (AOP_IMMD); + aop->aopu.immd = sym->rname; + aop->aopu.immd_off = 0; + aop->aopu.code = IN_CODESPACE (SPEC_OCLS (sym->etype)); + aop->aopu.func = true; + aop->size = getSize (sym->type); + } + /* Assign depending on the storage class */ + else if (sym && sym->onStack || sym && sym->iaccess) + { + aop = newAsmop (AOP_STK); + aop->size = getSize (sym->type); + int base = sym->stack + (sym->stack < 0 ? G.stack.param_offset : 0); + for (int offset = 0; offset < aop->size; offset++) + aop->aopu.bytes[offset].byteu.stk = base + offset; + } + /* sfr */ + else if (sym && IN_REGSP (SPEC_OCLS (sym->etype))) + { + wassertl (getSize (sym->type) <= 2, "Unimplemented support for wide (> 16 bit) I/O register"); + + aop = newAsmop (AOP_SFR); + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + } + else + { + aop = newAsmop (sym && IN_CODESPACE (SPEC_OCLS (sym->etype)) ? AOP_CODE : AOP_DIR); + if (sym) + { + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + } + } + + return (aop); +} + +/*-----------------------------------------------------------------*/ +/* aopForRemat - rematerializes an object */ +/*-----------------------------------------------------------------*/ +static asmop * +aopForRemat (symbol *sym) +{ + iCode *ic = sym->rematiCode; + asmop *aop; + int val = 0; + + wassert_bt (ic); + + for (;;) + { + if (ic->op == '+') + { + if (isOperandLiteral (IC_RIGHT (ic))) + { + val += (int) operandLitValue (IC_RIGHT (ic)); + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + } + else + { + val += (int) operandLitValue (IC_LEFT (ic)); + ic = OP_SYMBOL (IC_RIGHT (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)) + { + ic = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + } + else if (ic->op == ADDRESS_OF) + { + val += (int) operandLitValue (IC_RIGHT (ic)); + break; + } + else + wassert_bt (0); + } + + wassert (!OP_SYMBOL (IC_LEFT (ic))->onStack); +#if 0 // TODO: Enable for support for rematerialization of addresses on stack. + if (OP_SYMBOL (IC_LEFT (ic))->onStack) + { + aop = newAsmop (AOP_STL); + aop->aopu.stk_off = (long)(OP_SYMBOL (IC_LEFT (ic))->stack) + 1 + val; + } + else +#endif + { + aop = newAsmop (AOP_IMMD); + aop->aopu.immd = OP_SYMBOL (IC_LEFT (ic))->rname; + aop->aopu.immd_off = val; + aop->aopu.code = IN_CODESPACE (SPEC_OCLS (OP_SYMBOL (IC_LEFT (ic))->etype)); + } + + aop->size = getSize (sym->type); + + return aop; +} + +/*-----------------------------------------------------------------*/ +/* aopOp - allocates an asmop for an operand : */ +/*-----------------------------------------------------------------*/ +static void +aopOp (operand *op, const iCode *ic) +{ + wassert_bt (op); + + /* if already has an asmop */ + if (op->aop) + return; + + /* if this a literal */ + if (IS_OP_LITERAL (op)) + { + asmop *aop = newAsmop (AOP_LIT); + aop->aopu.aop_lit = OP_VALUE (op); + aop->size = getSize (operandType (op)); + op->aop = aop; + return; + } + + symbol *sym = OP_SYMBOL (op); + + /* if this is a true symbol */ + if (IS_TRUE_SYMOP (op)) + { + op->aop = aopForSym (ic, sym); + return; + } + + /* Rematerialize symbols where all bytes are spilt. */ + if (sym->remat && (sym->isspilt || regalloc_dry_run)) + { + bool completely_spilt = TRUE; + for (int i = 0; i < getSize (sym->type); i++) + if (sym->regs[i]) + completely_spilt = FALSE; + if (completely_spilt) + { + op->aop = aopForRemat (sym); + return; + } + } + + /* if the type is a conditional */ + if (sym->regType == REG_CND) + { + asmop *aop = newAsmop (AOP_CND); + op->aop = aop; + sym->aop = sym->aop; + return; + } + + /* None of the above, which only leaves temporaries. */ + if ((sym->isspilt || sym->nRegs == 0) && !(regalloc_dry_run && (options.stackAuto || reentrant))) + { + sym->aop = op->aop = aopForSym (ic, sym->usl.spillLoc); + op->aop->size = getSize (sym->type); + return; + } + + /* None of the above, which only leaves temporaries. */ + { + bool completely_in_regs = true; + bool completely_spilt = true; + asmop *aop = newAsmop (AOP_REGDIR); + + aop->size = getSize (operandType (op)); + op->aop = aop; + + for (int i = 0; i < aop->size; i++) + { + aop->aopu.bytes[i].in_reg = !!sym->regs[i]; + if (sym->regs[i]) + { + completely_spilt = false; + aop->aopu.bytes[i].byteu.reg = sym->regs[i]; + //aop->regs[sym->regs[i]->rIdx] = i; + } + else if (sym->isspilt && sym->usl.spillLoc || sym->nRegs && regalloc_dry_run) + { + completely_in_regs = false; + + if (!regalloc_dry_run) + { + /*aop->aopu.bytes[i].byteu.stk = (long int)(sym->usl.spillLoc->stack) + aop->size - i; + + if (sym->usl.spillLoc->stack + aop->size - (int)(i) <= -G.stack.pushed) + { + fprintf (stderr, "%s %d %d %d %d at ic %d\n", sym->name, (int)(sym->usl.spillLoc->stack), (int)(aop->size), (int)(i), (int)(G.stack.pushed), ic->key); + wassertl_bt (0, "Invalid stack offset."); + }*/ + } + else + { + static long int old_base = -10; + static const symbol *old_sym = 0; + if (sym != old_sym) + { + old_base -= aop->size; + if (old_base < -100) + old_base = -10; + old_sym = sym; + } + + // aop->aopu.bytes[i].byteu.stk = old_base + aop->size - i; + } + } + else // Dummy iTemp. + { + aop->type = AOP_DUMMY; + return; + } + + if (!completely_in_regs && (!currFunc || GcurMemmap == statsg)) + { + if (!regalloc_dry_run) + wassertl_bt (0, "Stack asmop outside of function."); + cost (180, 180); + } + } + + if (completely_in_regs) + aop->type = AOP_REG; + else if (completely_spilt && !(options.stackAuto || reentrant)) + { + aop->type = AOP_DIR; + aop->aopu.immd = sym->rname; + } + else if (completely_spilt) + aop->type = AOP_STK; + else + wassertl (0, "Unsupported partially spilt aop"); + } +} + +static void +cheapMove (const asmop *result, int roffset, const asmop *source, int soffset, bool a_dead, bool f_dead); + +/*-----------------------------------------------------------------*/ +/* pushAF - push af, adjusting stack tracking */ +/*-----------------------------------------------------------------*/ +static void pushAF (void) +{ + emit2 ("push", "af"); + cost (1, 1); + G.stack.pushed += 2; +} + +/*-----------------------------------------------------------------*/ +/* popAF - pop af, adjusting stack tracking */ +/*-----------------------------------------------------------------*/ +static void popAF (void) +{ + emit2 ("pop", "af"); + cost (1, 1); + G.stack.pushed -= 2; +} + +/*-----------------------------------------------------------------*/ +/* pointPStack - Make pseudo-register p point to stack location */ +/*-----------------------------------------------------------------*/ +static void pointPStack (int s, bool a_dead, bool f_dead) +{ + // Try to adjust p when doing so is cheaper. + if (G.p.type == AOP_STK) + { + if (G.p.offset == s) + return; + + if (!f_dead) + pushAF(); + + if (abs(G.p.offset - s) < 3) + { + while (G.p.offset < s) + { + emit2 ("inc", "p"); + cost (1, 1); + G.p.offset++; + } + + while (G.p.offset > s) + { + emit2 ("dec", "p"); + cost (1, 1); + G.p.offset--; + } + } + else if (a_dead || !f_dead) + { + emit2 ("mov", "a, #%d", s - G.p.offset); + emit2 ("add", "p, a"); + cost (2, 2); + G.p.offset = s; + } + else + { + emit2 ("xch", "a, p"); + emit2 ("add", "a, #%d", s - G.p.offset); + emit2 ("xch", "a, p"); + cost (3, 3); + G.p.offset = s; + } + + if (!f_dead) + popAF(); + + return; + } + + if (!a_dead && f_dead) + { + int soffset = s - G.stack.pushed; + emit2 ("xch", "a, p"); + emit2 ("mov", "a, sp"); + emit2 ("add", "a, #0x%02x", soffset & 0xff); + emit2 ("xch", "a, p"); + cost (4, 4); + } + else + { + if (!(a_dead && f_dead)) + pushAF(); + + int soffset = s - G.stack.pushed; + cheapMove (ASMOP_A, 0, ASMOP_SP, 0, true, true); + emit2 ("add", "a, #0x%02x", soffset & 0xff); + cost (1, 1); + cheapMove (ASMOP_P, 0, ASMOP_A, 0, true, true); + + if (!(a_dead && f_dead)) + popAF(); + } + + G.p.type = AOP_STK; + G.p.offset = s; +} + +/*-----------------------------------------------------------------*/ +/* moveStackStack - Move a block of memory on the stack. */ +/*-----------------------------------------------------------------*/ +static void +moveStackStack (int d, int s, int size, bool a_dead) +{ + if (!a_dead) + pushAF (); + + bool up = (d <= s); + + for (int i = up ? 0 : size - 1; up ? i < size : i >= 0; up ? i++ : i--) + { + pointPStack (s + i, true, true); + emit2 ("idxm", "a, p"); + cost (1, 2); + pointPStack (d + i, false, true); + emit2 ("idxm", "p, a"); + cost (1, 2); + } + + if (!a_dead) + popAF(); +} + +/*-----------------------------------------------------------------*/ +/* cheapMove - Copy a byte from one asmop to another */ +/*-----------------------------------------------------------------*/ +static void +cheapMove (const asmop *result, int roffset, const asmop *source, int soffset, bool a_dead, bool f_dead) +{ + bool dummy = (result->type == AOP_DUMMY || source->type == AOP_DUMMY); + + if (aopSame (result, roffset, source, soffset, 1)) + return; + else if (!dummy && (result->type == AOP_DIR || aopInReg (result, roffset, P_IDX)) && aopIsLitVal (source, soffset, 1, 0)) + { + emit2 ("clear", "%s", aopGet (result, roffset)); + cost (1, 1); + } + else if (source->type == AOP_CODE && aopInReg (result, roffset, A_IDX)) + { + emit2 ("call", "%s+%d", source->aopu.aop_dir, soffset); + cost (1, 4); + } + else if (source->type == AOP_STK && aopInReg (result, roffset, A_IDX) && !aopIsLitVal (source, soffset, 1, 0)) + { + pointPStack(source->aopu.bytes[soffset].byteu.stk, true, f_dead); + emit2 ("idxm", "a, p"); + cost (1, 2); + } + else if (result->type == AOP_STK && aopInReg (source, soffset, A_IDX)) + { + pointPStack(result->aopu.bytes[roffset].byteu.stk, false, f_dead); + emit2 ("idxm", "p, a"); + cost (1, 2); + } + else if (aopInReg (result, roffset, A_IDX)) + { + emit2 ("mov", "a, %s", aopGet (source, soffset)); + cost (1, 1); + } + else if (aopInReg (source, soffset, A_IDX)) + { + emit2 ("mov", "%s, a", aopGet (result, roffset)); + cost (1, 1); + } + else + { + if (!a_dead) + pushAF(); + cheapMove (ASMOP_A, 0, source, soffset, true, f_dead || !a_dead); + cheapMove (result, roffset, ASMOP_A, 0, true, f_dead || !a_dead); + if (!a_dead) + popAF(); + } + + if (aopInReg (result, roffset, P_IDX)) + G.p.type = AOP_INVALID; +} + +/*--------------------------------------------------------------------------*/ +/* adjustStack - Adjust the stack pointer by n bytes. */ +/*--------------------------------------------------------------------------*/ +static void +adjustStack (int n, bool a_free, bool p_free) +{ + wassertl_bt (!(n % 2), "Unsupported odd stack adjustment"); // The datasheets seem to require the stack pointer to be aligned to a 2-byte boundary. + + if (n >= 0 && (!(p_free || a_free) || n <= 4)) + for (int i = 0; i < n; i += 2) + { + emit2 ("push", "af"); + cost (1, 1); + } + else if (!a_free && p_free) + { + emit2 ("xch", "a, p"); + emit2 ("mov", "a, sp"); + emit2 ("add", "a, #%d", n); + emit2 ("mov", "sp, a"); + emit2 ("xch", "a, p"); + cost (5, 5); + } + else if (!a_free && !p_free && n < 0) + { + pushAF(); + cheapMove (ASMOP_A, 0, ASMOP_P, 0, true, true); + pushAF(); + + moveStackStack (G.stack.pushed - 4 + n, G.stack.pushed - 4, 4, true); + + emit2 ("mov", "a, sp"); + emit2 ("add", "a, #%d", n); + emit2 ("mov", "sp, a"); + cost (3, 3); + + popAF(); + cheapMove (ASMOP_P, 0, ASMOP_A, 0, true, true); + popAF(); + } + else // Can't use pop af, since it might affect reserved flag bits. + { + wassert (a_free); + emit2 ("mov", "a, sp"); + emit2 ("add", "a, #%d", n); + emit2 ("mov", "sp, a"); + cost (3, 3); + } + + G.stack.pushed += n; +} + +static void +push (const asmop *op, int offset, int size) +{ + wassertl (!(size % 2) && (op->type == AOP_DIR || op->type == AOP_LIT || op->type == AOP_IMMD || op->type == AOP_STK), "Unimplemented push operand"); + + if (op->type == AOP_STK) + { + int s = G.stack.pushed; + adjustStack (size, true, true); + moveStackStack (s, op->aopu.bytes[0].byteu.stk, size, true); + return; + } + else if (size == 2) + { + cheapMove (ASMOP_A, 0, op, 0, true, true); + pushAF (); + pointPStack (G.stack.pushed - 1, true, true); + cheapMove (ASMOP_A, 0, op, 1, true, true); + emit2 ("idxm", "p, a"); + cost (1, 1); + return; + } + + // Save old stack pointer + emit2 ("mov", "a, sp"); + emit2 ("mov", "p, a"); + G.p.type = AOP_INVALID; + cost (2, 2); + + adjustStack (size, true, false); + + // Write value onto stack + for (int i = offset; i < offset + size; i++) + { + cheapMove (ASMOP_A, 0, op, i, true, true); + emit2 ("idxm", "p, a"); + cost (1, 2); + if (i + 1 < offset + size) + { + emit2 ("inc", "p"); + cost (1, 1); + } + } + G.p.type = AOP_INVALID; +} + +/*-----------------------------------------------------------------*/ +/* genMove_o - Copy part of one asmop to another */ +/*-----------------------------------------------------------------*/ +static void +genMove_o (asmop *result, int roffset, asmop *source, int soffset, int size, bool a_dead_global) +{ + // Handle I/O first. + wassert_bt ((result->type == AOP_SFR) + (source->type == AOP_SFR) <= 1); + if (result->type == AOP_SFR || source->type == AOP_SFR) + switch (size) + { + case 1: + cheapMove (result, roffset, source, soffset, a_dead_global, true); + return; + case 2: +#if 0 // TODO: Implement alignment requirements - ldt16 needs 16-bit-aligned operand + if (result->type == AOP_SFR && source->type == AOP_DIR) + { + emit2 ("stt16", "%s", aopGet (source, soffset)); + cost (1, 1); // TODO: Really just 1 cycle? Other 16-bit-transfer instructions use 2. + } + else +#endif + if (result->type == AOP_SFR && source->type == AOP_LIT && aopIsLitVal (source, 1, 1, 0x00)) + { + cheapMove (ASMOP_P, 0, source, 0, true, true); + emit2 ("stt16", "p"); + cost (1, 1); // TODO: Really just 1 cycle? Other 16-bit-transfer instructions use 2. + } +#if 0 // TODO: Implement alignment requirements - ldt16 needs 16-bit-aligned operand + else if (result->type == AOP_DIR && source->type == AOP_SFR) + { + emit2 ("ldt16", "%s", aopGet (result, roffset)); + cost (1, 1); // TODO: Really just 1 cycle? Other 16-bit-transfer instructions use 2. + } +#endif + else if (regalloc_dry_run) + cost (1000, 1000); + else + wassertl (0, "Unimplemenetd operand in __sfr16 access"); + return; + default: + wassertl (0, "Unknown __sfr size"); + } + + wassert_bt (result->type == AOP_DIR || result->type == AOP_REG || result->type == AOP_STK); + wassert_bt (source->type == AOP_LIT || source->type == AOP_IMMD || source->type == AOP_DIR || source->type == AOP_REG || source->type == AOP_STK || source->type == AOP_CODE); + + if (size == 2 && aopInReg (result, roffset, P_IDX) && aopInReg (result, roffset + 1, A_IDX) && source->type == AOP_STK) + { + cheapMove (result, roffset + 1, source, soffset + 1, true, true); + cheapMove (result, roffset + 0, source, soffset + 0, false, true); + return; + } + else if (size == 2 && result->type == AOP_STK && aopInReg (source, soffset, A_IDX) && aopInReg (source, soffset + 1, P_IDX)) + { + pushAF (); + emit2 ("xch", "a, p"); + cost (1, 1); + cheapMove (result, roffset + 1, ASMOP_A, 0, false, true); + popAF (); + cheapMove (result, roffset + 0, ASMOP_A, 0, true, true); + return; + } + else if (size == 2 && result->type == AOP_DIR && !a_dead_global && // Using xch cheaper than push / pop. + aopInReg (source, soffset, A_IDX) && aopInReg (source, soffset + 1, P_IDX)) + { + cheapMove (result, roffset + 0, ASMOP_A, 0, false, true); + emit2 ("xch", "a, p"); + cost (1, 1); + cheapMove (result, roffset + 1, ASMOP_A, 0, false, true); + emit2 ("xch", "a, p"); + cost (1, 1); + return; + } + else if (size == 2 && result->type == AOP_DIR && !a_dead_global && // Using xch cheaper than push / pop. + aopInReg (source, soffset, P_IDX) && aopInReg (source, soffset + 1, A_IDX)) + { + cheapMove (result, roffset + 1, ASMOP_A, 0, false, true); + emit2 ("xch", "a, p"); + cost (1, 1); + cheapMove (result, roffset + 0, ASMOP_A, 0, false, true); + emit2 ("xch", "a, p"); + cost (1, 1); + return; + } + else if (size == 2 && // Assign upper byte first to avoid overwriting a. + (aopInReg (result, roffset, A_IDX) && aopInReg (result, roffset + 1, P_IDX) && source->type == AOP_DIR || + aopInReg (source, soffset, P_IDX) && aopInReg (source, soffset + 1, A_IDX) && result->type == AOP_DIR)) + { + cheapMove (result, roffset + 1, source, soffset + 1, a_dead_global, true); + cheapMove (result, roffset + 0, source, soffset + 0, a_dead_global, true); + return; + } + else if (size == 2 && + (aopInReg (source, soffset, A_IDX) && aopInReg (source, soffset + 1, P_IDX) && aopInReg (result, roffset, P_IDX) && aopInReg (result, roffset + 1, A_IDX) || + aopInReg (source, soffset, P_IDX) && aopInReg (source, soffset + 1, A_IDX) && aopInReg (result, roffset, A_IDX) && aopInReg (result, roffset + 1, P_IDX))) + { + emit2 ("xch", "a, p"); + cost (1, 1); + return; + } + else if (size >= 2 && result->type == AOP_DIR && source->type == AOP_DIR && !strcmp (result->aopu.aop_dir, source->aopu.aop_dir) && soffset < roffset) + { + if (!a_dead_global) + pushAF(); + if (soffset + 1 == roffset) // Use xch via a. + { + emit2 ("mov", "a, %s", aopGet (source, soffset)); + for (int i = 0; i < size - 1; i++) + emit2 ("xch", "a, %s", aopGet (result, roffset + i)); + emit2 ("mov", "%s, a", aopGet (result, roffset + size - 1)); + cost (size + 1, size + 1); + } + else // Copy high-to-low to avoid overwriting of still-needed bytes. + { + for (int i = size - 1; i >= 0; i--) + cheapMove (result, roffset + i, source, soffset + i, true, true); + } + if (!a_dead_global) + popAF(); + return; + } + else if (size >= 2 && result->type == AOP_DIR && source->type == AOP_DIR && !strcmp (result->aopu.aop_dir, source->aopu.aop_dir) && soffset > roffset && roffset + 1 == soffset && a_dead_global) // Use xch via a. + { + emit2 ("mov", "a, %s", aopGet (source, soffset + size - 1)); + for (int i = size - 1; i > 0; i--) + emit2 ("xch", "a, %s", aopGet (result, roffset + i)); + emit2 ("mov", "%s, a", aopGet (result, roffset)); + cost (size + 1, size + 1); + return; + } + + bool a_dead = a_dead_global; + for (unsigned int i = 0; i < size; i++) + { + cheapMove (result, roffset + i, source, soffset + i, a_dead, true); + if (aopInReg (result, roffset + i, A_IDX)) + a_dead = false; + } +} + +/*-----------------------------------------------------------------*/ +/* genMove - Copy the value from one asmop to another */ +/*-----------------------------------------------------------------*/ +static void +genMove (asmop *result, asmop *source, bool a_dead) +{ + genMove_o (result, 0, source, 0, result->size, a_dead); +} + +/*-----------------------------------------------------------------*/ +/* isLiteralBit - test if lit == 2^n */ +/*-----------------------------------------------------------------*/ +static int +isLiteralBit (unsigned long lit) +{ + unsigned long pw[32] = + { + 1l, 2l, 4l, 8l, 16l, 32l, 64l, 128l, + 0x100l, 0x200l, 0x400l, 0x800l, + 0x1000l, 0x2000l, 0x4000l, 0x8000l, + 0x10000l, 0x20000l, 0x40000l, 0x80000l, + 0x100000l, 0x200000l, 0x400000l, 0x800000l, + 0x1000000l, 0x2000000l, 0x4000000l, 0x8000000l, + 0x10000000l, 0x20000000l, 0x40000000l, 0x80000000l + }; + int idx; + + for (idx = 0; idx < 32; idx++) + if (lit == pw[idx]) + return idx; + return -1; +} + +/*-----------------------------------------------------------------*/ +/* genNot - generates code for ! */ +/*-----------------------------------------------------------------*/ +static void +genNot (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + + D (emit2 ("; genNot", "")); + + aopOp (left, ic); + aopOp (result, ic); + + cheapMove (ASMOP_A, 0, left->aop, 0, true, true); + for (int i = 1; i < left->aop->size; i++) + { + if (left->aop->type == AOP_STK) + { + if (!regDead (P_IDX, ic)) + { + cost (1000, 1000); + wassert (regalloc_dry_run); + } + cheapMove (ASMOP_P, 0, left->aop, i, false, true); + emit2 ("or", "a, p"); + } + else + emit2 ("or", "a, %s", aopGet (left->aop, i)); + cost (1, 1); + } + emit2 ("sub", "a, #0x01"); + emit2 ("mov", "a, #0x00"); + emit2 ("slc", "a"); + + cheapMove (result->aop, 0, ASMOP_A, 0, true, true); + genMove_o (result->aop, 1, ASMOP_ZERO, 0, result->aop->size - 1, true); + + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genCpl - generate code for complement */ +/*-----------------------------------------------------------------*/ +static void +genCpl (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + + D (emit2 ("; genCpl", "")); + + aopOp (left, ic); + aopOp (result, ic); + + int size = result->aop->size; + + genMove (result->aop, left->aop, true); + + for(int i = 0; i < size; i++) + { + emit2("not", "%s", aopGet (result->aop, i)); + cost (1, 1); + } + + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genSub - generates code for subtraction */ +/*-----------------------------------------------------------------*/ +static void +genSub (const iCode *ic, asmop *result_aop, asmop *left_aop, asmop *right_aop) +{ + int size = result_aop->size; + + bool started = false; + bool pushed_a = false; + for (int i = 0; i < size; i++) + { + if (!started && right_aop->type == AOP_LIT && aopIsLitVal (right_aop, i, 1, 0x00)) + { + cheapMove (result_aop, i, left_aop, i, regDead (A_IDX, ic), true); + if (aopInReg (result_aop, i, A_IDX) && i + 1 < size) + { + pushAF(); + pushed_a = true; + } + continue; + } + else if (!started && i + 1 == size && aopIsLitVal (left_aop, i, 1, 0x00) && + (right_aop->type == AOP_DIR || aopInReg (right_aop, i, P_IDX)) && aopSame (right_aop, i, result_aop, i, 1)) + { + emit2 ("neg", "%s", aopGet (right_aop, i)); + cost (1, 1); + started = true; + continue; + } + else if (!started && aopIsLitVal (right_aop, i, 1, 0x01) && + (left_aop->type == AOP_DIR || aopInReg (left_aop, i, P_IDX)) && aopSame (left_aop, i, result_aop, i, 1)) + { + emit2 ("dec", "%s", aopGet (left_aop, i)); + cost (1, 1); + started = true; + continue; + } + else if (!started && i + 1 == size && aopIsLitVal (right_aop, i, 1, 0xff) && + (left_aop->type == AOP_DIR || aopInReg (left_aop, i, P_IDX)) && aopSame (left_aop, i, result_aop, i, 1)) + { + emit2 ("inc", "%s", aopGet (left_aop, i)); + cost (1, 1); + started = true; + continue; + } + else if (started && (left_aop->type == AOP_DIR || left_aop->type == AOP_REGDIR || left_aop->type == AOP_REG) && aopIsLitVal (right_aop, i, 1, 0x00) && aopSame (left_aop, i, result_aop, i, 1)) + { + emit2 ("subc", "%s", aopGet (left_aop, i)); + cost (1, 1); + continue; + } + + if (!(regDead (A_IDX, ic) || pushed_a)) + { + pushAF(); + pushed_a = true; + } + + if ((left_aop->type == AOP_DIR || aopInReg (left_aop, i, P_IDX)) && right_aop->type != AOP_STK && aopSame (left_aop, i, result_aop, i, 1)) + { + cheapMove (ASMOP_A, 0, right_aop, i, true, true); + emit2 (started ? "subc" : "sub", "%s, a", aopGet (left_aop, i)); + cost (1, 1); + started = true; + continue; + } + else if (!started && i + 1 == size && aopIsLitVal (left_aop, i, 1, 0x00)) + { + cheapMove (ASMOP_A, 0, right_aop, i, true, true); + emit2 ("neg", "a"); + cost (1, 1); + started = true; + } + else if (right_aop->type == AOP_STK) + { + cheapMove (ASMOP_A, 0, left_aop, i, true, !started); + cheapMove (ASMOP_P, 0, right_aop, i, false, !started); + emit2 (started ? "subc" : "sub", "a, p"); + cost (1, 1); + started = true; + } + else if (started && (right_aop->type == AOP_LIT || right_aop->type == AOP_IMMD) && !aopIsLitVal (right_aop, i, 1, 0x00) && i + 1 == size) + { + cheapMove (ASMOP_A, 0, left_aop, i, true, true); + emit2 ("subc", "a"); + emit2 ("sub", "a, %s", aopGet (right_aop, i)); + cost (2, 2); + } + else if (started && (right_aop->type == AOP_LIT || right_aop->type == AOP_IMMD) && !aopIsLitVal (right_aop, i, 1, 0x00)) + { + cheapMove (ASMOP_P, 0, right_aop, i, !aopInReg (left_aop, i, A_IDX), false); + cheapMove (ASMOP_A, 0, left_aop, i, true, false); + emit2 ("subc", "a, p"); + cost (1, 1); + } + else + { + cheapMove (ASMOP_A, 0, left_aop, i, true, !started); + if (started || !aopIsLitVal (right_aop, i, 1, 0x00)) + { + if (aopInReg (right_aop, i, A_IDX)) + { + cost (1000, 1000); + if (!regalloc_dry_run) + wassertl (0, "Unimplemented operand in subtraction"); + } + if (started && aopIsLitVal (right_aop, i, 1, 0x00)) + emit2 ("subc", "a"); + else + emit2 (started ? "subc" : "sub", "a, %s", aopGet (right_aop, i)); + cost (1, 1); + started = true; + } + } + if (i + 1 < size && aopInReg (result_aop, i, P_IDX) && (left_aop->type == AOP_STK || right_aop->type == AOP_STK)) + if (regalloc_dry_run) + cost (1000, 1000); + else + wassertl (0, "Unimplemented p result in subtraction with stack operand"); + if (i + 1 < size && result_aop->type == AOP_STK && (aopInReg (left_aop, i + 1, P_IDX) || aopInReg (right_aop, i + 1, P_IDX))) + if (regalloc_dry_run) + cost (1000, 1000); + else + wassertl (0, "Unimplemented upper byte p operand in subtraction with stack result"); + + if (aopInReg (result_aop, i, A_IDX) && i + 1 < size) + { + pushAF(); + pushed_a = true; + } + else + cheapMove (result_aop, i, ASMOP_A, 0, true, i + 1 == size); + } + + if (pushed_a) + popAF(); +} + +/*-----------------------------------------------------------------*/ +/* genUminus - generates code for unary minus */ +/*-----------------------------------------------------------------*/ +static void +genUminusFloat (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + + D (emit2 ("; genUminusFloat", "")); + + genMove_o (result->aop, 0, left->aop, 0, result->aop->size - 1, true); + + cheapMove (ASMOP_A, 0, left->aop, result->aop->size - 1, true, true); + emit2 ("xor", "a, #0x80"); + cost (1, 1); + cheapMove (result->aop, result->aop->size - 1, ASMOP_A, 0, true, true); + + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genUminus - generates code for unary minus */ +/*-----------------------------------------------------------------*/ +static void +genUminus (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + + aopOp (left, ic); + aopOp (result, ic); + + if (IS_FLOAT (operandType (left))) + { + genUminusFloat (ic); + return; + } + + D (emit2 ("; genUminus", "")); + + genSub (ic, result->aop, ASMOP_ZERO, left->aop); + + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genIpush - generate code for pushing this gets a little complex */ +/*-----------------------------------------------------------------*/ +static void +genIpush (const iCode *ic) +{ + operand *left = IC_LEFT (ic); + + aopOp (left, ic); + + D (emit2 ("; genIPush", "")); + + if (!ic->parmPush) + wassertl (0, "Encountered an unsupported spill push."); + + wassertl (left->aop->size == 1 || !(left->aop->size % 2), "Unimplemented operand size for parameter push"); + + if (left->aop->size == 1) + { + cheapMove (ASMOP_A, 0, left->aop, 0, true, true); + pushAF(); + } + else + push (left->aop, 0, left->aop->size); + + freeAsmop (IC_LEFT (ic)); +} + +/*-----------------------------------------------------------------*/ +/* genCall - generates a call statement */ +/*-----------------------------------------------------------------*/ +static void +genCall (const iCode *ic) +{ + sym_link *dtype = operandType (IC_LEFT (ic)); + sym_link *etype = getSpec (dtype); + sym_link *ftype = IS_FUNCPTR (dtype) ? dtype->next : dtype; + bool bigreturn = (getSize (ftype->next) > 2) || IS_STRUCT (ftype->next); + + D (emit2 ("; genCall", "")); + + if (bigreturn) + { + wassertl (IC_RESULT (ic), "Unused return value in call to function returning large type."); + + const symbol *rsym = OP_SYMBOL_CONST (IC_RESULT (ic)); + if (rsym->usl.spillLoc) + rsym = rsym->usl.spillLoc; + + if (rsym->onStack || rsym->isspilt && regalloc_dry_run && (options.stackAuto || reentrant)) + { + emit2 ("mov", "a, sp"); + emit2 ("add", "a, #0x%02x", (rsym->stack + (rsym->stack < 0 ? G.stack.param_offset : 0) - G.stack.pushed) & 0xff); + } + else + { + emit2 ("mov", "a, #%s", rsym->rname); + cost (1, 1); + } + pushAF (); + } + + if (!regDead (A_IDX, ic) || !regDead (P_IDX, ic)) + { + cost (700, 700); + wassertl (regalloc_dry_run, "Register saving across call not yet implemented"); + } + + if (ic->op == PCALL) + { + operand *left = IC_LEFT (ic); + + aopOp (left, ic); + + // Push return address + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + + if (!regalloc_dry_run) + { + emit2 ("mov", "a, #<(!tlabel)", labelKey2num (tlbl->key)); + emit2 ("push", "af"); + emit2 ("mov", "a, sp"); + emit2 ("mov", "p, a"); + emit2 ("dec", "p"); + emit2 ("mov", "a, #>(!tlabel)", labelKey2num (tlbl->key)); + emit2 ("idxm", "p, a"); + G.p.type = AOP_INVALID; + } + G.stack.pushed += 2; + cost (7, 8); + + // Jump to function + push (left->aop, 0, 2); + emit2 ("ret", ""); + G.stack.pushed -= 4; + cost (2, 1); + + emitLabel (tlbl); + + freeAsmop (left); + } + else + { + if (IS_LITERAL (etype)) + emit2 ("call", "0x%04X", ulFromVal (OP_VALUE (IC_LEFT (ic)))); + else + { + bool jump = (!ic->parmBytes && IFFUNC_ISNORETURN (OP_SYMBOL (IC_LEFT (ic))->type)); + emit2 (jump ? "goto" : "call", "%s", + (OP_SYMBOL (IC_LEFT (ic))->rname[0] ? OP_SYMBOL (IC_LEFT (ic))->rname : OP_SYMBOL (IC_LEFT (ic))->name)); + } + cost (1, 2); + } + G.p.type = AOP_INVALID; + + bool SomethingReturned = (IS_ITEMP (IC_RESULT (ic)) && + (OP_SYMBOL (IC_RESULT (ic))->nRegs || OP_SYMBOL (IC_RESULT (ic))->spildir)) + || IS_TRUE_SYMOP (IC_RESULT (ic)); + + if (!SomethingReturned || bigreturn) + adjustStack (-ic->parmBytes - bigreturn * 2, true, true); + else + { + aopOp (IC_RESULT (ic), ic); + + genMove (IC_RESULT (ic)->aop, ASMOP_AP, true); + + adjustStack (-ic->parmBytes, !(aopInReg (IC_RESULT (ic)->aop, 0, A_IDX) || aopInReg (IC_RESULT (ic)->aop, 1, A_IDX)), !(aopInReg (IC_RESULT (ic)->aop, 0, P_IDX) || aopInReg (IC_RESULT (ic)->aop, 1, P_IDX))); + + freeAsmop (IC_RESULT (ic)); + } +} + +/*-----------------------------------------------------------------*/ +/* genFunction - generated code for function entry */ +/*-----------------------------------------------------------------*/ +static void +genFunction (iCode *ic) +{ + const symbol *sym = OP_SYMBOL_CONST (IC_LEFT (ic)); + sym_link *ftype = operandType (IC_LEFT (ic)); + + G.stack.pushed = 0; + G.stack.param_offset = 0; + + /* create the function header */ + emit2 (";", "-----------------------------------------"); + emit2 (";", " function %s", sym->name); + emit2 (";", "-----------------------------------------"); + + D (emit2 (";", pdk_assignment_optimal ? "Register assignment is optimal." : "Register assignment might be sub-optimal.")); + + emit2 ("", "%s:", sym->rname); + if (!regalloc_dry_run) + genLine.lineCurr->isLabel = 1; + + if (IFFUNC_ISNAKED(ftype)) + { + emit2 (";", "naked function: no prologue."); + return; + } + + if (IFFUNC_ISISR (sym->type)) + { + pushAF (); + cheapMove (ASMOP_A, 0, ASMOP_P, 0, true, true); + pushAF (); + } + + G.stack.param_offset -= (getSize (ftype->next) > 2 || IS_STRUCT (ftype->next)) * 2; // Account for hidden parameter holding address of return value. + + G.p.type = AOP_INVALID; + + adjustStack (sym->stack, true, true); +} + +/*-----------------------------------------------------------------*/ +/* genEndFunction - generates epilogue for functions */ +/*-----------------------------------------------------------------*/ +static void +genEndFunction (iCode *ic) +{ + symbol *sym = OP_SYMBOL (IC_LEFT (ic)); + int retsize = getSize (sym->type->next); + + D (emit2 ("; genEndFunction", "")); + + if (IFFUNC_ISNAKED(sym->type)) + { + D (emit2 (";", "naked function: no epilogue.")); + if (options.debug && currFunc && !regalloc_dry_run) + debugFile->writeEndFunction (currFunc, ic, 0); + return; + } + + /* adjust the stack for the function */ + if (sym->stack) + adjustStack (-sym->stack, retsize == 0 || retsize > 2, retsize != 2); + + /* if debug then send end of function */ + if (options.debug && currFunc && !regalloc_dry_run) + debugFile->writeEndFunction (currFunc, ic, 1); + + if (IFFUNC_ISISR (sym->type)) + { + popAF (); + cheapMove (ASMOP_P, 0, ASMOP_A, 0, true, true); + popAF (); + emit2 ("reti", ""); + cost (1, 2); + } + else + { + emit2 ("ret", ""); + cost (1, 1); + } + + wassertl (!G.stack.pushed, "Unbalanced stack."); +} + +/*-----------------------------------------------------------------*/ +/* genReturn - generate code for return statement */ +/*-----------------------------------------------------------------*/ +static void +genReturn (const iCode *ic) +{ + operand *left = IC_LEFT (ic); + + D (emit2 ("; genReturn", "")); + + /* if we have no return value then + just generate the "ret" */ + if (!left) + goto jumpret; + + /* we have something to return then + move the return value into place */ + aopOp (left, ic); + + wassertl (currFunc, "return iCode outside of function"); + + if (left->aop->size > 2) + { + if (left->aop->type == AOP_STK) + { + for (int i = 0; i < left->aop->size; i++) + { + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + pushAF (); + pointPStack (-4, true, true); + emit2 ("idxm", "a, p"); + emit2 ("mov", "p, a"); + cost (2, 3); + popAF (); + for (int j = 0; j < i; j++) + { + emit2 ("inc", "p"); + cost (1, 1); + } + emit2 ("idxm", "p, a"); + cost (1, 2); + G.p.type = AOP_INVALID; + } + } + else + { + pointPStack (-4, true, true); + emit2 ("idxm", "a, p"); + emit2 ("mov", "p, a"); + cost (2, 3); + for (int i = 0; i < left->aop->size; i++) + { + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + emit2 ("idxm", "p, a"); + cost (1, 2); + if (i + 1 < left->aop->size) + { + emit2 ("inc", "p"); + cost (1, 1); + } + } + } + goto end; + } + + if (left->aop->size == 2 && aopInReg (left->aop, 0, P_IDX) && aopInReg (left->aop, 1, A_IDX)) + { + emit2 ("xch", "a, p"); + cost(1, 1); + goto end; + } + else if (left->aop->size == 2 && left->aop->type == AOP_STK) + { + genMove (ASMOP_AP, left->aop, true); + goto end; + } + + if (left->aop->size > 1) + cheapMove (ASMOP_P, 0, left->aop, 1, true, true); + if ((left->aop->type == AOP_LIT || left->aop->type == AOP_IMMD) && !currFunc->stack) + { + emit2 ("ret", "%s", aopGet (left->aop, 0)); + cost (1, 2); + freeAsmop (left); + return; + } + cheapMove (ASMOP_A, 0, left->aop, 0, true, true); + +end: + freeAsmop (left); + +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)) + if (!currFunc->stack && !IFFUNC_ISISR (currFunc->type)) + { + emit2 ("ret", ""); + cost (2, 1); + } + else + emitJP(returnLabel, 1.0f); +} + +/*-----------------------------------------------------------------*/ +/* genLabel - generates a label */ +/*-----------------------------------------------------------------*/ +static void +genLabel (const iCode *ic) +{ + D (emit2 ("; genLabel", "")); + + /* special case never generate */ + if (IC_LABEL (ic) == entryLabel) + return; + + if (options.debug /*&& !regalloc_dry_run*/) + debugFile->writeLabel (IC_LABEL (ic), ic); + + emitLabel (IC_LABEL (ic)); + + G.p.type = AOP_INVALID; +} + +/*-----------------------------------------------------------------*/ +/* genGoto - generates a jump */ +/*-----------------------------------------------------------------*/ +static void +genGoto (const iCode *ic) +{ + D (emit2 ("; genGoto", "")); + + emitJP (IC_LABEL (ic), 1.0f); +} + +/*-----------------------------------------------------------------*/ +/* genPlus - generates code for addition */ +/*-----------------------------------------------------------------*/ +static void +genPlus (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + D (emit2 ("; genPlus", "")); + + aopOp (left, ic); + aopOp (right, ic); + aopOp (result, ic); + + int size = result->aop->size; + + /* Swap if left is literal or right is in A. */ + if (left->aop->type == AOP_LIT || aopInReg (right->aop, 0, A_IDX) || right->aop->type == AOP_STK && !aopInReg (left->aop, 0, A_IDX)) + { + operand *t = right; + right = left; + left = t; + } + + bool started = false; + bool pushed_a = false; + bool moved_to_a = false; + + for (int i = 0; i < size; i++) + { + if (!started && !moved_to_a && (left->aop->type == AOP_DIR || aopInReg (left->aop, i, P_IDX)) && aopIsLitVal (right->aop, i, 1, 0x01) && aopSame (left->aop, i, result->aop, i, 1)) + { + emit2 ("inc", "%s", aopGet (left->aop, i)); + cost (1, 1); + started = true; + continue; + } + else if (!started && !moved_to_a && i + 1 == size && (left->aop->type == AOP_DIR || aopInReg (left->aop, i, P_IDX)) && aopIsLitVal (right->aop, i, 1, 0xff) && aopSame (left->aop, i, result->aop, i, 1)) + { + emit2 ("dec", "%s", aopGet (left->aop, i)); + cost (1, 1); + started = true; + continue; + } + else if (started && !moved_to_a && (left->aop->type == AOP_DIR || aopInReg (left->aop, i, P_IDX)) && aopIsLitVal (right->aop, i, 1, 0x00) && aopSame (left->aop, i, result->aop, i, 1)) + { + emit2 ("addc", "%s", aopGet (left->aop, i)); + cost (1, 1); + continue; + } + else if (!started && !moved_to_a && right->aop->type == AOP_LIT && aopIsLitVal (right->aop, i, 1, 0x00)) + { + cheapMove (result->aop, i, left->aop, i, regDead (A_IDX, ic), true); + if (aopInReg (result->aop, i, A_IDX) && i + 1 < size) + { + pushAF(); + pushed_a = true; + } + continue; + } + + if (!(regDead (A_IDX, ic) || pushed_a)) + { + pushAF(); + pushed_a = true; + } + + if (!moved_to_a && (left->aop->type == AOP_DIR || aopInReg (left->aop, i, P_IDX)) && right->aop->type != AOP_STK && aopSame (left->aop, i, result->aop, i, 1)) + { + cheapMove (ASMOP_A, 0, right->aop, i, true, true); + emit2 (started ? "addc" : "add", "%s, a", aopGet (left->aop, i)); + cost (1, 1); + started = true; + continue; + } + else if (right->aop->type == AOP_STK) + { + if (!moved_to_a) + cheapMove (ASMOP_A, 0, left->aop, i, true, !started); + cheapMove (ASMOP_P, 0, right->aop, i, false, !started); + emit2 (started ? "addc" : "add", "a, p"); + cost (1, 1); + started = true; + } + else if (!moved_to_a && aopInReg (left->aop, i, P_IDX)) + { + cheapMove (ASMOP_A, 0, right->aop, i, true, !started); + emit2 (started ? "addc" : "add", "a, p"); + cost (1, 1); + } + else if (started && (right->aop->type == AOP_LIT || right->aop->type == AOP_IMMD) && !aopIsLitVal (right->aop, i, 1, 0x00) && i + 1 == size) + { + if (!moved_to_a) + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + emit2 ("addc", "a"); + emit2 ("add", "a, %s", aopGet (right->aop, i)); + cost (2, 2); + } + else if (started && (right->aop->type == AOP_LIT || right->aop->type == AOP_IMMD) && !aopIsLitVal (right->aop, i, 1, 0x00)) + { + if (!regDead (P_IDX, ic) || i > 0 && aopInReg (result->aop, i - 1, P_IDX)) + { + cost (100, 100); + wassert (regalloc_dry_run); + } + cheapMove (ASMOP_P, 0, right->aop, i, !aopInReg (left->aop, i, A_IDX) && !moved_to_a, false); + cheapMove (ASMOP_A, 0, left->aop, i, true, false); + emit2 ("addc", "a, p"); + cost (1, 1); + } + else + { + if (!moved_to_a) + cheapMove (ASMOP_A, 0, left->aop, i, true, !started); + if (started || !aopIsLitVal (right->aop, i, 1, 0x00)) + { + if (started && aopIsLitVal (right->aop, i, 1, 0x00)) + emit2 ("addc", "a"); + else + emit2 (started ? "addc" : "add", "a, %s", aopGet (right->aop, i)); + cost (1, 1); + started = true; + } + } + if (i + 1 < size && aopInReg (result->aop, i, P_IDX) && (left->aop->type == AOP_STK || right->aop->type == AOP_STK)) + if (regalloc_dry_run) + cost (1000, 1000); + else + wassertl (0, "Unimplemented p result in addition with stack operand"); + + if (aopInReg (result->aop, i, A_IDX) && i + 1 < size) + { + pushAF(); + pushed_a = true; + } + else if (aopInReg (result->aop, i, P_IDX) && regDead (P_IDX, ic) && aopInReg (left->aop, i + 1, P_IDX)) + { + emit2 ("xch", "a, p"); + cost (1, 1); + moved_to_a = true; + } + else + cheapMove (result->aop, i, ASMOP_A, 0, true, i + 1 == size); + } + + if (pushed_a) + popAF(); + + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genMinus - generates code for minus */ +/*-----------------------------------------------------------------*/ +static void +genMinus (const iCode *ic, const iCode *ifx) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + D (emit2 ("; genMinus", "")); + + aopOp (left, ic); + aopOp (right, ic); + aopOp (result, ic); + + if (ifx && ifx->generated) + { + wassert (IC_TRUE (ifx)); + wassert (left->aop->type == AOP_REG || left->aop->type == AOP_DIR); + wassert (aopIsLitVal (right->aop, 0, 2, 1)); + + emit2 ("dzsn", aopGet (left->aop, 0)); + cost (1, 1.8f); + emitJP (IC_TRUE (ifx), 0.2f); + + for(int i = 1; i < left->aop->size; i++) + { + emit2 ("subc", aopGet (left->aop, i)); + emit2 ("t1sn", "f, z"); + cost (2, 2.8f); + emitJP (IC_TRUE (ifx), 0.2f); + } + } + else + genSub (ic, result->aop, left->aop, right->aop); + + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genMult - generates code for multiplication */ +/*-----------------------------------------------------------------*/ +static void +genMultLit (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + /* Swap left and right such that right is a literal */ + if (left->aop->type == AOP_LIT) + { + operand *t = right; + right = left; + left = t; + } + + wassert (result->aop && result->aop->size == 1 && right->aop && right->aop->type == AOP_LIT); + + cheapMove (ASMOP_A, 0, left->aop, 0, true, true); + + asmop *add_aop; + if (aopInReg (left->aop, 0, P_IDX) || left->aop->type == AOP_DIR || left->aop->type == AOP_IMMD) + add_aop = left->aop; + else + { + add_aop = ASMOP_P; + cheapMove (ASMOP_P, 0, left->aop, 0, false, true); + } + + unsigned long long add, sub; + int topbit, nonzero; + + value *bval = valueFromLit (ulFromVal (right->aop->aopu.aop_lit) & 0xff); + wassert (!csdOfVal (&topbit, &nonzero, &add, &sub, bval)); + Safe_free (bval); + + // If the leading digits of the cse are 1 0 -1 we can use 0 1 1 instead to reduce the number of shifts. + if (topbit >= 2 && (add & (1ull << topbit)) && (sub & (1ull << (topbit - 2)))) + { + add = (add & ~(1u << topbit)) | (3u << (topbit - 2)); + sub &= ~(1u << (topbit - 1)); + topbit--; + } + + for (int bit = topbit - 1; bit >= 0; bit--) + { + emit2 ("sl", "a"); + cost (1, 1); + if ((add | sub) & (1ull << bit)) + { + emit2 (add & (1ull << bit) ? "add" : "sub" , "a, %s", aopGet (add_aop, 0)); + cost (1, 1); + } + } + + cheapMove (result->aop, 0, ASMOP_A, 0, true, true); +} + +/*-----------------------------------------------------------------*/ +/* genMult - generates code for multiplication */ +/*-----------------------------------------------------------------*/ +static void +genMult (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + D (emit2 ("; genMult", "")); + + aopOp (left, ic); + aopOp (right, ic); + aopOp (result, ic); + + if (left->aop->size >= 2 || right->aop->size >= 2 || result->aop->size > 2) + wassertl (0, "Wide multiplication is to be handled via support function calls."); + + /* Swap left and right such that right is a literal */ + if (left->aop->type == AOP_LIT) + { + operand *t = right; + right = left; + left = t; + } + + if (right->aop->type == AOP_LIT && result->aop->size == 1) + { + genMultLit (ic); + goto release; + } + + wassert (0); + +release: + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genCmp :- greater or less than comparison */ +/*-----------------------------------------------------------------*/ +static void +genCmp (const iCode *ic, iCode *ifx) +{ + operand *left, *right, *result; + + D (emit2 ("; genCmp", "")); + + result = IC_RESULT (ic); + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + + aopOp (left, ic); + aopOp (right, ic); + aopOp (result, ic); + + int size = max (left->aop->size, right->aop->size); + bool sign = false; + if (IS_SPEC (operandType (left)) && IS_SPEC (operandType (right))) + sign = !(SPEC_USIGN (operandType (left)) | SPEC_USIGN (operandType (right))); + + // Non-destructive 1-byte unsigned comparison. + if (!sign && size == 1 && ifx && aopInReg (left->aop, 0, A_IDX)) + { + if (ic->op == '>' && right->aop->type == AOP_LIT) + { + wassert (!aopIsLitVal (right->aop, 0, 1, 0x00)); + + if (IC_TRUE (ifx)) + { + emit2 ("ceqsn", "a, #0x%02x", byteOfVal (right->aop->aopu.aop_lit, 0) + 1); + emit2 ("t1sn", "f, c"); + cost (2, 2.5); + } + else + { + emit2 ("ceqsn", "a, #0x%02x", byteOfVal (right->aop->aopu.aop_lit, 0) + 1); + emit2 ("nop", ""); + emit2 ("t0sn", "f, c"); + cost (3, 3.5); + } + emitJP (IC_FALSE (ifx) ? IC_FALSE (ifx) : IC_TRUE (ifx), 0.5f); + goto release; + } + else if (ic->op == '<' && aopInReg (left->aop, 0, A_IDX) && (right->aop->type == AOP_LIT || right->aop->type == AOP_DIR) && (IC_TRUE (ifx) || !regDead (A_IDX, ic))) + { + if (IC_FALSE (ifx)) + { + emit2 ("ceqsn", "a, %s", aopGet (right->aop, 0)); + emit2 ("t1sn", "f, c"); + cost (2, 2.5); + } + else + { + emit2 ("ceqsn", "a, %s", aopGet (right->aop, 0)); + emit2 ("nop", ""); + emit2 ("t0sn", "f, c"); + cost (3, 3.5); + } + emitJP (IC_FALSE (ifx) ? IC_FALSE (ifx) : IC_TRUE (ifx), 0.5f); + goto release; + } + } + + if (ic->op == '>') + { + operand *t = right; + right = left; + left = t; + } + + bool started = false; + for (int i = 0; i < size; i++) + { + if (!started && sign && aopIsLitVal (right->aop, i, 1, 0x00) && i + 1 == size) + { + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + if (ifx) + { + if (IC_FALSE (ifx)) + { + if (aopInReg (left->aop, i, A_IDX) && !regDead (A_IDX, ic)) + { + emit2 ("ceqsn", "a, #0x80"); + emit2 ("nop", ""); + cost (2, 2); + } + else + { + emit2 ("sub", "a, #0x80"); + cost (1, 1); + } + emit2 ("t0sn", "f, c"); + cost (1, 1.5); + } + else + { + emit2 ("ceqsn", "a, #0x80"); + emit2 ("t1sn", "f, c"); + cost (2, 2.5); + } + + emitJP (IC_FALSE (ifx) ? IC_FALSE (ifx) : IC_TRUE (ifx), 0.5f); + } + else + { + emit2 ("sl", "a"); + emit2 ("mov", "a, #0x00"); + emit2 ("slc", "a"); + cheapMove (result->aop, 0, ASMOP_A, 0, true, true); + } + goto release; + } + + if (!started && aopIsLitVal (right->aop, i, 1, 0x00) && i + 1 < size) + ; + else if (started && aopIsLitVal (right->aop, i, 1, 0x00)) + { + cheapMove (ASMOP_A, 0, left->aop, i, true, !i); + emit2 ("subc", "a"); + cost (1, 1); + } + else if (started && (right->aop->type == AOP_LIT && !aopIsLitVal (right->aop, i, 1, 0x00) || right->aop->type == AOP_IMMD)) // Work around lack of subc a, #nn. + { + cheapMove (ASMOP_P, 0, right->aop, i, true, !i); + cheapMove (ASMOP_A, 0, left->aop, i, true, !i); + emit2 ("subc", "a, p"); + cost (1, 1); + } + else if (right->aop->type == AOP_STK) + { + cheapMove (ASMOP_A, 0, left->aop, i, true, !i); + cheapMove (ASMOP_P, 0, right->aop, i, false, !i); + emit2 (started ? "subc" : "sub", "a, p"); + cost (1, 1); + started = true; + } + else + { + if (aopInReg (right->aop, i, A_IDX)) + { + cost (100, 100); + wassert (regalloc_dry_run); + } + cheapMove (ASMOP_A, 0, left->aop, i, true, !i); + emit2 (started ? "subc" : "sub", "a, %s", aopGet (right->aop, i)); + cost (1, 1); + started = true; + } + } + + if (sign) + { + emit2 ("t0sn", "f, ov"); + emit2 ("xor", "a, #0x80"); + emit2 ("sl", "a"); + cost (3, 3); + } + + if (ifx) + { + emit2 (IC_FALSE(ifx) ? "t1sn" : "t0sn", "f, c"); + cost (1, 1.5); + emitJP (IC_FALSE (ifx) ? IC_FALSE (ifx) : IC_TRUE (ifx), 0.5f); + } + else + { + emit2 ("mov", "a, #0x00"); + emit2 ("slc", "a"); + cost (2, 2); + cheapMove (result->aop, 0, ASMOP_A, 0, true, true); + } + +release: + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genCmpEQorNE - equal or not equal comparison */ +/*-----------------------------------------------------------------*/ +static void +genCmpEQorNE (const iCode *ic, iCode *ifx) +{ + operand *left, *right, *result; + + D (emit2 ("; genCmpEQorNE", "")); + + result = IC_RESULT (ic); + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + + aopOp (left, ic); + aopOp (right, ic); + aopOp (result, ic); + + symbol *lbl_ne = 0; + symbol *endlbl = 0; + + if (ifx) + { + if ((ic->op == EQ_OP) ^ (bool)(IC_FALSE (ifx))) + lbl_ne = regalloc_dry_run ? 0 : newiTempLabel (NULL); + else + lbl_ne = IC_FALSE (ifx) ? IC_FALSE (ifx) : IC_TRUE (ifx); + } + else if (!regalloc_dry_run) + { + lbl_ne = newiTempLabel (NULL); + endlbl = newiTempLabel (NULL); + } + + int size = max (left->aop->size, right->aop->size); + + if (left->aop->type == AOP_LIT || aopInReg (right->aop, 0, A_IDX) || aopInReg (right->aop, 1, A_IDX) || + !aopInReg (left->aop, 0, A_IDX) && right->aop->type == AOP_CODE) + { + operand *temp = left; + left = right; + right = temp; + } + + if (aopInReg (left->aop, 1, A_IDX) && (right->aop->type == AOP_LIT || right->aop->type == AOP_DIR || right->aop->type == AOP_IMMD)) + { + wassert (regDead (A_IDX, ic)); + + emit2 ("ceqsn", "a, %s", aopGet (right->aop, 1)); + cost (1, 1); + emitJP (lbl_ne, 0.0f); + cheapMove (ASMOP_A, 0, left->aop, 0, true, true); + if (ifx && ((ic->op == EQ_OP) ^ (bool)(IC_FALSE(ifx)))) + { + if (TARGET_IS_PDK13) // pdk13 does not have cneqsn + { + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + emit2 ("ceqsn", "a, %s", aopGet (right->aop, 0)); + emitJP (tlbl, 0.0f); + cost (2, 3); + emitJP (IC_FALSE (ifx) ? IC_FALSE (ifx) : IC_TRUE (ifx), 0.0f); + emitLabel (tlbl); + } + else + { + emit2 ("cneqsn", "a, %s", aopGet (right->aop, 0)); + cost (1, 1); + emitJP (IC_FALSE (ifx) ? IC_FALSE (ifx) : IC_TRUE (ifx), 0.0f); + } + } + else + { + emit2 ("ceqsn", "a, %s", aopGet (right->aop, 0)); + cost (1, 1); + emitJP (lbl_ne, 0.0f); + } + } + else + for (int i = 0; i < size; i++) + { + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + + if (right->aop->type == AOP_STK || right->aop->type == AOP_CODE) + { + if (!regDead (P_IDX, ic) || i + 1 < size && aopInReg(left->aop, i + 1, P_IDX)) + { + cost (1000, 1000); + wassert (regalloc_dry_run); + } + cheapMove (ASMOP_P, 0, right->aop, i, false, true); + } + + if (ifx && i + 1 == size && ((ic->op == EQ_OP) ^ (bool)(IC_FALSE(ifx)))) + { + if (TARGET_IS_PDK13) + { + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + emit2 ("ceqsn", "a, %s", (right->aop->type == AOP_STK || right->aop->type == AOP_CODE) ? "p" : aopGet (right->aop, i)); + emitJP (tlbl, 0.0f); + cost (2, 3); + emitJP (IC_FALSE (ifx) ? IC_FALSE (ifx) : IC_TRUE (ifx), 0.0f); + emitLabel (tlbl); + } + else + { + emit2 ("cneqsn", "a, %s", (right->aop->type == AOP_STK || right->aop->type == AOP_CODE) ? "p" : aopGet (right->aop, i)); + cost (1, 1); + emitJP (IC_FALSE (ifx) ? IC_FALSE (ifx) : IC_TRUE (ifx), 0.0f); + } + } + else + { + emit2 ("ceqsn", "a, %s", (right->aop->type == AOP_STK || right->aop->type == AOP_CODE) ? "p" : aopGet (right->aop, i)); + cost (1, 1); + emitJP(lbl_ne, 0.0f); + } + } + + if (ifx) // Jump condition only. + { + if ((ic->op == EQ_OP) ^ (bool)(IC_FALSE(ifx))) + emitLabel (lbl_ne); + } + else // Needs result + { + if (!regDead (A_IDX, ic)) + pushAF(); + cheapMove (result->aop, 0, ic->op == EQ_OP ? ASMOP_ONE : ASMOP_ZERO, 0, true, true); + emitJP(endlbl, 0.0f); + emitLabel (lbl_ne); + if (!regDead (A_IDX, ic)) + { + emit2 ("push", "af"); + cost (1, 1); + } + cheapMove (result->aop, 0, ic->op == NE_OP ? ASMOP_ONE : ASMOP_ZERO, 0, true, true); + emitLabel (endlbl); + if (!regDead (A_IDX, ic)) + popAF(); + } + + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +static void +genXorByte (const asmop *result_aop, const asmop *left_aop, const asmop *right_aop, int i, bool *pushed_a, bool a_dead, bool p_dead) +{ + if ((aopInReg (left_aop, i, A_IDX) || aopInReg (left_aop, i, P_IDX) || left_aop->type == AOP_DIR) && + aopIsLitVal (right_aop, i, 1, 0xff) && aopSame (result_aop, i, left_aop, i, 1)) + { + emit2 ("not", "%s", aopGet (left_aop, i)); + cost (1, 1); + } + else if (aopIsLitVal (right_aop, i, 1, 0x00)) + { + cheapMove (result_aop, i, left_aop, i, a_dead, true); + } + else + { + if (!a_dead && !*pushed_a) + { + pushAF(); + *pushed_a = true; + } + + if ((left_aop->type == AOP_DIR || aopInReg (left_aop, i, P_IDX)) && aopSame (left_aop, i, result_aop, i, 1)) + { + cheapMove (ASMOP_A, 0, right_aop, i, true, true); + emit2 ("xor", "%s, a", aopGet (left_aop, i)); + cost (1, 1); + } + else if ((right_aop->type == AOP_DIR || aopInReg (right_aop, i, P_IDX)) && aopSame (right_aop, i, result_aop, i, 1)) + { + cheapMove (ASMOP_A, 0, left_aop, i, true, true); + emit2 ("xor", "%s, a", aopGet (right_aop, i)); + cost (1, 1); + } + else if (right_aop->type == AOP_STK) + { + if (!p_dead || aopInReg (left_aop, i, P_IDX)) + { + cost (100, 100); + wassert (regalloc_dry_run); + } + cheapMove (ASMOP_A, 0, left_aop, i, true, true); + cheapMove (ASMOP_P, 0, right_aop, i, false, true); + emit2 ("xor", "a, p"); + cost (1, 1); + cheapMove (result_aop, i, ASMOP_A, 0, true, true); + } + else + { + cheapMove (ASMOP_A, 0, left_aop, i, true, true); + emit2 ("xor", "a, %s", aopGet (right_aop, i)); + cost (1, 1); + cheapMove (result_aop, i, ASMOP_A, 0, true, true); + } + } +} + +/*-----------------------------------------------------------------*/ +/* genXor - code for or */ +/*-----------------------------------------------------------------*/ +static void +genXor (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + D (emit2 ("; genXor", "")); + + aopOp (left, ic); + aopOp (right, ic); + aopOp (result, ic); + + int size = result->aop->size; + int skip_byte = -1; + + /* Swap if left is literal or right is in A. */ + if (left->aop->type == AOP_LIT || aopInReg (right->aop, 0, A_IDX) || aopInReg (right->aop, 1, A_IDX) || right->aop->type == AOP_STK) + { + operand *t = right; + right = left; + left = t; + } + + bool a_free = regDead (A_IDX, ic); + bool pushed_a = false; + + for (int i = 0; i < size; i++) + if (aopInReg (left->aop, i, A_IDX)) + { + genXorByte (result->aop, left->aop, right->aop, i, &pushed_a, a_free, regDead (P_IDX, ic)); + skip_byte = i; + + if (aopInReg (result->aop, i, A_IDX)) + a_free = false; + } + + for (int i = 0; i < size; i++) + { + if (i == skip_byte) + continue; + + genXorByte (result->aop, left->aop, right->aop, i, &pushed_a, a_free, regDead (P_IDX, ic)); + + if (aopInReg (result->aop, i, A_IDX)) + a_free = false; + } + + if (pushed_a) + popAF(); + + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genOr - code for or */ +/*-----------------------------------------------------------------*/ +static void +genOr (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + D (emit2 ("; genOr", "")); + + aopOp (left, ic); + aopOp (right, ic); + aopOp (result, ic); + + int size = result->aop->size; + + /* Swap if left is literal or right is in A. */ + if (left->aop->type == AOP_LIT || aopInReg (right->aop, 0, A_IDX) || right->aop->type == AOP_STK) + { + operand *t = right; + right = left; + left = t; + } + + for (int i = 0; i < size; i++) + { + int bit = right->aop->type == AOP_LIT ? isLiteralBit (byteOfVal (right->aop->aopu.aop_lit, i)) : -1; + + if (aopIsLitVal (right->aop, i, 1, 0x00)) + { + cheapMove (result->aop, i, left->aop, i, true, true); + } + else if ((left->aop->type == AOP_SFR || aopInReg (left->aop, i, P_IDX) && !TARGET_IS_PDK13 /* set1 has weird encoding on pdk13, and is not yet supported by the assembler */) && aopSame (left->aop, i, result->aop, i, 1) && bit >= 0) + { + emit2 ("set1", "%s, #%d", aopGet (left->aop, i), bit); + cost (1, 1); + } + else if ((left->aop->type == AOP_DIR || aopInReg (left->aop, i, P_IDX) && right->aop->type != AOP_STK) && aopSame (left->aop, i, result->aop, i, 1)) + { + cheapMove (ASMOP_A, 0, right->aop, i, true, true); + emit2 ("or", "%s, a", aopGet (left->aop, i)); + cost (1, 1); + } + else if ((right->aop->type == AOP_DIR || aopInReg (right->aop, i, P_IDX) && left->aop->type != AOP_STK) && aopSame (right->aop, i, result->aop, i, 1)) + { + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + emit2 ("or", "%s, a", aopGet (right->aop, i)); + cost (1, 1); + } + else if (right->aop->type == AOP_STK) + { + if (!regDead (P_IDX, ic) || aopInReg (left->aop, i, P_IDX)) + { + cost (100, 100); + wassert (regalloc_dry_run); + } + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + cheapMove (ASMOP_P, 0, right->aop, i, false, true); + emit2 ("or", "a, p"); + cost (1, 1); + cheapMove (result->aop, i, ASMOP_A, 0, true, true); + } + else + { + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + emit2 ("or", "a, %s", aopGet (right->aop, i)); + cost (1, 1); + cheapMove (result->aop, i, ASMOP_A, 0, true, true); + } + } + + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genAnd - code for and */ +/*-----------------------------------------------------------------*/ +static void +genAnd (const iCode *ic, iCode *ifx) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + D (emit2 ("; genAnd", "")); + + aopOp (left, ic); + aopOp (right, ic); + aopOp (result, ic); + + int size = result->aop->size; + + /* Swap if left is literal or right is in A. */ + if (left->aop->type == AOP_LIT || aopInReg (right->aop, 0, A_IDX) || right->aop->type == AOP_STK) + { + operand *t = right; + right = left; + left = t; + } + + if (ifx && IC_FALSE (ifx) && result->aop->type == AOP_CND) + { + int i, j, nonzero; + + // Find the non-zero byte. + if (right->aop->type != AOP_LIT) + { + wassert (right->aop->size == 1); + i = 0; + nonzero = 1; + } + else + for (j = 0, nonzero = 0, i = 0; j < left->aop->size; j++) + if (byteOfVal (right->aop->aopu.aop_lit, j)) + { + i = j; + nonzero++; + } + + wassertl (nonzero <= 1, "Code generation for jump on bitwise and can handle at most one nonzero byte"); + + int bit = right->aop->type == AOP_LIT ? isLiteralBit (byteOfVal (right->aop->aopu.aop_lit, i)) : - 1; + + if (aopInReg (left->aop, i, P_IDX) && bit >= 0) + { + emit2 (IC_FALSE (ifx) ? "t1sn" : "t0sn", "p, #%d", bit); + cost (1, 1.5); + } + else if (aopInReg (left->aop, i, P_IDX) && regDead (P_IDX, ic) && + (byteOfVal (right->aop->aopu.aop_lit, i) == 0x7f || byteOfVal (right->aop->aopu.aop_lit, i) == 0xfe)) + { + emit2 (byteOfVal (right->aop->aopu.aop_lit, 0) == 0x7f ? "sl" : "sr", "p"); + emit2 (IC_FALSE (ifx) ? "t0sn" : "t1sn", "f, z"); + cost (2, 2.5); + } + else + { + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + if (!aopIsLitVal (right->aop, i, 1, 0xff)) + { + emit2 ("and", "a, %s", aopGet (right->aop, i)); + cost (1, 1); + } + if (TARGET_IS_PDK13 && IC_FALSE (ic)) // pdk13 does not have cneqsn + { + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (0)); + emit2 ("ceqsn", "a, #0x00"); + cost (1, 1.5); + emitJP (tlbl, 0.5f); + emitJP (IC_FALSE (ifx), 0.5f); + emitLabel (tlbl); + goto release; + } + else + { + emit2 (IC_FALSE (ifx) ? "cneqsn" : "ceqsn", "a, #0x00"); + cost (1, 1.5); + } + } + + emitJP (IC_FALSE (ifx) ? IC_FALSE (ifx) : IC_TRUE (ifx), 0.5f); + + goto release; + } + else if (ifx && IC_TRUE (ifx) && result->aop->type == AOP_CND) + { + for (int i = 0; i < right->aop->size; i++) + { + int bit = right->aop->type == AOP_LIT ? isLiteralBit (byteOfVal (right->aop->aopu.aop_lit, i)) : - 1; + + if (aopIsLitVal (right->aop, i, 1, 0x00)) + continue; + else if (aopInReg (left->aop, i, P_IDX) && bit >= 0) + { + emit2 ("t0sn", "p, #%d", bit); + cost (1, 1.5); + } + else if (aopInReg (left->aop, i, P_IDX) && regDead (P_IDX, ic) && + (byteOfVal (right->aop->aopu.aop_lit, i) == 0x7f || byteOfVal (right->aop->aopu.aop_lit, i) == 0xfe)) + { + emit2 (byteOfVal (right->aop->aopu.aop_lit, 0) == 0x7f ? "sl" : "sr", "p"); + emit2 ("t1sn", "f, z"); + cost (2, 2.5); + } + else + { + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + if (!aopIsLitVal (right->aop, i, 1, 0xff)) + { + emit2 ("and", "a, %s", aopGet (right->aop, i)); + cost (1, 1); + } + emit2 ("ceqsn", "a, #0x00"); + cost (1, 1.5); + } + + emitJP (IC_TRUE (ifx), 0.5f); + } + + goto release; + } + + for (int i = 0; i < size; i++) + { + if (aopInReg (right->aop, i, A_IDX)) + { + operand *t = right; + right = left; + left = t; + } + + int bit = right->aop->type == AOP_LIT ? isLiteralBit (~byteOfVal (right->aop->aopu.aop_lit, i) & 0xff) : -1; + + if (aopIsLitVal (right->aop, i, 1, 0xff)) + cheapMove (result->aop, i, left->aop, i, true, true); + else if (aopIsLitVal (right->aop, i, 1, 0x00)) + cheapMove (result->aop, i, ASMOP_ZERO, 0, true, true); + else if ((left->aop->type == AOP_SFR || aopInReg (left->aop, i, P_IDX) /* set1 has weird encoding on pdk13, and is not yet supported by the assembler */) && aopSame (left->aop, i, result->aop, i, 1) && bit >= 0) + { + emit2 ("set0", "%s, #%d", aopGet (left->aop, i), bit); + cost (1, 1); + } + else if ((left->aop->type == AOP_DIR || aopInReg (left->aop, i, P_IDX) && right->aop->type != AOP_STK) && aopSame (left->aop, i, result->aop, i, 1)) + { + cheapMove (ASMOP_A, 0, right->aop, i, true, true); + emit2 ("and", "%s, a", aopGet (left->aop, i)); + cost (1, 1); + } + else if ((right->aop->type == AOP_DIR || aopInReg (right->aop, i, P_IDX) && left->aop->type != AOP_STK) && aopSame (right->aop, i, result->aop, i, 1)) + { + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + emit2 ("and", "%s, a", aopGet (right->aop, i)); + cost (1, 1); + } + else if (right->aop->type == AOP_STK) + { + if (!regDead (P_IDX, ic) || aopInReg (left->aop, i, P_IDX)) + { + cost (100, 100); + wassert (regalloc_dry_run); + } + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + cheapMove (ASMOP_P, 0, right->aop, i, false, true); + emit2 ("and", "a, p"); + cost (1, 1); + cheapMove (result->aop, i, ASMOP_A, 0, true, true); + } + else + { + if (left->aop->type == AOP_STK && (!regDead (P_IDX, ic) || aopInReg (right->aop, i, P_IDX))) + { + cost (100, 100); + wassert (regalloc_dry_run); + } + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + emit2 ("and", "a, %s", aopGet (right->aop, i)); + cost (1, 1); + cheapMove (result->aop, i, ASMOP_A, 0, true, true); + } + } + +release: + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genLeftShift - generates code for right shifting */ +/*-----------------------------------------------------------------*/ +static void +genLeftShift (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + D (emit2 ("; genLeftShift", "")); + + aopOp (left, ic); + aopOp (right, ic); + aopOp (result, ic); + + int size = result->aop->size; + + if (right->aop->type == AOP_LIT) + { + int shCount = ulFromVal (right->aop->aopu.aop_lit); + int offset = shCount / 8; + + genMove_o (result->aop, offset, left->aop, 0, result->aop->size - shCount / 8, regDead (A_IDX, ic)); + genMove_o (result->aop, 0, ASMOP_ZERO, 0, offset, regDead (A_IDX, ic)); + shCount %= 8; + + if (!shCount) + goto release; + + bool loop = (shCount > 2 + ((size - offset) <= 1) * 2 + optimize.codeSpeed) && !(size == 1 && aopInReg (result->aop, 0, A_IDX)) && + regDead (A_IDX, ic) && !aopInReg (result->aop, 0, A_IDX) && !aopInReg (result->aop, 1, A_IDX); + symbol *tlbl = (!loop || regalloc_dry_run) ? 0 : newiTempLabel (0); + + if (loop) + { + emit2 ("mov", "a, #%d", shCount); + cost (1, 1); + emitLabel (tlbl); + regalloc_dry_run_cycle_scale = shCount; + shCount = 1; + if (result->aop->type == AOP_STK) + pushAF(); + } + + while (shCount) + { + if (shCount == 7 && (size - offset) == 1 && (result->aop->type == AOP_REG || result->aop->type == AOP_DIR)) + { + emit2 ("sr", "%s", aopGet (result->aop, offset)); + if (aopInReg (result->aop, 0, A_IDX)) + emit2("mov", "a, #0x00"); + else + emit2 ("clear", "%s", aopGet (result->aop, offset)); + emit2 ("src", "%s", aopGet (result->aop, offset)); + shCount = 0; + continue; + } + else if (shCount >= 4 && (size - offset) == 1 && aopInReg (result->aop, offset, A_IDX)) + { + emit2 ("swap", "a"); + emit2 ("and", "a, #0xf0"); + cost (2, 2); + shCount -= 4; + continue; + } + + for (int i = offset; i < size; i++) + { + if (result->aop->type == AOP_STK) + { + cheapMove (ASMOP_A, 0, result->aop, i, true, i <= offset); + emit2((i > offset) ? "slc" : "sl", "a"); + cost (1, 1); + cheapMove (result->aop, i, ASMOP_A, 0, true, i + 1 != size); + } + else + { + emit2((i > offset) ? "slc" : "sl", "%s", aopGet (result->aop, i)); + cost (1, 1); + } + } + shCount--; + } + + if (loop) + { + if (result->aop->type == AOP_STK) + popAF(); + emit2 ("dzsn", "a"); + if (!regalloc_dry_run) + emit2 ("goto", "!tlabel", labelKey2num (tlbl->key)); + cost (2, 2); + } + + regalloc_dry_run_cycle_scale = 1; + } + else + { + genMove (result->aop, left->aop, !aopInReg (right->aop, 0, A_IDX)); + + symbol *tlbl1 = regalloc_dry_run ? 0 : newiTempLabel (0); + symbol *tlbl2 = regalloc_dry_run ? 0 : newiTempLabel (0); + + cheapMove (ASMOP_A, 0, right->aop, 0, true, true); + emitLabel (tlbl1); + emit2 ("sub", "a, #1"); + emit2 ("t0sn", "f, c"); + if (!regalloc_dry_run) + emit2 ("goto", "!tlabel", labelKey2num (tlbl2->key)); + cost (3, 3); + + for(int i = 0; i < size; i++) + { + emit2(i ? "slc" : "sl", "%s", aopGet (result->aop, i)); + cost (1, 1); + } + + if (!regalloc_dry_run) + emit2 ("goto", "!tlabel", labelKey2num (tlbl1->key)); + cost (1, 1); + + emitLabel (tlbl2); + } + +release: + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genRightShift - generates code for right shifting */ +/*-----------------------------------------------------------------*/ +static void +genRightShift (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + D (emit2 ("; genRightShift", "")); + + aopOp (left, ic); + aopOp (right, ic); + aopOp (result, ic); + + bool pushed_a = false; + + int size = result->aop->size; + + if (right->aop->type == AOP_LIT) + { + int shCount = ulFromVal (right->aop->aopu.aop_lit); + + if (SPEC_USIGN (getSpec (operandType (left)))) + { + genMove_o (result->aop, 0, left->aop, shCount / 8, result->aop->size, regDead (A_IDX, ic)); + size -= shCount / 8; + shCount %= 8; + } + else + genMove (result->aop, left->aop, !aopInReg (right->aop, 0, A_IDX)); + + if (!shCount) + goto release; + + bool loop = (shCount > 2 + (size == 1) * 2 + optimize.codeSpeed) && !(size == 1 && aopInReg (result->aop, 0, A_IDX)) && + regDead (A_IDX, ic) && !aopInReg (result->aop, 0, A_IDX) && !aopInReg (result->aop, 1, A_IDX); + + symbol *tlbl = (!loop || regalloc_dry_run) ? 0 : newiTempLabel (0); + + if (loop) + { + emit2 ("mov", "a, #%d", shCount); + cost (1, 1); + emitLabel (tlbl); + regalloc_dry_run_cycle_scale = shCount; + shCount = 1; + } + + while (shCount) + { + if (SPEC_USIGN (getSpec (operandType (left))) && shCount == 7 && size == 1 && (result->aop->type == AOP_REG || result->aop->type == AOP_DIR)) + { + emit2 ("sl", "%s", aopGet (result->aop, 0)); + if (aopInReg (result->aop, 0, A_IDX)) + emit2("mov", "a, #0x00"); + else + emit2 ("clear", "%s", aopGet (result->aop, 0)); + emit2 ("slc", "%s", aopGet (result->aop, 0)); + shCount = 0; + continue; + } + else if (SPEC_USIGN (getSpec (operandType (left))) && shCount >= 4 && size == 1 && aopInReg (result->aop, 0, A_IDX)) + { + emit2 ("swap", "a"); + emit2 ("and", "a, #0x0f"); + cost (2, 2); + shCount -= 4; + continue; + } + + if (!SPEC_USIGN (getSpec (operandType (left))) && (loop || !regDead (A_IDX, ic))) + { + pushAF(); + pushed_a = true; + } + + // Padauk has no arithmetic right shift instructions. + // So we need this emulation sequence here. + if (!SPEC_USIGN (getSpec (operandType (left)))) + { + if (aopInReg (result->aop, size - 1, A_IDX) && + regDead (P_IDX, ic) && (size == 1 || !aopInReg (result->aop, 0, P_IDX))) + { + cheapMove (ASMOP_P, 0, ASMOP_A, 0, true, true); + emit2 ("sl", "p"); + emit2 ("src", "a"); + cost (2, 2); + } + else if (aopInReg (result->aop, size - 1, A_IDX)) + { + emit2 ("sl", "a"); + emit2 ("t0sn", "f, c"); + emit2 ("or", "a, #0x01", aopGet (result->aop, size - 1)); + emit2 ("src", "a"); + emit2 ("src", "a"); + cost (5, 5); + } + else + { + if (size > 1 && aopInReg (result->aop, 0, A_IDX) || !regDead (A_IDX, ic)) + { + wassert (regalloc_dry_run); + cost (500, 500); + } + cheapMove (ASMOP_A, 0, result->aop, size - 1, true, true); + emit2 ("sl", "a"); + emit2 ("src", aopGet (result->aop, size - 1)); + cost (2, 2); + } + } + else + { + emit2("sr", aopGet (result->aop, size - 1)); + cost (1, 1); + } + + for(int i = size - 2; i >= 0; i--) + { + if (pushed_a && aopInReg (result->aop, i, A_IDX)) + { + wassert (regalloc_dry_run); + cost (500, 500); + } + emit2 ("src", "%s", aopGet (result->aop, i)); + cost (1, 1); + } + + shCount--; + } + + if (loop) + { + if (pushed_a) + { + popAF(); + pushed_a = false; + } + emit2 ("dzsn", "a"); + if (!regalloc_dry_run) + emit2 ("goto", "!tlabel", labelKey2num (tlbl->key)); + cost (2, 2); + } + + regalloc_dry_run_cycle_scale = 1; + } + else + { + genMove (result->aop, left->aop, !aopInReg (right->aop, 0, A_IDX)); + + symbol *tlbl1 = regalloc_dry_run ? 0 : newiTempLabel (0); + symbol *tlbl2 = regalloc_dry_run ? 0 : newiTempLabel (0); + + cheapMove (ASMOP_A, 0, right->aop, 0, true, true); + emitLabel (tlbl1); + + emit2 ("sub", "a, #1"); + emit2 ("t0sn", "f, c"); + if (!regalloc_dry_run) + emit2 ("goto", "!tlabel", labelKey2num (tlbl2->key)); + cost (3, 3); + + if (!SPEC_USIGN (getSpec (operandType (left)))) + { + pushAF(); + pushed_a = true; + } + + // Padauk has no arithmetic right shift instructions. + // So we need this emulation sequence here. + if (!SPEC_USIGN (getSpec (operandType (left)))) + { + emit2 ("mov", "a, #0x01"); + emit2 ("sl", aopGet (result->aop, size - 1)); + emit2 ("t0sn", "f, c"); + emit2 ("or", "%s, a", aopGet (result->aop, size - 1)); + emit2 ("src", aopGet (result->aop, size - 1)); + emit2 ("src", aopGet (result->aop, size - 1)); + cost (6, 6); + } + else + { + emit2("sr", aopGet (result->aop, size - 1)); + cost (1, 1); + } + + for(int i = size - 2; i >= 0; i--) + { + emit2 ("src", "%s", aopGet (result->aop, i)); + cost (1, 1); + } + + if (!SPEC_USIGN (getSpec (operandType (left)))) + { + popAF(); + pushed_a = false; + } + + if (!regalloc_dry_run) + emit2 ("goto", "!tlabel", labelKey2num (tlbl1->key)); + cost (1, 1); + + emitLabel (tlbl2); + } + + if (pushed_a) + popAF(); + +release: + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + + +/*-----------------------------------------------------------------*/ +/* getBitFieldByte - process partial byte of bit-field */ +/*-----------------------------------------------------------------*/ +static void getBitFieldByte (int len, int str, bool sex) +{ + wassert (len >= 0 && len < 8); + + bool mask = len + str != 8; + + // Shift + if (len == 1 && str == 7) + { + emit2 ("sl", "a"); + emit2 ("slc", "a"); + str = 0; + mask = true; + } + if (str >= 4) + { + emit2 ("swap", "a"); + cost (1, 1); + str -= 4; + mask = true; + } + while (str--) + { + emit2 ("sr", "a"); + cost (1, 1); + } + + // Mask + if (mask) + { + emit2 ("and", "a, #0x%02x", 0xff >> (8 - len)); + cost (2, 1); + } + + // Sign-extend + if (sex) + { + symbol *const tlbl = regalloc_dry_run ? 0 : newiTempLabel (0); + emit2 ("ceqsn", "a, #0x%02x", 0x80 >> (8 - len)); + emit2 ("nop", ""); + emit2 ("t0sn", "f, c"); + if (tlbl) + emit2 ("goto", "!tlabel", labelKey2num (tlbl->key)); + emit2 ("or", "a, #0x%02x", (0xff00 >> (8 - len)) & 0xff); + cost (5, 5); + emitLabel (tlbl); + } +} + +/*-----------------------------------------------------------------*/ +/* genPointerGet - generate code for pointer get */ +/*-----------------------------------------------------------------*/ +static void +genPointerGet (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + D (emit2 ("; genPointerGet", "")); + + aopOp (left, ic); + aopOp (right, ic); + aopOp (result, ic); + + wassertl (right, "GET_VALUE_AT_ADDRESS without right operand"); + wassertl (IS_OP_LITERAL (right), "GET_VALUE_AT_ADDRESS with non-literal right operand"); + + bool pushed_a = false; + + bool bit_field = IS_BITVAR (getSpec (operandType (result))); + int size = result->aop->size; + int blen, bstr; + blen = bit_field ? (SPEC_BLEN (getSpec (operandType (IS_BITVAR (getSpec (operandType (right))) ? right : left)))) : 0; + bstr = bit_field ? (SPEC_BSTR (getSpec (operandType (IS_BITVAR (getSpec (operandType (right))) ? right : left)))) : 0; + + sym_link *type = operandType (left); + int ptype = (IS_PTR (type) && !IS_FUNC (type->next)) ? DCL_TYPE (type) : PTR_TYPE (SPEC_OCLS (getSpec (type))); + + if (left->aop->type == AOP_IMMD && ptype == GPOINTER && IS_SYMOP (left) && OP_SYMBOL (left)->remat) + ptype = left->aop->aopu.code ? CPOINTER : POINTER; + + wassertl (aopIsLitVal (right->aop, 0, 2, 0x0000), "Unimplemented nonzero right operand in pointer read"); + + if (left->aop->type == AOP_IMMD && (ptype == POINTER || ptype == CPOINTER)) + { + for (int i = 0; !bit_field ? i < size : blen > 0; i++, blen -= 8) + { + if (!regDead (A_IDX, ic) && !pushed_a) + { + pushAF(); + pushed_a = true; + } + + if (ptype == POINTER) + { + emit2 ("mov", "a, %s+%d", left->aop->aopu.immd, left->aop->aopu.immd_off + i); + cost (1, 1); + } + else + { + emit2 ("call", "%s+%d", left->aop->aopu.immd, left->aop->aopu.immd_off + i); + cost (1, 4); + } + + if (bit_field && blen < 8) + getBitFieldByte (blen, bstr, !SPEC_USIGN (getSpec (operandType (result)))); + + if (aopInReg (result->aop, i, A_IDX) && (!bit_field ? i + 1 < size : blen - 8 > 0)) + { + wassert (!pushed_a); + pushAF(); + pushed_a = true; + } + else + cheapMove (result->aop, i, ASMOP_A, 0, true, true); + } + } +#if 0 // TODO: Implement alignment requirements - ldt16 needs 16-bit-aligned operand + else if (TARGET_IS_PDK15 && ptype == CPOINTER && left->aop->type == AOP_DIR && size == 1) // pdk15 has ldtabl for efficient read from code space via a 12-bit address (top nibble of 16-bit value is ignored). + { + emit2 ("ldtabl", "a, %s", aopGet (left->aop, 0)); + cost (1, 2); + + if (bit_field && blen < 8) + getBitFieldByte (blen, bstr, !SPEC_USIGN (getSpec (operandType (result)))); + + cheapMove (result->aop, 0, ASMOP_A, 0, true, true); + goto release; + } +#endif + else if (ptype == POINTER) // Try to use efficient idxm when we know the target is in RAM. + { + const asmop *ptr_aop = (left->aop->type == AOP_DIR && TARGET_IS_PDK16) ? left->aop : ASMOP_P; + + cheapMove (ptr_aop, 0, left->aop, 0, true, true); + + for (int i = 0; !bit_field ? i < size : blen > 0; i++, blen -= 8) + { + if (i != 0 && aopInReg (ptr_aop, 0, P_IDX) && aopInReg (result->aop, i - 1, P_IDX)) // Would have been overwritten on previous byte. + { + cost (500, 500); + wassert (regalloc_dry_run); + } + + emit2 ("idxm", "a, %s", aopGet (ptr_aop, 0)); + cost (1, 2); + + if (bit_field && blen < 8) + getBitFieldByte (blen, bstr, !SPEC_USIGN (getSpec (operandType (result)))); + + if (aopInReg (result->aop, i, A_IDX) && (!bit_field ? i + 1 < size : blen - 8 <= 0)) + { + pushAF(); + pushed_a = true; + } + else + cheapMove (result->aop, i, ASMOP_A, 0, true, true); + + if (i + 1 != size) + { + emit2 ("inc", "%s", aopGet (ptr_aop, 0)); + cost (1, 1); + } + } + if (ptr_aop == left->aop && !(aopInReg (left->aop, 0, P_IDX) && regDead (P_IDX, ic))) + for (int i = 1; i < size; i++) + { + emit2 ("dec", "%s", aopGet (ptr_aop, 0)); + cost (1, 1); + } + goto release; + } + else // Generic, but also inefficient. + { + if (!regDead (A_IDX, ic)) + { + pushAF(); + pushed_a = true; + } + + if (!bit_field && size == 2) + { + genMove (ASMOP_PA, left->aop, true); + emit2 ("call", "__gptrget2"); + cost (1, (ptype == CPOINTER) ? 32 : 13); + genMove (result->aop, ASMOP_AP, true); + goto release; + } + + for (int i = 0; !bit_field ? i < size : blen > 0; i++, blen -= 8) + { + if (i != 0 && (aopInReg (left->aop, 0, A_IDX) || aopInReg (left->aop, 1, A_IDX) || aopInReg (result->aop, i - 1, P_IDX))) // Would have been overwritten on previous byte. + { + cost (500, 500); + wassert (regalloc_dry_run); + } + + genMove (ASMOP_PA, left->aop, true); + if (i > 2) + { + emit2 ("xch", "a, p"); + emit2 ("add", "a, #%d", i); + emit2 ("xch", "a, p"); + emit2 ("addc", "a"); + cost (4, 4); + } + else + for (int j = 0; j < i; j++) + { + emit2 ("inc", "p"); + emit2 ("addc", "a"); + cost (2, 2); + } + + emit2 ("call", "__gptrget"); + cost (1, (ptype == CPOINTER) ? 16 : 8); + + if (bit_field && blen < 8) + getBitFieldByte (blen, bstr, !SPEC_USIGN (getSpec (operandType (result)))); + + if (aopInReg (result->aop, i, P_IDX) && (!bit_field ? i + 1 < size : blen - 8 <= 0)) + { + wassert (regalloc_dry_run); + cost (200, 200); + } + else if (aopInReg (result->aop, i, A_IDX) && (!bit_field ? i + 1 < size : blen - 8 <= 0)) + { + pushAF(); + pushed_a = true; + } + else + { + cheapMove (result->aop, i, ASMOP_A, 0, true, true); + } + } + goto release; + } + +release: + if (pushed_a) + popAF(); + + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genPointerSet - stores the value into a pointer location */ +/*-----------------------------------------------------------------*/ +static void +genPointerSet (iCode *ic) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + D (emit2 ("; genPointerSet", "")); + + aopOp (left, ic); + aopOp (right, ic); + + bool pushed_p = false; + + bool bit_field = IS_BITVAR (getSpec (operandType (right))) || IS_BITVAR (getSpec (operandType (left))); + int size = right->aop->size; + + int blen, bstr; + blen = bit_field ? (SPEC_BLEN (getSpec (operandType (IS_BITVAR (getSpec (operandType (right))) ? right : left)))) : 0; + bstr = bit_field ? (SPEC_BSTR (getSpec (operandType (IS_BITVAR (getSpec (operandType (right))) ? right : left)))) : 0; + +#if 0 // TODO: Implement alignment requirements - idxm needs 16-bit-aligned operand + if (left->aop->type == AOP_DIR && TARGET_IS_PDK16 && !bit_field) + { + for (int i = 0; i < size; i++) + { + cheapMove (ASMOP_A, 0, right->aop, i, true, true); + emit2 ("idxm", "%s, a", aopGet (left->aop, 0)); + cost (1, 2); + if (i + 1 != size) + { + emit2 ("inc", "%s", aopGet (left->aop, 0)); + cost (1, 1); + } + } + for (int i = 1; i < size; i++) + { + emit2 ("dec", "%s", aopGet (left->aop, 0)); + cost (1, 1); + } + } + else +#endif + if (right->aop->type == AOP_STK && !bit_field) + { + if (!regDead (P_IDX, ic)) + { + emit2 ("xch", "a, p"); + cost (1, 1); + pushAF (); + emit2 ("xch", "a, p"); + cost (1, 1); + pushed_p = true; + } + for (int i = 0; i < size; i++) + { + cheapMove (ASMOP_A, 0, right->aop, i, true, true); + cheapMove (ASMOP_P, 0, left->aop, 0, false, true); + if (i > 3) + { + emit2 ("xch", "a, p"); + emit2 ("add", "a, #%d", i); + emit2 ("xch", "a, p"); + cost (3, 3); + } + else + for (int j = 0; j < i; j++) + { + emit2 ("inc", "p"); + cost (1, 1); + } + emit2 ("idxm", "p, a"); + cost (1, 2); + } + } + else if (aopInReg (right->aop, 0, P_IDX) && aopInReg (right->aop, 1, A_IDX) && left->aop->type == AOP_IMMD) + { + emit2 ("mov", "%s+%d, a", left->aop->aopu.immd, left->aop->aopu.immd_off + 1); + cost (1, 1); + if (regDead (A_IDX, ic)) + { + emit2 ("mov", "a, p"); + emit2 ("mov", "%s+%d, a", left->aop->aopu.immd, left->aop->aopu.immd_off); + cost (2, 2); + } + else + { + emit2 ("xch", "a, p"); + emit2 ("mov", "%s+%d, a", left->aop->aopu.immd, left->aop->aopu.immd_off); + emit2 ("xch", "a, p"); + cost (3, 3); + } + } + else + { + const asmop *ptr_aop; + bool swapped = false; + + if (left->aop->type == AOP_IMMD) + ptr_aop = 0; +#if 0 // TODO: Implement alignment requirements - idxm needs 16-bit-aligned operand + else if (left->aop->type == AOP_DIR && size == 1 || aopInReg (left->aop, 0, P_IDX)) + ptr_aop = left->aop; +#endif + else if (aopInReg (right->aop, 0, P_IDX) && regDead (P_IDX, ic)) + { + if (left->aop->type == AOP_STK) + { + cost (250, 250); + wassert (regalloc_dry_run); + } + ptr_aop = ASMOP_P; + cheapMove (ASMOP_A, 0, left->aop, 0, true, true); + emit2 ("xch", "a, p"); + cost (1, 1); + G.p.type = AOP_INVALID; + swapped = true; + } + else + { + ptr_aop = ASMOP_P; + cheapMove (ptr_aop, 0, left->aop, 0, !aopInReg (right->aop, 0, A_IDX), true); + G.p.type = AOP_INVALID; + if (!regDead (P_IDX, ic) || aopInReg (right->aop, 0, P_IDX)) + { + wassert (regalloc_dry_run); + cost (1000, 1000); + } + } + + for (int i = 0; !bit_field ? i < size : blen > 0; i++, blen -= 8) + { + if (!ptr_aop && aopIsLitVal (right->aop, i, 1, 0) && !(bit_field || blen >= 8)) + { + emit2 ("clear", "%s+%d", left->aop->aopu.immd, left->aop->aopu.immd_off + i); + cost (1, 1); + continue; + } + + if (bit_field && blen < 8) + { + if (right->aop->type == AOP_LIT) + { + unsigned char mval = ~((0xff >> (8 - blen)) << bstr) & 0xff; + unsigned char bval = (byteOfVal (right->aop->aopu.aop_lit, i) << bstr) & ((0xff >> (8 - blen)) << bstr); + + if (!ptr_aop && (byteOfVal (right->aop->aopu.aop_lit, i) << bstr) == ((0xff >> (8 - blen)) << bstr)) + { + emit2 ("mov", "a, #0x%02x", bval); + emit2 ("or", "%s+%d, a", left->aop->aopu.immd, left->aop->aopu.immd_off + i); + cost (2, 2); + continue; + } + else if (!ptr_aop && !bval) + { + emit2 ("mov", "a, #0x%02x", mval); + emit2 ("and", "%s+%d, a", left->aop->aopu.immd, left->aop->aopu.immd_off + i); + cost (2, 2); + continue; + } + + if (!ptr_aop) + { + emit2 ("mov", "a, %s+%d", left->aop->aopu.immd, left->aop->aopu.immd_off + i); + cost (1, 1); + } + else + { + emit2 ("idxm", "a, %s", aopGet (ptr_aop, 0)); + cost (1, 2); + } + + if ((byteOfVal (right->aop->aopu.aop_lit, i) << bstr) != ((0xff >> (8 - blen)) << bstr)) + { + emit2 ("and", "a, #0x%02x", mval); + cost (1, 1); + } + + if (bval) + { + emit2 ("or", "a, #0x%02x", bval); + cost (1, 1); + } + } + else if (bit_field && blen == 1) + { + if (!regDead (A_IDX, ic)) + { + cost (200, 200); + wassert (regalloc_dry_run); + } + + cheapMove (ASMOP_A, 0, right->aop, i, true, true); + emit2 ("sr", "a"); + cost (1, 1); + if (!ptr_aop) + { + emit2 ("mov", "a, %s+%d", left->aop->aopu.immd, left->aop->aopu.immd_off + i); + cost (1, 1); + } + else + { + emit2 ("idxm", "a, %s", aopGet (ptr_aop, 0)); + cost (1, 2); + } + emit2 ("and", "a, #0x%02x", ~((0xff >> (8 - blen)) << bstr) & 0xff); + emit2 ("t0sn", "f, c"); + emit2 ("or", "a, #0x%02x", 1 << bstr); + cost (3, 3); + } + else + { + if (aopInReg (right->aop, i, A_IDX) || !regDead (A_IDX, ic)) + { + cost (100, 100); + wassert (regalloc_dry_run); + } + if (!ptr_aop) + { + emit2 ("mov", "a, %s+%d", left->aop->aopu.immd, left->aop->aopu.immd_off + i); + cost (1, 1); + } + else + { + emit2 ("idxm", "a, %s", aopGet (ptr_aop, 0)); + cost (1, 2); + } + emit2 ("and", "a, #0x%02x", ~((0xff >> (8 - blen)) << bstr) & 0xff); + cost (1, 1); + + emit2 ("xch", "a, p"); + cost (1, 1); + if (!regDead (P_IDX, ic) && !pushed_p) + { + pushAF (); + pushed_p = true; + } + if (ptr_aop && aopInReg (ptr_aop, 0, P_IDX)) + pushAF (); + if (!aopInReg (right->aop, i, P_IDX)) // xch above would already have brought it into a. + cheapMove (ASMOP_A, 0, right->aop, i, true, true); + if (bstr >= 4) + { + emit2 ("swap", "a"); + cost (1, 1); + } + for (int j = (bstr >= 4 ? 4 : 0); j < bstr; j++) + { + emit2 ("sl", "a"); + cost (1, 1); + } + emit2 ("and", "a, #0x%02x", (0xff >> (8 - blen)) << bstr); + emit2 ("or", "a, p"); + cost (2, 2); + + if (ptr_aop && aopInReg (ptr_aop, 0, P_IDX)) + { + emit2 ("mov", "p, a"); + cost (1, 1); + popAF (); + emit2 ("xch", "a, p"); + cost (1, 1); + } + } + } + else if (!swapped) + { + if (!aopInReg (right->aop, i, A_IDX) && !regDead (A_IDX, ic)) + { + cost (100, 100); + wassert (regalloc_dry_run); + } + cheapMove (ASMOP_A, 0, right->aop, i, true, true); + } + + if (!ptr_aop) + { + emit2 ("mov", "%s+%d, a", left->aop->aopu.immd, left->aop->aopu.immd_off + i); + cost (1, 1); + } + else + { + emit2 ("idxm", "%s, a", aopGet (ptr_aop, 0)); + cost (1, 2); + } + + if (i + 1 != size && ptr_aop) + { + emit2 ("inc", "%s", aopGet (ptr_aop, 0)); + cost (1, 1); + } + } + } + + if (pushed_p) + { + emit2 ("xch", "a, p"); + cost (1, 1); + popAF (); + emit2 ("xch", "a, p"); + cost (1, 1); + } + + freeAsmop (right); + freeAsmop (left); +} + +/*-----------------------------------------------------------------*/ +/* genAssign - generate code for assignment */ +/*-----------------------------------------------------------------*/ +static void +genAssign (const iCode *ic) +{ + operand *result, *right; + + D (emit2 ("; genAssign", "")); + + result = IC_RESULT (ic); + right = IC_RIGHT (ic); + + aopOp (right, ic); + aopOp (result, ic); + + wassert (result->aop->type != AOP_DUMMY || right->aop->type != AOP_DUMMY); + + if ((result->aop->type == AOP_STK || right->aop->type == AOP_STK)&& !regDead (P_IDX, ic)) + { + cost (100, 100); // Todo: Implement! + wassert (regalloc_dry_run); + } + + if (right->aop->type == AOP_DUMMY) + { + int i; + D (emit2 ("; Dummy write", "")); + for (i = 0; i < result->aop->size; i++) + cheapMove (result->aop, i, ASMOP_A, 0, false, true); + } + else if (result->aop->type == AOP_DUMMY) + { + wassert (0); +#if 0 + int i; + D (emit2 ("; Dummy read", "")); + + if (!regDead(A_IDX, ic) && right->aop->type == AOP_DIR) + for (i = 0; i < right->aop->size; i++) + emit3_o (A_TNZ, right->aop, i, 0, 0); + else + { + if (!regDead(A_IDX, ic)) + push (ASMOP_A, 0, 1); + for (i = 0; i < right->aop->size; i++) + cheapMove (ASMOP_A, 0, right->aop, i, FALSE); + if (!regDead(A_IDX, ic)) + pop (ASMOP_A, 0, 1); + } +#endif + } + else + genMove(result->aop, right->aop, regDead (A_IDX, ic)); + + wassert (result->aop != right->aop); + + freeAsmop (right); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genIfx - generate code for Ifx statement */ +/*-----------------------------------------------------------------*/ +static void +genIfx (const iCode *ic) +{ + operand *const cond = IC_COND (ic); + + D (emit2 ("; genIfx", "")); + + aopOp (cond, ic); + + if ((aopInReg (cond->aop, 0, A_IDX) && aopInReg (cond->aop, 1, P_IDX) || + aopInReg (cond->aop, 0, P_IDX) && aopInReg (cond->aop, 1, A_IDX)) && + !regDead (A_IDX, ic)) + { + if (IC_TRUE (ic)) + { + emit2 ("ceqsn", "a, #0"); + cost (1, 1.5f); + emitJP (IC_TRUE (ic), 0.5f); + emit2 ("ceqsn", "a, p"); + cost (1, 0.75f); + emitJP (IC_TRUE (ic), 0.375f); + goto release; + } + else if (!TARGET_IS_PDK13) + { + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + emit2 ("ceqsn", "a, #0"); + if (!regalloc_dry_run) + emit2 ("goto", "#!tlabel", labelKey2num (tlbl->key)); + emit2 ("cneqsn", "a, p"); + cost (3, 3.25f); + emitJP (IC_FALSE (ic), 0.375f); + emitLabel (tlbl); + goto release; + } + } + + int skip_byte; + if (IS_FLOAT (operandType (cond))) // Clear sign bit for float. + { + cheapMove (ASMOP_A, 0, cond->aop, cond->aop->size - 1, true, true); + emit2 ("and", "a, #0x7f"); + cost (1, 1); + skip_byte = cond->aop->size - 1; + } + else if (aopInReg (cond->aop, 1, A_IDX)) + { + skip_byte = 1; + } + else + { + cheapMove (ASMOP_A, 0, cond->aop, 0, true, true); + skip_byte = 0; + } + + for (int i = 0; i < cond->aop->size; i++) + { + if (i == skip_byte) + continue; + + if (cond->aop->type == AOP_STK) + { + pushAF (); + cheapMove (ASMOP_P, 0, cond->aop, i, true, true); + popAF (); + emit2 ("or", "a, p"); + cost (1, 1); + } + else + { + emit2 ("or", "a, %s", aopGet (cond->aop, i)); + cost (1, 1); + } + } + + if (TARGET_IS_PDK13 && IC_FALSE (ic)) // pdk13 does not have cneqsn. + { + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + emit2 ("ceqsn", "a, #0x00"); + emitJP (tlbl, 0.0f); + cost (2, 3); + emitJP (IC_FALSE (ic), 0.0f); + emitLabel (tlbl); + } + else + { + emit2 (IC_FALSE (ic) ? "cneqsn" : "ceqsn", "a, #0x00"); + cost (1, 1); + emitJP (IC_FALSE (ic) ? IC_FALSE (ic) : IC_TRUE (ic), 0.0f); + } + +release: + freeAsmop (cond); +} + +/*-----------------------------------------------------------------*/ +/* genAddrOf - generates code for address of */ +/*-----------------------------------------------------------------*/ +static void +genAddrOf (const iCode *ic) +{ + operand *result, *left, *right; + + D (emit2 ("; genAddrOf", "")); + + result = IC_RESULT (ic); + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + + wassert (result); + wassert (left); + wassert (IS_TRUE_SYMOP (left)); + wassert (right && IS_OP_LITERAL (IC_RIGHT (ic))); + + const symbol *sym = OP_SYMBOL_CONST (left); + wassert (sym); + + aopOp (result, ic); + + int size = result->aop->size; + + wassert (size == 1 || size == 2); + if (sym->onStack) + { + int s = sym->stack + (sym->stack < 0 ? G.stack.param_offset : 0) + operandLitValue (right); + + if (G.p.type == AOP_STK && s == G.p.offset) + cheapMove (result->aop, 0, ASMOP_P, 0, true, true); + else + { + cheapMove (ASMOP_A, 0, ASMOP_SP, 0, true, true); + emit2 ("add", "a, #0x%02x", (s - G.stack.pushed) & 0xff); + cost (1, 1); + cheapMove (result->aop, 0, ASMOP_A, 0, true, true); + } + + if (size == 2) + cheapMove (result->aop, 1, ASMOP_ZERO, 0, true, true); + } + else if (PTR_TYPE (SPEC_OCLS (getSpec (operandType (IC_LEFT (ic))))) == CPOINTER) // In ROM + { + wassert (size == 2); + + emit2 ("mov", "a, #<(%s + %d)", sym->rname, (int)operandLitValue (right)); + cost (1, 1); + cheapMove (result->aop, 0, ASMOP_A, 0, true, true); + emit2 ("mov", "a, #>(%s + 0x8000 + %d)", sym->rname, (int)operandLitValue (right)); + cheapMove (result->aop, 1, ASMOP_A, 0, true, true); + } + else // In RAM + { + emit2 ("mov", "a, #(%s + %d)", sym->rname, (int)operandLitValue (right)); + cost (1, 1); + cheapMove (result->aop, 0, ASMOP_A, 0, true, true); + if (size == 2) + cheapMove (result->aop, 1, ASMOP_ZERO, 0, true, true); + } + + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genJumpTab - generate code for jump table */ +/*-----------------------------------------------------------------*/ +static void +genJumpTab (const iCode *ic) +{ + operand *cond; + + D (emit2 ("; genJumpTab", "")); + + cond = IC_JTCOND (ic); + + aopOp (cond, ic); + + wassertl (cond->aop->size == 1, "Jump table not implemented for operands wider than 1 byte."); + + cheapMove (ASMOP_A, 0, cond->aop, 0, true, true); + + emit2 ("add", "a, #0x01"); + emit2 ("pcadd", "a"); + cost (2, 3); + + for (symbol *jtab = setFirstItem (IC_JTLABELS (ic)); jtab; jtab = setNextItem (IC_JTLABELS (ic))) + { + if (!regalloc_dry_run) + emit2 ("goto", "#!tlabel", labelKey2num (jtab->key)); + cost (1, 0); + } + + freeAsmop (cond); +} + +/*-----------------------------------------------------------------*/ +/* genCast - generate code for cast */ +/*-----------------------------------------------------------------*/ +static void +genCast (const iCode *ic) +{ + operand *result, *right; + int offset; + sym_link *resulttype, *righttype; + + D (emit2 ("; genCast", "")); + + result = IC_RESULT (ic); + right = IC_RIGHT (ic); + resulttype = operandType (result); + righttype = operandType (right); + + bool pushed_a = false; + + if ((getSize (resulttype) <= getSize (righttype) || !IS_SPEC (righttype) || (SPEC_USIGN (righttype) || IS_BOOLEAN (righttype))) && + (!IS_BOOLEAN (resulttype) || IS_BOOLEAN (righttype))) + { + genAssign (ic); + return; + } + + aopOp (right, ic); + aopOp (result, ic); + + if (!regDead (A_IDX, ic)) + { + pushAF (); + pushed_a = true; + } + + if (IS_BOOL (resulttype)) + { + int size = right->aop->size; + int skipbyte; + + if (aopInReg (right->aop, 1, A_IDX)) + skipbyte = 1; + else + { + cheapMove (ASMOP_A, 0, right->aop, 0, true, true); + skipbyte = 0; + } + + for(offset = 0; offset < size; offset++) + { + if (offset == skipbyte) + continue; + emit2 ("or", "a, %s", aopGet (right->aop, offset)); + cost (1, 1); + } + + emit2 ("ceqsn", "a, #0x00"); + emit2 ("mov", "a, #0x01"); + if (!regalloc_dry_run) // Dummy label as target for ceqsn to prevent peephole optimizer from optimizing out mov. + { + symbol *tlbl = newiTempLabel (0); + emitLabel (tlbl); + } + cost (2, 2); + + cheapMove (result->aop, 0, ASMOP_A, 0, true, true); + } + else // Cast to signed type + { + genMove_o (result->aop, 0, right->aop, 0, right->aop->size, regDead (A_IDX, ic)); + + int size = result->aop->size - right->aop->size; + offset = right->aop->size; + + cheapMove (ASMOP_A, 0, result->aop, right->aop->size - 1, true, true); + emit2 ("sl", "a"); + emit2 ("mov", "a, #0x00"); + emit2 ("subc", "a"); + cost (3, 3); + + while (size--) + cheapMove (result->aop, offset++, ASMOP_A, 0, true, true); + } + + if (pushed_a) + popAF (); + + freeAsmop (right); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genDummyRead - generate code for dummy read of volatiles */ +/*-----------------------------------------------------------------*/ +static void +genDummyRead (const iCode *ic) +{ + operand *op; + + if ((op = IC_LEFT (ic)) && IS_SYMOP (op)) + ; + else if ((op = IC_RIGHT (ic)) && IS_SYMOP (op)) + ; + else + return; + + aopOp (op, ic); + + if (!regDead(A_IDX, ic) && op->aop->type == AOP_DIR && op->aop->size <= 2) + for (int i = 0; i < op->aop->size; i++) + { + emit2 ("ceqsn", "a, %s", aopGet (op->aop, i)); + emit2 ("nop", ""); + cost (2, 2); + } + else + { + if (!regDead(A_IDX, ic)) + { + emit2 ("push", "af"); + cost (1, 1); + } + + for (int i = 0; i < op->aop->size; i++) + cheapMove (ASMOP_A, 0, op->aop, i, true, true); + + if (!regDead(A_IDX, ic)) + { + emit2 ("pop", "af"); + cost (1, 1); + } + } + + freeAsmop (op); +} + +/*-----------------------------------------------------------------*/ +/* resultRemat - result is to be rematerialized */ +/*-----------------------------------------------------------------*/ +static bool +resultRemat (const iCode *ic) +{ + if (SKIP_IC (ic) || ic->op == IFX) + return 0; + + if (IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic))) + { + const symbol *sym = OP_SYMBOL_CONST (IC_RESULT (ic)); + + if (!sym->remat) + return(false); + + bool completely_spilt = TRUE; + for (unsigned int i = 0; i < getSize (sym->type); i++) + if (sym->regs[i]) + completely_spilt = FALSE; + + if (completely_spilt) + return(true); + } + + return (false); +} + +/*---------------------------------------------------------------------*/ +/* genSTM8Code - generate code for STM8 for a single iCode instruction */ +/*---------------------------------------------------------------------*/ +static void +genPdkiCode (iCode *ic) +{ + genLine.lineElement.ic = ic; + + if (resultRemat (ic)) + { + if (!regalloc_dry_run) + D (emit2 ("; skipping iCode since result will be rematerialized", "")); + return; + } + + if (ic->generated) + { + //if (!regalloc_dry_run) + D (emit2 ("; skipping generated iCode", "")); + return; + } + + switch (ic->op) + { + case '!': + genNot (ic); + break; + + case '~': + genCpl (ic); + break; + + case UNARYMINUS: + genUminus (ic); + break; + + case IPUSH: + genIpush (ic); + break; + + case IPOP: + wassertl (0, "Unimplemented iCode"); + break; + + case CALL: + case PCALL: + genCall (ic); + break; + + case FUNCTION: + genFunction (ic); + break; + + case ENDFUNCTION: + genEndFunction (ic); + break; + + case RETURN: + genReturn (ic); + break; + + case LABEL: + genLabel (ic); + break; + + case GOTO: + genGoto (ic); + break; + + case '+': + genPlus (ic); + break; + + case '-': + genMinus (ic, ic->next && ic->next->op == IFX ? ic->next : 0); + break; + + case '*': + genMult (ic); + break; + + case '/': + case '%': + wassertl (0, "Unimplemented iCode"); + break; + + case '>': + case '<': + genCmp (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case LE_OP: + case GE_OP: + wassertl (0, "Unimplemented iCode"); + break; + + case NE_OP: + case EQ_OP: + genCmpEQorNE (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case AND_OP: + case OR_OP: + wassertl (0, "Unimplemented iCode"); + break; + + case '^': + genXor (ic); + break; + + case '|': + genOr (ic); + break; + + case BITWISEAND: + genAnd (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case INLINEASM: + genInline (ic); + break; + + case RRC: + case RLC: + wassertl (0, "Unimplemented iCode"); + break; + + case GETABIT: + wassertl (0, "Unimplemented iCode"); + break; + + case LEFT_OP: + genLeftShift (ic); + break; + + case RIGHT_OP: + genRightShift (ic); + break; + + case GET_VALUE_AT_ADDRESS: + genPointerGet (ic); + break; + + case SET_VALUE_AT_ADDRESS: + genPointerSet (ic); + break; + + case '=': + wassert (!POINTER_SET (ic)); + genAssign (ic); + break; + + case IFX: + genIfx (ic); + break; + + case ADDRESS_OF: + genAddrOf (ic); + break; + + case JUMPTABLE: + genJumpTab (ic); + break; + + case CAST: + genCast (ic); + break; + + case RECEIVE: + case SEND: + wassertl (0, "Unimplemented iCode"); + break; + + case DUMMY_READ_VOLATILE: + genDummyRead (ic); + break; + + case CRITICAL: + wassertl (0, "Unimplemented iCode: Critical section"); + break; + + case ENDCRITICAL: + wassertl (0, "Unimplemented iCode: Critical section"); + break; + + default: + fprintf (stderr, "iCode op %d:\n", ic->op); + wassertl (0, "Unknown iCode"); + } +} + +float +dryPdkiCode (iCode *ic) +{ + regalloc_dry_run = true; + regalloc_dry_run_cost_words = 0; + regalloc_dry_run_cost_cycles = 0; + + initGenLineElement (); + + genPdkiCode (ic); + + G.p.type = AOP_INVALID; + + destroy_line_list (); + + wassert (regalloc_dry_run); + + const unsigned int word_cost_weight = 2 << (optimize.codeSize * 3 + !optimize.codeSpeed * 3); + + return (regalloc_dry_run_cost_words * word_cost_weight + regalloc_dry_run_cost_cycles * ic->count); +} + +/*---------------------------------------------------------------------*/ +/* genPdkCode - generate code for Padauk for a block of intructions */ +/*---------------------------------------------------------------------*/ +void +genPdkCode (iCode *lic) +{ + int clevel = 0; + int cblock = 0; + int cln = 0; + regalloc_dry_run = false; + + for (iCode *ic = lic; ic; ic = ic->next) + { + initGenLineElement (); + + genLine.lineElement.ic = ic; + + if (ic->level != clevel || ic->block != cblock) + { + if (options.debug) + debugFile->writeScope (ic); + clevel = ic->level; + cblock = ic->block; + } + + if (ic->lineno && cln != ic->lineno) + { + if (options.debug) + debugFile->writeCLine (ic); + + if (!options.noCcodeInAsm) + emit2 (";", "%s: %d: %s", ic->filename, ic->lineno, printCLine (ic->filename, ic->lineno)); + cln = ic->lineno; + } + + regalloc_dry_run_cost_words = 0; + regalloc_dry_run_cost_cycles = 0; + + if (options.iCodeInAsm) + { + const char *iLine = printILine (ic); + emit2 ("; ic:", "%d: %s", ic->key, iLine); + dbuf_free (iLine); + } + + genPdkiCode(ic); + +#if 0 + D (emit2 (";", "Cost for generated ic %d : (%d, %f)", ic->key, regalloc_dry_run_cost_words, regalloc_dry_run_cost_cycles)); +#endif + } + + if (options.debug) + debugFile->writeFrameAddress (NULL, NULL, 0); /* have no idea where frame is now */ + + /* now we are ready to call the + peephole optimizer */ + if (!options.nopeep) + peepHole (&genLine.lineHead); + + /* now do the actual printing */ + printLine (genLine.lineHead, codeOutBuf); + + G.p.type = AOP_INVALID; + + /* destroy the line list */ + destroy_line_list (); +} + diff --git a/src/pdk/gen.h b/src/pdk/gen.h new file mode 100644 index 0000000..e0eb838 --- /dev/null +++ b/src/pdk/gen.h @@ -0,0 +1,90 @@ +/*------------------------------------------------------------------------- + gen.h - header file for code generation for Padauk + + Written By - Philipp Krause . pkk@spth.de (2018) + + 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 PDKGEN_H +#define PDKGEN_H 1 + +typedef enum +{ + AOP_INVALID, + /* Is a literal */ + AOP_LIT = 1, + /* Is in a register */ + AOP_REG, + /* Is partially in a register, partially in direct space */ + AOP_REGDIR, + /* Is on the stack */ + AOP_STK, + /* Is an immediate value */ + AOP_IMMD, + /* Is in direct space */ + AOP_DIR, + /* I/O register */ + AOP_SFR, + /* Is in code space */ + AOP_CODE, + /* Read undefined, discard writes */ + AOP_DUMMY, + /* Implicit condition operand */ + AOP_CND, +} +AOP_TYPE; + +/* asmop_byte: A type for the location a single byte + of an operand can be in */ +typedef struct asmop_byte +{ + bool in_reg; + union + { + reg_info *reg; /* Register this byte is in. */ + int stk; /* Stack offset for this byte. */ + } byteu; +} asmop_byte; + +/* asmop: A homogenised type for all the different + spaces an operand can be in */ +typedef struct asmop +{ + AOP_TYPE type; + short size; + union + { + value *aop_lit; + struct + { + char *immd; + int immd_off; + bool code; /* in code space */ + bool func; /* function address */ + }; + char *aop_dir; + asmop_byte bytes[8]; + } aopu; +} +asmop; + +void genPdkCode (iCode *); + +extern bool pdk_assignment_optimal; +void pdk_init_asmops (void); + +#endif + diff --git a/src/pdk/gen.o b/src/pdk/gen.o Binary files differnew file mode 100644 index 0000000..f9cc656 --- /dev/null +++ b/src/pdk/gen.o diff --git a/src/pdk/main.c b/src/pdk/main.c new file mode 100644 index 0000000..21b1fc2 --- /dev/null +++ b/src/pdk/main.c @@ -0,0 +1,714 @@ +/*------------------------------------------------------------------------- + main.c - Padauk specific definitions. + + Philipp Klaus Krause <pkk@spth.de> 2012-2018 + + 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 "dbuf_string.h" + +#include "ralloc.h" +#include "peep.h" + +extern DEBUGFILE dwarf2DebugFile; +extern int dwarf2FinalizeFile(FILE *); + +static char pdk_defaultRules[] = { +#include "peeph.rul" + "" +}; + +static char *pdk_keywords[] = { + "at", + "code", + "data", + "interrupt", + "naked", + "near", + "reentrant", + "sfr", + "sfr16", + NULL +}; + +static void +pdk_genAssemblerStart (FILE *of) +{ + fprintf (of, "\n; default segment ordering in RAM for linker\n"); + tfprintf (of, "\t!area\n", DATA_NAME); + tfprintf (of, "\t!area\n", OVERLAY_NAME); + fprintf (of, "\n"); +} + +static void +pdk_genAssemblerEnd (FILE *of) +{ + if (options.out_fmt == 'E' && options.debug) + dwarf2FinalizeFile (of); +} + +int +pdk_genIVT(struct dbuf_s *oBuf, symbol **intTable, int intCount) +{ + dbuf_tprintf (oBuf, "\t.area\tHEADER (ABS)\n"); + dbuf_tprintf (oBuf, "\t.org\t 0x0020\n"); + if (interrupts[0]) + dbuf_tprintf (oBuf, "\tgoto\t%s\n", interrupts[0]->rname); + else + dbuf_tprintf (oBuf, "\treti\n"); + + return (true); +} + +static void +pdk_genInitStartup (FILE *of) +{ + fprintf (of, "\t.area\tPREG (ABS)\n"); + fprintf (of, "\t.org 0x00\n"); + fprintf (of, "p::\n"); + fprintf (of, "\t.ds 2\n"); + + + fprintf (of, "\t.area\tHEADER (ABS)\n"); // In the header we have 16 bytes. First should be nop. + fprintf (of, "\t.org 0x0000\n"); + fprintf (of, "\tnop\n"); // First word is a jump to self-test routine at end of ROM on some new devices. + + // Zero upper byte of pseudo-register p to make p usable for pointers. + fprintf (of, "\tclear\tp+1\n"); + + // Initialize stack pointer + if (options.stack_loc >= 0) + { + fprintf (of, "\tmov\ta, #0x%02x\n", options.stack_loc); + fprintf (of, "\tmov\tsp, a\n"); + } + else + { + fprintf (of, "\tmov\ta, #s_OSEG\n"); + fprintf (of, "\tadd\ta, #l_OSEG + 1\n"); + fprintf (of, "\tand\ta, #0xfe\n"); + fprintf (of, "\tmov\tsp, a\n"); + } + + fprintf (of, "\tcall\t__sdcc_external_startup\n"); + fprintf (of, "\tgoto\t__sdcc_gs_init_startup\n"); + + tfprintf (of, "\t!area\n", STATIC_NAME); + fprintf (of, "__sdcc_gs_init_startup:\n"); + + /* Init static & global variables */ + fprintf (of, "__sdcc_init_data:\n"); + + /* Zeroing memory (required by standard for static & global variables) */ + fprintf (of, "\tmov\ta, #s_DATA\n"); + fprintf (of, "\tmov\tp, a\n"); + fprintf (of, "\tgoto\t00002$\n"); + fprintf (of, "00001$:\n"); + fprintf (of, "\tmov\ta, #0x00\n"); + fprintf (of, "\tidxm\tp, a\n"); + fprintf (of, "\tinc\tp\n"); + fprintf (of, "\tmov\ta, #s_DATA\n"); + fprintf (of, "00002$:\n"); + fprintf (of, "\tadd\ta, #l_DATA\n"); + fprintf (of, "\tceqsn\ta, p\n"); + fprintf (of, "\tgoto\t00001$\n"); +} + +static void +pdk_init (void) +{ + asm_addTree (&asm_asxxxx_smallpdk_mapping); +} + +static void +pdk_reset_regparm (struct sym_link *funcType) +{ +} + +static int +pdk_reg_parm (sym_link *l, bool reentrant) +{ + return (0); +} + +static bool +pdk_parseOptions (int *pargc, char **argv, int *i) +{ + if (!strcmp (argv[*i], "--out-fmt-elf")) + { + options.out_fmt = 'E'; + debugFile = &dwarf2DebugFile; + return TRUE; + } + return FALSE; +} + +static void +pdk_finaliseOptions (void) +{ + port->mem.default_local_map = data; + port->mem.default_globl_map = data; +} + +static void +pdk_setDefaultOptions (void) +{ + options.out_fmt = 'i'; /* Default output format is ihx */ + options.data_loc = 0x02; /* First two bytes of RAM are used for the pseudo-register p */ + options.code_loc = 0x0022; + options.stack_loc = -1; +} + +static const char * +pdk_getRegName (const struct reg_info *reg) +{ + if (reg) + return reg->name; + return "err"; +} + +static bool +_hasNativeMulFor (iCode *ic, sym_link *left, sym_link *right) +{ + int result_size = IS_SYMOP (IC_RESULT(ic)) ? getSize (OP_SYM_TYPE (IC_RESULT(ic))) : 4; + + if (ic->op != '*') + return (false); + + return ((IS_LITERAL (left) || IS_LITERAL (right)) && result_size == 1); +} + +/* Indicate which extended bit operations this backend supports */ +static bool +hasExtBitOp (int op, int size) +{ + return (false); +} + +/** $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[] = +{ + "sdldpdk", "-nf", "\"$1\"", NULL +}; + +/* $3 is replaced by assembler.debug_opts resp. port->assembler.plain_opts */ +static const char *pdk13AsmCmd[] = +{ + "sdaspdk13", "$l", "$3", "\"$1.asm\"", NULL +}; + +static const char *const _libs_pdk13[] = { "pdk13", NULL, }; + +PORT pdk13_port = +{ + TARGET_ID_PDK13, + "pdk13", + "PDK13", /* Target name */ + 0, /* Processor name */ + { + glue, + true, + NO_MODEL, + NO_MODEL, + 0, /* model == target */ + }, + { /* Assembler */ + pdk13AsmCmd, + 0, + "-plosgffwy", /* Options with debug */ + "-plosgffw", /* Options without debug */ + 0, + ".asm" + }, + { /* Linker */ + _linkCmd, + 0, //LINKCMD, + 0, + ".rel", + 1, + 0, /* crt */ + _libs_pdk13, /* libs */ + }, + { /* Peephole optimizer */ + pdk_defaultRules, + pdkinstructionSize, + 0, + 0, + 0, + pdknotUsed, + 0, + pdknotUsedFrom, + 0, + }, + /* Sizes: char, short, int, long, long long, ptr, fptr, gptr, bit, float, max */ + { + 1, /* char */ + 2, /* short */ + 2, /* int */ + 4, /* long */ + 8, /* long long */ + 1, /* near ptr */ + 2, /* far ptr */ + 2, /* generic ptr */ + 2, /* func ptr */ + 0, /* banked func ptr */ + 1, /* bit */ + 4, /* float */ + }, + /* tags for generic pointers */ + { 0x00, 0x40, 0x60, 0x80 }, /* far, near, xstack, code */ + { + "XSEG", + "STACK", + "CODE", /* code */ + "DATA", /* data */ + NULL, /* idata */ + NULL, /* pdata */ + NULL, /* xdata */ + NULL, /* bit */ + "RSEG (ABS)", /* reg */ + "GSINIT", /* static initialization */ + "OSEG (OVR,DATA)", /* overlay */ + "GSFINAL", /* gsfinal */ + "HOME", /* home */ + NULL, /* xidata */ + NULL, /* xinit */ + "CONST", /* const_name */ + "CABS (ABS)", /* cabs_name */ + "DABS (ABS)", /* xabs_name */ + 0, /* iabs_name */ + 0, /* name of segment for initialized variables */ + 0, /* name of segment for copies of initialized variables in code space */ + 0, + 0, + 1, /* CODE is read-only */ + 1 /* No fancy alignments supported. */ + }, + { 0, 0 }, + { /* stack information */ + +1, /* direction: stack grows up */ + 0, + 7, /* isr overhead */ + 2, /* call overhead */ + 0, + 2, + 1, /* sp points to next free stack location */ + }, + { -1, false }, /* no int x int -> long multiplication support routine. */ + { 0, + { + 0, + 0, /* cfiSame */ + 0, /* cfiUndef */ + 0, /* addressSize */ + 0, /* regNumRet */ + 0, /* regNumSP */ + 0, /* regNumBP */ + 0, /* offsetSP */ + }, + }, + { + 256, /* maxCount */ + 1, /* sizeofElement */ + {2, 0, 0}, /* sizeofMatchJump[] - the 0s here ensure that we only generate jump tables for 8-bit operands, which is all the backend can handle */ + {4, 0, 0}, /* sizeofRangeCompare[] */ + 1, /* sizeofSubtract */ + 2, /* sizeofDispatch */ + }, + "_", + pdk_init, + pdk_parseOptions, + 0, + 0, + pdk_finaliseOptions, /* finaliseOptions */ + pdk_setDefaultOptions, /* setDefaultOptions */ + pdk_assignRegisters, + pdk_getRegName, + 0, + 0, + pdk_keywords, + pdk_genAssemblerStart, + pdk_genAssemblerEnd, + pdk_genIVT, + 0, /* no genXINIT code */ + pdk_genInitStartup, /* genInitStartup */ + pdk_reset_regparm, + pdk_reg_parm, + 0, /* process_pragma */ + 0, /* getMangledFunctionName */ + _hasNativeMulFor, /* hasNativeMulFor */ + hasExtBitOp, /* hasExtBitOp */ + 0, /* oclsExpense */ + false, /* data is represented in ROM using ret k instructions */ + true, /* little endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + false, /* Array initializer support. */ + 0, /* no CSE cost estimation yet */ + 0, /* builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local statics allowed */ + 2, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; + +/* $3 is replaced by assembler.debug_opts resp. port->assembler.plain_opts */ +static const char *pdk14AsmCmd[] = +{ + "sdaspdk14", "$l", "$3", "\"$1.asm\"", NULL +}; + +static const char *const _libs_pdk14[] = { "pdk14", NULL, }; + +PORT pdk14_port = +{ + TARGET_ID_PDK14, + "pdk14", + "PDK14", /* Target name */ + 0, /* Processor name */ + { + glue, + true, + NO_MODEL, + NO_MODEL, + 0, /* model == target */ + }, + { /* Assembler */ + pdk14AsmCmd, + 0, + "-plosgffwy", /* Options with debug */ + "-plosgffw", /* Options without debug */ + 0, + ".asm" + }, + { /* Linker */ + _linkCmd, + 0, //LINKCMD, + 0, + ".rel", + 1, + 0, /* crt */ + _libs_pdk14, /* libs */ + }, + { /* Peephole optimizer */ + pdk_defaultRules, + pdkinstructionSize, + 0, + 0, + 0, + pdknotUsed, + 0, + pdknotUsedFrom, + 0, + }, + /* Sizes: char, short, int, long, long long, ptr, fptr, gptr, bit, float, max */ + { + 1, /* char */ + 2, /* short */ + 2, /* int */ + 4, /* long */ + 8, /* long long */ + 1, /* near ptr */ + 2, /* far ptr */ + 2, /* generic ptr */ + 2, /* func ptr */ + 0, /* banked func ptr */ + 1, /* bit */ + 4, /* float */ + }, + /* tags for generic pointers */ + { 0x00, 0x40, 0x60, 0x80 }, /* far, near, xstack, code */ + { + "XSEG", + "STACK", + "CODE", /* code */ + "DATA", /* data */ + NULL, /* idata */ + NULL, /* pdata */ + NULL, /* xdata */ + NULL, /* bit */ + "RSEG (ABS)", /* reg */ + "GSINIT", /* static initialization */ + "OSEG (OVR,DATA)", /* overlay */ + "GSFINAL", /* gsfinal */ + "HOME", /* home */ + NULL, /* xidata */ + NULL, /* xinit */ + "CONST", /* const_name */ + "CABS (ABS)", /* cabs_name */ + "DABS (ABS)", /* xabs_name */ + 0, /* iabs_name */ + 0, /* name of segment for initialized variables */ + 0, /* name of segment for copies of initialized variables in code space */ + 0, + 0, + 1, /* CODE is read-only */ + 1 /* No fancy alignments supported. */ + }, + { 0, 0 }, + { /* stack information */ + +1, /* direction: stack grows up */ + 0, + 7, /* isr overhead */ + 2, /* call overhead */ + 0, + 2, + 1, /* sp points to next free stack location */ + }, + { -1, false }, /* no int x int -> long multiplication support routine. */ + { 0, + { + 0, + 0, /* cfiSame */ + 0, /* cfiUndef */ + 0, /* addressSize */ + 0, /* regNumRet */ + 0, /* regNumSP */ + 0, /* regNumBP */ + 0, /* offsetSP */ + }, + }, + { + 256, /* maxCount */ + 1, /* sizeofElement */ + {2, 0, 0}, /* sizeofMatchJump[] - the 0s here ensure that we only generate jump tables for 8-bit operands, which is all the backend can handle */ + {4, 0, 0}, /* sizeofRangeCompare[] */ + 1, /* sizeofSubtract */ + 2, /* sizeofDispatch */ + }, + "_", + pdk_init, + pdk_parseOptions, + 0, + 0, + pdk_finaliseOptions, /* finaliseOptions */ + pdk_setDefaultOptions, /* setDefaultOptions */ + pdk_assignRegisters, + pdk_getRegName, + 0, + 0, + pdk_keywords, + pdk_genAssemblerStart, + pdk_genAssemblerEnd, + pdk_genIVT, + 0, /* no genXINIT code */ + pdk_genInitStartup, /* genInitStartup */ + pdk_reset_regparm, + pdk_reg_parm, + 0, /* process_pragma */ + 0, /* getMangledFunctionName */ + _hasNativeMulFor, /* hasNativeMulFor */ + hasExtBitOp, /* hasExtBitOp */ + 0, /* oclsExpense */ + false, /* data is represented in ROM using ret k instructions */ + true, /* little endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + false, /* Array initializer support. */ + 0, /* no CSE cost estimation yet */ + 0, /* builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local statics allowed */ + 2, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; + +/* $3 is replaced by assembler.debug_opts resp. port->assembler.plain_opts */ +static const char *pdk15AsmCmd[] = +{ + "sdaspdk15", "$l", "$3", "\"$1.asm\"", NULL +}; + +static const char *const _libs_pdk15[] = { "pdk15", NULL, }; + +PORT pdk15_port = +{ + TARGET_ID_PDK15, + "pdk15", + "PDK15", /* Target name */ + 0, /* Processor name */ + { + glue, + true, + NO_MODEL, + NO_MODEL, + 0, /* model == target */ + }, + { /* Assembler */ + pdk15AsmCmd, + 0, + "-plosgffwy", /* Options with debug */ + "-plosgffw", /* Options without debug */ + 0, + ".asm" + }, + { /* Linker */ + _linkCmd, + 0, //LINKCMD, + 0, + ".rel", + 1, + 0, /* crt */ + _libs_pdk15, /* libs */ + }, + { /* Peephole optimizer */ + pdk_defaultRules, + pdkinstructionSize, + 0, + 0, + 0, + pdknotUsed, + 0, + pdknotUsedFrom, + 0, + }, + /* Sizes: char, short, int, long, long long, ptr, fptr, gptr, bit, float, max */ + { + 1, /* char */ + 2, /* short */ + 2, /* int */ + 4, /* long */ + 8, /* long long */ + 1, /* near ptr */ + 2, /* far ptr */ + 2, /* generic ptr */ + 2, /* func ptr */ + 0, /* banked func ptr */ + 1, /* bit */ + 4, /* float */ + }, + /* tags for generic pointers */ + { 0x00, 0x40, 0x60, 0x80 }, /* far, near, xstack, code */ + { + "XSEG", + "STACK", + "CODE", /* code */ + "DATA", /* data */ + NULL, /* idata */ + NULL, /* pdata */ + NULL, /* xdata */ + NULL, /* bit */ + "RSEG (ABS)", /* reg */ + "GSINIT", /* static initialization */ + "OSEG (OVR,DATA)", /* overlay */ + "GSFINAL", /* gsfinal */ + "HOME", /* home */ + NULL, /* xidata */ + NULL, /* xinit */ + "CONST", /* const_name */ + "CABS (ABS)", /* cabs_name */ + "DABS (ABS)", /* xabs_name */ + 0, /* iabs_name */ + 0, /* name of segment for initialized variables */ + 0, /* name of segment for copies of initialized variables in code space */ + 0, + 0, + 1, /* CODE is read-only */ + 1 /* No fancy alignments supported. */ + }, + { 0, 0 }, + { /* stack information */ + +1, /* direction: stack grows up */ + 0, + 7, /* isr overhead */ + 2, /* call overhead */ + 0, + 2, + 1, /* sp points to next free stack location */ + }, + { -1, false }, /* no int x int -> long multiplication support routine. */ + { 0, + { + 0, + 0, /* cfiSame */ + 0, /* cfiUndef */ + 0, /* addressSize */ + 0, /* regNumRet */ + 0, /* regNumSP */ + 0, /* regNumBP */ + 0, /* offsetSP */ + }, + }, + { + 256, /* maxCount */ + 1, /* sizeofElement */ + {2, 0, 0}, /* sizeofMatchJump[] - the 0s here ensure that we only generate jump tables for 8-bit operands, which is all the backend can handle */ + {4, 0, 0}, /* sizeofRangeCompare[] */ + 1, /* sizeofSubtract */ + 2, /* sizeofDispatch */ + }, + "_", + pdk_init, + pdk_parseOptions, + 0, + 0, + pdk_finaliseOptions, /* finaliseOptions */ + pdk_setDefaultOptions, /* setDefaultOptions */ + pdk_assignRegisters, + pdk_getRegName, + 0, + 0, + pdk_keywords, + pdk_genAssemblerStart, + pdk_genAssemblerEnd, + pdk_genIVT, + 0, /* no genXINIT code */ + pdk_genInitStartup, /* genInitStartup */ + pdk_reset_regparm, + pdk_reg_parm, + 0, /* process_pragma */ + 0, /* getMangledFunctionName */ + _hasNativeMulFor, /* hasNativeMulFor */ + hasExtBitOp, /* hasExtBitOp */ + 0, /* oclsExpense */ + false, /* data is represented in ROM using ret k instructions */ + true, /* little endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + false, /* Array initializer support. */ + 0, /* no CSE cost estimation yet */ + 0, /* builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local statics allowed */ + 2, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; + diff --git a/src/pdk/main.o b/src/pdk/main.o Binary files differnew file mode 100644 index 0000000..79372bb --- /dev/null +++ b/src/pdk/main.o diff --git a/src/pdk/peep.c b/src/pdk/peep.c new file mode 100644 index 0000000..153b090 --- /dev/null +++ b/src/pdk/peep.c @@ -0,0 +1,521 @@ +#include "common.h" +#include "SDCCgen.h" + +#include "peep.h" + +#define NOTUSEDERROR() do {werror(E_INTERNAL_ERROR, __FILE__, __LINE__, "error in notUsed()");} while(0) + +// #define D(_s) { printf _s; fflush(stdout); } +#define D(_s) + +#define ISINST(l, i) (!STRNCASECMP((l), (i), sizeof(i) - 1) && (!(l)[sizeof(i) - 1] || isspace((unsigned char)((l)[sizeof(i) - 1])))) + +typedef enum +{ + S4O_CONDJMP, + S4O_WR_OP, + S4O_RD_OP, + S4O_TERM, + S4O_VISITED, + S4O_ABORT, + S4O_CONTINUE +} S4O_RET; + +static struct +{ + lineNode *head; +} _G; + +static bool +isReturned(const char *what) +{ + symbol *sym; + sym_link *sym_lnk; + int size; + lineNode *l; + + l = _G.head; + do + { + l = l->next; + } while(l->isComment || l->ic == NULL || l->ic->op != FUNCTION); + + sym = OP_SYMBOL(IC_LEFT(l->ic)); + + if(sym && IS_DECL(sym->type)) + { + // Find size of return value. + specifier *spec; + if(sym->type->select.d.dcl_type != FUNCTION) + NOTUSEDERROR(); + spec = &(sym->etype->select.s); + if(spec->noun == V_VOID) + size = 0; + else if(spec->noun == V_CHAR || spec->noun == V_BOOL) + size = 1; + else if(spec->noun == V_INT && !(spec->b_long)) + size = 2; + else + size = 4; + + // Check for returned pointer. + sym_lnk = sym->type; + while (sym_lnk && !IS_PTR (sym_lnk)) + sym_lnk = sym_lnk->next; + if(IS_PTR(sym_lnk)) + size = 2; + } + else + { + NOTUSEDERROR(); + return TRUE; + } + + switch(*what) + { + case 'a': + return(size == 1 || size == 2); + case 'p': + return(size == 2); + default: + return false; + } +} + +/*-----------------------------------------------------------------*/ +/* 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 jump the label is at the end */ + p = strlen (pl->line) - 1 + pl->line; + + /* Skip trailing whitespace */ + while(isspace(*p)) + p--; + + /* scan backward until space or ',' */ + for (; p > pl->line; p--) + if (isspace(*p) || *p == ',') + break; + + /* sanity check */ + if (p == pl->line) + { + NOTUSEDERROR(); + 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; +} + +/* Check if reading arg implies reading what. */ +static bool argIs(const char *arg, const char *what) +{ + if (arg == NULL || strlen (arg) == 0) + return false; + + while (isblank ((unsigned char)(arg[0]))) + arg++; + + if (arg[0] == ',') + arg++; + + while (isblank ((unsigned char)(arg[0]))) + arg++; + + return !strncmp(arg, what, strlen(what)) && + (!arg[strlen(what)] || isspace((unsigned char)(arg[strlen(what)])) || arg[strlen(what)] == ','); +} + +static bool +stm8MightReadFlag(const lineNode *pl, const char *what) +{ + if (ISINST (pl->line, "push") && argIs (pl->line + 4, "af") || ISINST (pl->line, "pushaf")) + return true; + + if (ISINST (pl->line, "t0sn") || ISINST (pl->line, "t1sn")) + return argIs(strchr (pl->line, ','), what); + + if(ISINST (pl->line, "addc") || + ISINST (pl->line, "subc")) + return !strcmp(what, "c"); + + return false; +} + +static bool +pdkMightRead(const lineNode *pl, const char *what) +{ +//printf("pdkMightRead() for |%s|%s|\n", pl->line, what); + if (!strcmp(what, "z") || !strcmp(what, "c") || !strcmp(what, "ac") || !strcmp(what, "ov")) + return (stm8MightReadFlag(pl, what)); + else if (strcmp(what, "a") && strcmp(what, "p")) + return true; + + // Instructions that never read anything. + if (ISINST(pl->line, "engint") || + ISINST(pl->line, "disgint") || + ISINST(pl->line, "nop") || + ISINST(pl->line, "pop") || ISINST(pl->line, "popaf") || + ISINST(pl->line, "stopsys") || + ISINST(pl->line, "wdreset")) + return false; + + if (ISINST(pl->line, "ret") && strchr(pl->line + 2, '#') && !strcmp(what, "a")) + return false; + if (ISINST(pl->line, "ret")) + return isReturned(what); + + if (ISINST(pl->line, "mov")) + return argIs (strchr (pl->line, ','), what); + + if (ISINST (pl->line, "push") && argIs (pl->line + 4, "af") || ISINST (pl->line, "pushaf")) + return !strcmp(what, "a"); + + // Two-operand instructions that read both operands + if (ISINST (pl->line, "add") || + ISINST (pl->line, "and") || + ISINST (pl->line, "or") || + ISINST (pl->line, "sub") || + ISINST (pl->line, "xor")) + return argIs (pl->line + 3, what) || argIs (strchr (pl->line, ','), what); + if (ISINST(pl->line, "idxm")) + return argIs (pl->line + 4, what) || argIs (strchr (pl->line, ','), what); + if (ISINST (pl->line, "ceqsn") || + ISINST (pl->line, "cneqsn")) + return argIs (pl->line + 6, what) || argIs (strchr (pl->line, ','), what); + + // One-operand instructions + if (ISINST (pl->line, "neg") || + ISINST (pl->line, "not") || + ISINST (pl->line, "sl") || + ISINST (pl->line, "slc") || + ISINST (pl->line, "sr") || + ISINST (pl->line, "src")) + return argIs (pl->line + 3, what); + if (ISINST (pl->line, "dzsn") || + ISINST (pl->line, "izsn") || + ISINST (pl->line, "pcadd") || + ISINST (pl->line, "stt16")) + return argIs (pl->line + 5, what); + + // Todo: addc, subc, xch + + return true; +} + +static bool +stm8SurelyWritesFlag(const lineNode *pl, const char *what) +{ + if (ISINST (pl->line, "pop") && argIs (pl->line + 4, "af") || ISINST (pl->line, "popaf")) + return true; + + // Instructions that write all flags + if (ISINST (pl->line, "add") || + ISINST (pl->line, "addc") || + ISINST (pl->line, "ceqsn") || + ISINST (pl->line, "cneqsn") || + ISINST (pl->line, "dec") || + ISINST (pl->line, "dzsn") || + ISINST (pl->line, "inc") || + ISINST (pl->line, "izsn") || + ISINST (pl->line, "sub") || + ISINST (pl->line, "subc")) + return true; + + // Instructions that write c only + if (ISINST (pl->line, "sl") || + ISINST (pl->line, "slc") || + ISINST (pl->line, "sr") || + ISINST (pl->line, "src")) + return !strcmp(what, "c"); + + // Instructions that write z only + if (ISINST (pl->line, "and") || + ISINST (pl->line, "neg") || + ISINST (pl->line, "not") || + ISINST (pl->line, "or") || + ISINST (pl->line, "xor")) + return !strcmp(what, "z"); + + // mov writes z when the destination is a and hte source not an immediate only. + if (ISINST (pl->line, "mov") && !strcmp(what, "z") && pl->line[4] == 'a' && pl->line[5] == ',' && !strchr(pl->line, '#')) + return true; + + return false; +} + +static bool +pdkSurelyWrites(const lineNode *pl, const char *what) +{ + if (!strcmp(what, "z") || !strcmp(what, "c") || !strcmp(what, "ac") || !strcmp(what, "ov")) + return (stm8SurelyWritesFlag(pl, what)); + + if (ISINST(pl->line, "mov") || ISINST(pl->line, "idxm")) + return argIs (pl->line + 4, what); + + if (ISINST (pl->line, "pop") && argIs (pl->line + 4, "af") || ISINST (pl->line, "popaf")) + return !strcmp(what, "a"); + + // TODO: Other instructions + + // One-operand instructions that write their argument + if (ISINST (pl->line, "neg") || + ISINST (pl->line, "not") || + ISINST (pl->line, "sl") || + ISINST (pl->line, "slc") || + ISINST (pl->line, "sr") || + ISINST (pl->line, "src")) + return argIs (pl->line + 3, what); + if (ISINST (pl->line, "dzsn") || + ISINST (pl->line, "izsn") || + ISINST (pl->line, "ldt16")) + return argIs (pl->line + 5, what); + + return false; +} + + +static bool +pdkUncondJump(const lineNode *pl) +{ + return (ISINST(pl->line, "goto") || ISINST(pl->line, "pcadd")); +} + +static bool +pdkCondJump(const lineNode *pl) +{ + return (ISINST(pl->line, "ceqsn") || ISINST(pl->line, "cneqsn") || + ISINST(pl->line, "t0sn") || ISINST(pl->line, "t1sn") || + ISINST(pl->line, "izsn") || ISINST(pl->line, "dzsn")); +} + +static bool +pdkSurelyReturns(const lineNode *pl) +{ + return(ISINST(pl->line, "ret")); +} + +/*-----------------------------------------------------------------*/ +/* 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 a 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_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 *what, const char *untilOp, + lineNode **plCond) +{ + for (; *pl; *pl = (*pl)->next) + { + if (!(*pl)->line || (*pl)->isDebug || (*pl)->isComment || (*pl)->isLabel) + continue; + D(("Scanning %s for %s\n", (*pl)->line, what)); + /* don't optimize across inline assembler, + e.g. isLabel doesn't work there */ + if ((*pl)->isInline) + { + D(("S4O_ABORT at inline asm\n")); + return S4O_ABORT; + } + + if ((*pl)->visited) + { + D(("S4O_VISITED\n")); + return S4O_VISITED; + } + + (*pl)->visited = TRUE; + + if(pdkMightRead(*pl, what)) + { + D(("S4O_RD_OP\n")); + return S4O_RD_OP; + } + + // Check writes before conditional jumps, some jumps (btjf, btjt) write 'c' + if(pdkSurelyWrites(*pl, what)) + { + D(("S4O_WR_OP\n")); + return S4O_WR_OP; + } + + if(pdkUncondJump(*pl)) + { + *pl = findLabel (*pl); + if (!*pl) + { + D(("S4O_ABORT at unconditional jump\n")); + return S4O_ABORT; + } + } + if(pdkCondJump(*pl)) + { + *plCond = (*pl)->next->next; + if (!*plCond) + { + D(("S4O_ABORT at conditional jump\n")); + return S4O_ABORT; + } + D(("S4O_CONDJMP\n")); + return S4O_CONDJMP; + } + + /* Don't need to check for de, hl since stm8MightRead() does that */ + if(pdkSurelyReturns(*pl)) + { + D(("S4O_TERM\n")); + return S4O_TERM; + } + } + D(("S4O_ABORT\n")); + return S4O_ABORT; +} + +/*-----------------------------------------------------------------*/ +/* 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 *what) +{ + lineNode *plConditional; + for (;; *pl = (*pl)->next) + { + switch (scan4op (pl, what, NULL, &plConditional)) + { + case S4O_TERM: + case S4O_VISITED: + case S4O_WR_OP: + /* all these are terminating conditions */ + return true; + case S4O_CONDJMP: + /* two possible destinations: recurse */ + { + lineNode *pl2 = plConditional; + D(("CONDJMP trying other branch first\n")); + if (!doTermScan (&pl2, what)) + return false; + D(("Other branch OK.\n")); + } + continue; + case S4O_RD_OP: + default: + /* no go */ + return false; + } + } +} + +/*-----------------------------------------------------------------*/ +/* univisitLines - clear "visited" flag in all lines */ +/*-----------------------------------------------------------------*/ +static void +unvisitLines (lineNode *pl) +{ + for (; pl; pl = pl->next) + pl->visited = false; +} + +bool pdknotUsed(const char *what, lineNode *endPl, lineNode *head) +{ + lineNode *pl; + + _G.head = head; + + unvisitLines (_G.head); + + pl = endPl->next; + return (doTermScan (&pl, what)); +} + +bool pdknotUsedFrom(const char *what, const char *label, lineNode *head) +{ + lineNode *cpl; + + for (cpl = head; cpl; cpl = cpl->next) + if (cpl->isLabel && !strncmp (label, cpl->line, strlen(label))) + return (pdknotUsed (what, cpl, head)); + + return false; +} + +int +pdkinstructionSize(lineNode *pl) +{ + return 1; +} + diff --git a/src/pdk/peep.h b/src/pdk/peep.h new file mode 100644 index 0000000..9ce3a3e --- /dev/null +++ b/src/pdk/peep.h @@ -0,0 +1,6 @@ +#include <stdbool.h> + +bool pdknotUsed(const char *what, lineNode *endPl, lineNode *head); +bool pdknotUsedFrom(const char *what, const char *label, lineNode *head); +int pdkinstructionSize(lineNode *node); + diff --git a/src/pdk/peep.o b/src/pdk/peep.o Binary files differnew file mode 100644 index 0000000..50fc2b6 --- /dev/null +++ b/src/pdk/peep.o diff --git a/src/pdk/peeph.def b/src/pdk/peeph.def new file mode 100644 index 0000000..bac385a --- /dev/null +++ b/src/pdk/peeph.def @@ -0,0 +1,56 @@ +// peeph.def - PDK peephole rules + +replace restart { + mov %1, %2 +} by { + ; peephole 0 removed dead load into %1 from %2. +} if notUsed(%1) + +replace restart { + mov %1, a + mov a, %1 +} by { + mov %1, a + ; peephole 1 removed redundant load from %1 into a. +} if notVolatile(%1) + + +replace restart { + mov a, #%1 + ret +} by { + ; peephole 2 moved load to ret. + ret #%1 +} + +replace restart { + xch a, %1 + xch a, %1 +} by { + ; peephole 3 removed redundant double exchange between a and %1. +} if notVolatile(%1) + +replace restart { + mov a, #0x00 + slc a + and a, #0x01 +} by { + mov a, #0x00 + slc a + ; peephole 4 removed redundant and. +} + +replace restart { +%1: +} by { + ; peephole j0 removed unused label %1. +} if labelRefCount(%1 0) + +replace restart { + ret %1 + ret +} by { + ret %1 + ; peephole j1 removed redundant ret after ret k. +} + diff --git a/src/pdk/peeph.rul b/src/pdk/peeph.rul new file mode 100644 index 0000000..fbaeb82 --- /dev/null +++ b/src/pdk/peeph.rul @@ -0,0 +1,58 @@ +/* Generated file, DO NOT Edit! */ +/* To Make changes to rules edit */ +/* <port>/peeph.def instead. */ +"\n" +"replace restart {\n" +" mov %1, %2\n" +"} by {\n" +" ; peephole 0 removed dead load into %1 from %2.\n" +"} if notUsed(%1)\n" +"\n" +"replace restart {\n" +" mov %1, a\n" +" mov a, %1\n" +"} by {\n" +" mov %1, a\n" +" ; peephole 1 removed redundant load from %1 into a.\n" +"} if notVolatile(%1)\n" +"\n" +"\n" +"replace restart {\n" +" mov a, #%1\n" +" ret\n" +"} by {\n" +" ; peephole 2 moved load to ret.\n" +" ret #%1\n" +"}\n" +"\n" +"replace restart {\n" +" xch a, %1\n" +" xch a, %1\n" +"} by {\n" +" ; peephole 3 removed redundant double exchange between a and %1.\n" +"} if notVolatile(%1)\n" +"\n" +"replace restart {\n" +" mov a, #0x00\n" +" slc a\n" +" and a, #0x01\n" +"} by {\n" +" mov a, #0x00\n" +" slc a\n" +" ; peephole 4 removed redundant and.\n" +"}\n" +"\n" +"replace restart {\n" +"%1:\n" +"} by {\n" +" ; peephole j0 removed unused label %1.\n" +"} if labelRefCount(%1 0)\n" +"\n" +"replace restart {\n" +" ret %1\n" +" ret\n" +"} by {\n" +" ret %1\n" +" ; peephole j1 removed redundant ret after ret k.\n" +"}\n" +"\n" diff --git a/src/pdk/port.a b/src/pdk/port.a Binary files differnew file mode 100644 index 0000000..ac5f23a --- /dev/null +++ b/src/pdk/port.a diff --git a/src/pdk/ralloc.c b/src/pdk/ralloc.c new file mode 100644 index 0000000..735ae5c --- /dev/null +++ b/src/pdk/ralloc.c @@ -0,0 +1,747 @@ +#include "ralloc.h" +#include "gen.h" +#include "dbuf_string.h" + +reg_info pdk_regs[] = +{ + {REG_GPR, A_IDX, "a"}, + {REG_GPR, P_IDX, "p"}, + {REG_CND, C_IDX, "f.c"}, + {0, SP_IDX, "sp"}, +}; + +/* Flags to turn on debugging code. + */ +enum +{ + D_ALLOC = 0, +}; + +#if 1 +#define D(_a, _s) if (_a) { printf _s; fflush(stdout); } +#else +#define D(_a, _s) +#endif + +/*-----------------------------------------------------------------*/ +/* createStackSpil - create a location somewhere to spill */ +/*-----------------------------------------------------------------*/ +static symbol * +createStackSpil (symbol *sym) +{ + static int slocNum; + symbol *sloc = 0; + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "sloc%d", slocNum++); + sloc = newiTemp (dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + + /* set the type to the spilling symbol */ + sloc->type = copyLinkChain (sym->type); + sloc->etype = getSpec (sloc->type); + SPEC_SCLS (sloc->etype) = S_DATA; + SPEC_EXTR (sloc->etype) = 0; + SPEC_STAT (sloc->etype) = 0; + SPEC_VOLATILE(sloc->etype) = 0; + SPEC_ABSA(sloc->etype) = 0; + + allocLocal (sloc); + + sloc->isref = 1; + + /* if it is on the stack then update the stack */ + if (IN_STACK (sloc->etype)) + currFunc->stack += getSize (sloc->type); + + sym->usl.spillLoc = sloc; + sym->stackSpil = 1; + + return sym; +} + +/*-----------------------------------------------------------------*/ +/* spillThis - spils a specific operand */ +/*-----------------------------------------------------------------*/ +void +pdkSpillThis (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); + + sym->isspilt = sym->spillA = 1; + + for (i = 0; i < sym->nRegs; i++) + if (sym->regs[i]) + sym->regs[i] = 0; + + if (sym->usl.spillLoc && !sym->remat) + sym->usl.spillLoc->allocreq++; + return; +} + +/*-----------------------------------------------------------------*/ +/* regTypeNum - computes the type & number of registers required */ +/*-----------------------------------------------------------------*/ +static void +regTypeNum (void) +{ + symbol *sym; + int k; + + /* for each live range do */ + for (sym = hTabFirstItem (liveRanges, &k); sym; sym = hTabNextItem (liveRanges, &k)) + { + /* if used zero times then no registers needed. Exception: Variables larger than 2 bytes - these might need a spill location when they are return values */ + if ((sym->liveTo - sym->liveFrom) == 0 && getSize (sym->type) <= 2) + continue; + + D (D_ALLOC, ("regTypeNum: loop on sym %p\n", sym)); + + /* if the live range is a temporary */ + if (sym->isitmp) + { + /* if the type is marked as a conditional */ + if (sym->regType == REG_CND) + continue; + + /* if used in return only then we don't + need registers */ + if (sym->ruonly || sym->accuse) + { + if (IS_AGGREGATE (sym->type) || sym->isptr) + sym->type = aggrToPtr (sym->type, FALSE); + continue; + } + + /* if not then we require registers */ + D (D_ALLOC, + ("regTypeNum: isagg %u nRegs %u type %p\n", IS_AGGREGATE (sym->type) || sym->isptr, sym->nRegs, sym->type)); + sym->nRegs = + ((IS_AGGREGATE (sym->type) + || sym->isptr) ? getSize (sym->type = aggrToPtr (sym->type, FALSE)) : getSize (sym->type)); + D (D_ALLOC, ("regTypeNum: setting nRegs of %s (%p) to %u\n", sym->name, sym, sym->nRegs)); + + D (D_ALLOC, ("regTypeNum: setup to assign regs sym %p\n", sym)); + + if (sym->nRegs > 8) + { + fprintf (stderr, "allocated more than 8 registers for type "); + printTypeChain (sym->type, stderr); + fprintf (stderr, "\n"); + } + + /* determine the type of register required */ + /* Always general purpose */ + sym->regType = REG_GPR; + } + else + { + /* for the first run we don't provide */ + /* registers for true symbols we will */ + /* see how things go */ + D (D_ALLOC, ("regTypeNum: #2 setting num of %p to 0\n", sym)); + sym->nRegs = 0; + } + } +} + +/** Transform weird SDCC handling of writes via pointers + into something more sensible. */ +static void +transformPointerSet (eBBlock **ebbs, int count) +{ + /* for all blocks */ + for (int i = 0; i < count; i++) + { + iCode *ic; + + /* for all instructions do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + if (POINTER_SET (ic)) + { + IC_LEFT (ic) = IC_RESULT (ic); + IC_RESULT (ic) = 0; + ic->op = SET_VALUE_AT_ADDRESS; + } + } +} + +/** 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; + + /* Avoid having multiple named address spaces in one iCode. */ + if (IS_SYMOP (IC_RESULT (ic)) && SPEC_ADDRSPACE (OP_SYMBOL (IC_RESULT (ic))->etype)) + return 0; + + /* find the definition of iTempNN scanning backwards if we find a + a use of the true symbol in before we find the definition then + we cannot */ + for (dic = ic->prev; dic; dic = dic->prev) + { + /* PENDING: Don't pack across function calls. */ + if (dic->op == CALL || dic->op == PCALL) + { + 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) + { + 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 (!dic) + return 0; /* did not find */ + + /* Avoid having a result in a sfr for shifts. */ + if (IS_SYMOP (IC_RESULT (ic)) && IN_REGSP (SPEC_OCLS (OP_SYMBOL (IC_RESULT (ic))->etype)) && (dic->op == LEFT_OP || dic->op == RIGHT_OP)) + return 0; + + /* if assignment then check that right is not a bit */ + if (ic->op == '=') + { + 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 as at least 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 in between 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); + 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; + } + + remiCodeFromeBBlock (ebp, ic); + // PENDING: Check vs mcs51 + 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); + return 1; +} + +/** Will reduce some registers for single use. + */ +static int +packRegsForOneuse (iCode *ic, operand **opp, eBBlock *ebp) +{ + iCode *dic; + + operand *op = *opp; + + /* if returning a literal then do nothing */ + if (!IS_ITEMP (op)) + return 0; + + /* if rematerializable do nothing */ + if (OP_SYMBOL (op)->remat) + return 0; + + /* 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 */ + if (bitVectnBitsOn (OP_USES (op)) != 1 || bitVectnBitsOn (OP_DEFS (op)) != 1) + return 0; + + /* get the definition */ + if (!(dic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (op))))) + return 0; + + /* found the definition now check if it is local */ + if (dic->seq < ebp->fSeq || dic->seq > ebp->lSeq) + return 0; /* non-local */ + + /* for now handle results from assignments from globals only */ + if (!(dic->op == '=' || dic->op == CAST && SPEC_USIGN (getSpec (operandType (IC_RIGHT (dic)))) && operandSize (op) > operandSize (IC_RIGHT (dic))) + || !isOperandGlobal (IC_RIGHT (dic))) + return 0; + + if (IS_OP_VOLATILE (IC_RESULT (ic)) && IS_OP_VOLATILE (IC_RIGHT (dic))) + return 0; + + /* also make sure the intervenening instructions + don't have any thing in far space */ + for (iCode *nic = dic->next; nic && nic != ic; nic = nic->next) + { + /* if there is an intervening function call then no */ + if (nic->op == CALL || nic->op == PCALL) + return 0; + + if (nic->op == SET_VALUE_AT_ADDRESS) + return 0; + + /* if address of & the result is remat, then okay */ + if (nic->op == ADDRESS_OF && OP_SYMBOL (IC_RESULT (nic))->remat) + continue; + + if (IS_OP_VOLATILE (IC_LEFT (nic)) || + IS_OP_VOLATILE (IC_RIGHT (nic)) || + isOperandGlobal (IC_RESULT (nic))) + return 0; + } + + /* Optimize out the assignment */ + *opp = operandFromOperand (IC_RIGHT(dic)); + (*opp)->isaddr = true; + + bitVectUnSetBit (OP_SYMBOL (op)->defs, dic->key); + bitVectUnSetBit (OP_SYMBOL (op)->uses, ic->key); + + if (IS_ITEMP (IC_RESULT (dic)) && OP_SYMBOL (IC_RESULT (dic))->liveFrom > dic->seq) + OP_SYMBOL (IC_RESULT (dic))->liveFrom = dic->seq; + + /* delete from liverange table also + delete from all the points in between and the new + one */ + for (iCode *nic = dic; nic != ic; nic = nic->next) + bitVectUnSetBit (nic->rlive, op->key); + + remiCodeFromeBBlock (ebp, dic); + + hTabDeleteItem (&iCodehTab, dic->key, ic, DELETE_ITEM, NULL); + + return 1; +} + +/** Does some transformations to reduce register pressure. + */ +static void +packRegisters (eBBlock * ebp) +{ + iCode *ic; + int change = 0; + + D (D_ALLOC, ("packRegisters: entered.\n")); + + for(;;) + { + 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 == '=') + change += packRegsForAssign (ic, ebp); + } + if (!change) + break; + } + + for (ic = ebp->sch; ic; ic = ic->next) + { + D (D_ALLOC, ("packRegisters: looping on ic %p\n", ic)); + + /* Safe: address of a true sym is always constant. */ + /* if this is an itemp & result of a address of a true sym + then mark this as rematerialisable */ + if (ic->op == ADDRESS_OF && + IS_ITEMP (IC_RESULT (ic)) && bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && !IS_PARM (IC_RESULT (ic)) /* The receiving of the parameter is not accounted for in DEFS */ && + IS_TRUE_SYMOP (IC_LEFT (ic)) && !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; + } + + /* Safe: just propagates the remat flag */ + /* if straight assignment then carry remat flag if this is the + only definition */ + if (ic->op == '=' && IS_SYMOP (IC_RIGHT (ic)) && OP_SYMBOL (IC_RIGHT (ic))->remat && + !isOperandGlobal (IC_RESULT (ic)) && bitVectnBitsOn (OP_SYMBOL (IC_RESULT (ic))->defs) == 1 && !IS_PARM (IC_RESULT (ic)) && /* The receiving of the parameter is not accounted for in DEFS */ + !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 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 && + !isOperandGlobal (IC_RESULT (ic)) && bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && !IS_PARM (IC_RESULT (ic)) && /* The receiving of the paramter is not accounted for in DEFS */ + !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_INT (to_type)) && IS_PTR (from_type)) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + } + + /* if this is a +/- operation with a rematerizable + then mark this as rematerializable as well */ + if ((ic->op == '+' || ic->op == '-') && + (IS_SYMOP (IC_LEFT (ic)) && + IS_ITEMP (IC_RESULT (ic)) && + IS_OP_LITERAL (IC_RIGHT (ic))) && + OP_SYMBOL (IC_LEFT (ic))->remat && + (!IS_SYMOP (IC_RIGHT (ic)) || !IS_CAST_ICODE (OP_SYMBOL (IC_RIGHT (ic))->rematiCode)) && + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + + /* In some cases redundant moves can be eliminated */ + if (ic->op == GET_VALUE_AT_ADDRESS || ic->op == SET_VALUE_AT_ADDRESS || + ic->op == '+' || ic->op == '-' || ic->op == UNARYMINUS || + ic->op == '|' || ic->op == '&' || ic->op == '^' || + ic->op == EQ_OP || ic->op == NE_OP || + ic->op == IFX && operandSize (IC_COND (ic)) == 1 || + ic->op == IPUSH && operandSize (IC_LEFT (ic)) == 1 || + ic->op == LEFT_OP || ic->op == RIGHT_OP) + packRegsForOneuse (ic, &(IC_LEFT (ic)), ebp); + if (ic->op == '+' || ic->op == '-' || + ic->op == '|' || ic->op == '&' || ic->op == '^' || + ic->op == EQ_OP || ic->op == NE_OP || + ic->op == LEFT_OP || ic->op == RIGHT_OP) + packRegsForOneuse (ic, &(IC_RIGHT (ic)), ebp); + + // Optimize out some unsigned upcasts. + if (ic->op == CAST && IS_ITEMP (IC_RESULT (ic)) && !IS_OP_VOLATILE (IC_RIGHT (ic)) && + bitVectnBitsOn (OP_USES (IC_RESULT (ic))) == 1 && hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_USES (IC_RESULT (ic)))) == ic->next && + SPEC_USIGN (getSpec (operandType (IC_RIGHT (ic)))) && operandSize (IC_RESULT (ic)) >= operandSize (IC_RIGHT (ic)) && !IS_BOOL (operandType (IC_RESULT (ic)))) + { + iCode *use = ic->next; + operand *op = IC_RESULT (ic); + if ((use->op == LEFT_OP || use->op == '+' || use->op == '-' || use->op == UNARYMINUS || + use->op == '&' || use->op == '|' || use->op == '^') && + IC_LEFT (use)->key == op->key && (!IC_RIGHT(use) || IC_RIGHT (use)->key != op->key)) + { + bitVectUnSetBit (OP_SYMBOL (IC_RIGHT (ic))->uses, ic->key); + bitVectSetBit (OP_SYMBOL (IC_RIGHT (ic))->uses, use->key); + IC_LEFT (use) = operandFromOperand (IC_RIGHT(ic)); + remiCodeFromeBBlock (ebp, ic); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + if(ic->prev) + ic = ic->prev; + } + else if (((use->op == SET_VALUE_AT_ADDRESS && !IS_BITVAR (getSpec (operandType (IC_LEFT (use)))) && !IS_BITVAR (getSpec (operandType (IC_RIGHT (use))))) || + use->op == CAST && (SPEC_USIGN (getSpec (operandType (IC_RIGHT (use)))) || operandSize (IC_RESULT (use)) <= operandSize (IC_RIGHT (use))) || + use->op == LEFT_OP || use->op == RIGHT_OP || use->op == '+' || use->op == '-' || + use->op == '&' || use->op == '|' || use->op == '^') && + IC_RIGHT (use)->key == op->key && (!IC_LEFT(use) || IC_LEFT (use)->key != op->key)) + { + bitVectUnSetBit (OP_SYMBOL (IC_RIGHT (ic))->uses, ic->key); + bitVectSetBit (OP_SYMBOL (IC_RIGHT (ic))->uses, use->key); + IC_RIGHT (use) = operandFromOperand (IC_RIGHT(ic)); + remiCodeFromeBBlock (ebp, ic); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + if(ic->prev) + ic = ic->prev; + } + } + } +} + +/** + Mark variables for assignment by the register allocator. + */ +static void +serialRegMark (eBBlock **ebbs, int count) +{ + int i; + short int max_alloc_bytes = SHRT_MAX; // Byte limit. Set this to a low value to pass only few variables to the register allocator. This can be useful for debugging. + + D (D_ALLOC, ("serialRegMark for %s, currFunc->stack %d\n", currFunc->name, currFunc->stack)); + + /* 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) + { + if (ic->op == IPOP) + wassert (0); + + /* 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++; + + /* some don't need registers, since there is no result. */ + if (SKIP_IC2 (ic) || + ic->op == JUMPTABLE || ic->op == IFX || ic->op == IPUSH || ic->op == IPOP || ic->op == SET_VALUE_AT_ADDRESS) + continue; + + /* now we need to allocate registers only for the result */ + if (IC_RESULT (ic)) + { + symbol *sym = OP_SYMBOL (IC_RESULT (ic)); + + D (D_ALLOC, ("serialRegAssign: in loop on result %p %s\n", sym, sym->name)); + + if (sym->isspilt && sym->usl.spillLoc) // todo: Remove once remat is supported! + { + sym->usl.spillLoc->allocreq--; + sym->isspilt = FALSE; + } + + /* Make sure any spill location is definately 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 marked for the new allocator + or will not live beyond this instructions */ + if (!sym->nRegs || + sym->isspilt || sym->for_newralloc || sym->liveTo <= ic->seq && (sym->nRegs <= 2 || ic->op != CALL && ic->op != PCALL)) + { + D (D_ALLOC, ("serialRegMark: won't live long enough.\n")); + continue; + } + + if (sym->usl.spillLoc && !sym->usl.spillLoc->_isparm) // I have no idea where these spill locations come from. Sometime two symbols even have the same spill location, whic tends to mess up stack allocation. THose that come from previous iterations in this loop would be okay, but those from outside are a problem. + { + sym->usl.spillLoc = 0; + sym->isspilt = false; + } + + if (sym->nRegs > 2 && ic->op == CALL) + { + sym->for_newralloc = 0; + pdkSpillThis (sym); + } + else if (max_alloc_bytes >= sym->nRegs) + { + sym->for_newralloc = 1; + max_alloc_bytes -= sym->nRegs; + } + else if (!sym->for_newralloc) + { + pdkSpillThis (sym); + printf ("Spilt %s due to byte limit.\n", sym->name); + } + } + } + } +} + +/*------------------------------------------------------------------*/ +/* verifyRegsAssigned - make sure an iTemp is properly initialized; */ +/* it should either have registers or have been 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; + int i; + bool completely_in_regs; + + if (!op) + return; + if (!IS_ITEMP (op)) + return; + + sym = OP_SYMBOL (op); + + if (sym->regType == REG_CND) + return; + + if (sym->isspilt && !sym->remat && sym->usl.spillLoc && !sym->usl.spillLoc->allocreq) + sym->usl.spillLoc->allocreq++; + + if (sym->isspilt) + return; + + for(i = 0, completely_in_regs = true; i < sym->nRegs; i++) + if (!sym->regs[i]) + completely_in_regs = false; + if (completely_in_regs) + return; + + pdkSpillThis (sym); +} + +void +pdkRegFix (eBBlock ** ebbs, int count) +{ + int i; + + /* Check for and fix any problems with uninitialized operands */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && (ebbs[i]->entryLabel != entryLabel && ebbs[i]->entryLabel != returnLabel)) + continue; + + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + 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); + } + } +} + +/*-----------------------------------------------------------------*/ +/* assignRegisters - assigns registers to each live range as need */ +/*-----------------------------------------------------------------*/ +void +pdk_assignRegisters (ebbIndex *ebbi) +{ + eBBlock **ebbs = ebbi->bbOrder; + int count = ebbi->count; + iCode *ic; + + pdk_init_asmops(); + + transformPointerSet (ebbs, count); + + /* change assignments this will remove some + live ranges reducing some register pressure */ + for (int 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 (); + + /* Mark variables for assignment by the new allocator */ + serialRegMark (ebbs, count); + + /* Invoke optimal register allocator */ + ic = pdk_ralloc2_cc (ebbi); + + /* redo offsets for stacked automatic variables */ + if (currFunc) + { + redoStackOffsets (); + } + + if (options.dump_i_code) + { + dumpEbbsToFileExt (DUMP_RASSGN, ebbi); + dumpLiveRanges (DUMP_LRANGE, liveRanges); + } + + genPdkCode (ic); +} + diff --git a/src/pdk/ralloc.h b/src/pdk/ralloc.h new file mode 100644 index 0000000..bef02fa --- /dev/null +++ b/src/pdk/ralloc.h @@ -0,0 +1,64 @@ +/*------------------------------------------------------------------------- + + ralloc.h - header file register allocation + + Written By - Philipp Krause . pkk@spth.de (2018) + + 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 SDCCRALLOC_H +#define SDCCRALLOC_H 1 + +#include "common.h" + +enum +{ + A_IDX = 0, // The accumulator + P_IDX, // A memory location used as pseudoregister + C_IDX, // Implicit condition operand. + SP_IDX // SP - for use with debug info. +}; + +enum +{ + REG_GPR = 2, + REG_CND = 4, +}; + +/* definition for the registers */ +typedef struct reg_info +{ + short type; /* can have value + REG_GPR, REG_PTR or REG_CND */ + short rIdx; /* index into register table */ + char *name; /* name */ +} reg_info; + +extern reg_info pdk_regs[]; + +void pdk_assignRegisters (ebbIndex *); + +void pdkSpillThis (symbol *sym); +iCode *pdk_ralloc2_cc(ebbIndex *ebbi); + +void pdkRegFix (eBBlock **ebbs, int count); + +#endif + diff --git a/src/pdk/ralloc.o b/src/pdk/ralloc.o Binary files differnew file mode 100644 index 0000000..c71d394 --- /dev/null +++ b/src/pdk/ralloc.o diff --git a/src/pdk/ralloc2.cc b/src/pdk/ralloc2.cc new file mode 100644 index 0000000..dac7386 --- /dev/null +++ b/src/pdk/ralloc2.cc @@ -0,0 +1,692 @@ +// Philipp Klaus Krause, philipp@informatik.uni-frankfurt.de, pkk@spth.de, 2010 - 2018 +// +// 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. + +// #define DEBUG_RALLOC_DEC // Uncomment to get debug messages while doing register allocation on the tree decomposition. +// #define DEBUG_RALLOC_DEC_ASS // Uncomment to get debug messages about assignments while doing register allocation on the tree decomposition (much more verbose than the one above). + +#include "SDCCralloc.hpp" +#include "SDCCsalloc.hpp" + +extern "C" +{ + #include "ralloc.h" + #include "gen.h" + float dryPdkiCode (iCode *ic); + bool pdk_assignment_optimal; +} + +#define REG_A 0 +#define REG_P 1 + +template <class I_t> +static void add_operand_conflicts_in_node(const cfg_node &n, I_t &I) +{ + const iCode *ic = n.ic; + + const operand *result = IC_RESULT(ic); + const operand *left = IC_LEFT(ic); + const operand *right = IC_RIGHT(ic); + + if(!result || !IS_SYMOP(result)) + return; + + // Todo: More fine-grained control for these. + if (!(ic->op == '-' || ic->op == UNARYMINUS && !IS_FLOAT (operandType (left)) || ic->op == '~' || + ic->op == '^' || ic->op == '|' || ic->op == BITWISEAND)) + return; + + operand_map_t::const_iterator oir, oir_end, oirs; + boost::tie(oir, oir_end) = n.operands.equal_range(OP_SYMBOL_CONST(result)->key); + if(oir == oir_end) + return; + + operand_map_t::const_iterator oio, oio_end; + + if(left && IS_SYMOP(left)) + for(boost::tie(oio, oio_end) = n.operands.equal_range(OP_SYMBOL_CONST(left)->key); oio != oio_end; ++oio) + for(oirs = oir; oirs != oir_end; ++oirs) + { + var_t rvar = oirs->second; + var_t ovar = oio->second; + if(I[rvar].byte < I[ovar].byte) + boost::add_edge(rvar, ovar, I); + } + + if(right && IS_SYMOP(right)) + for(boost::tie(oio, oio_end) = n.operands.equal_range(OP_SYMBOL_CONST(right)->key); oio != oio_end; ++oio) + for(oirs = oir; oirs != oir_end; ++oirs) + { + var_t rvar = oirs->second; + var_t ovar = oio->second; + if(I[rvar].byte < I[ovar].byte) + boost::add_edge(rvar, ovar, I); + } +} + +// Return true, iff the operand is placed (partially) in r. +template <class G_t> +static bool operand_in_reg(const operand *o, reg_t r, const i_assignment_t &ia, unsigned short int i, const G_t &G) +{ + if(!o || !IS_SYMOP(o)) + return(false); + + if(r >= port->num_regs) + return(false); + + operand_map_t::const_iterator oi, oi_end; + for(boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); oi != oi_end; ++oi) + if(oi->second == ia.registers[r][1] || oi->second == ia.registers[r][0]) + return(true); + + return(false); +} + +// Return true, iff the operand is placed in a reg. +template <class G_t> +static bool operand_byte_in_reg(const operand *o, int offset, reg_t r, const assignment &a, unsigned short int i, const G_t &G) +{ + if(!o || !IS_SYMOP(o)) + return(false); + + operand_map_t::const_iterator oi, oi2, oi3, oi_end; + + for(boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); offset && oi != oi_end; offset--, oi++); + + if(oi == oi_end) + return(false); + + return(a.global[oi->second] == r); +} + +template <class G_t, class I_t> +static bool Ainst_ok(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + const i_assignment_t &ia = a.i_assignment; + + if(ia.registers[REG_A][1] < 0) + return(true); // Register a not in use. + + if(ic->op == GOTO || ic->op == LABEL) + return(true); + + const operand *left = IC_LEFT(ic); + const operand *right = IC_RIGHT(ic); + const operand *result = IC_RESULT(ic); + + bool result_in_A = operand_in_reg(result, REG_A, ia, i, G); + bool left_in_A = operand_in_reg(left, REG_A, ia, i, G); + bool right_in_A = operand_in_reg(right, REG_A, ia, i, G); + + const cfg_dying_t &dying = G[i].dying; + + bool dying_A = result_in_A || dying.find(ia.registers[REG_A][1]) != dying.end() || dying.find(ia.registers[REG_A][0]) != dying.end(); + + bool result_dir = IS_TRUE_SYMOP (result) || IS_ITEMP (result) && !(options.stackAuto || reentrant) && !result_in_A; + bool left_dir = IS_TRUE_SYMOP (left) || IS_ITEMP (left) && !(options.stackAuto || reentrant) && !left_in_A; + bool right_dir = IS_TRUE_SYMOP (right) || IS_ITEMP (right) && !(options.stackAuto || reentrant) && !right_in_A; + + if(result && IS_ITEMP(result) && OP_SYMBOL_CONST(result)->remat && !operand_in_reg(result, REG_A, ia, i, G) && !operand_in_reg(result, REG_P, ia, i, G)) + return(true); + + if (ic->op == DUMMY_READ_VOLATILE || ic->op == '=') + return(true); + + if ((ic->op == EQ_OP || ic->op == NE_OP) && dying_A && + (left_in_A && (right_dir || IS_OP_LITERAL(right) || IS_ITEMP(right) && OP_SYMBOL_CONST(right)->remat) || + right_in_A && (left_dir || IS_OP_LITERAL(left) || IS_ITEMP(left) && OP_SYMBOL_CONST(left)->remat))) + return (true); + + if ((ic->op == EQ_OP || ic->op == NE_OP || (ic->op == '>' || ic->op == '<') && SPEC_USIGN(getSpec(operandType(left)))) && // Non-destructive comparison. + (left_in_A && getSize(operandType(left)) == 1 && (IS_OP_LITERAL(right) || right_dir) || right_in_A && getSize(operandType(right)) == 1 && (IS_OP_LITERAL(left) || left_dir))) + return (true); + + if (ic->op == IFX && (dying_A || left_in_A)) + return (true); + + if (ic->op == '<' && IS_OP_LITERAL(right) && !ullFromVal(OP_VALUE_CONST (right)) && + (operand_byte_in_reg(left, getSize(operandType(left)) - 1, REG_A, a, i, G) || operand_byte_in_reg(left, getSize(operandType(left)) - 1, REG_P, a, i, G))) + return (true); + + if (ic->op == SET_VALUE_AT_ADDRESS && getSize(operandType(right)) == 1 && left_dir && right_in_A) + return (true); + if (ic->op == SET_VALUE_AT_ADDRESS && IS_ITEMP(left) && OP_SYMBOL_CONST(left)->remat && !operand_in_reg(left, REG_A, ia, i, G)) + return (true); + + if ((ic->op == '+' || ic->op == '-' || ic->op == UNARYMINUS) && (!left_in_A && !right_in_A || getSize(operandType(result)) == 1)) + return (true); + + if ((ic->op == CALL || ic->op == PCALL) && !left_in_A) + return(true); + + if (ic->op == RETURN || + (ic->op == GET_VALUE_AT_ADDRESS && getSize(operandType(result)) <= 2 || ic->op == SET_VALUE_AT_ADDRESS && getSize(operandType(right)) == 1) && dying_A) + return(true); + + if (ic->op == GET_VALUE_AT_ADDRESS && !left_in_A) + return(true); + + if (ic->op == CAST && + (getSize(operandType(result)) == 1 || getSize(operandType(result)) == 2 && SPEC_USIGN (getSpec(operandType(right))) && operand_byte_in_reg(result, 1, REG_P, a, i, G)) && + right_in_A && (result_dir || dying_A)) + return (true); + + if ((ic->op == CAST && (getSize(operandType(result)) <= getSize(operandType(right)) || SPEC_USIGN(getSpec(operandType(right))))) && + getSize(operandType(result)) <= 2 && + (result_dir && dying_A || result_in_A && right_dir || result_in_A && right_in_A)) + return (true); + + if ((ic->op == LEFT_OP || ic->op == RIGHT_OP)) + return(IS_OP_LITERAL(right) || right_in_A && !result_in_A); + + if (ic->op == '^' && + (operand_byte_in_reg(result, 0, REG_A, a, i, G) && (operand_byte_in_reg(left, 0, REG_A, a, i, G) || operand_byte_in_reg(right, 0, REG_A, a, i, G)) || + operand_byte_in_reg(result, 1, REG_A, a, i, G) && (operand_byte_in_reg(left, 1, REG_A, a, i, G) || operand_byte_in_reg(right, 1, REG_A, a, i, G)))) + return (true); + + // For most operations only allow lower byte in a for now (upper byte for result). + if (left_in_A && !operand_byte_in_reg(left, 0, REG_A, a, i, G) || right_in_A && !operand_byte_in_reg(right, 0, REG_A, a, i, G) || + ic->op != '+' && ic->op != '-' && ic->op != UNARYMINUS && result_in_A && !operand_byte_in_reg(result, getSize(operandType(result)) - 1, REG_A, a, i, G)) + return(false); + + if ((ic->op == GET_VALUE_AT_ADDRESS || ic->op == SET_VALUE_AT_ADDRESS) && (left_in_A || right_in_A)) + return(false); + + if(dying_A) + return(true); + + return(false); +} + +template <class G_t, class I_t> +static bool Pinst_ok(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + const i_assignment_t &ia = a.i_assignment; + + if(ia.registers[REG_P][1] < 0) + return(true); // Pseudoregister p not in use. + + const operand *left = IC_LEFT(ic); + const operand *right = IC_RIGHT(ic); + const operand *result = IC_RESULT(ic); + + bool result_in_P = operand_in_reg(result, REG_P, ia, i, G); + bool left_in_P = operand_in_reg(left, REG_P, ia, i, G); + bool right_in_P = operand_in_reg(right, REG_P, ia, i, G); + + bool left_in_A = operand_in_reg(left, REG_A, ia, i, G); + bool right_in_A = operand_in_reg(right, REG_A, ia, i, G); + + const cfg_dying_t &dying = G[i].dying; + + bool dying_P = result_in_P || dying.find(ia.registers[REG_P][1]) != dying.end() || dying.find(ia.registers[REG_P][0]) != dying.end(); + + bool left_stack = IS_ITEMP (left) && (options.stackAuto || reentrant) && !left_in_A && !left_in_P; + bool right_stack = IS_ITEMP (right) && (options.stackAuto || reentrant) && !right_in_A && !right_in_P; + + if(result && IS_ITEMP(result) && OP_SYMBOL_CONST(result)->remat && !operand_in_reg(result, REG_A, ia, i, G) && !operand_in_reg(result, REG_P, ia, i, G)) + return(true); + + // Arithmetic uses p internally for literal operands with multiple nonzero bytes. + if ((ic->op == '+' || ic->op == '-' || ic->op == '!' || ic->op == '<' || ic->op == '>') && (IS_OP_LITERAL(left) || IS_OP_LITERAL(right))) + { + const operand *const litop = IS_OP_LITERAL(left) ? left : right; + if ((ullFromVal(OP_VALUE_CONST (litop)) & 0x000000ffull) && (ullFromVal(OP_VALUE_CONST(litop)) & 0x0000ff00ull) && (ullFromVal(OP_VALUE_CONST (litop)) & 0x00ff0000ull) && (ullFromVal(OP_VALUE_CONST (litop)) & 0xff000000ull)) + return(false); + } + if (ic->op == PCALL || ic->op == IPUSH) + return(false); + + if (ic->op == CALL && !dying_P) + return(false); + + if (ic->op == GET_VALUE_AT_ADDRESS && !dying_P && !(left_in_P && getSize(operandType(result)) == 1)) + return(false); + + if ((ic->op == '+' || ic->op == '-' || ic->op == '^' || ic->op == '|' || ic->op == BITWISEAND) && + (left_stack || right_stack) && (!dying_P || left_stack || right_stack)) + return(false); + + return(true); +} + +template <class G_t, class I_t> +static void set_surviving_regs(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + iCode *ic = G[i].ic; + + bitVectClear(ic->rMask); + bitVectClear(ic->rSurv); + + cfg_alive_t::const_iterator v, v_end; + for (v = G[i].alive.begin(), v_end = G[i].alive.end(); v != v_end; ++v) + { + if(a.global[*v] < 0) + continue; + ic->rMask = bitVectSetBit(ic->rMask, a.global[*v]); + + if(!(IC_RESULT(ic) && IS_SYMOP(IC_RESULT(ic)) && OP_SYMBOL_CONST(IC_RESULT(ic))->key == I[*v].v)) + if(G[i].dying.find(*v) == G[i].dying.end()) + ic->rSurv = bitVectSetBit(ic->rSurv, a.global[*v]); + } +} + +template <class G_t, class I_t> +static void assign_operand_for_cost(operand *o, const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + if(!o || !IS_SYMOP(o)) + return; + symbol *sym = OP_SYMBOL(o); + operand_map_t::const_iterator oi, oi_end; + for(boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); oi != oi_end; ++oi) + { + var_t v = oi->second; + if(a.global[v] >= 0) + { + sym->regs[I[v].byte] = pdk_regs + a.global[v]; + sym->nRegs = I[v].size; + } + else + { + sym->regs[I[v].byte] = 0; + sym->nRegs = I[v].size; + } + } +} + +template <class G_t, class I_t> +static void assign_operands_for_cost(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + + if(ic->op == IFX) + assign_operand_for_cost(IC_COND(ic), a, i, G, I); + else if(ic->op == JUMPTABLE) + assign_operand_for_cost(IC_JTCOND(ic), a, i, G, I); + else + { + assign_operand_for_cost(IC_LEFT(ic), a, i, G, I); + assign_operand_for_cost(IC_RIGHT(ic), a, i, G, I); + assign_operand_for_cost(IC_RESULT(ic), a, i, G, I); + } + + if(ic->op == SEND && ic->builtinSEND) + assign_operands_for_cost(a, (unsigned short)*(adjacent_vertices(i, G).first), G, I); +} + +// Check that the operand is either fully in registers or fully in memory. Todo: Relax this once code generation can handle partially spilt variables! +template <class G_t, class I_t> +static bool operand_sane(const operand *o, const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + if(!o || !IS_SYMOP(o)) + return(true); + + operand_map_t::const_iterator oi, oi_end; + boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); + + if(oi == oi_end) + return(true); + + // In registers. + if(std::binary_search(a.local.begin(), a.local.end(), oi->second)) + { + while(++oi != oi_end) + if(!std::binary_search(a.local.begin(), a.local.end(), oi->second)) + return(false); + } + else + { + while(++oi != oi_end) + if(std::binary_search(a.local.begin(), a.local.end(), oi->second)) + return(false); + } + + return(true); +} + +template <class G_t, class I_t> +static bool inst_sane(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + + return(operand_sane(IC_RESULT(ic), a, i, G, I) && operand_sane(IC_LEFT(ic), a, i, G, I) && operand_sane(IC_RIGHT(ic), a, i, G, I)); +} + +// Cost function. +template <class G_t, class I_t> +static float instruction_cost(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + iCode *ic = G[i].ic; + float c; + + wassert(TARGET_PDK_LIKE); + wassert(ic); + + if(!inst_sane(a, i, G, I)) + return(std::numeric_limits<float>::infinity()); + +#if 0 + std::cout << "Calculating at cost at ic " << ic->key << ", op " << ic->op << " for: "; + print_assignment(a); + std::cout << "\n"; + std::cout.flush(); +#endif + + if(ic->generated) + { +#if 0 + std::cout << "Skipping, already generated.\n"; +#endif + return(0.0f); + } + + if(!Ainst_ok(a, i, G, I)) + return(std::numeric_limits<float>::infinity()); + + if(!Pinst_ok(a, i, G, I)) + return(std::numeric_limits<float>::infinity()); + + switch(ic->op) + { + // Register assignment doesn't matter for these: + case FUNCTION: + case ENDFUNCTION: + case LABEL: + case GOTO: + case INLINEASM: +#if 0 + std::cout << "Skipping, indepent from assignment.\n"; +#endif + return(0.0f); + case '!': + case '~': + case UNARYMINUS: + case '+': + case '-': + case '^': + case '|': + case BITWISEAND: + case IPUSH: + //case IPOP: + case CALL: + case PCALL: + case RETURN: + case '*': + case '/': + case '%': + case '>': + case '<': + case LE_OP: + case GE_OP: + case EQ_OP: + case NE_OP: + case AND_OP: + case OR_OP: + //case GETABIT: + //case GETBYTE: + //case GETWORD: + case LEFT_OP: + case RIGHT_OP: + case GET_VALUE_AT_ADDRESS: + case SET_VALUE_AT_ADDRESS: + case '=': + case IFX: + case ADDRESS_OF: + case JUMPTABLE: + case CAST: + /*case RECEIVE: + case SEND:*/ + case DUMMY_READ_VOLATILE: + /*case CRITICAL: + case ENDCRITICAL:*/ + case SWAP: + assign_operands_for_cost(a, i, G, I); + set_surviving_regs(a, i, G, I); + c = dryPdkiCode(ic); + + if (IC_RESULT (ic) && IS_ITEMP (IC_RESULT(ic)) && !OP_SYMBOL_CONST(IC_RESULT(ic))->remat && // Nudge towards saving RAM space. TODO: Do this in a better way, so it works for all backends! + !operand_in_reg(IC_RESULT(ic), REG_A, a.i_assignment, i, G) && !operand_in_reg(IC_RESULT(ic), REG_P, a.i_assignment, i, G)) + c += 0.0001; + + ic->generated = false; +#if 0 + std::cout << "Got cost " << c << "\n"; +#endif + return(c); + default: + return(0.0f); + } +} + +// For early removal of assignments that cannot be extended to valid assignments. This is just a dummy for now. +template <class G_t, class I_t> +static bool assignment_hopeless(const assignment &a, unsigned short int i, const G_t &G, const I_t &I, const var_t lastvar) +{ + return(false); +} + +// Increase chance of finding good compatible assignments at join nodes. +template <class T_t> +static void get_best_local_assignment_biased(assignment &a, typename boost::graph_traits<T_t>::vertex_descriptor t, const T_t &T) +{ + a = *T[t].assignments.begin(); + + std::set<var_t>::const_iterator vi, vi_end; + varset_t newlocal; + std::set_union(T[t].alive.begin(), T[t].alive.end(), a.local.begin(), a.local.end(), std::inserter(newlocal, newlocal.end())); + a.local = newlocal; +} + +// Suggest to honor register keyword. +template <class G_t, class I_t> +static float rough_cost_estimate(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const i_assignment_t &ia = a.i_assignment; + float c = 0.0f; + + if(ia.registers[REG_A][1] < 0) + c += 0.05f; + + varset_t::const_iterator v, v_end; + for(v = a.local.begin(), v_end = a.local.end(); v != v_end; ++v) + { + const symbol *const sym = (symbol *)(hTabItemWithKey(liveRanges, I[*v].v)); + if(a.global[*v] < 0 && !sym->remat) // Try to put non-rematerializeable variables into registers. + c += 0.1f; + if(a.global[*v] < 0 && IS_REGISTER(sym->type)) // Try to honour register keyword. + c += 4.0f; + } + + return(c); +} + +// Code for another ic is generated when generating this one. Mark the other as generated. +static void extra_ic_generated(iCode *ic) +{ + iCode *ifx; + + // - can only jump on nonzero result for decrement of register / direct variable. + if(ic->op == '-' && ic->next && ic->next->op == IFX && IC_TRUE(ic->next) && IC_COND (ic->next)->key == IC_RESULT(ic)->key) + { + ifx = ic->next; + + if ((!IS_ITEMP(IC_LEFT (ic)) || options.stackAuto || reentrant) && !isOperandGlobal (IC_LEFT (ic))) + return; + + if (!IS_OP_LITERAL(IC_RIGHT(ic))) + return; + + if (ullFromVal(OP_VALUE(IC_RIGHT(ic))) != 1) + return; + + if (!isOperandEqual (IC_RESULT(ic), IC_LEFT(ic))) + return; + + if (getSize(operandType(IC_RESULT(ic))) > 2) + return; + + ifx->generated = true; + return; + } + + if(ic->op != EQ_OP && ic->op != NE_OP && ic->op != '<' && ic->op != '>' && ic->op != BITWISEAND) + return; + + ifx = ifxForOp(IC_RESULT(ic), ic); + + if(!ifx) + return; + + // Bitwise and code generation can only do the jump if there is at most one nonzero byte. + if(ic->op == BITWISEAND) + { + int nonzero = 0; + operand *const litop = IS_OP_LITERAL(IC_LEFT(ic)) ? IC_LEFT(ic) : IC_RIGHT(ic); + + if (!IS_OP_LITERAL(litop)) + return; + + for(unsigned int i = 0; i < getSize(operandType(IC_LEFT (ic))) && i < getSize(operandType(IC_RIGHT(ic))) && i < getSize(operandType(IC_RESULT(ic))); i++) + if(byteOfVal(OP_VALUE(litop), i)) + nonzero++; + + if(nonzero > 1 && IC_FALSE (ifx)) + return; + } + +cnd: + OP_SYMBOL(IC_RESULT(ic))->for_newralloc = false; + OP_SYMBOL(IC_RESULT(ic))->regType = REG_CND; + ifx->generated = true; +} + +template <class T_t, class G_t, class I_t, class SI_t> +static bool tree_dec_ralloc(T_t &T, G_t &G, const I_t &I, SI_t &SI) +{ + bool assignment_optimal; + + con2_t I2(boost::num_vertices(I)); + for(unsigned int i = 0; i < boost::num_vertices(I); i++) + { + I2[i].v = I[i].v; + I2[i].byte = I[i].byte; + I2[i].size = I[i].size; + I2[i].name = I[i].name; + } + typename boost::graph_traits<I_t>::edge_iterator e, e_end; + for(boost::tie(e, e_end) = boost::edges(I); e != e_end; ++e) + add_edge(boost::source(*e, I), boost::target(*e, I), I2); + + assignment ac; + assignment_optimal = true; + tree_dec_ralloc_nodes(T, find_root(T), G, I2, ac, &assignment_optimal); + + /*const*/ assignment &winner = *(T[find_root(T)].assignments.begin()); + +#ifdef DEBUG_RALLOC_DEC + std::cout << "Winner: "; + for(unsigned int i = 0; i < boost::num_vertices(I); i++) + { + std::cout << "(" << i << ", " << int(winner.global[i]) << ") "; + } + std::cout << "\n"; + std::cout << "Cost: " << winner.s << "\n"; + std::cout.flush(); +#endif + + // Todo: Make this an assertion + if(winner.global.size() != boost::num_vertices(I)) + { + std::cerr << "ERROR: No Assignments at root\n"; + exit(-1); + } + + for(unsigned int v = 0; v < boost::num_vertices(I); v++) + { + symbol *sym = (symbol *)(hTabItemWithKey(liveRanges, I[v].v)); + bool spilt = false; + + if(winner.global[v] >= 0) + sym->regs[I[v].byte] = pdk_regs + winner.global[v]; + else + { + sym->regs[I[v].byte] = 0; + spilt = true; + } + + if(spilt) + pdkSpillThis(sym); + + sym->nRegs = I[v].size; + } + + for(unsigned int i = 0; i < boost::num_vertices(G); i++) + set_surviving_regs(winner, i, G, I); + + set_spilt(G, I, SI); + + return(!assignment_optimal); +} + +iCode *pdk_ralloc2_cc(ebbIndex *ebbi) +{ + eBBlock **const ebbs = ebbi->bbOrder; + const int count = ebbi->count; + iCode *ic; + +#ifdef DEBUG_RALLOC_DEC + std::cout << "Processing " << currFunc->name << " from " << dstFileName << "\n"; std::cout.flush(); +#endif + + cfg_t control_flow_graph; + + con_t conflict_graph; + + ic = create_cfg(control_flow_graph, conflict_graph, ebbi); + + if(options.dump_graphs) + dump_cfg(control_flow_graph); + + if(options.dump_graphs) + dump_con(conflict_graph); + + tree_dec_t tree_decomposition; + + get_nice_tree_decomposition(tree_decomposition, control_flow_graph); + + alive_tree_dec(tree_decomposition, control_flow_graph); + + good_re_root(tree_decomposition); + nicify(tree_decomposition); + alive_tree_dec(tree_decomposition, control_flow_graph); + + if(options.dump_graphs) + dump_tree_decomposition(tree_decomposition); + + guessCounts (ic, ebbi); + + scon_t spilt_conflict_graph; + + pdk_assignment_optimal = !tree_dec_ralloc(tree_decomposition, control_flow_graph, conflict_graph, spilt_conflict_graph); + + pdkRegFix (ebbs, count); + + doOverlays (ebbs, count); + + return(ic); +} + diff --git a/src/pdk/ralloc2.o b/src/pdk/ralloc2.o Binary files differnew file mode 100644 index 0000000..718d12c --- /dev/null +++ b/src/pdk/ralloc2.o |
