1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* coptstore.c */ |
4 | /* */ |
5 | /* Optimize stores */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 2002-2012, Ullrich von Bassewitz */ |
10 | /* Roemerstrasse 52 */ |
11 | /* D-70794 Filderstadt */ |
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 | /* cc65 */ |
37 | #include "codeent.h" |
38 | #include "codeinfo.h" |
39 | #include "coptstore.h" |
40 | |
41 | |
42 | |
43 | /*****************************************************************************/ |
44 | /* Code */ |
45 | /*****************************************************************************/ |
46 | |
47 | |
48 | |
49 | static void InsertStore (CodeSeg* S, unsigned* IP, LineInfo* LI) |
50 | { |
51 | CodeEntry* X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "sp" , 0, LI); |
52 | CS_InsertEntry (S, X, (*IP)++); |
53 | } |
54 | |
55 | |
56 | |
57 | unsigned OptStore1 (CodeSeg* S) |
58 | /* Search for the sequence |
59 | ** |
60 | ** ldy #n |
61 | ** jsr staxysp |
62 | ** ldy #n+1 |
63 | ** jsr ldaxysp |
64 | ** |
65 | ** and remove the useless load. |
66 | */ |
67 | { |
68 | unsigned Changes = 0; |
69 | |
70 | /* Walk over the entries */ |
71 | unsigned I = 0; |
72 | while (I < CS_GetEntryCount (S)) { |
73 | |
74 | CodeEntry* L[4]; |
75 | |
76 | /* Get next entry */ |
77 | L[0] = CS_GetEntry (S, I); |
78 | |
79 | /* Check for the sequence */ |
80 | if (L[0]->OPC == OP65_LDY && |
81 | CE_IsConstImm (L[0]) && |
82 | L[0]->Num < 0xFF && |
83 | !CS_RangeHasLabel (S, I+1, 3) && |
84 | CS_GetEntries (S, L+1, I+1, 3) && |
85 | CE_IsCallTo (L[1], "staxysp" ) && |
86 | L[2]->OPC == OP65_LDY && |
87 | CE_IsKnownImm (L[2], L[0]->Num + 1) && |
88 | CE_IsCallTo (L[3], "ldaxysp" )) { |
89 | |
90 | /* Register has already the correct value, remove the loads */ |
91 | CS_DelEntries (S, I+2, 2); |
92 | |
93 | /* Remember, we had changes */ |
94 | ++Changes; |
95 | |
96 | } |
97 | |
98 | /* Next entry */ |
99 | ++I; |
100 | |
101 | } |
102 | |
103 | /* Return the number of changes made */ |
104 | return Changes; |
105 | } |
106 | |
107 | |
108 | |
109 | unsigned OptStore2 (CodeSeg* S) |
110 | /* Search for a call to staxysp. If the ax register is not used later, and |
111 | ** the value is constant, just use the A register and store directly into the |
112 | ** stack. |
113 | */ |
114 | { |
115 | unsigned I; |
116 | unsigned Changes = 0; |
117 | |
118 | /* Walk over the entries */ |
119 | I = 0; |
120 | while (I < CS_GetEntryCount (S)) { |
121 | |
122 | /* Get next entry */ |
123 | CodeEntry* E = CS_GetEntry (S, I); |
124 | |
125 | /* Get the input registers */ |
126 | const RegInfo* RI = E->RI; |
127 | |
128 | /* Check for the call */ |
129 | if (CE_IsCallTo (E, "staxysp" ) && |
130 | RegValIsKnown (RI->In.RegA) && |
131 | RegValIsKnown (RI->In.RegX) && |
132 | RegValIsKnown (RI->In.RegY) && |
133 | !RegAXUsed (S, I+1)) { |
134 | |
135 | /* Get the register values */ |
136 | unsigned char A = (unsigned char) RI->In.RegA; |
137 | unsigned char X = (unsigned char) RI->In.RegX; |
138 | unsigned char Y = (unsigned char) RI->In.RegY; |
139 | |
140 | /* Setup other variables */ |
141 | CodeEntry* N; |
142 | unsigned IP = I + 1; /* Insertion point */ |
143 | |
144 | /* Replace the store. We will not remove the loads, since this is |
145 | ** too complex and will be done by other optimizer steps. |
146 | */ |
147 | N = NewCodeEntry (OP65_LDA, AM65_IMM, MakeHexArg (A), 0, E->LI); |
148 | CS_InsertEntry (S, N, IP++); |
149 | InsertStore (S, &IP, E->LI); |
150 | |
151 | /* Check if we can store one of the other bytes */ |
152 | if (A != X) { |
153 | N = NewCodeEntry (OP65_LDA, AM65_IMM, MakeHexArg (X), 0, E->LI); |
154 | CS_InsertEntry (S, N, IP++); |
155 | } |
156 | N = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (Y+1), 0, E->LI); |
157 | CS_InsertEntry (S, N, IP++); |
158 | InsertStore (S, &IP, E->LI); |
159 | |
160 | /* Remove the call */ |
161 | CS_DelEntry (S, I); |
162 | |
163 | /* Remember, we had changes */ |
164 | ++Changes; |
165 | |
166 | } |
167 | |
168 | /* Next entry */ |
169 | ++I; |
170 | |
171 | } |
172 | |
173 | /* Return the number of changes made */ |
174 | return Changes; |
175 | } |
176 | |
177 | |
178 | |
179 | unsigned OptStore3 (CodeSeg* S) |
180 | /* Search for a call to steaxysp. If the eax register is not used later, and |
181 | ** the value is constant, just use the A register and store directly into the |
182 | ** stack. |
183 | */ |
184 | { |
185 | unsigned I; |
186 | unsigned Changes = 0; |
187 | |
188 | /* Walk over the entries */ |
189 | I = 0; |
190 | while (I < CS_GetEntryCount (S)) { |
191 | |
192 | /* Get next entry */ |
193 | CodeEntry* E = CS_GetEntry (S, I); |
194 | |
195 | /* Get the input registers */ |
196 | const RegInfo* RI = E->RI; |
197 | |
198 | /* Check for the call */ |
199 | if (CE_IsCallTo (E, "steaxysp" ) && |
200 | RegValIsKnown (RI->In.RegA) && |
201 | RegValIsKnown (RI->In.RegX) && |
202 | RegValIsKnown (RI->In.RegY) && |
203 | RegValIsKnown (RI->In.SRegLo) && |
204 | RegValIsKnown (RI->In.SRegHi) && |
205 | !RegEAXUsed (S, I+1)) { |
206 | |
207 | /* Get the register values */ |
208 | unsigned char A = (unsigned char) RI->In.RegA; |
209 | unsigned char X = (unsigned char) RI->In.RegX; |
210 | unsigned char Y = (unsigned char) RI->In.RegY; |
211 | unsigned char L = (unsigned char) RI->In.SRegLo; |
212 | unsigned char H = (unsigned char) RI->In.SRegHi; |
213 | |
214 | /* Setup other variables */ |
215 | unsigned Done = 0; |
216 | CodeEntry* N; |
217 | unsigned IP = I + 1; /* Insertion point */ |
218 | |
219 | /* Replace the store. We will not remove the loads, since this is |
220 | ** too complex and will be done by other optimizer steps. |
221 | */ |
222 | N = NewCodeEntry (OP65_LDA, AM65_IMM, MakeHexArg (A), 0, E->LI); |
223 | CS_InsertEntry (S, N, IP++); |
224 | InsertStore (S, &IP, E->LI); |
225 | Done |= 0x01; |
226 | |
227 | /* Check if we can store one of the other bytes */ |
228 | if (A == X && (Done & 0x02) == 0) { |
229 | N = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (Y+1), 0, E->LI); |
230 | CS_InsertEntry (S, N, IP++); |
231 | InsertStore (S, &IP, E->LI); |
232 | Done |= 0x02; |
233 | } |
234 | if (A == L && (Done & 0x04) == 0) { |
235 | N = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (Y+2), 0, E->LI); |
236 | CS_InsertEntry (S, N, IP++); |
237 | InsertStore (S, &IP, E->LI); |
238 | Done |= 0x04; |
239 | } |
240 | if (A == H && (Done & 0x08) == 0) { |
241 | N = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (Y+3), 0, E->LI); |
242 | CS_InsertEntry (S, N, IP++); |
243 | InsertStore (S, &IP, E->LI); |
244 | Done |= 0x08; |
245 | } |
246 | |
247 | /* Store the second byte */ |
248 | if ((Done & 0x02) == 0) { |
249 | N = NewCodeEntry (OP65_LDA, AM65_IMM, MakeHexArg (X), 0, E->LI); |
250 | CS_InsertEntry (S, N, IP++); |
251 | N = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (Y+1), 0, E->LI); |
252 | CS_InsertEntry (S, N, IP++); |
253 | InsertStore (S, &IP, E->LI); |
254 | Done |= 0x02; |
255 | } |
256 | |
257 | /* Check if we can store one of the other bytes */ |
258 | if (X == L && (Done & 0x04) == 0) { |
259 | N = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (Y+2), 0, E->LI); |
260 | CS_InsertEntry (S, N, IP++); |
261 | InsertStore (S, &IP, E->LI); |
262 | Done |= 0x04; |
263 | } |
264 | if (X == H && (Done & 0x08) == 0) { |
265 | N = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (Y+3), 0, E->LI); |
266 | CS_InsertEntry (S, N, IP++); |
267 | InsertStore (S, &IP, E->LI); |
268 | Done |= 0x08; |
269 | } |
270 | |
271 | /* Store the third byte */ |
272 | if ((Done & 0x04) == 0) { |
273 | N = NewCodeEntry (OP65_LDA, AM65_IMM, MakeHexArg (L), 0, E->LI); |
274 | CS_InsertEntry (S, N, IP++); |
275 | N = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (Y+2), 0, E->LI); |
276 | CS_InsertEntry (S, N, IP++); |
277 | InsertStore (S, &IP, E->LI); |
278 | Done |= 0x04; |
279 | } |
280 | |
281 | /* Check if we can store one of the other bytes */ |
282 | if (L == H && (Done & 0x08) == 0) { |
283 | N = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (Y+3), 0, E->LI); |
284 | CS_InsertEntry (S, N, IP++); |
285 | InsertStore (S, &IP, E->LI); |
286 | Done |= 0x08; |
287 | } |
288 | |
289 | /* Store the fourth byte */ |
290 | if ((Done & 0x08) == 0) { |
291 | N = NewCodeEntry (OP65_LDA, AM65_IMM, MakeHexArg (H), 0, E->LI); |
292 | CS_InsertEntry (S, N, IP++); |
293 | N = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (Y+3), 0, E->LI); |
294 | CS_InsertEntry (S, N, IP++); |
295 | InsertStore (S, &IP, E->LI); |
296 | Done |= 0x08; |
297 | } |
298 | |
299 | /* Remove the call */ |
300 | CS_DelEntry (S, I); |
301 | |
302 | /* Remember, we had changes */ |
303 | ++Changes; |
304 | |
305 | } |
306 | |
307 | /* Next entry */ |
308 | ++I; |
309 | |
310 | } |
311 | |
312 | /* Return the number of changes made */ |
313 | return Changes; |
314 | } |
315 | |
316 | |
317 | |
318 | unsigned OptStore4 (CodeSeg* S) |
319 | /* Search for the sequence |
320 | ** |
321 | ** sta xx |
322 | ** stx yy |
323 | ** lda xx |
324 | ** ldx yy |
325 | ** |
326 | ** and remove the useless load, provided that the next insn doesn't use flags |
327 | ** from the load. |
328 | */ |
329 | { |
330 | unsigned Changes = 0; |
331 | |
332 | /* Walk over the entries */ |
333 | unsigned I = 0; |
334 | while (I < CS_GetEntryCount (S)) { |
335 | |
336 | CodeEntry* L[5]; |
337 | |
338 | /* Get next entry */ |
339 | L[0] = CS_GetEntry (S, I); |
340 | |
341 | /* Check for the sequence */ |
342 | if (L[0]->OPC == OP65_STA && |
343 | (L[0]->AM == AM65_ABS || L[0]->AM == AM65_ZP) && |
344 | !CS_RangeHasLabel (S, I+1, 3) && |
345 | CS_GetEntries (S, L+1, I+1, 4) && |
346 | L[1]->OPC == OP65_STX && |
347 | L[1]->AM == L[0]->AM && |
348 | L[2]->OPC == OP65_LDA && |
349 | L[2]->AM == L[0]->AM && |
350 | L[3]->OPC == OP65_LDX && |
351 | L[3]->AM == L[1]->AM && |
352 | strcmp (L[0]->Arg, L[2]->Arg) == 0 && |
353 | strcmp (L[1]->Arg, L[3]->Arg) == 0 && |
354 | !CE_UseLoadFlags (L[4])) { |
355 | |
356 | /* Register has already the correct value, remove the loads */ |
357 | CS_DelEntries (S, I+2, 2); |
358 | |
359 | /* Remember, we had changes */ |
360 | ++Changes; |
361 | |
362 | } |
363 | |
364 | /* Next entry */ |
365 | ++I; |
366 | |
367 | } |
368 | |
369 | /* Return the number of changes made */ |
370 | return Changes; |
371 | } |
372 | |
373 | |
374 | |
375 | unsigned OptStore5 (CodeSeg* S) |
376 | /* Search for the sequence |
377 | ** |
378 | ** lda foo |
379 | ** ldx bar |
380 | ** sta something |
381 | ** stx something-else |
382 | ** |
383 | ** and, replace it by |
384 | ** |
385 | ** lda bar |
386 | ** sta something-else |
387 | ** lda foo |
388 | ** sta something |
389 | ** |
390 | ** if the new value of X is not used later. That replacement doesn't save any |
391 | ** cycles or bytes; but, it keeps the old value of X, which may be reused later. |
392 | */ |
393 | { |
394 | unsigned Changes = 0; |
395 | unsigned I = 0; |
396 | |
397 | /* Walk over the entries */ |
398 | while (I < CS_GetEntryCount (S)) { |
399 | CodeEntry* L[4]; |
400 | |
401 | /* Get next entry */ |
402 | L[0] = CS_GetEntry (S, I); |
403 | |
404 | /* Check for the sequence */ |
405 | if (L[0]->OPC == OP65_LDA && |
406 | !CS_RangeHasLabel (S, I+1, 3) && |
407 | CS_GetEntries (S, L+1, I+1, 3) && |
408 | L[1]->OPC == OP65_LDX && |
409 | L[2]->OPC == OP65_STA && |
410 | L[3]->OPC == OP65_STX && |
411 | !RegXUsed (S, I+4)) { |
412 | |
413 | CodeEntry* E = CS_GetEntry (S, I+1); |
414 | |
415 | /* Move the high-byte code to the beginning of the sequence */ |
416 | CS_MoveEntry (S, I+1, I); |
417 | CS_MoveEntry (S, I+3, I+1); |
418 | |
419 | /* Change from the X register to the A register */ |
420 | CE_ReplaceOPC (E, OP65_LDA); |
421 | CE_ReplaceOPC (CS_GetEntry (S, I+1), OP65_STA); |
422 | |
423 | /* Move labels from the old first entry to the new first entry */ |
424 | CS_MoveLabels (S, CS_GetEntry (S, I+2), E); |
425 | |
426 | /* Remember, we had changes */ |
427 | ++Changes; |
428 | } |
429 | |
430 | /* Next entry */ |
431 | ++I; |
432 | } |
433 | |
434 | /* Return the number of changes made */ |
435 | return Changes; |
436 | } |
437 | |