From e5bbdfa995bcef1618d17c485dbb1eb081d5f59b Mon Sep 17 00:00:00 2001
From: acqn <acqn163@outlook.com>
Date: Sat, 21 Oct 2023 23:56:01 +0800
Subject: [PATCH 1/4] Separated boolean optimizers from bitwise unary operator
 optimizers. Renamed OptCmp6 to OptBoolCmp.

---
 src/cc65.vcxproj                   |   6 +-
 src/cc65/codeopt.c                 |   9 +-
 src/cc65/{coptneg.c => coptbool.c} | 453 ++++++++++++++++-------------
 src/cc65/{coptneg.h => coptbool.h} |  78 ++---
 src/cc65/coptcmp.c                 | 243 ----------------
 src/cc65/coptcmp.h                 |  20 --
 src/cc65/coptunary.c               | 236 +++++++++++++++
 src/cc65/coptunary.h               |  96 ++++++
 8 files changed, 619 insertions(+), 522 deletions(-)
 rename src/cc65/{coptneg.c => coptbool.c} (66%)
 rename src/cc65/{coptneg.h => coptbool.h} (80%)
 create mode 100644 src/cc65/coptunary.c
 create mode 100644 src/cc65/coptunary.h

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 @@
     <ClInclude Include="cc65\codeseg.h" />
     <ClInclude Include="cc65\compile.h" />
     <ClInclude Include="cc65\coptadd.h" />
+    <ClInclude Include="cc65\coptbool.h" />
     <ClInclude Include="cc65\coptc02.h" />
     <ClInclude Include="cc65\coptcmp.h" />
     <ClInclude Include="cc65\coptind.h" />
     <ClInclude Include="cc65\coptjmp.h" />
     <ClInclude Include="cc65\coptmisc.h" />
-    <ClInclude Include="cc65\coptneg.h" />
     <ClInclude Include="cc65\coptptrload.h" />
     <ClInclude Include="cc65\coptptrstore.h" />
     <ClInclude Include="cc65\coptpush.h" />
@@ -79,6 +79,7 @@
     <ClInclude Include="cc65\coptstore.h" />
     <ClInclude Include="cc65\coptsub.h" />
     <ClInclude Include="cc65\copttest.h" />
+    <ClInclude Include="cc65\coptunary.h" />
     <ClInclude Include="cc65\dataseg.h" />
     <ClInclude Include="cc65\datatype.h" />
     <ClInclude Include="cc65\declare.h" />
@@ -144,12 +145,12 @@
     <ClCompile Include="cc65\codeseg.c" />
     <ClCompile Include="cc65\compile.c" />
     <ClCompile Include="cc65\coptadd.c" />
+    <ClCompile Include="cc65\coptbool.c" />
     <ClCompile Include="cc65\coptc02.c" />
     <ClCompile Include="cc65\coptcmp.c" />
     <ClCompile Include="cc65\coptind.c" />
     <ClCompile Include="cc65\coptjmp.c" />
     <ClCompile Include="cc65\coptmisc.c" />
-    <ClCompile Include="cc65\coptneg.c" />
     <ClCompile Include="cc65\coptptrload.c" />
     <ClCompile Include="cc65\coptptrstore.c" />
     <ClCompile Include="cc65\coptpush.c" />
@@ -159,6 +160,7 @@
     <ClCompile Include="cc65\coptstore.c" />
     <ClCompile Include="cc65\coptsub.c" />
     <ClCompile Include="cc65\copttest.c" />
+    <ClCompile Include="cc65\coptunary.c" />
     <ClCompile Include="cc65\dataseg.c" />
     <ClCompile Include="cc65\datatype.c" />
     <ClCompile Include="cc65\declare.c" />
diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c
index 208ada134..6c2a1fddd 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,6 +116,7 @@ static OptFunc DOptBNegAX1      = { OptBNegAX1,      "OptBNegAX1",      100, 0,
 static OptFunc DOptBNegAX2      = { OptBNegAX2,      "OptBNegAX2",      100, 0, 0, 0, 0, 0 };
 static OptFunc DOptBNegAX3      = { OptBNegAX3,      "OptBNegAX3",      100, 0, 0, 0, 0, 0 };
 static OptFunc DOptBNegAX4      = { OptBNegAX4,      "OptBNegAX4",      100, 0, 0, 0, 0, 0 };
+static OptFunc DOptBoolCmp      = { OptBoolCmp,      "OptBoolCmp",      100, 0, 0, 0, 0, 0 };
 static OptFunc DOptBoolTrans    = { OptBoolTrans,    "OptBoolTrans",    100, 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 };
@@ -123,7 +125,6 @@ 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 };
@@ -221,6 +222,7 @@ static OptFunc* OptFuncs[] = {
     &DOptBNegAX2,
     &DOptBNegAX3,
     &DOptBNegAX4,
+    &DOptBoolCmp,
     &DOptBoolTrans,
     &DOptBranchDist,
     &DOptBranchDist2,
@@ -229,7 +231,6 @@ static OptFunc* OptFuncs[] = {
     &DOptCmp3,
     &DOptCmp4,
     &DOptCmp5,
-    &DOptCmp6,
     &DOptCmp7,
     &DOptCmp8,
     &DOptCmp9,
@@ -684,6 +685,7 @@ static unsigned RunOptGroup3 (CodeSeg* S)
         C += RunOptFunc (S, &DOptJumpCascades, 1);
         C += RunOptFunc (S, &DOptDeadJumps, 1);
         C += RunOptFunc (S, &DOptDeadCode, 1);
+        C += RunOptFunc (S, &DOptBoolCmp, 1);
         C += RunOptFunc (S, &DOptBoolTrans, 1);
         C += RunOptFunc (S, &DOptJumpTarget1, 1);
         C += RunOptFunc (S, &DOptJumpTarget2, 1);
@@ -696,7 +698,6 @@ static unsigned RunOptGroup3 (CodeSeg* S)
         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);
diff --git a/src/cc65/coptneg.c b/src/cc65/coptbool.c
similarity index 66%
rename from src/cc65/coptneg.c
rename to src/cc65/coptbool.c
index 27171c68d..ee88cac0d 100644
--- a/src/cc65/coptneg.c
+++ b/src/cc65/coptbool.c
@@ -1,8 +1,8 @@
 /*****************************************************************************/
 /*                                                                           */
-/*                                 coptneg.c                                 */
+/*                                coptbool.c                                 */
 /*                                                                           */
-/*                        Optimize negation sequences                        */
+/*                        Optimize boolean sequences                         */
 /*                                                                           */
 /*                                                                           */
 /*                                                                           */
@@ -36,7 +36,257 @@
 /* cc65 */
 #include "codeent.h"
 #include "codeinfo.h"
-#include "coptneg.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            */
+/*****************************************************************************/
+
+
+
+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) {
+
+            /* 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;
+}
 
 
 
@@ -408,200 +658,3 @@ unsigned OptBNegAX4 (CodeSeg* S)
     /* 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/coptneg.h b/src/cc65/coptbool.h
similarity index 80%
rename from src/cc65/coptneg.h
rename to src/cc65/coptbool.h
index 844d8b886..195751a02 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,26 @@
 
 
 
+/*****************************************************************************/
+/*           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.
+*/
+
+
+
 /*****************************************************************************/
 /*                            bnega optimizations                            */
 /*****************************************************************************/
@@ -132,54 +152,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/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

From 79c52e742f32d4e52d6dce9ed85a17f21bb4c102 Mon Sep 17 00:00:00 2001
From: acqn <acqn163@outlook.com>
Date: Sat, 21 Oct 2023 23:56:04 +0800
Subject: [PATCH 2/4] Added new opts OptBoolUnary1/OptBoolUnary2 to remove
 unnecessary cmp + bcastax/bnegax, as well as OptBoolUnary3 to
 "strength-reduce" certain bcastax/bnegax to boolne/booleq.

---
 src/cc65/codeopt.c  |  14 ++-
 src/cc65/coptbool.c | 221 ++++++++++++++++++++++++++++++++++++++++++++
 src/cc65/coptbool.h |  30 ++++++
 3 files changed, 263 insertions(+), 2 deletions(-)

diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c
index 6c2a1fddd..ab3111a9b 100644
--- a/src/cc65/codeopt.c
+++ b/src/cc65/codeopt.c
@@ -118,6 +118,9 @@ static OptFunc DOptBNegAX3      = { OptBNegAX3,      "OptBNegAX3",      100, 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 };
@@ -224,6 +227,9 @@ static OptFunc* OptFuncs[] = {
     &DOptBNegAX4,
     &DOptBoolCmp,
     &DOptBoolTrans,
+    &DOptBoolUnary1,
+    &DOptBoolUnary2,
+    &DOptBoolUnary3,
     &DOptBranchDist,
     &DOptBranchDist2,
     &DOptCmp1,
@@ -613,7 +619,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);
@@ -673,7 +678,12 @@ static unsigned RunOptGroup3 (CodeSeg* S)
         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, &DOptBNegAX1, 1);       /* After OptBoolUnary2 */
         C += RunOptFunc (S, &DOptShift1, 1);
         C += RunOptFunc (S, &DOptShift4, 1);
         C += RunOptFunc (S, &DOptComplAX1, 1);
