]> git.sur5r.net Git - cc65/blob - src/cc65/coptneg.c
Removed (pretty inconsistently used) tab chars from source code base.
[cc65] / src / cc65 / coptneg.c
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
609
610