]> git.sur5r.net Git - cc65/blob - src/cc65/coptcmp.c
c185ecc27ad9432bf3b5afb1b8374723ed661190
[cc65] / src / cc65 / coptcmp.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 coptcmp.c                                 */
4 /*                                                                           */
5 /*                             Optimize compares                             */
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 /* cc65 */
39 #include "codeent.h"
40 #include "codeinfo.h"
41 #include "error.h"
42 #include "coptcmp.h"
43
44
45
46 /*****************************************************************************/
47 /*                                   Data                                    */
48 /*****************************************************************************/
49
50
51
52 /* Defines for the conditions in a compare */
53 typedef enum {
54     CMP_INV = -1,
55     CMP_EQ,
56     CMP_NE,
57     CMP_GT,
58     CMP_GE,
59     CMP_LT,
60     CMP_LE,
61     CMP_UGT,
62     CMP_UGE,
63     CMP_ULT,
64     CMP_ULE
65 } cmp_t;
66
67 /* Table with the compare suffixes */
68 static const char CmpSuffixTab [][4] = {
69     "eq", "ne", "gt", "ge", "lt", "le", "ugt", "uge", "ult", "ule"
70 };
71
72 /* Table used to invert a condition, indexed by condition */
73 static const unsigned char CmpInvertTab [] = {
74     CMP_NE, CMP_EQ,
75     CMP_LE, CMP_LT, CMP_GE, CMP_GT,
76     CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT
77 };
78
79 /* Table to show which compares are signed (use the N flag) */
80 static const char CmpSignedTab [] = {
81     0, 0, 1, 1, 1, 1, 0, 0, 0, 0
82 };
83
84
85
86 /*****************************************************************************/
87 /*                             Helper functions                              */
88 /*****************************************************************************/
89
90
91
92 static cmp_t FindCmpCond (const char* Code, unsigned CodeLen)
93 /* Search for a compare condition by the given code using the given length */
94 {
95     unsigned I;
96
97     /* Linear search */
98     for (I = 0; I < sizeof (CmpSuffixTab) / sizeof (CmpSuffixTab [0]); ++I) {
99         if (strncmp (Code, CmpSuffixTab [I], CodeLen) == 0) {
100             /* Found */
101             return I;
102         }
103     }
104
105     /* Not found */
106     return CMP_INV;
107 }
108
109
110
111 static cmp_t FindBoolCmpCond (const char* Name)
112 /* Map a condition suffix to a code. Return the code or CMP_INV on failure */
113 {
114     /* Check for the correct subroutine name */
115     if (strncmp (Name, "bool", 4) == 0) {
116         /* Name is ok, search for the code in the table */
117         return FindCmpCond (Name+4, strlen(Name)-4);
118     } else {
119         /* Not found */
120         return CMP_INV;
121     }
122 }
123
124
125
126 static cmp_t FindTosCmpCond (const char* Name)
127 /* Check if this is a call to one of the TOS compare functions (tosgtax).
128  * Return the condition code or CMP_INV on failure.
129  */
130 {
131     unsigned Len = strlen (Name);
132
133     /* Check for the correct subroutine name */
134     if (strncmp (Name, "tos", 3) == 0 && strcmp (Name+Len-2, "ax") == 0) {
135         /* Name is ok, search for the code in the table */
136         return FindCmpCond (Name+3, Len-3-2);
137     } else {
138         /* Not found */
139         return CMP_INV;
140     }
141 }
142
143
144
145 static void ReplaceCmp (CodeSeg* S, unsigned I, cmp_t Cond)
146 /* Helper function for the replacement of routines that return a boolean
147  * followed by a conditional jump. Instead of the boolean value, the condition
148  * codes are evaluated directly.
149  * I is the index of the conditional branch, the sequence is already checked
150  * to be correct.
151  */
152 {
153     CodeEntry* N;
154     CodeLabel* L;
155
156     /* Get the entry */
157     CodeEntry* E = CS_GetEntry (S, I);
158
159     /* Replace the conditional branch */
160     switch (Cond) {
161
162         case CMP_EQ:
163             CE_ReplaceOPC (E, OP65_JEQ);
164             break;
165
166         case CMP_NE:
167             CE_ReplaceOPC (E, OP65_JNE);
168             break;
169
170         case CMP_GT:
171             /* Replace by
172              *     beq @L
173              *     jpl Target
174              * @L: ...
175              */
176             if ((N = CS_GetNextEntry (S, I)) == 0) {
177                 /* No such entry */
178                 Internal ("Invalid program flow");
179             }
180             L = CS_GenLabel (S, N);
181             N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI);
182             CS_InsertEntry (S, N, I);
183             CE_ReplaceOPC (E, OP65_JPL);
184             break;
185
186         case CMP_GE:
187             CE_ReplaceOPC (E, OP65_JPL);
188             break;
189
190         case CMP_LT:
191             CE_ReplaceOPC (E, OP65_JMI);
192             break;
193
194         case CMP_LE:
195             /* Replace by
196              *     jmi Target
197              *     jeq Target
198              */
199             CE_ReplaceOPC (E, OP65_JMI);
200             L = E->JumpTo;
201             N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI);
202             CS_InsertEntry (S, N, I+1);
203             break;
204
205         case CMP_UGT:
206             /* Replace by
207              *     beq @L
208              *     jcs Target
209              * @L: ...
210              */
211             if ((N = CS_GetNextEntry (S, I)) == 0) {
212                 /* No such entry */
213                 Internal ("Invalid program flow");
214             }
215             L = CS_GenLabel (S, N);
216             N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI);
217             CS_InsertEntry (S, N, I);
218             CE_ReplaceOPC (E, OP65_JCS);
219             break;
220
221         case CMP_UGE:
222             CE_ReplaceOPC (E, OP65_JCS);
223             break;
224
225         case CMP_ULT:
226             CE_ReplaceOPC (E, OP65_JCC);
227             break;
228
229         case CMP_ULE:
230             /* Replace by
231              *     jcc Target
232              *     jeq Target
233              */
234             CE_ReplaceOPC (E, OP65_JCC);
235             L = E->JumpTo;
236             N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI);
237             CS_InsertEntry (S, N, I+1);
238             break;
239
240         default:
241             Internal ("Unknown jump condition: %d", Cond);
242
243     }
244
245 }
246
247
248
249 static int IsImmCmp16 (CodeEntry** L)
250 /* Check if the instructions at L are an immidiate compare of a/x:
251  *
252  *
253  */
254 {
255     return (L[0]->OPC == OP65_CPX                              &&
256             L[0]->AM == AM65_IMM                               &&
257             (L[0]->Flags & CEF_NUMARG) != 0                    &&
258             !CE_HasLabel (L[0])                                &&
259             (L[1]->OPC == OP65_JNE || L[1]->OPC == OP65_BNE)   &&
260             L[1]->JumpTo != 0                                  &&
261             !CE_HasLabel (L[1])                                &&
262             L[2]->OPC == OP65_CMP                              &&
263             L[2]->AM == AM65_IMM                               &&
264             (L[2]->Flags & CEF_NUMARG) != 0                    &&
265             (L[3]->Info & OF_CBRA) != 0                        &&
266             L[3]->JumpTo != 0                                  &&
267             (L[1]->JumpTo->Owner == L[3] || L[1]->JumpTo == L[3]->JumpTo));
268 }
269
270
271
272 static int GetCmpRegVal (const CodeEntry* E)
273 /* Return the register value for an immediate compare */
274 {
275     switch (E->OPC) {
276         case OP65_CMP: return E->RI->In.RegA;
277         case OP65_CPX: return E->RI->In.RegX;
278         case OP65_CPY: return E->RI->In.RegY;
279         default:       Internal ("Invalid opcode in GetCmpRegVal");
280                        return 0;  /* Not reached */
281     }
282 }
283
284
285
286 /*****************************************************************************/
287 /*             Remove calls to the bool transformer subroutines              */
288 /*****************************************************************************/
289
290
291
292 unsigned OptBoolTrans (CodeSeg* S)
293 /* Try to remove the call to boolean transformer routines where the call is
294  * not really needed.
295  */
296 {
297     unsigned Changes = 0;
298
299     /* Walk over the entries */
300     unsigned I = 0;
301     while (I < CS_GetEntryCount (S)) {
302
303         CodeEntry* N;
304         cmp_t Cond;
305
306         /* Get next entry */
307         CodeEntry* E = CS_GetEntry (S, I);
308
309         /* Check for a boolean transformer */
310         if (E->OPC == OP65_JSR                           &&
311             (Cond = FindBoolCmpCond (E->Arg)) != CMP_INV &&
312             (N = CS_GetNextEntry (S, I)) != 0        &&
313             (N->Info & OF_ZBRA) != 0) {
314
315             /* Make the boolean transformer unnecessary by changing the
316              * the conditional jump to evaluate the condition flags that
317              * are set after the compare directly. Note: jeq jumps if
318              * the condition is not met, jne jumps if the condition is met.
319              * Invert the code if we jump on condition not met.
320              */
321             if (GetBranchCond (N->OPC) == BC_EQ) {
322                 /* Jumps if condition false, invert condition */
323                 Cond = CmpInvertTab [Cond];
324             }
325
326             /* Check if we can replace the code by something better */
327             ReplaceCmp (S, I+1, Cond);
328
329             /* Remove the call to the bool transformer */
330             CS_DelEntry (S, I);
331
332             /* Remember, we had changes */
333             ++Changes;
334
335         }
336
337         /* Next entry */
338         ++I;
339
340     }
341
342     /* Return the number of changes made */
343     return Changes;
344 }
345
346
347
348 /*****************************************************************************/
349 /*                        Optimizations for compares                         */
350 /*****************************************************************************/
351
352
353
354 unsigned OptCmp1 (CodeSeg* S)
355 /* Search for the sequence
356  *
357  *      stx     xx
358  *      stx     tmp1
359  *      ora     tmp1
360  *
361  * and replace it by
362  *
363  *      stx     xx
364  *      ora     xx
365  */
366 {
367     unsigned Changes = 0;
368
369     /* Walk over the entries */
370     unsigned I = 0;
371     while (I < CS_GetEntryCount (S)) {
372
373         CodeEntry* L[2];
374
375         /* Get next entry */
376         CodeEntry* E = CS_GetEntry (S, I);
377
378         /* Check for the sequence */
379         if (E->OPC == OP65_STX                  &&
380             !CS_RangeHasLabel (S, I+1, 2)       &&
381             CS_GetEntries (S, L, I+1, 2)        &&
382             L[0]->OPC == OP65_STX               &&
383             strcmp (L[0]->Arg, "tmp1") == 0     &&
384             L[1]->OPC == OP65_ORA               &&
385             strcmp (L[1]->Arg, "tmp1") == 0) {
386
387             /* Remove the remaining instructions */
388             CS_DelEntries (S, I+1, 2);
389
390             /* Insert the ora instead */
391             CS_InsertEntry (S, NewCodeEntry (OP65_ORA, E->AM, E->Arg, 0, E->LI), I+1);
392
393             /* Remember, we had changes */
394             ++Changes;
395
396         }
397
398         /* Next entry */
399         ++I;
400
401     }
402
403     /* Return the number of changes made */
404     return Changes;
405 }
406
407
408
409 unsigned OptCmp2 (CodeSeg* S)
410 /* Search for
411  *
412  *      lda/and/ora/eor ...
413  *      cmp #$00
414  *      jeq/jne
415  * or
416  *      lda/and/ora/eor ...
417  *      cmp #$00
418  *      jsr boolxx
419  *
420  * and remove the cmp.
421  */
422 {
423     unsigned Changes = 0;
424
425     /* Walk over the entries */
426     unsigned I = 0;
427     while (I < CS_GetEntryCount (S)) {
428
429         CodeEntry* L[3];
430
431         /* Get next entry */
432         L[0] = CS_GetEntry (S, I);
433
434         /* Check for the sequence */
435         if ((L[0]->OPC == OP65_ADC ||
436              L[0]->OPC == OP65_AND ||
437              L[0]->OPC == OP65_DEA ||
438              L[0]->OPC == OP65_EOR ||
439              L[0]->OPC == OP65_INA ||
440              L[0]->OPC == OP65_LDA ||
441              L[0]->OPC == OP65_ORA ||
442              L[0]->OPC == OP65_PLA ||
443              L[0]->OPC == OP65_SBC ||
444              L[0]->OPC == OP65_TXA ||
445              L[0]->OPC == OP65_TYA)         &&
446             !CS_RangeHasLabel (S, I+1, 2)   &&
447             CS_GetEntries (S, L+1, I+1, 2)   &&
448             L[1]->OPC == OP65_CMP           &&
449             CE_KnownImm (L[1])              &&
450             L[1]->Num == 0) {
451
452             /* Check for the call to boolxx. We cannot remove the compare if
453              * the carry flag is evaluated later, because the load will not
454              * set the carry flag.
455              */
456             if (L[2]->OPC == OP65_JSR) {
457                 switch (FindBoolCmpCond (L[2]->Arg)) {
458
459                     case CMP_EQ:
460                     case CMP_NE:
461                     case CMP_GT:
462                     case CMP_GE:
463                     case CMP_LT:
464                     case CMP_LE:
465                         /* Remove the compare */
466                         CS_DelEntry (S, I+1);
467                         ++Changes;
468                         break;
469
470                     case CMP_UGT:
471                     case CMP_UGE:
472                     case CMP_ULT:
473                     case CMP_ULE:
474                     case CMP_INV:
475                         /* Leave it alone */
476                         break;
477                 }
478
479             } else {
480
481                 /* Check for a branch on conditions that are set by the load.
482                  * Beware: The insn may branch to another conditional branch
483                  * that evaluates other flags, so check that.
484                  */
485                 CodeEntry* E = L[2];
486                 int Delete = 0;
487                 while (1) {
488                     if ((E->Info & (OF_CBRA|OF_UBRA)) != 0) {
489                         /* A conditional branch. Check if it jumps on a
490                          * condition not set by the load.
491                          */
492                         if ((E->Info & (OF_FBRA|OF_UBRA)) == 0) {
493                             /* Invalid branch */
494                             break;
495                         } else if (E->JumpTo == 0) {
496                             /* Jump to external */
497                             Delete = 1;
498                             break;
499                         } else {
500                             /* Check target of branch */
501                             E = E->JumpTo->Owner;
502                         }
503                     } else {
504                         /* Some other insn */
505                         Delete = 1;
506                         break;
507                     }
508                 }
509
510                 /* Delete the compare if we can */
511                 if (Delete) {
512                     CS_DelEntry (S, I+1);
513                     ++Changes;
514                 }
515             }
516         }
517
518         /* Next entry */
519         ++I;
520
521     }
522
523     /* Return the number of changes made */
524     return Changes;
525 }
526
527
528
529 unsigned OptCmp3 (CodeSeg* S)
530 /* Search for
531  *
532  *      lda     x
533  *      ldx     y
534  *      cpx     #a
535  *      bne     L1
536  *      cmp     #b
537  * L1:  jne/jeq L2
538  *
539  * If a is zero, we may remove the compare. If a and b are both zero, we may
540  * replace it by the sequence
541  *
542  *      lda     x
543  *      ora     x+1
544  *      jne/jeq ...
545  *
546  * L1 may be either the label at the branch instruction, or the target label
547  * of this instruction.
548  */
549 {
550     unsigned Changes = 0;
551
552     /* Walk over the entries */
553     unsigned I = 0;
554     while (I < CS_GetEntryCount (S)) {
555
556         CodeEntry* L[5];
557
558         /* Get next entry */
559         CodeEntry* E = CS_GetEntry (S, I);
560
561         /* Check for the sequence */
562         if (E->OPC == OP65_LDA               &&
563             CS_GetEntries (S, L, I+1, 5)     &&
564             L[0]->OPC == OP65_LDX            &&
565             !CE_HasLabel (L[0])              &&
566             IsImmCmp16 (L+1)                 &&
567             !RegAXUsed (S, I+6)) {
568
569             if ((L[4]->Info & OF_FBRA) != 0 && L[1]->Num == 0 && L[3]->Num == 0) {
570                 /* The value is zero, we may use the simple code version. */
571                 CE_ReplaceOPC (L[0], OP65_ORA);
572                 CS_DelEntries (S, I+2, 3);
573             } else {
574                 /* Move the lda instruction after the first branch. This will
575                  * improve speed, since the load is delayed after the first
576                  * test.
577                  */
578                 CS_MoveEntry (S, I, I+4);
579
580                 /* We will replace the ldx/cpx by lda/cmp */
581                 CE_ReplaceOPC (L[0], OP65_LDA);
582                 CE_ReplaceOPC (L[1], OP65_CMP);
583
584                 /* Beware: If the first LDA instruction had a label, we have
585                  * to move this label to the top of the sequence again.
586                  */
587                 if (CE_HasLabel (E)) {
588                     CS_MoveLabels (S, E, L[0]);
589                 }
590
591             }
592
593             ++Changes;
594         }
595
596         /* Next entry */
597         ++I;
598
599     }
600
601     /* Return the number of changes made */
602     return Changes;
603 }
604
605
606
607 unsigned OptCmp4 (CodeSeg* S)
608 /* Optimize compares of local variables:
609  *
610  *      ldy     #o
611  *      jsr     ldaxysp
612  *      cpx     #a
613  *      bne     L1
614  *      cmp     #b
615  *      jne/jeq L2
616  */
617 {
618     unsigned Changes = 0;
619
620     /* Walk over the entries */
621     unsigned I = 0;
622     while (I < CS_GetEntryCount (S)) {
623
624         CodeEntry* L[6];
625
626         /* Get the next entry */
627         L[0] = CS_GetEntry (S, I);
628
629         /* Check for the sequence */
630         if (L[0]->OPC == OP65_LDY           &&
631             CE_KnownImm (L[0])              &&
632             CS_GetEntries (S, L+1, I+1, 5)  &&
633             !CE_HasLabel (L[1])             &&
634             CE_IsCall (L[1], "ldaxysp")     &&
635             IsImmCmp16 (L+2)) {
636
637             if ((L[5]->Info & OF_FBRA) != 0 && L[2]->Num == 0 && L[4]->Num == 0) {
638
639                 CodeEntry* X;
640                 char Buf[20];
641
642                 /* The value is zero, we may use the simple code version:
643                  *      ldy     #o-1
644                  *      lda     (sp),y
645                  *      ldy     #o
646                  *      ora     (sp),y
647                  *      jne/jeq ...
648                  */
649                 sprintf (Buf, "$%02X", (int)(L[0]->Num-1));
650                 X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[0]->LI);
651                 CS_InsertEntry (S, X, I+1);
652
653                 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI);
654                 CS_InsertEntry (S, X, I+2);
655
656                 X = NewCodeEntry (OP65_LDY, AM65_IMM, L[0]->Arg, 0, L[0]->LI);
657                 CS_InsertEntry (S, X, I+3);
658
659                 X = NewCodeEntry (OP65_ORA, AM65_ZP_INDY, "sp", 0, L[1]->LI);
660                 CS_InsertEntry (S, X, I+4);
661
662                 CS_DelEntries (S, I+5, 3);   /* cpx/bne/cmp */
663                 CS_DelEntry (S, I);          /* ldy */
664
665             } else {
666
667                 CodeEntry* X;
668                 char Buf[20];
669
670                 /* Change the code to just use the A register. Move the load
671                  * of the low byte after the first branch if possible:
672                  *
673                  *      ldy     #o
674                  *      lda     (sp),y
675                  *      cmp     #a
676                  *      bne     L1
677                  *      ldy     #o-1
678                  *      lda     (sp),y
679                  *      cmp     #b
680                  *      jne/jeq ...
681                  */
682                 X = NewCodeEntry (OP65_LDY, AM65_IMM, L[0]->Arg, 0, L[0]->LI);
683                 CS_InsertEntry (S, X, I+3);
684
685                 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI);
686                 CS_InsertEntry (S, X, I+4);
687
688                 X = NewCodeEntry (OP65_CMP, L[2]->AM, L[2]->Arg, 0, L[2]->LI);
689                 CS_InsertEntry (S, X, I+5);
690
691                 sprintf (Buf, "$%02X", (int)(L[0]->Num-1));
692                 X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[0]->LI);
693                 CS_InsertEntry (S, X, I+7);
694
695                 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI);
696                 CS_InsertEntry (S, X, I+8);
697
698                 CS_DelEntries (S, I, 3);          /* ldy/jsr/cpx */
699
700             }
701
702             ++Changes;
703         }
704
705         /* Next entry */
706         ++I;
707
708     }
709
710     /* Return the number of changes made */
711     return Changes;
712 }
713
714
715
716 unsigned OptCmp5 (CodeSeg* S)
717 /* Search for calls to compare subroutines followed by a conditional branch
718  * and replace them by cheaper versions, since the branch means that the
719  * boolean value returned by these routines is not needed (we may also check
720  * that explicitly, but for the current code generator it is always true).
721  */
722 {
723     unsigned Changes = 0;
724
725     /* Walk over the entries */
726     unsigned I = 0;
727     while (I < CS_GetEntryCount (S)) {
728
729         CodeEntry* N;
730         cmp_t Cond;
731
732         /* Get next entry */
733         CodeEntry* E = CS_GetEntry (S, I);
734
735         /* Check for the sequence */
736         if (E->OPC == OP65_JSR                          &&
737             (Cond = FindTosCmpCond (E->Arg)) != CMP_INV &&
738             (N = CS_GetNextEntry (S, I)) != 0           &&
739             (N->Info & OF_ZBRA) != 0                    &&
740             !CE_HasLabel (N)) {
741
742             /* The tos... functions will return a boolean value in a/x and
743              * the Z flag says if this value is zero or not. We will call
744              * a cheaper subroutine instead, one that does not return a
745              * boolean value but only valid flags. Note: jeq jumps if
746              * the condition is not met, jne jumps if the condition is met.
747              * Invert the code if we jump on condition not met.
748              */
749             if (GetBranchCond (N->OPC) == BC_EQ) {
750                 /* Jumps if condition false, invert condition */
751                 Cond = CmpInvertTab [Cond];
752             }
753
754             /* Replace the subroutine call. */
755             E = NewCodeEntry (OP65_JSR, AM65_ABS, "tosicmp", 0, E->LI);
756             CS_InsertEntry (S, E, I+1);
757             CS_DelEntry (S, I);
758
759             /* Replace the conditional branch */
760             ReplaceCmp (S, I+1, Cond);
761
762             /* Remember, we had changes */
763             ++Changes;
764
765         }
766
767         /* Next entry */
768         ++I;
769
770     }
771
772     /* Return the number of changes made */
773     return Changes;
774 }
775
776
777
778 unsigned OptCmp6 (CodeSeg* S)
779 /* Search for a sequence ldx/txa/branch and remove the txa if A is not
780  * used later.
781  */
782 {
783     unsigned Changes = 0;
784
785     /* Walk over the entries */
786     unsigned I = 0;
787     while (I < CS_GetEntryCount (S)) {
788
789         CodeEntry* L[2];
790
791         /* Get next entry */
792         CodeEntry* E = CS_GetEntry (S, I);
793
794         /* Check for the sequence */
795         if ((E->OPC == OP65_LDX)                        &&
796             CS_GetEntries (S, L, I+1, 2)                &&
797             L[0]->OPC == OP65_TXA                       &&
798             !CE_HasLabel (L[0])                         &&
799             (L[1]->Info & OF_FBRA) != 0                 &&
800             !CE_HasLabel (L[1])                         &&
801             !RegAUsed (S, I+3)) {
802
803             /* Remove the txa */
804             CS_DelEntry (S, I+1);
805
806             /* Remember, we had changes */
807             ++Changes;
808
809         }
810
811         /* Next entry */
812         ++I;
813
814     }
815
816     /* Return the number of changes made */
817     return Changes;
818 }
819
820
821
822 unsigned OptCmp7 (CodeSeg* S)
823 /* Check for register compares where the contents of the register and therefore
824  * the result of the compare is known.
825  */
826 {
827     unsigned Changes = 0;
828     unsigned I;
829
830     /* Generate register info for this step */
831     CS_GenRegInfo (S);
832
833     /* Walk over the entries */
834     I = 0;
835     while (I < CS_GetEntryCount (S)) {
836
837         int RegVal;
838
839         /* Get next entry */
840         CodeEntry* E = CS_GetEntry (S, I);
841
842         /* Check for a compare against an immediate value */
843         if ((E->Info & OF_CMP) != 0           &&
844             (RegVal = GetCmpRegVal (E)) >= 0  &&
845             CE_KnownImm (E)) {
846
847             /* We are able to evaluate the compare at compile time. Check if
848              * one or more branches are ahead.
849              */
850             unsigned JumpsChanged = 0;
851             CodeEntry* N;
852             while ((N = CS_GetNextEntry (S, I)) != 0 &&   /* Followed by something.. */
853                    (N->Info & OF_CBRA) != 0          &&   /* ..that is a cond branch.. */
854                    !CE_HasLabel (N)) {                    /* ..and has no label */
855
856                 /* Evaluate the branch condition */
857                 int Cond;
858                 switch (GetBranchCond (N->OPC)) {
859                     case BC_CC:
860                         Cond = ((unsigned char)RegVal) < ((unsigned char)E->Num);
861                         break;
862
863                     case BC_CS:
864                         Cond = ((unsigned char)RegVal) >= ((unsigned char)E->Num);
865                         break;
866
867                     case BC_EQ:
868                         Cond = ((unsigned char)RegVal) == ((unsigned char)E->Num);
869                         break;
870
871                     case BC_MI:
872                         Cond = ((signed char)RegVal) < ((signed char)E->Num);
873                         break;
874
875                     case BC_NE:
876                         Cond = ((unsigned char)RegVal) != ((unsigned char)E->Num);
877                         break;
878
879                     case BC_PL:
880                         Cond = ((signed char)RegVal) >= ((signed char)E->Num);
881                         break;
882
883                     case BC_VC:
884                     case BC_VS:
885                         /* Not set by the compare operation, bail out (Note:
886                          * Just skipping anything here is rather stupid, but
887                          * the sequence is never generated by the compiler,
888                          * so it's quite safe to skip).
889                          */
890                         goto NextEntry;
891
892                     default:
893                         Internal ("Unknown branch condition");
894
895                 }
896
897                 /* If the condition is false, we may remove the jump. Otherwise
898                  * the branch will always be taken, so we may replace it by a
899                  * jump (and bail out).
900                  */
901                 if (!Cond) {
902                     CS_DelEntry (S, I+1);
903                 } else {
904                     CodeLabel* L = N->JumpTo;
905                     const char* LabelName = L? L->Name : N->Arg;
906                     CodeEntry* X = NewCodeEntry (OP65_JMP, AM65_BRA, LabelName, L, N->LI);
907                     CS_InsertEntry (S, X, I+2);
908                     CS_DelEntry (S, I+1);
909                 }
910
911                 /* Remember, we had changes */
912                 ++JumpsChanged;
913                 ++Changes;
914             }
915
916             /* If we have made changes above, we may also remove the compare */
917             if (JumpsChanged) {
918                 CS_DelEntry (S, I);
919             }
920
921         }
922
923 NextEntry:
924         /* Next entry */
925         ++I;
926
927     }
928
929     /* Free register info */
930     CS_FreeRegInfo (S);
931
932     /* Return the number of changes made */
933     return Changes;
934 }
935
936
937
938