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/gen.c | |
| download | sdcc-gas-268a53de823a6750d6256ee1fb1e7707b4b45740.tar.gz | |
sdcc-3.9.0 fork implementing GNU assembler syntax
This fork aims to provide better support for stm8-binutils
Diffstat (limited to 'src/pdk/gen.c')
| -rw-r--r-- | src/pdk/gen.c | 4244 |
1 files changed, 4244 insertions, 0 deletions
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 (); +} + |
