Prevent the removal of a ldaxysp runtime call as a "load instruction" for only the A or the X side, when the other is non-removable. Fixes issue #2942 / #2461.

This commit is contained in:
Alex Volkov
2026-03-25 19:08:18 -04:00
parent 40a20ab973
commit c9b885b144

View File

@@ -1123,6 +1123,8 @@ void AddOpLow (StackOpData* D, opc_t OPC, LoadInfo* LI)
InsertEntry (D, X, D->IP++);
if (LI->A.LoadEntry->OPC == OP65_JSR) {
/* This should only happen in one known ldaxysp case. */
CHECK (CE_IsCallTo (LI->A.LoadEntry, "ldaxysp"));
/* opc (c_sp),y */
X = NewCodeEntry (OPC, AM65_ZP_INDY, "c_sp", 0, D->OpEntry->LI);
} else {
@@ -1133,7 +1135,7 @@ void AddOpLow (StackOpData* D, opc_t OPC, LoadInfo* LI)
}
/* In both cases, we can remove the load */
/* In both cases, we may try removing the load */
LI->A.Flags |= LI_REMOVE;
} else {
@@ -1188,6 +1190,8 @@ void AddOpHigh (StackOpData* D, opc_t OPC, LoadInfo* LI, int KeepResult)
InsertEntry (D, X, D->IP++);
if (LI->X.LoadEntry->OPC == OP65_JSR) {
/* This should only happen in one known ldaxysp case. */
CHECK (CE_IsCallTo (LI->X.LoadEntry, "ldaxysp"));
/* opc (c_sp),y */
X = NewCodeEntry (OPC, AM65_ZP_INDY, "c_sp", 0, D->OpEntry->LI);
} else {
@@ -1197,7 +1201,7 @@ void AddOpHigh (StackOpData* D, opc_t OPC, LoadInfo* LI, int KeepResult)
InsertEntry (D, X, D->IP++);
}
/* In both cases, we can remove the load */
/* In both cases, we may try removing the load */
LI->X.Flags |= LI_REMOVE;
} else {
@@ -1225,12 +1229,65 @@ void AddOpHigh (StackOpData* D, opc_t OPC, LoadInfo* LI, int KeepResult)
void RemoveRegLoads (StackOpData* D, LoadInfo* LI)
/* Remove register load insns */
{
if ((LI->A.Flags & LI_REMOVE) == LI_REMOVE) {
if (LI->A.LoadIndex >= 0 &&
(LI->A.LoadEntry->Flags & CEF_DONT_REMOVE) == 0) {
DelEntry (D, LI->A.LoadIndex);
}
int CanRemoveA;
int CanRemoveX;
/* When either A or X load insn is a call to ldaxysp runtime, the load
** affects both and must be treated as a single A/X unit. A request to
** remove the runtime call for just one (A or X) load is not valid in that
** case. Either both must be removable+removed, or the other load is
** independent, or ldaxysp runtime call must remain.
*/
CanRemoveA = (LI->A.Flags & LI_REMOVE) == LI_REMOVE &&
LI->A.LoadEntry != 0 &&
(LI->A.LoadEntry->Flags & CEF_DONT_REMOVE) == 0;
CanRemoveX = (LI->X.Flags & LI_REMOVE) == LI_REMOVE &&
LI->X.LoadEntry != 0 &&
(LI->X.LoadEntry->Flags & CEF_DONT_REMOVE) == 0;
/* The only runtime calls removable as load insns are calls to ldaxysp. */
CHECK (!CanRemoveA || LI->A.LoadEntry->OPC != OP65_JSR ||
CE_IsCallTo (LI->A.LoadEntry, "ldaxysp"));
CHECK (!CanRemoveX || LI->X.LoadEntry->OPC != OP65_JSR ||
CE_IsCallTo (LI->X.LoadEntry, "ldaxysp"));
/* When the A load is a runtime call and the X load cannot be removed,
** we cannot remove the A load either.
*/
if (CanRemoveA && LI->A.LoadEntry->OPC == OP65_JSR && !CanRemoveX) {
/* A load is not removable */
LI->A.LoadEntry->Flags |= CEF_DONT_REMOVE;
CanRemoveA = 0;
}
/* When the X load is a runtime call and the A load cannot be removed,
** we cannot remove the X load either.
*/
if (CanRemoveX && LI->X.LoadEntry->OPC == OP65_JSR && !CanRemoveA) {
/* X load is not removable */
LI->X.LoadEntry->Flags |= CEF_DONT_REMOVE;
CanRemoveX = 0;
}
/* A request to remove a "change" insn (ChgIndex) when there is no
** corresponing load insn (LoadIndex) is never valid.
*/
CHECK ((LI->A.Flags & LI_REMOVE) == 0 || LI->A.ChgIndex < 0 ||
LI->A.LoadIndex >= 0);
CHECK ((LI->X.Flags & LI_REMOVE) == 0 || LI->X.ChgIndex < 0 ||
LI->X.LoadIndex >= 0);
if (CanRemoveA) {
CHECK (LI->A.LoadIndex >= 0);
DelEntry (D, LI->A.LoadIndex);
/* Only remove the Y load if it is not shared with a non-removable
** X load. A shared load will be removed here otherwise.
*/
if (LI->A.LoadYIndex >= 0 &&
(LI->A.LoadYIndex != LI->X.LoadYIndex || CanRemoveX) &&
(LI->A.LoadYEntry->Flags & CEF_DONT_REMOVE) == 0) {
DelEntry (D, LI->A.LoadYIndex);
}
@@ -1247,11 +1304,16 @@ void RemoveRegLoads (StackOpData* D, LoadInfo* LI)
LI->A.LoadYEntry->Flags |= CEF_DONT_REMOVE;
}
if ((LI->X.Flags & LI_REMOVE) == LI_REMOVE) {
if (LI->X.LoadIndex >= 0 &&
(LI->X.LoadEntry->Flags & CEF_DONT_REMOVE) == 0) {
DelEntry (D, LI->X.LoadIndex);
}
/* The X load may have already been removed above if it were a runtime
** call (i.e. "jsr ldaxysp"), so need to check again.
*/
if (CanRemoveX && LI->X.LoadIndex >= 0) {
DelEntry (D, LI->X.LoadIndex);
/* Only remove the Y load if it is not shared with non-removable A load.
** If it is shared and still needed, it was flaged non-removable above.
*/
if (LI->X.LoadYIndex >= 0 &&
(LI->X.LoadYEntry->Flags & CEF_DONT_REMOVE) == 0) {
DelEntry (D, LI->X.LoadYIndex);