]> git.sur5r.net Git - cc65/blob - src/cc65/coptstop.c
Added several constraints to the optimizer functions to avoid breaking code.
[cc65] / src / cc65 / coptstop.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 coptstop.c                                */
4 /*                                                                           */
5 /*           Optimize operations that take operands via the stack            */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2001-2009 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 #include <stdlib.h>
37
38 /* common */
39 #include "chartype.h"
40
41 /* cc65 */
42 #include "codeent.h"
43 #include "codeinfo.h"
44 #include "coptstop.h"
45
46
47
48 /*****************************************************************************/
49 /*                                   Data                                    */
50 /*****************************************************************************/
51
52
53
54 /* Flags for the functions */
55 typedef enum {
56     STOP_NONE       = 0x00,     /* Nothing special */
57     STOP_A_KNOWN    = 0x01,     /* Call only if A is known */
58     STOP_X_ZERO     = 0x02      /* Call only if X is zero */
59 } STOP_FLAGS;
60
61 /* Structure forward decl */
62 typedef struct StackOpData StackOpData;
63
64 /* Structure that describes an optimizer subfunction for a specific op */
65 typedef unsigned (*OptFunc) (StackOpData* D);
66 typedef struct OptFuncDesc OptFuncDesc;
67 struct OptFuncDesc {
68     const char*         Name;           /* Name of the replaced runtime function */
69     OptFunc             Func;           /* Function pointer */
70     unsigned            UnusedRegs;     /* Regs that must not be used later */
71     STOP_FLAGS          Flags;          /* Flags */
72 };
73
74 /* LoadData flags set by DirectOp */
75 #define LD_DIRECT       0x01            /* Direct op may be used */
76 #define LD_RELOAD_Y     0x02            /* Reload index register Y */
77 #define LD_REMOVE       0x04            /* Load may be removed */
78
79 /* Structure that tells us how to load the lhs values */
80 typedef struct LoadData LoadData;
81 struct LoadData {
82     unsigned char       Flags;          /* Tells us how to load */
83     unsigned char       Offs;           /* Stack offset if data is on stack */
84 };
85
86 /* Structure that holds the needed data */
87 struct StackOpData {
88     CodeSeg*            Code;           /* Pointer to code segment */
89     unsigned            Flags;          /* Flags to remember things */
90
91     /* Pointer to optimizer subfunction description */
92     const OptFuncDesc*  OptFunc;
93
94     /* ZP register usage inside the sequence */
95     unsigned            UsedRegs;
96
97     /* Several indices of insns in the code segment */
98     int                 LoadAIndex;     /* Index of load insns, -1 = invalid */
99     int                 LoadXIndex;
100     int                 LoadYIndex;
101     int                 PushIndex;      /* Index of call to pushax in codeseg */
102     int                 OpIndex;        /* Index of actual operation */
103
104     /* Pointers to insns in the code segment */
105     CodeEntry*          LoadAEntry;     /* Entry that loads A or NULL */
106     CodeEntry*          LoadXEntry;     /* Entry that loads X or NULL */
107     CodeEntry*          PrevEntry;      /* Entry before the call to pushax */
108     CodeEntry*          PushEntry;      /* Pointer to entry with call to pushax */
109     CodeEntry*          OpEntry;        /* Pointer to entry with op */
110     CodeEntry*          NextEntry;      /* Entry after the op */
111
112     /* Stack offsets if the lhs is loaded from stack */
113     LoadData            AData;
114     LoadData            XData;
115
116
117     const char*         ZPLo;           /* Lo byte of zero page loc to use */
118     const char*         ZPHi;           /* Hi byte of zero page loc to use */
119     unsigned            IP;             /* Insertion point used by some routines */
120 };
121
122
123
124 /*****************************************************************************/
125 /*                                  Helpers                                  */
126 /*****************************************************************************/
127
128
129
130 static void AdjustStackOffset (StackOpData* D, unsigned Offs)
131 /* Adjust the offset for all stack accesses in the range PushIndex to OpIndex.
132  * OpIndex is adjusted according to the insertions.
133  */
134 {
135     /* Walk over all entries */
136     int I = D->PushIndex + 1;
137     while (I < D->OpIndex) {
138
139         CodeEntry* E = CS_GetEntry (D->Code, I);
140
141         int NeedCorrection = 0;
142         if ((E->Use & REG_SP) != 0) {
143
144             /* Check for some things that should not happen */
145             CHECK (E->AM == AM65_ZP_INDY || E->RI->In.RegY >= (short) Offs);
146             CHECK (strcmp (E->Arg, "sp") == 0);
147
148             /* We need to correct this one */
149             NeedCorrection = 1;
150
151         } else if (CE_IsCallTo (E, "ldaxysp")) {
152
153             /* We need to correct this one */
154             NeedCorrection = 1;
155
156         }
157
158         if (NeedCorrection) {
159
160             /* Get the code entry before this one. If it's a LDY, adjust the
161              * value.
162              */
163             CodeEntry* P = CS_GetPrevEntry (D->Code, I);
164             if (P && P->OPC == OP65_LDY && CE_IsConstImm (P)) {
165
166                 /* The Y load is just before the stack access, adjust it */
167                 CE_SetNumArg (P, P->Num - Offs);
168
169             } else {
170
171                 /* Insert a new load instruction before the stack access */
172                 const char* Arg = MakeHexArg (E->RI->In.RegY - Offs);
173                 CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
174                 CS_InsertEntry (D->Code, X, I++);
175
176                 /* One more inserted entries */
177                 ++D->OpIndex;
178
179             }
180
181             /* If we need the value of Y later, be sure to reload it */
182             if (RegYUsed (D->Code, I+1)) {
183                 const char* Arg = MakeHexArg (E->RI->In.RegY);
184                 CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
185                 CS_InsertEntry (D->Code, X, I+1);
186
187                 /* One more inserted entries */
188                 ++D->OpIndex;
189
190                 /* Skip this instruction in the next round */
191                 ++I;
192             }
193         }
194
195         /* Next entry */
196         ++I;
197     }
198 }
199
200
201
202 static void InsertEntry (StackOpData* D, CodeEntry* E, int Index)
203 /* Insert a new entry. Depending on Index, D->PushIndex and D->OpIndex will
204  * be adjusted by this function.
205  */
206 {
207     /* Insert the entry into the code segment */
208     CS_InsertEntry (D->Code, E, Index);
209
210     /* Adjust the indices if necessary */
211     if (D->PushEntry && Index <= D->PushIndex) {
212         ++D->PushIndex;
213     }
214     if (D->OpEntry && Index <= D->OpIndex) {
215         ++D->OpIndex;
216     }
217 }
218
219
220
221 static void DelEntry (StackOpData* D, int Index)
222 /* Delete an entry. Depending on Index, D->PushIndex and D->OpIndex will be
223  * adjusted by this function, and PushEntry/OpEntry may get invalidated.
224  */
225 {
226     /* Delete the entry from the code segment */
227     CS_DelEntry (D->Code, Index);
228
229     /* Adjust the indices if necessary */
230     if (Index < D->PushIndex) {
231         --D->PushIndex;
232     } else if (Index == D->PushIndex) {
233         D->PushEntry = 0;
234     }
235     if (Index < D->OpIndex) {
236         --D->OpIndex;
237     } else if (Index == D->OpIndex) {
238         D->OpEntry = 0;
239     }
240 }
241
242
243
244 static void CheckOneDirectOp (CodeEntry* E, LoadData* L, unsigned char Offs)
245 /* Check if the given entry is a lda instruction with an addressing mode
246  * that allows us to replace it by another operation (like ora). If so, we may
247  * use this location for the or and must not save the value in the zero
248  * page location.
249  */
250 {
251     /* Check the load entry */
252     if (E) {
253         /* Must check the call first since addressing mode is ABS, so second
254          * "if" will catch otherwise.
255          */
256         if (CE_IsCallTo (E, "ldaxysp")) {
257             /* Same as single loads from stack. Since we must distinguish
258              * between A and X here, the necessary offset is passed to the
259              * function as a parameter.
260              */
261             L->Offs = (unsigned char) E->RI->In.RegY - Offs;
262             L->Flags |= (LD_DIRECT | LD_RELOAD_Y);
263         } else if (E->AM == AM65_IMM || E->AM == AM65_ZP || E->AM == AM65_ABS) {
264             /* These insns are all ok and replaceable */
265             L->Flags |= LD_DIRECT;
266         } else if (E->AM == AM65_ZP_INDY &&
267                    RegValIsKnown (E->RI->In.RegY) &&
268                    strcmp (E->Arg, "sp") == 0) {
269             /* A load from the stack with known offset is also ok, but in this
270              * case we must reload the index register later. Please note that
271              * a load indirect via other zero page locations is not ok, since
272              * these locations may change between the push and the actual
273              * operation.
274              */
275             L->Offs  = (unsigned char) E->RI->In.RegY;
276             L->Flags |= (LD_DIRECT | LD_RELOAD_Y);
277         }
278     }
279 }
280
281
282
283 static void CheckDirectOp (StackOpData* D)
284 /* Check if the given entry is a lda instruction with an addressing mode
285  * that allows us to replace it by another operation (like ora). If so, we may
286  * use this location for the or and must not save the value in the zero
287  * page location.
288  */
289 {
290     /* Check flags for A and X load instructions */
291     CheckOneDirectOp (D->LoadAEntry, &D->AData, 1);
292     CheckOneDirectOp (D->LoadXEntry, &D->XData, 0);
293 }
294
295
296
297 static void ReplacePushByStore (StackOpData* D)
298 /* Replace the call to the push subroutine by a store into the zero page
299  * location (actually, the push is not replaced, because we need it for
300  * later, but the name is still ok since the push will get removed at the
301  * end of each routine).
302  */
303 {
304     CodeEntry* X;
305
306     /* Store the value into the zeropage instead of pushing it. Check high
307      * byte first so that the store is later in A/X order.
308      */
309     if ((D->XData.Flags & LD_DIRECT) == 0) {
310         X = NewCodeEntry (OP65_STX, AM65_ZP, D->ZPHi, 0, D->PushEntry->LI);
311         InsertEntry (D, X, D->PushIndex+1);
312     }
313     if ((D->AData.Flags & LD_DIRECT) == 0) {
314         X = NewCodeEntry (OP65_STA, AM65_ZP, D->ZPLo, 0, D->PushEntry->LI);
315         InsertEntry (D, X, D->PushIndex+1);
316     }
317 }
318
319
320
321 static void AddOpLow (StackOpData* D, opc_t OPC)
322 /* Add an op for the low byte of an operator. This function honours the
323  * OP_DIRECT and OP_RELOAD_Y flags and generates the necessary instructions.
324  * All code is inserted at the current insertion point.
325  */
326 {
327     CodeEntry* X;
328
329     if ((D->AData.Flags & LD_DIRECT) != 0) {
330         /* Op with a variable location. If the location is on the stack, we
331          * need to reload the Y register.
332          */
333         if ((D->AData.Flags & LD_RELOAD_Y) == 0) {
334
335             /* opc ... */
336             CodeEntry* LoadA = D->LoadAEntry;
337             X = NewCodeEntry (OPC, LoadA->AM, LoadA->Arg, 0, D->OpEntry->LI);
338             InsertEntry (D, X, D->IP++);
339
340         } else {
341
342             /* ldy #offs */
343             const char* Arg = MakeHexArg (D->AData.Offs);
344             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
345             InsertEntry (D, X, D->IP++);
346
347             /* opc (sp),y */
348             X = NewCodeEntry (OPC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI);
349             InsertEntry (D, X, D->IP++);
350
351         }
352
353         /* In both cases, we can remove the load */
354         D->AData.Flags |= LD_REMOVE;
355
356     } else {
357
358         /* Op with temp storage */
359         X = NewCodeEntry (OPC, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI);
360         InsertEntry (D, X, D->IP++);
361
362     }
363 }
364
365
366
367 static void AddOpHigh (StackOpData* D, opc_t OPC)
368 /* Add an op for the high byte of an operator. Special cases (constant values
369  * or similar) have to be checked separately, the function covers only the
370  * generic case. Code is inserted at the insertion point.
371  */
372 {
373     CodeEntry* X;
374
375     /* pha */
376     X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, D->OpEntry->LI);
377     InsertEntry (D, X, D->IP++);
378
379     /* txa */
380     X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, D->OpEntry->LI);
381     InsertEntry (D, X, D->IP++);
382
383     if ((D->XData.Flags & LD_DIRECT) != 0) {
384
385         if ((D->XData.Flags & LD_RELOAD_Y) == 0) {
386
387             /* opc xxx */
388             CodeEntry* LoadX = D->LoadXEntry;
389             X = NewCodeEntry (OPC, LoadX->AM, LoadX->Arg, 0, D->OpEntry->LI);
390             InsertEntry (D, X, D->IP++);
391
392         } else {
393
394             /* ldy #const */
395             const char* Arg = MakeHexArg (D->XData.Offs);
396             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
397             InsertEntry (D, X, D->IP++);
398
399             /* opc (sp),y */
400             X = NewCodeEntry (OPC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI);
401             InsertEntry (D, X, D->IP++);
402         }
403
404         /* In both cases, we can remove the load */
405         D->XData.Flags |= LD_REMOVE;
406
407     } else {
408         /* opc zphi */
409         X = NewCodeEntry (OPC, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI);
410         InsertEntry (D, X, D->IP++);
411     }
412
413     /* tax */
414     X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, D->OpEntry->LI);
415     InsertEntry (D, X, D->IP++);
416
417     /* pla */
418     X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, D->OpEntry->LI);
419     InsertEntry (D, X, D->IP++);
420 }
421
422
423
424 static void RemoveRemainders (StackOpData* D)
425 /* Remove the code that is unnecessary after translation of the sequence */
426 {
427     /* Remove the push and the operator routine */
428     DelEntry (D, D->OpIndex);
429     DelEntry (D, D->PushIndex);
430
431     /* Remove the register loads before the push. Beware: There may only be
432      * one!
433      */
434     if (D->LoadAIndex >= 0 && D->LoadAIndex == D->LoadXIndex) {
435         /* Common load routine */
436         if ((D->AData.Flags & D->XData.Flags) & LD_REMOVE) {
437             /* Both say: remove */
438             DelEntry (D, D->LoadAIndex);
439         }
440     } else if (D->LoadAIndex >= 0 && (D->AData.Flags & LD_REMOVE)) {
441         DelEntry (D, D->LoadAIndex);
442     } else if (D->LoadXIndex >= 0 && (D->XData.Flags & LD_REMOVE)) {
443         DelEntry (D, D->LoadXIndex);
444     }
445 }
446
447
448
449 static int IsRegVar (StackOpData* D)
450 /* If the value pushed is that of a zeropage variable, replace ZPLo and ZPHi
451  * in the given StackOpData struct by the variable and return true. Otherwise
452  * leave D untouched and return false.
453  */
454 {
455     CodeEntry*  LoadA = D->LoadAEntry;
456     CodeEntry*  LoadX = D->LoadXEntry;
457     unsigned    Len;
458
459     /* Must have both load insns */
460     if (LoadA == 0 || LoadX == 0) {
461         return 0;
462     }
463
464     /* Must be loads from zp */
465     if (LoadA->AM != AM65_ZP || LoadX->AM != AM65_ZP) {
466         return 0;
467     }
468
469     /* Must be the same zp loc with high byte in X */
470     Len = strlen (LoadA->Arg);
471     if (strncmp (LoadA->Arg, LoadX->Arg, Len) != 0      ||
472         strcmp (LoadX->Arg + Len, "+1") != 0) {
473         return 0;
474     }
475
476     /* Use the zero page location directly */
477     D->ZPLo = LoadA->Arg;
478     D->ZPHi = LoadX->Arg;
479     return 1;
480 }
481
482
483
484 /*****************************************************************************/
485 /*                       Actual optimization functions                       */
486 /*****************************************************************************/
487
488
489
490 static unsigned Opt___bzero (StackOpData* D)
491 /* Optimize the __bzero sequence if possible */
492 {
493     CodeEntry*  X;
494     const char* Arg;
495     CodeLabel*  L;
496
497     /* Check if we're using a register variable */
498     if (!IsRegVar (D)) {
499         /* Store the value into the zeropage instead of pushing it */
500         ReplacePushByStore (D);
501     }
502
503     /* If the return value of __bzero is used, we have to add code to reload
504      * a/x from the pointer variable.
505      */
506     if (RegAXUsed (D->Code, D->OpIndex+1)) {
507         X = NewCodeEntry (OP65_LDA, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI);
508         InsertEntry (D, X, D->OpIndex+1);
509         X = NewCodeEntry (OP65_LDX, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI);
510         InsertEntry (D, X, D->OpIndex+2);
511     }
512
513     /* X is always zero, A contains the size of the data area to zero.
514      * Note: A may be zero, in which case the operation is null op.
515      */
516     if (D->OpEntry->RI->In.RegA != 0) {
517
518         /* lda #$00 */
519         X = NewCodeEntry (OP65_LDA, AM65_IMM, "$00", 0, D->OpEntry->LI);
520         InsertEntry (D, X, D->OpIndex+1);
521
522         /* The value of A is known */
523         if (D->OpEntry->RI->In.RegA <= 0x81) {
524
525             /* Loop using the sign bit */
526
527             /* ldy #count-1 */
528             Arg = MakeHexArg (D->OpEntry->RI->In.RegA - 1);
529             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
530             InsertEntry (D, X, D->OpIndex+2);
531
532             /* L: sta (zp),y */
533             X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, D->ZPLo, 0, D->OpEntry->LI);
534             InsertEntry (D, X, D->OpIndex+3);
535             L = CS_GenLabel (D->Code, X);
536
537             /* dey */
538             X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, D->OpEntry->LI);
539             InsertEntry (D, X, D->OpIndex+4);
540
541             /* bpl L */
542             X = NewCodeEntry (OP65_BPL, AM65_BRA, L->Name, L, D->OpEntry->LI);
543             InsertEntry (D, X, D->OpIndex+5);
544
545         } else {
546
547             /* Loop using an explicit compare */
548
549             /* ldy #$00 */
550             X = NewCodeEntry (OP65_LDY, AM65_IMM, "$00", 0, D->OpEntry->LI);
551             InsertEntry (D, X, D->OpIndex+2);
552
553             /* L: sta (zp),y */
554             X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, D->ZPLo, 0, D->OpEntry->LI);
555             InsertEntry (D, X, D->OpIndex+3);
556             L = CS_GenLabel (D->Code, X);
557
558             /* iny */
559             X = NewCodeEntry (OP65_INY, AM65_IMP, 0, 0, D->OpEntry->LI);
560             InsertEntry (D, X, D->OpIndex+4);
561
562             /* cpy #count */
563             Arg = MakeHexArg (D->OpEntry->RI->In.RegA);
564             X = NewCodeEntry (OP65_CPY, AM65_IMM, Arg, 0, D->OpEntry->LI);
565             InsertEntry (D, X, D->OpIndex+5);
566
567             /* bne L */
568             X = NewCodeEntry (OP65_BNE, AM65_BRA, L->Name, L, D->OpEntry->LI);
569             InsertEntry (D, X, D->OpIndex+6);
570         }
571
572     }
573
574     /* Remove the push and the call to the __bzero function */
575     RemoveRemainders (D);
576
577     /* We changed the sequence */
578     return 1;
579 }
580
581
582
583 static unsigned Opt_staspidx (StackOpData* D)
584 /* Optimize the staspidx sequence if possible */
585 {
586     CodeEntry* X;
587
588     /* Check if we're using a register variable */
589     if (!IsRegVar (D)) {
590         /* Store the value into the zeropage instead of pushing it */
591         ReplacePushByStore (D);
592     }
593
594     /* Replace the store subroutine call by a direct op */
595     X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, D->ZPLo, 0, D->OpEntry->LI);
596     InsertEntry (D, X, D->OpIndex+1);
597
598     /* Remove the push and the call to the staspidx function */
599     RemoveRemainders (D);
600
601     /* We changed the sequence */
602     return 1;
603 }
604
605
606
607 static unsigned Opt_staxspidx (StackOpData* D)
608 /* Optimize the staxspidx sequence if possible */
609 {
610     CodeEntry* X;
611
612     /* Check if we're using a register variable */
613     if (!IsRegVar (D)) {
614         /* Store the value into the zeropage instead of pushing it */
615         ReplacePushByStore (D);
616     }
617
618     /* Inline the store */
619
620     /* sta (zp),y */
621     X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, D->ZPLo, 0, D->OpEntry->LI);
622     InsertEntry (D, X, D->OpIndex+1);
623
624     if (RegValIsKnown (D->OpEntry->RI->In.RegY)) {
625         /* Value of Y is known */
626         const char* Arg = MakeHexArg (D->OpEntry->RI->In.RegY + 1);
627         X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
628     } else {
629         X = NewCodeEntry (OP65_INY, AM65_IMP, 0, 0, D->OpEntry->LI);
630     }
631     InsertEntry (D, X, D->OpIndex+2);
632
633     if (RegValIsKnown (D->OpEntry->RI->In.RegX)) {
634         /* Value of X is known */
635         const char* Arg = MakeHexArg (D->OpEntry->RI->In.RegX);
636         X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, D->OpEntry->LI);
637     } else {
638         /* Value unknown */
639         X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, D->OpEntry->LI);
640     }
641     InsertEntry (D, X, D->OpIndex+3);
642
643     /* sta (zp),y */
644     X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, D->ZPLo, 0, D->OpEntry->LI);
645     InsertEntry (D, X, D->OpIndex+4);
646
647     /* If we remove staxspidx, we must restore the Y register to what the
648      * function would return.
649      */
650     X = NewCodeEntry (OP65_LDY, AM65_IMM, "$00", 0, D->OpEntry->LI);
651     InsertEntry (D, X, D->OpIndex+5);
652
653     /* Remove the push and the call to the staxspidx function */
654     RemoveRemainders (D);
655
656     /* We changed the sequence */
657     return 1;
658 }
659
660
661
662 static unsigned Opt_tosaddax (StackOpData* D)
663 /* Optimize the tosaddax sequence if possible */
664 {
665     CodeEntry*  X;
666     CodeEntry*  N;
667
668     /* We need the entry behind the add */
669     CHECK (D->NextEntry != 0);
670
671     /* Check if the X register is known and zero when the add is done, and
672      * if the add is followed by
673      *
674      *  ldy     #$00
675      *  jsr     ldauidx         ; or ldaidx
676      *
677      * If this is true, the addition does actually add an offset to a pointer
678      * before it is dereferenced. Since both subroutines take an offset in Y,
679      * we can pass the offset (instead of #$00) and remove the addition
680      * alltogether.
681      */
682     if (D->OpEntry->RI->In.RegX == 0                            &&
683         D->NextEntry->OPC == OP65_LDY                           &&
684         CE_IsKnownImm (D->NextEntry, 0)                         &&
685         !CE_HasLabel (D->NextEntry)                             &&
686         (N = CS_GetNextEntry (D->Code, D->OpIndex + 1)) != 0    &&
687         (CE_IsCallTo (N, "ldauidx")                     ||
688          CE_IsCallTo (N, "ldaidx"))) {
689
690         int Signed = (strcmp (N->Arg, "ldaidx") == 0);
691
692         /* Store the value into the zeropage instead of pushing it */
693         ReplacePushByStore (D);
694
695         /* Replace the ldy by a tay. Be sure to create the new entry before
696          * deleting the ldy, since we will reference the line info from this
697          * insn.
698          */
699         X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, D->NextEntry->LI);
700         DelEntry (D, D->OpIndex + 1);
701         InsertEntry (D, X, D->OpIndex + 1);
702
703         /* Replace the call to ldaidx/ldauidx. Since X is already zero, and
704          * the ptr is in the zero page location, we just need to load from
705          * the pointer, and fix X in case of ldaidx.
706          */
707         X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, D->ZPLo, 0, N->LI);
708         DelEntry (D, D->OpIndex + 2);
709         InsertEntry (D, X, D->OpIndex + 2);
710         if (Signed) {
711
712             CodeLabel* L;
713
714             /* Add sign extension - N is unused now */
715             N = CS_GetNextEntry (D->Code, D->OpIndex + 2);
716             CHECK (N != 0);
717             L = CS_GenLabel (D->Code, N);
718
719             X = NewCodeEntry (OP65_BPL, AM65_BRA, L->Name, L, X->LI);
720             InsertEntry (D, X, D->OpIndex + 3);
721
722             X = NewCodeEntry (OP65_DEX, AM65_IMP, 0, 0, X->LI);
723             InsertEntry (D, X, D->OpIndex + 4);
724         }
725
726     } else {
727
728         /* Check the entry before the push. If it's a lda instruction with an
729          * addressing mode that allows us to replace it, we may use this
730          * location for the op and must not save the value in the zero page
731          * location.
732          */
733         CheckDirectOp (D);
734
735         /* Store the value into the zeropage instead of pushing it */
736         ReplacePushByStore (D);
737
738         /* Inline the add */
739         D->IP = D->OpIndex+1;
740
741         /* clc */
742         X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, D->OpEntry->LI);
743         InsertEntry (D, X, D->IP++);
744
745         /* Low byte */
746         AddOpLow (D, OP65_ADC);
747
748         /* High byte */
749         if (D->PushEntry->RI->In.RegX == 0) {
750             /* The high byte is the value in X plus the carry */
751             CodeLabel* L = CS_GenLabel (D->Code, D->NextEntry);
752             X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, D->OpEntry->LI);
753             InsertEntry (D, X, D->IP++);
754             X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, D->OpEntry->LI);
755             InsertEntry (D, X, D->IP++);
756         } else if (D->OpEntry->RI->In.RegX == 0) {
757             /* The high byte is that of the first operand plus carry */
758             CodeLabel* L;
759             if (RegValIsKnown (D->PushEntry->RI->In.RegX)) {
760                 /* Value of first op high byte is known */
761                 const char* Arg = MakeHexArg (D->PushEntry->RI->In.RegX);
762                 X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, D->OpEntry->LI);
763             } else {
764                 /* Value of first op high byte is unknown */
765                 X = NewCodeEntry (OP65_LDX, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI);
766             }
767             InsertEntry (D, X, D->IP++);
768
769             /* bcc label */
770             L = CS_GenLabel (D->Code, D->NextEntry);
771             X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, D->OpEntry->LI);
772             InsertEntry (D, X, D->IP++);
773
774             /* inx */
775             X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, D->OpEntry->LI);
776             InsertEntry (D, X, D->IP++);
777         } else {
778             /* High byte is unknown */
779             AddOpHigh (D, OP65_ADC);
780         }
781     }
782
783     /* Remove the push and the call to the tosaddax function */
784     RemoveRemainders (D);
785
786     /* We changed the sequence */
787     return 1;
788 }
789
790
791
792 static unsigned Opt_tosandax (StackOpData* D)
793 /* Optimize the tosandax sequence if possible */
794 {
795     CodeEntry*  X;
796
797     /* Check the entry before the push. If it's a lda instruction with an
798      * addressing mode that allows us to replace it, we may use this
799      * location for the op and must not save the value in the zero page
800      * location.
801      */
802     CheckDirectOp (D);
803
804     /* Store the value into the zeropage instead of pushing it */
805     ReplacePushByStore (D);
806
807     /* Inline the and, low byte */
808     D->IP = D->OpIndex + 1;
809     AddOpLow (D, OP65_AND);
810
811     /* High byte */
812     if (D->PushEntry->RI->In.RegX == 0 || D->OpEntry->RI->In.RegX == 0) {
813         /* The high byte is zero */
814         X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, D->OpEntry->LI);
815         InsertEntry (D, X, D->IP++);
816     } else {
817         /* High byte is unknown */
818         AddOpHigh (D, OP65_AND);
819     }
820
821     /* Remove the push and the call to the tosandax function */
822     RemoveRemainders (D);
823
824     /* We changed the sequence */
825     return 1;
826 }
827
828
829
830 static unsigned Opt_tosorax (StackOpData* D)
831 /* Optimize the tosorax sequence if possible */
832 {
833     CodeEntry*  X;
834
835     /* Check the entry before the push. If it's a lda instruction with an
836      * addressing mode that allows us to replace it, we may use this
837      * location for the op and must not save the value in the zero page
838      * location.
839      */
840     CheckDirectOp (D);
841
842     /* Store the value into the zeropage instead of pushing it */
843     ReplacePushByStore (D);
844
845     /* Inline the or, low byte */
846     D->IP = D->OpIndex + 1;
847     AddOpLow (D, OP65_ORA);
848
849     /* High byte */
850     if (RegValIsKnown (D->PushEntry->RI->In.RegX) &&
851         RegValIsKnown (D->OpEntry->RI->In.RegX)) {
852         /* Both values known, precalculate the result */
853         unsigned char Result = D->PushEntry->RI->In.RegX | D->OpEntry->RI->In.RegX;
854         const char* Arg = MakeHexArg (Result);
855         X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, D->OpEntry->LI);
856         InsertEntry (D, X, D->IP++);
857     } else if (D->PushEntry->RI->In.RegX != 0) {
858         /* High byte is unknown */
859         AddOpHigh (D, OP65_ORA);
860     }
861
862     /* Remove the push and the call to the tosorax function */
863     RemoveRemainders (D);
864
865     /* We changed the sequence */
866     return 1;
867 }
868
869
870
871 static unsigned Opt_tossubax (StackOpData* D)
872 /* Optimize the tossubax sequence if possible */
873 {
874     CodeEntry*  X;
875
876     /* Check the load entry before the push. If it's a lda instruction with an
877      * addressing mode that allows us to replace it, we may use this
878      * location for the op and must not save the value in the zero page
879      * location.
880      */
881     CheckDirectOp (D);
882
883     /* Store the value into the zeropage instead of pushing it */
884     ReplacePushByStore (D);
885
886     /* Inline the sbc */
887     D->IP = D->OpIndex+1;
888
889     /* sec */
890     X = NewCodeEntry (OP65_SEC, AM65_IMP, 0, 0, D->OpEntry->LI);
891     InsertEntry (D, X, D->IP++);
892
893     /* Low byte */
894     AddOpLow (D, OP65_SBC);
895
896     /* High byte */
897     AddOpHigh (D, OP65_SBC);
898
899     /* Remove the push and the call to the tosaddax function */
900     RemoveRemainders (D);
901
902     /* We changed the sequence */
903     return 1;
904 }
905
906
907
908 static unsigned Opt_tosxorax (StackOpData* D)
909 /* Optimize the tosxorax sequence if possible */
910 {
911     CodeEntry*  X;
912
913     /* Check the entry before the push. If it's a lda instruction with an
914      * addressing mode that allows us to replace it, we may use this
915      * location for the op and must not save the value in the zero page
916      * location.
917      */
918     CheckDirectOp (D);
919
920     /* Store the value into the zeropage instead of pushing it */
921     ReplacePushByStore (D);
922
923     /* Inline the xor, low byte */
924     D->IP = D->OpIndex + 1;
925     AddOpLow (D, OP65_EOR);
926
927     /* High byte */
928     if (RegValIsKnown (D->PushEntry->RI->In.RegX) &&
929         RegValIsKnown (D->OpEntry->RI->In.RegX)) {
930         /* Both values known, precalculate the result */
931         const char* Arg = MakeHexArg (D->PushEntry->RI->In.RegX ^ D->OpEntry->RI->In.RegX);
932         X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, D->OpEntry->LI);
933         InsertEntry (D, X, D->IP++);
934     } else if (D->PushEntry->RI->In.RegX != 0) {
935         /* High byte is unknown */
936         AddOpHigh (D, OP65_EOR);
937     }
938
939     /* Remove the push and the call to the tosandax function */
940     RemoveRemainders (D);
941
942     /* We changed the sequence */
943     return 1;
944 }
945
946
947
948 /*****************************************************************************/
949 /*                                   Code                                    */
950 /*****************************************************************************/
951
952
953
954 static const OptFuncDesc FuncTable[] = {
955     { "__bzero",    Opt___bzero,   REG_NONE, STOP_X_ZERO | STOP_A_KNOWN   },
956     { "staspidx",   Opt_staspidx,  REG_NONE, STOP_NONE                    },
957     { "staxspidx",  Opt_staxspidx, REG_AX,   STOP_NONE                    },
958     { "tosaddax",   Opt_tosaddax,  REG_NONE, STOP_NONE                    },
959     { "tosandax",   Opt_tosandax,  REG_NONE, STOP_NONE                    },
960     { "tosorax",    Opt_tosorax,   REG_NONE, STOP_NONE                    },
961     { "tossubax",   Opt_tossubax,  REG_NONE, STOP_NONE                    },
962     { "tosxorax",   Opt_tosxorax,  REG_NONE, STOP_NONE                    },
963 };
964 #define FUNC_COUNT (sizeof(FuncTable) / sizeof(FuncTable[0]))
965
966
967
968 static int CmpFunc (const void* Key, const void* Func)
969 /* Compare function for bsearch */
970 {
971     return strcmp (Key, ((const OptFuncDesc*) Func)->Name);
972 }
973
974
975
976 static const OptFuncDesc* FindFunc (const char* Name)
977 /* Find the function with the given name. Return a pointer to the table entry
978  * or NULL if the function was not found.
979  */
980 {
981     return bsearch (Name, FuncTable, FUNC_COUNT, sizeof(OptFuncDesc), CmpFunc);
982 }
983
984
985
986 static int CmpHarmless (const void* Key, const void* Entry)
987 /* Compare function for bsearch */
988 {
989     return strcmp (Key, *(const char**)Entry);
990 }
991
992
993
994 static int HarmlessCall (const char* Name)
995 /* Check if this is a call to a harmless subroutine that will not interrupt
996  * the pushax/op sequence when encountered.
997  */
998 {
999     static const char* Tab[] = {
1000         "aslax1",
1001         "aslax2",
1002         "asrax1",
1003         "asrax2",
1004         "ldaxidx",
1005         "ldaxysp",
1006         "negax",
1007         "shlax1",
1008         "shlax2",
1009         "shrax1",
1010         "shrax2",
1011     };
1012
1013     void* R = bsearch (Name,
1014                        Tab,
1015                        sizeof (Tab) / sizeof (Tab[0]),
1016                        sizeof (Tab[0]),
1017                        CmpHarmless);
1018     return (R != 0);
1019 }
1020
1021
1022
1023 static void ResetStackOpData (StackOpData* Data)
1024 /* Reset the given data structure */
1025 {
1026     Data->AData.Flags   = 0;
1027     Data->XData.Flags   = 0;
1028     Data->OptFunc       = 0;
1029
1030     Data->LoadAIndex    = -1;
1031     Data->LoadXIndex    = -1;
1032     Data->LoadYIndex    = -1;
1033     Data->PushIndex     = -1;
1034     Data->OpIndex       = -1;
1035
1036     Data->LoadAEntry    = 0;
1037     Data->LoadXEntry    = 0;
1038
1039     Data->UsedRegs      = REG_NONE;
1040 }
1041
1042
1043
1044 static int PreCondOk (StackOpData* D)
1045 /* Check if the preconditions for a call to the optimizer subfunction are
1046  * satisfied. As a side effect, this function will also choose the zero page
1047  * register to use.
1048  */
1049 {
1050     /* Check the flags */
1051     unsigned UnusedRegs = D->OptFunc->UnusedRegs;
1052     if (UnusedRegs != REG_NONE &&
1053         (GetRegInfo (D->Code, D->OpIndex+1, UnusedRegs) & UnusedRegs) != 0) {
1054         /* Cannot optimize */
1055         return 0;
1056     }
1057     if ((D->OptFunc->Flags & STOP_A_KNOWN) != 0 &&
1058         RegValIsUnknown (D->OpEntry->RI->In.RegA)) {
1059         /* Cannot optimize */
1060         return 0;
1061     }
1062     if ((D->OptFunc->Flags & STOP_X_ZERO) != 0 &&
1063         D->OpEntry->RI->In.RegX != 0) {
1064         /* Cannot optimize */
1065         return 0;
1066     }
1067
1068     /* Determine the zero page locations to use */
1069     if ((D->UsedRegs & REG_PTR1) == REG_NONE) {
1070         D->ZPLo = "ptr1";
1071         D->ZPHi = "ptr1+1";
1072     } else if ((D->UsedRegs & REG_SREG) == REG_NONE) {
1073         D->ZPLo = "sreg";
1074         D->ZPHi = "sreg+1";
1075     } else if ((D->UsedRegs & REG_PTR1) == REG_NONE) {
1076         D->ZPLo = "ptr1";
1077         D->ZPHi = "ptr1+1";
1078     } else if ((D->UsedRegs & REG_PTR2) == REG_NONE) {
1079         D->ZPLo = "ptr2";
1080         D->ZPHi = "ptr2+1";
1081     } else {
1082         /* No registers available */
1083         return 0;
1084     }
1085
1086     /* Determine if we have a basic block */
1087     return CS_IsBasicBlock (D->Code, D->PushIndex, D->OpIndex);
1088 }
1089
1090
1091
1092 /*****************************************************************************/
1093 /*                                   Code                                    */
1094 /*****************************************************************************/
1095
1096
1097
1098 unsigned OptStackOps (CodeSeg* S)
1099 /* Optimize operations that take operands via the stack */
1100 {
1101     unsigned            Changes = 0;    /* Number of changes in one run */
1102     StackOpData         Data;
1103     unsigned            I;
1104
1105     enum {
1106         Searching,
1107         FoundPush,
1108         FoundOp
1109     } State = Searching;
1110
1111
1112     /* Generate register info */
1113     CS_GenRegInfo (S);
1114
1115     /* Clear Data */
1116     Data.Code = S;
1117     ResetStackOpData (&Data);
1118
1119     /* Look for a call to pushax followed by a call to some other function
1120      * that takes it's first argument on the stack, and the second argument
1121      * in the primary register.
1122      * It depends on the code between the two if we can handle/transform the
1123      * sequence, so check this code for the following list of things:
1124      *
1125      *  - the range must be a basic block (one entry, one exit)
1126      *  - there may not be accesses to local variables with unknown
1127      *    offsets (because we have to adjust these offsets).
1128      *  - no subroutine calls
1129      *  - no jump labels
1130      *
1131      * Since we need a zero page register later, do also check the
1132      * intermediate code for zero page use.
1133      */
1134     I = 0;
1135     while (I < CS_GetEntryCount (S)) {
1136
1137         /* Get the next entry */
1138         CodeEntry* E = CS_GetEntry (S, I);
1139
1140         /* Actions depend on state */
1141         switch (State) {
1142
1143             case Searching:
1144                 /* While searching, track register load insns, so we can tell
1145                  * what is in a register once pushax is encountered.
1146                  */
1147                 if (CE_IsCallTo (E, "pushax")) {
1148                     Data.PushIndex = I;
1149                     State = FoundPush;
1150                 } else if (E->Info & OF_LOAD) {
1151                     if (E->Chg & REG_A) {
1152                         Data.LoadAIndex = I;
1153                     }
1154                     if (E->Chg & REG_X) {
1155                         Data.LoadXIndex = I;
1156                     }
1157                     if (E->Chg & REG_Y) {
1158                         Data.LoadYIndex = I;
1159                     }
1160                 } else if (E->Info & OF_XFR) {
1161                     switch (E->OPC) {
1162                         case OP65_TAX: Data.LoadXIndex = Data.LoadAIndex; break;
1163                         case OP65_TAY: Data.LoadYIndex = Data.LoadAIndex; break;
1164                         case OP65_TXA: Data.LoadAIndex = Data.LoadXIndex; break;
1165                         case OP65_TYA: Data.LoadAIndex = Data.LoadYIndex; break;
1166                         default:                                          break;
1167                     }
1168                 } else if (CE_IsCallTo (E, "ldaxysp")) {
1169                     /* Both registers set */
1170                     Data.LoadAIndex = I;
1171                     Data.LoadXIndex = I;
1172                 } else {
1173                     if (E->Chg & REG_A) {
1174                         Data.LoadAIndex = -1;
1175                     }
1176                     if (E->Chg & REG_X) {
1177                         Data.LoadXIndex = -1;
1178                     }
1179                     if (E->Chg & REG_Y) {
1180                         Data.LoadYIndex = -1;
1181                     }
1182                 }
1183                 break;
1184
1185             case FoundPush:
1186                 /* We' found a pushax before. Search for a stack op that may
1187                  * follow and in the meantime, track zeropage usage and check
1188                  * for code that will disable us from translating the sequence.
1189                  */
1190                 if (E->OPC == OP65_JSR) {
1191
1192                     /* Subroutine call: Check if this is one of the functions,
1193                      * we're going to replace.
1194                      */
1195                     Data.OptFunc = FindFunc (E->Arg);
1196                     if (Data.OptFunc) {
1197                         /* Remember the op index and go on */
1198                         Data.OpIndex = I;
1199                         Data.OpEntry = E;
1200                         State = FoundOp;
1201                         break;
1202                     } else if (HarmlessCall (E->Arg)) {
1203                         /* Track zeropage register usage */
1204                         Data.UsedRegs |= (E->Use | E->Chg);
1205                     } else {
1206                         /* A call to an unkown subroutine: We need to start
1207                          * over after the last pushax. Note: This will also
1208                          * happen if we encounter a call to pushax!
1209                          */
1210                         I = Data.PushIndex;
1211                         ResetStackOpData (&Data);
1212                         State = Searching;
1213                         break;
1214                     }
1215
1216                 } else if ((E->Use & REG_SP) != 0 &&
1217                     (E->AM != AM65_ZP_INDY || RegValIsUnknown (E->RI->In.RegY) ||
1218                      E->RI->In.RegY < 2)) {
1219
1220                     /* If we are using the stack, and we don't have "indirect Y"
1221                      * addressing mode, or the value of Y is unknown, or less
1222                      * than two, we cannot cope with this piece of code. Having
1223                      * an unknown value of Y means that we cannot correct the
1224                      * stack offset, while having an offset less than two means
1225                      * that the code works with the value on stack which is to
1226                      * be removed.
1227                      */
1228                     I = Data.PushIndex;
1229                     ResetStackOpData (&Data);
1230                     State = Searching;
1231                     break;
1232
1233                 } else {
1234                     /* Other stuff: Track zeropage register usage */
1235                     Data.UsedRegs |= (E->Use | E->Chg);
1236                 }
1237                 break;
1238
1239             case FoundOp:
1240                 /* Track zero page location usage beyond this point */
1241                 Data.UsedRegs |= GetRegInfo (S, I, REG_SREG | REG_PTR1 | REG_PTR2);
1242
1243                 /* Get the entry pointers to the load insns. If these insns
1244                  * load from zero page, we have to include them into UsedRegs
1245                  * registers used.
1246                  */
1247                 if (Data.LoadAIndex >= 0) {
1248                     Data.LoadAEntry = CS_GetEntry (S, Data.LoadAIndex);
1249                     if (Data.LoadAEntry->AM == AM65_ZP) {
1250                         Data.UsedRegs |= Data.LoadAEntry->Use;
1251                     }
1252                 }
1253                 if (Data.LoadXIndex >= 0) {
1254                     Data.LoadXEntry = CS_GetEntry (S, Data.LoadXIndex);
1255                     if (Data.LoadXEntry->AM == AM65_ZP) {
1256                         Data.UsedRegs |= Data.LoadXEntry->Use;
1257                     }
1258                 }
1259
1260                 /* Check the preconditions. If they aren't ok, reset the insn
1261                  * pointer to the pushax and start over. We will loose part of
1262                  * load tracking but at least a/x has probably lost between
1263                  * pushax and here and will be tracked again when restarting.
1264                  */
1265                 if (!PreCondOk (&Data)) {
1266                     I = Data.PushIndex;
1267                     ResetStackOpData (&Data);
1268                     State = Searching;
1269                     break;
1270                 }
1271
1272                 /* Adjust stack offsets to account for the upcoming removal */
1273                 AdjustStackOffset (&Data, 2);
1274
1275                 /* Prepare the remainder of the data structure. */
1276                 Data.PrevEntry = CS_GetPrevEntry (S, Data.PushIndex);
1277                 Data.PushEntry = CS_GetEntry (S, Data.PushIndex);
1278                 Data.OpEntry   = CS_GetEntry (S, Data.OpIndex);
1279                 Data.NextEntry = CS_GetNextEntry (S, Data.OpIndex);
1280
1281                 /* Call the optimizer function */
1282                 Changes += Data.OptFunc->Func (&Data);
1283
1284                 /* Regenerate register info */
1285                 CS_GenRegInfo (S);
1286
1287                 /* Done */
1288                 ResetStackOpData (&Data);
1289                 State = Searching;
1290                 break;
1291
1292         }
1293
1294         /* Next entry */
1295         ++I;
1296
1297     }
1298
1299     /* Free the register info */
1300     CS_FreeRegInfo (S);
1301
1302     /* Return the number of changes made */
1303     return Changes;
1304 }
1305
1306
1307