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 /*****************************************************************************/
55 /*****************************************************************************/
57 /*****************************************************************************/
61 /* Defines for the conditions in a compare */
76 /* Table with the compare suffixes */
77 static const char CmpSuffixTab [][4] = {
78 "eq", "ne", "gt", "ge", "lt", "le", "ugt", "uge", "ult", "ule"
81 /* Table used to invert a condition, indexed by condition */
82 static const unsigned char CmpInvertTab [] = {
84 CMP_LE, CMP_LT, CMP_GE, CMP_GT,
85 CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT
88 /* Table to show which compares are signed (use the N flag) */
89 static const char CmpSignedTab [] = {
90 0, 0, 1, 1, 1, 1, 0, 0, 0, 0
95 /*****************************************************************************/
96 /* Helper functions */
97 /*****************************************************************************/
101 static cmp_t FindCmpCond (const char* Code, unsigned CodeLen)
102 /* Search for a compare condition by the given code using the given length */
107 for (I = 0; I < sizeof (CmpSuffixTab) / sizeof (CmpSuffixTab [0]); ++I) {
108 if (strncmp (Code, CmpSuffixTab [I], CodeLen) == 0) {
120 static cmp_t FindBoolCmpCond (const char* Name)
121 /* Map a condition suffix to a code. Return the code or CMP_INV on failure */
123 /* Check for the correct subroutine name */
124 if (strncmp (Name, "bool", 4) == 0) {
125 /* Name is ok, search for the code in the table */
126 return FindCmpCond (Name+4, strlen(Name)-4);
135 static cmp_t FindTosCmpCond (const char* Name)
136 /* Check if this is a call to one of the TOS compare functions (tosgtax).
137 * Return the condition code or CMP_INV on failure.
140 unsigned Len = strlen (Name);
142 /* Check for the correct subroutine name */
143 if (strncmp (Name, "tos", 3) == 0 && strcmp (Name+Len-2, "ax") == 0) {
144 /* Name is ok, search for the code in the table */
145 return FindCmpCond (Name+3, Len-3-2);
154 static void ReplaceCmp (CodeSeg* S, unsigned I, cmp_t Cond)
155 /* Helper function for the replacement of routines that return a boolean
156 * followed by a conditional jump. Instead of the boolean value, the condition
157 * codes are evaluated directly.
158 * I is the index of the conditional branch, the sequence is already checked
166 CodeEntry* E = CS_GetEntry (S, I);
168 /* Replace the conditional branch */
172 CE_ReplaceOPC (E, OP65_JEQ);
176 CE_ReplaceOPC (E, OP65_JNE);
185 if ((N = CS_GetNextEntry (S, I)) == 0) {
187 Internal ("Invalid program flow");
189 L = CS_GenLabel (S, N);
190 N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI);
191 CS_InsertEntry (S, N, I);
192 CE_ReplaceOPC (E, OP65_JPL);
196 CE_ReplaceOPC (E, OP65_JPL);
200 CE_ReplaceOPC (E, OP65_JMI);
208 CE_ReplaceOPC (E, OP65_JMI);
210 N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI);
211 CS_InsertEntry (S, N, I+1);
220 if ((N = CS_GetNextEntry (S, I)) == 0) {
222 Internal ("Invalid program flow");
224 L = CS_GenLabel (S, N);
225 N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI);
226 CS_InsertEntry (S, N, I);
227 CE_ReplaceOPC (E, OP65_JCS);
231 CE_ReplaceOPC (E, OP65_JCS);
235 CE_ReplaceOPC (E, OP65_JCC);
243 CE_ReplaceOPC (E, OP65_JCC);
245 N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI);
246 CS_InsertEntry (S, N, I+1);
250 Internal ("Unknown jump condition: %d", Cond);
258 static int IsCmpToZero (const CodeEntry* E)
259 /* Check if the given instrcuction is a compare to zero instruction */
261 return (E->OPC == OP65_CMP &&
263 (E->Flags & CEF_NUMARG) != 0 &&
269 static int IsSpLoad (const CodeEntry* E)
270 /* Return true if this is the load of A from the stack */
272 return E->OPC == OP65_LDA && E->AM == AM65_ZP_INDY && strcmp (E->Arg, "sp") == 0;
277 static int IsLocalLoad16 (CodeSeg* S, unsigned Index,
278 CodeEntry** L, unsigned Count)
279 /* Check if a 16 bit load of a local variable follows:
287 * If so, read Count entries following the first ldy into L and return true
288 * if this is possible. Otherwise return false.
291 /* Be sure we read enough entries for the check */
294 /* Read the first entry */
295 L[0] = CS_GetEntry (S, Index);
297 /* Check for the sequence */
298 return (L[0]->OPC == OP65_LDY &&
299 L[0]->AM == AM65_IMM &&
300 (L[0]->Flags & CEF_NUMARG) != 0 &&
301 CS_GetEntries (S, L+1, Index+1, Count-1) &&
303 !CE_HasLabel (L[1]) &&
304 L[2]->OPC == OP65_TAX &&
305 !CE_HasLabel (L[2]) &&
306 L[3]->OPC == OP65_DEY &&
307 !CE_HasLabel (L[3]) &&
309 !CE_HasLabel (L[4]));
314 static int IsImmCmp16 (CodeSeg* S, CodeEntry** L)
315 /* Check if the instructions at L are an immidiate compare of a/x:
320 return (L[0]->OPC == OP65_CPX &&
321 L[0]->AM == AM65_IMM &&
322 (L[0]->Flags & CEF_NUMARG) != 0 &&
323 !CE_HasLabel (L[0]) &&
324 (L[1]->OPC == OP65_JNE || L[1]->OPC == OP65_BNE) &&
326 !CE_HasLabel (L[1]) &&
327 L[2]->OPC == OP65_CMP &&
328 L[2]->AM == AM65_IMM &&
329 (L[2]->Flags & CEF_NUMARG) != 0 &&
330 (L[3]->Info & OF_ZBRA) != 0 &&
332 (L[1]->JumpTo->Owner == L[3] || L[1]->JumpTo == L[3]->JumpTo));
337 /*****************************************************************************/
338 /* Remove calls to the bool transformer subroutines */
339 /*****************************************************************************/
343 static unsigned OptBoolTransforms (CodeSeg* S)
344 /* Try to remove the call to boolean transformer routines where the call is
348 unsigned Changes = 0;
350 /* Walk over the entries */
352 while (I < CS_GetEntryCount (S)) {
358 CodeEntry* E = CS_GetEntry (S, I);
360 /* Check for a boolean transformer */
361 if (E->OPC == OP65_JSR &&
362 (Cond = FindBoolCmpCond (E->Arg)) != CMP_INV &&
363 (N = CS_GetNextEntry (S, I)) != 0 &&
364 (N->Info & OF_ZBRA) != 0) {
366 /* Make the boolean transformer unnecessary by changing the
367 * the conditional jump to evaluate the condition flags that
368 * are set after the compare directly. Note: jeq jumps if
369 * the condition is not met, jne jumps if the condition is met.
370 * Invert the code if we jump on condition not met.
372 if (GetBranchCond (N->OPC) == BC_EQ) {
373 /* Jumps if condition false, invert condition */
374 Cond = CmpInvertTab [Cond];
377 /* Check if we can replace the code by something better */
378 ReplaceCmp (S, I+1, Cond);
380 /* Remove the call to the bool transformer */
383 /* Remember, we had changes */
393 /* Return the number of changes made */
399 /*****************************************************************************/
400 /* Optimize subtractions */
401 /*****************************************************************************/
405 static unsigned OptSub1 (CodeSeg* S)
406 /* Search for the sequence
413 * and remove the handling of the high byte if X is not used later.
416 unsigned Changes = 0;
418 /* Walk over the entries */
420 while (I < CS_GetEntryCount (S)) {
425 CodeEntry* E = CS_GetEntry (S, I);
427 /* Check for the sequence */
428 if (E->OPC == OP65_SBC &&
429 CS_GetEntries (S, L, I+1, 3) &&
430 (L[0]->OPC == OP65_BCS || L[0]->OPC == OP65_JCS) &&
432 !CE_HasLabel (L[0]) &&
433 L[1]->OPC == OP65_DEX &&
434 !CE_HasLabel (L[1]) &&
435 L[0]->JumpTo->Owner == L[2] &&
436 !RegXUsed (S, I+3)) {
438 /* Remove the bcs/dex */
439 CS_DelEntries (S, I+1, 2);
441 /* Remember, we had changes */
451 /* Return the number of changes made */
457 static unsigned OptSub2 (CodeSeg* S)
458 /* Search for the sequence
475 unsigned Changes = 0;
477 /* Walk over the entries */
479 while (I < CS_GetEntryCount (S)) {
484 CodeEntry* E = CS_GetEntry (S, I);
486 /* Check for the sequence */
487 if (E->OPC == OP65_LDA &&
488 CS_GetEntries (S, L, I+1, 5) &&
489 L[0]->OPC == OP65_SEC &&
490 !CE_HasLabel (L[0]) &&
491 L[1]->OPC == OP65_STA &&
492 strcmp (L[1]->Arg, "tmp1") == 0 &&
493 !CE_HasLabel (L[1]) &&
494 L[2]->OPC == OP65_LDA &&
495 !CE_HasLabel (L[2]) &&
496 L[3]->OPC == OP65_SBC &&
497 strcmp (L[3]->Arg, "tmp1") == 0 &&
498 !CE_HasLabel (L[3]) &&
499 L[4]->OPC == OP65_STA &&
500 strcmp (L[4]->Arg, L[2]->Arg) == 0 &&
501 !CE_HasLabel (L[4])) {
503 /* Remove the store to tmp1 */
504 CS_DelEntry (S, I+2);
506 /* Remove the subtraction */
507 CS_DelEntry (S, I+3);
509 /* Move the lda to the position of the subtraction and change the
512 CS_MoveEntry (S, I, I+3);
513 CE_ReplaceOPC (E, OP65_SBC);
515 /* If the sequence head had a label, move this label back to the
518 if (CE_HasLabel (E)) {
519 CS_MoveLabels (S, E, L[0]);
522 /* Remember, we had changes */
532 /* Return the number of changes made */
538 /*****************************************************************************/
539 /* Optimize additions */
540 /*****************************************************************************/
544 static unsigned OptAdd1 (CodeSeg* S)
545 /* Search for the sequence
563 unsigned Changes = 0;
565 /* Walk over the entries */
567 while (I < CS_GetEntryCount (S)) {
572 CodeEntry* E = CS_GetEntry (S, I);
574 /* Check for the sequence */
575 if (E->OPC == OP65_JSR &&
576 strcmp (E->Arg, "pushax") == 0 &&
577 CS_GetEntries (S, L, I+1, 5) &&
578 L[0]->OPC == OP65_LDY &&
579 !CE_HasLabel (L[0]) &&
580 L[1]->OPC == OP65_LDX &&
581 CE_KnownImm (L[1]) &&
583 !CE_HasLabel (L[1]) &&
584 L[2]->OPC == OP65_LDA &&
585 !CE_HasLabel (L[2]) &&
586 L[3]->OPC == OP65_JSR &&
587 strcmp (L[3]->Arg, "tosaddax") == 0 &&
588 !CE_HasLabel (L[3])) {
593 /* Remove the call to pushax */
597 X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, L[3]->LI);
598 CS_InsertEntry (S, X, I+1);
600 /* Remove the load */
601 CS_DelEntry (S, I+3); /* lda */
602 CS_DelEntry (S, I+2); /* ldx */
605 X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp", 0, L[3]->LI);
606 CS_InsertEntry (S, X, I+2);
608 /* Generate the branch label and the branch */
609 Label = CS_GenLabel (S, L[4]);
610 X = NewCodeEntry (OP65_BCC, AM65_BRA, Label->Name, Label, L[3]->LI);
611 CS_InsertEntry (S, X, I+3);
613 /* Generate the increment of the high byte */
614 X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, L[3]->LI);
615 CS_InsertEntry (S, X, I+4);
617 /* Delete the call to tosaddax */
618 CS_DelEntry (S, I+5);
620 /* Remember, we had changes */
630 /* Return the number of changes made */
636 static unsigned OptAdd2 (CodeSeg* S)
637 /* Search for the sequence
661 * provided that a/x is not used later.
664 unsigned Changes = 0;
666 /* Walk over the entries */
668 while (I < CS_GetEntryCount (S)) {
673 L[0] = CS_GetEntry (S, I);
675 /* Check for the sequence */
676 if (L[0]->OPC == OP65_LDY &&
677 CE_KnownImm (L[0]) &&
678 CS_GetEntries (S, L+1, I+1, 6) &&
679 L[1]->OPC == OP65_LDA &&
680 L[1]->AM == AM65_ZP_INDY &&
681 !CE_HasLabel (L[1]) &&
682 L[2]->OPC == OP65_TAX &&
683 !CE_HasLabel (L[2]) &&
684 L[3]->OPC == OP65_DEY &&
685 !CE_HasLabel (L[3]) &&
686 L[4]->OPC == OP65_LDA &&
687 L[4]->AM == AM65_ZP_INDY &&
688 !CE_HasLabel (L[4]) &&
689 L[5]->OPC == OP65_LDY &&
690 CE_KnownImm (L[5]) &&
691 !CE_HasLabel (L[5]) &&
692 L[6]->OPC == OP65_JSR &&
693 strcmp (L[6]->Arg, "addeqysp") == 0 &&
694 !CE_HasLabel (L[6]) &&
695 (GetRegInfo (S, I+7) & REG_AX) == 0) {
702 /* Create a replacement for the first LDY */
703 Offs = (int) (L[0]->Num - 1);
704 xsprintf (Buf, sizeof (Buf), "$%02X", Offs);
705 X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[0]->LI);
706 CS_InsertEntry (S, X, I+1);
709 /* Load Y with the low offset of the target variable */
710 X = NewCodeEntry (OP65_LDY, AM65_IMM, L[5]->Arg, 0, L[1]->LI);
711 CS_InsertEntry (S, X, I+2);
714 X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, L[1]->LI);
715 CS_InsertEntry (S, X, I+3);
717 /* Remove the TAX/DEY sequence */
718 CS_DelEntry (S, I+5); /* dey */
719 CS_DelEntry (S, I+4); /* tax */
721 /* Addition of the low byte */
722 X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp", 0, L[4]->LI);
723 CS_InsertEntry (S, X, I+4);
724 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "sp", 0, L[4]->LI);
725 CS_InsertEntry (S, X, I+5);
728 xsprintf (Buf, sizeof (Buf), "$%02X", (Offs+1));
729 X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[4]->LI);
730 CS_InsertEntry (S, X, I+6);
732 /* Addition of the high byte */
733 xsprintf (Buf, sizeof (Buf), "$%02X", (int)(L[5]->Num+1));
734 X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[5]->LI);
735 CS_InsertEntry (S, X, I+8);
736 X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp", 0, L[6]->LI);
737 CS_InsertEntry (S, X, I+9);
738 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "sp", 0, L[6]->LI);
739 CS_InsertEntry (S, X, I+10);
741 /* Delete the remaining stuff */
742 CS_DelEntry (S, I+12);
743 CS_DelEntry (S, I+11);
745 /* Remember, we had changes */
755 /* Return the number of changes made */
761 static unsigned OptAdd3 (CodeSeg* S)
762 /* Search for the sequence
769 * and remove the handling of the high byte if X is not used later.
772 unsigned Changes = 0;
774 /* Walk over the entries */
776 while (I < CS_GetEntryCount (S)) {
781 CodeEntry* E = CS_GetEntry (S, I);
783 /* Check for the sequence */
784 if (E->OPC == OP65_ADC &&
785 CS_GetEntries (S, L, I+1, 3) &&
786 (L[0]->OPC == OP65_BCC || L[0]->OPC == OP65_JCC) &&
788 !CE_HasLabel (L[0]) &&
789 L[1]->OPC == OP65_INX &&
790 !CE_HasLabel (L[1]) &&
791 L[0]->JumpTo->Owner == L[2] &&
792 !RegXUsed (S, I+3)) {
794 /* Remove the bcs/dex */
795 CS_DelEntries (S, I+1, 2);
797 /* Remember, we had changes */
807 /* Return the number of changes made */
813 /*****************************************************************************/
814 /* Optimize shifts */
815 /*****************************************************************************/
819 static unsigned OptShift1 (CodeSeg* S)
820 /* A call to the shlaxN routine may get replaced by one or more asl insns
821 * if the value of X is not used later.
824 unsigned Changes = 0;
826 /* Walk over the entries */
828 while (I < CS_GetEntryCount (S)) {
831 CodeEntry* E = CS_GetEntry (S, I);
833 /* Check for the sequence */
834 if (E->OPC == OP65_JSR &&
835 (strncmp (E->Arg, "shlax", 5) == 0 ||
836 strncmp (E->Arg, "aslax", 5) == 0) &&
837 strlen (E->Arg) == 6 &&
838 IsDigit (E->Arg[5]) &&
839 !RegXUsed (S, I+1)) {
841 /* Insert shift insns */
842 unsigned Count = E->Arg[5] - '0';
844 CodeEntry* X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI);
845 CS_InsertEntry (S, X, I+1);
848 /* Delete the call to shlax */
851 /* Remember, we had changes */
861 /* Return the number of changes made */
867 /*****************************************************************************/
868 /* Optimizations for compares */
869 /*****************************************************************************/
873 static unsigned OptCmp1 (CodeSeg* S)
874 /* Search for the sequence
886 unsigned Changes = 0;
888 /* Walk over the entries */
890 while (I < CS_GetEntryCount (S)) {
895 CodeEntry* E = CS_GetEntry (S, I);
897 /* Check for the sequence */
898 if (E->OPC == OP65_STX &&
899 CS_GetEntries (S, L, I+1, 2) &&
900 L[0]->OPC == OP65_STX &&
901 strcmp (L[0]->Arg, "tmp1") == 0 &&
902 !CE_HasLabel (L[0]) &&
903 L[1]->OPC == OP65_ORA &&
904 strcmp (L[1]->Arg, "tmp1") == 0 &&
905 !CE_HasLabel (L[1])) {
907 /* Remove the remaining instructions */
908 CS_DelEntries (S, I+1, 2);
910 /* Insert the ora instead */
911 CS_InsertEntry (S, NewCodeEntry (OP65_ORA, E->AM, E->Arg, 0, E->LI), I+1);
913 /* Remember, we had changes */
923 /* Return the number of changes made */
929 static unsigned OptCmp2 (CodeSeg* S)
932 * lda/and/ora/eor ...
936 * and remove the cmp.
939 unsigned Changes = 0;
941 /* Walk over the entries */
943 while (I < CS_GetEntryCount (S)) {
948 CodeEntry* E = CS_GetEntry (S, I);
950 /* Check for the sequence */
951 if ((E->OPC == OP65_ADC ||
952 E->OPC == OP65_AND ||
953 E->OPC == OP65_DEA ||
954 E->OPC == OP65_EOR ||
955 E->OPC == OP65_INA ||
956 E->OPC == OP65_LDA ||
957 E->OPC == OP65_ORA ||
958 E->OPC == OP65_PLA ||
959 E->OPC == OP65_SBC ||
960 E->OPC == OP65_TXA ||
961 E->OPC == OP65_TYA) &&
962 CS_GetEntries (S, L, I+1, 2) &&
963 IsCmpToZero (L[0]) &&
964 !CE_HasLabel (L[0]) &&
965 (L[1]->Info & OF_FBRA) != 0 &&
966 !CE_HasLabel (L[1])) {
968 /* Remove the compare */
969 CS_DelEntry (S, I+1);
971 /* Remember, we had changes */
981 /* Return the number of changes made */
987 static unsigned OptCmp3 (CodeSeg* S)
997 * If a is zero, we may remove the compare. If a and b are both zero, we may
998 * replace it by the sequence
1004 * L1 may be either the label at the branch instruction, or the target label
1005 * of this instruction.
1008 unsigned Changes = 0;
1010 /* Walk over the entries */
1012 while (I < CS_GetEntryCount (S)) {
1016 /* Get next entry */
1017 CodeEntry* E = CS_GetEntry (S, I);
1019 /* Check for the sequence */
1020 if (E->OPC == OP65_LDA &&
1021 CS_GetEntries (S, L, I+1, 5) &&
1022 L[0]->OPC == OP65_LDX &&
1023 !CE_HasLabel (L[0]) &&
1024 IsImmCmp16 (S, L+1)) {
1026 if (L[1]->Num == 0 && L[3]->Num == 0) {
1027 /* The value is zero, we may use the simple code version. */
1028 CE_ReplaceOPC (L[0], OP65_ORA);
1029 CS_DelEntries (S, I+2, 3);
1031 /* Move the lda instruction after the first branch. This will
1032 * improve speed, since the load is delayed after the first
1035 CS_MoveEntry (S, I, I+4);
1037 /* We will replace the ldx/cpx by lda/cmp */
1038 CE_ReplaceOPC (L[0], OP65_LDA);
1039 CE_ReplaceOPC (L[1], OP65_CMP);
1041 /* Beware: If the first LDA instruction had a label, we have
1042 * to move this label to the top of the sequence again.
1044 if (CE_HasLabel (E)) {
1045 CS_MoveLabels (S, E, L[0]);
1058 /* Return the number of changes made */
1064 static unsigned OptCmp4 (CodeSeg* S)
1065 /* Optimize compares of local variables:
1078 unsigned Changes = 0;
1080 /* Walk over the entries */
1082 while (I < CS_GetEntryCount (S)) {
1086 /* Check for the sequence */
1087 if (IsLocalLoad16 (S, I, L, 9) && IsImmCmp16 (S, L+5)) {
1089 if (L[5]->Num == 0 && L[7]->Num == 0) {
1091 /* The value is zero, we may use the simple code version:
1098 CE_ReplaceOPC (L[4], OP65_ORA);
1099 CS_DelEntries (S, I+5, 3); /* cpx/bne/cmp */
1100 CS_DelEntry (S, I+2); /* tax */
1104 /* Change the code to just use the A register. Move the load
1105 * of the low byte after the first branch if possible:
1116 CS_DelEntry (S, I+2); /* tax */
1117 CE_ReplaceOPC (L[5], OP65_CMP); /* cpx -> cmp */
1118 CS_MoveEntry (S, I+4, I+2); /* cmp */
1119 CS_MoveEntry (S, I+5, I+3); /* bne */
1131 /* Return the number of changes made */
1137 static unsigned OptCmp5 (CodeSeg* S)
1138 /* Search for calls to compare subroutines followed by a conditional branch
1139 * and replace them by cheaper versions, since the branch means that the
1140 * boolean value returned by these routines is not needed (we may also check
1141 * that explicitly, but for the current code generator it is always true).
1144 unsigned Changes = 0;
1146 /* Walk over the entries */
1148 while (I < CS_GetEntryCount (S)) {
1153 /* Get next entry */
1154 CodeEntry* E = CS_GetEntry (S, I);
1156 /* Check for the sequence */
1157 if (E->OPC == OP65_JSR &&
1158 (Cond = FindTosCmpCond (E->Arg)) != CMP_INV &&
1159 (N = CS_GetNextEntry (S, I)) != 0 &&
1160 (N->Info & OF_ZBRA) != 0 &&
1163 /* The tos... functions will return a boolean value in a/x and
1164 * the Z flag says if this value is zero or not. We will call
1165 * a cheaper subroutine instead, one that does not return a
1166 * boolean value but only valid flags. Note: jeq jumps if
1167 * the condition is not met, jne jumps if the condition is met.
1168 * Invert the code if we jump on condition not met.
1170 if (GetBranchCond (N->OPC) == BC_EQ) {
1171 /* Jumps if condition false, invert condition */
1172 Cond = CmpInvertTab [Cond];
1175 /* Replace the subroutine call. */
1176 E = NewCodeEntry (OP65_JSR, AM65_ABS, "tosicmp", 0, E->LI);
1177 CS_InsertEntry (S, E, I+1);
1180 /* Replace the conditional branch */
1181 ReplaceCmp (S, I+1, Cond);
1183 /* Remember, we had changes */
1193 /* Return the number of changes made */
1199 static unsigned OptCmp6 (CodeSeg* S)
1200 /* Search for a sequence ldx/txa/branch and remove the txa if A is not
1204 unsigned Changes = 0;
1206 /* Walk over the entries */
1208 while (I < CS_GetEntryCount (S)) {
1212 /* Get next entry */
1213 CodeEntry* E = CS_GetEntry (S, I);
1215 /* Check for the sequence */
1216 if ((E->OPC == OP65_LDX) &&
1217 CS_GetEntries (S, L, I+1, 2) &&
1218 L[0]->OPC == OP65_TXA &&
1219 !CE_HasLabel (L[0]) &&
1220 (L[1]->Info & OF_FBRA) != 0 &&
1221 !CE_HasLabel (L[1]) &&
1222 !RegAUsed (S, I+3)) {
1224 /* Remove the txa */
1225 CS_DelEntry (S, I+1);
1227 /* Remember, we had changes */
1237 /* Return the number of changes made */
1243 /*****************************************************************************/
1244 /* Optimize tests */
1245 /*****************************************************************************/
1249 static unsigned OptTest1 (CodeSeg* S)
1256 * if X is zero, the sequence may be changed to
1261 * which may be optimized further by another step.
1263 * If A is zero, the sequence may be changed to
1270 unsigned Changes = 0;
1273 /* Generate register info for this step */
1276 /* Walk over the entries */
1278 while (I < CS_GetEntryCount (S)) {
1282 /* Get next entry */
1283 L[0] = CS_GetEntry (S, I);
1285 /* Check if it's the sequence we're searching for */
1286 if (L[0]->OPC == OP65_STX &&
1287 CS_GetEntries (S, L+1, I+1, 2) &&
1288 !CE_HasLabel (L[1]) &&
1289 L[1]->OPC == OP65_ORA &&
1290 strcmp (L[0]->Arg, L[1]->Arg) == 0 &&
1291 !CE_HasLabel (L[2]) &&
1292 (L[2]->Info & OF_ZBRA) != 0) {
1294 /* Check if X is zero */
1295 if (L[0]->RI->In.RegX == 0) {
1297 /* Insert the compare */
1298 CodeEntry* N = NewCodeEntry (OP65_CMP, AM65_IMM, "$00", 0, L[0]->LI);
1299 CS_InsertEntry (S, N, I+2);
1301 /* Remove the two other insns */
1302 CS_DelEntry (S, I+1);
1305 /* We had changes */
1308 /* Check if A is zero */
1309 } else if (L[1]->RI->In.RegA == 0) {
1311 /* Insert the txa */
1312 CodeEntry* N = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, L[1]->LI);
1313 CS_InsertEntry (S, N, I+2);
1315 /* Remove the two other insns */
1316 CS_DelEntry (S, I+1);
1319 /* We had changes */
1329 /* Free register info */
1332 /* Return the number of changes made */
1338 /*****************************************************************************/
1339 /* nega optimizations */
1340 /*****************************************************************************/
1344 static unsigned OptNegA1 (CodeSeg* S)
1351 * Remove the ldx if the lda does not use it.
1354 unsigned Changes = 0;
1356 /* Walk over the entries */
1358 while (I < CS_GetEntryCount (S)) {
1362 /* Get next entry */
1363 CodeEntry* E = CS_GetEntry (S, I);
1365 /* Check for a ldx */
1366 if (E->OPC == OP65_LDX &&
1367 E->AM == AM65_IMM &&
1368 (E->Flags & CEF_NUMARG) != 0 &&
1370 CS_GetEntries (S, L, I+1, 2) &&
1371 L[0]->OPC == OP65_LDA &&
1372 (L[0]->Use & REG_X) == 0 &&
1373 !CE_HasLabel (L[0]) &&
1374 L[1]->OPC == OP65_JSR &&
1375 strcmp (L[1]->Arg, "bnega") == 0 &&
1376 !CE_HasLabel (L[1])) {
1378 /* Remove the ldx instruction */
1381 /* Remember, we had changes */
1391 /* Return the number of changes made */
1397 static unsigned OptNegA2 (CodeSeg* S)
1404 * Adjust the conditional branch and remove the call to the subroutine.
1407 unsigned Changes = 0;
1409 /* Walk over the entries */
1411 while (I < CS_GetEntryCount (S)) {
1415 /* Get next entry */
1416 CodeEntry* E = CS_GetEntry (S, I);
1418 /* Check for the sequence */
1419 if ((E->OPC == OP65_ADC ||
1420 E->OPC == OP65_AND ||
1421 E->OPC == OP65_DEA ||
1422 E->OPC == OP65_EOR ||
1423 E->OPC == OP65_INA ||
1424 E->OPC == OP65_LDA ||
1425 E->OPC == OP65_ORA ||
1426 E->OPC == OP65_PLA ||
1427 E->OPC == OP65_SBC ||
1428 E->OPC == OP65_TXA ||
1429 E->OPC == OP65_TYA) &&
1430 CS_GetEntries (S, L, I+1, 2) &&
1431 L[0]->OPC == OP65_JSR &&
1432 strcmp (L[0]->Arg, "bnega") == 0 &&
1433 !CE_HasLabel (L[0]) &&
1434 (L[1]->Info & OF_ZBRA) != 0 &&
1435 !CE_HasLabel (L[1])) {
1437 /* Invert the branch */
1438 CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
1440 /* Delete the subroutine call */
1441 CS_DelEntry (S, I+1);
1443 /* Remember, we had changes */
1453 /* Return the number of changes made */
1459 /*****************************************************************************/
1460 /* negax optimizations */
1461 /*****************************************************************************/
1465 static unsigned OptNegAX1 (CodeSeg* S)
1466 /* On a call to bnegax, if X is zero, the result depends only on the value in
1467 * A, so change the call to a call to bnega. This will get further optimized
1468 * later if possible.
1471 unsigned Changes = 0;
1474 /* Generate register info for this step */
1477 /* Walk over the entries */
1479 while (I < CS_GetEntryCount (S)) {
1481 /* Get next entry */
1482 CodeEntry* E = CS_GetEntry (S, I);
1484 /* Check if this is a call to bnegax, and if X is known and zero */
1485 if (E->OPC == OP65_JSR &&
1486 E->RI->In.RegX == 0 &&
1487 strcmp (E->Arg, "bnegax") == 0) {
1489 /* We're cheating somewhat here ... */
1493 /* We had changes */
1502 /* Free register info */
1505 /* Return the number of changes made */
1511 static unsigned OptNegAX2 (CodeSeg* S)
1512 /* Search for the sequence:
1529 unsigned Changes = 0;
1531 /* Walk over the entries */
1533 while (I < CS_GetEntryCount (S)) {
1537 /* Get next entry */
1538 CodeEntry* E = CS_GetEntry (S, I);
1540 /* Check for the sequence */
1541 if (E->OPC == OP65_LDA &&
1542 E->AM == AM65_ZP_INDY &&
1543 CS_GetEntries (S, L, I+1, 5) &&
1544 L[0]->OPC == OP65_TAX &&
1545 L[1]->OPC == OP65_DEY &&
1546 L[2]->OPC == OP65_LDA &&
1547 L[2]->AM == AM65_ZP_INDY &&
1548 strcmp (L[2]->Arg, E->Arg) == 0 &&
1549 !CE_HasLabel (L[2]) &&
1550 L[3]->OPC == OP65_JSR &&
1551 strcmp (L[3]->Arg, "bnegax") == 0 &&
1552 !CE_HasLabel (L[3]) &&
1553 (L[4]->Info & OF_ZBRA) != 0 &&
1554 !CE_HasLabel (L[4])) {
1557 CE_ReplaceOPC (L[2], OP65_ORA);
1559 /* Invert the branch */
1560 CE_ReplaceOPC (L[4], GetInverseBranch (L[4]->OPC));
1562 /* Delete the entries no longer needed. Beware: Deleting entries
1563 * will change the indices.
1565 CS_DelEntry (S, I+4); /* jsr bnegax */
1566 CS_DelEntry (S, I+1); /* tax */
1568 /* Remember, we had changes */
1578 /* Return the number of changes made */
1584 static unsigned OptNegAX3 (CodeSeg* S)
1585 /* Search for the sequence:
1599 unsigned Changes = 0;
1601 /* Walk over the entries */
1603 while (I < CS_GetEntryCount (S)) {
1607 /* Get next entry */
1608 CodeEntry* E = CS_GetEntry (S, I);
1610 /* Check for the sequence */
1611 if (E->OPC == OP65_LDA &&
1612 CS_GetEntries (S, L, I+1, 3) &&
1613 L[0]->OPC == OP65_LDX &&
1614 !CE_HasLabel (L[0]) &&
1615 L[1]->OPC == OP65_JSR &&
1616 strcmp (L[1]->Arg, "bnegax") == 0 &&
1617 !CE_HasLabel (L[1]) &&
1618 (L[2]->Info & OF_ZBRA) != 0 &&
1619 !CE_HasLabel (L[2])) {
1622 CE_ReplaceOPC (L[0], OP65_ORA);
1624 /* Invert the branch */
1625 CE_ReplaceOPC (L[2], GetInverseBranch (L[2]->OPC));
1627 /* Delete the subroutine call */
1628 CS_DelEntry (S, I+2);
1630 /* Remember, we had changes */
1640 /* Return the number of changes made */
1646 static unsigned OptNegAX4 (CodeSeg* S)
1647 /* Search for the sequence:
1653 * and replace it by:
1660 unsigned Changes = 0;
1662 /* Walk over the entries */
1664 while (I < CS_GetEntryCount (S)) {
1668 /* Get next entry */
1669 CodeEntry* E = CS_GetEntry (S, I);
1671 /* Check for the sequence */
1672 if (E->OPC == OP65_JSR &&
1673 CS_GetEntries (S, L, I+1, 2) &&
1674 L[0]->OPC == OP65_JSR &&
1675 strncmp (L[0]->Arg,"bnega",5) == 0 &&
1676 !CE_HasLabel (L[0]) &&
1677 (L[1]->Info & OF_ZBRA) != 0 &&
1678 !CE_HasLabel (L[1])) {
1682 /* Check if we're calling bnega or bnegax */
1683 int ByteSized = (strcmp (L[0]->Arg, "bnega") == 0);
1685 /* Insert apropriate test code */
1688 X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI);
1689 CS_InsertEntry (S, X, I+2);
1692 X = NewCodeEntry (OP65_STX, AM65_ZP, "tmp1", 0, L[0]->LI);
1693 CS_InsertEntry (S, X, I+2);
1694 X = NewCodeEntry (OP65_ORA, AM65_ZP, "tmp1", 0, L[0]->LI);
1695 CS_InsertEntry (S, X, I+3);
1698 /* Delete the subroutine call */
1699 CS_DelEntry (S, I+1);
1701 /* Invert the branch */
1702 CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
1704 /* Remember, we had changes */
1714 /* Return the number of changes made */
1720 /*****************************************************************************/
1721 /* Optimize stores through pointers */
1722 /*****************************************************************************/
1726 static unsigned OptPtrStore1Sub (CodeSeg* S, unsigned I, CodeEntry** const L)
1727 /* Check if this is one of the allowed suboperation for OptPtrStore1 */
1729 /* Check for a label attached to the entry */
1730 if (CE_HasLabel (L[0])) {
1734 /* Check for single insn sub ops */
1735 if (L[0]->OPC == OP65_AND ||
1736 L[0]->OPC == OP65_EOR ||
1737 L[0]->OPC == OP65_ORA ||
1738 (L[0]->OPC == OP65_JSR && strncmp (L[0]->Arg, "shlax", 5) == 0) ||
1739 (L[0]->OPC == OP65_JSR && strncmp (L[0]->Arg, "shrax", 5) == 0)) {
1744 } else if (L[0]->OPC == OP65_CLC &&
1745 (L[1] = CS_GetNextEntry (S, I)) != 0 &&
1746 L[1]->OPC == OP65_ADC &&
1747 !CE_HasLabel (L[1])) {
1749 } else if (L[0]->OPC == OP65_SEC &&
1750 (L[1] = CS_GetNextEntry (S, I)) != 0 &&
1751 L[1]->OPC == OP65_SBC &&
1752 !CE_HasLabel (L[1])) {
1764 static unsigned OptPtrStore1 (CodeSeg* S)
1765 /* Search for the sequence:
1774 * and replace it by:
1786 unsigned Changes = 0;
1788 /* Walk over the entries */
1790 while (I < CS_GetEntryCount (S)) {
1795 /* Get next entry */
1796 L[0] = CS_GetEntry (S, I);
1798 /* Check for the sequence */
1799 if (L[0]->OPC == OP65_JSR &&
1800 strcmp (L[0]->Arg, "pushax") == 0 &&
1801 CS_GetEntries (S, L+1, I+1, 3) &&
1802 L[1]->OPC == OP65_LDY &&
1803 CE_KnownImm (L[1]) &&
1804 !CE_HasLabel (L[1]) &&
1805 L[2]->OPC == OP65_JSR &&
1806 strcmp (L[2]->Arg, "ldauidx") == 0 &&
1807 !CE_HasLabel (L[2]) &&
1808 (K = OptPtrStore1Sub (S, I+3, L+3)) > 0 &&
1809 CS_GetEntries (S, L+3+K, I+3+K, 2) &&
1810 L[3+K]->OPC == OP65_LDY &&
1811 CE_KnownImm (L[3+K]) &&
1812 !CE_HasLabel (L[3+K]) &&
1813 L[4+K]->OPC == OP65_JSR &&
1814 strcmp (L[4+K]->Arg, "staspidx") == 0 &&
1815 !CE_HasLabel (L[4+K])) {
1819 /* Create and insert the stores */
1820 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
1821 CS_InsertEntry (S, X, I+1);
1823 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
1824 CS_InsertEntry (S, X, I+2);
1826 /* Delete the call to pushax */
1829 /* Delete the call to ldauidx */
1830 CS_DelEntry (S, I+3);
1832 /* Insert the load from ptr1 */
1833 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[3]->LI);
1834 CS_InsertEntry (S, X, I+3);
1835 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[2]->LI);
1836 CS_InsertEntry (S, X, I+4);
1838 /* Insert the store through ptr1 */
1839 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "ptr1", 0, L[3]->LI);
1840 CS_InsertEntry (S, X, I+6+K);
1842 /* Delete the call to staspidx */
1843 CS_DelEntry (S, I+7+K);
1845 /* Remember, we had changes */
1855 /* Return the number of changes made */
1861 static unsigned OptPtrStore2 (CodeSeg* S)
1862 /* Search for the sequence:
1869 * and replace it by:
1878 unsigned Changes = 0;
1880 /* Walk over the entries */
1882 while (I < CS_GetEntryCount (S)) {
1886 /* Get next entry */
1887 L[0] = CS_GetEntry (S, I);
1889 /* Check for the sequence */
1890 if (L[0]->OPC == OP65_JSR &&
1891 strcmp (L[0]->Arg, "pushax") == 0 &&
1892 CS_GetEntries (S, L+1, I+1, 3) &&
1893 L[1]->OPC == OP65_LDA &&
1894 !CE_HasLabel (L[1]) &&
1895 L[2]->OPC == OP65_LDY &&
1896 !CE_HasLabel (L[2]) &&
1897 L[3]->OPC == OP65_JSR &&
1898 strcmp (L[3]->Arg, "staspidx") == 0 &&
1899 !CE_HasLabel (L[3])) {
1903 /* Create and insert the stores */
1904 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
1905 CS_InsertEntry (S, X, I+1);
1907 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
1908 CS_InsertEntry (S, X, I+2);
1910 /* Delete the call to pushax */
1913 /* Insert the store through ptr1 */
1914 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "ptr1", 0, L[3]->LI);
1915 CS_InsertEntry (S, X, I+4);
1917 /* Delete the call to staspidx */
1918 CS_DelEntry (S, I+5);
1920 /* Remember, we had changes */
1930 /* Return the number of changes made */
1936 /*****************************************************************************/
1937 /* Optimize loads through pointers */
1938 /*****************************************************************************/
1942 static unsigned OptPtrLoad1 (CodeSeg* S)
1943 /* Search for the sequence:
1947 * lda (sp),y # May be any destination
1951 * and replace it by:
1962 unsigned Changes = 0;
1964 /* Walk over the entries */
1966 while (I < CS_GetEntryCount (S)) {
1970 /* Get next entry */
1971 L[0] = CS_GetEntry (S, I);
1973 /* Check for the sequence */
1974 if (L[0]->OPC == OP65_TAX &&
1975 CS_GetEntries (S, L+1, I+1, 4) &&
1976 L[1]->OPC == OP65_DEY &&
1977 !CE_HasLabel (L[1]) &&
1978 L[2]->OPC == OP65_LDA &&
1979 !CE_HasLabel (L[2]) &&
1980 L[3]->OPC == OP65_LDY &&
1981 !CE_HasLabel (L[3]) &&
1982 L[4]->OPC == OP65_JSR &&
1983 strcmp (L[4]->Arg, "ldauidx") == 0 &&
1984 !CE_HasLabel (L[4])) {
1988 /* Store the high byte and remove the TAX instead */
1989 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1+1", 0, L[0]->LI);
1990 CS_InsertEntry (S, X, I+1);
1993 /* Store the low byte */
1994 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[2]->LI);
1995 CS_InsertEntry (S, X, I+3);
1997 /* Delete the call to ldauidx */
1998 CS_DelEntry (S, I+5);
2000 /* Load high and low byte */
2001 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[3]->LI);
2002 CS_InsertEntry (S, X, I+5);
2003 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[3]->LI);
2004 CS_InsertEntry (S, X, I+6);
2006 /* Remember, we had changes */
2016 /* Return the number of changes made */
2022 static unsigned OptPtrLoad2 (CodeSeg* S)
2023 /* Search for the sequence:
2034 * and replace it by:
2046 unsigned Changes = 0;
2048 /* Walk over the entries */
2050 while (I < CS_GetEntryCount (S)) {
2054 /* Get next entry */
2055 L[0] = CS_GetEntry (S, I);
2057 /* Check for the sequence */
2058 if (L[0]->OPC == OP65_ADC &&
2059 CS_GetEntries (S, L+1, I+1, 7) &&
2060 L[1]->OPC == OP65_TAY &&
2061 !CE_HasLabel (L[1]) &&
2062 L[2]->OPC == OP65_TXA &&
2063 !CE_HasLabel (L[2]) &&
2064 L[3]->OPC == OP65_ADC &&
2065 !CE_HasLabel (L[3]) &&
2066 L[4]->OPC == OP65_TAX &&
2067 !CE_HasLabel (L[4]) &&
2068 L[5]->OPC == OP65_TYA &&
2069 !CE_HasLabel (L[5]) &&
2070 L[6]->OPC == OP65_LDY &&
2071 !CE_HasLabel (L[6]) &&
2072 L[7]->OPC == OP65_JSR &&
2073 strcmp (L[7]->Arg, "ldauidx") == 0 &&
2074 !CE_HasLabel (L[7])) {
2078 /* Store the low byte and remove the TAY instead */
2079 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
2080 CS_InsertEntry (S, X, I+1);
2081 CS_DelEntry (S, I+2);
2083 /* Store the high byte */
2084 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1+1", 0, L[3]->LI);
2085 CS_InsertEntry (S, X, I+4);
2087 /* Delete more transfer insns */
2088 CS_DelEntry (S, I+6);
2089 CS_DelEntry (S, I+5);
2091 /* Delete the call to ldauidx */
2092 CS_DelEntry (S, I+6);
2094 /* Load high and low byte */
2095 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[6]->LI);
2096 CS_InsertEntry (S, X, I+6);
2097 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[6]->LI);
2098 CS_InsertEntry (S, X, I+7);
2100 /* Remember, we had changes */
2110 /* Return the number of changes made */
2116 static unsigned OptPtrLoad3 (CodeSeg* S)
2117 /* Search for the sequence:
2129 * and replace it by:
2142 unsigned Changes = 0;
2144 /* Walk over the entries */
2146 while (I < CS_GetEntryCount (S)) {
2150 /* Get next entry */
2151 L[0] = CS_GetEntry (S, I);
2153 /* Check for the sequence */
2154 if (L[0]->OPC == OP65_ADC &&
2155 CS_GetEntries (S, L+1, I+1, 8) &&
2156 L[1]->OPC == OP65_PHA &&
2157 !CE_HasLabel (L[1]) &&
2158 L[2]->OPC == OP65_TXA &&
2159 !CE_HasLabel (L[2]) &&
2160 L[3]->OPC == OP65_INY &&
2161 !CE_HasLabel (L[3]) &&
2162 L[4]->OPC == OP65_ADC &&
2163 !CE_HasLabel (L[4]) &&
2164 L[5]->OPC == OP65_TAX &&
2165 !CE_HasLabel (L[5]) &&
2166 L[6]->OPC == OP65_PLA &&
2167 !CE_HasLabel (L[6]) &&
2168 L[7]->OPC == OP65_LDY &&
2169 !CE_HasLabel (L[7]) &&
2170 L[8]->OPC == OP65_JSR &&
2171 strcmp (L[8]->Arg, "ldauidx") == 0 &&
2172 !CE_HasLabel (L[8])) {
2176 /* Store the low byte and remove the PHA instead */
2177 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
2178 CS_InsertEntry (S, X, I+1);
2179 CS_DelEntry (S, I+2);
2181 /* Store the high byte */
2182 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1+1", 0, L[4]->LI);
2183 CS_InsertEntry (S, X, I+5);
2185 /* Delete more transfer and PLA insns */
2186 CS_DelEntry (S, I+7);
2187 CS_DelEntry (S, I+6);
2189 /* Delete the call to ldauidx */
2190 CS_DelEntry (S, I+7);
2192 /* Load high and low byte */
2193 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[6]->LI);
2194 CS_InsertEntry (S, X, I+7);
2195 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[6]->LI);
2196 CS_InsertEntry (S, X, I+8);
2198 /* Remember, we had changes */
2208 /* Return the number of changes made */
2214 static unsigned OptPtrLoad4 (CodeSeg* S)
2215 /* Search for the sequence
2220 * and replace it by:
2228 * This step must be execute *after* OptPtrLoad1!
2231 unsigned Changes = 0;
2233 /* Walk over the entries */
2235 while (I < CS_GetEntryCount (S)) {
2239 /* Get next entry */
2240 L[0] = CS_GetEntry (S, I);
2242 /* Check for the sequence */
2243 if (L[0]->OPC == OP65_LDY &&
2244 CS_GetEntries (S, L+1, I+1, 1) &&
2245 L[1]->OPC == OP65_JSR &&
2246 strcmp (L[1]->Arg, "ldauidx") == 0 &&
2247 !CE_HasLabel (L[1])) {
2251 /* Store the high byte */
2252 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
2253 CS_InsertEntry (S, X, I+1);
2255 /* Store the low byte */
2256 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
2257 CS_InsertEntry (S, X, I+2);
2259 /* Delete the call to ldauidx */
2260 CS_DelEntry (S, I+3);
2262 /* Load the high and low byte */
2263 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[0]->LI);
2264 CS_InsertEntry (S, X, I+3);
2265 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[0]->LI);
2266 CS_InsertEntry (S, X, I+4);
2268 /* Remember, we had changes */
2278 /* Return the number of changes made */
2284 /*****************************************************************************/
2286 /*****************************************************************************/
2290 /* Types of optimization steps */
2292 optPre, /* Repeated once */
2293 optPreMain, /* Repeated more than once */
2295 optPostMain, /* dito */
2296 optPost /* Repeated once */
2299 /* Table with all the optimization functions */
2300 typedef struct OptFunc OptFunc;
2302 unsigned (*Func) (CodeSeg*); /* Optimizer function */
2303 const char* Name; /* Name of optimizer step */
2304 unsigned char Type; /* Type of this step */
2305 char Disabled; /* True if pass disabled */
2308 /* Macro that builds a table entry */
2309 #define OptEntry(func,type) { func, #func, type, 0 }
2311 /* Table with optimizer steps */
2312 static OptFunc OptFuncs [] = {
2313 /* Optimizes stores through pointers */
2314 OptEntry (OptPtrStore1, optPre),
2315 OptEntry (OptPtrStore2, optPre),
2316 /* Optimize loads through pointers */
2317 OptEntry (OptPtrLoad1, optPre),
2318 OptEntry (OptPtrLoad2, optPre),
2319 OptEntry (OptPtrLoad3, optPre),
2320 OptEntry (OptPtrLoad4, optMain),
2321 /* Optimize calls to nega */
2322 OptEntry (OptNegA1, optMain),
2323 OptEntry (OptNegA2, optMain),
2324 /* Optimize calls to negax */
2325 OptEntry (OptNegAX1, optPre),
2326 OptEntry (OptNegAX2, optPre),
2327 OptEntry (OptNegAX3, optPre),
2328 OptEntry (OptNegAX4, optPre),
2329 /* Optimize subtractions */
2330 OptEntry (OptSub1, optMain),
2331 OptEntry (OptSub2, optMain),
2332 /* Optimize additions */
2333 OptEntry (OptAdd1, optPre),
2334 OptEntry (OptAdd2, optPre),
2335 OptEntry (OptAdd3, optMain),
2336 /* Optimize shifts */
2337 OptEntry (OptShift1, optPre),
2338 /* Optimize jump cascades */
2339 OptEntry (OptJumpCascades, optMain),
2340 /* Remove dead jumps */
2341 OptEntry (OptDeadJumps, optMain),
2342 /* Change jsr/rts to jmp */
2343 OptEntry (OptRTS, optMain),
2344 /* Remove dead code */
2345 OptEntry (OptDeadCode, optMain),
2346 /* Optimize jump targets */
2347 OptEntry (OptJumpTarget, optMain),
2348 /* Optimize conditional branches */
2349 OptEntry (OptCondBranches, optMain),
2350 /* Replace jumps to RTS by RTS */
2351 OptEntry (OptRTSJumps, optMain),
2352 /* Remove calls to the bool transformer subroutines */
2353 OptEntry (OptBoolTransforms, optMain),
2354 /* Optimize compares */
2355 OptEntry (OptCmp1, optMain),
2356 OptEntry (OptCmp2, optMain),
2357 OptEntry (OptCmp3, optMain),
2358 OptEntry (OptCmp4, optMain),
2359 OptEntry (OptCmp5, optMain),
2360 OptEntry (OptCmp6, optMain),
2361 /* Optimize tests */
2362 OptEntry (OptTest1, optMain),
2363 /* Remove unused loads */
2364 OptEntry (OptUnusedLoads, optMain),
2365 OptEntry (OptDuplicateLoads, optMain),
2366 OptEntry (OptStoreLoad, optMain),
2367 OptEntry (OptTransfers, optMain),
2368 /* Optimize branch distance */
2369 OptEntry (OptBranchDist, optPost),
2374 static OptFunc* FindOptStep (const char* Name)
2375 /* Find an optimizer step by name in the table and return a pointer. Print an
2376 * error and call AbEnd if not found.
2381 /* Run all optimization steps */
2382 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2383 if (strcmp (OptFuncs[I].Name, Name) == 0) {
2390 AbEnd ("Optimization step `%s' not found", Name);
2396 void DisableOpt (const char* Name)
2397 /* Disable the optimization with the given name */
2399 if (strcmp (Name, "any") == 0) {
2401 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2402 OptFuncs[I].Disabled = 1;
2405 OptFunc* F = FindOptStep (Name);
2412 void EnableOpt (const char* Name)
2413 /* Enable the optimization with the given name */
2415 if (strcmp (Name, "any") == 0) {
2417 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2418 OptFuncs[I].Disabled = 0;
2421 OptFunc* F = FindOptStep (Name);
2428 void ListOptSteps (FILE* F)
2429 /* List all optimization steps */
2432 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2433 fprintf (F, "%s\n", OptFuncs[I].Name);
2439 static void RepeatOptStep (CodeSeg* S, unsigned char Type, unsigned Max)
2440 /* Repeat the optimizer step of type Type at may Max times */
2444 unsigned OptChanges;
2446 /* Repeat max times of until there are no more changes */
2448 /* Reset the number of changes */
2451 /* Keep the user hapy */
2452 Print (stdout, 1, " Optimizer pass %u:\n", ++Pass);
2454 /* Run all optimization steps */
2455 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2457 /* Get the table entry */
2458 const OptFunc* F = OptFuncs + I;
2460 /* Check if the type matches */
2461 if (F->Type != Type) {
2466 /* Print the name of the following optimizer step */
2467 Print (stdout, 1, " %s:%*s", F->Name, (int) (30-strlen(F->Name)), "");
2469 /* Check if the step is disabled */
2471 Print (stdout, 1, "Disabled\n");
2473 unsigned Changes = F->Func (S);
2474 OptChanges += Changes;
2475 Print (stdout, 1, "%u Changes\n", Changes);
2479 } while (--Max > 0 && OptChanges > 0);
2484 void RunOpt (CodeSeg* S)
2485 /* Run the optimizer */
2488 /* If we shouldn't run the optimizer, bail out */
2493 /* Print the name of the function we are working on */
2495 Print (stdout, 1, "Running optimizer for function `%s'\n", S->Func->Name);
2497 Print (stdout, 1, "Running optimizer for global code segment\n");
2500 /* Repeat all steps until there are no more changes */
2501 RepeatOptStep (S, optPre, 1);
2502 RepeatOptStep (S, optPreMain, 0xFFFF);
2503 RepeatOptStep (S, optMain, 0xFFFF);
2504 RepeatOptStep (S, optPostMain, 0xFFFF);
2505 RepeatOptStep (S, optPost, 1);