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; }
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; }
void CS_GenRegInfo (CodeSeg* S) /* Generate register infos for all instructions */ { unsigned I; RegContents Regs; /* Initial register contents */ RegContents* CurrentRegs; /* Current register contents */ int WasJump; /* True if last insn was a jump */ int Done; /* All runs done flag */ /* Be sure to delete all register infos */ CS_FreeRegInfo (S); /* We may need two runs to get back references right */ do { /* Assume we're done after this run */ Done = 1; /* On entry, the register contents are unknown */ RC_Invalidate (&Regs); CurrentRegs = &Regs; /* Walk over all insns and note just the changes from one insn to the * next one. */ WasJump = 0; for (I = 0; I < CS_GetEntryCount (S); ++I) { CodeEntry* P; /* Get the next instruction */ CodeEntry* E = CollAtUnchecked (&S->Entries, I); /* If the instruction has a label, we need some special handling */ unsigned LabelCount = CE_GetLabelCount (E); if (LabelCount > 0) { /* Loop over all entry points that jump here. If these entry * points already have register info, check if all values are * known and identical. If all values are identical, and the * preceeding instruction was not an unconditional branch, check * if the register value on exit of the preceeding instruction * is also identical. If all these values are identical, the * value of a register is known, otherwise it is unknown. */ CodeLabel* Label = CE_GetLabel (E, 0); unsigned Entry; if (WasJump) { /* Preceeding insn was an unconditional branch */ CodeEntry* J = CL_GetRef(Label, 0); if (J->RI) { Regs = J->RI->Out2; } else { RC_Invalidate (&Regs); } Entry = 1; } else { Regs = *CurrentRegs; Entry = 0; } while (Entry < CL_GetRefCount (Label)) { /* Get this entry */ CodeEntry* J = CL_GetRef (Label, Entry); if (J->RI == 0) { /* No register info for this entry. This means that the * instruction that jumps here is at higher addresses and * the jump is a backward jump. We need a second run to * get the register info right in this case. Until then, * assume unknown register contents. */ Done = 0; RC_Invalidate (&Regs); break; } if (J->RI->Out2.RegA != Regs.RegA) { Regs.RegA = UNKNOWN_REGVAL; } if (J->RI->Out2.RegX != Regs.RegX) { Regs.RegX = UNKNOWN_REGVAL; } if (J->RI->Out2.RegY != Regs.RegY) { Regs.RegY = UNKNOWN_REGVAL; } if (J->RI->Out2.SRegLo != Regs.SRegLo) { Regs.SRegLo = UNKNOWN_REGVAL; } if (J->RI->Out2.SRegHi != Regs.SRegHi) { Regs.SRegHi = UNKNOWN_REGVAL; } if (J->RI->Out2.Tmp1 != Regs.Tmp1) { Regs.Tmp1 = UNKNOWN_REGVAL; } ++Entry; } /* Use this register info */ CurrentRegs = &Regs; } /* Generate register info for this instruction */ CE_GenRegInfo (E, CurrentRegs); /* Remember for the next insn if this insn was an uncondition branch */ WasJump = (E->Info & OF_UBRA) != 0; /* Output registers for this insn are input for the next */ CurrentRegs = &E->RI->Out; /* If this insn is a branch on zero flag, we may have more info on * register contents for one of both flow directions, but only if * there is a previous instruction. */ if ((E->Info & OF_ZBRA) != 0 && (P = CS_GetPrevEntry (S, I)) != 0) { /* Get the branch condition */ bc_t BC = GetBranchCond (E->OPC); /* Check the previous instruction */ switch (P->OPC) { case OP65_ADC: case OP65_AND: case OP65_DEA: case OP65_EOR: case OP65_INA: case OP65_LDA: case OP65_ORA: case OP65_PLA: case OP65_SBC: /* A is zero in one execution flow direction */ if (BC == BC_EQ) { E->RI->Out2.RegA = 0; } else { E->RI->Out.RegA = 0; } break; case OP65_CMP: /* If this is an immidiate compare, the A register has * the value of the compare later. */ if (CE_IsConstImm (P)) { if (BC == BC_EQ) { E->RI->Out2.RegA = (unsigned char)P->Num; } else { E->RI->Out.RegA = (unsigned char)P->Num; } } break; case OP65_CPX: /* If this is an immidiate compare, the X register has * the value of the compare later. */ if (CE_IsConstImm (P)) { if (BC == BC_EQ) { E->RI->Out2.RegX = (unsigned char)P->Num; } else { E->RI->Out.RegX = (unsigned char)P->Num; } } break; case OP65_CPY: /* If this is an immidiate compare, the Y register has * the value of the compare later. */ if (CE_IsConstImm (P)) { if (BC == BC_EQ) { E->RI->Out2.RegY = (unsigned char)P->Num; } else { E->RI->Out.RegY = (unsigned char)P->Num; } } break; case OP65_DEX: case OP65_INX: case OP65_LDX: case OP65_PLX: /* X is zero in one execution flow direction */ if (BC == BC_EQ) { E->RI->Out2.RegX = 0; } else { E->RI->Out.RegX = 0; } break; case OP65_DEY: case OP65_INY: case OP65_LDY: case OP65_PLY: /* X is zero in one execution flow direction */ if (BC == BC_EQ) { E->RI->Out2.RegY = 0; } else { E->RI->Out.RegY = 0; } break; case OP65_TAX: case OP65_TXA: /* If the branch is a beq, both A and X are zero at the * branch target, otherwise they are zero at the next * insn. */ if (BC == BC_EQ) { E->RI->Out2.RegA = E->RI->Out2.RegX = 0; } else { E->RI->Out.RegA = E->RI->Out.RegX = 0; } break; case OP65_TAY: case OP65_TYA: /* If the branch is a beq, both A and Y are zero at the * branch target, otherwise they are zero at the next * insn. */ if (BC == BC_EQ) { E->RI->Out2.RegA = E->RI->Out2.RegY = 0; } else { E->RI->Out.RegA = E->RI->Out.RegY = 0; } break; default: break; } } } } while (!Done); }
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; }