mirror of
https://github.com/cc65/cc65.git
synced 2024-11-19 06:31:31 +00:00
Added several constraints to the optimizer functions to avoid breaking code.
git-svn-id: svn://svn.cc65.org/cc65/trunk@4046 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
parent
4a93f188a0
commit
f63964e23c
@ -1319,16 +1319,18 @@ unsigned OptTransfers3 (CodeSeg* S)
|
||||
*/
|
||||
{
|
||||
unsigned Changes = 0;
|
||||
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.
|
||||
@ -1341,7 +1343,12 @@ unsigned OptTransfers3 (CodeSeg* S)
|
||||
|
||||
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;
|
||||
@ -1358,7 +1365,7 @@ unsigned OptTransfers3 (CodeSeg* S)
|
||||
|
||||
/* 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) {
|
||||
@ -1373,7 +1380,7 @@ unsigned OptTransfers3 (CodeSeg* S)
|
||||
State = FoundStore;
|
||||
} else {
|
||||
I = Xfer;
|
||||
State = Searching;
|
||||
State = Initialize;
|
||||
}
|
||||
|
||||
/* Does this insn change the target register of the transfer? */
|
||||
@ -1384,15 +1391,18 @@ unsigned OptTransfers3 (CodeSeg* S)
|
||||
* 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;
|
||||
|
||||
@ -1402,7 +1412,10 @@ unsigned OptTransfers3 (CodeSeg* S)
|
||||
* 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 */
|
||||
@ -1469,7 +1482,7 @@ unsigned OptTransfers3 (CodeSeg* S)
|
||||
/* Restart after last xfer insn */
|
||||
I = Xfer;
|
||||
}
|
||||
State = Searching;
|
||||
State = Initialize;
|
||||
break;
|
||||
|
||||
}
|
||||
@ -1496,10 +1509,10 @@ unsigned OptTransfers4 (CodeSeg* S)
|
||||
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.
|
||||
@ -1512,7 +1525,7 @@ unsigned OptTransfers4 (CodeSeg* S)
|
||||
|
||||
switch (State) {
|
||||
|
||||
case Searching:
|
||||
case Search:
|
||||
if (E->Info & OF_LOAD) {
|
||||
/* Found start of sequence */
|
||||
Load = I;
|
||||
@ -1529,7 +1542,7 @@ unsigned OptTransfers4 (CodeSeg* S)
|
||||
|
||||
/* 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) {
|
||||
@ -1544,7 +1557,7 @@ unsigned OptTransfers4 (CodeSeg* S)
|
||||
State = FoundXfer;
|
||||
} else {
|
||||
I = Load;
|
||||
State = Searching;
|
||||
State = Search;
|
||||
}
|
||||
|
||||
/* Does this insn change the target register of the load? */
|
||||
@ -1555,7 +1568,7 @@ unsigned OptTransfers4 (CodeSeg* S)
|
||||
* do that and bail out instead.
|
||||
*/
|
||||
I = Load;
|
||||
State = Searching;
|
||||
State = Search;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -1627,7 +1640,7 @@ unsigned OptTransfers4 (CodeSeg* S)
|
||||
/* Restart after last xfer insn */
|
||||
I = Xfer;
|
||||
}
|
||||
State = Searching;
|
||||
State = Search;
|
||||
break;
|
||||
|
||||
}
|
||||
|
@ -54,9 +54,8 @@
|
||||
/* 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 */
|
||||
@ -68,6 +67,7 @@ typedef struct OptFuncDesc OptFuncDesc;
|
||||
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 */
|
||||
};
|
||||
|
||||
@ -644,7 +644,7 @@ static unsigned Opt_staxspidx (StackOpData* D)
|
||||
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);
|
||||
@ -952,14 +952,14 @@ static unsigned Opt_tosxorax (StackOpData* D)
|
||||
|
||||
|
||||
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]))
|
||||
|
||||
@ -1048,15 +1048,18 @@ static int PreCondOk (StackOpData* D)
|
||||
*/
|
||||
{
|
||||
/* 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 &&
|
||||
}
|
||||
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 &&
|
||||
}
|
||||
if ((D->OptFunc->Flags & STOP_X_ZERO) != 0 &&
|
||||
D->OpEntry->RI->In.RegX != 0) {
|
||||
/* Cannot optimize */
|
||||
return 0;
|
||||
@ -1237,6 +1240,23 @@ unsigned OptStackOps (CodeSeg* S)
|
||||
/* 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
|
||||
@ -1249,18 +1269,10 @@ unsigned OptStackOps (CodeSeg* S)
|
||||
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);
|
||||
|
Loading…
Reference in New Issue
Block a user