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
49static 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
57unsigned 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
109unsigned 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
179unsigned 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
318unsigned 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
375unsigned 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