From 2b2c082efb11920ec7e720402079c2d2dae345b8 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira Date: Sun, 20 Jul 2025 14:36:30 +0200 Subject: [PATCH 1/3] Fix bug in PR #2778 Don't replace incaxy like incax[1-8]. --- src/cc65/codeopt.c | 2 ++ src/cc65/coptmisc.c | 64 +++++++++++++++++++++++++++++++++++++++++++-- src/cc65/coptmisc.h | 5 +++- test/val/bug2205.c | 9 +++++-- 4 files changed, 75 insertions(+), 5 deletions(-) diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index eccceccb3..3b88f1c16 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -114,6 +114,7 @@ static OptFunc DOptAdd4 = { OptAdd4, "OptAdd4", 90, 0, static OptFunc DOptAdd5 = { OptAdd5, "OptAdd5", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptAdd6 = { OptAdd6, "OptAdd6", 40, 0, 0, 0, 0, 0 }; static OptFunc DOptAXLoad = { OptAXLoad, "OptAXLoad", 50, 0, 0, 0, 0, 0 }; +static OptFunc DOptAXLoad2 = { OptAXLoad2, "OptAXLoad2", 66, 0, 0, 0, 0, 0 }; static OptFunc DOptAXOps = { OptAXOps, "OptAXOps", 50, 0, 0, 0, 0, 0 }; static OptFunc DOptBNegA1 = { OptBNegA1, "OptBNegA1", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptBNegA2 = { OptBNegA2, "OptBNegA2", 100, 0, 0, 0, 0, 0 }; @@ -876,6 +877,7 @@ static unsigned RunOptGroup7 (CodeSeg* S) */ Changes += RunOptFunc (S, &DOptUnusedLoads, 1); Changes += RunOptFunc (S, &DOptAXLoad, 5); + Changes += RunOptFunc (S, &DOptAXLoad2, 5); Changes += RunOptFunc (S, &DOptUnusedStores, 1); Changes += RunOptFunc (S, &DOptJumpTarget1, 5); Changes += RunOptFunc (S, &DOptStore5, 1); diff --git a/src/cc65/coptmisc.c b/src/cc65/coptmisc.c index 0ba447562..3e1b40b8d 100644 --- a/src/cc65/coptmisc.c +++ b/src/cc65/coptmisc.c @@ -499,7 +499,7 @@ unsigned OptAXOps (CodeSeg* S) unsigned OptAXLoad (CodeSeg* S) -/* Merge jsr incax/jsr ldaxi into ldy/jsr ldaxidx */ +/* Merge jsr incax[1-8]/jsr ldaxi into ldy/jsr ldaxidx */ { unsigned Changes = 0; unsigned I; @@ -514,9 +514,10 @@ unsigned OptAXLoad (CodeSeg* S) /* Get the next entry */ const CodeEntry* E = CS_GetEntry (S, I); - /* Check for incax followed by jsr/jmp ldaxi */ + /* Check for incax[1-8] followed by jsr/jmp ldaxi */ if (E->OPC == OP65_JSR && strncmp (E->Arg, "incax", 5) == 0 && + strcmp (E->Arg, "incaxy") != 0 && (N = CS_GetNextEntry (S, I)) != 0 && (N->OPC == OP65_JSR || N->OPC == OP65_JMP) && strcmp (N->Arg, "ldaxi") == 0 && @@ -555,6 +556,65 @@ unsigned OptAXLoad (CodeSeg* S) +unsigned OptAXLoad2 (CodeSeg* S) +/* Merge ldy/jsr incaxy/jsr ldaxi into ldy/jsr ldaxidx */ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + signed Val; + CodeEntry* E[3]; + CodeEntry *X; + char *End; + + /* Get the next entry */ + E[0] = CS_GetEntry (S, I); + + /* Check for ldy followed by incaxy followed by jsr/jmp ldaxi */ + if (E[0]->OPC == OP65_LDY && + E[0]->AM == AM65_IMM && + CS_GetEntries (S, E+1, I+1, 2) && + E[1]->OPC == OP65_JSR && + strcmp (E[1]->Arg, "incaxy") == 0 && + (E[2]->OPC == OP65_JSR || E[2]->OPC == OP65_JMP) && + strcmp (E[2]->Arg, "ldaxi") == 0 && + !CS_RangeHasLabel (S, I, 3)) { + + /* Replace with ldy (y+1) / jsr ldaxidx */ + Val = strtoul(E[0]->Arg + 1, &End, 16); + Val++; + + X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg(Val), 0, E[0]->LI); + CS_InsertEntry (S, X, I+3); + X = NewCodeEntry (E[2]->OPC, AM65_ABS, "ldaxidx", 0, E[0]->LI); + CS_InsertEntry (S, X, I+4); + + CS_DelEntries (S, I, 3); + + /* Regenerate register info */ + CS_GenRegInfo (S); + + /* Remember we had changes */ + ++Changes; + + } else { + + /* Next entry */ + ++I; + } + + } + + /* Return the number of changes made */ + return Changes; +} + + + unsigned OptGotoSPAdj (CodeSeg* S) /* Optimize SP adjustment for forward 'goto' */ { diff --git a/src/cc65/coptmisc.h b/src/cc65/coptmisc.h index cc6d1c519..df76e84f4 100644 --- a/src/cc65/coptmisc.h +++ b/src/cc65/coptmisc.h @@ -102,7 +102,10 @@ unsigned OptAXOps (CodeSeg* S); */ unsigned OptAXLoad (CodeSeg* S); -/* Merge adjacent calls to incax/ldaxi into ldy/ldaxidx */ +/* Merge jsr incax[1-8]/jsr ldaxi into ldy/jsr ldaxidx */ + +unsigned OptAXLoad2 (CodeSeg* S); +/* Merge ldy/jsr incaxy/jsr ldaxi into ldy/jsr ldaxidx */ unsigned OptGotoSPAdj (CodeSeg* S); /* Optimize SP adjustment for forward 'goto' */ diff --git a/test/val/bug2205.c b/test/val/bug2205.c index 1b5458249..0ca78e9b1 100644 --- a/test/val/bug2205.c +++ b/test/val/bug2205.c @@ -3,18 +3,23 @@ struct Object { int a; - int data[10]; + int data[20]; }; struct Object object_data = { 0x0102, {0x0304, 0x0506, 0x0708, 0x090A, 0x0B0C, 0x0D0E, 0x0F10, 0x1112, - 0x1314, 0x1516}}; + 0x1314, 0x1516, 0x1718, + 0x1920, 0x2122, 0x2324, + 0x2526, 0x2728, 0x2930, + 0x3132, 0x3334, 0x3536}}; TEST { struct Object *o = &object_data; ASSERT_IsTrue(o->a == 0x0102, "Wrong value for a"); ASSERT_IsTrue(o->data[2] == 0x0708, "Wrong value for data[2]"); + ASSERT_IsTrue(o->data[8] == 0x1314, "Wrong value for data[8]"); + ASSERT_IsTrue(o->data[19] == 0x3536, "Wrong value for data[19]"); } ENDTEST From fa1b6ed4f7ca99d30ea9d6bbcd81924d5fba3a17 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira Date: Sun, 20 Jul 2025 16:29:00 +0200 Subject: [PATCH 2/3] Address issues --- src/cc65/coptmisc.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/cc65/coptmisc.c b/src/cc65/coptmisc.c index 3e1b40b8d..3680a33ab 100644 --- a/src/cc65/coptmisc.c +++ b/src/cc65/coptmisc.c @@ -434,7 +434,7 @@ static unsigned OptIncDecOps (CodeSeg* S, const char* dec, const char* inc, cons (N = CS_GetNextEntry (S, I)) != 0 && (N->OPC == OP65_JSR || N->OPC == OP65_JMP) && (Val2 = IsShift (N, dec, inc, sub, add)) != 0 && - abs(Val1 += Val2) <= 255 && + abs (Val1 += Val2) <= 255 && !CE_HasLabel (N)) { CodeEntry* X; @@ -442,14 +442,14 @@ static unsigned OptIncDecOps (CodeSeg* S, const char* dec, const char* inc, cons if (Val1 != 0) { /* We can combine the two */ - if (abs(Val1) <= 8) { + if (abs (Val1) <= 8) { /* Insert a call to inc/dec using the last OPC */ - xsprintf (Buf, sizeof (Buf), "%s%u", Val1 < 0 ? dec:inc, abs(Val1)); + xsprintf (Buf, sizeof (Buf), "%s%u", Val1 < 0 ? dec:inc, abs (Val1)); X = NewCodeEntry (N->OPC, AM65_ABS, Buf, 0, N->LI); CS_InsertEntry (S, X, I+2); } else { /* Insert a call to add/sub */ - const char* Arg = MakeHexArg (abs(Val1)); + const char* Arg = MakeHexArg (abs (Val1)); X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, N->LI); CS_InsertEntry (S, X, I+2); if (Val1 < 0) { @@ -568,15 +568,14 @@ unsigned OptAXLoad2 (CodeSeg* S) signed Val; CodeEntry* E[3]; - CodeEntry *X; - char *End; + CodeEntry* X; /* Get the next entry */ E[0] = CS_GetEntry (S, I); /* Check for ldy followed by incaxy followed by jsr/jmp ldaxi */ if (E[0]->OPC == OP65_LDY && - E[0]->AM == AM65_IMM && + CE_IsConstImm (E[0]) && CS_GetEntries (S, E+1, I+1, 2) && E[1]->OPC == OP65_JSR && strcmp (E[1]->Arg, "incaxy") == 0 && @@ -585,10 +584,9 @@ unsigned OptAXLoad2 (CodeSeg* S) !CS_RangeHasLabel (S, I, 3)) { /* Replace with ldy (y+1) / jsr ldaxidx */ - Val = strtoul(E[0]->Arg + 1, &End, 16); - Val++; + Val = E[0]->Num + 1; - X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg(Val), 0, E[0]->LI); + X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (Val), 0, E[0]->LI); CS_InsertEntry (S, X, I+3); X = NewCodeEntry (E[2]->OPC, AM65_ABS, "ldaxidx", 0, E[0]->LI); CS_InsertEntry (S, X, I+4); From 6d96a952fd1b7f3d970299053f5a05c3e82636a2 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira Date: Sun, 20 Jul 2025 18:48:58 +0200 Subject: [PATCH 3/3] Stricter check for incax[1-8] Thanks to @kugelfuhr Co-authored-by: kugelfuhr <98353208+kugelfuhr@users.noreply.github.com> --- src/cc65/coptmisc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cc65/coptmisc.c b/src/cc65/coptmisc.c index 3680a33ab..f52999285 100644 --- a/src/cc65/coptmisc.c +++ b/src/cc65/coptmisc.c @@ -517,7 +517,8 @@ unsigned OptAXLoad (CodeSeg* S) /* Check for incax[1-8] followed by jsr/jmp ldaxi */ if (E->OPC == OP65_JSR && strncmp (E->Arg, "incax", 5) == 0 && - strcmp (E->Arg, "incaxy") != 0 && + E->Arg[5] >= '1' && E->Arg[5] <= '8' && + E->Arg[6] == '\0' && (N = CS_GetNextEntry (S, I)) != 0 && (N->OPC == OP65_JSR || N->OPC == OP65_JMP) && strcmp (N->Arg, "ldaxi") == 0 &&