mirror of
https://github.com/cc65/cc65.git
synced 2025-01-11 11:30:13 +00:00
Added processor flag usage/change-tracking for non-JSR/RTS code entries.
Some existing optimizations are impacted and need fixes in order to work correctly again. Fixed and improved OptPrecalc. Now it respects the C/V/Z/N flags. Fixed optimizations impacted by added support of tracking processor flags.
This commit is contained in:
parent
d02b12fa6c
commit
a7d6eb9190
@ -206,6 +206,145 @@ static void SetUseChgInfo (CodeEntry* E, const OPCDesc* D)
|
||||
/* Keep gcc silent */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Append processor flags as well as special usages */
|
||||
switch (E->OPC) {
|
||||
|
||||
case OP65_ADC:
|
||||
case OP65_SBC:
|
||||
E->Use |= PSTATE_C;
|
||||
E->Chg |= PSTATE_CZVN;
|
||||
break;
|
||||
case OP65_ROL:
|
||||
case OP65_ROR:
|
||||
E->Use |= PSTATE_C;
|
||||
E->Chg |= PSTATE_CZN;
|
||||
break;
|
||||
case OP65_ASL:
|
||||
case OP65_LSR:
|
||||
E->Chg |= PSTATE_CZN;
|
||||
break;
|
||||
case OP65_CMP:
|
||||
case OP65_CPX:
|
||||
case OP65_CPY:
|
||||
E->Chg |= PSTATE_CZN;
|
||||
break;
|
||||
case OP65_BIT:
|
||||
E->Chg |= PSTATE_ZVN;
|
||||
if (E->AM != AM65_IMM) {
|
||||
E->Chg &= ~(PSTATE_V | PSTATE_N);
|
||||
}
|
||||
break;
|
||||
case OP65_BRK:
|
||||
E->Chg |= PSTATE_B;
|
||||
break;
|
||||
case OP65_CLC:
|
||||
case OP65_SEC:
|
||||
E->Chg |= PSTATE_C;
|
||||
break;
|
||||
case OP65_CLD:
|
||||
case OP65_SED:
|
||||
E->Chg |= PSTATE_D;
|
||||
break;
|
||||
case OP65_CLI:
|
||||
case OP65_SEI:
|
||||
E->Chg |= PSTATE_I;
|
||||
break;
|
||||
case OP65_CLV:
|
||||
E->Chg |= PSTATE_V;
|
||||
break;
|
||||
case OP65_TRB:
|
||||
case OP65_TSB:
|
||||
E->Chg |= PSTATE_Z;
|
||||
break;
|
||||
case OP65_BCC:
|
||||
case OP65_BCS:
|
||||
case OP65_JCC:
|
||||
case OP65_JCS:
|
||||
E->Use |= PSTATE_C;
|
||||
break;
|
||||
case OP65_BEQ:
|
||||
case OP65_BNE:
|
||||
case OP65_JEQ:
|
||||
case OP65_JNE:
|
||||
E->Use |= PSTATE_Z;
|
||||
break;
|
||||
case OP65_BMI:
|
||||
case OP65_BPL:
|
||||
case OP65_JMI:
|
||||
case OP65_JPL:
|
||||
E->Use |= PSTATE_N;
|
||||
break;
|
||||
case OP65_BVC:
|
||||
case OP65_BVS:
|
||||
case OP65_JVC:
|
||||
case OP65_JVS:
|
||||
E->Use |= PSTATE_V;
|
||||
break;
|
||||
case OP65_BRA:
|
||||
case OP65_JMP:
|
||||
break;
|
||||
case OP65_AND:
|
||||
case OP65_EOR:
|
||||
case OP65_ORA:
|
||||
case OP65_DEA:
|
||||
case OP65_DEC:
|
||||
case OP65_DEX:
|
||||
case OP65_DEY:
|
||||
case OP65_INA:
|
||||
case OP65_INC:
|
||||
case OP65_INX:
|
||||
case OP65_INY:
|
||||
case OP65_LDA:
|
||||
case OP65_LDX:
|
||||
case OP65_LDY:
|
||||
case OP65_TAX:
|
||||
case OP65_TAY:
|
||||
case OP65_TXA:
|
||||
case OP65_TYA:
|
||||
E->Chg |= PSTATE_ZN;
|
||||
break;
|
||||
case OP65_TSX:
|
||||
E->Use |= SLV_SP65;
|
||||
E->Chg |= PSTATE_ZN;
|
||||
break;
|
||||
case OP65_TXS:
|
||||
E->Chg |= SLV_SP65;
|
||||
break;
|
||||
case OP65_PLA:
|
||||
case OP65_PLX:
|
||||
case OP65_PLY:
|
||||
E->Use |= SLV_SP65;
|
||||
E->Chg |= SLV_PL65 | PSTATE_ZN;
|
||||
break;
|
||||
case OP65_PLP:
|
||||
E->Use |= SLV_SP65;
|
||||
E->Chg |= SLV_PL65 | PSTATE_ALL;
|
||||
break;
|
||||
case OP65_PHA:
|
||||
case OP65_PHX:
|
||||
case OP65_PHY:
|
||||
E->Use |= SLV_SP65;
|
||||
E->Chg |= SLV_PH65;
|
||||
break;
|
||||
case OP65_PHP:
|
||||
E->Use |= SLV_SP65 | PSTATE_ALL;
|
||||
E->Chg |= SLV_PH65;
|
||||
break;
|
||||
case OP65_RTI:
|
||||
E->Chg |= PSTATE_ALL;
|
||||
break;
|
||||
case OP65_RTS:
|
||||
break;
|
||||
case OP65_STA:
|
||||
case OP65_STX:
|
||||
case OP65_STY:
|
||||
case OP65_STZ:
|
||||
case OP65_JSR:
|
||||
case OP65_NOP:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -672,7 +672,7 @@ unsigned OptTransfers2 (CodeSeg* S)
|
||||
(N = CS_GetNextEntry (S, I)) != 0 &&
|
||||
!CE_HasLabel (N) &&
|
||||
(N->Info & OF_XFR) != 0 &&
|
||||
(GetRegInfo (S, I+2, E->Chg) & E->Chg) == 0) {
|
||||
(GetRegInfo (S, I+2, E->Chg & REG_ALL) & E->Chg & REG_ALL) == 0) {
|
||||
|
||||
CodeEntry* X = 0;
|
||||
|
||||
@ -796,7 +796,7 @@ unsigned OptTransfers3 (CodeSeg* S)
|
||||
}
|
||||
|
||||
/* Does this insn change the target register of the transfer? */
|
||||
} else if (E->Chg & XferEntry->Chg) {
|
||||
} else if (E->Chg & XferEntry->Chg & ~PSTATE_ZN) {
|
||||
|
||||
/* We *may* add code here to remove the transfer, but I'm
|
||||
** currently not sure about the consequences, so I won't
|
||||
@ -823,8 +823,9 @@ unsigned OptTransfers3 (CodeSeg* S)
|
||||
** isn't used later, and we have an address mode match, we can
|
||||
** replace the transfer by a store and remove the store here.
|
||||
*/
|
||||
if ((GetRegInfo (S, I, XferEntry->Chg) & XferEntry->Chg) == 0 &&
|
||||
(StoreEntry->AM == AM65_ABS ||
|
||||
if ((GetRegInfo (S, I, XferEntry->Chg & REG_ALL) &
|
||||
XferEntry->Chg & REG_ALL) == 0 &&
|
||||
(StoreEntry->AM == AM65_ABS ||
|
||||
StoreEntry->AM == AM65_ZP) &&
|
||||
(StoreEntry->AM != AM65_ZP ||
|
||||
(StoreEntry->Chg & UsedRegs) == 0) &&
|
||||
@ -973,7 +974,7 @@ unsigned OptTransfers4 (CodeSeg* S)
|
||||
}
|
||||
|
||||
/* Does this insn change the target register of the load? */
|
||||
} else if (E->Chg & LoadEntry->Chg) {
|
||||
} else if (E->Chg & LoadEntry->Chg & ~PSTATE_ZN) {
|
||||
|
||||
/* We *may* add code here to remove the load, but I'm
|
||||
** currently not sure about the consequences, so I won't
|
||||
@ -989,9 +990,10 @@ unsigned OptTransfers4 (CodeSeg* S)
|
||||
** isn't used later, and we have an address mode match, we can
|
||||
** replace the transfer by a load and remove the initial load.
|
||||
*/
|
||||
if ((GetRegInfo (S, I, LoadEntry->Chg) & LoadEntry->Chg) == 0 &&
|
||||
(LoadEntry->AM == AM65_ABS ||
|
||||
LoadEntry->AM == AM65_ZP ||
|
||||
if ((GetRegInfo (S, I, LoadEntry->Chg & REG_ALL) &
|
||||
LoadEntry->Chg & REG_ALL) == 0 &&
|
||||
(LoadEntry->AM == AM65_ABS ||
|
||||
LoadEntry->AM == AM65_ZP ||
|
||||
LoadEntry->AM == AM65_IMM) &&
|
||||
!MemAccess (S, Load+1, Xfer-1, LoadEntry)) {
|
||||
|
||||
@ -1246,36 +1248,70 @@ unsigned OptPrecalc (CodeSeg* S)
|
||||
}
|
||||
break;
|
||||
|
||||
case OP65_EOR:
|
||||
if (RegValIsKnown (Out->RegA)) {
|
||||
/* Accu op zp with known contents */
|
||||
Arg = MakeHexArg (Out->RegA);
|
||||
}
|
||||
break;
|
||||
|
||||
case OP65_ADC:
|
||||
case OP65_SBC:
|
||||
/* If this is an operation with an immediate operand of zero,
|
||||
** and the register is zero, the operation won't give us any
|
||||
** results we don't already have (including the flags), so
|
||||
** remove it. Something like this is generated as a result of
|
||||
** a compare where parts of the values are known to be zero.
|
||||
** The only situation where we need to leave things as they are
|
||||
** is when V flag is being tested in the next instruction,
|
||||
** because ADC/SBC #0 always clears it.
|
||||
*/
|
||||
if (In->RegA == 0 && CE_IsKnownImm (E, 0x00) &&
|
||||
(E = CS_GetEntry (S, I + 1)) &&
|
||||
E->OPC != OP65_BVC &&
|
||||
E->OPC != OP65_BVS ) {
|
||||
/* 0-0 or 0+0 -> remove */
|
||||
CS_DelEntry (S, I);
|
||||
++Changes;
|
||||
if (CE_IsKnownImm (E, 0x00)) {
|
||||
/* If this is an operation with an immediate operand of zero,
|
||||
** and the Z/N flags reflect the current states of the content
|
||||
** in A, then the operation won't give us any results we don't
|
||||
** already have (including the flags) as long as the C flag is
|
||||
** set normally (cleared for ADC and set for SBC) for the
|
||||
** operation. So we can remove the operation if it is the
|
||||
** normal case or the result in A is not used later.
|
||||
** Something like this is generated as a result of a compare
|
||||
** where parts of the values are known to be zero.
|
||||
** The only situation where we need to leave things as they
|
||||
** are is when an indeterminate V flag is being tested later,
|
||||
** because ADC/SBC #0 always clears it.
|
||||
*/
|
||||
int CondC = PStatesAreKnown (In->PFlags, PSTATE_C) &&
|
||||
((E->OPC == OP65_ADC && (In->PFlags & PFVAL_C) == 0) ||
|
||||
(E->OPC == OP65_SBC && (In->PFlags & PFVAL_C) != 0));
|
||||
int CondV = PStatesAreKnown (In->PFlags, PSTATE_V) && (In->PFlags & PFVAL_V) == 0;
|
||||
int CondZN = (In->ZNRegs & ZNREG_A) != 0;
|
||||
unsigned R = 0;
|
||||
if (CondC) {
|
||||
R = (CondV ? 0 : PSTATE_V) | (CondZN ? 0 : PSTATE_ZN);
|
||||
} else {
|
||||
R = REG_A | PSTATE_CZVN;
|
||||
}
|
||||
if (R != 0) {
|
||||
/* Collect info on all flags in one round to save time */
|
||||
R = GetRegInfo (S, I + 1, R);
|
||||
}
|
||||
CondV = (CondC && CondV) || (R & PSTATE_V) == 0;
|
||||
CondZN = (CondC && CondZN) || (R & PSTATE_ZN) == 0;
|
||||
/* This is done last as it could change the info used by the two above */
|
||||
CondC = CondC || (R & (REG_A | PSTATE_C)) == 0;
|
||||
if (CondC && CondV && CondZN) {
|
||||
/* ?+0, ?-0 or result unused -> remove */
|
||||
CS_DelEntry (S, I);
|
||||
++Changes;
|
||||
}
|
||||
} else if (E->OPC == OP65_ADC && In->RegA == 0) {
|
||||
/* 0 + arg. In this case we need only care about the C/V flags and
|
||||
** let the load set the Z/N flags properly.
|
||||
*/
|
||||
int CondC = PStatesAreClear (In->PFlags, PSTATE_C);
|
||||
int CondV = PStatesAreClear (In->PFlags, PSTATE_V);
|
||||
unsigned R = (CondC ? 0 : REG_A | PSTATE_C) | (CondC && CondV ? 0 : PSTATE_V);
|
||||
if (R) {
|
||||
R = GetRegInfo (S, I + 1, R);
|
||||
}
|
||||
CondV = (CondC && CondV) || (R & PSTATE_V) == 0;
|
||||
CondC = CondC || (R & (REG_A | PSTATE_C)) == 0;
|
||||
if (CondC && CondV) {
|
||||
/* 0 + arg -> replace with lda arg */
|
||||
CE_ReplaceOPC (E, OP65_LDA);
|
||||
++Changes;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case OP65_AND:
|
||||
if (CE_IsKnownImm (E, 0xFF)) {
|
||||
if (CE_IsKnownImm (E, 0xFF) &&
|
||||
((In->ZNRegs & ZNREG_A) != 0 ||
|
||||
(GetRegInfo (S, I + 1, PSTATE_ZN) & PSTATE_ZN) == 0)) {
|
||||
/* AND with 0xFF, remove */
|
||||
CS_DelEntry (S, I);
|
||||
++Changes;
|
||||
@ -1293,7 +1329,9 @@ unsigned OptPrecalc (CodeSeg* S)
|
||||
break;
|
||||
|
||||
case OP65_ORA:
|
||||
if (CE_IsKnownImm (E, 0x00)) {
|
||||
if (CE_IsKnownImm (E, 0x00) &&
|
||||
((In->ZNRegs & ZNREG_A) != 0 ||
|
||||
(GetRegInfo (S, I + 1, PSTATE_ZN) & PSTATE_ZN) == 0)) {
|
||||
/* ORA with zero, remove */
|
||||
CS_DelEntry (S, I);
|
||||
++Changes;
|
||||
@ -1310,6 +1348,23 @@ unsigned OptPrecalc (CodeSeg* S)
|
||||
}
|
||||
break;
|
||||
|
||||
case OP65_EOR:
|
||||
if (CE_IsKnownImm (E, 0x00) &&
|
||||
((In->ZNRegs & ZNREG_A) != 0 ||
|
||||
(GetRegInfo (S, I + 1, PSTATE_ZN) & PSTATE_ZN) == 0)) {
|
||||
/* EOR with zero, remove */
|
||||
CS_DelEntry (S, I);
|
||||
++Changes;
|
||||
} else if (RegValIsKnown (Out->RegA)) {
|
||||
/* Accu op zp with known contents */
|
||||
Arg = MakeHexArg (Out->RegA);
|
||||
} else if (In->RegA == 0) {
|
||||
/* EOR but A contains 0x00 - replace by lda */
|
||||
CE_ReplaceOPC (E, OP65_LDA);
|
||||
++Changes;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
|
@ -153,7 +153,7 @@ unsigned OptTest2 (CodeSeg* S)
|
||||
(L[2]->Info & OF_FBRA) != 0 &&
|
||||
L[1]->AM == L[0]->AM &&
|
||||
strcmp (L[0]->Arg, L[1]->Arg) == 0 &&
|
||||
(GetRegInfo (S, I+2, L[1]->Chg) & L[1]->Chg) == 0) {
|
||||
(GetRegInfo (S, I+2, L[1]->Chg & ~PSTATE_ZN) & L[1]->Chg & ~PSTATE_ZN) == 0) {
|
||||
|
||||
/* Remove the load */
|
||||
CS_DelEntry (S, I+1);
|
||||
|
Loading…
x
Reference in New Issue
Block a user