]> git.sur5r.net Git - cc65/blob - src/cc65/coptshift.c
More shift optimizations.
[cc65] / src / cc65 / coptshift.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                coptshift.c                                */
4 /*                                                                           */
5 /*                              Optimize shifts                              */
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 /* common */
37 #include "chartype.h"
38
39 /* cc65 */
40 #include "codeent.h"
41 #include "codeinfo.h"
42 #include "coptshift.h"
43
44
45
46 /*****************************************************************************/
47 /*                                     Data                                  */
48 /*****************************************************************************/
49
50
51
52 /* Shift types. Shift type is in the first byte, shift count in the second */
53 enum {
54     SHIFT_NONE          = 0x0000,
55
56     /* Masks */
57     SHIFT_MASK_COUNT    = 0x00FF,
58     SHIFT_MASK_DIR      = 0x0F00,
59     SHIFT_MASK_MODE     = 0xF000,       /* Arithmetic or logical */
60     SHIFT_MASK_TYPE     = SHIFT_MASK_DIR | SHIFT_MASK_MODE,
61
62     /* Shift counts */
63     SHIFT_COUNT_Y       = 0x0000,       /* Count is in Y register */
64     SHIFT_COUNT_1       = 0x0001,
65     SHIFT_COUNT_2       = 0x0002,
66     SHIFT_COUNT_3       = 0x0003,
67     SHIFT_COUNT_4       = 0x0004,
68     SHIFT_COUNT_5       = 0x0005,
69     SHIFT_COUNT_6       = 0x0006,
70     SHIFT_COUNT_7       = 0x0007,
71
72     /* Shift directions */
73     SHIFT_DIR_LEFT      = 0x0100,
74     SHIFT_DIR_RIGHT     = 0x0200,
75
76     /* Shift modes */
77     SHIFT_MODE_ARITH    = 0x1000,
78     SHIFT_MODE_LOGICAL  = 0x2000,
79
80     /* Shift types */
81     SHIFT_TYPE_ASL      = SHIFT_DIR_LEFT  | SHIFT_MODE_ARITH,
82     SHIFT_TYPE_ASR      = SHIFT_DIR_RIGHT | SHIFT_MODE_ARITH,
83     SHIFT_TYPE_LSL      = SHIFT_DIR_LEFT  | SHIFT_MODE_LOGICAL,
84     SHIFT_TYPE_LSR      = SHIFT_DIR_RIGHT | SHIFT_MODE_LOGICAL,
85
86     /* Complete specs */
87     SHIFT_ASL_Y         = SHIFT_TYPE_ASL | SHIFT_COUNT_Y,
88     SHIFT_ASR_Y         = SHIFT_TYPE_ASR | SHIFT_COUNT_Y,
89     SHIFT_LSL_Y         = SHIFT_TYPE_LSL | SHIFT_COUNT_Y,
90     SHIFT_LSR_Y         = SHIFT_TYPE_LSR | SHIFT_COUNT_Y,
91
92     SHIFT_ASL_1         = SHIFT_TYPE_ASL | SHIFT_COUNT_1,
93     SHIFT_ASR_1         = SHIFT_TYPE_ASR | SHIFT_COUNT_1,
94     SHIFT_LSL_1         = SHIFT_TYPE_LSL | SHIFT_COUNT_1,
95     SHIFT_LSR_1         = SHIFT_TYPE_LSR | SHIFT_COUNT_1,
96
97     SHIFT_ASL_2         = SHIFT_TYPE_ASL | SHIFT_COUNT_2,
98     SHIFT_ASR_2         = SHIFT_TYPE_ASR | SHIFT_COUNT_2,
99     SHIFT_LSL_2         = SHIFT_TYPE_LSL | SHIFT_COUNT_2,
100     SHIFT_LSR_2         = SHIFT_TYPE_LSR | SHIFT_COUNT_2,
101
102     SHIFT_ASL_3         = SHIFT_TYPE_ASL | SHIFT_COUNT_3,
103     SHIFT_ASR_3         = SHIFT_TYPE_ASR | SHIFT_COUNT_3,
104     SHIFT_LSL_3         = SHIFT_TYPE_LSL | SHIFT_COUNT_3,
105     SHIFT_LSR_3         = SHIFT_TYPE_LSR | SHIFT_COUNT_3,
106
107     SHIFT_ASL_4         = SHIFT_TYPE_ASL | SHIFT_COUNT_4,
108     SHIFT_ASR_4         = SHIFT_TYPE_ASR | SHIFT_COUNT_4,
109     SHIFT_LSL_4         = SHIFT_TYPE_LSL | SHIFT_COUNT_4,
110     SHIFT_LSR_4         = SHIFT_TYPE_LSR | SHIFT_COUNT_4,
111
112     SHIFT_ASL_5         = SHIFT_TYPE_ASL | SHIFT_COUNT_5,
113     SHIFT_ASR_5         = SHIFT_TYPE_ASR | SHIFT_COUNT_5,
114     SHIFT_LSL_5         = SHIFT_TYPE_LSL | SHIFT_COUNT_5,
115     SHIFT_LSR_5         = SHIFT_TYPE_LSR | SHIFT_COUNT_5,
116
117     SHIFT_ASL_6         = SHIFT_TYPE_ASL | SHIFT_COUNT_6,
118     SHIFT_ASR_6         = SHIFT_TYPE_ASR | SHIFT_COUNT_6,
119     SHIFT_LSL_6         = SHIFT_TYPE_LSL | SHIFT_COUNT_6,
120     SHIFT_LSR_6         = SHIFT_TYPE_LSR | SHIFT_COUNT_6,
121
122     SHIFT_ASL_7         = SHIFT_TYPE_ASL | SHIFT_COUNT_7,
123     SHIFT_ASR_7         = SHIFT_TYPE_ASR | SHIFT_COUNT_7,
124     SHIFT_LSL_7         = SHIFT_TYPE_LSL | SHIFT_COUNT_7,
125     SHIFT_LSR_7         = SHIFT_TYPE_LSR | SHIFT_COUNT_7,
126 };
127
128
129
130 /* Macros to extract values from a shift type */
131 #define SHIFT_COUNT(S)          ((S) & SHIFT_MASK_COUNT)
132 #define SHIFT_DIR(S)            ((S) & SHIFT_MASK_DIR)
133 #define SHIFT_MODE(S)           ((S) & SHIFT_MASK_MODE)
134 #define SHIFT_TYPE(S)           ((S) & SHIFT_MASK_TYPE)
135
136
137
138 /*****************************************************************************/
139 /*                              Helper routines                              */
140 /*****************************************************************************/
141
142
143
144 static unsigned GetShift (const char* Name)
145 /* Determine the shift from the name of the subroutine */
146 {
147     unsigned Type;
148
149     if (strncmp (Name, "aslax", 5) == 0) {
150         Type = SHIFT_TYPE_ASL;
151     } else if (strncmp (Name, "asrax", 5) == 0) {
152         Type = SHIFT_TYPE_ASR;
153     } else if (strncmp (Name, "shlax", 5) == 0) {
154         Type = SHIFT_TYPE_LSL;
155     } else if (strncmp (Name, "shrax", 5) == 0) {
156         Type = SHIFT_TYPE_LSR;
157     } else {
158         /* Nothing we know */
159         return SHIFT_NONE;
160     }
161
162     /* Get the count */
163     switch (Name[5]) {
164         case 'y':       Type |= SHIFT_COUNT_Y;  break;
165         case '1':       Type |= SHIFT_COUNT_1;  break;
166         case '2':       Type |= SHIFT_COUNT_2;  break;
167         case '3':       Type |= SHIFT_COUNT_3;  break;
168         case '4':       Type |= SHIFT_COUNT_4;  break;
169         case '5':       Type |= SHIFT_COUNT_5;  break;
170         case '6':       Type |= SHIFT_COUNT_6;  break;
171         case '7':       Type |= SHIFT_COUNT_7;  break;
172         default:        return SHIFT_NONE;
173     }
174
175     /* Make sure nothing follows */
176     if (Name[6] == '\0') {
177         return Type;
178     } else {
179         return SHIFT_NONE;
180     }
181 }
182
183
184
185 /*****************************************************************************/
186 /*                              Optimize shifts                              */
187 /*****************************************************************************/
188
189
190
191 unsigned OptShift1 (CodeSeg* S)
192 /* A call to the shlaxN routine may get replaced by one or more asl insns
193  * if the value of X is not used later. If X is used later, but it is zero
194  * on entry and it's a shift by one, it may get replaced by:
195  *
196  *      asl     a
197  *      bcc     L1
198  *      inx
199  *  L1:
200  *
201  */
202 {
203     unsigned Changes = 0;
204     unsigned I;
205
206     /* Walk over the entries */
207     I = 0;
208     while (I < CS_GetEntryCount (S)) {
209
210         unsigned   Shift;
211         CodeEntry* N;
212         CodeEntry* X;
213         CodeLabel* L;
214
215         /* Get next entry */
216         CodeEntry* E = CS_GetEntry (S, I);
217
218         /* Check for the sequence */
219         if (E->OPC == OP65_JSR                          &&
220             (Shift = GetShift (E->Arg)) != SHIFT_NONE   &&
221             SHIFT_DIR (Shift) == SHIFT_DIR_LEFT) {
222
223
224             unsigned Count = SHIFT_COUNT (Shift);
225             if (!RegXUsed (S, I+1)) {
226
227                 if (Count == SHIFT_COUNT_Y) {
228
229                     CodeLabel* L;
230
231                     if (S->CodeSizeFactor < 200) {
232                         goto NextEntry;
233                     }
234
235                     /* Change into
236                      *
237                      * L1:  asl     a
238                      *      dey
239                      *      bpl     L1
240                      *      ror     a
241                      */
242
243                     /* asl a */
244                     X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI);
245                     CS_InsertEntry (S, X, I+1);
246                     L = CS_GenLabel (S, X);
247
248                     /* dey */
249                     X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, E->LI);
250                     CS_InsertEntry (S, X, I+2);
251
252                     /* bpl L1 */
253                     X = NewCodeEntry (OP65_BPL, AM65_BRA, L->Name, L, E->LI);
254                     CS_InsertEntry (S, X, I+3);
255
256                     /* ror a */
257                     X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, E->LI);
258                     CS_InsertEntry (S, X, I+4);
259
260                 } else {
261                     /* Insert shift insns */
262                     while (Count--) {
263                         X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI);
264                         CS_InsertEntry (S, X, I+1);
265                     }
266                 }
267
268             } else if (E->RI->In.RegX == 0              &&
269                        Count == 1                       &&
270                        (N = CS_GetNextEntry (S, I)) != 0) {
271
272                 /* asl a */
273                 X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI);
274                 CS_InsertEntry (S, X, I+1);
275
276                 /* bcc L1 */
277                 L = CS_GenLabel (S, N);
278                 X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, E->LI);
279                 CS_InsertEntry (S, X, I+2);
280
281                 /* inx */
282                 X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, E->LI);
283                 CS_InsertEntry (S, X, I+3);
284
285             } else {
286
287                 /* We won't handle this one */
288                 goto NextEntry;
289
290             }
291
292             /* Delete the call to shlax */
293             CS_DelEntry (S, I);
294
295             /* Remember, we had changes */
296             ++Changes;
297         }
298
299 NextEntry:
300         /* Next entry */
301         ++I;
302
303     }
304
305     /* Return the number of changes made */
306     return Changes;
307 }
308
309
310
311 unsigned OptShift2(CodeSeg* S)
312 /* A call to the asrax1 routines may get replaced by something simpler, if
313  * X is not used later:
314  *
315  *      cmp     #$80
316  *      ror     a
317  *
318  */
319 {
320     unsigned Changes = 0;
321     unsigned I;
322
323     /* Walk over the entries */
324     I = 0;
325     while (I < CS_GetEntryCount (S)) {
326
327         unsigned Shift;
328         unsigned Count;
329
330         /* Get next entry */
331         CodeEntry* E = CS_GetEntry (S, I);
332
333         /* Check for the sequence */
334         if (E->OPC == OP65_JSR                          &&
335             (Shift = GetShift (E->Arg)) != SHIFT_NONE   &&
336             SHIFT_TYPE (Shift) == SHIFT_TYPE_ASR        &&
337             (Count = SHIFT_COUNT (Shift)) > 0           &&
338             Count * 100 <= S->CodeSizeFactor    &&
339             !RegXUsed (S, I+1)) {
340
341             CodeEntry* X;
342             unsigned J = I+1;
343
344             /* Generate the replacement sequence */
345             while (Count--) {
346                 /* cmp #$80 */
347                 X = NewCodeEntry (OP65_CMP, AM65_IMM, "$80", 0, E->LI);
348                 CS_InsertEntry (S, X, J++);
349
350                 /* ror a */
351                 X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, E->LI);
352                 CS_InsertEntry (S, X, J++);
353             }
354
355             /* Delete the call to asrax */
356             CS_DelEntry (S, I);
357
358             /* Remember, we had changes */
359             ++Changes;
360         }
361
362         /* Next entry */
363         ++I;
364
365     }
366
367     /* Return the number of changes made */
368     return Changes;
369 }
370
371
372
373 unsigned OptShift3 (CodeSeg* S)
374 /* The sequence
375  *
376  *      bcc     L
377  *      inx
378  * L:   jsr     shrax1
379  *
380  * may get replaced by
381  *
382  *      ror     a
383  *
384  * if X is zero on entry and unused later. For shift counts > 1, more
385  *
386  *      shr     a
387  *
388  * must be added.
389  */
390 {
391     unsigned Changes = 0;
392     unsigned I;
393
394     /* Walk over the entries */
395     I = 0;
396     while (I < CS_GetEntryCount (S)) {
397
398         unsigned   Shift;
399         unsigned   Count;
400         CodeEntry* L[3];
401
402         /* Get next entry */
403         L[0] = CS_GetEntry (S, I);
404
405         /* Check for the sequence */
406         if ((L[0]->OPC == OP65_BCC || L[0]->OPC == OP65_JCC)    &&
407             L[0]->JumpTo != 0                                   &&
408             L[0]->RI->In.RegX == 0                              &&
409             CS_GetEntries (S, L+1, I+1, 2)                      &&
410             L[1]->OPC == OP65_INX                               &&
411             L[0]->JumpTo->Owner == L[2]                         &&
412             !CS_RangeHasLabel (S, I, 2)                         &&
413             L[2]->OPC == OP65_JSR                               &&
414             (Shift = GetShift (L[2]->Arg)) != SHIFT_NONE        &&
415             SHIFT_TYPE (Shift) == SHIFT_TYPE_ASR                &&
416             (Count = SHIFT_COUNT (Shift)) > 0                   &&
417             !RegXUsed (S, I+3)) {
418
419             /* Add the replacement insn instead */
420             CodeEntry* X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, L[2]->LI);
421             CS_InsertEntry (S, X, I+3);
422             while (--Count) {
423                 X = NewCodeEntry (OP65_LSR, AM65_ACC, "a", 0, L[2]->LI);
424                 CS_InsertEntry (S, X, I+4);
425             }
426
427             /* Remove the bcs/dex/jsr */
428             CS_DelEntries (S, I, 3);
429
430             /* Remember, we had changes */
431             ++Changes;
432
433         }
434
435         /* Next entry */
436         ++I;
437
438     }
439
440     /* Return the number of changes made */
441     return Changes;
442 }
443
444
445
446 unsigned OptShift4 (CodeSeg* S)
447 /* Calls to the asraxN or shraxN routines may get replaced by one or more lsr
448  * insns if the value of X is zero.
449  */
450 {
451     unsigned Changes = 0;
452     unsigned I;
453
454     /* Walk over the entries */
455     I = 0;
456     while (I < CS_GetEntryCount (S)) {
457
458         unsigned Shift;
459         unsigned Count;
460
461         /* Get next entry */
462         CodeEntry* E = CS_GetEntry (S, I);
463
464         /* Check for the sequence */
465         if (E->OPC == OP65_JSR                          &&
466             (Shift = GetShift (E->Arg)) != SHIFT_NONE   &&
467             SHIFT_DIR (Shift) == SHIFT_DIR_RIGHT        &&
468             E->RI->In.RegX == 0) {
469
470             CodeEntry* X;
471
472             /* Shift count may be in Y */
473             Count = SHIFT_COUNT (Shift);
474             if (Count == SHIFT_COUNT_Y) {
475
476                 CodeLabel* L;
477
478                 if (S->CodeSizeFactor < 200) {
479                     /* Not acceptable */
480                     goto NextEntry;
481                 }
482
483                 /* Generate:
484                  *
485                  * L1: lsr     a
486                  *     dey
487                  *     bpl     L1
488                  *     rol     a
489                  *
490                  * A negative shift count or one that is greater or equal than
491                  * the bit width of the left operand (which is promoted to
492                  * integer before the operation) causes undefined behaviour, so
493                  * above transformation is safe.
494                  */
495
496                 /* lsr a */
497                 X = NewCodeEntry (OP65_LSR, AM65_ACC, "a", 0, E->LI);
498                 CS_InsertEntry (S, X, I+1);
499                 L = CS_GenLabel (S, X);
500
501                 /* dey */
502                 X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, E->LI);
503                 CS_InsertEntry (S, X, I+2);
504
505                 /* bpl L1 */
506                 X = NewCodeEntry (OP65_BPL, AM65_BRA, L->Name, L, E->LI);
507                 CS_InsertEntry (S, X, I+3);
508
509                 /* rol a */
510                 X = NewCodeEntry (OP65_ROL, AM65_ACC, "a", 0, E->LI);
511                 CS_InsertEntry (S, X, I+4);
512
513             } else {
514                 /* Insert shift insns */
515                 while (Count--) {
516                     X = NewCodeEntry (OP65_LSR, AM65_ACC, "a", 0, E->LI);
517                     CS_InsertEntry (S, X, I+1);
518                 }
519
520             }
521
522             /* Delete the call to shrax */
523             CS_DelEntry (S, I);
524
525             /* Remember, we had changes */
526             ++Changes;
527
528         }
529
530 NextEntry:
531         /* Next entry */
532         ++I;
533
534     }
535
536     /* Return the number of changes made */
537     return Changes;
538 }
539
540
541
542 unsigned OptShift5 (CodeSeg* S)
543 /* Search for the sequence
544  *
545  *      lda     xxx
546  *      ldx     yyy
547  *      jsr     aslax1/asrax1/shlax1/shrax1
548  *      sta     aaa
549  *      stx     bbb
550  *
551  * and replace it by
552  *
553  *      lda     xxx
554  *      asl     a
555  *      sta     aaa
556  *      lda     yyy
557  *      rol     a
558  *      sta     bbb
559  *
560  * or similar, provided that a/x is not used later
561  */
562 {
563     unsigned Changes = 0;
564
565     /* Walk over the entries */
566     unsigned I = 0;
567     while (I < CS_GetEntryCount (S)) {
568
569         unsigned ShiftType;
570         CodeEntry* L[5];
571
572         /* Get next entry */
573         L[0] = CS_GetEntry (S, I);
574
575         /* Check for the sequence */
576         if (L[0]->OPC == OP65_LDA                               &&
577             (L[0]->AM == AM65_ABS || L[0]->AM == AM65_ZP)       &&
578             CS_GetEntries (S, L+1, I+1, 4)                      &&
579             !CS_RangeHasLabel (S, I+1, 4)                       &&
580             L[1]->OPC == OP65_LDX                               &&
581             (L[1]->AM == AM65_ABS || L[1]->AM == AM65_ZP)       &&
582             L[2]->OPC == OP65_JSR                               &&
583             (ShiftType = GetShift (L[2]->Arg)) != SHIFT_NONE    &&
584             SHIFT_COUNT(ShiftType) == 1                         &&
585             L[3]->OPC == OP65_STA                               &&
586             (L[3]->AM == AM65_ABS || L[3]->AM == AM65_ZP)       &&
587             L[4]->OPC == OP65_STX                               &&
588             (L[4]->AM == AM65_ABS || L[4]->AM == AM65_ZP)       &&
589             !RegAXUsed (S, I+5)) {
590
591             CodeEntry* X;
592
593             /* Handle the four shift types differently */
594             switch (ShiftType) {
595
596                 case SHIFT_ASR_1:
597                     X = NewCodeEntry (OP65_LDA, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
598                     CS_InsertEntry (S, X, I+5);
599                     X = NewCodeEntry (OP65_CMP, AM65_IMM, "$80", 0, L[2]->LI);
600                     CS_InsertEntry (S, X, I+6);
601                     X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, L[2]->LI);
602                     CS_InsertEntry (S, X, I+7);
603                     X = NewCodeEntry (OP65_STA, L[4]->AM, L[4]->Arg, 0, L[4]->LI);
604                     CS_InsertEntry (S, X, I+8);
605                     X = NewCodeEntry (OP65_LDA, L[0]->AM, L[0]->Arg, 0, L[0]->LI);
606                     CS_InsertEntry (S, X, I+9);
607                     X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, L[2]->LI);
608                     CS_InsertEntry (S, X, I+10);
609                     X = NewCodeEntry (OP65_STA, L[3]->AM, L[3]->Arg, 0, L[3]->LI);
610                     CS_InsertEntry (S, X, I+11);
611                     CS_DelEntries (S, I, 5);
612                     break;
613
614                 case SHIFT_LSR_1:
615                     X = NewCodeEntry (OP65_LDA, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
616                     CS_InsertEntry (S, X, I+5);
617                     X = NewCodeEntry (OP65_LSR, AM65_ACC, "a", 0, L[2]->LI);
618                     CS_InsertEntry (S, X, I+6);
619                     X = NewCodeEntry (OP65_STA, L[4]->AM, L[4]->Arg, 0, L[4]->LI);
620                     CS_InsertEntry (S, X, I+7);
621                     X = NewCodeEntry (OP65_LDA, L[0]->AM, L[0]->Arg, 0, L[0]->LI);
622                     CS_InsertEntry (S, X, I+8);
623                     X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, L[2]->LI);
624                     CS_InsertEntry (S, X, I+9);
625                     X = NewCodeEntry (OP65_STA, L[3]->AM, L[3]->Arg, 0, L[3]->LI);
626                     CS_InsertEntry (S, X, I+10);
627                     CS_DelEntries (S, I, 5);
628                     break;
629
630                 case SHIFT_LSL_1:
631                 case SHIFT_ASL_1:
632                     /* These two are identical */
633                     X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, L[2]->LI);
634                     CS_InsertEntry (S, X, I+1);
635                     X = NewCodeEntry (OP65_STA, L[3]->AM, L[3]->Arg, 0, L[3]->LI);
636                     CS_InsertEntry (S, X, I+2);
637                     X = NewCodeEntry (OP65_LDA, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
638                     CS_InsertEntry (S, X, I+3);
639                     X = NewCodeEntry (OP65_ROL, AM65_ACC, "a", 0, L[2]->LI);
640                     CS_InsertEntry (S, X, I+4);
641                     X = NewCodeEntry (OP65_STA, L[4]->AM, L[4]->Arg, 0, L[4]->LI);
642                     CS_InsertEntry (S, X, I+5);
643                     CS_DelEntries (S, I+6, 4);
644                     break;
645
646             }
647
648             /* Remember, we had changes */
649             ++Changes;
650
651         }
652
653         /* Next entry */
654         ++I;
655
656     }
657
658     /* Return the number of changes made */
659     return Changes;
660 }
661
662
663
664 unsigned OptShift6 (CodeSeg* S)
665 /* Inline the shift subroutines. */
666 {
667     unsigned Changes = 0;
668
669     /* Walk over the entries */
670     unsigned I = 0;
671     while (I < CS_GetEntryCount (S)) {
672
673         unsigned   Shift;
674         unsigned   Count;
675         CodeEntry* X;
676         unsigned   IP;
677
678         /* Get next entry */
679         CodeEntry* E = CS_GetEntry (S, I);
680
681         /* Check for a call to one of the shift routine */
682         if (E->OPC == OP65_JSR                          &&
683             (Shift = GetShift (E->Arg)) != SHIFT_NONE   &&
684             SHIFT_DIR (Shift) == SHIFT_DIR_LEFT         &&
685             (Count = SHIFT_COUNT (Shift)) > 0) {
686
687             /* Code is:
688              *
689              *      stx     tmp1
690              *      asl     a
691              *      rol     tmp1
692              *      (repeat ShiftCount-1 times)
693              *      ldx     tmp1
694              *
695              * which makes 4 + 3 * ShiftCount bytes, compared to the original
696              * 3 bytes for the subroutine call. However, in most cases, the
697              * final load of the X register gets merged with some other insn
698              * and replaces a txa, so for a shift count of 1, we get a factor
699              * of 200, which matches nicely the CodeSizeFactor enabled with -Oi
700              */
701             if (Count > 1 || S->CodeSizeFactor > 200) {
702                 unsigned Size = 4 + 3 * Count;
703                 if ((Size * 100 / 3) > S->CodeSizeFactor) {
704                     /* Not acceptable */
705                     goto NextEntry;
706                 }
707             }
708
709             /* Inline the code. Insertion point is behind the subroutine call */
710             IP = (I + 1);
711
712             /* stx tmp1 */
713             X = NewCodeEntry (OP65_STX, AM65_ZP, "tmp1", 0, E->LI);
714             CS_InsertEntry (S, X, IP++);
715
716             while (Count--) {
717                 /* asl a */
718                 X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI);
719                 CS_InsertEntry (S, X, IP++);
720
721                 /* rol tmp1 */
722                 X = NewCodeEntry (OP65_ROL, AM65_ZP, "tmp1", 0, E->LI);
723                 CS_InsertEntry (S, X, IP++);
724             }
725
726             /* ldx tmp1 */
727             X = NewCodeEntry (OP65_LDX, AM65_ZP, "tmp1", 0, E->LI);
728             CS_InsertEntry (S, X, IP++);
729
730             /* Remove the subroutine call */
731             CS_DelEntry (S, I);
732
733             /* Remember, we had changes */
734             ++Changes;
735         }
736
737 NextEntry:
738         /* Next entry */
739         ++I;
740
741     }
742
743     /* Return the number of changes made */
744     return Changes;
745 }
746
747
748