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 ||
73 (E->Use & REG_SP) != 0 ||
75 /* A jump or stack pointer usage - bail out */
78 UsedRegs |= (E->Use | E->Chg);
81 /* We prefer usage of sreg for the intermediate value, since sreg is
82 * tracked and optimized.
84 UsedRegs |= GetRegInfo (S, Push+1, REG_ALL);
85 if ((UsedRegs & REG_SREG) == REG_NONE) {
86 /* SREG is available */
89 } else if ((UsedRegs & REG_PTR1) == REG_NONE) {
92 } else if ((UsedRegs & REG_PTR2) == REG_NONE) {
96 /* No registers available */
100 /* We need the entry behind the add */
101 if ((N = CS_GetNextEntry (S, Add)) == 0) {
106 /* Generate register info */
109 /* Get the push entry */
110 PushEntry = CS_GetEntry (S, Push);
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);
118 /* Correct the index of the add and get a pointer to the entry */
120 AddEntry = CS_GetEntry (S, 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);
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);
148 /* Remove the push and the call to the tosaddax function */
149 CS_DelEntry (S, Add);
150 CS_DelEntry (S, Push);
152 /* Free the register info */
155 /* We changed the sequence */
161 static unsigned Opt_staspidx (CodeSeg* S, unsigned Push, unsigned Store)
162 /* Optimize the staspidx sequence if possible */
167 CodeEntry* PushEntry;
168 CodeEntry* StoreEntry;
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.
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 ||
183 /* A jump or stack pointer usage - bail out */
186 UsedRegs |= (E->Use | E->Chg);
189 /* We prefer usage of sreg for the intermediate value, since sreg is
190 * tracked and optimized.
192 UsedRegs |= GetRegInfo (S, Push+1, REG_SREG | REG_PTR1 | REG_PTR2);
193 if ((UsedRegs & REG_SREG) == REG_NONE) {
194 /* SREG is available */
197 } else if ((UsedRegs & REG_PTR1) == REG_NONE) {
200 } else if ((UsedRegs & REG_PTR2) == REG_NONE) {
204 /* No registers available */
208 /* We need the entry behind the store */
209 if ((N = CS_GetNextEntry (S, Store)) == 0) {
214 /* Generate register info */
217 /* Get the push entry */
218 PushEntry = CS_GetEntry (S, Push);
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);
226 /* Correct the index of the store and get a pointer to the entry */
228 StoreEntry = CS_GetEntry (S, Store);
230 /* Inline the store */
231 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, StoreEntry->LI);
232 CS_InsertEntry (S, X, Store+1);
234 /* Remove the push and the call to the staspidx function */
235 CS_DelEntry (S, Store);
236 CS_DelEntry (S, Push);
238 /* Free the register info */
241 /* We changed the sequence */
247 /*****************************************************************************/
249 /*****************************************************************************/
253 unsigned OptStackOps (CodeSeg* S)
254 /* Optimize operations that take operands via the stack */
256 unsigned Changes = 0; /* Number of changes in one run */
257 int LastPush = -1; /* Last call to pushax */
259 /* Walk over all entries */
261 while (I < CS_GetEntryCount (S)) {
263 /* Get the next entry */
264 CodeEntry* E = CS_GetEntry (S, I);
266 /* Check for a subroutine call */
267 if (E->OPC == OP65_JSR) {
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.
273 if (strcmp (E->Arg, "pushax") == 0) {
275 /* Just remember it */
278 } else if (LastPush >= 0) {
280 if (strcmp (E->Arg, "tosaddax") == 0) {
281 Changes += Opt_tosaddax (S, LastPush, I);
283 } else if (strcmp (E->Arg, "staspidx") == 0) {
284 Changes += Opt_staspidx (S, LastPush, I);
296 /* Return the number of changes made */