]> git.sur5r.net Git - cc65/blob - src/cc65/codeopt.c
Use a new specialized multiply routines
[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",          60, 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 DOptShift1       = { OptShift1,       "OptShift1",       100, 0, 0, 0, 0, 0 };
1429 static OptFunc DOptShift2       = { OptShift2,       "OptShift2",       100, 0, 0, 0, 0, 0 };
1430 /*static OptFunc DOptSize1        = { OptSize1,        "OptSize1",        100, 0, 0, 0, 0, 0 };*/
1431 static OptFunc DOptSize2        = { OptSize2,        "OptSize2",        100, 0, 0, 0, 0, 0 };
1432 static OptFunc DOptStackOps     = { OptStackOps,     "OptStackOps",     100, 0, 0, 0, 0, 0 };
1433 static OptFunc DOptStoreLoad    = { OptStoreLoad,    "OptStoreLoad",      0, 0, 0, 0, 0, 0 };
1434 static OptFunc DOptSub1         = { OptSub1,         "OptSub1",         100, 0, 0, 0, 0, 0 };
1435 static OptFunc DOptSub2         = { OptSub2,         "OptSub2",         100, 0, 0, 0, 0, 0 };
1436 static OptFunc DOptTest1        = { OptTest1,        "OptTest1",        100, 0, 0, 0, 0, 0 };
1437 static OptFunc DOptTransfers    = { OptTransfers,    "OptTransfers",      0, 0, 0, 0, 0, 0 };
1438 static OptFunc DOptUnusedLoads  = { OptUnusedLoads,  "OptUnusedLoads",    0, 0, 0, 0, 0, 0 };
1439 static OptFunc DOptUnusedStores = { OptUnusedStores, "OptUnusedStores",   0, 0, 0, 0, 0, 0 };
1440
1441
1442 /* Table containing all the steps in alphabetical order */
1443 static OptFunc* OptFuncs[] = {
1444     &DOpt65C02BitOps,
1445     &DOpt65C02Ind,
1446     &DOpt65C02Stores,
1447     &DOptAdd1,
1448     &DOptAdd2,
1449     &DOptAdd3,
1450     &DOptBoolTrans,
1451     &DOptBranchDist,
1452     &DOptCmp1,
1453     &DOptCmp2,
1454     &DOptCmp3,
1455     &DOptCmp4,
1456     &DOptCmp5,
1457     &DOptCmp6,
1458     &DOptCmp7,
1459     &DOptCondBranches,
1460     &DOptDeadCode,
1461     &DOptDeadJumps,
1462     &DOptDecouple,
1463     &DOptDupLoads,
1464     &DOptJumpCascades,
1465     &DOptJumpTarget,
1466     &DOptNegA1,
1467     &DOptNegA2,
1468     &DOptNegAX1,
1469     &DOptNegAX2,
1470     &DOptNegAX3,
1471     &DOptNegAX4,
1472     &DOptPtrLoad1,
1473     &DOptPtrLoad2,
1474     &DOptPtrLoad3,
1475     &DOptPtrLoad4,
1476     &DOptPtrLoad5,
1477     &DOptPtrLoad6,
1478     &DOptPtrStore1,
1479     &DOptPtrStore2,
1480     &DOptPush1,
1481     &DOptRTS,
1482     &DOptRTSJumps1,
1483     &DOptRTSJumps2,
1484     &DOptShift1,
1485     &DOptShift2,
1486     /*&DOptSize1,*/
1487     &DOptSize2,
1488     &DOptStackOps,
1489     &DOptStoreLoad,
1490     &DOptSub1,
1491     &DOptSub2,
1492     &DOptTest1,
1493     &DOptTransfers,
1494     &DOptUnusedLoads,
1495     &DOptUnusedStores,
1496 };
1497 #define OPTFUNC_COUNT  (sizeof(OptFuncs) / sizeof(OptFuncs[0]))
1498
1499
1500
1501 static int CmpOptStep (const void* Key, const void* Func)
1502 /* Compare function for bsearch */
1503 {
1504     return strcmp (Key, (*(const OptFunc**)Func)->Name);
1505 }
1506
1507
1508
1509 static OptFunc* FindOptFunc (const char* Name)
1510 /* Find an optimizer step by name in the table and return a pointer. Return
1511  * NULL if no such step is found.
1512  */
1513 {
1514     /* Search for the function in the list */
1515     OptFunc** O = bsearch (Name, OptFuncs, OPTFUNC_COUNT, sizeof (OptFuncs[0]), CmpOptStep);
1516     return O? *O : 0;
1517 }
1518
1519
1520
1521 static OptFunc* GetOptFunc (const char* Name)
1522 /* Find an optimizer step by name in the table and return a pointer. Print an
1523  * error and call AbEnd if not found.
1524  */
1525 {
1526     /* Search for the function in the list */
1527     OptFunc* F = FindOptFunc (Name);
1528     if (F == 0) {
1529         /* Not found */
1530         AbEnd ("Optimization step `%s' not found", Name);
1531     }
1532     return F;
1533 }
1534
1535
1536
1537 void DisableOpt (const char* Name)
1538 /* Disable the optimization with the given name */
1539 {
1540     if (strcmp (Name, "any") == 0) {
1541         unsigned I;
1542         for (I = 0; I < OPTFUNC_COUNT; ++I) {
1543             OptFuncs[I]->Disabled = 1;
1544         }
1545     } else {
1546         GetOptFunc(Name)->Disabled = 1;
1547     }
1548 }
1549
1550
1551
1552 void EnableOpt (const char* Name)
1553 /* Enable the optimization with the given name */
1554 {
1555     if (strcmp (Name, "any") == 0) {
1556         unsigned I;
1557         for (I = 0; I < OPTFUNC_COUNT; ++I) {
1558             OptFuncs[I]->Disabled = 0;
1559         }
1560     } else {
1561         GetOptFunc(Name)->Disabled = 0;
1562     }
1563 }
1564
1565
1566
1567 void ListOptSteps (FILE* F)
1568 /* List all optimization steps */
1569 {
1570     unsigned I;
1571     for (I = 0; I < OPTFUNC_COUNT; ++I) {
1572         fprintf (F, "%s\n", OptFuncs[I]->Name);
1573     }
1574 }
1575
1576
1577
1578 static void ReadOptStats (const char* Name)
1579 /* Read the optimizer statistics file */
1580 {
1581     char Buf [256];
1582     unsigned Lines;
1583
1584     /* Try to open the file */
1585     FILE* F = fopen (Name, "r");
1586     if (F == 0) {
1587         /* Ignore the error */
1588         return;
1589     }
1590
1591     /* Read and parse the lines */
1592     Lines = 0;
1593     while (fgets (Buf, sizeof (Buf), F) != 0) {
1594
1595         char* B;
1596         unsigned Len;
1597         OptFunc* Func;
1598
1599         /* Fields */
1600         char Name[32];
1601         unsigned long  TotalRuns;
1602         unsigned long  TotalChanges;
1603
1604         /* Count lines */
1605         ++Lines;
1606
1607         /* Remove trailing white space including the line terminator */
1608         B = Buf;
1609         Len = strlen (B);
1610         while (Len > 0 && IsSpace (B[Len-1])) {
1611             --Len;
1612         }
1613         B[Len] = '\0';
1614
1615         /* Remove leading whitespace */
1616         while (IsSpace (*B)) {
1617             ++B;
1618         }
1619
1620         /* Check for empty and comment lines */
1621         if (*B == '\0' || *B == ';' || *B == '#') {
1622             continue;
1623         }
1624
1625         /* Parse the line */
1626         if (sscanf (B, "%31s %lu %*u %lu %*u", Name, &TotalRuns, &TotalChanges) != 3) {
1627             /* Syntax error */
1628             continue;
1629         }
1630
1631         /* Search for the optimizer step. */
1632         Func = FindOptFunc (Name);
1633         if (Func == 0) {
1634             /* Not found */
1635             continue;
1636         }
1637
1638         /* Found the step, set the fields */
1639         Func->TotalRuns    = TotalRuns;
1640         Func->TotalChanges = TotalChanges;
1641
1642     }
1643
1644     /* Close the file, ignore errors here. */
1645     fclose (F);
1646 }
1647
1648
1649
1650 static void WriteOptStats (const char* Name)
1651 /* Write the optimizer statistics file */
1652 {
1653     unsigned I;
1654
1655     /* Try to open the file */
1656     FILE* F = fopen (Name, "w");
1657     if (F == 0) {
1658         /* Ignore the error */
1659         return;
1660     }
1661
1662     /* Write a header */
1663     fprintf (F,
1664              "; Optimizer           Total  Last   Total  Last\n"
1665              ";   Step              Runs   Runs    Chg   Chg\n");
1666
1667
1668     /* Write the data */
1669     for (I = 0; I < OPTFUNC_COUNT; ++I) {
1670         const OptFunc* O = OptFuncs[I];
1671         fprintf (F,
1672                  "%-20s %6lu %6lu %6lu %6lu\n",
1673                  O->Name,
1674                  O->TotalRuns,
1675                  O->LastRuns,
1676                  O->TotalChanges,
1677                  O->LastChanges);
1678     }
1679
1680     /* Close the file, ignore errors here. */
1681     fclose (F);
1682 }
1683
1684
1685
1686 static unsigned RunOptFunc (CodeSeg* S, OptFunc* F, unsigned Max)
1687 /* Run one optimizer function Max times or until there are no more changes */
1688 {
1689     unsigned Changes, C;
1690
1691     /* Don't run the function if it is disabled or if it is prohibited by the
1692      * code size factor
1693      */
1694     if (F->Disabled || CodeSizeFactor < F->CodeSizeFactor) {
1695         return 0;
1696     }
1697
1698     /* Run this until there are no more changes */
1699     Changes = 0;
1700     do {
1701
1702         /* Run the function */
1703         C = F->Func (S);
1704         Changes += C;
1705
1706         /* Do statistics */
1707         ++F->TotalRuns;
1708         ++F->LastRuns;
1709         F->TotalChanges += C;
1710         F->LastChanges  += C;
1711
1712     } while (--Max && C > 0);
1713
1714     /* Return the number of changes */
1715     return Changes;
1716 }
1717
1718
1719
1720 static unsigned RunOptGroup1 (CodeSeg* S)
1721 /* Run the first group of optimization steps. These steps translate known
1722  * patterns emitted by the code generator into more optimal patterns. Order
1723  * of the steps is important, because some of the steps done earlier cover
1724  * the same patterns as later steps as subpatterns.
1725  */
1726 {
1727     unsigned Changes = 0;
1728
1729     Changes += RunOptFunc (S, &DOptPtrStore1, 1);
1730     Changes += RunOptFunc (S, &DOptPtrStore2, 1);
1731     Changes += RunOptFunc (S, &DOptPtrLoad1, 1);
1732     Changes += RunOptFunc (S, &DOptPtrLoad2, 1);
1733     Changes += RunOptFunc (S, &DOptPtrLoad3, 1);
1734     Changes += RunOptFunc (S, &DOptPtrLoad4, 1);
1735     Changes += RunOptFunc (S, &DOptPtrLoad5, 1);
1736     Changes += RunOptFunc (S, &DOptNegAX1, 1);
1737     Changes += RunOptFunc (S, &DOptNegAX2, 1);
1738     Changes += RunOptFunc (S, &DOptNegAX3, 1);
1739     Changes += RunOptFunc (S, &DOptNegAX4, 1);
1740     Changes += RunOptFunc (S, &DOptAdd1, 1);
1741     Changes += RunOptFunc (S, &DOptAdd2, 1);
1742     Changes += RunOptFunc (S, &DOptShift1, 1);
1743     Changes += RunOptFunc (S, &DOptShift2, 1);
1744
1745     /* Return the number of changes */
1746     return Changes;
1747 }
1748
1749
1750
1751 static unsigned RunOptGroup2 (CodeSeg* S)
1752 /* Run one group of optimization steps. This step involves just decoupling
1753  * instructions by replacing them by instructions that do not depend on
1754  * previous instructions. This makes it easier to find instructions that
1755  * aren't used.
1756  */
1757 {
1758     unsigned Changes = 0;
1759
1760     Changes += RunOptFunc (S, &DOptDecouple, 1);
1761
1762     /* Return the number of changes */
1763     return Changes;
1764 }
1765
1766
1767
1768 static unsigned RunOptGroup3 (CodeSeg* S)
1769 /* Run one group of optimization steps. These steps depend on each other,
1770  * that means that one step may allow another step to do additional work,
1771  * so we will repeat the steps as long as we see any changes.
1772  */
1773 {
1774     unsigned Changes, C;
1775
1776     Changes = 0;
1777     do {
1778         C = 0;
1779
1780         C += RunOptFunc (S, &DOptPtrLoad6, 1);
1781         C += RunOptFunc (S, &DOptNegA1, 1);
1782         C += RunOptFunc (S, &DOptNegA2, 1);
1783         C += RunOptFunc (S, &DOptSub1, 1);
1784         C += RunOptFunc (S, &DOptSub2, 1);
1785         C += RunOptFunc (S, &DOptAdd3, 1);
1786         C += RunOptFunc (S, &DOptStackOps, 1);
1787         C += RunOptFunc (S, &DOptJumpCascades, 1);
1788         C += RunOptFunc (S, &DOptDeadJumps, 1);
1789         C += RunOptFunc (S, &DOptRTS, 1);
1790         C += RunOptFunc (S, &DOptDeadCode, 1);
1791         C += RunOptFunc (S, &DOptJumpTarget, 1);
1792         C += RunOptFunc (S, &DOptCondBranches, 1);
1793         C += RunOptFunc (S, &DOptRTSJumps1, 1);
1794         C += RunOptFunc (S, &DOptBoolTrans, 1);
1795         C += RunOptFunc (S, &DOptCmp1, 1);
1796         C += RunOptFunc (S, &DOptCmp2, 1);
1797         C += RunOptFunc (S, &DOptCmp3, 1);
1798         C += RunOptFunc (S, &DOptCmp4, 1);
1799         C += RunOptFunc (S, &DOptCmp5, 1);
1800         C += RunOptFunc (S, &DOptCmp6, 1);
1801         C += RunOptFunc (S, &DOptCmp7, 1);
1802         C += RunOptFunc (S, &DOptTest1, 1);
1803         C += RunOptFunc (S, &DOptUnusedLoads, 1);
1804         C += RunOptFunc (S, &DOptUnusedStores, 1);
1805         C += RunOptFunc (S, &DOptDupLoads, 1);
1806         C += RunOptFunc (S, &DOptStoreLoad, 1);
1807         C += RunOptFunc (S, &DOptTransfers, 1);
1808
1809         Changes += C;
1810
1811     } while (C);
1812
1813     /* Return the number of changes */
1814     return Changes;
1815 }
1816
1817
1818
1819 static unsigned RunOptGroup4 (CodeSeg* S)
1820 /* 65C02 specific optimizations. */
1821 {
1822     unsigned Changes = 0;
1823
1824     if (CPU >= CPU_65C02) {
1825         Changes += RunOptFunc (S, &DOpt65C02BitOps, 1);
1826         Changes += RunOptFunc (S, &DOpt65C02Ind, 1);
1827         Changes += RunOptFunc (S, &DOpt65C02Stores, 1);
1828         if (Changes) {
1829             /* The 65C02 replacement codes do often make the use of a register
1830              * value unnecessary, so if we have changes, run another load
1831              * removal pass.
1832              */
1833             Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
1834         }
1835     }
1836
1837     /* Return the number of changes */
1838     return Changes;
1839 }
1840
1841
1842
1843 static unsigned RunOptGroup5 (CodeSeg* S)
1844 /* Run another round of pattern replacements. These are done late, since there
1845  * may be better replacements before.
1846  */
1847 {
1848     unsigned Changes = 0;
1849
1850     Changes += RunOptFunc (S, &DOptPush1, 1);
1851
1852     /* Return the number of changes */
1853     return Changes;
1854 }
1855
1856
1857
1858 static unsigned RunOptGroup6 (CodeSeg* S)
1859 /* The last group of optimization steps. Adjust branches, do size optimizations.
1860  */
1861 {
1862     unsigned Changes = 0;
1863     unsigned C;
1864
1865     /* Optimize for size, that is replace operations by shorter ones, even
1866      * if this does hinder further optimizations (no problem since we're
1867      * done soon).
1868      */
1869     Changes += RunOptFunc (S, &DOptSize2, 1);
1870
1871     /* Run the jump target optimization again, since the size optimization
1872      * above may have opened new oportunities.
1873      */
1874     Changes += RunOptFunc (S, &DOptJumpTarget, 5);
1875
1876     /* Adjust branch distances */
1877     Changes += RunOptFunc (S, &DOptBranchDist, 3);
1878
1879     /* Replace conditional branches to RTS. If we had changes, we must run dead
1880      * code elimination again, since the change may have introduced dead code.
1881      */
1882     C = RunOptFunc (S, &DOptRTSJumps2, 1);
1883     Changes += C;
1884     if (C) {
1885         Changes += RunOptFunc (S, &DOptDeadCode, 1);
1886     }
1887
1888     /* Return the number of changes */
1889     return Changes;
1890 }
1891
1892
1893
1894 void RunOpt (CodeSeg* S)
1895 /* Run the optimizer */
1896 {
1897     const char* StatFileName;
1898
1899     /* If we shouldn't run the optimizer, bail out */
1900     if (!Optimize) {
1901         return;
1902     }
1903
1904     /* Check if we are requested to write optimizer statistics */
1905     StatFileName = getenv ("CC65_OPTSTATS");
1906     if (StatFileName) {
1907         ReadOptStats (StatFileName);
1908     }
1909
1910     /* Print the name of the function we are working on */
1911     if (S->Func) {
1912         Print (stdout, 1, "Running optimizer for function `%s'\n", S->Func->Name);
1913     } else {
1914         Print (stdout, 1, "Running optimizer for global code segment\n");
1915     }
1916
1917     /* Run groups of optimizations */
1918     RunOptGroup1 (S);
1919     RunOptGroup2 (S);
1920     RunOptGroup3 (S);
1921     RunOptGroup4 (S);
1922     RunOptGroup5 (S);
1923     RunOptGroup6 (S);
1924
1925     /* Write statistics */
1926     if (StatFileName) {
1927         WriteOptStats (StatFileName);
1928     }
1929 }
1930
1931
1932