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 /*****************************************************************************/
50 /*****************************************************************************/
52 /*****************************************************************************/
56 static unsigned Opt_staspidx (CodeSeg* S, unsigned Push, unsigned Store,
57 const char* ZPLo, const char* ZPHi)
58 /* Optimize the staspidx sequence if possible */
62 CodeEntry* StoreEntry;
64 /* Generate register info */
67 /* Get the push entry */
68 PushEntry = CS_GetEntry (S, Push);
70 /* Store the value into the zeropage instead of pushing it */
71 X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
72 CS_InsertEntry (S, X, Push+1);
73 X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
74 CS_InsertEntry (S, X, Push+2);
76 /* Correct the index of the store and get a pointer to the entry */
78 StoreEntry = CS_GetEntry (S, Store);
80 /* Inline the store */
81 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, StoreEntry->LI);
82 CS_InsertEntry (S, X, Store+1);
84 /* Remove the push and the call to the staspidx function */
85 CS_DelEntry (S, Store);
86 CS_DelEntry (S, Push);
88 /* Free the register info */
91 /* We changed the sequence */
97 static unsigned Opt_tosaddax (CodeSeg* S, unsigned Push, unsigned Add,
98 const char* ZPLo, const char* ZPHi)
99 /* Optimize the tosaddax sequence if possible */
103 CodeEntry* PushEntry;
106 /* We need the entry behind the add */
107 if ((N = CS_GetNextEntry (S, Add)) == 0) {
112 /* Generate register info */
115 /* Get the push entry */
116 PushEntry = CS_GetEntry (S, Push);
118 /* Store the value into the zeropage instead of pushing it */
119 X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
120 CS_InsertEntry (S, X, Push+1);
121 X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
122 CS_InsertEntry (S, X, Push+2);
124 /* Correct the index of the add and get a pointer to the entry */
126 AddEntry = CS_GetEntry (S, Add);
129 X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, AddEntry->LI);
130 CS_InsertEntry (S, X, Add+1);
131 X = NewCodeEntry (OP65_ADC, AM65_ZP, ZPLo, 0, AddEntry->LI);
132 CS_InsertEntry (S, X, Add+2);
133 if (PushEntry->RI->In.RegX == 0 && AddEntry->RI->In.RegX == 0) {
134 /* The high byte is zero on entry */
135 CodeLabel* L = CS_GenLabel (S, N);
136 X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, AddEntry->LI);
137 CS_InsertEntry (S, X, Add+3);
138 X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, AddEntry->LI);
139 CS_InsertEntry (S, X, Add+4);
141 /* High byte is unknown */
142 X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, AddEntry->LI);
143 CS_InsertEntry (S, X, Add+3);
144 X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, AddEntry->LI);
145 CS_InsertEntry (S, X, Add+4);
146 X = NewCodeEntry (OP65_ADC, AM65_ZP, ZPHi, 0, AddEntry->LI);
147 CS_InsertEntry (S, X, Add+5);
148 X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, AddEntry->LI);
149 CS_InsertEntry (S, X, Add+6);
150 X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, AddEntry->LI);
151 CS_InsertEntry (S, X, Add+7);
154 /* Remove the push and the call to the tosaddax function */
155 CS_DelEntry (S, Add);
156 CS_DelEntry (S, Push);
158 /* Free the register info */
161 /* We changed the sequence */
167 static unsigned Opt_tosandax (CodeSeg* S, unsigned Push, unsigned And,
168 const char* ZPLo, const char* ZPHi)
169 /* Optimize the tosandax sequence if possible */
172 CodeEntry* PushEntry;
175 /* Generate register info */
178 /* Get the push entry */
179 PushEntry = CS_GetEntry (S, Push);
181 /* Store the value into the zeropage instead of pushing it */
182 X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
183 CS_InsertEntry (S, X, Push+1);
184 X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
185 CS_InsertEntry (S, X, Push+2);
187 /* Correct the index of the add and get a pointer to the entry */
189 AndEntry = CS_GetEntry (S, And);
192 X = NewCodeEntry (OP65_AND, AM65_ZP, ZPLo, 0, AndEntry->LI);
193 CS_InsertEntry (S, X, And+1);
194 if (PushEntry->RI->In.RegX == 0 || AndEntry->RI->In.RegX == 0) {
195 /* The high byte is zero */
196 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, AndEntry->LI);
197 CS_InsertEntry (S, X, And+2);
199 /* High byte is unknown */
200 X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, AndEntry->LI);
201 CS_InsertEntry (S, X, And+2);
202 X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, AndEntry->LI);
203 CS_InsertEntry (S, X, And+3);
204 X = NewCodeEntry (OP65_AND, AM65_ZP, ZPHi, 0, AndEntry->LI);
205 CS_InsertEntry (S, X, And+4);
206 X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, AndEntry->LI);
207 CS_InsertEntry (S, X, And+5);
208 X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, AndEntry->LI);
209 CS_InsertEntry (S, X, And+6);
212 /* Remove the push and the call to the tosandax function */
213 CS_DelEntry (S, And);
214 CS_DelEntry (S, Push);
216 /* Free the register info */
219 /* We changed the sequence */
225 static unsigned Opt_tosorax (CodeSeg* S, unsigned Push, unsigned Or,
226 const char* ZPLo, const char* ZPHi)
227 /* Optimize the tosorax sequence if possible */
230 CodeEntry* PushEntry;
233 /* Generate register info */
236 /* Get the push entry */
237 PushEntry = CS_GetEntry (S, Push);
239 /* Store the value into the zeropage instead of pushing it */
240 X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
241 CS_InsertEntry (S, X, Push+1);
242 X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
243 CS_InsertEntry (S, X, Push+2);
245 /* Correct the index of the add and get a pointer to the entry */
247 OrEntry = CS_GetEntry (S, Or);
250 X = NewCodeEntry (OP65_ORA, AM65_ZP, ZPLo, 0, OrEntry->LI);
251 CS_InsertEntry (S, X, Or+1);
252 if (PushEntry->RI->In.RegX >= 0 && OrEntry->RI->In.RegX == 0) {
253 /* Value of X will be that of the first operand */
255 xsprintf (Buf, sizeof (Buf), "$%02X", PushEntry->RI->In.RegX);
256 X = NewCodeEntry (OP65_LDX, AM65_IMM, Buf, 0, OrEntry->LI);
257 CS_InsertEntry (S, X, Or+2);
258 } else if (PushEntry->RI->In.RegX != 0) {
259 /* High byte is unknown */
260 X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, OrEntry->LI);
261 CS_InsertEntry (S, X, Or+2);
262 X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, OrEntry->LI);
263 CS_InsertEntry (S, X, Or+3);
264 X = NewCodeEntry (OP65_ORA, AM65_ZP, ZPHi, 0, OrEntry->LI);
265 CS_InsertEntry (S, X, Or+4);
266 X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, OrEntry->LI);
267 CS_InsertEntry (S, X, Or+5);
268 X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, OrEntry->LI);
269 CS_InsertEntry (S, X, Or+6);
272 /* Remove the push and the call to the tosandax function */
274 CS_DelEntry (S, Push);
276 /* Free the register info */
279 /* We changed the sequence */
285 /*****************************************************************************/
287 /*****************************************************************************/
291 typedef unsigned (*OptFunc) (CodeSeg* S, unsigned Push, unsigned Store,
292 const char* ZPLo, const char* ZPHi);
293 typedef struct OptFuncDesc OptFuncDesc;
295 const char* Name; /* Name of the replaced runtime function */
296 OptFunc Func; /* Function pointer */
299 static const OptFuncDesc FuncTable[] = {
300 { "staspidx", Opt_staspidx },
301 { "tosaddax", Opt_tosaddax },
302 { "tosandax", Opt_tosandax },
303 { "tosorax", Opt_tosorax },
305 #define FUNC_COUNT (sizeof(FuncTable) / sizeof(FuncTable[0]))
309 static int CmpFunc (const void* Key, const void* Func)
310 /* Compare function for bsearch */
312 return strcmp (Key, ((const OptFuncDesc*) Func)->Name);
317 static const OptFuncDesc* FindFunc (const char* Name)
318 /* Find the function with the given name. Return a pointer to the table entry
319 * or NULL if the function was not found.
322 return bsearch (Name, FuncTable, FUNC_COUNT, sizeof(OptFuncDesc), CmpFunc);
327 /*****************************************************************************/
329 /*****************************************************************************/
333 unsigned OptStackOps (CodeSeg* S)
334 /* Optimize operations that take operands via the stack */
336 unsigned Changes = 0; /* Number of changes in one run */
337 int InSeq = 0; /* Inside a sequence */
338 unsigned Push = 0; /* Index of pushax */
339 unsigned UsedRegs = 0; /* Zeropage registers used in sequence */
342 /* Look for a call to pushax followed by a call to some other function
343 * that takes it's first argument on the stack, and the second argument
344 * in the primary register.
345 * It depends on the code between the two if we can handle/transform the
346 * sequence, so check this code for the following list of things:
348 * - there must not be a jump or conditional branch (this may
349 * get relaxed later).
350 * - there may not be accesses to local variables (may also be
352 * - no subroutine calls
355 * Since we need a zero page register later, do also check the
356 * intermediate code for zero page use.
359 while (I < CS_GetEntryCount (S)) {
361 /* Get the next entry */
362 CodeEntry* E = CS_GetEntry (S, I);
364 /* Handling depends if we're inside a sequence or not */
367 /* Subroutine call? */
368 if (E->OPC == OP65_JSR) {
370 /* Check if this is one of our functions */
371 const OptFuncDesc* F = FindFunc (E->Arg);
374 /* Determine the register to use */
377 UsedRegs |= GetRegInfo (S, I+1, REG_SREG | REG_PTR1 | REG_PTR2);
378 if ((UsedRegs & REG_SREG) == REG_NONE) {
379 /* SREG is available */
382 } else if ((UsedRegs & REG_PTR1) == REG_NONE) {
385 } else if ((UsedRegs & REG_PTR2) == REG_NONE) {
389 /* No registers available */
394 /* If we have a register, call the optimizer function */
396 Changes += F->Func (S, Push, I, ZPLo, ZPHi);
399 /* End of sequence */
402 } else if (strcmp (E->Arg, "pushax") == 0) {
403 /* Restart the sequence */
407 /* A call to an unkown subroutine ends the sequence */
411 } else if ((E->Info & OF_BRA) != 0 ||
412 (E->Use & REG_SP) != 0 ||
415 /* All this stuff is not allowed in a sequence */
420 /* Other stuff: Track zeropage register usage */
421 UsedRegs |= (E->Use | E->Chg);
425 } else if (CE_IsCall (E, "pushax")) {
427 /* This starts a sequence */
439 /* Return the number of changes made */