X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fcc65%2Fcoptstop.c;h=4f83b8ef5b64de7d7965b165c56d494eb299d391;hb=73dfa23c987d8a7f1154801b85c171f9e01dcd58;hp=6123b945abb19c4c9af7eadceba8df0b92062f4b;hpb=b08adf831dbaf418a178697dd7a1080873e2da66;p=cc65 diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index 6123b945a..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,59 +34,371 @@ #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) +/* 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. + */ +{ + /* Number of inserted instructions */ + unsigned Inserted = 0; + + /* Walk over all entries */ + unsigned I = Start; + while (I <= Stop) { + + CodeEntry* E = CS_GetEntry (S, I); + + 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. + */ + P = CS_GetPrevEntry (S, I); + if (P && P->OPC == OP65_LDY && CE_KnownImm (P)) { + + /* The Y load is just before the stack access, adjust it */ + CE_SetNumArg (P, P->Num - Offs); + + } else { + + /* Insert a new load instruction before the stack access */ + 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 */ + ++Inserted; + ++Stop; + + /* Be sure to skip the stack access for the next round */ + ++I; + + } + + } + + /* Next entry */ + ++I; + } + + /* Return the number of inserted entries */ + return Inserted; +} + + + +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 */ /*****************************************************************************/ -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; - /* Generate register info */ - CS_GenRegInfo (S); + /* Check if we're using a register variable */ + if ((ZPLo = IsRegVar (D)) == 0) { - /* Get the push entry */ - PushEntry = CS_GetEntry (S, Push); + /* Store the value into the zeropage instead of pushing it */ + ReplacePushByStore (D); - /* 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); + /* Use the given zero page loc */ + ZPLo = D->ZPLo; + } + + /* 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 */ + RemovePushAndOp (D); + + /* We changed the sequence */ + return 1; +} + + + +static unsigned Opt_staxspidx (StackOpData* D) +/* Optimize the staxspidx sequence if possible */ +{ + CodeEntry* X; + const char* ZPLo; + + /* Check if we're using a register variable */ + if ((ZPLo = IsRegVar (D)) == 0) { - /* Correct the index of the store and get a pointer to the entry */ - Store += 2; - StoreEntry = CS_GetEntry (S, Store); + /* 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, StoreEntry->LI); - CS_InsertEntry (S, X, Store+1); + 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 */ - CS_DelEntry (S, Store); - CS_DelEntry (S, Push); - - /* Free the register info */ - CS_FreeRegInfo (S); + RemovePushAndOp (D); /* We changed the sequence */ return 1; @@ -94,69 +406,65 @@ 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) +static unsigned Opt_tosaddax (StackOpData* D) /* Optimize the tosaddax sequence if possible */ { - CodeEntry* N; - CodeEntry* X; - CodeEntry* PushEntry; - CodeEntry* AddEntry; + CodeEntry* X; - /* We need the entry behind the add */ - if ((N = CS_GetNextEntry (S, Add)) == 0) { - /* Unavailable */ - return 0; - } - /* Generate register info */ - CS_GenRegInfo (S); + /* We need the entry behind the add */ + CHECK (D->NextEntry != 0); - /* 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 allows us to replace it, we may use this + * location for the op and must not save the value in the zero page + * location. + */ + CheckDirectOp (D); /* 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); - - /* Correct the index of the add and get a pointer to the entry */ - Add += 2; - 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); - X = NewCodeEntry (OP65_ADC, AM65_ZP, ZPLo, 0, AddEntry->LI); - CS_InsertEntry (S, X, Add+2); - if (PushEntry->RI->In.RegX == 0 && AddEntry->RI->In.RegX == 0) { - /* The high byte is zero on entry */ - 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); + 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 (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 (RegValIsKnown (D->PushEntry->RI->In.RegX)) { + /* Value of first op high byte is known */ + 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, D->ZPHi, 0, D->OpEntry->LI); + } + 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); - - /* Free the register info */ - CS_FreeRegInfo (S); + RemovePushAndOp (D); /* We changed the sequence */ return 1; @@ -164,57 +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* X; - CodeEntry* PushEntry; - CodeEntry* AndEntry; - - /* Generate register info */ - CS_GenRegInfo (S); + 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 allows us to replace it, we may use this + * location for the op and must not save the value in the zero page + * location. + */ + CheckDirectOp (D); /* 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); - - /* Correct the index of the add and get a pointer to the entry */ - And += 2; - AndEntry = CS_GetEntry (S, And); - - /* Inline the and */ - 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) { + ReplacePushByStore (D); + + /* Inline the and, low byte */ + D->IP = D->OpIndex + 1; + AddOpLow (D, OP65_AND); + + /* 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); - - /* Free the register info */ - CS_FreeRegInfo (S); + RemovePushAndOp (D); /* We changed the sequence */ return 1; @@ -222,59 +510,79 @@ 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* X; - CodeEntry* PushEntry; - CodeEntry* OrEntry; + CodeEntry* X; - /* Generate register info */ - CS_GenRegInfo (S); + /* 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. + */ + CheckDirectOp (D); + + /* Store the value into the zeropage instead of pushing it */ + ReplacePushByStore (D); + + /* Inline the or, low byte */ + D->IP = D->OpIndex + 1; + AddOpLow (D, OP65_ORA); + + /* High byte */ + if (RegValIsKnown (D->PushEntry->RI->In.RegX) && + RegValIsKnown (D->OpEntry->RI->In.RegX)) { + /* Both values known, precalculate the result */ + 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 */ + AddOpHigh (D, OP65_ORA); + } - /* Get the push entry */ - PushEntry = CS_GetEntry (S, Push); + /* Remove the push and the call to the tosorax function */ + RemovePushAndOp (D); + + /* We changed the sequence */ + return 1; +} + + + +static unsigned Opt_tosxorax (StackOpData* D) +/* Optimize the tosxorax sequence if possible */ +{ + CodeEntry* X; + + /* 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. + */ + CheckDirectOp (D); /* 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); - - /* Correct the index of the add and get a pointer to the entry */ - Or += 2; - OrEntry = CS_GetEntry (S, Or); - - /* Inline the or */ - 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) { - /* Value of X will be that of the first operand */ - char Buf [16]; - xsprintf (Buf, sizeof (Buf), "$%02X", PushEntry->RI->In.RegX); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Buf, 0, OrEntry->LI); - CS_InsertEntry (S, X, Or+2); - } else if (PushEntry->RI->In.RegX != 0) { + ReplacePushByStore (D); + + /* Inline the xor, low byte */ + D->IP = D->OpIndex + 1; + AddOpLow (D, OP65_EOR); + + /* High byte */ + if (RegValIsKnown (D->PushEntry->RI->In.RegX) && + RegValIsKnown (D->OpEntry->RI->In.RegX)) { + /* Both values known, precalculate the result */ + 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_EOR); } /* Remove the push and the call to the tosandax function */ - CS_DelEntry (S, Or); - CS_DelEntry (S, Push); - - /* Free the register info */ - CS_FreeRegInfo (S); + RemovePushAndOp (D); /* We changed the sequence */ return 1; @@ -283,24 +591,33 @@ static unsigned Opt_tosorax (CodeSeg* S, unsigned Push, unsigned Or, /*****************************************************************************/ -/* Code */ +/* Code */ /*****************************************************************************/ -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 }, + { "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])) @@ -324,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 */ /*****************************************************************************/ @@ -333,11 +679,15 @@ 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 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); /* Look for a call to pushax followed by a call to some other function * that takes it's first argument on the stack, and the second argument @@ -345,17 +695,16 @@ 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). - * - there may not be accesses to local variables (may also be - * 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 * - no jump labels * * Since we need a zero page register later, do also check the * intermediate code for zero page use. */ - unsigned I = 0; + I = 0; while (I < CS_GetEntryCount (S)) { /* Get the next entry */ @@ -364,65 +713,105 @@ unsigned OptStackOps (CodeSeg* S) /* Handling depends if we're inside a sequence or not */ if (InSeq) { - /* Subroutine call? */ - if (E->OPC == OP65_JSR) { - - /* Check if this is one of our functions */ - 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 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; + + } else if (E->OPC == OP65_JSR) { + + /* Subroutine call: Check if this is one of our functions */ + const OptFuncDesc* F = FindFunc (E->Arg); + if (F) { + + StackOpData Data; + int PreCondOk = 1; + + /* Check the flags */ + if (F->Flags & STOP_A_UNUSED) { + /* a must be unused later */ + if (RegAUsed (S, I+1)) { + /* Cannot optimize */ + PreCondOk = 0; + } } - /* If we have a register, call the optimizer function */ - if (ZPLo && ZPHi) { - Changes += F->Func (S, Push, I, ZPLo, ZPHi); + /* 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; + } } - /* End of sequence */ - InSeq = 0; - - } else if (strcmp (E->Arg, "pushax") == 0) { - /* Restart the sequence */ - Push = I; - UsedRegs = REG_NONE; - } else { - /* A call to an unkown subroutine ends the sequence */ - InSeq = 0; - } - - } else if ((E->Info & OF_BRA) != 0 || - (E->Use & REG_SP) != 0 || - CE_HasLabel (E)) { - - /* All this stuff is not allowed in a sequence */ - InSeq = 0; + /* Determine if we have a basic block */ + if (PreCondOk) { + PreCondOk = CS_IsBasicBlock (S, Push, I); + } + + /* If preconditions are ok, call the optimizer function */ + if (PreCondOk) { + + /* Adjust stack offsets */ + Data.OpIndex = I + AdjustStackOffset (S, Push, I, 2); + + /* Prepare the remainder of the data structure */ + Data.Code = S; + Data.Flags = 0; + Data.PushIndex = Push; + Data.PrevEntry = CS_GetPrevEntry (S, Data.PushIndex); + Data.PushEntry = CS_GetEntry (S, Data.PushIndex); + Data.OpEntry = E; + Data.NextEntry = CS_GetNextEntry (S, Data.OpIndex); + + /* Call the optimizer function */ + Changes += F->Func (&Data); + + /* Regenerate register info */ + CS_GenRegInfo (S); + } + + /* End of sequence */ + InSeq = 0; + + } else if (strcmp (E->Arg, "pushax") == 0) { + /* Restart the sequence */ + Push = I; + UsedRegs = REG_NONE; + } else if (HarmlessCall (E->Arg)) { + /* Track zeropage register usage */ + UsedRegs |= (E->Use | E->Chg); + } else { + /* A call to an unkown subroutine ends the sequence */ + InSeq = 0; + } } else { - - /* Other stuff: Track zeropage register usage */ - UsedRegs |= (E->Use | E->Chg); - + /* 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; @@ -436,6 +825,9 @@ unsigned OptStackOps (CodeSeg* S) } + /* Free the register info */ + CS_FreeRegInfo (S); + /* Return the number of changes made */ return Changes; }