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