1 /*****************************************************************************/
5 /* Optimizer subroutines */
9 /* (C) 2001 Ullrich von Bassewitz */
11 /* D-70597 Stuttgart */
12 /* EMail: uz@cc65.org */
15 /* This software is provided 'as-is', without any expressed or implied */
16 /* warranty. In no event will the authors be held liable for any damages */
17 /* arising from the use of this software. */
19 /* Permission is granted to anyone to use this software for any purpose, */
20 /* including commercial applications, and to alter it and redistribute it */
21 /* freely, subject to the following restrictions: */
23 /* 1. The origin of this software must not be misrepresented; you must not */
24 /* claim that you wrote the original software. If you use this software */
25 /* in a product, an acknowledgment in the product documentation would be */
26 /* appreciated but is not required. */
27 /* 2. Altered source versions must be plainly marked as such, and must not */
28 /* be misrepresented as being the original software. */
29 /* 3. This notice may not be removed or altered from any source */
32 /*****************************************************************************/
56 /*****************************************************************************/
58 /*****************************************************************************/
62 /* Defines for the conditions in a compare */
77 /* Table with the compare suffixes */
78 static const char CmpSuffixTab [][4] = {
79 "eq", "ne", "gt", "ge", "lt", "le", "ugt", "uge", "ult", "ule"
82 /* Table used to invert a condition, indexed by condition */
83 static const unsigned char CmpInvertTab [] = {
85 CMP_LE, CMP_LT, CMP_GE, CMP_GT,
86 CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT
89 /* Table to show which compares are signed (use the N flag) */
90 static const char CmpSignedTab [] = {
91 0, 0, 1, 1, 1, 1, 0, 0, 0, 0
96 /*****************************************************************************/
97 /* Helper functions */
98 /*****************************************************************************/
102 static cmp_t FindCmpCond (const char* Code, unsigned CodeLen)
103 /* Search for a compare condition by the given code using the given length */
108 for (I = 0; I < sizeof (CmpSuffixTab) / sizeof (CmpSuffixTab [0]); ++I) {
109 if (strncmp (Code, CmpSuffixTab [I], CodeLen) == 0) {
121 static cmp_t FindBoolCmpCond (const char* Name)
122 /* Map a condition suffix to a code. Return the code or CMP_INV on failure */
124 /* Check for the correct subroutine name */
125 if (strncmp (Name, "bool", 4) == 0) {
126 /* Name is ok, search for the code in the table */
127 return FindCmpCond (Name+4, strlen(Name)-4);
136 static cmp_t FindTosCmpCond (const char* Name)
137 /* Check if this is a call to one of the TOS compare functions (tosgtax).
138 * Return the condition code or CMP_INV on failure.
141 unsigned Len = strlen (Name);
143 /* Check for the correct subroutine name */
144 if (strncmp (Name, "tos", 3) == 0 && strcmp (Name+Len-2, "ax") == 0) {
145 /* Name is ok, search for the code in the table */
146 return FindCmpCond (Name+3, Len-3-2);
155 static void ReplaceCmp (CodeSeg* S, unsigned I, cmp_t Cond)
156 /* Helper function for the replacement of routines that return a boolean
157 * followed by a conditional jump. Instead of the boolean value, the condition
158 * codes are evaluated directly.
159 * I is the index of the conditional branch, the sequence is already checked
167 CodeEntry* E = CS_GetEntry (S, I);
169 /* Replace the conditional branch */
173 CE_ReplaceOPC (E, OP65_JEQ);
177 CE_ReplaceOPC (E, OP65_JNE);
186 if ((N = CS_GetNextEntry (S, I)) == 0) {
188 Internal ("Invalid program flow");
190 L = CS_GenLabel (S, N);
191 N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI);
192 CS_InsertEntry (S, N, I);
193 CE_ReplaceOPC (E, OP65_JPL);
197 CE_ReplaceOPC (E, OP65_JPL);
201 CE_ReplaceOPC (E, OP65_JMI);
209 CE_ReplaceOPC (E, OP65_JMI);
211 N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI);
212 CS_InsertEntry (S, N, I+1);
221 if ((N = CS_GetNextEntry (S, I)) == 0) {
223 Internal ("Invalid program flow");
225 L = CS_GenLabel (S, N);
226 N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI);
227 CS_InsertEntry (S, N, I);
228 CE_ReplaceOPC (E, OP65_JCS);
232 CE_ReplaceOPC (E, OP65_JCS);
236 CE_ReplaceOPC (E, OP65_JCC);
244 CE_ReplaceOPC (E, OP65_JCC);
246 N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI);
247 CS_InsertEntry (S, N, I+1);
251 Internal ("Unknown jump condition: %d", Cond);
259 static int IsCmpToZero (const CodeEntry* E)
260 /* Check if the given instrcuction is a compare to zero instruction */
262 return (E->OPC == OP65_CMP &&
264 (E->Flags & CEF_NUMARG) != 0 &&
270 static int IsSpLoad (const CodeEntry* E)
271 /* Return true if this is the load of A from the stack */
273 return E->OPC == OP65_LDA && E->AM == AM65_ZP_INDY && strcmp (E->Arg, "sp") == 0;
278 static int IsLocalLoad16 (CodeSeg* S, unsigned Index,
279 CodeEntry** L, unsigned Count)
280 /* Check if a 16 bit load of a local variable follows:
288 * If so, read Count entries following the first ldy into L and return true
289 * if this is possible. Otherwise return false.
292 /* Be sure we read enough entries for the check */
295 /* Read the first entry */
296 L[0] = CS_GetEntry (S, Index);
298 /* Check for the sequence */
299 return (L[0]->OPC == OP65_LDY &&
300 L[0]->AM == AM65_IMM &&
301 (L[0]->Flags & CEF_NUMARG) != 0 &&
302 CS_GetEntries (S, L+1, Index+1, Count-1) &&
304 !CE_HasLabel (L[1]) &&
305 L[2]->OPC == OP65_TAX &&
306 !CE_HasLabel (L[2]) &&
307 L[3]->OPC == OP65_DEY &&
308 !CE_HasLabel (L[3]) &&
310 !CE_HasLabel (L[4]));
315 static int IsImmCmp16 (CodeSeg* S, CodeEntry** L)
316 /* Check if the instructions at L are an immidiate compare of a/x:
321 return (L[0]->OPC == OP65_CPX &&
322 L[0]->AM == AM65_IMM &&
323 (L[0]->Flags & CEF_NUMARG) != 0 &&
324 !CE_HasLabel (L[0]) &&
325 (L[1]->OPC == OP65_JNE || L[1]->OPC == OP65_BNE) &&
327 !CE_HasLabel (L[1]) &&
328 L[2]->OPC == OP65_CMP &&
329 L[2]->AM == AM65_IMM &&
330 (L[2]->Flags & CEF_NUMARG) != 0 &&
331 (L[3]->Info & OF_ZBRA) != 0 &&
333 (L[1]->JumpTo->Owner == L[3] || L[1]->JumpTo == L[3]->JumpTo));
338 /*****************************************************************************/
339 /* Remove calls to the bool transformer subroutines */
340 /*****************************************************************************/
344 static unsigned OptBoolTransforms (CodeSeg* S)
345 /* Try to remove the call to boolean transformer routines where the call is
349 unsigned Changes = 0;
351 /* Walk over the entries */
353 while (I < CS_GetEntryCount (S)) {
359 CodeEntry* E = CS_GetEntry (S, I);
361 /* Check for a boolean transformer */
362 if (E->OPC == OP65_JSR &&
363 (Cond = FindBoolCmpCond (E->Arg)) != CMP_INV &&
364 (N = CS_GetNextEntry (S, I)) != 0 &&
365 (N->Info & OF_ZBRA) != 0) {
367 /* Make the boolean transformer unnecessary by changing the
368 * the conditional jump to evaluate the condition flags that
369 * are set after the compare directly. Note: jeq jumps if
370 * the condition is not met, jne jumps if the condition is met.
371 * Invert the code if we jump on condition not met.
373 if (GetBranchCond (N->OPC) == BC_EQ) {
374 /* Jumps if condition false, invert condition */
375 Cond = CmpInvertTab [Cond];
378 /* Check if we can replace the code by something better */
379 ReplaceCmp (S, I+1, Cond);
381 /* Remove the call to the bool transformer */
384 /* Remember, we had changes */
394 /* Return the number of changes made */
400 /*****************************************************************************/
401 /* Optimize subtractions */
402 /*****************************************************************************/
406 static unsigned OptSub1 (CodeSeg* S)
407 /* Search for the sequence
414 * and remove the handling of the high byte if X is not used later.
417 unsigned Changes = 0;
419 /* Walk over the entries */
421 while (I < CS_GetEntryCount (S)) {
426 CodeEntry* E = CS_GetEntry (S, I);
428 /* Check for the sequence */
429 if (E->OPC == OP65_SBC &&
430 CS_GetEntries (S, L, I+1, 3) &&
431 (L[0]->OPC == OP65_BCS || L[0]->OPC == OP65_JCS) &&
433 !CE_HasLabel (L[0]) &&
434 L[1]->OPC == OP65_DEX &&
435 !CE_HasLabel (L[1]) &&
436 L[0]->JumpTo->Owner == L[2] &&
437 !RegXUsed (S, I+3)) {
439 /* Remove the bcs/dex */
440 CS_DelEntries (S, I+1, 2);
442 /* Remember, we had changes */
452 /* Return the number of changes made */
458 static unsigned OptSub2 (CodeSeg* S)
459 /* Search for the sequence
476 unsigned Changes = 0;
478 /* Walk over the entries */
480 while (I < CS_GetEntryCount (S)) {
485 CodeEntry* E = CS_GetEntry (S, I);
487 /* Check for the sequence */
488 if (E->OPC == OP65_LDA &&
489 CS_GetEntries (S, L, I+1, 5) &&
490 L[0]->OPC == OP65_SEC &&
491 !CE_HasLabel (L[0]) &&
492 L[1]->OPC == OP65_STA &&
493 strcmp (L[1]->Arg, "tmp1") == 0 &&
494 !CE_HasLabel (L[1]) &&
495 L[2]->OPC == OP65_LDA &&
496 !CE_HasLabel (L[2]) &&
497 L[3]->OPC == OP65_SBC &&
498 strcmp (L[3]->Arg, "tmp1") == 0 &&
499 !CE_HasLabel (L[3]) &&
500 L[4]->OPC == OP65_STA &&
501 strcmp (L[4]->Arg, L[2]->Arg) == 0 &&
502 !CE_HasLabel (L[4])) {
504 /* Remove the store to tmp1 */
505 CS_DelEntry (S, I+2);
507 /* Remove the subtraction */
508 CS_DelEntry (S, I+3);
510 /* Move the lda to the position of the subtraction and change the
513 CS_MoveEntry (S, I, I+3);
514 CE_ReplaceOPC (E, OP65_SBC);
516 /* If the sequence head had a label, move this label back to the
519 if (CE_HasLabel (E)) {
520 CS_MoveLabels (S, E, L[0]);
523 /* Remember, we had changes */
533 /* Return the number of changes made */
539 /*****************************************************************************/
540 /* Optimize additions */
541 /*****************************************************************************/
545 static unsigned OptAdd1 (CodeSeg* S)
546 /* Search for the sequence
564 unsigned Changes = 0;
566 /* Walk over the entries */
568 while (I < CS_GetEntryCount (S)) {
573 CodeEntry* E = CS_GetEntry (S, I);
575 /* Check for the sequence */
576 if (E->OPC == OP65_JSR &&
577 strcmp (E->Arg, "pushax") == 0 &&
578 CS_GetEntries (S, L, I+1, 5) &&
579 L[0]->OPC == OP65_LDY &&
580 CE_KnownImm (L[0]) &&
581 !CE_HasLabel (L[0]) &&
582 L[1]->OPC == OP65_LDX &&
583 CE_KnownImm (L[1]) &&
585 !CE_HasLabel (L[1]) &&
586 L[2]->OPC == OP65_LDA &&
587 !CE_HasLabel (L[2]) &&
588 L[3]->OPC == OP65_JSR &&
589 strcmp (L[3]->Arg, "tosaddax") == 0 &&
590 !CE_HasLabel (L[3])) {
596 /* Remove the call to pushax */
599 /* Correct the stack offset (needed since pushax was removed) */
601 xsprintf (Buf, sizeof (Buf), "$%02lX", L[0]->Num);
602 CE_SetArg (L[0], Buf);
605 X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, L[3]->LI);
606 CS_InsertEntry (S, X, I+1);
608 /* Remove the load */
609 CS_DelEntry (S, I+3); /* lda */
610 CS_DelEntry (S, I+2); /* ldx */
613 X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp", 0, L[3]->LI);
614 CS_InsertEntry (S, X, I+2);
616 /* Generate the branch label and the branch */
617 Label = CS_GenLabel (S, L[4]);
618 X = NewCodeEntry (OP65_BCC, AM65_BRA, Label->Name, Label, L[3]->LI);
619 CS_InsertEntry (S, X, I+3);
621 /* Generate the increment of the high byte */
622 X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, L[3]->LI);
623 CS_InsertEntry (S, X, I+4);
625 /* Delete the call to tosaddax */
626 CS_DelEntry (S, I+5);
628 /* Remember, we had changes */
638 /* Return the number of changes made */
644 static unsigned OptAdd2 (CodeSeg* S)
645 /* Search for the sequence
669 * provided that a/x is not used later.
672 unsigned Changes = 0;
674 /* Walk over the entries */
676 while (I < CS_GetEntryCount (S)) {
681 L[0] = CS_GetEntry (S, I);
683 /* Check for the sequence */
684 if (L[0]->OPC == OP65_LDY &&
685 CE_KnownImm (L[0]) &&
686 CS_GetEntries (S, L+1, I+1, 6) &&
687 L[1]->OPC == OP65_LDA &&
688 L[1]->AM == AM65_ZP_INDY &&
689 !CE_HasLabel (L[1]) &&
690 L[2]->OPC == OP65_TAX &&
691 !CE_HasLabel (L[2]) &&
692 L[3]->OPC == OP65_DEY &&
693 !CE_HasLabel (L[3]) &&
694 L[4]->OPC == OP65_LDA &&
695 L[4]->AM == AM65_ZP_INDY &&
696 !CE_HasLabel (L[4]) &&
697 L[5]->OPC == OP65_LDY &&
698 CE_KnownImm (L[5]) &&
699 !CE_HasLabel (L[5]) &&
700 L[6]->OPC == OP65_JSR &&
701 strcmp (L[6]->Arg, "addeqysp") == 0 &&
702 !CE_HasLabel (L[6]) &&
703 (GetRegInfo (S, I+7) & REG_AX) == 0) {
710 /* Create a replacement for the first LDY */
711 Offs = (int) (L[0]->Num - 1);
712 xsprintf (Buf, sizeof (Buf), "$%02X", Offs);
713 X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[0]->LI);
714 CS_InsertEntry (S, X, I+1);
717 /* Load Y with the low offset of the target variable */
718 X = NewCodeEntry (OP65_LDY, AM65_IMM, L[5]->Arg, 0, L[1]->LI);
719 CS_InsertEntry (S, X, I+2);
722 X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, L[1]->LI);
723 CS_InsertEntry (S, X, I+3);
725 /* Remove the TAX/DEY sequence */
726 CS_DelEntry (S, I+5); /* dey */
727 CS_DelEntry (S, I+4); /* tax */
729 /* Addition of the low byte */
730 X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp", 0, L[4]->LI);
731 CS_InsertEntry (S, X, I+4);
732 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "sp", 0, L[4]->LI);
733 CS_InsertEntry (S, X, I+5);
736 xsprintf (Buf, sizeof (Buf), "$%02X", (Offs+1));
737 X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[4]->LI);
738 CS_InsertEntry (S, X, I+6);
740 /* Addition of the high byte */
741 xsprintf (Buf, sizeof (Buf), "$%02X", (int)(L[5]->Num+1));
742 X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[5]->LI);
743 CS_InsertEntry (S, X, I+8);
744 X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp", 0, L[6]->LI);
745 CS_InsertEntry (S, X, I+9);
746 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "sp", 0, L[6]->LI);
747 CS_InsertEntry (S, X, I+10);
749 /* Delete the remaining stuff */
750 CS_DelEntry (S, I+12);
751 CS_DelEntry (S, I+11);
753 /* Remember, we had changes */
763 /* Return the number of changes made */
769 static unsigned OptAdd3 (CodeSeg* S)
770 /* Search for the sequence
777 * and remove the handling of the high byte if X is not used later.
780 unsigned Changes = 0;
782 /* Walk over the entries */
784 while (I < CS_GetEntryCount (S)) {
789 CodeEntry* E = CS_GetEntry (S, I);
791 /* Check for the sequence */
792 if (E->OPC == OP65_ADC &&
793 CS_GetEntries (S, L, I+1, 3) &&
794 (L[0]->OPC == OP65_BCC || L[0]->OPC == OP65_JCC) &&
796 !CE_HasLabel (L[0]) &&
797 L[1]->OPC == OP65_INX &&
798 !CE_HasLabel (L[1]) &&
799 L[0]->JumpTo->Owner == L[2] &&
800 !RegXUsed (S, I+3)) {
802 /* Remove the bcs/dex */
803 CS_DelEntries (S, I+1, 2);
805 /* Remember, we had changes */
815 /* Return the number of changes made */
821 /*****************************************************************************/
822 /* Optimize shifts */
823 /*****************************************************************************/
827 static unsigned OptShift1 (CodeSeg* S)
828 /* A call to the shlaxN routine may get replaced by one or more asl insns
829 * if the value of X is not used later.
832 unsigned Changes = 0;
834 /* Walk over the entries */
836 while (I < CS_GetEntryCount (S)) {
839 CodeEntry* E = CS_GetEntry (S, I);
841 /* Check for the sequence */
842 if (E->OPC == OP65_JSR &&
843 (strncmp (E->Arg, "shlax", 5) == 0 ||
844 strncmp (E->Arg, "aslax", 5) == 0) &&
845 strlen (E->Arg) == 6 &&
846 IsDigit (E->Arg[5]) &&
847 !RegXUsed (S, I+1)) {
849 /* Insert shift insns */
850 unsigned Count = E->Arg[5] - '0';
852 CodeEntry* X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI);
853 CS_InsertEntry (S, X, I+1);
856 /* Delete the call to shlax */
859 /* Remember, we had changes */
869 /* Return the number of changes made */
875 /*****************************************************************************/
876 /* Optimizations for compares */
877 /*****************************************************************************/
881 static unsigned OptCmp1 (CodeSeg* S)
882 /* Search for the sequence
894 unsigned Changes = 0;
896 /* Walk over the entries */
898 while (I < CS_GetEntryCount (S)) {
903 CodeEntry* E = CS_GetEntry (S, I);
905 /* Check for the sequence */
906 if (E->OPC == OP65_STX &&
907 CS_GetEntries (S, L, I+1, 2) &&
908 L[0]->OPC == OP65_STX &&
909 strcmp (L[0]->Arg, "tmp1") == 0 &&
910 !CE_HasLabel (L[0]) &&
911 L[1]->OPC == OP65_ORA &&
912 strcmp (L[1]->Arg, "tmp1") == 0 &&
913 !CE_HasLabel (L[1])) {
915 /* Remove the remaining instructions */
916 CS_DelEntries (S, I+1, 2);
918 /* Insert the ora instead */
919 CS_InsertEntry (S, NewCodeEntry (OP65_ORA, E->AM, E->Arg, 0, E->LI), I+1);
921 /* Remember, we had changes */
931 /* Return the number of changes made */
937 static unsigned OptCmp2 (CodeSeg* S)
940 * lda/and/ora/eor ...
944 * and remove the cmp.
947 unsigned Changes = 0;
949 /* Walk over the entries */
951 while (I < CS_GetEntryCount (S)) {
956 CodeEntry* E = CS_GetEntry (S, I);
958 /* Check for the sequence */
959 if ((E->OPC == OP65_ADC ||
960 E->OPC == OP65_AND ||
961 E->OPC == OP65_DEA ||
962 E->OPC == OP65_EOR ||
963 E->OPC == OP65_INA ||
964 E->OPC == OP65_LDA ||
965 E->OPC == OP65_ORA ||
966 E->OPC == OP65_PLA ||
967 E->OPC == OP65_SBC ||
968 E->OPC == OP65_TXA ||
969 E->OPC == OP65_TYA) &&
970 CS_GetEntries (S, L, I+1, 2) &&
971 IsCmpToZero (L[0]) &&
972 !CE_HasLabel (L[0]) &&
973 (L[1]->Info & OF_FBRA) != 0 &&
974 !CE_HasLabel (L[1])) {
976 /* Remove the compare */
977 CS_DelEntry (S, I+1);
979 /* Remember, we had changes */
989 /* Return the number of changes made */
995 static unsigned OptCmp3 (CodeSeg* S)
1005 * If a is zero, we may remove the compare. If a and b are both zero, we may
1006 * replace it by the sequence
1012 * L1 may be either the label at the branch instruction, or the target label
1013 * of this instruction.
1016 unsigned Changes = 0;
1018 /* Walk over the entries */
1020 while (I < CS_GetEntryCount (S)) {
1024 /* Get next entry */
1025 CodeEntry* E = CS_GetEntry (S, I);
1027 /* Check for the sequence */
1028 if (E->OPC == OP65_LDA &&
1029 CS_GetEntries (S, L, I+1, 5) &&
1030 L[0]->OPC == OP65_LDX &&
1031 !CE_HasLabel (L[0]) &&
1032 IsImmCmp16 (S, L+1)) {
1034 if (L[1]->Num == 0 && L[3]->Num == 0) {
1035 /* The value is zero, we may use the simple code version. */
1036 CE_ReplaceOPC (L[0], OP65_ORA);
1037 CS_DelEntries (S, I+2, 3);
1039 /* Move the lda instruction after the first branch. This will
1040 * improve speed, since the load is delayed after the first
1043 CS_MoveEntry (S, I, I+4);
1045 /* We will replace the ldx/cpx by lda/cmp */
1046 CE_ReplaceOPC (L[0], OP65_LDA);
1047 CE_ReplaceOPC (L[1], OP65_CMP);
1049 /* Beware: If the first LDA instruction had a label, we have
1050 * to move this label to the top of the sequence again.
1052 if (CE_HasLabel (E)) {
1053 CS_MoveLabels (S, E, L[0]);
1066 /* Return the number of changes made */
1072 static unsigned OptCmp4 (CodeSeg* S)
1073 /* Optimize compares of local variables:
1086 unsigned Changes = 0;
1088 /* Walk over the entries */
1090 while (I < CS_GetEntryCount (S)) {
1094 /* Check for the sequence */
1095 if (IsLocalLoad16 (S, I, L, 9) && IsImmCmp16 (S, L+5)) {
1097 if (L[5]->Num == 0 && L[7]->Num == 0) {
1099 /* The value is zero, we may use the simple code version:
1106 CE_ReplaceOPC (L[4], OP65_ORA);
1107 CS_DelEntries (S, I+5, 3); /* cpx/bne/cmp */
1108 CS_DelEntry (S, I+2); /* tax */
1112 /* Change the code to just use the A register. Move the load
1113 * of the low byte after the first branch if possible:
1124 CS_DelEntry (S, I+2); /* tax */
1125 CE_ReplaceOPC (L[5], OP65_CMP); /* cpx -> cmp */
1126 CS_MoveEntry (S, I+4, I+2); /* cmp */
1127 CS_MoveEntry (S, I+5, I+3); /* bne */
1139 /* Return the number of changes made */
1145 static unsigned OptCmp5 (CodeSeg* S)
1146 /* Search for calls to compare subroutines followed by a conditional branch
1147 * and replace them by cheaper versions, since the branch means that the
1148 * boolean value returned by these routines is not needed (we may also check
1149 * that explicitly, but for the current code generator it is always true).
1152 unsigned Changes = 0;
1154 /* Walk over the entries */
1156 while (I < CS_GetEntryCount (S)) {
1161 /* Get next entry */
1162 CodeEntry* E = CS_GetEntry (S, I);
1164 /* Check for the sequence */
1165 if (E->OPC == OP65_JSR &&
1166 (Cond = FindTosCmpCond (E->Arg)) != CMP_INV &&
1167 (N = CS_GetNextEntry (S, I)) != 0 &&
1168 (N->Info & OF_ZBRA) != 0 &&
1171 /* The tos... functions will return a boolean value in a/x and
1172 * the Z flag says if this value is zero or not. We will call
1173 * a cheaper subroutine instead, one that does not return a
1174 * boolean value but only valid flags. Note: jeq jumps if
1175 * the condition is not met, jne jumps if the condition is met.
1176 * Invert the code if we jump on condition not met.
1178 if (GetBranchCond (N->OPC) == BC_EQ) {
1179 /* Jumps if condition false, invert condition */
1180 Cond = CmpInvertTab [Cond];
1183 /* Replace the subroutine call. */
1184 E = NewCodeEntry (OP65_JSR, AM65_ABS, "tosicmp", 0, E->LI);
1185 CS_InsertEntry (S, E, I+1);
1188 /* Replace the conditional branch */
1189 ReplaceCmp (S, I+1, Cond);
1191 /* Remember, we had changes */
1201 /* Return the number of changes made */
1207 static unsigned OptCmp6 (CodeSeg* S)
1208 /* Search for a sequence ldx/txa/branch and remove the txa if A is not
1212 unsigned Changes = 0;
1214 /* Walk over the entries */
1216 while (I < CS_GetEntryCount (S)) {
1220 /* Get next entry */
1221 CodeEntry* E = CS_GetEntry (S, I);
1223 /* Check for the sequence */
1224 if ((E->OPC == OP65_LDX) &&
1225 CS_GetEntries (S, L, I+1, 2) &&
1226 L[0]->OPC == OP65_TXA &&
1227 !CE_HasLabel (L[0]) &&
1228 (L[1]->Info & OF_FBRA) != 0 &&
1229 !CE_HasLabel (L[1]) &&
1230 !RegAUsed (S, I+3)) {
1232 /* Remove the txa */
1233 CS_DelEntry (S, I+1);
1235 /* Remember, we had changes */
1245 /* Return the number of changes made */
1251 /*****************************************************************************/
1252 /* Optimize tests */
1253 /*****************************************************************************/
1257 static unsigned OptTest1 (CodeSeg* S)
1264 * if X is zero, the sequence may be changed to
1269 * which may be optimized further by another step.
1271 * If A is zero, the sequence may be changed to
1278 unsigned Changes = 0;
1281 /* Generate register info for this step */
1284 /* Walk over the entries */
1286 while (I < CS_GetEntryCount (S)) {
1290 /* Get next entry */
1291 L[0] = CS_GetEntry (S, I);
1293 /* Check if it's the sequence we're searching for */
1294 if (L[0]->OPC == OP65_STX &&
1295 CS_GetEntries (S, L+1, I+1, 2) &&
1296 !CE_HasLabel (L[1]) &&
1297 L[1]->OPC == OP65_ORA &&
1298 strcmp (L[0]->Arg, L[1]->Arg) == 0 &&
1299 !CE_HasLabel (L[2]) &&
1300 (L[2]->Info & OF_ZBRA) != 0) {
1302 /* Check if X is zero */
1303 if (L[0]->RI->In.RegX == 0) {
1305 /* Insert the compare */
1306 CodeEntry* N = NewCodeEntry (OP65_CMP, AM65_IMM, "$00", 0, L[0]->LI);
1307 CS_InsertEntry (S, N, I+2);
1309 /* Remove the two other insns */
1310 CS_DelEntry (S, I+1);
1313 /* We had changes */
1316 /* Check if A is zero */
1317 } else if (L[1]->RI->In.RegA == 0) {
1319 /* Insert the txa */
1320 CodeEntry* N = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, L[1]->LI);
1321 CS_InsertEntry (S, N, I+2);
1323 /* Remove the two other insns */
1324 CS_DelEntry (S, I+1);
1327 /* We had changes */
1337 /* Free register info */
1340 /* Return the number of changes made */
1346 /*****************************************************************************/
1347 /* nega optimizations */
1348 /*****************************************************************************/
1352 static unsigned OptNegA1 (CodeSeg* S)
1359 * Remove the ldx if the lda does not use it.
1362 unsigned Changes = 0;
1364 /* Walk over the entries */
1366 while (I < CS_GetEntryCount (S)) {
1370 /* Get next entry */
1371 CodeEntry* E = CS_GetEntry (S, I);
1373 /* Check for a ldx */
1374 if (E->OPC == OP65_LDX &&
1375 E->AM == AM65_IMM &&
1376 (E->Flags & CEF_NUMARG) != 0 &&
1378 CS_GetEntries (S, L, I+1, 2) &&
1379 L[0]->OPC == OP65_LDA &&
1380 (L[0]->Use & REG_X) == 0 &&
1381 !CE_HasLabel (L[0]) &&
1382 L[1]->OPC == OP65_JSR &&
1383 strcmp (L[1]->Arg, "bnega") == 0 &&
1384 !CE_HasLabel (L[1])) {
1386 /* Remove the ldx instruction */
1389 /* Remember, we had changes */
1399 /* Return the number of changes made */
1405 static unsigned OptNegA2 (CodeSeg* S)
1412 * Adjust the conditional branch and remove the call to the subroutine.
1415 unsigned Changes = 0;
1417 /* Walk over the entries */
1419 while (I < CS_GetEntryCount (S)) {
1423 /* Get next entry */
1424 CodeEntry* E = CS_GetEntry (S, I);
1426 /* Check for the sequence */
1427 if ((E->OPC == OP65_ADC ||
1428 E->OPC == OP65_AND ||
1429 E->OPC == OP65_DEA ||
1430 E->OPC == OP65_EOR ||
1431 E->OPC == OP65_INA ||
1432 E->OPC == OP65_LDA ||
1433 E->OPC == OP65_ORA ||
1434 E->OPC == OP65_PLA ||
1435 E->OPC == OP65_SBC ||
1436 E->OPC == OP65_TXA ||
1437 E->OPC == OP65_TYA) &&
1438 CS_GetEntries (S, L, I+1, 2) &&
1439 L[0]->OPC == OP65_JSR &&
1440 strcmp (L[0]->Arg, "bnega") == 0 &&
1441 !CE_HasLabel (L[0]) &&
1442 (L[1]->Info & OF_ZBRA) != 0 &&
1443 !CE_HasLabel (L[1])) {
1445 /* Invert the branch */
1446 CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
1448 /* Delete the subroutine call */
1449 CS_DelEntry (S, I+1);
1451 /* Remember, we had changes */
1461 /* Return the number of changes made */
1467 /*****************************************************************************/
1468 /* negax optimizations */
1469 /*****************************************************************************/
1473 static unsigned OptNegAX1 (CodeSeg* S)
1474 /* On a call to bnegax, if X is zero, the result depends only on the value in
1475 * A, so change the call to a call to bnega. This will get further optimized
1476 * later if possible.
1479 unsigned Changes = 0;
1482 /* Generate register info for this step */
1485 /* Walk over the entries */
1487 while (I < CS_GetEntryCount (S)) {
1489 /* Get next entry */
1490 CodeEntry* E = CS_GetEntry (S, I);
1492 /* Check if this is a call to bnegax, and if X is known and zero */
1493 if (E->OPC == OP65_JSR &&
1494 E->RI->In.RegX == 0 &&
1495 strcmp (E->Arg, "bnegax") == 0) {
1497 /* We're cheating somewhat here ... */
1501 /* We had changes */
1510 /* Free register info */
1513 /* Return the number of changes made */
1519 static unsigned OptNegAX2 (CodeSeg* S)
1520 /* Search for the sequence:
1537 unsigned Changes = 0;
1539 /* Walk over the entries */
1541 while (I < CS_GetEntryCount (S)) {
1545 /* Get next entry */
1546 CodeEntry* E = CS_GetEntry (S, I);
1548 /* Check for the sequence */
1549 if (E->OPC == OP65_LDA &&
1550 E->AM == AM65_ZP_INDY &&
1551 CS_GetEntries (S, L, I+1, 5) &&
1552 L[0]->OPC == OP65_TAX &&
1553 L[1]->OPC == OP65_DEY &&
1554 L[2]->OPC == OP65_LDA &&
1555 L[2]->AM == AM65_ZP_INDY &&
1556 strcmp (L[2]->Arg, E->Arg) == 0 &&
1557 !CE_HasLabel (L[2]) &&
1558 L[3]->OPC == OP65_JSR &&
1559 strcmp (L[3]->Arg, "bnegax") == 0 &&
1560 !CE_HasLabel (L[3]) &&
1561 (L[4]->Info & OF_ZBRA) != 0 &&
1562 !CE_HasLabel (L[4])) {
1565 CE_ReplaceOPC (L[2], OP65_ORA);
1567 /* Invert the branch */
1568 CE_ReplaceOPC (L[4], GetInverseBranch (L[4]->OPC));
1570 /* Delete the entries no longer needed. Beware: Deleting entries
1571 * will change the indices.
1573 CS_DelEntry (S, I+4); /* jsr bnegax */
1574 CS_DelEntry (S, I+1); /* tax */
1576 /* Remember, we had changes */
1586 /* Return the number of changes made */
1592 static unsigned OptNegAX3 (CodeSeg* S)
1593 /* Search for the sequence:
1607 unsigned Changes = 0;
1609 /* Walk over the entries */
1611 while (I < CS_GetEntryCount (S)) {
1615 /* Get next entry */
1616 CodeEntry* E = CS_GetEntry (S, I);
1618 /* Check for the sequence */
1619 if (E->OPC == OP65_LDA &&
1620 CS_GetEntries (S, L, I+1, 3) &&
1621 L[0]->OPC == OP65_LDX &&
1622 !CE_HasLabel (L[0]) &&
1623 L[1]->OPC == OP65_JSR &&
1624 strcmp (L[1]->Arg, "bnegax") == 0 &&
1625 !CE_HasLabel (L[1]) &&
1626 (L[2]->Info & OF_ZBRA) != 0 &&
1627 !CE_HasLabel (L[2])) {
1630 CE_ReplaceOPC (L[0], OP65_ORA);
1632 /* Invert the branch */
1633 CE_ReplaceOPC (L[2], GetInverseBranch (L[2]->OPC));
1635 /* Delete the subroutine call */
1636 CS_DelEntry (S, I+2);
1638 /* Remember, we had changes */
1648 /* Return the number of changes made */
1654 static unsigned OptNegAX4 (CodeSeg* S)
1655 /* Search for the sequence:
1661 * and replace it by:
1668 unsigned Changes = 0;
1670 /* Walk over the entries */
1672 while (I < CS_GetEntryCount (S)) {
1676 /* Get next entry */
1677 CodeEntry* E = CS_GetEntry (S, I);
1679 /* Check for the sequence */
1680 if (E->OPC == OP65_JSR &&
1681 CS_GetEntries (S, L, I+1, 2) &&
1682 L[0]->OPC == OP65_JSR &&
1683 strncmp (L[0]->Arg,"bnega",5) == 0 &&
1684 !CE_HasLabel (L[0]) &&
1685 (L[1]->Info & OF_ZBRA) != 0 &&
1686 !CE_HasLabel (L[1])) {
1690 /* Check if we're calling bnega or bnegax */
1691 int ByteSized = (strcmp (L[0]->Arg, "bnega") == 0);
1693 /* Insert apropriate test code */
1696 X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI);
1697 CS_InsertEntry (S, X, I+2);
1700 X = NewCodeEntry (OP65_STX, AM65_ZP, "tmp1", 0, L[0]->LI);
1701 CS_InsertEntry (S, X, I+2);
1702 X = NewCodeEntry (OP65_ORA, AM65_ZP, "tmp1", 0, L[0]->LI);
1703 CS_InsertEntry (S, X, I+3);
1706 /* Delete the subroutine call */
1707 CS_DelEntry (S, I+1);
1709 /* Invert the branch */
1710 CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
1712 /* Remember, we had changes */
1722 /* Return the number of changes made */
1728 /*****************************************************************************/
1729 /* Optimize stores through pointers */
1730 /*****************************************************************************/
1734 static unsigned OptPtrStore1Sub (CodeSeg* S, unsigned I, CodeEntry** const L)
1735 /* Check if this is one of the allowed suboperation for OptPtrStore1 */
1737 /* Check for a label attached to the entry */
1738 if (CE_HasLabel (L[0])) {
1742 /* Check for single insn sub ops */
1743 if (L[0]->OPC == OP65_AND ||
1744 L[0]->OPC == OP65_EOR ||
1745 L[0]->OPC == OP65_ORA ||
1746 (L[0]->OPC == OP65_JSR && strncmp (L[0]->Arg, "shlax", 5) == 0) ||
1747 (L[0]->OPC == OP65_JSR && strncmp (L[0]->Arg, "shrax", 5) == 0)) {
1752 } else if (L[0]->OPC == OP65_CLC &&
1753 (L[1] = CS_GetNextEntry (S, I)) != 0 &&
1754 L[1]->OPC == OP65_ADC &&
1755 !CE_HasLabel (L[1])) {
1757 } else if (L[0]->OPC == OP65_SEC &&
1758 (L[1] = CS_GetNextEntry (S, I)) != 0 &&
1759 L[1]->OPC == OP65_SBC &&
1760 !CE_HasLabel (L[1])) {
1772 static unsigned OptPtrStore1 (CodeSeg* S)
1773 /* Search for the sequence:
1782 * and replace it by:
1794 unsigned Changes = 0;
1796 /* Walk over the entries */
1798 while (I < CS_GetEntryCount (S)) {
1803 /* Get next entry */
1804 L[0] = CS_GetEntry (S, I);
1806 /* Check for the sequence */
1807 if (L[0]->OPC == OP65_JSR &&
1808 strcmp (L[0]->Arg, "pushax") == 0 &&
1809 CS_GetEntries (S, L+1, I+1, 3) &&
1810 L[1]->OPC == OP65_LDY &&
1811 CE_KnownImm (L[1]) &&
1812 !CE_HasLabel (L[1]) &&
1813 L[2]->OPC == OP65_JSR &&
1814 strcmp (L[2]->Arg, "ldauidx") == 0 &&
1815 !CE_HasLabel (L[2]) &&
1816 (K = OptPtrStore1Sub (S, I+3, L+3)) > 0 &&
1817 CS_GetEntries (S, L+3+K, I+3+K, 2) &&
1818 L[3+K]->OPC == OP65_LDY &&
1819 CE_KnownImm (L[3+K]) &&
1820 !CE_HasLabel (L[3+K]) &&
1821 L[4+K]->OPC == OP65_JSR &&
1822 strcmp (L[4+K]->Arg, "staspidx") == 0 &&
1823 !CE_HasLabel (L[4+K])) {
1827 /* Create and insert the stores */
1828 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
1829 CS_InsertEntry (S, X, I+1);
1831 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
1832 CS_InsertEntry (S, X, I+2);
1834 /* Delete the call to pushax */
1837 /* Delete the call to ldauidx */
1838 CS_DelEntry (S, I+3);
1840 /* Insert the load from ptr1 */
1841 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[3]->LI);
1842 CS_InsertEntry (S, X, I+3);
1843 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[2]->LI);
1844 CS_InsertEntry (S, X, I+4);
1846 /* Insert the store through ptr1 */
1847 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "ptr1", 0, L[3]->LI);
1848 CS_InsertEntry (S, X, I+6+K);
1850 /* Delete the call to staspidx */
1851 CS_DelEntry (S, I+7+K);
1853 /* Remember, we had changes */
1863 /* Return the number of changes made */
1869 static unsigned OptPtrStore2 (CodeSeg* S)
1870 /* Search for the sequence:
1877 * and replace it by:
1886 unsigned Changes = 0;
1888 /* Walk over the entries */
1890 while (I < CS_GetEntryCount (S)) {
1894 /* Get next entry */
1895 L[0] = CS_GetEntry (S, I);
1897 /* Check for the sequence */
1898 if (L[0]->OPC == OP65_JSR &&
1899 strcmp (L[0]->Arg, "pushax") == 0 &&
1900 CS_GetEntries (S, L+1, I+1, 3) &&
1901 L[1]->OPC == OP65_LDA &&
1902 !CE_HasLabel (L[1]) &&
1903 L[2]->OPC == OP65_LDY &&
1904 !CE_HasLabel (L[2]) &&
1905 L[3]->OPC == OP65_JSR &&
1906 strcmp (L[3]->Arg, "staspidx") == 0 &&
1907 !CE_HasLabel (L[3])) {
1911 /* Create and insert the stores */
1912 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
1913 CS_InsertEntry (S, X, I+1);
1915 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
1916 CS_InsertEntry (S, X, I+2);
1918 /* Delete the call to pushax */
1921 /* Insert the store through ptr1 */
1922 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "ptr1", 0, L[3]->LI);
1923 CS_InsertEntry (S, X, I+4);
1925 /* Delete the call to staspidx */
1926 CS_DelEntry (S, I+5);
1928 /* Remember, we had changes */
1938 /* Return the number of changes made */
1944 /*****************************************************************************/
1945 /* Optimize loads through pointers */
1946 /*****************************************************************************/
1950 static unsigned OptPtrLoad1 (CodeSeg* S)
1951 /* Search for the sequence:
1955 * lda (sp),y # May be any destination
1959 * and replace it by:
1970 unsigned Changes = 0;
1972 /* Walk over the entries */
1974 while (I < CS_GetEntryCount (S)) {
1978 /* Get next entry */
1979 L[0] = CS_GetEntry (S, I);
1981 /* Check for the sequence */
1982 if (L[0]->OPC == OP65_TAX &&
1983 CS_GetEntries (S, L+1, I+1, 4) &&
1984 L[1]->OPC == OP65_DEY &&
1985 !CE_HasLabel (L[1]) &&
1986 L[2]->OPC == OP65_LDA &&
1987 !CE_HasLabel (L[2]) &&
1988 L[3]->OPC == OP65_LDY &&
1989 !CE_HasLabel (L[3]) &&
1990 L[4]->OPC == OP65_JSR &&
1991 strcmp (L[4]->Arg, "ldauidx") == 0 &&
1992 !CE_HasLabel (L[4])) {
1996 /* Store the high byte and remove the TAX instead */
1997 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1+1", 0, L[0]->LI);
1998 CS_InsertEntry (S, X, I+1);
2001 /* Store the low byte */
2002 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[2]->LI);
2003 CS_InsertEntry (S, X, I+3);
2005 /* Delete the call to ldauidx */
2006 CS_DelEntry (S, I+5);
2008 /* Load high and low byte */
2009 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[3]->LI);
2010 CS_InsertEntry (S, X, I+5);
2011 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[3]->LI);
2012 CS_InsertEntry (S, X, I+6);
2014 /* Remember, we had changes */
2024 /* Return the number of changes made */
2030 static unsigned OptPtrLoad2 (CodeSeg* S)
2031 /* Search for the sequence:
2042 * and replace it by:
2054 unsigned Changes = 0;
2056 /* Walk over the entries */
2058 while (I < CS_GetEntryCount (S)) {
2062 /* Get next entry */
2063 L[0] = CS_GetEntry (S, I);
2065 /* Check for the sequence */
2066 if (L[0]->OPC == OP65_ADC &&
2067 CS_GetEntries (S, L+1, I+1, 7) &&
2068 L[1]->OPC == OP65_TAY &&
2069 !CE_HasLabel (L[1]) &&
2070 L[2]->OPC == OP65_TXA &&
2071 !CE_HasLabel (L[2]) &&
2072 L[3]->OPC == OP65_ADC &&
2073 !CE_HasLabel (L[3]) &&
2074 L[4]->OPC == OP65_TAX &&
2075 !CE_HasLabel (L[4]) &&
2076 L[5]->OPC == OP65_TYA &&
2077 !CE_HasLabel (L[5]) &&
2078 L[6]->OPC == OP65_LDY &&
2079 !CE_HasLabel (L[6]) &&
2080 L[7]->OPC == OP65_JSR &&
2081 strcmp (L[7]->Arg, "ldauidx") == 0 &&
2082 !CE_HasLabel (L[7])) {
2086 /* Store the low byte and remove the TAY instead */
2087 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
2088 CS_InsertEntry (S, X, I+1);
2089 CS_DelEntry (S, I+2);
2091 /* Store the high byte */
2092 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1+1", 0, L[3]->LI);
2093 CS_InsertEntry (S, X, I+4);
2095 /* Delete more transfer insns */
2096 CS_DelEntry (S, I+6);
2097 CS_DelEntry (S, I+5);
2099 /* Delete the call to ldauidx */
2100 CS_DelEntry (S, I+6);
2102 /* Load high and low byte */
2103 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[6]->LI);
2104 CS_InsertEntry (S, X, I+6);
2105 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[6]->LI);
2106 CS_InsertEntry (S, X, I+7);
2108 /* Remember, we had changes */
2118 /* Return the number of changes made */
2124 static unsigned OptPtrLoad3 (CodeSeg* S)
2125 /* Search for the sequence:
2137 * and replace it by:
2150 unsigned Changes = 0;
2152 /* Walk over the entries */
2154 while (I < CS_GetEntryCount (S)) {
2158 /* Get next entry */
2159 L[0] = CS_GetEntry (S, I);
2161 /* Check for the sequence */
2162 if (L[0]->OPC == OP65_ADC &&
2163 CS_GetEntries (S, L+1, I+1, 8) &&
2164 L[1]->OPC == OP65_PHA &&
2165 !CE_HasLabel (L[1]) &&
2166 L[2]->OPC == OP65_TXA &&
2167 !CE_HasLabel (L[2]) &&
2168 L[3]->OPC == OP65_INY &&
2169 !CE_HasLabel (L[3]) &&
2170 L[4]->OPC == OP65_ADC &&
2171 !CE_HasLabel (L[4]) &&
2172 L[5]->OPC == OP65_TAX &&
2173 !CE_HasLabel (L[5]) &&
2174 L[6]->OPC == OP65_PLA &&
2175 !CE_HasLabel (L[6]) &&
2176 L[7]->OPC == OP65_LDY &&
2177 !CE_HasLabel (L[7]) &&
2178 L[8]->OPC == OP65_JSR &&
2179 strcmp (L[8]->Arg, "ldauidx") == 0 &&
2180 !CE_HasLabel (L[8])) {
2184 /* Store the low byte and remove the PHA instead */
2185 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
2186 CS_InsertEntry (S, X, I+1);
2187 CS_DelEntry (S, I+2);
2189 /* Store the high byte */
2190 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1+1", 0, L[4]->LI);
2191 CS_InsertEntry (S, X, I+5);
2193 /* Delete more transfer and PLA insns */
2194 CS_DelEntry (S, I+7);
2195 CS_DelEntry (S, I+6);
2197 /* Delete the call to ldauidx */
2198 CS_DelEntry (S, I+7);
2200 /* Load high and low byte */
2201 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[6]->LI);
2202 CS_InsertEntry (S, X, I+7);
2203 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[6]->LI);
2204 CS_InsertEntry (S, X, I+8);
2206 /* Remember, we had changes */
2216 /* Return the number of changes made */
2222 static unsigned OptPtrLoad4 (CodeSeg* S)
2223 /* Search for the sequence:
2234 * and replace it by:
2241 unsigned Changes = 0;
2243 /* Walk over the entries */
2245 while (I < CS_GetEntryCount (S)) {
2250 /* Get next entry */
2251 L[0] = CS_GetEntry (S, I);
2253 /* Check for the sequence */
2254 if (L[0]->OPC == OP65_LDA &&
2255 L[0]->AM == AM65_IMM &&
2256 CS_GetEntries (S, L+1, I+1, 7) &&
2257 L[1]->OPC == OP65_LDX &&
2258 L[1]->AM == AM65_IMM &&
2259 !CE_HasLabel (L[1]) &&
2260 L[2]->OPC == OP65_CLC &&
2261 !CE_HasLabel (L[2]) &&
2262 L[3]->OPC == OP65_ADC &&
2263 (L[3]->AM == AM65_ABS || L[3]->AM == AM65_ZP) &&
2264 !CE_HasLabel (L[3]) &&
2265 (L[4]->OPC == OP65_BCC || L[4]->OPC == OP65_JCC) &&
2266 L[4]->JumpTo != 0 &&
2267 L[4]->JumpTo->Owner == L[6] &&
2268 !CE_HasLabel (L[4]) &&
2269 L[5]->OPC == OP65_INX &&
2270 !CE_HasLabel (L[5]) &&
2271 L[6]->OPC == OP65_LDY &&
2272 CE_KnownImm (L[6]) &&
2274 L[7]->OPC == OP65_JSR &&
2275 strcmp (L[7]->Arg, "ldauidx") == 0 &&
2276 !CE_HasLabel (L[7]) &&
2277 /* Check the label last because this is quite costly */
2278 (Len = strlen (L[0]->Arg)) > 3 &&
2279 L[0]->Arg[0] == '<' &&
2280 L[0]->Arg[1] == '(' &&
2281 strlen (L[1]->Arg) == Len &&
2282 L[1]->Arg[0] == '>' &&
2283 memcmp (L[0]->Arg+1, L[1]->Arg+1, Len-1) == 0) {
2288 /* We will create all the new stuff behind the current one so
2289 * we keep the line references.
2291 X = NewCodeEntry (OP65_LDY, L[3]->AM, L[3]->Arg, 0, L[0]->LI);
2292 CS_InsertEntry (S, X, I+8);
2294 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[0]->LI);
2295 CS_InsertEntry (S, X, I+9);
2297 Label = memcpy (xmalloc (Len-2), L[0]->Arg+2, Len-3);
2298 Label[Len-3] = '\0';
2299 X = NewCodeEntry (OP65_LDA, AM65_ABSY, Label, 0, L[0]->LI);
2300 CS_InsertEntry (S, X, I+10);
2303 /* Remove the old code */
2304 CS_DelEntries (S, I, 8);
2306 /* Remember, we had changes */
2316 /* Return the number of changes made */
2322 static unsigned OptPtrLoad5 (CodeSeg* S)
2323 /* Search for the sequence:
2335 * and replace it by:
2344 unsigned Changes = 0;
2346 /* Walk over the entries */
2348 while (I < CS_GetEntryCount (S)) {
2353 /* Get next entry */
2354 L[0] = CS_GetEntry (S, I);
2356 /* Check for the sequence */
2357 if (L[0]->OPC == OP65_LDA &&
2358 L[0]->AM == AM65_IMM &&
2359 CS_GetEntries (S, L+1, I+1, 8) &&
2360 L[1]->OPC == OP65_LDX &&
2361 L[1]->AM == AM65_IMM &&
2362 !CE_HasLabel (L[1]) &&
2363 L[2]->OPC == OP65_LDY &&
2364 CE_KnownImm (L[2]) &&
2365 !CE_HasLabel (L[2]) &&
2366 L[3]->OPC == OP65_CLC &&
2367 !CE_HasLabel (L[3]) &&
2368 L[4]->OPC == OP65_ADC &&
2369 L[4]->AM == AM65_ZP_INDY &&
2370 !CE_HasLabel (L[4]) &&
2371 (L[5]->OPC == OP65_BCC || L[5]->OPC == OP65_JCC) &&
2372 L[5]->JumpTo != 0 &&
2373 L[5]->JumpTo->Owner == L[7] &&
2374 !CE_HasLabel (L[5]) &&
2375 L[6]->OPC == OP65_INX &&
2376 !CE_HasLabel (L[6]) &&
2377 L[7]->OPC == OP65_LDY &&
2378 CE_KnownImm (L[7]) &&
2380 L[8]->OPC == OP65_JSR &&
2381 strcmp (L[8]->Arg, "ldauidx") == 0 &&
2382 !CE_HasLabel (L[8]) &&
2383 /* Check the label last because this is quite costly */
2384 (Len = strlen (L[0]->Arg)) > 3 &&
2385 L[0]->Arg[0] == '<' &&
2386 L[0]->Arg[1] == '(' &&
2387 strlen (L[1]->Arg) == Len &&
2388 L[1]->Arg[0] == '>' &&
2389 memcmp (L[0]->Arg+1, L[1]->Arg+1, Len-1) == 0) {
2395 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[0]->LI);
2396 CS_InsertEntry (S, X, I+3);
2399 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, L[4]->Arg, 0, L[0]->LI);
2400 CS_InsertEntry (S, X, I+4);
2403 X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, L[0]->LI);
2404 CS_InsertEntry (S, X, I+5);
2407 Label = memcpy (xmalloc (Len-2), L[0]->Arg+2, Len-3);
2408 Label[Len-3] = '\0';
2409 X = NewCodeEntry (OP65_LDA, AM65_ABSY, Label, 0, L[0]->LI);
2410 CS_InsertEntry (S, X, I+6);
2413 /* Remove the old code */
2414 CS_DelEntries (S, I, 2);
2415 CS_DelEntries (S, I+5, 6);
2417 /* Remember, we had changes */
2427 /* Return the number of changes made */
2433 static unsigned OptPtrLoad6 (CodeSeg* S)
2434 /* Search for the sequence
2439 * and replace it by:
2447 * This step must be execute *after* OptPtrLoad1!
2450 unsigned Changes = 0;
2452 /* Walk over the entries */
2454 while (I < CS_GetEntryCount (S)) {
2458 /* Get next entry */
2459 L[0] = CS_GetEntry (S, I);
2461 /* Check for the sequence */
2462 if (L[0]->OPC == OP65_LDY &&
2463 CS_GetEntries (S, L+1, I+1, 1) &&
2464 L[1]->OPC == OP65_JSR &&
2465 strcmp (L[1]->Arg, "ldauidx") == 0 &&
2466 !CE_HasLabel (L[1])) {
2470 /* Store the high byte */
2471 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
2472 CS_InsertEntry (S, X, I+1);
2474 /* Store the low byte */
2475 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
2476 CS_InsertEntry (S, X, I+2);
2478 /* Delete the call to ldauidx */
2479 CS_DelEntry (S, I+3);
2481 /* Load the high and low byte */
2482 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[0]->LI);
2483 CS_InsertEntry (S, X, I+3);
2484 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[0]->LI);
2485 CS_InsertEntry (S, X, I+4);
2487 /* Remember, we had changes */
2497 /* Return the number of changes made */
2503 /*****************************************************************************/
2505 /*****************************************************************************/
2509 /* Types of optimization steps */
2511 optPre, /* Repeated once */
2512 optPreMain, /* Repeated more than once */
2514 optPostMain, /* dito */
2515 optPost /* Repeated once */
2518 /* Table with all the optimization functions */
2519 typedef struct OptFunc OptFunc;
2521 unsigned (*Func) (CodeSeg*); /* Optimizer function */
2522 const char* Name; /* Name of optimizer step */
2523 unsigned char Type; /* Type of this step */
2524 char Disabled; /* True if pass disabled */
2527 /* Macro that builds a table entry */
2528 #define OptEntry(func,type) { func, #func, type, 0 }
2530 /* Table with optimizer steps */
2531 static OptFunc OptFuncs [] = {
2532 /* Optimizes stores through pointers */
2533 OptEntry (OptPtrStore1, optPre),
2534 OptEntry (OptPtrStore2, optPre),
2535 /* Optimize loads through pointers */
2536 OptEntry (OptPtrLoad1, optPre),
2537 OptEntry (OptPtrLoad2, optPre),
2538 OptEntry (OptPtrLoad3, optPre),
2539 OptEntry (OptPtrLoad4, optPre),
2540 OptEntry (OptPtrLoad5, optPre),
2541 OptEntry (OptPtrLoad6, optMain),
2542 /* Optimize calls to nega */
2543 OptEntry (OptNegA1, optMain),
2544 OptEntry (OptNegA2, optMain),
2545 /* Optimize calls to negax */
2546 OptEntry (OptNegAX1, optPre),
2547 OptEntry (OptNegAX2, optPre),
2548 OptEntry (OptNegAX3, optPre),
2549 OptEntry (OptNegAX4, optPre),
2550 /* Optimize subtractions */
2551 OptEntry (OptSub1, optMain),
2552 OptEntry (OptSub2, optMain),
2553 /* Optimize additions */
2554 OptEntry (OptAdd1, optPre),
2555 OptEntry (OptAdd2, optPre),
2556 OptEntry (OptAdd3, optMain),
2557 /* Optimize shifts */
2558 OptEntry (OptShift1, optPre),
2559 /* Optimize jump cascades */
2560 OptEntry (OptJumpCascades, optMain),
2561 /* Remove dead jumps */
2562 OptEntry (OptDeadJumps, optMain),
2563 /* Change jsr/rts to jmp */
2564 OptEntry (OptRTS, optMain),
2565 /* Remove dead code */
2566 OptEntry (OptDeadCode, optMain),
2567 /* Optimize jump targets */
2568 OptEntry (OptJumpTarget, optMain),
2569 /* Optimize conditional branches */
2570 OptEntry (OptCondBranches, optMain),
2571 /* Replace jumps to RTS by RTS */
2572 OptEntry (OptRTSJumps, optMain),
2573 /* Remove calls to the bool transformer subroutines */
2574 OptEntry (OptBoolTransforms, optMain),
2575 /* Optimize compares */
2576 OptEntry (OptCmp1, optMain),
2577 OptEntry (OptCmp2, optMain),
2578 OptEntry (OptCmp3, optMain),
2579 OptEntry (OptCmp4, optMain),
2580 OptEntry (OptCmp5, optMain),
2581 OptEntry (OptCmp6, optMain),
2582 /* Optimize tests */
2583 OptEntry (OptTest1, optMain),
2584 /* Remove unused loads */
2585 OptEntry (OptUnusedLoads, optMain),
2586 OptEntry (OptDuplicateLoads, optMain),
2587 OptEntry (OptStoreLoad, optMain),
2588 OptEntry (OptTransfers, optMain),
2589 /* Optimize branch distance */
2590 OptEntry (OptBranchDist, optPost),
2595 static OptFunc* FindOptStep (const char* Name)
2596 /* Find an optimizer step by name in the table and return a pointer. Print an
2597 * error and call AbEnd if not found.
2602 /* Run all optimization steps */
2603 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2604 if (strcmp (OptFuncs[I].Name, Name) == 0) {
2611 AbEnd ("Optimization step `%s' not found", Name);
2617 void DisableOpt (const char* Name)
2618 /* Disable the optimization with the given name */
2620 if (strcmp (Name, "any") == 0) {
2622 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2623 OptFuncs[I].Disabled = 1;
2626 OptFunc* F = FindOptStep (Name);
2633 void EnableOpt (const char* Name)
2634 /* Enable the optimization with the given name */
2636 if (strcmp (Name, "any") == 0) {
2638 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2639 OptFuncs[I].Disabled = 0;
2642 OptFunc* F = FindOptStep (Name);
2649 void ListOptSteps (FILE* F)
2650 /* List all optimization steps */
2653 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2654 fprintf (F, "%s\n", OptFuncs[I].Name);
2660 static void RepeatOptStep (CodeSeg* S, unsigned char Type, unsigned Max)
2661 /* Repeat the optimizer step of type Type at may Max times */
2665 unsigned OptChanges;
2667 /* Repeat max times of until there are no more changes */
2669 /* Reset the number of changes */
2672 /* Keep the user hapy */
2673 Print (stdout, 1, " Optimizer pass %u:\n", ++Pass);
2675 /* Run all optimization steps */
2676 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2678 /* Get the table entry */
2679 const OptFunc* F = OptFuncs + I;
2681 /* Check if the type matches */
2682 if (F->Type != Type) {
2687 /* Print the name of the following optimizer step */
2688 Print (stdout, 1, " %s:%*s", F->Name, (int) (30-strlen(F->Name)), "");
2690 /* Check if the step is disabled */
2692 Print (stdout, 1, "Disabled\n");
2694 unsigned Changes = F->Func (S);
2695 OptChanges += Changes;
2696 Print (stdout, 1, "%u Changes\n", Changes);
2700 } while (--Max > 0 && OptChanges > 0);
2705 void RunOpt (CodeSeg* S)
2706 /* Run the optimizer */
2709 /* If we shouldn't run the optimizer, bail out */
2714 /* Print the name of the function we are working on */
2716 Print (stdout, 1, "Running optimizer for function `%s'\n", S->Func->Name);
2718 Print (stdout, 1, "Running optimizer for global code segment\n");
2721 /* Repeat all steps until there are no more changes */
2722 RepeatOptStep (S, optPre, 1);
2723 RepeatOptStep (S, optPreMain, 0xFFFF);
2724 RepeatOptStep (S, optMain, 0xFFFF);
2725 RepeatOptStep (S, optPostMain, 0xFFFF);
2726 RepeatOptStep (S, optPost, 1);