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