diff --git a/src/cc65/coptbool.c b/src/cc65/coptbool.c
index ee88cac0d..331fa77ab 100644
--- a/src/cc65/coptbool.c
+++ b/src/cc65/coptbool.c
@@ -290,6 +290,227 @@ unsigned OptBoolTrans (CodeSeg* S)
 
 
 
+/*****************************************************************************/
+/*           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                            */
 /*****************************************************************************/
diff --git a/src/cc65/coptbool.h b/src/cc65/coptbool.h
index 195751a02..19554482e 100644
--- a/src/cc65/coptbool.h
+++ b/src/cc65/coptbool.h
@@ -63,6 +63,36 @@ unsigned OptBoolTrans (CodeSeg* S);
 
 
 
+/*****************************************************************************/
+/*           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                            */
 /*****************************************************************************/

From f321bb16e56f0bfc7c0b1747162f566f46a1b220 Mon Sep 17 00:00:00 2001
From: acqn <acqn163@outlook.com>
Date: Sat, 21 Oct 2023 23:56:07 +0800
Subject: [PATCH 3/4] Fixed potential bugs with boolean branch optimizers when
 more than one jeq/jne follows.

---
 src/cc65/coptbool.c  |  15 ++--
 test/val/booltrans.c | 161 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 171 insertions(+), 5 deletions(-)
 create mode 100644 test/val/booltrans.c

diff --git a/src/cc65/coptbool.c b/src/cc65/coptbool.c
index 331fa77ab..663e4e85e 100644
--- a/src/cc65/coptbool.c
+++ b/src/cc65/coptbool.c
@@ -255,7 +255,8 @@ unsigned OptBoolTrans (CodeSeg* S)
         if (E->OPC == OP65_JSR                           &&
             (Cond = FindBoolCmpCond (E->Arg)) != CMP_INV &&
             (N = CS_GetNextEntry (S, I)) != 0            &&
-            (N->Info & OF_ZBRA) != 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
@@ -606,7 +607,8 @@ unsigned OptBNegA2 (CodeSeg* S)
             CE_IsCallTo (L[0], "bnega")         &&
             !CE_HasLabel (L[0])                 &&
             (L[1]->Info & OF_ZBRA) != 0         &&
-            !CE_HasLabel (L[1])) {
+            !CE_HasLabel (L[1])                 &&
+            (GetRegInfo (S, I + 3, PSTATE_Z) & PSTATE_Z) == 0) {
 
             /* Invert the branch */
             CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
@@ -709,7 +711,8 @@ unsigned OptBNegAX2 (CodeSeg* S)
             CS_GetEntries (S, L+1, I+1, 3)      &&
             CE_IsCallTo (L[1], "ldaxysp")       &&
             CE_IsCallTo (L[2], "bnegax")        &&
-            (L[3]->Info & OF_ZBRA) != 0) {
+            (L[3]->Info & OF_ZBRA) != 0         &&
+            (GetRegInfo (S, I + 4, PSTATE_Z) & PSTATE_Z) == 0) {
 
             CodeEntry* X;
 
@@ -781,7 +784,8 @@ unsigned OptBNegAX3 (CodeSeg* S)
             CE_IsCallTo (L[1], "bnegax")        &&
             !CE_HasLabel (L[1])                 &&
             (L[2]->Info & OF_ZBRA) != 0         &&
-            !CE_HasLabel (L[2])) {
+            !CE_HasLabel (L[2])                 &&
+            (GetRegInfo (S, I + 4, PSTATE_Z) & PSTATE_Z) == 0) {
 
             /* ldx --> ora */
             CE_ReplaceOPC (L[0], OP65_ORA);
@@ -840,7 +844,8 @@ unsigned OptBNegAX4 (CodeSeg* S)
             strncmp (L[0]->Arg,"bnega",5) == 0  &&
             !CE_HasLabel (L[0])                 &&
             (L[1]->Info & OF_ZBRA) != 0         &&
-            !CE_HasLabel (L[1])) {
+            !CE_HasLabel (L[1])                 &&
+            (GetRegInfo (S, I + 3, PSTATE_Z) & PSTATE_Z) == 0) {
 
             CodeEntry* X;
 
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 <stdint.h>
+#include <stdio.h>
+
+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;
+}

