diff --git a/src/cc65/codeent.c b/src/cc65/codeent.c index 928815934..a870ee981 100644 --- a/src/cc65/codeent.c +++ b/src/cc65/codeent.c @@ -2445,6 +2445,24 @@ static char* RegContentDesc (const RegContents* RC, char* Buf) sprintf (B, "Y:%02X ", RC->RegY); } B += 5; + if (RegValIsUnknown (RC->Ptr1Lo)) { + strcpy (B, "P1L:XX "); + } else { + sprintf (B, "P1L:%02X ", RC->Ptr1Lo); + } + B += 7; + if (RegValIsUnknown (RC->Ptr1Hi)) { + strcpy (B, "P1H:XX "); + } else { + sprintf (B, "P1H:%02X ", RC->Ptr1Hi); + } + B += 7; + if (RegValIsUnknown (RC->Tmp1)) { + strcpy (B, "T1:XX "); + } else { + sprintf (B, "T1:%02X ", RC->Tmp1); + } + B += 6; if (PStatesAreUnknown (RC->PFlags, PSTATE_C)) { strcpy (B, "~"); } else { diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index c9c1592bc..a716ad431 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -117,6 +117,7 @@ static OptFunc DOptBNegAX1 = { OptBNegAX1, "OptBNegAX1", 100, 0, static OptFunc DOptBNegAX2 = { OptBNegAX2, "OptBNegAX2", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptBNegAX3 = { OptBNegAX3, "OptBNegAX3", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptBNegAX4 = { OptBNegAX4, "OptBNegAX4", 100, 0, 0, 0, 0, 0 }; +static OptFunc DOptBinOps = { OptBinOps, "OptBinOps", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptBoolCmp = { OptBoolCmp, "OptBoolCmp", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptBoolTrans = { OptBoolTrans, "OptBoolTrans", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptBoolUnary1 = { OptBoolUnary1, "OptBoolUnary1", 40, 0, 0, 0, 0, 0 }; @@ -179,6 +180,7 @@ static OptFunc DOptPush1 = { OptPush1, "OptPush1", 65, 0, static OptFunc DOptPush2 = { OptPush2, "OptPush2", 50, 0, 0, 0, 0, 0 }; static OptFunc DOptPushPop1 = { OptPushPop1, "OptPushPop1", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptPushPop2 = { OptPushPop2, "OptPushPop2", 0, 0, 0, 0, 0, 0 }; +static OptFunc DOptPushPop3 = { OptPushPop3, "OptPushPop3", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptRTS = { OptRTS, "OptRTS", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptRTSJumps1 = { OptRTSJumps1, "OptRTSJumps1", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptRTSJumps2 = { OptRTSJumps2, "OptRTSJumps2", 100, 0, 0, 0, 0, 0 }; @@ -231,6 +233,7 @@ static OptFunc* OptFuncs[] = { &DOptBNegAX2, &DOptBNegAX3, &DOptBNegAX4, + &DOptBinOps, &DOptBoolCmp, &DOptBoolTrans, &DOptBoolUnary1, @@ -292,6 +295,8 @@ static OptFunc* OptFuncs[] = { &DOptPush1, &DOptPush2, &DOptPushPop1, + &DOptPushPop2, + &DOptPushPop3, &DOptRTS, &DOptRTSJumps1, &DOptRTSJumps2, @@ -740,9 +745,11 @@ static unsigned RunOptGroup3 (CodeSeg* S) C += RunOptFunc (S, &DOptStore5, 1); C += RunOptFunc (S, &DOptPushPop1, 1); C += RunOptFunc (S, &DOptPushPop2, 1); + C += RunOptFunc (S, &DOptPushPop3, 1); C += RunOptFunc (S, &DOptPrecalc, 1); C += RunOptFunc (S, &DOptShiftBack, 1); C += RunOptFunc (S, &DOptSignExtended, 1); + C += RunOptFunc (S, &DOptBinOps, 1); Changes += C; @@ -849,6 +856,7 @@ static unsigned RunOptGroup7 (CodeSeg* S) Changes += RunOptFunc (S, &DOptUnusedStores, 1); Changes += RunOptFunc (S, &DOptJumpTarget1, 5); Changes += RunOptFunc (S, &DOptStore5, 1); + Changes += RunOptFunc (S, &DOptTransfers1, 1); } C = RunOptFunc (S, &DOptSize2, 1); diff --git a/src/cc65/coptind.c b/src/cc65/coptind.c index 9e9985c10..49855a345 100644 --- a/src/cc65/coptind.c +++ b/src/cc65/coptind.c @@ -151,7 +151,9 @@ static short ZPRegVal (unsigned short Use, const RegContents* RC) unsigned OptUnusedLoads (CodeSeg* S) -/* Remove loads of registers where the value loaded is not used later. */ +/* Remove loads of or operations with registers where the value loaded or +** produced is not used later. +*/ { unsigned Changes = 0; @@ -164,17 +166,24 @@ unsigned OptUnusedLoads (CodeSeg* S) /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); - /* Check if it's a register load or transfer insn */ - if ((E->Info & (OF_LOAD | OF_XFR | OF_REG_INCDEC)) != 0 && - (N = CS_GetNextEntry (S, I)) != 0 && - !CE_UseLoadFlags (N)) { + /* Check if this is one of the instruction we can operate on */ + int IsOp = (E->Info & (OF_LOAD | OF_XFR | OF_REG_INCDEC)) != 0 || + E->OPC == OP65_AND || + E->OPC == OP65_EOR || + E->OPC == OP65_ORA; + + /* Check for the necessary preconditions */ + if (IsOp && (N = CS_GetNextEntry (S, I)) != 0 && !CE_UseLoadFlags (N)) { /* Check which sort of load or transfer it is */ unsigned R; switch (E->OPC) { + case OP65_AND: case OP65_DEA: + case OP65_EOR: case OP65_INA: case OP65_LDA: + case OP65_ORA: case OP65_TXA: case OP65_TYA: R = REG_A; break; case OP65_DEX: @@ -1342,6 +1351,97 @@ unsigned OptPushPop2 (CodeSeg* S) +unsigned OptPushPop3 (CodeSeg* S) +/* Remove a pha/pla sequence where the contents of A are known */ +{ + unsigned Changes = 0; + unsigned Pha = 0; /* Index of PHA insn */ + unsigned Pla = 0; /* Index of PLA insn */ + CodeEntry* PhaEntry = 0; /* Pointer to PHA */ + + enum { + Searching, + FoundPha, + FoundPla + } State = Searching; + + /* Walk over the entries. Look for a PHA instruction where the contents + ** of A is known followed by a PLA later. + */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Get the input registers */ + const RegInfo* RI = E->RI; + + + const char* Arg; + CodeEntry* X; + switch (State) { + + case Searching: + if (E->OPC == OP65_PHA && RegValIsKnown (RI->In.RegA)) { + /* Found start of sequence */ + Pha = I; + PhaEntry = E; + State = FoundPha; + } + break; + + case FoundPha: + /* Check for several things that abort the sequence: + ** - End of the basic block + ** - Another PHA or any other stack manipulating instruction + ** If we find something that aborts the sequence, start over + ** searching for the next PHA. + */ + if (CE_HasLabel (E)) { + /* Switch back to searching at this instruction */ + State = Searching; + continue; + } + if (E->OPC == OP65_PHA) { + /* Start over at this instruction */ + State = Searching; + continue; + } + if (E->OPC == OP65_PHP || E->OPC == OP65_PLP || E->OPC == OP65_TXS) { + /* Start over at the next instruction */ + State = Searching; + } else if (E->OPC == OP65_PLA) { + /* Switch state. This will also switch to the next insn + ** which is ok. + */ + Pla = I; + State = FoundPla; + } + break; + + case FoundPla: + /* We found the sequence we were looking for. Replace it. */ + Arg = MakeHexArg (PhaEntry->RI->In.RegA); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + CS_InsertEntry (S, X, Pla + 1); + CS_DelEntry (S, Pla); + CS_DelEntry (S, Pha); + ++Changes; + State = Searching; + break; + } + + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} + + + unsigned OptPrecalc (CodeSeg* S) /* Replace immediate operations with the accu where the current contents are ** known by a load of the final value. diff --git a/src/cc65/coptind.h b/src/cc65/coptind.h index 6d39ee1f8..3493543a4 100644 --- a/src/cc65/coptind.h +++ b/src/cc65/coptind.h @@ -50,7 +50,9 @@ unsigned OptUnusedLoads (CodeSeg* S); -/* Remove loads of registers where the value loaded is not used later. */ +/* Remove loads of or operations with registers where the value loaded or +** produced is not used later. +*/ unsigned OptUnusedStores (CodeSeg* S); /* Remove stores into zero page registers that aren't used later */ @@ -91,6 +93,9 @@ unsigned OptPushPop1 (CodeSeg* S); unsigned OptPushPop2 (CodeSeg* S); /* Remove a PHP/PLP sequence were no processor flags changed inside */ +unsigned OptPushPop3 (CodeSeg* S); +/* Remove a pha/pla sequence where the contents of A are known */ + unsigned OptPrecalc (CodeSeg* S); /* Replace immediate operations with the accu where the current contents are ** known by a load of the final value. diff --git a/src/cc65/coptmisc.c b/src/cc65/coptmisc.c index 523fbf17c..332247cf0 100644 --- a/src/cc65/coptmisc.c +++ b/src/cc65/coptmisc.c @@ -733,3 +733,175 @@ unsigned OptLoad2 (CodeSeg* S) /* Return the number of changes made */ return Changes; } + + + +unsigned OptBinOps (CodeSeg* S) +/* Search for an AND/EOR/ORA where the value of A or the operand is known and +** replace it by something simpler. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Get a pointer to the input registers of the insn */ + const RegContents* In = &E->RI->In; + + /* Check for AND/EOR/ORA and a known value in A */ + int Delete = 0; + CodeEntry* X = 0; + switch (E->OPC) { + + case OP65_AND: + if (In->RegA == 0x00) { + /* Zero AND anything gives zero. The instruction can be + ** replaced by an immediate load of zero. + */ + X = NewCodeEntry (OP65_LDA, AM65_IMM, "$00", 0, E->LI); + } else if (In->RegA == 0xFF) { + /* 0xFF AND anything equals the operand. The instruction + ** can be replaced by a simple load of the operand. + */ + X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI); + } else if (E->AM == AM65_ZP) { + short Operand = -1; + switch (GetKnownReg (E->Use & REG_ZP, In)) { + case REG_TMP1: Operand = In->Tmp1; break; + case REG_PTR1_LO: Operand = In->Ptr1Lo; break; + case REG_PTR1_HI: Operand = In->Ptr1Hi; break; + case REG_SREG_LO: Operand = In->SRegLo; break; + case REG_SREG_HI: Operand = In->SRegHi; break; + } + if (Operand == 0x00) { + /* AND with zero gives zero. The instruction can be + ** replaced by an immediate load of zero. + */ + X = NewCodeEntry (OP65_LDA, AM65_IMM, "$00", 0, E->LI); + } else if (Operand == 0xFF) { + /* AND with 0xFF is a no-op besides setting the flags. + ** The instruction can be removed if the flags aren't + ** used later. + */ + CodeEntry* N = CS_GetNextEntry (S, I); + if (N && !CE_UseLoadFlags (N)) { + Delete = 1; + } + } else if (Operand >= 0) { + /* The instruction can be replaced by an immediate + ** AND. + */ + const char* Arg = MakeHexArg (Operand); + X = NewCodeEntry (OP65_AND, AM65_IMM, Arg, 0, E->LI); + } + } + break; + + case OP65_EOR: + if (In->RegA == 0x00) { + /* Zero EOR anything equals the operand. The instruction + ** can be replaced by a simple load. + */ + X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI); + } else if (E->AM == AM65_ZP) { + short Operand = -1; + switch (GetKnownReg (E->Use & REG_ZP, In)) { + case REG_TMP1: Operand = In->Tmp1; break; + case REG_PTR1_LO: Operand = In->Ptr1Lo; break; + case REG_PTR1_HI: Operand = In->Ptr1Hi; break; + case REG_SREG_LO: Operand = In->SRegLo; break; + case REG_SREG_HI: Operand = In->SRegHi; break; + } + if (Operand == 0x00) { + /* EOR with 0x00 is a no-op besides setting the flags. + ** The instruction can be removed if the flags aren't + ** used later. + */ + CodeEntry* N = CS_GetNextEntry (S, I); + if (N && !CE_UseLoadFlags (N)) { + Delete = 1; + } + } else if (Operand >= 0) { + /* The instruction can be replaced by an immediate + ** EOR. + */ + const char* Arg = MakeHexArg (Operand); + X = NewCodeEntry (OP65_EOR, AM65_IMM, Arg, 0, E->LI); + } + } + break; + + case OP65_ORA: + if (In->RegA == 0x00) { + /* ORA with 0x00 is a no-op. The instruction can be + ** replaced by a simple load. + */ + X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI); + } else if (In->RegA == 0xFF) { + /* ORA with 0xFF gives 0xFF. The instruction can be replaced + ** by an immediate load of 0xFF. + */ + X = NewCodeEntry (OP65_LDA, AM65_IMM, "$FF", 0, E->LI); + } else if (E->AM == AM65_ZP) { + short Operand = -1; + switch (GetKnownReg (E->Use & REG_ZP, In)) { + case REG_TMP1: Operand = In->Tmp1; break; + case REG_PTR1_LO: Operand = In->Ptr1Lo; break; + case REG_PTR1_HI: Operand = In->Ptr1Hi; break; + case REG_SREG_LO: Operand = In->SRegLo; break; + case REG_SREG_HI: Operand = In->SRegHi; break; + } + if (Operand == 0x00) { + /* ORA with 0x00 is a no-op besides setting the flags. + ** The instruction can be removed if the flags aren't + ** used later. + */ + CodeEntry* N = CS_GetNextEntry (S, I); + if (N && !CE_UseLoadFlags (N)) { + Delete = 1; + } + } else if (Operand == 0xFF) { + /* ORA with 0xFF results in 0xFF. The instruction can + ** be replaced by a simple load. + */ + X = NewCodeEntry (OP65_LDA, AM65_IMM, "$FF", 0, E->LI); + } else if (Operand >= 0) { + /* The instruction can be replaced by an immediate + ** ORA. + */ + const char* Arg = MakeHexArg (Operand); + X = NewCodeEntry (OP65_ORA, AM65_IMM, Arg, 0, E->LI); + } + } + break; + + default: + break; + + } + + /* If we must delete the instruction, do that. If we have a replacement + ** entry, place it and remove the old one. + */ + if (X) { + CS_InsertEntry (S, X, I+1); + Delete = 1; + } + if (Delete) { + CS_DelEntry (S, I); + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} diff --git a/src/cc65/coptmisc.h b/src/cc65/coptmisc.h index 89242351c..418b61e94 100644 --- a/src/cc65/coptmisc.h +++ b/src/cc65/coptmisc.h @@ -107,6 +107,12 @@ unsigned OptLoad1 (CodeSeg* S); unsigned OptLoad2 (CodeSeg* S); /* Replace calls to ldaxysp by inline code */ +unsigned OptBinOps (CodeSeg* S); +/* Search for an AND/EOR/ORA where the value of A or the operand is known and +** replace it by something simpler. +*/ + + /* End of coptmisc.h */