]> git.sur5r.net Git - cc65/blob - src/cc65/codeopt.c
Fixed more E_MCONST issues
[cc65] / src / cc65 / codeopt.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 codeopt.c                                 */
4 /*                                                                           */
5 /*                           Optimizer subroutines                           */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2001      Ullrich von Bassewitz                                       */
10 /*               Wacholderweg 14                                             */
11 /*               D-70597 Stuttgart                                           */
12 /* EMail:        uz@cc65.org                                                 */
13 /*                                                                           */
14 /*                                                                           */
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.                                    */
18 /*                                                                           */
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:                            */
22 /*                                                                           */
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              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <string.h>
37
38 /* common */
39 #include "abend.h"
40 #include "print.h"
41
42 /* cc65 */
43 #include "asmlabel.h"
44 #include "codeent.h"
45 #include "codeinfo.h"
46 #include "coptind.h"
47 #include "error.h"
48 #include "global.h"
49 #include "codeopt.h"
50
51
52
53 /*****************************************************************************/
54 /*                                   Data                                    */
55 /*****************************************************************************/
56
57
58
59 /* Defines for the conditions in a compare */
60 typedef enum {
61     CMP_INV = -1,
62     CMP_EQ,
63     CMP_NE,
64     CMP_GT,
65     CMP_GE,
66     CMP_LT,
67     CMP_LE,
68     CMP_UGT,
69     CMP_UGE,
70     CMP_ULT,
71     CMP_ULE
72 } cmp_t;
73
74 /* Table with the compare suffixes */
75 static const char CmpSuffixTab [][4] = {
76     "eq", "ne", "gt", "ge", "lt", "le", "ugt", "uge", "ult", "ule"
77 };
78
79 /* Table used to invert a condition, indexed by condition */
80 static const unsigned char CmpInvertTab [] = {
81     CMP_NE, CMP_EQ,
82     CMP_LE, CMP_LT, CMP_GE, CMP_GT,
83     CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT
84 };
85
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
89 };
90
91
92
93 /*****************************************************************************/
94 /*                             Helper functions                              */
95 /*****************************************************************************/
96
97
98
99 static cmp_t FindCmpCond (const char* Code, unsigned CodeLen)
100 /* Search for a compare condition by the given code using the given length */
101 {
102     unsigned I;
103
104     /* Linear search */
105     for (I = 0; I < sizeof (CmpSuffixTab) / sizeof (CmpSuffixTab [0]); ++I) {
106         if (strncmp (Code, CmpSuffixTab [I], CodeLen) == 0) {
107             /* Found */
108             return I;
109         }
110     }
111
112     /* Not found */
113     return CMP_INV;
114 }
115
116
117
118 static cmp_t FindBoolCmpCond (const char* Name)
119 /* Map a condition suffix to a code. Return the code or CMP_INV on failure */
120 {
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);
125     } else {
126         /* Not found */
127         return CMP_INV;
128     }
129 }
130
131
132
133 static cmp_t FindTosCmpCond (const char* Name)
134 /* Check if this is a call to one of the TOS compare functions (tosgtax).
135  * Return the condition code or CMP_INV on failure.
136  */
137 {
138     unsigned Len = strlen (Name);
139
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);
144     } else {
145         /* Not found */
146         return CMP_INV;
147     }
148 }
149
150
151
152 static void ReplaceCmp (CodeSeg* S, unsigned I, cmp_t Cond)
153 /* Helper function for the replacement of routines that return a boolean
154  * followed by a conditional jump. Instead of the boolean value, the condition
155  * codes are evaluated directly.
156  * I is the index of the conditional branch, the sequence is already checked
157  * to be correct.
158  */
159 {
160     CodeEntry* N;
161     CodeLabel* L;
162
163     /* Get the entry */
164     CodeEntry* E = GetCodeEntry (S, I);
165
166     /* Replace the conditional branch */
167     switch (Cond) {
168
169         case CMP_EQ:
170             ReplaceOPC (E, OPC_JEQ);
171             break;
172
173         case CMP_NE:
174             ReplaceOPC (E, OPC_JNE);
175             break;
176
177         case CMP_GT:
178             /* Replace by
179              *     beq @L
180              *     jpl Target
181              * @L: ...
182              */
183             if ((N = GetNextCodeEntry (S, I)) == 0) {
184                 /* No such entry */
185                 Internal ("Invalid program flow");
186             }
187             L = GenCodeLabel (S, N);
188             N = NewCodeEntry (OPC_BEQ, AM_BRA, L->Name, L, E->LI);
189             InsertCodeEntry (S, N, I);
190             ReplaceOPC (E, OPC_JPL);
191             break;
192
193         case CMP_GE:
194             ReplaceOPC (E, OPC_JPL);
195             break;
196
197         case CMP_LT:
198             ReplaceOPC (E, OPC_JMI);
199             break;
200
201         case CMP_LE:
202             /* Replace by
203              *     jmi Target
204              *     jeq Target
205              */
206             ReplaceOPC (E, OPC_JMI);
207             L = E->JumpTo;
208             N = NewCodeEntry (OPC_JEQ, AM_BRA, L->Name, L, E->LI);
209             InsertCodeEntry (S, N, I+1);
210             break;
211
212         case CMP_UGT:
213             /* Replace by
214              *     beq @L
215              *     jcs Target
216              * @L: ...
217              */
218             if ((N = GetNextCodeEntry (S, I)) == 0) {
219                 /* No such entry */
220                 Internal ("Invalid program flow");
221             }
222             L = GenCodeLabel (S, N);
223             N = NewCodeEntry (OPC_BEQ, AM_BRA, L->Name, L, E->LI);
224             InsertCodeEntry (S, N, I);
225             ReplaceOPC (E, OPC_JCS);
226             break;
227
228         case CMP_UGE:
229             ReplaceOPC (E, OPC_JCS);
230             break;
231
232         case CMP_ULT:
233             ReplaceOPC (E, OPC_JCC);
234             break;
235
236         case CMP_ULE:
237             /* Replace by
238              *     jcc Target
239              *     jeq Target
240              */
241             ReplaceOPC (E, OPC_JCC);
242             L = E->JumpTo;
243             N = NewCodeEntry (OPC_JEQ, AM_BRA, L->Name, L, E->LI);
244             InsertCodeEntry (S, N, I+1);
245             break;
246
247         default:
248             Internal ("Unknown jump condition: %d", Cond);
249
250     }
251
252 }
253
254
255
256 static int IsUnsignedCmp (int Code)
257 /* Check if this is an unsigned compare */
258 {
259     CHECK (Code >= 0);
260     return CmpSignedTab [Code] == 0;
261 }
262
263
264
265 static int IsBitOp (const CodeEntry* E)
266 /* Check if E is one of the bit operations (and, or, eor) */
267 {
268     return (E->OPC == OPC_AND || E->OPC == OPC_ORA || E->OPC == OPC_EOR);
269 }
270
271
272
273 static int IsCmpToZero (const CodeEntry* E)
274 /* Check if the given instrcuction is a compare to zero instruction */
275 {
276     return (E->OPC == OPC_CMP             &&
277             E->AM  == AM_IMM              &&
278             (E->Flags & CEF_NUMARG) != 0  &&
279             E->Num == 0);
280 }
281
282
283
284 static int IsSpLoad (const CodeEntry* E)
285 /* Return true if this is the load of A from the stack */
286 {
287     return E->OPC == OPC_LDA && E->AM == AM_ZP_INDY && strcmp (E->Arg, "sp") == 0;
288 }
289
290
291
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:
295  *
296  *      ldy     #$xx
297  *      lda     (sp),y
298  *      tax
299  *      dey
300  *      lda     (sp),y
301  *
302  * If so, read Count entries following the first ldy into L and return true
303  * if this is possible. Otherwise return false.
304  */
305 {
306     /* Be sure we read enough entries for the check */
307     CHECK (Count >= 5);
308
309     /* Read the first entry */
310     L[0] = GetCodeEntry (S, Index);
311
312     /* Check for the sequence */
313     return (L[0]->OPC == OPC_LDY                      &&
314             L[0]->AM == AM_IMM                        &&
315             (L[0]->Flags & CEF_NUMARG) != 0           &&
316             GetCodeEntries (S, L+1, Index+1, Count-1) &&
317             IsSpLoad (L[1])                           &&
318             !CodeEntryHasLabel (L[1])                 &&
319             L[2]->OPC == OPC_TAX                      &&
320             !CodeEntryHasLabel (L[2])                 &&
321             L[3]->OPC == OPC_DEY                      &&
322             !CodeEntryHasLabel (L[3])                 &&
323             IsSpLoad (L[4])                           &&
324             !CodeEntryHasLabel (L[4]));
325 }
326
327
328
329 static int IsImmCmp16 (CodeSeg* S, CodeEntry** L)
330 /* Check if the instructions at L are an immidiate compare of a/x:
331  *
332  *
333  */
334 {
335     return (L[0]->OPC == OPC_CPX                                             &&
336             L[0]->AM == AM_IMM                                               &&
337             (L[0]->Flags & CEF_NUMARG) != 0                                  &&
338             !CodeEntryHasLabel (L[0])                                        &&
339             (L[1]->OPC == OPC_JNE || L[1]->OPC == OPC_BNE)                   &&
340             L[1]->JumpTo != 0                                                &&
341             !CodeEntryHasLabel (L[1])                                        &&
342             L[2]->OPC == OPC_CMP                                             &&
343             L[2]->AM == AM_IMM                                               &&
344             (L[2]->Flags & CEF_NUMARG) != 0                                  &&
345             (L[3]->Info & OF_ZBRA) != 0                                      &&
346             L[3]->JumpTo != 0                                                &&
347             (L[1]->JumpTo->Owner == L[3] || L[1]->JumpTo == L[3]->JumpTo));
348 }
349
350
351
352 /*****************************************************************************/
353 /*             Remove calls to the bool transformer subroutines              */
354 /*****************************************************************************/
355
356
357
358 static unsigned OptBoolTransforms (CodeSeg* S)
359 /* Try to remove the call to boolean transformer routines where the call is
360  * not really needed.
361  */
362 {
363     unsigned Changes = 0;
364
365     /* Walk over the entries */
366     unsigned I = 0;
367     while (I < GetCodeEntryCount (S)) {
368
369         CodeEntry* N;
370         cmp_t Cond;
371
372         /* Get next entry */
373         CodeEntry* E = GetCodeEntry (S, I);
374
375         /* Check for a boolean transformer */
376         if (E->OPC == OPC_JSR                            &&
377             (Cond = FindBoolCmpCond (E->Arg)) != CMP_INV &&
378             (N = GetNextCodeEntry (S, I)) != 0           &&
379             (N->Info & OF_ZBRA) != 0) {
380
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.
386              */
387             if (GetBranchCond (N->OPC) == BC_EQ) {
388                 /* Jumps if condition false, invert condition */
389                 Cond = CmpInvertTab [Cond];
390             }
391
392             /* Check if we can replace the code by something better */
393             ReplaceCmp (S, I+1, Cond);
394
395             /* Remove the call to the bool transformer */
396             DelCodeEntry (S, I);
397
398             /* Remember, we had changes */
399             ++Changes;
400
401         }
402
403         /* Next entry */
404         ++I;
405
406     }
407
408     /* Return the number of changes made */
409     return Changes;
410 }
411
412
413
414 /*****************************************************************************/
415 /*                           Optimize subtractions                           */
416 /*****************************************************************************/
417
418
419
420 static unsigned OptSub1 (CodeSeg* S)
421 /* Search for the sequence
422  *
423  *      sbc     ...
424  *      bcs     L
425  *      dex
426  * L:
427  *
428  * and remove the handling of the high byte if X is not used later.
429  */
430 {
431     unsigned Changes = 0;
432
433     /* Walk over the entries */
434     unsigned I = 0;
435     while (I < GetCodeEntryCount (S)) {
436
437         CodeEntry* L[3];
438
439         /* Get next entry */
440         CodeEntry* E = GetCodeEntry (S, I);
441
442         /* Check for the sequence */
443         if (E->OPC == OPC_SBC                              &&
444             GetCodeEntries (S, L, I+1, 3)                  &&
445             (L[0]->OPC == OPC_BCS || L[0]->OPC == OPC_JCS) &&
446             L[0]->JumpTo != 0                              &&
447             !CodeEntryHasLabel (L[0])                      &&
448             L[1]->OPC == OPC_DEX                           &&
449             !CodeEntryHasLabel (L[1])                      &&
450             L[0]->JumpTo->Owner == L[2]                    &&
451             !RegXUsed (S, I+3)) {
452
453             /* Remove the bcs/dex */
454             DelCodeEntries (S, I+1, 2);
455
456             /* Remember, we had changes */
457             ++Changes;
458
459         }
460
461         /* Next entry */
462         ++I;
463
464     }
465
466     /* Return the number of changes made */
467     return Changes;
468 }
469
470
471
472 static unsigned OptSub2 (CodeSeg* S)
473 /* Search for the sequence
474  *
475  *      lda     xx
476  *      sec
477  *      sta     tmp1
478  *      lda     yy
479  *      sbc     tmp1
480  *      sta     yy
481  *
482  * and replace it by
483  *
484  *      sec
485  *      lda     yy
486  *      sbc     xx
487  *      sta     yy
488  */
489 {
490     unsigned Changes = 0;
491
492     /* Walk over the entries */
493     unsigned I = 0;
494     while (I < GetCodeEntryCount (S)) {
495
496         CodeEntry* L[5];
497
498         /* Get next entry */
499         CodeEntry* E = GetCodeEntry (S, I);
500
501         /* Check for the sequence */
502         if (E->OPC == OPC_LDA                              &&
503             GetCodeEntries (S, L, I+1, 5)                  &&
504             L[0]->OPC == OPC_SEC                           &&
505             !CodeEntryHasLabel (L[0])                      &&
506             L[1]->OPC == OPC_STA                           &&
507             strcmp (L[1]->Arg, "tmp1") == 0                &&
508             !CodeEntryHasLabel (L[1])                      &&
509             L[2]->OPC == OPC_LDA                           &&
510             !CodeEntryHasLabel (L[2])                      &&
511             L[3]->OPC == OPC_SBC                           &&
512             strcmp (L[3]->Arg, "tmp1") == 0                &&
513             !CodeEntryHasLabel (L[3])                      &&
514             L[4]->OPC == OPC_STA                           &&
515             strcmp (L[4]->Arg, L[2]->Arg) == 0             &&
516             !CodeEntryHasLabel (L[4])) {
517
518             /* Remove the store to tmp1 */
519             DelCodeEntry (S, I+2);
520
521             /* Remove the subtraction */
522             DelCodeEntry (S, I+3);
523
524             /* Move the lda to the position of the subtraction and change the
525              * op to SBC.
526              */
527             MoveCodeEntry (S, I, I+3);
528             ReplaceOPC (E, OPC_SBC);
529
530             /* Remember, we had changes */
531             ++Changes;
532
533         }
534
535         /* Next entry */
536         ++I;
537
538     }
539
540     /* Return the number of changes made */
541     return Changes;
542 }
543
544
545
546 /*****************************************************************************/
547 /*                            Optimize additions                             */
548 /*****************************************************************************/
549
550
551
552 static unsigned OptAdd1 (CodeSeg* S)
553 /* Search for the sequence
554  *
555  *      adc     ...
556  *      bcc     L
557  *      inx
558  * L:
559  *
560  * and remove the handling of the high byte if X is not used later.
561  */
562 {
563     unsigned Changes = 0;
564
565     /* Walk over the entries */
566     unsigned I = 0;
567     while (I < GetCodeEntryCount (S)) {
568
569         CodeEntry* L[3];
570
571         /* Get next entry */
572         CodeEntry* E = GetCodeEntry (S, I);
573
574         /* Check for the sequence */
575         if (E->OPC == OPC_ADC                              &&
576             GetCodeEntries (S, L, I+1, 3)                  &&
577             (L[0]->OPC == OPC_BCC || L[0]->OPC == OPC_JCC) &&
578             L[0]->JumpTo != 0                              &&
579             !CodeEntryHasLabel (L[0])                      &&
580             L[1]->OPC == OPC_INX                           &&
581             !CodeEntryHasLabel (L[1])                      &&
582             L[0]->JumpTo->Owner == L[2]                    &&
583             !RegXUsed (S, I+3)) {
584
585             /* Remove the bcs/dex */
586             DelCodeEntries (S, I+1, 2);
587
588             /* Remember, we had changes */
589             ++Changes;
590
591         }
592
593         /* Next entry */
594         ++I;
595
596     }
597
598     /* Return the number of changes made */
599     return Changes;
600 }
601
602
603
604 /*****************************************************************************/
605 /*                        Optimizations for compares                         */
606 /*****************************************************************************/
607
608
609
610 static unsigned OptCmp1 (CodeSeg* S)
611 /* Search for the sequence
612  *
613  *      stx     xx
614  *      stx     tmp1
615  *      ora     tmp1
616  *
617  * and replace it by
618  *
619  *      stx     xx
620  *      ora     xx
621  */
622 {
623     unsigned Changes = 0;
624
625     /* Walk over the entries */
626     unsigned I = 0;
627     while (I < GetCodeEntryCount (S)) {
628
629         CodeEntry* L[2];
630
631         /* Get next entry */
632         CodeEntry* E = GetCodeEntry (S, I);
633
634         /* Check for the sequence */
635         if (E->OPC == OPC_STX                   &&
636             GetCodeEntries (S, L, I+1, 2)       &&
637             L[0]->OPC == OPC_STX                &&
638             strcmp (L[0]->Arg, "tmp1") == 0     &&
639             !CodeEntryHasLabel (L[0])           &&
640             L[1]->OPC == OPC_ORA                &&
641             strcmp (L[1]->Arg, "tmp1") == 0     &&
642             !CodeEntryHasLabel (L[1])) {
643
644             /* Remove the remaining instructions */
645             DelCodeEntries (S, I+1, 2);
646
647             /* Insert the ora instead */
648             InsertCodeEntry (S, NewCodeEntry (OPC_ORA, E->AM, E->Arg, 0, E->LI), I+1);
649
650             /* Remember, we had changes */
651             ++Changes;
652
653         }
654
655         /* Next entry */
656         ++I;
657
658     }
659
660     /* Return the number of changes made */
661     return Changes;
662 }
663
664
665
666 static unsigned OptCmp2 (CodeSeg* S)
667 /* Search for
668  *
669  *      lda/and/ora/eor ...
670  *      cmp #$00
671  *      jeq/jne
672  *
673  * and remove the cmp.
674  */
675 {
676     unsigned Changes = 0;
677
678     /* Walk over the entries */
679     unsigned I = 0;
680     while (I < GetCodeEntryCount (S)) {
681
682         CodeEntry* L[2];
683
684         /* Get next entry */
685         CodeEntry* E = GetCodeEntry (S, I);
686
687         /* Check for the sequence */
688         if ((E->OPC == OPC_LDA || IsBitOp (E))    &&
689             GetCodeEntries (S, L, I+1, 2)         &&
690             IsCmpToZero (L[0])                    &&
691             !CodeEntryHasLabel (L[0])             &&
692             (L[1]->Info & OF_FBRA) != 0           &&
693             !CodeEntryHasLabel (L[1])) {
694
695             /* Remove the compare */
696             DelCodeEntry (S, I+1);
697
698             /* Remember, we had changes */
699             ++Changes;
700
701         }
702
703         /* Next entry */
704         ++I;
705
706     }
707
708     /* Return the number of changes made */
709     return Changes;
710 }
711
712
713
714 static unsigned OptCmp3 (CodeSeg* S)
715 /* Search for
716  *
717  *      lda     x
718  *      ldx     y
719  *      cpx     #a
720  *      bne     L1
721  *      cmp     #b
722  *      jne/jeq L2
723  *
724  * If a is zero, we may remove the compare. If a and b are both zero, we may
725  * replace it by the sequence
726  *
727  *      lda     x
728  *      ora     x+1
729  *      jne/jeq ...
730  *
731  * L1 may be either the label at the branch instruction, or the target label
732  * of this instruction.
733  */
734 {
735     unsigned Changes = 0;
736
737     /* Walk over the entries */
738     unsigned I = 0;
739     while (I < GetCodeEntryCount (S)) {
740
741         CodeEntry* L[5];
742
743         /* Get next entry */
744         CodeEntry* E = GetCodeEntry (S, I);
745
746         /* Check for the sequence */
747         if (E->OPC == OPC_LDA                                                &&
748             GetCodeEntries (S, L, I+1, 5)                                    &&
749             L[0]->OPC == OPC_LDX                                             &&
750             !CodeEntryHasLabel (L[0])                                        &&
751             IsImmCmp16 (S, L+1)) {
752
753             if (L[1]->Num == 0 && L[3]->Num == 0) {
754                 /* The value is zero, we may use the simple code version. */
755                 ReplaceOPC (L[0], OPC_ORA);
756                 DelCodeEntries (S, I+2, 3);
757             } else {
758                 /* Move the lda instruction after the first branch. This will
759                  * improve speed, since the load is delayed after the first
760                  * test.
761                  */
762                 MoveCodeEntry (S, I, I+4);
763
764                 /* We will replace the ldx/cpx by lda/cmp */
765                 ReplaceOPC (L[0], OPC_LDA);
766                 ReplaceOPC (L[1], OPC_CMP);
767
768             }
769
770             ++Changes;
771         }
772
773         /* Next entry */
774         ++I;
775
776     }
777
778     /* Return the number of changes made */
779     return Changes;
780 }
781
782
783
784 static unsigned OptCmp4 (CodeSeg* S)
785 /* Optimize compares of local variables:
786  *
787  *      ldy     #o
788  *      lda     (sp),y
789  *      tax
790  *      dey
791  *      lda     (sp),y
792  *      cpx     #a
793  *      bne     L1
794  *      cmp     #b
795  *      jne/jeq L2
796  */
797 {
798     unsigned Changes = 0;
799
800     /* Walk over the entries */
801     unsigned I = 0;
802     while (I < GetCodeEntryCount (S)) {
803
804         CodeEntry* L[9];
805
806         /* Check for the sequence */
807         if (IsLocalLoad16 (S, I, L, 9) && IsImmCmp16 (S, L+5)) {
808
809             if (L[5]->Num == 0 && L[7]->Num == 0) {
810
811                 /* The value is zero, we may use the simple code version:
812                  *      ldy     #o
813                  *      lda     (sp),y
814                  *      dey
815                  *      ora     (sp),y
816                  *      jne/jeq ...
817                  */
818                 ReplaceOPC (L[4], OPC_ORA);
819                 DelCodeEntries (S, I+5, 3);   /* cpx/bne/cmp */
820                 DelCodeEntry (S, I+2);        /* tax */
821
822             } else {
823
824                 /* Change the code to just use the A register. Move the load
825                  * of the low byte after the first branch if possible:
826                  *
827                  *      ldy     #o
828                  *      lda     (sp),y
829                  *      cmp     #a
830                  *      bne     L1
831                  *      dey
832                  *      lda     (sp),y
833                  *      cmp     #b
834                  *      jne/jeq ...
835                  */
836                 DelCodeEntry (S, I+2);         /* tax */
837                 ReplaceOPC (L[5], OPC_CMP);    /* cpx -> cmp */
838                 MoveCodeEntry (S, I+4, I+2);   /* cmp */
839                 MoveCodeEntry (S, I+5, I+3);   /* bne */
840
841             }
842
843             ++Changes;
844         }
845
846         /* Next entry */
847         ++I;
848
849     }
850
851     /* Return the number of changes made */
852     return Changes;
853 }
854
855
856
857 static unsigned OptCmp5 (CodeSeg* S)
858 /* Search for calls to compare subroutines followed by a conditional branch
859  * and replace them by cheaper versions, since the branch means that the
860  * boolean value returned by these routines is not needed (we may also check
861  * that explicitly, but for the current code generator it is always true).
862  */
863 {
864     unsigned Changes = 0;
865
866     /* Walk over the entries */
867     unsigned I = 0;
868     while (I < GetCodeEntryCount (S)) {
869
870         CodeEntry* N;
871         cmp_t Cond;
872
873         /* Get next entry */
874         CodeEntry* E = GetCodeEntry (S, I);
875
876         /* Check for the sequence */
877         if (E->OPC == OPC_JSR                           &&
878             (Cond = FindTosCmpCond (E->Arg)) != CMP_INV &&
879             (N = GetNextCodeEntry (S, I)) != 0          &&
880             (N->Info & OF_ZBRA) != 0                    &&
881             !CodeEntryHasLabel (N)) {
882
883             /* The tos... functions will return a boolean value in a/x and
884              * the Z flag says if this value is zero or not. We will call
885              * a cheaper subroutine instead, one that does not return a
886              * boolean value but only valid flags. Note: jeq jumps if
887              * the condition is not met, jne jumps if the condition is met.
888              * Invert the code if we jump on condition not met.
889              */
890             if (GetBranchCond (N->OPC) == BC_EQ) {
891                 /* Jumps if condition false, invert condition */
892                 Cond = CmpInvertTab [Cond];
893             }
894
895             /* Replace the subroutine call. */
896             E = NewCodeEntry (OPC_JSR, AM_ABS, "tosicmp", 0, E->LI);
897             InsertCodeEntry (S, E, I+1);
898             DelCodeEntry (S, I);
899
900             /* Replace the conditional branch */
901             ReplaceCmp (S, I+1, Cond);
902
903             /* Remember, we had changes */
904             ++Changes;
905
906         }
907
908         /* Next entry */
909         ++I;
910
911     }
912
913     /* Return the number of changes made */
914     return Changes;
915 }
916
917
918
919 /*****************************************************************************/
920 /*                            nega optimizations                             */
921 /*****************************************************************************/
922
923
924
925 static unsigned OptNegA1 (CodeSeg* S)
926 /* Check for
927  *
928  *      ldx     #$00
929  *      lda     ..
930  *      jsr     bnega
931  *
932  * Remove the ldx if the lda does not use it.
933  */
934 {
935     unsigned Changes = 0;
936
937     /* Walk over the entries */
938     unsigned I = 0;
939     while (I < GetCodeEntryCount (S)) {
940
941         CodeEntry* L[2];
942
943         /* Get next entry */
944         CodeEntry* E = GetCodeEntry (S, I);
945
946         /* Check for a ldx */
947         if (E->OPC == OPC_LDX                   &&
948             E->AM == AM_IMM                     &&
949             (E->Flags & CEF_NUMARG) != 0        &&
950             E->Num == 0                         &&
951             GetCodeEntries (S, L, I+1, 2)       &&
952             L[0]->OPC == OPC_LDA                &&
953             (L[0]->Use & REG_X) == 0            &&
954             L[1]->OPC == OPC_JSR                &&
955             strcmp (L[1]->Arg, "bnega") == 0) {
956
957             /* Remove the ldx instruction */
958             DelCodeEntry (S, I);
959
960             /* Remember, we had changes */
961             ++Changes;
962
963         }
964
965         /* Next entry */
966         ++I;
967
968     }
969
970     /* Return the number of changes made */
971     return Changes;
972 }
973
974
975
976 static unsigned OptNegA2 (CodeSeg* S)
977 /* Check for
978  *
979  *      lda     ..
980  *      jsr     bnega
981  *      jeq/jne ..
982  *
983  * Adjust the conditional branch and remove the call to the subroutine.
984  */
985 {
986     unsigned Changes = 0;
987
988     /* Walk over the entries */
989     unsigned I = 0;
990     while (I < GetCodeEntryCount (S)) {
991
992         CodeEntry* L[2];
993
994         /* Get next entry */
995         CodeEntry* E = GetCodeEntry (S, I);
996
997         /* Check for the sequence */
998         if (E->OPC == OPC_LDA                   &&
999             GetCodeEntries (S, L, I+1, 2)       &&
1000             L[0]->OPC == OPC_JSR                &&
1001             strcmp (L[0]->Arg, "bnega") == 0    &&
1002             !CodeEntryHasLabel (L[0])           &&
1003             (L[1]->Info & OF_ZBRA) != 0) {
1004
1005             /* Invert the branch */
1006             ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
1007
1008             /* Delete the subroutine call */
1009             DelCodeEntry (S, I+1);
1010
1011             /* Remember, we had changes */
1012             ++Changes;
1013
1014         }
1015
1016         /* Next entry */
1017         ++I;
1018
1019     }
1020
1021     /* Return the number of changes made */
1022     return Changes;
1023 }
1024
1025
1026
1027 /*****************************************************************************/
1028 /*                            negax optimizations                            */
1029 /*****************************************************************************/
1030
1031
1032
1033 static unsigned OptNegAX1 (CodeSeg* S)
1034 /* Search for the sequence:
1035  *
1036  *      lda     (xx),y
1037  *      tax
1038  *      dey
1039  *      lda     (xx),y
1040  *      jsr     bnegax
1041  *      jne/jeq ...
1042  *
1043  * and replace it by
1044  *
1045  *      lda     (xx),y
1046  *      dey
1047  *      ora     (xx),y
1048  *      jeq/jne ...
1049  */
1050 {
1051     unsigned Changes = 0;
1052
1053     /* Walk over the entries */
1054     unsigned I = 0;
1055     while (I < GetCodeEntryCount (S)) {
1056
1057         CodeEntry* L[5];
1058
1059         /* Get next entry */
1060         CodeEntry* E = GetCodeEntry (S, I);
1061
1062         /* Check for the sequence */
1063         if (E->OPC == OPC_LDA                   &&
1064             E->AM == AM_ZP_INDY                 &&
1065             GetCodeEntries (S, L, I+1, 5)       &&
1066             L[0]->OPC == OPC_TAX                &&
1067             L[1]->OPC == OPC_DEY                &&
1068             L[2]->OPC == OPC_LDA                &&
1069             L[2]->AM == AM_ZP_INDY              &&
1070             strcmp (L[2]->Arg, E->Arg) == 0     &&
1071             !CodeEntryHasLabel (L[2])           &&
1072             L[3]->OPC == OPC_JSR                &&
1073             strcmp (L[3]->Arg, "bnegax") == 0   &&
1074             !CodeEntryHasLabel (L[3])           &&
1075             (L[4]->Info & OF_ZBRA) != 0) {
1076
1077             /* lda --> ora */
1078             ReplaceOPC (L[2], OPC_ORA);
1079
1080             /* Invert the branch */
1081             ReplaceOPC (L[4], GetInverseBranch (L[4]->OPC));
1082
1083             /* Delete the entries no longer needed. Beware: Deleting entries
1084              * will change the indices.
1085              */
1086             DelCodeEntry (S, I+4);              /* jsr bnegax */
1087             DelCodeEntry (S, I+1);              /* tax */
1088
1089             /* Remember, we had changes */
1090             ++Changes;
1091
1092         }
1093
1094         /* Next entry */
1095         ++I;
1096
1097     }
1098
1099     /* Return the number of changes made */
1100     return Changes;
1101 }
1102
1103
1104
1105 static unsigned OptNegAX2 (CodeSeg* S)
1106 /* Search for the sequence:
1107  *
1108  *      lda     xx
1109  *      ldx     yy
1110  *      jsr     bnegax
1111  *      jne/jeq ...
1112  *
1113  * and replace it by
1114  *
1115  *      lda     xx
1116  *      ora     xx+1
1117  *      jeq/jne ...
1118  */
1119 {
1120     unsigned Changes = 0;
1121
1122     /* Walk over the entries */
1123     unsigned I = 0;
1124     while (I < GetCodeEntryCount (S)) {
1125
1126         CodeEntry* L[3];
1127
1128         /* Get next entry */
1129         CodeEntry* E = GetCodeEntry (S, I);
1130
1131         /* Check for the sequence */
1132         if (E->OPC == OPC_LDA                   &&
1133             GetCodeEntries (S, L, I+1, 3)       &&
1134             L[0]->OPC == OPC_LDX                &&
1135             !CodeEntryHasLabel (L[0])           &&
1136             L[1]->OPC == OPC_JSR                &&
1137             strcmp (L[1]->Arg, "bnegax") == 0   &&
1138             !CodeEntryHasLabel (L[1])           &&
1139             (L[2]->Info & OF_ZBRA) != 0) {
1140
1141             /* ldx --> ora */
1142             ReplaceOPC (L[0], OPC_ORA);
1143
1144             /* Invert the branch */
1145             ReplaceOPC (L[2], GetInverseBranch (L[2]->OPC));
1146
1147             /* Delete the subroutine call */
1148             DelCodeEntry (S, I+2);
1149
1150             /* Remember, we had changes */
1151             ++Changes;
1152
1153         }
1154
1155         /* Next entry */
1156         ++I;
1157
1158     }
1159
1160     /* Return the number of changes made */
1161     return Changes;
1162 }
1163
1164
1165
1166 static unsigned OptNegAX3 (CodeSeg* S)
1167 /* Search for the sequence:
1168  *
1169  *      jsr     _xxx
1170  *      jsr     bnega(x)
1171  *      jeq/jne ...
1172  *
1173  * and replace it by:
1174  *
1175  *      jsr     _xxx
1176  *      <boolean test>
1177  *      jne/jeq ...
1178  */
1179 {
1180     unsigned Changes = 0;
1181
1182     /* Walk over the entries */
1183     unsigned I = 0;
1184     while (I < GetCodeEntryCount (S)) {
1185
1186         CodeEntry* L[2];
1187
1188         /* Get next entry */
1189         CodeEntry* E = GetCodeEntry (S, I);
1190
1191         /* Check for the sequence */
1192         if (E->OPC == OPC_JSR                   &&
1193             E->Arg[0] == '_'                    &&
1194             GetCodeEntries (S, L, I+1, 2)       &&
1195             L[0]->OPC == OPC_JSR                &&
1196             strncmp (L[0]->Arg,"bnega",5) == 0  &&
1197             !CodeEntryHasLabel (L[0])           &&
1198             (L[1]->Info & OF_ZBRA) != 0) {
1199
1200             CodeEntry* X;
1201
1202             /* Check if we're calling bnega or bnegax */
1203             int ByteSized = (strcmp (L[0]->Arg, "bnega") == 0);
1204
1205             /* Insert apropriate test code */
1206             if (ByteSized) {
1207                 /* Test bytes */
1208                 X = NewCodeEntry (OPC_TAX, AM_IMP, 0, 0, L[0]->LI);
1209                 InsertCodeEntry (S, X, I+2);
1210             } else {
1211                 /* Test words */
1212                 X = NewCodeEntry (OPC_STX, AM_ZP, "tmp1", 0, L[0]->LI);
1213                 InsertCodeEntry (S, X, I+2);
1214                 X = NewCodeEntry (OPC_ORA, AM_ZP, "tmp1", 0, L[0]->LI);
1215                 InsertCodeEntry (S, X, I+3);
1216             }
1217
1218             /* Delete the subroutine call */
1219             DelCodeEntry (S, I+1);
1220
1221             /* Invert the branch */
1222             ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
1223
1224             /* Remember, we had changes */
1225             ++Changes;
1226
1227         }
1228
1229         /* Next entry */
1230         ++I;
1231
1232     }
1233
1234     /* Return the number of changes made */
1235     return Changes;
1236 }
1237
1238
1239
1240 /*****************************************************************************/
1241 /*                                   Code                                    */
1242 /*****************************************************************************/
1243
1244
1245
1246 /* Table with all the optimization functions */
1247 typedef struct OptFunc OptFunc;
1248 struct OptFunc {
1249     unsigned (*Func) (CodeSeg*);/* Optimizer function */
1250     const char* Name;           /* Name of optimizer step */
1251     char        Disabled;       /* True if pass disabled */
1252 };
1253
1254
1255
1256 /* Table with optimizer steps -  are called in this order */
1257 static OptFunc OptFuncs [] = {
1258     /* Optimize subtractions */
1259     { OptSub1,              "OptSub1",                  0       },
1260     { OptSub2,              "OptSub2",                  0       },
1261     /* Optimize additions */
1262     { OptAdd1,              "OptAdd1",                  0       },
1263     /* Optimize jump cascades */
1264     { OptJumpCascades,      "OptJumpCascades",          0       },
1265     /* Remove dead jumps */
1266     { OptDeadJumps,         "OptDeadJumps",             0       },
1267     /* Change jsr/rts to jmp */
1268     { OptRTS,               "OptRTS",                   0       },
1269     /* Remove dead code */
1270     { OptDeadCode,          "OptDeadCode",              0       },
1271     /* Optimize jump targets */
1272     { OptJumpTarget,        "OptJumpTarget",            0       },
1273     /* Optimize conditional branches */
1274     { OptCondBranches,      "OptCondBranches",          0       },
1275     /* Replace jumps to RTS by RTS */
1276     { OptRTSJumps,          "OptRTSJumps",              0       },
1277     /* Remove calls to the bool transformer subroutines */
1278     { OptBoolTransforms,    "OptBoolTransforms",        0       },
1279     /* Optimize calls to nega */
1280     { OptNegA1,             "OptNegA1",                 0       },
1281     { OptNegA2,             "OptNegA2",                 0       },
1282     /* Optimize calls to negax */
1283     { OptNegAX1,            "OptNegAX1",                0       },
1284     { OptNegAX2,            "OptNegAX2",                0       },
1285     { OptNegAX3,            "OptNegAX3",                0       },
1286     /* Optimize compares */
1287     { OptCmp1,              "OptCmp1",                  0       },
1288     { OptCmp2,              "OptCmp2",                  0       },
1289     { OptCmp3,              "OptCmp3",                  0       },
1290     { OptCmp4,              "OptCmp4",                  0       },
1291     { OptCmp5,              "OptCmp5",                  0       },
1292     /* Remove unused loads */
1293     { OptUnusedLoads,       "OptUnusedLoads",           0       },
1294     /* Optimize branch distance */
1295     { OptBranchDist,        "OptBranchDist",            0       },
1296 };
1297
1298
1299
1300 static OptFunc* FindOptStep (const char* Name)
1301 /* Find an optimizer step by name in the table and return a pointer. Print an
1302  * error and call AbEnd if not found.
1303  */
1304 {
1305     unsigned I;
1306
1307     /* Run all optimization steps */
1308     for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
1309         if (strcmp (OptFuncs[I].Name, Name) == 0) {
1310             /* Found */
1311             return OptFuncs+I;
1312         }
1313     }
1314
1315     /* Not found */
1316     AbEnd ("Optimization step `%s' not found", Name);
1317     return 0;
1318 }
1319
1320
1321
1322 void DisableOpt (const char* Name)
1323 /* Disable the optimization with the given name */
1324 {
1325     if (strcmp (Name, "any") == 0) {
1326         unsigned I;
1327         for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
1328             OptFuncs[I].Disabled = 1;
1329         }
1330     } else {
1331         OptFunc* F = FindOptStep (Name);
1332         F->Disabled = 1;
1333     }
1334 }
1335
1336
1337
1338 void EnableOpt (const char* Name)
1339 /* Enable the optimization with the given name */
1340 {
1341     if (strcmp (Name, "any") == 0) {
1342         unsigned I;
1343         for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
1344             OptFuncs[I].Disabled = 0;
1345         }
1346     } else {
1347         OptFunc* F = FindOptStep (Name);
1348         F->Disabled = 0;
1349     }
1350 }
1351
1352
1353
1354 void RunOpt (CodeSeg* S)
1355 /* Run the optimizer */
1356 {
1357     unsigned I;
1358     unsigned Pass = 0;
1359     unsigned OptChanges;
1360
1361     /* If we shouldn't run the optimizer, bail out */
1362     if (!Optimize) {
1363         return;
1364     }
1365
1366     /* Print the name of the function we are working on */
1367     if (S->Func) {
1368         Print (stdout, 1, "Running optimizer for function `%s'\n", S->Func->Name);
1369     } else {
1370         Print (stdout, 1, "Running optimizer for global code segment\n");
1371     }
1372
1373     /* Repeat all steps until there are no more changes */
1374     do {
1375         /* Reset the number of changes */
1376         OptChanges = 0;
1377
1378         /* Keep the user hapy */
1379         Print (stdout, 1, "  Optimizer pass %u:\n", ++Pass);
1380
1381         /* Run all optimization steps */
1382         for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
1383
1384             /* Print the name of the following optimizer step */
1385             Print (stdout, 1, "    %s:%*s", OptFuncs[I].Name,
1386                    (int) (30-strlen(OptFuncs[I].Name)), "");
1387
1388             /* Check if the step is disabled */
1389             if (OptFuncs[I].Disabled) {
1390                 Print (stdout, 1, "Disabled\n");
1391             } else {
1392                 unsigned Changes = OptFuncs[I].Func (S);
1393                 OptChanges += Changes;
1394                 Print (stdout, 1, "%u Changes\n", Changes);
1395             }
1396         }
1397
1398     } while (OptChanges > 0);
1399 }
1400
1401
1402