1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* coptind.c */ |
4 | /* */ |
5 | /* Environment independent low level optimizations */ |
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 | /* common */ |
37 | #include "cpu.h" |
38 | |
39 | /* cc65 */ |
40 | #include "codeent.h" |
41 | #include "coptind.h" |
42 | #include "codeinfo.h" |
43 | #include "codeopt.h" |
44 | #include "error.h" |
45 | |
46 | |
47 | |
48 | /*****************************************************************************/ |
49 | /* Helper functions */ |
50 | /*****************************************************************************/ |
51 | |
52 | |
53 | |
54 | static int MemAccess (CodeSeg* S, unsigned From, unsigned To, const CodeEntry* N) |
55 | /* Checks a range of code entries if there are any memory accesses to N->Arg */ |
56 | { |
57 | /* Get the length of the argument */ |
58 | unsigned NLen = strlen (N->Arg); |
59 | |
60 | /* What to check for? */ |
61 | enum { |
62 | None = 0x00, |
63 | Base = 0x01, /* Check for location without "+1" */ |
64 | Word = 0x02, /* Check for location with "+1" added */ |
65 | } What = None; |
66 | |
67 | |
68 | /* If the argument of N is a zero page location that ends with "+1", we |
69 | ** must also check for word accesses to the location without +1. |
70 | */ |
71 | if (N->AM == AM65_ZP && NLen > 2 && strcmp (N->Arg + NLen - 2, "+1" ) == 0) { |
72 | What |= Base; |
73 | } |
74 | |
75 | /* If the argument is zero page indirect, we must also check for accesses |
76 | ** to "arg+1" |
77 | */ |
78 | if (N->AM == AM65_ZP_INDY || N->AM == AM65_ZPX_IND || N->AM == AM65_ZP_IND) { |
79 | What |= Word; |
80 | } |
81 | |
82 | /* Walk over all code entries */ |
83 | while (From <= To) { |
84 | |
85 | /* Get the next entry */ |
86 | CodeEntry* E = CS_GetEntry (S, From); |
87 | |
88 | /* Check if there is an argument and if this argument equals Arg in |
89 | ** some variants. |
90 | */ |
91 | if (E->Arg[0] != '\0') { |
92 | |
93 | unsigned ELen; |
94 | |
95 | if (strcmp (E->Arg, N->Arg) == 0) { |
96 | /* Found an access */ |
97 | return 1; |
98 | } |
99 | |
100 | ELen = strlen (E->Arg); |
101 | if ((What & Base) != 0) { |
102 | if (ELen == NLen - 2 && strncmp (E->Arg, N->Arg, NLen-2) == 0) { |
103 | /* Found an access */ |
104 | return 1; |
105 | } |
106 | } |
107 | |
108 | if ((What & Word) != 0) { |
109 | if (ELen == NLen + 2 && strncmp (E->Arg, N->Arg, NLen) == 0 && |
110 | E->Arg[NLen] == '+' && E->Arg[NLen+1] == '1') { |
111 | /* Found an access */ |
112 | return 1; |
113 | } |
114 | } |
115 | } |
116 | |
117 | /* Next entry */ |
118 | ++From; |
119 | } |
120 | |
121 | /* Nothing found */ |
122 | return 0; |
123 | } |
124 | |
125 | |
126 | |
127 | static int GetBranchDist (CodeSeg* S, unsigned From, CodeEntry* To) |
128 | /* Get the branch distance between the two entries and return it. The distance |
129 | ** will be negative for backward jumps and positive for forward jumps. |
130 | */ |
131 | { |
132 | /* Get the index of the branch target */ |
133 | unsigned TI = CS_GetEntryIndex (S, To); |
134 | |
135 | /* Determine the branch distance */ |
136 | int Distance = 0; |
137 | if (TI >= From) { |
138 | /* Forward branch, do not count the current insn */ |
139 | unsigned J = From+1; |
140 | while (J < TI) { |
141 | CodeEntry* N = CS_GetEntry (S, J++); |
142 | Distance += N->Size; |
143 | } |
144 | } else { |
145 | /* Backward branch */ |
146 | unsigned J = TI; |
147 | while (J < From) { |
148 | CodeEntry* N = CS_GetEntry (S, J++); |
149 | Distance -= N->Size; |
150 | } |
151 | } |
152 | |
153 | /* Return the calculated distance */ |
154 | return Distance; |
155 | } |
156 | |
157 | |
158 | |
159 | static int IsShortDist (int Distance) |
160 | /* Return true if the given distance is a short branch distance */ |
161 | { |
162 | return (Distance >= -125 && Distance <= 125); |
163 | } |
164 | |
165 | |
166 | |
167 | static short ZPRegVal (unsigned short Use, const RegContents* RC) |
168 | /* Return the contents of the given zeropage register */ |
169 | { |
170 | if ((Use & REG_TMP1) != 0) { |
171 | return RC->Tmp1; |
172 | } else if ((Use & REG_PTR1_LO) != 0) { |
173 | return RC->Ptr1Lo; |
174 | } else if ((Use & REG_PTR1_HI) != 0) { |
175 | return RC->Ptr1Hi; |
176 | } else if ((Use & REG_SREG_LO) != 0) { |
177 | return RC->SRegLo; |
178 | } else if ((Use & REG_SREG_HI) != 0) { |
179 | return RC->SRegHi; |
180 | } else { |
181 | return UNKNOWN_REGVAL; |
182 | } |
183 | } |
184 | |
185 | |
186 | |
187 | static short RegVal (unsigned short Use, const RegContents* RC) |
188 | /* Return the contents of the given register */ |
189 | { |
190 | if ((Use & REG_A) != 0) { |
191 | return RC->RegA; |
192 | } else if ((Use & REG_X) != 0) { |
193 | return RC->RegX; |
194 | } else if ((Use & REG_Y) != 0) { |
195 | return RC->RegY; |
196 | } else { |
197 | return ZPRegVal (Use, RC); |
198 | } |
199 | } |
200 | |
201 | |
202 | |
203 | /*****************************************************************************/ |
204 | /* Replace jumps to RTS by RTS */ |
205 | /*****************************************************************************/ |
206 | |
207 | |
208 | |
209 | unsigned OptRTSJumps1 (CodeSeg* S) |
210 | /* Replace jumps to RTS by RTS */ |
211 | { |
212 | unsigned Changes = 0; |
213 | |
214 | /* Walk over all entries minus the last one */ |
215 | unsigned I = 0; |
216 | while (I < CS_GetEntryCount (S)) { |
217 | |
218 | /* Get the next entry */ |
219 | CodeEntry* E = CS_GetEntry (S, I); |
220 | |
221 | /* Check if it's an unconditional branch to a local target */ |
222 | if ((E->Info & OF_UBRA) != 0 && |
223 | E->JumpTo != 0 && |
224 | E->JumpTo->Owner->OPC == OP65_RTS) { |
225 | |
226 | /* Insert an RTS instruction */ |
227 | CodeEntry* X = NewCodeEntry (OP65_RTS, AM65_IMP, 0, 0, E->LI); |
228 | CS_InsertEntry (S, X, I+1); |
229 | |
230 | /* Delete the jump */ |
231 | CS_DelEntry (S, I); |
232 | |
233 | /* Remember, we had changes */ |
234 | ++Changes; |
235 | |
236 | } |
237 | |
238 | /* Next entry */ |
239 | ++I; |
240 | |
241 | } |
242 | |
243 | /* Return the number of changes made */ |
244 | return Changes; |
245 | } |
246 | |
247 | |
248 | |
249 | unsigned OptRTSJumps2 (CodeSeg* S) |
250 | /* Replace long conditional jumps to RTS or to a final target */ |
251 | { |
252 | unsigned Changes = 0; |
253 | |
254 | /* Walk over all entries minus the last one */ |
255 | unsigned I = 0; |
256 | while (I < CS_GetEntryCount (S) - 1) { |
257 | |
258 | /* Get the next entry */ |
259 | CodeEntry* E = CS_GetEntry (S, I); |
260 | |
261 | /* Check if it's an conditional branch to a local target */ |
262 | if ((E->Info & OF_CBRA) != 0 && /* Conditional branch */ |
263 | (E->Info & OF_LBRA) != 0 && /* Long branch */ |
264 | E->JumpTo != 0) { /* Local label */ |
265 | |
266 | |
267 | /* Get the jump target and the next entry. There's always a next |
268 | ** entry, because we don't cover the last entry in the loop. |
269 | */ |
270 | CodeEntry* X = 0; |
271 | CodeEntry* T = E->JumpTo->Owner; |
272 | CodeEntry* N = CS_GetNextEntry (S, I); |
273 | |
274 | /* Check if it's a jump to an RTS insn */ |
275 | if (T->OPC == OP65_RTS) { |
276 | |
277 | /* It's a jump to RTS. Create a conditional branch around an |
278 | ** RTS insn. |
279 | */ |
280 | X = NewCodeEntry (OP65_RTS, AM65_IMP, 0, 0, T->LI); |
281 | |
282 | } else if (T->OPC == OP65_JMP && T->JumpTo == 0) { |
283 | |
284 | /* It's a jump to a label outside the function. Create a |
285 | ** conditional branch around a jump to the external label. |
286 | */ |
287 | X = NewCodeEntry (OP65_JMP, AM65_ABS, T->Arg, T->JumpTo, T->LI); |
288 | |
289 | } |
290 | |
291 | /* If we have a replacement insn, insert it */ |
292 | if (X) { |
293 | |
294 | CodeLabel* LN; |
295 | opc_t NewBranch; |
296 | |
297 | /* Insert the new insn */ |
298 | CS_InsertEntry (S, X, I+1); |
299 | |
300 | /* Create a conditional branch with the inverse condition |
301 | ** around the replacement insn |
302 | */ |
303 | |
304 | /* Get the new branch opcode */ |
305 | NewBranch = MakeShortBranch (GetInverseBranch (E->OPC)); |
306 | |
307 | /* Get the label attached to N, create a new one if needed */ |
308 | LN = CS_GenLabel (S, N); |
309 | |
310 | /* Generate the branch */ |
311 | X = NewCodeEntry (NewBranch, AM65_BRA, LN->Name, LN, E->LI); |
312 | CS_InsertEntry (S, X, I+1); |
313 | |
314 | /* Delete the long branch */ |
315 | CS_DelEntry (S, I); |
316 | |
317 | /* Remember, we had changes */ |
318 | ++Changes; |
319 | |
320 | } |
321 | } |
322 | |
323 | /* Next entry */ |
324 | ++I; |
325 | |
326 | } |
327 | |
328 | /* Return the number of changes made */ |
329 | return Changes; |
330 | } |
331 | |
332 | |
333 | |
334 | /*****************************************************************************/ |
335 | /* Remove dead jumps */ |
336 | /*****************************************************************************/ |
337 | |
338 | |
339 | |
340 | unsigned OptDeadJumps (CodeSeg* S) |
341 | /* Remove dead jumps (jumps to the next instruction) */ |
342 | { |
343 | unsigned Changes = 0; |
344 | |
345 | /* Walk over all entries minus the last one */ |
346 | unsigned I = 0; |
347 | while (I < CS_GetEntryCount (S)) { |
348 | |
349 | /* Get the next entry */ |
350 | CodeEntry* E = CS_GetEntry (S, I); |
351 | |
352 | /* Check if it's a branch, if it has a local target, and if the target |
353 | ** is the next instruction. |
354 | */ |
355 | if (E->AM == AM65_BRA && |
356 | E->JumpTo && |
357 | E->JumpTo->Owner == CS_GetNextEntry (S, I)) { |
358 | |
359 | /* Delete the dead jump */ |
360 | CS_DelEntry (S, I); |
361 | |
362 | /* Remember, we had changes */ |
363 | ++Changes; |
364 | |
365 | } else { |
366 | |
367 | /* Next entry */ |
368 | ++I; |
369 | |
370 | } |
371 | } |
372 | |
373 | /* Return the number of changes made */ |
374 | return Changes; |
375 | } |
376 | |
377 | |
378 | |
379 | /*****************************************************************************/ |
380 | /* Remove dead code */ |
381 | /*****************************************************************************/ |
382 | |
383 | |
384 | |
385 | unsigned OptDeadCode (CodeSeg* S) |
386 | /* Remove dead code (code that follows an unconditional jump or an rts/rti |
387 | ** and has no label) |
388 | */ |
389 | { |
390 | unsigned Changes = 0; |
391 | |
392 | /* Walk over all entries */ |
393 | unsigned I = 0; |
394 | while (I < CS_GetEntryCount (S)) { |
395 | |
396 | CodeEntry* N; |
397 | CodeLabel* LN; |
398 | |
399 | /* Get this entry */ |
400 | CodeEntry* E = CS_GetEntry (S, I); |
401 | |
402 | /* Check if it's an unconditional branch, and if the next entry has |
403 | ** no labels attached, or if the label is just used so that the insn |
404 | ** can jump to itself. |
405 | */ |
406 | if ((E->Info & OF_DEAD) != 0 && /* Dead code follows */ |
407 | (N = CS_GetNextEntry (S, I)) != 0 && /* Has next entry */ |
408 | (!CE_HasLabel (N) || /* Don't has a label */ |
409 | ((N->Info & OF_UBRA) != 0 && /* Uncond branch */ |
410 | (LN = N->JumpTo) != 0 && /* Jumps to known label */ |
411 | LN->Owner == N && /* Attached to insn */ |
412 | CL_GetRefCount (LN) == 1))) { /* Only reference */ |
413 | |
414 | /* Delete the next entry */ |
415 | CS_DelEntry (S, I+1); |
416 | |
417 | /* Remember, we had changes */ |
418 | ++Changes; |
419 | |
420 | } else { |
421 | |
422 | /* Next entry */ |
423 | ++I; |
424 | |
425 | } |
426 | } |
427 | |
428 | /* Return the number of changes made */ |
429 | return Changes; |
430 | } |
431 | |
432 | |
433 | |
434 | /*****************************************************************************/ |
435 | /* Optimize jump cascades */ |
436 | /*****************************************************************************/ |
437 | |
438 | |
439 | |
440 | unsigned OptJumpCascades (CodeSeg* S) |
441 | /* Optimize jump cascades (jumps to jumps). In such a case, the jump is |
442 | ** replaced by a jump to the final location. This will in some cases produce |
443 | ** worse code, because some jump targets are no longer reachable by short |
444 | ** branches, but this is quite rare, so there are more advantages than |
445 | ** disadvantages. |
446 | */ |
447 | { |
448 | unsigned Changes = 0; |
449 | |
450 | /* Walk over all entries */ |
451 | unsigned I = 0; |
452 | while (I < CS_GetEntryCount (S)) { |
453 | |
454 | CodeEntry* N; |
455 | CodeLabel* OldLabel; |
456 | |
457 | /* Get this entry */ |
458 | CodeEntry* E = CS_GetEntry (S, I); |
459 | |
460 | /* Check: |
461 | ** - if it's a branch, |
462 | ** - if it has a jump label, |
463 | ** - if this jump label is not attached to the instruction itself, |
464 | ** - if the target instruction is itself a branch, |
465 | ** - if either the first branch is unconditional or the target of |
466 | ** the second branch is internal to the function. |
467 | ** The latter condition will avoid conditional branches to targets |
468 | ** outside of the function (usually incspx), which won't simplify the |
469 | ** code, since conditional far branches are emulated by a short branch |
470 | ** around a jump. |
471 | */ |
472 | if ((E->Info & OF_BRA) != 0 && |
473 | (OldLabel = E->JumpTo) != 0 && |
474 | (N = OldLabel->Owner) != E && |
475 | (N->Info & OF_BRA) != 0 && |
476 | ((E->Info & OF_CBRA) == 0 || |
477 | N->JumpTo != 0)) { |
478 | |
479 | /* Check if we can use the final target label. That is the case, |
480 | ** if the target branch is an absolute branch; or, if it is a |
481 | ** conditional branch checking the same condition as the first one. |
482 | */ |
483 | if ((N->Info & OF_UBRA) != 0 || |
484 | ((E->Info & OF_CBRA) != 0 && |
485 | GetBranchCond (E->OPC) == GetBranchCond (N->OPC))) { |
486 | |
487 | /* This is a jump cascade and we may jump to the final target, |
488 | ** provided that the other insn does not jump to itself. If |
489 | ** this is the case, we can also jump to ourselves, otherwise |
490 | ** insert a jump to the new instruction and remove the old one. |
491 | */ |
492 | CodeEntry* X; |
493 | CodeLabel* LN = N->JumpTo; |
494 | |
495 | if (LN != 0 && LN->Owner == N) { |
496 | |
497 | /* We found a jump to a jump to itself. Replace our jump |
498 | ** by a jump to itself. |
499 | */ |
500 | CodeLabel* LE = CS_GenLabel (S, E); |
501 | X = NewCodeEntry (E->OPC, E->AM, LE->Name, LE, E->LI); |
502 | |
503 | } else { |
504 | |
505 | /* Jump to the final jump target */ |
506 | X = NewCodeEntry (E->OPC, E->AM, N->Arg, N->JumpTo, E->LI); |
507 | |
508 | } |
509 | |
510 | /* Insert it behind E */ |
511 | CS_InsertEntry (S, X, I+1); |
512 | |
513 | /* Remove E */ |
514 | CS_DelEntry (S, I); |
515 | |
516 | /* Remember, we had changes */ |
517 | ++Changes; |
518 | |
519 | /* Check if both are conditional branches, and the condition of |
520 | ** the second is the inverse of that of the first. In this case, |
521 | ** the second branch will never be taken, and we may jump directly |
522 | ** to the instruction behind this one. |
523 | */ |
524 | } else if ((E->Info & OF_CBRA) != 0 && (N->Info & OF_CBRA) != 0) { |
525 | |
526 | CodeEntry* X; /* Instruction behind N */ |
527 | CodeLabel* LX; /* Label attached to X */ |
528 | |
529 | /* Get the branch conditions of both branches */ |
530 | bc_t BC1 = GetBranchCond (E->OPC); |
531 | bc_t BC2 = GetBranchCond (N->OPC); |
532 | |
533 | /* Check the branch conditions */ |
534 | if (BC1 != GetInverseCond (BC2)) { |
535 | /* Condition not met */ |
536 | goto NextEntry; |
537 | } |
538 | |
539 | /* We may jump behind this conditional branch. Get the |
540 | ** pointer to the next instruction |
541 | */ |
542 | if ((X = CS_GetNextEntry (S, CS_GetEntryIndex (S, N))) == 0) { |
543 | /* N is the last entry, bail out */ |
544 | goto NextEntry; |
545 | } |
546 | |
547 | /* Get the label attached to X, create a new one if needed */ |
548 | LX = CS_GenLabel (S, X); |
549 | |
550 | /* Move the reference from E to the new label */ |
551 | CS_MoveLabelRef (S, E, LX); |
552 | |
553 | /* Remember, we had changes */ |
554 | ++Changes; |
555 | } |
556 | } |
557 | |
558 | NextEntry: |
559 | /* Next entry */ |
560 | ++I; |
561 | |
562 | } |
563 | |
564 | /* Return the number of changes made */ |
565 | return Changes; |
566 | } |
567 | |
568 | |
569 | |
570 | /*****************************************************************************/ |
571 | /* Optimize jsr/rts */ |
572 | /*****************************************************************************/ |
573 | |
574 | |
575 | |
576 | unsigned OptRTS (CodeSeg* S) |
577 | /* Optimize subroutine calls followed by an RTS. The subroutine call will get |
578 | ** replaced by a jump. Don't bother to delete the RTS if it does not have a |
579 | ** label, the dead code elimination should take care of it. |
580 | */ |
581 | { |
582 | unsigned Changes = 0; |
583 | |
584 | /* Walk over all entries minus the last one */ |
585 | unsigned I = 0; |
586 | while (I < CS_GetEntryCount (S)) { |
587 | |
588 | CodeEntry* N; |
589 | |
590 | /* Get this entry */ |
591 | CodeEntry* E = CS_GetEntry (S, I); |
592 | |
593 | /* Check if it's a subroutine call and if the following insn is RTS */ |
594 | if (E->OPC == OP65_JSR && |
595 | (N = CS_GetNextEntry (S, I)) != 0 && |
596 | N->OPC == OP65_RTS) { |
597 | |
598 | /* Change the jsr to a jmp and use the additional info for a jump */ |
599 | E->AM = AM65_BRA; |
600 | CE_ReplaceOPC (E, OP65_JMP); |
601 | |
602 | /* Remember, we had changes */ |
603 | ++Changes; |
604 | |
605 | } |
606 | |
607 | /* Next entry */ |
608 | ++I; |
609 | |
610 | } |
611 | |
612 | /* Return the number of changes made */ |
613 | return Changes; |
614 | } |
615 | |
616 | |
617 | |
618 | /*****************************************************************************/ |
619 | /* Optimize jump targets */ |
620 | /*****************************************************************************/ |
621 | |
622 | |
623 | |
624 | unsigned OptJumpTarget1 (CodeSeg* S) |
625 | /* If the instruction preceeding an unconditional branch is the same as the |
626 | ** instruction preceeding the jump target, the jump target may be moved |
627 | ** one entry back. This is a size optimization, since the instruction before |
628 | ** the branch gets removed. |
629 | */ |
630 | { |
631 | unsigned Changes = 0; |
632 | CodeEntry* E1; /* Entry 1 */ |
633 | CodeEntry* E2; /* Entry 2 */ |
634 | CodeEntry* T1; /* Jump target entry 1 */ |
635 | CodeLabel* TL1; /* Target label 1 */ |
636 | |
637 | /* Walk over the entries */ |
638 | unsigned I = 0; |
639 | while (I < CS_GetEntryCount (S)) { |
640 | |
641 | /* Get next entry */ |
642 | E2 = CS_GetNextEntry (S, I); |
643 | |
644 | /* Check if we have a jump or branch without a label attached, and |
645 | ** a jump target, which is not attached to the jump itself |
646 | */ |
647 | if (E2 != 0 && |
648 | (E2->Info & OF_UBRA) != 0 && |
649 | !CE_HasLabel (E2) && |
650 | E2->JumpTo && |
651 | E2->JumpTo->Owner != E2) { |
652 | |
653 | /* Get the entry preceeding the branch target */ |
654 | T1 = CS_GetPrevEntry (S, CS_GetEntryIndex (S, E2->JumpTo->Owner)); |
655 | if (T1 == 0) { |
656 | /* There is no such entry */ |
657 | goto NextEntry; |
658 | } |
659 | |
660 | /* The entry preceeding the branch target may not be the branch |
661 | ** insn. |
662 | */ |
663 | if (T1 == E2) { |
664 | goto NextEntry; |
665 | } |
666 | |
667 | /* Get the entry preceeding the jump */ |
668 | E1 = CS_GetEntry (S, I); |
669 | |
670 | /* Check if both preceeding instructions are identical */ |
671 | if (!CodeEntriesAreEqual (E1, T1)) { |
672 | /* Not equal, try next */ |
673 | goto NextEntry; |
674 | } |
675 | |
676 | /* Get the label for the instruction preceeding the jump target. |
677 | ** This routine will create a new label if the instruction does |
678 | ** not already have one. |
679 | */ |
680 | TL1 = CS_GenLabel (S, T1); |
681 | |
682 | /* Change the jump target to point to this new label */ |
683 | CS_MoveLabelRef (S, E2, TL1); |
684 | |
685 | /* If the instruction preceeding the jump has labels attached, |
686 | ** move references to this label to the new label. |
687 | */ |
688 | if (CE_HasLabel (E1)) { |
689 | CS_MoveLabels (S, E1, T1); |
690 | } |
691 | |
692 | /* Remove the entry preceeding the jump */ |
693 | CS_DelEntry (S, I); |
694 | |
695 | /* Remember, we had changes */ |
696 | ++Changes; |
697 | |
698 | } else { |
699 | NextEntry: |
700 | /* Next entry */ |
701 | ++I; |
702 | } |
703 | } |
704 | |
705 | /* Return the number of changes made */ |
706 | return Changes; |
707 | } |
708 | |
709 | |
710 | |
711 | unsigned OptJumpTarget2 (CodeSeg* S) |
712 | /* If a bcs jumps to a sec insn or a bcc jumps to clc, skip this insn, since |
713 | ** it's job is already done. |
714 | */ |
715 | { |
716 | unsigned Changes = 0; |
717 | |
718 | /* Walk over the entries */ |
719 | unsigned I = 0; |
720 | while (I < CS_GetEntryCount (S)) { |
721 | |
722 | /* OP that may be skipped */ |
723 | opc_t OPC; |
724 | |
725 | /* Jump target insn, old and new */ |
726 | CodeEntry* T; |
727 | CodeEntry* N; |
728 | |
729 | /* New jump label */ |
730 | CodeLabel* L; |
731 | |
732 | /* Get next entry */ |
733 | CodeEntry* E = CS_GetEntry (S, I); |
734 | |
735 | /* Check if this is a bcc insn */ |
736 | if (E->OPC == OP65_BCC || E->OPC == OP65_JCC) { |
737 | OPC = OP65_CLC; |
738 | } else if (E->OPC == OP65_BCS || E->OPC == OP65_JCS) { |
739 | OPC = OP65_SEC; |
740 | } else { |
741 | /* Not what we're looking for */ |
742 | goto NextEntry; |
743 | } |
744 | |
745 | /* Must have a jump target */ |
746 | if (E->JumpTo == 0) { |
747 | goto NextEntry; |
748 | } |
749 | |
750 | /* Get the owner insn of the jump target and check if it's the one, we |
751 | ** will skip if present. |
752 | */ |
753 | T = E->JumpTo->Owner; |
754 | if (T->OPC != OPC) { |
755 | goto NextEntry; |
756 | } |
757 | |
758 | /* Get the entry following the branch target */ |
759 | N = CS_GetNextEntry (S, CS_GetEntryIndex (S, T)); |
760 | if (N == 0) { |
761 | /* There is no such entry */ |
762 | goto NextEntry; |
763 | } |
764 | |
765 | /* Get the label for the instruction following the jump target. |
766 | ** This routine will create a new label if the instruction does |
767 | ** not already have one. |
768 | */ |
769 | L = CS_GenLabel (S, N); |
770 | |
771 | /* Change the jump target to point to this new label */ |
772 | CS_MoveLabelRef (S, E, L); |
773 | |
774 | /* Remember that we had changes */ |
775 | ++Changes; |
776 | |
777 | NextEntry: |
778 | /* Next entry */ |
779 | ++I; |
780 | } |
781 | |
782 | /* Return the number of changes made */ |
783 | return Changes; |
784 | } |
785 | |
786 | |
787 | |
788 | unsigned OptJumpTarget3 (CodeSeg* S) |
789 | /* Jumps to load instructions of a register, that do already have the matching |
790 | ** register contents may skip the load instruction, since it's job is already |
791 | ** done. |
792 | */ |
793 | { |
794 | unsigned Changes = 0; |
795 | unsigned I; |
796 | |
797 | /* Walk over the entries */ |
798 | I = 0; |
799 | while (I < CS_GetEntryCount (S)) { |
800 | |
801 | CodeEntry* N; |
802 | |
803 | /* Get next entry */ |
804 | CodeEntry* E = CS_GetEntry (S, I); |
805 | |
806 | /* Check if this is a load insn with a label and the next insn is not |
807 | ** a conditional branch that needs the flags from the load. |
808 | */ |
809 | if ((E->Info & OF_LOAD) != 0 && |
810 | CE_IsConstImm (E) && |
811 | CE_HasLabel (E) && |
812 | (N = CS_GetNextEntry (S, I)) != 0 && |
813 | !CE_UseLoadFlags (N)) { |
814 | |
815 | unsigned J; |
816 | int K; |
817 | |
818 | /* New jump label */ |
819 | CodeLabel* LN = 0; |
820 | |
821 | /* Walk over all insn that jump here */ |
822 | for (J = 0; J < CE_GetLabelCount (E); ++J) { |
823 | |
824 | /* Get the label */ |
825 | CodeLabel* L = CE_GetLabel (E, J); |
826 | |
827 | /* Loop over all insn that reference this label. Since we may |
828 | ** eventually remove a reference in the loop, we must loop |
829 | ** from end down to start. |
830 | */ |
831 | for (K = CL_GetRefCount (L) - 1; K >= 0; --K) { |
832 | |
833 | /* Get the entry that jumps here */ |
834 | CodeEntry* Jump = CL_GetRef (L, K); |
835 | |
836 | /* Get the register info from this insn */ |
837 | short Val = RegVal (E->Chg, &Jump->RI->Out2); |
838 | |
839 | /* Check if the outgoing value is the one thats's loaded */ |
840 | if (Val == (unsigned char) E->Num) { |
841 | |
842 | /* OK, skip the insn. First, generate a label for the |
843 | ** next insn after E. |
844 | */ |
845 | if (LN == 0) { |
846 | LN = CS_GenLabel (S, N); |
847 | } |
848 | |
849 | /* Change the jump target to point to this new label */ |
850 | CS_MoveLabelRef (S, Jump, LN); |
851 | |
852 | /* Remember that we had changes */ |
853 | ++Changes; |
854 | } |
855 | } |
856 | } |
857 | |
858 | } |
859 | |
860 | /* Next entry */ |
861 | ++I; |
862 | } |
863 | |
864 | /* Return the number of changes made */ |
865 | return Changes; |
866 | } |
867 | |
868 | |
869 | |
870 | /*****************************************************************************/ |
871 | /* Optimize conditional branches */ |
872 | /*****************************************************************************/ |
873 | |
874 | |
875 | |
876 | unsigned OptCondBranches1 (CodeSeg* S) |
877 | /* Performs several optimization steps: |
878 | ** |
879 | ** - If an immediate load of a register is followed by a conditional jump that |
880 | ** is never taken because the load of the register sets the flags in such a |
881 | ** manner, remove the conditional branch. |
882 | ** - If the conditional branch is always taken because of the register load, |
883 | ** replace it by a jmp. |
884 | ** - If a conditional branch jumps around an unconditional branch, remove the |
885 | ** conditional branch and make the jump a conditional branch with the |
886 | ** inverse condition of the first one. |
887 | */ |
888 | { |
889 | unsigned Changes = 0; |
890 | |
891 | /* Walk over the entries */ |
892 | unsigned I = 0; |
893 | while (I < CS_GetEntryCount (S)) { |
894 | |
895 | CodeEntry* N; |
896 | CodeLabel* L; |
897 | |
898 | /* Get next entry */ |
899 | CodeEntry* E = CS_GetEntry (S, I); |
900 | |
901 | /* Check if it's a register load */ |
902 | if ((E->Info & OF_LOAD) != 0 && /* It's a load instruction */ |
903 | E->AM == AM65_IMM && /* ..with immidiate addressing */ |
904 | (E->Flags & CEF_NUMARG) != 0 && /* ..and a numeric argument. */ |
905 | (N = CS_GetNextEntry (S, I)) != 0 && /* There is a following entry */ |
906 | (N->Info & OF_CBRA) != 0 && /* ..which is a conditional branch */ |
907 | !CE_HasLabel (N)) { /* ..and does not have a label */ |
908 | |
909 | /* Get the branch condition */ |
910 | bc_t BC = GetBranchCond (N->OPC); |
911 | |
912 | /* Check the argument against the branch condition */ |
913 | if ((BC == BC_EQ && E->Num != 0) || |
914 | (BC == BC_NE && E->Num == 0) || |
915 | (BC == BC_PL && (E->Num & 0x80) != 0) || |
916 | (BC == BC_MI && (E->Num & 0x80) == 0)) { |
917 | |
918 | /* Remove the conditional branch */ |
919 | CS_DelEntry (S, I+1); |
920 | |
921 | /* Remember, we had changes */ |
922 | ++Changes; |
923 | |
924 | } else if ((BC == BC_EQ && E->Num == 0) || |
925 | (BC == BC_NE && E->Num != 0) || |
926 | (BC == BC_PL && (E->Num & 0x80) == 0) || |
927 | (BC == BC_MI && (E->Num & 0x80) != 0)) { |
928 | |
929 | /* The branch is always taken, replace it by a jump */ |
930 | CE_ReplaceOPC (N, OP65_JMP); |
931 | |
932 | /* Remember, we had changes */ |
933 | ++Changes; |
934 | } |
935 | |
936 | } |
937 | |
938 | if ((E->Info & OF_CBRA) != 0 && /* It's a conditional branch */ |
939 | (L = E->JumpTo) != 0 && /* ..referencing a local label */ |
940 | (N = CS_GetNextEntry (S, I)) != 0 && /* There is a following entry */ |
941 | (N->Info & OF_UBRA) != 0 && /* ..which is an uncond branch, */ |
942 | !CE_HasLabel (N) && /* ..has no label attached */ |
943 | L->Owner == CS_GetNextEntry (S, I+1)) { /* ..and jump target follows */ |
944 | |
945 | /* Replace the jump by a conditional branch with the inverse branch |
946 | ** condition than the branch around it. |
947 | */ |
948 | CE_ReplaceOPC (N, GetInverseBranch (E->OPC)); |
949 | |
950 | /* Remove the conditional branch */ |
951 | CS_DelEntry (S, I); |
952 | |
953 | /* Remember, we had changes */ |
954 | ++Changes; |
955 | |
956 | } |
957 | |
958 | /* Next entry */ |
959 | ++I; |
960 | |
961 | } |
962 | |
963 | /* Return the number of changes made */ |
964 | return Changes; |
965 | } |
966 | |
967 | |
968 | |
969 | unsigned OptCondBranches2 (CodeSeg* S) |
970 | /* If on entry to a "rol a" instruction the accu is zero, and a beq/bne follows, |
971 | ** we can remove the rol and branch on the state of the carry flag. |
972 | */ |
973 | { |
974 | unsigned Changes = 0; |
975 | unsigned I; |
976 | |
977 | /* Walk over the entries */ |
978 | I = 0; |
979 | while (I < CS_GetEntryCount (S)) { |
980 | |
981 | CodeEntry* N; |
982 | |
983 | /* Get next entry */ |
984 | CodeEntry* E = CS_GetEntry (S, I); |
985 | |
986 | /* Check if it's a rol insn with A in accu and a branch follows */ |
987 | if (E->OPC == OP65_ROL && |
988 | E->AM == AM65_ACC && |
989 | E->RI->In.RegA == 0 && |
990 | !CE_HasLabel (E) && |
991 | (N = CS_GetNextEntry (S, I)) != 0 && |
992 | (N->Info & OF_ZBRA) != 0 && |
993 | !RegAUsed (S, I+1)) { |
994 | |
995 | /* Replace the branch condition */ |
996 | switch (GetBranchCond (N->OPC)) { |
997 | case BC_EQ: CE_ReplaceOPC (N, OP65_JCC); break; |
998 | case BC_NE: CE_ReplaceOPC (N, OP65_JCS); break; |
999 | default: Internal ("Unknown branch condition in OptCondBranches2" ); |
1000 | } |
1001 | |
1002 | /* Delete the rol insn */ |
1003 | CS_DelEntry (S, I); |
1004 | |
1005 | /* Remember, we had changes */ |
1006 | ++Changes; |
1007 | } |
1008 | |
1009 | /* Next entry */ |
1010 | ++I; |
1011 | } |
1012 | |
1013 | /* Return the number of changes made */ |
1014 | return Changes; |
1015 | } |
1016 | |
1017 | |
1018 | |
1019 | /*****************************************************************************/ |
1020 | /* Remove unused loads and stores */ |
1021 | /*****************************************************************************/ |
1022 | |
1023 | |
1024 | |
1025 | unsigned OptUnusedLoads (CodeSeg* S) |
1026 | /* Remove loads of registers where the value loaded is not used later. */ |
1027 | { |
1028 | unsigned Changes = 0; |
1029 | |
1030 | /* Walk over the entries */ |
1031 | unsigned I = 0; |
1032 | while (I < CS_GetEntryCount (S)) { |
1033 | |
1034 | CodeEntry* N; |
1035 | |
1036 | /* Get next entry */ |
1037 | CodeEntry* E = CS_GetEntry (S, I); |
1038 | |
1039 | /* Check if it's a register load or transfer insn */ |
1040 | if ((E->Info & (OF_LOAD | OF_XFR | OF_REG_INCDEC)) != 0 && |
1041 | (N = CS_GetNextEntry (S, I)) != 0 && |
1042 | !CE_UseLoadFlags (N)) { |
1043 | |
1044 | /* Check which sort of load or transfer it is */ |
1045 | unsigned R; |
1046 | switch (E->OPC) { |
1047 | case OP65_DEA: |
1048 | case OP65_INA: |
1049 | case OP65_LDA: |
1050 | case OP65_TXA: |
1051 | case OP65_TYA: R = REG_A; break; |
1052 | case OP65_DEX: |
1053 | case OP65_INX: |
1054 | case OP65_LDX: |
1055 | case OP65_TAX: R = REG_X; break; |
1056 | case OP65_DEY: |
1057 | case OP65_INY: |
1058 | case OP65_LDY: |
1059 | case OP65_TAY: R = REG_Y; break; |
1060 | default: goto NextEntry; /* OOPS */ |
1061 | } |
1062 | |
1063 | /* Get register usage and check if the register value is used later */ |
1064 | if ((GetRegInfo (S, I+1, R) & R) == 0) { |
1065 | |
1066 | /* Register value is not used, remove the load */ |
1067 | CS_DelEntry (S, I); |
1068 | |
1069 | /* Remember, we had changes. Account the deleted entry in I. */ |
1070 | ++Changes; |
1071 | --I; |
1072 | |
1073 | } |
1074 | } |
1075 | |
1076 | NextEntry: |
1077 | /* Next entry */ |
1078 | ++I; |
1079 | |
1080 | } |
1081 | |
1082 | /* Return the number of changes made */ |
1083 | return Changes; |
1084 | } |
1085 | |
1086 | |
1087 | |
1088 | unsigned OptUnusedStores (CodeSeg* S) |
1089 | /* Remove stores into zero page registers that aren't used later */ |
1090 | { |
1091 | unsigned Changes = 0; |
1092 | |
1093 | /* Walk over the entries */ |
1094 | unsigned I = 0; |
1095 | while (I < CS_GetEntryCount (S)) { |
1096 | |
1097 | /* Get next entry */ |
1098 | CodeEntry* E = CS_GetEntry (S, I); |
1099 | |
1100 | /* Check if it's a register load or transfer insn */ |
1101 | if ((E->Info & OF_STORE) != 0 && |
1102 | E->AM == AM65_ZP && |
1103 | (E->Chg & REG_ZP) != 0) { |
1104 | |
1105 | /* Check for the zero page location. We know that there cannot be |
1106 | ** more than one zero page location involved in the store. |
1107 | */ |
1108 | unsigned R = E->Chg & REG_ZP; |
1109 | |
1110 | /* Get register usage and check if the register value is used later */ |
1111 | if ((GetRegInfo (S, I+1, R) & R) == 0) { |
1112 | |
1113 | /* Register value is not used, remove the load */ |
1114 | CS_DelEntry (S, I); |
1115 | |
1116 | /* Remember, we had changes */ |
1117 | ++Changes; |
1118 | |
1119 | /* Continue with next insn */ |
1120 | continue; |
1121 | } |
1122 | } |
1123 | |
1124 | /* Next entry */ |
1125 | ++I; |
1126 | |
1127 | } |
1128 | |
1129 | /* Return the number of changes made */ |
1130 | return Changes; |
1131 | } |
1132 | |
1133 | |
1134 | |
1135 | unsigned OptDupLoads (CodeSeg* S) |
1136 | /* Remove loads of registers where the value loaded is already in the register. */ |
1137 | { |
1138 | unsigned Changes = 0; |
1139 | unsigned I; |
1140 | |
1141 | /* Walk over the entries */ |
1142 | I = 0; |
1143 | while (I < CS_GetEntryCount (S)) { |
1144 | |
1145 | CodeEntry* N; |
1146 | |
1147 | /* Get next entry */ |
1148 | CodeEntry* E = CS_GetEntry (S, I); |
1149 | |
1150 | /* Assume we won't delete the entry */ |
1151 | int Delete = 0; |
1152 | |
1153 | /* Get a pointer to the input registers of the insn */ |
1154 | const RegContents* In = &E->RI->In; |
1155 | |
1156 | /* Handle the different instructions */ |
1157 | switch (E->OPC) { |
1158 | |
1159 | case OP65_LDA: |
1160 | if (RegValIsKnown (In->RegA) && /* Value of A is known */ |
1161 | CE_IsKnownImm (E, In->RegA) && /* Value to be loaded is known */ |
1162 | (N = CS_GetNextEntry (S, I)) != 0 && /* There is a next entry */ |
1163 | !CE_UseLoadFlags (N)) { /* Which does not use the flags */ |
1164 | Delete = 1; |
1165 | } |
1166 | break; |
1167 | |
1168 | case OP65_LDX: |
1169 | if (RegValIsKnown (In->RegX) && /* Value of X is known */ |
1170 | CE_IsKnownImm (E, In->RegX) && /* Value to be loaded is known */ |
1171 | (N = CS_GetNextEntry (S, I)) != 0 && /* There is a next entry */ |
1172 | !CE_UseLoadFlags (N)) { /* Which does not use the flags */ |
1173 | Delete = 1; |
1174 | } |
1175 | break; |
1176 | |
1177 | case OP65_LDY: |
1178 | if (RegValIsKnown (In->RegY) && /* Value of Y is known */ |
1179 | CE_IsKnownImm (E, In->RegY) && /* Value to be loaded is known */ |
1180 | (N = CS_GetNextEntry (S, I)) != 0 && /* There is a next entry */ |
1181 | !CE_UseLoadFlags (N)) { /* Which does not use the flags */ |
1182 | Delete = 1; |
1183 | } |
1184 | break; |
1185 | |
1186 | case OP65_STA: |
1187 | /* If we store into a known zero page location, and this |
1188 | ** location does already contain the value to be stored, |
1189 | ** remove the store. |
1190 | */ |
1191 | if (RegValIsKnown (In->RegA) && /* Value of A is known */ |
1192 | E->AM == AM65_ZP && /* Store into zp */ |
1193 | In->RegA == ZPRegVal (E->Chg, In)) { /* Value identical */ |
1194 | |
1195 | Delete = 1; |
1196 | } |
1197 | break; |
1198 | |
1199 | case OP65_STX: |
1200 | /* If we store into a known zero page location, and this |
1201 | ** location does already contain the value to be stored, |
1202 | ** remove the store. |
1203 | */ |
1204 | if (RegValIsKnown (In->RegX) && /* Value of A is known */ |
1205 | E->AM == AM65_ZP && /* Store into zp */ |
1206 | In->RegX == ZPRegVal (E->Chg, In)) { /* Value identical */ |
1207 | |
1208 | Delete = 1; |
1209 | |
1210 | /* If the value in the X register is known and the same as |
1211 | ** that in the A register, replace the store by a STA. The |
1212 | ** optimizer will then remove the load instruction for X |
1213 | ** later. STX does support the zeropage,y addressing mode, |
1214 | ** so be sure to check for that. |
1215 | */ |
1216 | } else if (RegValIsKnown (In->RegX) && |
1217 | In->RegX == In->RegA && |
1218 | E->AM != AM65_ABSY && |
1219 | E->AM != AM65_ZPY) { |
1220 | /* Use the A register instead */ |
1221 | CE_ReplaceOPC (E, OP65_STA); |
1222 | } |
1223 | break; |
1224 | |
1225 | case OP65_STY: |
1226 | /* If we store into a known zero page location, and this |
1227 | ** location does already contain the value to be stored, |
1228 | ** remove the store. |
1229 | */ |
1230 | if (RegValIsKnown (In->RegY) && /* Value of Y is known */ |
1231 | E->AM == AM65_ZP && /* Store into zp */ |
1232 | In->RegY == ZPRegVal (E->Chg, In)) { /* Value identical */ |
1233 | |
1234 | Delete = 1; |
1235 | |
1236 | /* If the value in the Y register is known and the same as |
1237 | ** that in the A register, replace the store by a STA. The |
1238 | ** optimizer will then remove the load instruction for Y |
1239 | ** later. If replacement by A is not possible try a |
1240 | ** replacement by X, but check for invalid addressing modes |
1241 | ** in this case. |
1242 | */ |
1243 | } else if (RegValIsKnown (In->RegY)) { |
1244 | if (In->RegY == In->RegA) { |
1245 | CE_ReplaceOPC (E, OP65_STA); |
1246 | } else if (In->RegY == In->RegX && |
1247 | E->AM != AM65_ABSX && |
1248 | E->AM != AM65_ZPX) { |
1249 | CE_ReplaceOPC (E, OP65_STX); |
1250 | } |
1251 | } |
1252 | break; |
1253 | |
1254 | case OP65_STZ: |
1255 | /* If we store into a known zero page location, and this |
1256 | ** location does already contain the value to be stored, |
1257 | ** remove the store. |
1258 | */ |
1259 | if ((CPUIsets[CPU] & CPU_ISET_65SC02) != 0 && E->AM == AM65_ZP) { |
1260 | if (ZPRegVal (E->Chg, In) == 0) { |
1261 | Delete = 1; |
1262 | } |
1263 | } |
1264 | break; |
1265 | |
1266 | case OP65_TAX: |
1267 | if (RegValIsKnown (In->RegA) && |
1268 | In->RegA == In->RegX && |
1269 | (N = CS_GetNextEntry (S, I)) != 0 && |
1270 | !CE_UseLoadFlags (N)) { |
1271 | /* Value is identical and not followed by a branch */ |
1272 | Delete = 1; |
1273 | } |
1274 | break; |
1275 | |
1276 | case OP65_TAY: |
1277 | if (RegValIsKnown (In->RegA) && |
1278 | In->RegA == In->RegY && |
1279 | (N = CS_GetNextEntry (S, I)) != 0 && |
1280 | !CE_UseLoadFlags (N)) { |
1281 | /* Value is identical and not followed by a branch */ |
1282 | Delete = 1; |
1283 | } |
1284 | break; |
1285 | |
1286 | case OP65_TXA: |
1287 | if (RegValIsKnown (In->RegX) && |
1288 | In->RegX == In->RegA && |
1289 | (N = CS_GetNextEntry (S, I)) != 0 && |
1290 | !CE_UseLoadFlags (N)) { |
1291 | /* Value is identical and not followed by a branch */ |
1292 | Delete = 1; |
1293 | } |
1294 | break; |
1295 | |
1296 | case OP65_TYA: |
1297 | if (RegValIsKnown (In->RegY) && |
1298 | In->RegY == In->RegA && |
1299 | (N = CS_GetNextEntry (S, I)) != 0 && |
1300 | !CE_UseLoadFlags (N)) { |
1301 | /* Value is identical and not followed by a branch */ |
1302 | Delete = 1; |
1303 | } |
1304 | break; |
1305 | |
1306 | default: |
1307 | break; |
1308 | |
1309 | } |
1310 | |
1311 | /* Delete the entry if requested */ |
1312 | if (Delete) { |
1313 | |
1314 | /* Register value is not used, remove the load */ |
1315 | CS_DelEntry (S, I); |
1316 | |
1317 | /* Remember, we had changes */ |
1318 | ++Changes; |
1319 | |
1320 | } else { |
1321 | |
1322 | /* Next entry */ |
1323 | ++I; |
1324 | |
1325 | } |
1326 | |
1327 | } |
1328 | |
1329 | /* Return the number of changes made */ |
1330 | return Changes; |
1331 | } |
1332 | |
1333 | |
1334 | |
1335 | unsigned OptStoreLoad (CodeSeg* S) |
1336 | /* Remove a store followed by a load from the same location. */ |
1337 | { |
1338 | unsigned Changes = 0; |
1339 | |
1340 | /* Walk over the entries */ |
1341 | unsigned I = 0; |
1342 | while (I < CS_GetEntryCount (S)) { |
1343 | |
1344 | CodeEntry* N; |
1345 | CodeEntry* X; |
1346 | |
1347 | /* Get next entry */ |
1348 | CodeEntry* E = CS_GetEntry (S, I); |
1349 | |
1350 | /* Check if it is a store instruction followed by a load from the |
1351 | ** same address which is itself not followed by a conditional branch. |
1352 | */ |
1353 | if ((E->Info & OF_STORE) != 0 && |
1354 | (N = CS_GetNextEntry (S, I)) != 0 && |
1355 | !CE_HasLabel (N) && |
1356 | E->AM == N->AM && |
1357 | ((E->OPC == OP65_STA && N->OPC == OP65_LDA) || |
1358 | (E->OPC == OP65_STX && N->OPC == OP65_LDX) || |
1359 | (E->OPC == OP65_STY && N->OPC == OP65_LDY)) && |
1360 | strcmp (E->Arg, N->Arg) == 0 && |
1361 | (X = CS_GetNextEntry (S, I+1)) != 0 && |
1362 | !CE_UseLoadFlags (X)) { |
1363 | |
1364 | /* Register has already the correct value, remove the load */ |
1365 | CS_DelEntry (S, I+1); |
1366 | |
1367 | /* Remember, we had changes */ |
1368 | ++Changes; |
1369 | |
1370 | } |
1371 | |
1372 | /* Next entry */ |
1373 | ++I; |
1374 | |
1375 | } |
1376 | |
1377 | /* Return the number of changes made */ |
1378 | return Changes; |
1379 | } |
1380 | |
1381 | |
1382 | |
1383 | unsigned OptTransfers1 (CodeSeg* S) |
1384 | /* Remove transfers from one register to another and back */ |
1385 | { |
1386 | unsigned Changes = 0; |
1387 | |
1388 | /* Walk over the entries */ |
1389 | unsigned I = 0; |
1390 | while (I < CS_GetEntryCount (S)) { |
1391 | |
1392 | CodeEntry* N; |
1393 | CodeEntry* X; |
1394 | CodeEntry* P; |
1395 | |
1396 | /* Get next entry */ |
1397 | CodeEntry* E = CS_GetEntry (S, I); |
1398 | |
1399 | /* Check if we have two transfer instructions */ |
1400 | if ((E->Info & OF_XFR) != 0 && |
1401 | (N = CS_GetNextEntry (S, I)) != 0 && |
1402 | !CE_HasLabel (N) && |
1403 | (N->Info & OF_XFR) != 0) { |
1404 | |
1405 | /* Check if it's a transfer and back */ |
1406 | if ((E->OPC == OP65_TAX && N->OPC == OP65_TXA && !RegXUsed (S, I+2)) || |
1407 | (E->OPC == OP65_TAY && N->OPC == OP65_TYA && !RegYUsed (S, I+2)) || |
1408 | (E->OPC == OP65_TXA && N->OPC == OP65_TAX && !RegAUsed (S, I+2)) || |
1409 | (E->OPC == OP65_TYA && N->OPC == OP65_TAY && !RegAUsed (S, I+2))) { |
1410 | |
1411 | /* If the next insn is a conditional branch, check if the insn |
1412 | ** preceeding the first xfr will set the flags right, otherwise we |
1413 | ** may not remove the sequence. |
1414 | */ |
1415 | if ((X = CS_GetNextEntry (S, I+1)) == 0) { |
1416 | goto NextEntry; |
1417 | } |
1418 | if (CE_UseLoadFlags (X)) { |
1419 | if (I == 0) { |
1420 | /* No preceeding entry */ |
1421 | goto NextEntry; |
1422 | } |
1423 | P = CS_GetEntry (S, I-1); |
1424 | if ((P->Info & OF_SETF) == 0) { |
1425 | /* Does not set the flags */ |
1426 | goto NextEntry; |
1427 | } |
1428 | } |
1429 | |
1430 | /* Remove both transfers */ |
1431 | CS_DelEntry (S, I+1); |
1432 | CS_DelEntry (S, I); |
1433 | |
1434 | /* Remember, we had changes */ |
1435 | ++Changes; |
1436 | } |
1437 | } |
1438 | |
1439 | NextEntry: |
1440 | /* Next entry */ |
1441 | ++I; |
1442 | |
1443 | } |
1444 | |
1445 | /* Return the number of changes made */ |
1446 | return Changes; |
1447 | } |
1448 | |
1449 | |
1450 | |
1451 | unsigned OptTransfers2 (CodeSeg* S) |
1452 | /* Replace loads followed by a register transfer by a load with the second |
1453 | ** register if possible. |
1454 | */ |
1455 | { |
1456 | unsigned Changes = 0; |
1457 | |
1458 | /* Walk over the entries */ |
1459 | unsigned I = 0; |
1460 | while (I < CS_GetEntryCount (S)) { |
1461 | |
1462 | CodeEntry* N; |
1463 | |
1464 | /* Get next entry */ |
1465 | CodeEntry* E = CS_GetEntry (S, I); |
1466 | |
1467 | /* Check if we have a load followed by a transfer where the loaded |
1468 | ** register is not used later. |
1469 | */ |
1470 | if ((E->Info & OF_LOAD) != 0 && |
1471 | (N = CS_GetNextEntry (S, I)) != 0 && |
1472 | !CE_HasLabel (N) && |
1473 | (N->Info & OF_XFR) != 0 && |
1474 | GetRegInfo (S, I+2, E->Chg) != E->Chg) { |
1475 | |
1476 | CodeEntry* X = 0; |
1477 | |
1478 | if (E->OPC == OP65_LDA && N->OPC == OP65_TAX) { |
1479 | /* LDA/TAX - check for the right addressing modes */ |
1480 | if (E->AM == AM65_IMM || |
1481 | E->AM == AM65_ZP || |
1482 | E->AM == AM65_ABS || |
1483 | E->AM == AM65_ABSY) { |
1484 | /* Replace */ |
1485 | X = NewCodeEntry (OP65_LDX, E->AM, E->Arg, 0, N->LI); |
1486 | } |
1487 | } else if (E->OPC == OP65_LDA && N->OPC == OP65_TAY) { |
1488 | /* LDA/TAY - check for the right addressing modes */ |
1489 | if (E->AM == AM65_IMM || |
1490 | E->AM == AM65_ZP || |
1491 | E->AM == AM65_ZPX || |
1492 | E->AM == AM65_ABS || |
1493 | E->AM == AM65_ABSX) { |
1494 | /* Replace */ |
1495 | X = NewCodeEntry (OP65_LDY, E->AM, E->Arg, 0, N->LI); |
1496 | } |
1497 | } else if (E->OPC == OP65_LDY && N->OPC == OP65_TYA) { |
1498 | /* LDY/TYA. LDA supports all addressing modes LDY does */ |
1499 | X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, N->LI); |
1500 | } else if (E->OPC == OP65_LDX && N->OPC == OP65_TXA) { |
1501 | /* LDX/TXA. LDA doesn't support zp,y, so we must map it to |
1502 | ** abs,y instead. |
1503 | */ |
1504 | am_t AM = (E->AM == AM65_ZPY)? AM65_ABSY : E->AM; |
1505 | X = NewCodeEntry (OP65_LDA, AM, E->Arg, 0, N->LI); |
1506 | } |
1507 | |
1508 | /* If we have a load entry, add it and remove the old stuff */ |
1509 | if (X) { |
1510 | CS_InsertEntry (S, X, I+2); |
1511 | CS_DelEntries (S, I, 2); |
1512 | ++Changes; |
1513 | --I; /* Correct for one entry less */ |
1514 | } |
1515 | } |
1516 | |
1517 | /* Next entry */ |
1518 | ++I; |
1519 | } |
1520 | |
1521 | /* Return the number of changes made */ |
1522 | return Changes; |
1523 | } |
1524 | |
1525 | |
1526 | |
1527 | unsigned OptTransfers3 (CodeSeg* S) |
1528 | /* Replace a register transfer followed by a store of the second register by a |
1529 | ** store of the first register if this is possible. |
1530 | */ |
1531 | { |
1532 | unsigned Changes = 0; |
1533 | unsigned UsedRegs = REG_NONE; /* Track used registers */ |
1534 | unsigned Xfer = 0; /* Index of transfer insn */ |
1535 | unsigned Store = 0; /* Index of store insn */ |
1536 | CodeEntry* XferEntry = 0; /* Pointer to xfer insn */ |
1537 | CodeEntry* StoreEntry = 0; /* Pointer to store insn */ |
1538 | |
1539 | enum { |
1540 | Initialize, |
1541 | Search, |
1542 | FoundXfer, |
1543 | FoundStore |
1544 | } State = Initialize; |
1545 | |
1546 | /* Walk over the entries. Look for a xfer instruction that is followed by |
1547 | ** a store later, where the value of the register is not used later. |
1548 | */ |
1549 | unsigned I = 0; |
1550 | while (I < CS_GetEntryCount (S)) { |
1551 | |
1552 | /* Get next entry */ |
1553 | CodeEntry* E = CS_GetEntry (S, I); |
1554 | |
1555 | switch (State) { |
1556 | |
1557 | case Initialize: |
1558 | /* Clear the list of used registers */ |
1559 | UsedRegs = REG_NONE; |
1560 | /* FALLTHROUGH */ |
1561 | |
1562 | case Search: |
1563 | if (E->Info & OF_XFR) { |
1564 | /* Found start of sequence */ |
1565 | Xfer = I; |
1566 | XferEntry = E; |
1567 | State = FoundXfer; |
1568 | } |
1569 | break; |
1570 | |
1571 | case FoundXfer: |
1572 | /* If we find a conditional jump, abort the sequence, since |
1573 | ** handling them makes things really complicated. |
1574 | */ |
1575 | if (E->Info & OF_CBRA) { |
1576 | |
1577 | /* Switch back to searching */ |
1578 | I = Xfer; |
1579 | State = Initialize; |
1580 | |
1581 | /* Does this insn use the target register of the transfer? */ |
1582 | } else if ((E->Use & XferEntry->Chg) != 0) { |
1583 | |
1584 | /* It it's a store instruction, and the block is a basic |
1585 | ** block, proceed. Otherwise restart |
1586 | */ |
1587 | if ((E->Info & OF_STORE) != 0 && |
1588 | CS_IsBasicBlock (S, Xfer, I)) { |
1589 | Store = I; |
1590 | StoreEntry = E; |
1591 | State = FoundStore; |
1592 | } else { |
1593 | I = Xfer; |
1594 | State = Initialize; |
1595 | } |
1596 | |
1597 | /* Does this insn change the target register of the transfer? */ |
1598 | } else if (E->Chg & XferEntry->Chg) { |
1599 | |
1600 | /* We *may* add code here to remove the transfer, but I'm |
1601 | ** currently not sure about the consequences, so I won't |
1602 | ** do that and bail out instead. |
1603 | */ |
1604 | I = Xfer; |
1605 | State = Initialize; |
1606 | |
1607 | /* Does this insn have a label? */ |
1608 | } else if (CE_HasLabel (E)) { |
1609 | |
1610 | /* Too complex to handle - bail out */ |
1611 | I = Xfer; |
1612 | State = Initialize; |
1613 | |
1614 | } else { |
1615 | /* Track used registers */ |
1616 | UsedRegs |= E->Use; |
1617 | } |
1618 | break; |
1619 | |
1620 | case FoundStore: |
1621 | /* We are at the instruction behind the store. If the register |
1622 | ** isn't used later, and we have an address mode match, we can |
1623 | ** replace the transfer by a store and remove the store here. |
1624 | */ |
1625 | if ((GetRegInfo (S, I, XferEntry->Chg) & XferEntry->Chg) == 0 && |
1626 | (StoreEntry->AM == AM65_ABS || |
1627 | StoreEntry->AM == AM65_ZP) && |
1628 | (StoreEntry->AM != AM65_ZP || |
1629 | (StoreEntry->Chg & UsedRegs) == 0) && |
1630 | !MemAccess (S, Xfer+1, Store-1, StoreEntry)) { |
1631 | |
1632 | /* Generate the replacement store insn */ |
1633 | CodeEntry* X = 0; |
1634 | switch (XferEntry->OPC) { |
1635 | |
1636 | case OP65_TXA: |
1637 | X = NewCodeEntry (OP65_STX, |
1638 | StoreEntry->AM, |
1639 | StoreEntry->Arg, |
1640 | 0, |
1641 | StoreEntry->LI); |
1642 | break; |
1643 | |
1644 | case OP65_TAX: |
1645 | X = NewCodeEntry (OP65_STA, |
1646 | StoreEntry->AM, |
1647 | StoreEntry->Arg, |
1648 | 0, |
1649 | StoreEntry->LI); |
1650 | break; |
1651 | |
1652 | case OP65_TYA: |
1653 | X = NewCodeEntry (OP65_STY, |
1654 | StoreEntry->AM, |
1655 | StoreEntry->Arg, |
1656 | 0, |
1657 | StoreEntry->LI); |
1658 | break; |
1659 | |
1660 | case OP65_TAY: |
1661 | X = NewCodeEntry (OP65_STA, |
1662 | StoreEntry->AM, |
1663 | StoreEntry->Arg, |
1664 | 0, |
1665 | StoreEntry->LI); |
1666 | break; |
1667 | |
1668 | default: |
1669 | break; |
1670 | } |
1671 | |
1672 | /* If we have a replacement store, change the code */ |
1673 | if (X) { |
1674 | /* Insert after the xfer insn */ |
1675 | CS_InsertEntry (S, X, Xfer+1); |
1676 | |
1677 | /* Remove the xfer instead */ |
1678 | CS_DelEntry (S, Xfer); |
1679 | |
1680 | /* Remove the final store */ |
1681 | CS_DelEntry (S, Store); |
1682 | |
1683 | /* Correct I so we continue with the next insn */ |
1684 | I -= 2; |
1685 | |
1686 | /* Remember we had changes */ |
1687 | ++Changes; |
1688 | } else { |
1689 | /* Restart after last xfer insn */ |
1690 | I = Xfer; |
1691 | } |
1692 | } else { |
1693 | /* Restart after last xfer insn */ |
1694 | I = Xfer; |
1695 | } |
1696 | State = Initialize; |
1697 | break; |
1698 | |
1699 | } |
1700 | |
1701 | /* Next entry */ |
1702 | ++I; |
1703 | } |
1704 | |
1705 | /* Return the number of changes made */ |
1706 | return Changes; |
1707 | } |
1708 | |
1709 | |
1710 | |
1711 | unsigned OptTransfers4 (CodeSeg* S) |
1712 | /* Replace a load of a register followed by a transfer insn of the same register |
1713 | ** by a load of the second register if possible. |
1714 | */ |
1715 | { |
1716 | unsigned Changes = 0; |
1717 | unsigned Load = 0; /* Index of load insn */ |
1718 | unsigned Xfer = 0; /* Index of transfer insn */ |
1719 | CodeEntry* LoadEntry = 0; /* Pointer to load insn */ |
1720 | CodeEntry* XferEntry = 0; /* Pointer to xfer insn */ |
1721 | |
1722 | enum { |
1723 | Search, |
1724 | FoundLoad, |
1725 | FoundXfer |
1726 | } State = Search; |
1727 | |
1728 | /* Walk over the entries. Look for a load instruction that is followed by |
1729 | ** a load later. |
1730 | */ |
1731 | unsigned I = 0; |
1732 | while (I < CS_GetEntryCount (S)) { |
1733 | |
1734 | /* Get next entry */ |
1735 | CodeEntry* E = CS_GetEntry (S, I); |
1736 | |
1737 | switch (State) { |
1738 | |
1739 | case Search: |
1740 | if (E->Info & OF_LOAD) { |
1741 | /* Found start of sequence */ |
1742 | Load = I; |
1743 | LoadEntry = E; |
1744 | State = FoundLoad; |
1745 | } |
1746 | break; |
1747 | |
1748 | case FoundLoad: |
1749 | /* If we find a conditional jump, abort the sequence, since |
1750 | ** handling them makes things really complicated. |
1751 | */ |
1752 | if (E->Info & OF_CBRA) { |
1753 | |
1754 | /* Switch back to searching */ |
1755 | I = Load; |
1756 | State = Search; |
1757 | |
1758 | /* Does this insn use the target register of the load? */ |
1759 | } else if ((E->Use & LoadEntry->Chg) != 0) { |
1760 | |
1761 | /* It it's a xfer instruction, and the block is a basic |
1762 | ** block, proceed. Otherwise restart |
1763 | */ |
1764 | if ((E->Info & OF_XFR) != 0 && |
1765 | CS_IsBasicBlock (S, Load, I)) { |
1766 | Xfer = I; |
1767 | XferEntry = E; |
1768 | State = FoundXfer; |
1769 | } else { |
1770 | I = Load; |
1771 | State = Search; |
1772 | } |
1773 | |
1774 | /* Does this insn change the target register of the load? */ |
1775 | } else if (E->Chg & LoadEntry->Chg) { |
1776 | |
1777 | /* We *may* add code here to remove the load, but I'm |
1778 | ** currently not sure about the consequences, so I won't |
1779 | ** do that and bail out instead. |
1780 | */ |
1781 | I = Load; |
1782 | State = Search; |
1783 | } |
1784 | break; |
1785 | |
1786 | case FoundXfer: |
1787 | /* We are at the instruction behind the xfer. If the register |
1788 | ** isn't used later, and we have an address mode match, we can |
1789 | ** replace the transfer by a load and remove the initial load. |
1790 | */ |
1791 | if ((GetRegInfo (S, I, LoadEntry->Chg) & LoadEntry->Chg) == 0 && |
1792 | (LoadEntry->AM == AM65_ABS || |
1793 | LoadEntry->AM == AM65_ZP || |
1794 | LoadEntry->AM == AM65_IMM) && |
1795 | !MemAccess (S, Load+1, Xfer-1, LoadEntry)) { |
1796 | |
1797 | /* Generate the replacement load insn */ |
1798 | CodeEntry* X = 0; |
1799 | switch (XferEntry->OPC) { |
1800 | |
1801 | case OP65_TXA: |
1802 | case OP65_TYA: |
1803 | X = NewCodeEntry (OP65_LDA, |
1804 | LoadEntry->AM, |
1805 | LoadEntry->Arg, |
1806 | 0, |
1807 | LoadEntry->LI); |
1808 | break; |
1809 | |
1810 | case OP65_TAX: |
1811 | X = NewCodeEntry (OP65_LDX, |
1812 | LoadEntry->AM, |
1813 | LoadEntry->Arg, |
1814 | 0, |
1815 | LoadEntry->LI); |
1816 | break; |
1817 | |
1818 | case OP65_TAY: |
1819 | X = NewCodeEntry (OP65_LDY, |
1820 | LoadEntry->AM, |
1821 | LoadEntry->Arg, |
1822 | 0, |
1823 | LoadEntry->LI); |
1824 | break; |
1825 | |
1826 | default: |
1827 | break; |
1828 | } |
1829 | |
1830 | /* If we have a replacement load, change the code */ |
1831 | if (X) { |
1832 | /* Insert after the xfer insn */ |
1833 | CS_InsertEntry (S, X, Xfer+1); |
1834 | |
1835 | /* Remove the xfer instead */ |
1836 | CS_DelEntry (S, Xfer); |
1837 | |
1838 | /* Remove the initial load */ |
1839 | CS_DelEntry (S, Load); |
1840 | |
1841 | /* Correct I so we continue with the next insn */ |
1842 | I -= 2; |
1843 | |
1844 | /* Remember we had changes */ |
1845 | ++Changes; |
1846 | } else { |
1847 | /* Restart after last xfer insn */ |
1848 | I = Xfer; |
1849 | } |
1850 | } else { |
1851 | /* Restart after last xfer insn */ |
1852 | I = Xfer; |
1853 | } |
1854 | State = Search; |
1855 | break; |
1856 | |
1857 | } |
1858 | |
1859 | /* Next entry */ |
1860 | ++I; |
1861 | } |
1862 | |
1863 | /* Return the number of changes made */ |
1864 | return Changes; |
1865 | } |
1866 | |
1867 | |
1868 | |
1869 | unsigned OptPushPop (CodeSeg* S) |
1870 | /* Remove a PHA/PLA sequence were A is not used later */ |
1871 | { |
1872 | unsigned Changes = 0; |
1873 | unsigned Push = 0; /* Index of push insn */ |
1874 | unsigned Pop = 0; /* Index of pop insn */ |
1875 | unsigned ChgA = 0; /* Flag for A changed */ |
1876 | enum { |
1877 | Searching, |
1878 | FoundPush, |
1879 | FoundPop |
1880 | } State = Searching; |
1881 | |
1882 | /* Walk over the entries. Look for a push instruction that is followed by |
1883 | ** a pop later, where the pop is not followed by an conditional branch, |
1884 | ** and where the value of the A register is not used later on. |
1885 | ** Look out for the following problems: |
1886 | ** |
1887 | ** - There may be another PHA/PLA inside the sequence: Restart it. |
1888 | ** - If the PLA has a label, all jumps to this label must be inside |
1889 | ** the sequence, otherwise we cannot remove the PHA/PLA. |
1890 | */ |
1891 | unsigned I = 0; |
1892 | while (I < CS_GetEntryCount (S)) { |
1893 | |
1894 | CodeEntry* X; |
1895 | |
1896 | /* Get next entry */ |
1897 | CodeEntry* E = CS_GetEntry (S, I); |
1898 | |
1899 | switch (State) { |
1900 | |
1901 | case Searching: |
1902 | if (E->OPC == OP65_PHA) { |
1903 | /* Found start of sequence */ |
1904 | Push = I; |
1905 | ChgA = 0; |
1906 | State = FoundPush; |
1907 | } |
1908 | break; |
1909 | |
1910 | case FoundPush: |
1911 | if (E->OPC == OP65_PHA) { |
1912 | /* Inner push/pop, restart */ |
1913 | Push = I; |
1914 | ChgA = 0; |
1915 | } else if (E->OPC == OP65_PLA) { |
1916 | /* Found a matching pop */ |
1917 | Pop = I; |
1918 | /* Check that the block between Push and Pop is a basic |
1919 | ** block (one entry, one exit). Otherwise ignore it. |
1920 | */ |
1921 | if (CS_IsBasicBlock (S, Push, Pop)) { |
1922 | State = FoundPop; |
1923 | } else { |
1924 | /* Go into searching mode again */ |
1925 | State = Searching; |
1926 | } |
1927 | } else if (E->Chg & REG_A) { |
1928 | ChgA = 1; |
1929 | } |
1930 | break; |
1931 | |
1932 | case FoundPop: |
1933 | /* We're at the instruction after the PLA. |
1934 | ** Check for the following conditions: |
1935 | ** - If this instruction is a store of A that doesn't use |
1936 | ** another register, if the instruction does not have a |
1937 | ** label, and A is not used later, we may replace the PHA |
1938 | ** by the store and remove pla if several other conditions |
1939 | ** are met. |
1940 | ** - If this instruction is not a conditional branch, and A |
1941 | ** is either unused later, or not changed by the code |
1942 | ** between push and pop, we may remove PHA and PLA. |
1943 | */ |
1944 | if (E->OPC == OP65_STA && |
1945 | (E->AM == AM65_ABS || E->AM == AM65_ZP) && |
1946 | !CE_HasLabel (E) && |
1947 | !RegAUsed (S, I+1) && |
1948 | !MemAccess (S, Push+1, Pop-1, E)) { |
1949 | |
1950 | /* Insert a STA after the PHA */ |
1951 | X = NewCodeEntry (E->OPC, E->AM, E->Arg, E->JumpTo, E->LI); |
1952 | CS_InsertEntry (S, X, Push+1); |
1953 | |
1954 | /* Remove the PHA instead */ |
1955 | CS_DelEntry (S, Push); |
1956 | |
1957 | /* Remove the PLA/STA sequence */ |
1958 | CS_DelEntries (S, Pop, 2); |
1959 | |
1960 | /* Correct I so we continue with the next insn */ |
1961 | I -= 2; |
1962 | |
1963 | /* Remember we had changes */ |
1964 | ++Changes; |
1965 | |
1966 | } else if ((E->Info & OF_CBRA) == 0 && |
1967 | (!RegAUsed (S, I) || !ChgA)) { |
1968 | |
1969 | /* We can remove the PHA and PLA instructions */ |
1970 | CS_DelEntry (S, Pop); |
1971 | CS_DelEntry (S, Push); |
1972 | |
1973 | /* Correct I so we continue with the next insn */ |
1974 | I -= 2; |
1975 | |
1976 | /* Remember we had changes */ |
1977 | ++Changes; |
1978 | |
1979 | } |
1980 | /* Go into search mode again */ |
1981 | State = Searching; |
1982 | break; |
1983 | |
1984 | } |
1985 | |
1986 | /* Next entry */ |
1987 | ++I; |
1988 | } |
1989 | |
1990 | /* Return the number of changes made */ |
1991 | return Changes; |
1992 | } |
1993 | |
1994 | |
1995 | |
1996 | unsigned OptPrecalc (CodeSeg* S) |
1997 | /* Replace immediate operations with the accu where the current contents are |
1998 | ** known by a load of the final value. |
1999 | */ |
2000 | { |
2001 | unsigned Changes = 0; |
2002 | unsigned I; |
2003 | |
2004 | /* Walk over the entries */ |
2005 | I = 0; |
2006 | while (I < CS_GetEntryCount (S)) { |
2007 | |
2008 | /* Get next entry */ |
2009 | CodeEntry* E = CS_GetEntry (S, I); |
2010 | |
2011 | /* Get pointers to the input and output registers of the insn */ |
2012 | const RegContents* Out = &E->RI->Out; |
2013 | const RegContents* In = &E->RI->In; |
2014 | |
2015 | /* Argument for LDn and flag */ |
2016 | const char* Arg = 0; |
2017 | opc_t OPC = OP65_LDA; |
2018 | |
2019 | /* Handle the different instructions */ |
2020 | switch (E->OPC) { |
2021 | |
2022 | case OP65_LDA: |
2023 | if (E->AM != AM65_IMM && RegValIsKnown (Out->RegA)) { |
2024 | /* Result of load is known */ |
2025 | Arg = MakeHexArg (Out->RegA); |
2026 | } |
2027 | break; |
2028 | |
2029 | case OP65_LDX: |
2030 | if (E->AM != AM65_IMM && RegValIsKnown (Out->RegX)) { |
2031 | /* Result of load is known but register is X */ |
2032 | Arg = MakeHexArg (Out->RegX); |
2033 | OPC = OP65_LDX; |
2034 | } |
2035 | break; |
2036 | |
2037 | case OP65_LDY: |
2038 | if (E->AM != AM65_IMM && RegValIsKnown (Out->RegY)) { |
2039 | /* Result of load is known but register is Y */ |
2040 | Arg = MakeHexArg (Out->RegY); |
2041 | OPC = OP65_LDY; |
2042 | } |
2043 | break; |
2044 | |
2045 | case OP65_EOR: |
2046 | if (RegValIsKnown (Out->RegA)) { |
2047 | /* Accu op zp with known contents */ |
2048 | Arg = MakeHexArg (Out->RegA); |
2049 | } |
2050 | break; |
2051 | |
2052 | case OP65_ADC: |
2053 | case OP65_SBC: |
2054 | /* If this is an operation with an immediate operand of zero, |
2055 | ** and the register is zero, the operation won't give us any |
2056 | ** results we don't already have (including the flags), so |
2057 | ** remove it. Something like this is generated as a result of |
2058 | ** a compare where parts of the values are known to be zero. |
2059 | ** The only situation where we need to leave things as they are |
2060 | ** is when V flag is being tested in the next instruction, |
2061 | ** because ADC/SBC #0 always clears it. |
2062 | */ |
2063 | if (In->RegA == 0 && CE_IsKnownImm (E, 0x00) && |
2064 | (E = CS_GetEntry (S, I + 1)) && |
2065 | E->OPC != OP65_BVC && |
2066 | E->OPC != OP65_BVS ) { |
2067 | /* 0-0 or 0+0 -> remove */ |
2068 | CS_DelEntry (S, I); |
2069 | ++Changes; |
2070 | } |
2071 | break; |
2072 | |
2073 | case OP65_AND: |
2074 | if (CE_IsKnownImm (E, 0xFF)) { |
2075 | /* AND with 0xFF, remove */ |
2076 | CS_DelEntry (S, I); |
2077 | ++Changes; |
2078 | } else if (CE_IsKnownImm (E, 0x00)) { |
2079 | /* AND with 0x00, replace by lda #$00 */ |
2080 | Arg = MakeHexArg (0x00); |
2081 | } else if (RegValIsKnown (Out->RegA)) { |
2082 | /* Accu AND zp with known contents */ |
2083 | Arg = MakeHexArg (Out->RegA); |
2084 | } else if (In->RegA == 0xFF) { |
2085 | /* AND but A contains 0xFF - replace by lda */ |
2086 | CE_ReplaceOPC (E, OP65_LDA); |
2087 | ++Changes; |
2088 | } |
2089 | break; |
2090 | |
2091 | case OP65_ORA: |
2092 | if (CE_IsKnownImm (E, 0x00)) { |
2093 | /* ORA with zero, remove */ |
2094 | CS_DelEntry (S, I); |
2095 | ++Changes; |
2096 | } else if (CE_IsKnownImm (E, 0xFF)) { |
2097 | /* ORA with 0xFF, replace by lda #$ff */ |
2098 | Arg = MakeHexArg (0xFF); |
2099 | } else if (RegValIsKnown (Out->RegA)) { |
2100 | /* Accu AND zp with known contents */ |
2101 | Arg = MakeHexArg (Out->RegA); |
2102 | } else if (In->RegA == 0) { |
2103 | /* ORA but A contains 0x00 - replace by lda */ |
2104 | CE_ReplaceOPC (E, OP65_LDA); |
2105 | ++Changes; |
2106 | } |
2107 | break; |
2108 | |
2109 | default: |
2110 | break; |
2111 | |
2112 | } |
2113 | |
2114 | /* Check if we have to replace the insn by LDA */ |
2115 | if (Arg) { |
2116 | CodeEntry* X = NewCodeEntry (OPC, AM65_IMM, Arg, 0, E->LI); |
2117 | CS_InsertEntry (S, X, I+1); |
2118 | CS_DelEntry (S, I); |
2119 | ++Changes; |
2120 | } |
2121 | |
2122 | /* Next entry */ |
2123 | ++I; |
2124 | } |
2125 | |
2126 | /* Return the number of changes made */ |
2127 | return Changes; |
2128 | } |
2129 | |
2130 | |
2131 | |
2132 | /*****************************************************************************/ |
2133 | /* Optimize branch types */ |
2134 | /*****************************************************************************/ |
2135 | |
2136 | |
2137 | |
2138 | unsigned OptBranchDist (CodeSeg* S) |
2139 | /* Change branches for the distance needed. */ |
2140 | { |
2141 | unsigned Changes = 0; |
2142 | |
2143 | /* Walk over the entries */ |
2144 | unsigned I = 0; |
2145 | while (I < CS_GetEntryCount (S)) { |
2146 | |
2147 | /* Get next entry */ |
2148 | CodeEntry* E = CS_GetEntry (S, I); |
2149 | |
2150 | /* Check if it's a conditional branch to a local label. */ |
2151 | if (E->Info & OF_CBRA) { |
2152 | |
2153 | /* Is this a branch to a local symbol? */ |
2154 | if (E->JumpTo != 0) { |
2155 | |
2156 | /* Check if the branch distance is short */ |
2157 | int IsShort = IsShortDist (GetBranchDist (S, I, E->JumpTo->Owner)); |
2158 | |
2159 | /* Make the branch short/long according to distance */ |
2160 | if ((E->Info & OF_LBRA) == 0 && !IsShort) { |
2161 | /* Short branch but long distance */ |
2162 | CE_ReplaceOPC (E, MakeLongBranch (E->OPC)); |
2163 | ++Changes; |
2164 | } else if ((E->Info & OF_LBRA) != 0 && IsShort) { |
2165 | /* Long branch but short distance */ |
2166 | CE_ReplaceOPC (E, MakeShortBranch (E->OPC)); |
2167 | ++Changes; |
2168 | } |
2169 | |
2170 | } else if ((E->Info & OF_LBRA) == 0) { |
2171 | |
2172 | /* Short branch to external symbol - make it long */ |
2173 | CE_ReplaceOPC (E, MakeLongBranch (E->OPC)); |
2174 | ++Changes; |
2175 | |
2176 | } |
2177 | |
2178 | } else if ((CPUIsets[CPU] & CPU_ISET_65SC02) != 0 && |
2179 | (E->Info & OF_UBRA) != 0 && |
2180 | E->JumpTo != 0 && |
2181 | IsShortDist (GetBranchDist (S, I, E->JumpTo->Owner))) { |
2182 | |
2183 | /* The jump is short and may be replaced by a BRA on the 65C02 CPU */ |
2184 | CE_ReplaceOPC (E, OP65_BRA); |
2185 | ++Changes; |
2186 | } |
2187 | |
2188 | /* Next entry */ |
2189 | ++I; |
2190 | |
2191 | } |
2192 | |
2193 | /* Return the number of changes made */ |
2194 | return Changes; |
2195 | } |
2196 | |
2197 | |
2198 | |
2199 | /*****************************************************************************/ |
2200 | /* Optimize indirect loads */ |
2201 | /*****************************************************************************/ |
2202 | |
2203 | |
2204 | |
2205 | unsigned OptIndLoads1 (CodeSeg* S) |
2206 | /* Change |
2207 | ** |
2208 | ** lda (zp),y |
2209 | ** |
2210 | ** into |
2211 | ** |
2212 | ** lda (zp,x) |
2213 | ** |
2214 | ** provided that x and y are both zero. |
2215 | */ |
2216 | { |
2217 | unsigned Changes = 0; |
2218 | unsigned I; |
2219 | |
2220 | /* Walk over the entries */ |
2221 | I = 0; |
2222 | while (I < CS_GetEntryCount (S)) { |
2223 | |
2224 | /* Get next entry */ |
2225 | CodeEntry* E = CS_GetEntry (S, I); |
2226 | |
2227 | /* Check if it's what we're looking for */ |
2228 | if (E->OPC == OP65_LDA && |
2229 | E->AM == AM65_ZP_INDY && |
2230 | E->RI->In.RegY == 0 && |
2231 | E->RI->In.RegX == 0) { |
2232 | |
2233 | /* Replace by the same insn with other addressing mode */ |
2234 | CodeEntry* X = NewCodeEntry (E->OPC, AM65_ZPX_IND, E->Arg, 0, E->LI); |
2235 | CS_InsertEntry (S, X, I+1); |
2236 | |
2237 | /* Remove the old insn */ |
2238 | CS_DelEntry (S, I); |
2239 | ++Changes; |
2240 | } |
2241 | |
2242 | /* Next entry */ |
2243 | ++I; |
2244 | |
2245 | } |
2246 | |
2247 | /* Return the number of changes made */ |
2248 | return Changes; |
2249 | } |
2250 | |
2251 | |
2252 | |
2253 | unsigned OptIndLoads2 (CodeSeg* S) |
2254 | /* Change |
2255 | ** |
2256 | ** lda (zp,x) |
2257 | ** |
2258 | ** into |
2259 | ** |
2260 | ** lda (zp),y |
2261 | ** |
2262 | ** provided that x and y are both zero. |
2263 | */ |
2264 | { |
2265 | unsigned Changes = 0; |
2266 | unsigned I; |
2267 | |
2268 | /* Walk over the entries */ |
2269 | I = 0; |
2270 | while (I < CS_GetEntryCount (S)) { |
2271 | |
2272 | /* Get next entry */ |
2273 | CodeEntry* E = CS_GetEntry (S, I); |
2274 | |
2275 | /* Check if it's what we're looking for */ |
2276 | if (E->OPC == OP65_LDA && |
2277 | E->AM == AM65_ZPX_IND && |
2278 | E->RI->In.RegY == 0 && |
2279 | E->RI->In.RegX == 0) { |
2280 | |
2281 | /* Replace by the same insn with other addressing mode */ |
2282 | CodeEntry* X = NewCodeEntry (E->OPC, AM65_ZP_INDY, E->Arg, 0, E->LI); |
2283 | CS_InsertEntry (S, X, I+1); |
2284 | |
2285 | /* Remove the old insn */ |
2286 | CS_DelEntry (S, I); |
2287 | ++Changes; |
2288 | } |
2289 | |
2290 | /* Next entry */ |
2291 | ++I; |
2292 | |
2293 | } |
2294 | |
2295 | /* Return the number of changes made */ |
2296 | return Changes; |
2297 | } |
2298 | |