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 GetCmpRegVal (const CodeEntry* E)
260 /* Return the register value for an immediate compare */
263 case OP65_CMP: return E->RI->In.RegA;
264 case OP65_CPX: return E->RI->In.RegX;
265 case OP65_CPY: return E->RI->In.RegY;
266 default: Internal ("Invalid opcode in GetCmpRegVal");
267 return 0; /* Not reached */
273 static int IsCmpToZero (const CodeEntry* E)
274 /* Check if the given instrcuction is a compare to zero instruction */
276 return (E->OPC == OP65_CMP &&
278 (E->Flags & CEF_NUMARG) != 0 &&
284 static int IsSpLoad (const CodeEntry* E)
285 /* Return true if this is the load of A from the stack */
287 return E->OPC == OP65_LDA && E->AM == AM65_ZP_INDY && strcmp (E->Arg, "sp") == 0;
292 static int IsLocalLoad16 (CodeSeg* S, unsigned Index,
293 CodeEntry** L, unsigned Count)
294 /* Check if a 16 bit load of a local variable follows:
302 * If so, read Count entries following the first ldy into L and return true
303 * if this is possible. Otherwise return false.
306 /* Be sure we read enough entries for the check */
309 /* Read the first entry */
310 L[0] = CS_GetEntry (S, Index);
312 /* Check for the sequence */
313 return (L[0]->OPC == OP65_LDY &&
314 L[0]->AM == AM65_IMM &&
315 (L[0]->Flags & CEF_NUMARG) != 0 &&
316 CS_GetEntries (S, L+1, Index+1, Count-1) &&
318 !CE_HasLabel (L[1]) &&
319 L[2]->OPC == OP65_TAX &&
320 !CE_HasLabel (L[2]) &&
321 L[3]->OPC == OP65_DEY &&
322 !CE_HasLabel (L[3]) &&
324 !CE_HasLabel (L[4]));
329 static int IsImmCmp16 (CodeSeg* S, CodeEntry** L)
330 /* Check if the instructions at L are an immidiate compare of a/x:
335 return (L[0]->OPC == OP65_CPX &&
336 L[0]->AM == AM65_IMM &&
337 (L[0]->Flags & CEF_NUMARG) != 0 &&
338 !CE_HasLabel (L[0]) &&
339 (L[1]->OPC == OP65_JNE || L[1]->OPC == OP65_BNE) &&
341 !CE_HasLabel (L[1]) &&
342 L[2]->OPC == OP65_CMP &&
343 L[2]->AM == AM65_IMM &&
344 (L[2]->Flags & CEF_NUMARG) != 0 &&
345 (L[3]->Info & OF_ZBRA) != 0 &&
347 (L[1]->JumpTo->Owner == L[3] || L[1]->JumpTo == L[3]->JumpTo));
352 /*****************************************************************************/
353 /* Remove calls to the bool transformer subroutines */
354 /*****************************************************************************/
358 static unsigned OptBoolTransforms (CodeSeg* S)
359 /* Try to remove the call to boolean transformer routines where the call is
363 unsigned Changes = 0;
365 /* Walk over the entries */
367 while (I < CS_GetEntryCount (S)) {
373 CodeEntry* E = CS_GetEntry (S, I);
375 /* Check for a boolean transformer */
376 if (E->OPC == OP65_JSR &&
377 (Cond = FindBoolCmpCond (E->Arg)) != CMP_INV &&
378 (N = CS_GetNextEntry (S, I)) != 0 &&
379 (N->Info & OF_ZBRA) != 0) {
381 /* Make the boolean transformer unnecessary by changing the
382 * the conditional jump to evaluate the condition flags that
383 * are set after the compare directly. Note: jeq jumps if
384 * the condition is not met, jne jumps if the condition is met.
385 * Invert the code if we jump on condition not met.
387 if (GetBranchCond (N->OPC) == BC_EQ) {
388 /* Jumps if condition false, invert condition */
389 Cond = CmpInvertTab [Cond];
392 /* Check if we can replace the code by something better */
393 ReplaceCmp (S, I+1, Cond);
395 /* Remove the call to the bool transformer */
398 /* Remember, we had changes */
408 /* Return the number of changes made */
414 /*****************************************************************************/
415 /* Optimize subtractions */
416 /*****************************************************************************/
420 static unsigned OptSub1 (CodeSeg* S)
421 /* Search for the sequence
428 * and remove the handling of the high byte if X is not used later.
431 unsigned Changes = 0;
433 /* Walk over the entries */
435 while (I < CS_GetEntryCount (S)) {
440 CodeEntry* E = CS_GetEntry (S, I);
442 /* Check for the sequence */
443 if (E->OPC == OP65_SBC &&
444 CS_GetEntries (S, L, I+1, 3) &&
445 (L[0]->OPC == OP65_BCS || L[0]->OPC == OP65_JCS) &&
447 !CE_HasLabel (L[0]) &&
448 L[1]->OPC == OP65_DEX &&
449 !CE_HasLabel (L[1]) &&
450 L[0]->JumpTo->Owner == L[2] &&
451 !RegXUsed (S, I+3)) {
453 /* Remove the bcs/dex */
454 CS_DelEntries (S, I+1, 2);
456 /* Remember, we had changes */
466 /* Return the number of changes made */
472 static unsigned OptSub2 (CodeSeg* S)
473 /* Search for the sequence
490 unsigned Changes = 0;
492 /* Walk over the entries */
494 while (I < CS_GetEntryCount (S)) {
499 CodeEntry* E = CS_GetEntry (S, I);
501 /* Check for the sequence */
502 if (E->OPC == OP65_LDA &&
503 CS_GetEntries (S, L, I+1, 5) &&
504 L[0]->OPC == OP65_SEC &&
505 !CE_HasLabel (L[0]) &&
506 L[1]->OPC == OP65_STA &&
507 strcmp (L[1]->Arg, "tmp1") == 0 &&
508 !CE_HasLabel (L[1]) &&
509 L[2]->OPC == OP65_LDA &&
510 !CE_HasLabel (L[2]) &&
511 L[3]->OPC == OP65_SBC &&
512 strcmp (L[3]->Arg, "tmp1") == 0 &&
513 !CE_HasLabel (L[3]) &&
514 L[4]->OPC == OP65_STA &&
515 strcmp (L[4]->Arg, L[2]->Arg) == 0 &&
516 !CE_HasLabel (L[4])) {
518 /* Remove the store to tmp1 */
519 CS_DelEntry (S, I+2);
521 /* Remove the subtraction */
522 CS_DelEntry (S, I+3);
524 /* Move the lda to the position of the subtraction and change the
527 CS_MoveEntry (S, I, I+3);
528 CE_ReplaceOPC (E, OP65_SBC);
530 /* If the sequence head had a label, move this label back to the
533 if (CE_HasLabel (E)) {
534 CS_MoveLabels (S, E, L[0]);
537 /* Remember, we had changes */
547 /* Return the number of changes made */
553 /*****************************************************************************/
554 /* Optimize additions */
555 /*****************************************************************************/
559 static unsigned OptAdd1 (CodeSeg* S)
560 /* Search for the sequence
578 unsigned Changes = 0;
580 /* Walk over the entries */
582 while (I < CS_GetEntryCount (S)) {
587 CodeEntry* E = CS_GetEntry (S, I);
589 /* Check for the sequence */
590 if (E->OPC == OP65_JSR &&
591 strcmp (E->Arg, "pushax") == 0 &&
592 CS_GetEntries (S, L, I+1, 5) &&
593 L[0]->OPC == OP65_LDY &&
594 CE_KnownImm (L[0]) &&
595 !CE_HasLabel (L[0]) &&
596 L[1]->OPC == OP65_LDX &&
597 CE_KnownImm (L[1]) &&
599 !CE_HasLabel (L[1]) &&
600 L[2]->OPC == OP65_LDA &&
601 !CE_HasLabel (L[2]) &&
602 L[3]->OPC == OP65_JSR &&
603 strcmp (L[3]->Arg, "tosaddax") == 0 &&
604 !CE_HasLabel (L[3])) {
609 /* Remove the call to pushax */
612 /* Correct the stack offset (needed since pushax was removed) */
613 CE_SetNumArg (L[0], L[0]->Num - 2);
616 X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, L[3]->LI);
617 CS_InsertEntry (S, X, I+1);
619 /* Remove the load */
620 CS_DelEntry (S, I+3); /* lda */
621 CS_DelEntry (S, I+2); /* ldx */
624 X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp", 0, L[3]->LI);
625 CS_InsertEntry (S, X, I+2);
627 /* Generate the branch label and the branch */
628 Label = CS_GenLabel (S, L[4]);
629 X = NewCodeEntry (OP65_BCC, AM65_BRA, Label->Name, Label, L[3]->LI);
630 CS_InsertEntry (S, X, I+3);
632 /* Generate the increment of the high byte */
633 X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, L[3]->LI);
634 CS_InsertEntry (S, X, I+4);
636 /* Delete the call to tosaddax */
637 CS_DelEntry (S, I+5);
639 /* Remember, we had changes */
649 /* Return the number of changes made */
655 static unsigned OptAdd2 (CodeSeg* S)
656 /* Search for the sequence
680 * provided that a/x is not used later.
683 unsigned Changes = 0;
685 /* Walk over the entries */
687 while (I < CS_GetEntryCount (S)) {
692 L[0] = CS_GetEntry (S, I);
694 /* Check for the sequence */
695 if (L[0]->OPC == OP65_LDY &&
696 CE_KnownImm (L[0]) &&
697 CS_GetEntries (S, L+1, I+1, 6) &&
698 L[1]->OPC == OP65_LDA &&
699 L[1]->AM == AM65_ZP_INDY &&
700 !CE_HasLabel (L[1]) &&
701 L[2]->OPC == OP65_TAX &&
702 !CE_HasLabel (L[2]) &&
703 L[3]->OPC == OP65_DEY &&
704 !CE_HasLabel (L[3]) &&
705 L[4]->OPC == OP65_LDA &&
706 L[4]->AM == AM65_ZP_INDY &&
707 !CE_HasLabel (L[4]) &&
708 L[5]->OPC == OP65_LDY &&
709 CE_KnownImm (L[5]) &&
710 !CE_HasLabel (L[5]) &&
711 L[6]->OPC == OP65_JSR &&
712 strcmp (L[6]->Arg, "addeqysp") == 0 &&
713 !CE_HasLabel (L[6]) &&
714 (GetRegInfo (S, I+7, REG_AX) & REG_AX) == 0) {
721 /* Create a replacement for the first LDY */
722 Offs = (int) (L[0]->Num - 1);
723 xsprintf (Buf, sizeof (Buf), "$%02X", Offs);
724 X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[0]->LI);
725 CS_InsertEntry (S, X, I+1);
728 /* Load Y with the low offset of the target variable */
729 X = NewCodeEntry (OP65_LDY, AM65_IMM, L[5]->Arg, 0, L[1]->LI);
730 CS_InsertEntry (S, X, I+2);
733 X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, L[1]->LI);
734 CS_InsertEntry (S, X, I+3);
736 /* Remove the TAX/DEY sequence */
737 CS_DelEntry (S, I+5); /* dey */
738 CS_DelEntry (S, I+4); /* tax */
740 /* Addition of the low byte */
741 X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp", 0, L[4]->LI);
742 CS_InsertEntry (S, X, I+4);
743 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "sp", 0, L[4]->LI);
744 CS_InsertEntry (S, X, I+5);
747 xsprintf (Buf, sizeof (Buf), "$%02X", (Offs+1));
748 X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[4]->LI);
749 CS_InsertEntry (S, X, I+6);
751 /* Addition of the high byte */
752 xsprintf (Buf, sizeof (Buf), "$%02X", (int)(L[5]->Num+1));
753 X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[5]->LI);
754 CS_InsertEntry (S, X, I+8);
755 X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp", 0, L[6]->LI);
756 CS_InsertEntry (S, X, I+9);
757 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "sp", 0, L[6]->LI);
758 CS_InsertEntry (S, X, I+10);
760 /* Delete the remaining stuff */
761 CS_DelEntry (S, I+12);
762 CS_DelEntry (S, I+11);
764 /* Remember, we had changes */
774 /* Return the number of changes made */
780 static unsigned OptAdd3 (CodeSeg* S)
781 /* Search for the sequence
788 * and remove the handling of the high byte if X is not used later.
791 unsigned Changes = 0;
793 /* Walk over the entries */
795 while (I < CS_GetEntryCount (S)) {
800 CodeEntry* E = CS_GetEntry (S, I);
802 /* Check for the sequence */
803 if (E->OPC == OP65_ADC &&
804 CS_GetEntries (S, L, I+1, 3) &&
805 (L[0]->OPC == OP65_BCC || L[0]->OPC == OP65_JCC) &&
807 !CE_HasLabel (L[0]) &&
808 L[1]->OPC == OP65_INX &&
809 !CE_HasLabel (L[1]) &&
810 L[0]->JumpTo->Owner == L[2] &&
811 !RegXUsed (S, I+3)) {
813 /* Remove the bcs/dex */
814 CS_DelEntries (S, I+1, 2);
816 /* Remember, we had changes */
826 /* Return the number of changes made */
832 /*****************************************************************************/
833 /* Optimize shifts */
834 /*****************************************************************************/
838 static unsigned OptShift1 (CodeSeg* S)
839 /* A call to the shlaxN routine may get replaced by one or more asl insns
840 * if the value of X is not used later.
843 unsigned Changes = 0;
845 /* Walk over the entries */
847 while (I < CS_GetEntryCount (S)) {
850 CodeEntry* E = CS_GetEntry (S, I);
852 /* Check for the sequence */
853 if (E->OPC == OP65_JSR &&
854 (strncmp (E->Arg, "shlax", 5) == 0 ||
855 strncmp (E->Arg, "aslax", 5) == 0) &&
856 strlen (E->Arg) == 6 &&
857 IsDigit (E->Arg[5]) &&
858 !RegXUsed (S, I+1)) {
860 /* Insert shift insns */
861 unsigned Count = E->Arg[5] - '0';
863 CodeEntry* X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI);
864 CS_InsertEntry (S, X, I+1);
867 /* Delete the call to shlax */
870 /* Remember, we had changes */
880 /* Return the number of changes made */
886 /*****************************************************************************/
887 /* Optimizations for compares */
888 /*****************************************************************************/
892 static unsigned OptCmp1 (CodeSeg* S)
893 /* Search for the sequence
905 unsigned Changes = 0;
907 /* Walk over the entries */
909 while (I < CS_GetEntryCount (S)) {
914 CodeEntry* E = CS_GetEntry (S, I);
916 /* Check for the sequence */
917 if (E->OPC == OP65_STX &&
918 CS_GetEntries (S, L, I+1, 2) &&
919 L[0]->OPC == OP65_STX &&
920 strcmp (L[0]->Arg, "tmp1") == 0 &&
921 !CE_HasLabel (L[0]) &&
922 L[1]->OPC == OP65_ORA &&
923 strcmp (L[1]->Arg, "tmp1") == 0 &&
924 !CE_HasLabel (L[1])) {
926 /* Remove the remaining instructions */
927 CS_DelEntries (S, I+1, 2);
929 /* Insert the ora instead */
930 CS_InsertEntry (S, NewCodeEntry (OP65_ORA, E->AM, E->Arg, 0, E->LI), I+1);
932 /* Remember, we had changes */
942 /* Return the number of changes made */
948 static unsigned OptCmp2 (CodeSeg* S)
951 * lda/and/ora/eor ...
955 * lda/and/ora/eor ...
959 * and remove the cmp.
962 unsigned Changes = 0;
964 /* Walk over the entries */
966 while (I < CS_GetEntryCount (S)) {
971 CodeEntry* E = CS_GetEntry (S, I);
973 /* Check for the sequence */
974 if ((E->OPC == OP65_ADC ||
975 E->OPC == OP65_AND ||
976 E->OPC == OP65_DEA ||
977 E->OPC == OP65_EOR ||
978 E->OPC == OP65_INA ||
979 E->OPC == OP65_LDA ||
980 E->OPC == OP65_ORA ||
981 E->OPC == OP65_PLA ||
982 E->OPC == OP65_SBC ||
983 E->OPC == OP65_TXA ||
984 E->OPC == OP65_TYA) &&
985 CS_GetEntries (S, L, I+1, 2) &&
986 IsCmpToZero (L[0]) &&
987 !CE_HasLabel (L[0]) &&
988 ((L[1]->Info & OF_FBRA) != 0 ||
989 (L[1]->OPC == OP65_JSR &&
990 FindBoolCmpCond (L[1]->Arg) != CMP_INV)) &&
991 !CE_HasLabel (L[1])) {
993 /* Remove the compare */
994 CS_DelEntry (S, I+1);
996 /* Remember, we had changes */
1006 /* Return the number of changes made */
1012 static unsigned OptCmp3 (CodeSeg* S)
1022 * If a is zero, we may remove the compare. If a and b are both zero, we may
1023 * replace it by the sequence
1029 * L1 may be either the label at the branch instruction, or the target label
1030 * of this instruction.
1033 unsigned Changes = 0;
1035 /* Walk over the entries */
1037 while (I < CS_GetEntryCount (S)) {
1041 /* Get next entry */
1042 CodeEntry* E = CS_GetEntry (S, I);
1044 /* Check for the sequence */
1045 if (E->OPC == OP65_LDA &&
1046 CS_GetEntries (S, L, I+1, 5) &&
1047 L[0]->OPC == OP65_LDX &&
1048 !CE_HasLabel (L[0]) &&
1049 IsImmCmp16 (S, L+1)) {
1051 if (L[1]->Num == 0 && L[3]->Num == 0) {
1052 /* The value is zero, we may use the simple code version. */
1053 CE_ReplaceOPC (L[0], OP65_ORA);
1054 CS_DelEntries (S, I+2, 3);
1056 /* Move the lda instruction after the first branch. This will
1057 * improve speed, since the load is delayed after the first
1060 CS_MoveEntry (S, I, I+4);
1062 /* We will replace the ldx/cpx by lda/cmp */
1063 CE_ReplaceOPC (L[0], OP65_LDA);
1064 CE_ReplaceOPC (L[1], OP65_CMP);
1066 /* Beware: If the first LDA instruction had a label, we have
1067 * to move this label to the top of the sequence again.
1069 if (CE_HasLabel (E)) {
1070 CS_MoveLabels (S, E, L[0]);
1083 /* Return the number of changes made */
1089 static unsigned OptCmp4 (CodeSeg* S)
1090 /* Optimize compares of local variables:
1103 unsigned Changes = 0;
1105 /* Walk over the entries */
1107 while (I < CS_GetEntryCount (S)) {
1111 /* Check for the sequence */
1112 if (IsLocalLoad16 (S, I, L, 9) && IsImmCmp16 (S, L+5)) {
1114 if (L[5]->Num == 0 && L[7]->Num == 0) {
1116 /* The value is zero, we may use the simple code version:
1123 CE_ReplaceOPC (L[4], OP65_ORA);
1124 CS_DelEntries (S, I+5, 3); /* cpx/bne/cmp */
1125 CS_DelEntry (S, I+2); /* tax */
1129 /* Change the code to just use the A register. Move the load
1130 * of the low byte after the first branch if possible:
1141 CS_DelEntry (S, I+2); /* tax */
1142 CE_ReplaceOPC (L[5], OP65_CMP); /* cpx -> cmp */
1143 CS_MoveEntry (S, I+4, I+2); /* cmp */
1144 CS_MoveEntry (S, I+5, I+3); /* bne */
1156 /* Return the number of changes made */
1162 static unsigned OptCmp5 (CodeSeg* S)
1163 /* Search for calls to compare subroutines followed by a conditional branch
1164 * and replace them by cheaper versions, since the branch means that the
1165 * boolean value returned by these routines is not needed (we may also check
1166 * that explicitly, but for the current code generator it is always true).
1169 unsigned Changes = 0;
1171 /* Walk over the entries */
1173 while (I < CS_GetEntryCount (S)) {
1178 /* Get next entry */
1179 CodeEntry* E = CS_GetEntry (S, I);
1181 /* Check for the sequence */
1182 if (E->OPC == OP65_JSR &&
1183 (Cond = FindTosCmpCond (E->Arg)) != CMP_INV &&
1184 (N = CS_GetNextEntry (S, I)) != 0 &&
1185 (N->Info & OF_ZBRA) != 0 &&
1188 /* The tos... functions will return a boolean value in a/x and
1189 * the Z flag says if this value is zero or not. We will call
1190 * a cheaper subroutine instead, one that does not return a
1191 * boolean value but only valid flags. Note: jeq jumps if
1192 * the condition is not met, jne jumps if the condition is met.
1193 * Invert the code if we jump on condition not met.
1195 if (GetBranchCond (N->OPC) == BC_EQ) {
1196 /* Jumps if condition false, invert condition */
1197 Cond = CmpInvertTab [Cond];
1200 /* Replace the subroutine call. */
1201 E = NewCodeEntry (OP65_JSR, AM65_ABS, "tosicmp", 0, E->LI);
1202 CS_InsertEntry (S, E, I+1);
1205 /* Replace the conditional branch */
1206 ReplaceCmp (S, I+1, Cond);
1208 /* Remember, we had changes */
1218 /* Return the number of changes made */
1224 static unsigned OptCmp6 (CodeSeg* S)
1225 /* Search for a sequence ldx/txa/branch and remove the txa if A is not
1229 unsigned Changes = 0;
1231 /* Walk over the entries */
1233 while (I < CS_GetEntryCount (S)) {
1237 /* Get next entry */
1238 CodeEntry* E = CS_GetEntry (S, I);
1240 /* Check for the sequence */
1241 if ((E->OPC == OP65_LDX) &&
1242 CS_GetEntries (S, L, I+1, 2) &&
1243 L[0]->OPC == OP65_TXA &&
1244 !CE_HasLabel (L[0]) &&
1245 (L[1]->Info & OF_FBRA) != 0 &&
1246 !CE_HasLabel (L[1]) &&
1247 !RegAUsed (S, I+3)) {
1249 /* Remove the txa */
1250 CS_DelEntry (S, I+1);
1252 /* Remember, we had changes */
1262 /* Return the number of changes made */
1268 static unsigned OptCmp7 (CodeSeg* S)
1269 /* Check for register compares where the contents of the register and therefore
1270 * the result of the compare is known.
1273 unsigned Changes = 0;
1276 /* Generate register info for this step */
1279 /* Walk over the entries */
1281 while (I < CS_GetEntryCount (S)) {
1285 /* Get next entry */
1286 CodeEntry* E = CS_GetEntry (S, I);
1288 /* Check for a compare against an immediate value */
1289 if ((E->Info & OF_CMP) != 0 &&
1290 (RegVal = GetCmpRegVal (E)) >= 0 &&
1293 /* We are able to evaluate the compare at compile time. Check if
1294 * one or more branches are ahead.
1296 unsigned JumpsChanged = 0;
1298 while ((N = CS_GetNextEntry (S, I)) != 0 && /* Followed by something.. */
1299 (N->Info & OF_CBRA) != 0 && /* ..that is a cond branch.. */
1300 !CE_HasLabel (N)) { /* ..and has no label */
1302 /* Evaluate the branch condition */
1304 switch (GetBranchCond (N->OPC)) {
1306 Cond = ((unsigned char)RegVal) < ((unsigned char)E->Num);
1310 Cond = ((unsigned char)RegVal) >= ((unsigned char)E->Num);
1314 Cond = ((unsigned char)RegVal) == ((unsigned char)E->Num);
1318 Cond = ((signed char)RegVal) < ((signed char)E->Num);
1322 Cond = ((unsigned char)RegVal) != ((unsigned char)E->Num);
1326 Cond = ((signed char)RegVal) >= ((signed char)E->Num);
1333 /* If the condition is false, we may remove the jump. Otherwise
1334 * the branch will always be taken, so we may replace it by a
1335 * jump (and bail out).
1338 CS_DelEntry (S, I+1);
1340 CodeLabel* L = N->JumpTo;
1341 CodeEntry* X = NewCodeEntry (OP65_JMP, AM65_BRA, L->Name, L, N->LI);
1342 CS_InsertEntry (S, X, I+2);
1343 CS_DelEntry (S, I+1);
1346 /* Remember, we had changes */
1351 /* If we have made changes above, we may also remove the compare */
1363 /* Free register info */
1366 /* Return the number of changes made */
1372 /*****************************************************************************/
1373 /* Optimize tests */
1374 /*****************************************************************************/
1378 static unsigned OptTest1 (CodeSeg* S)
1385 * if X is zero, the sequence may be changed to
1390 * which may be optimized further by another step.
1392 * If A is zero, the sequence may be changed to
1399 unsigned Changes = 0;
1402 /* Generate register info for this step */
1405 /* Walk over the entries */
1407 while (I < CS_GetEntryCount (S)) {
1411 /* Get next entry */
1412 L[0] = CS_GetEntry (S, I);
1414 /* Check if it's the sequence we're searching for */
1415 if (L[0]->OPC == OP65_STX &&
1416 CS_GetEntries (S, L+1, I+1, 2) &&
1417 !CE_HasLabel (L[1]) &&
1418 L[1]->OPC == OP65_ORA &&
1419 strcmp (L[0]->Arg, L[1]->Arg) == 0 &&
1420 !CE_HasLabel (L[2]) &&
1421 (L[2]->Info & OF_ZBRA) != 0) {
1423 /* Check if X is zero */
1424 if (L[0]->RI->In.RegX == 0) {
1426 /* Insert the compare */
1427 CodeEntry* N = NewCodeEntry (OP65_CMP, AM65_IMM, "$00", 0, L[0]->LI);
1428 CS_InsertEntry (S, N, I+2);
1430 /* Remove the two other insns */
1431 CS_DelEntry (S, I+1);
1434 /* We had changes */
1437 /* Check if A is zero */
1438 } else if (L[1]->RI->In.RegA == 0) {
1440 /* Insert the txa */
1441 CodeEntry* N = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, L[1]->LI);
1442 CS_InsertEntry (S, N, I+2);
1444 /* Remove the two other insns */
1445 CS_DelEntry (S, I+1);
1448 /* We had changes */
1458 /* Free register info */
1461 /* Return the number of changes made */
1467 /*****************************************************************************/
1468 /* nega optimizations */
1469 /*****************************************************************************/
1473 static unsigned OptNegA1 (CodeSeg* S)
1480 * Remove the ldx if the lda does not use it.
1483 unsigned Changes = 0;
1485 /* Walk over the entries */
1487 while (I < CS_GetEntryCount (S)) {
1491 /* Get next entry */
1492 CodeEntry* E = CS_GetEntry (S, I);
1494 /* Check for a ldx */
1495 if (E->OPC == OP65_LDX &&
1496 E->AM == AM65_IMM &&
1497 (E->Flags & CEF_NUMARG) != 0 &&
1499 CS_GetEntries (S, L, I+1, 2) &&
1500 L[0]->OPC == OP65_LDA &&
1501 (L[0]->Use & REG_X) == 0 &&
1502 !CE_HasLabel (L[0]) &&
1503 L[1]->OPC == OP65_JSR &&
1504 strcmp (L[1]->Arg, "bnega") == 0 &&
1505 !CE_HasLabel (L[1])) {
1507 /* Remove the ldx instruction */
1510 /* Remember, we had changes */
1520 /* Return the number of changes made */
1526 static unsigned OptNegA2 (CodeSeg* S)
1533 * Adjust the conditional branch and remove the call to the subroutine.
1536 unsigned Changes = 0;
1538 /* Walk over the entries */
1540 while (I < CS_GetEntryCount (S)) {
1544 /* Get next entry */
1545 CodeEntry* E = CS_GetEntry (S, I);
1547 /* Check for the sequence */
1548 if ((E->OPC == OP65_ADC ||
1549 E->OPC == OP65_AND ||
1550 E->OPC == OP65_DEA ||
1551 E->OPC == OP65_EOR ||
1552 E->OPC == OP65_INA ||
1553 E->OPC == OP65_LDA ||
1554 E->OPC == OP65_ORA ||
1555 E->OPC == OP65_PLA ||
1556 E->OPC == OP65_SBC ||
1557 E->OPC == OP65_TXA ||
1558 E->OPC == OP65_TYA) &&
1559 CS_GetEntries (S, L, I+1, 2) &&
1560 L[0]->OPC == OP65_JSR &&
1561 strcmp (L[0]->Arg, "bnega") == 0 &&
1562 !CE_HasLabel (L[0]) &&
1563 (L[1]->Info & OF_ZBRA) != 0 &&
1564 !CE_HasLabel (L[1])) {
1566 /* Invert the branch */
1567 CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
1569 /* Delete the subroutine call */
1570 CS_DelEntry (S, I+1);
1572 /* Remember, we had changes */
1582 /* Return the number of changes made */
1588 /*****************************************************************************/
1589 /* negax optimizations */
1590 /*****************************************************************************/
1594 static unsigned OptNegAX1 (CodeSeg* S)
1595 /* On a call to bnegax, if X is zero, the result depends only on the value in
1596 * A, so change the call to a call to bnega. This will get further optimized
1597 * later if possible.
1600 unsigned Changes = 0;
1603 /* Generate register info for this step */
1606 /* Walk over the entries */
1608 while (I < CS_GetEntryCount (S)) {
1610 /* Get next entry */
1611 CodeEntry* E = CS_GetEntry (S, I);
1613 /* Check if this is a call to bnegax, and if X is known and zero */
1614 if (E->OPC == OP65_JSR &&
1615 E->RI->In.RegX == 0 &&
1616 strcmp (E->Arg, "bnegax") == 0) {
1618 /* We're cheating somewhat here ... */
1622 /* We had changes */
1631 /* Free register info */
1634 /* Return the number of changes made */
1640 static unsigned OptNegAX2 (CodeSeg* S)
1641 /* Search for the sequence:
1658 unsigned Changes = 0;
1660 /* Walk over the entries */
1662 while (I < CS_GetEntryCount (S)) {
1666 /* Get next entry */
1667 CodeEntry* E = CS_GetEntry (S, I);
1669 /* Check for the sequence */
1670 if (E->OPC == OP65_LDA &&
1671 E->AM == AM65_ZP_INDY &&
1672 CS_GetEntries (S, L, I+1, 5) &&
1673 L[0]->OPC == OP65_TAX &&
1674 L[1]->OPC == OP65_DEY &&
1675 L[2]->OPC == OP65_LDA &&
1676 L[2]->AM == AM65_ZP_INDY &&
1677 strcmp (L[2]->Arg, E->Arg) == 0 &&
1678 !CE_HasLabel (L[2]) &&
1679 L[3]->OPC == OP65_JSR &&
1680 strcmp (L[3]->Arg, "bnegax") == 0 &&
1681 !CE_HasLabel (L[3]) &&
1682 (L[4]->Info & OF_ZBRA) != 0 &&
1683 !CE_HasLabel (L[4])) {
1686 CE_ReplaceOPC (L[2], OP65_ORA);
1688 /* Invert the branch */
1689 CE_ReplaceOPC (L[4], GetInverseBranch (L[4]->OPC));
1691 /* Delete the entries no longer needed. Beware: Deleting entries
1692 * will change the indices.
1694 CS_DelEntry (S, I+4); /* jsr bnegax */
1695 CS_DelEntry (S, I+1); /* tax */
1697 /* Remember, we had changes */
1707 /* Return the number of changes made */
1713 static unsigned OptNegAX3 (CodeSeg* S)
1714 /* Search for the sequence:
1728 unsigned Changes = 0;
1730 /* Walk over the entries */
1732 while (I < CS_GetEntryCount (S)) {
1736 /* Get next entry */
1737 CodeEntry* E = CS_GetEntry (S, I);
1739 /* Check for the sequence */
1740 if (E->OPC == OP65_LDA &&
1741 CS_GetEntries (S, L, I+1, 3) &&
1742 L[0]->OPC == OP65_LDX &&
1743 !CE_HasLabel (L[0]) &&
1744 L[1]->OPC == OP65_JSR &&
1745 strcmp (L[1]->Arg, "bnegax") == 0 &&
1746 !CE_HasLabel (L[1]) &&
1747 (L[2]->Info & OF_ZBRA) != 0 &&
1748 !CE_HasLabel (L[2])) {
1751 CE_ReplaceOPC (L[0], OP65_ORA);
1753 /* Invert the branch */
1754 CE_ReplaceOPC (L[2], GetInverseBranch (L[2]->OPC));
1756 /* Delete the subroutine call */
1757 CS_DelEntry (S, I+2);
1759 /* Remember, we had changes */
1769 /* Return the number of changes made */
1775 static unsigned OptNegAX4 (CodeSeg* S)
1776 /* Search for the sequence:
1782 * and replace it by:
1789 unsigned Changes = 0;
1791 /* Walk over the entries */
1793 while (I < CS_GetEntryCount (S)) {
1797 /* Get next entry */
1798 CodeEntry* E = CS_GetEntry (S, I);
1800 /* Check for the sequence */
1801 if (E->OPC == OP65_JSR &&
1802 CS_GetEntries (S, L, I+1, 2) &&
1803 L[0]->OPC == OP65_JSR &&
1804 strncmp (L[0]->Arg,"bnega",5) == 0 &&
1805 !CE_HasLabel (L[0]) &&
1806 (L[1]->Info & OF_ZBRA) != 0 &&
1807 !CE_HasLabel (L[1])) {
1811 /* Check if we're calling bnega or bnegax */
1812 int ByteSized = (strcmp (L[0]->Arg, "bnega") == 0);
1814 /* Insert apropriate test code */
1817 X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI);
1818 CS_InsertEntry (S, X, I+2);
1821 X = NewCodeEntry (OP65_STX, AM65_ZP, "tmp1", 0, L[0]->LI);
1822 CS_InsertEntry (S, X, I+2);
1823 X = NewCodeEntry (OP65_ORA, AM65_ZP, "tmp1", 0, L[0]->LI);
1824 CS_InsertEntry (S, X, I+3);
1827 /* Delete the subroutine call */
1828 CS_DelEntry (S, I+1);
1830 /* Invert the branch */
1831 CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
1833 /* Remember, we had changes */
1843 /* Return the number of changes made */
1849 /*****************************************************************************/
1850 /* Optimize stores through pointers */
1851 /*****************************************************************************/
1855 static unsigned OptPtrStore1Sub (CodeSeg* S, unsigned I, CodeEntry** const L)
1856 /* Check if this is one of the allowed suboperation for OptPtrStore1 */
1858 /* Check for a label attached to the entry */
1859 if (CE_HasLabel (L[0])) {
1863 /* Check for single insn sub ops */
1864 if (L[0]->OPC == OP65_AND ||
1865 L[0]->OPC == OP65_EOR ||
1866 L[0]->OPC == OP65_ORA ||
1867 (L[0]->OPC == OP65_JSR && strncmp (L[0]->Arg, "shlax", 5) == 0) ||
1868 (L[0]->OPC == OP65_JSR && strncmp (L[0]->Arg, "shrax", 5) == 0)) {
1873 } else if (L[0]->OPC == OP65_CLC &&
1874 (L[1] = CS_GetNextEntry (S, I)) != 0 &&
1875 L[1]->OPC == OP65_ADC &&
1876 !CE_HasLabel (L[1])) {
1878 } else if (L[0]->OPC == OP65_SEC &&
1879 (L[1] = CS_GetNextEntry (S, I)) != 0 &&
1880 L[1]->OPC == OP65_SBC &&
1881 !CE_HasLabel (L[1])) {
1893 static unsigned OptPtrStore1 (CodeSeg* S)
1894 /* Search for the sequence:
1903 * and replace it by:
1915 unsigned Changes = 0;
1917 /* Walk over the entries */
1919 while (I < CS_GetEntryCount (S)) {
1924 /* Get next entry */
1925 L[0] = CS_GetEntry (S, I);
1927 /* Check for the sequence */
1928 if (L[0]->OPC == OP65_JSR &&
1929 strcmp (L[0]->Arg, "pushax") == 0 &&
1930 CS_GetEntries (S, L+1, I+1, 3) &&
1931 L[1]->OPC == OP65_LDY &&
1932 CE_KnownImm (L[1]) &&
1933 !CE_HasLabel (L[1]) &&
1934 L[2]->OPC == OP65_JSR &&
1935 strcmp (L[2]->Arg, "ldauidx") == 0 &&
1936 !CE_HasLabel (L[2]) &&
1937 (K = OptPtrStore1Sub (S, I+3, L+3)) > 0 &&
1938 CS_GetEntries (S, L+3+K, I+3+K, 2) &&
1939 L[3+K]->OPC == OP65_LDY &&
1940 CE_KnownImm (L[3+K]) &&
1941 !CE_HasLabel (L[3+K]) &&
1942 L[4+K]->OPC == OP65_JSR &&
1943 strcmp (L[4+K]->Arg, "staspidx") == 0 &&
1944 !CE_HasLabel (L[4+K])) {
1948 /* Create and insert the stores */
1949 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
1950 CS_InsertEntry (S, X, I+1);
1952 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
1953 CS_InsertEntry (S, X, I+2);
1955 /* Delete the call to pushax */
1958 /* Delete the call to ldauidx */
1959 CS_DelEntry (S, I+3);
1961 /* Insert the load from ptr1 */
1962 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[3]->LI);
1963 CS_InsertEntry (S, X, I+3);
1964 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[2]->LI);
1965 CS_InsertEntry (S, X, I+4);
1967 /* Insert the store through ptr1 */
1968 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "ptr1", 0, L[3]->LI);
1969 CS_InsertEntry (S, X, I+6+K);
1971 /* Delete the call to staspidx */
1972 CS_DelEntry (S, I+7+K);
1974 /* Remember, we had changes */
1984 /* Return the number of changes made */
1990 static unsigned OptPtrStore2 (CodeSeg* S)
1991 /* Search for the sequence:
1998 * and replace it by:
2007 unsigned Changes = 0;
2009 /* Walk over the entries */
2011 while (I < CS_GetEntryCount (S)) {
2015 /* Get next entry */
2016 L[0] = CS_GetEntry (S, I);
2018 /* Check for the sequence */
2019 if (L[0]->OPC == OP65_JSR &&
2020 strcmp (L[0]->Arg, "pushax") == 0 &&
2021 CS_GetEntries (S, L+1, I+1, 3) &&
2022 L[1]->OPC == OP65_LDA &&
2023 !CE_HasLabel (L[1]) &&
2024 L[2]->OPC == OP65_LDY &&
2025 !CE_HasLabel (L[2]) &&
2026 L[3]->OPC == OP65_JSR &&
2027 strcmp (L[3]->Arg, "staspidx") == 0 &&
2028 !CE_HasLabel (L[3])) {
2032 /* Create and insert the stores */
2033 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
2034 CS_InsertEntry (S, X, I+1);
2036 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
2037 CS_InsertEntry (S, X, I+2);
2039 /* Delete the call to pushax */
2042 /* Insert the store through ptr1 */
2043 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "ptr1", 0, L[3]->LI);
2044 CS_InsertEntry (S, X, I+4);
2046 /* Delete the call to staspidx */
2047 CS_DelEntry (S, I+5);
2049 /* Remember, we had changes */
2059 /* Return the number of changes made */
2065 /*****************************************************************************/
2066 /* Optimize loads through pointers */
2067 /*****************************************************************************/
2071 static unsigned OptPtrLoad1 (CodeSeg* S)
2072 /* Search for the sequence:
2076 * lda (sp),y # May be any destination
2080 * and replace it by:
2091 unsigned Changes = 0;
2093 /* Walk over the entries */
2095 while (I < CS_GetEntryCount (S)) {
2099 /* Get next entry */
2100 L[0] = CS_GetEntry (S, I);
2102 /* Check for the sequence */
2103 if (L[0]->OPC == OP65_TAX &&
2104 CS_GetEntries (S, L+1, I+1, 4) &&
2105 L[1]->OPC == OP65_DEY &&
2106 !CE_HasLabel (L[1]) &&
2107 L[2]->OPC == OP65_LDA &&
2108 !CE_HasLabel (L[2]) &&
2109 L[3]->OPC == OP65_LDY &&
2110 !CE_HasLabel (L[3]) &&
2111 L[4]->OPC == OP65_JSR &&
2112 strcmp (L[4]->Arg, "ldauidx") == 0 &&
2113 !CE_HasLabel (L[4])) {
2117 /* Store the high byte and remove the TAX instead */
2118 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1+1", 0, L[0]->LI);
2119 CS_InsertEntry (S, X, I+1);
2122 /* Store the low byte */
2123 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[2]->LI);
2124 CS_InsertEntry (S, X, I+3);
2126 /* Delete the call to ldauidx */
2127 CS_DelEntry (S, I+5);
2129 /* Load high and low byte */
2130 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[3]->LI);
2131 CS_InsertEntry (S, X, I+5);
2132 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[3]->LI);
2133 CS_InsertEntry (S, X, I+6);
2135 /* Remember, we had changes */
2145 /* Return the number of changes made */
2151 static unsigned OptPtrLoad2 (CodeSeg* S)
2152 /* Search for the sequence:
2163 * and replace it by:
2175 unsigned Changes = 0;
2177 /* Walk over the entries */
2179 while (I < CS_GetEntryCount (S)) {
2183 /* Get next entry */
2184 L[0] = CS_GetEntry (S, I);
2186 /* Check for the sequence */
2187 if (L[0]->OPC == OP65_ADC &&
2188 CS_GetEntries (S, L+1, I+1, 7) &&
2189 L[1]->OPC == OP65_TAY &&
2190 !CE_HasLabel (L[1]) &&
2191 L[2]->OPC == OP65_TXA &&
2192 !CE_HasLabel (L[2]) &&
2193 L[3]->OPC == OP65_ADC &&
2194 !CE_HasLabel (L[3]) &&
2195 L[4]->OPC == OP65_TAX &&
2196 !CE_HasLabel (L[4]) &&
2197 L[5]->OPC == OP65_TYA &&
2198 !CE_HasLabel (L[5]) &&
2199 L[6]->OPC == OP65_LDY &&
2200 !CE_HasLabel (L[6]) &&
2201 L[7]->OPC == OP65_JSR &&
2202 strcmp (L[7]->Arg, "ldauidx") == 0 &&
2203 !CE_HasLabel (L[7])) {
2207 /* Store the low byte and remove the TAY instead */
2208 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
2209 CS_InsertEntry (S, X, I+1);
2210 CS_DelEntry (S, I+2);
2212 /* Store the high byte */
2213 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1+1", 0, L[3]->LI);
2214 CS_InsertEntry (S, X, I+4);
2216 /* Delete more transfer insns */
2217 CS_DelEntry (S, I+6);
2218 CS_DelEntry (S, I+5);
2220 /* Delete the call to ldauidx */
2221 CS_DelEntry (S, I+6);
2223 /* Load high and low byte */
2224 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[6]->LI);
2225 CS_InsertEntry (S, X, I+6);
2226 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[6]->LI);
2227 CS_InsertEntry (S, X, I+7);
2229 /* Remember, we had changes */
2239 /* Return the number of changes made */
2245 static unsigned OptPtrLoad3 (CodeSeg* S)
2246 /* Search for the sequence:
2258 * and replace it by:
2271 unsigned Changes = 0;
2273 /* Walk over the entries */
2275 while (I < CS_GetEntryCount (S)) {
2279 /* Get next entry */
2280 L[0] = CS_GetEntry (S, I);
2282 /* Check for the sequence */
2283 if (L[0]->OPC == OP65_ADC &&
2284 CS_GetEntries (S, L+1, I+1, 8) &&
2285 L[1]->OPC == OP65_PHA &&
2286 !CE_HasLabel (L[1]) &&
2287 L[2]->OPC == OP65_TXA &&
2288 !CE_HasLabel (L[2]) &&
2289 L[3]->OPC == OP65_INY &&
2290 !CE_HasLabel (L[3]) &&
2291 L[4]->OPC == OP65_ADC &&
2292 !CE_HasLabel (L[4]) &&
2293 L[5]->OPC == OP65_TAX &&
2294 !CE_HasLabel (L[5]) &&
2295 L[6]->OPC == OP65_PLA &&
2296 !CE_HasLabel (L[6]) &&
2297 L[7]->OPC == OP65_LDY &&
2298 !CE_HasLabel (L[7]) &&
2299 L[8]->OPC == OP65_JSR &&
2300 strcmp (L[8]->Arg, "ldauidx") == 0 &&
2301 !CE_HasLabel (L[8])) {
2305 /* Store the low byte and remove the PHA instead */
2306 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
2307 CS_InsertEntry (S, X, I+1);
2308 CS_DelEntry (S, I+2);
2310 /* Store the high byte */
2311 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1+1", 0, L[4]->LI);
2312 CS_InsertEntry (S, X, I+5);
2314 /* Delete more transfer and PLA insns */
2315 CS_DelEntry (S, I+7);
2316 CS_DelEntry (S, I+6);
2318 /* Delete the call to ldauidx */
2319 CS_DelEntry (S, I+7);
2321 /* Load high and low byte */
2322 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[6]->LI);
2323 CS_InsertEntry (S, X, I+7);
2324 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[6]->LI);
2325 CS_InsertEntry (S, X, I+8);
2327 /* Remember, we had changes */
2337 /* Return the number of changes made */
2343 static unsigned OptPtrLoad4 (CodeSeg* S)
2344 /* Search for the sequence:
2355 * and replace it by:
2362 unsigned Changes = 0;
2364 /* Walk over the entries */
2366 while (I < CS_GetEntryCount (S)) {
2371 /* Get next entry */
2372 L[0] = CS_GetEntry (S, I);
2374 /* Check for the sequence */
2375 if (L[0]->OPC == OP65_LDA &&
2376 L[0]->AM == AM65_IMM &&
2377 CS_GetEntries (S, L+1, I+1, 7) &&
2378 L[1]->OPC == OP65_LDX &&
2379 L[1]->AM == AM65_IMM &&
2380 !CE_HasLabel (L[1]) &&
2381 L[2]->OPC == OP65_CLC &&
2382 !CE_HasLabel (L[2]) &&
2383 L[3]->OPC == OP65_ADC &&
2384 (L[3]->AM == AM65_ABS || L[3]->AM == AM65_ZP) &&
2385 !CE_HasLabel (L[3]) &&
2386 (L[4]->OPC == OP65_BCC || L[4]->OPC == OP65_JCC) &&
2387 L[4]->JumpTo != 0 &&
2388 L[4]->JumpTo->Owner == L[6] &&
2389 !CE_HasLabel (L[4]) &&
2390 L[5]->OPC == OP65_INX &&
2391 !CE_HasLabel (L[5]) &&
2392 L[6]->OPC == OP65_LDY &&
2393 CE_KnownImm (L[6]) &&
2395 L[7]->OPC == OP65_JSR &&
2396 strcmp (L[7]->Arg, "ldauidx") == 0 &&
2397 !CE_HasLabel (L[7]) &&
2398 /* Check the label last because this is quite costly */
2399 (Len = strlen (L[0]->Arg)) > 3 &&
2400 L[0]->Arg[0] == '<' &&
2401 L[0]->Arg[1] == '(' &&
2402 strlen (L[1]->Arg) == Len &&
2403 L[1]->Arg[0] == '>' &&
2404 memcmp (L[0]->Arg+1, L[1]->Arg+1, Len-1) == 0) {
2409 /* We will create all the new stuff behind the current one so
2410 * we keep the line references.
2412 X = NewCodeEntry (OP65_LDX, L[3]->AM, L[3]->Arg, 0, L[0]->LI);
2413 CS_InsertEntry (S, X, I+8);
2415 Label = memcpy (xmalloc (Len-2), L[0]->Arg+2, Len-3);
2416 Label[Len-3] = '\0';
2417 X = NewCodeEntry (OP65_LDA, AM65_ABSX, Label, 0, L[0]->LI);
2418 CS_InsertEntry (S, X, I+9);
2421 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[0]->LI);
2422 CS_InsertEntry (S, X, I+10);
2424 /* Remove the old code */
2425 CS_DelEntries (S, I, 8);
2427 /* Remember, we had changes */
2437 /* Return the number of changes made */
2443 static unsigned OptPtrLoad5 (CodeSeg* S)
2444 /* Search for the sequence:
2456 * and replace it by:
2465 unsigned Changes = 0;
2467 /* Walk over the entries */
2469 while (I < CS_GetEntryCount (S)) {
2474 /* Get next entry */
2475 L[0] = CS_GetEntry (S, I);
2477 /* Check for the sequence */
2478 if (L[0]->OPC == OP65_LDA &&
2479 L[0]->AM == AM65_IMM &&
2480 CS_GetEntries (S, L+1, I+1, 8) &&
2481 L[1]->OPC == OP65_LDX &&
2482 L[1]->AM == AM65_IMM &&
2483 !CE_HasLabel (L[1]) &&
2484 L[2]->OPC == OP65_LDY &&
2485 CE_KnownImm (L[2]) &&
2486 !CE_HasLabel (L[2]) &&
2487 L[3]->OPC == OP65_CLC &&
2488 !CE_HasLabel (L[3]) &&
2489 L[4]->OPC == OP65_ADC &&
2490 L[4]->AM == AM65_ZP_INDY &&
2491 !CE_HasLabel (L[4]) &&
2492 (L[5]->OPC == OP65_BCC || L[5]->OPC == OP65_JCC) &&
2493 L[5]->JumpTo != 0 &&
2494 L[5]->JumpTo->Owner == L[7] &&
2495 !CE_HasLabel (L[5]) &&
2496 L[6]->OPC == OP65_INX &&
2497 !CE_HasLabel (L[6]) &&
2498 L[7]->OPC == OP65_LDY &&
2499 CE_KnownImm (L[7]) &&
2501 L[8]->OPC == OP65_JSR &&
2502 strcmp (L[8]->Arg, "ldauidx") == 0 &&
2503 !CE_HasLabel (L[8]) &&
2504 /* Check the label last because this is quite costly */
2505 (Len = strlen (L[0]->Arg)) > 3 &&
2506 L[0]->Arg[0] == '<' &&
2507 L[0]->Arg[1] == '(' &&
2508 strlen (L[1]->Arg) == Len &&
2509 L[1]->Arg[0] == '>' &&
2510 memcmp (L[0]->Arg+1, L[1]->Arg+1, Len-1) == 0) {
2516 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, L[4]->Arg, 0, L[0]->LI);
2517 CS_InsertEntry (S, X, I+3);
2520 X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI);
2521 CS_InsertEntry (S, X, I+4);
2524 Label = memcpy (xmalloc (Len-2), L[0]->Arg+2, Len-3);
2525 Label[Len-3] = '\0';
2526 X = NewCodeEntry (OP65_LDA, AM65_ABSX, Label, 0, L[0]->LI);
2527 CS_InsertEntry (S, X, I+5);
2531 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[0]->LI);
2532 CS_InsertEntry (S, X, I+6);
2534 /* Remove the old code */
2535 CS_DelEntries (S, I, 2);
2536 CS_DelEntries (S, I+5, 6);
2538 /* Remember, we had changes */
2548 /* Return the number of changes made */
2554 static unsigned OptPtrLoad6 (CodeSeg* S)
2555 /* Search for the sequence
2560 * and replace it by:
2568 * This step must be execute *after* OptPtrLoad1!
2571 unsigned Changes = 0;
2573 /* Walk over the entries */
2575 while (I < CS_GetEntryCount (S)) {
2579 /* Get next entry */
2580 L[0] = CS_GetEntry (S, I);
2582 /* Check for the sequence */
2583 if (L[0]->OPC == OP65_LDY &&
2584 CS_GetEntries (S, L+1, I+1, 1) &&
2585 L[1]->OPC == OP65_JSR &&
2586 strcmp (L[1]->Arg, "ldauidx") == 0 &&
2587 !CE_HasLabel (L[1])) {
2591 /* Store the high byte */
2592 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
2593 CS_InsertEntry (S, X, I+1);
2595 /* Store the low byte */
2596 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
2597 CS_InsertEntry (S, X, I+2);
2599 /* Delete the call to ldauidx */
2600 CS_DelEntry (S, I+3);
2602 /* Load the high and low byte */
2603 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[0]->LI);
2604 CS_InsertEntry (S, X, I+3);
2605 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[0]->LI);
2606 CS_InsertEntry (S, X, I+4);
2608 /* Remember, we had changes */
2618 /* Return the number of changes made */
2624 /*****************************************************************************/
2626 /*****************************************************************************/
2630 /* Types of optimization steps */
2632 optPre, /* Repeated once */
2633 optPreMain, /* Repeated more than once */
2635 optPostMain, /* dito */
2636 optPost /* Repeated once */
2639 /* Table with all the optimization functions */
2640 typedef struct OptFunc OptFunc;
2642 unsigned (*Func) (CodeSeg*); /* Optimizer function */
2643 const char* Name; /* Name of optimizer step */
2644 unsigned char Type; /* Type of this step */
2645 char Disabled; /* True if pass disabled */
2648 /* Macro that builds a table entry */
2649 #define OptEntry(func,type) { func, #func, type, 0 }
2651 /* Table with optimizer steps */
2652 static OptFunc OptFuncs [] = {
2653 /* Optimizes stores through pointers */
2654 OptEntry (OptPtrStore1, optPre),
2655 OptEntry (OptPtrStore2, optPre),
2656 /* Optimize loads through pointers */
2657 OptEntry (OptPtrLoad1, optPre),
2658 OptEntry (OptPtrLoad2, optPre),
2659 OptEntry (OptPtrLoad3, optPre),
2660 OptEntry (OptPtrLoad4, optPre),
2661 OptEntry (OptPtrLoad5, optPre),
2662 OptEntry (OptPtrLoad6, optMain),
2663 /* Optimize calls to nega */
2664 OptEntry (OptNegA1, optMain),
2665 OptEntry (OptNegA2, optMain),
2666 /* Optimize calls to negax */
2667 OptEntry (OptNegAX1, optPre),
2668 OptEntry (OptNegAX2, optPre),
2669 OptEntry (OptNegAX3, optPre),
2670 OptEntry (OptNegAX4, optPre),
2671 /* Optimize subtractions */
2672 OptEntry (OptSub1, optMain),
2673 OptEntry (OptSub2, optMain),
2674 /* Optimize additions */
2675 OptEntry (OptAdd1, optPre),
2676 OptEntry (OptAdd2, optPre),
2677 OptEntry (OptAdd3, optMain),
2678 /* Optimize shifts */
2679 OptEntry (OptShift1, optPre),
2680 /* Optimize jump cascades */
2681 OptEntry (OptJumpCascades, optMain),
2682 /* Remove dead jumps */
2683 OptEntry (OptDeadJumps, optMain),
2684 /* Change jsr/rts to jmp */
2685 OptEntry (OptRTS, optMain),
2686 /* Remove dead code */
2687 OptEntry (OptDeadCode, optMain),
2688 /* Optimize jump targets */
2689 OptEntry (OptJumpTarget, optMain),
2690 /* Optimize conditional branches */
2691 OptEntry (OptCondBranches, optMain),
2692 /* Replace jumps to RTS by RTS */
2693 OptEntry (OptRTSJumps, optMain),
2694 /* Remove calls to the bool transformer subroutines */
2695 OptEntry (OptBoolTransforms, optMain),
2696 /* Optimize compares */
2697 OptEntry (OptCmp1, optMain),
2698 OptEntry (OptCmp2, optMain),
2699 OptEntry (OptCmp3, optMain),
2700 OptEntry (OptCmp4, optMain),
2701 OptEntry (OptCmp5, optMain),
2702 OptEntry (OptCmp6, optMain),
2703 OptEntry (OptCmp7, optMain),
2704 /* Optimize tests */
2705 OptEntry (OptTest1, optMain),
2706 /* Remove unused loads */
2707 OptEntry (OptUnusedLoads, optMain),
2708 OptEntry (OptUnusedStores, optMain),
2709 OptEntry (OptDuplicateLoads, optMain),
2710 OptEntry (OptStoreLoad, optMain),
2711 OptEntry (OptTransfers, optMain),
2712 /* Optimize branch distance */
2713 OptEntry (OptBranchDist, optPost),
2718 static OptFunc* FindOptStep (const char* Name)
2719 /* Find an optimizer step by name in the table and return a pointer. Print an
2720 * error and call AbEnd if not found.
2725 /* Run all optimization steps */
2726 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2727 if (strcmp (OptFuncs[I].Name, Name) == 0) {
2734 AbEnd ("Optimization step `%s' not found", Name);
2740 void DisableOpt (const char* Name)
2741 /* Disable the optimization with the given name */
2743 if (strcmp (Name, "any") == 0) {
2745 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2746 OptFuncs[I].Disabled = 1;
2749 OptFunc* F = FindOptStep (Name);
2756 void EnableOpt (const char* Name)
2757 /* Enable the optimization with the given name */
2759 if (strcmp (Name, "any") == 0) {
2761 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2762 OptFuncs[I].Disabled = 0;
2765 OptFunc* F = FindOptStep (Name);
2772 void ListOptSteps (FILE* F)
2773 /* List all optimization steps */
2776 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2777 fprintf (F, "%s\n", OptFuncs[I].Name);
2783 static void RepeatOptStep (CodeSeg* S, unsigned char Type, unsigned Max)
2784 /* Repeat the optimizer step of type Type at may Max times */
2788 unsigned OptChanges;
2790 /* Repeat max times of until there are no more changes */
2792 /* Reset the number of changes */
2795 /* Keep the user hapy */
2796 Print (stdout, 1, " Optimizer pass %u:\n", ++Pass);
2798 /* Run all optimization steps */
2799 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2801 /* Get the table entry */
2802 const OptFunc* F = OptFuncs + I;
2804 /* Check if the type matches */
2805 if (F->Type != Type) {
2810 /* Print the name of the following optimizer step */
2811 Print (stdout, 1, " %s:%*s", F->Name, (int) (30-strlen(F->Name)), "");
2813 /* Check if the step is disabled */
2815 Print (stdout, 1, "Disabled\n");
2817 unsigned Changes = F->Func (S);
2818 OptChanges += Changes;
2819 Print (stdout, 1, "%u Changes\n", Changes);
2823 } while (--Max > 0 && OptChanges > 0);
2828 void RunOpt (CodeSeg* S)
2829 /* Run the optimizer */
2832 /* If we shouldn't run the optimizer, bail out */
2837 /* Print the name of the function we are working on */
2839 Print (stdout, 1, "Running optimizer for function `%s'\n", S->Func->Name);
2841 Print (stdout, 1, "Running optimizer for global code segment\n");
2844 /* Repeat all steps until there are no more changes */
2845 RepeatOptStep (S, optPre, 1);
2846 RepeatOptStep (S, optPreMain, 0xFFFF);
2847 RepeatOptStep (S, optMain, 0xFFFF);
2848 RepeatOptStep (S, optPostMain, 0xFFFF);
2849 RepeatOptStep (S, optPost, 1);