X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fcc65%2Fcoptstop.c;h=4f83b8ef5b64de7d7965b165c56d494eb299d391;hb=73dfa23c987d8a7f1154801b85c171f9e01dcd58;hp=a9575022f0ecde28674bd6eac0fc87eb377d8fef;hpb=0c09cc724243bbdb40179564cb05e10a52bba519;p=cc65 diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index a9575022f..4f83b8ef5 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -6,7 +6,7 @@ /* */ /* */ /* */ -/* (C) 2001 Ullrich von Bassewitz */ +/* (C) 2001-2002 Ullrich von Bassewitz */ /* Wacholderweg 14 */ /* D-70597 Stuttgart */ /* EMail: uz@cc65.org */ @@ -34,27 +34,51 @@ #include - -/* common */ -#include "xsprintf.h" +#include /* cc65 */ #include "codeent.h" #include "codeinfo.h" -#include "codeopt.h" -#include "error.h" #include "coptstop.h" /*****************************************************************************/ -/* Helpers */ +/* Data */ +/*****************************************************************************/ + + + +/* Structure that holds the needed data */ +typedef struct StackOpData StackOpData; +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 */ + CodeEntry* PrevEntry; /* Entry before the call to pushax */ + CodeEntry* PushEntry; /* Pointer to entry with call to pushax */ + CodeEntry* OpEntry; /* Pointer to entry with op */ + CodeEntry* NextEntry; /* Entry after the op */ + const char* ZPLo; /* Lo byte of zero page loc to use */ + const char* ZPHi; /* Hi byte of zero page loc to use */ + unsigned IP; /* Insertion point used by some routines */ +}; + +/* Flags returned by DirectOp */ +#define OP_DIRECT 0x01 /* Direct op may be used */ +#define OP_RELOAD_Y 0x02 /* Must reload index register Y */ + + + +/*****************************************************************************/ +/* Helpers */ /*****************************************************************************/ static unsigned AdjustStackOffset (CodeSeg* S, unsigned Start, unsigned Stop, - unsigned Offs) + 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. @@ -69,14 +93,27 @@ static unsigned AdjustStackOffset (CodeSeg* S, unsigned Start, unsigned Stop, CodeEntry* E = CS_GetEntry (S, I); - if (E->Use & REG_SP) { - - CodeEntry* P; + int NeedCorrection = 0; + if ((E->Use & REG_SP) != 0) { /* Check for some things that should not happen */ CHECK (E->AM == AM65_ZP_INDY || E->RI->In.RegY >= (short) Offs); CHECK (strcmp (E->Arg, "sp") == 0); + /* We need to correct this one */ + NeedCorrection = 1; + + } else if (CE_IsCallTo (E, "ldaxysp")) { + + /* We need to correct this one */ + NeedCorrection = 1; + + } + + if (NeedCorrection) { + + CodeEntry* P; + /* Get the code entry before this one. If it's a LDY, adjust the * value. */ @@ -89,10 +126,8 @@ static unsigned AdjustStackOffset (CodeSeg* S, unsigned Start, unsigned Stop, } else { /* Insert a new load instruction before the stack access */ - char Buf [16]; - CodeEntry* X; - xsprintf (Buf, sizeof (Buf), "$%02X", E->RI->In.RegY - Offs); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, E->LI); + 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); /* One more inserted entries */ @@ -116,40 +151,212 @@ static unsigned AdjustStackOffset (CodeSeg* S, unsigned Start, unsigned Stop, +static void InsertEntry (StackOpData* D, CodeEntry* E, unsigned Index) +/* Insert a new entry. Depending on Index, D->PushIndex and D->OpIndex will + * be adjusted by this function. + */ +{ + /* Insert the entry into the code segment */ + CS_InsertEntry (D->Code, E, Index); + + /* Adjust the indices if necessary */ + if (D->PushEntry && Index <= D->PushIndex) { + ++D->PushIndex; + } + if (D->OpEntry && Index <= D->OpIndex) { + ++D->OpIndex; + } +} + + + +static void DelEntry (StackOpData* D, unsigned Index) +/* Delete an entry. Depending on Index, D->PushIndex and D->OpIndex will be + * adjusted by this function, and PushEntry/OpEntry may get invalidated. + */ +{ + /* Delete the entry from the code segment */ + CS_DelEntry (D->Code, Index); + + /* Adjust the indices if necessary */ + if (Index < D->PushIndex) { + --D->PushIndex; + } else if (Index == D->PushIndex) { + D->PushEntry = 0; + } + if (Index < D->OpIndex) { + --D->OpIndex; + } else if (Index == D->OpIndex) { + D->OpEntry = 0; + } +} + + + +static void CheckDirectOp (StackOpData* D) +/* Check if the given entry is a lda instruction with an addressing mode + * that allows us to replace it by another operation (like ora). If so, we may + * use this location for the or and must not save the value in the zero + * page location. + */ +{ + /* We need the entry before the push */ + CodeEntry* E; + CHECK ((E = D->PrevEntry) != 0); + + if (E->OPC == OP65_LDA) { + if (E->AM == AM65_IMM || E->AM == AM65_ZP || E->AM == AM65_ABS) { + /* These insns are all ok and replaceable */ + D->Flags |= OP_DIRECT; + } else if (E->AM == AM65_ZP_INDY && RegValIsKnown (E->RI->In.RegY) && + strcmp (E->Arg, "sp") == 0) { + /* A load from the stack with known offset is also ok, but in this + * case we must reload the index register later. Please note that + * a load indirect via other zero page locations is not ok, since + * these locations may change between the push and the actual + * operation. + */ + D->Flags |= (OP_DIRECT | OP_RELOAD_Y); + } + } +} + + + +static void ReplacePushByStore (StackOpData* D) +/* Replace the call to the push subroutine by a store into the zero page + * location (actually, the push is not replaced, because we need it for + * later, but the name is still ok since the push will get removed at the + * end of each routine). + */ +{ + CodeEntry* X; + + /* Store the value into the zeropage instead of pushing it */ + X = NewCodeEntry (OP65_STX, AM65_ZP, D->ZPHi, 0, D->PushEntry->LI); + InsertEntry (D, X, D->PushIndex+1); + if ((D->Flags & OP_DIRECT) == 0) { + X = NewCodeEntry (OP65_STA, AM65_ZP, D->ZPLo, 0, D->PushEntry->LI); + InsertEntry (D, X, D->PushIndex+1); + } +} + + + +static void AddOpLow (StackOpData* D, opc_t OPC) +/* Add an op for the low byte of an operator. This function honours the + * OP_DIRECT and OP_RELOAD_Y flags and generates the necessary instructions. + * All code is inserted at the current insertion point. + */ +{ + CodeEntry* X; + + if ((D->Flags & OP_DIRECT) != 0) { + /* Op with a variable location. If the location is on the stack, we + * need to reload the Y register. + */ + if ((D->Flags & OP_RELOAD_Y) != 0) { + const char* Arg = MakeHexArg (D->PrevEntry->RI->In.RegY); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + } + X = NewCodeEntry (OPC, D->PrevEntry->AM, D->PrevEntry->Arg, 0, D->OpEntry->LI); + } else { + /* Op with temp storage */ + X = NewCodeEntry (OPC, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI); + } + InsertEntry (D, X, D->IP++); +} + + + +static void AddOpHigh (StackOpData* D, opc_t OPC) +/* Add an op for the high byte of an operator. Special cases (constant values + * or similar have to be checked separately, the function covers only the + * generic case. Code is inserted at the insertion point. + */ +{ + CodeEntry* X; + + /* High byte is unknown */ + X = NewCodeEntry (OP65_STA, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + X = NewCodeEntry (OPC, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + X = NewCodeEntry (OP65_LDA, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); +} + + + +static void RemovePushAndOp (StackOpData* D) +/* Remove the call to pushax and the call to the operator subroutine */ +{ + DelEntry (D, D->OpIndex); + DelEntry (D, D->PushIndex); +} + + + +static const char* IsRegVar (const StackOpData* D) +/* If the value pushed is that of a register variable, return the name of the + * entry in the register bank. Otherwise return NULL. + */ +{ + CodeEntry* P; + + if (D->PushIndex >= 2 && + (P = D->PrevEntry) != 0 && + P->OPC == OP65_LDX && + P->AM == AM65_ZP && + strncmp (P->Arg, "regbank+", 7) == 0 && + isdigit (P->Arg[8]) && + (P = CS_GetEntry (D->Code, D->PushIndex-2)) != 0 && + P->OPC == OP65_LDA && + P->AM == AM65_ZP && + strncmp (P->Arg, "regbank+", 7) == 0 && + isdigit (P->Arg[8])) { + /* Ok, it loads the register variable */ + return P->Arg; + } else { + return 0; + } +} + + + /*****************************************************************************/ -/* Actual optimization functions */ +/* Actual optimization functions */ /*****************************************************************************/ -static unsigned Opt_staspidx (CodeSeg* S, unsigned Push, unsigned Store, - const char* ZPLo, const char* ZPHi) +static unsigned Opt_staspidx (StackOpData* D) /* Optimize the staspidx sequence if possible */ { CodeEntry* X; - CodeEntry* PushEntry; - CodeEntry* StoreEntry; + const char* ZPLo; - /* Get the push entry */ - PushEntry = CS_GetEntry (S, Push); + /* Check if we're using a register variable */ + if ((ZPLo = IsRegVar (D)) == 0) { - /* Store the value into the zeropage instead of pushing it */ - X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI); - CS_InsertEntry (S, X, Push+1); - X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI); - CS_InsertEntry (S, X, Push+2); + /* Store the value into the zeropage instead of pushing it */ + ReplacePushByStore (D); - /* Correct the index of the store and get a pointer to the entry */ - Store += 2; - StoreEntry = CS_GetEntry (S, Store); + /* Use the given zero page loc */ + ZPLo = D->ZPLo; + } - /* Inline the store */ - X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, StoreEntry->LI); - CS_InsertEntry (S, X, Store+1); + /* Replace the store subroutine call by a direct op */ + X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, D->OpEntry->LI); + InsertEntry (D, X, D->OpIndex+1); /* Remove the push and the call to the staspidx function */ - CS_DelEntry (S, Store); - CS_DelEntry (S, Push); + RemovePushAndOp (D); /* We changed the sequence */ return 1; @@ -157,101 +364,107 @@ static unsigned Opt_staspidx (CodeSeg* S, unsigned Push, unsigned Store, -static unsigned Opt_tosaddax (CodeSeg* S, unsigned Push, unsigned Add, - const char* ZPLo, const char* ZPHi) -/* Optimize the tosaddax sequence if possible */ +static unsigned Opt_staxspidx (StackOpData* D) +/* Optimize the staxspidx sequence if possible */ { - CodeEntry* P; - CodeEntry* N; CodeEntry* X; - CodeEntry* PushEntry; - CodeEntry* AddEntry; - int DirectAdd; + const char* ZPLo; + /* Check if we're using a register variable */ + if ((ZPLo = IsRegVar (D)) == 0) { - /* We need the entry behind the add */ - CHECK ((N = CS_GetNextEntry (S, Add)) != 0); + /* Store the value into the zeropage instead of pushing it */ + ReplacePushByStore (D); + + /* Use the given zero page loc */ + ZPLo = D->ZPLo; + } + + /* Inline the store */ + X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, D->OpEntry->LI); + InsertEntry (D, X, D->OpIndex+1); + X = NewCodeEntry (OP65_INY, AM65_IMP, 0, 0, D->OpEntry->LI); + InsertEntry (D, X, D->OpIndex+2); + if (RegValIsKnown (D->OpEntry->RI->In.RegX)) { + /* Value of X is known */ + const char* Arg = MakeHexArg (D->OpEntry->RI->In.RegX); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, D->OpEntry->LI); + } else { + /* Value unknown */ + X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, D->OpEntry->LI); + } + InsertEntry (D, X, D->OpIndex+3); + X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, D->OpEntry->LI); + InsertEntry (D, X, D->OpIndex+4); + + /* Remove the push and the call to the staspidx function */ + RemovePushAndOp (D); + + /* We changed the sequence */ + return 1; +} + + + +static unsigned Opt_tosaddax (StackOpData* D) +/* Optimize the tosaddax sequence if possible */ +{ + CodeEntry* X; - /* And the entry before the push */ - CHECK ((P = CS_GetPrevEntry (S, Push)) != 0); - /* Get the push entry */ - PushEntry = CS_GetEntry (S, Push); + /* We need the entry behind the add */ + CHECK (D->NextEntry != 0); - /* Check the entry before the push, if it's a lda instruction with an - * addressing mode that does not use an additional index register. If - * so, we may use this location for the add and must not save the - * value in the zero page location. + /* Check the entry before the push. If it's a lda instruction with an + * addressing mode that allows us to replace it, we may use this + * location for the op and must not save the value in the zero page + * location. */ - DirectAdd = (P->OPC == OP65_LDA && - (P->AM == AM65_IMM || P->AM == AM65_ZP || P->AM == AM65_ABS)); + CheckDirectOp (D); /* Store the value into the zeropage instead of pushing it */ - X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI); - CS_InsertEntry (S, X, Push+1); - ++Add; /* Correct the index */ - if (!DirectAdd) { - X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI); - CS_InsertEntry (S, X, Push+1); - ++Add; /* Correct the index */ - } - - /* Get a pointer to the add entry */ - AddEntry = CS_GetEntry (S, Add); + ReplacePushByStore (D); /* Inline the add */ - X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, AddEntry->LI); - CS_InsertEntry (S, X, Add+1); - if (DirectAdd) { - /* Add from variable location */ - X = NewCodeEntry (OP65_ADC, P->AM, P->Arg, 0, AddEntry->LI); - } else { - /* Add from temp storage */ - X = NewCodeEntry (OP65_ADC, AM65_ZP, ZPLo, 0, AddEntry->LI); - } - CS_InsertEntry (S, X, Add+2); - if (PushEntry->RI->In.RegX == 0) { + D->IP = D->OpIndex+1; + X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + /* Low byte */ + AddOpLow (D, OP65_ADC); + + /* High byte */ + if (D->PushEntry->RI->In.RegX == 0) { /* The high byte is the value in X plus the carry */ - CodeLabel* L = CS_GenLabel (S, N); - X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, AddEntry->LI); - CS_InsertEntry (S, X, Add+3); - X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, AddEntry->LI); - CS_InsertEntry (S, X, Add+4); - } else if (AddEntry->RI->In.RegX == 0) { - /* The high byte is that of the first operand plus carry */ + CodeLabel* L = CS_GenLabel (D->Code, D->NextEntry); + X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + } else if (D->OpEntry->RI->In.RegX == 0) { + /* The high byte is that of the first operand plus carry */ CodeLabel* L; - if (PushEntry->RI->In.RegX >= 0) { + if (RegValIsKnown (D->PushEntry->RI->In.RegX)) { /* Value of first op high byte is known */ - char Buf [16]; - xsprintf (Buf, sizeof (Buf), "$%02X", PushEntry->RI->In.RegX); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Buf, 0, AddEntry->LI); + const char* Arg = MakeHexArg (D->PushEntry->RI->In.RegX); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, D->OpEntry->LI); } else { /* Value of first op high byte is unknown */ - X = NewCodeEntry (OP65_LDX, AM65_ZP, ZPHi, 0, AddEntry->LI); + X = NewCodeEntry (OP65_LDX, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI); } - CS_InsertEntry (S, X, Add+3); - L = CS_GenLabel (S, N); - X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, AddEntry->LI); - CS_InsertEntry (S, X, Add+4); - X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, AddEntry->LI); - CS_InsertEntry (S, X, Add+5); + InsertEntry (D, X, D->IP++); + L = CS_GenLabel (D->Code, D->NextEntry); + X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); } else { /* High byte is unknown */ - X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, AddEntry->LI); - CS_InsertEntry (S, X, Add+3); - X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, AddEntry->LI); - CS_InsertEntry (S, X, Add+4); - X = NewCodeEntry (OP65_ADC, AM65_ZP, ZPHi, 0, AddEntry->LI); - CS_InsertEntry (S, X, Add+5); - X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, AddEntry->LI); - CS_InsertEntry (S, X, Add+6); - X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, AddEntry->LI); - CS_InsertEntry (S, X, Add+7); + AddOpHigh (D, OP65_ADC); } /* Remove the push and the call to the tosaddax function */ - CS_DelEntry (S, Add); - CS_DelEntry (S, Push); + RemovePushAndOp (D); /* We changed the sequence */ return 1; @@ -259,73 +472,37 @@ static unsigned Opt_tosaddax (CodeSeg* S, unsigned Push, unsigned Add, -static unsigned Opt_tosandax (CodeSeg* S, unsigned Push, unsigned And, - const char* ZPLo, const char* ZPHi) +static unsigned Opt_tosandax (StackOpData* D) /* Optimize the tosandax sequence if possible */ { - CodeEntry* P; - CodeEntry* X; - CodeEntry* PushEntry; - CodeEntry* AndEntry; - int DirectAnd; - - /* Get the entry before the push */ - CHECK ((P = CS_GetPrevEntry (S, Push)) != 0); + CodeEntry* X; - /* Get the push entry */ - PushEntry = CS_GetEntry (S, Push); - - /* Check the entry before the push, if it's a lda instruction with an - * addressing mode that does not use an additional index register. If - * so, we may use this location for the and and must not save the - * value in the zero page location. + /* Check the entry before the push. If it's a lda instruction with an + * addressing mode that allows us to replace it, we may use this + * location for the op and must not save the value in the zero page + * location. */ - DirectAnd = (P->OPC == OP65_LDA && - (P->AM == AM65_IMM || P->AM == AM65_ZP || P->AM == AM65_ABS)); + CheckDirectOp (D); /* Store the value into the zeropage instead of pushing it */ - X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI); - CS_InsertEntry (S, X, Push+1); - ++And; /* Correct the index */ - if (!DirectAnd) { - X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI); - CS_InsertEntry (S, X, Push+1); - ++And; /* Correct the index */ - } + ReplacePushByStore (D); - /* Get a pointer to the and entry */ - AndEntry = CS_GetEntry (S, And); + /* Inline the and, low byte */ + D->IP = D->OpIndex + 1; + AddOpLow (D, OP65_AND); - /* Inline the and */ - if (DirectAnd) { - /* And with variable location */ - X = NewCodeEntry (OP65_AND, P->AM, P->Arg, 0, AndEntry->LI); - } else { - /* And with temp storage */ - X = NewCodeEntry (OP65_AND, AM65_ZP, ZPLo, 0, AndEntry->LI); - } - CS_InsertEntry (S, X, And+1); - if (PushEntry->RI->In.RegX == 0 || AndEntry->RI->In.RegX == 0) { + /* High byte */ + if (D->PushEntry->RI->In.RegX == 0 || D->OpEntry->RI->In.RegX == 0) { /* The high byte is zero */ - X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, AndEntry->LI); - CS_InsertEntry (S, X, And+2); + X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); } else { /* High byte is unknown */ - X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, AndEntry->LI); - CS_InsertEntry (S, X, And+2); - X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, AndEntry->LI); - CS_InsertEntry (S, X, And+3); - X = NewCodeEntry (OP65_AND, AM65_ZP, ZPHi, 0, AndEntry->LI); - CS_InsertEntry (S, X, And+4); - X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, AndEntry->LI); - CS_InsertEntry (S, X, And+5); - X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, AndEntry->LI); - CS_InsertEntry (S, X, And+6); + AddOpHigh (D, OP65_AND); } /* Remove the push and the call to the tosandax function */ - CS_DelEntry (S, And); - CS_DelEntry (S, Push); + RemovePushAndOp (D); /* We changed the sequence */ return 1; @@ -333,75 +510,39 @@ static unsigned Opt_tosandax (CodeSeg* S, unsigned Push, unsigned And, -static unsigned Opt_tosorax (CodeSeg* S, unsigned Push, unsigned Or, - const char* ZPLo, const char* ZPHi) +static unsigned Opt_tosorax (StackOpData* D) /* Optimize the tosorax sequence if possible */ { - CodeEntry* P; - CodeEntry* X; - CodeEntry* PushEntry; - CodeEntry* OrEntry; - int DirectOr; - - /* Get the entry before the push */ - CHECK ((P = CS_GetPrevEntry (S, Push)) != 0); - - /* Get the push entry */ - PushEntry = CS_GetEntry (S, Push); + CodeEntry* X; - /* Check the entry before the push, if it's a lda instruction with an - * addressing mode that does not use an additional index register. If - * so, we may use this location for the or and must not save the - * value in the zero page location. + /* Check the entry before the push. If it's a lda instruction with an + * addressing mode that allows us to replace it, we may use this + * location for the op and must not save the value in the zero page + * location. */ - DirectOr = (P->OPC == OP65_LDA && - (P->AM == AM65_IMM || P->AM == AM65_ZP || P->AM == AM65_ABS)); + CheckDirectOp (D); /* Store the value into the zeropage instead of pushing it */ - X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI); - CS_InsertEntry (S, X, Push+1); - ++Or; /* Correct the index */ - if (DirectOr) { - X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI); - CS_InsertEntry (S, X, Push+1); - ++Or; /* Correct the index */ - } + ReplacePushByStore (D); - /* Get a pointer to the or entry */ - OrEntry = CS_GetEntry (S, Or); + /* Inline the or, low byte */ + D->IP = D->OpIndex + 1; + AddOpLow (D, OP65_ORA); - /* Inline the or */ - if (DirectOr) { - /* Or with variable location */ - X = NewCodeEntry (OP65_ORA, P->AM, P->Arg, 0, OrEntry->LI); - } else { - X = NewCodeEntry (OP65_ORA, AM65_ZP, ZPLo, 0, OrEntry->LI); - } - CS_InsertEntry (S, X, Or+1); - if (PushEntry->RI->In.RegX >= 0 && OrEntry->RI->In.RegX >= 0) { + /* High byte */ + if (RegValIsKnown (D->PushEntry->RI->In.RegX) && + RegValIsKnown (D->OpEntry->RI->In.RegX)) { /* Both values known, precalculate the result */ - char Buf [16]; - int Val = (PushEntry->RI->In.RegX | OrEntry->RI->In.RegX); - xsprintf (Buf, sizeof (Buf), "$%02X", Val); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Buf, 0, OrEntry->LI); - CS_InsertEntry (S, X, Or+2); - } else if (PushEntry->RI->In.RegX != 0) { + const char* Arg = MakeHexArg (D->PushEntry->RI->In.RegX | D->OpEntry->RI->In.RegX); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + } else if (D->PushEntry->RI->In.RegX != 0) { /* High byte is unknown */ - X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, OrEntry->LI); - CS_InsertEntry (S, X, Or+2); - X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, OrEntry->LI); - CS_InsertEntry (S, X, Or+3); - X = NewCodeEntry (OP65_ORA, AM65_ZP, ZPHi, 0, OrEntry->LI); - CS_InsertEntry (S, X, Or+4); - X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, OrEntry->LI); - CS_InsertEntry (S, X, Or+5); - X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, OrEntry->LI); - CS_InsertEntry (S, X, Or+6); + AddOpHigh (D, OP65_ORA); } - /* Remove the push and the call to the tosandax function */ - CS_DelEntry (S, Or); - CS_DelEntry (S, Push); + /* Remove the push and the call to the tosorax function */ + RemovePushAndOp (D); /* We changed the sequence */ return 1; @@ -409,76 +550,39 @@ static unsigned Opt_tosorax (CodeSeg* S, unsigned Push, unsigned Or, -static unsigned Opt_tosxorax (CodeSeg* S, unsigned Push, unsigned Xor, - const char* ZPLo, const char* ZPHi) +static unsigned Opt_tosxorax (StackOpData* D) /* Optimize the tosxorax sequence if possible */ { - CodeEntry* P; - CodeEntry* X; - CodeEntry* PushEntry; - CodeEntry* XorEntry; - int DirectXor; - - /* Get the entry before the push */ - CHECK ((P = CS_GetPrevEntry (S, Push)) != 0); + CodeEntry* X; - /* Get the push entry */ - PushEntry = CS_GetEntry (S, Push); - - /* Check the entry before the push, if it's a lda instruction with an - * addressing mode that does not use an additional index register. If - * so, we may use this location for the xor and must not save the - * value in the zero page location. + /* Check the entry before the push. If it's a lda instruction with an + * addressing mode that allows us to replace it, we may use this + * location for the op and must not save the value in the zero page + * location. */ - DirectXor = (P->OPC == OP65_LDA && - (P->AM == AM65_IMM || P->AM == AM65_ZP || P->AM == AM65_ABS)); + CheckDirectOp (D); /* Store the value into the zeropage instead of pushing it */ - X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI); - CS_InsertEntry (S, X, Push+1); - ++Xor; /* Correct the index */ - if (DirectXor) { - X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI); - CS_InsertEntry (S, X, Push+1); - ++Xor; /* Correct the index */ - } + ReplacePushByStore (D); - /* Get a pointer to the entry */ - XorEntry = CS_GetEntry (S, Xor); + /* Inline the xor, low byte */ + D->IP = D->OpIndex + 1; + AddOpLow (D, OP65_EOR); - /* Inline the xor */ - if (DirectXor) { - /* Xor with variable location */ - X = NewCodeEntry (OP65_EOR, P->AM, P->Arg, 0, XorEntry->LI); - } else { - /* Xor with temp storage */ - X = NewCodeEntry (OP65_EOR, AM65_ZP, ZPLo, 0, XorEntry->LI); - } - CS_InsertEntry (S, X, Xor+1); - if (PushEntry->RI->In.RegX >= 0 && XorEntry->RI->In.RegX >= 0) { + /* High byte */ + if (RegValIsKnown (D->PushEntry->RI->In.RegX) && + RegValIsKnown (D->OpEntry->RI->In.RegX)) { /* Both values known, precalculate the result */ - char Buf [16]; - int Val = (PushEntry->RI->In.RegX ^ XorEntry->RI->In.RegX); - xsprintf (Buf, sizeof (Buf), "$%02X", Val); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Buf, 0, XorEntry->LI); - CS_InsertEntry (S, X, Xor+2); - } else if (PushEntry->RI->In.RegX != 0) { + const char* Arg = MakeHexArg (D->PushEntry->RI->In.RegX ^ D->OpEntry->RI->In.RegX); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + } else if (D->PushEntry->RI->In.RegX != 0) { /* High byte is unknown */ - X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, XorEntry->LI); - CS_InsertEntry (S, X, Xor+2); - X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, XorEntry->LI); - CS_InsertEntry (S, X, Xor+3); - X = NewCodeEntry (OP65_EOR, AM65_ZP, ZPHi, 0, XorEntry->LI); - CS_InsertEntry (S, X, Xor+4); - X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, XorEntry->LI); - CS_InsertEntry (S, X, Xor+5); - X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, XorEntry->LI); - CS_InsertEntry (S, X, Xor+6); + AddOpHigh (D, OP65_EOR); } /* Remove the push and the call to the tosandax function */ - CS_DelEntry (S, Xor); - CS_DelEntry (S, Push); + RemovePushAndOp (D); /* We changed the sequence */ return 1; @@ -492,20 +596,28 @@ static unsigned Opt_tosxorax (CodeSeg* S, unsigned Push, unsigned Xor, -typedef unsigned (*OptFunc) (CodeSeg* S, unsigned Push, unsigned Store, - const char* ZPLo, const char* ZPHi); +/* Flags for the functions */ +typedef enum { + STOP_NONE, /* Nothing special */ + STOP_A_UNUSED /* Call only if a unused later */ +} 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 */ + const char* Name; /* Name of the replaced runtime function */ + OptFunc Func; /* Function pointer */ + STOP_FLAGS Flags; /* Flags */ }; static const OptFuncDesc FuncTable[] = { - { "staspidx", Opt_staspidx }, - { "tosaddax", Opt_tosaddax }, - { "tosandax", Opt_tosandax }, - { "tosorax", Opt_tosorax }, - { "tosxorax", Opt_tosxorax }, + { "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 }, + { "tosxorax", Opt_tosxorax, STOP_NONE }, }; #define FUNC_COUNT (sizeof(FuncTable) / sizeof(FuncTable[0])) @@ -529,6 +641,35 @@ static const OptFuncDesc* FindFunc (const char* Name) +static int CmpHarmless (const void* Key, const void* Entry) +/* Compare function for bsearch */ +{ + return strcmp (Key, *(const char**)Entry); +} + + + +static int HarmlessCall (const char* Name) +/* Check if this is a call to a harmless subroutine that will not interrupt + * the pushax/op sequence when encountered. + */ +{ + static const char* Tab[] = { + "ldaxidx", + "ldaxysp", + "negax", + }; + + void* R = bsearch (Name, + Tab, + sizeof (Tab) / sizeof (Tab[0]), + sizeof (Tab[0]), + CmpHarmless); + return (R != 0); +} + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ @@ -538,11 +679,12 @@ static const OptFuncDesc* FindFunc (const char* Name) 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 */ - unsigned I; + 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 */ + unsigned I; + /* Generate register info */ CS_GenRegInfo (S); @@ -553,8 +695,7 @@ unsigned OptStackOps (CodeSeg* S) * It depends on the code between the two if we can handle/transform the * sequence, so check this code for the following list of things: * - * - there must not be a jump or conditional branch (this may - * get relaxed later). + * - the range must be a basic block (one entry, one exit) * - there may not be accesses to local variables with unknown * offsets (because we have to adjust these offsets). * - no subroutine calls @@ -572,10 +713,16 @@ unsigned OptStackOps (CodeSeg* S) /* Handling depends if we're inside a sequence or not */ if (InSeq) { - if ((E->Info & OF_BRA) != 0 || - ((E->Use & REG_SP) != 0 && - (E->AM != AM65_ZP_INDY || E->RI->In.RegY < 0)) || - CE_HasLabel (E)) { + /* 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. + */ + 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; @@ -586,37 +733,62 @@ unsigned OptStackOps (CodeSeg* S) const OptFuncDesc* F = FindFunc (E->Arg); if (F) { - /* Determine the register to use */ - const char* ZPLo; - const char* ZPHi; - UsedRegs |= GetRegInfo (S, I+1, REG_SREG | REG_PTR1 | REG_PTR2); - if ((UsedRegs & REG_SREG) == REG_NONE) { - /* SREG is available */ - ZPLo = "sreg"; - ZPHi = "sreg+1"; - } else if ((UsedRegs & REG_PTR1) == REG_NONE) { - ZPLo = "ptr1"; - ZPHi = "ptr1+1"; - } else if ((UsedRegs & REG_PTR2) == REG_NONE) { - ZPLo = "ptr2"; - ZPHi = "ptr2+1"; - } else { - /* No registers available */ - ZPLo = 0; - ZPHi = 0; - } - - /* If we have a register, call the optimizer function */ - if (ZPLo && ZPHi) { + StackOpData Data; + int PreCondOk = 1; + + /* Check the flags */ + if (F->Flags & STOP_A_UNUSED) { + /* a must be unused later */ + if (RegAUsed (S, I+1)) { + /* 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 */ - unsigned Op = I + AdjustStackOffset (S, Push, I, 2); - - /* Call the optimizer function */ - Changes += F->Func (S, Push, Op, ZPLo, ZPHi); - - /* Regenerate register info */ - CS_GenRegInfo (S); + 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 */ @@ -626,19 +798,20 @@ unsigned OptStackOps (CodeSeg* S) /* Restart the sequence */ Push = I; UsedRegs = REG_NONE; - } else { + } 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; } } else { - /* Other stuff: Track zeropage register usage */ UsedRegs |= (E->Use | E->Chg); - } - } else if (CE_IsCall (E, "pushax")) { + } else if (CE_IsCallTo (E, "pushax")) { /* This starts a sequence */ Push = I;