unsigned OptPush2 (CodeSeg* S) /* A sequence * * jsr ldaxidx * jsr pushax * * may get replaced by * * jsr pushwidx * */ { unsigned I; unsigned Changes = 0; /* Generate register info */ CS_GenRegInfo (S); /* Walk over the entries */ I = 0; while (I < CS_GetEntryCount (S)) { CodeEntry* L[2]; /* Get next entry */ L[0] = CS_GetEntry (S, I); /* Check for the sequence */ if (CE_IsCallTo (L[0], "ldaxidx") && (L[1] = CS_GetNextEntry (S, I)) != 0 && !CE_HasLabel (L[1]) && CE_IsCallTo (L[1], "pushax")) { /* Insert new code behind the pushax */ CodeEntry* X; /* jsr pushwidx */ X = NewCodeEntry (OP65_JSR, AM65_ABS, "pushwidx", 0, L[1]->LI); CS_InsertEntry (S, X, I+2); /* Delete the old code */ CS_DelEntries (S, I, 2); /* Remember, we had changes */ ++Changes; } /* Next entry */ ++I; } /* Free the register info */ CS_FreeRegInfo (S); /* Return the number of changes made */ return Changes; }
void RunOpt (CodeSeg* S) /* Run the optimizer */ { const char* StatFileName; /* If we shouldn't run the optimizer, bail out */ if (!S->Optimize) { return; } /* Check if we are requested to write optimizer statistics */ StatFileName = getenv ("CC65_OPTSTATS"); if (StatFileName) { ReadOptStats (StatFileName); } /* 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"); } /* If requested, open an output file */ OpenDebugFile (S); WriteDebugOutput (S, 0); /* Generate register info for all instructions */ CS_GenRegInfo (S); /* Run groups of optimizations */ RunOptGroup1 (S); RunOptGroup2 (S); RunOptGroup3 (S); RunOptGroup4 (S); RunOptGroup5 (S); RunOptGroup6 (S); RunOptGroup7 (S); /* Free register info */ CS_FreeRegInfo (S); /* Close output file if necessary */ if (DebugOptOutput) { CloseOutputFile (); } /* Write statistics */ if (StatFileName) { WriteOptStats (StatFileName); } }
unsigned Opt65C02Stores (CodeSeg* S) /* Use STZ where possible */ { unsigned Changes = 0; unsigned I; /* Generate register info for this step */ CS_GenRegInfo (S); /* Walk over the entries */ I = 0; while (I < CS_GetEntryCount (S)) { /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check for a store with a register value of zero and an addressing * mode available with STZ. */ if (((E->OPC == OP65_STA && E->RI->In.RegA == 0) || (E->OPC == OP65_STX && E->RI->In.RegX == 0) || (E->OPC == OP65_STY && E->RI->In.RegY == 0)) && (E->AM == AM65_ZP || E->AM == AM65_ABS || E->AM == AM65_ZPX || E->AM == AM65_ABSX)) { /* Replace by STZ */ CodeEntry* X = NewCodeEntry (OP65_STZ, E->AM, E->Arg, 0, E->LI); CS_InsertEntry (S, X, I+1); /* Delete the old stuff */ CS_DelEntry (S, I); /* We had changes */ ++Changes; } /* Next entry */ ++I; } /* Free register info */ CS_FreeRegInfo (S); /* Return the number of changes made */ return Changes; }
unsigned Opt65C02Ind (CodeSeg* S) /* Try to use the indirect addressing mode where possible */ { unsigned Changes = 0; unsigned I; /* Generate register info for this step */ CS_GenRegInfo (S); /* Walk over the entries */ I = 0; while (I < CS_GetEntryCount (S)) { /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check for addressing mode indirect indexed Y where Y is zero. * Note: All opcodes that are available as (zp),y are also available * as (zp), so we can ignore the actual opcode here. */ if (E->AM == AM65_ZP_INDY && E->RI->In.RegY == 0) { /* Replace it by indirect addressing mode */ CodeEntry* X = NewCodeEntry (E->OPC, AM65_ZP_IND, E->Arg, 0, E->LI); CS_InsertEntry (S, X, I+1); CS_DelEntry (S, I); /* We had changes */ ++Changes; } /* Next entry */ ++I; } /* Free register info */ CS_FreeRegInfo (S); /* Return the number of changes made */ return Changes; }
static unsigned RunOptFunc (CodeSeg* S, OptFunc* F, unsigned Max) /* Run one optimizer function Max times or until there are no more changes */ { unsigned Changes, C; /* Don't run the function if it is disabled or if it is prohibited by the ** code size factor */ if (F->Disabled || F->CodeSizeFactor > S->CodeSizeFactor) { return 0; } /* Run this until there are no more changes */ Changes = 0; do { /* Run the function */ C = F->Func (S); Changes += C; /* Do statistics */ ++F->TotalRuns; ++F->LastRuns; F->TotalChanges += C; F->LastChanges += C; /* If we had changes, output stuff and regenerate register info */ if (C) { if (Debug) { printf ("Applied %s: %u changes\n", F->Name, C); } WriteDebugOutput (S, F->Name); CS_GenRegInfo (S); } } while (--Max && C > 0); /* Return the number of changes */ return Changes; }
unsigned Opt65C02BitOps (CodeSeg* S) /* Use special bit op instructions of the C02 */ { unsigned Changes = 0; unsigned I; /* Generate register info for this step */ CS_GenRegInfo (S); /* Walk over the entries */ I = 0; while (I < CS_GetEntryCount (S)) { CodeEntry* L[3]; /* Get next entry */ L[0] = CS_GetEntry (S, I); /* Check for the sequence */ if (L[0]->OPC == OP65_LDA && (L[0]->AM == AM65_ZP || L[0]->AM == AM65_ABS) && !CS_RangeHasLabel (S, I+1, 2) && CS_GetEntries (S, L+1, I+1, 2) && (L[1]->OPC == OP65_AND || L[1]->OPC == OP65_ORA) && CE_IsConstImm (L[1]) && L[2]->OPC == OP65_STA && L[2]->AM == L[0]->AM && strcmp (L[2]->Arg, L[0]->Arg) == 0 && !RegAUsed (S, I+3)) { char Buf[32]; CodeEntry* X; /* Use TRB for AND and TSB for ORA */ if (L[1]->OPC == OP65_AND) { /* LDA #XX */ sprintf (Buf, "$%02X", (int) ((~L[1]->Num) & 0xFF)); X = NewCodeEntry (OP65_LDA, AM65_IMM, Buf, 0, L[1]->LI); CS_InsertEntry (S, X, I+3); /* TRB */ X = NewCodeEntry (OP65_TRB, L[0]->AM, L[0]->Arg, 0, L[0]->LI); CS_InsertEntry (S, X, I+4); } else { /* LDA #XX */ sprintf (Buf, "$%02X", (int) L[1]->Num); X = NewCodeEntry (OP65_LDA, AM65_IMM, Buf, 0, L[1]->LI); CS_InsertEntry (S, X, I+3); /* TSB */ X = NewCodeEntry (OP65_TSB, L[0]->AM, L[0]->Arg, 0, L[0]->LI); CS_InsertEntry (S, X, I+4); } /* Delete the old stuff */ CS_DelEntries (S, I, 3); /* We had changes */ ++Changes; } /* Next entry */ ++I; } /* Free register info */ CS_FreeRegInfo (S); /* Return the number of changes made */ return Changes; }
static unsigned OptStackPtrOps (CodeSeg* S) /* Merge adjacent calls to decsp into one. NOTE: This function won't merge all ** known cases! */ { unsigned Changes = 0; unsigned I; /* Walk over the entries */ I = 0; while (I < CS_GetEntryCount (S)) { unsigned Dec1; unsigned Dec2; const CodeEntry* N; /* Get the next entry */ const CodeEntry* E = CS_GetEntry (S, I); /* Check for decspn or subysp */ if (E->OPC == OP65_JSR && (Dec1 = IsDecSP (E)) > 0 && (N = CS_GetNextEntry (S, I)) != 0 && (Dec2 = IsDecSP (N)) > 0 && (Dec1 += Dec2) <= 255 && !CE_HasLabel (N)) { CodeEntry* X; char Buf[20]; /* We can combine the two */ if (Dec1 <= 8) { /* Insert a call to decsp */ xsprintf (Buf, sizeof (Buf), "decsp%u", Dec1); X = NewCodeEntry (OP65_JSR, AM65_ABS, Buf, 0, N->LI); CS_InsertEntry (S, X, I+2); } else { /* Insert a call to subysp */ const char* Arg = MakeHexArg (Dec1); X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, N->LI); CS_InsertEntry (S, X, I+2); X = NewCodeEntry (OP65_JSR, AM65_ABS, "subysp", 0, N->LI); CS_InsertEntry (S, X, I+3); } /* Delete the old code */ CS_DelEntries (S, I, 2); /* Regenerate register info */ CS_GenRegInfo (S); /* Remember we had changes */ ++Changes; } else { /* Next entry */ ++I; } } /* Return the number of changes made */ return Changes; }
void CS_Output (CodeSeg* S) /* Output the code segment data to the output file */ { unsigned I; const LineInfo* LI; /* Get the number of entries in this segment */ unsigned Count = CS_GetEntryCount (S); /* If the code segment is empty, bail out here */ if (Count == 0) { return; } /* Generate register info */ CS_GenRegInfo (S); /* Output the segment directive */ WriteOutput (".segment\t\"%s\"\n\n", S->SegName); /* Output all entries, prepended by the line information if it has changed */ LI = 0; for (I = 0; I < Count; ++I) { /* Get the next entry */ const CodeEntry* E = CollConstAt (&S->Entries, I); /* Check if the line info has changed. If so, output the source line * if the option is enabled and output debug line info if the debug * option is enabled. */ if (E->LI != LI) { /* Line info has changed, remember the new line info */ LI = E->LI; /* Add the source line as a comment. Beware: When line continuation * was used, the line may contain newlines. */ if (AddSource) { const char* L = LI->Line; WriteOutput (";\n; "); while (*L) { const char* N = strchr (L, '\n'); if (N) { /* We have a newline, just write the first part */ WriteOutput ("%.*s\n; ", (int) (N - L), L); L = N+1; } else { /* No Newline, write as is */ WriteOutput ("%s\n", L); break; } } WriteOutput (";\n"); } /* Add line debug info */ if (DebugInfo) { WriteOutput ("\t.dbg\tline, \"%s\", %u\n", GetInputName (LI), GetInputLine (LI)); } } /* Output the code */ CE_Output (E); } /* If debug info is enabled, terminate the last line number information */ if (DebugInfo) { WriteOutput ("\t.dbg\tline\n"); } /* Free register info */ CS_FreeRegInfo (S); }
unsigned OptPush1 (CodeSeg* S) /* Given a sequence * * jsr ldaxysp * jsr pushax * * If a/x are not used later, and Y is known, replace that by * * ldy #xx+2 * jsr pushwysp * * saving 3 bytes and several cycles. */ { unsigned I; unsigned Changes = 0; /* Generate register info */ CS_GenRegInfo (S); /* Walk over the entries */ I = 0; while (I < CS_GetEntryCount (S)) { CodeEntry* L[2]; /* Get next entry */ L[0] = CS_GetEntry (S, I); /* Check for the sequence */ if (CE_IsCallTo (L[0], "ldaxysp") && RegValIsKnown (L[0]->RI->In.RegY) && L[0]->RI->In.RegY < 0xFE && (L[1] = CS_GetNextEntry (S, I)) != 0 && !CE_HasLabel (L[1]) && CE_IsCallTo (L[1], "pushax") && !RegAXUsed (S, I+2)) { /* Insert new code behind the pushax */ const char* Arg; CodeEntry* X; /* ldy #xx+1 */ Arg = MakeHexArg (L[0]->RI->In.RegY+2); X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[0]->LI); CS_InsertEntry (S, X, I+2); /* jsr pushwysp */ X = NewCodeEntry (OP65_JSR, AM65_ABS, "pushwysp", 0, L[1]->LI); CS_InsertEntry (S, X, I+3); /* Delete the old code */ CS_DelEntries (S, I, 2); /* Remember, we had changes */ ++Changes; } /* Next entry */ ++I; } /* Free the register info */ CS_FreeRegInfo (S); /* Return the number of changes made */ return Changes; }