mirror of
https://github.com/cc65/cc65.git
synced 2024-11-19 06:31:31 +00:00
Rewrite of the main function.
git-svn-id: svn://svn.cc65.org/cc65/trunk@4032 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
parent
548dfd3060
commit
97c0e45c9f
@ -51,13 +51,44 @@
|
||||
|
||||
|
||||
|
||||
/* Structure that holds the needed data */
|
||||
/* 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_FLAGS;
|
||||
|
||||
/* Structure forward decl */
|
||||
typedef struct StackOpData StackOpData;
|
||||
|
||||
/* Structure that describes an optimizer subfunction for a specific op */
|
||||
typedef unsigned (*OptFunc) (StackOpData* D);
|
||||
typedef struct OptFuncDesc OptFuncDesc;
|
||||
struct OptFuncDesc {
|
||||
const char* Name; /* Name of the replaced runtime function */
|
||||
OptFunc Func; /* Function pointer */
|
||||
STOP_FLAGS Flags; /* Flags */
|
||||
};
|
||||
|
||||
/* Structure that holds the needed data */
|
||||
struct StackOpData {
|
||||
CodeSeg* Code; /* Pointer to code segment */
|
||||
unsigned Flags; /* Flags to remember things */
|
||||
unsigned PushIndex; /* Index of call to pushax in codeseg */
|
||||
unsigned OpIndex; /* Index of actual operation */
|
||||
|
||||
/* Pointer to optimizer subfunction description */
|
||||
const OptFuncDesc* OptFunc;
|
||||
|
||||
/* ZP register usage inside the sequence */
|
||||
unsigned UsedRegs;
|
||||
|
||||
/* Several indices if insns in the code segment */
|
||||
int LoadAIndex; /* Index of load insns, -1 = invalid */
|
||||
int LoadXIndex;
|
||||
int LoadYIndex;
|
||||
int PushIndex; /* Index of call to pushax in codeseg */
|
||||
int OpIndex; /* Index of actual operation */
|
||||
|
||||
CodeEntry* PrevEntry; /* Entry before the call to pushax */
|
||||
CodeEntry* PushEntry; /* Pointer to entry with call to pushax */
|
||||
CodeEntry* OpEntry; /* Pointer to entry with op */
|
||||
@ -79,21 +110,16 @@ struct StackOpData {
|
||||
|
||||
|
||||
|
||||
static unsigned AdjustStackOffset (CodeSeg* S, unsigned Start, unsigned Stop,
|
||||
unsigned Offs)
|
||||
/* Adjust the offset for all stack accesses in the range Start to Stop, both
|
||||
* inclusive. The function returns the number of instructions that have been
|
||||
* inserted.
|
||||
static void AdjustStackOffset (StackOpData* D, unsigned Offs)
|
||||
/* Adjust the offset for all stack accesses in the range PushIndex to OpIndex.
|
||||
* OpIndex is adjusted according to the insertions.
|
||||
*/
|
||||
{
|
||||
/* Number of inserted instructions */
|
||||
unsigned Inserted = 0;
|
||||
|
||||
/* Walk over all entries */
|
||||
unsigned I = Start;
|
||||
while (I <= Stop) {
|
||||
int I = D->PushIndex + 1;
|
||||
while (I < D->OpIndex) {
|
||||
|
||||
CodeEntry* E = CS_GetEntry (S, I);
|
||||
CodeEntry* E = CS_GetEntry (D->Code, I);
|
||||
|
||||
int NeedCorrection = 0;
|
||||
if ((E->Use & REG_SP) != 0) {
|
||||
@ -114,18 +140,10 @@ static unsigned AdjustStackOffset (CodeSeg* S, unsigned Start, unsigned Stop,
|
||||
|
||||
if (NeedCorrection) {
|
||||
|
||||
CodeEntry* P;
|
||||
|
||||
/* If the Y register value is needed later, we have to reload the
|
||||
* register after changing it.
|
||||
*/
|
||||
int NeedY = RegYUsed (S, I+1);
|
||||
unsigned YVal = E->RI->In.RegY;
|
||||
|
||||
/* Get the code entry before this one. If it's a LDY, adjust the
|
||||
* value.
|
||||
*/
|
||||
P = CS_GetPrevEntry (S, I);
|
||||
CodeEntry* P = CS_GetPrevEntry (D->Code, I);
|
||||
if (P && P->OPC == OP65_LDY && CE_IsConstImm (P)) {
|
||||
|
||||
/* The Y load is just before the stack access, adjust it */
|
||||
@ -134,30 +152,25 @@ static unsigned AdjustStackOffset (CodeSeg* S, unsigned Start, unsigned Stop,
|
||||
} else {
|
||||
|
||||
/* Insert a new load instruction before the stack access */
|
||||
const char* Arg = MakeHexArg (YVal - Offs);
|
||||
const char* Arg = MakeHexArg (E->RI->In.RegY - Offs);
|
||||
CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
|
||||
CS_InsertEntry (S, X, I);
|
||||
CS_InsertEntry (D->Code, X, I++);
|
||||
|
||||
/* One more inserted entries */
|
||||
++Inserted;
|
||||
++Stop;
|
||||
|
||||
/* Be sure to skip the stack access for the next round */
|
||||
++I;
|
||||
++D->OpIndex;
|
||||
|
||||
}
|
||||
|
||||
/* If we need the value of Y later, be sure to reload it */
|
||||
if (NeedY) {
|
||||
const char* Arg = MakeHexArg (YVal);
|
||||
if (RegYUsed (D->Code, I+1)) {
|
||||
const char* Arg = MakeHexArg (E->RI->In.RegY);
|
||||
CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
|
||||
CS_InsertEntry (S, X, I+1);
|
||||
CS_InsertEntry (D->Code, X, I+1);
|
||||
|
||||
/* One more inserted entries */
|
||||
++Inserted;
|
||||
++Stop;
|
||||
++D->OpIndex;
|
||||
|
||||
/* Skip this instruction int the next round */
|
||||
/* Skip this instruction in the next round */
|
||||
++I;
|
||||
}
|
||||
}
|
||||
@ -165,14 +178,11 @@ static unsigned AdjustStackOffset (CodeSeg* S, unsigned Start, unsigned Stop,
|
||||
/* Next entry */
|
||||
++I;
|
||||
}
|
||||
|
||||
/* Return the number of inserted entries */
|
||||
return Inserted;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void InsertEntry (StackOpData* D, CodeEntry* E, unsigned Index)
|
||||
static void InsertEntry (StackOpData* D, CodeEntry* E, int Index)
|
||||
/* Insert a new entry. Depending on Index, D->PushIndex and D->OpIndex will
|
||||
* be adjusted by this function.
|
||||
*/
|
||||
@ -191,7 +201,7 @@ static void InsertEntry (StackOpData* D, CodeEntry* E, unsigned Index)
|
||||
|
||||
|
||||
|
||||
static void DelEntry (StackOpData* D, unsigned Index)
|
||||
static void DelEntry (StackOpData* D, int Index)
|
||||
/* Delete an entry. Depending on Index, D->PushIndex and D->OpIndex will be
|
||||
* adjusted by this function, and PushEntry/OpEntry may get invalidated.
|
||||
*/
|
||||
@ -759,23 +769,6 @@ static unsigned Opt_tosxorax (StackOpData* D)
|
||||
|
||||
|
||||
|
||||
/* 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_FLAGS;
|
||||
|
||||
|
||||
typedef unsigned (*OptFunc) (StackOpData* D);
|
||||
typedef struct OptFuncDesc OptFuncDesc;
|
||||
struct OptFuncDesc {
|
||||
const char* Name; /* Name of the replaced runtime function */
|
||||
OptFunc Func; /* Function pointer */
|
||||
STOP_FLAGS Flags; /* Flags */
|
||||
};
|
||||
|
||||
static const OptFuncDesc FuncTable[] = {
|
||||
{ "__bzero", Opt___bzero, STOP_X_ZERO | STOP_A_KNOWN },
|
||||
{ "staspidx", Opt_staspidx, STOP_NONE },
|
||||
@ -844,6 +837,65 @@ static int HarmlessCall (const char* Name)
|
||||
|
||||
|
||||
|
||||
static void ResetStackOpData (StackOpData* Data)
|
||||
/* Reset the given data structure */
|
||||
{
|
||||
Data->Flags = 0;
|
||||
Data->OptFunc = 0;
|
||||
|
||||
Data->LoadAIndex = -1;
|
||||
Data->LoadXIndex = -1;
|
||||
Data->LoadYIndex = -1;
|
||||
Data->PushIndex = -1;
|
||||
Data->OpIndex = -1;
|
||||
|
||||
Data->UsedRegs = REG_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int PreCondOk (StackOpData* D)
|
||||
/* Check if the preconditions for a call to the optimizer subfunction are
|
||||
* satisfied. As a side effect, this function will also choose the zero page
|
||||
* register to use.
|
||||
*/
|
||||
{
|
||||
/* Check the flags */
|
||||
if ((D->OptFunc->Flags & STOP_A_UNUSED) != 0 &&
|
||||
RegAUsed (D->Code, D->OpIndex+1)) {
|
||||
/* Cannot optimize */
|
||||
return 0;
|
||||
} else 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) {
|
||||
/* Cannot optimize */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Determine the zero page locations to use */
|
||||
if ((D->UsedRegs & REG_SREG) == REG_NONE) {
|
||||
D->ZPLo = "sreg";
|
||||
D->ZPHi = "sreg+1";
|
||||
} else if ((D->UsedRegs & REG_PTR1) == REG_NONE) {
|
||||
D->ZPLo = "ptr1";
|
||||
D->ZPHi = "ptr1+1";
|
||||
} else if ((D->UsedRegs & REG_PTR2) == REG_NONE) {
|
||||
D->ZPLo = "ptr2";
|
||||
D->ZPHi = "ptr2+1";
|
||||
} else {
|
||||
/* No registers available */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Determine if we have a basic block */
|
||||
return CS_IsBasicBlock (D->Code, D->PushIndex, D->OpIndex);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Code */
|
||||
/*****************************************************************************/
|
||||
@ -854,15 +906,23 @@ unsigned OptStackOps (CodeSeg* S)
|
||||
/* Optimize operations that take operands via the stack */
|
||||
{
|
||||
unsigned Changes = 0; /* Number of changes in one run */
|
||||
int InSeq = 0; /* Inside a sequence */
|
||||
unsigned Push = 0; /* Index of pushax */
|
||||
unsigned UsedRegs = 0; /* Zeropage registers used in sequence */
|
||||
StackOpData Data;
|
||||
unsigned I;
|
||||
|
||||
enum {
|
||||
Searching,
|
||||
FoundPush,
|
||||
FoundOp
|
||||
} State = Searching;
|
||||
|
||||
|
||||
/* Generate register info */
|
||||
CS_GenRegInfo (S);
|
||||
|
||||
/* Clear Data */
|
||||
Data.Code = S;
|
||||
ResetStackOpData (&Data);
|
||||
|
||||
/* Look for a call to pushax followed by a call to some other function
|
||||
* that takes it's first argument on the stack, and the second argument
|
||||
* in the primary register.
|
||||
@ -884,116 +944,138 @@ unsigned OptStackOps (CodeSeg* S)
|
||||
/* Get the next entry */
|
||||
CodeEntry* E = CS_GetEntry (S, I);
|
||||
|
||||
/* Handling depends if we're inside a sequence or not */
|
||||
if (InSeq) {
|
||||
/* Actions depend on state */
|
||||
switch (State) {
|
||||
|
||||
/* If we are using the stack, and we don't have "indirect Y"
|
||||
* addressing mode, or the value of Y is unknown, or less than
|
||||
* two, we cannot cope with this piece of code. Having an unknown
|
||||
* value of Y means that we cannot correct the stack offset, while
|
||||
* having an offset less than two means that the code works with
|
||||
* the value on stack which is to be removed.
|
||||
case Searching:
|
||||
/* While searching, track register load insns, so we can tell
|
||||
* what is in a register once pushax is encountered.
|
||||
*/
|
||||
if ((E->Use & REG_SP) != 0 &&
|
||||
if (CE_IsCallTo (E, "pushax")) {
|
||||
Data.PushIndex = I;
|
||||
State = FoundPush;
|
||||
} else if (E->Info & OF_LOAD) {
|
||||
if (E->Chg & REG_A) {
|
||||
Data.LoadAIndex = I;
|
||||
}
|
||||
if (E->Chg & REG_X) {
|
||||
Data.LoadXIndex = I;
|
||||
}
|
||||
if (E->Chg & REG_Y) {
|
||||
Data.LoadYIndex = I;
|
||||
}
|
||||
} else if (E->Info & OF_XFR) {
|
||||
switch (E->OPC) {
|
||||
case OP65_TAX: Data.LoadXIndex = Data.LoadAIndex; break;
|
||||
case OP65_TAY: Data.LoadYIndex = Data.LoadAIndex; break;
|
||||
case OP65_TXA: Data.LoadAIndex = Data.LoadXIndex; break;
|
||||
case OP65_TYA: Data.LoadAIndex = Data.LoadYIndex; break;
|
||||
default: break;
|
||||
}
|
||||
} else {
|
||||
if (E->Chg & REG_A) {
|
||||
Data.LoadAIndex = -1;
|
||||
}
|
||||
if (E->Chg & REG_X) {
|
||||
Data.LoadXIndex = -1;
|
||||
}
|
||||
if (E->Chg & REG_Y) {
|
||||
Data.LoadYIndex = -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case FoundPush:
|
||||
/* We' found a pushax before. Search for a stack op that may
|
||||
* follow and in the meantime, track zeropage usage and check
|
||||
* for code that will disable us from translating the sequence.
|
||||
*/
|
||||
if (E->OPC == OP65_JSR) {
|
||||
|
||||
/* Subroutine call: Check if this is one of the functions,
|
||||
* we're going to replace.
|
||||
*/
|
||||
Data.OptFunc = FindFunc (E->Arg);
|
||||
if (Data.OptFunc) {
|
||||
/* Remember the op index and go on */
|
||||
Data.OpIndex = I;
|
||||
Data.OpEntry = E;
|
||||
State = FoundOp;
|
||||
break;
|
||||
} else if (HarmlessCall (E->Arg)) {
|
||||
/* Track zeropage register usage */
|
||||
Data.UsedRegs |= (E->Use | E->Chg);
|
||||
} else {
|
||||
/* A call to an unkown subroutine: We need to start
|
||||
* over after the last pushax. Note: This will also
|
||||
* happen if we encounter a call to pushax!
|
||||
*/
|
||||
I = Data.PushIndex;
|
||||
ResetStackOpData (&Data);
|
||||
State = Searching;
|
||||
break;
|
||||
}
|
||||
|
||||
} else if ((E->Use & REG_SP) != 0 &&
|
||||
(E->AM != AM65_ZP_INDY || RegValIsUnknown (E->RI->In.RegY) ||
|
||||
E->RI->In.RegY < 2)) {
|
||||
|
||||
/* All this stuff is not allowed in a sequence */
|
||||
InSeq = 0;
|
||||
|
||||
} else if (E->OPC == OP65_JSR) {
|
||||
|
||||
/* Subroutine call: Check if this is one of our functions */
|
||||
const OptFuncDesc* F = FindFunc (E->Arg);
|
||||
if (F) {
|
||||
|
||||
StackOpData Data;
|
||||
int PreCondOk = 1;
|
||||
|
||||
/* Check the flags */
|
||||
if ((F->Flags & STOP_A_UNUSED) != 0 && RegAUsed (S, I+1)) {
|
||||
/* Cannot optimize */
|
||||
PreCondOk = 0;
|
||||
} else if ((F->Flags & STOP_A_KNOWN) != 0 && RegValIsUnknown (E->RI->In.RegA)) {
|
||||
/* Cannot optimize */
|
||||
PreCondOk = 0;
|
||||
} else if ((F->Flags & STOP_X_ZERO) != 0 && E->RI->In.RegX != 0) {
|
||||
/* Cannot optimize */
|
||||
PreCondOk = 0;
|
||||
}
|
||||
|
||||
/* Determine the zero page locations to use */
|
||||
if (PreCondOk) {
|
||||
UsedRegs |= GetRegInfo (S, I+1, REG_SREG | REG_PTR1 | REG_PTR2);
|
||||
if ((UsedRegs & REG_SREG) == REG_NONE) {
|
||||
/* SREG is available */
|
||||
Data.ZPLo = "sreg";
|
||||
Data.ZPHi = "sreg+1";
|
||||
} else if ((UsedRegs & REG_PTR1) == REG_NONE) {
|
||||
Data.ZPLo = "ptr1";
|
||||
Data.ZPHi = "ptr1+1";
|
||||
} else if ((UsedRegs & REG_PTR2) == REG_NONE) {
|
||||
Data.ZPLo = "ptr2";
|
||||
Data.ZPHi = "ptr2+1";
|
||||
} else {
|
||||
/* No registers available */
|
||||
PreCondOk = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine if we have a basic block */
|
||||
if (PreCondOk) {
|
||||
PreCondOk = CS_IsBasicBlock (S, Push, I);
|
||||
}
|
||||
|
||||
/* If preconditions are ok, call the optimizer function */
|
||||
if (PreCondOk) {
|
||||
|
||||
/* Adjust stack offsets */
|
||||
Data.OpIndex = I + AdjustStackOffset (S, Push, I, 2);
|
||||
|
||||
/* Prepare the remainder of the data structure */
|
||||
Data.Code = S;
|
||||
Data.Flags = 0;
|
||||
Data.PushIndex = Push;
|
||||
Data.PrevEntry = CS_GetPrevEntry (S, Data.PushIndex);
|
||||
Data.PushEntry = CS_GetEntry (S, Data.PushIndex);
|
||||
Data.OpEntry = E;
|
||||
Data.NextEntry = CS_GetNextEntry (S, Data.OpIndex);
|
||||
|
||||
/* Call the optimizer function */
|
||||
Changes += F->Func (&Data);
|
||||
|
||||
/* Regenerate register info */
|
||||
CS_GenRegInfo (S);
|
||||
}
|
||||
|
||||
/* End of sequence */
|
||||
InSeq = 0;
|
||||
|
||||
} else if (strcmp (E->Arg, "pushax") == 0) {
|
||||
/* Restart the sequence */
|
||||
Push = I;
|
||||
UsedRegs = REG_NONE;
|
||||
} else if (HarmlessCall (E->Arg)) {
|
||||
/* Track zeropage register usage */
|
||||
UsedRegs |= (E->Use | E->Chg);
|
||||
} else {
|
||||
/* A call to an unkown subroutine ends the sequence */
|
||||
InSeq = 0;
|
||||
}
|
||||
/* If we are using the stack, and we don't have "indirect Y"
|
||||
* addressing mode, or the value of Y is unknown, or less
|
||||
* than two, we cannot cope with this piece of code. Having
|
||||
* an unknown value of Y means that we cannot correct the
|
||||
* stack offset, while having an offset less than two means
|
||||
* that the code works with the value on stack which is to
|
||||
* be removed.
|
||||
*/
|
||||
I = Data.PushIndex;
|
||||
ResetStackOpData (&Data);
|
||||
State = Searching;
|
||||
break;
|
||||
|
||||
} else {
|
||||
/* Other stuff: Track zeropage register usage */
|
||||
UsedRegs |= (E->Use | E->Chg);
|
||||
Data.UsedRegs |= (E->Use | E->Chg);
|
||||
}
|
||||
break;
|
||||
|
||||
case FoundOp:
|
||||
/* Track zero page location usage beyond this point */
|
||||
Data.UsedRegs |= GetRegInfo (S, I, REG_SREG | REG_PTR1 | REG_PTR2);
|
||||
|
||||
/* 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
|
||||
* pushax and here and will be tracked again when restarting.
|
||||
*/
|
||||
if (!PreCondOk (&Data)) {
|
||||
I = Data.PushIndex;
|
||||
ResetStackOpData (&Data);
|
||||
State = Searching;
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (CE_IsCallTo (E, "pushax")) {
|
||||
/* Preconditions are ok, so call the optimizer function */
|
||||
|
||||
/* This starts a sequence */
|
||||
Push = I;
|
||||
UsedRegs = REG_NONE;
|
||||
InSeq = 1;
|
||||
/* Adjust stack offsets to account for the upcoming removal */
|
||||
AdjustStackOffset (&Data, 2);
|
||||
|
||||
/* 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);
|
||||
Data.NextEntry = CS_GetNextEntry (S, Data.OpIndex);
|
||||
|
||||
/* Call the optimizer function */
|
||||
Changes += Data.OptFunc->Func (&Data);
|
||||
|
||||
/* Regenerate register info */
|
||||
CS_GenRegInfo (S);
|
||||
|
||||
/* Done */
|
||||
ResetStackOpData (&Data);
|
||||
State = Searching;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user