diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index 57cb394e6..77bf2c55c 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -48,6 +48,7 @@ #include "codeent.h" #include "codeinfo.h" #include "coptadd.h" +#include "coptc02.h" #include "coptcmp.h" #include "coptind.h" #include "coptneg.h" @@ -1349,6 +1350,7 @@ struct OptFunc { #define OptFuncEntry(func) static OptFuncDesc D##func = { func, #func, 0 } /* A list of all the function descriptions */ +static OptFunc DOpt65C02Ind = { Opt65C02Ind, "Opt65C02Ind", 0, 0, 0, 0, 0 }; static OptFunc DOptAdd1 = { OptAdd1, "OptAdd1", 0, 0, 0, 0, 0 }; static OptFunc DOptAdd2 = { OptAdd2, "OptAdd2", 0, 0, 0, 0, 0 }; static OptFunc DOptAdd3 = { OptAdd3, "OptAdd3", 0, 0, 0, 0, 0 }; @@ -1401,6 +1403,7 @@ static OptFunc DOptUnusedStores = { OptUnusedStores, "OptUnusedStores", 0, 0, 0, /* Table containing all the steps in alphabetical order */ static OptFunc* OptFuncs[] = { + &DOpt65C02Ind, &DOptAdd1, &DOptAdd2, &DOptAdd3, @@ -1750,7 +1753,24 @@ static void RunOptGroup3 (CodeSeg* S) static void RunOptGroup4 (CodeSeg* S) -/* The last group of optimization steps. Adjust branches. +/* 65C02 specific optimizations. */ +{ + if (CPU < CPU_65C02) { + return; + } + + /* Replace (zp),y by (zp) if Y is zero. If we have changes, run register + * load optimization again, since loads of Y may have become unnecessary. + */ + if (RunOptFunc (S, &DOpt65C02Ind, 1) > 0) { + RunOptFunc (S, &DOptUnusedLoads, 1); + } +} + + + +static void RunOptGroup5 (CodeSeg* S) +/* The last group of optimization steps. Adjust branches, do size optimizations. */ { /* Optimize for size, that is replace operations by shorter ones, even @@ -1805,6 +1825,7 @@ void RunOpt (CodeSeg* S) RunOptGroup2 (S); RunOptGroup3 (S); RunOptGroup4 (S); + RunOptGroup5 (S); /* Write statistics */ if (StatFileName) { diff --git a/src/cc65/codeseg.c b/src/cc65/codeseg.c index 0ba5e0466..bbc21337a 100644 --- a/src/cc65/codeseg.c +++ b/src/cc65/codeseg.c @@ -160,7 +160,7 @@ static void CS_RemoveLabelFromHash (CodeSeg* S, CodeLabel* L) -static CodeLabel* CS_AddLabelInternal (CodeSeg* S, const char* Name, +static CodeLabel* CS_AddLabelInternal (CodeSeg* S, const char* Name, void (*ErrorFunc) (const char*, ...)) /* Add a code label for the next instruction to follow */ { @@ -1109,217 +1109,232 @@ void CS_GenRegInfo (CodeSeg* S) RegContents Regs; /* Initial register contents */ RegContents* CurrentRegs; /* Current register contents */ int WasJump; /* True if last insn was a jump */ + int Done; /* All runs done flag */ /* Be sure to delete all register infos */ CS_FreeRegInfo (S); - /* On entry, the register contents are unknown */ - RC_Invalidate (&Regs); - CurrentRegs = &Regs; + /* We may need two runs to get back references right */ + do { - /* First pass. Walk over all insns and note just the changes from one - * insn to the next one. - */ - WasJump = 0; - for (I = 0; I < CS_GetEntryCount (S); ++I) { + /* Assume we're done after this run */ + Done = 1; + + /* On entry, the register contents are unknown */ + RC_Invalidate (&Regs); + CurrentRegs = &Regs; - CodeEntry* P; - - /* Get the next instruction */ - CodeEntry* E = CollAtUnchecked (&S->Entries, I); - - /* If the instruction has a label, we need some special handling */ - unsigned LabelCount = CE_GetLabelCount (E); - if (LabelCount > 0) { - - /* Loop over all entry points that jump here. If these entry - * points already have register info, check if all values are - * known and identical. If all values are identical, and the - * preceeding instruction was not an unconditional branch, check - * if the register value on exit of the preceeding instruction - * is also identical. If all these values are identical, the - * value of a register is known, otherwise it is unknown. - */ - CodeLabel* Label = CE_GetLabel (E, 0); - unsigned Entry; - if (WasJump) { - /* Preceeding insn was an unconditional branch */ - CodeEntry* J = CL_GetRef(Label, 0); - if (J->RI) { - Regs = J->RI->Out2; - } else { - RC_Invalidate (&Regs); - } - Entry = 1; - } else { - Regs = *CurrentRegs; - Entry = 0; - } - - while (Entry < CL_GetRefCount (Label)) { - /* Get this entry */ - CodeEntry* J = CL_GetRef (Label, Entry); - if (J->RI == 0) { - /* No register info for this entry, bail out */ - RC_Invalidate (&Regs); - break; - } - if (J->RI->Out2.RegA != Regs.RegA) { - Regs.RegA = -1; - } - if (J->RI->Out2.RegX != Regs.RegX) { - Regs.RegX = -1; - } - if (J->RI->Out2.RegY != Regs.RegY) { - Regs.RegY = -1; - } - if (J->RI->Out2.SRegLo != Regs.SRegLo) { - Regs.SRegLo = -1; - } - if (J->RI->Out2.SRegHi != Regs.SRegHi) { - Regs.SRegHi = -1; - } - ++Entry; - } - - /* Use this register info */ - CurrentRegs = &Regs; - - } - - /* Generate register info for this instruction */ - CE_GenRegInfo (E, CurrentRegs); - - /* Remember for the next insn if this insn was an uncondition branch */ - WasJump = (E->Info & OF_UBRA) != 0; - - /* Output registers for this insn are input for the next */ - CurrentRegs = &E->RI->Out; - - /* If this insn is a branch on zero flag, we may have more info on - * register contents for one of both flow directions, but only if - * there is a previous instruction. + /* Walk over all insns and note just the changes from one insn to the + * next one. */ - if ((E->Info & OF_ZBRA) != 0 && (P = CS_GetPrevEntry (S, I)) != 0) { + WasJump = 0; + for (I = 0; I < CS_GetEntryCount (S); ++I) { - /* Get the branch condition */ - bc_t BC = GetBranchCond (E->OPC); + CodeEntry* P; - /* Check the previous instruction */ - switch (P->OPC) { + /* Get the next instruction */ + CodeEntry* E = CollAtUnchecked (&S->Entries, I); - case OP65_ADC: - case OP65_AND: - case OP65_DEA: - case OP65_EOR: - case OP65_INA: - case OP65_LDA: - case OP65_ORA: - case OP65_PLA: - case OP65_SBC: - /* A is zero in one execution flow direction */ - if (BC == BC_EQ) { - E->RI->Out2.RegA = 0; + /* If the instruction has a label, we need some special handling */ + unsigned LabelCount = CE_GetLabelCount (E); + if (LabelCount > 0) { + + /* Loop over all entry points that jump here. If these entry + * points already have register info, check if all values are + * known and identical. If all values are identical, and the + * preceeding instruction was not an unconditional branch, check + * if the register value on exit of the preceeding instruction + * is also identical. If all these values are identical, the + * value of a register is known, otherwise it is unknown. + */ + CodeLabel* Label = CE_GetLabel (E, 0); + unsigned Entry; + if (WasJump) { + /* Preceeding insn was an unconditional branch */ + CodeEntry* J = CL_GetRef(Label, 0); + if (J->RI) { + Regs = J->RI->Out2; } else { - E->RI->Out.RegA = 0; + RC_Invalidate (&Regs); } - break; + Entry = 1; + } else { + Regs = *CurrentRegs; + Entry = 0; + } - case OP65_CMP: - /* If this is an immidiate compare, the A register has - * the value of the compare later. - */ - if (CE_KnownImm (P)) { - if (BC == BC_EQ) { - E->RI->Out2.RegA = (unsigned char)P->Num; - } else { - E->RI->Out.RegA = (unsigned char)P->Num; - } - } - break; - - case OP65_CPX: - /* If this is an immidiate compare, the X register has - * the value of the compare later. - */ - if (CE_KnownImm (P)) { - if (BC == BC_EQ) { - E->RI->Out2.RegX = (unsigned char)P->Num; - } else { - E->RI->Out.RegX = (unsigned char)P->Num; - } - } - break; - - case OP65_CPY: - /* If this is an immidiate compare, the Y register has - * the value of the compare later. - */ - if (CE_KnownImm (P)) { - if (BC == BC_EQ) { - E->RI->Out2.RegY = (unsigned char)P->Num; - } else { - E->RI->Out.RegY = (unsigned char)P->Num; - } - } - break; - - case OP65_DEX: - case OP65_INX: - case OP65_LDX: - case OP65_PLX: - /* X is zero in one execution flow direction */ - if (BC == BC_EQ) { - E->RI->Out2.RegX = 0; - } else { - E->RI->Out.RegX = 0; + while (Entry < CL_GetRefCount (Label)) { + /* Get this entry */ + CodeEntry* J = CL_GetRef (Label, Entry); + if (J->RI == 0) { + /* No register info for this entry. This means that the + * instruction that jumps here is at higher addresses and + * the jump is a backward jump. We need a second run to + * get the register info right in this case. Until then, + * assume unknown register contents. + */ + Done = 0; + RC_Invalidate (&Regs); + break; } - break; - - case OP65_DEY: - case OP65_INY: - case OP65_LDY: - case OP65_PLY: - /* X is zero in one execution flow direction */ - if (BC == BC_EQ) { - E->RI->Out2.RegY = 0; - } else { - E->RI->Out.RegY = 0; + if (J->RI->Out2.RegA != Regs.RegA) { + Regs.RegA = -1; } - break; - - case OP65_TAX: - case OP65_TXA: - /* If the branch is a beq, both A and X are zero at the - * branch target, otherwise they are zero at the next - * insn. - */ - if (BC == BC_EQ) { - E->RI->Out2.RegA = E->RI->Out2.RegX = 0; - } else { - E->RI->Out.RegA = E->RI->Out.RegX = 0; + if (J->RI->Out2.RegX != Regs.RegX) { + Regs.RegX = -1; } - break; - - case OP65_TAY: - case OP65_TYA: - /* If the branch is a beq, both A and Y are zero at the - * branch target, otherwise they are zero at the next - * insn. - */ - if (BC == BC_EQ) { - E->RI->Out2.RegA = E->RI->Out2.RegY = 0; - } else { - E->RI->Out.RegA = E->RI->Out.RegY = 0; + if (J->RI->Out2.RegY != Regs.RegY) { + Regs.RegY = -1; } - break; - - default: - break; + if (J->RI->Out2.SRegLo != Regs.SRegLo) { + Regs.SRegLo = -1; + } + if (J->RI->Out2.SRegHi != Regs.SRegHi) { + Regs.SRegHi = -1; + } + ++Entry; + } + + /* Use this register info */ + CurrentRegs = &Regs; + + } + + /* Generate register info for this instruction */ + CE_GenRegInfo (E, CurrentRegs); + + /* Remember for the next insn if this insn was an uncondition branch */ + WasJump = (E->Info & OF_UBRA) != 0; + /* Output registers for this insn are input for the next */ + CurrentRegs = &E->RI->Out; + + /* If this insn is a branch on zero flag, we may have more info on + * register contents for one of both flow directions, but only if + * there is a previous instruction. + */ + if ((E->Info & OF_ZBRA) != 0 && (P = CS_GetPrevEntry (S, I)) != 0) { + + /* Get the branch condition */ + bc_t BC = GetBranchCond (E->OPC); + + /* Check the previous instruction */ + switch (P->OPC) { + + case OP65_ADC: + case OP65_AND: + case OP65_DEA: + case OP65_EOR: + case OP65_INA: + case OP65_LDA: + case OP65_ORA: + case OP65_PLA: + case OP65_SBC: + /* A is zero in one execution flow direction */ + if (BC == BC_EQ) { + E->RI->Out2.RegA = 0; + } else { + E->RI->Out.RegA = 0; + } + break; + + case OP65_CMP: + /* If this is an immidiate compare, the A register has + * the value of the compare later. + */ + if (CE_KnownImm (P)) { + if (BC == BC_EQ) { + E->RI->Out2.RegA = (unsigned char)P->Num; + } else { + E->RI->Out.RegA = (unsigned char)P->Num; + } + } + break; + + case OP65_CPX: + /* If this is an immidiate compare, the X register has + * the value of the compare later. + */ + if (CE_KnownImm (P)) { + if (BC == BC_EQ) { + E->RI->Out2.RegX = (unsigned char)P->Num; + } else { + E->RI->Out.RegX = (unsigned char)P->Num; + } + } + break; + + case OP65_CPY: + /* If this is an immidiate compare, the Y register has + * the value of the compare later. + */ + if (CE_KnownImm (P)) { + if (BC == BC_EQ) { + E->RI->Out2.RegY = (unsigned char)P->Num; + } else { + E->RI->Out.RegY = (unsigned char)P->Num; + } + } + break; + + case OP65_DEX: + case OP65_INX: + case OP65_LDX: + case OP65_PLX: + /* X is zero in one execution flow direction */ + if (BC == BC_EQ) { + E->RI->Out2.RegX = 0; + } else { + E->RI->Out.RegX = 0; + } + break; + + case OP65_DEY: + case OP65_INY: + case OP65_LDY: + case OP65_PLY: + /* X is zero in one execution flow direction */ + if (BC == BC_EQ) { + E->RI->Out2.RegY = 0; + } else { + E->RI->Out.RegY = 0; + } + break; + + case OP65_TAX: + case OP65_TXA: + /* If the branch is a beq, both A and X are zero at the + * branch target, otherwise they are zero at the next + * insn. + */ + if (BC == BC_EQ) { + E->RI->Out2.RegA = E->RI->Out2.RegX = 0; + } else { + E->RI->Out.RegA = E->RI->Out.RegX = 0; + } + break; + + case OP65_TAY: + case OP65_TYA: + /* If the branch is a beq, both A and Y are zero at the + * branch target, otherwise they are zero at the next + * insn. + */ + if (BC == BC_EQ) { + E->RI->Out2.RegA = E->RI->Out2.RegY = 0; + } else { + E->RI->Out.RegA = E->RI->Out.RegY = 0; + } + break; + + default: + break; + + } } } - } + } while (!Done); + } diff --git a/src/cc65/coptc02.c b/src/cc65/coptc02.c new file mode 100644 index 000000000..61ab10d4f --- /dev/null +++ b/src/cc65/coptc02.c @@ -0,0 +1,110 @@ +/*****************************************************************************/ +/* */ +/* coptc02.h */ +/* */ +/* 65C02 specific optimizations */ +/* */ +/* */ +/* */ +/* (C) 2001 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include + +/* cc65 */ +#include "codeent.h" +#include "codeinfo.h" +#include "error.h" +#include "coptc02.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/*****************************************************************************/ +/* Helper functions */ +/*****************************************************************************/ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +unsigned Opt65C02Ind (CodeSeg* S) +/* Try to use the indirect addressing mode where possible */ +{ + unsigned Changes = 0; + unsigned I; + + /* Generate register info for this step */ + CS_GenRegInfo (S); + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check for addressing mode indirect indexed Y where Y is zero. + * Note: All opcodes that are available as (zp),y are also available + * as (zp), so we can ignore the actual opcode here. + */ + if (E->AM == AM65_ZP_INDY && E->RI->In.RegY == 0) { + + /* Replace it by indirect addressing mode */ + CodeEntry* X = NewCodeEntry (E->OPC, AM65_ZP_IND, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, I+1); + CS_DelEntry (S, I); + + /* We had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Free register info */ + CS_FreeRegInfo (S); + + /* Return the number of changes made */ + return Changes; +} + + + + diff --git a/src/cc65/coptc02.h b/src/cc65/coptc02.h new file mode 100644 index 000000000..4ad470406 --- /dev/null +++ b/src/cc65/coptc02.h @@ -0,0 +1,61 @@ +/*****************************************************************************/ +/* */ +/* coptc02.h */ +/* */ +/* 65C02 specific optimizations */ +/* */ +/* */ +/* */ +/* (C) 2001 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef COPTC02_H +#define COPTC02_H + + + +/* cc65 */ +#include "codeseg.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +unsigned Opt65C02Ind (CodeSeg* S); +/* Try to use the indirect addressing mode where possible */ + + + +/* End of coptc02.h */ +#endif + + + diff --git a/src/cc65/make/gcc.mak b/src/cc65/make/gcc.mak index 9fa674dc4..34ab9af3a 100644 --- a/src/cc65/make/gcc.mak +++ b/src/cc65/make/gcc.mak @@ -35,6 +35,7 @@ OBJS = anonname.o \ codeseg.o \ compile.o \ coptadd.o \ + coptc02.o \ coptcmp.o \ coptind.o \ coptneg.o \ diff --git a/src/cc65/make/watcom.mak b/src/cc65/make/watcom.mak index 80527dd40..2fcc6431b 100644 --- a/src/cc65/make/watcom.mak +++ b/src/cc65/make/watcom.mak @@ -80,6 +80,7 @@ OBJS = anonname.obj \ codeseg.obj \ compile.obj \ coptadd.obj \ + coptc02.obj \ coptcmp.obj \ coptind.obj \ coptneg.obj \ @@ -157,6 +158,7 @@ FILE codeopt.obj FILE codeseg.obj FILE compile.obj FILE coptadd.obj +FILE coptc02.obj FILE coptcmp.obj FILE coptind.obj FILE coptneg.obj