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 /*****************************************************************************/
53 /*****************************************************************************/
55 /*****************************************************************************/
59 /* Defines for the conditions in a compare */
74 /* Table with the compare suffixes */
75 static const char CmpSuffixTab [][4] = {
76 "eq", "ne", "gt", "ge", "lt", "le", "ugt", "uge", "ult", "ule"
79 /* Table used to invert a condition, indexed by condition */
80 static const unsigned char CmpInvertTab [] = {
82 CMP_LE, CMP_LT, CMP_GE, CMP_GT,
83 CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT
86 /* Table to show which compares are signed (use the N flag) */
87 static const char CmpSignedTab [] = {
88 0, 0, 1, 1, 1, 1, 0, 0, 0, 0
93 /*****************************************************************************/
94 /* Helper functions */
95 /*****************************************************************************/
99 static cmp_t FindCmpCond (const char* Code, unsigned CodeLen)
100 /* Search for a compare condition by the given code using the given length */
105 for (I = 0; I < sizeof (CmpSuffixTab) / sizeof (CmpSuffixTab [0]); ++I) {
106 if (strncmp (Code, CmpSuffixTab [I], CodeLen) == 0) {
118 static cmp_t FindBoolCmpCond (const char* Name)
119 /* Map a condition suffix to a code. Return the code or CMP_INV on failure */
121 /* Check for the correct subroutine name */
122 if (strncmp (Name, "bool", 4) == 0) {
123 /* Name is ok, search for the code in the table */
124 return FindCmpCond (Name+4, strlen(Name)-4);
133 static cmp_t FindTosCmpCond (const char* Name)
134 /* Check if this is a call to one of the TOS compare functions (tosgtax).
135 * Return the condition code or CMP_INV on failure.
138 unsigned Len = strlen (Name);
140 /* Check for the correct subroutine name */
141 if (strncmp (Name, "tos", 3) == 0 && strcmp (Name+Len-2, "ax") == 0) {
142 /* Name is ok, search for the code in the table */
143 return FindCmpCond (Name+3, Len-3-2);
152 static void ReplaceCmp (CodeSeg* S, unsigned I, cmp_t Cond)
153 /* Helper function for the replacement of routines that return a boolean
154 * followed by a conditional jump. Instead of the boolean value, the condition
155 * codes are evaluated directly.
156 * I is the index of the conditional branch, the sequence is already checked
164 CodeEntry* E = CS_GetEntry (S, I);
166 /* Replace the conditional branch */
170 CE_ReplaceOPC (E, OP65_JEQ);
174 CE_ReplaceOPC (E, OP65_JNE);
183 if ((N = CS_GetNextEntry (S, I)) == 0) {
185 Internal ("Invalid program flow");
187 L = CS_GenLabel (S, N);
188 N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI);
189 CS_InsertEntry (S, N, I);
190 CE_ReplaceOPC (E, OP65_JPL);
194 CE_ReplaceOPC (E, OP65_JPL);
198 CE_ReplaceOPC (E, OP65_JMI);
206 CE_ReplaceOPC (E, OP65_JMI);
208 N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI);
209 CS_InsertEntry (S, N, I+1);
218 if ((N = CS_GetNextEntry (S, I)) == 0) {
220 Internal ("Invalid program flow");
222 L = CS_GenLabel (S, N);
223 N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI);
224 CS_InsertEntry (S, N, I);
225 CE_ReplaceOPC (E, OP65_JCS);
229 CE_ReplaceOPC (E, OP65_JCS);
233 CE_ReplaceOPC (E, OP65_JCC);
241 CE_ReplaceOPC (E, OP65_JCC);
243 N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI);
244 CS_InsertEntry (S, N, I+1);
248 Internal ("Unknown jump condition: %d", Cond);
256 static int IsCmpToZero (const CodeEntry* E)
257 /* Check if the given instrcuction is a compare to zero instruction */
259 return (E->OPC == OP65_CMP &&
261 (E->Flags & CEF_NUMARG) != 0 &&
267 static int IsSpLoad (const CodeEntry* E)
268 /* Return true if this is the load of A from the stack */
270 return E->OPC == OP65_LDA && E->AM == AM65_ZP_INDY && strcmp (E->Arg, "sp") == 0;
275 static int IsLocalLoad16 (CodeSeg* S, unsigned Index,
276 CodeEntry** L, unsigned Count)
277 /* Check if a 16 bit load of a local variable follows:
285 * If so, read Count entries following the first ldy into L and return true
286 * if this is possible. Otherwise return false.
289 /* Be sure we read enough entries for the check */
292 /* Read the first entry */
293 L[0] = CS_GetEntry (S, Index);
295 /* Check for the sequence */
296 return (L[0]->OPC == OP65_LDY &&
297 L[0]->AM == AM65_IMM &&
298 (L[0]->Flags & CEF_NUMARG) != 0 &&
299 CS_GetEntries (S, L+1, Index+1, Count-1) &&
301 !CE_HasLabel (L[1]) &&
302 L[2]->OPC == OP65_TAX &&
303 !CE_HasLabel (L[2]) &&
304 L[3]->OPC == OP65_DEY &&
305 !CE_HasLabel (L[3]) &&
307 !CE_HasLabel (L[4]));
312 static int IsImmCmp16 (CodeSeg* S, CodeEntry** L)
313 /* Check if the instructions at L are an immidiate compare of a/x:
318 return (L[0]->OPC == OP65_CPX &&
319 L[0]->AM == AM65_IMM &&
320 (L[0]->Flags & CEF_NUMARG) != 0 &&
321 !CE_HasLabel (L[0]) &&
322 (L[1]->OPC == OP65_JNE || L[1]->OPC == OP65_BNE) &&
324 !CE_HasLabel (L[1]) &&
325 L[2]->OPC == OP65_CMP &&
326 L[2]->AM == AM65_IMM &&
327 (L[2]->Flags & CEF_NUMARG) != 0 &&
328 (L[3]->Info & OF_ZBRA) != 0 &&
330 (L[1]->JumpTo->Owner == L[3] || L[1]->JumpTo == L[3]->JumpTo));
335 /*****************************************************************************/
336 /* Remove calls to the bool transformer subroutines */
337 /*****************************************************************************/
341 static unsigned OptBoolTransforms (CodeSeg* S)
342 /* Try to remove the call to boolean transformer routines where the call is
346 unsigned Changes = 0;
348 /* Walk over the entries */
350 while (I < CS_GetEntryCount (S)) {
356 CodeEntry* E = CS_GetEntry (S, I);
358 /* Check for a boolean transformer */
359 if (E->OPC == OP65_JSR &&
360 (Cond = FindBoolCmpCond (E->Arg)) != CMP_INV &&
361 (N = CS_GetNextEntry (S, I)) != 0 &&
362 (N->Info & OF_ZBRA) != 0) {
364 /* Make the boolean transformer unnecessary by changing the
365 * the conditional jump to evaluate the condition flags that
366 * are set after the compare directly. Note: jeq jumps if
367 * the condition is not met, jne jumps if the condition is met.
368 * Invert the code if we jump on condition not met.
370 if (GetBranchCond (N->OPC) == BC_EQ) {
371 /* Jumps if condition false, invert condition */
372 Cond = CmpInvertTab [Cond];
375 /* Check if we can replace the code by something better */
376 ReplaceCmp (S, I+1, Cond);
378 /* Remove the call to the bool transformer */
381 /* Remember, we had changes */
391 /* Return the number of changes made */
397 /*****************************************************************************/
398 /* Optimize subtractions */
399 /*****************************************************************************/
403 static unsigned OptSub1 (CodeSeg* S)
404 /* Search for the sequence
411 * and remove the handling of the high byte if X is not used later.
414 unsigned Changes = 0;
416 /* Walk over the entries */
418 while (I < CS_GetEntryCount (S)) {
423 CodeEntry* E = CS_GetEntry (S, I);
425 /* Check for the sequence */
426 if (E->OPC == OP65_SBC &&
427 CS_GetEntries (S, L, I+1, 3) &&
428 (L[0]->OPC == OP65_BCS || L[0]->OPC == OP65_JCS) &&
430 !CE_HasLabel (L[0]) &&
431 L[1]->OPC == OP65_DEX &&
432 !CE_HasLabel (L[1]) &&
433 L[0]->JumpTo->Owner == L[2] &&
434 !RegXUsed (S, I+3)) {
436 /* Remove the bcs/dex */
437 CS_DelEntries (S, I+1, 2);
439 /* Remember, we had changes */
449 /* Return the number of changes made */
455 static unsigned OptSub2 (CodeSeg* S)
456 /* Search for the sequence
473 unsigned Changes = 0;
475 /* Walk over the entries */
477 while (I < CS_GetEntryCount (S)) {
482 CodeEntry* E = CS_GetEntry (S, I);
484 /* Check for the sequence */
485 if (E->OPC == OP65_LDA &&
486 CS_GetEntries (S, L, I+1, 5) &&
487 L[0]->OPC == OP65_SEC &&
488 !CE_HasLabel (L[0]) &&
489 L[1]->OPC == OP65_STA &&
490 strcmp (L[1]->Arg, "tmp1") == 0 &&
491 !CE_HasLabel (L[1]) &&
492 L[2]->OPC == OP65_LDA &&
493 !CE_HasLabel (L[2]) &&
494 L[3]->OPC == OP65_SBC &&
495 strcmp (L[3]->Arg, "tmp1") == 0 &&
496 !CE_HasLabel (L[3]) &&
497 L[4]->OPC == OP65_STA &&
498 strcmp (L[4]->Arg, L[2]->Arg) == 0 &&
499 !CE_HasLabel (L[4])) {
501 /* Remove the store to tmp1 */
502 CS_DelEntry (S, I+2);
504 /* Remove the subtraction */
505 CS_DelEntry (S, I+3);
507 /* Move the lda to the position of the subtraction and change the
510 CS_MoveEntry (S, I, I+3);
511 CE_ReplaceOPC (E, OP65_SBC);
513 /* If the sequence head had a label, move this label back to the
516 if (CE_HasLabel (E)) {
517 CS_MoveLabels (S, E, L[0]);
520 /* Remember, we had changes */
530 /* Return the number of changes made */
536 /*****************************************************************************/
537 /* Optimize additions */
538 /*****************************************************************************/
542 static unsigned OptAdd1 (CodeSeg* S)
543 /* Search for the sequence
550 * and remove the handling of the high byte if X is not used later.
553 unsigned Changes = 0;
555 /* Walk over the entries */
557 while (I < CS_GetEntryCount (S)) {
562 CodeEntry* E = CS_GetEntry (S, I);
564 /* Check for the sequence */
565 if (E->OPC == OP65_ADC &&
566 CS_GetEntries (S, L, I+1, 3) &&
567 (L[0]->OPC == OP65_BCC || L[0]->OPC == OP65_JCC) &&
569 !CE_HasLabel (L[0]) &&
570 L[1]->OPC == OP65_INX &&
571 !CE_HasLabel (L[1]) &&
572 L[0]->JumpTo->Owner == L[2] &&
573 !RegXUsed (S, I+3)) {
575 /* Remove the bcs/dex */
576 CS_DelEntries (S, I+1, 2);
578 /* Remember, we had changes */
588 /* Return the number of changes made */
594 /*****************************************************************************/
595 /* Optimizations for compares */
596 /*****************************************************************************/
600 static unsigned OptCmp1 (CodeSeg* S)
601 /* Search for the sequence
613 unsigned Changes = 0;
615 /* Walk over the entries */
617 while (I < CS_GetEntryCount (S)) {
622 CodeEntry* E = CS_GetEntry (S, I);
624 /* Check for the sequence */
625 if (E->OPC == OP65_STX &&
626 CS_GetEntries (S, L, I+1, 2) &&
627 L[0]->OPC == OP65_STX &&
628 strcmp (L[0]->Arg, "tmp1") == 0 &&
629 !CE_HasLabel (L[0]) &&
630 L[1]->OPC == OP65_ORA &&
631 strcmp (L[1]->Arg, "tmp1") == 0 &&
632 !CE_HasLabel (L[1])) {
634 /* Remove the remaining instructions */
635 CS_DelEntries (S, I+1, 2);
637 /* Insert the ora instead */
638 CS_InsertEntry (S, NewCodeEntry (OP65_ORA, E->AM, E->Arg, 0, E->LI), I+1);
640 /* Remember, we had changes */
650 /* Return the number of changes made */
656 static unsigned OptCmp2 (CodeSeg* S)
659 * lda/and/ora/eor ...
663 * and remove the cmp.
666 unsigned Changes = 0;
668 /* Walk over the entries */
670 while (I < CS_GetEntryCount (S)) {
675 CodeEntry* E = CS_GetEntry (S, I);
677 /* Check for the sequence */
678 if ((E->OPC == OP65_ADC ||
679 E->OPC == OP65_AND ||
680 E->OPC == OP65_DEA ||
681 E->OPC == OP65_EOR ||
682 E->OPC == OP65_INA ||
683 E->OPC == OP65_LDA ||
684 E->OPC == OP65_ORA ||
685 E->OPC == OP65_PLA ||
686 E->OPC == OP65_SBC ||
687 E->OPC == OP65_TXA ||
688 E->OPC == OP65_TYA) &&
689 CS_GetEntries (S, L, I+1, 2) &&
690 IsCmpToZero (L[0]) &&
691 !CE_HasLabel (L[0]) &&
692 (L[1]->Info & OF_FBRA) != 0 &&
693 !CE_HasLabel (L[1])) {
695 /* Remove the compare */
696 CS_DelEntry (S, I+1);
698 /* Remember, we had changes */
708 /* Return the number of changes made */
714 static unsigned OptCmp3 (CodeSeg* S)
724 * If a is zero, we may remove the compare. If a and b are both zero, we may
725 * replace it by the sequence
731 * L1 may be either the label at the branch instruction, or the target label
732 * of this instruction.
735 unsigned Changes = 0;
737 /* Walk over the entries */
739 while (I < CS_GetEntryCount (S)) {
744 CodeEntry* E = CS_GetEntry (S, I);
746 /* Check for the sequence */
747 if (E->OPC == OP65_LDA &&
748 CS_GetEntries (S, L, I+1, 5) &&
749 L[0]->OPC == OP65_LDX &&
750 !CE_HasLabel (L[0]) &&
751 IsImmCmp16 (S, L+1)) {
753 if (L[1]->Num == 0 && L[3]->Num == 0) {
754 /* The value is zero, we may use the simple code version. */
755 CE_ReplaceOPC (L[0], OP65_ORA);
756 CS_DelEntries (S, I+2, 3);
758 /* Move the lda instruction after the first branch. This will
759 * improve speed, since the load is delayed after the first
762 CS_MoveEntry (S, I, I+4);
764 /* We will replace the ldx/cpx by lda/cmp */
765 CE_ReplaceOPC (L[0], OP65_LDA);
766 CE_ReplaceOPC (L[1], OP65_CMP);
768 /* Beware: If the first LDA instruction had a label, we have
769 * to move this label to the top of the sequence again.
771 if (CE_HasLabel (E)) {
772 CS_MoveLabels (S, E, L[0]);
785 /* Return the number of changes made */
791 static unsigned OptCmp4 (CodeSeg* S)
792 /* Optimize compares of local variables:
805 unsigned Changes = 0;
807 /* Walk over the entries */
809 while (I < CS_GetEntryCount (S)) {
813 /* Check for the sequence */
814 if (IsLocalLoad16 (S, I, L, 9) && IsImmCmp16 (S, L+5)) {
816 if (L[5]->Num == 0 && L[7]->Num == 0) {
818 /* The value is zero, we may use the simple code version:
825 CE_ReplaceOPC (L[4], OP65_ORA);
826 CS_DelEntries (S, I+5, 3); /* cpx/bne/cmp */
827 CS_DelEntry (S, I+2); /* tax */
831 /* Change the code to just use the A register. Move the load
832 * of the low byte after the first branch if possible:
843 CS_DelEntry (S, I+2); /* tax */
844 CE_ReplaceOPC (L[5], OP65_CMP); /* cpx -> cmp */
845 CS_MoveEntry (S, I+4, I+2); /* cmp */
846 CS_MoveEntry (S, I+5, I+3); /* bne */
858 /* Return the number of changes made */
864 static unsigned OptCmp5 (CodeSeg* S)
865 /* Search for calls to compare subroutines followed by a conditional branch
866 * and replace them by cheaper versions, since the branch means that the
867 * boolean value returned by these routines is not needed (we may also check
868 * that explicitly, but for the current code generator it is always true).
871 unsigned Changes = 0;
873 /* Walk over the entries */
875 while (I < CS_GetEntryCount (S)) {
881 CodeEntry* E = CS_GetEntry (S, I);
883 /* Check for the sequence */
884 if (E->OPC == OP65_JSR &&
885 (Cond = FindTosCmpCond (E->Arg)) != CMP_INV &&
886 (N = CS_GetNextEntry (S, I)) != 0 &&
887 (N->Info & OF_ZBRA) != 0 &&
890 /* The tos... functions will return a boolean value in a/x and
891 * the Z flag says if this value is zero or not. We will call
892 * a cheaper subroutine instead, one that does not return a
893 * boolean value but only valid flags. Note: jeq jumps if
894 * the condition is not met, jne jumps if the condition is met.
895 * Invert the code if we jump on condition not met.
897 if (GetBranchCond (N->OPC) == BC_EQ) {
898 /* Jumps if condition false, invert condition */
899 Cond = CmpInvertTab [Cond];
902 /* Replace the subroutine call. */
903 E = NewCodeEntry (OP65_JSR, AM65_ABS, "tosicmp", 0, E->LI);
904 CS_InsertEntry (S, E, I+1);
907 /* Replace the conditional branch */
908 ReplaceCmp (S, I+1, Cond);
910 /* Remember, we had changes */
920 /* Return the number of changes made */
926 static unsigned OptCmp6 (CodeSeg* S)
927 /* Search for a sequence ldx/txa/branch and remove the txa if A is not
931 unsigned Changes = 0;
933 /* Walk over the entries */
935 while (I < CS_GetEntryCount (S)) {
940 CodeEntry* E = CS_GetEntry (S, I);
942 /* Check for the sequence */
943 if ((E->OPC == OP65_LDX || E->OPC == OP65_TAX) &&
944 CS_GetEntries (S, L, I+1, 2) &&
945 L[0]->OPC == OP65_TXA &&
946 !CE_HasLabel (L[0]) &&
947 (L[1]->Info & OF_FBRA) != 0 &&
948 !CE_HasLabel (L[1]) &&
949 !RegAUsed (S, I+3)) {
952 CS_DelEntry (S, I+1);
954 /* Remember, we had changes */
964 /* Return the number of changes made */
970 /*****************************************************************************/
972 /*****************************************************************************/
976 static unsigned OptTest1 (CodeSeg* S)
983 * if X is zero, the sequence may be changed
988 * which may be optimized further by another step.
991 unsigned Changes = 0;
994 /* Generate register info for this step */
997 /* Walk over the entries */
999 while (I < CS_GetEntryCount (S)) {
1003 /* Get next entry */
1004 L[0] = CS_GetEntry (S, I);
1006 /* Check if it's the sequence we're searching for */
1007 if (L[0]->OPC == OP65_STX &&
1008 L[0]->RI->In.RegX == 0 &&
1009 CS_GetEntries (S, L+1, I+1, 2) &&
1010 !CE_HasLabel (L[1]) &&
1011 L[1]->OPC == OP65_ORA &&
1012 strcmp (L[0]->Arg, L[1]->Arg) == 0 &&
1013 !CE_HasLabel (L[2]) &&
1014 (L[2]->Info & OF_ZBRA) != 0) {
1016 /* Insert the compare */
1017 CodeEntry* N = NewCodeEntry (OP65_CMP, AM65_IMM, "$00", 0, L[0]->LI);
1018 CS_InsertEntry (S, N, I+2);
1020 /* Remove the two other insns */
1021 CS_DelEntry (S, I+1);
1024 /* We had changes */
1033 /* Free register info */
1036 /* Return the number of changes made */
1046 /*****************************************************************************/
1047 /* nega optimizations */
1048 /*****************************************************************************/
1052 static unsigned OptNegA1 (CodeSeg* S)
1059 * Remove the ldx if the lda does not use it.
1062 unsigned Changes = 0;
1064 /* Walk over the entries */
1066 while (I < CS_GetEntryCount (S)) {
1070 /* Get next entry */
1071 CodeEntry* E = CS_GetEntry (S, I);
1073 /* Check for a ldx */
1074 if (E->OPC == OP65_LDX &&
1075 E->AM == AM65_IMM &&
1076 (E->Flags & CEF_NUMARG) != 0 &&
1078 CS_GetEntries (S, L, I+1, 2) &&
1079 L[0]->OPC == OP65_LDA &&
1080 (L[0]->Use & REG_X) == 0 &&
1081 !CE_HasLabel (L[0]) &&
1082 L[1]->OPC == OP65_JSR &&
1083 strcmp (L[1]->Arg, "bnega") == 0 &&
1084 !CE_HasLabel (L[1])) {
1086 /* Remove the ldx instruction */
1089 /* Remember, we had changes */
1099 /* Return the number of changes made */
1105 static unsigned OptNegA2 (CodeSeg* S)
1112 * Adjust the conditional branch and remove the call to the subroutine.
1115 unsigned Changes = 0;
1117 /* Walk over the entries */
1119 while (I < CS_GetEntryCount (S)) {
1123 /* Get next entry */
1124 CodeEntry* E = CS_GetEntry (S, I);
1126 /* Check for the sequence */
1127 if ((E->OPC == OP65_ADC ||
1128 E->OPC == OP65_AND ||
1129 E->OPC == OP65_DEA ||
1130 E->OPC == OP65_EOR ||
1131 E->OPC == OP65_INA ||
1132 E->OPC == OP65_LDA ||
1133 E->OPC == OP65_ORA ||
1134 E->OPC == OP65_PLA ||
1135 E->OPC == OP65_SBC ||
1136 E->OPC == OP65_TXA ||
1137 E->OPC == OP65_TYA) &&
1138 CS_GetEntries (S, L, I+1, 2) &&
1139 L[0]->OPC == OP65_JSR &&
1140 strcmp (L[0]->Arg, "bnega") == 0 &&
1141 !CE_HasLabel (L[0]) &&
1142 (L[1]->Info & OF_ZBRA) != 0 &&
1143 !CE_HasLabel (L[1])) {
1145 /* Invert the branch */
1146 CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
1148 /* Delete the subroutine call */
1149 CS_DelEntry (S, I+1);
1151 /* Remember, we had changes */
1161 /* Return the number of changes made */
1167 /*****************************************************************************/
1168 /* negax optimizations */
1169 /*****************************************************************************/
1173 static unsigned OptNegAX1 (CodeSeg* S)
1174 /* On a call to bnegax, if X is zero, the result depends only on the value in
1175 * A, so change the call to a call to bnega. This will get further optimized
1176 * later if possible.
1179 unsigned Changes = 0;
1182 /* Generate register info for this step */
1185 /* Walk over the entries */
1187 while (I < CS_GetEntryCount (S)) {
1189 /* Get next entry */
1190 CodeEntry* E = CS_GetEntry (S, I);
1192 /* Check if this is a call to bnegax, and if X is known and zero */
1193 if (E->OPC == OP65_JSR &&
1194 E->RI->In.RegX == 0 &&
1195 strcmp (E->Arg, "bnegax") == 0) {
1197 /* We're cheating somewhat here ... */
1201 /* We had changes */
1210 /* Free register info */
1213 /* Return the number of changes made */
1219 static unsigned OptNegAX2 (CodeSeg* S)
1220 /* Search for the sequence:
1237 unsigned Changes = 0;
1239 /* Walk over the entries */
1241 while (I < CS_GetEntryCount (S)) {
1245 /* Get next entry */
1246 CodeEntry* E = CS_GetEntry (S, I);
1248 /* Check for the sequence */
1249 if (E->OPC == OP65_LDA &&
1250 E->AM == AM65_ZP_INDY &&
1251 CS_GetEntries (S, L, I+1, 5) &&
1252 L[0]->OPC == OP65_TAX &&
1253 L[1]->OPC == OP65_DEY &&
1254 L[2]->OPC == OP65_LDA &&
1255 L[2]->AM == AM65_ZP_INDY &&
1256 strcmp (L[2]->Arg, E->Arg) == 0 &&
1257 !CE_HasLabel (L[2]) &&
1258 L[3]->OPC == OP65_JSR &&
1259 strcmp (L[3]->Arg, "bnegax") == 0 &&
1260 !CE_HasLabel (L[3]) &&
1261 (L[4]->Info & OF_ZBRA) != 0 &&
1262 !CE_HasLabel (L[4])) {
1265 CE_ReplaceOPC (L[2], OP65_ORA);
1267 /* Invert the branch */
1268 CE_ReplaceOPC (L[4], GetInverseBranch (L[4]->OPC));
1270 /* Delete the entries no longer needed. Beware: Deleting entries
1271 * will change the indices.
1273 CS_DelEntry (S, I+4); /* jsr bnegax */
1274 CS_DelEntry (S, I+1); /* tax */
1276 /* Remember, we had changes */
1286 /* Return the number of changes made */
1292 static unsigned OptNegAX3 (CodeSeg* S)
1293 /* Search for the sequence:
1307 unsigned Changes = 0;
1309 /* Walk over the entries */
1311 while (I < CS_GetEntryCount (S)) {
1315 /* Get next entry */
1316 CodeEntry* E = CS_GetEntry (S, I);
1318 /* Check for the sequence */
1319 if (E->OPC == OP65_LDA &&
1320 CS_GetEntries (S, L, I+1, 3) &&
1321 L[0]->OPC == OP65_LDX &&
1322 !CE_HasLabel (L[0]) &&
1323 L[1]->OPC == OP65_JSR &&
1324 strcmp (L[1]->Arg, "bnegax") == 0 &&
1325 !CE_HasLabel (L[1]) &&
1326 (L[2]->Info & OF_ZBRA) != 0 &&
1327 !CE_HasLabel (L[2])) {
1330 CE_ReplaceOPC (L[0], OP65_ORA);
1332 /* Invert the branch */
1333 CE_ReplaceOPC (L[2], GetInverseBranch (L[2]->OPC));
1335 /* Delete the subroutine call */
1336 CS_DelEntry (S, I+2);
1338 /* Remember, we had changes */
1348 /* Return the number of changes made */
1354 static unsigned OptNegAX4 (CodeSeg* S)
1355 /* Search for the sequence:
1361 * and replace it by:
1368 unsigned Changes = 0;
1370 /* Walk over the entries */
1372 while (I < CS_GetEntryCount (S)) {
1376 /* Get next entry */
1377 CodeEntry* E = CS_GetEntry (S, I);
1379 /* Check for the sequence */
1380 if (E->OPC == OP65_JSR &&
1381 CS_GetEntries (S, L, I+1, 2) &&
1382 L[0]->OPC == OP65_JSR &&
1383 strncmp (L[0]->Arg,"bnega",5) == 0 &&
1384 !CE_HasLabel (L[0]) &&
1385 (L[1]->Info & OF_ZBRA) != 0 &&
1386 !CE_HasLabel (L[1])) {
1390 /* Check if we're calling bnega or bnegax */
1391 int ByteSized = (strcmp (L[0]->Arg, "bnega") == 0);
1393 /* Insert apropriate test code */
1396 X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI);
1397 CS_InsertEntry (S, X, I+2);
1400 X = NewCodeEntry (OP65_STX, AM65_ZP, "tmp1", 0, L[0]->LI);
1401 CS_InsertEntry (S, X, I+2);
1402 X = NewCodeEntry (OP65_ORA, AM65_ZP, "tmp1", 0, L[0]->LI);
1403 CS_InsertEntry (S, X, I+3);
1406 /* Delete the subroutine call */
1407 CS_DelEntry (S, I+1);
1409 /* Invert the branch */
1410 CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
1412 /* Remember, we had changes */
1422 /* Return the number of changes made */
1428 /*****************************************************************************/
1429 /* Optimize stores through pointers */
1430 /*****************************************************************************/
1434 static unsigned OptPtrStore1 (CodeSeg* S)
1435 /* Search for the sequence:
1442 * and replace it by:
1451 unsigned Changes = 0;
1453 /* Walk over the entries */
1455 while (I < CS_GetEntryCount (S)) {
1459 /* Get next entry */
1460 L[0] = CS_GetEntry (S, I);
1462 /* Check for the sequence */
1463 if (L[0]->OPC == OP65_JSR &&
1464 strcmp (L[0]->Arg, "pushax") == 0 &&
1465 CS_GetEntries (S, L+1, I+1, 3) &&
1466 L[1]->OPC == OP65_LDA &&
1467 !CE_HasLabel (L[1]) &&
1468 L[2]->OPC == OP65_LDY &&
1469 !CE_HasLabel (L[2]) &&
1470 L[3]->OPC == OP65_JSR &&
1471 strcmp (L[3]->Arg, "staspidx") == 0 &&
1472 !CE_HasLabel (L[3])) {
1476 /* Create and insert the stores */
1477 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
1478 CS_InsertEntry (S, X, I+1);
1480 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
1481 CS_InsertEntry (S, X, I+2);
1483 /* Delete the call to pushax */
1486 /* Insert the store through ptr1 */
1487 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "ptr1", 0, L[3]->LI);
1488 CS_InsertEntry (S, X, I+4);
1490 /* Delete the call to staspidx */
1491 CS_DelEntry (S, I+5);
1493 /* Remember, we had changes */
1503 /* Return the number of changes made */
1509 /*****************************************************************************/
1510 /* Optimize loads through pointers */
1511 /*****************************************************************************/
1515 static unsigned OptPtrLoad1 (CodeSeg* S)
1516 /* Search for the sequence:
1520 * lda (sp),y # May be any destination
1524 * and replace it by:
1535 unsigned Changes = 0;
1537 /* Walk over the entries */
1539 while (I < CS_GetEntryCount (S)) {
1543 /* Get next entry */
1544 L[0] = CS_GetEntry (S, I);
1546 /* Check for the sequence */
1547 if (L[0]->OPC == OP65_TAX &&
1548 CS_GetEntries (S, L+1, I+1, 4) &&
1549 L[1]->OPC == OP65_DEY &&
1550 !CE_HasLabel (L[1]) &&
1551 L[2]->OPC == OP65_LDA &&
1552 !CE_HasLabel (L[2]) &&
1553 L[3]->OPC == OP65_LDY &&
1554 !CE_HasLabel (L[3]) &&
1555 L[4]->OPC == OP65_JSR &&
1556 strcmp (L[4]->Arg, "ldauidx") == 0 &&
1557 !CE_HasLabel (L[4])) {
1561 /* Store the high byte and remove the TAX instead */
1562 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1+1", 0, L[0]->LI);
1563 CS_InsertEntry (S, X, I+1);
1566 /* Store the low byte */
1567 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[2]->LI);
1568 CS_InsertEntry (S, X, I+3);
1570 /* Delete the call to ldauidx */
1571 CS_DelEntry (S, I+5);
1573 /* Load high and low byte */
1574 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[3]->LI);
1575 CS_InsertEntry (S, X, I+5);
1576 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[3]->LI);
1577 CS_InsertEntry (S, X, I+6);
1579 /* Remember, we had changes */
1589 /* Return the number of changes made */
1595 static unsigned OptPtrLoad2 (CodeSeg* S)
1596 /* Search for the sequence
1601 * and replace it by:
1609 * This step must be execute *after* OptPtrLoad1!
1612 unsigned Changes = 0;
1614 /* Walk over the entries */
1616 while (I < CS_GetEntryCount (S)) {
1620 /* Get next entry */
1621 L[0] = CS_GetEntry (S, I);
1623 /* Check for the sequence */
1624 if (L[0]->OPC == OP65_LDY &&
1625 CS_GetEntries (S, L+1, I+1, 1) &&
1626 L[1]->OPC == OP65_JSR &&
1627 strcmp (L[1]->Arg, "ldauidx") == 0 &&
1628 !CE_HasLabel (L[1])) {
1632 /* Store the high byte */
1633 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
1634 CS_InsertEntry (S, X, I+1);
1636 /* Store the low byte */
1637 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
1638 CS_InsertEntry (S, X, I+2);
1640 /* Delete the call to ldauidx */
1641 CS_DelEntry (S, I+3);
1643 /* Load the high and low byte */
1644 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[0]->LI);
1645 CS_InsertEntry (S, X, I+3);
1646 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[0]->LI);
1647 CS_InsertEntry (S, X, I+4);
1649 /* Remember, we had changes */
1659 /* Return the number of changes made */
1665 /*****************************************************************************/
1667 /*****************************************************************************/
1671 /* Types of optimization steps */
1673 optPre, /* Repeated once */
1674 optPreMain, /* Repeated more than once */
1676 optPostMain, /* dito */
1677 optPost /* Repeated once */
1680 /* Table with all the optimization functions */
1681 typedef struct OptFunc OptFunc;
1683 unsigned (*Func) (CodeSeg*); /* Optimizer function */
1684 const char* Name; /* Name of optimizer step */
1685 unsigned char Type; /* Type of this step */
1686 char Disabled; /* True if pass disabled */
1689 /* Macro that builds a table entry */
1690 #define OptEntry(func,type) { func, #func, type, 0 }
1692 /* Table with optimizer steps */
1693 static OptFunc OptFuncs [] = {
1694 /* Optimizes stores through pointers */
1695 OptEntry (OptPtrStore1, optPre),
1696 /* Optimize loads through pointers */
1697 OptEntry (OptPtrLoad1, optMain),
1698 OptEntry (OptPtrLoad2, optMain),
1699 /* Optimize calls to nega */
1700 OptEntry (OptNegA1, optPre),
1701 OptEntry (OptNegA2, optPre),
1702 /* Optimize calls to negax */
1703 OptEntry (OptNegAX1, optPre),
1704 OptEntry (OptNegAX2, optPre),
1705 OptEntry (OptNegAX3, optPre),
1706 OptEntry (OptNegAX4, optPre),
1707 /* Optimize subtractions */
1708 OptEntry (OptSub1, optMain),
1709 OptEntry (OptSub2, optMain),
1710 /* Optimize additions */
1711 OptEntry (OptAdd1, optMain),
1712 /* Optimize jump cascades */
1713 OptEntry (OptJumpCascades, optMain),
1714 /* Remove dead jumps */
1715 OptEntry (OptDeadJumps, optMain),
1716 /* Change jsr/rts to jmp */
1717 OptEntry (OptRTS, optMain),
1718 /* Remove dead code */
1719 OptEntry (OptDeadCode, optMain),
1720 /* Optimize jump targets */
1721 OptEntry (OptJumpTarget, optMain),
1722 /* Optimize conditional branches */
1723 OptEntry (OptCondBranches, optMain),
1724 /* Replace jumps to RTS by RTS */
1725 OptEntry (OptRTSJumps, optMain),
1726 /* Remove calls to the bool transformer subroutines */
1727 OptEntry (OptBoolTransforms, optMain),
1728 /* Optimize compares */
1729 OptEntry (OptCmp1, optMain),
1730 OptEntry (OptCmp2, optMain),
1731 OptEntry (OptCmp3, optMain),
1732 OptEntry (OptCmp4, optMain),
1733 OptEntry (OptCmp5, optMain),
1734 OptEntry (OptCmp6, optMain),
1735 /* Optimize tests */
1736 OptEntry (OptTest1, optMain),
1737 /* Remove unused loads */
1738 OptEntry (OptUnusedLoads, optMain),
1739 OptEntry (OptDuplicateLoads, optMain),
1740 OptEntry (OptStoreLoad, optMain),
1741 /* Optimize branch distance */
1742 OptEntry (OptBranchDist, optMain),
1747 static OptFunc* FindOptStep (const char* Name)
1748 /* Find an optimizer step by name in the table and return a pointer. Print an
1749 * error and call AbEnd if not found.
1754 /* Run all optimization steps */
1755 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
1756 if (strcmp (OptFuncs[I].Name, Name) == 0) {
1763 AbEnd ("Optimization step `%s' not found", Name);
1769 void DisableOpt (const char* Name)
1770 /* Disable the optimization with the given name */
1772 if (strcmp (Name, "any") == 0) {
1774 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
1775 OptFuncs[I].Disabled = 1;
1778 OptFunc* F = FindOptStep (Name);
1785 void EnableOpt (const char* Name)
1786 /* Enable the optimization with the given name */
1788 if (strcmp (Name, "any") == 0) {
1790 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
1791 OptFuncs[I].Disabled = 0;
1794 OptFunc* F = FindOptStep (Name);
1801 void ListOptSteps (FILE* F)
1802 /* List all optimization steps */
1805 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
1806 fprintf (F, "%s\n", OptFuncs[I].Name);
1812 static void RepeatOptStep (CodeSeg* S, unsigned char Type, unsigned Max)
1813 /* Repeat the optimizer step of type Type at may Max times */
1817 unsigned OptChanges;
1819 /* Repeat max times of until there are no more changes */
1821 /* Reset the number of changes */
1824 /* Keep the user hapy */
1825 Print (stdout, 1, " Optimizer pass %u:\n", ++Pass);
1827 /* Run all optimization steps */
1828 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
1830 /* Get the table entry */
1831 const OptFunc* F = OptFuncs + I;
1833 /* Check if the type matches */
1834 if (F->Type != Type) {
1839 /* Print the name of the following optimizer step */
1840 Print (stdout, 1, " %s:%*s", F->Name, (int) (30-strlen(F->Name)), "");
1842 /* Check if the step is disabled */
1844 Print (stdout, 1, "Disabled\n");
1846 unsigned Changes = F->Func (S);
1847 OptChanges += Changes;
1848 Print (stdout, 1, "%u Changes\n", Changes);
1852 } while (--Max > 0 && OptChanges > 0);
1857 void RunOpt (CodeSeg* S)
1858 /* Run the optimizer */
1861 /* If we shouldn't run the optimizer, bail out */
1866 /* Print the name of the function we are working on */
1868 Print (stdout, 1, "Running optimizer for function `%s'\n", S->Func->Name);
1870 Print (stdout, 1, "Running optimizer for global code segment\n");
1873 /* Repeat all steps until there are no more changes */
1874 RepeatOptStep (S, optPre, 1);
1875 RepeatOptStep (S, optPreMain, 0xFFFF);
1876 RepeatOptStep (S, optMain, 0xFFFF);
1877 RepeatOptStep (S, optPostMain, 0xFFFF);
1878 RepeatOptStep (S, optPost, 1);