*/
{
unsigned Changes = 0;
- unsigned Xfer = 0; /* Index of transfer insn */
- unsigned Store = 0; /* Index of store insn */
- CodeEntry* XferEntry = 0; /* Pointer to xfer insn */
- CodeEntry* StoreEntry = 0; /* Pointer to store insn */
+ unsigned UsedRegs = REG_NONE; /* Track used registers */
+ unsigned Xfer = 0; /* Index of transfer insn */
+ unsigned Store = 0; /* Index of store insn */
+ CodeEntry* XferEntry = 0; /* Pointer to xfer insn */
+ CodeEntry* StoreEntry = 0; /* Pointer to store insn */
enum {
- Searching,
+ Initialize,
+ Search,
FoundXfer,
FoundStore
- } State = Searching;
+ } State = Initialize;
/* Walk over the entries. Look for a xfer instruction that is followed by
* a store later, where the value of the register is not used later.
switch (State) {
- case Searching:
+ case Initialize:
+ /* Clear the list of used registers */
+ UsedRegs = REG_NONE;
+ /* FALLTHROUGH */
+
+ case Search:
if (E->Info & OF_XFR) {
/* Found start of sequence */
Xfer = I;
/* Switch back to searching */
I = Xfer;
- State = Searching;
+ State = Initialize;
/* Does this insn use the target register of the transfer? */
} else if ((E->Use & XferEntry->Chg) != 0) {
State = FoundStore;
} else {
I = Xfer;
- State = Searching;
+ State = Initialize;
}
/* Does this insn change the target register of the transfer? */
* do that and bail out instead.
*/
I = Xfer;
- State = Searching;
+ State = Initialize;
/* Does this insn have a label? */
} else if (CE_HasLabel (E)) {
/* Too complex to handle - bail out */
I = Xfer;
- State = Searching;
+ State = Initialize;
+ } else {
+ /* Track used registers */
+ UsedRegs |= E->Use;
}
break;
* replace the transfer by a store and remove the store here.
*/
if ((GetRegInfo (S, I, XferEntry->Chg) & XferEntry->Chg) == 0 &&
- (StoreEntry->AM == AM65_ABS || StoreEntry->AM == AM65_ZP) &&
+ (StoreEntry->AM == AM65_ABS ||
+ StoreEntry->AM == AM65_ZP) &&
+ (StoreEntry->AM != AM65_ZP ||
+ (StoreEntry->Chg & UsedRegs) == 0) &&
!MemAccess (S, Xfer+1, Store-1, StoreEntry->Arg)) {
/* Generate the replacement store insn */
/* Restart after last xfer insn */
I = Xfer;
}
- State = Searching;
+ State = Initialize;
break;
}
CodeEntry* XferEntry = 0; /* Pointer to xfer insn */
enum {
- Searching,
+ Search,
FoundLoad,
FoundXfer
- } State = Searching;
+ } State = Search;
/* Walk over the entries. Look for a load instruction that is followed by
* a load later.
switch (State) {
- case Searching:
+ case Search:
if (E->Info & OF_LOAD) {
/* Found start of sequence */
Load = I;
/* Switch back to searching */
I = Load;
- State = Searching;
+ State = Search;
/* Does this insn use the target register of the load? */
} else if ((E->Use & LoadEntry->Chg) != 0) {
State = FoundXfer;
} else {
I = Load;
- State = Searching;
+ State = Search;
}
/* Does this insn change the target register of the load? */
* do that and bail out instead.
*/
I = Load;
- State = Searching;
+ State = Search;
}
break;
/* Restart after last xfer insn */
I = Xfer;
}
- State = Searching;
+ State = Search;
break;
}
/* Flags for the functions */
typedef enum {
STOP_NONE = 0x00, /* Nothing special */
- STOP_A_UNUSED = 0x01, /* Call only if a unused later */
- STOP_A_KNOWN = 0x02, /* Call only if A is known */
- STOP_X_ZERO = 0x04 /* Call only if X is zero */
+ STOP_A_KNOWN = 0x01, /* Call only if A is known */
+ STOP_X_ZERO = 0x02 /* Call only if X is zero */
} STOP_FLAGS;
/* Structure forward decl */
struct OptFuncDesc {
const char* Name; /* Name of the replaced runtime function */
OptFunc Func; /* Function pointer */
+ unsigned UnusedRegs; /* Regs that must not be used later */
STOP_FLAGS Flags; /* Flags */
};
/* If we need the value of Y later, be sure to reload it */
if (RegYUsed (D->Code, I+1)) {
- const char* Arg = MakeHexArg (E->RI->In.RegY);
+ const char* Arg = MakeHexArg (E->RI->In.RegY);
CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
CS_InsertEntry (D->Code, X, I+1);
X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, D->ZPLo, 0, D->OpEntry->LI);
InsertEntry (D, X, D->OpIndex+4);
- /* If we remove staspidx, we must restore the Y register to what the
+ /* If we remove staxspidx, we must restore the Y register to what the
* function would return.
*/
X = NewCodeEntry (OP65_LDY, AM65_IMM, "$00", 0, D->OpEntry->LI);
static const OptFuncDesc FuncTable[] = {
- { "__bzero", Opt___bzero, STOP_X_ZERO | STOP_A_KNOWN },
- { "staspidx", Opt_staspidx, STOP_NONE },
- { "staxspidx", Opt_staxspidx, STOP_A_UNUSED },
- { "tosaddax", Opt_tosaddax, STOP_NONE },
- { "tosandax", Opt_tosandax, STOP_NONE },
- { "tosorax", Opt_tosorax, STOP_NONE },
- { "tossubax", Opt_tossubax, STOP_NONE },
- { "tosxorax", Opt_tosxorax, STOP_NONE },
+ { "__bzero", Opt___bzero, REG_NONE, STOP_X_ZERO | STOP_A_KNOWN },
+ { "staspidx", Opt_staspidx, REG_NONE, STOP_NONE },
+ { "staxspidx", Opt_staxspidx, REG_AX, STOP_NONE },
+ { "tosaddax", Opt_tosaddax, REG_NONE, STOP_NONE },
+ { "tosandax", Opt_tosandax, REG_NONE, STOP_NONE },
+ { "tosorax", Opt_tosorax, REG_NONE, STOP_NONE },
+ { "tossubax", Opt_tossubax, REG_NONE, STOP_NONE },
+ { "tosxorax", Opt_tosxorax, REG_NONE, STOP_NONE },
};
#define FUNC_COUNT (sizeof(FuncTable) / sizeof(FuncTable[0]))
*/
{
/* Check the flags */
- if ((D->OptFunc->Flags & STOP_A_UNUSED) != 0 &&
- RegAUsed (D->Code, D->OpIndex+1)) {
+ unsigned UnusedRegs = D->OptFunc->UnusedRegs;
+ if (UnusedRegs != REG_NONE &&
+ (GetRegInfo (D->Code, D->OpIndex+1, UnusedRegs) & UnusedRegs) != 0) {
/* Cannot optimize */
return 0;
- } else if ((D->OptFunc->Flags & STOP_A_KNOWN) != 0 &&
- RegValIsUnknown (D->OpEntry->RI->In.RegA)) {
+ }
+ if ((D->OptFunc->Flags & STOP_A_KNOWN) != 0 &&
+ RegValIsUnknown (D->OpEntry->RI->In.RegA)) {
/* Cannot optimize */
return 0;
- } else if ((D->OptFunc->Flags & STOP_X_ZERO) != 0 &&
- D->OpEntry->RI->In.RegX != 0) {
+ }
+ if ((D->OptFunc->Flags & STOP_X_ZERO) != 0 &&
+ D->OpEntry->RI->In.RegX != 0) {
/* Cannot optimize */
return 0;
}
/* Track zero page location usage beyond this point */
Data.UsedRegs |= GetRegInfo (S, I, REG_SREG | REG_PTR1 | REG_PTR2);
+ /* Get the entry pointers to the load insns. If these insns
+ * load from zero page, we have to include them into UsedRegs
+ * registers used.
+ */
+ if (Data.LoadAIndex >= 0) {
+ Data.LoadAEntry = CS_GetEntry (S, Data.LoadAIndex);
+ if (Data.LoadAEntry->AM == AM65_ZP) {
+ Data.UsedRegs |= Data.LoadAEntry->Use;
+ }
+ }
+ if (Data.LoadXIndex >= 0) {
+ Data.LoadXEntry = CS_GetEntry (S, Data.LoadXIndex);
+ if (Data.LoadXEntry->AM == AM65_ZP) {
+ Data.UsedRegs |= Data.LoadXEntry->Use;
+ }
+ }
+
/* Check the preconditions. If they aren't ok, reset the insn
* pointer to the pushax and start over. We will loose part of
* load tracking but at least a/x has probably lost between
break;
}
- /* Preconditions are ok, so call the optimizer function */
-
/* Adjust stack offsets to account for the upcoming removal */
AdjustStackOffset (&Data, 2);
- /* Prepare the remainder of the data structure */
- if (Data.LoadAIndex >= 0) {
- Data.LoadAEntry = CS_GetEntry (S, Data.LoadAIndex);
- }
- if (Data.LoadXIndex >= 0) {
- Data.LoadXEntry = CS_GetEntry (S, Data.LoadXIndex);
- }
+ /* Prepare the remainder of the data structure. */
Data.PrevEntry = CS_GetPrevEntry (S, Data.PushIndex);
Data.PushEntry = CS_GetEntry (S, Data.PushIndex);
Data.OpEntry = CS_GetEntry (S, Data.OpIndex);