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