]> git.sur5r.net Git - cc65/blob - src/cc65/coptind.c
ba1ffdc7fb87f4b3c51e88d91b9478ac676146e4
[cc65] / src / cc65 / coptind.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 coptind.c                                 */
4 /*                                                                           */
5 /*              Environment independent low level optimizations              */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2001      Ullrich von Bassewitz                                       */
10 /*               Wacholderweg 14                                             */
11 /*               D-70597 Stuttgart                                           */
12 /* EMail:        uz@cc65.org                                                 */
13 /*                                                                           */
14 /*                                                                           */
15 /* This software is provided 'as-is', without any expressed or implied       */
16 /* warranty.  In no event will the authors be held liable for any damages    */
17 /* arising from the use of this software.                                    */
18 /*                                                                           */
19 /* Permission is granted to anyone to use this software for any purpose,     */
20 /* including commercial applications, and to alter it and redistribute it    */
21 /* freely, subject to the following restrictions:                            */
22 /*                                                                           */
23 /* 1. The origin of this software must not be misrepresented; you must not   */
24 /*    claim that you wrote the original software. If you use this software   */
25 /*    in a product, an acknowledgment in the product documentation would be  */
26 /*    appreciated but is not required.                                       */
27 /* 2. Altered source versions must be plainly marked as such, and must not   */
28 /*    be misrepresented as being the original software.                      */
29 /* 3. This notice may not be removed or altered from any source              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <string.h>
37
38 /* cc65 */
39 #include "codeent.h"
40 #include "codeinfo.h"
41 #include "codeopt.h"
42 #include "error.h"
43 #include "coptind.h"
44
45
46
47 /*****************************************************************************/
48 /*                                  Macros                                   */
49 /*****************************************************************************/
50
51
52
53 /* Macro to increment and decrement register contents if they're valid */
54 #define INC(reg,val)    if ((reg) >= 0) (reg) = ((reg) + val) & 0xFF
55 #define DEC(reg,val)    if ((reg) >= 0) (reg) = ((reg) - val) & 0xFF
56
57
58
59 /*****************************************************************************/
60 /*                                  Helpers                                  */
61 /*****************************************************************************/
62
63
64
65 static int IsKnownImm (const CodeEntry* E)
66 /* Return true if the argument of E is a known immediate value */
67 {
68     return (E->AM == AM65_IMM && (E->Flags & CEF_NUMARG) != 0);
69 }
70
71
72
73 /*****************************************************************************/
74 /*                        Replace jumps to RTS by RTS                        */
75 /*****************************************************************************/
76
77
78
79 unsigned OptRTSJumps (CodeSeg* S)
80 /* Replace jumps to RTS by RTS */
81 {
82     unsigned Changes = 0;
83
84     /* Walk over all entries minus the last one */
85     unsigned I = 0;
86     while (I < CS_GetEntryCount (S)) {
87
88         /* Get the next entry */
89         CodeEntry* E = CS_GetEntry (S, I);
90
91         /* Check if it's an unconditional branch to a local target */
92         if ((E->Info & OF_UBRA) != 0            &&
93             E->JumpTo != 0                      &&
94             E->JumpTo->Owner->OPC == OP65_RTS) {
95
96             /* Insert an RTS instruction */
97             CodeEntry* X = NewCodeEntry (OP65_RTS, AM65_IMP, 0, 0, E->LI);
98             CS_InsertEntry (S, X, I+1);
99
100             /* Delete the jump */
101             CS_DelEntry (S, I);
102
103             /* Remember, we had changes */
104             ++Changes;
105
106         }
107
108         /* Next entry */
109         ++I;
110
111     }
112
113     /* Return the number of changes made */
114     return Changes;
115 }
116
117
118
119 /*****************************************************************************/
120 /*                             Remove dead jumps                             */
121 /*****************************************************************************/
122
123
124
125 unsigned OptDeadJumps (CodeSeg* S)
126 /* Remove dead jumps (jumps to the next instruction) */
127 {
128     unsigned Changes = 0;
129     CodeEntry* E;
130     unsigned I;
131
132     /* Get the number of entries, bail out if we have less than two entries */
133     unsigned Count = CS_GetEntryCount (S);
134     if (Count < 2) {
135         return 0;
136     }
137
138     /* Walk over all entries minus the last one */
139     I = 0;
140     while (I < Count-1) {
141
142         /* Get the next entry */
143         E = CS_GetEntry (S, I);
144
145         /* Check if it's a branch, if it has a local target, and if the target
146          * is the next instruction.
147          */
148         if (E->AM == AM65_BRA && E->JumpTo && E->JumpTo->Owner == CS_GetEntry (S, I+1)) {
149
150             /* Delete the dead jump */
151             CS_DelEntry (S, I);
152
153             /* Keep the number of entries updated */
154             --Count;
155
156             /* Remember, we had changes */
157             ++Changes;
158
159         } else {
160
161             /* Next entry */
162             ++I;
163
164         }
165     }
166
167     /* Return the number of changes made */
168     return Changes;
169 }
170
171
172
173 /*****************************************************************************/
174 /*                             Remove dead code                              */
175 /*****************************************************************************/
176
177
178
179 unsigned OptDeadCode (CodeSeg* S)
180 /* Remove dead code (code that follows an unconditional jump or an rts/rti
181  * and has no label)
182  */
183 {
184     unsigned Changes = 0;
185     unsigned I;
186
187     /* Get the number of entries, bail out if we have less than two entries */
188     unsigned Count = CS_GetEntryCount (S);
189     if (Count < 2) {
190         return 0;
191     }
192
193     /* Walk over all entries */
194     I = 0;
195     while (I < Count) {
196
197         CodeEntry* N;
198
199         /* Get this entry */
200         CodeEntry* E = CS_GetEntry (S, I);
201
202         /* Check if it's an unconditional branch, and if the next entry has
203          * no labels attached
204          */
205         if ((E->Info & OF_DEAD) != 0           &&
206             (N = CS_GetNextEntry (S, I)) != 0  &&
207             !CE_HasLabel (N)) {
208
209             /* Delete the next entry */
210             CS_DelEntry (S, I+1);
211
212             /* Keep the number of entries updated */
213             --Count;
214
215             /* Remember, we had changes */
216             ++Changes;
217
218         } else {
219
220             /* Next entry */
221             ++I;
222
223         }
224     }
225
226     /* Return the number of changes made */
227     return Changes;
228 }
229
230
231
232 /*****************************************************************************/
233 /*                          Optimize jump cascades                           */
234 /*****************************************************************************/
235
236
237
238 unsigned OptJumpCascades (CodeSeg* S)
239 /* Optimize jump cascades (jumps to jumps). In such a case, the jump is
240  * replaced by a jump to the final location. This will in some cases produce
241  * worse code, because some jump targets are no longer reachable by short
242  * branches, but this is quite rare, so there are more advantages than
243  * disadvantages.
244  */
245 {
246     unsigned Changes = 0;
247
248     /* Walk over all entries */
249     unsigned I = 0;
250     while (I < CS_GetEntryCount (S)) {
251
252         CodeEntry* N;
253         CodeLabel* OldLabel;
254
255         /* Get this entry */
256         CodeEntry* E = CS_GetEntry (S, I);
257
258         /* Check if it's a branch, if it has a jump label, if this jump
259          * label is not attached to the instruction itself, and if the
260          * target instruction is itself a branch.
261          */
262         if ((E->Info & OF_BRA) != 0        &&
263             (OldLabel = E->JumpTo) != 0    &&
264             (N = OldLabel->Owner) != E     &&
265             (N->Info & OF_BRA) != 0) {
266
267             /* Check if we can use the final target label. This is the case,
268              * if the target branch is an absolut branch, or if it is a
269              * conditional branch checking the same condition as the first one.
270              */
271             if ((N->Info & OF_UBRA) != 0 ||
272                 ((E->Info & OF_CBRA) != 0 &&
273                  GetBranchCond (E->OPC)  == GetBranchCond (N->OPC))) {
274
275                 /* This is a jump cascade and we may jump to the final target.
276                  * Insert a new instruction, then remove the old one
277                  */
278                 CodeEntry* X = NewCodeEntry (E->OPC, E->AM, N->Arg, N->JumpTo, E->LI);
279
280                 /* Insert it behind E */
281                 CS_InsertEntry (S, X, I+1);
282
283                 /* Remove E */
284                 CS_DelEntry (S, I);
285
286                 /* Remember, we had changes */
287                 ++Changes;
288
289                 /* Done */
290                 continue;
291
292             }
293
294             /* Check if both are conditional branches, and the condition of
295              * the second is the inverse of that of the first. In this case,
296              * the second branch will never be taken, and we may jump directly
297              * to the instruction behind this one.
298              */
299             if ((E->Info & OF_CBRA) != 0 && (N->Info & OF_CBRA) != 0) {
300
301                 CodeEntry* X;   /* Instruction behind N */
302                 CodeLabel* LX;  /* Label attached to X */
303
304                 /* Get the branch conditions of both branches */
305                 bc_t BC1 = GetBranchCond (E->OPC);
306                 bc_t BC2 = GetBranchCond (N->OPC);
307
308                 /* Check the branch conditions */
309                 if (BC1 != GetInverseCond (BC2)) {
310                     /* Condition not met */
311                     goto NextEntry;
312                 }
313
314                 /* We may jump behind this conditional branch. Get the
315                  * pointer to the next instruction
316                  */
317                 if ((X = CS_GetNextEntry (S, CS_GetEntryIndex (S, N))) == 0) {
318                     /* N is the last entry, bail out */
319                     goto NextEntry;
320                 }
321
322                 /* Get the label attached to X, create a new one if needed */
323                 LX = CS_GenLabel (S, X);
324
325                 /* Move the reference from E to the new label */
326                 CS_MoveLabelRef (S, E, LX);
327
328                 /* Remember, we had changes */
329                 ++Changes;
330
331                 /* Done */
332                 continue;
333
334             }
335         }
336
337 NextEntry:
338         /* Next entry */
339         ++I;
340
341     }
342
343     /* Return the number of changes made */
344     return Changes;
345 }
346
347
348
349 /*****************************************************************************/
350 /*                             Optimize jsr/rts                              */
351 /*****************************************************************************/
352
353
354
355 unsigned OptRTS (CodeSeg* S)
356 /* Optimize subroutine calls followed by an RTS. The subroutine call will get
357  * replaced by a jump. Don't bother to delete the RTS if it does not have a
358  * label, the dead code elimination should take care of it.
359  */
360 {
361     unsigned Changes = 0;
362     unsigned I;
363
364     /* Get the number of entries, bail out if we have less than 2 entries */
365     unsigned Count = CS_GetEntryCount (S);
366     if (Count < 2) {
367         return 0;
368     }
369
370     /* Walk over all entries minus the last one */
371     I = 0;
372     while (I < Count-1) {
373
374         CodeEntry* N;
375
376         /* Get this entry */
377         CodeEntry* E = CS_GetEntry (S, I);
378
379         /* Check if it's a subroutine call and if the following insn is RTS */
380         if (E->OPC == OP65_JSR                    &&
381             (N = CS_GetNextEntry (S, I)) != 0 &&
382             N->OPC == OP65_RTS) {
383
384             /* Change the jsr to a jmp and use the additional info for a jump */
385             E->AM = AM65_BRA;
386             CE_ReplaceOPC (E, OP65_JMP);
387
388             /* Remember, we had changes */
389             ++Changes;
390
391         }
392
393         /* Next entry */
394         ++I;
395
396     }
397
398     /* Return the number of changes made */
399     return Changes;
400 }
401
402
403
404 /*****************************************************************************/
405 /*                           Optimize jump targets                           */
406 /*****************************************************************************/
407
408
409
410 unsigned OptJumpTarget (CodeSeg* S)
411 /* If the instruction preceeding an unconditional branch is the same as the
412  * instruction preceeding the jump target, the jump target may be moved
413  * one entry back. This is a size optimization, since the instruction before
414  * the branch gets removed.
415  */
416 {
417     unsigned Changes = 0;
418     CodeEntry* E1;              /* Entry 1 */
419     CodeEntry* E2;              /* Entry 2 */
420     CodeEntry* T1;              /* Jump target entry 1 */
421     CodeEntry* T2;              /* Jump target entry 2 */
422     CodeLabel* TL1;             /* Target label 1 */
423     unsigned TI;                /* Target index */
424     unsigned I;
425
426     /* Get the number of entries, bail out if we have not enough */
427     unsigned Count = CS_GetEntryCount (S);
428     if (Count < 3) {
429         return 0;
430     }
431
432     /* Walk over the entries */
433     I = 0;
434     while (I < Count-1) {
435
436         /* Get next entry */
437         E2 = CS_GetEntry (S, I+1);
438
439         /* Check if we have a jump or branch, and a matching label */
440         if ((E2->Info & OF_UBRA) != 0 && E2->JumpTo) {
441
442             /* Get the target instruction for the label */
443             T2 = E2->JumpTo->Owner;
444
445             /* Get the entry preceeding this one (if possible) */
446             TI = CS_GetEntryIndex (S, T2);
447             if (TI == 0) {
448                 /* There is no entry before this one */
449                 goto NextEntry;
450             }
451             T1 = CS_GetEntry (S, TI-1);
452
453             /* Get the entry preceeding the jump */
454             E1 = CS_GetEntry (S, I);
455
456             /* Check if both preceeding instructions are identical */
457             if (!CodeEntriesAreEqual (E1, T1)) {
458                 /* Not equal, try next */
459                 goto NextEntry;
460             }
461
462             /* Get the label for the instruction preceeding the jump target.
463              * This routine will create a new label if the instruction does
464              * not already have one.
465              */
466             TL1 = CS_GenLabel (S, T1);
467
468             /* Change the jump target to point to this new label */
469             CS_MoveLabelRef (S, E2, TL1);
470
471             /* If the instruction preceeding the jump has labels attached,
472              * move references to this label to the new label.
473              */
474             if (CE_HasLabel (E1)) {
475                 CS_MoveLabels (S, E1, T1);
476             }
477
478             /* Remove the entry preceeding the jump */
479             CS_DelEntry (S, I);
480             --Count;
481
482             /* Remember, we had changes */
483             ++Changes;
484
485         }
486
487 NextEntry:
488         /* Next entry */
489         ++I;
490
491     }
492
493     /* Return the number of changes made */
494     return Changes;
495 }
496
497
498
499 /*****************************************************************************/
500 /*                       Optimize conditional branches                       */
501 /*****************************************************************************/
502
503
504
505 unsigned OptCondBranches (CodeSeg* S)
506 /* Performs several optimization steps:
507  *
508  *  - If an immidiate load of a register is followed by a conditional jump that
509  *    is never taken because the load of the register sets the flags in such a
510  *    manner, remove the conditional branch.
511  *  - If the conditional branch is always taken because of the register load,
512  *    replace it by a jmp.
513  *  - If a conditional branch jumps around an unconditional branch, remove the
514  *    conditional branch and make the jump a conditional branch with the
515  *    inverse condition of the first one.
516  */
517 {
518     unsigned Changes = 0;
519     unsigned I;
520
521     /* Get the number of entries, bail out if we have not enough */
522     unsigned Count = CS_GetEntryCount (S);
523     if (Count < 2) {
524         return 0;
525     }
526
527     /* Walk over the entries */
528     I = 0;
529     while (I < Count-1) {
530
531         CodeEntry* N;
532         CodeLabel* L;
533
534         /* Get next entry */
535         CodeEntry* E = CS_GetEntry (S, I);
536
537         /* Check if it's a register load */
538         if ((E->Info & OF_LOAD) != 0              &&  /* It's a load instruction */
539             E->AM == AM65_IMM                     &&  /* ..with immidiate addressing */
540             (E->Flags & CEF_NUMARG) != 0          &&  /* ..and a numeric argument. */
541             (N = CS_GetNextEntry (S, I)) != 0     &&  /* There is a following entry */
542             (N->Info & OF_CBRA) != 0              &&  /* ..which is a conditional branch */
543             !CE_HasLabel (N)) {               /* ..and does not have a label */
544
545             /* Get the branch condition */
546             bc_t BC = GetBranchCond (N->OPC);
547
548             /* Check the argument against the branch condition */
549             if ((BC == BC_EQ && E->Num != 0)            ||
550                 (BC == BC_NE && E->Num == 0)            ||
551                 (BC == BC_PL && (E->Num & 0x80) != 0)   ||
552                 (BC == BC_MI && (E->Num & 0x80) == 0)) {
553
554                 /* Remove the conditional branch */
555                 CS_DelEntry (S, I+1);
556                 --Count;
557
558                 /* Remember, we had changes */
559                 ++Changes;
560
561             } else if ((BC == BC_EQ && E->Num == 0)             ||
562                        (BC == BC_NE && E->Num != 0)             ||
563                        (BC == BC_PL && (E->Num & 0x80) == 0)    ||
564                        (BC == BC_MI && (E->Num & 0x80) != 0)) {
565
566                 /* The branch is always taken, replace it by a jump */
567                 CE_ReplaceOPC (N, OP65_JMP);
568
569                 /* Remember, we had changes */
570                 ++Changes;
571             }
572
573         }
574
575         if ((E->Info & OF_CBRA) != 0              &&  /* It's a conditional branch */
576             (L = E->JumpTo) != 0                  &&  /* ..referencing a local label */
577             (N = CS_GetNextEntry (S, I)) != 0     &&  /* There is a following entry */
578             (N->Info & OF_UBRA) != 0              &&  /* ..which is an uncond branch, */
579             !CE_HasLabel (N)                      &&  /* ..has no label attached */
580             L->Owner == CS_GetNextEntry (S, I+1)) {/* ..and jump target follows */
581
582             /* Replace the jump by a conditional branch with the inverse branch
583              * condition than the branch around it.
584              */
585             CE_ReplaceOPC (N, GetInverseBranch (E->OPC));
586
587             /* Remove the conditional branch */
588             CS_DelEntry (S, I);
589             --Count;
590
591             /* Remember, we had changes */
592             ++Changes;
593
594         }
595
596         /* Next entry */
597         ++I;
598
599     }
600
601     /* Return the number of changes made */
602     return Changes;
603 }
604
605
606
607 /*****************************************************************************/
608 /*                            Remove unused loads                            */
609 /*****************************************************************************/
610
611
612
613 unsigned OptUnusedLoads (CodeSeg* S)
614 /* Remove loads of registers where the value loaded is not used later. */
615 {
616     unsigned Changes = 0;
617
618     /* Walk over the entries */
619     unsigned I = 0;
620     while (I < CS_GetEntryCount (S)) {
621
622         CodeEntry* N;
623
624         /* Get next entry */
625         CodeEntry* E = CS_GetEntry (S, I);
626
627         /* Check if it's a register load or transfer insn */
628         if ((E->Info & (OF_LOAD | OF_XFR)) != 0    &&
629             (N = CS_GetNextEntry (S, I)) != 0      &&
630             (N->Info & OF_FBRA) == 0) {
631
632             /* Check which sort of load or transfer it is */
633             unsigned R;
634             switch (E->OPC) {
635                 case OP65_TXA:
636                 case OP65_TYA:
637                 case OP65_LDA:  R = REG_A;      break;
638                 case OP65_TAX:
639                 case OP65_LDX:  R = REG_X;      break;
640                 case OP65_TAY:
641                 case OP65_LDY:  R = REG_Y;      break;
642                 default:        goto NextEntry;         /* OOPS */
643             }
644
645             /* Get register usage and check if the register value is used later */
646             if ((GetRegInfo (S, I+1) & R) == 0) {
647
648                 /* Register value is not used, remove the load */
649                 CS_DelEntry (S, I);
650
651                 /* Remember, we had changes */
652                 ++Changes;
653
654             }
655         }
656
657 NextEntry:
658         /* Next entry */
659         ++I;
660
661     }
662
663     /* Return the number of changes made */
664     return Changes;
665 }
666
667
668
669 unsigned OptDuplicateLoads (CodeSeg* S)
670 /* Remove loads of registers where the value loaded is already in the register. */
671 {
672     unsigned Changes = 0;
673
674     /* Remember the last load instructions for all registers */
675     int RegA = -1;
676     int RegX = -1;
677     int RegY = -1;
678
679     /* Walk over the entries */
680     unsigned I = 0;
681     while (I < CS_GetEntryCount (S)) {
682
683         unsigned char Use, Chg;
684         CodeEntry* N;
685
686         /* Get next entry */
687         CodeEntry* E = CS_GetEntry (S, I);
688
689         /* Assume we won't delete the entry */
690         int Delete = 0;
691
692         /* If this entry has a label attached, remove all knownledge about the
693          * register contents. This may be improved but for now it's ok.
694          */
695         if (CE_HasLabel (E)) {
696             RegA = RegX = RegY = -1;
697         }
698
699         /* Handle the different instructions */
700         switch (E->OPC) {
701
702             case OP65_ADC:
703                 /* We don't know the value of the carry, so the result is
704                  * always unknown.
705                  */
706                 RegA = -1;
707                 break;
708
709             case OP65_AND:
710                 if (RegA >= 0) {
711                     if (IsKnownImm (E)) {
712                         RegA &= (int) E->Num;
713                     } else {
714                         RegA = -1;
715                     }
716                 }
717                 break;
718
719             case OP65_ASL:
720                 if (RegA >= 0) {
721                     RegA = (RegA << 1) & 0xFF;
722                 }
723                 break;
724
725             case OP65_BCC:
726                 break;
727
728             case OP65_BCS:
729                 break;
730
731             case OP65_BEQ:
732                 break;
733
734             case OP65_BIT:
735                 break;
736
737             case OP65_BMI:
738                 break;
739
740             case OP65_BNE:
741                 break;
742
743             case OP65_BPL:
744                 break;
745
746             case OP65_BRA:
747                 break;
748
749             case OP65_BRK:
750                 break;
751
752             case OP65_BVC:
753                 break;
754
755             case OP65_BVS:
756                 break;
757
758             case OP65_CLC:
759                 break;
760
761             case OP65_CLD:
762                 break;
763
764             case OP65_CLI:
765                 break;
766
767             case OP65_CLV:
768                 break;
769
770             case OP65_CMP:
771                 break;
772
773             case OP65_CPX:
774                 break;
775
776             case OP65_CPY:
777                 break;
778
779             case OP65_DEA:
780                 DEC (RegA, 1);
781                 break;
782
783             case OP65_DEC:
784                 break;
785
786             case OP65_DEX:
787                 DEC (RegX, 1);
788                 break;
789
790             case OP65_DEY:
791                 DEC (RegY, 1);
792                 break;
793
794             case OP65_EOR:
795                 if (RegA >= 0) {
796                     if (IsKnownImm (E)) {
797                         RegA ^= (int) E->Num;
798                     } else {
799                         RegA = -1;
800                     }
801                 }
802                 break;
803
804             case OP65_INA:
805                 INC (RegA, 1);
806                 break;
807
808             case OP65_INC:
809                 break;
810
811             case OP65_INX:
812                 INC (RegX, 1);
813                 break;
814
815             case OP65_INY:
816                 INC (RegY, 1);
817                 break;
818
819             case OP65_JCC:
820                 break;
821
822             case OP65_JCS:
823                 break;
824
825             case OP65_JEQ:
826                 break;
827
828             case OP65_JMI:
829                 break;
830
831             case OP65_JMP:
832                 break;
833
834             case OP65_JNE:
835                 break;
836
837             case OP65_JPL:
838                 break;
839
840             case OP65_JSR:
841                 /* Get the code info for the function */
842                 GetFuncInfo (E->Arg, &Use, &Chg);
843                 if (Chg & REG_A) {
844                     RegA = -1;
845                 }
846                 if (Chg & REG_X) {
847                     RegX = -1;
848                 }
849                 if (Chg & REG_Y) {
850                     RegY = -1;
851                 }
852                 break;
853
854             case OP65_JVC:
855                 break;
856
857             case OP65_JVS:
858                 break;
859
860             case OP65_LDA:
861                 if (IsKnownImm (E)) {
862                     N = CS_GetNextEntry (S, I);
863                     if (RegA >= 0 && RegA == E->Num && N && (N->Info & OF_FBRA) == 0) {
864                         Delete = 1;
865                     } else {
866                         RegA = (unsigned char) E->Num;
867                     }
868                 } else {
869                     /* A is now unknown */
870                     RegA = -1;
871                 }
872                 break;
873
874             case OP65_LDX:
875                 if (IsKnownImm (E)) {
876                     N = CS_GetNextEntry (S, I);
877                     if (RegX >= 0 && RegX == E->Num && N && (N->Info & OF_FBRA) == 0) {
878                         Delete = 1;
879                     } else {
880                         RegX = (unsigned char) E->Num;
881                     }
882                 } else {
883                     /* X is now unknown */
884                     RegX = -1;
885                 }
886                 break;
887
888             case OP65_LDY:
889                 if (IsKnownImm (E)) {
890                     N = CS_GetNextEntry (S, I);
891                     if (RegY >= 0 && RegY == E->Num && N && (N->Info & OF_FBRA) == 0) {
892                         Delete = 1;
893                     } else {
894                         RegY = (unsigned char) E->Num;
895                     }
896                 } else {
897                     /* Y is now unknown */
898                     RegY = -1;
899                 }
900                 break;
901
902             case OP65_LSR:
903                 if (RegA >= 0) {
904                     RegA = (RegA >> 1) & 0xFF;
905                 }
906                 break;
907
908             case OP65_NOP:
909                 break;
910
911             case OP65_ORA:
912                 if (RegA >= 0) {
913                     if (IsKnownImm (E)) {
914                         RegA |= (unsigned char) E->Num;
915                     } else {
916                         /* A is now unknown */
917                         RegA = -1;
918                     }
919                 }
920                 break;
921
922             case OP65_PHA:
923                 break;
924
925             case OP65_PHP:
926                 break;
927
928             case OP65_PHX:
929                 break;
930
931             case OP65_PHY:
932                 break;
933
934             case OP65_PLA:
935                 RegA = -1;
936                 break;
937
938             case OP65_PLP:
939                 break;
940
941             case OP65_PLX:
942                 RegX = -1;
943                 break;
944
945             case OP65_PLY:
946                 RegY = -1;
947                 break;
948
949             case OP65_ROL:
950                 RegA = -1;
951                 break;
952
953             case OP65_ROR:
954                 RegA = -1;
955                 break;
956
957             case OP65_RTI:
958                 break;
959
960             case OP65_RTS:
961                 break;
962
963             case OP65_SBC:
964                 RegA = -1;
965                 break;
966
967             case OP65_SEC:
968                 break;
969
970             case OP65_SED:
971                 break;
972
973             case OP65_SEI:
974                 break;
975
976             case OP65_STA:
977                 break;
978
979             case OP65_STX:
980                 /* If the value in the X register is known and the same as
981                  * that in the A register, replace the store by a STA. The
982                  * optimizer will then remove the load instruction for X
983                  * later. STX does support the zeropage,y addressing mode,
984                  * so be sure to check for that.
985                  */
986                 if (RegX >= 0 && RegX == RegA &&
987                     E->AM != AM65_ABSY && E->AM != AM65_ZPY) {
988                     /* Use the A register instead */
989                     CE_ReplaceOPC (E, OP65_STA);
990                 }
991                 break;
992
993             case OP65_STY:
994                 /* If the value in the Y register is known and the same as
995                  * that in the A register, replace the store by a STA. The
996                  * optimizer will then remove the load instruction for Y
997                  * later. If replacement by A is not possible try a
998                  * replacement by X, but check for invalid addressing modes
999                  * in this case.
1000                  */
1001                 if (RegY >= 0) {
1002                     if (RegY == RegA) {
1003                         CE_ReplaceOPC (E, OP65_STA);
1004                     } else if (RegY == RegX && E->AM != AM65_ABSX && E->AM != AM65_ZPX) {
1005                         CE_ReplaceOPC (E, OP65_STX);
1006                     }
1007                 }
1008                 break;
1009
1010             case OP65_TAX:
1011                 N = CS_GetNextEntry (S, I);
1012                 if (RegA >= 0 && RegA == RegX && N && (N->Info & OF_FBRA) == 0) {
1013                     /* Value is identical and not followed by a branch */
1014                     Delete = 1;
1015                 } else {
1016                     RegX = RegA;
1017                 }
1018                 break;
1019
1020             case OP65_TAY:
1021                 N = CS_GetNextEntry (S, I);
1022                 if (RegA >= 0 && RegA == RegY && N && (N->Info & OF_FBRA) == 0) {
1023                     /* Value is identical and not followed by a branch */
1024                     Delete = 1;
1025                 } else {
1026                     RegY = RegA;
1027                 }
1028                 break;
1029
1030             case OP65_TRB:
1031                 break;
1032
1033             case OP65_TSB:
1034                 break;
1035
1036             case OP65_TSX:
1037                 RegX = -1;
1038                 break;
1039
1040             case OP65_TXA:
1041                 N = CS_GetNextEntry (S, I);
1042                 if (RegX >= 0 && RegX == RegA && N && (N->Info & OF_FBRA) == 0) {
1043                     /* Value is identical and not followed by a branch */
1044                     Delete = 1;
1045                 } else {
1046                     RegA = RegX;
1047                 }
1048                 break;
1049
1050             case OP65_TXS:
1051                 break;
1052
1053             case OP65_TYA:
1054                 N = CS_GetNextEntry (S, I);
1055                 if (RegY >= 0 && RegY == RegA && N && (N->Info & OF_FBRA) == 0) {
1056                     /* Value is identical and not followed by a branch */
1057                     Delete = 1;
1058                 } else {
1059                     RegA = RegY;
1060                 }
1061                 break;
1062
1063             default:
1064                 break;
1065
1066         }
1067
1068         /* Delete the entry if requested */
1069         if (Delete) {
1070
1071             /* Register value is not used, remove the load */
1072             CS_DelEntry (S, I);
1073
1074             /* Remember, we had changes */
1075             ++Changes;
1076
1077         } else {
1078
1079             /* Next entry */
1080             ++I;
1081
1082         }
1083
1084     }
1085
1086     /* Return the number of changes made */
1087     return Changes;
1088 }
1089
1090
1091
1092 unsigned OptStoreLoad (CodeSeg* S)
1093 /* Remove a store followed by a load from the same location. */
1094 {
1095     unsigned Changes = 0;
1096
1097     /* Walk over the entries */
1098     unsigned I = 0;
1099     while (I < CS_GetEntryCount (S)) {
1100
1101         CodeEntry* N;
1102         CodeEntry* X;
1103
1104         /* Get next entry */
1105         CodeEntry* E = CS_GetEntry (S, I);
1106
1107         /* Check if it is a store instruction followed by a load from the
1108          * same address which is itself not followed by a conditional branch.
1109          */
1110         if ((E->Info & OF_STORE) != 0                 &&
1111             (N = CS_GetNextEntry (S, I)) != 0         &&
1112             (N->Info & OF_LOAD) != 0                  &&
1113             strcmp (E->Arg, N->Arg) == 0              &&
1114             (X = CS_GetNextEntry (S, I+1)) != 0       &&
1115             (X->Info & OF_FBRA) == 0) {
1116
1117             /* Register value is not used, remove the load */
1118             CS_DelEntry (S, I+1);
1119
1120             /* Remember, we had changes */
1121             ++Changes;
1122
1123         }
1124
1125         /* Next entry */
1126         ++I;
1127
1128     }
1129
1130     /* Return the number of changes made */
1131     return Changes;
1132 }
1133
1134
1135
1136 /*****************************************************************************/
1137 /*                           Optimize branch types                           */
1138 /*****************************************************************************/
1139
1140
1141
1142 unsigned OptBranchDist (CodeSeg* S)
1143 /* Change branches for the distance needed. */
1144 {
1145     unsigned Changes = 0;
1146     unsigned I;
1147
1148     /* Get the number of entries, bail out if we have not enough */
1149     unsigned Count = CS_GetEntryCount (S);
1150
1151     /* Walk over the entries */
1152     I = 0;
1153     while (I < Count) {
1154
1155         /* Get next entry */
1156         CodeEntry* E = CS_GetEntry (S, I);
1157
1158         /* Check if it's a conditional branch to a local label. */
1159         if ((E->Info & OF_CBRA) != 0) {
1160
1161             /* Is this a branch to a local symbol? */
1162             if (E->JumpTo != 0) {
1163
1164                 /* Get the index of the branch target */
1165                 unsigned TI = CS_GetEntryIndex (S, E->JumpTo->Owner);
1166
1167                 /* Determine the branch distance */
1168                 int Distance = 0;
1169                 if (TI >= I) {
1170                     /* Forward branch */
1171                     unsigned J = I;
1172                     while (J < TI) {
1173                         CodeEntry* N = CS_GetEntry (S, J++);
1174                         Distance += N->Size;
1175                     }
1176                 } else {
1177                     /* Backward branch */
1178                     unsigned J = TI;
1179                     while (J < I) {
1180                         CodeEntry* N = CS_GetEntry (S, J++);
1181                         Distance += N->Size;
1182                     }
1183                 }
1184
1185                 /* Make the branch short/long according to distance */
1186                 if ((E->Info & OF_LBRA) == 0 && Distance > 120) {
1187                     /* Short branch but long distance */
1188                     CE_ReplaceOPC (E, MakeLongBranch (E->OPC));
1189                     ++Changes;
1190                 } else if ((E->Info & OF_LBRA) != 0 && Distance < 120) {
1191                     /* Long branch but short distance */
1192                     CE_ReplaceOPC (E, MakeShortBranch (E->OPC));
1193                     ++Changes;
1194                 }
1195
1196             } else if ((E->Info & OF_LBRA) == 0) {
1197
1198                 /* Short branch to external symbol - make it long */
1199                 CE_ReplaceOPC (E, MakeLongBranch (E->OPC));
1200                 ++Changes;
1201
1202             }
1203         }
1204
1205         /* Next entry */
1206         ++I;
1207
1208     }
1209
1210     /* Return the number of changes made */
1211     return Changes;
1212 }
1213
1214
1215