1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* coptneg.c */ |
4 | /* */ |
5 | /* Optimize negation sequences */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 2001-2012, Ullrich von Bassewitz */ |
10 | /* Roemerstrasse 52 */ |
11 | /* D-70794 Filderstadt */ |
12 | /* EMail: uz@cc65.org */ |
13 | /* */ |
14 | /* */ |
15 | /* This software is provided 'as-is', without any expressed or implied */ |
16 | /* warranty. In no event will the authors be held liable for any damages */ |
17 | /* arising from the use of this software. */ |
18 | /* */ |
19 | /* Permission is granted to anyone to use this software for any purpose, */ |
20 | /* including commercial applications, and to alter it and redistribute it */ |
21 | /* freely, subject to the following restrictions: */ |
22 | /* */ |
23 | /* 1. The origin of this software must not be misrepresented; you must not */ |
24 | /* claim that you wrote the original software. If you use this software */ |
25 | /* in a product, an acknowledgment in the product documentation would be */ |
26 | /* appreciated but is not required. */ |
27 | /* 2. Altered source versions must be plainly marked as such, and must not */ |
28 | /* be misrepresented as being the original software. */ |
29 | /* 3. This notice may not be removed or altered from any source */ |
30 | /* distribution. */ |
31 | /* */ |
32 | /*****************************************************************************/ |
33 | |
34 | |
35 | |
36 | /* cc65 */ |
37 | #include "codeent.h" |
38 | #include "codeinfo.h" |
39 | #include "coptneg.h" |
40 | |
41 | |
42 | |
43 | /*****************************************************************************/ |
44 | /* bnega optimizations */ |
45 | /*****************************************************************************/ |
46 | |
47 | |
48 | |
49 | unsigned OptBNegA1 (CodeSeg* S) |
50 | /* Check for |
51 | ** |
52 | ** ldx #$00 |
53 | ** lda .. |
54 | ** jsr bnega |
55 | ** |
56 | ** Remove the ldx if the lda does not use it. |
57 | */ |
58 | { |
59 | unsigned Changes = 0; |
60 | |
61 | /* Walk over the entries */ |
62 | unsigned I = 0; |
63 | while (I < CS_GetEntryCount (S)) { |
64 | |
65 | CodeEntry* L[2]; |
66 | |
67 | /* Get next entry */ |
68 | CodeEntry* E = CS_GetEntry (S, I); |
69 | |
70 | /* Check for a ldx */ |
71 | if (E->OPC == OP65_LDX && |
72 | E->AM == AM65_IMM && |
73 | (E->Flags & CEF_NUMARG) != 0 && |
74 | E->Num == 0 && |
75 | CS_GetEntries (S, L, I+1, 2) && |
76 | L[0]->OPC == OP65_LDA && |
77 | (L[0]->Use & REG_X) == 0 && |
78 | !CE_HasLabel (L[0]) && |
79 | CE_IsCallTo (L[1], "bnega" ) && |
80 | !CE_HasLabel (L[1])) { |
81 | |
82 | /* Remove the ldx instruction */ |
83 | CS_DelEntry (S, I); |
84 | |
85 | /* Remember, we had changes */ |
86 | ++Changes; |
87 | |
88 | } |
89 | |
90 | /* Next entry */ |
91 | ++I; |
92 | |
93 | } |
94 | |
95 | /* Return the number of changes made */ |
96 | return Changes; |
97 | } |
98 | |
99 | |
100 | |
101 | unsigned OptBNegA2 (CodeSeg* S) |
102 | /* Check for |
103 | ** |
104 | ** lda .. |
105 | ** jsr bnega |
106 | ** jeq/jne .. |
107 | ** |
108 | ** Adjust the conditional branch and remove the call to the subroutine. |
109 | */ |
110 | { |
111 | unsigned Changes = 0; |
112 | |
113 | /* Walk over the entries */ |
114 | unsigned I = 0; |
115 | while (I < CS_GetEntryCount (S)) { |
116 | |
117 | CodeEntry* L[2]; |
118 | |
119 | /* Get next entry */ |
120 | CodeEntry* E = CS_GetEntry (S, I); |
121 | |
122 | /* Check for the sequence */ |
123 | if ((E->OPC == OP65_ADC || |
124 | E->OPC == OP65_AND || |
125 | E->OPC == OP65_DEA || |
126 | E->OPC == OP65_EOR || |
127 | E->OPC == OP65_INA || |
128 | E->OPC == OP65_LDA || |
129 | E->OPC == OP65_ORA || |
130 | E->OPC == OP65_PLA || |
131 | E->OPC == OP65_SBC || |
132 | E->OPC == OP65_TXA || |
133 | E->OPC == OP65_TYA) && |
134 | CS_GetEntries (S, L, I+1, 2) && |
135 | CE_IsCallTo (L[0], "bnega" ) && |
136 | !CE_HasLabel (L[0]) && |
137 | (L[1]->Info & OF_ZBRA) != 0 && |
138 | !CE_HasLabel (L[1])) { |
139 | |
140 | /* Invert the branch */ |
141 | CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC)); |
142 | |
143 | /* Delete the subroutine call */ |
144 | CS_DelEntry (S, I+1); |
145 | |
146 | /* Remember, we had changes */ |
147 | ++Changes; |
148 | |
149 | } |
150 | |
151 | /* Next entry */ |
152 | ++I; |
153 | |
154 | } |
155 | |
156 | /* Return the number of changes made */ |
157 | return Changes; |
158 | } |
159 | |
160 | |
161 | |
162 | /*****************************************************************************/ |
163 | /* bnegax optimizations */ |
164 | /*****************************************************************************/ |
165 | |
166 | |
167 | |
168 | unsigned OptBNegAX1 (CodeSeg* S) |
169 | /* On a call to bnegax, if X is zero, the result depends only on the value in |
170 | ** A, so change the call to a call to bnega. This will get further optimized |
171 | ** later if possible. |
172 | */ |
173 | { |
174 | unsigned Changes = 0; |
175 | unsigned I; |
176 | |
177 | /* Walk over the entries */ |
178 | I = 0; |
179 | while (I < CS_GetEntryCount (S)) { |
180 | |
181 | /* Get next entry */ |
182 | CodeEntry* E = CS_GetEntry (S, I); |
183 | |
184 | /* Check if this is a call to bnegax, and if X is known and zero */ |
185 | if (E->RI->In.RegX == 0 && CE_IsCallTo (E, "bnegax" )) { |
186 | |
187 | CodeEntry* X = NewCodeEntry (OP65_JSR, AM65_ABS, "bnega" , 0, E->LI); |
188 | CS_InsertEntry (S, X, I+1); |
189 | CS_DelEntry (S, I); |
190 | |
191 | /* We had changes */ |
192 | ++Changes; |
193 | } |
194 | |
195 | /* Next entry */ |
196 | ++I; |
197 | |
198 | } |
199 | |
200 | /* Return the number of changes made */ |
201 | return Changes; |
202 | } |
203 | |
204 | |
205 | |
206 | unsigned OptBNegAX2 (CodeSeg* S) |
207 | /* Search for the sequence: |
208 | ** |
209 | ** ldy #xx |
210 | ** jsr ldaxysp |
211 | ** jsr bnegax |
212 | ** jne/jeq ... |
213 | ** |
214 | ** and replace it by |
215 | ** |
216 | ** ldy #xx |
217 | ** lda (sp),y |
218 | ** dey |
219 | ** ora (sp),y |
220 | ** jeq/jne ... |
221 | */ |
222 | { |
223 | unsigned Changes = 0; |
224 | |
225 | /* Walk over the entries */ |
226 | unsigned I = 0; |
227 | while (I < CS_GetEntryCount (S)) { |
228 | |
229 | CodeEntry* L[4]; |
230 | |
231 | /* Get next entry */ |
232 | L[0] = CS_GetEntry (S, I); |
233 | |
234 | /* Check for the sequence */ |
235 | if (L[0]->OPC == OP65_LDY && |
236 | CE_IsConstImm (L[0]) && |
237 | !CS_RangeHasLabel (S, I+1, 3) && |
238 | CS_GetEntries (S, L+1, I+1, 3) && |
239 | CE_IsCallTo (L[1], "ldaxysp" ) && |
240 | CE_IsCallTo (L[2], "bnegax" ) && |
241 | (L[3]->Info & OF_ZBRA) != 0) { |
242 | |
243 | CodeEntry* X; |
244 | |
245 | /* lda (sp),y */ |
246 | X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp" , 0, L[1]->LI); |
247 | CS_InsertEntry (S, X, I+1); |
248 | |
249 | /* dey */ |
250 | X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, L[1]->LI); |
251 | CS_InsertEntry (S, X, I+2); |
252 | |
253 | /* ora (sp),y */ |
254 | X = NewCodeEntry (OP65_ORA, AM65_ZP_INDY, "sp" , 0, L[1]->LI); |
255 | CS_InsertEntry (S, X, I+3); |
256 | |
257 | /* Invert the branch */ |
258 | CE_ReplaceOPC (L[3], GetInverseBranch (L[3]->OPC)); |
259 | |
260 | /* Delete the entries no longer needed. */ |
261 | CS_DelEntries (S, I+4, 2); |
262 | |
263 | /* Remember, we had changes */ |
264 | ++Changes; |
265 | |
266 | } |
267 | |
268 | /* Next entry */ |
269 | ++I; |
270 | |
271 | } |
272 | |
273 | /* Return the number of changes made */ |
274 | return Changes; |
275 | } |
276 | |
277 | |
278 | |
279 | unsigned OptBNegAX3 (CodeSeg* S) |
280 | /* Search for the sequence: |
281 | ** |
282 | ** lda xx |
283 | ** ldx yy |
284 | ** jsr bnegax |
285 | ** jne/jeq ... |
286 | ** |
287 | ** and replace it by |
288 | ** |
289 | ** lda xx |
290 | ** ora xx+1 |
291 | ** jeq/jne ... |
292 | */ |
293 | { |
294 | unsigned Changes = 0; |
295 | |
296 | /* Walk over the entries */ |
297 | unsigned I = 0; |
298 | while (I < CS_GetEntryCount (S)) { |
299 | |
300 | CodeEntry* L[3]; |
301 | |
302 | /* Get next entry */ |
303 | CodeEntry* E = CS_GetEntry (S, I); |
304 | |
305 | /* Check for the sequence */ |
306 | if (E->OPC == OP65_LDA && |
307 | CS_GetEntries (S, L, I+1, 3) && |
308 | L[0]->OPC == OP65_LDX && |
309 | !CE_HasLabel (L[0]) && |
310 | CE_IsCallTo (L[1], "bnegax" ) && |
311 | !CE_HasLabel (L[1]) && |
312 | (L[2]->Info & OF_ZBRA) != 0 && |
313 | !CE_HasLabel (L[2])) { |
314 | |
315 | /* ldx --> ora */ |
316 | CE_ReplaceOPC (L[0], OP65_ORA); |
317 | |
318 | /* Invert the branch */ |
319 | CE_ReplaceOPC (L[2], GetInverseBranch (L[2]->OPC)); |
320 | |
321 | /* Delete the subroutine call */ |
322 | CS_DelEntry (S, I+2); |
323 | |
324 | /* Remember, we had changes */ |
325 | ++Changes; |
326 | |
327 | } |
328 | |
329 | /* Next entry */ |
330 | ++I; |
331 | |
332 | } |
333 | |
334 | /* Return the number of changes made */ |
335 | return Changes; |
336 | } |
337 | |
338 | |
339 | |
340 | unsigned OptBNegAX4 (CodeSeg* S) |
341 | /* Search for the sequence: |
342 | ** |
343 | ** jsr xxx |
344 | ** jsr bnega(x) |
345 | ** jeq/jne ... |
346 | ** |
347 | ** and replace it by: |
348 | ** |
349 | ** jsr xxx |
350 | ** <boolean test> |
351 | ** jne/jeq ... |
352 | */ |
353 | { |
354 | unsigned Changes = 0; |
355 | |
356 | /* Walk over the entries */ |
357 | unsigned I = 0; |
358 | while (I < CS_GetEntryCount (S)) { |
359 | |
360 | CodeEntry* L[2]; |
361 | |
362 | /* Get next entry */ |
363 | CodeEntry* E = CS_GetEntry (S, I); |
364 | |
365 | /* Check for the sequence */ |
366 | if (E->OPC == OP65_JSR && |
367 | CS_GetEntries (S, L, I+1, 2) && |
368 | L[0]->OPC == OP65_JSR && |
369 | strncmp (L[0]->Arg,"bnega" ,5) == 0 && |
370 | !CE_HasLabel (L[0]) && |
371 | (L[1]->Info & OF_ZBRA) != 0 && |
372 | !CE_HasLabel (L[1])) { |
373 | |
374 | CodeEntry* X; |
375 | |
376 | /* Check if we're calling bnega or bnegax */ |
377 | int ByteSized = (strcmp (L[0]->Arg, "bnega" ) == 0); |
378 | |
379 | /* Insert apropriate test code */ |
380 | if (ByteSized) { |
381 | /* Test bytes */ |
382 | X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI); |
383 | CS_InsertEntry (S, X, I+2); |
384 | } else { |
385 | /* Test words */ |
386 | X = NewCodeEntry (OP65_STX, AM65_ZP, "tmp1" , 0, L[0]->LI); |
387 | CS_InsertEntry (S, X, I+2); |
388 | X = NewCodeEntry (OP65_ORA, AM65_ZP, "tmp1" , 0, L[0]->LI); |
389 | CS_InsertEntry (S, X, I+3); |
390 | } |
391 | |
392 | /* Delete the subroutine call */ |
393 | CS_DelEntry (S, I+1); |
394 | |
395 | /* Invert the branch */ |
396 | CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC)); |
397 | |
398 | /* Remember, we had changes */ |
399 | ++Changes; |
400 | |
401 | } |
402 | |
403 | /* Next entry */ |
404 | ++I; |
405 | |
406 | } |
407 | |
408 | /* Return the number of changes made */ |
409 | return Changes; |
410 | } |
411 | |
412 | |
413 | |
414 | /*****************************************************************************/ |
415 | /* negax optimizations */ |
416 | /*****************************************************************************/ |
417 | |
418 | |
419 | |
420 | unsigned OptNegAX1 (CodeSeg* S) |
421 | /* Search for a call to negax and replace it by |
422 | ** |
423 | ** eor #$FF |
424 | ** clc |
425 | ** adc #$01 |
426 | ** |
427 | ** if X isn't used later. |
428 | */ |
429 | { |
430 | unsigned Changes = 0; |
431 | unsigned I; |
432 | |
433 | /* Walk over the entries */ |
434 | I = 0; |
435 | while (I < CS_GetEntryCount (S)) { |
436 | |
437 | /* Get next entry */ |
438 | CodeEntry* E = CS_GetEntry (S, I); |
439 | |
440 | /* Check if this is a call to negax, and if X isn't used later */ |
441 | if (CE_IsCallTo (E, "negax" ) && !RegXUsed (S, I+1)) { |
442 | |
443 | CodeEntry* X; |
444 | |
445 | /* Add replacement code behind */ |
446 | X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF" , 0, E->LI); |
447 | CS_InsertEntry (S, X, I+1); |
448 | |
449 | X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, E->LI); |
450 | CS_InsertEntry (S, X, I+2); |
451 | |
452 | X = NewCodeEntry (OP65_ADC, AM65_IMM, "$01" , 0, E->LI); |
453 | CS_InsertEntry (S, X, I+3); |
454 | |
455 | /* Delete the call to negax */ |
456 | CS_DelEntry (S, I); |
457 | |
458 | /* Skip the generated code */ |
459 | I += 2; |
460 | |
461 | /* We had changes */ |
462 | ++Changes; |
463 | } |
464 | |
465 | /* Next entry */ |
466 | ++I; |
467 | |
468 | } |
469 | |
470 | /* Return the number of changes made */ |
471 | return Changes; |
472 | } |
473 | |
474 | |
475 | |
476 | unsigned OptNegAX2 (CodeSeg* S) |
477 | /* Search for a call to negax and replace it by |
478 | ** |
479 | ** ldx #$FF |
480 | ** eor #$FF |
481 | ** clc |
482 | ** adc #$01 |
483 | ** bne L1 |
484 | ** inx |
485 | ** L1: |
486 | ** |
487 | ** if X is known and zero on entry. |
488 | */ |
489 | { |
490 | unsigned Changes = 0; |
491 | unsigned I; |
492 | |
493 | /* Walk over the entries */ |
494 | I = 0; |
495 | while (I < CS_GetEntryCount (S)) { |
496 | |
497 | CodeEntry* P; |
498 | |
499 | /* Get next entry */ |
500 | CodeEntry* E = CS_GetEntry (S, I); |
501 | |
502 | /* Check if this is a call to negax, and if X is known and zero */ |
503 | if (E->RI->In.RegX == 0 && |
504 | CE_IsCallTo (E, "negax" ) && |
505 | (P = CS_GetNextEntry (S, I)) != 0) { |
506 | |
507 | CodeEntry* X; |
508 | CodeLabel* L; |
509 | |
510 | /* Add replacement code behind */ |
511 | |
512 | /* ldx #$FF */ |
513 | X = NewCodeEntry (OP65_LDX, AM65_IMM, "$FF" , 0, E->LI); |
514 | CS_InsertEntry (S, X, I+1); |
515 | |
516 | /* eor #$FF */ |
517 | X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF" , 0, E->LI); |
518 | CS_InsertEntry (S, X, I+2); |
519 | |
520 | /* clc */ |
521 | X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, E->LI); |
522 | CS_InsertEntry (S, X, I+3); |
523 | |
524 | /* adc #$01 */ |
525 | X = NewCodeEntry (OP65_ADC, AM65_IMM, "$01" , 0, E->LI); |
526 | CS_InsertEntry (S, X, I+4); |
527 | |
528 | /* Get the label attached to the insn following the call */ |
529 | L = CS_GenLabel (S, P); |
530 | |
531 | /* bne L */ |
532 | X = NewCodeEntry (OP65_BNE, AM65_BRA, L->Name, L, E->LI); |
533 | CS_InsertEntry (S, X, I+5); |
534 | |
535 | /* inx */ |
536 | X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, E->LI); |
537 | CS_InsertEntry (S, X, I+6); |
538 | |
539 | /* Delete the call to negax */ |
540 | CS_DelEntry (S, I); |
541 | |
542 | /* Skip the generated code */ |
543 | I += 5; |
544 | |
545 | /* We had changes */ |
546 | ++Changes; |
547 | } |
548 | |
549 | /* Next entry */ |
550 | ++I; |
551 | |
552 | } |
553 | |
554 | /* Return the number of changes made */ |
555 | return Changes; |
556 | } |
557 | |
558 | |
559 | |
560 | /*****************************************************************************/ |
561 | /* complax optimizations */ |
562 | /*****************************************************************************/ |
563 | |
564 | |
565 | |
566 | unsigned OptComplAX1 (CodeSeg* S) |
567 | /* Search for a call to complax and replace it by |
568 | ** |
569 | ** eor #$FF |
570 | ** |
571 | ** if X isn't used later. |
572 | */ |
573 | { |
574 | unsigned Changes = 0; |
575 | unsigned I; |
576 | |
577 | /* Walk over the entries */ |
578 | I = 0; |
579 | while (I < CS_GetEntryCount (S)) { |
580 | |
581 | /* Get next entry */ |
582 | CodeEntry* E = CS_GetEntry (S, I); |
583 | |
584 | /* Check if this is a call to negax, and if X isn't used later */ |
585 | if (CE_IsCallTo (E, "complax" ) && !RegXUsed (S, I+1)) { |
586 | |
587 | CodeEntry* X; |
588 | |
589 | /* Add replacement code behind */ |
590 | X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF" , 0, E->LI); |
591 | CS_InsertEntry (S, X, I+1); |
592 | |
593 | /* Delete the call to negax */ |
594 | CS_DelEntry (S, I); |
595 | |
596 | /* We had changes */ |
597 | ++Changes; |
598 | } |
599 | |
600 | /* Next entry */ |
601 | ++I; |
602 | |
603 | } |
604 | |
605 | /* Return the number of changes made */ |
606 | return Changes; |
607 | } |
608 | |