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
561 unsigned Changes = 0;
563 /* Walk over the entries */
565 while (I < CS_GetEntryCount (S)) {
570 CodeEntry* E = CS_GetEntry (S, I);
572 /* Check for the sequence */
573 if (E->OPC == OP65_JSR &&
574 strcmp (E->Arg, "pushax") == 0 &&
575 CS_GetEntries (S, L, I+1, 5) &&
576 L[0]->OPC == OP65_LDY &&
577 !CE_HasLabel (L[0]) &&
578 L[1]->OPC == OP65_LDX &&
579 CE_KnownImm (L[1]) &&
581 !CE_HasLabel (L[1]) &&
582 L[2]->OPC == OP65_LDA &&
583 !CE_HasLabel (L[2]) &&
584 L[3]->OPC == OP65_JSR &&
585 strcmp (L[3]->Arg, "tosaddax") == 0 &&
586 !CE_HasLabel (L[3])) {
591 /* Remove the call to pushax */
595 X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, L[3]->LI);
596 CS_InsertEntry (S, X, I+1);
598 /* Remove the load */
599 CS_DelEntry (S, I+3); /* lda */
600 CS_DelEntry (S, I+2); /* ldx */
603 X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp", 0, L[3]->LI);
604 CS_InsertEntry (S, X, I+2);
606 /* Generate the branch label and the branch */
607 Label = CS_GenLabel (S, L[4]);
608 X = NewCodeEntry (OP65_BCC, AM65_BRA, Label->Name, Label, L[3]->LI);
609 CS_InsertEntry (S, X, I+3);
611 /* Generate the increment of the high byte */
612 X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, L[3]->LI);
613 CS_InsertEntry (S, X, I+4);
615 /* Delete the call to tosaddax */
616 CS_DelEntry (S, I+5);
618 /* Remember, we had changes */
628 /* Return the number of changes made */
634 static unsigned OptAdd2 (CodeSeg* S)
635 /* Search for the sequence
642 * and remove the handling of the high byte if X is not used later.
645 unsigned Changes = 0;
647 /* Walk over the entries */
649 while (I < CS_GetEntryCount (S)) {
654 CodeEntry* E = CS_GetEntry (S, I);
656 /* Check for the sequence */
657 if (E->OPC == OP65_ADC &&
658 CS_GetEntries (S, L, I+1, 3) &&
659 (L[0]->OPC == OP65_BCC || L[0]->OPC == OP65_JCC) &&
661 !CE_HasLabel (L[0]) &&
662 L[1]->OPC == OP65_INX &&
663 !CE_HasLabel (L[1]) &&
664 L[0]->JumpTo->Owner == L[2] &&
665 !RegXUsed (S, I+3)) {
667 /* Remove the bcs/dex */
668 CS_DelEntries (S, I+1, 2);
670 /* Remember, we had changes */
680 /* Return the number of changes made */
686 /*****************************************************************************/
687 /* Optimizations for compares */
688 /*****************************************************************************/
692 static unsigned OptCmp1 (CodeSeg* S)
693 /* Search for the sequence
705 unsigned Changes = 0;
707 /* Walk over the entries */
709 while (I < CS_GetEntryCount (S)) {
714 CodeEntry* E = CS_GetEntry (S, I);
716 /* Check for the sequence */
717 if (E->OPC == OP65_STX &&
718 CS_GetEntries (S, L, I+1, 2) &&
719 L[0]->OPC == OP65_STX &&
720 strcmp (L[0]->Arg, "tmp1") == 0 &&
721 !CE_HasLabel (L[0]) &&
722 L[1]->OPC == OP65_ORA &&
723 strcmp (L[1]->Arg, "tmp1") == 0 &&
724 !CE_HasLabel (L[1])) {
726 /* Remove the remaining instructions */
727 CS_DelEntries (S, I+1, 2);
729 /* Insert the ora instead */
730 CS_InsertEntry (S, NewCodeEntry (OP65_ORA, E->AM, E->Arg, 0, E->LI), I+1);
732 /* Remember, we had changes */
742 /* Return the number of changes made */
748 static unsigned OptCmp2 (CodeSeg* S)
751 * lda/and/ora/eor ...
755 * and remove the cmp.
758 unsigned Changes = 0;
760 /* Walk over the entries */
762 while (I < CS_GetEntryCount (S)) {
767 CodeEntry* E = CS_GetEntry (S, I);
769 /* Check for the sequence */
770 if ((E->OPC == OP65_ADC ||
771 E->OPC == OP65_AND ||
772 E->OPC == OP65_DEA ||
773 E->OPC == OP65_EOR ||
774 E->OPC == OP65_INA ||
775 E->OPC == OP65_LDA ||
776 E->OPC == OP65_ORA ||
777 E->OPC == OP65_PLA ||
778 E->OPC == OP65_SBC ||
779 E->OPC == OP65_TXA ||
780 E->OPC == OP65_TYA) &&
781 CS_GetEntries (S, L, I+1, 2) &&
782 IsCmpToZero (L[0]) &&
783 !CE_HasLabel (L[0]) &&
784 (L[1]->Info & OF_FBRA) != 0 &&
785 !CE_HasLabel (L[1])) {
787 /* Remove the compare */
788 CS_DelEntry (S, I+1);
790 /* Remember, we had changes */
800 /* Return the number of changes made */
806 static unsigned OptCmp3 (CodeSeg* S)
816 * If a is zero, we may remove the compare. If a and b are both zero, we may
817 * replace it by the sequence
823 * L1 may be either the label at the branch instruction, or the target label
824 * of this instruction.
827 unsigned Changes = 0;
829 /* Walk over the entries */
831 while (I < CS_GetEntryCount (S)) {
836 CodeEntry* E = CS_GetEntry (S, I);
838 /* Check for the sequence */
839 if (E->OPC == OP65_LDA &&
840 CS_GetEntries (S, L, I+1, 5) &&
841 L[0]->OPC == OP65_LDX &&
842 !CE_HasLabel (L[0]) &&
843 IsImmCmp16 (S, L+1)) {
845 if (L[1]->Num == 0 && L[3]->Num == 0) {
846 /* The value is zero, we may use the simple code version. */
847 CE_ReplaceOPC (L[0], OP65_ORA);
848 CS_DelEntries (S, I+2, 3);
850 /* Move the lda instruction after the first branch. This will
851 * improve speed, since the load is delayed after the first
854 CS_MoveEntry (S, I, I+4);
856 /* We will replace the ldx/cpx by lda/cmp */
857 CE_ReplaceOPC (L[0], OP65_LDA);
858 CE_ReplaceOPC (L[1], OP65_CMP);
860 /* Beware: If the first LDA instruction had a label, we have
861 * to move this label to the top of the sequence again.
863 if (CE_HasLabel (E)) {
864 CS_MoveLabels (S, E, L[0]);
877 /* Return the number of changes made */
883 static unsigned OptCmp4 (CodeSeg* S)
884 /* Optimize compares of local variables:
897 unsigned Changes = 0;
899 /* Walk over the entries */
901 while (I < CS_GetEntryCount (S)) {
905 /* Check for the sequence */
906 if (IsLocalLoad16 (S, I, L, 9) && IsImmCmp16 (S, L+5)) {
908 if (L[5]->Num == 0 && L[7]->Num == 0) {
910 /* The value is zero, we may use the simple code version:
917 CE_ReplaceOPC (L[4], OP65_ORA);
918 CS_DelEntries (S, I+5, 3); /* cpx/bne/cmp */
919 CS_DelEntry (S, I+2); /* tax */
923 /* Change the code to just use the A register. Move the load
924 * of the low byte after the first branch if possible:
935 CS_DelEntry (S, I+2); /* tax */
936 CE_ReplaceOPC (L[5], OP65_CMP); /* cpx -> cmp */
937 CS_MoveEntry (S, I+4, I+2); /* cmp */
938 CS_MoveEntry (S, I+5, I+3); /* bne */
950 /* Return the number of changes made */
956 static unsigned OptCmp5 (CodeSeg* S)
957 /* Search for calls to compare subroutines followed by a conditional branch
958 * and replace them by cheaper versions, since the branch means that the
959 * boolean value returned by these routines is not needed (we may also check
960 * that explicitly, but for the current code generator it is always true).
963 unsigned Changes = 0;
965 /* Walk over the entries */
967 while (I < CS_GetEntryCount (S)) {
973 CodeEntry* E = CS_GetEntry (S, I);
975 /* Check for the sequence */
976 if (E->OPC == OP65_JSR &&
977 (Cond = FindTosCmpCond (E->Arg)) != CMP_INV &&
978 (N = CS_GetNextEntry (S, I)) != 0 &&
979 (N->Info & OF_ZBRA) != 0 &&
982 /* The tos... functions will return a boolean value in a/x and
983 * the Z flag says if this value is zero or not. We will call
984 * a cheaper subroutine instead, one that does not return a
985 * boolean value but only valid flags. Note: jeq jumps if
986 * the condition is not met, jne jumps if the condition is met.
987 * Invert the code if we jump on condition not met.
989 if (GetBranchCond (N->OPC) == BC_EQ) {
990 /* Jumps if condition false, invert condition */
991 Cond = CmpInvertTab [Cond];
994 /* Replace the subroutine call. */
995 E = NewCodeEntry (OP65_JSR, AM65_ABS, "tosicmp", 0, E->LI);
996 CS_InsertEntry (S, E, I+1);
999 /* Replace the conditional branch */
1000 ReplaceCmp (S, I+1, Cond);
1002 /* Remember, we had changes */
1012 /* Return the number of changes made */
1018 static unsigned OptCmp6 (CodeSeg* S)
1019 /* Search for a sequence ldx/txa/branch and remove the txa if A is not
1023 unsigned Changes = 0;
1025 /* Walk over the entries */
1027 while (I < CS_GetEntryCount (S)) {
1031 /* Get next entry */
1032 CodeEntry* E = CS_GetEntry (S, I);
1034 /* Check for the sequence */
1035 if ((E->OPC == OP65_LDX) &&
1036 CS_GetEntries (S, L, I+1, 2) &&
1037 L[0]->OPC == OP65_TXA &&
1038 !CE_HasLabel (L[0]) &&
1039 (L[1]->Info & OF_FBRA) != 0 &&
1040 !CE_HasLabel (L[1]) &&
1041 !RegAUsed (S, I+3)) {
1043 /* Remove the txa */
1044 CS_DelEntry (S, I+1);
1046 /* Remember, we had changes */
1056 /* Return the number of changes made */
1062 /*****************************************************************************/
1063 /* Optimize tests */
1064 /*****************************************************************************/
1068 static unsigned OptTest1 (CodeSeg* S)
1075 * if X is zero, the sequence may be changed to
1080 * which may be optimized further by another step.
1082 * If A is zero, the sequence may be changed to
1089 unsigned Changes = 0;
1092 /* Generate register info for this step */
1095 /* Walk over the entries */
1097 while (I < CS_GetEntryCount (S)) {
1101 /* Get next entry */
1102 L[0] = CS_GetEntry (S, I);
1104 /* Check if it's the sequence we're searching for */
1105 if (L[0]->OPC == OP65_STX &&
1106 CS_GetEntries (S, L+1, I+1, 2) &&
1107 !CE_HasLabel (L[1]) &&
1108 L[1]->OPC == OP65_ORA &&
1109 strcmp (L[0]->Arg, L[1]->Arg) == 0 &&
1110 !CE_HasLabel (L[2]) &&
1111 (L[2]->Info & OF_ZBRA) != 0) {
1113 /* Check if X is zero */
1114 if (L[0]->RI->In.RegX == 0) {
1116 /* Insert the compare */
1117 CodeEntry* N = NewCodeEntry (OP65_CMP, AM65_IMM, "$00", 0, L[0]->LI);
1118 CS_InsertEntry (S, N, I+2);
1120 /* Remove the two other insns */
1121 CS_DelEntry (S, I+1);
1124 /* We had changes */
1127 /* Check if A is zero */
1128 } else if (L[1]->RI->In.RegA == 0) {
1130 /* Insert the txa */
1131 CodeEntry* N = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, L[1]->LI);
1132 CS_InsertEntry (S, N, I+2);
1134 /* Remove the two other insns */
1135 CS_DelEntry (S, I+1);
1138 /* We had changes */
1148 /* Free register info */
1151 /* Return the number of changes made */
1157 /*****************************************************************************/
1158 /* nega optimizations */
1159 /*****************************************************************************/
1163 static unsigned OptNegA1 (CodeSeg* S)
1170 * Remove the ldx if the lda does not use it.
1173 unsigned Changes = 0;
1175 /* Walk over the entries */
1177 while (I < CS_GetEntryCount (S)) {
1181 /* Get next entry */
1182 CodeEntry* E = CS_GetEntry (S, I);
1184 /* Check for a ldx */
1185 if (E->OPC == OP65_LDX &&
1186 E->AM == AM65_IMM &&
1187 (E->Flags & CEF_NUMARG) != 0 &&
1189 CS_GetEntries (S, L, I+1, 2) &&
1190 L[0]->OPC == OP65_LDA &&
1191 (L[0]->Use & REG_X) == 0 &&
1192 !CE_HasLabel (L[0]) &&
1193 L[1]->OPC == OP65_JSR &&
1194 strcmp (L[1]->Arg, "bnega") == 0 &&
1195 !CE_HasLabel (L[1])) {
1197 /* Remove the ldx instruction */
1200 /* Remember, we had changes */
1210 /* Return the number of changes made */
1216 static unsigned OptNegA2 (CodeSeg* S)
1223 * Adjust the conditional branch and remove the call to the subroutine.
1226 unsigned Changes = 0;
1228 /* Walk over the entries */
1230 while (I < CS_GetEntryCount (S)) {
1234 /* Get next entry */
1235 CodeEntry* E = CS_GetEntry (S, I);
1237 /* Check for the sequence */
1238 if ((E->OPC == OP65_ADC ||
1239 E->OPC == OP65_AND ||
1240 E->OPC == OP65_DEA ||
1241 E->OPC == OP65_EOR ||
1242 E->OPC == OP65_INA ||
1243 E->OPC == OP65_LDA ||
1244 E->OPC == OP65_ORA ||
1245 E->OPC == OP65_PLA ||
1246 E->OPC == OP65_SBC ||
1247 E->OPC == OP65_TXA ||
1248 E->OPC == OP65_TYA) &&
1249 CS_GetEntries (S, L, I+1, 2) &&
1250 L[0]->OPC == OP65_JSR &&
1251 strcmp (L[0]->Arg, "bnega") == 0 &&
1252 !CE_HasLabel (L[0]) &&
1253 (L[1]->Info & OF_ZBRA) != 0 &&
1254 !CE_HasLabel (L[1])) {
1256 /* Invert the branch */
1257 CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
1259 /* Delete the subroutine call */
1260 CS_DelEntry (S, I+1);
1262 /* Remember, we had changes */
1272 /* Return the number of changes made */
1278 /*****************************************************************************/
1279 /* negax optimizations */
1280 /*****************************************************************************/
1284 static unsigned OptNegAX1 (CodeSeg* S)
1285 /* On a call to bnegax, if X is zero, the result depends only on the value in
1286 * A, so change the call to a call to bnega. This will get further optimized
1287 * later if possible.
1290 unsigned Changes = 0;
1293 /* Generate register info for this step */
1296 /* Walk over the entries */
1298 while (I < CS_GetEntryCount (S)) {
1300 /* Get next entry */
1301 CodeEntry* E = CS_GetEntry (S, I);
1303 /* Check if this is a call to bnegax, and if X is known and zero */
1304 if (E->OPC == OP65_JSR &&
1305 E->RI->In.RegX == 0 &&
1306 strcmp (E->Arg, "bnegax") == 0) {
1308 /* We're cheating somewhat here ... */
1312 /* We had changes */
1321 /* Free register info */
1324 /* Return the number of changes made */
1330 static unsigned OptNegAX2 (CodeSeg* S)
1331 /* Search for the sequence:
1348 unsigned Changes = 0;
1350 /* Walk over the entries */
1352 while (I < CS_GetEntryCount (S)) {
1356 /* Get next entry */
1357 CodeEntry* E = CS_GetEntry (S, I);
1359 /* Check for the sequence */
1360 if (E->OPC == OP65_LDA &&
1361 E->AM == AM65_ZP_INDY &&
1362 CS_GetEntries (S, L, I+1, 5) &&
1363 L[0]->OPC == OP65_TAX &&
1364 L[1]->OPC == OP65_DEY &&
1365 L[2]->OPC == OP65_LDA &&
1366 L[2]->AM == AM65_ZP_INDY &&
1367 strcmp (L[2]->Arg, E->Arg) == 0 &&
1368 !CE_HasLabel (L[2]) &&
1369 L[3]->OPC == OP65_JSR &&
1370 strcmp (L[3]->Arg, "bnegax") == 0 &&
1371 !CE_HasLabel (L[3]) &&
1372 (L[4]->Info & OF_ZBRA) != 0 &&
1373 !CE_HasLabel (L[4])) {
1376 CE_ReplaceOPC (L[2], OP65_ORA);
1378 /* Invert the branch */
1379 CE_ReplaceOPC (L[4], GetInverseBranch (L[4]->OPC));
1381 /* Delete the entries no longer needed. Beware: Deleting entries
1382 * will change the indices.
1384 CS_DelEntry (S, I+4); /* jsr bnegax */
1385 CS_DelEntry (S, I+1); /* tax */
1387 /* Remember, we had changes */
1397 /* Return the number of changes made */
1403 static unsigned OptNegAX3 (CodeSeg* S)
1404 /* Search for the sequence:
1418 unsigned Changes = 0;
1420 /* Walk over the entries */
1422 while (I < CS_GetEntryCount (S)) {
1426 /* Get next entry */
1427 CodeEntry* E = CS_GetEntry (S, I);
1429 /* Check for the sequence */
1430 if (E->OPC == OP65_LDA &&
1431 CS_GetEntries (S, L, I+1, 3) &&
1432 L[0]->OPC == OP65_LDX &&
1433 !CE_HasLabel (L[0]) &&
1434 L[1]->OPC == OP65_JSR &&
1435 strcmp (L[1]->Arg, "bnegax") == 0 &&
1436 !CE_HasLabel (L[1]) &&
1437 (L[2]->Info & OF_ZBRA) != 0 &&
1438 !CE_HasLabel (L[2])) {
1441 CE_ReplaceOPC (L[0], OP65_ORA);
1443 /* Invert the branch */
1444 CE_ReplaceOPC (L[2], GetInverseBranch (L[2]->OPC));
1446 /* Delete the subroutine call */
1447 CS_DelEntry (S, I+2);
1449 /* Remember, we had changes */
1459 /* Return the number of changes made */
1465 static unsigned OptNegAX4 (CodeSeg* S)
1466 /* Search for the sequence:
1472 * and replace it by:
1479 unsigned Changes = 0;
1481 /* Walk over the entries */
1483 while (I < CS_GetEntryCount (S)) {
1487 /* Get next entry */
1488 CodeEntry* E = CS_GetEntry (S, I);
1490 /* Check for the sequence */
1491 if (E->OPC == OP65_JSR &&
1492 CS_GetEntries (S, L, I+1, 2) &&
1493 L[0]->OPC == OP65_JSR &&
1494 strncmp (L[0]->Arg,"bnega",5) == 0 &&
1495 !CE_HasLabel (L[0]) &&
1496 (L[1]->Info & OF_ZBRA) != 0 &&
1497 !CE_HasLabel (L[1])) {
1501 /* Check if we're calling bnega or bnegax */
1502 int ByteSized = (strcmp (L[0]->Arg, "bnega") == 0);
1504 /* Insert apropriate test code */
1507 X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI);
1508 CS_InsertEntry (S, X, I+2);
1511 X = NewCodeEntry (OP65_STX, AM65_ZP, "tmp1", 0, L[0]->LI);
1512 CS_InsertEntry (S, X, I+2);
1513 X = NewCodeEntry (OP65_ORA, AM65_ZP, "tmp1", 0, L[0]->LI);
1514 CS_InsertEntry (S, X, I+3);
1517 /* Delete the subroutine call */
1518 CS_DelEntry (S, I+1);
1520 /* Invert the branch */
1521 CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
1523 /* Remember, we had changes */
1533 /* Return the number of changes made */
1539 /*****************************************************************************/
1540 /* Optimize stores through pointers */
1541 /*****************************************************************************/
1545 static unsigned OptPtrStore1Sub (CodeSeg* S, unsigned I, CodeEntry** const L)
1546 /* Check if this is one of the allowed suboperation for OptPtrStore1 */
1548 /* Check for a label attached to the entry */
1549 if (CE_HasLabel (L[0])) {
1553 /* Check for single insn sub ops */
1554 if (L[0]->OPC == OP65_AND ||
1555 L[0]->OPC == OP65_EOR ||
1556 L[0]->OPC == OP65_ORA ||
1557 (L[0]->OPC == OP65_JSR && strncmp (L[0]->Arg, "shlax", 5) == 0) ||
1558 (L[0]->OPC == OP65_JSR && strncmp (L[0]->Arg, "shrax", 5) == 0)) {
1563 } else if (L[0]->OPC == OP65_CLC &&
1564 (L[1] = CS_GetNextEntry (S, I)) != 0 &&
1565 L[1]->OPC == OP65_ADC &&
1566 !CE_HasLabel (L[1])) {
1568 } else if (L[0]->OPC == OP65_SEC &&
1569 (L[1] = CS_GetNextEntry (S, I)) != 0 &&
1570 L[1]->OPC == OP65_SBC &&
1571 !CE_HasLabel (L[1])) {
1583 static unsigned OptPtrStore1 (CodeSeg* S)
1584 /* Search for the sequence:
1593 * and replace it by:
1605 unsigned Changes = 0;
1607 /* Walk over the entries */
1609 while (I < CS_GetEntryCount (S)) {
1614 /* Get next entry */
1615 L[0] = CS_GetEntry (S, I);
1617 /* Check for the sequence */
1618 if (L[0]->OPC == OP65_JSR &&
1619 strcmp (L[0]->Arg, "pushax") == 0 &&
1620 CS_GetEntries (S, L+1, I+1, 3) &&
1621 L[1]->OPC == OP65_LDY &&
1622 CE_KnownImm (L[1]) &&
1623 !CE_HasLabel (L[1]) &&
1624 L[2]->OPC == OP65_JSR &&
1625 strcmp (L[2]->Arg, "ldauidx") == 0 &&
1626 !CE_HasLabel (L[2]) &&
1627 (K = OptPtrStore1Sub (S, I+3, L+3)) > 0 &&
1628 CS_GetEntries (S, L+3+K, I+3+K, 2) &&
1629 L[3+K]->OPC == OP65_LDY &&
1630 CE_KnownImm (L[3+K]) &&
1631 !CE_HasLabel (L[3+K]) &&
1632 L[4+K]->OPC == OP65_JSR &&
1633 strcmp (L[4+K]->Arg, "staspidx") == 0 &&
1634 !CE_HasLabel (L[4+K])) {
1638 /* Create and insert the stores */
1639 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
1640 CS_InsertEntry (S, X, I+1);
1642 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
1643 CS_InsertEntry (S, X, I+2);
1645 /* Delete the call to pushax */
1648 /* Delete the call to ldauidx */
1649 CS_DelEntry (S, I+3);
1651 /* Insert the load from ptr1 */
1652 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[3]->LI);
1653 CS_InsertEntry (S, X, I+3);
1654 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[2]->LI);
1655 CS_InsertEntry (S, X, I+4);
1657 /* Insert the store through ptr1 */
1658 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "ptr1", 0, L[3]->LI);
1659 CS_InsertEntry (S, X, I+6+K);
1661 /* Delete the call to staspidx */
1662 CS_DelEntry (S, I+7+K);
1664 /* Remember, we had changes */
1674 /* Return the number of changes made */
1680 static unsigned OptPtrStore2 (CodeSeg* S)
1681 /* Search for the sequence:
1688 * and replace it by:
1697 unsigned Changes = 0;
1699 /* Walk over the entries */
1701 while (I < CS_GetEntryCount (S)) {
1705 /* Get next entry */
1706 L[0] = CS_GetEntry (S, I);
1708 /* Check for the sequence */
1709 if (L[0]->OPC == OP65_JSR &&
1710 strcmp (L[0]->Arg, "pushax") == 0 &&
1711 CS_GetEntries (S, L+1, I+1, 3) &&
1712 L[1]->OPC == OP65_LDA &&
1713 !CE_HasLabel (L[1]) &&
1714 L[2]->OPC == OP65_LDY &&
1715 !CE_HasLabel (L[2]) &&
1716 L[3]->OPC == OP65_JSR &&
1717 strcmp (L[3]->Arg, "staspidx") == 0 &&
1718 !CE_HasLabel (L[3])) {
1722 /* Create and insert the stores */
1723 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
1724 CS_InsertEntry (S, X, I+1);
1726 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
1727 CS_InsertEntry (S, X, I+2);
1729 /* Delete the call to pushax */
1732 /* Insert the store through ptr1 */
1733 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "ptr1", 0, L[3]->LI);
1734 CS_InsertEntry (S, X, I+4);
1736 /* Delete the call to staspidx */
1737 CS_DelEntry (S, I+5);
1739 /* Remember, we had changes */
1749 /* Return the number of changes made */
1755 /*****************************************************************************/
1756 /* Optimize loads through pointers */
1757 /*****************************************************************************/
1761 static unsigned OptPtrLoad1 (CodeSeg* S)
1762 /* Search for the sequence:
1766 * lda (sp),y # May be any destination
1770 * and replace it by:
1781 unsigned Changes = 0;
1783 /* Walk over the entries */
1785 while (I < CS_GetEntryCount (S)) {
1789 /* Get next entry */
1790 L[0] = CS_GetEntry (S, I);
1792 /* Check for the sequence */
1793 if (L[0]->OPC == OP65_TAX &&
1794 CS_GetEntries (S, L+1, I+1, 4) &&
1795 L[1]->OPC == OP65_DEY &&
1796 !CE_HasLabel (L[1]) &&
1797 L[2]->OPC == OP65_LDA &&
1798 !CE_HasLabel (L[2]) &&
1799 L[3]->OPC == OP65_LDY &&
1800 !CE_HasLabel (L[3]) &&
1801 L[4]->OPC == OP65_JSR &&
1802 strcmp (L[4]->Arg, "ldauidx") == 0 &&
1803 !CE_HasLabel (L[4])) {
1807 /* Store the high byte and remove the TAX instead */
1808 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1+1", 0, L[0]->LI);
1809 CS_InsertEntry (S, X, I+1);
1812 /* Store the low byte */
1813 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[2]->LI);
1814 CS_InsertEntry (S, X, I+3);
1816 /* Delete the call to ldauidx */
1817 CS_DelEntry (S, I+5);
1819 /* Load high and low byte */
1820 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[3]->LI);
1821 CS_InsertEntry (S, X, I+5);
1822 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[3]->LI);
1823 CS_InsertEntry (S, X, I+6);
1825 /* Remember, we had changes */
1835 /* Return the number of changes made */
1841 static unsigned OptPtrLoad2 (CodeSeg* S)
1842 /* Search for the sequence
1847 * and replace it by:
1855 * This step must be execute *after* OptPtrLoad1!
1858 unsigned Changes = 0;
1860 /* Walk over the entries */
1862 while (I < CS_GetEntryCount (S)) {
1866 /* Get next entry */
1867 L[0] = CS_GetEntry (S, I);
1869 /* Check for the sequence */
1870 if (L[0]->OPC == OP65_LDY &&
1871 CS_GetEntries (S, L+1, I+1, 1) &&
1872 L[1]->OPC == OP65_JSR &&
1873 strcmp (L[1]->Arg, "ldauidx") == 0 &&
1874 !CE_HasLabel (L[1])) {
1878 /* Store the high byte */
1879 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
1880 CS_InsertEntry (S, X, I+1);
1882 /* Store the low byte */
1883 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
1884 CS_InsertEntry (S, X, I+2);
1886 /* Delete the call to ldauidx */
1887 CS_DelEntry (S, I+3);
1889 /* Load the high and low byte */
1890 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[0]->LI);
1891 CS_InsertEntry (S, X, I+3);
1892 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[0]->LI);
1893 CS_InsertEntry (S, X, I+4);
1895 /* Remember, we had changes */
1905 /* Return the number of changes made */
1911 /*****************************************************************************/
1913 /*****************************************************************************/
1917 /* Types of optimization steps */
1919 optPre, /* Repeated once */
1920 optPreMain, /* Repeated more than once */
1922 optPostMain, /* dito */
1923 optPost /* Repeated once */
1926 /* Table with all the optimization functions */
1927 typedef struct OptFunc OptFunc;
1929 unsigned (*Func) (CodeSeg*); /* Optimizer function */
1930 const char* Name; /* Name of optimizer step */
1931 unsigned char Type; /* Type of this step */
1932 char Disabled; /* True if pass disabled */
1935 /* Macro that builds a table entry */
1936 #define OptEntry(func,type) { func, #func, type, 0 }
1938 /* Table with optimizer steps */
1939 static OptFunc OptFuncs [] = {
1940 /* Optimizes stores through pointers */
1941 OptEntry (OptPtrStore1, optPre),
1942 OptEntry (OptPtrStore2, optPre),
1943 /* Optimize loads through pointers */
1944 OptEntry (OptPtrLoad1, optMain),
1945 OptEntry (OptPtrLoad2, optMain),
1946 /* Optimize calls to nega */
1947 OptEntry (OptNegA1, optMain),
1948 OptEntry (OptNegA2, optMain),
1949 /* Optimize calls to negax */
1950 OptEntry (OptNegAX1, optPre),
1951 OptEntry (OptNegAX2, optPre),
1952 OptEntry (OptNegAX3, optPre),
1953 OptEntry (OptNegAX4, optPre),
1954 /* Optimize subtractions */
1955 OptEntry (OptSub1, optMain),
1956 OptEntry (OptSub2, optMain),
1957 /* Optimize additions */
1958 OptEntry (OptAdd1, optPre),
1959 OptEntry (OptAdd2, optMain),
1960 /* Optimize jump cascades */
1961 OptEntry (OptJumpCascades, optMain),
1962 /* Remove dead jumps */
1963 OptEntry (OptDeadJumps, optMain),
1964 /* Change jsr/rts to jmp */
1965 OptEntry (OptRTS, optMain),
1966 /* Remove dead code */
1967 OptEntry (OptDeadCode, optMain),
1968 /* Optimize jump targets */
1969 OptEntry (OptJumpTarget, optMain),
1970 /* Optimize conditional branches */
1971 OptEntry (OptCondBranches, optMain),
1972 /* Replace jumps to RTS by RTS */
1973 OptEntry (OptRTSJumps, optMain),
1974 /* Remove calls to the bool transformer subroutines */
1975 OptEntry (OptBoolTransforms, optMain),
1976 /* Optimize compares */
1977 OptEntry (OptCmp1, optMain),
1978 OptEntry (OptCmp2, optMain),
1979 OptEntry (OptCmp3, optMain),
1980 OptEntry (OptCmp4, optMain),
1981 OptEntry (OptCmp5, optMain),
1982 OptEntry (OptCmp6, optMain),
1983 /* Optimize tests */
1984 OptEntry (OptTest1, optMain),
1985 /* Remove unused loads */
1986 OptEntry (OptUnusedLoads, optMain),
1987 OptEntry (OptDuplicateLoads, optMain),
1988 OptEntry (OptStoreLoad, optMain),
1989 OptEntry (OptTransfers, optMain),
1990 /* Optimize branch distance */
1991 OptEntry (OptBranchDist, optPost),
1996 static OptFunc* FindOptStep (const char* Name)
1997 /* Find an optimizer step by name in the table and return a pointer. Print an
1998 * error and call AbEnd if not found.
2003 /* Run all optimization steps */
2004 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2005 if (strcmp (OptFuncs[I].Name, Name) == 0) {
2012 AbEnd ("Optimization step `%s' not found", Name);
2018 void DisableOpt (const char* Name)
2019 /* Disable the optimization with the given name */
2021 if (strcmp (Name, "any") == 0) {
2023 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2024 OptFuncs[I].Disabled = 1;
2027 OptFunc* F = FindOptStep (Name);
2034 void EnableOpt (const char* Name)
2035 /* Enable the optimization with the given name */
2037 if (strcmp (Name, "any") == 0) {
2039 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2040 OptFuncs[I].Disabled = 0;
2043 OptFunc* F = FindOptStep (Name);
2050 void ListOptSteps (FILE* F)
2051 /* List all optimization steps */
2054 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2055 fprintf (F, "%s\n", OptFuncs[I].Name);
2061 static void RepeatOptStep (CodeSeg* S, unsigned char Type, unsigned Max)
2062 /* Repeat the optimizer step of type Type at may Max times */
2066 unsigned OptChanges;
2068 /* Repeat max times of until there are no more changes */
2070 /* Reset the number of changes */
2073 /* Keep the user hapy */
2074 Print (stdout, 1, " Optimizer pass %u:\n", ++Pass);
2076 /* Run all optimization steps */
2077 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2079 /* Get the table entry */
2080 const OptFunc* F = OptFuncs + I;
2082 /* Check if the type matches */
2083 if (F->Type != Type) {
2088 /* Print the name of the following optimizer step */
2089 Print (stdout, 1, " %s:%*s", F->Name, (int) (30-strlen(F->Name)), "");
2091 /* Check if the step is disabled */
2093 Print (stdout, 1, "Disabled\n");
2095 unsigned Changes = F->Func (S);
2096 OptChanges += Changes;
2097 Print (stdout, 1, "%u Changes\n", Changes);
2101 } while (--Max > 0 && OptChanges > 0);
2106 void RunOpt (CodeSeg* S)
2107 /* Run the optimizer */
2110 /* If we shouldn't run the optimizer, bail out */
2115 /* Print the name of the function we are working on */
2117 Print (stdout, 1, "Running optimizer for function `%s'\n", S->Func->Name);
2119 Print (stdout, 1, "Running optimizer for global code segment\n");
2122 /* Repeat all steps until there are no more changes */
2123 RepeatOptStep (S, optPre, 1);
2124 RepeatOptStep (S, optPreMain, 0xFFFF);
2125 RepeatOptStep (S, optMain, 0xFFFF);
2126 RepeatOptStep (S, optPostMain, 0xFFFF);
2127 RepeatOptStep (S, optPost, 1);