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"); }