1 /*****************************************************************************/
5 /* Size optimizations */
9 /* (C) 2002 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 /*****************************************************************************/
46 /*****************************************************************************/
48 /*****************************************************************************/
52 typedef struct CallDesc CallDesc;
54 const char* LongFunc; /* Long function name */
55 short A, X, Y; /* Register contents */
56 const char* ShortFunc; /* Short function name */
59 /* Note: The table is sorted. If there is more than one entry with the same
60 * name, entries are sorted best match first, so when searching linear for
61 * a match, the first one can be used because it is also the best one (or
62 * at least none of the following ones are better).
64 static const CallDesc CallTable [] = {
65 { "addeqysp", -1, -1, 0, "addeq0sp" },
66 { "laddeqysp", -1, -1, 0, "laddeq0sp" },
67 { "ldaxysp", -1, -1, 1, "ldax0sp" },
68 { "ldeaxidx", -1, -1, 3, "ldeaxi" },
69 { "ldeaxysp", -1, -1, 3, "ldeax0sp" },
70 { "pusha", 0, -1, -1, "pushc0" },
71 { "pusha", 1, -1, -1, "pushc1" },
72 { "pusha", 2, -1, -1, "pushc2" },
73 { "pushax", 0, 0, -1, "push0" },
74 { "pushax", 1, 0, -1, "push1" },
75 { "pushax", 2, 0, -1, "push2" },
76 { "pushax", 3, 0, -1, "push3" },
77 { "pushax", 4, 0, -1, "push4" },
78 { "pushax", 5, 0, -1, "push5" },
79 { "pushax", 6, 0, -1, "push6" },
80 { "pushax", 7, 0, -1, "push7" },
81 { "pushax", -1, 0, -1, "pusha0" },
82 { "pushax", -1, 0xFF, -1, "pushaFF" },
83 { "pushaysp", -1, -1, 0, "pusha0sp" },
84 { "staxysp", -1, -1, 0, "stax0sp" },
85 { "tosaddax", -1, 0, -1, "tosadda0" },
86 { "tosandax", -1, 0, -1, "tosanda0" },
87 { "tosdivax", -1, 0, -1, "tosdiva0" },
88 { "toseqax", -1, 0, -1, "toseqa0" },
89 { "tosgeax", -1, 0, -1, "tosgea0" },
90 { "tosgtax", -1, 0, -1, "tosgta0" },
91 { "tosleax", -1, 0, -1, "toslea0" },
92 { "tosorax", -1, 0, -1, "tosora0" },
93 { "ldaxidx", -1, -1, 1, "ldaxi" },
94 { "ldeaxysp", -1, -1, 3, "ldeax0sp" },
95 { "lsubeqysp", -1, -1, 0, "lsubeq0sp" },
96 { "steaxysp", -1, -1, 0, "steax0sp" },
97 { "subeqysp", -1, -1, 0, "subeq0sp" },
98 { "tosaslax", -1, 0, -1, "tosasla0" },
99 { "tosasrax", -1, 0, -1, "tosasra0" },
100 { "tosltax", -1, 0, -1, "toslta0" },
101 { "tosmodax", -1, 0, -1, "tosmoda0" },
102 { "tosmulax", -1, 0, -1, "tosmula0" },
103 { "tosneax", -1, 0, -1, "tosnea0" },
104 { "tosrsubax", -1, 0, -1, "tosrsuba0" },
105 { "tosshlax", -1, 0, -1, "tosshla0" },
106 { "tosshrax", -1, 0, -1, "tosshra0" },
107 { "tossubax", -1, 0, -1, "tossuba0" },
108 { "tosudivax", -1, 0, -1, "tosudiva0" },
109 { "tosugeax", -1, 0, -1, "tosugea0" },
110 { "tosugtax", -1, 0, -1, "tosugta0" },
111 { "tosuleax", -1, 0, -1, "tosulea0" },
112 { "tosultax", -1, 0, -1, "tosulta0" },
113 { "tosumodax", -1, 0, -1, "tosumoda0" },
114 { "tosumulax", -1, 0, -1, "tosumula0" },
115 { "tosxorax", -1, 0, -1, "tosxora0" },
116 { "zzzzzzzz", -1, -1, -1, "zzzzzzzz" },
119 "tosadd0ax", /* tosaddeax, sreg = 0 */
120 "laddeqa", /* laddeq, sreg = 0, x = 0 */
121 "laddeq1", /* laddeq, sreg = 0, x = 0, a = 1 */
122 "tosand0ax", /* tosandeax, sreg = 0 */
123 "tosdiv0ax", /* tosdiveax, sreg = 0 */
124 "tosmod0ax", /* tosmodeax, sreg = 0 */
125 "tosmul0ax", /* tosmuleax, sreg = 0 */
126 "tosumul0ax", /* tosumuleax, sreg = 0 */
127 "tosor0ax", /* tosoreax, sreg = 0 */
128 "push0ax", /* pusheax, sreg = 0 */
129 "tosrsub0ax", /* tosrsubeax, sreg = 0 */
130 "tosshl0ax", /* tosshleax, sreg = 0 */
131 "tosasl0ax", /* tosasleax, sreg = 0 */
132 "tosshr0ax", /* tosshreax, sreg = 0 */
133 "tosasr0ax", /* tosasreax, sreg = 0 */
134 "tossub0ax", /* tossubeax, sreg = 0 */
135 "lsubeqa", /* lsubeq, sreg = 0, x = 0 */
136 "lsubeq1", /* lsubeq, sreg = 0, x = 0, a = 1 */
137 "tosudiv0ax", /* tosudiveax, sreg = 0 */
138 "tosumod0ax", /* tosumodeax, sreg = 0 */
139 "tosxor0ax", /* tosxoreax, sreg = 0 */
142 #define CALL_COUNT (sizeof(CallTable) / sizeof(CallTable[0]))
146 /*****************************************************************************/
148 /*****************************************************************************/
152 static const CallDesc* FindCall (const char* Name)
153 /* Find the function with the given name. Return a pointer to the table entry
154 * or NULL if the function was not found.
157 /* Do a binary search */
159 int Last = (sizeof(CallTable) / sizeof(CallTable[0])) - 1;
164 while (First <= Last) {
166 /* Set current to mid of range */
167 Current = (Last + First) / 2;
170 Result = strcmp (CallTable[Current].LongFunc, Name);
176 /* Found. Repeat the procedure until the first of all entries
177 * with the same name is found.
185 /* Return the first entry if found, or NULL otherwise */
186 return Found? &CallTable[First] : 0;
191 /*****************************************************************************/
193 /*****************************************************************************/
197 unsigned OptSize1 (CodeSeg* S)
198 /* Do size optimization by calling special subroutines that preload registers.
199 * This routine does not work standalone, it needs a following register load
204 unsigned Changes = 0;
207 /* Generate register info for the following step */
210 /* Walk over the entries */
212 while (I < CS_GetEntryCount (S)) {
215 E = CS_GetEntry (S, I);
217 /* Check if it's a subroutine call */
218 if (E->OPC == OP65_JSR) {
220 /* Check for any of the known functions. */
221 const CallDesc* D = FindCall (E->Arg);
222 while (D && strcmp (D->LongFunc, E->Arg) == 0) {
223 /* Check the registers */
224 if ((D->A < 0 || D->A == E->RI->In.RegA) &&
225 (D->X < 0 || D->X == E->RI->In.RegX) &&
226 (D->Y < 0 || D->Y == E->RI->In.RegY)) {
227 /* Ok, match for all registers */
229 X = NewCodeEntry (E->OPC, E->AM, D->ShortFunc, 0, E->LI);
230 CS_InsertEntry (S, X, I+1);
233 /* Remember that we had changes */
245 /* Free register info */
248 /* Return the number of changes made */
254 unsigned OptSize2 (CodeSeg* S)
255 /* Do size optimization by using shorter code sequences, even if this
256 * introduces relations between instructions. This step must be one of the
257 * last steps, because it makes further work much more difficult.
260 unsigned Changes = 0;
263 /* Generate register info for the following step */
266 /* Walk over the entries */
268 while (I < CS_GetEntryCount (S)) {
272 CodeEntry* E = CS_GetEntry (S, I);
274 /* Assume we have no replacement */
277 /* Check the instruction */
281 if (CE_KnownImm (E)) {
282 short Val = (short) E->Num;
283 if (Val == E->RI->In.RegX) {
284 X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI);
285 } else if (Val == E->RI->In.RegY) {
286 X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI);
287 } else if (E->RI->In.RegA >= 0 && CPU >= CPU_65C02) {
288 if (Val == ((E->RI->In.RegA - 1) & 0xFF)) {
289 X = NewCodeEntry (OP65_DEA, AM65_IMP, 0, 0, E->LI);
290 } else if (Val == ((E->RI->In.RegA + 1) & 0xFF)) {
291 X = NewCodeEntry (OP65_INA, AM65_IMP, 0, 0, E->LI);
298 if (CE_KnownImm (E)) {
299 short Val = (short) E->Num;
300 if (E->RI->In.RegX >= 0 && Val == ((E->RI->In.RegX - 1) & 0xFF)) {
301 X = NewCodeEntry (OP65_DEX, AM65_IMP, 0, 0, E->LI);
302 } else if (E->RI->In.RegX >= 0 && Val == ((E->RI->In.RegX + 1) & 0xFF)) {
303 X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, E->LI);
304 } else if (Val == E->RI->In.RegA) {
305 X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI);
311 if (CE_KnownImm (E)) {
312 short Val = (short) E->Num;
313 if (E->RI->In.RegY >= 0 && Val == ((E->RI->In.RegY - 1) & 0xFF)) {
314 X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, E->LI);
315 } else if (E->RI->In.RegY >= 0 && Val == ((E->RI->In.RegY + 1) & 0xFF)) {
316 X = NewCodeEntry (OP65_INY, AM65_IMP, 0, 0, E->LI);
317 } else if (Val == E->RI->In.RegA) {
318 X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI);
324 /* Avoid gcc warnings */
329 /* Insert the replacement if we have one */
331 CS_InsertEntry (S, X, I+1);
341 /* Free register info */
344 /* Return the number of changes made */