mirror of
https://github.com/cc65/cc65.git
synced 2024-09-29 17:56:21 +00:00
Added backend optimizations
git-svn-id: svn://svn.cc65.org/cc65/trunk@722 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
parent
3ef00e7d2a
commit
a1da98103a
@ -33,9 +33,11 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
/* common */
|
/* common */
|
||||||
|
#include "chartype.h"
|
||||||
#include "check.h"
|
#include "check.h"
|
||||||
#include "xmalloc.h"
|
#include "xmalloc.h"
|
||||||
|
|
||||||
@ -90,6 +92,47 @@ static char* GetArgCopy (const char* Arg)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static int NumArg (const char* Arg, unsigned long* Num)
|
||||||
|
/* If the given argument is numerical, convert it and return true. Otherwise
|
||||||
|
* set Num to zero and return false.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
char* End;
|
||||||
|
unsigned long Val;
|
||||||
|
|
||||||
|
/* Determine the base */
|
||||||
|
int Base = 10;
|
||||||
|
if (*Arg == '$') {
|
||||||
|
++Arg;
|
||||||
|
Base = 16;
|
||||||
|
} else if (*Arg == '%') {
|
||||||
|
++Arg;
|
||||||
|
Base = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert the value. strtol is not exactly what we want here, but it's
|
||||||
|
* cheap and may be replaced by something fancier later.
|
||||||
|
*/
|
||||||
|
Val = strtoul (Arg, &End, Base);
|
||||||
|
|
||||||
|
/* Check if the conversion was successful */
|
||||||
|
if (*End != '\0') {
|
||||||
|
|
||||||
|
/* Could not convert */
|
||||||
|
*Num = 0;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
/* Conversion ok */
|
||||||
|
*Num = Val;
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* Code */
|
/* Code */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@ -108,8 +151,11 @@ CodeEntry* NewCodeEntry (const OPCDesc* D, am_t AM, const char* Arg, CodeLabel*
|
|||||||
E->Size = GetInsnSize (E->OPC, E->AM);
|
E->Size = GetInsnSize (E->OPC, E->AM);
|
||||||
E->Hints = 0;
|
E->Hints = 0;
|
||||||
E->Arg = GetArgCopy (Arg);
|
E->Arg = GetArgCopy (Arg);
|
||||||
E->Num = 0;
|
if (NumArg (E->Arg, &E->Num)) {
|
||||||
E->Flags = 0;
|
E-> Flags = CEF_NUMARG;
|
||||||
|
} else {
|
||||||
|
E->Flags = 0;
|
||||||
|
}
|
||||||
E->Info = D->Info;
|
E->Info = D->Info;
|
||||||
E->Use = D->Use;
|
E->Use = D->Use;
|
||||||
E->Chg = D->Chg;
|
E->Chg = D->Chg;
|
||||||
@ -118,7 +164,7 @@ CodeEntry* NewCodeEntry (const OPCDesc* D, am_t AM, const char* Arg, CodeLabel*
|
|||||||
GetFuncInfo (E->Arg, &E->Use, &E->Chg);
|
GetFuncInfo (E->Arg, &E->Use, &E->Chg);
|
||||||
} else {
|
} else {
|
||||||
/* Some other instruction */
|
/* Some other instruction */
|
||||||
E->Use |= GetAMUseInfo (AM);
|
E->Use |= GetAMUseInfo (E->AM);
|
||||||
}
|
}
|
||||||
E->JumpTo = JumpTo;
|
E->JumpTo = JumpTo;
|
||||||
InitCollection (&E->Labels);
|
InitCollection (&E->Labels);
|
||||||
@ -149,6 +195,31 @@ void FreeCodeEntry (CodeEntry* E)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void ReplaceOPC (CodeEntry* E, opc_t OPC)
|
||||||
|
/* Replace the opcode of the instruction. This will also replace related info,
|
||||||
|
* Size, Use and Chg, but it will NOT update any arguments or labels.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
/* Get the opcode descriptor */
|
||||||
|
const OPCDesc* D = GetOPCDesc (OPC);
|
||||||
|
|
||||||
|
/* Replace the opcode */
|
||||||
|
E->OPC = OPC;
|
||||||
|
E->Size = GetInsnSize (E->OPC, E->AM);
|
||||||
|
E->Info = D->Info;
|
||||||
|
E->Use = D->Use;
|
||||||
|
E->Chg = D->Chg;
|
||||||
|
if (E->OPC == OPC_JSR) {
|
||||||
|
/* A subroutine call */
|
||||||
|
GetFuncInfo (E->Arg, &E->Use, &E->Chg);
|
||||||
|
} else {
|
||||||
|
/* Some other instruction */
|
||||||
|
E->Use |= GetAMUseInfo (E->AM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int CodeEntriesAreEqual (const CodeEntry* E1, const CodeEntry* E2)
|
int CodeEntriesAreEqual (const CodeEntry* E1, const CodeEntry* E2)
|
||||||
/* Check if both code entries are equal */
|
/* Check if both code entries are equal */
|
||||||
{
|
{
|
||||||
|
@ -67,7 +67,7 @@ struct CodeEntry {
|
|||||||
unsigned char Size; /* Estimated size */
|
unsigned char Size; /* Estimated size */
|
||||||
unsigned char Hints; /* Hints for this entry */
|
unsigned char Hints; /* Hints for this entry */
|
||||||
char* Arg; /* Argument as string */
|
char* Arg; /* Argument as string */
|
||||||
unsigned Num; /* Numeric argument */
|
unsigned long Num; /* Numeric argument */
|
||||||
unsigned short Flags; /* Flags */
|
unsigned short Flags; /* Flags */
|
||||||
unsigned char Info; /* Additional code info */
|
unsigned char Info; /* Additional code info */
|
||||||
unsigned char Use; /* Registers used */
|
unsigned char Use; /* Registers used */
|
||||||
@ -90,6 +90,11 @@ CodeEntry* NewCodeEntry (const OPCDesc* D, am_t AM, const char* Arg, CodeLabel*
|
|||||||
void FreeCodeEntry (CodeEntry* E);
|
void FreeCodeEntry (CodeEntry* E);
|
||||||
/* Free the given code entry */
|
/* Free the given code entry */
|
||||||
|
|
||||||
|
void ReplaceOPC (CodeEntry* E, opc_t OPC);
|
||||||
|
/* Replace the opcode of the instruction. This will also replace related info,
|
||||||
|
* Size, Use and Chg, but it will NOT update any arguments or labels.
|
||||||
|
*/
|
||||||
|
|
||||||
int CodeEntriesAreEqual (const CodeEntry* E1, const CodeEntry* E2);
|
int CodeEntriesAreEqual (const CodeEntry* E1, const CodeEntry* E2);
|
||||||
/* Check if both code entries are equal */
|
/* Check if both code entries are equal */
|
||||||
|
|
||||||
|
@ -33,13 +33,17 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
/* common */
|
/* common */
|
||||||
|
#include "abend.h"
|
||||||
#include "print.h"
|
#include "print.h"
|
||||||
|
|
||||||
/* cc65 */
|
/* cc65 */
|
||||||
#include "asmlabel.h"
|
#include "asmlabel.h"
|
||||||
#include "codeent.h"
|
#include "codeent.h"
|
||||||
#include "codeinfo.h"
|
#include "codeinfo.h"
|
||||||
|
#include "error.h"
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
#include "codeopt.h"
|
#include "codeopt.h"
|
||||||
|
|
||||||
@ -56,10 +60,67 @@
|
|||||||
*/
|
*/
|
||||||
static unsigned OptChanges;
|
static unsigned OptChanges;
|
||||||
|
|
||||||
|
/* Defines for the conditions in a compare */
|
||||||
|
typedef enum {
|
||||||
|
CMP_INV = -1,
|
||||||
|
CMP_EQ,
|
||||||
|
CMP_NE,
|
||||||
|
CMP_GT,
|
||||||
|
CMP_GE,
|
||||||
|
CMP_LT,
|
||||||
|
CMP_LE,
|
||||||
|
CMP_UGT,
|
||||||
|
CMP_UGE,
|
||||||
|
CMP_ULT,
|
||||||
|
CMP_ULE
|
||||||
|
} cmp_t;
|
||||||
|
|
||||||
|
/* Table with the compare suffixes */
|
||||||
|
static const char CmpSuffixTab [][4] = {
|
||||||
|
"eq", "ne", "gt", "ge", "lt", "le", "ugt", "uge", "ult", "ule"
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Table used to invert a condition, indexed by condition */
|
||||||
|
static const unsigned char CmpInvertTab [] = {
|
||||||
|
CMP_NE, CMP_EQ,
|
||||||
|
CMP_LE, CMP_LT, CMP_GE, CMP_GT,
|
||||||
|
CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Table to show which compares are signed (use the N flag) */
|
||||||
|
static const char CmpSignedTab [] = {
|
||||||
|
0, 0, 1, 1, 1, 1, 0, 0, 0, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* Remove dead jumps */
|
/* Helper functions */
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static cmp_t FindCmpCond (const char* Suffix)
|
||||||
|
/* Map a condition suffix to a code. Return the code or CMP_INV on failure */
|
||||||
|
{
|
||||||
|
int I;
|
||||||
|
|
||||||
|
/* Linear search */
|
||||||
|
for (I = 0; I < sizeof (CmpSuffixTab) / sizeof (CmpSuffixTab [0]); ++I) {
|
||||||
|
if (strcmp (Suffix, CmpSuffixTab [I]) == 0) {
|
||||||
|
/* Found */
|
||||||
|
return I;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Not found */
|
||||||
|
return CMP_INV;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* Remove dead jumps */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
@ -109,7 +170,7 @@ static void OptDeadJumps (CodeSeg* S)
|
|||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* Remove dead code */
|
/* Remove dead code */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
@ -237,7 +298,7 @@ static void OptJumpCascades (CodeSeg* S)
|
|||||||
++OptChanges;
|
++OptChanges;
|
||||||
|
|
||||||
/* Done */
|
/* Done */
|
||||||
goto NextEntry;
|
continue;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,7 +321,7 @@ NextEntry:
|
|||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* Optimize jsr/rts */
|
/* Optimize jsr/rts */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
@ -377,7 +438,7 @@ static void OptJumpTarget (CodeSeg* S)
|
|||||||
* move references to this label to the new label.
|
* move references to this label to the new label.
|
||||||
*/
|
*/
|
||||||
if (CodeEntryHasLabel (E1)) {
|
if (CodeEntryHasLabel (E1)) {
|
||||||
MoveCodeLabels (S, E1, T1);
|
MoveCodeLabels (S, E1, T1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Remove the entry preceeding the jump */
|
/* Remove the entry preceeding the jump */
|
||||||
@ -398,44 +459,315 @@ NextEntry:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* Remove conditional jumps never taken */
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void OptDeadCondBranches (CodeSeg* S)
|
||||||
|
/* If an immidiate load of a register is followed by a conditional jump that
|
||||||
|
* is never taken because the load of the register sets the flags in such a
|
||||||
|
* manner, remove the conditional branch.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
unsigned I;
|
||||||
|
|
||||||
|
/* Get the number of entries, bail out if we have not enough */
|
||||||
|
unsigned Count = GetCodeEntryCount (S);
|
||||||
|
if (Count < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Walk over the entries */
|
||||||
|
I = 0;
|
||||||
|
while (I < Count-1) {
|
||||||
|
|
||||||
|
/* Get next entry */
|
||||||
|
CodeEntry* E = GetCodeEntry (S, I);
|
||||||
|
|
||||||
|
/* Check if it's a register load */
|
||||||
|
if ((E->Info & OF_LOAD) != 0 && E->AM == AM_IMM && (E->Flags & CEF_NUMARG) != 0) {
|
||||||
|
|
||||||
|
bc_t BC;
|
||||||
|
|
||||||
|
/* Immidiate register load, get next instruction */
|
||||||
|
CodeEntry* N = GetCodeEntry (S, I+1);
|
||||||
|
|
||||||
|
/* Check if the following insn is a conditional branch or if it
|
||||||
|
* has a label attached.
|
||||||
|
*/
|
||||||
|
if ((N->Info & OF_CBRA) == 0 || CodeEntryHasLabel (E)) {
|
||||||
|
/* No conditional jump or label attached, bail out */
|
||||||
|
goto NextEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the branch condition */
|
||||||
|
BC = GetBranchCond (N->OPC);
|
||||||
|
|
||||||
|
/* Check the argument against the branch condition */
|
||||||
|
if ((BC == BC_EQ && E->Num != 0) ||
|
||||||
|
(BC == BC_NE && E->Num == 0) ||
|
||||||
|
(BC == BC_PL && (E->Num & 0x80) != 0) ||
|
||||||
|
(BC == BC_MI && (E->Num & 0x80) == 0)) {
|
||||||
|
|
||||||
|
/* Remove the conditional branch */
|
||||||
|
DelCodeEntry (S, I+1);
|
||||||
|
--Count;
|
||||||
|
|
||||||
|
/* Remember, we had changes */
|
||||||
|
++OptChanges;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NextEntry:
|
||||||
|
/* Next entry */
|
||||||
|
++I;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* Remove calls to the bool transformer subroutines */
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void OptBoolTransforms (CodeSeg* S)
|
||||||
|
/* Try to remove the call to boolean transformer routines where the call is
|
||||||
|
* not really needed.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
unsigned I;
|
||||||
|
|
||||||
|
/* Get the number of entries, bail out if we have not enough */
|
||||||
|
unsigned Count = GetCodeEntryCount (S);
|
||||||
|
if (Count < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Walk over the entries */
|
||||||
|
I = 0;
|
||||||
|
while (I < Count-1) {
|
||||||
|
|
||||||
|
/* Get next entry */
|
||||||
|
CodeEntry* E = GetCodeEntry (S, I);
|
||||||
|
|
||||||
|
/* Check for a boolean transformer */
|
||||||
|
if (E->OPC == OPC_JSR && strncmp (E->Arg, "bool", 4) == 0) {
|
||||||
|
|
||||||
|
cmp_t Cond;
|
||||||
|
|
||||||
|
/* Get the next entry */
|
||||||
|
CodeEntry* N = GetCodeEntry (S, I+1);
|
||||||
|
|
||||||
|
/* Check if this is a conditional branch */
|
||||||
|
if ((N->Info & OF_CBRA) == 0) {
|
||||||
|
/* No conditional branch, bail out */
|
||||||
|
goto NextEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
* the condition is not met, jne jumps if the condition is met.
|
||||||
|
*/
|
||||||
|
Cond = FindCmpCond (E->Arg + 4);
|
||||||
|
if (Cond == CMP_INV) {
|
||||||
|
/* Unknown function */
|
||||||
|
goto NextEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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:
|
||||||
|
/* Not now ### */
|
||||||
|
goto NextEntry;
|
||||||
|
|
||||||
|
case CMP_GE:
|
||||||
|
ReplaceOPC (N, OPC_JPL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CMP_LT:
|
||||||
|
ReplaceOPC (N, OPC_JMI);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CMP_LE:
|
||||||
|
/* Not now ### */
|
||||||
|
goto NextEntry;
|
||||||
|
|
||||||
|
case CMP_UGT:
|
||||||
|
/* Not now ### */
|
||||||
|
goto NextEntry;
|
||||||
|
|
||||||
|
case CMP_UGE:
|
||||||
|
ReplaceOPC (N, OPC_JCS);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CMP_ULT:
|
||||||
|
ReplaceOPC (N, OPC_JCC);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CMP_ULE:
|
||||||
|
/* Not now ### */
|
||||||
|
goto NextEntry;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Internal ("Unknown jump condition: %d", Cond);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove the call to the bool transformer */
|
||||||
|
DelCodeEntry (S, I);
|
||||||
|
--Count;
|
||||||
|
|
||||||
|
/* Remember, we had changes */
|
||||||
|
++OptChanges;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
NextEntry:
|
||||||
|
/* Next entry */
|
||||||
|
++I;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* Code */
|
/* Code */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Table with all the optimization functions */
|
||||||
|
typedef struct OptFunc OptFunc;
|
||||||
|
struct OptFunc {
|
||||||
|
void (*Func) (CodeSeg*); /* Optimizer function */
|
||||||
|
const char* Name; /* Name of optimizer step */
|
||||||
|
char Disabled; /* True if pass disabled */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Table with optimizer steps - are called in this order */
|
||||||
|
static OptFunc OptFuncs [] = {
|
||||||
|
/* Optimize jump cascades */
|
||||||
|
{ OptJumpCascades, "OptJumpCascades", 0 },
|
||||||
|
/* Remove dead jumps */
|
||||||
|
{ OptDeadJumps, "OptDeadJumps", 0 },
|
||||||
|
/* Change jsr/rts to jmp */
|
||||||
|
{ OptRTS, "OptRTS", 0 },
|
||||||
|
/* Remove dead code */
|
||||||
|
{ OptDeadCode, "OptDeadCode", 0 },
|
||||||
|
/* Optimize jump targets */
|
||||||
|
{ OptJumpTarget, "OptJumpTarget", 0 },
|
||||||
|
/* Remove dead conditional branches */
|
||||||
|
{ OptDeadCondBranches, "OptDeadCondBranches", 0 },
|
||||||
|
/* Remove calls to the bool transformer subroutines */
|
||||||
|
{ OptBoolTransforms, "OptBoolTransforms", 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static OptFunc* FindOptStep (const char* Name)
|
||||||
|
/* Find an optimizer step by name in the table and return a pointer. Print an
|
||||||
|
* error and cann AbEnd if not found.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
unsigned I;
|
||||||
|
|
||||||
|
/* Run all optimization steps */
|
||||||
|
for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
|
||||||
|
if (strcmp (OptFuncs[I].Name, Name) == 0) {
|
||||||
|
/* Found */
|
||||||
|
return OptFuncs+I;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Not found */
|
||||||
|
AbEnd ("Optimization step `%s' not found", Name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void DisableOpt (const char* Name)
|
||||||
|
/* Disable the optimization with the given name */
|
||||||
|
{
|
||||||
|
OptFunc* F = FindOptStep (Name);
|
||||||
|
F->Disabled = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void EnableOpt (const char* Name)
|
||||||
|
/* Enable the optimization with the given name */
|
||||||
|
{
|
||||||
|
OptFunc* F = FindOptStep (Name);
|
||||||
|
F->Disabled = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void RunOpt (CodeSeg* S)
|
void RunOpt (CodeSeg* S)
|
||||||
/* Run the optimizer */
|
/* Run the optimizer */
|
||||||
{
|
{
|
||||||
typedef void (*OptFunc) (CodeSeg*);
|
unsigned Pass = 0;
|
||||||
|
|
||||||
/* Table with optimizer steps - are called in this order */
|
/* Print the name of the function we are working on */
|
||||||
static const OptFunc OptFuncs [] = {
|
if (S->Func) {
|
||||||
OptJumpCascades, /* Optimize jump cascades */
|
Print (stdout, 1, "Running optimizer for function `%s'\n", S->Func->Name);
|
||||||
OptDeadJumps, /* Remove dead jumps */
|
} else {
|
||||||
OptDeadCode, /* Remove dead code */
|
Print (stdout, 1, "Running optimizer for global code segment\n");
|
||||||
OptRTS, /* Change jsr/rts to jmp */
|
}
|
||||||
OptJumpTarget, /* Optimize jump targets */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Repeat all steps until there are no more changes */
|
/* Repeat all steps until there are no more changes */
|
||||||
do {
|
do {
|
||||||
|
|
||||||
unsigned long Flags;
|
unsigned I;
|
||||||
unsigned I;
|
|
||||||
|
|
||||||
/* Reset the number of changes */
|
/* Reset the number of changes */
|
||||||
OptChanges = 0;
|
OptChanges = 0;
|
||||||
|
|
||||||
|
/* Keep the user hapy */
|
||||||
|
Print (stdout, 1, " Optimizer pass %u:\n", ++Pass);
|
||||||
|
|
||||||
/* Run all optimization steps */
|
/* Run all optimization steps */
|
||||||
Flags = 1UL;
|
|
||||||
for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
|
for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
|
||||||
if ((OptDisable & Flags) == 0) {
|
|
||||||
OptFuncs[I] (S);
|
/* Print the name of the following optimizer step */
|
||||||
} else if (Verbosity > 0 || Debug) {
|
Print (stdout, 1, " %s:%*s", OptFuncs[I].Name,
|
||||||
printf ("Optimizer pass %u skipped\n", I);
|
(int) (30-strlen(OptFuncs[I].Name)), "");
|
||||||
|
|
||||||
|
/* Check if the step is disabled */
|
||||||
|
if (OptFuncs[I].Disabled) {
|
||||||
|
Print (stdout, 1, "Disabled\n");
|
||||||
|
} else {
|
||||||
|
unsigned Changes = OptChanges;
|
||||||
|
OptFuncs[I].Func (S);
|
||||||
|
Changes = OptChanges - Changes;
|
||||||
|
Print (stdout, 1, "%u Changes\n", Changes);
|
||||||
}
|
}
|
||||||
Flags <<= 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} while (OptChanges > 0);
|
} while (OptChanges > 0);
|
||||||
|
@ -55,6 +55,12 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void DisableOpt (const char* Name);
|
||||||
|
/* Disable the optimization with the given name */
|
||||||
|
|
||||||
|
void EnableOpt (const char* Name);
|
||||||
|
/* Enable the optimization with the given name */
|
||||||
|
|
||||||
void RunOpt (CodeSeg* S);
|
void RunOpt (CodeSeg* S);
|
||||||
/* Run the optimizer */
|
/* Run the optimizer */
|
||||||
|
|
||||||
|
@ -52,6 +52,7 @@
|
|||||||
/* cc65 */
|
/* cc65 */
|
||||||
#include "asmcode.h"
|
#include "asmcode.h"
|
||||||
#include "compile.h"
|
#include "compile.h"
|
||||||
|
#include "codeopt.h"
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
@ -103,6 +104,8 @@ static void Usage (void)
|
|||||||
" --data-name seg\tSet the name of the DATA segment\n"
|
" --data-name seg\tSet the name of the DATA segment\n"
|
||||||
" --debug\t\tDebug mode\n"
|
" --debug\t\tDebug mode\n"
|
||||||
" --debug-info\t\tAdd debug info to object file\n"
|
" --debug-info\t\tAdd debug info to object file\n"
|
||||||
|
" --disable-opt name\tDisable an optimization step\n"
|
||||||
|
" --enable-opt name\tEnable an optimization step\n"
|
||||||
" --help\t\tHelp (this text)\n"
|
" --help\t\tHelp (this text)\n"
|
||||||
" --include-dir dir\tSet an include directory search path\n"
|
" --include-dir dir\tSet an include directory search path\n"
|
||||||
" --rodata-name seg\tSet the name of the RODATA segment\n"
|
" --rodata-name seg\tSet the name of the RODATA segment\n"
|
||||||
@ -377,6 +380,22 @@ static void OptDebugInfo (const char* Opt, const char* Arg)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void OptDisableOpt (const char* Opt, const char* Arg)
|
||||||
|
/* Disable an optimization step */
|
||||||
|
{
|
||||||
|
DisableOpt (Arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void OptEnableOpt (const char* Opt, const char* Arg)
|
||||||
|
/* Enable an optimization step */
|
||||||
|
{
|
||||||
|
EnableOpt (Arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void OptHelp (const char* Opt, const char* Arg)
|
static void OptHelp (const char* Opt, const char* Arg)
|
||||||
/* Print usage information and exit */
|
/* Print usage information and exit */
|
||||||
{
|
{
|
||||||
@ -463,6 +482,8 @@ int main (int argc, char* argv[])
|
|||||||
{ "--data-name", 1, OptDataName },
|
{ "--data-name", 1, OptDataName },
|
||||||
{ "--debug", 0, OptDebug },
|
{ "--debug", 0, OptDebug },
|
||||||
{ "--debug-info", 0, OptDebugInfo },
|
{ "--debug-info", 0, OptDebugInfo },
|
||||||
|
{ "--disable-opt", 1, OptDisableOpt },
|
||||||
|
{ "--enable-opt", 1, OptEnableOpt, },
|
||||||
{ "--help", 0, OptHelp },
|
{ "--help", 0, OptHelp },
|
||||||
{ "--include-dir", 1, OptIncludeDir },
|
{ "--include-dir", 1, OptIncludeDir },
|
||||||
{ "--rodata-name", 1, OptRodataName },
|
{ "--rodata-name", 1, OptRodataName },
|
||||||
|
@ -96,9 +96,9 @@ static const OPCDesc OPCTable[OPC_COUNT] = {
|
|||||||
{ OPC_JSR, "jsr", 3, REG_NONE, REG_NONE, OF_NONE },
|
{ OPC_JSR, "jsr", 3, REG_NONE, REG_NONE, OF_NONE },
|
||||||
{ OPC_JVC, "jvc", 5, REG_NONE, REG_NONE, OF_CBRA },
|
{ OPC_JVC, "jvc", 5, REG_NONE, REG_NONE, OF_CBRA },
|
||||||
{ OPC_JVS, "jvs", 5, REG_NONE, REG_NONE, OF_CBRA },
|
{ OPC_JVS, "jvs", 5, REG_NONE, REG_NONE, OF_CBRA },
|
||||||
{ OPC_LDA, "lda", 0, REG_NONE, REG_A, OF_NONE },
|
{ OPC_LDA, "lda", 0, REG_NONE, REG_A, OF_LOAD },
|
||||||
{ OPC_LDX, "ldx", 0, REG_NONE, REG_X, OF_NONE },
|
{ OPC_LDX, "ldx", 0, REG_NONE, REG_X, OF_LOAD },
|
||||||
{ OPC_LDY, "ldy", 0, REG_NONE, REG_Y, OF_NONE },
|
{ OPC_LDY, "ldy", 0, REG_NONE, REG_Y, OF_LOAD },
|
||||||
{ OPC_LSR, "lsr", 0, REG_A, REG_A, OF_NONE },
|
{ OPC_LSR, "lsr", 0, REG_A, REG_A, OF_NONE },
|
||||||
{ OPC_NOP, "nop", 1, REG_NONE, REG_NONE, OF_NONE },
|
{ OPC_NOP, "nop", 1, REG_NONE, REG_NONE, OF_NONE },
|
||||||
{ OPC_ORA, "ora", 0, REG_A, REG_A, OF_NONE },
|
{ OPC_ORA, "ora", 0, REG_A, REG_A, OF_NONE },
|
||||||
|
@ -156,6 +156,7 @@ typedef enum {
|
|||||||
#define OF_UBRA 0x0001U /* Unconditional branch */
|
#define OF_UBRA 0x0001U /* Unconditional branch */
|
||||||
#define OF_CBRA 0x0002U /* Conditional branch */
|
#define OF_CBRA 0x0002U /* Conditional branch */
|
||||||
#define OF_RET 0x0004U /* Return from function */
|
#define OF_RET 0x0004U /* Return from function */
|
||||||
|
#define OF_LOAD 0x0008U /* Register load */
|
||||||
#define OF_BRA (OF_UBRA|OF_CBRA) /* Operation is a jump/branch */
|
#define OF_BRA (OF_UBRA|OF_CBRA) /* Operation is a jump/branch */
|
||||||
#define OF_DEAD (OF_UBRA|OF_RET) /* Dead end - no exec behind this point */
|
#define OF_DEAD (OF_UBRA|OF_RET) /* Dead end - no exec behind this point */
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user