mirror of
https://github.com/cc65/cc65.git
synced 2024-11-19 06:31:31 +00:00
Trace register usage, remove duplicate loads
git-svn-id: svn://svn.cc65.org/cc65/trunk@793 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
parent
38f076a59e
commit
f98db88f36
@ -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 },
|
||||
};
|
||||
|
@ -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 */
|
||||
|
@ -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. */
|
||||
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user