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