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])) {
595 /* Remove the call to pushax */
598 /* Correct the stack offset (needed since pushax was removed) */
599 CE_SetNumArg (L[0], L[0]->Num - 2);
602 X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, L[3]->LI);
603 CS_InsertEntry (S, X, I+1);
605 /* Remove the load */
606 CS_DelEntry (S, I+3); /* lda */
607 CS_DelEntry (S, I+2); /* ldx */
610 X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp", 0, L[3]->LI);
611 CS_InsertEntry (S, X, I+2);
613 /* Generate the branch label and the branch */
614 Label = CS_GenLabel (S, L[4]);
615 X = NewCodeEntry (OP65_BCC, AM65_BRA, Label->Name, Label, L[3]->LI);
616 CS_InsertEntry (S, X, I+3);
618 /* Generate the increment of the high byte */
619 X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, L[3]->LI);
620 CS_InsertEntry (S, X, I+4);
622 /* Delete the call to tosaddax */
623 CS_DelEntry (S, I+5);
625 /* Remember, we had changes */
635 /* Return the number of changes made */
641 static unsigned OptAdd2 (CodeSeg* S)
642 /* Search for the sequence
666 * provided that a/x is not used later.
669 unsigned Changes = 0;
671 /* Walk over the entries */
673 while (I < CS_GetEntryCount (S)) {
678 L[0] = CS_GetEntry (S, I);
680 /* Check for the sequence */
681 if (L[0]->OPC == OP65_LDY &&
682 CE_KnownImm (L[0]) &&
683 CS_GetEntries (S, L+1, I+1, 6) &&
684 L[1]->OPC == OP65_LDA &&
685 L[1]->AM == AM65_ZP_INDY &&
686 !CE_HasLabel (L[1]) &&
687 L[2]->OPC == OP65_TAX &&
688 !CE_HasLabel (L[2]) &&
689 L[3]->OPC == OP65_DEY &&
690 !CE_HasLabel (L[3]) &&
691 L[4]->OPC == OP65_LDA &&
692 L[4]->AM == AM65_ZP_INDY &&
693 !CE_HasLabel (L[4]) &&
694 L[5]->OPC == OP65_LDY &&
695 CE_KnownImm (L[5]) &&
696 !CE_HasLabel (L[5]) &&
697 L[6]->OPC == OP65_JSR &&
698 strcmp (L[6]->Arg, "addeqysp") == 0 &&
699 !CE_HasLabel (L[6]) &&
700 (GetRegInfo (S, I+7) & REG_AX) == 0) {
707 /* Create a replacement for the first LDY */
708 Offs = (int) (L[0]->Num - 1);
709 xsprintf (Buf, sizeof (Buf), "$%02X", Offs);
710 X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[0]->LI);
711 CS_InsertEntry (S, X, I+1);
714 /* Load Y with the low offset of the target variable */
715 X = NewCodeEntry (OP65_LDY, AM65_IMM, L[5]->Arg, 0, L[1]->LI);
716 CS_InsertEntry (S, X, I+2);
719 X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, L[1]->LI);
720 CS_InsertEntry (S, X, I+3);
722 /* Remove the TAX/DEY sequence */
723 CS_DelEntry (S, I+5); /* dey */
724 CS_DelEntry (S, I+4); /* tax */
726 /* Addition of the low byte */
727 X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp", 0, L[4]->LI);
728 CS_InsertEntry (S, X, I+4);
729 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "sp", 0, L[4]->LI);
730 CS_InsertEntry (S, X, I+5);
733 xsprintf (Buf, sizeof (Buf), "$%02X", (Offs+1));
734 X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[4]->LI);
735 CS_InsertEntry (S, X, I+6);
737 /* Addition of the high byte */
738 xsprintf (Buf, sizeof (Buf), "$%02X", (int)(L[5]->Num+1));
739 X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[5]->LI);
740 CS_InsertEntry (S, X, I+8);
741 X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp", 0, L[6]->LI);
742 CS_InsertEntry (S, X, I+9);
743 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "sp", 0, L[6]->LI);
744 CS_InsertEntry (S, X, I+10);
746 /* Delete the remaining stuff */
747 CS_DelEntry (S, I+12);
748 CS_DelEntry (S, I+11);
750 /* Remember, we had changes */
760 /* Return the number of changes made */
766 static unsigned OptAdd3 (CodeSeg* S)
767 /* Search for the sequence
774 * and remove the handling of the high byte if X is not used later.
777 unsigned Changes = 0;
779 /* Walk over the entries */
781 while (I < CS_GetEntryCount (S)) {
786 CodeEntry* E = CS_GetEntry (S, I);
788 /* Check for the sequence */
789 if (E->OPC == OP65_ADC &&
790 CS_GetEntries (S, L, I+1, 3) &&
791 (L[0]->OPC == OP65_BCC || L[0]->OPC == OP65_JCC) &&
793 !CE_HasLabel (L[0]) &&
794 L[1]->OPC == OP65_INX &&
795 !CE_HasLabel (L[1]) &&
796 L[0]->JumpTo->Owner == L[2] &&
797 !RegXUsed (S, I+3)) {
799 /* Remove the bcs/dex */
800 CS_DelEntries (S, I+1, 2);
802 /* Remember, we had changes */
812 /* Return the number of changes made */
818 /*****************************************************************************/
819 /* Optimize shifts */
820 /*****************************************************************************/
824 static unsigned OptShift1 (CodeSeg* S)
825 /* A call to the shlaxN routine may get replaced by one or more asl insns
826 * if the value of X is not used later.
829 unsigned Changes = 0;
831 /* Walk over the entries */
833 while (I < CS_GetEntryCount (S)) {
836 CodeEntry* E = CS_GetEntry (S, I);
838 /* Check for the sequence */
839 if (E->OPC == OP65_JSR &&
840 (strncmp (E->Arg, "shlax", 5) == 0 ||
841 strncmp (E->Arg, "aslax", 5) == 0) &&
842 strlen (E->Arg) == 6 &&
843 IsDigit (E->Arg[5]) &&
844 !RegXUsed (S, I+1)) {
846 /* Insert shift insns */
847 unsigned Count = E->Arg[5] - '0';
849 CodeEntry* X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI);
850 CS_InsertEntry (S, X, I+1);
853 /* Delete the call to shlax */
856 /* Remember, we had changes */
866 /* Return the number of changes made */
872 /*****************************************************************************/
873 /* Optimizations for compares */
874 /*****************************************************************************/
878 static unsigned OptCmp1 (CodeSeg* S)
879 /* Search for the sequence
891 unsigned Changes = 0;
893 /* Walk over the entries */
895 while (I < CS_GetEntryCount (S)) {
900 CodeEntry* E = CS_GetEntry (S, I);
902 /* Check for the sequence */
903 if (E->OPC == OP65_STX &&
904 CS_GetEntries (S, L, I+1, 2) &&
905 L[0]->OPC == OP65_STX &&
906 strcmp (L[0]->Arg, "tmp1") == 0 &&
907 !CE_HasLabel (L[0]) &&
908 L[1]->OPC == OP65_ORA &&
909 strcmp (L[1]->Arg, "tmp1") == 0 &&
910 !CE_HasLabel (L[1])) {
912 /* Remove the remaining instructions */
913 CS_DelEntries (S, I+1, 2);
915 /* Insert the ora instead */
916 CS_InsertEntry (S, NewCodeEntry (OP65_ORA, E->AM, E->Arg, 0, E->LI), I+1);
918 /* Remember, we had changes */
928 /* Return the number of changes made */
934 static unsigned OptCmp2 (CodeSeg* S)
937 * lda/and/ora/eor ...
941 * and remove the cmp.
944 unsigned Changes = 0;
946 /* Walk over the entries */
948 while (I < CS_GetEntryCount (S)) {
953 CodeEntry* E = CS_GetEntry (S, I);
955 /* Check for the sequence */
956 if ((E->OPC == OP65_ADC ||
957 E->OPC == OP65_AND ||
958 E->OPC == OP65_DEA ||
959 E->OPC == OP65_EOR ||
960 E->OPC == OP65_INA ||
961 E->OPC == OP65_LDA ||
962 E->OPC == OP65_ORA ||
963 E->OPC == OP65_PLA ||
964 E->OPC == OP65_SBC ||
965 E->OPC == OP65_TXA ||
966 E->OPC == OP65_TYA) &&
967 CS_GetEntries (S, L, I+1, 2) &&
968 IsCmpToZero (L[0]) &&
969 !CE_HasLabel (L[0]) &&
970 (L[1]->Info & OF_FBRA) != 0 &&
971 !CE_HasLabel (L[1])) {
973 /* Remove the compare */
974 CS_DelEntry (S, I+1);
976 /* Remember, we had changes */
986 /* Return the number of changes made */
992 static unsigned OptCmp3 (CodeSeg* S)
1002 * If a is zero, we may remove the compare. If a and b are both zero, we may
1003 * replace it by the sequence
1009 * L1 may be either the label at the branch instruction, or the target label
1010 * of this instruction.
1013 unsigned Changes = 0;
1015 /* Walk over the entries */
1017 while (I < CS_GetEntryCount (S)) {
1021 /* Get next entry */
1022 CodeEntry* E = CS_GetEntry (S, I);
1024 /* Check for the sequence */
1025 if (E->OPC == OP65_LDA &&
1026 CS_GetEntries (S, L, I+1, 5) &&
1027 L[0]->OPC == OP65_LDX &&
1028 !CE_HasLabel (L[0]) &&
1029 IsImmCmp16 (S, L+1)) {
1031 if (L[1]->Num == 0 && L[3]->Num == 0) {
1032 /* The value is zero, we may use the simple code version. */
1033 CE_ReplaceOPC (L[0], OP65_ORA);
1034 CS_DelEntries (S, I+2, 3);
1036 /* Move the lda instruction after the first branch. This will
1037 * improve speed, since the load is delayed after the first
1040 CS_MoveEntry (S, I, I+4);
1042 /* We will replace the ldx/cpx by lda/cmp */
1043 CE_ReplaceOPC (L[0], OP65_LDA);
1044 CE_ReplaceOPC (L[1], OP65_CMP);
1046 /* Beware: If the first LDA instruction had a label, we have
1047 * to move this label to the top of the sequence again.
1049 if (CE_HasLabel (E)) {
1050 CS_MoveLabels (S, E, L[0]);
1063 /* Return the number of changes made */
1069 static unsigned OptCmp4 (CodeSeg* S)
1070 /* Optimize compares of local variables:
1083 unsigned Changes = 0;
1085 /* Walk over the entries */
1087 while (I < CS_GetEntryCount (S)) {
1091 /* Check for the sequence */
1092 if (IsLocalLoad16 (S, I, L, 9) && IsImmCmp16 (S, L+5)) {
1094 if (L[5]->Num == 0 && L[7]->Num == 0) {
1096 /* The value is zero, we may use the simple code version:
1103 CE_ReplaceOPC (L[4], OP65_ORA);
1104 CS_DelEntries (S, I+5, 3); /* cpx/bne/cmp */
1105 CS_DelEntry (S, I+2); /* tax */
1109 /* Change the code to just use the A register. Move the load
1110 * of the low byte after the first branch if possible:
1121 CS_DelEntry (S, I+2); /* tax */
1122 CE_ReplaceOPC (L[5], OP65_CMP); /* cpx -> cmp */
1123 CS_MoveEntry (S, I+4, I+2); /* cmp */
1124 CS_MoveEntry (S, I+5, I+3); /* bne */
1136 /* Return the number of changes made */
1142 static unsigned OptCmp5 (CodeSeg* S)
1143 /* Search for calls to compare subroutines followed by a conditional branch
1144 * and replace them by cheaper versions, since the branch means that the
1145 * boolean value returned by these routines is not needed (we may also check
1146 * that explicitly, but for the current code generator it is always true).
1149 unsigned Changes = 0;
1151 /* Walk over the entries */
1153 while (I < CS_GetEntryCount (S)) {
1158 /* Get next entry */
1159 CodeEntry* E = CS_GetEntry (S, I);
1161 /* Check for the sequence */
1162 if (E->OPC == OP65_JSR &&
1163 (Cond = FindTosCmpCond (E->Arg)) != CMP_INV &&
1164 (N = CS_GetNextEntry (S, I)) != 0 &&
1165 (N->Info & OF_ZBRA) != 0 &&
1168 /* The tos... functions will return a boolean value in a/x and
1169 * the Z flag says if this value is zero or not. We will call
1170 * a cheaper subroutine instead, one that does not return a
1171 * boolean value but only valid flags. Note: jeq jumps if
1172 * the condition is not met, jne jumps if the condition is met.
1173 * Invert the code if we jump on condition not met.
1175 if (GetBranchCond (N->OPC) == BC_EQ) {
1176 /* Jumps if condition false, invert condition */
1177 Cond = CmpInvertTab [Cond];
1180 /* Replace the subroutine call. */
1181 E = NewCodeEntry (OP65_JSR, AM65_ABS, "tosicmp", 0, E->LI);
1182 CS_InsertEntry (S, E, I+1);
1185 /* Replace the conditional branch */
1186 ReplaceCmp (S, I+1, Cond);
1188 /* Remember, we had changes */
1198 /* Return the number of changes made */
1204 static unsigned OptCmp6 (CodeSeg* S)
1205 /* Search for a sequence ldx/txa/branch and remove the txa if A is not
1209 unsigned Changes = 0;
1211 /* Walk over the entries */
1213 while (I < CS_GetEntryCount (S)) {
1217 /* Get next entry */
1218 CodeEntry* E = CS_GetEntry (S, I);
1220 /* Check for the sequence */
1221 if ((E->OPC == OP65_LDX) &&
1222 CS_GetEntries (S, L, I+1, 2) &&
1223 L[0]->OPC == OP65_TXA &&
1224 !CE_HasLabel (L[0]) &&
1225 (L[1]->Info & OF_FBRA) != 0 &&
1226 !CE_HasLabel (L[1]) &&
1227 !RegAUsed (S, I+3)) {
1229 /* Remove the txa */
1230 CS_DelEntry (S, I+1);
1232 /* Remember, we had changes */
1242 /* Return the number of changes made */
1248 /*****************************************************************************/
1249 /* Optimize tests */
1250 /*****************************************************************************/
1254 static unsigned OptTest1 (CodeSeg* S)
1261 * if X is zero, the sequence may be changed to
1266 * which may be optimized further by another step.
1268 * If A is zero, the sequence may be changed to
1275 unsigned Changes = 0;
1278 /* Generate register info for this step */
1281 /* Walk over the entries */
1283 while (I < CS_GetEntryCount (S)) {
1287 /* Get next entry */
1288 L[0] = CS_GetEntry (S, I);
1290 /* Check if it's the sequence we're searching for */
1291 if (L[0]->OPC == OP65_STX &&
1292 CS_GetEntries (S, L+1, I+1, 2) &&
1293 !CE_HasLabel (L[1]) &&
1294 L[1]->OPC == OP65_ORA &&
1295 strcmp (L[0]->Arg, L[1]->Arg) == 0 &&
1296 !CE_HasLabel (L[2]) &&
1297 (L[2]->Info & OF_ZBRA) != 0) {
1299 /* Check if X is zero */
1300 if (L[0]->RI->In.RegX == 0) {
1302 /* Insert the compare */
1303 CodeEntry* N = NewCodeEntry (OP65_CMP, AM65_IMM, "$00", 0, L[0]->LI);
1304 CS_InsertEntry (S, N, I+2);
1306 /* Remove the two other insns */
1307 CS_DelEntry (S, I+1);
1310 /* We had changes */
1313 /* Check if A is zero */
1314 } else if (L[1]->RI->In.RegA == 0) {
1316 /* Insert the txa */
1317 CodeEntry* N = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, L[1]->LI);
1318 CS_InsertEntry (S, N, I+2);
1320 /* Remove the two other insns */
1321 CS_DelEntry (S, I+1);
1324 /* We had changes */
1334 /* Free register info */
1337 /* Return the number of changes made */
1343 /*****************************************************************************/
1344 /* nega optimizations */
1345 /*****************************************************************************/
1349 static unsigned OptNegA1 (CodeSeg* S)
1356 * Remove the ldx if the lda does not use it.
1359 unsigned Changes = 0;
1361 /* Walk over the entries */
1363 while (I < CS_GetEntryCount (S)) {
1367 /* Get next entry */
1368 CodeEntry* E = CS_GetEntry (S, I);
1370 /* Check for a ldx */
1371 if (E->OPC == OP65_LDX &&
1372 E->AM == AM65_IMM &&
1373 (E->Flags & CEF_NUMARG) != 0 &&
1375 CS_GetEntries (S, L, I+1, 2) &&
1376 L[0]->OPC == OP65_LDA &&
1377 (L[0]->Use & REG_X) == 0 &&
1378 !CE_HasLabel (L[0]) &&
1379 L[1]->OPC == OP65_JSR &&
1380 strcmp (L[1]->Arg, "bnega") == 0 &&
1381 !CE_HasLabel (L[1])) {
1383 /* Remove the ldx instruction */
1386 /* Remember, we had changes */
1396 /* Return the number of changes made */
1402 static unsigned OptNegA2 (CodeSeg* S)
1409 * Adjust the conditional branch and remove the call to the subroutine.
1412 unsigned Changes = 0;
1414 /* Walk over the entries */
1416 while (I < CS_GetEntryCount (S)) {
1420 /* Get next entry */
1421 CodeEntry* E = CS_GetEntry (S, I);
1423 /* Check for the sequence */
1424 if ((E->OPC == OP65_ADC ||
1425 E->OPC == OP65_AND ||
1426 E->OPC == OP65_DEA ||
1427 E->OPC == OP65_EOR ||
1428 E->OPC == OP65_INA ||
1429 E->OPC == OP65_LDA ||
1430 E->OPC == OP65_ORA ||
1431 E->OPC == OP65_PLA ||
1432 E->OPC == OP65_SBC ||
1433 E->OPC == OP65_TXA ||
1434 E->OPC == OP65_TYA) &&
1435 CS_GetEntries (S, L, I+1, 2) &&
1436 L[0]->OPC == OP65_JSR &&
1437 strcmp (L[0]->Arg, "bnega") == 0 &&
1438 !CE_HasLabel (L[0]) &&
1439 (L[1]->Info & OF_ZBRA) != 0 &&
1440 !CE_HasLabel (L[1])) {
1442 /* Invert the branch */
1443 CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
1445 /* Delete the subroutine call */
1446 CS_DelEntry (S, I+1);
1448 /* Remember, we had changes */
1458 /* Return the number of changes made */
1464 /*****************************************************************************/
1465 /* negax optimizations */
1466 /*****************************************************************************/
1470 static unsigned OptNegAX1 (CodeSeg* S)
1471 /* On a call to bnegax, if X is zero, the result depends only on the value in
1472 * A, so change the call to a call to bnega. This will get further optimized
1473 * later if possible.
1476 unsigned Changes = 0;
1479 /* Generate register info for this step */
1482 /* Walk over the entries */
1484 while (I < CS_GetEntryCount (S)) {
1486 /* Get next entry */
1487 CodeEntry* E = CS_GetEntry (S, I);
1489 /* Check if this is a call to bnegax, and if X is known and zero */
1490 if (E->OPC == OP65_JSR &&
1491 E->RI->In.RegX == 0 &&
1492 strcmp (E->Arg, "bnegax") == 0) {
1494 /* We're cheating somewhat here ... */
1498 /* We had changes */
1507 /* Free register info */
1510 /* Return the number of changes made */
1516 static unsigned OptNegAX2 (CodeSeg* S)
1517 /* Search for the sequence:
1534 unsigned Changes = 0;
1536 /* Walk over the entries */
1538 while (I < CS_GetEntryCount (S)) {
1542 /* Get next entry */
1543 CodeEntry* E = CS_GetEntry (S, I);
1545 /* Check for the sequence */
1546 if (E->OPC == OP65_LDA &&
1547 E->AM == AM65_ZP_INDY &&
1548 CS_GetEntries (S, L, I+1, 5) &&
1549 L[0]->OPC == OP65_TAX &&
1550 L[1]->OPC == OP65_DEY &&
1551 L[2]->OPC == OP65_LDA &&
1552 L[2]->AM == AM65_ZP_INDY &&
1553 strcmp (L[2]->Arg, E->Arg) == 0 &&
1554 !CE_HasLabel (L[2]) &&
1555 L[3]->OPC == OP65_JSR &&
1556 strcmp (L[3]->Arg, "bnegax") == 0 &&
1557 !CE_HasLabel (L[3]) &&
1558 (L[4]->Info & OF_ZBRA) != 0 &&
1559 !CE_HasLabel (L[4])) {
1562 CE_ReplaceOPC (L[2], OP65_ORA);
1564 /* Invert the branch */
1565 CE_ReplaceOPC (L[4], GetInverseBranch (L[4]->OPC));
1567 /* Delete the entries no longer needed. Beware: Deleting entries
1568 * will change the indices.
1570 CS_DelEntry (S, I+4); /* jsr bnegax */
1571 CS_DelEntry (S, I+1); /* tax */
1573 /* Remember, we had changes */
1583 /* Return the number of changes made */
1589 static unsigned OptNegAX3 (CodeSeg* S)
1590 /* Search for the sequence:
1604 unsigned Changes = 0;
1606 /* Walk over the entries */
1608 while (I < CS_GetEntryCount (S)) {
1612 /* Get next entry */
1613 CodeEntry* E = CS_GetEntry (S, I);
1615 /* Check for the sequence */
1616 if (E->OPC == OP65_LDA &&
1617 CS_GetEntries (S, L, I+1, 3) &&
1618 L[0]->OPC == OP65_LDX &&
1619 !CE_HasLabel (L[0]) &&
1620 L[1]->OPC == OP65_JSR &&
1621 strcmp (L[1]->Arg, "bnegax") == 0 &&
1622 !CE_HasLabel (L[1]) &&
1623 (L[2]->Info & OF_ZBRA) != 0 &&
1624 !CE_HasLabel (L[2])) {
1627 CE_ReplaceOPC (L[0], OP65_ORA);
1629 /* Invert the branch */
1630 CE_ReplaceOPC (L[2], GetInverseBranch (L[2]->OPC));
1632 /* Delete the subroutine call */
1633 CS_DelEntry (S, I+2);
1635 /* Remember, we had changes */
1645 /* Return the number of changes made */
1651 static unsigned OptNegAX4 (CodeSeg* S)
1652 /* Search for the sequence:
1658 * and replace it by:
1665 unsigned Changes = 0;
1667 /* Walk over the entries */
1669 while (I < CS_GetEntryCount (S)) {
1673 /* Get next entry */
1674 CodeEntry* E = CS_GetEntry (S, I);
1676 /* Check for the sequence */
1677 if (E->OPC == OP65_JSR &&
1678 CS_GetEntries (S, L, I+1, 2) &&
1679 L[0]->OPC == OP65_JSR &&
1680 strncmp (L[0]->Arg,"bnega",5) == 0 &&
1681 !CE_HasLabel (L[0]) &&
1682 (L[1]->Info & OF_ZBRA) != 0 &&
1683 !CE_HasLabel (L[1])) {
1687 /* Check if we're calling bnega or bnegax */
1688 int ByteSized = (strcmp (L[0]->Arg, "bnega") == 0);
1690 /* Insert apropriate test code */
1693 X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI);
1694 CS_InsertEntry (S, X, I+2);
1697 X = NewCodeEntry (OP65_STX, AM65_ZP, "tmp1", 0, L[0]->LI);
1698 CS_InsertEntry (S, X, I+2);
1699 X = NewCodeEntry (OP65_ORA, AM65_ZP, "tmp1", 0, L[0]->LI);
1700 CS_InsertEntry (S, X, I+3);
1703 /* Delete the subroutine call */
1704 CS_DelEntry (S, I+1);
1706 /* Invert the branch */
1707 CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
1709 /* Remember, we had changes */
1719 /* Return the number of changes made */
1725 /*****************************************************************************/
1726 /* Optimize stores through pointers */
1727 /*****************************************************************************/
1731 static unsigned OptPtrStore1Sub (CodeSeg* S, unsigned I, CodeEntry** const L)
1732 /* Check if this is one of the allowed suboperation for OptPtrStore1 */
1734 /* Check for a label attached to the entry */
1735 if (CE_HasLabel (L[0])) {
1739 /* Check for single insn sub ops */
1740 if (L[0]->OPC == OP65_AND ||
1741 L[0]->OPC == OP65_EOR ||
1742 L[0]->OPC == OP65_ORA ||
1743 (L[0]->OPC == OP65_JSR && strncmp (L[0]->Arg, "shlax", 5) == 0) ||
1744 (L[0]->OPC == OP65_JSR && strncmp (L[0]->Arg, "shrax", 5) == 0)) {
1749 } else if (L[0]->OPC == OP65_CLC &&
1750 (L[1] = CS_GetNextEntry (S, I)) != 0 &&
1751 L[1]->OPC == OP65_ADC &&
1752 !CE_HasLabel (L[1])) {
1754 } else if (L[0]->OPC == OP65_SEC &&
1755 (L[1] = CS_GetNextEntry (S, I)) != 0 &&
1756 L[1]->OPC == OP65_SBC &&
1757 !CE_HasLabel (L[1])) {
1769 static unsigned OptPtrStore1 (CodeSeg* S)
1770 /* Search for the sequence:
1779 * and replace it by:
1791 unsigned Changes = 0;
1793 /* Walk over the entries */
1795 while (I < CS_GetEntryCount (S)) {
1800 /* Get next entry */
1801 L[0] = CS_GetEntry (S, I);
1803 /* Check for the sequence */
1804 if (L[0]->OPC == OP65_JSR &&
1805 strcmp (L[0]->Arg, "pushax") == 0 &&
1806 CS_GetEntries (S, L+1, I+1, 3) &&
1807 L[1]->OPC == OP65_LDY &&
1808 CE_KnownImm (L[1]) &&
1809 !CE_HasLabel (L[1]) &&
1810 L[2]->OPC == OP65_JSR &&
1811 strcmp (L[2]->Arg, "ldauidx") == 0 &&
1812 !CE_HasLabel (L[2]) &&
1813 (K = OptPtrStore1Sub (S, I+3, L+3)) > 0 &&
1814 CS_GetEntries (S, L+3+K, I+3+K, 2) &&
1815 L[3+K]->OPC == OP65_LDY &&
1816 CE_KnownImm (L[3+K]) &&
1817 !CE_HasLabel (L[3+K]) &&
1818 L[4+K]->OPC == OP65_JSR &&
1819 strcmp (L[4+K]->Arg, "staspidx") == 0 &&
1820 !CE_HasLabel (L[4+K])) {
1824 /* Create and insert the stores */
1825 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
1826 CS_InsertEntry (S, X, I+1);
1828 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
1829 CS_InsertEntry (S, X, I+2);
1831 /* Delete the call to pushax */
1834 /* Delete the call to ldauidx */
1835 CS_DelEntry (S, I+3);
1837 /* Insert the load from ptr1 */
1838 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[3]->LI);
1839 CS_InsertEntry (S, X, I+3);
1840 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[2]->LI);
1841 CS_InsertEntry (S, X, I+4);
1843 /* Insert the store through ptr1 */
1844 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "ptr1", 0, L[3]->LI);
1845 CS_InsertEntry (S, X, I+6+K);
1847 /* Delete the call to staspidx */
1848 CS_DelEntry (S, I+7+K);
1850 /* Remember, we had changes */
1860 /* Return the number of changes made */
1866 static unsigned OptPtrStore2 (CodeSeg* S)
1867 /* Search for the sequence:
1874 * and replace it by:
1883 unsigned Changes = 0;
1885 /* Walk over the entries */
1887 while (I < CS_GetEntryCount (S)) {
1891 /* Get next entry */
1892 L[0] = CS_GetEntry (S, I);
1894 /* Check for the sequence */
1895 if (L[0]->OPC == OP65_JSR &&
1896 strcmp (L[0]->Arg, "pushax") == 0 &&
1897 CS_GetEntries (S, L+1, I+1, 3) &&
1898 L[1]->OPC == OP65_LDA &&
1899 !CE_HasLabel (L[1]) &&
1900 L[2]->OPC == OP65_LDY &&
1901 !CE_HasLabel (L[2]) &&
1902 L[3]->OPC == OP65_JSR &&
1903 strcmp (L[3]->Arg, "staspidx") == 0 &&
1904 !CE_HasLabel (L[3])) {
1908 /* Create and insert the stores */
1909 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
1910 CS_InsertEntry (S, X, I+1);
1912 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
1913 CS_InsertEntry (S, X, I+2);
1915 /* Delete the call to pushax */
1918 /* Insert the store through ptr1 */
1919 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "ptr1", 0, L[3]->LI);
1920 CS_InsertEntry (S, X, I+4);
1922 /* Delete the call to staspidx */
1923 CS_DelEntry (S, I+5);
1925 /* Remember, we had changes */
1935 /* Return the number of changes made */
1941 /*****************************************************************************/
1942 /* Optimize loads through pointers */
1943 /*****************************************************************************/
1947 static unsigned OptPtrLoad1 (CodeSeg* S)
1948 /* Search for the sequence:
1952 * lda (sp),y # May be any destination
1956 * and replace it by:
1967 unsigned Changes = 0;
1969 /* Walk over the entries */
1971 while (I < CS_GetEntryCount (S)) {
1975 /* Get next entry */
1976 L[0] = CS_GetEntry (S, I);
1978 /* Check for the sequence */
1979 if (L[0]->OPC == OP65_TAX &&
1980 CS_GetEntries (S, L+1, I+1, 4) &&
1981 L[1]->OPC == OP65_DEY &&
1982 !CE_HasLabel (L[1]) &&
1983 L[2]->OPC == OP65_LDA &&
1984 !CE_HasLabel (L[2]) &&
1985 L[3]->OPC == OP65_LDY &&
1986 !CE_HasLabel (L[3]) &&
1987 L[4]->OPC == OP65_JSR &&
1988 strcmp (L[4]->Arg, "ldauidx") == 0 &&
1989 !CE_HasLabel (L[4])) {
1993 /* Store the high byte and remove the TAX instead */
1994 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1+1", 0, L[0]->LI);
1995 CS_InsertEntry (S, X, I+1);
1998 /* Store the low byte */
1999 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[2]->LI);
2000 CS_InsertEntry (S, X, I+3);
2002 /* Delete the call to ldauidx */
2003 CS_DelEntry (S, I+5);
2005 /* Load high and low byte */
2006 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[3]->LI);
2007 CS_InsertEntry (S, X, I+5);
2008 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[3]->LI);
2009 CS_InsertEntry (S, X, I+6);
2011 /* Remember, we had changes */
2021 /* Return the number of changes made */
2027 static unsigned OptPtrLoad2 (CodeSeg* S)
2028 /* Search for the sequence:
2039 * and replace it by:
2051 unsigned Changes = 0;
2053 /* Walk over the entries */
2055 while (I < CS_GetEntryCount (S)) {
2059 /* Get next entry */
2060 L[0] = CS_GetEntry (S, I);
2062 /* Check for the sequence */
2063 if (L[0]->OPC == OP65_ADC &&
2064 CS_GetEntries (S, L+1, I+1, 7) &&
2065 L[1]->OPC == OP65_TAY &&
2066 !CE_HasLabel (L[1]) &&
2067 L[2]->OPC == OP65_TXA &&
2068 !CE_HasLabel (L[2]) &&
2069 L[3]->OPC == OP65_ADC &&
2070 !CE_HasLabel (L[3]) &&
2071 L[4]->OPC == OP65_TAX &&
2072 !CE_HasLabel (L[4]) &&
2073 L[5]->OPC == OP65_TYA &&
2074 !CE_HasLabel (L[5]) &&
2075 L[6]->OPC == OP65_LDY &&
2076 !CE_HasLabel (L[6]) &&
2077 L[7]->OPC == OP65_JSR &&
2078 strcmp (L[7]->Arg, "ldauidx") == 0 &&
2079 !CE_HasLabel (L[7])) {
2083 /* Store the low byte and remove the TAY instead */
2084 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
2085 CS_InsertEntry (S, X, I+1);
2086 CS_DelEntry (S, I+2);
2088 /* Store the high byte */
2089 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1+1", 0, L[3]->LI);
2090 CS_InsertEntry (S, X, I+4);
2092 /* Delete more transfer insns */
2093 CS_DelEntry (S, I+6);
2094 CS_DelEntry (S, I+5);
2096 /* Delete the call to ldauidx */
2097 CS_DelEntry (S, I+6);
2099 /* Load high and low byte */
2100 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[6]->LI);
2101 CS_InsertEntry (S, X, I+6);
2102 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[6]->LI);
2103 CS_InsertEntry (S, X, I+7);
2105 /* Remember, we had changes */
2115 /* Return the number of changes made */
2121 static unsigned OptPtrLoad3 (CodeSeg* S)
2122 /* Search for the sequence:
2134 * and replace it by:
2147 unsigned Changes = 0;
2149 /* Walk over the entries */
2151 while (I < CS_GetEntryCount (S)) {
2155 /* Get next entry */
2156 L[0] = CS_GetEntry (S, I);
2158 /* Check for the sequence */
2159 if (L[0]->OPC == OP65_ADC &&
2160 CS_GetEntries (S, L+1, I+1, 8) &&
2161 L[1]->OPC == OP65_PHA &&
2162 !CE_HasLabel (L[1]) &&
2163 L[2]->OPC == OP65_TXA &&
2164 !CE_HasLabel (L[2]) &&
2165 L[3]->OPC == OP65_INY &&
2166 !CE_HasLabel (L[3]) &&
2167 L[4]->OPC == OP65_ADC &&
2168 !CE_HasLabel (L[4]) &&
2169 L[5]->OPC == OP65_TAX &&
2170 !CE_HasLabel (L[5]) &&
2171 L[6]->OPC == OP65_PLA &&
2172 !CE_HasLabel (L[6]) &&
2173 L[7]->OPC == OP65_LDY &&
2174 !CE_HasLabel (L[7]) &&
2175 L[8]->OPC == OP65_JSR &&
2176 strcmp (L[8]->Arg, "ldauidx") == 0 &&
2177 !CE_HasLabel (L[8])) {
2181 /* Store the low byte and remove the PHA instead */
2182 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
2183 CS_InsertEntry (S, X, I+1);
2184 CS_DelEntry (S, I+2);
2186 /* Store the high byte */
2187 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1+1", 0, L[4]->LI);
2188 CS_InsertEntry (S, X, I+5);
2190 /* Delete more transfer and PLA insns */
2191 CS_DelEntry (S, I+7);
2192 CS_DelEntry (S, I+6);
2194 /* Delete the call to ldauidx */
2195 CS_DelEntry (S, I+7);
2197 /* Load high and low byte */
2198 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[6]->LI);
2199 CS_InsertEntry (S, X, I+7);
2200 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[6]->LI);
2201 CS_InsertEntry (S, X, I+8);
2203 /* Remember, we had changes */
2213 /* Return the number of changes made */
2219 static unsigned OptPtrLoad4 (CodeSeg* S)
2220 /* Search for the sequence:
2231 * and replace it by:
2238 unsigned Changes = 0;
2240 /* Walk over the entries */
2242 while (I < CS_GetEntryCount (S)) {
2247 /* Get next entry */
2248 L[0] = CS_GetEntry (S, I);
2250 /* Check for the sequence */
2251 if (L[0]->OPC == OP65_LDA &&
2252 L[0]->AM == AM65_IMM &&
2253 CS_GetEntries (S, L+1, I+1, 7) &&
2254 L[1]->OPC == OP65_LDX &&
2255 L[1]->AM == AM65_IMM &&
2256 !CE_HasLabel (L[1]) &&
2257 L[2]->OPC == OP65_CLC &&
2258 !CE_HasLabel (L[2]) &&
2259 L[3]->OPC == OP65_ADC &&
2260 (L[3]->AM == AM65_ABS || L[3]->AM == AM65_ZP) &&
2261 !CE_HasLabel (L[3]) &&
2262 (L[4]->OPC == OP65_BCC || L[4]->OPC == OP65_JCC) &&
2263 L[4]->JumpTo != 0 &&
2264 L[4]->JumpTo->Owner == L[6] &&
2265 !CE_HasLabel (L[4]) &&
2266 L[5]->OPC == OP65_INX &&
2267 !CE_HasLabel (L[5]) &&
2268 L[6]->OPC == OP65_LDY &&
2269 CE_KnownImm (L[6]) &&
2271 L[7]->OPC == OP65_JSR &&
2272 strcmp (L[7]->Arg, "ldauidx") == 0 &&
2273 !CE_HasLabel (L[7]) &&
2274 /* Check the label last because this is quite costly */
2275 (Len = strlen (L[0]->Arg)) > 3 &&
2276 L[0]->Arg[0] == '<' &&
2277 L[0]->Arg[1] == '(' &&
2278 strlen (L[1]->Arg) == Len &&
2279 L[1]->Arg[0] == '>' &&
2280 memcmp (L[0]->Arg+1, L[1]->Arg+1, Len-1) == 0) {
2285 /* We will create all the new stuff behind the current one so
2286 * we keep the line references.
2288 X = NewCodeEntry (OP65_LDX, L[3]->AM, L[3]->Arg, 0, L[0]->LI);
2289 CS_InsertEntry (S, X, I+8);
2291 Label = memcpy (xmalloc (Len-2), L[0]->Arg+2, Len-3);
2292 Label[Len-3] = '\0';
2293 X = NewCodeEntry (OP65_LDA, AM65_ABSX, Label, 0, L[0]->LI);
2294 CS_InsertEntry (S, X, I+9);
2297 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[0]->LI);
2298 CS_InsertEntry (S, X, I+10);
2300 /* Remove the old code */
2301 CS_DelEntries (S, I, 8);
2303 /* Remember, we had changes */
2313 /* Return the number of changes made */
2319 static unsigned OptPtrLoad5 (CodeSeg* S)
2320 /* Search for the sequence:
2332 * and replace it by:
2341 unsigned Changes = 0;
2343 /* Walk over the entries */
2345 while (I < CS_GetEntryCount (S)) {
2350 /* Get next entry */
2351 L[0] = CS_GetEntry (S, I);
2353 /* Check for the sequence */
2354 if (L[0]->OPC == OP65_LDA &&
2355 L[0]->AM == AM65_IMM &&
2356 CS_GetEntries (S, L+1, I+1, 8) &&
2357 L[1]->OPC == OP65_LDX &&
2358 L[1]->AM == AM65_IMM &&
2359 !CE_HasLabel (L[1]) &&
2360 L[2]->OPC == OP65_LDY &&
2361 CE_KnownImm (L[2]) &&
2362 !CE_HasLabel (L[2]) &&
2363 L[3]->OPC == OP65_CLC &&
2364 !CE_HasLabel (L[3]) &&
2365 L[4]->OPC == OP65_ADC &&
2366 L[4]->AM == AM65_ZP_INDY &&
2367 !CE_HasLabel (L[4]) &&
2368 (L[5]->OPC == OP65_BCC || L[5]->OPC == OP65_JCC) &&
2369 L[5]->JumpTo != 0 &&
2370 L[5]->JumpTo->Owner == L[7] &&
2371 !CE_HasLabel (L[5]) &&
2372 L[6]->OPC == OP65_INX &&
2373 !CE_HasLabel (L[6]) &&
2374 L[7]->OPC == OP65_LDY &&
2375 CE_KnownImm (L[7]) &&
2377 L[8]->OPC == OP65_JSR &&
2378 strcmp (L[8]->Arg, "ldauidx") == 0 &&
2379 !CE_HasLabel (L[8]) &&
2380 /* Check the label last because this is quite costly */
2381 (Len = strlen (L[0]->Arg)) > 3 &&
2382 L[0]->Arg[0] == '<' &&
2383 L[0]->Arg[1] == '(' &&
2384 strlen (L[1]->Arg) == Len &&
2385 L[1]->Arg[0] == '>' &&
2386 memcmp (L[0]->Arg+1, L[1]->Arg+1, Len-1) == 0) {
2392 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, L[4]->Arg, 0, L[0]->LI);
2393 CS_InsertEntry (S, X, I+3);
2396 X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI);
2397 CS_InsertEntry (S, X, I+4);
2400 Label = memcpy (xmalloc (Len-2), L[0]->Arg+2, Len-3);
2401 Label[Len-3] = '\0';
2402 X = NewCodeEntry (OP65_LDA, AM65_ABSX, Label, 0, L[0]->LI);
2403 CS_InsertEntry (S, X, I+5);
2407 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[0]->LI);
2408 CS_InsertEntry (S, X, I+6);
2410 /* Remove the old code */
2411 CS_DelEntries (S, I, 2);
2412 CS_DelEntries (S, I+5, 6);
2414 /* Remember, we had changes */
2424 /* Return the number of changes made */
2430 static unsigned OptPtrLoad6 (CodeSeg* S)
2431 /* Search for the sequence
2436 * and replace it by:
2444 * This step must be execute *after* OptPtrLoad1!
2447 unsigned Changes = 0;
2449 /* Walk over the entries */
2451 while (I < CS_GetEntryCount (S)) {
2455 /* Get next entry */
2456 L[0] = CS_GetEntry (S, I);
2458 /* Check for the sequence */
2459 if (L[0]->OPC == OP65_LDY &&
2460 CS_GetEntries (S, L+1, I+1, 1) &&
2461 L[1]->OPC == OP65_JSR &&
2462 strcmp (L[1]->Arg, "ldauidx") == 0 &&
2463 !CE_HasLabel (L[1])) {
2467 /* Store the high byte */
2468 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
2469 CS_InsertEntry (S, X, I+1);
2471 /* Store the low byte */
2472 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
2473 CS_InsertEntry (S, X, I+2);
2475 /* Delete the call to ldauidx */
2476 CS_DelEntry (S, I+3);
2478 /* Load the high and low byte */
2479 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[0]->LI);
2480 CS_InsertEntry (S, X, I+3);
2481 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[0]->LI);
2482 CS_InsertEntry (S, X, I+4);
2484 /* Remember, we had changes */
2494 /* Return the number of changes made */
2500 /*****************************************************************************/
2502 /*****************************************************************************/
2506 /* Types of optimization steps */
2508 optPre, /* Repeated once */
2509 optPreMain, /* Repeated more than once */
2511 optPostMain, /* dito */
2512 optPost /* Repeated once */
2515 /* Table with all the optimization functions */
2516 typedef struct OptFunc OptFunc;
2518 unsigned (*Func) (CodeSeg*); /* Optimizer function */
2519 const char* Name; /* Name of optimizer step */
2520 unsigned char Type; /* Type of this step */
2521 char Disabled; /* True if pass disabled */
2524 /* Macro that builds a table entry */
2525 #define OptEntry(func,type) { func, #func, type, 0 }
2527 /* Table with optimizer steps */
2528 static OptFunc OptFuncs [] = {
2529 /* Optimizes stores through pointers */
2530 OptEntry (OptPtrStore1, optPre),
2531 OptEntry (OptPtrStore2, optPre),
2532 /* Optimize loads through pointers */
2533 OptEntry (OptPtrLoad1, optPre),
2534 OptEntry (OptPtrLoad2, optPre),
2535 OptEntry (OptPtrLoad3, optPre),
2536 OptEntry (OptPtrLoad4, optPre),
2537 OptEntry (OptPtrLoad5, optPre),
2538 OptEntry (OptPtrLoad6, optMain),
2539 /* Optimize calls to nega */
2540 OptEntry (OptNegA1, optMain),
2541 OptEntry (OptNegA2, optMain),
2542 /* Optimize calls to negax */
2543 OptEntry (OptNegAX1, optPre),
2544 OptEntry (OptNegAX2, optPre),
2545 OptEntry (OptNegAX3, optPre),
2546 OptEntry (OptNegAX4, optPre),
2547 /* Optimize subtractions */
2548 OptEntry (OptSub1, optMain),
2549 OptEntry (OptSub2, optMain),
2550 /* Optimize additions */
2551 OptEntry (OptAdd1, optPre),
2552 OptEntry (OptAdd2, optPre),
2553 OptEntry (OptAdd3, optMain),
2554 /* Optimize shifts */
2555 OptEntry (OptShift1, optPre),
2556 /* Optimize jump cascades */
2557 OptEntry (OptJumpCascades, optMain),
2558 /* Remove dead jumps */
2559 OptEntry (OptDeadJumps, optMain),
2560 /* Change jsr/rts to jmp */
2561 OptEntry (OptRTS, optMain),
2562 /* Remove dead code */
2563 OptEntry (OptDeadCode, optMain),
2564 /* Optimize jump targets */
2565 OptEntry (OptJumpTarget, optMain),
2566 /* Optimize conditional branches */
2567 OptEntry (OptCondBranches, optMain),
2568 /* Replace jumps to RTS by RTS */
2569 OptEntry (OptRTSJumps, optMain),
2570 /* Remove calls to the bool transformer subroutines */
2571 OptEntry (OptBoolTransforms, optMain),
2572 /* Optimize compares */
2573 OptEntry (OptCmp1, optMain),
2574 OptEntry (OptCmp2, optMain),
2575 OptEntry (OptCmp3, optMain),
2576 OptEntry (OptCmp4, optMain),
2577 OptEntry (OptCmp5, optMain),
2578 OptEntry (OptCmp6, optMain),
2579 /* Optimize tests */
2580 OptEntry (OptTest1, optMain),
2581 /* Remove unused loads */
2582 OptEntry (OptUnusedLoads, optMain),
2583 OptEntry (OptDuplicateLoads, optMain),
2584 OptEntry (OptStoreLoad, optMain),
2585 OptEntry (OptTransfers, optMain),
2586 /* Optimize branch distance */
2587 OptEntry (OptBranchDist, optPost),
2592 static OptFunc* FindOptStep (const char* Name)
2593 /* Find an optimizer step by name in the table and return a pointer. Print an
2594 * error and call AbEnd if not found.
2599 /* Run all optimization steps */
2600 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2601 if (strcmp (OptFuncs[I].Name, Name) == 0) {
2608 AbEnd ("Optimization step `%s' not found", Name);
2614 void DisableOpt (const char* Name)
2615 /* Disable the optimization with the given name */
2617 if (strcmp (Name, "any") == 0) {
2619 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2620 OptFuncs[I].Disabled = 1;
2623 OptFunc* F = FindOptStep (Name);
2630 void EnableOpt (const char* Name)
2631 /* Enable the optimization with the given name */
2633 if (strcmp (Name, "any") == 0) {
2635 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2636 OptFuncs[I].Disabled = 0;
2639 OptFunc* F = FindOptStep (Name);
2646 void ListOptSteps (FILE* F)
2647 /* List all optimization steps */
2650 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2651 fprintf (F, "%s\n", OptFuncs[I].Name);
2657 static void RepeatOptStep (CodeSeg* S, unsigned char Type, unsigned Max)
2658 /* Repeat the optimizer step of type Type at may Max times */
2662 unsigned OptChanges;
2664 /* Repeat max times of until there are no more changes */
2666 /* Reset the number of changes */
2669 /* Keep the user hapy */
2670 Print (stdout, 1, " Optimizer pass %u:\n", ++Pass);
2672 /* Run all optimization steps */
2673 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2675 /* Get the table entry */
2676 const OptFunc* F = OptFuncs + I;
2678 /* Check if the type matches */
2679 if (F->Type != Type) {
2684 /* Print the name of the following optimizer step */
2685 Print (stdout, 1, " %s:%*s", F->Name, (int) (30-strlen(F->Name)), "");
2687 /* Check if the step is disabled */
2689 Print (stdout, 1, "Disabled\n");
2691 unsigned Changes = F->Func (S);
2692 OptChanges += Changes;
2693 Print (stdout, 1, "%u Changes\n", Changes);
2697 } while (--Max > 0 && OptChanges > 0);
2702 void RunOpt (CodeSeg* S)
2703 /* Run the optimizer */
2706 /* If we shouldn't run the optimizer, bail out */
2711 /* Print the name of the function we are working on */
2713 Print (stdout, 1, "Running optimizer for function `%s'\n", S->Func->Name);
2715 Print (stdout, 1, "Running optimizer for global code segment\n");
2718 /* Repeat all steps until there are no more changes */
2719 RepeatOptStep (S, optPre, 1);
2720 RepeatOptStep (S, optPreMain, 0xFFFF);
2721 RepeatOptStep (S, optMain, 0xFFFF);
2722 RepeatOptStep (S, optPostMain, 0xFFFF);
2723 RepeatOptStep (S, optPost, 1);