]> git.sur5r.net Git - cc65/blob - src/cc65/coptshift.c
a4b99307372874f0cf7b83a7823f9ae54a78cc9c
[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     unsigned Changes = 0;
203     unsigned I;
204
205     /* Walk over the entries */
206     I = 0;
207     while (I < CS_GetEntryCount (S)) {
208
209         unsigned   Shift;
210         CodeEntry* N;
211         CodeEntry* X;
212         CodeLabel* L;
213
214         /* Get next entry */
215         CodeEntry* E = CS_GetEntry (S, I);
216
217         /* Check for the sequence */
218         if (E->OPC == OP65_JSR                          &&
219             (Shift = GetShift (E->Arg)) != SHIFT_NONE   &&
220             SHIFT_DIR (Shift) == SHIFT_DIR_LEFT) {
221
222
223             unsigned Count = SHIFT_COUNT (Shift);
224             if (!RegXUsed (S, I+1)) {
225
226                 if (Count == SHIFT_COUNT_Y) {
227
228                     CodeLabel* L;
229
230                     if (S->CodeSizeFactor < 200) {
231                         goto NextEntry;
232                     }
233
234                     /* Change into
235                     **
236                     ** L1:  asl     a
237                     **      dey
238                     **      bpl     L1
239                     **      ror     a
240                     */
241
242                     /* asl a */
243                     X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI);
244                     CS_InsertEntry (S, X, I+1);
245                     L = CS_GenLabel (S, X);
246
247                     /* dey */
248                     X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, E->LI);
249                     CS_InsertEntry (S, X, I+2);
250
251                     /* bpl L1 */
252                     X = NewCodeEntry (OP65_BPL, AM65_BRA, L->Name, L, E->LI);
253                     CS_InsertEntry (S, X, I+3);
254
255                     /* ror a */
256                     X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, E->LI);
257                     CS_InsertEntry (S, X, I+4);
258
259                 } else {
260                     /* Insert shift insns */
261                     while (Count--) {
262                         X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI);
263                         CS_InsertEntry (S, X, I+1);
264                     }
265                 }
266
267             } else if (E->RI->In.RegX == 0              &&
268                        Count == 1                       &&
269                        (N = CS_GetNextEntry (S, I)) != 0) {
270
271                 /* asl a */
272                 X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI);
273                 CS_InsertEntry (S, X, I+1);
274
275                 /* bcc L1 */
276                 L = CS_GenLabel (S, N);
277                 X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, E->LI);
278                 CS_InsertEntry (S, X, I+2);
279
280                 /* inx */
281                 X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, E->LI);
282                 CS_InsertEntry (S, X, I+3);
283
284             } else {
285
286                 /* We won't handle this one */
287                 goto NextEntry;
288
289             }
290
291             /* Delete the call to shlax */
292             CS_DelEntry (S, I);
293
294             /* Remember, we had changes */
295             ++Changes;
296         }
297
298 NextEntry:
299         /* Next entry */
300         ++I;
301
302     }
303
304     /* Return the number of changes made */
305     return Changes;
306 }
307
308
309
310 unsigned OptShift2(CodeSeg* S)
311 /* A call to the asrax1 routines may get replaced by something simpler, if
312 ** X is not used later:
313 **
314 **      cmp     #$80
315 **      ror     a
316 */
317 {
318     unsigned Changes = 0;
319     unsigned I;
320
321     /* Walk over the entries */
322     I = 0;
323     while (I < CS_GetEntryCount (S)) {
324
325         unsigned Shift;
326         unsigned Count;
327
328         /* Get next entry */
329         CodeEntry* E = CS_GetEntry (S, I);
330
331         /* Check for the sequence */
332         if (E->OPC == OP65_JSR                          &&
333             (Shift = GetShift (E->Arg)) != SHIFT_NONE   &&
334             SHIFT_TYPE (Shift) == SHIFT_TYPE_ASR        &&
335             (Count = SHIFT_COUNT (Shift)) > 0           &&
336             Count * 100 <= S->CodeSizeFactor    &&
337             !RegXUsed (S, I+1)) {
338
339             CodeEntry* X;
340             unsigned J = I+1;
341
342             /* Generate the replacement sequence */
343             while (Count--) {
344                 /* cmp #$80 */
345                 X = NewCodeEntry (OP65_CMP, AM65_IMM, "$80", 0, E->LI);
346                 CS_InsertEntry (S, X, J++);
347
348                 /* ror a */
349                 X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, E->LI);
350                 CS_InsertEntry (S, X, J++);
351             }
352
353             /* Delete the call to asrax */
354             CS_DelEntry (S, I);
355
356             /* Remember, we had changes */
357             ++Changes;
358         }
359
360         /* Next entry */
361         ++I;
362
363     }
364
365     /* Return the number of changes made */
366     return Changes;
367 }
368
369
370
371 unsigned OptShift3 (CodeSeg* S)
372 /* The sequence
373 **
374 **      bcc     L
375 **      inx
376 ** L:   jsr     shrax1
377 **
378 ** may get replaced by
379 **
380 **      ror     a
381 **
382 ** if X is zero on entry. For shift counts > 1, more
383 **
384 **      shr     a
385 **
386 ** must be added.
387 */
388 {
389     unsigned Changes = 0;
390     unsigned I;
391
392     /* Walk over the entries */
393     I = 0;
394     while (I < CS_GetEntryCount (S)) {
395
396         unsigned   Shift;
397         unsigned   Count;
398         CodeEntry* L[3];
399
400         /* Get next entry */
401         L[0] = CS_GetEntry (S, I);
402
403         /* Check for the sequence */
404         if ((L[0]->OPC == OP65_BCC || L[0]->OPC == OP65_JCC)    &&
405             L[0]->JumpTo != 0                                   &&
406             L[0]->RI->In.RegX == 0                              &&
407             CS_GetEntries (S, L+1, I+1, 2)                      &&
408             L[1]->OPC == OP65_INX                               &&
409             L[0]->JumpTo->Owner == L[2]                         &&
410             !CS_RangeHasLabel (S, I, 2)                         &&
411             L[2]->OPC == OP65_JSR                               &&
412             (Shift = GetShift (L[2]->Arg)) != SHIFT_NONE        &&
413             SHIFT_DIR (Shift) == SHIFT_DIR_RIGHT                &&
414             (Count = SHIFT_COUNT (Shift)) > 0) {
415                                                 
416             /* Add the replacement insn instead */
417             CodeEntry* X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, L[2]->LI);
418             CS_InsertEntry (S, X, I+3);
419             while (--Count) {
420                 X = NewCodeEntry (OP65_LSR, AM65_ACC, "a", 0, L[2]->LI);
421                 CS_InsertEntry (S, X, I+4);
422             }
423
424             /* Remove the bcs/dex/jsr */
425             CS_DelEntries (S, I, 3);
426
427             /* Remember, we had changes */
428             ++Changes;
429
430         }
431
432         /* Next entry */
433         ++I;
434
435     }
436
437     /* Return the number of changes made */
438     return Changes;
439 }
440
441
442
443 unsigned OptShift4 (CodeSeg* S)
444 /* Calls to the asraxN or shraxN routines may get replaced by one or more lsr
445 ** insns if the value of X is zero.
446 */
447 {
448     unsigned Changes = 0;
449     unsigned I;
450
451     /* Walk over the entries */
452     I = 0;
453     while (I < CS_GetEntryCount (S)) {
454
455         unsigned Shift;
456         unsigned Count;
457
458         /* Get next entry */
459         CodeEntry* E = CS_GetEntry (S, I);
460
461         /* Check for the sequence */
462         if (E->OPC == OP65_JSR                          &&
463             (Shift = GetShift (E->Arg)) != SHIFT_NONE   &&
464             SHIFT_DIR (Shift) == SHIFT_DIR_RIGHT        &&
465             E->RI->In.RegX == 0) {
466
467             CodeEntry* X;
468
469             /* Shift count may be in Y */
470             Count = SHIFT_COUNT (Shift);
471             if (Count == SHIFT_COUNT_Y) {
472
473                 CodeLabel* L;
474
475                 if (S->CodeSizeFactor < 200) {
476                     /* Not acceptable */
477                     goto NextEntry;
478                 }
479
480                 /* Generate:
481                 **
482                 ** L1: lsr     a
483                 **     dey
484                 **     bpl     L1
485                 **     rol     a
486                 **
487                 ** A negative shift count or one that is greater or equal than
488                 ** the bit width of the left operand (which is promoted to
489                 ** integer before the operation) causes undefined behaviour, so
490                 ** above transformation is safe.
491                 */
492
493                 /* lsr a */
494                 X = NewCodeEntry (OP65_LSR, AM65_ACC, "a", 0, E->LI);
495                 CS_InsertEntry (S, X, I+1);
496                 L = CS_GenLabel (S, X);
497
498                 /* dey */
499                 X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, E->LI);
500                 CS_InsertEntry (S, X, I+2);
501
502                 /* bpl L1 */
503                 X = NewCodeEntry (OP65_BPL, AM65_BRA, L->Name, L, E->LI);
504                 CS_InsertEntry (S, X, I+3);
505
506                 /* rol a */
507                 X = NewCodeEntry (OP65_ROL, AM65_ACC, "a", 0, E->LI);
508                 CS_InsertEntry (S, X, I+4);
509
510             } else {
511                 /* Insert shift insns */
512                 while (Count--) {
513                     X = NewCodeEntry (OP65_LSR, AM65_ACC, "a", 0, E->LI);
514                     CS_InsertEntry (S, X, I+1);
515                 }
516
517             }
518
519             /* Delete the call to shrax */
520             CS_DelEntry (S, I);
521
522             /* Remember, we had changes */
523             ++Changes;
524
525         }
526
527 NextEntry:
528         /* Next entry */
529         ++I;
530
531     }
532
533     /* Return the number of changes made */
534     return Changes;
535 }
536
537
538
539 unsigned OptShift5 (CodeSeg* S)
540 /* Search for the sequence
541 **
542 **      lda     xxx
543 **      ldx     yyy
544 **      jsr     aslax1/asrax1/shlax1/shrax1
545 **      sta     aaa
546 **      stx     bbb
547 **
548 ** and replace it by
549 **
550 **      lda     xxx
551 **      asl     a
552 **      sta     aaa
553 **      lda     yyy
554 **      rol     a
555 **      sta     bbb
556 **
557 ** or similar, provided that a/x is not used later
558 */
559 {
560     unsigned Changes = 0;
561
562     /* Walk over the entries */
563     unsigned I = 0;
564     while (I < CS_GetEntryCount (S)) {
565
566         unsigned ShiftType;
567         CodeEntry* L[5];
568
569         /* Get next entry */
570         L[0] = CS_GetEntry (S, I);
571
572         /* Check for the sequence */
573         if (L[0]->OPC == OP65_LDA                               &&
574             (L[0]->AM == AM65_ABS || L[0]->AM == AM65_ZP)       &&
575             CS_GetEntries (S, L+1, I+1, 4)                      &&
576             !CS_RangeHasLabel (S, I+1, 4)                       &&
577             L[1]->OPC == OP65_LDX                               &&
578             (L[1]->AM == AM65_ABS || L[1]->AM == AM65_ZP)       &&
579             L[2]->OPC == OP65_JSR                               &&
580             (ShiftType = GetShift (L[2]->Arg)) != SHIFT_NONE    &&
581             SHIFT_COUNT(ShiftType) == 1                         &&
582             L[3]->OPC == OP65_STA                               &&
583             (L[3]->AM == AM65_ABS || L[3]->AM == AM65_ZP)       &&
584             L[4]->OPC == OP65_STX                               &&
585             (L[4]->AM == AM65_ABS || L[4]->AM == AM65_ZP)       &&
586             !RegAXUsed (S, I+5)) {
587
588             CodeEntry* X;
589
590             /* Handle the four shift types differently */
591             switch (ShiftType) {
592
593                 case SHIFT_ASR_1:
594                     X = NewCodeEntry (OP65_LDA, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
595                     CS_InsertEntry (S, X, I+5);
596                     X = NewCodeEntry (OP65_CMP, AM65_IMM, "$80", 0, L[2]->LI);
597                     CS_InsertEntry (S, X, I+6);
598                     X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, L[2]->LI);
599                     CS_InsertEntry (S, X, I+7);
600                     X = NewCodeEntry (OP65_STA, L[4]->AM, L[4]->Arg, 0, L[4]->LI);
601                     CS_InsertEntry (S, X, I+8);
602                     X = NewCodeEntry (OP65_LDA, L[0]->AM, L[0]->Arg, 0, L[0]->LI);
603                     CS_InsertEntry (S, X, I+9);
604                     X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, L[2]->LI);
605                     CS_InsertEntry (S, X, I+10);
606                     X = NewCodeEntry (OP65_STA, L[3]->AM, L[3]->Arg, 0, L[3]->LI);
607                     CS_InsertEntry (S, X, I+11);
608                     CS_DelEntries (S, I, 5);
609                     break;
610
611                 case SHIFT_LSR_1:
612                     X = NewCodeEntry (OP65_LDA, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
613                     CS_InsertEntry (S, X, I+5);
614                     X = NewCodeEntry (OP65_LSR, AM65_ACC, "a", 0, L[2]->LI);
615                     CS_InsertEntry (S, X, I+6);
616                     X = NewCodeEntry (OP65_STA, L[4]->AM, L[4]->Arg, 0, L[4]->LI);
617                     CS_InsertEntry (S, X, I+7);
618                     X = NewCodeEntry (OP65_LDA, L[0]->AM, L[0]->Arg, 0, L[0]->LI);
619                     CS_InsertEntry (S, X, I+8);
620                     X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, L[2]->LI);
621                     CS_InsertEntry (S, X, I+9);
622                     X = NewCodeEntry (OP65_STA, L[3]->AM, L[3]->Arg, 0, L[3]->LI);
623                     CS_InsertEntry (S, X, I+10);
624                     CS_DelEntries (S, I, 5);
625                     break;
626
627                 case SHIFT_LSL_1:
628                 case SHIFT_ASL_1:
629                     /* These two are identical */
630                     X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, L[2]->LI);
631                     CS_InsertEntry (S, X, I+1);
632                     X = NewCodeEntry (OP65_STA, L[3]->AM, L[3]->Arg, 0, L[3]->LI);
633                     CS_InsertEntry (S, X, I+2);
634                     X = NewCodeEntry (OP65_LDA, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
635                     CS_InsertEntry (S, X, I+3);
636                     X = NewCodeEntry (OP65_ROL, AM65_ACC, "a", 0, L[2]->LI);
637                     CS_InsertEntry (S, X, I+4);
638                     X = NewCodeEntry (OP65_STA, L[4]->AM, L[4]->Arg, 0, L[4]->LI);
639                     CS_InsertEntry (S, X, I+5);
640                     CS_DelEntries (S, I+6, 4);
641                     break;
642
643             }
644
645             /* Remember, we had changes */
646             ++Changes;
647
648         }
649
650         /* Next entry */
651         ++I;
652
653     }
654
655     /* Return the number of changes made */
656     return Changes;
657 }
658
659
660
661 unsigned OptShift6 (CodeSeg* S)
662 /* Inline the shift subroutines. */
663 {
664     unsigned Changes = 0;
665
666     /* Walk over the entries */
667     unsigned I = 0;
668     while (I < CS_GetEntryCount (S)) {
669
670         unsigned   Shift;
671         unsigned   Count;
672         CodeEntry* X;
673         unsigned   IP;
674
675         /* Get next entry */
676         CodeEntry* E = CS_GetEntry (S, I);
677
678         /* Check for a call to one of the shift routine */
679         if (E->OPC == OP65_JSR                          &&
680             (Shift = GetShift (E->Arg)) != SHIFT_NONE   &&
681             SHIFT_DIR (Shift) == SHIFT_DIR_LEFT         &&
682             (Count = SHIFT_COUNT (Shift)) > 0) {
683
684             /* Code is:
685             **
686             **      stx     tmp1
687             **      asl     a
688             **      rol     tmp1
689             **      (repeat ShiftCount-1 times)
690             **      ldx     tmp1
691             **
692             ** which makes 4 + 3 * ShiftCount bytes, compared to the original
693             ** 3 bytes for the subroutine call. However, in most cases, the
694             ** final load of the X register gets merged with some other insn
695             ** and replaces a txa, so for a shift count of 1, we get a factor
696             ** of 200, which matches nicely the CodeSizeFactor enabled with -Oi
697             */
698             if (Count > 1 || S->CodeSizeFactor > 200) {
699                 unsigned Size = 4 + 3 * Count;
700                 if ((Size * 100 / 3) > S->CodeSizeFactor) {
701                     /* Not acceptable */
702                     goto NextEntry;
703                 }
704             }
705
706             /* Inline the code. Insertion point is behind the subroutine call */
707             IP = (I + 1);
708
709             /* stx tmp1 */
710             X = NewCodeEntry (OP65_STX, AM65_ZP, "tmp1", 0, E->LI);
711             CS_InsertEntry (S, X, IP++);
712
713             while (Count--) {
714                 /* asl a */
715                 X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI);
716                 CS_InsertEntry (S, X, IP++);
717
718                 /* rol tmp1 */
719                 X = NewCodeEntry (OP65_ROL, AM65_ZP, "tmp1", 0, E->LI);
720                 CS_InsertEntry (S, X, IP++);
721             }
722
723             /* ldx tmp1 */
724             X = NewCodeEntry (OP65_LDX, AM65_ZP, "tmp1", 0, E->LI);
725             CS_InsertEntry (S, X, IP++);
726
727             /* Remove the subroutine call */
728             CS_DelEntry (S, I);
729
730             /* Remember, we had changes */
731             ++Changes;
732         }
733
734 NextEntry:
735         /* Next entry */
736         ++I;
737
738     }
739
740     /* Return the number of changes made */
741     return Changes;
742 }