From 70549e868edba97c3cb3271c2993aeafc41fd232 Mon Sep 17 00:00:00 2001
From: acqn <acqn163@outlook.com>
Date: Sat, 21 Oct 2023 23:56:13 +0800
Subject: [PATCH 4/4] New optimizer steps to restore some possibly lost
 optimization with boolean due to the previous fix.

---
 src/cc65/codeopt.c  | 36 +++++++++++--------
 src/cc65/coptbool.c | 58 ++++++++++++++++++++++++++++++-
 src/cc65/coptjmp.c  | 84 ++++++++++++++++++++++++++++++++++++++++-----
 src/cc65/coptjmp.h  | 24 ++++++++++---
 4 files changed, 173 insertions(+), 29 deletions(-)

diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c
index ab3111a9b..baa44f99e 100644
--- a/src/cc65/codeopt.c
+++ b/src/cc65/codeopt.c
@@ -132,8 +132,10 @@ static OptFunc DOptCmp7         = { OptCmp7,         "OptCmp7",          85, 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 };
@@ -241,8 +243,10 @@ static OptFunc* OptFuncs[] = {
     &DOptCmp8,
     &DOptCmp9,
     &DOptComplAX1,
-    &DOptCondBranches1,
-    &DOptCondBranches2,
+    &DOptCondBranch1,
+    &DOptCondBranch2,
+    &DOptCondBranch3,
+    &DOptCondBranchC,
     &DOptDeadCode,
     &DOptDeadJumps,
     &DOptDecouple,
@@ -619,9 +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, &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);
@@ -674,8 +675,6 @@ 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);      /* Before OptBoolUnary1 */
@@ -683,6 +682,7 @@ static unsigned RunOptGroup3 (CodeSeg* S)
         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);
@@ -695,16 +695,22 @@ static unsigned RunOptGroup3 (CodeSeg* S)
         C += RunOptFunc (S, &DOptJumpCascades, 1);
         C += RunOptFunc (S, &DOptDeadJumps, 1);
         C += RunOptFunc (S, &DOptDeadCode, 1);
-        C += RunOptFunc (S, &DOptBoolCmp, 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);
@@ -712,7 +718,7 @@ static unsigned RunOptGroup3 (CodeSeg* S)
         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
index 663e4e85e..3a3b3fa7c 100644
--- a/src/cc65/coptbool.c
+++ b/src/cc65/coptbool.c
@@ -167,7 +167,7 @@ static void ReplaceBranchCond (CodeSeg* S, unsigned I, cmp_t Cond)
 
 
 /*****************************************************************************/
-/*           Optimize bool comparison and transformer subroutines            */
+/*    Optimize bool comparison and transformer subroutines with branches     */
 /*****************************************************************************/
 
 
@@ -291,6 +291,62 @@ unsigned OptBoolTrans (CodeSeg* S)
 
 
 
+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           */
 /*****************************************************************************/
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.
 */