From f321bb16e56f0bfc7c0b1747162f566f46a1b220 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 21 Oct 2023 23:56:07 +0800 Subject: [PATCH] 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 +#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; +}