1
0
mirror of https://github.com/cc65/cc65.git synced 2024-06-28 19:29:53 +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:
cuz 2001-05-09 21:56:03 +00:00
parent 3ef00e7d2a
commit a1da98103a
7 changed files with 465 additions and 29 deletions

View File

@ -33,9 +33,11 @@
#include <stdlib.h>
#include <string.h>
/* common */
#include "chartype.h"
#include "check.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 */
/*****************************************************************************/
@ -108,8 +151,11 @@ CodeEntry* NewCodeEntry (const OPCDesc* D, am_t AM, const char* Arg, CodeLabel*
E->Size = GetInsnSize (E->OPC, E->AM);
E->Hints = 0;
E->Arg = GetArgCopy (Arg);
E->Num = 0;
E->Flags = 0;
if (NumArg (E->Arg, &E->Num)) {
E-> Flags = CEF_NUMARG;
} else {
E->Flags = 0;
}
E->Info = D->Info;
E->Use = D->Use;
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);
} else {
/* Some other instruction */
E->Use |= GetAMUseInfo (AM);
E->Use |= GetAMUseInfo (E->AM);
}
E->JumpTo = JumpTo;
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)
/* Check if both code entries are equal */
{

View File

@ -67,7 +67,7 @@ struct CodeEntry {
unsigned char Size; /* Estimated size */
unsigned char Hints; /* Hints for this entry */
char* Arg; /* Argument as string */
unsigned Num; /* Numeric argument */
unsigned long Num; /* Numeric argument */
unsigned short Flags; /* Flags */
unsigned char Info; /* Additional code info */
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);
/* 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);
/* Check if both code entries are equal */

View File

@ -33,13 +33,17 @@
#include <string.h>
/* common */
#include "abend.h"
#include "print.h"
/* cc65 */
#include "asmlabel.h"
#include "codeent.h"
#include "codeinfo.h"
#include "error.h"
#include "global.h"
#include "codeopt.h"
@ -56,10 +60,67 @@
*/
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;
/* 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.
*/
if (CodeEntryHasLabel (E1)) {
MoveCodeLabels (S, E1, T1);
MoveCodeLabels (S, E1, T1);
}
/* 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 */
/*****************************************************************************/
/* 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)
/* Run the optimizer */
{
typedef void (*OptFunc) (CodeSeg*);
unsigned Pass = 0;
/* Table with optimizer steps - are called in this order */
static const OptFunc OptFuncs [] = {
OptJumpCascades, /* Optimize jump cascades */
OptDeadJumps, /* Remove dead jumps */
OptDeadCode, /* Remove dead code */
OptRTS, /* Change jsr/rts to jmp */
OptJumpTarget, /* Optimize jump targets */
};
/* Print the name of the function we are working on */
if (S->Func) {
Print (stdout, 1, "Running optimizer for function `%s'\n", S->Func->Name);
} else {
Print (stdout, 1, "Running optimizer for global code segment\n");
}
/* Repeat all steps until there are no more changes */
do {
unsigned long Flags;
unsigned I;
unsigned I;
/* Reset the number of changes */
OptChanges = 0;
/* Keep the user hapy */
Print (stdout, 1, " Optimizer pass %u:\n", ++Pass);
/* Run all optimization steps */
Flags = 1UL;
for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
if ((OptDisable & Flags) == 0) {
OptFuncs[I] (S);
} else if (Verbosity > 0 || Debug) {
printf ("Optimizer pass %u skipped\n", I);
/* Print the name of the following optimizer step */
Print (stdout, 1, " %s:%*s", OptFuncs[I].Name,
(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);

View File

@ -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);
/* Run the optimizer */

View File

@ -52,6 +52,7 @@
/* cc65 */
#include "asmcode.h"
#include "compile.h"
#include "codeopt.h"
#include "cpu.h"
#include "error.h"
#include "global.h"
@ -103,6 +104,8 @@ static void Usage (void)
" --data-name seg\tSet the name of the DATA segment\n"
" --debug\t\tDebug mode\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"
" --include-dir dir\tSet an include directory search path\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)
/* Print usage information and exit */
{
@ -463,6 +482,8 @@ int main (int argc, char* argv[])
{ "--data-name", 1, OptDataName },
{ "--debug", 0, OptDebug },
{ "--debug-info", 0, OptDebugInfo },
{ "--disable-opt", 1, OptDisableOpt },
{ "--enable-opt", 1, OptEnableOpt, },
{ "--help", 0, OptHelp },
{ "--include-dir", 1, OptIncludeDir },
{ "--rodata-name", 1, OptRodataName },

View File

@ -96,9 +96,9 @@ static const OPCDesc OPCTable[OPC_COUNT] = {
{ OPC_JSR, "jsr", 3, REG_NONE, REG_NONE, OF_NONE },
{ OPC_JVC, "jvc", 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_LDX, "ldx", 0, REG_NONE, REG_X, OF_NONE },
{ OPC_LDY, "ldy", 0, REG_NONE, REG_Y, OF_NONE },
{ OPC_LDA, "lda", 0, REG_NONE, REG_A, OF_LOAD },
{ OPC_LDX, "ldx", 0, REG_NONE, REG_X, OF_LOAD },
{ OPC_LDY, "ldy", 0, REG_NONE, REG_Y, OF_LOAD },
{ OPC_LSR, "lsr", 0, REG_A, REG_A, OF_NONE },
{ OPC_NOP, "nop", 1, REG_NONE, REG_NONE, OF_NONE },
{ OPC_ORA, "ora", 0, REG_A, REG_A, OF_NONE },

View File

@ -156,6 +156,7 @@ typedef enum {
#define OF_UBRA 0x0001U /* Unconditional branch */
#define OF_CBRA 0x0002U /* Conditional branch */
#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_DEAD (OF_UBRA|OF_RET) /* Dead end - no exec behind this point */