]> git.sur5r.net Git - cc65/blob - src/cc65/codeopt.c
3ec3f12db6e0ac8a70364accd7daca05406350a6
[cc65] / src / cc65 / codeopt.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 codeopt.c                                 */
4 /*                                                                           */
5 /*                           Optimizer subroutines                           */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2001      Ullrich von Bassewitz                                       */
10 /*               Wacholderweg 14                                             */
11 /*               D-70597 Stuttgart                                           */
12 /* EMail:        uz@cc65.org                                                 */
13 /*                                                                           */
14 /*                                                                           */
15 /* This software is provided 'as-is', without any expressed or implied       */
16 /* warranty.  In no event will the authors be held liable for any damages    */
17 /* arising from the use of this software.                                    */
18 /*                                                                           */
19 /* Permission is granted to anyone to use this software for any purpose,     */
20 /* including commercial applications, and to alter it and redistribute it    */
21 /* freely, subject to the following restrictions:                            */
22 /*                                                                           */
23 /* 1. The origin of this software must not be misrepresented; you must not   */
24 /*    claim that you wrote the original software. If you use this software   */
25 /*    in a product, an acknowledgment in the product documentation would be  */
26 /*    appreciated but is not required.                                       */
27 /* 2. Altered source versions must be plainly marked as such, and must not   */
28 /*    be misrepresented as being the original software.                      */
29 /* 3. This notice may not be removed or altered from any source              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <string.h>
37
38 /* common */
39 #include "abend.h"
40 #include "chartype.h"
41 #include "print.h"
42 #include "xsprintf.h"
43
44 /* cc65 */
45 #include "asmlabel.h"
46 #include "codeent.h"
47 #include "codeinfo.h"
48 #include "coptind.h"
49 #include "error.h"
50 #include "global.h"
51 #include "codeopt.h"
52
53
54
55 /*****************************************************************************/
56 /*                                   Data                                    */
57 /*****************************************************************************/
58
59
60
61 /* Defines for the conditions in a compare */
62 typedef enum {
63     CMP_INV = -1,
64     CMP_EQ,
65     CMP_NE,
66     CMP_GT,
67     CMP_GE,
68     CMP_LT,
69     CMP_LE,
70     CMP_UGT,
71     CMP_UGE,
72     CMP_ULT,
73     CMP_ULE
74 } cmp_t;
75
76 /* Table with the compare suffixes */
77 static const char CmpSuffixTab [][4] = {
78     "eq", "ne", "gt", "ge", "lt", "le", "ugt", "uge", "ult", "ule"
79 };
80
81 /* Table used to invert a condition, indexed by condition */
82 static const unsigned char CmpInvertTab [] = {
83     CMP_NE, CMP_EQ,
84     CMP_LE, CMP_LT, CMP_GE, CMP_GT,
85     CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT
86 };
87
88 /* Table to show which compares are signed (use the N flag) */
89 static const char CmpSignedTab [] = {
90     0, 0, 1, 1, 1, 1, 0, 0, 0, 0
91 };
92
93
94
95 /*****************************************************************************/
96 /*                             Helper functions                              */
97 /*****************************************************************************/
98
99
100
101 static cmp_t FindCmpCond (const char* Code, unsigned CodeLen)
102 /* Search for a compare condition by the given code using the given length */
103 {
104     unsigned I;
105
106     /* Linear search */
107     for (I = 0; I < sizeof (CmpSuffixTab) / sizeof (CmpSuffixTab [0]); ++I) {
108         if (strncmp (Code, CmpSuffixTab [I], CodeLen) == 0) {
109             /* Found */
110             return I;
111         }
112     }
113
114     /* Not found */
115     return CMP_INV;
116 }
117
118
119
120 static cmp_t FindBoolCmpCond (const char* Name)
121 /* Map a condition suffix to a code. Return the code or CMP_INV on failure */
122 {
123     /* Check for the correct subroutine name */
124     if (strncmp (Name, "bool", 4) == 0) {
125         /* Name is ok, search for the code in the table */
126         return FindCmpCond (Name+4, strlen(Name)-4);
127     } else {
128         /* Not found */
129         return CMP_INV;
130     }
131 }
132
133
134
135 static cmp_t FindTosCmpCond (const char* Name)
136 /* Check if this is a call to one of the TOS compare functions (tosgtax).
137  * Return the condition code or CMP_INV on failure.
138  */
139 {
140     unsigned Len = strlen (Name);
141
142     /* Check for the correct subroutine name */
143     if (strncmp (Name, "tos", 3) == 0 && strcmp (Name+Len-2, "ax") == 0) {
144         /* Name is ok, search for the code in the table */
145         return FindCmpCond (Name+3, Len-3-2);
146     } else {
147         /* Not found */
148         return CMP_INV;
149     }
150 }
151
152
153
154 static void ReplaceCmp (CodeSeg* S, unsigned I, cmp_t Cond)
155 /* Helper function for the replacement of routines that return a boolean
156  * followed by a conditional jump. Instead of the boolean value, the condition
157  * codes are evaluated directly.
158  * I is the index of the conditional branch, the sequence is already checked
159  * to be correct.
160  */
161 {
162     CodeEntry* N;
163     CodeLabel* L;
164
165     /* Get the entry */
166     CodeEntry* E = CS_GetEntry (S, I);
167
168     /* Replace the conditional branch */
169     switch (Cond) {
170
171         case CMP_EQ:
172             CE_ReplaceOPC (E, OP65_JEQ);
173             break;
174
175         case CMP_NE:
176             CE_ReplaceOPC (E, OP65_JNE);
177             break;
178
179         case CMP_GT:
180             /* Replace by
181              *     beq @L
182              *     jpl Target
183              * @L: ...
184              */
185             if ((N = CS_GetNextEntry (S, I)) == 0) {
186                 /* No such entry */
187                 Internal ("Invalid program flow");
188             }
189             L = CS_GenLabel (S, N);
190             N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI);
191             CS_InsertEntry (S, N, I);
192             CE_ReplaceOPC (E, OP65_JPL);
193             break;
194
195         case CMP_GE:
196             CE_ReplaceOPC (E, OP65_JPL);
197             break;
198
199         case CMP_LT:
200             CE_ReplaceOPC (E, OP65_JMI);
201             break;
202
203         case CMP_LE:
204             /* Replace by
205              *     jmi Target
206              *     jeq Target
207              */
208             CE_ReplaceOPC (E, OP65_JMI);
209             L = E->JumpTo;
210             N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI);
211             CS_InsertEntry (S, N, I+1);
212             break;
213
214         case CMP_UGT:
215             /* Replace by
216              *     beq @L
217              *     jcs Target
218              * @L: ...
219              */
220             if ((N = CS_GetNextEntry (S, I)) == 0) {
221                 /* No such entry */
222                 Internal ("Invalid program flow");
223             }
224             L = CS_GenLabel (S, N);
225             N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI);
226             CS_InsertEntry (S, N, I);
227             CE_ReplaceOPC (E, OP65_JCS);
228             break;
229
230         case CMP_UGE:
231             CE_ReplaceOPC (E, OP65_JCS);
232             break;
233
234         case CMP_ULT:
235             CE_ReplaceOPC (E, OP65_JCC);
236             break;
237
238         case CMP_ULE:
239             /* Replace by
240              *     jcc Target
241              *     jeq Target
242              */
243             CE_ReplaceOPC (E, OP65_JCC);
244             L = E->JumpTo;
245             N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI);
246             CS_InsertEntry (S, N, I+1);
247             break;
248
249         default:
250             Internal ("Unknown jump condition: %d", Cond);
251
252     }
253
254 }
255
256
257
258 static int IsCmpToZero (const CodeEntry* E)
259 /* Check if the given instrcuction is a compare to zero instruction */
260 {
261     return (E->OPC == OP65_CMP            &&
262             E->AM  == AM65_IMM            &&
263             (E->Flags & CEF_NUMARG) != 0  &&
264             E->Num == 0);
265 }
266
267
268
269 static int IsSpLoad (const CodeEntry* E)
270 /* Return true if this is the load of A from the stack */
271 {
272     return E->OPC == OP65_LDA && E->AM == AM65_ZP_INDY && strcmp (E->Arg, "sp") == 0;
273 }
274
275
276
277 static int IsLocalLoad16 (CodeSeg* S, unsigned Index,
278                           CodeEntry** L, unsigned Count)
279 /* Check if a 16 bit load of a local variable follows:
280  *
281  *      ldy     #$xx
282  *      lda     (sp),y
283  *      tax
284  *      dey
285  *      lda     (sp),y
286  *
287  * If so, read Count entries following the first ldy into L and return true
288  * if this is possible. Otherwise return false.
289  */
290 {
291     /* Be sure we read enough entries for the check */
292     CHECK (Count >= 5);
293
294     /* Read the first entry */
295     L[0] = CS_GetEntry (S, Index);
296
297     /* Check for the sequence */
298     return (L[0]->OPC == OP65_LDY                        &&
299             L[0]->AM == AM65_IMM                         &&
300             (L[0]->Flags & CEF_NUMARG) != 0              &&
301             CS_GetEntries (S, L+1, Index+1, Count-1)     &&
302             IsSpLoad (L[1])                              &&
303             !CE_HasLabel (L[1])                          &&
304             L[2]->OPC == OP65_TAX                        &&
305             !CE_HasLabel (L[2])                          &&
306             L[3]->OPC == OP65_DEY                        &&
307             !CE_HasLabel (L[3])                          &&
308             IsSpLoad (L[4])                              &&
309             !CE_HasLabel (L[4]));
310 }
311
312
313
314 static int IsImmCmp16 (CodeSeg* S, CodeEntry** L)
315 /* Check if the instructions at L are an immidiate compare of a/x:
316  *
317  *
318  */
319 {
320     return (L[0]->OPC == OP65_CPX                              &&
321             L[0]->AM == AM65_IMM                               &&
322             (L[0]->Flags & CEF_NUMARG) != 0                    &&
323             !CE_HasLabel (L[0])                                &&
324             (L[1]->OPC == OP65_JNE || L[1]->OPC == OP65_BNE)   &&
325             L[1]->JumpTo != 0                                  &&
326             !CE_HasLabel (L[1])                                &&
327             L[2]->OPC == OP65_CMP                              &&
328             L[2]->AM == AM65_IMM                               &&
329             (L[2]->Flags & CEF_NUMARG) != 0                    &&
330             (L[3]->Info & OF_ZBRA) != 0                        &&
331             L[3]->JumpTo != 0                                  &&
332             (L[1]->JumpTo->Owner == L[3] || L[1]->JumpTo == L[3]->JumpTo));
333 }
334
335
336
337 /*****************************************************************************/
338 /*             Remove calls to the bool transformer subroutines              */
339 /*****************************************************************************/
340
341
342
343 static unsigned OptBoolTransforms (CodeSeg* S)
344 /* Try to remove the call to boolean transformer routines where the call is
345  * not really needed.
346  */
347 {
348     unsigned Changes = 0;
349
350     /* Walk over the entries */
351     unsigned I = 0;
352     while (I < CS_GetEntryCount (S)) {
353
354         CodeEntry* N;
355         cmp_t Cond;
356
357         /* Get next entry */
358         CodeEntry* E = CS_GetEntry (S, I);
359
360         /* Check for a boolean transformer */
361         if (E->OPC == OP65_JSR                           &&
362             (Cond = FindBoolCmpCond (E->Arg)) != CMP_INV &&
363             (N = CS_GetNextEntry (S, I)) != 0        &&
364             (N->Info & OF_ZBRA) != 0) {
365
366             /* Make the boolean transformer unnecessary by changing the
367              * the conditional jump to evaluate the condition flags that
368              * are set after the compare directly. Note: jeq jumps if
369              * the condition is not met, jne jumps if the condition is met.
370              * Invert the code if we jump on condition not met.
371              */
372             if (GetBranchCond (N->OPC) == BC_EQ) {
373                 /* Jumps if condition false, invert condition */
374                 Cond = CmpInvertTab [Cond];
375             }
376
377             /* Check if we can replace the code by something better */
378             ReplaceCmp (S, I+1, Cond);
379
380             /* Remove the call to the bool transformer */
381             CS_DelEntry (S, I);
382
383             /* Remember, we had changes */
384             ++Changes;
385
386         }
387
388         /* Next entry */
389         ++I;
390
391     }
392
393     /* Return the number of changes made */
394     return Changes;
395 }
396
397
398
399 /*****************************************************************************/
400 /*                           Optimize subtractions                           */
401 /*****************************************************************************/
402
403
404
405 static unsigned OptSub1 (CodeSeg* S)
406 /* Search for the sequence
407  *
408  *      sbc     ...
409  *      bcs     L
410  *      dex
411  * L:
412  *
413  * and remove the handling of the high byte if X is not used later.
414  */
415 {
416     unsigned Changes = 0;
417
418     /* Walk over the entries */
419     unsigned I = 0;
420     while (I < CS_GetEntryCount (S)) {
421
422         CodeEntry* L[3];
423
424         /* Get next entry */
425         CodeEntry* E = CS_GetEntry (S, I);
426
427         /* Check for the sequence */
428         if (E->OPC == OP65_SBC                               &&
429             CS_GetEntries (S, L, I+1, 3)                     &&
430             (L[0]->OPC == OP65_BCS || L[0]->OPC == OP65_JCS) &&
431             L[0]->JumpTo != 0                                &&
432             !CE_HasLabel (L[0])                              &&
433             L[1]->OPC == OP65_DEX                            &&
434             !CE_HasLabel (L[1])                              &&
435             L[0]->JumpTo->Owner == L[2]                      &&
436             !RegXUsed (S, I+3)) {
437
438             /* Remove the bcs/dex */
439             CS_DelEntries (S, I+1, 2);
440
441             /* Remember, we had changes */
442             ++Changes;
443
444         }
445
446         /* Next entry */
447         ++I;
448
449     }
450
451     /* Return the number of changes made */
452     return Changes;
453 }
454
455
456
457 static unsigned OptSub2 (CodeSeg* S)
458 /* Search for the sequence
459  *
460  *      lda     xx
461  *      sec
462  *      sta     tmp1
463  *      lda     yy
464  *      sbc     tmp1
465  *      sta     yy
466  *
467  * and replace it by
468  *
469  *      sec
470  *      lda     yy
471  *      sbc     xx
472  *      sta     yy
473  */
474 {
475     unsigned Changes = 0;
476
477     /* Walk over the entries */
478     unsigned I = 0;
479     while (I < CS_GetEntryCount (S)) {
480
481         CodeEntry* L[5];
482
483         /* Get next entry */
484         CodeEntry* E = CS_GetEntry (S, I);
485
486         /* Check for the sequence */
487         if (E->OPC == OP65_LDA                             &&
488             CS_GetEntries (S, L, I+1, 5)                   &&
489             L[0]->OPC == OP65_SEC                          &&
490             !CE_HasLabel (L[0])                            &&
491             L[1]->OPC == OP65_STA                          &&
492             strcmp (L[1]->Arg, "tmp1") == 0                &&
493             !CE_HasLabel (L[1])                            &&
494             L[2]->OPC == OP65_LDA                          &&
495             !CE_HasLabel (L[2])                            &&
496             L[3]->OPC == OP65_SBC                          &&
497             strcmp (L[3]->Arg, "tmp1") == 0                &&
498             !CE_HasLabel (L[3])                            &&
499             L[4]->OPC == OP65_STA                          &&
500             strcmp (L[4]->Arg, L[2]->Arg) == 0             &&
501             !CE_HasLabel (L[4])) {
502
503             /* Remove the store to tmp1 */
504             CS_DelEntry (S, I+2);
505
506             /* Remove the subtraction */
507             CS_DelEntry (S, I+3);
508
509             /* Move the lda to the position of the subtraction and change the
510              * op to SBC.
511              */
512             CS_MoveEntry (S, I, I+3);
513             CE_ReplaceOPC (E, OP65_SBC);
514
515             /* If the sequence head had a label, move this label back to the
516              * head.
517              */
518             if (CE_HasLabel (E)) {
519                 CS_MoveLabels (S, E, L[0]);
520             }
521
522             /* Remember, we had changes */
523             ++Changes;
524
525         }
526
527         /* Next entry */
528         ++I;
529
530     }
531
532     /* Return the number of changes made */
533     return Changes;
534 }
535
536
537
538 /*****************************************************************************/
539 /*                            Optimize additions                             */
540 /*****************************************************************************/
541
542
543
544 static unsigned OptAdd1 (CodeSeg* S)
545 /* Search for the sequence
546  *
547  *      jsr     pushax
548  *      ldy     xxx
549  *      ldx     #$00
550  *      lda     (sp),y
551  *      jsr     tosaddax
552  *
553  * and replace it by:
554  *
555  *      ldy     xxx
556  *      clc
557  *      adc     (sp),y
558  *      bcc     L
559  *      inx
560  * L:
561  */
562 {
563     unsigned Changes = 0;
564
565     /* Walk over the entries */
566     unsigned I = 0;
567     while (I < CS_GetEntryCount (S)) {
568
569         CodeEntry* L[5];
570
571         /* Get next entry */
572         CodeEntry* E = CS_GetEntry (S, I);
573
574         /* Check for the sequence */
575         if (E->OPC == OP65_JSR                               &&
576             strcmp (E->Arg, "pushax") == 0                   &&
577             CS_GetEntries (S, L, I+1, 5)                     &&
578             L[0]->OPC == OP65_LDY                            &&
579             !CE_HasLabel (L[0])                              &&
580             L[1]->OPC == OP65_LDX                            &&
581             CE_KnownImm (L[1])                               &&
582             L[1]->Num == 0                                   &&
583             !CE_HasLabel (L[1])                              &&
584             L[2]->OPC == OP65_LDA                            &&
585             !CE_HasLabel (L[2])                              &&
586             L[3]->OPC == OP65_JSR                            &&
587             strcmp (L[3]->Arg, "tosaddax") == 0              &&
588             !CE_HasLabel (L[3])) {
589
590             CodeEntry* X;
591             CodeLabel* Label;
592
593             /* Remove the call to pushax */
594             CS_DelEntry (S, I);
595
596             /* Add the clc . */
597             X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, L[3]->LI);
598             CS_InsertEntry (S, X, I+1);
599
600             /* Remove the load */
601             CS_DelEntry (S, I+3);      /* lda */
602             CS_DelEntry (S, I+2);      /* ldx */
603
604             /* Add the adc */
605             X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp", 0, L[3]->LI);
606             CS_InsertEntry (S, X, I+2);
607
608             /* Generate the branch label and the branch */
609             Label = CS_GenLabel (S, L[4]);
610             X = NewCodeEntry (OP65_BCC, AM65_BRA, Label->Name, Label, L[3]->LI);
611             CS_InsertEntry (S, X, I+3);
612
613             /* Generate the increment of the high byte */
614             X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, L[3]->LI);
615             CS_InsertEntry (S, X, I+4);
616
617             /* Delete the call to tosaddax */
618             CS_DelEntry (S, I+5);
619
620             /* Remember, we had changes */
621             ++Changes;
622
623         }
624
625         /* Next entry */
626         ++I;
627
628     }
629
630     /* Return the number of changes made */
631     return Changes;
632 }
633
634
635
636 static unsigned OptAdd2 (CodeSeg* S)
637 /* Search for the sequence
638  *
639  *      ldy     #xx
640  *      lda     (sp),y
641  *      tax
642  *      dey
643  *      lda     (sp),y
644  *      ldy     #$yy
645  *      jsr     addeqysp
646  *
647  * and replace it by:
648  *
649  *      ldy     #xx-1
650  *      lda     (sp),y
651  *      ldy     #yy
652  *      clc
653  *      adc     (sp),y
654  *      sta     (sp),y
655  *      ldy     #xx
656  *      lda     (sp),y
657  *      ldy     #yy+1
658  *      adc     (sp),y
659  *      sta     (sp),y
660  *
661  * provided that a/x is not used later.
662  */
663 {
664     unsigned Changes = 0;
665
666     /* Walk over the entries */
667     unsigned I = 0;
668     while (I < CS_GetEntryCount (S)) {
669
670         CodeEntry* L[7];
671
672         /* Get next entry */
673         L[0] = CS_GetEntry (S, I);
674
675         /* Check for the sequence */
676         if (L[0]->OPC == OP65_LDY               &&
677             CE_KnownImm (L[0])                  &&
678             CS_GetEntries (S, L+1, I+1, 6)      &&
679             L[1]->OPC == OP65_LDA               &&
680             L[1]->AM == AM65_ZP_INDY            &&
681             !CE_HasLabel (L[1])                 &&
682             L[2]->OPC == OP65_TAX               &&
683             !CE_HasLabel (L[2])                 &&
684             L[3]->OPC == OP65_DEY               &&
685             !CE_HasLabel (L[3])                 &&
686             L[4]->OPC == OP65_LDA               &&
687             L[4]->AM == AM65_ZP_INDY            &&
688             !CE_HasLabel (L[4])                 &&
689             L[5]->OPC == OP65_LDY               &&
690             CE_KnownImm (L[5])                  &&
691             !CE_HasLabel (L[5])                 &&
692             L[6]->OPC == OP65_JSR               &&
693             strcmp (L[6]->Arg, "addeqysp") == 0 &&
694             !CE_HasLabel (L[6])                 &&
695             (GetRegInfo (S, I+7) & REG_AX) == 0) {
696
697             char Buf [20];
698             CodeEntry* X;
699             int Offs;
700
701
702             /* Create a replacement for the first LDY */
703             Offs = (int) (L[0]->Num - 1);
704             xsprintf (Buf, sizeof (Buf), "$%02X", Offs);
705             X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[0]->LI);
706             CS_InsertEntry (S, X, I+1);
707             CS_DelEntry (S, I);
708
709             /* Load Y with the low offset of the target variable */
710             X = NewCodeEntry (OP65_LDY, AM65_IMM, L[5]->Arg, 0, L[1]->LI);
711             CS_InsertEntry (S, X, I+2);
712
713             /* Add the CLC */
714             X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, L[1]->LI);
715             CS_InsertEntry (S, X, I+3);
716
717             /* Remove the TAX/DEY sequence */
718             CS_DelEntry (S, I+5);      /* dey */
719             CS_DelEntry (S, I+4);      /* tax */
720
721             /* Addition of the low byte */
722             X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp", 0, L[4]->LI);
723             CS_InsertEntry (S, X, I+4);
724             X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "sp", 0, L[4]->LI);
725             CS_InsertEntry (S, X, I+5);
726
727             /* LDY */
728             xsprintf (Buf, sizeof (Buf), "$%02X", (Offs+1));
729             X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[4]->LI);
730             CS_InsertEntry (S, X, I+6);
731
732             /* Addition of the high byte */
733             xsprintf (Buf, sizeof (Buf), "$%02X", (int)(L[5]->Num+1));
734             X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[5]->LI);
735             CS_InsertEntry (S, X, I+8);
736             X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp", 0, L[6]->LI);
737             CS_InsertEntry (S, X, I+9);
738             X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "sp", 0, L[6]->LI);
739             CS_InsertEntry (S, X, I+10);
740
741             /* Delete the remaining stuff */
742             CS_DelEntry (S, I+12);
743             CS_DelEntry (S, I+11);
744
745             /* Remember, we had changes */
746             ++Changes;
747
748         }
749
750         /* Next entry */
751         ++I;
752
753     }
754
755     /* Return the number of changes made */
756     return Changes;
757 }
758
759
760
761 static unsigned OptAdd3 (CodeSeg* S)
762 /* Search for the sequence
763  *
764  *      adc     ...
765  *      bcc     L
766  *      inx
767  * L:
768  *
769  * and remove the handling of the high byte if X is not used later.
770  */
771 {
772     unsigned Changes = 0;
773
774     /* Walk over the entries */
775     unsigned I = 0;
776     while (I < CS_GetEntryCount (S)) {
777
778         CodeEntry* L[3];
779
780         /* Get next entry */
781         CodeEntry* E = CS_GetEntry (S, I);
782
783         /* Check for the sequence */
784         if (E->OPC == OP65_ADC                               &&
785             CS_GetEntries (S, L, I+1, 3)                     &&
786             (L[0]->OPC == OP65_BCC || L[0]->OPC == OP65_JCC) &&
787             L[0]->JumpTo != 0                                &&
788             !CE_HasLabel (L[0])                              &&
789             L[1]->OPC == OP65_INX                            &&
790             !CE_HasLabel (L[1])                              &&
791             L[0]->JumpTo->Owner == L[2]                      &&
792             !RegXUsed (S, I+3)) {
793
794             /* Remove the bcs/dex */
795             CS_DelEntries (S, I+1, 2);
796
797             /* Remember, we had changes */
798             ++Changes;
799
800         }
801
802         /* Next entry */
803         ++I;
804
805     }
806
807     /* Return the number of changes made */
808     return Changes;
809 }
810
811
812
813 /*****************************************************************************/
814 /*                              Optimize shifts                              */
815 /*****************************************************************************/
816
817
818
819 static unsigned OptShift1 (CodeSeg* S)
820 /* A call to the shlaxN routine may get replaced by one or more asl insns
821  * if the value of X is not used later.
822  */
823 {
824     unsigned Changes = 0;
825
826     /* Walk over the entries */
827     unsigned I = 0;
828     while (I < CS_GetEntryCount (S)) {
829
830         /* Get next entry */
831         CodeEntry* E = CS_GetEntry (S, I);
832
833         /* Check for the sequence */
834         if (E->OPC == OP65_JSR                       &&
835             (strncmp (E->Arg, "shlax", 5) == 0 ||
836              strncmp (E->Arg, "aslax", 5) == 0)      &&
837             strlen (E->Arg) == 6                     &&
838             IsDigit (E->Arg[5])                      &&
839             !RegXUsed (S, I+1)) {
840
841             /* Insert shift insns */
842             unsigned Count = E->Arg[5] - '0';
843             while (Count--) {
844                 CodeEntry* X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI);
845                 CS_InsertEntry (S, X, I+1);
846             }
847
848             /* Delete the call to shlax */
849             CS_DelEntry (S, I);
850
851             /* Remember, we had changes */
852             ++Changes;
853
854         }
855
856         /* Next entry */
857         ++I;
858
859     }
860
861     /* Return the number of changes made */
862     return Changes;
863 }
864
865
866
867 /*****************************************************************************/
868 /*                        Optimizations for compares                         */
869 /*****************************************************************************/
870
871
872
873 static unsigned OptCmp1 (CodeSeg* S)
874 /* Search for the sequence
875  *
876  *      stx     xx
877  *      stx     tmp1
878  *      ora     tmp1
879  *
880  * and replace it by
881  *
882  *      stx     xx
883  *      ora     xx
884  */
885 {
886     unsigned Changes = 0;
887
888     /* Walk over the entries */
889     unsigned I = 0;
890     while (I < CS_GetEntryCount (S)) {
891
892         CodeEntry* L[2];
893
894         /* Get next entry */
895         CodeEntry* E = CS_GetEntry (S, I);
896
897         /* Check for the sequence */
898         if (E->OPC == OP65_STX                  &&
899             CS_GetEntries (S, L, I+1, 2)        &&
900             L[0]->OPC == OP65_STX               &&
901             strcmp (L[0]->Arg, "tmp1") == 0     &&
902             !CE_HasLabel (L[0])                 &&
903             L[1]->OPC == OP65_ORA               &&
904             strcmp (L[1]->Arg, "tmp1") == 0     &&
905             !CE_HasLabel (L[1])) {
906
907             /* Remove the remaining instructions */
908             CS_DelEntries (S, I+1, 2);
909
910             /* Insert the ora instead */
911             CS_InsertEntry (S, NewCodeEntry (OP65_ORA, E->AM, E->Arg, 0, E->LI), I+1);
912
913             /* Remember, we had changes */
914             ++Changes;
915
916         }
917
918         /* Next entry */
919         ++I;
920
921     }
922
923     /* Return the number of changes made */
924     return Changes;
925 }
926
927
928
929 static unsigned OptCmp2 (CodeSeg* S)
930 /* Search for
931  *
932  *      lda/and/ora/eor ...
933  *      cmp #$00
934  *      jeq/jne
935  *
936  * and remove the cmp.
937  */
938 {
939     unsigned Changes = 0;
940
941     /* Walk over the entries */
942     unsigned I = 0;
943     while (I < CS_GetEntryCount (S)) {
944
945         CodeEntry* L[2];
946
947         /* Get next entry */
948         CodeEntry* E = CS_GetEntry (S, I);
949
950         /* Check for the sequence */
951         if ((E->OPC == OP65_ADC ||
952              E->OPC == OP65_AND ||
953              E->OPC == OP65_DEA ||
954              E->OPC == OP65_EOR ||
955              E->OPC == OP65_INA ||
956              E->OPC == OP65_LDA ||
957              E->OPC == OP65_ORA ||
958              E->OPC == OP65_PLA ||
959              E->OPC == OP65_SBC ||
960              E->OPC == OP65_TXA ||
961              E->OPC == OP65_TYA)                  &&
962             CS_GetEntries (S, L, I+1, 2)          &&
963             IsCmpToZero (L[0])                    &&
964             !CE_HasLabel (L[0])                   &&
965             (L[1]->Info & OF_FBRA) != 0           &&
966             !CE_HasLabel (L[1])) {
967
968             /* Remove the compare */
969             CS_DelEntry (S, I+1);
970
971             /* Remember, we had changes */
972             ++Changes;
973
974         }
975
976         /* Next entry */
977         ++I;
978
979     }
980
981     /* Return the number of changes made */
982     return Changes;
983 }
984
985
986
987 static unsigned OptCmp3 (CodeSeg* S)
988 /* Search for
989  *
990  *      lda     x
991  *      ldx     y
992  *      cpx     #a
993  *      bne     L1
994  *      cmp     #b
995  *      jne/jeq L2
996  *
997  * If a is zero, we may remove the compare. If a and b are both zero, we may
998  * replace it by the sequence
999  *
1000  *      lda     x
1001  *      ora     x+1
1002  *      jne/jeq ...
1003  *
1004  * L1 may be either the label at the branch instruction, or the target label
1005  * of this instruction.
1006  */
1007 {
1008     unsigned Changes = 0;
1009
1010     /* Walk over the entries */
1011     unsigned I = 0;
1012     while (I < CS_GetEntryCount (S)) {
1013
1014         CodeEntry* L[5];
1015
1016         /* Get next entry */
1017         CodeEntry* E = CS_GetEntry (S, I);
1018
1019         /* Check for the sequence */
1020         if (E->OPC == OP65_LDA               &&
1021             CS_GetEntries (S, L, I+1, 5) &&
1022             L[0]->OPC == OP65_LDX            &&
1023             !CE_HasLabel (L[0])              &&
1024             IsImmCmp16 (S, L+1)) {
1025
1026             if (L[1]->Num == 0 && L[3]->Num == 0) {
1027                 /* The value is zero, we may use the simple code version. */
1028                 CE_ReplaceOPC (L[0], OP65_ORA);
1029                 CS_DelEntries (S, I+2, 3);
1030             } else {
1031                 /* Move the lda instruction after the first branch. This will
1032                  * improve speed, since the load is delayed after the first
1033                  * test.
1034                  */
1035                 CS_MoveEntry (S, I, I+4);
1036
1037                 /* We will replace the ldx/cpx by lda/cmp */
1038                 CE_ReplaceOPC (L[0], OP65_LDA);
1039                 CE_ReplaceOPC (L[1], OP65_CMP);
1040
1041                 /* Beware: If the first LDA instruction had a label, we have
1042                  * to move this label to the top of the sequence again.
1043                  */
1044                 if (CE_HasLabel (E)) {
1045                     CS_MoveLabels (S, E, L[0]);
1046                 }
1047
1048             }
1049
1050             ++Changes;
1051         }
1052
1053         /* Next entry */
1054         ++I;
1055
1056     }
1057
1058     /* Return the number of changes made */
1059     return Changes;
1060 }
1061
1062
1063
1064 static unsigned OptCmp4 (CodeSeg* S)
1065 /* Optimize compares of local variables:
1066  *
1067  *      ldy     #o
1068  *      lda     (sp),y
1069  *      tax
1070  *      dey
1071  *      lda     (sp),y
1072  *      cpx     #a
1073  *      bne     L1
1074  *      cmp     #b
1075  *      jne/jeq L2
1076  */
1077 {
1078     unsigned Changes = 0;
1079
1080     /* Walk over the entries */
1081     unsigned I = 0;
1082     while (I < CS_GetEntryCount (S)) {
1083
1084         CodeEntry* L[9];
1085
1086         /* Check for the sequence */
1087         if (IsLocalLoad16 (S, I, L, 9) && IsImmCmp16 (S, L+5)) {
1088
1089             if (L[5]->Num == 0 && L[7]->Num == 0) {
1090
1091                 /* The value is zero, we may use the simple code version:
1092                  *      ldy     #o
1093                  *      lda     (sp),y
1094                  *      dey
1095                  *      ora     (sp),y
1096                  *      jne/jeq ...
1097                  */
1098                 CE_ReplaceOPC (L[4], OP65_ORA);
1099                 CS_DelEntries (S, I+5, 3);   /* cpx/bne/cmp */
1100                 CS_DelEntry (S, I+2);        /* tax */
1101
1102             } else {
1103
1104                 /* Change the code to just use the A register. Move the load
1105                  * of the low byte after the first branch if possible:
1106                  *
1107                  *      ldy     #o
1108                  *      lda     (sp),y
1109                  *      cmp     #a
1110                  *      bne     L1
1111                  *      dey
1112                  *      lda     (sp),y
1113                  *      cmp     #b
1114                  *      jne/jeq ...
1115                  */
1116                 CS_DelEntry (S, I+2);             /* tax */
1117                 CE_ReplaceOPC (L[5], OP65_CMP);   /* cpx -> cmp */
1118                 CS_MoveEntry (S, I+4, I+2);       /* cmp */
1119                 CS_MoveEntry (S, I+5, I+3);       /* bne */
1120
1121             }
1122
1123             ++Changes;
1124         }
1125
1126         /* Next entry */
1127         ++I;
1128
1129     }
1130
1131     /* Return the number of changes made */
1132     return Changes;
1133 }
1134
1135
1136
1137 static unsigned OptCmp5 (CodeSeg* S)
1138 /* Search for calls to compare subroutines followed by a conditional branch
1139  * and replace them by cheaper versions, since the branch means that the
1140  * boolean value returned by these routines is not needed (we may also check
1141  * that explicitly, but for the current code generator it is always true).
1142  */
1143 {
1144     unsigned Changes = 0;
1145
1146     /* Walk over the entries */
1147     unsigned I = 0;
1148     while (I < CS_GetEntryCount (S)) {
1149
1150         CodeEntry* N;
1151         cmp_t Cond;
1152
1153         /* Get next entry */
1154         CodeEntry* E = CS_GetEntry (S, I);
1155
1156         /* Check for the sequence */
1157         if (E->OPC == OP65_JSR                          &&
1158             (Cond = FindTosCmpCond (E->Arg)) != CMP_INV &&
1159             (N = CS_GetNextEntry (S, I)) != 0           &&
1160             (N->Info & OF_ZBRA) != 0                    &&
1161             !CE_HasLabel (N)) {
1162
1163             /* The tos... functions will return a boolean value in a/x and
1164              * the Z flag says if this value is zero or not. We will call
1165              * a cheaper subroutine instead, one that does not return a
1166              * boolean value but only valid flags. Note: jeq jumps if
1167              * the condition is not met, jne jumps if the condition is met.
1168              * Invert the code if we jump on condition not met.
1169              */
1170             if (GetBranchCond (N->OPC) == BC_EQ) {
1171                 /* Jumps if condition false, invert condition */
1172                 Cond = CmpInvertTab [Cond];
1173             }
1174
1175             /* Replace the subroutine call. */
1176             E = NewCodeEntry (OP65_JSR, AM65_ABS, "tosicmp", 0, E->LI);
1177             CS_InsertEntry (S, E, I+1);
1178             CS_DelEntry (S, I);
1179
1180             /* Replace the conditional branch */
1181             ReplaceCmp (S, I+1, Cond);
1182
1183             /* Remember, we had changes */
1184             ++Changes;
1185
1186         }
1187
1188         /* Next entry */
1189         ++I;
1190
1191     }
1192
1193     /* Return the number of changes made */
1194     return Changes;
1195 }
1196
1197
1198
1199 static unsigned OptCmp6 (CodeSeg* S)
1200 /* Search for a sequence ldx/txa/branch and remove the txa if A is not
1201  * used later.
1202  */
1203 {
1204     unsigned Changes = 0;
1205
1206     /* Walk over the entries */
1207     unsigned I = 0;
1208     while (I < CS_GetEntryCount (S)) {
1209
1210         CodeEntry* L[2];
1211
1212         /* Get next entry */
1213         CodeEntry* E = CS_GetEntry (S, I);
1214
1215         /* Check for the sequence */
1216         if ((E->OPC == OP65_LDX)                        &&
1217             CS_GetEntries (S, L, I+1, 2)                &&
1218             L[0]->OPC == OP65_TXA                       &&
1219             !CE_HasLabel (L[0])                         &&
1220             (L[1]->Info & OF_FBRA) != 0                 &&
1221             !CE_HasLabel (L[1])                         &&
1222             !RegAUsed (S, I+3)) {
1223
1224             /* Remove the txa */
1225             CS_DelEntry (S, I+1);
1226
1227             /* Remember, we had changes */
1228             ++Changes;
1229
1230         }
1231
1232         /* Next entry */
1233         ++I;
1234
1235     }
1236
1237     /* Return the number of changes made */
1238     return Changes;
1239 }
1240
1241
1242
1243 /*****************************************************************************/
1244 /*                              Optimize tests                               */
1245 /*****************************************************************************/
1246
1247
1248
1249 static unsigned OptTest1 (CodeSeg* S)
1250 /* On a sequence
1251  *
1252  *     stx     xxx
1253  *     ora     xxx
1254  *     beq/bne ...
1255  *
1256  * if X is zero, the sequence may be changed to
1257  *
1258  *     cmp     #$00
1259  *     beq/bne ...
1260  *
1261  * which may be optimized further by another step.
1262  *
1263  * If A is zero, the sequence may be changed to
1264  *
1265  *     txa
1266  *     beq/bne ...
1267  *
1268  */
1269 {
1270     unsigned Changes = 0;
1271     unsigned I;
1272
1273     /* Generate register info for this step */
1274     CS_GenRegInfo (S);
1275
1276     /* Walk over the entries */
1277     I = 0;
1278     while (I < CS_GetEntryCount (S)) {
1279
1280         CodeEntry* L[3];
1281
1282         /* Get next entry */
1283         L[0] = CS_GetEntry (S, I);
1284
1285         /* Check if it's the sequence we're searching for */
1286         if (L[0]->OPC == OP65_STX              &&
1287             CS_GetEntries (S, L+1, I+1, 2)     &&
1288             !CE_HasLabel (L[1])                &&
1289             L[1]->OPC == OP65_ORA              &&
1290             strcmp (L[0]->Arg, L[1]->Arg) == 0 &&
1291             !CE_HasLabel (L[2])                &&
1292             (L[2]->Info & OF_ZBRA) != 0) {
1293
1294             /* Check if X is zero */
1295             if (L[0]->RI->In.RegX == 0) {
1296
1297                 /* Insert the compare */
1298                 CodeEntry* N = NewCodeEntry (OP65_CMP, AM65_IMM, "$00", 0, L[0]->LI);
1299                 CS_InsertEntry (S, N, I+2);
1300
1301                 /* Remove the two other insns */
1302                 CS_DelEntry (S, I+1);
1303                 CS_DelEntry (S, I);
1304
1305                 /* We had changes */
1306                 ++Changes;
1307
1308             /* Check if A is zero */
1309             } else if (L[1]->RI->In.RegA == 0) {
1310
1311                 /* Insert the txa */
1312                 CodeEntry* N = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, L[1]->LI);
1313                 CS_InsertEntry (S, N, I+2);
1314
1315                 /* Remove the two other insns */
1316                 CS_DelEntry (S, I+1);
1317                 CS_DelEntry (S, I);
1318
1319                 /* We had changes */
1320                 ++Changes;
1321             }
1322         }
1323
1324         /* Next entry */
1325         ++I;
1326
1327     }
1328
1329     /* Free register info */
1330     CS_FreeRegInfo (S);
1331
1332     /* Return the number of changes made */
1333     return Changes;
1334 }
1335
1336
1337
1338 /*****************************************************************************/
1339 /*                            nega optimizations                             */
1340 /*****************************************************************************/
1341
1342
1343
1344 static unsigned OptNegA1 (CodeSeg* S)
1345 /* Check for
1346  *
1347  *      ldx     #$00
1348  *      lda     ..
1349  *      jsr     bnega
1350  *
1351  * Remove the ldx if the lda does not use it.
1352  */
1353 {
1354     unsigned Changes = 0;
1355
1356     /* Walk over the entries */
1357     unsigned I = 0;
1358     while (I < CS_GetEntryCount (S)) {
1359
1360         CodeEntry* L[2];
1361
1362         /* Get next entry */
1363         CodeEntry* E = CS_GetEntry (S, I);
1364
1365         /* Check for a ldx */
1366         if (E->OPC == OP65_LDX                  &&
1367             E->AM == AM65_IMM                   &&
1368             (E->Flags & CEF_NUMARG) != 0        &&
1369             E->Num == 0                         &&
1370             CS_GetEntries (S, L, I+1, 2)        &&
1371             L[0]->OPC == OP65_LDA               &&
1372             (L[0]->Use & REG_X) == 0            &&
1373             !CE_HasLabel (L[0])                 &&
1374             L[1]->OPC == OP65_JSR               &&
1375             strcmp (L[1]->Arg, "bnega") == 0    &&
1376             !CE_HasLabel (L[1])) {
1377
1378             /* Remove the ldx instruction */
1379             CS_DelEntry (S, I);
1380
1381             /* Remember, we had changes */
1382             ++Changes;
1383
1384         }
1385
1386         /* Next entry */
1387         ++I;
1388
1389     }
1390
1391     /* Return the number of changes made */
1392     return Changes;
1393 }
1394
1395
1396
1397 static unsigned OptNegA2 (CodeSeg* S)
1398 /* Check for
1399  *
1400  *      lda     ..
1401  *      jsr     bnega
1402  *      jeq/jne ..
1403  *
1404  * Adjust the conditional branch and remove the call to the subroutine.
1405  */
1406 {
1407     unsigned Changes = 0;
1408
1409     /* Walk over the entries */
1410     unsigned I = 0;
1411     while (I < CS_GetEntryCount (S)) {
1412
1413         CodeEntry* L[2];
1414
1415         /* Get next entry */
1416         CodeEntry* E = CS_GetEntry (S, I);
1417
1418         /* Check for the sequence */
1419         if ((E->OPC == OP65_ADC ||
1420              E->OPC == OP65_AND ||
1421              E->OPC == OP65_DEA ||
1422              E->OPC == OP65_EOR ||
1423              E->OPC == OP65_INA ||
1424              E->OPC == OP65_LDA ||
1425              E->OPC == OP65_ORA ||
1426              E->OPC == OP65_PLA ||
1427              E->OPC == OP65_SBC ||
1428              E->OPC == OP65_TXA ||
1429              E->OPC == OP65_TYA)                &&
1430             CS_GetEntries (S, L, I+1, 2)        &&
1431             L[0]->OPC == OP65_JSR               &&
1432             strcmp (L[0]->Arg, "bnega") == 0    &&
1433             !CE_HasLabel (L[0])                 &&
1434             (L[1]->Info & OF_ZBRA) != 0         &&
1435             !CE_HasLabel (L[1])) {
1436
1437             /* Invert the branch */
1438             CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
1439
1440             /* Delete the subroutine call */
1441             CS_DelEntry (S, I+1);
1442
1443             /* Remember, we had changes */
1444             ++Changes;
1445
1446         }
1447
1448         /* Next entry */
1449         ++I;
1450
1451     }
1452
1453     /* Return the number of changes made */
1454     return Changes;
1455 }
1456
1457
1458
1459 /*****************************************************************************/
1460 /*                            negax optimizations                            */
1461 /*****************************************************************************/
1462
1463
1464
1465 static unsigned OptNegAX1 (CodeSeg* S)
1466 /* On a call to bnegax, if X is zero, the result depends only on the value in
1467  * A, so change the call to a call to bnega. This will get further optimized
1468  * later if possible.
1469  */
1470 {
1471     unsigned Changes = 0;
1472     unsigned I;
1473
1474     /* Generate register info for this step */
1475     CS_GenRegInfo (S);
1476
1477     /* Walk over the entries */
1478     I = 0;
1479     while (I < CS_GetEntryCount (S)) {
1480
1481         /* Get next entry */
1482         CodeEntry* E = CS_GetEntry (S, I);
1483
1484         /* Check if this is a call to bnegax, and if X is known and zero */
1485         if (E->OPC == OP65_JSR              &&
1486             E->RI->In.RegX == 0             &&
1487             strcmp (E->Arg, "bnegax") == 0) {
1488
1489             /* We're cheating somewhat here ... */
1490             E->Arg[5] = '\0';
1491             E->Use &= ~REG_X;
1492
1493             /* We had changes */
1494             ++Changes;
1495         }
1496
1497         /* Next entry */
1498         ++I;
1499
1500     }
1501
1502     /* Free register info */
1503     CS_FreeRegInfo (S);
1504
1505     /* Return the number of changes made */
1506     return Changes;
1507 }
1508
1509
1510
1511 static unsigned OptNegAX2 (CodeSeg* S)
1512 /* Search for the sequence:
1513  *
1514  *      lda     (xx),y
1515  *      tax
1516  *      dey
1517  *      lda     (xx),y
1518  *      jsr     bnegax
1519  *      jne/jeq ...
1520  *
1521  * and replace it by
1522  *
1523  *      lda     (xx),y
1524  *      dey
1525  *      ora     (xx),y
1526  *      jeq/jne ...
1527  */
1528 {
1529     unsigned Changes = 0;
1530
1531     /* Walk over the entries */
1532     unsigned I = 0;
1533     while (I < CS_GetEntryCount (S)) {
1534
1535         CodeEntry* L[5];
1536
1537         /* Get next entry */
1538         CodeEntry* E = CS_GetEntry (S, I);
1539
1540         /* Check for the sequence */
1541         if (E->OPC == OP65_LDA                  &&
1542             E->AM == AM65_ZP_INDY               &&
1543             CS_GetEntries (S, L, I+1, 5)        &&
1544             L[0]->OPC == OP65_TAX               &&
1545             L[1]->OPC == OP65_DEY               &&
1546             L[2]->OPC == OP65_LDA               &&
1547             L[2]->AM == AM65_ZP_INDY            &&
1548             strcmp (L[2]->Arg, E->Arg) == 0     &&
1549             !CE_HasLabel (L[2])                 &&
1550             L[3]->OPC == OP65_JSR               &&
1551             strcmp (L[3]->Arg, "bnegax") == 0   &&
1552             !CE_HasLabel (L[3])                 &&
1553             (L[4]->Info & OF_ZBRA) != 0         &&
1554             !CE_HasLabel (L[4])) {
1555
1556             /* lda --> ora */
1557             CE_ReplaceOPC (L[2], OP65_ORA);
1558
1559             /* Invert the branch */
1560             CE_ReplaceOPC (L[4], GetInverseBranch (L[4]->OPC));
1561
1562             /* Delete the entries no longer needed. Beware: Deleting entries
1563              * will change the indices.
1564              */
1565             CS_DelEntry (S, I+4);               /* jsr bnegax */
1566             CS_DelEntry (S, I+1);               /* tax */
1567
1568             /* Remember, we had changes */
1569             ++Changes;
1570
1571         }
1572
1573         /* Next entry */
1574         ++I;
1575
1576     }
1577
1578     /* Return the number of changes made */
1579     return Changes;
1580 }
1581
1582
1583
1584 static unsigned OptNegAX3 (CodeSeg* S)
1585 /* Search for the sequence:
1586  *
1587  *      lda     xx
1588  *      ldx     yy
1589  *      jsr     bnegax
1590  *      jne/jeq ...
1591  *
1592  * and replace it by
1593  *
1594  *      lda     xx
1595  *      ora     xx+1
1596  *      jeq/jne ...
1597  */
1598 {
1599     unsigned Changes = 0;
1600
1601     /* Walk over the entries */
1602     unsigned I = 0;
1603     while (I < CS_GetEntryCount (S)) {
1604
1605         CodeEntry* L[3];
1606
1607         /* Get next entry */
1608         CodeEntry* E = CS_GetEntry (S, I);
1609
1610         /* Check for the sequence */
1611         if (E->OPC == OP65_LDA                  &&
1612             CS_GetEntries (S, L, I+1, 3)        &&
1613             L[0]->OPC == OP65_LDX               &&
1614             !CE_HasLabel (L[0])                 &&
1615             L[1]->OPC == OP65_JSR               &&
1616             strcmp (L[1]->Arg, "bnegax") == 0   &&
1617             !CE_HasLabel (L[1])                 &&
1618             (L[2]->Info & OF_ZBRA) != 0         &&
1619             !CE_HasLabel (L[2])) {
1620
1621             /* ldx --> ora */
1622             CE_ReplaceOPC (L[0], OP65_ORA);
1623
1624             /* Invert the branch */
1625             CE_ReplaceOPC (L[2], GetInverseBranch (L[2]->OPC));
1626
1627             /* Delete the subroutine call */
1628             CS_DelEntry (S, I+2);
1629
1630             /* Remember, we had changes */
1631             ++Changes;
1632
1633         }
1634
1635         /* Next entry */
1636         ++I;
1637
1638     }
1639
1640     /* Return the number of changes made */
1641     return Changes;
1642 }
1643
1644
1645
1646 static unsigned OptNegAX4 (CodeSeg* S)
1647 /* Search for the sequence:
1648  *
1649  *      jsr     xxx
1650  *      jsr     bnega(x)
1651  *      jeq/jne ...
1652  *
1653  * and replace it by:
1654  *
1655  *      jsr     xxx
1656  *      <boolean test>
1657  *      jne/jeq ...
1658  */
1659 {
1660     unsigned Changes = 0;
1661
1662     /* Walk over the entries */
1663     unsigned I = 0;
1664     while (I < CS_GetEntryCount (S)) {
1665
1666         CodeEntry* L[2];
1667
1668         /* Get next entry */
1669         CodeEntry* E = CS_GetEntry (S, I);
1670
1671         /* Check for the sequence */
1672         if (E->OPC == OP65_JSR                  &&
1673             CS_GetEntries (S, L, I+1, 2)        &&
1674             L[0]->OPC == OP65_JSR               &&
1675             strncmp (L[0]->Arg,"bnega",5) == 0  &&
1676             !CE_HasLabel (L[0])                 &&
1677             (L[1]->Info & OF_ZBRA) != 0         &&
1678             !CE_HasLabel (L[1])) {
1679
1680             CodeEntry* X;
1681
1682             /* Check if we're calling bnega or bnegax */
1683             int ByteSized = (strcmp (L[0]->Arg, "bnega") == 0);
1684
1685             /* Insert apropriate test code */
1686             if (ByteSized) {
1687                 /* Test bytes */
1688                 X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI);
1689                 CS_InsertEntry (S, X, I+2);
1690             } else {
1691                 /* Test words */
1692                 X = NewCodeEntry (OP65_STX, AM65_ZP, "tmp1", 0, L[0]->LI);
1693                 CS_InsertEntry (S, X, I+2);
1694                 X = NewCodeEntry (OP65_ORA, AM65_ZP, "tmp1", 0, L[0]->LI);
1695                 CS_InsertEntry (S, X, I+3);
1696             }
1697
1698             /* Delete the subroutine call */
1699             CS_DelEntry (S, I+1);
1700
1701             /* Invert the branch */
1702             CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
1703
1704             /* Remember, we had changes */
1705             ++Changes;
1706
1707         }
1708
1709         /* Next entry */
1710         ++I;
1711
1712     }
1713
1714     /* Return the number of changes made */
1715     return Changes;
1716 }
1717
1718
1719
1720 /*****************************************************************************/
1721 /*                     Optimize stores through pointers                      */
1722 /*****************************************************************************/
1723
1724
1725
1726 static unsigned OptPtrStore1Sub (CodeSeg* S, unsigned I, CodeEntry** const L)
1727 /* Check if this is one of the allowed suboperation for OptPtrStore1 */
1728 {
1729     /* Check for a label attached to the entry */
1730     if (CE_HasLabel (L[0])) {
1731         return 0;
1732     }
1733
1734     /* Check for single insn sub ops */
1735     if (L[0]->OPC == OP65_AND                                           ||
1736         L[0]->OPC == OP65_EOR                                           ||
1737         L[0]->OPC == OP65_ORA                                           ||
1738         (L[0]->OPC == OP65_JSR && strncmp (L[0]->Arg, "shlax", 5) == 0) ||
1739         (L[0]->OPC == OP65_JSR && strncmp (L[0]->Arg, "shrax", 5) == 0)) {
1740
1741         /* One insn */
1742         return 1;
1743
1744     } else if (L[0]->OPC == OP65_CLC                      &&
1745                (L[1] = CS_GetNextEntry (S, I)) != 0       &&
1746                L[1]->OPC == OP65_ADC                      &&
1747                !CE_HasLabel (L[1])) {
1748         return 2;
1749     } else if (L[0]->OPC == OP65_SEC                      &&
1750                (L[1] = CS_GetNextEntry (S, I)) != 0       &&
1751                L[1]->OPC == OP65_SBC                      &&
1752                !CE_HasLabel (L[1])) {
1753         return 2;
1754     }
1755
1756
1757
1758     /* Not found */
1759     return 0;
1760 }
1761
1762
1763
1764 static unsigned OptPtrStore1 (CodeSeg* S)
1765 /* Search for the sequence:
1766  *
1767  *      jsr     pushax
1768  *      ldy     xxx
1769  *      jsr     ldauidx
1770  *      subop
1771  *      ldy     yyy
1772  *      jsr     staspidx
1773  *
1774  * and replace it by:
1775  *
1776  *      sta     ptr1
1777  *      stx     ptr1+1
1778  *      ldy     xxx
1779  *      ldx     #$00
1780  *      lda     (ptr1),y
1781  *      subop
1782  *      ldy     yyy
1783  *      sta     (ptr1),y
1784  */
1785 {
1786     unsigned Changes = 0;
1787
1788     /* Walk over the entries */
1789     unsigned I = 0;
1790     while (I < CS_GetEntryCount (S)) {
1791
1792         unsigned K;
1793         CodeEntry* L[10];
1794
1795         /* Get next entry */
1796         L[0] = CS_GetEntry (S, I);
1797
1798         /* Check for the sequence */
1799         if (L[0]->OPC == OP65_JSR                   &&
1800             strcmp (L[0]->Arg, "pushax") == 0       &&
1801             CS_GetEntries (S, L+1, I+1, 3)          &&
1802             L[1]->OPC == OP65_LDY                   &&
1803             CE_KnownImm (L[1])                      &&
1804             !CE_HasLabel (L[1])                     &&
1805             L[2]->OPC == OP65_JSR                   &&
1806             strcmp (L[2]->Arg, "ldauidx") == 0      &&
1807             !CE_HasLabel (L[2])                     &&
1808             (K = OptPtrStore1Sub (S, I+3, L+3)) > 0 &&
1809             CS_GetEntries (S, L+3+K, I+3+K, 2)      &&
1810             L[3+K]->OPC == OP65_LDY                 &&
1811             CE_KnownImm (L[3+K])                    &&
1812             !CE_HasLabel (L[3+K])                   &&
1813             L[4+K]->OPC == OP65_JSR                 &&
1814             strcmp (L[4+K]->Arg, "staspidx") == 0   &&
1815             !CE_HasLabel (L[4+K])) {
1816
1817             CodeEntry* X;
1818
1819             /* Create and insert the stores */
1820             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
1821             CS_InsertEntry (S, X, I+1);
1822
1823             X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
1824             CS_InsertEntry (S, X, I+2);
1825
1826             /* Delete the call to pushax */
1827             CS_DelEntry (S, I);
1828
1829             /* Delete the call to ldauidx */
1830             CS_DelEntry (S, I+3);
1831
1832             /* Insert the load from ptr1 */
1833             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[3]->LI);
1834             CS_InsertEntry (S, X, I+3);
1835             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[2]->LI);
1836             CS_InsertEntry (S, X, I+4);
1837
1838             /* Insert the store through ptr1 */
1839             X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "ptr1", 0, L[3]->LI);
1840             CS_InsertEntry (S, X, I+6+K);
1841
1842             /* Delete the call to staspidx */
1843             CS_DelEntry (S, I+7+K);
1844
1845             /* Remember, we had changes */
1846             ++Changes;
1847
1848         }
1849
1850         /* Next entry */
1851         ++I;
1852
1853     }
1854
1855     /* Return the number of changes made */
1856     return Changes;
1857 }
1858
1859
1860
1861 static unsigned OptPtrStore2 (CodeSeg* S)
1862 /* Search for the sequence:
1863  *
1864  *      jsr     pushax
1865  *      lda     xxx
1866  *      ldy     yyy
1867  *      jsr     staspidx
1868  *
1869  * and replace it by:
1870  *
1871  *      sta     ptr1
1872  *      stx     ptr1+1
1873  *      lda     xxx
1874  *      ldy     yyy
1875  *      sta     (ptr1),y
1876  */
1877 {
1878     unsigned Changes = 0;
1879
1880     /* Walk over the entries */
1881     unsigned I = 0;
1882     while (I < CS_GetEntryCount (S)) {
1883
1884         CodeEntry* L[4];
1885
1886         /* Get next entry */
1887         L[0] = CS_GetEntry (S, I);
1888
1889         /* Check for the sequence */
1890         if (L[0]->OPC == OP65_JSR               &&
1891             strcmp (L[0]->Arg, "pushax") == 0   &&
1892             CS_GetEntries (S, L+1, I+1, 3)      &&
1893             L[1]->OPC == OP65_LDA               &&
1894             !CE_HasLabel (L[1])                 &&
1895             L[2]->OPC == OP65_LDY               &&
1896             !CE_HasLabel (L[2])                 &&
1897             L[3]->OPC == OP65_JSR               &&
1898             strcmp (L[3]->Arg, "staspidx") == 0 &&
1899             !CE_HasLabel (L[3])) {
1900
1901             CodeEntry* X;
1902
1903             /* Create and insert the stores */
1904             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
1905             CS_InsertEntry (S, X, I+1);
1906
1907             X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
1908             CS_InsertEntry (S, X, I+2);
1909
1910             /* Delete the call to pushax */
1911             CS_DelEntry (S, I);
1912
1913             /* Insert the store through ptr1 */
1914             X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "ptr1", 0, L[3]->LI);
1915             CS_InsertEntry (S, X, I+4);
1916
1917             /* Delete the call to staspidx */
1918             CS_DelEntry (S, I+5);
1919
1920             /* Remember, we had changes */
1921             ++Changes;
1922
1923         }
1924
1925         /* Next entry */
1926         ++I;
1927
1928     }
1929
1930     /* Return the number of changes made */
1931     return Changes;
1932 }
1933
1934
1935
1936 /*****************************************************************************/
1937 /*                      Optimize loads through pointers                      */
1938 /*****************************************************************************/
1939
1940
1941
1942 static unsigned OptPtrLoad1 (CodeSeg* S)
1943 /* Search for the sequence:
1944  *
1945  *      tax
1946  *      dey
1947  *      lda     (sp),y             # May be any destination
1948  *      ldy     ...
1949  *      jsr     ldauidx
1950  *
1951  * and replace it by:
1952  *
1953  *      sta     ptr1+1
1954  *      dey
1955  *      lda     (sp),y
1956  *      sta     ptr1
1957  *      ldy     ...
1958  *      ldx     #$00
1959  *      lda     (ptr1),y
1960  */
1961 {
1962     unsigned Changes = 0;
1963
1964     /* Walk over the entries */
1965     unsigned I = 0;
1966     while (I < CS_GetEntryCount (S)) {
1967
1968         CodeEntry* L[5];
1969
1970         /* Get next entry */
1971         L[0] = CS_GetEntry (S, I);
1972
1973         /* Check for the sequence */
1974         if (L[0]->OPC == OP65_TAX               &&
1975             CS_GetEntries (S, L+1, I+1, 4)      &&
1976             L[1]->OPC == OP65_DEY               &&
1977             !CE_HasLabel (L[1])                 &&
1978             L[2]->OPC == OP65_LDA               &&
1979             !CE_HasLabel (L[2])                 &&
1980             L[3]->OPC == OP65_LDY               &&
1981             !CE_HasLabel (L[3])                 &&
1982             L[4]->OPC == OP65_JSR               &&
1983             strcmp (L[4]->Arg, "ldauidx") == 0  &&
1984             !CE_HasLabel (L[4])) {
1985
1986             CodeEntry* X;
1987
1988             /* Store the high byte and remove the TAX instead */
1989             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1+1", 0, L[0]->LI);
1990             CS_InsertEntry (S, X, I+1);
1991             CS_DelEntry (S, I);
1992
1993             /* Store the low byte */
1994             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[2]->LI);
1995             CS_InsertEntry (S, X, I+3);
1996
1997             /* Delete the call to ldauidx */
1998             CS_DelEntry (S, I+5);
1999
2000             /* Load high and low byte */
2001             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[3]->LI);
2002             CS_InsertEntry (S, X, I+5);
2003             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[3]->LI);
2004             CS_InsertEntry (S, X, I+6);
2005
2006             /* Remember, we had changes */
2007             ++Changes;
2008
2009         }
2010
2011         /* Next entry */
2012         ++I;
2013
2014     }
2015
2016     /* Return the number of changes made */
2017     return Changes;
2018 }
2019
2020
2021
2022 static unsigned OptPtrLoad2 (CodeSeg* S)
2023 /* Search for the sequence:
2024  *
2025  *      adc     xxx
2026  *      tay
2027  *      txa
2028  *      adc     yyy
2029  *      tax
2030  *      tya
2031  *      ldy
2032  *      jsr     ldauidx
2033  *
2034  * and replace it by:
2035  *
2036  *      adc     xxx
2037  *      sta     ptr1
2038  *      txa
2039  *      adc     yyy
2040  *      sta     ptr1+1
2041  *      ldy
2042  *      ldx     #$00
2043  *      lda     (ptr1),y
2044  */
2045 {
2046     unsigned Changes = 0;
2047
2048     /* Walk over the entries */
2049     unsigned I = 0;
2050     while (I < CS_GetEntryCount (S)) {
2051
2052         CodeEntry* L[8];
2053
2054         /* Get next entry */
2055         L[0] = CS_GetEntry (S, I);
2056
2057         /* Check for the sequence */
2058         if (L[0]->OPC == OP65_ADC               &&
2059             CS_GetEntries (S, L+1, I+1, 7)      &&
2060             L[1]->OPC == OP65_TAY               &&
2061             !CE_HasLabel (L[1])                 &&
2062             L[2]->OPC == OP65_TXA               &&
2063             !CE_HasLabel (L[2])                 &&
2064             L[3]->OPC == OP65_ADC               &&
2065             !CE_HasLabel (L[3])                 &&
2066             L[4]->OPC == OP65_TAX               &&
2067             !CE_HasLabel (L[4])                 &&
2068             L[5]->OPC == OP65_TYA               &&
2069             !CE_HasLabel (L[5])                 &&
2070             L[6]->OPC == OP65_LDY               &&
2071             !CE_HasLabel (L[6])                 &&
2072             L[7]->OPC == OP65_JSR               &&
2073             strcmp (L[7]->Arg, "ldauidx") == 0  &&
2074             !CE_HasLabel (L[7])) {
2075
2076             CodeEntry* X;
2077
2078             /* Store the low byte and remove the TAY instead */
2079             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
2080             CS_InsertEntry (S, X, I+1);
2081             CS_DelEntry (S, I+2);
2082
2083             /* Store the high byte */
2084             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1+1", 0, L[3]->LI);
2085             CS_InsertEntry (S, X, I+4);
2086
2087             /* Delete more transfer insns */
2088             CS_DelEntry (S, I+6);
2089             CS_DelEntry (S, I+5);
2090
2091             /* Delete the call to ldauidx */
2092             CS_DelEntry (S, I+6);
2093
2094             /* Load high and low byte */
2095             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[6]->LI);
2096             CS_InsertEntry (S, X, I+6);
2097             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[6]->LI);
2098             CS_InsertEntry (S, X, I+7);
2099
2100             /* Remember, we had changes */
2101             ++Changes;
2102
2103         }
2104
2105         /* Next entry */
2106         ++I;
2107
2108     }
2109
2110     /* Return the number of changes made */
2111     return Changes;
2112 }
2113
2114
2115
2116 static unsigned OptPtrLoad3 (CodeSeg* S)
2117 /* Search for the sequence:
2118  *
2119  *      adc     xxx
2120  *      pha
2121  *      txa
2122  *      iny
2123  *      adc     yyy
2124  *      tax
2125  *      pla
2126  *      ldy
2127  *      jsr     ldauidx
2128  *
2129  * and replace it by:
2130  *
2131  *      adc     xxx
2132  *      sta     ptr1
2133  *      txa
2134  *      iny
2135  *      adc     yyy
2136  *      sta     ptr1+1
2137  *      ldy
2138  *      ldx     #$00
2139  *      lda     (ptr1),y
2140  */
2141 {
2142     unsigned Changes = 0;
2143
2144     /* Walk over the entries */
2145     unsigned I = 0;
2146     while (I < CS_GetEntryCount (S)) {
2147
2148         CodeEntry* L[9];
2149
2150         /* Get next entry */
2151         L[0] = CS_GetEntry (S, I);
2152
2153         /* Check for the sequence */
2154         if (L[0]->OPC == OP65_ADC               &&
2155             CS_GetEntries (S, L+1, I+1, 8)      &&
2156             L[1]->OPC == OP65_PHA               &&
2157             !CE_HasLabel (L[1])                 &&
2158             L[2]->OPC == OP65_TXA               &&
2159             !CE_HasLabel (L[2])                 &&
2160             L[3]->OPC == OP65_INY               &&
2161             !CE_HasLabel (L[3])                 &&
2162             L[4]->OPC == OP65_ADC               &&
2163             !CE_HasLabel (L[4])                 &&
2164             L[5]->OPC == OP65_TAX               &&
2165             !CE_HasLabel (L[5])                 &&
2166             L[6]->OPC == OP65_PLA               &&
2167             !CE_HasLabel (L[6])                 &&
2168             L[7]->OPC == OP65_LDY               &&
2169             !CE_HasLabel (L[7])                 &&
2170             L[8]->OPC == OP65_JSR               &&
2171             strcmp (L[8]->Arg, "ldauidx") == 0  &&
2172             !CE_HasLabel (L[8])) {
2173
2174             CodeEntry* X;
2175
2176             /* Store the low byte and remove the PHA instead */
2177             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
2178             CS_InsertEntry (S, X, I+1);
2179             CS_DelEntry (S, I+2);
2180
2181             /* Store the high byte */
2182             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1+1", 0, L[4]->LI);
2183             CS_InsertEntry (S, X, I+5);
2184
2185             /* Delete more transfer and PLA insns */
2186             CS_DelEntry (S, I+7);
2187             CS_DelEntry (S, I+6);
2188
2189             /* Delete the call to ldauidx */
2190             CS_DelEntry (S, I+7);
2191
2192             /* Load high and low byte */
2193             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[6]->LI);
2194             CS_InsertEntry (S, X, I+7);
2195             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[6]->LI);
2196             CS_InsertEntry (S, X, I+8);
2197
2198             /* Remember, we had changes */
2199             ++Changes;
2200
2201         }
2202
2203         /* Next entry */
2204         ++I;
2205
2206     }
2207
2208     /* Return the number of changes made */
2209     return Changes;
2210 }
2211
2212
2213
2214 static unsigned OptPtrLoad4 (CodeSeg* S)
2215 /* Search for the sequence
2216  *
2217  *      ldy     ...
2218  *      jsr     ldauidx
2219  *
2220  * and replace it by:
2221  *
2222  *      ldy     ...
2223  *      stx     ptr1+1
2224  *      sta     ptr1
2225  *      ldx     #$00
2226  *      lda     (ptr1),y
2227  *
2228  * This step must be execute *after* OptPtrLoad1!
2229  */
2230 {
2231     unsigned Changes = 0;
2232
2233     /* Walk over the entries */
2234     unsigned I = 0;
2235     while (I < CS_GetEntryCount (S)) {
2236
2237         CodeEntry* L[2];
2238
2239         /* Get next entry */
2240         L[0] = CS_GetEntry (S, I);
2241
2242         /* Check for the sequence */
2243         if (L[0]->OPC == OP65_LDY               &&
2244             CS_GetEntries (S, L+1, I+1, 1)      &&
2245             L[1]->OPC == OP65_JSR               &&
2246             strcmp (L[1]->Arg, "ldauidx") == 0  &&
2247             !CE_HasLabel (L[1])) {
2248
2249             CodeEntry* X;
2250
2251             /* Store the high byte */
2252             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
2253             CS_InsertEntry (S, X, I+1);
2254
2255             /* Store the low byte */
2256             X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
2257             CS_InsertEntry (S, X, I+2);
2258
2259             /* Delete the call to ldauidx */
2260             CS_DelEntry (S, I+3);
2261
2262             /* Load the high and low byte */
2263             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[0]->LI);
2264             CS_InsertEntry (S, X, I+3);
2265             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[0]->LI);
2266             CS_InsertEntry (S, X, I+4);
2267
2268             /* Remember, we had changes */
2269             ++Changes;
2270
2271         }
2272
2273         /* Next entry */
2274         ++I;
2275
2276     }
2277
2278     /* Return the number of changes made */
2279     return Changes;
2280 }
2281
2282
2283
2284 /*****************************************************************************/
2285 /*                                   Code                                    */
2286 /*****************************************************************************/
2287
2288
2289
2290 /* Types of optimization steps */
2291 enum {
2292     optPre,                     /* Repeated once */
2293     optPreMain,                 /* Repeated more than once */
2294     optMain,                    /* dito */
2295     optPostMain,                /* dito */
2296     optPost                     /* Repeated once */
2297 };
2298
2299 /* Table with all the optimization functions */
2300 typedef struct OptFunc OptFunc;
2301 struct OptFunc {
2302     unsigned        (*Func) (CodeSeg*); /* Optimizer function */
2303     const char*     Name;               /* Name of optimizer step */
2304     unsigned char   Type;               /* Type of this step */
2305     char            Disabled;           /* True if pass disabled */
2306 };
2307
2308 /* Macro that builds a table entry */
2309 #define OptEntry(func,type)     { func, #func, type, 0 }
2310
2311 /* Table with optimizer steps */
2312 static OptFunc OptFuncs [] = {
2313     /* Optimizes stores through pointers */
2314     OptEntry (OptPtrStore1, optPre),
2315     OptEntry (OptPtrStore2, optPre),
2316     /* Optimize loads through pointers */
2317     OptEntry (OptPtrLoad1, optPre),
2318     OptEntry (OptPtrLoad2, optPre),
2319     OptEntry (OptPtrLoad3, optPre),
2320     OptEntry (OptPtrLoad4, optMain),
2321     /* Optimize calls to nega */
2322     OptEntry (OptNegA1, optMain),
2323     OptEntry (OptNegA2, optMain),
2324     /* Optimize calls to negax */
2325     OptEntry (OptNegAX1, optPre),
2326     OptEntry (OptNegAX2, optPre),
2327     OptEntry (OptNegAX3, optPre),
2328     OptEntry (OptNegAX4, optPre),
2329     /* Optimize subtractions */
2330     OptEntry (OptSub1, optMain),
2331     OptEntry (OptSub2, optMain),
2332     /* Optimize additions */
2333     OptEntry (OptAdd1, optPre),
2334     OptEntry (OptAdd2, optPre),
2335     OptEntry (OptAdd3, optMain),
2336     /* Optimize shifts */
2337     OptEntry (OptShift1, optPre),
2338     /* Optimize jump cascades */
2339     OptEntry (OptJumpCascades, optMain),
2340     /* Remove dead jumps */
2341     OptEntry (OptDeadJumps, optMain),
2342     /* Change jsr/rts to jmp */
2343     OptEntry (OptRTS, optMain),
2344     /* Remove dead code */
2345     OptEntry (OptDeadCode, optMain),
2346     /* Optimize jump targets */
2347     OptEntry (OptJumpTarget, optMain),
2348     /* Optimize conditional branches */
2349     OptEntry (OptCondBranches, optMain),
2350     /* Replace jumps to RTS by RTS */
2351     OptEntry (OptRTSJumps, optMain),
2352     /* Remove calls to the bool transformer subroutines */
2353     OptEntry (OptBoolTransforms, optMain),
2354     /* Optimize compares */
2355     OptEntry (OptCmp1, optMain),
2356     OptEntry (OptCmp2, optMain),
2357     OptEntry (OptCmp3, optMain),
2358     OptEntry (OptCmp4, optMain),
2359     OptEntry (OptCmp5, optMain),
2360     OptEntry (OptCmp6, optMain),
2361     /* Optimize tests */
2362     OptEntry (OptTest1, optMain),
2363     /* Remove unused loads */
2364     OptEntry (OptUnusedLoads, optMain),
2365     OptEntry (OptDuplicateLoads, optMain),
2366     OptEntry (OptStoreLoad, optMain),
2367     OptEntry (OptTransfers, optMain),
2368     /* Optimize branch distance */
2369     OptEntry (OptBranchDist, optPost),
2370 };
2371
2372
2373
2374 static OptFunc* FindOptStep (const char* Name)
2375 /* Find an optimizer step by name in the table and return a pointer. Print an
2376  * error and call AbEnd if not found.
2377  */
2378 {
2379     unsigned I;
2380
2381     /* Run all optimization steps */
2382     for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2383         if (strcmp (OptFuncs[I].Name, Name) == 0) {
2384             /* Found */
2385             return OptFuncs+I;
2386         }
2387     }
2388
2389     /* Not found */
2390     AbEnd ("Optimization step `%s' not found", Name);
2391     return 0;
2392 }
2393
2394
2395
2396 void DisableOpt (const char* Name)
2397 /* Disable the optimization with the given name */
2398 {
2399     if (strcmp (Name, "any") == 0) {
2400         unsigned I;
2401         for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2402             OptFuncs[I].Disabled = 1;
2403         }
2404     } else {
2405         OptFunc* F = FindOptStep (Name);
2406         F->Disabled = 1;
2407     }
2408 }
2409
2410
2411
2412 void EnableOpt (const char* Name)
2413 /* Enable the optimization with the given name */
2414 {
2415     if (strcmp (Name, "any") == 0) {
2416         unsigned I;
2417         for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2418             OptFuncs[I].Disabled = 0;
2419         }
2420     } else {
2421         OptFunc* F = FindOptStep (Name);
2422         F->Disabled = 0;
2423     }
2424 }
2425
2426
2427
2428 void ListOptSteps (FILE* F)
2429 /* List all optimization steps */
2430 {
2431     unsigned I;
2432     for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2433         fprintf (F, "%s\n", OptFuncs[I].Name);
2434     }
2435 }
2436
2437
2438
2439 static void RepeatOptStep (CodeSeg* S, unsigned char Type, unsigned Max)
2440 /* Repeat the optimizer step of type Type at may Max times */
2441 {
2442     unsigned I;
2443     unsigned Pass = 0;
2444     unsigned OptChanges;
2445
2446     /* Repeat max times of until there are no more changes */
2447     do {
2448         /* Reset the number of changes */
2449         OptChanges = 0;
2450
2451         /* Keep the user hapy */
2452         Print (stdout, 1, "  Optimizer pass %u:\n", ++Pass);
2453
2454         /* Run all optimization steps */
2455         for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
2456
2457             /* Get the table entry */
2458             const OptFunc* F = OptFuncs + I;
2459
2460             /* Check if the type matches */
2461             if (F->Type != Type) {
2462                 /* Skip it */
2463                 continue;
2464             }
2465
2466             /* Print the name of the following optimizer step */
2467             Print (stdout, 1, "    %s:%*s", F->Name, (int) (30-strlen(F->Name)), "");
2468
2469             /* Check if the step is disabled */
2470             if (F->Disabled) {
2471                 Print (stdout, 1, "Disabled\n");
2472             } else {
2473                 unsigned Changes = F->Func (S);
2474                 OptChanges += Changes;
2475                 Print (stdout, 1, "%u Changes\n", Changes);
2476             }
2477         }
2478
2479     } while (--Max > 0 && OptChanges > 0);
2480 }
2481
2482
2483
2484 void RunOpt (CodeSeg* S)
2485 /* Run the optimizer */
2486 {
2487
2488     /* If we shouldn't run the optimizer, bail out */
2489     if (!Optimize) {
2490         return;
2491     }
2492
2493     /* Print the name of the function we are working on */
2494     if (S->Func) {
2495         Print (stdout, 1, "Running optimizer for function `%s'\n", S->Func->Name);
2496     } else {
2497         Print (stdout, 1, "Running optimizer for global code segment\n");
2498     }
2499
2500     /* Repeat all steps until there are no more changes */
2501     RepeatOptStep (S, optPre, 1);
2502     RepeatOptStep (S, optPreMain, 0xFFFF);
2503     RepeatOptStep (S, optMain, 0xFFFF);
2504     RepeatOptStep (S, optPostMain, 0xFFFF);
2505     RepeatOptStep (S, optPost, 1);
2506 }
2507
2508
2509