1 /*****************************************************************************/
5 /* An optimizer for the cc65 C compiler */
9 /* (C) 1998 Ullrich von Bassewitz */
11 /* D-70597 Stuttgart */
12 /* EMail: uz@musoftware.de */
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 /*****************************************************************************/
58 /*****************************************************************************/
60 /*****************************************************************************/
64 /* Bitset of flags that switch the different optimizer passes */
65 unsigned long OptDisable = 0;
69 /* Bitmapped flags for the Flags field in the Line struct */
70 #define OF_CODE 0x0001 /* This line is in a code segment */
72 /* Pointer to first code line */
73 static Line* FirstCode;
76 static Line** Labels = 0; /* Pointers to label lines */
77 static unsigned LabelCount = 0; /* Count of local labels found */
79 /* A collection of lines */
80 typedef struct LineColl_ LineColl;
82 unsigned Count; /* Count of lines in the collection */
83 unsigned Max; /* Maximum count of lines */
84 Line* Lines[1]; /* Lines, dynamically allocated */
89 /* Calculate the element count of a table */
90 #define COUNT(T) (sizeof (T) / sizeof (T [0]))
92 /* Macro to increment and decrement register contents if they're valid */
93 #define INC(reg,val) if ((reg) >= 0) (reg) = ((reg) + val) & 0xFF
94 #define DEC(reg,val) if ((reg) >= 0) (reg) = ((reg) - val) & 0xFF
96 /* Defines for the conditions in a compare */
108 /* Defines for registers */
109 #define REG_NONE 0x00
113 #define REG_AX (REG_A | REG_X)
114 #define REG_ALL (REG_A | REG_X | REG_Y)
116 /* Description of the commands */
117 static const struct {
118 const char* Insn; /* Instruction */
119 unsigned char FullMatch; /* Match full instuction? */
120 unsigned char Use; /* Registers used */
121 unsigned char Load; /* Registers loaded */
123 { "\tadc\t", 0, REG_A, REG_NONE },
124 { "\tand\t", 0, REG_A, REG_NONE },
125 { "\tasl\ta", 1, REG_A, REG_NONE },
126 { "\tasl\t", 0, REG_NONE, REG_NONE },
127 { "\tclc", 1, REG_NONE, REG_NONE },
128 { "\tcld", 1, REG_NONE, REG_NONE },
129 { "\tcli", 1, REG_NONE, REG_NONE },
130 { "\tcmp\t", 0, REG_A, REG_NONE },
131 { "\tcpx\t", 0, REG_X, REG_NONE },
132 { "\tcpy\t", 0, REG_Y, REG_NONE },
133 { "\tdea", 1, REG_A, REG_NONE },
134 { "\tdec\ta", 1, REG_A, REG_NONE },
135 { "\tdec\t", 0, REG_NONE, REG_NONE },
136 { "\tdex", 1, REG_X, REG_NONE },
137 { "\tdey", 1, REG_Y, REG_NONE },
138 { "\teor\t", 0, REG_A, REG_NONE },
139 { "\tina", 1, REG_A, REG_NONE },
140 { "\tinc\ta", 1, REG_A, REG_NONE },
141 { "\tinc\t", 0, REG_NONE, REG_NONE },
142 { "\tinx", 1, REG_X, REG_NONE },
143 { "\tiny", 1, REG_Y, REG_NONE },
144 { "\tjsr\tbool", 0, REG_NONE, REG_AX },
145 { "\tjsr\tdecaxy", 1, REG_ALL, REG_AX },
146 { "\tjsr\tdecax", 0, REG_AX, REG_AX },
147 { "\tjsr\tdecsp2", 1, REG_NONE, REG_A },
148 { "\tjsr\tdecsp3", 1, REG_NONE, REG_A },
149 { "\tjsr\tdecsp4", 1, REG_NONE, REG_A },
150 { "\tjsr\tdecsp5", 1, REG_NONE, REG_A },
151 { "\tjsr\tdecsp6", 1, REG_NONE, REG_A },
152 { "\tjsr\tdecsp7", 1, REG_NONE, REG_A },
153 { "\tjsr\tdecsp8", 1, REG_NONE, REG_A },
154 { "\tjsr\tldax0sp", 1, REG_Y, REG_AX },
155 { "\tjsr\tldaxysp", 1, REG_Y, REG_AX },
156 { "\tjsr\tpusha", 1, REG_A, REG_Y },
157 { "\tjsr\tpusha0", 1, REG_A, REG_X | REG_Y },
158 { "\tjsr\tpushax", 1, REG_AX, REG_Y },
159 { "\tjsr\tpushw0sp", 1, REG_NONE, REG_ALL },
160 { "\tjsr\tpushwysp", 1, REG_Y, REG_ALL },
161 { "\tjsr\ttosicmp", 1, REG_AX, REG_ALL },
162 { "\tlda\t", 0, REG_NONE, REG_A },
163 { "\tldax\t", 0, REG_NONE, REG_AX },
164 { "\tldx\t", 0, REG_NONE, REG_X },
165 { "\tldy\t", 0, REG_NONE, REG_Y },
166 { "\tlsr\ta", 1, REG_A, REG_NONE },
167 { "\tlsr\t", 0, REG_NONE, REG_NONE },
168 { "\tnop", 1, REG_NONE, REG_NONE },
169 { "\tora\t", 0, REG_A, REG_NONE },
170 { "\tpha", 1, REG_A, REG_NONE },
171 { "\tphp", 1, REG_NONE, REG_NONE },
172 { "\tpla", 1, REG_NONE, REG_A },
173 { "\tplp", 1, REG_NONE, REG_NONE },
174 { "\trol\ta", 1, REG_A, REG_A },
175 { "\trol\t", 0, REG_NONE, REG_NONE },
176 { "\tror\ta", 1, REG_A, REG_A },
177 { "\tror\t", 0, REG_NONE, REG_NONE },
178 { "\tsbc\t", 0, REG_A, REG_NONE },
179 { "\tsec", 1, REG_NONE, REG_NONE },
180 { "\tsed", 1, REG_NONE, REG_NONE },
181 { "\tsei", 1, REG_NONE, REG_NONE },
182 { "\tsta\t", 0, REG_A, REG_NONE },
183 { "\tstx\t", 0, REG_X, REG_NONE },
184 { "\tsty\t", 0, REG_Y, REG_NONE },
185 { "\tstz\t", 0, REG_NONE, REG_NONE },
186 { "\ttax", 1, REG_A, REG_X },
187 { "\ttay", 1, REG_A, REG_Y },
188 { "\ttrb\t", 0, REG_A, REG_NONE },
189 { "\ttsb\t", 0, REG_A, REG_NONE },
190 { "\ttsx", 1, REG_NONE, REG_X },
191 { "\ttxa", 1, REG_X, REG_A },
192 { "\ttya", 1, REG_Y, REG_A },
197 /* Table with the compare suffixes */
198 static const char CmpSuffixTab [][4] = {
199 "eq", "ne", "gt", "ge", "lt", "le", "ugt", "uge", "ult", "ule"
202 /* Table used to invert a condition, indexed by condition */
203 static const unsigned char CmpInvertTab [] = {
205 CMP_LE, CMP_LT, CMP_GE, CMP_GT,
206 CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT
209 /* Table to show which compares are signed (use the N flag) */
210 static const char CmpSignedTab [] = {
211 0, 0, 1, 1, 1, 1, 0, 0, 0, 0
216 /* Lists of branches */
217 static const char* ShortBranches [] = {
228 static const char* LongBranches [] = {
242 /*****************************************************************************/
244 /*****************************************************************************/
248 static unsigned EstimateSize (Line* L);
249 /* Estimate the size of an instruction */
251 static int IsLocalLabel (const Line* L);
252 /* Return true if the line is a local label line */
254 static unsigned GetLabelNum (const char* L);
255 /* Return the label number of a label line */
257 static unsigned RVUInt1 (Line* L, LineColl* LC, unsigned Used, unsigned Unused);
258 /* Subfunction for RegValUsed. Will be called recursively in case of branches. */
260 static Line* NewLineAfter (Line* LineBefore, const char* Format, ...) attribute ((format(printf,2,3)));
261 /* Create a new line, insert it after L and return it. The new line is marked
265 static Line* ReplaceLine (Line* L, const char* Format, ...)
266 attribute ((format(printf,2,3)));
267 /* Replace one line by another */
271 /*****************************************************************************/
273 /*****************************************************************************/
277 static Line* NewLineAfter (Line* LineBefore, const char* Format, ...)
278 /* Create a new line, insert it after L and return it. The new line is marked
284 /* Format the new line and add it */
286 va_start (ap, Format);
287 L = NewCodeLineAfter (LineBefore, Format, ap);
290 /* Make the line a code line */
293 /* Estimate the code size */
294 L->Size = EstimateSize (L);
296 /* Return the new line */
302 static Line* NewLabelAfter (Line* L, unsigned Label)
303 /* Add a new line with a definition of a local label after the line L */
307 /* Create the label */
308 sprintf (Buf, "L%04X:", Label);
310 /* Create a new line */
311 L = NewLineAfter (L, Buf);
313 /* Insert this label into the label list */
316 /* Return the new line */
322 static void FreeLine (Line* L)
323 /* Remove a line from the list and free it */
325 /* If this is a label line, remove it from the label list */
326 if (IsLocalLabel (L)) {
327 Labels [GetLabelNum (L->Line)] = 0;
330 /* Unlink the line */
336 static Line* ReplaceLine (Line* L, const char* Format, ...)
337 /* Replace one line by another */
342 /* Format the new line */
344 va_start (ap, Format);
345 xvsprintf (S, sizeof (S), Format, ap);
348 /* Get the length of the new line */
351 /* We can copy the line if the old line has space enough */
354 /* Just copy the new line, but don't update the length */
355 memcpy (L->Line, S, Len);
356 L->Line [Len] = '\0';
360 /* We must allocate new space */
361 Line* NewLine = xmalloc (sizeof (Line) + Len);
363 /* Set the values in the new struct */
364 NewLine->Flags = L->Flags;
365 NewLine->Index = L->Index;
366 NewLine->Size = L->Size; /* Hmm ... */
368 memcpy (NewLine->Line, S, Len + 1);
370 /* Replace the old struct in the list */
371 NewLine->Next = L->Next;
373 NewLine->Next->Prev = NewLine;
378 NewLine->Prev = L->Prev;
380 NewLine->Prev->Next = NewLine;
386 /* Free the old struct */
391 /* Estimate the new size */
392 if (L->Flags & OF_CODE) {
393 L->Size = EstimateSize (L);
396 /* Return the line */
402 static Line* PrevCodeLine (Line* L)
403 /* Return the previous line containing code */
407 if (L->Flags & OF_CODE && L->Line [0] != '+') {
417 static Line* NextCodeSegLine (Line* L)
418 /* Return the next line in the code segment */
422 if (L->Flags & OF_CODE) {
432 static Line* NextCodeLine (Line* L)
433 /* Return the next line containing code */
437 if ((L->Flags & OF_CODE) != 0 && L->Line [0] != '+') {
447 static Line* NextInstruction (Line* L)
448 /* Return the next line containing code, ignoring labels. */
451 L = NextCodeLine (L);
452 } while (L && (L->Line[0] == '+' || IsLocalLabel(L)));
458 static void FreeLines (Line* Start, Line* End)
459 /* Delete all lines from Start to End, both inclusive */
464 Start = NextCodeSegLine (Start);
471 /*****************************************************************************/
472 /* Line Collections */
473 /*****************************************************************************/
477 static LineColl* NewLineColl (unsigned Size)
478 /* Create a new line collection and return it */
480 /* Allocate memory */
481 LineColl* LC = xmalloc (sizeof (LineColl) + sizeof (Line) * (Size-1));
483 /* Initialize members */
487 /* Return the new collection */
493 static void FreeLineColl (LineColl* LC)
494 /* Delete a line collection */
501 static int LCAddLine (LineColl* LC, Line* L)
502 /* Add a line. Return 0 if no space available, return 1 otherwise */
504 /* Check if there is enough space available */
505 if (LC->Count >= LC->Max) {
506 /* No room available */
511 LC->Lines [LC->Count++] = L;
519 static int LCHasLine (LineColl* LC, Line* L)
520 /* Check if the given line is in the collection */
523 for (I = 0; I < LC->Count; ++I) {
524 if (LC->Lines[I] == L) {
533 /*****************************************************************************/
534 /* Test a line for several things */
535 /*****************************************************************************/
539 static int IsLocalLabel (const Line* L)
540 /* Return true if the line is a local label line */
542 return (L->Line [0] == 'L' && IsXDigit (L->Line [1]));
547 static int IsExtLabel (const Line* L)
548 /* Return true if the line is an external label line */
550 return (L->Line [0] == '_');
555 static int IsLabel (const Line* L)
556 /* Return true if the line is a label line */
558 return (L->Line [0] == 'L' && IsXDigit (L->Line [1])) ||
559 (L->Line [0] == '_');;
564 static int IsHintLine (const Line* L)
565 /* Return true if the line contains an optimizer hint */
567 return L->Line [0] == '+';
572 static int IsSegHint (const Line* L)
573 /* Return true if the given line contains a segment hint */
575 return (L->Line [0] == '+' && strncmp (L->Line + 1, "seg:", 4) == 0);
580 static int IsHint (const Line* L, const char* Hint)
581 /* Check if the line contains a given hint */
583 return (L->Line [0] == '+' && strcmp (L->Line + 1, Hint) == 0);
588 static int IsCondJump (const Line* L)
589 /* Return true if the line contains a conditional jump */
591 return (L->Line [0] == '\t' &&
592 (strncmp (L->Line + 1, "beq\t", 4) == 0 ||
593 strncmp (L->Line + 1, "bne\t", 4) == 0 ||
594 strncmp (L->Line + 1, "jeq\t", 4) == 0 ||
595 strncmp (L->Line + 1, "jne\t", 4) == 0));
600 static int IsXAddrMode (const Line* L)
601 /* Return true if the given line does use the X register */
603 unsigned Len = strlen (L->Line);
604 return (strcmp (L->Line + Len - 3, ",x)") == 0 ||
605 strcmp (L->Line + Len - 2, ",x") == 0);
610 static int NoXAddrMode (const Line* L)
611 /* Return true if the given line does use the X register */
613 return !IsXAddrMode (L);
618 static int IsYAddrMode (const Line* L)
619 /* Return true if the given line does use the Y register */
621 unsigned Len = strlen (L->Line);
622 return (strcmp (L->Line + Len - 2, ",y") == 0);
627 static int Is16BitStore (const Line* L1, const Line* L2)
628 /* Check if L1 and L2 are a store of ax into a 16 bit location */
630 unsigned Len1 = strlen (L1->Line);
631 return (strncmp (L1->Line, "\tsta\t", 5) == 0 &&
632 strncmp (L2->Line, "\tstx\t", 5) == 0 &&
633 strncmp (L1->Line+5, L2->Line+5, Len1-5) == 0 &&
634 strcmp (L2->Line+Len1, "+1") == 0);
639 static Line* FindHint (Line* L, const char* Hint)
640 /* Search for a line with the given hint */
643 if (IsHint (L, Hint)) {
653 static unsigned GetHexNum (const char* S)
654 /* Get a hex number from a string */
658 while (IsXDigit (S [I])) {
659 int C = (unsigned char) (S [I++]);
665 Val = (Val << 4) + C;
672 static unsigned GetLabelNum (const char* L)
673 /* Return the label number of a label line */
676 return GetHexNum (L+1);
681 static Line* GetTargetLine (const char* L)
682 /* Get the line with the target label of a jump. L must be a pointer to the
683 * string containing the label number.
688 /* Get the label number of the target */
689 unsigned Label = GetLabelNum (L);
690 CHECK (Label < LabelCount);
692 /* Get the line with this label */
693 Target = Labels [Label];
694 CHECK (Target != 0 && (Target->Flags & OF_CODE) != 0);
702 static unsigned GetJumpDistance (Line* L, Line* Target)
703 /* Get the distance between both lines */
705 unsigned Distance = 0;
708 if (Target->Index > L->Index) {
709 /* This is a forward jump. */
711 L = NextCodeLine (L);
713 } while (L != Target);
715 /* This is a backward jump */
717 L = PrevCodeLine (L);
719 } while (L != Target);
723 /* Return the calculated distance */
729 static int LineMatch (const Line* L, const char* Start)
730 /* Check if the start of the line matches Start */
732 return strncmp (L->Line, Start, strlen (Start)) == 0;
737 static int LineFullMatch (const Line* L, const char* Start)
738 /* Check if the matches Start */
740 return strcmp (L->Line, Start) == 0;
745 static int LineMatchX (const Line* L, const char** Start)
746 /* Check the start of the line against a list of patterns. Return the
747 * number of the pattern that matched, or -1 in case of no match.
752 if (LineMatch (L, *Start)) {
765 static int LineFullMatchX (const Line* L, const char** Start)
766 /* Check the the line against a list of patterns. Return the
767 * number of the pattern that matched, or -1 in case of no match.
772 if (LineFullMatch (L, *Start)) {
785 static int IsLoadAX (Line* L1, Line* L2)
786 /* Check if the both lines load a static variable into ax. That is, both lines
792 return LineMatch (L1, "\tlda\t") &&
793 LineMatch (L2, "\tldx\t") &&
794 strncmp (L1->Line+5, L2->Line+5, strlen (L1->Line+5)) == 0 &&
795 strcmp (L2->Line+strlen(L1->Line), "+1") == 0;
800 /*****************************************************************************/
801 /* Initial optimizer setup */
802 /*****************************************************************************/
806 static void FindCodeStart (void)
807 /* Find and remember the first line of actual code */
809 Line* L = FindHint (FirstLine, "end_of_preamble");
810 FirstCode = L? L->Next : 0;
815 static unsigned EstimateDataSize (Line* L, unsigned Chunk)
816 /* Estimate the size of a .byte, .word or .dword command */
818 unsigned Size = Chunk;
820 while ((S = strchr (S, ',')) != 0) {
829 static unsigned EstimateSize (Line* L)
830 /* Estimate the size of an instruction */
832 static const char* OneByteCmds [] = {
848 if (L->Line [0] != '\t') {
851 if (LineMatch (L, "\tldax\t")) {
852 /* Immidiate load of both, A and X */
855 if (LineMatch (L, "\tld")) {
856 OpStart = L->Line [5];
857 return (OpStart == '#' || OpStart == '(')? 2 : 3;
859 if (LineMatch (L, "\tst")) {
860 OpStart = L->Line [5];
861 return (OpStart == '(')? 2 : 3;
863 if (LineMatch (L, "\t.byte\t")) {
864 return EstimateDataSize (L, 1);
866 if (LineMatch (L, "\t.word\t")) {
867 return EstimateDataSize (L, 2);
869 if (LineMatch (L, "\t.dword\t")) {
870 return EstimateDataSize (L, 4);
872 if (LineMatchX (L, ShortBranches) >= 0) {
875 if (LineMatchX (L, LongBranches) >= 0) {
878 if (LineMatchX (L, OneByteCmds) >= 0) {
886 static void MarkCodeLines (void)
887 /* Mark all lines that are inside a code segment */
893 InCode = IsHint (L, "seg:code");
894 } else if (InCode && L->Line[0] != '\0') {
896 L->Size = EstimateSize (L);
904 static void CreateLabelList (void)
905 /* Create a list with pointers to local labels */
911 /* Get the next label number. This is also the current label count.
912 * Make some room for more labels when optimizing code.
914 LabelCount = GetLocalLabel () + 100;
916 /* Allocate memory for the array and clear it */
917 Labels = xmalloc (LabelCount * sizeof (Line*));
918 for (I = 0; I < LabelCount; ++I) {
922 /* Walk through the code and insert all label lines */
925 if (IsLocalLabel (L)) {
926 unsigned LabelNum = GetLabelNum (L->Line);
927 CHECK (LabelNum < LabelCount);
928 Labels [LabelNum] = L;
936 static unsigned AllocLabel (void)
937 /* Get a new label. The current code does not realloc the label list, so there
938 * must be room enough in the current list.
943 /* Search for a free slot, start at 1, since 0 is "no label" */
944 for (I = 1; I < LabelCount; ++I) {
945 if (Labels[I] == 0) {
946 /* Found a free slot */
951 /* No label space available */
952 Internal ("Out of label space in the optimizer");
960 /*****************************************************************************/
961 /* Helper functions */
962 /*****************************************************************************/
966 static int GetNextCodeLines (Line* L, Line** Lines, unsigned Count)
967 /* Get a number of code lines ignoring hints and other stuff. The function
968 * returns 1 if we got the lines and 0 if we are at the end of the code
969 * segment or if we hit a label.
974 /* Get the next valid line */
976 L = NextCodeLine (L);
977 } while (L && IsHintLine (L));
979 /* Did we get one? */
980 if (L == 0 || IsLabel (L)) {
985 /* Remember the line */
995 static int FindCond (const char* Suffix)
996 /* Map a condition suffix to a code. Return the code or -1 on failure */
1001 for (I = 0; I < sizeof (CmpSuffixTab) / sizeof (CmpSuffixTab [0]); ++I) {
1002 if (strncmp (Suffix, CmpSuffixTab [I], strlen (CmpSuffixTab[I])) == 0) {
1014 static int CheckAndGetIntCmp (const Line* JSR, const Line* JMP)
1015 /* Helper function to check for a compare subroutine call followed by a
1016 * conditional branch. Will return the condition found, or -1 if no
1017 * or invalid condition.
1024 /* Extract the condition from the function name. */
1025 if ((Cond [0] = JSR->Line [8]) == 'u') {
1026 Cond [1] = JSR->Line [9];
1027 Cond [2] = JSR->Line [10];
1029 Tail = JSR->Line + 11;
1031 Cond [1] = JSR->Line [9];
1033 Tail = JSR->Line + 10;
1036 /* Check if this is indeed an integer function */
1037 if (strcmp (Tail, "ax") != 0) {
1042 /* Get the condition code */
1043 C = FindCond (Cond);
1049 /* Invert the code if we jump on condition not met. */
1050 if (JMP->Line [2] == 'e' && JMP->Line [3] == 'q') {
1051 /* Jumps if condition false, invert condition */
1052 C = CmpInvertTab [C];
1055 /* Return the condition code */
1061 static int TosCmpFunc (Line* L)
1062 /* Check if this is a call to one of the TOS compare functions (tosgtax).
1063 * Return the condition code or -1 if not found.
1066 if (LineMatch (L, "\tjsr\ttos") &&
1067 strcmp (L->Line+strlen(L->Line)-2, "ax") == 0) {
1069 /* Ok, found. Get the condition. */
1070 return FindCond (L->Line+8);
1081 static int IsUnsignedCmp (int Code)
1082 /* Check if this is an unsigned compare */
1085 return CmpSignedTab [Code] == 0;
1090 static void InvertZJump (Line* L)
1091 /* Invert a jeq/jne jump */
1093 if (L->Line [2] == 'n' && L->Line [3] == 'e') {
1094 /* This was a bne/jne */
1098 /* This was (hopefully) a beq/jeq */
1106 static int FindCmd (Line* L)
1110 /* Search for the known patterns */
1111 for (I = 0; I < COUNT(CmdDesc); ++I) {
1112 if (CmdDesc[I].FullMatch) {
1113 if (LineFullMatch (L, CmdDesc[I].Insn)) {
1118 if (LineMatch (L, CmdDesc[I].Insn)) {
1130 static unsigned RVUInt2 (Line* L,
1131 LineColl* LC, /* To remember visited lines */
1132 unsigned Used, /* Definitely used registers */
1133 unsigned Unused) /* Definitely unused registers */
1134 /* Subfunction for RegValUsed. Will be called recursively in case of branches. */
1138 /* Check the following instructions. We classifiy them into primary
1139 * loads (register value not used), neutral (check next instruction),
1140 * and unknown (assume register was used).
1146 /* Get the next line and follow jumps */
1149 /* Handle jumps to local labels (continue there) */
1150 if (LineMatch (L, "\tjmp\tL") || LineMatch (L, "\tbra\tL")) {
1151 /* Get the target of the jump */
1152 L = GetTargetLine (L->Line+5);
1155 /* Get the next line, skip local labels */
1157 L = NextCodeSegLine (L);
1158 } while (L && (IsLocalLabel (L) || L->Line[0] == '\0'));
1160 /* Bail out if we're done */
1161 if (L == 0 || IsExtLabel (L)) {
1162 /* End of function reached */
1166 /* Check if we had this line already. If so, bail out, if not,
1167 * add it to the list of known lines.
1169 if (LCHasLine (LC, L) || !LCAddLine (LC, L)) {
1173 } while (LineMatch (L, "\tjmp\tL") || LineMatch (L, "\tbra\tL"));
1175 /* Special handling of code hints */
1176 if (IsHintLine (L)) {
1178 if (IsHint (L, "a:-") && (Used & REG_A) == 0) {
1180 } else if (IsHint (L, "x:-") && (Used & REG_X) == 0) {
1182 } else if (IsHint (L, "y:-") && (Used & REG_Y) == 0) {
1186 /* Special handling for branches */
1187 } else if (LineMatchX (L, ShortBranches) >= 0 ||
1188 LineMatchX (L, LongBranches) >= 0) {
1189 const char* Target = L->Line+5;
1190 if (Target[0] == 'L') {
1191 /* Jump to local label. Check the register usage starting at
1192 * the branch target and at the code following the branch.
1193 * All registers that are unused in both execution flows are
1194 * returned as unused.
1197 U2 = RVUInt1 (GetTargetLine (Target), LC, Used, Unused);
1198 U1 = RVUInt1 (L, LC, Used, Unused);
1199 return U1 | U2; /* Used in any of the branches */
1203 /* Search for the instruction in this line */
1206 /* If we don't find it, assume all other registers are used */
1211 /* Evaluate the use flags, check for addressing modes */
1213 if (IsXAddrMode (L)) {
1215 } else if (IsYAddrMode (L)) {
1219 /* Remove registers that were already new loaded */
1222 /* Remember the remaining registers */
1226 /* Evaluate the load flags */
1227 R = CmdDesc[I].Load;
1229 /* Remove registers that were already used */
1232 /* Remember the remaining registers */
1238 /* If we know about all registers, bail out */
1239 if ((Used | Unused) == REG_ALL) {
1245 /* Return to the caller the complement of all unused registers */
1246 return ~Unused & REG_ALL;
1251 static unsigned RVUInt1 (Line* L,
1252 LineColl* LC, /* To remember visited lines */
1253 unsigned Used, /* Definitely used registers */
1254 unsigned Unused) /* Definitely unused registers */
1255 /* Subfunction for RegValUsed. Will be called recursively in case of branches. */
1257 /* Remember the current count of the line collection */
1258 unsigned Count = LC->Count;
1260 /* Call the worker routine */
1261 unsigned R = RVUInt2 (L, LC, Used, Unused);
1263 /* Restore the old count */
1266 /* Return the result */
1272 static unsigned RegValUsed (Line* Start)
1273 /* Check the next instructions after the one in L for register usage. If
1274 * a register is used as an index, or in a store or other instruction, it
1275 * is assumed to be used. If a register is loaded with a value, before it
1276 * was used by one of the actions described above, it is assumed unused.
1277 * If the end of the lookahead is reached, all registers that are uncertain
1278 * are marked as used.
1279 * The result of the search is returned.
1284 /* Create a new line collection and enter the start line */
1285 LineColl* LC = NewLineColl (256);
1286 LCAddLine (LC, Start);
1288 /* Call the recursive subfunction */
1289 R = RVUInt1 (Start, LC, REG_NONE, REG_NONE);
1291 /* Delete the line collection */
1294 /* Return the registers used */
1300 static int RegAUsed (Line* Start)
1301 /* Check if the value in A is used. */
1303 return (RegValUsed (Start) & REG_A) != 0;
1308 static int RegXUsed (Line* Start)
1309 /* Check if the value in X is used. */
1311 return (RegValUsed (Start) & REG_X) != 0;
1316 static int RegYUsed (Line* Start)
1317 /* Check if the value in Y is used. */
1319 return (RegValUsed (Start) & REG_Y) != 0;
1324 /*****************************************************************************/
1325 /* Real optimizer routines */
1326 /*****************************************************************************/
1330 static void OptCompares1 (void)
1331 /* Try to optimize the integer compare subroutines. */
1333 Line* L2[10]; /* Line lookahead */
1334 int Cond; /* Condition to evaluate */
1335 unsigned Label; /* Local label number */
1336 unsigned Offs; /* Stack offset */
1337 Line* DelStart; /* First line to delete */
1339 Line* L = FirstCode;
1342 /* Search for compares of local byte sized variables. This looks
1354 * Replace it by a direct compare:
1362 if (LineMatch (L, "\tldy\t#$") &&
1363 GetNextCodeLines (L, L2, 7) &&
1364 LineFullMatch (L2[0], "\tldx\t#$00") &&
1365 LineFullMatch (L2[1], "\tlda\t(sp),y") &&
1366 LineFullMatch (L2[2], "\tjsr\tpushax") &&
1367 LineMatch (L2[3], "\tldy\t#$") &&
1368 LineFullMatch (L2[4], "\tldx\t#$00") &&
1369 LineFullMatch (L2[5], "\tlda\t(sp),y") &&
1370 (Cond = TosCmpFunc (L2[6])) >= 0) {
1372 /* Get the stack offset and correct it, since we will remove
1375 Offs = GetHexNum (L2[3]->Line+7) - 2;
1378 L = NewLineAfter (L, "\tlda\t(sp),y");
1379 L = NewLineAfter (L, "\tldy\t#$%02X", Offs);
1380 L = NewLineAfter (L, "\tcmp\t(sp),y");
1381 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1383 /* Remove the old cruft */
1384 FreeLines (L2[0], L2[6]);
1387 /* Compares of byte sized global variables */
1388 else if (LineFullMatch (L, "\tldx\t#$00") &&
1389 GetNextCodeLines (L, L2, 5) &&
1390 LineMatch (L2[0], "\tlda\t") &&
1391 LineFullMatch (L2[1], "\tjsr\tpushax") &&
1392 LineFullMatch (L2[2], "\tldx\t#$00") &&
1393 LineMatch (L2[3], "\tlda\t") &&
1394 (Cond = TosCmpFunc (L2[4])) >= 0) {
1397 if (IsXAddrMode (L2[0])) {
1398 /* The load is X indirect, so we may not remove the load
1399 * of the X register.
1404 L = ReplaceLine (L, L2[0]->Line);
1407 L = NewLineAfter (L, "\tcmp\t%s", L2[3]->Line+5);
1408 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1410 /* Remove the old cruft */
1411 FreeLines (DelStart, L2[4]);
1415 /* Byte sized local to global */
1416 else if (LineMatch (L, "\tldy\t#$") &&
1417 GetNextCodeLines (L, L2, 6) &&
1418 LineFullMatch (L2[0], "\tldx\t#$00") &&
1419 LineFullMatch (L2[1], "\tlda\t(sp),y") &&
1420 LineFullMatch (L2[2], "\tjsr\tpushax") &&
1421 LineFullMatch (L2[3], "\tldx\t#$00") &&
1422 LineMatch (L2[4], "\tlda\t") &&
1423 (Cond = TosCmpFunc (L2[5])) >= 0) {
1426 L = NewLineAfter (L, L2[1]->Line);
1427 L = NewLineAfter (L, "\tcmp\t%s", L2[4]->Line+5);
1428 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1430 /* Remove the old cruft */
1431 FreeLines (L2[0], L2[5]);
1435 /* Byte sized global to local */
1436 else if (LineFullMatch (L, "\tldx\t#$00") &&
1437 GetNextCodeLines (L, L2, 6) &&
1438 LineMatch (L2[0], "\tlda\t") &&
1439 LineFullMatch (L2[1], "\tjsr\tpushax") &&
1440 LineMatch (L2[2], "\tldy\t#$") &&
1441 LineFullMatch (L2[3], "\tldx\t#$00") &&
1442 LineFullMatch (L2[4], "\tlda\t(sp),y") &&
1443 (Cond = TosCmpFunc (L2[5])) >= 0) {
1445 /* Get the stack offset and correct it, since we will remove
1448 Offs = GetHexNum (L2[2]->Line+7) - 2;
1451 if (IsXAddrMode (L2[0])) {
1452 /* The load is X indirect, so we may not remove the load
1453 * of the X register.
1458 L = ReplaceLine (L, L2[0]->Line);
1461 L = NewLineAfter (L, "\tldy\t#$%02X", Offs);
1462 L = NewLineAfter (L, "\tcmp\t(sp),y");
1463 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1465 /* Remove the old cruft */
1466 FreeLines (DelStart, L2[5]);
1470 /* Search for unsigned compares against global variables. This looks
1478 * Replace that by a direct compare:
1486 else if (LineFullMatch (L, "\tjsr\tpushax") &&
1487 GetNextCodeLines (L, L2, 3) &&
1488 IsLoadAX (L2[0], L2[1]) &&
1489 (Cond = TosCmpFunc (L2[2])) >= 0 &&
1490 IsUnsignedCmp (Cond)) {
1492 /* Get a free label number */
1493 Label = AllocLabel ();
1495 /* Replace the code */
1496 L = ReplaceLine (L, "\tcpx\t%s", L2[1]->Line+5);
1497 L = NewLineAfter (L, "\tbne\tL%04X", Label);
1498 L = NewLineAfter (L, "\tcmp\t%s", L2[0]->Line+5);
1499 L = NewLabelAfter(L, Label);
1500 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1502 /* Remove the old code */
1503 FreeLines (L2[0], L2[2]);
1507 L = NextCodeLine (L);
1513 static void OptDeadJumps (void)
1514 /* Remove jumps to the following instruction */
1516 static const char* Jumps [] = {
1525 Line* L = FirstCode;
1528 /* Get a pointer to the next instruction line */
1529 Line* NextLine = NextInstruction (L);
1531 /* Is this line a jump? */
1532 int I = LineMatchX (L, Jumps);
1534 /* Yes. Get the target label, skip labels */
1535 Line* Target = NextInstruction (GetTargetLine (L->Line+5));
1537 /* If the target label is the next line, remove the jump */
1538 if (Target == NextLine) {
1543 /* Go to the next line */
1550 static void OptLoads (void)
1551 /* Remove unnecessary loads of values */
1558 Line* L = FirstCode;
1580 * This change will cost 3 cycles (one additional jump inside the
1581 * subroutine), but it saves a lot of code (6 bytes per occurrence),
1582 * so we will accept the overhead. It may even be possible to rewrite
1583 * the library routine to get rid of the additional overhead.
1585 if (LineMatch (L, "\tldy\t#$") &&
1586 GetNextCodeLines (L, L2, 5) &&
1587 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
1588 LineFullMatch (L2 [1], "\ttax") &&
1589 LineFullMatch (L2 [2], "\tdey") &&
1590 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
1591 LineFullMatch (L2 [4], "\tjsr\tpushax")) {
1593 /* Found - replace it */
1594 if (LineFullMatch (L, "\tldy\t#$01")) {
1595 /* Word at offset zero */
1597 L = ReplaceLine (L2 [4], "\tjsr\tpushw0sp");
1599 ReplaceLine (L2 [4], "\tjsr\tpushwysp");
1602 /* Delete the remaining lines */
1603 FreeLines (L2 [0], L2 [3]);
1605 /* We have changes */
1628 * This change will cost 2 cycles, but it saves a lot of code (6 bytes
1629 * per occurrence), so we will accept the overhead. It may even be
1630 * possible to rewrite the library routine to get rid of the additional
1633 } else if (LineMatch (L, "\tldy\t#$") &&
1634 GetNextCodeLines (L, L2, 6) &&
1635 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
1636 LineFullMatch (L2 [1], "\ttax") &&
1637 LineFullMatch (L2 [2], "\tdey") &&
1638 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
1639 LineMatch (L2 [4], "\tldy\t#$") &&
1640 LineFullMatch (L2 [5], "\tjsr\tldauidx")) {
1642 /* Found - replace it */
1643 L2 [4]->Line [3] = 'x'; /* Change to ldx */
1644 if (LineFullMatch (L, "\tldy\t#$01")) {
1645 /* Word at offset zero */
1647 L = ReplaceLine (L2 [5], "\tjsr\tldaui0sp");
1649 ReplaceLine (L2 [5], "\tjsr\tldauiysp");
1652 /* Delete the remaining lines */
1653 FreeLines (L2 [0], L2 [3]);
1655 /* We have changes */
1667 } else if (LineFullMatch (L, "\tlda\t(sp),y") &&
1668 GetNextCodeLines (L, L2, 1) &&
1669 LineFullMatch (L2 [0], "\tjsr\tpusha")) {
1671 /* Found, replace it */
1672 L = ReplaceLine (L, "\tjsr\tpushaysp");
1675 /* We have changes */
1685 * and replace it by:
1692 * provided that that the X register is not used later. While this is
1693 * no direct optimization, it helps with other optimizations.
1695 } else if (LineMatch (L, "\tldx\t") &&
1696 GetNextCodeLines (L, L2, 3) &&
1697 LineMatch (L2 [0], "\tlda\t") &&
1698 Is16BitStore (L2[1], L2[2]) &&
1699 !RegXUsed (L2[2])) {
1701 /* Found - replace it */
1703 NewLineAfter (L, "\tsta\t%s", L2[2]->Line+5);
1707 /* We have changes */
1717 * and replace it by:
1726 * provided that that the X register is not used later. This code
1727 * sequence is two bytes longer, but a lot faster and it does not
1728 * use the X register, so other loads may get removed later.
1730 } else if (LineMatch (L, "\tldx\t") &&
1731 GetNextCodeLines (L, L2, 3) &&
1732 LineMatch (L2 [0], "\tlda\t") &&
1733 LineMatch (L2 [1], "\tldy\t#$") &&
1734 LineFullMatch (L2 [2], "\tjsr\tstaxysp") &&
1735 !RegXUsed (L2[2])) {
1737 /* Found - replace it */
1739 L = NewLineAfter (L, "\tldy\t#$%02X", GetHexNum (L2[1]->Line+7)+1);
1740 L = NewLineAfter (L, "\tsta\t(sp),y");
1741 L = NewLineAfter (L, "\tdey");
1742 L = NewLineAfter (L2[0], "\tsta\t(sp),y");
1744 /* Remove the remaining lines */
1745 FreeLines (L2[1], L2[2]);
1747 /* We have changes */
1756 * and replace it by:
1765 * provided that that the X register is not used later. This code
1766 * sequence is four bytes longer, but a lot faster and it does not
1767 * use the X register, so other loads may get removed later.
1769 } else if (LineMatch (L, "\tldx\t") &&
1770 GetNextCodeLines (L, L2, 2) &&
1771 LineMatch (L2 [0], "\tlda\t") &&
1772 LineFullMatch (L2 [1], "\tjsr\tstax0sp") &&
1773 !RegXUsed (L2[1])) {
1775 /* Found - replace it */
1777 L = NewLineAfter (L, "\tldy\t#$01");
1778 L = NewLineAfter (L, "\tsta\t(sp),y");
1779 L = NewLineAfter (L, "\tdey");
1780 L = NewLineAfter (L2[0], "\tsta\t(sp),y");
1782 /* Remove the remaining line */
1785 /* We have changes */
1794 * Remove the handling of the high byte if the X register
1795 * is not used any more
1797 } else if (LineMatch (L, "\tadc\t") &&
1798 GetNextCodeLines (L, L2, 3) &&
1799 LineFullMatch (L2[0], "\tbcc\t*+3") &&
1800 LineFullMatch (L2[1], "\tinx") &&
1802 IsHint (L2[1]->Next, "x:!") &&
1803 !RegXUsed (L2[1])) {
1805 /* Delete the lines */
1806 FreeLines (L2[0], L2[1]->Next);
1808 /* We have changes */
1817 * Remove the handling of the high byte if the X register
1818 * is not used any more
1820 } else if (LineMatch (L, "\tsbc\t") &&
1821 GetNextCodeLines (L, L2, 3) &&
1822 LineFullMatch (L2[0], "\tbcs\t*+3") &&
1823 LineFullMatch (L2[1], "\tdex") &&
1825 IsHint (L2[1]->Next, "x:!") &&
1826 !RegXUsed (L2[1])) {
1828 /* Delete the lines */
1829 FreeLines (L2[0], L2[1]->Next);
1831 /* We have changes */
1840 * Remove the handling of the high byte if the X register
1841 * is not used any more
1843 } else if (LineMatch (L, "\tlda\t") &&
1844 GetNextCodeLines (L, L2, 3) &&
1845 LineFullMatch (L2[0], "\tbpl\t*+3") &&
1846 LineFullMatch (L2[1], "\tdex") &&
1848 IsHint (L2[1]->Next, "x:!") &&
1849 !RegXUsed (L2[1])) {
1851 /* Delete the lines */
1852 FreeLines (L2[0], L2[1]->Next);
1854 /* We have changes */
1859 /* All other patterns start with this one: */
1860 if (!LineFullMatch (L, "\tldx\t#$00")) {
1870 * and replace it by:
1875 if (GetNextCodeLines (L, L2, 1) &&
1876 LineFullMatch (L2 [0], "\tjsr\tpushax")) {
1878 /* Replace the subroutine call */
1879 L = ReplaceLine (L, "\tjsr\tpusha0");
1881 /* Remove the unnecessary line */
1884 /* We have changes */
1894 * and replace it by:
1900 else if (GetNextCodeLines (L, L2, 2) &&
1901 LineMatch (L2 [0], "\tlda\t") &&
1902 LineFullMatch (L2 [1], "\tjsr\tpushax")) {
1904 /* Be sure, X is not used in the load */
1905 if (NoXAddrMode (L2 [0])) {
1907 /* Replace the subroutine call */
1908 L2 [1] = ReplaceLine (L2 [1], "\tjsr\tpusha0");
1910 /* Remove the unnecessary load */
1913 /* L must be valid */
1916 /* We have changes */
1928 * and replace it by:
1933 else if (GetNextCodeLines (L, L2, 2) &&
1934 LineMatch (L2 [0], "\tlda\t") &&
1935 LineMatch (L2 [1], "\tcmp\t#$")) {
1937 /* Be sure, X is not used in the load */
1938 if (NoXAddrMode (L2 [0])) {
1940 /* Remove the unnecessary load */
1943 /* L must be valid */
1946 /* We have changes */
1957 * and replace it by:
1962 else if (GetNextCodeLines (L, L2, 2) &&
1963 LineMatch (L2 [0], "\tlda\t") &&
1964 LineFullMatch (L2 [1], "\tjsr\tbnega")) {
1966 /* Be sure, X is not used in the load */
1967 if (NoXAddrMode (L2 [0])) {
1969 /* Remove the unnecessary load */
1972 /* L must be valid */
1975 /* We have changes */
1981 /* Go to the next line */
1982 L = NextCodeLine (L);
1990 static void OptRegLoads (void)
1991 /* Remove unnecessary loads of registers */
1997 /* Repeat this until there is nothing more to delete */
2005 /* Search for a load of X and check if the value is used later */
2006 if (LineMatch (L, "\tldx\t") &&
2008 !IsCondJump (NextInstruction (L))) {
2010 /* Remember to delete this line */
2014 /* Search for a load of A and check if the value is used later */
2015 else if (LineMatch (L, "\tlda\t") &&
2017 !IsCondJump (NextInstruction (L))) {
2019 /* Remember to delete this line */
2023 /* Search for a load of Y and check if the value is used later */
2024 else if (LineMatch (L, "\tldy\t") &&
2026 !IsCondJump (NextInstruction (L))) {
2028 /* Remember to delete this line */
2032 /* Go to the next line, delete the current if requested */
2034 L = NextCodeLine (L);
2040 } while (Deletions > 0);
2045 static int OptPtrOps1 (Line** Start)
2046 /* Optimize several pointer and array constructs - subfunction 1 */
2051 unsigned LinesToRemove;
2056 /* Use a local variable for the working line */
2059 /* Search for (23B/XXT)
2071 * and replace it by something like (24B/26T)
2085 if (!LineMatch (L, "\tlda\t") ||
2086 !GetNextCodeLines (L, L2, 4) ||
2087 !IsLoadAX (L, L2 [0]) ||
2088 !LineFullMatch (L2[1], "\tsta\tregsave") ||
2089 !LineFullMatch (L2[2], "\tstx\tregsave+1")) {
2096 if (LineMatch (L2[3], "\tjsr\tincax")) {
2097 /* Get next code lines */
2098 if (GetNextCodeLines (L2[3], &L2[4], 4) == 0) {
2099 /* Cannot get lines */
2102 Inc = GetHexNum (L2[3]->Line+10);
2106 /* Get next code lines */
2107 if (GetNextCodeLines (L2[3], &L2[4], 7) == 0) {
2108 /* Cannot get lines */
2111 if (LineFullMatch (L2[3], "\tclc") &&
2112 LineMatch (L2[4], "\tadc\t#$") &&
2113 LineFullMatch (L2[5], "\tbcc\t*+3") &&
2114 LineFullMatch (L2[6], "\tinx")) {
2115 /* Inlined increment */
2116 Inc = GetHexNum (L2[4]->Line+7);
2125 /* Check for the remainder */
2126 if (!LineMatch (L3[0], "\tsta\t") ||
2127 strcmp (L3[0]->Line+5, L->Line+5) != 0 ||
2128 !LineMatch (L3[1], "\tstx\t") ||
2129 strcmp (L3[1]->Line+5, L2[0]->Line+5) != 0 ||
2130 !LineFullMatch (L3[2], "\tlda\tregsave") ||
2131 !LineFullMatch (L3[3], "\tldx\tregsave+1")) {
2137 /* Check if AX is actually used following the code above. If not,
2138 * we don't need to load A/X from regsave. Since X will never be
2139 * used without A, check just for A.
2141 NeedLoad = RegAUsed (L3[3]);
2143 /* Special code for register variables */
2145 if (LineMatch (L, "\tlda\tregbank+") &&
2146 GetNextCodeLines (L3[3], &L3[4], 1) &&
2149 /* Remember the offset into the register bank */
2151 strcpy (Reg, L->Line+5);
2153 /* Check for several special sequences */
2154 if (LineFullMatch (L3[4], "\tjsr\tldaui")) {
2155 /* Load char indirect */
2156 L = ReplaceLine (L, "\tldx\t#$00");
2157 L = NewLineAfter (L, "\tlda\t(%s,x)", Reg);
2158 L = NewLineAfter (L, "\tinc\t%s", Reg);
2159 L = NewLineAfter (L, "\tbne\t*+4");
2160 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
2163 } else if (LineFullMatch (L3[4], "\tsta\tptr1") &&
2164 GetNextCodeLines (L3[4], &L3[5], 3) &&
2165 LineFullMatch (L3[5], "\tstx\tptr1+1") &&
2166 LineFullMatch (L3[6], "\tldx\t#$00") &&
2167 LineFullMatch (L3[7], "\tlda\t(ptr1,x)")) {
2169 /* Load char indirect, inlined */
2170 L = ReplaceLine (L, "\tldx\t#$00");
2171 L = NewLineAfter (L, "\tlda\t(%s,x)", Reg);
2172 L = NewLineAfter (L, "\tinc\t%s", Reg);
2173 L = NewLineAfter (L, "\tbne\t*+4");
2174 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
2178 } else if (LineFullMatch (L3[4], "\tjsr\tpushax")) {
2179 if (GetNextCodeLines (L3[4], &L3[5], 2) &&
2180 LineMatch (L3[5], "\tlda\t") &&
2181 LineFullMatch (L3[6], "\tjsr\tstaspp")) {
2183 /* Store to pointer */
2184 L = ReplaceLine (L, L3[5]->Line);
2185 L = NewLineAfter (L, "\tldy\t#$00");
2186 L = NewLineAfter (L, "\tsta\t(%s),y", Reg);
2187 L = NewLineAfter (L, "\tinc\t%s", Reg);
2188 L = NewLineAfter (L, "\tbne\t*+4");
2189 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
2194 } else if (GetNextCodeLines (L3[4], &L3[5], 3) &&
2195 LineMatch (L3[5], "\tldy\t#$") &&
2196 LineFullMatch (L3[6], "\tlda\t(sp),y") &&
2197 LineFullMatch (L3[7], "\tjsr\tstaspp")) {
2199 /* Beware: We have to correct the stack offset, since we will
2200 * remove the pushax instruction!
2202 Offs = GetHexNum (L3[5]->Line+7) - 2;
2204 /* Store to pointer */
2205 L = ReplaceLine (L, "\tldy\t#$%02X", Offs);
2206 L = NewLineAfter (L, "\tldx\t#$00");
2207 L = NewLineAfter (L, "\tlda\t(sp),y");
2208 L = NewLineAfter (L, "\tsta\t(%s,x)", Reg);
2209 L = NewLineAfter (L, "\tinc\t%s", Reg);
2210 L = NewLineAfter (L, "\tbne\t*+4");
2211 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
2221 /* No register variable - insert the first part of the code */
2223 L = NewLineAfter (L, "\tsta\tptr1");
2225 L = NewLineAfter (L, "\tclc");
2226 L = NewLineAfter (L, "\tadc\t#$%02X", Inc);
2227 L = NewLineAfter (L, "\tsta\t%s", L3[0]->Line+5);
2228 L = NewLineAfter (L, "\tlda\t%s", L3[1]->Line+5);
2230 L = NewLineAfter (L, "\tsta\tptr1+1");
2232 L = NewLineAfter (L, "\tadc\t#$00");
2233 L = NewLineAfter (L, "\tsta\t%s", L3[1]->Line+5);
2235 /* Check if we must really load the old value into a/x or if the
2236 * code may be replaced by something else.
2238 if (GetNextCodeLines (L3[3], &L3[4], 1)) {
2239 if (LineFullMatch (L3[4], "\tjsr\tldaui")) {
2240 /* Load char indirect */
2241 L = NewLineAfter (L, "\tldx\t#$00");
2242 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2245 } else if (LineFullMatch (L3[4], "\tsta\tptr1") &&
2246 GetNextCodeLines (L3[4], &L3[5], 3) &&
2247 LineFullMatch (L3[5], "\tstx\tptr1+1") &&
2248 LineFullMatch (L3[6], "\tldx\t#$00") &&
2249 LineFullMatch (L3[7], "\tlda\t(ptr1,x)")) {
2251 /* Load char indirect, inlined */
2252 L = NewLineAfter (L, "\tldx\t#$00");
2253 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2257 } else if (LineFullMatch (L3[4], "\tjsr\tldaxi")) {
2258 /* Load word indirect */
2259 L = NewLineAfter (L, "\tldy\t#$01");
2260 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2261 L = NewLineAfter (L, "\ttax");
2262 L = NewLineAfter (L, "\tdey");
2263 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2267 } else if (LineFullMatch (L3[4], "\tjsr\tpushax")) {
2268 if (GetNextCodeLines (L3[4], &L3[5], 2) &&
2269 LineMatch (L3[5], "\tlda\t") &&
2270 LineFullMatch (L3[6], "\tjsr\tstaspp")) {
2272 /* Store to pointer */
2273 L = NewLineAfter (L, L3[5]->Line);
2274 L = NewLineAfter (L, "\tldy\t#$00");
2275 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2279 } else if (GetNextCodeLines (L3[4], &L3[5], 3) &&
2280 LineMatch (L3[5], "\tldy\t#$") &&
2281 LineFullMatch (L3[6], "\tlda\t(sp),y") &&
2282 LineFullMatch (L3[7], "\tjsr\tstaspp")) {
2284 /* Beware: We have to correct the stack offset, since we will
2285 * remove the pushax instruction!
2287 sprintf (L3[5]->Line+7, "%02X", GetHexNum (L3[5]->Line+7)-2);
2289 /* Store to pointer */
2290 L = NewLineAfter (L, L3[5]->Line);
2291 L = NewLineAfter (L, L3[6]->Line);
2292 L = NewLineAfter (L, "\tldy\t#$00");
2293 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2302 /* If we need to load a/x, add the code */
2304 L = NewLineAfter (L, "\tlda\tptr1");
2305 L = NewLineAfter (L, "\tldx\tptr1+1");
2309 /* Remove the code that is no longer needed */
2310 FreeLines (L2[0], L2[LinesToRemove-1]);
2312 /* Return the new line and success */
2313 *Start = NextCodeLine (L);
2319 static int OptPtrOps2 (Line** Start)
2320 /* Optimize several pointer and array constructs - subfunction 2 */
2325 unsigned LinesToRemove;
2330 /* Use a local variable for the working line */
2333 /* Same as subfunction 1 but for local variables. */
2334 if (LineMatch (L, "\tldy\t#$") == 0) {
2338 /* Get the stack offset. The offset points to the high byte, correct that. */
2339 Offs = GetHexNum (L->Line+7) - 1;
2341 /* Check for the actual sequences */
2342 if (GetNextCodeLines (L, L2, 7) &&
2343 LineFullMatch (L2[0], "\tjsr\tldaxysp") &&
2344 LineFullMatch (L2[1], "\tsta\tregsave") &&
2345 LineFullMatch (L2[2], "\tstx\tregsave+1") &&
2346 LineMatch (L2[3], "\tjsr\tincax")) {
2348 /* Non inlined version */
2349 Inc = GetHexNum (L2[3]->Line+10);
2351 /* Check for stack offset zero */
2352 if (LineFullMatch (L2[4], "\tjsr\tstax0sp") &&
2353 LineFullMatch (L2[5], "\tlda\tregsave") &&
2354 LineFullMatch (L2[6], "\tldx\tregsave+1")) {
2358 } else if (GetNextCodeLines (L2[6], &L2[7], 1) &&
2359 LineMatch (L2[4], "\tldy\t#$") &&
2360 GetHexNum (L2[4]->Line+7) == Offs &&
2361 LineFullMatch (L2[5], "\tjsr\tstaxysp") &&
2362 LineFullMatch (L2[6], "\tlda\tregsave") &&
2363 LineFullMatch (L2[7], "\tldx\tregsave+1")) {
2372 } else if (GetNextCodeLines (L, L2, 13) &&
2373 LineFullMatch (L2[0], "\tlda\t(sp),y") &&
2374 LineFullMatch (L2[1], "\ttax") &&
2375 LineFullMatch (L2[2], "\tdey") &&
2376 LineFullMatch (L2[3], "\tlda\t(sp),y") &&
2377 LineFullMatch (L2[4], "\tsta\tregsave") &&
2378 LineFullMatch (L2[5], "\tstx\tregsave+1") &&
2379 LineFullMatch (L2[6], "\tclc") &&
2380 LineMatch (L2[7], "\tadc\t#$") &&
2381 LineFullMatch (L2[8], "\tbcc\t*+3") &&
2382 LineFullMatch (L2[9], "\tinx")) {
2384 /* Inlined version */
2385 Inc = GetHexNum (L2[7]->Line+7);
2387 /* Check for stack offset zero */
2388 if (LineFullMatch (L2[10], "\tjsr\tstax0sp") &&
2389 LineFullMatch (L2[11], "\tlda\tregsave") &&
2390 LineFullMatch (L2[12], "\tldx\tregsave+1")) {
2394 } else if (GetNextCodeLines (L2[12], &L2[13], 1) &&
2395 LineMatch (L2[10], "\tldy\t#$") &&
2396 GetHexNum (L2[10]->Line+7) == Offs &&
2397 LineFullMatch (L2[11], "\tjsr\tstaxysp") &&
2398 LineFullMatch (L2[12], "\tlda\tregsave") &&
2399 LineFullMatch (L2[13], "\tldx\tregsave+1")) {
2412 /* Get a pointer to the last line of the preceding sequence */
2413 L3 = &L2[LinesToRemove-1];
2415 /* Check if AX is actually used following the code above. If not,
2416 * we don't need to load A/X from regsave. Since X will never by
2417 * used without A, check just for A.
2419 NeedLoad = RegAUsed (L3[0]);
2421 /* Replace the ldy instruction, offset must point to the low byte */
2422 sprintf (L->Line+7, "%02X", Offs);
2424 /* Insert the first part of the code */
2425 L = NewLineAfter (L, "\tlda\t(sp),y");
2427 L = NewLineAfter (L, "\tsta\tptr1");
2429 L = NewLineAfter (L, "\tclc");
2430 L = NewLineAfter (L, "\tadc\t#$%02X", Inc);
2431 L = NewLineAfter (L, "\tsta\t(sp),y");
2432 L = NewLineAfter (L, "\tiny");
2433 L = NewLineAfter (L, "\tlda\t(sp),y");
2435 L = NewLineAfter (L, "\tsta\tptr1+1");
2437 L = NewLineAfter (L, "\tadc\t#$00");
2438 L = NewLineAfter (L, "\tsta\t(sp),y");
2440 /* Check if we must really load the old value into a/x or if the
2441 * code may be replaced by something else.
2443 if (GetNextCodeLines (L3[0], &L3[1], 1)) {
2444 if (LineFullMatch (L3[1], "\tjsr\tldaui")) {
2445 /* Load char indirect */
2446 L = NewLineAfter (L, "\tldx\t#$00");
2447 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2450 } else if (LineFullMatch (L3[1], "\tsta\tptr1") &&
2451 GetNextCodeLines (L3[1], &L3[2], 3) &&
2452 LineFullMatch (L3[2], "\tstx\tptr1+1") &&
2453 LineFullMatch (L3[3], "\tldx\t#$00") &&
2454 LineFullMatch (L3[4], "\tlda\t(ptr1,x)")) {
2456 /* Load char indirect, inlined */
2457 L = NewLineAfter (L, "\tldx\t#$00");
2458 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2462 } else if (LineFullMatch (L3[1], "\tjsr\tldaxi")) {
2463 /* Load word indirect */
2464 L = NewLineAfter (L, "\tldy\t#$01");
2465 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2466 L = NewLineAfter (L, "\ttax");
2467 L = NewLineAfter (L, "\tdey");
2468 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2472 } else if (LineFullMatch (L3[1], "\tjsr\tpushax")) {
2473 if (GetNextCodeLines (L3[1], &L3[2], 2) &&
2474 LineMatch (L3[2], "\tlda\t") &&
2475 LineFullMatch (L3[3], "\tjsr\tstaspp")) {
2477 /* Store to pointer */
2478 L = NewLineAfter (L, L3[2]->Line);
2479 L = NewLineAfter (L, "\tldy\t#$00");
2480 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2484 } else if (GetNextCodeLines (L3[1], &L3[2], 3) &&
2485 LineMatch (L3[2], "\tldy\t#$") &&
2486 LineFullMatch (L3[3], "\tlda\t(sp),y") &&
2487 LineFullMatch (L3[4], "\tjsr\tstaspp")) {
2489 /* Beware: We have to correct the stack offset, since we will
2490 * remove the pushax instruction!
2492 sprintf (L3[2]->Line+7, "%02X", GetHexNum (L3[2]->Line+7)-2);
2494 /* Store to pointer */
2495 L = NewLineAfter (L, L3[2]->Line);
2496 L = NewLineAfter (L, L3[3]->Line);
2497 L = NewLineAfter (L, "\tldy\t#$00");
2498 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2507 /* If we need to load a/x, add the code */
2509 L = NewLineAfter (L, "\tlda\tptr1");
2510 L = NewLineAfter (L, "\tldx\tptr1+1");
2513 /* Remove the code that is no longer needed */
2514 FreeLines (L2[0], L2[LinesToRemove-1]);
2516 /* Return the new line and success */
2517 *Start = NextCodeLine (L);
2523 static void OptPtrOps (void)
2524 /* Optimize several pointer and array constructs */
2528 Line* L = FirstCode;
2531 if (OptPtrOps1 (&L)) {
2533 } else if (OptPtrOps2 (&L)) {
2537 /* Search for the following sequence:
2545 * and replace it by:
2552 else if (LineFullMatch (L, "\tlda\tregsave") && /* Match on start */
2553 GetNextCodeLines (L, L2, 4) && /* Fetch next lines */
2554 LineFullMatch (L2 [0], "\tldx\tregsave+1") && /* Match line 2 ... */
2555 LineFullMatch (L2 [1], "\tjsr\tpushax") &&
2556 LineMatch (L2 [2], "\tlda\t#$") &&
2557 LineFullMatch (L2 [3], "\tjsr\tstaspp")) {
2559 /* Found the sequence, replace it */
2560 L = ReplaceLine (L, L2 [2]->Line); /* lda #$.. */
2561 L2 [0] = ReplaceLine (L2 [0], "\tldy\t#$00");
2562 L2 [1] = ReplaceLine (L2 [1], "\tsta\t(regsave),y");
2564 /* Free the remaining lines */
2565 FreeLines (L2 [2], L2 [3]);
2568 /* Search for the following sequence:
2574 * and replace it by:
2580 else if (LineFullMatch (L, "\tlda\tregsave") && /* Match on start */
2581 GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
2582 LineFullMatch (L2 [0], "\tldx\tregsave+1") && /* Match line 2 ... */
2583 LineFullMatch (L2 [1], "\tjsr\tldaui")) {
2585 /* Found the sequence, replace it */
2586 L = ReplaceLine (L, "\tldx\t#$00");
2587 L2 [0] = ReplaceLine (L2 [0], "\tlda\t(regsave,x)");
2589 /* Free the remaining lines */
2594 * Search for the following sequence:
2603 * and replace it by:
2614 else if (LineFullMatch (L, "\tlda\tregsave") &&
2615 GetNextCodeLines (L, L2, 5) &&
2616 LineFullMatch (L2 [0], "\tldx\tregsave+1") &&
2617 LineFullMatch (L2 [1], "\tjsr\tpushax") &&
2618 LineMatch (L2 [2], "\tldx\t#$") &&
2619 LineMatch (L2 [3], "\tlda\t#$") &&
2620 LineFullMatch (L2 [4], "\tjsr\tstaxspp")) {
2622 /* Found the sequence, replace it */
2623 L = ReplaceLine (L, "\tldy\t#$01");
2624 L2 [0] = ReplaceLine (L2 [0], L2 [2]->Line);
2625 L2 [0]->Line [3] = 'a';
2626 L2 [1] = ReplaceLine (L2 [1], "\tsta\t(regsave),y");
2627 L2 [4] = ReplaceLine (L2 [4], L2 [3]->Line);
2628 L2 [2] = ReplaceLine (L2 [2], "\ttax");
2629 L2 [3] = ReplaceLine (L2 [3], "\tdey");
2630 L = NewLineAfter (L2 [4], "\tsta\t(regsave),y");
2634 * Search for the following sequence:
2643 * and replace it by:
2649 else if (LineFullMatch (L, "\tlda\tregsave") &&
2650 GetNextCodeLines (L, L2, 5) &&
2651 LineFullMatch (L2 [0], "\tldx\tregsave+1") &&
2652 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2653 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2654 LineFullMatch (L2 [3], "\tldx\t#$00") &&
2655 LineFullMatch (L2 [4], "\tlda\t(ptr1,x)")) {
2657 /* Found the sequence, replace it */
2658 L = ReplaceLine (L, "\tldx\t#$00");
2659 L2 [0] = ReplaceLine (L2 [0], "\tlda\t(regsave,x)");
2661 /* Remove the remaining lines */
2662 FreeLines (L2 [1], L2 [4]);
2665 /* Search for the following sequence:
2671 * and replace it by:
2680 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2681 GetNextCodeLines (L, L2, 2) &&
2682 LineMatch (L2 [0], "\tlda\t") &&
2683 LineFullMatch (L2 [1], "\tjsr\tstaspp")) {
2685 /* Found the sequence, replace it */
2686 L = ReplaceLine (L, "\tsta\tptr1");
2687 L2 [1] = ReplaceLine (L2 [1], L2 [0]->Line); /* lda ... */
2688 L2 [0] = ReplaceLine (L2 [0], "\tstx\tptr1+1");
2689 L2 [2] = NewLineAfter (L2 [1], "\tldy\t#$00");
2690 L = NewLineAfter (L2 [2], "\tsta\t(ptr1),y");
2693 /* Search for the following sequence:
2700 * and replace it by:
2709 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2710 GetNextCodeLines (L, L2, 3) &&
2711 LineMatch (L2 [0], "\tlda\t") &&
2712 LineMatch (L2 [1], "\tldy\t#$") &&
2713 LineFullMatch (L2 [2], "\tjsr\tstaspidx")) {
2715 /* Found the sequence, replace it */
2716 L = ReplaceLine (L, "\tsta\tptr1");
2717 L = NewLineAfter (L, "\tstx\tptr1+1");
2718 L2 [2] = ReplaceLine (L2 [2], "\tsta\t(ptr1),y");
2721 /* Search for the following sequence:
2728 * and replace it by:
2737 * Beware: Since we remove a call to a function that changes the stack
2738 * pointer, we have to adjust the stack address for the lda.
2741 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2742 GetNextCodeLines (L, L2, 3) &&
2743 LineMatch (L2 [0], "\tldy\t#$") &&
2744 LineFullMatch (L2 [1], "\tlda\t(sp),y") &&
2745 LineFullMatch (L2 [2], "\tjsr\tstaspp")) {
2747 /* Found the sequence, replace it. First create a new load
2748 * instruction for the changed stack offset.
2751 sprintf (Buf, "\tldy\t#$%02X", GetHexNum (L2 [0]->Line+7) - 2);
2752 L = ReplaceLine (L, "\tsta\tptr1");
2753 L2 [1] = ReplaceLine (L2 [1], Buf); /* ldy ... */
2754 L2 [0] = ReplaceLine (L2 [0], "\tstx\tptr1+1");
2755 L2 [2] = ReplaceLine (L2 [2], "\tlda\t(sp),y");
2756 L2 [3] = NewLineAfter (L2 [2], "\tldy\t#$00");
2757 L = NewLineAfter (L2 [3], "\tsta\t(ptr1),y");
2760 /* Search for the following sequence:
2768 * and replace it by:
2777 * Beware: Since we remove a call to a function that changes the stack
2778 * pointer, we have to adjust the stack address for the lda.
2781 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2782 GetNextCodeLines (L, L2, 4) &&
2783 LineMatch (L2 [0], "\tldy\t#$") &&
2784 LineFullMatch (L2 [1], "\tlda\t(sp),y") &&
2785 LineMatch (L2 [2], "\tldy\t#$") &&
2786 LineFullMatch (L2 [3], "\tjsr\tstaspidx")) {
2788 /* Found the sequence, replace it. First create a new load
2789 * instruction for the changed stack offset.
2792 sprintf (Buf, "\tldy\t#$%02X", GetHexNum (L2 [0]->Line+7) - 2);
2793 L = ReplaceLine (L, "\tsta\tptr1");
2794 L = NewLineAfter (L, "\tstx\tptr1+1");
2795 L2 [0] = ReplaceLine (L2 [0], Buf); /* ldy ... */
2796 L2 [3] = ReplaceLine (L2 [3], "\tsta\t(ptr1),y");
2799 /* Search for the following sequence:
2812 * and replace it by:
2820 * The load of X may be omitted if X is not used below.
2822 else if (LineMatch (L, "\tldax\t_") &&
2823 GetNextCodeLines (L, L2, 9) &&
2824 LineMatch (L2 [0], "\tldy\t#$") &&
2825 LineFullMatch (L2 [1], "\tclc") &&
2826 LineFullMatch (L2 [2], "\tadc\t(sp),y") &&
2827 LineFullMatch (L2 [3], "\tbcc\t*+3") &&
2828 LineFullMatch (L2 [4], "\tinx") &&
2829 LineFullMatch (L2 [5], "\tsta\tptr1") &&
2830 LineFullMatch (L2 [6], "\tstx\tptr1+1") &&
2831 LineFullMatch (L2 [7], "\tldx\t#$00") &&
2832 LineFullMatch (L2 [8], "\tlda\t(ptr1,x)")) {
2834 /* Found the sequence, replace it */
2836 strcpy (Label, L->Line + 6); /* Remember the label */
2837 L = ReplaceLine (L, L2 [0]->Line); /* ldy .. */
2838 L = NewLineAfter (L, "\tlda\t(sp),y");
2839 L = NewLineAfter (L, "\ttay");
2840 if (RegXUsed (L2[8])) {
2841 L = NewLineAfter (L, "\tldx\t#$00");
2843 L = NewLineAfter (L, "\tlda\t%s,y", Label);
2845 /* Remove the remaining stuff. There may be hints between the
2846 * instructions, remove them too
2848 FreeLines (L2[0], L2 [8]);
2872 * This change will cost 2 cycles, but it saves a lot of code (6 bytes
2873 * per occurrence), so we will accept the overhead. It may even be
2874 * possible to rewrite the library routine to get rid of the additional
2877 else if (LineMatch (L, "\tldy\t#$") &&
2878 GetNextCodeLines (L, L2, 6) &&
2879 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
2880 LineFullMatch (L2 [1], "\ttax") &&
2881 LineFullMatch (L2 [2], "\tdey") &&
2882 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
2883 LineMatch (L2 [4], "\tldy\t#$") &&
2884 LineFullMatch (L2 [5], "\tjsr\tldauidx")) {
2886 /* Found - replace it */
2887 L2 [4]->Line [3] = 'x'; /* Change to ldx */
2888 if (LineFullMatch (L, "\tldy\t#$01")) {
2889 /* Word at offset zero */
2891 L = ReplaceLine (L2 [5], "\tjsr\tldaui0sp");
2893 ReplaceLine (L2 [5], "\tjsr\tldauiysp");
2896 /* Delete the remaining lines */
2897 FreeLines (L2 [0], L2 [3]);
2921 * This change will has an overhead of 10 cycles, but it saves 11(!)
2922 * bytes per invocation. Maybe we should apply only if FavourSize is
2925 else if (LineMatch (L, "\tldy\t#$") &&
2926 GetNextCodeLines (L, L2, 8) &&
2927 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
2928 LineFullMatch (L2 [1], "\ttax") &&
2929 LineFullMatch (L2 [2], "\tdey") &&
2930 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
2931 LineFullMatch (L2 [4], "\tsta\tptr1") &&
2932 LineFullMatch (L2 [5], "\tstx\tptr1+1") &&
2933 LineFullMatch (L2 [6], "\tldx\t#$00") &&
2934 LineFullMatch (L2 [7], "\tlda\t(ptr1,x)")) {
2936 /* Found - replace it */
2937 if (LineFullMatch (L, "\tldy\t#$01")) {
2938 /* Word at offset zero */
2940 L = ReplaceLine (L2 [0], "\tjsr\tldau00sp");
2942 ReplaceLine (L2 [0], "\tjsr\tldau0ysp");
2945 /* Delete the remaining lines */
2946 FreeLines (L2 [1], L2 [7]);
2968 * Provided that X is not used later.
2970 else if (LineMatch (L, "\tldy\t#$") &&
2971 GetNextCodeLines (L, L2, 6) &&
2972 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
2973 LineFullMatch (L2 [1], "\ttax") &&
2974 LineFullMatch (L2 [2], "\tdey") &&
2975 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
2976 Is16BitStore (L2[4], L2[5]) &&
2977 !RegXUsed (L2[5])) {
2979 /* Found - replace it */
2980 L2[1] = ReplaceLine (L2[1], L2[5]->Line);
2981 L2[1]->Line[3] = 'a';
2983 /* Delete the remaining lines */
2986 /* Start over at last line */
2991 L = NextCodeLine (L);
2997 static void OptRegVars (void)
2998 /* Optimize register variable uses */
3002 Line* L = FirstCode;
3005 /* Search for the following sequence:
3011 * and replace it by:
3017 if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
3018 GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
3019 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
3020 LineFullMatch (L2 [1], "\tjsr\tldaui") &&
3021 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
3022 strcmp (L2 [0]->Line + 14, "+1") == 0) {
3025 sprintf (Buf, "\tlda\t(%s,x)", L->Line + 5);
3027 /* Found the sequence, replace it */
3028 L = ReplaceLine (L, "\tldx\t#$00");
3029 L2 [0] = ReplaceLine (L2 [0], Buf);
3031 /* Free the remaining lines */
3035 /* Search for the following sequence:
3044 * and replace it by:
3050 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
3051 GetNextCodeLines (L, L2, 5) && /* Fetch next lines */
3052 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
3053 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
3054 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
3055 LineFullMatch (L2 [1], "\tsta\tptr1") &&
3056 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
3057 LineFullMatch (L2 [3], "\tldx\t#$00") &&
3058 LineFullMatch (L2 [4], "\tlda\t(ptr1,x)")) {
3061 sprintf (Buf, "\tlda\t(%s,x)", L->Line + 5);
3063 /* Found the sequence, replace it */
3064 L = ReplaceLine (L, "\tldx\t#$00");
3065 L2 [0] = ReplaceLine (L2 [0], Buf);
3067 /* Free the remaining lines */
3068 FreeLines (L2 [1], L2 [4]);
3071 /* Search for the following sequence:
3078 * and replace it by:
3085 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
3086 GetNextCodeLines (L, L2, 3) && /* Fetch next lines */
3087 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
3088 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
3089 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
3090 LineMatch (L2 [1], "\tldy\t#$") &&
3091 LineFullMatch (L2 [2], "\tjsr\tldauidx")) {
3094 sprintf (Buf, "\tlda\t(%s),y", L->Line + 5);
3096 /* Found the sequence, replace it */
3097 L = ReplaceLine (L, L2 [1]->Line);
3098 L2 [0] = ReplaceLine (L2 [0], "\tldx\t#$00");
3099 L2 [1] = ReplaceLine (L2 [1], Buf);
3101 /* Free the remaining lines */
3105 /* Search for the following sequence:
3115 * and replace it by:
3121 * The source form is not generated by the parser but by the optimizer.
3123 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
3124 GetNextCodeLines (L, L2, 6) && /* Fetch next lines */
3125 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
3126 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
3127 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
3128 LineFullMatch (L2 [1], "\tsta\tptr1") &&
3129 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
3130 LineMatch (L2 [3], "\tlda\t") &&
3131 LineMatch (L2 [4], "\tldy\t#$") &&
3132 LineMatch (L2 [5], "\tsta\t(ptr1),y")) {
3135 sprintf (Buf, "\tsta\t(%s),y", L->Line + 5);
3137 /* Found the sequence, replace it */
3138 L2 [5] = ReplaceLine (L2 [5], Buf);
3140 /* Free the remaining lines */
3141 FreeLines (L, L2 [2]);
3143 /* Make the line pointer valid again */
3147 /* Search for the following sequence:
3158 * and replace it by:
3165 * The source form is not generated by the parser but by the optimizer.
3167 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
3168 GetNextCodeLines (L, L2, 7) && /* Fetch next lines */
3169 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
3170 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
3171 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
3172 LineFullMatch (L2 [1], "\tsta\tptr1") &&
3173 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
3174 LineMatch (L2 [3], "\tldy\t#$") &&
3175 LineFullMatch (L2 [4], "\tlda\t(sp),y") &&
3176 LineMatch (L2 [5], "\tldy\t#$") &&
3177 LineMatch (L2 [6], "\tsta\t(ptr1),y")) {
3180 sprintf (Buf, "\tsta\t(%s),y", L->Line + 5);
3182 /* Found the sequence, replace it */
3183 L2 [6] = ReplaceLine (L2 [6], Buf);
3185 /* Free the remaining lines */
3186 FreeLines (L, L2 [2]);
3188 /* Make the line pointer valid again */
3193 L = NextCodeLine (L);
3199 static void OptDoubleJumps (void)
3200 /* Remove/rearrange jumps that jump to other jumps */
3202 static const char* Jumps [] = {
3213 Line* L = FirstCode;
3218 /* Is this a jump? */
3219 while ((I = LineMatchX (L, Jumps)) >= 0) {
3221 /* Yes. Get the target label */
3222 Line* Target = GetTargetLine (L->Line + 5);
3224 /* Target points to the label itself. Skip lines until we reach
3225 * one that is not a label.
3227 Target = NextInstruction (Target);
3229 /* Be sure, this line is not the same as the one the jump is
3230 * in (this happens if there is an empty loop).
3236 if (LineMatch (Target, "\tjmp\t")) {
3238 /* The target is itself a jump. If this is a short branch, get
3239 * the final target and check if it is in reach. Bail out if
3242 if (L->Line[1] == 'b') {
3243 Line* FinalTarget = GetTargetLine (Target->Line+5);
3244 FinalTarget = NextInstruction (FinalTarget);
3245 if ((D = GetJumpDistance (L, FinalTarget)) >= 123) {
3250 /* Make sure the jump does indeed point to another label.
3251 * It may happen that this is not the case for some endless
3252 * loop (while(1) and similar).
3254 if (strcmp (L->Line+5, Target->Line+5) == 0) {
3255 /* Same label, bail out */
3259 /* Use the label in the original jump instead */
3260 L = ReplaceLine (L, "%.5s%s", L->Line, Target->Line+5);
3262 } else if (I < 2 && LineMatch (Target, Jumps [I])) {
3264 /* Conditional jump. Use final label */
3265 strcpy (L->Line+5, Target->Line+5);
3273 L = NextCodeLine (L);
3279 static void OptJumpRTS (void)
3280 /* Replace jumps to an RTS by an RTS */
3282 Line* L = FirstCode;
3284 /* Is this a jump to a numbered label? */
3285 if (LineMatch (L, "\tjmp\t") && L->Line [5] == 'L' && IsDigit (L->Line [6])) {
3287 /* Yes. Get the target label */
3288 Line* Target = GetTargetLine (L->Line+5);
3290 /* Target points to the label itself. Get the next line */
3291 Target = NextCodeLine (Target);
3292 if (LineFullMatch (Target, "\trts")) {
3293 /* Replace the jump by an RTS */
3294 L = ReplaceLine (L, "\trts");
3297 L = NextCodeLine (L);
3303 static void OptBoolTransforms (void)
3304 /* Try to remove the boolean transformation subroutines where they aren't
3310 const char* BranchTarget;
3312 Line* L = FirstCode;
3315 /* Search for a boolean transformer followed by a conditional jump. */
3316 if (LineMatch (L, "\tjsr\tbool") &&
3317 GetNextCodeLines (L, L2, 1) &&
3318 IsCondJump (L2 [0])) {
3320 /* Make the boolean transformer unnecessary by changing the
3321 * the conditional jump to evaluate the condition flags that
3322 * are set after the compare directly. Note: jeq jumps if
3323 * the condition is not met, jne jumps if the condition is met.
3326 /* Get the condition code */
3327 int Cond = FindCond (L->Line + 9);
3333 /* Invert the code if we jump on condition not met. */
3334 if (L2[0]->Line [2] == 'e' && L2[0]->Line [3] == 'q') {
3335 /* Jumps if condition false, invert condition */
3336 Cond = CmpInvertTab [Cond];
3339 /* For easier reading, get a pointer to the jump target */
3340 BranchTarget = L2[0]->Line+5;
3342 /* Check if we can replace the jump (sometimes we would need two
3343 * conditional jumps, we will not handle that for now since it
3344 * has some complications - both jumps may be far jumps for
3345 * example making the jumps more costly than the bool transformer
3346 * subroutine). If we cannot replace the jump, bail out.
3351 L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3355 L = ReplaceLine (L, "\tjne\t%s", BranchTarget);
3359 Label = AllocLabel ();
3360 L = ReplaceLine (L, "\tbeq\tL%04X", Label);
3361 L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3362 L = NewLabelAfter(L, Label);
3366 L = ReplaceLine (L, "\tjpl\t%s", BranchTarget);
3370 L = ReplaceLine (L, "\tjmi\t%s", BranchTarget);
3374 L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3375 L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3379 Label = AllocLabel ();
3380 L = ReplaceLine (L, "\tbeq\tL%04X", Label);
3381 L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3382 L = NewLabelAfter(L, Label);
3386 L = ReplaceLine (L, "\tjcs\t%s", BranchTarget);
3390 L = ReplaceLine (L, "\tjcc\t%s", BranchTarget);
3394 L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3395 L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3399 Internal ("Unknown jump condition: %u", Cond);
3403 /* Remove the old stuff */
3409 L = NextCodeLine (L);
3415 static void OptCompares2 (void)
3416 /* Try to optimize the integer compare subroutines. */
3420 const char* BranchTarget;
3423 Line* L = FirstCode;
3441 if (LineMatch (L, "\tlda\t") &&
3442 GetNextCodeLines (L, L2, 5) &&
3443 IsLoadAX (L, L2[0]) &&
3444 LineFullMatch (L2[1], "\tcpx\t#$00") &&
3445 LineFullMatch (L2[2], "\tbne\t*+4") &&
3446 LineFullMatch (L2[3], "\tcmp\t#$00") &&
3447 IsCondJump (L2[4])) {
3449 /* Replace the load of X by an ora */
3450 L2[0]->Line[1] = 'o';
3451 L2[0]->Line[2] = 'r';
3452 L2[0]->Line[3] = 'a';
3454 /* Remove unneeded stuff */
3455 FreeLines (L2[1], L2[3]);
3459 /* Same for local variables: Replace
3479 else if (LineMatch (L, "\tldy\t#$") &&
3480 GetNextCodeLines (L, L2, 8) &&
3481 LineFullMatch (L2[0], "\tlda\t(sp),y") &&
3482 LineFullMatch (L2[1], "\ttax") &&
3483 LineFullMatch (L2[2], "\tdey") &&
3484 LineFullMatch (L2[3], "\tlda\t(sp),y") &&
3485 LineFullMatch (L2[4], "\tcpx\t#$00") &&
3486 LineFullMatch (L2[5], "\tbne\t*+4") &&
3487 LineFullMatch (L2[6], "\tcmp\t#$00") &&
3488 IsCondJump (L2[7])) {
3490 /* Replace the second load by an ora */
3491 L2[3]->Line[1] = 'o';
3492 L2[3]->Line[2] = 'r';
3493 L2[3]->Line[3] = 'a';
3495 /* Remove unneeded stuff */
3497 FreeLines (L2[4], L2[6]);
3501 /* Search for the call to a compare subroutine followed by a
3504 else if (LineMatch (L, "\tjsr\ttos") &&
3505 (L2[0] = NextCodeLine (L)) != 0 &&
3506 IsCondJump (L2[0])) {
3508 /* Extract the condition from the function name and branch */
3509 C = CheckAndGetIntCmp (L, L2[0]);
3511 /* Something is wrong */
3515 /* Replace the subroutine call by a cheaper one */
3516 L = ReplaceLine (L, "\tjsr\ttosicmp");
3518 /* For easier reading, get a pointer to the jump target */
3519 BranchTarget = L2[0]->Line+5;
3521 /* Check if we can replace the jump (sometimes we would need two
3522 * conditional jumps, we will not handle that for now since it
3523 * has some complications - both jumps may be far jumps for
3524 * example making the jumps more costly than the bool transformer
3525 * subroutine). If we cannot replace the jump, bail out.
3530 L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3534 L = NewLineAfter (L, "\tjne\t%s", BranchTarget);
3538 Label = AllocLabel ();
3539 L = NewLineAfter (L, "\tbeq\tL%04X", Label);
3540 L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3541 L = NewLabelAfter(L, Label);
3545 L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3549 L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3553 L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3554 L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3558 Label = AllocLabel ();
3559 L = NewLineAfter (L, "\tbeq\tL%04X", Label);
3560 L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3561 L = NewLabelAfter(L, Label);
3565 L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3569 L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3573 L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3574 L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3578 Internal ("Unknown jump condition: %u", C);
3582 /* Remove the old stuff */
3587 L = NextCodeLine (L);
3593 static void OptTests (void)
3594 /* Remove unnecessary tests */
3598 static const char* BitOps [] = {
3605 /* Search for lda/tay/jne or lda/tay/jeq, remove the tay.
3612 Line* L = FirstCode;
3615 /* Search for lda/tay/jne or lda/tay/jeq, remove the tay.
3622 if ((LineMatch (L, "\tlda\t") ||
3623 LineMatch (L, "\tand\t") ||
3624 LineMatch (L, "\tora\t") ||
3625 LineMatch (L, "\teor\t")) &&
3626 GetNextCodeLines (L, L2, 2) &&
3627 (LineFullMatch (L2 [0], "\ttay") ||
3628 LineFullMatch (L2 [0], "\tcmp\t#$00")) &&
3629 IsCondJump (L2 [1])) {
3631 /* We can remove the tay */
3642 * and remove the tax.
3644 else if (LineMatchX (L, BitOps) >= 0 &&
3645 GetNextCodeLines (L, L2, 2) &&
3646 LineFullMatch (L2[0], "\ttax") &&
3647 IsCondJump (L2[1])) {
3649 /* Remove the tax including a hint line of there is one */
3650 if (LineFullMatch (L2[0]->Prev, "+forcetest")) {
3651 FreeLine (L2[0]->Prev);
3655 /* If the line before L loads X, this is useless and may be removed */
3656 L2[0] = PrevCodeLine (L);
3657 if (LineFullMatch (L2[0], "\tldx\t#$00")) {
3663 /* Search for the sequence
3674 else if (LineMatch (L, "\tstx\t") &&
3675 GetNextCodeLines (L, L2, 2) &&
3676 LineFullMatch (L2[0], "\tstx\ttmp1") &&
3677 LineFullMatch (L2[1], "\tora\ttmp1")) {
3679 /* Found, replace it */
3680 L = NewLineAfter (L, "\tora\t%s", L->Line+5);
3682 /* Remove remaining stuff */
3683 FreeLines (L2[0], L2[1]);
3689 L = NextCodeLine (L);
3695 static void OptBitOps (void)
3696 /* Optimize bit oeprations */
3700 /* Walk over the code */
3701 Line* L = FirstCode;
3707 * and #$yy ; adc/eor/ora
3716 * While this saves nothing here, it transforms the code to contain an
3717 * explicit register load that may be removed by the basic block
3718 * optimization later. As a special optimization for the 65C02, the
3719 * "ora" and "and" ops may be replaced by "trb" and "tsb" resp. if the
3720 * value in A is not used later.
3722 if (LineMatch (L, "\tlda\t") &&
3723 L->Line[5] != '#' &&
3724 GetNextCodeLines (L, L2, 2) &&
3725 LineMatch (L2[1], "\tsta\t") &&
3726 strcmp (L->Line+5, L2[1]->Line+5) == 0) {
3728 if (LineMatch (L2[0], "\tand\t#$")) {
3730 unsigned Val = GetHexNum (L2[0]->Line+7);
3733 /* AND with 0x00, remove the mem access */
3737 /* Replace the AND by a load */
3738 L = ReplaceLine (L2[0], "\tlda\t#$%02X", Val);
3740 } else if (Val == 0xFF) {
3742 /* AND with 0xFF, just load the value from memory */
3743 FreeLines (L2[0], L2[1]);
3745 } else if (CPU == CPU_65C02 &&
3748 !RegAUsed (L2[1])) {
3750 /* Replace by trb */
3751 ReplaceLine (L, "\tlda\t#$%02X", (~Val) & 0xFF);
3752 L = ReplaceLine (L2[0], "\ttrb\t%s", L2[1]->Line+5);
3758 ReplaceLine (L, "\tlda\t#$%02X", Val);
3759 ReplaceLine (L2[0], "\tand\t%s", L2[1]->Line+5);
3764 } else if (LineMatch (L2[0], "\tora\t#$")) {
3766 unsigned Val = GetHexNum (L2[0]->Line+7);
3769 /* ORA with 0x00, just load the value from memory */
3770 FreeLines (L2[0], L2[1]);
3772 } else if (Val == 0xFF) {
3774 /* ORA with 0xFF, replace by a store of $FF */
3776 L = ReplaceLine (L2[0], "\tlda\t#$FF");
3778 } else if (CPU == CPU_65C02 &&
3781 !RegAUsed (L2[1])) {
3783 /* Replace by trb */
3784 ReplaceLine (L, "\tlda\t#$%02X", Val);
3785 L = ReplaceLine (L2[0], "\ttsb\t%s", L2[1]->Line+5);
3791 ReplaceLine (L, "\tlda\t#$%02X", Val);
3792 ReplaceLine (L2[0], "\tora\t%s", L2[1]->Line+5);
3797 } else if (LineMatch (L2[0], "\teor\t#$") ||
3798 LineMatch (L2[0], "\tadc\t#$")) {
3801 L = ReplaceLine (L, "\tlda\t%s", L2[0]->Line+5);
3802 ReplaceLine (L2[0], "\t%.3s\t%s", L2[0]->Line+1, L2[1]->Line+5);
3808 L = NextCodeLine (L);
3814 static void OptNeg (void)
3815 /* Optimize the "bnegax/jeq" and "bnegax/jne" sequences */
3819 Line* L = FirstCode;
3822 /* Search for the sequence:
3828 * and replace it by:
3833 if (LineMatch (L, "\tlda\t") && /* Match on start */
3834 GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
3835 LineFullMatch (L2 [0], "\tjsr\tbnega") &&
3836 IsCondJump (L2 [1])) {
3838 /* Found the sequence, replace it */
3840 InvertZJump (L2 [1]);
3844 /* Search for the sequence:
3862 else if (LineMatch (L, "\tldy\t#$") &&
3863 GetNextCodeLines (L, L2, 6) &&
3864 LineFullMatch (L2[0], "\tlda\t(sp),y") &&
3865 LineFullMatch (L2[1], "\ttax") &&
3866 LineFullMatch (L2[2], "\tdey") &&
3867 LineFullMatch (L2[3], "\tlda\t(sp),y") &&
3868 LineFullMatch (L2[4], "\tjsr\tbnegax") &&
3869 IsCondJump (L2[5])) {
3871 L2[1] = ReplaceLine (L2[1], "\tdey");
3872 L2[2] = ReplaceLine (L2[2], "\tora\t(sp),y");
3873 FreeLines (L2[3], L2[4]);
3874 InvertZJump (L2[5]);
3878 /* Search for the sequence:
3891 else if (LineMatch (L, "\tlda\t") &&
3892 GetNextCodeLines (L, L2, 3) &&
3893 IsLoadAX (L, L2[0]) &&
3894 LineFullMatch (L2[1], "\tjsr\tbnegax") &&
3895 IsCondJump (L2[2])) {
3897 /* Replace the load of X by ora */
3898 L2[0]->Line[1] = 'o';
3899 L2[0]->Line[2] = 'r';
3900 L2[0]->Line[3] = 'a';
3902 InvertZJump (L2[2]);
3906 /* Search for the sequence:
3912 * and replace it by:
3918 else if (LineMatch (L, "\tjsr\t_") && /* Match on start */
3919 GetNextCodeLines (L, L2, 2) &&
3920 LineMatch (L2 [0], "\tjsr\tbnega") &&
3921 IsCondJump (L2 [1])) {
3923 if (LineFullMatch (L2 [0], "\tjsr\tbnega")) {
3925 L2 [0] = ReplaceLine (L2 [0], "\ttax"); /* Test a */
3928 L2 [0] = ReplaceLine (L2 [0], "\tstx\ttmp1");
3929 NewLineAfter (L2 [0], "\tora\ttmp1");
3932 /* Invert the jump */
3933 InvertZJump (L2 [1]);
3938 L = NextCodeLine (L);
3944 static void OptTriples (void)
3945 /* Replace code triples */
3947 static const char* Pat1 [] = {
3955 static const char* Pat2 [] = {
3963 static const char* Replace [] = {
3971 Line* L = FirstCode;
3973 int I = LineFullMatchX (L, Pat1);
3975 /* We found the first match, get the next line */
3976 Line* L2 = NextCodeLine (L);
3977 if (L2 && LineFullMatch (L2, Pat2 [I])) {
3978 /* Found. Replace by the short call */
3980 L = ReplaceLine (L, Replace [I]);
3984 L = NextCodeLine (L);
3990 static Line* OptOneBlock (Line* L)
3991 /* Optimize the register contents inside one basic block */
3993 static const char* Compares [] = {
3994 "\tjsr\ttoseq00", "\tjsr\ttoseqa0", "\tjsr\ttoseqax",
3995 "\tjsr\ttoseqeax", "\tjsr\ttosne00", "\tjsr\ttosnea0",
3996 "\tjsr\ttosneax", "\tjsr\ttosneeax", "\tjsr\ttoslt00",
3997 "\tjsr\ttoslta0", "\tjsr\ttosltax", "\tjsr\ttosult00",
3998 "\tjsr\ttosulta0", "\tjsr\ttosultax", "\tjsr\ttoslteax",
3999 "\tjsr\ttosulteax", "\tjsr\ttosle00", "\tjsr\ttoslea0",
4000 "\tjsr\ttosleax", "\tjsr\ttosule00", "\tjsr\ttosulea0",
4001 "\tjsr\ttosuleax", "\tjsr\ttosleeax", "\tjsr\ttosuleeax",
4002 "\tjsr\ttosgt00", "\tjsr\ttosgta0", "\tjsr\ttosgtax",
4003 "\tjsr\ttosugt00", "\tjsr\ttosugta0", "\tjsr\ttosugtax",
4004 "\tjsr\ttosgteax", "\tjsr\ttosugteax", "\tjsr\ttosge00",
4005 "\tjsr\ttosgea0", "\tjsr\ttosgeax", "\tjsr\ttosuge00",
4006 "\tjsr\ttosugea0", "\tjsr\ttosugeax", "\tjsr\ttosgeeax",
4011 static const char* MakeBool [] = {
4012 "\tjsr\tbooleq", "\tjsr\tboolne", "\tjsr\tboollt",
4013 "\tjsr\tboolle", "\tjsr\tboolgt", "\tjsr\tboolge",
4014 "\tjsr\tboolult", "\tjsr\tboolule", "\tjsr\tboolugt",
4019 int A = -1; /* Contents of A register */
4020 int X = -1; /* Contents of X register */
4021 int Y = -1; /* Contents of Y register */
4026 while (L && !IsLabel (L)) {
4028 /* Handle all instructions. All instructions not tested here have
4029 * no effects on the register contents.
4032 if (L->Line [0] == '+') {
4033 /* This is a hint */
4034 if (LineMatch (L, "+a:")) {
4035 /* Information about a */
4036 switch (L->Line [3]) {
4037 case '!': A = -1; break;
4038 case '=': A = GetHexNum (L->Line + 4); break;
4040 } else if (LineMatch (L, "+x:")) {
4041 /* The code generator tells something about the x register */
4042 switch (L->Line [3]) {
4043 case '!': X = -1; break;
4044 case '=': X = GetHexNum (L->Line + 4); break;
4046 } else if (LineMatch (L, "+y:")) {
4047 /* Information about the y register */
4048 switch (L->Line [3]) {
4049 case '!': Y = -1; break;
4050 case '=': Y = GetHexNum (L->Line + 4); break;
4053 } else if (LineMatch (L, "\tadc\t")) {
4054 if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
4055 L->Line[strlen(L->Line)-2] = '\0';
4058 } else if (LineMatch (L, "\tand\t")) {
4060 } else if (LineFullMatch (L, "\tasl\ta")) {
4062 A = (A << 1) & 0xFF;
4064 } else if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
4065 L->Line[strlen(L->Line)-2] = '\0';
4066 } else if (CPU == CPU_65C02 && (LineFullMatch (L, "\tdea") ||
4067 LineFullMatch (L, "\tdec\ta"))) {
4069 } else if (LineFullMatch (L, "\tdex")) {
4071 } else if (LineFullMatch (L, "\tdey")) {
4073 } else if (LineMatch (L, "\teor")) {
4075 } else if (CPU == CPU_65C02 && (LineFullMatch (L, "\tina") ||
4076 LineFullMatch (L, "\tinc\ta"))) {
4078 } else if (LineFullMatch (L, "\tinx")) {
4080 } else if (LineFullMatch (L, "\tiny")) {
4082 } else if (LineFullMatch (L, "\tjsr\taddeq0sp")) {
4083 /* We know about this function */
4086 } else if (LineFullMatch (L, "\tjsr\taddeqysp")) {
4087 /* We know about this function */
4090 } else if (LineFullMatch (L, "\tjsr\taxulong")) {
4091 /* We know about this function and we're trying to replace it by
4092 * inline code if we have already a register that contains zero.
4097 } else if (X == 0) {
4099 } else if (Y == 0) {
4105 /* We cannot replace the code, but we know about the results */
4108 L = ReplaceLine (L, "\tst%c\tsreg", C);
4109 NewLineAfter (L, "\tst%c\tsreg+1", C);
4111 } else if (LineFullMatch (L, "\tjsr\tbnega")) {
4112 /* We know about this function */
4115 } else if (LineFullMatch (L, "\tjsr\tbnegax")) {
4116 /* We know about this function */
4119 } else if (LineFullMatch (L, "\tjsr\tbnegeax")) {
4120 /* We know about this function */
4123 } else if (LineFullMatch (L, "\tjsr\tcomplax")) {
4124 /* We know about this function */
4131 } else if (LineFullMatch (L, "\tjsr\tdecax1")) {
4132 /* We know about this function */
4134 } else if (LineFullMatch (L, "\tjsr\tdecax2")) {
4135 /* We know about this function */
4137 } else if (LineFullMatch (L, "\tjsr\tdecaxy")) {
4138 /* We know about this function */
4140 } else if (LineFullMatch (L, "\tjsr\tdeceaxy")) {
4141 /* We know about this function */
4143 } else if (LineMatch (L, "\tjsr\tdecsp") && IsDigit (L->Line[10])) {
4144 if (L->Line[10] == '1') {
4145 /* Destroys Y, leaves all other regs alone */
4148 /* Destroys A, leaves all other regs alone */
4151 } else if (LineFullMatch (L, "\tjsr\tincax1")) {
4152 /* We know about this function */
4154 } else if (LineFullMatch (L, "\tjsr\tincax2")) {
4155 /* We know about this function */
4157 } else if (LineFullMatch (L, "\tjsr\tinceaxy")) {
4158 /* We know about this function */
4160 } else if (LineFullMatch (L, "\tjsr\tladdeq")) {
4161 /* We know about this function */
4164 } else if (LineFullMatch (L, "\tjsr\tladdeqb")) {
4165 /* We know about this function */
4168 } else if (LineFullMatch (L, "\tjsr\tlbneg")) {
4169 /* We know about this function */
4172 } else if (LineFullMatch (L, "\tjsr\tldai")) {
4173 /* We know about this function */
4176 } else if (LineFullMatch (L, "\tjsr\tldaidx")) {
4177 /* We know about this function */
4179 } else if (LineFullMatch (L, "\tjsr\tldau00sp")) {
4180 /* We know about this function */
4184 } else if (LineFullMatch (L, "\tjsr\tldau0ysp")) {
4185 /* We know about this function */
4189 } else if (LineFullMatch (L, "\tjsr\tldaui")) {
4190 /* We know about this function */
4194 } else if (LineFullMatch (L, "\tjsr\tldaui0sp")) {
4198 } else if (LineFullMatch (L, "\tjsr\tldauidx")) {
4199 /* We know about this function */
4202 } else if (LineFullMatch (L, "\tjsr\tldauiysp")) {
4203 /* We know about this function */
4207 } else if (LineFullMatch (L, "\tjsr\tldax0sp")) {
4208 /* We know about this function */
4211 } else if (LineFullMatch (L, "\tjsr\tldaxi")) {
4212 /* We know about this function */
4215 } else if (LineFullMatch (L, "\tjsr\tldaxidx")) {
4216 /* We know about this function */
4219 } else if (LineFullMatch (L, "\tjsr\tldaxysp")) {
4220 /* We know about this function */
4223 } else if (LineFullMatch (L, "\tjsr\tldeaxi")) {
4224 /* We know about this function */
4227 } else if (LineFullMatch (L, "\tjsr\tldeaxidx")) {
4228 /* We know about this function */
4231 } else if (LineFullMatch (L, "\tjsr\tlsubeq")) {
4232 /* We know about this function */
4235 } else if (LineFullMatch (L, "\tjsr\tlsubeqb")) {
4236 /* We know about this function */
4239 } else if (LineFullMatch (L, "\tjsr\tnegax")) {
4240 /* We know about this function */
4242 } else if (LineFullMatch (L, "\tjsr\tnegeax")) {
4243 /* We know about this function */
4245 } else if (LineFullMatch (L, "\tjsr\tpush0")) {
4246 /* We know about this function */
4250 } else if (LineFullMatch (L, "\tjsr\tpush0ax")) {
4251 /* We know about this function */
4253 } else if (LineFullMatch (L, "\tjsr\tpush1")) {
4254 /* We know about this function */
4258 } else if (LineFullMatch (L, "\tjsr\tpush2")) {
4259 /* We know about this function */
4263 } else if (LineFullMatch (L, "\tjsr\tpush3")) {
4264 /* We know about this function */
4268 } else if (LineFullMatch (L, "\tjsr\tpush4")) {
4269 /* We know about this function */
4273 } else if (LineFullMatch (L, "\tjsr\tpush5")) {
4274 /* We know about this function */
4278 } else if (LineFullMatch (L, "\tjsr\tpush6")) {
4279 /* We know about this function */
4283 } else if (LineFullMatch (L, "\tjsr\tpush7")) {
4284 /* We know about this function */
4288 } else if (CPU != CPU_65C02 && LineFullMatch (L, "\tjsr\tpusha")) {
4289 /* We know about this function */
4291 } else if (LineFullMatch (L, "\tjsr\tpusha0")) {
4292 /* We know about this function
4293 * If X is already zero, we may call pushax instead and save two
4297 L = ReplaceLine (L, "\tjsr\tpushax");
4301 } else if (LineFullMatch (L, "\tjsr\tpushax")) {
4302 /* We know about this function */
4304 } else if (CPU != CPU_65C02 && LineFullMatch (L, "\tjsr\tpushaysp")) {
4305 /* We know about this function */
4308 } else if (LineFullMatch (L, "\tjsr\tpushc0")) {
4309 /* We know about this function */
4312 } else if (LineFullMatch (L, "\tjsr\tpushc1")) {
4313 /* We know about this function */
4316 } else if (LineFullMatch (L, "\tjsr\tpushc2")) {
4317 /* We know about this function */
4320 } else if (LineFullMatch (L, "\tjsr\tpusheax")) {
4321 /* We know about this function */
4323 } else if (LineFullMatch (L, "\tjsr\tpushw")) {
4324 /* We know about this function (calls pushax) */
4327 } else if (LineFullMatch (L, "\tjsr\tpushw0sp")) {
4328 /* We know about this function(calls pushax) */
4331 } else if (LineFullMatch (L, "\tjsr\tpushwidx")) {
4332 /* We know about this function (calls pushax) */
4335 } else if (LineFullMatch (L, "\tjsr\tpushwysp")) {
4336 /* We know about this function (calls pushax) */
4339 } else if (LineFullMatch (L, "\tjsr\tresteax")) {
4340 /* We know about this function */
4342 } else if (LineFullMatch (L, "\tjsr\tsaveeax")) {
4343 /* We know about this function */
4344 /* Changes nothing */
4345 } else if (LineFullMatch (L, "\tjsr\tshrax1")) {
4346 /* We know about this function */
4348 } else if (LineFullMatch (L, "\tjsr\tshrax2")) {
4349 /* We know about this function */
4351 } else if (LineFullMatch (L, "\tjsr\tshrax3")) {
4352 /* We know about this function */
4354 } else if (LineFullMatch (L, "\tjsr\tshreax1")) {
4355 /* We know about this function */
4357 } else if (LineFullMatch (L, "\tjsr\tshreax2")) {
4358 /* We know about this function */
4360 } else if (LineFullMatch (L, "\tjsr\tshreax3")) {
4361 /* We know about this function */
4363 } else if (LineFullMatch (L, "\tjsr\tstaspp")) {
4364 /* We know about this function */
4366 } else if (LineFullMatch (L, "\tjsr\tstaxspp")) {
4367 /* We know about this function */
4369 } else if (LineFullMatch (L, "\tjsr\tstax0sp")) {
4370 /* We know about this function */
4372 } else if (LineFullMatch (L, "\tjsr\tstaxysp")) {
4373 /* We know about this function */
4375 } else if (LineFullMatch (L, "\tjsr\tsubeq0sp")) {
4376 /* We know about this function */
4379 } else if (LineFullMatch (L, "\tjsr\tsubeqysp")) {
4380 /* We know about this function */
4383 } else if (LineFullMatch (L, "\tjsr\tsubysp")) {
4384 /* We know about this function */
4386 } else if (LineFullMatch (L, "\tjsr\ttosadda0")) {
4387 /* We know about this function */
4390 } else if (LineFullMatch (L, "\tjsr\ttosaddax")) {
4391 /* We know about this function */
4394 } else if (LineFullMatch (L, "\tjsr\ttosicmp")) {
4395 /* We know about this function */
4398 } else if (LineFullMatchX (L, Compares) >= 0) {
4401 } else if (LineFullMatchX (L, MakeBool) >= 0) {
4404 } else if (LineMatch (L, "\tjsr\t")) {
4405 /* Subroutine call, forget all register information */
4407 } else if (LineMatch (L, "\tlda\t")) {
4408 if (!RegAUsed (L) && !IsCondJump (NextInstruction (L))) {
4409 /* The value loaded is not used later, remove it */
4411 } else if (LineMatch (L, "\tlda\t(")) {
4412 if (IsXAddrMode (L)) {
4414 /* If X is zero and we have a 65C02 cpu, replace it by
4417 if (X == 0 && CPU == CPU_65C02) {
4418 unsigned Len = strlen (L->Line);
4419 L->Line [Len-3] = ')';
4420 L->Line [Len-2] = '\0';
4421 /* If Y and X are both zero, replace by load indirect
4422 * y and save one cycle in some cases.
4424 } else if (X == 0 && Y == 0) {
4426 const char* S = L->Line + 6;
4428 strcpy (Buf, "\tlda\t(");
4436 L = ReplaceLine (L, Buf);
4438 } else if (IsYAddrMode (L)) {
4439 /* lda (zp),y. If Y is zero and we have a 65C02 CPU,
4440 * replace it by an indirect load.
4442 if (Y == 0 && CPU == CPU_65C02) {
4443 unsigned Len = strlen (L->Line);
4444 L->Line [Len-3] = ')';
4445 L->Line [Len-2] = '\0';
4448 /* In any case invalidate A */
4450 } else if (LineMatch (L, "\tlda\t#$")) {
4451 /* Immidiate load into A */
4452 NewVal = GetHexNum (L->Line + 7);
4454 /* Load has no effect */
4456 } else if (NewVal == X) {
4457 /* Requested value is already in X */
4458 L = ReplaceLine (L, "\ttxa");
4459 } else if (NewVal == Y) {
4460 /* Requested value is already in Y */
4461 L = ReplaceLine (L, "\ttya");
4462 } else if (CPU == CPU_65C02 && A != -1) {
4463 /* Try ina/dea operators of 65C02 */
4464 if (NewVal == ((A - 1) & 0xFF)) {
4465 L = ReplaceLine (L, "\tdea");
4466 } else if (NewVal == ((A + 1) & 0xFF)) {
4467 L = ReplaceLine (L, "\tina");
4470 /* Anyway, the new value is now in A */
4473 /* Memory load into A */
4476 } else if (LineMatch (L, "\tldax\t")) {
4477 /* Memory load into A and X */
4479 } else if (LineMatch (L, "\tldx\t")) {
4480 if (!RegXUsed (L) && !IsCondJump (NextInstruction (L))) {
4481 /* The value loaded is not used later, remove it */
4483 } else if (LineMatch (L, "\tldx\t#$")) {
4484 /* Immidiate load into X */
4485 NewVal = GetHexNum (L->Line + 7);
4487 /* Load has no effect */
4489 } else if (NewVal == A) {
4490 /* Requested value is already in A */
4491 L = ReplaceLine (L, "\ttax");
4492 } else if (X != -1 && NewVal == ((X + 1) & 0xFF)) {
4493 /* Requested value is one more than current contents */
4494 L = ReplaceLine (L, "\tinx");
4495 } else if (X != -1 && NewVal == ((X - 1) & 0xFF)) {
4496 /* Requested value is one less than current contents */
4497 L = ReplaceLine (L, "\tdex");
4499 /* Anyway, the new value is now in X */
4502 /* Memory load into X */
4505 } else if (LineMatch (L, "\tldy\t")) {
4506 if (!RegYUsed (L) && !IsCondJump (NextInstruction (L))) {
4507 /* The value loaded is not used later, remove it */
4509 } else if (LineMatch (L, "\tldy\t#$")) {
4510 /* Immidiate load into Y */
4511 NewVal = GetHexNum (L->Line + 7);
4513 /* Load has no effect */
4515 } else if (NewVal == A) {
4516 /* Requested value is already in A */
4517 L = ReplaceLine (L, "\ttay");
4518 } else if (Y != -1 && NewVal == ((Y + 1) & 0xFF)) {
4519 /* Requested value is one more than current contents */
4520 L = ReplaceLine (L, "\tiny");
4521 } else if (Y != -1 && NewVal == ((Y - 1) & 0xFF)) {
4522 /* Requested value is one less than current contents */
4523 L = ReplaceLine (L, "\tdey");
4525 /* Anyway, the new value is now in Y */
4528 /* Memory load into Y */
4531 } else if (LineFullMatch (L, "\tlsr\ta")) {
4535 } else if (LineMatch (L, "\tora\t#$")) {
4537 A |= GetHexNum (L->Line + 7);
4539 } else if (LineMatch (L, "\tora\t")) {
4541 } else if (LineFullMatch (L, "\tpla")) {
4543 } else if (LineFullMatch (L, "\trol\ta")) {
4545 } else if (LineFullMatch (L, "\tror\ta")) {
4547 } else if (LineFullMatch (L, "\trts")) {
4549 } else if (LineFullMatch (L, "\trti")) {
4551 } else if (LineMatch (L, "\tsbc\t")) {
4552 if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
4553 L->Line[strlen(L->Line)-2] = '\0';
4556 } else if (CPU == CPU_65C02 && LineMatch (L, "\tst")) {
4557 /* Try to replace by stz if possible */
4558 if (A == 0 && LineMatch (L, "\tsta\t")) {
4559 /* Not indirect and not Y allowed */
4560 if (L->Line[5] != '(' && !IsYAddrMode (L)) {
4563 } else if (X == 0 && LineMatch (L, "\tstx\t")) {
4564 /* absolute,y not allowed */
4565 if (!IsYAddrMode (L)) {
4568 } else if (Y == 0 && LineMatch (L, "\tsty\t")) {
4569 /* sty and stz share all addressing modes */
4572 } else if (LineFullMatch (L, "\ttax")) {
4573 if (A != -1 && X == A) {
4574 /* Load has no effect */
4579 } else if (LineFullMatch (L, "\ttay")) {
4580 if (A != -1 && Y == A) {
4581 /* Load has no effect */
4586 } else if (LineFullMatch (L, "\ttsx")) {
4588 } else if (LineFullMatch (L, "\ttxa")) {
4589 if (X != -1 && A == X) {
4590 /* Load has no effect */
4595 } else if (LineFullMatch (L, "\ttya")) {
4596 if (Y != -1 && A == Y) {
4597 /* Load has no effect */
4604 /* Set to next line, handle deletions */
4605 L2 = NextCodeSegLine (L);
4613 /* Skip the label */
4614 L = NextCodeSegLine (L);
4621 static void OptBlocks (void)
4622 /* Optimize the register contents inside basic blocks */
4624 Line* L = FirstCode;
4626 L = OptOneBlock (L);
4632 static void OptJumps (void)
4633 /* Optimize jumps */
4635 static const char* Jumps [] = {
4645 Line* L = FirstCode;
4647 int I = LineMatchX (L, Jumps);
4649 Line* Target = GetTargetLine (L->Line+5);
4650 if (Target->Index > L->Index) {
4651 /* This is a forward jump. Backward jumps are handled
4652 * automagically by the assembler.
4654 unsigned Distance = GetJumpDistance (L, Target);
4655 if (Distance < 123) { /* Safety */
4656 L->Line [1] = 'b'; /* Make a short branch */
4657 L->Size = 2; /* Set new size */
4661 L = NextCodeLine (L);
4664 /* Special treatment for jumps on the 65C02 */
4665 if (CPU == CPU_65C02) {
4667 Line* L = FirstCode;
4669 if (LineMatch (L, "\tjmp\tL")) {
4670 Line* Target = GetTargetLine (L->Line+5);
4671 unsigned Distance = GetJumpDistance (L, Target);
4672 if (Distance < 123) { /* Safety */
4673 L->Line [1] = 'b'; /* Make a short branch */
4676 L->Size = 2; /* Set new size */
4679 L = NextCodeLine (L);
4687 static void OptRTS (void)
4688 /* Change sequences of jsr XXX/rts to jmp XXX */
4690 Line* L = FirstCode;
4692 if (LineMatch (L, "\tjsr\t")) {
4693 /* This is a jsr, get the next instruction */
4694 Line* L2 = NextCodeLine (L);
4695 if (L2 && LineFullMatch (L2, "\trts")) {
4696 /* We found a sequence */
4702 /* Try the next line */
4703 L = NextCodeLine (L);
4709 /*****************************************************************************/
4711 /*****************************************************************************/
4715 void OptDoOpt (void)
4716 /* Run the optimizer over the collected stuff */
4718 typedef void (*OptFunc)(void);
4720 /* Table with optimizer steps - are called in this order */
4721 static const OptFunc OptFuncs [] = {
4722 OptCompares1, /* Optimize compares - first step */
4723 OptDeadJumps, /* Remove dead jumps */
4724 OptLoads, /* Remove unnecessary loads */
4725 OptRegLoads, /* Remove unnecessary register loads */
4726 OptPtrOps, /* Optimize stores through pointers */
4727 OptRegVars, /* Optimize use of register variables */
4728 OptDoubleJumps, /* Remove jump cascades - must be used before OptNeg */
4729 OptNeg, /* Remove unnecessary boolean negates */
4730 OptJumpRTS, /* Replace jumps to an RTS by an RTS */
4731 OptBoolTransforms, /* Optimize boolean transforms */
4732 OptCompares2, /* Optimize compares */
4733 OptTests, /* Remove unnecessary tests */
4734 OptBitOps, /* Optimize bit operations */
4735 OptTriples, /* Optimize several triples */
4736 OptBlocks, /* Optimize basic blocks */
4737 OptRegLoads, /* Remove unnecessary register loads (another pass) */
4738 OptBlocks, /* Optimize basic blocks */
4739 OptJumps, /* Optimize jumps */
4740 OptRTS, /* Optimize jsr/rts sequences */
4743 unsigned long Flags;
4746 /* Find and remember the first line of code */
4749 /* Mark all lines inside the code segment */
4752 /* Create a list of all local labels for fast access */
4755 /* Ok, now start the real optimizations */
4757 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I, Flags <<= 1) {
4758 if ((OptDisable & Flags) == 0) {
4760 } else if (Verbosity > 0 || Debug) {
4761 printf ("Optimizer pass %u skipped\n", I);