From f98db88f36b08316b34b6c979ee166f05d947c91 Mon Sep 17 00:00:00 2001 From: cuz Date: Mon, 16 Jul 2001 16:32:14 +0000 Subject: [PATCH] Trace register usage, remove duplicate loads git-svn-id: svn://svn.cc65.org/cc65/trunk@793 b7a2c559-68d2-44c3-8de9-860c34a00d81 --- src/cc65/codeopt.c | 1 + src/cc65/coptind.c | 440 +++++++++++++++++++++++++++++++++++++++++++-- src/cc65/coptind.h | 5 +- src/cc65/opcodes.h | 1 + 4 files changed, 435 insertions(+), 12 deletions(-) diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index c91e15ace..470836942 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -1296,6 +1296,7 @@ static OptFunc OptFuncs [] = { { OptCmp5, "OptCmp5", 0 }, /* Remove unused loads */ { OptUnusedLoads, "OptUnusedLoads", 0 }, + { OptDuplicateLoads, "OptDuplicateLoads", 0 }, /* Optimize branch distance */ { OptBranchDist, "OptBranchDist", 0 }, }; diff --git a/src/cc65/coptind.c b/src/cc65/coptind.c index 833116847..4fbad2bbc 100644 --- a/src/cc65/coptind.c +++ b/src/cc65/coptind.c @@ -44,6 +44,32 @@ +/*****************************************************************************/ +/* Macros */ +/*****************************************************************************/ + + + +/* Macro to increment and decrement register contents if they're valid */ +#define INC(reg,val) if ((reg) >= 0) (reg) = ((reg) + val) & 0xFF +#define DEC(reg,val) if ((reg) >= 0) (reg) = ((reg) - val) & 0xFF + + + +/*****************************************************************************/ +/* Helpers */ +/*****************************************************************************/ + + + +static int IsKnownImm (const CodeEntry* E) +/* Return true if the argument of E is a known immediate value */ +{ + return (E->AM == AM65_IMM && (E->Flags & CEF_NUMARG) != 0); +} + + + /*****************************************************************************/ /* Replace jumps to RTS by RTS */ /*****************************************************************************/ @@ -606,20 +632,20 @@ unsigned OptUnusedLoads (CodeSeg* S) /* Check which sort of load or transfer it is */ unsigned R; switch (E->OPC) { - case OP65_TXA: - case OP65_TYA: - case OP65_LDA: R = REG_A; break; - case OP65_TAX: + case OP65_TXA: + case OP65_TYA: + case OP65_LDA: R = REG_A; break; + case OP65_TAX: case OP65_LDX: R = REG_X; break; - case OP65_TAY: - case OP65_LDY: R = REG_Y; break; - default: goto NextEntry; /* OOPS */ + case OP65_TAY: + case OP65_LDY: R = REG_Y; break; + default: goto NextEntry; /* OOPS */ } /* Get register usage and check if the register value is used later */ if ((GetRegInfo (S, I+1) & R) == 0) { - /* Register value is not used, remove the load */ + /* Register value is not used, remove the load */ CS_DelEntry (S, I); /* Remember, we had changes */ @@ -629,8 +655,400 @@ unsigned OptUnusedLoads (CodeSeg* S) } NextEntry: - /* Next entry */ - ++I; + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptDuplicateLoads (CodeSeg* S) +/* Remove loads of registers where the value loaded is already in the register. */ +{ + unsigned Changes = 0; + + /* Remember the last load instructions for all registers */ + int RegA = -1; + int RegX = -1; + int RegY = -1; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + unsigned char Use, Chg; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Assume we won't delete the entry */ + int Delete = 0; + + /* If this entry has a label attached, remove all knownledge about the + * register contents. This may be improved but for now it's ok. + */ + if (CE_HasLabel (E)) { + RegA = RegX = RegY = -1; + } + + /* Handle the different instructions */ + switch (E->OPC) { + + case OP65_ADC: + /* We don't know the value of the carry, so the result is + * always unknown. + */ + RegA = -1; + break; + + case OP65_AND: + if (RegA >= 0) { + if (IsKnownImm (E)) { + RegA &= (int) E->Num; + } else { + RegA = -1; + } + } + break; + + case OP65_ASL: + if (RegA >= 0) { + RegA = (RegA << 1) & 0xFF; + } + break; + + case OP65_BCC: + break; + + case OP65_BCS: + break; + + case OP65_BEQ: + break; + + case OP65_BIT: + break; + + case OP65_BMI: + break; + + case OP65_BNE: + break; + + case OP65_BPL: + break; + + case OP65_BRA: + break; + + case OP65_BRK: + break; + + case OP65_BVC: + break; + + case OP65_BVS: + break; + + case OP65_CLC: + break; + + case OP65_CLD: + break; + + case OP65_CLI: + break; + + case OP65_CLV: + break; + + case OP65_CMP: + break; + + case OP65_CPX: + break; + + case OP65_CPY: + break; + + case OP65_DEA: + DEC (RegA, 1); + break; + + case OP65_DEC: + break; + + case OP65_DEX: + DEC (RegX, 1); + break; + + case OP65_DEY: + DEC (RegY, 1); + break; + + case OP65_EOR: + if (RegA >= 0) { + if (IsKnownImm (E)) { + RegA ^= (int) E->Num; + } else { + RegA = -1; + } + } + break; + + case OP65_INA: + INC (RegA, 1); + break; + + case OP65_INC: + break; + + case OP65_INX: + INC (RegX, 1); + break; + + case OP65_INY: + INC (RegY, 1); + break; + + case OP65_JCC: + break; + + case OP65_JCS: + break; + + case OP65_JEQ: + break; + + case OP65_JMI: + break; + + case OP65_JMP: + break; + + case OP65_JNE: + break; + + case OP65_JPL: + break; + + case OP65_JSR: + /* Get the code info for the function */ + GetFuncInfo (E->Arg, &Use, &Chg); + if (Chg & REG_A) { + RegA = -1; + } + if (Chg & REG_X) { + RegX = -1; + } + if (Chg & REG_Y) { + RegY = -1; + } + break; + + case OP65_JVC: + break; + + case OP65_JVS: + break; + + case OP65_LDA: + if (IsKnownImm (E)) { + CodeEntry* N = CS_GetNextEntry (S, I); + if (RegA >= 0 && RegA == E->Num && N && (N->Info & OF_FBRA) == 0) { + Delete = 1; + } else { + RegA = (unsigned char) E->Num; + } + } else { + /* A is now unknown */ + RegA = -1; + } + break; + + case OP65_LDX: + if (IsKnownImm (E)) { + CodeEntry* N = CS_GetNextEntry (S, I); + if (RegX >= 0 && RegX == E->Num && N && (N->Info & OF_FBRA) == 0) { + Delete = 1; + } else { + RegX = (unsigned char) E->Num; + } + } else { + /* X is now unknown */ + RegX = -1; + } + break; + + case OP65_LDY: + if (IsKnownImm (E)) { + CodeEntry* N = CS_GetNextEntry (S, I); + if (RegY >= 0 && RegY == E->Num && N && (N->Info & OF_FBRA) == 0) { + Delete = 1; + } else { + RegY = (unsigned char) E->Num; + } + } else { + /* Y is now unknown */ + RegY = -1; + } + break; + + case OP65_LSR: + if (RegA >= 0) { + RegA = (RegA >> 1) & 0xFF; + } + break; + + case OP65_NOP: + break; + + case OP65_ORA: + if (RegA >= 0) { + if (IsKnownImm (E)) { + RegA |= (unsigned char) E->Num; + } else { + /* A is now unknown */ + RegA = -1; + } + } + break; + + case OP65_PHA: + break; + + case OP65_PHP: + break; + + case OP65_PHX: + break; + + case OP65_PHY: + break; + + case OP65_PLA: + RegA = -1; + break; + + case OP65_PLP: + break; + + case OP65_PLX: + RegX = -1; + break; + + case OP65_PLY: + RegY = -1; + break; + + case OP65_ROL: + RegA = -1; + break; + + case OP65_ROR: + RegA = -1; + break; + + case OP65_RTI: + break; + + case OP65_RTS: + break; + + case OP65_SBC: + RegA = -1; + break; + + case OP65_SEC: + break; + + case OP65_SED: + break; + + case OP65_SEI: + break; + + case OP65_STA: + break; + + case OP65_STX: + /* If the value in the X register is known and the same as + * that in the A register, replace the store by a STA. The + * optimizer will then remove the load instruction for X + * later. STX does support the zeropage,y addressing mode, + * so be sure to check for that. + */ + if (RegX >= 0 && RegX == RegA && + E->AM != AM65_ABSY && E->AM != AM65_ZPY) { + /* Use the A register instead */ + CE_ReplaceOPC (E, OP65_STA); + } + break; + + case OP65_STY: + /* If the value in the Y register is known and the same as + * that in the A register, replace the store by a STA. The + * optimizer will then remove the load instruction for Y + * later. + */ + if (RegY >= 0 && RegY == RegA) { + CE_ReplaceOPC (E, OP65_STA); + } + break; + + case OP65_TAX: + RegX = RegA; + break; + + case OP65_TAY: + RegY = RegA; + break; + + case OP65_TRB: + break; + + case OP65_TSB: + break; + + case OP65_TSX: + RegX = -1; + break; + + case OP65_TXA: + RegA = RegX; + break; + + case OP65_TXS: + break; + + case OP65_TYA: + RegA = RegY; + break; + + default: + break; + + } + + /* Delete the entry if requested */ + if (Delete) { + + /* Register value is not used, remove the load */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } else { + + /* Next entry */ + ++I; + + } } @@ -678,7 +1096,7 @@ unsigned OptBranchDist (CodeSeg* S) unsigned J = I; while (J < TI) { CodeEntry* N = CS_GetEntry (S, J++); - Distance += N->Size; + Distance += N->Size; } } else { /* Backward branch */ diff --git a/src/cc65/coptind.h b/src/cc65/coptind.h index d0afc5c0f..a5bb5635b 100644 --- a/src/cc65/coptind.h +++ b/src/cc65/coptind.h @@ -51,7 +51,7 @@ unsigned OptRTSJumps (CodeSeg* S); /* Replace jumps to RTS by RTS */ -unsigned OptDeadJumps (CodeSeg* S); +unsigned OptDeadJumps (CodeSeg* S); /* Remove dead jumps (jumps to the next instruction) */ unsigned OptDeadCode (CodeSeg* S); @@ -89,6 +89,9 @@ unsigned OptCondBranches (CodeSeg* S); unsigned OptUnusedLoads (CodeSeg* S); /* Remove loads of registers where the value loaded is not used later. */ +unsigned OptDuplicateLoads (CodeSeg* S); +/* Remove loads of registers where the value loaded is already in the register. */ + unsigned OptBranchDist (CodeSeg* S); /* Change branches for the distance needed. */ diff --git a/src/cc65/opcodes.h b/src/cc65/opcodes.h index c81c978f5..e2369aa19 100644 --- a/src/cc65/opcodes.h +++ b/src/cc65/opcodes.h @@ -169,6 +169,7 @@ typedef enum { AM65_IMM, /* immidiate */ AM65_ZP, /* zeropage */ AM65_ZPX, /* zeropage,X */ + AM65_ZPY, /* zeropage,Y */ AM65_ABS, /* absolute */ AM65_ABSX, /* absolute,X */ AM65_ABSY, /* absolute,Y */