From 00a1f1d447838f8025036db0bf47aada597af5e1 Mon Sep 17 00:00:00 2001 From: Alex Volkov Date: Wed, 25 Mar 2026 19:14:14 -0400 Subject: [PATCH] Clearly delineate load instruction "must remove" conditions from "may remove" with a new LI_MUST_REMOVE flag. A demand to remove a non-removable load is fatal. --- src/cc65/codeoptutil.c | 6 ++++ src/cc65/codeoptutil.h | 1 + src/cc65/coptstop.c | 72 ++++++++++++++++++++++++------------------ 3 files changed, 48 insertions(+), 31 deletions(-) diff --git a/src/cc65/codeoptutil.c b/src/cc65/codeoptutil.c index c2c815e4c..7b81abb10 100644 --- a/src/cc65/codeoptutil.c +++ b/src/cc65/codeoptutil.c @@ -1270,6 +1270,12 @@ void RemoveRegLoads (StackOpData* D, LoadInfo* LI) CanRemoveX = 0; } + /* A load removal demand which cannot be satisifed is a fatal condition */ + if (((LI->A.Flags & LI_MUST_REMOVE) != 0 && !CanRemoveA) || + ((LI->X.Flags & LI_MUST_REMOVE) != 0 && !CanRemoveX)) { + Internal ("Cannot remove a load instruction which must be removed"); + } + /* A request to remove a "change" insn (ChgIndex) when there is no ** corresponing load insn (LoadIndex) is never valid. */ diff --git a/src/cc65/codeoptutil.h b/src/cc65/codeoptutil.h index 06a9d4ce3..c62ff28f3 100644 --- a/src/cc65/codeoptutil.h +++ b/src/cc65/codeoptutil.h @@ -70,6 +70,7 @@ typedef enum { LI_USED_BY_Y = 0x4000, /* Content used by RegY */ LI_SP = 0x8000, /* Content on stack */ LI_LOAD_INSN = 0x010000, /* Is a load insn */ + LI_MUST_REMOVE = 0x020000, /* Load must be removed */ } LI_FLAGS; /* Structure that tells us how to load the lhs values */ diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index 0bc533297..57985fda0 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -436,9 +436,9 @@ static unsigned Opt_toseqax_tosneax (StackOpData* D, const char* BoolTransformer X = NewCodeEntry (OP65_CMP, LoadA->AM, LoadA->Arg, 0, D->OpEntry->LI); InsertEntry (D, X, D->IP++); - /* Rhs load entries must be removed */ - D->Rhs.X.Flags |= LI_REMOVE; - D->Rhs.A.Flags |= LI_REMOVE; + /* Rhs load entries must be removed because Lhs must be in effect */ + D->Rhs.X.Flags |= (LI_REMOVE | LI_MUST_REMOVE); + D->Rhs.A.Flags |= (LI_REMOVE | LI_MUST_REMOVE); } else if (RhsIsRemovable (D)) { @@ -454,6 +454,10 @@ static unsigned Opt_toseqax_tosneax (StackOpData* D, const char* BoolTransformer /* Add operand for high byte */ AddOpHigh (D, OP65_CMP, &D->Rhs, 0); + /* Rhs load entries must be removed because Lhs must be in effect */ + D->Rhs.X.Flags |= LI_MUST_REMOVE; + D->Rhs.A.Flags |= LI_MUST_REMOVE; + } else { /* Implement the op via a temp ZP location */ @@ -1018,9 +1022,9 @@ static unsigned Opt_tosgeax (StackOpData* D) X = NewCodeEntry (OP65_ROL, AM65_ACC, "a", 0, D->OpEntry->LI); InsertEntry (D, X, D->IP++); - /* Rhs load entries must be removed */ - D->Rhs.X.Flags |= LI_REMOVE; - D->Rhs.A.Flags |= LI_REMOVE; + /* Rhs load entries must be removed because Lhs must be in effect */ + D->Rhs.X.Flags |= LI_MUST_REMOVE; + D->Rhs.A.Flags |= LI_MUST_REMOVE; /* Remove the push and the call to the tosgeax function */ RemoveRemainders (D); @@ -1075,9 +1079,9 @@ static unsigned Opt_tosltax (StackOpData* D) X = NewCodeEntry (OP65_ROL, AM65_ACC, "a", 0, D->OpEntry->LI); InsertEntry (D, X, D->IP++); - /* Rhs load entries must be removed */ - D->Rhs.X.Flags |= LI_REMOVE; - D->Rhs.A.Flags |= LI_REMOVE; + /* Rhs load entries must be removed because Lhs must be in effect */ + D->Rhs.X.Flags |= LI_MUST_REMOVE; + D->Rhs.A.Flags |= LI_MUST_REMOVE; /* Remove the push and the call to the tosltax function */ RemoveRemainders (D); @@ -1158,9 +1162,9 @@ static unsigned Opt_tossubax (StackOpData* D) /* Add code for high operand */ AddOpHigh (D, OP65_SBC, &D->Rhs, 1); - /* Rhs load entries must be removed */ - D->Rhs.X.Flags |= LI_REMOVE; - D->Rhs.A.Flags |= LI_REMOVE; + /* Rhs load entries must be removed because Lhs must be in effect */ + D->Rhs.X.Flags |= LI_MUST_REMOVE; + D->Rhs.A.Flags |= LI_MUST_REMOVE; /* Remove the push and the call to the tossubax function */ RemoveRemainders (D); @@ -1200,9 +1204,9 @@ static unsigned Opt_tosugeax (StackOpData* D) X = NewCodeEntry (OP65_ROL, AM65_ACC, "a", 0, D->OpEntry->LI); InsertEntry (D, X, D->IP++); - /* Rhs load entries must be removed */ - D->Rhs.X.Flags |= LI_REMOVE; - D->Rhs.A.Flags |= LI_REMOVE; + /* Rhs load entries must be removed because Lhs must be in effect */ + D->Rhs.X.Flags |= LI_MUST_REMOVE; + D->Rhs.A.Flags |= LI_MUST_REMOVE; /* Remove the push and the call to the tosugeax function */ RemoveRemainders (D); @@ -1246,9 +1250,9 @@ static unsigned Opt_tosugtax (StackOpData* D) X = NewCodeEntry (OP65_JSR, AM65_ABS, "boolugt", 0, D->OpEntry->LI); InsertEntry (D, X, D->IP++); - /* Rhs load entries must be removed */ - D->Rhs.X.Flags |= LI_REMOVE; - D->Rhs.A.Flags |= LI_REMOVE; + /* Rhs load entries must be removed because Lhs must be in effect */ + D->Rhs.X.Flags |= LI_MUST_REMOVE; + D->Rhs.A.Flags |= LI_MUST_REMOVE; /* Remove the push and the call to the operator function */ RemoveRemainders (D); @@ -1292,9 +1296,9 @@ static unsigned Opt_tosuleax (StackOpData* D) X = NewCodeEntry (OP65_JSR, AM65_ABS, "boolule", 0, D->OpEntry->LI); InsertEntry (D, X, D->IP++); - /* Rhs load entries must be removed */ - D->Rhs.X.Flags |= LI_REMOVE; - D->Rhs.A.Flags |= LI_REMOVE; + /* Rhs load entries must be removed because Lhs must be in effect */ + D->Rhs.X.Flags |= LI_MUST_REMOVE; + D->Rhs.A.Flags |= LI_MUST_REMOVE; /* Remove the push and the call to the operator function */ RemoveRemainders (D); @@ -1326,9 +1330,9 @@ static unsigned Opt_tosultax (StackOpData* D) X = NewCodeEntry (OP65_JSR, AM65_ABS, "boolult", 0, D->OpEntry->LI); InsertEntry (D, X, D->IP++); - /* Rhs load entries must be removed */ - D->Rhs.X.Flags |= LI_REMOVE; - D->Rhs.A.Flags |= LI_REMOVE; + /* Rhs load entries must be removed because Lhs must be in effect */ + D->Rhs.X.Flags |= LI_MUST_REMOVE; + D->Rhs.A.Flags |= LI_MUST_REMOVE; /* Remove the push and the call to the operator function */ RemoveRemainders (D); @@ -1396,8 +1400,8 @@ static unsigned Opt_a_tosbitwise (StackOpData* D, opc_t OPC) X = NewCodeEntry (OPC, D->Rhs.A.LoadEntry->AM, D->Rhs.A.LoadEntry->Arg, 0, D->OpEntry->LI); InsertEntry (D, X, D->IP++); - /* Rhs load entries must be removed */ - D->Rhs.A.Flags |= LI_REMOVE; + /* Rhs A load must be removed because Lhs must be in effect */ + D->Rhs.A.Flags |= (LI_REMOVE | LI_MUST_REMOVE); } else if (RegIsDirectNonStackLoaded (&D->Lhs.A)) { @@ -1433,10 +1437,12 @@ static unsigned Opt_a_tosbitwise (StackOpData* D, opc_t OPC) /* Since this is a "same X" EOR, the result is always 0. */ X = NewCodeEntry (OP65_LDX, AM65_IMM, MakeHexArg (0), 0, D->Rhs.X.ChgEntry->LI); InsertEntry (D, X, D->IP++); + + /* Rhs X load may be removed (but this is not a demand) */ D->Rhs.X.Flags |= LI_REMOVE; } } else { - /* Rhs load entries may be removed */ + /* Rhs X load may be removed (but this is not a demand) */ D->Rhs.X.Flags |= LI_REMOVE; } @@ -1464,7 +1470,7 @@ static unsigned Opt_a_toscmpbool (StackOpData* D, const char* BoolTransformer) /* Rhs low-byte load must be removed and hi-byte load may be removed */ D->Rhs.X.Flags |= LI_REMOVE; - D->Rhs.A.Flags |= LI_REMOVE; + D->Rhs.A.Flags |= LI_MUST_REMOVE; } else if (RegIsDirectLoaded (&D->Lhs.A)) { /* If the lhs is direct (but not stack relative), encode compares with lhs, @@ -1604,7 +1610,9 @@ static unsigned Opt_a_tosicmp (StackOpData* D) InsertEntry (D, X, D->IP++); } - /* RHS may be removed */ + /* RHS may be removed, but it is not a demand because Lhs was + ** reloaded at Op and is in effect. + */ D->Rhs.A.Flags |= LI_REMOVE; D->Rhs.X.Flags |= LI_REMOVE; } @@ -1703,9 +1711,11 @@ static unsigned Opt_a_tossub (StackOpData* D) InsertEntry (D, X, D->IP++); } - /* Rhs load entries must be removed */ + /* Rhs low-byte load must be removed (because Lhs must be in effect) + ** and hi-byte load may be removed. + */ D->Rhs.X.Flags |= LI_REMOVE; - D->Rhs.A.Flags |= LI_REMOVE; + D->Rhs.A.Flags |= LI_MUST_REMOVE; /* Remove the push and the call to the tossubax function */ RemoveRemainders (D);