]> git.sur5r.net Git - cc65/blob - src/cc65/codeopt.c
4e5b4890a6ca552a7a979a5bfa43ff8dc0bf1941
[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 "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 zp     -> lda #imm
966  *   ldx zp     -> ldx #imm
967  *   ldy zp     -> 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         const char* Arg;
983
984         /* Get next entry and it's input register values */
985         CodeEntry* E = CS_GetEntry (S, I);
986         const RegContents* In = &E->RI->In;
987
988         /* Assume we have no replacement */
989         CodeEntry* X = 0;
990
991         /* Check the instruction */
992         switch (E->OPC) {
993
994             case OP65_DEX:
995                 if (E->RI->In.RegX >= 0) {
996                     Arg = MakeHexArg ((E->RI->In.RegX - 1) & 0xFF);
997                     X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
998                 }
999                 break;
1000
1001             case OP65_DEY:
1002                 if (E->RI->In.RegY >= 0) {
1003                     Arg = MakeHexArg ((E->RI->In.RegY - 1) & 0xFF);
1004                     X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1005                 }
1006                 break;
1007
1008             case OP65_INX:
1009                 if (E->RI->In.RegX >= 0) {
1010                     Arg = MakeHexArg ((E->RI->In.RegX + 1) & 0xFF);
1011                     X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1012                 }
1013                 break;
1014
1015             case OP65_INY:
1016                 if (E->RI->In.RegY >= 0) {
1017                     Arg = MakeHexArg ((E->RI->In.RegY + 1) & 0xFF);
1018                     X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1019                 }
1020                 break;
1021
1022             case OP65_LDA:
1023                 if (E->AM == AM65_ZP) {
1024                     switch (GetKnownReg (E->Use, In)) {
1025                         case REG_TMP1:
1026                             Arg = MakeHexArg (In->Tmp1);
1027                             X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1028                             break;
1029
1030                         case REG_SREG_LO:
1031                             Arg = MakeHexArg (In->SRegLo);
1032                             X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1033                             break;
1034
1035                         case REG_SREG_HI:
1036                             Arg = MakeHexArg (In->SRegHi);
1037                             X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1038                             break;
1039                     }
1040                 }
1041                 break;
1042
1043             case OP65_LDX:
1044                 if (E->AM == AM65_ZP) {
1045                     switch (GetKnownReg (E->Use, In)) {
1046                         case REG_TMP1:
1047                             Arg = MakeHexArg (In->Tmp1);
1048                             X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1049                             break;
1050
1051                         case REG_SREG_LO:
1052                             Arg = MakeHexArg (In->SRegLo);
1053                             X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1054                             break;
1055
1056                         case REG_SREG_HI:
1057                             Arg = MakeHexArg (In->SRegHi);
1058                             X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1059                             break;
1060                     }
1061                 }
1062                 break;
1063
1064             case OP65_LDY:
1065                 if (E->AM == AM65_ZP) {
1066                     switch (GetKnownReg (E->Use, In)) {
1067                         case REG_TMP1:
1068                             Arg = MakeHexArg (In->Tmp1);
1069                             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1070                             break;
1071
1072                         case REG_SREG_LO:
1073                             Arg = MakeHexArg (In->SRegLo);
1074                             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1075                             break;
1076
1077                         case REG_SREG_HI:
1078                             Arg = MakeHexArg (In->SRegHi);
1079                             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1080                             break;
1081                     }
1082                 }
1083                 break;
1084
1085             case OP65_TAX:
1086                 if (E->RI->In.RegA >= 0) {
1087                     Arg = MakeHexArg (In->RegA);
1088                     X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1089                 }
1090                 break;
1091
1092             case OP65_TAY:
1093                 if (E->RI->In.RegA >= 0) {
1094                     Arg = MakeHexArg (In->RegA);
1095                     X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1096                 }
1097                 break;
1098
1099             case OP65_TXA:
1100                 if (E->RI->In.RegX >= 0) {
1101                     Arg = MakeHexArg (In->RegX);
1102                     X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1103                 }
1104                 break;
1105
1106             case OP65_TYA:
1107                 if (E->RI->In.RegY >= 0) {
1108                     Arg = MakeHexArg (In->RegY);
1109                     X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1110                 }
1111                 break;
1112
1113             default:
1114                 /* Avoid gcc warnings */
1115                 break;
1116
1117         }
1118
1119         /* Insert the replacement if we have one */
1120         if (X) {
1121             CS_InsertEntry (S, X, I+1);
1122             CS_DelEntry (S, I);
1123             ++Changes;
1124         }
1125
1126         /* Next entry */
1127         ++I;
1128
1129     }
1130
1131     /* Free register info */
1132     CS_FreeRegInfo (S);
1133
1134     /* Return the number of changes made */
1135     return Changes;
1136 }
1137
1138
1139
1140 /*****************************************************************************/
1141 /*                             Size optimization                             */
1142 /*****************************************************************************/
1143
1144
1145
1146 #if 0
1147 static unsigned OptSize1 (CodeSeg* S)
1148 /* Do size optimization by calling special subroutines that preload registers.
1149  * This routine does not work standalone, it needs a following register load
1150  * removal pass.
1151  */
1152 {
1153     static const char* Func = {
1154         "stax0sp",           /* staxysp, y = 0 */
1155         "addeq0sp",
1156         "ldax0sp",           /* ldaxysp, y = 1 */
1157         "ldeax0sp",          /* ldeaxysp, y = 3 */
1158         "push0",             /* pushax, a = 0, x = 0 */
1159         "pusha0",            /* pushax, x = 0 */
1160         "pushaFF",           /* pushax, x = ff */
1161         "pusha0sp",          /* pushaysp, y = 0 */
1162         "tosadda0",          /* tosaddax, x = 0 */
1163         "tosanda0",          /* tosandax, x = 0 */
1164         "tosdiva0",          /* tosdivax, x = 0 */
1165         "toseqa0",           /* toseqax, x = 0 */
1166         "tosgea0",           /* tosgeax, x = 0 */
1167         "tosgta0",           /* tosgtax, x = 0 */
1168         "tosadd0ax",         /* tosaddeax, sreg = 0 */
1169         "laddeqa",           /* laddeq, sreg = 0, x = 0 */
1170         "laddeq1",           /* laddeq, sreg = 0, x = 0, a = 1 */
1171         "laddeq0sp",         /* laddeqysp, y = 0 */
1172         "tosand0ax",         /* tosandeax, sreg = 0 */
1173         "ldaxi",             /* ldaxidx, y = 1 */
1174         "ldeaxi",            /* ldeaxidx, y = 3 */
1175         "ldeax0sp",          /* ldeaxysp, y = 3 */
1176         "tosdiv0ax",         /* tosdiveax, sreg = 0 */
1177         "toslea0",           /* tosleax, x = 0 */
1178         "tosmod0ax",         /* tosmodeax, sreg = 0 */
1179         "tosmul0ax",         /* tosmuleax, sreg = 0 */
1180         "tosumul0ax",        /* tosumuleax, sreg = 0 */
1181         "tosor0ax",          /* tosoreax, sreg = 0 */
1182         "push0ax",           /* pusheax, sreg = 0 */
1183         "tosrsub0ax",        /* tosrsubeax, sreg = 0 */
1184         "tosshl0ax",         /* tosshleax, sreg = 0 */
1185         "tosasl0ax",         /* tosasleax, sreg = 0 */
1186         "tosshr0ax",         /* tosshreax, sreg = 0 */
1187         "tosasr0ax",         /* tosasreax, sreg = 0 */
1188         "tossub0ax",         /* tossubeax, sreg = 0 */
1189         "lsubeqa",           /* lsubeq, sreg = 0, x = 0 */
1190         "lsubeq1",           /* lsubeq, sreg = 0, x = 0, a = 1 */
1191         "lsubeq0sp",         /* lsubeqysp, y = 0 */
1192         "toslta0",           /* tosltax, x = 0 */
1193         "tosudiv0ax",        /* tosudiveax, sreg = 0 */
1194         "tosumod0ax",        /* tosumodeax, sreg = 0 */
1195         "tosxor0ax",         /* tosxoreax, sreg = 0 */
1196         "tosmoda0",          /* tosmodax, x = 0 */
1197         "tosmula0",          /* tosmulax, x = 0 */
1198         "tosumula0",         /* tosumulax, x = 0 */
1199         "tosnea0",           /* tosneax, x = 0 */
1200         "tosora0",           /* tosorax, x = 0 */
1201         "push1",             /* pushax, x = 0, a = 1 */
1202         "push2",             /* pushax, x = 0, a = 2 */
1203         "push3",             /* pushax, x = 0, a = 3 */
1204         "push4",             /* pushax, x = 0, a = 4 */
1205         "push5",             /* pushax, x = 0, a = 5 */
1206         "push6",             /* pushax, x = 0, a = 6 */
1207         "push7",             /* pushax, x = 0, a = 7 */
1208         "pushc0",            /* pusha, a = 0 */
1209         "pushc1",            /* pusha, a = 1 */
1210         "pushc2",            /* pusha, a = 2 */
1211         "tosrsuba0",         /* tosrsubax, x = 0 */
1212         "tosshla0",          /* tosshlax, x = 0 */
1213         "tosasla0",          /* tosaslax, x = 0 */
1214         "tosshra0",          /* tosshrax, x = 0 */
1215         "tosasra0",          /* tosasrax, x = 0 */
1216         "steax0sp",          /* steaxsp, y = 0 */
1217         "tossuba0",          /* tossubax, x = 0 */
1218         "subeq0sp",          /* subeqysp, y = 0 */
1219         "tosudiva0",         /* tosudivax, x = 0 */
1220         "tosugea0",          /* tosugeax, x = 0 */
1221         "tosugta0",          /* tosugtax, x = 0 */
1222         "tosulea0",          /* tosuleax, x = 0 */
1223         "tosulta0",          /* tosultax, x = 0 */
1224         "tosumoda0",         /* tosumodax, x = 0 */
1225         "tosxora0",          /* tosxorax, x = 0 */
1226     };
1227
1228     unsigned Changes = 0;
1229     unsigned I;
1230
1231     /* Generate register info for the following step */
1232     CS_GenRegInfo (S);
1233
1234     /* Walk over the entries */
1235     I = 0;
1236     while (I < CS_GetEntryCount (S)) {
1237
1238         /* Get next entry */
1239         CodeEntry* E = CS_GetEntry (S, I);
1240
1241         /* Check if it's a subroutine call */
1242         if (E->OPC == OP65_JSR) {
1243
1244             /* Check for any of the known functions */
1245
1246
1247
1248         }
1249
1250         /* Next entry */
1251         ++I;
1252
1253     }
1254
1255     /* Free register info */
1256     CS_FreeRegInfo (S);
1257
1258     /* Return the number of changes made */
1259     return Changes;
1260 }
1261 #endif
1262
1263
1264
1265 static unsigned OptSize2 (CodeSeg* S)
1266 /* Do size optimization by using shorter code sequences, even if this
1267  * introduces relations between instructions. This step must be one of the
1268  * last steps, because it makes further work much more difficult.
1269  */
1270 {
1271     unsigned Changes = 0;
1272     unsigned I;
1273
1274     /* Generate register info for the following step */
1275     CS_GenRegInfo (S);
1276
1277     /* Walk over the entries */
1278     I = 0;
1279     while (I < CS_GetEntryCount (S)) {
1280
1281
1282         /* Get next entry */
1283         CodeEntry* E = CS_GetEntry (S, I);
1284
1285         /* Assume we have no replacement */
1286         CodeEntry* X = 0;
1287
1288         /* Check the instruction */
1289         switch (E->OPC) {
1290
1291             case OP65_LDA:
1292                 if (CE_KnownImm (E)) {
1293                     short Val = (short) E->Num;
1294                     if (Val == E->RI->In.RegX) {
1295                         X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI);
1296                     } else if (Val == E->RI->In.RegY) {
1297                         X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI);
1298                     } else if (E->RI->In.RegA >= 0 && CPU >= CPU_65C02) {
1299                         if (Val == ((E->RI->In.RegA - 1) & 0xFF)) {
1300                             X = NewCodeEntry (OP65_DEA, AM65_IMP, 0, 0, E->LI);
1301                         } else if (Val == ((E->RI->In.RegA + 1) & 0xFF)) {
1302                             X = NewCodeEntry (OP65_INA, AM65_IMP, 0, 0, E->LI);
1303                         }
1304                     }
1305                 }
1306                 break;
1307
1308             case OP65_LDX:
1309                 if (CE_KnownImm (E)) {
1310                     short Val = (short) E->Num;
1311                     if (E->RI->In.RegX >= 0 && Val == ((E->RI->In.RegX - 1) & 0xFF)) {
1312                         X = NewCodeEntry (OP65_DEX, AM65_IMP, 0, 0, E->LI);
1313                     } else if (E->RI->In.RegX >= 0 && Val == ((E->RI->In.RegX + 1) & 0xFF)) {
1314                         X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, E->LI);
1315                     } else if (Val == E->RI->In.RegA) {
1316                         X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI);
1317                     }
1318                 }
1319                 break;
1320
1321             case OP65_LDY:
1322                 if (CE_KnownImm (E)) {
1323                     short Val = (short) E->Num;
1324                     if (E->RI->In.RegY >= 0 && Val == ((E->RI->In.RegY - 1) & 0xFF)) {
1325                         X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, E->LI);
1326                     } else if (E->RI->In.RegY >= 0 && Val == ((E->RI->In.RegY + 1) & 0xFF)) {
1327                         X = NewCodeEntry (OP65_INY, AM65_IMP, 0, 0, E->LI);
1328                     } else if (Val == E->RI->In.RegA) {
1329                         X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI);
1330                     }
1331                 }
1332                 break;
1333
1334             default:
1335                 /* Avoid gcc warnings */
1336                 break;
1337
1338         }
1339
1340         /* Insert the replacement if we have one */
1341         if (X) {
1342             CS_InsertEntry (S, X, I+1);
1343             CS_DelEntry (S, I);
1344             ++Changes;
1345         }
1346
1347         /* Next entry */
1348         ++I;
1349
1350     }
1351
1352     /* Free register info */
1353     CS_FreeRegInfo (S);
1354
1355     /* Return the number of changes made */
1356     return Changes;
1357 }
1358
1359
1360
1361 /*****************************************************************************/
1362 /*                              struct OptFunc                               */
1363 /*****************************************************************************/
1364
1365
1366
1367 typedef struct OptFunc OptFunc;
1368 struct OptFunc {
1369     unsigned       (*Func) (CodeSeg*);  /* Optimizer function */
1370     const char*    Name;                /* Name of the function/group */
1371     unsigned       CodeSizeFactor;      /* Code size factor for this opt func */
1372     unsigned long  TotalRuns;           /* Total number of runs */
1373     unsigned long  LastRuns;            /* Last number of runs */
1374     unsigned long  TotalChanges;        /* Total number of changes */
1375     unsigned long  LastChanges;         /* Last number of changes */
1376     char           Disabled;            /* True if function disabled */
1377 };
1378
1379
1380
1381 /*****************************************************************************/
1382 /*                                   Code                                    */
1383 /*****************************************************************************/
1384
1385
1386
1387 /* A list of all the function descriptions */
1388 static OptFunc DOpt65C02BitOps  = { Opt65C02BitOps,  "Opt65C02BitOps",   66, 0, 0, 0, 0, 0 };
1389 static OptFunc DOpt65C02Ind     = { Opt65C02Ind,     "Opt65C02Ind",     100, 0, 0, 0, 0, 0 };
1390 static OptFunc DOpt65C02Stores  = { Opt65C02Stores,  "Opt65C02Stores",  100, 0, 0, 0, 0, 0 };
1391 static OptFunc DOptAdd1         = { OptAdd1,         "OptAdd1",         125, 0, 0, 0, 0, 0 };
1392 static OptFunc DOptAdd2         = { OptAdd2,         "OptAdd2",         200, 0, 0, 0, 0, 0 };
1393 static OptFunc DOptAdd3         = { OptAdd3,         "OptAdd3",          40, 0, 0, 0, 0, 0 };
1394 static OptFunc DOptBoolTrans    = { OptBoolTrans,    "OptBoolTrans",    100, 0, 0, 0, 0, 0 };
1395 static OptFunc DOptBranchDist   = { OptBranchDist,   "OptBranchDist",     0, 0, 0, 0, 0, 0 };
1396 static OptFunc DOptCmp1         = { OptCmp1,         "OptCmp1",          85, 0, 0, 0, 0, 0 };
1397 static OptFunc DOptCmp2         = { OptCmp2,         "OptCmp2",          75, 0, 0, 0, 0, 0 };
1398 static OptFunc DOptCmp3         = { OptCmp3,         "OptCmp3",          75, 0, 0, 0, 0, 0 };
1399 static OptFunc DOptCmp4         = { OptCmp4,         "OptCmp4",         100, 0, 0, 0, 0, 0 };
1400 static OptFunc DOptCmp5         = { OptCmp5,         "OptCmp5",         100, 0, 0, 0, 0, 0 };
1401 static OptFunc DOptCmp6         = { OptCmp6,         "OptCmp6",          85, 0, 0, 0, 0, 0 };
1402 static OptFunc DOptCmp7         = { OptCmp7,         "OptCmp7",          50, 0, 0, 0, 0, 0 };
1403 static OptFunc DOptCondBranches = { OptCondBranches, "OptCondBranches",  80, 0, 0, 0, 0, 0 };
1404 static OptFunc DOptDeadCode     = { OptDeadCode,     "OptDeadCode",     100, 0, 0, 0, 0, 0 };
1405 static OptFunc DOptDeadJumps    = { OptDeadJumps,    "OptDeadJumps",    100, 0, 0, 0, 0, 0 };
1406 static OptFunc DOptDecouple     = { OptDecouple,     "OptDecouple",     100, 0, 0, 0, 0, 0 };
1407 static OptFunc DOptDupLoads     = { OptDupLoads,     "OptDupLoads",       0, 0, 0, 0, 0, 0 };
1408 static OptFunc DOptJumpCascades = { OptJumpCascades, "OptJumpCascades", 100, 0, 0, 0, 0, 0 };
1409 static OptFunc DOptJumpTarget   = { OptJumpTarget,   "OptJumpTarget",   100, 0, 0, 0, 0, 0 };
1410 static OptFunc DOptRTS          = { OptRTS,          "OptRTS",          100, 0, 0, 0, 0, 0 };
1411 static OptFunc DOptRTSJumps1    = { OptRTSJumps1,    "OptRTSJumps1",    100, 0, 0, 0, 0, 0 };
1412 static OptFunc DOptRTSJumps2    = { OptRTSJumps2,    "OptRTSJumps2",    100, 0, 0, 0, 0, 0 };
1413 static OptFunc DOptNegA1        = { OptNegA1,        "OptNegA1",        100, 0, 0, 0, 0, 0 };
1414 static OptFunc DOptNegA2        = { OptNegA2,        "OptNegA2",        100, 0, 0, 0, 0, 0 };
1415 static OptFunc DOptNegAX1       = { OptNegAX1,       "OptNegAX1",       100, 0, 0, 0, 0, 0 };
1416 static OptFunc DOptNegAX2       = { OptNegAX2,       "OptNegAX2",       100, 0, 0, 0, 0, 0 };
1417 static OptFunc DOptNegAX3       = { OptNegAX3,       "OptNegAX3",       100, 0, 0, 0, 0, 0 };
1418 static OptFunc DOptNegAX4       = { OptNegAX4,       "OptNegAX4",       100, 0, 0, 0, 0, 0 };
1419 static OptFunc DOptPtrLoad1     = { OptPtrLoad1,     "OptPtrLoad1",     100, 0, 0, 0, 0, 0 };
1420 static OptFunc DOptPtrLoad2     = { OptPtrLoad2,     "OptPtrLoad2",     100, 0, 0, 0, 0, 0 };
1421 static OptFunc DOptPtrLoad3     = { OptPtrLoad3,     "OptPtrLoad3",     100, 0, 0, 0, 0, 0 };
1422 static OptFunc DOptPtrLoad4     = { OptPtrLoad4,     "OptPtrLoad4",     100, 0, 0, 0, 0, 0 };
1423 static OptFunc DOptPtrLoad5     = { OptPtrLoad5,     "OptPtrLoad5",     100, 0, 0, 0, 0, 0 };
1424 static OptFunc DOptPtrLoad6     = { OptPtrLoad6,     "OptPtrLoad6",     100, 0, 0, 0, 0, 0 };
1425 static OptFunc DOptPtrStore1    = { OptPtrStore1,    "OptPtrStore1",    100, 0, 0, 0, 0, 0 };
1426 static OptFunc DOptPtrStore2    = { OptPtrStore2,    "OptPtrStore2",    100, 0, 0, 0, 0, 0 };
1427 static OptFunc DOptPush1        = { OptPush1,        "OptPush1",         65, 0, 0, 0, 0, 0 };
1428 static OptFunc DOptPushPop      = { OptPushPop,      "OptPushPop",        0, 0, 0, 0, 0, 0 };
1429 static OptFunc DOptShift1       = { OptShift1,       "OptShift1",       100, 0, 0, 0, 0, 0 };
1430 static OptFunc DOptShift2       = { OptShift2,       "OptShift2",       100, 0, 0, 0, 0, 0 };
1431 /*static OptFunc DOptSize1        = { OptSize1,        "OptSize1",        100, 0, 0, 0, 0, 0 };*/
1432 static OptFunc DOptSize2        = { OptSize2,        "OptSize2",        100, 0, 0, 0, 0, 0 };
1433 static OptFunc DOptStackOps     = { OptStackOps,     "OptStackOps",     100, 0, 0, 0, 0, 0 };
1434 static OptFunc DOptStoreLoad    = { OptStoreLoad,    "OptStoreLoad",      0, 0, 0, 0, 0, 0 };
1435 static OptFunc DOptSub1         = { OptSub1,         "OptSub1",         100, 0, 0, 0, 0, 0 };
1436 static OptFunc DOptSub2         = { OptSub2,         "OptSub2",         100, 0, 0, 0, 0, 0 };
1437 static OptFunc DOptTest1        = { OptTest1,        "OptTest1",        100, 0, 0, 0, 0, 0 };
1438 static OptFunc DOptTransfers    = { OptTransfers,    "OptTransfers",      0, 0, 0, 0, 0, 0 };
1439 static OptFunc DOptUnusedLoads  = { OptUnusedLoads,  "OptUnusedLoads",    0, 0, 0, 0, 0, 0 };
1440 static OptFunc DOptUnusedStores = { OptUnusedStores, "OptUnusedStores",   0, 0, 0, 0, 0, 0 };
1441
1442
1443 /* Table containing all the steps in alphabetical order */
1444 static OptFunc* OptFuncs[] = {
1445     &DOpt65C02BitOps,
1446     &DOpt65C02Ind,
1447     &DOpt65C02Stores,
1448     &DOptAdd1,
1449     &DOptAdd2,
1450     &DOptAdd3,
1451     &DOptBoolTrans,
1452     &DOptBranchDist,
1453     &DOptCmp1,
1454     &DOptCmp2,
1455     &DOptCmp3,
1456     &DOptCmp4,
1457     &DOptCmp5,
1458     &DOptCmp6,
1459     &DOptCmp7,
1460     &DOptCondBranches,
1461     &DOptDeadCode,
1462     &DOptDeadJumps,
1463     &DOptDecouple,
1464     &DOptDupLoads,
1465     &DOptJumpCascades,
1466     &DOptJumpTarget,
1467     &DOptNegA1,
1468     &DOptNegA2,
1469     &DOptNegAX1,
1470     &DOptNegAX2,
1471     &DOptNegAX3,
1472     &DOptNegAX4,
1473     &DOptPtrLoad1,
1474     &DOptPtrLoad2,
1475     &DOptPtrLoad3,
1476     &DOptPtrLoad4,
1477     &DOptPtrLoad5,
1478     &DOptPtrLoad6,
1479     &DOptPtrStore1,
1480     &DOptPtrStore2,
1481     &DOptPush1,
1482     &DOptPushPop,
1483     &DOptRTS,
1484     &DOptRTSJumps1,
1485     &DOptRTSJumps2,
1486     &DOptShift1,
1487     &DOptShift2,
1488     /*&DOptSize1,*/
1489     &DOptSize2,
1490     &DOptStackOps,
1491     &DOptStoreLoad,
1492     &DOptSub1,
1493     &DOptSub2,
1494     &DOptTest1,
1495     &DOptTransfers,
1496     &DOptUnusedLoads,
1497     &DOptUnusedStores,
1498 };
1499 #define OPTFUNC_COUNT  (sizeof(OptFuncs) / sizeof(OptFuncs[0]))
1500
1501
1502
1503 static int CmpOptStep (const void* Key, const void* Func)
1504 /* Compare function for bsearch */
1505 {
1506     return strcmp (Key, (*(const OptFunc**)Func)->Name);
1507 }
1508
1509
1510
1511 static OptFunc* FindOptFunc (const char* Name)
1512 /* Find an optimizer step by name in the table and return a pointer. Return
1513  * NULL if no such step is found.
1514  */
1515 {
1516     /* Search for the function in the list */
1517     OptFunc** O = bsearch (Name, OptFuncs, OPTFUNC_COUNT, sizeof (OptFuncs[0]), CmpOptStep);
1518     return O? *O : 0;
1519 }
1520
1521
1522
1523 static OptFunc* GetOptFunc (const char* Name)
1524 /* Find an optimizer step by name in the table and return a pointer. Print an
1525  * error and call AbEnd if not found.
1526  */
1527 {
1528     /* Search for the function in the list */
1529     OptFunc* F = FindOptFunc (Name);
1530     if (F == 0) {
1531         /* Not found */
1532         AbEnd ("Optimization step `%s' not found", Name);
1533     }
1534     return F;
1535 }
1536
1537
1538
1539 void DisableOpt (const char* Name)
1540 /* Disable the optimization with the given name */
1541 {
1542     if (strcmp (Name, "any") == 0) {
1543         unsigned I;
1544         for (I = 0; I < OPTFUNC_COUNT; ++I) {
1545             OptFuncs[I]->Disabled = 1;
1546         }
1547     } else {
1548         GetOptFunc(Name)->Disabled = 1;
1549     }
1550 }
1551
1552
1553
1554 void EnableOpt (const char* Name)
1555 /* Enable the optimization with the given name */
1556 {
1557     if (strcmp (Name, "any") == 0) {
1558         unsigned I;
1559         for (I = 0; I < OPTFUNC_COUNT; ++I) {
1560             OptFuncs[I]->Disabled = 0;
1561         }
1562     } else {
1563         GetOptFunc(Name)->Disabled = 0;
1564     }
1565 }
1566
1567
1568
1569 void ListOptSteps (FILE* F)
1570 /* List all optimization steps */
1571 {
1572     unsigned I;
1573     for (I = 0; I < OPTFUNC_COUNT; ++I) {
1574         fprintf (F, "%s\n", OptFuncs[I]->Name);
1575     }
1576 }
1577
1578
1579
1580 static void ReadOptStats (const char* Name)
1581 /* Read the optimizer statistics file */
1582 {
1583     char Buf [256];
1584     unsigned Lines;
1585
1586     /* Try to open the file */
1587     FILE* F = fopen (Name, "r");
1588     if (F == 0) {
1589         /* Ignore the error */
1590         return;
1591     }
1592
1593     /* Read and parse the lines */
1594     Lines = 0;
1595     while (fgets (Buf, sizeof (Buf), F) != 0) {
1596
1597         char* B;
1598         unsigned Len;
1599         OptFunc* Func;
1600
1601         /* Fields */
1602         char Name[32];
1603         unsigned long  TotalRuns;
1604         unsigned long  TotalChanges;
1605
1606         /* Count lines */
1607         ++Lines;
1608
1609         /* Remove trailing white space including the line terminator */
1610         B = Buf;
1611         Len = strlen (B);
1612         while (Len > 0 && IsSpace (B[Len-1])) {
1613             --Len;
1614         }
1615         B[Len] = '\0';
1616
1617         /* Remove leading whitespace */
1618         while (IsSpace (*B)) {
1619             ++B;
1620         }
1621
1622         /* Check for empty and comment lines */
1623         if (*B == '\0' || *B == ';' || *B == '#') {
1624             continue;
1625         }
1626
1627         /* Parse the line */
1628         if (sscanf (B, "%31s %lu %*u %lu %*u", Name, &TotalRuns, &TotalChanges) != 3) {
1629             /* Syntax error */
1630             continue;
1631         }
1632
1633         /* Search for the optimizer step. */
1634         Func = FindOptFunc (Name);
1635         if (Func == 0) {
1636             /* Not found */
1637             continue;
1638         }
1639
1640         /* Found the step, set the fields */
1641         Func->TotalRuns    = TotalRuns;
1642         Func->TotalChanges = TotalChanges;
1643
1644     }
1645
1646     /* Close the file, ignore errors here. */
1647     fclose (F);
1648 }
1649
1650
1651
1652 static void WriteOptStats (const char* Name)
1653 /* Write the optimizer statistics file */
1654 {
1655     unsigned I;
1656
1657     /* Try to open the file */
1658     FILE* F = fopen (Name, "w");
1659     if (F == 0) {
1660         /* Ignore the error */
1661         return;
1662     }
1663
1664     /* Write a header */
1665     fprintf (F,
1666              "; Optimizer           Total  Last   Total  Last\n"
1667              ";   Step              Runs   Runs    Chg   Chg\n");
1668
1669
1670     /* Write the data */
1671     for (I = 0; I < OPTFUNC_COUNT; ++I) {
1672         const OptFunc* O = OptFuncs[I];
1673         fprintf (F,
1674                  "%-20s %6lu %6lu %6lu %6lu\n",
1675                  O->Name,
1676                  O->TotalRuns,
1677                  O->LastRuns,
1678                  O->TotalChanges,
1679                  O->LastChanges);
1680     }
1681
1682     /* Close the file, ignore errors here. */
1683     fclose (F);
1684 }
1685
1686
1687
1688 static unsigned RunOptFunc (CodeSeg* S, OptFunc* F, unsigned Max)
1689 /* Run one optimizer function Max times or until there are no more changes */
1690 {
1691     unsigned Changes, C;
1692
1693     /* Don't run the function if it is disabled or if it is prohibited by the
1694      * code size factor
1695      */
1696     if (F->Disabled || CodeSizeFactor < F->CodeSizeFactor) {
1697         return 0;
1698     }
1699
1700     /* Run this until there are no more changes */
1701     Changes = 0;
1702     do {
1703
1704         /* Run the function */
1705         C = F->Func (S);
1706         Changes += C;
1707
1708         /* Do statistics */
1709         ++F->TotalRuns;
1710         ++F->LastRuns;
1711         F->TotalChanges += C;
1712         F->LastChanges  += C;
1713
1714     } while (--Max && C > 0);
1715
1716     /* Return the number of changes */
1717     return Changes;
1718 }
1719
1720
1721
1722 static unsigned RunOptGroup1 (CodeSeg* S)
1723 /* Run the first group of optimization steps. These steps translate known
1724  * patterns emitted by the code generator into more optimal patterns. Order
1725  * of the steps is important, because some of the steps done earlier cover
1726  * the same patterns as later steps as subpatterns.
1727  */
1728 {
1729     unsigned Changes = 0;
1730
1731     Changes += RunOptFunc (S, &DOptPtrStore1, 1);
1732     Changes += RunOptFunc (S, &DOptPtrStore2, 1);
1733     Changes += RunOptFunc (S, &DOptPtrLoad1, 1);
1734     Changes += RunOptFunc (S, &DOptPtrLoad2, 1);
1735     Changes += RunOptFunc (S, &DOptPtrLoad3, 1);
1736     Changes += RunOptFunc (S, &DOptPtrLoad4, 1);
1737     Changes += RunOptFunc (S, &DOptPtrLoad5, 1);
1738     Changes += RunOptFunc (S, &DOptNegAX1, 1);
1739     Changes += RunOptFunc (S, &DOptNegAX2, 1);
1740     Changes += RunOptFunc (S, &DOptNegAX3, 1);
1741     Changes += RunOptFunc (S, &DOptNegAX4, 1);
1742     Changes += RunOptFunc (S, &DOptAdd1, 1);
1743     Changes += RunOptFunc (S, &DOptAdd2, 1);
1744     Changes += RunOptFunc (S, &DOptShift1, 1);
1745     Changes += RunOptFunc (S, &DOptShift2, 1);
1746
1747     /* Return the number of changes */
1748     return Changes;
1749 }
1750
1751
1752
1753 static unsigned RunOptGroup2 (CodeSeg* S)
1754 /* Run one group of optimization steps. This step involves just decoupling
1755  * instructions by replacing them by instructions that do not depend on
1756  * previous instructions. This makes it easier to find instructions that
1757  * aren't used.
1758  */
1759 {
1760     unsigned Changes = 0;
1761
1762     Changes += RunOptFunc (S, &DOptDecouple, 1);
1763
1764     /* Return the number of changes */
1765     return Changes;
1766 }
1767
1768
1769
1770 static unsigned RunOptGroup3 (CodeSeg* S)
1771 /* Run one group of optimization steps. These steps depend on each other,
1772  * that means that one step may allow another step to do additional work,
1773  * so we will repeat the steps as long as we see any changes.
1774  */
1775 {
1776     unsigned Changes, C;
1777
1778     Changes = 0;
1779     do {
1780         C = 0;
1781
1782         C += RunOptFunc (S, &DOptPtrLoad6, 1);
1783         C += RunOptFunc (S, &DOptNegA1, 1);
1784         C += RunOptFunc (S, &DOptNegA2, 1);
1785         C += RunOptFunc (S, &DOptSub1, 1);
1786         C += RunOptFunc (S, &DOptSub2, 1);
1787         C += RunOptFunc (S, &DOptAdd3, 1);
1788         C += RunOptFunc (S, &DOptStackOps, 1);
1789         C += RunOptFunc (S, &DOptJumpCascades, 1);
1790         C += RunOptFunc (S, &DOptDeadJumps, 1);
1791         C += RunOptFunc (S, &DOptRTS, 1);
1792         C += RunOptFunc (S, &DOptDeadCode, 1);
1793         C += RunOptFunc (S, &DOptJumpTarget, 1);
1794         C += RunOptFunc (S, &DOptCondBranches, 1);
1795         C += RunOptFunc (S, &DOptRTSJumps1, 1);
1796         C += RunOptFunc (S, &DOptBoolTrans, 1);
1797         C += RunOptFunc (S, &DOptCmp1, 1);
1798         C += RunOptFunc (S, &DOptCmp2, 1);
1799         C += RunOptFunc (S, &DOptCmp3, 1);
1800         C += RunOptFunc (S, &DOptCmp4, 1);
1801         C += RunOptFunc (S, &DOptCmp5, 1);
1802         C += RunOptFunc (S, &DOptCmp6, 1);
1803         C += RunOptFunc (S, &DOptCmp7, 1);
1804         C += RunOptFunc (S, &DOptTest1, 1);
1805         C += RunOptFunc (S, &DOptUnusedLoads, 1);
1806         C += RunOptFunc (S, &DOptUnusedStores, 1);
1807         C += RunOptFunc (S, &DOptDupLoads, 1);
1808         C += RunOptFunc (S, &DOptStoreLoad, 1);
1809         C += RunOptFunc (S, &DOptTransfers, 1);
1810         C += RunOptFunc (S, &DOptPushPop, 1);
1811
1812         Changes += C;
1813
1814     } while (C);
1815
1816     /* Return the number of changes */
1817     return Changes;
1818 }
1819
1820
1821
1822 static unsigned RunOptGroup4 (CodeSeg* S)
1823 /* 65C02 specific optimizations. */
1824 {
1825     unsigned Changes = 0;
1826
1827     if (CPU >= CPU_65C02) {
1828         Changes += RunOptFunc (S, &DOpt65C02BitOps, 1);
1829         Changes += RunOptFunc (S, &DOpt65C02Ind, 1);
1830         Changes += RunOptFunc (S, &DOpt65C02Stores, 1);
1831         if (Changes) {
1832             /* The 65C02 replacement codes do often make the use of a register
1833              * value unnecessary, so if we have changes, run another load
1834              * removal pass.
1835              */
1836             Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
1837         }
1838     }
1839
1840     /* Return the number of changes */
1841     return Changes;
1842 }
1843
1844
1845
1846 static unsigned RunOptGroup5 (CodeSeg* S)
1847 /* Run another round of pattern replacements. These are done late, since there
1848  * may be better replacements before.
1849  */
1850 {
1851     unsigned Changes = 0;
1852
1853     Changes += RunOptFunc (S, &DOptPush1, 1);
1854
1855     /* Return the number of changes */
1856     return Changes;
1857 }
1858
1859
1860
1861 static unsigned RunOptGroup6 (CodeSeg* S)
1862 /* The last group of optimization steps. Adjust branches, do size optimizations.
1863  */
1864 {
1865     unsigned Changes = 0;
1866     unsigned C;
1867
1868     /* Optimize for size, that is replace operations by shorter ones, even
1869      * if this does hinder further optimizations (no problem since we're
1870      * done soon).
1871      */
1872     Changes += RunOptFunc (S, &DOptSize2, 1);
1873
1874     /* Run the jump target optimization again, since the size optimization
1875      * above may have opened new oportunities.
1876      */
1877     Changes += RunOptFunc (S, &DOptJumpTarget, 5);
1878
1879     /* Adjust branch distances */
1880     Changes += RunOptFunc (S, &DOptBranchDist, 3);
1881
1882     /* Replace conditional branches to RTS. If we had changes, we must run dead
1883      * code elimination again, since the change may have introduced dead code.
1884      */
1885     C = RunOptFunc (S, &DOptRTSJumps2, 1);
1886     Changes += C;
1887     if (C) {
1888         Changes += RunOptFunc (S, &DOptDeadCode, 1);
1889     }
1890
1891     /* Return the number of changes */
1892     return Changes;
1893 }
1894
1895
1896
1897 void RunOpt (CodeSeg* S)
1898 /* Run the optimizer */
1899 {
1900     const char* StatFileName;
1901
1902     /* If we shouldn't run the optimizer, bail out */
1903     if (!Optimize) {
1904         return;
1905     }
1906
1907     /* Check if we are requested to write optimizer statistics */
1908     StatFileName = getenv ("CC65_OPTSTATS");
1909     if (StatFileName) {
1910         ReadOptStats (StatFileName);
1911     }
1912
1913     /* Print the name of the function we are working on */
1914     if (S->Func) {
1915         Print (stdout, 1, "Running optimizer for function `%s'\n", S->Func->Name);
1916     } else {
1917         Print (stdout, 1, "Running optimizer for global code segment\n");
1918     }
1919
1920     /* Run groups of optimizations */
1921     RunOptGroup1 (S);
1922     RunOptGroup2 (S);
1923     RunOptGroup3 (S);
1924     RunOptGroup4 (S);
1925     RunOptGroup5 (S);
1926     RunOptGroup6 (S);
1927
1928     /* Write statistics */
1929     if (StatFileName) {
1930         WriteOptStats (StatFileName);
1931     }
1932 }
1933
1934
1935