AddCodeLine ("dey");
AddCodeLine ("ora (sp),y");
} else {
- if (CodeSizeFactor > 180) {
- ldyconst (offs + 1);
- AddCodeLine ("lda (sp),y");
- AddCodeLine ("tax");
- AddCodeLine ("dey");
- AddCodeLine ("lda (sp),y");
- } else {
- ldyconst (offs+1);
- AddCodeLine ("jsr ldaxysp");
- }
+ ldyconst (offs+1);
+ AddCodeLine ("jsr ldaxysp");
}
break;
{ "ldax0sp", REG_Y, REG_AX },
{ "ldaxi", REG_AX, REG_AXY | REG_PTR1 },
{ "ldaxidx", REG_AXY, REG_AXY | REG_PTR1 },
- { "ldaxysp", REG_Y, REG_AX },
+ { "ldaxysp", REG_Y, REG_AXY },
{ "leaasp", REG_A, REG_AX },
{ "negax", REG_AX, REG_AX },
{ "pusha", REG_A, REG_Y },
#include "coptcmp.h"
#include "coptind.h"
#include "coptneg.h"
+#include "coptpush.h"
#include "coptstop.h"
#include "coptsub.h"
#include "copttest.h"
/* A list of all the function descriptions */
static OptFunc DOpt65C02Ind = { Opt65C02Ind, "Opt65C02Ind", 100, 0, 0, 0, 0, 0 };
-static OptFunc DOptAdd1 = { OptAdd1, "OptAdd1", 100, 0, 0, 0, 0, 0 };
-static OptFunc DOptAdd2 = { OptAdd2, "OptAdd2", 100, 0, 0, 0, 0, 0 };
+static OptFunc DOptAdd1 = { OptAdd1, "OptAdd1", 60, 0, 0, 0, 0, 0 };
+static OptFunc DOptAdd2 = { OptAdd2, "OptAdd2", 200, 0, 0, 0, 0, 0 };
static OptFunc DOptAdd3 = { OptAdd3, "OptAdd3", 100, 0, 0, 0, 0, 0 };
static OptFunc DOptBoolTrans = { OptBoolTrans, "OptBoolTrans", 100, 0, 0, 0, 0, 0 };
static OptFunc DOptBranchDist = { OptBranchDist, "OptBranchDist", 100, 0, 0, 0, 0, 0 };
static OptFunc DOptPtrLoad6 = { OptPtrLoad6, "OptPtrLoad6", 100, 0, 0, 0, 0, 0 };
static OptFunc DOptPtrStore1 = { OptPtrStore1, "OptPtrStore1", 100, 0, 0, 0, 0, 0 };
static OptFunc DOptPtrStore2 = { OptPtrStore2, "OptPtrStore2", 100, 0, 0, 0, 0, 0 };
+static OptFunc DOptPush1 = { OptPush1, "OptPush1", 65, 0, 0, 0, 0, 0 };
static OptFunc DOptShift1 = { OptShift1, "OptShift1", 100, 0, 0, 0, 0, 0 };
static OptFunc DOptShift2 = { OptShift2, "OptShift2", 100, 0, 0, 0, 0, 0 };
/*static OptFunc DOptSize1 = { OptSize1, "OptSize1", 100, 0, 0, 0, 0, 0 };*/
&DOptPtrLoad6,
&DOptPtrStore1,
&DOptPtrStore2,
+ &DOptPush1,
&DOptRTS,
&DOptRTSJumps1,
&DOptRTSJumps2,
/* Parse the line */
if (sscanf (B, "%31s %lu %*u %lu %*u", Name, &TotalRuns, &TotalChanges) != 3) {
- /* Syntax error */
+ /* Syntax error */
continue;
}
"%-20s %6lu %6lu %6lu %6lu\n",
O->Name,
O->TotalRuns,
- O->LastRuns,
+ O->LastRuns,
O->TotalChanges,
O->LastChanges);
}
{
unsigned Changes, C;
- /* Don't run the function if it is disabled or if it is prohibited by the
+ /* Don't run the function if it is disabled or if it is prohibited by the
* code size factor
*/
if (F->Disabled || CodeSizeFactor < F->CodeSizeFactor) {
-static void RunOptGroup1 (CodeSeg* S)
+static unsigned RunOptGroup1 (CodeSeg* S)
/* Run the first group of optimization steps. These steps translate known
* patterns emitted by the code generator into more optimal patterns. Order
* of the steps is important, because some of the steps done earlier cover
* the same patterns as later steps as subpatterns.
*/
{
- RunOptFunc (S, &DOptPtrStore1, 1);
- RunOptFunc (S, &DOptPtrStore2, 1);
- RunOptFunc (S, &DOptPtrLoad1, 1);
- RunOptFunc (S, &DOptPtrLoad2, 1);
- RunOptFunc (S, &DOptPtrLoad3, 1);
- RunOptFunc (S, &DOptPtrLoad4, 1);
- RunOptFunc (S, &DOptPtrLoad5, 1);
- RunOptFunc (S, &DOptNegAX1, 1);
- RunOptFunc (S, &DOptNegAX2, 1);
- RunOptFunc (S, &DOptNegAX3, 1);
- RunOptFunc (S, &DOptNegAX4, 1);
- RunOptFunc (S, &DOptAdd1, 1);
- RunOptFunc (S, &DOptAdd2, 1);
- RunOptFunc (S, &DOptShift1, 1);
- RunOptFunc (S, &DOptShift2, 1);
+ unsigned Changes = 0;
+
+ Changes += RunOptFunc (S, &DOptPtrStore1, 1);
+ Changes += RunOptFunc (S, &DOptPtrStore2, 1);
+ Changes += RunOptFunc (S, &DOptPtrLoad1, 1);
+ Changes += RunOptFunc (S, &DOptPtrLoad2, 1);
+ Changes += RunOptFunc (S, &DOptPtrLoad3, 1);
+ Changes += RunOptFunc (S, &DOptPtrLoad4, 1);
+ Changes += RunOptFunc (S, &DOptPtrLoad5, 1);
+ Changes += RunOptFunc (S, &DOptNegAX1, 1);
+ Changes += RunOptFunc (S, &DOptNegAX2, 1);
+ Changes += RunOptFunc (S, &DOptNegAX3, 1);
+ Changes += RunOptFunc (S, &DOptNegAX4, 1);
+ Changes += RunOptFunc (S, &DOptAdd1, 1);
+ Changes += RunOptFunc (S, &DOptAdd2, 1);
+ Changes += RunOptFunc (S, &DOptShift1, 1);
+ Changes += RunOptFunc (S, &DOptShift2, 1);
+
+ /* Return the number of changes */
+ return Changes;
}
-static void RunOptGroup2 (CodeSeg* S)
+static unsigned RunOptGroup2 (CodeSeg* S)
/* Run one group of optimization steps. This step involves just decoupling
* instructions by replacing them by instructions that do not depend on
* previous instructions. This makes it easier to find instructions that
* aren't used.
*/
{
- RunOptFunc (S, &DOptDecouple, 1);
-}
+ unsigned Changes = 0;
+
+ Changes += RunOptFunc (S, &DOptDecouple, 1);
+ /* Return the number of changes */
+ return Changes;
+}
-static void RunOptGroup3 (CodeSeg* S)
+static unsigned RunOptGroup3 (CodeSeg* S)
/* Run one group of optimization steps. These steps depend on each other,
* that means that one step may allow another step to do additional work,
* so we will repeat the steps as long as we see any changes.
*/
{
- unsigned Changes;
+ unsigned Changes, C;
+ Changes = 0;
do {
- Changes = 0;
-
- Changes += RunOptFunc (S, &DOptPtrLoad6, 1);
- Changes += RunOptFunc (S, &DOptNegA1, 1);
- Changes += RunOptFunc (S, &DOptNegA2, 1);
- Changes += RunOptFunc (S, &DOptSub1, 1);
- Changes += RunOptFunc (S, &DOptSub2, 1);
- Changes += RunOptFunc (S, &DOptAdd3, 1);
- Changes += RunOptFunc (S, &DOptStackOps, 1);
- Changes += RunOptFunc (S, &DOptJumpCascades, 1);
- Changes += RunOptFunc (S, &DOptDeadJumps, 1);
- Changes += RunOptFunc (S, &DOptRTS, 1);
- Changes += RunOptFunc (S, &DOptDeadCode, 1);
- Changes += RunOptFunc (S, &DOptJumpTarget, 1);
- Changes += RunOptFunc (S, &DOptCondBranches, 1);
- Changes += RunOptFunc (S, &DOptRTSJumps1, 1);
- Changes += RunOptFunc (S, &DOptBoolTrans, 1);
- Changes += RunOptFunc (S, &DOptCmp1, 1);
- Changes += RunOptFunc (S, &DOptCmp2, 1);
- Changes += RunOptFunc (S, &DOptCmp3, 1);
- Changes += RunOptFunc (S, &DOptCmp4, 1);
- Changes += RunOptFunc (S, &DOptCmp5, 1);
- Changes += RunOptFunc (S, &DOptCmp6, 1);
- Changes += RunOptFunc (S, &DOptCmp7, 1);
- Changes += RunOptFunc (S, &DOptTest1, 1);
- Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
- Changes += RunOptFunc (S, &DOptUnusedStores, 1);
- Changes += RunOptFunc (S, &DOptDupLoads, 1);
- Changes += RunOptFunc (S, &DOptStoreLoad, 1);
- Changes += RunOptFunc (S, &DOptTransfers, 1);
-
- } while (Changes);
+ C = 0;
+
+ C += RunOptFunc (S, &DOptPtrLoad6, 1);
+ C += RunOptFunc (S, &DOptNegA1, 1);
+ C += RunOptFunc (S, &DOptNegA2, 1);
+ C += RunOptFunc (S, &DOptSub1, 1);
+ C += RunOptFunc (S, &DOptSub2, 1);
+ C += RunOptFunc (S, &DOptAdd3, 1);
+ C += RunOptFunc (S, &DOptStackOps, 1);
+ C += RunOptFunc (S, &DOptJumpCascades, 1);
+ C += RunOptFunc (S, &DOptDeadJumps, 1);
+ C += RunOptFunc (S, &DOptRTS, 1);
+ C += RunOptFunc (S, &DOptDeadCode, 1);
+ C += RunOptFunc (S, &DOptJumpTarget, 1);
+ C += RunOptFunc (S, &DOptCondBranches, 1);
+ C += RunOptFunc (S, &DOptRTSJumps1, 1);
+ C += RunOptFunc (S, &DOptBoolTrans, 1);
+ C += RunOptFunc (S, &DOptCmp1, 1);
+ C += RunOptFunc (S, &DOptCmp2, 1);
+ C += RunOptFunc (S, &DOptCmp3, 1);
+ C += RunOptFunc (S, &DOptCmp4, 1);
+ C += RunOptFunc (S, &DOptCmp5, 1);
+ C += RunOptFunc (S, &DOptCmp6, 1);
+ C += RunOptFunc (S, &DOptCmp7, 1);
+ C += RunOptFunc (S, &DOptTest1, 1);
+ C += RunOptFunc (S, &DOptUnusedLoads, 1);
+ C += RunOptFunc (S, &DOptUnusedStores, 1);
+ C += RunOptFunc (S, &DOptDupLoads, 1);
+ C += RunOptFunc (S, &DOptStoreLoad, 1);
+ C += RunOptFunc (S, &DOptTransfers, 1);
+
+ Changes += C;
+
+ } while (C);
+
+ /* Return the number of changes */
+ return Changes;
}
-static void RunOptGroup4 (CodeSeg* S)
+static unsigned RunOptGroup4 (CodeSeg* S)
/* 65C02 specific optimizations. */
{
- if (CPU < CPU_65C02) {
- return;
- }
+ unsigned Changes = 0;
- /* Replace (zp),y by (zp) if Y is zero. If we have changes, run register
- * load optimization again, since loads of Y may have become unnecessary.
- */
- if (RunOptFunc (S, &DOpt65C02Ind, 1) > 0) {
- RunOptFunc (S, &DOptUnusedLoads, 1);
+ if (CPU >= CPU_65C02) {
+ /* Replace (zp),y by (zp) if Y is zero. If we have changes, run register
+ * load optimization again, since loads of Y may have become unnecessary.
+ */
+ unsigned C = RunOptFunc (S, &DOpt65C02Ind, 1);
+ Changes += C;
+ if (C) {
+ Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
+ }
}
+
+ /* Return the number of changes */
+ return Changes;
}
-static void RunOptGroup5 (CodeSeg* S)
+static unsigned RunOptGroup5 (CodeSeg* S)
+/* Run another round of pattern replacements. These are done late, since there
+ * may be better replacements before.
+ */
+{
+ unsigned Changes = 0;
+
+ Changes += RunOptFunc (S, &DOptPush1, 1);
+
+ /* Return the number of changes */
+ return Changes;
+}
+
+
+
+static unsigned RunOptGroup6 (CodeSeg* S)
/* The last group of optimization steps. Adjust branches, do size optimizations.
*/
{
+ unsigned Changes = 0;
+ unsigned C;
+
/* Optimize for size, that is replace operations by shorter ones, even
* if this does hinder further optimizations (no problem since we're
* done soon).
*/
- RunOptFunc (S, &DOptSize2, 1);
+ Changes += RunOptFunc (S, &DOptSize2, 1);
/* Run the jump target optimization again, since the size optimization
* above may have opened new oportunities.
*/
- RunOptFunc (S, &DOptJumpTarget, 5);
+ Changes += RunOptFunc (S, &DOptJumpTarget, 5);
/* Adjust branch distances */
- RunOptFunc (S, &DOptBranchDist, 3);
+ Changes += RunOptFunc (S, &DOptBranchDist, 3);
/* Replace conditional branches to RTS. If we had changes, we must run dead
* code elimination again, since the change may have introduced dead code.
*/
- if (RunOptFunc (S, &DOptRTSJumps2, 1)) {
- RunOptFunc (S, &DOptDeadCode, 1);
+ C = RunOptFunc (S, &DOptRTSJumps2, 1);
+ Changes += C;
+ if (C) {
+ Changes += RunOptFunc (S, &DOptDeadCode, 1);
}
+
+ /* Return the number of changes */
+ return Changes;
}
/* If we shouldn't run the optimizer, bail out */
if (!Optimize) {
- return;
+ return;
}
/* Check if we are requested to write optimizer statistics */
RunOptGroup3 (S);
RunOptGroup4 (S);
RunOptGroup5 (S);
+ RunOptGroup6 (S);
/* Write statistics */
if (StatFileName) {
unsigned OptAdd2 (CodeSeg* S)
/* Search for the sequence
*
- * ldy #yy
- * lda (sp),y
- * tax
- * ldy #xx
- * lda (sp),y
- * ldy #zz
+ * ldy #xx
+ * jsr ldaxysp
+ * ldy #yy
* jsr addeqysp
*
* and replace it by:
*
- * ldy #xx
+ * ldy #xx-1
* lda (sp),y
- * ldy #zz
+ * ldy #yy
* clc
* adc (sp),y
* sta (sp),y
- * ldy #yy
+ * ldy #xx
* lda (sp),y
- * ldy #zz+1
+ * ldy #yy+1
* adc (sp),y
* sta (sp),y
*
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
- CodeEntry* L[7];
+ CodeEntry* L[4];
/* Get next entry */
L[0] = CS_GetEntry (S, I);
/* Check for the sequence */
if (L[0]->OPC == OP65_LDY &&
CE_KnownImm (L[0]) &&
- CS_GetEntries (S, L+1, I+1, 6) &&
- L[1]->OPC == OP65_LDA &&
- L[1]->AM == AM65_ZP_INDY &&
- !CE_HasLabel (L[1]) &&
- L[2]->OPC == OP65_TAX &&
- !CE_HasLabel (L[2]) &&
- L[3]->OPC == OP65_LDY &&
- CE_KnownImm (L[3]) &&
- !CE_HasLabel (L[3]) &&
- L[4]->OPC == OP65_LDA &&
- L[4]->AM == AM65_ZP_INDY &&
- !CE_HasLabel (L[4]) &&
- L[5]->OPC == OP65_LDY &&
- CE_KnownImm (L[5]) &&
- !CE_HasLabel (L[5]) &&
- CE_IsCall (L[6], "addeqysp") &&
- !CE_HasLabel (L[6]) &&
- (GetRegInfo (S, I+7, REG_AX) & REG_AX) == 0) {
+ !CS_RangeHasLabel (S, I+1, 3) &&
+ CS_GetEntries (S, L+1, I+1, 3) &&
+ CE_IsCall (L[1], "ldaxysp") &&
+ L[2]->OPC == OP65_LDY &&
+ CE_KnownImm (L[2]) &&
+ CE_IsCall (L[3], "addeqysp") &&
+ (GetRegInfo (S, I+4, REG_AX) & REG_AX) == 0) {
+ /* Insert new code behind the addeqysp */
char Buf [20];
CodeEntry* X;
+ /* ldy #xx-1 */
+ xsprintf (Buf, sizeof (Buf), "$%02X", (int)(L[0]->Num-1));
+ X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[0]->LI);
+ CS_InsertEntry (S, X, I+4);
+
+ /* lda (sp),y */
+ X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI);
+ CS_InsertEntry (S, X, I+5);
- /* Insert new code behind the addeqysp */
- X = NewCodeEntry (OP65_LDY, AM65_IMM, L[3]->Arg, 0, L[3]->LI);
+ /* ldy #yy */
+ X = NewCodeEntry (OP65_LDY, AM65_IMM, L[2]->Arg, 0, L[2]->LI);
+ CS_InsertEntry (S, X, I+6);
+
+ /* clc */
+ X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, L[3]->LI);
CS_InsertEntry (S, X, I+7);
- X = NewCodeEntry (OP65_LDA, L[4]->AM, L[4]->Arg, 0, L[4]->LI);
+ /* adc (sp),y */
+ X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp", 0, L[3]->LI);
CS_InsertEntry (S, X, I+8);
- X = NewCodeEntry (OP65_LDY, AM65_IMM, L[5]->Arg, 0, L[5]->LI);
+ /* sta (sp),y */
+ X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "sp", 0, L[3]->LI);
CS_InsertEntry (S, X, I+9);
- X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, L[6]->LI);
+ /* ldy #xx */
+ X = NewCodeEntry (OP65_LDY, AM65_IMM, L[0]->Arg, 0, L[0]->LI);
CS_InsertEntry (S, X, I+10);
- X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp", 0, L[6]->LI);
+ /* lda (sp),y */
+ X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI);
CS_InsertEntry (S, X, I+11);
- X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "sp", 0, L[6]->LI);
+ /* ldy #yy+1 */
+ xsprintf (Buf, sizeof (Buf), "$%02X", (int)(L[2]->Num+1));
+ X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[2]->LI);
CS_InsertEntry (S, X, I+12);
- X = NewCodeEntry (OP65_LDY, AM65_IMM, L[0]->Arg, 0, L[0]->LI);
+ /* adc (sp),y */
+ X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp", 0, L[3]->LI);
CS_InsertEntry (S, X, I+13);
- X = NewCodeEntry (OP65_LDA, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
+ /* sta (sp),y */
+ X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "sp", 0, L[3]->LI);
CS_InsertEntry (S, X, I+14);
- xsprintf (Buf, sizeof (Buf), "$%02X", (int)(L[5]->Num+1));
- X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[5]->LI);
- CS_InsertEntry (S, X, I+15);
-
- X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp", 0, L[6]->LI);
- CS_InsertEntry (S, X, I+16);
-
- X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "sp", 0, L[6]->LI);
- CS_InsertEntry (S, X, I+17);
-
/* Delete the old code */
- CS_DelEntries (S, I, 7);
+ CS_DelEntries (S, I, 4);
/* Remember, we had changes */
++Changes;
-static int IsCmpToZero (const CodeEntry* E)
-/* Check if the given instrcuction is a compare to zero instruction */
-{
- return (E->OPC == OP65_CMP &&
- E->AM == AM65_IMM &&
- (E->Flags & CEF_NUMARG) != 0 &&
- E->Num == 0);
-}
-
-
-
-static int IsSpLoad (const CodeEntry* E)
-/* Return true if this is the load of A from the stack */
-{
- return E->OPC == OP65_LDA && E->AM == AM65_ZP_INDY && strcmp (E->Arg, "sp") == 0;
-}
-
-
-
-static int IsLocalLoad16 (CodeSeg* S, unsigned Index,
- CodeEntry** L, unsigned Count)
-/* Check if a 16 bit load of a local variable follows:
- *
- * ldy #$xx
- * lda (sp),y
- * tax
- * dey
- * lda (sp),y
- *
- * If so, read Count entries following the first ldy into L and return true
- * if this is possible. Otherwise return false.
- */
-{
- /* Be sure we read enough entries for the check */
- CHECK (Count >= 5);
-
- /* Read the first entry */
- L[0] = CS_GetEntry (S, Index);
-
- /* Check for the sequence */
- return (L[0]->OPC == OP65_LDY &&
- CE_KnownImm (L[0]) &&
- CS_GetEntries (S, L+1, Index+1, Count-1) &&
- IsSpLoad (L[1]) &&
- !CE_HasLabel (L[1]) &&
- L[2]->OPC == OP65_TAX &&
- !CE_HasLabel (L[2]) &&
- L[3]->OPC == OP65_LDY &&
- CE_KnownImm (L[3]) &&
- L[3]->Num == L[0]->Num - 1 &&
- !CE_HasLabel (L[3]) &&
- IsSpLoad (L[4]) &&
- !CE_HasLabel (L[4]));
-}
-
-
-
/*****************************************************************************/
/* Remove calls to the bool transformer subroutines */
/*****************************************************************************/
/* Check for the sequence */
if (E->OPC == OP65_STX &&
+ !CS_RangeHasLabel (S, I+1, 2) &&
CS_GetEntries (S, L, I+1, 2) &&
L[0]->OPC == OP65_STX &&
strcmp (L[0]->Arg, "tmp1") == 0 &&
- !CE_HasLabel (L[0]) &&
L[1]->OPC == OP65_ORA &&
- strcmp (L[1]->Arg, "tmp1") == 0 &&
- !CE_HasLabel (L[1])) {
+ strcmp (L[1]->Arg, "tmp1") == 0) {
/* Remove the remaining instructions */
CS_DelEntries (S, I+1, 2);
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
- CodeEntry* L[2];
+ CodeEntry* L[3];
/* Get next entry */
- CodeEntry* E = CS_GetEntry (S, I);
+ L[0] = CS_GetEntry (S, I);
/* Check for the sequence */
- if ((E->OPC == OP65_ADC ||
- E->OPC == OP65_AND ||
- E->OPC == OP65_DEA ||
- E->OPC == OP65_EOR ||
- E->OPC == OP65_INA ||
- E->OPC == OP65_LDA ||
- E->OPC == OP65_ORA ||
- E->OPC == OP65_PLA ||
- E->OPC == OP65_SBC ||
- E->OPC == OP65_TXA ||
- E->OPC == OP65_TYA) &&
- CS_GetEntries (S, L, I+1, 2) &&
- IsCmpToZero (L[0]) &&
- !CE_HasLabel (L[0]) &&
- ((L[1]->Info & OF_FBRA) != 0 ||
- (L[1]->OPC == OP65_JSR &&
- FindBoolCmpCond (L[1]->Arg) != CMP_INV)) &&
- !CE_HasLabel (L[1])) {
-
- /* Remove the compare */
- CS_DelEntry (S, I+1);
+ if ((L[0]->OPC == OP65_ADC ||
+ L[0]->OPC == OP65_AND ||
+ L[0]->OPC == OP65_DEA ||
+ L[0]->OPC == OP65_EOR ||
+ L[0]->OPC == OP65_INA ||
+ L[0]->OPC == OP65_LDA ||
+ L[0]->OPC == OP65_ORA ||
+ L[0]->OPC == OP65_PLA ||
+ L[0]->OPC == OP65_SBC ||
+ L[0]->OPC == OP65_TXA ||
+ L[0]->OPC == OP65_TYA) &&
+ !CS_RangeHasLabel (S, I+1, 2) &&
+ CS_GetEntries (S, L+1, I+1, 2) &&
+ L[1]->OPC == OP65_CMP &&
+ CE_KnownImm (L[1]) &&
+ L[1]->Num == 0) {
+
+ /* Check for the call to boolxx. We cannot remove the compare if
+ * the carry flag is evaluated later, because the load will not
+ * set the carry flag.
+ */
+ if (L[2]->OPC == OP65_JSR) {
+ switch (FindBoolCmpCond (L[2]->Arg)) {
+
+ case CMP_EQ:
+ case CMP_NE:
+ case CMP_GT:
+ case CMP_GE:
+ case CMP_LT:
+ case CMP_LE:
+ /* Remove the compare */
+ CS_DelEntry (S, I+1);
+ ++Changes;
+ break;
+
+ case CMP_UGT:
+ case CMP_UGE:
+ case CMP_ULT:
+ case CMP_ULE:
+ case CMP_INV:
+ /* Leave it alone */
+ break;
+ }
- /* Remember, we had changes */
- ++Changes;
+ } else {
+ /* Check for a branch on conditions that are set by the load.
+ * Beware: The insn may branch to another conditional branch
+ * that evaluates other flags, so check that.
+ */
+ CodeEntry* E = L[2];
+ int Delete = 0;
+ while (1) {
+ if ((E->Info & (OF_CBRA|OF_UBRA)) != 0) {
+ /* A conditional branch. Check if it jumps on a
+ * condition not set by the load.
+ */
+ if ((E->Info & (OF_FBRA|OF_UBRA)) == 0) {
+ /* Invalid branch */
+ break;
+ } else if (E->JumpTo == 0) {
+ /* Jump to external */
+ Delete = 1;
+ break;
+ } else {
+ /* Check target of branch */
+ E = E->JumpTo->Owner;
+ }
+ } else {
+ /* Some other insn */
+ Delete = 1;
+ break;
+ }
+ }
+
+ /* Delete the compare if we can */
+ if (Delete) {
+ CS_DelEntry (S, I+1);
+ ++Changes;
+ }
+ }
}
/* Next entry */
/* Check for the sequence */
if (E->OPC == OP65_LDA &&
- CS_GetEntries (S, L, I+1, 5) &&
+ CS_GetEntries (S, L, I+1, 5) &&
L[0]->OPC == OP65_LDX &&
!CE_HasLabel (L[0]) &&
IsImmCmp16 (L+1) &&
/* Optimize compares of local variables:
*
* ldy #o
- * lda (sp),y
- * tax
- * dey
- * lda (sp),y
+ * jsr ldaxysp
* cpx #a
* bne L1
* cmp #b
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
- CodeEntry* L[9];
+ CodeEntry* L[6];
+
+ /* Get the next entry */
+ L[0] = CS_GetEntry (S, I);
/* Check for the sequence */
- if (IsLocalLoad16 (S, I, L, 9) && IsImmCmp16 (L+5)) {
+ if (L[0]->OPC == OP65_LDY &&
+ CE_KnownImm (L[0]) &&
+ CS_GetEntries (S, L+1, I+1, 5) &&
+ !CE_HasLabel (L[1]) &&
+ CE_IsCall (L[1], "ldaxysp") &&
+ IsImmCmp16 (L+2)) {
- if ((L[8]->Info & OF_FBRA) != 0 && L[5]->Num == 0 && L[7]->Num == 0) {
+ if ((L[5]->Info & OF_FBRA) != 0 && L[2]->Num == 0 && L[4]->Num == 0) {
- /* The value is zero, we may use the simple code version:
- * ldy #o
- * lda (sp),y
+ CodeEntry* X;
+ char Buf[20];
+
+ /* The value is zero, we may use the simple code version:
* ldy #o-1
+ * lda (sp),y
+ * ldy #o
* ora (sp),y
* jne/jeq ...
*/
- CE_ReplaceOPC (L[4], OP65_ORA);
+ sprintf (Buf, "$%02X", (int)(L[0]->Num-1));
+ X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[0]->LI);
+ CS_InsertEntry (S, X, I+1);
+
+ X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI);
+ CS_InsertEntry (S, X, I+2);
+
+ X = NewCodeEntry (OP65_LDY, AM65_IMM, L[0]->Arg, 0, L[0]->LI);
+ CS_InsertEntry (S, X, I+3);
+
+ X = NewCodeEntry (OP65_ORA, AM65_ZP_INDY, "sp", 0, L[1]->LI);
+ CS_InsertEntry (S, X, I+4);
+
CS_DelEntries (S, I+5, 3); /* cpx/bne/cmp */
- CS_DelEntry (S, I+2); /* tax */
+ CS_DelEntry (S, I); /* ldy */
} else {
+ CodeEntry* X;
+ char Buf[20];
+
/* Change the code to just use the A register. Move the load
* of the low byte after the first branch if possible:
*
- * ldy #o
+ * ldy #o-1
* lda (sp),y
* cmp #a
* bne L1
- * ldy #o-1
+ * ldy #o
* lda (sp),y
* cmp #b
* jne/jeq ...
*/
- CS_DelEntry (S, I+2); /* tax */
- CE_ReplaceOPC (L[5], OP65_CMP); /* cpx -> cmp */
- CS_MoveEntry (S, I+4, I+2); /* cmp */
- CS_MoveEntry (S, I+5, I+3); /* bne */
+ sprintf (Buf, "$%02X", (int)(L[0]->Num-1));
+ X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[0]->LI);
+ CS_InsertEntry (S, X, I+3);
+
+ X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI);
+ CS_InsertEntry (S, X, I+4);
+
+ X = NewCodeEntry (OP65_CMP, L[2]->AM, L[2]->Arg, 0, L[2]->LI);
+ CS_InsertEntry (S, X, I+5);
+
+ X = NewCodeEntry (OP65_LDY, AM65_IMM, L[0]->Arg, 0, L[0]->LI);
+ CS_InsertEntry (S, X, I+7);
+
+ X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI);
+ CS_InsertEntry (S, X, I+8);
+
+ CS_DelEntries (S, I, 3); /* ldy/jsr/cpx */
}
+
if ((GetRegInfo (S, I+1, R) & R) == 0) {
/* Register value is not used, remove the load */
- CS_DelEntry (S, I);
+ CS_DelEntry (S, I);
- /* Remember, we had changes */
- ++Changes;
+ /* Remember, we had changes */
+ ++Changes;
}
}
* that in the A register, replace the store by a STA. The
* optimizer will then remove the load instruction for Y
* later. If replacement by A is not possible try a
- * replacement by X, but check for invalid addressing modes
+ * replacement by X, but check for invalid addressing modes
* in this case.
*/
} else if (In->RegY >= 0) {
goto NextEntry;
}
if ((X->Info & OF_FBRA) != 0) {
- if (I == 0) {
+ if (I == 0) {
/* No preceeding entry */
goto NextEntry;
}
CodeEntry* E = CS_GetEntry (S, I);
/* Check if this is a call to bnegax, and if X is known and zero */
- if (E->RI->In.RegX == 0 &&
- CE_IsCall (E, "bnegax")) {
+ if (E->RI->In.RegX == 0 && CE_IsCall (E, "bnegax")) {
- /* We're cheating somewhat here ... */
- E->Arg[5] = '\0';
- E->Use &= ~REG_X;
+ CodeEntry* X = NewCodeEntry (OP65_JSR, AM65_ABS, "bnega", 0, E->LI);
+ CS_InsertEntry (S, X, I+1);
+ CS_DelEntry (S, I);
/* We had changes */
++Changes;
unsigned OptNegAX2 (CodeSeg* S)
/* Search for the sequence:
*
- * lda (xx),y
- * tax
- * dey
- * lda (xx),y
+ * ldy #xx
+ * jsr ldaxysp
* jsr bnegax
* jne/jeq ...
*
* and replace it by
*
- * lda (xx),y
+ * ldy #xx
+ * lda (sp),y
* dey
- * ora (xx),y
+ * ora (sp),y
* jeq/jne ...
*/
{
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
- CodeEntry* L[5];
+ CodeEntry* L[4];
/* Get next entry */
- CodeEntry* E = CS_GetEntry (S, I);
+ L[0] = CS_GetEntry (S, I);
/* Check for the sequence */
- if (E->OPC == OP65_LDA &&
- E->AM == AM65_ZP_INDY &&
- CS_GetEntries (S, L, I+1, 5) &&
- L[0]->OPC == OP65_TAX &&
- L[1]->OPC == OP65_DEY &&
- L[2]->OPC == OP65_LDA &&
- L[2]->AM == AM65_ZP_INDY &&
- strcmp (L[2]->Arg, E->Arg) == 0 &&
- !CE_HasLabel (L[2]) &&
- CE_IsCall (L[3], "bnegax") &&
- !CE_HasLabel (L[3]) &&
- (L[4]->Info & OF_ZBRA) != 0 &&
- !CE_HasLabel (L[4])) {
-
- /* lda --> ora */
- CE_ReplaceOPC (L[2], OP65_ORA);
+ if (L[0]->OPC == OP65_LDY &&
+ CE_KnownImm (L[0]) &&
+ !CS_RangeHasLabel (S, I+1, 3) &&
+ CS_GetEntries (S, L+1, I+1, 3) &&
+ CE_IsCall (L[1], "ldaxysp") &&
+ CE_IsCall (L[2], "bnegax") &&
+ (L[3]->Info & OF_ZBRA) != 0) {
+
+ CodeEntry* X;
+
+ /* lda (sp),y */
+ X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI);
+ CS_InsertEntry (S, X, I+1);
+
+ /* dey */
+ X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, L[1]->LI);
+ CS_InsertEntry (S, X, I+2);
+
+ /* ora (sp),y */
+ X = NewCodeEntry (OP65_ORA, AM65_ZP_INDY, "sp", 0, L[1]->LI);
+ CS_InsertEntry (S, X, I+3);
/* Invert the branch */
- CE_ReplaceOPC (L[4], GetInverseBranch (L[4]->OPC));
+ CE_ReplaceOPC (L[3], GetInverseBranch (L[3]->OPC));
- /* Delete the entries no longer needed. Beware: Deleting entries
- * will change the indices.
- */
- CS_DelEntry (S, I+4); /* jsr bnegax */
- CS_DelEntry (S, I+1); /* tax */
+ /* Delete the entries no longer needed. */
+ CS_DelEntries (S, I+4, 2);
/* Remember, we had changes */
++Changes;
--- /dev/null
+/*****************************************************************************/
+/* */
+/* coptpush.c */
+/* */
+/* Optimize push sequences */
+/* */
+/* */
+/* */
+/* (C) 2001 Ullrich von Bassewitz */
+/* Wacholderweg 14 */
+/* D-70597 Stuttgart */
+/* EMail: uz@cc65.org */
+/* */
+/* */
+/* This software is provided 'as-is', without any expressed or implied */
+/* warranty. In no event will the authors be held liable for any damages */
+/* arising from the use of this software. */
+/* */
+/* Permission is granted to anyone to use this software for any purpose, */
+/* including commercial applications, and to alter it and redistribute it */
+/* freely, subject to the following restrictions: */
+/* */
+/* 1. The origin of this software must not be misrepresented; you must not */
+/* claim that you wrote the original software. If you use this software */
+/* in a product, an acknowledgment in the product documentation would be */
+/* appreciated but is not required. */
+/* 2. Altered source versions must be plainly marked as such, and must not */
+/* be misrepresented as being the original software. */
+/* 3. This notice may not be removed or altered from any source */
+/* distribution. */
+/* */
+/*****************************************************************************/
+
+
+
+/* common */
+#include "xsprintf.h"
+
+/* cc65 */
+#include "codeent.h"
+#include "codeinfo.h"
+#include "coptpush.h"
+
+
+
+/*****************************************************************************/
+/* Code */
+/*****************************************************************************/
+
+
+
+unsigned OptPush1 (CodeSeg* S)
+/* Given a sequence
+ *
+ * ldy #xx
+ * jsr ldaxysp
+ * jsr pushax
+ *
+ * If a/x are not used later, replace that by
+ *
+ * ldy #xx+2
+ * jsr pushwysp
+ *
+ * saving 3 bytes and several cycles.
+ */
+{
+ unsigned Changes = 0;
+
+ /* Walk over the entries */
+ unsigned I = 0;
+ while (I < CS_GetEntryCount (S)) {
+
+ CodeEntry* L[3];
+
+ /* Get next entry */
+ L[0] = CS_GetEntry (S, I);
+
+ /* Check for the sequence */
+ if (L[0]->OPC == OP65_LDY &&
+ CE_KnownImm (L[0]) &&
+ L[0]->Num < 0xFE &&
+ !CS_RangeHasLabel (S, I+1, 2) &&
+ CS_GetEntries (S, L+1, I+1, 2) &&
+ CE_IsCall (L[1], "ldaxysp") &&
+ CE_IsCall (L[2], "pushax") &&
+ (GetRegInfo (S, I+3, REG_AX) & REG_AX) == 0) {
+
+ /* Insert new code behind the pushax */
+ char Buf [20];
+ CodeEntry* X;
+
+ /* ldy #xx+1 */
+ xsprintf (Buf, sizeof (Buf), "$%02X", (int)(L[0]->Num+2));
+ X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[0]->LI);
+ CS_InsertEntry (S, X, I+3);
+
+ /* jsr pushwysp */
+ X = NewCodeEntry (OP65_JSR, AM65_ABS, "pushwysp", 0, L[2]->LI);
+ CS_InsertEntry (S, X, I+4);
+
+ /* Delete the old code */
+ CS_DelEntries (S, I, 3);
+
+ /* Remember, we had changes */
+ ++Changes;
+
+ }
+
+ /* Next entry */
+ ++I;
+
+ }
+
+ /* Return the number of changes made */
+ return Changes;
+}
+
+
+
--- /dev/null
+/*****************************************************************************/
+/* */
+/* coptpush.h */
+/* */
+/* Optimize push sequences */
+/* */
+/* */
+/* */
+/* (C) 2001 Ullrich von Bassewitz */
+/* Wacholderweg 14 */
+/* D-70597 Stuttgart */
+/* EMail: uz@cc65.org */
+/* */
+/* */
+/* This software is provided 'as-is', without any expressed or implied */
+/* warranty. In no event will the authors be held liable for any damages */
+/* arising from the use of this software. */
+/* */
+/* Permission is granted to anyone to use this software for any purpose, */
+/* including commercial applications, and to alter it and redistribute it */
+/* freely, subject to the following restrictions: */
+/* */
+/* 1. The origin of this software must not be misrepresented; you must not */
+/* claim that you wrote the original software. If you use this software */
+/* in a product, an acknowledgment in the product documentation would be */
+/* appreciated but is not required. */
+/* 2. Altered source versions must be plainly marked as such, and must not */
+/* be misrepresented as being the original software. */
+/* 3. This notice may not be removed or altered from any source */
+/* distribution. */
+/* */
+/*****************************************************************************/
+
+
+
+#ifndef COPTPUSH_H
+#define COPTPUSH_H
+
+
+
+/* cc65 */
+#include "codeseg.h"
+
+
+
+/*****************************************************************************/
+/* Code */
+/*****************************************************************************/
+
+
+
+unsigned OptPush1 (CodeSeg* S);
+/* Given a sequence
+ *
+ * ldy #xx
+ * jsr ldaxysp
+ * jsr pushax
+ *
+ * If a/x are not used later, replace that by
+ *
+ * ldy #xx+2
+ * jsr pushwysp
+ *
+ * saving 3 bytes and several cycles.
+ */
+
+
+
+/* End of coptpush.h */
+
+#endif
+
+
+
/* Check for the sequence */
if (E->OPC == OP65_LDA &&
+ !CS_RangeHasLabel (S, I+1, 5) &&
CS_GetEntries (S, L, I+1, 5) &&
L[0]->OPC == OP65_SEC &&
- !CE_HasLabel (L[0]) &&
L[1]->OPC == OP65_STA &&
strcmp (L[1]->Arg, "tmp1") == 0 &&
- !CE_HasLabel (L[1]) &&
L[2]->OPC == OP65_LDA &&
- !CE_HasLabel (L[2]) &&
L[3]->OPC == OP65_SBC &&
strcmp (L[3]->Arg, "tmp1") == 0 &&
- !CE_HasLabel (L[3]) &&
L[4]->OPC == OP65_STA &&
- strcmp (L[4]->Arg, L[2]->Arg) == 0 &&
- !CE_HasLabel (L[4])) {
+ strcmp (L[4]->Arg, L[2]->Arg) == 0) {
/* Remove the store to tmp1 */
CS_DelEntry (S, I+2);
coptcmp.o \
coptind.o \
coptneg.o \
+ coptpush.o \
coptstop.o \
coptsub.o \
copttest.o \
coptcmp.obj \
coptind.obj \
coptneg.obj \
+ coptpush.obj \
coptstop.obj \
coptsub.obj \
copttest.obj \
FILE coptcmp.obj
FILE coptind.obj
FILE coptneg.obj
+FILE coptpush.obj
FILE coptstop.obj
FILE coptsub.obj
FILE copttest.obj