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
49unsigned 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
101unsigned 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
168unsigned 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
206unsigned 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
279unsigned 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
340unsigned 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
420unsigned 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
476unsigned 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
566unsigned 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