X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fcc65%2Fcodeopt.c;h=498aa0ff2b889c6fc0a14b2e2da58db40663dc6c;hb=dca99d59e51515dbf511271afa093247dbd6869a;hp=566fdf954dfbfa92ffe02c2d313dfdf62a24eacf;hpb=4185caf8558690ae4720d4a83d829ed3ed087ae9;p=cc65 diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index 566fdf954..498aa0ff2 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -69,7 +69,7 @@ #include "error.h" #include "global.h" #include "output.h" - +#include "symtab.h" /*****************************************************************************/ @@ -80,8 +80,8 @@ 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; @@ -161,8 +161,8 @@ static unsigned OptLoad2 (CodeSeg* S) !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); @@ -251,8 +251,8 @@ static unsigned OptLoad3 (CodeSeg* S) 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 && @@ -299,21 +299,21 @@ static unsigned OptLoad3 (CodeSeg* S) 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; @@ -529,9 +529,9 @@ static unsigned OptDecouple (CodeSeg* S) 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') { @@ -549,8 +549,8 @@ static unsigned IsDecSP (const CodeEntry* E) 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; @@ -613,7 +613,116 @@ static unsigned OptStackPtrOps (CodeSeg* S) 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 */ @@ -675,6 +784,7 @@ static OptFunc DOptDeadCode = { OptDeadCode, "OptDeadCode", 100, 0, 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 }; @@ -704,6 +814,8 @@ static OptFunc DOptPtrLoad14 = { OptPtrLoad14, "OptPtrLoad14", 108, 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 }; @@ -774,6 +886,7 @@ static OptFunc* OptFuncs[] = { &DOptDeadJumps, &DOptDecouple, &DOptDupLoads, + &DOptGotoSPAdj, &DOptIndLoads1, &DOptIndLoads2, &DOptJumpCascades, @@ -794,6 +907,8 @@ static OptFunc* OptFuncs[] = { &DOptPtrLoad15, &DOptPtrLoad16, &DOptPtrLoad17, + &DOptPtrLoad18, + &DOptPtrLoad19, &DOptPtrLoad2, &DOptPtrLoad3, &DOptPtrLoad4, @@ -851,8 +966,8 @@ static int CmpOptStep (const void* Key, const void* Func) 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); @@ -863,14 +978,14 @@ static OptFunc* FindOptFunc (const char* Name) 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; } @@ -911,6 +1026,8 @@ void ListOptSteps (FILE* 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); } @@ -1055,10 +1172,10 @@ static void WriteDebugOutput (CodeSeg* S, const char* Step) /* 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 : ""); } else { - WriteOutput ("Code after applying `%s':\n", Step); + WriteOutput ("Code after applying '%s':\n", Step); } /* Output the code segment */ @@ -1074,8 +1191,8 @@ static unsigned RunOptFunc (CodeSeg* S, OptFunc* F, unsigned Max) 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; } @@ -1113,13 +1230,14 @@ static unsigned RunOptFunc (CodeSeg* S, OptFunc* F, unsigned Max) 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); @@ -1131,6 +1249,8 @@ static unsigned RunOptGroup1 (CodeSeg* S) Changes += RunOptFunc (S, &DOptPtrLoad4, 1); Changes += RunOptFunc (S, &DOptPtrLoad5, 1); Changes += RunOptFunc (S, &DOptPtrLoad6, 1); + Changes += RunOptFunc (S, &DOptPtrLoad18, 1); /* Before OptPtrLoad7 */ + Changes += RunOptFunc (S, &DOptPtrLoad19, 1); /* Before OptPtrLoad7 */ Changes += RunOptFunc (S, &DOptPtrLoad7, 1); Changes += RunOptFunc (S, &DOptPtrLoad11, 1); Changes += RunOptFunc (S, &DOptPtrLoad12, 1); @@ -1168,10 +1288,10 @@ static unsigned RunOptGroup1 (CodeSeg* S) 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; @@ -1185,9 +1305,9 @@ static unsigned RunOptGroup2 (CodeSeg* S) 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; @@ -1254,8 +1374,8 @@ static unsigned RunOptGroup3 (CodeSeg* S) 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; @@ -1287,9 +1407,9 @@ static unsigned RunOptGroup5 (CodeSeg* S) 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); } } @@ -1302,18 +1422,18 @@ static unsigned RunOptGroup5 (CodeSeg* S) 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); @@ -1328,21 +1448,21 @@ static unsigned RunOptGroup6 (CodeSeg* S) 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); @@ -1353,8 +1473,8 @@ static unsigned RunOptGroup7 (CodeSeg* S) 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); @@ -1366,8 +1486,8 @@ static unsigned RunOptGroup7 (CodeSeg* S) 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) { @@ -1398,7 +1518,7 @@ void RunOpt (CodeSeg* S) /* 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"); }