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;
+}