mirror of
https://github.com/cc65/cc65.git
synced 2024-12-23 19:29:37 +00:00
Moved some of the currently existing into a separate module.
git-svn-id: svn://svn.cc65.org/cc65/trunk@723 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
parent
a1da98103a
commit
c9cb564b9b
@ -3466,6 +3466,14 @@ void g_lt (unsigned flags, unsigned long val)
|
||||
break;
|
||||
|
||||
case CF_LONG:
|
||||
if ((flags & CF_UNSIGNED) == 0 && val == 0) {
|
||||
/* If we have a signed compare against zero, we only need to
|
||||
* test the high byte.
|
||||
*/
|
||||
AddCodeLine ("lda sreg+1");
|
||||
AddCodeLine ("jsr boollt");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -3475,7 +3483,7 @@ void g_lt (unsigned flags, unsigned long val)
|
||||
/* If we go here, we didn't emit code. Push the lhs on stack and fall
|
||||
* into the normal, non-optimized stuff.
|
||||
*/
|
||||
g_push (flags & ~CF_CONST, 0);
|
||||
g_push (flags & ~CF_CONST, 0);
|
||||
|
||||
}
|
||||
|
||||
@ -3589,27 +3597,27 @@ void g_gt (unsigned flags, unsigned long val)
|
||||
if (flags & CF_UNSIGNED) {
|
||||
/* If we have a compare > 0, we will replace it by
|
||||
* != 0 here, since both are identical but the latter
|
||||
* is easier to optimize.
|
||||
*/
|
||||
if ((val & 0xFFFF) == 0) {
|
||||
AddCodeLine ("stx tmp1");
|
||||
AddCodeLine ("ora tmp1");
|
||||
AddCodeLine ("jsr boolne");
|
||||
} else {
|
||||
* is easier to optimize.
|
||||
*/
|
||||
if ((val & 0xFFFF) == 0) {
|
||||
AddCodeLine ("stx tmp1");
|
||||
AddCodeLine ("ora tmp1");
|
||||
AddCodeLine ("jsr boolne");
|
||||
} else {
|
||||
AddCodeLine ("cpx #$%02X", (unsigned char)(val >> 8));
|
||||
AddCodeLine ("bne *+4");
|
||||
AddCodeLine ("cmp #$%02X", (unsigned char)val);
|
||||
AddCodeLine ("bne *+4");
|
||||
AddCodeLine ("cmp #$%02X", (unsigned char)val);
|
||||
AddCodeLine ("jsr boolugt");
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case CF_LONG:
|
||||
break;
|
||||
break;
|
||||
|
||||
default:
|
||||
typeerror (flags);
|
||||
typeerror (flags);
|
||||
}
|
||||
|
||||
/* If we go here, we didn't emit code. Push the lhs on stack and fall
|
||||
@ -3662,20 +3670,37 @@ void g_ge (unsigned flags, unsigned long val)
|
||||
/* FALLTHROUGH */
|
||||
|
||||
case CF_INT:
|
||||
if ((flags & CF_UNSIGNED) == 0 && val == 0) {
|
||||
/* If we have a signed compare against zero, we only need to
|
||||
* test the high byte.
|
||||
*/
|
||||
AddCodeLine ("txa");
|
||||
AddCodeLine ("jsr boolge");
|
||||
return;
|
||||
}
|
||||
/* Direct code only for unsigned data types */
|
||||
if (flags & CF_UNSIGNED) {
|
||||
AddCodeLine ("cpx #$%02X", (unsigned char)(val >> 8));
|
||||
AddCodeLine ("bne *+4");
|
||||
AddCodeLine ("cmp #$%02X", (unsigned char)val);
|
||||
AddCodeLine ("jsr booluge");
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CF_LONG:
|
||||
break;
|
||||
if ((flags & CF_UNSIGNED) == 0 && val == 0) {
|
||||
/* If we have a signed compare against zero, we only need to
|
||||
* test the high byte.
|
||||
*/
|
||||
AddCodeLine ("lda sreg+1");
|
||||
AddCodeLine ("jsr boolge");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
typeerror (flags);
|
||||
typeerror (flags);
|
||||
}
|
||||
|
||||
/* If we go here, we didn't emit code. Push the lhs on stack and fall
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "asmlabel.h"
|
||||
#include "codeent.h"
|
||||
#include "codeinfo.h"
|
||||
#include "coptind.h"
|
||||
#include "error.h"
|
||||
#include "global.h"
|
||||
#include "codeopt.h"
|
||||
@ -55,11 +56,6 @@
|
||||
|
||||
|
||||
|
||||
/* Counter for the number of changes in one run. The optimizer process is
|
||||
* repeated until there are no more changes.
|
||||
*/
|
||||
static unsigned OptChanges;
|
||||
|
||||
/* Defines for the conditions in a compare */
|
||||
typedef enum {
|
||||
CMP_INV = -1,
|
||||
@ -119,434 +115,24 @@ static cmp_t FindCmpCond (const char* Suffix)
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Remove dead jumps */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
static void OptDeadJumps (CodeSeg* S)
|
||||
/* Remove dead jumps (jumps to the next instruction) */
|
||||
{
|
||||
CodeEntry* E;
|
||||
unsigned I;
|
||||
|
||||
/* Get the number of entries, bail out if we have less than two entries */
|
||||
unsigned Count = GetCodeEntryCount (S);
|
||||
if (Count < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Walk over all entries minus the last one */
|
||||
I = 0;
|
||||
while (I < Count-1) {
|
||||
|
||||
/* Get the next entry */
|
||||
E = GetCodeEntry (S, I);
|
||||
|
||||
/* Check if it's a branch, if it has a local target, and if the target
|
||||
* is the next instruction.
|
||||
*/
|
||||
if (E->AM == AM_BRA && E->JumpTo && E->JumpTo->Owner == GetCodeEntry (S, I+1)) {
|
||||
|
||||
/* Delete the dead jump */
|
||||
DelCodeEntry (S, I);
|
||||
|
||||
/* Keep the number of entries updated */
|
||||
--Count;
|
||||
|
||||
/* Remember, we had changes */
|
||||
++OptChanges;
|
||||
|
||||
} else {
|
||||
|
||||
/* Next entry */
|
||||
++I;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Remove dead code */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
static void OptDeadCode (CodeSeg* S)
|
||||
/* Remove dead code (code that follows an unconditional jump or an rts/rti
|
||||
* and has no label)
|
||||
*/
|
||||
{
|
||||
unsigned I;
|
||||
|
||||
/* Get the number of entries, bail out if we have less than two entries */
|
||||
unsigned Count = GetCodeEntryCount (S);
|
||||
if (Count < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Walk over all entries minus the last one */
|
||||
I = 0;
|
||||
while (I < Count-1) {
|
||||
|
||||
/* Get this entry */
|
||||
CodeEntry* E = GetCodeEntry (S, I);
|
||||
|
||||
/* Check if it's an unconditional branch, and if the next entry has
|
||||
* no labels attached
|
||||
*/
|
||||
if ((E->Info & OF_DEAD) != 0 && !CodeEntryHasLabel (GetCodeEntry (S, I+1))) {
|
||||
|
||||
/* Delete the next entry */
|
||||
DelCodeEntry (S, I+1);
|
||||
|
||||
/* Keep the number of entries updated */
|
||||
--Count;
|
||||
|
||||
/* Remember, we had changes */
|
||||
++OptChanges;
|
||||
|
||||
} else {
|
||||
|
||||
/* Next entry */
|
||||
++I;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Optimize jump cascades */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
static void OptJumpCascades (CodeSeg* S)
|
||||
/* Optimize jump cascades (jumps to jumps). In such a case, the jump is
|
||||
* replaced by a jump to the final location. This will in some cases produce
|
||||
* worse code, because some jump targets are no longer reachable by short
|
||||
* branches, but this is quite rare, so there are more advantages than
|
||||
* disadvantages.
|
||||
*/
|
||||
{
|
||||
unsigned I;
|
||||
|
||||
/* Get the number of entries, bail out if we have no entries */
|
||||
unsigned Count = GetCodeEntryCount (S);
|
||||
if (Count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Walk over all entries */
|
||||
I = 0;
|
||||
while (I < Count) {
|
||||
|
||||
/* Get this entry */
|
||||
CodeEntry* E = GetCodeEntry (S, I);
|
||||
|
||||
/* Check if it's a branch, if it has a jump label, and if this jump
|
||||
* label is not attached to the instruction itself.
|
||||
*/
|
||||
if ((E->Info & OF_BRA) != 0 && E->JumpTo != 0 && E->JumpTo->Owner != E) {
|
||||
|
||||
/* Get the label this insn is branching to */
|
||||
CodeLabel* OldLabel = E->JumpTo;
|
||||
|
||||
/* Get the entry we're branching to */
|
||||
CodeEntry* N = OldLabel->Owner;
|
||||
|
||||
/* If the entry we're branching to is not itself a branch, it is
|
||||
* not what we're searching for.
|
||||
*/
|
||||
if ((N->Info & OF_BRA) == 0) {
|
||||
goto NextEntry;
|
||||
}
|
||||
|
||||
/* Check if we can use the final target label. This is the case,
|
||||
* if the target branch is an absolut branch, or if it is a
|
||||
* conditional branch checking the same condition as the first one.
|
||||
*/
|
||||
if ((N->Info & OF_UBRA) != 0 ||
|
||||
((E->Info & OF_CBRA) != 0 &&
|
||||
GetBranchCond (E->OPC) == GetBranchCond (N->OPC))) {
|
||||
|
||||
/* This is a jump cascade and we may jump to the final target.
|
||||
* If we have a label, move the reference to this label. If
|
||||
* we don't have a label, use the argument instead.
|
||||
*/
|
||||
if (N->JumpTo) {
|
||||
/* Move the reference to the new insn */
|
||||
MoveCodeLabelRef (S, E, N->JumpTo);
|
||||
} else {
|
||||
/* Remove the reference to the old label */
|
||||
RemoveCodeLabelRef (S, E);
|
||||
}
|
||||
|
||||
/* Use the new argument */
|
||||
CodeEntrySetArg (E, N->Arg);
|
||||
|
||||
/* Use the usage information from the new instruction */
|
||||
E->Use = N->Use;
|
||||
E->Chg = N->Chg;
|
||||
|
||||
/* Remember, we had changes */
|
||||
++OptChanges;
|
||||
|
||||
/* Done */
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
/* Check if both are conditional branches, and the condition of
|
||||
* the second is the inverse of that of the first. In this case,
|
||||
* the second branch will never be taken, and we may jump directly
|
||||
* to the instruction behind this one.
|
||||
*/
|
||||
goto NextEntry;
|
||||
|
||||
}
|
||||
|
||||
NextEntry:
|
||||
/* Next entry */
|
||||
++I;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Optimize jsr/rts */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
static void OptRTS (CodeSeg* S)
|
||||
/* Optimize subroutine calls followed by an RTS. The subroutine call will get
|
||||
* replaced by a jump. Don't bother to delete the RTS if it does not have a
|
||||
* label, the dead code elimination should take care of it.
|
||||
*/
|
||||
{
|
||||
unsigned I;
|
||||
|
||||
/* Get the number of entries, bail out if we have less than 2 entries */
|
||||
unsigned Count = GetCodeEntryCount (S);
|
||||
if (Count < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Walk over all entries minus the last one */
|
||||
I = 0;
|
||||
while (I < Count-1) {
|
||||
|
||||
/* Get this entry */
|
||||
CodeEntry* E = GetCodeEntry (S, I);
|
||||
|
||||
/* Check if it's a subroutine call and if the following insn is RTS */
|
||||
if (E->OPC == OPC_JSR && GetCodeEntry(S,I+1)->OPC == OPC_RTS) {
|
||||
|
||||
/* Change the jsr to a jmp and use the additional info for a jump */
|
||||
E->OPC = OPC_JMP;
|
||||
E->AM = AM_BRA;
|
||||
E->Info = GetOPCInfo (OPC_JMP);
|
||||
|
||||
/* Remember, we had changes */
|
||||
++OptChanges;
|
||||
|
||||
}
|
||||
|
||||
/* Next entry */
|
||||
++I;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Optimize jump targets */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
static void OptJumpTarget (CodeSeg* S)
|
||||
/* If the instruction preceeding an unconditional branch is the same as the
|
||||
* instruction preceeding the jump target, the jump target may be moved
|
||||
* one entry back. This is a size optimization, since the instruction before
|
||||
* the branch gets removed.
|
||||
*/
|
||||
{
|
||||
CodeEntry* E1; /* Entry 1 */
|
||||
CodeEntry* E2; /* Entry 2 */
|
||||
CodeEntry* T1; /* Jump target entry 1 */
|
||||
CodeEntry* T2; /* Jump target entry 2 */
|
||||
CodeLabel* TL1; /* Target label 1 */
|
||||
unsigned TI; /* Target index */
|
||||
unsigned I;
|
||||
|
||||
/* Get the number of entries, bail out if we have not enough */
|
||||
unsigned Count = GetCodeEntryCount (S);
|
||||
if (Count < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Walk over the entries */
|
||||
I = 0;
|
||||
while (I < Count-1) {
|
||||
|
||||
/* Get next entry */
|
||||
E2 = GetCodeEntry (S, I+1);
|
||||
|
||||
/* Check if we have a jump or branch, and a matching label */
|
||||
if ((E2->Info & OF_UBRA) != 0 && E2->JumpTo) {
|
||||
|
||||
/* Get the target instruction for the label */
|
||||
T2 = E2->JumpTo->Owner;
|
||||
|
||||
/* Get the entry preceeding this one (if possible) */
|
||||
TI = GetCodeEntryIndex (S, T2);
|
||||
if (TI == 0) {
|
||||
/* There is no entry before this one */
|
||||
goto NextEntry;
|
||||
}
|
||||
T1 = GetCodeEntry (S, TI-1);
|
||||
|
||||
/* Get the entry preceeding the jump */
|
||||
E1 = GetCodeEntry (S, I);
|
||||
|
||||
/* Check if both preceeding instructions are identical */
|
||||
if (!CodeEntriesAreEqual (E1, T1)) {
|
||||
/* Not equal, try next */
|
||||
goto NextEntry;
|
||||
}
|
||||
|
||||
/* Get the label for the instruction preceeding the jump target.
|
||||
* This routine will create a new label if the instruction does
|
||||
* not already have one.
|
||||
*/
|
||||
TL1 = GenCodeLabel (S, T1);
|
||||
|
||||
/* Change the jump target to point to this new label */
|
||||
MoveCodeLabelRef (S, E2, TL1);
|
||||
|
||||
/* If the instruction preceeding the jump has labels attached,
|
||||
* move references to this label to the new label.
|
||||
*/
|
||||
if (CodeEntryHasLabel (E1)) {
|
||||
MoveCodeLabels (S, E1, T1);
|
||||
}
|
||||
|
||||
/* Remove the entry preceeding the jump */
|
||||
DelCodeEntry (S, I);
|
||||
--Count;
|
||||
|
||||
/* Remember, we had changes */
|
||||
++OptChanges;
|
||||
|
||||
}
|
||||
|
||||
NextEntry:
|
||||
/* Next entry */
|
||||
++I;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* 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)
|
||||
static unsigned OptBoolTransforms (CodeSeg* S)
|
||||
/* Try to remove the call to boolean transformer routines where the call is
|
||||
* not really needed.
|
||||
*/
|
||||
{
|
||||
unsigned Changes = 0;
|
||||
unsigned I;
|
||||
|
||||
/* Get the number of entries, bail out if we have not enough */
|
||||
unsigned Count = GetCodeEntryCount (S);
|
||||
if (Count < 2) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Walk over the entries */
|
||||
@ -640,7 +226,7 @@ static void OptBoolTransforms (CodeSeg* S)
|
||||
--Count;
|
||||
|
||||
/* Remember, we had changes */
|
||||
++OptChanges;
|
||||
++Changes;
|
||||
|
||||
}
|
||||
|
||||
@ -649,6 +235,9 @@ NextEntry:
|
||||
++I;
|
||||
|
||||
}
|
||||
|
||||
/* Return the number of changes made */
|
||||
return Changes;
|
||||
}
|
||||
|
||||
|
||||
@ -662,7 +251,7 @@ NextEntry:
|
||||
/* Table with all the optimization functions */
|
||||
typedef struct OptFunc OptFunc;
|
||||
struct OptFunc {
|
||||
void (*Func) (CodeSeg*); /* Optimizer function */
|
||||
unsigned (*Func) (CodeSeg*);/* Optimizer function */
|
||||
const char* Name; /* Name of optimizer step */
|
||||
char Disabled; /* True if pass disabled */
|
||||
};
|
||||
@ -691,7 +280,7 @@ static OptFunc OptFuncs [] = {
|
||||
|
||||
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.
|
||||
* error and call AbEnd if not found.
|
||||
*/
|
||||
{
|
||||
unsigned I;
|
||||
@ -732,7 +321,9 @@ void EnableOpt (const char* Name)
|
||||
void RunOpt (CodeSeg* S)
|
||||
/* Run the optimizer */
|
||||
{
|
||||
unsigned I;
|
||||
unsigned Pass = 0;
|
||||
unsigned OptChanges;
|
||||
|
||||
/* Print the name of the function we are working on */
|
||||
if (S->Func) {
|
||||
@ -743,9 +334,6 @@ void RunOpt (CodeSeg* S)
|
||||
|
||||
/* Repeat all steps until there are no more changes */
|
||||
do {
|
||||
|
||||
unsigned I;
|
||||
|
||||
/* Reset the number of changes */
|
||||
OptChanges = 0;
|
||||
|
||||
@ -763,9 +351,8 @@ void RunOpt (CodeSeg* S)
|
||||
if (OptFuncs[I].Disabled) {
|
||||
Print (stdout, 1, "Disabled\n");
|
||||
} else {
|
||||
unsigned Changes = OptChanges;
|
||||
OptFuncs[I].Func (S);
|
||||
Changes = OptChanges - Changes;
|
||||
unsigned Changes = OptFuncs[I].Func (S);
|
||||
OptChanges += Changes;
|
||||
Print (stdout, 1, "%u Changes\n", Changes);
|
||||
}
|
||||
}
|
||||
|
481
src/cc65/coptind.c
Normal file
481
src/cc65/coptind.c
Normal file
@ -0,0 +1,481 @@
|
||||
/*****************************************************************************/
|
||||
/* */
|
||||
/* coptind.c */
|
||||
/* */
|
||||
/* Environment independent low level optimizations */
|
||||
/* */
|
||||
/* */
|
||||
/* */
|
||||
/* (C) 2001 Ullrich von Bassewitz */
|
||||
/* Wacholderweg 14 */
|
||||
/* D-70597 Stuttgart */
|
||||
/* EMail: uz@cc65.org */
|
||||
/* */
|
||||
/* */
|
||||
/* This software is provided 'as-is', without any expressed or implied */
|
||||
/* warranty. In no event will the authors be held liable for any damages */
|
||||
/* arising from the use of this software. */
|
||||
/* */
|
||||
/* Permission is granted to anyone to use this software for any purpose, */
|
||||
/* including commercial applications, and to alter it and redistribute it */
|
||||
/* freely, subject to the following restrictions: */
|
||||
/* */
|
||||
/* 1. The origin of this software must not be misrepresented; you must not */
|
||||
/* claim that you wrote the original software. If you use this software */
|
||||
/* in a product, an acknowledgment in the product documentation would be */
|
||||
/* appreciated but is not required. */
|
||||
/* 2. Altered source versions must be plainly marked as such, and must not */
|
||||
/* be misrepresented as being the original software. */
|
||||
/* 3. This notice may not be removed or altered from any source */
|
||||
/* distribution. */
|
||||
/* */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* cc65 */
|
||||
#include "codeent.h"
|
||||
#include "codeinfo.h"
|
||||
#include "codeopt.h"
|
||||
#include "error.h"
|
||||
#include "coptind.h"
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Remove dead jumps */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
unsigned OptDeadJumps (CodeSeg* S)
|
||||
/* Remove dead jumps (jumps to the next instruction) */
|
||||
{
|
||||
unsigned Changes = 0;
|
||||
CodeEntry* E;
|
||||
unsigned I;
|
||||
|
||||
/* Get the number of entries, bail out if we have less than two entries */
|
||||
unsigned Count = GetCodeEntryCount (S);
|
||||
if (Count < 2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Walk over all entries minus the last one */
|
||||
I = 0;
|
||||
while (I < Count-1) {
|
||||
|
||||
/* Get the next entry */
|
||||
E = GetCodeEntry (S, I);
|
||||
|
||||
/* Check if it's a branch, if it has a local target, and if the target
|
||||
* is the next instruction.
|
||||
*/
|
||||
if (E->AM == AM_BRA && E->JumpTo && E->JumpTo->Owner == GetCodeEntry (S, I+1)) {
|
||||
|
||||
/* Delete the dead jump */
|
||||
DelCodeEntry (S, I);
|
||||
|
||||
/* Keep the number of entries updated */
|
||||
--Count;
|
||||
|
||||
/* Remember, we had changes */
|
||||
++Changes;
|
||||
|
||||
} else {
|
||||
|
||||
/* Next entry */
|
||||
++I;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the number of changes made */
|
||||
return Changes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Remove dead code */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
unsigned OptDeadCode (CodeSeg* S)
|
||||
/* Remove dead code (code that follows an unconditional jump or an rts/rti
|
||||
* and has no label)
|
||||
*/
|
||||
{
|
||||
unsigned Changes = 0;
|
||||
unsigned I;
|
||||
|
||||
/* Get the number of entries, bail out if we have less than two entries */
|
||||
unsigned Count = GetCodeEntryCount (S);
|
||||
if (Count < 2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Walk over all entries minus the last one */
|
||||
I = 0;
|
||||
while (I < Count-1) {
|
||||
|
||||
/* Get this entry */
|
||||
CodeEntry* E = GetCodeEntry (S, I);
|
||||
|
||||
/* Check if it's an unconditional branch, and if the next entry has
|
||||
* no labels attached
|
||||
*/
|
||||
if ((E->Info & OF_DEAD) != 0 && !CodeEntryHasLabel (GetCodeEntry (S, I+1))) {
|
||||
|
||||
/* Delete the next entry */
|
||||
DelCodeEntry (S, I+1);
|
||||
|
||||
/* Keep the number of entries updated */
|
||||
--Count;
|
||||
|
||||
/* Remember, we had changes */
|
||||
++Changes;
|
||||
|
||||
} else {
|
||||
|
||||
/* Next entry */
|
||||
++I;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the number of changes made */
|
||||
return Changes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Optimize jump cascades */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
unsigned OptJumpCascades (CodeSeg* S)
|
||||
/* Optimize jump cascades (jumps to jumps). In such a case, the jump is
|
||||
* replaced by a jump to the final location. This will in some cases produce
|
||||
* worse code, because some jump targets are no longer reachable by short
|
||||
* branches, but this is quite rare, so there are more advantages than
|
||||
* disadvantages.
|
||||
*/
|
||||
{
|
||||
unsigned Changes = 0;
|
||||
unsigned I;
|
||||
|
||||
/* Get the number of entries, bail out if we have no entries */
|
||||
unsigned Count = GetCodeEntryCount (S);
|
||||
if (Count == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Walk over all entries */
|
||||
I = 0;
|
||||
while (I < Count) {
|
||||
|
||||
/* Get this entry */
|
||||
CodeEntry* E = GetCodeEntry (S, I);
|
||||
|
||||
/* Check if it's a branch, if it has a jump label, and if this jump
|
||||
* label is not attached to the instruction itself.
|
||||
*/
|
||||
if ((E->Info & OF_BRA) != 0 && E->JumpTo != 0 && E->JumpTo->Owner != E) {
|
||||
|
||||
/* Get the label this insn is branching to */
|
||||
CodeLabel* OldLabel = E->JumpTo;
|
||||
|
||||
/* Get the entry we're branching to */
|
||||
CodeEntry* N = OldLabel->Owner;
|
||||
|
||||
/* If the entry we're branching to is not itself a branch, it is
|
||||
* not what we're searching for.
|
||||
*/
|
||||
if ((N->Info & OF_BRA) == 0) {
|
||||
goto NextEntry;
|
||||
}
|
||||
|
||||
/* Check if we can use the final target label. This is the case,
|
||||
* if the target branch is an absolut branch, or if it is a
|
||||
* conditional branch checking the same condition as the first one.
|
||||
*/
|
||||
if ((N->Info & OF_UBRA) != 0 ||
|
||||
((E->Info & OF_CBRA) != 0 &&
|
||||
GetBranchCond (E->OPC) == GetBranchCond (N->OPC))) {
|
||||
|
||||
/* This is a jump cascade and we may jump to the final target.
|
||||
* If we have a label, move the reference to this label. If
|
||||
* we don't have a label, use the argument instead.
|
||||
*/
|
||||
if (N->JumpTo) {
|
||||
/* Move the reference to the new insn */
|
||||
MoveCodeLabelRef (S, E, N->JumpTo);
|
||||
} else {
|
||||
/* Remove the reference to the old label */
|
||||
RemoveCodeLabelRef (S, E);
|
||||
}
|
||||
|
||||
/* Use the new argument */
|
||||
CodeEntrySetArg (E, N->Arg);
|
||||
|
||||
/* Use the usage information from the new instruction */
|
||||
E->Use = N->Use;
|
||||
E->Chg = N->Chg;
|
||||
|
||||
/* Remember, we had changes */
|
||||
++Changes;
|
||||
|
||||
/* Done */
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
/* Check if both are conditional branches, and the condition of
|
||||
* the second is the inverse of that of the first. In this case,
|
||||
* the second branch will never be taken, and we may jump directly
|
||||
* to the instruction behind this one.
|
||||
*/
|
||||
goto NextEntry;
|
||||
|
||||
}
|
||||
|
||||
NextEntry:
|
||||
/* Next entry */
|
||||
++I;
|
||||
|
||||
}
|
||||
|
||||
/* Return the number of changes made */
|
||||
return Changes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Optimize jsr/rts */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
unsigned OptRTS (CodeSeg* S)
|
||||
/* Optimize subroutine calls followed by an RTS. The subroutine call will get
|
||||
* replaced by a jump. Don't bother to delete the RTS if it does not have a
|
||||
* label, the dead code elimination should take care of it.
|
||||
*/
|
||||
{
|
||||
unsigned Changes = 0;
|
||||
unsigned I;
|
||||
|
||||
/* Get the number of entries, bail out if we have less than 2 entries */
|
||||
unsigned Count = GetCodeEntryCount (S);
|
||||
if (Count < 2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Walk over all entries minus the last one */
|
||||
I = 0;
|
||||
while (I < Count-1) {
|
||||
|
||||
/* Get this entry */
|
||||
CodeEntry* E = GetCodeEntry (S, I);
|
||||
|
||||
/* Check if it's a subroutine call and if the following insn is RTS */
|
||||
if (E->OPC == OPC_JSR && GetCodeEntry(S,I+1)->OPC == OPC_RTS) {
|
||||
|
||||
/* Change the jsr to a jmp and use the additional info for a jump */
|
||||
E->OPC = OPC_JMP;
|
||||
E->AM = AM_BRA;
|
||||
E->Info = GetOPCInfo (OPC_JMP);
|
||||
|
||||
/* Remember, we had changes */
|
||||
++Changes;
|
||||
|
||||
}
|
||||
|
||||
/* Next entry */
|
||||
++I;
|
||||
|
||||
}
|
||||
|
||||
/* Return the number of changes made */
|
||||
return Changes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Optimize jump targets */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
unsigned OptJumpTarget (CodeSeg* S)
|
||||
/* If the instruction preceeding an unconditional branch is the same as the
|
||||
* instruction preceeding the jump target, the jump target may be moved
|
||||
* one entry back. This is a size optimization, since the instruction before
|
||||
* the branch gets removed.
|
||||
*/
|
||||
{
|
||||
unsigned Changes = 0;
|
||||
CodeEntry* E1; /* Entry 1 */
|
||||
CodeEntry* E2; /* Entry 2 */
|
||||
CodeEntry* T1; /* Jump target entry 1 */
|
||||
CodeEntry* T2; /* Jump target entry 2 */
|
||||
CodeLabel* TL1; /* Target label 1 */
|
||||
unsigned TI; /* Target index */
|
||||
unsigned I;
|
||||
|
||||
/* Get the number of entries, bail out if we have not enough */
|
||||
unsigned Count = GetCodeEntryCount (S);
|
||||
if (Count < 3) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Walk over the entries */
|
||||
I = 0;
|
||||
while (I < Count-1) {
|
||||
|
||||
/* Get next entry */
|
||||
E2 = GetCodeEntry (S, I+1);
|
||||
|
||||
/* Check if we have a jump or branch, and a matching label */
|
||||
if ((E2->Info & OF_UBRA) != 0 && E2->JumpTo) {
|
||||
|
||||
/* Get the target instruction for the label */
|
||||
T2 = E2->JumpTo->Owner;
|
||||
|
||||
/* Get the entry preceeding this one (if possible) */
|
||||
TI = GetCodeEntryIndex (S, T2);
|
||||
if (TI == 0) {
|
||||
/* There is no entry before this one */
|
||||
goto NextEntry;
|
||||
}
|
||||
T1 = GetCodeEntry (S, TI-1);
|
||||
|
||||
/* Get the entry preceeding the jump */
|
||||
E1 = GetCodeEntry (S, I);
|
||||
|
||||
/* Check if both preceeding instructions are identical */
|
||||
if (!CodeEntriesAreEqual (E1, T1)) {
|
||||
/* Not equal, try next */
|
||||
goto NextEntry;
|
||||
}
|
||||
|
||||
/* Get the label for the instruction preceeding the jump target.
|
||||
* This routine will create a new label if the instruction does
|
||||
* not already have one.
|
||||
*/
|
||||
TL1 = GenCodeLabel (S, T1);
|
||||
|
||||
/* Change the jump target to point to this new label */
|
||||
MoveCodeLabelRef (S, E2, TL1);
|
||||
|
||||
/* If the instruction preceeding the jump has labels attached,
|
||||
* move references to this label to the new label.
|
||||
*/
|
||||
if (CodeEntryHasLabel (E1)) {
|
||||
MoveCodeLabels (S, E1, T1);
|
||||
}
|
||||
|
||||
/* Remove the entry preceeding the jump */
|
||||
DelCodeEntry (S, I);
|
||||
--Count;
|
||||
|
||||
/* Remember, we had changes */
|
||||
++Changes;
|
||||
|
||||
}
|
||||
|
||||
NextEntry:
|
||||
/* Next entry */
|
||||
++I;
|
||||
|
||||
}
|
||||
|
||||
/* Return the number of changes made */
|
||||
return Changes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Remove conditional jumps never taken */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
unsigned 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 Changes = 0;
|
||||
unsigned I;
|
||||
|
||||
/* Get the number of entries, bail out if we have not enough */
|
||||
unsigned Count = GetCodeEntryCount (S);
|
||||
if (Count < 2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
++Changes;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
NextEntry:
|
||||
/* Next entry */
|
||||
++I;
|
||||
|
||||
}
|
||||
|
||||
/* Return the number of changes made */
|
||||
return Changes;
|
||||
}
|
||||
|
||||
|
||||
|
92
src/cc65/coptind.h
Normal file
92
src/cc65/coptind.h
Normal file
@ -0,0 +1,92 @@
|
||||
/*****************************************************************************/
|
||||
/* */
|
||||
/* coptind.h */
|
||||
/* */
|
||||
/* Environment independent low level optimizations */
|
||||
/* */
|
||||
/* */
|
||||
/* */
|
||||
/* (C) 2001 Ullrich von Bassewitz */
|
||||
/* Wacholderweg 14 */
|
||||
/* D-70597 Stuttgart */
|
||||
/* EMail: uz@cc65.org */
|
||||
/* */
|
||||
/* */
|
||||
/* This software is provided 'as-is', without any expressed or implied */
|
||||
/* warranty. In no event will the authors be held liable for any damages */
|
||||
/* arising from the use of this software. */
|
||||
/* */
|
||||
/* Permission is granted to anyone to use this software for any purpose, */
|
||||
/* including commercial applications, and to alter it and redistribute it */
|
||||
/* freely, subject to the following restrictions: */
|
||||
/* */
|
||||
/* 1. The origin of this software must not be misrepresented; you must not */
|
||||
/* claim that you wrote the original software. If you use this software */
|
||||
/* in a product, an acknowledgment in the product documentation would be */
|
||||
/* appreciated but is not required. */
|
||||
/* 2. Altered source versions must be plainly marked as such, and must not */
|
||||
/* be misrepresented as being the original software. */
|
||||
/* 3. This notice may not be removed or altered from any source */
|
||||
/* distribution. */
|
||||
/* */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
#ifndef COPTIND_H
|
||||
#define COPTIND_H
|
||||
|
||||
|
||||
|
||||
#include "codeseg.h"
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Code */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
unsigned OptDeadJumps (CodeSeg* S);
|
||||
/* Remove dead jumps (jumps to the next instruction) */
|
||||
|
||||
unsigned OptDeadCode (CodeSeg* S);
|
||||
/* Remove dead code (code that follows an unconditional jump or an rts/rti
|
||||
* and has no label)
|
||||
*/
|
||||
|
||||
unsigned OptJumpCascades (CodeSeg* S);
|
||||
/* Optimize jump cascades (jumps to jumps). In such a case, the jump is
|
||||
* replaced by a jump to the final location. This will in some cases produce
|
||||
* worse code, because some jump targets are no longer reachable by short
|
||||
* branches, but this is quite rare, so there are more advantages than
|
||||
* disadvantages.
|
||||
*/
|
||||
|
||||
unsigned OptRTS (CodeSeg* S);
|
||||
/* Optimize subroutine calls followed by an RTS. The subroutine call will get
|
||||
* replaced by a jump. Don't bother to delete the RTS if it does not have a
|
||||
* label, the dead code elimination should take care of it.
|
||||
*/
|
||||
|
||||
unsigned OptJumpTarget (CodeSeg* S);
|
||||
/* If the instruction preceeding an unconditional branch is the same as the
|
||||
* instruction preceeding the jump target, the jump target may be moved
|
||||
* one entry back. This is a size optimization, since the instruction before
|
||||
* the branch gets removed.
|
||||
*/
|
||||
|
||||
unsigned 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.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* End of coptind.h */
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -32,6 +32,7 @@ OBJS = anonname.o \
|
||||
codeopt.o \
|
||||
codeseg.o \
|
||||
compile.o \
|
||||
coptind.o \
|
||||
cpu.o \
|
||||
dataseg.o \
|
||||
datatype.o \
|
||||
|
Loading…
Reference in New Issue
Block a user