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