1 /*****************************************************************************/
5 /* Optimize operations that take operands via the stack */
9 /* (C) 2001 Ullrich von Bassewitz */
11 /* D-70597 Stuttgart */
12 /* EMail: uz@cc65.org */
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. */
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: */
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 */
32 /*****************************************************************************/
47 /*****************************************************************************/
49 /*****************************************************************************/
53 static unsigned Opt_tosaddax (CodeSeg* S, unsigned Push, unsigned Add)
54 /* Optimize the tosaddax sequence if possible */
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.
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 ||
74 /* A jump or stack pointer usage - bail out */
77 UsedRegs |= (E->Use | E->Chg);
80 /* We prefer usage of sreg for the intermediate value, since sreg is
81 * tracked and optimized.
83 UsedRegs |= GetRegInfo (S, Push+1, REG_ALL);
84 if ((UsedRegs & REG_SREG) == REG_NONE) {
85 /* SREG is available */
88 } else if ((UsedRegs & REG_PTR1) == REG_NONE) {
91 } else if ((UsedRegs & REG_PTR2) == REG_NONE) {
95 /* No registers available */
99 /* We need the entry behind the add */
100 if ((N = CS_GetNextEntry (S, Add)) == 0) {
105 /* Generate register info */
108 /* Get the push entry */
109 PushEntry = CS_GetEntry (S, Push);
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);
117 /* Correct the index of the add and get a pointer to the entry */
119 AddEntry = CS_GetEntry (S, 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);
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);
147 /* Remove the push and the call to the tosaddax function */
148 CS_DelEntry (S, Add);
149 CS_DelEntry (S, Push);
151 /* Free the register info */
154 /* We changed the sequence */
160 static unsigned Opt_staspidx (CodeSeg* S, unsigned Push, unsigned Store)
161 /* Optimize the staspidx sequence if possible */
166 CodeEntry* PushEntry;
167 CodeEntry* StoreEntry;
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.
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 ||
181 /* A jump or stack pointer usage - bail out */
184 UsedRegs |= (E->Use | E->Chg);
187 /* We prefer usage of sreg for the intermediate value, since sreg is
188 * tracked and optimized.
190 UsedRegs |= GetRegInfo (S, Push+1, REG_SREG | REG_PTR1 | REG_PTR2);
191 if ((UsedRegs & REG_SREG) == REG_NONE) {
192 /* SREG is available */
195 } else if ((UsedRegs & REG_PTR1) == REG_NONE) {
198 } else if ((UsedRegs & REG_PTR2) == REG_NONE) {
202 /* No registers available */
206 /* We need the entry behind the store */
207 if ((N = CS_GetNextEntry (S, Store)) == 0) {
212 /* Generate register info */
215 /* Get the push entry */
216 PushEntry = CS_GetEntry (S, Push);
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);
224 /* Correct the index of the store and get a pointer to the entry */
226 StoreEntry = CS_GetEntry (S, Store);
228 /* Inline the store */
229 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, StoreEntry->LI);
230 CS_InsertEntry (S, X, Store+1);
232 /* Remove the push and the call to the staspidx function */
233 CS_DelEntry (S, Store);
234 CS_DelEntry (S, Push);
236 /* Free the register info */
239 /* We changed the sequence */
245 /*****************************************************************************/
247 /*****************************************************************************/
251 unsigned OptStackOps (CodeSeg* S)
252 /* Optimize operations that take operands via the stack */
254 unsigned Changes = 0; /* Number of changes in one run */
255 int LastPush = -1; /* Last call to pushax */
257 /* Walk over all entries */
259 while (I < CS_GetEntryCount (S)) {
261 /* Get the next entry */
262 CodeEntry* E = CS_GetEntry (S, I);
264 /* Check for a subroutine call */
265 if (E->OPC == OP65_JSR) {
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.
271 if (strcmp (E->Arg, "pushax") == 0) {
273 /* Just remember it */
276 } else if (LastPush >= 0) {
278 if (strcmp (E->Arg, "tosaddax") == 0) {
279 Changes += Opt_tosaddax (S, LastPush, I);
281 } else if (strcmp (E->Arg, "staspidx") == 0) {
282 Changes += Opt_staspidx (S, LastPush, I);
294 /* Return the number of changes made */