From c0a1b1b887af536612987847c9344a1226bf4397 Mon Sep 17 00:00:00 2001 From: Kugel Fuhr <98353208+kugelfuhr@users.noreply.github.com> Date: Tue, 24 Jun 2025 18:02:24 +0200 Subject: [PATCH 1/5] Add an optimization step that removes compare instructions preceeding an RTS. Since nothing is passed back in the flags, these instructions have no effect. Fixes #2025. --- src/cc65/codeopt.c | 3 +++ src/cc65/coptcmp.c | 37 +++++++++++++++++++++++++++++++++++++ src/cc65/coptcmp.h | 5 +++++ 3 files changed, 45 insertions(+) diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index 19acfde74..f57417526 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -136,6 +136,7 @@ static OptFunc DOptCmp5 = { OptCmp5, "OptCmp5", 100, 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 DOptCmp10 = { OptCmp10, "OptCmp10", 33, 0, 0, 0, 0, 0 }; static OptFunc DOptComplAX1 = { OptComplAX1, "OptComplAX1", 65, 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 }; @@ -251,6 +252,7 @@ static OptFunc* OptFuncs[] = { &DOptBranchDist, &DOptBranchDist2, &DOptCmp1, + &DOptCmp10, &DOptCmp2, &DOptCmp3, &DOptCmp4, @@ -729,6 +731,7 @@ static unsigned RunOptGroup3 (CodeSeg* S) C += RunOptFunc (S, &DOptCondBranch3, 1); C += RunOptFunc (S, &DOptCondBranchC, 1); C += RunOptFunc (S, &DOptRTSJumps1, 1); + C += RunOptFunc (S, &DOptCmp10, 1); /* After OptRTSJumps1 */ C += RunOptFunc (S, &DOptBoolCmp, 1); C += RunOptFunc (S, &DOptBoolTrans, 1); C += RunOptFunc (S, &DOptBNegA2, 1); /* After OptCondBranch's */ diff --git a/src/cc65/coptcmp.c b/src/cc65/coptcmp.c index 2970b363b..25499dc3c 100644 --- a/src/cc65/coptcmp.c +++ b/src/cc65/coptcmp.c @@ -741,3 +741,40 @@ unsigned OptCmp9 (CodeSeg* S) /* Return the number of changes made */ return Changes; } + + + +unsigned OptCmp10 (CodeSeg* S) +/* Remove compare instructions before an RTS. This is safe since no C function +** passes back something in the flags. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check for a compare followed by an RTS */ + if ((E->Info & OF_CMP) != 0 && /* Compare insn */ + (N = CS_GetNextEntry (S, I)) != 0 && /* Next entry ... */ + N->OPC == OP65_RTS) { /* ... is RTS */ + + /* Found, remove the compare */ + CS_DelEntry (S, I); + ++Changes; + } + + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} diff --git a/src/cc65/coptcmp.h b/src/cc65/coptcmp.h index dd188f7fc..ff58d37df 100644 --- a/src/cc65/coptcmp.h +++ b/src/cc65/coptcmp.h @@ -146,6 +146,11 @@ unsigned OptCmp9 (CodeSeg* S); ** flag instead of the carry flag and remove the asl. */ +unsigned OptCmp10 (CodeSeg* S); +/* Remove compare instructions before an RTS. This is safe since no C function +** passes back something in the flags. +*/ + /* End of coptcmp.h */ From 7a6c60ade423c186d11b89be9fb1d99aa8a33a6b Mon Sep 17 00:00:00 2001 From: Kugel Fuhr <98353208+kugelfuhr@users.noreply.github.com> Date: Tue, 24 Jun 2025 20:21:43 +0200 Subject: [PATCH 2/5] Do also sort variables to satisfy sorted_codeopt.sh. --- src/cc65/codeopt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index f57417526..e9cf8914f 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -129,6 +129,7 @@ static OptFunc DOptBoolUnary3 = { OptBoolUnary3, "OptBoolUnary3", 40, 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 }; +static OptFunc DOptCmp10 = { OptCmp10, "OptCmp10", 33, 0, 0, 0, 0, 0 }; static OptFunc DOptCmp2 = { OptCmp2, "OptCmp2", 85, 0, 0, 0, 0, 0 }; static OptFunc DOptCmp3 = { OptCmp3, "OptCmp3", 75, 0, 0, 0, 0, 0 }; static OptFunc DOptCmp4 = { OptCmp4, "OptCmp4", 75, 0, 0, 0, 0, 0 }; @@ -136,7 +137,6 @@ static OptFunc DOptCmp5 = { OptCmp5, "OptCmp5", 100, 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 DOptCmp10 = { OptCmp10, "OptCmp10", 33, 0, 0, 0, 0, 0 }; static OptFunc DOptComplAX1 = { OptComplAX1, "OptComplAX1", 65, 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 }; From 1feeee9ce076bff2aead0ddfa8427a0f9e452d6a Mon Sep 17 00:00:00 2001 From: Kugel Fuhr <98353208+kugelfuhr@users.noreply.github.com> Date: Wed, 25 Jun 2025 09:37:58 +0200 Subject: [PATCH 3/5] Do only check .c and .h files. --- .github/checks/sorted_codeopt.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/checks/sorted_codeopt.sh b/.github/checks/sorted_codeopt.sh index c78662b9d..20145851a 100755 --- a/.github/checks/sorted_codeopt.sh +++ b/.github/checks/sorted_codeopt.sh @@ -63,6 +63,6 @@ function checkarray } -for N in `grep -rl "BEGIN DECL SORTED_CODEOPT.SH" "$CHECK_DIR"`; do - checkarray $N +find "$CHECK_DIR" -name \*.\[ch\] -print | while read N; do + grep -q "BEGIN DECL SORTED_CODEOPT.SH" "$N" && checkarray $N done From 55d9b2dbd0530aa5f355be94126676ff100f2cc9 Mon Sep 17 00:00:00 2001 From: Kugel Fuhr <98353208+kugelfuhr@users.noreply.github.com> Date: Wed, 25 Jun 2025 20:07:36 +0200 Subject: [PATCH 4/5] Rewrite the optimization step from c0a1b1b887af536612987847c9344a1226bf4397 to remove compares not only before RTS but also befoire function calls that don't use the flags but destroy all of them. The latter is currently the case for all functions called. This way the optimization covers a lot more cases than just checking for RTS. --- src/cc65/codeopt.c | 6 ++-- src/cc65/coptcmp.c | 82 +++++++++++++++++++++++++--------------------- src/cc65/coptcmp.h | 10 +++--- 3 files changed, 53 insertions(+), 45 deletions(-) diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index e9cf8914f..f7e3d4a1a 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -129,11 +129,11 @@ static OptFunc DOptBoolUnary3 = { OptBoolUnary3, "OptBoolUnary3", 40, 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 }; -static OptFunc DOptCmp10 = { OptCmp10, "OptCmp10", 33, 0, 0, 0, 0, 0 }; static OptFunc DOptCmp2 = { OptCmp2, "OptCmp2", 85, 0, 0, 0, 0, 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", 33, 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 }; @@ -252,11 +252,11 @@ static OptFunc* OptFuncs[] = { &DOptBranchDist, &DOptBranchDist2, &DOptCmp1, - &DOptCmp10, &DOptCmp2, &DOptCmp3, &DOptCmp4, &DOptCmp5, + &DOptCmp6, &DOptCmp7, &DOptCmp8, &DOptCmp9, @@ -731,7 +731,7 @@ static unsigned RunOptGroup3 (CodeSeg* S) C += RunOptFunc (S, &DOptCondBranch3, 1); C += RunOptFunc (S, &DOptCondBranchC, 1); C += RunOptFunc (S, &DOptRTSJumps1, 1); - C += RunOptFunc (S, &DOptCmp10, 1); /* After OptRTSJumps1 */ + C += RunOptFunc (S, &DOptCmp6, 1); /* After OptRTSJumps1 */ C += RunOptFunc (S, &DOptBoolCmp, 1); C += RunOptFunc (S, &DOptBoolTrans, 1); C += RunOptFunc (S, &DOptBNegA2, 1); /* After OptCondBranch's */ diff --git a/src/cc65/coptcmp.c b/src/cc65/coptcmp.c index 25499dc3c..8f487969e 100644 --- a/src/cc65/coptcmp.c +++ b/src/cc65/coptcmp.c @@ -503,6 +503,51 @@ unsigned OptCmp5 (CodeSeg* S) +unsigned OptCmp6 (CodeSeg* S) +/* Remove compare instructions before an RTS or an subroutine call that doesn't +** use the flags. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check for a compare followed by something else. + ** Note: The test could be improved by checking the flag usage of the + ** function explicitly against the flags set by the compare instruction. + ** For current code generation this makes no difference, however. + */ + if ((E->Info & OF_CMP) != 0 && + (N = CS_GetNextEntry (S, I)) != 0 && + (N->OPC == OP65_RTS || /* Either RTS, or ... */ + (N->OPC == OP65_JSR && /* ... or JSR ... */ + N->JumpTo == 0 && /* ... to external ... */ + (N->Use & PSTATE_ALL) == 0 && /* ... with no flags used ... */ + (N->Chg & PSTATE_ALL) == PSTATE_ALL))) { /* ... but all destroyed */ + + /* Found, remove the compare */ + CS_DelEntry (S, I); + ++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. @@ -741,40 +786,3 @@ unsigned OptCmp9 (CodeSeg* S) /* Return the number of changes made */ return Changes; } - - - -unsigned OptCmp10 (CodeSeg* S) -/* Remove compare instructions before an RTS. This is safe since no C function -** passes back something in the flags. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* N; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check for a compare followed by an RTS */ - if ((E->Info & OF_CMP) != 0 && /* Compare insn */ - (N = CS_GetNextEntry (S, I)) != 0 && /* Next entry ... */ - N->OPC == OP65_RTS) { /* ... is RTS */ - - /* Found, remove the compare */ - CS_DelEntry (S, I); - ++Changes; - } - - /* Next entry */ - ++I; - } - - /* Return the number of changes made */ - return Changes; -} diff --git a/src/cc65/coptcmp.h b/src/cc65/coptcmp.h index ff58d37df..acd146822 100644 --- a/src/cc65/coptcmp.h +++ b/src/cc65/coptcmp.h @@ -123,6 +123,11 @@ unsigned OptCmp5 (CodeSeg* S); ** jne/jeq L2 */ +unsigned OptCmp6 (CodeSeg* S); +/* Remove compare instructions before an RTS or an exit by jumping to some +** other function. +*/ + unsigned OptCmp7 (CodeSeg* S); /* Search for a sequence ldx/txa/branch and remove the txa if A is not ** used later. @@ -146,11 +151,6 @@ unsigned OptCmp9 (CodeSeg* S); ** flag instead of the carry flag and remove the asl. */ -unsigned OptCmp10 (CodeSeg* S); -/* Remove compare instructions before an RTS. This is safe since no C function -** passes back something in the flags. -*/ - /* End of coptcmp.h */ From fa81f14cbf2b7cbfee4dbda68d65666b78d2ca91 Mon Sep 17 00:00:00 2001 From: Kugel Fuhr <98353208+kugelfuhr@users.noreply.github.com> Date: Wed, 25 Jun 2025 20:34:41 +0200 Subject: [PATCH 5/5] Add an explicit "exit 0" to the shell scripts. This worked more or less by coincidence before since shell scripts return the exit code of the last command run if there is no explicit exit statement. --- .github/checks/lastline.sh | 1 + .github/checks/lineendings.sh | 1 + .github/checks/noexec.sh | 1 + .github/checks/sorted.sh | 1 + .github/checks/sorted_codeopt.sh | 1 + .github/checks/sorted_opcodes.sh | 2 +- .github/checks/spaces.sh | 1 + .github/checks/tabs.sh | 1 + 8 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/checks/lastline.sh b/.github/checks/lastline.sh index d243a01e1..6bb6e0dc5 100755 --- a/.github/checks/lastline.sh +++ b/.github/checks/lastline.sh @@ -23,3 +23,4 @@ if [ x"$FILES"x != xx ]; then done exit -1 fi +exit 0 diff --git a/.github/checks/lineendings.sh b/.github/checks/lineendings.sh index 5b445522f..5baac514e 100755 --- a/.github/checks/lineendings.sh +++ b/.github/checks/lineendings.sh @@ -16,3 +16,4 @@ if [ x"$FILES"x != xx ]; then done exit -1 fi +exit 0 diff --git a/.github/checks/noexec.sh b/.github/checks/noexec.sh index c76ae481d..5e53fe869 100755 --- a/.github/checks/noexec.sh +++ b/.github/checks/noexec.sh @@ -16,3 +16,4 @@ if [ x"$FILES"x != xx ]; then done exit -1 fi +exit 0 diff --git a/.github/checks/sorted.sh b/.github/checks/sorted.sh index 4f53b0484..b5451a21f 100755 --- a/.github/checks/sorted.sh +++ b/.github/checks/sorted.sh @@ -36,3 +36,4 @@ function checkarray_quoted_name for N in `grep -rl "BEGIN SORTED.SH" "$CHECK_DIR"`; do checkarray_quoted_name $N done +exit 0 diff --git a/.github/checks/sorted_codeopt.sh b/.github/checks/sorted_codeopt.sh index 20145851a..cfca028dd 100755 --- a/.github/checks/sorted_codeopt.sh +++ b/.github/checks/sorted_codeopt.sh @@ -66,3 +66,4 @@ function checkarray find "$CHECK_DIR" -name \*.\[ch\] -print | while read N; do grep -q "BEGIN DECL SORTED_CODEOPT.SH" "$N" && checkarray $N done +exit 0 diff --git a/.github/checks/sorted_opcodes.sh b/.github/checks/sorted_opcodes.sh index 3e45ea752..34156bde6 100755 --- a/.github/checks/sorted_opcodes.sh +++ b/.github/checks/sorted_opcodes.sh @@ -37,4 +37,4 @@ function checkarray_quoted_name for N in `grep -rl "BEGIN SORTED_OPCODES.SH" "$CHECK_DIR"`; do checkarray_quoted_name $N done - +exit 0 diff --git a/.github/checks/spaces.sh b/.github/checks/spaces.sh index e231f6c2d..f2eea6f3f 100755 --- a/.github/checks/spaces.sh +++ b/.github/checks/spaces.sh @@ -16,3 +16,4 @@ if [ x"$FILES"x != xx ]; then done exit -1 fi +exit 0 diff --git a/.github/checks/tabs.sh b/.github/checks/tabs.sh index 80dac3f2d..ed8d45bac 100755 --- a/.github/checks/tabs.sh +++ b/.github/checks/tabs.sh @@ -16,3 +16,4 @@ if [ x"$FILES"x != xx ]; then done exit -1 fi +exit 0