From: cuz Date: Mon, 24 Sep 2001 22:36:35 +0000 (+0000) Subject: Track usage of the sreg and several other zero page registers and remove X-Git-Tag: V2.12.0~2619 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=f4fbbc3dccb1353242a6ab69d6c5aa92e71f6673;p=cc65 Track usage of the sreg and several other zero page registers and remove unused stores into these registers. Because of this, the old code using regsave does no longer work. Started to rewrite it. git-svn-id: svn://svn.cc65.org/cc65/trunk@966 b7a2c559-68d2-44c3-8de9-860c34a00d81 --- diff --git a/src/cc65/codeent.c b/src/cc65/codeent.c index 6b487adba..e37d079f5 100644 --- a/src/cc65/codeent.c +++ b/src/cc65/codeent.c @@ -412,6 +412,12 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) case OP65_ASL: if (E->AM == AM65_ACC && In->RegA >= 0) { Out->RegA = (In->RegA << 1) & 0xFF; + } else if (E->AM == AM65_ZP) { + if ((E->Chg & REG_SREG_LO) != 0 && In->SRegLo >= 0) { + Out->SRegLo = (In->SRegLo << 1) & 0xFF; + } else if ((E->Chg & REG_SREG_HI) != 0 && In->SRegHi >= 0) { + Out->SRegHi = (In->SRegHi << 1) & 0xFF; + } } break; @@ -471,25 +477,31 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) case OP65_DEA: if (In->RegA >= 0) { - Out->RegA = In->RegA - 1; + Out->RegA = (In->RegA - 1) & 0xFF; } break; case OP65_DEC: if (E->AM == AM65_ACC && In->RegA >= 0) { - Out->RegA = In->RegA - 1; + Out->RegA = (In->RegA - 1) & 0xFF; + } else if (E->AM == AM65_ZP) { + if ((E->Chg & REG_SREG_LO) != 0 && In->SRegLo >= 0) { + Out->SRegLo = (In->SRegLo - 1) & 0xFF; + } else if ((E->Chg & REG_SREG_HI) != 0 && In->SRegHi >= 0) { + Out->SRegHi = (In->SRegHi - 1) & 0xFF; + } } break; case OP65_DEX: if (In->RegX >= 0) { - Out->RegX = In->RegX - 1; + Out->RegX = (In->RegX - 1) & 0xFF; } break; case OP65_DEY: if (In->RegY >= 0) { - Out->RegY = In->RegY - 1; + Out->RegY = (In->RegY - 1) & 0xFF; } break; @@ -505,25 +517,31 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) case OP65_INA: if (In->RegA >= 0) { - Out->RegA = In->RegA + 1; + Out->RegA = (In->RegA + 1) & 0xFF; } break; case OP65_INC: if (E->AM == AM65_ACC && In->RegA >= 0) { - Out->RegA = In->RegA + 1; + Out->RegA = (In->RegA + 1) & 0xFF; + } else if (E->AM == AM65_ZP) { + if ((E->Chg & REG_SREG_LO) != 0 && In->SRegLo >= 0) { + Out->SRegLo = (In->SRegLo + 1) & 0xFF; + } else if ((E->Chg & REG_SREG_HI) != 0 && In->SRegHi >= 0) { + Out->SRegHi = (In->SRegHi + 1) & 0xFF; + } } break; case OP65_INX: if (In->RegX >= 0) { - Out->RegX = In->RegX + 1; + Out->RegX = (In->RegX + 1) & 0xFF; } break; case OP65_INY: if (In->RegY >= 0) { - Out->RegY = In->RegY + 1; + Out->RegY = (In->RegY + 1) & 0xFF; } break; @@ -560,6 +578,12 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) if (Chg & REG_Y) { Out->RegY = -1; } + if (Chg & REG_SREG_LO) { + Out->SRegLo = -1; + } + if (Chg & REG_SREG_HI) { + Out->SRegHi = -1; + } break; case OP65_JVC: @@ -570,25 +594,49 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) case OP65_LDA: if (CE_KnownImm (E)) { - Out->RegA = (unsigned char) E->Num; + Out->RegA = (unsigned char) E->Num; + } else if (E->AM == AM65_ZP) { + if (E->Use & REG_SREG_LO) { + Out->RegA = In->SRegLo; + } else if (E->Use & REG_SREG_HI) { + Out->RegA = In->SRegHi; + } else { + Out->RegA = -1; + } } else { - /* A is now unknown */ - Out->RegA = -1; + /* A is now unknown */ + Out->RegA = -1; } break; case OP65_LDX: if (CE_KnownImm (E)) { - Out->RegX = (unsigned char) E->Num; + Out->RegX = (unsigned char) E->Num; + } else if (E->AM == AM65_ZP) { + if (E->Use & REG_SREG_LO) { + Out->RegX = In->SRegLo; + } else if (E->Use & REG_SREG_HI) { + Out->RegX = In->SRegHi; + } else { + Out->RegX = -1; + } } else { - /* X is now unknown */ - Out->RegX = -1; + /* X is now unknown */ + Out->RegX = -1; } break; case OP65_LDY: if (CE_KnownImm (E)) { - Out->RegY = (unsigned char) E->Num; + Out->RegY = (unsigned char) E->Num; + } else if (E->AM == AM65_ZP) { + if (E->Use & REG_SREG_LO) { + Out->RegY = In->SRegLo; + } else if (E->Use & REG_SREG_HI) { + Out->RegY = In->SRegHi; + } else { + Out->RegY = -1; + } } else { /* Y is now unknown */ Out->RegY = -1; @@ -598,6 +646,12 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) case OP65_LSR: if (E->AM == AM65_ACC && In->RegA >= 0) { Out->RegA = (In->RegA >> 1) & 0xFF; + } else if (E->AM == AM65_ZP) { + if ((E->Chg & REG_SREG_LO) != 0 && In->SRegLo >= 0) { + Out->SRegLo = (In->SRegLo >> 1) & 0xFF; + } else if (E->Chg & REG_SREG_HI) { + Out->SRegHi = (In->SRegHi >> 1) & 0xFF; + } } break; @@ -634,56 +688,93 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) case OP65_PLP: break; - case OP65_PLX: - Out->RegX = -1; - break; + case OP65_PLX: + Out->RegX = -1; + break; - case OP65_PLY: - Out->RegY = -1; - break; + case OP65_PLY: + Out->RegY = -1; + break; - case OP65_ROL: - Out->RegA = -1; - break; + case OP65_ROL: + if (E->AM == AM65_ACC) { + Out->RegA = -1; + } else if (E->AM == AM65_ZP) { + if (E->Chg & REG_SREG_LO) { + Out->SRegLo = -1; + } else if (E->Chg & REG_SREG_HI) { + Out->SRegHi = -1; + } + } + break; - case OP65_ROR: - Out->RegA = -1; - break; + case OP65_ROR: + if (E->AM == AM65_ACC) { + Out->RegA = -1; + } else if (E->AM == AM65_ZP) { + if (E->Chg & REG_SREG_LO) { + Out->SRegLo = -1; + } else if (E->Chg & REG_SREG_HI) { + Out->SRegHi = -1; + } + } + break; - case OP65_RTI: - break; + case OP65_RTI: + break; - case OP65_RTS: - break; + case OP65_RTS: + break; - case OP65_SBC: - /* We don't know the value of the carry bit */ - Out->RegA = -1; - break; + case OP65_SBC: + /* We don't know the value of the carry bit */ + Out->RegA = -1; + break; - case OP65_SEC: - break; + case OP65_SEC: + break; - case OP65_SED: - break; + case OP65_SED: + break; - case OP65_SEI: - break; + case OP65_SEI: + break; - case OP65_STA: + case OP65_STA: + if (E->AM == AM65_ZP) { + if (E->Chg & REG_SREG_LO) { + Out->SRegLo = In->RegA; + } else if (E->Chg & REG_SREG_HI) { + Out->SRegHi = In->RegA; + } + } break; case OP65_STX: + if (E->AM == AM65_ZP) { + if (E->Chg & REG_SREG_LO) { + Out->SRegLo = In->RegX; + } else if (E->Chg & REG_SREG_HI) { + Out->SRegHi = In->RegX; + } + } break; case OP65_STY: + if (E->AM == AM65_ZP) { + if (E->Chg & REG_SREG_LO) { + Out->SRegLo = In->RegY; + } else if (E->Chg & REG_SREG_HI) { + Out->SRegHi = In->RegY; + } + } break; case OP65_TAX: Out->RegX = In->RegA; break; - case OP65_TAY: + case OP65_TAY: Out->RegY = In->RegA; break; @@ -725,21 +816,22 @@ static char* RegInfoDesc (unsigned U, char* Buf) { Buf[0] = '\0'; - strcat (Buf, U & REG_SREG? "E" : "_"); - strcat (Buf, U & REG_A? "A" : "_"); - strcat (Buf, U & REG_X? "X" : "_"); - strcat (Buf, U & REG_Y? "Y" : "_"); - strcat (Buf, U & REG_SP? "S" : "_"); - strcat (Buf, U & REG_TMP1? "T1" : "__"); - strcat (Buf, U & REG_TMP2? "T2" : "__"); - strcat (Buf, U & REG_TMP3? "T3" : "__"); - strcat (Buf, U & REG_TMP4? "T4" : "__"); - strcat (Buf, U & REG_PTR1? "1" : "_"); - strcat (Buf, U & REG_PTR2? "2" : "_"); - strcat (Buf, U & REG_PTR3? "3" : "_"); - strcat (Buf, U & REG_PTR4? "4" : "_"); - strcat (Buf, U & REG_SAVE? "V" : "_"); - strcat (Buf, U & REG_BANK? "B" : "_"); + strcat (Buf, U & REG_SREG_HI? "H" : "_"); + strcat (Buf, U & REG_SREG_LO? "L" : "_"); + strcat (Buf, U & REG_A? "A" : "_"); + strcat (Buf, U & REG_X? "X" : "_"); + strcat (Buf, U & REG_Y? "Y" : "_"); + strcat (Buf, U & REG_SP? "S" : "_"); + strcat (Buf, U & REG_TMP1? "T1" : "__"); + strcat (Buf, U & REG_TMP2? "T2" : "__"); + strcat (Buf, U & REG_TMP3? "T3" : "__"); + strcat (Buf, U & REG_TMP4? "T4" : "__"); + strcat (Buf, U & REG_PTR1? "1" : "_"); + strcat (Buf, U & REG_PTR2? "2" : "_"); + strcat (Buf, U & REG_PTR3? "3" : "_"); + strcat (Buf, U & REG_PTR4? "4" : "_"); + strcat (Buf, U & REG_SAVE? "V" : "_"); + strcat (Buf, U & REG_BANK? "B" : "_"); return Buf; } @@ -834,7 +926,7 @@ void CE_Output (const CodeEntry* E, FILE* F) char Use [128]; char Chg [128]; fprintf (F, - "%*s; USE: %-19s CHG: %-19s SIZE: %u\n", + "%*s; USE: %-20s CHG: %-20s SIZE: %u\n", 30-Chars, "", RegInfoDesc (E->Use, Use), RegInfoDesc (E->Chg, Chg), diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index 1d537111b..74a1710d4 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -1011,12 +1011,8 @@ void g_putlocal (unsigned Flags, int Offs, long Val) } } else { if ((Flags & CF_NOKEEP) == 0 || CodeSizeFactor < 160) { - if (Offs) { - ldyconst (Offs); - AddCodeLine ("jsr staxysp"); - } else { - AddCodeLine ("jsr stax0sp"); - } + ldyconst (Offs); + AddCodeLine ("jsr staxysp"); } else { if (CPU == CPU_65C02 && Offs == 0) { AddCodeLine ("sta (sp)"); diff --git a/src/cc65/codeinfo.c b/src/cc65/codeinfo.c index 3eeb48506..abf8a3bf5 100644 --- a/src/cc65/codeinfo.c +++ b/src/cc65/codeinfo.c @@ -172,7 +172,8 @@ static const ZPInfo ZPInfoTable[] = { { 7, "regbank", REG_BANK }, { 7, "regsave", REG_SAVE }, { 2, "sp", REG_SP }, - { 4, "sreg", REG_SREG }, + { 0, "sreg", REG_SREG_LO }, + { 0, "sreg+1", REG_SREG_HI }, { 4, "tmp1", REG_TMP1 }, { 4, "tmp2", REG_TMP2 }, { 4, "tmp3", REG_TMP3 }, @@ -245,7 +246,7 @@ void GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg) } else { - /* Search for the function in the list of builtin functions */ + /* Search for the function in the list of builtin functions */ const FuncInfo* Info = bsearch (Name, FuncInfoTable, FuncInfoCount, sizeof(FuncInfo), CompareFuncInfo); @@ -277,12 +278,18 @@ static int CompareZPInfo (const void* Name, const void* Info) /* Do the compare. Be careful because of the length (Info may contain * more than just the zeropage name). */ - int Res = strncmp (N, E->Name, E->Len); - if (Res == 0 && (N[E->Len] != '\0' && N[E->Len] != '+')) { - /* Name is actually longer than Info->Name */ - Res = -1; + if (E->Len == 0) { + /* Do a full compare */ + return strcmp (N, E->Name); + } else { + /* Only compare the first part */ + int Res = strncmp (N, E->Name, E->Len); + if (Res == 0 && (N[E->Len] != '\0' && N[E->Len] != '+')) { + /* Name is actually longer than Info->Name */ + Res = -1; + } + return Res; } - return Res; } @@ -312,22 +319,13 @@ int IsZPName (const char* Name, unsigned short* RegInfo) -static unsigned GetRegInfo1 (CodeSeg* S, - CodeEntry* E, - int Index, - Collection* Visited, - unsigned Used, - unsigned Unused); -/* Recursively called subfunction for GetRegInfo. */ - - - static unsigned GetRegInfo2 (CodeSeg* S, CodeEntry* E, int Index, Collection* Visited, unsigned Used, - unsigned Unused) + unsigned Unused, + unsigned Wanted) /* Recursively called subfunction for GetRegInfo. */ { /* Follow the instruction flow recording register usage. */ @@ -373,7 +371,7 @@ static unsigned GetRegInfo2 (CodeSeg* S, } /* If we know about all registers now, bail out */ - if ((Used | Unused) == REG_AXY) { + if (((Used | Unused) & Wanted) == Wanted) { break; } @@ -392,7 +390,7 @@ static unsigned GetRegInfo2 (CodeSeg* S, /* Unconditional jump */ E = E->JumpTo->Owner; - Index = -1; /* Invalidate */ + Index = -1; /* Invalidate */ } else { /* Jump outside means we're done */ @@ -411,7 +409,7 @@ static unsigned GetRegInfo2 (CodeSeg* S, unsigned U1; unsigned U2; - U1 = GetRegInfo1 (S, E->JumpTo->Owner, -1, Visited, Used, Unused); + U1 = GetRegInfo2 (S, E->JumpTo->Owner, -1, Visited, Used, Unused, Wanted); if (U1 == REG_AXY) { /* All registers used, no need for second call */ return REG_AXY; @@ -422,7 +420,7 @@ static unsigned GetRegInfo2 (CodeSeg* S, if ((E = CS_GetEntry (S, ++Index)) == 0) { Internal ("GetRegInfo2: No next entry!"); } - U2 = GetRegInfo1 (S, E, Index, Visited, Used, Unused); + U2 = GetRegInfo2 (S, E, Index, Visited, Used, Unused, Wanted); return U1 | U2; /* Used in any of the branches */ } else { @@ -457,14 +455,15 @@ static unsigned GetRegInfo1 (CodeSeg* S, int Index, Collection* Visited, unsigned Used, - unsigned Unused) + unsigned Unused, + unsigned Wanted) /* Recursively called subfunction for GetRegInfo. */ { /* Remember the current count of the line collection */ unsigned Count = CollCount (Visited); /* Call the worker routine */ - unsigned R = GetRegInfo2 (S, E, Index, Visited, Used, Unused); + unsigned R = GetRegInfo2 (S, E, Index, Visited, Used, Unused, Wanted); /* Restore the old count, unmarking all new entries */ unsigned NewCount = CollCount (Visited); @@ -480,7 +479,7 @@ static unsigned GetRegInfo1 (CodeSeg* S, -unsigned GetRegInfo (struct CodeSeg* S, unsigned Index) +unsigned GetRegInfo (struct CodeSeg* S, unsigned Index, unsigned Wanted) /* Determine register usage information for the instructions starting at the * given index. */ @@ -500,7 +499,7 @@ unsigned GetRegInfo (struct CodeSeg* S, unsigned Index) InitCollection (&Visited); /* Call the recursive subfunction */ - R = GetRegInfo1 (S, E, Index, &Visited, REG_NONE, REG_NONE); + R = GetRegInfo1 (S, E, Index, &Visited, REG_NONE, REG_NONE, Wanted); /* Delete the line collection */ DoneCollection (&Visited); @@ -514,7 +513,7 @@ unsigned GetRegInfo (struct CodeSeg* S, unsigned Index) int RegAUsed (struct CodeSeg* S, unsigned Index) /* Check if the value in A is used. */ { - return (GetRegInfo (S, Index) & REG_A) != 0; + return (GetRegInfo (S, Index, REG_A) & REG_A) != 0; } @@ -522,7 +521,7 @@ int RegAUsed (struct CodeSeg* S, unsigned Index) int RegXUsed (struct CodeSeg* S, unsigned Index) /* Check if the value in X is used. */ { - return (GetRegInfo (S, Index) & REG_X) != 0; + return (GetRegInfo (S, Index, REG_X) & REG_X) != 0; } @@ -530,7 +529,7 @@ int RegXUsed (struct CodeSeg* S, unsigned Index) int RegYUsed (struct CodeSeg* S, unsigned Index) /* Check if the value in Y is used. */ { - return (GetRegInfo (S, Index) & REG_Y) != 0; + return (GetRegInfo (S, Index, REG_Y) & REG_Y) != 0; } diff --git a/src/cc65/codeinfo.h b/src/cc65/codeinfo.h index ff4c866e4..b4df0dd03 100644 --- a/src/cc65/codeinfo.h +++ b/src/cc65/codeinfo.h @@ -59,20 +59,22 @@ struct CodeSeg; #define REG_A 0x0001U #define REG_X 0x0002U #define REG_Y 0x0004U -#define REG_TMP1 0x0010U -#define REG_TMP2 0x0020U -#define REG_TMP3 0x0040U -#define REG_TMP4 0x0080U -#define REG_PTR1 0x0100U -#define REG_PTR2 0x0200U -#define REG_PTR3 0x0400U -#define REG_PTR4 0x0800U -#define REG_SREG 0x1000U +#define REG_TMP1 0x0008U +#define REG_TMP2 0x0010U +#define REG_TMP3 0x0020U +#define REG_TMP4 0x0040U +#define REG_PTR1 0x0080U +#define REG_PTR2 0x0100U +#define REG_PTR3 0x0200U +#define REG_PTR4 0x0400U +#define REG_SREG_LO 0x0800U +#define REG_SREG_HI 0x1000U #define REG_SP 0x2000U #define REG_SAVE 0x4000U #define REG_BANK 0x8000U /* Combined register defines */ +#define REG_SREG (REG_SREG_LO | REG_SREG_HI) #define REG_AX (REG_A | REG_X) #define REG_AY (REG_A | REG_Y) #define REG_XY (REG_X | REG_Y) @@ -102,7 +104,7 @@ int IsZPName (const char* Name, unsigned short* RegInfo); * zero page location found. */ -unsigned GetRegInfo (struct CodeSeg* S, unsigned Index); +unsigned GetRegInfo (struct CodeSeg* S, unsigned Index, unsigned Wanted); /* Determine register usage information for the instructions starting at the * given index. */ diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index 7b4928e51..7be0efb9c 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -697,7 +697,7 @@ static unsigned OptAdd2 (CodeSeg* S) L[6]->OPC == OP65_JSR && strcmp (L[6]->Arg, "addeqysp") == 0 && !CE_HasLabel (L[6]) && - (GetRegInfo (S, I+7) & REG_AX) == 0) { + (GetRegInfo (S, I+7, REG_AX) & REG_AX) == 0) { char Buf [20]; CodeEntry* X; @@ -2580,6 +2580,7 @@ static OptFunc OptFuncs [] = { OptEntry (OptTest1, optMain), /* Remove unused loads */ OptEntry (OptUnusedLoads, optMain), + OptEntry (OptUnusedStores, optMain), OptEntry (OptDuplicateLoads, optMain), OptEntry (OptStoreLoad, optMain), OptEntry (OptTransfers, optMain), diff --git a/src/cc65/codeseg.c b/src/cc65/codeseg.c index cc4ee7364..f29c6767e 100644 --- a/src/cc65/codeseg.c +++ b/src/cc65/codeseg.c @@ -414,8 +414,7 @@ CodeSeg* NewCodeSeg (const char* SegName, SymEntry* Func) /* If we have a function given, get the return type of the function. * Assume ANY return type besides void will use the A and X registers. */ - RetType = GetFuncReturn (Func->Type); - if (S->Func && !IsTypeVoid (RetType)) { + if (S->Func && !IsTypeVoid ((RetType = GetFuncReturn (Func->Type)))) { if (SizeOf (RetType) == SizeOf (type_long)) { S->ExitRegs = REG_EAX; } else { @@ -1059,7 +1058,7 @@ void CS_GenRegInfo (CodeSeg* S) RC_Invalidate (&Regs); CurrentRegs = &Regs; - /* First pass. Walk over all insns an note just the changes from one + /* First pass. Walk over all insns and note just the changes from one * insn to the next one. */ WasJump = 0; @@ -1115,6 +1114,12 @@ void CS_GenRegInfo (CodeSeg* S) if (J->RI->Out2.RegY != Regs.RegY) { Regs.RegY = -1; } + if (J->RI->Out2.SRegLo != Regs.SRegLo) { + Regs.SRegLo = -1; + } + if (J->RI->Out2.SRegHi != Regs.SRegHi) { + Regs.SRegHi = -1; + } ++Entry; } diff --git a/src/cc65/coptind.c b/src/cc65/coptind.c index ddd78a325..d64262b8f 100644 --- a/src/cc65/coptind.c +++ b/src/cc65/coptind.c @@ -579,7 +579,7 @@ unsigned OptCondBranches (CodeSeg* S) /*****************************************************************************/ -/* Remove unused loads */ +/* Remove unused loads and stores */ /*****************************************************************************/ @@ -623,7 +623,7 @@ unsigned OptUnusedLoads (CodeSeg* S) } /* Get register usage and check if the register value is used later */ - if ((GetRegInfo (S, I+1) & R) == 0) { + if ((GetRegInfo (S, I+1, R) & R) == 0) { /* Register value is not used, remove the load */ CS_DelEntry (S, I); @@ -646,6 +646,51 @@ NextEntry: +unsigned OptUnusedStores (CodeSeg* S) +/* Remove stores into zero page registers that aren't used later */ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's a register load or transfer insn */ + if ((E->Info & OF_STORE) != 0 && + E->AM == AM65_ZP && + (E->Chg & REG_ZP) != 0) { + + /* Check for the zero page location. We know that there cannot be + * more than one zero page location involved in the store. + */ + unsigned R = E->Chg & REG_ZP; + + /* Get register usage and check if the register value is used later */ + if ((GetRegInfo (S, I+1, R) & R) == 0) { + + /* Register value is not used, remove the load */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + unsigned OptDuplicateLoads (CodeSeg* S) /* Remove loads of registers where the value loaded is already in the register. */ { @@ -667,13 +712,16 @@ unsigned OptDuplicateLoads (CodeSeg* S) /* Assume we won't delete the entry */ int Delete = 0; + /* Get a pointer to the input registers of the insn */ + const RegContents* In = &E->RI->In; + /* Handle the different instructions */ switch (E->OPC) { case OP65_LDA: - if (E->RI->In.RegA >= 0 && /* Value of A is known */ + if (In->RegA >= 0 && /* Value of A is known */ CE_KnownImm (E) && /* Value to be loaded is known */ - E->RI->In.RegA == (long) E->Num && /* Both are equal */ + In->RegA == (long) E->Num && /* Both are equal */ (N = CS_GetNextEntry (S, I)) != 0 && /* There is a next entry */ (N->Info & OF_FBRA) == 0) { /* Which is not a cond branch */ Delete = 1; @@ -681,9 +729,9 @@ unsigned OptDuplicateLoads (CodeSeg* S) break; case OP65_LDX: - if (E->RI->In.RegX >= 0 && /* Value of X is known */ + if (In->RegX >= 0 && /* Value of X is known */ CE_KnownImm (E) && /* Value to be loaded is known */ - E->RI->In.RegX == (long) E->Num && /* Both are equal */ + In->RegX == (long) E->Num && /* Both are equal */ (N = CS_GetNextEntry (S, I)) != 0 && /* There is a next entry */ (N->Info & OF_FBRA) == 0) { /* Which is not a cond branch */ Delete = 1; @@ -691,32 +739,70 @@ unsigned OptDuplicateLoads (CodeSeg* S) break; case OP65_LDY: - if (E->RI->In.RegY >= 0 && /* Value of Y is known */ + if (In->RegY >= 0 && /* Value of Y is known */ CE_KnownImm (E) && /* Value to be loaded is known */ - E->RI->In.RegY == (long) E->Num && /* Both are equal */ + In->RegY == (long) E->Num && /* Both are equal */ (N = CS_GetNextEntry (S, I)) != 0 && /* There is a next entry */ (N->Info & OF_FBRA) == 0) { /* Which is not a cond branch */ Delete = 1; } break; + case OP65_STA: + /* If we store into a known zero page location, and this + * location does already contain the value to be stored, + * remove the store. + */ + if (In->RegA >= 0 && /* Value of A is known */ + E->AM == AM65_ZP && /* Store into zp */ + (((E->Chg & REG_SREG_LO) != 0 && /* Store into sreg */ + In->RegA == In->SRegLo) || /* Value identical */ + ((E->Chg & REG_SREG_HI) != 0 && /* Store into sreg+1 */ + In->RegA == In->SRegHi))) { /* Value identical */ + Delete = 1; + } + break; + case OP65_STX: - /* If the value in the X register is known and the same as + /* If we store into a known zero page location, and this + * location does already contain the value to be stored, + * remove the store. + */ + if (In->RegX >= 0 && /* Value of A is known */ + E->AM == AM65_ZP && /* Store into zp */ + (((E->Chg & REG_SREG_LO) != 0 && /* Store into sreg */ + In->RegX == In->SRegLo) || /* Value identical */ + ((E->Chg & REG_SREG_HI) != 0 && /* Store into sreg+1 */ + In->RegX == In->SRegHi))) { /* Value identical */ + Delete = 1; + + /* If the value in the X register is known and the same as * that in the A register, replace the store by a STA. The * optimizer will then remove the load instruction for X * later. STX does support the zeropage,y addressing mode, * so be sure to check for that. */ - if (E->RI->In.RegX >= 0 && - E->RI->In.RegX == E->RI->In.RegA && - E->AM != AM65_ABSY && - E->AM != AM65_ZPY) { + } else if (In->RegX >= 0 && + In->RegX == In->RegA && + E->AM != AM65_ABSY && + E->AM != AM65_ZPY) { /* Use the A register instead */ CE_ReplaceOPC (E, OP65_STA); } break; case OP65_STY: + /* If we store into a known zero page location, and this + * location does already contain the value to be stored, + * remove the store. + */ + if (In->RegX >= 0 && /* Value of A is known */ + E->AM == AM65_ZP && /* Store into zp */ + (((E->Chg & REG_SREG_LO) != 0 && /* Store into sreg */ + In->RegX == In->SRegLo) || /* Value identical */ + ((E->Chg & REG_SREG_HI) != 0 && /* Store into sreg+1 */ + In->RegX == In->SRegHi))) { /* Value identical */ + Delete = 1; /* If the value in the Y register is known and the same as * that in the A register, replace the store by a STA. The * optimizer will then remove the load instruction for Y @@ -724,11 +810,11 @@ unsigned OptDuplicateLoads (CodeSeg* S) * replacement by X, but check for invalid addressing modes * in this case. */ - if (E->RI->In.RegY >= 0) { - if (E->RI->In.RegY == E->RI->In.RegA) { + } else if (In->RegY >= 0) { + if (In->RegY == In->RegA) { CE_ReplaceOPC (E, OP65_STA); - } else if (E->RI->In.RegY == E->RI->In.RegX && - E->AM != AM65_ABSX && + } else if (In->RegY == In->RegX && + E->AM != AM65_ABSX && E->AM != AM65_ZPX) { CE_ReplaceOPC (E, OP65_STX); } @@ -736,9 +822,9 @@ unsigned OptDuplicateLoads (CodeSeg* S) break; case OP65_TAX: - if (E->RI->In.RegA >= 0 && - E->RI->In.RegA == E->RI->In.RegX && - (N = CS_GetNextEntry (S, I)) != 0 && + if (In->RegA >= 0 && + In->RegA == In->RegX && + (N = CS_GetNextEntry (S, I)) != 0 && (N->Info & OF_FBRA) == 0) { /* Value is identical and not followed by a branch */ Delete = 1; @@ -746,8 +832,8 @@ unsigned OptDuplicateLoads (CodeSeg* S) break; case OP65_TAY: - if (E->RI->In.RegA >= 0 && - E->RI->In.RegA == E->RI->In.RegY && + if (In->RegA >= 0 && + In->RegA == In->RegY && (N = CS_GetNextEntry (S, I)) != 0 && (N->Info & OF_FBRA) == 0) { /* Value is identical and not followed by a branch */ @@ -756,8 +842,8 @@ unsigned OptDuplicateLoads (CodeSeg* S) break; case OP65_TXA: - if (E->RI->In.RegX >= 0 && - E->RI->In.RegX == E->RI->In.RegA && + if (In->RegX >= 0 && + In->RegX == In->RegA && (N = CS_GetNextEntry (S, I)) != 0 && (N->Info & OF_FBRA) == 0) { /* Value is identical and not followed by a branch */ @@ -766,8 +852,8 @@ unsigned OptDuplicateLoads (CodeSeg* S) break; case OP65_TYA: - if (E->RI->In.RegY >= 0 && - E->RI->In.RegY == E->RI->In.RegA && + if (In->RegY >= 0 && + In->RegY == In->RegA && (N = CS_GetNextEntry (S, I)) != 0 && (N->Info & OF_FBRA) == 0) { /* Value is identical and not followed by a branch */ diff --git a/src/cc65/coptind.h b/src/cc65/coptind.h index 04530f194..746637092 100644 --- a/src/cc65/coptind.h +++ b/src/cc65/coptind.h @@ -89,6 +89,9 @@ unsigned OptCondBranches (CodeSeg* S); unsigned OptUnusedLoads (CodeSeg* S); /* Remove loads of registers where the value loaded is not used later. */ +unsigned OptUnusedStores (CodeSeg* S); +/* Remove stores into zero page registers that aren't used later */ + unsigned OptDuplicateLoads (CodeSeg* S); /* Remove loads of registers where the value loaded is already in the register. */ diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 4da5470b9..47944b37a 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -682,22 +682,22 @@ int IsQualVolatile (const type* T) int IsFastCallFunc (const type* T) -/* Return true if this is a function type with __fastcall__ calling conventions */ +/* Return true if this is a function type or pointer to function with + * __fastcall__ calling conventions + */ { - FuncDesc* F; - CHECK (IsTypeFunc (T)); - F = (FuncDesc*) DecodePtr (T+1); + FuncDesc* F = GetFuncDesc (T); return (F->Flags & FD_FASTCALL) != 0; } int IsVariadicFunc (const type* T) -/* Return true if this is a function type with variable parameter list */ +/* Return true if this is a function type or pointer to function type with + * variable parameter list + */ { - FuncDesc* F; - CHECK (IsTypeFunc (T)); - F = (FuncDesc*) DecodePtr (T+1); + FuncDesc* F = GetFuncDesc (T); return (F->Flags & FD_VARIADIC) != 0; } diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index b561da26f..beabf2bd8 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -287,10 +287,14 @@ int IsQualVolatile (const type* T) attribute ((const)); /* Return true if the given type has a volatile type qualifier */ int IsFastCallFunc (const type* T) attribute ((const)); -/* Return true if this is a function type with __fastcall__ calling conventions */ +/* Return true if this is a function type or pointer to function with + * __fastcall__ calling conventions + */ int IsVariadicFunc (const type* T) attribute ((const)); -/* Return true if this is a function type with variable parameter list */ +/* Return true if this is a function type or pointer to function type with + * variable parameter list + */ int IsTypeFuncPtr (const type* T) attribute ((const)); /* Return true if this is a function pointer */ diff --git a/src/cc65/declare.c b/src/cc65/declare.c index feac79212..9aabf0eab 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -807,12 +807,12 @@ static void Decl (Declaration* D, unsigned Mode) /* Parse the function */ Decl (D, Mode); /* Set the fastcall flag */ - if (!IsTypeFunc (T)) { + if (!IsTypeFunc (T) && !IsTypeFuncPtr (T)) { Error ("__fastcall__ modifier applied to non function"); } else if (IsVariadicFunc (T)) { Error ("Cannot apply __fastcall__ to functions with variable parameter list"); } else { - FuncDesc* F = (FuncDesc*) DecodePtr (T+1); + FuncDesc* F = GetFuncDesc (T); F->Flags |= FD_FASTCALL; } return; diff --git a/src/cc65/expr.c b/src/cc65/expr.c index a26935902..ed0b1cf1e 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -707,53 +707,122 @@ static unsigned FunctionParamList (FuncDesc* Func) -static void FunctionCall (ExprDesc* lval) -/* Perform a function call. Called from hie11, this routine will - * either call the named function, or the function pointer in a/x. - */ +static void FunctionCall (int k, ExprDesc* lval) +/* Perform a function call. */ { - FuncDesc* Func; /* Function descriptor */ - unsigned ParamSize; /* Number of parameter bytes */ - CodeMark Mark; - + FuncDesc* Func; /* Function descriptor */ + int IsFuncPtr; /* Flag */ + unsigned ParamSize; /* Number of parameter bytes */ + CodeMark Mark = 0; /* Initialize to keep gcc silent */ + int PtrOffs = 0; /* Offset of function pointer on stack */ + int IsFastCall = 0; /* True if it's a fast call function */ + int PtrOnStack = 0; /* True if a pointer copy is on stack */ /* Get a pointer to the function descriptor from the type string */ Func = GetFuncDesc (lval->Type); - /* Initialize vars to keep gcc silent */ - Mark = 0; + /* Handle function pointers transparently */ + IsFuncPtr = IsTypeFuncPtr (lval->Type); + if (IsFuncPtr) { - /* Check if this is a function pointer. If so, save it. If not, check for - * special known library functions that may be inlined. - */ - if (lval->Flags & E_MEXPR) { - /* Function pointer is in primary register, save it */ - Mark = GetCodePos (); - g_save (CF_PTR); + /* Check wether it's a fastcall function */ + IsFastCall = IsFastCallFunc (lval->Type + 1); + + /* Things may be difficult, depending on where the function pointer + * resides. If the function pointer is an expression of some sort + * (not a local or global variable), we have to evaluate this + * expression now and save the result for later. Since calls to + * function pointers may be nested, we must save it onto the stack. + * For fastcall functions we do also need to place a copy of the + * pointer on stack, since we cannot use a/x. + */ + PtrOnStack = IsFastCall || ((lval->Flags & (E_MGLOBAL | E_MLOCAL)) == 0); + if (PtrOnStack) { + + /* Not a global or local variable, or a fastcall function. Load + * the pointer into the primary and mark it as an expression. + */ + exprhs (CF_NONE, k, lval); + lval->Flags |= E_MEXPR; + + /* Remember the code position */ + Mark = GetCodePos (); + + /* Push the pointer onto the stack and remember the offset */ + g_push (CF_PTR, 0); + PtrOffs = oursp; + } + + /* Check for known standard functions and inline them if requested */ } else if (InlineStdFuncs && IsStdFunc ((const char*) lval->Name)) { - /* Inline this function */ + + /* Inline this function */ HandleStdFunc (lval); return; + } /* Parse the parameter list */ ParamSize = FunctionParamList (Func); - /* We need the closing bracket here */ + /* We need the closing paren here */ ConsumeRParen (); - /* */ - if (lval->Flags & E_MEXPR) { - /* Function called via pointer: Restore it and call function */ - if (ParamSize != 0) { - g_restore (CF_PTR); - } else { - /* We had no parameters - remove save code */ - RemoveCode (Mark); + /* Special handling for function pointers */ + if (IsFuncPtr) { + + /* If the function is not a fastcall function, load the pointer to + * the function into the primary. + */ + if (!IsFastCallFunc (lval->Type)) { + + /* Not a fastcall function - we may use the primary */ + if (PtrOnStack) { + /* If we have no parameters, the pointer is still in the + * primary. Remove the code to push it and correct the + * stack pointer. + */ + if (ParamSize == 0) { + RemoveCode (Mark); + pop (CF_PTR); + PtrOnStack = 0; + } else { + /* Load from the saved copy */ + g_getlocal (CF_PTR, PtrOffs); + } + } else { + /* Load from original location */ + exprhs (CF_NONE, k, lval); + } + + /* Call the function */ + g_callind (TypeOf (lval->Type), ParamSize); + + /* If we have a pointer on stack, remove it */ + if (PtrOnStack) { + g_space (- (int) sizeofarg (CF_PTR)); + pop (CF_PTR); + } + + } else { + + /* Fastcall function. We cannot use the primary for the function + * pointer and must therefore use an offset to the stack location. + * Since fastcall functions may never be variadic, we can use the + * index register for this purpose. + */ + Error ("Not implemented"); + pop (CF_PTR); } - g_callind (TypeOf (lval->Type), ParamSize); + + /* Skip T_PTR */ + ++lval->Type; + } else { + + /* Normal function */ g_call (TypeOf (lval->Type), (const char*) lval->Name, ParamSize); + } } @@ -829,7 +898,7 @@ static int primary (ExprDesc* lval) /* Check for legal symbol types */ if ((Sym->Flags & SC_CONST) == SC_CONST) { - /* Enum or some other numeric constant */ + /* Enum or some other numeric constant */ lval->Flags = E_MCONST; lval->ConstVal = Sym->V.ConstVal; return 0; @@ -840,19 +909,19 @@ static int primary (ExprDesc* lval) lval->ConstVal = 0; } else if ((Sym->Flags & SC_AUTO) == SC_AUTO) { /* Local variable. If this is a parameter for a variadic - * function, we have to add some address calculations, and the - * address is not const. - */ + * function, we have to add some address calculations, and the + * address is not const. + */ if ((Sym->Flags & SC_PARAM) == SC_PARAM && IsVariadic (CurrentFunc)) { - /* Variadic parameter */ - g_leavariadic (Sym->V.Offs - GetParamSize (CurrentFunc)); - lval->Flags = E_MEXPR; - lval->ConstVal = 0; - } else { - /* Normal parameter */ - lval->Flags = E_MLOCAL | E_TLOFFS; - lval->ConstVal = Sym->V.Offs; - } + /* Variadic parameter */ + g_leavariadic (Sym->V.Offs - GetParamSize (CurrentFunc)); + lval->Flags = E_MEXPR; + lval->ConstVal = 0; + } else { + /* Normal parameter */ + lval->Flags = E_MLOCAL | E_TLOFFS; + lval->ConstVal = Sym->V.Offs; + } } else if ((Sym->Flags & SC_STATIC) == SC_STATIC) { /* Static variable */ if (Sym->Flags & (SC_EXTERN | SC_STORAGE)) { @@ -1253,16 +1322,17 @@ static int hie11 (ExprDesc *lval) /* Function call. Skip the opening parenthesis */ NextToken (); tptr = lval->Type; - if (IsTypeFunc (tptr) || IsTypeFuncPtr (tptr)) { - if (IsTypeFuncPtr (tptr)) { - /* Pointer to function. Handle transparently */ - exprhs (CF_NONE, k, lval); /* Function pointer to A/X */ - ++lval->Type; /* Skip T_PTR */ - lval->Flags |= E_MEXPR; - } - FunctionCall (lval); + if (IsTypeFunc (lval->Type) || IsTypeFuncPtr (lval->Type)) { + + /* Call the function */ + FunctionCall (k, lval); + + /* Result is in the primary register */ lval->Flags = E_MEXPR; - lval->Type += DECODE_SIZE + 1; /* Set to result */ + + /* Set to result */ + lval->Type = GetFuncReturn (lval->Type); + } else { Error ("Illegal function call"); }