]> git.sur5r.net Git - cc65/blob - src/cc65/codeopt.c
More splitting
[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 <stdlib.h>
37 #include <string.h>
38
39 /* common */
40 #include "abend.h"
41 #include "chartype.h"
42 #include "print.h"
43 #include "xmalloc.h"
44 #include "xsprintf.h"
45
46 /* cc65 */
47 #include "asmlabel.h"
48 #include "codeent.h"
49 #include "codeinfo.h"
50 #include "coptadd.h"
51 #include "coptcmp.h"
52 #include "coptind.h"
53 #include "coptstop.h"
54 #include "coptsub.h"
55 #include "copttest.h"
56 #include "error.h"
57 #include "global.h"
58 #include "codeopt.h"
59
60
61
62 /*****************************************************************************/
63 /*                              Optimize shifts                              */
64 /*****************************************************************************/
65
66
67
68 static unsigned OptShift1 (CodeSeg* S)
69 /* A call to the shlaxN routine may get replaced by one or more asl insns
70  * if the value of X is not used later.
71  */
72 {
73     unsigned Changes = 0;
74
75     /* Walk over the entries */
76     unsigned I = 0;
77     while (I < CS_GetEntryCount (S)) {
78
79         /* Get next entry */
80         CodeEntry* E = CS_GetEntry (S, I);
81
82         /* Check for the sequence */
83         if (E->OPC == OP65_JSR                       &&
84             (strncmp (E->Arg, "shlax", 5) == 0 ||
85              strncmp (E->Arg, "aslax", 5) == 0)      &&
86             strlen (E->Arg) == 6                     &&
87             IsDigit (E->Arg[5])                      &&
88             !RegXUsed (S, I+1)) {
89
90             /* Insert shift insns */
91             unsigned Count = E->Arg[5] - '0';
92             while (Count--) {
93                 CodeEntry* X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI);
94                 CS_InsertEntry (S, X, I+1);
95             }
96
97             /* Delete the call to shlax */
98             CS_DelEntry (S, I);
99
100             /* Remember, we had changes */
101             ++Changes;
102
103         }
104
105         /* Next entry */
106         ++I;
107
108     }
109
110     /* Return the number of changes made */
111     return Changes;
112 }
113
114
115
116 /*****************************************************************************/
117 /*                            nega optimizations                             */
118 /*****************************************************************************/
119
120
121
122 static unsigned OptNegA1 (CodeSeg* S)
123 /* Check for
124  *
125  *      ldx     #$00
126  *      lda     ..
127  *      jsr     bnega
128  *
129  * Remove the ldx if the lda does not use it.
130  */
131 {
132     unsigned Changes = 0;
133
134     /* Walk over the entries */
135     unsigned I = 0;
136     while (I < CS_GetEntryCount (S)) {
137
138         CodeEntry* L[2];
139
140         /* Get next entry */
141         CodeEntry* E = CS_GetEntry (S, I);
142
143         /* Check for a ldx */
144         if (E->OPC == OP65_LDX                  &&
145             E->AM == AM65_IMM                   &&
146             (E->Flags & CEF_NUMARG) != 0        &&
147             E->Num == 0                         &&
148             CS_GetEntries (S, L, I+1, 2)        &&
149             L[0]->OPC == OP65_LDA               &&
150             (L[0]->Use & REG_X) == 0            &&
151             !CE_HasLabel (L[0])                 &&
152             L[1]->OPC == OP65_JSR               &&
153             strcmp (L[1]->Arg, "bnega") == 0    &&
154             !CE_HasLabel (L[1])) {
155
156             /* Remove the ldx instruction */
157             CS_DelEntry (S, I);
158
159             /* Remember, we had changes */
160             ++Changes;
161
162         }
163
164         /* Next entry */
165         ++I;
166
167     }
168
169     /* Return the number of changes made */
170     return Changes;
171 }
172
173
174
175 static unsigned OptNegA2 (CodeSeg* S)
176 /* Check for
177  *
178  *      lda     ..
179  *      jsr     bnega
180  *      jeq/jne ..
181  *
182  * Adjust the conditional branch and remove the call to the subroutine.
183  */
184 {
185     unsigned Changes = 0;
186
187     /* Walk over the entries */
188     unsigned I = 0;
189     while (I < CS_GetEntryCount (S)) {
190
191         CodeEntry* L[2];
192
193         /* Get next entry */
194         CodeEntry* E = CS_GetEntry (S, I);
195
196         /* Check for the sequence */
197         if ((E->OPC == OP65_ADC ||
198              E->OPC == OP65_AND ||
199              E->OPC == OP65_DEA ||
200              E->OPC == OP65_EOR ||
201              E->OPC == OP65_INA ||
202              E->OPC == OP65_LDA ||
203              E->OPC == OP65_ORA ||
204              E->OPC == OP65_PLA ||
205              E->OPC == OP65_SBC ||
206              E->OPC == OP65_TXA ||
207              E->OPC == OP65_TYA)                &&
208             CS_GetEntries (S, L, I+1, 2)        &&
209             L[0]->OPC == OP65_JSR               &&
210             strcmp (L[0]->Arg, "bnega") == 0    &&
211             !CE_HasLabel (L[0])                 &&
212             (L[1]->Info & OF_ZBRA) != 0         &&
213             !CE_HasLabel (L[1])) {
214
215             /* Invert the branch */
216             CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
217
218             /* Delete the subroutine call */
219             CS_DelEntry (S, I+1);
220
221             /* Remember, we had changes */
222             ++Changes;
223
224         }
225
226         /* Next entry */
227         ++I;
228
229     }
230
231     /* Return the number of changes made */
232     return Changes;
233 }
234
235
236
237 /*****************************************************************************/
238 /*                            negax optimizations                            */
239 /*****************************************************************************/
240
241
242
243 static unsigned OptNegAX1 (CodeSeg* S)
244 /* On a call to bnegax, if X is zero, the result depends only on the value in
245  * A, so change the call to a call to bnega. This will get further optimized
246  * later if possible.
247  */
248 {
249     unsigned Changes = 0;
250     unsigned I;
251
252     /* Generate register info for this step */
253     CS_GenRegInfo (S);
254
255     /* Walk over the entries */
256     I = 0;
257     while (I < CS_GetEntryCount (S)) {
258
259         /* Get next entry */
260         CodeEntry* E = CS_GetEntry (S, I);
261
262         /* Check if this is a call to bnegax, and if X is known and zero */
263         if (E->OPC == OP65_JSR              &&
264             E->RI->In.RegX == 0             &&
265             strcmp (E->Arg, "bnegax") == 0) {
266
267             /* We're cheating somewhat here ... */
268             E->Arg[5] = '\0';
269             E->Use &= ~REG_X;
270
271             /* We had changes */
272             ++Changes;
273         }
274
275         /* Next entry */
276         ++I;
277
278     }
279
280     /* Free register info */
281     CS_FreeRegInfo (S);
282
283     /* Return the number of changes made */
284     return Changes;
285 }
286
287
288
289 static unsigned OptNegAX2 (CodeSeg* S)
290 /* Search for the sequence:
291  *
292  *      lda     (xx),y
293  *      tax
294  *      dey
295  *      lda     (xx),y
296  *      jsr     bnegax
297  *      jne/jeq ...
298  *
299  * and replace it by
300  *
301  *      lda     (xx),y
302  *      dey
303  *      ora     (xx),y
304  *      jeq/jne ...
305  */
306 {
307     unsigned Changes = 0;
308
309     /* Walk over the entries */
310     unsigned I = 0;
311     while (I < CS_GetEntryCount (S)) {
312
313         CodeEntry* L[5];
314
315         /* Get next entry */
316         CodeEntry* E = CS_GetEntry (S, I);
317
318         /* Check for the sequence */
319         if (E->OPC == OP65_LDA                  &&
320             E->AM == AM65_ZP_INDY               &&
321             CS_GetEntries (S, L, I+1, 5)        &&
322             L[0]->OPC == OP65_TAX               &&
323             L[1]->OPC == OP65_DEY               &&
324             L[2]->OPC == OP65_LDA               &&
325             L[2]->AM == AM65_ZP_INDY            &&
326             strcmp (L[2]->Arg, E->Arg) == 0     &&
327             !CE_HasLabel (L[2])                 &&
328             L[3]->OPC == OP65_JSR               &&
329             strcmp (L[3]->Arg, "bnegax") == 0   &&
330             !CE_HasLabel (L[3])                 &&
331             (L[4]->Info & OF_ZBRA) != 0         &&
332             !CE_HasLabel (L[4])) {
333
334             /* lda --> ora */
335             CE_ReplaceOPC (L[2], OP65_ORA);
336
337             /* Invert the branch */
338             CE_ReplaceOPC (L[4], GetInverseBranch (L[4]->OPC));
339
340             /* Delete the entries no longer needed. Beware: Deleting entries
341              * will change the indices.
342              */
343             CS_DelEntry (S, I+4);               /* jsr bnegax */
344             CS_DelEntry (S, I+1);               /* tax */
345
346             /* Remember, we had changes */
347             ++Changes;
348
349         }
350
351         /* Next entry */
352         ++I;
353
354     }
355
356     /* Return the number of changes made */
357     return Changes;
358 }
359
360
361
362 static unsigned OptNegAX3 (CodeSeg* S)
363 /* Search for the sequence:
364  *
365  *      lda     xx
366  *      ldx     yy
367  *      jsr     bnegax
368  *      jne/jeq ...
369  *
370  * and replace it by
371  *
372  *      lda     xx
373  *      ora     xx+1
374  *      jeq/jne ...
375  */
376 {
377     unsigned Changes = 0;
378
379     /* Walk over the entries */
380     unsigned I = 0;
381     while (I < CS_GetEntryCount (S)) {
382
383         CodeEntry* L[3];
384
385         /* Get next entry */
386         CodeEntry* E = CS_GetEntry (S, I);
387
388         /* Check for the sequence */
389         if (E->OPC == OP65_LDA                  &&
390             CS_GetEntries (S, L, I+1, 3)        &&
391             L[0]->OPC == OP65_LDX               &&
392             !CE_HasLabel (L[0])                 &&
393             L[1]->OPC == OP65_JSR               &&
394             strcmp (L[1]->Arg, "bnegax") == 0   &&
395             !CE_HasLabel (L[1])                 &&
396             (L[2]->Info & OF_ZBRA) != 0         &&
397             !CE_HasLabel (L[2])) {
398
399             /* ldx --> ora */
400             CE_ReplaceOPC (L[0], OP65_ORA);
401
402             /* Invert the branch */
403             CE_ReplaceOPC (L[2], GetInverseBranch (L[2]->OPC));
404
405             /* Delete the subroutine call */
406             CS_DelEntry (S, I+2);
407
408             /* Remember, we had changes */
409             ++Changes;
410
411         }
412
413         /* Next entry */
414         ++I;
415
416     }
417
418     /* Return the number of changes made */
419     return Changes;
420 }
421
422
423
424 static unsigned OptNegAX4 (CodeSeg* S)
425 /* Search for the sequence:
426  *
427  *      jsr     xxx
428  *      jsr     bnega(x)
429  *      jeq/jne ...
430  *
431  * and replace it by:
432  *
433  *      jsr     xxx
434  *      <boolean test>
435  *      jne/jeq ...
436  */
437 {
438     unsigned Changes = 0;
439
440     /* Walk over the entries */
441     unsigned I = 0;
442     while (I < CS_GetEntryCount (S)) {
443
444         CodeEntry* L[2];
445
446         /* Get next entry */
447         CodeEntry* E = CS_GetEntry (S, I);
448
449         /* Check for the sequence */
450         if (E->OPC == OP65_JSR                  &&
451             CS_GetEntries (S, L, I+1, 2)        &&
452             L[0]->OPC == OP65_JSR               &&
453             strncmp (L[0]->Arg,"bnega",5) == 0  &&
454             !CE_HasLabel (L[0])                 &&
455             (L[1]->Info & OF_ZBRA) != 0         &&
456             !CE_HasLabel (L[1])) {
457
458             CodeEntry* X;
459
460             /* Check if we're calling bnega or bnegax */
461             int ByteSized = (strcmp (L[0]->Arg, "bnega") == 0);
462
463             /* Insert apropriate test code */
464             if (ByteSized) {
465                 /* Test bytes */
466                 X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI);
467                 CS_InsertEntry (S, X, I+2);
468             } else {
469                 /* Test words */
470                 X = NewCodeEntry (OP65_STX, AM65_ZP, "tmp1", 0, L[0]->LI);
471                 CS_InsertEntry (S, X, I+2);
472                 X = NewCodeEntry (OP65_ORA, AM65_ZP, "tmp1", 0, L[0]->LI);
473                 CS_InsertEntry (S, X, I+3);
474             }
475
476             /* Delete the subroutine call */
477             CS_DelEntry (S, I+1);
478
479             /* Invert the branch */
480             CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
481
482             /* Remember, we had changes */
483             ++Changes;
484
485         }
486
487         /* Next entry */
488         ++I;
489
490     }
491
492     /* Return the number of changes made */
493     return Changes;
494 }
495
496
497
498 /*****************************************************************************/
499 /*                     Optimize stores through pointers                      */
500 /*****************************************************************************/
501
502
503
504 static unsigned OptPtrStore1Sub (CodeSeg* S, unsigned I, CodeEntry** const L)
505 /* Check if this is one of the allowed suboperation for OptPtrStore1 */
506 {
507     /* Check for a label attached to the entry */
508     if (CE_HasLabel (L[0])) {
509         return 0;
510     }
511
512     /* Check for single insn sub ops */
513     if (L[0]->OPC == OP65_AND                                           ||
514         L[0]->OPC == OP65_EOR                                           ||
515         L[0]->OPC == OP65_ORA                                           ||
516         (L[0]->OPC == OP65_JSR && strncmp (L[0]->Arg, "shlax", 5) == 0) ||
517         (L[0]->OPC == OP65_JSR && strncmp (L[0]->Arg, "shrax", 5) == 0)) {
518
519         /* One insn */
520         return 1;
521
522     } else if (L[0]->OPC == OP65_CLC                      &&
523                (L[1] = CS_GetNextEntry (S, I)) != 0       &&
524                L[1]->OPC == OP65_ADC                      &&
525                !CE_HasLabel (L[1])) {
526         return 2;
527     } else if (L[0]->OPC == OP65_SEC                      &&
528                (L[1] = CS_GetNextEntry (S, I)) != 0       &&
529                L[1]->OPC == OP65_SBC                      &&
530                !CE_HasLabel (L[1])) {
531         return 2;
532     }
533
534
535
536     /* Not found */
537     return 0;
538 }
539
540
541
542 static unsigned OptPtrStore1 (CodeSeg* S)
543 /* Search for the sequence:
544  *
545  *      jsr     pushax
546  *      ldy     xxx
547  *      jsr     ldauidx
548  *      subop
549  *      ldy     yyy
550  *      jsr     staspidx
551  *
552  * and replace it by:
553  *
554  *      sta     ptr1
555  *      stx     ptr1+1
556  *      ldy     xxx
557  *      ldx     #$00
558  *      lda     (ptr1),y
559  *      subop
560  *      ldy     yyy
561  *      sta     (ptr1),y
562  */
563 {
564     unsigned Changes = 0;
565
566     /* Walk over the entries */
567     unsigned I = 0;
568     while (I < CS_GetEntryCount (S)) {
569
570         unsigned K;
571         CodeEntry* L[10];
572
573         /* Get next entry */
574         L[0] = CS_GetEntry (S, I);
575
576         /* Check for the sequence */
577         if (L[0]->OPC == OP65_JSR                   &&
578             strcmp (L[0]->Arg, "pushax") == 0       &&
579             CS_GetEntries (S, L+1, I+1, 3)          &&
580             L[1]->OPC == OP65_LDY                   &&
581             CE_KnownImm (L[1])                      &&
582             !CE_HasLabel (L[1])                     &&
583             L[2]->OPC == OP65_JSR                   &&
584             strcmp (L[2]->Arg, "ldauidx") == 0      &&
585             !CE_HasLabel (L[2])                     &&
586             (K = OptPtrStore1Sub (S, I+3, L+3)) > 0 &&
587             CS_GetEntries (S, L+3+K, I+3+K, 2)      &&
588             L[3+K]->OPC == OP65_LDY                 &&
589             CE_KnownImm (L[3+K])                    &&
590             !CE_HasLabel (L[3+K])                   &&
591             L[4+K]->OPC == OP65_JSR                 &&
592             strcmp (L[4+K]->Arg, "staspidx") == 0   &&
593             !CE_HasLabel (L[4+K])) {
594
595             CodeEntry* X;
596
597             /* Create and insert the stores */
598             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
599             CS_InsertEntry (S, X, I+1);
600
601             X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
602             CS_InsertEntry (S, X, I+2);
603
604             /* Delete the call to pushax */
605             CS_DelEntry (S, I);
606
607             /* Delete the call to ldauidx */
608             CS_DelEntry (S, I+3);
609
610             /* Insert the load from ptr1 */
611             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[3]->LI);
612             CS_InsertEntry (S, X, I+3);
613             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[2]->LI);
614             CS_InsertEntry (S, X, I+4);
615
616             /* Insert the store through ptr1 */
617             X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "ptr1", 0, L[3]->LI);
618             CS_InsertEntry (S, X, I+6+K);
619
620             /* Delete the call to staspidx */
621             CS_DelEntry (S, I+7+K);
622
623             /* Remember, we had changes */
624             ++Changes;
625
626         }
627
628         /* Next entry */
629         ++I;
630
631     }
632
633     /* Return the number of changes made */
634     return Changes;
635 }
636
637
638
639 static unsigned OptPtrStore2 (CodeSeg* S)
640 /* Search for the sequence:
641  *
642  *      jsr     pushax
643  *      lda     xxx
644  *      ldy     yyy
645  *      jsr     staspidx
646  *
647  * and replace it by:
648  *
649  *      sta     ptr1
650  *      stx     ptr1+1
651  *      lda     xxx
652  *      ldy     yyy
653  *      sta     (ptr1),y
654  */
655 {
656     unsigned Changes = 0;
657
658     /* Walk over the entries */
659     unsigned I = 0;
660     while (I < CS_GetEntryCount (S)) {
661
662         CodeEntry* L[4];
663
664         /* Get next entry */
665         L[0] = CS_GetEntry (S, I);
666
667         /* Check for the sequence */
668         if (L[0]->OPC == OP65_JSR               &&
669             strcmp (L[0]->Arg, "pushax") == 0   &&
670             CS_GetEntries (S, L+1, I+1, 3)      &&
671             L[1]->OPC == OP65_LDA               &&
672             !CE_HasLabel (L[1])                 &&
673             L[2]->OPC == OP65_LDY               &&
674             !CE_HasLabel (L[2])                 &&
675             L[3]->OPC == OP65_JSR               &&
676             strcmp (L[3]->Arg, "staspidx") == 0 &&
677             !CE_HasLabel (L[3])) {
678
679             CodeEntry* X;
680
681             /* Create and insert the stores */
682             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
683             CS_InsertEntry (S, X, I+1);
684
685             X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
686             CS_InsertEntry (S, X, I+2);
687
688             /* Delete the call to pushax */
689             CS_DelEntry (S, I);
690
691             /* Insert the store through ptr1 */
692             X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "ptr1", 0, L[3]->LI);
693             CS_InsertEntry (S, X, I+4);
694
695             /* Delete the call to staspidx */
696             CS_DelEntry (S, I+5);
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 /*****************************************************************************/
715 /*                      Optimize loads through pointers                      */
716 /*****************************************************************************/
717
718
719
720 static unsigned OptPtrLoad1 (CodeSeg* S)
721 /* Search for the sequence:
722  *
723  *      tax
724  *      dey
725  *      lda     (sp),y             # May be any destination
726  *      ldy     ...
727  *      jsr     ldauidx
728  *
729  * and replace it by:
730  *
731  *      sta     ptr1+1
732  *      dey
733  *      lda     (sp),y
734  *      sta     ptr1
735  *      ldy     ...
736  *      ldx     #$00
737  *      lda     (ptr1),y
738  */
739 {
740     unsigned Changes = 0;
741
742     /* Walk over the entries */
743     unsigned I = 0;
744     while (I < CS_GetEntryCount (S)) {
745
746         CodeEntry* L[5];
747
748         /* Get next entry */
749         L[0] = CS_GetEntry (S, I);
750
751         /* Check for the sequence */
752         if (L[0]->OPC == OP65_TAX               &&
753             CS_GetEntries (S, L+1, I+1, 4)      &&
754             L[1]->OPC == OP65_DEY               &&
755             !CE_HasLabel (L[1])                 &&
756             L[2]->OPC == OP65_LDA               &&
757             !CE_HasLabel (L[2])                 &&
758             L[3]->OPC == OP65_LDY               &&
759             !CE_HasLabel (L[3])                 &&
760             L[4]->OPC == OP65_JSR               &&
761             strcmp (L[4]->Arg, "ldauidx") == 0  &&
762             !CE_HasLabel (L[4])) {
763
764             CodeEntry* X;
765
766             /* Store the high byte and remove the TAX instead */
767             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1+1", 0, L[0]->LI);
768             CS_InsertEntry (S, X, I+1);
769             CS_DelEntry (S, I);
770
771             /* Store the low byte */
772             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[2]->LI);
773             CS_InsertEntry (S, X, I+3);
774
775             /* Delete the call to ldauidx */
776             CS_DelEntry (S, I+5);
777
778             /* Load high and low byte */
779             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[3]->LI);
780             CS_InsertEntry (S, X, I+5);
781             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[3]->LI);
782             CS_InsertEntry (S, X, I+6);
783
784             /* Remember, we had changes */
785             ++Changes;
786
787         }
788
789         /* Next entry */
790         ++I;
791
792     }
793
794     /* Return the number of changes made */
795     return Changes;
796 }
797
798
799
800 static unsigned OptPtrLoad2 (CodeSeg* S)
801 /* Search for the sequence:
802  *
803  *      adc     xxx
804  *      tay
805  *      txa
806  *      adc     yyy
807  *      tax
808  *      tya
809  *      ldy
810  *      jsr     ldauidx
811  *
812  * and replace it by:
813  *
814  *      adc     xxx
815  *      sta     ptr1
816  *      txa
817  *      adc     yyy
818  *      sta     ptr1+1
819  *      ldy
820  *      ldx     #$00
821  *      lda     (ptr1),y
822  */
823 {
824     unsigned Changes = 0;
825
826     /* Walk over the entries */
827     unsigned I = 0;
828     while (I < CS_GetEntryCount (S)) {
829
830         CodeEntry* L[8];
831
832         /* Get next entry */
833         L[0] = CS_GetEntry (S, I);
834
835         /* Check for the sequence */
836         if (L[0]->OPC == OP65_ADC               &&
837             CS_GetEntries (S, L+1, I+1, 7)      &&
838             L[1]->OPC == OP65_TAY               &&
839             !CE_HasLabel (L[1])                 &&
840             L[2]->OPC == OP65_TXA               &&
841             !CE_HasLabel (L[2])                 &&
842             L[3]->OPC == OP65_ADC               &&
843             !CE_HasLabel (L[3])                 &&
844             L[4]->OPC == OP65_TAX               &&
845             !CE_HasLabel (L[4])                 &&
846             L[5]->OPC == OP65_TYA               &&
847             !CE_HasLabel (L[5])                 &&
848             L[6]->OPC == OP65_LDY               &&
849             !CE_HasLabel (L[6])                 &&
850             L[7]->OPC == OP65_JSR               &&
851             strcmp (L[7]->Arg, "ldauidx") == 0  &&
852             !CE_HasLabel (L[7])) {
853
854             CodeEntry* X;
855
856             /* Store the low byte and remove the TAY instead */
857             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
858             CS_InsertEntry (S, X, I+1);
859             CS_DelEntry (S, I+2);
860
861             /* Store the high byte */
862             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1+1", 0, L[3]->LI);
863             CS_InsertEntry (S, X, I+4);
864
865             /* Delete more transfer insns */
866             CS_DelEntry (S, I+6);
867             CS_DelEntry (S, I+5);
868
869             /* Delete the call to ldauidx */
870             CS_DelEntry (S, I+6);
871
872             /* Load high and low byte */
873             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[6]->LI);
874             CS_InsertEntry (S, X, I+6);
875             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[6]->LI);
876             CS_InsertEntry (S, X, I+7);
877
878             /* Remember, we had changes */
879             ++Changes;
880
881         }
882
883         /* Next entry */
884         ++I;
885
886     }
887
888     /* Return the number of changes made */
889     return Changes;
890 }
891
892
893
894 static unsigned OptPtrLoad3 (CodeSeg* S)
895 /* Search for the sequence:
896  *
897  *      adc     xxx
898  *      pha
899  *      txa
900  *      iny
901  *      adc     yyy
902  *      tax
903  *      pla
904  *      ldy
905  *      jsr     ldauidx
906  *
907  * and replace it by:
908  *
909  *      adc     xxx
910  *      sta     ptr1
911  *      txa
912  *      iny
913  *      adc     yyy
914  *      sta     ptr1+1
915  *      ldy
916  *      ldx     #$00
917  *      lda     (ptr1),y
918  */
919 {
920     unsigned Changes = 0;
921
922     /* Walk over the entries */
923     unsigned I = 0;
924     while (I < CS_GetEntryCount (S)) {
925
926         CodeEntry* L[9];
927
928         /* Get next entry */
929         L[0] = CS_GetEntry (S, I);
930
931         /* Check for the sequence */
932         if (L[0]->OPC == OP65_ADC               &&
933             CS_GetEntries (S, L+1, I+1, 8)      &&
934             L[1]->OPC == OP65_PHA               &&
935             !CE_HasLabel (L[1])                 &&
936             L[2]->OPC == OP65_TXA               &&
937             !CE_HasLabel (L[2])                 &&
938             L[3]->OPC == OP65_INY               &&
939             !CE_HasLabel (L[3])                 &&
940             L[4]->OPC == OP65_ADC               &&
941             !CE_HasLabel (L[4])                 &&
942             L[5]->OPC == OP65_TAX               &&
943             !CE_HasLabel (L[5])                 &&
944             L[6]->OPC == OP65_PLA               &&
945             !CE_HasLabel (L[6])                 &&
946             L[7]->OPC == OP65_LDY               &&
947             !CE_HasLabel (L[7])                 &&
948             L[8]->OPC == OP65_JSR               &&
949             strcmp (L[8]->Arg, "ldauidx") == 0  &&
950             !CE_HasLabel (L[8])) {
951
952             CodeEntry* X;
953
954             /* Store the low byte and remove the PHA instead */
955             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
956             CS_InsertEntry (S, X, I+1);
957             CS_DelEntry (S, I+2);
958
959             /* Store the high byte */
960             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1+1", 0, L[4]->LI);
961             CS_InsertEntry (S, X, I+5);
962
963             /* Delete more transfer and PLA insns */
964             CS_DelEntry (S, I+7);
965             CS_DelEntry (S, I+6);
966
967             /* Delete the call to ldauidx */
968             CS_DelEntry (S, I+7);
969
970             /* Load high and low byte */
971             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[6]->LI);
972             CS_InsertEntry (S, X, I+7);
973             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[6]->LI);
974             CS_InsertEntry (S, X, I+8);
975
976             /* Remember, we had changes */
977             ++Changes;
978
979         }
980
981         /* Next entry */
982         ++I;
983
984     }
985
986     /* Return the number of changes made */
987     return Changes;
988 }
989
990
991
992 static unsigned OptPtrLoad4 (CodeSeg* S)
993 /* Search for the sequence:
994  *
995  *      lda     #<(label+0)
996  *      ldx     #>(label+0)
997  *      clc
998  *      adc     xxx
999  *      bcc     L
1000  *      inx
1001  * L:   ldy     #$00
1002  *      jsr     ldauidx
1003  *
1004  * and replace it by:
1005  *
1006  *      ldy     xxx
1007  *      ldx     #$00
1008  *      lda     label,y
1009  */
1010 {
1011     unsigned Changes = 0;
1012
1013     /* Walk over the entries */
1014     unsigned I = 0;
1015     while (I < CS_GetEntryCount (S)) {
1016
1017         CodeEntry* L[8];
1018         unsigned Len;
1019
1020         /* Get next entry */
1021         L[0] = CS_GetEntry (S, I);
1022
1023         /* Check for the sequence */
1024         if (L[0]->OPC == OP65_LDA                            &&
1025             L[0]->AM == AM65_IMM                             &&
1026             CS_GetEntries (S, L+1, I+1, 7)                   &&
1027             L[1]->OPC == OP65_LDX                            &&
1028             L[1]->AM == AM65_IMM                             &&
1029             !CE_HasLabel (L[1])                              &&
1030             L[2]->OPC == OP65_CLC                            &&
1031             !CE_HasLabel (L[2])                              &&
1032             L[3]->OPC == OP65_ADC                            &&
1033             (L[3]->AM == AM65_ABS || L[3]->AM == AM65_ZP)    &&
1034             !CE_HasLabel (L[3])                              &&
1035             (L[4]->OPC == OP65_BCC || L[4]->OPC == OP65_JCC) &&
1036             L[4]->JumpTo != 0                                &&
1037             L[4]->JumpTo->Owner == L[6]                      &&
1038             !CE_HasLabel (L[4])                              &&
1039             L[5]->OPC == OP65_INX                            &&
1040             !CE_HasLabel (L[5])                              &&
1041             L[6]->OPC == OP65_LDY                            &&
1042             CE_KnownImm (L[6])                               &&
1043             L[6]->Num == 0                                   &&
1044             L[7]->OPC == OP65_JSR                            &&
1045             strcmp (L[7]->Arg, "ldauidx") == 0               &&
1046             !CE_HasLabel (L[7])                              &&
1047             /* Check the label last because this is quite costly */
1048             (Len = strlen (L[0]->Arg)) > 3                   &&
1049             L[0]->Arg[0] == '<'                              &&
1050             L[0]->Arg[1] == '('                              &&
1051             strlen (L[1]->Arg) == Len                        &&
1052             L[1]->Arg[0] == '>'                              &&
1053             memcmp (L[0]->Arg+1, L[1]->Arg+1, Len-1) == 0) {
1054
1055             CodeEntry* X;
1056             char* Label;
1057
1058             /* We will create all the new stuff behind the current one so
1059              * we keep the line references.
1060              */
1061             X = NewCodeEntry (OP65_LDY, L[3]->AM, L[3]->Arg, 0, L[0]->LI);
1062             CS_InsertEntry (S, X, I+8);
1063
1064             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[0]->LI);
1065             CS_InsertEntry (S, X, I+9);
1066
1067             Label = memcpy (xmalloc (Len-2), L[0]->Arg+2, Len-3);
1068             Label[Len-3] = '\0';
1069             X = NewCodeEntry (OP65_LDA, AM65_ABSY, Label, 0, L[0]->LI);
1070             CS_InsertEntry (S, X, I+10);
1071             xfree (Label);
1072
1073             /* Remove the old code */
1074             CS_DelEntries (S, I, 8);
1075
1076             /* Remember, we had changes */
1077             ++Changes;
1078
1079         }
1080
1081         /* Next entry */
1082         ++I;
1083
1084     }
1085
1086     /* Return the number of changes made */
1087     return Changes;
1088 }
1089
1090
1091
1092 static unsigned OptPtrLoad5 (CodeSeg* S)
1093 /* Search for the sequence:
1094  *
1095  *      lda     #<(label+0)
1096  *      ldx     #>(label+0)
1097  *      ldy     #$xx
1098  *      clc
1099  *      adc     (sp),y
1100  *      bcc     L
1101  *      inx
1102  * L:   ldy     #$00
1103  *      jsr     ldauidx
1104  *
1105  * and replace it by:
1106  *
1107  *      ldy     #$xx
1108  *      lda     (sp),y
1109  *      tay
1110  *      ldx     #$00
1111  *      lda     label,y
1112  */
1113 {
1114     unsigned Changes = 0;
1115
1116     /* Walk over the entries */
1117     unsigned I = 0;
1118     while (I < CS_GetEntryCount (S)) {
1119
1120         CodeEntry* L[9];
1121         unsigned Len;
1122
1123         /* Get next entry */
1124         L[0] = CS_GetEntry (S, I);
1125
1126         /* Check for the sequence */
1127         if (L[0]->OPC == OP65_LDA                            &&
1128             L[0]->AM == AM65_IMM                             &&
1129             CS_GetEntries (S, L+1, I+1, 8)                   &&
1130             L[1]->OPC == OP65_LDX                            &&
1131             L[1]->AM == AM65_IMM                             &&
1132             !CE_HasLabel (L[1])                              &&
1133             L[2]->OPC == OP65_LDY                            &&
1134             CE_KnownImm (L[2])                               &&
1135             !CE_HasLabel (L[2])                              &&
1136             L[3]->OPC == OP65_CLC                            &&
1137             !CE_HasLabel (L[3])                              &&
1138             L[4]->OPC == OP65_ADC                            &&
1139             L[4]->AM == AM65_ZP_INDY                         &&
1140             !CE_HasLabel (L[4])                              &&
1141             (L[5]->OPC == OP65_BCC || L[5]->OPC == OP65_JCC) &&
1142             L[5]->JumpTo != 0                                &&
1143             L[5]->JumpTo->Owner == L[7]                      &&
1144             !CE_HasLabel (L[5])                              &&
1145             L[6]->OPC == OP65_INX                            &&
1146             !CE_HasLabel (L[6])                              &&
1147             L[7]->OPC == OP65_LDY                            &&
1148             CE_KnownImm (L[7])                               &&
1149             L[7]->Num == 0                                   &&
1150             L[8]->OPC == OP65_JSR                            &&
1151             strcmp (L[8]->Arg, "ldauidx") == 0               &&
1152             !CE_HasLabel (L[8])                              &&
1153             /* Check the label last because this is quite costly */
1154             (Len = strlen (L[0]->Arg)) > 3                   &&
1155             L[0]->Arg[0] == '<'                              &&
1156             L[0]->Arg[1] == '('                              &&
1157             strlen (L[1]->Arg) == Len                        &&
1158             L[1]->Arg[0] == '>'                              &&
1159             memcmp (L[0]->Arg+1, L[1]->Arg+1, Len-1) == 0) {
1160
1161             CodeEntry* X;
1162             char* Label;
1163
1164             /* Add the lda */
1165             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, L[4]->Arg, 0, L[0]->LI);
1166             CS_InsertEntry (S, X, I+3);
1167
1168             /* Add the tay */
1169             X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, L[0]->LI);
1170             CS_InsertEntry (S, X, I+4);
1171
1172             /* Add the ldx */
1173             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[0]->LI);
1174             CS_InsertEntry (S, X, I+5);
1175
1176             /* Add the lda */
1177             Label = memcpy (xmalloc (Len-2), L[0]->Arg+2, Len-3);
1178             Label[Len-3] = '\0';
1179             X = NewCodeEntry (OP65_LDA, AM65_ABSY, Label, 0, L[0]->LI);
1180             CS_InsertEntry (S, X, I+6);
1181             xfree (Label);
1182
1183             /* Remove the old code */
1184             CS_DelEntries (S, I, 2);
1185             CS_DelEntries (S, I+5, 6);
1186
1187             /* Remember, we had changes */
1188             ++Changes;
1189
1190         }
1191
1192         /* Next entry */
1193         ++I;
1194
1195     }
1196
1197     /* Return the number of changes made */
1198     return Changes;
1199 }
1200
1201
1202
1203 static unsigned OptPtrLoad6 (CodeSeg* S)
1204 /* Search for the sequence
1205  *
1206  *      ldy     ...
1207  *      jsr     ldauidx
1208  *
1209  * and replace it by:
1210  *
1211  *      ldy     ...
1212  *      stx     ptr1+1
1213  *      sta     ptr1
1214  *      ldx     #$00
1215  *      lda     (ptr1),y
1216  *
1217  * This step must be execute *after* OptPtrLoad1!
1218  */
1219 {
1220     unsigned Changes = 0;
1221
1222     /* Walk over the entries */
1223     unsigned I = 0;
1224     while (I < CS_GetEntryCount (S)) {
1225
1226         CodeEntry* L[2];
1227
1228         /* Get next entry */
1229         L[0] = CS_GetEntry (S, I);
1230
1231         /* Check for the sequence */
1232         if (L[0]->OPC == OP65_LDY               &&
1233             CS_GetEntries (S, L+1, I+1, 1)      &&
1234             L[1]->OPC == OP65_JSR               &&
1235             strcmp (L[1]->Arg, "ldauidx") == 0  &&
1236             !CE_HasLabel (L[1])) {
1237
1238             CodeEntry* X;
1239
1240             /* Store the high byte */
1241             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
1242             CS_InsertEntry (S, X, I+1);
1243
1244             /* Store the low byte */
1245             X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
1246             CS_InsertEntry (S, X, I+2);
1247
1248             /* Delete the call to ldauidx */
1249             CS_DelEntry (S, I+3);
1250
1251             /* Load the high and low byte */
1252             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[0]->LI);
1253             CS_InsertEntry (S, X, I+3);
1254             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[0]->LI);
1255             CS_InsertEntry (S, X, I+4);
1256
1257             /* Remember, we had changes */
1258             ++Changes;
1259
1260         }
1261
1262         /* Next entry */
1263         ++I;
1264
1265     }
1266
1267     /* Return the number of changes made */
1268     return Changes;
1269 }
1270
1271
1272
1273 /*****************************************************************************/
1274 /*                              struct OptFunc                               */
1275 /*****************************************************************************/
1276
1277
1278
1279 typedef struct OptFunc OptFunc;
1280 struct OptFunc {
1281     unsigned       (*Func) (CodeSeg*);  /* Optimizer function */
1282     const char*    Name;                /* Name of the function/group */
1283     unsigned long  TotalRuns;           /* Total number of runs */
1284     unsigned long  LastRuns;            /* Last number of runs */
1285     unsigned long  TotalChanges;        /* Total number of changes */
1286     unsigned long  LastChanges;         /* Last number of changes */
1287     char           Disabled;            /* True if function disabled */
1288 };
1289
1290
1291
1292 /*****************************************************************************/
1293 /*                                   Code                                    */
1294 /*****************************************************************************/
1295
1296
1297
1298 /* Macro that builds a function description */
1299 #define OptFuncEntry(func) static OptFuncDesc D##func = { func, #func, 0 }
1300
1301 /* A list of all the function descriptions */
1302 static OptFunc DOptPtrStore1    = { OptPtrStore1,    "OptPtrStore1",    0, 0, 0, 0, 0 };
1303 static OptFunc DOptPtrStore2    = { OptPtrStore2,    "OptPtrStore2",    0, 0, 0, 0, 0 };
1304 static OptFunc DOptPtrLoad1     = { OptPtrLoad1,     "OptPtrLoad1",     0, 0, 0, 0, 0 };
1305 static OptFunc DOptPtrLoad2     = { OptPtrLoad2,     "OptPtrLoad2",     0, 0, 0, 0, 0 };
1306 static OptFunc DOptPtrLoad3     = { OptPtrLoad3,     "OptPtrLoad3",     0, 0, 0, 0, 0 };
1307 static OptFunc DOptPtrLoad4     = { OptPtrLoad4,     "OptPtrLoad4",     0, 0, 0, 0, 0 };
1308 static OptFunc DOptPtrLoad5     = { OptPtrLoad5,     "OptPtrLoad5",     0, 0, 0, 0, 0 };
1309 static OptFunc DOptPtrLoad6     = { OptPtrLoad6,     "OptPtrLoad6",     0, 0, 0, 0, 0 };
1310 static OptFunc DOptNegA1        = { OptNegA1,        "OptNegA1",        0, 0, 0, 0, 0 };
1311 static OptFunc DOptNegA2        = { OptNegA2,        "OptNegA2",        0, 0, 0, 0, 0 };
1312 static OptFunc DOptNegAX1       = { OptNegAX1,       "OptNegAX1",       0, 0, 0, 0, 0 };
1313 static OptFunc DOptNegAX2       = { OptNegAX2,       "OptNegAX2",       0, 0, 0, 0, 0 };
1314 static OptFunc DOptNegAX3       = { OptNegAX3,       "OptNegAX3",       0, 0, 0, 0, 0 };
1315 static OptFunc DOptNegAX4       = { OptNegAX4,       "OptNegAX4",       0, 0, 0, 0, 0 };
1316 static OptFunc DOptSub1         = { OptSub1,         "OptSub1",         0, 0, 0, 0, 0 };
1317 static OptFunc DOptSub2         = { OptSub2,         "OptSub2",         0, 0, 0, 0, 0 };
1318 static OptFunc DOptAdd1         = { OptAdd1,         "OptAdd1",         0, 0, 0, 0, 0 };
1319 static OptFunc DOptAdd2         = { OptAdd2,         "OptAdd2",         0, 0, 0, 0, 0 };
1320 static OptFunc DOptAdd3         = { OptAdd3,         "OptAdd3",         0, 0, 0, 0, 0 };
1321 static OptFunc DOptShift1       = { OptShift1,       "OptShift1",       0, 0, 0, 0, 0 };
1322 static OptFunc DOptJumpCascades = { OptJumpCascades, "OptJumpCascades", 0, 0, 0, 0, 0 };
1323 static OptFunc DOptDeadJumps    = { OptDeadJumps,    "OptDeadJumps",    0, 0, 0, 0, 0 };
1324 static OptFunc DOptRTS          = { OptRTS,          "OptRTS",          0, 0, 0, 0, 0 };
1325 static OptFunc DOptDeadCode     = { OptDeadCode,     "OptDeadCode",     0, 0, 0, 0, 0 };
1326 static OptFunc DOptJumpTarget   = { OptJumpTarget,   "OptJumpTarget",   0, 0, 0, 0, 0 };
1327 static OptFunc DOptCondBranches = { OptCondBranches, "OptCondBranches", 0, 0, 0, 0, 0 };
1328 static OptFunc DOptRTSJumps     = { OptRTSJumps,     "OptRTSJumps",     0, 0, 0, 0, 0 };
1329 static OptFunc DOptBoolTrans    = { OptBoolTrans,    "OptBoolTrans",    0, 0, 0, 0, 0 };
1330 static OptFunc DOptCmp1         = { OptCmp1,         "OptCmp1",         0, 0, 0, 0, 0 };
1331 static OptFunc DOptCmp2         = { OptCmp2,         "OptCmp2",         0, 0, 0, 0, 0 };
1332 static OptFunc DOptCmp3         = { OptCmp3,         "OptCmp3",         0, 0, 0, 0, 0 };
1333 static OptFunc DOptCmp4         = { OptCmp4,         "OptCmp4",         0, 0, 0, 0, 0 };
1334 static OptFunc DOptCmp5         = { OptCmp5,         "OptCmp5",         0, 0, 0, 0, 0 };
1335 static OptFunc DOptCmp6         = { OptCmp6,         "OptCmp6",         0, 0, 0, 0, 0 };
1336 static OptFunc DOptCmp7         = { OptCmp7,         "OptCmp7",         0, 0, 0, 0, 0 };
1337 static OptFunc DOptTest1        = { OptTest1,        "OptTest1",        0, 0, 0, 0, 0 };
1338 static OptFunc DOptUnusedLoads  = { OptUnusedLoads,  "OptUnusedLoads",  0, 0, 0, 0, 0 };
1339 static OptFunc DOptUnusedStores = { OptUnusedStores, "OptUnusedStores", 0, 0, 0, 0, 0 };
1340 static OptFunc DOptDupLoads     = { OptDupLoads,     "OptDupLoads",     0, 0, 0, 0, 0 };
1341 static OptFunc DOptStoreLoad    = { OptStoreLoad,    "OptStoreLoad",    0, 0, 0, 0, 0 };
1342 static OptFunc DOptTransfers    = { OptTransfers,    "OptTransfers",    0, 0, 0, 0, 0 };
1343 static OptFunc DOptStackOps     = { OptStackOps,     "OptStackOps",     0, 0, 0, 0, 0 };
1344 static OptFunc DOptBranchDist   = { OptBranchDist,   "OptBranchDist",   0, 0, 0, 0, 0 };
1345
1346 /* Table containing all the steps in alphabetical order */
1347 static OptFunc* OptFuncs[] = {
1348     &DOptAdd1,
1349     &DOptAdd2,
1350     &DOptAdd3,
1351     &DOptBoolTrans,
1352     &DOptBranchDist,
1353     &DOptCmp1,
1354     &DOptCmp2,
1355     &DOptCmp3,
1356     &DOptCmp4,
1357     &DOptCmp5,
1358     &DOptCmp6,
1359     &DOptCmp7,
1360     &DOptCondBranches,
1361     &DOptDeadCode,
1362     &DOptDeadJumps,
1363     &DOptDupLoads,
1364     &DOptJumpCascades,
1365     &DOptJumpTarget,
1366     &DOptNegA1,
1367     &DOptNegA2,
1368     &DOptNegAX1,
1369     &DOptNegAX2,
1370     &DOptNegAX3,
1371     &DOptNegAX4,
1372     &DOptPtrStore1,
1373     &DOptPtrStore2,
1374     &DOptPtrLoad1,
1375     &DOptPtrLoad2,
1376     &DOptPtrLoad3,
1377     &DOptPtrLoad4,
1378     &DOptPtrLoad5,
1379     &DOptPtrLoad6,
1380     &DOptRTS,
1381     &DOptRTSJumps,
1382     &DOptSub1,
1383     &DOptSub2,
1384     &DOptShift1,
1385     &DOptStackOps,
1386     &DOptStoreLoad,
1387     &DOptTest1,
1388     &DOptTransfers,
1389     &DOptUnusedLoads,
1390     &DOptUnusedStores,
1391 };
1392 #define OPTFUNC_COUNT  (sizeof(OptFuncs) / sizeof(OptFuncs[0]))
1393
1394
1395
1396 static int CmpOptStep (const void* Key, const void* Func)
1397 /* Compare function for bsearch */
1398 {
1399     return strcmp (Key, (*(const OptFunc**)Func)->Name);
1400 }
1401
1402
1403
1404 static OptFunc* FindOptFunc (const char* Name)
1405 /* Find an optimizer step by name in the table and return a pointer. Return
1406  * NULL if no such step is found.
1407  */
1408 {
1409     /* Search for the function in the list */
1410     OptFunc** O = bsearch (Name, OptFuncs, OPTFUNC_COUNT, sizeof (OptFuncs[0]), CmpOptStep);
1411     return O? *O : 0;
1412 }
1413
1414
1415
1416 static OptFunc* GetOptFunc (const char* Name)
1417 /* Find an optimizer step by name in the table and return a pointer. Print an
1418  * error and call AbEnd if not found.
1419  */
1420 {
1421     /* Search for the function in the list */
1422     OptFunc* F = FindOptFunc (Name);
1423     if (F == 0) {
1424         /* Not found */
1425         AbEnd ("Optimization step `%s' not found", Name);
1426     }
1427     return F;
1428 }
1429
1430
1431
1432 void DisableOpt (const char* Name)
1433 /* Disable the optimization with the given name */
1434 {
1435     if (strcmp (Name, "any") == 0) {
1436         unsigned I;
1437         for (I = 0; I < OPTFUNC_COUNT; ++I) {
1438             OptFuncs[I]->Disabled = 1;
1439         }
1440     } else {
1441         GetOptFunc(Name)->Disabled = 1;
1442     }
1443 }
1444
1445
1446
1447 void EnableOpt (const char* Name)
1448 /* Enable the optimization with the given name */
1449 {
1450     if (strcmp (Name, "any") == 0) {
1451         unsigned I;
1452         for (I = 0; I < OPTFUNC_COUNT; ++I) {
1453             OptFuncs[I]->Disabled = 0;
1454         }
1455     } else {
1456         GetOptFunc(Name)->Disabled = 0;
1457     }
1458 }
1459
1460
1461
1462 void ListOptSteps (FILE* F)
1463 /* List all optimization steps */
1464 {
1465     unsigned I;
1466     for (I = 0; I < OPTFUNC_COUNT; ++I) {
1467         fprintf (F, "%s\n", OptFuncs[I]->Name);
1468     }
1469 }
1470
1471
1472
1473 static void ReadOptStats (const char* Name)
1474 /* Read the optimizer statistics file */
1475 {
1476     char Buf [256];
1477     unsigned Lines;
1478
1479     /* Try to open the file */
1480     FILE* F = fopen (Name, "r");
1481     if (F == 0) {
1482         /* Ignore the error */
1483         return;
1484     }
1485
1486     /* Read and parse the lines */
1487     Lines = 0;
1488     while (fgets (Buf, sizeof (Buf), F) != 0) {
1489
1490         char* B;
1491         unsigned Len;
1492         OptFunc* Func;
1493
1494         /* Fields */
1495         char Name[32];
1496         unsigned long  TotalRuns;
1497         unsigned long  TotalChanges;
1498
1499         /* Count lines */
1500         ++Lines;
1501
1502         /* Remove trailing white space including the line terminator */
1503         B = Buf;
1504         Len = strlen (B);
1505         while (Len > 0 && IsSpace (B[Len-1])) {
1506             --Len;
1507         }
1508         B[Len] = '\0';
1509
1510         /* Remove leading whitespace */
1511         while (IsSpace (*B)) {
1512             ++B;
1513         }
1514
1515         /* Check for empty and comment lines */
1516         if (*B == '\0' || *B == ';' || *B == '#') {
1517             continue;
1518         }
1519
1520         /* Parse the line */
1521         if (sscanf (B, "%31s %lu %*u %lu %*u", Name, &TotalRuns, &TotalChanges) != 3) {
1522             /* Syntax error */
1523             continue;
1524         }
1525
1526         /* Search for the optimizer step. */
1527         Func = FindOptFunc (Name);
1528         if (Func == 0) {
1529             /* Not found */
1530             continue;
1531         }
1532
1533         /* Found the step, set the fields */
1534         Func->TotalRuns    = TotalRuns;
1535         Func->TotalChanges = TotalChanges;
1536
1537     }
1538
1539     /* Close the file, ignore errors here. */
1540     fclose (F);
1541 }
1542
1543
1544
1545 static void WriteOptStats (const char* Name)
1546 /* Write the optimizer statistics file */
1547 {
1548     unsigned I;
1549
1550     /* Try to open the file */
1551     FILE* F = fopen (Name, "w");
1552     if (F == 0) {
1553         /* Ignore the error */
1554         return;
1555     }
1556
1557     /* Write the file */
1558     for (I = 0; I < OPTFUNC_COUNT; ++I) {
1559         const OptFunc* O = OptFuncs[I];
1560         fprintf (F,
1561                  "%s %lu %lu %lu %lu\n",
1562                  O->Name,
1563                  O->TotalRuns,
1564                  O->LastRuns,
1565                  O->TotalChanges,
1566                  O->LastChanges);
1567     }
1568
1569     /* Close the file, ignore errors here. */
1570     fclose (F);
1571 }
1572
1573
1574
1575 static unsigned RunOptFunc (CodeSeg* S, OptFunc* F, unsigned Max)
1576 /* Run one optimizer function Max times or until there are no more changes */
1577 {
1578     unsigned Changes, C;
1579
1580     /* Don't run the function if it is disabled */
1581     if (F->Disabled) {
1582         return 0;
1583     }
1584
1585     /* Run this until there are no more changes */
1586     Changes = 0;
1587     do {
1588
1589         /* Run the function */
1590         C = F->Func (S);
1591         Changes += C;
1592
1593         /* Do statistics */
1594         ++F->TotalRuns;
1595         ++F->LastRuns;
1596         F->TotalChanges += C;
1597         F->LastChanges  += C;
1598
1599     } while (--Max && C > 0);
1600
1601     /* Return the number of changes */
1602     return Changes;
1603 }
1604
1605
1606
1607 static void RunOptGroup1 (CodeSeg* S)
1608 /* Run the first group of optimization steps. These steps translate known
1609  * patterns emitted by the code generator into more optimal patterns. Order
1610  * of the steps is important, because some of the steps done earlier cover
1611  * the same patterns as later steps as subpatterns.
1612  */
1613 {
1614     RunOptFunc (S, &DOptPtrStore1, 1);
1615     RunOptFunc (S, &DOptPtrStore2, 1);
1616     RunOptFunc (S, &DOptPtrLoad1, 1);
1617     RunOptFunc (S, &DOptPtrLoad2, 1);
1618     RunOptFunc (S, &DOptPtrLoad3, 1);
1619     RunOptFunc (S, &DOptPtrLoad4, 1);
1620     RunOptFunc (S, &DOptPtrLoad5, 1);
1621     RunOptFunc (S, &DOptNegAX1, 1);
1622     RunOptFunc (S, &DOptNegAX2, 1);
1623     RunOptFunc (S, &DOptNegAX3, 1);
1624     RunOptFunc (S, &DOptNegAX4, 1);
1625     RunOptFunc (S, &DOptAdd1, 1);
1626     RunOptFunc (S, &DOptAdd2, 1);
1627     RunOptFunc (S, &DOptShift1, 1);
1628 }
1629
1630
1631
1632 static void RunOptGroup2 (CodeSeg* S)
1633 /* Run one group of optimization steps. These steps depend on each other,
1634  * that means that one step may allow another step to do additional work,
1635  * so we will repeat the steps as long as we see any changes.
1636  */
1637 {
1638     unsigned Changes;
1639
1640     do {
1641         Changes = 0;
1642
1643         Changes += RunOptFunc (S, &DOptPtrLoad6, 1);
1644         Changes += RunOptFunc (S, &DOptNegA1, 1);
1645         Changes += RunOptFunc (S, &DOptNegA2, 1);
1646         Changes += RunOptFunc (S, &DOptSub1, 1);
1647         Changes += RunOptFunc (S, &DOptSub2, 1);
1648         Changes += RunOptFunc (S, &DOptAdd3, 1);
1649         Changes += RunOptFunc (S, &DOptJumpCascades, 1);
1650         Changes += RunOptFunc (S, &DOptDeadJumps, 1);
1651         Changes += RunOptFunc (S, &DOptRTS, 1);
1652         Changes += RunOptFunc (S, &DOptDeadCode, 1);
1653         Changes += RunOptFunc (S, &DOptJumpTarget, 1);
1654         Changes += RunOptFunc (S, &DOptCondBranches, 1);
1655         Changes += RunOptFunc (S, &DOptRTSJumps, 1);
1656         Changes += RunOptFunc (S, &DOptBoolTrans, 1);
1657         Changes += RunOptFunc (S, &DOptCmp1, 1);
1658         Changes += RunOptFunc (S, &DOptCmp2, 1);
1659         Changes += RunOptFunc (S, &DOptCmp3, 1);
1660         Changes += RunOptFunc (S, &DOptCmp4, 1);
1661         Changes += RunOptFunc (S, &DOptCmp5, 1);
1662         Changes += RunOptFunc (S, &DOptCmp6, 1);
1663         Changes += RunOptFunc (S, &DOptCmp7, 1);
1664         Changes += RunOptFunc (S, &DOptTest1, 1);
1665         Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
1666         Changes += RunOptFunc (S, &DOptUnusedStores, 1);
1667         Changes += RunOptFunc (S, &DOptDupLoads, 1);
1668         Changes += RunOptFunc (S, &DOptStoreLoad, 1);
1669         Changes += RunOptFunc (S, &DOptTransfers, 1);
1670         Changes += RunOptFunc (S, &DOptStackOps, 1);
1671
1672     } while (Changes);
1673 }
1674
1675
1676
1677 static void RunOptGroup3 (CodeSeg* S)
1678 /* The last group of optimization steps. Adjust branches.
1679  */
1680 {
1681     RunOptFunc (S, &DOptBranchDist, 3);
1682 }
1683
1684
1685
1686 void RunOpt (CodeSeg* S)
1687 /* Run the optimizer */
1688 {
1689     const char* StatFileName;
1690
1691     /* If we shouldn't run the optimizer, bail out */
1692     if (!Optimize) {
1693         return;
1694     }
1695
1696     /* Check if we are requested to write optimizer statistics */
1697     StatFileName = getenv ("CC65_OPTSTATS");
1698     if (StatFileName) {
1699         ReadOptStats (StatFileName);
1700     }
1701
1702     /* Print the name of the function we are working on */
1703     if (S->Func) {
1704         Print (stdout, 1, "Running optimizer for function `%s'\n", S->Func->Name);
1705     } else {
1706         Print (stdout, 1, "Running optimizer for global code segment\n");
1707     }
1708
1709     /* Run groups of optimizations */
1710     RunOptGroup1 (S);
1711     RunOptGroup2 (S);
1712     RunOptGroup3 (S);
1713
1714     /* Write statistics */
1715     if (StatFileName) {
1716         WriteOptStats (StatFileName);
1717     }
1718 }
1719
1720
1721