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/pic16/pcode.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/pic16/pcode.c')
| -rw-r--r-- | src/pic16/pcode.c | 12278 |
1 files changed, 12278 insertions, 0 deletions
diff --git a/src/pic16/pcode.c b/src/pic16/pcode.c new file mode 100644 index 0000000..5f45b45 --- /dev/null +++ b/src/pic16/pcode.c @@ -0,0 +1,12278 @@ +/*------------------------------------------------------------------------- + pcode.c - post code generation + + Copyright (C) 2000, Scott Dattalo scott@dattalo.com + PIC16 port: + Copyright (C) 2002, Martin Dubuc m.dubuc@rogers.com + + 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 <stdio.h> + +#include "common.h" // Include everything in the SDCC src directory +#include "newalloc.h" + + +#include "main.h" +#include "pcode.h" +#include "pcodeflow.h" +#include "ralloc.h" +#include "device.h" + +extern char *pic16_aopGet (struct asmop *aop, int offset, bool bit16, bool dname); + +#if defined(__BORLANDC__) || defined(_MSC_VER) +#define inline +#endif + +#define DUMP_DF_GRAPHS 0 + +/****************************************************************/ +/****************************************************************/ + +static peepCommand peepCommands[] = { + + {NOTBITSKIP, "_NOTBITSKIP_"}, + {BITSKIP, "_BITSKIP_"}, + {INVERTBITSKIP, "_INVERTBITSKIP_"}, + + {-1, NULL} +}; + + + +// Eventually this will go into device dependent files: +pCodeOpReg pic16_pc_status = {{PO_STATUS, "STATUS"}, -1, NULL,0,NULL}; +pCodeOpReg pic16_pc_intcon = {{PO_INTCON, "INTCON"}, -1, NULL,0,NULL}; +pCodeOpReg pic16_pc_pcl = {{PO_PCL, "PCL"}, -1, NULL,0,NULL}; +pCodeOpReg pic16_pc_pclath = {{PO_PCLATH, "PCLATH"}, -1, NULL,0,NULL}; +pCodeOpReg pic16_pc_pclatu = {{PO_PCLATU, "PCLATU"}, -1, NULL,0,NULL}; // patch 14 +pCodeOpReg pic16_pc_wreg = {{PO_WREG, "WREG"}, -1, NULL,0,NULL}; +pCodeOpReg pic16_pc_bsr = {{PO_BSR, "BSR"}, -1, NULL,0,NULL}; + +pCodeOpReg pic16_pc_tosl = {{PO_SFR_REGISTER, "TOSL"}, -1, NULL,0,NULL}; // patch 14 +pCodeOpReg pic16_pc_tosh = {{PO_SFR_REGISTER, "TOSH"}, -1, NULL,0,NULL}; // +pCodeOpReg pic16_pc_tosu = {{PO_SFR_REGISTER, "TOSU"}, -1, NULL,0,NULL}; // patch 14 + +pCodeOpReg pic16_pc_tblptrl = {{PO_SFR_REGISTER, "TBLPTRL"}, -1, NULL,0,NULL}; // patch 15 +pCodeOpReg pic16_pc_tblptrh = {{PO_SFR_REGISTER, "TBLPTRH"}, -1, NULL,0,NULL}; // +pCodeOpReg pic16_pc_tblptru = {{PO_SFR_REGISTER, "TBLPTRU"}, -1, NULL,0,NULL}; // +pCodeOpReg pic16_pc_tablat = {{PO_SFR_REGISTER, "TABLAT"}, -1, NULL,0,NULL}; // patch 15 + +//pCodeOpReg pic16_pc_fsr0 = {{PO_FSR0, "FSR0"}, -1, NULL,0,NULL}; //deprecated ! + +pCodeOpReg pic16_pc_fsr0l = {{PO_FSR0, "FSR0L"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_fsr0h = {{PO_FSR0, "FSR0H"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_fsr1l = {{PO_FSR0, "FSR1L"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_fsr1h = {{PO_FSR0, "FSR1H"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_fsr2l = {{PO_FSR0, "FSR2L"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_fsr2h = {{PO_FSR0, "FSR2H"}, -1, NULL, 0, NULL}; + +pCodeOpReg pic16_pc_indf0 = {{PO_INDF0, "INDF0"}, -1, NULL,0,NULL}; +pCodeOpReg pic16_pc_postinc0 = {{PO_INDF0, "POSTINC0"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_postdec0 = {{PO_INDF0, "POSTDEC0"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_preinc0 = {{PO_INDF0, "PREINC0"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_plusw0 = {{PO_INDF0, "PLUSW0"}, -1, NULL, 0, NULL}; + +pCodeOpReg pic16_pc_indf1 = {{PO_INDF0, "INDF1"}, -1, NULL,0,NULL}; +pCodeOpReg pic16_pc_postinc1 = {{PO_INDF0, "POSTINC1"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_postdec1 = {{PO_INDF0, "POSTDEC1"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_preinc1 = {{PO_INDF0, "PREINC1"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_plusw1 = {{PO_INDF0, "PLUSW1"}, -1, NULL, 0, NULL}; + +pCodeOpReg pic16_pc_indf2 = {{PO_INDF0, "INDF2"}, -1, NULL,0,NULL}; +pCodeOpReg pic16_pc_postinc2 = {{PO_INDF0, "POSTINC2"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_postdec2 = {{PO_INDF0, "POSTDEC2"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_preinc2 = {{PO_INDF0, "PREINC2"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_plusw2 = {{PO_INDF0, "PLUSW2"}, -1, NULL, 0, NULL}; + +pCodeOpReg pic16_pc_prodl = {{PO_PRODL, "PRODL"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_prodh = {{PO_PRODH, "PRODH"}, -1, NULL, 0, NULL}; + +/* EEPROM registers */ +pCodeOpReg pic16_pc_eecon1 = {{PO_SFR_REGISTER, "EECON1"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_eecon2 = {{PO_SFR_REGISTER, "EECON2"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_eedata = {{PO_SFR_REGISTER, "EEDATA"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_eeadr = {{PO_SFR_REGISTER, "EEADR"}, -1, NULL, 0, NULL}; + +pCodeOpReg pic16_pc_kzero = {{PO_GPR_REGISTER, "KZ"}, -1, NULL,0,NULL}; +pCodeOpReg pic16_pc_wsave = {{PO_GPR_REGISTER, "WSAVE"}, -1, NULL,0,NULL}; +pCodeOpReg pic16_pc_ssave = {{PO_GPR_REGISTER, "SSAVE"}, -1, NULL,0,NULL}; + +pCodeOpReg *pic16_stackpnt_lo; +pCodeOpReg *pic16_stackpnt_hi; +pCodeOpReg *pic16_stack_postinc; +pCodeOpReg *pic16_stack_postdec; +pCodeOpReg *pic16_stack_preinc; +pCodeOpReg *pic16_stack_plusw; + +pCodeOpReg *pic16_framepnt_lo; +pCodeOpReg *pic16_framepnt_hi; +pCodeOpReg *pic16_frame_postinc; +pCodeOpReg *pic16_frame_postdec; +pCodeOpReg *pic16_frame_preinc; +pCodeOpReg *pic16_frame_plusw; + +pCodeOpReg pic16_pc_gpsimio = {{PO_GPR_REGISTER, "GPSIMIO"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_gpsimio2 = {{PO_GPR_REGISTER, "GPSIMIO2"}, -1, NULL, 0, NULL}; + +static const char *OPT_TYPE_STR[] = { "begin", "end", "jumptable_begin", "jumptable_end" }; +const char *LR_TYPE_STR[] = { "entry begin", "entry end", "exit begin", "exit end" }; + + +static int mnemonics_initialized = 0; + + +static hTab *pic16MnemonicsHash = NULL; +static hTab *pic16pCodePeepCommandsHash = NULL; + +static pFile *the_pFile = NULL; +static pBlock *pb_dead_pcodes = NULL; + +/* Hardcoded flags to change the behavior of the PIC port */ +static int peepOptimizing = 1; /* run the peephole optimizer if nonzero */ +static int functionInlining = 1; /* inline functions if nonzero */ +int pic16_debug_verbose = 0; /* Set true to inundate .asm file */ + +int pic16_pcode_verbose = 0; + +//static int GpCodeSequenceNumber = 1; +static int GpcFlowSeq = 1; + +extern void pic16_RemoveUnusedRegisters(void); +extern void pic16_RegsUnMapLiveRanges(void); +extern void pic16_BuildFlowTree(pBlock *pb); +extern void pic16_pCodeRegOptimizeRegUsage(int level); + +/****************************************************************/ +/* Forward declarations */ +/****************************************************************/ + +void pic16_unlinkpCode(pCode *pc); +#if 0 +static void genericAnalyze(pCode *pc); +static void AnalyzeGOTO(pCode *pc); +static void AnalyzeSKIP(pCode *pc); +static void AnalyzeRETURN(pCode *pc); +#endif + +static void genericDestruct(pCode *pc); +static void genericPrint(FILE *of,pCode *pc); + +static void pCodePrintLabel(FILE *of, pCode *pc); +static void pCodePrintFunction(FILE *of, pCode *pc); +static void pCodeOpPrint(FILE *of, pCodeOp *pcop); +static char *pic16_get_op_from_instruction( pCodeInstruction *pcc); +char *pic16_get_op(pCodeOp *pcop,char *buff,size_t buf_size); +int pCodePeepMatchLine(pCodePeep *peepBlock, pCode *pcs, pCode *pcd); +int pic16_pCodePeepMatchRule(pCode *pc); +static void pBlockStats(FILE *of, pBlock *pb); +static pBlock *newpBlock(void); +extern void pic16_pCodeInsertAfter(pCode *pc1, pCode *pc2); +extern pCodeOp *pic16_popCopyReg(pCodeOpReg *pc); +pCodeOp *pic16_popCopyGPR2Bit(pCodeOp *pc, int bitval); +void pic16_pCodeRegMapLiveRanges(pBlock *pb); +void OptimizeLocalRegs(void); +pCodeOp *pic16_popGet2p(pCodeOp *src, pCodeOp *dst); + +const char *dumpPicOptype(PIC_OPTYPE type); + +pCodeOp *pic16_popGetLit2(int, pCodeOp *); +pCodeOp *pic16_popGetLit(int); +pCodeOp *pic16_popGetWithString(const char *); +int isBanksel(pCode *pc); +extern int inWparamList(char *s); + +/** data flow optimization helpers **/ +#if defined (DUMP_DF_GRAPHS) && DUMP_DF_GRAPHS > 0 +static void pic16_vcg_dump (FILE *of, pBlock *pb); +static void pic16_vcg_dump_default (pBlock *pb); +#endif +static int pic16_pCodeIsAlive (pCode *pc); +static void pic16_df_stats (); +static void pic16_createDF (pBlock *pb); +static int pic16_removeUnusedRegistersDF (); +static void pic16_destructDF (pBlock *pb); +static void releaseStack (void); + +/****************************************************************/ +/* PIC Instructions */ +/****************************************************************/ + +pCodeInstruction pic16_pciADDWF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_ADDWF, + "ADDWF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_REGISTER | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciADDFW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_ADDFW, + "ADDWF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_W | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciADDWFC = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_ADDWFC, + "ADDWFC", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER | PCC_C), // inCond + (PCC_REGISTER | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciADDFWC = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_ADDFWC, + "ADDWFC", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER | PCC_C), // inCond + (PCC_W | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciADDLW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_ADDLW, + "ADDLW", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_LITERAL), // inCond + (PCC_W | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciANDLW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_ANDLW, + "ANDLW", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_LITERAL), // inCond + (PCC_W | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciANDWF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_ANDWF, + "ANDWF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_REGISTER | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciANDFW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_ANDFW, + "ANDWF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_W | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBC = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_BC, + "BC", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_REL_ADDR | PCC_C), // inCond + PCC_NONE, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBCF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_BCF, + "BCF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + TRUE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_BSF, + (PCC_REGISTER | PCC_EXAMINE_PCOP), // inCond + PCC_REGISTER, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBN = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_BN, + "BN", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_REL_ADDR | PCC_N), // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBNC = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_BNC, + "BNC", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_REL_ADDR | PCC_C), // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBNN = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_BNN, + "BNN", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_REL_ADDR | PCC_N), // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBNOV = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_BNOV, + "BNOV", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_REL_ADDR | PCC_OV), // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBNZ = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_BNZ, + "BNZ", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_REL_ADDR | PCC_Z), // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBOV = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_BOV, + "BOV", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_REL_ADDR | PCC_OV), // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBRA = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_BRA, + "BRA", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REL_ADDR, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBSF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_BSF, + "BSF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + TRUE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_BCF, + (PCC_REGISTER | PCC_EXAMINE_PCOP), // inCond + (PCC_REGISTER | PCC_EXAMINE_PCOP), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBTFSC = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // AnalyzeSKIP, + genericDestruct, + genericPrint}, + POC_BTFSC, + "BTFSC", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + TRUE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_BTFSS, + (PCC_REGISTER | PCC_EXAMINE_PCOP), // inCond + PCC_EXAMINE_PCOP, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBTFSS = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // AnalyzeSKIP, + genericDestruct, + genericPrint}, + POC_BTFSS, + "BTFSS", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + TRUE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_BTFSC, + (PCC_REGISTER | PCC_EXAMINE_PCOP), // inCond + PCC_EXAMINE_PCOP, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBTG = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_BTG, + "BTG", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + TRUE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_REGISTER | PCC_EXAMINE_PCOP), // inCond + (PCC_REGISTER | PCC_EXAMINE_PCOP), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBZ = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_BZ, + "BZ", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_REL_ADDR | PCC_Z), // inCond + PCC_NONE, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciCALL = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_CALL, + "CALL", + 4, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + TRUE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciCOMF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_COMF, + "COMF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_REGISTER | PCC_Z | PCC_N) , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciCOMFW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_COMFW, + "COMF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_W | PCC_Z | PCC_N) , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciCLRF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_CLRF, + "CLRF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + (PCC_REGISTER | PCC_Z), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciCLRWDT = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_CLRWDT, + "CLRWDT", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciCPFSEQ = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_CPFSEQ, + "CPFSEQ", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciCPFSGT = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_CPFSGT, + "CPFSGT", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciCPFSLT = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_CPFSLT, + "CPFSLT", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + TRUE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciDAW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_DAW, + "DAW", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + FALSE, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_W, // inCond + (PCC_W | PCC_C), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciDCFSNZ = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_DCFSNZ, + "DCFSNZ", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + PCC_REGISTER , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciDCFSNZW = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_DCFSNZW, + "DCFSNZ", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + PCC_W , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciDECF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_DECF, + "DECF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_REGISTER | PCC_STATUS) , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciDECFW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_DECFW, + "DECF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_W | PCC_STATUS) , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciDECFSZ = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // AnalyzeSKIP, + genericDestruct, + genericPrint}, + POC_DECFSZ, + "DECFSZ", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + PCC_REGISTER , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciDECFSZW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // AnalyzeSKIP, + genericDestruct, + genericPrint}, + POC_DECFSZW, + "DECFSZ", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + PCC_W , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciGOTO = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // AnalyzeGOTO, + genericDestruct, + genericPrint}, + POC_GOTO, + "GOTO", + 4, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REL_ADDR, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciINCF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_INCF, + "INCF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_REGISTER | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciINCFW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_INCFW, + "INCF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_W | PCC_STATUS) , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciINCFSZ = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // AnalyzeSKIP, + genericDestruct, + genericPrint}, + POC_INCFSZ, + "INCFSZ", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_INFSNZ, + PCC_REGISTER, // inCond + PCC_REGISTER , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciINCFSZW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // AnalyzeSKIP, + genericDestruct, + genericPrint}, + POC_INCFSZW, + "INCFSZ", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_INFSNZW, + PCC_REGISTER, // inCond + PCC_W , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciINFSNZ = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // AnalyzeSKIP, + genericDestruct, + genericPrint}, + POC_INFSNZ, + "INFSNZ", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_INCFSZ, + PCC_REGISTER, // inCond + PCC_REGISTER , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciINFSNZW = { // vrokas - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // AnalyzeSKIP, + genericDestruct, + genericPrint}, + POC_INFSNZW, + "INFSNZ", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_INCFSZW, + PCC_REGISTER, // inCond + PCC_W , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciIORWF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_IORWF, + "IORWF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_REGISTER | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciIORFW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_IORFW, + "IORWF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_W | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciIORLW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_IORLW, + "IORLW", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_LITERAL), // inCond + (PCC_W | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciLFSR = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_LFSR, + "LFSR", + 4, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + TRUE, // second literal operand + POC_NOP, + PCC_LITERAL, // inCond + PCC_NONE, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciMOVF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_MOVF, + "MOVF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciMOVFW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_MOVFW, + "MOVF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_W | PCC_N | PCC_Z), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciMOVFF = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_MOVFF, + "MOVFF", + 4, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + TRUE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + PCC_REGISTER2, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciMOVLB = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_MOVLB, + "MOVLB", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_NONE | PCC_LITERAL), // inCond + PCC_REGISTER, // outCond - BSR + PCI_MAGIC +}; + +pCodeInstruction pic16_pciMOVLW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_MOVLW, + "MOVLW", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_NONE | PCC_LITERAL), // inCond + PCC_W, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciMOVWF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_MOVWF, + "MOVWF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_W, // inCond + PCC_REGISTER, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciMULLW = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_MULLW, + "MULLW", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_LITERAL), // inCond + PCC_NONE, // outCond - PROD + PCI_MAGIC +}; + +pCodeInstruction pic16_pciMULWF = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_MULWF, + "MULWF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + PCC_REGISTER, // outCond - PROD + PCI_MAGIC +}; + +pCodeInstruction pic16_pciNEGF = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_NEGF, + "NEGF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_REGISTER | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciNOP = { + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_NOP, + "NOP", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciPOP = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_POP, + "POP", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciPUSH = { + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_PUSH, + "PUSH", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciRCALL = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_RCALL, + "RCALL", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REL_ADDR, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciRETFIE = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // AnalyzeRETURN, + genericDestruct, + genericPrint}, + POC_RETFIE, + "RETFIE", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + TRUE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE, // outCond (not true... affects the GIE bit too) + PCI_MAGIC +}; + +pCodeInstruction pic16_pciRETLW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // AnalyzeRETURN, + genericDestruct, + genericPrint}, + POC_RETLW, + "RETLW", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + TRUE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_LITERAL, // inCond + PCC_W, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciRETURN = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // AnalyzeRETURN, + genericDestruct, + genericPrint}, + POC_RETURN, + "RETURN", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + TRUE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE, // outCond + PCI_MAGIC +}; +pCodeInstruction pic16_pciRLCF = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_RLCF, + "RLCF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_C | PCC_REGISTER), // inCond + (PCC_REGISTER | PCC_C | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciRLCFW = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_RLCFW, + "RLCF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_C | PCC_REGISTER), // inCond + (PCC_W | PCC_C | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciRLNCF = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_RLNCF, + "RLNCF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_REGISTER | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; +pCodeInstruction pic16_pciRLNCFW = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_RLNCFW, + "RLNCF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_W | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; +pCodeInstruction pic16_pciRRCF = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_RRCF, + "RRCF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_C | PCC_REGISTER), // inCond + (PCC_REGISTER | PCC_C | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; +pCodeInstruction pic16_pciRRCFW = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_RRCFW, + "RRCF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_C | PCC_REGISTER), // inCond + (PCC_W | PCC_C | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; +pCodeInstruction pic16_pciRRNCF = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_RRNCF, + "RRNCF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_REGISTER | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciRRNCFW = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_RRNCFW, + "RRNCF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_W | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciSETF = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_SETF, + "SETF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_REGISTER , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciSUBLW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_SUBLW, + "SUBLW", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_LITERAL), // inCond + (PCC_W | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciSUBFWB = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_SUBFWB, + "SUBFWB", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER | PCC_C), // inCond + (PCC_W | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciSUBWF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_SUBWF, + "SUBWF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_REGISTER | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciSUBFW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_SUBFW, + "SUBWF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_W | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciSUBFWB_D1 = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_SUBFWB_D1, + "SUBFWB", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER | PCC_C), // inCond + (PCC_REGISTER | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciSUBFWB_D0 = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_SUBFWB_D0, + "SUBFWB", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER | PCC_C), // inCond + (PCC_W | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciSUBWFB_D1 = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_SUBWFB_D1, + "SUBWFB", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER | PCC_C), // inCond + (PCC_REGISTER | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciSUBWFB_D0 = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_SUBWFB_D0, + "SUBWFB", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER | PCC_C), // inCond + (PCC_W | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciSWAPF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_SWAPF, + "SWAPF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_REGISTER), // inCond + (PCC_REGISTER), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciSWAPFW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_SWAPFW, + "SWAPF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_REGISTER), // inCond + (PCC_W), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciTBLRD = { // patch 15 + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_TBLRD, + "TBLRD*", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciTBLRD_POSTINC = { // patch 15 + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_TBLRD_POSTINC, + "TBLRD*+", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciTBLRD_POSTDEC = { // patch 15 + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_TBLRD_POSTDEC, + "TBLRD*-", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciTBLRD_PREINC = { // patch 15 + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_TBLRD_PREINC, + "TBLRD+*", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciTBLWT = { // patch 15 + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_TBLWT, + "TBLWT*", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciTBLWT_POSTINC = { // patch 15 + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_TBLWT_POSTINC, + "TBLWT*+", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciTBLWT_POSTDEC = { // patch 15 + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_TBLWT_POSTDEC, + "TBLWT*-", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciTBLWT_PREINC = { // patch 15 + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_TBLWT_PREINC, + "TBLWT+*", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciTSTFSZ = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_TSTFSZ, + "TSTFSZ", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + PCC_NONE, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciXORWF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_XORWF, + "XORWF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_REGISTER | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciXORFW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_XORFW, + "XORWF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_W | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciXORLW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_XORLW, + "XORLW", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_LITERAL), // inCond + (PCC_W | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + + +pCodeInstruction pic16_pciBANKSEL = { + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_BANKSEL, + "BANKSEL", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE, // outCond + PCI_MAGIC +}; + + +#define MAX_PIC16MNEMONICS 100 +pCodeInstruction *pic16Mnemonics[MAX_PIC16MNEMONICS]; + +extern set *externs; +extern reg_info *pic16_allocProcessorRegister(int rIdx, char * name, short po_type, int alias); +extern reg_info *pic16_allocInternalRegister(int rIdx, char * name, short po_type, int alias); + +void pic16_pCodeInitRegisters(void) +{ + static int initialized=0; + + if(initialized) + return; + + initialized = 1; + + pic16_pc_status.r = pic16_allocProcessorRegister(IDX_STATUS,"STATUS", PO_STATUS, 0x80); + pic16_pc_pcl.r = pic16_allocProcessorRegister(IDX_PCL,"PCL", PO_PCL, 0x80); + pic16_pc_pclath.r = pic16_allocProcessorRegister(IDX_PCLATH,"PCLATH", PO_PCLATH, 0x80); + pic16_pc_pclatu.r = pic16_allocProcessorRegister(IDX_PCLATU,"PCLATU", PO_PCLATU, 0x80); + pic16_pc_intcon.r = pic16_allocProcessorRegister(IDX_INTCON,"INTCON", PO_INTCON, 0x80); + pic16_pc_wreg.r = pic16_allocProcessorRegister(IDX_WREG,"WREG", PO_WREG, 0x80); + pic16_pc_bsr.r = pic16_allocProcessorRegister(IDX_BSR,"BSR", PO_BSR, 0x80); + + pic16_pc_tosl.r = pic16_allocProcessorRegister(IDX_TOSL,"TOSL", PO_SFR_REGISTER, 0x80); + pic16_pc_tosh.r = pic16_allocProcessorRegister(IDX_TOSH,"TOSH", PO_SFR_REGISTER, 0x80); + pic16_pc_tosu.r = pic16_allocProcessorRegister(IDX_TOSU,"TOSU", PO_SFR_REGISTER, 0x80); + + pic16_pc_tblptrl.r = pic16_allocProcessorRegister(IDX_TBLPTRL,"TBLPTRL", PO_SFR_REGISTER, 0x80); + pic16_pc_tblptrh.r = pic16_allocProcessorRegister(IDX_TBLPTRH,"TBLPTRH", PO_SFR_REGISTER, 0x80); + pic16_pc_tblptru.r = pic16_allocProcessorRegister(IDX_TBLPTRU,"TBLPTRU", PO_SFR_REGISTER, 0x80); + pic16_pc_tablat.r = pic16_allocProcessorRegister(IDX_TABLAT,"TABLAT", PO_SFR_REGISTER, 0x80); + + pic16_pc_fsr0l.r = pic16_allocProcessorRegister(IDX_FSR0L, "FSR0L", PO_FSR0, 0x80); + pic16_pc_fsr0h.r = pic16_allocProcessorRegister(IDX_FSR0H, "FSR0H", PO_FSR0, 0x80); + pic16_pc_fsr1l.r = pic16_allocProcessorRegister(IDX_FSR1L, "FSR1L", PO_FSR0, 0x80); + pic16_pc_fsr1h.r = pic16_allocProcessorRegister(IDX_FSR1H, "FSR1H", PO_FSR0, 0x80); + pic16_pc_fsr2l.r = pic16_allocProcessorRegister(IDX_FSR2L, "FSR2L", PO_FSR0, 0x80); + pic16_pc_fsr2h.r = pic16_allocProcessorRegister(IDX_FSR2H, "FSR2H", PO_FSR0, 0x80); + + pic16_stackpnt_lo = &pic16_pc_fsr1l; + pic16_stackpnt_hi = &pic16_pc_fsr1h; + pic16_stack_postdec = &pic16_pc_postdec1; + pic16_stack_postinc = &pic16_pc_postinc1; + pic16_stack_preinc = &pic16_pc_preinc1; + pic16_stack_plusw = &pic16_pc_plusw1; + + pic16_framepnt_lo = &pic16_pc_fsr2l; + pic16_framepnt_hi = &pic16_pc_fsr2h; + pic16_frame_postdec = &pic16_pc_postdec2; + pic16_frame_postinc = &pic16_pc_postinc2; + pic16_frame_preinc = &pic16_pc_preinc2; + pic16_frame_plusw = &pic16_pc_plusw2; + + pic16_pc_indf0.r = pic16_allocProcessorRegister(IDX_INDF0,"INDF0", PO_INDF0, 0x80); + pic16_pc_postinc0.r = pic16_allocProcessorRegister(IDX_POSTINC0, "POSTINC0", PO_INDF0, 0x80); + pic16_pc_postdec0.r = pic16_allocProcessorRegister(IDX_POSTDEC0, "POSTDEC0", PO_INDF0, 0x80); + pic16_pc_preinc0.r = pic16_allocProcessorRegister(IDX_PREINC0, "PREINC0", PO_INDF0, 0x80); + pic16_pc_plusw0.r = pic16_allocProcessorRegister(IDX_PLUSW0, "PLUSW0", PO_INDF0, 0x80); + + pic16_pc_indf1.r = pic16_allocProcessorRegister(IDX_INDF1,"INDF1", PO_INDF0, 0x80); + pic16_pc_postinc1.r = pic16_allocProcessorRegister(IDX_POSTINC1, "POSTINC1", PO_INDF0, 0x80); + pic16_pc_postdec1.r = pic16_allocProcessorRegister(IDX_POSTDEC1, "POSTDEC1", PO_INDF0, 0x80); + pic16_pc_preinc1.r = pic16_allocProcessorRegister(IDX_PREINC1, "PREINC1", PO_INDF0, 0x80); + pic16_pc_plusw1.r = pic16_allocProcessorRegister(IDX_PLUSW1, "PLUSW1", PO_INDF0, 0x80); + + pic16_pc_indf2.r = pic16_allocProcessorRegister(IDX_INDF2,"INDF2", PO_INDF0, 0x80); + pic16_pc_postinc2.r = pic16_allocProcessorRegister(IDX_POSTINC2, "POSTINC2", PO_INDF0, 0x80); + pic16_pc_postdec2.r = pic16_allocProcessorRegister(IDX_POSTDEC2, "POSTDEC2", PO_INDF0, 0x80); + pic16_pc_preinc2.r = pic16_allocProcessorRegister(IDX_PREINC2, "PREINC2", PO_INDF0, 0x80); + pic16_pc_plusw2.r = pic16_allocProcessorRegister(IDX_PLUSW2, "PLUSW2", PO_INDF0, 0x80); + + pic16_pc_prodl.r = pic16_allocProcessorRegister(IDX_PRODL, "PRODL", PO_PRODL, 0x80); + pic16_pc_prodh.r = pic16_allocProcessorRegister(IDX_PRODH, "PRODH", PO_PRODH, 0x80); + + + pic16_pc_eecon1.r = pic16_allocProcessorRegister(IDX_EECON1, "EECON1", PO_SFR_REGISTER, 0x80); + pic16_pc_eecon2.r = pic16_allocProcessorRegister(IDX_EECON2, "EECON2", PO_SFR_REGISTER, 0x80); + pic16_pc_eedata.r = pic16_allocProcessorRegister(IDX_EEDATA, "EEDATA", PO_SFR_REGISTER, 0x80); + pic16_pc_eeadr.r = pic16_allocProcessorRegister(IDX_EEADR, "EEADR", PO_SFR_REGISTER, 0x80); + + + pic16_pc_status.rIdx = IDX_STATUS; + pic16_pc_intcon.rIdx = IDX_INTCON; + pic16_pc_pcl.rIdx = IDX_PCL; + pic16_pc_pclath.rIdx = IDX_PCLATH; + pic16_pc_pclatu.rIdx = IDX_PCLATU; + pic16_pc_wreg.rIdx = IDX_WREG; + pic16_pc_bsr.rIdx = IDX_BSR; + + pic16_pc_tosl.rIdx = IDX_TOSL; + pic16_pc_tosh.rIdx = IDX_TOSH; + pic16_pc_tosu.rIdx = IDX_TOSU; + + pic16_pc_tblptrl.rIdx = IDX_TBLPTRL; + pic16_pc_tblptrh.rIdx = IDX_TBLPTRH; + pic16_pc_tblptru.rIdx = IDX_TBLPTRU; + pic16_pc_tablat.rIdx = IDX_TABLAT; + + pic16_pc_fsr0l.rIdx = IDX_FSR0L; + pic16_pc_fsr0h.rIdx = IDX_FSR0H; + pic16_pc_fsr1l.rIdx = IDX_FSR1L; + pic16_pc_fsr1h.rIdx = IDX_FSR1H; + pic16_pc_fsr2l.rIdx = IDX_FSR2L; + pic16_pc_fsr2h.rIdx = IDX_FSR2H; + pic16_pc_indf0.rIdx = IDX_INDF0; + pic16_pc_postinc0.rIdx = IDX_POSTINC0; + pic16_pc_postdec0.rIdx = IDX_POSTDEC0; + pic16_pc_preinc0.rIdx = IDX_PREINC0; + pic16_pc_plusw0.rIdx = IDX_PLUSW0; + pic16_pc_indf1.rIdx = IDX_INDF1; + pic16_pc_postinc1.rIdx = IDX_POSTINC1; + pic16_pc_postdec1.rIdx = IDX_POSTDEC1; + pic16_pc_preinc1.rIdx = IDX_PREINC1; + pic16_pc_plusw1.rIdx = IDX_PLUSW1; + pic16_pc_indf2.rIdx = IDX_INDF2; + pic16_pc_postinc2.rIdx = IDX_POSTINC2; + pic16_pc_postdec2.rIdx = IDX_POSTDEC2; + pic16_pc_preinc2.rIdx = IDX_PREINC2; + pic16_pc_plusw2.rIdx = IDX_PLUSW2; + pic16_pc_prodl.rIdx = IDX_PRODL; + pic16_pc_prodh.rIdx = IDX_PRODH; + + pic16_pc_kzero.r = pic16_allocInternalRegister(IDX_KZ,"KZ",PO_GPR_REGISTER,0); + pic16_pc_ssave.r = pic16_allocInternalRegister(IDX_SSAVE,"SSAVE", PO_GPR_REGISTER, 0); + pic16_pc_wsave.r = pic16_allocInternalRegister(IDX_WSAVE,"WSAVE", PO_GPR_REGISTER, 0); + + pic16_pc_kzero.rIdx = IDX_KZ; + pic16_pc_wsave.rIdx = IDX_WSAVE; + pic16_pc_ssave.rIdx = IDX_SSAVE; + + pic16_pc_eecon1.rIdx = IDX_EECON1; + pic16_pc_eecon2.rIdx = IDX_EECON2; + pic16_pc_eedata.rIdx = IDX_EEDATA; + pic16_pc_eeadr.rIdx = IDX_EEADR; + + + pic16_pc_gpsimio.r = pic16_allocProcessorRegister(IDX_GPSIMIO, "GPSIMIO", PO_GPR_REGISTER, 0x80); + pic16_pc_gpsimio2.r = pic16_allocProcessorRegister(IDX_GPSIMIO2, "GPSIMIO2", PO_GPR_REGISTER, 0x80); + + pic16_pc_gpsimio.rIdx = IDX_GPSIMIO; + pic16_pc_gpsimio2.rIdx = IDX_GPSIMIO2; + + /* probably should put this in a separate initialization routine */ + pb_dead_pcodes = newpBlock(); + +} + +/*-----------------------------------------------------------------*/ +/* mnem2key - convert a pic mnemonic into a hash key */ +/* (BTW - this spreads the mnemonics quite well) */ +/* */ +/*-----------------------------------------------------------------*/ + +static int mnem2key(unsigned char const *mnem) +{ + int key = 0; + + if(!mnem) + return 0; + + while(*mnem) { + + key += toupper(*mnem++) +1; + + } + + return (key & 0x1f); + +} + +void pic16initMnemonics(void) +{ + int i = 0; + int key; + // char *str; + pCodeInstruction *pci; + + if(mnemonics_initialized) + return; + + // NULL out the array before making the assignments + // since we check the array contents below this initialization. + + for (i = 0; i < MAX_PIC16MNEMONICS; i++) { + pic16Mnemonics[i] = NULL; + } + + pic16Mnemonics[POC_ADDLW] = &pic16_pciADDLW; + pic16Mnemonics[POC_ADDWF] = &pic16_pciADDWF; + pic16Mnemonics[POC_ADDFW] = &pic16_pciADDFW; + pic16Mnemonics[POC_ADDWFC] = &pic16_pciADDWFC; + pic16Mnemonics[POC_ADDFWC] = &pic16_pciADDFWC; + pic16Mnemonics[POC_ANDLW] = &pic16_pciANDLW; + pic16Mnemonics[POC_ANDWF] = &pic16_pciANDWF; + pic16Mnemonics[POC_ANDFW] = &pic16_pciANDFW; + pic16Mnemonics[POC_BC] = &pic16_pciBC; + pic16Mnemonics[POC_BCF] = &pic16_pciBCF; + pic16Mnemonics[POC_BN] = &pic16_pciBN; + pic16Mnemonics[POC_BNC] = &pic16_pciBNC; + pic16Mnemonics[POC_BNN] = &pic16_pciBNN; + pic16Mnemonics[POC_BNOV] = &pic16_pciBNOV; + pic16Mnemonics[POC_BNZ] = &pic16_pciBNZ; + pic16Mnemonics[POC_BOV] = &pic16_pciBOV; + pic16Mnemonics[POC_BRA] = &pic16_pciBRA; + pic16Mnemonics[POC_BSF] = &pic16_pciBSF; + pic16Mnemonics[POC_BTFSC] = &pic16_pciBTFSC; + pic16Mnemonics[POC_BTFSS] = &pic16_pciBTFSS; + pic16Mnemonics[POC_BTG] = &pic16_pciBTG; + pic16Mnemonics[POC_BZ] = &pic16_pciBZ; + pic16Mnemonics[POC_CALL] = &pic16_pciCALL; + pic16Mnemonics[POC_CLRF] = &pic16_pciCLRF; + pic16Mnemonics[POC_CLRWDT] = &pic16_pciCLRWDT; + pic16Mnemonics[POC_COMF] = &pic16_pciCOMF; + pic16Mnemonics[POC_COMFW] = &pic16_pciCOMFW; + pic16Mnemonics[POC_CPFSEQ] = &pic16_pciCPFSEQ; + pic16Mnemonics[POC_CPFSGT] = &pic16_pciCPFSGT; + pic16Mnemonics[POC_CPFSLT] = &pic16_pciCPFSLT; + pic16Mnemonics[POC_DAW] = &pic16_pciDAW; + pic16Mnemonics[POC_DCFSNZ] = &pic16_pciDCFSNZ; + pic16Mnemonics[POC_DECF] = &pic16_pciDECF; + pic16Mnemonics[POC_DECFW] = &pic16_pciDECFW; + pic16Mnemonics[POC_DECFSZ] = &pic16_pciDECFSZ; + pic16Mnemonics[POC_DECFSZW] = &pic16_pciDECFSZW; + pic16Mnemonics[POC_GOTO] = &pic16_pciGOTO; + pic16Mnemonics[POC_INCF] = &pic16_pciINCF; + pic16Mnemonics[POC_INCFW] = &pic16_pciINCFW; + pic16Mnemonics[POC_INCFSZ] = &pic16_pciINCFSZ; + pic16Mnemonics[POC_INCFSZW] = &pic16_pciINCFSZW; + pic16Mnemonics[POC_INFSNZ] = &pic16_pciINFSNZ; + pic16Mnemonics[POC_INFSNZW] = &pic16_pciINFSNZW; + pic16Mnemonics[POC_IORWF] = &pic16_pciIORWF; + pic16Mnemonics[POC_IORFW] = &pic16_pciIORFW; + pic16Mnemonics[POC_IORLW] = &pic16_pciIORLW; + pic16Mnemonics[POC_LFSR] = &pic16_pciLFSR; + pic16Mnemonics[POC_MOVF] = &pic16_pciMOVF; + pic16Mnemonics[POC_MOVFW] = &pic16_pciMOVFW; + pic16Mnemonics[POC_MOVFF] = &pic16_pciMOVFF; + pic16Mnemonics[POC_MOVLB] = &pic16_pciMOVLB; + pic16Mnemonics[POC_MOVLW] = &pic16_pciMOVLW; + pic16Mnemonics[POC_MOVWF] = &pic16_pciMOVWF; + pic16Mnemonics[POC_MULLW] = &pic16_pciMULLW; + pic16Mnemonics[POC_MULWF] = &pic16_pciMULWF; + pic16Mnemonics[POC_NEGF] = &pic16_pciNEGF; + pic16Mnemonics[POC_NOP] = &pic16_pciNOP; + pic16Mnemonics[POC_POP] = &pic16_pciPOP; + pic16Mnemonics[POC_PUSH] = &pic16_pciPUSH; + pic16Mnemonics[POC_RCALL] = &pic16_pciRCALL; + pic16Mnemonics[POC_RETFIE] = &pic16_pciRETFIE; + pic16Mnemonics[POC_RETLW] = &pic16_pciRETLW; + pic16Mnemonics[POC_RETURN] = &pic16_pciRETURN; + pic16Mnemonics[POC_RLCF] = &pic16_pciRLCF; + pic16Mnemonics[POC_RLCFW] = &pic16_pciRLCFW; + pic16Mnemonics[POC_RLNCF] = &pic16_pciRLNCF; + pic16Mnemonics[POC_RLNCFW] = &pic16_pciRLNCFW; + pic16Mnemonics[POC_RRCF] = &pic16_pciRRCF; + pic16Mnemonics[POC_RRCFW] = &pic16_pciRRCFW; + pic16Mnemonics[POC_RRNCF] = &pic16_pciRRNCF; + pic16Mnemonics[POC_RRNCFW] = &pic16_pciRRNCFW; + pic16Mnemonics[POC_SETF] = &pic16_pciSETF; + pic16Mnemonics[POC_SUBLW] = &pic16_pciSUBLW; + pic16Mnemonics[POC_SUBWF] = &pic16_pciSUBWF; + pic16Mnemonics[POC_SUBFW] = &pic16_pciSUBFW; + pic16Mnemonics[POC_SUBWFB_D0] = &pic16_pciSUBWFB_D0; + pic16Mnemonics[POC_SUBWFB_D1] = &pic16_pciSUBWFB_D1; + pic16Mnemonics[POC_SUBFWB_D0] = &pic16_pciSUBFWB_D0; + pic16Mnemonics[POC_SUBFWB_D1] = &pic16_pciSUBFWB_D1; + pic16Mnemonics[POC_SWAPF] = &pic16_pciSWAPF; + pic16Mnemonics[POC_SWAPFW] = &pic16_pciSWAPFW; + pic16Mnemonics[POC_TBLRD] = &pic16_pciTBLRD; + pic16Mnemonics[POC_TBLRD_POSTINC] = &pic16_pciTBLRD_POSTINC; + pic16Mnemonics[POC_TBLRD_POSTDEC] = &pic16_pciTBLRD_POSTDEC; + pic16Mnemonics[POC_TBLRD_PREINC] = &pic16_pciTBLRD_PREINC; + pic16Mnemonics[POC_TBLWT] = &pic16_pciTBLWT; + pic16Mnemonics[POC_TBLWT_POSTINC] = &pic16_pciTBLWT_POSTINC; + pic16Mnemonics[POC_TBLWT_POSTDEC] = &pic16_pciTBLWT_POSTDEC; + pic16Mnemonics[POC_TBLWT_PREINC] = &pic16_pciTBLWT_PREINC; + pic16Mnemonics[POC_TSTFSZ] = &pic16_pciTSTFSZ; + pic16Mnemonics[POC_XORLW] = &pic16_pciXORLW; + pic16Mnemonics[POC_XORWF] = &pic16_pciXORWF; + pic16Mnemonics[POC_XORFW] = &pic16_pciXORFW; + pic16Mnemonics[POC_BANKSEL] = &pic16_pciBANKSEL; + + for(i=0; i<MAX_PIC16MNEMONICS; i++) + if(pic16Mnemonics[i]) + hTabAddItem(&pic16MnemonicsHash, mnem2key((const unsigned char *)pic16Mnemonics[i]->mnemonic), pic16Mnemonics[i]); + pci = hTabFirstItem(pic16MnemonicsHash, &key); + + while(pci) { + DFPRINTF((stderr, "element %d key %d, mnem %s\n",i++,key,pci->mnemonic)); + pci = hTabNextItem(pic16MnemonicsHash, &key); + } + + mnemonics_initialized = 1; +} + +int pic16_getpCodePeepCommand(const char *cmd); + +int pic16_getpCode(const char *mnem,unsigned dest) +{ + pCodeInstruction *pci; + int key = mnem2key((unsigned char *)mnem); + + if(!mnemonics_initialized) + pic16initMnemonics(); + + pci = hTabFirstItemWK(pic16MnemonicsHash, key); + + while(pci) { + + if(STRCASECMP(pci->mnemonic, mnem) == 0) { + if((pci->num_ops <= 1) + || (pci->isModReg == dest) + || (pci->isBitInst) + || (pci->num_ops <= 2 && pci->isAccess) + || (pci->num_ops <= 2 && pci->isFastCall) + || (pci->num_ops <= 2 && pci->is2MemOp) + || (pci->num_ops <= 2 && pci->is2LitOp) ) + return(pci->op); + } + + pci = hTabNextItemWK (pic16MnemonicsHash); + + } + + return -1; +} + +/*-----------------------------------------------------------------* + * pic16initpCodePeepCommands + * + *-----------------------------------------------------------------*/ +void pic16initpCodePeepCommands(void) +{ + int key, i; + peepCommand *pcmd; + + i = 0; + do { + hTabAddItem(&pic16pCodePeepCommandsHash, + mnem2key((const unsigned char *)peepCommands[i].cmd), &peepCommands[i]); + i++; + } while (peepCommands[i].cmd); + + pcmd = hTabFirstItem(pic16pCodePeepCommandsHash, &key); + + while(pcmd) { + //fprintf(stderr, "peep command %s key %d\n",pcmd->cmd,pcmd->id); + pcmd = hTabNextItem(pic16pCodePeepCommandsHash, &key); + } +} + +/*----------------------------------------------------------------- + * + * + *-----------------------------------------------------------------*/ + +int pic16_getpCodePeepCommand(const char *cmd) +{ + peepCommand *pcmd; + int key = mnem2key((unsigned char *)cmd); + + + pcmd = hTabFirstItemWK(pic16pCodePeepCommandsHash, key); + + while(pcmd) { + // fprintf(stderr," comparing %s to %s\n",pcmd->cmd,cmd); + if(STRCASECMP(pcmd->cmd, cmd) == 0) { + return pcmd->id; + } + + pcmd = hTabNextItemWK (pic16pCodePeepCommandsHash); + + } + + return -1; +} + +static char getpBlock_dbName(pBlock *pb) +{ + if(!pb) + return 0; + + if(pb->cmemmap) + return pb->cmemmap->dbName; + + return pb->dbName; +} +void pic16_pBlockConvert2ISR(pBlock *pb) +{ + if(!pb)return; + + if(pb->cmemmap)pb->cmemmap = NULL; + + pb->dbName = 'I'; + + if(pic16_pcode_verbose) + fprintf(stderr, "%s:%d converting to 'I'interrupt pBlock\n", __FILE__, __LINE__); +} + +void pic16_pBlockConvert2Absolute(pBlock *pb) +{ + if(!pb)return; + if(pb->cmemmap)pb->cmemmap = NULL; + + pb->dbName = 'A'; + + if(pic16_pcode_verbose) + fprintf(stderr, "%s:%d converting to 'A'bsolute pBlock\n", __FILE__, __LINE__); +} + +/*-----------------------------------------------------------------*/ +/* pic16_movepBlock2Head - given the dbname of a pBlock, move all */ +/* instances to the front of the doubly linked */ +/* list of pBlocks */ +/*-----------------------------------------------------------------*/ + +void pic16_movepBlock2Head(char dbName) +{ + pBlock *pb; + + + /* this can happen in sources without code, + * only variable definitions */ + if(!the_pFile)return; + + pb = the_pFile->pbHead; + + while(pb) { + + if(getpBlock_dbName(pb) == dbName) { + pBlock *pbn = pb->next; + pb->next = the_pFile->pbHead; + the_pFile->pbHead->prev = pb; + the_pFile->pbHead = pb; + + if(pb->prev) + pb->prev->next = pbn; + + // If the pBlock that we just moved was the last + // one in the link of all of the pBlocks, then we + // need to point the tail to the block just before + // the one we moved. + // Note: if pb->next is NULL, then pb must have + // been the last pBlock in the chain. + + if(pbn) + pbn->prev = pb->prev; + else + the_pFile->pbTail = pb->prev; + + pb = pbn; + + } else + pb = pb->next; + + } +} + +void pic16_copypCode(FILE *of, char dbName) +{ + pBlock *pb; + + if(!of || !the_pFile) + return; + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + if(getpBlock_dbName(pb) == dbName) { +// fprintf(stderr, "%s:%d: output of pb= 0x%p\n", __FILE__, __LINE__, pb); + pBlockStats(of,pb); + pic16_printpBlock(of,pb); + } + } + +} +void pic16_pcode_test(void) +{ + DFPRINTF((stderr,"pcode is alive!\n")); + + //initMnemonics(); + + if (the_pFile) { + pBlock *pb; + FILE *pFile; + char buffer[100]; + + /* create the file name */ + SNPRINTF(buffer, sizeof(buffer), "%s.p", dstFileName); + + if(!(pFile = fopen(buffer, "w" ))) { + werror(E_FILE_OPEN_ERR,buffer); + exit(1); + } + + fprintf(pFile,"pcode dump\n\n"); + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + fprintf(pFile,"\n\tNew pBlock\n\n"); + if(pb->cmemmap) + fprintf(pFile,"%s",pb->cmemmap->sname); + else + fprintf(pFile,"internal pblock"); + + fprintf(pFile,", dbName =%c\n",getpBlock_dbName(pb)); + pic16_printpBlock(pFile,pb); + } + fclose(pFile); + } +} + + +unsigned long pic16_countInstructions(void) +{ + pBlock *pb; + pCode *pc; + unsigned long isize=0; + + if(!the_pFile)return -1; + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + for(pc = pb->pcHead; pc; pc = pc->next) { + if(isPCI(pc) || isPCAD(pc))isize += PCI(pc)->isize; + } + } + return (isize); +} + + +/*-----------------------------------------------------------------*/ +/* int RegCond(pCodeOp *pcop) - if pcop points to the STATUS reg- */ +/* ister, RegCond will return the bit being referenced. */ +/* */ +/* fixme - why not just OR in the pcop bit field */ +/*-----------------------------------------------------------------*/ + +static int RegCond(const pCodeOp *pcop) +{ + if(!pcop) + return 0; + + if(!pcop->name)return 0; + + if(pcop->type == PO_GPR_BIT && !strcmp(pcop->name, pic16_pc_status.pcop.name)) { + switch(PCORB(pcop)->bit) { + case PIC_C_BIT: + return PCC_C; + case PIC_DC_BIT: + return PCC_DC; + case PIC_Z_BIT: + return PCC_Z; + } + } + + return 0; +} + + +/*-----------------------------------------------------------------*/ +/* pic16_newpCode - create and return a newly initialized pCode */ +/* */ +/* fixme - rename this */ +/* */ +/* The purpose of this routine is to create a new Instruction */ +/* pCode. This is called by gen.c while the assembly code is being */ +/* generated. */ +/* */ +/* Inouts: */ +/* PIC_OPCODE op - the assembly instruction we wish to create. */ +/* (note that the op is analogous to but not the */ +/* same thing as the opcode of the instruction.) */ +/* pCdoeOp *pcop - pointer to the operand of the instruction. */ +/* */ +/* Outputs: */ +/* a pointer to the new malloc'd pCode is returned. */ +/* */ +/* */ +/* */ +/*-----------------------------------------------------------------*/ +pCode *pic16_newpCode (PIC_OPCODE op, pCodeOp *pcop) +{ + pCodeInstruction *pci ; + + if(!mnemonics_initialized) + pic16initMnemonics(); + + pci = Safe_alloc(sizeof(pCodeInstruction)); + + if((op>=0) && (op < MAX_PIC16MNEMONICS) && pic16Mnemonics[op]) { + memcpy(pci, pic16Mnemonics[op], sizeof(pCodeInstruction)); + pci->pcop = pcop; + + if(pci->inCond & PCC_EXAMINE_PCOP) + pci->inCond |= RegCond(pcop); + + if(pci->outCond & PCC_EXAMINE_PCOP) + pci->outCond |= RegCond(pcop); + + pci->pc.prev = pci->pc.next = NULL; + return (pCode *)pci; + } + + fprintf(stderr, "pCode mnemonic error %s,%d\n",__FUNCTION__,__LINE__); + exit(1); + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* pic16_newpCodeWild - create a "wild" as in wild card pCode */ +/* */ +/* Wild pcodes are used during the peep hole optimizer to serve */ +/* as place holders for any instruction. When a snippet of code is */ +/* compared to a peep hole rule, the wild card opcode will match */ +/* any instruction. However, the optional operand and label are */ +/* additional qualifiers that must also be matched before the */ +/* line (of assembly code) is declared matched. Note that the */ +/* operand may be wild too. */ +/* */ +/* Note, a wild instruction is specified just like a wild var: */ +/* %4 ; A wild instruction, */ +/* See the peeph.def file for additional examples */ +/* */ +/*-----------------------------------------------------------------*/ + +pCode *pic16_newpCodeWild(int pCodeID, pCodeOp *optional_operand, pCodeOp *optional_label) +{ + pCodeWild *pcw; + + pcw = Safe_alloc(sizeof(pCodeWild)); + + pcw->pci.pc.type = PC_WILD; + pcw->pci.pc.prev = pcw->pci.pc.next = NULL; + pcw->pci.from = pcw->pci.to = pcw->pci.label = NULL; + pcw->pci.pc.pb = NULL; + + // pcw->pci.pc.analyze = genericAnalyze; + pcw->pci.pc.destruct = genericDestruct; + pcw->pci.pc.print = genericPrint; + + pcw->id = pCodeID; // this is the 'n' in %n + pcw->operand = optional_operand; + pcw->label = optional_label; + + pcw->mustBeBitSkipInst = FALSE; + pcw->mustNotBeBitSkipInst = FALSE; + pcw->invertBitSkipInst = FALSE; + + return ((pCode *)pcw); +} + + /*-----------------------------------------------------------------*/ +/* newPcodeInlineP - create a new pCode from a char string */ +/*-----------------------------------------------------------------*/ + + +pCode *pic16_newpCodeInlineP(const char *cP) +{ + pCodeComment *pcc; + + pcc = Safe_alloc(sizeof(pCodeComment)); + + pcc->pc.type = PC_INLINE; + pcc->pc.prev = pcc->pc.next = NULL; + //pcc->pc.from = pcc->pc.to = pcc->pc.label = NULL; + pcc->pc.pb = NULL; + + // pcc->pc.analyze = genericAnalyze; + pcc->pc.destruct = genericDestruct; + pcc->pc.print = genericPrint; + + pcc->comment = (cP != NULL) ? Safe_strdup(cP) : NULL; + + return ((pCode *)pcc); +} + +/*-----------------------------------------------------------------*/ +/* newPcodeCharP - create a new pCode from a char string */ +/*-----------------------------------------------------------------*/ + +pCode *pic16_newpCodeCharP(const char *cP) +{ + pCodeComment *pcc; + + pcc = Safe_alloc(sizeof(pCodeComment)); + + pcc->pc.type = PC_COMMENT; + pcc->pc.prev = pcc->pc.next = NULL; + //pcc->pc.from = pcc->pc.to = pcc->pc.label = NULL; + pcc->pc.pb = NULL; + + // pcc->pc.analyze = genericAnalyze; + pcc->pc.destruct = genericDestruct; + pcc->pc.print = genericPrint; + + pcc->comment = (cP != NULL) ? Safe_strdup(cP) : NULL; + + return ((pCode *)pcc); +} + +/*-----------------------------------------------------------------*/ +/* pic16_newpCodeFunction - */ +/*-----------------------------------------------------------------*/ + + +pCode *pic16_newpCodeFunction(const char *mod, const char *f) +{ + pCodeFunction *pcf; + + pcf = Safe_alloc(sizeof(pCodeFunction)); + + pcf->pc.type = PC_FUNCTION; + pcf->pc.prev = pcf->pc.next = NULL; + //pcf->pc.from = pcf->pc.to = pcf->pc.label = NULL; + pcf->pc.pb = NULL; + + //pcf->pc.analyze = genericAnalyze; + pcf->pc.destruct = genericDestruct; + pcf->pc.print = pCodePrintFunction; + + pcf->ncalled = 0; + pcf->absblock = FALSE; + + pcf->modname = (mod != NULL) ? Safe_strdup(mod) : NULL; + pcf->fname = (f != NULL) ? Safe_strdup(f) : NULL; + + pcf->stackusage = 0; + + return ((pCode *)pcf); +} + +/*-----------------------------------------------------------------*/ +/* pic16_newpCodeFlow */ +/*-----------------------------------------------------------------*/ +static void destructpCodeFlow(pCode *pc) +{ + if(!pc || !isPCFL(pc)) + return; + +/* + if(PCFL(pc)->from) + if(PCFL(pc)->to) +*/ + pic16_unlinkpCode(pc); + + deleteSet(&PCFL(pc)->registers); + deleteSet(&PCFL(pc)->from); + deleteSet(&PCFL(pc)->to); + + /* Instead of deleting the memory used by this pCode, mark + * the object as bad so that if there's a pointer to this pCode + * dangling around somewhere then (hopefully) when the type is + * checked we'll catch it. + */ + + pc->type = PC_BAD; + pic16_addpCode2pBlock(pb_dead_pcodes, pc); + +// Safe_free(pc); +} + +pCode *pic16_newpCodeFlow(void) +{ + pCodeFlow *pcflow; + + //_ALLOC(pcflow,sizeof(pCodeFlow)); + pcflow = Safe_alloc(sizeof(pCodeFlow)); + + pcflow->pc.type = PC_FLOW; + pcflow->pc.prev = pcflow->pc.next = NULL; + pcflow->pc.pb = NULL; + + // pcflow->pc.analyze = genericAnalyze; + pcflow->pc.destruct = destructpCodeFlow; + pcflow->pc.print = genericPrint; + + pcflow->pc.seq = GpcFlowSeq++; + + pcflow->from = pcflow->to = NULL; + + pcflow->inCond = PCC_NONE; + pcflow->outCond = PCC_NONE; + + pcflow->firstBank = -1; + pcflow->lastBank = -1; + + pcflow->FromConflicts = 0; + pcflow->ToConflicts = 0; + + pcflow->end = NULL; + + pcflow->registers = newSet(); + + return ((pCode *)pcflow); +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pCodeFlowLink *pic16_newpCodeFlowLink(pCodeFlow *pcflow) +{ + pCodeFlowLink *pcflowLink; + + pcflowLink = Safe_alloc(sizeof(pCodeFlowLink)); + + pcflowLink->pcflow = pcflow; + pcflowLink->bank_conflict = 0; + + return pcflowLink; +} + +/*-----------------------------------------------------------------*/ +/* pic16_newpCodeCSource - create a new pCode Source Symbol */ +/*-----------------------------------------------------------------*/ + +pCode *pic16_newpCodeCSource(int ln, const char *f, const char *l) +{ + pCodeCSource *pccs; + + pccs = Safe_alloc(sizeof(pCodeCSource)); + + pccs->pc.type = PC_CSOURCE; + pccs->pc.prev = pccs->pc.next = NULL; + pccs->pc.pb = NULL; + + pccs->pc.destruct = genericDestruct; + pccs->pc.print = genericPrint; + + pccs->line_number = ln; + pccs->line = (l != NULL) ? Safe_strdup(l) : NULL; + pccs->file_name = (f != NULL) ? Safe_strdup(f) : NULL; + + return ((pCode *)pccs); +} + + +/*******************************************************************/ +/* pic16_newpCodeAsmDir - create a new pCode Assembler Directive */ +/* added by VR 6-Jun-2003 */ +/*******************************************************************/ + +pCode *pic16_newpCodeAsmDir(const char *asdir, const char *argfmt, ...) +{ + pCodeAsmDir *pcad; + va_list ap; + char buffer[512]; + char *lbp=buffer; + + pcad = Safe_alloc(sizeof(pCodeAsmDir)); + pcad->pci.pc.type = PC_ASMDIR; + pcad->pci.pc.prev = pcad->pci.pc.next = NULL; + pcad->pci.pc.pb = NULL; + pcad->pci.isize = 2; + pcad->pci.pc.destruct = genericDestruct; + pcad->pci.pc.print = genericPrint; + + if(asdir && *asdir) { + while(isspace((const unsigned char)*asdir)) asdir++; // strip any white space from the beginning + + pcad->directive = Safe_strdup(asdir); + } + + va_start(ap, argfmt); + + memset(buffer, 0, sizeof(buffer)); + if(argfmt && *argfmt) + vsprintf(buffer, argfmt, ap); + + va_end(ap); + + while(isspace((unsigned char)*lbp)) lbp++; + + if(lbp && *lbp) + pcad->arg = Safe_strdup(lbp); + + return ((pCode *)pcad); +} + +/*-----------------------------------------------------------------*/ +/* pCodeLabelDestruct - free memory used by a label. */ +/*-----------------------------------------------------------------*/ +static void pCodeLabelDestruct(pCode *pc) +{ + if(!pc) + return; + + pic16_unlinkpCode(pc); + +// if((pc->type == PC_LABEL) && PCL(pc)->label) +// Safe_free(PCL(pc)->label); + + /* Instead of deleting the memory used by this pCode, mark + * the object as bad so that if there's a pointer to this pCode + * dangling around somewhere then (hopefully) when the type is + * checked we'll catch it. + */ + + pc->type = PC_BAD; + pic16_addpCode2pBlock(pb_dead_pcodes, pc); + +// Safe_free(pc); +} + +pCode *pic16_newpCodeLabel(const char *name, int key) +{ + const char *s; + pCodeLabel *pcl; + + pcl = Safe_alloc(sizeof(pCodeLabel)); + + pcl->pc.type = PC_LABEL; + pcl->pc.prev = pcl->pc.next = NULL; + //pcl->pc.from = pcl->pc.to = pcl->pc.label = NULL; + pcl->pc.pb = NULL; + + // pcl->pc.analyze = genericAnalyze; + pcl->pc.destruct = pCodeLabelDestruct; + pcl->pc.print = pCodePrintLabel; + + pcl->key = key; + pcl->force = FALSE; + + if(key>0) { + SNPRINTF(buffer, sizeof(buffer), "_%05d_DS_",key); + s = buffer; + } else + s = name; + + pcl->label = (s != NULL) ? Safe_strdup(s) : NULL; + +// if(pic16_pcode_verbose) +// fprintf(stderr, "%s:%d label name: %s\n", __FILE__, __LINE__, pcl->label); + + return ((pCode *)pcl); +} + +pCode *pic16_newpCodeLabelFORCE(const char *name, int key) +{ + pCodeLabel *pcl = (pCodeLabel *)pic16_newpCodeLabel(name, key); + + pcl->force = TRUE; + + return ((pCode *)pcl); +} + +pCode *pic16_newpCodeInfo(INFO_TYPE type, pCodeOp *pcop) +{ + pCodeInfo *pci; + + pci = Safe_alloc(sizeof(pCodeInfo)); + pci->pci.pc.type = PC_INFO; + pci->pci.pc.prev = pci->pci.pc.next = NULL; + pci->pci.pc.pb = NULL; + pci->pci.label = NULL; + + pci->pci.pc.destruct = genericDestruct; + pci->pci.pc.print = genericPrint; + + pci->type = type; + pci->oper1 = pcop; + + return ((pCode *)pci); +} + + +/*-----------------------------------------------------------------*/ +/* newpBlock - create and return a pointer to a new pBlock */ +/*-----------------------------------------------------------------*/ +static pBlock *newpBlock(void) +{ + pBlock *PpB; + + PpB = Safe_alloc(sizeof(pBlock)); + PpB->next = PpB->prev = NULL; + + PpB->function_entries = PpB->function_exits = PpB->function_calls = NULL; + PpB->tregisters = NULL; + PpB->visited = FALSE; + PpB->FlowTree = NULL; + + return PpB; +} + +/*-----------------------------------------------------------------*/ +/* pic16_newpCodeChain - create a new chain of pCodes */ +/*-----------------------------------------------------------------* + * + * This function will create a new pBlock and the pointer to the + * pCode that is passed in will be the first pCode in the block. + *-----------------------------------------------------------------*/ + + +pBlock *pic16_newpCodeChain(memmap *cm,char c, pCode *pc) +{ + pBlock *pB = newpBlock(); + + pB->pcHead = pB->pcTail = pc; + pB->cmemmap = cm; + pB->dbName = c; + + return pB; +} + +/*-----------------------------------------------------------------*/ +/* pic16_newpCodeOpLabel - Create a new label given the key */ +/* Note, a negative key means that the label is part of wild card */ +/* (and hence a wild card label) used in the pCodePeep */ +/* optimizations). */ +/*-----------------------------------------------------------------*/ + +pCodeOp *pic16_newpCodeOpLabel(const char *name, int key) +{ + static int label_key = -1; + + const char *s; + + pCodeOp *pcop; + + pcop = Safe_alloc(sizeof(pCodeOpLabel)); + pcop->type = PO_LABEL; + + if(key>0) { + SNPRINTF(buffer, sizeof(buffer), "_%05d_DS_",key); + s = buffer; + } + else { + key = label_key--; + s = name; + } + + pcop->name = (s != NULL) ? Safe_strdup(s) : NULL; + + ((pCodeOpLabel *)pcop)->key = key; + + //fprintf(stderr,"pic16_newpCodeOpLabel: key=%d, name=%s\n",key,((s)?s:"")); + return pcop; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pCodeOp *pic16_newpCodeOpLit(int lit) +{ + pCodeOp *pcop; + + pcop = Safe_alloc(sizeof(pCodeOpLit)); + pcop->type = PO_LITERAL; + + //if(lit>=0) + SNPRINTF(buffer, sizeof(buffer), "0x%02x", (unsigned char)lit); + //else + // SNPRINTF(buffer, sizeof(buffer), "%i", lit); + + pcop->name = Safe_strdup(buffer); + + ((pCodeOpLit *)pcop)->lit = lit; + + return pcop; +} + +/* Allow for 12 bit literals, required for LFSR */ +pCodeOp *pic16_newpCodeOpLit12(int lit) +{ + pCodeOp *pcop; + + pcop = Safe_alloc(sizeof(pCodeOpLit)); + pcop->type = PO_LITERAL; + + //if(lit>=0) + SNPRINTF(buffer, sizeof(buffer), "0x%03x", ((unsigned int)lit) & 0x0fff); + //else + // SNPRINTF(buffer, sizeof(buffer), "%i", lit); + + pcop->name = Safe_strdup(buffer); + + ((pCodeOpLit *)pcop)->lit = lit; + + return pcop; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pCodeOp *pic16_newpCodeOpLit2(int lit, pCodeOp *arg2) +{ + char tbuf[256], *tb = tbuf; + pCodeOp *pcop; + + tb = pic16_get_op(arg2, NULL, 0); + pcop = Safe_alloc(sizeof(pCodeOpLit2)); + pcop->type = PO_LITERAL; + + //pcop->name = NULL; + //if(lit>=0) { + SNPRINTF(buffer, sizeof(buffer), "0x%02x, %s", (unsigned char)lit, tb); + pcop->name = Safe_strdup(buffer); + //} + + ((pCodeOpLit2 *)pcop)->lit = lit; + ((pCodeOpLit2 *)pcop)->arg2 = arg2; + + return pcop; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pCodeOp *pic16_newpCodeOpImmd(const char *name, int offset, int index, int code_space) +{ + pCodeOp *pcop; + + pcop = Safe_alloc(sizeof(pCodeOpImmd)); + pcop->type = PO_IMMEDIATE; + if(name) { + reg_info *r = pic16_dirregWithName(name); + pcop->name = Safe_strdup(name); + PCOI(pcop)->r = r; + + if(r) { +// fprintf(stderr, "%s:%d %s reg %s exists (r: %p)\n",__FILE__, __LINE__, __FUNCTION__, name, r); + PCOI(pcop)->rIdx = r->rIdx; + } else { +// fprintf(stderr, "%s:%d %s reg %s doesn't exist\n", __FILE__, __LINE__, __FUNCTION__, name); + PCOI(pcop)->rIdx = -1; + } +// fprintf(stderr,"%s %s %d\n",__FUNCTION__,name,offset); + } else { + pcop->name = NULL; + PCOI(pcop)->rIdx = -1; + } + + PCOI(pcop)->index = index; + PCOI(pcop)->offset = offset; + PCOI(pcop)->_const = code_space; + + return pcop; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pCodeOp *pic16_newpCodeOpWild(int id, pCodeWildBlock *pcwb, pCodeOp *subtype) +{ + pCodeOp *pcop; + + if(!pcwb || !subtype) { + fprintf(stderr, "Wild opcode declaration error: %s-%d\n",__FILE__,__LINE__); + exit(1); + } + + pcop = Safe_alloc(sizeof(pCodeOpWild)); + pcop->type = PO_WILD; + SNPRINTF(buffer, sizeof(buffer), "%%%d", id); + pcop->name = Safe_strdup(buffer); + + PCOW(pcop)->id = id; + PCOW(pcop)->pcwb = pcwb; + PCOW(pcop)->subtype = subtype; + PCOW(pcop)->matched = NULL; + + PCOW(pcop)->pcop2 = NULL; + + return pcop; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pCodeOp *pic16_newpCodeOpWild2(int id, int id2, pCodeWildBlock *pcwb, pCodeOp *subtype, pCodeOp *subtype2) +{ + pCodeOp *pcop; + + if(!pcwb || !subtype || !subtype2) { + fprintf(stderr, "Wild opcode declaration error: %s-%d\n",__FILE__,__LINE__); + exit(1); + } + + pcop = Safe_alloc(sizeof(pCodeOpWild)); + pcop->type = PO_WILD; + SNPRINTF(buffer, sizeof(buffer), "%%%d", id); + pcop->name = Safe_strdup(buffer); + + PCOW(pcop)->id = id; + PCOW(pcop)->pcwb = pcwb; + PCOW(pcop)->subtype = subtype; + PCOW(pcop)->matched = NULL; + + PCOW(pcop)->pcop2 = Safe_alloc(sizeof(pCodeOpWild)); + + if(!subtype2->name) { + PCOW(pcop)->pcop2 = Safe_alloc(sizeof(pCodeOpWild)); + PCOW2(pcop)->pcop.type = PO_WILD; + SNPRINTF(buffer, sizeof(buffer), "%%%d", id2); + PCOW2(pcop)->pcop.name = Safe_strdup(buffer); + PCOW2(pcop)->id = id2; + PCOW2(pcop)->subtype = subtype2; + +// fprintf(stderr, "%s:%d %s [wild,wild] for name: %s (%d)\tname2: %s (%d)\n", __FILE__, __LINE__, __FUNCTION__, +// pcop->name, id, PCOW2(pcop)->pcop.name, id2); + } else { + PCOW2(pcop)->pcop2 = pic16_pCodeOpCopy(subtype2); + +// fprintf(stderr, "%s:%d %s [wild,str] for name: %s (%d)\tname2: %s (%d)\n", __FILE__, __LINE__, __FUNCTION__, +// pcop->name, id, PCOW2(pcop)->pcop.name, id2); + } + + return pcop; +} + + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pCodeOp *pic16_newpCodeOpBit(const char *s, int bit, int inBitSpace, PIC_OPTYPE subt) +{ + pCodeOp *pcop; + + pcop = Safe_alloc(sizeof(pCodeOpRegBit)); + pcop->type = PO_GPR_BIT; + pcop->name = (s != NULL) ? Safe_strdup(s) : NULL; + PCORB(pcop)->bit = bit; + PCORB(pcop)->inBitSpace = inBitSpace; + PCORB(pcop)->subtype = subt; + + /* pCodeOpBit is derived from pCodeOpReg. We need to init this too */ + PCOR(pcop)->r = pic16_regWithName(s); //NULL; +// fprintf(stderr, "%s:%d %s for reg: %s\treg= %p\n", __FILE__, __LINE__, __FUNCTION__, s, PCOR(pcop)->r); +// PCOR(pcop)->rIdx = 0; + return pcop; +} + +pCodeOp *pic16_newpCodeOpBit_simple (struct asmop *op, int offs, int bit) +{ + return pic16_newpCodeOpBit (pic16_aopGet(op,offs,FALSE,FALSE), + bit, 0, PO_GPR_REGISTER); +} + + +/*-----------------------------------------------------------------* + * pCodeOp *pic16_newpCodeOpReg(int rIdx) - allocate a new register + * + * If rIdx >=0 then a specific register from the set of registers + * will be selected. If rIdx <0, then a new register will be searched + * for. + *-----------------------------------------------------------------*/ + +pCodeOp *pic16_newpCodeOpReg(int rIdx) +{ + pCodeOp *pcop; + reg_info *r; + + pcop = Safe_alloc(sizeof(pCodeOpReg)); + + pcop->name = NULL; + + if(rIdx >= 0) { + r = pic16_regWithIdx(rIdx); + if(!r) + r = pic16_allocWithIdx(rIdx); + } else { + r = pic16_findFreeReg(REG_GPR); + + if(!r) { + fprintf(stderr, "%s:%d Could not find a free GPR register\n", + __FUNCTION__, __LINE__); + exit(EXIT_FAILURE); + } + } + + PCOR(pcop)->rIdx = rIdx; + PCOR(pcop)->r = r; + pcop->type = PCOR(pcop)->r->pc_type; + + return pcop; +} + +pCodeOp *pic16_newpCodeOpRegNotVect(bitVect *bv) +{ + pCodeOp *pcop; + reg_info *r; + + pcop = Safe_alloc(sizeof(pCodeOpReg)); + pcop->name = NULL; + + r = pic16_findFreeReg(REG_GPR); + + while(r) { + if(!bitVectBitValue(bv, r->rIdx)) { + PCOR(pcop)->r = r; + PCOR(pcop)->rIdx = r->rIdx; + pcop->type = r->pc_type; + return (pcop); + } + + r = pic16_findFreeRegNext(REG_GPR, r); + } + + return NULL; +} + + + +pCodeOp *pic16_newpCodeOpRegFromStr(const char *name) +{ + pCodeOp *pcop; + reg_info *r; + + pcop = Safe_alloc(sizeof(pCodeOpReg)); + PCOR(pcop)->r = r = pic16_allocRegByName(name, 1, NULL); + PCOR(pcop)->rIdx = PCOR(pcop)->r->rIdx; + pcop->type = PCOR(pcop)->r->pc_type; + pcop->name = PCOR(pcop)->r->name; + +// if(pic16_pcode_verbose) { +// fprintf(stderr, "%s:%d %s allocates register %s rIdx:0x%02x\n", +// __FILE__, __LINE__, __FUNCTION__, r->name, r->rIdx); +// } + + return pcop; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pCodeOp *pic16_newpCodeOpOpt(OPT_TYPE type, const char *key) +{ + pCodeOpOpt *pcop; + + pcop = Safe_alloc(sizeof(pCodeOpOpt)); + + pcop->type = type; + pcop->key = Safe_strdup(key); + + return (PCOP(pcop)); +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pCodeOp *pic16_newpCodeOpLocalRegs(LR_TYPE type) +{ + pCodeOpLocalReg *pcop; + + pcop = Safe_alloc(sizeof(pCodeOpLocalReg)); + + pcop->type = type; + + return (PCOP(pcop)); +} + + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ + +pCodeOp *pic16_newpCodeOp(const char *name, PIC_OPTYPE type) +{ + pCodeOp *pcop; + + switch(type) { + case PO_BIT: + case PO_GPR_BIT: + pcop = pic16_newpCodeOpBit(name, -1,0, type); + break; + + case PO_LITERAL: + pcop = pic16_newpCodeOpLit(-1); + break; + + case PO_LABEL: + pcop = pic16_newpCodeOpLabel(NULL,-1); + break; + + case PO_GPR_TEMP: + pcop = pic16_newpCodeOpReg(-1); + break; + + case PO_GPR_REGISTER: + pcop = (name != NULL) ? pic16_newpCodeOpRegFromStr(name) : pic16_newpCodeOpReg(-1); + break; + + case PO_TWO_OPS: + assert( !"Cannot create PO_TWO_OPS from string!" ); + pcop = NULL; + break; + + default: + pcop = Safe_alloc(sizeof(pCodeOp)); + pcop->type = type; + pcop->name = (name != NULL) ? Safe_strdup(name) : NULL; + } + + return pcop; +} + +pCodeOp *pic16_newpCodeOp2(pCodeOp *src, pCodeOp *dst) +{ + pCodeOp2 *pcop2 = Safe_alloc(sizeof(pCodeOp2)); + pcop2->pcop.type = PO_TWO_OPS; + pcop2->pcopL = src; + pcop2->pcopR = dst; + return PCOP(pcop2); +} + +/* This is a multiple of two as gpasm pads DB directives to even length, + * thus the data would be interleaved with \0 bytes... + * This is a multiple of three in order to have arrays of 3-byte pointers + * continuously in memory (without 0-padding at the lines' end). + * This is rather 12 than 6 in order not to split up 4-byte data types + * in arrays right in the middle of a 4-byte word. */ +#define DB_ITEMS_PER_LINE 12 + +typedef struct DBdata + { + int count; + char buffer[512]; + } DBdata; + +struct DBdata DBd; +static int DBd_init = -1; + +/*-----------------------------------------------------------------*/ +/* Initialiase "DB" data buffer */ +/*-----------------------------------------------------------------*/ +void pic16_initDB(void) +{ + DBd_init = -1; +} + + +/*-----------------------------------------------------------------*/ +/* Flush pending "DB" data to a pBlock */ +/* */ +/* ptype - type of p pointer, 'f' file pointer, 'p' pBlock pointer */ +/*-----------------------------------------------------------------*/ +void pic16_flushDB(char ptype, void *p) +{ + if (DBd.count>0) { + if(ptype == 'p') + pic16_addpCode2pBlock(((pBlock *)p),pic16_newpCodeAsmDir("DB", "%s", DBd.buffer)); + else + if(ptype == 'f') + fprintf(((FILE *)p), "\tdb\t%s\n", DBd.buffer); + else { + /* sanity check */ + fprintf(stderr, "PIC16 port error: could not emit initial value data\n"); + } + + DBd.count = 0; + DBd.buffer[0] = '\0'; + } +} + + +/*-----------------------------------------------------------------*/ +/* Add "DB" directives to a pBlock */ +/*-----------------------------------------------------------------*/ +void pic16_emitDB(int c, char ptype, void *p) +{ + size_t l; + + if (DBd_init < 0) { + // we need to initialize + DBd_init = 0; + DBd.count = 0; + DBd.buffer[0] = '\0'; + } + + l = strlen(DBd.buffer); + + if (sizeof(DBd.buffer) <= l) { + fprintf(stderr, "%s() -- Error: Size of DBd.buffer too small. (%zu <= %zu)\n", __func__, sizeof(DBd.buffer), l); + exit(1); + } + + SNPRINTF(DBd.buffer + l, sizeof(DBd.buffer) - l, "%s0x%02x", ((DBd.count > 0) ? ", " : ""), c & 0xff); + +// fprintf(stderr, "%s:%d DBbuffer: '%s'\n", __FILE__, __LINE__, DBd.buffer); + + DBd.count++; + if (DBd.count >= DB_ITEMS_PER_LINE) + pic16_flushDB(ptype, p); +} + +void pic16_emitDS(const char *s, char ptype, void *p) +{ + size_t l; + + if (DBd_init < 0) { + // we need to initialize + DBd_init = 0; + DBd.count = 0; + DBd.buffer[0] = '\0'; + } + + l = strlen(DBd.buffer); + + if (sizeof(DBd.buffer) <= l) { + fprintf(stderr, "%s() -- Error: Size of DBd.buffer too small. (%zu <= %zu)\n", __func__, sizeof(DBd.buffer), l); + exit(1); + } + + SNPRINTF(DBd.buffer + l, sizeof(DBd.buffer) - l, "%s%s", ((DBd.count > 0) ? ", " : ""), s); + +// fprintf(stderr, "%s:%d DBbuffer: '%s'\n", __FILE__, __LINE__, DBd.buffer); + + DBd.count++; //=strlen(s); + if (DBd.count >= DB_ITEMS_PER_LINE) + pic16_flushDB(ptype, p); +} + + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +void pic16_pCodeConstString(const char *name, const char *value, unsigned length) +{ + pBlock *pb; + char *item; + static set *emittedSymbols = NULL; + + if(!name || !value) + return; + + /* keep track of emitted symbols to avoid multiple definition of str_<nr> */ + if (emittedSymbols) { + /* scan set for name */ + for (item = setFirstItem (emittedSymbols); item; item = setNextItem (emittedSymbols)) + { + if (!strcmp (item,name)) { + //fprintf (stderr, "%s already emitted\n", name); + return; + } // if + } // for + } // if + addSet (&emittedSymbols, Safe_strdup (name)); + + //fprintf(stderr, " %s %s %s\n",__FUNCTION__,name,value); + + pb = pic16_newpCodeChain(NULL, 'P',pic16_newpCodeCharP("; Starting pCode block")); + + pic16_addpBlock(pb); + +// SNPRINTF(buffer, sizeof(buffer), "; %s = %s", name, value); +// fputs(buffer, stderr); + +// pic16_addpCode2pBlock(pb,pic16_newpCodeCharP(buffer)); + pic16_addpCode2pBlock(pb,pic16_newpCodeLabel(name,-1)); + + while (length--) + pic16_emitDB(*value++, 'p', (void *)pb); + + pic16_flushDB('p', (void *)pb); +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +#if 0 +static void pCodeReadCodeTable(void) +{ + pBlock *pb; + + fprintf(stderr, " %s\n",__FUNCTION__); + + pb = pic16_newpCodeChain(NULL, 'P',pic16_newpCodeCharP("; Starting pCode block")); + + pic16_addpBlock(pb); + + pic16_addpCode2pBlock(pb,pic16_newpCodeCharP("; ReadCodeTable - built in function")); + pic16_addpCode2pBlock(pb,pic16_newpCodeCharP("; Inputs: temp1,temp2 = code pointer")); + pic16_addpCode2pBlock(pb,pic16_newpCodeCharP("; Outpus: W (from RETLW at temp2:temp1)")); + pic16_addpCode2pBlock(pb,pic16_newpCodeLabel("ReadCodeTable:",-1)); + + pic16_addpCode2pBlock(pb,pic16_newpCode(POC_MOVFW,pic16_newpCodeOpRegFromStr("temp2"))); + pic16_addpCode2pBlock(pb,pic16_newpCode(POC_MOVWF,pic16_newpCodeOpRegFromStr("PCLATH"))); + pic16_addpCode2pBlock(pb,pic16_newpCode(POC_MOVFW,pic16_newpCodeOpRegFromStr("temp1"))); + pic16_addpCode2pBlock(pb,pic16_newpCode(POC_MOVWF,pic16_newpCodeOpRegFromStr("PCL"))); + + +} +#endif +/*-----------------------------------------------------------------*/ +/* pic16_addpCode2pBlock - place the pCode into the pBlock linked list */ +/*-----------------------------------------------------------------*/ +void pic16_addpCode2pBlock(pBlock *pb, pCode *pc) +{ + if(!pc) + return; + + if(!pb->pcHead) { + /* If this is the first pcode to be added to a block that + * was initialized with a NULL pcode, then go ahead and + * make this pcode the head and tail */ + pb->pcHead = pb->pcTail = pc; + } else { + // if(pb->pcTail) + pb->pcTail->next = pc; + + pc->prev = pb->pcTail; + pc->pb = pb; + + pb->pcTail = pc; + } +} + +/*-----------------------------------------------------------------*/ +/* pic16_addpBlock - place a pBlock into the pFile */ +/*-----------------------------------------------------------------*/ +void pic16_addpBlock(pBlock *pb) +{ + // fprintf(stderr," Adding pBlock: dbName =%c\n",getpBlock_dbName(pb)); + + if(!the_pFile) { + /* First time called, we'll pass through here. */ + //_ALLOC(the_pFile,sizeof(pFile)); + the_pFile = Safe_alloc(sizeof(pFile)); + the_pFile->pbHead = the_pFile->pbTail = pb; + the_pFile->functions = NULL; + return; + } + + the_pFile->pbTail->next = pb; + pb->prev = the_pFile->pbTail; + pb->next = NULL; + the_pFile->pbTail = pb; +} + +/*-----------------------------------------------------------------*/ +/* removepBlock - remove a pBlock from the pFile */ +/*-----------------------------------------------------------------*/ +static void removepBlock(pBlock *pb) +{ + pBlock *pbs; + + if(!the_pFile) + return; + + //fprintf(stderr," Removing pBlock: dbName =%c\n",getpBlock_dbName(pb)); + + for(pbs = the_pFile->pbHead; pbs; pbs = pbs->next) { + if(pbs == pb) { + + if(pbs == the_pFile->pbHead) + the_pFile->pbHead = pbs->next; + + if (pbs == the_pFile->pbTail) + the_pFile->pbTail = pbs->prev; + + if(pbs->next) + pbs->next->prev = pbs->prev; + + if(pbs->prev) + pbs->prev->next = pbs->next; + + return; + + } + } + + fprintf(stderr, "Warning: call to %s:%s didn't find pBlock\n",__FILE__,__FUNCTION__); +} + +/*-----------------------------------------------------------------*/ +/* printpCode - write the contents of a pCode to a file */ +/*-----------------------------------------------------------------*/ +static void printpCode(FILE *of, pCode *pc) +{ + if(!pc || !of) + return; + + if(pc->print) { + pc->print(of,pc); + return; + } + + fprintf(of,"warning - unable to print pCode\n"); +} + +/*-----------------------------------------------------------------*/ +/* pic16_printpBlock - write the contents of a pBlock to a file */ +/*-----------------------------------------------------------------*/ +void pic16_printpBlock(FILE *of, pBlock *pb) +{ + pCode *pc; + + if(!pb)return; + + if(!of)of=stderr; + + for(pc = pb->pcHead; pc; pc = pc->next) { + if(isPCF(pc) && PCF(pc)->fname) { + fprintf(of, "S_%s_%s\tcode", PCF(pc)->modname, PCF(pc)->fname); + if(pb->dbName == 'A') { + absSym *ab; + for(ab=setFirstItem(absSymSet); ab; ab=setNextItem(absSymSet)) { +// fprintf(stderr, "%s:%d testing %s <-> %s\n", __FILE__, __LINE__, PCF(pc)->fname, ab->name); + if(!strcmp(ab->name, PCF(pc)->fname)) { +// fprintf(stderr, "%s:%d address = %x\n", __FILE__, __LINE__, ab->address); + if(ab->address != -1) + fprintf(of, "\t0X%06X", ab->address); + break; + } + } + } + fprintf(of, "\n"); + } + printpCode(of,pc); + } +} + +/*-----------------------------------------------------------------*/ +/* */ +/* pCode processing */ +/* */ +/* */ +/* */ +/*-----------------------------------------------------------------*/ +pCode * pic16_findNextInstruction(pCode *pci); +pCode * pic16_findPrevInstruction(pCode *pci); + +void pic16_unlinkpCode(pCode *pc) +{ + pCode *prev; + + if(pc) { +#ifdef PCODE_DEBUG + fprintf(stderr,"Unlinking: "); + printpCode(stderr, pc); +#endif + if(pc->prev) { + pc->prev->next = pc->next; + } else if (pc->pb && (pc->pb->pcHead == pc)) { + pc->pb->pcHead = pc->next; + } + if(pc->next) { + pc->next->prev = pc->prev; + } else if (pc->pb && (pc->pb->pcTail == pc)) { + pc->pb->pcTail = pc->prev; + } + + /* move C source line down (or up) */ + if (isPCI(pc) && PCI(pc)->cline) { + prev = pic16_findNextInstruction (pc->next); + if (prev && isPCI(prev) && !PCI(prev)->cline) { + PCI(prev)->cline = PCI(pc)->cline; + } else { + prev = pic16_findPrevInstruction (pc->prev); + if (prev && isPCI(prev) && !PCI(prev)->cline) + PCI(prev)->cline = PCI(pc)->cline; + } + } + pc->prev = pc->next = NULL; + } +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ + +static void genericDestruct(pCode *pc) +{ + pic16_unlinkpCode(pc); + + if(isPCI(pc)) { + /* For instructions, tell the register (if there's one used) + * that it's no longer needed */ + reg_info *reg = pic16_getRegFromInstruction(pc); + if(reg) + deleteSetItem (&(reg->reglives.usedpCodes),pc); + + if(PCI(pc)->is2MemOp) { + reg = pic16_getRegFromInstruction2(pc); + if(reg) + deleteSetItem(&(reg->reglives.usedpCodes), pc); + } + } + + /* Instead of deleting the memory used by this pCode, mark + * the object as bad so that if there's a pointer to this pCode + * dangling around somewhere then (hopefully) when the type is + * checked we'll catch it. + */ + + pc->type = PC_BAD; + pic16_addpCode2pBlock(pb_dead_pcodes, pc); + + //Safe_free(pc); +} + + +void DEBUGpic16_emitcode (char *inst,char *fmt, ...); +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +/* modifiers for constant immediate */ + +static const char *immdmod[3] = {"LOW", "HIGH", "UPPER"}; + +char *pic16_get_op(pCodeOp *pcop, char *buffer, size_t size) +{ + reg_info *r; + static char b[128]; + char *s; + int use_buffer = 1; // copy the string to the passed buffer pointer + + if(!buffer) { + buffer = b; + size = sizeof(b); + use_buffer = 0; // Don't bother copying the string to the buffer. + } + + if(pcop) { + + switch(pcop->type) { + case PO_W: + case PO_WREG: + case PO_PRODL: + case PO_PRODH: + case PO_INDF0: + case PO_FSR0: + if(use_buffer) { + SNPRINTF(buffer,size,"%s",PCOR(pcop)->r->name); + return (buffer); + } + return (PCOR(pcop)->r->name); + break; + case PO_GPR_TEMP: + r = pic16_regWithIdx(PCOR(pcop)->r->rIdx); + if(use_buffer) { + SNPRINTF(buffer,size,"%s",r->name); + return (buffer); + } + return (r->name); + break; + + case PO_IMMEDIATE: + s = buffer; + if(PCOI(pcop)->offset && PCOI(pcop)->offset<4) { + if(PCOI(pcop)->index) { + SNPRINTF(s,size, "%s(%s + %d)", + immdmod[ PCOI(pcop)->offset ], + pcop->name, + PCOI(pcop)->index); + } else { + SNPRINTF(s,size,"%s(%s)", + immdmod[ PCOI(pcop)->offset ], + pcop->name); + } + } else { + if(PCOI(pcop)->index) { + SNPRINTF(s,size, "%s(%s + %d)", + immdmod[ 0 ], + pcop->name, + PCOI(pcop)->index); + } else { + SNPRINTF(s,size, "%s(%s)", + immdmod[ 0 ], + pcop->name); + } + } + return (buffer); + break; + + case PO_GPR_REGISTER: + case PO_DIR: + s = buffer; + //size = sizeof(buffer); + if( PCOR(pcop)->instance) { + SNPRINTF(s,size,"(%s + %d)", + pcop->name, + PCOR(pcop)->instance ); + } else { + SNPRINTF(s,size,"%s",pcop->name); + } + return (buffer); + break; + + case PO_GPR_BIT: + s = buffer; + if(PCORB(pcop)->subtype == PO_GPR_TEMP) { + SNPRINTF(s, size, "%s", pcop->name); + } else { + if(PCORB(pcop)->pcor.instance) + SNPRINTF(s, size, "(%s + %d)", pcop->name, PCORB(pcop)->pcor.instance); + else + SNPRINTF(s, size, "%s", pcop->name); + } + return (buffer); + break; + + case PO_TWO_OPS: + return (pic16_get_op( PCOP2(pcop)->pcopL, use_buffer ? buffer : NULL, size )); + break; + + default: + if(pcop->name) { + if(use_buffer) { + SNPRINTF(buffer,size,"%s",pcop->name); + return (buffer); + } + return (pcop->name); + } + + } + return ("unhandled type for op1"); + } + + return ("NO operand1"); +} + +/*-----------------------------------------------------------------*/ +/* pic16_get_op2 - variant to support two memory operand commands */ +/*-----------------------------------------------------------------*/ +char *pic16_get_op2(pCodeOp *pcop, char *buffer, size_t size) +{ + if(pcop && pcop->type == PO_TWO_OPS) { + return pic16_get_op(PCOP2(pcop)->pcopR, buffer, size); + } + + return "NO operand2"; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static char *pic16_get_op_from_instruction( pCodeInstruction *pcc) +{ + if(pcc) + return pic16_get_op(pcc->pcop,NULL,0); + + /* gcc 3.2: warning: concatenation of string literals with __FUNCTION__ is deprecated + * return ("ERROR Null: "__FUNCTION__); + */ + return ("ERROR Null: pic16_get_op_from_instruction"); +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void pCodeOpPrint(FILE *of, pCodeOp *pcop) +{ + fprintf(of,"pcodeopprint- not implemented\n"); +} + +/*-----------------------------------------------------------------*/ +/* pic16_pCode2str - convert a pCode instruction to string */ +/*-----------------------------------------------------------------*/ +char *pic16_pCode2str(char *str, size_t size, pCode *pc) +{ + char *s = str; + reg_info *r; + size_t len; + +#if 0 + if(isPCI(pc) && (PCI(pc)->pci_magic != PCI_MAGIC)) { + fprintf(stderr, "%s:%d: pCodeInstruction initialization error in instruction %s, magic is %x (defaut: %x)\n", + __FILE__, __LINE__, PCI(pc)->mnemonic, PCI(pc)->pci_magic, PCI_MAGIC); + // exit(EXIT_FAILURE); + } +#endif + + switch(pc->type) { + + case PC_OPCODE: + SNPRINTF(s, size, "\t%s\t", PCI(pc)->mnemonic); + len = strlen(s); + size -= len; + s += len; + + if( (PCI(pc)->num_ops >= 1) && (PCI(pc)->pcop)) { + + if (PCI(pc)->pcop->type == PO_TWO_OPS) + { + /* split into two phases due to static buffer in pic16_get_op() */ + SNPRINTF(s, size, "%s", pic16_get_op((PCI(pc)->pcop), NULL, 0)); + len = strlen(s); + size -= len; + s += len; + SNPRINTF(s, size, ", %s", pic16_get_op2((PCI(pc)->pcop), NULL, 0)); + break; + } + + if(PCI(pc)->is2LitOp) { + SNPRINTF(s,size, "%s", PCOP(PCI(pc)->pcop)->name); + break; + } + + if(PCI(pc)->isBitInst) { + if(PCI(pc)->pcop->type != PO_GPR_BIT) { + if( (((pCodeOpRegBit *)(PCI(pc)->pcop))->inBitSpace) ) + SNPRINTF(s,size,"(%s >> 3), (%s & 7)", PCI(pc)->pcop->name, PCI(pc)->pcop->name); + else + SNPRINTF(s,size,"%s,%d", pic16_get_op_from_instruction(PCI(pc)), + (((pCodeOpRegBit *)(PCI(pc)->pcop))->bit)); + + } else if(PCI(pc)->pcop->type == PO_GPR_BIT) { + SNPRINTF(s,size,"%s, %d", pic16_get_op_from_instruction(PCI(pc)),PCORB(PCI(pc)->pcop)->bit); + } else + SNPRINTF(s,size,"%s,0 ; ?bug", pic16_get_op_from_instruction(PCI(pc))); + } else { + + if(PCI(pc)->pcop->type == PO_GPR_BIT) { + if(PCI(pc)->num_ops == 3) + SNPRINTF(s,size,"(%s >> 3),%c",pic16_get_op_from_instruction(PCI(pc)),((PCI(pc)->isModReg) ? 'F':'W')); + else + SNPRINTF(s,size,"(1 << (%s & 7))",pic16_get_op_from_instruction(PCI(pc))); + } else { + SNPRINTF(s,size,"%s", pic16_get_op_from_instruction(PCI(pc))); + } + } + + if(PCI(pc)->num_ops == 3 || ((PCI(pc)->num_ops == 2) && (PCI(pc)->isAccess))) { + len = strlen(s); + size -= len; + s += len; + if(PCI(pc)->num_ops == 3 && !PCI(pc)->isBitInst) { + SNPRINTF(s,size,", %c", ( (PCI(pc)->isModReg) ? 'F':'W')); + len = strlen(s); + size -= len; + s += len; + } + + r = pic16_getRegFromInstruction(pc); + + if(PCI(pc)->isAccess) { + static char *bank_spec[2][2] = { + { "", ", ACCESS" }, /* gpasm uses access bank by default */ + { ", B", ", BANKED" }/* MPASM (should) use BANKED by default */ + }; + + SNPRINTF(s,size,"%s", bank_spec[(r && !isACCESS_BANK(r)) ? 1 : 0][pic16_mplab_comp ? 1 : 0]); + } + } + } + break; + + case PC_COMMENT: + /* assuming that comment ends with a \n */ + SNPRINTF(s,size,";%s", ((pCodeComment *)pc)->comment); + break; + + case PC_INFO: + SNPRINTF(s,size,"; info ==>"); + len = strlen(s); + size -= len; + s += len; + switch( PCINF(pc)->type ) { + case INF_OPTIMIZATION: + SNPRINTF(s,size, " [optimization] %s\n", OPT_TYPE_STR[ PCOO(PCINF(pc)->oper1)->type ]); + break; + case INF_LOCALREGS: + SNPRINTF(s,size, " [localregs] %s\n", LR_TYPE_STR[ PCOLR(PCINF(pc)->oper1)->type ]); + break; + }; break; + + case PC_INLINE: + /* assuming that inline code ends with a \n */ + SNPRINTF(s,size,"%s", ((pCodeComment *)pc)->comment); + break; + + case PC_LABEL: + SNPRINTF(s,size,";label=%s, key=%d\n",PCL(pc)->label,PCL(pc)->key); + break; + case PC_FUNCTION: + SNPRINTF(s,size,";modname=%s,function=%s: id=%d\n",PCF(pc)->modname,PCF(pc)->fname); + break; + case PC_WILD: + SNPRINTF(s,size,";\tWild opcode: id=%d\n",PCW(pc)->id); + break; + case PC_FLOW: + SNPRINTF(s,size,";\t--FLOW change.\n"); + break; + case PC_CSOURCE: + SNPRINTF(s,size,"%s\t.line\t%d; %s\t%s\n", ((pic16_mplab_comp || !options.debug)?";":""), + PCCS(pc)->line_number, PCCS(pc)->file_name, PCCS(pc)->line); + break; + case PC_ASMDIR: + if(PCAD(pc)->directive) { + SNPRINTF(s,size,"\t%s%s%s\n", PCAD(pc)->directive, PCAD(pc)->arg?"\t":"", PCAD(pc)->arg?PCAD(pc)->arg:""); + } else + if(PCAD(pc)->arg) { + /* special case to handle inline labels without a tab */ + SNPRINTF(s,size,"%s\n", PCAD(pc)->arg); + } + break; + + case PC_BAD: + SNPRINTF(s,size,";A bad pCode is being used.\n"); + break; + } + + return str; +} + +/*-----------------------------------------------------------------*/ +/* genericPrint - the contents of a pCode to a file */ +/*-----------------------------------------------------------------*/ +static void genericPrint(FILE *of, pCode *pc) +{ + if(!pc || !of) + return; + + switch(pc->type) { + case PC_COMMENT: +// fputs(((pCodeComment *)pc)->comment, of); + fprintf(of,"; %s\n", ((pCodeComment *)pc)->comment); + break; + + case PC_INFO: + { + pBranch *pbl = PCI(pc)->label; + while(pbl && pbl->pc) { + if(pbl->pc->type == PC_LABEL) + pCodePrintLabel(of, pbl->pc); + pbl = pbl->next; + } + } + + if(pic16_pcode_verbose) { + fprintf(of, "; info ==>"); + switch(((pCodeInfo *)pc)->type) { + case INF_OPTIMIZATION: + fprintf(of, " [optimization] %s\n", OPT_TYPE_STR[ PCOO(PCINF(pc)->oper1)->type ]); + break; + case INF_LOCALREGS: + fprintf(of, " [localregs] %s\n", LR_TYPE_STR[ PCOLR(PCINF(pc)->oper1)->type ]); + break; + } + } + + break; + + case PC_INLINE: + fprintf(of,"%s\n", ((pCodeComment *)pc)->comment); + break; + + case PC_OPCODE: + // If the opcode has a label, print that first + { + pBranch *pbl = PCI(pc)->label; + while(pbl && pbl->pc) { + if(pbl->pc->type == PC_LABEL) + pCodePrintLabel(of, pbl->pc); + pbl = pbl->next; + } + } + + if(PCI(pc)->cline) + genericPrint(of,PCODE(PCI(pc)->cline)); + + { + char str[256]; + + pic16_pCode2str(str, sizeof(str), pc); + + fprintf(of,"%s",str); + /* Debug */ + if(pic16_debug_verbose) { + fprintf(of, "\t;key=%03x",pc->seq); + if(PCI(pc)->pcflow) + fprintf(of,", flow seq=%03x",PCI(pc)->pcflow->pc.seq); + } + } + fprintf(of, "\n"); + break; + + case PC_WILD: + fprintf(of,";\tWild opcode: id=%d\n",PCW(pc)->id); + if(PCW(pc)->pci.label) + pCodePrintLabel(of, PCW(pc)->pci.label->pc); + + if(PCW(pc)->operand) { + fprintf(of,";\toperand "); + pCodeOpPrint(of,PCW(pc)->operand ); + } + break; + + case PC_FLOW: + if(pic16_debug_verbose) { + fprintf(of,";<>Start of new flow, seq=0x%x",pc->seq); + if(PCFL(pc)->ancestor) + fprintf(of," ancestor = 0x%x", PCODE(PCFL(pc)->ancestor)->seq); + fprintf(of,"\n"); + + } + break; + + case PC_CSOURCE: +// fprintf(of,";#CSRC\t%s %d\t\t%s\n", PCCS(pc)->file_name, PCCS(pc)->line_number, PCCS(pc)->line); + fprintf(of,"%s\t.line\t%d; %s\t%s\n", ((pic16_mplab_comp || !options.debug)?";":""), + PCCS(pc)->line_number, PCCS(pc)->file_name, PCCS(pc)->line); + + break; + + case PC_ASMDIR: + { + pBranch *pbl = PCAD(pc)->pci.label; + while(pbl && pbl->pc) { + if(pbl->pc->type == PC_LABEL) + pCodePrintLabel(of, pbl->pc); + pbl = pbl->next; + } + } + if(PCAD(pc)->directive) { + fprintf(of, "\t%s%s%s\n", PCAD(pc)->directive, PCAD(pc)->arg?"\t":"", PCAD(pc)->arg?PCAD(pc)->arg:""); + } else + if(PCAD(pc)->arg) { + /* special case to handle inline labels without tab */ + fprintf(of, "%s\n", PCAD(pc)->arg); + } + break; + + case PC_LABEL: + default: + fprintf(of,"unknown pCode type %d\n",pc->type); + } +} + +/*-----------------------------------------------------------------*/ +/* pCodePrintFunction - prints function begin/end */ +/*-----------------------------------------------------------------*/ + +static void pCodePrintFunction(FILE *of, pCode *pc) +{ + if(!pc || !of) + return; + +#if 0 + if( ((pCodeFunction *)pc)->modname) + fprintf(of,"F_%s",((pCodeFunction *)pc)->modname); +#endif + + if(!PCF(pc)->absblock) { + if(PCF(pc)->fname) { + pBranch *exits = PCF(pc)->to; + int i=0; + + fprintf(of,"%s:", PCF(pc)->fname); + + if(pic16_pcode_verbose) + fprintf(of, "\t;Function start."); + + fprintf(of, "\n"); + + while(exits) { + i++; + exits = exits->next; + } + //if(i) i--; + + if(pic16_pcode_verbose) + fprintf(of,"; %d exit point%c\n",i, ((i==1) ? ' ':'s')); + + } else { + if((PCF(pc)->from && + PCF(pc)->from->pc->type == PC_FUNCTION && + PCF(PCF(pc)->from->pc)->fname) ) { + + if(pic16_pcode_verbose) + fprintf(of,"; exit point of %s\n",PCF(PCF(pc)->from->pc)->fname); + } else { + if(pic16_pcode_verbose) + fprintf(of,"; exit point [can't find entry point]\n"); + } + fprintf(of, "\n"); + } + } +} +/*-----------------------------------------------------------------*/ +/* pCodePrintLabel - prints label */ +/*-----------------------------------------------------------------*/ + +static void pCodePrintLabel(FILE *of, pCode *pc) +{ + if(!pc || !of) + return; + + if(PCL(pc)->label) + fprintf(of,"%s:\n",PCL(pc)->label); + else if (PCL(pc)->key >=0) + fprintf(of,"_%05d_DS_:\n",PCL(pc)->key); + else + fprintf(of,";wild card label: id=%d\n",-PCL(pc)->key); + +} +/*-----------------------------------------------------------------*/ +/* unlinkpCodeFromBranch - Search for a label in a pBranch and */ +/* remove it if it is found. */ +/*-----------------------------------------------------------------*/ +static void unlinkpCodeFromBranch(pCode *pcl , pCode *pc) +{ + pBranch *b, *bprev; + + bprev = NULL; + + if(pcl->type == PC_OPCODE || pcl->type == PC_INLINE || pcl->type == PC_ASMDIR) + b = PCI(pcl)->label; + else { + fprintf(stderr, "LINE %d. can't unlink from non opcode.\n",__LINE__); + exit(1); + } + + //fprintf (stderr, "%s \n",__FUNCTION__); + //pcl->print(stderr,pcl); + //pc->print(stderr,pc); + while(b) { + if(b->pc == pc) { + //fprintf (stderr, "found label\n"); + //pc->print(stderr, pc); + + /* Found a label */ + if(bprev) { + bprev->next = b->next; /* Not first pCode in chain */ +// Safe_free(b); + } else { + pc->destruct(pc); + PCI(pcl)->label = b->next; /* First pCode in chain */ +// Safe_free(b); + } + return; /* A label can't occur more than once */ + } + bprev = b; + b = b->next; + } +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pBranch * pic16_pBranchAppend(pBranch *h, pBranch *n) +{ + pBranch *b; + + if(!h) + return n; + + if(h == n) + return n; + + b = h; + while(b->next) + b = b->next; + + b->next = n; + + return h; +} + +/*-----------------------------------------------------------------*/ +/* pBranchLink - given two pcodes, this function will link them */ +/* together through their pBranches */ +/*-----------------------------------------------------------------*/ +static void pBranchLink(pCodeFunction *f, pCodeFunction *t) +{ + pBranch *b; + + // Declare a new branch object for the 'from' pCode. + + //_ALLOC(b,sizeof(pBranch)); + b = Safe_alloc(sizeof(pBranch)); + b->pc = PCODE(t); // The link to the 'to' pCode. + b->next = NULL; + + f->to = pic16_pBranchAppend(f->to,b); + + // Now do the same for the 'to' pCode. + + //_ALLOC(b,sizeof(pBranch)); + b = Safe_alloc(sizeof(pBranch)); + b->pc = PCODE(f); + b->next = NULL; + + t->from = pic16_pBranchAppend(t->from,b); +} + +#if 1 +/*-----------------------------------------------------------------*/ +/* pBranchFind - find the pBranch in a pBranch chain that contains */ +/* a pCode */ +/*-----------------------------------------------------------------*/ +static pBranch *pBranchFind(pBranch *pb,pCode *pc) +{ + while(pb) { + + if(pb->pc == pc) + return pb; + + pb = pb->next; + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* pic16_pCodeUnlink - Unlink the given pCode from its pCode chain. */ +/*-----------------------------------------------------------------*/ +void pic16_pCodeUnlink(pCode *pc) +{ + pBranch *pb1,*pb2; + pCode *pc1; + + if (!pc) { + return; + } + + /* Remove the branches */ + + pb1 = PCI(pc)->from; + while(pb1) { + pc1 = pb1->pc; /* Get the pCode that branches to the + * one we're unlinking */ + + /* search for the link back to this pCode (the one we're + * unlinking) */ + if((pb2 = pBranchFind(PCI(pc1)->to,pc))) { + pb2->pc = PCI(pc)->to->pc; // make the replacement + + /* if the pCode we're unlinking contains multiple 'to' + * branches (e.g. this a skip instruction) then we need + * to copy these extra branches to the chain. */ + if(PCI(pc)->to->next) + pic16_pBranchAppend(pb2, PCI(pc)->to->next); + } + + pb1 = pb1->next; + } + + pic16_unlinkpCode (pc); +} +#endif +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +#if 0 +static void genericAnalyze(pCode *pc) +{ + switch(pc->type) { + case PC_WILD: + case PC_COMMENT: + return; + case PC_LABEL: + case PC_FUNCTION: + case PC_OPCODE: + { + // Go through the pCodes that are in pCode chain and link + // them together through the pBranches. Note, the pCodes + // are linked together as a contiguous stream like the + // assembly source code lines. The linking here mimics this + // except that comments are not linked in. + // + pCode *npc = pc->next; + while(npc) { + if(npc->type == PC_OPCODE || npc->type == PC_LABEL) { + pBranchLink(pc,npc); + return; + } else + npc = npc->next; + } + /* reached the end of the pcode chain without finding + * an instruction we could link to. */ + } + break; + case PC_FLOW: + fprintf(stderr,"analyze PC_FLOW\n"); + + return; + case PC_BAD: + fprintf(stderr,,";A bad pCode is being used\n"); + + } +} +#endif + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static int compareLabel(const pCode *pc, const pCodeOpLabel *pcop_label) +{ + pBranch *pbr; + + if(pc->type == PC_LABEL) { + if(((pCodeLabel *)pc)->key == pcop_label->key) + return TRUE; + } + + if((pc->type == PC_OPCODE) || (pc->type == PC_ASMDIR)) { + pbr = PCI(pc)->label; + while(pbr) { + if(pbr->pc->type == PC_LABEL) { + if(((pCodeLabel *)(pbr->pc))->key == pcop_label->key) + return TRUE; + } + pbr = pbr->next; + } + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static int checkLabel(const pCode *pc) +{ + pBranch *pbr; + + if(pc && isPCI(pc)) { + pbr = PCI(pc)->label; + while(pbr) { + if(isPCL(pbr->pc) && (PCL(pbr->pc)->key >= 0)) + return TRUE; + + pbr = pbr->next; + } + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* findLabelinpBlock - Search the pCode for a particular label */ +/*-----------------------------------------------------------------*/ +static pCode *findLabelinpBlock(pBlock *pb, const pCodeOpLabel *pcop_label) +{ + pCode *pc; + + if(!pb) + return NULL; + + for(pc = pb->pcHead; pc; pc = pc->next) + if(compareLabel(pc,pcop_label)) + return pc; + + return NULL; +} +#if 0 +/*-----------------------------------------------------------------*/ +/* findLabel - Search the pCode for a particular label */ +/*-----------------------------------------------------------------*/ +static pCode *findLabel(const pCodeOpLabel *pcop_label) +{ + pBlock *pb; + pCode *pc; + + if(!the_pFile) + return NULL; + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + if((pc = findLabelinpBlock(pb,pcop_label)) != NULL) + return pc; + } + + fprintf(stderr,"Couldn't find label %s", pcop_label->pcop.name); + return NULL; +} +#endif +/*-----------------------------------------------------------------*/ +/* pic16_findNextpCode - given a pCode, find the next of type 'pct' */ +/* in the linked list */ +/*-----------------------------------------------------------------*/ +pCode * pic16_findNextpCode(pCode *pc, PC_TYPE pct) +{ + + while(pc) { + if(pc->type == pct) + return pc; + + pc = pc->next; + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* findPrevpCode - given a pCode, find the previous of type 'pct' */ +/* in the linked list */ +/*-----------------------------------------------------------------*/ +static pCode * findPrevpCode(pCode *pc, PC_TYPE pct) +{ + + while(pc) { + if(pc->type == pct) + return pc; + + pc = pc->prev; + } + + return NULL; +} + + +//#define PCODE_DEBUG +/*-----------------------------------------------------------------*/ +/* pic16_findNextInstruction - given a pCode, find the next instruction */ +/* in the linked list */ +/*-----------------------------------------------------------------*/ +pCode * pic16_findNextInstruction(pCode *pci) +{ + pCode *pc = pci; + + while(pc) { + if((pc->type == PC_OPCODE) + || (pc->type == PC_WILD) + || (pc->type == PC_ASMDIR) + ) + return pc; + +#ifdef PCODE_DEBUG + fprintf(stderr,"pic16_findNextInstruction: "); + printpCode(stderr, pc); +#endif + pc = pc->next; + } + + //fprintf(stderr,"Couldn't find instruction\n"); + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* pic16_findPrevInstruction - given a pCode, find the next instruction */ +/* in the linked list */ +/*-----------------------------------------------------------------*/ +pCode * pic16_findPrevInstruction(pCode *pci) +{ + pCode *pc = pci; + + while(pc) { + + if((pc->type == PC_OPCODE) || (pc->type == PC_WILD) || (pc->type == PC_ASMDIR)) + return pc; + +#ifdef PCODE_DEBUG + fprintf(stderr,"pic16_findPrevInstruction: "); + printpCode(stderr, pc); +#endif + pc = pc->prev; + } + + //fprintf(stderr,"Couldn't find instruction\n"); + return NULL; +} + +#undef PCODE_DEBUG + +#if 0 +/*-----------------------------------------------------------------*/ +/* findFunctionEnd - given a pCode find the end of the function */ +/* that contains it */ +/*-----------------------------------------------------------------*/ +static pCode * findFunctionEnd(pCode *pc) +{ + while(pc) { + if(pc->type == PC_FUNCTION && !(PCF(pc)->fname)) + return pc; + + pc = pc->next; + } + + fprintf(stderr,"Couldn't find function end\n"); + return NULL; +} +#endif +#if 0 +/*-----------------------------------------------------------------*/ +/* AnalyzeLabel - if the pCode is a label, then merge it with the */ +/* instruction with which it is associated. */ +/*-----------------------------------------------------------------*/ +static void AnalyzeLabel(pCode *pc) +{ + pic16_pCodeUnlink(pc); +} +#endif + +#if 0 +static void AnalyzeGOTO(pCode *pc) +{ + pBranchLink(pc,findLabel( (pCodeOpLabel *) (PCI(pc)->pcop) )); +} + +static void AnalyzeSKIP(pCode *pc) +{ + pBranchLink(pc,pic16_findNextInstruction(pc->next)); + pBranchLink(pc,pic16_findNextInstruction(pc->next->next)); +} + +static void AnalyzeRETURN(pCode *pc) +{ + // branch_link(pc,findFunctionEnd(pc->next)); +} + +#endif + +/*-------------------------------------------------------------------*/ +/* pic16_getRegFrompCodeOp - extract the register from a pCodeOp */ +/* if one is present. This is the common */ +/* part of pic16_getRegFromInstruction(2) */ +/*-------------------------------------------------------------------*/ + +reg_info * pic16_getRegFrompCodeOp (pCodeOp *pcop) +{ + if (!pcop) return NULL; + + switch(pcop->type) { + case PO_PRODL: + case PO_PRODH: + case PO_INDF0: + case PO_FSR0: + case PO_W: + case PO_WREG: + case PO_STATUS: + case PO_INTCON: + case PO_PCL: + case PO_PCLATH: + case PO_PCLATU: + case PO_BSR: + return PCOR(pcop)->r; + + case PO_SFR_REGISTER: + //fprintf (stderr, "%s - SFR\n", __FUNCTION__); + return PCOR(pcop)->r; + + case PO_BIT: + case PO_GPR_TEMP: +// fprintf(stderr, "pic16_getRegFromInstruction - bit or temp\n"); + return PCOR(pcop)->r; + + case PO_IMMEDIATE: +// return pic16_dirregWithName(PCOI(pcop)->r->name); +/* if(PCOI(pcop)->r) + return (PCOI(pcop)->r); + else + return NULL;*/ + return (PCOI(pcop)->r); + + case PO_GPR_BIT: + return PCOR(pcop)->r; + + case PO_GPR_REGISTER: + case PO_DIR: +// fprintf(stderr, "pic16_getRegFromInstruction - dir\n"); + return PCOR(pcop)->r; + + case PO_LITERAL: + //fprintf(stderr, "pic16_getRegFromInstruction - literal\n"); + break; + + case PO_REL_ADDR: + case PO_LABEL: + //fprintf (stderr, "%s - label or address: %d (%s)\n", __FUNCTION__, pcop->type, dumpPicOptype(pcop->type)); + break; + + case PO_CRY: + case PO_STR: + /* this should never turn up */ + //fprintf (stderr, "%s - unused pCodeOp->type: %d (%s)\n", __FUNCTION__, pcop->type, dumpPicOptype(pcop->type)); + break; + + case PO_WILD: + break; + + case PO_TWO_OPS: + return pic16_getRegFrompCodeOp( PCOP2(pcop)->pcopL ); + break; + + default: + fprintf(stderr, "pic16_getRegFrompCodeOp - unknown reg type %d (%s)\n",pcop->type, dumpPicOptype (pcop->type)); +// assert( 0 ); + break; + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +reg_info * pic16_getRegFromInstruction(pCode *pc) +{ + if(!pc || + !isPCI(pc) || + !PCI(pc)->pcop || + PCI(pc)->num_ops == 0 || + (PCI(pc)->num_ops == 1 && PCI(pc)->isFastCall)) + return NULL; + +#if 0 + fprintf(stderr, "pic16_getRegFromInstruction - reg type %s (%d)\n", + dumpPicOptype( PCI(pc)->pcop->type), PCI(pc)->pcop->type); +#endif + + return( pic16_getRegFrompCodeOp (PCI(pc)->pcop) ); +} + +/*-------------------------------------------------------------------------------*/ +/* pic16_getRegFromInstruction2 - variant to support two memory operand commands */ +/*-------------------------------------------------------------------------------*/ +reg_info * pic16_getRegFromInstruction2(pCode *pc) +{ + if(!pc || + !isPCI(pc) || + !PCI(pc)->pcop || + PCI(pc)->num_ops == 0 || + (PCI(pc)->num_ops == 1)) // accept only 2 operand commands + return NULL; + + if (PCI(pc)->pcop->type != PO_TWO_OPS) + return NULL; + +#if 0 + fprintf(stderr, "pic16_getRegFromInstruction2 - reg type %s (%d)\n", + dumpPicOptype( PCI(pc)->pcop->type), PCI(pc)->pcop->type); +#endif + + return pic16_getRegFrompCodeOp (PCOP2(PCI(pc)->pcop)->pcopR); +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ + +static void AnalyzepBlock(pBlock *pb) +{ + pCode *pc; + + if(!pb) + return; + + /* Find all of the registers used in this pBlock + * by looking at each instruction and examining it's + * operands + */ + for(pc = pb->pcHead; pc; pc = pc->next) { + + /* Is this an instruction with operands? */ + if(pc->type == PC_OPCODE && PCI(pc)->pcop) { + + if(PCI(pc)->pcop->type == PO_GPR_TEMP) { + + /* Loop through all of the registers declared so far in + this block and see if we find this one there */ + + reg_info *r = setFirstItem(pb->tregisters); + + while(r) { + if(r->rIdx == PCOR(PCI(pc)->pcop)->r->rIdx) { + PCOR(PCI(pc)->pcop)->r = r; + break; + } + r = setNextItem(pb->tregisters); + } + + if(!r) { + /* register wasn't found */ + //r = Safe_alloc(sizeof(regs)); + //memcpy(r,PCOR(PCI(pc)->pcop)->r, sizeof(regs)); + //addSet(&pb->tregisters, r); + addSet(&pb->tregisters, PCOR(PCI(pc)->pcop)->r); + //PCOR(PCI(pc)->pcop)->r = r; + //fprintf(stderr,"added register to pblock: reg %d\n",r->rIdx); + }/* else + fprintf(stderr,"found register in pblock: reg %d\n",r->rIdx); + */ + } + if(PCI(pc)->pcop->type == PO_GPR_REGISTER) { + if(PCOR(PCI(pc)->pcop)->r) { + pic16_allocWithIdx(PCOR(PCI(pc)->pcop)->r->rIdx); /* FIXME! - VR */ + DFPRINTF((stderr,"found register in pblock: reg 0x%x\n",PCOR(PCI(pc)->pcop)->r->rIdx)); + } else { + if(PCI(pc)->pcop->name) + fprintf(stderr,"ERROR: %s is a NULL register\n",PCI(pc)->pcop->name ); + else + fprintf(stderr,"ERROR: NULL register\n"); + } + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* */ +/*-----------------------------------------------------------------*/ +#define PCI_HAS_LABEL(x) ((x) && (PCI(x)->label != NULL)) + +static void InsertpFlow(pCode *pc, pCode **pflow) +{ + if(*pflow) + PCFL(*pflow)->end = pc; + + if(!pc || !pc->next) + return; + + *pflow = pic16_newpCodeFlow(); + pic16_pCodeInsertAfter(pc, *pflow); +} + +/*-----------------------------------------------------------------*/ +/* pic16_BuildFlow(pBlock *pb) - examine the code in a pBlock and build */ +/* the flow blocks. */ +/* + * pic16_BuildFlow inserts pCodeFlow objects into the pCode chain at each + * point the instruction flow changes. + */ +/*-----------------------------------------------------------------*/ +void pic16_BuildFlow(pBlock *pb) +{ + pCode *pc; + pCode *last_pci=NULL; + pCode *pflow=NULL; + int seq = 0; + + if(!pb) + return; + + //fprintf (stderr,"build flow start seq %d ",GpcFlowSeq); + /* Insert a pCodeFlow object at the beginning of a pBlock */ + + InsertpFlow(pb->pcHead, &pflow); + + //pflow = pic16_newpCodeFlow(); /* Create a new Flow object */ + //pflow->next = pb->pcHead; /* Make the current head the next object */ + //pb->pcHead->prev = pflow; /* let the current head point back to the flow object */ + //pb->pcHead = pflow; /* Make the Flow object the head */ + //pflow->pb = pb; + + for( pc = pic16_findNextInstruction(pb->pcHead); + pc != NULL; + pc=pic16_findNextInstruction(pc)) { + + pc->seq = seq++; + PCI(pc)->pcflow = PCFL(pflow); + + //fprintf(stderr," build: "); + //pflow->print(stderr,pflow); + + if (checkLabel(pc)) { + + /* This instruction marks the beginning of a + * new flow segment */ + + pc->seq = 0; + seq = 1; + + /* If the previous pCode is not a flow object, then + * insert a new flow object. (This check prevents + * two consecutive flow objects from being insert in + * the case where a skip instruction preceeds an + * instruction containing a label.) */ + + if(last_pci && (PCI(last_pci)->pcflow == PCFL(pflow))) + InsertpFlow(pic16_findPrevInstruction(pc->prev), &pflow); + + PCI(pc)->pcflow = PCFL(pflow); + + } + + if( PCI(pc)->isSkip) { + + /* The two instructions immediately following this one + * mark the beginning of a new flow segment */ + + while(pc && PCI(pc)->isSkip) { + + PCI(pc)->pcflow = PCFL(pflow); + pc->seq = seq-1; + seq = 1; + + InsertpFlow(pc, &pflow); + pc=pic16_findNextInstruction(pc->next); + } + + seq = 0; + + if(!pc) + break; + + PCI(pc)->pcflow = PCFL(pflow); + pc->seq = 0; + InsertpFlow(pc, &pflow); + + } else if ( PCI(pc)->isBranch && !checkLabel(pic16_findNextInstruction(pc->next))) { + + InsertpFlow(pc, &pflow); + seq = 0; + + } + last_pci = pc; + pc = pc->next; + } + + //fprintf (stderr,",end seq %d",GpcFlowSeq); + if(pflow) + PCFL(pflow)->end = pb->pcTail; +} + +/*-------------------------------------------------------------------*/ +/* unBuildFlow(pBlock *pb) - examine the code in a pBlock and build */ +/* the flow blocks. */ +/* + * unBuildFlow removes pCodeFlow objects from a pCode chain + */ +/*-----------------------------------------------------------------*/ +static void unBuildFlow(pBlock *pb) +{ + pCode *pc,*pcnext; + + if(!pb) + return; + + pc = pb->pcHead; + + while(pc) { + pcnext = pc->next; + + if(isPCI(pc)) { + + pc->seq = 0; + if(PCI(pc)->pcflow) { + //Safe_free(PCI(pc)->pcflow); + PCI(pc)->pcflow = NULL; + } + + } else if(isPCFL(pc) ) + pc->destruct(pc); + + pc = pcnext; + } +} +#if 0 +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void dumpCond(int cond) +{ + static const char *pcc_str[] = { + //"PCC_NONE", + "PCC_REGISTER", + "PCC_C", + "PCC_Z", + "PCC_DC", + "PCC_OV", + "PCC_N", + "PCC_W", + "PCC_EXAMINE_PCOP", + "PCC_LITERAL", + "PCC_REL_ADDR" + }; + + int ncond = sizeof(pcc_str) / sizeof(char *); + int i,j; + + fprintf(stderr, "0x%04X\n",cond); + + for(i=0,j=1; i<ncond; i++, j<<=1) { + if(cond & j) { + fprintf(stderr, " %s\n",pcc_str[i]); + } + } +} +#endif + +#if 0 +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void FlowStats(pCodeFlow *pcflow) +{ + pCode *pc; + + if(!isPCFL(pcflow)) + return; + + fprintf(stderr, " FlowStats - flow block (seq=%d)\n", pcflow->pc.seq); + + pc = pic16_findNextpCode(PCODE(pcflow), PC_OPCODE); + + if(!pc) { + fprintf(stderr, " FlowStats - empty flow (seq=%d)\n", pcflow->pc.seq); + return; + } + + fprintf(stderr, " FlowStats inCond: "); + dumpCond(pcflow->inCond); + fprintf(stderr, " FlowStats outCond: "); + dumpCond(pcflow->outCond); +} +#endif +/*-----------------------------------------------------------------* + * int isBankInstruction(pCode *pc) - examine the pCode *pc to determine + * if it affects the banking bits. + * + * return: -1 == Banking bits are unaffected by this pCode. + * + * return: > 0 == Banking bits are affected. + * + * If the banking bits are affected, then the returned value describes + * which bits are affected and how they're affected. The lower half + * of the integer maps to the bits that are affected, the upper half + * to whether they're set or cleared. + * + *-----------------------------------------------------------------*/ + +static int isBankInstruction(pCode *pc) +{ + reg_info *reg; + + if(!isPCI(pc)) + return FALSE; + + if((PCI(pc)->op == POC_MOVLB) || + (((reg = pic16_getRegFromInstruction(pc)) != NULL) && isBSR_REG(reg))) { + } + + return TRUE; +} + + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void FillFlow(pCodeFlow *pcflow) +{ + pCode *pc; + + if(!isPCFL(pcflow)) + return; + + // fprintf(stderr, " FillFlow - flow block (seq=%d)\n", pcflow->pc.seq); + + pc = pic16_findNextpCode(PCODE(pcflow), PC_OPCODE); + + if(!pc) { + //fprintf(stderr, " FillFlow - empty flow (seq=%d)\n", pcflow->pc.seq); + return; + } + + do { + isBankInstruction(pc); + pc = pc->next; + } while (pc && (pc != pcflow->end) && !isPCFL(pc)); + +/* + if(!pc ) { + fprintf(stderr, " FillFlow - Bad end of flow\n"); + } else { + fprintf(stderr, " FillFlow - Ending flow with\n "); + pc->print(stderr,pc); + } + + fprintf(stderr, " FillFlow inCond: "); + dumpCond(pcflow->inCond); + fprintf(stderr, " FillFlow outCond: "); + dumpCond(pcflow->outCond); +*/ +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void LinkFlow_pCode(pCodeInstruction *from, pCodeInstruction *to) +{ + pCodeFlowLink *fromLink, *toLink; + + if(!from || !to || !to->pcflow || !from->pcflow) + return; + + fromLink = pic16_newpCodeFlowLink(from->pcflow); + toLink = pic16_newpCodeFlowLink(to->pcflow); + + addSetIfnotP(&(from->pcflow->to), toLink); //to->pcflow); + addSetIfnotP(&(to->pcflow->from), fromLink); //from->pcflow); +} + +static pCode *pic16_getJumptabpCode (pCode *pc) +{ + pCode *pcinf; + + //fprintf (stderr, "%s - start for %p in %p", __FUNCTION__, pc, isPCI(pc) ? PCI(pc)->pcflow : NULL); + //pc->print (stderr, pc); + pcinf = pc; + while (pcinf) { + if (isPCI(pcinf) && PCI(pcinf)->op != POC_GOTO) return NULL; + if (pcinf->type == PC_INFO && PCINF(pcinf)->type == INF_OPTIMIZATION) { + switch (PCOO(PCINF(pcinf)->oper1)->type) { + case OPT_JUMPTABLE_BEGIN: + /* leading begin of jump table -- in one */ + pcinf = pic16_findPrevInstruction (pcinf); + return pcinf; + break; + + case OPT_JUMPTABLE_END: + /* leading end of jumptable -- not in one */ + return NULL; + break; + + default: + /* ignore all other PCInfos */ + break; + } + } + pcinf = pcinf->prev; + } + + /* no PCInfo found -- not in a jumptable */ + return NULL; +} + +/*-----------------------------------------------------------------* + * void LinkFlow(pBlock *pb) + * + * In pic16_BuildFlow, the PIC code has been partitioned into contiguous + * non-branching segments. In LinkFlow, we determine the execution + * order of these segments. For example, if one of the segments ends + * with a skip, then we know that there are two possible flow segments + * to which control may be passed. + *-----------------------------------------------------------------*/ +static void LinkFlow(pBlock *pb) +{ + pCode *pc=NULL; + pCode *pcflow; + pCode *pct; + pCode *jumptab_pre = NULL; + + //fprintf(stderr,"linkflow \n"); + + for( pcflow = pic16_findNextpCode(pb->pcHead, PC_FLOW); + pcflow != NULL; + pcflow = pic16_findNextpCode(pcflow->next, PC_FLOW) ) { + + if(!isPCFL(pcflow)) + fprintf(stderr, "LinkFlow - pcflow is not a flow object "); + + //fprintf(stderr," link: "); + //pcflow->print(stderr,pcflow); + + //FillFlow(PCFL(pcflow)); + + pc = PCFL(pcflow)->end; + + //fprintf(stderr, "LinkFlow - flow block (seq=%d) ", pcflow->seq); + if(isPCI_SKIP(pc)) { +// fprintf(stderr, "ends with skip\n"); +// pc->print(stderr,pc); + + pct=pic16_findNextInstruction(pc->next); + LinkFlow_pCode(PCI(pc),PCI(pct)); + pct=pic16_findNextInstruction(pct->next); + LinkFlow_pCode(PCI(pc),PCI(pct)); + continue; + } + + if(isPCI_BRANCH(pc)) { + pCodeOpLabel *pcol = PCOLAB(PCI(pc)->pcop); + + /* handle GOTOs in jumptables */ + if ((jumptab_pre = pic16_getJumptabpCode (pc)) != NULL) { + /* link to previous flow */ + //fprintf (stderr, "linked jumptable GOTO to predecessor %p\n", PCI(jumptab_pre)->pcflow); + LinkFlow_pCode (PCI(jumptab_pre), PCI(pc)); + } + + switch (PCI(pc)->op) { + case POC_GOTO: + case POC_BRA: + case POC_RETURN: + case POC_RETLW: + case POC_RETFIE: + /* unconditional branches -- do not link to next instruction */ + //fprintf (stderr, "%s: flow ended by unconditional branch\n", __FUNCTION__); + break; + + case POC_CALL: + case POC_RCALL: + /* unconditional calls -- link to next instruction */ + //fprintf (stderr, "%s: flow ended by CALL\n", __FUNCTION__); + LinkFlow_pCode(PCI(pc),PCI(pic16_findNextInstruction(pc->next))); + break; + + case POC_BC: + case POC_BN: + case POC_BNC: + case POC_BNN: + case POC_BNOV: + case POC_BNZ: + case POC_BOV: + case POC_BZ: + /* conditional branches -- also link to next instruction */ + //fprintf (stderr, "%s: flow ended by conditional branch\n", __FUNCTION__); + LinkFlow_pCode(PCI(pc),PCI(pic16_findNextInstruction(pc->next))); + break; + + default: + fprintf (stderr, "%s: unhandled op %u (%s)\n", __FUNCTION__, PCI(pc)->op , PCI(pc)->mnemonic); + assert (0 && "unhandled branching instruction"); + break; + } + + //fprintf(stderr, "ends with branch\n "); + //pc->print(stderr,pc); + + if(!(pcol && isPCOLAB(pcol))) { + if((PCI(pc)->op != POC_RETLW) + && (PCI(pc)->op != POC_RETURN) && (PCI(pc)->op != POC_CALL) && (PCI(pc)->op != POC_RCALL) && (PCI(pc)->op != POC_RETFIE) ) { + + /* continue if label is '$' which assembler knows how to parse */ + if(((PCI(pc)->pcop->type == PO_STR) && !strcmp(PCI(pc)->pcop->name, "$")))continue; + + if(pic16_pcode_verbose) { + pc->print(stderr,pc); + fprintf(stderr, "ERROR: %s, branch instruction doesn't have label\n",__FUNCTION__); + } + } + continue; + } + + if( (pct = findLabelinpBlock(pb,pcol)) != NULL) + LinkFlow_pCode(PCI(pc),PCI(pic16_findNextInstruction(pct))); + else + fprintf(stderr, "ERROR: %s, couldn't find label. key=%d,lab=%s\n", + __FUNCTION__,pcol->key,((PCOP(pcol)->name)?PCOP(pcol)->name:"-")); + +// fprintf(stderr,"pic16_newpCodeOpLabel: key=%d, name=%s\n",pcol->key,(PCOP(pcol)->name)?(PCOP(pcol)->name):"<unknown>"); + + continue; + } + + if(isPCI(pc)) { + //fprintf(stderr, "ends with non-branching instruction:\n"); + //pc->print(stderr,pc); + + LinkFlow_pCode(PCI(pc),PCI(pic16_findNextInstruction(pc->next))); + + continue; + } + + if(pc) { + //fprintf(stderr, "ends with unknown\n"); + //pc->print(stderr,pc); + continue; + } + + //fprintf(stderr, "ends with nothing: ERROR\n"); + } +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +int pic16_isPCinFlow(const pCode *pc, const pCode *pcflow) +{ + if(!pc || !pcflow) + return FALSE; + + if((!isPCI(pc) && !isPCAD(pc)) || !PCI(pc)->pcflow || !isPCFL(pcflow) ) + return FALSE; + + if( PCI(pc)->pcflow->pc.seq == pcflow->seq) + return TRUE; + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* insertBankSwitch - inserts a bank switch statement in the */ +/* assembly listing */ +/* */ +/* position == 0: insert before */ +/* position == 1: insert after pc */ +/* position == 2: like 0 but previous was a skip instruction */ +/*-----------------------------------------------------------------*/ +pCodeOp *pic16_popGetLabel(unsigned int key); +extern int pic16_labelOffset; + +static void insertBankSwitch(unsigned char position, pCode *pc) +{ + pCode *new_pc; + + if(!pc) + return; + + /* emit BANKSEL [symbol] */ + + + new_pc = pic16_newpCodeAsmDir("BANKSEL", "%s", pic16_get_op_from_instruction(PCI(pc))); + +// position = 0; // position is always before (sanity check!) + +#if 0 + fprintf(stderr, "%s:%d: inserting bank switch (pos: %d)\n", __FUNCTION__, __LINE__, position); + pc->print(stderr, pc); +#endif + + switch(position) { + case 1: { + /* insert the bank switch after this pc instruction */ + pCode *pcnext = pic16_findNextInstruction(pc); + + pic16_pCodeInsertAfter(pc, new_pc); + if(pcnext)pc = pcnext; + }; break; + + case 0: + /* insert the bank switch BEFORE this pc instruction */ + pic16_pCodeInsertAfter(pc->prev, new_pc); + break; + + case 2: { + symbol *tlbl; + pCode *pcnext, *pcprev, *npci, *ppc; + PIC_OPCODE ipci; + int ofs1=0, ofs2=0; + + /* just like 0, but previous was a skip instruction, + * so some care should be taken */ + + pic16_labelOffset += 10000; + tlbl = newiTempLabel(NULL); + + /* invert skip instruction */ + pcprev = pic16_findPrevInstruction(pc->prev); + ipci = PCI(pcprev)->inverted_op; + npci = pic16_newpCode(ipci, PCI(pcprev)->pcop); + +// fprintf(stderr, "%s:%d old OP: %d\tnew OP: %d\n", __FILE__, __LINE__, PCI(pcprev)->op, ipci); + + /* copy info from old pCode */ + ofs1 = ofs2 = sizeof( pCode ) + sizeof(PIC_OPCODE); + ofs1 += strlen( PCI(pcprev)->mnemonic) + 1; + ofs2 += strlen( PCI(npci)->mnemonic) + 1; + memcpy(&PCI(npci)->from, &PCI(pcprev)->from, (char *)(&(PCI(npci)->pci_magic)) - (char *)(&(PCI(npci)->from))); + PCI(npci)->op = PCI(pcprev)->inverted_op; + + /* unlink old pCode */ + ppc = pcprev->prev; + ppc->next = pcprev->next; + pcprev->next->prev = ppc; + pic16_pCodeInsertAfter(ppc, npci); + + /* extra instructions to handle invertion */ + pcnext = pic16_newpCode(POC_BRA, pic16_popGetLabel(tlbl->key)); + pic16_pCodeInsertAfter(npci, pcnext); + pic16_pCodeInsertAfter(pc->prev, new_pc); + + pcnext = pic16_newpCodeLabel(NULL,tlbl->key+100+pic16_labelOffset); + pic16_pCodeInsertAfter(pc, pcnext); + }; break; + } + + + /* Move the label, if there is one */ + if(PCI(pc)->label) { +// fprintf(stderr, "%s:%d: moving label due to bank switch directive src= 0x%p dst= 0x%p\n", +// __FILE__, __LINE__, pc, new_pc); + PCAD(new_pc)->pci.label = PCI(pc)->label; + PCI(pc)->label = NULL; + } +} + + +#if 0 +/*-----------------------------------------------------------------*/ +/*int compareBankFlow - compare the banking requirements between */ +/* flow objects. */ +/*-----------------------------------------------------------------*/ +static int compareBankFlow(pCodeFlow *pcflow, pCodeFlowLink *pcflowLink, int toORfrom) +{ + + if(!pcflow || !pcflowLink || !pcflowLink->pcflow) + return FALSE; + + if(!isPCFL(pcflow) || !isPCFL(pcflowLink->pcflow)) + return FALSE; + + if(pcflow->firstBank == -1) + return FALSE; + + if(pcflowLink->pcflow->firstBank == -1) { + pCodeFlowLink *pctl = setFirstItem( toORfrom ? + pcflowLink->pcflow->to : + pcflowLink->pcflow->from); + return compareBankFlow(pcflow, pctl, toORfrom); + } + + if(toORfrom) { + if(pcflow->lastBank == pcflowLink->pcflow->firstBank) + return FALSE; + + pcflowLink->bank_conflict++; + pcflowLink->pcflow->FromConflicts++; + pcflow->ToConflicts++; + } else { + + if(pcflow->firstBank == pcflowLink->pcflow->lastBank) + return FALSE; + + pcflowLink->bank_conflict++; + pcflowLink->pcflow->ToConflicts++; + pcflow->FromConflicts++; + + } + /* + fprintf(stderr,"compare flow found conflict: seq %d from conflicts %d, to conflicts %d\n", + pcflowLink->pcflow->pc.seq, + pcflowLink->pcflow->FromConflicts, + pcflowLink->pcflow->ToConflicts); + */ + return TRUE; +} +#endif + +#if 0 +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void DumpFlow(pBlock *pb) +{ + pCode *pc=NULL; + pCode *pcflow; + pCodeFlowLink *pcfl; + + fprintf(stderr,"Dump flow \n"); + pb->pcHead->print(stderr, pb->pcHead); + + pcflow = pic16_findNextpCode(pb->pcHead, PC_FLOW); + pcflow->print(stderr,pcflow); + + for( pcflow = pic16_findNextpCode(pb->pcHead, PC_FLOW); + pcflow != NULL; + pcflow = pic16_findNextpCode(pcflow->next, PC_FLOW) ) { + + if(!isPCFL(pcflow)) { + fprintf(stderr, "DumpFlow - pcflow is not a flow object "); + continue; + } + fprintf(stderr,"dumping: "); + pcflow->print(stderr,pcflow); + FlowStats(PCFL(pcflow)); + + for(pcfl = setFirstItem(PCFL(pcflow)->to); pcfl; pcfl=setNextItem(PCFL(pcflow)->to)) { + + pc = PCODE(pcfl->pcflow); + + fprintf(stderr, " from seq %d:\n",pc->seq); + if(!isPCFL(pc)) { + fprintf(stderr,"oops dumpflow - from is not a pcflow\n"); + pc->print(stderr,pc); + } + } + + for(pcfl = setFirstItem(PCFL(pcflow)->to); pcfl; pcfl=setNextItem(PCFL(pcflow)->to)) { + + pc = PCODE(pcfl->pcflow); + + fprintf(stderr, " to seq %d:\n",pc->seq); + if(!isPCFL(pc)) { + fprintf(stderr,"oops dumpflow - to is not a pcflow\n"); + pc->print(stderr,pc); + } + } + } +} +#endif +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static int OptimizepBlock(pBlock *pb) +{ + pCode *pc, *pcprev; + int matches =0; + + if(!pb || !peepOptimizing) + return 0; + + DFPRINTF((stderr," Optimizing pBlock: %c\n",getpBlock_dbName(pb))); +/* + for(pc = pb->pcHead; pc; pc = pc->next) + matches += pic16_pCodePeepMatchRule(pc); +*/ + + pc = pic16_findNextInstruction(pb->pcHead); + if(!pc) + return 0; + + pcprev = pc->prev; + do { + if(pic16_pCodePeepMatchRule(pc)) { + matches++; + + if(pcprev) + pc = pic16_findNextInstruction(pcprev->next); + else + pc = pic16_findNextInstruction(pb->pcHead); + } else + pc = pic16_findNextInstruction(pc->next); + } while(pc); + + if(matches) + DFPRINTF((stderr," Optimizing pBlock: %c - matches=%d\n",getpBlock_dbName(pb),matches)); + + return matches; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static pCode * findInstructionUsingLabel(pCodeLabel *pcl, pCode *pcs) +{ + pCode *pc; + + for(pc = pcs; pc; pc = pc->next) { + + if(((pc->type == PC_OPCODE) || (pc->type == PC_INLINE) || (pc->type == PC_ASMDIR)) && + (PCI(pc)->pcop) && + (PCI(pc)->pcop->type == PO_LABEL) && + (PCOLAB(PCI(pc)->pcop)->key == pcl->key)) + return pc; + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void exchangeLabels(pCodeLabel *pcl, pCode *pc) +{ + const char *s; + + if(isPCI(pc) && + (PCI(pc)->pcop) && + (PCI(pc)->pcop->type == PO_LABEL)) { + + pCodeOpLabel *pcol = PCOLAB(PCI(pc)->pcop); + +// fprintf(stderr,"changing label key from %d to %d\n",pcol->key, pcl->key); +// if(pcol->pcop.name) +// Safe_free(pcol->pcop.name); + + /* If the key is negative, then we (probably) have a label to + * a function and the name is already defined */ + + if(pcl->key>0) { + SNPRINTF(buffer, sizeof(buffer), "_%05d_DS_", pcl->key); + s = buffer; + } + else + s = pcl->label; + + //SNPRINTF(buffer, sizeof(buffer), "_%05d_DS_",pcl->key); + if(!s) { + fprintf(stderr, "ERROR %s:%d function label is null\n",__FUNCTION__,__LINE__); + } + pcol->pcop.name = Safe_strdup(s); + pcol->key = pcl->key; + //pc->print(stderr,pc); + } +} + +/*-----------------------------------------------------------------*/ +/* pBlockRemoveUnusedLabels - remove the pCode labels from the */ +/* pCode chain if they're not used. */ +/*-----------------------------------------------------------------*/ +static void pBlockRemoveUnusedLabels(pBlock *pb) +{ + pCode *pc; + pCodeLabel *pcl; + + if(!pb || !pb->pcHead) + return; + + for(pc = pb->pcHead; (pc=pic16_findNextInstruction(pc->next)) != NULL; ) { + + pBranch *pbr = PCI(pc)->label; + if(pbr && pbr->next) { + pCode *pcd = pb->pcHead; + +// fprintf(stderr, "multiple labels\n"); +// pc->print(stderr,pc); + + pbr = pbr->next; + while(pbr) { + + while ( (pcd = findInstructionUsingLabel(PCL(PCI(pc)->label->pc), pcd)) != NULL) { + //fprintf(stderr,"Used by:\n"); + //pcd->print(stderr,pcd); + + exchangeLabels(PCL(pbr->pc),pcd); + + pcd = pcd->next; + } + pbr = pbr->next; + } + } + } + + for(pc = pb->pcHead; pc; pc = pc->next) { + + if(isPCL(pc)) // pc->type == PC_LABEL) + pcl = PCL(pc); + else if (isPCI(pc) && PCI(pc)->label) //((pc->type == PC_OPCODE) && PCI(pc)->label) + pcl = PCL(PCI(pc)->label->pc); + else continue; + +// fprintf(stderr," found A LABEL !!! key = %d, %s\n", pcl->key,pcl->label); + + /* This pCode is a label, so search the pBlock to see if anyone + * refers to it */ + + if((pcl->key>0) && (!findInstructionUsingLabel(pcl, pb->pcHead)) + && (!pcl->force)) { + //if( !findInstructionUsingLabel(pcl, pb->pcHead)) { + /* Couldn't find an instruction that refers to this label + * So, unlink the pCode label from it's pCode chain + * and destroy the label */ +// fprintf(stderr," removed A LABEL !!! key = %d, %s\n", pcl->key,pcl->label); + + DFPRINTF((stderr," !!! REMOVED A LABEL !!! key = %d, %s\n", pcl->key,pcl->label)); + if(pc->type == PC_LABEL) { + pic16_unlinkpCode(pc); + pCodeLabelDestruct(pc); + } else { + unlinkpCodeFromBranch(pc, PCODE(pcl)); + /*if(pc->label->next == NULL && pc->label->pc == NULL) { + Safe_free(pc->label); + }*/ + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* pic16_pBlockMergeLabels - remove the pCode labels from the pCode */ +/* chain and put them into pBranches that are */ +/* associated with the appropriate pCode */ +/* instructions. */ +/*-----------------------------------------------------------------*/ +void pic16_pBlockMergeLabels(pBlock *pb) +{ + pBranch *pbr; + pCode *pc, *pcnext=NULL; + + if(!pb) + return; + + /* First, Try to remove any unused labels */ + //pBlockRemoveUnusedLabels(pb); + + /* Now loop through the pBlock and merge the labels with the opcodes */ + + pc = pb->pcHead; + // for(pc = pb->pcHead; pc; pc = pc->next) { + + while(pc) { + pCode *pcn = pc->next; + + if(pc->type == PC_LABEL) { + +// fprintf(stderr," checking merging label %s\n",PCL(pc)->label); +// fprintf(stderr,"Checking label key = %d\n",PCL(pc)->key); + + if((pcnext = pic16_findNextInstruction(pc) )) { + +// pcnext->print(stderr, pcnext); + + // Unlink the pCode label from it's pCode chain + pic16_unlinkpCode(pc); + +// fprintf(stderr,"Merged label key = %d\n",PCL(pc)->key); + // And link it into the instruction's pBranch labels. (Note, since + // it's possible to have multiple labels associated with one instruction + // we must provide a means to accomodate the additional labels. Thus + // the labels are placed into the singly-linked list "label" as + // opposed to being a single member of the pCodeInstruction.) + + //_ALLOC(pbr,sizeof(pBranch)); +#if 1 + pbr = Safe_alloc(sizeof(pBranch)); + pbr->pc = pc; + pbr->next = NULL; + + PCI(pcnext)->label = pic16_pBranchAppend(PCI(pcnext)->label,pbr); +#endif + } else { + if(pic16_pcode_verbose) + fprintf(stderr, "WARNING: couldn't associate label %s with an instruction\n",PCL(pc)->label); + } + } else if(pc->type == PC_CSOURCE) { + + /* merge the source line symbolic info into the next instruction */ + if((pcnext = pic16_findNextInstruction(pc) )) { + + // Unlink the pCode label from it's pCode chain + pic16_unlinkpCode(pc); + PCI(pcnext)->cline = PCCS(pc); + //fprintf(stderr, "merging CSRC\n"); + //genericPrint(stderr,pcnext); + } + } + pc = pcn; + } + + pBlockRemoveUnusedLabels(pb); +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static int OptimizepCode(char dbName) +{ +#define MAX_PASSES 4 + + int matches = 0; + int passes = 0; + pBlock *pb; + + if(!the_pFile) + return 0; + + DFPRINTF((stderr," Optimizing pCode\n")); + + do { + matches = 0; + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + if('*' == dbName || getpBlock_dbName(pb) == dbName) + matches += OptimizepBlock(pb); + } + } + while(matches && ++passes < MAX_PASSES); + + return matches; +} + +const char *pic16_pCodeOpType(pCodeOp *pcop); +const char *pic16_pCodeOpSubType(pCodeOp *pcop); + +/*-----------------------------------------------------------------*/ +/* pic16_popCopyGPR2Bit - copy a pcode operator */ +/*-----------------------------------------------------------------*/ + +pCodeOp *pic16_popCopyGPR2Bit(pCodeOp *pc, int bitval) +{ + pCodeOp *pcop=NULL; + +// fprintf(stderr, "%s:%d pc type: %s\tname: %s\n", __FILE__, __LINE__, pic16_pCodeOpType(pc), pc->name); + + if(pc->name) { + pcop = pic16_newpCodeOpBit(pc->name, bitval, 0, pc->type); + } else { + if(PCOR(pc)->r)pcop = pic16_newpCodeOpBit(PCOR(pc)->r->name, bitval, 0, pc->type); + } + + assert(pcop != NULL); + + if( !( (pcop->type == PO_LABEL) || + (pcop->type == PO_LITERAL) || + (pcop->type == PO_STR) )) + PCOR(pcop)->r = PCOR(pc)->r; /* This is dangerous... */ + PCOR(pcop)->r->wasUsed = 1; + PCOR(pcop)->instance = PCOR(pc)->instance; + + return pcop; +} + + +/*----------------------------------------------------------------------* + * pic16_areRegsSame - check to see if the names of two registers match * + *----------------------------------------------------------------------*/ + +#if 0 +static int pic16_areRegsSame(const reg_info *r1, const reg_info *r2) +{ + return ((strcmp(r1->name, r2->name) == 0) ? TRUE : FALSE); +} +#endif + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void pic16_FixRegisterBanking(pBlock *pb) +{ + pCode *pc=NULL; + pCode *pcprev=NULL; + reg_info *reg, *prevreg; + unsigned char flag=0; + + if(!pb) + return; + + pc = pic16_findNextpCode(pb->pcHead, PC_OPCODE); + if(!pc)return; + + /* loop through all of the flow blocks with in one pblock */ + +// fprintf(stderr,"%s:%d: Register banking\n", __FUNCTION__, __LINE__); + + prevreg = NULL; + do { + /* at this point, pc should point to a PC_FLOW object */ + /* for each flow block, determine the register banking + * requirements */ + + + /* if label, then might come from other point, force banksel */ + if(isPCL(pc))prevreg = NULL; + + if(!isPCI(pc))goto loop; + + if(PCI(pc)->label)prevreg = NULL; + + if(PCI(pc)->is2MemOp)goto loop; + + /* if goto, then force banksel */ +// if(PCI(pc)->op == POC_GOTO)prevreg = NULL; + + reg = pic16_getRegFromInstruction(pc); + +#if 0 + pc->print(stderr, pc); + fprintf(stderr, "reg = %p\n", reg); + + if(reg) { + fprintf(stderr, "%s:%d: %s %d\n",__FUNCTION__, __LINE__, reg->name, reg->rIdx); + fprintf(stderr, "addr = 0x%03x, bit=%d\tfix=%d\n", + reg->address,reg->isBitField, reg->isFixed); + } +#endif + + /* now make some tests to make sure that instruction needs bank switch */ + + /* if no register exists, and if not a bit opcode goto loop */ + if(!reg) { + if(!(PCI(pc)->pcop && PCI(pc)->pcop->type == PO_GPR_BIT))goto loop; + } + + if(isPCI_SKIP(pc)) { +// fprintf(stderr, "instruction is SKIP instruction\n"); +// prevreg = NULL; + } + if(reg && isACCESS_BANK(reg))goto loop; + + if(!isBankInstruction(pc))goto loop; + + if(isPCI_LIT(pc))goto loop; + + if(PCI(pc)->op == POC_CALL)goto loop; + + /* Examine the instruction before this one to make sure it is + * not a skip type instruction */ + pcprev = findPrevpCode(pc->prev, PC_OPCODE); + + flag = 0; /* add before this instruction */ + + /* if previous instruction is a skip one, then set flag + * to 2 and call insertBankSwitch */ + if(pcprev && isPCI_SKIP(pcprev)) { + flag=2; //goto loop +// prevreg = NULL; + } + + if(pic16_options.opt_banksel>0) { + char op1[128], op2[128]; + + if(prevreg) { + strncpy(op1, pic16_get_op_from_instruction(PCI(pc)), sizeof(op1) - 1); + op1[sizeof(op1) - 1] = '\0'; + + strncpy(op2, pic16_get_op_from_instruction(PCI(pcprev)), sizeof(op2) - 1); + op2[sizeof(op2) - 1] = '\0'; + + if(!strcmp(op1, op2))goto loop; + } + } + prevreg = reg; + insertBankSwitch(flag, pc); + +// fprintf(stderr, "BANK SWITCH inserted\n"); + +loop: + pcprev = pc; + pc = pc->next; + } while (pc); +} + +/** ADDITIONS BY RAPHAEL NEIDER, 2004-11-16: GOTO OPTIMIZATIONS **/ + +/* Returns the (maximum of the) number of bytes used by the specified pCode. */ +static int instrSize (pCode *pc) +{ + if (!pc) return 0; + + if (isPCAD(pc)) { + if (!PCAD(pc)->directive || strlen (PCAD(pc)->directive) < 3) return 0; + return 4; // assumes only regular instructions using <= 4 bytes + } + + if (isPCI(pc)) return PCI(pc)->isize; + + return 0; +} + +/* Returns 1 if pc is referenced by the given label (either + * pc is the label itself or is an instruction with an attached + * label). + * Returns 0 if pc is not preceeded by the specified label. + */ +static int isLabel (const pCode *pc, const char *label) +{ + if (!pc) return FALSE; + + // label attached to the pCode? + if (isPCI(pc) || isPCAD(pc) || isPCW(pc) || pc->type == PC_INFO) { + pBranch *lab = NULL; + lab = PCI(pc)->label; + + while (lab) { + if (isPCL(lab->pc) && strcmp(PCL(lab->pc)->label, label) == 0) { + return TRUE; + } + lab = lab->next; + } + } + + // is inline assembly label? + if (isPCAD(pc) && PCAD(pc)->directive == NULL && PCAD(pc)->arg) { + // do not compare trailing ':' + if (strncmp(PCAD(pc)->arg, label, strlen(label)) == 0) { + return TRUE; + } + } + + // is pCodeLabel? + if (isPCL(pc)) { + if (strcmp(PCL(pc)->label, label) == 0) { + return TRUE; + } + } + + // no label/no label attached/wrong label(s) + return FALSE; +} + +/* Returns the distance to the given label in terms of words. + * Labels are searched only within -max .. max words from pc. + * Returns max if the label could not be found or + * its distance from pc in (-max..+max). + */ +static int findpCodeLabel (pCode *pc, char *label, int max, pCode **target) { + int dist = instrSize(pc); + pCode *curr = pc; + + // search backwards + while (dist < max && curr && !isLabel (curr, label)) { + curr = curr->prev; + dist += instrSize(curr); // sizeof (instruction) + } // while + if (curr && dist < max) { + if (target != NULL) *target = curr; + return -dist; + } + + dist = 0; + curr = pic16_findNextInstruction (pc->next); + //search forwards + while (dist < max && curr && !isLabel (curr, label)) { + dist += instrSize(curr); // sizeof (instruction) + curr = curr->next; + } // while + if (curr && dist < max) { + if (target != NULL) *target = curr; + return dist; + } + + if (target != NULL) *target = NULL; + return max; +} + +/* Returns -1 if pc does NOT denote an instruction like + * BTFS[SC] STATUS,i + * Otherwise we return + * (a) 0x10 + i for BTFSS + * (b) 0x00 + i for BTFSC + */ +static int isSkipOnStatus (const pCode *pc) +{ + int res = -1; + pCodeOp *pcop; + + if (!pc || !isPCI(pc)) return -1; + + if (PCI(pc)->op == POC_BTFSS) res = 0x10; + else if (PCI(pc)->op == POC_BTFSC) res = 0x00; + else return -1; + + pcop = PCI(pc)->pcop; + + if (pcop->type == PO_STATUS || (pcop->type == PO_GPR_BIT && strcmp(pcop->name, "STATUS") == 0)) { + return (res + ((pCodeOpRegBit *)pcop)->bit); + } + + return -1; +} + +/* Returns 1 if pc is one of BC, BZ, BOV, BN, BNC, BNZ, BNOV or BNN, + * returns 0 otherwise. */ +static int isConditionalBranch (const pCode *pc) +{ + if (!pc || !isPCI_BRANCH(pc)) return FALSE; + + switch (PCI(pc)->op) { + case POC_BC: + case POC_BZ: + case POC_BOV: + case POC_BN: + case POC_BNC: + case POC_BNZ: + case POC_BNOV: + case POC_BNN: + return TRUE; + + default: + break; + } // switch + + return FALSE; +} + +/* Returns 1 if pc has a label attached to it. + * This can be either a label stored in the pCode itself (.label) + * or a label making up its own pCode preceding this pc. + * Returns 0 if pc cannot be reached directly via a label. + */ +const int hasNoLabel (const pCode *pc) +{ + pCode *prev; + + if (!pc) return TRUE; + + // are there any label pCodes between pc and the previous instruction? + prev = pic16_findPrevInstruction (pc->prev); + while (pc && pc != prev) { + // pCode with attached label? + if ((isPCI(pc) || isPCAD(pc) || isPCW(pc) || pc->type == PC_INFO) + && PCI(pc)->label) { + return FALSE; + } + // is inline assembly label? + if (isPCAD(pc) && PCAD(pc)->directive == NULL) return FALSE; + if (isPCW(pc) && PCW(pc)->label) return FALSE; + + // pCodeLabel? + if (isPCL(pc)) return FALSE; + + pc = pc->prev; + } // if + + // no label found + return TRUE; +} + +static void pic16_InsertCommentAfter (pCode *pc, const char *fmt, ...) { + char buf[512]; + va_list va; + + va_start (va, fmt); + vsprintf (buf, fmt, va); + va_end (va); + + pic16_pCodeInsertAfter (pc, pic16_newpCodeCharP(buf)); +} + +/* Replaces the old pCode with the new one, moving the labels, + * C source line and probably flow information to the new pCode. + */ +void pic16_pCodeReplace (pCode *oldPC, pCode *newPC) { + if (!oldPC || !newPC || !isPCI(oldPC) || !isPCI(newPC)) + return; + + /* first move all labels from old to new */ + PCI(newPC)->label = pic16_pBranchAppend (PCI(oldPC)->label, PCI(newPC)->label); + PCI(oldPC)->label = NULL; + +#if 0 + /* move C source line (if possible) */ + if (PCI(oldPC)->cline && !PCI(newPC)->cline) + PCI(newPC)->cline = PCI(oldPC)->cline; +#endif + + /* keep flow information intact */ + newPC->seq = oldPC->seq; + PCI(newPC)->pcflow = PCI(oldPC)->pcflow; + if (PCI(newPC)->pcflow && PCI(newPC)->pcflow->end == oldPC) { + PCI(newPC)->pcflow->end = newPC; + } + + /* insert a comment stating which pCode has been replaced */ +#if 1 + if (pic16_pcode_verbose || pic16_debug_verbose) { + char pc_str[256]; + + pic16_pCode2str (pc_str, sizeof(pc_str), oldPC); + pic16_InsertCommentAfter (oldPC->prev, "%s: replaced %s", __FUNCTION__, pc_str); + } +#endif + + /* insert new pCode into pBlock */ + pic16_pCodeInsertAfter (oldPC, newPC); + pic16_unlinkpCode (oldPC); + + /* destruct replaced pCode */ + oldPC->destruct (oldPC); +} + +/* Returns the inverted conditional branch (if any) or NULL. + * pcop must be set to the new jump target. + */ +static pCode *getNegatedBcc (const pCode *bcc, pCodeOp *pcop) +{ + pCode *newBcc; + + if (!bcc || !isPCI(bcc)) return NULL; + + switch (PCI(bcc)->op) { + case POC_BC: newBcc = pic16_newpCode (POC_BNC , pcop); break; + case POC_BZ: newBcc = pic16_newpCode (POC_BNZ , pcop); break; + case POC_BOV: newBcc = pic16_newpCode (POC_BNOV, pcop); break; + case POC_BN: newBcc = pic16_newpCode (POC_BNN , pcop); break; + case POC_BNC: newBcc = pic16_newpCode (POC_BC , pcop); break; + case POC_BNZ: newBcc = pic16_newpCode (POC_BZ , pcop); break; + case POC_BNOV: newBcc = pic16_newpCode (POC_BOV , pcop); break; + case POC_BNN: newBcc = pic16_newpCode (POC_BN , pcop); break; + default: + newBcc = NULL; + } + return newBcc; +} + +#define MAX_DIST_GOTO 0x7FFFFFFF +#define MAX_DIST_BRA 1020 // maximum offset (in bytes) possible with BRA +#define MAX_DIST_BCC 120 // maximum offset (in bytes) possible with Bcc +#define MAX_JUMPCHAIN_DEPTH 16 // number of GOTOs to follow in resolveJumpChain() (to prevent endless loops) +#define IS_GOTO(arg) ((arg) && isPCI(arg) && (PCI(arg)->op == POC_GOTO || PCI(arg)->op == POC_BRA)) + +/* Follows GOTO/BRA instructions to their target instructions, stores the + * final destination (not a GOTO or BRA instruction) in target and returns + * the distance from the original pc to *target. + */ +int resolveJumpChain (pCode *pc, pCode **target, pCodeOp **pcop) { + pCode *curr = pc; + pCode *last = NULL; + pCodeOp *lastPCOP = NULL; + int dist = 0; + int depth = 0; + + //fprintf (stderr, "%s:%d: -=-", __FUNCTION__, __LINE__); + + /* only follow unconditional branches, except for the initial pCode (which may be a conditional branch) */ + while (curr && (last != curr) && (depth++ < MAX_JUMPCHAIN_DEPTH) && isPCI(curr) + && (PCI(curr)->op == POC_GOTO || PCI(curr)->op == POC_BRA || (curr == pc && isConditionalBranch(curr)))) { + last = curr; + lastPCOP = PCI(curr)->pcop; + dist = findpCodeLabel (pc, PCI(curr)->pcop->name, MAX_DIST_GOTO, &curr); + //fprintf (stderr, "last:%p, curr:%p, label:%s\n", last, curr, PCI(last)->pcop->name); + } // while + + if (target) *target = last; + if (pcop) *pcop = lastPCOP; + return dist; +} + +/* Returns pc if it is not a OPT_JUMPTABLE_BEGIN INFO pCode. + * Otherwise the first pCode after the jumptable (after + * the OPT_JUMPTABLE_END tag) is returned. + */ +pCode *skipJumptables (pCode *pc, int *isJumptable) +{ + *isJumptable = 0; + + while (pc && pc->type == PC_INFO && PCINF(pc)->type == INF_OPTIMIZATION && PCOO(PCINF(pc)->oper1)->type == OPT_JUMPTABLE_BEGIN) { + *isJumptable = 1; + //fprintf (stderr, "SKIPPING jumptable\n"); + do { + //pc->print(stderr, pc); + pc = pc->next; + } while (pc && (pc->type != PC_INFO || PCINF(pc)->type != INF_OPTIMIZATION + || PCOO(PCINF(pc)->oper1)->type != OPT_JUMPTABLE_END)); + //fprintf (stderr, "<<JUMPTAB:\n"); + // skip OPT_END as well + if (pc) pc = pc->next; + } // while + + return pc; +} + +pCode *pic16_findNextInstructionSkipJumptables (pCode *pc, int *isJumptable) +{ + int isJumptab; + + *isJumptable = 0; + while (pc && !isPCI(pc) && !isPCAD(pc) && !isPCW(pc)) { + // set pc to the first pCode after a jumptable, leave pc untouched otherwise + pc = skipJumptables (pc, &isJumptab); + if (isJumptab) { + // pc is the first pCode after the jumptable + *isJumptable = 1; + } else { + // pc has not been changed by skipJumptables() + pc = pc->next; + } + } // while + + return pc; +} + +/* Turn GOTOs into BRAs if distance between GOTO and label + * is less than 1024 bytes. + * + * This method is especially useful if GOTOs after BTFS[SC] + * can be turned into BRAs as GOTO would cost another NOP + * if skipped. + */ +void pic16_OptimizeJumps (void) +{ + pCode *pc; + pCode *pc_prev = NULL; + pCode *pc_next = NULL; + pBlock *pb; + pCode *target; + int change, iteration, isJumptab; + int isHandled = 0; + char *label; + int opt=0, toofar=0, opt_cond = 0, cond_toofar=0, opt_reorder = 0, opt_gotonext = 0, opt_gotochain = 0; + + if (!the_pFile) return; + + //fprintf (stderr, "%s:%d: %s\n", __FILE__, __LINE__, __FUNCTION__); + + for (pb = the_pFile->pbHead; pb != NULL; pb = pb->next) { + int matchedInvertRule = 1; + iteration = 1; + do { + //fprintf (stderr, "%s:%d: iterating over pBlock %p\n", __FUNCTION__, __LINE__, pb); + change = 0; + pc = pic16_findNextInstruction (pb->pcHead); + + while (pc) { + pc_next = pic16_findNextInstructionSkipJumptables (pc->next, &isJumptab); + if (isJumptab) { + // skip jumptable, i.e. start over with no pc_prev! + pc_prev = NULL; + pc = pc_next; + continue; + } // if + + /* (1) resolve chained jumps + * Do not perform this until pattern (4) is no longer present! Otherwise we will + * (a) leave dead code in and + * (b) skip over the dead code with an (unneccessary) jump. + */ + if (!matchedInvertRule && (IS_GOTO(pc) || isConditionalBranch(pc))) { + pCodeOp *lastTargetOp = NULL; + int newDist = resolveJumpChain (pc, &target, &lastTargetOp); + int maxDist = MAX_DIST_BCC; + if (PCI(pc)->op == POC_BRA) maxDist = MAX_DIST_BRA; + if (PCI(pc)->op == POC_GOTO) maxDist = MAX_DIST_GOTO; + + /* be careful NOT to make the jump instruction longer (might break previously shortened jumps!) */ + if (lastTargetOp && newDist <= maxDist && lastTargetOp != PCI(pc)->pcop + && strcmp (lastTargetOp->name, PCI(pc)->pcop->name) != 0) { + //fprintf (stderr, "(1) ");pc->print(stderr, pc); fprintf (stderr, " --> %s\n", lastTargetOp->name); + if (pic16_pcode_verbose) { pic16_pCodeInsertAfter (pc->prev, pic16_newpCodeCharP("(1) jump chain resolved")); } + PCI(pc)->pcop->name = lastTargetOp->name; + change++; + opt_gotochain++; + } // if + } // if + + + if (IS_GOTO(pc)) { + int dist; + int condBraType = isSkipOnStatus(pc_prev); + label = PCI(pc)->pcop->name; + dist = findpCodeLabel(pc, label, MAX_DIST_BRA, &target); + if (dist < 0) dist = -dist; + //fprintf (stderr, "distance: %d (", dist); pc->print(stderr, pc);fprintf (stderr, ")\n"); + isHandled = 0; + + + /* (2) remove "GOTO label; label:" */ + if (isLabel (pc_next, label)) { + //fprintf (stderr, "(2) GOTO next instruction: ");pc->print(stderr, pc);fprintf (stderr, " --> ");pc_next->print(stderr, pc_next); fprintf(stderr, "\n"); + // first remove all preceeding SKIP instructions + while (pc_prev && isPCI_SKIP(pc_prev)) { + // attach labels on this instruction to pc_next + //fprintf (stderr, "(2) preceeding SKIP removed: ");pc_prev->print(stderr, pc_prev);fprintf(stderr, "\n"); + PCI(pc_next)->label = pic16_pBranchAppend (PCI(pc_prev)->label, PCI(pc_next)->label); + PCI(pc_prev)->label = NULL; + if (pic16_pcode_verbose) { pic16_pCodeInsertAfter (pc->prev, pic16_newpCodeCharP("(2) SKIP removed")); } + pic16_unlinkpCode (pc_prev); + pc_prev = pic16_findPrevInstruction (pc); + } // while + // now remove the redundant goto itself + PCI(pc_next)->label = pic16_pBranchAppend (PCI(pc)->label, PCI(pc_next)->label); + if (pic16_pcode_verbose) { pic16_pCodeInsertAfter (pc, pic16_newpCodeCharP("(2) GOTO next instruction removed")); } + pic16_unlinkpCode (pc); + pc = pic16_findPrevInstruction(pc_next->prev); + isHandled = 1; // do not perform further optimizations + opt_gotonext++; + change++; + } // if + + + /* (3) turn BTFSx STATUS,i; GOTO label into Bcc label if possible */ + if (!isHandled && condBraType != -1 && hasNoLabel(pc)) { + if (dist < MAX_DIST_BCC) { + pCode *bcc = NULL; + switch (condBraType) { + case 0x00: bcc = pic16_newpCode (POC_BC, PCI(pc)->pcop);break; + // no BDC on DIGIT CARRY available + case 0x02: bcc = pic16_newpCode (POC_BZ, PCI(pc)->pcop);break; + case 0x03: bcc = pic16_newpCode (POC_BOV, PCI(pc)->pcop);break; + case 0x04: bcc = pic16_newpCode (POC_BN, PCI(pc)->pcop);break; + case 0x10: bcc = pic16_newpCode (POC_BNC, PCI(pc)->pcop);break; + // no BNDC on DIGIT CARRY available + case 0x12: bcc = pic16_newpCode (POC_BNZ, PCI(pc)->pcop);break; + case 0x13: bcc = pic16_newpCode (POC_BNOV, PCI(pc)->pcop);break; + case 0x14: bcc = pic16_newpCode (POC_BNN, PCI(pc)->pcop);break; + default: + // no replacement possible + bcc = NULL; + break; + } // switch + if (bcc) { + // ATTENTION: keep labels attached to BTFSx! + // HINT: GOTO is label free (checked above) + //fprintf (stderr, "%s:%d: (3) turning %s %s into %s %s\n", __FUNCTION__, __LINE__, PCI(pc)->mnemonic, label, PCI(bcc)->mnemonic, label); + isHandled = 1; // do not perform further optimizations + if (pic16_pcode_verbose) { pic16_pCodeInsertAfter(pc_prev->prev, pic16_newpCodeCharP("(3) conditional branch introduced")); } + pic16_pCodeReplace (pc_prev, bcc); + pc->destruct(pc); + pc = bcc; + opt_cond++; + change++; + } // if + } else { + //fprintf (stderr, "(%d, too far for Bcc)\n", dist); + cond_toofar++; + } // if + } // if + + if (!isHandled) { + // (4) eliminate the following (common) tripel: + // <pred.>; + // labels1: Bcc label2; + // GOTO somewhere; ; <-- instruction referenced by pc + // label2: <cont.> + // and replace it by + // labels1: B#(cc) somewhere; ; #(cc) is the negated condition cc + // label2: <cont.> + // ATTENTION: all labels pointing to "Bcc label2" must be attached + // to <cont.> instead + // ATTENTION: This optimization is only valid if <pred.> is + // not a skip operation! + // ATTENTION: somewhere must be within MAX_DIST_BCC bytes! + // ATTENTION: no label may be attached to the GOTO instruction! + if (isConditionalBranch(pc_prev) + && (!isPCI_SKIP(pic16_findPrevInstruction(pc_prev->prev))) + && (dist < MAX_DIST_BCC) + && isLabel(pc_next,PCI(pc_prev)->pcop->name) + && hasNoLabel(pc)) { + pCode *newBcc = getNegatedBcc (pc_prev, PCI(pc)->pcop); + + if (newBcc) { + //fprintf (stderr, "%s:%d: (4) turning %s %s into %s %s\n", __FUNCTION__, __LINE__, PCI(pc)->mnemonic, label, PCI(newBcc)->mnemonic, label); + isHandled = 1; // do not perform further optimizations + if (pic16_pcode_verbose) { pic16_pCodeInsertAfter(pc_prev->prev, pic16_newpCodeCharP("(4) conditional skipping branch inverted")); } + pic16_pCodeReplace (pc_prev, newBcc); + pc->destruct(pc); + pc = newBcc; + opt_reorder++; + change++; + matchedInvertRule++; + } + } + } + + /* (5) now just turn GOTO into BRA */ + if (!isHandled && (PCI(pc)->op == POC_GOTO)) { + if (dist < MAX_DIST_BRA) { + pCode *newBra = pic16_newpCode (POC_BRA, PCI(pc)->pcop); + //fprintf (stderr, "%s:%d: (5) turning %s %s into %s %s\n", __FUNCTION__, __LINE__, PCI(pc)->mnemonic, label, PCI(newBra)->mnemonic, label); + if (pic16_pcode_verbose) { pic16_pCodeInsertAfter(pc->prev, pic16_newpCodeCharP("(5) GOTO replaced by BRA")); } + pic16_pCodeReplace (pc, newBra); + pc = newBra; + opt++; + change++; + } else { + //fprintf (stderr, "(%d, too far for BRA)\n", dist); + toofar++; + } + } // if (!isHandled) + } // if + + pc_prev = pc; + pc = pc_next; + } // while (pc) + + pBlockRemoveUnusedLabels (pb); + + // This line enables goto chain resolution! + if (matchedInvertRule > 1) matchedInvertRule = 1; else matchedInvertRule = 0; + + iteration++; + } while (change); /* fixpoint iteration per pBlock */ + } // for (pb) + + // emit some statistics concerning goto-optimization +#if 0 + if (pic16_debug_verbose || pic16_pcode_verbose) { + fprintf (stderr, "optimize-goto:\n" + "\t%5d GOTO->BRA; (%d GOTOs too far)\n" + "\t%5d BTFSx, GOTO->Bcc (%d too far)\n" + "\t%5d conditional \"skipping\" jumps inverted\n" + "\t%5d GOTOs to next instruction removed\n" + "\t%5d chained GOTOs resolved\n", + opt, toofar, opt_cond, cond_toofar, opt_reorder, opt_gotonext, opt_gotochain); + } // if +#endif + //fprintf (stderr, "%s:%d: %s\n", __FILE__, __LINE__, __FUNCTION__); +} + +#undef IS_GOTO +#undef MAX_JUMPCHAIN_DEPTH +#undef MAX_DIST_GOTO +#undef MAX_DIST_BRA +#undef MAX_DIST_BCC + +/** END OF RAPHAEL NEIDER'S ADDITIONS **/ + +static void pBlockDestruct(pBlock *pb) +{ + if(!pb) + return; + +// Safe_free(pb); +} + +/*-----------------------------------------------------------------*/ +/* void mergepBlocks(char dbName) - Search for all pBlocks with the*/ +/* name dbName and combine them */ +/* into one block */ +/*-----------------------------------------------------------------*/ +static void mergepBlocks(char dbName) +{ + pBlock *pb, *pbmerged = NULL,*pbn; + + pb = the_pFile->pbHead; + + //fprintf(stderr," merging blocks named %c\n",dbName); + while(pb) { + + pbn = pb->next; + //fprintf(stderr,"looking at %c\n",getpBlock_dbName(pb)); + if( getpBlock_dbName(pb) == dbName) { + + //fprintf(stderr," merged block %c\n",dbName); + + if(!pbmerged) { + pbmerged = pb; + } else { + pic16_addpCode2pBlock(pbmerged, pb->pcHead); + /* pic16_addpCode2pBlock doesn't handle the tail: */ + pbmerged->pcTail = pb->pcTail; + + pb->prev->next = pbn; + if(pbn) + pbn->prev = pb->prev; + + pBlockDestruct(pb); + } + //pic16_printpBlock(stderr, pbmerged); + } + pb = pbn; + } +} + +/*-----------------------------------------------------------------*/ +/* AnalyzeFlow - Examine the flow of the code and optimize */ +/* */ +/* level 0 == minimal optimization */ +/* optimize registers that are used only by two instructions */ +/* level 1 == maximal optimization */ +/* optimize by looking at pairs of instructions that use the */ +/* register. */ +/*-----------------------------------------------------------------*/ + +static void AnalyzeFlow(int level) +{ + static int times_called=0; + pBlock *pb; + + if(!the_pFile) { + /* remove unused allocated registers before exiting */ + pic16_RemoveUnusedRegisters(); + return; + } + + /* if this is not the first time this function has been called, + * then clean up old flow information */ + if(times_called++) { + for(pb = the_pFile->pbHead; pb; pb = pb->next) + unBuildFlow(pb); + pic16_RegsUnMapLiveRanges(); + } + GpcFlowSeq = 1; + + /* Phase 2 - Flow Analysis - Register Banking + * + * In this phase, the individual flow blocks are examined + * and register banking is fixed. + */ + +#if 0 + for(pb = the_pFile->pbHead; pb; pb = pb->next) + pic16_FixRegisterBanking(pb); +#endif + + /* Phase 2 - Flow Analysis + * + * In this phase, the pCode is partition into pCodeFlow + * blocks. The flow blocks mark the points where a continuous + * stream of instructions changes flow (e.g. because of + * a call or goto or whatever). + */ + + for(pb = the_pFile->pbHead; pb; pb = pb->next) + pic16_BuildFlow(pb); + + /* Phase 2 - Flow Analysis - linking flow blocks + * + * In this phase, the individual flow blocks are examined + * to determine their order of excution. + */ + + for(pb = the_pFile->pbHead; pb; pb = pb->next) + LinkFlow(pb); + +#if 1 + if (pic16_options.opt_flags & OF_OPTIMIZE_DF) { + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + pic16_createDF (pb); +#if defined (DUMP_DF_GRAPHS) && DUMP_DF_GRAPHS > 0 + pic16_vcg_dump_default (pb); +#endif + //pic16_destructDF (pb); + } + + pic16_df_stats (); + if (0) releaseStack (); // releasing is costly... + } +#endif + + /* Phase 3 - Flow Analysis - Flow Tree + * + * In this phase, the individual flow blocks are examined + * to determine their order of execution. + */ + + for(pb = the_pFile->pbHead; pb; pb = pb->next) + pic16_BuildFlowTree(pb); + + + /* Phase x - Flow Analysis - Used Banks + * + * In this phase, the individual flow blocks are examined + * to determine the Register Banks they use + */ + +#if 0 + for(pb = the_pFile->pbHead; pb; pb = pb->next) + FixBankFlow(pb); +#endif + + + for(pb = the_pFile->pbHead; pb; pb = pb->next) + pic16_pCodeRegMapLiveRanges(pb); + + pic16_RemoveUnusedRegisters(); + pic16_removeUnusedRegistersDF (); + + // for(pb = the_pFile->pbHead; pb; pb = pb->next) + pic16_pCodeRegOptimizeRegUsage(level); + + +#if 0 + if(!options.nopeep) + OptimizepCode('*'); +#endif + +#if 0 + for(pb = the_pFile->pbHead; pb; pb = pb->next) + DumpFlow(pb); +#endif + + /* debug stuff */ + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + pCode *pcflow; + + for( pcflow = pic16_findNextpCode(pb->pcHead, PC_FLOW); + (pcflow = pic16_findNextpCode(pcflow, PC_FLOW)) != NULL; + pcflow = pcflow->next) { + FillFlow(PCFL(pcflow)); + } + } + +#if 0 + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + pCode *pcflow; + + for( pcflow = pic16_findNextpCode(pb->pcHead, PC_FLOW); + (pcflow = pic16_findNextpCode(pcflow, PC_FLOW)) != NULL; + pcflow = pcflow->next) { + FlowStats(PCFL(pcflow)); + } + } +#endif +} + +/* VR -- no need to analyze banking in flow, but left here : + * 1. because it may be used in the future for other purposes + * 2. because if omitted we'll miss some optimization done here + * + * Perhaps I should rename it to something else + */ + +/*-----------------------------------------------------------------*/ +/* pic16_AnalyzeBanking - Called after the memory addresses have been */ +/* assigned to the registers. */ +/* */ +/*-----------------------------------------------------------------*/ + +void pic16_AnalyzeBanking(void) +{ + pBlock *pb; + + /* Phase x - Flow Analysis - Used Banks + * + * In this phase, the individual flow blocks are examined + * to determine the Register Banks they use + */ + + AnalyzeFlow(0); + AnalyzeFlow(1); + + if(!options.nopeep) + OptimizepCode('*'); + + + if(!the_pFile)return; + + if(!pic16_options.no_banksel) { + for(pb = the_pFile->pbHead; pb; pb = pb->next) { +// fprintf(stderr, "%s:%d: Fix register banking in pb= 0x%p\n", __FILE__, __LINE__, pb); + pic16_FixRegisterBanking(pb); + } + } +} + +/*-----------------------------------------------------------------*/ +/* buildCallTree - Look at the flow and extract all of the calls. */ +/*-----------------------------------------------------------------*/ +#if 0 +static set *register_usage(pBlock *pb); +#endif + +static void buildCallTree(void) +{ + pBranch *pbr; + pBlock *pb; + pCode *pc; + reg_info *r; + + if(!the_pFile) + return; + + + + /* Now build the call tree. + First we examine all of the pCodes for functions. + Keep in mind that the function boundaries coincide + with pBlock boundaries. + + The algorithm goes something like this: + We have two nested loops. The outer loop iterates + through all of the pBlocks/functions. The inner + loop iterates through all of the pCodes for + a given pBlock. When we begin iterating through + a pBlock, the variable pc_fstart, pCode of the start + of a function, is cleared. We then search for pCodes + of type PC_FUNCTION. When one is encountered, we + initialize pc_fstart to this and at the same time + associate a new pBranch object that signifies a + branch entry. If a return is found, then this signifies + a function exit point. We'll link the pCodes of these + returns to the matching pc_fstart. + + When we're done, a doubly linked list of pBranches + will exist. The head of this list is stored in + `the_pFile', which is the meta structure for all + of the pCode. Look at the pic16_printCallTree function + on how the pBranches are linked together. + + */ + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + pCode *pc_fstart=NULL; + for(pc = pb->pcHead; pc; pc = pc->next) { + + if(isPCI(pc) && pc_fstart) { + if(PCI(pc)->is2MemOp) { + r = pic16_getRegFromInstruction2(pc); + if(r && !strcmp(r->name, "POSTDEC1")) + PCF(pc_fstart)->stackusage++; + } else { + r = pic16_getRegFromInstruction(pc); + if(r && !strcmp(r->name, "PREINC1")) + PCF(pc_fstart)->stackusage--; + } + } + + if(isPCF(pc)) { + if (PCF(pc)->fname) { + char buf[16]; + + SNPRINTF(buf, sizeof(buf), "%smain", port->fun_prefix); + if(STRCASECMP(PCF(pc)->fname, buf) == 0) { + //fprintf(stderr," found main \n"); + pb->cmemmap = NULL; /* FIXME do we need to free ? */ + pb->dbName = 'M'; + } + + pbr = Safe_alloc(sizeof(pBranch)); + pbr->pc = pc_fstart = pc; + pbr->next = NULL; + + the_pFile->functions = pic16_pBranchAppend(the_pFile->functions,pbr); + + // Here's a better way of doing the same: + addSet(&pb->function_entries, pc); + + } else { + // Found an exit point in a function, e.g. return + // (Note, there may be more than one return per function) + if(pc_fstart) + pBranchLink(PCF(pc_fstart), PCF(pc)); + + addSet(&pb->function_exits, pc); + } + } else if(isCALL(pc)) { + addSet(&pb->function_calls,pc); + } + } + } + + +#if 0 + /* This is not needed because currently all register used + * by a function are stored in stack -- VR */ + + /* Re-allocate the registers so that there are no collisions + * between local variables when one function call another */ + + // this is weird... + // pic16_deallocateAllRegs(); + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + if(!pb->visited) + register_usage(pb); + } +#endif + +} + +/*-----------------------------------------------------------------*/ +/* pic16_AnalyzepCode - parse the pCode that has been generated and form */ +/* all of the logical connections. */ +/* */ +/* Essentially what's done here is that the pCode flow is */ +/* determined. */ +/*-----------------------------------------------------------------*/ + +void pic16_AnalyzepCode(char dbName) +{ + pBlock *pb; + int i,changes; + + if(!the_pFile) + return; + + mergepBlocks('D'); + + + /* Phase 1 - Register allocation and peep hole optimization + * + * The first part of the analysis is to determine the registers + * that are used in the pCode. Once that is done, the peep rules + * are applied to the code. We continue to loop until no more + * peep rule optimizations are found (or until we exceed the + * MAX_PASSES threshold). + * + * When done, the required registers will be determined. + * + */ + i = 0; + do { + + DFPRINTF((stderr," Analyzing pCode: PASS #%d\n",i+1)); + //fprintf(stderr," Analyzing pCode: PASS #%d\n",i+1); + + /* First, merge the labels with the instructions */ + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + if('*' == dbName || getpBlock_dbName(pb) == dbName) { + + DFPRINTF((stderr," analyze and merging block %c\n",dbName)); + //fprintf(stderr," analyze and merging block %c\n",dbName); + pic16_pBlockMergeLabels(pb); + AnalyzepBlock(pb); + } else { + DFPRINTF((stderr," skipping block analysis dbName=%c blockname=%c\n",dbName,getpBlock_dbName)); + } + } + + if(!options.nopeep) + changes = OptimizepCode(dbName); + else changes = 0; + + } while(changes && (i++ < MAX_PASSES)); + + + buildCallTree(); +} + + +/* convert a series of movff's of local regs to stack, with a single call to + * a support functions which does the same thing via loop */ +static void pic16_convertLocalRegs2Support(pCode *pcstart, pCode *pcend, int count, reg_info *r, int entry) +{ + pBranch *pbr; + pCode *pc, *pct; + char *fname[]={"__lr_store", "__lr_restore"}; + +// pc = pic16_newpCode(POC_CALL, pic16_popGetFromString( (entry?fname[0]:fname[1]) )); + + pct = pic16_findNextInstruction(pcstart->next); + do { + pc = pct; + pct = pc->next; //pic16_findNextInstruction(pc->next); +// pc->print(stderr, pc); + if(isPCI(pc) && PCI(pc)->label) { + pbr = PCI(pc)->label; + while(pbr && pbr->pc) { + PCI(pcstart)->label = pic16_pBranchAppend(PCI(pcstart)->label, pbr); + pbr = pbr->next; + } + +// pc->print(stderr, pc); + /* unlink pCode */ + pc->prev->next = pct; + pct->prev = pc->prev; +// pc->next = NULL; +// pc->prev = NULL; + } + } while (pc != pcend); + + /* unlink movff instructions */ + pcstart->next = pcend; + pcend->prev = pcstart; + + pc = pcstart; +// if(!entry) { +// pic16_pCodeInsertAfter(pc, pct = pic16_newpCode(POC_MOVFF, pic16_popGet2p( +// pic16_popCopyReg(&pic16_pc_fsr0l), pic16_popCopyReg(pic16_framepnt_lo)))); pc = pct; +// } + + pic16_pCodeInsertAfter(pc, pct=pic16_newpCode(POC_LFSR, pic16_popGetLit2(0, pic16_popGetWithString(r->name)))); pc = pct; + pic16_pCodeInsertAfter(pc, pct=pic16_newpCode(POC_MOVLW, pic16_popGetLit( count ))); pc = pct; + pic16_pCodeInsertAfter(pc, pct=pic16_newpCode(POC_CALL, pic16_popGetWithString( fname[ (entry==1?0:1) ] ))); pc = pct; + +// if(!entry) { +// pic16_pCodeInsertAfter(pc, pct = pic16_newpCode(POC_MOVFF, pic16_popGet2p( +// pic16_popCopyReg(pic16_framepnt_lo), pic16_popCopyReg(&pic16_pc_fsr0l)))); pc = pct; +// } + + + { + symbol *sym; + + sym = newSymbol( fname[ entry?0:1 ], 0 ); + strcpy(sym->rname, fname[ entry?0:1 ]); + checkAddSym(&externs, sym); + +// fprintf(stderr, "%s:%d adding extern symbol %s in externs\n", __FILE__, __LINE__, fname[ entry?0:1 ]); + } +} + +/*-----------------------------------------------------------------*/ +/* OptimizeLocalRegs - turn sequence of MOVFF instructions for */ +/* local registers to a support function call */ +/*-----------------------------------------------------------------*/ +void pic16_OptimizeLocalRegs(void) +{ + pBlock *pb; + pCode *pc; + pCodeInfo *pci; + pCodeOpLocalReg *pclr; + int regCount=0; + int inRegCount=0; + reg_info *r, *lastr=NULL, *firstr=NULL; + pCode *pcstart=NULL, *pcend=NULL; + int inEntry=0; + char *curFunc=NULL; + + /* Overview: + * local_regs begin mark + * MOVFF r0x01, POSTDEC1 + * MOVFF r0x02, POSTDEC1 + * ... + * ... + * MOVFF r0x0n, POSTDEC1 + * local_regs end mark + * + * convert the above to the below: + * MOVLW starting_register_index + * MOVWF PRODL + * MOVLW register_count + * call __save_registers_in_stack + */ + + if(!the_pFile) + return; + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + inRegCount = regCount = 0; + firstr = lastr = NULL; + for(pc = pb->pcHead; pc; pc = pc->next) { + + /* hold current function name */ + if(pc && isPCF(pc))curFunc = PCF(pc)->fname; + + if(pc && (pc->type == PC_INFO)) { + pci = PCINF(pc); + + if(pci->type == INF_LOCALREGS) { + pclr = PCOLR(pci->oper1); + + if((pclr->type == LR_ENTRY_BEGIN) + || (pclr->type == LR_ENTRY_END))inEntry = 1; + else inEntry = 0; + + switch(pclr->type) { + case LR_ENTRY_BEGIN: + case LR_EXIT_BEGIN: + inRegCount = 1; regCount = 0; + pcstart = pc; //pic16_findNextInstruction(pc->next); + firstr = lastr = NULL; + break; + + case LR_ENTRY_END: + case LR_EXIT_END: + inRegCount = -1; + pcend = pc; //pic16_findPrevInstruction(pc->prev); + +#if 1 + if(curFunc && inWparamList(curFunc+1)) { + fprintf(stderr, "sdcc: %s: warning: disabling lr-support for function %s\n", + filename, curFunc); + } else { + if(regCount>2) { + pic16_convertLocalRegs2Support(pcstart, pcend, regCount, + firstr, inEntry); + } + } +#endif + firstr = lastr = NULL; + break; + } + + if(inRegCount == -1) { +// fprintf(stderr, "%s:%d registers used [%s] %d\n", __FILE__, __LINE__, inEntry?"entry":"exit", regCount); + regCount = 0; + inRegCount = 0; + } + } + } else { + if(isPCI(pc) && (PCI(pc)->op == POC_MOVFF) && (inRegCount == 1)) { + if(inEntry) + r = pic16_getRegFromInstruction(pc); + else + r = pic16_getRegFromInstruction2(pc); + if(r && (r->type == REG_GPR) && (r->pc_type == PO_GPR_TEMP)) { + if(!firstr)firstr = r; + regCount++; +// fprintf(stderr, "%s:%d\t%s\t%i\t%d/%d\n", __FILE__, __LINE__, r->name, r->rIdx); + } + } + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* ispCodeFunction - returns true if *pc is the pCode of a */ +/* function */ +/*-----------------------------------------------------------------*/ +static bool ispCodeFunction(pCode *pc) +{ + + if(pc && pc->type == PC_FUNCTION && PCF(pc)->fname) + return 1; + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* findFunction - Search for a function by name (given the name) */ +/* in the set of all functions that are in a pBlock */ +/* (note - I expect this to change because I'm planning to limit */ +/* pBlock's to just one function declaration */ +/*-----------------------------------------------------------------*/ +static pCode *findFunction(char *fname) +{ + pBlock *pb; + pCode *pc; + if(!fname) + return NULL; + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + + pc = setFirstItem(pb->function_entries); + while(pc) { + + if((pc->type == PC_FUNCTION) && + (PCF(pc)->fname) && + (strcmp(fname, PCF(pc)->fname)==0)) + return pc; + + pc = setNextItem(pb->function_entries); + + } + + } + return NULL; +} + +#if 0 +static void MarkUsedRegisters(set *regset) +{ + + regs *r1,*r2; + + for(r1=setFirstItem(regset); r1; r1=setNextItem(regset)) { +// fprintf(stderr, "marking register = %s\t", r1->name); + r2 = pic16_regWithIdx(r1->rIdx); +// fprintf(stderr, "to register = %s\n", r2->name); + r2->isFree = 0; + r2->wasUsed = 1; + } +} +#endif + +static void pBlockStats(FILE *of, pBlock *pb) +{ + pCode *pc; + reg_info *r; + + if(!pic16_pcode_verbose)return; + + fprintf(of,";***\n; pBlock Stats: dbName = %c\n;***\n",getpBlock_dbName(pb)); + + // for now just print the first element of each set + pc = setFirstItem(pb->function_entries); + if(pc) { + fprintf(of,";entry: "); + pc->print(of,pc); + } + pc = setFirstItem(pb->function_exits); + if(pc) { + fprintf(of,";has an exit\n"); + //pc->print(of,pc); + } + + pc = setFirstItem(pb->function_calls); + if(pc) { + fprintf(of,";functions called:\n"); + + while(pc) { + if(pc->type == PC_OPCODE && PCI(pc)->op == POC_CALL) { + fprintf(of,"; %s\n",pic16_get_op_from_instruction(PCI(pc))); + } + pc = setNextItem(pb->function_calls); + } + } + + r = setFirstItem(pb->tregisters); + if(r) { + int n = elementsInSet(pb->tregisters); + + fprintf(of,";%d compiler assigned register%c:\n",n, ( (n!=1) ? 's' : ' ')); + + while (r) { + fprintf(of, "; %s\n",r->name); + r = setNextItem(pb->tregisters); + } + } + + fprintf(of, "; uses %d bytes of stack\n", 1+ elementsInSet(pb->tregisters)); +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +#if 0 +static void sequencepCode(void) +{ + pBlock *pb; + pCode *pc; + + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + + pb->seq = GpCodeSequenceNumber+1; + + for( pc = pb->pcHead; pc; pc = pc->next) + pc->seq = ++GpCodeSequenceNumber; + } +} +#endif + +#if 0 +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static set *register_usage(pBlock *pb) +{ + pCode *pc,*pcn; + set *registers=NULL; + set *registersInCallPath = NULL; + + /* check recursion */ + + pc = setFirstItem(pb->function_entries); + + if(!pc) + return registers; + + pb->visited = TRUE; + + if(pc->type != PC_FUNCTION) + fprintf(stderr,"%s, first pc is not a function???\n",__FUNCTION__); + + pc = setFirstItem(pb->function_calls); + for( ; pc; pc = setNextItem(pb->function_calls)) { + + if(pc->type == PC_OPCODE && PCI(pc)->op == POC_CALL) { + char *dest = pic16_get_op_from_instruction(PCI(pc)); + + pcn = findFunction(dest); + if(pcn) + registersInCallPath = register_usage(pcn->pb); + } else + fprintf(stderr,"BUG? pCode isn't a POC_CALL %d\n",__LINE__); + + } + +#ifdef PCODE_DEBUG + pBlockStats(stderr,pb); // debug +#endif + + // Mark the registers in this block as used. + + MarkUsedRegisters(pb->tregisters); + if(registersInCallPath) { + /* registers were used in the functions this pBlock has called */ + /* so now, we need to see if these collide with the ones we are */ + /* using here */ + + regs *r1,*r2, *newreg; + + DFPRINTF((stderr,"comparing registers\n")); + + r1 = setFirstItem(registersInCallPath); + while(r1) { + + r2 = setFirstItem(pb->tregisters); + + while(r2 && (r1->type != REG_STK)) { + + if(r2->rIdx == r1->rIdx) { + newreg = pic16_findFreeReg(REG_GPR); + + + if(!newreg) { + DFPRINTF((stderr,"Bummer, no more registers.\n")); + exit(1); + } + + DFPRINTF((stderr,"Cool found register collision nIdx=%d moving to %d\n", + r1->rIdx, newreg->rIdx)); + r2->rIdx = newreg->rIdx; + //if(r2->name) Safe_free(r2->name); + if(newreg->name) + r2->name = Safe_strdup(newreg->name); + else + r2->name = NULL; + newreg->isFree = 0; + newreg->wasUsed = 1; + } + r2 = setNextItem(pb->tregisters); + } + + r1 = setNextItem(registersInCallPath); + } + + /* Collisions have been resolved. Now free the registers in the call path */ + r1 = setFirstItem(registersInCallPath); + while(r1) { + if(r1->type != REG_STK) { + newreg = pic16_regWithIdx(r1->rIdx); + newreg->isFree = 1; + } + r1 = setNextItem(registersInCallPath); + } + + }// else + // MarkUsedRegisters(pb->registers); + + registers = unionSets(pb->tregisters, registersInCallPath, THROW_NONE); +#ifdef PCODE_DEBUG + if(registers) + DFPRINTF((stderr,"returning regs\n")); + else + DFPRINTF((stderr,"not returning regs\n")); + + DFPRINTF((stderr,"pBlock after register optim.\n")); + pBlockStats(stderr,pb); // debug +#endif + + return registers; +} +#endif + +/*-----------------------------------------------------------------*/ +/* pct2 - writes the call tree to a file */ +/* */ +/*-----------------------------------------------------------------*/ +static void pct2(FILE *of,pBlock *pb,int indent,int usedstack) +{ + pCode *pc,*pcn; + int i; + // set *registersInCallPath = NULL; + + if(!of) + return; + + if(indent > 10) { + fprintf(of, "recursive function\n"); + return; //recursion ? + } + + pc = setFirstItem(pb->function_entries); + + if(!pc) + return; + + pb->visited = FALSE; + + for(i=0;i<indent;i++) // Indentation + fputs("+ ", of); + fputs("+- ", of); + + if(pc->type == PC_FUNCTION) { + usedstack += PCF(pc)->stackusage; + fprintf(of,"%s (stack: %i)\n",PCF(pc)->fname, usedstack); + } else return; // ??? + + + pc = setFirstItem(pb->function_calls); + for( ; pc; pc = setNextItem(pb->function_calls)) { + + if(pc->type == PC_OPCODE && PCI(pc)->op == POC_CALL) { + char *dest = pic16_get_op_from_instruction(PCI(pc)); + + pcn = findFunction(dest); + if(pcn) + pct2(of,pcn->pb,indent+1, usedstack); // + PCF(pcn)->stackusage); + } else + fprintf(of,"BUG? pCode isn't a POC_CALL %d\n",__LINE__); + + } +} + + +/*-----------------------------------------------------------------*/ +/* pic16_printCallTree - writes the call tree to a file */ +/* */ +/*-----------------------------------------------------------------*/ + +void pic16_printCallTree(FILE *of) +{ + pBranch *pbr; + pBlock *pb; + pCode *pc; + + if(!the_pFile) + return; + + if(!of) + of = stderr; + + fprintf(of, "\npBlock statistics\n"); + for(pb = the_pFile->pbHead; pb; pb = pb->next ) + pBlockStats(of,pb); + + + fprintf(of,"Call Tree\n"); + pbr = the_pFile->functions; + while(pbr) { + if(pbr->pc) { + pc = pbr->pc; + if(!ispCodeFunction(pc)) + fprintf(of,"bug in call tree"); + + + fprintf(of,"Function: %s\n", PCF(pc)->fname); + + while(pc->next && !ispCodeFunction(pc->next)) { + pc = pc->next; + if(pc->type == PC_OPCODE && PCI(pc)->op == POC_CALL) + fprintf(of,"\t%s\n",pic16_get_op_from_instruction(PCI(pc))); + } + } + + pbr = pbr->next; + } + + + fprintf(of,"\n**************\n\na better call tree\n"); + for(pb = the_pFile->pbHead; pb; pb = pb->next) { +// if(pb->visited) + pct2(of,pb,0,0); + } + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + fprintf(of,"block dbname: %c\n", getpBlock_dbName(pb)); + } +} + + + +/*-----------------------------------------------------------------*/ +/* */ +/*-----------------------------------------------------------------*/ + +static void InlineFunction(pBlock *pb) +{ + pCode *pc; + pCode *pc_call; + + if(!pb) + return; + + pc = setFirstItem(pb->function_calls); + + for( ; pc; pc = setNextItem(pb->function_calls)) { + + if(isCALL(pc)) { + pCode *pcn = findFunction(pic16_get_op_from_instruction(PCI(pc))); + pCode *pct; + pCode *pce; + + pBranch *pbr; + + if(pcn && isPCF(pcn) && (PCF(pcn)->ncalled == 0)) { /* change 0 to 1 to enable inlining */ + + //fprintf(stderr,"Cool can inline:\n"); + //pcn->print(stderr,pcn); + + //fprintf(stderr,"recursive call Inline\n"); + InlineFunction(pcn->pb); + //fprintf(stderr,"return from recursive call Inline\n"); + + /* + At this point, *pc points to a CALL mnemonic, and + *pcn points to the function that is being called. + + To in-line this call, we need to remove the CALL + and RETURN(s), and link the function pCode in with + the CALLee pCode. + + */ + + + /* Remove the CALL */ + pc_call = pc; + pc = pc->prev; + + /* remove callee pBlock from the pBlock linked list */ + removepBlock(pcn->pb); + + pce = pcn; + while(pce) { + pce->pb = pb; + pce = pce->next; + } + + /* Remove the Function pCode */ + pct = pic16_findNextInstruction(pcn->next); + + /* Link the function with the callee */ + pc->next = pcn->next; + pcn->next->prev = pc; + + /* Convert the function name into a label */ + + pbr = Safe_alloc(sizeof(pBranch)); + pbr->pc = pic16_newpCodeLabel(PCF(pcn)->fname, -1); + pbr->next = NULL; + PCI(pct)->label = pic16_pBranchAppend(PCI(pct)->label,pbr); + PCI(pct)->label = pic16_pBranchAppend(PCI(pct)->label,PCI(pc_call)->label); + + /* turn all of the return's except the last into goto's */ + /* check case for 2 instruction pBlocks */ + pce = pic16_findNextInstruction(pcn->next); + while(pce) { + pCode *pce_next = pic16_findNextInstruction(pce->next); + + if(pce_next == NULL) { + /* found the last return */ + pCode *pc_call_next = pic16_findNextInstruction(pc_call->next); + + //fprintf(stderr,"found last return\n"); + //pce->print(stderr,pce); + pce->prev->next = pc_call->next; + pc_call->next->prev = pce->prev; + PCI(pc_call_next)->label = pic16_pBranchAppend(PCI(pc_call_next)->label, + PCI(pce)->label); + } + + pce = pce_next; + } + } + } else + fprintf(stderr,"BUG? pCode isn't a POC_CALL %d\n",__LINE__); + } +} + +/*-----------------------------------------------------------------*/ +/* */ +/*-----------------------------------------------------------------*/ + +void pic16_InlinepCode(void) +{ + pBlock *pb; + pCode *pc; + + if(!the_pFile) + return; + + if(!functionInlining) + return; + + /* Loop through all of the function definitions and count the + * number of times each one is called */ + //fprintf(stderr,"inlining %d\n",__LINE__); + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + + pc = setFirstItem(pb->function_calls); + + for( ; pc; pc = setNextItem(pb->function_calls)) { + + if(isCALL(pc)) { + pCode *pcn = findFunction(pic16_get_op_from_instruction(PCI(pc))); + if(pcn && isPCF(pcn)) { + PCF(pcn)->ncalled++; + } + } else + fprintf(stderr,"BUG? pCode isn't a POC_CALL %d\n",__LINE__); + + } + } + + //fprintf(stderr,"inlining %d\n",__LINE__); + + /* Now, Loop through the function definitions again, but this + * time inline those functions that have only been called once. */ + + InlineFunction(the_pFile->pbHead); + //fprintf(stderr,"inlining %d\n",__LINE__); + + for(pb = the_pFile->pbHead; pb; pb = pb->next) + unBuildFlow(pb); +} + +static const char *pic_optype_names[] = { + "PO_NONE", // No operand e.g. NOP + "PO_W", // The working register (as a destination) + "PO_WREG", // The working register (as a file register) + "PO_STATUS", // The 'STATUS' register + "PO_BSR", // The 'BSR' register + "PO_FSR0", // The "file select register" (in PIC18 family it's one + // of three) + "PO_INDF0", // The Indirect register + "PO_INTCON", // Interrupt Control register + "PO_GPR_REGISTER", // A general purpose register + "PO_GPR_BIT", // A bit of a general purpose register + "PO_GPR_TEMP", // A general purpose temporary register + "PO_SFR_REGISTER", // A special function register (e.g. PORTA) + "PO_PCL", // Program counter Low register + "PO_PCLATH", // Program counter Latch high register + "PO_PCLATU", // Program counter Latch upper register + "PO_PRODL", // Product Register Low + "PO_PRODH", // Product Register High + "PO_LITERAL", // A constant + "PO_REL_ADDR", // A relative address + "PO_IMMEDIATE", // (8051 legacy) + "PO_DIR", // Direct memory (8051 legacy) + "PO_CRY", // bit memory (8051 legacy) + "PO_BIT", // bit operand. + "PO_STR", // (8051 legacy) + "PO_LABEL", + "PO_WILD", // Wild card operand in peep optimizer + "PO_TWO_OPS" // combine two operands +}; + + +const char *dumpPicOptype(PIC_OPTYPE type) +{ + assert( type >= 0 && type < sizeof(pic_optype_names)/sizeof( char *) ); + return (pic_optype_names[ type ]); +} + + +/*** BEGIN of stuff belonging to the BANKSEL optimization ***/ +#include "graph.h" + +#define MAX_COMMON_BANK_SIZE 32 +#define FIRST_PSEUDO_BANK_NR 1000 + +hTab *sym2bank = NULL; // <OPERAND NAME> --> <PSEUDO BANK NR> +hTab *bank2sym = NULL; // <PSEUDO BANK NR> --> <OPERAND NAME> +hTab *coerce = NULL; // <PSEUDO BANK NR> --> <&PSEUDOBANK> +Graph *adj = NULL; + +typedef enum { INVALID_BANK = -1, UNKNOWN_BANK = -2, FIXED_BANK = -3 } pseudoBankNr; + +typedef struct { + pseudoBankNr bank; // number assigned to this pseudoBank + unsigned int size; // number of operands assigned to this bank + unsigned int ref; // number of symbols referring to this pseudoBank (for garbage collection) +} pseudoBank; + +/*----------------------------------------------------------------------*/ +/* hashSymbol - hash function used to map SYMBOLs (or operands) to ints */ +/*----------------------------------------------------------------------*/ +unsigned int hashSymbol (const char *str) +{ + unsigned int res = 0; + if (!str) return 0; + + while (*str) { + res ^= (*str); + res = (res << 4) | (res >> (8 * sizeof(unsigned int) - 4)); + str++; + } // while + + return res; +} + +/*-----------------------------------------------------------------------*/ +/* compareSymbol - return 1 iff sym1 equals sym2 */ +/*-----------------------------------------------------------------------*/ +int compareSymbol (const void *sym1, const void *sym2) +{ + char *s1 = (char*) sym1; + char *s2 = (char*) sym2; + + return (strcmp (s1,s2) == 0); +} + +/*-----------------------------------------------------------------------*/ +/* comparePre - return 1 iff p1 == p2 */ +/*-----------------------------------------------------------------------*/ +int comparePtr (const void *p1, const void *p2) +{ + return (p1 == p2); +} + +/*----------------------------------------------------------*/ +/* getSymbolFromOperand - return a pointer to the symbol in */ +/* the given operand and its length */ +/*----------------------------------------------------------*/ +char *getSymbolFromOperand (char *op, int *len) +{ + char *sym, *curr; + *len = 0; + + if (!op) return NULL; + + // we recognize two forms of operands: SYMBOL and (SYMBOL + offset) + sym = op; + if (*sym == '(') sym++; + + curr = sym; + while (((*curr >= 'A') && (*curr <= 'Z')) + || ((*curr >= 'a') && (*curr <= 'z')) + || ((curr != sym) && (*curr >= '0') && (*curr <= '9')) + || (*curr == '_')) { + // find end of symbol [A-Za-z_]?[A-Za-z0-9]* + curr++; + (*len)++; + } // while + + return sym; +} + +/*--------------------------------------------------------------------------*/ +/* getSymFromBank - get (one) name of a symbol assigned to the given bank */ +/*--------------------------------------------------------------------------*/ +char *getSymFromBank (pseudoBankNr bank) +{ + assert (bank2sym); + + if (bank < 0) return "<INVALID BANK NR>"; + return hTabFindByKey (bank2sym, bank % bank2sym->size, (void *) bank, &comparePtr); +} + +/*-----------------------------------------------------------------------*/ +/* getPseudoBsrFromOperand - maps a string to its corresponding pseudo */ +/* bank number (uses hTab sym2bank), if the */ +/* symbol is not yet assigned a pseudo bank it */ +/* is assigned one here */ +/*-----------------------------------------------------------------------*/ +pseudoBankNr getPseudoBankNrFromOperand (const char *op) +{ + static pseudoBankNr next_bank = FIRST_PSEUDO_BANK_NR; + pseudoBankNr bank; + unsigned int hash; + + assert (sym2bank); + + hash = hashSymbol (op) % sym2bank->size; + bank = (pseudoBankNr) hTabFindByKey (sym2bank, hash, op, &compareSymbol); + if (bank == (pseudoBankNr)NULL) bank = UNKNOWN_BANK; + + if (bank == UNKNOWN_BANK) { + // create a pseudo bank for the operand + bank = next_bank++; + hTabAddItemLong (&sym2bank, hash, (char *)op, (void *)bank); + hTabAddItemLong (&bank2sym, bank, (void *) bank, (void *)op); + getOrAddGNode (adj, NULL, bank); // adds the node if it does not exist yet + //fprintf (stderr, "%s:%d: adding %s with hash %u in bank %u\n", __FUNCTION__, __LINE__, op, hash, bank); + } else { + //fprintf (stderr, "%s:%d: found %s with hash %u in bank %u\n", __FUNCTION__, __LINE__, op, hash, bank); + } // if + + assert (bank >= 0); + + return bank; +} + +/*--------------------------------------------------------------------*/ +/* isBanksel - check whether the given pCode is a BANKSEL instruction */ +/*--------------------------------------------------------------------*/ +int isBanksel (pCode *pc) +{ + if (!pc) return 0; + + if (isPCI(pc) && (PCI(pc)->op == POC_BANKSEL || PCI(pc)->op == POC_MOVLB)) { + // BANKSEL <variablename> or MOVLB <banknr> + //fprintf (stderr, "%s:%d: BANKSEL found: %s %s\n", __FUNCTION__, __LINE__, PCAD(pc)->directive, PCAD(pc)->arg); + return 1; + } + + // check for inline assembler BANKSELs + if (isPCAD(pc) && PCAD(pc)->directive && (STRCASECMP(PCAD(pc)->directive,"BANKSEL") == 0 || + STRCASECMP(PCAD(pc)->directive,"MOVLB") == 0)) { + //fprintf (stderr, "%s:%d: BANKSEL found: %s %s\n", __FUNCTION__, __LINE__, PCAD(pc)->directive, PCAD(pc)->arg); + return 1; + } + + // assume pc is no BANKSEL instruction + return 0; +} + +/*---------------------------------------------------------------------------------*/ +/* invalidatesBSR - check whether the pCodeInstruction passed in modifies the BSR */ +/* This method can not guarantee to find all modifications of the */ +/* BSR (e.g. via INDirection registers) but covers all compiler */ +/* generated plus some cases. */ +/*---------------------------------------------------------------------------------*/ +int invalidatesBSR(pCode *pc) +{ + // assembler directives invalidate BSR (well, they might, we don't know) + if (isPCAD(pc)) return 1; + + // only ASMDIRs and pCodeInstructions can invalidate BSR + if (!isPCI(pc)) return 0; + + // we have a pCodeInstruction + + // check for BSR modifying instructions + switch (PCI(pc)->op) { + case POC_CALL: + case POC_RCALL: + case POC_MOVLB: + case POC_RETFIE: // might be used as CALL replacement + case POC_RETLW: // might be used as CALL replacement + case POC_RETURN: // might be used as CALL replacement + case POC_BANKSEL: + return 1; + break; + + default: // other instruction do not change BSR unless BSR is an explicit operand! + // TODO: check for BSR as an explicit operand (e.g. INCF BSR,F), which should be rather unlikely...! + break; + } // switch + + // no change of BSR possible/probable + return 0; +} + +/*------------------------------------------------------------*/ +/* getBankFromBanksel - return the pseudo bank nr assigned to */ +/* the symbol referenced in this BANKSEL */ +/*------------------------------------------------------------*/ +pseudoBankNr getBankFromBanksel (pCode *pc) +{ + char *sym; + int data = 0; + + if (!pc) return INVALID_BANK; + + if (isPCAD(pc) && PCAD(pc)->directive) { + if (STRCASECMP(PCAD(pc)->directive,"BANKSEL") == 0) { + // get symbolname from PCAD(pc)->arg + //fprintf (stderr, "%s:%d: BANKSEL found: %s %s\n", __FUNCTION__, __LINE__, PCAD(pc)->directive, PCAD(pc)->arg); + sym = PCAD(pc)->arg; + data = getPseudoBankNrFromOperand (sym); + //fprintf (stderr, "symbol: %s, data=%i\n", sym, data); + } else if (STRCASECMP(PCAD(pc)->directive,"MOVLB")) { + // get (literal) bank number from PCAD(pc)->arg + fprintf (stderr, "%s:%d: MOVLB found: %s %s\n", __FUNCTION__, __LINE__, PCAD(pc)->directive, PCAD(pc)->arg); + assert (0 && "not yet implemented - turn off banksel optimization for now"); + } + } else if (isPCI(pc)) { + if (PCI(pc)->op == POC_BANKSEL) { + // get symbolname from PCI(pc)->pcop->name (?) + //fprintf (stderr, "%s:%d: BANKSEL found: %s %s\n", __FUNCTION__, __LINE__, PCI(pc)->mnemonic, PCI(pc)->pcop->name); + sym = PCI(pc)->pcop->name; + data = getPseudoBankNrFromOperand (sym); + //fprintf (stderr, "symbol: %s, data=%i\n", sym, data); + } else if (PCI(pc)->op == POC_MOVLB) { + // get (literal) bank number from PCI(pc)->pcop->name + fprintf (stderr, "%s:%d: MOVLB found: %s %s\n", __FUNCTION__, __LINE__, PCI(pc)->mnemonic, PCI(pc)->pcop->name); + assert (0 && "not yet implemented - turn off banksel optimization for now"); + } + } + + if (data == 0) + // no assigned bank could be found + return UNKNOWN_BANK; + else + return data; +} + +/*------------------------------------------------------------------------------*/ +/* getEffectiveBank - resolves the currently assigned effective pseudo bank nr */ +/*------------------------------------------------------------------------------*/ +pseudoBankNr getEffectiveBank (pseudoBankNr bank) +{ + pseudoBank *data; + + if (bank < FIRST_PSEUDO_BANK_NR) return bank; + + do { + //fprintf (stderr, "%s:%d: bank=%d\n", __FUNCTION__, __LINE__, bank); + data = (pseudoBank *) hTabFindByKey (coerce, bank % coerce->size, (void *) bank, &comparePtr); + if (data) { + if (data->bank != bank) + bank = data->bank; + else + data = NULL; + } + } while (data); + + //fprintf (stderr, "%s:%d: effective bank=%d\n", __FUNCTION__, __LINE__, bank); + return bank; +} + +/*------------------------------------------------------------------*/ +/* attachBsrInfo2pBlock - create a look-up table as to which pseudo */ +/* bank is selected at a given pCode */ +/*------------------------------------------------------------------*/ + +/* Create a graph with pseudo banks as its nodes and switches between + * these as edges (with the edge weight representing the absolute + * number of BANKSELs from one to the other). + * Removes redundand BANKSELs instead iff mod == 1. + * BANKSELs update the pseudo BSR, labels invalidate the current BSR + * value (setting it to 0=UNNKOWN), (R)CALLs also invalidate the + * pseudo BSR. + * TODO: check ALL instructions operands if they modify BSR directly... + * + * pb - the pBlock to annotate + * mod - select either graph creation (0) or BANKSEL removal (1) + */ +unsigned int attachBsrInfo2pBlock (pBlock *pb, int mod) +{ + pCode *pc, *pc_next; + unsigned int prevBSR = UNKNOWN_BANK, pseudoBSR = UNKNOWN_BANK; + int isBankselect = 0; + unsigned int banksels=0; + + if (!pb) return 0; + + pc = pic16_findNextInstruction(pb->pcHead); + while (pc) { + isBankselect = isBanksel (pc); + pc_next = pic16_findNextInstruction (pc->next); + + if (!hasNoLabel (pc)) { + // we don't know our predecessors -- assume different BSRs + prevBSR = UNKNOWN_BANK; + pseudoBSR = UNKNOWN_BANK; + //fprintf (stderr, "invalidated by label at "); pc->print (stderr, pc); + } // if + + // check if this is a BANKSEL instruction + if (isBankselect) { + pseudoBSR = getEffectiveBank (getBankFromBanksel(pc)); + //fprintf (stderr, "BANKSEL via "); pc->print (stderr, pc); + if (mod) { + if (prevBSR == pseudoBSR && pseudoBSR >= 0) { + //fprintf (stderr, "removing redundant "); pc->print (stderr, pc); + if (1 || pic16_pcode_verbose) pic16_pCodeInsertAfter (pc->prev, pic16_newpCodeCharP("removed redundant BANKSEL")); + pic16_unlinkpCode (pc); + banksels++; + } + } else { + addGEdge2 (getOrAddGNode (adj, NULL, prevBSR), getOrAddGNode (adj, NULL, pseudoBSR), 1, 0); + banksels++; + } + } // if + + if (!isBankselect && invalidatesBSR(pc)) { + // check if this instruction invalidates the pseudoBSR + pseudoBSR = UNKNOWN_BANK; + //fprintf (stderr, "invalidated via "); pc->print (stderr, pc); + } // if + + prevBSR = pseudoBSR; + pc = pc_next; + } // while + + return banksels; +} + +/*------------------------------------------------------------------------------------*/ +/* assignToSameBank - returns 0 on success or an error code */ +/* 1 - common bank would be too large */ +/* 2 - assignment to fixed (absolute) bank not performed */ +/* */ +/* This functions assumes that unsplittable operands are already assigned to the same */ +/* bank (e.g. all objects being referenced as (SYMBOL + offset) must be in the same */ +/* bank so that we can make sure the bytes are laid out sequentially in memory) */ +/* TODO: Symbols with an abslute address must be handled specially! */ +/*------------------------------------------------------------------------------------*/ +int assignToSameBank (int bank0, int bank1, int doAbs, int force) +{ + int eff0, eff1, dummy; + pseudoBank *pbank0, *pbank1; + hashtItem *hitem; + + eff0 = getEffectiveBank (bank0); + eff1 = getEffectiveBank (bank1); + + //fprintf (stderr, "%s:%d: bank0=%d/%d, bank1=%d/%d, doAbs=%d\n", __FUNCTION__, __LINE__, bank0, eff0, bank1, eff1, doAbs); + + // nothing to do if already same bank + if (eff0 == eff1) return 0; + + if (!doAbs && (eff0 < FIRST_PSEUDO_BANK_NR || eff1 < FIRST_PSEUDO_BANK_NR)) + return 2; + + // ensure eff0 < eff1 + if (eff0 > eff1) { + // swap eff0 and eff1 + dummy = eff0; + eff0 = eff1; + eff1 = dummy; + dummy = bank0; + bank0 = bank1; + bank1 = dummy; + } // if + + // now assign bank eff1 to bank eff0 + pbank0 = (pseudoBank *) hTabFindByKey (coerce, eff0 % coerce->size, (void *)((char*)0+eff0), &comparePtr); + if (!pbank0) { + pbank0 = Safe_alloc(sizeof(pseudoBank)); + pbank0->bank = eff0; + pbank0->size = 1; + pbank0->ref = 1; + hTabAddItemLong (&coerce, eff0 % coerce->size, (void *)((char*)0+eff0), (void *) pbank0); + } // if + + pbank1 = NULL; + hitem = hTabSearch (coerce, eff1 % coerce->size); + while (hitem && hitem->pkey != (void *)((char*)0+eff1)) + hitem = hitem->next; + + if (hitem) pbank1 = (pseudoBank *) hitem->item; + +#if 0 + fprintf (stderr, "bank #%d/%d & bank #%d/%d --> bank #%d: %u (%s & %s)\n", bank0, eff0, bank1, eff1, + pbank0->bank, pbank0->size, + getSymFromBank (eff0), getSymFromBank (eff1)); +#endif + + if (pbank1) { + if (!force && (pbank0->size + pbank1->size > MAX_COMMON_BANK_SIZE)) { +#if 0 + fprintf (stderr, "bank #%d: %u, bank #%d: %u --> bank #%d': %u > %u (%s,%s)\n", + pbank0->bank, pbank0->size, pbank1->bank, pbank1->size, + pbank0->bank, pbank0->size + pbank1->size, MAX_COMMON_BANK_SIZE, + getSymFromBank (pbank0->bank), getSymFromBank (pbank1->bank)); +#endif + return 1; + } // if + pbank0->size += pbank1->size; + pbank1->ref--; + if (pbank1->ref == 0) Safe_free (pbank1); + } else { + pbank0->size++; + } // if + + if (hitem) + hitem->item = pbank0; + else + hTabAddItemLong (&coerce, eff1 % coerce->size, (void *)((char*)0+eff1), (void *) pbank0); + pbank0->ref++; + + //fprintf (stderr, "%s:%d: leaving.\n", __FUNCTION__, __LINE__); + + return 0; +} + +/*----------------------------------------------------------------*/ +/* mergeGraphNodes - combines two nodes into one and modifies all */ +/* edges to and from the nodes accordingly */ +/* This method needs complete backedges, i.e. if (A,B) is an edge */ +/* then also (B,A) must be an edge (possibly with weight 0). */ +/*----------------------------------------------------------------*/ +void mergeGraphNodes (GraphNode *node1, GraphNode *node2) +{ + GraphEdge *edge, *backedge, *nextedge; + GraphNode *node; + int backweight; + + assert (node1 && node2); + assert (node1 != node2); + + // add all edges starting at node2 to node1 + edge = node2->edge; + while (edge) { + nextedge = edge->next; + node = edge->node; + backedge = getGEdge (node, node2); + if (backedge) + backweight = backedge->weight; + else + backweight = 0; + // insert edges (node1,node) and (node,node1) + addGEdge2 (node1, node, edge->weight, backweight); + // remove edges (node, node2) and (node2, node) + remGEdge (node2, node); + remGEdge (node, node2); + edge = nextedge; + } // while + + // now node2 should not be referenced by any other GraphNode... + //remGNode (adj, node2->data, node2->hash); +} + +/*----------------------------------------------------------------*/ +/* showGraph - dump the current BANKSEL graph as a node/edge list */ +/*----------------------------------------------------------------*/ +void showGraph (Graph *g) +{ + GraphNode *node; + GraphEdge *edge; + pseudoBankNr bankNr; + pseudoBank *pbank; + unsigned int size; + + node = g->node; + while (node) { + edge = node->edge; + bankNr = getEffectiveBank (node->hash); + assert (bankNr >= 0); + pbank = (pseudoBank *) hTabFindByKey (coerce, bankNr % coerce->size, (void *) bankNr, &comparePtr); + if (pbank) { + bankNr = pbank->bank; + size = pbank->size; + } else { + size = 1; + } + + fprintf (stderr, "edges from %s (bank %u, size %u) to:\n", getSymFromBank (node->hash), bankNr, size); + + while (edge) { + if (edge->weight > 0) + fprintf (stderr, " %4u x %s\n", edge->weight, getSymFromBank (edge->node->hash)); + edge = edge->next; + } // while (edge) + node = node->next; + } // while (node) +} + +/*---------------------------------------------------------------*/ +/* pic16_OptimizeBanksel - remove redundant BANKSEL instructions */ +/*---------------------------------------------------------------*/ +void pic16_OptimizeBanksel () +{ + GraphNode *node, *node1, *node1next; + +#if 0 + // needed for more effective bank assignment (needs adjusted pic16_emit_usection()) + GraphEdge *edge, *backedge; + GraphEdge *max; + int maxWeight, weight, mergeMore, absMaxWeight; + pseudoBankNr curr0, curr1; +#endif + pseudoBank *pbank; + pseudoBankNr bankNr; + char *base_symbol0, *base_symbol1; + int len0, len1; + pBlock *pb; + set *set; + reg_info *reg; + unsigned int bankselsTotal = 0, bankselsRemoved = 0; + + //fprintf (stderr, "%s:%s:%d: entered.\n", __FILE__, __FUNCTION__, __LINE__); + + if (!the_pFile || !the_pFile->pbHead) return; + + adj = newGraph (NULL); + sym2bank = newHashTable ( 255 ); + bank2sym = newHashTable ( 255 ); + coerce = newHashTable ( 255 ); + + // create graph of BANKSEL relationships (node = operands, edge (A,B) iff BANKSEL B follows BANKSEL A) + for (pb = the_pFile->pbHead; pb; pb = pb->next) { + bankselsTotal += attachBsrInfo2pBlock (pb, 0); + } // for pb + +#if 1 + // assign symbols with absolute addresses to their respective bank nrs + set = pic16_fix_udata; + for (reg = setFirstItem (set); reg; reg = setNextItem (set)) { + bankNr = reg->address >> 8; + node = getOrAddGNode (adj, NULL, bankNr); + bankNr = (pseudoBankNr) getEffectiveBank (getPseudoBankNrFromOperand(reg->name)); + assignToSameBank (node->hash, bankNr, 1, 1); + + assert (bankNr >= 0); + pbank = (pseudoBank *) hTabFindByKey (coerce, bankNr % coerce->size, (void *) bankNr, &comparePtr); + if (!pbank) { + pbank = Safe_alloc(sizeof(pseudoBank)); + pbank->bank = reg->address >> 8; //FIXED_BANK; + pbank->size = 1; + pbank->ref = 1; + hTabAddItemLong (&coerce, bankNr % coerce->size, (void *) bankNr, pbank); + } else { + assert (pbank->bank == (reg->address >> 8)); + pbank->bank = reg->address >> 8; //FIXED_BANK; + pbank->size++; + } + //fprintf (stderr, "ABS: %s (%d bytes) at %x in bank %u\n", reg->name, reg->size, reg->address, bankNr); + } // for reg +#endif + +#if 1 + // assign operands referring to the same symbol (which is not given an absolute address) to the same bank + //fprintf (stderr, "assign operands with the same symbol to the same bank\n"); + node = adj->node; + while (node) { + if (node->hash < 0) { node = node->next; continue; } + base_symbol0 = getSymbolFromOperand (getSymFromBank (getEffectiveBank(node->hash)), &len0); + node1 = node->next; + while (node1) { + if (node1->hash < 0) { node1 = node1->next; continue; } + node1next = node1->next; + base_symbol1 = getSymbolFromOperand (getSymFromBank (getEffectiveBank (node1->hash)), &len1); + if (len0 == len1 && len0 > 0 && strncmp (base_symbol0, base_symbol1, len0) == 0) { + int res; + // TODO: check for symbols with absolute addresses -- these might be placed across bank boundaries! + //fprintf (stderr, "merging %s and %s\n", getSymFromBank (getEffectiveBank(node->hash)), getSymFromBank (getEffectiveBank(node1->hash))); + if (0 != (res = assignToSameBank (node->hash, node1->hash, 0, 1))) { + fprintf (stderr, "%s(%d) == %s(%d), res=%d\n", base_symbol0, len0, base_symbol1, len1, res); + assert (0 && "Could not assign a symbol to a bank!"); + } + mergeGraphNodes (node, node1); + /* + if (node->hash < node1->hash) + mergeGraphNodes (node, node1); + else + mergeGraphNodes (node1, node); // this removes node so node->next will fail... + */ + } // if + node1 = node1next; + } // while (node1) + node = node->next; + } // while (node) +#endif + +#if 0 + // >>> THIS ALSO NEEDS AN UPDATED pic16_emit_usection() TO REFLECT THE BANK ASSIGNMENTS <<< + // assign tightly coupled operands to the same (pseudo) bank + //fprintf (stderr, "assign tightly coupled operands to the same bank\n"); + mergeMore = 1; + absMaxWeight = 0; + while (mergeMore) { + node = adj->node; + max = NULL; + maxWeight = 0; + while (node) { + curr0 = getEffectiveBank (node->hash); + if (curr0 < 0) { node = node->next; continue; } + edge = node->edge; + while (edge) { + assert (edge->src == node); + backedge = getGEdge (edge->node, edge->src); + weight = edge->weight + (backedge ? backedge->weight : 0); + curr1 = getEffectiveBank (edge->node->hash); + if (curr1 < 0) { edge = edge->next; continue; } + + // merging is only useful if the items are not assigned to the same bank already... + if (curr0 != curr1 && weight > maxWeight) { + if (maxWeight > absMaxWeight) absMaxWeight = maxWeight; + maxWeight = weight; + max = edge; + } // if + edge = edge->next; + } // while + node = node->next; + } // while + + if (maxWeight > 0) { +#if 0 + fprintf (stderr, "%s:%d: merging (%4u) %d(%s) and %d(%s)\n", __FUNCTION__, __LINE__, maxWeight, + max->src->hash, getSymFromBank (max->src->hash), + max->node->hash, getSymFromBank (max->node->hash)); +#endif + + node = getGNode (adj, max->src->data, max->src->hash); + node1 = getGNode (adj, max->node->data, max->node->hash); + + if (0 == assignToSameBank (max->src->hash, max->node->hash, 0, 0)) { + if (max->src->hash < max->node->hash) + mergeGraphNodes (node, node1); + else + mergeGraphNodes (node1, node); + } else { + remGEdge (node, node1); + remGEdge (node1, node); + //mergeMore = 0; + } + + } else { + mergeMore = 0; + } + } // while +#endif + +#if 1 + // remove redundant BANKSELs + //fprintf (stderr, "removing redundant BANKSELs\n"); + for (pb = the_pFile->pbHead; pb; pb = pb->next) { + bankselsRemoved += attachBsrInfo2pBlock (pb, 1); + } // for pb +#endif + +#if 0 + fprintf (stderr, "display graph\n"); + showGraph (); +#endif + + deleteGraph (adj); + //fprintf (stderr, "%s:%s:%d: leaving, %u/%u BANKSELs removed...\n", __FILE__, __FUNCTION__, __LINE__, bankselsRemoved, bankselsTotal); +} + +/*** END of stuff belonging to the BANKSEL optimization ***/ + + + +/*** BEGIN of helpers for pCode dataflow optimizations ***/ + +typedef unsigned int symbol_t; +typedef unsigned int valnum_t; +//typedef unsigned int hash_t; + +#ifndef INT_TO_PTR +#define INT_TO_PTR(x) (((char *) 0) + (x)) +#endif + +#ifndef PTR_TO_INT +#define PTR_TO_INT(x) (((char *)(x)) - ((char *) 0)) +#endif + +static int pic16_regIsLocal (reg_info *r); +static int pic16_safepCodeRemove (pCode *pc, char *comment); + +/* statistics */ +static unsigned int pic16_df_removed_pcodes = 0; +static unsigned int pic16_df_saved_bytes = 0; +static unsigned int df_findall_sameflow = 0; +static unsigned int df_findall_otherflow = 0; +static unsigned int df_findall_in_vals = 0; + +static void pic16_df_stats () { + return; + if (pic16_debug_verbose || pic16_pcode_verbose) { + fprintf (stderr, "PIC16: dataflow analysis removed %u instructions (%u bytes)\n", pic16_df_removed_pcodes, pic16_df_saved_bytes); + fprintf (stderr, "findAll: same flow %u (%u in_vals), other flow %u\n", df_findall_sameflow, df_findall_in_vals, df_findall_otherflow); + //pic16_df_removed_pcodes = pic16_df_saved_bytes = 0; + } +} + +/* Remove a pCode iff possible: + * - previous pCode is no SKIP + * - pc has no label + * Returns 1 iff the pCode has been removed, 0 otherwise. */ +static int pic16_safepCodeUnlink (pCode *pc, char *comment) { + pCode *pcprev, *pcnext; + char buf[256], *total=NULL; + int len; + + if (!comment) comment = "=DF= pCode removed by pic16_safepCodeUnlink"; + + pcprev = pic16_findPrevInstruction (pc->prev); + pcnext = pic16_findNextInstruction (pc->next); + + /* move labels to next instruction (if possible) */ + if (PCI(pc)->label && !pcnext) return 0; + + /* if this is a SKIP with side-effects -- do not remove */ + /* XXX: might try to replace this one with the side-effect only version */ + if (isPCI_SKIP(pc) + && ((PCI(pc)->outCond & (PCC_REGISTER | PCC_W)) != 0)) + { + pCode *newpc; + switch (PCI(pc)->op) + { + case POC_INCFSZ: + case POC_INFSNZ: + newpc = pic16_newpCode(POC_INCF, pic16_pCodeOpCopy( PCI(pc)->pcop ) ); + pic16_pCodeReplace( pc, newpc ); + return 1; + break; + case POC_INCFSZW: + newpc = pic16_newpCode(POC_INCFW, pic16_pCodeOpCopy( PCI(pc)->pcop ) ); + pic16_pCodeReplace( pc, newpc ); + return 1; + break; + case POC_DECFSZ: + case POC_DCFSNZ: + newpc = pic16_newpCode(POC_INCF, pic16_pCodeOpCopy( PCI(pc)->pcop ) ); + pic16_pCodeReplace( pc, newpc ); + return 1; + break; + case POC_DECFSZW: + newpc = pic16_newpCode(POC_INCF, pic16_pCodeOpCopy( PCI(pc)->pcop ) ); + pic16_pCodeReplace( pc, newpc ); + return 1; + break; + default: + return 0; + } + return 0; + } + + /* if previous instruction is a skip -- do not remove */ + if (pcprev && isPCI_SKIP(pcprev)) { + if (!pic16_safepCodeUnlink (pcprev, "=DF= removed now unused SKIP")) { + /* preceeding SKIP could not be removed -- keep this instruction! */ + return 0; + } + } + + if (PCI(pc)->label) { + //fprintf (stderr, "%s: moving label(s)\n", __FUNCTION__); + //pc->print (stderr, pc); + PCI(pcnext)->label = pic16_pBranchAppend (PCI(pc)->label, PCI(pcnext)->label); + PCI(pc)->label = NULL; + } + + /* update statistics */ + pic16_df_removed_pcodes++; + if (isPCI(pc)) pic16_df_saved_bytes += PCI(pc)->isize; + + /* remove the pCode */ + pic16_pCode2str (buf, 256, pc); + //fprintf (stderr, "%s: removing pCode: %s\n", __FUNCTION__, buf); + if (0 || pic16_debug_verbose || pic16_pcode_verbose) { + len = strlen (buf) + strlen (comment) + 10; + total = (char *) Safe_malloc (len); + SNPRINTF (total, len, "%s: %s", comment, buf); + pic16_pCodeInsertAfter (pc, pic16_newpCodeCharP(total)); + Safe_free (total); + } + + /* actually unlink it from the pBlock -- also remove from to/from lists */ + pic16_pCodeUnlink (pc); + + /* remove the pCode -- release registers */ + pc->destruct (pc); + + /* report success */ + return 1; +} + + +/* ======================================================================== */ +/* === SYMBOL HANDLING ==================================================== */ +/* ======================================================================== */ + +static hTab *map_strToSym = NULL; /** (char *) --> symbol_t */ +static hTab *map_symToStr = NULL; /** symbol_t -> (char *) */ +static symbol_t nextSymbol = 0x2000; /** next symbol_t assigned to the next generated symbol */ + +/** Calculate a hash for a given string. + * If len == 0 the string is assumed to be NUL terminated. */ +static hash_t symbolHash (const char *str, unsigned int len) { + hash_t hash = 0; + if (!len) { + while (*str) { + hash = (hash << 2) ^ *str; + str++; + } // while + } else { + while (len--) { + hash = (hash << 2) ^ *str; + str++; + } + } + return hash; +} + +/** Return 1 iff strings v1 and v2 are identical. */ +static int symcmp (const void *v1, const void *v2) { + return !strcmp ((const char *) v1, (const char *) v2); +} + +/** Return 1 iff pointers v1 and v2 are identical. */ +static int ptrcmp (const void *v1, const void *v2) { + return (v1 == v2); +} + +enum { SPO_WREG=0x1000, + SPO_STATUS, + SPO_PRODL, + SPO_PRODH, + SPO_INDF0, + SPO_POSTDEC0, + SPO_POSTINC0, + SPO_PREINC0, + SPO_PLUSW0, + SPO_INDF1, + SPO_POSTDEC1, + SPO_POSTINC1, + SPO_PREINC1, + SPO_PLUSW1, + SPO_INDF2, + SPO_POSTDEC2, + SPO_POSTINC2, + SPO_PREINC2, + SPO_PLUSW2, + SPO_STKPTR, + SPO_TOSL, + SPO_TOSH, + SPO_TOSU, + SPO_BSR, + SPO_FSR0L, + SPO_FSR0H, + SPO_FSR1L, + SPO_FSR1H, + SPO_FSR2L, + SPO_FSR2H, + SPO_PCL, + SPO_PCLATH, + SPO_PCLATU, + SPO_TABLAT, + SPO_TBLPTRL, + SPO_TBLPTRH, + SPO_TBLPTRU, + SPO_LAST +}; + +/* Return the unique symbol_t for the given string. */ +static symbol_t symFromStr (const char *str) { + hash_t hash; + char *res; + symbol_t sym; + + if (!map_symToStr) { + int i; + struct { char *name; symbol_t sym; } predefsyms[] = { + {"WREG", SPO_WREG}, + {"STATUS", SPO_STATUS}, + {"PRODL", SPO_PRODL}, + {"PRODH", SPO_PRODH}, + {"INDF0", SPO_INDF0}, + {"POSTDEC0", SPO_POSTDEC0}, + {"POSTINC0", SPO_POSTINC0}, + {"PREINC0", SPO_PREINC0}, + {"PLUSW0", SPO_PLUSW0}, + {"INDF1", SPO_INDF1}, + {"POSTDEC1", SPO_POSTDEC1}, + {"POSTINC1", SPO_POSTINC1}, + {"PREINC1", SPO_PREINC1}, + {"PLUSW1", SPO_PLUSW1}, + {"INDF2", SPO_INDF2}, + {"POSTDEC2", SPO_POSTDEC2}, + {"POSTINC2", SPO_POSTINC2}, + {"PREINC2", SPO_PREINC2}, + {"PLUSW2", SPO_PLUSW2}, + {"STKPTR", SPO_STKPTR}, + {"TOSL", SPO_TOSL}, + {"TOSH", SPO_TOSH}, + {"TOSU", SPO_TOSU}, + {"BSR", SPO_BSR}, + {"FSR0L", SPO_FSR0L}, + {"FSR0H", SPO_FSR0H}, + {"FSR1L", SPO_FSR1L}, + {"FSR1H", SPO_FSR1H}, + {"FSR2L", SPO_FSR2L}, + {"FSR2H", SPO_FSR2H}, + {"PCL", SPO_PCL}, + {"PCLATH", SPO_PCLATH}, + {"PCLATU", SPO_PCLATU}, + {"TABLAT", SPO_TABLAT}, + {"TBLPTRL", SPO_TBLPTRL}, + {"TBLPTRH", SPO_TBLPTRH}, + {"TBLPTRU", SPO_TBLPTRU}, + {NULL, 0} + }; + + map_strToSym = newHashTable (128); + map_symToStr = newHashTable (128); + + for (i=0; predefsyms[i].name; i++) { + char *name; + + /* enter new symbol */ + sym = predefsyms[i].sym; + name = predefsyms[i].name; + res = Safe_strdup (name); + hash = symbolHash (name, 0); + + hTabAddItemLong (&map_strToSym, hash, res, INT_TO_PTR(sym)); + hTabAddItemLong (&map_symToStr, sym % map_symToStr->size, INT_TO_PTR(sym), res); + } // for i + } + + hash = symbolHash (str, 0) % map_strToSym->size; + + /* find symbol in table */ + sym = PTR_TO_INT(hTabFindByKey (map_strToSym, hash, str, &symcmp)); + if (sym) { + //fprintf (stderr, "found symbol %x for %s\n", sym, str); + return sym; + } + + /* enter new symbol */ + sym = nextSymbol++; + res = Safe_strdup (str); + + hTabAddItemLong (&map_strToSym, hash, res, INT_TO_PTR(sym)); + hTabAddItemLong (&map_symToStr, sym % map_symToStr->size, INT_TO_PTR(sym), res); + + //fprintf (stderr, "created symbol %x for %s\n", sym, res); + + return sym; +} + +#if 1 +static const char *strFromSym (symbol_t sym) { + return (const char *) hTabFindByKey (map_symToStr, sym % map_symToStr->size, INT_TO_PTR(sym), &ptrcmp); +} +#endif + +/* ======================================================================== */ +/* === DEFINITION MAP HANDLING ============================================ */ +/* ======================================================================== */ + +/* A defmap provides information about which symbol is defined by which pCode. + * The most recent definitions are prepended to the list, so that the most + * recent definition can be found by forward scanning the list. + * pc2: MOVFF r0x00, r0x01 + * pc1: INCF r0x01 + * head --> ("r0x01",pc1,42) --> ("STATUS",pc1,44) --> ("r0x01",pc2,28) --> NULL + * + * We attach one defmap to each flow object, and each pCode will occur at + * least once in its flow's defmap (maybe defining the 0 symbol). This can be + * used to find definitions for a pCode in its own defmap that precede pCode. + */ + +typedef struct defmap_s { + symbol_t sym; /** symbol this item refers to */ + union { + struct { + unsigned int in_mask:8; /** mask leaving in accessed bits */ + unsigned int mask:8; /** mask leaving in modified bits (if isWrite) */ + int isRead:1; /** sym/mask is read */ + int isWrite:1; /** sym/mask is written */ + } access; + int accessmethod; + } acc; + pCode *pc; /** pCode this symbol is refrenced at */ + valnum_t in_val; /** valnum_t of symbol's previous value (the one read at pc) */ + valnum_t val; /** new unique number for this value (if isWrite) */ + struct defmap_s *prev, *next; /** link to previous an next definition */ +} defmap_t; + +static defmap_t *defmap_free = NULL; /** list of unused defmaps */ +static int defmap_free_count = 0; /** number of released defmap items */ + +/* Returns a defmap_t with the specified data; this will be the new list head. + * next - pointer to the current list head */ +static defmap_t *newDefmap (symbol_t sym, int in_mask, int mask, int isRead, int isWrite, pCode *pc, valnum_t val, defmap_t *next) { + defmap_t *map; + + if (defmap_free) { + map = defmap_free; + defmap_free = map->next; + --defmap_free_count; + } else { + map = (defmap_t *)Safe_alloc(sizeof(defmap_t)); + } + map->sym = sym; + map->acc.access.in_mask = (isRead ? (in_mask ? in_mask : 0xFF) : 0x00); + map->acc.access.mask = (isWrite ? (mask ? mask : 0xFF) : 0x00); + map->acc.access.isRead = (isRead != 0); + map->acc.access.isWrite = (isWrite != 0); + map->pc = pc; + map->in_val = 0; + map->val = (isWrite ? val : 0); + map->prev = NULL; + map->next = next; + if (next) next->prev = map; + + return map; +} + +/* Returns a copy of the single defmap item. */ +static defmap_t *copyDefmap (defmap_t *map) { + defmap_t *res = (defmap_t *) Safe_malloc (sizeof (defmap_t)); + memcpy (res, map, sizeof (defmap_t)); + res->next = NULL; + res->prev = NULL; + return res; +} + +/* Insert a defmap item after the specified one. */ +static int defmapInsertAfter (defmap_t *ref, defmap_t *newItem) { + if (!ref || !newItem) return 1; + + newItem->next = ref->next; + newItem->prev = ref; + ref->next = newItem; + if (newItem->next) newItem->next->prev = newItem; + + return 0; +} + +/* Check whether item (or an identical one) is already in the chain and add it if neccessary. + * item is copied before insertion into chain and therefore left untouched. + * Returns 1 iff the item has been inserted into the list, 0 otherwise. */ +static int defmapAddCopyIfNew (defmap_t **head, defmap_t *item) { + defmap_t *dummy; + dummy = *head; + while (dummy && (dummy->sym != item->sym + || dummy->pc != item->pc + || dummy->acc.accessmethod != item->acc.accessmethod + || dummy->val != item->val + || dummy->in_val != item->in_val)) { + dummy = dummy->next; + } // while + + /* item already present? */ + if (dummy) return 0; + + /* otherwise: insert copy of item */ + dummy = copyDefmap (item); + dummy->next = *head; + if (*head) (*head)->prev = dummy; + *head = dummy; + + return 1; +} + +/* Releases a defmap. This also removes the map from its chain -- update the head manually! */ +static void deleteDefmap (defmap_t *map) { + if (!map) return; + + /* unlink from chain -- fails for the first item (head is not updated!) */ + if (map->next) map->next->prev = map->prev; + if (map->prev) map->prev->next = map->next; + + /* clear map */ + memset (map, 0, sizeof (defmap_t)); + + /* save for future use */ + map->next = defmap_free; + defmap_free = map; + ++defmap_free_count; +} + +/* Release all defmaps referenced from map. */ +static void deleteDefmapChain (defmap_t **_map) { + defmap_t *map, *next; + + if (!_map) return; + + map = *_map; + + /* find list head */ + while (map && map->prev) map = map->prev; + + /* delete all items */ + while (map) { + next = map->next; + deleteDefmap (map); + map = next; + } // while + + *_map = NULL; +} + +/* Free all defmap items. */ +static void freeDefmap (defmap_t **_map) { + defmap_t *next; + defmap_t *map; + + if (!_map) return; + + map = (*_map); + + /* find list head */ + while (map->prev) map = map->prev; + + /* release all items */ + while (map) { + next = map->next; + Safe_free (map); + map = next; + } + + (*_map) = NULL; +} + +/* Returns the most recent definition for the given symbol preceeding pc. + * If no definition is found, NULL is returned. + * If pc == NULL the whole list is scanned. */ +static defmap_t *defmapFindDef (defmap_t *map, symbol_t sym, pCode *pc) { + defmap_t *curr = map; + + if (pc) { + /* skip all definitions up to pc */ + while (curr && (curr->pc != pc)) curr = curr->next; + + /* pc not in the list -- scan the whole list for definitions */ + if (!curr) { + fprintf (stderr, "pc %p not found in defmap -- scanning whole list for symbol '%s'\n", pc, strFromSym (sym)); + curr = map; + } else { + /* skip all definitions performed by pc */ + while (curr && (curr->pc == pc)) curr = curr->next; + } + } // if (pc) + + /* find definition for sym */ + while (curr && (!curr->acc.access.isWrite || (curr->sym != sym))) { + curr = curr->next; + } + + return curr; +} + +#if 0 +/* Returns the first use (read) of the given symbol AFTER pc. + * If no such use is found, NULL is returned. + * If pc == NULL the whole list is scanned. */ +static defmap_t *defmapFindUse (defmap_t *map, symbol_t sym, pCode *pc) { + defmap_t *curr = map, *prev = NULL; + + if (pc) { + /* skip all definitions up to pc */ + while (curr && (curr->pc != pc)) { prev = curr; curr = curr->next; } + + /* pc not in the list -- scan the whole list for definitions */ + if (!curr) { + //fprintf (stderr, "pc %p not found in defmap -- scanning whole list for symbol '%s'\n", pc, strFromSym (sym)); + curr = prev; + } + } else { + /* find end of list */ + while (curr && curr->next) curr = curr->next; + } // if (pc) + + /* find use of sym (scan list backwards) */ + while (curr && (!curr->acc.access.isRead || (curr->sym != sym))) curr = curr->prev; + + return curr; +} +#endif + +/* Return the defmap entry for sym AT pc. + * If none is found, NULL is returned. + * If more than one entry is found an assertion is triggered. */ +static defmap_t *defmapCurr (defmap_t *map, symbol_t sym, pCode *pc) { + defmap_t *res = NULL; + + /* find entries for pc */ + while (map && map->pc != pc) map = map->next; + + /* find first entry for sym @ pc */ + while (map && map->pc == pc && map->sym != sym) map = map->next; + + /* no entry found */ + if (!map) return NULL; + + /* check for more entries */ + res = map; + map = map->next; + while (map && map->pc == pc) { + /* more than one entry for sym @ pc found? */ + assert (map->sym != sym); + map = map->next; + } + + /* return single entry for sym @ pc */ + return res; +} + +/* Modifies the definition of sym at pCode to newval. + * Returns 0 on success, 1 if no definition of sym in pc has been found. + */ +static int defmapUpdate (defmap_t *map, symbol_t sym, pCode *pc, valnum_t newval) { + defmap_t *m = map; + + /* find definitions of pc */ + while (m && m->pc != pc) m = m->next; + + /* find definition of sym at pc */ + while (m && m->pc == pc && (!m->acc.access.isWrite || (m->sym != sym))) m = m->next; + + /* no definition found */ + if (!m) return 1; + + /* redefine */ + m->val = newval; + + /* update following uses of sym */ + while (m && m->pc == pc) m = m->prev; + while (m) { + if (m->sym == sym) { + m->in_val = newval; + if (m->acc.access.isWrite) m = NULL; + } // if + if (m) m = m->prev; + } // while + + return 0; +} + +/* ======================================================================== */ +/* === STACK ROUTINES ===================================================== */ +/* ======================================================================== */ + +typedef struct stack_s { + void *data; + struct stack_s *next; +} stackitem_t; + +typedef stackitem_t *dynstack_t; +static stackitem_t *free_stackitems = NULL; + +/* Create a stack with one item. */ +static dynstack_t *newStack () { + dynstack_t *s = (dynstack_t *) Safe_malloc (sizeof (dynstack_t)); + *s = NULL; + return s; +} + +/* Remove a stack -- its items are only marked free. */ +static void deleteStack (dynstack_t *s) { + stackitem_t *i; + + while (*s) { + i = *s; + *s = (*s)->next; + i->next = free_stackitems; + free_stackitems = i; + } // while + Safe_free (s); +} + +/* Release all stackitems. */ +static void releaseStack () { + stackitem_t *i; + + while (free_stackitems) { + i = free_stackitems->next; + Safe_free(free_stackitems); + free_stackitems = i; + } // while +} + +static void stackPush (dynstack_t *stack, void *data) { + stackitem_t *i; + + if (free_stackitems) { + i = free_stackitems; + free_stackitems = free_stackitems->next; + } else { + i = (stackitem_t *)Safe_alloc(sizeof(stackitem_t)); + } + i->data = data; + i->next = *stack; + *stack = i; +} + +static void *stackPop (dynstack_t *stack) { + void *data; + stackitem_t *i; + + if (stack && *stack) { + data = (*stack)->data; + i = *stack; + *stack = (*stack)->next; + i->next = free_stackitems; + free_stackitems = i; + return data; + } else { + return NULL; + } +} + +#if 0 +static int stackContains (dynstack_t *s, void *data) { + stackitem_t *i; + if (!s) return 0; + i = *s; + while (i) { + if (i->data == data) return 1; + i = i->next; + } // while + + /* not found */ + return 0; +} +#endif + +static int stackIsEmpty (dynstack_t *s) { + return (*s == NULL); +} + + +typedef struct { + pCodeFlow *flow; + defmap_t *lastdef; +} state_t; + +static state_t *newState (pCodeFlow *flow, defmap_t *lastdef) { + state_t *s = (state_t *)Safe_alloc(sizeof(state_t)); + s->flow = flow; + s->lastdef = lastdef; + return s; +} + +static void deleteState (state_t *s) { + Safe_free (s); +} + +static int stateIsNew (state_t *state, dynstack_t *todo, dynstack_t *done) { + stackitem_t *i; + + /* scan working list for state */ + if (todo) { + i = *todo; + while (i) { + /* is i == state? -- state not new */ + if ((((state_t *) (i->data))->flow == state->flow) && (((state_t *) (i->data))->lastdef == state->lastdef)) return 0; + i = i->next; + } // while + } + + if (done) { + i = *done; + while (i) { + /* is i == state? -- state not new */ + if ((((state_t *) (i->data))->flow == state->flow) && (((state_t *) (i->data))->lastdef == state->lastdef)) return 0; + i = i->next; + } // while + } + + /* not found -- state is new */ + return 1; +} + +static inline valnum_t newValnum (); + +const char *pic16_pBlockGetFunctionName (pBlock *pb) { + pCode *pc; + + if (!pb) return "<unknown function>"; + + pc = pic16_findNextpCode (pb->pcHead, PC_FUNCTION); + if (pc && isPCF(pc)) return PCF(pc)->fname; + else return "<unknown function>"; +} + +static defmap_t *pic16_pBlockAddInval (pBlock *pb, symbol_t sym) { + defmap_t *map; + pCodeFlow *pcfl; + + assert(pb); + + pcfl = PCI(pic16_findNextInstruction (pb->pcHead))->pcflow; + + /* find initial value (assigning pc == NULL) */ + map = PCFL(pcfl)->in_vals; + while (map && map->sym != sym) map = map->next; + + /* initial value already present? */ + if (map) { + //fprintf (stderr, "found init value for sym %s (%x): %u\n", strFromSym(sym), sym, map->val); + return map; + } + + /* create a new initial value */ + map = newDefmap (sym, 0x00, 0xff, 0, 1, NULL, newValnum(), PCFL(pcfl)->in_vals); + PCFL(pcfl)->in_vals = map; + //fprintf (stderr, "Created init value for sym %s (%x): %u\n", strFromSym(sym), sym, map->val); + return map; + +#if 0 + /* insert map as last item in pcfl's defmap */ + if (!prev) prev = PCFL(pcfl)->defmap; + if (!prev) { + PCFL(pcfl)->defmap = map; + } else { + while (prev->next) prev = prev->next; + prev->next = map; + map->prev = prev; + } + + return map; +#endif +} + +/* Find all reaching definitions for sym at pc. + * A new (!) list of definitions is returned. + * Returns the number of reaching definitions found. + * The defining defmap entries are returned in *chain. + */ +static int defmapFindAll (symbol_t sym, pCode *pc, defmap_t **chain) { + defmap_t *map; + defmap_t *res; + + pCodeFlow *curr; + pCodeFlowLink *succ; + state_t *state; + dynstack_t *todo; /** stack of state_t */ + dynstack_t *done; /** stack of state_t */ + + int n_defs; + + assert (pc && isPCI(pc) && PCI(pc)->pcflow); + assert (chain); + + /* initialize return list */ + *chain = NULL; + + /* wildcard symbol? */ + if (!sym) return 0; + + //fprintf (stderr, "Searching definition of sym %s(%x) @ pc %p(%p)\n", strFromSym(sym), sym, pc, pc->pb); + + map = PCI(pc)->pcflow->defmap; + + res = defmapFindDef (map, sym, pc); + //if (res) fprintf (stderr, "found def in own flow @ pc %p\n", res->pc); + +#define USE_PRECALCED_INVALS 1 +#if USE_PRECALCED_INVALS + if (!res && PCI(pc)->pcflow->in_vals) { + res = defmapFindDef (PCI(pc)->pcflow->in_vals, sym, NULL); + if (res) { + //fprintf (stderr, "found def in init values\n"); + df_findall_in_vals++; + } + } +#endif + + if (res) { + // found a single definition (in pc's flow) + //fprintf (stderr, "unique definition for %s @ %p found @ %p (val: %x)\n", strFromSym(sym), pc, res->pc, res->val); + defmapAddCopyIfNew (chain, res); + df_findall_sameflow++; + return 1; + } + +#if USE_PRECALCED_INVALS + else { + defmapAddCopyIfNew (chain, pic16_pBlockAddInval (pc->pb, sym)); + return 1; + } + +#endif + +#define FORWARD_FLOW_ANALYSIS 1 +#if defined FORWARD_FLOW_ANALYSIS && FORWARD_FLOW_ANALYSIS + /* no definition found in pc's flow preceeding pc */ + todo = newStack (); + done = newStack (); + n_defs = 0; + stackPush (todo, newState (PCI(pic16_findNextInstruction(pc->pb->pcHead))->pcflow, res)); + + while (!stackIsEmpty (todo)) { + state = (state_t *) stackPop (todo); + stackPush (done, state); + curr = state->flow; + res = state->lastdef; + //fprintf (stderr, "searching def of sym %s in pcFlow %p (lastdef %x @ %p)\n", strFromSym(sym), curr, res ? res->val : 0, res ? res->pc : NULL); + + /* there are no definitions BEFORE pc in pc's flow (see above) */ + if (curr == PCI(pc)->pcflow) { + if (!res) { + //fprintf (stderr, "symbol %s(%x) might be used uninitialized at %p\n", strFromSym(sym), sym, pc); + res = pic16_pBlockAddInval (pc->pb, sym); + if (defmapAddCopyIfNew (chain, res)) n_defs++; + res = NULL; + } else { + //fprintf (stderr, "reaching definition for %s @ %p found @ %p (val: %x)\n", strFromSym(sym), pc, res->pc, res->val); + if (defmapAddCopyIfNew (chain, res)) n_defs++; + } + } + + /* save last definition of sym in this flow as initial def in successors */ + res = defmapFindDef (curr->defmap, sym, NULL); + if (!res) res = state->lastdef; + + /* add successors to working list */ + state = newState (NULL, NULL); + succ = (pCodeFlowLink *) setFirstItem (curr->to); + while (succ) { + //fprintf (stderr, " %p --> %p with %x\n", curr, succ->pcflow, res ? res->val : 0); + state->flow = succ->pcflow; + state->lastdef = res; + if (stateIsNew (state, todo, done)) { + stackPush (todo, state); + state = newState (NULL, NULL); + } // if + succ = (pCodeFlowLink *) setNextItem (curr->to); + } // while + deleteState (state); + } // while + +#else // !FORWARD_FLOW_ANALYSIS + + { + int firstState = 1; + + /* no definition found in pc's flow preceeding pc */ + todo = newStack (); + done = newStack (); + n_defs = 0; + stackPush (todo, newState (PCI(pc)->pcflow, res)); + + while (!stackIsEmpty (todo)) { + state = (state_t *) stackPop (todo); + curr = state->flow; + + if (firstState) { + firstState = 0; + /* only check predecessor flows */ + } else { + /* get (last) definition of sym in this flow */ + res = defmapFindDef (curr->defmap, sym, NULL); + } + + if (res) { + /* definition found */ + //fprintf (stderr, "reaching definition for %s @ %p found @ %p (val: %x)\n", strFromSym(sym), pc, res->pc, res->val); + if (defmapAddCopyIfNew (chain, res)) n_defs++; + } else { + /* no definition found -- check predecessor flows */ + state = newState (NULL, NULL); + succ = (pCodeFlowLink *) setFirstItem (curr->from); + + /* if no flow predecessor available -- sym might be uninitialized */ + if (!succ) { + //fprintf (stder, "sym %s might be used uninitialized at %p\n", strFromSym (sym), pc); + res = newDefmap (sym, 0xff, 0, 1, NULL, 0, *chain); + if (defmapAddCopyIfNew (chain, res)) n_defs++; + deleteDefmap (res); res = NULL; + } + + while (succ) { + //fprintf (stderr, " %p --> %p with %x\n", curr, succ->pcflow, res ? res->val : 0); + state->flow = succ->pcflow; + state->lastdef = res; + if (stateIsNew (state, todo, done)) { + stackPush (todo, state); + state = newState (NULL, NULL); + } // if + succ = (pCodeFlowLink *) setNextItem (curr->from); + } // while + deleteState (state); + } + } // while + } + +#endif + + /* clean up done stack */ + while (!stackIsEmpty(done)) { + deleteState ((state_t *) stackPop (done)); + } // while + deleteStack (done); + + /* return number of items in result set */ + if (n_defs == 0) { + //fprintf (stderr, "sym %s might be used uninitialized at %p\n", strFromSym (sym), pc); + } else if (n_defs == 1) { + assert (*chain); + //fprintf (stderr, "sym %s at %p always defined as %x @ %p\n", strFromSym(sym), pc, (*chain)->val, (*chain)->pc); + } else if (n_defs > 0) { + //fprintf (stderr, "%u definitions for sym %s at %p found:\n", n_defs, strFromSym(sym), pc); +#if 0 + res = *chain; + while (res) { + fprintf (stderr, " as %4x @ %p\n", res->val, res->pc); + res = res->next; + } // while +#endif + } + //fprintf (stderr, "%u definitions for sym %s at %p found\n", n_defs, strFromSym(sym), pc); + df_findall_otherflow++; + return n_defs; +} + +/* ======================================================================== */ +/* === VALUE NUMBER HANDLING ============================================== */ +/* ======================================================================== */ + +static valnum_t nextValnum = 0x1000; +static hTab *map_symToValnum = NULL; + +/** Return a new value number. */ +static inline valnum_t newValnum () { + return (nextValnum += 4); +} + +static valnum_t valnumFromStr (const char *str) { + symbol_t sym; + valnum_t val; + void *res; + + sym = symFromStr (str); + + if (!map_symToValnum) { + map_symToValnum = newHashTable (128); + } // if + + /* literal already known? */ + res = hTabFindByKey (map_symToValnum, sym % map_symToValnum->size, INT_TO_PTR(sym), &ptrcmp); + + /* return existing valnum */ + if (res) return (valnum_t) PTR_TO_INT(res); + + /* create new valnum */ + val = newValnum(); + hTabAddItemLong (&map_symToValnum, sym % map_symToValnum->size, INT_TO_PTR(sym), INT_TO_PTR(val)); + //fprintf (stderr, "NEW VALNUM %x for symbol %s\n", val, str); + return val; +} + +/* Create a valnum for a literal. */ +static valnum_t valnumFromLit (unsigned int lit) { + return ((valnum_t) 0x100 + (lit & 0x0FF)); +} + +/* Return the (positive) literal value represented by val + * or -1 iff val is no known literal's valnum. */ +static int litFromValnum (valnum_t val) { + if (val >= 0x100 && val < 0x200) { + /* valnum is a (known) literal */ + return val & 0x00FF; + } else { + /* valnum is not a known literal */ + return -1; + } +} + +#if 0 +/* Sanity check - all flows in a block must be reachable from initial flow. */ +static int verifyAllFlowsReachable (pBlock *pb) { + set *reached; + set *flowInBlock; + set *checked; + pCode *pc; + pCodeFlow *pcfl; + pCodeFlowLink *succ; + int res; + + //fprintf (stderr, "%s - started for %s.\n" ,__FUNCTION__, pic16_pBlockGetFunctionName (pb)); + + reached = NULL; + flowInBlock = NULL; + checked = NULL; + /* mark initial flow as reached (and "not needs to be reached") */ + pc = pic16_findNextpCode (pb->pcHead, PC_FLOW); + assert (pc); + addSetHead (&reached, pc); + addSetHead (&checked, pc); + + /* mark all further flows in block as "need to be reached" */ + pc = pb->pcHead; + do { + if (isPCI(pc)) addSetIfnotP (&flowInBlock, PCI(pc)->pcflow); + pc = pic16_findNextInstruction (pc->next); + } while (pc); + + while (reached && (pcfl = (pCodeFlow *)indexSet (reached, 0)) != NULL) { + /* mark as reached and "not need to be reached" */ + deleteSetItem (&reached, pcfl); + //fprintf (stderr, "%s - checking %p\n" ,__FUNCTION__, pcfl); + + /* flow is no longer considered unreachable */ + deleteSetItem (&flowInBlock, pcfl); + + for (succ = setFirstItem (pcfl->to); succ; succ = setNextItem (pcfl->to)) { + if (!isinSet (checked, succ->pcflow)) { + /* flow has never been reached before */ + addSetHead (&reached, succ->pcflow); + addSetHead (&checked, succ->pcflow); + } // if + } // for succ + } // while + + //fprintf (stderr, "%s - finished\n", __FUNCTION__); + + /* by now every flow should have been reached + * --> flowInBlock should be empty */ + res = (flowInBlock == NULL); + +#if 1 + if (flowInBlock) { + fprintf (stderr, "not all flows reached in %s:\n", pic16_pBlockGetFunctionName (pb)); + while (flowInBlock) { + pcfl = indexSet (flowInBlock, 0); + fprintf (stderr, "not reached: flow %p\n", pcfl); + deleteSetItem (&flowInBlock, pcfl); + } // while + } +#endif + + /* clean up */ + deleteSet (&reached); + deleteSet (&flowInBlock); + deleteSet (&checked); + + /* if we reached every flow, succ is NULL by now... */ + //assert (res); // will fire on unreachable code... + return (res); +} +#endif + +/* Checks a flow for accesses to sym AFTER pc. + * + * Returns -1 if the symbol is read in this flow (before redefinition), + * returns 0 if the symbol is redefined in this flow or + * returns a mask [0x01 -- 0xFF] indicating the bits still alive after this flow. + */ +int pic16_isAliveInFlow (symbol_t sym, int mask, pCodeFlow *pcfl, pCode *pc) { + defmap_t *map, *mappc; + + /* find pc or start of definitions */ + map = pcfl->defmap; + while (map && (map->pc != pc) && map->next) map = map->next; + /* if we found pc -- ignore it */ + while (map && map->pc == pc) map = map->prev; + + /* scan list backwards (first definition first) */ + while (map && mask) { +// if (map->sym == sym) { + //fprintf (stderr, "%s: accessing sym %s in pc %p/map %p\n", __FUNCTION__, strFromSym(sym), map->pc, map); + mappc = map; + /* scan list for reads at this pc first */ + while (map && map->pc == mappc->pc) { + /* is the symbol (partially) read? */ + if ((map->sym == sym) && (map->acc.access.isRead && ((map->acc.access.in_mask & mask) != 0))) { + //if (sym != SPO_STATUS) fprintf (stderr, "%s: symbol %s read at pc %p\n", __FUNCTION__, strFromSym (sym), map->pc); + return -1; + } + map = map->prev; + } // while + map = mappc; + + while (map && map->pc == mappc->pc) { + /* honor (partial) redefinitions of sym */ + if ((map->sym == sym) && (map->acc.access.isWrite)) { + mask &= ~map->acc.access.mask; + //if (sym != SPO_STATUS) fprintf (stderr, "%s: symbol %s redefined at pc %p, alive mask: %x\n", __FUNCTION__, strFromSym (sym), map->pc, mask); + } + map = map->prev; + } // while +// } // if + /* map already points to the first defmap for the next pCode */ + //map = mappc->prev; + } // while + + /* the symbol is not completely redefined in this flow and not accessed -- symbol + * is still alive; return the appropriate mask of alive bits */ + return mask; +} + +/* Check whether a symbol is alive (AFTER pc). */ +static int pic16_isAlive (symbol_t sym, pCode *pc) { + int mask, visit; + dynstack_t *todo, *done; + state_t *state; + pCodeFlow *pcfl; + pCodeFlowLink *succ; + + mask = 0x00ff; + + assert (isPCI(pc)); + pcfl = PCI(pc)->pcflow; + + todo = newStack (); + done = newStack (); + + state = newState (pcfl, (defmap_t *) INT_TO_PTR(mask)); + stackPush (todo, state); + visit = 0; + + while (!stackIsEmpty (todo)) { + state = (state_t *) stackPop (todo); + pcfl = state->flow; + mask = PTR_TO_INT(state->lastdef); + if (visit) stackPush (done, state); else deleteState(state); + //fprintf (stderr, "%s: checking flow %p for symbol %s (%x)/%x\n", __FUNCTION__, pcfl, strFromSym(sym), sym, mask); + // make sure flows like A(i1,i2,pc,i3,...) --> A with pc reading and writing sym are handled correctly! + mask = pic16_isAliveInFlow (sym, mask, pcfl, visit == 0 ? pc : NULL); + visit++; + + /* symbol is redefined in flow before use -- not alive in this flow (maybe in others?) */ + if (mask == 0) continue; + + /* symbol is (partially) read before redefinition in flow */ + if (mask == -1) break; + + /* symbol is neither read nor completely redefined -- check successor flows */ + for (succ = setFirstItem(pcfl->to); succ; succ = setNextItem (pcfl->to)) { + state = newState (succ->pcflow, (defmap_t *) INT_TO_PTR(mask)); + if (stateIsNew (state, todo, done)) { + stackPush (todo, state); + } else { + deleteState (state); + } + } // for + } // while + + while (!stackIsEmpty (todo)) deleteState ((state_t *) stackPop (todo)); + while (!stackIsEmpty (done)) deleteState ((state_t *) stackPop (done)); + + /* symbol is read in at least one flow -- is alive */ + if (mask == -1) return 1; + + /* symbol is read in no flow */ + return 0; +} + +/* Returns whether access to the given symbol has side effects. */ +static int pic16_symIsSpecial (symbol_t sym) { + //fprintf (stderr, "%s: sym=%x\n", __FUNCTION__, sym); + switch (sym) { + case SPO_INDF0: + case SPO_PLUSW0: + case SPO_POSTINC0: + case SPO_POSTDEC0: + case SPO_PREINC0: + case SPO_INDF1: + case SPO_PLUSW1: + case SPO_POSTINC1: + case SPO_POSTDEC1: + case SPO_PREINC1: + case SPO_INDF2: + case SPO_PLUSW2: + case SPO_POSTINC2: + case SPO_POSTDEC2: + case SPO_PREINC2: + case SPO_PCL: + return 1; + default: + /* no special effects known */ + return 0; + } // switch + + return 0; +} + +/* Check whether a register should be considered local (to the current function) or not. */ +static int pic16_regIsLocal (reg_info *r) { + symbol_t sym; + if (r) { + if (r->type == REG_TMP) return 1; + + sym = symFromStr (r->name); + switch (sym) { + case SPO_WREG: + case SPO_FSR0L: // used in ptrget/ptrput + case SPO_FSR0H: // ... as well + case SPO_FSR1L: // used as stack pointer... (so not really local but shared among function calls) + case SPO_FSR1H: // ... as well + case SPO_FSR2L: // used as frame pointer + case SPO_FSR2H: // ... as well + case SPO_PRODL: // used to return values from functions + case SPO_PRODH: // ... as well + /* these registers (and some more...) are considered local */ + return 1; + break; + default: + /* for unknown regs: check is marked local, leave if not */ + if (r->isLocal) { + return 1; + } else { + //fprintf (stderr, "%s: non-local reg used: %s\n", __FUNCTION__, r->name); + return 0; + } + } // switch + } // if + + /* if in doubt, assume non-local... */ + return 0; +} + +/* Check all symbols touched by pc whether their newly assigned values are read. + * Returns 0 if no symbol is used later on, 1 otherwise. */ +static int pic16_pCodeIsAlive (pCode *pc) { + pCodeInstruction *pci; + defmap_t *map; + reg_info *checkreg; + + /* we can only handle PCIs */ + if (!isPCI(pc)) return 1; + + //pc->print (stderr, pc); + + pci = PCI(pc); + assert (pci && pci->pcflow && pci->pcflow->defmap); + + /* NEVER remove instructions with implicit side effects */ + switch (pci->op) { + case POC_TBLRD: + case POC_TBLRD_POSTINC: /* modify TBLPTRx */ + case POC_TBLRD_POSTDEC: + case POC_TBLRD_PREINC: + case POC_TBLWT: /* modify program memory */ + case POC_TBLWT_POSTINC: /* modify TBLPTRx */ + case POC_TBLWT_POSTDEC: + case POC_TBLWT_PREINC: + case POC_CLRWDT: /* clear watchdog timer */ + case POC_PUSH: /* should be safe to remove though... */ + case POC_POP: /* should be safe to remove though... */ + case POC_CALL: + case POC_RCALL: + case POC_RETFIE: + case POC_RETURN: + //fprintf (stderr, "%s: instruction with implicit side effects not removed: %s\n", __FUNCTION__, pci->mnemonic); + return 1; + + default: + /* no special instruction */ + break; + } // switch + + /* prevent us from removing assignments to non-local variables */ + checkreg = NULL; + if (PCI(pc)->outCond & PCC_REGISTER) checkreg = pic16_getRegFromInstruction (pc); + else if (PCI(pc)->outCond & PCC_REGISTER2) checkreg = pic16_getRegFromInstruction2(pc); + + if ((PCI(pc)->outCond & (PCC_REGISTER | PCC_REGISTER2)) && !checkreg) { + /* assignment to DIRECT operand like "BSF (_global + 1),6" */ + //fprintf (stderr, "%s: assignment to register detected, but register not available!\n", __FUNCTION__); + //pc->print (stderr, pc); + return 1; + } + if ((PCI(pc)->outCond & (PCC_REGISTER | PCC_REGISTER2)) && !pic16_regIsLocal (checkreg)) { + //fprintf (stderr, "%s: dest-reg not local %s\n", __FUNCTION__, checkreg ? checkreg->name : "<unknown>"); + return 1; + } + +#if 1 + /* OVERKILL: prevent us from removing reads from non-local variables + * THIS IS HERE TO AVOID PROBLEMS WITH VOLATILE OPERANDS ONLY! + * Once registers get a "isVolatile" field this might be handled more efficiently... */ + checkreg = NULL; + if (PCI(pc)->inCond & PCC_REGISTER) checkreg = pic16_getRegFromInstruction (pc); + else if (PCI(pc)->inCond & PCC_REGISTER2) checkreg = pic16_getRegFromInstruction2(pc); + + if ((PCI(pc)->inCond & (PCC_REGISTER | PCC_REGISTER2)) && !checkreg) { + /* read from DIRECT operand like "BTFSS (_global + 1),6" -- might be volatile */ + //fprintf (stderr, "%s: read from register detected, but register not available!\n", __FUNCTION__); + //pc->print (stderr, pc); + return 1; + } + if ((PCI(pc)->inCond & (PCC_REGISTER | PCC_REGISTER2)) && !pic16_regIsLocal (checkreg)) { + //fprintf (stderr, "%s: src-reg not local: %s\n", __FUNCTION__, checkreg ? checkreg->name : "<unknown>"); + return 1; + } +#endif + + /* now check that the defined symbols are not used */ + map = pci->pcflow->defmap; + + /* find items for pc */ + while (map && map->pc != pc) map = map->next; + + /* no entries found? something is fishy with DF analysis... -- play safe */ + if (!map) { + if (pic16_pcode_verbose) { + fprintf (stderr, "%s: defmap not found\n", __FUNCTION__); + } + return 1; + } + + /* check all symbols being modified by pc */ + while (map && map->pc == pc) { + if (map->sym == 0) { map = map->next; continue; } + + /* keep pc if it references special symbols (like POSTDEC0) */ +#if 0 + { + char buf[256]; + pic16_pCode2str (buf, sizeof(buf), pc); + fprintf (stderr, "%s: checking for sym %x(%s) at pc %p (%s)\n", __FUNCTION__, map->sym, strFromSym (map->sym), pc, buf); + } +#endif + if (pic16_symIsSpecial (map->sym)) { + //fprintf (stderr, "%s: special sym\n", __FUNCTION__); + return 1; + } + if (map->acc.access.isWrite) { + if (pic16_isAlive (map->sym, pc)) { + //fprintf (stderr, "%s(%s): pCode is alive (sym %s still used)\n", __FUNCTION__, pic16_pBlockGetFunctionName (pc->pb),strFromSym (map->sym)); + return 1; + } + } + map = map->next; + } // while + + /* no use for any of the pc-assigned symbols found -- pCode is dead and can be removed */ +#if 0 + { + char buf[256]; + pic16_pCode2str (buf, sizeof(buf), pc); + fprintf (stderr, "%s: pCode %p (%s) is dead.\n", __FUNCTION__, pc, buf); + } +#endif + return 0; +} + +/* Adds implied operands to the list. + * sym - operand being accessed in the pCode + * list - list to append the operand + * isRead - set to 1 iff sym is read in pCode + * listRead - set to 1 iff all operands being read are to be listed + * + * Returns 0 for "normal" operands, 1 for special operands. + */ +static int fixupSpecialOperands (symbol_t sym, int in_mask, int mask, pCode *pc, valnum_t val, defmap_t **list, int isRead, int isWrite) { + /* check whether accessing REG accesses other REGs as well */ + switch (sym) { + case SPO_INDF0: + /* reads FSR0x */ + *list = newDefmap (sym, 0xff, 0xff, 0, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR0L, 0xff, 0xff, 1, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR0H, 0xff, 0xff, 1, 0, pc, 0, *list); + break; + + case SPO_PLUSW0: + /* reads FSR0x and WREG */ + *list = newDefmap (SPO_WREG, 0xff, 0x00, 1, 0, pc, 0, *list); + *list = newDefmap (sym, 0xff, 0xff, 0, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR0L, 0xff, 0xff, 1, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR0H, 0xff, 0xff, 1, 0, pc, 0, *list); + break; + + case SPO_POSTDEC0: + case SPO_POSTINC0: + case SPO_PREINC0: + /* reads/modifies FSR0x */ + *list = newDefmap (sym, 0xff, 0xff, 0, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR0L, 0xff, 0xff, 1, 1, pc, newValnum (), *list); + *list = newDefmap (SPO_FSR0H, 0xff, 0xff, 1, 1, pc, newValnum (), *list); + break; + + case SPO_INDF1: + /* reads FSR1x */ + *list = newDefmap (sym, 0xff, 0xff, 0, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR1L, 0xff, 0xff, 1, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR1H, 0xff, 0xff, 1, 0, pc, 0, *list); + break; + + case SPO_PLUSW1: + /* reads FSR1x and WREG */ + *list = newDefmap (SPO_WREG, 0xff, 0x00, 1, 0, pc, 0, *list); + *list = newDefmap (sym, 0xff, 0xff, 0, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR1L, 0xff, 0xff, 1, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR1H, 0xff, 0xff, 1, 0, pc, 0, *list); + break; + + case SPO_POSTDEC1: + case SPO_POSTINC1: + case SPO_PREINC1: + /* reads/modifies FSR1x */ + *list = newDefmap (sym, 0xff, 0xff, 0, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR1L, 0xff, 0xff, 1, 1, pc, newValnum (), *list); + *list = newDefmap (SPO_FSR1H, 0xff, 0xff, 1, 1, pc, newValnum (), *list); + break; + + case SPO_INDF2: + /* reads FSR2x */ + *list = newDefmap (sym, 0xff, 0xff, 0, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR2L, 0xff, 0xff, 1, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR2H, 0xff, 0xff, 1, 0, pc, 0, *list); + break; + + case SPO_PLUSW2: + /* reads FSR2x and WREG */ + *list = newDefmap (SPO_WREG, 0xff, 0x00, 1, 0, pc, 0, *list); + *list = newDefmap (sym, 0xff, 0xff, 0, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR2L, 0xff, 0xff, 1, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR2H, 0xff, 0xff, 1, 0, pc, 0, *list); + break; + + case SPO_POSTDEC2: + case SPO_POSTINC2: + case SPO_PREINC2: + /* reads/modifies FSR2x */ + *list = newDefmap (sym, 0xff, 0xff, 0, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR2L, 0xff, 0xff, 1, 1, pc, newValnum (), *list); + *list = newDefmap (SPO_FSR2H, 0xff, 0xff, 1, 1, pc, newValnum (), *list); + break; + + case SPO_PCL: + /* modifies PCLATH and PCLATU */ + *list = newDefmap (SPO_PCL, 0xff, 0xff, isRead, isWrite, pc, newValnum (), *list); + if (isRead) { + /* reading PCL updates PCLATx */ + *list = newDefmap (SPO_PCLATH, 0xff, 0xff, 0, 1, pc, newValnum (), *list); + *list = newDefmap (SPO_PCLATU, 0xff, 0xff, 0, 1, pc, newValnum (), *list); + } + if (isWrite) { + /* writing PCL implicitly reads PCLATx (computed GOTO) */ + *list = newDefmap (SPO_PCLATH, 0xff, 0xff, 1, 0, pc, 0, *list); + *list = newDefmap (SPO_PCLATU, 0xff, 0xff, 1, 0, pc, 0, *list); + } + break; + + default: + *list = newDefmap (sym, in_mask, mask, isRead, isWrite, pc, val, *list); + /* nothing special */ + return 0; + break; + } + + /* has been a special operand */ + return 1; +} + +static symbol_t pic16_fsrsym_idx[][2] = { + {SPO_FSR0L, SPO_FSR0H}, + {SPO_FSR1L, SPO_FSR1H}, + {SPO_FSR2L, SPO_FSR2H} +}; + +/* Merge multiple defmap entries for the same symbol for list's pCode. */ +static void mergeDefmapSymbols (defmap_t *list) { + defmap_t *ref, *curr, *temp; + + /* now make sure that each symbol occurs at most once per pc */ + ref = list; + while (ref && (ref->pc == list->pc)) { + curr = ref->next; + while (curr && (curr->pc == list->pc)) { + if (curr->sym == ref->sym) { + //fprintf (stderr, "Merging defmap entries for symbol %s\n", strFromSym (ref->sym)); + /* found a symbol occuring twice... merge the two */ + if (curr->acc.access.isRead) { + //if (ref->acc.access.isRead) fprintf (stderr, "symbol %s was marked twice as read at pc %p\n", strFromSym (ref->sym), ref->pc); + ref->acc.access.isRead = 1; + ref->acc.access.in_mask |= curr->acc.access.in_mask; + } + if (curr->acc.access.isWrite) { + //if (ref->acc.access.isWrite) fprintf (stderr, "symbol %s was marked twice as written at pc %p\n", strFromSym (ref->sym), ref->pc); + ref->acc.access.isWrite = 1; + ref->acc.access.mask |= curr->acc.access.mask; + } + temp = curr; + curr = curr->next; + deleteDefmap (temp); + continue; // do not skip curr! + } // if + curr = curr->next; + } // while + ref = ref->next; + } // while +} + +/** Prepend list with the reads and definitions performed by pc. */ +static defmap_t *createDefmap (pCode *pc, defmap_t *list) { + pCodeInstruction *pci; + int cond, inCond, outCond; + int mask = 0xff, smask; + symbol_t sym, sym2; + char *name; + + if (isPCAD(pc)) { + /* make sure there is at least one entry for each pc (needed by list traversal routines) */ + /* TODO: mark this defmap node as an ASMDIR -- any values might be read/modified */ + fprintf (stderr, "ASMDIRs not supported by data flow analysis!\n"); + list = newDefmap (0, 0xff, 0xff, 0, 0, pc, 0, list); + return list; + } + assert (isPCI(pc)); + pci = PCI(pc); + + /* handle bit instructions */ + if (pci->isBitInst) { + assert (pci->pcop->type == PO_GPR_BIT); + mask = 1U << (PCORB(PCI(pc)->pcop)->bit); + } + + /* handle (additional) implicit arguments */ + switch (pci->op) { + case POC_LFSR: + { + int lit; + valnum_t val; + lit = PCOL(pci->pcop)->lit; + assert (lit >= 0 && lit < 3); + //fprintf (stderr, "LFSR: %s // %s\n", pci->pcop->name, pic16_get_op(((pCodeOpLit2 *)(pci->pcop))->arg2, NULL, 0)); + val = valnumFromStr (pic16_get_op(((pCodeOpLit2 *)(pci->pcop))->arg2, NULL, 0)); + //fprintf (stderr, "LFSR lit=%u, symval=%4x\n", lit, val); + list = newDefmap (pic16_fsrsym_idx[lit][0], 0x00, 0xff, 0, 1, pc, val, list); + list = newDefmap (pic16_fsrsym_idx[lit][1], 0x00, 0xff, 0, 1, pc, val+1, list); // val+1 is guaranteed not be used as a valnum... + } + break; + + case POC_MOVLB: // BSR + case POC_BANKSEL: // BSR + list = newDefmap (SPO_BSR, 0x00, 0xff, 0, 1, pc, valnumFromStr (pic16_get_op (((pCodeOpLit2 *)(pci->pcop))->arg2, NULL, 0)), list); + break; + + case POC_MULWF: // PRODx + case POC_MULLW: // PRODx + list = newDefmap (SPO_PRODH, 0x00, 0xff, 0, 1, pc, newValnum (), list); + list = newDefmap (SPO_PRODL, 0x00, 0xff, 0, 1, pc, newValnum (), list); + break; + + case POC_POP: // TOS, STKPTR + list = newDefmap (SPO_STKPTR, 0xff, 0xff, 1, 1, pc, newValnum (), list); + list = newDefmap (SPO_TOSL, 0x00, 0xff, 0, 1, pc, newValnum (), list); + list = newDefmap (SPO_TOSH, 0x00, 0xff, 0, 1, pc, newValnum (), list); + list = newDefmap (SPO_TOSU, 0x00, 0xff, 0, 1, pc, newValnum (), list); + break; + + case POC_PUSH: // STKPTR + list = newDefmap (SPO_STKPTR, 0xff, 0xff, 1, 1, pc, newValnum (), list); + list = newDefmap (SPO_TOSL, 0xff, 0xff, 0, 1, pc, newValnum (), list); + list = newDefmap (SPO_TOSH, 0xff, 0xff, 0, 1, pc, newValnum (), list); + list = newDefmap (SPO_TOSU, 0xff, 0xff, 0, 1, pc, newValnum (), list); + break; + + case POC_CALL: // return values (and arguments?): WREG, PRODx, FSR0L + case POC_RCALL: // return values (and arguments?): WREG, PRODx, FSR0L + list = newDefmap (SPO_WREG, 0xff, 0xff, 1, 1, pc, newValnum (), list); + list = newDefmap (SPO_PRODL, 0xff, 0xff, 1, 1, pc, newValnum (), list); + list = newDefmap (SPO_PRODH, 0xff, 0xff, 1, 1, pc, newValnum (), list); + list = newDefmap (SPO_FSR0L, 0xff, 0xff, 1, 1, pc, newValnum (), list); + + /* needs correctly set-up stack pointer */ + list = newDefmap (SPO_FSR1L, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_FSR1H, 0xff, 0x00, 1, 0, pc, 0, list); + break; + + case POC_RETLW: // return values: WREG, PRODx, FSR0L + /* pseudo read on (possible) return values */ + // WREG is handled below via outCond + list = newDefmap (SPO_PRODL, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_PRODH, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_FSR0L, 0xff, 0x00, 1, 0, pc, 0, list); + + /* caller's stack pointers must be restored */ + list = newDefmap (SPO_FSR1L, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_FSR1H, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_FSR2L, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_FSR2H, 0xff, 0x00, 1, 0, pc, 0, list); + break; + + case POC_RETURN: // return values; WREG, PRODx, FSR0L + case POC_RETFIE: // return value: WREG, PRODx, FSR0L + /* pseudo read on (possible) return values */ + list = newDefmap (SPO_WREG, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_PRODL, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_PRODH, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_FSR0L, 0xff, 0x00, 1, 0, pc, 0, list); + + /* caller's stack pointers must be restored */ + list = newDefmap (SPO_FSR1L, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_FSR1H, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_FSR2L, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_FSR2H, 0xff, 0x00, 1, 0, pc, 0, list); + break; + + case POC_TBLRD: + list = newDefmap (SPO_TBLPTRL, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_TBLPTRH, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_TBLPTRU, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_TABLAT, 0x00, 0xff, 0, 1, pc, newValnum(), list); + break; + + case POC_TBLRD_POSTINC: + case POC_TBLRD_POSTDEC: + case POC_TBLRD_PREINC: + list = newDefmap (SPO_TBLPTRL, 0xff, 0xff, 1, 1, pc, newValnum(), list); + list = newDefmap (SPO_TBLPTRH, 0xff, 0xff, 1, 1, pc, newValnum(), list); + list = newDefmap (SPO_TBLPTRU, 0xff, 0xff, 1, 1, pc, newValnum(), list); + list = newDefmap (SPO_TABLAT, 0x00, 0xff, 0, 1, pc, newValnum(), list); + break; + + case POC_TBLWT: + list = newDefmap (SPO_TBLPTRL, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_TBLPTRH, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_TBLPTRU, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_TABLAT, 0xff, 0x00, 1, 0, pc, 0, list); + break; + + case POC_TBLWT_POSTINC: + case POC_TBLWT_POSTDEC: + case POC_TBLWT_PREINC: + list = newDefmap (SPO_TBLPTRL, 0xff, 0xff, 1, 1, pc, newValnum(), list); + list = newDefmap (SPO_TBLPTRH, 0xff, 0xff, 1, 1, pc, newValnum(), list); + list = newDefmap (SPO_TBLPTRU, 0xff, 0xff, 1, 1, pc, newValnum(), list); + list = newDefmap (SPO_TABLAT, 0xff, 0x00, 1, 0, pc, 0, list); + break; + + default: + /* many instruction implicitly read BSR... -- THIS IS IGNORED! */ + break; + } // switch + + /* handle explicit arguments */ + inCond = pci->inCond; + outCond = pci->outCond; + cond = inCond | outCond; + if (cond & PCC_W) { + list = newDefmap (symFromStr ("WREG"), mask, mask, inCond & PCC_W, outCond & PCC_W, pc, newValnum (), list); + } // if + + /* keep STATUS read BEFORE STATUS write in the list (still neccessary?) */ + if (inCond & PCC_STATUS) { + smask = 0; + if (inCond & PCC_C) smask |= 1U << PIC_C_BIT; + if (inCond & PCC_DC) smask |= 1U << PIC_DC_BIT; + if (inCond & PCC_Z) smask |= 1U << PIC_Z_BIT; + if (inCond & PCC_OV) smask |= 1U << PIC_OV_BIT; + if (inCond & PCC_N) smask |= 1U << PIC_N_BIT; + + list = newDefmap (symFromStr ("STATUS"), smask, 0x00, 1, 0, pc, 0, list); + //fprintf (stderr, "pc %p: def STATUS & %02x\n", pc, smask); + } // if + + if (outCond & PCC_STATUS) { + smask = 0; + if (outCond & PCC_C) smask |= 1U << PIC_C_BIT; + if (outCond & PCC_DC) smask |= 1U << PIC_DC_BIT; + if (outCond & PCC_Z) smask |= 1U << PIC_Z_BIT; + if (outCond & PCC_OV) smask |= 1U << PIC_OV_BIT; + if (outCond & PCC_N) smask |= 1U << PIC_N_BIT; + + list = newDefmap (symFromStr ("STATUS"), 0x00, smask, 0, 1, pc, newValnum (), list); + //fprintf (stderr, "pc %p: def STATUS & %02x\n", pc, smask); + } // if + + sym = sym2 = 0; + if (cond & PCC_REGISTER) { + name = pic16_get_op (pci->pcop, NULL, 0); + sym = symFromStr (name); + fixupSpecialOperands (sym, mask, mask, pc, newValnum(), &list, inCond & PCC_REGISTER, outCond & PCC_REGISTER); + //fprintf (stderr, "pc %p: def REG %s(%x) & %02x\n", pc, name, sym, mask); + } + + if (cond & PCC_REGISTER2) { + name = pic16_get_op2 (pci->pcop, NULL, 0); + sym2 = symFromStr (name); + fixupSpecialOperands (sym2, mask, mask, pc, newValnum(), &list, inCond & PCC_REGISTER2, outCond & PCC_REGISTER2); + //fprintf (stderr, "pc %p: def REG2 %s(%x) & %02x\n", pc, name, sym2, mask); + } + + + /* make sure there is at least one entry for each pc (needed by list traversal routines) */ + list = newDefmap (0, 0x00, 0x00, 0, 0, pc, 0, list); + + mergeDefmapSymbols (list); + + return list; +} + +#if 0 +static void printDefmap (defmap_t *map) { + defmap_t *curr; + + curr = map; + fprintf (stderr, "defmap @ %p:\n", curr); + while (curr) { + fprintf (stderr, "%s%s: %4x|%4x / %02x|%02x, sym %s(%x) @ pc %p\n", + curr->acc.access.isRead ? "R" : " ", + curr->acc.access.isWrite ? "W": " ", + curr->in_val, curr->val, + curr->acc.access.in_mask, curr->acc.access.mask, + strFromSym(curr->sym), curr->sym, + curr->pc); + curr = curr->next; + } // while + fprintf (stderr, "<EOL>\n"); +} +#endif + +/* Add "additional" definitions to uniq. + * This can be used to merge the in_values and the flow's defmap to create an in_value-list for the flow's successors. + * This can also be used to create a uniq (out)list from a flow's defmap by passing *uniq==NULL. + * + * If symbols defined in additional are not present in uniq, a definition is created. + * Otherwise the present definition is altered to reflect the newer assignments. + * + * flow: <uniq> --> assign1 --> assign2 --> assign3 --> ... --> <uniq'> + * before `------- noted in additional --------' after + * + * I assume that each symbol occurs AT MOST ONCE in uniq. + * + */ +static int defmapUpdateUniqueSym (defmap_t **uniq, defmap_t *additional) { + defmap_t *curr; + defmap_t *old; + int change = 0; + + //fprintf (stderr, "%s: merging %p & %p\n", __FUNCTION__, *uniq, additional); + /* find tail of additional list (holds the first assignment) */ + curr = additional; + while (curr && curr->next) curr = curr->next; + + /* update uniq */ + do { + /* find next assignment in additionals */ + while (curr && !curr->acc.access.isWrite) curr = curr->prev; + + if (!curr) break; + + /* find item in uniq */ + old = *uniq; + //printDefmap (*uniq); + while (old && (old->sym != curr->sym)) old = old->next; + + if (old) { + /* definition found -- replace */ + if (old->val != curr->val) { + old->val = curr->val; + change++; + } // if + } else { + /* new definition */ + *uniq = newDefmap (curr->sym, 0x00, 0xff, 0, 1, NULL, curr->val, *uniq); + change++; + } + + curr = curr->prev; + } while (1); + + /* return 0 iff uniq remained unchanged */ + return change; +} + +/* Creates the in_value list of a flow by (iteratively) merging the out_value + * lists of its predecessor flows. + * Initially *combined should be NULL, alt_in will be copied to combined. + * If *combined != NULL, combined will be altered: + * - for symbols defined in *combined but not in alt_in, + * *combined is altered to 0 (value unknown, either *combined or INIT). + * - for symbols defined in alt_in but not in *combined, + * a 0 definition is created (value unknown, either INIT or alt). + * - for symbols defined in both, *combined is: + * > left unchanged if *combined->val == alt_in->val or + * > modified to 0 otherwise (value unknown, either alt or *combined). + * + * I assume that each symbol occurs AT MOST ONCE in each list! + */ +static int defmapCombineFlows (defmap_t **combined, defmap_t *alt_in, pBlock *pb) { + defmap_t *curr; + defmap_t *old; + int change = 0; + valnum_t val; + + //fprintf (stderr, "%s: merging %p & %p\n", __FUNCTION__, *combined, alt_in); + + if (!(*combined)) { + return defmapUpdateUniqueSym (combined, alt_in); + } // if + + /* merge the two */ + curr = alt_in; + while (curr) { + /* find symbols definition in *combined */ + old = *combined; + while (old && (old->sym != curr->sym)) old = old->next; + + if (old) { + /* definition found */ + if (old->val && (old->val != curr->val)) { + old->val = 0; /* value unknown */ + change++; + } + } else { + /* no definition found -- can be either INIT or alt_in's value */ + val = pic16_pBlockAddInval (pb, curr->sym)->val; + *combined = newDefmap (curr->sym, 0x00, 0xff, 0, 1, NULL, (val == curr->val) ? val : 0, *combined); + if (val != curr->val) change++; + } + + curr = curr->next; + } // while (curr) + + /* update symbols from *combined that are NOT defined in alt_in -- can be either *combined's value or INIT */ + old = *combined; + while (old) { + if (old->val != 0) { + /* find definition in alt_in */ + curr = alt_in; + while (curr && curr->sym != old->sym) curr = curr->next; + if (!curr) { + /* symbol defined in *combined only -- can be either INIT or *combined */ + val = pic16_pBlockAddInval (pb, old->sym)->val; + if (old->val != val) { + old->val = 0; + change++; + } + } // if + } // if + + old = old->next; + } // while + + return change; +} + +static int defmapCompareUnique (defmap_t *map1, defmap_t *map2) { + defmap_t *curr1, *curr2; + symbol_t sym; + + /* identical maps are equal */ + if (map1 == map2) return 0; + + if (!map1) return -1; + if (!map2) return 1; + + //fprintf (stderr, "%s: comparing %p & %p\n", __FUNCTION__, map1, map2); + + /* check length */ + curr1 = map1; + curr2 = map2; + while (curr1 && curr2) { + curr1 = curr1->next; + curr2 = curr2->next; + } // while + + /* one of them longer? */ + if (curr1) return 1; + if (curr2) return -1; + + /* both lists are of equal length -- compare (in O(n^2)) */ + curr1 = map1; + while (curr1) { + sym = curr1->sym; + curr2 = map2; + while (curr2 && curr2->sym != sym) curr2 = curr2->next; + if (!curr2) return 1; // symbol not found in curr2 + if (curr2->val != curr1->val) return 1; // values differ + + /* compare next symbol */ + curr1 = curr1->next; + } // while + + /* no difference found */ + return 0; +} + + +/* Prepare a list of all reaching definitions per flow. + * This is done using a forward dataflow analysis. + */ +static void createReachingDefinitions (pBlock *pb) { + defmap_t *out_vals, *in_vals; + pCode *pc; + pCodeFlow *pcfl; + pCodeFlowLink *link; + set *todo; + set *blacklist; + + if (!pb) return; + + /* initialize out_vals to unique'fied defmaps per pCodeFlow */ + for (pc = pic16_findNextInstruction (pb->pcHead); pc; pc = pic16_findNextInstruction (pc->next)) { + if (isPCFL(pc)) { + deleteDefmapChain (&PCFL(pc)->in_vals); + deleteDefmapChain (&PCFL(pc)->out_vals); + defmapUpdateUniqueSym (&PCFL(pc)->out_vals, PCFL(pc)->defmap); + } // if + } // for + + pc = pic16_findNextInstruction (pb->pcHead); + if (!pc) { + // empty function, avoid NULL pointer dereference + return; + } // if + todo = NULL; blacklist = NULL; + addSetHead (&todo, PCI(pc)->pcflow); + + //fprintf (stderr, "%s: function %s()\n", __FUNCTION__, pic16_pBlockGetFunctionName (pb)); + while (elementsInSet (todo)) { + //fprintf (stderr, "%u items in todo-set\n", elementsInSet (todo)); + pcfl = PCFL(indexSet (todo, 0)); + deleteSetItem (&todo, pcfl); + //fprintf (stderr, "%s: checking %p\n", __FUNCTION__, pcfl); + in_vals = NULL; + out_vals = NULL; + + if (isinSet (blacklist, pcfl)) { + fprintf (stderr, "ignoring blacklisted flow\n"); + continue; + } + + /* create in_vals from predecessors out_vals */ + link = setFirstItem (pcfl->from); + while (link) { + defmapCombineFlows (&in_vals, link->pcflow->out_vals, pb); + link = setNextItem (pcfl->from); + } // while + + //printDefmap (in_vals); + //printDefmap (pcfl->in_vals); + + if (!pcfl->in_vals || !pcfl->out_vals || defmapCompareUnique (in_vals, pcfl->in_vals)) { + //fprintf (stderr, "in_vals changed\n"); + /* in_vals changed -- update out_vals */ + deleteDefmapChain (&pcfl->in_vals); + pcfl->in_vals = in_vals; + + /* create out_val from in_val and defmap */ + out_vals = NULL; + defmapUpdateUniqueSym (&out_vals, in_vals); + defmapUpdateUniqueSym (&out_vals, pcfl->defmap); + + /* is out_vals different from pcfl->out_vals */ + if (!pcfl->out_vals || defmapCompareUnique (out_vals, pcfl->out_vals)) { + //fprintf (stderr, "out_vals changed\n"); + deleteDefmapChain (&pcfl->out_vals); + pcfl->out_vals = out_vals; + + if (pcfl->out_vals == NULL && pcfl->in_vals == NULL) { + addSet (&blacklist, pcfl); + } // if + + /* reschedule all successors */ + link = setFirstItem (pcfl->to); + while (link) { + //fprintf (stderr, " %p --> %p\n", pcfl, link->pcflow); + addSetIfnotP (&todo, link->pcflow); + link = setNextItem (pcfl->to); + } // while + } else { + deleteDefmapChain (&out_vals); + }// if + } else { + deleteDefmapChain (&in_vals); + } // if + } // while +} + +#if 0 +static void showAllDefs (symbol_t sym, pCode *pc) { + defmap_t *map; + int count; + + assert (isPCI(pc)); + count = defmapFindAll (sym, pc, &map); + + fprintf (stderr, "sym %s(%x) @ %p defined as (val@pc): ", strFromSym(sym), sym, pc); + while (map) { +#if 1 + fprintf (stderr, "(%x @ %p) ", map->val, map->pc); +#else + { + char buf[256]; + + pic16_pCode2str (buf, sizeof(buf), map->pc); + fprintf (stderr, "\n (%x @ %p(%s)) ", map->val, map->pc, buf); + } +#endif + map = map->next; + } + deleteDefmapChain (&map); +} +#endif + +/* safepCodeUnlink and remove pc from defmap. */ +static int pic16_safepCodeRemove (pCode *pc, char *comment) { + defmap_t *map, *next, **head; + int res; + + map = isPCI(pc) ? PCI(pc)->pcflow->defmap : NULL; + head = isPCI(pc) ? &PCI(pc)->pcflow->defmap : NULL; + res = pic16_safepCodeUnlink (pc, comment); + + if (res && map) { + /* remove pc from defmap */ + while (map) { + next = map->next; + if (map->pc == pc) { + if (!map->prev && head) *head = map->next; + deleteDefmap (map); + } // if + map = next; + } + } + + return res; +} + +void pic16_fixDefmap (pCode *pc, pCode *newpc) { + defmap_t *map; + /* This breaks the defmap chain's references to pCodes... fix it! */ + map = PCI(pc)->pcflow->defmap; + + while (map && map->pc != pc) map = map->next; + + while (map && map->pc == pc) { + map->pc = newpc; + map = map->next; + } // while +} + +/* Replace a defmap entry for sym with newsym for read accesses (isRead == 1) or + * write accesses (isRead == 0). */ +void defmapReplaceSymRef (pCode *pc, symbol_t sym, symbol_t newsym, int isRead) { + defmap_t *map, *map_start; + defmap_t *copy; + if (!isPCI(pc)) return; + if (sym == newsym) return; + + map = PCI(pc)->pcflow->defmap; + + while (map && map->pc != pc) map = map->next; + map_start = map; + while (map && map->pc == pc) { + if (map->sym == sym) { + assert ((isRead && map->acc.access.isRead) || ((!isRead) && (map->acc.access.isWrite))); + if (!(map->acc.access.isRead && map->acc.access.isWrite)) { + /* only one kind of access handled... this is easy */ + map->sym = newsym; + } else { + /* must copy defmap entry before replacing symbol... */ + copy = copyDefmap (map); + if (isRead) { + map->acc.access.isRead = 0; + copy->acc.access.isWrite = 0; + } else { + map->acc.access.isWrite = 0; + copy->acc.access.isRead = 0; + } + copy->sym = newsym; + /* insert copy into defmap chain */ + defmapInsertAfter (map, copy); + } + } + map = map->next; + } // while + + /* as this might introduce multiple defmap entries for newsym... */ + mergeDefmapSymbols (map_start); +} + +/* Assign "better" valnums to results. */ +static void assignValnums (pCode *pc) { + pCodeInstruction *pci; + pCode *newpc; + symbol_t sym1, sym2; + int cond, isSpecial1, isSpecial2, count, mask, lit; + defmap_t *list, *val, *oldval, *dummy; + reg_info *reg1 = NULL, *reg2 = NULL; + valnum_t litnum; + + /* only works for pCodeInstructions... */ + if (!isPCI(pc)) return; + + pci = PCI(pc); + cond = pci->inCond | pci->outCond; + list = pci->pcflow->defmap; + sym1 = sym2 = isSpecial1 = isSpecial2 = 0; + + if (cond & PCC_REGISTER) { + sym1 = symFromStr (pic16_get_op (pci->pcop, NULL, 0)); + reg1 = pic16_getRegFromInstruction (pc); + isSpecial1 = pic16_symIsSpecial (sym1); + } + if (cond & PCC_REGISTER2) { + sym2 = symFromStr (pic16_get_op2 (pci->pcop, NULL, 0)); + reg2 = pic16_getRegFromInstruction (pc); + isSpecial2 = pic16_symIsSpecial (sym2); + } + + /* determine input values */ + val = list; + while (val && val->pc != pc) val = val->next; + //list = val; /* might save some time later... */ + while (val && val->pc == pc) { + val->in_val = 0; + if (val->sym != 0 && (1 || val->acc.access.isRead)) { + /* get valnum for sym */ + count = defmapFindAll (val->sym, pc, &oldval); + //fprintf (stderr, "%d defs for sym %s\n", count, strFromSym (val->sym)); + if (count == 1) { + if ((val->acc.access.in_mask & oldval->acc.access.mask) == val->acc.access.in_mask) { + val->in_val = oldval->val; + } else { + val->in_val = 0; + } + } else if (count == 0) { + /* no definition found */ + val->in_val = 0; + } else { + /* multiple definition(s) found -- value not known (unless always the same valnum) */ + assert (oldval); + dummy = oldval->next; + mask = oldval->acc.access.mask; + val->in_val = oldval->val; + while (dummy && (dummy->val == val->in_val)) { + mask &= dummy->acc.access.mask; + dummy = dummy->next; + } // while + + /* found other values or to restictive mask */ + if (dummy || ((mask & val->acc.access.in_mask) != val->acc.access.in_mask)) { + val->in_val = 0; + } + } + if (count > 0) deleteDefmapChain (&oldval); + } // if + val = val->next; + } + + /* handle valnum assignment */ + switch (pci->op) { + case POC_CLRF: /* modifies STATUS (Z) */ + if (!isSpecial1 && pic16_regIsLocal (reg1)) { + oldval = defmapCurr (list, sym1, pc); + if (oldval && (litFromValnum (oldval->in_val) == 0)) { + //fprintf (stderr, "%s: REG (%s) already set up correctly (%x)\n", pci->mnemonic, strFromSym(sym1), oldval->in_val); + if (!pic16_isAlive (SPO_STATUS, pc)) pic16_safepCodeRemove (pc, "=DF= redundant CLRF removed"); + } + defmapUpdate (list, sym1, pc, valnumFromLit(0)); + } + break; + + case POC_SETF: /* SETF does not touch STATUS */ + if (!isSpecial1 && pic16_regIsLocal (reg1)) { + oldval = defmapCurr (list, sym1, pc); + if (oldval && (litFromValnum (oldval->in_val) == 0x00FF)) { + //fprintf (stderr, "%s: REG (%s) already set up correctly (%x)\n", pci->mnemonic, strFromSym(sym1), oldval->in_val); + pic16_safepCodeRemove (pc, "=DF= redundant SETF removed"); + } + defmapUpdate (list, sym1, pc, valnumFromLit (0x00FF)); + } + break; + + case POC_MOVLW: /* does not touch STATUS */ + oldval = defmapCurr (list, SPO_WREG, pc); + if (pci->pcop->type == PO_LITERAL) { + //fprintf (stderr, "MOVLW: literal %u\n", PCOL(pci->pcop)->lit); + litnum = valnumFromLit ((unsigned char)PCOL(pci->pcop)->lit); + } else { + //fprintf (stderr, "MOVLW: %s\n", pic16_get_op (pci->pcop, NULL, 0)); + litnum = valnumFromStr (pic16_get_op (pci->pcop, NULL, 0)); + } + if (oldval && oldval->in_val == litnum) { + //fprintf (stderr, "%s: W already set up correctly (%x)\n", PCI(pc)->mnemonic, oldval->in_val); + pic16_safepCodeRemove (pc, "=DF= redundant MOVLW removed"); + } + defmapUpdate (list, SPO_WREG, pc, litnum); + break; + + case POC_ANDLW: /* modifies STATUS (Z,N) */ + case POC_IORLW: /* modifies STATUS (Z,N) */ + case POC_XORLW: /* modifies STATUS (Z,N) */ + /* can be optimized iff WREG contains a known literal (0x100 - 0x1FF) */ + if (pci->pcop->type == PO_LITERAL) { + int vallit = -1; + lit = (unsigned char) PCOL(pci->pcop)->lit; + val = defmapCurr (list, SPO_WREG, pc); + if (val) vallit = litFromValnum (val->in_val); + if (vallit != -1) { + /* xxxLW <literal>, WREG contains a known literal */ + //fprintf (stderr, "%s 0x%02x, WREG: 0x%x\n", pci->mnemonic, lit, vallit); + if (pci->op == POC_ANDLW) { + lit &= vallit; + } else if (pci->op == POC_IORLW) { + lit |= vallit; + } else if (pci->op == POC_XORLW) { + lit ^= vallit; + } else { + assert (0 && "invalid operation"); + } + if (vallit == lit) { + //fprintf (stderr, "%s: W already set up correctly (%x = val %x)\n", pci->mnemonic, vallit, val->in_val); + if (!pic16_isAlive (SPO_STATUS, pc)) pic16_safepCodeRemove (pc, "=DF= redundant ANDLW/IORLW/XORLW removed"); + } + defmapUpdate (list, SPO_WREG, pc, valnumFromLit (lit)); + } // if + } + break; + + case POC_LFSR: + { + /* check if old value matches new value */ + int lit; + int ok = TRUE; + assert (pci->pcop->type == PO_LITERAL); + + lit = PCOL(pci->pcop)->lit; + + val = defmapCurr (list, pic16_fsrsym_idx[lit][0], pc); + + if (val && (val->in_val != 0) && (val->in_val == val->val)) { + //fprintf (stderr, "FSR%dL already set up correctly at %p (%x)\n", lit, pc, val->val); + } else { + /* cannot remove this LFSR */ + ok = FALSE; + } // if + + val = defmapCurr (list, pic16_fsrsym_idx[lit][1], pc); + if (val && (val->in_val != 0) && (val->in_val == val->val)) { + //fprintf (stderr, "FSR%dH already set up correctly at %p (%x)\n", lit, pc, val->val); + } else { + ok = FALSE; + } // if + + if (ok) { + pic16_safepCodeRemove (pc, "=DF= redundant LFSR removed"); + } + } + break; + + case POC_MOVWF: /* does not touch flags */ + /* find value of WREG */ + val = defmapCurr (list, SPO_WREG, pc); + oldval = defmapCurr (list, sym1, pc); + if (val) lit = litFromValnum (val->in_val); + else lit = -1; + //fprintf (stderr, "MOVWF: lit: %i (%x, %x)\n", lit, lit, val->in_val); + + if ((lit == 0 || lit == 0x0ff) && !pic16_isAlive (SPO_STATUS, pc)) { + /* might replace with CLRF/SETF (will possibly make previous MOVLW 0x00/0xff unneccessary --> dead code elimination) */ + //fprintf (stderr, "replacing MOVWF with CLRF/SETF\n"); + if (lit == 0) { + newpc = pic16_newpCode (POC_CLRF, pic16_pCodeOpCopy (pci->pcop)); + } else { + assert (lit == 0x0ff); + newpc = pic16_newpCode (POC_SETF, pic16_pCodeOpCopy (pci->pcop)); + } + if (pic16_debug_verbose || pic16_pcode_verbose) pic16_InsertCommentAfter (pc->prev, "=DF= MOVWF: replaced by CLRF/SETF"); + pic16_pCodeReplace (pc, newpc); + defmapReplaceSymRef (pc, SPO_WREG, 0, 1); + pic16_fixDefmap (pc, newpc); + pc = newpc; + + /* This breaks the defmap chain's references to pCodes... fix it! */ + if (!val->prev) PCI(pc)->pcflow->defmap = val->next; + if (!val->acc.access.isWrite) { + deleteDefmap (val); // delete reference to WREG as in value + val = NULL; + } else { + val->acc.access.isRead = 0; // delete reference to WREG as in value + } + oldval = PCI(pc)->pcflow->defmap; + while (oldval) { + if (oldval->pc == pc) oldval->pc = newpc; + oldval = oldval->next; + } // while + } else if (!isSpecial1 && pic16_regIsLocal (reg1) && val && oldval && (val->in_val != 0) && (val->in_val == oldval->in_val)) { + //fprintf (stderr, "MOVWF: F (%s) already set up correctly (%x) at %p\n", strFromSym (sym1), oldval->in_val, pc); + pic16_safepCodeRemove (pc, "=DF= redundant MOVWF removed"); + } + if (val) defmapUpdate (list, sym1, pc, val->in_val); + break; + + case POC_MOVFW: /* modifies STATUS (Z,N) */ + /* find value of REG */ + if (!isSpecial1 && pic16_regIsLocal (reg1)) { + val = defmapCurr (list, sym1, pc); + oldval = defmapCurr (list, SPO_WREG, pc); + if (val && oldval && (val->in_val != 0) && (val->in_val == oldval->in_val)) { + //fprintf (stderr, "MOVFW: W already set up correctly (%x) at %p\n", oldval->in_val, pc); + if (!pic16_isAlive (SPO_STATUS, pc)) pic16_safepCodeRemove (pc, "=DF= redundant MOVFW removed"); + } else { + defmap_t *pred, *predpred; + /* Optimize MOVLW immd; MOVWF reg1; [...]; MOVFW reg1 + * into MOVLW immd; MOVWF reg1; [...]; MOVLW immd + * This might allow removal of the first two assignments. */ + pred = defmapFindDef (list, sym1, pc); + predpred = pred ? defmapFindDef (list, SPO_WREG, pred->pc) : NULL; + if (pred && predpred && (PCI(pred->pc)->op == POC_MOVWF) && (PCI(predpred->pc)->op == POC_MOVLW) + && !pic16_isAlive (SPO_STATUS, pc)) + { + newpc = pic16_newpCode (POC_MOVLW, pic16_pCodeOpCopy (PCI(predpred->pc)->pcop)); + + if (pic16_debug_verbose || pic16_pcode_verbose) { + pic16_InsertCommentAfter (pc->prev, "=DF= MOVFW: replaced last of MOVLW;MOVWF;MOVFW by MOVLW"); + } // if + pic16_pCodeReplace (pc, newpc); + defmapReplaceSymRef (pc, sym1, 0, 1); + pic16_fixDefmap (pc, newpc); + pc = newpc; + + /* This breaks the defmap chain's references to pCodes... fix it! */ + if (!val->prev) PCI(pc)->pcflow->defmap = val->next; + if (!val->acc.access.isWrite) { + deleteDefmap (val); // delete reference to reg1 as in value + val = NULL; + } else { + val->acc.access.isRead = 0; // delete reference to reg1 as in value + } + oldval = PCI(pc)->pcflow->defmap; + while (oldval) { + if (oldval->pc == pc) oldval->pc = newpc; + oldval = oldval->next; + } // while + } // if + } + if (val) defmapUpdate (list, SPO_WREG, pc, val->in_val); + } + break; + + case POC_MOVFF: /* does not touch STATUS */ + /* find value of REG */ + val = defmapCurr (list, sym1, pc); + oldval = defmapCurr (list, sym2, pc); + if (val) lit = litFromValnum (val->in_val); + else lit = -1; + newpc = NULL; + if (!isSpecial1 && pic16_regIsLocal (reg1) && val && oldval && !pic16_isAlive (SPO_STATUS, pc)) { + //pc->print (stderr, pc); fprintf (stderr, "lit: %d (%x, %x)\n", lit, lit, val->in_val); + if (lit == 0) { + newpc = pic16_newpCode (POC_CLRF, PCOP2(pci->pcop)->pcopR); + } else if (lit == 0x00ff) { + newpc = pic16_newpCode (POC_SETF, PCOP2(pci->pcop)->pcopR); + } else { + newpc = NULL; + } + if (newpc) { + pic16_InsertCommentAfter (pc->prev, "=DF= MOVFF: replaced by CRLF/SETF"); + pic16_df_saved_bytes += PCI(pc)->isize - PCI(newpc)->isize; + pic16_pCodeReplace (pc, newpc); + defmapReplaceSymRef (pc, sym1, 0, 1); + pic16_fixDefmap (pc, newpc); + pc = newpc; + break; // do not process instruction as MOVFF... + } + } else if (!isSpecial1 && !isSpecial2 + && pic16_regIsLocal (reg1) && pic16_regIsLocal (reg2) + && val && oldval && (val->in_val != 0)) { + if (val->in_val == oldval->in_val) { + //fprintf (stderr, "MOVFF: F2 (%s) already set up correctly (%x) at %p\n", strFromSym (sym2), oldval->in_val, pc); + pic16_safepCodeRemove (pc, "=DF= redundant MOVFF removed"); + } else { + if (!pic16_isAlive (sym1, pc)) { + defmap_t *copy = NULL; + /* If there is another symbol S storing sym1's value we should assign from S thus shortening the liferange of sym1. + * This should help eliminate + * MOVFF A,B + * <do something not changing A or using B> + * MOVFF B,C + * <B is not alive anymore> + * and turn it into + * <do something not changing A or using B> + * MOVFF A,C + */ + + /* scan defmap for symbols storing sym1's value */ + while (oldval && (oldval->pc == pc || oldval->in_val != val->in_val)) oldval = oldval->next; + if (oldval && (oldval->sym != sym1) && defmapFindAll (oldval->sym, pc, ©) == 1) { + /* unique reaching definition for sym found */ + if (copy->val && copy->val == val->in_val) { + //fprintf (stderr, "found replacement symbol for %s (val %x) <-- %s (assigned %x @ %p)\n", strFromSym(sym1), val->in_val, strFromSym(copy->sym), copy->val, copy->pc); + if (copy->sym == SPO_WREG) { + newpc = pic16_newpCode (POC_MOVWF, pic16_pCodeOpCopy (PCOP2(pci->pcop)->pcopR)); + } else { + pCodeOp *pcop = NULL; + /* the code below fails if we try to replace + * MOVFF PRODL, r0x03 + * MOVFF r0x03, PCLATU + * with + * MOVFF PRODL, PCLATU + * as copy(PRODL) contains has pc==NULL, by name fails... + */ + if (!copy->pc || !PCI(copy->pc)->pcop) break; + + if (copy->pc && PCI(copy->pc)->pcop) + pcop = PCI(copy->pc)->pcop; +#if 0 + /* This code is broken--see above. */ + else + { + const char *symname = strFromSym(copy->sym); + + assert( symname ); + pic16_InsertCommentAfter (pc->prev, "BUG-ME"); + pic16_InsertCommentAfter (pc->prev, "=DF= MOVFF: newpCodeOpregFromStr(%s)", (char *)symname); + //pcop = pic16_newpCodeOpRegFromStr((char *)symname); + } +#endif + assert( pcop ); + newpc = pic16_newpCode(POC_MOVFF, pic16_popGet2p( + pcop, + pic16_pCodeOpCopy (PCOP2(pci->pcop)->pcopR))); + } + pic16_InsertCommentAfter (pc->prev, "=DF= MOVFF: SRC op %s replaced by %s", strFromSym(sym1), strFromSym(copy->sym)); + pic16_df_saved_bytes += PCI(pc)->isize - PCI(newpc)->isize; + pic16_pCodeReplace (pc, newpc); + assert (val->sym == sym1 && val->acc.access.isRead && !val->acc.access.isWrite); + defmapReplaceSymRef (pc, sym1, copy->sym, 1); + pic16_fixDefmap (pc, newpc); + pc = newpc; + } + } + deleteDefmapChain (©); + } + } + if (val) defmapUpdate (list, sym2, pc, val->in_val); + } + break; + + default: + /* cannot optimize */ + break; + } // switch +} + +static void pic16_destructDF (pBlock *pb) { + pCode *pc, *next; + + if (!pb) return; + + /* remove old defmaps */ + pc = pic16_findNextInstruction (pb->pcHead); + while (pc) { + next = pic16_findNextInstruction (pc->next); + + assert (isPCI(pc) || isPCAD(pc)); + assert (PCI(pc)->pcflow); + deleteDefmapChain (&PCI(pc)->pcflow->defmap); + deleteDefmapChain (&PCI(pc)->pcflow->in_vals); + deleteDefmapChain (&PCI(pc)->pcflow->out_vals); + + pc = next; + } // while + + if (defmap_free || defmap_free_count) { + //fprintf (stderr, "released defmaps: %u -- freeing up memory\n", defmap_free_count); + freeDefmap (&defmap_free); + defmap_free_count = 0; + } +} + +/* Checks whether a pBlock contains ASMDIRs. */ +static int pic16_pBlockHasAsmdirs (pBlock *pb) { + pCode *pc; + + if (!pb) return FALSE; + + pc = pic16_findNextInstruction (pb->pcHead); + while (pc) { + if (isPCAD(pc)) return TRUE; + + pc = pic16_findNextInstruction (pc->next); + } // while + + /* no PCADs found */ + return FALSE; +} + +#if 1 +/* Remove MOVFF r0x??, POSTDEC1 and MOVFF PREINC1, r0x?? for otherwise unused registers. */ +static int pic16_removeUnusedRegistersDF () { + pCode *pc, *pc2; + pBlock *pb; + reg_info *reg1, *reg2, *reg3; + set *seenRegs = NULL; + int cond, i; + int islocal, change = 0; + + /* no pBlocks? */ + if (!the_pFile || !the_pFile->pbHead) return 0; + + for (pb = the_pFile->pbHead; pb; pb = pb->next) { + //fprintf (stderr, "%s: examining function %s\n", __FUNCTION__, pic16_pBlockGetFunctionName (pb)); +#if 1 + /* find set of using pCodes per register */ + for (pc = pic16_findNextInstruction (pb->pcHead); pc; + pc = pic16_findNextInstruction(pc->next)) { + + cond = PCI(pc)->inCond | PCI(pc)->outCond; + reg1 = reg2 = NULL; + if (cond & PCC_REGISTER) reg1 = pic16_getRegFromInstruction (pc); + if (cond & PCC_REGISTER2) reg2 = pic16_getRegFromInstruction2 (pc); + + if (reg1) { + if (!isinSet (seenRegs, reg1)) reg1->reglives.usedpCodes = NULL; + addSetIfnotP (&seenRegs, reg1); + addSetIfnotP (®1->reglives.usedpCodes, pc); + } + if (reg2) { + if (!isinSet (seenRegs, reg2)) reg2->reglives.usedpCodes = NULL; + addSetIfnotP (&seenRegs, reg2); + addSetIfnotP (®2->reglives.usedpCodes, pc); + } + } // for pc +#endif + for (reg1 = setFirstItem (seenRegs); reg1; reg1 = setNextItem (seenRegs)) { + /* may not use pic16_regIsLocal() here -- in interrupt routines + * WREG, PRODx, FSR0x must be saved */ + islocal = (reg1->isLocal || reg1->rIdx == pic16_framepnt_lo->rIdx || reg1->rIdx == pic16_framepnt_hi->rIdx); + if (islocal && elementsInSet (reg1->reglives.usedpCodes) == 2) { + pc = pc2 = NULL; + for (i=0; i < 2; i++) { + pc = (pCode *) indexSet(reg1->reglives.usedpCodes, i); + if (!pc2) pc2 = pc; + if (!isPCI(pc) || !(PCI(pc)->op == POC_MOVFF)) continue; + reg2 = pic16_getRegFromInstruction (pc); + reg3 = pic16_getRegFromInstruction2 (pc); + if (!reg2 || !reg3 + || (reg2->rIdx != pic16_stack_preinc->rIdx + && reg3->rIdx != pic16_stack_postdec->rIdx)) break; + if (i == 1) { + /* both pCodes are MOVFF R,POSTDEC1 / MOVFF PREINC1,R */ + //fprintf (stderr, "%s: removing local register %s from %s\n", __FUNCTION__, reg1->name, pic16_pBlockGetFunctionName (pb)); + pic16_safepCodeRemove (pc, "removed unused local reg IN"); + pic16_safepCodeRemove (pc2, "removed unused local reg OUT"); + } + } // for + } // if + deleteSet (®1->reglives.usedpCodes); + } // for reg1 + + deleteSet (&seenRegs); + } // for pb + + return change; +} +#endif + +/* Set up pCodeFlow's defmap_ts. + * Needs correctly set up to/from fields. */ +static void pic16_createDF (pBlock *pb) { + pCode *pc, *next; + int change=0; + + if (!pb) return; + + //fprintf (stderr, "creating DF for pb %p (%s)\n", pb, pic16_pBlockGetFunctionName (pb)); + + pic16_destructDF (pb); + + /* check pBlock: do not analyze pBlocks with ASMDIRs (for now...) */ + if (pic16_pBlockHasAsmdirs (pb)) { + //fprintf (stderr, "%s: pBlock contains ASMDIRs -- data flow analysis not performed!\n", __FUNCTION__); + return; + } + + /* integrity check -- we need to reach all flows to guarantee + * correct data flow analysis (reaching definitions, aliveness) */ +#if 0 + if (!verifyAllFlowsReachable (pb)) { + fprintf (stderr, "not all flows reachable -- aborting dataflow analysis for %s!\n", pic16_pBlockGetFunctionName (pb)); + return; + } +#endif + + /* establish new defmaps */ + pc = pic16_findNextInstruction (pb->pcHead); + while (pc) { + next = pic16_findNextInstruction (pc->next); + + assert (PCI(pc)->pcflow); + PCI(pc)->pcflow->defmap = createDefmap (pc, PCI(pc)->pcflow->defmap); + + pc = next; + } // while + + //fprintf (stderr, "%s: creating reaching definitions...\n", __FUNCTION__); + createReachingDefinitions (pb); + +#if 1 + /* assign better valnums */ + //fprintf (stderr, "assigning valnums for pb %p\n", pb); + pc = pic16_findNextInstruction (pb->pcHead); + while (pc) { + next = pic16_findNextInstruction (pc->next); + + assert (PCI(pc)->pcflow); + assignValnums (pc); + + pc = next; + } // while +#endif + +#if 1 + /* remove dead pCodes */ + //fprintf (stderr, "removing dead pCodes in %p (%s)\n", pb, pic16_pBlockGetFunctionName (pb)); + do { + change = 0; + pc = pic16_findNextInstruction (pb->pcHead); + while (pc) { + next = pic16_findNextInstruction (pc->next); + + if (isPCI(pc) && !isPCI_BRANCH(pc) && !pic16_pCodeIsAlive (pc)) { + change += pic16_safepCodeRemove (pc, "=DF= removed dead pCode"); + } + + pc = next; + } // while + } while (change); +#endif +} + +/* ======================================================================== */ +/* === VCG DUMPER ROUTINES ================================================ */ +/* ======================================================================== */ +#if defined (DUMP_DF_GRAPHS) && DUMP_DF_GRAPHS > 0 +hTab *dumpedNodes = NULL; + +/** Dump VCG header into of. */ +static void pic16_vcg_init (FILE *of) { + /* graph defaults */ + fprintf (of, "graph:{\n"); + fprintf (of, "title:\"graph1\"\n"); + fprintf (of, "label:\"graph1\"\n"); + fprintf (of, "color:white\n"); + fprintf (of, "textcolor:black\n"); + fprintf (of, "bordercolor:black\n"); + fprintf (of, "borderwidth:1\n"); + fprintf (of, "textmode:center\n"); + + fprintf (of, "layoutalgorithm:dfs\n"); + fprintf (of, "late_edge_labels:yes\n"); + fprintf (of, "display_edge_labels:yes\n"); + fprintf (of, "dirty_edge_labels:yes\n"); + fprintf (of, "finetuning:yes\n"); + fprintf (of, "ignoresingles:no\n"); + fprintf (of, "straight_phase:yes\n"); + fprintf (of, "priority_phase:yes\n"); + fprintf (of, "manhattan_edges:yes\n"); + fprintf (of, "smanhattan_edges:no\n"); + fprintf (of, "nearedges:no\n"); + fprintf (of, "node_alignment:center\n"); // bottom|top|center + fprintf (of, "port_sharing:no\n"); + fprintf (of, "arrowmode:free\n"); // fixed|free + fprintf (of, "crossingphase2:yes\n"); + fprintf (of, "crossingoptimization:yes\n"); + fprintf (of, "edges:yes\n"); + fprintf (of, "nodes:yes\n"); + fprintf (of, "splines:no\n"); + + /* node defaults */ + fprintf (of, "node.color:lightyellow\n"); + fprintf (of, "node.textcolor:black\n"); + fprintf (of, "node.textmode:center\n"); + fprintf (of, "node.shape:box\n"); + fprintf (of, "node.bordercolor:black\n"); + fprintf (of, "node.borderwidth:1\n"); + + /* edge defaults */ + fprintf (of, "edge.textcolor:black\n"); + fprintf (of, "edge.color:black\n"); + fprintf (of, "edge.thickness:1\n"); + fprintf (of, "edge.arrowcolor:black\n"); + fprintf (of, "edge.backarrowcolor:black\n"); + fprintf (of, "edge.arrowsize:15\n"); + fprintf (of, "edge.backarrowsize:15\n"); + fprintf (of, "edge.arrowstyle:line\n"); // none|solid|line + fprintf (of, "edge.backarrowstyle:none\n"); // none|solid|line + fprintf (of, "edge.linestyle:continuous\n"); // continuous|solid|dotted|dashed|invisible + + fprintf (of, "\n"); + + /* prepare data structures */ + if (dumpedNodes) { + hTabDeleteAll (dumpedNodes); + dumpedNodes = NULL; + } + dumpedNodes = newHashTable (128); +} + +/** Dump VCG footer into of. */ +static void pic16_vcg_close (FILE *of) { + fprintf (of, "}\n"); +} + +#define BUF_SIZE 128 +#define pcTitle(pc) (SNPRINTF (buf, BUF_SIZE, "n_%p, %p/%u", PCODE(pc), isPCI(pc) ? PCI(pc)->pcflow : NULL, PCODE(pc)->seq), &buf[0]) + +#if 0 +static int ptrcmp (const void *p1, const void *p2) { + return p1 == p2; +} +#endif + +/** Dump a pCode node as VCG to of. */ +static void pic16_vcg_dumpnode (pCode *pc, FILE *of) { + char buf[BUF_SIZE]; + + if (hTabFindByKey (dumpedNodes, (((char *) pc - (char *) 0)>>2) % 128, pc, ptrcmp)) { + // dumped already + return; + } + hTabAddItemLong (&dumpedNodes, (((char *) pc - (char *) 0)>>2) % 128, pc, pc); + //fprintf (stderr, "dumping %p\n", pc); + + /* only dump pCodeInstructions and Flow nodes */ + if (!isPCI(pc) && !isPCAD(pc) && !isPCFL(pc)) return; + + /* emit node */ + fprintf (of, "node:{"); + fprintf (of, "title:\"%s\" ", pcTitle(pc)); + fprintf (of, "label:\"%s\n", pcTitle(pc)); + if (isPCFL(pc)) { + fprintf (of, "<PCFLOW>"); + } else if (isPCI(pc) || isPCAD(pc)) { + pc->print (of, pc); + } else { + fprintf (of, "<!PCI>"); + } + fprintf (of, "\" "); + fprintf (of, "}\n"); + + if (1 && isPCFL(pc)) { + defmap_t *map, *prev; + unsigned int i; + map = PCFL(pc)->defmap; + i=0; + while (map) { + if (map->sym != 0) { + i++; + + /* emit definition node */ + fprintf (of, "node:{title:\"%s_def%u\" ", pcTitle(pc), i); + fprintf (of, "label:\""); + + prev = map; + do { + fprintf (of, "%s%c%c: val %4x|%4x & %02x|%02x, sym %s", (prev == map) ? "" : "\n", map->acc.access.isRead ? 'R' : ' ', map->acc.access.isWrite ? 'W' : ' ', map->in_val, map->val, map->acc.access.in_mask, map->acc.access.mask, strFromSym (map->sym)); + prev = map; + map = map->next; + } while (map && prev->pc == map->pc); + map = prev; + + fprintf (of, "\" "); + + fprintf (of, "color:green "); + fprintf (of, "}\n"); + + /* emit edge to previous definition */ + fprintf (of, "edge:{sourcename:\"%s_def%u\" ", pcTitle(pc), i); + if (i == 1) { + fprintf (of, "targetname:\"%s\" ", pcTitle(pc)); + } else { + fprintf (of, "targetname:\"%s_def%u\" ", pcTitle(pc), i-1); + } + fprintf (of, "color:green "); + fprintf (of, "}\n"); + + if (map->pc) { + pic16_vcg_dumpnode (map->pc, of); + fprintf (of, "edge:{sourcename:\"%s_def%u\" ", pcTitle(pc), i); + fprintf (of, "targetname:\"%s\" linestyle:dashed color:lightgreen}\n", pcTitle(map->pc)); + } + } + map = map->next; + } // while + } + + /* emit additional nodes (e.g. operands) */ +} + +/** Dump a pCode's edges (control flow/data flow) as VCG to of. */ +static void pic16_vcg_dumpedges (pCode *pc, FILE *of) { + char buf[BUF_SIZE]; + pCodeInstruction *pci; + pBranch *curr; + int i; + + if (1 && isPCFL(pc)) { + /* emit edges to flow successors */ + void *pcfl; + //fprintf (stderr, "PCFLOWe @ %p\n", pc); + pcfl = setFirstItem (PCFL(pc)->to); + while (pcfl) { + pcfl = ((pCodeFlowLink *) (pcfl))->pcflow; + pic16_vcg_dumpnode (pc, of); + pic16_vcg_dumpnode ((pCode *) pcfl, of); + fprintf (of, "edge:{sourcename:\"%s\" ", pcTitle(pc)); + fprintf (of, "targetname:\"%s\" color:lightred linestyle:dashed}\n", pcTitle(pcfl)); + pcfl = setNextItem (PCFL(pc)->to); + } // while + } // if + + if (!isPCI(pc) && !isPCAD(pc)) return; + + pci = PCI(pc); + + /* emit control flow edges (forward only) */ + curr = pci->to; + i=0; + while (curr) { + pic16_vcg_dumpnode (curr->pc, of); + fprintf (of, "edge:{"); + fprintf (of, "sourcename:\"%s\" ", pcTitle(pc)); + fprintf (of, "targetname:\"%s\" ", pcTitle(curr->pc)); + fprintf (of, "color:red "); + fprintf (of, "}\n"); + curr = curr->next; + } // while + +#if 1 + /* dump "flow" edge (link pCode according to pBlock order) */ + { + pCode *pcnext; + pcnext = pic16_findNextInstruction (pc->next); + if (pcnext) { + pic16_vcg_dumpnode (pcnext, of); + fprintf (of, "edge:{sourcename:\"%s\" ", pcTitle(pc)); + fprintf (of, "targetname:\"%s\" color:red linestyle:solid}\n", pcTitle(pcnext)); + } + } +#endif + +#if 0 + /* emit flow */ + if (pci->pcflow) { + pic16_vcg_dumpnode (&pci->pcflow->pc, of); + fprintf (of, "edge:{sourcename:\"%s\" ", pcTitle(pc)); + fprintf (of, "targetname:\"%s\" color:lightblue linestyle:dashed}\n", pcTitle (pci->pcflow)); + } +#endif + + /* emit data flow edges (backward only) */ + /* TODO: gather data flow information... */ +} + +static void pic16_vcg_dump (FILE *of, pBlock *pb) { + pCode *pc; + + if (!pb) return; + + /* check pBlock: do not analyze pBlocks with ASMDIRs (for now...) */ + if (pic16_pBlockHasAsmdirs (pb)) { + //fprintf (stderr, "%s: pBlock contains ASMDIRs -- data flow analysis not performed!\n", __FUNCTION__); + return; + } + + for (pc=pb->pcHead; pc; pc = pc->next) { + pic16_vcg_dumpnode (pc, of); + } // for pc + + for (pc=pb->pcHead; pc; pc = pc->next) { + pic16_vcg_dumpedges (pc, of); + } // for pc +} + +static void pic16_vcg_dump_default (pBlock *pb) { + FILE *of; + char buf[BUF_SIZE]; + pCode *pc; + + if (!pb) return; + + /* get function name */ + pc = pb->pcHead; + while (pc && !isPCF(pc)) pc = pc->next; + if (pc) { + SNPRINTF (buf, BUF_SIZE, "%s_%s.vcg", PCF(pc)->modname, PCF(pc)->fname); + } else { + SNPRINTF (buf, BUF_SIZE, "pb_%p.vcg", pb); + } + + //fprintf (stderr, "now dumping %s\n", buf); + of = fopen (buf, "w"); + pic16_vcg_init (of); + pic16_vcg_dump (of, pb); + pic16_vcg_close (of); + fclose (of); +} +#endif + +/*** END of helpers for pCode dataflow optimizations ***/ |
