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; }
unsigned OptCmp6 (CodeSeg* S) /* Search for calls to compare subroutines followed by a conditional branch * and replace them by cheaper versions, since the branch means that the * boolean value returned by these routines is not needed (we may also check * that explicitly, but for the current code generator it is always true). */ { unsigned Changes = 0; /* Walk over the entries */ unsigned I = 0; while (I < CS_GetEntryCount (S)) { CodeEntry* N; cmp_t Cond; /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check for the sequence */ if (E->OPC == OP65_JSR && (Cond = FindTosCmpCond (E->Arg)) != CMP_INV && (N = CS_GetNextEntry (S, I)) != 0 && (N->Info & OF_ZBRA) != 0 && !CE_HasLabel (N)) { /* The tos... functions will return a boolean value in a/x and * the Z flag says if this value is zero or not. We will call * a cheaper subroutine instead, one that does not return a * boolean value but only valid flags. Note: jeq jumps if * the condition is not met, jne jumps if the condition is met. * Invert the code if we jump on condition not met. */ if (GetBranchCond (N->OPC) == BC_EQ) { /* Jumps if condition false, invert condition */ Cond = CmpInvertTab [Cond]; } /* Replace the subroutine call. */ E = NewCodeEntry (OP65_JSR, AM65_ABS, "tosicmp", 0, E->LI); CS_InsertEntry (S, E, I+1); CS_DelEntry (S, I); /* Replace the conditional branch */ ReplaceCmp (S, I+1, Cond); /* Remember, we had changes */ ++Changes; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
unsigned OptBoolTrans (CodeSeg* S) /* Try to remove the call to boolean transformer routines where the call is * not really needed. */ { unsigned Changes = 0; /* Walk over the entries */ unsigned I = 0; while (I < CS_GetEntryCount (S)) { CodeEntry* N; cmp_t Cond; /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check for a boolean transformer */ if (E->OPC == OP65_JSR && (Cond = FindBoolCmpCond (E->Arg)) != CMP_INV && (N = CS_GetNextEntry (S, I)) != 0 && (N->Info & OF_ZBRA) != 0) { /* Make the boolean transformer unnecessary by changing the * the conditional jump to evaluate the condition flags that * are set after the compare directly. Note: jeq jumps if * the condition is not met, jne jumps if the condition is met. * Invert the code if we jump on condition not met. */ if (GetBranchCond (N->OPC) == BC_EQ) { /* Jumps if condition false, invert condition */ Cond = CmpInvertTab [Cond]; } /* Check if we can replace the code by something better */ ReplaceCmp (S, I+1, Cond); /* Remove the call to the bool transformer */ CS_DelEntry (S, I); /* Remember, we had changes */ ++Changes; } /* Next entry */ ++I; } /* 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; }
static unsigned OptLoad3 (CodeSeg* S) /* Remove repeated loads from one and the same memory location */ { unsigned Changes = 0; CodeEntry* Load = 0; /* Walk over the entries */ unsigned I = 0; while (I < CS_GetEntryCount (S)) { /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Forget a preceeding load if we have a label */ if (Load && CE_HasLabel (E)) { Load = 0; } /* Check if this insn is a load */ if (E->Info & OF_LOAD) { 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 (Load != 0 && E->OPC == Load->OPC && E->AM == Load->AM && ((E->Arg == 0 && Load->Arg == 0) || strcmp (E->Arg, Load->Arg) == 0) && (N = CS_GetNextEntry (S, I)) != 0 && (N->Info & OF_CBRA) == 0) { /* Now remove the call to the subroutine */ CS_DelEntry (S, I); /* Remember, we had changes */ ++Changes; /* Next insn */ continue; } else { Load = E; } } else if ((E->Info & OF_CMP) == 0 && (E->Info & OF_CBRA) == 0) { /* Forget the first load on occurance of any insn we don't like */ Load = 0; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
unsigned OptCmp8 (CodeSeg* S) /* Check for register compares where the contents of the register and therefore * the result of the compare is known. */ { unsigned Changes = 0; unsigned I; /* Walk over the entries */ I = 0; while (I < CS_GetEntryCount (S)) { int RegVal; /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check for a compare against an immediate value */ if ((E->Info & OF_CMP) != 0 && (RegVal = GetCmpRegVal (E)) >= 0 && CE_IsConstImm (E)) { /* We are able to evaluate the compare at compile time. Check if * one or more branches are ahead. */ unsigned JumpsChanged = 0; CodeEntry* N; while ((N = CS_GetNextEntry (S, I)) != 0 && /* Followed by something.. */ (N->Info & OF_CBRA) != 0 && /* ..that is a cond branch.. */ !CE_HasLabel (N)) { /* ..and has no label */ /* Evaluate the branch condition */ int Cond; switch (GetBranchCond (N->OPC)) { case BC_CC: Cond = ((unsigned char)RegVal) < ((unsigned char)E->Num); break; case BC_CS: Cond = ((unsigned char)RegVal) >= ((unsigned char)E->Num); break; case BC_EQ: Cond = ((unsigned char)RegVal) == ((unsigned char)E->Num); break; case BC_MI: Cond = ((signed char)RegVal) < ((signed char)E->Num); break; case BC_NE: Cond = ((unsigned char)RegVal) != ((unsigned char)E->Num); break; case BC_PL: Cond = ((signed char)RegVal) >= ((signed char)E->Num); break; case BC_VC: case BC_VS: /* Not set by the compare operation, bail out (Note: * Just skipping anything here is rather stupid, but * the sequence is never generated by the compiler, * so it's quite safe to skip). */ goto NextEntry; default: Internal ("Unknown branch condition"); } /* If the condition is false, we may remove the jump. Otherwise * the branch will always be taken, so we may replace it by a * jump (and bail out). */ if (!Cond) { CS_DelEntry (S, I+1); } else { CodeLabel* L = N->JumpTo; const char* LabelName = L? L->Name : N->Arg; CodeEntry* X = NewCodeEntry (OP65_JMP, AM65_BRA, LabelName, L, N->LI); CS_InsertEntry (S, X, I+2); CS_DelEntry (S, I+1); } /* Remember, we had changes */ ++JumpsChanged; ++Changes; } /* If we have made changes above, we may also remove the compare */ if (JumpsChanged) { CS_DelEntry (S, I); } } NextEntry: /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
static void ReplaceCmp (CodeSeg* S, unsigned I, cmp_t Cond) /* Helper function for the replacement of routines that return a boolean * followed by a conditional jump. Instead of the boolean value, the condition * codes are evaluated directly. * I is the index of the conditional branch, the sequence is already checked * to be correct. */ { CodeEntry* N; CodeLabel* L; /* Get the entry */ CodeEntry* E = CS_GetEntry (S, I); /* Replace the conditional branch */ switch (Cond) { case CMP_EQ: CE_ReplaceOPC (E, OP65_JEQ); break; case CMP_NE: CE_ReplaceOPC (E, OP65_JNE); break; case CMP_GT: /* Replace by * beq @L * jpl Target * @L: ... */ if ((N = CS_GetNextEntry (S, I)) == 0) { /* No such entry */ Internal ("Invalid program flow"); } L = CS_GenLabel (S, N); N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI); CS_InsertEntry (S, N, I); CE_ReplaceOPC (E, OP65_JPL); break; case CMP_GE: CE_ReplaceOPC (E, OP65_JPL); break; case CMP_LT: CE_ReplaceOPC (E, OP65_JMI); break; case CMP_LE: /* Replace by * jmi Target * jeq Target */ CE_ReplaceOPC (E, OP65_JMI); L = E->JumpTo; N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI); CS_InsertEntry (S, N, I+1); break; case CMP_UGT: /* Replace by * beq @L * jcs Target * @L: ... */ if ((N = CS_GetNextEntry (S, I)) == 0) { /* No such entry */ Internal ("Invalid program flow"); } L = CS_GenLabel (S, N); N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI); CS_InsertEntry (S, N, I); CE_ReplaceOPC (E, OP65_JCS); break; case CMP_UGE: CE_ReplaceOPC (E, OP65_JCS); break; case CMP_ULT: CE_ReplaceOPC (E, OP65_JCC); break; case CMP_ULE: /* Replace by * jcc Target * jeq Target */ CE_ReplaceOPC (E, OP65_JCC); L = E->JumpTo; N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI); CS_InsertEntry (S, N, I+1); break; default: Internal ("Unknown jump condition: %d", Cond); } }
unsigned OptCmp3 (CodeSeg* S) /* Search for * * lda/and/ora/eor ... * cmp #$00 * jeq/jne * or * lda/and/ora/eor ... * cmp #$00 * jsr boolxx * * and remove the cmp. */ { unsigned Changes = 0; /* Walk over the entries */ unsigned 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_ADC || L[0]->OPC == OP65_AND || L[0]->OPC == OP65_ASL || L[0]->OPC == OP65_DEA || L[0]->OPC == OP65_EOR || L[0]->OPC == OP65_INA || L[0]->OPC == OP65_LDA || L[0]->OPC == OP65_LSR || L[0]->OPC == OP65_ORA || L[0]->OPC == OP65_PLA || L[0]->OPC == OP65_SBC || L[0]->OPC == OP65_TXA || L[0]->OPC == OP65_TYA) && !CS_RangeHasLabel (S, I+1, 2) && CS_GetEntries (S, L+1, I+1, 2) && L[1]->OPC == OP65_CMP && CE_IsKnownImm (L[1], 0)) { int Delete = 0; /* Check for the call to boolxx. We only remove the compare if * the carry flag is not evaluated later, because the load will * not set the carry flag. */ if (L[2]->OPC == OP65_JSR) { switch (FindBoolCmpCond (L[2]->Arg)) { case CMP_EQ: case CMP_NE: case CMP_GT: case CMP_GE: case CMP_LT: case CMP_LE: /* Remove the compare */ Delete = 1; break; case CMP_UGT: case CMP_UGE: case CMP_ULT: case CMP_ULE: case CMP_INV: /* Leave it alone */ break; } } else if ((L[2]->Info & OF_FBRA) != 0) { /* The following insn branches on the condition of the load, * so the compare instruction might be removed. For safety, * do some more checks if the carry isn't used later, since * the compare does set the carry, but the load does not. */ CodeEntry* E; CodeEntry* N; if ((E = CS_GetNextEntry (S, I+2)) != 0 && L[2]->JumpTo != 0 && (N = L[2]->JumpTo->Owner) != 0 && N->OPC != OP65_BCC && N->OPC != OP65_BCS && N->OPC != OP65_JCC && N->OPC != OP65_JCS && (N->OPC != OP65_JSR || FindBoolCmpCond (N->Arg) == CMP_INV)) { /* The following insn branches on the condition of a load, * and there's no use of the carry flag in sight, so the * compare instruction can be removed. */ Delete = 1; } } /* Delete the compare if we can */ if (Delete) { CS_DelEntry (S, I+1); ++Changes; } } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
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; /* 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; } /* Return the number of changes made */ return Changes; }
void CS_DelCodeRange (CodeSeg* S, unsigned First, unsigned Last) /* Delete all entries between first and last, both inclusive. The function * can only handle basic blocks (First is the only entry, Last the only exit) * and no open labels. It will call FAIL if any of these preconditions are * violated. */ { unsigned I; CodeEntry* FirstEntry; /* Do some sanity checks */ CHECK (First <= Last && Last < CS_GetEntryCount (S)); /* If Last is actually the last insn, call CS_DelCodeAfter instead, which * is more flexible in this case. */ if (Last == CS_GetEntryCount (S) - 1) { CS_DelCodeAfter (S, First); return; } /* Get the first entry and check if it has any labels. If it has, move * them to the insn following Last. If Last is the last insn of the code * segment, make them ownerless and move them to the label pool. */ FirstEntry = CS_GetEntry (S, First); if (CE_HasLabel (FirstEntry)) { /* Get the entry following last */ CodeEntry* FollowingEntry = CS_GetNextEntry (S, Last); if (FollowingEntry) { /* There is an entry after Last - move the labels */ CS_MoveLabels (S, FirstEntry, FollowingEntry); } else { /* Move the labels to the pool and clear the owner pointer */ CS_MoveLabelsToPool (S, FirstEntry); } } /* First pass: Delete all references to labels. If the reference count * for a label drops to zero, delete it. */ for (I = Last; I >= First; --I) { /* Get the next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check if this entry has a label reference */ if (E->JumpTo) { /* If the label is a label in the label pool, this is an error */ CodeLabel* L = E->JumpTo; CHECK (CollIndex (&S->Labels, L) < 0); /* Remove the reference to the label */ CS_RemoveLabelRef (S, E); } } /* Second pass: Delete the instructions. If a label attached to an * instruction still has references, it must be references from outside * the deleted area, which is an error. */ for (I = Last; I >= First; --I) { /* Get the next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check if this entry has a label attached */ CHECK (!CE_HasLabel (E)); /* Delete the pointer to the entry */ CollDelete (&S->Entries, I); /* Delete the entry itself */ FreeCodeEntry (E); } }
unsigned OptNegAX2 (CodeSeg* S) /* Search for a call to negax and replace it by * * ldx #$FF * eor #$FF * clc * adc #$01 * bne L1 * inx * L1: * * if X is known and zero on entry. */ { unsigned Changes = 0; unsigned I; /* Walk over the entries */ I = 0; while (I < CS_GetEntryCount (S)) { CodeEntry* P; /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check if this is a call to negax, and if X is known and zero */ if (E->RI->In.RegX == 0 && CE_IsCallTo (E, "negax") && (P = CS_GetNextEntry (S, I)) != 0) { CodeEntry* X; CodeLabel* L; /* Add replacement code behind */ /* ldx #$FF */ X = NewCodeEntry (OP65_LDX, AM65_IMM, "$FF", 0, E->LI); CS_InsertEntry (S, X, I+1); /* eor #$FF */ X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI); CS_InsertEntry (S, X, I+2); /* clc */ X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, E->LI); CS_InsertEntry (S, X, I+3); /* adc #$01 */ X = NewCodeEntry (OP65_ADC, AM65_IMM, "$01", 0, E->LI); CS_InsertEntry (S, X, I+4); /* Get the label attached to the insn following the call */ L = CS_GenLabel (S, P); /* bne L */ X = NewCodeEntry (OP65_BNE, AM65_BRA, L->Name, L, E->LI); CS_InsertEntry (S, X, I+5); /* inx */ X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, E->LI); CS_InsertEntry (S, X, I+6); /* Delete the call to negax */ CS_DelEntry (S, I); /* Skip the generated code */ I += 5; /* We had changes */ ++Changes; } /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }
unsigned OptShift1 (CodeSeg* S) /* A call to the shlaxN routine may get replaced by one or more asl insns * if the value of X is not used later. If X is used later, but it is zero * on entry and it's a shift by one, it may get replaced by: * * asl a * bcc L1 * inx * L1: * */ { unsigned Changes = 0; unsigned I; /* Walk over the entries */ I = 0; while (I < CS_GetEntryCount (S)) { unsigned Shift; CodeEntry* N; CodeEntry* X; CodeLabel* L; /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check for the sequence */ if (E->OPC == OP65_JSR && (Shift = GetShift (E->Arg)) != SHIFT_NONE && SHIFT_DIR (Shift) == SHIFT_DIR_LEFT) { unsigned Count = SHIFT_COUNT (Shift); if (!RegXUsed (S, I+1)) { if (Count == SHIFT_COUNT_Y) { CodeLabel* L; if (S->CodeSizeFactor < 200) { goto NextEntry; } /* Change into * * L1: asl a * dey * bpl L1 * ror a */ /* asl a */ X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI); CS_InsertEntry (S, X, I+1); L = CS_GenLabel (S, X); /* dey */ X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, E->LI); CS_InsertEntry (S, X, I+2); /* bpl L1 */ X = NewCodeEntry (OP65_BPL, AM65_BRA, L->Name, L, E->LI); CS_InsertEntry (S, X, I+3); /* ror a */ X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, E->LI); CS_InsertEntry (S, X, I+4); } else { /* Insert shift insns */ while (Count--) { X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI); CS_InsertEntry (S, X, I+1); } } } else if (E->RI->In.RegX == 0 && Count == 1 && (N = CS_GetNextEntry (S, I)) != 0) { /* asl a */ X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI); CS_InsertEntry (S, X, I+1); /* bcc L1 */ L = CS_GenLabel (S, N); X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, E->LI); CS_InsertEntry (S, X, I+2); /* inx */ X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, E->LI); CS_InsertEntry (S, X, I+3); } else { /* We won't handle this one */ goto NextEntry; } /* Delete the call to shlax */ CS_DelEntry (S, I); /* Remember, we had changes */ ++Changes; } NextEntry: /* Next entry */ ++I; } /* Return the number of changes made */ return Changes; }