1 /*****************************************************************************/
5 /* Optimizer subroutines */
9 /* (C) 2001 Ullrich von Bassewitz */
11 /* D-70597 Stuttgart */
12 /* EMail: uz@cc65.org */
15 /* This software is provided 'as-is', without any expressed or implied */
16 /* warranty. In no event will the authors be held liable for any damages */
17 /* arising from the use of this software. */
19 /* Permission is granted to anyone to use this software for any purpose, */
20 /* including commercial applications, and to alter it and redistribute it */
21 /* freely, subject to the following restrictions: */
23 /* 1. The origin of this software must not be misrepresented; you must not */
24 /* claim that you wrote the original software. If you use this software */
25 /* in a product, an acknowledgment in the product documentation would be */
26 /* appreciated but is not required. */
27 /* 2. Altered source versions must be plainly marked as such, and must not */
28 /* be misrepresented as being the original software. */
29 /* 3. This notice may not be removed or altered from any source */
32 /*****************************************************************************/
53 /*****************************************************************************/
55 /*****************************************************************************/
59 /* Defines for the conditions in a compare */
74 /* Table with the compare suffixes */
75 static const char CmpSuffixTab [][4] = {
76 "eq", "ne", "gt", "ge", "lt", "le", "ugt", "uge", "ult", "ule"
79 /* Table used to invert a condition, indexed by condition */
80 static const unsigned char CmpInvertTab [] = {
82 CMP_LE, CMP_LT, CMP_GE, CMP_GT,
83 CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT
86 /* Table to show which compares are signed (use the N flag) */
87 static const char CmpSignedTab [] = {
88 0, 0, 1, 1, 1, 1, 0, 0, 0, 0
93 /*****************************************************************************/
94 /* Helper functions */
95 /*****************************************************************************/
99 static cmp_t FindCmpCond (const char* Code, unsigned CodeLen)
100 /* Search for a compare condition by the given code using the given length */
105 for (I = 0; I < sizeof (CmpSuffixTab) / sizeof (CmpSuffixTab [0]); ++I) {
106 if (strncmp (Code, CmpSuffixTab [I], CodeLen) == 0) {
118 static cmp_t FindBoolCmpCond (const char* Name)
119 /* Map a condition suffix to a code. Return the code or CMP_INV on failure */
121 /* Check for the correct subroutine name */
122 if (strncmp (Name, "bool", 4) == 0) {
123 /* Name is ok, search for the code in the table */
124 return FindCmpCond (Name+4, strlen(Name)-4);
133 static int FindTosCmpCond (const char* Name)
134 /* Check if this is a call to one of the TOS compare functions (tosgtax).
135 * Return the condition code or CMP_INV on failure.
138 unsigned Len = strlen (Name);
140 /* Check for the correct subroutine name */
141 if (strncmp (Name, "tos", 3) == 0 && strcmp (Name+Len-2, "ax") == 0) {
142 /* Name is ok, search for the code in the table */
143 return FindCmpCond (Name+3, Len-3-2);
152 static int IsUnsignedCmp (int Code)
153 /* Check if this is an unsigned compare */
156 return CmpSignedTab [Code] == 0;
161 static const char* MakeBoolCmp (cmp_t Cond)
162 /* Create the name of a bool transformer subroutine for the given code. The
163 * result is placed into a static buffer, so beware!
166 static char Buffer[20];
167 CHECK (Cond != CMP_INV);
168 sprintf (Buffer, "bool%s", CmpSuffixTab[Cond]);
174 static const char* MakeTosCmp (cmp_t Cond)
175 /* Create the name of a tos compare subroutine for the given code. The
176 * result is placed into a static buffer, so beware!
179 static char Buffer[20];
180 CHECK (Cond != CMP_INV);
181 sprintf (Buffer, "tos%sax", CmpSuffixTab[Cond]);
187 static int IsBitOp (const CodeEntry* E)
188 /* Check if E is one of the bit operations (and, or, eor) */
190 return (E->OPC == OPC_AND || E->OPC == OPC_ORA || E->OPC == OPC_EOR);
195 static int IsCmpToZero (const CodeEntry* E)
196 /* Check if the given instrcuction is a compare to zero instruction */
198 return (E->OPC == OPC_CMP &&
200 (E->Flags & CEF_NUMARG) != 0 &&
206 /*****************************************************************************/
207 /* Remove calls to the bool transformer subroutines */
208 /*****************************************************************************/
212 static unsigned OptBoolTransforms (CodeSeg* S)
213 /* Try to remove the call to boolean transformer routines where the call is
217 unsigned Changes = 0;
219 /* Walk over the entries */
221 while (I < GetCodeEntryCount (S)) {
227 CodeEntry* E = GetCodeEntry (S, I);
229 /* Check for a boolean transformer */
230 if (E->OPC == OPC_JSR &&
231 (Cond = FindBoolCmpCond (E->Arg)) != CMP_INV &&
232 (N = GetNextCodeEntry (S, I)) != 0 &&
233 (N->Info & OF_ZBRA) != 0) {
238 /* Make the boolean transformer unnecessary by changing the
239 * the conditional jump to evaluate the condition flags that
240 * are set after the compare directly. Note: jeq jumps if
241 * the condition is not met, jne jumps if the condition is met.
242 * Invert the code if we jump on condition not met.
244 if (GetBranchCond (N->OPC) == BC_EQ) {
245 /* Jumps if condition false, invert condition */
246 Cond = CmpInvertTab [Cond];
249 /* Check if we can replace the code by something better */
253 ReplaceOPC (N, OPC_JEQ);
257 ReplaceOPC (N, OPC_JNE);
266 if ((X = GetNextCodeEntry (S, I+1)) == 0) {
270 L = GenCodeLabel (S, X);
271 X = NewCodeEntry (OPC_BEQ, AM_BRA, L->Name, L);
272 InsertCodeEntry (S, X, I+1);
273 ReplaceOPC (N, OPC_JPL);
277 ReplaceOPC (N, OPC_JPL);
281 ReplaceOPC (N, OPC_JMI);
289 ReplaceOPC (N, OPC_JMI);
291 X = NewCodeEntry (OPC_JEQ, AM_BRA, L->Name, L);
292 InsertCodeEntry (S, X, I+2);
301 if ((X = GetNextCodeEntry (S, I+1)) == 0) {
305 L = GenCodeLabel (S, X);
306 X = NewCodeEntry (OPC_BEQ, AM_BRA, L->Name, L);
307 InsertCodeEntry (S, X, I+1);
308 ReplaceOPC (N, OPC_JCS);
312 ReplaceOPC (N, OPC_JCS);
316 ReplaceOPC (N, OPC_JCC);
324 ReplaceOPC (N, OPC_JCC);
326 X = NewCodeEntry (OPC_JEQ, AM_BRA, L->Name, L);
327 InsertCodeEntry (S, X, I+2);
331 Internal ("Unknown jump condition: %d", Cond);
335 /* Remove the call to the bool transformer */
338 /* Remember, we had changes */
349 /* Return the number of changes made */
355 /*****************************************************************************/
356 /* Optimizations for compares */
357 /*****************************************************************************/
361 static unsigned OptCmp1 (CodeSeg* S)
362 /* Search for the sequence
374 unsigned Changes = 0;
376 /* Walk over the entries */
378 while (I < GetCodeEntryCount (S)) {
383 CodeEntry* E = GetCodeEntry (S, I);
385 /* Check for the sequence */
386 if (E->OPC == OPC_STX &&
387 GetCodeEntries (S, L, I+1, 2) &&
388 L[0]->OPC == OPC_STX &&
389 strcmp (L[0]->Arg, "tmp1") == 0 &&
390 !CodeEntryHasLabel (L[0]) &&
391 L[1]->OPC == OPC_ORA &&
392 strcmp (L[1]->Arg, "tmp1") == 0 &&
393 !CodeEntryHasLabel (L[1])) {
395 /* Remove the remaining instructions */
396 DelCodeEntries (S, I+1, 2);
398 /* Insert the ora instead */
399 InsertCodeEntry (S, NewCodeEntry (OPC_ORA, E->AM, E->Arg, 0), I+1);
401 /* Remember, we had changes */
411 /* Return the number of changes made */
417 static unsigned OptCmp2 (CodeSeg* S)
420 * lda/and/ora/eor ...
424 * and remove the cmp.
427 unsigned Changes = 0;
429 /* Walk over the entries */
431 while (I < GetCodeEntryCount (S)) {
436 CodeEntry* E = GetCodeEntry (S, I);
438 /* Check for the sequence */
439 if ((E->OPC == OPC_LDA || IsBitOp (E)) &&
440 GetCodeEntries (S, L, I+1, 2) &&
441 IsCmpToZero (L[0]) &&
442 !CodeEntryHasLabel (L[0]) &&
443 (L[1]->Info & OF_FBRA) != 0 &&
444 !CodeEntryHasLabel (L[1])) {
446 /* Remove the compare */
447 DelCodeEntry (S, I+1);
449 /* Remember, we had changes */
459 /* Return the number of changes made */
465 static unsigned OptCmp3 (CodeSeg* S)
475 * If a is zero, we may remove the compare. If a and b are both zero, we may
476 * replace it by the sequence
482 * L1 may be either the label at the branch instruction, or the target label
483 * of this instruction.
486 unsigned Changes = 0;
488 /* Walk over the entries */
490 while (I < GetCodeEntryCount (S)) {
495 CodeEntry* E = GetCodeEntry (S, I);
497 /* Check for the sequence */
498 if (E->OPC == OPC_LDA &&
499 GetCodeEntries (S, L, I+1, 5) &&
500 L[0]->OPC == OPC_LDX &&
501 !CodeEntryHasLabel (L[0]) &&
502 L[1]->OPC == OPC_CPX &&
503 L[1]->AM == AM_IMM &&
504 (L[1]->Flags & CEF_NUMARG) != 0 &&
505 !CodeEntryHasLabel (L[1]) &&
506 (L[2]->OPC == OPC_JNE || L[2]->OPC == OPC_BNE) &&
508 !CodeEntryHasLabel (L[2]) &&
509 L[3]->OPC == OPC_CMP &&
510 L[3]->AM == AM_IMM &&
511 (L[3]->Flags & CEF_NUMARG) != 0 &&
512 (L[4]->Info & OF_ZBRA) != 0 &&
514 (L[2]->JumpTo->Owner == L[4] || L[2]->JumpTo == L[4]->JumpTo)) {
516 /* Get the compare value */
517 unsigned Val = ((L[1]->Num & 0xFF) << 8) | (L[3]->Num & 0xFF);
520 /* The value is zero, we may use the simple code version. */
521 ReplaceOPC (L[0], OPC_ORA);
522 DelCodeEntries (S, I+2, 3);
524 /* Move the lda instruction after the first branch */
525 CodeEntry* N = RetrieveCodeEntry (S, I);
526 InsertCodeEntry (S, N, I+3);
528 /* Replace the ldx/cpx by lda/cmp */
529 ReplaceOPC (L[0], OPC_LDA);
530 ReplaceOPC (L[1], OPC_CMP);
532 /* The high byte is zero, remove the CMP */
533 if ((Val & 0xFF00) == 0) {
534 DelCodeEntry (S, I+1);
546 /* Return the number of changes made */
552 /*****************************************************************************/
553 /* nega optimizations */
554 /*****************************************************************************/
558 static unsigned OptNegA1 (CodeSeg* S)
565 * Remove the ldx if the lda does not use it.
568 unsigned Changes = 0;
570 /* Walk over the entries */
572 while (I < GetCodeEntryCount (S)) {
577 CodeEntry* E = GetCodeEntry (S, I);
579 /* Check for a ldx */
580 if (E->OPC == OPC_LDX &&
582 (E->Flags & CEF_NUMARG) != 0 &&
584 GetCodeEntries (S, L, I+1, 2) &&
585 L[0]->OPC == OPC_LDA &&
586 (L[0]->Use & REG_X) == 0 &&
587 L[1]->OPC == OPC_JSR &&
588 strcmp (L[1]->Arg, "bnega") == 0) {
590 /* Remove the ldx instruction */
593 /* Remember, we had changes */
603 /* Return the number of changes made */
609 static unsigned OptNegA2 (CodeSeg* S)
616 * Adjust the conditional branch and remove the call to the subroutine.
619 unsigned Changes = 0;
621 /* Walk over the entries */
623 while (I < GetCodeEntryCount (S)) {
628 CodeEntry* E = GetCodeEntry (S, I);
630 /* Check for the sequence */
631 if (E->OPC == OPC_LDA &&
632 GetCodeEntries (S, L, I+1, 2) &&
633 L[0]->OPC == OPC_JSR &&
634 strcmp (L[0]->Arg, "bnega") == 0 &&
635 !CodeEntryHasLabel (L[0]) &&
636 (L[1]->Info & OF_ZBRA) != 0) {
638 /* Invert the branch */
639 ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
641 /* Delete the subroutine call */
642 DelCodeEntry (S, I+1);
644 /* Remember, we had changes */
654 /* Return the number of changes made */
660 /*****************************************************************************/
661 /* negax optimizations */
662 /*****************************************************************************/
666 static unsigned OptNegAX1 (CodeSeg* S)
667 /* Search for the sequence:
684 unsigned Changes = 0;
686 /* Walk over the entries */
688 while (I < GetCodeEntryCount (S)) {
693 CodeEntry* E = GetCodeEntry (S, I);
695 /* Check for the sequence */
696 if (E->OPC == OPC_LDA &&
697 E->AM == AM_ZP_INDY &&
698 GetCodeEntries (S, L, I+1, 5) &&
699 L[0]->OPC == OPC_TAX &&
700 L[1]->OPC == OPC_DEY &&
701 L[2]->OPC == OPC_LDA &&
702 L[2]->AM == AM_ZP_INDY &&
703 strcmp (L[2]->Arg, E->Arg) == 0 &&
704 !CodeEntryHasLabel (L[2]) &&
705 L[3]->OPC == OPC_JSR &&
706 strcmp (L[3]->Arg, "bnegax") == 0 &&
707 !CodeEntryHasLabel (L[3]) &&
708 (L[4]->Info & OF_ZBRA) != 0) {
711 ReplaceOPC (L[2], OPC_ORA);
713 /* Invert the branch */
714 ReplaceOPC (L[4], GetInverseBranch (L[4]->OPC));
716 /* Delete the entries no longer needed. Beware: Deleting entries
717 * will change the indices.
719 DelCodeEntry (S, I+4); /* jsr bnegax */
720 DelCodeEntry (S, I+1); /* tax */
722 /* Remember, we had changes */
732 /* Return the number of changes made */
738 static unsigned OptNegAX2 (CodeSeg* S)
739 /* Search for the sequence:
753 unsigned Changes = 0;
755 /* Walk over the entries */
757 while (I < GetCodeEntryCount (S)) {
762 CodeEntry* E = GetCodeEntry (S, I);
764 /* Check for the sequence */
765 if (E->OPC == OPC_LDA &&
766 GetCodeEntries (S, L, I+1, 3) &&
767 L[0]->OPC == OPC_LDX &&
768 !CodeEntryHasLabel (L[0]) &&
769 L[1]->OPC == OPC_JSR &&
770 strcmp (L[1]->Arg, "bnegax") == 0 &&
771 !CodeEntryHasLabel (L[1]) &&
772 (L[2]->Info & OF_ZBRA) != 0) {
775 ReplaceOPC (L[0], OPC_ORA);
777 /* Invert the branch */
778 ReplaceOPC (L[2], GetInverseBranch (L[2]->OPC));
780 /* Delete the subroutine call */
781 DelCodeEntry (S, I+2);
783 /* Remember, we had changes */
793 /* Return the number of changes made */
799 static unsigned OptNegAX3 (CodeSeg* S)
800 /* Search for the sequence:
813 unsigned Changes = 0;
815 /* Walk over the entries */
817 while (I < GetCodeEntryCount (S)) {
822 CodeEntry* E = GetCodeEntry (S, I);
824 /* Check for the sequence */
825 if (E->OPC == OPC_JSR &&
827 GetCodeEntries (S, L, I+1, 2) &&
828 L[0]->OPC == OPC_JSR &&
829 strncmp (L[0]->Arg,"bnega",5) == 0 &&
830 !CodeEntryHasLabel (L[0]) &&
831 (L[1]->Info & OF_ZBRA) != 0) {
833 /* Check if we're calling bnega or bnegax */
834 int ByteSized = (strcmp (L[0]->Arg, "bnega") == 0);
836 /* Delete the subroutine call */
837 DelCodeEntry (S, I+1);
839 /* Insert apropriate test code */
842 InsertCodeEntry (S, NewCodeEntry (OPC_TAX, AM_IMP, 0, 0), I+1);
845 InsertCodeEntry (S, NewCodeEntry (OPC_STX, AM_ZP, "tmp1", 0), I+1);
846 InsertCodeEntry (S, NewCodeEntry (OPC_ORA, AM_ZP, "tmp1", 0), I+2);
849 /* Invert the branch */
850 ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
852 /* Remember, we had changes */
862 /* Return the number of changes made */
868 /*****************************************************************************/
870 /*****************************************************************************/
874 /* Table with all the optimization functions */
875 typedef struct OptFunc OptFunc;
877 unsigned (*Func) (CodeSeg*);/* Optimizer function */
878 const char* Name; /* Name of optimizer step */
879 char Disabled; /* True if pass disabled */
884 /* Table with optimizer steps - are called in this order */
885 static OptFunc OptFuncs [] = {
886 /* Optimize jump cascades */
887 { OptJumpCascades, "OptJumpCascades", 0 },
888 /* Remove dead jumps */
889 { OptDeadJumps, "OptDeadJumps", 0 },
890 /* Change jsr/rts to jmp */
891 { OptRTS, "OptRTS", 0 },
892 /* Remove dead code */
893 { OptDeadCode, "OptDeadCode", 0 },
894 /* Optimize jump targets */
895 { OptJumpTarget, "OptJumpTarget", 0 },
896 /* Optimize conditional branches */
897 { OptCondBranches, "OptCondBranches", 0 },
898 /* Replace jumps to RTS by RTS */
899 { OptRTSJumps, "OptRTSJumps", 0 },
900 /* Remove calls to the bool transformer subroutines */
901 { OptBoolTransforms, "OptBoolTransforms", 0 },
902 /* Optimize calls to nega */
903 { OptNegA1, "OptNegA1", 0 },
904 /* Optimize calls to nega */
905 { OptNegA2, "OptNegA2", 0 },
906 /* Optimize calls to negax */
907 { OptNegAX1, "OptNegAX1", 0 },
908 /* Optimize calls to negax */
909 { OptNegAX2, "OptNegAX2", 0 },
910 /* Optimize calls to negax */
911 { OptNegAX3, "OptNegAX3", 0 },
912 /* Optimize compares */
913 { OptCmp1, "OptCmp1", 0 },
914 /* Optimize compares */
915 { OptCmp2, "OptCmp2", 0 },
916 /* Optimize compares */
917 { OptCmp3, "OptCmp3", 0 },
918 /* Remove unused loads */
919 { OptUnusedLoads, "OptUnusedLoads", 0 },
920 /* Optimize branch distance */
921 { OptBranchDist, "OptBranchDist", 0 },
926 static OptFunc* FindOptStep (const char* Name)
927 /* Find an optimizer step by name in the table and return a pointer. Print an
928 * error and call AbEnd if not found.
933 /* Run all optimization steps */
934 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
935 if (strcmp (OptFuncs[I].Name, Name) == 0) {
942 AbEnd ("Optimization step `%s' not found", Name);
948 void DisableOpt (const char* Name)
949 /* Disable the optimization with the given name */
951 OptFunc* F = FindOptStep (Name);
957 void EnableOpt (const char* Name)
958 /* Enable the optimization with the given name */
960 OptFunc* F = FindOptStep (Name);
966 void RunOpt (CodeSeg* S)
967 /* Run the optimizer */
973 /* If we shouldn't run the optimizer, bail out */
978 /* Print the name of the function we are working on */
980 Print (stdout, 1, "Running optimizer for function `%s'\n", S->Func->Name);
982 Print (stdout, 1, "Running optimizer for global code segment\n");
985 /* Repeat all steps until there are no more changes */
987 /* Reset the number of changes */
990 /* Keep the user hapy */
991 Print (stdout, 1, " Optimizer pass %u:\n", ++Pass);
993 /* Run all optimization steps */
994 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
996 /* Print the name of the following optimizer step */
997 Print (stdout, 1, " %s:%*s", OptFuncs[I].Name,
998 (int) (30-strlen(OptFuncs[I].Name)), "");
1000 /* Check if the step is disabled */
1001 if (OptFuncs[I].Disabled) {
1002 Print (stdout, 1, "Disabled\n");
1004 unsigned Changes = OptFuncs[I].Func (S);
1005 OptChanges += Changes;
1006 Print (stdout, 1, "%u Changes\n", Changes);
1010 } while (OptChanges > 0);