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