diff --git a/src/cc65.vcxproj b/src/cc65.vcxproj index 556c616b0..6f3f8e4e4 100644 --- a/src/cc65.vcxproj +++ b/src/cc65.vcxproj @@ -64,12 +64,12 @@ + - @@ -79,6 +79,7 @@ + @@ -144,12 +145,12 @@ + - @@ -159,6 +160,7 @@ + diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index 208ada134..baa44f99e 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -52,12 +52,12 @@ #include "codeinfo.h" #include "codeopt.h" #include "coptadd.h" +#include "coptbool.h" #include "coptc02.h" #include "coptcmp.h" #include "coptind.h" #include "coptjmp.h" #include "coptmisc.h" -#include "coptneg.h" #include "coptptrload.h" #include "coptptrstore.h" #include "coptpush.h" @@ -67,6 +67,7 @@ #include "coptstore.h" #include "coptsub.h" #include "copttest.h" +#include "coptunary.h" #include "error.h" #include "global.h" #include "output.h" @@ -115,7 +116,11 @@ 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 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 }; +static OptFunc DOptBoolUnary2 = { OptBoolUnary2, "OptBoolUnary2", 40, 0, 0, 0, 0, 0 }; +static OptFunc DOptBoolUnary3 = { OptBoolUnary3, "OptBoolUnary3", 40, 0, 0, 0, 0, 0 }; static OptFunc DOptBranchDist = { OptBranchDist, "OptBranchDist", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptBranchDist2 = { OptBranchDist2, "OptBranchDist2", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptCmp1 = { OptCmp1, "OptCmp1", 42, 0, 0, 0, 0, 0 }; @@ -123,13 +128,14 @@ static OptFunc DOptCmp2 = { OptCmp2, "OptCmp2", 85, 0, static OptFunc DOptCmp3 = { OptCmp3, "OptCmp3", 75, 0, 0, 0, 0, 0 }; static OptFunc DOptCmp4 = { OptCmp4, "OptCmp4", 75, 0, 0, 0, 0, 0 }; static OptFunc DOptCmp5 = { OptCmp5, "OptCmp5", 100, 0, 0, 0, 0, 0 }; -static OptFunc DOptCmp6 = { OptCmp6, "OptCmp6", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptCmp7 = { OptCmp7, "OptCmp7", 85, 0, 0, 0, 0, 0 }; static OptFunc DOptCmp8 = { OptCmp8, "OptCmp8", 50, 0, 0, 0, 0, 0 }; static OptFunc DOptCmp9 = { OptCmp9, "OptCmp9", 85, 0, 0, 0, 0, 0 }; static OptFunc DOptComplAX1 = { OptComplAX1, "OptComplAX1", 65, 0, 0, 0, 0, 0 }; -static OptFunc DOptCondBranches1= { OptCondBranches1,"OptCondBranches1", 80, 0, 0, 0, 0, 0 }; -static OptFunc DOptCondBranches2= { OptCondBranches2,"OptCondBranches2", 0, 0, 0, 0, 0, 0 }; +static OptFunc DOptCondBranch1 = { OptCondBranch1, "OptCondBranch1", 80, 0, 0, 0, 0, 0 }; +static OptFunc DOptCondBranch2 = { OptCondBranch2, "OptCondBranch2", 40, 0, 0, 0, 0, 0 }; +static OptFunc DOptCondBranch3 = { OptCondBranch3, "OptCondBranch3", 40, 0, 0, 0, 0, 0 }; +static OptFunc DOptCondBranchC = { OptCondBranchC, "OptCondBranchC", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptDeadCode = { OptDeadCode, "OptDeadCode", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptDeadJumps = { OptDeadJumps, "OptDeadJumps", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptDecouple = { OptDecouple, "OptDecouple", 100, 0, 0, 0, 0, 0 }; @@ -221,7 +227,11 @@ static OptFunc* OptFuncs[] = { &DOptBNegAX2, &DOptBNegAX3, &DOptBNegAX4, + &DOptBoolCmp, &DOptBoolTrans, + &DOptBoolUnary1, + &DOptBoolUnary2, + &DOptBoolUnary3, &DOptBranchDist, &DOptBranchDist2, &DOptCmp1, @@ -229,13 +239,14 @@ static OptFunc* OptFuncs[] = { &DOptCmp3, &DOptCmp4, &DOptCmp5, - &DOptCmp6, &DOptCmp7, &DOptCmp8, &DOptCmp9, &DOptComplAX1, - &DOptCondBranches1, - &DOptCondBranches2, + &DOptCondBranch1, + &DOptCondBranch2, + &DOptCondBranch3, + &DOptCondBranchC, &DOptDeadCode, &DOptDeadJumps, &DOptDecouple, @@ -612,10 +623,6 @@ static unsigned RunOptGroup1 (CodeSeg* S) Changes += RunOptFunc (S, &DOptPtrLoad15, 1); Changes += RunOptFunc (S, &DOptPtrLoad16, 1); Changes += RunOptFunc (S, &DOptPtrLoad17, 1); - Changes += RunOptFunc (S, &DOptBNegAX1, 1); - Changes += RunOptFunc (S, &DOptBNegAX2, 1); - Changes += RunOptFunc (S, &DOptBNegAX3, 1); - Changes += RunOptFunc (S, &DOptBNegAX4, 1); Changes += RunOptFunc (S, &DOptAdd1, 1); Changes += RunOptFunc (S, &DOptAdd2, 1); Changes += RunOptFunc (S, &DOptAdd4, 1); @@ -668,11 +675,15 @@ static unsigned RunOptGroup3 (CodeSeg* S) do { C = 0; - C += RunOptFunc (S, &DOptBNegA1, 1); - C += RunOptFunc (S, &DOptBNegA2, 1); C += RunOptFunc (S, &DOptNegAX1, 1); C += RunOptFunc (S, &DOptNegAX2, 1); - C += RunOptFunc (S, &DOptStackOps, 3); + C += RunOptFunc (S, &DOptStackOps, 3); /* Before OptBoolUnary1 */ + C += RunOptFunc (S, &DOptCmp8, 1); /* Before OptBoolUnary1 */ + C += RunOptFunc (S, &DOptBoolUnary1, 3); + C += RunOptFunc (S, &DOptBoolUnary2, 3); + C += RunOptFunc (S, &DOptBoolUnary3, 1); + C += RunOptFunc (S, &DOptBNegA1, 1); + C += RunOptFunc (S, &DOptBNegAX1, 1); /* After OptBoolUnary2 */ C += RunOptFunc (S, &DOptShift1, 1); C += RunOptFunc (S, &DOptShift4, 1); C += RunOptFunc (S, &DOptComplAX1, 1); @@ -684,24 +695,30 @@ static unsigned RunOptGroup3 (CodeSeg* S) C += RunOptFunc (S, &DOptJumpCascades, 1); C += RunOptFunc (S, &DOptDeadJumps, 1); C += RunOptFunc (S, &DOptDeadCode, 1); - C += RunOptFunc (S, &DOptBoolTrans, 1); C += RunOptFunc (S, &DOptJumpTarget1, 1); C += RunOptFunc (S, &DOptJumpTarget2, 1); - C += RunOptFunc (S, &DOptCondBranches1, 1); - C += RunOptFunc (S, &DOptCondBranches2, 1); + C += RunOptFunc (S, &DOptCondBranch1, 1); + C += RunOptFunc (S, &DOptCondBranch2, 1); + C += RunOptFunc (S, &DOptCondBranch3, 1); + C += RunOptFunc (S, &DOptCondBranchC, 1); C += RunOptFunc (S, &DOptRTSJumps1, 1); + C += RunOptFunc (S, &DOptBoolCmp, 1); + C += RunOptFunc (S, &DOptBoolTrans, 1); + C += RunOptFunc (S, &DOptBNegA2, 1); /* After OptCondBranch's */ + C += RunOptFunc (S, &DOptBNegAX2, 1); /* After OptCondBranch's */ + C += RunOptFunc (S, &DOptBNegAX3, 1); /* After OptCondBranch's */ + C += RunOptFunc (S, &DOptBNegAX4, 1); /* After OptCondBranch's */ C += RunOptFunc (S, &DOptCmp1, 1); C += RunOptFunc (S, &DOptCmp2, 1); - C += RunOptFunc (S, &DOptCmp8, 1); /* Must run before OptCmp3 */ + C += RunOptFunc (S, &DOptCmp8, 1); /* Must run before OptCmp3 */ C += RunOptFunc (S, &DOptCmp3, 1); C += RunOptFunc (S, &DOptCmp4, 1); C += RunOptFunc (S, &DOptCmp5, 1); - C += RunOptFunc (S, &DOptCmp6, 1); C += RunOptFunc (S, &DOptCmp7, 1); C += RunOptFunc (S, &DOptCmp9, 1); C += RunOptFunc (S, &DOptTest1, 1); C += RunOptFunc (S, &DOptLoad1, 1); - C += RunOptFunc (S, &DOptJumpTarget3, 1); /* After OptCondBranches2 */ + C += RunOptFunc (S, &DOptJumpTarget3, 1); /* After OptCondBranches2 */ C += RunOptFunc (S, &DOptUnusedLoads, 1); C += RunOptFunc (S, &DOptUnusedStores, 1); C += RunOptFunc (S, &DOptDupLoads, 1); diff --git a/src/cc65/coptbool.c b/src/cc65/coptbool.c new file mode 100644 index 000000000..3a3b3fa7c --- /dev/null +++ b/src/cc65/coptbool.c @@ -0,0 +1,942 @@ +/*****************************************************************************/ +/* */ +/* coptbool.c */ +/* */ +/* Optimize boolean sequences */ +/* */ +/* */ +/* */ +/* (C) 2001-2012, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* 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. */ +/* */ +/*****************************************************************************/ + + + +/* cc65 */ +#include "codeent.h" +#include "codeinfo.h" +#include "error.h" +#include "coptbool.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Table used to invert a condition, indexed by condition */ +static const unsigned char CmpInvertTab[] = { + CMP_NE, CMP_EQ, + CMP_LE, CMP_LT, CMP_GE, CMP_GT, + CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT +}; + + + +/*****************************************************************************/ +/* Helper functions */ +/*****************************************************************************/ + + + +static void ReplaceBranchCond (CodeSeg* S, unsigned I, cmp_t Cond) +/* Helper function for the replacement of routines that return a boolean +** followed by a conditional jump. Instead of the boolean value, the condition +** codes are evaluated directly. +** I is the index of the conditional branch, the sequence is already checked +** to be correct. +*/ +{ + CodeEntry* N; + CodeLabel* L; + + /* Get the entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Replace the conditional branch */ + switch (Cond) { + + case CMP_EQ: + CE_ReplaceOPC (E, OP65_JEQ); + break; + + case CMP_NE: + CE_ReplaceOPC (E, OP65_JNE); + break; + + case CMP_GT: + /* Replace by + ** beq @L + ** jpl Target + ** @L: ... + */ + if ((N = CS_GetNextEntry (S, I)) == 0) { + /* No such entry */ + Internal ("Invalid program flow"); + } + L = CS_GenLabel (S, N); + N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI); + CS_InsertEntry (S, N, I); + CE_ReplaceOPC (E, OP65_JPL); + break; + + case CMP_GE: + CE_ReplaceOPC (E, OP65_JPL); + break; + + case CMP_LT: + CE_ReplaceOPC (E, OP65_JMI); + break; + + case CMP_LE: + /* Replace by + ** jmi Target + ** jeq Target + */ + CE_ReplaceOPC (E, OP65_JMI); + L = E->JumpTo; + N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI); + CS_InsertEntry (S, N, I+1); + break; + + case CMP_UGT: + /* Replace by + ** beq @L + ** jcs Target + ** @L: ... + */ + if ((N = CS_GetNextEntry (S, I)) == 0) { + /* No such entry */ + Internal ("Invalid program flow"); + } + L = CS_GenLabel (S, N); + N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI); + CS_InsertEntry (S, N, I); + CE_ReplaceOPC (E, OP65_JCS); + break; + + case CMP_UGE: + CE_ReplaceOPC (E, OP65_JCS); + break; + + case CMP_ULT: + CE_ReplaceOPC (E, OP65_JCC); + break; + + case CMP_ULE: + /* Replace by + ** jcc Target + ** jeq Target + */ + CE_ReplaceOPC (E, OP65_JCC); + L = E->JumpTo; + N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI); + CS_InsertEntry (S, N, I+1); + break; + + default: + Internal ("Unknown jump condition: %d", Cond); + + } + +} + + + +/*****************************************************************************/ +/* Optimize bool comparison and transformer subroutines with branches */ +/*****************************************************************************/ + + + +unsigned OptBoolCmp (CodeSeg* S) +/* Search for calls to compare subroutines followed by a conditional branch +** and replace them by cheaper versions, since the branch means that the +** boolean value returned by these routines is not needed (we may also check +** that explicitly, but for the current code generator it is always true). +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + cmp_t Cond; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check for the sequence */ + if (E->OPC == OP65_JSR && + (Cond = FindTosCmpCond (E->Arg)) != CMP_INV && + (N = CS_GetNextEntry (S, I)) != 0 && + (N->Info & OF_ZBRA) != 0 && + !CE_HasLabel (N)) { + + /* The tos... functions will return a boolean value in a/x and + ** the Z flag says if this value is zero or not. We will call + ** a cheaper subroutine instead, one that does not return a + ** boolean value but only valid flags. Note: jeq jumps if + ** the condition is not met, jne jumps if the condition is met. + ** Invert the code if we jump on condition not met. + */ + if (GetBranchCond (N->OPC) == BC_EQ) { + /* Jumps if condition false, invert condition */ + Cond = CmpInvertTab [Cond]; + } + + /* Replace the subroutine call. */ + E = NewCodeEntry (OP65_JSR, AM65_ABS, "tosicmp", 0, E->LI); + CS_InsertEntry (S, E, I+1); + CS_DelEntry (S, I); + + /* Replace the conditional branch */ + ReplaceBranchCond (S, I+1, Cond); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptBoolTrans (CodeSeg* S) +/* Try to remove the call to boolean transformer routines where the call is +** not really needed and change following branch condition accordingly. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + cmp_t Cond; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check for a boolean transformer */ + if (E->OPC == OP65_JSR && + (Cond = FindBoolCmpCond (E->Arg)) != CMP_INV && + (N = CS_GetNextEntry (S, I)) != 0 && + (N->Info & OF_ZBRA) != 0 && + (GetRegInfo (S, I + 2, PSTATE_Z) & PSTATE_Z) == 0) { + + /* Make the boolean transformer unnecessary by changing the + ** the conditional jump to evaluate the condition flags that + ** are set after the compare directly. Note: jeq jumps if + ** the condition is not met, jne jumps if the condition is met. + ** Invert the code if we jump on condition not met. + */ + if (GetBranchCond (N->OPC) == BC_EQ) { + /* Jumps if condition false, invert condition */ + Cond = CmpInvertTab [Cond]; + } + + /* Check if we can replace the code by something better */ + ReplaceBranchCond (S, I+1, Cond); + + /* Remove the call to the bool transformer */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptBoolUnary (CodeSeg* S) +/* Try to remove the call to a bcastax/bnegax routines where the call is +** not really needed and change following branch condition accordingly. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + cmp_t Cond; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check for a boolean transformer */ + if (E->OPC == OP65_JSR && + (Cond = FindBoolCmpCond (E->Arg)) != CMP_INV && + (N = CS_GetNextEntry (S, I)) != 0 && + (N->Info & OF_ZBRA) != 0) { + + /* Make the boolean transformer unnecessary by changing the + ** the conditional jump to evaluate the condition flags that + ** are set after the compare directly. Note: jeq jumps if + ** the condition is not met, jne jumps if the condition is met. + ** Invert the code if we jump on condition not met. + */ + if (GetBranchCond (N->OPC) == BC_EQ) { + /* Jumps if condition false, invert condition */ + Cond = CmpInvertTab [Cond]; + } + + /* Check if we can replace the code by something better */ + ReplaceBranchCond (S, I+1, Cond); + + /* Remove the call to the bool transformer */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Remove calls to the boolean cast/negation subroutines */ +/*****************************************************************************/ + + + +unsigned OptBoolUnary1 (CodeSeg* S) +/* Search for and remove cmp #0/bcastax/boolne following a bcastax/bnegax. +** Or search for and remove cmp #1/bnegax/booleq following a bcastax/bnegax +** and invert the bcastax/bnegax. +*/ +{ + unsigned Changes = 0; + int Neg = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[2]; + + /* Get next entry */ + L[0] = CS_GetEntry (S, I); + + /* Check for the sequence. + ** We allow the first entry to have labels. + */ + if (L[0]->OPC == OP65_JSR && + (L[1] = CS_GetNextEntry (S, I)) != 0 && + !CE_HasLabel (L[1])) { + if (strcmp (L[0]->Arg, "bnegax") == 0) { + Neg = 1; + } else if (strcmp (L[0]->Arg, "bcastax") == 0) { + Neg = 0; + } else { + /* Next entry */ + ++I; + continue; + } + if ((L[1]->OPC == OP65_CMP && CE_IsKnownImm (L[1], 0x0)) || + CE_IsCallTo (L[1], "boolne") || + CE_IsCallTo (L[1], "bcastax")) { + /* Delete the entry no longer needed. */ + CS_DelEntry (S, I + 1); + + /* Remember, we had changes */ + ++Changes; + + /* We are still at this index */ + continue; + + } else if ((L[1]->OPC == OP65_CMP && CE_IsKnownImm (L[1], 0x1)) || + CE_IsCallTo (L[1], "booleq") || + CE_IsCallTo (L[1], "bnegax")) { + /* Invert the previous bool conversion */ + CE_SetArg (L[0], Neg ? "bcastax" : "bnegax"); + + /* Delete the entry no longer needed */ + CS_DelEntry (S, I + 1); + + /* Remember, we had changes */ + ++Changes; + + /* We are still at this index */ + continue; + } + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptBoolUnary2 (CodeSeg* S) +/* Search for and remove cmp #0/bcastax/boolne following a boolean transformer. +** Or search for and remove cmp #1/bnegax/booleq following a boolean transformer +** and invert the boolean transformer. +*/ +{ + unsigned Changes = 0; + cmp_t Cond; + char Buf[16]; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[2]; + + /* Get next entry */ + L[0] = CS_GetEntry (S, I); + + /* Check for the sequence. + ** We allow the first entry to have labels. + */ + if (L[0]->OPC == OP65_JSR && + (L[1] = CS_GetNextEntry (S, I)) != 0 && + !CE_HasLabel (L[1]) && + (Cond = FindBoolCmpCond (L[0]->Arg)) != CMP_INV) { + if ((L[1]->OPC == OP65_CMP && CE_IsKnownImm (L[1], 0x0)) || + CE_IsCallTo (L[1], "boolne") || + CE_IsCallTo (L[1], "bcastax")) { + /* Delete the entry no longer needed */ + CS_DelEntry (S, I + 1); + + /* Remember, we had changes */ + ++Changes; + + /* We are still at this index */ + continue; + + } else if ((L[1]->OPC == OP65_CMP && CE_IsKnownImm (L[1], 0x1)) || + CE_IsCallTo (L[1], "booleq") || + CE_IsCallTo (L[1], "bnegax")) { + /* Invert the bool conversion */ + if (GetBoolCmpSuffix (Buf, GetNegatedCond (Cond)) == 0) { + Internal ("No inverted boolean transformer for: %s", L[0]->Arg); + } + CE_SetArg (L[0], Buf); + + /* Delete the entry no longer needed */ + CS_DelEntry (S, I + 1); + + /* Remember, we had changes */ + ++Changes; + + /* We are still at this index */ + continue; + } + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptBoolUnary3 (CodeSeg* S) +/* If A == 0, replace bcastax/bnegax with +** +** cpx #0 +** jsr boolne/booleq +** +** Or if X == 0, replace bcastax/bnegax with +** +** cmp #0 +** jsr boolne/booleq +** +*/ +{ + unsigned Changes = 0; + opc_t Op = OP65_COUNT; + const char* Sub = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* E; + CodeEntry* X; + + /* Get next entry */ + E = CS_GetEntry (S, I); + + /* Check for the sequence */ + if (!CE_HasLabel (E)) { + /* Choose the right subroutine */ + if (CE_IsCallTo (E, "bnegax")) { + Sub = "booleq"; + } else if (CE_IsCallTo (E, "bcastax")) { + Sub = "boolne"; + } + /* Choose the right opcode */ + if (RegValIsKnown (E->RI->In.RegA) && E->RI->In.RegA == 0) { + Op = OP65_CPX; + } else if (RegValIsKnown (E->RI->In.RegX) && E->RI->In.RegX == 0) { + Op = OP65_CMP; + } + /* Replace the sequence if all requirements are met*/ + if (Op != OP65_COUNT && Sub != 0) { + /* Replace bcastax/bnegax with boolne/booleq */ + CE_SetArg (E, Sub); + + /* Insert the compare */ + X = NewCodeEntry (Op, AM65_IMM, "$00", 0, E->LI); + CS_InsertEntry (S, X, I); + + /* Remember, we had changes */ + ++Changes; + + /* Correct the index */ + ++I; + } + + /* Reset the choices */ + Op = OP65_COUNT; + Sub = 0; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* bnega optimizations */ +/*****************************************************************************/ + + + +unsigned OptBNegA1 (CodeSeg* S) +/* Check for +** +** ldx #$00 +** lda .. +** jsr bnega +** +** Remove the ldx if the lda does not use it. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[2]; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check for a ldx */ + if (E->OPC == OP65_LDX && + E->AM == AM65_IMM && + CE_HasNumArg (E) && + E->Num == 0 && + CS_GetEntries (S, L, I+1, 2) && + L[0]->OPC == OP65_LDA && + (L[0]->Use & REG_X) == 0 && + !CE_HasLabel (L[0]) && + CE_IsCallTo (L[1], "bnega") && + !CE_HasLabel (L[1])) { + + /* Remove the ldx instruction */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptBNegA2 (CodeSeg* S) +/* Check for +** +** lda .. +** jsr bnega +** jeq/jne .. +** +** Adjust the conditional branch and remove the call to the subroutine. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[2]; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check for the sequence */ + if ((E->OPC == OP65_ADC || + E->OPC == OP65_AND || + E->OPC == OP65_DEA || + E->OPC == OP65_EOR || + E->OPC == OP65_INA || + E->OPC == OP65_LDA || + E->OPC == OP65_ORA || + E->OPC == OP65_PLA || + E->OPC == OP65_SBC || + E->OPC == OP65_TXA || + E->OPC == OP65_TYA) && + CS_GetEntries (S, L, I+1, 2) && + CE_IsCallTo (L[0], "bnega") && + !CE_HasLabel (L[0]) && + (L[1]->Info & OF_ZBRA) != 0 && + !CE_HasLabel (L[1]) && + (GetRegInfo (S, I + 3, PSTATE_Z) & PSTATE_Z) == 0) { + + /* Invert the branch */ + CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC)); + + /* Delete the subroutine call */ + CS_DelEntry (S, I+1); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* bnegax optimizations */ +/*****************************************************************************/ + + + +unsigned OptBNegAX1 (CodeSeg* S) +/* On a call to bnegax, if X is zero, the result depends only on the value in +** A, so change the call to a call to bnega. This will get further optimized +** later if possible. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if this is a call to bnegax, and if X is known and zero */ + if (E->RI->In.RegX == 0 && CE_IsCallTo (E, "bnegax")) { + + CodeEntry* X = NewCodeEntry (OP65_JSR, AM65_ABS, "bnega", 0, E->LI); + CS_InsertEntry (S, X, I+1); + CS_DelEntry (S, I); + + /* We had changes */ + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptBNegAX2 (CodeSeg* S) +/* Search for the sequence: +** +** ldy #xx +** jsr ldaxysp +** jsr bnegax +** jne/jeq ... +** +** and replace it by +** +** ldy #xx +** lda (sp),y +** dey +** ora (sp),y +** jeq/jne ... +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[4]; + + /* Get next entry */ + L[0] = CS_GetEntry (S, I); + + /* Check for the sequence */ + if (L[0]->OPC == OP65_LDY && + CE_IsConstImm (L[0]) && + !CS_RangeHasLabel (S, I+1, 3) && + CS_GetEntries (S, L+1, I+1, 3) && + CE_IsCallTo (L[1], "ldaxysp") && + CE_IsCallTo (L[2], "bnegax") && + (L[3]->Info & OF_ZBRA) != 0 && + (GetRegInfo (S, I + 4, PSTATE_Z) & PSTATE_Z) == 0) { + + CodeEntry* X; + + /* lda (sp),y */ + X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI); + CS_InsertEntry (S, X, I+1); + + /* dey */ + X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, L[1]->LI); + CS_InsertEntry (S, X, I+2); + + /* ora (sp),y */ + X = NewCodeEntry (OP65_ORA, AM65_ZP_INDY, "sp", 0, L[1]->LI); + CS_InsertEntry (S, X, I+3); + + /* Invert the branch */ + CE_ReplaceOPC (L[3], GetInverseBranch (L[3]->OPC)); + + /* Delete the entries no longer needed. */ + CS_DelEntries (S, I+4, 2); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptBNegAX3 (CodeSeg* S) +/* Search for the sequence: +** +** lda xx +** ldx yy +** jsr bnegax +** jne/jeq ... +** +** and replace it by +** +** lda xx +** ora xx+1 +** jeq/jne ... +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[3]; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check for the sequence */ + if (E->OPC == OP65_LDA && + CS_GetEntries (S, L, I+1, 3) && + L[0]->OPC == OP65_LDX && + !CE_HasLabel (L[0]) && + CE_IsCallTo (L[1], "bnegax") && + !CE_HasLabel (L[1]) && + (L[2]->Info & OF_ZBRA) != 0 && + !CE_HasLabel (L[2]) && + (GetRegInfo (S, I + 4, PSTATE_Z) & PSTATE_Z) == 0) { + + /* ldx --> ora */ + CE_ReplaceOPC (L[0], OP65_ORA); + + /* Invert the branch */ + CE_ReplaceOPC (L[2], GetInverseBranch (L[2]->OPC)); + + /* Delete the subroutine call */ + CS_DelEntry (S, I+2); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptBNegAX4 (CodeSeg* S) +/* Search for the sequence: +** +** jsr xxx +** jsr bnega(x) +** jeq/jne ... +** +** and replace it by: +** +** jsr xxx +** +** jne/jeq ... +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[2]; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check for the sequence */ + if (E->OPC == OP65_JSR && + CS_GetEntries (S, L, I+1, 2) && + L[0]->OPC == OP65_JSR && + strncmp (L[0]->Arg,"bnega",5) == 0 && + !CE_HasLabel (L[0]) && + (L[1]->Info & OF_ZBRA) != 0 && + !CE_HasLabel (L[1]) && + (GetRegInfo (S, I + 3, PSTATE_Z) & PSTATE_Z) == 0) { + + CodeEntry* X; + + /* Check if we're calling bnega or bnegax */ + int ByteSized = (strcmp (L[0]->Arg, "bnega") == 0); + + /* Insert apropriate test code */ + if (ByteSized) { + /* Test bytes */ + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI); + CS_InsertEntry (S, X, I+2); + } else { + /* Test words */ + X = NewCodeEntry (OP65_STX, AM65_ZP, "tmp1", 0, L[0]->LI); + CS_InsertEntry (S, X, I+2); + X = NewCodeEntry (OP65_ORA, AM65_ZP, "tmp1", 0, L[0]->LI); + CS_InsertEntry (S, X, I+3); + } + + /* Delete the subroutine call */ + CS_DelEntry (S, I+1); + + /* Invert the branch */ + CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC)); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} diff --git a/src/cc65/coptneg.h b/src/cc65/coptbool.h similarity index 77% rename from src/cc65/coptneg.h rename to src/cc65/coptbool.h index 844d8b886..19554482e 100644 --- a/src/cc65/coptneg.h +++ b/src/cc65/coptbool.h @@ -1,8 +1,8 @@ /*****************************************************************************/ /* */ -/* coptneg.h */ +/* coptbool.h */ /* */ -/* Optimize negation sequences */ +/* Optimize boolean sequences */ /* */ /* */ /* */ @@ -33,8 +33,8 @@ -#ifndef COPTNEG_H -#define COPTNEG_H +#ifndef COPTBOOL_H +#define COPTBOOL_H @@ -43,6 +43,56 @@ +/*****************************************************************************/ +/* Optimize bool comparison and transformer subroutines */ +/*****************************************************************************/ + + + +unsigned OptBoolCmp (CodeSeg* S); +/* Search for calls to compare subroutines followed by a conditional branch +** and replace them by cheaper versions, since the branch means that the +** boolean value returned by these routines is not needed (we may also check +** that explicitly, but for the current code generator it is always true). +*/ + +unsigned OptBoolTrans (CodeSeg* S); +/* Try to remove the call to boolean transformer routines where the call is +** not really needed and change following branch condition accordingly. +*/ + + + +/*****************************************************************************/ +/* Remove calls to the boolean cast/negation subroutines */ +/*****************************************************************************/ + + + +unsigned OptBoolUnary1 (CodeSeg* S); +/* Search for and remove bcastax adjacent to bnegax */ + +unsigned OptBoolUnary2 (CodeSeg* S); +/* Search for and remove bcastax/bnegax following a boolean transformer. +** Invert the boolean transformer if it is bnegax to be removed. +*/ + +unsigned OptBoolUnary3 (CodeSeg* S); +/* Replace bcastax/bnegax with +** +** cpx #0 +** jsr boolne/booleq +** +** if A == 0, or replace bcastax/bnegax with +** +** cmp #0 +** jsr boolne/booleq +** +** if X == 0. +*/ + + + /*****************************************************************************/ /* bnega optimizations */ /*****************************************************************************/ @@ -132,54 +182,6 @@ unsigned OptBNegAX4 (CodeSeg* S); -/*****************************************************************************/ -/* negax optimizations */ -/*****************************************************************************/ - - - -unsigned OptNegAX1 (CodeSeg* S); -/* Search for a call to negax and replace it by -** -** eor #$FF -** clc -** adc #$01 -** -** if X isn't used later. -*/ - -unsigned OptNegAX2 (CodeSeg* S); -/* Search for a call to negax and replace it by -** -** ldx #$FF -** eor #$FF -** clc -** adc #$01 -** bcc L1 -** inx -** L1: -** -** if X is known and zero on entry. -*/ - - - -/*****************************************************************************/ -/* complax optimizations */ -/*****************************************************************************/ - - - -unsigned OptComplAX1 (CodeSeg* S); -/* Search for a call to complax and replace it by -** -** eor #$FF -** -** if X isn't used later. -*/ - - - -/* End of coptneg.h */ +/* End of coptbool.h */ #endif diff --git a/src/cc65/coptcmp.c b/src/cc65/coptcmp.c index 92401a858..2970b363b 100644 --- a/src/cc65/coptcmp.c +++ b/src/cc65/coptcmp.c @@ -43,131 +43,12 @@ -/*****************************************************************************/ -/* Data */ -/*****************************************************************************/ - - - -/* Table used to invert a condition, indexed by condition */ -static const unsigned char CmpInvertTab [] = { - CMP_NE, CMP_EQ, - CMP_LE, CMP_LT, CMP_GE, CMP_GT, - CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT -}; - - - /*****************************************************************************/ /* Helper functions */ /*****************************************************************************/ -static void ReplaceCmp (CodeSeg* S, unsigned I, cmp_t Cond) -/* Helper function for the replacement of routines that return a boolean -** followed by a conditional jump. Instead of the boolean value, the condition -** codes are evaluated directly. -** I is the index of the conditional branch, the sequence is already checked -** to be correct. -*/ -{ - CodeEntry* N; - CodeLabel* L; - - /* Get the entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Replace the conditional branch */ - switch (Cond) { - - case CMP_EQ: - CE_ReplaceOPC (E, OP65_JEQ); - break; - - case CMP_NE: - CE_ReplaceOPC (E, OP65_JNE); - break; - - case CMP_GT: - /* Replace by - ** beq @L - ** jpl Target - ** @L: ... - */ - if ((N = CS_GetNextEntry (S, I)) == 0) { - /* No such entry */ - Internal ("Invalid program flow"); - } - L = CS_GenLabel (S, N); - N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI); - CS_InsertEntry (S, N, I); - CE_ReplaceOPC (E, OP65_JPL); - break; - - case CMP_GE: - CE_ReplaceOPC (E, OP65_JPL); - break; - - case CMP_LT: - CE_ReplaceOPC (E, OP65_JMI); - break; - - case CMP_LE: - /* Replace by - ** jmi Target - ** jeq Target - */ - CE_ReplaceOPC (E, OP65_JMI); - L = E->JumpTo; - N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI); - CS_InsertEntry (S, N, I+1); - break; - - case CMP_UGT: - /* Replace by - ** beq @L - ** jcs Target - ** @L: ... - */ - if ((N = CS_GetNextEntry (S, I)) == 0) { - /* No such entry */ - Internal ("Invalid program flow"); - } - L = CS_GenLabel (S, N); - N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI); - CS_InsertEntry (S, N, I); - CE_ReplaceOPC (E, OP65_JCS); - break; - - case CMP_UGE: - CE_ReplaceOPC (E, OP65_JCS); - break; - - case CMP_ULT: - CE_ReplaceOPC (E, OP65_JCC); - break; - - case CMP_ULE: - /* Replace by - ** jcc Target - ** jeq Target - */ - CE_ReplaceOPC (E, OP65_JCC); - L = E->JumpTo; - N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI); - CS_InsertEntry (S, N, I+1); - break; - - default: - Internal ("Unknown jump condition: %d", Cond); - - } - -} - - - static int IsImmCmp16 (CodeEntry** L) /* Check if the instructions at L are an immediate compare of a/x: ** @@ -205,68 +86,6 @@ static int GetCmpRegVal (const CodeEntry* E) -/*****************************************************************************/ -/* Remove calls to the bool transformer subroutines */ -/*****************************************************************************/ - - - -unsigned OptBoolTrans (CodeSeg* S) -/* Try to remove the call to boolean transformer routines where the call is -** not really needed. -*/ -{ - unsigned Changes = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* N; - cmp_t Cond; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check for a boolean transformer */ - if (E->OPC == OP65_JSR && - (Cond = FindBoolCmpCond (E->Arg)) != CMP_INV && - (N = CS_GetNextEntry (S, I)) != 0 && - (N->Info & OF_ZBRA) != 0) { - - /* Make the boolean transformer unnecessary by changing the - ** the conditional jump to evaluate the condition flags that - ** are set after the compare directly. Note: jeq jumps if - ** the condition is not met, jne jumps if the condition is met. - ** Invert the code if we jump on condition not met. - */ - if (GetBranchCond (N->OPC) == BC_EQ) { - /* Jumps if condition false, invert condition */ - Cond = CmpInvertTab [Cond]; - } - - /* Check if we can replace the code by something better */ - ReplaceCmp (S, I+1, Cond); - - /* Remove the call to the bool transformer */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - /*****************************************************************************/ /* Optimizations for compares */ /*****************************************************************************/ @@ -684,68 +503,6 @@ unsigned OptCmp5 (CodeSeg* S) -unsigned OptCmp6 (CodeSeg* S) -/* Search for calls to compare subroutines followed by a conditional branch -** and replace them by cheaper versions, since the branch means that the -** boolean value returned by these routines is not needed (we may also check -** that explicitly, but for the current code generator it is always true). -*/ -{ - unsigned Changes = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* N; - cmp_t Cond; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check for the sequence */ - if (E->OPC == OP65_JSR && - (Cond = FindTosCmpCond (E->Arg)) != CMP_INV && - (N = CS_GetNextEntry (S, I)) != 0 && - (N->Info & OF_ZBRA) != 0 && - !CE_HasLabel (N)) { - - /* The tos... functions will return a boolean value in a/x and - ** the Z flag says if this value is zero or not. We will call - ** a cheaper subroutine instead, one that does not return a - ** boolean value but only valid flags. Note: jeq jumps if - ** the condition is not met, jne jumps if the condition is met. - ** Invert the code if we jump on condition not met. - */ - if (GetBranchCond (N->OPC) == BC_EQ) { - /* Jumps if condition false, invert condition */ - Cond = CmpInvertTab [Cond]; - } - - /* Replace the subroutine call. */ - E = NewCodeEntry (OP65_JSR, AM65_ABS, "tosicmp", 0, E->LI); - CS_InsertEntry (S, E, I+1); - CS_DelEntry (S, I); - - /* Replace the conditional branch */ - ReplaceCmp (S, I+1, Cond); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - unsigned OptCmp7 (CodeSeg* S) /* Search for a sequence ldx/txa/branch and remove the txa if A is not ** used later. diff --git a/src/cc65/coptcmp.h b/src/cc65/coptcmp.h index 0cdcf2d3d..dd188f7fc 100644 --- a/src/cc65/coptcmp.h +++ b/src/cc65/coptcmp.h @@ -43,19 +43,6 @@ -/*****************************************************************************/ -/* Remove calls to the bool transformer subroutines */ -/*****************************************************************************/ - - - -unsigned OptBoolTrans (CodeSeg* S); -/* Try to remove the call to boolean transformer routines where the call is -** not really needed. -*/ - - - /*****************************************************************************/ /* Optimizations for compares */ /*****************************************************************************/ @@ -136,13 +123,6 @@ unsigned OptCmp5 (CodeSeg* S); ** jne/jeq L2 */ -unsigned OptCmp6 (CodeSeg* S); -/* Search for calls to compare subroutines followed by a conditional branch -** and replace them by cheaper versions, since the branch means that the -** boolean value returned by these routines is not needed (we may also check -** that explicitly, but for the current code generator it is always true). -*/ - unsigned OptCmp7 (CodeSeg* S); /* Search for a sequence ldx/txa/branch and remove the txa if A is not ** used later. diff --git a/src/cc65/coptjmp.c b/src/cc65/coptjmp.c index 9dd4a29c5..e0b53ad91 100644 --- a/src/cc65/coptjmp.c +++ b/src/cc65/coptjmp.c @@ -898,17 +898,13 @@ unsigned OptJumpTarget3 (CodeSeg* S) -unsigned OptCondBranches1 (CodeSeg* S) -/* Performs several optimization steps: -** +unsigned OptCondBranch1 (CodeSeg* S) +/* Performs some optimization steps: ** - If an immediate load of a register is followed by a conditional jump that ** is never taken because the load of the register sets the flags in such a ** manner, remove the conditional branch. ** - If the conditional branch is always taken because of the register load, ** replace it by a jmp. -** - If a conditional branch jumps around an unconditional branch, remove the -** conditional branch and make the jump a conditional branch with the -** inverse condition of the first one. */ { unsigned Changes = 0; @@ -918,7 +914,6 @@ unsigned OptCondBranches1 (CodeSeg* S) while (I < CS_GetEntryCount (S)) { CodeEntry* N; - CodeLabel* L; /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); @@ -960,6 +955,35 @@ unsigned OptCondBranches1 (CodeSeg* S) } + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptCondBranch2 (CodeSeg* S) +/* If a conditional branch jumps around an unconditional branch, remove the +** conditional branch and make the jump a conditional branch with the inverse +** condition of the first one. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + CodeLabel* L; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + if ((E->Info & OF_CBRA) != 0 && /* It's a conditional branch */ (L = E->JumpTo) != 0 && /* ..referencing a local label */ (N = CS_GetNextEntry (S, I)) != 0 && /* There is a following entry */ @@ -991,7 +1015,51 @@ unsigned OptCondBranches1 (CodeSeg* S) -unsigned OptCondBranches2 (CodeSeg* S) +unsigned OptCondBranch3 (CodeSeg* S) +/* If the conditional branch is always taken because it follows an inverse +** conditional branch, replace it by a jmp. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's a conditional branch */ + if ((E->Info & OF_CBRA) != 0 && /* It's a conditional branch */ + (N = CS_GetNextEntry (S, I)) != 0 && /* There is a following entry */ + (N->Info & OF_CBRA) != 0 && /* ..which is a conditional branch */ + !CE_HasLabel (N)) { /* ..and does not have a label */ + + /* Check if the branches conditions are inverse of each other */ + if (GetInverseCond (GetBranchCond (N->OPC)) == GetBranchCond (E->OPC)) { + /* The branch is always taken, replace it by a jump */ + CE_ReplaceOPC (N, OP65_JMP); + + /* Remember, we had changes */ + ++Changes; + } + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptCondBranchC (CodeSeg* S) /* If on entry to a "rol a" instruction the accu is zero, and a beq/bne follows, ** we can remove the rol and branch on the state of the carry flag. */ diff --git a/src/cc65/coptjmp.h b/src/cc65/coptjmp.h index 8df53415d..194117729 100644 --- a/src/cc65/coptjmp.h +++ b/src/cc65/coptjmp.h @@ -101,13 +101,27 @@ unsigned OptJumpTarget3 (CodeSeg* S); ** done. */ -unsigned OptCondBranches1 (CodeSeg* S); -/* If an immidiate load of a register is followed by a conditional jump that -** is never taken because the load of the register sets the flags in such a -** manner, remove the conditional branch. +unsigned OptCondBranch1 (CodeSeg* S); +/* Performs some optimization steps: +** - If an immediate load of a register is followed by a conditional jump that +** is never taken because the load of the register sets the flags in such a +** manner, remove the conditional branch. +** - If the conditional branch is always taken because of the register load, +** replace it by a jmp. */ -unsigned OptCondBranches2 (CodeSeg* S); +unsigned OptCondBranch2 (CodeSeg* S); +/* If a conditional branch jumps around an unconditional branch, remove the +** conditional branch and make the jump a conditional branch with the inverse +** condition of the first one. +*/ + +unsigned OptCondBranch3 (CodeSeg* S); +/* If the conditional branch is always taken because it follows an inverse +** conditional branch, replace it by a jmp. +*/ + +unsigned OptCondBranchC (CodeSeg* S); /* If on entry to a "rol a" instruction the accu is zero, and a beq/bne follows, ** we can remove the rol and branch on the state of the carry. */ diff --git a/src/cc65/coptneg.c b/src/cc65/coptneg.c deleted file mode 100644 index 27171c68d..000000000 --- a/src/cc65/coptneg.c +++ /dev/null @@ -1,607 +0,0 @@ -/*****************************************************************************/ -/* */ -/* coptneg.c */ -/* */ -/* Optimize negation sequences */ -/* */ -/* */ -/* */ -/* (C) 2001-2012, Ullrich von Bassewitz */ -/* Roemerstrasse 52 */ -/* D-70794 Filderstadt */ -/* 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. */ -/* */ -/*****************************************************************************/ - - - -/* cc65 */ -#include "codeent.h" -#include "codeinfo.h" -#include "coptneg.h" - - - -/*****************************************************************************/ -/* bnega optimizations */ -/*****************************************************************************/ - - - -unsigned OptBNegA1 (CodeSeg* S) -/* Check for -** -** ldx #$00 -** lda .. -** jsr bnega -** -** Remove the ldx if the lda does not use it. -*/ -{ - unsigned Changes = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* L[2]; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check for a ldx */ - if (E->OPC == OP65_LDX && - E->AM == AM65_IMM && - CE_HasNumArg (E) && - E->Num == 0 && - CS_GetEntries (S, L, I+1, 2) && - L[0]->OPC == OP65_LDA && - (L[0]->Use & REG_X) == 0 && - !CE_HasLabel (L[0]) && - CE_IsCallTo (L[1], "bnega") && - !CE_HasLabel (L[1])) { - - /* Remove the ldx instruction */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptBNegA2 (CodeSeg* S) -/* Check for -** -** lda .. -** jsr bnega -** jeq/jne .. -** -** Adjust the conditional branch and remove the call to the subroutine. -*/ -{ - unsigned Changes = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* L[2]; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check for the sequence */ - if ((E->OPC == OP65_ADC || - E->OPC == OP65_AND || - E->OPC == OP65_DEA || - E->OPC == OP65_EOR || - E->OPC == OP65_INA || - E->OPC == OP65_LDA || - E->OPC == OP65_ORA || - E->OPC == OP65_PLA || - E->OPC == OP65_SBC || - E->OPC == OP65_TXA || - E->OPC == OP65_TYA) && - CS_GetEntries (S, L, I+1, 2) && - CE_IsCallTo (L[0], "bnega") && - !CE_HasLabel (L[0]) && - (L[1]->Info & OF_ZBRA) != 0 && - !CE_HasLabel (L[1])) { - - /* Invert the branch */ - CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC)); - - /* Delete the subroutine call */ - CS_DelEntry (S, I+1); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* bnegax optimizations */ -/*****************************************************************************/ - - - -unsigned OptBNegAX1 (CodeSeg* S) -/* On a call to bnegax, if X is zero, the result depends only on the value in -** A, so change the call to a call to bnega. This will get further optimized -** later if possible. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if this is a call to bnegax, and if X is known and zero */ - if (E->RI->In.RegX == 0 && CE_IsCallTo (E, "bnegax")) { - - CodeEntry* X = NewCodeEntry (OP65_JSR, AM65_ABS, "bnega", 0, E->LI); - CS_InsertEntry (S, X, I+1); - CS_DelEntry (S, I); - - /* We had changes */ - ++Changes; - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptBNegAX2 (CodeSeg* S) -/* Search for the sequence: -** -** ldy #xx -** jsr ldaxysp -** jsr bnegax -** jne/jeq ... -** -** and replace it by -** -** ldy #xx -** lda (sp),y -** dey -** ora (sp),y -** jeq/jne ... -*/ -{ - unsigned Changes = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* L[4]; - - /* Get next entry */ - L[0] = CS_GetEntry (S, I); - - /* Check for the sequence */ - if (L[0]->OPC == OP65_LDY && - CE_IsConstImm (L[0]) && - !CS_RangeHasLabel (S, I+1, 3) && - CS_GetEntries (S, L+1, I+1, 3) && - CE_IsCallTo (L[1], "ldaxysp") && - CE_IsCallTo (L[2], "bnegax") && - (L[3]->Info & OF_ZBRA) != 0) { - - CodeEntry* X; - - /* lda (sp),y */ - X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI); - CS_InsertEntry (S, X, I+1); - - /* dey */ - X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, L[1]->LI); - CS_InsertEntry (S, X, I+2); - - /* ora (sp),y */ - X = NewCodeEntry (OP65_ORA, AM65_ZP_INDY, "sp", 0, L[1]->LI); - CS_InsertEntry (S, X, I+3); - - /* Invert the branch */ - CE_ReplaceOPC (L[3], GetInverseBranch (L[3]->OPC)); - - /* Delete the entries no longer needed. */ - CS_DelEntries (S, I+4, 2); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptBNegAX3 (CodeSeg* S) -/* Search for the sequence: -** -** lda xx -** ldx yy -** jsr bnegax -** jne/jeq ... -** -** and replace it by -** -** lda xx -** ora xx+1 -** jeq/jne ... -*/ -{ - unsigned Changes = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* L[3]; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check for the sequence */ - if (E->OPC == OP65_LDA && - CS_GetEntries (S, L, I+1, 3) && - L[0]->OPC == OP65_LDX && - !CE_HasLabel (L[0]) && - CE_IsCallTo (L[1], "bnegax") && - !CE_HasLabel (L[1]) && - (L[2]->Info & OF_ZBRA) != 0 && - !CE_HasLabel (L[2])) { - - /* ldx --> ora */ - CE_ReplaceOPC (L[0], OP65_ORA); - - /* Invert the branch */ - CE_ReplaceOPC (L[2], GetInverseBranch (L[2]->OPC)); - - /* Delete the subroutine call */ - CS_DelEntry (S, I+2); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptBNegAX4 (CodeSeg* S) -/* Search for the sequence: -** -** jsr xxx -** jsr bnega(x) -** jeq/jne ... -** -** and replace it by: -** -** jsr xxx -** -** jne/jeq ... -*/ -{ - unsigned Changes = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* L[2]; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check for the sequence */ - if (E->OPC == OP65_JSR && - CS_GetEntries (S, L, I+1, 2) && - L[0]->OPC == OP65_JSR && - strncmp (L[0]->Arg,"bnega",5) == 0 && - !CE_HasLabel (L[0]) && - (L[1]->Info & OF_ZBRA) != 0 && - !CE_HasLabel (L[1])) { - - CodeEntry* X; - - /* Check if we're calling bnega or bnegax */ - int ByteSized = (strcmp (L[0]->Arg, "bnega") == 0); - - /* Insert apropriate test code */ - if (ByteSized) { - /* Test bytes */ - X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI); - CS_InsertEntry (S, X, I+2); - } else { - /* Test words */ - X = NewCodeEntry (OP65_STX, AM65_ZP, "tmp1", 0, L[0]->LI); - CS_InsertEntry (S, X, I+2); - X = NewCodeEntry (OP65_ORA, AM65_ZP, "tmp1", 0, L[0]->LI); - CS_InsertEntry (S, X, I+3); - } - - /* Delete the subroutine call */ - CS_DelEntry (S, I+1); - - /* Invert the branch */ - CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC)); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* negax optimizations */ -/*****************************************************************************/ - - - -unsigned OptNegAX1 (CodeSeg* S) -/* Search for a call to negax and replace it by -** -** eor #$FF -** clc -** adc #$01 -** -** if X isn't used later. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if this is a call to negax, and if X isn't used later */ - if (CE_IsCallTo (E, "negax") && !RegXUsed (S, I+1)) { - - CodeEntry* X; - - /* Add replacement code behind */ - X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI); - CS_InsertEntry (S, X, I+1); - - X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, E->LI); - CS_InsertEntry (S, X, I+2); - - X = NewCodeEntry (OP65_ADC, AM65_IMM, "$01", 0, E->LI); - CS_InsertEntry (S, X, I+3); - - /* Delete the call to negax */ - CS_DelEntry (S, I); - - /* Skip the generated code */ - I += 2; - - /* We had changes */ - ++Changes; - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptNegAX2 (CodeSeg* S) -/* Search for a call to negax and replace it by -** -** ldx #$FF -** eor #$FF -** clc -** adc #$01 -** bcc L1 -** inx -** L1: -** -** if X is known and zero on entry. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* P; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if this is a call to negax, and if X is known and zero */ - if (E->RI->In.RegX == 0 && - CE_IsCallTo (E, "negax") && - (P = CS_GetNextEntry (S, I)) != 0) { - - CodeEntry* X; - CodeLabel* L; - - /* Add replacement code behind */ - - /* ldx #$FF */ - X = NewCodeEntry (OP65_LDX, AM65_IMM, "$FF", 0, E->LI); - CS_InsertEntry (S, X, I+1); - - /* eor #$FF */ - X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI); - CS_InsertEntry (S, X, I+2); - - /* clc */ - X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, E->LI); - CS_InsertEntry (S, X, I+3); - - /* adc #$01 */ - X = NewCodeEntry (OP65_ADC, AM65_IMM, "$01", 0, E->LI); - CS_InsertEntry (S, X, I+4); - - /* Get the label attached to the insn following the call */ - L = CS_GenLabel (S, P); - - /* bcc L */ - X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, E->LI); - CS_InsertEntry (S, X, I+5); - - /* inx */ - X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, E->LI); - CS_InsertEntry (S, X, I+6); - - /* Delete the call to negax */ - CS_DelEntry (S, I); - - /* Skip the generated code */ - I += 5; - - /* We had changes */ - ++Changes; - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* complax optimizations */ -/*****************************************************************************/ - - - -unsigned OptComplAX1 (CodeSeg* S) -/* Search for a call to complax and replace it by -** -** eor #$FF -** -** if X isn't used later. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if this is a call to negax, and if X isn't used later */ - if (CE_IsCallTo (E, "complax") && !RegXUsed (S, I+1)) { - - CodeEntry* X; - - /* Add replacement code behind */ - X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI); - CS_InsertEntry (S, X, I+1); - - /* Delete the call to negax */ - CS_DelEntry (S, I); - - /* We had changes */ - ++Changes; - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} diff --git a/src/cc65/coptunary.c b/src/cc65/coptunary.c new file mode 100644 index 000000000..4d92f9d4a --- /dev/null +++ b/src/cc65/coptunary.c @@ -0,0 +1,236 @@ +/*****************************************************************************/ +/* */ +/* coptunary.c */ +/* */ +/* Optimize bitwise unary sequences */ +/* */ +/* */ +/* */ +/* (C) 2001-2012, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* 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. */ +/* */ +/*****************************************************************************/ + + + +/* cc65 */ +#include "codeent.h" +#include "codeinfo.h" +#include "coptbool.h" + + + +/*****************************************************************************/ +/* negax optimizations */ +/*****************************************************************************/ + + + +unsigned OptNegAX1 (CodeSeg* S) +/* Search for a call to negax and replace it by +** +** eor #$FF +** clc +** adc #$01 +** +** if X isn't used later. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if this is a call to negax, and if X isn't used later */ + if (CE_IsCallTo (E, "negax") && !RegXUsed (S, I+1)) { + + CodeEntry* X; + + /* Add replacement code behind */ + X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI); + CS_InsertEntry (S, X, I+1); + + X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, I+2); + + X = NewCodeEntry (OP65_ADC, AM65_IMM, "$01", 0, E->LI); + CS_InsertEntry (S, X, I+3); + + /* Delete the call to negax */ + CS_DelEntry (S, I); + + /* Skip the generated code */ + I += 2; + + /* We had changes */ + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptNegAX2 (CodeSeg* S) +/* Search for a call to negax and replace it by +** +** ldx #$FF +** eor #$FF +** clc +** adc #$01 +** bcc L1 +** inx +** L1: +** +** if X is known and zero on entry. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* P; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if this is a call to negax, and if X is known and zero */ + if (E->RI->In.RegX == 0 && + CE_IsCallTo (E, "negax") && + (P = CS_GetNextEntry (S, I)) != 0) { + + CodeEntry* X; + CodeLabel* L; + + /* Add replacement code behind */ + + /* ldx #$FF */ + X = NewCodeEntry (OP65_LDX, AM65_IMM, "$FF", 0, E->LI); + CS_InsertEntry (S, X, I+1); + + /* eor #$FF */ + X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI); + CS_InsertEntry (S, X, I+2); + + /* clc */ + X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, I+3); + + /* adc #$01 */ + X = NewCodeEntry (OP65_ADC, AM65_IMM, "$01", 0, E->LI); + CS_InsertEntry (S, X, I+4); + + /* Get the label attached to the insn following the call */ + L = CS_GenLabel (S, P); + + /* bcc L */ + X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, E->LI); + CS_InsertEntry (S, X, I+5); + + /* inx */ + X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, I+6); + + /* Delete the call to negax */ + CS_DelEntry (S, I); + + /* Skip the generated code */ + I += 5; + + /* We had changes */ + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* complax optimizations */ +/*****************************************************************************/ + + + +unsigned OptComplAX1 (CodeSeg* S) +/* Search for a call to complax and replace it by +** +** eor #$FF +** +** if X isn't used later. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if this is a call to negax, and if X isn't used later */ + if (CE_IsCallTo (E, "complax") && !RegXUsed (S, I+1)) { + + CodeEntry* X; + + /* Add replacement code behind */ + X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI); + CS_InsertEntry (S, X, I+1); + + /* Delete the call to negax */ + CS_DelEntry (S, I); + + /* We had changes */ + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} diff --git a/src/cc65/coptunary.h b/src/cc65/coptunary.h new file mode 100644 index 000000000..a7fd6d7b4 --- /dev/null +++ b/src/cc65/coptunary.h @@ -0,0 +1,96 @@ +/*****************************************************************************/ +/* */ +/* coptunary.h */ +/* */ +/* Optimize bitwise unary sequences */ +/* */ +/* */ +/* */ +/* (C) 2001-2012, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* 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 COPTUNARY_H +#define COPTUNARY_H + + + +/* cc65 */ +#include "codeseg.h" + + + +/*****************************************************************************/ +/* negax optimizations */ +/*****************************************************************************/ + + + +unsigned OptNegAX1 (CodeSeg* S); +/* Search for a call to negax and replace it by +** +** eor #$FF +** clc +** adc #$01 +** +** if X isn't used later. +*/ + +unsigned OptNegAX2 (CodeSeg* S); +/* Search for a call to negax and replace it by +** +** ldx #$FF +** eor #$FF +** clc +** adc #$01 +** bcc L1 +** inx +** L1: +** +** if X is known and zero on entry. +*/ + + + +/*****************************************************************************/ +/* complax optimizations */ +/*****************************************************************************/ + + + +unsigned OptComplAX1 (CodeSeg* S); +/* Search for a call to complax and replace it by +** +** eor #$FF +** +** if X isn't used later. +*/ + + + +/* End of coptunary.h */ + +#endif diff --git a/test/val/booltrans.c b/test/val/booltrans.c new file mode 100644 index 000000000..51439d45b --- /dev/null +++ b/test/val/booltrans.c @@ -0,0 +1,161 @@ +/* Optimization bugs with multiple inverse Z branches following one boolean transformer */ + +#include +#include + +unsigned failures; + +int a; + +/* To reveal the bug, the second Z branch must jump over the destination of the first Z branch */ + +int test_booltrans(int8_t x) +{ + a = x; + __asm__("lda #$00"); + __asm__("cmp %v", a); + __asm__("jsr booleq"); + __asm__("jeq %g", L1); + __asm__("jne %g", L0); +L1: + return 1; +L0: + return 0; +} + +int test_bnega2(int8_t x) +{ + a = x; + __asm__("lda %v", a); + __asm__("jsr bnega"); + __asm__("jeq %g", L1); + __asm__("jne %g", L0); +L1: + return 1; +L0: + return 0; +} + +int test_bnegax2(int16_t x) +{ + int a = x; + __asm__("ldy #%o+1", a); + __asm__("jsr ldaxysp"); + __asm__("jsr bnegax"); + __asm__("jeq %g", L1); + __asm__("jne %g", L0); +L1: + return 1; +L0: + return 0; +} + +void __fastcall__ f(void) {} + +int test_bnegax3(int16_t x) +{ + a = x; + __asm__("lda %v", a); + __asm__("ldx %v+1", a); + __asm__("jsr %v", f); + __asm__("jsr bnegax"); + __asm__("jeq %g", L1); + __asm__("jne %g", L0); +L1: + return 1; +L0: + return 0; +} + +int test_bnegax4(int16_t x) +{ + a = x; + __asm__("lda %v", a); + __asm__("ldx %v+1", a); + __asm__("jsr bnegax"); + __asm__("jeq %g", L1); + __asm__("jne %g", L0); +L1: + return 1; +L0: + return 0; +} + +int main(void) +{ + a = test_booltrans(0); + if (a != 0) + { + ++failures; + printf("test_booltrans(0): %d, expected: 0\n", a); + } + + a = test_booltrans(1); + if (a != 1) + { + ++failures; + printf("test_booltrans(1): %d, expected: 1\n", a); + } + + a = test_bnega2(0); + if (a != 0) + { + ++failures; + printf("test_bnega2(0): %d, expected: 0\n", a); + } + + a = test_bnega2(1); + if (a != 1) + { + ++failures; + printf("test_bnega2(1): %d, expected: 1\n", a); + } + + a = test_bnegax2(0); + if (a != 0) + { + ++failures; + printf("test_bnegax2(0): %d, expected: 0\n", a); + } + + a = test_bnegax2(1); + if (a != 1) + { + ++failures; + printf("test_bnegax2(1): %d, expected: 1\n", a); + } + + a = test_bnegax3(0); + if (a != 0) + { + ++failures; + printf("test_bnegax3(0): %d, expected: 0\n", a); + } + + a = test_bnegax3(1); + if (a != 1) + { + ++failures; + printf("test_bnegax3(1): %d, expected: 1\n", a); + } + + a = test_bnegax4(0); + if (a != 0) + { + ++failures; + printf("test_bnegax4(0): %d, expected: 0\n", a); + } + + a = test_bnegax4(1); + if (a != 1) + { + ++failures; + printf("test_bnegax4(1): %d, expected: 1\n", a); + } + + if (failures > 0) + { + printf("failures: %u\n", failures); + } + return failures; +}