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