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