1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* coptadd.c */ |
4 | /* */ |
5 | /* Optimize addition sequences */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 2001-2005, Ullrich von Bassewitz */ |
10 | /* Römerstrasse 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 | /* common */ |
37 | #include "chartype.h" |
38 | |
39 | /* cc65 */ |
40 | #include "codeent.h" |
41 | #include "codeinfo.h" |
42 | #include "coptadd.h" |
43 | |
44 | |
45 | |
46 | /*****************************************************************************/ |
47 | /* Optimize additions */ |
48 | /*****************************************************************************/ |
49 | |
50 | |
51 | |
52 | unsigned OptAdd1 (CodeSeg* S) |
53 | /* Search for the sequence |
54 | ** |
55 | ** ldy #xx |
56 | ** jsr ldaxysp |
57 | ** jsr pushax |
58 | ** ldy #yy |
59 | ** jsr ldaxysp |
60 | ** jsr tosaddax |
61 | ** |
62 | ** and replace it by: |
63 | ** |
64 | ** ldy #xx-1 |
65 | ** lda (sp),y |
66 | ** ldy #yy-3 |
67 | ** clc |
68 | ** adc (sp),y |
69 | ** pha |
70 | ** ldy #xx |
71 | ** lda (sp),y |
72 | ** ldy #yy-2 |
73 | ** adc (sp),y |
74 | ** tax |
75 | ** pla |
76 | */ |
77 | { |
78 | unsigned Changes = 0; |
79 | |
80 | /* Walk over the entries */ |
81 | unsigned I = 0; |
82 | while (I < CS_GetEntryCount (S)) { |
83 | |
84 | CodeEntry* L[6]; |
85 | |
86 | /* Get next entry */ |
87 | L[0] = CS_GetEntry (S, I); |
88 | |
89 | /* Check for the sequence */ |
90 | if (L[0]->OPC == OP65_LDY && |
91 | CE_IsConstImm (L[0]) && |
92 | !CS_RangeHasLabel (S, I+1, 5) && |
93 | CS_GetEntries (S, L+1, I+1, 5) && |
94 | CE_IsCallTo (L[1], "ldaxysp" ) && |
95 | CE_IsCallTo (L[2], "pushax" ) && |
96 | L[3]->OPC == OP65_LDY && |
97 | CE_IsConstImm (L[3]) && |
98 | CE_IsCallTo (L[4], "ldaxysp" ) && |
99 | CE_IsCallTo (L[5], "tosaddax" )) { |
100 | |
101 | CodeEntry* X; |
102 | const char* Arg; |
103 | |
104 | /* Correct the stack of the first Y register load */ |
105 | CE_SetNumArg (L[0], L[0]->Num - 1); |
106 | |
107 | /* lda (sp),y */ |
108 | X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp" , 0, L[1]->LI); |
109 | CS_InsertEntry (S, X, I+1); |
110 | |
111 | /* ldy #yy-3 */ |
112 | Arg = MakeHexArg (L[3]->Num - 3); |
113 | X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[4]->LI); |
114 | CS_InsertEntry (S, X, I+2); |
115 | |
116 | /* clc */ |
117 | X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, L[5]->LI); |
118 | CS_InsertEntry (S, X, I+3); |
119 | |
120 | /* adc (sp),y */ |
121 | X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp" , 0, L[5]->LI); |
122 | CS_InsertEntry (S, X, I+4); |
123 | |
124 | /* pha */ |
125 | X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, L[5]->LI); |
126 | CS_InsertEntry (S, X, I+5); |
127 | |
128 | /* ldy #xx (beware: L[0] has changed) */ |
129 | Arg = MakeHexArg (L[0]->Num + 1); |
130 | X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[1]->LI); |
131 | CS_InsertEntry (S, X, I+6); |
132 | |
133 | /* lda (sp),y */ |
134 | X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp" , 0, L[1]->LI); |
135 | CS_InsertEntry (S, X, I+7); |
136 | |
137 | /* ldy #yy-2 */ |
138 | Arg = MakeHexArg (L[3]->Num - 2); |
139 | X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[4]->LI); |
140 | CS_InsertEntry (S, X, I+8); |
141 | |
142 | /* adc (sp),y */ |
143 | X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp" , 0, L[5]->LI); |
144 | CS_InsertEntry (S, X, I+9); |
145 | |
146 | /* tax */ |
147 | X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[5]->LI); |
148 | CS_InsertEntry (S, X, I+10); |
149 | |
150 | /* pla */ |
151 | X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, L[5]->LI); |
152 | CS_InsertEntry (S, X, I+11); |
153 | |
154 | /* Delete the old code */ |
155 | CS_DelEntries (S, I+12, 5); |
156 | |
157 | /* Remember, we had changes */ |
158 | ++Changes; |
159 | |
160 | } |
161 | |
162 | /* Next entry */ |
163 | ++I; |
164 | |
165 | } |
166 | |
167 | /* Return the number of changes made */ |
168 | return Changes; |
169 | } |
170 | |
171 | |
172 | |
173 | unsigned OptAdd2 (CodeSeg* S) |
174 | /* Search for the sequence |
175 | ** |
176 | ** ldy #xx |
177 | ** jsr ldaxysp |
178 | ** ldy #yy |
179 | ** jsr addeqysp |
180 | ** |
181 | ** and replace it by: |
182 | ** |
183 | ** ldy #xx-1 |
184 | ** lda (sp),y |
185 | ** ldy #yy |
186 | ** clc |
187 | ** adc (sp),y |
188 | ** sta (sp),y |
189 | ** ldy #xx |
190 | ** lda (sp),y |
191 | ** ldy #yy+1 |
192 | ** adc (sp),y |
193 | ** sta (sp),y |
194 | ** |
195 | ** provided that a/x is not used later. |
196 | */ |
197 | { |
198 | unsigned Changes = 0; |
199 | |
200 | /* Walk over the entries */ |
201 | unsigned I = 0; |
202 | while (I < CS_GetEntryCount (S)) { |
203 | |
204 | CodeEntry* L[4]; |
205 | |
206 | /* Get next entry */ |
207 | L[0] = CS_GetEntry (S, I); |
208 | |
209 | /* Check for the sequence */ |
210 | if (L[0]->OPC == OP65_LDY && |
211 | CE_IsConstImm (L[0]) && |
212 | !CS_RangeHasLabel (S, I+1, 3) && |
213 | CS_GetEntries (S, L+1, I+1, 3) && |
214 | CE_IsCallTo (L[1], "ldaxysp" ) && |
215 | L[2]->OPC == OP65_LDY && |
216 | CE_IsConstImm (L[2]) && |
217 | CE_IsCallTo (L[3], "addeqysp" ) && |
218 | (GetRegInfo (S, I+4, REG_AX) & REG_AX) == 0) { |
219 | |
220 | /* Insert new code behind the addeqysp */ |
221 | const char* Arg; |
222 | CodeEntry* X; |
223 | |
224 | /* ldy #xx-1 */ |
225 | Arg = MakeHexArg (L[0]->Num-1); |
226 | X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[0]->LI); |
227 | CS_InsertEntry (S, X, I+4); |
228 | |
229 | /* lda (sp),y */ |
230 | X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp" , 0, L[1]->LI); |
231 | CS_InsertEntry (S, X, I+5); |
232 | |
233 | /* ldy #yy */ |
234 | X = NewCodeEntry (OP65_LDY, AM65_IMM, L[2]->Arg, 0, L[2]->LI); |
235 | CS_InsertEntry (S, X, I+6); |
236 | |
237 | /* clc */ |
238 | X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, L[3]->LI); |
239 | CS_InsertEntry (S, X, I+7); |
240 | |
241 | /* adc (sp),y */ |
242 | X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp" , 0, L[3]->LI); |
243 | CS_InsertEntry (S, X, I+8); |
244 | |
245 | /* sta (sp),y */ |
246 | X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "sp" , 0, L[3]->LI); |
247 | CS_InsertEntry (S, X, I+9); |
248 | |
249 | /* ldy #xx */ |
250 | X = NewCodeEntry (OP65_LDY, AM65_IMM, L[0]->Arg, 0, L[0]->LI); |
251 | CS_InsertEntry (S, X, I+10); |
252 | |
253 | /* lda (sp),y */ |
254 | X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp" , 0, L[1]->LI); |
255 | CS_InsertEntry (S, X, I+11); |
256 | |
257 | /* ldy #yy+1 */ |
258 | Arg = MakeHexArg (L[2]->Num+1); |
259 | X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[2]->LI); |
260 | CS_InsertEntry (S, X, I+12); |
261 | |
262 | /* adc (sp),y */ |
263 | X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp" , 0, L[3]->LI); |
264 | CS_InsertEntry (S, X, I+13); |
265 | |
266 | /* sta (sp),y */ |
267 | X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "sp" , 0, L[3]->LI); |
268 | CS_InsertEntry (S, X, I+14); |
269 | |
270 | /* Delete the old code */ |
271 | CS_DelEntries (S, I, 4); |
272 | |
273 | /* Remember, we had changes */ |
274 | ++Changes; |
275 | |
276 | } |
277 | |
278 | /* Next entry */ |
279 | ++I; |
280 | |
281 | } |
282 | |
283 | /* Return the number of changes made */ |
284 | return Changes; |
285 | } |
286 | |
287 | |
288 | |
289 | unsigned OptAdd3 (CodeSeg* S) |
290 | /* Search for the sequence |
291 | ** |
292 | ** jsr pushax |
293 | ** ldx #$00 |
294 | ** lda xxx |
295 | ** jsr tosaddax |
296 | ** |
297 | ** and replace it by |
298 | ** |
299 | ** clc |
300 | ** adc xxx |
301 | ** bcc L1 |
302 | ** inx |
303 | ** L1: |
304 | */ |
305 | { |
306 | unsigned Changes = 0; |
307 | |
308 | /* Walk over the entries */ |
309 | unsigned I = 0; |
310 | while (I < CS_GetEntryCount (S)) { |
311 | |
312 | CodeEntry* L[5]; |
313 | |
314 | /* Get next entry */ |
315 | L[0] = CS_GetEntry (S, I); |
316 | |
317 | /* Check for the sequence */ |
318 | if (CE_IsCallTo (L[0], "pushax" ) && |
319 | CS_GetEntries (S, L+1, I+1, 4) && |
320 | !CS_RangeHasLabel (S, I+1, 3) && |
321 | L[1]->OPC == OP65_LDX && |
322 | CE_IsKnownImm (L[1], 0) && |
323 | L[2]->OPC == OP65_LDA && |
324 | CE_IsCallTo (L[3], "tosaddax" )) { |
325 | |
326 | CodeEntry* X; |
327 | CodeLabel* Label; |
328 | |
329 | /* Insert new code behind the sequence */ |
330 | X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, L[3]->LI); |
331 | CS_InsertEntry (S, X, I+4); |
332 | |
333 | /* adc xxx */ |
334 | X = NewCodeEntry (OP65_ADC, L[2]->AM, L[2]->Arg, 0, L[3]->LI); |
335 | CS_InsertEntry (S, X, I+5); |
336 | |
337 | /* bcc L1 */ |
338 | Label = CS_GenLabel (S, L[4]); |
339 | X = NewCodeEntry (OP65_BCC, AM65_BRA, Label->Name, Label, L[3]->LI); |
340 | CS_InsertEntry (S, X, I+6); |
341 | |
342 | /* inx */ |
343 | X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, L[3]->LI); |
344 | CS_InsertEntry (S, X, I+7); |
345 | |
346 | /* Delete the old code */ |
347 | CS_DelEntries (S, I, 4); |
348 | |
349 | /* Remember, we had changes */ |
350 | ++Changes; |
351 | |
352 | } |
353 | |
354 | /* Next entry */ |
355 | ++I; |
356 | |
357 | } |
358 | |
359 | /* Return the number of changes made */ |
360 | return Changes; |
361 | } |
362 | |
363 | |
364 | |
365 | unsigned OptAdd4 (CodeSeg* S) |
366 | /* Search for the sequence |
367 | ** |
368 | ** jsr pushax |
369 | ** lda xxx |
370 | ** ldx yyy |
371 | ** jsr tosaddax |
372 | ** |
373 | ** and replace it by |
374 | ** |
375 | ** clc |
376 | ** adc xxx |
377 | ** pha |
378 | ** txa |
379 | ** adc yyy |
380 | ** tax |
381 | ** pla |
382 | */ |
383 | { |
384 | unsigned Changes = 0; |
385 | |
386 | /* Walk over the entries */ |
387 | unsigned I = 0; |
388 | while (I < CS_GetEntryCount (S)) { |
389 | |
390 | CodeEntry* L[4]; |
391 | |
392 | /* Get next entry */ |
393 | L[0] = CS_GetEntry (S, I); |
394 | |
395 | /* Check for the sequence */ |
396 | if (CE_IsCallTo (L[0], "pushax" ) && |
397 | CS_GetEntries (S, L+1, I+1, 3) && |
398 | !CS_RangeHasLabel (S, I+1, 3) && |
399 | L[1]->OPC == OP65_LDA && |
400 | (L[1]->AM == AM65_ABS || L[1]->AM == AM65_ZP) && |
401 | L[2]->OPC == OP65_LDX && |
402 | (L[2]->AM == AM65_ABS || L[2]->AM == AM65_ZP) && |
403 | CE_IsCallTo (L[3], "tosaddax" )) { |
404 | |
405 | CodeEntry* X; |
406 | |
407 | /* Insert new code behind the sequence */ |
408 | X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, L[3]->LI); |
409 | CS_InsertEntry (S, X, I+4); |
410 | |
411 | /* adc xxx */ |
412 | X = NewCodeEntry (OP65_ADC, L[1]->AM, L[1]->Arg, 0, L[3]->LI); |
413 | CS_InsertEntry (S, X, I+5); |
414 | |
415 | /* pha */ |
416 | X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, L[3]->LI); |
417 | CS_InsertEntry (S, X, I+6); |
418 | |
419 | /* txa */ |
420 | X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, L[3]->LI); |
421 | CS_InsertEntry (S, X, I+7); |
422 | |
423 | /* adc yyy */ |
424 | X = NewCodeEntry (OP65_ADC, L[2]->AM, L[2]->Arg, 0, L[3]->LI); |
425 | CS_InsertEntry (S, X, I+8); |
426 | |
427 | /* tax */ |
428 | X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[3]->LI); |
429 | CS_InsertEntry (S, X, I+9); |
430 | |
431 | /* pla */ |
432 | X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, L[3]->LI); |
433 | CS_InsertEntry (S, X, I+10); |
434 | |
435 | /* Delete the old code */ |
436 | CS_DelEntries (S, I, 4); |
437 | |
438 | /* Remember, we had changes */ |
439 | ++Changes; |
440 | |
441 | } |
442 | |
443 | /* Next entry */ |
444 | ++I; |
445 | |
446 | } |
447 | |
448 | /* Return the number of changes made */ |
449 | return Changes; |
450 | } |
451 | |
452 | |
453 | |
454 | unsigned OptAdd5 (CodeSeg* S) |
455 | /* Search for a call to incaxn and replace it by an 8 bit add if the X register |
456 | ** is not used later. |
457 | */ |
458 | { |
459 | unsigned Changes = 0; |
460 | |
461 | /* Walk over the entries */ |
462 | unsigned I = 0; |
463 | while (I < CS_GetEntryCount (S)) { |
464 | |
465 | CodeEntry* E; |
466 | |
467 | /* Get next entry */ |
468 | E = CS_GetEntry (S, I); |
469 | |
470 | /* Check for the sequence */ |
471 | if (E->OPC == OP65_JSR && |
472 | strncmp (E->Arg, "incax" , 5) == 0 && |
473 | IsDigit (E->Arg[5]) && |
474 | E->Arg[6] == '\0' && |
475 | !RegXUsed (S, I+1)) { |
476 | |
477 | CodeEntry* X; |
478 | const char* Arg; |
479 | |
480 | /* Insert new code behind the sequence */ |
481 | X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, E->LI); |
482 | CS_InsertEntry (S, X, I+1); |
483 | |
484 | Arg = MakeHexArg (E->Arg[5] - '0'); |
485 | X = NewCodeEntry (OP65_ADC, AM65_IMM, Arg, 0, E->LI); |
486 | CS_InsertEntry (S, X, I+2); |
487 | |
488 | /* Delete the old code */ |
489 | CS_DelEntry (S, I); |
490 | |
491 | /* Remember, we had changes */ |
492 | ++Changes; |
493 | |
494 | } |
495 | |
496 | /* Next entry */ |
497 | ++I; |
498 | |
499 | } |
500 | |
501 | /* Return the number of changes made */ |
502 | return Changes; |
503 | } |
504 | |
505 | |
506 | |
507 | unsigned OptAdd6 (CodeSeg* S) |
508 | /* Search for the sequence |
509 | ** |
510 | ** adc ... |
511 | ** bcc L |
512 | ** inx |
513 | ** L: |
514 | ** |
515 | ** and remove the handling of the high byte if X is not used later. |
516 | */ |
517 | { |
518 | unsigned Changes = 0; |
519 | |
520 | /* Walk over the entries */ |
521 | unsigned I = 0; |
522 | while (I < CS_GetEntryCount (S)) { |
523 | |
524 | CodeEntry* L[3]; |
525 | |
526 | /* Get next entry */ |
527 | CodeEntry* E = CS_GetEntry (S, I); |
528 | |
529 | /* Check for the sequence */ |
530 | if (E->OPC == OP65_ADC && |
531 | CS_GetEntries (S, L, I+1, 3) && |
532 | (L[0]->OPC == OP65_BCC || L[0]->OPC == OP65_JCC) && |
533 | L[0]->JumpTo != 0 && |
534 | !CE_HasLabel (L[0]) && |
535 | L[1]->OPC == OP65_INX && |
536 | !CE_HasLabel (L[1]) && |
537 | L[0]->JumpTo->Owner == L[2] && |
538 | !RegXUsed (S, I+3)) { |
539 | |
540 | /* Remove the bcs/dex */ |
541 | CS_DelEntries (S, I+1, 2); |
542 | |
543 | /* Remember, we had changes */ |
544 | ++Changes; |
545 | |
546 | } |
547 | |
548 | /* Next entry */ |
549 | ++I; |
550 | |
551 | } |
552 | |
553 | /* Return the number of changes made */ |
554 | return Changes; |
555 | } |
556 | |