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