mirror of
https://github.com/cc65/cc65.git
synced 2024-12-28 22:30:12 +00:00
Working
git-svn-id: svn://svn.cc65.org/cc65/trunk@732 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
parent
68c0d38dfa
commit
df6d71d91e
@ -91,22 +91,22 @@ static const char CmpSignedTab [] = {
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Helper functions */
|
||||
/* Helper functions */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
static cmp_t FindCmpCond (const char* Suffix)
|
||||
/* Map a condition suffix to a code. Return the code or CMP_INV on failure */
|
||||
static cmp_t FindCmpCond (const char* Code, unsigned CodeLen)
|
||||
/* Search for a compare condition by the given code using the given length */
|
||||
{
|
||||
int I;
|
||||
unsigned I;
|
||||
|
||||
/* Linear search */
|
||||
for (I = 0; I < sizeof (CmpSuffixTab) / sizeof (CmpSuffixTab [0]); ++I) {
|
||||
if (strcmp (Suffix, CmpSuffixTab [I]) == 0) {
|
||||
if (strncmp (Code, CmpSuffixTab [I], CodeLen) == 0) {
|
||||
/* Found */
|
||||
return I;
|
||||
}
|
||||
return I;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not found */
|
||||
@ -115,6 +115,94 @@ static cmp_t FindCmpCond (const char* Suffix)
|
||||
|
||||
|
||||
|
||||
static cmp_t FindBoolCmpCond (const char* Name)
|
||||
/* Map a condition suffix to a code. Return the code or CMP_INV on failure */
|
||||
{
|
||||
/* Check for the correct subroutine name */
|
||||
if (strncmp (Name, "bool", 4) == 0) {
|
||||
/* Name is ok, search for the code in the table */
|
||||
return FindCmpCond (Name+4, strlen(Name)-4);
|
||||
} else {
|
||||
/* Not found */
|
||||
return CMP_INV;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int FindTosCmpCond (const char* Name)
|
||||
/* Check if this is a call to one of the TOS compare functions (tosgtax).
|
||||
* Return the condition code or CMP_INV on failure.
|
||||
*/
|
||||
{
|
||||
unsigned Len = strlen (Name);
|
||||
|
||||
/* Check for the correct subroutine name */
|
||||
if (strncmp (Name, "tos", 3) == 0 && strcmp (Name+Len-2, "ax") == 0) {
|
||||
/* Name is ok, search for the code in the table */
|
||||
return FindCmpCond (Name+3, Len-3-2);
|
||||
} else {
|
||||
/* Not found */
|
||||
return CMP_INV;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int IsUnsignedCmp (int Code)
|
||||
/* Check if this is an unsigned compare */
|
||||
{
|
||||
CHECK (Code >= 0);
|
||||
return CmpSignedTab [Code] == 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const char* MakeBoolCmp (cmp_t Cond)
|
||||
/* Create the name of a bool transformer subroutine for the given code. The
|
||||
* result is placed into a static buffer, so beware!
|
||||
*/
|
||||
{
|
||||
static char Buffer[20];
|
||||
CHECK (Cond != CMP_INV);
|
||||
sprintf (Buffer, "bool%s", CmpSuffixTab[Cond]);
|
||||
return Buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const char* MakeTosCmp (cmp_t Cond)
|
||||
/* Create the name of a tos compare subroutine for the given code. The
|
||||
* result is placed into a static buffer, so beware!
|
||||
*/
|
||||
{
|
||||
static char Buffer[20];
|
||||
CHECK (Cond != CMP_INV);
|
||||
sprintf (Buffer, "tos%sax", CmpSuffixTab[Cond]);
|
||||
return Buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int IsBitOp (const CodeEntry* E)
|
||||
/* Check if E is one of the bit operations (and, or, eor) */
|
||||
{
|
||||
return (E->OPC == OPC_AND || E->OPC == OPC_ORA || E->OPC == OPC_EOR);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int IsCmpToZero (const CodeEntry* E)
|
||||
/* Check if the given instrcuction is a compare to zero instruction */
|
||||
{
|
||||
return (E->OPC == OPC_CMP &&
|
||||
E->AM == AM_IMM &&
|
||||
(E->Flags & CEF_NUMARG) != 0 &&
|
||||
E->Num == 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Remove calls to the bool transformer subroutines */
|
||||
/*****************************************************************************/
|
||||
@ -140,10 +228,9 @@ static unsigned OptBoolTransforms (CodeSeg* S)
|
||||
|
||||
/* Check for a boolean transformer */
|
||||
if (E->OPC == OPC_JSR &&
|
||||
strncmp (E->Arg, "bool", 4) == 0 &&
|
||||
(Cond = FindBoolCmpCond (E->Arg)) != CMP_INV &&
|
||||
(N = GetNextCodeEntry (S, I)) != 0 &&
|
||||
(N->Info & OF_ZBRA) != 0 &&
|
||||
(Cond = FindCmpCond (E->Arg+4)) != CMP_INV) {
|
||||
(N->Info & OF_ZBRA) != 0) {
|
||||
|
||||
CodeEntry* X;
|
||||
CodeLabel* L;
|
||||
@ -266,7 +353,204 @@ NextEntry:
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* nega optimizations */
|
||||
/* Optimizations for compares */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
static unsigned OptCmp1 (CodeSeg* S)
|
||||
/* Search for the sequence
|
||||
*
|
||||
* stx xx
|
||||
* stx tmp1
|
||||
* ora tmp1
|
||||
*
|
||||
* and replace it by
|
||||
*
|
||||
* stx xx
|
||||
* ora xx
|
||||
*/
|
||||
{
|
||||
unsigned Changes = 0;
|
||||
|
||||
/* Walk over the entries */
|
||||
unsigned I = 0;
|
||||
while (I < GetCodeEntryCount (S)) {
|
||||
|
||||
CodeEntry* L[2];
|
||||
|
||||
/* Get next entry */
|
||||
CodeEntry* E = GetCodeEntry (S, I);
|
||||
|
||||
/* Check for the sequence */
|
||||
if (E->OPC == OPC_STX &&
|
||||
GetCodeEntries (S, L, I+1, 2) &&
|
||||
L[0]->OPC == OPC_STX &&
|
||||
strcmp (L[0]->Arg, "tmp1") == 0 &&
|
||||
!CodeEntryHasLabel (L[0]) &&
|
||||
L[1]->OPC == OPC_ORA &&
|
||||
strcmp (L[1]->Arg, "tmp1") == 0 &&
|
||||
!CodeEntryHasLabel (L[1])) {
|
||||
|
||||
/* Remove the remaining instructions */
|
||||
DelCodeEntries (S, I+1, 2);
|
||||
|
||||
/* Insert the ora instead */
|
||||
InsertCodeEntry (S, NewCodeEntry (OPC_ORA, E->AM, E->Arg, 0), I+1);
|
||||
|
||||
/* Remember, we had changes */
|
||||
++Changes;
|
||||
|
||||
}
|
||||
|
||||
/* Next entry */
|
||||
++I;
|
||||
|
||||
}
|
||||
|
||||
/* Return the number of changes made */
|
||||
return Changes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static unsigned OptCmp2 (CodeSeg* S)
|
||||
/* Search for
|
||||
*
|
||||
* lda/and/ora/eor ...
|
||||
* cmp #$00
|
||||
* jeq/jne
|
||||
*
|
||||
* and remove the cmp.
|
||||
*/
|
||||
{
|
||||
unsigned Changes = 0;
|
||||
|
||||
/* Walk over the entries */
|
||||
unsigned I = 0;
|
||||
while (I < GetCodeEntryCount (S)) {
|
||||
|
||||
CodeEntry* L[2];
|
||||
|
||||
/* Get next entry */
|
||||
CodeEntry* E = GetCodeEntry (S, I);
|
||||
|
||||
/* Check for the sequence */
|
||||
if ((E->OPC == OPC_LDA || IsBitOp (E)) &&
|
||||
GetCodeEntries (S, L, I+1, 2) &&
|
||||
IsCmpToZero (L[0]) &&
|
||||
!CodeEntryHasLabel (L[0]) &&
|
||||
(L[1]->Info & OF_FBRA) != 0 &&
|
||||
!CodeEntryHasLabel (L[1])) {
|
||||
|
||||
/* Remove the compare */
|
||||
DelCodeEntry (S, I+1);
|
||||
|
||||
/* Remember, we had changes */
|
||||
++Changes;
|
||||
|
||||
}
|
||||
|
||||
/* Next entry */
|
||||
++I;
|
||||
|
||||
}
|
||||
|
||||
/* Return the number of changes made */
|
||||
return Changes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static unsigned OptCmp3 (CodeSeg* S)
|
||||
/* Search for
|
||||
*
|
||||
* lda x
|
||||
* ldx y
|
||||
* cpx #a
|
||||
* bne L1
|
||||
* cmp #b
|
||||
* jne/jeq L2
|
||||
*
|
||||
* If a is zero, we may remove the compare. If a and b are both zero, we may
|
||||
* replace it by the sequence
|
||||
*
|
||||
* lda x
|
||||
* ora x+1
|
||||
* jne/jeq ...
|
||||
*
|
||||
* L1 may be either the label at the branch instruction, or the target label
|
||||
* of this instruction.
|
||||
*/
|
||||
{
|
||||
unsigned Changes = 0;
|
||||
|
||||
/* Walk over the entries */
|
||||
unsigned I = 0;
|
||||
while (I < GetCodeEntryCount (S)) {
|
||||
|
||||
CodeEntry* L[5];
|
||||
|
||||
/* Get next entry */
|
||||
CodeEntry* E = GetCodeEntry (S, I);
|
||||
|
||||
/* Check for the sequence */
|
||||
if (E->OPC == OPC_LDA &&
|
||||
GetCodeEntries (S, L, I+1, 5) &&
|
||||
L[0]->OPC == OPC_LDX &&
|
||||
!CodeEntryHasLabel (L[0]) &&
|
||||
L[1]->OPC == OPC_CPX &&
|
||||
L[1]->AM == AM_IMM &&
|
||||
(L[1]->Flags & CEF_NUMARG) != 0 &&
|
||||
!CodeEntryHasLabel (L[1]) &&
|
||||
(L[2]->OPC == OPC_JNE || L[2]->OPC == OPC_BNE) &&
|
||||
L[2]->JumpTo != 0 &&
|
||||
!CodeEntryHasLabel (L[2]) &&
|
||||
L[3]->OPC == OPC_CMP &&
|
||||
L[3]->AM == AM_IMM &&
|
||||
(L[3]->Flags & CEF_NUMARG) != 0 &&
|
||||
(L[4]->Info & OF_ZBRA) != 0 &&
|
||||
L[4]->JumpTo != 0 &&
|
||||
(L[2]->JumpTo->Owner == L[4] || L[2]->JumpTo == L[4]->JumpTo)) {
|
||||
|
||||
/* Get the compare value */
|
||||
unsigned Val = ((L[1]->Num & 0xFF) << 8) | (L[3]->Num & 0xFF);
|
||||
|
||||
if (Val == 0) {
|
||||
/* The value is zero, we may use the simple code version. */
|
||||
ReplaceOPC (L[0], OPC_ORA);
|
||||
DelCodeEntries (S, I+2, 3);
|
||||
} else {
|
||||
/* Move the lda instruction after the first branch */
|
||||
CodeEntry* N = RetrieveCodeEntry (S, I);
|
||||
InsertCodeEntry (S, N, I+3);
|
||||
|
||||
/* Replace the ldx/cpx by lda/cmp */
|
||||
ReplaceOPC (L[0], OPC_LDA);
|
||||
ReplaceOPC (L[1], OPC_CMP);
|
||||
|
||||
/* The high byte is zero, remove the CMP */
|
||||
if ((Val & 0xFF00) == 0) {
|
||||
DelCodeEntry (S, I+1);
|
||||
}
|
||||
}
|
||||
|
||||
++Changes;
|
||||
}
|
||||
|
||||
/* Next entry */
|
||||
++I;
|
||||
|
||||
}
|
||||
|
||||
/* Return the number of changes made */
|
||||
return Changes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* nega optimizations */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
@ -625,6 +909,12 @@ static OptFunc OptFuncs [] = {
|
||||
{ OptNegAX2, "OptNegAX2", 0 },
|
||||
/* Optimize calls to negax */
|
||||
{ OptNegAX3, "OptNegAX3", 0 },
|
||||
/* Optimize compares */
|
||||
{ OptCmp1, "OptCmp1", 0 },
|
||||
/* Optimize compares */
|
||||
{ OptCmp2, "OptCmp2", 0 },
|
||||
/* Optimize compares */
|
||||
{ OptCmp3, "OptCmp3", 0 },
|
||||
/* Remove unused loads */
|
||||
{ OptUnusedLoads, "OptUnusedLoads", 0 },
|
||||
/* Optimize branch distance */
|
||||
|
@ -542,6 +542,20 @@ void DelCodeEntries (CodeSeg* S, unsigned Start, unsigned Count)
|
||||
|
||||
|
||||
|
||||
struct CodeEntry* RetrieveCodeEntry (CodeSeg* S, unsigned Index)
|
||||
/* Retrieve a code entry. This means, the code entry is removed from the
|
||||
* entry collection, but not deleted and returned instead. The entry may
|
||||
* then be inserted again at another position.
|
||||
*/
|
||||
{
|
||||
/* Get the code entry, remove it from the collection and return it */
|
||||
CodeEntry* E = GetCodeEntry (S, Index);
|
||||
CollDelete (&S->Entries, Index);
|
||||
return E;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct CodeEntry* GetNextCodeEntry (CodeSeg* S, unsigned Index)
|
||||
/* Get the code entry following the one with the index Index. If there is no
|
||||
* following code entry, return NULL.
|
||||
|
@ -112,6 +112,12 @@ void DelCodeEntries (CodeSeg* S, unsigned Start, unsigned Count);
|
||||
* labels attached to the entries and so on.
|
||||
*/
|
||||
|
||||
struct CodeEntry* RetrieveCodeEntry (CodeSeg* S, unsigned Index);
|
||||
/* Retrieve a code entry. This means, the code entry is removed from the
|
||||
* entry collection, but not deleted and returned instead. The entry may
|
||||
* then be inserted again at another position.
|
||||
*/
|
||||
|
||||
#if defined(HAVE_INLINE)
|
||||
INLINE struct CodeEntry* GetCodeEntry (CodeSeg* S, unsigned Index)
|
||||
/* Get an entry from the given code segment */
|
||||
|
@ -61,11 +61,11 @@ const OPCDesc OPCTable[OPC_COUNT] = {
|
||||
{ OPC_ASL, "asl", 0, REG_A, REG_A, OF_NONE },
|
||||
{ OPC_BCC, "bcc", 2, REG_NONE, REG_NONE, OF_CBRA },
|
||||
{ OPC_BCS, "bcs", 2, REG_NONE, REG_NONE, OF_CBRA },
|
||||
{ OPC_BEQ, "beq", 2, REG_NONE, REG_NONE, OF_CBRA | OF_ZBRA },
|
||||
{ OPC_BEQ, "beq", 2, REG_NONE, REG_NONE, OF_CBRA | OF_ZBRA | OF_FBRA },
|
||||
{ OPC_BIT, "bit", 0, REG_A, REG_NONE, OF_NONE },
|
||||
{ OPC_BMI, "bmi", 2, REG_NONE, REG_NONE, OF_CBRA },
|
||||
{ OPC_BNE, "bne", 2, REG_NONE, REG_NONE, OF_CBRA | OF_ZBRA },
|
||||
{ OPC_BPL, "bpl", 2, REG_NONE, REG_NONE, OF_CBRA },
|
||||
{ OPC_BMI, "bmi", 2, REG_NONE, REG_NONE, OF_CBRA | OF_FBRA },
|
||||
{ OPC_BNE, "bne", 2, REG_NONE, REG_NONE, OF_CBRA | OF_ZBRA | OF_FBRA },
|
||||
{ OPC_BPL, "bpl", 2, REG_NONE, REG_NONE, OF_CBRA | OF_FBRA },
|
||||
{ OPC_BRA, "bra", 2, REG_NONE, REG_NONE, OF_UBRA },
|
||||
{ OPC_BRK, "brk", 1, REG_NONE, REG_NONE, OF_NONE },
|
||||
{ OPC_BVC, "bvc", 2, REG_NONE, REG_NONE, OF_CBRA },
|
||||
@ -88,11 +88,11 @@ const OPCDesc OPCTable[OPC_COUNT] = {
|
||||
{ OPC_INY, "iny", 1, REG_Y, REG_Y, OF_NONE },
|
||||
{ OPC_JCC, "jcc", 5, REG_NONE, REG_NONE, OF_CBRA | OF_LBRA },
|
||||
{ OPC_JCS, "jcs", 5, REG_NONE, REG_NONE, OF_CBRA | OF_LBRA },
|
||||
{ OPC_JEQ, "jeq", 5, REG_NONE, REG_NONE, OF_CBRA | OF_LBRA | OF_ZBRA },
|
||||
{ OPC_JMI, "jmi", 5, REG_NONE, REG_NONE, OF_CBRA | OF_LBRA },
|
||||
{ OPC_JEQ, "jeq", 5, REG_NONE, REG_NONE, OF_CBRA | OF_LBRA | OF_ZBRA | OF_FBRA },
|
||||
{ OPC_JMI, "jmi", 5, REG_NONE, REG_NONE, OF_CBRA | OF_LBRA | OF_FBRA },
|
||||
{ OPC_JMP, "jmp", 3, REG_NONE, REG_NONE, OF_UBRA | OF_LBRA },
|
||||
{ OPC_JNE, "jne", 5, REG_NONE, REG_NONE, OF_CBRA | OF_LBRA | OF_ZBRA },
|
||||
{ OPC_JPL, "jpl", 5, REG_NONE, REG_NONE, OF_CBRA | OF_LBRA },
|
||||
{ OPC_JNE, "jne", 5, REG_NONE, REG_NONE, OF_CBRA | OF_LBRA | OF_ZBRA | OF_FBRA },
|
||||
{ OPC_JPL, "jpl", 5, REG_NONE, REG_NONE, OF_CBRA | OF_LBRA | OF_FBRA },
|
||||
{ OPC_JSR, "jsr", 3, REG_NONE, REG_NONE, OF_NONE },
|
||||
{ OPC_JVC, "jvc", 5, REG_NONE, REG_NONE, OF_CBRA | OF_LBRA },
|
||||
{ OPC_JVS, "jvs", 5, REG_NONE, REG_NONE, OF_CBRA | OF_LBRA },
|
||||
|
@ -158,12 +158,13 @@ typedef enum {
|
||||
|
||||
/* Opcode info */
|
||||
#define OF_NONE 0x0000U /* No additional information */
|
||||
#define OF_UBRA 0x0001U /* Unconditional branch */
|
||||
#define OF_CBRA 0x0002U /* Conditional branch */
|
||||
#define OF_ZBRA 0x0004U /* Branch on zero flag condition */
|
||||
#define OF_LBRA 0x0008U /* Jump/branch is long */
|
||||
#define OF_RET 0x0010U /* Return from function */
|
||||
#define OF_LOAD 0x0020U /* Register load */
|
||||
#define OF_UBRA 0x0001U /* Unconditional branch */
|
||||
#define OF_CBRA 0x0002U /* Conditional branch */
|
||||
#define OF_ZBRA 0x0004U /* Branch on zero flag condition */
|
||||
#define OF_FBRA 0x0008U /* Branch on cond set by a load */
|
||||
#define OF_LBRA 0x0010U /* Jump/branch is long */
|
||||
#define OF_RET 0x0020U /* Return from function */
|
||||
#define OF_LOAD 0x0040U /* Register load */
|
||||
#define OF_BRA (OF_UBRA|OF_CBRA) /* Operation is a jump/branch */
|
||||
#define OF_DEAD (OF_UBRA|OF_RET) /* Dead end - no exec behind this point */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user