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