]> git.sur5r.net Git - cc65/blob - src/cc65/coptshift.c
933c3dfa328aa78976a686ecae7708bf9972be81
[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. 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_DIR (Shift) == SHIFT_DIR_RIGHT                &&
416             (Count = SHIFT_COUNT (Shift)) > 0) {
417                                                 
418             /* Add the replacement insn instead */
419             CodeEntry* X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, L[2]->LI);
420             CS_InsertEntry (S, X, I+3);
421             while (--Count) {
422                 X = NewCodeEntry (OP65_LSR, AM65_ACC, "a", 0, L[2]->LI);
423                 CS_InsertEntry (S, X, I+4);
424             }
425
426             /* Remove the bcs/dex/jsr */
427             CS_DelEntries (S, I, 3);
428
429             /* Remember, we had changes */
430             ++Changes;
431
432         }
433
434         /* Next entry */
435         ++I;
436
437     }
438
439     /* Return the number of changes made */
440     return Changes;
441 }
442
443
444
445 unsigned OptShift4 (CodeSeg* S)
446 /* Calls to the asraxN or shraxN routines may get replaced by one or more lsr
447  * insns if the value of X is zero.
448  */
449 {
450     unsigned Changes = 0;
451     unsigned I;
452
453     /* Walk over the entries */
454     I = 0;
455     while (I < CS_GetEntryCount (S)) {
456
457         unsigned Shift;
458         unsigned Count;
459
460         /* Get next entry */
461         CodeEntry* E = CS_GetEntry (S, I);
462
463         /* Check for the sequence */
464         if (E->OPC == OP65_JSR                          &&
465             (Shift = GetShift (E->Arg)) != SHIFT_NONE   &&
466             SHIFT_DIR (Shift) == SHIFT_DIR_RIGHT        &&
467             E->RI->In.RegX == 0) {
468
469             CodeEntry* X;
470
471             /* Shift count may be in Y */
472             Count = SHIFT_COUNT (Shift);
473             if (Count == SHIFT_COUNT_Y) {
474
475                 CodeLabel* L;
476
477                 if (S->CodeSizeFactor < 200) {
478                     /* Not acceptable */
479                     goto NextEntry;
480                 }
481
482                 /* Generate:
483                  *
484                  * L1: lsr     a
485                  *     dey
486                  *     bpl     L1
487                  *     rol     a
488                  *
489                  * A negative shift count or one that is greater or equal than
490                  * the bit width of the left operand (which is promoted to
491                  * integer before the operation) causes undefined behaviour, so
492                  * above transformation is safe.
493                  */
494
495                 /* lsr a */
496                 X = NewCodeEntry (OP65_LSR, AM65_ACC, "a", 0, E->LI);
497                 CS_InsertEntry (S, X, I+1);
498                 L = CS_GenLabel (S, X);
499
500                 /* dey */
501                 X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, E->LI);
502                 CS_InsertEntry (S, X, I+2);
503
504                 /* bpl L1 */
505                 X = NewCodeEntry (OP65_BPL, AM65_BRA, L->Name, L, E->LI);
506                 CS_InsertEntry (S, X, I+3);
507
508                 /* rol a */
509                 X = NewCodeEntry (OP65_ROL, AM65_ACC, "a", 0, E->LI);
510                 CS_InsertEntry (S, X, I+4);
511
512             } else {
513                 /* Insert shift insns */
514                 while (Count--) {
515                     X = NewCodeEntry (OP65_LSR, AM65_ACC, "a", 0, E->LI);
516                     CS_InsertEntry (S, X, I+1);
517                 }
518
519             }
520
521             /* Delete the call to shrax */
522             CS_DelEntry (S, I);
523
524             /* Remember, we had changes */
525             ++Changes;
526
527         }
528
529 NextEntry:
530         /* Next entry */
531         ++I;
532
533     }
534
535     /* Return the number of changes made */
536     return Changes;
537 }
538
539
540
541 unsigned OptShift5 (CodeSeg* S)
542 /* Search for the sequence
543  *
544  *      lda     xxx
545  *      ldx     yyy
546  *      jsr     aslax1/asrax1/shlax1/shrax1
547  *      sta     aaa
548  *      stx     bbb
549  *
550  * and replace it by
551  *
552  *      lda     xxx
553  *      asl     a
554  *      sta     aaa
555  *      lda     yyy
556  *      rol     a
557  *      sta     bbb
558  *
559  * or similar, provided that a/x is not used later
560  */
561 {
562     unsigned Changes = 0;
563
564     /* Walk over the entries */
565     unsigned I = 0;
566     while (I < CS_GetEntryCount (S)) {
567
568         unsigned ShiftType;
569         CodeEntry* L[5];
570
571         /* Get next entry */
572         L[0] = CS_GetEntry (S, I);
573
574         /* Check for the sequence */
575         if (L[0]->OPC == OP65_LDA                               &&
576             (L[0]->AM == AM65_ABS || L[0]->AM == AM65_ZP)       &&
577             CS_GetEntries (S, L+1, I+1, 4)                      &&
578             !CS_RangeHasLabel (S, I+1, 4)                       &&
579             L[1]->OPC == OP65_LDX                               &&
580             (L[1]->AM == AM65_ABS || L[1]->AM == AM65_ZP)       &&
581             L[2]->OPC == OP65_JSR                               &&
582             (ShiftType = GetShift (L[2]->Arg)) != SHIFT_NONE    &&
583             SHIFT_COUNT(ShiftType) == 1                         &&
584             L[3]->OPC == OP65_STA                               &&
585             (L[3]->AM == AM65_ABS || L[3]->AM == AM65_ZP)       &&
586             L[4]->OPC == OP65_STX                               &&
587             (L[4]->AM == AM65_ABS || L[4]->AM == AM65_ZP)       &&
588             !RegAXUsed (S, I+5)) {
589
590             CodeEntry* X;
591
592             /* Handle the four shift types differently */
593             switch (ShiftType) {
594
595                 case SHIFT_ASR_1:
596                     X = NewCodeEntry (OP65_LDA, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
597                     CS_InsertEntry (S, X, I+5);
598                     X = NewCodeEntry (OP65_CMP, AM65_IMM, "$80", 0, L[2]->LI);
599                     CS_InsertEntry (S, X, I+6);
600                     X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, L[2]->LI);
601                     CS_InsertEntry (S, X, I+7);
602                     X = NewCodeEntry (OP65_STA, L[4]->AM, L[4]->Arg, 0, L[4]->LI);
603                     CS_InsertEntry (S, X, I+8);
604                     X = NewCodeEntry (OP65_LDA, L[0]->AM, L[0]->Arg, 0, L[0]->LI);
605                     CS_InsertEntry (S, X, I+9);
606                     X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, L[2]->LI);
607                     CS_InsertEntry (S, X, I+10);
608                     X = NewCodeEntry (OP65_STA, L[3]->AM, L[3]->Arg, 0, L[3]->LI);
609                     CS_InsertEntry (S, X, I+11);
610                     CS_DelEntries (S, I, 5);
611                     break;
612
613                 case SHIFT_LSR_1:
614                     X = NewCodeEntry (OP65_LDA, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
615                     CS_InsertEntry (S, X, I+5);
616                     X = NewCodeEntry (OP65_LSR, AM65_ACC, "a", 0, L[2]->LI);
617                     CS_InsertEntry (S, X, I+6);
618                     X = NewCodeEntry (OP65_STA, L[4]->AM, L[4]->Arg, 0, L[4]->LI);
619                     CS_InsertEntry (S, X, I+7);
620                     X = NewCodeEntry (OP65_LDA, L[0]->AM, L[0]->Arg, 0, L[0]->LI);
621                     CS_InsertEntry (S, X, I+8);
622                     X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, L[2]->LI);
623                     CS_InsertEntry (S, X, I+9);
624                     X = NewCodeEntry (OP65_STA, L[3]->AM, L[3]->Arg, 0, L[3]->LI);
625                     CS_InsertEntry (S, X, I+10);
626                     CS_DelEntries (S, I, 5);
627                     break;
628
629                 case SHIFT_LSL_1:
630                 case SHIFT_ASL_1:
631                     /* These two are identical */
632                     X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, L[2]->LI);
633                     CS_InsertEntry (S, X, I+1);
634                     X = NewCodeEntry (OP65_STA, L[3]->AM, L[3]->Arg, 0, L[3]->LI);
635                     CS_InsertEntry (S, X, I+2);
636                     X = NewCodeEntry (OP65_LDA, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
637                     CS_InsertEntry (S, X, I+3);
638                     X = NewCodeEntry (OP65_ROL, AM65_ACC, "a", 0, L[2]->LI);
639                     CS_InsertEntry (S, X, I+4);
640                     X = NewCodeEntry (OP65_STA, L[4]->AM, L[4]->Arg, 0, L[4]->LI);
641                     CS_InsertEntry (S, X, I+5);
642                     CS_DelEntries (S, I+6, 4);
643                     break;
644
645             }
646
647             /* Remember, we had changes */
648             ++Changes;
649
650         }
651
652         /* Next entry */
653         ++I;
654
655     }
656
657     /* Return the number of changes made */
658     return Changes;
659 }
660
661
662
663 unsigned OptShift6 (CodeSeg* S)
664 /* Inline the shift subroutines. */
665 {
666     unsigned Changes = 0;
667
668     /* Walk over the entries */
669     unsigned I = 0;
670     while (I < CS_GetEntryCount (S)) {
671
672         unsigned   Shift;
673         unsigned   Count;
674         CodeEntry* X;
675         unsigned   IP;
676
677         /* Get next entry */
678         CodeEntry* E = CS_GetEntry (S, I);
679
680         /* Check for a call to one of the shift routine */
681         if (E->OPC == OP65_JSR                          &&
682             (Shift = GetShift (E->Arg)) != SHIFT_NONE   &&
683             SHIFT_DIR (Shift) == SHIFT_DIR_LEFT         &&
684             (Count = SHIFT_COUNT (Shift)) > 0) {
685
686             /* Code is:
687              *
688              *      stx     tmp1
689              *      asl     a
690              *      rol     tmp1
691              *      (repeat ShiftCount-1 times)
692              *      ldx     tmp1
693              *
694              * which makes 4 + 3 * ShiftCount bytes, compared to the original
695              * 3 bytes for the subroutine call. However, in most cases, the
696              * final load of the X register gets merged with some other insn
697              * and replaces a txa, so for a shift count of 1, we get a factor
698              * of 200, which matches nicely the CodeSizeFactor enabled with -Oi
699              */
700             if (Count > 1 || S->CodeSizeFactor > 200) {
701                 unsigned Size = 4 + 3 * Count;
702                 if ((Size * 100 / 3) > S->CodeSizeFactor) {
703                     /* Not acceptable */
704                     goto NextEntry;
705                 }
706             }
707
708             /* Inline the code. Insertion point is behind the subroutine call */
709             IP = (I + 1);
710
711             /* stx tmp1 */
712             X = NewCodeEntry (OP65_STX, AM65_ZP, "tmp1", 0, E->LI);
713             CS_InsertEntry (S, X, IP++);
714
715             while (Count--) {
716                 /* asl a */
717                 X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI);
718                 CS_InsertEntry (S, X, IP++);
719
720                 /* rol tmp1 */
721                 X = NewCodeEntry (OP65_ROL, AM65_ZP, "tmp1", 0, E->LI);
722                 CS_InsertEntry (S, X, IP++);
723             }
724
725             /* ldx tmp1 */
726             X = NewCodeEntry (OP65_LDX, AM65_ZP, "tmp1", 0, E->LI);
727             CS_InsertEntry (S, X, IP++);
728
729             /* Remove the subroutine call */
730             CS_DelEntry (S, I);
731
732             /* Remember, we had changes */
733             ++Changes;
734         }
735
736 NextEntry:
737         /* Next entry */
738         ++I;
739
740     }
741
742     /* Return the number of changes made */
743     return Changes;
744 }
745
746
747