From d711b6d6fa3526095cf6013c31fa4fba2c3141dc Mon Sep 17 00:00:00 2001 From: Alex Volkov Date: Sun, 22 Mar 2026 19:42:57 -0400 Subject: [PATCH] Prevent Lhs load insns shared with the Rhs from being inadvertently removed by Rhs optimizers. Fixes issue #2947. --- src/cc65/codeoptutil.c | 62 ++++++++++++++++++++++++++++++++++++++++++ src/cc65/codeoptutil.h | 5 ++++ src/cc65/coptstop.c | 6 ++++ 3 files changed, 73 insertions(+) diff --git a/src/cc65/codeoptutil.c b/src/cc65/codeoptutil.c index 44174c5fc..b098845b4 100644 --- a/src/cc65/codeoptutil.c +++ b/src/cc65/codeoptutil.c @@ -509,6 +509,68 @@ void SetIfOperandLoadUnremovable (LoadInfo* LI, unsigned Used) +static int IsRegInsnSameAsOtherAX (int InsnIndex, const LoadInfo* OtherLI) +/* Helper: Return true if the other side (Rhs or Lhs) refers to the same insn */ +{ + if (InsnIndex < 0) { + /* No, do not have an insn */ + return 0; + } + + /* Check if this insn matches any load/change on the other side */ + return InsnIndex == OtherLI->A.LoadIndex || + InsnIndex == OtherLI->A.ChgIndex || + InsnIndex == OtherLI->X.LoadIndex || + InsnIndex == OtherLI->X.ChgIndex; +} + + + +static int IsRegInsnSameAsOtherY (int InsnIndex, const LoadInfo* OtherLI) +/* Helper: Return true if the other side (Rhs or Lhs) refers to the same insn */ +{ + if (InsnIndex < 0) { + /* No, do not have an insn */ + return 0; + } + + /* Check if this insn matches any load/change on the other side */ + return InsnIndex == OtherLI->Y.LoadIndex || + InsnIndex == OtherLI->Y.ChgIndex; +} + + + +void SetUnremovableIfUsedByOther (LoadInfo* LI, const LoadInfo* OtherLI) +/* Check and flag operand loads unremovable if the other side (Rhs or Lhs) +** uses the same load insn (checked by index). +*/ +{ + /* Disallow removing the loads if they are shared between Lhs and Rhs. + ** Note: For safety, we check the ChgIndex as well, which accounts for + ** conditions where a label is encountered in the block. + */ + if (IsRegInsnSameAsOtherAX (LI->A.LoadIndex, OtherLI) || + IsRegInsnSameAsOtherAX (LI->A.ChgIndex, OtherLI)) { + /* Parts of A load are used by other and cannot be removed */ + LI->A.Flags |= LI_DONT_REMOVE; + } + + if (IsRegInsnSameAsOtherAX (LI->X.LoadIndex, OtherLI) || + IsRegInsnSameAsOtherAX (LI->X.ChgIndex, OtherLI)) { + /* Parts of X load are used by other and cannot be removed */ + LI->X.Flags |= LI_DONT_REMOVE; + } + + if (IsRegInsnSameAsOtherY (LI->Y.LoadIndex, OtherLI) || + IsRegInsnSameAsOtherY (LI->Y.ChgIndex, OtherLI)) { + /* Parts of Y load are used by other and cannot be removed */ + LI->Y.Flags |= LI_DONT_REMOVE; + } +} + + + unsigned int TrackLoads (LoadInfo* LI, CodeSeg* S, int I) /* Track loads for a code entry. ** Return used registers. diff --git a/src/cc65/codeoptutil.h b/src/cc65/codeoptutil.h index cf7743605..06a9d4ce3 100644 --- a/src/cc65/codeoptutil.h +++ b/src/cc65/codeoptutil.h @@ -180,6 +180,11 @@ void SetIfOperandSrcAffected (LoadInfo* LLI, CodeEntry* E); void SetIfOperandLoadUnremovable (LoadInfo* LI, unsigned Used); /* Check and flag operand load that may be unremovable */ +void SetUnremovableIfUsedByOther (LoadInfo* LI, const LoadInfo* OtherLI); +/* Check and flag operand loads unremovable if the other side (Rhs or Lhs) +** uses the same load insn (checked by index). +*/ + unsigned int TrackLoads (LoadInfo* LI, CodeSeg* S, int I); /* Track loads for a code entry. ** Return used registers. diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index 945dc1c54..0bc533297 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -2090,6 +2090,12 @@ unsigned OptStackOps (CodeSeg* S) } } + /* Lhs and Rhs may shared some of the load insns; or due to how + ** the labels are processed, "change" insns may refer to the + ** others' loads. In either case, it is unsafe to remove them. + */ + SetUnremovableIfUsedByOther (&Data.Lhs, &Data.Rhs); + /* Flag entries that can't be removed */ SetDontRemoveEntryFlags (&Data);