]> git.sur5r.net Git - cc65/blob - src/cc65/coptstop.c
697a0b5630433b80f2eff7bd7b2caf6d62559ce2
[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-2002 Ullrich von Bassewitz                                       */
10 /*               Wacholderweg 14                                             */
11 /*               D-70597 Stuttgart                                           */
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 /* cc65 */
39 #include "codeent.h"
40 #include "codeinfo.h"
41 #include "codeopt.h"
42 #include "error.h"
43 #include "coptstop.h"
44
45
46
47 /*****************************************************************************/
48 /*                                  Helpers                                  */
49 /*****************************************************************************/
50
51
52
53 static unsigned AdjustStackOffset (CodeSeg* S, unsigned Start, unsigned Stop,
54                                    unsigned Offs)
55 /* Adjust the offset for all stack accesses in the range Start to Stop, both
56  * inclusive. The function returns the number of instructions that have been
57  * inserted.
58  */
59 {
60     /* Number of inserted instructions */
61     unsigned Inserted = 0;
62
63     /* Walk over all entries */
64     unsigned I = Start;
65     while (I <= Stop) {
66
67         CodeEntry* E = CS_GetEntry (S, I);
68
69         if (E->Use & REG_SP) {
70
71             CodeEntry* P;
72
73             /* Check for some things that should not happen */
74             CHECK (E->AM == AM65_ZP_INDY || E->RI->In.RegY >= (short) Offs);
75             CHECK (strcmp (E->Arg, "sp") == 0);
76
77             /* Get the code entry before this one. If it's a LDY, adjust the
78              * value.
79              */
80             P = CS_GetPrevEntry (S, I);
81             if (P && P->OPC == OP65_LDY && CE_KnownImm (P)) {
82
83                 /* The Y load is just before the stack access, adjust it */
84                 CE_SetNumArg (P, P->Num - Offs);
85
86             } else {
87
88                 /* Insert a new load instruction before the stack access */
89                 const char* Arg = MakeHexArg (E->RI->In.RegY - Offs);
90                 CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
91                 CS_InsertEntry (S, X, I);
92
93                 /* One more inserted entries */
94                 ++Inserted;
95                 ++Stop;
96
97                 /* Be sure to skip the stack access for the next round */
98                 ++I;
99
100             }
101
102         }
103
104         /* Next entry */
105         ++I;
106     }
107
108     /* Return the number of inserted entries */
109     return Inserted;
110 }
111
112
113
114 /*****************************************************************************/
115 /*                       Actual optimization functions                       */
116 /*****************************************************************************/
117
118
119
120 static unsigned Opt_staspidx (CodeSeg* S, unsigned Push, unsigned Store,
121                               const char* ZPLo, const char* ZPHi)
122 /* Optimize the staspidx sequence if possible */
123 {
124     CodeEntry* X;
125     CodeEntry* PushEntry;
126     CodeEntry* StoreEntry;
127
128     /* Get the push entry */
129     PushEntry = CS_GetEntry (S, Push);
130
131     /* Store the value into the zeropage instead of pushing it */
132     X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
133     CS_InsertEntry (S, X, Push+1);
134     X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
135     CS_InsertEntry (S, X, Push+2);
136
137     /* Correct the index of the store and get a pointer to the entry */
138     Store += 2;
139     StoreEntry = CS_GetEntry (S, Store);
140
141     /* Inline the store */
142     X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, StoreEntry->LI);
143     CS_InsertEntry (S, X, Store+1);
144
145     /* Remove the push and the call to the staspidx function */
146     CS_DelEntry (S, Store);
147     CS_DelEntry (S, Push);
148
149     /* We changed the sequence */
150     return 1;
151 }
152
153
154
155 static unsigned Opt_staxspidx (CodeSeg* S, unsigned Push, unsigned Store,
156                                const char* ZPLo, const char* ZPHi)
157 /* Optimize the staxspidx sequence if possible */
158 {
159     CodeEntry* X;
160     CodeEntry* PushEntry;
161     CodeEntry* StoreEntry;
162
163     /* Get the push entry */
164     PushEntry = CS_GetEntry (S, Push);
165
166     /* Store the value into the zeropage instead of pushing it */
167     X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
168     CS_InsertEntry (S, X, Push+1);
169     X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
170     CS_InsertEntry (S, X, Push+2);
171
172     /* Correct the index of the store and get a pointer to the entry */
173     Store += 2;
174     StoreEntry = CS_GetEntry (S, Store);
175
176     /* Inline the store */
177     X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, StoreEntry->LI);
178     CS_InsertEntry (S, X, Store+1);
179     X = NewCodeEntry (OP65_INY, AM65_IMP, 0, 0, StoreEntry->LI);
180     CS_InsertEntry (S, X, Store+2);
181     if (StoreEntry->RI->In.RegX >= 0) {
182         /* Value of X is known */
183         const char* Arg = MakeHexArg (StoreEntry->RI->In.RegX);
184         X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, StoreEntry->LI);
185     } else {
186         /* Value unknown */
187         X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, StoreEntry->LI);
188     }
189     CS_InsertEntry (S, X, Store+3);
190     X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, StoreEntry->LI);
191     CS_InsertEntry (S, X, Store+4);
192
193     /* Remove the push and the call to the staspidx function */
194     CS_DelEntry (S, Store);
195     CS_DelEntry (S, Push);
196
197     /* We changed the sequence */
198     return 1;
199 }
200
201
202
203 static unsigned Opt_tosaddax (CodeSeg* S, unsigned Push, unsigned Add,
204                               const char* ZPLo, const char* ZPHi)
205 /* Optimize the tosaddax sequence if possible */
206 {
207     CodeEntry* P;
208     CodeEntry* N;
209     CodeEntry* X;
210     CodeEntry* PushEntry;
211     CodeEntry* AddEntry;
212     int        DirectAdd;
213
214
215     /* We need the entry behind the add */
216     CHECK ((N = CS_GetNextEntry (S, Add)) != 0);
217
218     /* And the entry before the push */
219     CHECK ((P = CS_GetPrevEntry (S, Push)) != 0);
220
221     /* Get the push entry */
222     PushEntry = CS_GetEntry (S, Push);
223
224     /* Check the entry before the push, if it's a lda instruction with an
225      * addressing mode that does not use an additional index register. If
226      * so, we may use this location for the add and must not save the
227      * value in the zero page location.
228      */
229     DirectAdd = (P->OPC == OP65_LDA &&
230                  (P->AM == AM65_IMM || P->AM == AM65_ZP || P->AM == AM65_ABS));
231
232     /* Store the value into the zeropage instead of pushing it */
233     X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
234     CS_InsertEntry (S, X, Push+1);
235     ++Add;      /* Correct the index */
236     if (!DirectAdd) {
237         X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
238         CS_InsertEntry (S, X, Push+1);
239         ++Add;  /* Correct the index */
240     }
241
242     /* Get a pointer to the add entry */
243     AddEntry = CS_GetEntry (S, Add);
244
245     /* Inline the add */
246     X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, AddEntry->LI);
247     CS_InsertEntry (S, X, Add+1);
248     if (DirectAdd) {
249         /* Add from variable location */
250         X = NewCodeEntry (OP65_ADC, P->AM, P->Arg, 0, AddEntry->LI);
251     } else {
252         /* Add from temp storage */
253         X = NewCodeEntry (OP65_ADC, AM65_ZP, ZPLo, 0, AddEntry->LI);
254     }
255     CS_InsertEntry (S, X, Add+2);
256     if (PushEntry->RI->In.RegX == 0) {
257         /* The high byte is the value in X plus the carry */
258         CodeLabel* L = CS_GenLabel (S, N);
259         X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, AddEntry->LI);
260         CS_InsertEntry (S, X, Add+3);
261         X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, AddEntry->LI);
262         CS_InsertEntry (S, X, Add+4);
263     } else if (AddEntry->RI->In.RegX == 0) {
264         /* The high byte is that of the first operand plus carry */
265         CodeLabel* L;
266         if (PushEntry->RI->In.RegX >= 0) {
267             /* Value of first op high byte is known */
268             const char* Arg = MakeHexArg (PushEntry->RI->In.RegX);
269             X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, AddEntry->LI);
270         } else {
271             /* Value of first op high byte is unknown */
272             X = NewCodeEntry (OP65_LDX, AM65_ZP, ZPHi, 0, AddEntry->LI);
273         }
274         CS_InsertEntry (S, X, Add+3);
275         L = CS_GenLabel (S, N);
276         X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, AddEntry->LI);
277         CS_InsertEntry (S, X, Add+4);
278         X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, AddEntry->LI);
279         CS_InsertEntry (S, X, Add+5);
280     } else {
281         /* High byte is unknown */
282         X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, AddEntry->LI);
283         CS_InsertEntry (S, X, Add+3);
284         X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, AddEntry->LI);
285         CS_InsertEntry (S, X, Add+4);
286         X = NewCodeEntry (OP65_ADC, AM65_ZP, ZPHi, 0, AddEntry->LI);
287         CS_InsertEntry (S, X, Add+5);
288         X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, AddEntry->LI);
289         CS_InsertEntry (S, X, Add+6);
290         X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, AddEntry->LI);
291         CS_InsertEntry (S, X, Add+7);
292     }
293
294     /* Remove the push and the call to the tosaddax function */
295     CS_DelEntry (S, Add);
296     CS_DelEntry (S, Push);
297
298     /* We changed the sequence */
299     return 1;
300 }
301
302
303
304 static unsigned Opt_tosandax (CodeSeg* S, unsigned Push, unsigned And,
305                               const char* ZPLo, const char* ZPHi)
306 /* Optimize the tosandax sequence if possible */
307 {
308     CodeEntry* P;
309     CodeEntry* X;
310     CodeEntry* PushEntry;
311     CodeEntry* AndEntry;
312     int        DirectAnd;
313
314     /* Get the entry before the push */
315     CHECK ((P = CS_GetPrevEntry (S, Push)) != 0);
316
317     /* Get the push entry */
318     PushEntry = CS_GetEntry (S, Push);
319
320     /* Check the entry before the push, if it's a lda instruction with an
321      * addressing mode that does not use an additional index register. If
322      * so, we may use this location for the and and must not save the
323      * value in the zero page location.
324      */
325     DirectAnd = (P->OPC == OP65_LDA &&
326                  (P->AM == AM65_IMM || P->AM == AM65_ZP || P->AM == AM65_ABS));
327
328     /* Store the value into the zeropage instead of pushing it */
329     X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
330     CS_InsertEntry (S, X, Push+1);
331     ++And;      /* Correct the index */
332     if (!DirectAnd) {
333         X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
334         CS_InsertEntry (S, X, Push+1);
335         ++And;  /* Correct the index */
336     }
337
338     /* Get a pointer to the and entry */
339     AndEntry = CS_GetEntry (S, And);
340
341     /* Inline the and */
342     if (DirectAnd) {
343         /* And with variable location */
344         X = NewCodeEntry (OP65_AND, P->AM, P->Arg, 0, AndEntry->LI);
345     } else {
346         /* And with temp storage */
347         X = NewCodeEntry (OP65_AND, AM65_ZP, ZPLo, 0, AndEntry->LI);
348     }
349     CS_InsertEntry (S, X, And+1);
350     if (PushEntry->RI->In.RegX == 0 || AndEntry->RI->In.RegX == 0) {
351         /* The high byte is zero */
352         X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, AndEntry->LI);
353         CS_InsertEntry (S, X, And+2);
354     } else {
355         /* High byte is unknown */
356         X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, AndEntry->LI);
357         CS_InsertEntry (S, X, And+2);
358         X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, AndEntry->LI);
359         CS_InsertEntry (S, X, And+3);
360         X = NewCodeEntry (OP65_AND, AM65_ZP, ZPHi, 0, AndEntry->LI);
361         CS_InsertEntry (S, X, And+4);
362         X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, AndEntry->LI);
363         CS_InsertEntry (S, X, And+5);
364         X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, AndEntry->LI);
365         CS_InsertEntry (S, X, And+6);
366     }
367
368     /* Remove the push and the call to the tosandax function */
369     CS_DelEntry (S, And);
370     CS_DelEntry (S, Push);
371
372     /* We changed the sequence */
373     return 1;
374 }
375
376
377
378 static unsigned Opt_tosorax (CodeSeg* S, unsigned Push, unsigned Or,
379                              const char* ZPLo, const char* ZPHi)
380 /* Optimize the tosorax sequence if possible */
381 {
382     CodeEntry* P;
383     CodeEntry* X;
384     CodeEntry* PushEntry;
385     CodeEntry* OrEntry;
386     int        DirectOr;
387
388     /* Get the entry before the push */
389     CHECK ((P = CS_GetPrevEntry (S, Push)) != 0);
390
391     /* Get the push entry */
392     PushEntry = CS_GetEntry (S, Push);
393
394     /* Check the entry before the push, if it's a lda instruction with an
395      * addressing mode that does not use an additional index register. If
396      * so, we may use this location for the or and must not save the
397      * value in the zero page location.
398      */
399     DirectOr = (P->OPC == OP65_LDA &&
400                 (P->AM == AM65_IMM || P->AM == AM65_ZP || P->AM == AM65_ABS));
401
402     /* Store the value into the zeropage instead of pushing it */
403     X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
404     CS_InsertEntry (S, X, Push+1);
405     ++Or;  /* Correct the index */
406     if (!DirectOr) {
407         X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
408         CS_InsertEntry (S, X, Push+1);
409         ++Or;  /* Correct the index */
410     }
411
412     /* Get a pointer to the or entry */
413     OrEntry = CS_GetEntry (S, Or);
414
415     /* Inline the or */
416     if (DirectOr) {
417         /* Or with variable location */
418         X = NewCodeEntry (OP65_ORA, P->AM, P->Arg, 0, OrEntry->LI);
419     } else {
420         X = NewCodeEntry (OP65_ORA, AM65_ZP, ZPLo, 0, OrEntry->LI);
421     }
422     CS_InsertEntry (S, X, Or+1);
423     if (PushEntry->RI->In.RegX >= 0 && OrEntry->RI->In.RegX >= 0) {
424         /* Both values known, precalculate the result */
425         const char* Arg = MakeHexArg (PushEntry->RI->In.RegX | OrEntry->RI->In.RegX);
426         X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, OrEntry->LI);
427         CS_InsertEntry (S, X, Or+2);
428     } else if (PushEntry->RI->In.RegX != 0) {
429         /* High byte is unknown */
430         X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, OrEntry->LI);
431         CS_InsertEntry (S, X, Or+2);
432         X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, OrEntry->LI);
433         CS_InsertEntry (S, X, Or+3);
434         X = NewCodeEntry (OP65_ORA, AM65_ZP, ZPHi, 0, OrEntry->LI);
435         CS_InsertEntry (S, X, Or+4);
436         X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, OrEntry->LI);
437         CS_InsertEntry (S, X, Or+5);
438         X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, OrEntry->LI);
439         CS_InsertEntry (S, X, Or+6);
440     }
441
442     /* Remove the push and the call to the tosandax function */
443     CS_DelEntry (S, Or);
444     CS_DelEntry (S, Push);
445
446     /* We changed the sequence */
447     return 1;
448 }
449
450
451
452 static unsigned Opt_tosxorax (CodeSeg* S, unsigned Push, unsigned Xor,
453                               const char* ZPLo, const char* ZPHi)
454 /* Optimize the tosxorax sequence if possible */
455 {
456     CodeEntry* P;
457     CodeEntry* X;
458     CodeEntry* PushEntry;
459     CodeEntry* XorEntry;
460     int        DirectXor;
461
462     /* Get the entry before the push */
463     CHECK ((P = CS_GetPrevEntry (S, Push)) != 0);
464
465     /* Get the push entry */
466     PushEntry = CS_GetEntry (S, Push);
467
468     /* Check the entry before the push, if it's a lda instruction with an
469      * addressing mode that does not use an additional index register. If
470      * so, we may use this location for the xor and must not save the
471      * value in the zero page location.
472      */
473     DirectXor = (P->OPC == OP65_LDA &&
474                  (P->AM == AM65_IMM || P->AM == AM65_ZP || P->AM == AM65_ABS));
475
476     /* Store the value into the zeropage instead of pushing it */
477     X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
478     CS_InsertEntry (S, X, Push+1);
479     ++Xor;  /* Correct the index */
480     if (!DirectXor) {
481         X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
482         CS_InsertEntry (S, X, Push+1);
483         ++Xor;  /* Correct the index */
484     }
485
486     /* Get a pointer to the entry */
487     XorEntry = CS_GetEntry (S, Xor);
488
489     /* Inline the xor */
490     if (DirectXor) {
491         /* Xor with variable location */
492         X = NewCodeEntry (OP65_EOR, P->AM, P->Arg, 0, XorEntry->LI);
493     } else {
494         /* Xor with temp storage */
495         X = NewCodeEntry (OP65_EOR, AM65_ZP, ZPLo, 0, XorEntry->LI);
496     }
497     CS_InsertEntry (S, X, Xor+1);
498     if (PushEntry->RI->In.RegX >= 0 && XorEntry->RI->In.RegX >= 0) {
499         /* Both values known, precalculate the result */
500         const char* Arg = MakeHexArg (PushEntry->RI->In.RegX ^ XorEntry->RI->In.RegX);
501         X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, XorEntry->LI);
502         CS_InsertEntry (S, X, Xor+2);
503     } else if (PushEntry->RI->In.RegX != 0) {
504         /* High byte is unknown */
505         X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, XorEntry->LI);
506         CS_InsertEntry (S, X, Xor+2);
507         X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, XorEntry->LI);
508         CS_InsertEntry (S, X, Xor+3);
509         X = NewCodeEntry (OP65_EOR, AM65_ZP, ZPHi, 0, XorEntry->LI);
510         CS_InsertEntry (S, X, Xor+4);
511         X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, XorEntry->LI);
512         CS_InsertEntry (S, X, Xor+5);
513         X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, XorEntry->LI);
514         CS_InsertEntry (S, X, Xor+6);
515     }
516
517     /* Remove the push and the call to the tosandax function */
518     CS_DelEntry (S, Xor);
519     CS_DelEntry (S, Push);
520
521     /* We changed the sequence */
522     return 1;
523 }
524
525
526
527 /*****************************************************************************/
528 /*                                   Code                                    */
529 /*****************************************************************************/
530
531
532
533 /* Flags for the functions */
534 typedef enum {
535     STOP_NONE,              /* Nothing special */
536     STOP_A_UNUSED           /* Call only if a unused later */
537 } STOP_FLAGS;
538
539
540 typedef unsigned (*OptFunc) (CodeSeg* S, unsigned Push, unsigned Store,
541                              const char* ZPLo, const char* ZPHi);
542 typedef struct OptFuncDesc OptFuncDesc;
543 struct OptFuncDesc {
544     const char*     Name;   /* Name of the replaced runtime function */
545     OptFunc         Func;   /* Function pointer */
546     STOP_FLAGS      Flags;  /* Flags */
547 };
548
549 static const OptFuncDesc FuncTable[] = {
550     { "staspidx",   Opt_staspidx,  STOP_NONE },
551     { "staxspidx",  Opt_staxspidx, STOP_A_UNUSED },
552     { "tosaddax",   Opt_tosaddax,  STOP_NONE },
553     { "tosandax",   Opt_tosandax,  STOP_NONE },
554     { "tosorax",    Opt_tosorax,   STOP_NONE },
555     { "tosxorax",   Opt_tosxorax,  STOP_NONE },
556 };
557 #define FUNC_COUNT (sizeof(FuncTable) / sizeof(FuncTable[0]))
558
559
560
561 static int CmpFunc (const void* Key, const void* Func)
562 /* Compare function for bsearch */
563 {
564     return strcmp (Key, ((const OptFuncDesc*) Func)->Name);
565 }
566
567
568
569 static const OptFuncDesc* FindFunc (const char* Name)
570 /* Find the function with the given name. Return a pointer to the table entry
571  * or NULL if the function was not found.
572  */
573 {
574     return bsearch (Name, FuncTable, FUNC_COUNT, sizeof(OptFuncDesc), CmpFunc);
575 }
576
577
578
579 /*****************************************************************************/
580 /*                                   Code                                    */
581 /*****************************************************************************/
582
583
584
585 unsigned OptStackOps (CodeSeg* S)
586 /* Optimize operations that take operands via the stack */
587 {
588     unsigned Changes = 0;     /* Number of changes in one run */
589     int      InSeq = 0;       /* Inside a sequence */
590     unsigned Push = 0;        /* Index of pushax */
591     unsigned UsedRegs = 0;    /* Zeropage registers used in sequence */
592     unsigned I;
593
594     /* Generate register info */
595     CS_GenRegInfo (S);
596
597     /* Look for a call to pushax followed by a call to some other function
598      * that takes it's first argument on the stack, and the second argument
599      * in the primary register.
600      * It depends on the code between the two if we can handle/transform the
601      * sequence, so check this code for the following list of things:
602      *
603      *  - there must not be a jump or conditional branch (this may
604      *    get relaxed later).
605      *  - there may not be accesses to local variables with unknown
606      *    offsets (because we have to adjust these offsets).
607      *  - no subroutine calls
608      *  - no jump labels
609      *
610      * Since we need a zero page register later, do also check the
611      * intermediate code for zero page use.
612      */
613     I = 0;
614     while (I < CS_GetEntryCount (S)) {
615
616         /* Get the next entry */
617         CodeEntry* E = CS_GetEntry (S, I);
618
619         /* Handling depends if we're inside a sequence or not */
620         if (InSeq) {
621
622             if ((E->Info & OF_BRA) != 0                              ||
623                 ((E->Use & REG_SP) != 0                         &&
624                  (E->AM != AM65_ZP_INDY || E->RI->In.RegY < 0))      ||
625                 CE_HasLabel (E)) {
626
627                 /* All this stuff is not allowed in a sequence */
628                 InSeq = 0;
629
630             } else if (E->OPC == OP65_JSR) {
631
632                 /* Subroutine call: Check if this is one of our functions */
633                 const OptFuncDesc* F = FindFunc (E->Arg);
634                 if (F) {
635
636                     const char* ZPLo = 0;
637                     const char* ZPHi = 0;
638                     int PreCondOk    = 1;
639
640                     /* Check the flags */
641                     if (F->Flags & STOP_A_UNUSED) {
642                         /* a must be unused later */
643                         if (RegAUsed (S, I+1)) {
644                             /* Cannot optimize */
645                             PreCondOk = 0;
646                         }
647                     }
648
649                     /* Determine the zero page locations to use */
650                     if (PreCondOk) {
651                         UsedRegs |= GetRegInfo (S, I+1, REG_SREG | REG_PTR1 | REG_PTR2);
652                         if ((UsedRegs & REG_SREG) == REG_NONE) {
653                             /* SREG is available */
654                             ZPLo = "sreg";
655                             ZPHi = "sreg+1";
656                         } else if ((UsedRegs & REG_PTR1) == REG_NONE) {
657                             ZPLo = "ptr1";
658                             ZPHi = "ptr1+1";
659                         } else if ((UsedRegs & REG_PTR2) == REG_NONE) {
660                             ZPLo = "ptr2";
661                             ZPHi = "ptr2+1";
662                         } else {
663                             /* No registers available */
664                             PreCondOk = 0;
665                         }
666                     }
667
668                     /* If preconditions are ok, call the optimizer function */
669                     if (PreCondOk) {
670
671                         /* Adjust stack offsets */
672                         unsigned Op = I + AdjustStackOffset (S, Push, I, 2);
673
674                         /* Call the optimizer function */
675                         Changes += F->Func (S, Push, Op, ZPLo, ZPHi);
676
677                         /* Regenerate register info */
678                         CS_GenRegInfo (S);
679                     }
680
681                     /* End of sequence */
682                     InSeq = 0;
683
684                 } else if (strcmp (E->Arg, "pushax") == 0) {
685                     /* Restart the sequence */
686                     Push     = I;
687                     UsedRegs = REG_NONE;
688                 } else {
689                     /* A call to an unkown subroutine ends the sequence */
690                     InSeq = 0;
691                 }
692
693             } else {
694
695                 /* Other stuff: Track zeropage register usage */
696                 UsedRegs |= (E->Use | E->Chg);
697
698             }
699
700         } else if (CE_IsCall (E, "pushax")) {
701
702             /* This starts a sequence */
703             Push     = I;
704             UsedRegs = REG_NONE;
705             InSeq    = 1;
706
707         }
708
709         /* Next entry */
710         ++I;
711
712     }
713
714     /* Free the register info */
715     CS_FreeRegInfo (S);
716
717     /* Return the number of changes made */
718     return Changes;
719 }
720
721
722