]> git.sur5r.net Git - cc65/blob - src/cc65/coptcmp.c
71dbfc6246da58ba4065259c774a44adc4ebd1df
[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             int Delete = 0;
438
439             /* Check for the call to boolxx. We only remove the compare if
440              * the carry flag is evaluated later, because the load will not
441              * set the carry flag.
442              */
443             if (L[2]->OPC == OP65_JSR) {
444                 switch (FindBoolCmpCond (L[2]->Arg)) {
445
446                     case CMP_EQ:
447                     case CMP_NE:
448                     case CMP_GT:
449                     case CMP_GE:
450                     case CMP_LT:
451                     case CMP_LE:
452                         /* Remove the compare */
453                         Delete = 1;
454                         break;
455
456                     case CMP_UGT:
457                     case CMP_UGE:
458                     case CMP_ULT:
459                     case CMP_ULE:
460                     case CMP_INV:
461                         /* Leave it alone */
462                         break;
463                 }
464
465             } else if ((L[2]->Info & OF_FBRA) != 0) {
466
467                 /* The following insn branches on the condition of a load, so
468                  * the compare instruction can be removed.
469                  */
470                 Delete = 1;
471
472             }
473
474             /* Delete the compare if we can */
475             if (Delete) {
476                 CS_DelEntry (S, I+1);
477                 ++Changes;
478             }
479         }
480
481         /* Next entry */
482         ++I;
483
484     }
485
486     /* Return the number of changes made */
487     return Changes;
488 }
489
490
491
492 unsigned OptCmp4 (CodeSeg* S)
493 /* Search for
494  *
495  *      lda     x
496  *      ldx     y
497  *      cpx     #a
498  *      bne     L1
499  *      cmp     #b
500  * L1:  jne/jeq L2
501  *
502  * If a is zero, we may remove the compare. If a and b are both zero, we may
503  * replace it by the sequence
504  *
505  *      lda     x
506  *      ora     x+1
507  *      jne/jeq ...
508  *
509  * L1 may be either the label at the branch instruction, or the target label
510  * of this instruction.
511  */
512 {
513     unsigned Changes = 0;
514
515     /* Walk over the entries */
516     unsigned I = 0;
517     while (I < CS_GetEntryCount (S)) {
518
519         CodeEntry* L[5];
520
521         /* Get next entry */
522         CodeEntry* E = CS_GetEntry (S, I);
523
524         /* Check for the sequence */
525         if (E->OPC == OP65_LDA               &&
526             CS_GetEntries (S, L, I+1, 5)     &&
527             L[0]->OPC == OP65_LDX            &&
528             !CE_HasLabel (L[0])              &&
529             IsImmCmp16 (L+1)                 &&
530             !RegAXUsed (S, I+6)) {
531
532             if ((L[4]->Info & OF_FBRA) != 0 && L[1]->Num == 0 && L[3]->Num == 0) {
533                 /* The value is zero, we may use the simple code version. */
534                 CE_ReplaceOPC (L[0], OP65_ORA);
535                 CS_DelEntries (S, I+2, 3);
536             } else {
537                 /* Move the lda instruction after the first branch. This will
538                  * improve speed, since the load is delayed after the first
539                  * test.
540                  */
541                 CS_MoveEntry (S, I, I+4);
542
543                 /* We will replace the ldx/cpx by lda/cmp */
544                 CE_ReplaceOPC (L[0], OP65_LDA);
545                 CE_ReplaceOPC (L[1], OP65_CMP);
546
547                 /* Beware: If the first LDA instruction had a label, we have
548                  * to move this label to the top of the sequence again.
549                  */
550                 if (CE_HasLabel (E)) {
551                     CS_MoveLabels (S, E, L[0]);
552                 }
553
554             }
555
556             ++Changes;
557         }
558
559         /* Next entry */
560         ++I;
561
562     }
563
564     /* Return the number of changes made */
565     return Changes;
566 }
567
568
569
570 unsigned OptCmp5 (CodeSeg* S)
571 /* Optimize compares of local variables:
572  *
573  *      ldy     #o
574  *      jsr     ldaxysp
575  *      cpx     #a
576  *      bne     L1
577  *      cmp     #b
578  *      jne/jeq L2
579  */
580 {
581     unsigned Changes = 0;
582
583     /* Walk over the entries */
584     unsigned I = 0;
585     while (I < CS_GetEntryCount (S)) {
586
587         CodeEntry* L[6];
588
589         /* Get the next entry */
590         L[0] = CS_GetEntry (S, I);
591
592         /* Check for the sequence */
593         if (L[0]->OPC == OP65_LDY           &&
594             CE_IsConstImm (L[0])            &&
595             CS_GetEntries (S, L+1, I+1, 5)  &&
596             !CE_HasLabel (L[1])             &&
597             CE_IsCallTo (L[1], "ldaxysp")   &&
598             IsImmCmp16 (L+2)) {
599
600             if ((L[5]->Info & OF_FBRA) != 0 && L[2]->Num == 0 && L[4]->Num == 0) {
601
602                 CodeEntry* X;
603                 char Buf[20];
604
605                 /* The value is zero, we may use the simple code version:
606                  *      ldy     #o-1
607                  *      lda     (sp),y
608                  *      ldy     #o
609                  *      ora     (sp),y
610                  *      jne/jeq ...
611                  */
612                 sprintf (Buf, "$%02X", (int)(L[0]->Num-1));
613                 X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[0]->LI);
614                 CS_InsertEntry (S, X, I+1);
615
616                 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI);
617                 CS_InsertEntry (S, X, I+2);
618
619                 X = NewCodeEntry (OP65_LDY, AM65_IMM, L[0]->Arg, 0, L[0]->LI);
620                 CS_InsertEntry (S, X, I+3);
621
622                 X = NewCodeEntry (OP65_ORA, AM65_ZP_INDY, "sp", 0, L[1]->LI);
623                 CS_InsertEntry (S, X, I+4);
624
625                 CS_DelEntries (S, I+5, 3);   /* cpx/bne/cmp */
626                 CS_DelEntry (S, I);          /* ldy */
627
628             } else {
629
630                 CodeEntry* X;
631                 char Buf[20];
632
633                 /* Change the code to just use the A register. Move the load
634                  * of the low byte after the first branch if possible:
635                  *
636                  *      ldy     #o
637                  *      lda     (sp),y
638                  *      cmp     #a
639                  *      bne     L1
640                  *      ldy     #o-1
641                  *      lda     (sp),y
642                  *      cmp     #b
643                  *      jne/jeq ...
644                  */
645                 X = NewCodeEntry (OP65_LDY, AM65_IMM, L[0]->Arg, 0, L[0]->LI);
646                 CS_InsertEntry (S, X, I+3);
647
648                 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI);
649                 CS_InsertEntry (S, X, I+4);
650
651                 X = NewCodeEntry (OP65_CMP, L[2]->AM, L[2]->Arg, 0, L[2]->LI);
652                 CS_InsertEntry (S, X, I+5);
653
654                 sprintf (Buf, "$%02X", (int)(L[0]->Num-1));
655                 X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, L[0]->LI);
656                 CS_InsertEntry (S, X, I+7);
657
658                 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI);
659                 CS_InsertEntry (S, X, I+8);
660
661                 CS_DelEntries (S, I, 3);          /* ldy/jsr/cpx */
662
663             }
664
665             ++Changes;
666         }
667
668         /* Next entry */
669         ++I;
670
671     }
672
673     /* Return the number of changes made */
674     return Changes;
675 }
676
677
678
679 unsigned OptCmp6 (CodeSeg* S)
680 /* Search for calls to compare subroutines followed by a conditional branch
681  * and replace them by cheaper versions, since the branch means that the
682  * boolean value returned by these routines is not needed (we may also check
683  * that explicitly, but for the current code generator it is always true).
684  */
685 {
686     unsigned Changes = 0;
687
688     /* Walk over the entries */
689     unsigned I = 0;
690     while (I < CS_GetEntryCount (S)) {
691
692         CodeEntry* N;
693         cmp_t Cond;
694
695         /* Get next entry */
696         CodeEntry* E = CS_GetEntry (S, I);
697
698         /* Check for the sequence */
699         if (E->OPC == OP65_JSR                          &&
700             (Cond = FindTosCmpCond (E->Arg)) != CMP_INV &&
701             (N = CS_GetNextEntry (S, I)) != 0           &&
702             (N->Info & OF_ZBRA) != 0                    &&
703             !CE_HasLabel (N)) {
704
705             /* The tos... functions will return a boolean value in a/x and
706              * the Z flag says if this value is zero or not. We will call
707              * a cheaper subroutine instead, one that does not return a
708              * boolean value but only valid flags. Note: jeq jumps if
709              * the condition is not met, jne jumps if the condition is met.
710              * Invert the code if we jump on condition not met.
711              */
712             if (GetBranchCond (N->OPC) == BC_EQ) {
713                 /* Jumps if condition false, invert condition */
714                 Cond = CmpInvertTab [Cond];
715             }
716
717             /* Replace the subroutine call. */
718             E = NewCodeEntry (OP65_JSR, AM65_ABS, "tosicmp", 0, E->LI);
719             CS_InsertEntry (S, E, I+1);
720             CS_DelEntry (S, I);
721
722             /* Replace the conditional branch */
723             ReplaceCmp (S, I+1, Cond);
724
725             /* Remember, we had changes */
726             ++Changes;
727
728         }
729
730         /* Next entry */
731         ++I;
732
733     }
734
735     /* Return the number of changes made */
736     return Changes;
737 }
738
739
740
741 unsigned OptCmp7 (CodeSeg* S)
742 /* Search for a sequence ldx/txa/branch and remove the txa if A is not
743  * used later.
744  */
745 {
746     unsigned Changes = 0;
747
748     /* Walk over the entries */
749     unsigned I = 0;
750     while (I < CS_GetEntryCount (S)) {
751
752         CodeEntry* L[2];
753
754         /* Get next entry */
755         CodeEntry* E = CS_GetEntry (S, I);
756
757         /* Check for the sequence */
758         if ((E->OPC == OP65_LDX)                        &&
759             CS_GetEntries (S, L, I+1, 2)                &&
760             L[0]->OPC == OP65_TXA                       &&
761             !CE_HasLabel (L[0])                         &&
762             (L[1]->Info & OF_FBRA) != 0                 &&
763             !CE_HasLabel (L[1])                         &&
764             !RegAUsed (S, I+3)) {
765
766             /* Remove the txa */
767             CS_DelEntry (S, I+1);
768
769             /* Remember, we had changes */
770             ++Changes;
771
772         }
773
774         /* Next entry */
775         ++I;
776
777     }
778
779     /* Return the number of changes made */
780     return Changes;
781 }
782
783
784
785 unsigned OptCmp8 (CodeSeg* S)
786 /* Check for register compares where the contents of the register and therefore
787  * the result of the compare is known.
788  */
789 {
790     unsigned Changes = 0;
791     unsigned I;
792
793     /* Generate register info for this step */
794     CS_GenRegInfo (S);
795
796     /* Walk over the entries */
797     I = 0;
798     while (I < CS_GetEntryCount (S)) {
799
800         int RegVal;
801
802         /* Get next entry */
803         CodeEntry* E = CS_GetEntry (S, I);
804
805         /* Check for a compare against an immediate value */
806         if ((E->Info & OF_CMP) != 0           &&
807             (RegVal = GetCmpRegVal (E)) >= 0  &&
808             CE_IsConstImm (E)) {
809
810             /* We are able to evaluate the compare at compile time. Check if
811              * one or more branches are ahead.
812              */
813             unsigned JumpsChanged = 0;
814             CodeEntry* N;
815             while ((N = CS_GetNextEntry (S, I)) != 0 &&   /* Followed by something.. */
816                    (N->Info & OF_CBRA) != 0          &&   /* ..that is a cond branch.. */
817                    !CE_HasLabel (N)) {                    /* ..and has no label */
818
819                 /* Evaluate the branch condition */
820                 int Cond;
821                 switch (GetBranchCond (N->OPC)) {
822                     case BC_CC:
823                         Cond = ((unsigned char)RegVal) < ((unsigned char)E->Num);
824                         break;
825
826                     case BC_CS:
827                         Cond = ((unsigned char)RegVal) >= ((unsigned char)E->Num);
828                         break;
829
830                     case BC_EQ:
831                         Cond = ((unsigned char)RegVal) == ((unsigned char)E->Num);
832                         break;
833
834                     case BC_MI:
835                         Cond = ((signed char)RegVal) < ((signed char)E->Num);
836                         break;
837
838                     case BC_NE:
839                         Cond = ((unsigned char)RegVal) != ((unsigned char)E->Num);
840                         break;
841
842                     case BC_PL:
843                         Cond = ((signed char)RegVal) >= ((signed char)E->Num);
844                         break;
845
846                     case BC_VC:
847                     case BC_VS:
848                         /* Not set by the compare operation, bail out (Note:
849                          * Just skipping anything here is rather stupid, but
850                          * the sequence is never generated by the compiler,
851                          * so it's quite safe to skip).
852                          */
853                         goto NextEntry;
854
855                     default:
856                         Internal ("Unknown branch condition");
857
858                 }
859
860                 /* If the condition is false, we may remove the jump. Otherwise
861                  * the branch will always be taken, so we may replace it by a
862                  * jump (and bail out).
863                  */
864                 if (!Cond) {
865                     CS_DelEntry (S, I+1);
866                 } else {
867                     CodeLabel* L = N->JumpTo;
868                     const char* LabelName = L? L->Name : N->Arg;
869                     CodeEntry* X = NewCodeEntry (OP65_JMP, AM65_BRA, LabelName, L, N->LI);
870                     CS_InsertEntry (S, X, I+2);
871                     CS_DelEntry (S, I+1);
872                 }
873
874                 /* Remember, we had changes */
875                 ++JumpsChanged;
876                 ++Changes;
877             }
878
879             /* If we have made changes above, we may also remove the compare */
880             if (JumpsChanged) {
881                 CS_DelEntry (S, I);
882             }
883
884         }
885
886 NextEntry:
887         /* Next entry */
888         ++I;
889
890     }
891
892     /* Free register info */
893     CS_FreeRegInfo (S);
894
895     /* Return the number of changes made */
896     return Changes;
897 }
898
899
900
901 unsigned OptCmp9 (CodeSeg* S)
902 /* Search for the sequence
903  *
904  *    sbc       xx
905  *    bvs/bvc   L
906  *    eor       #$80
907  * L: asl       a
908  *    bcc/bcs   somewhere
909  *
910  * If A is not used later (which should be the case), we can branch on the N
911  * flag instead of the carry flag and remove the asl.
912  */
913 {
914     unsigned Changes = 0;
915     unsigned I;
916
917     /* Walk over the entries */
918     I = 0;
919     while (I < CS_GetEntryCount (S)) {
920
921         CodeEntry* L[5];
922
923         /* Get next entry */
924         L[0] = CS_GetEntry (S, I);
925
926         /* Check for the sequence */
927         if (L[0]->OPC == OP65_SBC                       &&
928             CS_GetEntries (S, L+1, I+1, 4)              &&
929             (L[1]->OPC == OP65_BVC              ||
930              L[1]->OPC == OP65_BVS)                     &&
931             L[1]->JumpTo != 0                           &&
932             L[1]->JumpTo->Owner == L[3]                 &&
933             L[2]->OPC == OP65_EOR                       &&
934             CE_IsKnownImm (L[2], 0x80)                  &&
935             L[3]->OPC == OP65_ASL                       &&
936             L[3]->AM == AM65_ACC                        &&
937             (L[4]->OPC == OP65_BCC              ||
938              L[4]->OPC == OP65_BCS              ||
939              L[4]->OPC == OP65_JCC              ||
940              L[4]->OPC == OP65_JCS)                     &&
941             !CE_HasLabel (L[4])                         &&
942             !RegAUsed (S, I+4)) {
943
944             /* Replace the branch condition */
945             switch (GetBranchCond (L[4]->OPC)) {
946                 case BC_CC:     CE_ReplaceOPC (L[4], OP65_JPL); break;
947                 case BC_CS:     CE_ReplaceOPC (L[4], OP65_JMI); break;
948                 default:        Internal ("Unknown branch condition in OptCmp9");
949             }
950
951             /* Delete the asl insn */
952             CS_DelEntry (S, I+3);
953
954             /* Next sequence is somewhat ahead (if any) */
955             I += 3;
956
957             /* Remember, we had changes */
958             ++Changes;
959         }
960
961         /* Next entry */
962         ++I;
963     }
964
965     /* Return the number of changes made */
966     return Changes;
967 }
968
969
970