]> git.sur5r.net Git - cc65/blob - src/cc65/codeopt.c
0656aebc891a353499f94177623768d4eea0ea73
[cc65] / src / cc65 / codeopt.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 codeopt.c                                 */
4 /*                                                                           */
5 /*                           Optimizer subroutines                           */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2001-2010, Ullrich von Bassewitz                                      */
10 /*                Roemerstrasse 52                                           */
11 /*                D-70794 Filderstadt                                        */
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 "cpu.h"
43 #include "print.h"
44 #include "xmalloc.h"
45 #include "xsprintf.h"
46
47 /* cc65 */
48 #include "asmlabel.h"
49 #include "codeent.h"
50 #include "codeinfo.h"
51 #include "coptadd.h"
52 #include "coptc02.h"
53 #include "coptcmp.h"
54 #include "coptind.h"
55 #include "coptneg.h"
56 #include "coptptrload.h"
57 #include "coptpush.h"
58 #include "coptsize.h"
59 #include "coptstop.h"
60 #include "coptstore.h"
61 #include "coptsub.h"
62 #include "copttest.h"
63 #include "error.h"
64 #include "global.h"
65 #include "codeopt.h"
66
67
68
69 /*****************************************************************************/
70 /*                                     Data                                  */
71 /*****************************************************************************/
72
73
74
75 /* Shift types */
76 enum {
77     SHIFT_NONE,
78     SHIFT_ASR_1,
79     SHIFT_ASL_1,
80     SHIFT_LSR_1,
81     SHIFT_LSL_1
82 };
83
84
85
86 /*****************************************************************************/
87 /*                              Optimize shifts                              */
88 /*****************************************************************************/
89
90
91
92 static unsigned OptShift1 (CodeSeg* S)
93 /* A call to the shlaxN routine may get replaced by one or more asl insns
94  * if the value of X is not used later. If X is used later, but it is zero
95  * on entry and it's a shift by one, it may get replaced by:
96  *
97  *      asl     a
98  *      bcc     L1
99  *      inx
100  *  L1:
101  *
102  */
103 {
104     unsigned Changes = 0;
105     unsigned I;
106
107     /* Generate register info */
108     CS_GenRegInfo (S);
109
110     /* Walk over the entries */
111     I = 0;
112     while (I < CS_GetEntryCount (S)) {
113
114         CodeEntry* N;
115         CodeEntry* X;
116         CodeLabel* L;
117
118         /* Get next entry */
119         CodeEntry* E = CS_GetEntry (S, I);
120
121         /* Check for the sequence */
122         if (E->OPC == OP65_JSR                       &&
123             (strncmp (E->Arg, "shlax", 5) == 0 ||
124              strncmp (E->Arg, "aslax", 5) == 0)      &&
125             strlen (E->Arg) == 6                     &&
126             IsDigit (E->Arg[5])) {
127
128             if (!RegXUsed (S, I+1)) {
129
130                 /* Insert shift insns */
131                 unsigned Count = E->Arg[5] - '0';
132                 while (Count--) {
133                     X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI);
134                     CS_InsertEntry (S, X, I+1);
135                 }
136
137                 /* Delete the call to shlax */
138                 CS_DelEntry (S, I);
139
140                 /* Remember, we had changes */
141                 ++Changes;
142
143             } else if (E->RI->In.RegX == 0              &&
144                        E->Arg[5] == '1'                 &&
145                        (N = CS_GetNextEntry (S, I)) != 0) {
146
147                 /* asl a */
148                 X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI);
149                 CS_InsertEntry (S, X, I+1);
150
151                 /* bcc L1 */
152                 L = CS_GenLabel (S, N);
153                 X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, E->LI);
154                 CS_InsertEntry (S, X, I+2);
155
156                 /* inx */
157                 X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, E->LI);
158                 CS_InsertEntry (S, X, I+3);
159
160                 /* Delete the call to shlax */
161                 CS_DelEntry (S, I);
162
163                 /* Remember, we had changes */
164                 ++Changes;
165             }
166
167         }
168
169         /* Next entry */
170         ++I;
171
172     }
173
174     /* Free the register info */
175     CS_FreeRegInfo (S);
176
177     /* Return the number of changes made */
178     return Changes;
179 }
180
181
182
183 static unsigned OptShift2(CodeSeg* S)
184 /* A call to the asrax1 routines may get replaced by something simpler, if
185  * X is not used later:
186  *
187  *      cmp     #$80
188  *      ror     a
189  *
190  */
191 {
192     unsigned Changes = 0;
193     unsigned I;
194
195     /* Generate register info */
196     CS_GenRegInfo (S);
197
198     /* Walk over the entries */
199     I = 0;
200     while (I < CS_GetEntryCount (S)) {
201
202         unsigned Count;
203
204         /* Get next entry */
205         CodeEntry* E = CS_GetEntry (S, I);
206
207         /* Check for the sequence */
208         if (E->OPC == OP65_JSR                  &&
209             strncmp (E->Arg, "asrax", 5) == 0   &&
210             strlen (E->Arg) == 6                &&
211             IsDigit (E->Arg[5])                 &&
212             (Count = (E->Arg[5] - '0')) >= 1    &&
213             Count * 100 <= S->CodeSizeFactor    &&
214             !RegXUsed (S, I+1)) {
215
216             CodeEntry* X;
217             unsigned J = I+1;
218
219             /* Generate the replacement sequence */
220             while (Count--) {
221                 /* cmp #$80 */
222                 X = NewCodeEntry (OP65_CMP, AM65_IMM, "$80", 0, E->LI);
223                 CS_InsertEntry (S, X, J++);
224
225                 /* ror a */
226                 X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, E->LI);
227                 CS_InsertEntry (S, X, J++);
228             }
229
230             /* Delete the call to asrax */
231             CS_DelEntry (S, I);
232
233             /* Remember, we had changes */
234             ++Changes;
235         }
236
237         /* Next entry */
238         ++I;
239
240     }
241
242     /* Free the register info */
243     CS_FreeRegInfo (S);
244
245     /* Return the number of changes made */
246     return Changes;
247 }
248
249
250
251 static unsigned OptShift3 (CodeSeg* S)
252 /* A call to the shraxN routine may get replaced by one or more lsr insns
253  * if the value of X is zero.
254  */
255 {
256     unsigned Changes = 0;
257     unsigned I;
258
259     /* Generate register info */
260     CS_GenRegInfo (S);
261
262     /* Walk over the entries */
263     I = 0;
264     while (I < CS_GetEntryCount (S)) {
265
266         /* Get next entry */
267         CodeEntry* E = CS_GetEntry (S, I);
268
269         /* Check for the sequence */
270         if (E->OPC == OP65_JSR                       &&
271             strncmp (E->Arg, "shrax", 5) == 0        &&
272             strlen (E->Arg) == 6                     &&
273             IsDigit (E->Arg[5])                      &&
274             E->RI->In.RegX == 0) {
275
276             /* Insert shift insns */
277             unsigned Count = E->Arg[5] - '0';
278             while (Count--) {
279                 CodeEntry* X = NewCodeEntry (OP65_LSR, AM65_ACC, "a", 0, E->LI);
280                 CS_InsertEntry (S, X, I+1);
281             }
282
283             /* Delete the call to shrax */
284             CS_DelEntry (S, I);
285
286             /* Remember, we had changes */
287             ++Changes;
288
289         }
290
291         /* Next entry */
292         ++I;
293
294     }
295
296     /* Free the register info */
297     CS_FreeRegInfo (S);
298
299     /* Return the number of changes made */
300     return Changes;
301 }
302
303
304
305 static unsigned GetShiftType (const char* Sub)
306 /* Helper function for OptShift3 */
307 {
308     if (*Sub == 'a') {
309         if (strcmp (Sub+1, "slax1") == 0) {
310             return SHIFT_ASL_1;
311         } else if (strcmp (Sub+1, "srax1") == 0) {
312             return SHIFT_ASR_1;
313         }
314     } else if (*Sub == 's') {
315         if (strcmp (Sub+1, "hlax1") == 0) {
316             return SHIFT_LSL_1;
317         } else if (strcmp (Sub+1, "hrax1") == 0) {
318             return SHIFT_LSR_1;
319         }
320     }
321     return SHIFT_NONE;
322 }
323
324
325
326 static unsigned OptShift4 (CodeSeg* S)
327 /* Search for the sequence
328  *
329  *      lda     xxx
330  *      ldx     yyy
331  *      jsr     aslax1/asrax1/shlax1/shrax1
332  *      sta     aaa
333  *      stx     bbb
334  *
335  * and replace it by
336  *
337  *      lda     xxx
338  *      asl     a
339  *      sta     aaa
340  *      lda     yyy
341  *      rol     a
342  *      sta     bbb
343  *
344  * or similar, provided that a/x is not used later
345  */
346 {
347     unsigned Changes = 0;
348
349     /* Walk over the entries */
350     unsigned I = 0;
351     while (I < CS_GetEntryCount (S)) {
352
353         unsigned ShiftType;
354         CodeEntry* L[5];
355
356         /* Get next entry */
357         L[0] = CS_GetEntry (S, I);
358
359         /* Check for the sequence */
360         if (L[0]->OPC == OP65_LDA                               &&
361             (L[0]->AM == AM65_ABS || L[0]->AM == AM65_ZP)       &&
362             CS_GetEntries (S, L+1, I+1, 4)                      &&
363             !CS_RangeHasLabel (S, I+1, 4)                       &&
364             L[1]->OPC == OP65_LDX                               &&
365             (L[1]->AM == AM65_ABS || L[1]->AM == AM65_ZP)       &&
366             L[2]->OPC == OP65_JSR                               &&
367             (ShiftType = GetShiftType (L[2]->Arg)) != SHIFT_NONE&&
368             L[3]->OPC == OP65_STA                               &&
369             (L[3]->AM == AM65_ABS || L[3]->AM == AM65_ZP)       &&
370             L[4]->OPC == OP65_STX                               &&
371             (L[4]->AM == AM65_ABS || L[4]->AM == AM65_ZP)       &&
372             !RegAXUsed (S, I+5)) {
373
374             CodeEntry* X;
375
376             /* Handle the four shift types differently */
377             switch (ShiftType) {
378
379                 case SHIFT_ASR_1:
380                     X = NewCodeEntry (OP65_LDA, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
381                     CS_InsertEntry (S, X, I+5);
382                     X = NewCodeEntry (OP65_CMP, AM65_IMM, "$80", 0, L[2]->LI);
383                     CS_InsertEntry (S, X, I+6);
384                     X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, L[2]->LI);
385                     CS_InsertEntry (S, X, I+7);
386                     X = NewCodeEntry (OP65_STA, L[4]->AM, L[4]->Arg, 0, L[4]->LI);
387                     CS_InsertEntry (S, X, I+8);
388                     X = NewCodeEntry (OP65_LDA, L[0]->AM, L[0]->Arg, 0, L[0]->LI);
389                     CS_InsertEntry (S, X, I+9);
390                     X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, L[2]->LI);
391                     CS_InsertEntry (S, X, I+10);
392                     X = NewCodeEntry (OP65_STA, L[3]->AM, L[3]->Arg, 0, L[3]->LI);
393                     CS_InsertEntry (S, X, I+11);
394                     CS_DelEntries (S, I, 5);
395                     break;
396
397                 case SHIFT_LSR_1:
398                     X = NewCodeEntry (OP65_LDA, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
399                     CS_InsertEntry (S, X, I+5);
400                     X = NewCodeEntry (OP65_LSR, AM65_ACC, "a", 0, L[2]->LI);
401                     CS_InsertEntry (S, X, I+6);
402                     X = NewCodeEntry (OP65_STA, L[4]->AM, L[4]->Arg, 0, L[4]->LI);
403                     CS_InsertEntry (S, X, I+7);
404                     X = NewCodeEntry (OP65_LDA, L[0]->AM, L[0]->Arg, 0, L[0]->LI);
405                     CS_InsertEntry (S, X, I+8);
406                     X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, L[2]->LI);
407                     CS_InsertEntry (S, X, I+9);
408                     X = NewCodeEntry (OP65_STA, L[3]->AM, L[3]->Arg, 0, L[3]->LI);
409                     CS_InsertEntry (S, X, I+10);
410                     CS_DelEntries (S, I, 5);
411                     break;
412
413                 case SHIFT_LSL_1:
414                 case SHIFT_ASL_1:
415                     /* These two are identical */
416                     X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, L[2]->LI);
417                     CS_InsertEntry (S, X, I+1);
418                     X = NewCodeEntry (OP65_STA, L[3]->AM, L[3]->Arg, 0, L[3]->LI);
419                     CS_InsertEntry (S, X, I+2);
420                     X = NewCodeEntry (OP65_LDA, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
421                     CS_InsertEntry (S, X, I+3);
422                     X = NewCodeEntry (OP65_ROL, AM65_ACC, "a", 0, L[2]->LI);
423                     CS_InsertEntry (S, X, I+4);
424                     X = NewCodeEntry (OP65_STA, L[4]->AM, L[4]->Arg, 0, L[4]->LI);
425                     CS_InsertEntry (S, X, I+5);
426                     CS_DelEntries (S, I+6, 4);
427                     break;
428
429             }
430
431             /* Remember, we had changes */
432             ++Changes;
433
434         }
435
436         /* Next entry */
437         ++I;
438
439     }
440
441     /* Return the number of changes made */
442     return Changes;
443 }
444
445
446
447 static unsigned OptShift5 (CodeSeg* S)
448 /* Inline the shift subroutines. */
449 {
450     unsigned Changes = 0;
451
452     /* Walk over the entries */
453     unsigned I = 0;
454     while (I < CS_GetEntryCount (S)) {
455
456         CodeEntry* X;
457         unsigned   IP;
458
459         /* Get next entry */
460         CodeEntry* E = CS_GetEntry (S, I);
461
462         /* Check for a call to one of the shift routine */
463         if (E->OPC == OP65_JSR                          &&
464             (strncmp (E->Arg, "shlax", 5) == 0  ||
465              strncmp (E->Arg, "aslax", 5) == 0)         &&
466             strlen (E->Arg) == 6                        &&
467             IsDigit (E->Arg[5])) {
468
469             /* Get number of shifts */
470             unsigned ShiftCount = (E->Arg[5] - '0');
471
472             /* Code is:
473              *
474              *      stx     tmp1
475              *      asl     a
476              *      rol     tmp1
477              *      (repeat ShiftCount-1 times)
478              *      ldx     tmp1
479              *
480              * which makes 4 + 3 * ShiftCount bytes, compared to the original
481              * 3 bytes for the subroutine call. However, in most cases, the
482              * final load of the X register gets merged with some other insn
483              * and replaces a txa, so for a shift count of 1, we get a factor
484              * of 200, which matches nicely the CodeSizeFactor enabled with -Oi
485              */
486             if (ShiftCount > 1 || S->CodeSizeFactor > 200) {
487                 unsigned Size = 4 + 3 * ShiftCount;
488                 if ((Size * 100 / 3) > S->CodeSizeFactor) {
489                     /* Not acceptable */
490                     goto NextEntry;
491                 }
492             }
493
494             /* Inline the code. Insertion point is behind the subroutine call */
495             IP = (I + 1);
496
497             /* stx tmp1 */
498             X = NewCodeEntry (OP65_STX, AM65_ZP, "tmp1", 0, E->LI);
499             CS_InsertEntry (S, X, IP++);
500
501             while (ShiftCount--) {
502                 /* asl a */
503                 X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI);
504                 CS_InsertEntry (S, X, IP++);
505
506                 /* rol tmp1 */
507                 X = NewCodeEntry (OP65_ROL, AM65_ZP, "tmp1", 0, E->LI);
508                 CS_InsertEntry (S, X, IP++);
509             }
510
511             /* ldx tmp1 */
512             X = NewCodeEntry (OP65_LDX, AM65_ZP, "tmp1", 0, E->LI);
513             CS_InsertEntry (S, X, IP++);
514
515             /* Remove the subroutine call */
516             CS_DelEntry (S, I);
517
518             /* Remember, we had changes */
519             ++Changes;
520         }
521
522 NextEntry:
523         /* Next entry */
524         ++I;
525
526     }
527
528     /* Return the number of changes made */
529     return Changes;
530 }
531
532
533
534 /*****************************************************************************/
535 /*                              Optimize loads                               */
536 /*****************************************************************************/
537
538
539
540 static unsigned OptLoad1 (CodeSeg* S)
541 /* Search for a call to ldaxysp where X is not used later and replace it by
542  * a load of just the A register.
543  */
544 {
545     unsigned I;
546     unsigned Changes = 0;
547
548     /* Generate register info */
549     CS_GenRegInfo (S);
550
551     /* Walk over the entries */
552     I = 0;
553     while (I < CS_GetEntryCount (S)) {
554
555         CodeEntry* E;
556
557         /* Get next entry */
558         E = CS_GetEntry (S, I);
559
560         /* Check for the sequence */
561         if (CE_IsCallTo (E, "ldaxysp")          &&
562             RegValIsKnown (E->RI->In.RegY)      &&
563             !RegXUsed (S, I+1)) {
564
565             CodeEntry* X;
566
567             /* Reload the Y register */
568             const char* Arg = MakeHexArg (E->RI->In.RegY - 1);
569             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
570             CS_InsertEntry (S, X, I+1);
571
572             /* Load from stack */
573             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, E->LI);
574             CS_InsertEntry (S, X, I+2);
575
576             /* Now remove the call to the subroutine */
577             CS_DelEntry (S, I);
578
579             /* Remember, we had changes */
580             ++Changes;
581
582         }
583
584         /* Next entry */
585         ++I;
586
587     }
588
589     /* Free the register info */
590     CS_FreeRegInfo (S);
591
592     /* Return the number of changes made */
593     return Changes;
594 }
595
596
597
598 static unsigned OptLoad2 (CodeSeg* S)
599 /* Replace calls to ldaxysp by inline code */
600 {
601     unsigned I;
602     unsigned Changes = 0;
603
604     /* Walk over the entries */
605     I = 0;
606     while (I < CS_GetEntryCount (S)) {
607
608         CodeEntry* E;
609
610         /* Get next entry */
611         E = CS_GetEntry (S, I);
612
613         /* Check for the sequence */
614         if (CE_IsCallTo (E, "ldaxysp")) {
615
616             CodeEntry* X;
617
618             /* lda (sp),y */
619             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, E->LI);
620             CS_InsertEntry (S, X, I+1);
621
622             /* tax */
623             X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI);
624             CS_InsertEntry (S, X, I+2);
625
626             /* dey */
627             X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, E->LI);
628             CS_InsertEntry (S, X, I+3);
629
630             /* lda (sp),y */
631             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, E->LI);
632             CS_InsertEntry (S, X, I+4);
633
634             /* Now remove the call to the subroutine */
635             CS_DelEntry (S, I);
636
637             /* Remember, we had changes */
638             ++Changes;
639
640         }
641
642         /* Next entry */
643         ++I;
644     }
645
646     /* Return the number of changes made */
647     return Changes;
648 }
649
650
651
652 /*****************************************************************************/
653 /*                     Optimize stores through pointers                      */
654 /*****************************************************************************/
655
656
657
658 static unsigned OptPtrStore1Sub (CodeSeg* S, unsigned I, CodeEntry** const L)
659 /* Check if this is one of the allowed suboperation for OptPtrStore1 */
660 {
661     /* Check for a label attached to the entry */
662     if (CE_HasLabel (L[0])) {
663         return 0;
664     }
665
666     /* Check for single insn sub ops */
667     if (L[0]->OPC == OP65_AND                                           ||
668         L[0]->OPC == OP65_EOR                                           ||
669         L[0]->OPC == OP65_ORA                                           ||
670         (L[0]->OPC == OP65_JSR && strncmp (L[0]->Arg, "shlax", 5) == 0) ||
671         (L[0]->OPC == OP65_JSR && strncmp (L[0]->Arg, "shrax", 5) == 0)) {
672
673         /* One insn */
674         return 1;
675
676     } else if (L[0]->OPC == OP65_CLC                      &&
677                (L[1] = CS_GetNextEntry (S, I)) != 0       &&
678                L[1]->OPC == OP65_ADC                      &&
679                !CE_HasLabel (L[1])) {
680         return 2;
681     } else if (L[0]->OPC == OP65_SEC                      &&
682                (L[1] = CS_GetNextEntry (S, I)) != 0       &&
683                L[1]->OPC == OP65_SBC                      &&
684                !CE_HasLabel (L[1])) {
685         return 2;
686     }
687
688
689
690     /* Not found */
691     return 0;
692 }
693
694
695
696 static unsigned OptPtrStore1 (CodeSeg* S)
697 /* Search for the sequence:
698  *
699  *      jsr     pushax
700  *      ldy     xxx
701  *      jsr     ldauidx
702  *      subop
703  *      ldy     yyy
704  *      jsr     staspidx
705  *
706  * and replace it by:
707  *
708  *      sta     ptr1
709  *      stx     ptr1+1
710  *      ldy     xxx
711  *      ldx     #$00
712  *      lda     (ptr1),y
713  *      subop
714  *      ldy     yyy
715  *      sta     (ptr1),y
716  *
717  * In case a/x is loaded from the register bank before the pushax, we can even
718  * use the register bank instead of ptr1.
719  */
720 /*
721  *      jsr     pushax
722  *      ldy     xxx
723  *      jsr     ldauidx
724  *      ldx     #$00
725  *      lda     (zp),y
726  *      subop
727  *      ldy     yyy
728  *      sta     (zp),y
729  *      jsr     staspidx
730  */
731 {
732     unsigned Changes = 0;
733
734     /* Walk over the entries */
735     unsigned I = 0;
736     while (I < CS_GetEntryCount (S)) {
737
738         unsigned K;
739         CodeEntry* L[10];
740
741         /* Get next entry */
742         L[0] = CS_GetEntry (S, I);
743
744         /* Check for the sequence */
745         if (CE_IsCallTo (L[0], "pushax")            &&
746             CS_GetEntries (S, L+1, I+1, 3)          &&
747             L[1]->OPC == OP65_LDY                   &&
748             CE_IsConstImm (L[1])                    &&
749             !CE_HasLabel (L[1])                     &&
750             CE_IsCallTo (L[2], "ldauidx")           &&
751             !CE_HasLabel (L[2])                     &&
752             (K = OptPtrStore1Sub (S, I+3, L+3)) > 0 &&
753             CS_GetEntries (S, L+3+K, I+3+K, 2)      &&
754             L[3+K]->OPC == OP65_LDY                 &&
755             CE_IsConstImm (L[3+K])                  &&
756             !CE_HasLabel (L[3+K])                   &&
757             CE_IsCallTo (L[4+K], "staspidx")        &&
758             !CE_HasLabel (L[4+K])) {
759
760
761             const char* RegBank = 0;
762             const char* ZPLoc   = "ptr1";
763             CodeEntry* X;
764
765
766             /* Get the preceeding two instructions and check them. We check
767              * for:
768              *          lda     regbank+n
769              *          ldx     regbank+n+1
770              */
771             if (I > 1) {
772                 CodeEntry* P[2];
773                 P[0] = CS_GetEntry (S, I-2);
774                 P[1] = CS_GetEntry (S, I-1);
775                 if (P[0]->OPC == OP65_LDA &&
776                     P[0]->AM  == AM65_ZP  &&
777                     P[1]->OPC == OP65_LDX &&
778                     P[1]->AM  == AM65_ZP  &&
779                     !CE_HasLabel (P[1])   &&
780                     strncmp (P[0]->Arg, "regbank+", 8) == 0) {
781
782                     unsigned Len = strlen (P[0]->Arg);
783
784                     if (strncmp (P[0]->Arg, P[1]->Arg, Len) == 0 &&
785                         P[1]->Arg[Len+0] == '+'                  &&
786                         P[1]->Arg[Len+1] == '1'                  &&
787                         P[1]->Arg[Len+2] == '\0') {
788
789                         /* Ok, found. Use the name of the register bank */
790                         RegBank = ZPLoc = P[0]->Arg;
791                     }
792                 }
793             }
794
795             /* Insert the load via the zp pointer */
796             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[3]->LI);
797             CS_InsertEntry (S, X, I+3);
798             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, ZPLoc, 0, L[2]->LI);
799             CS_InsertEntry (S, X, I+4);
800
801             /* Insert the store through the zp pointer */
802             X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLoc, 0, L[3]->LI);
803             CS_InsertEntry (S, X, I+6+K);
804
805             /* Delete the old code */
806             CS_DelEntry (S, I+7+K);     /* jsr spaspidx */
807             CS_DelEntry (S, I+2);       /* jsr ldauidx */
808
809             /* Create and insert the stores into the zp pointer if needed */
810             if (RegBank == 0) {
811                 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
812                 CS_InsertEntry (S, X, I+1);
813                 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
814                 CS_InsertEntry (S, X, I+2);
815             }
816
817             /* Delete more old code. Do it here to keep a label attached to
818              * entry I in place.
819              */
820             CS_DelEntry (S, I);         /* jsr pushax */
821
822             /* Remember, we had changes */
823             ++Changes;
824
825         }
826
827         /* Next entry */
828         ++I;
829
830     }
831
832     /* Return the number of changes made */
833     return Changes;
834 }
835
836
837
838 static unsigned OptPtrStore2 (CodeSeg* S)
839 /* Search for the sequence:
840  *
841  *      lda     #<(label+0)
842  *      ldx     #>(label+0)
843  *      clc
844  *      adc     xxx
845  *      bcc     L
846  *      inx
847  * L:   jsr     pushax
848  *      ldx     #$00
849  *      lda     yyy
850  *      ldy     #$00
851  *      jsr     staspidx
852  *
853  * and replace it by:
854  *
855  *      ldy     xxx
856  *      ldx     #$00
857  *      lda     yyy
858  *      sta     label,y
859  */
860 {
861     unsigned Changes = 0;
862
863     /* Walk over the entries */
864     unsigned I = 0;
865     while (I < CS_GetEntryCount (S)) {
866
867         CodeEntry* L[11];
868         unsigned Len;
869
870         /* Get next entry */
871         L[0] = CS_GetEntry (S, I);
872
873         /* Check for the sequence */
874         if (L[0]->OPC == OP65_LDA                            &&
875             L[0]->AM == AM65_IMM                             &&
876             CS_GetEntries (S, L+1, I+1, 10)                  &&
877             L[1]->OPC == OP65_LDX                            &&
878             L[1]->AM == AM65_IMM                             &&
879             L[2]->OPC == OP65_CLC                            &&
880             L[3]->OPC == OP65_ADC                            &&
881             (L[3]->AM == AM65_ABS || L[3]->AM == AM65_ZP)    &&
882             (L[4]->OPC == OP65_BCC || L[4]->OPC == OP65_JCC) &&
883             L[4]->JumpTo != 0                                &&
884             L[4]->JumpTo->Owner == L[6]                      &&
885             L[5]->OPC == OP65_INX                            &&
886             CE_IsCallTo (L[6], "pushax")                     &&
887             L[7]->OPC == OP65_LDX                            &&
888             L[8]->OPC == OP65_LDA                            &&
889             L[9]->OPC == OP65_LDY                            &&
890             CE_IsKnownImm (L[9], 0)                          &&
891             CE_IsCallTo (L[10], "staspidx")                  &&
892             !CS_RangeHasLabel (S, I+1, 5)                    &&
893             !CS_RangeHasLabel (S, I+7, 4)                    &&
894             /* Check the label last because this is quite costly */
895             (Len = strlen (L[0]->Arg)) > 3                   &&
896             L[0]->Arg[0] == '<'                              &&
897             L[0]->Arg[1] == '('                              &&
898             strlen (L[1]->Arg) == Len                        &&
899             L[1]->Arg[0] == '>'                              &&
900             memcmp (L[0]->Arg+1, L[1]->Arg+1, Len-1) == 0) {
901
902             CodeEntry* X;
903             char* Label;
904
905             /* We will create all the new stuff behind the current one so
906              * we keep the line references.
907              */
908             X = NewCodeEntry (OP65_LDY, L[3]->AM, L[3]->Arg, 0, L[0]->LI);
909             CS_InsertEntry (S, X, I+11);
910
911             X = NewCodeEntry (OP65_LDX, L[7]->AM, L[7]->Arg, 0, L[7]->LI);
912             CS_InsertEntry (S, X, I+12);
913
914             X = NewCodeEntry (OP65_LDA, L[8]->AM, L[8]->Arg, 0, L[8]->LI);
915             CS_InsertEntry (S, X, I+13);
916
917             Label = memcpy (xmalloc (Len-2), L[0]->Arg+2, Len-3);
918             Label[Len-3] = '\0';
919             X = NewCodeEntry (OP65_STA, AM65_ABSY, Label, 0, L[10]->LI);
920             CS_InsertEntry (S, X, I+14);
921             xfree (Label);
922
923             /* Remove the old code */
924             CS_DelEntries (S, I, 11);
925
926             /* Remember, we had changes */
927             ++Changes;
928
929         }
930
931         /* Next entry */
932         ++I;
933
934     }
935
936     /* Return the number of changes made */
937     return Changes;
938 }
939
940
941
942 static unsigned OptPtrStore3 (CodeSeg* S)
943 /* Search for the sequence:
944  *
945  *      lda     #<(label+0)
946  *      ldx     #>(label+0)
947  *      ldy     aaa
948  *      clc
949  *      adc     (sp),y
950  *      bcc     L
951  *      inx
952  * L:   jsr     pushax
953  *      ldx     #$00
954  *      lda     yyy
955  *      ldy     #$00
956  *      jsr     staspidx
957  *
958  * and replace it by:
959  *
960  *      ldy     aaa
961  *      ldx     #$00
962  *      lda     (sp),y
963  *      tay
964  *      lda     yyy
965  *      sta     label,y
966  */
967 {
968     unsigned Changes = 0;
969
970     /* Walk over the entries */
971     unsigned I = 0;
972     while (I < CS_GetEntryCount (S)) {
973
974         CodeEntry* L[12];
975         unsigned Len;
976
977         /* Get next entry */
978         L[0] = CS_GetEntry (S, I);
979
980         /* Check for the sequence */
981         if (L[0]->OPC == OP65_LDA                            &&
982             L[0]->AM == AM65_IMM                             &&
983             CS_GetEntries (S, L+1, I+1, 11)                  &&
984             L[1]->OPC == OP65_LDX                            &&
985             L[1]->AM == AM65_IMM                             &&
986             L[2]->OPC == OP65_LDY                            &&
987             L[3]->OPC == OP65_CLC                            &&
988             L[4]->OPC == OP65_ADC                            &&
989             L[4]->AM == AM65_ZP_INDY                         &&
990             strcmp (L[4]->Arg, "sp") == 0                    &&
991             (L[5]->OPC == OP65_BCC || L[5]->OPC == OP65_JCC) &&
992             L[5]->JumpTo != 0                                &&
993             L[5]->JumpTo->Owner == L[7]                      &&
994             L[6]->OPC == OP65_INX                            &&
995             CE_IsCallTo (L[7], "pushax")                     &&
996             L[8]->OPC == OP65_LDX                            &&
997             L[9]->OPC == OP65_LDA                            &&
998             L[10]->OPC == OP65_LDY                           &&
999             CE_IsKnownImm (L[10], 0)                         &&
1000             CE_IsCallTo (L[11], "staspidx")                  &&
1001             !CS_RangeHasLabel (S, I+1, 6)                    &&
1002             !CS_RangeHasLabel (S, I+8, 4)                    &&
1003             /* Check the label last because this is quite costly */
1004             (Len = strlen (L[0]->Arg)) > 3                   &&
1005             L[0]->Arg[0] == '<'                              &&
1006             L[0]->Arg[1] == '('                              &&
1007             strlen (L[1]->Arg) == Len                        &&
1008             L[1]->Arg[0] == '>'                              &&
1009             memcmp (L[0]->Arg+1, L[1]->Arg+1, Len-1) == 0) {
1010
1011             CodeEntry* X;
1012             char* Label;
1013
1014             /* We will create all the new stuff behind the current one so
1015              * we keep the line references.
1016              */
1017             X = NewCodeEntry (OP65_LDY, L[2]->AM, L[2]->Arg, 0, L[2]->LI);
1018             CS_InsertEntry (S, X, I+12);
1019
1020             X = NewCodeEntry (OP65_LDX, L[8]->AM, L[8]->Arg, 0, L[8]->LI);
1021             CS_InsertEntry (S, X, I+13);
1022
1023             X = NewCodeEntry (OP65_LDA, L[4]->AM, L[4]->Arg, 0, L[4]->LI);
1024             CS_InsertEntry (S, X, I+14);
1025
1026             X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, L[4]->LI);
1027             CS_InsertEntry (S, X, I+15);
1028
1029             X = NewCodeEntry (OP65_LDA, L[9]->AM, L[9]->Arg, 0, L[9]->LI);
1030             CS_InsertEntry (S, X, I+16);
1031
1032             Label = memcpy (xmalloc (Len-2), L[0]->Arg+2, Len-3);
1033             Label[Len-3] = '\0';
1034             X = NewCodeEntry (OP65_STA, AM65_ABSY, Label, 0, L[11]->LI);
1035             CS_InsertEntry (S, X, I+17);
1036             xfree (Label);
1037
1038             /* Remove the old code */
1039             CS_DelEntries (S, I, 12);
1040
1041             /* Remember, we had changes */
1042             ++Changes;
1043
1044         }
1045
1046         /* Next entry */
1047         ++I;
1048
1049     }
1050
1051     /* Return the number of changes made */
1052     return Changes;
1053 }
1054
1055
1056
1057 /*****************************************************************************/
1058 /*                            Decouple operations                            */
1059 /*****************************************************************************/
1060
1061
1062
1063 static unsigned OptDecouple (CodeSeg* S)
1064 /* Decouple operations, that is, do the following replacements:
1065  *
1066  *   dex        -> ldx #imm
1067  *   inx        -> ldx #imm
1068  *   dey        -> ldy #imm
1069  *   iny        -> ldy #imm
1070  *   tax        -> ldx #imm
1071  *   txa        -> lda #imm
1072  *   tay        -> ldy #imm
1073  *   tya        -> lda #imm
1074  *   lda zp     -> lda #imm
1075  *   ldx zp     -> ldx #imm
1076  *   ldy zp     -> ldy #imm
1077  *
1078  * Provided that the register values are known of course.
1079  */
1080 {
1081     unsigned Changes = 0;
1082     unsigned I;
1083
1084     /* Generate register info for the following step */
1085     CS_GenRegInfo (S);
1086
1087     /* Walk over the entries */
1088     I = 0;
1089     while (I < CS_GetEntryCount (S)) {
1090
1091         const char* Arg;
1092
1093         /* Get next entry and it's input register values */
1094         CodeEntry* E = CS_GetEntry (S, I);
1095         const RegContents* In = &E->RI->In;
1096
1097         /* Assume we have no replacement */
1098         CodeEntry* X = 0;
1099
1100         /* Check the instruction */
1101         switch (E->OPC) {
1102
1103             case OP65_DEA:
1104                 if (RegValIsKnown (In->RegA)) {
1105                     Arg = MakeHexArg ((In->RegA - 1) & 0xFF);
1106                     X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1107                 }
1108                 break;
1109
1110             case OP65_DEX:
1111                 if (RegValIsKnown (In->RegX)) {
1112                     Arg = MakeHexArg ((In->RegX - 1) & 0xFF);
1113                     X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1114                 }
1115                 break;
1116
1117             case OP65_DEY:
1118                 if (RegValIsKnown (In->RegY)) {
1119                     Arg = MakeHexArg ((In->RegY - 1) & 0xFF);
1120                     X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1121                 }
1122                 break;
1123
1124             case OP65_INA:
1125                 if (RegValIsKnown (In->RegA)) {
1126                     Arg = MakeHexArg ((In->RegA + 1) & 0xFF);
1127                     X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1128                 }
1129                 break;
1130
1131             case OP65_INX:
1132                 if (RegValIsKnown (In->RegX)) {
1133                     Arg = MakeHexArg ((In->RegX + 1) & 0xFF);
1134                     X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1135                 }
1136                 break;
1137
1138             case OP65_INY:
1139                 if (RegValIsKnown (In->RegY)) {
1140                     Arg = MakeHexArg ((In->RegY + 1) & 0xFF);
1141                     X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1142                 }
1143                 break;
1144
1145             case OP65_LDA:
1146                 if (E->AM == AM65_ZP) {
1147                     switch (GetKnownReg (E->Use & REG_ZP, In)) {
1148                         case REG_TMP1:
1149                             Arg = MakeHexArg (In->Tmp1);
1150                             X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1151                             break;
1152
1153                         case REG_PTR1_LO:
1154                             Arg = MakeHexArg (In->Ptr1Lo);
1155                             X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1156                             break;
1157
1158                         case REG_PTR1_HI:
1159                             Arg = MakeHexArg (In->Ptr1Hi);
1160                             X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1161                             break;
1162
1163                         case REG_SREG_LO:
1164                             Arg = MakeHexArg (In->SRegLo);
1165                             X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1166                             break;
1167
1168                         case REG_SREG_HI:
1169                             Arg = MakeHexArg (In->SRegHi);
1170                             X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1171                             break;
1172                     }
1173                 }
1174                 break;
1175
1176             case OP65_LDX:
1177                 if (E->AM == AM65_ZP) {
1178                     switch (GetKnownReg (E->Use & REG_ZP, In)) {
1179                         case REG_TMP1:
1180                             Arg = MakeHexArg (In->Tmp1);
1181                             X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1182                             break;
1183
1184                         case REG_PTR1_LO:
1185                             Arg = MakeHexArg (In->Ptr1Lo);
1186                             X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1187                             break;
1188
1189                         case REG_PTR1_HI:
1190                             Arg = MakeHexArg (In->Ptr1Hi);
1191                             X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1192                             break;
1193
1194                         case REG_SREG_LO:
1195                             Arg = MakeHexArg (In->SRegLo);
1196                             X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1197                             break;
1198
1199                         case REG_SREG_HI:
1200                             Arg = MakeHexArg (In->SRegHi);
1201                             X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1202                             break;
1203                     }
1204                 }
1205                 break;
1206
1207             case OP65_LDY:
1208                 if (E->AM == AM65_ZP) {
1209                     switch (GetKnownReg (E->Use, In)) {
1210                         case REG_TMP1:
1211                             Arg = MakeHexArg (In->Tmp1);
1212                             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1213                             break;
1214
1215                         case REG_PTR1_LO:
1216                             Arg = MakeHexArg (In->Ptr1Lo);
1217                             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1218                             break;
1219
1220                         case REG_PTR1_HI:
1221                             Arg = MakeHexArg (In->Ptr1Hi);
1222                             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1223                             break;
1224
1225                         case REG_SREG_LO:
1226                             Arg = MakeHexArg (In->SRegLo);
1227                             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1228                             break;
1229
1230                         case REG_SREG_HI:
1231                             Arg = MakeHexArg (In->SRegHi);
1232                             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1233                             break;
1234                     }
1235                 }
1236                 break;
1237
1238             case OP65_TAX:
1239                 if (E->RI->In.RegA >= 0) {
1240                     Arg = MakeHexArg (In->RegA);
1241                     X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1242                 }
1243                 break;
1244
1245             case OP65_TAY:
1246                 if (E->RI->In.RegA >= 0) {
1247                     Arg = MakeHexArg (In->RegA);
1248                     X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1249                 }
1250                 break;
1251
1252             case OP65_TXA:
1253                 if (E->RI->In.RegX >= 0) {
1254                     Arg = MakeHexArg (In->RegX);
1255                     X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1256                 }
1257                 break;
1258
1259             case OP65_TYA:
1260                 if (E->RI->In.RegY >= 0) {
1261                     Arg = MakeHexArg (In->RegY);
1262                     X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1263                 }
1264                 break;
1265
1266             default:
1267                 /* Avoid gcc warnings */
1268                 break;
1269
1270         }
1271
1272         /* Insert the replacement if we have one */
1273         if (X) {
1274             CS_InsertEntry (S, X, I+1);
1275             CS_DelEntry (S, I);
1276             ++Changes;
1277         }
1278
1279         /* Next entry */
1280         ++I;
1281
1282     }
1283
1284     /* Free register info */
1285     CS_FreeRegInfo (S);
1286
1287     /* Return the number of changes made */
1288     return Changes;
1289 }
1290
1291
1292
1293 /*****************************************************************************/
1294 /*                        Optimize stack pointer ops                         */
1295 /*****************************************************************************/
1296
1297
1298
1299 static unsigned IsDecSP (const CodeEntry* E)
1300 /* Check if this is an insn that decrements the stack pointer. If so, return
1301  * the decrement. If not, return zero.
1302  * The function expects E to be a subroutine call.
1303  */
1304 {
1305     if (strncmp (E->Arg, "decsp", 5) == 0) {
1306         if (E->Arg[5] >= '1' && E->Arg[5] <= '8') {
1307             return (E->Arg[5] - '0');
1308         }
1309     } else if (strcmp (E->Arg, "subysp") == 0 && RegValIsKnown (E->RI->In.RegY)) {
1310         return E->RI->In.RegY;
1311     }
1312
1313     /* If we come here, it's not a decsp op */
1314     return 0;
1315 }
1316
1317
1318
1319 static unsigned OptStackPtrOps (CodeSeg* S)
1320 /* Merge adjacent calls to decsp into one. NOTE: This function won't merge all
1321  * known cases!
1322  */
1323 {
1324     unsigned Changes = 0;
1325     unsigned I;
1326
1327     /* Generate register info for the following step */
1328     CS_GenRegInfo (S);
1329
1330     /* Walk over the entries */
1331     I = 0;
1332     while (I < CS_GetEntryCount (S)) {
1333
1334         unsigned Dec1;
1335         unsigned Dec2;
1336         const CodeEntry* N;
1337
1338         /* Get the next entry */
1339         const CodeEntry* E = CS_GetEntry (S, I);
1340
1341         /* Check for decspn or subysp */
1342         if (E->OPC == OP65_JSR                          &&
1343             (Dec1 = IsDecSP (E)) > 0                    &&
1344             (N = CS_GetNextEntry (S, I)) != 0           &&
1345             (Dec2 = IsDecSP (N)) > 0                    &&
1346             (Dec1 += Dec2) <= 255                       &&
1347             !CE_HasLabel (N)) {
1348
1349             CodeEntry* X;
1350             char Buf[20];
1351
1352             /* We can combine the two */
1353             if (Dec1 <= 8) {
1354                 /* Insert a call to decsp */
1355                 xsprintf (Buf, sizeof (Buf), "decsp%u", Dec1);
1356                 X = NewCodeEntry (OP65_JSR, AM65_ABS, Buf, 0, N->LI);
1357                 CS_InsertEntry (S, X, I+2);
1358             } else {
1359                 /* Insert a call to subysp */
1360                 const char* Arg = MakeHexArg (Dec1);
1361                 X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, N->LI);
1362                 CS_InsertEntry (S, X, I+2);
1363                 X = NewCodeEntry (OP65_JSR, AM65_ABS, "subysp", 0, N->LI);
1364                 CS_InsertEntry (S, X, I+3);
1365             }
1366
1367             /* Delete the old code */
1368             CS_DelEntries (S, I, 2);
1369
1370             /* Regenerate register info */
1371             CS_GenRegInfo (S);
1372
1373             /* Remember we had changes */
1374             ++Changes;
1375
1376         } else {
1377
1378             /* Next entry */
1379             ++I;
1380         }
1381
1382     }
1383
1384     /* Free register info */
1385     CS_FreeRegInfo (S);
1386
1387     /* Return the number of changes made */
1388     return Changes;
1389 }
1390
1391
1392
1393 /*****************************************************************************/
1394 /*                              struct OptFunc                               */
1395 /*****************************************************************************/
1396
1397
1398
1399 typedef struct OptFunc OptFunc;
1400 struct OptFunc {
1401     unsigned       (*Func) (CodeSeg*);  /* Optimizer function */
1402     const char*    Name;                /* Name of the function/group */
1403     unsigned       CodeSizeFactor;      /* Code size factor for this opt func */
1404     unsigned long  TotalRuns;           /* Total number of runs */
1405     unsigned long  LastRuns;            /* Last number of runs */
1406     unsigned long  TotalChanges;        /* Total number of changes */
1407     unsigned long  LastChanges;         /* Last number of changes */
1408     char           Disabled;            /* True if function disabled */
1409 };
1410
1411
1412
1413 /*****************************************************************************/
1414 /*                                   Code                                    */
1415 /*****************************************************************************/
1416
1417
1418
1419 /* A list of all the function descriptions */
1420 static OptFunc DOpt65C02BitOps  = { Opt65C02BitOps,  "Opt65C02BitOps",   66, 0, 0, 0, 0, 0 };
1421 static OptFunc DOpt65C02Ind     = { Opt65C02Ind,     "Opt65C02Ind",     100, 0, 0, 0, 0, 0 };
1422 static OptFunc DOpt65C02Stores  = { Opt65C02Stores,  "Opt65C02Stores",  100, 0, 0, 0, 0, 0 };
1423 static OptFunc DOptAdd1         = { OptAdd1,         "OptAdd1",         125, 0, 0, 0, 0, 0 };
1424 static OptFunc DOptAdd2         = { OptAdd2,         "OptAdd2",         200, 0, 0, 0, 0, 0 };
1425 static OptFunc DOptAdd3         = { OptAdd3,         "OptAdd3",          65, 0, 0, 0, 0, 0 };
1426 static OptFunc DOptAdd4         = { OptAdd4,         "OptAdd4",          90, 0, 0, 0, 0, 0 };
1427 static OptFunc DOptAdd5         = { OptAdd5,         "OptAdd5",         100, 0, 0, 0, 0, 0 };
1428 static OptFunc DOptAdd6         = { OptAdd6,         "OptAdd6",          40, 0, 0, 0, 0, 0 };
1429 static OptFunc DOptBoolTrans    = { OptBoolTrans,    "OptBoolTrans",    100, 0, 0, 0, 0, 0 };
1430 static OptFunc DOptBranchDist   = { OptBranchDist,   "OptBranchDist",     0, 0, 0, 0, 0, 0 };
1431 static OptFunc DOptCmp1         = { OptCmp1,         "OptCmp1",          42, 0, 0, 0, 0, 0 };
1432 static OptFunc DOptCmp2         = { OptCmp2,         "OptCmp2",          85, 0, 0, 0, 0, 0 };
1433 static OptFunc DOptCmp3         = { OptCmp3,         "OptCmp3",          75, 0, 0, 0, 0, 0 };
1434 static OptFunc DOptCmp4         = { OptCmp4,         "OptCmp4",          75, 0, 0, 0, 0, 0 };
1435 static OptFunc DOptCmp5         = { OptCmp5,         "OptCmp5",         100, 0, 0, 0, 0, 0 };
1436 static OptFunc DOptCmp6         = { OptCmp6,         "OptCmp6",         100, 0, 0, 0, 0, 0 };
1437 static OptFunc DOptCmp7         = { OptCmp7,         "OptCmp7",          85, 0, 0, 0, 0, 0 };
1438 static OptFunc DOptCmp8         = { OptCmp8,         "OptCmp8",          50, 0, 0, 0, 0, 0 };
1439 static OptFunc DOptCmp9         = { OptCmp9,         "OptCmp9",          85, 0, 0, 0, 0, 0 };
1440 static OptFunc DOptCondBranches1= { OptCondBranches1,"OptCondBranches1", 80, 0, 0, 0, 0, 0 };
1441 static OptFunc DOptCondBranches2= { OptCondBranches2,"OptCondBranches2",  0, 0, 0, 0, 0, 0 };
1442 static OptFunc DOptDeadCode     = { OptDeadCode,     "OptDeadCode",     100, 0, 0, 0, 0, 0 };
1443 static OptFunc DOptDeadJumps    = { OptDeadJumps,    "OptDeadJumps",    100, 0, 0, 0, 0, 0 };
1444 static OptFunc DOptDecouple     = { OptDecouple,     "OptDecouple",     100, 0, 0, 0, 0, 0 };
1445 static OptFunc DOptDupLoads     = { OptDupLoads,     "OptDupLoads",       0, 0, 0, 0, 0, 0 };
1446 static OptFunc DOptIndLoads1    = { OptIndLoads1,    "OptIndLoads1",      0, 0, 0, 0, 0, 0 };
1447 static OptFunc DOptIndLoads2    = { OptIndLoads2,    "OptIndLoads2",      0, 0, 0, 0, 0, 0 };
1448 static OptFunc DOptJumpCascades = { OptJumpCascades, "OptJumpCascades", 100, 0, 0, 0, 0, 0 };
1449 static OptFunc DOptJumpTarget1  = { OptJumpTarget1,  "OptJumpTarget1",  100, 0, 0, 0, 0, 0 };
1450 static OptFunc DOptJumpTarget2  = { OptJumpTarget2,  "OptJumpTarget2",  100, 0, 0, 0, 0, 0 };
1451 static OptFunc DOptJumpTarget3  = { OptJumpTarget3,  "OptJumpTarget3",  100, 0, 0, 0, 0, 0 };
1452 static OptFunc DOptLoad1        = { OptLoad1,        "OptLoad1",        100, 0, 0, 0, 0, 0 };
1453 static OptFunc DOptLoad2        = { OptLoad2,        "OptLoad2",        200, 0, 0, 0, 0, 0 };
1454 static OptFunc DOptRTS          = { OptRTS,          "OptRTS",          100, 0, 0, 0, 0, 0 };
1455 static OptFunc DOptRTSJumps1    = { OptRTSJumps1,    "OptRTSJumps1",    100, 0, 0, 0, 0, 0 };
1456 static OptFunc DOptRTSJumps2    = { OptRTSJumps2,    "OptRTSJumps2",    100, 0, 0, 0, 0, 0 };
1457 static OptFunc DOptNegA1        = { OptNegA1,        "OptNegA1",        100, 0, 0, 0, 0, 0 };
1458 static OptFunc DOptNegA2        = { OptNegA2,        "OptNegA2",        100, 0, 0, 0, 0, 0 };
1459 static OptFunc DOptNegAX1       = { OptNegAX1,       "OptNegAX1",       100, 0, 0, 0, 0, 0 };
1460 static OptFunc DOptNegAX2       = { OptNegAX2,       "OptNegAX2",       100, 0, 0, 0, 0, 0 };
1461 static OptFunc DOptNegAX3       = { OptNegAX3,       "OptNegAX3",       100, 0, 0, 0, 0, 0 };
1462 static OptFunc DOptNegAX4       = { OptNegAX4,       "OptNegAX4",       100, 0, 0, 0, 0, 0 };
1463 static OptFunc DOptPrecalc      = { OptPrecalc,      "OptPrecalc",      100, 0, 0, 0, 0, 0 };
1464 static OptFunc DOptPtrLoad1     = { OptPtrLoad1,     "OptPtrLoad1",     100, 0, 0, 0, 0, 0 };
1465 static OptFunc DOptPtrLoad2     = { OptPtrLoad2,     "OptPtrLoad2",     100, 0, 0, 0, 0, 0 };
1466 static OptFunc DOptPtrLoad3     = { OptPtrLoad3,     "OptPtrLoad3",     100, 0, 0, 0, 0, 0 };
1467 static OptFunc DOptPtrLoad4     = { OptPtrLoad4,     "OptPtrLoad4",     100, 0, 0, 0, 0, 0 };
1468 static OptFunc DOptPtrLoad5     = { OptPtrLoad5,     "OptPtrLoad5",      50, 0, 0, 0, 0, 0 };
1469 static OptFunc DOptPtrLoad6     = { OptPtrLoad6,     "OptPtrLoad6",      60, 0, 0, 0, 0, 0 };
1470 static OptFunc DOptPtrLoad7     = { OptPtrLoad7,     "OptPtrLoad7",     140, 0, 0, 0, 0, 0 };
1471 static OptFunc DOptPtrLoad11    = { OptPtrLoad11,    "OptPtrLoad11",     92, 0, 0, 0, 0, 0 };
1472 static OptFunc DOptPtrLoad12    = { OptPtrLoad12,    "OptPtrLoad12",     50, 0, 0, 0, 0, 0 };
1473 static OptFunc DOptPtrLoad13    = { OptPtrLoad13,    "OptPtrLoad13",     65, 0, 0, 0, 0, 0 };
1474 static OptFunc DOptPtrLoad14    = { OptPtrLoad14,    "OptPtrLoad14",    108, 0, 0, 0, 0, 0 };
1475 static OptFunc DOptPtrLoad15    = { OptPtrLoad15,    "OptPtrLoad15",     86, 0, 0, 0, 0, 0 };
1476 static OptFunc DOptPtrLoad16    = { OptPtrLoad16,    "OptPtrLoad16",    100, 0, 0, 0, 0, 0 };
1477 static OptFunc DOptPtrLoad17    = { OptPtrLoad17,    "OptPtrLoad17",    190, 0, 0, 0, 0, 0 };
1478 static OptFunc DOptPtrStore1    = { OptPtrStore1,    "OptPtrStore1",    100, 0, 0, 0, 0, 0 };
1479 static OptFunc DOptPtrStore2    = { OptPtrStore2,    "OptPtrStore2",     40, 0, 0, 0, 0, 0 };
1480 static OptFunc DOptPtrStore3    = { OptPtrStore3,    "OptPtrStore3",     50, 0, 0, 0, 0, 0 };
1481 static OptFunc DOptPush1        = { OptPush1,        "OptPush1",         65, 0, 0, 0, 0, 0 };
1482 static OptFunc DOptPush2        = { OptPush2,        "OptPush2",         50, 0, 0, 0, 0, 0 };
1483 static OptFunc DOptPushPop      = { OptPushPop,      "OptPushPop",        0, 0, 0, 0, 0, 0 };
1484 static OptFunc DOptShift1       = { OptShift1,       "OptShift1",       100, 0, 0, 0, 0, 0 };
1485 static OptFunc DOptShift2       = { OptShift2,       "OptShift2",       100, 0, 0, 0, 0, 0 };
1486 static OptFunc DOptShift3       = { OptShift3,       "OptShift3",       100, 0, 0, 0, 0, 0 };
1487 static OptFunc DOptShift4       = { OptShift4,       "OptShift4",       110, 0, 0, 0, 0, 0 };
1488 static OptFunc DOptShift5       = { OptShift5,       "OptShift5",       200, 0, 0, 0, 0, 0 };
1489 static OptFunc DOptSize1        = { OptSize1,        "OptSize1",        100, 0, 0, 0, 0, 0 };
1490 static OptFunc DOptSize2        = { OptSize2,        "OptSize2",        100, 0, 0, 0, 0, 0 };
1491 static OptFunc DOptStackOps     = { OptStackOps,     "OptStackOps",     100, 0, 0, 0, 0, 0 };
1492 static OptFunc DOptStackPtrOps  = { OptStackPtrOps,  "OptStackPtrOps",   50, 0, 0, 0, 0, 0 };
1493 static OptFunc DOptStore1       = { OptStore1,       "OptStore1",        70, 0, 0, 0, 0, 0 };
1494 static OptFunc DOptStore2       = { OptStore2,       "OptStore2",       115, 0, 0, 0, 0, 0 };
1495 static OptFunc DOptStore3       = { OptStore3,       "OptStore3",       120, 0, 0, 0, 0, 0 };
1496 static OptFunc DOptStore4       = { OptStore4,       "OptStore4",        50, 0, 0, 0, 0, 0 };
1497 static OptFunc DOptStore5       = { OptStore5,       "OptStore5",       100, 0, 0, 0, 0, 0 };
1498 static OptFunc DOptStoreLoad    = { OptStoreLoad,    "OptStoreLoad",      0, 0, 0, 0, 0, 0 };
1499 static OptFunc DOptSub1         = { OptSub1,         "OptSub1",         100, 0, 0, 0, 0, 0 };
1500 static OptFunc DOptSub2         = { OptSub2,         "OptSub2",         100, 0, 0, 0, 0, 0 };
1501 static OptFunc DOptSub3         = { OptSub3,         "OptSub3",         100, 0, 0, 0, 0, 0 };
1502 static OptFunc DOptTest1        = { OptTest1,        "OptTest1",         65, 0, 0, 0, 0, 0 };
1503 static OptFunc DOptTest2        = { OptTest2,        "OptTest2",         50, 0, 0, 0, 0, 0 };
1504 static OptFunc DOptTransfers1   = { OptTransfers1,   "OptTransfers1",     0, 0, 0, 0, 0, 0 };
1505 static OptFunc DOptTransfers2   = { OptTransfers2,   "OptTransfers2",    60, 0, 0, 0, 0, 0 };
1506 static OptFunc DOptTransfers3   = { OptTransfers3,   "OptTransfers3",    65, 0, 0, 0, 0, 0 };
1507 static OptFunc DOptTransfers4   = { OptTransfers4,   "OptTransfers4",    65, 0, 0, 0, 0, 0 };
1508 static OptFunc DOptUnusedLoads  = { OptUnusedLoads,  "OptUnusedLoads",    0, 0, 0, 0, 0, 0 };
1509 static OptFunc DOptUnusedStores = { OptUnusedStores, "OptUnusedStores",   0, 0, 0, 0, 0, 0 };
1510
1511
1512 /* Table containing all the steps in alphabetical order */
1513 static OptFunc* OptFuncs[] = {
1514     &DOpt65C02BitOps,
1515     &DOpt65C02Ind,
1516     &DOpt65C02Stores,
1517     &DOptAdd1,
1518     &DOptAdd2,
1519     &DOptAdd3,
1520     &DOptAdd4,
1521     &DOptAdd5,
1522     &DOptAdd6,
1523     &DOptBoolTrans,
1524     &DOptBranchDist,
1525     &DOptCmp1,
1526     &DOptCmp2,
1527     &DOptCmp3,
1528     &DOptCmp4,
1529     &DOptCmp5,
1530     &DOptCmp6,
1531     &DOptCmp7,
1532     &DOptCmp8,
1533     &DOptCmp9,
1534     &DOptCondBranches1,
1535     &DOptCondBranches2,
1536     &DOptDeadCode,
1537     &DOptDeadJumps,
1538     &DOptDecouple,
1539     &DOptDupLoads,
1540     &DOptIndLoads1,
1541     &DOptIndLoads2,
1542     &DOptJumpCascades,
1543     &DOptJumpTarget1,
1544     &DOptJumpTarget2,
1545     &DOptJumpTarget3,
1546     &DOptLoad1,
1547     &DOptLoad2,
1548     &DOptNegA1,
1549     &DOptNegA2,
1550     &DOptNegAX1,
1551     &DOptNegAX2,
1552     &DOptNegAX3,
1553     &DOptNegAX4,
1554     &DOptPrecalc,
1555     &DOptPtrLoad1,
1556     &DOptPtrLoad11,
1557     &DOptPtrLoad12,
1558     &DOptPtrLoad13,
1559     &DOptPtrLoad14,
1560     &DOptPtrLoad15,
1561     &DOptPtrLoad16,
1562     &DOptPtrLoad17,
1563     &DOptPtrLoad2,
1564     &DOptPtrLoad3,
1565     &DOptPtrLoad4,
1566     &DOptPtrLoad5,
1567     &DOptPtrLoad6,
1568     &DOptPtrLoad7,
1569     &DOptPtrStore1,
1570     &DOptPtrStore2,
1571     &DOptPtrStore3,
1572     &DOptPush1,
1573     &DOptPush2,
1574     &DOptPushPop,
1575     &DOptRTS,
1576     &DOptRTSJumps1,
1577     &DOptRTSJumps2,
1578     &DOptShift1,
1579     &DOptShift2,
1580     &DOptShift3,
1581     &DOptShift4,
1582     &DOptShift5,
1583     &DOptSize1,
1584     &DOptSize2,
1585     &DOptStackOps,
1586     &DOptStackPtrOps,
1587     &DOptStore1,
1588     &DOptStore2,
1589     &DOptStore3,
1590     &DOptStore4,
1591     &DOptStore5,
1592     &DOptStoreLoad,
1593     &DOptSub1,
1594     &DOptSub2,
1595     &DOptSub3,
1596     &DOptTest1,
1597     &DOptTest2,
1598     &DOptTransfers1,
1599     &DOptTransfers2,
1600     &DOptTransfers3,
1601     &DOptTransfers4,
1602     &DOptUnusedLoads,
1603     &DOptUnusedStores,
1604 };
1605 #define OPTFUNC_COUNT  (sizeof(OptFuncs) / sizeof(OptFuncs[0]))
1606
1607
1608
1609 static int CmpOptStep (const void* Key, const void* Func)
1610 /* Compare function for bsearch */
1611 {
1612     return strcmp (Key, (*(const OptFunc**)Func)->Name);
1613 }
1614
1615
1616
1617 static OptFunc* FindOptFunc (const char* Name)
1618 /* Find an optimizer step by name in the table and return a pointer. Return
1619  * NULL if no such step is found.
1620  */
1621 {
1622     /* Search for the function in the list */
1623     OptFunc** O = bsearch (Name, OptFuncs, OPTFUNC_COUNT, sizeof (OptFuncs[0]), CmpOptStep);
1624     return O? *O : 0;
1625 }
1626
1627
1628
1629 static OptFunc* GetOptFunc (const char* Name)
1630 /* Find an optimizer step by name in the table and return a pointer. Print an
1631  * error and call AbEnd if not found.
1632  */
1633 {
1634     /* Search for the function in the list */
1635     OptFunc* F = FindOptFunc (Name);
1636     if (F == 0) {
1637         /* Not found */
1638         AbEnd ("Optimization step `%s' not found", Name);
1639     }
1640     return F;
1641 }
1642
1643
1644
1645 void DisableOpt (const char* Name)
1646 /* Disable the optimization with the given name */
1647 {
1648     if (strcmp (Name, "any") == 0) {
1649         unsigned I;
1650         for (I = 0; I < OPTFUNC_COUNT; ++I) {
1651             OptFuncs[I]->Disabled = 1;
1652         }
1653     } else {
1654         GetOptFunc(Name)->Disabled = 1;
1655     }
1656 }
1657
1658
1659
1660 void EnableOpt (const char* Name)
1661 /* Enable the optimization with the given name */
1662 {
1663     if (strcmp (Name, "any") == 0) {
1664         unsigned I;
1665         for (I = 0; I < OPTFUNC_COUNT; ++I) {
1666             OptFuncs[I]->Disabled = 0;
1667         }
1668     } else {
1669         GetOptFunc(Name)->Disabled = 0;
1670     }
1671 }
1672
1673
1674
1675 void ListOptSteps (FILE* F)
1676 /* List all optimization steps */
1677 {
1678     unsigned I;
1679     for (I = 0; I < OPTFUNC_COUNT; ++I) {
1680         fprintf (F, "%s\n", OptFuncs[I]->Name);
1681     }
1682 }
1683
1684
1685
1686 static void ReadOptStats (const char* Name)
1687 /* Read the optimizer statistics file */
1688 {
1689     char Buf [256];
1690     unsigned Lines;
1691
1692     /* Try to open the file */
1693     FILE* F = fopen (Name, "r");
1694     if (F == 0) {
1695         /* Ignore the error */
1696         return;
1697     }
1698
1699     /* Read and parse the lines */
1700     Lines = 0;
1701     while (fgets (Buf, sizeof (Buf), F) != 0) {
1702
1703         char* B;
1704         unsigned Len;
1705         OptFunc* Func;
1706
1707         /* Fields */
1708         char Name[32];
1709         unsigned long  TotalRuns;
1710         unsigned long  TotalChanges;
1711
1712         /* Count lines */
1713         ++Lines;
1714
1715         /* Remove trailing white space including the line terminator */
1716         B = Buf;
1717         Len = strlen (B);
1718         while (Len > 0 && IsSpace (B[Len-1])) {
1719             --Len;
1720         }
1721         B[Len] = '\0';
1722
1723         /* Remove leading whitespace */
1724         while (IsSpace (*B)) {
1725             ++B;
1726         }
1727
1728         /* Check for empty and comment lines */
1729         if (*B == '\0' || *B == ';' || *B == '#') {
1730             continue;
1731         }
1732
1733         /* Parse the line */
1734         if (sscanf (B, "%31s %lu %*u %lu %*u", Name, &TotalRuns, &TotalChanges) != 3) {
1735             /* Syntax error */
1736             continue;
1737         }
1738
1739         /* Search for the optimizer step. */
1740         Func = FindOptFunc (Name);
1741         if (Func == 0) {
1742             /* Not found */
1743             continue;
1744         }
1745
1746         /* Found the step, set the fields */
1747         Func->TotalRuns    = TotalRuns;
1748         Func->TotalChanges = TotalChanges;
1749
1750     }
1751
1752     /* Close the file, ignore errors here. */
1753     fclose (F);
1754 }
1755
1756
1757
1758 static void WriteOptStats (const char* Name)
1759 /* Write the optimizer statistics file */
1760 {
1761     unsigned I;
1762
1763     /* Try to open the file */
1764     FILE* F = fopen (Name, "w");
1765     if (F == 0) {
1766         /* Ignore the error */
1767         return;
1768     }
1769
1770     /* Write a header */
1771     fprintf (F,
1772              "; Optimizer               Total      Last       Total      Last\n"
1773              ";   Step                  Runs       Runs        Chg       Chg\n");
1774
1775
1776     /* Write the data */
1777     for (I = 0; I < OPTFUNC_COUNT; ++I) {
1778         const OptFunc* O = OptFuncs[I];
1779         fprintf (F,
1780                  "%-20s %10lu %10lu %10lu %10lu\n",
1781                  O->Name,
1782                  O->TotalRuns,
1783                  O->LastRuns,
1784                  O->TotalChanges,
1785                  O->LastChanges);
1786     }
1787
1788     /* Close the file, ignore errors here. */
1789     fclose (F);
1790 }
1791
1792
1793
1794 static unsigned RunOptFunc (CodeSeg* S, OptFunc* F, unsigned Max)
1795 /* Run one optimizer function Max times or until there are no more changes */
1796 {
1797     unsigned Changes, C;
1798
1799     /* Don't run the function if it is disabled or if it is prohibited by the
1800      * code size factor
1801      */
1802     if (F->Disabled || F->CodeSizeFactor > S->CodeSizeFactor) {
1803         return 0;
1804     }
1805
1806     /* Run this until there are no more changes */
1807     Changes = 0;
1808     do {
1809
1810         /* Run the function */
1811         C = F->Func (S);
1812         Changes += C;
1813
1814         /* Do statistics */
1815         ++F->TotalRuns;
1816         ++F->LastRuns;
1817         F->TotalChanges += C;
1818         F->LastChanges  += C;
1819
1820     } while (--Max && C > 0);
1821
1822     /* Return the number of changes */
1823     return Changes;
1824 }
1825
1826
1827
1828 static unsigned RunOptGroup1 (CodeSeg* S)
1829 /* Run the first group of optimization steps. These steps translate known
1830  * patterns emitted by the code generator into more optimal patterns. Order
1831  * of the steps is important, because some of the steps done earlier cover
1832  * the same patterns as later steps as subpatterns.
1833  */
1834 {
1835     unsigned Changes = 0;
1836
1837     Changes += RunOptFunc (S, &DOptStackPtrOps, 5);
1838     Changes += RunOptFunc (S, &DOptPtrStore1, 1);
1839     Changes += RunOptFunc (S, &DOptPtrStore2, 1);
1840     Changes += RunOptFunc (S, &DOptPtrStore3, 1);
1841     Changes += RunOptFunc (S, &DOptAdd3, 1);    /* Before OptPtrLoad5! */
1842     Changes += RunOptFunc (S, &DOptPtrLoad1, 1);
1843     Changes += RunOptFunc (S, &DOptPtrLoad2, 1);
1844     Changes += RunOptFunc (S, &DOptPtrLoad3, 1);
1845     Changes += RunOptFunc (S, &DOptPtrLoad4, 1);
1846     Changes += RunOptFunc (S, &DOptPtrLoad5, 1);
1847     Changes += RunOptFunc (S, &DOptPtrLoad6, 1);
1848     Changes += RunOptFunc (S, &DOptPtrLoad7, 1);
1849     Changes += RunOptFunc (S, &DOptPtrLoad11, 1);
1850     Changes += RunOptFunc (S, &DOptPtrLoad12, 1);
1851     Changes += RunOptFunc (S, &DOptPtrLoad13, 1);
1852     Changes += RunOptFunc (S, &DOptPtrLoad14, 1);
1853     Changes += RunOptFunc (S, &DOptPtrLoad15, 1);
1854     Changes += RunOptFunc (S, &DOptPtrLoad16, 1);
1855     Changes += RunOptFunc (S, &DOptPtrLoad17, 1);
1856     Changes += RunOptFunc (S, &DOptNegAX1, 1);
1857     Changes += RunOptFunc (S, &DOptNegAX2, 1);
1858     Changes += RunOptFunc (S, &DOptNegAX3, 1);
1859     Changes += RunOptFunc (S, &DOptNegAX4, 1);
1860     Changes += RunOptFunc (S, &DOptAdd1, 1);
1861     Changes += RunOptFunc (S, &DOptAdd2, 1);
1862     Changes += RunOptFunc (S, &DOptAdd4, 1);
1863     Changes += RunOptFunc (S, &DOptAdd5, 1);
1864     Changes += RunOptFunc (S, &DOptAdd6, 1);
1865     Changes += RunOptFunc (S, &DOptAdd6, 1);
1866     Changes += RunOptFunc (S, &DOptSub1, 1);
1867     Changes += RunOptFunc (S, &DOptSub3, 1);
1868     Changes += RunOptFunc (S, &DOptStore4, 1);
1869     Changes += RunOptFunc (S, &DOptStore5, 1);
1870     Changes += RunOptFunc (S, &DOptShift1, 1);
1871     Changes += RunOptFunc (S, &DOptShift2, 1);
1872     Changes += RunOptFunc (S, &DOptShift3, 1);
1873     Changes += RunOptFunc (S, &DOptShift4, 1);
1874     Changes += RunOptFunc (S, &DOptStore1, 1);
1875     Changes += RunOptFunc (S, &DOptStore2, 5);
1876     Changes += RunOptFunc (S, &DOptStore3, 5);
1877
1878     /* Return the number of changes */
1879     return Changes;
1880 }
1881
1882
1883
1884 static unsigned RunOptGroup2 (CodeSeg* S)
1885 /* Run one group of optimization steps. This step involves just decoupling
1886  * instructions by replacing them by instructions that do not depend on
1887  * previous instructions. This makes it easier to find instructions that
1888  * aren't used.
1889  */
1890 {
1891     unsigned Changes = 0;
1892
1893     Changes += RunOptFunc (S, &DOptDecouple, 1);
1894
1895     /* Return the number of changes */
1896     return Changes;
1897 }
1898
1899
1900
1901 static unsigned RunOptGroup3 (CodeSeg* S)
1902 /* Run one group of optimization steps. These steps depend on each other,
1903  * that means that one step may allow another step to do additional work,
1904  * so we will repeat the steps as long as we see any changes.
1905  */
1906 {
1907     unsigned Changes, C;
1908
1909     Changes = 0;
1910     do {
1911         C = 0;
1912
1913         C += RunOptFunc (S, &DOptNegA1, 1);
1914         C += RunOptFunc (S, &DOptNegA2, 1);
1915         C += RunOptFunc (S, &DOptStackOps, 1);
1916         C += RunOptFunc (S, &DOptSub1, 1);
1917         C += RunOptFunc (S, &DOptSub2, 1);
1918         C += RunOptFunc (S, &DOptSub3, 1);
1919         C += RunOptFunc (S, &DOptAdd5, 1);
1920         C += RunOptFunc (S, &DOptAdd6, 1);
1921         C += RunOptFunc (S, &DOptJumpCascades, 1);
1922         C += RunOptFunc (S, &DOptDeadJumps, 1);
1923         C += RunOptFunc (S, &DOptRTS, 1);
1924         C += RunOptFunc (S, &DOptDeadCode, 1);
1925         C += RunOptFunc (S, &DOptBoolTrans, 1);
1926         C += RunOptFunc (S, &DOptJumpTarget1, 1);
1927         C += RunOptFunc (S, &DOptJumpTarget2, 1);
1928         C += RunOptFunc (S, &DOptCondBranches1, 1);
1929         C += RunOptFunc (S, &DOptCondBranches2, 1);
1930         C += RunOptFunc (S, &DOptRTSJumps1, 1);
1931         C += RunOptFunc (S, &DOptCmp1, 1);
1932         C += RunOptFunc (S, &DOptCmp2, 1);
1933         C += RunOptFunc (S, &DOptCmp3, 1);
1934         C += RunOptFunc (S, &DOptCmp4, 1);
1935         C += RunOptFunc (S, &DOptCmp5, 1);
1936         C += RunOptFunc (S, &DOptCmp6, 1);
1937         C += RunOptFunc (S, &DOptCmp7, 1);
1938         C += RunOptFunc (S, &DOptCmp8, 1);
1939         C += RunOptFunc (S, &DOptCmp9, 1);
1940         C += RunOptFunc (S, &DOptTest1, 1);
1941         C += RunOptFunc (S, &DOptLoad1, 1);
1942         C += RunOptFunc (S, &DOptJumpTarget3, 1);       /* After OptCondBranches2 */
1943         C += RunOptFunc (S, &DOptUnusedLoads, 1);
1944         C += RunOptFunc (S, &DOptUnusedStores, 1);
1945         C += RunOptFunc (S, &DOptDupLoads, 1);
1946         C += RunOptFunc (S, &DOptStoreLoad, 1);
1947         C += RunOptFunc (S, &DOptTransfers1, 1);
1948         C += RunOptFunc (S, &DOptTransfers3, 1);
1949         C += RunOptFunc (S, &DOptTransfers4, 1);
1950         C += RunOptFunc (S, &DOptStore1, 1);
1951         C += RunOptFunc (S, &DOptStore5, 1);
1952         C += RunOptFunc (S, &DOptPushPop, 1);
1953         C += RunOptFunc (S, &DOptPrecalc, 1);
1954
1955         Changes += C;
1956
1957     } while (C);
1958
1959     /* Return the number of changes */
1960     return Changes;
1961 }
1962
1963
1964
1965 static unsigned RunOptGroup4 (CodeSeg* S)
1966 /* Run another round of pattern replacements. These are done late, since there
1967  * may be better replacements before.
1968  */
1969 {
1970     unsigned Changes = 0;
1971
1972     /* Repeat some of the steps here */
1973     Changes += RunOptFunc (S, &DOptPush1, 1);
1974     Changes += RunOptFunc (S, &DOptPush2, 1);
1975     Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
1976     Changes += RunOptFunc (S, &DOptTest2, 1);
1977     Changes += RunOptFunc (S, &DOptTransfers2, 1);
1978     Changes += RunOptFunc (S, &DOptLoad2, 1);
1979
1980     /* Return the number of changes */
1981     return Changes;
1982 }
1983
1984
1985
1986 static unsigned RunOptGroup5 (CodeSeg* S)
1987 /* 65C02 specific optimizations. */
1988 {
1989     unsigned Changes = 0;
1990
1991     if (CPUIsets[CPU] & CPU_ISET_65SC02) {
1992         Changes += RunOptFunc (S, &DOpt65C02BitOps, 1);
1993         Changes += RunOptFunc (S, &DOpt65C02Ind, 1);
1994         Changes += RunOptFunc (S, &DOpt65C02Stores, 1);
1995         if (Changes) {
1996             /* The 65C02 replacement codes do often make the use of a register
1997              * value unnecessary, so if we have changes, run another load
1998              * removal pass.
1999              */
2000             Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
2001         }
2002     }
2003
2004     /* Return the number of changes */
2005     return Changes;
2006 }
2007
2008
2009
2010 static unsigned RunOptGroup6 (CodeSeg* S)
2011 /* This one is quite special. It tries to replace "lda (sp),y" by "lda (sp,x)".
2012  * The latter is ony cycle slower, but if we're able to remove the necessary
2013  * load of the Y register, because X is zero anyway, we gain 1 cycle and
2014  * shorten the code by one (transfer) or two bytes (load). So what we do is
2015  * to replace the insns, remove unused loads, and then change back all insns
2016  * where Y is still zero (meaning that the load has not been removed).
2017  */
2018 {
2019     unsigned Changes = 0;
2020
2021     /* This group will only run for a standard 6502, because the 65C02 has a
2022      * better addressing mode that covers this case.
2023      */
2024     if ((CPUIsets[CPU] & CPU_ISET_65SC02) == 0) {
2025         Changes += RunOptFunc (S, &DOptIndLoads1, 1);
2026         Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
2027         Changes += RunOptFunc (S, &DOptIndLoads2, 1);
2028     }
2029
2030     /* Return the number of changes */
2031     return Changes;
2032 }
2033
2034
2035
2036 static unsigned RunOptGroup7 (CodeSeg* S)
2037 /* The last group of optimization steps. Adjust branches, do size optimizations.
2038  */
2039 {
2040     unsigned Changes = 0;
2041     unsigned C;
2042
2043     /* Optimize for size, that is replace operations by shorter ones, even
2044      * if this does hinder further optimizations (no problem since we're
2045      * done soon).
2046      */
2047     C = RunOptFunc (S, &DOptSize1, 1);
2048     if (C) {
2049         Changes += C;
2050         /* Run some optimization passes again, since the size optimizations
2051          * may have opened new oportunities.
2052          */
2053         Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
2054         Changes += RunOptFunc (S, &DOptUnusedStores, 1);
2055         Changes += RunOptFunc (S, &DOptJumpTarget1, 5);
2056         Changes += RunOptFunc (S, &DOptStore5, 1);
2057     }
2058
2059     C = RunOptFunc (S, &DOptSize2, 1);
2060     if (C) {
2061         Changes += C;
2062         /* Run some optimization passes again, since the size optimizations
2063          * may have opened new oportunities.
2064          */
2065         Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
2066         Changes += RunOptFunc (S, &DOptJumpTarget1, 5);
2067         Changes += RunOptFunc (S, &DOptStore5, 1);
2068         Changes += RunOptFunc (S, &DOptTransfers3, 1);
2069     }
2070
2071     /* Adjust branch distances */
2072     Changes += RunOptFunc (S, &DOptBranchDist, 3);
2073
2074     /* Replace conditional branches to RTS. If we had changes, we must run dead
2075      * code elimination again, since the change may have introduced dead code.
2076      */
2077     C = RunOptFunc (S, &DOptRTSJumps2, 1);
2078     Changes += C;
2079     if (C) {
2080         Changes += RunOptFunc (S, &DOptDeadCode, 1);
2081     }
2082
2083     /* Return the number of changes */
2084     return Changes;
2085 }
2086
2087
2088
2089 void RunOpt (CodeSeg* S)
2090 /* Run the optimizer */
2091 {
2092     const char* StatFileName;
2093
2094     /* If we shouldn't run the optimizer, bail out */
2095     if (!S->Optimize) {
2096         return;
2097     }
2098
2099     /* Check if we are requested to write optimizer statistics */
2100     StatFileName = getenv ("CC65_OPTSTATS");
2101     if (StatFileName) {
2102         ReadOptStats (StatFileName);
2103     }
2104
2105     /* Print the name of the function we are working on */
2106     if (S->Func) {
2107         Print (stdout, 1, "Running optimizer for function `%s'\n", S->Func->Name);
2108     } else {
2109         Print (stdout, 1, "Running optimizer for global code segment\n");
2110     }
2111
2112     /* Run groups of optimizations */
2113     RunOptGroup1 (S);
2114     RunOptGroup2 (S);
2115     RunOptGroup3 (S);
2116     RunOptGroup4 (S);
2117     RunOptGroup5 (S);
2118     RunOptGroup6 (S);
2119     RunOptGroup7 (S);
2120
2121     /* Write statistics */
2122     if (StatFileName) {
2123         WriteOptStats (StatFileName);
2124     }
2125 }
2126
2127
2128