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