#include "error.h"
#include "global.h"
#include "output.h"
-
+#include "symtab.h"
/*****************************************************************************/
static unsigned OptLoad1 (CodeSeg* S)
/* Search for a call to ldaxysp where X is not used later and replace it by
- * a load of just the A register.
- */
+** a load of just the A register.
+*/
{
unsigned I;
unsigned Changes = 0;
!RegXUsed (S, I+3)) {
/* A/X are stored into memory somewhere and X is not used
- * later
- */
+ ** later
+ */
/* lda (sp),y */
X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI);
CodeEntry* N;
/* If we had a preceeding load that is identical, remove this one.
- * If it is not identical, or we didn't have one, remember it.
- */
+ ** If it is not identical, or we didn't have one, remember it.
+ */
if (Load != 0 &&
E->OPC == Load->OPC &&
E->AM == Load->AM &&
static unsigned OptDecouple (CodeSeg* S)
/* Decouple operations, that is, do the following replacements:
- *
- * dex -> ldx #imm
- * inx -> ldx #imm
- * dey -> ldy #imm
- * iny -> ldy #imm
- * tax -> ldx #imm
- * txa -> lda #imm
- * tay -> ldy #imm
- * tya -> lda #imm
- * lda zp -> lda #imm
- * ldx zp -> ldx #imm
- * ldy zp -> ldy #imm
- *
- * Provided that the register values are known of course.
- */
+**
+** dex -> ldx #imm
+** inx -> ldx #imm
+** dey -> ldy #imm
+** iny -> ldy #imm
+** tax -> ldx #imm
+** txa -> lda #imm
+** tay -> ldy #imm
+** tya -> lda #imm
+** lda zp -> lda #imm
+** ldx zp -> ldx #imm
+** ldy zp -> ldy #imm
+**
+** Provided that the register values are known of course.
+*/
{
unsigned Changes = 0;
unsigned I;
static unsigned IsDecSP (const CodeEntry* E)
/* Check if this is an insn that decrements the stack pointer. If so, return
- * the decrement. If not, return zero.
- * The function expects E to be a subroutine call.
- */
+** the decrement. If not, return zero.
+** The function expects E to be a subroutine call.
+*/
{
if (strncmp (E->Arg, "decsp", 5) == 0) {
if (E->Arg[5] >= '1' && E->Arg[5] <= '8') {
static unsigned OptStackPtrOps (CodeSeg* S)
/* Merge adjacent calls to decsp into one. NOTE: This function won't merge all
- * known cases!
- */
+** known cases!
+*/
{
unsigned Changes = 0;
unsigned I;
return Changes;
}
+static unsigned OptGotoSPAdj (CodeSeg* S)
+/* Optimize SP adjustment for forward 'goto' */
+{
+ unsigned Changes = 0;
+ unsigned I;
+
+ /* Walk over the entries */
+ I = 0;
+ while (I < CS_GetEntryCount (S)) {
+
+ CodeEntry* L[10], *X;
+ unsigned short adjustment;
+ const char* Arg;
+ /* Get next entry */
+ L[0] = CS_GetEntry (S, I);
+
+ /* Check for the sequence generated by g_lateadjustSP */
+ if (L[0]->OPC == OP65_PHA &&
+ CS_GetEntries (S, L+1, I+1, 9) &&
+ L[1]->OPC == OP65_LDA &&
+ L[1]->AM == AM65_ABS &&
+ L[2]->OPC == OP65_CLC &&
+ L[3]->OPC == OP65_ADC &&
+ strcmp (L[3]->Arg, "sp") == 0 &&
+ L[6]->OPC == OP65_ADC &&
+ strcmp (L[6]->Arg, "sp+1") == 0 &&
+ L[9]->OPC == OP65_JMP) {
+ adjustment = FindSPAdjustment (L[1]->Arg);
+
+ if (adjustment == 0) {
+ /* No SP adjustment needed, remove the whole sequence */
+ CS_DelEntries (S, I, 9);
+ }
+ else if (adjustment >= 65536 - 8) {
+ /* If adjustment is in range [-8, 0) we use decsp* calls */
+ char Buf[20];
+ adjustment = 65536 - adjustment;
+ xsprintf (Buf, sizeof (Buf), "decsp%u", adjustment);
+ X = NewCodeEntry (OP65_JSR, AM65_ABS, Buf, 0, L[1]->LI);
+ CS_InsertEntry (S, X, I + 9);
+
+ /* Delete the old code */
+ CS_DelEntries (S, I, 9);
+ }
+ else if (adjustment >= 65536 - 255) {
+ /* For range [-255, -8) we have ldy #, jsr subysp */
+ adjustment = 65536 - adjustment;
+ Arg = MakeHexArg (adjustment);
+ X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[1]->LI);
+ CS_InsertEntry (S, X, I + 9);
+ X = NewCodeEntry (OP65_JSR, AM65_ABS, "subysp", 0, L[1]->LI);
+ CS_InsertEntry (S, X, I + 10);
+
+ /* Delete the old code */
+ CS_DelEntries (S, I, 9);
+ }
+ else if (adjustment > 255) {
+ /* For ranges [-32768, 255) and (255, 32767) the only modification
+ ** is to replace the absolute with immediate addressing
+ */
+ Arg = MakeHexArg (adjustment & 0xff);
+ X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, L[1]->LI);
+ CS_InsertEntry (S, X, I + 1);
+ Arg = MakeHexArg (adjustment >> 8);
+ X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, L[5]->LI);
+ CS_InsertEntry (S, X, I + 6);
+
+ /* Delete the old code */
+ CS_DelEntry (S, I + 2);
+ CS_DelEntry (S, I + 6);
+ }
+ else if (adjustment > 8) {
+ /* For range (8, 255] we have ldy #, jsr addysp */
+ Arg = MakeHexArg (adjustment & 0xff);
+ X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[1]->LI);
+ CS_InsertEntry (S, X, I + 9);
+ X = NewCodeEntry (OP65_JSR, AM65_ABS, "addysp", 0, L[1]->LI);
+ CS_InsertEntry (S, X, I + 10);
+
+ /* Delete the old code */
+ CS_DelEntries (S, I, 9);
+ }
+ else {
+ /* If adjustment is in range (0, 8] we use incsp* calls */
+ char Buf[20];
+ xsprintf (Buf, sizeof (Buf), "incsp%u", adjustment);
+ X = NewCodeEntry (OP65_JSR, AM65_ABS, Buf, 0, L[1]->LI);
+ CS_InsertEntry (S, X, I + 9);
+
+ /* Delete the old code */
+ CS_DelEntries (S, I, 9);
+ }
+ /* Regenerate register info */
+ CS_GenRegInfo (S);
+
+ /* Remember we had changes */
+ Changes++;
+
+ } else {
+
+ /* Next entry */
+ ++I;
+ }
+
+ }
+
+ /* Return the number of changes made */
+ return Changes;
+}
/*****************************************************************************/
/* struct OptFunc */
static OptFunc DOptDeadJumps = { OptDeadJumps, "OptDeadJumps", 100, 0, 0, 0, 0, 0 };
static OptFunc DOptDecouple = { OptDecouple, "OptDecouple", 100, 0, 0, 0, 0, 0 };
static OptFunc DOptDupLoads = { OptDupLoads, "OptDupLoads", 0, 0, 0, 0, 0, 0 };
+static OptFunc DOptGotoSPAdj = { OptGotoSPAdj, "OptGotoSPAdj", 0, 0, 0, 0, 0, 0 };
static OptFunc DOptIndLoads1 = { OptIndLoads1, "OptIndLoads1", 0, 0, 0, 0, 0, 0 };
static OptFunc DOptIndLoads2 = { OptIndLoads2, "OptIndLoads2", 0, 0, 0, 0, 0, 0 };
static OptFunc DOptJumpCascades = { OptJumpCascades, "OptJumpCascades", 100, 0, 0, 0, 0, 0 };
static OptFunc DOptPtrLoad15 = { OptPtrLoad15, "OptPtrLoad15", 86, 0, 0, 0, 0, 0 };
static OptFunc DOptPtrLoad16 = { OptPtrLoad16, "OptPtrLoad16", 100, 0, 0, 0, 0, 0 };
static OptFunc DOptPtrLoad17 = { OptPtrLoad17, "OptPtrLoad17", 190, 0, 0, 0, 0, 0 };
+static OptFunc DOptPtrLoad18 = { OptPtrLoad18, "OptPtrLoad18", 100, 0, 0, 0, 0, 0 };
+static OptFunc DOptPtrLoad19 = { OptPtrLoad19, "OptPtrLoad19", 65, 0, 0, 0, 0, 0 };
static OptFunc DOptPtrStore1 = { OptPtrStore1, "OptPtrStore1", 65, 0, 0, 0, 0, 0 };
static OptFunc DOptPtrStore2 = { OptPtrStore2, "OptPtrStore2", 65, 0, 0, 0, 0, 0 };
static OptFunc DOptPtrStore3 = { OptPtrStore3, "OptPtrStore3", 100, 0, 0, 0, 0, 0 };
&DOptDeadJumps,
&DOptDecouple,
&DOptDupLoads,
+ &DOptGotoSPAdj,
&DOptIndLoads1,
&DOptIndLoads2,
&DOptJumpCascades,
&DOptPtrLoad15,
&DOptPtrLoad16,
&DOptPtrLoad17,
+ &DOptPtrLoad18,
+ &DOptPtrLoad19,
&DOptPtrLoad2,
&DOptPtrLoad3,
&DOptPtrLoad4,
static OptFunc* FindOptFunc (const char* Name)
/* Find an optimizer step by name in the table and return a pointer. Return
- * NULL if no such step is found.
- */
+** NULL if no such step is found.
+*/
{
/* Search for the function in the list */
OptFunc** O = bsearch (Name, OptFuncs, OPTFUNC_COUNT, sizeof (OptFuncs[0]), CmpOptStep);
static OptFunc* GetOptFunc (const char* Name)
/* Find an optimizer step by name in the table and return a pointer. Print an
- * error and call AbEnd if not found.
- */
+** error and call AbEnd if not found.
+*/
{
/* Search for the function in the list */
OptFunc* F = FindOptFunc (Name);
if (F == 0) {
/* Not found */
- AbEnd ("Optimization step `%s' not found", Name);
+ AbEnd ("Optimization step '%s' not found", Name);
}
return F;
}
/* List all optimization steps */
{
unsigned I;
+
+ fprintf (F, "any\n");
for (I = 0; I < OPTFUNC_COUNT; ++I) {
fprintf (F, "%s\n", OptFuncs[I]->Name);
}
/* Output a header line */
if (Step == 0) {
/* Initial output */
- WriteOutput ("Initial code for function `%s':\n",
+ WriteOutput ("Initial code for function '%s':\n",
S->Func? S->Func->Name : "<global>");
} else {
- WriteOutput ("Code after applying `%s':\n", Step);
+ WriteOutput ("Code after applying '%s':\n", Step);
}
/* Output the code segment */
unsigned Changes, C;
/* Don't run the function if it is disabled or if it is prohibited by the
- * code size factor
- */
+ ** code size factor
+ */
if (F->Disabled || F->CodeSizeFactor > S->CodeSizeFactor) {
return 0;
}
static unsigned RunOptGroup1 (CodeSeg* S)
/* Run the first group of optimization steps. These steps translate known
- * patterns emitted by the code generator into more optimal patterns. Order
- * of the steps is important, because some of the steps done earlier cover
- * the same patterns as later steps as subpatterns.
- */
+** patterns emitted by the code generator into more optimal patterns. Order
+** of the steps is important, because some of the steps done earlier cover
+** the same patterns as later steps as subpatterns.
+*/
{
unsigned Changes = 0;
+ Changes += RunOptFunc (S, &DOptGotoSPAdj, 1);
Changes += RunOptFunc (S, &DOptStackPtrOps, 5);
Changes += RunOptFunc (S, &DOptPtrStore1, 1);
Changes += RunOptFunc (S, &DOptPtrStore2, 1);
Changes += RunOptFunc (S, &DOptPtrLoad5, 1);
Changes += RunOptFunc (S, &DOptPtrLoad6, 1);
Changes += RunOptFunc (S, &DOptPtrLoad7, 1);
+ Changes += RunOptFunc (S, &DOptPtrLoad18, 1); /* Before OptPtrLoad11 */
Changes += RunOptFunc (S, &DOptPtrLoad11, 1);
Changes += RunOptFunc (S, &DOptPtrLoad12, 1);
Changes += RunOptFunc (S, &DOptPtrLoad13, 1);
Changes += RunOptFunc (S, &DOptPtrLoad15, 1);
Changes += RunOptFunc (S, &DOptPtrLoad16, 1);
Changes += RunOptFunc (S, &DOptPtrLoad17, 1);
+ Changes += RunOptFunc (S, &DOptPtrLoad19, 1);
Changes += RunOptFunc (S, &DOptBNegAX1, 1);
Changes += RunOptFunc (S, &DOptBNegAX2, 1);
Changes += RunOptFunc (S, &DOptBNegAX3, 1);
static unsigned RunOptGroup2 (CodeSeg* S)
/* Run one group of optimization steps. This step involves just decoupling
- * instructions by replacing them by instructions that do not depend on
- * previous instructions. This makes it easier to find instructions that
- * aren't used.
- */
+** instructions by replacing them by instructions that do not depend on
+** previous instructions. This makes it easier to find instructions that
+** aren't used.
+*/
{
unsigned Changes = 0;
static unsigned RunOptGroup3 (CodeSeg* S)
/* Run one group of optimization steps. These steps depend on each other,
- * that means that one step may allow another step to do additional work,
- * so we will repeat the steps as long as we see any changes.
- */
+** that means that one step may allow another step to do additional work,
+** so we will repeat the steps as long as we see any changes.
+*/
{
unsigned Changes, C;
static unsigned RunOptGroup4 (CodeSeg* S)
/* Run another round of pattern replacements. These are done late, since there
- * may be better replacements before.
- */
+** may be better replacements before.
+*/
{
unsigned Changes = 0;
Changes += RunOptFunc (S, &DOpt65C02Stores, 1);
if (Changes) {
/* The 65C02 replacement codes do often make the use of a register
- * value unnecessary, so if we have changes, run another load
- * removal pass.
- */
+ ** value unnecessary, so if we have changes, run another load
+ ** removal pass.
+ */
Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
}
}
static unsigned RunOptGroup6 (CodeSeg* S)
/* This one is quite special. It tries to replace "lda (sp),y" by "lda (sp,x)".
- * The latter is ony cycle slower, but if we're able to remove the necessary
- * load of the Y register, because X is zero anyway, we gain 1 cycle and
- * shorten the code by one (transfer) or two bytes (load). So what we do is
- * to replace the insns, remove unused loads, and then change back all insns
- * where Y is still zero (meaning that the load has not been removed).
- */
+** The latter is ony cycle slower, but if we're able to remove the necessary
+** load of the Y register, because X is zero anyway, we gain 1 cycle and
+** shorten the code by one (transfer) or two bytes (load). So what we do is
+** to replace the insns, remove unused loads, and then change back all insns
+** where Y is still zero (meaning that the load has not been removed).
+*/
{
unsigned Changes = 0;
/* This group will only run for a standard 6502, because the 65C02 has a
- * better addressing mode that covers this case.
- */
+ ** better addressing mode that covers this case.
+ */
if ((CPUIsets[CPU] & CPU_ISET_65SC02) == 0) {
Changes += RunOptFunc (S, &DOptIndLoads1, 1);
Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
static unsigned RunOptGroup7 (CodeSeg* S)
/* The last group of optimization steps. Adjust branches, do size optimizations.
- */
+*/
{
unsigned Changes = 0;
unsigned C;
/* Optimize for size, that is replace operations by shorter ones, even
- * if this does hinder further optimizations (no problem since we're
- * done soon).
- */
+ ** if this does hinder further optimizations (no problem since we're
+ ** done soon).
+ */
C = RunOptFunc (S, &DOptSize1, 1);
if (C) {
Changes += C;
/* Run some optimization passes again, since the size optimizations
- * may have opened new oportunities.
- */
+ ** may have opened new oportunities.
+ */
Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
Changes += RunOptFunc (S, &DOptUnusedStores, 1);
Changes += RunOptFunc (S, &DOptJumpTarget1, 5);
if (C) {
Changes += C;
/* Run some optimization passes again, since the size optimizations
- * may have opened new oportunities.
- */
+ ** may have opened new oportunities.
+ */
Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
Changes += RunOptFunc (S, &DOptJumpTarget1, 5);
Changes += RunOptFunc (S, &DOptStore5, 1);
Changes += RunOptFunc (S, &DOptBranchDist, 3);
/* Replace conditional branches to RTS. If we had changes, we must run dead
- * code elimination again, since the change may have introduced dead code.
- */
+ ** code elimination again, since the change may have introduced dead code.
+ */
C = RunOptFunc (S, &DOptRTSJumps2, 1);
Changes += C;
if (C) {
/* 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);
+ Print (stdout, 1, "Running optimizer for function '%s'\n", S->Func->Name);
} else {
Print (stdout, 1, "Running optimizer for global code segment\n");
}