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