mirror of
https://github.com/cc65/cc65.git
synced 2024-11-19 06:31:31 +00:00
Working
git-svn-id: svn://svn.cc65.org/cc65/trunk@738 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
parent
c1b6680a92
commit
a687912ea3
@ -359,14 +359,15 @@ void OutputCodeEntry (const CodeEntry* E, FILE* F)
|
||||
/* Print usage info if requested by the debugging flag */
|
||||
// if (Debug) {
|
||||
Chars += fprintf (F,
|
||||
"%*s; USE: %c%c%c CHG: %c%c%c",
|
||||
"%*s; USE: %c%c%c CHG: %c%c%c SIZE: %u",
|
||||
30-Chars, "",
|
||||
(E->Use & REG_A)? 'A' : '_',
|
||||
(E->Use & REG_X)? 'X' : '_',
|
||||
(E->Use & REG_Y)? 'Y' : '_',
|
||||
(E->Chg & REG_A)? 'A' : '_',
|
||||
(E->Chg & REG_X)? 'X' : '_',
|
||||
(E->Chg & REG_Y)? 'Y' : '_');
|
||||
(E->Chg & REG_Y)? 'Y' : '_',
|
||||
E->Size);
|
||||
// }
|
||||
|
||||
}
|
||||
|
@ -112,6 +112,11 @@ static const FuncInfo FuncInfoTable[] = {
|
||||
};
|
||||
#define FuncInfoCount (sizeof(FuncInfoTable) / sizeof(FuncInfoTable[0]))
|
||||
|
||||
/* Table with names of zero page locations used by the compiler */
|
||||
static const char* ZPNameTable[] = {
|
||||
"ptr1", "regbank", "regsave", "sp", "sreg", "tmp1"
|
||||
};
|
||||
#define ZPNameCount (sizeof(ZPNameTable) / sizeof(ZPNameTable[0]))
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
@ -150,7 +155,7 @@ void GetFuncInfo (const char* Name, unsigned char* Use, unsigned char* Chg)
|
||||
/* A function may use the A or A/X registers if it is a fastcall
|
||||
* function. If it is not a fastcall function but a variadic one,
|
||||
* it will use the Y register (the parameter size is passed here).
|
||||
* In all other cases, no registers are used. However, we assume
|
||||
* In all other cases, no registers are used. However, we assume
|
||||
* that any function will destroy all registers.
|
||||
*/
|
||||
FuncDesc* D = E->V.F.Func;
|
||||
@ -164,7 +169,7 @@ void GetFuncInfo (const char* Name, unsigned char* Use, unsigned char* Chg)
|
||||
}
|
||||
} else if ((D->Flags & FD_VARIADIC) != 0) {
|
||||
*Use = REG_Y;
|
||||
} else {
|
||||
} else {
|
||||
/* Will not use any registers */
|
||||
*Use = REG_NONE;
|
||||
}
|
||||
@ -198,6 +203,25 @@ void GetFuncInfo (const char* Name, unsigned char* Use, unsigned char* Chg)
|
||||
|
||||
|
||||
|
||||
int IsZPName (const char* Name)
|
||||
/* Return true if the given name is a zero page symbol */
|
||||
{
|
||||
unsigned I;
|
||||
|
||||
/* Because of the low number of symbols, we do a linear search here */
|
||||
for (I = 0; I < ZPNameCount; ++I) {
|
||||
if (strcmp (Name, ZPNameTable[I]) == 0) {
|
||||
/* Found */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not found */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static unsigned char GetRegInfo2 (CodeSeg* S,
|
||||
CodeEntry* E,
|
||||
int Index,
|
||||
|
@ -82,6 +82,9 @@ void GetFuncInfo (const char* Name, unsigned char* Use, unsigned char* Chg);
|
||||
* load all registers.
|
||||
*/
|
||||
|
||||
int IsZPName (const char* Name);
|
||||
/* Return true if the given name is a zero page symbol */
|
||||
|
||||
unsigned char GetRegInfo (struct CodeSeg* S, unsigned Index);
|
||||
/* Determine register usage information for the instructions starting at the
|
||||
* given index.
|
||||
|
@ -130,7 +130,7 @@ static cmp_t FindBoolCmpCond (const char* Name)
|
||||
|
||||
|
||||
|
||||
static int FindTosCmpCond (const char* Name)
|
||||
static cmp_t FindTosCmpCond (const char* Name)
|
||||
/* Check if this is a call to one of the TOS compare functions (tosgtax).
|
||||
* Return the condition code or CMP_INV on failure.
|
||||
*/
|
||||
@ -149,6 +149,110 @@ static int FindTosCmpCond (const char* Name)
|
||||
|
||||
|
||||
|
||||
static void ReplaceCmp (CodeSeg* S, unsigned I, cmp_t Cond)
|
||||
/* Helper function for the replacement of routines that return a boolean
|
||||
* followed by a conditional jump. Instead of the boolean value, the condition
|
||||
* codes are evaluated directly.
|
||||
* I is the index of the conditional branch, the sequence is already checked
|
||||
* to be correct.
|
||||
*/
|
||||
{
|
||||
CodeEntry* N;
|
||||
CodeLabel* L;
|
||||
|
||||
/* Get the entry */
|
||||
CodeEntry* E = GetCodeEntry (S, I);
|
||||
|
||||
/* Replace the conditional branch */
|
||||
switch (Cond) {
|
||||
|
||||
case CMP_EQ:
|
||||
ReplaceOPC (E, OPC_JEQ);
|
||||
break;
|
||||
|
||||
case CMP_NE:
|
||||
ReplaceOPC (E, OPC_JNE);
|
||||
break;
|
||||
|
||||
case CMP_GT:
|
||||
/* Replace by
|
||||
* beq @L
|
||||
* jpl Target
|
||||
* @L: ...
|
||||
*/
|
||||
if ((N = GetNextCodeEntry (S, I)) == 0) {
|
||||
/* No such entry */
|
||||
Internal ("Invalid program flow");
|
||||
}
|
||||
L = GenCodeLabel (S, N);
|
||||
N = NewCodeEntry (OPC_BEQ, AM_BRA, L->Name, L);
|
||||
InsertCodeEntry (S, N, I);
|
||||
ReplaceOPC (E, OPC_JPL);
|
||||
break;
|
||||
|
||||
case CMP_GE:
|
||||
ReplaceOPC (E, OPC_JPL);
|
||||
break;
|
||||
|
||||
case CMP_LT:
|
||||
ReplaceOPC (E, OPC_JMI);
|
||||
break;
|
||||
|
||||
case CMP_LE:
|
||||
/* Replace by
|
||||
* jmi Target
|
||||
* jeq Target
|
||||
*/
|
||||
ReplaceOPC (E, OPC_JMI);
|
||||
L = E->JumpTo;
|
||||
N = NewCodeEntry (OPC_JEQ, AM_BRA, L->Name, L);
|
||||
InsertCodeEntry (S, N, I+1);
|
||||
break;
|
||||
|
||||
case CMP_UGT:
|
||||
/* Replace by
|
||||
* beq @L
|
||||
* jcs Target
|
||||
* @L: ...
|
||||
*/
|
||||
if ((N = GetNextCodeEntry (S, I)) == 0) {
|
||||
/* No such entry */
|
||||
Internal ("Invalid program flow");
|
||||
}
|
||||
L = GenCodeLabel (S, N);
|
||||
N = NewCodeEntry (OPC_BEQ, AM_BRA, L->Name, L);
|
||||
InsertCodeEntry (S, N, I);
|
||||
ReplaceOPC (E, OPC_JCS);
|
||||
break;
|
||||
|
||||
case CMP_UGE:
|
||||
ReplaceOPC (E, OPC_JCS);
|
||||
break;
|
||||
|
||||
case CMP_ULT:
|
||||
ReplaceOPC (E, OPC_JCC);
|
||||
break;
|
||||
|
||||
case CMP_ULE:
|
||||
/* Replace by
|
||||
* jcc Target
|
||||
* jeq Target
|
||||
*/
|
||||
ReplaceOPC (E, OPC_JCC);
|
||||
L = E->JumpTo;
|
||||
N = NewCodeEntry (OPC_JEQ, AM_BRA, L->Name, L);
|
||||
InsertCodeEntry (S, N, I+1);
|
||||
break;
|
||||
|
||||
default:
|
||||
Internal ("Unknown jump condition: %d", Cond);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int IsUnsignedCmp (int Code)
|
||||
/* Check if this is an unsigned compare */
|
||||
{
|
||||
@ -158,32 +262,6 @@ static int IsUnsignedCmp (int Code)
|
||||
|
||||
|
||||
|
||||
static const char* MakeBoolCmp (cmp_t Cond)
|
||||
/* Create the name of a bool transformer subroutine for the given code. The
|
||||
* result is placed into a static buffer, so beware!
|
||||
*/
|
||||
{
|
||||
static char Buffer[20];
|
||||
CHECK (Cond != CMP_INV);
|
||||
sprintf (Buffer, "bool%s", CmpSuffixTab[Cond]);
|
||||
return Buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const char* MakeTosCmp (cmp_t Cond)
|
||||
/* Create the name of a tos compare subroutine for the given code. The
|
||||
* result is placed into a static buffer, so beware!
|
||||
*/
|
||||
{
|
||||
static char Buffer[20];
|
||||
CHECK (Cond != CMP_INV);
|
||||
sprintf (Buffer, "tos%sax", CmpSuffixTab[Cond]);
|
||||
return Buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int IsBitOp (const CodeEntry* E)
|
||||
/* Check if E is one of the bit operations (and, or, eor) */
|
||||
{
|
||||
@ -300,9 +378,6 @@ static unsigned OptBoolTransforms (CodeSeg* S)
|
||||
(N = GetNextCodeEntry (S, I)) != 0 &&
|
||||
(N->Info & OF_ZBRA) != 0) {
|
||||
|
||||
CodeEntry* X;
|
||||
CodeLabel* L;
|
||||
|
||||
/* Make the boolean transformer unnecessary by changing the
|
||||
* the conditional jump to evaluate the condition flags that
|
||||
* are set after the compare directly. Note: jeq jumps if
|
||||
@ -315,90 +390,7 @@ static unsigned OptBoolTransforms (CodeSeg* S)
|
||||
}
|
||||
|
||||
/* Check if we can replace the code by something better */
|
||||
switch (Cond) {
|
||||
|
||||
case CMP_EQ:
|
||||
ReplaceOPC (N, OPC_JEQ);
|
||||
break;
|
||||
|
||||
case CMP_NE:
|
||||
ReplaceOPC (N, OPC_JNE);
|
||||
break;
|
||||
|
||||
case CMP_GT:
|
||||
/* Replace by
|
||||
* beq @L
|
||||
* jpl Target
|
||||
* @L: ...
|
||||
*/
|
||||
if ((X = GetNextCodeEntry (S, I+1)) == 0) {
|
||||
/* No such entry */
|
||||
goto NextEntry;
|
||||
}
|
||||
L = GenCodeLabel (S, X);
|
||||
X = NewCodeEntry (OPC_BEQ, AM_BRA, L->Name, L);
|
||||
InsertCodeEntry (S, X, I+1);
|
||||
ReplaceOPC (N, OPC_JPL);
|
||||
break;
|
||||
|
||||
case CMP_GE:
|
||||
ReplaceOPC (N, OPC_JPL);
|
||||
break;
|
||||
|
||||
case CMP_LT:
|
||||
ReplaceOPC (N, OPC_JMI);
|
||||
break;
|
||||
|
||||
case CMP_LE:
|
||||
/* Replace by
|
||||
* jmi Target
|
||||
* jeq Target
|
||||
*/
|
||||
ReplaceOPC (N, OPC_JMI);
|
||||
L = N->JumpTo;
|
||||
X = NewCodeEntry (OPC_JEQ, AM_BRA, L->Name, L);
|
||||
InsertCodeEntry (S, X, I+2);
|
||||
break;
|
||||
|
||||
case CMP_UGT:
|
||||
/* Replace by
|
||||
* beq @L
|
||||
* jcs Target
|
||||
* @L: ...
|
||||
*/
|
||||
if ((X = GetNextCodeEntry (S, I+1)) == 0) {
|
||||
/* No such entry */
|
||||
goto NextEntry;
|
||||
}
|
||||
L = GenCodeLabel (S, X);
|
||||
X = NewCodeEntry (OPC_BEQ, AM_BRA, L->Name, L);
|
||||
InsertCodeEntry (S, X, I+1);
|
||||
ReplaceOPC (N, OPC_JCS);
|
||||
break;
|
||||
|
||||
case CMP_UGE:
|
||||
ReplaceOPC (N, OPC_JCS);
|
||||
break;
|
||||
|
||||
case CMP_ULT:
|
||||
ReplaceOPC (N, OPC_JCC);
|
||||
break;
|
||||
|
||||
case CMP_ULE:
|
||||
/* Replace by
|
||||
* jcc Target
|
||||
* jeq Target
|
||||
*/
|
||||
ReplaceOPC (N, OPC_JCC);
|
||||
L = N->JumpTo;
|
||||
X = NewCodeEntry (OPC_JEQ, AM_BRA, L->Name, L);
|
||||
InsertCodeEntry (S, X, I+2);
|
||||
break;
|
||||
|
||||
default:
|
||||
Internal ("Unknown jump condition: %d", Cond);
|
||||
|
||||
}
|
||||
ReplaceCmp (S, I+1, Cond);
|
||||
|
||||
/* Remove the call to the bool transformer */
|
||||
DelCodeEntry (S, I);
|
||||
@ -408,7 +400,6 @@ static unsigned OptBoolTransforms (CodeSeg* S)
|
||||
|
||||
}
|
||||
|
||||
NextEntry:
|
||||
/* Next entry */
|
||||
++I;
|
||||
|
||||
@ -863,6 +854,68 @@ static unsigned OptCmp4 (CodeSeg* S)
|
||||
|
||||
|
||||
|
||||
static unsigned OptCmp5 (CodeSeg* S)
|
||||
/* Search for calls to compare subroutines followed by a conditional branch
|
||||
* and replace them by cheaper versions, since the branch means that the
|
||||
* boolean value returned by these routines is not needed (we may also check
|
||||
* that explicitly, but for the current code generator it is always true).
|
||||
*/
|
||||
{
|
||||
unsigned Changes = 0;
|
||||
|
||||
/* Walk over the entries */
|
||||
unsigned I = 0;
|
||||
while (I < GetCodeEntryCount (S)) {
|
||||
|
||||
CodeEntry* N;
|
||||
cmp_t Cond;
|
||||
|
||||
/* Get next entry */
|
||||
CodeEntry* E = GetCodeEntry (S, I);
|
||||
|
||||
/* Check for the sequence */
|
||||
if (E->OPC == OPC_JSR &&
|
||||
(Cond = FindTosCmpCond (E->Arg)) != CMP_INV &&
|
||||
(N = GetNextCodeEntry (S, I)) != 0 &&
|
||||
(N->Info & OF_ZBRA) != 0 &&
|
||||
!CodeEntryHasLabel (N)) {
|
||||
|
||||
/* The tos... functions will return a boolean value in a/x and
|
||||
* the Z flag says if this value is zero or not. We will call
|
||||
* a cheaper subroutine instead, one that does not return a
|
||||
* boolean value but only valid flags. Note: jeq jumps if
|
||||
* the condition is not met, jne jumps if the condition is met.
|
||||
* Invert the code if we jump on condition not met.
|
||||
*/
|
||||
if (GetBranchCond (N->OPC) == BC_EQ) {
|
||||
/* Jumps if condition false, invert condition */
|
||||
Cond = CmpInvertTab [Cond];
|
||||
}
|
||||
|
||||
/* Replace the subroutine call. */
|
||||
DelCodeEntry (S, I);
|
||||
E = NewCodeEntry (OPC_JSR, AM_ABS, "tosicmp", 0);
|
||||
InsertCodeEntry (S, E, I);
|
||||
|
||||
/* Replace the conditional branch */
|
||||
ReplaceCmp (S, I+1, Cond);
|
||||
|
||||
/* Remember, we had changes */
|
||||
++Changes;
|
||||
|
||||
}
|
||||
|
||||
/* Next entry */
|
||||
++I;
|
||||
|
||||
}
|
||||
|
||||
/* Return the number of changes made */
|
||||
return Changes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* nega optimizations */
|
||||
/*****************************************************************************/
|
||||
@ -1136,12 +1189,12 @@ static unsigned OptNegAX3 (CodeSeg* S)
|
||||
CodeEntry* E = GetCodeEntry (S, I);
|
||||
|
||||
/* Check for the sequence */
|
||||
if (E->OPC == OPC_JSR &&
|
||||
E->Arg[0] == '_' &&
|
||||
GetCodeEntries (S, L, I+1, 2) &&
|
||||
if (E->OPC == OPC_JSR &&
|
||||
E->Arg[0] == '_' &&
|
||||
GetCodeEntries (S, L, I+1, 2) &&
|
||||
L[0]->OPC == OPC_JSR &&
|
||||
strncmp (L[0]->Arg,"bnega",5) == 0 &&
|
||||
!CodeEntryHasLabel (L[0]) &&
|
||||
strncmp (L[0]->Arg,"bnega",5) == 0 &&
|
||||
!CodeEntryHasLabel (L[0]) &&
|
||||
(L[1]->Info & OF_ZBRA) != 0) {
|
||||
|
||||
/* Check if we're calling bnega or bnegax */
|
||||
@ -1230,6 +1283,7 @@ static OptFunc OptFuncs [] = {
|
||||
{ OptCmp2, "OptCmp2", 0 },
|
||||
{ OptCmp3, "OptCmp3", 0 },
|
||||
{ OptCmp4, "OptCmp4", 0 },
|
||||
{ OptCmp5, "OptCmp5", 0 },
|
||||
/* Remove unused loads */
|
||||
{ OptUnusedLoads, "OptUnusedLoads", 0 },
|
||||
/* Optimize branch distance */
|
||||
@ -1263,8 +1317,15 @@ static OptFunc* FindOptStep (const char* Name)
|
||||
void DisableOpt (const char* Name)
|
||||
/* Disable the optimization with the given name */
|
||||
{
|
||||
OptFunc* F = FindOptStep (Name);
|
||||
F->Disabled = 1;
|
||||
if (strcmp (Name, "any") == 0) {
|
||||
unsigned I;
|
||||
for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
|
||||
OptFuncs[I].Disabled = 1;
|
||||
}
|
||||
} else {
|
||||
OptFunc* F = FindOptStep (Name);
|
||||
F->Disabled = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1272,8 +1333,15 @@ void DisableOpt (const char* Name)
|
||||
void EnableOpt (const char* Name)
|
||||
/* Enable the optimization with the given name */
|
||||
{
|
||||
OptFunc* F = FindOptStep (Name);
|
||||
F->Disabled = 0;
|
||||
if (strcmp (Name, "any") == 0) {
|
||||
unsigned I;
|
||||
for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
|
||||
OptFuncs[I].Disabled = 0;
|
||||
}
|
||||
} else {
|
||||
OptFunc* F = FindOptStep (Name);
|
||||
F->Disabled = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -296,8 +296,15 @@ static CodeEntry* ParseInsn (CodeSeg* S, const char* L)
|
||||
/* Absolute, maybe indexed */
|
||||
L = ReadToken (L, ",", Arg, sizeof (Arg));
|
||||
if (*L == '\0') {
|
||||
/* Assume absolute */
|
||||
AM = AM_ABS;
|
||||
/* Absolute, zeropage or branch */
|
||||
if ((OPC->Info & OF_BRA) != 0) {
|
||||
/* Branch */
|
||||
AM = AM_BRA;
|
||||
} else if (IsZPName (Arg)) {
|
||||
AM = AM_ZP;
|
||||
} else {
|
||||
AM = AM_ABS;
|
||||
}
|
||||
} else if (*L == ',') {
|
||||
/* Indexed */
|
||||
L = SkipSpace (L+1);
|
||||
@ -308,7 +315,11 @@ static CodeEntry* ParseInsn (CodeSeg* S, const char* L)
|
||||
Reg = toupper (*L);
|
||||
L = SkipSpace (L+1);
|
||||
if (Reg == 'X') {
|
||||
AM = AM_ABSX;
|
||||
if (IsZPName (Arg)) {
|
||||
AM = AM_ZPX;
|
||||
} else {
|
||||
AM = AM_ABSX;
|
||||
}
|
||||
} else if (Reg == 'Y') {
|
||||
AM = AM_ABSY;
|
||||
} else {
|
||||
@ -329,18 +340,10 @@ static CodeEntry* ParseInsn (CodeSeg* S, const char* L)
|
||||
* if it does not exist. Ignore anything but local labels here.
|
||||
*/
|
||||
Label = 0;
|
||||
if ((OPC->Info & OF_BRA) != 0 && Arg[0] == 'L') {
|
||||
|
||||
unsigned Hash;
|
||||
|
||||
/* Addressing mode must be alsobute or something is really wrong */
|
||||
CHECK (AM == AM_ABS);
|
||||
|
||||
/* Addressing mode is a branch/jump */
|
||||
AM = AM_BRA;
|
||||
if (AM == AM_BRA && Arg[0] == 'L') {
|
||||
|
||||
/* Generate the hash over the label, then search for the label */
|
||||
Hash = HashStr (Arg) % CS_LABEL_HASH_SIZE;
|
||||
unsigned Hash = HashStr (Arg) % CS_LABEL_HASH_SIZE;
|
||||
Label = FindCodeLabel (S, Arg, Hash);
|
||||
|
||||
/* If we don't have the label, it's a forward ref - create it */
|
||||
@ -959,14 +962,18 @@ void OutputCodeSeg (const CodeSeg* S, FILE* F)
|
||||
unsigned char Use;
|
||||
|
||||
OutputCodeEntry (CollConstAt (&S->Entries, I), F);
|
||||
|
||||
/* Print usage info */
|
||||
Use = GetRegInfo ((CodeSeg*) S, I+1);
|
||||
fprintf (F,
|
||||
" Use: %c%c%c\n",
|
||||
(Use & REG_A)? 'A' : '_',
|
||||
(Use & REG_X)? 'X' : '_',
|
||||
(Use & REG_Y)? 'Y' : '_');
|
||||
|
||||
#if 0
|
||||
/* Print usage info */
|
||||
Use = GetRegInfo ((CodeSeg*) S, I+1);
|
||||
fprintf (F,
|
||||
" Use: %c%c%c\n",
|
||||
(Use & REG_A)? 'A' : '_',
|
||||
(Use & REG_X)? 'X' : '_',
|
||||
(Use & REG_Y)? 'Y' : '_');
|
||||
#else
|
||||
fprintf (F, "\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* If this is a segment for a function, leave the function */
|
||||
|
Loading…
Reference in New Issue
Block a user