1
0
mirror of https://github.com/cc65/cc65.git synced 2024-06-09 22:29:35 +00:00

Be more flexible when detecting a special sequence that can be optimized.

git-svn-id: svn://svn.cc65.org/cc65/trunk@5718 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
uz 2012-06-15 20:51:14 +00:00
parent b014bd8196
commit 7c9b27c048
5 changed files with 251 additions and 218 deletions

View File

@ -397,7 +397,7 @@ void CE_SetNumArg (CodeEntry* E, long Num)
int CE_IsConstImm (const CodeEntry* E)
/* Return true if the argument of E is a constant immediate value */
{
return (E->AM == AM65_IMM && (E->Flags & CEF_NUMARG) != 0);
return (E->AM == AM65_IMM && CE_HasNumArg (E));
}
@ -407,9 +407,7 @@ int CE_IsKnownImm (const CodeEntry* E, unsigned long Num)
* equal to Num.
*/
{
return E->AM == AM65_IMM &&
(E->Flags & CEF_NUMARG) != 0 &&
E->Num == Num;
return (E->AM == AM65_IMM && CE_HasNumArg (E) && E->Num == Num);
}
@ -1388,7 +1386,7 @@ void CE_Output (const CodeEntry* E)
/* Print the mnemonic */
Chars = WriteOutput ("\t%s", D->Mnemo);
/* Space to leave before the operand */
Space = 9 - Chars;
@ -1453,7 +1451,7 @@ void CE_Output (const CodeEntry* E)
}
/* Print usage info if requested by the debugging flag */
if (Debug) {
if (Debug) {
char Use [128];
char Chg [128];
WriteOutput ("%*s; USE: %-12s CHG: %-12s SIZE: %u",

View File

@ -183,6 +183,16 @@ INLINE void CE_ResetMark (CodeEntry* E)
# define CE_ResetMark(E) ((E)->Flags &= ~CEF_USERMARK)
#endif
#if defined(HAVE_INLINE)
INLINE int CE_HasNumArg (const CodeEntry* E)
/* Return true if the instruction has a numeric argument */
{
return (E->Flags & CEF_NUMARG) != 0;
}
#else
# define CE_HasNumArg(E) (((E)->Flags & CEF_NUMARG) != 0)
#endif
void CE_SetArg (CodeEntry* E, const char* Arg);
/* Replace the argument by the new one. */

View File

@ -1200,12 +1200,11 @@ static OptFunc DOptPtrLoad14 = { OptPtrLoad14, "OptPtrLoad14", 108, 0,
static OptFunc DOptPtrLoad15 = { OptPtrLoad15, "OptPtrLoad15", 86, 0, 0, 0, 0, 0 };
static OptFunc DOptPtrLoad16 = { OptPtrLoad16, "OptPtrLoad16", 100, 0, 0, 0, 0, 0 };
static OptFunc DOptPtrLoad17 = { OptPtrLoad17, "OptPtrLoad17", 190, 0, 0, 0, 0, 0 };
static OptFunc DOptPtrStore1 = { OptPtrStore1, "OptPtrStore1", 40, 0, 0, 0, 0, 0 };
static OptFunc DOptPtrStore1 = { OptPtrStore1, "OptPtrStore1", 65, 0, 0, 0, 0, 0 };
static OptFunc DOptPtrStore2 = { OptPtrStore2, "OptPtrStore2", 50, 0, 0, 0, 0, 0 };
static OptFunc DOptPtrStore3 = { OptPtrStore3, "OptPtrStore3", 50, 0, 0, 0, 0, 0 };
static OptFunc DOptPtrStore4 = { OptPtrStore4, "OptPtrStore4", 65, 0, 0, 0, 0, 0 };
static OptFunc DOptPtrStore5 = { OptPtrStore5, "OptPtrStore5", 65, 0, 0, 0, 0, 0 };
static OptFunc DOptPtrStore6 = { OptPtrStore6, "OptPtrStore6", 100, 0, 0, 0, 0, 0 };
static OptFunc DOptPtrStore5 = { OptPtrStore5, "OptPtrStore5", 100, 0, 0, 0, 0, 0 };
static OptFunc DOptPush1 = { OptPush1, "OptPush1", 65, 0, 0, 0, 0, 0 };
static OptFunc DOptPush2 = { OptPush2, "OptPush2", 50, 0, 0, 0, 0, 0 };
static OptFunc DOptPushPop = { OptPushPop, "OptPushPop", 0, 0, 0, 0, 0, 0 };
@ -1300,7 +1299,6 @@ static OptFunc* OptFuncs[] = {
&DOptPtrStore3,
&DOptPtrStore4,
&DOptPtrStore5,
&DOptPtrStore6,
&DOptPush1,
&DOptPush2,
&DOptPushPop,
@ -1576,7 +1574,6 @@ static unsigned RunOptGroup1 (CodeSeg* S)
Changes += RunOptFunc (S, &DOptPtrStore3, 1);
Changes += RunOptFunc (S, &DOptPtrStore4, 1);
Changes += RunOptFunc (S, &DOptPtrStore5, 1);
Changes += RunOptFunc (S, &DOptPtrStore6, 1);
Changes += RunOptFunc (S, &DOptAdd3, 1); /* Before OptPtrLoad5! */
Changes += RunOptFunc (S, &DOptPtrLoad1, 1);
Changes += RunOptFunc (S, &DOptPtrLoad2, 1);

View File

@ -36,7 +36,9 @@
#include <string.h>
/* common */
#include "strbuf.h"
#include "xmalloc.h"
#include "xsprintf.h"
/* cc65 */
#include "codeent.h"
@ -89,7 +91,7 @@ static unsigned OptPtrStore1Sub (CodeSeg* S, unsigned I, CodeEntry** const L)
static const char* ZPLoadAX (CodeSeg* S, unsigned I)
static const char* LoadAXZP (CodeSeg* S, unsigned I)
/* If the two instructions at S/I are a load of A/X from a two byte zero byte
* location, return the name of the zero page location. Otherwise return NULL.
*/
@ -121,8 +123,74 @@ static const char* ZPLoadAX (CodeSeg* S, unsigned I)
static const char* LoadAXImm (CodeSeg* S, unsigned I)
/* If the two instructions at S/I are a load of A/X of a constant value or a
* wqord sized address label, return the address of the location as a string.
* Beware: In case of a numeric value, the result is returned in static
* storage which is overwritten with each call.
*/
{
static StrBuf Buf = STATIC_STRBUF_INITIALIZER;
CodeEntry* L[2];
unsigned Len;
if (CS_GetEntries (S, L, I, 2) &&
((L[0]->OPC == OP65_LDA && L[1]->OPC == OP65_LDX) ||
(L[0]->OPC == OP65_LDX && L[1]->OPC == OP65_LDA)) &&
L[0]->AM == AM65_IMM &&
L[1]->AM == AM65_IMM &&
!CE_HasLabel (L[1])) {
/* Immediate load of A/X */
if (CE_HasNumArg (L[0]) && CE_HasNumArg (L[1])) {
/* Numeric argument - get low and high byte */
unsigned Hi, Lo;
if (L[0]->OPC == OP65_LDA) {
Lo = (L[0]->Num & 0xFF);
Hi = (L[1]->Num & 0xFF);
} else {
Lo = (L[1]->Num & 0xFF);
Hi = (L[0]->Num & 0xFF);
}
/* Format into buffer */
SB_Printf (&Buf, "$%04X", Lo | (Hi << 8));
/* Return the address as a string */
return SB_GetConstBuf (&Buf);
} else if ((Len = strlen (L[0]->Arg)) > 3 &&
L[0]->Arg[0] == '<' &&
L[0]->Arg[1] == '(' &&
strlen (L[1]->Arg) == Len &&
L[1]->Arg[0] == '>' &&
memcmp (L[0]->Arg+1, L[1]->Arg+1, Len-1) == 0) {
/* Load of an address label */
SB_CopyBuf (&Buf, L[0]->Arg + 2, Len - 3);
SB_Terminate (&Buf);
return SB_GetConstBuf (&Buf);
} else {
/* Not found */
return 0;
}
} else {
/* Not found */
return 0;
}
}
/*****************************************************************************/
/* Code */
/* Code */
/*****************************************************************************/
@ -130,8 +198,6 @@ static const char* ZPLoadAX (CodeSeg* S, unsigned I)
unsigned OptPtrStore1 (CodeSeg* S)
/* Search for the sequence:
*
* lda #<(label+0)
* ldx #>(label+0)
* clc
* adc xxx
* bcc L
@ -144,10 +210,35 @@ unsigned OptPtrStore1 (CodeSeg* S)
*
* and replace it by:
*
* sta ptr1
* stx ptr1+1
* ldy xxx
* ldx #$00
* lda yyy
* sta label,y
* ldx #$00
* lda yyy
* sta (ptr1),y
*
* or by
*
* ldy xxx
* ldx #$00
* lda yyy
* sta label,y
*
* or by
*
* ldy xxx
* ldx #$00
* lda yyy
* sta $xxxx,y
*
* or by
*
* ldy xxx
* ldx #$00
* lda yyy
* sta (zp),y
*
* depending on the two instructions preceeding the sequence above.
*/
{
unsigned Changes = 0;
@ -156,64 +247,95 @@ unsigned OptPtrStore1 (CodeSeg* S)
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
CodeEntry* L[11];
unsigned Len;
CodeEntry* L[9];
/* Get next entry */
L[0] = CS_GetEntry (S, I);
/* Check for the sequence */
if (L[0]->OPC == OP65_LDA &&
L[0]->AM == AM65_IMM &&
CS_GetEntries (S, L+1, I+1, 10) &&
L[1]->OPC == OP65_LDX &&
L[1]->AM == AM65_IMM &&
L[2]->OPC == OP65_CLC &&
L[3]->OPC == OP65_ADC &&
(L[3]->AM == AM65_ABS || L[3]->AM == AM65_ZP) &&
(L[4]->OPC == OP65_BCC || L[4]->OPC == OP65_JCC) &&
L[4]->JumpTo != 0 &&
L[4]->JumpTo->Owner == L[6] &&
L[5]->OPC == OP65_INX &&
CE_IsCallTo (L[6], "pushax") &&
L[7]->OPC == OP65_LDX &&
L[8]->OPC == OP65_LDA &&
L[9]->OPC == OP65_LDY &&
CE_IsKnownImm (L[9], 0) &&
CE_IsCallTo (L[10], "staspidx") &&
!CS_RangeHasLabel (S, I+1, 5) &&
!CS_RangeHasLabel (S, I+7, 4) &&
/* Check the label last because this is quite costly */
(Len = strlen (L[0]->Arg)) > 3 &&
L[0]->Arg[0] == '<' &&
L[0]->Arg[1] == '(' &&
strlen (L[1]->Arg) == Len &&
L[1]->Arg[0] == '>' &&
memcmp (L[0]->Arg+1, L[1]->Arg+1, Len-1) == 0) {
if (L[0]->OPC == OP65_CLC &&
CS_GetEntries (S, L+1, I+1, 8) &&
L[1]->OPC == OP65_ADC &&
(L[1]->AM == AM65_ABS ||
L[1]->AM == AM65_ZP ||
L[1]->AM == AM65_IMM) &&
(L[2]->OPC == OP65_BCC || L[2]->OPC == OP65_JCC) &&
L[2]->JumpTo != 0 &&
L[2]->JumpTo->Owner == L[4] &&
L[3]->OPC == OP65_INX &&
CE_IsCallTo (L[4], "pushax") &&
L[5]->OPC == OP65_LDX &&
L[6]->OPC == OP65_LDA &&
L[7]->OPC == OP65_LDY &&
CE_IsKnownImm (L[7], 0) &&
CE_IsCallTo (L[8], "staspidx") &&
!CS_RangeHasLabel (S, I+1, 3) &&
!CS_RangeHasLabel (S, I+5, 4)) {
CodeEntry* X;
char* Label;
const char* Loc;
am_t AM;
/* We will create all the new stuff behind the current one so
* we keep the line references.
*/
X = NewCodeEntry (OP65_LDY, L[3]->AM, L[3]->Arg, 0, L[0]->LI);
CS_InsertEntry (S, X, I+11);
/* Track the insertion point */
unsigned IP = I + 9;
X = NewCodeEntry (OP65_LDX, L[7]->AM, L[7]->Arg, 0, L[7]->LI);
CS_InsertEntry (S, X, I+12);
unsigned DeleteStart = I;
unsigned DeleteCount = 9;
if (I >= 2) {
if ((Loc = LoadAXZP (S, I-2)) != 0) {
/* If the sequence is preceeded by a load of a ZP value,
* we can use this ZP value as a pointer using ZP
* indirect Y addressing.
*/
AM = AM65_ZP_INDY;
DeleteStart -= 2;
DeleteCount += 2;
} else if ((Loc = LoadAXImm (S, I-2)) != 0) {
/* If the sequence is preceeded by a load of an immediate
* value, we can use this absolute value as an address
* using absolute indexed Y addressing.
*/
AM = AM65_ABSY;
DeleteStart -= 2;
DeleteCount += 2;
}
}
X = NewCodeEntry (OP65_LDA, L[8]->AM, L[8]->Arg, 0, L[8]->LI);
CS_InsertEntry (S, X, I+13);
/* If we don't have a zero page location, we use ptr1 with zp
* indirect Y addressing. We must store the value in A/X into
* ptr1 in this case.
*/
if (Loc == 0) {
Label = memcpy (xmalloc (Len-2), L[0]->Arg+2, Len-3);
Label[Len-3] = '\0';
X = NewCodeEntry (OP65_STA, AM65_ABSY, Label, 0, L[10]->LI);
CS_InsertEntry (S, X, I+14);
xfree (Label);
/* Must use ptr1 */
Loc = "ptr1";
AM = AM65_ZP_INDY;
X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[8]->LI);
CS_InsertEntry (S, X, IP++);
X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[8]->LI);
CS_InsertEntry (S, X, IP++);
}
X = NewCodeEntry (OP65_LDY, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
CS_InsertEntry (S, X, IP++);
X = NewCodeEntry (OP65_LDX, L[5]->AM, L[5]->Arg, 0, L[5]->LI);
CS_InsertEntry (S, X, IP++);
X = NewCodeEntry (OP65_LDA, L[6]->AM, L[6]->Arg, 0, L[6]->LI);
CS_InsertEntry (S, X, IP++);
X = NewCodeEntry (OP65_STA, AM, Loc, 0, L[8]->LI);
CS_InsertEntry (S, X, IP++);
/* Remove the old code */
CS_DelEntries (S, I, 11);
CS_DelEntries (S, DeleteStart, DeleteCount);
/* Skip most of the generated replacement */
I += 3;
/* Remember, we had changes */
++Changes;
@ -221,7 +343,7 @@ unsigned OptPtrStore1 (CodeSeg* S)
}
/* Next entry */
++I;
++I;
}
@ -471,116 +593,6 @@ unsigned OptPtrStore3 (CodeSeg* S)
unsigned OptPtrStore4 (CodeSeg* S)
/* Search for the sequence:
*
* clc
* adc xxx
* bcc L
* inx
* L: jsr pushax
* ldx #$00
* lda yyy
* ldy #$00
* jsr staspidx
*
* and replace it by:
*
* sta ptr1
* stx ptr1+1
* ldy xxx
* ldx #$00
* lda yyy
* sta (ptr1),y
*
* In case a/x is loaded from the register bank before the clc, we can even
* use the register bank instead of ptr1.
*/
{
unsigned Changes = 0;
/* Walk over the entries */
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
CodeEntry* L[9];
/* Get next entry */
L[0] = CS_GetEntry (S, I);
/* Check for the sequence */
if (L[0]->OPC == OP65_CLC &&
CS_GetEntries (S, L+1, I+1, 8) &&
L[1]->OPC == OP65_ADC &&
(L[1]->AM == AM65_ABS ||
L[1]->AM == AM65_ZP ||
L[1]->AM == AM65_IMM) &&
(L[2]->OPC == OP65_BCC || L[2]->OPC == OP65_JCC) &&
L[2]->JumpTo != 0 &&
L[2]->JumpTo->Owner == L[4] &&
L[3]->OPC == OP65_INX &&
CE_IsCallTo (L[4], "pushax") &&
L[5]->OPC == OP65_LDX &&
L[6]->OPC == OP65_LDA &&
L[7]->OPC == OP65_LDY &&
CE_IsKnownImm (L[7], 0) &&
CE_IsCallTo (L[8], "staspidx") &&
!CS_RangeHasLabel (S, I+1, 3) &&
!CS_RangeHasLabel (S, I+5, 4)) {
CodeEntry* X;
const char* ZPLoc;
/* Track the insertion point */
unsigned IP = I + 9;
/* If the sequence is preceeded by a load of a ZP value, we can
* use this ZP value as a pointer.
*/
if (I < 2 || (ZPLoc = ZPLoadAX (S, I-2)) == 0) {
ZPLoc = "ptr1";
/* Must use ptr1 */
X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[8]->LI);
CS_InsertEntry (S, X, IP++);
X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[8]->LI);
CS_InsertEntry (S, X, IP++);
}
X = NewCodeEntry (OP65_LDY, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
CS_InsertEntry (S, X, IP++);
X = NewCodeEntry (OP65_LDX, L[5]->AM, L[5]->Arg, 0, L[5]->LI);
CS_InsertEntry (S, X, IP++);
X = NewCodeEntry (OP65_LDA, L[6]->AM, L[6]->Arg, 0, L[6]->LI);
CS_InsertEntry (S, X, IP++);
X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLoc, 0, L[8]->LI);
CS_InsertEntry (S, X, IP++);
/* Remove the old code */
CS_DelEntries (S, I, 9);
/* Remember, we had changes */
++Changes;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
unsigned OptPtrStore5 (CodeSeg* S)
/* Search for the sequence:
*
* clc
@ -647,23 +659,42 @@ unsigned OptPtrStore5 (CodeSeg* S)
CodeEntry* X;
const char* Arg;
const char* ZPLoc;
const char* Loc;
am_t AM;
/* Track the insertion point */
unsigned IP = I + 10;
/* If the sequence is preceeded by a load of a ZP value, we can
* use this ZP value as a pointer.
*/
if (I < 2 || (ZPLoc = ZPLoadAX (S, I-2)) == 0) {
if (I >= 2) {
if ((Loc = LoadAXZP (S, I-2)) != 0) {
/* If the sequence is preceeded by a load of a ZP value,
* we can use this ZP value as a pointer using ZP
* indirect Y addressing.
*/
AM = AM65_ZP_INDY;
} else if ((Loc = LoadAXImm (S, I-2)) != 0) {
/* If the sequence is preceeded by a load of an immediate
* value, we can use this absolute value as an address
* using absolute indexed Y addressing.
*/
AM = AM65_ABSY;
}
}
ZPLoc = "ptr1";
/* If we don't have a zero page location, we use ptr1 with zp
* indirect Y addressing. We must store the value in A/X into
* ptr1 in this case.
*/
if (Loc == 0) {
/* Must use ptr1 */
X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[9]->LI);
Loc = "ptr1";
AM = AM65_ZP_INDY;
X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[8]->LI);
CS_InsertEntry (S, X, IP++);
X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[9]->LI);
X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[8]->LI);
CS_InsertEntry (S, X, IP++);
}
@ -681,7 +712,7 @@ unsigned OptPtrStore5 (CodeSeg* S)
X = NewCodeEntry (OP65_LDY, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
CS_InsertEntry (S, X, IP++);
X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLoc, 0, L[9]->LI);
X = NewCodeEntry (OP65_STA, AM, Loc, 0, L[9]->LI);
CS_InsertEntry (S, X, IP++);
/* Remove the old code */
@ -703,7 +734,7 @@ unsigned OptPtrStore5 (CodeSeg* S)
unsigned OptPtrStore6 (CodeSeg* S)
unsigned OptPtrStore5 (CodeSeg* S)
/* Search for the sequence:
*
* jsr pushax

View File

@ -52,8 +52,6 @@
unsigned OptPtrStore1 (CodeSeg* S);
/* Search for the sequence:
*
* lda #<(label+0)
* ldx #>(label+0)
* clc
* adc xxx
* bcc L
@ -66,11 +64,36 @@ unsigned OptPtrStore1 (CodeSeg* S);
*
* and replace it by:
*
* sta ptr1
* stx ptr1+1
* ldy xxx
* ldx #$00
* lda yyy
* sta label,y
*/
* ldx #$00
* lda yyy
* sta (ptr1),y
*
* or by
*
* ldy xxx
* ldx #$00
* lda yyy
* sta label,y
*
* or by
*
* ldy xxx
* ldx #$00
* lda yyy
* sta $xxxx,y
*
* or by
*
* ldy xxx
* ldx #$00
* lda yyy
* sta (zp),y
*
* depending on the two instructions preceeding the sequence above.
*/
unsigned OptPtrStore2 (CodeSeg* S);
/* Search for the sequence:
@ -127,32 +150,6 @@ unsigned OptPtrStore3 (CodeSeg* S);
*/
unsigned OptPtrStore4 (CodeSeg* S);
/* Search for the sequence:
*
* ldy #offs1
* clc
* adc (sp),y
* bcc L
* inx
* L: jsr pushax
* ldy #offs2
* ldx #$00
* lda (sp),y
* ldy #$00
* jsr staspidx
*
* and replace it by:
*
* sta ptr1
* stx ptr1+1
* ldx #$00
* ldy #offs2-2
* lda (sp),y
* ldy #offs1
* sta (ptr1),y
*/
unsigned OptPtrStore5 (CodeSeg* S);
/* Search for the sequence:
*
* clc
@ -179,8 +176,8 @@ unsigned OptPtrStore5 (CodeSeg* S);
* In case a/x is loaded from the register bank before the clc, we can even
* use the register bank instead of ptr1.
*/
unsigned OptPtrStore6 (CodeSeg* S);
unsigned OptPtrStore5 (CodeSeg* S);
/* Search for the sequence:
*
* jsr pushax