]> git.sur5r.net Git - cc65/blob - src/cc65/coptstop.c
Command line options cleanup
[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      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 <string.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 Opt_tosaddax (CodeSeg* S, unsigned Push, unsigned Add)
54 /* Optimize the tosaddax sequence if possible */
55 {
56     unsigned I;
57     CodeEntry* N;
58     CodeEntry* X;
59     CodeEntry* PushEntry;
60     CodeEntry* AddEntry;
61     const char* ZPLo;
62     const char* ZPHi;
63
64     /* Check if the sequence is safe. This means that there may not be any
65      * jumps between the two data points, and no usage of the stack. Handling
66      * these conditions is possible and may be done later.
67      */
68     unsigned UsedRegs = REG_NONE;
69     for (I = Push + 1; I < Add; ++I) {
70         CodeEntry* E = CS_GetEntry (S, I);
71         if ((E->Info & OF_BRA) != 0 ||
72             (E->Use & REG_SP) != 0  ||
73             CE_HasLabel (E)) {
74             /* A jump or stack pointer usage - bail out */
75             return 0;
76         }
77         UsedRegs |= (E->Use | E->Chg);
78     }
79
80     /* We prefer usage of sreg for the intermediate value, since sreg is
81      * tracked and optimized.
82      */
83     UsedRegs |= GetRegInfo (S, Push+1, REG_ALL);
84     if ((UsedRegs & REG_SREG) == REG_NONE) {
85         /* SREG is available */
86         ZPLo = "sreg";
87         ZPHi = "sreg+1";
88     } else if ((UsedRegs & REG_PTR1) == REG_NONE) {
89         ZPLo = "ptr1";
90         ZPHi = "ptr1+1";
91     } else if ((UsedRegs & REG_PTR2) == REG_NONE) {
92         ZPLo = "ptr2";
93         ZPHi = "ptr2+1";
94     } else {
95         /* No registers available */
96         return 0;
97     }
98
99     /* We need the entry behind the add */
100     if ((N = CS_GetNextEntry (S, Add)) == 0) {
101         /* Unavailable */
102         return 0;
103     }
104
105     /* Generate register info */
106     CS_GenRegInfo (S);
107
108     /* Get the push entry */
109     PushEntry = CS_GetEntry (S, Push);
110
111     /* Store the value into sreg instead of pushing it */
112     X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
113     CS_InsertEntry (S, X, Push+1);
114     X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
115     CS_InsertEntry (S, X, Push+2);
116
117     /* Correct the index of the add and get a pointer to the entry */
118     Add += 2;
119     AddEntry = CS_GetEntry (S, Add);
120
121     /* Inline the add */
122     X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, AddEntry->LI);
123     CS_InsertEntry (S, X, Add+1);
124     X = NewCodeEntry (OP65_ADC, AM65_ZP, ZPLo, 0, AddEntry->LI);
125     CS_InsertEntry (S, X, Add+2);
126     if (PushEntry->RI->In.RegX == 0 && AddEntry->RI->In.RegX == 0) {
127         /* The high byte is zero on entry */
128         CodeLabel* L = CS_GenLabel (S, N);
129         X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, AddEntry->LI);
130         CS_InsertEntry (S, X, Add+3);
131         X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, AddEntry->LI);
132         CS_InsertEntry (S, X, Add+4);
133     } else {
134         /* High byte is unknown */
135         X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, AddEntry->LI);
136         CS_InsertEntry (S, X, Add+3);
137         X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, AddEntry->LI);
138         CS_InsertEntry (S, X, Add+4);
139         X = NewCodeEntry (OP65_ADC, AM65_ZP, ZPHi, 0, AddEntry->LI);
140         CS_InsertEntry (S, X, Add+5);
141         X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, AddEntry->LI);
142         CS_InsertEntry (S, X, Add+6);
143         X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, AddEntry->LI);
144         CS_InsertEntry (S, X, Add+7);
145     }
146
147     /* Remove the push and the call to the tosaddax function */
148     CS_DelEntry (S, Add);
149     CS_DelEntry (S, Push);
150
151     /* Free the register info */
152     CS_FreeRegInfo (S);
153
154     /* We changed the sequence */
155     return 1;
156 }
157
158
159
160 static unsigned Opt_staspidx (CodeSeg* S, unsigned Push, unsigned Store)
161 /* Optimize the staspidx sequence if possible */
162 {
163     unsigned I;
164     CodeEntry* N;
165     CodeEntry* X;
166     CodeEntry* PushEntry;
167     CodeEntry* StoreEntry;
168     const char* ZPLo;
169     const char* ZPHi;
170
171     /* Check if the sequence is safe. This means that there may not be any
172      * jumps between the two data points, and no usage of the stack. Handling
173      * these conditions is possible and may be done later.
174      */
175     unsigned UsedRegs = REG_NONE;
176     for (I = Push + 1; I < Store; ++I) {
177         CodeEntry* E = CS_GetEntry (S, I);
178         if ((E->Info & OF_BRA) != 0 ||
179             (E->Use & REG_SP) != 0  ||
180             CE_HasLabel (E)) {
181             /* A jump or stack pointer usage - bail out */
182             return 0;
183         }
184         UsedRegs |= (E->Use | E->Chg);
185     }
186
187     /* We prefer usage of sreg for the intermediate value, since sreg is
188      * tracked and optimized.
189      */
190     UsedRegs |= GetRegInfo (S, Push+1, REG_SREG | REG_PTR1 | REG_PTR2);
191     if ((UsedRegs & REG_SREG) == REG_NONE) {
192         /* SREG is available */
193         ZPLo = "sreg";
194         ZPHi = "sreg+1";
195     } else if ((UsedRegs & REG_PTR1) == REG_NONE) {
196         ZPLo = "ptr1";
197         ZPHi = "ptr1+1";
198     } else if ((UsedRegs & REG_PTR2) == REG_NONE) {
199         ZPLo = "ptr2";
200         ZPHi = "ptr2+1";
201     } else {
202         /* No registers available */
203         return 0;
204     }
205
206     /* We need the entry behind the store */
207     if ((N = CS_GetNextEntry (S, Store)) == 0) {
208         /* Unavailable */
209         return 0;
210     }
211
212     /* Generate register info */
213     CS_GenRegInfo (S);
214
215     /* Get the push entry */
216     PushEntry = CS_GetEntry (S, Push);
217
218     /* Store the value into sreg instead of pushing it */
219     X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
220     CS_InsertEntry (S, X, Push+1);
221     X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
222     CS_InsertEntry (S, X, Push+2);
223
224     /* Correct the index of the store and get a pointer to the entry */
225     Store += 2;
226     StoreEntry = CS_GetEntry (S, Store);
227
228     /* Inline the store */
229     X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, StoreEntry->LI);
230     CS_InsertEntry (S, X, Store+1);
231
232     /* Remove the push and the call to the staspidx function */
233     CS_DelEntry (S, Store);
234     CS_DelEntry (S, Push);
235
236     /* Free the register info */
237     CS_FreeRegInfo (S);
238
239     /* We changed the sequence */
240     return 1;
241 }
242
243
244
245 /*****************************************************************************/
246 /*                                   Code                                    */
247 /*****************************************************************************/
248
249
250
251 unsigned OptStackOps (CodeSeg* S)
252 /* Optimize operations that take operands via the stack */
253 {
254     unsigned Changes = 0;          /* Number of changes in one run */
255     int LastPush = -1;             /* Last call to pushax */
256
257     /* Walk over all entries */
258     unsigned I = 0;
259     while (I < CS_GetEntryCount (S)) {
260
261         /* Get the next entry */
262         CodeEntry* E = CS_GetEntry (S, I);
263
264         /* Check for a subroutine call */
265         if (E->OPC == OP65_JSR) {
266
267             /* We look for two things: A call to pushax, and a call to one
268              * of the known functions we're going to replace. We're only
269              * interested in the latter ones, if we had a push before.
270              */
271             if (strcmp (E->Arg, "pushax") == 0) {
272
273                 /* Just remember it */
274                 LastPush = I;
275
276             } else if (LastPush >= 0) {
277
278                 if (strcmp (E->Arg, "tosaddax") == 0) {
279                     Changes += Opt_tosaddax (S, LastPush, I);
280                     LastPush = -1;
281                 } else if (strcmp (E->Arg, "staspidx") == 0) {
282                     Changes += Opt_staspidx (S, LastPush, I);
283                     LastPush = -1;
284                 }
285
286             }
287         }
288
289         /* Next entry */
290         ++I;
291
292     }
293
294     /* Return the number of changes made */
295     return Changes;
296 }
297
298
299